2015-05-21 4 views
2

Я работаю как приложение с классами и подклассами. Для каждого класса, как супер, так и sub, существует переменная класса, называемая label. Мне нужна переменная label для суперкласса по умолчанию для имени класса. Например:Имя класса Python как переменная класса

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. Поэтому я хотел бы, чтобы иметь возможность ссылаться на значение, не имея на самом деле создать супер() или Sub() объект, как показано ниже:

class Super(): 
    label = # Magic 

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

Вы должны были указать, что вам нужно, чтобы он работал с неинтересными классами. вы бы получили более правильное решение раньше –

+0

Вы правы, я должен был. Сейчас это исправит. – user2004245

ответ

3

Здесь может быть полезен метакласс.

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, но это то, о чем нужно знать.

+0

Мне нравится видеть хорошие применения метаклассов –

+0

Не могли бы вы указать на некоторое объяснение, почему 'super (Labeller, meta)' должно использоваться вместо просто 'super()'? – mkrieger1

+0

В Python 3 появилась возможность использования 'super' без явных аргументов; Я более привык к Python 2 и добавил аргументы по привычке. – chepner

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

Я просто запустился на ipython, чтобы попытаться использовать dir (self) внутри класса, когда вы добавили ответ. Браво! Я узнал что-то новое! –

+0

Смогу ли я снова ссылаться на свойство экземпляра внутри метода @classmethod? – user2004245

+0

@ user2004245 'self .__ class__' может быть оценен только * после того, как * класс был полностью определен, поэтому вы можете получить метку во время выполнения, но не во время разработки класса (так что вы можете использовать его * внутри * метод, но не как значение атрибута класса). – poke

4

Вы можете использовать a 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' 
+0

отличный ответ, и вы избили мое редактирование, которое делает это также (+1 от меня хороший сэр :)) –

+0

Существует определенная асимметрия здесь. 'SecondSub.label' - настоящая строка, уже не дескриптор. Класс, такой как 'class ThirdSub (SecondSub): pass', будет иметь метку« Foo », а не« ThirdSub ». – chepner

+0

@chepner: Работа над этим. – user2357112

Смежные вопросы