Динамическое наследование в Python
Скажем, у меня есть 2 разные реализации класса
class ParentA:
def initialize(self):
pass
def some_event(self):
pass
def order(self, value):
# handle order in some way for Parent A
class ParentB:
def initialize(self):
pass
def some_event(self):
pass
def order(self, value):
# handle order in another for Parent B
Как я могу динамически позволить некоторым 3-м классам наследовать ParentA
или же ParentB
на основе чего-то вроде этого?
class MyCode:
def initialize(self):
self.initial_value = 1
def some_event(self):
# handle event
order(self.initial_value)
# let MyCode inherit from ParentA and run
run(my_code, ParentA)
4 ответа
Просто сохраните объект класса в переменной (в приведенном ниже примере он называется base
), и используйте переменную в спецификации базового класса вашего class
заявление.
def get_my_code(base):
class MyCode(base):
def initialize(self):
...
return MyCode
my_code = get_my_code(ParentA)
Также вы можете использовать type
встроенный. Как вызываемый, он принимает аргументы: name, bases, dct
(в простейшем виде).
def initialize(self):
self.initial_value = 1
def some_event(self):
# handle event
order(self.initial_value)
subclass_body_dict = {
"initialize": initialize,
"some_event": some_event
}
base_class = ParentA # or ParentB, as you wish
MyCode = type("MyCode", (base_class, ), subclass_body_dict)
Это более явно, чем решение snx2, но все же - мне нравится его путь лучше.
PS. конечно, вам не нужно хранить ни base_class, ни subclass_body_dict, вы можете построить эти значения в type()
позвоните как:
MyCode = type("MyCode", (ParentA, ), {
"initialize": initialize,
"some_event": some_event
})
В качестве альтернативы ответу Криса, реализующему предложение memoization для ответа shx2 , я бы предпочел использовать декоратор memoize (конечным результатом по-прежнему является класс, но мне яснее, что функция представляет собой интерфейс), а также использовать
setdefault
чтобы упростить добавление в памятку и не преобразовывать имена в строку, а использовать кортеж
bases
себя в качестве ключа, упрощая код до:
class Memoize:
def __init__(self, f):
self.f = f
self.memo = {}
def __call__(self, *args):
return self.memo.setdefault(args, self.f(*args))
class ParentA:
def initialize(self):
pass
class ParentB:
def initialize(self):
pass
@Memoize
def get_my_code(base):
class MyCode(base):
def initialize(self):
pass
return MyCode
a1 = get_my_code(ParentA)
a2 = get_my_code(ParentA)
b1 = get_my_code(ParentB)
print(a1 is a2) # True
print(a1 is b1) # False
(Не очень хороший пример, так как предоставленный код на самом деле не делает ничего, кроме перезаписи родительского класса.
initialize
метод...)
Так же, как фрагмент, готовый для быстрого копирования и вставки, я добавил комментарии из ответа shx2, чтобы создать это:
class ParentA:
val = "ParentA"
class ParentB:
val = "ParentB"
class DynamicClassCreator():
def __init__(self):
self.created_classes = {}
def __call__(self, *bases):
rep = ",".join([i.__name__ for i in bases])
if rep in self.created_classes:
return self.created_classes[rep]
class MyCode(*bases):
pass
self.created_classes[rep] = MyCode
return MyCode
creator = DynamicClassCreator()
instance1 = creator(ParentA, ParentB)()
print(instance1.val) #prints "ParentA"
instance2 = creator(ParentB, ParentA)()
print(instance2.val) #prints "ParentB"
Если вы хотите пофантазировать, вы даже можете сделать DynamicClassCreator синглтоном: /questions/3564781/est-li-prostoj-elegantnyij-sposob-opredeleniya-singletonov/3564782#3564782