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)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • 一阶惯性滤波特点_一阶惯性环节仿真

    一阶惯性滤波特点_一阶惯性环节仿真一阶惯性环节采用后置反馈的方式可以实现较精确的系统跟踪性能。上述系统的传递函数为因此启动性能良好,另,一阶惯性环节无超调量,因此可通过修改反馈参数实现最优的跟踪性能。因此在针对温度等变化较小的物理量方面的控制上是较占优势的,但精确跟踪也就意味着出现高频干扰、低频干扰、白噪声时,传感器也会精确地将这些干扰输出。这对一些容易受到干扰的系统是极为不利的。如下图为加入高频正弦信号后上述系统的输出(幅值为1,频率为1000(rad/sec))可见,系统虽然有一定的滤波性能(正弦输出

    2022年10月4日
    2
  • mips交叉编译 error: ‘PTHREAD_MUTEX_ERRORCHECK_NP’ was not declared in this scope

    mips交叉编译 error: ‘PTHREAD_MUTEX_ERRORCHECK_NP’ was not declared in this scope

    2022年3月11日
    50
  • python转置矩阵函数_对python 矩阵转置transpose的实例讲解

    python转置矩阵函数_对python 矩阵转置transpose的实例讲解在读图片时,会用到这么的一段代码:image_vector_len=np.prod(image_size)#总元素大小,3*55*47img=Image.open(path)arr_img=np.asarray(img,dtype=’float64′)arr_img=arr_img.transpose(2,0,1).reshape((image_vector_len,))#4…

    2022年5月5日
    48
  • 自学cad 零基础_零基础自学吉他的步骤

    自学cad 零基础_零基础自学吉他的步骤学习CAD制图其实不难,主要还是看个人,下面是学习啦小编带来关于cad的零基础自学教程的内容,希望可以让大家有所收获! CAD的零基础自学教程  自学cad制图第1章基础  1.设备绘图界限一般来说,如果用户不作任何设置,系统对作图范围没有限制,可以将绘图区看作是一幅无穷大的图纸。格式-图形界线命令:limits出现四个选项:开、关、指定左下角点、指定右下角点。高手课专注帮你学习。  2.设备绘图单位格式-单位命令:ddunits长度、角度、插入比例、方向…

    2025年8月30日
    6
  • Tomcat常见问题解决 – The web application appears to have started a thread named [FileWatchdog]

    Tomcat常见问题解决 – The web application appears to have started a thread named [FileWatchdog]Tomcat常见问题解决 – The web application appears to have started a thread named [FileWatchdog]

    2022年4月23日
    52
  • pytorch中tensor转numpy

    pytorch中tensor转numpycputensor转numpy:#假定a为tensora.numpy()gputensor转numpy:gpu下的tensor不能直接转numpy,需要先转到cputensor后再转为numpya.cpu().numpy()注:若tensor带有梯度,以上述方式转换时会报错:RuntimeError:Can’tcallnumpy()onTensorthatrequiresgrad.Usetensor.detach().numpy()instead.

    2022年10月19日
    3

发表回复

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

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