Добавление динамической функции на основе критериев Python в класс

Как динамически добавлять функции в класс на основе некоторых критериев.

Например, у меня есть следующий класс:

class A(object):
     def __init__(self,type):
         self.type = type

Теперь, основываясь на значении типа, я хочу добавить функции из класса B или класса C в класс A.

Например,

lass B(object):
    def fun1(self):
        print 'fun1'
    def fun2(self):
        print 'fun2'

class C(object):
    def fun3(self):
        print 'fun3'
    def fun4(self):
        print 'fun4'

Если значение атрибута 'type' класса A равно 'B', тогда динамически добавляйте функции класса B (т.е. fun1 и fun2) к классу A, иначе, если значение атрибута 'type' равно 'C', тогда добавьте функции класса C к классу A (т.е. fun3 и fun4) в класс A, так что дальше в моем коде я могу получить доступ к этим функциям как A.fun1 () или A.fun3.

Я предполагаю, что метапрограммирование может помочь мне в этом, но я понятия не имею, как это сделать. Пожалуйста, направляйте.

Кроме того, мне нужно создать объекты класса A и иметь возможность использовать эти функции через эти объекты класса A.

a = class A(type='B')
a.fun1()  #should work

Пожалуйста, помогите, так как я не могу понять это.

Это часть моего кода.

class Group(object):
    def __init__(self,type):
        if type == 'source'
            self.mgr = SManager(self)             #grp_mgr
        elif type == 'target'
            self.mgr = TManager(self)

    def __getattr__(self,name):
        if hasattr(self.mgr,name):
            return getattr(self.mgr,name)
        return AttributeError

class SManager(object):
    def __init__(self,groupobj):
        self.some_list = []
        self.groupobj = groupobj

    def doProcess(self):
        #this function accesses self.groupobj
        pass

    def get_valid_list(self):
        #this function accesses self.groupobj
        pass

Но при этом я получаю следующую ошибку.

    if hasattr(self.mgr,name):
  File "c:\workspace\project\dev.py", line 27, in __ge
tattr__
    if hasattr(self.mgr,name):
  File "c:\workspace\project\dev.py", line 27, in __ge
tattr__
    if hasattr(self.mgr,name):
  File "c:\workspace\project\dev.py", line 27, in __ge
tattr__
    if hasattr(self.mgr,name):
  File "c:\workspace\project\dev.py", line 27, in __ge
tattr__
    if hasattr(self.mgr,name):
  File "c:\workspace\project\dev.py", line 27, in __ge
tattr__
    if hasattr(self.mgr,name):
RuntimeError: maximum recursion depth exceeded while calling a Python object

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


person PyBegginer    schedule 28.02.2012    source источник
comment
Метаклассы - это более глубокое волшебство, о котором 99% пользователей никогда не должны беспокоиться. Если вам интересно, нужны ли они вам, то нет. - TP. Не могли бы вы объяснить, зачем вам такое поведение? Может быть, есть лучший дизайн, с которым мы могли бы вам помочь.   -  person Rik Poggi    schedule 28.02.2012


Ответы (3)


Как я уже сказал в комментарии к вопросу: вам, вероятно, вообще не нужны метаклассы.

Но поскольку вы не даете дополнительной информации, вот как то, что вы просите, можно сделать с помощью метаклассов:

class MetaX(type):

    def __add__(cls, other):
        name = '{}+{}'.format(cls.__name__, other.__name__)
        return type(cls)(name, (cls, other), dict())


class X(object):    # metaclass=MetaX in py3k

    __metaclass__ = MetaX

    def __add__(self, other):
        cls = type(self) + type(other)
        return cls

Вот и все, вам просто нужно наследовать от X.

Пример:

class A(X):
    def func_a(self):
        print "I'm in func_a"

class B(X):
    def func_b(self):
        print "I'm in func_b"

class C(X):
    def func_c(self):
        print "I'm in func_c"

L = A + B
l = L()
l.func_a()  # -> I'm in func_a
l.func_b()  # -> I'm in func_b

