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

голоса
488

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

Я понимаю, что это не всегда хорошо, чтобы сделать это. Но как можно это сделать?

Задан 04/08/2008 в 03:17
источник пользователем
На других языках...                            


18 ответов

голоса
736

В 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'

Более подробную информацию можно найти, читая о дескрипторах и метаклассом программирования .

Ответил 06/08/2008 в 01:33
источник пользователем

голоса
80

Модуль новый является устаревшим , поскольку Python 2.6 и удален в 3,0, использование типов

см http://docs.python.org/library/new.html

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

import types

class A(object):#but seems to work for old style objects too
    pass

def patch_me(target):
    def method(target,x):
        print "x=",x
        print "called from", target
    target.method = types.MethodType(method,target)
    #add more if needed

a = A()
print a
#out: <__main__.A object at 0x2b73ac88bfd0>  
patch_me(a)    #patch instance
a.method(5)
#out: x= 5
#out: called from <__main__.A object at 0x2b73ac88bfd0>
patch_me(A)
A.method(6)        #can patch class too
#out: x= 6
#out: called from <class '__main__.A'>
Ответил 06/06/2009 в 06:31
источник пользователем

голоса
47

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

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

Я понимаю , что это не всегда хорошее решение , чтобы сделать это. Но, как может один сделать это?

Да, это возможно - Но не рекомендуется

Я не рекомендую это. Это плохая идея. Не делайте этого.

Вот несколько причин:

  • Вы добавите связанный объект к каждому экземпляру вы делаете это. Если вы сделаете это много, вы, вероятно, тратить много памяти. Связанные методы обычно создаются только в течение короткого срока их вызова, а затем они перестают существовать, когда автоматически сборщик мусора. Если вы делаете это вручную, вы будете иметь имя привязки ссылки на связанный метод - который позволит предотвратить его вывоз мусора по использованию.
  • Экземпляры объектов данного типа обычно имеют свои методы по всем объектам данного типа. Если добавить метод в других местах, некоторые экземпляры будут иметь те методы , и другие не будут. Программисты не будет ожидать , что это, и вы рискуете нарушить правило наименьшего удивления .
  • Поскольку есть и другие очень веские причины, чтобы не сделать это, вы будете дополнительно дать себе плохую репутацию, если вы делаете это.

Таким образом, я полагаю , что вы не сделаете этого , если у вас есть действительно веские основания. Это гораздо лучше , чтобы определить правильный метод в определении класса или менее предпочтительно обезьяна повязки класса непосредственно, как это:

Foo.sample_method = sample_method

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

Как это можно сделать

Вот код установки. Нам нужно определение класса. Это может быть импортировано, но это действительно не имеет значения.

class Foo(object):
    '''An empty class to demonstrate adding a method to an instance'''

Создание экземпляра:

foo = Foo()

Создать метод добавления к нему:

def sample_method(self, bar, baz):
    print(bar + baz)

Метод ноль (0) - использовать метод дескриптора, __get__

Пунктирные на поиски функций вызовите __get__метод функции с экземпляром, привязка объекта к методу и , таким образом создавая «связанный метод.»

foo.sample_method = sample_method.__get__(foo)

и сейчас:

>>> foo.sample_method(1,2)
3

Метод один - types.MethodType

Во-первых, типы импорта, из которого мы получим конструктор метода:

import types

Теперь мы добавим метод экземпляра. Для этого нам понадобится конструктор MethodType из typesмодуля (который мы импортировали выше).

Аргумент подписи для types.MethodType является (function, instance, class):

foo.sample_method = types.MethodType(sample_method, foo, Foo)

и использование:

>>> foo.sample_method(1,2)
3

Способ второй: связывание лексической

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

def bind(instance, method):
    def binding_scope_fn(*args, **kwargs): 
        return method(instance, *args, **kwargs)
    return binding_scope_fn

Применение:

>>> foo.sample_method = bind(foo, sample_method)    
>>> foo.sample_method(1,2)
3

Способ три: functools.partial

Частичная функция применяется первый аргумент (ы) на функцию (и, возможно, именованные аргументы), а в дальнейшем может быть вызвана с остальными аргументами (и первостепенными именованными аргументами). Таким образом:

>>> from functools import partial
>>> foo.sample_method = partial(sample_method, foo)
>>> foo.sample_method(1,2)
3    

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

