-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy path3.functions.md
More file actions
330 lines (242 loc) · 11.6 KB
/
3.functions.md
File metadata and controls
330 lines (242 loc) · 11.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
Python 中的函数
---
### 概述
- 函数的定义
- 变量作用域
- 文档
- Lambda 表达式
- 迭代器和生成器
---
### 定义函数
- 默认参数: 可以向函数中添加默认参数,以便为在函数调用中未指定的参数提供默认值
```Python
# 如果调用 cylinder_volume 函数时,不提供radius参数,那么radius的值为5
def cylinder_volume(height, radius=5):
pi = 3.14159
return height * pi * radius ** 2
```
- 向函数中的参数传值的方法:按照位置和按照名称
```Python
cylinder_volume(10, 7) # 1539.3791
cylinder_volume(height=10, radius=7) # 1539.3791
cylinder_volume(radius=7, height=10) # 1539.3791
```
注意:上述第一种是常用的按照位置传值,第二种和第三种是按照名称传值
### 定义函数[相关练习]
- 写一个名称为 population_density 的函数,该函数有两个参数 population 和 land_area,并根据这两个值返回人口密度。
解决方案:
```Python
def population_density(population, land_area):
return population / land_area
test1 = population_density(10, 1)
expected_result1 = 10
print("expected result: {}, actual result: {}".format(expected_result1, test1)) # expected result: 10, actual result: 10.0
test2 = population_density(864816, 121.4)
expected_result2 = 7123.6902801
print("expected result: {}, actual result: {}".format(expected_result2, test2)) # expected result: 7123.6902801, actual result: 7123.690280065897
```
- 写一个叫做 readable_timedelta 的函数,该函数有一个参数:整数 days,并返回一个表示由多少周多少天组成的字符串。例如 readable_timedelta(10) 应返回“1 week(s) and 3 day(s).”。
解决方案:
```Python
def readable_timedelta(days):
weeks = int(days / 7)
day = days % 7
return str(weeks) + ' week(s) and ' + str(day) + ' day(s).'
print(readable_timedelta(10)) # 1 week(s) and 3 day(s).
```
### 函数中的变量作用域
- 变量作用域是指可以在程序的哪个部分引用或使用某个变量。
- 在函数中使用变量时,务必要考虑作用域。如果变量是在函数内创建的,则只能在该函数内使用该变量。你无法从该函数外面访问该变量。
错误的示例:
```Python
# This will result in an error
def some_function():
word = "hello"
print(word)
```
这意味着你可以为在不同函数内使用的不同变量使用相同的名称, 正确的示例如下:
```Python
def some_function():
word = "hello"
def another_function():
word = "goodbye"
```
- 在函数之外定义的变量依然可以在函数内访问。
```Python
word = "hello"
def some_function():
print(word)
print(word)
```
- Best Practise:建议将变量定义在所需的最小作用域内。虽然函数可以引用在更大的作用域内定义的变量,但是通常不建议这么做,因为如果程序有很多变量,你可能不知道你定义了什么变量。
- 注意:Python 不允许函数修改不在函数作用域内的变量,执行下列代码,看发生了什么
```Python
egg_count = 0
def buy_eggs():
egg_count += 12 # purchase a dozen eggs
buy_eggs()
```
此时会发生错误, 导致 `UnboundLocalError` : 当我们尝试将函数外的一个变量的值更改或重新赋值为另一个值时,我们将遇到这个错误, 但是这个原则仅适用于整数和字符串, `列表、字典、集合、类`中可以在子程序中(子函数)通过修改局部变量达到修改全局变量的目的。
### 文档 docstrings
- 文档字符串是一种注释,用于解释函数的作用以及使用方式,文档字符串用三个引号引起来:
```python
def population_density(population, land_area):
"""Calculate the population density of an area. """
return population / land_area
```
- 一行文档字符串完全可接受, 可以在一行摘要后面添加更多信息。我们还可以对函数的参数进行了解释,描述每个参数的作用和类型。我们经常还会对函数输出进行说明:
```python
def population_density(population, land_area):
"""Calculate the population density of an area.
INPUT:
population: int. The population of that area
land_area: int or float. This function is unit-agnostic, if you pass in values in terms
of square km or square miles the function will return a density in those units.
OUTPUT:
population_density: population / land_area. The population density of a particular area.
"""
return population / land_area
```
### Lambda 表达式
- 使用 Lambda 表达式创建匿名函数,即没有名称的函数。lambda 表达式非常适合快速创建在代码中以后不会用到的函数。
麻烦的写法:
```python
def multiply(x, y):
return x * y
```
使用Lambda之后:
```python
double = lambda x, y: x * y
```
- Lambda 函数的组成部分:
* 关键字 `lambda` 表示这是一个 lambda 表达式。
* `lambda` 之后是该匿名函数的一个或多个参数(用英文逗号分隔),然后是一个英文冒号 `:`。和函数相似,lambda 表达式中的参数名称是随意的。
* 最后一部分是被评估并在该函数中返回的表达式,和你可能会在函数中看到的 `return` 语句很像。
* 备注:鉴于这种结构,lambda 表达式不太适合复杂的函数,但是非常适合简短的函数。
### Lambda 表达式[相关练习]
- `map()` 是一个高阶内置函数,接受函数和可迭代对象作为输入,并返回一个将该函数应用到可迭代对象的每个元素的迭代器。下面的代码使用 `map()` 计算 `numbers` 中每个列表的均值,并创建列表 `averages`, 通过将 `mean` 函数替换为在 `map()` 的调用中定义的 `lambda` 表达式,重写这段代码,使代码更简练。
```python
numbers = [
[34, 63, 88, 71, 29],
[90, 78, 51, 27, 45],
[63, 37, 85, 46, 22],
[51, 22, 34, 11, 18]
]
def mean(num_list):
return sum(num_list) / len(num_list)
averages = list(map(mean, numbers))
print(averages)
```
使用lambda重写后:
```python
numbers = [
[34, 63, 88, 71, 29],
[90, 78, 51, 27, 45],
[63, 37, 85, 46, 22],
[51, 22, 34, 11, 18]
]
averages = list(map(lambda x: sum(x) / len(x), numbers))
print(averages) # [57.0, 58.2, 50.6, 27.2]
```
- `filter()` 是一个高阶内置函数,接受函数和可迭代对象作为输入,并返回一个由可迭代对象中的特定元素(该函数针对该元素会返回 True)组成的迭代器。下面的代码使用 `filter()` 从 cities 中获取长度少于 10 个字符的名称以创建列表 short_cities。通过将 `is_short` 函数替换为在 `filter()` 的调用中定义的 `lambda` 表达式,重写这段代码,使代码更简练。
```python
cities = ["New York City", "Los Angeles", "Chicago", "Mountain View", "Denver", "Boston"]
def is_short(name):
return len(name) < 10
short_cities = list(filter(is_short, cities))
print(short_cities)
```
使用lambda重写后:
```python
cities = ["New York City", "Los Angeles", "Chicago", "Mountain View", "Denver", "Boston"]
short_cities = list(filter(lambda n: len(n) < 10, cities))
print(short_cities) # ['Chicago', 'Denver', 'Boston']
```
### 迭代器和生成器
- 迭代器
* 每次可以返回一个对象元素的对象,例如返回一个列表。我们到目前为止使用的很多内置函数(例如 enumerate)都会返回一个迭代器。
* 是一种表示数据流的对象。这与列表不同,列表是可迭代对象,但不是迭代器,因为它不是数据流。
- 生成器
* 是使用函数创建迭代器的简单方式。也可以使用类定义迭代器
* 下面是一个叫做 my_range 的生成器函数,它会生成一个从 0 到 (x - 1) 的数字流:
```python
def my_range(x):
i = 0
while i < x:
yield i
i += 1
```
* 该函数使用了 yield 而不是关键字 return。这样使函数能够一次返回一个值,并且每次被调用时都从停下的位置继续。关键字 yield 是将生成器与普通函数区分开来的依据。
* 因为上述代码会返回一个迭代器,因此我们可以将其转换为列表或用 `for` 循环遍历它,以查看其内容。例如,下面的代码:
```python
for x in my_range(5):
print(x)
```
* 输出如下:
```log
0
1
2
3
4
```
- 为何要使用生成器?
* 生成器是构建迭代器的 “懒惰” 方式。当内存不够存储完整实现的列表时,或者计算每个列表元素的代价很高,你希望尽量推迟计算时,就可以使用生成器。但是这些元素只能遍历一次。
* 由于使用生成器是一次处理一个数据,在内存和存储的需求上会比使用list方式直接全部生成再存储节省很多资源。由此区别,在处理大量数据时,经常使用生成器初步处理数据后,再进行长期存储,而不是使用 list。
* 因为无论使用生成器还是 `list`,都是使用过就要丢弃的临时数据。既然功能和结果一样,那就不如用生成器。
* 但是生成器也有自己的局限,它产生的数据不能回溯,不像list可以任意选择。
### 迭代器和生成器[相关练习]
- 请自己写一个效果和内置函数 `enumerate` 一样的生成器函数。如下所示地调用该函数:
```python
lessons = ["Why Python Programming", "Data Types and Operators", "Control Flow", "Functions", "Scripting"]
for i, lesson in my_enumerate(lessons, 1):
print("Lesson {}: {}".format(i, lesson))
```
应该会输出:<br>
```log
Lesson 1: Why Python Programming
Lesson 2: Data Types and Operators
Lesson 3: Control Flow
Lesson 4: Functions
Lesson 5: Scripting
```
解决方案:
```python
lessons = ["Why Python Programming", "Data Types and Operators", "Control Flow", "Functions", "Scripting"]
def my_enumerate(iterable, start=0):
# Implement your generator function here
i = start
for element in iterable:
yield i, element
i += 1
for i, lesson in my_enumerate(lessons, 1):
print("Lesson {}: {}".format(i, lesson))
```
- 如果可迭代对象太大,无法完整地存储在内存中(例如处理大型文件时),每次能够使用一部分很有用。实现一个生成器函数 `chunker`,接受一个可迭代对象并每次生成指定大小的部分数据。如下所示地调用该函数:
```python
for chunk in chunker(range(25), 4):
print(list(chunk))
```
应该会输出:
```python
[0, 1, 2, 3]
[4, 5, 6, 7]
[8, 9, 10, 11]
[12, 13, 14, 15]
[16, 17, 18, 19]
[20, 21, 22, 23]
[24]
```
解决方案:
```python
def chunker(iterable, size):
for i in range(0, len(iterable), size):
yield iterable[i:i + size]
for chunk in chunker(range(25), 4):
print(list(chunk))
```
### 学习链接
- https://www.python.org/dev/peps/pep-0257/
- https://docs.python.org/3/tutorial/classes.html#iterators
- https://softwareengineering.stackexchange.com/questions/290231/when-should-i-use-a-generator-and-when-a-list-in-python/290235
- https://stackoverflow.com/questions/312443/how-do-you-split-a-list-into-evenly-sized-chunks