0

我不确定这样做的正确方法是什么,但我有以下代码:

class ToolKit(object):
    def printname(self):
        print self.name


class Test(ToolKit):
    def __init__(self):
        self.name = "Test"


b = Test()
b.printname()

我的目标是拥有某种抽象基类,我可以将其用作其他类的工具包。这个工具包类不应该是可实例化的。它应该具有抽象方法,但其他方法应该被继承,并且不应该在子类中实现,因为它们将在子类之间共享。以下代码正在运行。但是,我正在使用 Pycharm 并且“self.name”导致了这个警告:

类“工具包”的未解析属性引用“名称”

我想知道这样做的正确方法是什么。我已经研究了 ABC 元类,但由于两个原因无法使其按我的意图工作。首先,如果所有方法都不是抽象方法,则可以实例化一个抽象类;我只是想确保它根本不能被实例化。其次,我想要一些将用作默认值的方法(不需要像 printname 那样被覆盖),我似乎无法弄清楚如何实现这一点。

提前致谢!

编辑:

当我的意思是它有效时,我的意思是它正确打印“测试”。

4

1 回答 1

2

如果你想阻止你的“基”类被实例化而其他类可以实例化它并且你不想使用元类,你可以简单地在实例创建级别阻止它,例如:

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由于缺少属性而引发错误。正如我所说,只需将您代码的用户视为成年人,并要求他们做您希望他们做的事情,以使您的代码正常运行。它的麻烦要少得多,并且您可以节省大量时间来改进代码的实际有用部分,而不是发明方法来防止用户伤害自己。

于 2017-06-17T02:08:12.097 回答