2012-01-04 19 views
7

Değiştirmek istemediğim ana projede bir dersim var.Maymun eklendiğinde orijinal yöntem nasıl aranır?

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

Bir eklenti mechansim oluşturmaya çalışıyorum.

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

bir eklenti böyle yazılabilir::

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

Bunların hepsi çok iyi çalışıyor Şimdiye kadar iyi, böyle bir uzatma noktası vardır. Şimdi "John DOE" aldım.

Sorunum, özgün name() yönteminin oldukça karmaşık olabileceğidir. Başka bir deyişle, AExtended numaralı telefondan self.lastname.upper() numaralı telefonu arayacağım paranız yok. Artık mevcut olmayan "süper" yöntemini çağırmak istiyorum, çünkü üzerine yazılmıştır. Yardımlarınız için

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

Teşekkür: Böyle bir şey elde etmek için, benim kodunu değiştirmek nasıl

!

Düzenleme: Yapmaya çalıştığımın bazı açıklamaları.

  • Eklentinin A davranışını yamasını istiyorum. Ben mevcut tüketicileri değiştirmeye bütçesi yetmeyen
  • değiştirilebilir A gibi birçok sınıflar ben tam kontrol ve sorumluluğu
  • olması eklentileri istiyorum vardır O AExtendedA devralmak zorunda değildir doğru, ancak self.firstname'a erişmenin kolay bir yoluydu. Yardımcı olabiliyorsa farklı bir tasarım deseninin ardından problemim yok.

ben bir çözüm var, ama her zaman this non-standard package den @override dekoratör kullanmaktır Python gibi bir dilde en iyi olmayabilir

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

Evet, ([üst sınıf yöntemini çağırmak] nasıl okumak http://stackoverflow.com/questions/805066/how-to-call-a-parent-classs-method-from- çocuk class-in-python) ama 'AExtended.name' içinde,' self' bir 'A' – rds

+0

geçersiz kıldığından dolayı [özel bir yöntemi geçersiz kılarak] daha lcuky olabilir (http://stackoverflow.com/ soruları/2484215/nasıl yapılır-psthon-a-ebeveyn sınıfları-işlevleri-python) ama A yöntem adını değiştiremiyorum. – rds

+0

Açıklamalarınız ışığında, soru başlığını değiştirmenizi öneririm. Daha doğru bir başlık, "Maymun yamalandığında orijinal yöntem nasıl aranır?" – Weeble

cevap

3

İşte ima ettiğim şeyin tam bir örneği. Bana bağırmaktan ve cevaplarımı birleştirmemden ya da yeni bir cevap olarak bir alternatif sunmanın bir ya da bir kaçını reddetmekten çekinmeyin. Kodun kötü bir şekilde açıklamak yerine konuşmayı yapmasına izin vereceğim.:)

## 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

(Ve tabii ki, sen bir classmethod ve sadece bağımsız bir işlev yapmak ve '@ (A,' isim ') 'olarak' '' '' '' '' '' '' '' olarak adlandırın, böylece herhangi bir sınıf herhangi bir sınıf için kullanılabilir hale getirmek olarak adlandırmak için özgürsünüz. – Spencer

+0

'patch' dekoratörünü 'A' dışına çıkarmak ve aynı zamanda herhangi bir alt sınıfın yama ismini _outside_ yama dosyasına uygulamak daha kolay olur mu? Orijinal 'A' yöntemini değiştirdiğinizde, bir alt sınıfın' PatchedA 'amacı tam olarak nedir? –

0

Bir seçeneği genellemek çok şık ve zor değil. Ancak bu, iki işlevinizin farklı türlerde veya farklı sayıda argüman üzerinde çalıştığı durumlarda geçerli bir seçenektir. Bunun dışında, işlevinizi yeniden adlandırmanın yanı sıra yapabileceğiniz pek bir şey yok.

+0

İlginç bir paket, ancak bana nasıl yardımcı olabileceğini göremiyorum: -/ – 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

Hangi sınıfın kullanılmasının gerektiğini anladığımdan emin değilim. Tüm yöntemleri ABase'den 'A''ya (' A.name = ABase.name' vb.) Kopyalayıp daha sonra uzantılarını yüklemem gerekiyor ('A.name = AExtension.name')? – rds

+0

'A' zaten ABase' için tüm yöntemlere sahiptir, çünkü' A' 'ABase'yi genişletir. Ancak genel olarak yöntemleri başka sınıflara yeniden atama fikri şüpheli görünüyor. Bu konuda asgari bir eklenti mimarisinin doğru bir şekilde nasıl yapılacağı hakkında bazı fikirler bulabilirsiniz: http://stackoverflow.com/questions/932069/building-a-minimal-plugin-architecture-in-python – Ski

+0

@Skirmantas, süslemek alışılmadık bir şey değil Yapmak istediği gibi işlevler. Dilin daha yeni sürümleri, örneğimin yazmasını daha da kolaylaştırmak için sözdizimsel şekeri destekliyor. – Spencer

12

Bu, 'dekoratör' deseni olarak adlandırdığımız şeydir. Orijinali alan bir işlevi çağırmak için adın orijinal yeniden adlandırılmasını değiştirin. Daha sonra yeni bir işlev döndürür.

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

Daha sonra A.name çağıran yeniden düzenlemenin anda işlevine işaret hangi kendisine sunulacak geçerli örneği olarak self ve method ile decorate_name arayacak.

+0

+1 python dekoratörler istediğim şeyi yapıyorlar. Bu cevabı kabul edip genelleştirilebileceğini kontrol edeceğim. – rds

+0

Python 3 kullanıyorum. Hangi sözdizimi şeker yardımcı olabilir? – rds

+0

Bakınız: http://docs.python.org/reference/compound%5Fstmts.html#function - açık geçersiz kılmalar yapmak yerine, süslemek istediğiniz herhangi bir işlev def'sinin önüne '@ my_decorator 'yerleştirebilirsiniz. – Spencer

İlgili konular