Skip to content

Commit

Permalink
Update Descriptor-HOW-TO-Guide.rst.
Browse files Browse the repository at this point in the history
  • Loading branch information
iceout committed Nov 26, 2014
1 parent b5744ba commit ba6dcd6
Showing 1 changed file with 21 additions and 26 deletions.
47 changes: 21 additions & 26 deletions docs/Descriptor-HOW-TO-Guide.rst
Original file line number Diff line number Diff line change
Expand Up @@ -17,20 +17,20 @@ Python描述器引导(翻译)
摘要
----

定义描述器, 总结描述器协定,并展示描述器怎么被调用的。测试一个普通的描述器和包括方法,属性(property), 静态方法(static method), 类方法在内的几个Python内置描述器。通过给出一个纯Python的实现和示例应用来展示每个描述器是怎么工作的。
定义描述器, 总结描述器协议,并展示描述器是怎么被调用的。展示一个自定义的描述器和包括函数,属性(property), 静态方法(static method), 类方法在内的几个Python内置描述器。通过给出一个纯Python的实现和示例应用来展示每个描述器是怎么工作的。

学习描述器不仅让你接触到更多的工具,还可以让你更深入地了解Python是如何工作的,让你体会到Python设计的优雅之处。
学习描述器不仅让你接触到更多的工具,还可以让你更深入地了解Python,让你体会到Python设计的优雅之处。

定义和介绍
----------

一般来说,一个描述器是一个有“绑定行为”的对象属性,它的访问控制被描述器协定方法重写。这些方法是 :meth:`__get__`, :meth:`__set__`, 和 :meth:`__delete__`.有这些方法的对象叫做描述器。
一般来说,一个描述器是一个有“绑定行为”的对象属性,它的访问控制被描述器协议方法重写。这些方法是 :meth:`__get__`, :meth:`__set__`, 和 :meth:`__delete__`有这些方法的对象叫做描述器。

默认对属性的访问控制是从对象的字典里面(__dict__)中获取(get), 设置(set)和删除(delete)它.对于实例 ``a`` 来说, ``a.x`` 的查找顺序是, ``a.__dict__['x']`` , 然后 ``type(a).__dict__['x']`` , 然后找 ``type(a)`` 的父类(不包括元类(metaclass)).如果查找到的值是一个描述器, Python就会调用描述器的方法来重写默认的控制行为。这个重写发生在这个查找环节的哪里取决于定义了哪个描述器方法。注意, 只有在新式类中时描述器才会起作用。(新式类是继承自 ``type`` 或者 ``object`` 的类)
默认对属性的访问控制是从对象的字典里面(__dict__)中获取(get), 设置(set)和删除(delete)它对于实例 ``a`` 来说, ``a.x`` 的查找顺序是, ``a.__dict__['x']`` , 然后 ``type(a).__dict__['x']`` , 然后找 ``type(a)`` 的父类(不包括元类(metaclass)).如果查找到的值是一个描述器, Python就会调用描述器的方法来重写默认的控制行为。这个重写发生在这个查找环节的哪里取决于定义了哪个描述器方法。注意, 只有在新式类中时描述器才会起作用。(新式类是继承自 ``type`` 或者 ``object`` 的类)

描述器是强大的,应用广泛的。描述器正是属性, 实例方法, 静态方法, 类方法和 ``super`` 的背后的实现机制。描述器在Python自身中广泛使用,以实现Python 2.2中引入的新式类。描述器简化了底层的C代码,并为Python的日常编程提供了一套灵活的新工具。

描述器协定
描述器协议
----------

``descr.__get__(self, obj, type=None) --> value``
Expand Down Expand Up @@ -127,7 +127,7 @@ Python描述器引导(翻译)
>>> m.y
5

