如何在Python中执行BaseClass方法,以在DerivedClass方法覆盖之前调用它
我几乎可以肯定我想做的事情有一个专业术语,但因为我不太熟悉,所以我会尽量详细描述我的想法。我有一组类,它们都继承自一个基类。这些类几乎都是由各自特有的方法组成,只有少数几个方法在名称、功能和一些逻辑上是相似的,但它们的具体实现还是有很大不同。所以我想知道,是否可以在基类中创建一个方法,执行一些所有方法都相似的逻辑,然后再继续执行各个类特有的方法。希望这样说能让你明白,我会给出一个基本的例子来说明我的想法。
首先,想象一个基类,它大概是这样的:
class App(object):
def __init__(self, testName):
self.localLog = logging.getLogger(testName)
def access(self):
LOGIC_SHARED
然后是一个派生类的例子:
class App1(App):
def __init__(self, testName):
. . .
super(App1, self).__init__(testName)
def access(self):
LOGIC_SPECIFIC
我希望实现的效果是,当调用任何的App
类的access
方法时,能够先执行基类中的access
方法里的LOGIC_SHARED
部分,然后再执行各个派生类中access
方法的LOGIC_SPECIFIC
部分(顾名思义,这部分是特定于每个类的)。
如果这有什么不同的话,LOGIC_SHARED
部分主要包含一些日志记录和维护任务。
希望这样说够清楚,能让你理解我的想法。
注意 1:在LOGIC_SHARED
部分中使用了一些特定于类的参数。
注意 2:重要的是要仅使用Python内置的函数和模块来实现这个行为。
注意 3:LOGIC_SHARED
部分大概是这样的:
try:
self.localLog.info("Checking the actual link for %s", self.application)
self.link = self.checkLink(self.application)
self.localLog.info("Actual link found!: %s", self.link)
except:
self.localLog.info("No links found. Going to use the default link: %s", self.link)
所以,我有很多特定类的实例属性,但我不太确定如何从基类中使用这些属性。
6 个回答
我个人更喜欢Jonathon Reinhart的回答,但既然你想要更多选择,这里还有两个方法。我可能永远不会使用元类的方法,尽管它很酷,但我可能会考虑第二种使用装饰器的方法。
使用元类
这个方法使用了一个元类来处理基类,这样可以确保基类的访问方法会优先被调用,而不需要单独的私有函数,也不需要明确调用super
或其他类似的东西。最终的结果是:在继承类中没有额外的工作或代码。
而且,它的效果就像魔法一样 </spongebob>
下面是实现这个功能的代码。在这里 http://dbgr.cc/W 你可以实时跟踪代码,看看它是怎么工作的:
#!/usr/bin/env python
class ForceBaseClassFirst(type):
def __new__(cls, name, bases, attrs):
"""
"""
print("Creating class '%s'" % name)
def wrap_function(fn_name, base_fn, other_fn):
def new_fn(*args, **kwargs):
print("calling base '%s' function" % fn_name)
base_fn(*args, **kwargs)
print("calling other '%s' function" % fn_name)
other_fn(*args, **kwargs)
new_fn.__name__ = "wrapped_%s" % fn_name
return new_fn
if name != "BaseClass":
print("setting attrs['access'] to wrapped function")
attrs["access"] = wrap_function(
"access",
getattr(bases[0], "access", lambda: None),
attrs.setdefault("access", lambda: None)
)
return type.__new__(cls, name, bases, attrs)
class BaseClass(object):
__metaclass__ = ForceBaseClassFirst
def access(self):
print("in BaseClass access function")
class OtherClass(BaseClass):
def access(self):
print("in OtherClass access function")
print("OtherClass attributes:")
for k,v in OtherClass.__dict__.iteritems():
print("%15s: %r" % (k, v))
o = OtherClass()
print("Calling access on OtherClass instance")
print("-------------------------------------")
o.access()
这个代码使用元类来替换OtherClass
的访问函数,变成一个先调用BaseClass
的访问函数,再调用OtherClass
的访问函数的函数。想了解元类的最佳解释可以查看这里 https://stackoverflow.com/a/6581949。
逐步跟踪代码应该能帮助你理解事情的顺序。
使用装饰器
这个功能也可以很容易地放入一个装饰器中,如下所示。同样,你可以在这里找到一个可逐步调试和运行的版本 http://dbgr.cc/0
#!/usr/bin/env python
def superfy(some_func):
def wrapped(self, *args, **kwargs):
# NOTE might need to be changed when dealing with
# multiple inheritance
base_fn = getattr(self.__class__.__bases__[0], some_func.__name__, lambda *args, **kwargs: None)
# bind the parent class' function and call it
base_fn.__get__(self, self.__class__)(*args, **kwargs)
# call the child class' function
some_func(self, *args, **kwargs)
wrapped.__name__ = "superfy(%s)" % some_func.__name__
return wrapped
class BaseClass(object):
def access(self):
print("in BaseClass access function")
class OtherClass(BaseClass):
@superfy
def access(self):
print("in OtherClass access function")
print("OtherClass attributes")
print("----------------------")
for k,v in OtherClass.__dict__.iteritems():
print("%15s: %r" % (k, v))
print("")
o = OtherClass()
print("Calling access on OtherClass instance")
print("-------------------------------------")
o.access()
上面的装饰器会先获取BaseClass
同名的函数,然后在调用OtherClass
的函数之前先调用它。
希望这个简单的方法能帮到你。
class App:
def __init__(self, testName):
self.localLog = logging.getLogger(testName)
self.application = None
self.link = None
def access(self):
print('There is something BaseClass must do')
print('The application is ', self.application)
print('The link is ', self.link)
class App1(App):
def __init__(self, testName):
# ...
super(App1, self).__init__(testName)
def access(self):
self.application = 'Application created by App1'
self.link = 'Link created by App1'
super(App1, self).access()
print('There is something App1 must do')
class App2(App):
def __init__(self, testName):
# ...
super(App2, self).__init__(testName)
def access(self):
self.application = 'Application created by App2'
self.link = 'Link created by App2'
super(App2, self).access()
print('There is something App2 must do')
这是测试的结果:
>>>
>>> app = App('Baseclass')
>>> app.access()
There is something BaseClass must do
The application is None
The link is None
>>> app1 = App1('App1 test')
>>> app1.access()
There is something BaseClass must do
The application is Application created by App1
The link is Link created by App1
There is something App1 must do
>>> app2 = App2('App2 text')
>>> app2.access()
There is something BaseClass must do
The application is Application created by App2
The link is Link created by App2
There is something App2 must do
>>>
如果我理解得没错,这条评论的意思是,你想在派生类中使用传递给父类的额外参数。
这个内容是基于Jonathon Reinhart的回答。
下面是你可以这样做的方式:
class Base(object):
def access(self,
param1 ,param2, #first common parameters
*args, #second positional parameters
**kwargs #third keyword arguments
):
# Shared logic 1
self._specific_logic(param1, param2, *args, **kwargs)
# Shared logic 2
def _specific_logic(self, param1, param2, *args, **kwargs):
# Nothing special to do in the base class
pass
# Or you could even raise an exception
raise Exception('Called access on Base class instance')
class DerivedA(Base):
# overrides Base implementation
def _specific_logic(self, param1, param2, param3):
# DerivedA specific logic
class DerivedB(Base):
# overrides Base implementation
def _specific_logic(self, param1, param2, param4):
# DerivedB specific logic
def test():
x = Base()
a = DerivedA()
a.access("param1", "param2", "param3") # Shared logic 1
# Derived A specific logic
# Shared logic 2
b = DerivedB()
b.access("param1", "param2", param4="param4") # Shared logic 1
# Derived B specific logic
# Shared logic 2
你想要做的事情最简单的方法就是在子类的 access
方法里直接调用父类的 access
方法。
class App(object):
def __init__(self, testName):
self.localLog = logging.getLogger(testName)
def access(self):
LOGIC_SHARED
class App1(App):
def __init__(self, testName):
super(App1, self).__init__(testName)
def access(self):
App.access(self)
# or use super
super(App1, self).access()
不过,你们共享的功能主要是记录日志和维护。除非有特别的理由把这些功能放在父类里,否则你可以考虑把这些共享的功能重构成一个装饰器函数。如果你想在类里的多个方法中重复使用类似的日志记录和维护功能,这样做特别有用。
你可以在这里了解更多关于函数装饰器的内容:http://www.artima.com/weblogs/viewpost.jsp?thread=240808,或者在Stack Overflow上查看这个问题:如何创建一系列函数装饰器?。
def decorated(method):
def decorated_method(self, *args, **kwargs):
LOGIC_SHARED
method(self, *args, **kwargs)
return decorated_method
记住,在Python中,函数是第一类对象。这意味着你可以把一个函数作为参数传递给另一个函数。装饰器函数就是利用这一点。装饰器函数接受另一个函数作为参数(这里叫做方法),然后创建一个新的函数(这里叫做decorated_method),这个新函数替代了原来的函数。
你的App1类看起来会是这样的:
class App1(App):
@logged
def access(self):
LOGIC_SPECIFIC
这实际上是这个的简写:
class App1(App):
def access(self):
LOGIC_SPECIFIC
decorated_access = logged(App.access)
App.access = decorated_access
我觉得这样比在超类中添加方法来捕获共享功能要优雅得多。
当然,可以把具体的逻辑放在一个“私有”的函数里,这样派生类就可以重写这个函数,而把access
留在基类中。
class Base(object):
def access(self):
# Shared logic 1
self._specific_logic()
# Shared logic 2
def _specific_logic(self):
# Nothing special to do in the base class
pass
# Or you could even raise an exception
raise Exception('Called access on Base class instance')
class DerivedA(Base):
# overrides Base implementation
def _specific_logic(self):
# DerivedA specific logic
class DerivedB(Base):
# overrides Base implementation
def _specific_logic(self):
# DerivedB specific logic
def test():
x = Base()
x.access() # Shared logic 1
# Shared logic 2
a = DerivedA()
a.access() # Shared logic 1
# Derived A specific logic
# Shared logic 2
b = DerivedB()
b.access() # Shared logic 1
# Derived B specific logic
# Shared logic 2