使用python类作为工具箱?

使用python类作为工具箱?,python,inheritance,abstract-base-class,Python,Inheritance,Abstract Base Class,我不确定这样做的正确方法是什么,但我有以下代码: class ToolKit(object): def printname(self): print self.name class Test(ToolKit): def __init__(self): self.name = "Test" b = Test() b.printname() 我的目标是拥有某种抽象基类,可以用作其他类的工具包。此工具箱类不应是可实例化的。它应该有抽象方法,但其

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

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”会导致以下警告:

类“Toolkit”的未解析属性引用“name”

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

提前谢谢

编辑:


当我的意思是它工作时,我的意思是它正确地打印“Test”。

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

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
您可以通过强制实现/重写方法来做类似的事情,但它很快就会变得难以处理,所以您最好从一开始就使用它

另外,无论如何,您可能需要重新考虑这样的实现模式。您可以将他们视为成年人,在文档中清楚地写下您的意图,而不是千方百计阻止用户以无法保证其操作/正确性的方式使用代码。那样的话,如果他们决定以一种不应该的方式使用你的代码,那将是他们的错,你将在这个过程中节省大量的时间

更新-如果您想要强制执行属性的子类定义,有一个专门用于此的描述符-这并不理想,因为它不是强制子类设置属性,而是重写属性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))
你会回来的:

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` 正在尝试实例化“ToolKit”。。。 实例化失败:无法实例化基“ToolKit”类 正在尝试实例化“Test1”。。。 Test1还活着! 正在尝试实例化“Test2”。。。 实例化失败:缺少必需的属性:`id` 正在尝试实例化“Test3”。。。 实例化失败:缺少必需的属性:`name`
但是,Python是一种动态语言,因此即使实例化级别检查通过,用户也可以随后删除该属性,这将导致
printname
由于缺少属性而引发错误。正如我所说的,只要把代码的用户当作成年人来对待,让他们做你希望他们做的事情,这样代码才能正常运行。这大大减少了麻烦,而且可以节省大量时间,您可以致力于改进代码的实际有用部分,而不是发明方法来阻止用户伤害自己。

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

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
您可以通过强制实现/重写方法来做类似的事情,但它很快就会变得难以处理,所以您最好从一开始就使用它

另外,无论如何,您可能需要重新考虑这样的实现模式。您可以将他们视为成年人,在文档中清楚地写下您的意图,而不是千方百计阻止用户以无法保证其操作/正确性的方式使用代码。那样的话,如果他们决定以一种不应该的方式使用你的代码,那将是他们的错,你将在这个过程中节省大量的时间

更新-如果您想要强制执行属性的子类定义,有一个专门用于此的描述符-这并不理想,因为它不是强制子类设置属性,而是重写属性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))
你会回来的:

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` 正在尝试实例化“ToolKit”。。。 实例化失败:无法实例化基“ToolKit”类 正在尝试实例化“Test1”。。。 Test1还活着! 正在尝试实例化“Test2”。。。 实例化失败:缺少必需的属性:`id` 正在尝试实例化“Test3”。。。 实例化失败:缺少必需的属性:`name` 但是,Python是一种动态语言,因此即使实例化级别检查通过,用户也可以随后删除该属性,这将导致
printname
由于缺少属性而引发错误。正如我所说的,只要把代码的用户当作成年人来对待,让他们做你希望他们做的事情,这样代码才能正常运行。这会省去很多麻烦,而且你会省下很多钱