python学习-函数
参考:
-
http://www.cnblogs.com/linhaifeng/articles/7532512.html#_label4
- http://www.cnblogs.com/linhaifeng/articles/7532512.html#_label3
阅读目录
- 一 数学定义的函数与python中的函数
- 二 为何使用函数
- 背景提要
- 三 函数和过程
- 四 函数参数
- 五 局部变量和全局变量
- 六 前向引用之'函数即变量'
- 七 嵌套函数和作用域
- 八 递归调用
- 九 匿名函数
- 十 函数式编程
- 十一 内置函数
- 十二 本节作业
一 数学定义的函数与python中的函数
初中数学函数定义:一般的,在一个变化过程中,如果有两个变量x和y,并且对于x的每一个确定的值,y都有唯一确定的值与其对应,那么我们就把x称为自变量,把y称为因变量,y是x的函数。自变量x的取值范围叫做这个函数的定义域
例如y=2*x
补充:
1.编程语言中的函数与数学意义的函数是截然不同的俩个概念,编程语言中的函数是通过一个函数名封装好一串用来完成某一特定功能的逻辑,数学定义的函数就是一个等式,等式在传入因变量值x不同会得到一个结果y,这一点与编程语言中类似(也是传入一个参数,得到一个返回值),不同的是数学意义的函数,传入值相同,得到的结果必然相同且没有任何变量的修改(不修改状态),而编程语言中的函数传入的参数相同返回值可不一定相同且可以修改其他的全局变量值(因为一个函数a的执行可能依赖于另外一个函数b的结果,b可能得到不同结果,那即便是你给a传入相同的参数,那么a得到的结果也肯定不同)
1 python中函数定义方法:
2
3 def test(x):
4 "The function definitions"
5 x+=1
6 return x
7
8 def:定义函数的关键字
9 test:函数名
10 ():内可定义形参
11 "":文档描述(非必要,但是强烈建议为你的函数添加描述信息)
12 x+=1:泛指代码块或程序处理逻辑
13 return:定义返回值
14
15
16 调用运行:可以带参数也可以不带
17 函数名()
2.函数式编程就是:先定义一个数学函数(数学建模),然后按照这个数学模型用编程语言去实现它。至于具体如何实现和这么做的好处,且看后续的函数式编程。
二 为何使用函数
背景提要
现在老板让你写一个监控程序,监控服务器的系统状况,当cpu\memory\disk等指标的使用量超过阀值时即发邮件报警,你掏空了所有的知识量,写出了以下代码
1 while True:
2 if cpu利用率 > 90%:
3 #发送邮件提醒
4 连接邮箱服务器
5 发送邮件
6 关闭连接
7
8 if 硬盘使用空间 > 90%:
9 #发送邮件提醒
10 连接邮箱服务器
11 发送邮件
12 关闭连接
13
14 if 内存占用 > 80%:
15 #发送邮件提醒
16 连接邮箱服务器
17 发送邮件
18 关闭连接
上面的代码实现了功能,但即使是邻居老王也看出了端倪,老王亲切的摸了下你家儿子的脸蛋,说,你这个重复代码太多了,每次报警都要重写一段发邮件的代码,太low了,这样干存在2个问题:
- 代码重复过多,一个劲的copy and paste不符合高端程序员的气质
- 如果日后需要修改发邮件的这段代码,比如加入群发功能,那你就需要在所有用到这段代码的地方都修改一遍
你觉得老王说的对,你也不想写重复代码,但又不知道怎么搞,老王好像看出了你的心思,此时他抱起你儿子,笑着说,其实很简单,只需要把重复的代码提取出来,放在一个公共的地方,起个名字,以后谁想用这段代码,就通过这个名字调用就行了,如下
1 def 发送邮件(内容)
2 #发送邮件提醒
3 连接邮箱服务器
4 发送邮件
5 关闭连接
6
7 while True:
8
9 if cpu利用率 > 90%:
10 发送邮件('CPU报警')
11
12 if 硬盘使用空间 > 90%:
13 发送邮件('硬盘报警')
14
15 if 内存占用 > 80%:
16 发送邮件('内存报警')
你看着老王写的代码,气势恢宏、磅礴大气,代码里透露着一股内敛的傲气,心想,老王这个人真是不一般,突然对他的背景更感兴趣了,问老王,这些花式玩法你都是怎么知道的? 老王亲了一口你儿子,捋了捋不存在的胡子,淡淡的讲,“老夫,年少时,师从京西沙河淫魔银角大王 ”, 你一听“银角大王”这几个字,不由的娇躯一震,心想,真nb,怪不得代码写的这么6, 这“银角大王”当年在江湖上可是数得着的响当当的名字,只可惜后期纵欲过度,卒于公元2016年, 真是可惜了,只留下其哥哥孤守当年兄弟俩一起打下来的江山。 此时你看着的老王离开的身影,感觉你儿子跟他越来越像了。。。
总结使用函数的好处:
1.代码重用
2.保持一致性,易维护
3.可扩展性
三 函数和过程
过程定义:过程就是简单特殊没有返回值的函数
这么看来我们在讨论为何使用函数的的时候引入的函数,都没有返回值,没有返回值就是过程,没错,但是在python中有比较神奇的事情
- 补充:函数名其实就是内存地址
1 def test01():
2 msg='hello The little green frog'
3 print msg
4
5 def test02():
6 msg='hello WuDaLang'
7 print msg
8 return msg
9
10
11 t1=test01()
12
13 t2=test02()
14
15
16 print 'from test01 return is [%s]' %t1
17 print 'from test02 return is [%s]' %t2
总结:当一个函数/过程没有使用return显示的定义返回值时,python解释器会隐式的返回None,
所以在python中即便是过程也可以算作函数。
def test01():
pass
def test02():
return 0
def test03():
return 0,10,'hello',['alex','lb'],{'WuDaLang':'lb'}
t1=test01()
t2=test02()
t3=test03()
print 'from test01 return is [%s]: ' %type(t1),t1
print 'from test02 return is [%s]: ' %type(t2),t2
print 'from test03 return is [%s]: ' %type(t3),t3
总结:
返回值数=0:返回None
返回值数=1:返回object
返回值数>1:返回tuple
四 函数参数
1.形参变量只有在被调用时才分配内存单元,在调用结束时,即刻释放所分配的内存单元。因此,形参只在函数内部有效。函数调用结束返回主调用函数后则不能再使用该形参变量
2.实参可以是常量、变量、表达式、函数等,无论实参是何种类型的量,在进行函数调用时,它们都必须有确定的值,以便把这些值传送给形参。因此应预先用赋值,输入等办法使参数获得确定值
3.返回值return,如果不return,默认返回None。返回多个的时候结果的数据类型是元组。

