繁体   English   中英

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

[英]Removing specific methods from child class which are inherited from parent class

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

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

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

我可以在 class C中覆盖此方法并引发错误,但我不想这样做。

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

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

基本上许多不同的类将从Foo继承,但其中一些不应该有特定的方法可用,比如C不应该有specialmethod的特殊方法。

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

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

尝试以另一种方式思考。 例如,您可以修改访问C class 属性的逻辑:

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

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

您可以通过使用多个 inheritance来获得类似的结果。

将您只希望某些子级拥有的方法从Foo移动到ExtraFoo 然后使用class A(Foo, ExtraFoo)class C(Foo) 这样,您甚至可以在子层次结构的下方“重新附加”给定方法。

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

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

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

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

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

示例代码:

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”和腋窝方法,但我已经删除了他们所有的测试。

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM