Можно ли изменить реализацию метода экземпляра без изменения всех других экземпляров одного и того же класса?
Я не очень хорошо знаю python (никогда не использовал его раньше: D), но я не могу найти что-либо в Интернете. Возможно, я просто не ответил на правильный вопрос, но здесь я иду:
Я хочу изменить реализацию экземпляра определенного метода. Когда я искал ее для поиска, я обнаружил, что вы можете это сделать, но она меняет реализацию для всех других экземпляров одного и того же класса, например:
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
.
Любая подсказка?:)
Ответы
Ответ 1
Все, что вы хотели знать об атрибутах и методах 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
Ответ 2
Начиная с Python 2.6, вы должны использовать класс types
MethodType
class:
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__()
.
Ответ 3
Если вам когда-либо понадобится сделать это для специального метода (который для класса нового стиля - именно то, что вы всегда должны использовать и единственный вид в 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
... присвоение нового класса отлично подходит для этого случая и для всех остальных. BTW, как я надеюсь, ясно, как только вы сделали это с данным экземпляром, вы можете позже добавить к его x.__class__
еще один метод и другие атрибуты класса и на самом деле повлиять только на один экземпляр!
Ответ 4
Если вы привязываетесь к экземпляру, вы не должны включать аргумент 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()
>>>
Ответ 5
Не делай этого.
Изменение одного метода экземпляра просто неверно.
Вот правила OO Design.
Это означает, что каждый раз, когда вы думаете, что вам нужно что-то волшебное, вы должны были написать "обертку" или "Фасад" вокруг объекта, чтобы добавить нужные вам функции.
Просто напишите обертку.
Ответ 6
Ваш пример - извращенный и сложный, и я не совсем понимаю, что он должен делать с вашим вопросом. Не стесняйтесь уточнять, если хотите.
Однако, довольно легко сделать то, что вы хотите сделать, предполагая, что я правильно читаю ваш вопрос.
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