在 Python 中,所谓的虚拟类通常是指抽象基类(Abstract Base Class,简称 ABC)。抽象基类不可实例化,其主要作用是定义一组抽象方法,子类必须实现这些抽象方法才能被实例化。
要正确实现虚拟类(抽象基类),可以按照我下文写的步骤来。
1、问题背景
在类继承、抽象基类甚至python接口的文档中,没有一种方式能够完全满足需求。当调用虚拟类时,希望它能够根据给定参数实例化一些更具体的类,并将该类返回给调用函数。
在现有的实现中,通过一种汇总方式将对虚拟类的调用重定向到基础类。思路如下:
代码语言:javascript复制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类添加类似以下内容似乎也无法解决问题:
代码语言:javascript复制def __getattr__(self, *args):
return underlying_class.__getattr__(*args)
2、解决方案
有一些方法可以解决这个问题:
方法1:使用new方法
代码语言:javascript复制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.
方法2:使用工厂函数
代码语言:javascript复制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
对于方法3,Python中没有开箱即用的虚拟类,需要自行实现(这应该是可能的,Python的反射能力足以实现这一点)。然而,如果需要使用虚拟类,建议使用具有虚拟类的编程语言,如Beta、gBeta或Newspeak。不过,在具体的示例中,并不清楚虚拟类如何简化解决方案,或者为什么需要使用虚拟类。
在这几个例子中:
Animal
是一个抽象基类,定义了一个抽象方法make_sound
。Dog
和Cat
是Animal
的具体子类,它们必须实现make_sound
方法才能被实例化。- 如果某个子类没有实现抽象基类中的所有抽象方法,尝试实例化该子类会引发
TypeError
。
注意事项:
- 抽象基类本身不能被实例化,只能用作其他类的基类。
- 子类必须实现抽象基类中的所有抽象方法,否则会导致运行时错误。
- 抽象基类可以包含具体方法,这些方法可以被子类继承或重写。
- 使用抽象基类能够帮助你设计更加规范和易于维护的类层次结构,强制执行接口和方法的一致性。
通过合理使用抽象基类,可以提高代码的可读性和可维护性,同时在设计接口时提供更好的约束和规范。