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

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

时间: 2015-04-30

Python中__init__和__new__的区别详解

__init__ 方法是什么? 使用Python写过面向对象的代码的同学,可能对 __init__ 方法已经非常熟悉了,__init__ 方法通常用在初始化一个类实例的时候.例如: # -*- coding: utf-8 -*- class Person(object): """Silly Person""" def __init__(self, name, age): self.name = name self.age = age def __

Python中__init__.py文件的作用详解

__init__.py 文件的作用是将文件夹变为一个Python模块,Python 中的每个模块的包中,都有__init__.py 文件. 通常__init__.py 文件为空,但是我们还可以为它增加其他的功能.我们在导入一个包时,实际上是导入了它的__init__.py文件.这样我们可以在__init__.py文件中批量导入我们所需要的模块,而不再需要一个一个的导入. # package # __init__.py import re import urllib import sys impo

Python类方法__init__和__del__构造、析构过程分析

最近学习<Python参考手册>学到Class部分,遇到了类的构造析构部分的问题: 1.什么时候构造? 2.什么时候析构? 3.成员变量如何处理? 4.Python中的共享成员函数如何访问? ------------------------ 探索过程: 1.经过查找,Python中没有专用的构造和析构函数,但是一般可以在__init__和__del__分别完成初始化和删除操作,可用这个替代构造和析构.还有一个__new__用来定制类的创建过程,不过需要一定的配置,此处不做讨论. 2.类的成员函

详解Python中的__init__和__new__

一.__init__ 方法是什么?使用Python写过面向对象的代码的同学,可能对 __init__ 方法已经非常熟悉了,__init__ 方法通常用在初始化一个类实例的时候.例如: 复制代码 代码如下: # -*- coding: utf-8 -*- class Person(object):    """Silly Person""" def __init__(self, name, age):        self.name = name

Python中__new__与__init__方法的区别详解

在python2.x中,从object继承得来的类称为新式类(如class A(object))不从object继承得来的类称为经典类(如class A()) 新式类跟经典类的差别主要是以下几点: 1. 新式类对象可以直接通过__class__属性获取自身类型:type 2. 继承搜索的顺序发生了改变,经典类多继承时属性搜索顺序: 先深入继承树左侧,再返回,开始找右侧(即深度优先搜索);新式类多继承属性搜索顺序: 先水平搜索,然后再向上移动 例子: 经典类: 搜索顺序是(D,B,A,C) >>

Python RuntimeError: thread.__init__() not called解决方法

在写一个多线程类的时候调用报错 RuntimeError: thread.__init__() not called 复制代码 代码如下: class NotifyTread(threading.Thread):      def __init__(self, params):          threading.Thread.__init__(self)  #here......          self.params = params            def run(self): 

详解Python中的__new__、__init__、__call__三个特殊方法

