1. 程式人生 > >python中為類和例項動態增加方法

python中為類和例項動態增加方法

>>> def func(a,b):

...     print a,b

...  

>>> class Foo(object):

...     pass

...     

>>> foo = Foo()

>>> foo.func(4)

Traceback (most recent call last):

  File "<input>", line 1, in <module>

AttributeError: 'Foo' object has no attribute 'func'

>>> Foo.func = func #類動態增加方法一:直接賦值

>>> Foo.func

<unbound method Foo.func>

>>> 

>>> foo.func(4)

<__main__.Foo object at 0x3f79db0> 4

>>> foo.func

<bound method Foo.func of <__main__.Foo object at 0x3f79db0>>

>>> foo.func2=func #

例項這樣做是不行的,得到的只是一個可呼叫的屬性

>>> foo.func2

<function func at 0x3f66df0>

>>> foo.func2(1,2)

1 2

>>> foo.func2(4)

Traceback (most recent call last):

  File "<input>", line 1, in <module>

TypeError: func() takes exactly 2 arguments (1 given)

>>> import new

>>> foo.func3 = new.instancemethod(func,foo,Foo)#例項動態增加方法一:

>>> foo.func3

<bound method Foo.func of <__main__.Foo object at 0x3f79db0>>

>>> foo.func3(4)

<__main__.Foo object at 0x3f79db0> 4

>>> dir(Foo)

