如何在当前模块上调用setattr()函数?

168

我应该将第一个参数 "object" 传递给函数 setattr(object, name, value),以在当前模块上设置变量吗?

举个例子:

setattr(object, "SOME_CONSTANT", 42);

给出与以下代码相同的效果:

SOME_CONSTANT = 42

在包含这些代码行的模块中(使用正确的object)。

我在模块级别动态生成了多个值,由于我无法在模块级别定义__getattr__,所以这是我的备选方案。

4个回答

279
import sys

thismodule = sys.modules[__name__]

setattr(thismodule, name, value)

或者,不使用setattr(这违背了问题的本意,但是可以达到相同的实际效果;-):

globals()[name] = value

注意:在模块作用域中,后者等同于:

vars()[name] = value

这种方法更加简洁,但无法在函数内部工作(vars()会返回它被调用时所在作用域的变量:当在全局作用域调用时返回模块变量,此时可以读写,但如果在函数中调用,则返回函数的变量,此时必须视为只读。Python在线文档对这种具体区别可能有点令人困惑)。


9
文档警告不要修改 vars()。http://docs.python.org/library/functions.html#vars 。什么情况下可以这样做? - unutbu
2
@~unutbu,我不会真的说这很“好”,但是当你在模块级别作用域中调用vars()而不是在函数内部调用时,它会起作用。 - Mike Graham
4
vars() 在模块作用域下等同于 globals()(因此返回一个真正的可修改字典),但在函数作用域下等同于 locals()(因此返回一个永远不会被修改的伪字典)。我在模块作用域下使用 vars(),因为与其在该作用域中的同义词 globals() 相比,它可以省下3个字符,一个音节;-) - Alex Martelli
15
是的,这将破坏Python编译器实现的最重要的优化之一:函数的局部变量不是保存在字典中,而是保存在一个紧凑的向量中,并且每个局部变量访问使用该向量中的索引,而不是名称查找。为了打败这种优化,强制存在所需的字典,请在函数开头使用exec'':比较一下带有几个实质性循环的函数的时间,你就会看到这个核心优化对Python性能的重要性。 - Alex Martelli
3
@msw,我觉得你忘记了“实用性胜于纯粹性”;-)。 - Alex Martelli
显示剩余11条评论

15

在 Python 3.7 中,您可以在模块级别使用 __getattr__相关答案)。

根据 PEP 562

def __getattr__(name):
    if name == "SOME_CONSTANT":
        return 42
    raise AttributeError(f"module {__name__} has no attribute {name}")

6
如果您必须在模块内部设置模块范围的变量,那么使用global有什么问题吗?
# my_module.py

def define_module_scoped_variables():
    global a, b, c
    a, b, c = 'a', ['b'], 3

因此:
>>> import my_module
>>> my_module.define_module_scoped_variables()
>>> a
NameError: name 'a' is not defined
>>> my_module.a
'a'
>>> my_module.b
['b']

1
是的,我一直(“一直”被定义为“过去几个月我一直在学习Python”)觉得“全局但不完全是全局”的声明令人困惑。我猜它可能是一个早期的历史遗留问题,早于模块命名空间的出现。 - msw
1
原问题是如何设置一个由字符串给出名称的属性(这正是我现在正在搜索的东西),因此这并没有帮助。 - Curt

-1
  1. 你不会这样做。你应该使用 globals()["SOME_CONSTANT"] = 42
  2. 你不会这样做。你应该将动态生成的内容存储在模块之外的其他地方。

是的,运行时计算的SOME_CONSTANT并不完全是常量。如果你无法使用globals(),那么你必须访问另一个模块来修改其属性;这肯定会引起人们的疑惑。 - msw
4
常量和可变量是互斥的。常量和动态生成的值并不互斥。我正在生成的值总是相同的,并且基于进一步的“常量”来确定,以节省我的算术和输入时间。 - Matt Joiner

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接