函数
函数就是把一段代码打包起来,定义一个名字,按照函数的规则顶一次一个函数。当要使用这段代码的时候,调用该函数即可。函数的作用在于减少重复代码,提高代码可读性,易于扩展。
一、先来看一个最简单的函数。
def first_func(): # 定义函数 #写逻辑 print('hello world!') first_func() #调用
--------------------------- hello world! #运行结果
函数定义规则: def 函数名(): #函数逻辑 pass # 相当于占位符号 函数调用方式: 函数名()
二、函数的参数
def say_hi(name): #定义时的参数叫形参 print('Hi,', name) say_hi('Web') # 调用时传入的参数叫实参 ---------------------- Hi, Web
当参数有多个时,根据参数传入的方式分为位置参数、关键字参数、和非固定参数
1、位置参数,函数在调用时,实参和形参的位置必须一一对应,下面的例子如果调用时say_hi(25,'Web'),则输出:25 is Web years old.
def say(name, age): print('%s is %s years old.' % (name, age)) say('Web', 25) ---------------------- Web is 25 years old.
在定义函数时,可以给形参设置默认值,函数调用时如果不传该参数,则使用默认值,否则使用实传入的值。
有默认值的形参放到后边。
def say(name, age=25): print('%s is %s years old.' % (name, age)) say('Web') -->Web is 25 years old. say('Web', 30) -->Web is 30 years old.
2、关键字参数,在函数调用时指定哪个形参的值是什么
def say(name, age=25): print('%s is %s years old.' % (name, age)) say(age=30, name='Web') -->Web is 30 years old.
当关键字参数和位置参数混合使用时,调用时位置参数在前,关键字参数在后。
3、非固定参数--*args和**kwargs
定义函数时使用*args和**kwargs,则在调用时可以传入任意数量的参数。
def say(*args, **kwargs): print('args: ', args) print('kwargs: ', kwargs) for item in args: print('Hi,', item) for j in kwargs: print('%s is eating.' % kwargs[j]) say('Web', 'Rice', 'Black', p1='cat', p2='dag') ----------------------------------------------- args: ('Web', 'Rice', 'Black') kwargs: {'p1': 'cat', 'p2': 'dag'} Hi, Web Hi, Rice Hi, Black cat is eating. dag is eating.
可以看到,位置参数以元组形式传给了args,关键字参数以字典形式传给了kwargs(不能写成say_hi('Web', 'Rice', p1='cat', p2='dag', 'Black'))
如果在调用的时候人为的传入列表、字典、或元组呢?
say_hi(*['Web', 'Rice', 'Black'], **{'p1': 'cat', 'p2': 'dag'})
输出结果与上面是相同的。
def func(*args, name=1, **kwargs,): print(args) print(name) print(kwargs) return 1 a = func(11, 22, 33, x=123, y=456, ) b = func(11, 22, 33, name=123, x=123, y=456, ) c = func(11, 22, 33, x=123, y=456, name=123, ) ######################## (11, 22, 33) 1 {'x': 123, 'y': 456} (11, 22, 33) 123 {'x': 123, 'y': 456} (11, 22, 33) 123 {'x': 123, 'y': 456}
参数顺序:位置参数,args,默认值参数, kwargs
def f1(x, y, *args, z='2', **kwargs, ): print(z) print(args) print(kwargs) # 默认值参数位置 f1(1, 2, 'a', 'b', 5, 4, z='zz', q=3, w=123, e='456') f1(1, 2, 'a', 'b', 5, 4, q=3, z='zz', w=123, e='456')
三、返回值
函数的返回值用return,前面没写,默认返回None
def calc(a, b): result = a+b return 'result', result print('还能执行到这里吗') ret = calc(1, 2) print(ret) ------------------- ('result', 3)
可以看出,打印函数的执行结果得到函数的返回值,返回值可以有多个,并且当遇到return时,函数结束。在程序中可以根据函数的返回值做一些相应的操作。
四、局部变量
尝试一下在函数内部修改变量
name = 'Web' # print(id(name)) def change_name(): # global name # 强改全局变量 name = 'ww' print('-->', name) # print(id(name)) change_name() print(name) ------------------------- --> ww Web
what?为什么没有改成功呢,看一下ID吧,可以看到函数内部和外部的name其实是两个不同的变量,如果硬要改,就使用global关键字,声明name是全局变量,但是,不要随便这么用。
五、作用域
在python中,一个函数就是一个作用域。
name = "lzl" def f1(): name = "Eric" def f2(): name = "Snor" print(name) f2() f1()
name = "lzl" def f1(): print(name) def f2(): name = "eric" f1() f2()
name = "lzl" def f1(): print(name) def f2(): name = "eric" return f1 ret = f2() ret() >>:'lzl'
在函数未执行之前,作用域已经形成了,作用域链也生成了
六、匿名函数
def calc(x, y): return x+y func = lambda x, y: x+y print(calc(1, 2)) print(func(1, 2))
格式:lambda 变量: 逻辑/返回值
匿名函数最多支持三元运算,不能实现更复杂的逻辑
def calc(x, y): if x < y: return x+y else: return x - y func = lambda x, y: x+y if x < y else x - y print(calc(1, 2)) print(func(1, 2))
七、高阶函数:接受一个或多个函数作为参数或return返回另外一个函数
八、递归
在函数内部,可以调用其他函数。如果一个函数在内部调用自身本身,这个函数就是递归函数。
def dev(a): print(a) if a > 0: return dev(a // 2)
return a result = dev(10) print(result)
递归特性:
- 必须有一个明确的结束条件
- 每次进入更深一层递归时,问题规模相比上次递归都应有所减少
- 递归效率不高,递归层次过多会导致栈溢出(在计算机中,函数调用是通过栈(stack)这种数据结构实现的,每当进入一个函数调用,栈就会加一层栈帧,每当函数返回,栈就会减一层栈帧。由于栈的大小不是无限的,所以,递归调用的次数过多,会导致栈溢出)
九、装饰器
1、闭包:在一个函数(outer)内定义一个内部函数(inner),inner函数可以调用outer函数的变量,而函数outer返回函数inner。
作用:无论在何处调用inner,都优先使用outer定义的变量。
def outer(): name = 'eric' def inner(): print("在inner里打印外层函数的变量:", name) return inner f = outer() f()
2、装饰器:装饰器的本质就是返回函数的函数
import time def outer(func): def inner(*args, **kwargs): """添加装饰功能""" start = time.time() print('*****装饰器******') ret = func(*args, **kwargs) # 获取被装饰函数的返回值 time.sleep(1) over = time.time() spend = str(over-start) print('cost %s secend' % spend) return ret return inner @outer # 调用装饰器 def say(name, age): # 函数的参数理论上是无限制的,但实际上最多6个 print('My name is %s,%d years old'%(name,age)) return 'say的返回值' say('Web', 80) """@outer 调用装饰器的原理""" # say = outer(say) # say('Web', 20) # 相当于执行inner('Web', 20) inner内func == say
当一个函数有多个装饰器时,装饰器按顺序从上到下依次执行。
from functools import wraps def a(a, b): def xx(func): @wraps(func) def inner(*args, **kwargs): print('aaaaaaaa') return func(*args, **kwargs) return inner return xx @a(1, 2) def b(func): @wraps(func) def inner(*args, **kwargs): print('bbbbbbbbbbb') return func(*args, **kwargs) return inner @b def test(): print('ttttttttttt') test() print(test.__name__) print(a.__name__)
def sum_list(obj): s = [] def sum_inner(obj1): for i in obj1: if isinstance(i, list): sum_inner(i) else: s.append(i) return sum(s) return sum_inner(obj) lst = [1, 2, 3, [1, 2, [3]], 1, [1, [[1]], [1]]] print(sum_list(lst))