目次
はじめに
构造和初始化
属性访问控制
__getattr__(self, name)
__setattr__(self, name, value)
__delattr__(self, name)
__getattribute__(self, name)
描述器对象
__get__(self, instance, owner)
__set__(self, instance, value)
__delete__(self, instance)
构造自定义容器(Container)
__len__(self)
__getitem__(self, key)
__setitem__(self, key, value)
__delitem__(self, key)
__iter__(self)
__reversed__(self)
__contains__(self, item)
__missing__(self, key)
上下文管理
__enter__(self)
__exit__(self, exception_type, exception_value, traceback)
对象的序列化
__getinitargs__(self)
__getnewargs__(self)
__getstate__(self)
__setstate__(self, state)
__reduce__(self)
__reduce_ex__(self)
运算符相关的魔术方法
比较运算符
__cmp__(self, other)
__eq__(self, other)
__ne__(self, other)
__lt__(self, other)
__gt__(self, other)
__le__(self, other)
__ge__(self, other)
一元运算符和函数
__pos__(self)
__neg__(self)
__invert__(self)
__abs__(self)
__round__(self, n)
__floor__(self)
__ceil__(self)
__trunc__(self)
算术运算符
__add__(self, other)
__sub__(self, other)
__mul__(self, other)
__floorp__(self, other)
__p__(self, other)
__truep__(self, other)
__mod__(self, other)
__pmod__(self, other)
__pow__(self, other)
__lshift__(self, other)
__rshift__(self, other)
__and__(self, other)
__or__(self, other)
__xor__(self, other)
反算术运算符
增量赋值
类型转化
__int__(self)
__long__(self)
__float__(self)
__complex__(self)
__oct__(self)
__hex__(self)
__index__(self)
__coerce__(self, other)
其他魔术方法
__str__(self)
__repr__(self)
__unicode__(self)
__format__(self, formatstr)
__hash__(self)
__nonzero__(self)
__dir__(self)
__sizeof__(self)
__instancecheck__(self, instance)
__subclasscheck__(self, subclass)
__copy__(self)
__deepcopy__(self, memodict={})
__call__(self, [args...])
Python3中的差异
ホームページ バックエンド開発 Python チュートリアル Python のマジック メソッドの紹介

Python のマジック メソッドの紹介

Mar 18, 2017 am 11:38 AM
python

<h2 id="はじめに">はじめに</h2> <p> Pythonでは、「__」二重アンダースコアで囲まれたメソッドを総称して「<span class="wp_keywordlink">マジックメソッド</span>」と呼びます。たとえば、私たちが最も頻繁に触れる <code>__init__</code> <code>__init__</code>.</p> <p>有些魔术方法,我们可能以后一辈子都不会再遇到了,这里也就只是简单介绍下;</p> <p>而有些魔术方法,巧妙使用它可以构造出非常优美的代码,比如将复杂的逻辑封装成简单的API。</p> <p>本文编辑的思路借鉴自Rafe Kettler的这篇博客: A Guide to Python Magic Methods,并补充了一些代码示例。</p> <p>介绍的顺序大概是:常见的先介绍,越少见的越靠后讲。</p> <p>本文中用到的代码示例,可以在我的github上下载到。</p> <h2 id="构造和初始化">构造和初始化</h2> <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><div class="code" style="position:relative; padding:0px; margin:0px;"><pre class='brush:php;toolbar:false;'>class Foo(object): def __init__(self): print 'foo __init__' return None # 必须返回None,否则抛TypeError def __del__(self): print 'foo __del__'</pre><div class="contentsignin">ログイン後にコピー</div></div><p>实际中,你很少会用到<code>__new__</code>,除非你希望能够控制类的创建。<br/>如果要讲解<code>__new__</code>,往往需要牵扯到<code>metaclass</code>(元类)的介绍。<br/>如果你有兴趣深入,可以参考我的另一篇博客: 理解Python的metaclass</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><div class="code" style="position:relative; padding:0px; margin:0px;"><pre class='brush:php;toolbar:false;'>foo = Foo() foo.__del__() print foo del foo print foo # NameError, foo is not defined</pre><div class="contentsignin">ログイン後にコピー</div></div><p>如果调用了<code>foo.__del__()</code>,对象本身仍然存在. 但是调用了<code>del foo</code>, 就再也没有foo这个对象了.</p><p>请注意,如果解释器退出的时候对象还存在,就不能保证 <code>__del__</code> 被确切的执行了。所以<code>__del__</code>并不能替代良好的编程习惯。<br/>比如,在处理socket时,及时关闭结束的连接。</p><h2 id="属性访问控制">属性访问控制</h2><p>总有人要吐槽Python缺少对于类的封装,比如希望Python能够定义私有属性,然后提供公共可访问的getter和 setter。Python其实可以通过魔术方法来实现封装。</p><h4 id="code-getattr-self-name-code"><code>__getattr__(self, name)</code></h4><p>该方法定义了你试图访问一个不存在的属性时的行为。因此,重载该方法可以实现捕获错误拼写然后进行重定向, 或者对一些废弃的属性进行警告。</p><h4 id="code-setattr-self-name-value-code"><code>__setattr__(self, name, value)</code></h4><p><code>__setattr__</code> 是实现封装的解决方案,它定义了你对属性进行赋值和修改操作时的行为。<br/>不管对象的某个属性是否存在,它都允许你为该属性进行赋值,因此你可以为属性的值进行自定义操作。有一点需要注意,实现<code>__setattr__</code>时要避免”无限递归”的错误,下面的代码示例中会提到。</p><h4 id="code-delattr-self-name-code"><code>__delattr__(self, name)</code></h4><p><code>__delattr__</code>与<code>__setattr__</code>很像,只是它定义的是你删除属性时的行为。实现<code>__delattr__</code>是同时要避免”无限递归”的错误。</p><h4 id="code-getattribute-self-name-code"><code>__getattribute__(self, name)</code></h4><p><code>__getattribute__</code>定义了你的属性被访问时的行为,相比较,<code>__getattr__</code>只有该属性不存在时才会起作用。</p><p>因此,在支持<code>__getattribute__</code>的Python版本,调用<code>__getattr__</code>前必定会用 <code>__getattribute__</code>。<code>__getattribute__</code>同样要避免”无限递归”的错误。</p><p>需要提醒的是,最好不要尝试去实现<code>__getattribute__</code>,因为很少见到这种做法,而且很容易出bug。</p><p>例子说明<code>__setattr__</code></p>一部のマジック メソッドは、私たちの生活の中で再び遭遇することはないかもしれないので、ここでは簡単に紹介します <p></p>複雑なロジックを単純な API にカプセル化するなど、非常に美しいコードを構築します。 🎜🎜この記事の編集アイデアは Rafe Kettler のブログ「A Guide to Python Magic Methods」から借用しており、いくつかのコード例が追加されています。 🎜🎜紹介の順番は大まかに、一般的なものを最初に紹介し、珍しいものを後で紹介します。 🎜🎜この記事で使用されているコード例は、私の github からダウンロードできます。 🎜🎜構築と初期化🎜🎜<code>__init__</code>これは、オブジェクトが初期化されるときに呼び出されます。一般に、これは「コンストラクター」として理解されます。🎜🎜実際、<を呼び出すときに呼び出されます。 code> x = SomeClass()</code> が呼び出されたとき、最初に実行されるのは <code>__init__</code> ではなく、<code>__new__</code> です。したがって、正確に言うと、<code>__new__</code> と <code>__init__</code> は合わせて「コンストラクター」を構成します 🎜🎜<code>__new__</code> は、クラスを作成し、このクラス インスタンスを返すために使用されます。 <code>__init__</code> は渡されたパラメーターでインスタンスを初期化するだけです 🎜🎜<code>__new__</code> はインスタンスの作成プロセス中に必ず呼び出されますが、<code>__init__ </code>たとえば、<code>pickle.load</code> を通じてインスタンスを逆シリアル化する場合、<code>__init__</code> は呼び出されません。 🎜🎜 <code>__new__</code> メソッドは常にクラスのインスタンスを返す必要がありますが、<code>__init__</code> は None 以外の値を返すことはできません。たとえば、次の例は次のとおりです。 🎜<div class="code" style="position:relative; padding:0px; margin:0px;"><pre class='brush:php;toolbar:false;'>def __setattr__(self, name, value): self.name = value # 每一次属性赋值时, __setattr__都会被调用,因此不断调用自身导致无限递归了。</pre><div class="contentsignin">ログイン後にコピー</div></div>🎜 実際には、クラスの作成を制御できるようにしたくない場合を除き、<code>__new__</code> を使用することはほとんどありません。 <br/><code>__new__</code> を説明する場合、多くの場合、<code>metaclass</code> (メタクラス) の導入が必要になります。 <br/>さらに詳しく知りたい場合は、私の他のブログ「Python のメタクラスを理解する」を参照してください🎜🎜<code>__new__</code> のオーバーロードについては、Python ドキュメントでも詳しく紹介されています。 🎜🎜オブジェクトのライフサイクルの終わりに、<code>__del__</code> が呼び出され、<code>__del__</code> は「デストラクター」として理解できます。<br/><code>__del__< /code code> は、オブジェクトがガベージ コレクションされるときの動作を定義します。 🎜🎜実際、<code>x.__del__()</code> は <code>del x</code> の実装ではありませんが、多くの場合 <code>del x</ を実行します。 code > は <code>x.__del__()</code> を呼び出します。🎜🎜この文をどのように理解すればよいでしょうか? 引き続き上記の Foo クラス コードを例として使用します:🎜<div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><pre class='brush:php;toolbar:false;'>def __setattr__(self, name, value): self.__dict__[name] = value</pre><div class="contentsignin">ログイン後にコピー</div></div><div class="contentsignin">ログイン後にコピー</div></div>🎜If <code>foo.__del__ () )</code> では、オブジェクト自体はまだ存在しますが、<code>del foo</code> が呼び出されると、オブジェクト foo はもう存在しません。🎜🎜 インタプリタが終了するときにオブジェクトがまだ存在する場合は、存在することに注意してください。 <code>__del__</code> が正確に実行されるという保証はありません。したがって、<code>__del__</code> は、優れたプログラミング習慣の代わりにはなりません。 <br/>たとえば、ソケットを処理するときは、完了した接続を時間内に閉じます。 🎜🎜属性アクセス制御🎜🎜 Python にはクラスのカプセル化がないことに文句を言いたい人が常にいます。たとえば、Python がプライベート プロパティを定義して、パブリックにアクセスできるゲッターとセッターを提供できることを望んでいます。 Python は実際に魔法のメソッドを通じてカプセル化を実現できます。 🎜<h4 id="code-getattr-self-name-code"><code>__getattr__(self, name)</code></h4>🎜このメソッドは、存在しない属性にアクセスしようとしたときの動作を定義します。したがって、このメソッドをオーバーロードすると、スペルミスを検出してリダイレクトしたり、一部の非推奨の属性について警告したりすることができます。 🎜<h4 id="code-setattr-self-name-value-code"><code>__setattr__(self, name, value)</code></h4>🎜<code>__setattr__</code> は、属性の割り当てと変更の動作を定義します。時間。 <br/>オブジェクトの特定の属性が存在するかどうかに関係なく、属性に値を割り当てることができるため、属性の値に対してカスタマイズされた操作を実行できます。注意すべき点の 1 つは、<code>__setattr__</code> を実装するときに「無限再帰」エラーを回避することです。これについては、次のコード例で説明します。 🎜<h4 id="code-delattr-self-name-code"><code>__delattr__(self, name)</code></h4>🎜<code>__delattr__</code> は <code>__setattr__</code> とよく似ていますが、削除することを定義している点が異なります。その時の属性行動。 「無限再帰」エラーを回避するには、<code>__delattr__</code> を実装することも必要です。 🎜<h4 id="code-getattribute-self-name-code"><code>__getattribute__(self, name)</code></h4>🎜<code>__getattribute__</code> は、<code>__getattr__</ code> のみと比較して、属性にアクセスしたときの動作を定義します属性が存在しない場合に機能します。 🎜🎜 したがって、<code>__getattribute__</code> をサポートする Python バージョンでは、<code>__getattr__</code> を呼び出す前に <code>__getattribute__</code> を使用する必要があります。 <code>__getattribute__</code>「無限再帰」エラーも回避します。 🎜🎜このアプローチはめったに見られず、バグが発生しやすいため、<code>__getattribute__</code> を実装しようとしないことが最善であることに注意してください。 🎜🎜<code>__setattr__</code> の無限再帰エラーを示す例:🎜<div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><pre class='brush:php;toolbar:false;'>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__调用</pre><div class="contentsignin">ログイン後にコピー</div></div><div class="contentsignin">ログイン後にコピー</div></div>🎜 したがって、正しい記述は次のようになります:🎜<div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><pre class='brush:php;toolbar:false;'>def __setattr__(self, name, value): self.__dict__[name] = value</pre><div class="contentsignin">ログイン後にコピー</div></div><div class="contentsignin">ログイン後にコピー</div></div><p><code>__delattr__</code>如果在其实现中出现<code>del self.name</code> 这样的代码也会出现”无限递归”错误,这是一样的原因。</p><p>下面的例子很好的说明了上面介绍的4个魔术方法的调用情况:</p><div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><pre class='brush:php;toolbar:false;'>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__调用</pre><div class="contentsignin">ログイン後にコピー</div></div><div class="contentsignin">ログイン後にコピー</div></div><h2 id="描述器对象">描述器对象</h2><p>我们从一个例子来入手,介绍什么是描述符,并介绍<code>__get__</code>, <code>__set__</code>, <code>__delete__</code> 的使用。(放在这里介绍是为了跟上一小节介绍的魔术方法作对比)</p><p>我们知道,距离既可以用单位”米”表示,也可以用单位”英尺”表示。现在我们定义一个类来表示距离,它有两个属性: 米和英尺。</p><div class="code" style="position:relative; padding:0px; margin:0px;"><pre class='brush:php;toolbar:false;'>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</pre><div class="contentsignin">ログイン後にコピー</div></div><p>在上面例子中,在还没有对Distance的实例赋值前, 我们认为meter和foot应该是各自类的实例对象, 但是输出却是数值。这是因为<code>__get__</code>发挥了作用.</p><p>我们只是修改了meter,并且将其赋值成为int,但foot也修改了。这是<code>__set__</code>发挥了作用.</p><p>描述器对象(Meter、Foot)不能独立存在, 它需要被另一个所有者类(Distance)所持有。</p><p>描述器对象可以访问到其拥有者实例的属性,比如例子中Foot的<code>instance.meter</code>。</p><p>在面向对象编程时,如果一个类的属性有相互依赖的关系时,使用描述器来编写代码可以很巧妙的组织逻辑。在Django的ORM中, models.Model中的InterField等字段, 就是通过描述器来实现功能的。</p><p>一个类要成为描述器,必须实现<code>__get__</code>, <code>__set__</code>, <code>__delete__</code> 中的至少一个方法。下面简单介绍下:</p><h4 id="code-get-self-instance-owner-code"><code>__get__(self, instance, owner)</code></h4><p>参数instance是拥有者类的实例。参数owner是拥有者类本身。<code>__get__</code>在其拥有者对其读值的时候调用。</p><h4 id="code-set-self-instance-value-code"><code>__set__(self, instance, value)</code></h4><p><code>__set__</code>在其拥有者对其进行修改值的时候调用。</p><h4 id="code-delete-self-instance-code"><code>__delete__(self, instance)</code></h4><p><code>__delete__</code>在其拥有者对其进行删除的时候调用。</p><h2 id="构造自定义容器-Container">构造自定义容器(Container)</h2><p>在Python中,常见的容器类型有: dict, tuple, list, string。</p><p>其中tuple, string是不可变容器,dict, list是可变容器。</p><p>可变容器和不可变容器的区别在于,不可变容器一旦赋值后,不可对其中的某个元素进行修改。</p><p>比如定义了<code>l = [1, 2, 3]</code>和<code>t = (1, 2, 3)</code>后, 执行<code>l[0] = 0</code>是可以的,但执行<code>t[0] = 0</code>则会报错。</p><p>如果我们要自定义一些数据结构,使之能够跟以上的容器类型表现一样,那就需要去实现某些协议。</p><p>这里的协议跟其他语言中所谓的”接口”概念很像,一样的需要你去实现才行,只不过没那么正式而已。</p><p>如果要自定义不可变容器类型,只需要定义<code>__len__</code> 和 <code>__getitem__</code>方法;</p><p>如果要自定义可变容器类型,还需要在不可变容器类型的基础上增加定义<code>__setitem__</code> 和 <code>__delitem__</code>。</p><p>如果你希望你的自定义数据结构还支持”可迭代”, 那就还需要定义<code>__iter__</code>。</p><h4 id="code-len-self-code"><code>__len__(self)</code></h4><p>需要返回数值类型,以表示容器的长度。该方法在可变容器和不可变容器中必须实现。</p><h4 id="code-getitem-self-key-code"><code>__getitem__(self, key)</code></h4><p>当你执行<code>self[key]</code>的时候,调用的就是该方法。该方法在可变容器和不可变容器中也都必须实现。<br/>调用的时候,如果key的类型错误,该方法应该抛出TypeError;<br/>如果没法返回key对应的数值时,该方法应该抛出ValueError。</p><h4 id="code-setitem-self-key-value-code"><code>__setitem__(self, key, value)</code></h4><p>当你执行<code>self[key] = value</code>时,调用的是该方法。</p><h4 id="code-delitem-self-key-code"><code>__delitem__(self, key)</code></h4><p>当你执行<code>del self[key]</code>的时候,调用的是该方法。</p><h4 id="code-iter-self-code"><code>__iter__(self)</code></h4><p>该方法需要返回一个迭代器(iterator)。当你执行<code>for x in container:</code> 或者使用<code>iter(container)</code>时,该方法被调用。</p><h4 id="code-reversed-self-code"><code>__reversed__(self)</code></h4><p>如果想要该数据结构被內建函数<code>reversed()</code>支持,就还需要实现该方法。</p><h4 id="code-contains-self-item-code"><code>__contains__(self, item)</code></h4><p>如果定义了该方法,那么在执行<code>item in container</code> 或者 <code>item not in container</code>时该方法就会被调用。<br/>如果没有定义,那么Python会迭代容器中的元素来一个一个比较,从而决定返回True或者False。</p><h4 id="code-missing-self-key-code"><code>__missing__(self, key)</code></h4><p><code>dict</code>字典类型会有该方法,它定义了key如果在容器中找不到时触发的行为。<br/>比如<code>d = {'a': 1}</code>, 当你执行<code>d[notexist]</code>时,<code>d.__missing__['notexist']</code>就会被调用。</p><p>下面举例,使用上面讲的魔术方法来实现Haskell语言中的一个数据结构。</p><div class="code" style="position:relative; padding:0px; margin:0px;"><pre class='brush:php;toolbar:false;'># -*- coding: utf-8 -*- class FunctionalList: ''' 实现了内置类型list的功能,并丰富了一些其他方法: head, tail, init, last, drop, take''' def __init__(self, values=None): if values is None: self.values = [] else: self.values = values def __len__(self): return len(self.values) def __getitem__(self, key): return self.values[key] def __setitem__(self, key, value): self.values[key] = value def __delitem__(self, key): del self.values[key] def __iter__(self): return iter(self.values) def __reversed__(self): return FunctionalList(reversed(self.values)) def append(self, value): self.values.append(value) def head(self): # 获取第一个元素 return self.values[0] def tail(self): # 获取第一个元素之后的所有元素 return self.values[1:] def init(self): # 获取最后一个元素之前的所有元素 return self.values[:-1] def last(self): # 获取最后一个元素 return self.values[-1] def drop(self, n): # 获取所有元素,除了前N个 return self.values[n:] def take(self, n): # 获取前N个元素 return self.values[:n]</pre><div class="contentsignin">ログイン後にコピー</div></div><p>我们再举个例子,实现Perl语言的autovivification,它会在你每次引用一个值未定义的属性时为你自动创建数组或者字典。</p><div class="code" style="position:relative; padding:0px; margin:0px;"><pre class='brush:php;toolbar:false;'>class AutoVivification(dict): """Implementation of perl's autovivification feature.""" def __missing__(self, key): value = self[key] = type(self)() return value weather = AutoVivification() weather['china']['guangdong']['shenzhen'] = 'sunny' weather['china']['hubei']['wuhan'] = 'windy' weather['USA']['California']['Los Angeles'] = 'sunny' print weather 结果输出:{'china': {'hubei': {'wuhan': 'windy'}, 'guangdong': {'shenzhen': 'sunny'}}, 'USA': {'California': {'Los Angeles': 'sunny'}}}</pre><div class="contentsignin">ログイン後にコピー</div></div><p>在Python中,关于自定义容器的实现还有更多实用的例子,但只有很少一部分能够集成在Python标准库中,比如Counter, OrderedDict等</p><h2 id="上下文管理">上下文管理</h2><p><code>with</code>声明是从Python2.5开始引进的关键词。你应该遇过这样子的代码:</p><div class="code" style="position:relative; padding:0px; margin:0px;"><pre class='brush:php;toolbar:false;'>with open('foo.txt') as bar: # do something with bar</pre><div class="contentsignin">ログイン後にコピー</div></div><p>在with声明的代码段中,我们可以做一些对象的开始操作和清除操作,还能对异常进行处理。</p><p>这需要实现两个魔术方法: <code>__enter__</code> 和 <code>__exit__</code>。</p><h4 id="code-enter-self-code"><code>__enter__(self)</code></h4><p><code>__enter__</code>会返回一个值,并赋值给<code>as</code>关键词之后的变量。在这里,你可以定义代码段开始的一些操作。</p><h4 id="code-exit-self-exception-type-exception-value-traceback-code"><code>__exit__(self, exception_type, exception_value, traceback)</code></h4><p><code>__exit__</code>定义了代码段结束后的一些操作,可以这里执行一些清除操作,或者做一些代码段结束后需要立即执行的命令,比如文件的关闭,socket断开等。如果代码段成功结束,那么exception_type, exception_value, traceback 三个参数传进来时都将为None。如果代码段抛出异常,那么传进来的三个参数将分别为: 异常的类型,异常的值,异常的追踪栈。</p><p>如果<code>__exit__</code>返回True, 那么with声明下的代码段的一切异常将会被屏蔽。</p><p>如果<code>__exit__</code>返回None, 那么如果有异常,异常将正常抛出,这时候with的作用将不会显现出来。</p><p>举例说明:</p><p>这该示例中,IndexError始终会被隐藏,而TypeError始终会抛出。</p><div class="code" style="position:relative; padding:0px; margin:0px;"><pre class='brush:php;toolbar:false;'>class DemoManager(object): def __enter__(self): pass def __exit__(self, ex_type, ex_value, ex_tb): if ex_type is IndexError: print ex_value.__class__ return True if ex_type is TypeError: print ex_value.__class__ return # return None with DemoManager() as nothing: data = [1, 2, 3] data[4] # raise IndexError, 该异常被__exit__处理了 with DemoManager() as nothing: data = [1, 2, 3] data['a'] # raise TypeError, 该异常没有被__exit__处理 输出: <type 'exceptions.IndexError'> <type 'exceptions.TypeError'> Traceback (most recent call last): ...</pre><div class="contentsignin">ログイン後にコピー</div></div><h2 id="对象的序列化">对象的序列化</h2><p>Python对象的序列化操作是pickling进行的。pickling非常的重要,以至于Python对此有单独的模块<code>pickle</code>,还有一些相关的魔术方法。使用pickling, 你可以将数据存储在文件中,之后又从文件中进行恢复。</p><p>下面举例来描述pickle的操作。从该例子中也可以看出,如果通过pickle.load 初始化一个对象, 并不会调用<code>__init__</code>方法。</p><div class="code" style="position:relative; padding:0px; margin:0px;"><pre class='brush:php;toolbar:false;'># -*- coding: utf-8 -*- from datetime import datetime import pickle class Distance(object): def __init__(self, meter): print 'distance __init__' self.meter = meter data = { 'foo': [1, 2, 3], 'bar': ('Hello', 'world!'), 'baz': True, 'dt': datetime(2016, 10, 01), 'distance': Distance(1.78), } print 'before dump:', data with open('data.pkl', 'wb') as jar: pickle.dump(data, jar) # 将数据存储在文件中 del data print 'data is deleted!' with open('data.pkl', 'rb') as jar: data = pickle.load(jar) # 从文件中恢复数据 print 'after load:', data</pre><div class="contentsignin">ログイン後にコピー</div></div><p>值得一提,从其他文件进行pickle.load操作时,需要注意有恶意代码的可能性。另外,Python的各个版本之间,pickle文件可能是互不兼容的。</p><p>pickling并不是Python的內建类型,它支持所有实现pickle协议(可理解为接口)的类。pickle协议有以下几个可选方法来自定义Python对象的行为。</p><h4 id="code-getinitargs-self-code"><code>__getinitargs__(self)</code></h4><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><h4 id="code-getnewargs-self-code"><code>__getnewargs__(self)</code></h4><p>跟<code>__getinitargs__</code>很类似,只不过返回的参数元组将传值给<code>__new__</code></p><h4 id="code-getstate-self-code"><code>__getstate__(self)</code></h4><p>在调用<code>pickle.dump</code>时,默认是对象的<code>__dict__</code>属性被存储,如果你要修改这种行为,可以在<code>__getstate__</code>方法中返回一个state。state将在调用<code>pickle.load</code>时传值给<code>__setstate__</code></p><h4 id="code-setstate-self-state-code"><code>__setstate__(self, state)</code></h4><p>一般来说,定义了<code>__getstate__</code>,就需要相应地定义<code>__setstate__</code>来对<code>__getstate__</code>返回的state进行处理。</p><h4 id="code-reduce-self-code"><code>__reduce__(self)</code></h4><p>如果pickle的数据包含了自定义的扩展类(比如使用C语言实现的Python扩展类)时,就需要通过实现<code>__reduce__</code>方法来控制行为了。由于使用过于生僻,这里就不展开继续讲解了。</p><p>令人容易混淆的是,我们知道, <code>reduce()</code>是Python的一个內建函数, 需要指出<code>__reduce__</code>并非定义了<code>reduce()</code>的行为,二者没有关系。</p><h4 id="code-reduce-ex-self-code"><code>__reduce_ex__(self)</code></h4><p><code>__reduce_ex__</code> 是为了兼容性而存在的, 如果定义了<code>__reduce_ex__</code>, 它将代替<code>__reduce__</code> 执行。</p><p>下面的代码示例很有意思,我们定义了一个类Slate(中文是板岩的意思)。这个类能够记录历史上每次写入给它的值,但每次<code>pickle.dump</code>时当前值就会被清空,仅保留了历史。</p><div class="code" style="position:relative; padding:0px; margin:0px;"><pre class='brush:php;toolbar:false;'># -*- coding: utf-8 -*- import pickle import time class Slate: '''Class to store a string and a changelog, and forget its value when pickled.''' def __init__(self, value): self.value = value self.last_change = time.time() self.history = [] def change(self, new_value): # 修改value, 将上次的valeu记录在history self.history.append((self.last_change, self.value)) self.value = new_value self.last_change = time.time() def print_changes(self): print 'Changelog for Slate object:' for k, v in self.history: print '%s %s' % (k, v) def __getstate__(self): # 故意不返回self.value和self.last_change, # 以便每次unpickle时清空当前的状态,仅仅保留history return self.history def __setstate__(self, state): self.history = state self.value, self.last_change = None, None slate = Slate(0) time.sleep(0.5) slate.change(100) time.sleep(0.5) slate.change(200) slate.change(300) slate.print_changes() # 与下面的输出历史对比 with open('slate.pkl', 'wb') as jar: pickle.dump(slate, jar) del slate # delete it with open('slate.pkl', 'rb') as jar: slate = pickle.load(jar) print 'current value:', slate.value # None print slate.print_changes() # 输出历史记录与上面一致</pre><div class="contentsignin">ログイン後にコピー</div></div><h2 id="运算符相关的魔术方法">运算符相关的魔术方法</h2><p>运算符相关的魔术方法实在太多了,也很好理解,不打算多讲。在其他语言里,也有重载运算符的操作,所以我们对这些魔术方法已经很了解了。</p><h3 id="比较运算符">比较运算符</h3><h4 id="code-cmp-self-other-code"><code>__cmp__(self, other)</code></h4><p>如果该方法返回负数,说明<code>self < other</code>; 返回正数,说明<code>self > other</code>; 返回0说明<code>self == other</code>。</p><p>强烈不推荐来定义<code>__cmp__</code>, 取而代之, 最好分别定义<code>__lt__</code>等方法从而实现比较功能。<br/><code>__cmp__</code>在Python3中被废弃了。</p><h4 id="code-eq-self-other-code"><code>__eq__(self, other)</code></h4><p>定义了比较操作符<code>==</code>的行为.</p><h4 id="code-ne-self-other-code"><code>__ne__(self, other)</code></h4><p>定义了比较操作符<code>!=</code>的行为.</p><h4 id="code-lt-self-other-code"><code>__lt__(self, other)</code></h4><p>定义了比较操作符<code><</code>的行为.</p><h4 id="code-gt-self-other-code"><code>__gt__(self, other)</code></h4><p>定义了比较操作符<code>></code>的行为.</p><h4 id="code-le-self-other-code"><code>__le__(self, other)</code></h4><p>定义了比较操作符<code><=</code>的行为.</p><h4 id="code-ge-self-other-code"><code>__ge__(self, other)</code></h4><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><div class="code" style="position:relative; padding:0px; margin:0px;"><pre class='brush:php;toolbar:false;'># -*- coding: utf-8 -*- class Word(str): '''存储单词的类,定义比较单词的几种方法''' def __new__(cls, word): # 注意我们必须要用到__new__方法,因为str是不可变类型 # 所以我们必须在创建的时候将它初始化 if ' ' in word: print "Value contains spaces. Truncating to first space." word = word[:word.index(' ')] # 单词是第一个空格之前的所有字符 return str.__new__(cls, word) def __gt__(self, other): return len(self) > len(other) def __lt__(self, other): return len(self) < len(other) def __ge__(self, other): return len(self) >= len(other) def __le__(self, other): return len(self) <= len(other) print 'foo < fool:', Word('foo') < Word('fool') # True print 'foolish > fool:', Word('foolish') > Word('fool') # True print 'bar >= foo:', Word('bar') >= Word('foo') # True print 'bar <= foo:', Word('bar') <= Word('foo') # True print 'bar == foo:', Word('bar') == Word('foo') # False, 用了str内置的比较方法来进行比较 print 'bar != foo:', Word('bar') != Word('foo') # True</pre><div class="contentsignin">ログイン後にコピー</div></div><h3 id="一元运算符和函数">一元运算符和函数</h3><h4 id="code-pos-self-code"><code>__pos__(self)</code></h4><p>实现了’+'号一元运算符(比如<code>+some_object</code>)</p><h4 id="code-neg-self-code"><code>__neg__(self)</code></h4><p>实现了’-'号一元运算符(比如<code>-some_object</code>)</p><h4 id="code-invert-self-code"><code>__invert__(self)</code></h4><p>实现了<code>~</code>号一元运算符(比如<code>~some_object</code>)</p><h4 id="code-abs-self-code"><code>__abs__(self)</code></h4><p>实现了<code>abs()</code>內建函数.</p><h4 id="code-round-self-n-code"><code>__round__(self, n)</code></h4><p>实现了<code>round()</code>内建函数. 参数n表示四舍五进的精度.</p><h4 id="code-floor-self-code"><code>__floor__(self)</code></h4><p>实现了<code>math.round()</code>, 向下取整.</p><h4 id="code-ceil-self-code"><code>__ceil__(self)</code></h4><p>实现了<code>math.ceil()</code>, 向上取整.</p><h4 id="code-trunc-self-code"><code>__trunc__(self)</code></h4><p>实现了<code>math.trunc()</code>, 向0取整.</p><h3 id="算术运算符">算术运算符</h3><h4 id="code-add-self-other-code"><code>__add__(self, other)</code></h4><p>实现了加号运算.</p><h4 id="code-sub-self-other-code"><code>__sub__(self, other)</code></h4><p>实现了减号运算.</p><h4 id="code-mul-self-other-code"><code>__mul__(self, other)</code></h4><p>实现了乘法运算.</p><h4 id="code-floorp-self-other-code"><code>__floorp__(self, other)</code></h4><p>实现了<code>//</code>运算符.</p><h4 id="code-p-self-other-code"><code>__p__(self, other)</code></h4><p>实现了<code>/</code>运算符. 该方法在Python3中废弃. 原因是Python3中,pision默认就是true pision.</p><h4 id="code-truep-code-self-other"><code>__truep__</code>(self, other)</h4><p>实现了true pision. 只有你声明了<code>from __future__ import pision</code>该方法才会生效.</p><h4 id="code-mod-self-other-code"><code>__mod__(self, other)</code></h4><p>实现了<code>%</code>运算符, 取余运算.</p><h4 id="code-pmod-self-other-code"><code>__pmod__(self, other)</code></h4><p>实现了<code>pmod()</code>內建函数.</p><h4 id="code-pow-self-other-code"><code>__pow__(self, other)</code></h4><p>实现了<code>**</code>操作. N次方操作.</p><h4 id="code-lshift-self-other-code"><code>__lshift__(self, other)</code></h4><p>实现了位操作<code><<</code>.</p><h4 id="code-rshift-self-other-code"><code>__rshift__(self, other)</code></h4><p>实现了位操作<code>>></code>.</p><h4 id="code-and-self-other-code"><code>__and__(self, other)</code></h4><p>实现了位操作<code>&</code>.</p><h4 id="code-or-self-other-code"><code>__or__(self, other)</code></h4><p>实现了位操作<code>|</code></p><h4 id="code-xor-self-other-code"><code>__xor__(self, other)</code></h4><p>实现了位操作<code>^</code></p><h3 id="反算术运算符">反算术运算符</h3><p>这里只需要解释一下概念即可。假设针对some_object这个对象:</p><div class="code" style="position:relative; padding:0px; margin:0px;"><pre class='brush:php;toolbar:false;'>some_object + other</pre><div class="contentsignin">ログイン後にコピー</div></div><p>上面的代码非常正常地实现了some_object的<code>__add__</code>方法。那么如果遇到相反的情况呢?</p><div class="code" style="position:relative; padding:0px; margin:0px;"><pre class='brush:php;toolbar:false;'>other + some_object</pre><div class="contentsignin">ログイン後にコピー</div></div><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 class=" list-paddingleft-2"><li><p><code>__rsub__(self, other)</code></p></li><li><p><code>__rmul__(self, other)</code></p></li><li><p><code>__rmul__(self, other)</code></p></li><li><p><code>__rfloorp__(self, other)</code></p></li><li><p><code>__rp__(self, other)</code></p></li><li><p><code>__rtruep__(self, other)</code></p></li><li><p><code>__rmod__(self, other)</code></p></li><li><p><code>__rpmod__(self, other)</code></p></li><li><p><code>__rpow__(self, other)</code></p></li><li><p><code>__rlshift__(self, other)</code></p></li><li><p><code>__rrshift__(self, other)</code></p></li><li><p><code>__rand__(self, other)</code></p></li><li><p><code>__ror__(self, other)</code></p></li><li><p><code>__rxor__(self, other)</code></p></li></ul><h3 id="增量赋值">增量赋值</h3><p>这也是只要理解了概念就容易掌握的运算。举个例子:</p><div class="code" style="position:relative; padding:0px; margin:0px;"><pre class='brush:php;toolbar:false;'>x = 5 x += 1 # 这里的+=就是增量赋值,将x+1赋值给了x</pre><div class="contentsignin">ログイン後にコピー</div></div><p>因此对于<code>a += b</code>, <code>__iadd__</code> 将返回<code>a + b</code>, 并赋值给a。<br/>所以很容易理解下面的魔术方法了:</p><ul class=" list-paddingleft-2"><li><p><code>__iadd__(self, other)</code></p></li><li><p><code>__isub__(self, other)</code></p></li><li><p><code>__imul__(self, other)</code></p></li><li><p><code>__ifloorp__(self, other)</code></p></li><li><p><code>__ip__(self, other)</code></p></li><li><p><code>__itruep__(self, other)</code></p></li><li><p><code>__imod__(self, other)</code></p></li><li><p><code>__ipow__(self, other)</code></p></li><li><p><code>__ilshift__(self, other)</code></p></li><li><p><code>__irshift__(self, other)</code></p></li><li><p><code>__iand__(self, other)</code></p></li><li><p><code>__ior__(self, other)</code></p></li><li><p><code>__ixor__(self, other)</code></p></li></ul><h3 id="类型转化">类型转化</h3><h4 id="code-int-self-code"><code>__int__(self)</code></h4><p>实现了类型转化为int的行为.</p><h4 id="code-long-self-code"><code>__long__(self)</code></h4><p>实现了类型转化为long的行为.</p><h4 id="code-float-self-code"><code>__float__(self)</code></h4><p>实现了类型转化为float的行为.</p><h4 id="code-complex-self-code"><code>__complex__(self)</code></h4><p>实现了类型转化为complex(复数, 也即1+2j这样的虚数)的行为.</p><h4 id="code-oct-self-code"><code>__oct__(self)</code></h4><p>实现了类型转化为八进制数的行为.</p><h4 id="code-hex-self-code"><code>__hex__(self)</code></h4><p>实现了类型转化为十六进制数的行为.</p><h4 id="code-index-self-code"><code>__index__(self)</code></h4><p>在切片运算中将对象转化为int, 因此该方法的返回值必须是int。用一个例子来解释这个用法。</p><div class="code" style="position:relative; padding:0px; margin:0px;"><pre class='brush:php;toolbar:false;'>class Thing(object): def __index__(self): return 1 thing = Thing() list_ = ['a', 'b', 'c'] print list_[thing] # 'b' print list_[thing:thing] # []</pre><div class="contentsignin">ログイン後にコピー</div></div><p>上面例子中, <code>list_[thing]</code>的表现跟<code>list_[1]</code>一致,正是因为Thing实现了<code>__index__</code>方法。</p><p>可能有的人会想,<code>list_[thing]</code>为什么不是相当于<code>list_[int(thing)]</code>呢? 通过实现Thing的<code>__int__</code>方法能否达到这个目的呢?</p><p>显然不能。如果真的是这样的话,那么<code>list_[1.1:2.2]</code>这样的写法也应该是通过的。<br/>而实际上,该写法会抛出TypeError: <code>slice indices must be integers or None or have an __index__ method</code></p><p>下面我们再做个例子,如果对一个dict对象执行<code>dict_[thing]</code>会怎么样呢?</p><div class="code" style="position:relative; padding:0px; margin:0px;"><pre class='brush:php;toolbar:false;'>dict_ = {1: 'apple', 2: 'banana', 3: 'cat'} print dict_[thing] # raise KeyError</pre><div class="contentsignin">ログイン後にコピー</div></div><p>这个时候就不是调用<code>__index__</code>了。虽然<code>list</code>和<code>dict</code>都实现了<code>__getitem__</code>方法, 但是它们的实现方式是不一样的。<br/>如果希望上面例子能够正常执行, 需要实现Thing的<code>__hash__</code> 和 <code>__eq__</code>方法.</p><div class="code" style="position:relative; padding:0px; margin:0px;"><pre class='brush:php;toolbar:false;'>class Thing(object): def __hash__(self): return 1 def __eq__(self, other): return hash(self) == hash(other) dict_ = {1: 'apple', 2: 'banana', 3: 'cat'} print dict_[thing] # apple</pre><div class="contentsignin">ログイン後にコピー</div></div><h4 id="code-coerce-self-other-code"><code>__coerce__(self, other)</code></h4><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="其他魔术方法">其他魔术方法</h2><p>还没讲到的魔术方法还有很多,但有些我觉得很简单,或者很少见,就不再累赘展开说明了。</p><h4 id="code-str-self-code"><code>__str__(self)</code></h4><p>对实例使用<code>str()</code>时调用。</p><h4 id="code-repr-self-code"><code>__repr__(self)</code></h4><p>对实例使用<code>repr()</code>时调用。<code>str()</code>和<code>repr()</code>都是返回一个代表该实例的字符串,<br/>主要区别在于: str()的返回值要方便人来看,而repr()的返回值要方便计算机看。</p><h4 id="code-unicode-self-code"><code>__unicode__(self)</code></h4><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><div class="code" style="position:relative; padding:0px; margin:0px;"><pre class='brush:php;toolbar:false;'>class StrDemo2: def __str__(self): return 'StrDemo2' class StrDemo3: def __unicode__(self): return u'StrDemo3' demo2 = StrDemo2() print str(demo2) # StrDemo2 print unicode(demo2) # StrDemo2 demo3 = StrDemo3() print str(demo3) # <__main__.StrDemo3 instance> print unicode(demo3) # StrDemo3</pre><div class="contentsignin">ログイン後にコピー</div></div><h4 id="code-format-self-formatstr-code"><code>__format__(self, formatstr)</code></h4><p><code>"Hello, {0:abc}".format(a)</code>等价于<code>format(a, "abc")</code>, 等价于<code>a.__format__("abc")</code>。</p><p>这在需要格式化展示对象的时候非常有用,比如格式化时间对象。</p><h4 id="code-hash-self-code"><code>__hash__(self)</code></h4><p>对实例使用<code>hash()</code>时调用, 返回值是数值类型。</p><h4 id="code-nonzero-self-code"><code>__nonzero__(self)</code></h4><p>对实例使用<code>bool()</code>时调用, 返回True或者False。<br/>你可能会问, 为什么不是命名为<code>__bool__</code>? 我也不知道。<br/>我只知道该方法在Python3中改名为<code>__bool__</code>了。</p><h4 id="code-dir-self-code"><code>__dir__(self)</code></h4><p>对实例使用<code>dir()</code>时调用。通常实现该方法是没必要的。</p><h4 id="code-sizeof-self-code"><code>__sizeof__(self)</code></h4><p>对实例使用<code>sys.getsizeof()</code>时调用。返回对象的大小,单位是bytes。</p><h4 id="code-instancecheck-self-instance-code"><code>__instancecheck__(self, instance)</code></h4><p>对实例调用<code>isinstance(instance, class)</code>时调用。 返回值是布尔值。它会判断instance是否是该类的实例。</p><h4 id="code-subclasscheck-self-subclass-code"><code>__subclasscheck__(self, subclass)</code></h4><p>对实例使用<code>issubclass(subclass, class)</code>时调用。返回值是布尔值。它会判断subclass否是该类的子类。</p><h4 id="code-copy-self-code"><code>__copy__(self)</code></h4><p>对实例使用<code>copy.copy()</code>时调用。返回”浅复制”的对象。</p><h4 id="code-deepcopy-self-memodict-code"><code>__deepcopy__(self, memodict={})</code></h4><p>对实例使用<code>copy.deepcopy()</code>时调用。返回”深复制”的对象。</p><h4 id="code-call-self-args-code"><code>__call__(self, [args...])</code></h4><p>该方法允许类的实例跟函数一样表现:</p><div class="code" style="position:relative; padding:0px; margin:0px;"><pre class='brush:php;toolbar:false;'>class XClass: def __call__(self, a, b): return a + b def add(a, b): return a + b x = XClass() print 'x(1, 2)', x(1, 2) print 'callable(x)', callable(x) # True print 'add(1, 2)', add(1, 2) print 'callable(add)', callable(add) # True</pre><div class="contentsignin">ログイン後にコピー</div></div><h2 id="Python-中的差异">Python3中的差异</h2> <ul class=" list-paddingleft-2"> <li><p>Python3中,str与unicode的区别被废除了,因而<code>__unicode__</code>没有了,取而代之地出现了<code>__bytes__</code>.</p></li> <li><p>Python3中,pision默认就是true pision, 因而<code>__p__</code>废弃.</p></li> <li><p><code>__coerce__</code>因存在冗余而废弃.</p></li> <li><p><code>__cmp__</code>因存在冗余而废弃.</p></li> <li><p><code>__nonzero__</code>改名为<code>__bool__</code>.</p></li> </ul> <div></div>

