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

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

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

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)
blank

相关推荐

  • hibernate和hibernation_unique sql

    hibernate和hibernation_unique sql Useruser=(Users)query.uniqueResult();//如果有多个值抛错;//如果有值且只有一个,返回一个object;//如果没值,返回null

  • IIS http 500 内部服务器错误 (解决方案)「建议收藏」

    IIS http 500 内部服务器错误 (解决方案)「建议收藏」 第一步:“开始”->“设置”->“控制面板”->“管理工具”->“组件服务”中,“控制台根目录”->“组件服务”->“计算机”->“我的电脑”->“COM+应用程序”中,有一个“IISOut-Of-ProcessPooled”鼠标右键“属性”–“标识”–把“此用户”调整为“交互式用户–目前已登录的用户”。然后“确定”,再鼠标右键“属性”–“启动”.第二步:“开始”->“管理工具”-

  • AOP实现原理详解[通俗易懂]

    AOP实现原理详解[通俗易懂]转载地址:https://my.oschina.net/elain/blog/382494一、什么是AOPAOP(Aspect-OrientedProgramming,面向切面编程),可以说是OOP(Object-OrientedPrograming,面向对象编程)的补充和完善。OOP引入封装、继承和多态性等概念来建立一种对象层次结构,用以模拟公共行为的一个集合。当我们需要为分散的对象引入…

    2022年10月30日
  • Android startActivityForResult用法

    Android startActivityForResult用法一、介绍当我们在第一个Activity打开第二个Activity时,第二个Activity关闭并想返回数据给第一个Activity时,我们就可以使用startActivityForResult进行传值。用到的几个方法介绍:1.startActivityForResult(Intentintent,intrequestCode)requestCode:如果>=…

  • Android 再次探究Fragment在各种情况下的onResume与onPause

    Android 再次探究Fragment在各种情况下的onResume与onPause之前写过一篇关于Fragment真正的onResume与onPause的文章,但是当时写的比较匆忙,并不是很严谨,导致问题多多,今天抽空更新下关于fragment在各种情况下的onResume与onPause的方法,首先附一张fragment的生命周期图:1.fragment通过replace方式使用fragment在该方式中使用,使用周期基本与图上的生命周期一样,onResume与onP…

  • 自我介绍

    自我介绍

发表回复

您的电子邮箱地址不会被公开。

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