3.位置参数和关键字(标准调用:实参与形参位置一一对应;关键字调用:位置无需固定)
4.默认参数
5.参数组
1、形式参数:形参不占内存空间,使用的时候才会被调用,调用完之后立马释放。
2、实际参数:占内存空间
3、位置参数、关键字参数--位置参数必须一一对应,多一个🤨,少一个也不行;关键字参数不需要一一对应;如果混合使用,关键字参数必须在最右边,并且不能多传和漏传参。
4、默认参数:在定义函数的时候已经默认给了一个值,如果实际参数不传那么就用默认值,如果实际参数传了就用实际参数值。通常用在安装的时候的一些默认选项。
5、参数组:任意类型任意长度;*表示列表,**表示字典;在函数内部会把参数作为列表或者字典来操作,可空。*可以分开,也可以*args作为一个整体。其实*有个遍历操作。
五 局部变量和全局变量
name='lhf'
def change_name():
print('我的名字',name)
change_name()
def change_name():
name='帅了一笔'
print('我的名字',name)
change_name()
print(name)
def change_name():
global name
name='帅了一笔'
print('我的名字',name)
change_name()
print(name)
-
- 补充:
- 如果函数的内容无global关键字,那么优先读取局部变量,如果没有局部变量,那么能读取全局变量,但无法对全局变量进行重新赋值(只能读,不能改),会报错;但是对于字符串、列表、字典,可以对内部元素进行操作。因为位置不一样所以在内存中的存储位置也是不一样的,如果在函数内部进行从新复制,其实已经是重新定义了一个变量了,这个重新定义的变量的内存地址和全局变量的地址是不一样的,所以压根就没有对全局变量进行重新赋值,虽然在是使用的时候从表面的名字来看还是使用的同一个,但是内部却不是同一个,因为参数的调用,在函数内部是先找局部再找全局。
- 有声明局部变量:
- 无声明局部变量:
- 如果函数内部有global关键字,变量本质就是全局的那个变量,其实global就是把指定了这个变量的地址就是全局变量的那个地址,那么此时可以对全局变量进行读取和重新赋值(可读,可改)。
- 有声明局部变量:global必须放在局部变量之前,因为此时无法对全局变量进行重新赋值,会报错。
- 无声明局部变量:六 前向引用之'函数即变量'
- 如果函数的内容无global关键字,那么优先读取局部变量,如果没有局部变量,那么能读取全局变量,但无法对全局变量进行重新赋值(只能读,不能改),会报错;但是对于字符串、列表、字典,可以对内部元素进行操作。因为位置不一样所以在内存中的存储位置也是不一样的,如果在函数内部进行从新复制,其实已经是重新定义了一个变量了,这个重新定义的变量的内存地址和全局变量的地址是不一样的,所以压根就没有对全局变量进行重新赋值,虽然在是使用的时候从表面的名字来看还是使用的同一个,但是内部却不是同一个,因为参数的调用,在函数内部是先找局部再找全局。
- 补充:
六 前向引用之'函数即变量'
def action():
print 'in the action'
logger()
action()
报错NameError: global name 'logger' is not defined
def logger():
print 'in the logger'
def action():
print 'in the action'
logger()
action()
def action():
print 'in the action'
logger()
def logger():
print 'in the logger'
action()
- 函数即变量:函数名其实就是函数的内存地址

