【问题标题】:Using Python setattr to set an attribute reference to a method or function使用 Python setattr 设置对方法或函数的属性引用
【发布时间】:2016-02-09 22:56:36
【问题描述】:

我需要使用 setattr 从配置文件中读取,并将类的属性设置为另一个模块中的函数。粗略来说,这就是问题所在。

这行得通:

class MyClass:
    def __init__(self):
        self.number_cruncher = anothermodule.some_function

# method works like this, of course;
foo = MyClass()
foo.number_cruncher(inputs)

好的,这很简单,但是如果我想从配置文件中读取some_function 的名称怎么办?读取文件并使用 setattr 也很简单:

def read_ini(target):
    # file reading/parsing stuff here, so we effectively do:
    setattr(target, 'number_cruncher', 'anothermodule.some_function')

bar = MyClass()
read_ini(bar)
bar.number_cruncher(inputs)

这给了我一个 'str' object is not callable 错误。如果我理解正确,那是因为在第一种情况下,我将属性设置为对另一个模块的引用,但在第二种情况下,它只是将属性设置为字符串。

问题:1) 我对这个错误的理解正确吗? 2) 如何使用setattr 将属性设置为对函数的引用而不是简单的字符串属性?

我查看了其他问题并发现了类似的问题,但似乎没有任何问题可以准确解决这个问题。

【问题讨论】:

  • 模块是提前知道的,还是需要根据字符串加载模块?
  • 你是说anothermodule.some_function的值是从文件中读取的吗?如果是这样,您希望使用动态导入,并在生成的模块对象上使用getattr() 将该字符串解析为函数对象。
  • 否则,如果anothermodule.some_function 是静态的(在您编写代码时已知),那么只需使用etattr(target, 'number_cruncher', anothermodule.some_function)。第三个参数不必是字符串,它是您要用作所设置属性值的实际对象。
  • 编辑:@MartijnPieters 基本上已经说过了...setattr 的第三个参数是您设置的值,所以如果这是一个字符串,您就设置一个字符串。如果它是一个 int,它将是一个 int 等,然后是一个对象的对象。
  • 模块已知并已导入。基本上,它是一个可能的函数库。查看下面的答案,但我认为他们已经解决了。

标签: python setattr


【解决方案1】:

最简单的方法是这样的:

module_name, function_name = "module.func".rsplit('.', 1)
f = getattr(sys.modules[module_name], function_name)
f()  # f is now callable, analogous to anothermodule.some_function in your code snippets.

显然,很多潜在的问题都没有得到解决。首先,它假设模块已经被导入。要解决这个问题,您可以参考Dynamic module import in Python 并使用__import__ 的返回值。不用担心,CPython 会在内部对其进行优化,并且不会对同一个模块进行多次解释。

稍微好一点的版本是:

module_name, function_name = "module.func".rsplit('.', 1)
f = getattr(__import__(module_name), function_name)
f()

【讨论】:

  • 对于前向兼容,最好使用importlib.import_module 而不是__import__
  • 我在概念上理解这一点,但是如何让importlib.import_module 简单地从与我的程序位于同一目录中的另一个 .py 文件导入?这是一个简单的import whatever 的工作方式,但我无法理解 [link]docs.python.org/2/library/…。我收到诸如“No module named... xxx is not a module”之类的错误。如何简单地从与程序位于同一目录中的另一个 .py 文件导入?
  • @user5747140 import whatever 等价于whatever = importlib.import_module('whatever')。第二个参数(签名是importlib.import_module(name, package=None))是可选的,允许您在name 参数中使用相对符号。
【解决方案2】:

是的,你的理解是正确的。当您使用 setattr 时,您正在将属性设置为当前示例中的字符串。因此,当您随后调用 bar.number_cruncher(inputs) 时,它会正确地告诉您字符串对象不可调用。相反,如果您想让bar.number_chruncher 成为函数anothermodule.some_function 的可调用等效项,那么您可以导入它并将函数(而不是字符串)设置为属性。

def read_ini(target):
    # file reading/parsing stuff here, so we effectively do:
    __import__("anothermodule.some_function") #Allows you to import based on a string parsed from your configuration file.
    setattr(target, 'number_cruncher', anothermodule.some_function)

【讨论】:

  • 这是有道理的,但我无法弄清楚如何让“另一个模块”引用与程序位于同一目录中的 py 文件。
猜你喜欢
  • 2012-05-16
  • 2021-01-04
  • 2014-06-18
  • 2015-06-12
  • 1970-01-01
  • 2015-08-12
  • 2019-04-18
  • 1970-01-01
  • 2011-07-16
相关资源
最近更新 更多