get_price函数定义python_fluent-python/第五章-一级函数.md at master · linwhitehat/fluent-python · GitHub...

Chapter 5. First-class functions

I have never considered Python to be heavily influenced by functional languages, no matter what people say or think. I was much more familiar with imperative languages such as C and Algol 68 and although I had made functions first-class objects, I didn’t view Python as a functional programming language[43].

— Guido van Rossum Python BDFL

Functions in Python are first-class objects. Programming language theorists define a “first-class object” as a program entity that can be:

Python中的函数是一级对象。编程语言专家定义“一级对象”作为程序实体时具有以下特点:

created at runtime;

assigned to a variable or element in a data structure;

passed as an argument to a function;

returned as the result of a function.

在运行时创建;

在一个数据结构中变量或者元素;

作为参数传递到一个函数;

作为函数的结果被返回;

Integers, strings and dictionaries are other examples of first-class objects in Python—nothing fancy here. But, if you came to Python from a language where functions are not first-class citizens, this chapter and the rest of Part III of the book focuses on the implications and practical applications of treating functions as objects.

这里整数、字符串和字典以及Python中其他一级对象的例子都没什么特别的。但是,如果你

TIP 提示

The term “first-class functions” is widely used as shorthand for “functions as first class objects”. It’s not perfect because it seems to imply an “elite” among functions. In Python, all functions are first-class.

术语“一级函数”被广泛用作“函数即一级对象”的简写。这样的说法并不完美,因为它。在Python中,所有的函数都是一级对象。

Treating a function like an object 把函数看作对象

The following console session shows that Python functions are objects. Here we create a function, call it, read its __doc__ attribute, and check that the function object itself is an instance of the function class:

下面的终端会话显示了Python函数就是对象。这里我们创建了一个函数,然后调用它,以读取它的__doc__属性,然后

Example 5-1. Create and test a function, then read its __doc__ and check its type.

例子 5-1。创建并测试一个函数,然后读区它的__doc__,再检查其类型。

>>>def factorial(n): # 1

... '''returns n!'''

... return 1 if n < 2 else n * factorial(n-1)

...

>>> factorial(42)

1405006117752879898543142606244511569936384000000000

>>> factorial.__doc__ # 2

'returns n!'

>>> type(factorial) # 3

This is a console session, so we’re creating a function in “run time”.

__doc__ is one of several attributes of function objects.

factorial is an instance of the function class.

因为这是一个终端会话,所以我们是在“运行时”创建一个函数。

__doc__是函数对象多个属性的其中一个。

factorial是function类的实例。

The __doc__ attribute is used to generate the help text of an object. In the Python interactive console, the command help(factorial) will display a screen like that in Figure 5-1:

__doc__属性用来生成一个对象的注释文本。在Python的交互控制台中,命令help(factorial)显示的屏幕结果会和图5-1中的一样:

Figure 5-1. Help screen for the factorial function. The text is from the __doc__ attribute of the function object.

图5-1. factorial函数的屏幕结果。文本来自函数对象的__doc__属性。

The next snippet shows the “first class” nature of a function object. We can assign it a variable fact and call it through that name. We can also pass factorial as an argument to map. The map function returns an iterable where each item is the the result of the application of the first argument (a function) to succesive elements of the second argument (an iterable), range(10) in this example.

接下来的代码片段展示了函数对象“一级对象”本性。我们还可以将factorial作为参数传递到map。map函数返回一个可迭代对象,其中每个项都是第一个参数(函数)与第二个参数的连续元素(可迭代对象)的交互结果,本例中为range(10)。

Example 5-2. Use function through a different name, and pass function as argument.

例子5-2. 通过不同的名称调用函数,并把函数作为参数进行传递。

>>> fact = factorial

>>> fact

>>> fact(5)

120

>>> map(factorial, range(11))

>>> list(map(fact, range(11)))

[1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880, 3628800]

Having first-class functions enables programming in a functional style. One of the hallmarks of functional programming is the use of higher-order functions, our next topic.

利用一级函数能够编进行函数化风格编程。函数化编程的一个标志性用法是高阶函数,这也正是我们接下来要谈到议题。

Higher-order functions 高阶函数

A function that takes a function as argument or returns a function as result is a higher-order function. One example is map, shown in Example 5-2. Another is the sorted built-in function: an optional key argument lets you provide a function to be applied to each item for sorting, as seen in list.sort and the sorted built-in function.

高阶函数是一个把函数作为参数,或者把函数作为返回结果的函数。其中一个例子是例子5-2中的map。另外一个是内建函数sorted:可选的参数key可以让你在排序时将提供函数应用到每个项,一如在list.sort和内建函数sorted中所见的那样。

For example, to sort a list of words by length, simply pass the len function as the key, as in Example 5-3.

例如,按单词长度排序一个列表,简单的把len函数作为键传递即可,一如例子5-3所示。

Example 5-3. Sorting a list of words by length.

例子5-3. 按照长度排列单词列表。

>>> fruits = ['strawberry', 'fig', 'apple', 'cherry', 'raspberry', 'banana']

>>> sorted(fruits, key=len)

['fig', 'apple', 'cherry', 'banana', 'raspberry', 'strawberry']

Any one-argument function can be used as key. For example, to create a rhyme dictionary it might be useful to sort each word spelled backwards. In the next snippet, note that the words in the list are not changed at all, only their reversed spelling is used as the sort criterion, so that the berries appear together.

任何的单参数函数都可用作key。例如,要创建一个押韵的字典,向后排列每个单词的拼写就显得非常有用。在接下来的代码片段中,列表中的单词根本没有被改变,只有它们的逆序拼写作为排序标准,所以浆果们都排在了一起。

Example 5-4. Sorting a list of words by their reversed spelling.

例子5-4. 根据逆序拼写排列一个单词列表。

>>> def reverse(word):

... return word[::-1]

>>> reverse('testing')

'gnitset'

>>> sorted(fruits, key=reverse)

['banana', 'apple', 'fig', 'raspberry', 'strawberry', 'cherry']

In the functional programming paradigm, some of the best known higher-order functions are map, filter, reduce and apply. The apply function was deprecated in Python 2.3 and removed in Python 3 because it’s no longer necessary. If you need to call a function with a dynamic set of arguments, you can just write fn(*args, **keywords) instead of apply(fn, args, kwargs).

在函数化编程范式中,一些最常见的高阶函数有map,filter,reduce和apply。apply函数在Python2.3时被丢弃,在Python 3 时被删除,因为没有使用的必要了。如果你需要调用一个包含一组动态参数的函数,你可以这样写fn(*args, **keywords)而不是使用(fn, args, kwargs)。

The map, filter and reduce higher-order functions are still around, but better alternatives are available for most of their use cases, as the next section shows.

高阶函数map, filter和reduce仍在被人们广泛使用,但是对于它们使用的多数场景来说还有更好选择可用,一如下面章节的显示。

Modern replacements for map, filter and reduce 对map,filter和reduce的替换

Functional languages commonly offer the map, filter and reduce higher-order functions (sometimes with different names). The map and filter functions are still built-ins in Python 3, but since the introduction of list comprehensions and generator expressions, they are not as important. A listcomp or a genexp does the job of map and filter combined, but is more readable. Consider this:

函数化语言通常提供map,filter和reduce高阶函数(有时使用另外的名称)。map和filter函数在Python 3 中仍旧是内建的,但是自从列表解析式和生成器表达式的引入,它们已显得不再重要了。列表解析式或者生成器表达式做了map和filter工作的总和,而且更具有可读性。考虑下面这个例子:

