详细解读Python中的__init__()方法


Posted in Python onMay 02, 2015

__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的程序中提供合适的诊断信息来展示如何实现不变性。

Python 相关文章推荐
Python基于回溯法子集树模板解决旅行商问题(TSP)实例
Sep 05 Python
Python文件操作基本流程代码实例
Dec 11 Python
python3爬取各类天气信息
Feb 24 Python
详解pyqt5 动画在QThread线程中无法运行问题
May 05 Python
Python基于property实现类的特性操作示例
Jun 15 Python
python利用pandas将excel文件转换为txt文件的方法
Oct 23 Python
基于wxPython的GUI实现输入对话框(1)
Feb 27 Python
python导包的几种方法(自定义包的生成以及导入详解)
Jul 15 Python
python爬虫刷访问量 2019 7月
Aug 01 Python
wxpython绘制圆角窗体
Nov 18 Python
python 监控logcat关键字功能
Sep 04 Python
再也不用花钱买漫画!Python爬取某漫画的脚本及源码
Jun 09 Python
举例讲解Python的Tornado框架实现数据可视化的教程
May 02 #Python
Python的Bottle框架中返回静态文件和JSON对象的方法
Apr 30 #Python
使用Python编写提取日志中的中文的脚本的方法
Apr 30 #Python
简单的连接MySQL与Python的Bottle框架的方法
Apr 30 #Python
Python的Bottle框架中实现最基本的get和post的方法的教程
Apr 30 #Python
Python中使用Beautiful Soup库的超详细教程
Apr 30 #Python
Python中正则表达式的详细教程
Apr 30 #Python
You might like
开启CURL扩展,让服务器支持PHP curl函数(远程采集)
2011/03/19 PHP
基于session_unset与session_destroy的区别详解
2013/06/03 PHP
php查询whois信息的方法
2015/06/08 PHP
日常整理PHP中简单的图形处理(经典)
2015/10/26 PHP
php设计模式之观察者模式实例详解【星际争霸游戏案例】
2020/03/30 PHP
JavaScript实现动态增加文件域表单
2009/02/12 Javascript
jQuery 图片切换插件(代码比较少)
2012/05/07 Javascript
js函数中onmousedown和onclick的区别和联系探讨
2013/05/19 Javascript
javascript中动态加载js文件多种解决办法总结
2013/11/15 Javascript
JS中自定义定时器让它在某一时刻执行
2014/09/02 Javascript
jQuery中animate()方法用法实例
2014/12/24 Javascript
javascript中undefined与null的区别
2015/08/16 Javascript
举例说明如何为JavaScript的方法参数设置默认值
2015/11/17 Javascript
jQuery模拟360浏览器切屏效果幻灯片(附demo源码下载)
2016/01/29 Javascript
JS实现屏蔽网页右键复制及ctrl+c复制的方法【2种方法】
2016/09/04 Javascript
jquery延迟对象解析
2016/10/26 Javascript
EasyUI 结合JS导出Excel文件的实现方法
2016/11/10 Javascript
bootstrap3 dialog 更强大、更灵活的模态框
2017/04/20 Javascript
js中apply与call简单用法详解
2017/11/06 Javascript
Angular中使用MathJax遇到的一些问题
2017/12/15 Javascript
nodejs读取图片返回给浏览器显示
2019/07/25 NodeJs
JavaScript实现京东快递单号查询
2020/11/30 Javascript
Python socket C/S结构的聊天室应用实现
2014/11/30 Python
Python中返回字典键的值的values()方法使用
2015/05/22 Python
详解opencv Python特征检测及K-最近邻匹配
2019/01/21 Python
Python3.4学习笔记之类型判断,异常处理,终止程序操作小结
2019/03/01 Python
Matlab使用Plot函数实现数据动态显示方法总结
2021/02/25 Python
CSS3 :nth-child()伪类选择器实现奇偶行显示不同样式
2013/11/05 HTML / CSS
HTML5 Canvas中使用路径描画二阶、三阶贝塞尔曲线
2015/01/01 HTML / CSS
应届生污水处理求职信
2013/11/06 职场文书
节能环保标语
2014/06/12 职场文书
医学专业大学生求职信
2014/07/12 职场文书
员工趣味活动方案
2014/08/27 职场文书
捐助感谢信
2015/01/22 职场文书
2015年党务公开工作总结
2015/05/19 职场文书
《从现在开始》教学反思
2016/02/16 职场文书