Выполнение функций без переопределения

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

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

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

Новый метатип вызывается всякий раз, когда создается подкласс; это приведет к ошибке в том случае, если вы присутствуете. Он будет принимать определение roo только в том случае, если нет базовых классов.

Вы можете сделать подход более привлекательным, используя аннотации, чтобы объявить, какие методы являются окончательными; вам необходимо проверить все базы и вычислить все окончательные методы, чтобы проверить, отменено ли какое-либо из них.

Это все равно не мешает кому-либо обезглавливать метод в класс после его определения; вы можете попытаться поймать их, используя пользовательский словарь в качестве словаря классов (который может не работать во всех версиях Python, поскольку классы могут потребовать, чтобы словарь классов имел точный тип dict).

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

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

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

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

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

 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" # -------------------- 

Поздно к партии, но не все методы 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 классов, вы застряли в подходе комментариев (или метаклассах). Мое личное мнение заключается в том, что последнее ключевое слово очень полезно для наследования – так как вы можете избежать краха класса коварными способами при переопределении (например, подумайте об использовании «окончательного» метода в суперреализации, а затем кто-то переопределит – бум, супер сломанный) – и для целей документации (никакие документы не лучше, чем синтаксическая ошибка времени компиляции), но динамическая природа Python не позволила бы ее, а хаки были хрупкими – так что добавьте docstring:

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