2012-02-24 34 views
16

我有一個課程,是許多其他課程的超級課程。我想知道(在我的超一流的初始化()如果子類已覆蓋的具體方法如何檢測python中子類中的方法重載?

我試圖用一個類的方法來做到這一點,但結果是錯誤的:

class Super: 
    def __init__(self): 
     if self.method == Super.method: 
     print 'same' 
     else: 
     print 'different' 

    @classmethod 
    def method(cls): 
     pass 

class Sub1(Super): 
    def method(self): 
     print 'hi' 

class Sub2(Super): 
    pass 

Super() # should be same 
Sub1() # should be different 
Sub2() # should be same 

>>> same 
>>> different 
>>> different 

有沒有什麼辦法了超一流的知道,如果一個子類已覆蓋的方法?

+1

莫非你說幾句關於*爲什麼*你想這樣做? – NPE 2012-02-24 19:21:10

+0

立即想到什麼,但是如果您爲方法添加文檔字符串,那麼當方法被覆蓋時,它們將被覆蓋。因此,您應該可以使用'MyClass.methodname .__ doc__'來跟蹤它。但我覺得這個解決方案很破解,這就是爲什麼我不把它作爲答案發布的原因 – inspectorG4dget 2012-02-24 19:21:50

+0

基本上我想讓超類有這些方法定義爲'通',並有一個單獨的方法(實際上__init__)調用這些函數。我想讓init在打印語句中聲明我們正在開始或結束函數,但如果該函數仍然爲空,這些語句看起來不合適,我想廢除它們。 – Brian 2012-02-24 19:52:38

回答

7

您可以使用自己的裝飾。但這是一個竅門,只能在你控制實現的類上工作。

def override(method): 
    method.is_overridden = True 
    return method 

class Super: 
    def __init__(self): 
     if hasattr(self.method, 'is_overridden'): 
     print 'different' 
     else: 
     print 'same' 
    @classmethod 
    def method(cls): 
     pass 

class Sub1(Super): 
    @override 
    def method(self): 
     print 'hi' 

class Sub2(Super): 
    pass 

Super() # should be same 
Sub1() # should be different 
Sub2() # should be same 

>>> same 
>>> different 
>>> same 
+4

我進入了一個稍微不同的方向,用@native裝飾我的方法,並假設子類不會。我想我會這樣做,因爲我沒有太多的控制子類。 它運作良好,謝謝你的幫助! – Brian 2012-02-24 20:46:42

+0

甚至更​​簡單的說'Super.method._original = True'是'Super()'類的主體,然後檢查'hasattr(self.method,'_original')'。 – 2017-09-06 22:52:30

3

您可以比較無論是在類的__dict__與 可以從檢索方法內部功能object - 「detect_overriden」functionbel低是這樣做的 - 訣竅是通過 「父類」的名稱,就像一個人在調用「超級」時一樣 - 否則,從父類本身 中取回屬性並不容易,而不是那些子類:如果它檢測到父類中的類方法,在繼續之前提取的基本功能 :

# -*- coding: utf-8 -*- 
from types import FunctionType 

def detect_overriden(cls, obj): 
    res = [] 
    for key, value in cls.__dict__.items(): 
     if isinstance(value, classmethod): 
      value = getattr(cls, key).im_func 
     if isinstance(value, (FunctionType, classmethod)): 
      meth = getattr(obj, key) 
      if not meth.im_func is value: 
       res.append(key) 
    return res 


# Test and example 
class A(object): 
    def __init__(self): 
     print detect_overriden(A, self) 

    def a(self): pass 
    @classmethod 
    def b(self): pass 
    def c(self): pass 

class B(A): 
    def a(self): pass 
    #@classmethod 
    def b(self): pass 

編輯改變的代碼以做工精細與classmethods爲好。

- 這樣做,而不必硬代碼的類名的另一種方法,是遵循實例的類(self.__class__)方法解析順序(由__mro__屬性給出)和搜索的方法重複和沿着繼承鏈的每個類中定義的屬性。

+1

在Python 3中,它是'__func__'屬性。 – 2017-03-16 06:21:55

4

看來簡單的和充分的比較實例的字典的公共子集和基類本身,如要做到這一點:

def detect_overridden(cls, obj): 
    common = cls.__dict__.keys() & obj.__class__.__dict__.keys() 
    diff = [m for m in common if cls.__dict__[m] != obj.__class__.__dict__[m]] 
    print(diff) 

def f1(self): 
    pass 

class Foo: 
    def __init__(self): 
    detect_overridden(Foo, self) 
    def method1(self): 
    print("Hello foo") 
    method2=f1 

class Bar(Foo): 
    def method1(self): 
    print("Hello bar") 
    method2=f1 # This is pointless but not an override 
# def method2(self): 
# pass 

b=Bar() 
f=Foo() 

奔跑,並給出:

['method1'] 
[] 
4

在回答https://stackoverflow.com/a/9437273/1258307時,由於我還沒有足夠的信用評論它,它不會在python 3下工作,除非您替換im_func__func__,並將不會在python 3.4(並最有可能的),因爲函數不再具有__func__屬性,只有綁定的方法。

編輯:這裏是解決原來的問題(這在2.7和3.4的工作,我認爲兩者之間的所有其它版本):

class Super: 
     def __init__(self): 
      if self.method.__code__ is Super.method.__code__: 
       print('same') 
      else: 
       print('different') 

     @classmethod 
     def method(cls): 
      pass 

    class Sub1(Super): 
     def method(self): 
      print('hi') 

    class Sub2(Super): 
     pass 

    Super() # should be same 
    Sub1() # should be different 
    Sub2() # should be same 

而這裏的輸出:

same 
different 
same 
+0

只是爲了說明:在cython(至少在Python 3.4中),'self.method .__ code__'不存在,但在乾淨的Python中起作用。由於某種原因,'__code__'在'dir(self.method)'中缺失 – socketpair 2015-10-15 20:54:30