Python中的类(下)
本文将介绍一下类的构造函数和初始化函数,以及如何通过"魔术方法"定制一个类。
类构造和初始化
在前面的文章中,经常使用初始化函数"__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中的类(下)的更多相关文章
- Python中的类、对象、继承
类 Python中,类的命名使用帕斯卡命名方式,即首字母大写. Python中定义类的方式如下: class 类名([父类名[,父类名[,...]]]): pass 省略父类名表示该类直接继承自obj ...
- python中那些双下划线开头得函数和变量--转载
Python中下划线---完全解读 Python 用下划线作为变量前缀和后缀指定特殊变量 _xxx 不能用'from module import *'导入 __xxx__ 系统定义名字 __x ...
- python python中那些双下划线开头的那些函数都是干啥用用的
1.写在前面 今天遇到了__slots__,,所以我就想了解下python中那些双下划线开头的那些函数都是干啥用用的,翻到了下面这篇博客,看着很全面,我只了解其中的一部分,还不敢乱下定义. 其实如果足 ...
- 4、Python中的类详解(0601)
<大话数据结构>的作者程杰在博客园也有博客,网址是:http://cj723.cnblogs.com/ 面向对象编程(OOP) 1.程序 = 指令 + 数据 代码可以选择以指令为核心或以数 ...
- python中那些双下划线开头得函数和变量
Python中下划线---完全解读 Python 用下划线作为变量前缀和后缀指定特殊变量 _xxx 不能用’from module import *’导入 __xxx__ 系统定义名字 __x ...
- Python中的类(上)
在Python中,可以通过class关键字定义自己的类,然后通过自定义的类对象类创建实例对象. 例如,下面创建了一个Student的类,并且实现了这个类的初始化函数"__init__&quo ...
- python中新式类和经典类
python中的类分为新式类和经典类,具体有什么区别呢?简单的说, 1.新式类都从object继承,经典类不需要. Python 2.x中默认都是经典类,只有显式继承了object才是新式类 Pyth ...
- python中的类(二)
python中的类(二) 六.类的成员 字段:普通字段,静态字段 eg: class Province(): country=’中国’ #静态字段,保存在类中,执行时可以通过类或对象访问 def __ ...
- Python中的类(一)
Python中的类(一) 一. 应用场景 如果多个函数中有一些相同的参数时,转换成面向对象. 二. 如何创建类 类是用来描述具有相同的属性和方法的对象的集合.它定义了该集合中每个对象所共有的属性和方法 ...
- 关于Python中的类普通继承与super函数继承
关于Python中的类普通继承与super函数继承 1.super只能用于新式类 2.多重继承super可以保公共父类仅被执行一次 一.首先看下普通继承的写法 二.再看看super继承的写法 参考链接 ...
随机推荐
- WebRTC 源码分析(四):VideoCRE 与内存抖动优化
WebRTC 是个宝,初窥这部分代码时就被它的 Capturer 类的设计惊艳到了,仔细品鉴后越发佩服起来,里面简直填了太多坑了,如此宝贝,如不能为我所用,岂非一大憾事!而前三篇的解读,正是为了今天能 ...
- How to deal with "Could not find component on update server. Contact VMware Support or your system administrator." in Vmware.
手动将vmware安装目录下的vmtools镜像文件,windows.iso文件放到虚拟机的光区里. 再进入虚拟机的系统,在系统里打开光盘进行安装
- SAP+ 差旅报销集成方案的实现
跨国集团通常都会有一个财务共享中心SSC的集中处理业务部门,用于统计来自各个公司的差旅报销,而1).传统的OA审批 虽然审批流是优势存在着需要SAP数据二次录入,数据不同步等困难,2).如果终端用户都 ...
- Java如何格式化秒数?
在Java中,如何格式化秒数? 此示例使用SimpleDateFormat类的SimpleDateFormat('ss')构造函数和sdf.format(date)方法格式化秒数. package c ...
- MyBatis where标签语句
当 where 中的条件使用的 if 标签较多时,这样的组合可能会导致错误.当 java 代码按如下方法调用时: @Test public void select_test_where() { Use ...
- C#自定义事件 范例:监视变量变化
很多时候我们需要程序具有一种功能,就是当满足某一条件时触发某个动作,使用C#的事件机制就可以达到这个目的下面的例子是一个很好的演示,这段代码实现了对一个变量的监视,一旦变量发生改变,就触发动作 定义事 ...
- 多媒体开发之rtmp---rtmp client 编译
静态库连接编译问题: assert 原来在c编译器下没定义 ceill 没连接没加 -lm http://blog.chinaunix.net/uid-20681545-id-3786786.html ...
- 下载android源码
http://source.android.com Step 1.按照http://source.android.com/source/initializing.html配置好android编译环境 ...
- hdu 2771(uva 12171) Sculpture bfs+离散化
题意: 给出一些边平行于坐标轴的长方体,这些长方体可能相交.也可能相互嵌套.这些长方体形成了一个雕塑,求这个雕塑的整体积和表面积. 题解: 最easy想到直接进行bfs或者dfs统计,但此题的麻烦之处 ...
- ORA-00918: column ambiguously defined
今天遇到了一个Oracle的SQL问题:ORA-00918: column ambiguously defined 大致的意思就是字段名称不明确,可能存在同名的字段 SELECT * FROM (SE ...