Python Tricks - Effective Functions(0)

Python’s Functions Are First-Class

Python’s functions are first-class objects. You can assign them to variables,store them in data structures, pass them as arguments to otherfunctions, and even return them as values from other functions.

我们可以讲python函数赋值给一个变量,可以存储在数据结构中,可以将函数当做参数输入到其他函数中,甚至可以作为一个值从其他函数中传递出来。

Grokking these concepts intuitively will make understanding advanced features in Python like lambdas and decorators much easier. It also puts you on a path towards functional programming techniques.

grok 通过感觉意会
通过对这些概念的直观的意会会让我们对lambda表达式或者装饰器这些高级的语言特性理解起来更容易。

Over the next few pages I’ll guide you through a number of examples to help you develop this intuitive understanding. The examples will build on top of each other, so you might want to read them in sequence and even to try out some of them in a Python interpreter session as you go along.

在接下来的几页里,我将通过几个例子来帮助你建立这种直观的理解。这些例子是相互累计的,所以你可以按照顺序去进行学习甚至可以实际操作一下。

Wrapping your head around the concepts we’ll be discussing here might take a little longer than you’d expect. Don’t worry—that’s completely normal. I’ve been there. You might feel like you’re banging your head against the wall, and then suddenly things will “click” and fall into place when you’re ready.

这些被讨论的概念可能会比较弯弯绕。不要担心,这个是比较正常的。我一直在这。你可能会感觉到你在拿头撞墙,然后突然间,你就全懂了。

Throughout this chapter I’ll be using this yell function for demonstration purposes. It’s a simple toy example with easily recognizable output:

def yell(text):
  return text.upper() + '!'

>>> yell('hello')
'HELLO!'

Functions Are Objects

All data in a Python program is represented by objects or relations
between objects. Things like strings, lists, modules, and functions
are all objects. There’s nothing particularly special about functions in Python. They’re also just objects.

在python程序里面所有的数据都以对象或者与对象关联的方式而展现出来。字符串,类,函数这些都是对象。python里面的函数没有什么特别的,他们也是对象。

Because the yell function is an object in Python, you can assign it to
another variable, just like any other object:

>>> bark = yell

因为yell函数是一个对象,所以可以被赋予到其他的变量。

This line doesn’t call the function. It takes the function object referenced by yell and creates a second name, bark, that points to it. You could now also execute the same underlying function object by calling bark:

>>> bark('woof')
'WOOF!'

bark = yell并没有调用函数,这里就是创造了一个bark变量也将其指向了yell函数。我们可以通过向bark里面传递参数以调用函数。

Function objects and their names are two separate concerns. Here’s more proof: You can delete the function’s original name (yell). Since another name (bark) still points to the underlying function, you can still call the function through it:

>>> del yell

>>> yell('hello?')
NameError: "name 'yell' is not defined"

>>> bark('hey')
'HEY!'

函数对象和函数的名称是两个不同的分离的概念。你可以删除函数最早的那个名字。因为另外一个名字依然指向到那个函数,你依旧可以通过另外的那个名字调用这个函数。(注:其实这里就像有一个人,他原先可以叫二狗子,现在他又有了一个学名,叫小明。后来又因为二狗子不好听,就不叫二狗子了。但是在不叫二狗子之前,叫他二狗子或者小明他都答应,都知道是他。但是删掉二狗子后,你叫二狗子他就不答应了,只有叫小明才行。所以这也体现了函数名称就是个指向函数内存段的一个标识符。)

By the way, Python attaches a string identifier to every function at
creation time for debugging purposes. You can access this internal
identifier with the name attribute:

>>> bark.__name__
'yell'

我们可以通过var.name的方式访问在函数创造的时候产生的与之相关联的字符串标识符。这样我们也就知道了最原始的时候的函数的名称。

Now, while the function’s name is still “yell,” that doesn’t affect how you can access the function object from your code. The name identifier is merely a debugging aid. A variable pointing to a function and the function itself are really two separate concerns.

一个指向函数的变量名和函数本身是两个分离的概念。

Functions Can Be Stored in Data Structures

Since functions are first-class citizens, you can store them in data structures, just like you can with other objects. For example, you can add functions to a list:

>>> funcs = [bark, str.lower, str.capitalize]
>>> funcs
[,
,
]

因为函数是一等公民,所以你可以将其存储到数据结构中。

Accessing the function objects stored inside the list works like it would with any other type of object:

>>> for f in funcs:
... print(f, f('hey there'))
 'HEY THERE!'
 'hey there'
 'Hey there'

使用在存储在列表中的函数对象和使用其他对象类型是一样的。

You can even call a function object stored in the list without first assigning it to a variable. You can do the lookup and then immediately call the resulting “disembodied” function object within a single expression:

>>> funcs[0]('heyho')
'HEYHO!'

你甚至可以调用一个存储在列表中的函数对象而不用先对其进行变量名的赋予。你可以进行查找然后在单行表达式中直接调用包含于其中的函数对象。