以上がPython のマジック メソッドの紹介の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。

ホットAIツール

Undresser.AI Undress

Undresser.AI Undress

リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover

AI Clothes Remover

写真から衣服を削除するオンライン AI ツール。

Undress AI Tool

Undress AI Tool

脱衣画像を無料で

Clothoff.io

Clothoff.io

AI衣類リムーバー

Video Face Swap

Video Face Swap

完全無料の AI 顔交換ツールを使用して、あらゆるビデオの顔を簡単に交換できます。

ホットツール

メモ帳++7.3.1

メモ帳++7.3.1

使いやすく無料のコードエディター

SublimeText3 中国語版

SublimeText3 中国語版

中国語版、とても使いやすい

ゼンドスタジオ 13.0.1

ゼンドスタジオ 13.0.1

強力な PHP 統合開発環境

ドリームウィーバー CS6

ドリームウィーバー CS6

ビジュアル Web 開発ツール

SublimeText3 Mac版

SublimeText3 Mac版

神レベルのコード編集ソフト(SublimeText3)

PHPおよびPython:さまざまなパラダイムが説明されています PHPおよびPython:さまざまなパラダイムが説明されています Apr 18, 2025 am 12:26 AM

PHPは主に手順プログラミングですが、オブジェクト指向プログラミング(OOP)もサポートしています。 Pythonは、OOP、機能、手続き上のプログラミングなど、さまざまなパラダイムをサポートしています。 PHPはWeb開発に適しており、Pythonはデータ分析や機械学習などのさまざまなアプリケーションに適しています。

