一、构造&初始化(new&init)
__init__我们很熟悉了,它在对象初始化的时候调用,我们一般将它理解为"构造函数".
实际上, 当我们调用x = SomeClass()的时候调用,__init__并不是第一个执行的, __new__才是。所以准确来说,是__new__和__init__共同构成了"构造函数".
__new__是用来创建类并返回这个类的实例, 而__init__只是将传入的参数来初始化该实例.
__new__在创建一个实例的过程中必定会被调用,但__init__就不一定,比如通过pickle.load的方式反序列化一个实例时就不会调用__init__。
__new__方法总是需要返回该类的一个实例,而__init__不能返回除了None的任何值。比如下面例子:
classFoo(object):
def__init__(self):
print 'foo __init__'
return None # 必须返回None,否则抛TypeError
def__del__(self):
print 'foo __del__'
实际中,你很少会用到__new__,除非你希望能够控制类的创建。
如果要讲解__new__,往往需要牵扯到metaclass(元类)的介绍。
对于__new__的重载,详见python官方文档,有详细的介绍。
在对象的生命周期结束时, __del__会被调用,可以将__del__理解为"析构函数".
__del__定义的是当一个对象进行垃圾回收时候的行为。
有一点容易被人误解, 实际上,x.__del__() 并不是对于del x的实现,但是往往执行del x时会调用x.__del__()。
怎么来理解这句话呢? 继续用上面的Foo类的代码为例:
foo = Foo()
foo.__del__()
print foo
del foo
print foo # NameError, foo is not defined
如果调用了foo.__del__(),对象本身仍然存在. 但是调用了del foo, 就再也没有foo这个对象了.
请注意,如果解释器退出的时候对象还存在,就不能保证 __del__ 被确切的执行了。所以__del__并不能替代良好的编程习惯。比如,在处理socket时,及时关闭结束的连接。
二、属性访问(getattr、setattr、delattr)
总有人要吐槽Python缺少对于类的封装,比如希望Python能够定义私有属性,然后提供公共可访问的getter和 setter。Python其实可以通过魔术方法来实现封装。
__getattr__(self, name)
该方法定义了你试图访问一个不存在的属性时的行为。因此,重载该方法可以实现捕获错误拼写然后进行重定向, 或者对一些废弃的属性进行警告。
__setattr__(self, name, value)
__setattr__是实现封装的解决方案,它定义了你对属性进行赋值和修改操作时的行为。
不管对象的某个属性是否存在,它都允许你为该属性进行赋值,因此你可以为属性的值进行自定义操作。有一点需要注意,实现__setattr__时要避免"无限递归"的错误,下面的代码示例中会提到。
__delattr__(self, name)
__delattr__与__setattr__很像,只是它定义的是你删除属性时的行为。实现__delattr__也同时要避免"无限递归"的错误。
下例就是说明了一下上述三种方法的调用
class Access(object):
def__getattr__(self, name):
print '__getattr__'
return super(Access, self).__getattr__(name)
def__setattr__(self, name, value):
print '__setattr__'
return super(Access, self).__setattr__(name, value)
def__delattr__(self, name):
print '__delattr__'
return super(Access, self).__delattr__(name)
access = Access()
access.attr1 = True # __setattr__调用
try:
access.attr2 # 属性不存在, 调用__getattr__
except AttributeError:
pass
del access.attr1 # __delattr__调用
三、容器构造(list/dict/tuple/string)
其中tuple, string是不可变的,dict, list是可变的。
可变和不可变的区别在于,不可变一旦赋值后,不可对其中的某个元素进行修改。
比如定义了l = [1, 2, 3]和t = (1, 2, 3)后, 执行l[0] = 0是可以的,但执行t[0] = 0则会报错。
如果我们要自定义一些数据结构,使之能够跟以上的类型表现一样,那就需要去实现某些协议。
这里的协议跟其他语言中所谓的"接口"概念很像,一样的需要你去实现才行,只不过没那么正式而已。
如果要自定义不可变容器类型,只需要定义__len__ 和 __getitem__方法;
如果要自定义可变容器类型,还需要在不可变容器类型的基础上增加定义__setitem__ 和 __delitem__。
如果你希望你的自定义数据结构还支持"可迭代", 那就还需要定义__iter__。
__len__(self)
需要返回数值类型,以表示容器的长度。该方法在可变容器和不可变容器中必须实现。
__getitem__(self, key)
当你执行self[key]的时候,调用的就是该方法。该方法在可变容器和不可变容器中也都必须实现。
调用的时候,如果key的类型错误,该方法应该抛出TypeError;
如果没法返回key对应的数值时,该方法应该抛出ValueError。
__setitem__(self, key, value)
当你执行self[key] = value时,调用的是该方法。
__delitem__(self, key)
当你执行del self[key]的时候,调用的是该方法。
__iter__(self)
该方法需要返回一个迭代器(iterator)。当你执行for x in container:或者使用iter(container)时,该方法被调用。
__reversed__(self)
如果想要该数据结构被內建函数reversed()支持,就还需要实现该方法。
__contains__(self, item)
如果定义了该方法,那么在执行item in container 或者 item not in container时该方法就会被调用。
如果没有定义,那么Python会迭代容器中的元素来一个一个比较,从而决定返回True或者False。
__missing__(self, key)
dict字典类型会有该方法,它定义了key如果在容器中找不到时触发的行为。
比如d = {'a': 1}, 当你执行d[notexist]时,d.__missing__('notexist')就会被调用。
下面的例子就是我们如何自定义一个类实现python内置list的方法:
class FunctionalList:
''' 实现了内置类型list的功能,并丰富了一些其他方法: head, tail, init, last, drop, take'''
def __init__(self, values=None):
if values is None:
self.values = []
else:
self.values = values
def __len__(self):
return len(self.values)
def __getitem__(self, key):
return self.values[key]
def __setitem__(self, key, value):
self.values[key] = value
def __delitem__(self, key):
del self.values[key]
def __iter__(self):
return iter(self.values)
def __reversed__(self):
return FunctionalList(reversed(self.values))
def append(self, value):
self.values.append(value)
def head(self):
# 获取第一个元素
return self.values[0]
def tail(self):
# 获取第一个元素之后的所有元素
return self.values[1:]
def init(self):
# 获取最后一个元素之前的所有元素
return self.values[:-1]
def last(self):
# 获取最后一个元素
return self.values[-1]
def drop(self, n):
# 获取所有元素,除了前N个
return self.values[n:]
def take(self, n):
# 获取前N个元素
return self.values[:n]
原文链接:https://blog.csdn.net/qq_30108237/article/details/106453277