tl; dr:属性装饰器如何使用类级函数定义,而不是模块级定义?
我将属性装饰器应用于某些模块级函数,认为它们允许我通过属性查找来调用方法。
这特别诱人,因为我正在定义一组配置函数,例如get_port
,get_hostname
等,所有这些函数都可以用更简单,更简洁的属性对象替换:{ {1}},port
等
因此,hostname
只会更好config.get_port()
当我发现以下追溯时,我感到很惊讶,证明这不是一个可行的选择:
config.port
我知道我已经在模块级别看到了类似属性的功能,因为我使用优雅但是hacky pbs library来编写shell命令脚本。
下面有趣的黑客可以在pbs library source code中找到。它使得能够在模块级别执行类似属性的属性查找,但是它非常可怕,非常hackish。
TypeError: int() argument must be a string or a number, not 'property'
以下是将此类插入导入命名空间的代码。它实际上直接修补了# this is a thin wrapper around THIS module (we patch sys.modules[__name__]).
# this is in the case that the user does a "from pbs import whatever"
# in other words, they only want to import certain programs, not the whole
# system PATH worth of commands. in this case, we just proxy the
# import lookup to our Environment class
class SelfWrapper(ModuleType):
def __init__(self, self_module):
# this is super ugly to have to copy attributes like this,
# but it seems to be the only way to make reload() behave
# nicely. if i make these attributes dynamic lookups in
# __getattr__, reload sometimes chokes in weird ways...
for attr in ["__builtins__", "__doc__", "__name__", "__package__"]:
setattr(self, attr, getattr(self_module, attr))
self.self_module = self_module
self.env = Environment(globals())
def __getattr__(self, name):
return self.env[name]
!
sys.modules
现在我已经看到了# we're being run as a stand-alone script, fire up a REPL
if __name__ == "__main__":
globs = globals()
f_globals = {}
for k in ["__builtins__", "__doc__", "__name__", "__package__"]:
f_globals[k] = globs[k]
env = Environment(f_globals)
run_repl(env)
# we're being imported from somewhere
else:
self = sys.modules[__name__]
sys.modules[__name__] = SelfWrapper(self)
必须经历的长度,我想知道为什么Python的这个功能没有直接内置到语言中。特别是pbs
装饰器似乎是添加此类功能的自然场所。
为什么不直接建立这个原因还有任何原因或动机吗?
答案 0 :(得分:8)
这与两个因素的组合有关:第一,使用descriptor protocol实现属性,第二,模块始终是特定类的实例,而不是可实例化的类。
描述符协议的这一部分在object.__getattribute__
中实现(相关代码是PyObject_GenericGetAttr
,从第1319行开始)。查找规则如下:
mro
中搜索包含name
__get__
并返回其结果__get__
并返回结果raise AttributeError
关键是第3位 - 如果在实例字典中找到name
(就像模块一样),那么它的值将被返回 - 它赢了不测试描述符,并且不会调用它的__get__
。这导致了这种情况(使用Python 3):
>>> class F:
... def __getattribute__(self, attr):
... print('hi')
... return object.__getattribute__(self, attr)
...
>>> f = F()
>>> f.blah = property(lambda: 5)
>>> f.blah
hi
<property object at 0xbfa1b0>
您可以看到.__getattribute__
被调用,但不将f.blah
视为描述符。
规则以这种方式构造的原因很可能是在实例上允许描述符(以及因此在模块中)的有用性与这将导致的额外代码复杂性之间的明确权衡。
答案 1 :(得分:0)
属性是特定于类的特性(具体是新风格类),因此通过扩展,属性修饰器只能应用于类方法。
新式类是从对象派生的类,即类Foo(对象):
更多信息:Can modules have properties the same way that objects can?