2010-06-19 10 views
11

He estado leyendo la documentación que describe la herencia de clases, las clases base abstractas e incluso las interfaces de Python. Pero nada parece ser exactamente lo que quiero. A saber, una forma simple de construir clases virtuales. Cuando se llama a la clase virtual, me gustaría que creara una instancia de una clase más específica en función de los parámetros que se le asignan y que devuelva la función de llamada. Por ahora tengo una forma resumida de redirigir las llamadas a la clase virtual a la clase subyacente.Clases virtuales: hacerlo bien?

La idea es la siguiente:

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) 

Mi cambio de ruta está lejos de ser óptima, como llamadas sólo a los number_of_edges() la función sea traspasada. Agregando algo así como a la forma no costura para hacer el truco, ya sea:

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

Lo que estoy haciendo mal? ¿La idea está mal implementada? Cualquier ayuda muy apreciada.

+0

'__getattr__' sólo funciona para las clases de nuevo estilo. Esto significa que tus clases tienen que ser subclases de 'objeto'. –

+0

Lo que estás tratando de hacer también se conoce como una clase que tiene un constructor virtual (en lugar de "clases virtuales").Consulte la pregunta relacionada [_¿Qué es exactamente una Fábrica de clases? _] (Http://stackoverflow.com/questions/2526879/what-excaly-is-a-class-factory) – martineau

Respuesta

14

yo preferiría hacerlo con una fábrica:

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] 

y heredar las clases de Forma

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

Su segunda función de fábrica construye un objeto de todo tipo cada Hora en que se llama, aunque solo devuelva uno de ellos. Es mejor almacenar clases en el diccionario que objetos: 'class_dict = {" It is flat ": Line, ...}' then 'return class_dict [description] (description)'. –

0

Puede cambiar la clase con object.__class__, pero es mucho mejor hacer una función que devuelva una instancia de una clase arbitraria.

Por otro lado, toda clase debe heredar de object a menos que use el uso de Python 3, de esta manera, de lo contrario se termina con una clase de estilo antiguo:

class A(object): 
    pass 
1

Python no tiene clases virtuales a cabo De la caja. Tendrás que implementarlos tú mismo (debería ser posible, las capacidades de reflexión de Python deberían ser lo suficientemente poderosas como para permitirte hacer esto).

Sin embargo, si necesita clases virtuales, ¿por qué no utiliza un lenguaje de programación que tiene clases virtuales como Beta, gBeta o Newspeak? (Por cierto: ¿hay otros?)

En este caso particular, sin embargo, realmente no veo cómo las clases virtuales simplificarían su solución, al menos no en el ejemplo que usted ha dado. ¿Tal vez podrías explicar por qué crees que necesitas clases virtuales?

No me malinterpreten: me gustan las clases virtuales, pero el hecho de que solo tres idiomas alguna vez las hayan implementado, solo una de esas tres sigue viva y exactamente 0 de esas tres son utilizadas por alguien es algo revelador & hellip;

+1

* BTW: ¿hay algún otro? *: C++ viene a la mente. – user1717828

+0

¡Interesante! No lo sabía. ¿Tienes un enlace donde puedo leer sobre clases virtuales en C++? Según Wikipedia, en C++, las clases anidadas son miembros estáticos de la clase adjunta. En otras palabras: no son en realidad * clases anidadas * verdaderas (que son miembros de una * instancia * envolvente de la clase adjunta). Y teniendo en cuenta que las clases virtuales son un caso especial de clases anidadas verdaderas (es decir, verdaderas clases anidadas que se anulan en una subclase al igual que cualquier otro miembro virtual), esto parece impedir que C++ tenga clases virtuales. –

+0

Mmm, AFAIK C++ tipo * define * qué clases virtuales son, porque el lenguaje es tan popular y hace un uso tan intenso de ellas. Una [clase virtual/abstracta] (https://en.m.wikipedia.org/wiki/Virtual_method_table#Example) no se puede crear una instancia, solo se hereda, por lo que, en cierto modo, se parece más a una plantilla para que otras clases se puedan alimentar. – user1717828

18

Estoy de acuerdo con TooAngel, pero usaría __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 - También pensé en algo así, pero no sabía la sintaxis de python para él – TooAngel

+0

En este ejemplo, Shape se conoce como _metaclass_. –

+2

@Daniel: No lo creo. Las metaclases suelen cambiar la forma en que funciona una clase. En Objective-C esto se llamaría * Class Cluster *. No estoy seguro de cuál es el nombre correcto para Python. –

Cuestiones relacionadas