背景

  • 有场景分别定义两组逻辑,随后有统一入口做基类属性的整合
  • 其中两组逻辑的积累构造函数定义入参不同
  • 设计类继承图如:

  • 实际的使用方式抽象为[使用] 小节
  • 实际开发过程中遇到问题

先说结论

  • python 多继承,需要使用super函数进行MRO的依次不重复初始化
  • python 多继承的情况下,构造函数__init__会被依次调用并传递参数
  • python 多继承情况下,__init__参数需要保持一致,否则会出现某些继承路径上的基类初始化遇到异常
  • python 多继承情况下,若构造函数参数不一致,可通过(*args, **kwargs)来统一
  • python 多继承情况下,若有公共基类,MRO可被调整为有跳跃路径,进而利用子类不同的构造函数完成正常初始化,但需要临近基类可以处理子类传来的所有参数。

使用

    # tjc = TestJobConfiger()
tjc = SubTest()
print vars(tjc)
print "\n".join([tjc.base_key,
tjc.base1_key,
tjc.subbase_key,
tjc.subbase1_key,
tjc.subtest_key])

期望结果

enter Base
enter SubBase
enter Base1|_arg1 |_arg2
enter SubBase1|_arg1 |_arg2
vars : {'base1_key': 'base1_key', 'subtest_key': 'subtest_key', 'subbase1_key': 'subbase1_key', 'subbase_key': 'subbase_key', 'base_key': 'base_key'}
values: base_key
base1_key
subbase_key
subbase1_key
subtest_key

第一版实现

class Base(object):
def __init__(self):
print "enter Base"
self.base_key = "base_key" class Base1(object):
def __init__(self, _arg1 = "_arg1 ", _arg2 = "_arg2 "):
print "enter Base1" + "|" + _arg1 + "|" + _arg2
self.base1_key = "base1_key" class SubBase(Base):
def __init__(self):
super(SubBase, self).__init__()
print "enter SubBase"
self.subbase_key = "subbase_key" class SubBase1(Base1):
def __init__(self, _arg1 = "_arg1 ", _arg2 = "_arg2 "):
super(SubBase1, self).__init__(_arg1 = _arg1, _arg2=_arg2)
print "enter SubBase1" + "|" + _arg1 + "|" + _arg2
self.subbase1_key = "subbase1_key" class SubTest(SubBase,SubBase1):
def __init__(self, _arg1 = "_arg1 "):
super(SubTest, self).__init__(_arg1=_arg1, _arg2="None")
# self.__dict__.update(vars(SubBase()))
self.subtest_key = "subtest_key"
  • 运行结果为:
Traceback (most recent call last):
File "/Users/enzhao/suanec/ksp/dispatch/weiclient/client/weiclient/libs/com/weibo/tools/job_manager/job_configer_tester.py", line 43, in <module>
tjc = SubTest()
File "/Users/enzhao/suanec/ksp/dispatch/weiclient/client/weiclient/libs/com/weibo/tools/job_manager/job_configer_tester.py", line 38, in __init__
super(SubTest, self).__init__(_arg1=_arg1, _arg2="None")
TypeError: __init__() got an unexpected keyword argument '_arg1'

第二版实现

class Base(object):
def __init__(self):
print "enter Base"
self.base_key = "base_key" class Base1(object):
def __init__(self, _arg1 = "_arg1 ", _arg2 = "_arg2 "):
print "enter Base1" + "|" + _arg1 + "|" + _arg2
self.base1_key = "base1_key" class SubBase(Base):
def __init__(self, **kwargs):
super(SubBase, self).__init__()
print "enter SubBase"
self.subbase_key = "subbase_key" class SubBase1(Base1):
def __init__(self, _arg1 = "_arg1 ", _arg2 = "_arg2 "):
super(SubBase1, self).__init__(_arg1 = _arg1, _arg2=_arg2)
print "enter SubBase1" + "|" + _arg1 + "|" + _arg2
self.subbase1_key = "subbase1_key" class SubTest(SubBase,SubBase1):
def __init__(self, _arg1 = "_arg1 "):
super(SubTest, self).__init__(_arg1=_arg1, _arg2="None")
# self.__dict__.update(vars(SubBase()))
self.subtest_key = "subtest_key"
  • 运行结果为:
