使用py3,我有一个使用@property装饰器的对象
class O(object):
def __init__(self):
self._a = None
@property
def a(self):
return 1
通过 dict (使用_a)访问属性a似乎没有返回属性修饰值但是初始化值无
o = O()
print(o.a, o__dict__['_a'])
>>> 1, None
有没有通用的方法来使这项工作?
我最需要这个def __str__(self):
return ' '.join('{}: {}'.format(key, val) for key, val in self.__dict__.items())
答案 0 :(得分:3)
当然self.__dict__["_a"]
会返回self._a
(实际上反之亦然 - self._a
将返回self.__dict__["_a"]
- 但无论如何),而不是{{ 1}}。 self.a
在这里做的唯一事情是自动调用它的getter(你的property
函数),这样你就不必输入parens,否则它就是只是简单的方法调用。
如果你想要的东西也适用于属性,你必须从a(self)
和dir(self.__class__)
手动获取,即:
getattr(self.__class__, name)
请注意,这不会阻止def __str__(self):
# py2
attribs = self.__dict__.items()
# py3
# attribs = list(self.__dict__.items())
for name in dir(self.__class__):
obj = getattr(self.__class__, name)
if isinstance(obj, property):
val = obj.__get__(self, self.__class__)
attribs.append((name, val))
return ' '.join('{}: {}'.format(key, val) for key, val in attribs)
显示在_a
中 - 如果您想避免这种情况,您还必须从{{1}过滤掉受保护的名称} list(所有受保护的名称,因为你要求通用的东西):
attribs
另请注意,这不会处理其他计算属性(attribs
只是描述符协议的一个通用实现)。在这一点上,你最好的选择仍然是尽可能通用,但如果需要可以自定义,可以将上面的内容实现为一个带有几个专业化钩子的mixin类:
def __str__(self):
attribs = [(k, v) for k, v in self.__dict__.items() if not k.startswith("_")]
for name in dir(self.__class__):
# a protected property is somewhat uncommon but
# let's stay consistent with plain attribs
if name.startswith("_"):
continue
obj = getattr(self.__class__, name)
if isinstance(obj, property):
val = obj.__get__(self, self.__class__)
attribs.append((name, val))
return ' '.join('{}: {}'.format(key, val) for key, val in attribs)
答案 1 :(得分:1)
属性基本上是"计算属性"。通常,属性的值不存储在任何地方,它是按需计算的。这就是为什么你无法在__dict__
中找到它。
@property
装饰器用描述符对象替换类方法,然后将原始方法作为其getter调用。这发生在班级。
o.a
的查找从实例开始。它不存在,下一步检查类。 O.a
存在并且是描述符(因为它具有描述符协议的特殊方法),因此调用描述符的getter并使用返回的值。
(编者) 没有通用的方法来转储名称:描述符的值对。必须检查包括基础在内的类,这部分并不困难。但是,检索值等同于函数调用,并且可能具有意外和不期望的副作用。对于不同的观点,我想在这里引用bruno desthuilliers的评论:&#34; 属性get不应该有不必要的副作用(如果它确实那么那么明显的设计错误)< / EM>&#34;
答案 2 :(得分:0)
您还可以将self._a
更新为getter,因为getter的返回应始终反映存储的self._a
:
class O(object):
def __init__(self):
self._a = self.a
@property
def a(self):
self._a = 1
return self._a
也许有点多余,但是在这种情况下,最初设置self._a = None
没用。
如果删除getter的第一行,这也将是兼容的:
@a.setter
def a(self, value):
self._a = value