是否可以在不更改同一类的所有其他实例的情况下更改实例的方法实现?

时间:2009-10-30 01:42:07

标签: python

我不太了解python(之前从未使用过它:D),但我似乎无法在网上找到任何东西。也许我只是没有谷歌正确的问题,但在这里我去:

我想更改实例的特定方法的实现。当我用Google搜索它时,我发现你可以这样做,但它改变了同一个类的所有其他实例的实现,例如:

def showyImp(self):
    print self.y

class Foo:
    def __init__(self):
        self.x = "x = 25"
        self.y = "y = 4"

    def showx(self):
        print self.x

    def showy(self):
         print "y = woohoo"

class Bar:
    def __init__(self):
        Foo.showy = showyImp
        self.foo = Foo()

    def show(self):
        self.foo.showx()
        self.foo.showy()

if __name__ == '__main__':
    b = Bar()
    b.show()
    f = Foo()
    f.showx()
    f.showy()

这不能按预期工作,因为输出如下:

  

x = 25

     

y = 4

     

x = 25

     

y = 4

我希望它是:

  

x = 25

     

y = 4

     

x = 25

     

y = woohoo

我尝试用这个改变Bar的init方法:

def __init__(self):
    self.foo = Foo()
    self.foo.showy = showyImp

但是我收到以下错误消息:

  

showyImp()只需1个参数(0给定)

所以是的...我尝试使用setattr(),但似乎与self.foo.showy = showyImp相同。

有任何线索吗? :)

6 个答案:

答案 0 :(得分:23)

您想了解的关于Python Attributes and Methods的所有信息。

是的,这是一个间接的答案,但它展示了许多技巧,并解释了一些更复杂的细节和“魔力”。

要获得“更直接”的答案,请考虑python's new module。特别是,请查看允许将方法“绑定”到实例的instancemethod函数 - 在这种情况下,允许您在方法中使用“self”。

import new
class Z(object):
  pass
z = Z() 
def method(self):
  return self
z.q = new.instancemethod(method, z, None)
z is z.q()  # true

答案 1 :(得分:23)

从Python 2.6开始,您应该使用types模块的MethodType类:

from types import MethodType

class A(object):
    def m(self):
        print 'aaa'

a = A()

def new_m(self):
    print 'bbb'

a.m = MethodType(new_m, a)

然而,正如另一个答案所指出的,这对于新式课程的“魔术”方法不起作用,例如__str__()

答案 2 :(得分:8)

如果您需要为特殊方法(对于新式类 - 这是您应该始终使用的,也是Python 3中唯一的类 - - 查看了类,而不是实例),你可以只创建一个实例类,例如......:

self.foo = Foo()
meths = {'__str__': lambda self: 'peekaboo!'}
self.foo.__class__ = type('yFoo', (Foo,), meths)

编辑:我被要求澄清这种方法的优势,以及new.instancemethod ......:

>>> class X(object): 
...   def __str__(self): return 'baah'
... 
>>> x=X()
>>> y=X()
>>> print x, y
baah baah
>>> x.__str__ = new.instancemethod(lambda self: 'boo!', x)
>>> print x, y
baah baah

正如您所看到的,new.instance方法在这种情况下完全没用。 OTOH ...

>>> x.__class__=type('X',(X,),{'__str__':lambda self:'boo!'})
>>> print x, y
boo! baah

...分配一个新类对于这个案例和其他所有案例都很有用。顺便说一句,我希望很清楚,一旦你对给定的实例做了这个,你可以稍后在它的x.__class__中添加更多方法和其他类属性,并且本质上只影响那个实例!

答案 3 :(得分:5)

如果您绑定到实例,则不应包含self参数:

>>> class Foo(object):
...     pass
... 
>>> def donothing():
...     pass
... 
>>> f = Foo()
>>> f.x = donothing
>>> f.x()
>>> 

如果你绑定到一个类,你确实需要self参数:

>>> def class_donothing(self):
...     pass
... 
>>> foo.y = class_donothing
>>> f.y()
>>> 

答案 4 :(得分:0)

你的例子有点扭曲和复杂,我不太明白它与你的问题有什么关系。如果你愿意,请随意澄清。

然而,假设我正在正确地阅读你的问题,你很容易做你想做的事。

class Foo(object):
    def bar(self):
        print('bar')

def baz():
    print('baz')

在口译员......

>>> f = Foo()
>>> f.bar()
bar
>>> f.bar = baz
>>> f.bar()
baz
>>> g = Foo()
>>> g.bar()
bar
>>> f.bar()
baz

答案 5 :(得分:0)

不要这样做。

更改一个实例的方法是错误的。

以下是面向对象设计的规则。

  1. 避免魔术。

  2. 如果您不能使用继承,请使用委托。

  3. 这意味着每次你认为你需要魔法时,你应该在对象周围写一个“包装器”或Facade来添加你想要的功能。

    只需写一个包装器。