使功能无法覆盖

时间:2008-11-26 15:08:30

标签: python

我知道python函数默认是虚拟的。假设我有这个:

class Foo:
    def __init__(self, args):
        do some stuff
    def goo():
        print "You can overload me"
    def roo():
        print "You cannot overload me"

我不希望他们能够这样做:

class Aoo(Foo):
    def roo():
        print "I don't want you to be able to do this"

有没有办法防止用户超载roo()?

5 个答案:

答案 0 :(得分:32)

您可以使用元类:

class NonOverridable(type):
    def __new__(self, name, bases, dct):
        if bases and "roo" in dct:
            raise SyntaxError, "Overriding roo is not allowed"
        return type.__new__(self, name, bases, dct)

class foo:
    __metaclass__=NonOverridable
    ...

每当创建子类时,都会调用元类型的 new ;如果您出现,这将导致错误。只有在没有基类的情况下,它才会接受roo的定义。

通过使用注释声明哪些方法是最终的,您可以使方法更加花哨;然后,您需要检查所有基础并计算所有最终方法,以查看是否有任何基础被覆盖。

这仍然不能阻止某人在定义之后将方法修补到类中;您可以尝试通过使用自定义词典作为类的字典来捕获这些(这可能不适用于所有Python版本,因为类可能要求类字典具有精确的dict类型)。

答案 1 :(得分:8)

由于Python有猴子修补,你不仅不能做任何“私人”。即使你可以,有人仍然可以在方法函数的新版本中进行monkeypatch。

您可以将此类名称用作“不要靠近”警告。

class Foo( object ):
    def _roo( self ):
       """Change this at your own risk."""

这是通常的做法。每个人都可以阅读你的来源。他们被警告了。如果他们大胆地去了他们被警告不去的地方,他们就会得到他们应得的。它不起作用,你无法帮助他们。

您可以尝试使用“私有”方法调用的内部类和“隐藏”实现模块来故意隐藏这一点。但是......每个人都有你的来源。你不能阻止任何事情。你只能告诉人们他们行为的后果。

答案 2 :(得分:6)

def non_overridable(f):
    f.non_overridable = True
    return f

class ToughMeta(type):
    def __new__(cls, name, bases, dct):
        non_overridables = get_non_overridables(bases)
        for name in dct:
            if name in non_overridables:
                raise Exception ("You can not override %s, it is non-overridable" % name)
        return type.__new__(cls, name, bases, dct)

def get_non_overridables(bases):
    ret = []
    for source in bases:
        for name, attr in source.__dict__.items():
            if getattr(attr, "non_overridable", False):
                ret.append(name)
        ret.extend(get_non_overridables(source.__bases__))
    return ret

class ToughObject(object):
    __metaclass__ = ToughMeta
    @non_overridable
    def test1():
        pass

# Tests ---------------
class Derived(ToughObject):
    @non_overridable
    def test2(self):
        print "hello"

class Derived2(Derived):
    def test1(self):
        print "derived2"

# --------------------

答案 3 :(得分:2)

Python 3.8(于2019年10月发布)在输入中添加了final限定词。

一个final限定词将以最终修饰符和最终类型注释的形式添加到键入模块中,以实现三个相关目的:

  • 声明不应重写方法
  • 声明一个类不应被子类化
  • 声明不应重新分配变量或属性
from typing import final

class Base:
    @final
    def foo(self) -> None:
        ...

class Derived(Base):
    def foo(self) -> None:  # Error: Cannot override final attribute "foo"
                            # (previously declared in base class "Base")
        ...

这似乎更符合您的要求,并且现在得到了核心Python的支持。

有关更多详细信息,请查看PEP-591

答案 4 :(得分:0)

派对迟到但默认情况下并非所有python方法都是“虚拟” - 请考虑:

class B(object):
    def __priv(self): print '__priv:', repr(self)

    def call_private(self):
        print self.__class__.__name__
        self.__priv()

class E(B):
    def __priv(self): super(E, self).__priv()

    def call_my_private(self):
        print self.__class__.__name__
        self.__priv()

B().call_private()
E().call_private()
E().call_my_private()

由于名称损坏导致的打击:

B
__priv: <__main__.B object at 0x02050670>
E
__priv: <__main__.E object at 0x02050670>
E
Traceback (most recent call last):
  File "C:/Users/MrD/.PyCharm2016.3/config/scratches/test_double__underscore", line 35, in <module>
    E().call_my_private()
  File "C:/Users/MrD/.PyCharm2016.3/config/scratches/test_double__underscore", line 31, in call_my_private
    self.__priv()
  File "C:/Users/MrD/.PyCharm2016.3/config/scratches/test_double__underscore", line 27, in __priv
    def __priv(self): super(E, self).__priv()
AttributeError: 'super' object has no attribute '_E__priv'

因此,如果你想从语言中获得一些帮助,禁止人们在课堂上覆盖你需要的一些功能,那么这就是你要走的路。如果您想要最终的方法是您的类API的一部分,但是您仍然坚持使用注释方法(或元类黑客)。我个人的观点是,最后一个关键字对于继承非常有用 - 因为你可以避免在被覆盖时以类似的方式破坏类(例如,考虑在超级实现中使用“final”方法然后有人覆盖 - 繁荣,超级破坏) - 并且出于文档目的(没有文档比编译时语法错误更好) - 但是Python的动态性质不允许它和hacks很脆弱 - 所以添加一个文档字符串:

"""DON'T OVERRIDE THIS METHOD"""