- 注意:程序的加载顺序和执行顺序:
- 加载顺序:
- 执行顺序:
七 嵌套函数和作用域
看上面的标题的意思是,函数还能套函数?of course
name = "Alex"
def change_name():
name = "Alex2"
def change_name2():
name = "Alex3"
print("第3层打印",name)
change_name2() #调用内层函数
print("第2层打印",name)
change_name()
print("最外层打印",name)
此时,在最外层调用change_name2()会出现什么效果?
没错, 出错了, 为什么呢?
作用域在定义函数时就已经固定住了,不会随着调用位置的改变而改变
例一:
name='alex'
def foo():
name='lhf'
def bar():
print(name)
return bar
func=foo()
func()
例二:
name='alex'
def foo():
name='lhf'
def bar():
name='wupeiqi'
def tt():
print(name)
return tt
return bar
func=foo()
func()()
八 递归调用
古之欲明明德于天下者,先治其国;欲治其国者,先齐其家;欲齐其家者,先修其身;欲修其身者,先正其心;欲正其心者,先诚其意;欲诚其意者,先致其知,致知在格物。物格而后知至,知至而后意诚,意诚而后心正,心正而后身修,身修而后家齐,家齐而后国治,国治而后天下平。
在函数内部,可以调用其他函数。如果在调用一个函数的过程中直接或间接调用自身本身
def calc(n):
print(n)
if int(n/2) ==0:
return n
return calc(int(n/2))
calc(10)
输出:
10
5
2
1
#_*_coding:utf-8_*_
__author__ = 'Linhaifeng'
import time
person_list=['alex','wupeiqi','yuanhao','linhaifeng']
def ask_way(person_list):
print('-'*60)
if len(person_list) == 0:
return '没人知道'
person=person_list.pop(0)
if person == 'linhaifeng':
return '%s说:我知道,老男孩就在沙河汇德商厦,下地铁就是' %person
print('hi 美男[%s],敢问路在何方' %person)
print('%s回答道:我不知道,但念你慧眼识猪,你等着,我帮你问问%s...' %(person,person_list))
time.sleep(3)
res=ask_way(person_list)
# print('%s问的结果是: %res' %(person,res))
return res
res=ask_way(person_list)
print(res)
递归问路
递归特性:
1. 必须有一个明确的结束条件(return),不然就会成为死循环。
2. 每次进入更深一层递归时,问题规模相比上次递归都应有所减少
3. 递归效率不高,递归层次过多会导致栈溢出(在计算机中,函数调用是通过栈(stack)这种数据结构实现的,每当进入一个函数调用,栈就会加一层栈帧,每当函数返回,栈就会减一层栈帧。由于栈的大小不是无限的,所以,递归调用的次数过多,会导致栈溢出)
堆栈扫盲http://www.cnblogs.com/lln7777/archive/2012/03/14/2396164.html
尾递归优化:http://egon09.blog.51cto.com/9161406/1842475
1 data = [1, 3, 6, 7, 9, 12, 14, 16, 17, 18, 20, 21, 22, 23, 30, 32, 33, 35]
2
3
4 def binary_search(dataset,find_num):
5 print(dataset)
6
7 if len(dataset) >1:
8 mid = int(len(dataset)/2)
9 if dataset[mid] == find_num: #find it
10 print("找到数字",dataset[mid])
11 elif dataset[mid] > find_num :# 找的数在mid左面
12 print("\033[31;1m找的数在mid[%s]左面\033[0m" % dataset[mid])
13 return binary_search(dataset[0:mid], find_num)
14 else:# 找的数在mid右面
15 print("\033[32;1m找的数在mid[%s]右面\033[0m" % dataset[mid])
16 return binary_search(dataset[mid+1:],find_num)
17 else:
18 if dataset[0] == find_num: #find it
19 print("找到数字啦",dataset[0])
20 else:
21 print("没的分了,要找的数字[%s]不在列表里" % find_num)
22
23
24 binary_search(data,66)
25
26 二分查找
十 函数式编程
峰哥原创面向过程解释:
函数的参数传入,是函数吃进去的食物,而函数return的返回值,是函数拉出来的结果,面向过程的思路就是,把程序的执行当做一串首尾相连的函数,一个函数吃,拉出的东西给另外一个函数吃,另外一个函数吃了再继续拉给下一个函数吃。。。
例如:
用户登录流程:前端接收处理用户请求-》将用户信息传给逻辑层,逻辑词处理用户信息-》将用户信息写入数据库
验证用户登录流程:数据库查询/处理用户信息-》交给逻辑层,逻辑层处理用户信息-》用户信息交给前端,前端显示用户信息
函数式编程:
http://egon09.blog.51cto.com/9161406/1842475
11 高阶函数
满足俩个特性任意一个即为高阶函数
1.函数的传入参数是一个函数名
2.函数的返回值是一个函数名
1 array=[1,3,4,71,2]
2
3 ret=[]
4 for i in array:
5 ret.append(i**2)
6 print(ret)
7
8 #如果我们有一万个列表,那么你只能把上面的逻辑定义成函数
9 def map_test(array):
10 ret=[]
11 for i in array:
12 ret.append(i**2)
13 return ret
14
15 print(map_test(array))
16
17 #如果我们的需求变了,不是把列表中每个元素都平方,还有加1,减一,那么可以这样
18 def add_num(x):
19 return x+1
20 def map_test(func,array):
21 ret=[]
22 for i in array:
23 ret.append(func(i))
24 return ret
25
26 print(map_test(add_num,array))
27 #可以使用匿名函数
28 print(map_test(lambda x:x-1,array))
29
30
31 #上面就是map函数的功能,map得到的结果是可迭代对象
32 print(map(lambda x:x-1,range(5)))
33
34 map函数
1 from functools import reduce
2 #合并,得一个合并的结果
3 array_test=[1,2,3,4,5,6,7]
4 array=range(100)
5
6 #报错啊,res没有指定初始值
7 def reduce_test(func,array):
8 l=list(array)
9 for i in l:
10 res=func(res,i)
11 return res
12
13 # print(reduce_test(lambda x,y:x+y,array))
14
15 #可以从列表左边弹出第一个值
16 def reduce_test(func,array):
17 l=list(array)
18 res=l.pop(0)
19 for i in l:
20 res=func(res,i)
21 return res
22
23 print(reduce_test(lambda x,y:x+y,array))
24
25 #我们应该支持用户自己传入初始值
26 def reduce_test(func,array,init=None):
27 l=list(array)
28 if init is None:
29 res=l.pop(0)
30 else:
31 res=init
32 for i in l:
33 res=func(res,i)
34 return res
35
36 print(reduce_test(lambda x,y:x+y,array))
37 print(reduce_test(lambda x,y:x+y,array,50))
38
39 reduce函数
1 #电影院聚集了一群看电影bb的傻逼,让我们找出他们
2 movie_people=['alex','wupeiqi','yuanhao','sb_alex','sb_wupeiqi','sb_yuanhao']
3
4 def tell_sb(x):
5 return x.startswith('sb')
6
7
8 def filter_test(func,array):
9 ret=[]
10 for i in array:
11 if func(i):
12 ret.append(i)
13 return ret
14
15 print(filter_test(tell_sb,movie_people))
16
17
18 #函数filter,返回可迭代对象
19 print(filter(lambda x:x.startswith('sb'),movie_people))
20
21 filter函数
1 #当然了,map,filter,reduce,可以处理所有数据类型
2
3 name_dic=[
4 {'name':'alex','age':1000},
5 {'name':'wupeiqi','age':10000},
6 {'name':'yuanhao','age':9000},
7 {'name':'linhaifeng','age':18},
8 ]
9 #利用filter过滤掉千年王八,万年龟,还有一个九千岁
10 def func(x):
11 age_list=[1000,10000,9000]
12 return x['age'] not in age_list
13
14
15 res=filter(func,name_dic)
16 for i in res:
17 print(i)
18
19 res=filter(lambda x:x['age'] == 18,name_dic)
20 for i in res:
21 print(i)
22
23
24 #reduce用来计算1到100的和
25 from functools import reduce
26 print(reduce(lambda x,y:x+y,range(100),100))
27 print(reduce(lambda x,y:x+y,range(1,101)))
28
29 #用map来处理字符串列表啊,把列表中所有人都变成sb,比方alex_sb
30 name=['alex','wupeiqi','yuanhao']
31
32 res=map(lambda x:x+'_sb',name)
33 for i in res:
34 print(i)
35
36 总结
十一 内置函数

