8

Добавление метода к существующему экземпляру объекта в Python

1

Как добавить метод к существующему объекту (т.е. не в определении класса) в Python?

Я понимаю, что в общем это не считается хорошей практикой, кроме некоторых случаев.

5 ответ(ов)

1

Модуль new устарел с версии Python 2.6 и был удален в версии 3.0. Вместо него рекомендуется использовать types.

Смотрите документацию для получения дополнительной информации.

В приведенном ниже примере я специально убрал возвращаемое значение из функции patch_me(). Я считаю, что возвращаемое значение может создать ложное представление о том, что патч возвращает новый объект, что не является правдой — он изменяет переданный объект. Возможно, это поможет использовать "мокипатчинг" более дисциплинированно.

import types

class A(object):  # хотя, похоже, это работает и для объектов старого стиля
    pass

def patch_me(target):
    def method(target, x):
        print("x=", x)
        print("called from", target)
    target.method = types.MethodType(method, target)
    # добавьте дополнительные методы, если нужно

a = A()
print(a)
# вывод: <__main__.A object at 0x2b73ac88bfd0>  
patch_me(a)  # патчим экземпляр
a.method(5)
# вывод: x= 5
# вывод: called from <__main__.A object at 0x2b73ac88bfd0>
patch_me(A)  
A.method(6)  # также можно патчить классы
# вывод: x= 6
# вывод: called from <class '__main__.A'>

Если у вас есть дополнительные вопросы или нужна помощь, не стесняйтесь спрашивать!

0

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

def run(self):
    print(self._instanceString)

class A(object):
    def __init__(self):
        self._instanceString = "Это строка экземпляра"

a = A()
a.run = lambda: run(a)
a.run()

Вывод будет следующим:

Это строка экземпляра

В этом примере мы определяем метод run, который выводит строку экземпляра. Затем создаем класс A с инициализатором, который задает значение _instanceString. После создания экземпляра a класса A, мы заменяем метод run на лямбду, которая вызывает функцию run, передавая в нее экземпляр a. В результате при вызове a.run() будет выведена строка экземпляра.

0

Вы ищете функцию setattr, как я полагаю. Ее можно использовать для установки атрибута объекта.

>>> def printme(s): print(repr(s))
>>> class A: pass
>>> setattr(A, 'printme', printme)
>>> a = A()
>>> a.printme()  # s становится неявным аргументом 'self'
<__main__.A instance at 0xABCDEFG>

Таким образом, вы добавляете метод printme в класс A, и затем можете вызывать его на экземплярах этого класса.

0

Консолидируя ответы Джейсона Пратта и сообщества по этому вопросу, давайте взглянем на результаты различных методов привязки:

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

#!/usr/bin/python -u
import types
import inspect

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

# Получаем список атрибутов методов класса
def listattr(c):
    for m in [(n, v) for n, v in inspect.getmembers(c, inspect.ismethod) if isinstance(v, types.MethodType)]:
        print(m[0], m[1])

# Внешнее связывание функции как метода экземпляра класса
def ADDMETHOD(c, method, name):
    c.__dict__[name] = types.MethodType(method, c)

class C():
    r = 10  # Атрибут класса для проверки области видимости привязки

    def __init__(self):
        pass

    # Внутреннее связывание функции как метода класса self -- обратите внимание, что тут есть проблемы!
    def addmethod(self, method, name):
        self.__dict__[name] = types.MethodType(method, self.__class__)

    # Предопределенная функция для сравнения
    def f0(self, x):
        print('f0\tx = %d\tr = %d' % (x, self.r))

a = C()  # Создан до изменения экземпляра
b = C()  # Измененный экземпляр

def f1(self, x):  # Внутреннее связывание
    print('f1\tx = %d\tr = %d' % (x, self.r))

def f2(self, x):  # Добавление в .__dict__ экземпляра класса как метод
    print('f2\tx = %d\tr = %d' % (x, self.r))

def f3(self, x):  # Присвоение классу как тип метода
    print('f3\tx = %d\tr = %d' % (x, self.r))

