Добавление в класс динамической функции на основе критериев Python - PullRequest
2 голосов
/ 28 февраля 2012

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

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

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

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

Ответы [ 3 ]

3 голосов
/ 28 февраля 2012

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

class A(B, C):
    pass

Так что теперь вы можете сделать A().fun1() а также A().fun3().

1 голос
/ 28 февраля 2012

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

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

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

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

1 голос
/ 28 февраля 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()

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

...