Создание функций, которые не могут быть переопределены - PullRequest
10 голосов
/ 26 ноября 2008

Я знаю, что функции 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 ()?

Ответы [ 4 ]

32 голосов
/ 26 ноября 2008

Вы можете использовать метакласс:

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).

9 голосов
/ 26 ноября 2008

Поскольку в Python есть исправления для обезьян, вы не только не можете сделать что-то «приватным». Даже если бы вы могли это сделать, кто-то мог бы по-прежнему использовать новую версию функции метода.

Вы можете использовать это имя в качестве предупреждения "не подходите близко".

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

Это обычный подход. Каждый может прочитать ваш источник. Они были предупреждены. Если они смело идут туда, где их предупредили не идти, они получают то, что заслуживают. Это не работает, и вы не можете им помочь.

Вы можете попытаться сделать это намеренно скрытым с помощью внутренних классов и «скрытых» модулей реализации, которые вызываются «закрытыми» методами. Но ... у каждого есть ваш источник. Вы не можете ничего предотвратить . Вы можете только консультировать людей о последствиях их действий.

6 голосов
/ 26 ноября 2008
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"

# --------------------
0 голосов
/ 07 февраля 2017

Поздно, но не все методы 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'

Так что, если вы хотите получить некоторую помощь от языка, чтобы запретить людям переопределять некоторые функциональные возможности, которые вам нужны в вашем классе, это путь. Если метод, который вы хотите сделать final, является частью вашего класса API, вы застряли на подходе с комментариями (или хаке метакласса). Мое личное мнение таково, что последнее ключевое слово очень полезно для наследования - так как вы можете избежать коварного взлома класса при его переопределении (рассмотрите возможность использования, например, метода "final" в супер реализации, а затем кто-то переопределяет - boom, super broken) и для целей документации (никакие документы не лучше, чем синтаксическая ошибка времени компиляции), но динамическая природа Python не позволяет этого, а хаки хрупки - поэтому добавьте строку документации:

"""DON'T OVERRIDE THIS METHOD"""
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...