__new__: 对象的创建,是一个静态方法,第一个参数是cls.(想想也是,不可能是self,对象还没创建,哪来的self) __init__ : 对象的初始化, 是一个实例方法,第一个参数是self. __call__ : 对象可call,注意不是类,是对象. 先有创建,才有初始化.即先__new__,而后__init__. 上面说的不好理解,看例子. 1.对于__new__ class Bar(object): pass class Foo(object): def __new__(cls

python中的__init__ 、__new__、__call__小结

1.__new__(cls, *args, **kwargs)  创建对象时调用,返回当前对象的一个实例;注意:这里的第一个参数是cls即class本身2.__init__(self, *args, **kwargs) 创建完对象后调用,对当前对象的实例的一些初始化,无返回值,即在调用__new__之后,根据返回的实例初始化:注意,这里的第一个参数是self即对象本身[注意和new的区别]3.__call__(self,  *args, **kwargs) 如果类实现了这个方法,相当于把这个类型

Python中的__new__与__init__魔术方法理解笔记

很喜欢Python这门语言.在看过语法后学习了Django 这个 Web 开发框架.算是对 Python 有些熟悉了.不过对里面很多东西还是不知道,因为用的少.今天学习了两个魔术方法:__new__ 和 __init__. 开攻: 如果对 Python 有所简单了解的话应该知道它包含类这个概念的.语法如下: 复制代码 代码如下: class ClassName:     <statement - 1>:         .         .           .     <state

python中子类继承父类的__init__方法实例

前言 使用Python写过面向对象的代码的同学,可能对 __init__ 方法已经非常熟悉了,__init__方法在类的一个对象被建立时,马上运行.这个方法可以用来对你的对象做一些你希望的 初始化 . 注意:这个名称的开始和结尾都是双下划线. 父类A class A(object): def __init__(self, name): self.name=name print "name:", self.name def getName(self): return 'A ' + sel

python中子类调用父类函数的方法示例

前言 本文主要给大家介绍了关于python子类调用父类函数的相关内容,Python中子类中的__init__()函数会覆盖父类的函数,一些情况往往需要在子类里调用父类函数.下面话不多说了,来一起看看详细的介绍: 如下例程里,???处是需要调用父类函数的地方,接下来结合例程具体介绍. # -*- coding:utf-8 -*- class Student: def __init__(self,name): self.name=name def ps(self): print('I am %s'%

python中pygame针对游戏窗口的显示方法实例分析(附源码)

本文实例讲述了python中pygame针对游戏窗口的显示方法.分享给大家供大家参考,具体如下: 在这篇教程中,我将给出一个demo演示: 当我们按下键盘的'f'键的时候,演示的窗口会切换到全屏显示和默认显示两种显示模式 并且在后台我们可以看到相关的信息输出: 上面给出了一个简单的例子,当然在pygame的官方文档中有对显示策略的更权威的说明: http://www.pygame.org/docs/ref/display.html#pygame.display.set_mode ''' pyga

java中子类继承父类,程序运行顺序的深入分析

我们经常在项目中使用继承,但是往往不太明白,程序运行的顺序以及原理,尤其是使用上转型对象的时候,以及父类子类中都有static变量和方法时,不知道先运行谁.我也是写了一个例子.总结了一下. 复制代码 代码如下: 父类:public class TestStatic { public static String name="china"; {       System.out.println("========方法体========");    } static{  

C#中子类调用父类的实现方法

本文实例讲述了C#中实现子类调用父类的方法,分享给大家供大家参考之用.具体方法如下: 一.通过子类无参构造函数创建子类实例 创建父类Person和子类Student. public class Person { public Person() { Console.WriteLine("我是人"); } } public class Student : Person { public Student() { Console.WriteLine("我是学生"); } }

Python实现子类调用父类的方法

本文实例讲述了Python实现子类调用父类的方法.分享给大家供大家参考.具体实现方法如下: python和其他面向对象语言类似,每个类可以拥有一个或者多个父类,它们从父类那里继承了属性和方法.如果一个方法在子类的实例中被调用,或者一个属性在子类的实例中被访问,但是该方法或属性在子类中并不存在,那么就会自动的去其父类中进行查找. 继承父类后,就能调用父类方法和访问父类属性,而要完成整个集成过程,子类是需要调用的构造函数的. 子类不显式调用父类的构造方法,而父类构造函数初始化了一些属性,就会出现问题

PHP中子类重载父类的方法【parent::方法名】

在PHP中不能定义重名的函数,也包括不能再同一个类中定义重名的方法,所以也就没有方法重载.单在子类中可以定义和父类重名的方法,因为父类的方法已经在子类中存在,这样在子类中就可以把从父类中继承过来的方法重写. 子类中重载父类的方法就是在子类中覆盖从父类中继承过来的方法,父类中的方法被子类继承过来不就可以直接使用吗?为什么还要重载呢?因为有一些情况我们必须要覆盖的.例如,有一个"鸟"类,在这个类中定义了鸟的通用方法"飞翔".将"鸵鸟"类作为它的子类,

Python实现子类调用父类的初始化实例

前言 python中进行面向对象编程,当在子类的实例中调用父类的属性时,由于子类的__init__方法重写了父类的__init__方法,如果在子类中这些属性未经过初始化,使用时就会出错. 例如以下的代码: class A(object): def __init__(self): self.a = 5 def function_a(self): print('I am from A, my value is %d' % self.a) class B(A): def __init__(self):

浅谈Python中的继承

继承 Python 中所有的类都是object类的子类,而object 继承自type 继承分为 接口继承和实现继承 接口继承:使用父类的接口名,子类重写这个方法.尽可能的继承接口类,在子类中实现方法,鼓励对接口类的多继承,这样遵循接口隔离原则,有利于归一化设计,不提倡对抽象类进行多继承 实现继承:子类不需要实现任何东西,直接使用父类接口和实现会增强代码的耦合性,不推荐使用. 一些细节 类继承最终要被实例化,我们多数时候使用的还是对象而不是类.因此我们还是来一点点看继- 继承的过程 承仅仅是一种

Python 实现子类获取父类的类成员方法

大家好,今天在写代码的时候,遇到了这样一种情况.我有如下所示的几个类用来存放程序配置(其实当做命名空间来用,同时感觉能够继承方便一点), import os class Config: BASE_DIR = "/tmp" class TestConfig(Config): DATA_DIR = os.path.join(Config.BASE_DIR, "data") 然后我在子类中想要访问父类的类成员变量,而且这两个类都是只有类成员变量.感觉目前我使用的方法笨一点