python对象的不同参数集合「建议收藏」

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 

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

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

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

        python对象的不同参数集合「建议收藏」

从上图中,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 著

转载于:https://www.cnblogs.com/anovana/p/8192610.html

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/107779.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • 测试用例等价类和边界值_等价类划分和边界值的区别与联系

    测试用例等价类和边界值_等价类划分和边界值的区别与联系一、测试用例/案例(testcase/testinstance)  1、定义:是在测试执行之前,由测试人员编写的指导测试过程的重要文档,主要包括:用例编号、测试目的、测试步骤(用例描述),预期结果  2、介绍编写测试用例的7种方法:    1)等价类划分法()    2)边界值法()    3)因果图法    4)判定表法    5)正交排列法    6)测试大纲法    7)场景法(*****)  至少要掌握每种方法的适用场合(用在哪)和使用步骤(怎么用)  编写测试用例可以参考

    2022年10月10日
    4
  • 十天学会php第五天

    十天学会php第五天

    2022年1月11日
    42
  • 喉头梗阻的常见原因有哪六项_晕针的原因症状处理及预防

    喉头梗阻的常见原因有哪六项_晕针的原因症状处理及预防点击上方”IT牧场”,选择”设为星标”技术干货每日送达!每一个JVM线程都拥有一个私有的JVM线程栈,用于存放当前线程的JVM栈帧(包括被调用函数的参数、局部…

    2025年7月24日
    4
  • 1.巴特沃斯模拟滤波器(低通,高通,带通,带阻)设计-MATLAB实现

    1.巴特沃斯模拟滤波器(低通,高通,带通,带阻)设计-MATLAB实现1.基础知识介绍我们首先明确一个知识(这个非常重要):某正弦信号,频率为50Hz这意味着信号的模拟频率fff=50(Hz),注意它的单位是Hz信号的表达式为y=sin(2πft)=sin(2π∗50t)=sin(100πt)y=sin(2\pift)=sin(2\pi*50t)=sin(100\pit)y=sin(2πft)=sin(2π∗50t)=sin(100πt)由于信号也可以表示为y=sin(Ωt)y=sin(\Omegat)y=sin(Ωt)的形式,所以这里

    2022年5月16日
    768
  • pycharm怎么打开工具栏_pycharm菜单栏介绍

    pycharm怎么打开工具栏_pycharm菜单栏介绍  

    2022年8月26日
    7
  • SQL Server2008安装详细教程[通俗易懂]

    SQL Server2008安装详细教程[通俗易懂]1.将光盘文件解压成文件夹格式,(解压过程比较慢,请耐心等待);2.打开开始菜单的设置;3.打开设置后,点击更新和安全,然后进入;4.在Windows安全中心,将其关闭(注意我这里已经关闭了);5.然后再到安装包文件夹目录,找到setup.exe文件,右击,以管理员身份运行;6.右击运行后,会出来这个页面(如果没有出现这个页面,请直接跳转至第14步),然后点击下载并安装此功能,进入下一步;7.进入下一个页面后,你会发现它会出来一个正在下载所需的文件的页面,然后等待就行;8

    2022年6月23日
    52

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号