背景

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

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

先说结论

  • 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. 线上FullGC问题排查实践——手把手教你排查线上问题

    作者:京东科技 韩国凯 一.问题发现与排查 1.1 找到问题原因 问题起因是我们收到了jdos的容器CPU告警,CPU使用率已经达到104% 观察该机器日志发现,此时有很多线程在执行跑批任务.正常来说 ...

  2. JavaScript中的四种枚举方式

    字符串和数字具有无数个值,而其他类型如布尔值则是有限的集合. 一周的日子(星期一,星期二,...,星期日),一年的季节(冬季,春季,夏季,秋季)和基本方向(北,东,南,西)都是具有有限值集合的例子. ...

  3. UDP内核发包流程

    背景 工作中遇到客户反馈,上层应用UDP固定间隔100ms发包,但本地tcpdump抓包存在波动,有的数据包之间间隔107ms甚至更多,以此重新梳理了下udp的发送流程. udp发包流程 udp_se ...

  4. BS系统自动更新的实现

    背景: 我公司做的考试系统(基于java开发的BS系统)是卖给学校的,随着客户数量增多,日常版本升级.远程维护工作占了程序员很多时间,遂考虑实现系统自动化更新. 要解决的问题及解决方案: 1.什么时候 ...

  5. 2022-08-31:以下go语言代码输出什么?A:江苏;B:v[“province“]取值错误;C:m.Store存储错误;D:不知道。 package main import ( “fm

    2022-08-31:以下go语言代码输出什么?A:江苏:B:v["province"]取值错误:C:m.Store存储错误:D:不知道. package main import ...

  6. 2022-02-03:有一队人(两人或以上)想要在一个地方碰面,他们希望能够最小化他们的总行走距离。 给你一个 2D 网格,其中各个格子内的值要么是 0,要么是

    2022-02-03:最佳的碰头地点. 有一队人(两人或以上)想要在一个地方碰面,他们希望能够最小化他们的总行走距离. 给你一个 2D 网格,其中各个格子内的值要么是 0,要么是 1. 1 表示某个人 ...

  7. 【CF】Round #618 div2 C(文末有技巧)

    目录 round #618 div2 C(文末有技巧) 题目: 样例输入输出 思路 小技巧(拿小本本记下来) round #618 div2 C(文末有技巧) 这是题目链接->链接 题目: 原题 ...

  8. 详解RocketMQ 顺序消费机制

    摘要:顺序消息是指对于一个指定的 Topic ,消息严格按照先进先出(FIFO)的原则进行消息发布和消费,即先发布的消息先消费,后发布的消息后消费. 本文分享自华为云社区<RocketMQ 顺序 ...

  9. .NET周报 【5月第4期 2023-05-27】

    国内文章 C#使用词嵌入向量与向量数据库为大语言模型(LLM)赋能长期记忆实现私域问答机器人落地之openai接口平替 https://www.cnblogs.com/gmmy/p/17430613. ...

  10. mysql和neo4j集成多数据源和事务

    在微服务大行其道的今天,按理说不应该有多数据源这种问题(嗯,主从库算是一个多数据源的很常见的场景.),但是也没人规定不能这样做. 就算有人规定的,曾经被奉为圭臬的数据库三大范式现在被宽表冲得七零八落, ...