PHPとPythonの選択:ガイド PHPとPythonの選択:ガイド Apr 18, 2025 am 12:24 AM

PHPはWeb開発と迅速なプロトタイピングに適しており、Pythonはデータサイエンスと機械学習に適しています。 1.PHPは、単純な構文と迅速な開発に適した動的なWeb開発に使用されます。 2。Pythonには簡潔な構文があり、複数のフィールドに適しており、強力なライブラリエコシステムがあります。

Python vs. JavaScript:学習曲線と使いやすさ Python vs. JavaScript:学習曲線と使いやすさ Apr 16, 2025 am 12:12 AM

Pythonは、スムーズな学習曲線と簡潔な構文を備えた初心者により適しています。 JavaScriptは、急な学習曲線と柔軟な構文を備えたフロントエンド開発に適しています。 1。Python構文は直感的で、データサイエンスやバックエンド開発に適しています。 2。JavaScriptは柔軟で、フロントエンドおよびサーバー側のプログラミングで広く使用されています。

Visual StudioコードはPythonで使用できますか Visual StudioコードはPythonで使用できますか Apr 15, 2025 pm 08:18 PM

VSコードはPythonの書き込みに使用でき、Pythonアプリケーションを開発するための理想的なツールになる多くの機能を提供できます。ユーザーは以下を可能にします。Python拡張機能をインストールして、コードの完了、構文の強調表示、デバッグなどの関数を取得できます。デバッガーを使用して、コードを段階的に追跡し、エラーを見つけて修正します。バージョンコントロールのためにGitを統合します。コードフォーマットツールを使用して、コードの一貫性を維持します。糸くずツールを使用して、事前に潜在的な問題を発見します。

