2015-06-18 12 views
5

Il mio caso d'uso specifico è che sto provando a sottoclasse pathlib.Path. Voglio essere in grado di aggiungere o sovrascrivere alcune funzionalità, ma voglio anche ereditare tutto il percorso. Percorso ha un __new__ e in essa ha:Come sottoclasse una classe che ha un __new__ e si basa sul valore di cls?

if cls is Path: 
    cls = WindowsPath if os.name == 'nt' else PosixPath 

In altre parole, il percorso richiede il passaggio alla classe propria. Il problema è che non so come creare entrambi la classe MY e chiamare Path.__new__ con cls == Path.

Ho provato molte cose e ognuna mi dà un problema diverso. Questo mi dà AttributeError: type object 'RPath' has no attribute '_flavour' perché sto provando a sovrascrivere la classe genitore.

python3:

class RPath(Path): 
    def __new__(cls, basedir, *args, **kwargs): 
     return Path.__new__(cls, *args, **kwargs) 

    def __init__(self, basedir, *pathsegs): 
     super().__init__() 
     self.basedir = basedir 

    def newfunction(self): 
     print('Something new') 

E questo dà restituisce un oggetto Path, e quindi non mi permette di fare il mio sostituzioni.

def __new__(cls, basedir, *args, **kwargs): 
    return Path.__new__(Path, *args, **kwargs) 

Ho anche provato vari usi di super(), senza alcun risultato.

Sembra che dovrebbe essere abbastanza facile. Cosa mi manca?

Aggiornamento: cosa sto cercando di realizzare? In particolare, voglio fare class RPath(basedir, *pathsegments):

rpath=RPath('\root\dir', 'relpath\path2\file.ext) 
assert rpath.basedir == '\root\dir' # True 
rpath.rebase('\new_basedir') 
assert rpath.basedir === '\newbasedir' # True 
# And while I'm at it 
assert rpath.str == str(rpath) # make str a property == __str__(self) 
+0

Qual è la funzionalità esatta che si desidera inserire in __new__? – Meitham

+0

Hai bisogno di chiarire cosa stai cercando di fare qui. La funzionalità di 'Path .__ new__' che stai tentando di conservare creerà un'istanza di una * classe diversa *, quindi se hai ottenuto quella parte del codice da eseguire, la sottoclasse andrebbe via. –

+0

Aggiornato sopra. Mi spiace di non essere stato abbastanza chiaro. E più specificamente, pathlib non chiama '__init__'. Quindi da qualche parte voglio prendere il primo argomento 'class RPath (baselib, * pathsegments)' e impostare 'self.baselib = baselib' in' __new__' o nel mio '__init__'. –

risposta

2

non credo che sara 'possibile nel solito modo. Ma anche se lo si potesse fare, non funzionerebbe, perché ciò che Path sta facendo non restituisce neanche un Path semplice, restituisce alcune sottoclassi (WindowsPath o PosixPath). Quindi le tue sostituzioni a Path non avranno effetto, perché se sei in grado di ereditare Path.__new__, restituirebbe comunque, ad esempio, un WindowsPath e WindowsPath erediterà dal percorso, non dalla sottoclasse del percorso personalizzato.

Sembra che pathlib.Path abbia una struttura di classe idiosincratica e dovrai eseguire un lavoro speciale per duplicarlo. Ad una prima ipotesi, è necessario creare sottoclassi proprie di WindowsPath e PosixPath, quindi creare una sottoclasse Path che deleghi a creare un'istanza di una di quelle anziché a se stessa.

0

Ecco la soluzione a, sebbene non utilizzi l'ereditarietà. Sospetto ancora che ci sia un modo di ereditarietà semplice e diretto per farlo (anche se vedi la risposta di BrenBarn per l'altra parte).

Questo metodo utilizza la composizione. La chiave utilizza __getattr__ e getattr() per delegare automaticamente tutte le richieste non trovate nella classe wrapper alla classe spostata. Ecco un esempio:

class RPath(object): 
    def __init__(self, basedir, *pathsegs): 
     self.p = Path(*pathsegs) 
     self.basedir = basedir 

    # Override existing behavior 
    def __eq__(self, other): 
     return type(other) is RPath and self.basedir == other.basedir and self.p == other.p  

    # Add new behavior 
    def cp(self): 
     return self.basedir/self.p  

    # Everything not found here, automatically delegate to Path 
    def __getattr__(self, attr): 
     return getattr(self.cp, attr) 
0

Ricordate che siete sotto assolutamente nessun obbligo chiamare in realtà di __new__ o __init__ superclasse. L'unico vincolo è che alla fine si chiama object.__new__(cls) per allocare un oggetto di tipo cls.

Quindi, è sufficiente copiare la logica pertinente da __new__ della propria superclasse nella propria.

Problemi correlati