您的位置:首页 > 编程语言 > Python开发

python中为类和实例动态增加方法

2013-05-09 01:11 513 查看
>>> 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

'''

class MyClass:

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

想了不少路子,在Python的QQ群里面也得到不少灵感,最后顺利实现:

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_str在exec前,改变为:

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

a.foo =new.instancemethod(foo, a, A)
问题又来了,新加的方法里有调用实例的私有函数(以双下划线开头),报了如下错误:
1

2

3

4

5

6

7

8

9

10

11

12

13

14
class A():

def__private(self):

print "private"

defpublic(self):

self.__private()

deffoo(self):

self.__private()

a =A()

import new

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
class A():

def__private(self):

print "private"

defpublic(self):

self.__private()

deffoo(self):

self.__private()

a =A()

import dis

dis.dis(a.public)

a.public()

import new

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
6 0
LOAD_FAST 0
(self)

3LOAD_ATTR
0(_A__private)
# a.public

6CALL_FUNCTION
0

9 POP_TOP

10LOAD_CONST
0(None)

13 RETURN_VALUE

private

9 0
LOAD_FAST 0
(self)

3LOAD_ATTR
0(__private)
# a.foo

6CALL_FUNCTION
0

9 POP_TOP

10LOAD_CONST
0(None)

13 RETURN_VALUE
这里的原因是python会对private方法进行名字粉碎(name
mangling) 。因此修改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

import new

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

>>>

---------------目前为止动态加的方法都不能访问实例的私有变量,除了加类名的方式
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: