python __reduce__魔法方法_Python的魔法方法

1.简介

魔法方法是python内置方法,不需要主动调用。

魔法方法存在的目的是为了给python的解释器进行调用。

几乎每个魔法方法都有一个对应的内置函数,或者运算符。

当我们对这个对象使用这些内置函数或者运算符时,就会调用类中的对应魔法方法,可以理解为重写内置函数。

2.详细介绍

2.1 构造和初始化

_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文档中也有了详细的介绍。

2.2 析构函数

在对象的生命周期结束时, _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时,及时关闭结束的连接。

2.3 属性访问控制

总有人要吐槽Python缺少对于类的封装,比如希望Python能够定义私有属性,然后提供公共可访问的getter和 setter。Python其实可以通过魔术方法来实现封装。

_getattr_(self, name)

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

_setattr_(self, name, value)

_setattr_ 是实现封装的解决方案,它定义了你对属性进行赋值和修改操作时的行为。

不管对象的某个属性是否存在,它都允许你为该属性进行赋值,因此你可以为属性的值进行自定义操作。有一点需要注意,实现_setattr_时要避免"无限递归"的错误,下面的代码示例中会提到。

_delattr_(self, name)

_delattr_与_setattr_很像,只是它定义的是你删除属性时的行为。

实现_delattr_是同时要避免"无限递归"的错误。

_getattribute_(self, name)

_getattribute_定义了你的属性被访问时的行为,相比较,_getattr_只有该属性不存在时才会起作用。

因此,在支持_getattribute_的Python版本,调用_getattr_前必定会调用 _getattribute_。_getattribute_同样要避免"无限递归"的错误。

需要提醒的是,最好不要尝试去实现_getattribute_,因为很少见到这种做法,而且很容易出bug。

例子说明_setattr_的无限递归错误:

def__setattr__(self, name, value):

self.name = value

# 每一次属性赋值时, __setattr__都会被调用,因此不断调用自身导致无限递归了。

因此正确的写法应该是:

def__setattr__(self, name, value):

self.__dict__[name] = value

_delattr_如果在其实现中出现del self.name 这样的代码也会出现"无限递归"错误,这是一样的原因。

下面的例子很好的说明了上面介绍的4个魔术方法的调用情况:

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)

def__getattribute__(self, name):

print '__getattribute__'

return super(Access, self).__getattribute__(name)

access = Access()

access.attr1 = True # __setattr__调用

access.attr1 # 属性存在,只有__getattribute__调用

try:

access.attr2 # 属性不存在, 先调用__getattribute__, 后调用__getattr__

except AttributeError:

pass

del access.attr1 # __delattr__调用

2.4 描述符对象

我们从一个例子来入手,介绍什么是描述符,并介绍_get_,_set_, _delete_ 的使用。(放在这里介绍是为了跟上一小节介绍的魔术方法作对比)

我们知道,距离既可以用单位"米"表示,也可以用单位"英尺"表示。

现在我们定义一个类来表示距离,它有两个属性: 米和英尺。

class Meter(object):

'''Descriptor for a meter.'''

def__init__(self, value=0.0):

self.value = float(value)

def__get__(self, instance, owner):

return self.value

def__set__(self, instance, value):

self.value = float(value)

class Foot(object):

'''Descriptor for a foot.'''

def__get__(self, instance, owner):

return instance.meter * 3.2808

def__set__(self, instance, value):

instance.meter = float(value) / 3.2808

class Distance(object):

meter = Meter()

foot = Foot()

d = Distance()

print d.meter, d.foot # 0.0, 0.0

d.meter = 1

print d.meter, d.foot # 1.0 3.2808

d.meter = 2

print d.meter, d.foot # 2.0 6.5616

在上面例子中,在还没有对Distance的实例赋值前, 我们认为meter和foot应该是各自类的实例对象, 但是输出却是数值。这是因为get发挥了作用.

我们

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值