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

70 投票
7 回答
28304 浏览
提问于 2025-04-16 03:56

如果我有这个:

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

f = foo()

我怎么能在不实际调用这个方法的情况下,获取到 f.bar 的引用呢?这可能吗?

补充说明:我想写一个函数,遍历 f 的成员,并对它们做一些处理(具体做什么不重要)。但是属性让我困惑,因为在 getattr() 中仅仅提到它们就会调用它们的 __get__() 方法。

7 个回答

6

在你的情况下,

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 绑定在一起。

9

简短回答:

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

同时,它们还有一些字段,里面包含指向实际方法的链接:fgetfsetfdel

详细说明:

所以,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)
51

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>

你可以看到 barFoo.__dict__ 中的一个键:

print(Foo.__dict__['bar'])
# <property object at 0xb775dbbc>

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

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

你可以通过传入对象 ff 的类作为参数来调用这个方法:

print(Foo.bar.__get__(f,Foo))
# 0

我喜欢下面这个图。垂直线表示对象和对象类之间的关系。

当你遇到这种情况时:

   Foo                                B
   | Foo.__dict__={'bar':b}           | B.__dict__={'__get__':...}
   |                      \           |      
   f                       `--------> b

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

详细解释可以在 这里找到。

撰写回答