Добавление метода к существующему экземпляру объекта в Python
Как добавить метод к существующему объекту (т.е. не в определении класса) в Python?
Я понимаю, что в общем это не считается хорошей практикой, кроме некоторых случаев.
5 ответ(ов)
Модуль 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'>
Если у вас есть дополнительные вопросы или нужна помощь, не стесняйтесь спрашивать!
Вы можете использовать лямбда-функцию, чтобы связать метод с экземпляром класса. Вот пример кода:
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()
будет выведена строка экземпляра.
Вы ищете функцию 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
, и затем можете вызывать его на экземплярах этого класса.
Консолидируя ответы Джейсона Пратта и сообщества по этому вопросу, давайте взглянем на результаты различных методов привязки:
Обратите особое внимание на то, как добавление функции привязки в качестве метода класса работает, но область видимости ссылки оказывается некорректной.
#!/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>>
В ответ на вопрос, касающийся версий, кроме Python, вот пример на JavaScript:
a.methodname = function () { console.log("Ура, новый метод!"); }
Понимание Python super() с методами __init__()
Почему классы в Python наследуют от object?
Разница между старыми и новыми классами в Python?
Что делают __init__ и self в Python?
Что такое monkey patching?