基类中的冲突属性¶
ID: py/conflicting-attributes
Kind: problem
Security severity:
Severity: warning
Precision: high
Tags:
- reliability
- maintainability
- modularity
Query suites:
- python-security-and-quality.qls
当一个类继承多个基类时,属性查找从左到右在基类中进行。这种形式的属性查找称为“方法解析顺序”,它是解决 菱形继承问题 的解决方案,其中多个基类在共享的超类中重写了一个方法。
不幸的是,这意味着如果多个基类定义了相同的属性,最左边的基类将有效地覆盖最右边的基类的属性,即使最左边的基类不是最右边的基类的子类。除非所讨论的方法是为继承而设计的,使用 super
,否则这种隐式覆盖可能不是预期行为。即使它是预期行为,它也会使代码难以理解和维护。
建议¶
有一些方法可以用来解决这个问题
在子类中覆盖属性以实现正确的行为。
修改类层次结构,将等效或冗余的方法移动到一个公共超类。
修改方法层次结构,将复杂的方法分解成组成部分。
使用委托而不是继承。
示例¶
在这个例子中,类 ThreadingTCPServer
继承自 ThreadingMixIn
和 TCPServer
。但是,这两个类都实现了 process_request
,这意味着 ThreadingTCPServer
将从 ThreadingMixIn
继承 process_request
。因此,TCPServer
中 process_request
的实现将被忽略,这可能不是正确的行为。
class TCPServer(object):
def process_request(self, request, client_address):
self.do_work(request, client_address)
self.shutdown_request(request)
class ThreadingMixIn:
"""Mix-in class to handle each request in a new thread."""
def process_request(self, request, client_address):
"""Start a new thread to process the request."""
t = threading.Thread(target = self.do_work, args = (request, client_address))
t.daemon = self.daemon_threads
t.start()
class ThreadingTCPServer(ThreadingMixIn, TCPServer): pass
这可以通过覆盖方法来解决,如类 ThreadingTCPServerOverriding
所示,或者通过确保两个基类提供的功能不重叠来解决,如类 ThreadingTCPServerChangedHierarchy
所示。
#Fixed by overriding. This does not change behavior, but makes it explicit and comprehensible.
class ThreadingTCPServerOverriding(ThreadingMixIn, TCPServer):
def process_request(self, request, client_address):
#process_request forwards to do_work, so it is OK to call ThreadingMixIn.process_request directly
ThreadingMixIn.process_request(self, request, client_address)
#Fixed by separating threading functionality from request handling.
class ThreadingMixIn:
"""Mix-in class to help with threads."""
def do_job_in_thread(self, job, args):
"""Start a new thread to do the job"""
t = threading.Thread(target = job, args = args)
t.start()
class ThreadingTCPServerChangedHierarchy(ThreadingMixIn, TCPServer):
def process_request(self, request, client_address):
"""Start a new thread to process the request."""
self.do_job_in_thread(self.do_work, (request, client_address))
参考资料¶
Python 语言参考:数据模型。
Python 版本:Python 2.3 方法解析顺序。
维基百科:C3 线性化。
维基百科:组合优于继承。