Python类名作为类变量

时间:2015-05-21 18:49:38

标签: python python-3.x class-variables

我正在使用类和子类作为应用程序。对于每个类,super和sub,都有一个名为label的类变量。我希望超类的label变量默认为类名。例如:

class Super():
    label = 'Super'

class Sub(Super):
    label = 'Sub'

不是手动为每个类输出变量,是否可以从超类中的类名派生变量并自动为子类填充?

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

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

class Super():
    label = # Magic

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

4 个答案:

答案 0 :(得分:8)

您可以在标签中将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__

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

要访问类方法中的类名,只需在__name__

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

print XYZ.my_label()

这个解决方案也可能有用(从https://stackoverflow.com/a/13624858/541038获取)

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

答案 1 :(得分:4)

您可以使用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',那么您可以通过更多工作来实现。在类级别分配label将被继承,除非你使用一个元类(这比它更值得讨厌),但是我们可以找到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'

答案 2 :(得分:4)

元类在这里可能很有用。

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

免责声明:为您的班级提供自定义元类时,您需要确保它与其祖先中任何类使用的任何元类都兼容。通常,这意味着确保您的元类继承自所有其他元类,但这样做可能非常重要。实际上,元类并不常用,因此通常只需要对type进行子类化,但这是需要注意的事项。

答案 3 :(得分:0)

从 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