Example 5-5. Lists of factorials produced with map and filter compared to alternatives coded as list comprehensions.

例子5.5。使用map和filter生成的阶乘列表与另外的列表解析式所生成的相比较的代码。

>>> list(map(fact, range(6))) # 1

[1, 1, 2, 6, 24, 120]

>>> [fact(n) for n in range(6)] # 2

[1, 1, 2, 6, 24, 120]

>>> list(map(factorial, filter(lambda n: n % 2, range(6)))) # 3

[1, 6, 120]

>>> [factorial(n) for n in range(6) if n % 2] # 4

[1, 6, 120]

Build a list of factorials from 0! to 5!

构造一个从0到5的阶乘列表

Same operation, with a list comprehension.

同样的操作,使用列表解析式

List of factorials of odd numbers up to 5!, using both map and filter.

奇数的阶乘列表增加到5,使用的是map和filter。

List comprehension does the same job, replacing map and filter, and making lambda unnecessary.

列表解析式完成了同样的工作,把替换为了map和filte,而且使用lamda是没有必要的。

In Python 3, map and filter return generators—a form of iterator—so their direct substitute is now a generator expression (in Python 2 these functions returned lists, therefore their closest alternative is a listcomp).

在Python 3中,map和filter返回一个迭代器形式的生成器,所以它们现在直接被替换为生成器表达式(在Python 2中这些函数返回的是列表,因此它们的最接近选择是listcomp)。

The reduce function was demoted from a built-in in Python 2 to the functools module in Python 3. Its most common use case, summation, is better served by the sum built-in available since Python 2.3 was released in 2003. This is a big win in terms of readability and performance (see Example 5-5).

reduce函数在Python2中的内建函数中降级到了Python 3中的functools模块。它最常见的用是求和,最好使用是在2003年发布的Python 2.3中的sum。这是在可读性术语和性能方面一大进步(参见例子5-5)。

Example 5-6. Sum of integers up to 99 performed with reduce and sum.

例子5-6. 使用reduce和sum之行整数加到99的总和。

>>> from functools import reduce # 1

>>> from operator import add # 2

>>> reduce(add, range(100)) # 3

4950

>>> sum(range(100)) # 4

4950

Starting with Python 3.0, reduce is not a built-in.

从Python 3.0开始,reduce不再内置。

Import add to avoid creating a function just to add two numbers.

导入add以避免创建

Sum integers up to 99.

总和整数增加到99.

Same task using sum; import or adding function not needed.

同样的任务是用sum完成,导入或者加入函数并需要。

The common idea of sum and reduce is to apply some operation to successive items in a sequence, accumulating previous results, thus reducing a sequence of values to a single value.

sum和reduce的共有理念是在一个序列中对连续的项进行操作,累加之前的结果,这样就将值序列减少为单个值。

Other reducing built-ins are all and any:

其它内建的缩减为all和any:

all(iterable)

return True if every element of the iterable is truthy; all([]) returns True.

all(可迭代)

如果每个可迭代的元素都是真的则返回True;all([])返回True。

any(iterable)

return True if any element of the iterable is truthy; all([]) returns False.

any(可迭代)

如果每个元素都是真正可迭代,则返回True;all([])返回False。

I give a fuller explanation of reduce in "Vector take #4: hashing and a faster ==" where an ongoing example provides a meaningful context for the use of this function. The reducing functions are summarized later in the book when iterables are in focus, in Iterable reducing functions.

我在“#4:散列和更快的==”中给出了reduce的完整解释,这里的仍在进行的中的例子对于该函数的使用提供了富有含义的上下文。在我们关注在可迭代对象上时在本书中稍后给出了总结,

To use a higher-order function sometimes it is convenient to create a small, one-off function. That is why anonymous functions exist. We’ll cover them next.

某些情况下使用高阶函数可以很方便的创建一个小的、一次性的函数。这也是匿名函数存在的原因。接下来我们就要来好好讲一讲。

Anonymous functions 匿名函数

The lambda keyword creates an anonymous function within a Python expression.

However, the simple syntax of Python limits the body of lambda functions to be pure expressions. In other words, the body of a lambda cannot make assignments or use any other Python statement such as while, try etc.

lambda关键字可以创建位于Python表达式内部的匿名函数。

不过,由于Python的简单与法限制了lambda函数的主体成为纯表达式的可能。换句话说,

The best use of anonymous functions is in the context of an argument list. For example, here is the rhyme index example from Example 5-4 rewritten with lambda, without defining a reverse function:

匿名函数的最佳用法是在一个参数列表的上下文中。例如,下面是来自例子5-4的使用了lambda重写,而不用定义reverse函数的押韵索引示例:

Example 5-7. Sorting a list of words by their reversed spelling using lambda.

例子5-7. 使用lambda按照逆序排列列表。

>>> fruits = ['strawberry', 'fig', 'apple', 'cherry', 'raspberry', 'banana']

>>> sorted(fruits, key=lambda word: word[::-1])

['banana', 'apple', 'fig', 'raspberry', 'strawberry', 'cherry']

Outside the limited context of arguments to higher-order functions, anonymous functions are rarely useful in Python. The syntactic restrictions tend to make non-trivial lambdas either unreadable or unworkable.

从外部的有限参数上下文,到高阶函数,在Python中都很少使用。语法上的限制意图在于让不寻常的lambda不可读或者不可工作。

LUNDH’S LAMBDA REFACTORING RECIPE

If you find a piece of code hard to understand because of a lambda, Fredrik Lundh suggests this refactoring procedure:

1 Write a comment explaining what the heck that lambda does.

2 Study the comment for a while, and think of a name that captures the essence of the comment.

3 Convert the lambda to a def statement, using that name.

4 Remove the comment.

The steps above are quoted from the Functional Programming HOWTO, a must read.

郎德的lambda重构技巧

如果你由于lambda的原因导致一段代码很难理解,那么Fredrik Lundh建议用以下过程来重构:

1 写一段注释来解释为什么lambda 到底干了些什么。

2

3 使用这个名字把lambda转换到def语句。

4 移除注释。

上面的步骤引述自那本必读的《函数化编程如何做》

The lambda syntax is just syntactic sugar: a lambda expression creates a function object just like the def statement. That is just one of several kinds of callable objects in Python. The following section overviews all of them.

lambda只是语法糖而已:lambda表达式和def语句一样能够创建一个函数对象。而且创建这个对象也只是Python中可调用对象的其中一种。下面一节概览了全部的可调用对象类型。

The seven flavors of callable objects

7种风格的可调用对象

The call operator, i.e. (), may be applied to other objects beyond user-defined functions. To determine whether an object is callable, use the callable() built-in function. The Python Data Model documentation lists seven callable types:

通过用户定义的函数调用运算符,比如,()应用到其他对象。为了确定一个对象是否可调用,你可以使用内建函数callable()。Python数据模型文档列出了7中可调用对象类型:

User-defined functions

created with def statements or lambda expressions.

用户自定义函数

使用def语句创建或者使用lamda表达式。

Built-in functions

a function implemented in C (for CPython), like len or time.strftime.

内建函数

使用C实现的函数,比如len或者time.strftime。

Built-in methods

methods implemented in C, like dict.get.

内建方法

使用C实现的方法,像dict.get。

Methods

functions defined in the body of a class.

方法

定义在类主体中的函数。

Classes

when invoked, a class runs its __new__ method to create an instance, then __init__ to initialize it, and finally the instance is returned to the caller. Because there is no new operator in Python, calling a class is like calling a function[44].

在调用时,类运行自身的__new__方法创建一个实例,然后__init__初始化这个实例,最后实例返回给调用者。因为在Python中没有新的运算符,所以调用类就好调用函数一样。

