只能从python

时间:2015-11-13 07:07:13

标签: python class oop inheritance member

我们说我有以下两个班级

class A:
    def own_method(self):
        pass
    def descendant_method(self):
        pass

class B(A):
    pass

我希望descendant_method可以从B的实例调用,但不能从A调用,own_method可以从任何地方调用。

我能想到几个解决方案,都不尽如人意:

  1. 检查一些字段并手动提升NotImplementedError
  2. class A:
        def __init__(self):
            self.some_field = None
        def own_method(self):
            pass
        def descendant_method(self):
            if self.some_field is None:
                raise NotImplementedError
    
    class B(A):
        def __init__(self):
            super(B, self).__init__()
            self.some_field = 'B'
        pass
    

    但这是修改方法的运行时行为,我不想做

    1. 使用mixin:
    2. class A:
          def own_method(self):
              pass
      
      class AA:
          def descendant_method(self):
              pass
      
      class B(AA, A):
          pass
      

      只要descendant_methodA中使用不多,这就很好,否则我们必须继承AA(A),这就违背了整点< / p>

      1. A中将方法设为私有,并在元类中重新定义:
      2. class A:
            def own_method(self):
                pass
            def __descendant_method(self):
                pass
        
        class AMeta(type):
            def __new__(mcs, name, parents, dct):
                par = parents[0]
                desc_method_private_name = '_{}__descendant_method'.format(par.__name__)
                if desc_method_private_name in par.__dict__:
                    dct['descendant_method'] = par.__dict__[desc_method_private_name]
        
                return super(AMeta, mcs).__new__(mcs, name, parents, dct)
        
        class B(A, metaclass=AMeta):
            def __init__(self):
                super(B, self).__init__()
        

        这很有效,但看起来很脏,就像在self.descendant_method = self._A__descendant_method中写B一样。

        什么是正确的&#34; pythonic&#34;实现这种行为的方式?

        UPD:当然,将方法直接放在B中会起作用,但我希望A将有许多将使用此方法的后代,并且不希望在每个子类中定义它。

3 个答案:

答案 0 :(得分:2)

据我所知,这可能是实现你想要的最恐怖的方式:

class A:
    def own_method(self):
        pass
    def descendant_method(self):
        raise NotImplementedError

class B(A):
    def descendant_method(self):
        ...

另一种选择可能如下:

class A:
    def own_method(self):
        pass
    def _descendant_method(self):
        pass

class B(A):
    def descendant_method(self):
        return self._descendant_method(self)

他们都是Pythonic,因为它是明确,可读,清晰和简洁

  • 这是明确的,因为它没有做任何不必要的魔法。
  • 这是可读的,因为 人们可以准确地说出你在做什么,以及你的意图是什么 乍一看。
  • 很明显,因为领先的单下划线是 私有的Python社区中广泛使用的约定 (非魔法)方法 - 任何使用它的开发人员都应该知道 谨慎。

选择其中一种方法取决于您对用例的意图。你问题中一个更具体的例子会有所帮助。

答案 1 :(得分:2)

xxx zzz yyy 继承AA有什么不妥?它基本上是一个抽象的基类,它增加了A中不可用的附加功能。如果你真的不希望A被实例化,那么pythonic答案就不用担心了,只记录用户并不打算这样做。虽然如果您真的坚持,如果用户尝试实例化AA,您可以定义__new__以引发错误。

AA

另一种选择可能是class A: def f(self): pass class AA(A): def g(self): pass def __new__(cls, *args, **kwargs): if cls is AA: raise TypeError("AA is not meant to be instansiated") return super().__new__(cls) class B(AA): pass Abstract Base Class。要使其工作,您需要将至少一个方法定义为抽象 - AA如果没有其他方法可以说是抽象的,那么就可以做到。

__init__

最后,在from abc import ABCMeta, abstractmethod class A: def __init__(self, val): self.val = val def f(self): pass class AA(A, metaclass=ABCMeta): @abstractmethod def __init__(self, val): super().__init__(val) def g(self): pass class B(AA): def __init__(self, val): super().__init__(val) 上使用后代方法有什么不好,但只是没有使用它。您正在编写A的代码,因此请勿使用该方法......您甚至可以记录A不能直接使用的方法,但相当意味着可供儿童班使用。这样未来的开发人员就会知道你的意图。

答案 2 :(得分:0)

尝试使用__class__.__name__检查班级名称。

class A(object):

    def descendant_method(self):
        if self.__class__.__name__ == A.__name__:
            raise NotImplementedError
        print 'From descendant'


class B(A):
    pass


b = B()
b.descendant_method()
a = A()
a.descendant_method()