Динамически привязывать метод к экземпляру класса в python - PullRequest
4 голосов
/ 24 января 2012

Скажем, у меня есть класс, определенный в 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 ]

4 голосов
/ 24 января 2012
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
4 голосов
/ 24 января 2012

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

from moduleB import meth2
ClassA.meth1 = meth2

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

2 голосов
/ 22 октября 2015

Так как 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 
0 голосов
/ 01 сентября 2016

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

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