Есть ли способ в 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
.Назначив связанный метод атрибуту экземпляра, вы создали почти полную симуляцию переопределения метода. Одна удобная функция, которой не хватает, - это доступ к версии без аргументов
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()
и результаты
источник
Хотя мне понравилась идея наследования от С. Лотта, и я согласен с «типом (а)», но поскольку функции тоже имеют доступные атрибуты, я думаю, что этим можно управлять следующим образом:
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__
и вывод:
источник
Уважаемый, это не отменяет, вы просто дважды вызываете одну и ту же функцию с объектом. В основном переопределение относится к более чем одному классу. когда один и тот же метод подписи существует в разных классах, то какая функция, которую вы вызываете, решает объект, который это вызывает. В 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()
источник