python的常用魔法方法详细总结
<div id="cnblogs_post_body" class="blogpost-body"><p> </p>
<h2 id="articleHeader2">构造和初始化</h2>
<div class="article fmt article__content" data-id="1190000007256392" data-license="nd">
<p><code>__init__</code>我们很熟悉了,它在对象初始化的时候调用,我们一般将它理解为"构造函数".</p>
<p>实际上, 当我们调用<code>x = SomeClass()</code>的时候调用,<code>__init__</code>并不是第一个执行的, <code>__new__</code>才是。所以准确来说,是<code>__new__</code>和<code>__init__</code>共同构成了"构造函数".</p>
<p><code>__new__</code>是用来创建类并返回这个类的实例, 而<code>__init__</code>只是将传入的参数来初始化该实例.</p>
<p><code>__new__</code>在创建一个实例的过程中必定会被调用,但<code>__init__</code>就不一定,比如通过<code>pickle.load</code>的方式反序列化一个实例时就不会调用<code>__init__</code>。</p>
<p><code>__new__</code>方法总是需要返回该类的一个实例,而<code>__init__</code>不能返回除了None的任何值。比如下面例子:</p>
<pre class="python hljs"><code class="python hljs"><span class="hljs-class"><span class="hljs-keyword"><span class="hljs-class"><span class="hljs-keyword"><span class="hljs-class"><span class="hljs-keyword">class</span></span></span></span><span class="hljs-title"><span class="hljs-class"><span class="hljs-title"><span class="hljs-class"><span class="hljs-title">Foo</span></span></span></span><span class="hljs-params"><span class="hljs-class"><span class="hljs-params"><span class="hljs-class"><span class="hljs-params">(object)</span></span></span><span class="hljs-class">:</span></span>
<span class="hljs-function"><span class="hljs-keyword"><span class="hljs-function"><span class="hljs-keyword"><span class="hljs-function"><span class="hljs-keyword">def</span></span></span></span><span class="hljs-title"><span class="hljs-function"><span class="hljs-title"><span class="hljs-function"><span class="hljs-title">__init__</span></span></span></span><span class="hljs-params"><span class="hljs-function"><span class="hljs-params"><span class="hljs-function"><span class="hljs-params">(self)</span></span></span><span class="hljs-function">:</span></span>
<span class="hljs-keyword"><span class="hljs-keyword"><span class="hljs-keyword">print</span></span> <span class="hljs-string"><span class="hljs-string"><span class="hljs-string">'foo __init__'</span></span>
<span class="hljs-keyword"><span class="hljs-keyword"><span class="hljs-keyword">return</span></span> <span class="hljs-keyword"><span class="hljs-keyword"><span class="hljs-keyword">None</span></span><span class="hljs-comment"><span class="hljs-comment"><span class="hljs-comment"># 必须返回None,否则抛TypeError</span></span>
<span class="hljs-function"><span class="hljs-keyword"><span class="hljs-function"><span class="hljs-keyword"><span class="hljs-function"><span class="hljs-keyword">def</span></span></span></span><span class="hljs-title"><span class="hljs-function"><span class="hljs-title"><span class="hljs-function"><span class="hljs-title">__del__</span></span></span></span><span class="hljs-params"><span class="hljs-function"><span class="hljs-params"><span class="hljs-function"><span class="hljs-params">(self)</span></span></span><span class="hljs-function">:</span></span>
<span class="hljs-keyword"><span class="hljs-keyword"><span class="hljs-keyword">print</span></span> <span class="hljs-string"><span class="hljs-string"><span class="hljs-string">'foo __del__'</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre>
<p>实际中,你很少会用到<code>__new__</code>,除非你希望能够控制类的创建。<br>如果要讲解<code>__new__</code>,往往需要牵扯到<code>metaclass</code>(元类)的介绍。</p>
<p>对于<code>__new__</code>的重载,Python文档中也有了详细的介绍。</p>
<p>在对象的生命周期结束时, <code>__del__</code>会被调用,可以将<code>__del__</code>理解为"析构函数".<br><code>__del__</code>定义的是当一个对象进行垃圾回收时候的行为。</p>
<p>有一点容易被人误解, 实际上,<code>x.__del__()</code> 并不是对于<code>del x</code>的实现,但是往往执行<code>del x</code>时会调用<code>x.__del__()</code>.</p>
<p>怎么来理解这句话呢? 继续用上面的Foo类的代码为例:</p>
<pre class="python hljs"><code class="python hljs">foo = Foo()
foo.__del__()
<span class="hljs-keyword"><span class="hljs-keyword"><span class="hljs-keyword">print</span></span> foo
<span class="hljs-keyword"><span class="hljs-keyword"><span class="hljs-keyword">del</span></span> foo
<span class="hljs-keyword"><span class="hljs-keyword"><span class="hljs-keyword">print</span></span> foo<span class="hljs-comment"><span class="hljs-comment"><span class="hljs-comment"># NameError, foo is not defined</span></span></span></span></span></span></code></pre>
<p>如果调用了<code>foo.__del__()</code>,对象本身仍然存在. 但是调用了<code>del foo</code>, 就再也没有foo这个对象了.</p>
<p>请注意,如果解释器退出的时候对象还存在,就不能保证 <code>__del__</code> 被确切的执行了。所以<code>__del__</code>并不能替代良好的编程习惯。<br>比如,在处理socket时,及时关闭结束的连接。</p>
<h2 id="articleHeader2">属性访问控制</h2>
<p>总有人要吐槽Python缺少对于类的封装,比如希望Python能够定义私有属性,然后提供公共可访问的getter和 setter。Python其实可以通过魔术方法来实现封装。</p>
<p><code>__getattr__(self, name)</code></p>
<p>该方法定义了你试图访问一个不存在的属性时的行为。因此,重载该方法可以实现捕获错误拼写然后进行重定向, 或者对一些废弃的属性进行警告。</p>
<p><code>__setattr__(self, name, value)</code></p>
<p><code>__setattr__</code> 是实现封装的解决方案,它定义了你对属性进行赋值和修改操作时的行为。<br>不管对象的某个属性是否存在,它都允许你为该属性进行赋值,因此你可以为属性的值进行自定义操作。有一点需要注意,实现<code>__setattr__</code>时要避免"无限递归"的错误,下面的代码示例中会提到。</p>
<p><code>__delattr__(self, name)</code></p>
<p><code>__delattr__</code>与<code>__setattr__</code>很像,只是它定义的是你删除属性时的行为。实现<code>__delattr__</code>是同时要避免"无限递归"的错误。</p>
<p><code>__getattribute__(self, name)</code></p>
<p><code>__getattribute__</code>定义了你的属性被访问时的行为,相比较,<code>__getattr__</code>只有该属性不存在时才会起作用。<br>因此,在支持<code>__getattribute__</code>的Python版本,调用<code>__getattr__</code>前必定会调用 <code>__getattribute__</code>。<code>__getattribute__</code>同样要避免"无限递归"的错误。<br>需要提醒的是,最好不要尝试去实现<code>__getattribute__</code>,因为很少见到这种做法,而且很容易出bug。</p>
<p>例子说明<code>__setattr__</code>的无限递归错误:</p>
<pre class="python hljs"><code class="python hljs"><span class="hljs-function"><span class="hljs-keyword"><span class="hljs-function"><span class="hljs-keyword"><span class="hljs-function"><span class="hljs-keyword">def</span></span></span></span><span class="hljs-title"><span class="hljs-function"><span class="hljs-title"><span class="hljs-function"><span class="hljs-title">__setattr__</span></span></span></span><span class="hljs-params"><span class="hljs-function"><span class="hljs-params"><span class="hljs-function"><span class="hljs-params">(self, name, value)</span></span></span><span class="hljs-function">:</span></span>
self.name = value
<span class="hljs-comment"><span class="hljs-comment"><span class="hljs-comment"># 每一次属性赋值时, __setattr__都会被调用,因此不断调用自身导致无限递归了。</span></span></span></span></span></span></span></code></pre>
<p>因此正确的写法应该是:</p>
<pre class="python hljs"><code class="python hljs"><span class="hljs-function"><span class="hljs-keyword"><span class="hljs-function"><span class="hljs-keyword"><span class="hljs-function"><span class="hljs-keyword">def</span></span></span></span><span class="hljs-title"><span class="hljs-function"><span class="hljs-title"><span class="hljs-function"><span class="hljs-title">__setattr__</span></span></span></span><span class="hljs-params"><span class="hljs-function"><span class="hljs-params"><span class="hljs-function"><span class="hljs-params">(self, name, value)</span></span></span><span class="hljs-function">:</span></span>
self.__dict__ = value</span></span></span></span></code></pre>
<p><code>__delattr__</code>如果在其实现中出现<code>del self.name</code> 这样的代码也会出现"无限递归"错误,这是一样的原因。</p>
<p>下面的例子很好的说明了上面介绍的4个魔术方法的调用情况:</p>
<pre class="python hljs"><code class="python hljs"><span class="hljs-class"><span class="hljs-keyword"><span class="hljs-class"><span class="hljs-keyword"><span class="hljs-class"><span class="hljs-keyword">class</span></span></span></span><span class="hljs-title"><span class="hljs-class"><span class="hljs-title"><span class="hljs-class"><span class="hljs-title">Access</span></span></span></span><span class="hljs-params"><span class="hljs-class"><span class="hljs-params"><span class="hljs-class"><span class="hljs-params">(object)</span></span></span><span class="hljs-class">:</span></span>
<span class="hljs-function"><span class="hljs-keyword"><span class="hljs-function"><span class="hljs-keyword"><span class="hljs-function"><span class="hljs-keyword">def</span></span></span></span><span class="hljs-title"><span class="hljs-function"><span class="hljs-title"><span class="hljs-function"><span class="hljs-title">__getattr__</span></span></span></span><span class="hljs-params"><span class="hljs-function"><span class="hljs-params"><span class="hljs-function"><span class="hljs-params">(self, name)</span></span></span><span class="hljs-function">:</span></span>
<span class="hljs-keyword"><span class="hljs-keyword"><span class="hljs-keyword">print</span></span> <span class="hljs-string"><span class="hljs-string"><span class="hljs-string">'__getattr__'</span></span>
<span class="hljs-keyword"><span class="hljs-keyword"><span class="hljs-keyword">return</span></span> super(Access, self).__getattr__(name)
<span class="hljs-function"><span class="hljs-keyword"><span class="hljs-function"><span class="hljs-keyword"><span class="hljs-function"><span class="hljs-keyword">def</span></span></span></span><span class="hljs-title"><span class="hljs-function"><span class="hljs-title"><span class="hljs-function"><span class="hljs-title">__setattr__</span></span></span></span><span class="hljs-params"><span class="hljs-function"><span class="hljs-params"><span class="hljs-function"><span class="hljs-params">(self, name, value)</span></span></span><span class="hljs-function">:</span></span>
<span class="hljs-keyword"><span class="hljs-keyword"><span class="hljs-keyword">print</span></span> <span class="hljs-string"><span class="hljs-string"><span class="hljs-string">'__setattr__'</span></span>
<span class="hljs-keyword"><span class="hljs-keyword"><span class="hljs-keyword">return</span></span> super(Access, self).__setattr__(name, value)
<span class="hljs-function"><span class="hljs-keyword"><span class="hljs-function"><span class="hljs-keyword"><span class="hljs-function"><span class="hljs-keyword">def</span></span></span></span><span class="hljs-title"><span class="hljs-function"><span class="hljs-title"><span class="hljs-function"><span class="hljs-title">__delattr__</span></span></span></span><span class="hljs-params"><span class="hljs-function"><span class="hljs-params"><span class="hljs-function"><span class="hljs-params">(self, name)</span></span></span><span class="hljs-function">:</span></span>
<span class="hljs-keyword"><span class="hljs-keyword"><span class="hljs-keyword">print</span></span> <span class="hljs-string"><span class="hljs-string"><span class="hljs-string">'__delattr__'</span></span>
<span class="hljs-keyword"><span class="hljs-keyword"><span class="hljs-keyword">return</span></span> super(Access, self).__delattr__(name)
<span class="hljs-function"><span class="hljs-keyword"><span class="hljs-function"><span class="hljs-keyword"><span class="hljs-function"><span class="hljs-keyword">def</span></span></span></span><span class="hljs-title"><span class="hljs-function"><span class="hljs-title"><span class="hljs-function"><span class="hljs-title">__getattribute__</span></span></span></span><span class="hljs-params"><span class="hljs-function"><span class="hljs-params"><span class="hljs-function"><span class="hljs-params">(self, name)</span></span></span><span class="hljs-function">:</span></span>
<span class="hljs-keyword"><span class="hljs-keyword"><span class="hljs-keyword">print</span></span> <span class="hljs-string"><span class="hljs-string"><span class="hljs-string">'__getattribute__'</span></span>
<span class="hljs-keyword"><span class="hljs-keyword"><span class="hljs-keyword">return</span></span> super(Access, self).__getattribute__(name)
access = Access()
access.attr1 = <span class="hljs-keyword"><span class="hljs-keyword"><span class="hljs-keyword">True</span></span><span class="hljs-comment"><span class="hljs-comment"><span class="hljs-comment"># __setattr__调用</span></span>
access.attr1<span class="hljs-comment"><span class="hljs-comment"><span class="hljs-comment"># 属性存在,只有__getattribute__调用</span></span>
<span class="hljs-keyword"><span class="hljs-keyword"><span class="hljs-keyword">try</span></span>:
access.attr2<span class="hljs-comment"><span class="hljs-comment"><span class="hljs-comment"># 属性不存在, 先调用__getattribute__, 后调用__getattr__</span></span>
<span class="hljs-keyword"><span class="hljs-keyword"><span class="hljs-keyword">except</span></span> AttributeError:
<span class="hljs-keyword"><span class="hljs-keyword"><span class="hljs-keyword">pass</span></span>
<span class="hljs-keyword"><span class="hljs-keyword"><span class="hljs-keyword">del</span></span> access.attr1<span class="hljs-comment"><span class="hljs-comment"><span class="hljs-comment"># __delattr__调用</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre>
<h2 id="articleHeader3">描述器对象</h2>
<p>我们从一个例子来入手,介绍什么是描述符,并介绍<code>__get__</code>, <code>__set__</code>, <code>__delete__</code> 的使用。(放在这里介绍是为了跟上一小节介绍的魔术方法作对比)</p>
<p>我们知道,距离既可以用单位"米"表示,也可以用单位"英尺"表示。<br>现在我们定义一个类来表示距离,它有两个属性: 米和英尺。</p>
<pre class="python hljs"><code class="python hljs"><span class="hljs-class"><span class="hljs-keyword"><span class="hljs-class"><span class="hljs-keyword"><span class="hljs-class"><span class="hljs-keyword">class</span></span></span></span><span class="hljs-title"><span class="hljs-class"><span class="hljs-title"><span class="hljs-class"><span class="hljs-title">Meter</span></span></span></span><span class="hljs-params"><span class="hljs-class"><span class="hljs-params"><span class="hljs-class"><span class="hljs-params">(object)</span></span></span><span class="hljs-class">:</span></span>
<span class="hljs-string"><span class="hljs-string"><span class="hljs-string">'''Descriptor for a meter.'''</span></span>
<span class="hljs-function"><span class="hljs-keyword"><span class="hljs-function"><span class="hljs-keyword"><span class="hljs-function"><span class="hljs-keyword">def</span></span></span></span><span class="hljs-title"><span class="hljs-function"><span class="hljs-title"><span class="hljs-function"><span class="hljs-title">__init__</span></span></span></span><span class="hljs-params"><span class="hljs-function"><span class="hljs-params"><span class="hljs-function"><span class="hljs-params">(self, value=</span></span></span></span><span class="hljs-number"><span class="hljs-function"><span class="hljs-params"><span class="hljs-number"><span class="hljs-function"><span class="hljs-params"><span class="hljs-number">0.0</span></span></span></span><span class="hljs-function"><span class="hljs-params">)</span></span></span><span class="hljs-function">:</span></span>
self.value = float(value)
<span class="hljs-function"><span class="hljs-keyword"><span class="hljs-function"><span class="hljs-keyword"><span class="hljs-function"><span class="hljs-keyword">def</span></span></span></span><span class="hljs-title"><span class="hljs-function"><span class="hljs-title"><span class="hljs-function"><span class="hljs-title">__get__</span></span></span></span><span class="hljs-params"><span class="hljs-function"><span class="hljs-params"><span class="hljs-function"><span class="hljs-params">(self, instance, owner)</span></span></span><span class="hljs-function">:</span></span>
<span class="hljs-keyword"><span class="hljs-keyword"><span class="hljs-keyword">return</span></span> self.value
<span class="hljs-function"><span class="hljs-keyword"><span class="hljs-function"><span class="hljs-keyword"><span class="hljs-function"><span class="hljs-keyword">def</span></span></span></span><span class="hljs-title"><span class="hljs-function"><span class="hljs-title"><span class="hljs-function"><span class="hljs-title">__set__</span></span></span></span><span class="hljs-params"><span class="hljs-function"><span class="hljs-params"><span class="hljs-function"><span class="hljs-params">(self, instance, value)</span></span></span><span class="hljs-function">:</span></span>
self.value = float(value)
<span class="hljs-class"><span class="hljs-keyword"><span class="hljs-class"><span class="hljs-keyword"><span class="hljs-class"><span class="hljs-keyword">class</span></span></span></span><span class="hljs-title"><span class="hljs-class"><span class="hljs-title"><span class="hljs-class"><span class="hljs-title">Foot</span></span></span></span><span class="hljs-params"><span class="hljs-class"><span class="hljs-params"><span class="hljs-class"><span class="hljs-params">(object)</span></span></span><span class="hljs-class">:</span></span>
<span class="hljs-string"><span class="hljs-string"><span class="hljs-string">'''Descriptor for a foot.'''</span></span>
<span class="hljs-function"><span class="hljs-keyword"><span class="hljs-function"><span class="hljs-keyword"><span class="hljs-function"><span class="hljs-keyword">def</span></span></span></span><span class="hljs-title"><span class="hljs-function"><span class="hljs-title"><span class="hljs-function"><span class="hljs-title">__get__</span></span></span></span><span class="hljs-params"><span class="hljs-function"><span class="hljs-params"><span class="hljs-function"><span class="hljs-params">(self, instance, owner)</span></span></span><span class="hljs-function">:</span></span>
<span class="hljs-keyword"><span class="hljs-keyword"><span class="hljs-keyword">return</span></span> instance.meter * <span class="hljs-number"><span class="hljs-number"><span class="hljs-number">3.2808</span></span>
<span class="hljs-function"><span class="hljs-keyword"><span class="hljs-function"><span class="hljs-keyword"><span class="hljs-function"><span class="hljs-keyword">def</span></span></span></span><span class="hljs-title"><span class="hljs-function"><span class="hljs-title"><span class="hljs-function"><span class="hljs-title">__set__</span></span></span></span><span class="hljs-params"><span class="hljs-function"><span class="hljs-params"><span class="hljs-function"><span class="hljs-params">(self, instance, value)</span></span></span><span class="hljs-function">:</span></span>
instance.meter = float(value) / <span class="hljs-number"><span class="hljs-number"><span class="hljs-number">3.2808</span></span>
<span class="hljs-class"><span class="hljs-keyword"><span class="hljs-class"><span class="hljs-keyword"><span class="hljs-class"><span class="hljs-keyword">class</span></span></span></span><span class="hljs-title"><span class="hljs-class"><span class="hljs-title"><span class="hljs-class"><span class="hljs-title">Distance</span></span></span></span><span class="hljs-params"><span class="hljs-class"><span class="hljs-params"><span class="hljs-class"><span class="hljs-params">(object)</span></span></span><span class="hljs-class">:</span></span>
meter = Meter()
foot = Foot()
d = Distance()
<span class="hljs-keyword"><span class="hljs-keyword"><span class="hljs-keyword">print</span></span> d.meter, d.foot<span class="hljs-comment"><span class="hljs-comment"><span class="hljs-comment"># 0.0, 0.0</span></span>
d.meter = <span class="hljs-number"><span class="hljs-number"><span class="hljs-number">1</span></span>
<span class="hljs-keyword"><span class="hljs-keyword"><span class="hljs-keyword">print</span></span> d.meter, d.foot<span class="hljs-comment"><span class="hljs-comment"><span class="hljs-comment"># 1.0 3.2808</span></span>
d.meter = <span class="hljs-number"><span class="hljs-number"><span class="hljs-number">2</span></span>
<span class="hljs-keyword"><span class="hljs-keyword"><span class="hljs-keyword">print</span></span> d.meter, d.foot<span class="hljs-comment"><span class="hljs-comment"><span class="hljs-comment"># 2.0 6.5616</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre>
<p>在上面例子中,在还没有对Distance的实例赋值前, 我们认为meter和foot应该是各自类的实例对象, 但是输出却是数值。这是因为<code>__get__</code>发挥了作用.</p>
<p>我们只是修改了meter,并且将其赋值成为int,但foot也修改了。这是<code>__set__</code>发挥了作用.</p>
<p>描述器对象(Meter、Foot)不能独立存在, 它需要被另一个所有者类(Distance)所持有。<br>描述器对象可以访问到其拥有者实例的属性,比如例子中Foot的<code>instance.meter</code>。</p>
<p>在面向对象编程时,如果一个类的属性有相互依赖的关系时,使用描述器来编写代码可以很巧妙的组织逻辑。<br>在Django的ORM中, models.Model中的IntegerField等, 就是通过描述器来实现功能的。</p>
<p>一个类要成为描述器,必须实现<code>__get__</code>, <code>__set__</code>, <code>__delete__</code> 中的至少一个方法。下面简单介绍下:</p>
<p><code>__get__(self, instance, owner)</code></p>
<p>参数instance是拥有者类的实例。参数owner是拥有者类本身。<code>__get__</code>在其拥有者对其读值的时候调用。</p>
<p><code>__set__(self, instance, value)</code></p>
<p><code>__set__</code>在其拥有者对其进行修改值的时候调用。</p>
<p><code>__delete__(self, instance)</code></p>
<p><code>__delete__</code>在其拥有者对其进行删除的时候调用。</p>
<h2 id="articleHeader4">构造自定义容器(Container)</h2>
<p>在Python中,常见的容器类型有: dict, tuple, list, string。<br>其中tuple, string是不可变容器,dict, list是可变容器。<br>可变容器和不可变容器的区别在于,不可变容器一旦赋值后,不可对其中的某个元素进行修改。<br>比如定义了<code>l = </code>和<code>t = (1, 2, 3)</code>后, 执行<code>l = 0</code>是可以的,但执行<code>t = 0</code>则会报错。</p>
<p>如果我们要自定义一些数据结构,使之能够跟以上的容器类型表现一样,那就需要去实现某些协议。</p>
<p>这里的协议跟其他语言中所谓的"接口"概念很像,一样的需要你去实现才行,只不过没那么正式而已。</p>
<p>如果要自定义不可变容器类型,只需要定义<code>__len__</code> 和 <code>__getitem__</code>方法;<br>如果要自定义可变容器类型,还需要在不可变容器类型的基础上增加定义<code>__setitem__</code> 和 <code>__delitem__</code>。<br>如果你希望你的自定义数据结构还支持"可迭代", 那就还需要定义<code>__iter__</code>。</p>
<p><code>__len__(self)</code></p>
<p>需要返回数值类型,以表示容器的长度。该方法在可变容器和不可变容器中必须实现。</p>
<p><code>__getitem__(self, key)</code></p>
<p>当你执行<code>self</code>的时候,调用的就是该方法。该方法在可变容器和不可变容器中也都必须实现。<br>调用的时候,如果key的类型错误,该方法应该抛出TypeError;<br>如果没法返回key对应的数值时,该方法应该抛出ValueError。</p>
<p><code>__setitem__(self, key, value)</code></p>
<p>当你执行<code>self = value</code>时,调用的是该方法。</p>
<p><code>__delitem__(self, key)</code></p>
<p>当你执行<code>del self</code>的时候,调用的是该方法。</p>
<p><code>__iter__(self)</code></p>
<p>该方法需要返回一个迭代器(iterator)。当你执行<code>for x in container:</code> 或者使用<code>iter(container)</code>时,该方法被调用。</p>
<p><code>__reversed__(self)</code></p>
<p>如果想要该数据结构被內建函数<code>reversed()</code>支持,就还需要实现该方法。</p>
<p><code>__contains__(self, item)</code></p>
<p>如果定义了该方法,那么在执行<code>item in container</code> 或者 <code>item not in container</code>时该方法就会被调用。<br>如果没有定义,那么Python会迭代容器中的元素来一个一个比较,从而决定返回True或者False。</p>
<p><code>__missing__(self, key)</code></p>
<p><code>dict</code>字典类型会有该方法,它定义了key如果在容器中找不到时触发的行为。<br>比如<code>d = {'a': 1}</code>, 当你执行<code>d</code>时,<code>d.__missing__('notexist')</code>就会被调用。</p>
<p>下面举例,使用上面讲的魔术方法来实现Haskell语言中的一个数据结构。</p>
<pre class="python hljs"><code class="python hljs"><span class="hljs-comment"><span class="hljs-comment"><span class="hljs-comment"># -*- coding: utf-8 -*-</span></span>
<span class="hljs-class"><span class="hljs-keyword"><span class="hljs-class"><span class="hljs-keyword"><span class="hljs-class"><span class="hljs-keyword">class</span></span></span></span><span class="hljs-title"><span class="hljs-class"><span class="hljs-title"><span class="hljs-class"><span class="hljs-title">FunctionalList</span></span></span><span class="hljs-class">:</span></span>
<span class="hljs-string"><span class="hljs-string"><span class="hljs-string">''' 实现了内置类型list的功能,并丰富了一些其他方法: head, tail, init, last, drop, take'''</span></span>
<span class="hljs-function"><span class="hljs-keyword"><span class="hljs-function"><span class="hljs-keyword"><span class="hljs-function"><span class="hljs-keyword">def</span></span></span></span><span class="hljs-title"><span class="hljs-function"><span class="hljs-title"><span class="hljs-function"><span class="hljs-title">__init__</span></span></span></span><span class="hljs-params"><span class="hljs-function"><span class="hljs-params"><span class="hljs-function"><span class="hljs-params">(self, values=None)</span></span></span><span class="hljs-function">:</span></span>
<span class="hljs-keyword"><span class="hljs-keyword"><span class="hljs-keyword">if</span></span> values <span class="hljs-keyword"><span class="hljs-keyword"><span class="hljs-keyword">is</span></span> <span class="hljs-keyword"><span class="hljs-keyword"><span class="hljs-keyword">None</span></span>:
self.values = []
<span class="hljs-keyword"><span class="hljs-keyword"><span class="hljs-keyword">else</span></span>:
self.values = values
<span class="hljs-function"><span class="hljs-keyword"><span class="hljs-function"><span class="hljs-keyword"><span class="hljs-function"><span class="hljs-keyword">def</span></span></span></span><span class="hljs-title"><span class="hljs-function"><span class="hljs-title"><span class="hljs-function"><span class="hljs-title">__len__</span></span></span></span><span class="hljs-params"><span class="hljs-function"><span class="hljs-params"><span class="hljs-function"><span class="hljs-params">(self)</span></span></span><span class="hljs-function">:</span></span>
<span class="hljs-keyword"><span class="hljs-keyword"><span class="hljs-keyword">return</span></span> len(self.values)
<span class="hljs-function"><span class="hljs-keyword"><span class="hljs-function"><span class="hljs-keyword"><span class="hljs-function"><span class="hljs-keyword">def</span></span></span></span><span class="hljs-title"><span class="hljs-function"><span class="hljs-title"><span class="hljs-function"><span class="hljs-title">__getitem__</span></span></span></span><span class="hljs-params"><span class="hljs-function"><span class="hljs-params"><span class="hljs-function"><span class="hljs-params">(self, key)</span></span></span><span class="hljs-function">:</span></span>
<span class="hljs-keyword"><span class="hljs-keyword"><span class="hljs-keyword">return</span></span> self.values
<span class="hljs-function"><span class="hljs-keyword"><span class="hljs-function"><span class="hljs-keyword"><span class="hljs-function"><span class="hljs-keyword">def</span></span></span></span><span class="hljs-title"><span class="hljs-function"><span class="hljs-title"><span class="hljs-function"><span class="hljs-title">__setitem__</span></span></span></span><span class="hljs-params"><span class="hljs-function"><span class="hljs-params"><span class="hljs-function"><span class="hljs-params">(self, key, value)</span></span></span><span class="hljs-function">:</span></span>
self.values = value
<span class="hljs-function"><span class="hljs-keyword"><span class="hljs-function"><span class="hljs-keyword"><span class="hljs-function"><span class="hljs-keyword">def</span></span></span></span><span class="hljs-title"><span class="hljs-function"><span class="hljs-title"><span class="hljs-function"><span class="hljs-title">__delitem__</span></span></span></span><span class="hljs-params"><span class="hljs-function"><span class="hljs-params"><span class="hljs-function"><span class="hljs-params">(self, key)</span></span></span><span class="hljs-function">:</span></span>
<span class="hljs-keyword"><span class="hljs-keyword"><span class="hljs-keyword">del</span></span> self.values
<span class="hljs-function"><span class="hljs-keyword"><span class="hljs-function"><span class="hljs-keyword"><span class="hljs-function"><span class="hljs-keyword">def</span></span></span></span><span class="hljs-title"><span class="hljs-function"><span class="hljs-title"><span class="hljs-function"><span class="hljs-title">__iter__</span></span></span></span><span class="hljs-params"><span class="hljs-function"><span class="hljs-params"><span class="hljs-function"><span class="hljs-params">(self)</span></span></span><span class="hljs-function">:</span></span>
<span class="hljs-keyword"><span class="hljs-keyword"><span class="hljs-keyword">return</span></span> iter(self.values)
<span class="hljs-function"><span class="hljs-keyword"><span class="hljs-function"><span class="hljs-keyword"><span class="hljs-function"><span class="hljs-keyword">def</span></span></span></span><span class="hljs-title"><span class="hljs-function"><span class="hljs-title"><span class="hljs-function"><span class="hljs-title">__reversed__</span></span></span></span><span class="hljs-params"><span class="hljs-function"><span class="hljs-params"><span class="hljs-function"><span class="hljs-params">(self)</span></span></span><span class="hljs-function">:</span></span>
<span class="hljs-keyword"><span class="hljs-keyword"><span class="hljs-keyword">return</span></span> FunctionalList(reversed(self.values))
<span class="hljs-function"><span class="hljs-keyword"><span class="hljs-function"><span class="hljs-keyword"><span class="hljs-function"><span class="hljs-keyword">def</span></span></span></span><span class="hljs-title"><span class="hljs-function"><span class="hljs-title"><span class="hljs-function"><span class="hljs-title">append</span></span></span></span><span class="hljs-params"><span class="hljs-function"><span class="hljs-params"><span class="hljs-function"><span class="hljs-params">(self, value)</span></span></span><span class="hljs-function">:</span></span>
self.values.append(value)
<span class="hljs-function"><span class="hljs-keyword"><span class="hljs-function"><span class="hljs-keyword"><span class="hljs-function"><span class="hljs-keyword">def</span></span></span></span><span class="hljs-title"><span class="hljs-function"><span class="hljs-title"><span class="hljs-function"><span class="hljs-title">head</span></span></span></span><span class="hljs-params"><span class="hljs-function"><span class="hljs-params"><span class="hljs-function"><span class="hljs-params">(self)</span></span></span><span class="hljs-function">:</span></span>
<span class="hljs-comment"><span class="hljs-comment"><span class="hljs-comment"># 获取第一个元素</span></span>
<span class="hljs-keyword"><span class="hljs-keyword"><span class="hljs-keyword">return</span></span> self.values[<span class="hljs-number"><span class="hljs-number"><span class="hljs-number">0</span></span>]
<span class="hljs-function"><span class="hljs-keyword"><span class="hljs-function"><span class="hljs-keyword"><span class="hljs-function"><span class="hljs-keyword">def</span></span></span></span><span class="hljs-title"><span class="hljs-function"><span class="hljs-title"><span class="hljs-function"><span class="hljs-title">tail</span></span></span></span><span class="hljs-params"><span class="hljs-function"><span class="hljs-params"><span class="hljs-function"><span class="hljs-params">(self)</span></span></span><span class="hljs-function">:</span></span>
<span class="hljs-comment"><span class="hljs-comment"><span class="hljs-comment"># 获取第一个元素之后的所有元素</span></span>
<span class="hljs-keyword"><span class="hljs-keyword"><span class="hljs-keyword">return</span></span> self.values[<span class="hljs-number"><span class="hljs-number"><span class="hljs-number">1</span></span>:]
<span class="hljs-function"><span class="hljs-keyword"><span class="hljs-function"><span class="hljs-keyword"><span class="hljs-function"><span class="hljs-keyword">def</span></span></span></span><span class="hljs-title"><span class="hljs-function"><span class="hljs-title"><span class="hljs-function"><span class="hljs-title">init</span></span></span></span><span class="hljs-params"><span class="hljs-function"><span class="hljs-params"><span class="hljs-function"><span class="hljs-params">(self)</span></span></span><span class="hljs-function">:</span></span>
<span class="hljs-comment"><span class="hljs-comment"><span class="hljs-comment"># 获取最后一个元素之前的所有元素</span></span>
<span class="hljs-keyword"><span class="hljs-keyword"><span class="hljs-keyword">return</span></span> self.values[:<span class="hljs-number"><span class="hljs-number"><span class="hljs-number">-1</span></span>]
<span class="hljs-function"><span class="hljs-keyword"><span class="hljs-function"><span class="hljs-keyword"><span class="hljs-function"><span class="hljs-keyword">def</span></span></span></span><span class="hljs-title"><span class="hljs-function"><span class="hljs-title"><span class="hljs-function"><span class="hljs-title">last</span></span></span></span><span class="hljs-params"><span class="hljs-function"><span class="hljs-params"><span class="hljs-function"><span class="hljs-params">(self)</span></span></span><span class="hljs-function">:</span></span>
<span class="hljs-comment"><span class="hljs-comment"><span class="hljs-comment"># 获取最后一个元素</span></span>
<span class="hljs-keyword"><span class="hljs-keyword"><span class="hljs-keyword">return</span></span> self.values[<span class="hljs-number"><span class="hljs-number"><span class="hljs-number">-1</span></span>]
<span class="hljs-function"><span class="hljs-keyword"><span class="hljs-function"><span class="hljs-keyword"><span class="hljs-function"><span class="hljs-keyword">def</span></span></span></span><span class="hljs-title"><span class="hljs-function"><span class="hljs-title"><span class="hljs-function"><span class="hljs-title">drop</span></span></span></span><span class="hljs-params"><span class="hljs-function"><span class="hljs-params"><span class="hljs-function"><span class="hljs-params">(self, n)</span></span></span><span class="hljs-function">:</span></span>
<span class="hljs-comment"><span class="hljs-comment"><span class="hljs-comment"># 获取所有元素,除了前N个</span></span>
<span class="hljs-keyword"><span class="hljs-keyword"><span class="hljs-keyword">return</span></span> self.values
<span class="hljs-function"><span class="hljs-keyword"><span class="hljs-function"><span class="hljs-keyword"><span class="hljs-function"><span class="hljs-keyword">def</span></span></span></span><span class="hljs-title"><span class="hljs-function"><span class="hljs-title"><span class="hljs-function"><span class="hljs-title">take</span></span></span></span><span class="hljs-params"><span class="hljs-function"><span class="hljs-params"><span class="hljs-function"><span class="hljs-params">(self, n)</span></span></span><span class="hljs-function">:</span></span>
<span class="hljs-comment"><span class="hljs-comment"><span class="hljs-comment"># 获取前N个元素</span></span>
<span class="hljs-keyword"><span class="hljs-keyword"><span class="hljs-keyword">return</span></span> self.values[:n]</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre>
<p>我们再举个例子,实现Perl语言的AutoVivification,它会在你每次引用一个值未定义的属性时为你自动创建数组或者字典。</p>
<pre class="python hljs"><code class="python hljs"><span class="hljs-class"><span class="hljs-keyword"><span class="hljs-class"><span class="hljs-keyword"><span class="hljs-class"><span class="hljs-keyword">class</span></span></span></span><span class="hljs-title"><span class="hljs-class"><span class="hljs-title"><span class="hljs-class"><span class="hljs-title">AutoVivification</span></span></span></span><span class="hljs-params"><span class="hljs-class"><span class="hljs-params"><span class="hljs-class"><span class="hljs-params">(dict)</span></span></span><span class="hljs-class">:</span></span>
<span class="hljs-string"><span class="hljs-string"><span class="hljs-string">"""Implementation of perl's autovivification feature."""</span></span>
<span class="hljs-function"><span class="hljs-keyword"><span class="hljs-function"><span class="hljs-keyword"><span class="hljs-function"><span class="hljs-keyword">def</span></span></span></span><span class="hljs-title"><span class="hljs-function"><span class="hljs-title"><span class="hljs-function"><span class="hljs-title">__missing__</span></span></span></span><span class="hljs-params"><span class="hljs-function"><span class="hljs-params"><span class="hljs-function"><span class="hljs-params">(self, key)</span></span></span><span class="hljs-function">:</span></span>
value = self = type(self)()
<span class="hljs-keyword"><span class="hljs-keyword"><span class="hljs-keyword">return</span></span> value
weather = AutoVivification()
weather[<span class="hljs-string"><span class="hljs-string"><span class="hljs-string">'china'</span></span>][<span class="hljs-string"><span class="hljs-string"><span class="hljs-string">'guangdong'</span></span>][<span class="hljs-string"><span class="hljs-string"><span class="hljs-string">'shenzhen'</span></span>] = <span class="hljs-string"><span class="hljs-string"><span class="hljs-string">'sunny'</span></span>
weather[<span class="hljs-string"><span class="hljs-string"><span class="hljs-string">'china'</span></span>][<span class="hljs-string"><span class="hljs-string"><span class="hljs-string">'hubei'</span></span>][<span class="hljs-string"><span class="hljs-string"><span class="hljs-string">'wuhan'</span></span>] = <span class="hljs-string"><span class="hljs-string"><span class="hljs-string">'windy'</span></span>
weather[<span class="hljs-string"><span class="hljs-string"><span class="hljs-string">'USA'</span></span>][<span class="hljs-string"><span class="hljs-string"><span class="hljs-string">'California'</span></span>][<span class="hljs-string"><span class="hljs-string"><span class="hljs-string">'Los Angeles'</span></span>] = <span class="hljs-string"><span class="hljs-string"><span class="hljs-string">'sunny'</span></span>
<span class="hljs-keyword"><span class="hljs-keyword"><span class="hljs-keyword">print</span></span> weather
<span class="hljs-comment"><span class="hljs-comment"><span class="hljs-comment"># 结果输出:{'china': {'hubei': {'wuhan': 'windy'}, 'guangdong': {'shenzhen': 'sunny'}}, 'USA': {'California': {'Los Angeles': 'sunny'}}}</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre>
<p>在Python中,关于自定义容器的实现还有更多实用的例子,但只有很少一部分能够集成在Python标准库中,比如Counter, OrderedDict等</p>
<h2 id="articleHeader5">上下文管理</h2>
<p><code>with</code>声明是从Python2.5开始引进的关键词。你应该遇过这样子的代码:</p>
<pre class="python hljs"><code class="python hljs"><span class="hljs-keyword"><span class="hljs-keyword"><span class="hljs-keyword">with</span></span> open(<span class="hljs-string"><span class="hljs-string"><span class="hljs-string">'foo.txt'</span></span>) <span class="hljs-keyword"><span class="hljs-keyword"><span class="hljs-keyword">as</span></span> bar:
<span class="hljs-comment"><span class="hljs-comment"><span class="hljs-comment"># do something with bar</span></span></span></span></span></span></code></pre>
<p>在with声明的代码段中,我们可以做一些对象的开始操作和清除操作,还能对异常进行处理。<br>这需要实现两个魔术方法: <code>__enter__</code> 和 <code>__exit__</code>。</p>
<p><code>__enter__(self)</code></p>
<p><code>__enter__</code>会返回一个值,并赋值给<code>as</code>关键词之后的变量。在这里,你可以定义代码段开始的一些操作。</p>
<p><code>__exit__(self, exception_type, exception_value, traceback)</code></p>
<p><code>__exit__</code>定义了代码段结束后的一些操作,可以这里执行一些清除操作,或者做一些代码段结束后需要立即执行的命令,比如文件的关闭,socket断开等。如果代码段成功结束,那么exception_type, exception_value, traceback 三个参数传进来时都将为None。如果代码段抛出异常,那么传进来的三个参数将分别为: 异常的类型,异常的值,异常的追踪栈。<br>如果<code>__exit__</code>返回True, 那么with声明下的代码段的一切异常将会被屏蔽。<br>如果<code>__exit__</code>返回None, 那么如果有异常,异常将正常抛出,这时候with的作用将不会显现出来。</p>
<p>举例说明:</p>
<p>这该示例中,IndexError始终会被隐藏,而TypeError始终会抛出。</p>
<pre class="python hljs"><code class="python hljs"><span class="hljs-class"><span class="hljs-keyword"><span class="hljs-class"><span class="hljs-keyword"><span class="hljs-class"><span class="hljs-keyword">class</span></span></span></span><span class="hljs-title"><span class="hljs-class"><span class="hljs-title"><span class="hljs-class"><span class="hljs-title">DemoManager</span></span></span></span><span class="hljs-params"><span class="hljs-class"><span class="hljs-params"><span class="hljs-class"><span class="hljs-params">(object)</span></span></span><span class="hljs-class">:</span></span>
<span class="hljs-function"><span class="hljs-keyword"><span class="hljs-function"><span class="hljs-keyword"><span class="hljs-function"><span class="hljs-keyword">def</span></span></span></span><span class="hljs-title"><span class="hljs-function"><span class="hljs-title"><span class="hljs-function"><span class="hljs-title">__enter__</span></span></span></span><span class="hljs-params"><span class="hljs-function"><span class="hljs-params"><span class="hljs-function"><span class="hljs-params">(self)</span></span></span><span class="hljs-function">:</span></span>
<span class="hljs-keyword"><span class="hljs-keyword"><span class="hljs-keyword">pass</span></span>
<span class="hljs-function"><span class="hljs-keyword"><span class="hljs-function"><span class="hljs-keyword"><span class="hljs-function"><span class="hljs-keyword">def</span></span></span></span><span class="hljs-title"><span class="hljs-function"><span class="hljs-title"><span class="hljs-function"><span class="hljs-title">__exit__</span></span></span></span><span class="hljs-params"><span class="hljs-function"><span class="hljs-params"><span class="hljs-function"><span class="hljs-params">(self, ex_type, ex_value, ex_tb)</span></span></span><span class="hljs-function">:</span></span>
<span class="hljs-keyword"><span class="hljs-keyword"><span class="hljs-keyword">if</span></span> ex_type <span class="hljs-keyword"><span class="hljs-keyword"><span class="hljs-keyword">is</span></span> IndexError:
<span class="hljs-keyword"><span class="hljs-keyword"><span class="hljs-keyword">print</span></span> ex_value.__class__
<span class="hljs-keyword"><span class="hljs-keyword"><span class="hljs-keyword">return</span></span> <span class="hljs-keyword"><span class="hljs-keyword"><span class="hljs-keyword">True</span></span>
<span class="hljs-keyword"><span class="hljs-keyword"><span class="hljs-keyword">if</span></span> ex_type <span class="hljs-keyword"><span class="hljs-keyword"><span class="hljs-keyword">is</span></span> TypeError:
<span class="hljs-keyword"><span class="hljs-keyword"><span class="hljs-keyword">print</span></span> ex_value.__class__
<span class="hljs-keyword"><span class="hljs-keyword"><span class="hljs-keyword">return</span></span><span class="hljs-comment"><span class="hljs-comment"><span class="hljs-comment"># return None</span></span>
<span class="hljs-keyword"><span class="hljs-keyword"><span class="hljs-keyword">with</span></span> DemoManager() <span class="hljs-keyword"><span class="hljs-keyword"><span class="hljs-keyword">as</span></span> nothing:
data = [<span class="hljs-number"><span class="hljs-number"><span class="hljs-number">1</span></span>, <span class="hljs-number"><span class="hljs-number"><span class="hljs-number">2</span></span>, <span class="hljs-number"><span class="hljs-number"><span class="hljs-number">3</span></span>]
data[<span class="hljs-number"><span class="hljs-number"><span class="hljs-number">4</span></span>]<span class="hljs-comment"><span class="hljs-comment"><span class="hljs-comment"># raise IndexError, 该异常被__exit__处理了</span></span>
<span class="hljs-keyword"><span class="hljs-keyword"><span class="hljs-keyword">with</span></span> DemoManager() <span class="hljs-keyword"><span class="hljs-keyword"><span class="hljs-keyword">as</span></span> nothing:
data = [<span class="hljs-number"><span class="hljs-number"><span class="hljs-number">1</span></span>, <span class="hljs-number"><span class="hljs-number"><span class="hljs-number">2</span></span>, <span class="hljs-number"><span class="hljs-number"><span class="hljs-number">3</span></span>]
data[<span class="hljs-string"><span class="hljs-string"><span class="hljs-string">'a'</span></span>]<span class="hljs-comment"><span class="hljs-comment"><span class="hljs-comment"># raise TypeError, 该异常没有被__exit__处理</span></span>
<span class="hljs-string"><span class="hljs-string"><span class="hljs-string">'''
输出:
<type 'exceptions.IndexError'>
<type 'exceptions.TypeError'>
Traceback (most recent call last):
...
'''</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre>
<h2 id="articleHeader6">对象的序列化</h2>
<p>Python对象的序列化操作是pickling进行的。pickling非常的重要,以至于Python对此有单独的模块<code>pickle</code>,还有一些相关的魔术方法。使用pickling, 你可以将数据存储在文件中,之后又从文件中进行恢复。</p>
<p>下面举例来描述pickle的操作。从该例子中也可以看出,如果通过pickle.load 初始化一个对象, 并不会调用<code>__init__</code>方法。</p>
<pre class="python hljs"><code class="python hljs"><span class="hljs-comment"><span class="hljs-comment"><span class="hljs-comment"># -*- coding: utf-8 -*-</span></span>
<span class="hljs-keyword"><span class="hljs-keyword"><span class="hljs-keyword">from</span></span> datetime <span class="hljs-keyword"><span class="hljs-keyword"><span class="hljs-keyword">import</span></span> datetime
<span class="hljs-keyword"><span class="hljs-keyword"><span class="hljs-keyword">import</span></span> pickle
<span class="hljs-class"><span class="hljs-keyword"><span class="hljs-class"><span class="hljs-keyword"><span class="hljs-class"><span class="hljs-keyword">class</span></span></span></span><span class="hljs-title"><span class="hljs-class"><span class="hljs-title"><span class="hljs-class"><span class="hljs-title">Distance</span></span></span></span><span class="hljs-params"><span class="hljs-class"><span class="hljs-params"><span class="hljs-class"><span class="hljs-params">(object)</span></span></span><span class="hljs-class">:</span></span>
<span class="hljs-function"><span class="hljs-keyword"><span class="hljs-function"><span class="hljs-keyword"><span class="hljs-function"><span class="hljs-keyword">def</span></span></span></span><span class="hljs-title"><span class="hljs-function"><span class="hljs-title"><span class="hljs-function"><span class="hljs-title">__init__</span></span></span></span><span class="hljs-params"><span class="hljs-function"><span class="hljs-params"><span class="hljs-function"><span class="hljs-params">(self, meter)</span></span></span><span class="hljs-function">:</span></span>
<span class="hljs-keyword"><span class="hljs-keyword"><span class="hljs-keyword">print</span></span> <span class="hljs-string"><span class="hljs-string"><span class="hljs-string">'distance __init__'</span></span>
self.meter = meter
data = {
<span class="hljs-string"><span class="hljs-string"><span class="hljs-string">'foo'</span></span>: [<span class="hljs-number"><span class="hljs-number"><span class="hljs-number">1</span></span>, <span class="hljs-number"><span class="hljs-number"><span class="hljs-number">2</span></span>, <span class="hljs-number"><span class="hljs-number"><span class="hljs-number">3</span></span>],
<span class="hljs-string"><span class="hljs-string"><span class="hljs-string">'bar'</span></span>: (<span class="hljs-string"><span class="hljs-string"><span class="hljs-string">'Hello'</span></span>, <span class="hljs-string"><span class="hljs-string"><span class="hljs-string">'world!'</span></span>),
<span class="hljs-string"><span class="hljs-string"><span class="hljs-string">'baz'</span></span>: <span class="hljs-keyword"><span class="hljs-keyword"><span class="hljs-keyword">True</span></span>,
<span class="hljs-string"><span class="hljs-string"><span class="hljs-string">'dt'</span></span>: datetime(<span class="hljs-number"><span class="hljs-number"><span class="hljs-number">2016</span></span>, <span class="hljs-number"><span class="hljs-number"><span class="hljs-number">10</span></span>, <span class="hljs-number"><span class="hljs-number"><span class="hljs-number">01</span></span>),
<span class="hljs-string"><span class="hljs-string"><span class="hljs-string">'distance'</span></span>: Distance(<span class="hljs-number"><span class="hljs-number"><span class="hljs-number">1.78</span></span>),
}
<span class="hljs-keyword"><span class="hljs-keyword"><span class="hljs-keyword">print</span></span> <span class="hljs-string"><span class="hljs-string"><span class="hljs-string">'before dump:'</span></span>, data
<span class="hljs-keyword"><span class="hljs-keyword"><span class="hljs-keyword">with</span></span> open(<span class="hljs-string"><span class="hljs-string"><span class="hljs-string">'data.pkl'</span></span>, <span class="hljs-string"><span class="hljs-string"><span class="hljs-string">'wb'</span></span>) <span class="hljs-keyword"><span class="hljs-keyword"><span class="hljs-keyword">as</span></span> jar:
pickle.dump(data, jar)<span class="hljs-comment"><span class="hljs-comment"><span class="hljs-comment"># 将数据存储在文件中</span></span>
<span class="hljs-keyword"><span class="hljs-keyword"><span class="hljs-keyword">del</span></span> data
<span class="hljs-keyword"><span class="hljs-keyword"><span class="hljs-keyword">print</span></span> <span class="hljs-string"><span class="hljs-string"><span class="hljs-string">'data is deleted!'</span></span>
<span class="hljs-keyword"><span class="hljs-keyword"><span class="hljs-keyword">with</span></span> open(<span class="hljs-string"><span class="hljs-string"><span class="hljs-string">'data.pkl'</span></span>, <span class="hljs-string"><span class="hljs-string"><span class="hljs-string">'rb'</span></span>) <span class="hljs-keyword"><span class="hljs-keyword"><span class="hljs-keyword">as</span></span> jar:
data = pickle.load(jar)<span class="hljs-comment"><span class="hljs-comment"><span class="hljs-comment"># 从文件中恢复数据</span></span>
<span class="hljs-keyword"><span class="hljs-keyword"><span class="hljs-keyword">print</span></span> <span class="hljs-string"><span class="hljs-string"><span class="hljs-string">'after load:'</span></span>, data</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre>
<p>值得一提,从其他文件进行pickle.load操作时,需要注意有恶意代码的可能性。另外,Python的各个版本之间,pickle文件可能是互不兼容的。</p>
<p>pickling并不是Python的內建类型,它支持所有实现pickle协议(可理解为接口)的类。pickle协议有以下几个可选方法来自定义Python对象的行为。</p>
<p><code>__getinitargs__(self)</code></p>
<p>如果你希望unpickle时,<code>__init__</code>方法能够调用,那么就需要定义<code>__getinitargs__</code>, 该方法需要返回一系列参数的元组,这些参数就是传给<code>__init__</code>的参数。</p>
<p>该方法只对<code>old-style class</code>有效。所谓<code>old-style class</code>,指的是不继承自任何对象的类,往往定义时这样表示: <code>class A:</code>, 而非<code>class A(object):</code></p>
<p><code>__getnewargs__(self)</code></p>
<p>跟<code>__getinitargs__</code>很类似,只不过返回的参数元组将传值给<code>__new__</code></p>
<p><code>__getstate__(self)</code></p>
<p>在调用<code>pickle.dump</code>时,默认是对象的<code>__dict__</code>属性被存储,如果你要修改这种行为,可以在<code>__getstate__</code>方法中返回一个state。state将在调用<code>pickle.load</code>时传值给<code>__setstate__</code></p>
<p><code>__setstate__(self, state)</code></p>
<p>一般来说,定义了<code>__getstate__</code>,就需要相应地定义<code>__setstate__</code>来对<code>__getstate__</code>返回的state进行处理。</p>
<p><code>__reduce__(self)</code></p>
<p>如果pickle的数据包含了自定义的扩展类(比如使用C语言实现的Python扩展类)时,就需要通过实现<code>__reduce__</code>方法来控制行为了。由于使用过于生僻,这里就不展开继续讲解了。</p>
<p>令人容易混淆的是,我们知道, <code>reduce()</code>是Python的一个內建函数, 需要指出<code>__reduce__</code>并非定义了<code>reduce()</code>的行为,二者没有关系。</p>
<p><code>__reduce_ex__(self)</code></p>
<p><code>__reduce_ex__</code> 是为了兼容性而存在的, 如果定义了<code>__reduce_ex__</code>, 它将代替<code>__reduce__</code> 执行。</p>
<p>下面的代码示例很有意思,我们定义了一个类Slate(中文是板岩的意思)。这个类能够记录历史上每次写入给它的值,但每次<code>pickle.dump</code>时当前值就会被清空,仅保留了历史。</p>
<pre class="python hljs"><code class="python hljs"><span class="hljs-comment"><span class="hljs-comment"><span class="hljs-comment"># -*- coding: utf-8 -*-</span></span>
<span class="hljs-keyword"><span class="hljs-keyword"><span class="hljs-keyword">import</span></span> pickle
<span class="hljs-keyword"><span class="hljs-keyword"><span class="hljs-keyword">import</span></span> time
<span class="hljs-class"><span class="hljs-keyword"><span class="hljs-class"><span class="hljs-keyword"><span class="hljs-class"><span class="hljs-keyword">class</span></span></span></span><span class="hljs-title"><span class="hljs-class"><span class="hljs-title"><span class="hljs-class"><span class="hljs-title">Slate</span></span></span><span class="hljs-class">:</span></span>
<span class="hljs-string"><span class="hljs-string"><span class="hljs-string">'''Class to store a string and a changelog, and forget its value when pickled.'''</span></span>
<span class="hljs-function"><span class="hljs-keyword"><span class="hljs-function"><span class="hljs-keyword"><span class="hljs-function"><span class="hljs-keyword">def</span></span></span></span><span class="hljs-title"><span class="hljs-function"><span class="hljs-title"><span class="hljs-function"><span class="hljs-title">__init__</span></span></span></span><span class="hljs-params"><span class="hljs-function"><span class="hljs-params"><span class="hljs-function"><span class="hljs-params">(self, value)</span></span></span><span class="hljs-function">:</span></span>
self.value = value
self.last_change = time.time()
self.history = []
<span class="hljs-function"><span class="hljs-keyword"><span class="hljs-function"><span class="hljs-keyword"><span class="hljs-function"><span class="hljs-keyword">def</span></span></span></span><span class="hljs-title"><span class="hljs-function"><span class="hljs-title"><span class="hljs-function"><span class="hljs-title">change</span></span></span></span><span class="hljs-params"><span class="hljs-function"><span class="hljs-params"><span class="hljs-function"><span class="hljs-params">(self, new_value)</span></span></span><span class="hljs-function">:</span></span>
<span class="hljs-comment"><span class="hljs-comment"><span class="hljs-comment"># 修改value, 将上次的valeu记录在history</span></span>
self.history.append((self.last_change, self.value))
self.value = new_value
self.last_change = time.time()
<span class="hljs-function"><span class="hljs-keyword"><span class="hljs-function"><span class="hljs-keyword"><span class="hljs-function"><span class="hljs-keyword">def</span></span></span></span><span class="hljs-title"><span class="hljs-function"><span class="hljs-title"><span class="hljs-function"><span class="hljs-title">print_changes</span></span></span></span><span class="hljs-params"><span class="hljs-function"><span class="hljs-params"><span class="hljs-function"><span class="hljs-params">(self)</span></span></span><span class="hljs-function">:</span></span>
<span class="hljs-keyword"><span class="hljs-keyword"><span class="hljs-keyword">print</span></span> <span class="hljs-string"><span class="hljs-string"><span class="hljs-string">'Changelog for Slate object:'</span></span>
<span class="hljs-keyword"><span class="hljs-keyword"><span class="hljs-keyword">for</span></span> k, v <span class="hljs-keyword"><span class="hljs-keyword"><span class="hljs-keyword">in</span></span> self.history:
<span class="hljs-keyword"><span class="hljs-keyword"><span class="hljs-keyword">print</span></span> <span class="hljs-string"><span class="hljs-string"><span class="hljs-string">'%s %s'</span></span> % (k, v)
<span class="hljs-function"><span class="hljs-keyword"><span class="hljs-function"><span class="hljs-keyword"><span class="hljs-function"><span class="hljs-keyword">def</span></span></span></span><span class="hljs-title"><span class="hljs-function"><span class="hljs-title"><span class="hljs-function"><span class="hljs-title">__getstate__</span></span></span></span><span class="hljs-params"><span class="hljs-function"><span class="hljs-params"><span class="hljs-function"><span class="hljs-params">(self)</span></span></span><span class="hljs-function">:</span></span>
<span class="hljs-comment"><span class="hljs-comment"><span class="hljs-comment"># 故意不返回self.value和self.last_change,</span></span>
<span class="hljs-comment"><span class="hljs-comment"><span class="hljs-comment"># 以便每次unpickle时清空当前的状态,仅仅保留history</span></span>
<span class="hljs-keyword"><span class="hljs-keyword"><span class="hljs-keyword">return</span></span> self.history
<span class="hljs-function"><span class="hljs-keyword"><span class="hljs-function"><span class="hljs-keyword"><span class="hljs-function"><span class="hljs-keyword">def</span></span></span></span><span class="hljs-title"><span class="hljs-function"><span class="hljs-title"><span class="hljs-function"><span class="hljs-title">__setstate__</span></span></span></span><span class="hljs-params"><span class="hljs-function"><span class="hljs-params"><span class="hljs-function"><span class="hljs-params">(self, state)</span></span></span><span class="hljs-function">:</span></span>
self.history = state
self.value, self.last_change = <span class="hljs-keyword"><span class="hljs-keyword"><span class="hljs-keyword">None</span></span>, <span class="hljs-keyword"><span class="hljs-keyword"><span class="hljs-keyword">None</span></span>
slate = Slate(<span class="hljs-number"><span class="hljs-number"><span class="hljs-number">0</span></span>)
time.sleep(<span class="hljs-number"><span class="hljs-number"><span class="hljs-number">0.5</span></span>)
slate.change(<span class="hljs-number"><span class="hljs-number"><span class="hljs-number">100</span></span>)
time.sleep(<span class="hljs-number"><span class="hljs-number"><span class="hljs-number">0.5</span></span>)
slate.change(<span class="hljs-number"><span class="hljs-number"><span class="hljs-number">200</span></span>)
slate.change(<span class="hljs-number"><span class="hljs-number"><span class="hljs-number">300</span></span>)
slate.print_changes()<span class="hljs-comment"><span class="hljs-comment"><span class="hljs-comment"># 与下面的输出历史对比</span></span>
<span class="hljs-keyword"><span class="hljs-keyword"><span class="hljs-keyword">with</span></span> open(<span class="hljs-string"><span class="hljs-string"><span class="hljs-string">'slate.pkl'</span></span>, <span class="hljs-string"><span class="hljs-string"><span class="hljs-string">'wb'</span></span>) <span class="hljs-keyword"><span class="hljs-keyword"><span class="hljs-keyword">as</span></span> jar:
pickle.dump(slate, jar)
<span class="hljs-keyword"><span class="hljs-keyword"><span class="hljs-keyword">del</span></span> slate<span class="hljs-comment"><span class="hljs-comment"><span class="hljs-comment"># delete it</span></span>
<span class="hljs-keyword"><span class="hljs-keyword"><span class="hljs-keyword">with</span></span> open(<span class="hljs-string"><span class="hljs-string"><span class="hljs-string">'slate.pkl'</span></span>, <span class="hljs-string"><span class="hljs-string"><span class="hljs-string">'rb'</span></span>) <span class="hljs-keyword"><span class="hljs-keyword"><span class="hljs-keyword">as</span></span> jar:
slate = pickle.load(jar)
<span class="hljs-keyword"><span class="hljs-keyword"><span class="hljs-keyword">print</span></span> <span class="hljs-string"><span class="hljs-string"><span class="hljs-string">'current value:'</span></span>, slate.value<span class="hljs-comment"><span class="hljs-comment"><span class="hljs-comment"># None</span></span>
<span class="hljs-keyword"><span class="hljs-keyword"><span class="hljs-keyword">print</span></span> slate.print_changes()<span class="hljs-comment"><span class="hljs-comment"><span class="hljs-comment"># 输出历史记录与上面一致</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre>
<h2 id="articleHeader7">运算符相关的魔术方法</h2>
<p>运算符相关的魔术方法实在太多了,也很好理解,不打算多讲。在其他语言里,也有重载运算符的操作,所以我们对这些魔术方法已经很了解了。</p>
<h3 id="articleHeader8">比较运算符</h3>
<p><code>__cmp__(self, other)</code></p>
<p>如果该方法返回负数,说明<code>self < other</code>; 返回正数,说明<code>self > other</code>; 返回0说明<code>self == other</code>。<br>强烈不推荐来定义<code>__cmp__</code>, 取而代之, 最好分别定义<code>__lt__</code>等方法从而实现比较功能。<br><code>__cmp__</code>在Python3中被废弃了。</p>
<p><code>__eq__(self, other)</code></p>
<p>定义了比较操作符<code>==</code>的行为.</p>
<p><code>__ne__(self, other)</code></p>
<p>定义了比较操作符<code>!=</code>的行为.</p>
<p><code>__lt__(self, other)</code></p>
<p>定义了比较操作符<code><</code>的行为.</p>
<p><code>__gt__(self, other)</code></p>
<p>定义了比较操作符<code>></code>的行为.</p>
<p><code>__le__(self, other)</code></p>
<p>定义了比较操作符<code><=</code>的行为.</p>
<p><code>__ge__(self, other)</code></p>
<p>定义了比较操作符<code>>=</code>的行为.</p>
<p>下面我们定义一种类型Word, 它会使用单词的长度来进行大小的比较, 而不是采用str的比较方式。<br>但是为了避免 <code>Word('bar') == Word('foo')</code> 这种违背直觉的情况出现,并没有定义<code>__eq__</code>, 因此Word会使用它的父类(str)中的<code>__eq__</code>来进行比较。</p>
<p>下面的例子中也可以看出: 在编程语言中, 如果<code>a >=b and a <= b</code>, 并不能推导出<code>a == b</code>这样的结论。</p>
<pre class="python hljs"><code class="python hljs"><span class="hljs-comment"><span class="hljs-comment"><span class="hljs-comment"># -*- coding: utf-8 -*-</span></span>
<span class="hljs-class"><span class="hljs-keyword"><span class="hljs-class"><span class="hljs-keyword"><span class="hljs-class"><span class="hljs-keyword">class</span></span></span></span><span class="hljs-title"><span class="hljs-class"><span class="hljs-title"><span class="hljs-class"><span class="hljs-title">Word</span></span></span></span><span class="hljs-params"><span class="hljs-class"><span class="hljs-params"><span class="hljs-class"><span class="hljs-params">(str)</span></span></span><span class="hljs-class">:</span></span>
<span class="hljs-string"><span class="hljs-string"><span class="hljs-string">'''存储单词的类,定义比较单词的几种方法'''</span></span>
<span class="hljs-function"><span class="hljs-keyword"><span class="hljs-function"><span class="hljs-keyword"><span class="hljs-function"><span class="hljs-keyword">def</span></span></span></span><span class="hljs-title"><span class="hljs-function"><span class="hljs-title"><span class="hljs-function"><span class="hljs-title">__new__</span></span></span></span><span class="hljs-params"><span class="hljs-function"><span class="hljs-params"><span class="hljs-function"><span class="hljs-params">(cls, word)</span></span></span><span class="hljs-function">:</span></span>
<span class="hljs-comment"><span class="hljs-comment"><span class="hljs-comment"># 注意我们必须要用到__new__方法,因为str是不可变类型</span></span>
<span class="hljs-comment"><span class="hljs-comment"><span class="hljs-comment"># 所以我们必须在创建的时候将它初始化</span></span>
<span class="hljs-keyword"><span class="hljs-keyword"><span class="hljs-keyword">if</span></span> <span class="hljs-string"><span class="hljs-string"><span class="hljs-string">' '</span></span> <span class="hljs-keyword"><span class="hljs-keyword"><span class="hljs-keyword">in</span></span> word:
<span class="hljs-keyword"><span class="hljs-keyword"><span class="hljs-keyword">print</span></span> <span class="hljs-string"><span class="hljs-string"><span class="hljs-string">"Value contains spaces. Truncating to first space."</span></span>
word = word[:word.index(<span class="hljs-string"><span class="hljs-string"><span class="hljs-string">' '</span></span>)]<span class="hljs-comment"><span class="hljs-comment"><span class="hljs-comment"># 单词是第一个空格之前的所有字符</span></span>
<span class="hljs-keyword"><span class="hljs-keyword"><span class="hljs-keyword">return</span></span> str.__new__(cls, word)
<span class="hljs-function"><span class="hljs-keyword"><span class="hljs-function"><span class="hljs-keyword"><span class="hljs-function"><span class="hljs-keyword">def</span></span></span></span><span class="hljs-title"><span class="hljs-function"><span class="hljs-title"><span class="hljs-function"><span class="hljs-title">__gt__</span></span></span></span><span class="hljs-params"><span class="hljs-function"><span class="hljs-params"><span class="hljs-function"><span class="hljs-params">(self, other)</span></span></span><span class="hljs-function">:</span></span>
<span class="hljs-keyword"><span class="hljs-keyword"><span class="hljs-keyword">return</span></span> len(self) > len(other)
<span class="hljs-function"><span class="hljs-keyword"><span class="hljs-function"><span class="hljs-keyword"><span class="hljs-function"><span class="hljs-keyword">def</span></span></span></span><span class="hljs-title"><span class="hljs-function"><span class="hljs-title"><span class="hljs-function"><span class="hljs-title">__lt__</span></span></span></span><span class="hljs-params"><span class="hljs-function"><span class="hljs-params"><span class="hljs-function"><span class="hljs-params">(self, other)</span></span></span><span class="hljs-function">:</span></span>
<span class="hljs-keyword"><span class="hljs-keyword"><span class="hljs-keyword">return</span></span> len(self) < len(other)
<span class="hljs-function"><span class="hljs-keyword"><span class="hljs-function"><span class="hljs-keyword"><span class="hljs-function"><span class="hljs-keyword">def</span></span></span></span><span class="hljs-title"><span class="hljs-function"><span class="hljs-title"><span class="hljs-function"><span class="hljs-title">__ge__</span></span></span></span><span class="hljs-params"><span class="hljs-function"><span class="hljs-params"><span class="hljs-function"><span class="hljs-params">(self, other)</span></span></span><span class="hljs-function">:</span></span>
<span class="hljs-keyword"><span class="hljs-keyword"><span class="hljs-keyword">return</span></span> len(self) >= len(other)
<span class="hljs-function"><span class="hljs-keyword"><span class="hljs-function"><span class="hljs-keyword"><span class="hljs-function"><span class="hljs-keyword">def</span></span></span></span><span class="hljs-title"><span class="hljs-function"><span class="hljs-title"><span class="hljs-function"><span class="hljs-title">__le__</span></span></span></span><span class="hljs-params"><span class="hljs-function"><span class="hljs-params"><span class="hljs-function"><span class="hljs-params">(self, other)</span></span></span><span class="hljs-function">:</span></span>
<span class="hljs-keyword"><span class="hljs-keyword"><span class="hljs-keyword">return</span></span> len(self) <= len(other)
<span class="hljs-keyword"><span class="hljs-keyword"><span class="hljs-keyword">print</span></span> <span class="hljs-string"><span class="hljs-string"><span class="hljs-string">'foo < fool:'</span></span>, Word(<span class="hljs-string"><span class="hljs-string"><span class="hljs-string">'foo'</span></span>) < Word(<span class="hljs-string"><span class="hljs-string"><span class="hljs-string">'fool'</span></span>)<span class="hljs-comment"><span class="hljs-comment"><span class="hljs-comment"># True</span></span>
<span class="hljs-keyword"><span class="hljs-keyword"><span class="hljs-keyword">print</span></span> <span class="hljs-string"><span class="hljs-string"><span class="hljs-string">'foolish > fool:'</span></span>, Word(<span class="hljs-string"><span class="hljs-string"><span class="hljs-string">'foolish'</span></span>) > Word(<span class="hljs-string"><span class="hljs-string"><span class="hljs-string">'fool'</span></span>)<span class="hljs-comment"><span class="hljs-comment"><span class="hljs-comment"># True</span></span>
<span class="hljs-keyword"><span class="hljs-keyword"><span class="hljs-keyword">print</span></span> <span class="hljs-string"><span class="hljs-string"><span class="hljs-string">'bar >= foo:'</span></span>, Word(<span class="hljs-string"><span class="hljs-string"><span class="hljs-string">'bar'</span></span>) >= Word(<span class="hljs-string"><span class="hljs-string"><span class="hljs-string">'foo'</span></span>)<span class="hljs-comment"><span class="hljs-comment"><span class="hljs-comment"># True</span></span>
<span class="hljs-keyword"><span class="hljs-keyword"><span class="hljs-keyword">print</span></span> <span class="hljs-string"><span class="hljs-string"><span class="hljs-string">'bar <= foo:'</span></span>, Word(<span class="hljs-string"><span class="hljs-string"><span class="hljs-string">'bar'</span></span>) <= Word(<span class="hljs-string"><span class="hljs-string"><span class="hljs-string">'foo'</span></span>)<span class="hljs-comment"><span class="hljs-comment"><span class="hljs-comment"># True</span></span>
<span class="hljs-keyword"><span class="hljs-keyword"><span class="hljs-keyword">print</span></span> <span class="hljs-string"><span class="hljs-string"><span class="hljs-string">'bar == foo:'</span></span>, Word(<span class="hljs-string"><span class="hljs-string"><span class="hljs-string">'bar'</span></span>) == Word(<span class="hljs-string"><span class="hljs-string"><span class="hljs-string">'foo'</span></span>)<span class="hljs-comment"><span class="hljs-comment"><span class="hljs-comment"># False, 用了str内置的比较方法来进行比较</span></span>
<span class="hljs-keyword"><span class="hljs-keyword"><span class="hljs-keyword">print</span></span> <span class="hljs-string"><span class="hljs-string"><span class="hljs-string">'bar != foo:'</span></span>, Word(<span class="hljs-string"><span class="hljs-string"><span class="hljs-string">'bar'</span></span>) != Word(<span class="hljs-string"><span class="hljs-string"><span class="hljs-string">'foo'</span></span>)<span class="hljs-comment"><span class="hljs-comment"><span class="hljs-comment"># True</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre>
<h3 id="articleHeader9">一元运算符和函数</h3>
<p><code>__pos__(self)</code></p>
<p>实现了'+'号一元运算符(比如<code>+some_object</code>)</p>
<p><code>__neg__(self)</code></p>
<p>实现了'-'号一元运算符(比如<code>-some_object</code>)</p>
<p><code>__invert__(self)</code></p>
<p>实现了<code>~</code>号(波浪号)一元运算符(比如<code>~some_object</code>)</p>
<p><code>__abs__(self)</code></p>
<p>实现了<code>abs()</code>內建函数.</p>
<p><code>__round__(self, n)</code></p>
<p>实现了<code>round()</code>内建函数. 参数n表示四舍五进的精度.</p>
<p><code>__floor__(self)</code></p>
<p>实现了<code>math.floor()</code>, 向下取整.</p>
<p><code>__ceil__(self)</code></p>
<p>实现了<code>math.ceil()</code>, 向上取整.</p>
<p><code>__trunc__(self)</code></p>
<p>实现了<code>math.trunc()</code>, 向0取整.</p>
<h3 id="articleHeader10">算术运算符</h3>
<p><code>__add__(self, other)</code></p>
<p>实现了加号运算.</p>
<p><code>__sub__(self, other)</code></p>
<p>实现了减号运算.</p>
<p><code>__mul__(self, other)</code></p>
<p>实现了乘法运算.</p>
<p><code>__floordiv__(self, other)</code></p>
<p>实现了<code>//</code>运算符.</p>
<p><code>__div__(self, other)</code></p>
<p>实现了<code>/</code>运算符. 该方法在Python3中废弃. 原因是Python3中,division默认就是true division.</p>
<p><code>__truediv__</code>(self, other)</p>
<p>实现了true division. 只有你声明了<code>from __future__ import division</code>该方法才会生效.</p>
<p><code>__mod__(self, other)</code></p>
<p>实现了<code>%</code>运算符, 取余运算.</p>
<p><code>__divmod__(self, other)</code></p>
<p>实现了<code>divmod()</code>內建函数.</p>
<p><code>__pow__(self, other)</code></p>
<p>实现了<code>**</code>操作. N次方操作.</p>
<p><code>__lshift__(self, other)</code></p>
<p>实现了位操作<code><<</code>.</p>
<p><code>__rshift__(self, other)</code></p>
<p>实现了位操作<code>>></code>.</p>
<p><code>__and__(self, other)</code></p>
<p>实现了位操作<code>&</code>.</p>
<p><code>__or__(self, other)</code></p>
<p>实现了位操作<code>|</code></p>
<p><code>__xor__(self, other)</code></p>
<p>实现了位操作<code>^</code></p>
<h3 id="articleHeader11">反算术运算符</h3>
<p>这里只需要解释一下概念即可。<br>假设针对some_object这个对象:</p>
<pre class="hljs nginx"><code class="hljs"><span class="hljs-attribute"><span class="hljs-attribute"><span class="hljs-attribute">some_object</span></span> + other
</span></code></pre>
<p>上面的代码非常正常地实现了some_object的<code>__add__</code>方法。那么如果遇到相反的情况呢?</p>
<pre class="hljs nginx"><code class="hljs"><span class="hljs-attribute"><span class="hljs-attribute"><span class="hljs-attribute">other</span></span> + some_object
</span></code></pre>
<p>这时候,如果other没有定义<code>__add__</code>方法,但是some_object定义了<code>__radd__</code>, 那么上面的代码照样可以运行。<br>这里的<code>__radd__(self, other)</code>就是<code>__add__(self, other)</code>的反算术运算符。</p>
<p>所以,类比的,我们就知道了更多的反算术运算符, 就不一一展开了:</p>
<ul>
<li><code>__rsub__(self, other)</code></li>
<li><code>__rmul__(self, other)</code></li>
<li><code>__rmul__(self, other)</code></li>
<li><code>__rfloordiv__(self, other)</code></li>
<li><code>__rdiv__(self, other)</code></li>
<li><code>__rtruediv__(self, other)</code></li>
<li><code>__rmod__(self, other)</code></li>
<li><code>__rdivmod__(self, other)</code></li>
<li><code>__rpow__(self, other)</code></li>
<li><code>__rlshift__(self, other)</code></li>
<li><code>__rrshift__(self, other)</code></li>
<li><code>__rand__(self, other)</code></li>
<li><code>__ror__(self, other)</code></li>
<li><code>__rxor__(self, other)</code></li>
</ul>
<h3 id="articleHeader12">增量赋值</h3>
<p>这也是只要理解了概念就容易掌握的运算。举个例子:</p>
<pre class="hljs makefile"><code class="hljs">x = 5
x += 1<span class="hljs-comment"><span class="hljs-comment"><span class="hljs-comment"># 这里的+=就是增量赋值,将x+1赋值给了x</span></span>
</span></code></pre>
<p>因此对于<code>a += b</code>, <code>__iadd__</code> 将返回<code>a + b</code>, 并赋值给a。<br>所以很容易理解下面的魔术方法了:</p>
<ul>
<li><code>__iadd__(self, other)</code></li>
<li><code>__isub__(self, other)</code></li>
<li><code>__imul__(self, other)</code></li>
<li><code>__ifloordiv__(self, other)</code></li>
<li><code>__idiv__(self, other)</code></li>
<li><code>__itruediv__(self, other)</code></li>
<li><code>__imod__(self, other)</code></li>
<li><code>__ipow__(self, other)</code></li>
<li><code>__ilshift__(self, other)</code></li>
<li><code>__irshift__(self, other)</code></li>
<li><code>__iand__(self, other)</code></li>
<li><code>__ior__(self, other)</code></li>
<li><code>__ixor__(self, other)</code></li>
</ul>
<h3 id="articleHeader13">类型转化</h3>
<p><code>__int__(self)</code></p>
<p>实现了类型转化为int的行为.</p>
<p><code>__long__(self)</code></p>
<p>实现了类型转化为long的行为.</p>
<p><code>__float__(self)</code></p>
<p>实现了类型转化为float的行为.</p>
<p><code>__complex__(self)</code></p>
<p>实现了类型转化为complex(复数, 也即1+2j这样的虚数)的行为.</p>
<p><code>__oct__(self)</code></p>
<p>实现了类型转化为八进制数的行为.</p>
<p><code>__hex__(self)</code></p>
<p>实现了类型转化为十六进制数的行为.</p>
<p><code>__index__(self)</code></p>
<p>在切片运算中将对象转化为int, 因此该方法的返回值必须是int。用一个例子来解释这个用法。</p>
<pre class="python hljs"><code class="python hljs"><span class="hljs-class"><span class="hljs-keyword"><span class="hljs-class"><span class="hljs-keyword"><span class="hljs-class"><span class="hljs-keyword">class</span></span></span></span><span class="hljs-title"><span class="hljs-class"><span class="hljs-title"><span class="hljs-class"><span class="hljs-title">Thing</span></span></span></span><span class="hljs-params"><span class="hljs-class"><span class="hljs-params"><span class="hljs-class"><span class="hljs-params">(object)</span></span></span><span class="hljs-class">:</span></span>
<span class="hljs-function"><span class="hljs-keyword"><span class="hljs-function"><span class="hljs-keyword"><span class="hljs-function"><span class="hljs-keyword">def</span></span></span></span><span class="hljs-title"><span class="hljs-function"><span class="hljs-title"><span class="hljs-function"><span class="hljs-title">__index__</span></span></span></span><span class="hljs-params"><span class="hljs-function"><span class="hljs-params"><span class="hljs-function"><span class="hljs-params">(self)</span></span></span><span class="hljs-function">:</span></span>
<span class="hljs-keyword"><span class="hljs-keyword"><span class="hljs-keyword">return</span></span> <span class="hljs-number"><span class="hljs-number"><span class="hljs-number">1</span></span>
thing = Thing()
list_ = [<span class="hljs-string"><span class="hljs-string"><span class="hljs-string">'a'</span></span>, <span class="hljs-string"><span class="hljs-string"><span class="hljs-string">'b'</span></span>, <span class="hljs-string"><span class="hljs-string"><span class="hljs-string">'c'</span></span>]
<span class="hljs-keyword"><span class="hljs-keyword"><span class="hljs-keyword">print</span></span> list_<span class="hljs-comment"><span class="hljs-comment"><span class="hljs-comment"># 'b'</span></span>
<span class="hljs-keyword"><span class="hljs-keyword"><span class="hljs-keyword">print</span></span> list_<span class="hljs-comment"><span class="hljs-comment"><span class="hljs-comment"># []</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre>
<p>上面例子中, <code>list_</code>的表现跟<code>list_</code>一致,正是因为Thing实现了<code>__index__</code>方法。</p>
<p>可能有的人会想,<code>list_</code>为什么不是相当于<code>list_</code>呢? 通过实现Thing的<code>__int__</code>方法能否达到这个目的呢?</p>
<p>显然不能。如果真的是这样的话,那么<code>list_</code>这样的写法也应该是通过的。<br>而实际上,该写法会抛出TypeError: <code>slice indices must be integers or None or have an __index__ method</code></p>
<p>下面我们再做个例子,如果对一个dict对象执行<code>dict_</code>会怎么样呢?</p>
<pre class="python hljs"><code class="python hljs">dict_ = {<span class="hljs-number"><span class="hljs-number"><span class="hljs-number">1</span></span>: <span class="hljs-string"><span class="hljs-string"><span class="hljs-string">'apple'</span></span>, <span class="hljs-number"><span class="hljs-number"><span class="hljs-number">2</span></span>: <span class="hljs-string"><span class="hljs-string"><span class="hljs-string">'banana'</span></span>, <span class="hljs-number"><span class="hljs-number"><span class="hljs-number">3</span></span>: <span class="hljs-string"><span class="hljs-string"><span class="hljs-string">'cat'</span></span>}
<span class="hljs-keyword"><span class="hljs-keyword"><span class="hljs-keyword">print</span></span> dict_<span class="hljs-comment"><span class="hljs-comment"><span class="hljs-comment"># raise KeyError</span></span></span></span></span></span></span></span></span></span></code></pre>
<p>这个时候就不是调用<code>__index__</code>了。虽然<code>list</code>和<code>dict</code>都实现了<code>__getitem__</code>方法, 但是它们的实现方式是不一样的。<br>如果希望上面例子能够正常执行, 需要实现Thing的<code>__hash__</code> 和 <code>__eq__</code>方法.</p>
<pre class="python hljs"><code class="python hljs"><span class="hljs-class"><span class="hljs-keyword"><span class="hljs-class"><span class="hljs-keyword"><span class="hljs-class"><span class="hljs-keyword">class</span></span></span></span><span class="hljs-title"><span class="hljs-class"><span class="hljs-title"><span class="hljs-class"><span class="hljs-title">Thing</span></span></span></span><span class="hljs-params"><span class="hljs-class"><span class="hljs-params"><span class="hljs-class"><span class="hljs-params">(object)</span></span></span><span class="hljs-class">:</span></span>
<span class="hljs-function"><span class="hljs-keyword"><span class="hljs-function"><span class="hljs-keyword"><span class="hljs-function"><span class="hljs-keyword">def</span></span></span></span><span class="hljs-title"><span class="hljs-function"><span class="hljs-title"><span class="hljs-function"><span class="hljs-title">__hash__</span></span></span></span><span class="hljs-params"><span class="hljs-function"><span class="hljs-params"><span class="hljs-function"><span class="hljs-params">(self)</span></span></span><span class="hljs-function">:</span></span>
<span class="hljs-keyword"><span class="hljs-keyword"><span class="hljs-keyword">return</span></span> <span class="hljs-number"><span class="hljs-number"><span class="hljs-number">1</span></span>
<span class="hljs-function"><span class="hljs-keyword"><span class="hljs-function"><span class="hljs-keyword"><span class="hljs-function"><span class="hljs-keyword">def</span></span></span></span><span class="hljs-title"><span class="hljs-function"><span class="hljs-title"><span class="hljs-function"><span class="hljs-title">__eq__</span></span></span></span><span class="hljs-params"><span class="hljs-function"><span class="hljs-params"><span class="hljs-function"><span class="hljs-params">(self, other)</span></span></span><span class="hljs-function">:</span></span>
<span class="hljs-keyword"><span class="hljs-keyword"><span class="hljs-keyword">return</span></span> hash(self) == hash(other)
dict_ = {<span class="hljs-number"><span class="hljs-number"><span class="hljs-number">1</span></span>: <span class="hljs-string"><span class="hljs-string"><span class="hljs-string">'apple'</span></span>, <span class="hljs-number"><span class="hljs-number"><span class="hljs-number">2</span></span>: <span class="hljs-string"><span class="hljs-string"><span class="hljs-string">'banana'</span></span>, <span class="hljs-number"><span class="hljs-number"><span class="hljs-number">3</span></span>: <span class="hljs-string"><span class="hljs-string"><span class="hljs-string">'cat'</span></span>}
<span class="hljs-keyword"><span class="hljs-keyword"><span class="hljs-keyword">print</span></span> dict_<span class="hljs-comment"><span class="hljs-comment"><span class="hljs-comment"># apple</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre>
<p><code>__coerce__(self, other)</code></p>
<p>实现了混合模式运算。</p>
<p>要了解这个方法,需要先了解<code>coerce()</code>内建函数: 官方文档上的解释是, coerce(x, y)返回一组数字类型的参数, 它们被转化为同一种类型,以便它们可以使用相同的算术运算符进行操作。如果过程中转化失败,抛出TypeError。</p>
<p>比如对于<code>coerce(10, 10.1)</code>, 因为10和10.1在进行算术运算时,会先将10转为10.0再来运算。因此<code>coerce(10, 10.1)</code>返回值是(10.0, 10.1).</p>
<p><code>__coerce__</code>在Python3中废弃了。</p>
<h2 id="articleHeader14">其他魔术方法</h2>
<p>还没讲到的魔术方法还有很多,但有些我觉得很简单,或者很少见,就不再累赘展开说明了。</p>
<p><code>__str__(self)</code></p>
<p>对实例使用<code>str()</code>时调用。</p>
<p><code>__repr__(self)</code></p>
<p>对实例使用<code>repr()</code>时调用。<code>str()</code>和<code>repr()</code>都是返回一个代表该实例的字符串,<br>主要区别在于: str()的返回值要方便人来看,而repr()的返回值要方便计算机看。</p>
<p><code>__unicode__(self)</code></p>
<p>对实例使用<code>unicode()</code>时调用。<code>unicode()</code>与<code>str()</code>的区别在于: 前者返回值是unicode, 后者返回值是str。unicode和str都是<code>basestring</code>的子类。</p>
<p>当你对一个类只定义了<code>__str__</code>但没定义<code>__unicode__</code>时,<code>__unicode__</code>会根据<code>__str__</code>的返回值自动实现,即<code>return unicode(self.__str__())</code>;<br>但返回来则不成立。</p>
<pre class="python hljs"><code class="python hljs"><span class="hljs-class"><span class="hljs-keyword"><span class="hljs-class"><span class="hljs-keyword"><span class="hljs-class"><span class="hljs-keyword">class</span></span></span></span><span class="hljs-title"><span class="hljs-class"><span class="hljs-title"><span class="hljs-class"><span class="hljs-title">StrDemo2</span></span></span><span class="hljs-class">:</span></span>
<span class="hljs-function"><span class="hljs-keyword"><span class="hljs-function"><span class="hljs-keyword"><span class="hljs-function"><span class="hljs-keyword">def</span></span></span></span><span class="hljs-title"><span class="hljs-function"><span class="hljs-title"><span class="hljs-function"><span class="hljs-title">__str__</span></span></span></span><span class="hljs-params"><span class="hljs-function"><span class="hljs-params"><span class="hljs-function"><span class="hljs-params">(self)</span></span></span><span class="hljs-function">:</span></span>
<span class="hljs-keyword"><span class="hljs-keyword"><span class="hljs-keyword">return</span></span> <span class="hljs-string"><span class="hljs-string"><span class="hljs-string">'StrDemo2'</span></span>
<span class="hljs-class"><span class="hljs-keyword"><span class="hljs-class"><span class="hljs-keyword"><span class="hljs-class"><span class="hljs-keyword">class</span></span></span></span><span class="hljs-title"><span class="hljs-class"><span class="hljs-title"><span class="hljs-class"><span class="hljs-title">StrDemo3</span></span></span><span class="hljs-class">:</span></span>
<span class="hljs-function"><span class="hljs-keyword"><span class="hljs-function"><span class="hljs-keyword"><span class="hljs-function"><span class="hljs-keyword">def</span></span></span></span><span class="hljs-title"><span class="hljs-function"><span class="hljs-title"><span class="hljs-function"><span class="hljs-title">__unicode__</span></span></span></span><span class="hljs-params"><span class="hljs-function"><span class="hljs-params"><span class="hljs-function"><span class="hljs-params">(self)</span></span></span><span class="hljs-function">:</span></span>
<span class="hljs-keyword"><span class="hljs-keyword"><span class="hljs-keyword">return</span></span> <span class="hljs-string"><span class="hljs-string"><span class="hljs-string">u'StrDemo3'</span></span>
demo2 = StrDemo2()
<span class="hljs-keyword"><span class="hljs-keyword"><span class="hljs-keyword">print</span></span> str(demo2)<span class="hljs-comment"><span class="hljs-comment"><span class="hljs-comment"># StrDemo2</span></span>
<span class="hljs-keyword"><span class="hljs-keyword"><span class="hljs-keyword">print</span></span> unicode(demo2)<span class="hljs-comment"><span class="hljs-comment"><span class="hljs-comment"># StrDemo2</span></span>
demo3 = StrDemo3()
<span class="hljs-keyword"><span class="hljs-keyword"><span class="hljs-keyword">print</span></span> str(demo3)<span class="hljs-comment"><span class="hljs-comment"><span class="hljs-comment"># <__main__.StrDemo3 instance></span></span>
<span class="hljs-keyword"><span class="hljs-keyword"><span class="hljs-keyword">print</span></span> unicode(demo3)<span class="hljs-comment"><span class="hljs-comment"><span class="hljs-comment"># StrDemo3</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre>
<p><code>__format__(self, formatstr)</code></p>
<p><code>"Hello, {0:abc}".format(a)</code>等价于<code>format(a, "abc")</code>, 等价于<code>a.__format__("abc")</code>。</p>
<p>这在需要格式化展示对象的时候非常有用,比如格式化时间对象。</p>
<p><code>__hash__(self)</code></p>
<p>对实例使用<code>hash()</code>时调用, 返回值是数值类型。</p>
<p><code>__nonzero__(self)</code></p>
<p>对实例使用<code>bool()</code>时调用, 返回True或者False。<br>你可能会问, 为什么不是命名为<code>__bool__</code>? 我也不知道。<br>我只知道该方法在Python3中改名为<code>__bool__</code>了。</p>
<p><code>__dir__(self)</code></p>
<p>对实例使用<code>dir()</code>时调用。通常实现该方法是没必要的。</p>
<p><code>__sizeof__(self)</code></p>
<p>对实例使用<code>sys.getsizeof()</code>时调用。返回对象的大小,单位是bytes。</p>
<p><code>__instancecheck__(self, instance)</code></p>
<p>对实例调用<code>isinstance(instance, class)</code>时调用。 返回值是布尔值。它会判断instance是否是该类的实例。</p>
<p><code>__subclasscheck__(self, subclass)</code></p>
<p>对实例使用<code>issubclass(subclass, class)</code>时调用。返回值是布尔值。它会判断subclass否是该类的子类。</p>
<p><code>__copy__(self)</code></p>
<p>对实例使用<code>copy.copy()</code>时调用。返回"浅复制"的对象。</p>
<p><code>__deepcopy__(self, memodict={})</code></p>
<p>对实例使用<code>copy.deepcopy()</code>时调用。返回"深复制"的对象。</p>
<p><code>__call__(self, )</code></p>
<p>该方法允许类的实例跟函数一样表现:</p>
<pre class="python hljs"><code class="python hljs"><span class="hljs-class"><span class="hljs-keyword"><span class="hljs-class"><span class="hljs-keyword"><span class="hljs-class"><span class="hljs-keyword">class</span></span></span></span><span class="hljs-title"><span class="hljs-class"><span class="hljs-title"><span class="hljs-class"><span class="hljs-title">XClass</span></span></span><span class="hljs-class">:</span></span>
<span class="hljs-function"><span class="hljs-keyword"><span class="hljs-function"><span class="hljs-keyword"><span class="hljs-function"><span class="hljs-keyword">def</span></span></span></span><span class="hljs-title"><span class="hljs-function"><span class="hljs-title"><span class="hljs-function"><span class="hljs-title">__call__</span></span></span></span><span class="hljs-params"><span class="hljs-function"><span class="hljs-params"><span class="hljs-function"><span class="hljs-params">(self, a, b)</span></span></span><span class="hljs-function">:</span></span>
<span class="hljs-keyword"><span class="hljs-keyword"><span class="hljs-keyword">return</span></span> a + b
<span class="hljs-function"><span class="hljs-keyword"><span class="hljs-function"><span class="hljs-keyword"><span class="hljs-function"><span class="hljs-keyword">def</span></span></span></span><span class="hljs-title"><span class="hljs-function"><span class="hljs-title"><span class="hljs-function"><span class="hljs-title">add</span></span></span></span><span class="hljs-params"><span class="hljs-function"><span class="hljs-params"><span class="hljs-function"><span class="hljs-params">(a, b)</span></span></span><span class="hljs-function">:</span></span>
<span class="hljs-keyword"><span class="hljs-keyword"><span class="hljs-keyword">return</span></span> a + b
x = XClass()
<span class="hljs-keyword"><span class="hljs-keyword"><span class="hljs-keyword">print</span></span> <span class="hljs-string"><span class="hljs-string"><span class="hljs-string">'x(1, 2)'</span></span>, x(<span class="hljs-number"><span class="hljs-number"><span class="hljs-number">1</span></span>, <span class="hljs-number"><span class="hljs-number"><span class="hljs-number">2</span></span>)
<span class="hljs-keyword"><span class="hljs-keyword"><span class="hljs-keyword">print</span></span> <span class="hljs-string"><span class="hljs-string"><span class="hljs-string">'callable(x)'</span></span>, callable(x)<span class="hljs-comment"><span class="hljs-comment"><span class="hljs-comment"># True</span></span>
<span class="hljs-keyword"><span class="hljs-keyword"><span class="hljs-keyword">print</span></span> <span class="hljs-string"><span class="hljs-string"><span class="hljs-string">'add(1, 2)'</span></span>, add(<span class="hljs-number"><span class="hljs-number"><span class="hljs-number">1</span></span>, <span class="hljs-number"><span class="hljs-number"><span class="hljs-number">2</span></span>)
<span class="hljs-keyword"><span class="hljs-keyword"><span class="hljs-keyword">print</span></span> <span class="hljs-string"><span class="hljs-string"><span class="hljs-string">'callable(add)'</span></span>, callable(add)<span class="hljs-comment"><span class="hljs-comment"><span class="hljs-comment"># True</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre>
<h2 id="articleHeader15">Python3中的差异</h2>
<ul>
<li>Python3中,str与unicode的区别被废除了,因而<code>__unicode__</code>没有了,取而代之地出现了<code>__bytes__</code>.</li>
<li>Python3中,division默认就是true division, 因而<code>__div__</code>废弃.</li>
<li><code>__coerce__</code>因存在冗余而废弃.</li>
<li><code>__cmp__</code>因存在冗余而废弃.</li>
<li><code>__nonzero__</code>改名为<code>__bool__</code>.</li>
</ul>
</div>
</div>
</div>
<div id="MySignature" role="contentinfo">
为中华之崛起而读书。
--孙中山<br><br>
来源:https://www.cnblogs.com/zhouyixian/p/11129347.html
頁:
[1]