mva*_*een 10 python properties built-in
tl; dr:属性装饰器如何使用类级函数定义,而不是模块级定义?
我正在将属性修饰器应用于某些模块级函数,认为它们允许我通过属性查找来调用方法.
这是特别诱人的,因为我定义的配置功能集合,像get_port,get_hostname等等,所有这些都可能被替换的更简单,更简洁的财产同行:port,hostname,等.
因此,config.get_port()只会更好config.port
当我发现以下追溯时,我感到很惊讶,证明这不是一个可行的选择:
TypeError: int() argument must be a string or a number, not 'property'
Run Code Online (Sandbox Code Playgroud)
我知道我已经在模块级别看到了类似属性的功能,因为我使用它来使用优雅但hacky pbs库编写shell命令脚本.
下面有趣的黑客可以在pbs库源代码中找到.它使得能够在模块级别进行类似属性的属性查找,但它非常可怕,非常可怕.
# 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]
Run Code Online (Sandbox Code Playgroud)
下面是将此类插入导入名称空间的代码.它实际上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)
Run Code Online (Sandbox Code Playgroud)
现在我已经看到了pbs必须经历的长度,我想知道为什么Python的这个功能没有直接内置到语言中.该property特别装饰似乎是一个自然的地方加入这样的功能.
为什么不直接建立这个原因还有任何部分原因或动机吗?
这与两个因素的组合有关:第一,使用描述符协议实现属性,第二,模块始终是特定类的实例,而不是可实例化的类.
描述符协议的这部分在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>
Run Code Online (Sandbox Code Playgroud)
你可以看到,.__getattribute__ 在被调用,但没有治疗f.blah的描述.
规则以这种方式构造的原因很可能是在实例上允许描述符(以及因此在模块中)的有用性与这将导致的额外代码复杂性之间的明确权衡.