2016-03-06 46 views
15

Provenendo da Java, sto faticando un po 'a scaricare l'ereditarietà, le classi astratte, i metodi statici e concetti simili di programmazione OO in Python.Python 3: super() solleva TypeError in modo imprevisto

devo un'implementazione di una classe di albero di espressione, data (semplificato) di

# Generic node class 
class Node(ABC): 
    @abstractmethod 
    def to_expr(self): 
     pass 

    @staticmethod 
    def bracket_complex(child): 
     s = child.to_expr() 
     return s if isinstance(child, Leaf) or isinstance(child, UnaryOpNode) else "(" + s + ")" 


# Leaf class - used for values and variables 
class Leaf(Node): 
    def __init__(self, val): 
     self.val = val 

    def to_expr(self): 
     return str(self.val) 


# Unary operator node 
class UnaryOpNode(Node): 
    def __init__(self, op, child): 
     self.op = op 
     self.child = child 

    def to_expr(self): 
     return str(self.op) + super().bracket_complex(self.child) 


# Binary operator node 
class BinaryOpNode(Node): 
    def __init__(self, op, lchild, rchild): 
     self.op = op 
     self.lchild = lchild 
     self.rchild = rchild 

    def to_expr(self): 
     return super().bracket_complex(self.lchild) + " " + str(self.op) + " " + super().bracket_complex(self.rchild) 


# Variadic operator node (arbitrary number of arguments) 
# Assumes commutative operator 
class VariadicOpNode(Node): 
    def __init__(self, op, list_): 
     self.op = op 
     self.children = list_ 

    def to_expr(self): 
     return (" " + str(self.op) + " ").join(super().bracket_complex(child) for child in self.children) 

Il metodo to_expr() funziona bene quando viene chiamato in casi di Leaf, UnaryOpNode e BinaryOpNode, ma solleva un TypeError quando viene chiamato su un istanza di VariadicOpNode:

TypeError: super(type, obj): obj must be an instance or subtype of type 

che cosa sto facendo di sbagliato in quella classe specifica che super() non è improvvisamente wo rking?

In Java il metodo statico viene ereditato, quindi non avrei nemmeno bisogno della chiamata super, ma in Python non sembra essere il caso.

+0

stai usando Python2 o 3? https://docs.python.org/2/library/functions.html#super – Jasper

+0

@ Jasper Dal momento che sta usando 'super' senza argomenti sta usando python3.3 + credo. – Bakuriu

+0

Off-topic: perché stai usando 'super()' in 'to_expr' per chiamare' bracket_complex'? Dovresti usare 'self', altrimenti rischi di introdurre problemi in sottoclassi che sovrascrivono' bracket_complex' –

risposta

12

Si sta utilizzando super() senza argomenti in un'espressione di generatore. Il super() è magico: si basa sulle informazioni nel frame del chiamante. Poiché l'espressione del generatore crea una funzione aggiuntiva, super() senza argomenti non funziona lì. Tuttavia, poiché il vostro superclasse non è probabile cambiare nel bel mezzo di esecuzione di un metodo, è possibile spostare fuori dal generatore di espressione - questo dovrebbe anche accelerare le cose:

def to_expr(self): 
    bracket_complex = super().bracket_complex 
    return (" " + str(self.op) + " ").join(bracket_complex(child) for child in self.children) 

Tuttavia come metodi statici sono " ereditato "in Python, è possibile chiamare il metodo super tramite self a condizione che non sia stato sovrascritto in una sottoclasse. Quindi in questo caso semplice si può scrivere:

def to_expr(self): 
    return (" " + str(self.op) + " ").join(self.bracket_complex(child) for child in self.children) 

Il dettaglio attuazione è che se sono forniti argomenti, il primo argomento è il valore compreso nella cella __class__ del telaio chiamante, e la seconda deve essere il primo argomento dato alla funzione chiamante. Solitamente si ottiene uno SystemError quando si utilizza super in un posto sbagliato, ma le espressioni del generatore sono racchiuse all'interno di una funzione di generatore implicito che crea un altro frame di chiamata. Sfortunatamente questa funzione ottiene un argomento, che porta lo super() a lamentarsi di questa eccezione.

così normalmente super() sarebbe passata Foo là come primo argomento, ma all'interno generatore di espressione, è stato passato un oggetto generatore - e quindi è ovvio che TypeError deve essere sollevato.

+0

Questo risolve il problema, evviva! –

9

rispondere alla tua domanda implicita:

In Java il metodo statico sarebbe ottenere ereditato così non avrei nemmeno bisogno la chiamata super, ma in Python questo non sembra essere il caso.

staticmethod s sono ereditarie:

class A: 
    @staticmethod 
    def a(): 
     print('Hello') 

class B(A): 
    def b(self): 
     self.a() 

b = B() 
b.a() 
b.b() 

uscite:

Hello 
Hello 

Si noti che è non può semplicemente scrivere:

class B(A): 
    def b(self): 
     a() 

Python sarà mai risolvere un nome semplice in un metodo/staticmethod; per Python a()deve essere essere una chiamata di funzione, locale o globale. È necessario fare riferimento all'istanza utilizzando self.a o la classe utilizzando B.a.

In python il self è esplicito come è il riferimento di classe corrente. Non confondere con l'implicito this di Java.

+0

Ah, abbastanza giusto, ha senso. –

+0

Suvati: D durante il debug del motivo dietro a questo fenomeno, non sono riuscito a vedere che il 'super()' non era necessario –