2012-02-24 17 views
16

Ho una classe che è una super classe per molte altre classi. Mi piacerebbe sapere (nel init() della mia super-classe, se la sottoclasse ha ignorato un metodo specificoCome rilevare il sovraccarico del metodo in sottoclassi in python?

Ho provato a fare questo con un metodo di classe, ma i risultati erano sbagliate:.

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 

c'è un modo per un super-classe per sapere se una sottoclasse ha ignorato un metodo?

+1

Could dici qualche parola su * perché * vorresti farlo? – NPE

+0

Immediatamente, non viene in mente nulla, ma se si includono le docstring per i propri metodi, vengono sovrascritte quando il metodo viene sovrascritto. Dovresti quindi essere in grado di seguirlo con 'MyClass.methodname .__ doc__'.Ma trovo questa soluzione molto compromessa, motivo per cui non la sto postando come risposta – inspectorG4dget

+0

Fondamentalmente voglio che la super-classe abbia questi metodi definiti semplicemente 'pass' e abbiano un metodo separato (in realtà __init__) chiama queste funzioni. Mi piacerebbe che init inserisse delle istruzioni di stampa che dicono che stiamo iniziando o terminando la funzione, ma se quella funzione è ancora nulla, queste affermazioni sembrano fuori luogo e vorrei eliminarle. – Brian

risposta

7

È possibile utilizzare il proprio decoratore. Ma questo è un trucco e funziona solo su classi in cui controlli l'implementazione.

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

Sono andato in una direzione leggermente diversa, decorando il mio metodo con @native e assumendo che le sottoclassi non lo faranno. Penso che lo farò in questo modo dato che non ho molto controllo sulle sottoclassi. Funziona bene, grazie per il vostro aiuto! – Brian

+0

O anche più semplice, 'Super.method._original = True' è il corpo della classe' Super() ', e quindi controlla se' hasattr (self.method, '_original') '. –

3

è possibile confrontare ciò che è in __dict__ della classe con la funzione all'interno del metodo è possibile recuperare dal oggetto - la funzione "detect_overriden" low fa - il trucco è passare la "classe genitore" per il suo nome, proprio come si fa in una chiamata a "super" - altrimenti non è facile recuperare gli attributi dalla stessa classe parent invece di quelli del sottoclasse:

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

modificare codice modificato per funzionare bene con classmethods così: se rileva un classmethod sulla classe genitore, estrae la funzione sottostante prima di procedere.

- Un altro modo di fare questo, senza la necessità di codificare il nome della classe, potrebbe essere quella di seguire l'istanza della classe (self.__class__) ordine di risoluzione metodo (dato dall'attributo __mro__) e la ricerca di duplicati dei metodi e attributi definiti in ogni classe lungo la catena di ereditarietà.

+1

In Python 3 è l'attributo '__func__'. –

4

Sembra più semplice e sufficiente per fare questo confrontando il sottoinsieme comune dei dizionari di un'istanza e la classe base stessa, ad esempio:

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

corre e dà:

['method1'] 
[] 
4

In risposta alla risposta https://stackoverflow.com/a/9437273/1258307, poiché non ho ancora abbastanza crediti per commentarlo, non funzionerà con python 3 a meno che non si sostituisca im_func con __func__ e non funzionerà anche in python 3.4 (e molto probabilmente in avanti) poiché le funzioni non hanno più l'attributo __func__, solo i metodi associati.

EDIT: Ecco la soluzione alla domanda iniziale (che ha lavorato su 2.7 e 3.4, e presumo tutte le altre versioni in mezzo):

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 

Ed ecco l'output:

same 
different 
same 
+0

Solo per la nota: in cython (almeno in python 3.4), 'self.method .__ code__' è assente, ma funziona in clean Python. Per qualche ragione, '__code__' manca in' dir (self.method) ' – socketpair

Problemi correlati