【问题标题】:How to override assignment of class attributes如何覆盖类属性的分配
【发布时间】:2018-09-05 17:46:04
【问题描述】:

我正在尝试创建一个返回类名和属性的类。这需要同时使用实例属性和类属性

class TestClass:
    obj1 = 'hi'

即我想要以下(注意:有和没有类实例化)

>>> TestClass.obj1
('TestClass', 'hi')
>>> TestClass().obj1
('TestClass', 'hi')

在python中使用Enum包也可以得到类似的效果,但是如果我从Enum继承,就无法创建__init__函数,我也想做

如果我使用 Enum,我会得到:

from enum import Enum
class TestClass2(Enum):
    obj1 = 'hi'

>>> TestClass2.obj1
<TestClass2.obj1: 'hi'>

我已经尝试过在元类中重写 __getattribute__ 魔术方法,如下所示:How can I override class attribute access in python。但是,这破坏了__dir__ 魔术方法,该方法不会返回任何内容,而且它似乎返回元类的名称,而不是子类的名称。示例如下:

class BooType(type):
    def __getattribute__(self, attr):
        if attr == '__class__':
            return super().__getattribute__(attr)
        else:
            return self.__class__.__name__, attr

class Boo(metaclass=BooType):
    asd = 'hi'

print(Boo.asd)
print(dir(Boo))

我也尝试过覆盖 __setattr__ 魔术方法,但这似乎只影响实例属性,而不影响类属性。

我应该声明我正在寻找一个通用的解决方案。不是我需要为每个属性编写 @property@classmethod 函数或类似的东西

【问题讨论】:

  • 您可以简单地使用一些新方法,例如.view() 返回您想要的输出。似乎没有必要重载 gettrib,使用 Enum 也是如此。
  • 问题是你要实现的行为是由每个属性本身提供的,而不是TestClass。您可以使用元类 __prepare__ 方法来“预处理”您的类属性(例如,使它们成为某个自定义描述符的所有实例)。可以使用__setattr__ 而不是__prepare__ 类似地处理实例属性。
  • 对,所以我需要通过一个元类来定义这个行为。使用__prepare__ 意味着不是返回值,而是返回带有该值的字典?我想这可能会起作用,即使它不会像我的示例中所示将元组归还给我。通过__prepare__ 是最简单的,还是使用__new__ 也可以?

标签: python metaclass


【解决方案1】:

方式 1

您可以使用classmethod 装饰器来定义可在整个类中调用的方法:

class TestClass:
    _obj1 = 'hi'

    @classmethod
    def obj1(cls):
        return cls.__name__, cls._obj1


class TestSubClass(TestClass):
    pass


print(TestClass.obj1())
# ('TestClass', 'hi')
print(TestSubClass.obj1())
# ('TestSubClass', 'hi')

方式 2

也许你应该使用property 装饰器,这样某个类的实例而不是类本身就可以访问disered 输出:

class TestClass:
    _obj1 = 'hi'

    @property
    def obj1(self):
        return self.__class__.__name__, self._obj1


class TestSubClass(TestClass):
    pass

a = TestClass()
b = TestSubClass()

print(a.obj1)
# ('TestClass', 'hi')
print(b.obj1)
# ('TestSubClass', 'hi')

【讨论】:

  • 我知道这是一个老回复。我最近刚刚编辑了我的问题,以便更清楚地了解我在寻找什么,不幸的是,由于需要为每个属性编写一个 @property@classmethod 函数,这个答案并没有解决我的问题
【解决方案2】:

我从一位同事那里得到了定义元类的帮助,并提出了以下解决方案

class MyMeta(type):
    def __new__(mcs, name, bases, dct):
        c = super(MyMeta, mcs).__new__(mcs, name, bases, dct)
        c._member_names = []
        for key, value in c.__dict__.items():
            if type(value) is str and not key.startswith("__"):
                c._member_names.append(key)
                setattr(c, key, (c.__name__, value))
        return c

    def __dir__(cls):
        return cls._member_names

class TestClass(metaclass=MyMeta):
    a = 'hi'
    b = 'hi again'

print(TestClass.a)
# ('TestClass', 'hi')
print(TestClass.b)
# ('TestClass', 'hi again')
print(dir(TestClass))
# ['a', 'b']

【讨论】:

    猜你喜欢
    • 2012-08-02
    • 1970-01-01
    • 1970-01-01
    • 2016-11-06
    • 2021-12-30
    • 2021-09-14
    • 1970-01-01
    • 2013-04-05
    • 1970-01-01
    相关资源
    最近更新 更多