2012-01-31 3 views
3

У меня есть несколько классов, которые обернуты другими классами, чтобы добавить новые функции.Как создать фабрику Mixin в Python?

К сожалению, классы-оболочки не реализуют сквозные функции для классов, которые они обертывают, поэтому оболочка не может использоваться взаимозаменяемо с исходным классом.

Я хотел бы динамически создавать классы, которые содержат функциональность как оболочки, так и исходного класса.

Идея, которую я имел, заключалась в создании класса микширования и использовании фабрики для применения к существующему классу для динамического создания нового класса двойного назначения. Это должно позволить мне написать mix-in один раз, и для смешанного класса, который будет использоваться, чтобы обеспечить либо исходную функциональность, либо расширенную функциональность из-за микширования через один объект.

Это та вещь, я после того, как:

class A: 
    def __init__(self): 
     self.name = 'A' 

    def doA(self): 
     print "A:", self.name 


class B(A): 
    def __init__(self): 
     self.name = 'B' 

    def doB(self): 
     print "B:", self.name 


class C(A): 
    def __init__(self): 
     self.name = 'C' 

    def doC(self): 
     print "C:", self.name 


class D: 
    def doD(self): 
     print "D:", self.name 


class BD(B,D): 
    pass 


def MixinFactory(name, base_class, mixin): 
    print "Creating %s" % name 
    return class(base_class, mixin)  # SyntaxError: invalid syntax 

a, b, c, d, bd = A(), B(), C(), D(), BD() 

bd2 = MixinFactory('BD2', B, D)() 
cd = MixinFactory('CD', C, D)() 

a.doA()  # A: A 

b.doA()  # A: B 
b.doB()  # B: B 

c.doA()  # A: C 
c.doC()  # C: C 

bd.doA() # A: B 
bd.doB() # B: B 
bd.doD() # D: B 

bd2.doA() # A: B 
bd2.doB() # B: B 
bd2.doD() # D: B 

cd.doA() # A: C 
cd.doC() # C: C 
cd.doD() # D: C 

Проблема заключается в том, что, очевидно, что вы не просто возвращения класса из функции может. Однако, игнорируя синтаксическую ошибку, приведенный выше код действительно показывает, чего я пытаюсь достичь.

У меня была игра с тремя вариантами аргумента type(), но я не мог заставить ее работать, поэтому я не уверен, что это правильный подход.

Я предполагаю, что создание фабрики смешивания такого типа возможно в Python, так что я должен понимать, чтобы реализовать его?


Как Niklas R прокомментировал this answer на вопрос Python dynamic inheritance: How to choose base class upon instance creation? обеспечивает решение моего вопроса, но Ben «s ответ здесь дает лучшее объяснение того, почему.

+2

Что случилось с 'класс BD2 (B, D): pass'? Почему это не идеальная «фабрика микшинга»? Что случилось с доступным синтаксисом mixin? –

+0

Связанный: http://stackoverflow.com/questions/7057019/python-dynamic-inheritance-how-to-choose-base-class-upon-instance-creation/7058640#7058640 –

+0

* Неверный комментарий * –

ответ

6

На самом деле вы можете вернуть класс из функции. Синтаксическая ошибка заключается в том, что вы используете ключевое слово , как если бы это была функция, которую вы можете вызвать.

Помните, что для всех классов классов создается новый класс, а затем привязывается к имени, которое вы ему выбрали (в текущей области). Поэтому просто поместите блок класса внутри своей функции, а затем верните класс!

def mixinFactory(name, base, mixin): 
    class _tmp(base, mixin): 
     pass 
    _tmp.__name__ = name 
    return _tmp 

Как отметил ejucovy, вы также можете позвонить type непосредственно:

def mixinFactory(name, base, mixin): 
    return type(name, (base, mixin), {}) 

Это работает, потому что это (как правило), что класс блок на самом деле делает; он собирает все имена, которые вы определяете в блоке классов, в словарь, затем передает имя класса, кортеж базовых классов и словарь на type для создания нового класса.

Однако type является не более чем по умолчанию метакласса. Классы - это объекты, подобные всем остальным, и являются экземплярами класса. Большинство классов - это экземпляры type, но если есть другой метакласс, вы должны назвать это вместо type, так же, как вы бы не вызвали object, чтобы создать новый экземпляр вашего класса.

Ваш микс (предположительно) не определяет метакласс, поэтому он должен быть совместим с любым метаклассом, который является подклассом type. Таким образом, вы можете просто использовать любой класс base является:

def mixinFactory(name, base, mixin): 
    return base.__class__(name, (base, mixin), {}) 

Однако, комментарий С. Лотт действительно кажется лучшим «ответ» на этот вопрос, если ваш подмешать завод не делает ничего более сложного, чем просто создание новый класс. Это гораздо яснее и менее типирование, чем любые из динамического создания класса вариант предложено:

class NewClass(base, mixin): 
    pass 
+1

Разве вы не используете 'base' и' mixin' в неправильном порядке? Обычно mixin будет идти до базового класса в определении класса. Я бы сказал, что что-то вроде строк типа (name, (mixin, base), {}) 'было бы лучше. – seddonym

4

Вы можете иметь объявления классов в любом месте, и объявление классов может ссылаться на переменные для подклассов. Что касается имени класса, это всего лишь атрибут .__name__ объекта класса. Таким образом:

def MixinFactory(name, base_class, mixin): 
    print "Creating %s" % name 
    class kls(base_class, mixin): 
    pass 
    kls.__name__ = name 
    return kls 

должен это сделать.

Для однострочника, функция три-аргумент type должен работать тоже:

def MixinFactory(name, base_class, mixin): 
    return type(name, (base_class, mixin), {}) 
Смежные вопросы