动态添加和删除方法 [英] Dynamically adding and removing methods

查看:61
本文介绍了动态添加和删除方法的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

假设我用一些方法创建了一个类:


py> C类:

.... def spam(self,x):

.... print" spam" * x

.... def ham(self,x):

.... print" ham *%s" %x

....

py> C()。垃圾邮件(3)

垃圾邮件垃圾邮件

C()。ham(3 )
ham * 3


要动态删除这些方法,请从类中删除它们

会有任何其他属性:


py> del C.ham

py> C()。ham(3)

回溯(最近一次调用最后一次):

文件"< stdin>",第1行,在?

AttributeError:C实例没有属性''ham''


那是相当直接的 - 没有重大意外。

如何动态地向实例或类添加方法?你可以

认为你可以这样做:


py> def eggs(self,x):

.... print" eggs *%s" %x

....

py> inst = C()

py> inst.eggs = eggs

py> inst.eggs(3)

回溯(最近一次调用最后一次):

文件"< stdin>",第1行,在?

TypeError:eggs()只需2个参数(给定1个)


即使我们已经为实例添加了一个函数,它也没有全部

机器作为一种正确的方法。解决方法是明确传递

一个实例:


py> inst.eggs(inst,3)

eggs * 3

要创建一个合适的方法,我们这样做:


py>导入新的

py> inst.eggs = new.instancemethod(eggs,None,C)

py> inst.eggs(3)

eggs * 3

你现在可以删除顶级函数egg,并且绑定的方法是

inst不会受到影响。因为鸡蛋被绑定到实例,新的

实例将无法理解它:


py> C()。eggs(3)

Traceback(最近一次调用最后一次):

文件"< stdin>",第1行,在?

AttributeError:C实例没有属性''eggs''


或者您可以将该方法放在类中并让所有实例识别




py> C.eggs = new.instancemethod(eggs,None,C)

py> C()。eggs(3)

eggs * 3

而不是将None作为instancemethod的第二个参数传递,你可以

传递一个实例。如果你这样做,self将自动设置为

实例而不是执行调用的实例:


py> def松露(自我,x):

....打印自己,x

....

py> C.truffles = new.instancemethod(truffles,inst,C)

py> inst

< __ main __。位于0xf6d2d6cc的C实例>

py> inst.truffles(3)

< __ main __。位于0xf6d2d6cc的C实例> 3


那里没有惊喜。但是看看当我们使用新实例时会发生什么:


py> x = C()

py> x

< __ main __。位于0xf6d2ca6c的C实例> x.truffles(3)



< __ main __。位于0xf6d2d6cc>的C实例3

希望这对一些人有用。

-

史蒂文。

解决方案

我最近发现,动态添加方法的一个警告是,它不适用于__foo__方法。例如,你不能通过动态地将绑定方法分配给
obj .__ iter__和obj.next来将对象变成迭代器。与__getitem __,__ setitem __,

等相同;将它们直接添加到实例中并不会得到一个

可订阅对象。这是因为解释器查看了类定义的

方法,而不是实例的属性。


只是提醒一句。


Collin Winter


Steven D''Aprano写道:

或者您可以将该方法放入类中并让所有实例识别它:

py> C.eggs = new.instancemethod(eggs,None,C)
py> C()。eggs(3)
eggs * 3




为什么不直接将它添加到类中?你必须确定

它是一个类而不是一个类的实例。

def beacon(self,x):
.... print" beacon +%s" %x

.... C.beacon = beacon
dir(A)
[''__doc__'',''__ module _'',''beacon'', ''ham'',''spam''] A.beacon(3)
beacon + 3 del beacon
dir(A)
[''__doc__'',''__ modle__' ',''beacon'',''ham'',''spam''] A.beacon(3)
beacon + 3 dir(C)



[''__doc__'',''__ module _'',''beacon'',''ham'',''垃圾邮件'']

干杯,

Ron


On Sun,2005年9月25日14:52:56 +0000,Ron Adam写道:

Steven D''Aprano写道:

或者您可以将该方法放入类中并让所有实例识别它:

py> C.eggs = new.instancemethod(eggs,None,C)
py> C()。eggs(3)
eggs * 3



为什么不直接将它添加到类中?你只需要确定它是一个类而不是一个类的实例。




因为我开始直接向实例显式添加函数,

当我发现它没有正常工作时,我甚至从未尝试过将

添加到类中,直到我发现instancemethod()工作为止。


