Python 2.5 中的抽象类

1 投票
6 回答
676 浏览
提问于 2025-04-16 20:39

我现在正在重构一个类,这个类用来定义客户端或服务器。这个类里面有很多代码。

if client:
    XXXXXXXXXXXX
elif server:
    YYYYYYYYYYYY

所以我决定创建一个类A,把相似的代码放进去,然后再创建一个类C用于客户端,另一个类S用于服务器,这两个类都继承自A。(当然它们并不是这个名字^^)

所以类A有点像一个抽象类。但是问题是,Python 2.5没有抽象类,这个功能是在2.6版本才有的。所以我在想有没有办法禁止直接创建类A的实例。

一个解决方案是,在类A的构造函数里抛出一个NotImplemented错误,但C和S的构造函数代码是一样的,所以我把这个放在了“抽象”类A里面(这样做是个坏主意吗?)。

这可能听起来有点傻,但我只是偶尔用Python开发,而且我还是个年轻的程序员。你们有什么建议吗?

6 个回答

1

我第一个问题是:为什么不能简单地避免从类 A 创建对象呢?我的意思是,这有点像关于实现单例模式的那些问题……正如这位回答者正确引用的:

在“四人帮”给我们带来学术化之前,“单例”(没有正式名称)只是一个简单的想法,值得用一行简单的代码来实现,而不是变成一种宗教。

我认为,抽象类也是如此(实际上,抽象类在Python中被引入是出于其他原因,而不是你想用它们的那个目的)。

话虽如此,你可以在类 A__init__ 方法中抛出一个异常。可以这样写:

>>> class A():
...     def __init__(self):
...             raise BaseException()
... 
>>> a = A()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 3, in __init__
BaseException
>>> class B(A):
...     def __init__(self):
...             pass
... 
>>> b = B()
>>>

当然,这只是一个粗略的想法:如果你在 A.__init__ 中有一些有用的东西,你应该检查一下 __class__ 属性等等……

2

这种方法的好处是,你不需要对子类做任何修改,就能让它变成非抽象的。

class ABC(object):
    abstract = True
    def __new__(cls, *args, **kwargs):
        if "abstract" in cls.__dict__ and cls.__dict__["abstract"] == True:
            raise RuntimeError(cls.__name__ + " is abstract!")
        return object.__new__(cls)

class Subclass(ABC):
    pass

print Subclass()
print ABC()

输出结果:

<__main__.Subclass object at 0xb7878a6c>
Traceback (most recent call last):
  File "abc.py", line 14, in <module>
    print ABC()
  File "abc.py", line 6, in __new__
    raise RuntimeError(cls.__name__ + " is abstract!")
RuntimeError: ABC is abstract!

如果你想创建一个抽象的子类,只需这样做:

class AbstractSubclass(ABC):
    abstract = True
2

在静态类型语言中,你需要使用一个抽象基类(ABC),因为你需要一个有明确大小和接口的对象来传递。否则,代码在尝试调用这个对象的方法时就无法编译通过。

而Python不是静态类型语言,调用代码根本不需要知道它正在调用的对象是什么类型。所以,你可以通过文档来“定义”你的抽象基类的接口要求,然后在两个没有关系的类中直接实现这个接口。

比如,

class Server:
    def do_thing(self):
        pass #do that server thing here

class Client:
    def do_thing(self):
        pass #do that client thing here

def do_thing(thingy):
    thingy.do_thing() # is it a Client? a Server? something else?

s=Server()
c=Client()

do_thing(s)
do_thing(c)

在这里,你可以传入任何有do_thing方法且参数与调用匹配的对象。

撰写回答