使用 python class 作为工具包?

Use a python class as a toolkit?

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

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


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


b = Test()
b.printname()

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

Unresolved attribute reference 'name' for class 'Toolkit'

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

提前致谢!

编辑:

当我说它有效时,我的意思是它可以正确打印 "Test"。

如果你想阻止你的 'base' class 被实例化而其他 classes 可以实例化它 你不想使用 metaclasses,你可以简单地在实例创建级别阻止它,如:

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 class,您将得到一个不同的故事:

a = ToolKit()
# AssertionError: You cannot instantiate the base `ToolKit` class

你可以通过强制 method 为 implemented/overriden 来做类似的事情,但它很快就会变得难以处理,所以你最好从一开始就使用 abc.ABCMeta

P.S。无论如何,您可能想要重新考虑实施此类模式。与其竭力阻止用户以无法保证其 operation/correctness 的方式使用您的代码,不如将他们视为成年人,并在文档中清楚地写下您的意图。这样一来,如果他们决定以非预期的方式使用您的代码,那将是他们的错,您将在此过程中节省大量时间。

UPDATE - 如果你想强制执行属性的 subclass 定义,有一个专门用于此的特殊 @abc.abstractproperty 描述符 - 它并不理想它不是强制 subclasses 设置 属性 而是覆盖 属性 getter/setter,但是你不能在变量周围有描述符。

您至少可以强制执行 class 级变量(如在简单属性中,没有定义访问器),例如:

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))

你会回来的:

Attempting to instantiate `ToolKit`...
Instantiation failed: You cannot instantiate the base `ToolKit` class
Attempting to instantiate `Test1`...
Test1 is alive!
Attempting to instantiate `Test2`...
Instantiation failed: Missing a required property: `id`
Attempting to instantiate `Test3`...
Instantiation failed: Missing a required property: `name`

然而,Python是一种动态语言,因此即使实例化级别检查通过,用户可以在之后删除属性,这将导致printname引发错误由于缺少 属性。正如我所说,只需将代码的用户视为成年人,并要求他们做您希望他们做的事情,以使您的代码正常运行。它的麻烦要少得多,而且您可以节省大量时间来改进代码的实际有用部分,而不是发明方法来防止您的用户伤害自己。