如何覆盖 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']
我正在尝试创建一个 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']