Class instances

if a class defines a __call__ method, then its instances may be invoked as functions. See User defined callable types below.

类实例

如果一个类定义了__call__方法,那么它的实力可以当作函数来调用。参见下面的用户自定义可调用类型。

Generator functions

functions or methods that use the yield keyword. When called, generator functions return a generator object.

生成器函数

使用了yield关键字的函数或者方法。在被调用时,生成器函数返回一个生成器对象。

Generator functions are unlike other callables in many respects. Chapter 14 is devoted to them. They can also be used as coroutines, which are covered in Chapter 16.

在多数情况下,生成器函数都和其他可调用对象不同。第十四章专门用来讲解它们。它们也可以当作协程来使用,这部分内容在第十六章被提及。

TIP

Given the variety of existing callable types in Python, the safest way to determine whether an object is callable is to use the callable() built-in:

提示

Python中现存多种可调用类型,要确定一个对象是否可被调用的最安全方式是使用内建的callable():

>>> abs, str, 13

(, , 13)

>>> [callable(obj) for obj in (abs, str, 13)]

[True, True, False]

We now move to building class instances that work as callable objects.

现在我们来学习构建可以作为可调用对象的类实例。

User defined callable types 用户定义可调用类型

Not only are Python functions real objects, but arbitrary Python objects may also be made to behave like functions. Implementing a __call__ instance method is all it takes.

不仅仅是Python函数才是对象,任意Python对象都可以写成类似函数的行为。

Example 5-8 implements a BingoCage class. An instance is built from any iterable, and stores an internal list of items, in random order. Calling the instance pops an item.

例子5-8实现了BinggoCage类。实例的构建可以为任意可迭代对象,并以随机顺序存储一个内部的项列表。调用实例弹出一个项。

Example 5-8. bingocall.py: A BingoCage does one thing: picks items from a shuffled list.

例子5-8.bingocall.py:BingoCage只做了一件事,即从打乱了次序的列表中选取项。

import random

class BingoCage:

def __init__(self, items):

self._items = list(items) # 1

random.shuffle(self._items) # 2

def pick(self): # 3

try:

return self._items.pop()

except IndexError:

raise LookupError('pick from empty BingoCage') # 4

def __call__(self): # 5

return self.pick()

__init__ accepts any iterable; building a local copy prevents unexpected side-effects on any list passed as an argument.

__init__接受任意可迭代对象;构建一份本地拷贝以防止未预料到边际效应。

shuffle is guaranteed to work because self._items is a list.

shuffle是正常工作的保证,因为self._items是一个列表。

The main method.

主方法。

Raise exception with custom message if self._items is empty.

如果self._items为空则抛出含有自定义消息的异常。

Shortcut to bingo.pick(): bingo().

访问bingo.pick()的快捷方式:bingo()。

Here is a simple demo of Example 5-8. Note how a bingo instance can be invoked as a function, and the callable(…) built-in recognizes it as a callable object.

这里是一个例子5-8的简单用例。要注意bingo实例是如何被当作函数调用,内建的callable(…)识别出它为一个可调用对象的。

>>> bingo = BingoCage(range(3))

>>> bingo.pick()

1

>>> bingo()

0

>>> callable(bingo)

True

A class implementing __call__ is an easy way to create function-like objects that have some internal state that must be kept across invocations, like the remaining items in the BingoCage. An example is a decorator. Decorators must be functions, but it is sometimes convenient to be able to “remember” something between calls of the decorator, for example for memoization—caching the results of expensive computations for later use.

类实现的__call__是一个创建拥有保持特定内部状态可调用的类函数对象的简单办法,比如BingoCage中余下的项。其中一个例子就是装饰器。装饰器必须是函数,而且有时候能够很方便的“记住”装饰器调用之间的某些内容,例如为了稍后使用的缓存记忆昂贵计算的结果。

A totally different approach to creating functions with internal state is to use closures. Closures, as well as decorators, are the subject of Chapter 7.

一种完全不同的创建含有内部状态的函数方法是使用闭包。闭包,即装饰器,就是第七章的话题。

We now move to another aspect of handling functions as objects: run-time introspection.

我们现在学习一些将函数作为对象另外一个方面:运行时的自省。

Function introspection

函数内省

Function objects have many attributes beyond __doc__. See below what the dir function reveals about our factorial:

函数对象拥有除了__doc__之外的很多属性。看看下面dir函数打印出了factorial的什么内容:

>>> dir(factorial)

['__annotations__', '__call__', '__class__', '__closure__', '__code__',

'__defaults__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__',

'__format__', '__ge__', '__get__', '__getattribute__', '__globals__',

'__gt__', '__hash__', '__init__', '__kwdefaults__', '__le__', '__lt__',

'__module__', '__name__', '__ne__', '__new__', '__qualname__', '__reduce__',

'__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__',

'__subclasshook__']

Most of these attributes are common to Python objects in general. In this section we cover those which are especially relevant to treating functions as objects, starting with __dict__.

通常来说这些属性中的大多数的都是Python共有的。本节我们从__dict__学习到这些属性,特别是和把函数当作对象的相关内容。

Like the instances of a plain user-defined class, a function uses the __dict__ attribute to store user attributes assigned to it. This is useful as a primitive form of annotation. Assigning arbitrary attributes to functions is not a very common practice in general, but Django is one framework that uses it. See, for example, the short_description, boolean and allow_tags attributes described in The Django admin site documentation. In the Django docs this example shows attaching a short_description to a method, to determine the description that will appear in record listings in the Django admin when that method is used:

和普通的用户定义类的实例一样,函数使用__dict__属性存储用户赋值到实例的属性。作为注记的基本形式着非常有用。通常对函数赋予任意属性并不是很常见的做法,但是Django就是这样做的其中一个框架。例如,简单来说,你看描述在Django admin管理后台中文档中的布尔值和allow_description就是这样。在Django文档中这个例子展示了对方法附加一个short_description,已确定方法在Django admin中使用是描述出现在记录列表中:

def upper_case_name(obj):

return ("%s %s" % (obj.first_name, obj.last_name)).upper()

upper_case_name.short_description = 'Customer name'

Now let us focus on the attributes that are specific to functions and are not found in a generic Python user-defined object. d the difference of two sets quickly gives us a list of the function-specific attributes:

现在我们关注点转移到函数专用而且在普通的用户定义对象中找不到属性。通过快速计算两个集合的不同可以给出一个函数专有属性列表:

Example 5-9. Listing attributes of functions that don’t exist in plain instances.

例子5-9. 列出不存在普通实例中的函数属性。

>>> class C: pass # 1

>>> obj = C() # 2

>>> def func(): pass # 3

>>> sorted(set(dir(func)) - set(dir(obj))) #

['__annotations__', '__call__', '__closure__', '__code__', '__defaults__',

'__get__', '__globals__', '__kwdefaults__', '__name__', '__qualname__']

Create bare user-defined class.

创建空的用户自定义类。

Make an instance of it.

创建类的实例。

Create a bare function.

创建一个空函数。

Using set difference, generate a sorted list of the attributes that exist in a function but not in an instance of a bare class.

使用set差集,来生成一个存在函数中,但是不存在基本类实例中的排序过的属性列表。

Table 5-1 shows a summary of the attributes listed by Example 5-9.

表格5-1显示了例子5-9的属性汇总列表。

Table 5-1. Attributes of user-defined functions

name

type

description

__annotations__

dict

parameter and return annotations

__call__

method-wrapper

implementation of the () operator; a.k.a. the callable object protocol

__closure__

tuple

