Я читал, что возможно добавить метод к существующему объекту (то есть, не в определении класса) в Python.
Я понимаю, что это не всегда хорошо. Но как можно это сделать?
источник
Я читал, что возможно добавить метод к существующему объекту (то есть, не в определении класса) в Python.
Я понимаю, что это не всегда хорошо. Но как можно это сделать?
В Python есть разница между функциями и связанными методами.
>>> def foo():
... print "foo"
...
>>> class A:
... def bar( self ):
... print "bar"
...
>>> a = A()
>>> foo
<function foo at 0x00A98D70>
>>> a.bar
<bound method A.bar of <__main__.A instance at 0x00A9BC88>>
>>>
Связанные методы были «привязаны» (насколько описательно) к экземпляру, и этот экземпляр будет передан в качестве первого аргумента при каждом вызове метода.
Однако вызываемые элементы, являющиеся атрибутами класса (в отличие от экземпляра), по-прежнему не связаны, поэтому вы можете изменить определение класса в любое время:
>>> def fooFighters( self ):
... print "fooFighters"
...
>>> A.fooFighters = fooFighters
>>> a2 = A()
>>> a2.fooFighters
<bound method A.fooFighters of <__main__.A instance at 0x00A9BEB8>>
>>> a2.fooFighters()
fooFighters
Ранее определенные экземпляры также обновляются (если они сами не переопределяют атрибут):
>>> a.fooFighters()
fooFighters
Проблема возникает, когда вы хотите присоединить метод к одному экземпляру:
>>> def barFighters( self ):
... print "barFighters"
...
>>> a.barFighters = barFighters
>>> a.barFighters()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: barFighters() takes exactly 1 argument (0 given)
Функция не привязывается автоматически, когда она прикреплена непосредственно к экземпляру:
>>> a.barFighters
<function barFighters at 0x00A98EF0>
Чтобы связать его, мы можем использовать функцию MethodType в модуле типов :
>>> import types
>>> a.barFighters = types.MethodType( barFighters, a )
>>> a.barFighters
<bound method ?.barFighters of <__main__.A instance at 0x00A9BC88>>
>>> a.barFighters()
barFighters
На этот раз другие экземпляры класса не были затронуты:
>>> a2.barFighters()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: A instance has no attribute 'barFighters'
Дополнительную информацию можно найти, прочитав о дескрипторах и программировании метаклассов .
MethodType
, вызовите протокол дескриптора вручную, и пусть функция создаст ваш экземпляр: создастbarFighters.__get__(a)
связанный метод дляbarFighters
привязанного кa
.descriptor protocol
против создания,MethodType
возможно, быть немного более читабельным.classmethod
иstaticmethod
и другие дескрипторы тоже. Это позволяет избежать загромождения пространства имен еще одним импортом.a.barFighters = barFighters.__get__(a)
Новый модуль устарел с python 2.6 и удален в 3.0, используйте типы
см. http://docs.python.org/library/new.html
В приведенном ниже примере я намеренно удалил возвращаемое значение из
patch_me()
функции. Я думаю, что возвращаемое значение может заставить поверить, что patch возвращает новый объект, который не соответствует действительности - он изменяет входящий. Вероятно, это может способствовать более дисциплинированному использованию обезьяноподготовки.источник
Предисловие - примечание о совместимости: другие ответы могут работать только в Python 2 - этот ответ должен прекрасно работать в Python 2 и 3. Если вы пишете только Python 3, вы можете явно отказаться от наследования
object
, но в противном случае код должен остаться прежним ,Да, это возможно - но не рекомендуется
Я не рекомендую это. Это плохая идея. Не делай этого.
Вот несколько причин:
Таким образом, я предлагаю вам не делать этого, если у вас нет действительно веской причины. Гораздо лучше определить правильный метод в определении класса или, менее предпочтительно, напрямую связать его с классом, например, так:
Однако, поскольку это поучительно, я покажу вам несколько способов сделать это.
Как это можно сделать
Вот некоторый установочный код. Нам нужно определение класса. Это может быть импортировано, но это действительно не имеет значения.
Создайте экземпляр:
Создайте метод для добавления к нему:
Метод naught (0) - использовать метод дескриптора,
__get__
Пунктирный поиск функций вызывает
__get__
метод функции с экземпляром, связывая объект с методом и создавая, таким образом, «связанный метод».и сейчас:
Метод первый - types.MethodType
Сначала импортируем типы, из которых мы получим конструктор метода:
Теперь мы добавляем метод к экземпляру. Для этого нам понадобится конструктор MethodType из
types
модуля (который мы импортировали выше).Сигнатура аргумента для types.MethodType
(function, instance, class)
:и использование:
Способ второй: лексическое связывание
Сначала мы создаем функцию-оболочку, которая связывает метод с экземпляром:
Применение:
Метод третий: functools.partial
Неполная функция применяет первый аргумент (ы) к функции (и, необязательно, аргументы с ключевыми словами) и может быть позже вызвана с остальными аргументами (и с переопределением аргументов с ключевыми словами). Таким образом:
Это имеет смысл, если учесть, что связанные методы являются частичными функциями экземпляра.
Несвязанная функция как атрибут объекта - почему это не работает:
Если мы попытаемся добавить образец_метода таким же образом, как мы могли бы добавить его к классу, он не связан с экземпляром и не принимает неявное «я» в качестве первого аргумента.
Мы можем заставить работать несвязанную функцию, явно передавая экземпляр (или что-нибудь еще, поскольку этот метод фактически не использует
self
переменную аргумента), но это не будет соответствовать ожидаемой сигнатуре других экземпляров (если мы будем использовать патчи для обезьян) этот экземпляр):Вывод
Теперь вы знаете несколько способов , которыми Вы могли бы сделать это, но со всей серьезностью - не делайте этого.
источник
__get__
Метод также необходим класс в качестве следующего параметра:sample_method.__get__(foo, Foo)
.Я думаю, что приведенные выше ответы упустили ключевой момент.
Давайте иметь класс с методом:
Теперь давайте поиграем с этим в ipython:
Итак, м () какой - то образом становится несвязанным методом А . Но так ли это на самом деле?
Оказывается, что m () - это просто функция, ссылка на которую добавляется в словарь классов A - в этом нет никакой магии. Тогда почему Am дает нам несвязанный метод? Это потому, что точка не переводится в простой поиск по словарю. Это де-факто вызов класса A .__ __.__ getattribute __ (A, 'm'):
Теперь я не совсем уверен, почему последняя строка печатается дважды, но все же ясно, что там происходит.
Теперь то, что по умолчанию делает __getattribute__, это то, что он проверяет, является ли атрибут так называемым дескриптором или нет, то есть реализует ли он специальный метод __get__. Если он реализует этот метод, то возвращаемый результат является результатом вызова этого метода __get__. Возвращаясь к первой версии нашего класса А , вот что у нас есть:
А поскольку функции Python реализуют протокол дескриптора, если они вызываются от имени объекта, они связывают себя с этим объектом в своем методе __get__.
Итак, как добавить метод к существующему объекту? Предполагая, что вы не против исправления класса, это так просто, как:
Тогда Bm «становится» несвязанным методом благодаря магии дескриптора.
И если вы хотите добавить метод только к одному объекту, то вы должны сами эмулировать механизм, используя types.MethodType:
Кстати:
источник
В Python исправление обезьяны обычно работает путем перезаписи сигнатуры класса или функции вашей собственной. Ниже приведен пример из Zope Wiki :
Этот код перезапишет / создаст метод с именем talk в классе. В недавнем посте Джеффа Этвуда по исправлению обезьян . Он показывает пример в C # 3.0, который является текущим языком, который я использую для работы.
источник
Вы можете использовать лямбду, чтобы привязать метод к экземпляру:
Вывод:
источник
Существует как минимум два способа присоединить метод к экземпляру без
types.MethodType
:1:
2:
Полезные ссылки:
Модель данных - вызов дескрипторов
Descriptor HowTo Guide - вызов дескрипторов
источник
То, что вы ищете,
setattr
я верю. Используйте это, чтобы установить атрибут объекта.источник
A
, а не экземпляраa
.setattr(A,'printme',printme)
вместо простоA.printme = printme
?Так как этот вопрос задан для не-Python версий, вот JavaScript:
источник
Консолидация вики-ответов Джейсона Пратта и сообщества с обзором результатов различных методов привязки:
Особенно обратите внимание , как добавление функции привязки в качестве метода класса работ , но ссылки сферы неверна.
Лично я предпочитаю внешний маршрут функции ADDMETHOD, так как он позволяет мне динамически присваивать новые имена методов также внутри итератора.
источник
addmethod
переписано следующим образомdef addmethod(self, method, name): self.__dict__[name] = types.MethodType( method, self )
решает проблемуЭто на самом деле дополнение к ответу "Джейсон Пратт"
Хотя ответ Jasons работает, он работает, только если кто-то хочет добавить функцию в класс. Это не сработало для меня, когда я попытался перезагрузить уже существующий метод из файла исходного кода .py.
У меня ушло целую вечность, чтобы найти обходной путь, но хитрость кажется простой ... 1. импортировать код из файла исходного кода 2. и принудительно выполнить перезагрузку 3.rd использовать types.FunctionType (...) для преобразования импортированный и привязанный метод к функции, вы также можете передать текущие глобальные переменные, так как перезагруженный метод будет находиться в другом пространстве имен 4. Теперь вы можете продолжить, как предложено "Jason Pratt", используя types.MethodType (... )
Пример:
источник
Если это может помочь, я недавно выпустил библиотеку Python под названием Gorilla, чтобы сделать процесс исправления обезьян более удобным.
Использование функции
needle()
для исправления названного модуляguineapig
происходит следующим образом:Но он также заботится о более интересных случаях использования, как показано в FAQ из документации .
Код доступен на GitHub .
источник
Этот вопрос был открыт несколько лет назад, но, эй, есть простой способ симулировать привязку функции к экземпляру класса с помощью декораторов:
Там, когда вы передаете функцию и экземпляр декоратору связывателя, она создаст новую функцию с тем же объектом кода, что и первый. Затем данный экземпляр класса сохраняется в атрибуте вновь созданной функции. Декоратор возвращает (третью) функцию, вызывающую автоматически скопированную функцию, давая экземпляр в качестве первого параметра.
В заключение вы получаете функцию, имитирующую привязку к экземпляру класса. Оставив исходную функцию без изменений.
источник
То, что написал Джейсон Пратт, верно.
Как видите, Python не считает b () чем-то отличным от a (). В Python все методы - это просто переменные, которые являются функциями.
источник
Test
, а не его экземпляр.Мне кажется странным, что никто не упомянул, что все перечисленные выше методы создают ссылку на цикл между добавленным методом и экземпляром, в результате чего объект остается постоянным до сборки мусора. Был старый трюк, добавляющий дескриптор путем расширения класса объекта:
источник
При этом вы можете использовать сам указатель
источник