Functions Can Be Passed to Other Functions

Because functions are objects, you can pass them as arguments to other functions. Here’s a greet function that formats a greeting string using the function object passed to it and then prints it:

def greet(func):
  greeting = func('Hi, I am a Python program')
  print(greeting)

因为函数都是对象,你可以将他们当做参数直接传递到其他函数里。这里我们有一个greet函数使用被传递进入的函数对象,并且设置一个greeting的字符串,最后打印出来。

You can influence the resulting greeting by passing in different functions. Here’s what happens if you pass the bark function to greet:

>>> greet(bark)
'HI, I AM A PYTHON PROGRAM!'

上面示例了将bark函数传入到greet函数中。

Of course, you could also define a new function to generate a different flavor of greeting. For example, the following whisper function might work better if you don’t want your Python programs to sound like Optimus Prime:

def whisper(text):
  return text.lower() + '...'

>>> greet(whisper)
'hi, i am a python program...'

这里可以换一个新的函数再导入到greet中。

The ability to pass function objects as arguments to other functions is powerful. It allows you to abstract away and pass around behavior in your programs. In this example, the greet function stays the same but you can influence its output by passing in different greeting behaviors.

greet函数没有变,但是我们可以通过修改导入的函数从而影响greet函数的行为。

Functions that can accept other functions as arguments are also called higher-order functions. They are a necessity for the functional programming style.

可以接受其他函数作为参数的函数我们可以将其称之为高阶函数。高阶函数对于函数式编程来说还是很重要的。

The classical example for higher-order functions in Python is the builtin map function. It takes a function object and an iterable, and then calls the function on each element in the iterable, yielding the results as it goes along.

在python中比较经典的高阶函数就是内置函数map。他需要一个函数对象和一个迭代器然后对迭代器里面的每一个元素调用函数,并且随之产生相应的结果。

Here’s how you might format a sequence of greetings all at once by
mapping the bark function to them:

>>> list(map(bark, ['hello', 'hey', 'hi']))
['HELLO!', 'HEY!', 'HI!']

As you saw, map went through the entire list and applied the bark function to each element. As a result, we now have a new list object with modified greeting strings.

正如我们在上面的例子中所看到的,map会遍历整个列表,然后对每一个列表中的元素应用bark函数,结果就是我们得到了修改后的字符串的新的列表对象。

Functions Can Be Nested

Perhaps surprisingly, Python allows functions to be defined inside other functions. These are often called nested functions or inner functions.Here’s an example:

def speak(text):
  def whisper(t):
    return t.lower() + '...'
  return whisper(text)

>>> speak('Hello, World')
'hello, world...'

令人惊奇的是,python允许函数再其他函数中定义,我们将其称为嵌套函数或者内部函数。

Now, what’s going on here? Every time you call speak, it defines a new inner function whisper and then calls it immediately after. My brain’s starting to itch just a little here but, all in all, that’s still relatively straightforward stuff.

Here’s the kicker though—whisper does not exist outside speak:

>>> whisper('Yo')
NameError:
"name 'whisper' is not defined"

>>> speak.whisper
AttributeError:"'function' object has no attribute 'whisper'"

But what if you really wanted to access that nested whisper function
from outside speak? Well, functions are objects—you can return the
inner function to the caller of the parent function.

如果我们真的想从外部函数speak中去通讯到内部函数whisper该如何办呢?我们可以返回父函数的内部函数。

For example, here’s a function defining two inner functions. Depending on the argument passed to top-level function, it selects and returns one of the inner functions to the caller:

def get_speak_func(volume):
  def whisper(text):
    return text.lower() + '...'
  def yell(text):
    return text.upper() + '!'
  if volume > 0.5:
    return yell
  else:
    return whisper

Notice how get_speak_func doesn’t actually call any of its inner functions—it simply selects the appropriate inner function based on the volume argument and then returns the function object:

>>> get_speak_func(0.3)
.whisper at 0x10ae18>

>>> get_speak_func(0.7)
.yell at 0x1008c8>

get_speak_func函数没有实际调用任何内部函数。它根据volume数值的大小而选择合适的内部函数然后返回函数对象。

Of course, you could then go on and call the returned function, either directly or by assigning it to a variable name first:

>>> speak_func = get_speak_func(0.7)
>>> speak_func('Hello')
'HELLO!'

当然,我们可以调用返回的函数,直接调用或者变量名赋值后再调用。

Let that sink in for a second here… This means not only can functions accept behaviors through arguments but they can also return behaviors. How cool is that?

这意味着函数不仅可以接受参数产生行为而且可以返回行为。这里的行为其实就是定义好的内部函数。

You know what, things are starting to get a little loopy here. I’m going to take a quick coffee break before I continue writing (and I suggest you do the same).

休息,休息一下。(摸摸脑袋)

Functions Can Capture Local State

You just saw how functions can contain inner functions, and that it’s even possible to return these (otherwise hidden) inner functions from the parent function.