the function closure, i.e. bindings for free variables (often is None)

__code__

code

function metadata and function body compiled into bytecode

__defaults__

tuple

default values for the formal parameters

__get__

method-wrapper

implementation of the read-only descriptor protocol (see XREF)

__globals__

dict

global variables of the module where the function is defined

__kwdefaults__

dict

default values for the keyword-only formal parameters

__name__

str

the function name

__qualname__

str

the qualified function name, ex.: Random.choice (see PEP-3155)

名称

类型

描述

__annotations__

字典

参数并返回注解

__call__

方法包装器

实现()运算符;又被称为,可调用对象协议

__closure__

元组

函数闭包,比如,绑定中的自由变量(通常为None)

__code__

代码

函数元数据和变异为字节码的函数主体

__defaults__

元组

形参的默认值

__get__

方法包装器

只读描述符协议的实现(参见XREF)

__globals__

字典

定义函数的模块中的全局变量

__kwdefaults__

字典

仅关键字的形参的默认值

__name__

字符串

函数名称

__qualname__

字符串

合格的函数名称,例如:Random.choice(参见 PEP-3155)

In coming sections we will discuss the __defaults__, __code__ and __annotations__, used by IDEs and frameworks to extract information about function signatures. But to fully appreciate these attributes, we will make a detour to explore the powerful syntax Python offers to declare function parameters and to pass arguments into them.

在接下来的的小节中我们会讨论用于在IDE和框架中提取函数签名信息的__defaults__, __code__ 和 __annotations__。但是要充分认识这些属性,我们会绕上一圈来浏览Python提供的强大语法来声明函数参数,并把参数传递到它们。

From positional to keyword-only parameters

位置参数与关键字参数

One of the best features of Python functions is the extremely flexible parameter handling mechanism, enhanced with keyword-only arguments in Python 3. Closely related are the use of * and ** to “explode” iterables and mappings into separate arguments when we call a function. To see these features in action, see the code for Example 5-10 and tests showing its use in Example 5-11.

Python函数的其中一个最好的特点就是极其灵活的参数处理机制,Python 3中增强的仅关键字参数。

Example 5-10. tag generates HTML. A keyword-only argument cls is used to pass "class" attributes as a work-around because class is a keyword in Python.

例子5-10. tag生成HTML。为了方便调用,关键字参数cls用来传递“类”属性,因为类在Python中是关键字。

def tag(name, *content, cls=None, **attrs):

"""Generate one or more HTML tags 生成一个或者多个HTML标签"""

if cls is not None:

attrs['class'] = cls

if attrs:

attr_str = ''.join(' %s="%s"' % (attr, value)

for attr, value in sorted(attrs.items()))

else:

attr_str = ''

if content:

return '\n'.join('%s%s>' %

(name, attr_str, c, name) for c in content)

else:

return '' % (name, attr_str)

The tag function can be invoked in many ways, as Example 5-11 shows.

就像例子5-11显示的那样,tag函数可以使用多种方式来调用。

Example 5-11. Some of the many ways of calling the tag function from Example 5-10.

例子5-11. 从例子5-10中调用tag函数中多种方式中的一种。

>>> tag('br')

'
'

>>> tag('p', 'hello')

'

hello

'

>>> print(tag('p', 'hello', 'world'))

hello

world

>>> tag('p', 'hello', id=33)

'

hello

'

>>> print(tag('p', 'hello', 'world', cls='sidebar'))

>>> tag(content='testing', name="img")

''

>>> my_tag = {'name': 'img', 'title': 'Sunset Boulevard',

... 'src': 'sunset.jpg', 'cls': 'framed'}

>>> tag(**my_tag)

'sunset.jpg'

A single positional argument produces an empty tag with that name.

单个位置参数输出一个包含名称的空标签。

Any number of arguments after the first are captured by content as a tuple.

在第一个参数之后的任意数量参数都会被content捕获为一个元组。

Keyword arguments not explicitly named in the tag signature are captured by attrs as a dict.

在tag标签中没有明确命名的关键字参数被attrs捕捉为一个字典。

The cls parameter can only be passed as a keyword argument.

参数cls只可以作为关键字参数来传递。

Even the first positional argument can be passed as a keyword when tag is called.

在tab被调用时,第一个位置参数就可以作为关键字来传递。

Prefixing the my_tag dict with ** passes all its items as separate arguments which are then bound to the named parameters, with the remaining caught by **attrs.

给字典my_tag加入**前缀将把它的所有项做为一个独立的参数

Keyword-only arguments are a new feature in Python 3. In Example 5-10 the cls parameter can only be given as a keyword argument—it will never capture unnamed positional arguments. To specify keyword-only arguments when defining a function, name them after the argument prefixed with *. If you don’t want to support variable positional arguments but still want keyword-only arguments, put a * by itself in the signature, like this:

仅关键字参数是出现在Python 3中的新功能。在栗子5-10中cls参数金匮做为关键字参数使用——它绝对不会不活未被命名的位置参数。再定义个函数时,为指定仅关键字参数

>>> def f(a, *, b):

... return a, b

...

>>> f(1, b=2)

(1, 2)

Note that keyword-only arguments do not need to have a default value: they can be mandatory, like b in the example above.

注意,仅关键字参数不需要拥有默认值:这些值是强制的,就像上面例子中的b那样。

We now move to the introspection of function parameters, starting with a motivating example from a web framework, and on through introspection techniques.

我们现在进入函数参数的内省的学习,并从一个富有启发web框架例子开始彻底了解内省的技术。

Retrieving information about parameters

重新取回参数信息

An interesting application of function introspection can be found in the Bobo HTTP micro-framework. To see that in action, consider a variation of the Bobo tutorial “Hello world” application in Example 5-12.

你可以在Bobo HTTP微框架中发现一个有趣的函数内省应用。

Example 5-12. Bobo knows that hello requires a person argument, and retrieves it from the HTTP request.

例子5-12. Bobo知道hello要求一个person参数,并从HTTP请求中重新取回它。

import bobo

@bobo.query('/')

def hello(person):

return 'Hello %s!' % person

The bobo.query decorator integrates a plain function such as hello with the request handling machinery of the framework. We’ll cover decorators in Chapter 7—that’s not the point of this example here. The point is that Bobo introspects the hello function and finds out it needs one parameter named person to work, and it will retrieve a parameter with that name from the request and pass it to hello, so the programmer does not need to touch the request object at all.

bobo.query装饰器集成到了一个普通函数,比如包含请求的框架系统hello。我们会在第七章中学习装饰器——这也不是此处这个例子的要点。要点是Bobo自省了hello函数,并发现自己需要一个名称为person的参数以便正常工作,然后它重新取回来自request的person参数并传递到hello,这样程序员根本就不需要接触任何request对象。

If you install Bobo and point its development server to the code above (e.g. bobo -f hello.py), a hit on the URL http://localhost:8080/ will produce the message “Missing form variable person” with a 403 HTTP code. This happens because Bobo understands that the person argument is required to call hello, but no such name was found in the request. Example 5-13 is a shell session using curl to show this behavior.

如果男装Bobo并且把开发服务器指向上面的代码(比如,bobo -f hello.py),输入URLhttp://localhost:8080/ 会得到包含403HTTP代码的消息。出现这种结果是因为Bobo理解需要person参数才能调用hello,但是在request中也没有找到对应的名称。例子5-13

Example 5-13. Bobo issues a 403 forbidden response if there are missing function arguments in the request; curl -i is used to dump the headers to standard output.

例子5-13。 如果在请求中缺失函数参数那么Bobo会返回一个403禁止响应;

$ curl -i http://localhost:8080/

