我可以获取Python属性的引用吗?
如果我有这个:
class foo(object):
@property
def bar(self):
return 0
f = foo()
我怎么能在不实际调用这个方法的情况下,获取到 f.bar 的引用呢?这可能吗?
补充说明:我想写一个函数,遍历 f 的成员,并对它们做一些处理(具体做什么不重要)。但是属性让我困惑,因为在 getattr() 中仅仅提到它们就会调用它们的 __get__() 方法。
7 个回答
在你的情况下,
class Foo(object):
@property
def bar(self):
return 0
f = Foo()
你可以通过类 Foo 来访问这个属性。这些方式都是一样的:
Foo.bar
Foo.__dict__['bar']
f.__class__.__dict__['bar']
=> <property at 0x4920c28>
如你所见,这就是一个 property
实例。在这个属性上,有一个叫 fget
的方法,它会返回“获取器”的值。
isinstance(Foo.bar, property)
=> True
Foo.bar.fget
=> <function Foo.bar at 0x0000014DC0CF21F0>
你可以直接调用这个函数(一个实例方法)。
Foo.bar.fget(f) => 0注意:你需要自己提供对象 f
(也就是 self
参数)给 fget
,因为通过类来访问时,类并不知道实例 f
。换句话说,实例方法 Foo.bar.fget
还没有和 f
绑定在一起。
简短回答:
属性在从类中调用时会返回它自己:MyClass.my_prop
同时,它们还有一些字段,里面包含指向实际方法的链接:fget
、fset
和 fdel
。
详细说明:
所以,my_class.my_prop
(其中 my_class = MyClass()
)会返回一个值,但 MyClass.my_prop
会返回属性对象,而 MyClass.my_prop.fget
会返回这个属性的获取方法。这里的 self
并没有直接关联,所以在调用时需要填充:MyClass.my_prop.fget(my_class)
示例:
class MyClass:
my_prop = property(lambda self: 'get', lambda self, x: print('set', x))
setter = MyClass.my_prop.fset
getter = MyClass.my_prop.fget
my_class = MyClass()
setter(my_class, 5) # equals my_class.my_prop = 5
print(getter(my_class)) # equals print(my_class.my_prop)
get_dict_attr
(下面的代码)是用来查找一个对象的 __dict__
中的 attr
属性,如果找到了就返回对应的值。如果 attr
不是这个 __dict__
中的一个键,就会去查找这个对象的 MRO(方法解析顺序)中的 __dict__
。如果还是找不到,就会抛出一个 AttributeError
错误。
def get_dict_attr(obj, attr):
for obj in [obj] + obj.__class__.mro():
if attr in obj.__dict__:
return obj.__dict__[attr]
raise AttributeError
举个例子,
class Foo(object):
x=1
def bar(self):
pass
@property
def baz(self):
return 0
foo=Foo()
print(get_dict_attr(foo,'x'))
# 1
print(get_dict_attr(foo,'bar'))
# <unbound method Foo.bar>
print(get_dict_attr(foo,'baz'))
# <property object at 0xb77c0dc4>
print(get_dict_attr(foo,'y'))
# AttributeError
需要注意的是,这和正常的属性查找规则是很不同的。首先,在 obj.__class__.__dict__
中的数据描述符(同时有 __get__
和 __set__
方法的描述符)通常会优先于 obj.__dict__
中的值。而在 get_dict_attr
中,obj.__dict__
是优先的。
get_dict_attr
不会尝试调用 __getattr__
。
最后,get_dict_attr
只适用于新式类的对象 obj
。
不过,我希望这些信息能对你有所帮助。
class Foo(object):
@property
def bar(self):
return 0
f = Foo()
这段代码引用了属性 bar
:
print(Foo.bar)
# <property object at 0xb76d1d9c>
你可以看到 bar
是 Foo.__dict__
中的一个键:
print(Foo.__dict__['bar'])
# <property object at 0xb775dbbc>
所有属性都是描述符,这意味着它有一个 __get__
方法:
print(Foo.bar.__get__)
# <method-wrapper '__get__' of property object at 0xb76d7d74>
你可以通过传入对象 f
和 f
的类作为参数来调用这个方法:
print(Foo.bar.__get__(f,Foo))
# 0
我喜欢下面这个图。垂直线表示对象和对象类之间的关系。
当你遇到这种情况时:
Foo B
| Foo.__dict__={'bar':b} | B.__dict__={'__get__':...}
| \ |
f `--------> b
f.bar
会导致调用 b.__get__(f,Foo)
。
详细解释可以在 这里找到。