Windows 8でコードを実行できます Windows 8でコードを実行できます Apr 15, 2025 pm 07:24 PM

VSコードはWindows 8で実行できますが、エクスペリエンスは大きくない場合があります。まず、システムが最新のパッチに更新されていることを確認してから、システムアーキテクチャに一致するVSコードインストールパッケージをダウンロードして、プロンプトとしてインストールします。インストール後、一部の拡張機能はWindows 8と互換性があり、代替拡張機能を探すか、仮想マシンで新しいWindowsシステムを使用する必要があることに注意してください。必要な拡張機能をインストールして、適切に動作するかどうかを確認します。 Windows 8ではVSコードは実行可能ですが、開発エクスペリエンスとセキュリティを向上させるために、新しいWindowsシステムにアップグレードすることをお勧めします。

VSCODE拡張機能は悪意がありますか? VSCODE拡張機能は悪意がありますか? Apr 15, 2025 pm 07:57 PM

VSコード拡張機能は、悪意のあるコードの隠れ、脆弱性の活用、合法的な拡張機能としての自慰行為など、悪意のあるリスクを引き起こします。悪意のある拡張機能を識別する方法には、パブリッシャーのチェック、コメントの読み取り、コードのチェック、およびインストールに注意してください。セキュリティ対策には、セキュリティ認識、良好な習慣、定期的な更新、ウイルス対策ソフトウェアも含まれます。

PHPとPython:彼らの歴史を深く掘り下げます PHPとPython:彼らの歴史を深く掘り下げます Apr 18, 2025 am 12:25 AM

PHPは1994年に発信され、Rasmuslerdorfによって開発されました。もともとはウェブサイトの訪問者を追跡するために使用され、サーバー側のスクリプト言語に徐々に進化し、Web開発で広く使用されていました。 Pythonは、1980年代後半にGuidovan Rossumによって開発され、1991年に最初にリリースされました。コードの読みやすさとシンプルさを強調し、科学的コンピューティング、データ分析、その他の分野に適しています。

ターミナルVSCODEでプログラムを実行する方法 ターミナルVSCODEでプログラムを実行する方法 Apr 15, 2025 pm 06:42 PM

VSコードでは、次の手順を通じて端末でプログラムを実行できます。コードを準備し、統合端子を開き、コードディレクトリが端末作業ディレクトリと一致していることを確認します。プログラミング言語(pythonのpython your_file_name.pyなど)に従って実行コマンドを選択して、それが正常に実行されるかどうかを確認し、エラーを解決します。デバッガーを使用して、デバッグ効率を向上させます。

See all articles