HTTP/1.0 403 Forbidden

Date: Thu, 21 Aug 2014 21:39:44 GMT

Server: WSGIServer/0.2 CPython/3.4.1

Content-Type: text/html; charset=UTF-8

Content-Length: 103

Missing parameter

However if you get http://localhost:8080/?person=Jim, the response will be the string 'Hello Jim!'. See Example 5-14.

Example 5-14. Passing

例子5-14. 传递。

$ curl -i http://localhost:8080/?person=Jim

HTTP/1.0 200 OK

Date: Thu, 21 Aug 2014 21:42:32 GMT

Server: WSGIServer/0.2 CPython/3.4.1

Content-Type: text/html; charset=UTF-8

Content-Length: 10

Hello Jim!

How does Bobo know what are the parameter names required by the function, and whether they have default values or not?

Bobo如何知道函数需要什么参数,函数是否拥有默认值?

Within a function object, the __defaults__ attribute holds a tuple with the default values of positional and keyword arguments. The defaults for keyword-only arguments appear in __kwdefaults__. The names of the arguments, however, are found within the __code__ attribute, which is a reference to a code object with many attributes of its own.

在一个函数对象内部,__defaults__属性拥有一个含有位置参数、关键参数的默认值组成的元祖。默认的仅关键字参数可以在__kwdefaults__中找到。不过,

To demonstrate the use of these attributes, we will inspect the function clip in a module clip.py, listed in Example 5-15.

为了说明这些属性的用法,在例子5-15,我们对位于模块clip.py中的clip函数进行检查。

Example 5-15. Function to shorten a string by clipping at a space near the desired length.

def clip(text, max_len=80):

"""Return text clipped at the last space before or after max_len

"""

end = None

if len(text) > max_len:

space_before = text.rfind(' ', 0, max_len)

if space_before >= 0:

end = space_before

else:

space_after = text.rfind(' ', max_len)

if space_after >= 0:

end = space_after

if end is None: # no spaces were found

end = len(text)

return text[:end].rstrip()

Example 5-16 shows the values of defaults, code.co_varnames and code.co_argcount for the clip function listed above.

例子5-16展示了上面列出的函数的__defaults__, code.co_varnames以及__code__.co_argcount值。

Example 5-16. Extracting information about the function arguments.

例子5-16. 提取函数参数信息。

>>> from clip import clip

>>> clip.__defaults__

(80,)

>>> clip.__code__ # doctest: +ELLIPSIS

>>> clip.__code__.co_varnames

('text', 'max_len', 'end', 'space_before', 'space_after')

>>> clip.__code__.co_argcount

2

As you can see, this is not the most convenient arrangement of information. The argument names appear in code.co_varnames, but that also includes the names of the local variables created in the body of the function. Therefore the argument names are the first N strings, where N is given by code.co_argcount which—by the way—does not include any variable arguments prefixed with * or **. The default values are identified only by their position in the defaults tuple, so to link each with the respective argument you have to scan from last to first. In the example, we have two arguments, text and max_len, and one default, 80, so it must belong to the last argument, max_len.

你可以看得到,

This is awkward.

Fortunately there is a better way: the inspect module.

幸运地是我们用一个更好的办法:inspect模块。

Take a look at Example 5-17.

我们来看看来例子5-17.

Example 5-17. Extracting the function signature.

例子5-17. 提取函数签名。

>>> from clip import clip

>>> from inspect import signature

>>> sig = signature(clip)

>>> sig # doctest: +ELLIPSIS

>>> str(sig)

'(text, max_len=80)'

>>> for name, param in sig.parameters.items():

... print(param.kind, ':', name, '=', param.default)

...

POSITIONAL_OR_KEYWORD : text =

POSITIONAL_OR_KEYWORD : max_len = 80

Much better: inspect.signature returns an inspect.Signature object, which has a parameters attribute that lets you read an ordered mapping of names to inspect.Parameter objects. Each Parameter instance has attributes such as name, default and kind. The special value inspect._empty denotes parameters with no default—which makes sense considering that None is a valid—and popular—default value.

现在好多了:inspect.signature返回一个inspect.Signature对象,该对象拥有一个让你可以读取一个排序的关联到inspect.Parameter对象的名称映射。每个参数实例都拥有一个此类名称,

The kind attribute holds one of five possible values from _ParameterKind class:

POSITIONAL_OR_KEYWORD

a parameter that may be passed as a positional or as a keyword argument (most Python function parameters are of this kind).

VAR_POSITIONAL

a tuple of positional parameters.

VAR_KEYWORD

a dict of keyword parameters.

KEYWORD_ONLY

a keyword-only parameter (new in Python 3).

POSITIONAL_ONLY

a positional-only parameter; currently unsupported by Python function declaration syntax, but exemplified by existing functions implemented in C—like divmod—that do not accept parameters passed by keyword.

Besides name, default and kind, inspect.Parameter objects have an annotation attribute which is usually inspect._empty but may contain function signature metadata provided via the new annotations syntax in Python 3 (annotations are covered in the next section).

An inspect.Signature object has a bind method that takes any number of arguments and binds them to the parameters in the signature, applying the usual rules for matching actual arguments to formal parameters. This can be used by a framework to validate arguments prior to the actual function invocation.

inspect.Signature对象用一个接受任意数量参数的绑定方法,

Example 5-18. Binding the function signature from the tag function in Example 5-10 do a dict of arguments.

>>> import inspect

>>> sig = inspect.signature(tag)

>>> my_tag = {'name': 'img', 'title': 'Sunset Boulevard',

... 'src': 'sunset.jpg', 'cls': 'framed'}

>>> bound_args = sig.bind(**my_tag)

>>> bound_args

>>> for name, value in bound_args.arguments.items():

... print(name, '=', value)

...

name = img

cls = framed

attrs = {'title': 'Sunset Boulevard', 'src': 'sunset.jpg'}

>>> del my_tag['name']

>>> bound_args = sig.bind(**my_tag)

Traceback (most recent call last):

...

TypeError: 'name' parameter lacking default value

Get the signature from tag function in Example 5-10.

Pass a dict of arguments to .bind().

An inspect.BoundArguments object is produced.

Iterate over the items in bound_args.arguments, which is an OrderedDict, to display the names and values of the arguments.

Remove the mandatory argument name from my_tag.

Calling sig.bind(**my_tag) raises a TypeError complaining of the missing name parameter.

This is shows how the Python Data Model, with the help of inspect, exposes the same machinery the interpreter uses to bind arguments to formal parameters in function calls.

Frameworks and tools like IDEs can use this information to validate code. Another feature of Python 3, function annotations, enhances the possible uses of this, as we will see next.

Function annotations

函数注解

Python 3 provides syntax to attach metadata to the parameters of a function declaration and its return value. Example 5-19 is an annotated version of Example 5-15. The only differences are in the first line.

Python 3 提供了

Example 5-19. Annotated clip function.

def clip(text:str, max_len:'int > 0'=80) -> str:

"""Return text clipped at the last space before or after max_len

"""

end = None

if len(text) > max_len:

space_before = text.rfind(' ', 0, max_len)

if space_before >= 0:

end = space_before

else:

space_after = text.rfind(' ', max_len)

if space_after >= 0:

end = space_after

if end is None: # no spaces were found

end = len(text)

return text[:end].rstrip()

The annotated function declaration.

Each argument in the function declaration may have an annotation expression preceded by :. If there is a default value, the annotation goes between the argument name and the = sign. To annotate the return value, add -> and another expression between the ) and the : at the tail of the function declaration. The expressions may be of any type. The most common types used in annotations are classes, like str or int, or strings, like 'int > 0', as seen in the annotation for max_len in Example 5-19.

