如何正确地实现虚拟类?

在 Python 中,所谓的虚拟类通常是指抽象基类(Abstract Base Class,简称 ABC)。抽象基类不可实例化,其主要作用是定义一组抽象方法,子类必须实现这些抽象方法才能被实例化。

要正确实现虚拟类(抽象基类),可以按照我下文写的步骤来。

在这里插入图片描述

1、问题背景

在类继承、抽象基类甚至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)

2、解决方案

有一些方法可以解决这个问题:

方法1:使用__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'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:使用工厂函数

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
  • DogCatAnimal 的具体子类,它们必须实现 make_sound 方法才能被实例化。
  • 如果某个子类没有实现抽象基类中的所有抽象方法,尝试实例化该子类会引发 TypeError

注意事项:

  • 抽象基类本身不能被实例化,只能用作其他类的基类。
  • 子类必须实现抽象基类中的所有抽象方法,否则会导致运行时错误。
  • 抽象基类可以包含具体方法,这些方法可以被子类继承或重写。
  • 使用抽象基类能够帮助你设计更加规范和易于维护的类层次结构,强制执行接口和方法的一致性。

通过合理使用抽象基类,可以提高代码的可读性和可维护性,同时在设计接口时提供更好的约束和规范。

  • 6
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值