本文将介绍一下类的构造函数和初始化函数,以及如何通过"魔术方法"定制一个类。

类构造和初始化

在前面的文章中,经常使用初始化函数"__init__",下面看看"__init__"和"__new__"的联系和差别。

下面先通过一段代码看看这两个方法的调用顺序:

class A(object):
def __init__(self,*args, **kwargs):
print "init %s" %self.__class__
def __new__(cls,*args, **kwargs):
print "new %s" %cls
return object.__new__(cls, *args, **kwargs) a = A()

从代码的输出可以看到,当通过类实例化一个对象的时候,"__new__"方法首先被调用,然后是"__init__"方法。

一般来说,"__init__"和"__new__"函数都会有下面的形式:

def __init__(self, *args, **kwargs):
# func_suite def __new__(cls, *args, **kwargs):
# func_suite
return obj

对于"__new__"和"__init__"可以概括为:

  • "__new__"方法在Python中是真正的构造方法(创建并返回实例),通过这个方法可以产生一个"cls"对应的实例对象,所以说"__new__"方法一定要有返回
  • 对于"__init__"方法,是一个初始化的方法,"self"代表由类产生出来的实例对象,"__init__"将对这个对象进行相应的初始化操作

前面文章中已经介绍过了"__init__"的一些行为,包括继承情况中"__init__"的表现。下面就重点看看"__new__"方法。

__new__特性

"__new__"是在新式类中新出现的方法,它有以下行为特性:

  • "__new__" 方法是在类实例化对象时第一个调用的方法,将返回实例对象
  • "__new__" 方法始终都是类的静态方法(即第一个参数为cls),即使没有被加上静态方法装饰器
  • 第一个参数cls是当前正在实例化的类,如果要得到当前类的实例,应当在当前类中的 "__new__" 方法语句中调用当前类的父类的" __new__" 方法

对于上面的第三点,如果当前类是直接继承自 object,那当前类的 "__new__" 方法返回的对象应该为:

def __new__(cls, *args, **kwargs):
# func_suite
return object.__new__(cls, *args, **kwargs)

重写__new__

如果(新式)类中没有重写"__new__"方法,Python默认是调用该类的直接父类的"__new__"方法来构造该类的实例,如果该类的父类也没有重写"__new__",那么将一直按照同样的规则追溯至object的"__new__"方法,因为object是所有新式类的基类。

而如果新式类中重写了"__new__"方法,那么可以选择任意一个其他的新式类(必须是新式类,只有新式类有"__new__",因为所有新式类都是从object派生)的"__new__"方法来创建实例,包括这个新式类的所有前代类和后代类,只要它们不会造成递归死循环。

看一段例子代码:

class Foo(object):
def __new__(cls, *args, **kwargs):
obj = object.__new__(cls, *args, **kwargs)
# 这里的object.__new__(cls, *args, **kwargs) 等价于
# super(Foo, cls).__new__(cls, *args, **kwargs)
# object.__new__(Foo, *args, **kwargs)
# Bar.__new__(cls, *args, **kwargs)
# Student.__new__(cls, *args, **kwargs),即使Student跟Foo没有关系,也是允许的,因为Student是从object派生的新式类 # 在任何新式类,不能调用自身的“__new__”来创建实例,因为这会造成死循环
# 所以要避免return Foo.__new__(cls, *args, **kwargs)或return cls.__new__(cls, *args, **kwargs)
print "Call __new__ for %s" %obj.__class__
return obj class Bar(Foo):
def __new__(cls, *args, **kwargs):
obj = object.__new__(cls, *args, **kwargs)
print "Call __new__ for %s" %obj.__class__
return obj class Student(object):
# Student没有“__new__”方法,那么会自动调用其父类的“__new__”方法来创建实例,即会自动调用 object.__new__(cls)
pass class Car(object):
def __new__(cls, *args, **kwargs):
# 可以选择用Bar来创建实例
obj = object.__new__(Bar, *args, **kwargs)
print "Call __new__ for %s" %obj.__class__
return obj foo = Foo()
bar = Bar()
car = Car()

代码的输出为:

__init__的调用

"__new__"决定是否要使用该类的"__init__"方法,因为"__new__" 可以调用其他类的构造方法或者直接返回别的类创建的对象来作为本类的实例。

通常来说,新式类开始实例化时,"__new__"方法会返回cls(cls指代当前类)的实例,然后调用该类的"__init__"方法作为初始化方法,该方法接收这个实例(即self)作为自己的第一个参数,然后依次传入"__new__"方法中接收的位置参数和命名参数。

但是,如果"__new__"没有返回cls(即当前类)的实例,那么当前类的"__init__"方法是不会被调用的。看下面的例子:

