Aggiunta di un metodo a un'istanza di oggetto esistente


643

Ho letto che è possibile aggiungere un metodo a un oggetto esistente (cioè, non nella definizione della classe) in Python.

Capisco che non è sempre bello farlo. Ma come si può fare questo?

Risposte:


921

In Python c'è una differenza tra funzioni e metodi associati.

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

I metodi associati sono stati "associati" (in modo descrittivo) a un'istanza e tale istanza verrà passata come primo argomento ogni volta che viene chiamato il metodo.

I callable che sono attributi di una classe (al contrario di un'istanza) sono comunque non associati, quindi puoi modificare la definizione della classe ogni volta che vuoi:

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

Anche le istanze definite in precedenza vengono aggiornate (purché non abbiano sovrascritto l'attributo stesso):

>>> a.fooFighters()
fooFighters

Il problema si presenta quando si desidera collegare un metodo a una singola istanza:

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

La funzione non è automaticamente associata quando è collegata direttamente a un'istanza:

>>> a.barFighters
<function barFighters at 0x00A98EF0>

Per associarlo, possiamo usare la funzione MethodType nel modulo types :

>>> import types
>>> a.barFighters = types.MethodType( barFighters, a )
>>> a.barFighters
<bound method ?.barFighters of <__main__.A instance at 0x00A9BC88>>
>>> a.barFighters()
barFighters

Questa volta altre istanze della classe non sono state interessate:

>>> a2.barFighters()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: A instance has no attribute 'barFighters'

Ulteriori informazioni possono essere trovate leggendo i descrittori e la programmazione delle metaclasse .


65
Anziché creare manualmente un MethodType, invocare manualmente il protocollo descrittore e fare in modo che la funzione produca la propria istanza: barFighters.__get__(a)produce un metodo barFightersassociato per il binding a.
Martijn Pieters

2
@MartijnPieters ha qualche vantaggio nell'usare il descriptor protocolvs creando un vantaggio MethodTypeforse un po 'più leggibile.
EndermanAPM,

17
@EndermanAPM: Diversi: è più probabile che continui a funzionare esattamente come quello che accede all'attributo su un'istanza. Funzionerà anche per classmethode staticmethode altri descrittori. Evita di ingombrare lo spazio dei nomi con un'altra importazione.
Martijn Pieters

34
Il codice completo per l'approccio descrittore suggerito èa.barFighters = barFighters.__get__(a)
eqzx

98

Il nuovo modulo è obsoleto a partire da Python 2.6 e rimosso in 3.0, usa i tipi

vedi http://docs.python.org/library/new.html

Nell'esempio seguente ho deliberatamente rimosso il valore restituito dalla patch_me()funzione. Penso che dare valore restituito possa far credere che patch ritorni un nuovo oggetto, il che non è vero - modifica quello in arrivo. Probabilmente questo può facilitare un uso più disciplinato del 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'>

1
Come funzionerebbe se il metodo aggiunto debba fare riferimento a sé? Il mio primo tentativo porta a un errore di sintassi, ma l'aggiunta di self nella definizione di metodo non sembra funzionare. tipi di importazione classe A (oggetto): # ma sembra funzionare anche per oggetti di vecchio stile ax = 'ax' pass def patch_me (target): metodo def (target, x): print (self.ax) print ("x =" , x) print ("chiamato da", target) target.method = types.MethodType (metodo, target) #aggiungi di più se necessario a = A () print (a.ax)
WesR

85

Prefazione - una nota sulla compatibilità: altre risposte possono funzionare solo in Python 2 - questa risposta dovrebbe funzionare perfettamente in Python 2 e 3. Se si scrive solo Python 3, si potrebbe tralasciare di ereditare esplicitamente object, ma altrimenti il ​​codice dovrebbe rimanere lo stesso .

Aggiunta di un metodo a un'istanza di oggetto esistente

Ho letto che è possibile aggiungere un metodo a un oggetto esistente (es. Non nella definizione della classe) in Python.

Capisco che non è sempre una buona decisione farlo. Ma come si fa?

Sì, è possibile - Ma non raccomandato

Non lo consiglio. Questa è una cattiva idea. Non farlo

Ecco un paio di motivi:

  • Aggiungerai un oggetto associato a ogni istanza in cui lo fai. Se lo fai molto, probabilmente perderai molta memoria. I metodi associati vengono in genere creati solo per la breve durata della loro chiamata e quindi cessano di esistere quando vengono automaticamente raccolti. Se lo fai manualmente, avrai un nome che fa riferimento al metodo associato, che impedirà la sua garbage collection durante l'utilizzo.
  • Le istanze di oggetti di un determinato tipo generalmente hanno i suoi metodi su tutti gli oggetti di quel tipo. Se aggiungi metodi altrove, alcune istanze avranno quei metodi e altri no. I programmatori non se lo aspettano, e rischi di violare la regola del minimo stupore .
  • Dal momento che ci sono altri buoni motivi per non farlo, ti farai anche una cattiva reputazione se lo fai.

Pertanto, ti suggerisco di non farlo a meno che tu non abbia una buona ragione. È molto meglio definire il metodo corretto nella definizione della classe o, meno preferibilmente, applicare una patch alla classe direttamente in questo modo:

Foo.sample_method = sample_method

Dato che è istruttivo, tuttavia, ti mostrerò alcuni modi per farlo.

Come si può fare

Ecco un po 'di codice di installazione. Abbiamo bisogno di una definizione di classe. Potrebbe essere importato, ma non importa.

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

Crea un'istanza:

foo = Foo()

Crea un metodo per aggiungerlo:

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

Metodo nulla (0): utilizza il metodo descrittore, __get__

Le ricerche punteggiate sulle funzioni chiamano il __get__metodo della funzione con l'istanza, associando l'oggetto al metodo e creando così un "metodo associato".

foo.sample_method = sample_method.__get__(foo)

e adesso:

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

Metodo uno - types.MethodType

Innanzitutto, importare i tipi, da cui otterremo il costruttore del metodo:

import types

Ora aggiungiamo il metodo all'istanza. Per fare ciò, richiediamo il costruttore MethodType dal typesmodulo (che abbiamo importato sopra).

La firma dell'argomento per types.MethodType è (function, instance, class):

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

e utilizzo:

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

Metodo due: legame lessicale

Innanzitutto, creiamo una funzione wrapper che associa il metodo all'istanza:

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

utilizzo:

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

Metodo tre: functools.partial

Una funzione parziale applica il primo argomento (i) a una funzione (e facoltativamente argomenti di parole chiave) e può essere successivamente chiamata con gli argomenti rimanenti (e sostituendo argomenti di parole chiave). Così:

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

Ciò ha senso se si considera che i metodi associati sono funzioni parziali dell'istanza.

Funzione non associata come attributo oggetto: perché non funziona:

Se proviamo ad aggiungere sample_method nello stesso modo in cui potremmo aggiungerlo alla classe, non è associato all'istanza e non considera il sé implicito come primo argomento.

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

Possiamo far funzionare la funzione non associata passando esplicitamente l'istanza (o qualsiasi altra cosa, poiché questo metodo in realtà non utilizza la selfvariabile argomento), ma non sarebbe coerente con la firma prevista di altre istanze (se stiamo correggendo le scimmie questa istanza):

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

Conclusione

Ora sapete diversi modi si può fare questo, ma in tutta serietà - non si esegue questa.


1
Il Disclaimer è ciò di cui mi chiedevo. Le definizioni dei metodi sono semplicemente funzioni nidificate nella definizione della classe.
Atcold

1
@Atcold Ho ampliato i motivi per evitare di farlo nell'introduzione.
Aaron Hall

Il __get__metodo ha bisogno anche la classe come il parametro successivo: sample_method.__get__(foo, Foo).
Aidas Bendoraitis

2
@AidasBendoraitis Non direi che "ne ha bisogno", è un parametro facoltativo che viene fornito quando si applica il protocollo descrittore - ma le funzioni python non usano l'argomento: github.com/python/cpython/blob/master/Objects/funcobject .c # L581
Aaron Hall

Il mio commento si basava su questo riferimento: python-reference.readthedocs.io/en/latest/docs/dunderdsc/… Quello che vedo ora dai documenti ufficiali, è facoltativo: docs.python.org/3/howto/descriptor.html# descrittore-protocollo
Aidas Bendoraitis

35

Penso che le risposte di cui sopra abbiano mancato il punto chiave.

Diamo una classe con un metodo:

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

Ora, giochiamo con esso in ipython:

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

Ok, quindi m () diventa in qualche modo un metodo non legato di A . Ma è davvero così?

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

Si scopre che m () è solo una funzione, riferimento al quale viene aggiunto al dizionario di classe A - non c'è magia. Allora perché Am ci offre un metodo illimitato? È perché il punto non è tradotto in una semplice ricerca nel dizionario. È di fatto una chiamata di classe A .__ __.__ getattribute __ (A, 'm'):

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

Ora, non sono sicuro dalla cima della mia testa perché l'ultima riga è stampata due volte, ma è ancora chiaro cosa sta succedendo lì.

Ora, ciò che fa il __getattribute__ predefinito è che controlla se l'attributo è un cosiddetto descrittore o meno, cioè se implementa un metodo speciale __get__. Se implementa quel metodo, ciò che viene restituito è il risultato della chiamata a quel metodo __get__. Tornando alla prima versione della nostra classe A , questo è ciò che abbiamo:

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

E poiché le funzioni Python implementano il protocollo descrittore, se vengono chiamate per conto di un oggetto, si legano a quell'oggetto nel loro metodo __get__.

Ok, quindi come aggiungere un metodo a un oggetto esistente? Supponendo che non ti dispiaccia la lezione di patch, è semplice come:

B.m = m

Quindi Bm "diventa" un metodo non associato, grazie al descrittore magico.

E se vuoi aggiungere un metodo solo a un singolo oggetto, allora devi emulare il macchinario tu stesso, usando types.MethodType:

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

A proposito:

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

20

In Python il patching delle scimmie generalmente funziona sovrascrivendo una firma di classe o di funzioni con la tua. Di seguito è riportato un esempio dal Wiki di Zope :

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

Quel codice sovrascriverà / creerà un metodo chiamato speak sulla classe. Nel recente post di Jeff Atwood sul patching delle scimmie . Mostra un esempio in C # 3.0 che è la lingua corrente che uso per lavoro.


6
Ma influenza tutti i casi della classe, non solo uno.
glglgl,

14

È possibile utilizzare lambda per associare un metodo a un'istanza:

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

Produzione:

This is instance string

9

Esistono almeno due modi per collegare un metodo a un'istanza senza 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>>

Link utili:
Modello di dati - invocazione di descrittori
Descrittore HowTo Guide - invocazione di descrittori


7

Quello che stai cercando è setattrcredo. Usalo per impostare un attributo su un oggetto.

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

8
Questo è correggere la classe A, non l'istanza a.
Ethan Furman,

5
C'è un motivo per usare setattr(A,'printme',printme)invece di semplicemente A.printme = printme?
Tobias Kienzler,

1
Ha senso se si costruisce il nome del metodo in fase di esecuzione.
r-

6

Poiché questa domanda ha richiesto versioni non Python, ecco JavaScript:

a.methodname = function () { console.log("Yay, a new method!") }

5

Consolidando le risposte di Jason Pratt e della wiki della community, con uno sguardo ai risultati di diversi metodi di associazione:

Soprattutto notare come l'aggiunta la funzione di legame come metodo di classe opere , ma la portata riferimento non è corretto.

#!/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>>

Personalmente, preferisco il percorso della funzione ADDMETHOD esterna, in quanto mi consente di assegnare dinamicamente nuovi nomi di metodo all'interno di un iteratore.

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

addmethodriscritto nel modo seguente def addmethod(self, method, name): self.__dict__[name] = types.MethodType( method, self )risolve il problema
Antony Hatchkins,

5

Questo è in realtà un addon alla risposta di "Jason Pratt"

Sebbene la risposta di Jasons funzioni, funziona solo se si desidera aggiungere una funzione a una classe. Non ha funzionato per me quando ho provato a ricaricare un metodo già esistente dal file di codice sorgente .py.

Mi ci sono voluti anni per trovare una soluzione alternativa, ma il trucco sembra semplice ... 1.st importare il codice dal file del codice sorgente 2.nd forzare una ricarica 3.rd use types.FunctionType (...) per convertire il metodo importato e associato a una funzione che puoi anche trasmettere sulle variabili globali correnti, poiché il metodo ricaricato si troverà in un altro spazio dei nomi 4. Ora puoi continuare come suggerito da "Jason Pratt" usando i tipi. )

Esempio:

# 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

3

Se può essere di qualche aiuto, di recente ho rilasciato una libreria Python di nome Gorilla per rendere più conveniente il processo di patching delle scimmie.

L'uso di una funzione needle()per patchare un modulo chiamato è guineapigil seguente:

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

Ma si occupa anche di casi d'uso più interessanti, come mostrato nelle FAQ dalla documentazione .

Il codice è disponibile su GitHub .


3

Questa domanda è stata aperta anni fa, ma ehi, c'è un modo semplice per simulare l'associazione di una funzione a un'istanza di classe usando i decoratori:

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

Lì, quando si passa la funzione e l'istanza al decoratore del raccoglitore, verrà creata una nuova funzione, con lo stesso oggetto codice del primo. Quindi, l'istanza data della classe viene archiviata in un attributo della funzione appena creata. Il decoratore restituisce una (terza) funzione chiamando automaticamente la funzione copiata, fornendo l'istanza come primo parametro.

In conclusione si ottiene una funzione che simula il suo legame con l'istanza della classe. Lasciare invariata la funzione originale.


2

Ciò che Jason Pratt ha pubblicato è corretto.

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

Come puoi vedere, Python non considera b () diverso da a (). In Python tutti i metodi sono solo variabili che sembrano essere funzioni.


7
Stai correggendo la classe Test, non un'istanza di essa.
Ethan Furman,

Stai aggiungendo un metodo a una classe, non a un'istanza di oggetto.
TomSawyer,

2

Trovo strano che nessuno abbia menzionato che tutti i metodi sopra elencati creano un riferimento di ciclo tra il metodo aggiunto e l'istanza, facendo sì che l'oggetto sia persistente fino alla garbage collection. C'era un vecchio trucco per aggiungere un descrittore estendendo la classe dell'oggetto:

def addmethod(obj, name, func):
    klass = obj.__class__
    subclass = type(klass.__name__, (klass,), {})
    setattr(subclass, name, func)
    obj.__class__ = subclass

2
from types import MethodType

def method(self):
   print 'hi!'


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

Con questo, è possibile utilizzare il puntatore automatico

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.