这个协定非常简单,并且提供了令人激动的可能。一些用途实在是太普遍以致于它们被打包成独立的函数。像属性(property), 方法(bound和unbound method), 静态方法和类方法都是基于描述器协定的
这个协议非常简单,并且提供了令人激动的可能。一些用途实在是太普遍以致于它们被打包成独立的函数。像属性(property), 方法(bound和unbound method), 静态方法和类方法都是基于描述器协议的

属性(properties)
----------------
Expand All @@ -136,7 +136,7 @@ Python描述器引导(翻译)

property(fget=None, fset=None, fdel=None, doc=None) -> property attribute

下面展示了一个典型应用:定义一个托管属性(Managed Attribute) ``x`` ::
下面展示了一个典型应用:定义一个托管属性(Managed Attribute) ``x`` ::

class C(object):
def getx(self): return self.__x
Expand Down Expand Up @@ -183,7 +183,7 @@ Python描述器引导(翻译)

当用户接口已经被授权访问属性之后,需求发生一些变化,属性需要进一步处理才能返回给用户。这时 :func:`property` 能够提供很大帮助。

例如,一个电子表格类提供了访问单元格的方式: ``Cell('b10').value``. 之后,对这个程序的改善要求在每次访问单元格时重新计算单元格的值。然而,程序员并不想影响那些客户端中直接访问属性的代码。那么解决方案是将属性访问包装在一个属性资料描述器中::
例如,一个电子表格类提供了访问单元格的方式: ``Cell('b10').value`` 之后,对这个程序的改善要求在每次访问单元格时重新计算单元格的值。然而,程序员并不想影响那些客户端中直接访问属性的代码。那么解决方案是将属性访问包装在一个属性资料描述器中::

class Cell(object):
. . .
Expand All @@ -208,9 +208,7 @@ Python的面向对象特征是建立在基于函数的环境之上的。非资
"Simulate func_descr_get() in Objects/funcobject.c"
return types.MethodType(self, obj, objtype)

运行解释器来展示实际情况下函数描述器是如何工作的:

::
下面运行解释器来展示实际情况下函数描述器是如何工作的::

