ฉันอ่านว่ามันเป็นไปได้ที่จะเพิ่มวิธีการในวัตถุที่มีอยู่ (เช่นไม่ได้อยู่ในคำจำกัดความของชั้นเรียน) ใน Python
ฉันเข้าใจว่ามันไม่ดีเสมอไป แต่คนเราจะทำสิ่งนี้ได้อย่างไร?
ฉันอ่านว่ามันเป็นไปได้ที่จะเพิ่มวิธีการในวัตถุที่มีอยู่ (เช่นไม่ได้อยู่ในคำจำกัดความของชั้นเรียน) ใน Python
ฉันเข้าใจว่ามันไม่ดีเสมอไป แต่คนเราจะทำสิ่งนี้ได้อย่างไร?
คำตอบ:
ใน 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>>
>>>
วิธีการที่ถูกผูกไว้ได้รับ "ผูกพัน" (วิธีอธิบาย) กับอินสแตนซ์และอินสแตนซ์นั้นจะถูกส่งผ่านเป็นอาร์กิวเมนต์แรกเมื่อใดก็ตามที่วิธีการที่เรียกว่า
Callables ที่เป็นคุณลักษณะของคลาส (ซึ่งตรงข้ามกับอินสแตนซ์) ยังคงไม่ถูกผูกไว้ดังนั้นคุณสามารถแก้ไขคำจำกัดความของคลาสได้ทุกเมื่อที่คุณต้องการ:
>>> 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 ในโมดูล types :
>>> 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'
ข้อมูลเพิ่มเติมสามารถพบได้โดยการอ่านเกี่ยวกับการอธิบายและmetaclass การเขียนโปรแกรม
descriptor protocol
vs การสร้างMethodType
นอกเหนือจากอาจจะอ่านง่ายขึ้นเล็กน้อย
classmethod
และstaticmethod
และอธิบายอื่น ๆ ด้วย มันหลีกเลี่ยงความยุ่งเหยิงใน namespace ด้วยการนำเข้าอื่น
a.barFighters = barFighters.__get__(a)
โมดูลใหม่เลิกใช้แล้วตั้งแต่ python 2.6 และถูกลบใน 3.0 ใช้ชนิด
ดูhttp://docs.python.org/library/new.html
ในตัวอย่างด้านล่างฉันตั้งใจลบค่าส่งคืนจากpatch_me()
ฟังก์ชัน ฉันคิดว่าการให้ค่าตอบแทนอาจทำให้ใครเชื่อว่า patch ส่งคืนวัตถุใหม่ซึ่งไม่เป็นความจริง - มันปรับเปลี่ยนวัตถุที่เข้ามา น่าจะเป็นสิ่งนี้สามารถช่วยให้การใช้งานการจับลิงเป็นไปอย่างมีวินัยยิ่งขึ้น
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'>
คำนำ - หมายเหตุเกี่ยวกับความเข้ากันได้: คำตอบอื่น ๆ อาจทำงานได้ใน Python 2 เท่านั้น - คำตอบนี้ควรทำงานได้ดีอย่างสมบูรณ์ใน Python 2 และ 3 หากเขียน Python 3 เท่านั้นคุณอาจไม่ได้รับมรดกโดยชัดแจ้งobject
แต่อย่างอื่น .
การเพิ่มวิธีการไปยังอินสแตนซ์ของวัตถุที่มีอยู่
ฉันอ่านว่ามันเป็นไปได้ที่จะเพิ่มวิธีการในวัตถุที่มีอยู่ (เช่นไม่ได้อยู่ในคำจำกัดความของคลาส) ใน 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)
__get__
การค้นหาจุดในฟังก์ชันเรียก__get__
เมธอดของฟังก์ชันด้วยอินสแตนซ์เชื่อมวัตถุเข้ากับเมธอดและสร้าง "วิธีผูกมัด"
foo.sample_method = sample_method.__get__(foo)
และตอนนี้:
>>> foo.sample_method(1,2)
3
ขั้นแรกให้นำเข้าประเภทที่เราจะได้รับวิธีการสร้าง:
import types
ตอนนี้เราเพิ่มวิธีการในอินสแตนซ์ ในการทำเช่นนี้เราต้องการตัวสร้าง MethodType จากtypes
โมดูล (ซึ่งเรานำเข้ามาด้านบน)
ลายเซ็นอาร์กิวเมนต์สำหรับ types.MethodType คือ(function, instance, class)
:
foo.sample_method = types.MethodType(sample_method, foo, Foo)
และการใช้งาน:
>>> foo.sample_method(1,2)
3
ก่อนอื่นเราสร้างฟังก์ชั่น wrapper ที่เชื่อมเมธอดเข้ากับอินสแตนซ์:
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
ฟังก์ชั่นบางส่วนจะใช้อาร์กิวเมนต์แรกกับฟังก์ชัน (และอาร์กิวเมนต์ที่เป็นทางเลือก) และสามารถเรียกใช้ในภายหลังด้วยอาร์กิวเมนต์ที่เหลือ (และแทนที่อาร์กิวเมนต์ของคำหลัก) ดังนั้น:
>>> 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)
เราสามารถทำให้ฟังก์ชั่น unbound ทำงานได้อย่างชัดเจนโดยผ่านอินสแตนซ์ (หรืออะไรก็ได้เนื่องจากวิธีนี้ไม่ได้ใช้self
ตัวแปรอาร์กิวเมนต์) แต่มันจะไม่สอดคล้องกับลายเซ็นที่คาดหวังของอินสแตนซ์อื่น ๆ (ถ้าเรากำลังแก้ไขลิง ตัวอย่างนี้):
>>> foo.sample_method(foo, 1, 2)
3
ตอนนี้คุณรู้หลายวิธีที่คุณสามารถทำได้ แต่ในทุกกรณี - อย่าทำอย่างนี้
__get__
sample_method.__get__(foo, Foo)
ฉันคิดว่าคำตอบข้างต้นพลาดจุดสำคัญ
มามีคลาสพร้อมเมธอด:
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>
ปรากฎว่าm ()เป็นเพียงฟังก์ชั่นอ้างอิงถึงสิ่งที่ถูกเพิ่มในพจนานุกรมคลาสA - ไม่มีเวทมนตร์ ถ้าเช่นนั้นทำไมAmถึงทำให้เรามีวิธีไม่ผูกมัด? เป็นเพราะจุดไม่ได้ถูกแปลเป็นการค้นหาพจนานุกรมอย่างง่าย อันที่จริงแล้วมันเป็นสายของคลาส. __.__ 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
ตอนนี้ฉันไม่แน่ใจว่าจากด้านบนของหัวของฉันทำไมบรรทัดสุดท้ายจะถูกพิมพ์สองครั้ง แต่ก็ยังเป็นที่ชัดเจนว่าเกิดอะไรขึ้น
ตอนนี้สิ่งที่ __getattribute__ เริ่มต้นทำคือตรวจสอบว่าแอตทริบิวต์นั้นเป็นตัวบอกหรือไม่เช่นถ้ามันใช้วิธีการพิเศษ __get__ ถ้ามันใช้วิธีการที่แล้วสิ่งที่จะถูกส่งกลับเป็นผลจากการเรียกวิธี __get__ ว่า กลับไปสู่รุ่นAรุ่นแรกของเรานี่คือสิ่งที่เรามี:
In [28]: A.__dict__['m'].__get__(None, A)
Out[28]: <unbound method A.m>
และเนื่องจากฟังก์ชั่น Python ใช้โพรโทคอล descriptor หากมีการเรียกใช้ในนามของวัตถุพวกมันจะผูกตัวเองกับวัตถุนั้นในวิธีการ __get__
ตกลงดังนั้นวิธีการเพิ่มวิธีการไปยังวัตถุที่มีอยู่? สมมติว่าคุณไม่ได้สนใจคลาส patching มันง่ายเหมือน:
B.m = m
จากนั้นBm "จะกลายเป็น" วิธีการที่ไม่ถูกผูกขอบคุณด้วยเวทมนตร์ descriptor
และถ้าคุณต้องการเพิ่มวิธีการเพียงวัตถุเดียวแล้วคุณต้องเลียนแบบเครื่องจักรด้วยตัวเองโดยใช้ประเภทวิธีประเภท:
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'>
ใน Python Monkey patching นั้นทำงานได้โดยการเขียนทับคลาสหรือฟังก์ชั่นลายมือชื่อของคุณเอง ด้านล่างเป็นตัวอย่างจากZope Wiki :
from SomeOtherProduct.SomeModule import SomeClass
def speak(self):
return "ook ook eee eee eee!"
SomeClass.speak = speak
รหัสนั้นจะเขียนทับ / สร้างวิธีการที่เรียกว่า speak บนคลาส ในเจฟฟ์แอดโพสต์ล่าสุดเมื่อลิง patching เขาแสดงตัวอย่างใน C # 3.0 ซึ่งเป็นภาษาปัจจุบันที่ฉันใช้ในการทำงาน
คุณสามารถใช้แลมบ์ดาเพื่อผูกเมธอดกับอินสแตนซ์:
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()
เอาท์พุท:
This is instance string
มีอย่างน้อยสองวิธีในการแนบเมธอดเข้ากับอินสแตนซ์ที่ไม่มี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 Guide - ตัวอธิบาย descriptor
สิ่งที่คุณกำลังมองหาคือ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>
A
a
setattr(A,'printme',printme)
แทนเพียงA.printme = printme
?
เนื่องจากคำถามนี้ถามถึงรุ่นที่ไม่ใช่ Python JavaScript ของที่นี่:
a.methodname = function () { console.log("Yay, a new method!") }
การรวม Jason Pratt และวิกิชุมชนตอบคำถามด้วยการดูผลลัพธ์ของวิธีการรวมที่แตกต่างกัน:
โดยเฉพาะอย่างยิ่งทราบว่าการเพิ่มฟังก์ชั่นที่มีผลผูกพันเป็นวิธีการเรียนการทำงานแต่ขอบเขตอ้างอิงไม่ถูกต้อง
#!/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>>
addmethod
การเขียนในลักษณะต่อไปนี้def addmethod(self, method, name): self.__dict__[name] = types.MethodType( method, self )
จะช่วยแก้ปัญหา
ถึงแม้ว่าคำตอบของ Jasons จะใช้ได้ แต่จะใช้ได้ก็ต่อเมื่อต้องการเพิ่มฟังก์ชันลงในคลาส มันไม่ทำงานสำหรับฉันเมื่อฉันพยายามโหลดวิธีที่มีอยู่แล้วจากไฟล์ซอร์ส. py
ฉันใช้เวลานานหลายปีในการหาวิธีแก้ปัญหา แต่เคล็ดลับดูง่าย ... 1. นำเข้าโค้ดจากไฟล์ซอร์สโค้ด 2. บังคับให้โหลดรีโหลด 3. ใช้ type.FunctionType (... ) เพื่อแปลง วิธีที่นำเข้าและผูกไว้กับฟังก์ชั่นที่คุณสามารถส่งผ่านตัวแปรทั่วโลกในปัจจุบันเป็นวิธีการโหลดจะอยู่ใน namespace ที่แตกต่างกัน 4 ตอนนี้คุณสามารถดำเนินการต่อตามที่แนะนำโดย "Jason 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
หากสามารถช่วยได้ฉันเพิ่งเปิดตัว Python library ชื่อ Gorilla เพื่อให้กระบวนการของการปะแก้ลิงสะดวกยิ่งขึ้น
การใช้ฟังก์ชั่นneedle()
เพื่อแก้ไขโมดูลที่มีชื่อguineapig
จะเป็นดังนี้:
import gorilla
import guineapig
@gorilla.patch(guineapig)
def needle():
print("awesome")
แต่มันก็ยังดูแลกรณีการใช้งานที่น่าสนใจมากขึ้นดังที่แสดงในคำถามที่พบบ่อยจากเอกสาร
คำถามนี้เปิดเมื่อหลายปีก่อน แต่เดี๋ยวก่อนมีวิธีง่าย ๆ ในการจำลองการเชื่อมโยงของฟังก์ชันกับอินสแตนซ์ของคลาสโดยใช้มัณฑนากร:
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 ()
ที่นั่นเมื่อคุณส่งผ่านฟังก์ชั่นและอินสแตนซ์ไปยังเครื่องมือตกแต่ง binder มันจะสร้างฟังก์ชั่นใหม่โดยมีรหัสวัตถุเดียวกับตัวแรก จากนั้นอินสแตนซ์ที่กำหนดของคลาสจะถูกเก็บไว้ในแอตทริบิวต์ของฟังก์ชันที่สร้างขึ้นใหม่ มัณฑนากรส่งคืนฟังก์ชัน (ที่สาม) ที่เรียกใช้ฟังก์ชันที่คัดลอกโดยอัตโนมัติโดยให้อินสแตนซ์เป็นพารามิเตอร์แรก
โดยสรุปคุณได้รับฟังก์ชั่นการจำลองมันผูกพันกับตัวอย่างของชั้น ปล่อยให้ฟังก์ชั่นดั้งเดิมไม่เปลี่ยนแปลง
สิ่งที่ Jason 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 ไม่พิจารณา b () แตกต่างจาก a () ใน Python วิธีการทั้งหมดเป็นเพียงตัวแปรที่เกิดขึ้นเป็นฟังก์ชั่น
Test
ไม่ใช่อินสแตนซ์ของมัน
ฉันคิดว่ามันแปลกที่ไม่มีใครพูดถึงวิธีการทั้งหมดที่กล่าวมาข้างต้นสร้างการอ้างอิงรอบระหว่างวิธีการเพิ่มและตัวอย่างทำให้วัตถุยังคงอยู่จนกว่าจะมีการเก็บขยะ มีกลอุบายเก่า ๆ ที่เพิ่ม descriptor โดยขยายคลาสของวัตถุ:
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)) )
ด้วยสิ่งนี้คุณสามารถใช้ตัวชี้ด้วยตนเอง
MethodType
, เรียกโปรโตคอลบ่งตนเองและมีฟังก์ชั่นการผลิตเช่นคุณ:barFighters.__get__(a)
ผลิตวิธีการที่ถูกผูกไว้สำหรับผูกไว้กับbarFighters
a