gpt4 book ai didi

python - 为什么只为类定义属性装饰器?

转载 作者:太空狗 更新时间:2023-10-29 18:31:46 24 4
gpt4 key购买 nike

tl;dr:属性装饰器如何与类级函数定义一起使用,而不是与模块级定义一起使用?

我将属性装饰器应用于一些模块级函数,认为它们可以让我仅通过属性查找来调用这些方法。

这特别诱人,因为我正在定义一组配置函数,如 get_portget_hostname 等,所有这些都可以用更简单的方式替换,更简洁的属性对应物:porthostname

因此,config.get_port() 会是更好的config.port

当我发现以下回溯时,我很惊讶,证明这不是一个可行的选择:

TypeError: int() argument must be a string or a number, not 'property'

我知道我已经在模块级别看到了一些类似属性的功能的先例,因为我曾用它来编写 shell 命令脚本,使用了优雅但很老套的 pbs library .

下面有趣的 hack 可以在 pbs library source code 中找到.它支持在模块级别执行类似属性的属性查找,但它非常非常骇人听闻。

# 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)

既然我已经了解了 pbs 必须经历的长度,我想知道为什么 Python 的这种功能没有直接内置到语言中。 property 装饰器似乎特别适合添加此类功能。

为什么不直接内置它有任何特殊原因或动机吗?

最佳答案

这与两个因素的组合有关:首先,属性是使用 descriptor protocol 实现的,其次,模块始终是特定类的实例,而不是可实例化的类。

这部分描述符协议(protocol)在object.__getattribute__中实现(相关代码是从第 1319 行开始的 PyObject_GenericGetAttr)。查找规则如下:

  1. 在类 mro 中搜索具有 name 的类型字典
  2. 如果第一个匹配项是一个数据描述符,调用它的__get__并返回它的结果
  3. 如果名字在实例字典中,返回它的关联值
  4. 如果类字典中有一个匹配项并且它是一个非数据描述符,则调用它的__get__ 并返回结果
  5. 如果类词典中有匹配项,则将其返回
  6. 引发 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 视为一个描述符。

规则以这种方式构建的原因很可能是在实例上(因此在模块中)允许描述符的有用性和这将导致的额外代码复杂性之间的明确权衡。

关于python - 为什么只为类定义属性装饰器?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11752534/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com