>>> class D(object):
def f(self, x):
Expand All @@ -233,11 +231,11 @@ Objects/classobject.c(http://svn.python.org/view/python/trunk/Objects/classobjec
静态方法和类方法
----------------

非资料描述器提供了一个简单的把函数绑定成一个实例的方法的通常模式
非资料描述器为将函数绑定成方法这种常见模式提供了一个简单的实现机制

简而言之,函数有个方法 :meth:`__get__` 的时候就会变成一个实例方法。非资料描述器把 ``obj.f(*args)`` 的调用 变成 ``f(obj, *args)``. 调用 ``klass.f(*args)`` 就相当于调用 ``f(*args)``.
简而言之,函数有个方法 :meth:`__get__` ,当函数被当作属性访问时,它就会把函数变成一个实例方法。非资料描述器把 ``obj.f(*args)`` 的调用转换成 ``f(obj, *args)``调用 ``klass.f(*args)`` 就变成调用 ``f(*args)``

下面的表格总结了这个绑定和它的两个最有用的变种:
下面的表格总结了绑定和它最有用的两个变种:

+-----------------+----------------------+------------------+
| Transformation | Called from an | Called from a |
Expand All @@ -250,15 +248,14 @@ Objects/classobject.c(http://svn.python.org/view/python/trunk/Objects/classobjec
| classmethod | f(type(obj), \*args) | f(klass, \*args) |
+-----------------+----------------------+------------------+

静态方法原样返回那个函数,调用 ``c.f`` 或者 ``C.f`` 都是等价的,都是在调用 ``object.__getattribute__(c, "f")`` 或者 ``object.__getattribute__(C, "f")`` 。就是说,这个函数可以同时用类和实例去访问
静态方法原样返回函数,调用 ``c.f`` 或者 ``C.f`` 分别等价于 ``object.__getattribute__(c, "f")`` 或者 ``object.__getattribute__(C, "f")`` 。也就是说,无论是从一个对象还是一个类中,这个函数都会同样地访问到

那些不需要 ``self`` 变量做参数的函数适合用做静态方法
那些不需要 ``self`` 变量的方法适合用做静态方法

例如, 一个用做统计的包(pkg)可能包含一个类用做实验数据的容器。这个类提供了一般的计算平均数据的方法
, 平均数,中位数,和其他依赖于这些数据的描述性统计。然而,可能会有些有用的函数和这个统计主题相关,但是并不依赖于这些实验的数据。比如 ``erf(x)`` 是遇到统计工作经常用到的一个函数,但它并不依赖于那些特定的数据。它可以从类或者实例调用: ``s.erf(1.5) --> .9332`` 或者 ``Sample.erf(1.5) --> .9332``.
例如, 一个统计包可能包含一个用来做实验数据容器的类。这个类提供了一般的方法,来计算平均数,中位数,以及其他基于数据的描述性统计指标。然而,这个类可能包含一些概念上与统计相关但不依赖具体数据的函数。比如 ``erf(x)`` 就是一个统计工作中经常用到的,但却不依赖于特定数据的函数。它可以从类或者实例调用: ``s.erf(1.5) --> .9332`` 或者 ``Sample.erf(1.5) --> .9332``.


既然静态方法是原封不动的被调用,下面的代码看上去就没什么意思了:) ::
既然staticmethod将函数原封不动的返回,那下面的代码看上去就很正常了::

>>> class E(object):
def f(x):
Expand All @@ -270,7 +267,7 @@ Objects/classobject.c(http://svn.python.org/view/python/trunk/Objects/classobjec
>>> print E().f(3)
3

利用非资料描述器,我们用Python来实现 :func:`staticmethod` ::
利用非资料描述器, :func:`staticmethod` 的纯Python版本看起来像这样::

class StaticMethod(object):
"Emulate PyStaticMethod_Type() in Objects/funcobject.c"
Expand All @@ -281,9 +278,7 @@ Objects/classobject.c(http://svn.python.org/view/python/trunk/Objects/classobjec
def __get__(self, obj, objtype=None):
return self.f

不像静态方法,类方法需要在调用这个函数之前在参数列表前添上class的引用作为第一个参数。这个格式不管是对实例调用的情形还是类调用的情形都一样:

::
不像静态方法,类方法需要在调用函数之前会在参数列表前添上class的引用作为第一个参数。不管调用者是对象还是类,这个格式是一样的::

>>> class E(object):
def f(klass, x):
Expand All @@ -295,7 +290,7 @@ Objects/classobject.c(http://svn.python.org/view/python/trunk/Objects/classobjec
>>> print E().f(3)
('E', 3)

当一个函数不需要相关的数据做参数而之需要一个类的引用的时候,这个特征就显得必要了。一个用途就是用来创建一个类的构造器。在Python 2.3中, :func:`dict.fromkeys` 可以用键的列表来创建一个新的字典。等价的Python实现就是 ::
当一个函数不需要相关的数据做参数而只需要一个类的引用的时候,这个特征就显得很有用了。类方法的一个用途是用来创建不同的类构造器。在Python 2.3中, :func:`dict.fromkeys` 可以依据一个key列表来创建一个新的字典。等价的Python实现就是::

class Dict:
. . .
Expand All @@ -307,12 +302,12 @@ Objects/classobject.c(http://svn.python.org/view/python/trunk/Objects/classobjec
return d
fromkeys = classmethod(fromkeys)

这样,一个新的字典就可以这么创建::
现在,一个新的字典就可以这么创建::

>>> Dict.fromkeys('abracadabra')
{'a': None, 'r': None, 'b': None, 'c': None, 'd': None}

用非资料描述器来给出 :func:`classmethod` 的一个Python实现::
用非资料描述器协议, :func:`classmethod` 的纯Python版本实现看起来像这样::

class ClassMethod(object):
"Emulate PyClassMethod_Type() in Objects/funcobject.c"
Expand Down

0 comments on commit ba6dcd6

Please sign in to comment.