我正在编辑原始问题,因为我们都专注于你应该做的事情。我的问题是我能做到这一点和如何(理解可能有几种解决方案)。所以我只是要离开实际的问题并删除背景。
假设我有一个基类和一个子类。我可以在基类中做些什么来防止在子类上调用__init__ - 或者至少抛出一个异常甚至是日志,如果__init__存在或在子类上被调用?我确实希望在父类上调用__init__方法。
编辑/结论 - 在探索答案中提供的选项后,我认为这样做会很糟糕。我将以不同的方式解决我的问题。尽管如此,希望下面的答案对其他人想要这样做有帮助。
答案 0 :(得分:10)
这很可行,但我认为你不应该这样做。 告诉用户如何使用你的课程,他们应该遵守。此外,如果某人是子类,他应该知道如何调用父级的初始化方法。
作为概念证明,这里是如何使用元类(Python 2.x语法)完成的:
>>> class WhoMovedMyInit(object):
class __metaclass__(type):
def __init__(self, *args, **kw):
super(type,self).__init__(*args, **kw)
if self.__init__ is not WhoMovedMyInit.__init__:
raise Exception('Dude, I told not to override my __init__')
>>> class IAmOk(WhoMovedMyInit):
pass
>>> class Lol(WhoMovedMyInit):
def __init__(self):
pass
Traceback (most recent call last):
File "<pyshell#35>", line 1, in <module>
class Lol(WhoMovedMyInit):
File "<pyshell#31>", line 6, in __init__
raise Exception('Dude, I told not to override my __init__')
Exception: Dude, I told not to override my __init__
您还可以将子类__init__
方法替换为警告用户或在“运行时”引发错误的方法。
答案 1 :(得分:9)
“我是否应该或需要这样做是一个单独的讨论:)”
请记住这一点。
但是可以这样做 - 当实例化一个类时,不仅语法就像 方法调用 - 类对象名称后跟一个括号 - 类本身(它是一个Python对象)被称为 - 作为可调用对象。
在Python中调用对象会调用其类中的__call__
魔术方法。因此,实例化一个类,在其元类上调用__call__
方法。
标准元类中的__call__
方法内部是什么(“类型”)大致等同于:
def __call__(cls, *args, **kw):
self = cls.__new__(cls, *args, **kw)
cls.__init__(self, *args, **kw)
return self
因此,如果您编写了一个元类,覆盖__call__
并取消对这些中的__init__
的调用,则根本不会调用它:
class Meta(type):
def __call__(cls, *args, **kw):
return cls.__new__(cls, *args, **kw)
class NoInit(object):
__metaclass__ = Meta
def __init__(self):
print "Hello!"
NoInit()
如果你只想避免sublcasses有__init__
而不是不调用它,你可以做一个更简单的元类,它只会在类实例化时引发一个异常:
class Meta(type):
def __new__(metacls, name, bases, dct):
if "__init__" in dct:
raise NameError("Classes in this hierarchy should not have an __init__ method")
return type.__new__(metacls, name, bases, dct)
答案 2 :(得分:1)
这些答案大多数都已过时。
从python 3.6开始可以很容易地做到这一点。它是在PEP487中定义的。
class Base:
def __init_subclass__(cls):
if Base.__init__ is not cls.__init__:
raise Exception(f'Do not override {cls}.__init__')
class Good(Base):
pass
class Bad(Base):
def __init__(self):
pass
Good() # No problem
Bad() # Raises Exception