从子类中删除从父类继承的特定方法
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
,但该方法应该可用于实例A
和B
。
我可以在 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)
.
【讨论】:
我意识到我可以通过使用Foo
和FooExtra
为需要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”和腋窝方法,但我已经删除了他们所有的测试。
任何现实生活中的应用程序都不应该使用这种技术。
【讨论】:
以上是关于从子类中删除从父类继承的特定方法的主要内容,如果未能解决你的问题,请参考以下文章