1.python中函数定义
函数是逻辑结构化和过程结构化的一种编程方法。
(1)、python中函数定义方法:
def test(x): '''The function definitions''' x+=1 return x
说明:
def :定义函数的关键字
test :函数名
():可定义形参
'''''':文档描述,非必要
x+=1: 泛指代码块或程序处理逻辑
return: 定义返回值
2.为什么要使用函数
背景摘要:现在需要写一个监控程序,当CPU,MEMORY,DISK等指标的使用量超过阈值时发送报警邮件
(1)、传统方式实现:
while1: if cpu>90%: #发送邮件提醒 连接邮箱服务器 发送邮件 关闭链接 if memory >90%: #发送邮件提醒 连接邮箱服务器 发送邮件 关闭链接 if disk >90% #发送邮件提醒 连接邮箱服务器 发送邮件 关闭链接
(2)、使用函数实现:
def 发送邮件(内容): #发送邮件提醒 连接邮箱服务器 发送邮件 关闭链接 while 1: if cpu>90%: 发送邮件('cpu报警') if memory>90%: 发送邮件('memory报警') if disk>90%: 发送邮件('disk报警')
总结使用函数的好处:
1.代码重用
2.保持一致性,容易维护
3.可扩展性
3.函数参数
(1)、形参
形参变量只有在调用时候才分配内存单元,在调用结束时,即刻释放所调用的内存单元。因此,形参只在函数内部有效,函数调用结束返回主调用函数后则不能在使用该形参变量。
(2)、实参
实参可以是常量,变量,表达式,函数等,无论实参是何种类型的量,在进行函数调用时,它们必须有确定的值,以便把这些值传给形参。因此应预先用赋值,输入等办法使参数获得确定值。
def calc(x,y): #x,y为形参 res=x**y return res calc(a,b) #a,b为实参 print(calc(a,b))
(3)、位置参数和关键字
标准调用:实参和形参一一对应
关键字调用:位置无需固定
def test(x,y,z): print(x) print(y) print(z) test(1,2,3) #位置标准调用 test(x=1,y=3,z=2)#关键字调用
注意:如果混合使用,位置参数一定要在关键字参数的左边
(4)、默认参数
def handle(x,type='mysql'): print(x,type) handle('hello') #type默认值为'mysql'
handle('hello',1) #重新赋值,覆盖原本默认参数
>> hello mysql
hello 1
(5)、可变长参数
①、*args
能够接收列表或元祖
为了能让一个函数接受任意数量的位置参数。
def avg(first, *args): return (first + sum(rest)) / (1 + len(args)) avg(1, 2) # 1.5 avg(1, 2, 3, 4) # 2.5
在这个例子中,args是由所有其他位置参数组成的元组。然后我们在代码中把它当成了一个序列来进行后续的计算。
②、**kwargs
为了接受任意数量的关键字参数
def test(**kwargs) print(kwargs)
test(a=1, b=2) >>{'a': 1, 'b': 2}
如果希望某个函数能同时接受任意数量的位置参数和关键字参数,可以同时使用*和**。
def anyargs(*args, **kwargs): print(args) # A tuple print(kwargs) # A dict
使用这个函数时,所有位置参数会被放到args元组中,所有关键字参数会被放到字典kwargs中。
注意:
一个*参数只能出现在函数定义中最后一个位置参数后面,而 **参数只能出现在最后一个参数。 有一点要注意的是,在*参数后面仍然可以定义其他参数。
def a(x, *args, y): #此时y必须是关键字参数 pass def b(x, *args, y, **kwargs): # 此时y必须是关键字参数 pass
这种参数就是我们所说的强制关键字参数。
扩展:只接受关键字参数的函数
希望函数的某些参数强制使用关键字参数传递,只需将强制关键字参数放到某个*参数或者单个*后面就能达到这种效果。
def recv(maxsize, *, block): 'Receives a message' pass recv(1024, True) # TypeError recv(1024, block=True) # Ok
利用这种技术,我们还能在接受任意多个位置参数的函数中指定关键字参数。比如:
def minimum(*values, clip=None): m = min(values) if clip is not None: m = clip if clip > m else m return m minimum(1, 5, 2, -5, 10) # Returns -5 minimum(1, 5, 2, -5, 10, clip=0) # Returns 0
很多情况下,使用强制关键字参数会比使用位置参数表意更加清晰,程序也更加具有可读性。 例如,考虑下如下一个函数调用:
msg = recv(1024, False)
如果调用者对recv函数并不是很熟悉,那他肯定不明白那个False参数到底来干嘛用的。 但是,如果代码变成下面这样子的话就清楚多了:
msg = recv(1024, block=False)
另外,使用强制关键字参数也会比使用**kwargs参数更好,因为在使用函数help的时候输出也会更容易理解:
>>> help(recv) Help on function recv in module __main__: recv(maxsize, *, block) Receives a message
(6)、局部变量和全局变量
规则:全局变量名大写,局部变量名小写
在子程序中定义的变量称为局部变量,在程序的一开始定义的变量称为全局变量。
如果函数内无global关键字
#有声明局部变量,函数调用会调用局部变量 HOBBY = ['上网', '打游戏'] def test(): HOBBY = '泡妞' print('我的爱好:', HOBBY) test() >>我的爱好:泡妞
#无声明局部变量,函数会调用全局变量 HOBBY = ['上网', '打游戏'] def test(): print('我的爱好:',HOBBY) test() >>我的爱好:['上网', '打游戏']
如果函数中有global关键字
a = 1 def test(): global a a += 1 # 改变全局变量a的值 print('函数中的a:', a) return a test() print(a)
(7)、返回值
要想获取函数的执行结果,就可以用return语句把结果返回
注意:
- 函数在执行过程中只要遇到return语句,就会停止执行并返回结果,所以也可以理解为 return 语句代表着函数的结束
- 如果未在函数中指定return,那这个函数的返回值为None
4、嵌套函数
def negan():
name = 'Negan'
print(name)
def alex():
name = 'Alex'
print(name)
def egon():
name = 'egon'
print(name)
print(name)
egon()
alex()
print(name)
说明:函数只有在被调用的时候才会被执行
5、递归函数
在函数内部,可以调用其他函数。如果一个函数在内部调用自身本身,这个函数就是递归函数。
(1)、使用递归计算n的阶乘
计算阶乘n! = 1 x 2 x 3 x ... x n
,用函数fact(n)
表示,可以看出:
fact(n) = n! = 1 x 2 x 3 x ... x (n-1) x n = (n-1)! x n = fact(n-1) x n
所以,fact(n)
可以表示为n x fact(n-1)
,只有n=1时需要特殊处理。
于是,fact(n)
用递归的方式写出来就是:
def fact(n): if n==1: return 1 return n * fact(n - 1)
假如我们要计算5的阶乘,则:
>>fact(5) >>5 * fact(4) >>5 * 4 * fact(3) >>5 * 4 * 3 * fact(2) >>5 * 4 * 3 * 2 * fact(1) >>5 * 4 * 3 * 2 * 1
(2)、递归问路
import time person_list = ['Alex','Bob','Christian'] def ask_way(person_list): print('_'*60) if len(person_list) == 0: print('没有人知道') return '没有人知道' person = person_list.pop(0) if person == 'Christian': return '%s说:我知道,路在脚下' %person print('Hi,%s敢问路在何方?' %person) print('%s说:我不知道,我可以帮你去问问%s' %(person,person_list)) time.sleep(1) res = ask_way(person_list) print('%s问的结果是:%res' %(person, res)) return res res = ask_way(person_list) print(res)
____________________________________________________________ Hi,Alex敢问路在何方? Alex说:我不知道,我可以帮你去问问['Bob', 'Christian'] ____________________________________________________________ Hi,Bob敢问路在何方? Bob说:我不知道,我可以帮你去问问['Christian'] ____________________________________________________________ Bob问的结果是:'Christian说:我知道,路在脚下'es Alex问的结果是:'Christian说:我知道,路在脚下'es Christian说:我知道,路在脚下
(3)、递归计算列表中所有元素的和
item = [1,2,3,4,5,6,7] def sum(item) head, *tail = item return head + sum(tail) if tail else head
递归特性:
a. 必须有一个明确的结束条件
b. 每次进入更深一层递归时,问题规模相比上次递归都应有所减少
c. 递归效率不高,递归层次过多会导致栈溢出(在计算机中,函数调用是通过栈(stack)这种数据结构实现的,
每当进入一个函数调用,栈就会加一层栈帧,每当函数返回,栈就会减一层栈帧。由于栈的大小不是无限的,
所以,递归调用的次数过多,会导致栈溢出,Python3中默认栈的大小是998)
6、匿名函数
Python中可以不用def关键字创建函数,使用lambda即可创建匿名函数,语法格式如下:
lambda = param1,param2,...,paramN:expression
匿名函数也是函数,与普通函数一样,参数也是可选的。
# 普通函数 def calc(x): return x*x # 改成匿名函数 lambda x : x*x
扩展:
>>> x = 10 >>> a = lambda y: x + y >>> x = 20 >>> b = lambda y: x + y
a(10)和b(10)返回的结果是什么?如果你认为结果是20和30,那么你就错了。
这其中的奥妙在于lambda表达式中的x是一个自由变量, 在运行时绑定值,而不是定义时就绑定,这跟函数的默认值参数定义是不同的。 因此,在调用这个lambda表达式的时候,x的值是执行时的值。例如:
>>> x = 15 >>> a(10) 25 >>> x = 3 >>> a(10) 13
如果你想让某个匿名函数在定义时就捕获到值,可以将那个参数值定义成默认参数即可,就像下面这样:
>>> x = 10 >>> a = lambda y, x=x: x + y >>> x = 20 >>> b = lambda y, x=x: x + y >>> a(10) 20 >>> b(10) 30
在这里列出来的问题是新手很容易犯的错误,有些新手可能会不恰当的使用lambda表达式。 比如,通过在一个循环或列表推导中创建一个lambda表达式列表,并期望函数能在定义时就记住每次的迭代值。例如:
>>> funcs = [lambda x: x+n for n in range(5)] >>> for f in funcs: ... print(f(0)) ... 4 4 4 4 4 >>>
但是实际效果是运行是n的值为迭代的最后一个值。现在我们用另一种方式修改一下:
>>> funcs = [lambda x, n=n: x+n for n in range(5)] >>> for f in funcs: ... print(f(0)) ... 0 1 2 3 4 >>>
7、高阶函数
满足下面两个条件即为高阶函数:
(1)、函数传入的参数是一个函数名
(2)、函数的返回值是一个函数
(1)、把函数当作参数传给另一个函数
def foo(n): print(n) def bar(name): print('My name is %s'%name) foo(bar('alex')) >>My name is alex None
(2)、返回值中包含函数
def foo(): print('from foo') return bar def bar(): print('from bar') foo()() >> from foo from bar
(3)、尾调用
在函数的最后一步调用另外一个函数(最后一行不一定是函数的最后一步)
尾调用的关键在于函数的最后一步调用别的函数,根据函数即变量的定义,定义a函数,a函数内调用b函数,b函数内调用函数c,在内存中形成一个调用记录,又称为调用栈,用于保存调用位置以及变量信息,即a->b->c,直到c返回结果给b,c的调用记录才会消失,b返回结果给a,b的调用结果消失,a返回结果,a的调用记录消失,所有的调用记录都是“”先进后出”,形成一个‘’调用栈‘’。(4)、map()函数
num = [1,2,3,4] def add(x): return x +1 def map_test(func, array) ret = [] for i in array: ret.append(func(i)) return ret print(map_test(add, num)) >> [2,3,4,5] # 使用普通函数
print(map_test(lambda x:x+1, num)) >> [2,3,4,5] # 配合匿名函数实现
使用map函数实现上面的功能
注意:调用map时候返回的是map类的实例,此时func的方法并没有执行,根据需要返回的类型,在map实例中调用list、tuple、set、dict等方法触发回调函数。
num = [1,2,3,4] print(list(map(lambad x: x+1, num)))
(5)、filter()函数
遍历序列中每个元素,判断每个元素得到一个布尔值,如果是True则留下来。
data = list(range(10)) def func(item): if item % 2 == 0: return item def filter_test(func, array): ret = [0] for i in array: if func(i): ret.append(func(i)) return ret
filter_test(func, data) >> [0,2,3,6,8]
使用filter()函数实现
list(filter(lambda x:x%2==0, list(range(10))))
(6)、reduce()函数
reduct方法有三个参数:
reduce(function, sequence, initial=None)
function:待执行的方法
sequence:序列
initial:初始值
在没有初始值的情况下,首次执行会从序列中取出来两个值,传入function得到一个结果,然后从序列中按照顺序取出下一个值,和该结果一起传入function,直到序列中把所有
num = [1,2,3,10] def multi(x,y): return x*y def reduce_test(func,array,init=None): if init is None: res = array.pop(0) else: res = init for i in array: res = func(res,i) return res print(reduce_test(multi,num,10))
使用reduce()实现
from functools import reduce num = [1,2,3,10] reduce(lambda x,y: x*y, num)
带默认参数
from functools import reduce num = [1,2,3,10] reduce(lambda x,y: x*y, num) >>120