据我所知,当你动态地将
添加到类和实例时,Python对函数的处理相当困惑。例如,参见:


py> class Klass:

....传递

....

py> def eggs(self,x):

.... print" eggs *%s" %x

....

py> inst = Klass()#创建一个类实例。

py> inst.eggs = eggs#动态添加函数/方法。

py> inst.eggs(1)

回溯(最近一次调用最后一次):

文件"< stdin>",第1行,在?

TypeError:eggs()只需2个参数(给定1个)


从这里,我可以得出结论,当你将函数分配给

实例属性,它被修改为采用两个参数而不是一个。

通过显式传递实例来测试它:


py> inst.eggs(inst,1)

eggs * 1


我的假设得到确认。


我们可以再次获得未修改的功能?


py> neweggs = inst.eggs

py> neweggs(1)

回溯(最近一次调用最后一次):

文件"< stdin>",第1行,在?

TypeError:eggs()只需要2个参数(给定1个)


不。这是一个问题。将一个函数对象存储为属性,然后

检索它,不会再次给你原始对象。


所以你可以这样做:


def printgraph(f):#打印函数图

parameters = get_params()

draw_graph(f) ,参数)


你不能这样做:


def printgraph(功能):#打印函数图表/>
参数= get_params()

parameters.function = f#警告:f在这里被修改

draw_graph(参数)

当将函数对象存储为实例对象时,将它转换为一个方法:即使将egg修改为期望两个

参数, Python不足以自动传递实例

对象作为第一个参数,就像它调用真实实例时那样

方法。


此外,属性的类型没有改变:


py>类型(鸡蛋)

< type''function''>

py> type(inst.eggs)

< type''function''>


但是如果你为一个函数赋一个class属性,那么类型就会改变,并且

Python知道传递实例对象:


py> Klass.eggs =鸡蛋

py> inst2 = Klass()

py> type(inst2.eggs)

< type''instancemethod''>

py> inst2.eggs(1)

eggs * 1

在类中添加函数和

实例之间的不同行为是不一致的。类行为很有用,实例

行为被破坏。

>>> def beacon(self,x):... print" beacon +%s" %x
...




您的意思是培根吗? * wink *

>>> C.beacon = beacon
>>> dir(A)


[''__doc__'',''__ module _'',''beacon'',''ham'',''spam'']




好​​的,你没有显示所有代码。什么是A?

-

史蒂文。


Suppose I create a class with some methods:

py> class C:
.... def spam(self, x):
.... print "spam " * x
.... def ham(self, x):
.... print "ham * %s" % x
....
py> C().spam(3)
spam spam spam

C().ham(3) ham * 3

To dynamically remove the methods, delete them from the class like you
would any other attribute:

py> del C.ham
py> C().ham(3)
Traceback (most recent call last):
File "<stdin>", line 1, in ?
AttributeError: C instance has no attribute ''ham''

That''s fairly straight forward -- no major surprises there.

How does one dynamically add methods to an instance or class? You might
think you can do this:

py> def eggs(self, x):
.... print "eggs * %s" % x
....
py> inst = C()
py> inst.eggs = eggs
py> inst.eggs(3)
Traceback (most recent call last):
File "<stdin>", line 1, in ?
TypeError: eggs() takes exactly 2 arguments (1 given)

Even though we''ve added a function to the instance, it hasn''t got all the
machinery to work as a proper method. A work-around is to explicitly pass
an instance:

py> inst.eggs(inst, 3)
eggs * 3

To create a proper method, we do this:

py> import new
py> inst.eggs = new.instancemethod(eggs, None, C)
py> inst.eggs(3)
eggs * 3

You can now delete the top-level function eggs, and the method bound to
inst will not be affected. Because eggs is bound to the instance, new
instances will not understand it:

py> C().eggs(3)
Traceback (most recent call last):
File "<stdin>", line 1, in ?
AttributeError: C instance has no attribute ''eggs''

Or you could put the method in the class and have all instances recognise
it:

py> C.eggs = new.instancemethod(eggs, None, C)
py> C().eggs(3)
eggs * 3

Instead of passing None as the second argument to instancemethod, you can
pass an instance. If you do that, self will automatically be set to that
instance instead of the one doing the calling:

py> def truffles(self, x):
.... print self, x
....
py> C.truffles = new.instancemethod(truffles, inst, C)
py> inst
<__main__.C instance at 0xf6d2d6cc>
py> inst.truffles(3)
<__main__.C instance at 0xf6d2d6cc> 3

No surprises there. But look what happens when we use a new instance:

