详细解读Python中的__init__()方法
作者:young_ipython 发布时间:2023-03-25 17:10:27
__init__()方法意义重大的原因有两个。第一个原因是在对象生命周期中初始化是最重要的一步;每个对象必须正确初始化后才能正常工作。第二个原因是__init__()参数值可以有多种形式。
因为有很多种方式为__init__()提供参数值,对于对象创建有大量的用例,我们可以看看其中的几个。我们想尽可能的弄清楚,因此我们需要定义一个初始化来正确的描述问题区域。
在我们接触__init__()方法之前,无论如何,我们都需要粗略、简单地看看在Python中隐含的object类的层次结构。
在这一章,我们看看不同形式的简单对象的初始化(例如:打牌)。在这之后,我们还可以看看更复杂的对象,就像包含集合的hands对象以及包含策略和状态的players。
隐含的超类——object
每一个Python类都隐含了一个超类:object。它是一个非常简单的类定义,几乎不做任何事情。我们可以创建object的实例,但是我们不能用它做太多,因为许多特殊的方法容易抛出异常。
当我们自定义一个类,object则为超类。下面是一个类定义示例,它使用新的名称简单的继承了object:
class X:
pass
下面是和自定义类的一些交互:
>>> X.__class__
<class 'type'>
>>> X.__class__.__base__
<class 'object'>
我们可以看到该类是type类的一个对象,且它的基类为object。
就像在每个方法中看到的那样,我们也看看从object继承的默认行为。在某些情况下,超类特殊方法的行为是我们所想要的。在其他情况下,我们需要覆盖这个特殊方法。
基类对象的init()方法
对象生命周期的基础是创建、初始化和销毁。我们将创建和销毁的高级特殊方法推迟到后面的章节中,目前只关注初始化。
所有类的超类object,有一个默认包含pass的__init__()实现,我们不需要去实现__init__()。如果不实现它,则在对象创建后就不会创建实例变量。在某些情况下,这种默认行为是可以接受的。
我们总是给对象添加属性,该对象为基类object的子类。思考以下类,需要两个实例变量但不初始化它们:
class Rectangle:
def area(self):
return self.length * self.width
Rectangle类有一个使用两个属性来返回一个值的方法。这些属性没有初始化。这是合法的Python代码。它可以有效的避免专门设置属性,虽然感觉有点奇怪,但是有效。
下面是于Rectangle类的交互:
>>> r = Rectangle()
>>> r.length, r.width = 13, 8
>>> r.area()
104
显然这是合法的,但也是容易混淆的根源,所以也是我们需要避免的原因。
无论如何,这个设计给予了很大的灵活性,这样有时候我们不用在__init__()方法中设置所有属性。至此我们走的很顺利。一个可选属性其实就是一个子类,只是没有真正的正式声明为子类。我们创建多态在某种程度上可能会引起混乱以及if语句的不恰当使用所造成的盘绕。虽然未初始化的属性可能是有用的,但很有可能是糟糕设计的前兆。
《Python之禅》中的建议:
"显式比隐式更好。"
一个__init__()方法应该让实例变量显式。
可怜的多态
灵活和愚蠢就在一念之间。
当我们觉得需要像下面这样写的时候,我们正从灵活的边缘走向愚蠢:
if 'x' in self.__dict__:
或者:
try:
self.x
except AttributeError:
是时候重新考虑API并添加一个通用的方法或属性。重构比添加if语句更明智。
在超类中实现init()
我们通过实现__init__()方法来初始化对象。当一个对象被创建,Python首先创建一个空对象,然后为那个新对象调用__init__()方法。这个方法函数通常用来创建对象的实例变量并执行任何其他一次性处理。
下面是Card类示例定义的层次结构。我们将定义Card超类和三个子类,这三个子类是Card的变种。两个实例变量直接由参数值设置,两个变量通过初始化方法计算:
class Card:
def __init__(self, rank, suit):
self.suit = suit
self.rank = rank
self.hard, self.soft = self._points()
class NumberCard(Card):
def _points(self):
return int(self.rank), int(self.rank)
class AceCard(Card):
def _points(self):
return 1, 11
class FaceCard(Card):
def _points(self):
return 10, 10
在这个示例中,我们提取__init__()方法到超类,这样在Card超类中的通用初始化可以适用于三个子类NumberCard、AceCard和FaceCard。
这是一种常见的多态设计。每一个子类都提供一个唯一的_points()方法实现。所有子类都有相同的签名:有相同的方法和属性。这三个子类的对象在一个应用程序中可以交替使用。
如果我们为花色使用简单的字符,我们可以创建Card实例,如下所示:
cards = [AceCard('A', '?'), NumberCard('2','?'), NumberCard('3','?'),]
我们在列表中枚举出一些牌的类、牌值和花色。从长远来说,我们需要更智能的工厂函数来创建Card实例;用这个方法枚举52张牌无聊且容易出错。在我们接触工厂函数之前,我们看一些其他问题。
使用init()创建显式常量
可以给牌定义花色类。在二十一点中,花色无关紧要,简单的字符串就可以。
我们使用花色构造函数作为创建常量对象的示例。在许多情况下,我们应用中小部分对象可以通过常量集合来定义。小部分的静态对象可能是实现策略模式或状态模式的一部分。
在某些情况下,我们会有一个在初始化或配置文件中创建的常量对象池,或者我们可以基于命令行参数创建常量对象。我们会在第十六章《通过命令进行复制》中获取初始化设计和启动设计的详细信息。
Python没有简单正式的机制来定义一个不可变对象,我们将在第三章《属性访问、方法属性和描述符》看看保证不可变性的相关技术。在本示例中,花色不可变是有道理的。
下面这个类,我们将用于创建四个显式常量:
class Suit:
def __init__(self, name, symbol):
self.name= name
self.symbol= symbol
下面是通过这个类创建的常量:
Club, Diamond, Heart, Spade = Suit('Club','?'), Suit('Diamond','?'), Suit('Heart','?'), Suit('Spade','?')
现在我们可以通过下面展示的代码片段创建cards:
cards = [AceCard('A', Spade), NumberCard('2', Spade), NumberCard('3', Spade),]
这个小示例,这种方法对于单个特性的花色代码来说并不是一个巨大的进步。在更复杂的情况下,会有一些策略或状态对象通过这个方式创建。通过从小的、静态的常量对象中复用可以使策略或状态设计模式更有效率。
我们必须承认,在Python中这些对象并不是技术上一成不变的,它是可变的。进行额外的编码使得这些对象真正不变可能会有一些好处。
无关紧要的不变性
不变性很有吸引力但却容易带来麻烦。有时候被神话般的“恶意程序员”在他们的应用程序中通过修改常量值进行调整。从设计上考虑,这是非常愚蠢的。这些神话般的、恶意的程序员不会停止这样做,因为已经没有更好的方法去更简洁简单的在Python中编码。恶意程序员访问到源码并且修改它仅仅是希望尽可能轻松地编写代码来修改一个常数。
在定义不可变对象的类的时候最好不要挣扎太久。在第三章《属性访问、方法属性和描述符》中,我们将通过在有bug的程序中提供合适的诊断信息来展示如何实现不变性。


