2012-02-24 4 views
16

私は他の多くのクラスのスーパークラスであるクラスを持っています。 。サブクラスは特定のメソッドをオーバーライドしている場合、私は、私はクラスメソッドでこれを実現しようとしたPythonのサブクラスでメソッドのオーバーロードを検出する方法は?

私のスーパークラスののinit()に(知っていただきたいと思いますが、結果は間違っていた:

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

+0

すぐに気になることはありませんが、メソッドにdocstringを含めると、メソッドがオーバーライドされたときに上書きされます。したがって、MyClass.methodname .__ doc__でそれを追跡することができます。しかし、私はこの解決策が非常にハックになることを知っているので、私は答えとして投稿していません。 – inspectorG4dget

+0

基本的に私はスーパークラスにこれらのメソッドを「パス」として定義し、別個のメソッド(実際には__init__)これらの関数を呼び出します。関数を開始したり終了したりすると言っているprintステートメントをinitに入れたいのですが、その関数がまだnullの場合、これらのステートメントは不調に見えるので、それらを取り除きたいと思います。 – Brian

答えて

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

+0

また、 'Super.method._original = True'は' Super() 'クラスの本体であり、' hasattr(self.method、 '_original') 'をチェックしてください。 –

3

あなたがから取得することができ メソッド内の関数を持つクラスの__dict__にあるものは何でも比較することができますオブジェクト - "detect_overriden"機能ベル低いです - その秘訣は の名前に "親クラス"を渡すことです。 それ以外の場合は、親クラス自体から属性を取り出すのは容易ではありません サブクラス:

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

編集は、同様にクラスメソッドで正常に動作するコードを変更: それは親クラスのクラスメソッドを検出した場合、先に進む前に、基本的な機能を抽出します。

- ハードコードにクラス名を持たずに、これを行うための別の方法は、インスタンスのクラス(__mro__属性によって与えられた)(self.__class__)メソッド解決順序をたどるとメソッドの重複を検索することであろうと、継承チェーンに沿った各クラスで定義された属性。

+1

Python 3では '__func__'属性です。 –

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、まだ十分なクレジットがないので、コメントしてください。im_funcを置き換えない限り、Python 3では動作しません__func__であり、機能はもはや__func__属性を持たず、バインドされたメソッドしか持たないので、Python 3.4(そしてほとんどの場合、後に続く)では機能しません。

編集:ここでは、元の質問へのソリューションです(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で動作します。何らかの理由で、 'dir(self.method)'に '__code__'がありません。 – socketpair

関連する問題