内置函数内置参数详解 https://docs.python.org/3/library/functions.html?highlight=built#ascii
1 abs()取绝对值
2 all()如果迭代器中的所有值的布尔值全部都是true,那么返回true;如果迭代器为空也返回true。
3 any()如果迭代器中只要一个为真就返回true
4 bin()十进制转为二进制
5 bool()判断布尔值---空,None,0为false,其余为true
6 bytes()把一个字符串表现为字节的形式,可以用于手动修改编码,网络编程用得多,用什么编码就用什么解码,不然就乱码
7 chr()获取字符对应的ascii码表中的值
8 ord()根据一个ascii码获取对应的值
9 dir()打印某对象中的方法
10 divmod()获取商和余数:divmod(10,3)---3,1;可以用于分页显示计算
11 eval()提取字符串中的数据结构;把字符串中表达式进行计算
12 hash()可哈希的数据类型即不可变数据类型,同一个字符串的哈希值是不变的,哈希值长度固定,不可反推
13 help()查看方法的使用
14 hex()十进制转为十六进制
15 oct()十进制转为八进制
16 id()打印一个对象的地址
17 isinstance()判断是否为某类的实例
18 globals()打印该文件下系统或者自定义的全局变量
19 locals()打印当前位置所有的局部变量
20
21 max()取最大值
22 参数数据类型必须是可迭代,相当于for循环取出进行比较
23 如果参数是字典,默认比较的是字典的key
24 字符串的比较是一位一位进行比较,通过ascii表作为判断大小,从前往后比,如果已经出结果那后面的将不再比较
25 不同数据类型间不能进行比较
26
27 min()取最小值
28 people = [
29 {'name':'alex','age':100},
30 {'name':'lhf','age':1000},
31 {'name':'wpq','age':99},
32 {'name':'szw','age':110}
33 ]
34 max = max(people,key=lambda dic:dic['age'])
35 print(max)
36
37 zip()拉链左边和右边一一对应,传两个序列的参数,然后匹配生成元组,不管左边多还是右边多,按照最少能对应的进行对应
38 print(list(zip(('a','b','c'),(1,2,3))))
39 p = {'name':'alex','age':18,'gender':'none'}
40 print(list(zip(p.keys(),p.values())))
41 print(list(zip('hello','12345')))
42
43 pow()乘方,
44 pow(3,3) #3**3
45 pow(3,3,2)#3**3%2
46
47 reversed()反转可迭代对象
48 round()四舍五入
49 set()集合
50 slice()切片
51 l = 'hello'
52 s1 = slice(2,5)
53 s2 = slice(1,4,2)
54 print(l[s1])
55 print(l[s2])
56
57 sorted()排序,本质就是在比较大小,同类型之间才可以比较
58 str()转换为字符串类型
59 sum()求和
60 type()
61 vars()如果没有传参数,那么和locals()功能一样,没啥卵用
62 range() 函数可创建一个整数列表,一般用在 for 循环中
63 __import__()如果文件名是一个字符串类型,那么用这种形式导入,需要用一个参数接收;import-->sys-->__import__()
十二 本节作业
有以下员工信息表