def f4(self, x):  # Добавление в .__dict__ экземпляра класса с использованием общей функции
    print('f4\tx = %d\tr = %d' % (x, self.r))

b.addmethod(f1, 'f1')
b.__dict__['f2'] = types.MethodType(f2, b)
b.f3 = types.MethodType(f3, b)
ADDMETHOD(b, f4, 'f4')

b.f0(0)  # OUT: f0   x = 0   r = 10
b.f1(1)  # OUT: f1   x = 1   r = 10
b.f2(2)  # OUT: f2   x = 2   r = 10
b.f3(3)  # OUT: f3   x = 3   r = 10
b.f4(4)  # OUT: f4   x = 4   r = 10

k = 2
print('изменяя b.r с {0} на {1}'.format(b.r, k))
b.r = k
print('новое b.r = {0}'.format(b.r))

b.f0(0)  # OUT: f0   x = 0   r = 2
b.f1(1)  # OUT: f1   x = 1   r = 10  !!!!!!!!!
b.f2(2)  # OUT: f2   x = 2   r = 2
b.f3(3)  # OUT: f3   x = 3   r = 2
b.f4(4)  # OUT: f4   x = 4   r = 2

c = C()  # Создан после изменения экземпляра

# Давайте посмотрим на атрибуты типов методов каждого экземпляра
print('\nатрибуты a:')
listattr(a)
# OUT:
# атрибуты a:
# __init__ <bound method C.__init__ of <__main__.C instance at 0x000000000230FD88>>
# addmethod <bound method C.addmethod of <__main__.C instance at 0x000000000230FD88>>
# f0 <bound method C.f0 of <__main__.C instance at 0x000000000230FD88>>

print('\nатрибуты b:')
listattr(b)
# OUT:
# атрибуты b:
# __init__ <bound method C.__init__ of <__main__.C instance at 0x000000000230FE08>>
# addmethod <bound method C.addmethod of <__main__.C instance at 0x000000000230FE08>>
# f0 <bound method C.f0 of <__main__.C instance at 0x000000000230FE08>>
# f1 <bound method ?.f1 of <class __main__.C at 0x000000000237AB28>>
# f2 <bound method ?.f2 of <__main__.C instance at 0x000000000230FE08>>
# f3 <bound method ?.f3 of <__main__.C instance at 0x000000000230FE08>>
# f4 <bound method ?.f4 of <__main__.C instance at 0x000000000230FE08>>

print('\nатрибуты c:')
listattr(c)
# OUT:
# атрибуты c:
# __init__ <bound method C.__init__ of <__main__.C instance at 0x0000000002313108>>
# addmethod <bound method C.addmethod of <__main__.C instance at 0x0000000002313108>>
# f0 <bound method C.f0 of <__main__.C instance at 0x0000000002313108>>

Лично я предпочитаю метод внешней функции ADDMETHOD, так как это позволяет динамически назначать новые имена методов в итераторе.

def y(self, x):
    pass

d = C()
for i in range(1, 5):
    ADDMETHOD(d, y, 'f%d' % i)

print('\nатрибуты d:')
listattr(d)
# OUT:
# атрибуты d:
# __init__ <bound method C.__init__ of <__main__.C instance at 0x0000000002303508>>
# addmethod <bound method C.addmethod of <__main__.C instance at 0x0000000002303508>>
# f0 <bound method C.f0 of <__main__.C instance at 0x0000000002303508>>
# f1 <bound method ?.y of <__main__.C instance at 0x0000000002303508>>
# f2 <bound method ?.y of <__main__.C instance at 0x0000000002303508>>
# f3 <bound method ?.y of <__main__.C instance at 0x0000000002303508>>
# f4 <bound method ?.y of <__main__.C instance at 0x0000000002303508>>
0

В ответ на вопрос, касающийся версий, кроме Python, вот пример на JavaScript:

a.methodname = function () { console.log("Ура, новый метод!"); }
Чтобы ответить на вопрос, пожалуйста, войдите или зарегистрируйтесь