【问题标题】:Virtual classes: doing it right?虚拟课程:做对了吗?
【发布时间】:2011-03-05 19:52:27
【问题描述】:

我一直在阅读描述类继承、抽象基类甚至 python 接口的文档。但没有什么接缝正是我想要的。即,一种构建虚拟类的简单方法。当调用虚拟类时,我希望它根据给定的参数实例化一些更具体的类,并将其交还给调用函数。现在,我有一个总结方法,可以将对虚拟类的调用重新路由到底层类。

思路如下:

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)

我的重新路由远非最佳,因为只有对 number_of_edges() 函数的调用会被传递。在 Shape 中添加这样的东西也不能解决问题:

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

我做错了什么?整个想法实施得不好?非常感谢任何帮助。

【问题讨论】:

  • __getattr__ 仅适用于新型类。这意味着您的类必须是 object 的子类。
  • 您尝试做的也被称为具有虚拟构造函数的类(而不是“虚拟类”)。查看相关问题What exactly is a Class Factory?

标签: python class inheritance virtual abstract


【解决方案1】:

我同意TooAngel,但我会使用__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.

【讨论】:

  • 不错 - 我也想过类似的事情,但不知道它的 python 语法
  • @Daniel:我不这么认为。元类通常会改变类的工作方式。在 Objective-C 中,这将被称为 Class Cluster。我不确定它在 Python 中的正确名称是什么。
  • 我实现了这个解决方案,发现它有一个缺陷:创建实例时会调用Shape类的init方法两次。
  • 我可以确认此解决方案不合适。要修复它,请参阅stackoverflow.com/questions/5953759
【解决方案2】:

我更喜欢在工厂里做:

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)

或:

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]

并从 Shape 继承类

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

【讨论】:

  • 您的第二个工厂函数在每次调用时都会构造一个每种类型的对象,即使您只返回其中一个。将类存储在字典中比对象更好:class_dict = {"It's flat": Line, ...} 然后return class_dict[description](description)
【解决方案3】:

Python 没有开箱即用的虚拟类。您必须自己实现它们(应该可以,Python 的反射功能应该足够强大,可以让您执行此操作)。

但是,如果您需要虚拟课程,那么为什么不直接使用具有虚拟课程的编程语言,例如 Beta、gBeta 或 Newspeak? (顺便说一句:还有其他人吗?)

但是,在这种特殊情况下,我并没有真正看到虚拟类如何简化您的解决方案,至少在您给出的示例中没有。也许您可以详细说明为什么您认为需要虚拟课程?

不要误会我的意思:我喜欢虚拟课程,但事实上只有三种语言实现了它们,这三种语言中只有一种仍然存在,而这三种语言中恰好有 0 种被任何人实际使用,这有点说明......

【讨论】:

  • 有趣!我不知道。你有一个链接可以让我阅读 C++ 中的虚拟类吗?根据维基百科,在 C++ 中,嵌套类是封闭类的静态成员。换句话说:它们实际上不是 true 嵌套类(它们是封闭类的封闭 instance 的成员)。考虑到虚拟类是真正嵌套类的特例(即真正的嵌套类,它像任何其他虚拟成员一样在子类中被覆盖),这似乎阻止了 C++ 拥有虚拟类。
  • 嗯,AFAIK C++ 有点定义什么是虚拟类,因为这种语言非常流行并且大量使用它们。 virtual/abstract class 不能被实例化,只能被继承,所以在某种程度上它更像是其他类的模板。
  • 我不确定你的意思。 virtual class 一词定义明确,具有与 C++ 或任何其他特定语言无关的精确含义。您所描述的与虚拟课程完全无关。您描述的是一个抽象基类。 Google 对 C++ 中的虚拟类的点击率正好为 0。 C++中有虚拟base类,它们与虚拟继承相关,但没有一个与虚拟相关。就像我说的:虚拟类是一个类,它是实例的成员,可以在子类中被覆盖,就像虚拟方法一样......
  • ... 是实例的成员,可以在子类中被覆盖。事实上,在像 Python 这样的语言中,类是一等对象,您可以使用返回类的方法来实现虚拟类。当你说 C++ 有虚拟类时,我相信你,我对 C++ 的了解还不够,无法判断,但是你的例子和你的链接都没有显示 C++ 中的虚拟类,谷歌没有关于 C++ 中的虚拟类的点击,而且维基百科关于 C++ 的文章说嵌套类是静态成员,这与嵌套类是非静态成员的虚拟类冲突。
  • 看来你是对的;我从来不知道我混淆了这两个概念。谢谢你的收获。
【解决方案4】:

您可以使用object.__class__ 更改类,但最好只创建一个返回任意类实例的函数。

另一方面,所有类都应该从object 继承,除非你使用 Python 3,像这样,否则你最终会得到一个老式的类:

class A(object):
    pass

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-10-09
    • 2015-11-05
    • 2015-06-05
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多