假设我有这段代码:
class Foo:
def write(self, s=""):
# Make sure that overwritten
# 'write' method in child class
# does what it's specified, and
# then what comes next...
print "-From Foo"
class Bar(Foo):
def write(self, s=""):
print s
baz = Bar()
baz.write("Hello, World!")
最后一次通话显然会自动输出你好的世界。我需要让它写“-From Foo”,但不修改Bar类,只需要Foo类。我已尝试使用__bases__
和其他内容,但它不适用于我的目的。
答案 0 :(得分:4)
我100%同意Lattyware:你不应该这样做。父类不应该“知道”子类或它们如何工作。
但我必须说可以使用一些__getattribute__
魔法:
class Foo(object):
def __getattribute__(self, attr):
if attr != 'write':
return super(Foo, self).__getattribute__(attr)
meth = super(Foo, self).__getattribute__(attr)
if meth.im_func is Foo.write.im_func:
# subclass does not override the method
return meth
def assure_calls_base_class(*args, **kwargs):
meth(*args, **kwargs)
Foo.write(self, *args, **kwargs)
return assure_calls_base_class
def write(self, s=""):
print "-From Foo"
class Bar(Foo):
def write(self, s=""):
print s
运行代码:
>>> b = Bar()
>>> b.write('Hello, World!')
Hello, World!
-From Foo
但是请注意,这只是一个黑客攻击,并且在使用一些继承时可能会中断,或者即使您从类中访问write
:
>>> Bar.write(b, 'Hello, World!') #should be equivalent to b.write('Hello, World!')
Hello, World!
答案 1 :(得分:3)
在没有修改Bar()
的情况下,没有(好的)方法可以做到这一点 - 您要做的是在super()
内使用Bar()
,这将允许您调用父方法。
如果您正在使用一个无法修改的类而不执行此操作,那么最好的解决方案是创建一个包装类,使用不好玩的类手动执行您想要的操作。 E.g:
class BarWrapper(Foo):
def __init__(self, *args, **kwargs):
self.bar = Bar(*args, **kwargs)
def write(self, *args, **kwargs):
super(BarWrapper, self).write(*args, **kwargs)
self.bar.write(*args, **kwargs)
(当然,根据你的课程有多少,需要更多,并且在3.x中注意,你可以通过删除参数来使用更简单的super()
语法。)
答案 2 :(得分:3)
这是使用元类魔法做到这一点的一种方法;恕我直言,它比其他方法更强大和灵活,它还处理无限呼叫(例如Bar.write(x, "hello")
)和单一继承(见下面的Baz):
class ReverserMetaclass(type):
def __new__(cls, name, bases, dct):
""" This metaclass replaces methods of classes made from it
with a version that first calls their base classes
"""
# create a new namespace for the new class
new_dct = {}
for member_name, member in dct.items():
# only decorate methods/callable in the new class
if callable(member):
member = cls.wrap(bases, member_name, member)
new_dct[member_name] = member
# construct the class
return super(ReverserMetaclass, cls).__new__(cls, name, bases, new_dct)
# instead of the above, you can also use something much simpler
# dct['read'] = cls.wrap(bases, 'read', dct['read'])
# return super(ReverserMetaclass, cls).__new__(cls, name, bases, dct)
# if you have a specific method that you want to wrap and want to
# leave the rest alone
@classmethod
def wrap(cls, bases, name, method):
""" this method calls methods in the bases before calling the method """
def _method(*args, **kwargs):
for base in bases:
if hasattr(base, name):
getattr(base, name)(*args, **kwargs)
# put this above the loop if you want to reverse the call order
ret = method(*args, **kwargs)
return ret
return _method
示例控制台运行:
>>> class Foo(object):
... __metaclass__ = ReverserMetaclass
... def write(self, s=""):
... # Make sure that overwritten
... # 'write' method in child class
... # does what it's specified, and
... # then what comes next...
... print "Write - From Foo", s
... def read(self):
... print "Read - From Foo"
...
>>> class Bar(Foo):
... def write(self, s=""):
... print "Write - from Bar", s
... def read(self):
... print "Read - From Bar"
...
>>> class Baz(Bar):
... def write(self, s=""):
... print "Write - from Baz", s
...
>>> x = Bar()
>>> x.write("hello")
Write - From Foo hello
Write - from Bar hello
>>> Bar.read(x)
Read - From Foo
Read - From Bar
>>>
>>> x = Baz()
>>> x.read()
Read - From Foo
Read - From Bar
>>> x.write("foo")
Write - From Foo foo
Write - from Bar foo
Write - from Baz foo
Python元类非常强大,但正如其他人所说,你真的不想经常做这种魔术。
答案 3 :(得分:1)
这是使用元类进行此操作的另一种方法。它使用__getattribute__()
的一个重要优点是,访问或使用其他子类属性和方法不会产生额外的开销。如果定义了它的子类,它还支持单继承。
class Foo(object):
class __metaclass__(type):
def __new__(metaclass, classname, bases, classdict):
clsobj = super(metaclass, metaclass).__new__(metaclass, classname,
bases, classdict)
if classname != 'Foo' and 'write' in classdict: # subclass?
def call_base_write_after(self, *args, **kwargs):
classdict['write'](self, *args, **kwargs)
Foo.write(self, *args, **kwargs)
setattr(clsobj, 'write', call_base_write_after) # replace method
return clsobj
def write(self, s=""):
print "-From Foo"
class Bar(Foo):
def write(self, s=""):
print 'Bar:', s
class Baz(Bar): # sub-subclass
def write(self, s=""):
print 'Baz:', s
Bar().write('test')
Baz().write('test')
输出:
Bar: test
-From Foo
Baz: test
-From Foo
如果您希望子类write()
方法之后调用他们的基类的版本而不是根(Foo
)类,只需更改硬编码:
Foo.write(self, *args, **kwargs)
致电:
super(clsobj, self).write(*args, **kwargs)
在Foo.__new__()
。