2010-06-19 8 views
11

Czytałem dokumentację opisującą dziedziczenie klas, abstrakcyjne klasy bazowe, a nawet interfejsy Pythona. Ale żadne szwy nie są dokładnie tym, czego chcę. Mianowicie prosty sposób budowania wirtualnych klas. Kiedy wywoływana jest klasa wirtualna, chciałbym utworzyć instancję klasy bardziej specyficznej w oparciu o podane parametry i przekazać funkcję wywołującą. Na razie mam krótki sposób przekierowania wywołań do klasy wirtualnej do klasy bazowej.Wirtualne klasy: robienie tego dobrze?

Idea jest następująca:

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) 

Moja przekierowanie jest daleka od optymalnej, jak nazywa się tylko do number_of_edges() funkcja przejdzie dalej. Dodanie czegoś takiego do Shape również nie daje rady:

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

Co robię źle? Czy cały pomysł został źle wdrożony? Każda pomoc bardzo doceniona.

+0

'__getattr__' działa tylko dla klas w nowym stylu. Oznacza to, że twoje klasy muszą być podklasami obiektu. –

+0

To, co próbujesz zrobić, jest również określane jako klasa mająca wirtualny konstruktor (a nie "wirtualne klasy").Zobacz powiązane pytanie [Czym dokładnie jest fabryka klas?] (Http://stackoverflow.com/questions/2526879/what-exactly-is-a-class-factory) – martineau

Odpowiedz

14

wolałbym robić to z fabryki:

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) 

czyli

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] 

i dziedziczyć klasy z Shape

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

Twoja druga fabryka konstruuje obiekt każdego typu co czas jest wywoływany, nawet jeśli zwrócisz tylko jeden z nich. Lepiej przechowywać klasy w słowniku niż obiekty: 'class_dict = {" It's flat ": Line, ...}' then 'return class_dict [description] (description)'. –

0

Można zmienić klasę z object.__class__, ale znacznie lepiej jest po prostu utworzyć funkcję, która zwraca instancję dowolnej klasy.

Na innym uwaga, wszystkie klasy powinny dziedziczyć object chyba użyć użyciu Python 3, jak to, w przeciwnym razie możesz skończyć z klasy starym stylu:

class A(object): 
    pass 
1

Python nie ma klas wirtualnych out w polu. Będziesz musiał je zaimplementować samodzielnie (powinno być to możliwe, możliwości odbicia Pythona powinny być wystarczająco mocne, abyś mógł to zrobić).

Jeśli jednak potrzebujesz wirtualnych zajęć, dlaczego nie użyjesz języka programowania, który ma wirtualne klasy, takie jak Beta, gBeta lub Newspeak? (BTW: czy są jakieś inne?)

W tym konkretnym przypadku nie widzę jednak, w jaki sposób wirtualne klasy uprościłyby twoje rozwiązanie, przynajmniej nie w podanym przykładzie. Może mógłbyś wyjaśnić, dlaczego uważasz, że potrzebujesz wirtualnych zajęć?

Nie zrozum mnie źle: lubię wirtualne lekcje, ale fakt, że tylko trzy języki kiedykolwiek je wdrożyły, tylko jedna z tych trzech wciąż żyje, a dokładnie 0 z tych trzech jest faktycznie używanych przez kogokolwiek & hellip;

+1

* BTW: czy są jeszcze jakieś inne? *: C++ przychodzi na myśl. – user1717828

+0

Interesujące! Nie wiedziałem o tym. Czy masz link, który mogę przeczytać na wirtualnych lekcjach w C++? Według Wikipedii w C++ klasy zagnieżdżone są statycznymi członkami klasy otaczającej. Innymi słowy: nie są one faktycznie * prawdziwymi * klasami zagnieżdżonymi (które są członkami otaczającej * instancji * z otaczającej klasy). Biorąc pod uwagę, że klasy wirtualne są szczególnym przypadkiem prawdziwych klas zagnieżdżonych (a mianowicie prawdziwych klas zagnieżdżonych, które są nadpisywane w podklasie, podobnie jak każdy inny element wirtualny), wydaje się to wykluczać C++ z posiadania klas wirtualnych. –

+0

Mmm, AFAIK C++ rodzaj * określa * czym są wirtualne klasy, ponieważ język jest tak popularny i sprawia, że ​​tak intensywnie się z nich korzysta. Klasa [wirtualna/abstrakcyjna] (https://en.m.wikipedia.org/wiki/Virtual_method_table#Example) nie może być utworzona w postaci instancji, tylko dziedziczona, więc w pewnym sensie jest bardziej jak szablon, z którego mogą korzystać inne klasy. – user1717828

18

Zgadzam się z TooAngel, ale użyłbym __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 - Też myślałem o czymś takim, ale nie znałem składni python dla tego – TooAngel

+0

W tym przykładzie, Kształt jest znany jako _metaclass_. –

+2

@Daniel: Nie sądzę. Metaclasses zazwyczaj zmieniają sposób działania klasy. W Objective-C nazywa się to * Class Cluster *. Nie jestem pewien, jaka jest właściwa nazwa tego w Pythonie. –

Powiązane problemy