['__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'func']

>>> dir(foo)

['__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'func', 'func2', 'func3']

>>> 

http://wendal.net/351.html

Python: 為物件動態新增函式,且函式定義來自一個str

Python,通常情況下,你只能為物件新增一個已經寫好的方法

需求:傳入一個str型別的變數,其值是一個完整的合法的Python函式定義,然後為一個物件新增這個函式:

method_str = u'''

def say(self, name)

    print 'My nameis', name

'''

classMyClass:

    def __init__(self):

        pass

    def extends(self, method_name, method_str):

#完成這個方法...

obj =MyClass();

obj.extends('say', method_str)

obj.say('wendal')#打印出My name is wendal

想了不少路子,PythonQQ群裡面也得到不少靈感,最後順利實現:

    def extends(sefl, method_name, method_str):

#_method = None

        exec method_str + '''\n_method = %s''' % method_name

        self.__dict__[method_name]= new.instancemethod(_method,self, None)

簡單解釋一下: method_strexec,改變為:

method_str = u'''

def say(self, name)

    print 'My nameis', name

_method = abc

然後, exec執行後,_method變數就賦值為say函式接下來,就是Python的自省機制了,通過new模組,生成特定物件(本例中是self)的例項方法最後,為特定物件新增say這個函式

,這例子,就足以體現出Python在這方面的擴充套件性 1. method_str是一個字串,可以動態建立,例如使用者輸出,模板生成 2. 方法的名字可以通過字串分割等方法獲取到

昨晚完成這個實現之後,足足興奮了一個小時,哈哈 – 2行程式碼就搞定!!

http://blog.lzhaohao.info/archive/python-dynamic-instance-bound-method-access-to-private-method/

python中例項動態繫結的方法訪問私有方法

Posted in python On 2011-05-24 09:46:59 , tagged withpython.

python有一個編譯好的模組,需要增加一個方法。由於不想修改原始碼再編譯,所以使用動態繫結方法來給例項增加方法。

第一印象,想到使用如下方法:

1

2

3

4

5

deffoo(self):

    print self.name

a =A()

a.foo =foo

1

2

3

4

>>> a.foo()

Traceback (most recent call last):

File"<stdin>", line 1, in<module>

TypeError: foo() takes exactly 1argument (0given)

結果是無法訪問例項的變數。比較新繫結的方法與原有的例項方法,發現原有的例項方法是bound method。只有bound method才能訪問例項的變數。

要動態為例項繫結方法,可以使用new模組(http://docs.python.org/library/new.html)(文件中說new模組已經過期,推薦使用types模組。但我看types的文件,想不明白如何取代new模組)

1

2

importnew

a.foo =new.instancemethod(foo, a, A)

問題又來了,新加的方法裡有呼叫例項的私有函式(以雙下劃線開頭),報瞭如下錯誤:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

classA():

def__private(self):

        print "private"

defpublic(self):

self.__private()

deffoo(self):

self.__private()

a =A()

importnew

a.foo =new.instancemethod(foo, a, A)

a.foo()

1

2

3

4

5

6

Traceback (most recent call last):

File"E:tmptest.py", line 14, in<module>

a.foo()

File"E:tmptest.py", line 9,infoo

self.__private()

AttributeError: A instance has no attribute '__private'

通過觀察原有方法和動態繫結方法的位元組碼,發現LOAD_ATTR有差別。原有方法的LOAD_ATTR“_A__private”,動態繫結的方法的LOAD_ATTR“__private”

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

classA():

def__private(self):

        print "private"

defpublic(self):

self.__private()

deffoo(self):

self.__private()

a =A()

importdis

dis.dis(a.public)

a.public()

importnew

a.foo =new.instancemethod(foo, a, A)

dis.dis(a.foo)

a.foo()

1

2

3

4

5

6

7

8

9

10

11

12

13

60LOAD_FAST               0(self)

3LOAD_ATTR                0(_A__private) # a.public

6CALL_FUNCTION            0

              9 POP_TOP

10LOAD_CONST               0(None)

             13 RETURN_VALUE

private

90LOAD_FAST               0(self)

3LOAD_ATTR                0(__private) # a.foo

6CALL_FUNCTION            0

              9 POP_TOP

10LOAD_CONST               0(None)

             13 RETURN_VALUE

這裡的原因是python會對private方法進行名字粉碎。因此修改foo方法裡面的呼叫為self._A__private(),通過。但這樣修改後,方法對於不同的class會不通用,有待研究更好的方法

http://outofmemory.cn/code-snippet/2856/python-dongtai-modify-class-method-execution-logical

python動態修改類方法的執行邏輯

下面的程式碼演示如何給已經定義好的python類新增方法,替代已有方法:

from __future__ import nested_scopes

importnew

def enhance_method(klass, method_name, replacement):

'替代已有的方法'

    method = getattr(klass, method_name)

    setattr(klass, method_name, new.instancemethod(

        lambda *args, **kwds: replacement(method, *args, **kwds),None, klass))

def method_logger(old_method, self, *args, **kwds):

'給方法新增呼叫執行日誌'

    print '*** calling: %s%s, kwds=%s' % (old_method.__name__, args, kwds)

    return_value = old_method(self, *args, **kwds)# call the original method

print'*** %s returns: %s' % (old_method.__name__, `return_value`)

    return return_value

def demo():

    class Deli:

        def order_cheese(self, cheese_type):

print'Sorry, we are completely out of %s' % cheese_type

    d = Deli()

    d.order_cheese('Gouda')

    enhance_method(Deli, 'order_cheese', method_logger)

    d.order_cheese('Cheddar')

當需要修改第三方python包的某個類的某個方法時,這種修改方式非常有用。

-----------setattr方法-------------

>>> def func3(self, a):

...     print self.__a

...     

>>> def func4(self,b):

...     print self._b

... 

>>> def func5(self,c):

...     print self.c

...

>>> class Foo(object):

...     def __init__(self):

...         self.__a = 3

...         self._b=4

...         self.c=5

...     def printme(self):

...         print self.__a, self._b, self.c

...         

>>> foo = Foo()

>>> foo.printme()

3 4 5

>>> foo.funcc=new.instancemethod(func5,foo,Foo)

>>> foo.funcc(5)

5

>>> foo.funcb=new.instancemethod(func4,foo,Foo)

>>> foo.funcb(4)

4

>>> foo.funca=new.instancemethod(func3,foo,Foo)

>>> foo.funca(3)

Traceback (most recent call last):

  File "<input>", line 1, in <module>

  File "<input>", line 2, in func3

AttributeError: 'Foo' object has no attribute '__a'

>>> foo.funcx=new.instancemethod(func3,foo,None)

>>> foo.funcx(0)

Traceback (most recent call last):

  File "<input>", line 1, in <module>

  File "<input>", line 2, in func3

AttributeError: 'Foo' object has no attribute '__a'

>>> setattr(foo,'funcy',func3.__get__(foo,Foo))

>>> foo.funcy

<bound method Foo.func3 of <__main__.Foo object at 0x3f81730>>

>>> foo.funcy(3)

Traceback (most recent call last):

  File "<input>", line 1, in <module>

  File "<input>", line 2, in func3

AttributeError: 'Foo' object has no attribute '__a'

>>> 

>>> def func6(self,a):

...     print self._Foo__a

...     

>>> foo.funcz=new.instancemethod(func6,foo,None)

>>> foo.funcz(6)

3

>>> 

---------------目前為止動態加的方法都不能訪問例項的私有變數,除了加類名的方式