函数可以从父函数中返回除了隐藏函数之外的内部函数。 注:隐藏的内部函数 __func

Best put on your seat belt now because it’s going to get a little crazier still—we’re about to enter even deeper functional programming territory. (You had that coffee break, right?)

系好安全带 老司机要飙车了。我们将进入更加深入的函数编程领域。

Not only can functions return other functions, these inner functions can also capture and carry some of the parent function’s state with them. Well, what does that mean?

函数不仅可以返回其他的函数,这些内部函数也可以捕获和携带一些父函数的状态。看这个描述,我们是不看明白的。

I’m going to slightly rewrite the previous get_speak_func example to illustrate this. The new version takes a “volume” and a “text” argument right away to make the returned function immediately callable:

def get_speak_func(text, volume):
  def whisper():
    return text.lower() + '...'
  def yell():
    return text.upper() + '!'
  if volume > 0.5:
    return yell
  else:
    return whisper

>>> get_speak_func('Hello, World', 0.7)()
'HELLO, WORLD!'

Take a good look at the inner functions whisper and yell now. Notice how they no longer have a text parameter? But somehow they can still access the text parameter defined in the parent function. In fact, they seem to capture and “remember” the value of that argument.

现在这get_speak_func函数与之前不同之处在于首先该父函数所接受的参数个数发生了变化,之前的那个只有volume而现在的有volume和text。第二个在于内部函数whisper和yell的参数处没有了text而之前的是有text的。这也就意味着内部函数找到了父函数的输入参数并且记忆了下来。

Functions that do this are called lexical closures (or just closures, for short). A closure remembers the values from its enclosing lexical scope even when the program flow is no longer in that scope.

这样的函数操作我们将其称之为闭包。即使程序流不在该范围内,但是闭包会记住在该范围内的值。

In practical terms, this means not only can functions return behaviors but they can also pre-configure those behaviors. Here’s another barebones example to illustrate this idea:

def make_adder(n):
  def add(x):
    return x + n
  return add

>>> plus_3 = make_adder(3)

>>> plus_5 = make_adder(5)

>>> plus_3(4)
7
>>> plus_5(4)
9

实际上,函数不仅可以返回行为,而且可以提前配置这些行为。就像上面的函数所示,通过提前对于make_adder函数进行配置,我们生产了新的函数plus_3和plus_5.从结果看,将make_adder函数配置完以后我们实际得到的就是

plus_3 = (
  def add(x):
     return x + 3
)

plus_5 = (
  def add(x):
     return x + 5
) 

In this example, make_adder serves as a factory to create and configure “adder” functions. Notice how the “adder” functions can still access the n argument of the make_adder function (the enclosing scope).

make_adder实际上就是创造和配置adder函数的一个工厂(这个工厂表达得挺好)。我们需要注意到是adder函数依然可以通讯到make_adder闭包函数的n参数。

从个人角度看,其实整个来看,函数的闭包更像是将函数的相同部分进行了工厂化

Objects Can Behave Like Functions

While all functions are objects in Python, the reverse isn’t true. Objects aren’t functions. But they can be made callable, which allows you to treat them like functions in many cases.

虽然所有的函数都是对象,但是反过来确不是对的。对象并不是函数。但是使对象可以被调用,从而让我们可以像对象函数一样对待对象。

If an object is callable it means you can use the round parentheses function call syntax on it and even pass in function call arguments. This is all powered by the __call__ dunder method. Here’s an example of class defining a callable object:

class Adder:
  def __init__(self, n):
    self.n = n
  def __call__(self, x):
    return self.n + x

>>> plus_3 = Adder(3)
>>> plus_3(4)
7

如果一个对象是可以被调用的,那么我们就可以使用圆括号函数调用语法,甚至可以向其中传递参数。而这些都是__call__函数支持的。在上面那个例子中其实也是工厂化的思路,但是表达的方式不同。plus_3 = Adder(3)这个就是对对象的初始化,通过对对象的初始化,对象(或adder类)变成了:

class Adder:
  def __call__(self,3):
    return self.3 + x

Behind the scenes, “calling” an object instance as a function attempts to execute the object’s call method.

Of course, not all objects will be callable. That’s why there’s a built in callable function to check whether an object appears to be callable or not:

>>> callable(plus_3)
True
>>> callable(yell)
True
>>> callable('hello')
False

不是所有的对象都是可以调用的。我们可以用callable函数来检查一个对象是否可以被调用。

Key Takeaways
  • Everything in Python is an object, including functions. You can
    assign them to variables, store them in data structures, and pass
    or return them to and from other functions (first-class functions.)
  • First-class functions allow you to abstract away and pass
    around behavior in your programs.
  • Functions can be nested and they can capture and carry some
    of the parent function's state with them. Functions that do this
    are called closures.
  • Objects can be made callable. In many cases this allows you to
    treat them like functions.

你可能感兴趣的:(Python Tricks - Effective Functions(0))