class A(object):
def __init__(self, *args, **kwargs):
print "Call __init__ from %s" %self.__class__ def __new__(cls, *args, **kwargs):
obj = object.__new__(cls, *args, **kwargs)
print "Call __new__ for %s" %obj.__class__
return obj class B(object):
def __init__(self, *args, **kwargs):
print "Call __init__ from %s" %self.__class__ def __new__(cls, *args, **kwargs):
obj = object.__new__(A, *args, **kwargs)
print "Call __new__ for %s" %obj.__class__
return obj b = B()
print type(b)

代码中,在B的"__new__"方法中,通过"obj = object.__new__(A, *args, **kwargs)"创建了一个A的实例,在这种情况下,B的"__init__"函数就不会被调用到。

派生不可变类型

关于"__new__"方法还有一个重要的用途就是用来派生不可变类型。

例如,Python中float是不可变类型,如果想要从float中派生一个子类,就要实现"__new__"方法:

class Round2Float(float):
def __new__(cls, num):
num = round(num, 2)
#return super(Round2Float, cls).__new__(cls, num)
return float.__new__(Round2Float, num) f = Round2Float(4.14159)
print f

代码中从float派生出了一个Round2Float类,该类的实例就是保留小数点后两位的浮点数。

定制一个类

在Python中,我们可以通过"魔术方法"使自定义的class变得强大、易用。

例如,前面的文章中介绍过Python迭代器,当我们想定义一个可迭代的类对象的时候,就可以去实现"__iter__(self)"这个魔术方法;

又例如,前面文章介绍的上下文管理器,当需要建立一个上下文管理器类对象的时候,就可以去实现"__enter__(self)"和"__exit__(self)"方法。

所以,建议参考 "魔术方法"的文档,通过魔术方法来定制自定义的类。

调用魔术方法

一些魔术方法直接和内建函数相对应的,在这种情况下,调用他们的方法很简单,下面给出了一些对应表。

魔术方法

调用方式

解释

__new__(cls [,...])

instance = MyClass(arg1, arg2)

__new__ 在创建实例的时候被调用

__init__(self [,...])

instance = MyClass(arg1, arg2)

__init__ 在创建实例的时候被调用

__cmp__(self, other)

self == other, self > other, 等。

在比较的时候调用

__pos__(self)

+self

一元加运算符

__neg__(self)

-self

一元减运算符

__invert__(self)

~self

取反运算符

__index__(self)

x[self]

对象被作为索引使用的时候

__nonzero__(self)

bool(self)

对象的布尔值

__getattr__(self, name)

self.name # name 不存在

访问一个不存在的属性时

__setattr__(self, name, val)

self.name = val

对一个属性赋值时

__delattr__(self, name)

del self.name

删除一个属性时

__getattribute(self, name)

self.name

访问任何属性时

__getitem__(self, key)

self[key]

使用索引访问元素时

__setitem__(self, key, val)

self[key] = val

对某个索引值赋值时

__delitem__(self, key)

del self[key]

删除某个索引值时

__iter__(self)

for x in self

迭代时

__contains__(self, value)

value in self, value not in self

使用 in 操作测试关系时

__concat__(self, value)

self + other

连接两个对象时

__call__(self [,...])

self(args)

"调用"对象时

__enter__(self)

with self as x:

with 语句环境管理

__exit__(self, exc, val, trace)

with self as x:

with 语句环境管理

__getstate__(self)

pickle.dump(pkl_file, self)

序列化

__setstate__(self)

data = pickle.load(pkl_file)

序列化

总结

文中介绍了类的构造和初始化方法:"__new__"和"__init__"。

"__new__"方法是新式类特有的方法,通常情况下,__new__方法会创建返回cls(cls指代当前类)的实例,然后调用该类的"__init__"方法作为初始化方法,该方法接收这个实例(即self)作为自己的第一个参数,然后依次传入"__new__"方法中接收的位置参数和命名参数;但是,如果"__new__"没有返回cls(即当前类)的实例,那么当前类的"__init__"方法是不会被调用的。

通过"魔术方法",可以对自定义类进行定制、扩展,使得自定义类变得强大、易用。

