Можно ли изменить реализацию метода экземпляра, не изменяя все другие экземпляры того же класса? - PullRequest
20 голосов
/ 30 октября 2009

Я не очень хорошо знаю 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()

Это не работает должным образом, потому что вывод следующий:

х = 25

y = 4

х = 25

y = 4

И я хочу, чтобы это было:

х = 25

у = 4

х = 25

y = woohoo

Я пытался изменить метод init Бар с помощью этого:

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

Но я получаю следующее сообщение об ошибке:

showyImp () принимает ровно 1 аргумент (задано 0)

Так что да ... Я пытался использовать setattr(), но похоже, что он такой же, как self.foo.showy = showyImp.

Есть подсказка? :)

Ответы [ 6 ]

23 голосов
/ 19 августа 2012

Начиная с Python 2.6, вы должны использовать класс MethodType модуля types:

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__().

23 голосов
/ 30 октября 2009

Все, что вы хотели знать о Атрибуты и методы Python .

Да, это косвенный ответ, но он демонстрирует ряд приемов и объясняет некоторые более сложные детали и "магию".

Для «более прямого» ответа рассмотрим новый модуль Python . В частности, посмотрите на функцию 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
8 голосов
/ 30 октября 2009

Если вам когда-либо понадобится сделать это для специального метода (который для класса нового стиля - который вы всегда должны использовать и единственный вид в 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 совершенно бесполезен в этом случае. Ото ...:

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

... назначение нового класса прекрасно работает в этом и во всех других случаях. Кстати, как я надеюсь, ясно, как только вы сделали это с данным экземпляром, вы можете позже добавить больше метода и других атрибутов класса к его x.__class__ и по сути повлиять только на этот один экземпляр!

5 голосов
/ 30 октября 2009

Если вы привязываетесь к экземпляру, вы не должны включать аргумент 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()
>>> 
0 голосов
/ 30 октября 2009

Не делай этого.

Изменение методов одного экземпляра просто неправильно.

Вот правила OO Design.

  1. Избегайте магии.

  2. Если вы не можете использовать наследование, используйте делегирование.

Это означает, что каждый раз, когда вы думаете, что вам нужно что-то волшебное, вы должны были написать «обертку» или Facade вокруг объекта, чтобы добавить нужные вам функции.

Просто напишите обертку.

0 голосов
/ 30 октября 2009

Ваш пример немного запутан и сложен, и я не совсем понимаю, как это связано с вашим вопросом. Не стесняйтесь уточнять, если хотите.

Тем не менее, довольно легко сделать то, что вы хотите сделать, при условии, что я правильно понял ваш вопрос.

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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...