从子类中删除从父类继承的特定方法

Posted

技术标签:

【中文标题】从子类中删除从父类继承的特定方法【英文标题】:Removing specific methods from child class which are inherited from parent class 【发布时间】:2011-10-05 08:20:44 【问题描述】:

代码如下,只是基本结构:

class FooType(type):
    def __new__( cls, name, bases, classdict ):
        instance = type.__new__( cls, name, bases, classdict )
        # What can I do here?
        return instance

class FooBase( object, metaclass=FooType ):
    def __init__( self ):
        pass

class Foo( FooBase ):
    def __init__( self, name ):
        self.name = name

    def method1( self ):
        pass

    def method2( self ):
        pass

    def specialmethod( self ):
        pass

class A( Foo ):
    pass

class B( Foo ):
    pass

class C( Foo ):
    _disallowed_methods = ['specialmethod']

我想要做的是C 类的实例不应该有specialmethod,但该方法应该可用于实例AB

我可以在 C 类中重写此方法并引发错误,但我不想这样做。

我意识到我可以添加代码来检查FooType 中的_disallowed_methods,并在此基础上检查instance 是否在dir(instance) 的输出中包含其中任何一个。但我似乎无法使用迄今为止我尝试过的任何方法从C__dict__ 中删除该方法。我试过的方法是delattr(instance, 'specialmethod')del instance.__dict__['specialmethod']

delattr 方法导致“AttributeError: specialmethod”,del 方法导致“TypeError: 'dict_proxy' 对象不支持删除项目”

基本上许多不同的类都将继承自Foo,但其中一些不应该有特定的方法可供它们使用,例如C 不应该有specialmethod 可用。

我做错了什么?或者我还能如何做到这一点?

【问题讨论】:

顺便说一句:这将违反Liskov Substitution Principle。 【参考方案1】:

如果您有一个不想被修改的父级,以及一个具有一个或多个继承方法的子级,您希望它无法访问,您可以使用描述符来实现。 最简单的方法之一是使用内置的property

class Parent:
    def good_method(self):
        print('Good one')

    def bad_method(self):
        print('Bad one')

class Child(Parent):
    bad_method = property(doc='(!) Disallowed inherited')

one = Parent()
one.good_method()  # > 'Good one'
one.bad_method()   # > 'Bad one'

two = Child()
two.good_method()  # > 'Good one'
two.bad_method()   # > AttributeError: unreadable attribute
two.bad_method     # > AttributeError: unreadable attribute
two.bad_method = 'Test'  # > AttributeError: can't set attribute

help(two) 如何打印它:

class Child(Parent)
 |  Method resolution order:
 |      Child
 |      Parent
 |      builtins.object
 |  
 |  Data descriptors defined here:
 |  
 |  bad_method
 |      (!) Disallowed inherited
 |  
 |  ----------------------------------------------------------------------
 |  Methods inherited from Parent:
 |  
 |  good_method(self)
 |  
 |  ----------------------------------------------------------------------
 |  Data descriptors inherited from Parent:
 |  
 |  __dict__
 |      dictionary for instance variables (if defined)
 |  
 |  __weakref__
 |      list of weak references to the object (if defined)

在我看来,相当不错。但是如果其他方法依赖于它们,您应该注意不要以这种方式定义继承方法(这可以通过使用代理类来避免,该代理类继承自父类并将此类方法重新定义为使用super().bad_method() 而不仅仅是self.bad_method() 并指出bad_method 本身不允许描述符)。 如果需要,您可以编写更复杂的描述符逻辑

【讨论】:

【参考方案2】:

好吧,你不能以这种方式完成,因为你必须修改的不是C类,而是Foo类,它确实包含specialmethod。但实际上你不能这样做,因为class 是全局可变对象,对Foo 的任何更改都会影响所有子类。

尝试换一种方式思考。例如。你可以修改访问C类属性的逻辑:

class C( Foo ):
    def __getattribute__(self, name):
        if name in ['specialmethod']:
            raise AttributeError('no such method')
        return super(C, self).__getattribute__(name)

在那之后C('a').specialmethod() 产生一个回溯:

Traceback (most recent call last):
  File "meta.py", line 37, in <module>
    C('a').specialmethod()
  File "meta.py", line 34, in __getattribute__
    raise AttributeError('no such method')
