Динамически привязывать метод к экземпляру класса в python

Допустим, у меня есть класс, определенный в moduleA.py к которому я хочу добавить метод, используя некоторый метод загрузчика, который принимает имя второго модуля и метод, определенный там, который должен быть связан

class ClassA(object):
    def __init__(self,config):
        super(ClassA, self).__init__()

        self.a = 1
        self.b = 2
        self.meth1 = self. bind_method(config)

    def bind_method(self,config):
        # load method
        <return method defined in config as a str 'moduleB.meth2'>

    def calling_method():
        return self.meth1() 

где метод определен в moduleB.py выглядит примерно так:

def meth2(self):
    return self.a + self.b

Дело в том, что я хочу иметь возможность писать meth2 чтобы иметь возможность доступа к переменным класса ClassA как только это связано. Таким образом, когда у вас будет что-то вроде:

from moduleA import ClassA

A = ClassA()
aout = A.calling_method()

призвание A.calling_method() правильно вызывает метод, определенный в moduleB.py,

Я видел такую ​​привязку в ответах на SO после ClassA создается с использованием types.MethodType, но я не смог выяснить, как связать внутри определения класса, чтобы это было сделано внутри, когда создается экземпляр класса.

Любые предложения о том, что должно идти в bind_method метод будет высоко ценится.

4 ответа

Решение
import sys
import types

def getobj(astr):
    """
    getobj('scipy.stats.stats') returns the associated module
    getobj('scipy.stats.stats.chisquare') returns the associated function
    """
    try:
        return globals()[astr]
    except KeyError:
        try:
            return __import__(astr, fromlist=[''])
        except ImportError:
            modname, _, basename = astr.rpartition('.')
            if modname:
                mod = getobj(modname)
                return getattr(mod, basename)
            else:
                raise

class ClassA(object):
    def __init__(self, methpath):
        super(ClassA, self).__init__()
        self.a = 1
        self.b = 2
        self.meth1 = types.MethodType(getobj(methpath), self)

a = ClassA('moduleB.meth2')
print(a.meth1())
# 3

Пропуская вещи конфигурации, которые мне не ясны, сама привязка будет выглядеть так:

from moduleB import meth2
ClassA.meth1 = meth2

Важной частью является то, что вы привязываетесь к классу, а не к экземпляру. Таким образом, если вы звоните meth1 на экземпляре он автоматически получит экземпляр в качестве первого аргумента.

Поскольку meth2() - это функция, это дескриптор, и вы можете связать ее, вызвав метод __get__().

def meth2(self):
    return self.a + self.b

class ClassA(object):
    def __init__(self,config):
        super(ClassA, self).__init__()
        self.a = 1
        self.b = 2
        self.meth1 = config.__get__(self, ClassA)

c = ClassA(meth2)
print c.meth1() #correctly prints 3 

На самом деле есть гораздо более простой способ сделать это:

class ClassA(object):
    def __init__(self,config):
        super(ClassA, self).__init__()

        self.a = 1
        self.b = 2

    from moduleB import meth2 as meth1

    def calling_method():
        return self.meth1()
Другие вопросы по тегам