如下,我们已经有了一个从Contact类继承过来的Friend类

class ContactList(list):
def search(self, name):
'''Return all contacts that contain the search value
in their name.'''
matching_contacts = []
for contact in self:
if name in contact.name:
matching_contacts.append(contact)
return matching_contacts class Contact:
all_contacts = ContactList() def __init__(self, name, email):
self.name = name
self.email = email
Contact.all_contacts.append(self) class Friend(Contact):
'''通过super得到父类对象的实例,并且调用这个对象的__init__方法,
传递给它预期的参数,然后这个类做了自己的初始化,即设置phone属性'''
def __init__(self, name, email, phone):
super().__init__(name, email)
self.phone = phone

如果要给Friend类增加一个住址的方法,住址信息包括街道、城市、国家等。我们可以把这些字符串直接传递给Friend中的__init__方法,另外也可以把这些字符串先存放在一个元组或者字典里面,然后再把他作为单一的参数传递给__init__方法。

另一种方法就是,创建一个新的Address类来专门包括这些字符串,并且把这个类的一个实例传给Friend类的__init__方法。这样做的好处是在其他的如建筑、商业、组织中重用这个Address类。

class AddressHolder:
def __init__(self, street, city, state, code):
self.street = street
self.city = city
self.state = state
self.code = code

现在问题来了,在已经存在的从Contact类继承过来的Friend类中如何增加一个住址。

最好的方法是多重继承,但是这样会有两个父类的__init__方法需要被初始化,并且他们要通过不同的参数进行初始化,如何来做呢?让我们从一个天真的方法开始,对上述代码的Friend进行改写:

class Friend(Contact, AddressHolder):
def __init__(self, name, email, phone, street, city, state, code):
Contact.__init__(self, name, email)
AddressHolder.__init__(self, street, city, state, code)
self.phone = phone

上述从技术层面上是可以工作的,但是存在一些问题。

首先,如果我们忽略显式地调用初始化函数可能会导致一个超类未被初始化。在这里并不明显,但是在另一些场景会导致程序崩溃,比如把数据插入到一个未连接的数据库里。

第二,由于这些类的层次结果,可能会导致某个超类被调用多次。如下图所示。

从上图中,Friend中的__init__首先调用了Contact中的__init__,隐私初始化了object(所有类都继承于object)。Friend然后又调用AddressHolder的__init__,又一次隐式初始化了object超类,父类被创建了两次。在我们的这个情况下,它是无害的,但是在一些场景中,会带来灾难。(每一个方法的调用顺序可以通过__mro__修改,这里略)

-------------------------------

在如上Friend多重继承的例子中,直接调用了两个父类的__init__方法:

        Contact.__init__(self, name, email)
AddressHolder.__init__(self, street, city, state, code)

但是如何变成了使用super的模式呢?这里需要super能够将参数传递给Contact.__init__方法,同时也需要将参数传递给下一个方法,也就是AddressHolder.__init__。

如下是Friend多重继承代码的正确版本:

class Contact:
all_contacts = [] def __init__(self, name = '', email = '', **kwargs):
super().__init__(**
kwargs)
self.name = name
self.email = email
self.all_contacts.append(self) class AddressHolder:
def __init__(self, street = '',city = '', state = '', code = '',
**kwargs):
super().__init__(**
kwargs)
self.street = street
self.city = city
self.state = state
self.code = code class Friend(Contact, AddressHolder):
def __init__(self, phone = '', **kwargs):
super().__init__(**
kwargs)
self.phone = phone

通过设置空字符串为参数默认值,我们已经把所有的参数编程了关键字参数。这里包含了一个**kwargs参数,它可以捕获任何特殊方法不知道如何处理的额外参数。通过调用super方法,它把参数传递给了下一个类。

**kwargs主要是收集任何传递到方法但是没有在参数列表中显式列出的关键字参数。这些参数会被存于一个叫kwargs(可以随意称呼这个参数,但通常叫kw或者kwargs)的字典里。当我们调用一个携带**kwargs语法的不同方法(例如super.__init__),它会打开这个字典并且把结果以标准关键字参数的形式传给这个方法。

如果我们想要在父类中“重用”这个变量,这种实现方式甚至可能是不够的。当我们传递**kwargs变量给super,这个字典并不包括任何包含在显式关键字参数中的变量。例如,在Friend.__init__方法里,调用super方法并没有在kwargs字典里包含phone参数。如何任何其他类需要phone参数,我们需要保证它在传递的这个字典里。如果我们忘记这么做,这将很难调试,因为超类将不会报错。但是会简单的给这个变量赋一个默认值(本例中是一个空字符串)。

这里有一些方法来保证向上传递的变量。例如Contact这个类,处于某种原因常需要在初始化的时候携带一个电话号码的参数,同时Friend类也需要访问它。我们可以做如下的事情:

1、不要把phone包含在显式关键字参数里。想法,把它放在kwargs字典里。Friend类可以通过kwargs['phone']语法查找它。当它吧**kwargs传递给super调用时,phone参数也会包含在这个字典里。

2、让phone作为显式关键字参数,但是在把它传给super之前,使用标准的字典语法kwargs['phone']=phone来更新kwargs字典

3、让phone作为显式关键字参数,但是使用kwargs.update方法更新kwargs字典。如果你有多个参数需要更新,这种方法是很有帮助的。可以使用dict(phone = phone)构造函数或者使用字典的语法{'phone':phone}来创建一个字典,并作为参数传递给update调用。

