Python Class 命名为 Class 变量

Python Class Name as Class Variable

我正在使用 classes 和 subclasses 的应用程序。对于每个 class,包括 super 和 sub,都有一个名为 label 的 class 变量。我希望超级 class 的 label 变量默认为 class 名称。例如:

class Super():
    label = 'Super'

class Sub(Super):
    label = 'Sub'

与其手动输入每个 class 的变量,是否可以从超级 class 中的 class 名称派生变量并自动填充subclasses?

class Super():
    label = # Code to get class name

class Sub(Super)
    pass
    # When inherited Sub.label == 'Sub'.

原因是这将是默认行为。我也希望如果我可以获得默认行为,我可以稍后通过指定替代 label.

来覆盖它
class SecondSub(Super):
    label = 'Pie'  # Override the default of SecondSub.label == 'SecondSub'

我试过使用 __name__,但它不起作用,只给了我 '__main__'

我想在 @classmethod 方法中使用 class 变量 label。所以我希望能够引用该值而不必实际创建 Super() 或 Sub() 对象,如下所示:

class Super():
    label = # Magic

    @classmethod
    def do_something_with_label(cls):
        print(cls.label)

您可以 return self.__class__.__name__ 在标签中作为 属性

class Super:
    @property
    def label(self):
        return self.__class__.__name__

class Sub(Super):
   pass

print Sub().label

或者您可以在 __init__ 方法中设置它

def __init__(self):
    self.label = self.__class__.__name__

这显然只适用于实例化的 classes

要在 class 方法中访问 class 名称,您只需在 cls

上调用 __name__
class XYZ:
    @classmethod
    def my_label(cls):
        return cls.__name__

print XYZ.my_label()

这个解决方案也可能有效(摘自 )

class classproperty(object):
    def __init__(self, fget):
        self.fget = fget
    def __get__(self, owner_self, owner_cls):
        return self.fget(owner_cls)

class Super(object): 
    @classproperty
    def label(cls):
        return cls.__name__

class Sub(Super):
   pass

print Sub.label  #works on class
print Sub().label #also works on an instance

class Sub2(Sub):
   @classmethod
   def some_classmethod(cls):
       print cls.label

Sub2.some_classmethod()

您可以使用 descriptor:

class ClassNameDescriptor(object):
    def __get__(self, obj, type_):
        return type_.__name__

class Super(object):
    label = ClassNameDescriptor()

class Sub(Super):
    pass

class SecondSub(Super):
    label = 'Foo'

演示:

>>> Super.label
'Super'
>>> Sub.label
'Sub'
>>> SecondSub.label
'Foo'
>>> Sub().label
'Sub'
>>> SecondSub().label
'Foo'

如果 class ThirdSub(SecondSub) 应该有 ThirdSub.label == 'ThirdSub' 而不是 ThirdSub.label == 'Foo',您可以多做一些工作。在 class 级别分配 label 将被继承,除非你使用元 class (这比它的价值要麻烦得多),但我们可以让 label 描述符改为查找 _label 属性:

class ClassNameDescriptor(object):
    def __get__(self, obj, type_):
        try:
            return type_.__dict__['_label']
        except KeyError:
            return type_.__name__

演示:

>>> class SecondSub(Super):
...     _label = 'Foo'
...
>>> class ThirdSub(SecondSub):
...     pass
...
>>> SecondSub.label
'Foo'
>>> ThirdSub.label
'ThirdSub'

metaclass 在这里可能会有用。

class Labeller(type):
    def __new__(meta, name, bases, dct):
        dct.setdefault('label', name)
        return super(Labeller, meta).__new__(meta, name, bases, dct)

# Python 2
# class Super(object):
#    __metaclass__ = Labeller

class Super(metaclass=Labeller):
    pass

class Sub(Super):
    pass

class SecondSub(Super):
    label = 'Pie'

class ThirdSub(SecondSub):
    pass

免责声明:在为您的 class 提供自定义元 class 时,您需要确保它与任何 [= 使用的任何元 class(es) 兼容16=] 在其祖先中。通常,这意味着确保您的元class 继承自所有其他元class,但这样做并不容易。在实践中,metaclasses 并不常用,所以它通常只是一个 subclassing type 的问题,但这是需要注意的事情。

从 Python 3.6 开始,实现这一点的最干净的方法是使用 PEP 487 中引入的 __init_subclass__ 钩子。它比使用元类更简单(并且更容易管理继承)。

class Base:
    @classmethod
    def __init_subclass__(cls, **kwargs):
        super().__init_subclass__(**kwargs)
        if 'label' not in cls.__dict__:  # Check if label has been set in the class itself, i.e. not inherited from any of its superclasses
            cls.label = cls.__name__  # If not, default to class's __name__

class Sub1(Base):
    pass
    
class Sub2(Base):
    label = 'Custom'

class SubSub(Sub2):
    pass

print(Sub1.label)    # Sub1
print(Sub2.label)    # Custom
print(SubSub.label)  # SubSub