【问题标题】:Python inheritance - how to disable a functionPython继承 - 如何禁用函数
【发布时间】:2010-09-18 21:53:07
【问题描述】:

在 C++ 中,您可以通过在子类中将其声明为私有来禁用父类中的函数。如何在 Python 中做到这一点? IE。如何在孩子的公共界面中隐藏父母的功能?

【问题讨论】:

    标签: python inheritance interface private


    【解决方案1】:
    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")
    

    【讨论】:

      【解决方案2】:

      在 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'
      

      【讨论】:

      • >Python 中确实没有任何真正的“私有”属性或方法。这就是为什么我没有问如何将它们设为私有,而是如何从界面中“删除”它们。希望修改后的版本更准确
      • 我同意 NotImplementedError 可能是最好的使用方法,但如果你真的想匹配没有继承的方法,请改为提高 AttributeError (如果父方法是你会得到的)不存在)。
      • 关于 AttributeError 的要点。我将更新我的示例。
      • 这不是你想要的——只有当 foo 方法是 /invoked/ 时才会引发 AttributeError——getattr(b, 'foo') 仍然返回一个方法对象(一个属性!)。
      • @JBernardo,您不应该对答案投反对票,因为它不会以您认为最好的方式回答问题。问题中没有任何内容表明此答案不正确。给猫剥皮的方法可能不止一种。
      【解决方案3】:

      kurosch 解决问题的方法并不完全正确,因为您仍然可以使用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()
      

      Bar 使用“wrap”模式来限制对被包装对象的访问。 Martelli has a good talk 处理这个问题。 Baz 使用the property built-in 实现要覆盖的属性的描述符协议。

      【讨论】:

      • 嗯,当然,在我的回答中它仍然是“可见的”,但你不能“使用”它本身,因为它会引发异常。不过,这是一个有效的观点。
      • +1,使用“空”属性“删除” foo 方法是多么聪明的技巧! :D
      • 这将破坏类以使用在父级上定义的运算符,因为没有子类化。 __getattr__ 也很慢
      • 将它设置为property()而不是其他任何东西有什么好处,例如None
      【解决方案4】:

      这是我所知道的最干净的方法。

      覆盖这些方法并让每个被覆盖的方法调用您的 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()
      

      【讨论】:

      • 一个有趣的方法,但是你需要确保在pop的情况下参数编号匹配,并且允许关键字args进行排序和反转。否则会抛出 TypeError 而不是预期的异常。
      • 请编辑@staticmethod缩进的答案
      【解决方案5】:

      kurosch 答案的变体:

      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

      我会在评论中建议它,但不幸的是还没有它的声誉。

      【讨论】:

      • 如果您调用getattr(b, "Foo"),这会引发 AttributeError 吗?不幸的是,我这里没有 Python 解释器来测试它。
      • 如果你的意思是getattr(b, 'foo'),那么是的
      • 是的,我就是这个意思。虽然getattr(b, 'Foo') 也会给你一个属性错误,所以不用担心!
      【解决方案6】:

      这可能更简单。

      @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
      

      【讨论】:

        【解决方案7】:

        另一种方法是定义访问时出错的描述符。

            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 的描述符

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2014-08-28
          • 2019-01-04
          • 1970-01-01
          • 2020-09-10
          • 2011-11-24
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多