Виртуальные классы: делать это правильно?

Я читал документацию, описывающую наследование классов, абстрактные базовые классы и даже интерфейсы python. Но ничто не швы, чтобы быть именно тем, чего я хочу. А именно, простой способ создания виртуальных классов. Когда виртуальный класс вызывается, я хотел бы, чтобы он создавал экземпляр некоторого более определенного класса, основываясь на том, какие параметры он задает, и передайте обратно вызывающую функцию. На данный момент у меня есть сводный способ перенаправления вызовов на виртуальный класс до базового класса.

Идея такова:

class Shape:
    def __init__(self, description):
        if   description == "It flat":  self.underlying_class = Line(description)
        elif description == "It spiky": self.underlying_class = Triangle(description)
        elif description == "It 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 big")
shape_dont_know_what_it_is.number_of_edges(parameters)

Мое перенаправление далеко не оптимальное, поскольку передаются только вызовы функции number_of_edges(). Добавление чего-то вроде этого в Shape не является швом, чтобы сделать трюк:

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

Что я делаю неправильно? Вся идея плохо реализована? Любая помощь была высоко оценена.

Ответ 1

Я бы предпочел сделать это с помощью factory:

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

или

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

и наследуйте классы от формы

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

Ответ 2

Я согласен с TooAngel, но я бы использовал метод __ new__.

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 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 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.

Ответ 3

У Python нет виртуальных классов из коробки. Вам придется реализовать их самостоятельно (возможно, возможности Python-отражения должны быть достаточно мощными, чтобы вы могли это сделать).

Однако, если вам нужны виртуальные классы, то почему бы вам просто не использовать язык программирования, который имеет виртуальные классы, такие как Beta, gBeta или Newspeak? (Кстати: есть ли другие?)

В этом конкретном случае, однако, я не вижу, как виртуальные классы упростят ваше решение, по крайней мере, не в примере, который вы указали. Может быть, вы могли бы объяснить, почему вы считаете, что вам нужны виртуальные классы?

Не поймите меня неправильно: мне нравятся виртуальные классы, но факт, что только три языка когда-либо реализовывали их, только один из этих трех до сих пор жив, и ровно 0 из этих трех фактически используются кем-то, что-то говорит и hellip;

Ответ 4

Вы можете изменить класс с помощью object.__class__, но гораздо лучше просто создать функцию, возвращающую экземпляр произвольного класса.

В другой заметке весь класс должен наследовать от object, если вы не используете Python 3, например, в противном случае вы получите класс старого стиля:

class A(object):
    pass