Odpowiedzi:
W Pythonie istnieje różnica między funkcjami a powiązanymi metodami.
>>> 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>>
>>>
Metody powiązane zostały „powiązane” (jak opisowe) z instancją i ta instancja będzie przekazywana jako pierwszy argument przy każdym wywołaniu metody.
Odwołania, które są atrybutami klasy (w przeciwieństwie do instancji) są nadal niezwiązane, więc możesz zmodyfikować definicję klasy, kiedy tylko chcesz:
>>> 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
Wcześniej zdefiniowane instancje są również aktualizowane (o ile same nie zastąpiły atrybutu):
>>> a.fooFighters()
fooFighters
Problem pojawia się, gdy chcesz dołączyć metodę do pojedynczego wystąpienia:
>>> 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)
Funkcja nie jest automatycznie powiązana, gdy jest dołączona bezpośrednio do instancji:
>>> a.barFighters
<function barFighters at 0x00A98EF0>
Aby to powiązać, możemy użyć funkcji MethodType w module typów :
>>> import types
>>> a.barFighters = types.MethodType( barFighters, a )
>>> a.barFighters
<bound method ?.barFighters of <__main__.A instance at 0x00A9BC88>>
>>> a.barFighters()
barFighters
Tym razem nie dotyczy to innych instancji klasy:
>>> a2.barFighters()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: A instance has no attribute 'barFighters'
Więcej informacji można znaleźć, czytając o deskryptorach i programowaniu metaklasy .
descriptor protocol
vs tworzą na MethodType
bok być może nieco bardziej czytelne.
classmethod
i staticmethod
oraz inne deskryptory też. Pozwala to uniknąć zaśmiecania przestrzeni nazw kolejnym importem.
a.barFighters = barFighters.__get__(a)
Nowy moduł jest przestarzały od Pythona 2.6 i usunięty w 3.0, użyj typów
patrz http://docs.python.org/library/new.html
W poniższym przykładzie celowo usunąłem wartość zwracaną z patch_me()
funkcji. Myślę, że podanie wartości zwracanej może sprawić, że uwierzysz, że łatka zwraca nowy obiekt, co nie jest prawdą - modyfikuje przychodzący. Prawdopodobnie może to ułatwić bardziej zdyscyplinowane korzystanie z monkeypatchingu.
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'>
Przedmowa - uwaga na temat zgodności: inne odpowiedzi mogą działać tylko w Pythonie 2 - ta odpowiedź powinna działać idealnie w Pythonie 2 i 3. Jeśli piszesz tylko w Pythonie 3, możesz pominąć dziedziczenie object
, ale w przeciwnym razie kod powinien pozostać ten sam .
Dodawanie metody do istniejącej instancji obiektu
Czytałem, że można dodać metodę do istniejącego obiektu (np. Nie w definicji klasy) w Pythonie.
Rozumiem, że nie zawsze jest to dobra decyzja. Ale jak to zrobić?
Nie polecam tego. To jest zły pomysł. Nie rób tego
Oto kilka powodów:
Dlatego sugeruję, abyś tego nie robił, chyba że masz naprawdę dobry powód. Zdecydowanie lepiej jest zdefiniować poprawną metodę w definicji klasy, lub mniej korzystnie, aby łatać klasę bezpośrednio, tak jak to:
Foo.sample_method = sample_method
Ponieważ jest to pouczające, pokażę ci kilka sposobów na zrobienie tego.
Oto kod instalacyjny. Potrzebujemy definicji klasy. Można go zaimportować, ale to naprawdę nie ma znaczenia.
class Foo(object):
'''An empty class to demonstrate adding a method to an instance'''
Utwórz instancję:
foo = Foo()
Utwórz metodę, aby ją dodać:
def sample_method(self, bar, baz):
print(bar + baz)
__get__
Kropkowane wyszukiwania funkcji wywołują __get__
metodę funkcji z instancją, wiążąc obiekt z metodą i tworząc w ten sposób „powiązaną metodę”.
foo.sample_method = sample_method.__get__(foo)
i teraz:
>>> foo.sample_method(1,2)
3
Najpierw zaimportuj typy, z których otrzymamy konstruktor metod:
import types
Teraz dodajemy metodę do instancji. Aby to zrobić, potrzebujemy konstruktora MethodType z types
modułu (który zaimportowaliśmy powyżej).
Podpis argumentu dla typów.MethodType to (function, instance, class)
:
foo.sample_method = types.MethodType(sample_method, foo, Foo)
i użycie:
>>> foo.sample_method(1,2)
3
Najpierw tworzymy funkcję otoki, która wiąże metodę z instancją:
def bind(instance, method):
def binding_scope_fn(*args, **kwargs):
return method(instance, *args, **kwargs)
return binding_scope_fn
stosowanie:
>>> foo.sample_method = bind(foo, sample_method)
>>> foo.sample_method(1,2)
3
Funkcja częściowa stosuje pierwsze argumenty do funkcji (i opcjonalnie argumenty słów kluczowych), a później można je wywołać z pozostałymi argumentami (i nadpisując argumenty słów kluczowych). A zatem:
>>> from functools import partial
>>> foo.sample_method = partial(sample_method, foo)
>>> foo.sample_method(1,2)
3
Ma to sens, gdy weźmie się pod uwagę, że powiązane metody są częściowymi funkcjami instancji.
Jeśli spróbujemy dodać metodę_próbki w taki sam sposób, w jaki moglibyśmy dodać ją do klasy, jest ona niezwiązana z instancją i nie przyjmuje domyślnego „ja” jako pierwszego argumentu.
>>> 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)
Możemy sprawić, by funkcja niezwiązana działała, jawnie przekazując instancję (lub cokolwiek innego, ponieważ ta metoda tak naprawdę nie używa self
zmiennej argumentu), ale nie byłaby zgodna z oczekiwaną sygnaturą innych instancji (jeśli łatamy małpy to wystąpienie):
>>> foo.sample_method(foo, 1, 2)
3
Znasz teraz kilka sposobów, aby to zrobić, ale z całą powagą - nie rób tego.
__get__
Metoda wymaga również klasę jako następnego parametru: sample_method.__get__(foo, Foo)
.
Myślę, że powyższe odpowiedzi pominęły kluczowy punkt.
Zróbmy klasę z metodą:
class A(object):
def m(self):
pass
Teraz zagrajmy z nim w ipython:
In [2]: A.m
Out[2]: <unbound method A.m>
Ok, m () w jakiś sposób staje się niezwiązany metoda A . Ale czy to naprawdę tak?
In [5]: A.__dict__['m']
Out[5]: <function m at 0xa66b8b4>
Okazuje się, że m () to tylko funkcja, do której odniesienie jest dodane do słownika klasy A - nie ma magii. Więc dlaczego Am daje nam sposób niezwiązany? To dlatego, że kropka nie jest tłumaczona na proste wyszukiwanie w słowniku. Jest to de facto wywołanie klasy 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
Teraz nie jestem pewien, dlaczego ostatnia linia jest drukowana dwukrotnie, ale nadal jest jasne, co się tam dzieje.
Teraz domyślna funkcja __getattribute__ polega na sprawdzeniu, czy atrybut jest tak zwanym deskryptorem, czy nie, tj. Czy implementuje specjalną metodę __get__. Jeśli implementuje tę metodę, zwracany jest wynik wywołania tej metody __get__. Wracając do pierwszej wersji naszej klasy A , mamy to:
In [28]: A.__dict__['m'].__get__(None, A)
Out[28]: <unbound method A.m>
A ponieważ funkcje Pythona implementują protokół deskryptora, jeśli są wywoływane w imieniu obiektu, wiążą się z tym obiektem za pomocą metody __get__.
Ok, więc jak dodać metodę do istniejącego obiektu? Zakładając, że nie masz nic przeciwko łataniu klasy, jest to tak proste jak:
B.m = m
Wtedy Bm „staje się” metodą niezwiązaną dzięki magii deskryptorów.
A jeśli chcesz dodać metodę tylko do jednego obiektu, musisz sam emulować maszynerię, używając types.MethodType:
b.m = types.MethodType(m, b)
Tak poza tym:
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'>
W Pythonie łatanie małp na ogół działa poprzez nadpisanie podpisu klasy lub funkcji własnym. Poniżej znajduje się przykład z Wiki Zope :
from SomeOtherProduct.SomeModule import SomeClass
def speak(self):
return "ook ook eee eee eee!"
SomeClass.speak = speak
Ten kod nadpisze / utworzy metodę o nazwie speak na klasie. W ostatnim poście Jeffa Atwooda na temat łatania małp . Pokazuje przykład w C # 3.0, który jest bieżącym językiem, którego używam do pracy.
Możesz użyć lambda do powiązania metody z instancją:
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()
Wynik:
This is instance string
Istnieją co najmniej dwa sposoby dołączenia metody do instancji bez 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>>
Przydatne linki:
Model danych - wywoływanie deskryptorów
Deskryptor Poradnik - wywoływanie deskryptorów
To, czego szukasz, to setattr
wierzę. Użyj tego, aby ustawić atrybut obiektu.
>>> 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>
A
, a nie instancję a
.
setattr(A,'printme',printme)
zamiast po prostu A.printme = printme
?
Ponieważ pytanie dotyczy wersji innych niż Python, oto JavaScript:
a.methodname = function () { console.log("Yay, a new method!") }
Konsolidacja odpowiedzi Jasona Pratta i społeczności wiki, z uwzględnieniem wyników różnych metod wiązania:
Szczególnie zwrócić uwagę w jaki sposób dodanie funkcji wiążącej jako metoda klasy dzieł , ale zakres przedstawieniu jest nieprawidłowy.
#!/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>>
Osobiście wolę zewnętrzną trasę funkcji ADDMETHOD, ponieważ pozwala mi dynamicznie przypisywać nowe nazwy metod również w iteratorze.
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>>
addmethod
przepisany w następujący sposób def addmethod(self, method, name): self.__dict__[name] = types.MethodType( method, self )
rozwiązuje problem
Chociaż odpowiedź Jadesa działa, działa ona tylko wtedy, gdy chce się dodać funkcję do klasy. Nie działało to dla mnie, gdy próbowałem ponownie załadować już istniejącą metodę z pliku kodu źródłowego .py.
Znalezienie obejścia zajęło mi od wieków, ale sztuczka wydaje się prosta ... 1. zaimportuj kod z pliku kodu źródłowego 2. i wymuś przeładowanie 3. typy użycia. Funkcja Typ (...) do konwersji zaimportowaną i powiązaną metodę z funkcją można również przekazać bieżącym zmiennym globalnym, ponieważ ponownie załadowana metoda będzie w innej przestrzeni nazw 4. teraz można kontynuować zgodnie z sugestią „Jason Pratt”, używając typów.MethodType (... )
Przykład:
# 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
Jeśli to może pomóc, niedawno wydałem bibliotekę Python o nazwie Gorilla, aby ułatwić łatanie małp.
Korzystanie z funkcji needle()
do łatania modułu o nazwie guineapig
przebiega następująco:
import gorilla
import guineapig
@gorilla.patch(guineapig)
def needle():
print("awesome")
Ale zajmuje się również bardziej interesującymi przypadkami użycia, jak pokazano w FAQ z dokumentacji .
Kod jest dostępny na GitHub .
To pytanie zostało otwarte lata temu, ale hej, istnieje prosty sposób na symulację powiązania funkcji z instancją klasy za pomocą dekoratorów:
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 ()
Tam, gdy przekażesz funkcję i instancję do dekoratora spoiwa, utworzy ona nową funkcję, z tym samym obiektem kodu co pierwszy. Następnie dane wystąpienie klasy jest przechowywane w atrybucie nowo utworzonej funkcji. Dekorator zwraca (trzecią) funkcję wywołującą automatycznie skopiowaną funkcję, podając instancję jako pierwszy parametr.
Podsumowując, otrzymujesz funkcję symulującą jej wiązanie z instancją klasy. Pozostawienie oryginalnej funkcji niezmienionej.
To, co napisał Jason Pratt, jest poprawne.
>>> 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'>
Jak widać, Python nie uważa, że b () różni się od a (). W Pythonie wszystkie metody są tylko zmiennymi, które okazały się funkcjami.
Test
, a nie jej instancję.
Wydaje mi się dziwne, że nikt nie wspomniał, że wszystkie metody wymienione powyżej tworzą odwołanie do cyklu między dodaną metodą a instancją, powodując, że obiekt jest trwały aż do wyrzucania elementów bezużytecznych. Była stara sztuczka polegająca na dodaniu deskryptora przez rozszerzenie klasy obiektu:
def addmethod(obj, name, func):
klass = obj.__class__
subclass = type(klass.__name__, (klass,), {})
setattr(subclass, name, func)
obj.__class__ = subclass
from types import MethodType
def method(self):
print 'hi!'
setattr( targetObj, method.__name__, MethodType(method, targetObj, type(method)) )
Dzięki temu możesz użyć wskaźnika własnego
MethodType
, ręcznie wywołaj protokół deskryptora i niech funkcja wygeneruje twoją instancję:barFighters.__get__(a)
tworzy powiązaną metodębarFighters
powiązania za
.