Files
hustoj/web/python-docs/howto/descriptor.html
2025-03-09 17:10:56 +08:00

618 lines
61 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" lang="zh_CN">
<head>
<meta charset="utf-8" /><meta name="generator" content="Docutils 0.17.1: http://docutils.sourceforge.net/" />
<title>描述器使用指南 &#8212; Python 3.8.20 文档</title><meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="../_static/pydoctheme.css" type="text/css" />
<link rel="stylesheet" href="../_static/pygments.css" type="text/css" />
<script id="documentation_options" data-url_root="../" src="../_static/documentation_options.js"></script>
<script src="../_static/jquery.js"></script>
<script src="../_static/underscore.js"></script>
<script src="../_static/doctools.js"></script>
<script src="../_static/language_data.js"></script>
<script src="../_static/translations.js"></script>
<script src="../_static/sidebar.js"></script>
<link rel="search" type="application/opensearchdescription+xml"
title="在 Python 3.8.20 文档 中搜索"
href="../_static/opensearch.xml"/>
<link rel="author" title="关于这些文档" href="../about.html" />
<link rel="index" title="索引" href="../genindex.html" />
<link rel="search" title="搜索" href="../search.html" />
<link rel="copyright" title="版权所有" href="../copyright.html" />
<link rel="next" title="函数式编程指引" href="functional.html" />
<link rel="prev" title="用 Python 进行 Curses 编程" href="curses.html" />
<link rel="canonical" href="https://docs.python.org/3/howto/descriptor.html" />
<style>
@media only screen {
table.full-width-table {
width: 100%;
}
}
</style>
<link rel="shortcut icon" type="image/png" href="../_static/py.svg" />
<script type="text/javascript" src="../_static/copybutton.js"></script>
<script type="text/javascript" src="../_static/menu.js"></script>
</head>
<body>
<div class="mobile-nav">
<input type="checkbox" id="menuToggler" class="toggler__input" aria-controls="navigation"
aria-pressed="false" aria-expanded="false" role="button" aria-label="Menu" />
<label for="menuToggler" class="toggler__label">
<span></span>
</label>
<nav class="nav-content" role="navigation">
<a href="https://www.python.org/" class="nav-logo">
<img src="../_static/py.svg" alt="Logo"/>
</a>
<div class="version_switcher_placeholder"></div>
<form role="search" class="search" action="../search.html" method="get">
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" class="search-icon">
<path fill-rule="nonzero"
d="M15.5 14h-.79l-.28-.27a6.5 6.5 0 001.48-5.34c-.47-2.78-2.79-5-5.59-5.34a6.505 6.505 0 00-7.27 7.27c.34 2.8 2.56 5.12 5.34 5.59a6.5 6.5 0 005.34-1.48l.27.28v.79l4.25 4.25c.41.41 1.08.41 1.49 0 .41-.41.41-1.08 0-1.49L15.5 14zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z" fill="#444"></path>
</svg>
<input type="text" name="q" aria-label="快速搜索"/>
<input type="submit" value="转向"/>
</form>
</nav>
<div class="menu-wrapper">
<nav class="menu" role="navigation" aria-label="main navigation">
<div class="language_switcher_placeholder"></div>
<h3><a href="../contents.html">目录</a></h3>
<ul>
<li><a class="reference internal" href="#">描述器使用指南</a><ul>
<li><a class="reference internal" href="#abstract">摘要</a></li>
<li><a class="reference internal" href="#definition-and-introduction">定义和简介</a></li>
<li><a class="reference internal" href="#descriptor-protocol">描述器协议</a></li>
<li><a class="reference internal" href="#invoking-descriptors">调用描述器</a></li>
<li><a class="reference internal" href="#descriptor-example">描述器示例</a></li>
<li><a class="reference internal" href="#properties">属性</a></li>
<li><a class="reference internal" href="#functions-and-methods">函数和方法</a></li>
<li><a class="reference internal" href="#static-methods-and-class-methods">静态方法和类方法</a></li>
</ul>
</li>
</ul>
<h4>上一个主题</h4>
<p class="topless"><a href="curses.html"
title="上一章">用 Python 进行 Curses 编程</a></p>
<h4>下一个主题</h4>
<p class="topless"><a href="functional.html"
title="下一章">函数式编程指引</a></p>
<div role="note" aria-label="source link">
<h3>本页</h3>
<ul class="this-page-menu">
<li><a href="../bugs.html">报告 Bug</a></li>
<li>
<a href="https://github.com/python/cpython/blob/3.8/Doc/howto/descriptor.rst"
rel="nofollow">显示源代码
</a>
</li>
</ul>
</div>
</nav>
</div>
</div>
<div id="outdated-warning" style="padding: .5em; text-align: center; background-color: #FFBABA; color: #6A0E0E;">
这个文档所针对的是一个已不再受支持的 Python 旧版本。
你应当升级版本,并阅读
<a href="/3/howto/descriptor.html"> Python 当前稳定版本的文档</a>.
</div>
<div class="related" role="navigation" aria-label="related navigation">
<h3>导航</h3>
<ul>
<li class="right" style="margin-right: 10px">
<a href="../genindex.html" title="总目录"
accesskey="I">索引</a></li>
<li class="right" >
<a href="../py-modindex.html" title="Python 模块索引"
>模块</a> |</li>
<li class="right" >
<a href="functional.html" title="函数式编程指引"
accesskey="N">下一页</a> |</li>
<li class="right" >
<a href="curses.html" title="用 Python 进行 Curses 编程"
accesskey="P">上一页</a> |</li>
<li><img src="../_static/py.svg" alt="python logo" style="vertical-align: middle; margin-top: -1px"/></li>
<li><a href="https://www.python.org/">Python</a> &#187;</li>
<li class="switchers">
<div class="language_switcher_placeholder"></div>
<div class="version_switcher_placeholder"></div>
</li>
<li>
</li>
<li id="cpython-language-and-version">
<a href="../index.html">3.8.20 Documentation</a> &#187;
</li>
<li class="nav-item nav-item-1"><a href="index.html" accesskey="U">Python 指南</a> &#187;</li>
<li class="right">
<div class="inline-search" role="search">
<form class="inline-search" action="../search.html" method="get">
<input placeholder="快速搜索" aria-label="快速搜索" type="text" name="q" />
<input type="submit" value="转向" />
<input type="hidden" name="check_keywords" value="yes" />
<input type="hidden" name="area" value="default" />
</form>
</div>
|
</li>
</ul>
</div>
<div class="document">
<div class="documentwrapper">
<div class="bodywrapper">
<div class="body" role="main">
<section id="descriptor-howto-guide">
<h1><a class="toc-backref" href="#id2">描述器使用指南</a><a class="headerlink" href="#descriptor-howto-guide" title="永久链接至标题"></a></h1>
<dl class="field-list simple">
<dt class="field-odd">作者</dt>
<dd class="field-odd"><p>Raymond Hettinger</p>
</dd>
<dt class="field-even">联系方式</dt>
<dd class="field-even"><p>&lt;python at rcn dot com&gt;</p>
</dd>
</dl>
<div class="contents topic" id="id1">
<p class="topic-title">目录</p>
<ul class="simple">
<li><p><a class="reference internal" href="#descriptor-howto-guide" id="id2">描述器使用指南</a></p>
<ul>
<li><p><a class="reference internal" href="#abstract" id="id3">摘要</a></p></li>
<li><p><a class="reference internal" href="#definition-and-introduction" id="id4">定义和简介</a></p></li>
<li><p><a class="reference internal" href="#descriptor-protocol" id="id5">描述器协议</a></p></li>
<li><p><a class="reference internal" href="#invoking-descriptors" id="id6">调用描述器</a></p></li>
<li><p><a class="reference internal" href="#descriptor-example" id="id7">描述器示例</a></p></li>
<li><p><a class="reference internal" href="#properties" id="id8">属性</a></p></li>
<li><p><a class="reference internal" href="#functions-and-methods" id="id9">函数和方法</a></p></li>
<li><p><a class="reference internal" href="#static-methods-and-class-methods" id="id10">静态方法和类方法</a></p></li>
</ul>
</li>
</ul>
</div>
<section id="abstract">
<h2><a class="toc-backref" href="#id3">摘要</a><a class="headerlink" href="#abstract" title="永久链接至标题"></a></h2>
<p>定义描述器,总结描述器协议,展示描述器被如何使用。测试一个自定义的描述器和若干 Python 内置的描述器,包括函数、属性、静态方法和类方法。通过给出一个纯 Python 的等价实现和例程,展示每个描述器如何工作。</p>
<p>学习描述器不仅能提供接触到更多工具集的途径,还能更深地理解 Python 工作的原理并更加体会到其设计的优雅性。</p>
</section>
<section id="definition-and-introduction">
<h2><a class="toc-backref" href="#id4">定义和简介</a><a class="headerlink" href="#definition-and-introduction" title="永久链接至标题"></a></h2>
<p>一般地,一个描述器是一个包含 “绑定行为” 的对象,对其属性的访问被描述器协议中定义的方法覆盖。这些方法有:<a class="reference internal" href="../reference/datamodel.html#object.__get__" title="object.__get__"><code class="xref py py-meth docutils literal notranslate"><span class="pre">__get__()</span></code></a><a class="reference internal" href="../reference/datamodel.html#object.__set__" title="object.__set__"><code class="xref py py-meth docutils literal notranslate"><span class="pre">__set__()</span></code></a><a class="reference internal" href="../reference/datamodel.html#object.__delete__" title="object.__delete__"><code class="xref py py-meth docutils literal notranslate"><span class="pre">__delete__()</span></code></a>。如果某个对象中定义了这些方法中的任意一个,那么这个对象就可以被称为一个描述器。</p>
<p>属性访问的默认行为是从一个对象的字典中获取、设置或删除属性。例如,<code class="docutils literal notranslate"><span class="pre">a.x</span></code> 的查找顺序会从 <code class="docutils literal notranslate"><span class="pre">a.__dict__['x']</span></code> 开始,然后是 <code class="docutils literal notranslate"><span class="pre">type(a).__dict__['x']</span></code>,接下来依次查找 <code class="docutils literal notranslate"><span class="pre">type(a)</span></code> 的基类,不包括元类。 如果找到的值是定义了某个描述器方法的对象,则 Python 可能会重载默认行为并转而发起调用描述器方法。这具体发生在优先级链的哪个环节则要根据所定义的描述器方法及其被调用的方式来决定。</p>
<p>描述器是一个强大而通用的协议。 它们是特征属性、方法静态方法、类方法和 <a class="reference internal" href="../library/functions.html#super" title="super"><code class="xref py py-func docutils literal notranslate"><span class="pre">super()</span></code></a> 背后的实现机制。 它们在 Python 内部被广泛使用来实现自 2.2 版中引入的新式类。 描述器简化了底层的 C 代码并为 Python 的日常程序提供了一组灵活的新工具。</p>
</section>
<section id="descriptor-protocol">
<h2><a class="toc-backref" href="#id5">描述器协议</a><a class="headerlink" href="#descriptor-protocol" title="永久链接至标题"></a></h2>
<p><code class="docutils literal notranslate"><span class="pre">descr.__get__(self,</span> <span class="pre">obj,</span> <span class="pre">type=None)</span> <span class="pre">-&gt;</span> <span class="pre">value</span></code></p>
<p><code class="docutils literal notranslate"><span class="pre">descr.__set__(self,</span> <span class="pre">obj,</span> <span class="pre">value)</span> <span class="pre">-&gt;</span> <span class="pre">None</span></code></p>
<p><code class="docutils literal notranslate"><span class="pre">descr.__delete__(self,</span> <span class="pre">obj)</span> <span class="pre">-&gt;</span> <span class="pre">None</span></code></p>
<p>以上就是全部。定义这些方法中的任何一个的对象被视为描述器,并在被作为属性时覆盖其默认行为。</p>
<p>如果一个对象定义了 <a class="reference internal" href="../reference/datamodel.html#object.__set__" title="object.__set__"><code class="xref py py-meth docutils literal notranslate"><span class="pre">__set__()</span></code></a><a class="reference internal" href="../reference/datamodel.html#object.__delete__" title="object.__delete__"><code class="xref py py-meth docutils literal notranslate"><span class="pre">__delete__()</span></code></a>,则它会被视为数据描述器。 仅定义了 <a class="reference internal" href="../reference/datamodel.html#object.__get__" title="object.__get__"><code class="xref py py-meth docutils literal notranslate"><span class="pre">__get__()</span></code></a> 的描述器称为非数据描述器(它们通常被用于方法,但也可以有其他用途)。</p>
<p>数据和非数据描述器的不同之处在于,如何计算实例字典中条目的替代值。如果实例的字典具有与数据描述器同名的条目,则数据描述器优先。如果实例的字典具有与非数据描述器同名的条目,则该字典条目优先。</p>
<p>为了使数据描述器成为只读的,应该同时定义 <a class="reference internal" href="../reference/datamodel.html#object.__get__" title="object.__get__"><code class="xref py py-meth docutils literal notranslate"><span class="pre">__get__()</span></code></a><a class="reference internal" href="../reference/datamodel.html#object.__set__" title="object.__set__"><code class="xref py py-meth docutils literal notranslate"><span class="pre">__set__()</span></code></a> ,并在 <a class="reference internal" href="../reference/datamodel.html#object.__set__" title="object.__set__"><code class="xref py py-meth docutils literal notranslate"><span class="pre">__set__()</span></code></a> 中引发 <a class="reference internal" href="../library/exceptions.html#AttributeError" title="AttributeError"><code class="xref py py-exc docutils literal notranslate"><span class="pre">AttributeError</span></code></a> 。用引发异常的占位符定义 <a class="reference internal" href="../reference/datamodel.html#object.__set__" title="object.__set__"><code class="xref py py-meth docutils literal notranslate"><span class="pre">__set__()</span></code></a> 方法使其成为数据描述器。</p>
</section>
<section id="invoking-descriptors">
<h2><a class="toc-backref" href="#id6">调用描述器</a><a class="headerlink" href="#invoking-descriptors" title="永久链接至标题"></a></h2>
<p>描述器可以通过其方法名称直接调用。例如, <code class="docutils literal notranslate"><span class="pre">d.__get__(obj)</span></code></p>
<p>或者,更常见的是在属性访问时自动调用描述器。例如,在中 <code class="docutils literal notranslate"><span class="pre">obj.d</span></code> 会在 <code class="docutils literal notranslate"><span class="pre">d</span></code> 的字典中查找 <code class="docutils literal notranslate"><span class="pre">obj</span></code> 。如果 <code class="docutils literal notranslate"><span class="pre">d</span></code> 定义了方法 <a class="reference internal" href="../reference/datamodel.html#object.__get__" title="object.__get__"><code class="xref py py-meth docutils literal notranslate"><span class="pre">__get__()</span></code></a> ,则 <code class="docutils literal notranslate"><span class="pre">d.__get__(obj)</span></code> 根据下面列出的优先级规则进行调用。</p>
<p>调用的细节取决于 <code class="docutils literal notranslate"><span class="pre">obj</span></code> 是对象还是类。</p>
<p>对于对象来说,机制是 <a class="reference internal" href="../reference/datamodel.html#object.__getattribute__" title="object.__getattribute__"><code class="xref py py-meth docutils literal notranslate"><span class="pre">object.__getattribute__()</span></code></a> 中将 <code class="docutils literal notranslate"><span class="pre">b.x</span></code> 转换为 <code class="docutils literal notranslate"><span class="pre">type(b).__dict__['x'].__get__(b,</span> <span class="pre">type(b))</span></code> 。这个实现通过一个优先级链完成,该优先级链赋予数据描述器优先于实例变量的优先级,实例变量优先于非数据描述器的优先级,并如果 <a class="reference internal" href="../reference/datamodel.html#object.__getattr__" title="object.__getattr__"><code class="xref py py-meth docutils literal notranslate"><span class="pre">__getattr__()</span></code></a> 方法存在,为其分配最低的优先级。 完整的C实现可在 <a class="reference external" href="https://github.com/python/cpython/tree/3.8/Objects/object.c">Objects/object.c</a> 中的 <a class="reference internal" href="../c-api/object.html#c.PyObject_GenericGetAttr" title="PyObject_GenericGetAttr"><code class="xref c c-func docutils literal notranslate"><span class="pre">PyObject_GenericGetAttr()</span></code></a> 找到。</p>
<p>对于类来说,机制是 <code class="xref py py-meth docutils literal notranslate"><span class="pre">type.__getattribute__()</span></code> 中将 <code class="docutils literal notranslate"><span class="pre">B.x</span></code> 转换为 <code class="docutils literal notranslate"><span class="pre">B.__dict__['x'].__get__(None,</span> <span class="pre">B)</span></code> 。在纯 Python中 ,它是这样的:</p>
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="k">def</span> <span class="fm">__getattribute__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">key</span><span class="p">):</span>
<span class="s2">&quot;Emulate type_getattro() in Objects/typeobject.c&quot;</span>
<span class="n">v</span> <span class="o">=</span> <span class="nb">object</span><span class="o">.</span><span class="fm">__getattribute__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">key</span><span class="p">)</span>
<span class="k">if</span> <span class="nb">hasattr</span><span class="p">(</span><span class="n">v</span><span class="p">,</span> <span class="s1">&#39;__get__&#39;</span><span class="p">):</span>
<span class="k">return</span> <span class="n">v</span><span class="o">.</span><span class="fm">__get__</span><span class="p">(</span><span class="kc">None</span><span class="p">,</span> <span class="bp">self</span><span class="p">)</span>
<span class="k">return</span> <span class="n">v</span>
</pre></div>
</div>
<p>要记住的重要点是:</p>
<ul class="simple">
<li><p>描述器由 <a class="reference internal" href="../reference/datamodel.html#object.__getattribute__" title="object.__getattribute__"><code class="xref py py-meth docutils literal notranslate"><span class="pre">__getattribute__()</span></code></a> 方法调用</p></li>
<li><p>重写 <a class="reference internal" href="../reference/datamodel.html#object.__getattribute__" title="object.__getattribute__"><code class="xref py py-meth docutils literal notranslate"><span class="pre">__getattribute__()</span></code></a> 会阻止描述器的自动调用</p></li>
<li><p><a class="reference internal" href="../reference/datamodel.html#object.__getattribute__" title="object.__getattribute__"><code class="xref py py-meth docutils literal notranslate"><span class="pre">object.__getattribute__()</span></code></a><code class="xref py py-meth docutils literal notranslate"><span class="pre">type.__getattribute__()</span></code> 会用不同的方式调用 <a class="reference internal" href="../reference/datamodel.html#object.__get__" title="object.__get__"><code class="xref py py-meth docutils literal notranslate"><span class="pre">__get__()</span></code></a>.</p></li>
<li><p>数据描述器始终会覆盖实例字典。</p></li>
<li><p>非数据描述器会被实例字典覆盖。</p></li>
</ul>
<p>The object returned by <code class="docutils literal notranslate"><span class="pre">super()</span></code> also has a custom <a class="reference internal" href="../reference/datamodel.html#object.__getattribute__" title="object.__getattribute__"><code class="xref py py-meth docutils literal notranslate"><span class="pre">__getattribute__()</span></code></a>
method for invoking descriptors. The attribute lookup <code class="docutils literal notranslate"><span class="pre">super(B,</span> <span class="pre">obj).m</span></code> searches
<code class="docutils literal notranslate"><span class="pre">obj.__class__.__mro__</span></code> for the base class <code class="docutils literal notranslate"><span class="pre">A</span></code> immediately following <code class="docutils literal notranslate"><span class="pre">B</span></code>
and then returns <code class="docutils literal notranslate"><span class="pre">A.__dict__['m'].__get__(obj,</span> <span class="pre">B)</span></code>. If not a descriptor,
<code class="docutils literal notranslate"><span class="pre">m</span></code> is returned unchanged. If not in the dictionary, <code class="docutils literal notranslate"><span class="pre">m</span></code> reverts to a
search using <a class="reference internal" href="../reference/datamodel.html#object.__getattribute__" title="object.__getattribute__"><code class="xref py py-meth docutils literal notranslate"><span class="pre">object.__getattribute__()</span></code></a>.</p>
<p>这个实现的具体细节在 <a class="reference external" href="https://github.com/python/cpython/tree/3.8/Objects/typeobject.c">Objects/typeobject.c</a>. 的 <code class="xref c c-func docutils literal notranslate"><span class="pre">super_getattro()</span></code> 中,并且你还可以在 <a class="reference external" href="https://www.python.org/download/releases/2.2.3/descrintro/#cooperation">Guido's Tutorial</a> 中找到等价的纯 Python 实现。</p>
<p>以上展示的关于描述器机制的细节嵌入在 <a class="reference internal" href="../library/functions.html#object" title="object"><code class="xref py py-class docutils literal notranslate"><span class="pre">object</span></code></a> <a class="reference internal" href="../library/functions.html#type" title="type"><code class="xref py py-class docutils literal notranslate"><span class="pre">type</span></code></a> <a class="reference internal" href="../library/functions.html#super" title="super"><code class="xref py py-func docutils literal notranslate"><span class="pre">super()</span></code></a> 中的 <a class="reference internal" href="../reference/datamodel.html#object.__getattribute__" title="object.__getattribute__"><code class="xref py py-meth docutils literal notranslate"><span class="pre">__getattribute__()</span></code></a> 。当类派生自类 <a class="reference internal" href="../library/functions.html#object" title="object"><code class="xref py py-class docutils literal notranslate"><span class="pre">object</span></code></a> 或有提供类似功能的元类时,它们将继承此机制。同样,类可以通过重写 <a class="reference internal" href="../reference/datamodel.html#object.__getattribute__" title="object.__getattribute__"><code class="xref py py-meth docutils literal notranslate"><span class="pre">__getattribute__()</span></code></a> 阻止描述器调用。</p>
</section>
<section id="descriptor-example">
<h2><a class="toc-backref" href="#id7">描述器示例</a><a class="headerlink" href="#descriptor-example" title="永久链接至标题"></a></h2>
<p>以下代码创建一个类,其对象是数据描述器,该描述器为每个 get 或 set 打印一条消息。 覆盖 <a class="reference internal" href="../reference/datamodel.html#object.__getattribute__" title="object.__getattribute__"><code class="xref py py-meth docutils literal notranslate"><span class="pre">__getattribute__()</span></code></a> 是可以对每个属性执行此操作的替代方法。但是,此描述器对于跟踪少数几个选定的属性很有用:</p>
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">RevealAccess</span><span class="p">(</span><span class="nb">object</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;A data descriptor that sets and returns values</span>
<span class="sd"> normally and prints a message logging their access.</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">initval</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span> <span class="n">name</span><span class="o">=</span><span class="s1">&#39;var&#39;</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">val</span> <span class="o">=</span> <span class="n">initval</span>
<span class="bp">self</span><span class="o">.</span><span class="n">name</span> <span class="o">=</span> <span class="n">name</span>
<span class="k">def</span> <span class="fm">__get__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">obj</span><span class="p">,</span> <span class="n">objtype</span><span class="p">):</span>
<span class="nb">print</span><span class="p">(</span><span class="s1">&#39;Retrieving&#39;</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">name</span><span class="p">)</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">val</span>
<span class="k">def</span> <span class="fm">__set__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">obj</span><span class="p">,</span> <span class="n">val</span><span class="p">):</span>
<span class="nb">print</span><span class="p">(</span><span class="s1">&#39;Updating&#39;</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">name</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">val</span> <span class="o">=</span> <span class="n">val</span>
<span class="o">&gt;&gt;&gt;</span> <span class="k">class</span> <span class="nc">MyClass</span><span class="p">(</span><span class="nb">object</span><span class="p">):</span>
<span class="o">...</span> <span class="n">x</span> <span class="o">=</span> <span class="n">RevealAccess</span><span class="p">(</span><span class="mi">10</span><span class="p">,</span> <span class="s1">&#39;var &quot;x&quot;&#39;</span><span class="p">)</span>
<span class="o">...</span> <span class="n">y</span> <span class="o">=</span> <span class="mi">5</span>
<span class="o">...</span>
<span class="o">&gt;&gt;&gt;</span> <span class="n">m</span> <span class="o">=</span> <span class="n">MyClass</span><span class="p">()</span>
<span class="o">&gt;&gt;&gt;</span> <span class="n">m</span><span class="o">.</span><span class="n">x</span>
<span class="n">Retrieving</span> <span class="n">var</span> <span class="s2">&quot;x&quot;</span>
<span class="mi">10</span>
<span class="o">&gt;&gt;&gt;</span> <span class="n">m</span><span class="o">.</span><span class="n">x</span> <span class="o">=</span> <span class="mi">20</span>
<span class="n">Updating</span> <span class="n">var</span> <span class="s2">&quot;x&quot;</span>
<span class="o">&gt;&gt;&gt;</span> <span class="n">m</span><span class="o">.</span><span class="n">x</span>
<span class="n">Retrieving</span> <span class="n">var</span> <span class="s2">&quot;x&quot;</span>
<span class="mi">20</span>
<span class="o">&gt;&gt;&gt;</span> <span class="n">m</span><span class="o">.</span><span class="n">y</span>
<span class="mi">5</span>
</pre></div>
</div>
<p>这个协议很简单,并提供了令人兴奋的可能性。有几种用例非常普遍,以至于它们被打包到单独的函数调用中。属性、绑定方法、静态方法和类方法均基于描述器协议。</p>
</section>
<section id="properties">
<h2><a class="toc-backref" href="#id8">属性</a><a class="headerlink" href="#properties" title="永久链接至标题"></a></h2>
<p>调用 <a class="reference internal" href="../library/functions.html#property" title="property"><code class="xref py py-func docutils literal notranslate"><span class="pre">property()</span></code></a> 是构建数据描述器的简洁方式,该数据描述器在访问属性时触发函数调用。它的签名是:</p>
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="nb">property</span><span class="p">(</span><span class="n">fget</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span> <span class="n">fset</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span> <span class="n">fdel</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span> <span class="n">doc</span><span class="o">=</span><span class="kc">None</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">property</span> <span class="n">attribute</span>
</pre></div>
</div>
<p>该文档显示了定义托管属性 <code class="docutils literal notranslate"><span class="pre">x</span></code> 的典型用法:</p>
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">C</span><span class="p">(</span><span class="nb">object</span><span class="p">):</span>
<span class="k">def</span> <span class="nf">getx</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">__x</span>
<span class="k">def</span> <span class="nf">setx</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">value</span><span class="p">):</span> <span class="bp">self</span><span class="o">.</span><span class="n">__x</span> <span class="o">=</span> <span class="n">value</span>
<span class="k">def</span> <span class="nf">delx</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="k">del</span> <span class="bp">self</span><span class="o">.</span><span class="n">__x</span>
<span class="n">x</span> <span class="o">=</span> <span class="nb">property</span><span class="p">(</span><span class="n">getx</span><span class="p">,</span> <span class="n">setx</span><span class="p">,</span> <span class="n">delx</span><span class="p">,</span> <span class="s2">&quot;I&#39;m the &#39;x&#39; property.&quot;</span><span class="p">)</span>
</pre></div>
</div>
<p>要了解 <a class="reference internal" href="../library/functions.html#property" title="property"><code class="xref py py-func docutils literal notranslate"><span class="pre">property()</span></code></a> 如何根据描述器协议实现,这里是一个纯 Python 的等价实现如下:</p>
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">Property</span><span class="p">(</span><span class="nb">object</span><span class="p">):</span>
<span class="s2">&quot;Emulate PyProperty_Type() in Objects/descrobject.c&quot;</span>
<span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">fget</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span> <span class="n">fset</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span> <span class="n">fdel</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span> <span class="n">doc</span><span class="o">=</span><span class="kc">None</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">fget</span> <span class="o">=</span> <span class="n">fget</span>
<span class="bp">self</span><span class="o">.</span><span class="n">fset</span> <span class="o">=</span> <span class="n">fset</span>
<span class="bp">self</span><span class="o">.</span><span class="n">fdel</span> <span class="o">=</span> <span class="n">fdel</span>
<span class="k">if</span> <span class="n">doc</span> <span class="ow">is</span> <span class="kc">None</span> <span class="ow">and</span> <span class="n">fget</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span><span class="p">:</span>
<span class="n">doc</span> <span class="o">=</span> <span class="n">fget</span><span class="o">.</span><span class="vm">__doc__</span>
<span class="bp">self</span><span class="o">.</span><span class="vm">__doc__</span> <span class="o">=</span> <span class="n">doc</span>
<span class="k">def</span> <span class="fm">__get__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">obj</span><span class="p">,</span> <span class="n">objtype</span><span class="o">=</span><span class="kc">None</span><span class="p">):</span>
<span class="k">if</span> <span class="n">obj</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
<span class="k">return</span> <span class="bp">self</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">fget</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
<span class="k">raise</span> <span class="ne">AttributeError</span><span class="p">(</span><span class="s2">&quot;unreadable attribute&quot;</span><span class="p">)</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">fget</span><span class="p">(</span><span class="n">obj</span><span class="p">)</span>
<span class="k">def</span> <span class="fm">__set__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">obj</span><span class="p">,</span> <span class="n">value</span><span class="p">):</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">fset</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
<span class="k">raise</span> <span class="ne">AttributeError</span><span class="p">(</span><span class="s2">&quot;can&#39;t set attribute&quot;</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">fset</span><span class="p">(</span><span class="n">obj</span><span class="p">,</span> <span class="n">value</span><span class="p">)</span>
<span class="k">def</span> <span class="fm">__delete__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">obj</span><span class="p">):</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">fdel</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
<span class="k">raise</span> <span class="ne">AttributeError</span><span class="p">(</span><span class="s2">&quot;can&#39;t delete attribute&quot;</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">fdel</span><span class="p">(</span><span class="n">obj</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">getter</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">fget</span><span class="p">):</span>
<span class="k">return</span> <span class="nb">type</span><span class="p">(</span><span class="bp">self</span><span class="p">)(</span><span class="n">fget</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">fset</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">fdel</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="vm">__doc__</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">setter</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">fset</span><span class="p">):</span>
<span class="k">return</span> <span class="nb">type</span><span class="p">(</span><span class="bp">self</span><span class="p">)(</span><span class="bp">self</span><span class="o">.</span><span class="n">fget</span><span class="p">,</span> <span class="n">fset</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">fdel</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="vm">__doc__</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">deleter</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">fdel</span><span class="p">):</span>
<span class="k">return</span> <span class="nb">type</span><span class="p">(</span><span class="bp">self</span><span class="p">)(</span><span class="bp">self</span><span class="o">.</span><span class="n">fget</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">fset</span><span class="p">,</span> <span class="n">fdel</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="vm">__doc__</span><span class="p">)</span>
</pre></div>
</div>
<p>这个内置的 <a class="reference internal" href="../library/functions.html#property" title="property"><code class="xref py py-func docutils literal notranslate"><span class="pre">property()</span></code></a> 每当用户访问属性时生效,随后的变化需要一个方法的参与。</p>
<p>例如,一个电子表格类可以通过 <code class="docutils literal notranslate"><span class="pre">Cell('b10').value</span></code> 授予对单元格值的访问权限。对程序的后续改进要求每次访问都要重新计算单元格;但是,程序员不希望影响直接访问该属性的现有客户端代码。解决方案是将对 value 属性的访问包装在属性数据描述器中:</p>
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">Cell</span><span class="p">(</span><span class="nb">object</span><span class="p">):</span>
<span class="o">.</span> <span class="o">.</span> <span class="o">.</span>
<span class="k">def</span> <span class="nf">getvalue</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="s2">&quot;Recalculate the cell before returning value&quot;</span>
<span class="bp">self</span><span class="o">.</span><span class="n">recalc</span><span class="p">()</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_value</span>
<span class="n">value</span> <span class="o">=</span> <span class="nb">property</span><span class="p">(</span><span class="n">getvalue</span><span class="p">)</span>
</pre></div>
</div>
</section>
<section id="functions-and-methods">
<h2><a class="toc-backref" href="#id9">函数和方法</a><a class="headerlink" href="#functions-and-methods" title="永久链接至标题"></a></h2>
<p>Python 的面向对象功能是在基于函数的环境构建的。通过使用非数据描述器,这两方面完成了无缝融合。</p>
<p>类字典将方法存储为函数。在类定义中,方法是用 <a class="reference internal" href="../reference/compound_stmts.html#def"><code class="xref std std-keyword docutils literal notranslate"><span class="pre">def</span></code></a><a class="reference internal" href="../reference/expressions.html#lambda"><code class="xref std std-keyword docutils literal notranslate"><span class="pre">lambda</span></code></a> 这两个创建函数的常用工具编写的。方法与常规函数的不同之处仅在于第一个参数是为对象实例保留的。按照 Python 约定,实例引用称为 <em>self</em> ,但也可以称为 <em>this</em> 或任何其他变量名称。</p>
<p>为了支持方法调用,函数包含 <a class="reference internal" href="../reference/datamodel.html#object.__get__" title="object.__get__"><code class="xref py py-meth docutils literal notranslate"><span class="pre">__get__()</span></code></a> 方法用于在访问属性时将其绑定成方法。这意味着所有函数都是非数据描述器,当从对象调用它们时,它们返回绑定方法。在纯 Python 中,它的工作方式如下:</p>
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">Function</span><span class="p">(</span><span class="nb">object</span><span class="p">):</span>
<span class="o">.</span> <span class="o">.</span> <span class="o">.</span>
<span class="k">def</span> <span class="fm">__get__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">obj</span><span class="p">,</span> <span class="n">objtype</span><span class="o">=</span><span class="kc">None</span><span class="p">):</span>
<span class="s2">&quot;Simulate func_descr_get() in Objects/funcobject.c&quot;</span>
<span class="k">if</span> <span class="n">obj</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
<span class="k">return</span> <span class="bp">self</span>
<span class="k">return</span> <span class="n">types</span><span class="o">.</span><span class="n">MethodType</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">obj</span><span class="p">)</span>
</pre></div>
</div>
<p>运行解释器显示了函数描述器在实践中的工作方式:</p>
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="gp">&gt;&gt;&gt; </span><span class="k">class</span> <span class="nc">D</span><span class="p">(</span><span class="nb">object</span><span class="p">):</span>
<span class="gp">... </span> <span class="k">def</span> <span class="nf">f</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">x</span><span class="p">):</span>
<span class="gp">... </span> <span class="k">return</span> <span class="n">x</span>
<span class="gp">...</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">d</span> <span class="o">=</span> <span class="n">D</span><span class="p">()</span>
<span class="go"># Access through the class dictionary does not invoke __get__.</span>
<span class="go"># It just returns the underlying function object.</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">D</span><span class="o">.</span><span class="vm">__dict__</span><span class="p">[</span><span class="s1">&#39;f&#39;</span><span class="p">]</span>
<span class="go">&lt;function D.f at 0x00C45070&gt;</span>
<span class="go"># Dotted access from a class calls __get__() which just returns</span>
<span class="go"># the underlying function unchanged.</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">D</span><span class="o">.</span><span class="n">f</span>
<span class="go">&lt;function D.f at 0x00C45070&gt;</span>
<span class="go"># The function has a __qualname__ attribute to support introspection</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">D</span><span class="o">.</span><span class="n">f</span><span class="o">.</span><span class="vm">__qualname__</span>
<span class="go">&#39;D.f&#39;</span>
<span class="go"># Dotted access from an instance calls __get__() which returns the</span>
<span class="go"># function wrapped in a bound method object</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">d</span><span class="o">.</span><span class="n">f</span>
<span class="go">&lt;bound method D.f of &lt;__main__.D object at 0x00B18C90&gt;&gt;</span>
<span class="go"># Internally, the bound method stores the underlying function and</span>
<span class="go"># the bound instance.</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">d</span><span class="o">.</span><span class="n">f</span><span class="o">.</span><span class="vm">__func__</span>
<span class="go">&lt;function D.f at 0x1012e5ae8&gt;</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">d</span><span class="o">.</span><span class="n">f</span><span class="o">.</span><span class="vm">__self__</span>
<span class="go">&lt;__main__.D object at 0x1012e1f98&gt;</span>
</pre></div>
</div>
</section>
<section id="static-methods-and-class-methods">
<h2><a class="toc-backref" href="#id10">静态方法和类方法</a><a class="headerlink" href="#static-methods-and-class-methods" title="永久链接至标题"></a></h2>
<p>非数据描述器为把函数绑定为方法的通常模式提供了一种简单的机制。</p>
<p>概括地说,函数具有 <a class="reference internal" href="../reference/datamodel.html#object.__get__" title="object.__get__"><code class="xref py py-meth docutils literal notranslate"><span class="pre">__get__()</span></code></a> 方法,以便在作为属性访问时可以将其转换为方法。非数据描述符将 <code class="docutils literal notranslate"><span class="pre">obj.f(*args)</span></code> 的调用转换为 <code class="docutils literal notranslate"><span class="pre">f(obj,</span> <span class="pre">*args)</span></code> 。调用 <cite>klass.f(*args)`</cite> 因而变成 <code class="docutils literal notranslate"><span class="pre">f(*args)</span></code></p>
<p>下表总结了绑定及其两个最有用的变体:</p>
<blockquote>
<div><table class="docutils align-default">
<colgroup>
<col style="width: 30%" />
<col style="width: 39%" />
<col style="width: 32%" />
</colgroup>
<thead>
<tr class="row-odd"><th class="head"><p>转换形式</p></th>
<th class="head"><p>通过对象调用</p></th>
<th class="head"><p>通过类调用</p></th>
</tr>
</thead>
<tbody>
<tr class="row-even"><td><p>function -- 函数</p></td>
<td><p>f(obj, *args)</p></td>
<td><p>f(*args)</p></td>
</tr>
<tr class="row-odd"><td><p>静态方法</p></td>
<td><p>f(*args)</p></td>
<td><p>f(*args)</p></td>
</tr>
<tr class="row-even"><td><p>类方法</p></td>
<td><p>f(type(obj), *args)</p></td>
<td><p>f(klass, *args)</p></td>
</tr>
</tbody>
</table>
</div></blockquote>
<p>静态方法返回底层函数,不做任何更改。调用 <code class="docutils literal notranslate"><span class="pre">c.f</span></code><code class="docutils literal notranslate"><span class="pre">C.f</span></code> 等效于通过 <code class="docutils literal notranslate"><span class="pre">object.__getattribute__(c,</span> <span class="pre">&quot;f&quot;)</span></code><code class="docutils literal notranslate"><span class="pre">object.__getattribute__(C,</span> <span class="pre">&quot;f&quot;)</span></code> 查找。这样该函数就可以从对象或类中进行相同的访问。</p>
<p>适合于作为静态方法的是那些不引用 <code class="docutils literal notranslate"><span class="pre">self</span></code> 变量的方法。</p>
<p>例如,一个统计用的包可能包含一个实验数据的容器类。该容器类提供了用于计算数据的平均值,均值,中位数和其他描述性统计信息的常规方法。但是,可能有在概念上相关但不依赖于数据的函数。例如, <code class="docutils literal notranslate"><span class="pre">erf(x)</span></code> 是在统计中的便捷转换,但并不直接依赖于特定的数据集。可以从对象或类中调用它: <code class="docutils literal notranslate"><span class="pre">s.erf(1.5)</span> <span class="pre">--&gt;</span> <span class="pre">.9332</span></code><code class="docutils literal notranslate"><span class="pre">Sample.erf(1.5)</span> <span class="pre">--&gt;</span> <span class="pre">.9332</span></code></p>
<p>由于静态方法直接返回了底层的函数,因此示例调用是相同的:</p>
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="gp">&gt;&gt;&gt; </span><span class="k">class</span> <span class="nc">E</span><span class="p">(</span><span class="nb">object</span><span class="p">):</span>
<span class="gp">... </span> <span class="k">def</span> <span class="nf">f</span><span class="p">(</span><span class="n">x</span><span class="p">):</span>
<span class="gp">... </span> <span class="nb">print</span><span class="p">(</span><span class="n">x</span><span class="p">)</span>
<span class="gp">... </span> <span class="n">f</span> <span class="o">=</span> <span class="nb">staticmethod</span><span class="p">(</span><span class="n">f</span><span class="p">)</span>
<span class="gp">...</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">E</span><span class="o">.</span><span class="n">f</span><span class="p">(</span><span class="mi">3</span><span class="p">)</span>
<span class="go">3</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">E</span><span class="p">()</span><span class="o">.</span><span class="n">f</span><span class="p">(</span><span class="mi">3</span><span class="p">)</span>
<span class="go">3</span>
</pre></div>
</div>
<p>使用非数据描述器,纯 Python 版本的 <a class="reference internal" href="../library/functions.html#staticmethod" title="staticmethod"><code class="xref py py-func docutils literal notranslate"><span class="pre">staticmethod()</span></code></a> 如下所示:</p>
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">StaticMethod</span><span class="p">(</span><span class="nb">object</span><span class="p">):</span>
<span class="s2">&quot;Emulate PyStaticMethod_Type() in Objects/funcobject.c&quot;</span>
<span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">f</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">f</span> <span class="o">=</span> <span class="n">f</span>
<span class="k">def</span> <span class="fm">__get__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">obj</span><span class="p">,</span> <span class="n">objtype</span><span class="o">=</span><span class="kc">None</span><span class="p">):</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">f</span>
</pre></div>
</div>
<p>与静态方法不同,类方法在调用函数之前将类引用放在参数列表的最前。无论调用方是对象还是类,此格式相同:</p>
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="gp">&gt;&gt;&gt; </span><span class="k">class</span> <span class="nc">E</span><span class="p">(</span><span class="nb">object</span><span class="p">):</span>
<span class="gp">... </span> <span class="k">def</span> <span class="nf">f</span><span class="p">(</span><span class="n">klass</span><span class="p">,</span> <span class="n">x</span><span class="p">):</span>
<span class="gp">... </span> <span class="k">return</span> <span class="n">klass</span><span class="o">.</span><span class="vm">__name__</span><span class="p">,</span> <span class="n">x</span>
<span class="gp">... </span> <span class="n">f</span> <span class="o">=</span> <span class="nb">classmethod</span><span class="p">(</span><span class="n">f</span><span class="p">)</span>
<span class="gp">...</span>
<span class="gp">&gt;&gt;&gt; </span><span class="nb">print</span><span class="p">(</span><span class="n">E</span><span class="o">.</span><span class="n">f</span><span class="p">(</span><span class="mi">3</span><span class="p">))</span>
<span class="go">(&#39;E&#39;, 3)</span>
<span class="gp">&gt;&gt;&gt; </span><span class="nb">print</span><span class="p">(</span><span class="n">E</span><span class="p">()</span><span class="o">.</span><span class="n">f</span><span class="p">(</span><span class="mi">3</span><span class="p">))</span>
<span class="go">(&#39;E&#39;, 3)</span>
</pre></div>
</div>
<p>此行为适用于当函数仅需要使用类引用并且不关心任何底层数据时的情况。 类方法的一种用途是创建替代的类构造器。 在 Python 2.3 中,类方法 <a class="reference internal" href="../library/stdtypes.html#dict.fromkeys" title="dict.fromkeys"><code class="xref py py-func docutils literal notranslate"><span class="pre">dict.fromkeys()</span></code></a> 会从键列表中创建一个新的字典。 纯 Python 的等价形式是:</p>
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">Dict</span><span class="p">(</span><span class="nb">object</span><span class="p">):</span>
<span class="o">.</span> <span class="o">.</span> <span class="o">.</span>
<span class="k">def</span> <span class="nf">fromkeys</span><span class="p">(</span><span class="n">klass</span><span class="p">,</span> <span class="n">iterable</span><span class="p">,</span> <span class="n">value</span><span class="o">=</span><span class="kc">None</span><span class="p">):</span>
<span class="s2">&quot;Emulate dict_fromkeys() in Objects/dictobject.c&quot;</span>
<span class="n">d</span> <span class="o">=</span> <span class="n">klass</span><span class="p">()</span>
<span class="k">for</span> <span class="n">key</span> <span class="ow">in</span> <span class="n">iterable</span><span class="p">:</span>
<span class="n">d</span><span class="p">[</span><span class="n">key</span><span class="p">]</span> <span class="o">=</span> <span class="n">value</span>
<span class="k">return</span> <span class="n">d</span>
<span class="n">fromkeys</span> <span class="o">=</span> <span class="nb">classmethod</span><span class="p">(</span><span class="n">fromkeys</span><span class="p">)</span>
</pre></div>
</div>
<p>现在可以这样构造一个新的唯一键字典:</p>
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="gp">&gt;&gt;&gt; </span><span class="n">Dict</span><span class="o">.</span><span class="n">fromkeys</span><span class="p">(</span><span class="s1">&#39;abracadabra&#39;</span><span class="p">)</span>
<span class="go">{&#39;a&#39;: None, &#39;r&#39;: None, &#39;b&#39;: None, &#39;c&#39;: None, &#39;d&#39;: None}</span>
</pre></div>
</div>
<p>使用非数据描述符协议,纯 Python 版本的 <a class="reference internal" href="../library/functions.html#classmethod" title="classmethod"><code class="xref py py-func docutils literal notranslate"><span class="pre">classmethod()</span></code></a> 如下:</p>
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">ClassMethod</span><span class="p">(</span><span class="nb">object</span><span class="p">):</span>
<span class="s2">&quot;Emulate PyClassMethod_Type() in Objects/funcobject.c&quot;</span>
<span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">f</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">f</span> <span class="o">=</span> <span class="n">f</span>
<span class="k">def</span> <span class="fm">__get__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">obj</span><span class="p">,</span> <span class="n">klass</span><span class="o">=</span><span class="kc">None</span><span class="p">):</span>
<span class="k">if</span> <span class="n">klass</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
<span class="n">klass</span> <span class="o">=</span> <span class="nb">type</span><span class="p">(</span><span class="n">obj</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">newfunc</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">):</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">f</span><span class="p">(</span><span class="n">klass</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">)</span>
<span class="k">return</span> <span class="n">newfunc</span>
</pre></div>
</div>
</section>
</section>
</div>
</div>
</div>
<div class="sphinxsidebar" role="navigation" aria-label="main navigation">
<div class="sphinxsidebarwrapper">
<h3><a href="../contents.html">目录</a></h3>
<ul>
<li><a class="reference internal" href="#">描述器使用指南</a><ul>
<li><a class="reference internal" href="#abstract">摘要</a></li>
<li><a class="reference internal" href="#definition-and-introduction">定义和简介</a></li>
<li><a class="reference internal" href="#descriptor-protocol">描述器协议</a></li>
<li><a class="reference internal" href="#invoking-descriptors">调用描述器</a></li>
<li><a class="reference internal" href="#descriptor-example">描述器示例</a></li>
<li><a class="reference internal" href="#properties">属性</a></li>
<li><a class="reference internal" href="#functions-and-methods">函数和方法</a></li>
<li><a class="reference internal" href="#static-methods-and-class-methods">静态方法和类方法</a></li>
</ul>
</li>
</ul>
<h4>上一个主题</h4>
<p class="topless"><a href="curses.html"
title="上一章">用 Python 进行 Curses 编程</a></p>
<h4>下一个主题</h4>
<p class="topless"><a href="functional.html"
title="下一章">函数式编程指引</a></p>
<div role="note" aria-label="source link">
<h3>本页</h3>
<ul class="this-page-menu">
<li><a href="../bugs.html">报告 Bug</a></li>
<li>
<a href="https://github.com/python/cpython/blob/3.8/Doc/howto/descriptor.rst"
rel="nofollow">显示源代码
</a>
</li>
</ul>
</div>
</div>
</div>
<div class="clearer"></div>
</div>
<div class="related" role="navigation" aria-label="related navigation">
<h3>导航</h3>
<ul>
<li class="right" style="margin-right: 10px">
<a href="../genindex.html" title="总目录"
>索引</a></li>
<li class="right" >
<a href="../py-modindex.html" title="Python 模块索引"
>模块</a> |</li>
<li class="right" >
<a href="functional.html" title="函数式编程指引"
>下一页</a> |</li>
<li class="right" >
<a href="curses.html" title="用 Python 进行 Curses 编程"
>上一页</a> |</li>
<li><img src="../_static/py.svg" alt="python logo" style="vertical-align: middle; margin-top: -1px"/></li>
<li><a href="https://www.python.org/">Python</a> &#187;</li>
<li class="switchers">
<div class="language_switcher_placeholder"></div>
<div class="version_switcher_placeholder"></div>
</li>
<li>
</li>
<li id="cpython-language-and-version">
<a href="../index.html">3.8.20 Documentation</a> &#187;
</li>
<li class="nav-item nav-item-1"><a href="index.html" >Python 指南</a> &#187;</li>
<li class="right">
<div class="inline-search" role="search">
<form class="inline-search" action="../search.html" method="get">
<input placeholder="快速搜索" aria-label="快速搜索" type="text" name="q" />
<input type="submit" value="转向" />
<input type="hidden" name="check_keywords" value="yes" />
<input type="hidden" name="area" value="default" />
</form>
</div>
|
</li>
</ul>
</div>
<div class="footer">
&copy; <a href="../copyright.html">版权所有</a> 2001-2024, Python Software Foundation.
<br />
This page is licensed under the Python Software Foundation License Version 2.
<br />
Examples, recipes, and other code in the documentation are additionally licensed under the Zero Clause BSD License.
<br />
<br />
The Python Software Foundation is a non-profit corporation.
<a href="https://www.python.org/psf/donations/">Please donate.</a>
<br />
<br />
最后更新于 12月 09, 2024.
<a href="https://docs.python.org/3/bugs.html">Found a bug</a>?
<br />
Created using <a href="https://www.sphinx-doc.org/">Sphinx</a> 2.4.4.
</div>
</body>
</html>