python对象的不同参数集合
如下,我们已经有了一个从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对象的不同参数集合的更多相关文章
- C#语法糖之第二篇: 参数默认值和命名参数 对象初始化器与集合初始化器
今天继续写上一篇文章C#4.0语法糖之第二篇,在开始今天的文章之前感谢各位园友的支持,通过昨天写的文章,今天有很多园友们也提出了文章中的一些不足,再次感谢这些关心我的园友,在以后些文章的过程中不断的完 ...
- Python中为什么不能用可变对象作为默认参数的值
def func(numbers = [], num=1): numbers.append(num) for number in numbers: print(number) func() >& ...
- Python函数中的参数(一)
函数传递参数时的简要关键点: 1.参数的传递是通过自动将对象赋值给本地变量名来实现的.函数参数在实际中只是Python赋值的一个实例.因为引用是以指针的形式实现的,所有的参数实际上都是通过指针进行传递 ...
- Python:映像、集合
一.字典 字典(dictionary)是Python中唯一的“映射”类型,映射这个概念在高中就学过:一个函数f将键(key, 定义域)映射到值(value, 值域).这样的函数在字典中可以称为哈希(H ...
- python学习笔记:python对象
一.python对象 python使用对象模型来存储数据,构造任何类型的值都是一个对象.所有的python对象都拥有三个特性:身份.类型和值. 身份:每个对象都有一个唯一的身份标识自己,对象的身份可以 ...
- Python数据类型(字典和集合)
1.5 Dictionary(字典) 在Python中,字典用放在花括号{}中一系列键-值对表示.键和值之间用冒号分隔,键-值对之间用逗号分隔. 在字典中,你想存储多少个键-值对都可以.每个键都与一个 ...
- 十:python 对象类型详解六:文件
一:文件 1.简介:内置open 函数会创建一个python 文件对象,可以作为计算机上的一个文件链接.在调用open 之后,可以通过调用返回文件对象的方法来读写相关外部文件.文件对象只是常见文件处理 ...
- 四:python 对象类型详解一:数字(下)
一:位操作 除了一般的数学运算,python也支持c语言中的大多数数学表达式.这包括那些把整数当作二进制位串对待的操作.例如,还可以实现位移及布尔操作: >>> x = 1 > ...
- Python基础--文件操作和集合
这篇博客来说一下python对文件的操作. 对文件的操作分三步: 1.打开文件获取文件的句柄,句柄就理解为这个文件 2.通过文件句柄操作文件 3.关闭文件. 现有以下文件file.txt: 我们哭了 ...
随机推荐
- codeforces525B
Pasha and String CodeForces - 525B Pasha got a very beautiful string s for his birthday, the string ...
- python 脚本之 IP地址探测
#第一种方法#!/usr/bin/env python #_*_ coding:utf8 _*_ #### 该脚本需要使用fping命令 如果没有安装需要提前安装fping #### yum inst ...
- Web.xml中Filter过滤器标签几个说明
在研究liferay框架中看到Web.xml中加入了过滤器的标签,可以根据页面提交的URL地址进行过滤,发现有几个新标签没用过,下面就介绍以下几个过滤器的标签用法: <!-- 定义Filter ...
- wpgwhpg
//f[i][j]就是第is时wpgwhpg的疲劳度是j,那么我们就可以就ta这1s是否休息进行讨论 #include<bits/stdc++.h> using namespace std ...
- Json.net 反序列化 部分对象
主要通过 Jobject获取想要序列化的部分对象. 直接上代码 static void Main(string[] args) { //先反序列化看看 string json = "{\&q ...
- 洛谷P2722总分题解
题目 这个题是一个裸的完全背包问题,但是数组需要开大, 代码 #include<iostream> using namespace std; int n,m,v,i; int c[1000 ...
- 【BZOJ1580】【USACO2009Hol】杀手游戏 计算几何
题目描述 一个平面上有很多个点在运动.给你每个点的初始坐标和每个点的速度,求出最多有多少个点到\(0\)号店的距离同时不超过\(r\). \(n\leq 50000\) 题解 我们先把\(0\)号点平 ...
- SSL加速卡调研的原因及背景
SSL加速卡调研的原因及背景 SSL加速卡调研的原因及背景 网络信息安全已经成为电子商务和网络信息业发展的一个瓶颈,安全套接层(SSL)协议能较好地解决安全处理问题,而SSL加速器有效地提高了网络安全 ...
- BZOJ 5477: 星际穿越
当初随便出的一道 思博题 竟然被交换到了八中 QAQ 然后就上了 BZOJ ...作为原作者还是把原来写的详细题解放出来吧 qwq 题意 \(n\) 个点的数,每个点初始有权值 \(v_i\) ,需要 ...
- CF1137C Museums Tour(Tarjan,强连通分量)
好题,神题. 题目链接:CF原网 洛谷 题目大意: 一个国家有 $n$ 个城市,$m$ 条有向道路组成.在这个国家一个星期有 $d$ 天,每个城市有一个博物馆. 有个旅行团在城市 $1$ 出发,当天是 ...