Varolan Bir Nesne Örneğine Yöntem Ekleme


643

Python mevcut bir nesneye (yani sınıf tanımında değil) bir yöntem eklemek mümkün olduğunu okudum.

Bunu yapmanın her zaman iyi olmadığını anlıyorum. Ama bunu nasıl yapabiliriz?

Yanıtlar:


921

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 .


65
Aksine elle daha oluşturmak MethodTypeçağırmak, açıklayıcısı protokolü elle ve örneği üretmek fonksiyonlara sahiptir: barFighters.__get__(a)bir bağımlı yöntemi üretir barFightersbağlı a.
Martijn Pieters

2
@MartijnPoliteyi biraz daha okunabilir descriptor protocolbir MethodTypeyana yaratmanın avantajlarını kullanır .
EndermanAPM

17
@EndermanAPM: Birkaç: bir örnekteki özniteliğe erişenlerle aynı şekilde çalışmaya devam etme olasılığı daha yüksektir. Bunun için çalışacağız classmethodve staticmethodve diğer tanımlayıcılar de. Ad alanını başka bir içe aktarma ile karıştırmaktan kaçınır.
Martijn Pieters

34
Önerilen tanımlayıcı yaklaşımın tam kodua.barFighters = barFighters.__get__(a)
eqzx

98

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

1
Eklenen yöntemin kendine başvurması gerekiyorsa bu nasıl çalışır? İlk denemem bir sözdizimi hatasına yol açıyor, ancak yöntem tanımına kendini eklemek işe yaramıyor gibi görünüyor. içe aktarma türleri sınıf A (nesne): # ama eski stil nesneleri için çalışıyor gibi görünüyor ax = 'ax' pass def patch_me (hedef): def yöntemi (hedef, x): print (self.ax) print ("x =" , x) print ("denilen", hedef) target.method = types.MethodType (yöntem, hedef) # gerekirse daha fazlasını ekleyin a = A () print (a.ax)
WesR

85

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

Evet, mümkün - Ama önerilmez

Bunu tavsiye etmiyorum. Bu kötü bir fikir. Yapma.

İşte birkaç neden:

  • Bunu yaptığınız her örneğe ilişkili bir nesne ekleyeceksiniz. Bunu çok yaparsanız, muhtemelen çok fazla bellek harcarsınız. Bağlı yöntemler tipik olarak yalnızca kısa bir süre için oluşturulurlar ve daha sonra otomatik olarak çöp toplandığında var olmazlar. Bunu manuel olarak yaparsanız, ilişkili yönteme başvuran bir ad bağlamanız olur - bu da kullanımda çöp toplamasını önler.
  • Belirli bir türdeki nesne örnekleri genellikle bu türdeki tüm nesneler üzerinde yöntemlerine sahiptir. Başka bir yere yöntem eklerseniz, bazı örnekler bu yöntemlere sahip olurken, diğerleri olmaz. Programcılar bunu beklemeyecek ve en az sürpriz kuralını ihlal etme riskiniz var .
  • Bunu yapmamanın başka iyi nedenleri olduğundan, bunu yaparsanız ek olarak kendinize kötü bir itibar kazandıracaksınız.

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.

Nasıl yapılabilir

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

Naught yöntemi (0) - açıklayıcı yöntemi kullanın, __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

Birinci yöntem - türleri.MethodType

İ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, typesmodü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

İkinci yöntem: Sözcüksel bağlanma

İ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

Üçüncü yöntem: functools.partial

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.

Nesne niteliği olarak bağlanmamış işlev - neden çalışmaz:

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 selfbağı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

Sonuç

Artık birkaç yol biliyor olabilir bunu ancak bütün ciddiyetiyle - bunu yapmayın.


1
Yasal Uyarı Ben merak ediyordum budur. Yöntem tanımı, sınıf tanımının içine yerleştirilmiş işlevlerdir.
Atcold

1
@Atcold Girişte bunu yapmaktan kaçınmak için nedenleri genişlettim.
Aaron Hall

__get__Yöntem ayrıca, bir sonraki parametre olarak sınıf gerekir: sample_method.__get__(foo, Foo).
Aidas Bendoraitis

2
@AidasBendoraitis "İhtiyaç duyduğunu" söylemezdim, tanımlayıcı protokolü uygulanırken sağlanan isteğe bağlı bir parametredir - ancak python işlevleri argümanı kullanmaz: github.com/python/cpython/blob/master/Objects/funcobject .c # L581
Aaron Hall

Yorumum bu referansa dayanıyordu: python-reference.readthedocs.io/en/latest/docs/dunderdsc/… Resmi dokümanlarda gördüğüm şey, isteğe bağlı: docs.python.org/3/howto/descriptor.html# tanımlayıcı protokolü
Aidas Bendoraitis

35

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

20

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.


6
Ancak , sınıfın tüm örneklerini etkiler , sadece bir tanesini değil.
glglgl

14

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

9

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


7

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>

8
Bu, Aörneği değil , yamayı yayar a.
Ethan Furman

5
setattr(A,'printme',printme)Basitçe kullanmak için bir neden var mı A.printme = printme?
Tobias Kienzler

1
Birinin çalışma zamanında yöntem adı oluşturması anlamlıdır.
rr-

6

Bu soru Python olmayan sürümleri istediğinden, JavaScript aşağıdadır:

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

5

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

addmethodaşağıdaki şekilde yeniden yazılan def addmethod(self, method, name): self.__dict__[name] = types.MethodType( method, self )sorunu çözer
Antony Hatchkins

5

Bu aslında "Jason Pratt" cevabına bir eklenti

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

3

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 guineapigaş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 .


3

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.


2

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.


7
Sınıfın Testbir örneğini değil yamasını ekliyorsunuz .
Ethan Furman

Nesneye değil, sınıfa yöntem ekliyorsunuz.
TomSawyer

2

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

2
from types import MethodType

def method(self):
   print 'hi!'


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

Bununla, otomatik işaretçiyi kullanabilirsiniz

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.