Чи є в Python спосіб замінити метод класу на рівні екземпляра? Наприклад:
class Dog:
def bark(self):
print "WOOF"
boby = Dog()
boby.bark() # WOOF
# METHOD OVERRIDE
boby.bark() # WoOoOoF!!
Відповіді:
Не робіть цього, як показано. Ваш код стає нечитабельним, коли monkeypatch примірник відрізняється від класу.
Ви не можете налагодити код, який виправлений за допомогою мавпи.
Коли ви знайдете помилку в bobyі print type(boby), ви побачите, що (а) це Собака, але (б) з якоїсь незрозумілої причини вона не гавкає правильно. Це кошмар. Не роби цього.
Будь ласка, зробіть це замість цього.
class Dog:
def bark(self):
print "WOOF"
class BobyDog( Dog ):
def bark( self ):
print "WoOoOoF!!"
otherDog= Dog()
otherDog.bark() # WOOF
boby = BobyDog()
boby.bark() # WoOoOoF!!
Так, можливо:
class Dog:
def bark(self):
print "Woof"
def new_bark(self):
print "Woof Woof"
foo = Dog()
funcType = type(Dog.bark)
# "Woof"
foo.bark()
# replace bark with new_bark for this object only
foo.bark = funcType(new_bark, foo, Dog)
foo.bark()
# "Woof Woof"
funcTypeробить і чому це потрібно.
funcType = types.MethodType(після імпорту types) замість funcType = type(Dog.bark).
__get__функцію, щоб прив’язати її до екземпляра.
Вам потрібно використовувати MethodType з typesмодуля. Призначення MethodType- перезаписати методи рівня екземпляра (щоб вони selfмогли бути доступними в перезаписаних методах).
Див. Приклад нижче.
import types
class Dog:
def bark(self):
print "WOOF"
boby = Dog()
boby.bark() # WOOF
def _bark(self):
print "WoOoOoF!!"
boby.bark = types.MethodType(_bark, boby)
boby.bark() # WoOoOoF!!
Щоб пояснити чудову відповідь @ codelogic, я пропоную більш чіткий підхід. Це той самий прийом, який .оператор ретельно виконує для прив’язки методу класу, коли ви отримуєте доступ до нього як атрибут екземпляра, за винятком того, що ваш метод насправді буде функцією, визначеною поза класом.
Працюючи з кодом @ codelogic, різниця лише в тому, як метод пов'язаний. Я використовую той факт, що функції та методи не є дескрипторами даних у Python, і викликаю __get__метод. Зауважте, зокрема, що і оригінал, і заміна мають однакові підписи, це означає, що ви можете написати заміну як метод повного класу, отримуючи доступ до всіх атрибутів примірника через self.
клас собака:
деф кора (сама):
друк "Woof"
def new_bark (self):
друк "Woof Woof"
foo = собака ()
# "Вуф"
foo.bark ()
# замінити кору на new_bark лише для цього об’єкта
foo.bark = new_bark .__ отримати __ (foo, собака)
foo.bark ()
# "Woof Woof"
Присвоївши прив'язаний метод атрибуту екземпляра, ви створили майже повне моделювання перевизначення методу. Відсутня одна зручна функція - це доступ до версії no-arg super, оскільки ви не знаходитесь у визначенні класу. Інша справа, що __name__атрибут вашого зв’язаного методу не буде приймати ім’я функції, яку він замінює, як це було б у визначенні класу, але ви все одно можете встановити його вручну. Третя відмінність полягає в тому, що ваш метод, прив'язаний вручну, є простим посиланням на атрибут, який просто є функцією. .Оператор нічого не робить , але витягти це посилання. З іншого боку, при виклику звичайного методу з екземпляра, процес прив’язки кожного разу створює новий зв’язаний метод.
Єдина причина, що це працює, до речі, полягає в тому, що атрибути екземпляра замінюють дескриптори , що не стосуються даних . Дескриптори даних мають __set__методи, яких методів (на щастя для вас) немає. Дескриптори даних у класі насправді мають пріоритет перед будь-якими атрибутами екземпляра. Ось чому ви можете призначити властивість: їх __set__метод викликається при спробі зробити присвоєння. Мені особисто подобається зробити цей крок далі і приховати фактичне значення базового атрибута в екземплярі __dict__, де він звичайно недоступний саме тому, що властивість затінює його.
Також слід пам’ятати, що для магічних методів (подвійне підкреслення) це безглуздо . Звичайно, магічні методи можна замінити таким чином, але операції, що їх використовують, розглядають лише тип. Наприклад, ви можете встановити __contains__щось особливе у вашому екземплярі, але виклик не x in instanceбуде враховувати це і використовувати type(instance).__contains__(instance, x)замість цього. Це стосується всіх магічних методів, зазначених у моделі даних Python .
class Dog:
def bark(self):
print "WOOF"
boby = Dog()
boby.bark() # WOOF
# METHOD OVERRIDE
def new_bark():
print "WoOoOoF!!"
boby.bark = new_bark
boby.bark() # WoOoOoF!!
Якщо потрібно, ви можете використовувати bobyзмінну всередині функції. Оскільки ви перевизначаєте метод лише для цього об'єкта одного екземпляра, цей спосіб простіший і має точно такий же ефект, як використання self.
patchingі це правильний спосіб це зробити (наприклад, boby = Dog()і boby.bark = new_bark). Це неймовірно корисно при модульному тестуванні елементів управління. Для отримання додаткових пояснень див. Tryolabs.com/blog/2013/07/05/run-time-method-patching-python (приклади) - ні, я не пов’язаний із пов’язаним сайтом чи автором.
Оскільки тут ніхто не згадує functools.partial:
from functools import partial
class Dog:
name = "aaa"
def bark(self):
print("WOOF")
boby = Dog()
boby.bark() # WOOF
def _bark(self):
print("WoOoOoF!!")
boby.bark = partial(_bark, boby)
boby.bark() # WoOoOoF!!
Оскільки функції є об'єктами першого класу в Python, ви можете передавати їх під час ініціалізації об'єкта класу або замінювати його в будь-який час для даного екземпляра класу:
class Dog:
def __init__(self, barkmethod=None):
self.bark=self.barkp
if barkmethod:
self.bark=barkmethod
def barkp(self):
print "woof"
d=Dog()
print "calling original bark"
d.bark()
def barknew():
print "wooOOOoof"
d1=Dog(barknew)
print "calling the new bark"
d1.bark()
def barknew1():
print "nowoof"
d1.bark=barknew1
print "calling another new"
d1.bark()
і результати є
calling original bark
woof
calling the new bark
wooOOOoof
calling another new
nowoof
Хоча мені сподобалася ідея успадкування від С. Лотта і я погоджуюся з річчю 'type (a)', але оскільки функції теж мають доступні атрибути, я думаю, що нею можна керувати таким чином:
class Dog:
def __init__(self, barkmethod=None):
self.bark=self.barkp
if barkmethod:
self.bark=barkmethod
def barkp(self):
"""original bark"""
print "woof"
d=Dog()
print "calling original bark"
d.bark()
print "that was %s\n" % d.bark.__doc__
def barknew():
"""a new type of bark"""
print "wooOOOoof"
d1=Dog(barknew)
print "calling the new bark"
d1.bark()
print "that was %s\n" % d1.bark.__doc__
def barknew1():
"""another type of new bark"""
print "nowoof"
d1.bark=barknew1
print "another new"
d1.bark()
print "that was %s\n" % d1.bark.__doc__
і результат:
calling original bark
woof
that was original bark
calling the new bark
wooOOOoof
that was a new type of bark
another new
nowoof
that was another type of new bark
Шановний, це не перевизначення, ви просто двічі викликаєте одну і ту ж функцію з об'єктом. В основному перевизначення пов'язане з кількома класами. коли однаковий метод підпису існує в різних класах, тоді яка функція, яку ви викликаєте, вирішує об'єкт, який це викликає. Перевизначення можливо в python, коли ви робите більше одного класу, пише однакові функції і ще одне, щоб поділитися тим, що перевантаження не дозволяється в python
Я виявив, що це найточніша відповідь на вихідне запитання
https://stackoverflow.com/a/10829381/7640677
import a
def _new_print_message(message):
print "NEW:", message
a.print_message = _new_print_message
import b
b.execute()