Yanıtlar:
Python'da işlevler ve bağlı yöntemler arasında bir fark vardır.
>>> 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>>
>>>
Bağlı yöntemler bir örneğe "nasıl bağlanır" (ne kadar açıklayıcı) ve yöntem çağrıldığında bu örnek ilk argüman olarak geçirilir.
Bununla birlikte, bir sınıfın nitelikleri olan callables (bir örneğin aksine) hala sınırsızdır, böylece sınıf tanımını istediğiniz zaman değiştirebilirsiniz:
>>> 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
Önceden tanımlanmış örnekler de güncellenir (özelliğin kendiliğinden geçersiz kılmadığı sürece):
>>> a.fooFighters()
fooFighters
Tek bir örneğe yöntem eklemek istediğinizde sorun ortaya çıkıyor:
>>> 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)
Doğrudan bir örneğe bağlandığında işlev otomatik olarak bağlanmaz:
>>> a.barFighters
<function barFighters at 0x00A98EF0>
Bağlamak için types modülünde MethodType işlevini kullanabiliriz :
>>> import types
>>> a.barFighters = types.MethodType( barFighters, a )
>>> a.barFighters
<bound method ?.barFighters of <__main__.A instance at 0x00A9BC88>>
>>> a.barFighters()
barFighters
Bu kez sınıfın diğer örnekleri etkilenmedi:
>>> a2.barFighters()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: A instance has no attribute 'barFighters'
Tanımlayıcılar ve metasınıf programlama hakkında daha fazla bilgi edinebilirsiniz .
descriptor protocol
bir MethodType
yana yaratmanın avantajlarını kullanır .
classmethod
ve staticmethod
ve diğer tanımlayıcılar de. Ad alanını başka bir içe aktarma ile karıştırmaktan kaçınır.
a.barFighters = barFighters.__get__(a)
Yeni modül python 2.6'dan beri kullanımdan kaldırıldı ve 3.0'da kaldırıldı, kullanım türleri
bkz. http://docs.python.org/library/new.html
Aşağıdaki örnekte, döndürme değerini patch_me()
işlevden kasten kaldırdım . Dönüş değeri vermenin bir yamanın yeni olmayan bir nesne döndürdüğüne inanmasını sağlayabileceğini düşünüyorum - bu doğru değil - geleni değiştirir. Muhtemelen bu, maymunların daha disiplinli bir şekilde kullanılmasını kolaylaştırabilir.
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'>
Önsöz - uyumluluk hakkında bir not: diğer yanıtlar yalnızca Python 2'de çalışabilir - bu cevap Python 2 ve 3'te mükemmel şekilde çalışmalıdır. Yalnızca Python 3 yazıyorsanız, açıkça miras bırakabilirsiniz object
, aksi takdirde kod aynı kalmalıdır. .
Varolan Bir Nesne Örneğine Yöntem Ekleme
Python mevcut bir nesneye (örneğin sınıf tanımında değil) bir yöntem eklemek mümkün olduğunu okudum.
Bunu yapmak her zaman iyi bir karar değildir. Ancak, bunu nasıl yapabiliriz?
Bunu tavsiye etmiyorum. Bu kötü bir fikir. Yapma.
İşte birkaç neden:
Bu nedenle, gerçekten iyi bir nedeniniz yoksa bunu yapmamanızı öneririm. Sınıf tanımında doğru yöntemi tanımlamak veya daha az tercihen sınıfı doğrudan maymun yaması yapmak daha iyidir :
Foo.sample_method = sample_method
Ancak öğretici olduğu için bunu yapmanın bazı yollarını göstereceğim.
İşte bazı kurulum kodu. Sınıf tanımına ihtiyacımız var. İthal edilebilir, ama gerçekten önemli değil.
class Foo(object):
'''An empty class to demonstrate adding a method to an instance'''
Bir örnek oluşturun:
foo = Foo()
Eklemek için bir yöntem oluşturun:
def sample_method(self, bar, baz):
print(bar + baz)
__get__
İşlevlerdeki noktalı aramalar, işlevin __get__
yöntemini örnekle çağırır, nesneyi yönteme bağlar ve böylece bir "bağlı yöntem" oluşturur.
foo.sample_method = sample_method.__get__(foo)
ve şimdi:
>>> foo.sample_method(1,2)
3
İlk olarak, yöntem oluşturucuyu alacağımız içe aktarma türleri:
import types
Şimdi yöntemi örneğe ekliyoruz. Bunu yapmak için, types
modülden (yukarıda içe aktardığımız) MethodType yapıcısına ihtiyacımız var .
Türler için bağımsız değişken imzası.MethodType (function, instance, class)
:
foo.sample_method = types.MethodType(sample_method, foo, Foo)
ve kullanım:
>>> foo.sample_method(1,2)
3
İlk olarak, yöntemi örneğe bağlayan bir sarmalayıcı işlevi yaratırız:
def bind(instance, method):
def binding_scope_fn(*args, **kwargs):
return method(instance, *args, **kwargs)
return binding_scope_fn
kullanımı:
>>> foo.sample_method = bind(foo, sample_method)
>>> foo.sample_method(1,2)
3
Kısmi bir işlev, ilk bağımsız değişken (ler) i bir işleve (ve isteğe bağlı olarak anahtar kelime bağımsız değişkenlerine) uygular ve daha sonra kalan bağımsız değişkenlerle (ve geçersiz anahtar sözcük bağımsız değişkenleriyle) çağrılabilir. Böylece:
>>> from functools import partial
>>> foo.sample_method = partial(sample_method, foo)
>>> foo.sample_method(1,2)
3
Bu, ilişkili yöntemlerin örneğin kısmi işlevleri olduğunu düşündüğünüzde anlamlıdır.
Sample_method öğesini sınıfa ekleyebileceğimiz gibi eklemeye çalışırsak, örnekten bağlanmamıştır ve örtük benliği ilk argüman olarak almaz.
>>> 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)
Sınırsız işlevi, örneği (veya bu yöntem aslında self
bağımsız değişken değişkenini kullanmadığından ) açıkça geçirerek çalıştırabiliriz , ancak diğer örneklerin beklenen imzasıyla tutarlı olmaz (eğer maymun yama bu örnek):
>>> foo.sample_method(foo, 1, 2)
3
Artık birkaç yol biliyor olabilir bunu ancak bütün ciddiyetiyle - bunu yapmayın.
__get__
Yöntem ayrıca, bir sonraki parametre olarak sınıf gerekir: sample_method.__get__(foo, Foo)
.
Yukarıdaki cevapların kilit noktayı kaçırdığını düşünüyorum.
Bir yöntemle bir sınıf edelim:
class A(object):
def m(self):
pass
Şimdi onunla ipython'da oynayalım:
In [2]: A.m
Out[2]: <unbound method A.m>
Tamam, m () bir şekilde A'nın bağlanmamış bir yöntemi haline gelir . Ama gerçekten böyle mi?
In [5]: A.__dict__['m']
Out[5]: <function m at 0xa66b8b4>
O çıkıyor m () eklenir referans olan sadece bir fonksiyonudur bir sihirli yoktur - sınıf sözlükte. Öyleyse neden Am bize bağlı olmayan bir yöntem sunuyor? Çünkü nokta basit bir sözlük aramasına çevrilmemiştir. Bu fiilen A .__ class __.__ getattribute __ (A, 'm') çağrısı:
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
Şimdi, son satırın neden iki kez basıldığından emin değilim, ama yine de orada neler olduğu açık.
Şimdi, varsayılan __getattribute__'nin yaptığı şey, özelliğin sözde tanımlayıcı olup olmadığını, yani özel bir __get__ yöntemi uygulayıp uygulamadığını kontrol etmesidir. Bu yöntemi uygularsa, döndürülen şey __get__ yöntemini çağırmanın sonucudur. A sınıfımızın ilk versiyonuna geri dönersek, sahip olduğumuz şey budur:
In [28]: A.__dict__['m'].__get__(None, A)
Out[28]: <unbound method A.m>
Python işlevleri, tanımlayıcı protokolünü uyguladığından, bir nesne adına çağrılırlarsa, __get__ yöntemlerinde kendilerini bu nesneye bağlarlar.
Peki, mevcut bir nesneye yöntem nasıl eklenir? Yama sınıfını önemsemediğinizi varsayarsak, bu kadar basit:
B.m = m
Daha sonra Bm , tanımlayıcı sihir sayesinde bağlı olmayan bir yöntem haline gelir.
Ve sadece tek bir nesneye bir yöntem eklemek istiyorsanız, türleri kullanarak makineleri kendiniz taklit etmeniz gerekir.
b.m = types.MethodType(m, b)
Bu arada:
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'>
Python maymun yama yama genellikle kendi üzerine bir sınıf veya işlev imzasının üzerine yazarak çalışır. Aşağıda Zope Wiki'den bir örnek verilmiştir :
from SomeOtherProduct.SomeModule import SomeClass
def speak(self):
return "ook ook eee eee eee!"
SomeClass.speak = speak
Bu kod, sınıfta speak adında bir yöntem üzerine yazacak / oluşturacaktır. Jeff Atwood'un maymun yamasıyla ilgili son görevinde . C # 3.0'da iş için kullandığım geçerli dil olan bir örnek gösteriyor.
Bir yöntemi bir örneğe bağlamak için lambda'yı kullanabilirsiniz:
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()
Çıktı:
This is instance string
Olmayan bir örneğe yöntem eklemenin en az iki yolu vardır 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>>
Yararlı bağlantılar:
Veri modeli - tanımlayıcıları çağırmak Tanımlayıcı
Nasıl Yapılır Kılavuzu - tanımlayıcıları çağırmak
Aradığın setattr
şey inanıyorum. Bir nesne üzerinde bir öznitelik ayarlamak için bunu kullanın.
>>> 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
örneği değil , yamayı yayar a
.
setattr(A,'printme',printme)
Basitçe kullanmak için bir neden var mı A.printme = printme
?
Bu soru Python olmayan sürümleri istediğinden, JavaScript aşağıdadır:
a.methodname = function () { console.log("Yay, a new method!") }
Jason Pratt ve topluluk wiki cevaplarını farklı bağlama yöntemlerinin sonuçlarına bakarak birleştirmek:
Özellikle ciltleme işlevinin bir sınıf yöntemi olarak eklenmesinin nasıl çalıştığını , ancak başvuru kapsamının yanlış olduğunu unutmayın.
#!/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>>
Şahsen, harici bir ADDMETHOD işlev yolunu tercih ediyorum, çünkü bir yineleyicide dinamik olarak yeni yöntem adları atamama izin veriyor.
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
aşağıdaki şekilde yeniden yazılan def addmethod(self, method, name): self.__dict__[name] = types.MethodType( method, self )
sorunu çözer
Jasons'un yanıtı işe yarıyor olsa da, sadece bir sınıfa fonksiyon eklemek isterse çalışır. Zaten var olan bir yöntemi .py kaynak kodu dosyasından yeniden yüklemeye çalıştığımda benim için çalışmadı.
Bir çözüm bulmak için yaşlarımı aldı, ama hile basit görünüyor ... 1.st kaynak kod dosyasından kodu alma 2. ve yeniden yükleme 3.rd kullanım türleri.FunctionType (...) dönüştürmek için bir işleve ithal ve bağlı yöntemi de yeniden yüklenen yöntem farklı bir ad alanında 4. olacağı gibi, geçerli genel değişkenleri iletebilirsiniz. şimdi türleri kullanarak "Jason Pratt" tarafından önerildiği gibi devam edebilirsiniz.MethodType (... )
Misal:
# 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
Herhangi bir yardımcı olabilirse, yakın zamanda maymun yama işlemini daha kolay hale getirmek için Gorilla adında bir Python kütüphanesi yayınladım.
needle()
Adlı bir modülü yamalamak için bir fonksiyon kullanmak guineapig
aşağıdaki gibidir:
import gorilla
import guineapig
@gorilla.patch(guineapig)
def needle():
print("awesome")
Ancak , belgelerdeki SSS'de gösterildiği gibi daha ilginç kullanım durumlarıyla da ilgilenir .
Kod GitHub'da bulunur .
Bu soru yıllar önce açıldı, ama hey, bir fonksiyonun dekoratörler kullanarak bir sınıf örneğine bağlanmasını simüle etmenin kolay bir yolu var:
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 ()
Burada, işlevi ve örneği bağlayıcı dekoratörüne ilettiğinizde, ilkiyle aynı kod nesnesine sahip yeni bir işlev oluşturur. Ardından, sınıfın verilen örneği yeni oluşturulan işlevin bir özniteliğinde depolanır. Dekoratör, kopyalanan işlevi otomatik olarak çağıran bir (üçüncü) işlev döndürür ve örneği ilk parametre olarak verir.
Sonuç olarak, sınıf örneğine bağlandığını simüle eden bir işlev elde edersiniz. Orijinal fonksiyonun değiştirilmesine izin verilmesi.
Jason Pratt'in gönderdiği doğru.
>>> 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'>
Gördüğünüz gibi, Python b () 'yi a ()' den farklı bulmaz. Python'da tüm yöntemler sadece fonksiyonlar olan değişkenlerdir.
Test
bir örneğini değil yamasını ekliyorsunuz .
Kimsenin yukarıda listelenen tüm yöntemlerin eklenen yöntem ve örnek arasında bir döngü referansı oluşturduğunu ve nesnenin çöp toplanana kadar kalıcı olmasına neden olduğunu söylememesi garip buluyorum. Nesnenin sınıfını genişleterek tanımlayıcı ekleyerek eski bir hile vardı:
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)) )
Bununla, otomatik işaretçiyi kullanabilirsiniz
MethodType
çağırmak, açıklayıcısı protokolü elle ve örneği üretmek fonksiyonlara sahiptir:barFighters.__get__(a)
bir bağımlı yöntemi üretirbarFighters
bağlıa
.