2012-01-04 2 views
7

У меня есть класс в основном проекте, который я не хочу менять.Как вызвать исходный метод, когда он очищен от обезьяны?

class A(): 
    def __init__(self, firstname, lastname): 
     self.firstname = firstname 
     self.lastname = lastname 

    def name(self): 
     # this method could be much more complex 
     return self.lastname.upper() 

Я пытаюсь создать плагин mechansim. До сих пор так хорошо, у меня есть точка расширения, как это:

if __name__ == '__main__': 
    ''' The main project has an extension point that allows me to do''' 
    # for each class extension such as AExtended: 
    A.name = AExtended.name 
    ''' After the extensions are loaded, some behaviours may be changed''' 
    a = A("John", "Doe") 
    print(a.name()) 

плагин можно записать так:

class AExtended(A): 
    ''' This is an extension I provide through a plugin mechanism 
    ''' 
    def name(self): 
     return self.firstname + ' ' + self.lastname.upper() 

Это все работает очень хорошо. Теперь я получаю «John DOE».

Моя проблема заключается в том, что метод оригинала name() может быть довольно сложным. Другими словами, я не могу позволить позвонить по телефону self.lastname.upper() в AExtended. Я бы назвал метод «супер», который больше не существует, потому что он был перезаписан.

Как я могу изменить свой код для того, чтобы добиться чего-то вроде этого:

class AExtended(A): 
    def name(self): 
     # I'm looking for a way to call the base implementation that was in A.name() 
     return self.firstname + ' ' + parent.name() 

Спасибо за вашу помощь!

Редактировать: Некоторые объяснения того, что я пытаюсь сделать.

  • Я хочу, чтобы плагин исправлял поведение A. Я не могу позволить себе, чтобы изменить существующие потребитель
  • Есть много классов, как A, которые могут быть изменены, я хотел бы плагин, чтобы иметь полный контроль и ответственность
  • Это правда AExtended не должна наследовать от A, но это был простой способ получить доступ к self.firstname. У меня нет проблем, следуя другому шаблону дизайна, если он может помочь.

У меня есть обходной путь, но это не очень элегантный и трудно обобщать

class AExtended(A): 
    def name(self): 
     # I'm looking for a way to call the base implementation that was in A.name() 
     return self.firstname + ' ' + self.parentname() 
#in main 
A.parentname = A.name 
A.name = AExtended.name 
+0

Да, я прочитал, как [вызвать метод родительского класса] (http://stackoverflow.com/questions/805066/how-to-call-a-parent-classs-method-from- child-class-in-python), но в 'AExtended.name',' self' является 'A' из-за переопределения – rds

+0

Я мог бы быть более lcuky с [переопределением частного метода] (http://stackoverflow.com/ questions/2484215/how-do-i-override-a-parent-classes-functions-in-python), но я не могу изменить имя метода A. – rds

+0

Учитывая ваши разъяснения, я предлагаю вам изменить заголовок вопроса. Более точным заголовком может быть «Как вызвать исходный метод, когда он пропатчен обезьяной»? – Weeble

ответ

3

Вот полный пример того, что я намекал. Не стесняйтесь кричать на меня, и я могу объединить свои ответы или сжать ни одного или что-то еще, просто проще предоставить альтернативу в качестве нового ответа. Я позволю коду говорить, вместо того, чтобы плохо объяснять это.:)

## Some shared class that is used all over the place and needs to be patched. 

class A(object): 
    def __init__(self): 
     self.firstname = 'Bob' 

    # Print my first name. 
    def name(self): 
     return self.firstname 

    # Use this to allow patching arbitrary methods... 
    @classmethod 
    def patch(cls, func_name): 
     def patch_by_name(new_func): 
      old_func = getattr(cls, func_name) 
      def patched_func(self): 
       return new_func(self, old_func) 
      setattr(cls, func_name, patched_func) 
     return patch_by_name 

## Some other area of the code where you want to throw in a patch 

class PatchedA(A): # doesn't need to subclass, but comes in handy sometimes 
    @A.patch('name') 
    def name(self, orig_func): 
     return 'I am ' + orig_func(self) + 'McWizwaz' 

print 'Who are you, A class?' 
print A().name() # prints 'I am Bob McWizwaz' 
+0

(И, конечно же, вы можете сделать это не classmethod и просто автономной функцией, а вместо этого называть его '@patch (A, 'name')', что делает его пригодным для использования в любом классе в любом месте.) – Spencer

+0

Willn 't было бы проще сделать, чтобы этот декоратор '@ patch' был за пределами' A', а также применил его к исправлению 'def name' _outside_ какого-либо подкласса? В чем же цель подкласса 'PatchedA' в любом случае, когда вы заменяете оригинальный метод' A'? –

0

Один вариант, который не всегда может быть лучшим в языке, как Python является использование @override декоратора из this non-standard package. Но это жизнеспособный вариант, только если ваши две функции работают на разные типы или различное количество аргументов. Помимо этого, вы не можете многое сделать, помимо переименования вашей функции.

+0

Интересный пакет, но я не вижу, как он может мне помочь: -/ – rds

1
class ABase(object): 
    def name(self): 
     pass 

class A(object): 
    pass 

class AExtension(ABase): 
    def name(self): 
     return ABase.name(self) 

A.name = AExtension.name 
+0

Я не уверен, что понимаю, какой класс следует использовать. Мне нужно скопировать все методы из 'ABase' в' A' ('A.name = ABase.name' и т. Д.), А затем загрузить расширения (' A.name = AExtension.name')? – rds

+0

'A' уже имеет все методы' ABase', потому что 'A' расширяет' ABase'. Но в целом идея переназначения методов другому классу выглядит подозрительно. Вы можете найти некоторые идеи относительно правильной минимальной архитектуры плагина в этом разделе: http://stackoverflow.com/questions/932069/building-a-minimal-plugin-architecture-in-python – Ski

+0

@Skirmantas, это не редкость украшать как он этого хочет. Более новые версии языка даже поддерживают синтаксический сахар, чтобы сделать мой пример еще проще для записи. – Spencer

12

Это то, что мы называем «декоратором». Замените первоначальное переназначение имени, чтобы вместо этого вызвать функцию, которая берет оригинал. Затем он возвращает новую функцию.

def name_decorator(method): 
    def decorate_name(self=None): 
     return stuff + method(self) 
    return decorate_name 
A.name = name_decorator(A.name) 

Позже вызова A.name будет вызывать decorate_name с self в качестве текущего экземпляра и method будут доступны для него, который указывает на функцию в момент переназначения.

+0

+1 потому что декораторы python делают именно то, что я хочу. Я приму этот ответ, когда смогу проверить, что его можно обобщить. – rds

+0

Я использую Python 3. Какой синтаксический сахар может помочь? – rds

+0

Смотрите: http://docs.python.org/reference/compound%5Fstmts.html#function - вместо явных переопределений вы можете поместить '@ my_decorator' прямо перед любой функцией def, которую хотите украсить. – Spencer

Смежные вопросы