猜你喜欢
- 我就废话不多说了,直接上代码了。非常简单哦!pytorch转成longtensorb = torch.rand(3,3)#得到的是float
- 本文实例讲述了python网络编程之数据传输UDP实现方法。分享给大家供大家参考。具体分析如下:一、问题:你觉得网络上像msn,qq之类的工
- 变量的存储在高级语言中,变量是对内存及其地址的抽象。对于python而言,python的一切变量都是对象,变量的存储,采用了引用语义的方式,
- 对str类型数据进行split操作如下:>>> s = 'abc\ndef'>>> s.
- 面包屑导航(breadcrumb)面包屑导航显示当前页面的路径,同时支持跳回之前任意页面breadcrumb的使用:按需引入的需要引入两个模
- 本文实例讲述了Python中列表元素转为数字的方法。分享给大家供大家参考,具体如下:有一个数字字符的列表:numbers = ['1
- 前言最近网站从HTTPS转为HTTP,更换了网址,旧网址做了301重定向,折腾有点大,于是在百度站长平台提交网址,不管是主动推送还是手动提交
- 还有种片面的观点认为,做网站设计与平面差不多,比如老罗发布的这则招聘中提到:年薪十万招擅长做下列网站设计风格的平面设计师一名。在专业角度,网
- 通常测试人员或公司实习人员需要处理一些txt文本内容,而此时使用Python是比较方便的语言。它不光在爬取网上资料上方便,还在NLP自然语言
- 正在看的ORACLE教程是:MySQL安全性指南(3)(转)。 &nb
- PHP图像处理组件GD的常用函数总结——概述 PHP有一系列很强大的图形处理函数,他们都统一包含在GD库中,这些函数已经基本满足了一个网络应
- 谷歌在大型图像数据库ImageNet上训练好了一个Inception-v3模型,这个模型我们可以直接用来进来图像分类。下载链接: https
- 在以前的MySQL版本中是没有窗口函数的,直到MySQL8.0才引入了窗口函数。窗口函数是对查询中的每一条记录执行一个计算,并且这个计算结果
- 比如:,master,test, 表示 该用户为 test 的下级代码,test登录后可以看到 test名下的业务和所有下级代理的业务。相关
- 这里列出了13种实现图片或网页内容 lightbox 效果的方法,大部分是链接到各种lightbox作者的英文页面,里面都有源代码下载。Th
- 本节讨论安装和使用第三方插件。关于编写自己的插件,我们下一章继续。一、pip 安装就像安装一些其他第三方库一样,使用pip也可以很容易地安装
- zip通常用于将两个列表合并在一起以同时进行迭代遍历注意:直接使用zip输出结果为<zip at 0x1d72cf30bc8>,
- MySQL出现乱码的原因要了解为什么会出现乱码,我们就先要理解:从客户端发起请求,到MySQL存储数据,再到下次从表取回客户端的过程中,哪些
- 一、事务 mysql事务是用于处理操作量大、复杂性高的数据1. 事务特性
- 现在vue.js几乎是程序员必会的前端框架啦~今天就学习记录一下怎么运行一个vue项目无论是Idea还是webstorm,都是一样的操作。去