详细解读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利用elaphe制作二维条形码实现代码
May 25 Python
Python不规范的日期字符串处理类
Jun 10 Python
Python科学计算之NumPy入门教程
Jan 15 Python
基于python的Tkinter编写登陆注册界面
Jun 30 Python
Django 如何获取前端发送的头文件详解(推荐)
Aug 15 Python
Python实现的堆排序算法原理与用法实例分析
Nov 22 Python
Python类的继承和多态代码详解
Dec 27 Python
如何通过雪花算法用Python实现一个简单的发号器
Jul 03 Python
python爬虫 爬取超清壁纸代码实例
Aug 16 Python
python实现把二维列表变为一维列表的方法分析
Oct 08 Python
pycharm中导入模块错误时提示Try to run this command from the system terminal
Mar 26 Python
Python利用PyPDF2库获取PDF文件总页码实例
Apr 03 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
PHP初学入门
2006/11/19 PHP
PHP7之Mongodb API使用详解
2015/12/26 PHP
php中preg_replace正则替换用法分析【一次替换多个值】
2017/01/17 PHP
PHP实现图片的等比缩放和Logo水印功能示例
2017/05/04 PHP
PHP实现一维数组与二维数组去重功能示例
2018/05/24 PHP
jquery ui dialog里调用datepicker的问题
2009/08/06 Javascript
JavaScript高级程序设计 阅读笔记(十八) js跨平台的事件
2012/08/14 Javascript
Javasipt:操作radio标签详解
2013/12/30 Javascript
JS实现让访问者自助选择网页文字颜色的方法
2015/02/24 Javascript
JS实现淡蓝色简洁竖向Tab点击切换效果
2015/10/06 Javascript
js实现有过渡渐变效果的图片轮播相册(兼容IE,ff)
2016/01/19 Javascript
JavaScript 链式结构序列化详解
2016/09/30 Javascript
jQuery插件HighCharts绘制2D圆环图效果示例【附demo源码下载】
2017/03/09 Javascript
利用webstrom调试Vue.js单页面程序的方法教程
2017/06/06 Javascript
vue 音乐App QQ音乐搜索列表最新接口跨域设置方法
2018/09/25 Javascript
react+redux仿微信聊天界面
2019/06/21 Javascript
Python类的专用方法实例分析
2015/01/09 Python
Python 数据结构之旋转链表
2017/02/25 Python
Python模糊查询本地文件夹去除文件后缀的实例(7行代码)
2017/11/09 Python
解决Pycharm下面出现No R interpreter defined的问题
2018/10/29 Python
python中partial()基础用法说明
2018/12/30 Python
Python实现12306火车票抢票系统
2019/07/04 Python
使用Python的turtle模块画国旗
2019/09/24 Python
Python 装饰器原理、定义与用法详解
2019/12/07 Python
python 伯努利分布详解
2020/02/25 Python
python使用matplotlib绘制折线图的示例代码
2020/09/22 Python
html5 迷宫游戏(碰撞检测)实例一
2013/07/25 HTML / CSS
美国葡萄酒网上商店:Martha Stewart Wine Co.
2019/03/17 全球购物
Regatta官网:英国最受欢迎的户外服装和鞋类品牌
2019/05/01 全球购物
阿巴庭院:Abba Patio
2019/06/18 全球购物
九年级历史教学反思
2014/01/27 职场文书
企业文化建设实施方案
2014/03/22 职场文书
合作意向书
2014/07/30 职场文书
三八红旗手主要事迹材料
2015/11/04 职场文书
python代码实现扫码关注公众号登录的实战
2021/11/01 Python
postgreSQL数据库基础知识介绍
2022/04/12 PostgreSQL