如何覆盖 class 属性的分配

How to override assignment of class attributes

我正在尝试创建一个 class,其中 return 是 class 名称和属性。这需要同时使用实例属性和 class 属性

class TestClass:
    obj1 = 'hi'

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

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

使用python中的Enum包也有类似的效果,但是如果我继承了Enum,我就不能创建我想做的__init__函数

如果我使用枚举,我会得到:

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

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

我已经尝试按照此处的建议覆盖元 class 中的 __getattribute__ 魔法方法:How can I override class attribute access in python。然而,这打破了 __dir__ 魔术方法,它不会 return 任何东西,而且它似乎 return 元 class 的名称,而不是子 [=37] =].示例如下:

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__ 魔术方法,但这似乎只影响实例属性,而不影响 class 属性。

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

方式一

您可以使用 classmethod 装饰器来定义整个 class:

可调用的方法
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 装饰器,这样特定 class 的实例而不是 class 本身就可以访问已删除的输出:

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')

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

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']