如果我有这个:
class foo(object):
@property
def bar(self):
return 0
f = foo()
如果没有实际调用方法,如何获得对f.bar的引用,如果可能的话?
编辑添加:我想要做的是编写一个迭代f成员的函数并对它们做一些事情(不重要)。属性让我失望,因为只需在getattr()中命名它们就会调用它们的__get __()方法。
答案 0 :(得分:35)
get_dict_attr
(下方)在给定对象的attr
中查找__dict__
,并在其中返回相关值。如果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)
被调用。
详细解释here。
答案 1 :(得分:1)
在你的情况下,
class foo(object):
@property
def bar(self):
return 0
f = foo()
您可以通过班级字典访问该媒体资源:
f.__class__.__dict__['bar']
=> <property at 0x4920c28>
如您所见,这是一个property
实例。
isinstance(f.__class__.__dict__['bar'], property)
=> True
您可以通过return 0
访问其getter方法(fget
),如下所示:
f.__class__.__dict__['bar'].fget(f)
=> 0
请注意,您必须将实例f
提供给fget()
,因为它是一种实例方法(非静态),您可以通过__class__
访问它。
答案 2 :(得分:1)
我遇到了类似的情况,其他答案都没有帮助我,所以这是另一种方法。
我的情况略有不同,因为我不是只有一个@property
,而是混合了一个自定义装饰器,它为包装方法添加了属性。所以通常我会有这样的事情:
class Foo:
@my_decorator
def bar(self):
return do_stuff() # Pretend this is non-trivial
我的@my_decorator
会让我访问self.bar.my_attribute
,其中包含我需要访问的某些数据。这对于函数和方法很有用,但是在尝试将它与@property
混合时遇到了麻烦,因为该属性隐藏了对方法的引用(self.bar.my_attribute
失败,因为self.bar
返回了返回值该方法没有我想要的my_attribute
。
我使用@property
作为外部装饰器,如下所示:
class Foo:
@property
@my_decorator
def bar(self):
return do_stuff() # Pretend this is non-trivial
然后解决方案是访问my_attribute
作为Foo.bar.fget.my_attribute
(这里重要的细节是从类中访问属性返回属性对象,而从实例访问属性只调用方法并返回值,无需访问原始方法的引用。)
TL; DR:如果您需要引用提供@property
的方法,则需要使用YourClassName.your_property.fget
。
答案 3 :(得分:0)
您应该知道的一件事是,数据描述符(即属性)仅在应用于(新式)类时才有效(请参阅http://docs.python.org/reference/datamodel.html#implementing-descriptors)。将它们复制到对象不会在该对象上创建属性。您需要将它们复制到(新式)类才能生效。
答案 4 :(得分:0)
class A:
prop = property(lambda self: 'get', lambda self, x: print('set', x))
setter = A.prop.fset
getter = A.prop.fget
my_a = A()
setter(my_a, 5)
print(getter(my_a))
答案 5 :(得分:-1)
我会说忽略其他答案,因为它们很糟糕。
您可以通过foo.bar
关于你试图对f
成员进行迭代的内容,请参阅下面的具体内容。
答案很长:你必须要了解的是Python中的方法不属于实例,而是类对象的属性。例如,
class Foo:
def bar(self):
pass
foo = Foo()
如果您致电foo.bar()
,Python首先会检查foo
是否有bar
(它没有),然后检查Foo
是否bar
它确实如此。然后,Python将foo.bar
“绑定”到Foo.bar(foo)
,并调用它。
属性也是如此。我不知道确切的过程(我想它在实现之间有所不同),但基本上Python查找foo.bar
,然后Foo.bar
,看到它是一个属性,所以评估它的{{1}并返回。