4、让phone作为显式关键字参数,但是通过语法super().__init__(phone=phone, **kwargs)显式地把他传给super调用

到这里我们已经介绍了phone涉及多重继承的多项注意事项,当我们需要考虑所有的情况时,我们应该做个计划,不然代码会变得很乱。

参考:

1、《Python3 面向对象编程》 [加]Dusty Philips 著

python对象的不同参数集合的更多相关文章

  1. C#语法糖之第二篇: 参数默认值和命名参数 对象初始化器与集合初始化器

    今天继续写上一篇文章C#4.0语法糖之第二篇,在开始今天的文章之前感谢各位园友的支持,通过昨天写的文章,今天有很多园友们也提出了文章中的一些不足,再次感谢这些关心我的园友,在以后些文章的过程中不断的完 ...

  2. Python中为什么不能用可变对象作为默认参数的值

    def func(numbers = [], num=1): numbers.append(num) for number in numbers: print(number) func() >& ...

  3. Python函数中的参数(一)

    函数传递参数时的简要关键点: 1.参数的传递是通过自动将对象赋值给本地变量名来实现的.函数参数在实际中只是Python赋值的一个实例.因为引用是以指针的形式实现的,所有的参数实际上都是通过指针进行传递 ...

  4. Python:映像、集合

    一.字典 字典(dictionary)是Python中唯一的“映射”类型,映射这个概念在高中就学过:一个函数f将键(key, 定义域)映射到值(value, 值域).这样的函数在字典中可以称为哈希(H ...

  5. python学习笔记:python对象

    一.python对象 python使用对象模型来存储数据,构造任何类型的值都是一个对象.所有的python对象都拥有三个特性:身份.类型和值. 身份:每个对象都有一个唯一的身份标识自己,对象的身份可以 ...

  6. Python数据类型(字典和集合)

    1.5 Dictionary(字典) 在Python中,字典用放在花括号{}中一系列键-值对表示.键和值之间用冒号分隔,键-值对之间用逗号分隔. 在字典中,你想存储多少个键-值对都可以.每个键都与一个 ...

  7. 十:python 对象类型详解六:文件

    一:文件 1.简介:内置open 函数会创建一个python 文件对象,可以作为计算机上的一个文件链接.在调用open 之后,可以通过调用返回文件对象的方法来读写相关外部文件.文件对象只是常见文件处理 ...

  8. 四:python 对象类型详解一:数字(下)

    一:位操作 除了一般的数学运算,python也支持c语言中的大多数数学表达式.这包括那些把整数当作二进制位串对待的操作.例如,还可以实现位移及布尔操作: >>> x = 1 > ...

  9. Python基础--文件操作和集合

    这篇博客来说一下python对文件的操作. 对文件的操作分三步: 1.打开文件获取文件的句柄,句柄就理解为这个文件 2.通过文件句柄操作文件 3.关闭文件. 现有以下文件file.txt: 我们哭了 ...

随机推荐

  1. c++创建文件夹以及子文件夹

    #ifdef WIN32 #include <io.h> #include <direct.h> #else #include <unistd.h> #includ ...

  2. c++ 怎么输出保留2位小数的浮点数

    //添加头文件 #include<iomanip> //定义变量 folat a=9.1; cout<<setiosflags(ios::fixed)<<setpr ...

  3. pycharm2019+破解补丁

    事先声明,由于本人的pycharm已经装好,故部分图片来自网络,转载请注明地址 这篇博文以前是2018版本 后来换电脑重装时发现失效了 索性装的2019版本 具体步骤可以参考下面的删除线,新的补丁和激 ...

  4. HDU5769-Substring-多校#4-1006-后缀数组

    给定一个字符x和一个字符串.要求输出包含此字符的所有不同字串. 后缀数组可以计算一个字符串的所有不同字串,理解了原理就能做这题了. 对于每一个后缀i,将产生len-sa[i]-hight[i]的前缀, ...

  5. 查询SQLSERVER中系统所有表

    SQL 查询所有表名: SELECT NAME FROM SYSOBJECTS WHERE TYPE='U' SELECT * FROM INFORMATION_SCHEMA.TABLES 查询表的所 ...

  6. SpringBoot整合ssm

    1.创建工程 使用idea可以快速创建SpringBoot的工程 这里选择常用的类库,SpringBoot将各种框架类库都进行了封装,可以减少pom文件中的引用配置: 比如Spring和Mybatis ...

  7. restTemplate 发送http post请求带有文件流、参数

    String httpMethod = ""; RestTemplate restTemplate = new RestTemplate(); String args = &quo ...

  8. 【XSY2731】Div 数论 杜教筛 莫比乌斯反演

    题目大意 定义复数\(a+bi\)为整数\(k\)的约数,当且仅当\(a\)和\(b\)为整数且存在整数\(c\)和\(d\)满足\((a+bi)(c+di)=k\). 定义复数\(a+bi\)的实部 ...

  9. bzoj 2653: middle (主席树+二分)

    2653: middle Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 2522  Solved: 1434[Submit][Status][Disc ...

  10. 【AtCoder078D】Fennec VS. Snuke

    AtCoder Regular Contest 078 D - Fennec VS. Snuke 题意 给一个树,1是白色,n是黑色,其它没有颜色.Fennec每次可以染白色点的直接邻居为白色.Snu ...