Есть ли способ в Python переопределить метод класса на уровне экземпляра? Например:
class Dog:
def bark(self):
print "WOOF"
boby = Dog()
boby.bark() # WOOF
# METHOD OVERRIDE
boby.bark() # WoOoOoF!!
Ответы:
Пожалуйста, не делайте этого, как показано. Ваш код становится нечитаемым, когда вы изменяете экземпляр, чтобы он отличался от класса.
Вы не можете отлаживать код, исправленный обезьяной.
Когда вы найдете ошибку в 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.
класс Dog:
def кора (сам):
печать "Гав"
def new_bark (сам):
принт "Woof Woof"
foo = Собака ()
# «Гав»
foo.bark ()
# замените bark на new_bark только для этого объекта
foo.bark = new_bark .__ получить __ (фу, собака)
foo.bark ()
# "Гав гав"
Назначив связанный метод атрибуту экземпляра, вы создали почти полную симуляцию переопределения метода. Одна удобная функция, которой не хватает, - это доступ к версии без аргументов 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
Хотя мне понравилась идея наследования от С. Лотта, и я согласен с «типом (а)», но поскольку функции тоже имеют доступные атрибуты, я думаю, что этим можно управлять следующим образом:
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()