Освобожденная функция как атрибут объекта - почему это не работает:

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

>>> foo.sample_method = sample_method
>>> foo.sample_method(1,2)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: sample_method() takes exactly 3 arguments (2 given)

Мы можем сделать несвязанную работу функции, явно передавая экземпляр (или что - нибудь, так как этот метод не использовать selfпеременный аргумент), но оно не будет соответствовать ожидаемой подписи других случаев (если мы обезьяна-латание этот экземпляр):

>>> foo.sample_method(foo, 1, 2)
3

Вывод

Теперь вы знаете несколько способов , которыми Вы могли бы сделать это, но со всей серьезностью - не делайте этого.

Ответил 21/01/2015 в 05:31
источник пользователем

голоса
30

Я думаю, что приведенные выше ответы пропустили ключевой момент.

Давайте есть класс с методом:

class A(object):
    def m(self):
        pass

Теперь, давайте играть с ним в IPython:

In [2]: A.m
Out[2]: <unbound method A.m>

Итак, м () какой - то образом становится несвязанным методом . Но действительно ли это так?

In [5]: A.__dict__['m']
Out[5]: <function m at 0xa66b8b4>

Оказывается, что м () это просто функция, ссылка на которую добавляется А словарь класса - нет никакой магии. Тогда почему Am дает нам несвязанный метод? Это потому , что точка не переводится на простой поиск в словаре. Это де - факто вызова A .__ класса __.__ GetAttribute __ (А, 'т'):

In [11]: class MetaA(type):
   ....:     def __getattribute__(self, attr_name):
   ....:         print str(self), '-', attr_name

In [12]: class A(object):
   ....:     __metaclass__ = MetaA

In [23]: A.m
<class '__main__.A'> - m
<class '__main__.A'> - m

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

Теперь, что __getattribute__ по умолчанию делает то , что он проверяет , является ли атрибут так называемый дескриптор или нет, то есть , если он реализует специальный метод __get__. Если он реализует этот метод, то , что возвращается результат вызова этого метода __get__. Возвращаясь к первой версии нашего А класса, это то , что мы имеем:

In [28]: A.__dict__['m'].__get__(None, A)
Out[28]: <unbound method A.m>

А поскольку функции Python реализуют протокол дескриптора, если они называются по имени объекта, они связывают себя к этому объекту в их методе __get__.

Итак, как добавить метод к существующему объекту? Предполагая, что вы не возражаете зашивки класса, это так просто, как:

B.m = m

Тогда Bm «становится» несвязанный метод, благодаря дескриптору магии.

И если вы хотите, чтобы добавить метод только к одному объекту, то вы должны подражать машинное оборудование самостоятельно, с помощью types.MethodType:

b.m = types.MethodType(m, b)

Кстати:

In [2]: A.m
Out[2]: <unbound method A.m>

In [59]: type(A.m)
Out[59]: <type 'instancemethod'>

In [60]: type(b.m)
Out[60]: <type 'instancemethod'>

In [61]: types.MethodType
Out[61]: <type 'instancemethod'>
Ответил 22/01/2012 в 15:20
источник пользователем

голоса
16

В Python обезьяна латание обычно работает путем переписывания класса или функции подписи с вашим собственным. Ниже приведен пример из Zope Wiki :

from SomeOtherProduct.SomeModule import SomeClass
def speak(self):
   return "ook ook eee eee eee!"
SomeClass.speak = speak

Этот код будет перезаписывать / создать метод , называемый говорить о классе. В Джефф Этвуд недавно пост на обезьяне заплат . Он показывает пример в C # 3.0 , который является текущим языком я использую для работы.

Ответил 04/08/2008 в 03:31
источник пользователем

голоса
9

Есть по крайней мере два способа прикрепить метод к экземпляру без types.MethodType:

>>> class A:
...  def m(self):
...   print 'im m, invoked with: ', self

>>> a = A()
>>> a.m()
im m, invoked with:  <__main__.A instance at 0x973ec6c>
>>> a.m
<bound method A.m of <__main__.A instance at 0x973ec6c>>
>>> 
>>> def foo(firstargument):
...  print 'im foo, invoked with: ', firstargument

>>> foo
<function foo at 0x978548c>

1:

>>> a.foo = foo.__get__(a, A) # or foo.__get__(a, type(a))
>>> a.foo()
im foo, invoked with:  <__main__.A instance at 0x973ec6c>
>>> a.foo
<bound method A.foo of <__main__.A instance at 0x973ec6c>>

