Наследование Python — как отключить функцию

В C++ вы можете отключить функцию в родительском классе, объявив ее как приватную в дочернем классе. Как это можно сделать в Python? т.е. Как я могу скрыть родительскую функцию от общедоступного интерфейса ребенка?


person Boris Gorelik    schedule 23.10.2008    source источник


Ответы (7)


На самом деле в Python нет настоящих «частных» атрибутов или методов. Одна вещь, которую вы можете сделать, это просто переопределить метод, который вам не нужен в подклассе, и вызвать исключение:

>>> class Foo( object ):
...     def foo( self ):
...         print 'FOO!'
...         
>>> class Bar( Foo ):
...     def foo( self ):
...         raise AttributeError( "'Bar' object has no attribute 'foo'" )
...     
>>> b = Bar()
>>> b.foo()
Traceback (most recent call last):
  File "<interactive input>", line 1, in <module>
  File "<interactive input>", line 3, in foo
AttributeError: 'Bar' object has no attribute 'foo'
person kurosch    schedule 23.10.2008
comment
›В Python действительно нет настоящих частных атрибутов или методов. Поэтому я не спрашивал, как сделать их приватными, а как «убрать» их из интерфейса. Надеюсь, что отредактированная версия будет более точной - person Boris Gorelik; 24.10.2008
comment
Я согласен с тем, что NotImplementedError, вероятно, лучше всего использовать, но если вы действительно хотите сопоставить отсутствие унаследованного метода, вместо этого вызовите AttributeError (что вы получили бы, если бы родительский метод не существовал). - person Tony Meyer; 24.10.2008
comment
Хороший вопрос относительно AttributeError. Я обновлю свой пример. - person kurosch; 24.10.2008
comment
Это не делает то, что вы хотите - AttributeError будет вызван только в том случае, если метод foo /invoked/ -- getattr(b, 'foo') по-прежнему возвращает объект метода (атрибут!). - person cdleary; 25.10.2008
comment
@JBernardo, вы не должны отрицать ответ, потому что он не отвечает на вопрос так, как вы думаете лучше всего. Ничто в вопросе не указывает на то, что этот ответ неверен. Способов содрать шкуру с кошки может быть несколько. - person broccoli2000; 08.09.2016
comment
Документация Python по NotImplementedError рекомендует просто установить его в None: его не следует использовать для указания того, что оператор или метод вообще не предназначен для поддержки — в этом случае либо оставьте оператор/метод неопределенным, либо, если это подкласс, установите его на Нет. - person Bachsau; 17.12.2018

способ решения проблемы Курошем не совсем корректен, потому что вы все еще можете использовать b.foo, не получая AttributeError. Если вы не вызываете функцию, ошибка не возникает. Вот два способа, которыми я могу это сделать:

import doctest

class Foo(object):
    """
    >>> Foo().foo()
    foo
    """
    def foo(self): print 'foo'
    def fu(self): print 'fu'

class Bar(object):
    """
    >>> b = Bar()
    >>> b.foo()
    Traceback (most recent call last):
    ...
    AttributeError
    >>> hasattr(b, 'foo')
    False
    >>> hasattr(b, 'fu')
    True
    """
    def __init__(self): self._wrapped = Foo()

    def __getattr__(self, attr_name):
        if attr_name == 'foo': raise AttributeError
        return getattr(self._wrapped, attr_name)

class Baz(Foo):
    """
    >>> b = Baz()
    >>> b.foo() # doctest: +ELLIPSIS
    Traceback (most recent call last):
    ...
    AttributeError...
    >>> hasattr(b, 'foo')
    False
    >>> hasattr(b, 'fu')
    True
    """
    foo = property()

if __name__ == '__main__':
    doctest.testmod()

Бар использует шаблон «обертки», чтобы ограничить доступ к обернутому объекту. Мартелли хорошо рассказал об этом. Baz использует встроенное свойство для реализации протокола дескриптора для атрибут для переопределения.