当然此表你在文件存储时可以这样表示
1 1,Alex Li,22,13651054608,IT,2013-04-01
现需要对这个员工信息文件,实现增删改查操作
- 可进行模糊查询,语法至少支持下面3种:
- select name,age from staff_table where age > 22
- select * from staff_table where dept = "IT"
- select * from staff_table where enroll_date like "2013"
- 查到的信息,打印后,最后面还要显示查到的条数
- 可创建新员工纪录,以phone做唯一键,staff_id需自增
- 可删除指定员工信息纪录,输入员工id,即可删除
- 可修改员工信息,语法如下:
- UPDATE staff_table SET dept="Market" WHERE where dept = "IT"
注意:以上需求,要充分使用函数,请尽你的最大限度来减少重复代码!
1 内置函数
2 匿名函数
3 递归
4 =====================作业一
5 #用map来处理字符串列表啊,把列表中所有人都变成sb,比方alex_sb
6 name=['alex','wupeiqi','yuanhao']
7
8
9 #用map来处理下述l,然后用list得到一个新的列表,列表中每个人的名字都是sb结尾
10 >>> l=[{'name':'alex'},{'name':'y'}]
11 >>> x=map(lambda i:{'name':i['name']+'sb'},l)
12 >>> for i in x:
13 ... print(i)
14 ...
15 {'name': 'alexsb'}
16 {'name': 'ysb'}
17
18
19 =====================作业二
20 #用filter来处理,得到股票价格大于20的股票名字
21 shares={
22 'IBM':36.6,
23 'Lenovo':23.2,
24 'oldboy':21.2,
25 'ocean':10.2,
26 }
27 >>> f=filter(lambda k:shares[k]>20,shares)
28 >>> list(f)
29 ['IBM', 'Lenovo', 'oldboy']
30
31
32
33 =====================作业三
34 #如下,每个小字典的name对应股票名字,shares对应多少股,price对应股票的价格
35 portfolio = [
36 {'name': 'IBM', 'shares': 100, 'price': 91.1},
37 {'name': 'AAPL', 'shares': 50, 'price': 543.22},
38 {'name': 'FB', 'shares': 200, 'price': 21.09},
39 {'name': 'HPQ', 'shares': 35, 'price': 31.75},
40 {'name': 'YHOO', 'shares': 45, 'price': 16.35},
41 {'name': 'ACME', 'shares': 75, 'price': 115.65}
42 ]
43
44 1:map来得出一个包含数字的迭代器,数字指的是:购买每支股票的总价格
45 >>> m=map(lambda item:item['shares']*item['price'],l)
46
47 2:基于1的结果,用reduce来计算,购买这些股票总共花了多少钱
48 >>> r=reduce(lambda x,y:x+y,m)
49 >>> r
50 51009.75
51
52 3:用filter过滤出,单价大于100的股票有哪些
53 >>> f=filter(lambda item:item['price'] > 100,l)
浙公网安备 33010602011771号