使用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
由于缺少属性而引发错误。正如我所说的,只要把代码的用户当作成年人来对待,让他们做你希望他们做的事情,这样代码才能正常运行。这会省去很多麻烦,而且你会省下很多钱