防止类在 Python 中直接实例化

问题描述 投票:0回答:5

我有一个超类,它的方法调用仅在其子类中定义的其他方法。这就是为什么当我创建超类的实例并调用其方法时,它找不到该方法并引发错误。

这是一个例子:

class SuperClass(object):

  def method_one(self):
    value = self.subclass_method()
    print value


class SubClassOne(SuperClass):

  def subclass_method(self):
    return 'subclass 1'


class SubClassTwo(SuperClass):

  def subclass_method(self):
    return 'nubclass 2'


s1 = SubClassOne()
s1.method_one()

s2 = SubClassTwo()
s2.method_one()

c = SuperClass()
c.method_one()

# Results:
# subclass 1
# nubclass 2
# Traceback (most recent call last):
#   File "abst.py", line 28, in <module>
#     c.method_one()
#   File "abst.py", line 4, in method_one
#     value = self.subclass_method()
# AttributeError: 'SuperClass' object has no attribute 'subclass_method'

我正在考虑在创建新实例时更改超类的

__init__
并验证对象的类型。如果该对象属于超类,则会引发错误。然而,我不太确定这是否是 Pythonic 的做法。

有什么推荐吗?

python class oop locking superclass
5个回答
91
投票

我会重写基类中的

__new__()
,如果它是基类,则根本无法实例化。

class BaseClass:    # Py3

    def __new__(cls, *args, **kwargs):
        if cls is BaseClass:
            raise TypeError(f"only children of '{cls.__name__}' may be instantiated")
        return object.__new__(cls, *args, **kwargs)

这比将其放在

__init__()
中更好地分离关注点,并且“快速失败。”


29
投票

您的方法是典型的框架模式

使用 __init__ 来验证

type(self) is not SuperClass
是确保超类没有被直接实例化的合理方法。

另一种常见方法是提供调用时

raise NotImplementedError
的存根方法。这更可靠,因为它还验证子类已覆盖预期的方法。


17
投票

这就是我可能会做的:

class SuperClass(object):
    def __init__(self):
        if type(self) == SuperClass:
            raise Exception("<SuperClass> must be subclassed.")
        # assert(type(self) == SuperClass)

class SubClass(SuperClass):
    def __init__(self):
        SuperClass.__init__(self)

subC = SubClassOne()
supC = SuperClass() # This line should throw an exception

运行时(抛出异常!):

[ 18:32 jon@hozbox ~/so/python ]$ ./preventing-direct-instantiation.py
Traceback (most recent call last):
  File "./preventing-direct-instantiation.py", line 15, in <module>
    supC = SuperClass()
  File "./preventing-direct-instantiation.py", line 7, in __init__
    raise Exception("<SuperClass> must be subclassed.")
Exception: <SuperClass> must be subclassed.

编辑(来自评论):

[ 20:13 jon@hozbox ~/SO/python ]$ cat preventing-direct-instantiation.py 
#!/usr/bin/python

class SuperClass(object):
    def __init__(self):
        if type(self) == SuperClass:
            raise Exception("<SuperClass> must be subclassed.")

class SubClassOne(SuperClass):
    def __init__(self):
        SuperClass.__init__(self)

class SubSubClass(SubClassOne):
    def __init__(self):
        SubClassOne.__init__(self)

class SubClassTwo(SubClassOne, SuperClass):
    def __init__(self):
        SubClassOne.__init__(self)
        SuperClass.__init__(self)

subC = SubClassOne()

try:
    supC = SuperClass()
except Exception, e:
    print "FAILED: supC = SuperClass() - %s" % e
else:
    print "SUCCESS: supC = SuperClass()"

try:
    subSubC = SubSubClass()
except Exception, e:
    print "FAILED: subSubC = SubSubClass() - %s" % e
else:
    print "SUCCESS: subSubC = SubSubClass()"

try:
    subC2 = SubClassTwo()
except Exception, e:
    print "FAILED: subC2 = SubClassTwo() - %s" % e
else:
    print "SUCCESS: subC2 = SubClassTwo()"

打印:

[ 20:12 jon@hozbox ~/SO/python ]$ ./preventing-direct-instantiation.py 
FAILED: supC = SuperClass() - <SuperClass> must be subclassed.
SUCCESS: subSubC = SubSubClass()
SUCCESS: subC2 = SubClassTwo()

9
投票

您正在谈论抽象基类,而 Python 语言本身并不支持它们。

但是,在标准库中,有一个模块可以帮助您完成任务。查看 abc 文档。


0
投票

当前所有答案均来自 2011 年。我不确定这到底是什么时候引入的,但如果你使用

@abstractmethod
装饰器,现在绝对是可能的。这假设你的 ABC 有一个必须由子类实现的方法(但如果没有,我不明白将其抽象化的意义)

from abc import ABC, abstractmethod


class SuperClass(ABC):

  def method_one(self):
    value = self.subclass_method()
    print(value)

  @abstractmethod
  def subclass_method(self):
    raise NotImplementedError


class SubClassOne(SuperClass):

  def subclass_method(self):
    return 'subclass 1'


# Works
s1 = SubClassOne()
s1.method_one()

# TypeError prevents super class instantiation as desired
# "Can't instantiate abstract class SuperClass with abstract methods subclass_method"
c = SuperClass()
© www.soinside.com 2019 - 2024. All rights reserved.