深入理解python__new__和__init__
2017-05-26 14:44 核桃不是桃 阅读(221) 评论(0) 收藏 举报先看题目:如何派生内置不可变类型并修改实例化行为?
题目:自定义一种新类型的元组,对于传入的可迭代对象,只保留作为其中int类型并且值大于0的元素,例如intTuple([1,-1,'abc',6,['x','y'],3])=>(1,6,3)
要求intTuple是tuple的子类,如何实现?
- 1 class IntTuple(tuple): 2 def __init__(self, iterable): 3 #before 4 super(IntTuple, self).__init__(iterable) 5 #after 6 7 t = IntTuple([1,-1,'abc',6,['x','y'],3]) 8 print t
第一行定义了一个名字叫做IntTuple的类,继承内置不可变类型tuple,第二行定义构造方法,在构造方法中,调用父类的构造方法,并返回一个可迭代对象。没有任何修改。
现在我们在构造方法中尝试过滤掉无关的参数。方法是在调用父类构造器之前和之后,分别测试。
首先看在调用父类构造器之后能否改变?答案是否定的,因为self是一个tuple的实例,而tuple是一个不可变类型,因此这种方法不成立。
现在尝试在调用父类构造器之前,通过改变参数iterable来看能否达到需求。
1 class IntTuple(tuple): 2 def __init__(self, iterable): 3 #before 4 super(IntTuple, self).__init__((1,6,3)) 5 #after 6 7 t = IntTuple([1,-1,'abc',6,['x','y'],3]) 8 print t
这里我们手动将其改为一个(1,6,3),然后看一下返回结构:
(1, -1, 'abc', 6, ['x', 'y'], 3)
运行之后发现结构并不是预想的,分析可知,在__init__之前,是__new__这个静态方法创建了self这个参数。
1 # -*-endcoding:utf8-*- 2 class IntTuple(tuple): 3 def __new__(cls, iterable): 4 g = (x for x in iterable if isinstance(x, int) and x > 0) 5 # 调用父类的__new__方法: 6 return super(IntTuple, cls).__new__(cls, g) 7 8 def __init__(self, iterable): 9 #before 10 super(IntTuple, self).__init__(iterable) 11 #after 12 13 t = IntTuple([1,-1,'abc',6,['x','y'],3]) 14 print t
第三行,在调用__init__之前调用了__new__方法,关于__new__有下面这些知识点:
__new__只有继承自object的新式类才有
__new__至少要有一个参数cls,表示要实例化的类,此参数在实例化时有python解释器自动提供
__new__必须要有返回值,返回实例化出来的实例,这点在自己实现new的时候要特别注意。
__init__有一个参数self,就是这个__new__返回的实例,__init__在__new__的基础上可以完成一些其它初始化的动作,__init__不需要返回值
若__new__没有正确返回当前类cls的实例,那__init__是不会被调用的,即使是父类的实例也不行
理解了这些,再看运行结果:
1 (1, 6, 3)
结果却是如我们所料。
我们总结__new__和__init__的区别
- __new__是一个静态方法,__init__是一个实例方法
- __new__方法会返回一个创建的实例,而__init__接受了这个返回值,且什么都不返回
- 只有在__new__返回一个cls的实例时后面的__init__才能被调用
- 当创建一个新实例时调用__new__初始化一个实例时用__init__
浙公网安备 33010602011771号