函数是一种重复代码的抽象方式,Python 内建支持的一种封装;
调用一个函数,需要知道函数的名称和参数;函数名是只想一个函数对象的引用
>>> a = abs
>>> a(-1)
1
可以在交互式命令行通过 help(abs) 查看 abs 函数的帮助信息;
数据类型转换
>>> int('123')
123
>>> int(12.34)
12
>>> float('12.34')
12.34
>>> str(1.23)
'1.23'
>>> str(100)
'100'
>>> bool(1)
True
>>> bool('')
False
开平方
# 方法一
>>> import math
>>> math.sqrt(100)
10.0
# 方法二
>>> pow(100, 0.5)
10.0
# 方法三
>>> 100 ** 0.5
10.0
定义一个函数要使用 def 语句,依次写出函数名、括号、括号中的参数和冒号:
,然后,在缩进块中编写函数体,函数的返回值用 return 语句返回。
如果没有 return 语句,函数执行完毕后也会返回结果,只是结果为 None。return None 可以简写为 return。
空函数
定义一个什么事也不做的空函数,可以用 pass
来作为占位符
参数检查
调用函数时,如果参数个数不对,Python 解释器会自动检查出来,并抛出 TypeError
返回多个值
Python 的函数返回多值其实就是返回一个 tuple;在语法上,返回一个 tuple 可以省略括号,而多个变量可以同时接收一个 tuple,按位置赋给对应的值
参数类型 | 说明 |
---|---|
位置参数 | def power(x, n): 传入的值依次赋给对应位置的参数 |
默认参数 | def power(x, n=2): 在调用时可以不用输入该位置的参数,而直接使用默认值;变化大的参数放在前,变化小的放在后作为默认参数,降低调用难度;调用含多个默认参数的函数时,可以写上参数名;如调用 def enroll(name, gender, age=6, city='Beiging'): 可以用enroll('Adam', 'M', city='Tianjin') |
可变参数 | def calc(*numbers): 传入的参数个数时可变的,在参数前面加 1 个 * 号,参数接收到的将是一个 tuple ;在 tuple /list 前加 1 个 * ,可以将其以可变参数传入函数 calc(*[1,2,3]) |
关键字参数 | def person(name, age, **kw): 同理可变参数,在参数前面加 2 个_ 号,允许传入 0 个或任意个含参数名的参数,这些关键字参数在函数内部自动组装为一个dict ;在 dict 前加 2 个_ 可以将其以关键字参数传入函数 person(name, age, **kw) |
命名关键字参数 | def person(name, age, *, city, job): 如果要限制关键字参数的名字,可以用命名关键字参数;命名参数需要以 * 分隔,其后视为命名关键字参数,如果函数定义了一个可变参数,可以不要 * ,命名关键字参数可以给默认值 |
默认参数必须只想不可变对象
不可变对象减少了由于修改数据导致的错误,多任务环境同时读取不需加锁
# 错误写法
>>> def add_end(L=[]):
... L.append('End')
... return L
...
>>> add_end()
['End']
>>> add_end()
['End', 'End']
参数组合
顺序:必选参数 > 默认参数 > 可变参数 > 命名关键字参数 > 关键字参数
一个函数在内部调用自己本身,就叫递归函数
函数调用是通过栈实现的,每进入一个函数调用,加一层栈锁,过多时会栈溢出
尾递归
把每一步的结果传递给递归函数,和循环的效果一样,栈不会增加;python 解释器没有对尾递归做优化,会栈溢出
汉诺塔
def move(n, a, b, c):
if n == 1:
print(a, '->', c)
else:
move(n-1, a, c, b)
move(1, a, b, c)
move(n-1, b, a, c)
Higher-order function
变量可以指向函数 and
函数名也是变量 ->
函数可以接收另一个函数作为参数
其参数能够接收别的函数的函数,就是高阶函数
map()
接收两个参数,一个是函数(单个参数),一个是 Iterable 对象,map 将传入的函数依次作用在 Iterable 对象的每一个元素上,并把结果作为一个新的 Iterator 返回,注意 Iterator 是惰性的
>>> list(map(str, [1, 2, 3, 4, 5]))
['1', '2', '3', '4', '5']
reduce()
把一个函数(必须是两个参数)作用在一个序列上,reduce 把结果与下个元素做累计计算
reuce(f, [x1, x2, x3, x4]) = f(f(f(x1, x2), x3), x4)
# str2int
from functools import reduce
DIGITS = {
'0': 0,
'1': 1,
'2': 2,
'3': 3,
'4': 4,
'5': 5,
'6': 6,
'7': 7,
'8': 8,
'9': 9
}
def char2num(c):
return DIGITS[c]
def str2int(s):
return reduce(lambda x1, x2: x1 * 10 + x2, map(char2num, s))
# str2float
from functools import reduce
DIGITS = {
'0': 0,
'1': 1,
'2': 2,
'3': 3,
'4': 4,
'5': 5,
'6': 6,
'7': 7,
'8': 8,
'9': 9
}
def char2num(c):
return DIGITS[c]
def str2int(s):
return reduce(lambda x1, x2: x1 * 10 + x2,
map(char2num, [c for c in s if c != '.']))
def str2float(s):
return str2int(s) / (10**s[::-1].index('.'))
# str2float
def str2float(s):
point = 0
def to_float(i, c):
nonlocal point
if not isinstance(c, int):
point = 1
return i
if point == 0:
return i * 10 + c
else:
point *= 10
return i + c / point
return reduce(to_float, map(char2num, s))
nonlocal
关键字用来在函数或其他作用域使用外层(非全局)变量
接收一个函数(单个参数)和一个序列,序列的每个元素作用于函数,返回 True/False 决定是否保留该元素
素数 - 埃氏筛选
def _odd_iter():
n = 1
while True:
n += 2
yield n
def _not_divisible(n):
return lambda x: x % n > 0
def primes():
yield 2
oi = _odd_iter()
while True:
n = next(oi)
yield n
oi = filter(_not_divisible(n), oi)
接收一个 Iterable 对象,一个函数(形参:key,一个参数的函数),以及 reverse;key 作用与序列的每个元素,再对结果排序,reverse 表示是否反向排序,默认为 False
面向过程程序设计
通过一层一层的函数调用,把复杂的任务分解成简单任务,这种分解称之为面向过程的程序设计,函数
是面向过程编程的基本单元
Functional Programming
一种抽象程度很高的编程范式,纯粹的函数式编程语言(Lisp)编写的函数没有变量,只要输入确定,输出就是确定的(没有副作用)。允许使用变量的函数内部变量状态不确定,同样输入可能输出不同
Python 对函数式编程提供部分支持,其允许使用变量,不是纯函数式编程语言
函数作为返回值
往往不需要立即执行的时候,可以使用返回函数的方式达到惰性计算的效果(lazy
)
闭包(Closure)
当返回函数时,相关参数和变量都保存在返回的函数中,这种程序结构成为闭包
def count():
fs = []
for i in range(1, 4):
def f():
return i*i
fs.append(f)
return fs
f1, f2, f3 = count()
>>> f1()
9
>>> f2()
9
>>> f3()
9
返回的函数引用了变量 i,但它并没有立刻执行,等到 3 个函数都返回时,它们引用的变量 i 已经变成了 3,因此最终结果都是 9
返回闭包时,返回函数不要引用任何循环变量,或者后续会发生变化的变量
def count():
def f(j):
def g():
return j*j
return g
fs = []
for i in range(1, 4):
# f(i) 立即执行,因此 i 的当前值被传入 f()
fs.append(f(i))
return fs
>>> f1, f2, f3 = count()
>>> f1()
1
>>> f2()
4
>>> f3()
9
添加多层函数,用执行外层函数将循环变量的值绑定到函数的参数中,可以绑定循环变量变化过程中的值
计数器(闭包)
def createCounter():
def counter():
n = 0
while True:
n += 1
yield n
c = counter()
return lambda : next(c)
def createCounter():
n = 0
def counter():
nonlocal n
n += 1
return n
return counter
关键字 lambda
表示匿名函数,冒号前面的是参数表,冒号后面的是返回结果,不用写 return,只能有一个表达式
匿名函数也是一个函数对象,也可以把匿名函数赋值给一个变量,再利用变量来调用该函数
>>> f = lambda x: x*x
>>> f(5)
25
也可以把匿名函数当作一个函数的返回值返回
def build(x, y):
return lambda x, y: x*x + y*y
Decorator
在函数调用前后自动增加处理,不修改函数的定义,这种在代码运行期间动态增加功能的方式,即为 Decorator
import functools
def log(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
print(f'call {func.__name__}():')
return func(*args, **kwargs)
return wrapper
def log(info):
def decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
print(f'{info} {func.__name__}')
return func(*args, **kwargs)
return wrapper
return decorator
调用 Decorator
@log
def now():
print('2020-11-12')
now = log(now)
@log('execute')
def now():
print('2020-11-12')
now = log('execute')(now)
functools.wraps(func)
的作用是将 wrapper
函数的 __name__
改为被装饰函数对象的 __name__
, 相当于:
wrapper.__name__ = func.__name__
Decorator
即在 面向对象(OOP)
的 设计模式
中的 装饰模式
,OOP 的装饰模式通过类的继承和组合实现,而 Python 可以直接从语法层面支持 decorator,也可以通过类实现
Partial function
把一个函数的某些参数固定住(给这些参数设置默认值),返回一个新函数,以方便调用
偏函数仅仅是给参数设定了默认值,在调用新函数时是可以传入其他值给这些参数的
import functools
# 相当于
# kw = {'base': 2}
# int('1000000', **kw)
>>> int2 = functools.partial(int, base=2)
>>> int2('1000000')
64
>>> int2('1000000', base=10)
1000000
# 相当于
# args = [10]
# args.extend([5, 6, 7])
# max(*args)
>>> max2 = functools.partial(max, 10)
>>> max2(5, 6, 7)
10
创建偏函数时,实际接收的参数是:函数对象,*args、**kwargs;
上一篇:「Python 基础」基础语法与高级特性
下一篇:「Python 基础」面向对象编程
PS:感谢每一位志同道合者的阅读,欢迎关注、评论、赞!