py> x = C()
py> x
<__main__.C instance at 0xf6d2ca6c> x.truffles(3)


<__main__.C instance at 0xf6d2d6cc> 3
Hope this is useful to some folks.
--
Steven.

解决方案

One caveat, as I recently discovered, to dynamically adding methods is
that it doesn''t work for __foo__ methods. For example, you can''t make
an object into an iterator by dynamically assigning bound methods to
obj.__iter__ and obj.next. Same thing with __getitem__, __setitem__,
etc; adding them directly to the instance doesn''t get you a
subscriptable object. This is because the interpreter looks at the
methods defined by the class, rather than the instance''s attributes.

Just a word of caution.

Collin Winter


Steven D''Aprano wrote:

Or you could put the method in the class and have all instances recognise
it:

py> C.eggs = new.instancemethod(eggs, None, C)
py> C().eggs(3)
eggs * 3



Why not just add it to the class directly? You just have to be sure
it''s a class and not an instance of a class.

def beacon(self, x): .... print "beacon + %s" % x
.... C.beacon = beacon
dir(A) [''__doc__'', ''__module__'', ''beacon'', ''ham'', ''spam''] A.beacon(3) beacon + 3 del beacon
dir(A) [''__doc__'', ''__module__'', ''beacon'', ''ham'', ''spam''] A.beacon(3) beacon + 3 dir(C)


[''__doc__'', ''__module__'', ''beacon'', ''ham'', ''spam'']
Cheers,
Ron


On Sun, 25 Sep 2005 14:52:56 +0000, Ron Adam wrote:

Steven D''Aprano wrote:

Or you could put the method in the class and have all instances recognise
it:

py> C.eggs = new.instancemethod(eggs, None, C)
py> C().eggs(3)
eggs * 3



Why not just add it to the class directly? You just have to be sure
it''s a class and not an instance of a class.



Because I started off explicitly adding functions to instances directly,
and when I discovered that didn''t work properly, I never even tried adding
it to the class until after I discovered that instancemethod() worked.

As far as I can see, Python''s treatment of functions when you dynamically
add them to classes and instances is rather confused. See, for example:

py> class Klass:
.... pass
....
py> def eggs(self, x):
.... print "eggs * %s" % x
....
py> inst = Klass() # Create a class instance.
py> inst.eggs = eggs # Dynamically add a function/method.
py> inst.eggs(1)
Traceback (most recent call last):
File "<stdin>", line 1, in ?
TypeError: eggs() takes exactly 2 arguments (1 given)

From this, I can conclude that when you assign the function to the
instance attribute, it gets modified to take two arguments instead of one.
Test it by explicitly passing an instance:

py> inst.eggs(inst, 1)
eggs * 1

My hypothesis is confirmed.

Can we get the unmodified function back again?

py> neweggs = inst.eggs
py> neweggs(1)
Traceback (most recent call last):
File "<stdin>", line 1, in ?
TypeError: eggs() takes exactly 2 arguments (1 given)

Nope. That is a gotcha. Storing a function object as an attribute, then
retrieving it, doesn''t give you back the original object again.

So while you can do this:

def printgraph(f): # print a graph of a function
parameters = get_params()
draw_graph(f, parameters)

you can''t do this:

def printgraph(function): # print a graph of a function
parameters = get_params()
parameters.function = f # WARNING: f is modified here
draw_graph(parameters)

When storing the function object as an instance object, it is
half-converted to a method: even though eggs is modified to expect two
arguments, Python doesn''t know enough to automatically pass the instance
object as the first argument like it does when you call a true instance
method.

Furthermore, the type of the attribute isn''t changed:

py> type(eggs)
<type ''function''>
py> type(inst.eggs)
<type ''function''>

But if you assign a class attribute to a function, the type changes, and
Python knows to pass the instance object:

py> Klass.eggs = eggs
py> inst2 = Klass()
py> type(inst2.eggs)
<type ''instancemethod''>
py> inst2.eggs(1)
eggs * 1

The different behaviour between adding a function to a class and an
instance is an inconsistency. The class behaviour is useful, the instance
behaviour is broken.

>>> def beacon(self, x): ... print "beacon + %s" % x
...



Did you mean bacon? *wink*

>>> C.beacon = beacon
>>> dir(A)


[''__doc__'', ''__module__'', ''beacon'', ''ham'', ''spam'']



Okay, you aren''t showing all your code. What is A?
--
Steven.


这篇关于动态添加和删除方法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