Python你必须了解的一些点——魔法方法

一、构造&初始化(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其实可以通过魔术方法来实现封装。

  1. __getattr__(self, name)
    该方法定义了你试图访问一个不存在的属性时的行为。因此,重载该方法可以实现捕获错误拼写然后进行重定向, 或者对一些废弃的属性进行警告。

  2. __setattr__(self, name, value)
    __setattr__是实现封装的解决方案,它定义了你对属性进行赋值和修改操作时的行为。
    不管对象的某个属性是否存在,它都允许你为该属性进行赋值,因此你可以为属性的值进行自定义操作。有一点需要注意,实现__setattr__时要避免"无限递归"的错误,下面的代码示例中会提到。

  3. __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]
  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值