2016-11-26 1 views
3

Я пытаюсь дублировать, а затем изменить класс программно, но я бегу в проблемы с волшебной супер Python 3 для примера следующийPython 3 супер и метапрограммированием

class Base(): 
    def __init__(self): 
     print("Base init") 

class Orginal(Base): 
    def __init__(self): 
     super().__init__() 
     print("Orginal init") 

Modified = type(Orginal.__name__, Orginal.__bases__, dict(Orginal.__dict__)) 
Modified.f = lambda self: print("f") 

m = Modified() 

поднимает

TypeError: super (type, obj): obj должен быть экземпляром или подтипом типа

Так что мне интересно, есть ли способ помочь супер() найти нужную ячейку __class__ в классе, созданном с помощью type()?

+0

Я думаю, что ваш класс должен называться «Оригинал». – CodenameLambda

ответ

2

Примечание: Я исправил написание Original в этом ответе


Представляется, что проблема заключается в том, что, когда ваш Modified объект быть экземпляр и super называется, его называют, как: super(Original, self), поскольку это аргументы по умолчанию для super. Поскольку Original не является суперклассом Modified (отметьте isinstance(m, Original)), питон не позволяет вам звонить super таким образом.

Следующий код делает что-то похожее на ваше, но лучше иллюстрирует проблему.

class Base(): 
    def __init__(self): 
     print("Base init") 

class Original(Base): 
    def __init__(self): 
     super().__init__() 
     print("Orginal init") 

class Modified(Base): 
    def __init__(self): 
     super(Original, self).__init__() # illegal 

Modified.__name__ == Original.__name__ 

m = Modified() # raises same Exception that your your code does 

Добавление Original к bases из Modified сделают работу:

Modified = type(Original.__name__, (Original,) + Original.__bases__, dict(Original.__dict__)) 

Редактировать:

За комментариями, указанное предложение может быть упрощена, так как все методы, содержащиеся в пределах Original, будут включены в Modified без необходимости проходить dict в type вызова:

Modified = type(Original.__name__, (Original,), {}) 

и собирается один шаг дальше, если вы не хотите Modified быть подклассом Original, вы можете просто сделать Modified копию из Original и затем добавить те же атрибуты, как вы в вашем примере:

from copy import deepcopy 
Modified = deepcopy(Original) 
Modified.f = lambda self: print("f") 

m = Modified() 

L ooking в this answer, кажется, что вы не можете использовать deepcopy копировать классы, и при условии, что у вас есть, что super() вызов в методе Original.__init__, Modified быть подклассом Original (если вы также изменить метод __init__ в вашем Modified классе после того, как это было " скопировано ").

+1

FYI, Если 'Original' находится в базах, не нужно добавлять' Original .__ bases__' и 'dict (Original .__ dict __)': 'Modified = type (Original .__ name__, (Original,), {})' должно быть достаточно. – falsetru

+0

Использование deepcopy - хорошая идея, это именно то, что мне нужно - за исключением того, что она не работает на самом деле. Если я перезаписываю атрибуты оригинала, то модификации также меняются! –

1

Согласно the documentation,

Also note that, aside from the zero argument form, super() is not limited to use inside methods. The two argument form specifies the arguments exactly and makes the appropriate references. The zero argument form only works inside a class definition, as the compiler fills in the necessary details to correctly retrieve the class being defined, as well as accessing the current instance for ordinary methods.

Таким образом, super() в коде эквивалентно super(Orginal, self).__init__(), и это определяется во время компиляции.

+0

Он фактически добавляет ячейку '__class__', содержащую класс, с которым вы могли бы манипулировать внутри этой функции. Но Даниэль Бантинг уже знает это, судя по последней части его вопроса. Он спрашивает, как установить значение ячейки. – CodenameLambda

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