2

我需要使用 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”对象不可调用错误。如果我理解正确,那是因为在第一种情况下,我将属性设置为对另一个模块的引用,但在第二种情况下,它只是将属性设置为字符串。

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

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

4

2 回答 2

1

是的,你的理解是正确的。使用时,setattr您正在将属性设置为当前示例中的字符串。因此,当您bar.number_cruncher(inputs)正确调用它时,会告诉您字符串对象不可调用。相反,如果您希望bar.number_chruncher成为 function 的可调用等效项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)
于 2016-02-09T23:06:58.967 回答
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.

显然,很多潜在的问题没有得到解决。首先,它假设模块已经被导入。为了解决这个问题,您可以参考Python 中的动态模块导入并使用__import__. 不用担心,CPython 会在内部对其进行优化,并且不会多次解释同一个模块。

稍微好一点的版本是:

module_name, function_name = "module.func".rsplit('.', 1)
f = getattr(__import__(module_name), function_name)
f()
于 2016-02-09T23:18:11.133 回答