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)
上一篇 2022年3月12日 下午9:35
下一篇 2022年3月12日 下午10:00


相关推荐

  • jetbrains golang 2021 激活码【永久激活】[通俗易懂]

    (jetbrains golang 2021 激活码)2021最新分享一个能用的的激活码出来,希望能帮到需要激活的朋友。目前这个是能用的,但是用的人多了之后也会失效,会不定时更新的,大家持续关注此网站~https://javaforall.net/100143.htmlIntelliJ2021最新激活注册码,破解教程可免费永久激活,亲测有效,上面是详细链接哦~ML…

    2022年3月21日
    341
  • js如何生成二维码_jquery 生成二维码无法识别

    js如何生成二维码_jquery 生成二维码无法识别前言最近根据自己的需求寻找到了一款简单易用的生成二维码插件,特此分享一、使用步骤1.下载插件npmiqrcodejs2或yarnaddqrcodejs2二、vue中使用1.vue2<template><div><divref=”qrcodeDom”></div><button@click=”show”>展示二维码</button><bu.

    2022年10月17日
    4
  • 浅谈Vue响应式原理

    浅谈Vue响应式原理一、Vue中的响应式Vue最独特的特性之一,是其非侵入性的响应式系统。数据模型仅仅是普通的JavaScript对象。而当你修改它们时,视图会进行更新。二、响应式的基本原理1.通过Object.definePropert来实现监听数据的改变和读取(属性中的getter和setter方法)实现数据劫持2.观察者模式(发布者-订阅者)观察者(订阅者)–Watcher:update():当事件发生时,具体要做的事情目标(发布者)–Dep:①subs数组:存储所有的观察者②

    2022年6月13日
    30
  • python pymssql_Python模块-pymssql[通俗易懂]

    python pymssql_Python模块-pymssql[通俗易懂]Python默认的数据库是SQLlite,不过它对MySql以及SQLserver的支持也可以。如果想链接操作SQLserver,需使用第三方包pymssqlpymssql是一个Python的数据库接口,基于FreeTDS构建,对_mssql模块进行了封装,遵循Python的DBAPI规范,而FreeTDS是一个C语言连接sqlserver的公共开源库工作原理使用connect创建连接对象;…

    2022年6月16日
    44
  • 华为静态路由配置[通俗易懂]

    华为静态路由配置[通俗易懂]静态路由静态路由是由管理员手动配置和管理的路由,静态路由配置简单,因此,被广泛应用于结构简单网络中,静态路由还可以实现负载均衡与路由备份。拓扑PC1位于192.168.1.0网段,它的IP是192.168.1.2,网关是192.168.1.1,掩码255.255.255.0。PC2位于192.168.2.0网段,它的IP是192.168.2.2,网关是192.168.2.1,掩码255….

    2026年3月11日
    4
  • 知识图谱构建全流程

    知识图谱构建全流程知识图谱 是结构化的语义知识库 用于迅速描述物理世界中的概念及其相互关系 通过知识图谱能够将 Web 上的信息 数据以及链接关系聚集为知识 使信息资源更易于计算 理解以及评价 并能实现知识的快速响应和推理 当下知识图谱已在工业领域得到了广泛应用 如搜索领域的 Google 搜索 百度搜索 社交领域的领英经济图谱 企业信息领域的天眼查企业图谱 电商领域的淘宝商品图谱 O2O 领域的美团知识大脑 医疗领域的丁香园知识图谱 以及工业制造业知识图谱等 在知识图谱技术发展初期 很多企业和科研机构会采用自顶向下的方式构建基础知识

    2026年3月19日
    2

发表回复

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

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