person cdleary    schedule 25.10.2008
comment
Ну, конечно, в моем ответе это все еще видно, но вы не можете использовать его как таковое, потому что это вызовет исключение. Однако верное замечание. - person kurosch; 27.10.2008
comment
+1, какой умный трюк использовать пустое свойство для удаления метода foo! :D - person MestreLion; 13.05.2012
comment
Это сломает класс для использования операторов, определенных для родителя, потому что нет подкласса. Также __getattr__ медленный - person JBernardo; 21.04.2013
comment
В чем преимущество установки property() вместо чего-либо еще, например None? - person Praxeolitic; 10.04.2016

Вариант ответа куроша:

class Foo( object ):
    def foo( self ):
        print 'FOO!'

class Bar( Foo ):
    @property
    def foo( self ):
        raise AttributeError( "'Bar' object has no attribute 'foo'" )

b = Bar()
b.foo

Это вызывает AttributeError для свойства, а не при вызове метода.

Я бы предложил это в комментарии, но, к сожалению, у меня пока нет репутации.

person John Damen    schedule 17.04.2014
comment
Будет ли это вызывать AttributeError, если вы вызовете getattr(b, "Foo")? К сожалению, у меня нет интерпретатора Python, чтобы протестировать его. - person Adrian Ratnapala; 17.04.2014
comment
если вы имеете в виду getattr(b, 'foo'), то да, будет - person John Damen; 17.04.2014
comment
Да, это то, что я имел в виду. Хотя getattr(b, 'Foo') также выдаст вам ошибку атрибута, так что не беспокойтесь! - person Adrian Ratnapala; 17.04.2014

class X(object):
    def some_function(self):
        do_some_stuff()

class Y(object):
    some_function = None

Это может привести к возникновению некоторых неприятных и трудно находимых исключений, поэтому вы можете попробовать следующее:

class X(object):
    def some_function(self):
        do_some_stuff()

class Y(object):
    def some_function(self):
        raise NotImplementedError("function some_function not implemented")
person Jason Baker    schedule 23.10.2008

Это самый чистый способ, который я знаю.

Переопределите методы, и пусть каждый из переопределенных методов вызывает ваш метод disabledmethods(). Так:

class Deck(list):
...
@staticmethod
    def disabledmethods():
        raise Exception('Function Disabled')
    def pop(self): Deck.disabledmethods()
    def sort(self): Deck.disabledmethods()
    def reverse(self): Deck.disabledmethods()
    def __setitem__(self, loc, val): Deck.disabledmethods()
person Joey Nelson    schedule 17.04.2014
comment
Интересный подход, но вам нужно убедиться, что номер аргумента совпадает в случае pop, а аргументы ключевого слова разрешены для сортировки и реверса. В противном случае вместо предполагаемого исключения будет выброшено исключение TypeError. - person Vlas Sokolov; 25.10.2018

Другой подход заключается в определении дескриптора, который выдает ошибки при доступе.

    class NotHereDescriptor:
        def __get__(self, obj, type=None):
            raise AttributeError
    
    class Bar:
        foo = NotHereDescriptor()

По своей природе это похоже на подход свойств, который использовали несколько человек выше. Однако у него есть то преимущество, что hasattr(Bar, 'foo') вернет False, как и следовало ожидать, если бы функция действительно не существовала. Что еще больше снижает вероятность странных ошибок. Хотя он все еще отображается в dir(Bar).

Если вам интересно, что это делает и почему это работает, ознакомьтесь с разделом дескрипторов на странице модели данных https://docs.python.org/3/reference/datamodel.html#descriptors и дескриптор как https://docs.python.org/3/howto/descriptor.html

person Gordon Wrigley    schedule 25.08.2020

Это может быть еще проще.

@property
def private(self):
    raise AttributeError

class A:
    def __init__(self):
        pass
    def hello(self):
        print("Hello World")

class B(A):
    hello = private # that short, really
    def hi(self):
        A.hello(self)

obj = A()
obj.hello()
obj = B()
obj.hi() # works
obj.hello() # raises AttributeError
person Community    schedule 26.06.2019