Traceback (most recent call last):
enter Base
enter SubBase
File "/Users/enzhao/suanec/ksp/dispatch/weiclient/client/weiclient/libs/com/weibo/tools/job_manager/job_configer_tester.py", line 46, in <module>
vars : {'subtest_key': 'subtest_key', 'subbase_key': 'subbase_key', 'base_key': 'base_key'}
tjc.base1_key,
AttributeError: 'SubTest' object has no attribute 'base1_key'

二次继承实现

class Base(object):
def __init__(self):
print "enter Base"
self.base_key = "base_key" class Base1(Base):
def __init__(self, _arg1 = "_arg1 ", _arg2 = "_arg2 "):
super(Base1, self).__init__()
print "enter Base1" + "|" + _arg1 + "|" + _arg2
self.base1_key = "base1_key" class SubBase(Base):
def __init__(self, **kwargs):
super(SubBase, self).__init__()
print "enter SubBase"
self.subbase_key = "subbase_key" class SubBase1(Base1):
def __init__(self, _arg1 = "_arg1 ", _arg2 = "_arg2 "):
super(SubBase1, self).__init__(_arg1 = _arg1, _arg2=_arg2)
print "enter SubBase1" + "|" + _arg1 + "|" + _arg2
self.subbase1_key = "subbase1_key" class SubTest(SubBase,SubBase1):
def __init__(self, _arg1 = "_arg1 "):
super(SubTest, self).__init__(_arg1=_arg1, _arg2="None")
# self.__dict__.update(vars(SubBase()))
self.subtest_key = "subtest_key"
  • 运行结果为:
enter Base
enter Base1|_arg1 |_arg2
enter SubBase1|_arg1 |_arg2
enter SubBase
vars : {'base1_key': 'base1_key', 'subtest_key': 'subtest_key', 'subbase_key': 'subbase_key', 'subbase1_key': 'subbase1_key', 'base_key': 'base_key'}
values: base_key
base1_key
subbase_key
subbase1_key
subtest_key

公共基类实现

class BBase(object):
def __init__(self):
pass class Base(BBase):
def __init__(self):
print "enter Base"
self.base_key = "base_key" class Base1(BBase):
def __init__(self, _arg1 = "_arg1 ", _arg2 = "_arg2 "):
print "enter Base1" + "|" + _arg1 + "|" + _arg2
self.base1_key = "base1_key" class SubBase(Base):
def __init__(self, **kwargs):
super(SubBase, self).__init__()
print "enter SubBase"
self.subbase_key = "subbase_key" class SubBase1(Base1):
def __init__(self, _arg1 = "_arg1 ", _arg2 = "_arg2 "):
super(SubBase1, self).__init__(_arg1 = _arg1, _arg2=_arg2)
print "enter SubBase1" + "|" + _arg1 + "|" + _arg2
self.subbase1_key = "subbase1_key" class SubTest(SubBase,SubBase1):
def __init__(self, _arg1 = "_arg1 "):
super(SubTest, self).__init__(_arg1=_arg1, _arg2="None")
# self.__dict__.update(vars(SubBase()))
self.subtest_key = "subtest_key"
  • 运行结果为:
enter Base
enter SubBase
vars : {'subtest_key': 'subtest_key', 'subbase_key': 'subbase_key', 'base_key': 'base_key'}
Traceback (most recent call last):
File "/Users/enzhao/suanec/ksp/dispatch/weiclient/client/weiclient/libs/com/weibo/tools/job_manager/job_configer_tester.py", line 50, in <module>
tjc.base1_key,
AttributeError: 'SubTest' object has no attribute 'base1_key'

