目录
1, 什么是函数
函数是对程序的逻辑进行结构化的一种编程方法, 不指定返回值,则返回None, 先定义,再使用
2, 调用函数
通过函数名(参数), 进行函数调用
In [31]: def func():
...: print('hello python')
In [32]: func()
hello python
3, 函数参数
3-1, 位置参数
位置参数必须按形参的顺序传入,在没有默认参数的情况,实参和形参的数量一致
位置参数必须要默认参数前
3-2, 关键字参数
关键字参数的概念仅仅针对函数的调用, 允许参数不按顺序传入
In [33]: def func(host, port):
...: print('host:', host, 'port:', port)
# 这里使用关键字参数调用函数,没有按照形参的顺序赋值
In [34]: func(port=8080, host='aaa')
host: aaa port: 8080
3-3, 默认参数
默认参数就是定义默认值的参数,在函数调用时,不传入参数,则参数去默认值;传入参数,则按传入参数为准
# 定义默认参数
In [40]: def func(host='aaa', port=8080):
...: print('host:', host, 'port:', port)
# 不传入参数,以默认参数为准
In [41]: func()
host: aaa port: 8080
# 传入参数,则以传入参数为准
In [42]: func(host='aa1', port=8088)
host: aa1 port: 8088
3-4, 参数组
通过func(*args, **kw), 一个元组(非关键字参数), 一个字典(关键字参数)作为参数组传递给函数, args是元组形式的非关键字参数, kw是字典形式的关键字参数
def func(*args, **kw):
print('args:', args)
print('kw:', kw)
if __name__ == '__main__':
args = (1, 2, 3)
kw = dict(a='a1', b='b1')
func(*args, **kw)
# 这里没有以关键字和非关键字形式传入,args, kw会被当着位置参数传递给*args
func(args, kw)
# 输出结果
args: (1, 2, 3)
kw: {'a': 'a1', 'b': 'b1'}
args: ((1, 2, 3), {'a': 'a1', 'b': 'b1'})
kw: {}
元组形式非关键参数, 在函数定义时,需要放到默认参数之后
# *args需要放到默认参数之后, 在调用tupleVarArgs,额外参数将以元组形式保存到args中
def tupleVarArgs(arg1, arg2='defaultB', *args):
print('formal arg 1:', arg1)
print('formal arg 2:', arg2)
print('other args:', args)
if __name__ == '__main__':
# 只传入位置参数情况, args将是空元组
tupleVarArgs('abc')
# 输出结果:
formal arg 1: abc
formal arg 2: defaultB
other args: ()
if __name__ == '__main__':
# 多余的'defaultD', 'defaultE'参数,以元组形式存放在args中
tupleVarArgs('abc', 'defaultC', 'defaultD', 'defaultE')
# 输出结果
formal arg 1: abc
formal arg 2: defaultC
other args: ('defaultD', 'defaultE')
字典形式关键字参数, 在函数定义时,需要放到非关键字参数之后
def dictVarArgs(arg1, arg2='defaultB', *args, **kwargs):
print('formal arg 1:', arg1)
print('formal arg 2:', arg2)
print('args:', args)
print('kwargs:', kwargs)
if __name__ == '__main__':
dictVarArgs('abc')
# 输出结果
# 说明:因为函数调用时,值传入了位置参数,所以args, kwargs为空
formal arg 1: abc
formal arg 2: defaultB
args: ()
kwargs: {}
if __name__ == '__main__':
dictVarArgs('abc', '740', c='haha')
# 说明:额外参数c='haha'以字典形式传入关键字参数
# 输出结果
formal arg 1: abc
formal arg 2: 740
args: ()
kwargs: {'c': 'haha'}
if __name__ == '__main__':
dictVarArgs('abc', '740', (1, 2, 3), 'a', 'b', c='haha', d='dd')
# 说明:额外参数(1, 2, 3), 'a', 'b', 以元组形式传入非关键字参数
# 额外参数c='haha', d='dd', 以字典形式传入关键字参数中
# 输出结果
formal arg 1: abc
formal arg 2: 740
args: ((1, 2, 3), 'a', 'b')
kwargs: {'c': 'haha', 'd': 'dd'}
3-5, 函数作为参数传递
函数名本质上是函数对象的一个引用,相当于一个变量
def convert(func, seq):
return [func(each_num) for each_num in seq]
if __name__ == "__main__":
myseq = (123, 45.67, 9999, -6.2e8)
print(convert(int, myseq))
print(convert(float, myseq))
# 输出结果
[123, 45, 9999, -620000000]
[123.0, 45.67, 9999.0, -620000000.0]
4, 内嵌函数
在函数体内定义的函数,叫着内嵌函数, 内嵌函数的作用域仅在外部函数的函数体中
# bar是内嵌函数, 作用域仅在foo中
In [8]: def foo():
...: def bar():
...: print('bar() called')
...: print('foo() called')
...: bar()
4-1, 闭包
若内嵌入函数定义中使用了外部函数的变量,这种内部函数就是闭包,
这种由外部函数定义变量(不是全局作用域的变量),内部函数使用的变量叫着自由变量
函数的自由变量通过函数名.__closure__, 进行访问
output = '<int %r id=%#0x val=%d>'
w = x = y = z = 1
# f1是顶层函数,所以没有闭包变量
def f1():
x = y = z = 2
# f2中使用了外部变量x, 所以x是f2的闭包变量
def f2():
y = z = 3
# f3中使用了外部变量x,y; 所以x, y是f3的闭包变量
def f3():
z = 4
print(output % ('w', id(w), w))
print(output % ('x', id(x), x))
print(output % ('y', id(y), y))
print(output % ('z', id(z), z))
clo = f3.__closure__
if clo:
print('f3 closure vars:', [str(c) for c in clo])
else:
print("no f3 closure vars")
f3()
clo = f2.__closure__
if clo:
print('f2 closure vars:', [str(c) for c in clo])
else:
print("no f2 closure vars")
f2()
if __name__ == "__main__":
# 函数名.__closure__用来显示闭包变量的ID
clo = f1.__closure__
if clo:
print('f1 closure vars:', [str(c) for c in clo])
else:
print("no f1 closure vars")
f1()
# 输出结果
no f1 closure vars
f2 closure vars: ['<cell at 0x000001E1670683A0: int object at 0x00007FF83A1C06C0>']
f3 closure vars: ['<cell at 0x000001E1670683A0: int object at 0x00007FF83A1C06C0>', '<cell at 0x000001E167096FA0: int object at 0x00007FF83A1C06E0>']
<int 'w' id=0x7ff83a1c06a0 val=1>
<int 'x' id=0x7ff83a1c06c0 val=2>
<int 'y' id=0x7ff83a1c06e0 val=3>
<int 'z' id=0x7ff83a1c0700 val=4>
5, 装饰器
用来动态修改其他函数的功能, 也是函数,接收的函数作为参数
装饰器函数都返回的是wapper, 在最内层调用被装饰的函数
在wrapper上, 通过@functools.wrapper(函数名称), 可以起到被装饰的函数名不变
5-1, 无参装饰器
不带参数的装饰器函数,叫着无参装饰器
import functools
from time import ctime, sleep
# 定义装饰器
def deco(func):
# @functools.wraps(func)作用是保持foo的函数名没有因为装饰的原因而被修改
@functools.wraps(func)
def wrapper(*args, **kw):
print('%s %s' % (ctime(), func.__name__))
return func(*args, **kw)
return wrapper
# 使用deco来装饰foo
@deco
def foo():
print('start ...')
if __name__ == "__main__":
for i in range(2):
sleep(1)
# 调用foo(),相当于执行的是deco(foo)()
foo()
print('name:', foo.__name__)
# 输出结果
Wed Jul 26 13:59:38 2023 foo
start ...
Wed Jul 26 13:59:39 2023 foo
start ...
name: foo
5-2, 有参装饰器
带参数的装饰器函数,叫着有参装饰器
import functools
from time import ctime, sleep
# 定义装饰器
def deco2(option):
def deco(func):
# @functools.wraps(func)作用是保持foo的函数名没有因为装饰的原因而被修改
@functools.wraps(func)
def wrapper(*args, **kw):
print('%s %s %s' % (option, func.__name__, ctime()))
return func(*args, **kw)
return wrapper
return deco
# 使用deco2(option='begin')来装饰bar
@deco2(option='begin')
def bar():
print('start ...')
if __name__ == "__main__":
for i in range(2):
sleep(1)
# 调用bar(), 相当于执行deco2(option='begin')(bar)()
bar()
# 输出结果
begin bar Wed Jul 26 14:02:24 2023
start ...
begin bar Wed Jul 26 14:02:25 2023
start ...
5-3, 多个装饰器
# 定义装饰器
def deco1(func):
# @functools.wraps(func)作用是保持foo的函数名没有因为装饰的原因而被修改
@functools.wraps(func)
def wrapper(*args, **kw):
print('%s %s' % (ctime(), func.__name__))
return func(*args, **kw)
return wrapper
# 定义装饰器
def deco2(option):
def deco(func):
@functools.wraps(func)
def wrapper(*args, **kw):
print('%s %s %s' % (option, func.__name__, ctime()))
return func(*args, **kw)
return wrapper
return deco
# 使用两个装饰器来装饰func
@deco2(option='begin')
@deco1
def func():
print('now is %s' % func.__name__)
if __name__ == "__main__":
# 调用func(),相当于执行deco2(option='begin')(deco1(func))()
func()
# 输出结果
begin func Wed Jul 26 14:04:46 2023
Wed Jul 26 14:04:46 2023 func
now is func
6, lambda函数
通过f = lambda [args]: expression, 定义一个lambda函数, 并将其赋值给变量f, 通过f(args), 可获的expression的值
# 无参数lambda
In [1]: func = lambda: True
In [2]: func()
Out[2]: True
# 有参数的lambda
In [3]: func = lambda x, y: x + y
In [4]: func(1, 3)
Out[4]: 4
7, map函数
通过map(函数, 可迭代对象)方式, 可将函数依次作用到可迭代对象的每个元素,返回一个map对象,可通过list转换成列表
In [48]: map(str, range(10))
Out[48]: <map at 0x20df92d6080>
# map接收一个序列
In [49]: list(map(str, range(10)))
Out[49]: ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']
# map接收多个序列
# 此时lambda的参数数量, 与序列的个数一一对应
# 参数x对应第一个序列[1, 2, 3], 参数y对应第二个序列[1, 2, 3], 参数z对应第三个序列[1, 2, 3]
In [2]: list(map(lambda x, y, z: x+y+z, [1, 2, 3], [1, 2, 3], [1, 2, 3]))
Out[2]: [3, 6, 9]
8, reduce函数
python3中必须通过from functools import reduce导入, 来使用reduce
通过reduce(函数, 可迭代对象), 函数接收可迭代对象前两个参数,把函数结果继续和序列的下一个元素传入到reduce计算,直到最后一个元素,返回最终的结果
In [4]: from functools import reduce
In [5]: reduce(lambda x, y: x+y, range(10))
Out[5]: 45
9, filter函数
通过filter(函数, 可迭代对象), 对可迭代对象的元素使用函数,过滤出结果为真的元素
返回的一个filter对象, 可通过list返回一个列表对象
In [9]: def odd(n):
...: if n % 2 == 0:
...: return n
# 返回的是filter对象
In [10]: filter(odd, range(10))
Out[10]: <filter at 0x2168666d4b0>
In [13]: a = filter(odd, range(10))
# 将filter转换为列表
In [14]: list(a)
Out[14]: [2, 4, 6, 8]
# 使用lambda简化代码
In [19]: list(filter(lambda n: n % 2==0, range(10)))
Out[19]: [0, 2, 4, 6, 8]
10, 偏函数
接收一个函数对象和固定的参数值, 固定住函数的部分参数值,返回一个函数对象
需要通过from functools import partial, 才能使用偏函数
# 注意,固定住的参数是以关键字形式,不是位置参数
# 固定参数base=2
In [22]: int2 = partial(int, base=2)
# 在调用的时候不用传入固定的参数值
In [23]: int2('1111')
Out[23]: 15
# 也可以指定参数值传入
In [24]: int2('1111', base=10)
Out[24]: 1111
11, 变量的作用域
11-1, 全局变量
全局变量在函数外定义的变量或者内建变量, 程序结束前都可以访问
11-2, 局部变量
局部变量在函数中定义的变量,只能在函数中进行访问
11-3, 搜索标识符的顺序
先在局部名字空间查找,再到全局名字空间,再到内建名字空间, 都没有找到的话就会报错
# global_str:全局变量
In [40]: global_str = 'foo'
# local_str:局部变量
In [44]: def foo():
...: local_str = 'bar'
...: return global_str + local_str
In [45]: foo()
Out[45]: 'foobar'
11-4, global语句
在函数体内使用global语句,可以修改全局变量的值
In [55]: is_this_global = 'abc'
In [57]: def foo():
# 使用global语句,修改全局变量的值
...: global is_this_global
...: this_is_loacl = 'aaa'
...: is_this_global = 'bbb'
...: return this_is_loacl + is_this_global
In [58]: foo()
Out[58]: 'aaabbb'
In [59]: is_this_global
Out[59]: 'bbb'
_________________________________________________________________________
def foo():
global is_this_global
this_is_loacl = 'aaa'
# 注意,这里通过复合运算符修改时,必须在函数体内用global声明is_this_global
is_this_global += 'bbb'
return this_is_loacl + is_this_global
if __name__ == "__main__":
print(foo())
12, 递归
函数体内对自身的调用,该函数就是递归函数
def factorial(n):
if n in [0, 1]:
return 1
# 在函数内调用了函数本身
return n * factorial(n-1)
if __name__ == "__main__":
print(factorial(5))
13, 生成器
从语法上讲,是一个带yield的函数, 能暂停执行,并返回一个中间结果, 调用next()方法, 输出yield值,并从暂停的地方继续执行
# 定义生成器
In [11]: def simbal_gen():
...: yield 1
...: yield '2 ---> punch!'
# 通过for循环,输出生成器的元素
In [17]: for item in simbal_gen():
...: print(item)
1
2 ---> punch!
def counter(start_at=0):
count = start_at
while True:
# 若没有通过send发送值,则val=None
# 若通过send发送值 ,则val=send发送的值
val = yield count
if val is not None:
count = val
else:
count += 1
if __name__ == "__main__":
count = counter(5)
print(next(count))
print(count.send(10))
count.close()
14, 迭代器
迭代器为类序列对象创建了一个类序列接口,通过next()方法获取下一个元素, 通过for循环迭代输出所有的元素, 相比于序列对象性能得到了提升
14-1, 创建迭代器
14-1-1,iter(seq)
通过iter(seq), 将一个序列对象转换为迭代器
# 通过iter将序列'abcdef'转换成一个迭代器
In [11]: iter1 = iter(list('abcdef'))
In [12]: iter1
Out[12]: <list_iterator at 0x1c16d3ed0c0>
# 通过next(迭代器), 获取迭代器的下个元素
In [13]: next(iter1)
Out[13]: 'a'
# 通过for循环,遍历迭代器的所有元素
In [14]: for i in iter1:
...: print(i)
...:
b
c
d
e
f
14-1-2, 定制迭代器(__iter__)
在类中,使用特殊方法__iter__, __next__, 可定制类为一个迭代器
class Fib(object):
def __init__(self, num):
self.num = num
self.a, self.b = 0, 1
self.count = 0
# 返回self, 表示self本身是个迭代器
def __iter__(self):
return self
# 通过next方法获取元素下个值
def __next__(self):
if self.count != 10:
self.count += 1
self.a, self.b = self.b, self.a + self.b
return self.a
else:
# 注意,在不满住条件时, 必须用StopIteration抛出,不然会一直迭代
raise StopIteration
if __name__ == "__main__":
m = Fib(10)
for i in m:
print('i:', i)