可以防止调用init吗?

时间:2012-02-08 03:15:10

标签: python initialization

我正在编辑原始问题,因为我们都专注于你应该做的事情。我的问题是我能做到这一点和如何(理解可能有几种解决方案)。所以我只是要离开实际的问题并删除背景。

假设我有一个基类和一个子类。我可以在基类中做些什么来防止在子类上调用__init__ - 或者至少抛出一个异常甚至是日志,如果__init__存在或在子类上被调用?我确实希望在父类上调用__init__方法。

编辑/结论 - 在探索答案中提供的选项后,我认为这样做会很糟糕。我将以不同的方式解决我的问题。尽管如此,希望下面的答案对其他人想要这样做有帮助。

3 个答案:

答案 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