Hack实现

  • 分别保持各自定义逻辑
  • 在入口子类中,声明冲突基类的对象,利用python的vars和__dict__的特性进行属性的声明
  • 相当于手动hard-code 构造函数的调用,完成基类的初始化
class Base(object):
def __init__(self):
print "enter Base"
self.base_key = "base_key" class Base1(object):
def __init__(self, _arg1 = "_arg1 ", _arg2 = "_arg2 "):
print "enter Base1" + "|" + _arg1 + "|" + _arg2
self.base1_key = "base1_key" class SubBase(Base):
def __init__(self):
super(SubBase, self).__init__()
print "enter SubBase"
self.subbase_key = "subbase_key" class SubBase1(Base1):
def __init__(self, _arg1 = "_arg1 ", _arg2 = "_arg2 "):
super(SubBase1, self).__init__(_arg1 = _arg1, _arg2=_arg2)
print "enter SubBase1" + "|" + _arg1 + "|" + _arg2
self.subbase1_key = "subbase1_key" class SubTest(SubBase1):
def __init__(self, _arg1 = "_arg1 "):
super(SubTest, self).__init__(_arg1=_arg1, _arg2="None")
self.__dict__.update(vars(SubBase()))
self.subtest_key = "subtest_key"
  • 运行结果为:
enter Base1|_arg1 |None
enter SubBase1|_arg1 |None
enter Base
enter SubBase
vars : {'base1_key': 'base1_key', 'subtest_key': 'subtest_key', 'subbase_key': 'subbase_key', 'subbase1_key': 'subbase1_key', 'base_key': 'base_key'}
values: base_key
base1_key
subbase_key
subbase1_key
subtest_key

官方建议实现

class A(object):
def __init__(self, *args, **kwargs):
print "A" class B(object):
def __init__(self, *args, **kwargs):
print "B" class C(A):
def __init__(self, arg, *args, **kwargs):
print "C","arg=",arg
super(C, self).__init__(arg, *args, **kwargs) class D(B):
def __init__(self, arg, *args, **kwargs):
print "D", "arg=",arg
super(D, self).__init__(arg, *args, **kwargs) class E(C,D):
def __init__(self, arg, *args, **kwargs):
print "E", "arg=",arg
super(E, self).__init__(arg, *args, **kwargs) print "MRO:", [x.__name__ for x in E.__mro__]
E(10)
MRO: ['E', 'C', 'A', 'D', 'B', 'object']
E arg= 10
C arg= 10
A

结论

  • 放最前面说吧

