2012-02-08 38 views
4

我在编辑原始问题,因为我们都专注于应该如何做到这一点。我的问题很简单,我可以做到这一点和如何(了解可能有几种解决方案)。所以我只是要留下实际的问题并剪掉背景。可能阻止init被调用?

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

编辑/结论 - 在探索答案中提出的选项后,我决定这样做会是不好的风格。我会以不同的方式解决我的问题。尽管如此,希望下面的答案有助于防止其他人想要这样做。

+1

添加注释:“不要覆盖'__init__'方法”。相信你的用户。另外,聪明的用户会知道如何使用'super' – JBernardo 2012-02-08 03:19:42

+0

我没有看到'__init__'需要f ***,这个类可以简单地跟踪'state_entry()'是否被调用并拒绝执行在此之前的任何事 – wim 2012-02-08 03:21:21

+0

这些建议很有帮助,但我很好奇这是否可以在技术上完成。不管我是否应该这样做,都是一个单独的讨论:) – 2012-02-08 03:27:57

回答

9

这是相当可行的,但我不认为你应该。 告诉用户如何使用你的课堂,他们应该服从。另外,如果某人正在进行子类化,他应该知道如何调用父级的初始化方法。

由于概念证明,这里是如何将其与元类来完成(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

因为有异常消息。 :) – Arafangion 2012-02-08 03:54:01

8

“不管是不是我应该或需要做的是一个单独的讨论:)”

请记住这一点。

但是可以这样做 - 当一个类被实例化时,不仅语法就像 一个方法调用 - 类对象名称跟在ya括号 - 类本身(它是一个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__而不是不调用它,你可以做一个简单得多的元类是只想RAIS e类实例化时的例外情况:

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) 
相关问题