2010-06-19 3 views
11

Ho letto la documentazione che descrive l'ereditarietà della classe, le classi base astratte e persino le interfacce python. Ma niente sembra essere esattamente quello che voglio. Vale a dire, un modo semplice per costruire classi virtuali. Quando viene chiamata la classe virtuale, mi piacerebbe istanziare una classe più specifica in base a quali parametri sono assegnati e restituire la funzione di chiamata. Per ora ho un modo sommario per reindirizzare le chiamate alla classe virtuale fino alla classe sottostante.Classi virtuali: farlo bene?

L'idea è la seguente:

class Shape: 
    def __init__(self, description): 
     if description == "It's flat": self.underlying_class = Line(description) 
     elif description == "It's spiky": self.underlying_class = Triangle(description) 
     elif description == "It's big": self.underlying_class = Rectangle(description) 
    def number_of_edges(self, parameters): 
     return self.underlying_class(parameters) 

class Line: 
    def __init__(self, description): 
     self.desc = description 
    def number_of_edges(self, parameters): 
     return 1 

class Triangle: 
    def __init__(self, description): 
     self.desc = description 
    def number_of_edges(self, parameters): 
     return 3 

class Rectangle: 
    def __init__(self, description): 
     self.desc = description 
    def number_of_edges(self, parameters): 
     return 4 

shape_dont_know_what_it_is = Shape("It's big") 
shape_dont_know_what_it_is.number_of_edges(parameters) 

mio dirottamento è lungi dall'essere ottimale, come le chiamate solo ai number_of_edges() funzione viene passato su. L'aggiunta di qualcosa di simile per forma non cucitura a fare il trucco o:

def __getattr__(self, *args): 
    return underlying_class.__getattr__(*args) 

Quello che sto facendo male? L'intera idea è mal implementata? Qualsiasi aiuto molto apprezzato.

+0

'__getattr__' funziona solo per classi di nuovo stile. Questo significa che le tue classi devono essere sottoclassi di 'object'. –

+0

Quello che stai cercando di fare è anche definito come una classe con costruttore virtuale (piuttosto che "classi virtuali").Vedi domanda correlata [_Che cos'è esattamente una fabbrica di classe? _] (Http://stackoverflow.com/questions/2526879/what-exactly-is-a-class-factory) – martineau

risposta

14

io preferirei farlo con una fabbrica:

def factory(description): 
    if description == "It's flat": return Line(description) 
    elif description == "It's spiky": return Triangle(description) 
    elif description == "It's big": return Rectangle(description) 

o:

def factory(description): 
    classDict = {"It's flat":Line("It's flat"), "It's spiky":Triangle("It's spiky"), "It's big":Rectangle("It's big")} 
    return classDict[description] 

ed ereditare le classi da Shape

class Line(Shape): 
    def __init__(self, description): 
     self.desc = description 
    def number_of_edges(self, parameters): 
     return 1 
+0

La tua seconda funzione di fabbrica costruisce un oggetto di ogni tipo ogni ora si chiama, anche se si restituisce solo uno di essi. Meglio memorizzare classi nel dizionario che oggetti: 'class_dict = {" E 'piatto ": Line, ...}' then 'return class_dict [description] (description)'. –

0

È possibile cambiare la classe con object.__class__, ma è molto meglio semplicemente creare una funzione che restituisca un'istanza di una classe arbitraria.

In un'altra nota, tutti classe dovrebbe ereditare da object a meno che non si utilizza utilizzando Python 3, in questo modo, altrimenti si finisce con una classe di vecchio stile:

class A(object): 
    pass 
1

Python non ha classi virtuali fuori della scatola. Dovrai implementarli tu stesso (dovrebbe essere possibile, le capacità di riflessione di Python dovrebbero essere abbastanza potenti da permetterti di farlo).

Tuttavia, se hai bisogno di lezioni virtuali, perché non usi semplicemente un linguaggio di programmazione che ha classi virtuali come Beta, gBeta o NewsPeak? (BTW: ce ne sono altri?)

In questo caso particolare, tuttavia, non vedo come le classi virtuali possano semplificare la soluzione, almeno non nell'esempio che hai fornito. Forse potresti elaborare il motivo per cui pensi di aver bisogno di lezioni virtuali?

Non fraintendetemi: mi piacciono le lezioni virtuali, ma il fatto che solo tre lingue le abbiano mai implementate, solo una di queste tre è ancora viva e esattamente 0 di queste tre sono effettivamente utilizzate da qualcuno & hellip;

+1

* BTW: ce ne sono altri? *: Mi viene in mente il C++. – user1717828

+0

Interessante! Non lo sapevo. Hai un link dove posso leggere su classi virtuali in C++? Secondo Wikipedia, in C++, le classi nidificate sono membri statici della classe che li include. In altre parole: non sono in realtà * vere * classi nidificate (che sono membri di un'istanza * che racchiude * della classe che li include). E considerando che le classi virtuali sono un caso speciale di vere classi nidificate (vale a dire vere classi nidificate che vengono sovrascritte in una sottoclasse come qualsiasi altro membro virtuale), questo sembra precludere al C++ di avere classi virtuali. –

+0

Mmm, AFAIK C++ * definisce * quali sono le classi virtuali, perché la lingua è così popolare e ne fa un uso così pesante. Una [classe virtuale/astratta] (https://en.m.wikipedia.org/wiki/Virtual_method_table#Example) non può essere istanziata, solo ereditata, quindi in un certo senso è più simile a un modello di cui altre classi possono nutrirsi. – user1717828

18

Sono d'accordo con TooAngel, ma vorrei usare lo __new__ method.

class Shape(object): 
    def __new__(cls, *args, **kwargs): 
     if cls is Shape:       # <-- required because Line's 
      description, args = args[0], args[1:] #  __new__ method is the 
      if description == "It's flat":   #  same as Shape's 
       new_cls = Line 
      else: 
       raise ValueError("Invalid description: {}.".format(description)) 
     else: 
      new_cls = cls 
     return super(Shape, cls).__new__(new_cls, *args, **kwargs) 

    def number_of_edges(self): 
     return "A shape can have many edges…" 

class Line(Shape): 
    def number_of_edges(self): 
     return 1 

class SomeShape(Shape): 
    pass 

>>> l1 = Shape("It's flat") 
>>> l1.number_of_edges() 
1 
>>> l2 = Line() 
>>> l2.number_of_edges() 
1 
>>> u = SomeShape() 
>>> u.number_of_edges() 
'A shape can have many edges…' 
>>> s = Shape("Hexagon") 
ValueError: Invalid description: Hexagon. 
+1

nice - Ho pensato anche a qualcosa del genere, ma non conoscevo la sintassi python per esso – TooAngel

+0

In questo esempio, Shape è conosciuto come _metaclass_. –

+2

@Daniel: io non la penso così. I metaclasses di solito cambiano il modo in cui una classe funziona. In Objective-C questo sarebbe chiamato * Class Cluster *. Non sono sicuro di quale sia il nome corretto in Python. –