懒惰是一种美德
抽象和结构
- 抽象可节省人力,但实际上还有个更重要的优点:抽象是程序能够被人理解的关键所在。
自定义函数
- 函数执行特定的操作并返回一个值,你可以调用它
- 调用时可能需要提供一些参数——放在圆括号中的内容
- 要判断某个对象是否可调用,可使用内置函数callable
import math
x = 1
y = math.sqrt
callable(x)
# False
callable(y)
# True
给函数编写文档
- 放在函数开头的字符串称为文档字符串(docstring)
def square(x):
'请测试'
return x * x
square.__doc__
- 特殊的内置函数help很有用
- 在交互式解释器中,可使用它获取有关函数的信息,其中包含函数的文档字符串。
其实并不是函数的函数
- 所有的函数都返回值。如果你没有告诉它们该返回什么,将返回None。
参数魔法
- 在def语句中,位于函数名后面的变量通常称为形参,而调用函数时提供的值称为实参.
- 在函数内部给参数赋值对外部没有任何影响。
- 如果参数是可变的数据结构,函数内部修改后可能会影响外部
names = ['Mrs. Entity', 'Mrs. Thing']
n = names[:]
# 现在n和names包含两个相等但不同的列表
关键字参数和默认值
- 关键字参数最大的优点在于,可以指定默认值。
收集参数
def print_params(*params):
print(params)
# 星号将提供的所有值都放在一个元组中
- 与赋值时一样,带星号的参数也可放在其他位置(而不是最后),但不同的是,在这种情况与赋值时一样,带星号的参数也可放在其他位置(而不是最后),但不同的是,在这种情况下你需要做些额外的工作:使用名称来指定后续参数。
- 星号不会收集关键字参数。
- 要收集关键字参数,可使用两个星号。
def print_params_3(**params):
print(params)
print_params_3(x=1, y=2, z=3)
# {'z':3, 'x':1, 'y':2}
分配参数
def add(x, y):
return x + y
params = (1, 2)
add(*params)
# 3
- 使用这些拆分运算符来传递参数很有用,因为这样无需操心参数个数之类的问题
- foo(*args, **kwds) : 这在调用超类的构造函数时特别有用
作用域
- 变量到底是什么呢?可将其视为指向值的名称。
- 有一个名为vars的内置函数,它返回这个不可见的字典
x = 1
scope = vars()
scope['x']
# 1
- 这种“看不见的字典”称为 命名空间或作用域。
- 参数类似于局部变量,因此参数与全局变量同名不会有任何问题。
- 访问全局变量是众多bug的根源。务必慎用全局变量。
- “遮盖”的问题: 读取全局变量的值通常不会有问题,但还是存在出现问题的可能性。
- 如果有一个局部变量或参数与你要访问的全局变量同名,就无法直接访问全局变量,因为它被局部变量遮住了。
- 可使用函数globals来访问全局变量
- 这个函数类似于vars,返回一个包含全局变量的字典。
- locals返回一个包含局部变量的字典。
def combine(parameter):
print(parameter + globals()['parameter'])
parameter = 'berry'
combine('Shrub')
# Shrubberry
x = 1
def chage_global():
global x
x = x + 1
chage_global()
print(x)
# 2
作用域嵌套
- 使用一个函数来创建另一个函数。
def multiplier(factor):
def multiplyByFactor(number):
return number * factor
return multiplyByFactor
- 一个函数位于另一个函数中,且外面的函数返回里面的函数。
- 像multiplyByFactor这样存储其所在作用域的函数称为闭包。
- 使用关键字nonlocal, 让你能够给外部作用域(非全局作用域)内的变量赋值.
递归
- 递归意味着引用(这里是调用)自身。
- 递归函数通常包含下面两部分:基线条件,递归条件
- 基线条件(针对最小的问题):满足这种条件时函数将直接返回一个值。
- 递归条件:包含一个或多个调用,这些调用旨在解决问题的一部分。
- 两个经典案例:阶乘和幂
def factorial(n):
if n == 1:
return 1
else:
return n * factorial(n - 1)
def power(x , n):
if n == 0:
return 1
else:
return x * power(x, n-1)
- 使用循环的效率可能更高。然而,在很多情况下,使用递归的可读性更高,且有时要高得多.
- 另一个经典案例:二分查找
def search(sequence, number, lower=0, upper=None):
if upper is None: upper = len(sequence) - 1
if lower == upper:
assert number == sequence[upper]
return upper
else:
middle = (lower + upper) // 2
if number > sequence[middle]:
return search(sequence, number, middle + 1, upper)
else:
return search(sequence, number, lower, middle)
函数式编程
- map: 将序列的所有元素传递给函数。
list(map(str, range(10)))
# 与[str(i) for i in range(10)]等价
# ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']
- filter: 根据布尔函数的返回值来对元素进行过滤。
def func(x):
return x.isalnum()
seq = ["foo", "x41", "?!", "***"]
list(filter(func, seq))
# ['foo', 'x41']
- Python提供了一种名为lambda表达式的功能,让你能够创建内嵌的简单函数
seq = ["foo", "x41", "?!", "***"]
filter(lambda x: x.isalum(), seq)
- reduce: 它使用指定的函数将序列的前两个元素合二为一,再将结果与第3个元素合二为一,依此类推,直到处理完整个序列并得到一个结果。
numbers = [72, 101, 108, 108, 111, 44, 32, 119, 111, 114, 108, 100, 33]
from functools import reduce
reduce(lambda x, y: x + y, numbers)
# 1161
- 与使用自定义函数相比,使用模块operator中的函数总是效率更高。