Переопределение метода на уровне экземпляра



есть ли способ в Python, чтобы переопределить метод класса на уровне экземпляра?
Например:



class Dog:
def bark(self):
print "WOOF"

boby = Dog()
boby.bark() # WOOF
# METHOD OVERRIDE
boby.bark() # WoOoOoF!!
269   9  

9 ответов:

пожалуйста, не делайте этого, как показано на рисунке. Вы код становится нечитаемым, когда вы monkeypatch экземпляр будет отличаться от класса.

вы не можете отлаживать monkeypatched код.

когда вы найдете ошибку в 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"
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.

вам нужно использовать MethodType из модуля типов. Цель 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.

class Dog:
    def bark(self):
        print "Woof"

def new_bark(self):
    print "Woof Woof"

foo = Dog()

# "Woof"
foo.bark()

# replace bark with new_bark for this object only
foo.bark = new_bark.__get__(foo, Dog)

foo.bark()
# "Woof Woof"

назначив связанный метод атрибуту экземпляра, вы создали почти полную имитацию переопределения метода. Одна удобная функция, которая отсутствует, - это доступ к версии no-arg super, так как вы не в определении класса. Другое дело, что __name__ атрибут вашего связанного метода не будет принимать имя функции, которую он переопределяет, как это было бы в определении класса, но вы все равно можете установить его вручную. Третье отличие заключается в том, что ваш метод с ручной привязкой является простой ссылкой на атрибут, которая просто является функцией. Элемент . оператор не делает ничего, кроме извлечения этой ссылки. С другой стороны, при вызове регулярного метода из экземпляра процесс привязки создает новый метод привязки каждый раз.

единственная причина, по которой это работает, кстати, заключается в том, что атрибуты экземпляра переопределяют не дескрипторов. Дескрипторы данных имеют __set__ методы, которые методы (К счастью для вас) не делают. Дескрипторы данных в классе фактически имеют приоритет над любыми атрибутами экземпляра. Вот почему вы можете назначить свойству: их __set__ метод вызывается при попытке выполнить назначение. Я лично хотел бы сделать этот шаг далее и скрыть фактическое значение базового атрибута в экземпляре __dict__, где он недоступен обычными средствами именно потому, что свойство затеняет его.

вы должны также иметь в виду, что это бессмысленно для магия (двойное подчеркивание) методы. Магические методы, конечно, могут быть переопределены таким образом, но операции, которые используют их только на вид. Например, вы можете установить __contains__ что-то особенное в вашем случае, но вызов x in instance проигнорировал бы это и использовал type(instance).__contains__(instance, x) вместо. Это относится ко всем магическим методам, указанным в Python модель данных.

Так как функции являются объектами первого класса в 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

Я нашел, что это самый точный ответ на исходный вопрос

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

хотя мне понравилась идея наследования от S. Lott и согласна с вещью " 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

Comments

    Ничего не найдено.