Python中的类(下)的更多相关文章

  1. Python中的类、对象、继承

    类 Python中,类的命名使用帕斯卡命名方式,即首字母大写. Python中定义类的方式如下: class 类名([父类名[,父类名[,...]]]): pass 省略父类名表示该类直接继承自obj ...

  2. python中那些双下划线开头得函数和变量--转载

    Python中下划线---完全解读     Python 用下划线作为变量前缀和后缀指定特殊变量 _xxx 不能用'from module import *'导入 __xxx__ 系统定义名字 __x ...

  3. python python中那些双下划线开头的那些函数都是干啥用用的

    1.写在前面 今天遇到了__slots__,,所以我就想了解下python中那些双下划线开头的那些函数都是干啥用用的,翻到了下面这篇博客,看着很全面,我只了解其中的一部分,还不敢乱下定义. 其实如果足 ...

  4. 4、Python中的类详解(0601)

    <大话数据结构>的作者程杰在博客园也有博客,网址是:http://cj723.cnblogs.com/ 面向对象编程(OOP) 1.程序 = 指令 + 数据 代码可以选择以指令为核心或以数 ...

  5. python中那些双下划线开头得函数和变量

    Python中下划线---完全解读     Python 用下划线作为变量前缀和后缀指定特殊变量 _xxx 不能用’from module import *’导入 __xxx__ 系统定义名字 __x ...

  6. Python中的类(上)

    在Python中,可以通过class关键字定义自己的类,然后通过自定义的类对象类创建实例对象. 例如,下面创建了一个Student的类,并且实现了这个类的初始化函数"__init__&quo ...

  7. python中新式类和经典类

    python中的类分为新式类和经典类,具体有什么区别呢?简单的说, 1.新式类都从object继承,经典类不需要. Python 2.x中默认都是经典类,只有显式继承了object才是新式类 Pyth ...

  8. python中的类(二)

    python中的类(二) 六.类的成员 字段:普通字段,静态字段 eg: class Province(): country=’中国’ #静态字段,保存在类中,执行时可以通过类或对象访问 def __ ...

  9. Python中的类(一)

    Python中的类(一) 一. 应用场景 如果多个函数中有一些相同的参数时,转换成面向对象. 二. 如何创建类 类是用来描述具有相同的属性和方法的对象的集合.它定义了该集合中每个对象所共有的属性和方法 ...

  10. 关于Python中的类普通继承与super函数继承

    关于Python中的类普通继承与super函数继承 1.super只能用于新式类 2.多重继承super可以保公共父类仅被执行一次 一.首先看下普通继承的写法 二.再看看super继承的写法 参考链接 ...

随机推荐

  1. 【常用配置】——WPS文字常用快捷键大全【史上最全面】转

    WPS文字快捷键大全 Word快捷键 Excel快捷键 PPT快捷键 Office快捷键大全 WPS文字快捷键 WPS表格快捷键 WPS演示快捷键 WPS快捷键大全 用于处理WPS文档的快捷键 创建新 ...

  2. beansbinding NetBeans IDE 中 Swing数据绑定插件

    http://my.oschina.net/u/127459/blog/111486 https://kenai.com/projects/betterbeansbinding/pages/Home ...

  3. 【转】腾讯移动品质中心TMQ [腾讯 TMQ] 测试管理平台大比拼

    简介 测试管理平台是贯穿测试整个生命周期的工具集合,它主要解决的是测试过程中团队协作的问题,比如缺陷管理.用例管理.测试任务管理等. 目前市面上比较流行的测试管理工具有QC. Mantis. BugZ ...

  4. Sword redis补充

    Redis 键(key) Redis 键命令用于管理 redis 的键. redis任何数据类型都有key --删除key的命令 redis> del key Redis 事务 Redis 事务 ...

  5. FileOutPutStream in 创新实训 自然语言交流系统

    FileOutPutStream在c盘等一级目录下是可以创建文件的,如: new FileOutputStream("c:\\kk.txt");但是在c\\test等就创建不了,F ...

  6. What is systemvolumeinformation? delete it?

    System Volume Information完全可以删除 许多人为了自己的电脑上的System Volume Information不知道而苦恼..我再此给大家介绍一下希望能给你点帮助.. Sy ...

  7. 摄像头驱动0V7725学习笔记连载(三):0V7725 SCCB时序的实现

    上一篇博客主要是讲解了关于需要配置的重要寄存器,那么接下来就是要通过SCCB接口实现对OV7725的配置.参考<OmniVision Serial Camera Control Bus (SCC ...

  8. 初试PyOpenGL三 (Python+OpenGL)GPGPU基本运算与乒乓技术

    这篇GPGPU 概念1: 数组= 纹理 - 文档文章提出的数组与纹理相等让人打开新的眼界与思维,本文在这文基础上,尝试把这部分思想拿来用在VBO粒子系统上. 在前面的文章中,我们把CPU的数据传到GP ...

  9. MySQL导入数据错误error: 13 及解决办法

    先说解决办法 将sql文件放到你的账号能够访问的地方!!! 因为我用的grid账号,所以只需要将sql放到 ~grid/ 或者 /tmp等grid能够访问的地方即可. Don't place the ...

  10. Eclipse创建一个Maven Web项目

    在这篇文章中,我们将演示如何在Eclipse IDE中使用maven创建一个动态Web项目. 使用的工具和技术 - Eclipse Jee Oxygen Maven 3.3.3 JavaSE 1.8 ...