No processing is done with the annotations. They are merely stored in the annotations attribute of the function, a dict:

>>> from clip_annot import clip

>>> clip.__annotations__

{'text': , 'max_len': 'int > 0', 'return': }

The item with key 'return' holds the return value annotation marked with -> in the function declaration in Example 5-19.

The only thing Python does with annotations is to store them in the annotations attribute of the function. Nothing else: no checks, enforcement, validation, or any other action is performed. In other words, annotations have no meaning to the Python interpreter. They are just metadata that may be used by tools, such as IDEs, frameworks and decorators. At this writing no tools that use this metadata exist in the standard library, except that inspect.signature() knows how to extract the annotations, as Example 5-20 shows.

Example 5-20. Extracting annotations from the function signature.

例子5-20. 从函数签名中提取注释

>>> from clip_annot import clip

>>> from inspect import signature

>>> sig = signature(clip)

>>> sig.return_annotation

>>> for param in sig.parameters.values():

... note = repr(param.annotation).ljust(13)

... print(note, ':', param.name, '=', param.default)

: text =

'int > 0' : max_len = 80

The signature function returns a Signature object which has a return_annotation attribute and a parameters dictionary mapping parameter names to Parameter objects. Each Parameter object has its own annotation attribute. That’s how Example 5-20 works.

In the future, frameworks such as Bobo could support annotations to further automate request processing. For example, an argument annotated as price:float may be automatically converted from query string to the float expected by the function; a string annotation like quantity:'int > 0' might be parsed to perform conversion and validation of a parameter.

The biggest impact of functions annotations will probably not be dynamic settings such as Bobo, but in providing optional type information for static type checking in tools like IDEs and linters.

After this deep dive into the anatomy of functions, the remaining of this chapter covers the most useful packages in the standard library supporting functional programming.

Packages for functional programming

Although Guido makes it clear that Python does not aim to be a functional programming language, a functional coding style can be used to good extent, thanks to the support of packages like operator and functools, which we cover in the next two sections.

The operator module 模块operator

Often in functional programming it is convenient to use an arithmetic operator as a function. For example, suppose you want to multiply a sequence of numbers to calculate factorials without using recursion. To perform summation you can use sum, but there is no equivalent function for multiplication. You could use reduce—as we saw in Modern replacements for map, filter and reduce—but this requires a function to multiply two items of the sequence. Here is how to solve this using lambda:

在函数式编程中使用将数字运算符作为函数使用是很方便的。例如,假设你

Example 5-21. Factorial implemented with reduce and an anonymous function.

from functools import reduce

def fact(n):

return reduce(lambda a, b: a*b, range(1, n+1))

To save you the trouble of writing trivial anonymous functions like lambda a, b: a*b, the operator module provides function equivalents for dozens of arithmetic operators. With it, we can rewrite Example 5-21 as Example 5-22.

Example 5-22. Factorial implemented with reduce and operator.mul.

from functools import reduce

from operator import mul

def fact(n):

return reduce(mul, range(1, n+1))

Another group of one-trick lambdas that operator replaces are functions to pick items from sequences or read attributes from objects: itemgetter and attrgetter actually build custom functions to do that.

Example 5-23 shows a common use of itemgetter: sorting a list of tuples by the value of one field. In the example, the cities are printed sorted by country code (field 1). Essentially, itemgetter(1) does the same as lambda fields:

fields[1]: create a function that, given a collection, returns the item at index 1.

Example 5-23. Demo of itemgetter to sort a list of tuples (data from Example 2-8).

>>> metro_data = [

... ('Tokyo', 'JP', 36.933, (35.689722, 139.691667)),

... ('Delhi NCR', 'IN', 21.935, (28.613889, 77.208889)),

... ('Mexico City', 'MX', 20.142, (19.433333, -99.133333)),

... ('New York-Newark', 'US', 20.104, (40.808611, -74.020386)),

... ('Sao Paulo', 'BR', 19.649, (-23.547778, -46.635833)),

... ]

>>>

>>> from operator import itemgetter

>>> for city in sorted(metro_data, key=itemgetter(1)):

... print(city)

...

('Sao Paulo', 'BR', 19.649, (-23.547778, -46.635833))

('Delhi NCR', 'IN', 21.935, (28.613889, 77.208889))

('Tokyo', 'JP', 36.933, (35.689722, 139.691667))

('Mexico City', 'MX', 20.142, (19.433333, -99.133333))

('New York-Newark', 'US', 20.104, (40.808611, -74.020386))

If you pass multiple index arguments to itemgetter, the function it builds will return tuples with the extracted values:

如果你传递多个索引参数到itemgetter,函数将返回拥有提取值构成的元组:

>>> cc_name = itemgetter(1, 0)

>>> for city in metro_data:

... print(cc_name(city))

...

('JP', 'Tokyo')

('IN', 'Delhi NCR')

('MX', 'Mexico City')

('US', 'New York-Newark')

('BR', 'Sao Paulo')

Because itemgetter uses the [] operator, it supports not only sequences but also mappings and any class that implements __getitem__.

因为itemgetter使用的是运算符[],它不仅支持序列而且还可以

A sibling of itemgetter is attrgetter, which creates functions to extract object attributes by name. If you pass attrgetter several attribute names as arguments, it also returns a tuple of values. In addition, if any argument name contains a . (dot), attrgetter navigates through nested objects to retrieve the attribute. These behaviors are shown in Example 5-24. This is not the shortest console session because we need to build a nested structure to showcase the handling of dotted attributes by attrgetter.

itemgetter的兄弟是attrgetter,它创建函数并通过属性名称来提取对象。如果你

Example 5-24. Demo of attrgetter to process a previously defined list of namedtuple called metro_data (the same list that appears in Example 5-23).

例子5-24.

>>> from collections import namedtuple

>>> LatLong = namedtuple('LatLong', 'lat long') #

>>> Metropolis = namedtuple('Metropolis', 'name cc pop coord') #

>>> metro_areas = [Metropolis(name, cc, pop, LatLong(lat, long)) #

... for name, cc, pop, (lat, long) in metro_data]

>>> metro_areas[0]

Metropolis(name='Tokyo', cc='JP', pop=36.933, coord=LatLong(lat=35.689722, long=139.691667))

>>> metro_areas[0].coord.lat #

35.689722

>>> from operator import attrgetter

>>> name_lat = attrgetter('name', 'coord.lat') #

>>>

>>> for city in sorted(metro_areas, key=attrgetter('coord.lat')): #

... print(name_lat(city)) #

...

('Sao Paulo', -23.547778)

('Mexico City', 19.433333)

('Delhi NCR', 28.613889)

('Tokyo', 35.689722)

('New York-Newark', 40.808611)

Use namedtuple to define LatLong.

使用namedtuple定义Latlong。

Also define Metropolis.

Build metro_areas list with Metropolis instances; note the nested tuple unpacking to extract (lat, long) and use them o build the LatLong for the coord attribute of Metropolis.

Reach into element metro_areas[0] to get its latitude.

Define an attrgetter to retrieve the name and the coord.lat nested attribute.

Use attrgetter again to sort list of cities by latitude.

Use the attrgetter defined in <5> to show only city name and latitude.

Here is a partial list of functions defined in operator (names starting with _ are omitted, as they are mostly implementation details):

>>> [name for name in dir(operator) if not name.startswith('_')]