2:

>>> instancemethod = type(A.m)
>>> instancemethod
<type 'instancemethod'>
>>> a.foo2 = instancemethod(foo, a, type(a))
>>> a.foo2()
im foo, invoked with:  <__main__.A instance at 0x973ec6c>
>>> a.foo2
<bound method instance.foo of <__main__.A instance at 0x973ec6c>>

Полезные ссылки:
Модель данных - дескрипторы , призывающее
Руководство Descriptor HowTo - призывающие дескрипторы

Ответил 26/04/2013 в 16:47
источник пользователем

голоса
7

Вы можете использовать лямбда связать метод экземпляра:

def run(self):
    print self._instanceString

class A(object):
    def __init__(self):
        self._instanceString = "This is instance string"

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

Это пример строки

Процесс закончил с кодом выхода 0

Ответил 21/07/2014 в 13:55
источник пользователем

голоса
6

Поскольку этот вопрос, заданный для версий непитоновских, вот JavaScript:

a.methodname = function () { console.log("Yay, a new method!") }
Ответил 09/03/2012 в 16:07
источник пользователем

голоса
6

То , что вы ищете, setattrя считаю. Используйте это , чтобы установить атрибут объекта.

>>> def printme(s): print repr(s)
>>> class A: pass
>>> setattr(A,'printme',printme)
>>> a = A()
>>> a.printme() # s becomes the implicit 'self' variable
< __ main __ . A instance at 0xABCDEFG>
Ответил 07/08/2008 в 12:30
источник пользователем

голоса
5

Это на самом деле аддон к ответу «Джейсон Pratt»

Хотя Jasons ответ работает, он работает только тогда, когда один хочет, чтобы добавить функцию к классу. Это не работает для меня, когда я попытался перезагрузить уже существующий метод из .py исходного кода файла.

Он взял меня на века, чтобы найти обходной путь, но трюк кажется простым ... 1.st импортировать код из файла исходного кода 2-ом принудительно использовать перезарядка 3-яя types.FunctionType (...), чтобы преобразовать импортирован и связанный метод к функции вы можете также перейти на текущих глобальных переменных, как перегружается метод будет в другом пространстве имен 4.th теперь вы можете продолжить как это было предложено «Джейсон Pratt», используя types.MethodType (... )

Пример:

# this class resides inside ReloadCodeDemo.py
class A:
    def bar( self ):
        print "bar1"

    def reloadCode(self, methodName):
        ''' use this function to reload any function of class A'''
        import types
        import ReloadCodeDemo as ReloadMod # import the code as module
        reload (ReloadMod) # force a reload of the module
        myM = getattr(ReloadMod.A,methodName) #get reloaded Method
        myTempFunc = types.FunctionType(# convert the method to a simple function
                                myM.im_func.func_code, #the methods code
                                globals(), # globals to use
                                argdefs=myM.im_func.func_defaults # default values for variables if any
                                ) 
        myNewM = types.MethodType(myTempFunc,self,self.__class__) #convert the function to a method
        setattr(self,methodName,myNewM) # add the method to the function

if __name__ == '__main__':
    a = A()
    a.bar()
    # now change your code and save the file
    a.reloadCode('bar') # reloads the file
    a.bar() # now executes the reloaded code
Ответил 18/08/2015 в 15:32
источник пользователем

голоса
5

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

Ответил 25/08/2013 в 22:56
источник пользователем

голоса
5

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

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

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

## dynamically adding methods to a unique instance of a class


# get a list of a class's method type attributes
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]

# externally bind a function as a method of an instance of a class
def ADDMETHOD(c, method, name):
    c.__dict__[name] = types.MethodType(method, c)

class C():
    r = 10 # class attribute variable to test bound scope

    def __init__(self):
        pass

    #internally bind a function as a method of self's class -- note that this one has issues!
    def addmethod(self, method, name):
        self.__dict__[name] = types.MethodType( method, self.__class__ )

    # predfined function to compare with
    def f0(self, x):
        print 'f0\tx = %d\tr = %d' % ( x, self.r)

a = C() # created before modified instnace
b = C() # modified instnace


def f1(self, x): # bind internally
    print 'f1\tx = %d\tr = %d' % ( x, self.r )
