如果你想阻止你的“基”类被实例化而其他类可以实例化它并且你不想使用元类,你可以简单地在实例创建级别阻止它,例如:
class ToolKit(object):
def __new__(cls, *args, **kwargs):
assert cls is not ToolKit, "You cannot instantiate the base `ToolKit` class"
return super(ToolKit, cls).__new__(cls)
def printname(self):
print(self.name)
class Test(ToolKit):
def __init__(self):
self.name = "Test"
现在,如果您尝试像这样使用它:
b = Test()
b.printname()
一切都会好起来的,它会打印出来Test
,但是如果你尝试实例化 ToolKit 类,你会得到一个不同的故事:
a = ToolKit()
# AssertionError: You cannot instantiate the base `ToolKit` class
您可以通过强制实现/覆盖方法来做类似的事情,但它很快就会变得难以处理,因此您最好从abc.ABCMeta
一开始就使用它。
PS你可能想重新考虑实现这样的模式。与其竭力阻止用户以无法保证其操作/正确性的方式使用您的代码,不如将他们视为成年人,并在文档中清楚地写下您的意图。这样一来,如果他们决定以不应该的方式使用您的代码,那将是他们的错,您将在此过程中节省大量时间。
更新- 如果您想强制执行属性的子类定义,则有一个特殊的@abc.abstractproperty
描述符 - 这并不理想,因为它不会强制子类设置属性而是覆盖属性 getter/setter,但您不能在周围有描述符多变的。
您至少可以通过以下方式强制执行类级变量(如在简单属性中,没有定义的访问器):
class ToolKit(object):
__REQUIRED = ["id", "name"]
def __new__(cls, *args, **kwargs):
assert cls is not ToolKit, "You cannot instantiate the base `ToolKit` class"
for req in ToolKit.__REQUIRED:
assert hasattr(cls, req), "Missing a required property: `{}`".format(req)
return super(ToolKit, cls).__new__(cls)
def printname(self):
print("{} is alive!".format(self.name))
class Test1(ToolKit):
id = 1
name = "Test1"
class Test2(ToolKit):
name = "Test2"
class Test3(ToolKit):
id = 3
现在,如果您测试它们中的每一个的实例化:
for typ in [ToolKit, Test1, Test2, Test3]:
print("Attempting to instantiate `{}`...".format(typ.__name__))
try:
inst = typ()
inst.printname()
except AssertionError as e:
print("Instantiation failed: {}".format(e))
你会回来:
正在尝试实例化 `ToolKit`...
实例化失败:您无法实例化基础“工具包”类
正在尝试实例化“Test1”...
Test1 还活着!
正在尝试实例化“Test2”...
实例化失败:缺少必需的属性:`id`
正在尝试实例化“Test3”...
实例化失败:缺少必需的属性:`name`
但是,Python 是一种动态语言,因此即使实例化级别的检查通过,用户也可以在之后删除该属性,这将导致printname
由于缺少属性而引发错误。正如我所说,只需将您代码的用户视为成年人,并要求他们做您希望他们做的事情,以使您的代码正常运行。它的麻烦要少得多,并且您可以节省大量时间来改进代码的实际有用部分,而不是发明方法来防止用户伤害自己。