我可以获得Python属性的引用吗?

kin*_*all 46 python properties

如果我有这个:

class foo(object):
    @property
    def bar(self):
        return 0

f = foo()
Run Code Online (Sandbox Code Playgroud)

如果可能的话,如何在不实际调用方法的情况下获得对f.bar的引用?

编辑添加:我想要做的是编写一个函数,迭代f的成员并对它们做一些事情(什么是不重要的).属性让我失望,因为只需在getattr()中命名它们就会调用它们的__get __()方法.

unu*_*tbu 37

get_dict_attr(下面)attr在给定对象中查找__dict__,并返回相关值(如果在那里).如果attr不是那里的关键,则搜索__dict__对象的MRO __dict__.如果找不到密钥,AttributeError则会引发a.

def get_dict_attr(obj, attr):
    for obj in [obj] + obj.__class__.mro():
        if attr in obj.__dict__:
            return obj.__dict__[attr]
    raise AttributeError
Run Code Online (Sandbox Code Playgroud)

例如,

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
Run Code Online (Sandbox Code Playgroud)

请注意,这与属性查找的常规规则非常不同.首先,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()
Run Code Online (Sandbox Code Playgroud)

这引用了该属性bar:

print(Foo.bar)
# <property object at 0xb76d1d9c>
Run Code Online (Sandbox Code Playgroud)

你看到的bar是一个关键Foo.__dict__:

print(Foo.__dict__['bar'])
# <property object at 0xb775dbbc>
Run Code Online (Sandbox Code Playgroud)

所有属性都是描述符,这意味着它有一个__get__方法:

print(Foo.bar.__get__)
# <method-wrapper '__get__' of property object at 0xb76d7d74>
Run Code Online (Sandbox Code Playgroud)

您可以通过传递对象ff作为参数的类来调用该方法:

print(Foo.bar.__get__(f,Foo))
# 0
Run Code Online (Sandbox Code Playgroud)

我喜欢下面的图表.垂直线表示对象与对象类之间的关系.

遇到这种情况时:

   Foo                                B
   | Foo.__dict__={'bar':b}           | B.__dict__={'__get__':...}
   |                      \           |      
   f                       `--------> b
Run Code Online (Sandbox Code Playgroud)

f.bar导致b.__get__(f,Foo)被调用.

这在详细解释这里.


flo*_*sla 6

就你而言,

class Foo(object):
    @property
    def bar(self):
        return 0
f = Foo()
Run Code Online (Sandbox Code Playgroud)

您可以通过 Foo 类访问该属性。这些都是等价的:

Foo.bar
Foo.__dict__['bar']
f.__class__.__dict__['bar']
=> <property at 0x4920c28>
Run Code Online (Sandbox Code Playgroud)

正如您所看到的,这是一个property实例。在属性上,您有一个调用的方法fget,该方法返回“getter”值。

isinstance(Foo.bar, property)
=> True
Foo.bar.fget
=> <function Foo.bar at 0x0000014DC0CF21F0>
Run Code Online (Sandbox Code Playgroud)

您可以直接调用该函数(实例方法)。Foo.bar.fget(f) => 0

注意:您必须向自己提供对象fself参数)fget,因为当通过类访问时,该类还不知道实例f。换句话说,实例方法Foo.bar.fget尚未“绑定”到f.


rob*_*bru 6

我遇到了与此类似的情况,其他答案都没有帮助我,所以这是另一种方法。

我的情况略有不同,因为我不是只有一个@property,而是混合了一个自定义装饰器,该装饰器向包装的方法添加属性。所以例如通常我会有这样的东西:

class Foo:
    @my_decorator
    def bar(self):
        return do_stuff()  # Pretend this is non-trivial
Run Code Online (Sandbox Code Playgroud)

然后我@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
Run Code Online (Sandbox Code Playgroud)

然后解决方案是访问my_attributeas Foo.bar.fget.my_attribute(这里重要的细节是从类访问属性返回属性对象,而从实例访问属性只是调用方法并返回值,而不访问原始引用方法)。

TL;DR:如果您需要对提供 的方法的引用@property,则需要使用YourClassName.your_property.fget.


ADR*_*ADR 5

简短的回答:

属性在从类中调用时返回自身: MyClass.my_prop

此外,它们的字段包含指向实际方法的链接:fgetfsetfdel

描述:

所以,my_class.my_prop(where my_class = MyClass()) 返回值,但MyClass.my_prop返回属性对象并MyClass.my_prop.fget返回此属性的 getter 方法。在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)
Run Code Online (Sandbox Code Playgroud)

  • 虽然此代码片段可能会解决问题,但 [包括解释](//meta.stackexchange.com/questions/114762/explaining-entirely-code-based-answers) 确实有助于提高帖子的质量。请记住,您是在为将来的读者回答问题,而那些人可能不知道您提出代码建议的原因。也请尽量不要在代码中添加解释性注释,这会降低代码和解释的可读性! (2认同)