def f2( self, x): # add to class instance's .__dict__ as method type
    print 'f2\tx = %d\tr = %d' % ( x, self.r )
def f3( self, x): # assign to class as method type
    print 'f3\tx = %d\tr = %d' % ( x, self.r )
def f4( self, x): # add to class instance's .__dict__ using a general function
    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 'changing b.r from {0} to {1}'.format(b.r, k)
b.r = k
print 'new 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() # created after modifying instance

# let's have a look at each instance's method type attributes
print '\nattributes of a:'
listattr(a)
# OUT:
# attributes of 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 '\nattributes of b:'
listattr(b)
# OUT:
# attributes of 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 '\nattributes of c:'
listattr(c)
# OUT:
# attributes of 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 '\nattributes of d:'
listattr(d)
# OUT:
# attributes of 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>>
Ответил 28/01/2012 в 01:12
источник пользователем

голоса
4

Что Джейсон Pratt отвечал правильно.

>>> class Test(object):
...   def a(self):
...     pass
... 
>>> def b(self):
...   pass
... 
>>> Test.b = b
>>> type(b)
<type 'function'>
>>> type(Test.a)
<type 'instancemethod'>
>>> type(Test.b)
<type 'instancemethod'>

Как вы можете видеть, Python не учитывает б () не отличается чем (). В Python все методы являются лишь переменные, которые происходят с функциями.

Ответил 22/08/2008 в 15:40
источник пользователем

голоса
3

Если это может быть какой-либо помощи, я недавно выпустила библиотеку Python под названием Gorilla, чтобы сделать процесс обезьяны зашивки более удобным.

Использование функции needle()пропатчить модуль с именем guineapigвыглядит следующим образом :

import gorilla
import guineapig
@gorilla.patch(guineapig)
def needle():
    print("awesome")

Но он также заботится о более интересных случаев использования , как показано в FAQ от документации .

Код доступен на GitHub .

Ответил 15/07/2014 в 03:12
источник пользователем

голоса
2

Я нахожу странным, что никто не отметил, что все методы, перечисленные выше создает ссылку цикла между дополнительным способом и, например, в результате чего объекта быть стойкими до сбора мусора. Был старый трюк добавить дескриптор, расширив класс объекта:

def addmethod(obj, name, func):
    klass = obj.__class__
    subclass = type(klass.__name__, (klass,), {})
    setattr(subclass, name, func)
    obj.__class__ = subclass
Ответил 30/04/2017 в 04:57
источник пользователем

голоса
2

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

def binder (function, instance):
  copy_of_function = type (function) (function.func_code, {})
  copy_of_function.__bind_to__ = instance
  def bound_function (*args, **kwargs):
    return copy_of_function (copy_of_function.__bind_to__, *args, **kwargs)
  return bound_function


class SupaClass (object):
  def __init__ (self):
    self.supaAttribute = 42


def new_method (self):
  print self.supaAttribute


supaInstance = SupaClass ()
supaInstance.supMethod = binder (new_method, supaInstance)

otherInstance = SupaClass ()
otherInstance.supaAttribute = 72
otherInstance.supMethod = binder (new_method, otherInstance)

otherInstance.supMethod ()
supaInstance.supMethod ()

Там, когда вы передаете функцию и экземпляр связующего декоратор, он будет создавать новую функцию, с тем же кодом объекта , как и первый. Затем, данный экземпляр класса хранится в атрибуте вновь созданной функции. Декоратора возвращают (третью) вызывающую функцию автоматически скопированную функцию, давая экземпляр в качестве первого параметра.

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

Ответил 21/12/2015 в 21:39
источник пользователем

голоса
1
from types import MethodType

def method(self):
   print 'hi!'


setattr( targetObj, method.__name__, MethodType(method, targetObj, type(method)) )

При этом, вы можете использовать указатель собственного

Ответил 27/07/2017 в 04:21
источник пользователем

голоса
-8

Я не знаю синтаксис Python, но я знаю, что рубин может это сделать, и это довольно тривиально. Допустим, вы хотите добавить метод массив, который печатает длину на стандартный вывод:

class Array
  def print_length
    puts length
  end
end

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

array = [1, 2, 3]
def array.print_length
  puts length
end

Просто быть в курсе вопросов , связанных с использованием этой функции. Джефф Этвуд на самом деле об этом писал не так давно.

Ответил 04/08/2008 в 03:36
источник пользователем

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more