['abs', 'add', 'and_', 'attrgetter', 'concat', 'contains',

'countOf', 'delitem', 'eq', 'floordiv', 'ge', 'getitem', 'gt',

'iadd', 'iand', 'iconcat', 'ifloordiv', 'ilshift', 'imod', 'imul',

'index', 'indexOf', 'inv', 'invert', 'ior', 'ipow', 'irshift',

'is_', 'is_not', 'isub', 'itemgetter', 'itruediv', 'ixor', 'le',

'length_hint', 'lshift', 'lt', 'methodcaller', 'mod', 'mul', 'ne',

'neg', 'not_', 'or_', 'pos', 'pow', 'rshift', 'setitem', 'sub',

'truediv', 'truth', 'xor']

Most of the 52 names above are self-evident. The group of names prefixed with i and the name of another operator—e.g. iadd, iand etc.—correspond to the augmented assignment operators—e.g. +=, &= etc. These change their first argument in-place, if it is mutable; if not, the function works like the one without the i prefix: it simply returns the result of the operation.

上面52个名称的大都数都是不解自明的。

Of the remaining operator functions, methodcaller is the last we will cover. It is somewhat similar to attrgetter and itemgetter in that it creates a function on-the-fly. The function it creates calls a method by name on the object given as argument.

Example 5-25. Demo of methodcaller: second test shows the binding of extra arguments.

>>> from operator import methodcaller

>>> s = 'The time has come'

>>> upcase = methodcaller('upper')

>>> upcase(s)

'THE TIME HAS COME'

>>> hiphenate = methodcaller('replace', ' ', '-')

>>> hiphenate(s)

'The-time-has-come'

The first test in Example 5-25 is there just to show methodcaller at work, but if you need to use the str.upper as a function you can just call it on the str class and pass a string as argument, like this:

>>> str.upper(s)

'THE TIME HAS COME'

The second test in Example 5-25 shows that methodcaller can also do a partial application to freeze some arguments, like the functools.partial function does. That is our next subject.

Freezing arguments with functools.partial 使用functools.partial冻结参数

The functools module brings together a handful of higher-order functions. The best known of them is probably reduce, which was covered in Modern replacements for map, filter and reduce. Of the remaining functions in functools the most useful is partial and its variation, partialmethod.

functools模块带来很多的好用的高阶函数。其中最常见可能就是reduce,

The functools.partial is a higher-order function that allows partial application of a function. Given a function, a partial application produces a new callable with some of the arguments of the original function fixed. This is useful to adapt a function that takes one or more arguments to an API that requires a callback with less arguments. Example 5-26 is a trivial demonstration.

functools.partial是一个能够分离函数应用的高阶函数。给出一个函数,

Example 5-26. Using partial to use a 2-argument function where a 1-argument callable is required.

例子5-26.使用partial以便使用包含2个参数的函数,其中一个函数要求为可调用对象。

>>> from operator import mul

>>> from functools import partial

>>> triple = partial(mul, 3)

>>> triple(7)

21

>>> list(map(triple, range(1, 10)))

[3, 6, 9, 12, 15, 18, 21, 24, 27]

Create new triple function from mul, binding first positional argument to 3.

从mul创建新的triple函数,并把第一个位置参数赋值为3.

Test it.

Use triple with map; mul would not work with map in this example.

对map使用triple;在这个离中,mul

A more useful example involves the unicode.normalize function that we saw in Normalizing Unicode for saner comparisons. If you work with text from many languages, you may want to apply unicode.normalize('NFC', s) to any string s before comparing or storing it. If you do that often, its handy to have an nfc function to do that, as in Example 5-27.

Example 5-27. Building a convenient Unicode normalizing function with partial.

>>> import unicodedata, functools

>>> nfc = functools.partial(unicodedata.normalize, 'NFC')

>>> s1 = 'café'

>>> s2 = 'cafe\u0301'

>>> s1, s2

('café', 'café')

>>> s1 == s2

False

>>> nfc(s1) == nfc(s2)

True

partial takes a callable as first argument, followed by an arbitrary number of positional and keyword arguments to bind.

partial把可调用对象作为一个参数,其后紧跟需要绑定的任意数字的位置和关键字参数。

Example 5-28 shows the use of partial with the tag function from Example 5-10, to freeze one positional argument and one keyword argument.

例子5-28展示了对来自例子5-10中的tag函数的partial运用,以便冻结一个位置参数和一个关键字参数。

Example 5-28. Demo of partial applied to the function tag from Example 5-10.

例子5-28. 对来自5-10中的tag函数应用partial的示例。

>>> from tagger import tag

>>> tag

>>> from functools import partial

>>> picture = partial(tag, 'img', cls='pic-frame')

>>> picture(src='wumpus.jpeg')

'wumpus.jpeg'

>>> picture

functools.partial(, 'img', cls='pic-frame')

>>> picture.func

function tag at 0x10206d1e0>

>>> picture.args

('img',)

>>> picture.keywords

{'cls': 'pic-frame'}

Import tag from Example 5-10 and show its id.

从例子5-10导入tag并显示其ID。

Create picture function from tag by fixing the first positional argument with 'img' and the cls keyword argument with 'pic-frame'.

通过使用‘img’修复第一个位置参数,

picture works as expected.

pitture如预期一样正常工作。

partial() returns a functools.partial object[45].

partial()返回一个functools.partial对象。

A functools.partial object has attributes providing access to the original function and the fixed arguments.

functools.partial对象拥有能够访问原始函数和固定参数到属性。

The functools.partial method function (new in Python 3.4) does the same job as partial, but is designed to work with methods.

functools.partial方法函数和partial做的是同样的事情,但是

An impressive functools function is lru_cache, which does memoization—a form of automatic optimization that works by storing the results of function calls to avoid expensive recalculations. We will cover it in Chapter 7, where decorators are explained, along with other higher-order functions designed to be used as decorators: singledispatch and wraps.

令人影响深刻的functools一个函数就是lru_cache,

Chapter summary

本章总结

The goal of this chapter was to explore the first-class nature of functions in Python. The main ideas are that you can assign functions to variables, pass them to other functions, store them in data structures and access function attributes, allowing frameworks and tools to act on that information. Higher-order functions, a staple of functional programming, are common in Python—even if the use of map, filter and reduce is not as frequent as it was—thanks to list comprehensions (and similar constructs like generator expressions) and the appearance of reducing built-ins like sum, all and any. The sorted, min, max built-ins, and functools.partial are examples of commonly used higher-order functions in the language.

本章的目标为探究Python中函数的一级特性。主要思想在于你可以把函数赋值给变量,再把它们传递其他函数,

Callables come in seven different flavors in Python, from the simple functions created with lambda to instances of classes implementing __call__. They can all be detected by the callable() built-in. Every callable supports the same rich syntax for declaring formal parameters, including keyword-only parameters and annotations—both new features introduced with Python 3.

可调用以多个不同的风格出现在Python中,从使用lambda创建的简单函数到

Python functions and their annotations have a rich set of attributes that can be read with the help of the inspect module, which includes the Signature.bind method to apply the flexible rules that Python uses to bind actual arguments to declared parameters.

Python函数和它们的注记拥有一组丰富的有助于检查模块的属性,

Lastly, we covered some functions from the operator module and functools.partial, which facilitate functional programming by minimizing the need for the functionally-challenged lambda syntax.

最后,我们学习了一些来自运算符模块以及能够让functools.partial的函数,

Further reading 深入阅读

The next two chapters continue our exploration of programming with function objects. Chapter 6 shows how first-class functions can simplify some classic Object Oriented design patterns, while Chapter 7 dives into function decorators—a special kind of higher-order function—and the closure mechanism that makes them work.

