获取Python中类和实例的属性

3 投票
1 回答
7473 浏览
提问于 2025-04-17 18:56

在Python中,下面的代码可以工作:

class MyClass(object):
    field = 1

>>> MyClass.field
1

>>> MyClass().field
1

当我想要返回自定义字段的值时,我会使用下面的代码:

class MyClass(object):
    def __getattr__(self, name):
       if name.startswith('fake'):
           return name
       raise AttributeError("%r object has no attribute %r" %
                            (type(self).__name__, name))

>>> MyClass().fake
fake

但是:

>>> MyClass.fake
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: class MyClass has no attribute 'fake'

好吧,对于类,我可以使用下面的代码:

class MyClassMeta(type):
    def __getattr__(cls, name):
       if name.startswith('fake'):
           return name
       raise AttributeError("%r object has no attribute %r" %
                            (type(self).__name__, name))

class MyClass(object):
    __metaclass__ = MyClassMeta

>>> MyClass.fake
fake

但是:

>>> MyClass().fake
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'MyClass' object has no attribute 'fake'

为了解决这个问题,我使用下面的代码:

class FakeAttrMixin():
   def __getattr__(self, name):
       if name.startswith('fake'):
           return name
       raise AttributeError("%r object has no attribute %r" %
                            (type(self).__name__, name))

class MyClassMeta(type, FakeAttrMixin):
    pass

class MyClass(object, FakeAttrMixin):
    __metaclass__ = MyClassMeta

>>> MyClass.fake
fake

>>> MyClass().fake
fake

MyClass.fake 会调用 __getattr__,传入 MyClassfake 作为参数。

MyClass().fake 会调用 __getattr__,传入 MyClass 的实例和 fake 作为参数。

如果我只在我的混合类中实现 __getattr__ 的逻辑,而不使用 self 参数,这样是可以的。

我能否以更优雅的方式编写类和实例的自定义值解析?为什么 MyClass.fieldMyClass().fieldfield 值解析在定义为 MyClass(object): field = 1 时表现不同,而与 __getattr__ 方法相比?因为当我想获取 field 时,它首先会在实例中查找,然后在类中查找,但我不明白为什么 __getattr__ 的工作方式不同。

类似的问题有: __getattr__ 在类和实例上的区别 以及 访问实例属性和类属性的区别

1 个回答

3

不,如果你需要同时支持在类和实例上查找任意属性,那你唯一的办法就是在元类和类上各实现一个 __getattr__ 钩子方法,一个用来支持类的查找,另一个用来支持实例的查找。

这是因为特殊的钩子方法总是会在类型上查找,所以会用到 type(obj).__getattr__。因此,对于 MyClass.fake,会使用元类的 __getattr__。你可以查看 新风格类的特殊方法查找;我在 之前的回答 中解释过为什么会这样。

简单来说,在你的情况下,MyClass.fake 会变成 MyClass.__getattr__('fake'),而 __getattr__ 是一个未绑定的方法,它需要两个参数(selfname),这样就会出错。

撰写回答