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继承的写法 参考链接 ...
随机推荐
- 最近玩Bootstrap , 一些小工具 记录在案。
最近玩Bootstrap , 一些小工具 记录在案. 1 定制Bootstrap ,所见即所得的修改Bootstrap的各种变量,即时查看样式的变化. http://bootswatchr.com/ ...
- javascript 学习记录
关于牛B的Jquery源头 (function(){ //这里省略jQuery所有实现 })(); :无论你怎么去定义你的函数 JS解释器都会把它翻译成一个 Function对象 :那什么是Funct ...
- js if判断 遍历 替换图片地质
<script> $(document).ready(function() { var s = "The rain in Spain falls mainly in the pl ...
- 技能UP:SAP CO掌上配置手册
No. 配置对象 事务代码 路径 1 Enterprise Structure and General Controlling configration Maintain EC-PCA : ...
- FTP服务器的配置与实现
一.准备工作 实验目的:完成FTP服务器的配置,并能熟练操作. 环境搭建: 虚拟机 vmware workstation windows2003镜像文件 Serv-U 主机 二.步骤 1,在虚拟机中 ...
- VS自带的dbghelp.h文件 报错
场景: 编译报错: 解决方法: 在#include <dbghelp.h> 之前 #include <Windows.h>
- e611. Setting Focus Traversal Keys for the Entire Application
This example changes the focus traversal keys for the entire application. For an example of how to c ...
- c# 生成随机时间
Random random = new Random((int)(DateTime.Now.Ticks)); ; ) { , ); , ); , ); string tempStr = string. ...
- Asp.net webapi Owin Get request form data
var formData = await context.Request.ReadFormAsync() as IEnumerable<KeyValuePair<string, strin ...
- unity---gameScreen 的Stats参数
Unity Stats 面板介绍 原创 2016年06月12日 10:06:12 1544 0 3 Time per frame and FPS (每帧的时间和FPS):处理和渲染一个游戏框架(以及由 ...