接下来的两章对于函数对象继续我们的编程探索。第六章展示了如何将一级函数简化为一些经典的面向对象设计模式,而第七章则会深入函数装饰器——一种特殊的高阶函数——以及是装饰器生效的闭包机制。

Chapter 7 of the Python Cookbook, 3rd. edition (O’Reilly, 2013), by David Beazley and Brian K. Jones, is an excellent complement to the current chapter as well as Chapter 7 of this book, covering mostly the same concepts with a different approach.

In the Python Language Reference, chapter “3. Data model”, section 3.2 The standard type hierarchy presents the seven callable types, along with all the other built-in types.

在Python 语言手册中,第三张“数据模型”中的章节3.2.

The Python-3-only features discussed in this chapter have their own PEPs: PEP 3102—Keyword-Only Arguments and PEP 3107—Function Annotations.

For more about the current (as of mid-2014) use of annotations, two Stack Overflow questions are worth reading: What are good uses for Python3’s “Function Annotations”, has a practical answer and insightful comments by Raymond Hettinger, and the answer for What good are Python function annotations? quotes extensively from Guido van Rossum.

PEP 362—Function Signature Object is worth reading if you intend to use the inspect module which implements that feature.

如果你打算使用

A great introduction to functional programming in Python is A. M. Kuchling’s Python Functional Programming HOWTO. The main focus of that text, however, is on the use of iterators and generators, which are the subject of Chapter 14.

在Python中

fn.py is a package to support Functional programming in Python 2 and 3. According to its author, Alexey Kachayev, fn.py provides “implementation of missing features to enjoy FP” in Python. It includes a @recur.tco decorator that implements tail-call optimization for unlimited recursion in Python, among many other functions, data structures and recipes.

fn.py是一个支持在Python 2和3中进行函数的。

The StackOverflow question Python: Why is functools.partial necessary? has a highly informative (and funny) reply by Alex Martelli, author of the classic Python in a Nutshell.

StackOverflow上的Python问题:为什么functools.partial必须使用?

Jim Fulton’s Bobo was probably the first Web framework that deserved to be called Object Oriented. If you were intrigued by it and want to learn more about its modern rewrite, start at its Introduction. A little of the early history of Bobo appears in a comment by Phillip J. Eby in a discussion at Joel Spolsky’s blog.

SOAPBOX 演讲台

About Bobo

I owe my Python career to Bobo. I used it in my first Python Web project, in 1998. I discovered Bobo while looking for an object-oriented way to code web applications, after trying Perl and Java alternatives.

关于Bobo

我的Python职业生涯始于Bobo。在1998年,我把它用在我的第一个Python Web项目上。我是在尝试了Perl和Java之外的选择之后,寻找一种面向对象的方法来编写web应用时发现的Bobo。

In 1997 Bobo had pioneered the object publishing concept: direct mapping from URLs to a hierarchy of objects, with no need to configure routes. I was hooked when I saw the beauty of this. Bobo also featured automatic HTTP query handling based on analysis of the signatures of the methods or functions used to handle requests.

在1997年Bobo拥有一个先进的对象发布理念:直接从URL映射到各级对象,而不用配置路由。在我见识到了它的美妙,便被吸引住了。

Bobo was created by Jim Fulton, known as “The Zope Pope” thanks to his leading role in the development of the Zope framework, the foundation of the Plone CMS, SchoolTool, ERP5 and other large-scale Python projects. Jim is also the creator of ZODB—the Zope Object Database—a transactional object database that provides ACID (atomicity, consistency, isolation and durability), designed for ease of use from Python.

Jim has since rewritten Bobo from scratch to support WSGI and modern Python (including Python 3). As of this writing, Bobo uses the six library to do the function introspection, in order to be compatible with Python 2 and Python 3 in spite of the changes in function objects and related APIs.

Is Python a functional language? Python是函数化语言吗?

Around the year 2000 I was at a training in the US when Guido van Rossum dropped by the classroom (he was not the instructor). In the Q&A that followed, somebody asked him which features of Python were borrowed from other languages. His answer: “Everything that is good in Python was stolen from other languages.”

Shriram Krishnamurthi, professor of Computer Science at Brown University, starts his Teaching Programming Languages in a Post-Linnaean Age paper with this:

Programming language “paradigms” are a moribund and tedious legacy of a bygone age. Modern language designers pay them no respect, so why do our courses slavishly adhere to them?

In that paper, Python is mentioned by name in this passage:

What else to make of a language like Python, Ruby, or Perl? Their designers have no patience for the niceties of these Linnaean hierarchies; they borrow features as they wish, creating melanges that utterly defy characterization.

Krishnamurthi submits that instead of trying to classify languages in some taxonomy, it’s more useful to consider them as aggregations of features.

Even if it was not Guido’s goal, endowing Python with first-class functions opened the door to functional programming. In his post Origins of Python’s “Functional” Features, he tells that map, filter and reduce were the motivation for adding lambda to Python in the first place. All of these features were contributed together by Amrit Prem for Python 1.0 in 1994 (according to Misc/HISTORY in the CPython source code).

lambda, map, filter and reduce first appeared in Lisp, the original functional language. However, Lisp does not limit what can be done inside a lambda, because everything in Lisp is an expression. Python uses a statement-oriented syntax in which expressions cannot contain statements, and many language constructs are statements—including try/catch, which is what I miss most often when writing lambdas. This is the price to pay for Python’s highly readable syntax[46]. Lisp has many strengths, but readability is not one of them.

Ironically, stealing the list comprehension syntax from another functional language—Haskell—significantly diminished the need for map and filter, and also for lambda.

Besides the limited anonymous function syntax, the biggest obstacle to wider adoption of functional programming idioms in Python is the lack of tail-recursion elimination, an optimization that allows memory-efficient computation of a function that makes a recursive call at the “tail” of its body. In another blog post, Tail Recursion Elimination Guido gives several reasons why such optimization is not a good fit for Python. That post is a great read for the technical arguments, but even more so because the first three and most important reasons given are usability issues. It is no accident that Python is a pleasure to use, learn and teach. Guido made it so.

So there you have it: Python is, by design, not a functional language—whatever that means. Python just borrows a few good ideas from functional languages.

The problem with anonymous functions 使用匿名函数带来的问题

Beyond the Python-specific syntax constraints, anonymous functions have a serious drawback in every language: they have no name.

撇开Python特定的语法限制,在每一个语言中匿名函数都带来一个严重的缺陷:它们没有名字。

I am only half joking here. Stack traces are easier to read when functions have names. Anonymous functions are a handy shortcut, people have fun coding with them, but sometimes they get carried away—especially if the language and environment encourage deep nesting of anonymous functions, like JavaScript on Node.js. Lots of nested anonymous functions make debugging and error handling hard. Asynchronous programming in Python is more structured, perhaps because the limited lambda demands it. I promise to write more about asynchronous programming in the future, but this subject must be deferred to Chapter 18. By the way, promises, futures and deferreds are concepts used in modern asynchronous APIs. Along with coroutines, they provide an escape from the so called “callback hell”. We’ll see how callback-free asynchronous programming works in From callbacks to futures and coroutines.

这里我是半开玩笑的。当函数拥有名称时栈追踪会更容易。

[43] Origins of Python’s “Functional” Features, in Guido’s Python History blog

[44] Usually calling a class creates an instance of the same class, but other behaviors are possible by overriding __new__. We’ll see an example of this in Flexible object creation with __new__

[45] The source code for functools.py reveals that the functools.partial class is implemented in C and is used by default. If that is not available, a pure-Python implementation of partial is available since Python 3.4.in the functools module.

[46] There also the problem of lost indentation when pasting code to Web forums, but I digress.

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值