AttributeError: no such method

【讨论】:

我试过了(很抱歉在我最初的帖子中没有提到这一点),并计划将这条路线作为最后的手段。据我了解,这种方法意味着每次使用 C 类实例的属性时,它都会通过这种方法,因此我担心它对速度/性能的影响。这是微不足道的,我不必担心吗? @skulled 是的,它可能会影响性能。但我不敢相信这对你的情况很重要。如果您担心它,只需timeit【参考方案3】:

或者我还能如何做到这一点?

您可以使用multiple inheritance 获得类似的结果。

将您只希望部分孩子拥有的方法从Foo 移动到ExtraFoo。然后使用class A(Foo, ExtraFoo)class C(Foo)。通过这种方式,您甚至可以“重新附加”给定的方法,使其在子层次结构的更下方。

如果重新附加方法不是您感兴趣的事情,那么您可以简单地将ExtraFoo 作为Foo 的子项(因此:添加方法,而不是分离它们)并拥有class A(ExtraFoo)class C(Foo) .

【讨论】:

我意识到我可以通过使用FooFooExtra 为需要specialmethod 的类和其他需要Foo 的类创建额外的类和子类。我只是想我也许可以在使用元类创建实例时从 dict 中删除该方法。猜我错了…… @skulled - Python 非常灵活,您很有可能通过内部机制实现这一目标。然而,最重要的一点是,这将是糟糕的设计。 OOP 原则之一是子类从其父类“继承”,因此“取消继承”子类就像在设计为使其飞行的飞机上建造机翼! :) 顺便说一句:您应该对您认为有用的问题进行投票,并且 - 如果有任何问题解决了您的问题 - 您也可以将其标记为“已接受”(仅提及我注意到您是该网站的新手,如果您已经知道,请原谅我那个)。 谢谢!我有点意识到,一旦回答证实了我的怀疑,我可能采取了错误的方法。 @mac - re "OOP 原则之一..." 我可以从头开始构建一个stack 类,或者我可以通过从list 继承来接近堆栈,然后丢弃一些方法。我更喜欢后者。 “对任何系统的最强测试不是它的功能是否符合预期的需求,而是当一个人想要做设计师没有预见到的事情时它的性能如何”(Alan Kay,1984,“计算机软件”科学美国人 251:4--9)。做未预见之事的一种简单形式是不做预见之事。 @AnaNimbus - 也许在某些情况下。在这个特定的例子中,可能只是糟糕的设计,因为 tera 是可预见的、更健壮、更优雅、更有弹性和完善的可用模式。【参考方案4】:

我进行过测试并偶然发现了完全相同的问题。

我发现只有一种真正的方法可以从继承的类中删除“不必要的方法”:从父类中删除它。 (这是个坏主意,因为如果至少调用一次该函数,它将破坏所有父类的所有实例和所有继承类的所有实例)。

示例代码:

class Base(object):
    def excessive(self):
        pass

class Inher(Base):
    def __init__(self):
        #import pdb
        #pdb.set_trace()
        del Base.excessive

b=Base()
example = Inher()
try:
    example.excessive()
except  AttributeError:
    print("Removed!")
else:
    raise Exception("Not removed!")
try:
    b.excessive()
except AttributeError:
    print("Unfortunately, all instances of Base no longer have .excessive() method")

原因是“继承”方法没有存储在父级中(作为代码或链接),而是保存在父级中。当有人调用一个方法时,python 会遍历所有父类,直到找到一个或停止。

就我而言,我能够使用这种技术是因为我使用了其他人的测试来达到我的目的,并且我保留了他们的“setUp/tearDown”和腋窝方法,但我已经删除了他们所有的测试。

任何现实生活中的应用程序都不应该使用这种技术。

【讨论】:

以上是关于从子类中删除从父类继承的特定方法的主要内容,如果未能解决你的问题,请参考以下文章

子类从父类继承过来的方法可以操作子类自己定义的成员变量吗

PHP - 子类没有从父类继承[重复]

Java 继承

简述在类的继承关系中,子类可以继承父类的都有哪些成员

初学Java——类和方法继承

《类的继承》第3节:方法的重写