P = A + C
p = P()
p.func_a()  # -> I'm in func_a
p.func_c()  # -> I'm in func_c

Изменить: то, что вам действительно нужно, зависит от того, как выглядит ваш реальный код.

То, что вы описали в своем вопросе, - это динамическое наследование, если это то, что вам действительно нужно, тогда IMO метакласс - правильный инструмент, и переопределение __getattr__ было бы просто взломом.

Если вы не хотите строить целый метакласс, вы можете просто использовать какую-то фабрику классов:

def get_enhanced_A(typ)   # you can't use type as a keyword here
    dct = {'B': B, 'C': C}
    return type('A+{}'.format(typ), (A,dct[typ]), dict())

Я бы не знал, какой из двух вариантов выглядит лучше.

person Rik Poggi    schedule 28.02.2012
comment
Спасибо. Переопределение getattr, упомянутое zeekay, кажется проще, чем ваше, но даже это было действительно полезно. Это будет моя вторая ставка. Спасибо :) - person PyBegginer; 28.02.2012
comment
Также, как вы заметили, вам нужна дополнительная информация. Пожалуйста, дайте мне знать, какая дополнительная информация вам нужна по моему вопросу, и я передам то же самое здесь. Благодарность - person PyBegginer; 28.02.2012
comment
@PyBegginer: То, что вы описали в своем вопросе, звучит как динамическое наследование, переопределение __getattr__ выглядит как взлом. Но главное, действительно ли вам нужно такое динамическое поведение? Не могли найти лучшего дизайна? Вы не показали свой код, так что мы немного в темноте. - person Rik Poggi; 28.02.2012
comment
Я отредактировал свой вопрос и добавил свой фрагмент кода. Пожалуйста, посмотрите - person PyBegginer; 01.03.2012

Похоже, ваш вопрос проблема XY, а решение вашей исходной проблемы "X" - множественное наследование. , нравиться:

class A(B, C):
    pass

Итак, теперь вы можете делать как A().fun1(), так и A().fun3().

person Roman Bodnarchuk    schedule 28.02.2012
comment
Я действительно думал об этом. Но в моем случае мне нужны либо функции класса B, либо класса C, но никогда того и другого. - person PyBegginer; 28.02.2012
comment
@PyBegginer: если вам нужны функции A или B, но не оба, то наследуйте только от одного из них; затем вы можете передать соответствующий класс любому коду, который необходим для создания экземпляров таких объектов. - person Marcin; 28.02.2012

Ответ Романа, наверное, самый разумный подход. Но давай забудем об этом и подумаем о каких-нибудь безумных вещах, которые ты мог бы сделать вместо этого!

Вы можете просто использовать функцию, но это слишком просто:

def A(type):
    if type == 'B':
        return B()
    elif type == 'C':
        return C

Вы можете переопределить __getattr__, чтобы отсутствующие атрибуты просматривались в экземпляре выбранного класса:

class A(object):
    def __init__(self, type):
        if type == 'B':
            self.type = B()
        if type == 'C':
            self.type = C()

    def __getattr__(self, name):
        if hasattr(self.type, name):
            return getattr(self.type, name)
        return AttributeError

Или вы можете переопределить __new__ и вернуть какой-то сумасшедший новый класс или экземпляр интересующего вас класса:

class A(object):
    bases = {'B': B, 'C': C}

    def __new__(cls, type):
        class Cls(A.bases[type]):
            pass
        return Cls()

И, наверное, много других заблуждений!

person zeekay    schedule 28.02.2012
comment
идея с getattr звучит достаточно хорошо для моей цели. Спасибо :) - person PyBegginer; 28.02.2012
comment
При использовании этого метода я получаю следующую ошибку. 'if hasattr (self.grp_mgr, name): файл c: \ workspace \ project \ trial.py, строка 27, в getattr if hasattr (self.type, name): RuntimeError: максимальная глубина рекурсии превышено при вызове объекта Python ' - person PyBegginer; 01.03.2012
comment
Я отредактировал свой вопрос, добавив фрагмент кода. Пожалуйста, посмотрите. - person PyBegginer; 01.03.2012