python-多继承构造函数声明问题的更多相关文章

  1. [py]python的继承体系-源码目录结构

    python3安装目录 pip install virtualenv pip install virtualenvwrapper pip install virtualenvwrapper-win m ...

  2. Python中的构造函数

    Python中的构造函数是__init__函数.在Python中,子类如果定义了构造函数,而没有调用父类的,那么Python不会自动调用,也就是说父类的构造函数不会执行. 比如有test.py的mod ...

  3. C++11中的继承构造函数

    时间:2014.06.19 地点:基地 ------------------------------------------------------------------------- 一.问题描写 ...

  4. c++11 继承构造函数

    若基类拥有数量众多的不同版本的构造函数,而派生类中只有一些成员函数,则对于派生类而言,其构造函数就等同于构造基类. struct A { A(int i) {} A(double d, int i) ...

  5. python类变量与构造函数的使用

    类变量:可在类的所有实例之间共享的变量 实例类对象:类的实例是调用类对象来创建的.如:par = Parent(),par就是类Parent的一个实例类对象. 实例变量(成员变量):同一个类对象可以创 ...

  6. sqlalchemy mark-deleted 和 python 多继承下的方法解析顺序 MRO

    sqlalchemy mark-deleted 和 python 多继承下的方法解析顺序 MRO 今天在弄一个 sqlalchemy 的数据库基类的时候,遇到了跟多继承相关的一个小问题,因此顺便看了一 ...

  7. python基础——继承和多态

    python基础——继承和多态 在OOP程序设计中,当我们定义一个class的时候,可以从某个现有的class继承,新的class称为子类(Subclass),而被继承的class称为基类.父类或超类 ...

  8. js最好的继承机制:用对象冒充继承构造函数的属性,用原型prototype继承对象的方法。

    js最好的继承机制:用对象冒充继承构造函数的属性,用原型prototype继承对象的方法. function ClassA(sColor) { this.color = sColor; } Class ...

  9. [修]python普通继承方式和super继承方式

    [转]python普通继承方式和super继承方式 原文出自:http://www.360doc.com/content/13/0306/15/9934052_269664772.shtml 原文的错 ...

  10. python中的编码声明

    python中的第一行,目的就是指出,你想要你的这个文件中的代码用什么可执行程序去运行它,就这么简单 #!/usr/bin/python 相当于写死了 python 路径(是告诉OS执行这个py时,调 ...

随机推荐

  1. 「学习笔记」AC 自动机

    「学习笔记」AC 自动机 点击查看目录 目录 「学习笔记」AC 自动机 算法 问题 思路 代码 例题 Keywords Search 玄武密码 单词 病毒 最短母串 文本生成器 背单词 密码 禁忌 前 ...

  2. 如何将c#在线编辑器嵌入自己项目

    如何将c#在线编辑器嵌入自己项目 首先我们需要介绍一下这个在线编辑器,当前的在线编辑器支持c#的代码编译运行,并且无需后台服务,基于WebAssembly实现的在浏览器端去执行我们的c#代码,基于Ro ...

  3. 2021-12-04:公交路线。给你一个数组 routes ,表示一系列公交线路,其中每个 routes[i] 表示一条公交线路,第 i 辆公交车将会在上面循环行驶。 例如,路线 routes[0]

    2021-12-04:公交路线.给你一个数组 routes ,表示一系列公交线路,其中每个 routes[i] 表示一条公交线路,第 i 辆公交车将会在上面循环行驶. 例如,路线 routes[0] ...

  4. jmeter如何保存变量到结果jtl文件里

    将变量保存到结果jtl文件里,可以方便的在generate报告时,自行取用jtl中的变量进行展示,实现过程如下: 1.打开jmeter/bin目录下的jmeter.properties文件,将变量名加 ...

  5. List的拆分的几种方式

    开发中我们可能会遇到一个大的集合,然后我们需要对集合进行拆分,然后再对拆分的集合进行相关的操作.当然我们可以自己写一个拆分的方法,我自己写过用了不少代码,但是感觉还不是很好,最近看了不少工具才发现很多 ...

  6. 03. 选择器补充及CSS动画

    1.了解frameset 2.iframe html5新特性 iframe 元素会创建包含另外一个文档的内联框架(即行内框架). 提示:您可以把需要的文本放置在 <iframe> 和 &l ...

  7. Python 列表推导式:简洁、高效的数据操作艺术

    Python 列表推导式:简洁.高效的数据操作艺术 Python 的列表推导式,这个看似简单的语法糖,实则内含无限威力.在 Python 代码编写中,列表推导式的灵活性和简洁性让它成为了不可或缺的一部 ...

  8. 案例实践 | 某能源企业API安全实践

    随着智能电网.全球能源互联网."互联网+电力".新电改的全面实施,分布式能源.新能源.电力交易.智能用电等新型业务不断涌现,运营模式.用户群体都将发生较大变化,电力市场由相对专业向 ...

  9. Vue_Django 登录注册+图书管理系统

    Vue前端 注册页面 点击查看代码 <template> <div class="register"> <el-row :gutter="2 ...

  10. Curl 中 关于PUT, POST, DELETE, UPDATE 的使用

    POST curl -H "Content-Type:application/json" -X POST --data '{"id":1, "text ...