Python 第三课笔记
本节内容
- 数据运算
- 集合操作
- 文件操作
- 字符编码与转码
- 函数介绍
- 局部变量
- 递归
数据运算
1.算数运算:
注:取模的用法主要判断奇偶数(看一个数是否能被2整除)
2.比较运算:
注:不等于一般用!=,一般都不用<>,<>也不方便阅读
3.赋值运算
4、逻辑运算
5、成员运算
6、身份运算
用法:
7、位运算
用法:
#!/usr/bin/python a = 60 # 60 = 0011 1100 b = 13 # 13 = 0000 1101 c = 0 c = a & b; # 12 = 0000 1100 print "Line 1 - Value of c is ", c c = a | b; # 61 = 0011 1101 print "Line 2 - Value of c is ", c c = a ^ b; # 49 = 0011 0001 print "Line 3 - Value of c is ", c c = ~a; # -61 = 1100 0011 print "Line 4 - Value of c is ", c c = a << 2; # 240 = 1111 0000 print "Line 5 - Value of c is ", c c = a >> 2; # 15 = 0000 1111 print "Line 6 - Value of c is ", c
注:
①异或 ^:相同取0,不同为1
②与 &:有一个为0,取0;两个都为1,取1 (两个都为真才为真,否则为假)
③或 |:有一个为1,取1 (任意为真就为真)
④取反(~a):单个数取反,获取的值= 取反的值-256 *按位取反运算规则(按位取反再加1) 详解http://blog.csdn.net/wenxinwukui234/article/details/42119265
按位取反,只取一个值,a=60,按位取反就是1变0,0变1
⑤左移:向最左边移动n位,最右边补上n个0, 64>>1相当于除以2,64>>2相当于除以4
⑥右移:向最右边移动n位,最左边补上n个0,右移1位*2,右移2位*4
8、运算符优先级
总结:
①计算机中能表示的最小单位,是一个二进制位(bit)
②计算机中能存储的最小单位,是一个二进制位(bit)
③其实我们实际使用中,真正的存储是按照字节来存储的(8bit=1byte)
④任何一个英文字符至少需要一个字节表示
字节的单位换算:
1byte = 8bit
1kbyte = 1024byte
1Mbyte = 1024kbyte
1Gbyte = 1024Mbyte
1Tbyte = 1024Gbyte
集合操作
概念:集合是一个无序的,不重复的数据组合,它的主要作用如下:
- 去重,把一个列表变成集合,就自动去重了
- 关系测试,测试两组数据之前的交集、差集、并集等关系
定义:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
>>> name_1 = [1,2,3,4,7,8,7,10] #把列表转换为集合 >>> name_1 = set(name_1) #转换后,去重 >>> print(name_1,type(name_1)) {1, 2, 3, 4, 7, 8, 10} <class 'set'>
关系测试
1、交集(intersection())
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
>>> name_1 = [1,2,3,4,7,8,7,10] >>> name_2 = [1,3,5,8,10] >>> name_1 = set(name_1) >>> name_2 = set(name_2) #输出结果 >>> name_1.intersection(name_2) {8, 1, 10, 3}
2、并集(union())
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
>>> name_1 = [1,2,3,4,7,8,7,10] >>> name_2 = [1,3,5,8,10] >>> name_1 = set(name_1) >>> name_2 = set(name_2) #输出结果 >>> name_1.union(name_2) {1, 2, 3, 4, 5, 7, 8, 10}
3、差集(difference())
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
>>> name_1 = [1,2,3,4,7,8,7,10] >>> name_2 = [1,3,5,8,10] >>> name_1 = set(name_1) >>> name_2 = set(name_2) #输出结果 >>> name_1.difference(name_2) {2, 4, 7}
注:差集取的是数值在第一个集合中,但是不在第二个集合中(在我不在你)
4、子集issubset()
判断一个集合是否是另一个集合的子集
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
>>> name_1 = [1,2,3,4,7,8,7,10] >>> name_3 = [1,2,3,4] >>> name_1 = set(name_1) >>> name_3 = set(name_3) #输出结果 >>> name_3.issubset(name_1) True
5、父集issuperset()
判断一个集合是否是另一个集合的父集
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
>>> name_1 = [1,2,3,4,7,8,7,10] >>> name_3 = [1,2,3,4] >>> name_1 = set(name_1) >>> name_3 = set(name_3) #输出结果 >>> name_1.issuperset(name_3) True
6、对称差集(symmetric_difference())
把两个集合没有交集的数值取出来
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
>>> name_1 = [1,2,3,4,7,8,7,10] >>> name_2 = [1,3,5,8,10] >>> name_1 = set(name_1) >>> name_2 = set(name_2) #输出结果 >>> name_1.symmetric_difference(name_2) {2, 4, 5, 7}
7、isdisjoint()
判断两个集合是否有交集,没有交集,则返回True
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
>>> name_1 = [1,2,3,4,7,8,7,10] >>> name_2 = [1,3,5,8,10] >>> name_3 = [11] >>> name_1 = set(name_1) >>> name_2 = set(name_2) >>> name_3 = set(name_3) #有交集 >>> name_1.isdisjoint(name_2) False #无交集 >>> name_1.isdisjoint(name_3) True
关系测试--符号
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
s = set([3, 5, 9, 10]) # 创建一个数值集合 t = set("Hello") # 创建一个唯一字符的集合 a = t | s # t 和 s的并集 b = t & s # t 和 s的交集 c = t – s # 求差集(项在t中,但不在s中) d = t ^ s # 对称差集(项在t或s中,但不会同时出现在二者中) 基本操作: t.add('x') # 添加一项 s.update([10, 37, 42]) # 在s中添加多项 使用remove() #可以删除一项: t.remove('H') len(s) # set的长度 x in s # 测试x是否是s的成员 x not in s # 测试x是否不是s的成员 s.issubset(t) s <= t # 测试是否s中的每一个元素都在t中 s.issuperset(t) s >= t # 测试是否t中的每一个元素都在s中 s.union(t) s | t # 返回一个新的set包含s和t中的每一个元素 s.intersection(t) s & t # 返回一个新的set包含s和t中的公共元素 s.difference(t) s - t # 返回一个新的set包含s中有但是t中没有的元素 s.symmetric_difference(t) s ^ t # 返回一个新的set包含s和t中不重复的元素 s.copy() # 返回set “s”的一个浅复制
2. 文件操作
对文件操作流程
- 打开文件,得到文件句柄并赋值给一个变量
- 通过句柄对文件进行操作
- 关闭文件
现有一个文件
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Somehow, it seems the love I knew was always the most destructive kind 不知为何,我经历的爱情总是最具毁灭性的的那种 Yesterday when I was young 昨日当我年少轻狂 The taste of life was sweet 生命的滋味是甜的 As rain upon my tongue 就如舌尖上的雨露 I teased at life as if it were a foolish game 我戏弄生命 视其为愚蠢的游戏 The way the evening breeze 就如夜晚的微风 May tease the candle flame 逗弄蜡烛的火苗 The thousand dreams I dreamed 我曾千万次梦见 The splendid things I planned 那些我计划的绚丽蓝图 I always built to last on weak and shifting sand 但我总是将之建筑在易逝的流沙上 I lived by night and shunned the naked light of day 我夜夜笙歌 逃避白昼赤裸的阳光 And only now I see how the time ran away 事到如今我才看清岁月是如何匆匆流逝 Yesterday when I was young 昨日当我年少轻狂 So many lovely songs were waiting to be sung 有那么多甜美的曲儿等我歌唱 So many wild pleasures lay in store for me 有那么多肆意的快乐等我享受 And so much pain my eyes refused to see 还有那么多痛苦 我的双眼却视而不见 I ran so fast that time and youth at last ran out 我飞快地奔走 最终时光与青春消逝殆尽 I never stopped to think what life was all about 我从未停下脚步去思考生命的意义 And every conversation that I can now recall 如今回想起的所有对话 Concerned itself with me and nothing else at all 除了和我相关的 什么都记不得了 The game of love I played with arrogance and pride 我用自负和傲慢玩着爱情的游戏 And every flame I lit too quickly, quickly died 所有我点燃的火焰都熄灭得太快 The friends I made all somehow seemed to slip away 所有我交的朋友似乎都不知不觉地离开了 And only now I'm left alone to end the play, yeah 只剩我一个人在台上来结束这场闹剧 Oh, yesterday when I was young 噢 昨日当我年少轻狂 So many, many songs were waiting to be sung 有那么那么多甜美的曲儿等我歌唱 So many wild pleasures lay in store for me 有那么多肆意的快乐等我享受 And so much pain my eyes refused to see 还有那么多痛苦 我的双眼却视而不见 There are so many songs in me that won't be sung 我有太多歌曲永远不会被唱起 I feel the bitter taste of tears upon my tongue 我尝到了舌尖泪水的苦涩滋味 The time has come for me to pay for yesterday 终于到了付出代价的时间 为了昨日 When I was young 当我年少轻狂
基本操作
1.open()
open(文件路径,模式,字符编码)
1 open("song") #1、文件打开了 2 open("song").read() #2.读取内容 3 data=open("song").read() #3.给读取内容赋值 4 5 print(data)
【报错】
ps:运行文件时候没有指定字符编码,默认要设置一个字符编码集,windows默认的编码格式是gbk的
增加字符编码部分内容,打印成功
data = open ("song", encoding="utf-8").read() print(data)
不符合编写规范,没有有关闭文件,所有都读出来,没有办法做文件操作
对文件进行操作,对文件打开在内存里,没有赋值给文件,不能找到,所以只能读,不能进行操作
赋一个变量,对变量操作
1 f = open ("song", encoding="utf-8") #f是一个文件句柄,文件的内存对象 2 data=f.read() 3 print(data)
文件相当于指针
打开后,从第一行开始读,读到最后一行,
文件读完一遍就完了,从头读到尾之后,就没有东西,所以读取不到了,如果还想读取一遍,需要把光标移回去。
正确写法
#获取文件句柄
f = open("yesterday","r",encoding="utf-8")
#读取数据
data = f.read()
#打印读取的文件
print(data)
#关闭文件
f.close()
注: f 又叫文件句柄,它包含文件的文件名、文件的字符集、文件的大小、文件在硬盘上的起始位置
1 f = open ("song","w", encoding="utf-8") #f是一个文件句柄,文件的内存对象 2 data=f.read() 3 print(data) 4 f.write("快进快进看的即可") 5 6 【运行结果】 7 Traceback (most recent call last): 8 File "D:/Python/s014/Day3/file_op.py", line 6, in <module> 9 data=f.read() 10 io.UnsupportedOperation: not readable
2、打开文件模式
- r,英文:read,只读模式(默认)
- w,英文:write,只写模式(不可读,不存在则创建新文件,存在则删除内容)
- a,英文:append,追加模式(不可读,不存在则创建,存在则只追加内容)
"+"号表示同时读写某个文件
- r+,可读写文件(可读;可写;可追加)
- w+,可写读文件
- a+,可追加和读文件
"U"表示在读取文件时,可以把\r\n自动转化为\n(与 r 或 r+ 模式同使用)
- rU
- r+U
"b"表示处理二进制文件
- rb
- wb
- ab
1 f = open ("song","a", encoding="utf-8") #f是一个文件句柄,文件的内存对象 2 3 f.write("11111我爱北京天安门\n") 4 f.write("22222天安门上太阳升\n") 5 f.close()
6 【运行结果打开文件】
7 我爱北京天安门
8 天安门上太阳升
9 11111我爱北京天安门
10 22222天安门上太阳升
1. readline()
如果一行一行读取文件
1 f = open ("song","r", encoding="utf-8") #f是一个文件句柄,文件的内存对象 2 3 #一行一行读 4 print(f.readline()) 5 print(f.readline()) 6 print(f.readline()) 7 8 【运行结果】 9 10 Somehow, it seems the love I knew was always the most destructive kind 11 12 不知为何,我经历的爱情总是最具毁灭性的的那种 13 14 Yesterday when I was young
2.打印前五行
1 #打印前五行 2 for i in range(5): 3 print(f.readline())
3.readlines()
循环读取文件中的内容,把文件中的每一行作为一个元素形成一个列表
1 #把文件循环一遍 2 print(f.readlines()) 3 4 5 【运行结果一坨坨不换行】 6 ['Somehow, it seems the love I knew was always the most destructive kind\n', '不知为何,我经历的爱情总是最具毁灭性的的那种\n', 'Yesterday when I was young\n', '昨日当我年少轻狂\n', 'The taste of life was sweet\n', '生命的滋味是甜的\n', 'As rain upon my tongue\n', '就如舌尖上的雨露\n', 'I teased at life as if it were a foolish game\n', '我戏弄生命 视其为愚蠢的游戏\n', 'The way the evening breeze\n', '就如夜晚的微风\n', 'May tease the candle flame\n', '逗弄蜡烛的火苗\n', 'The thousand dreams I dreamed\n', '我曾千万次梦见\n', 'The splendid things I planned\n', '那些我计划的绚丽蓝图\n', 'I always built to last on weak and shifting sand\n', '但我总是将之建筑在易逝的流沙上\n', 'I lived by night and shunned the naked light of day\n', '我夜夜笙歌 逃避白昼赤裸的阳光\n', 'And only now I see how the time ran away\n', '事到如今我才看清岁月是如何匆匆流逝\n', 'Yesterday when I was young\n', '昨日当我年少轻狂\n', 'So many lovely songs were waiting to be sung\n', '有那么多甜美的曲儿等我歌唱\n', 'So many wild pleasures lay in store for me\n', '有那么多肆意的快乐等我享受\n', 'And so much pain my eyes refused to see\n', '还有那么多痛苦 我的双眼却视而不见\n', 'I ran so fast that time and youth at last ran out\n', '我飞快地奔走 最终时光与青春消逝殆尽\n', 'I never stopped to think what life was all about\n', '我从未停下脚步去思考生命的意义\n', 'And every conversation that I can now recall\n', '如今回想起的所有对话\n', 'Concerned itself with me and nothing else at all\n', '除了和我相关的 什么都记不得了\n', 'The game of love I played with arrogance and pride\n', '我用自负和傲慢玩着爱情的游戏\n', 'And every flame I lit too quickly, quickly died\n', '所有我点燃的火焰都熄灭得太快\n', 'The friends I made all somehow seemed to slip away\n', '所有我交的朋友似乎都不知不觉地离开了\n', "And only now I'm left alone to end the play, yeah\n", '只剩我一个人在台上来结束这场闹剧\n', 'Oh, yesterday when I was young\n', '噢 昨日当我年少轻狂\n', 'So many, many songs were waiting to be sung\n', '有那么那么多甜美的曲儿等我歌唱\n', 'So many wild pleasures lay in store for me\n', '有那么多肆意的快乐等我享受\n', 'And so much pain my eyes refused to see\n', '还有那么多痛苦 我的双眼却视而不见\n', "There are so many songs in me that won't be sung\n", '我有太多歌曲永远不会被唱起\n', 'I feel the bitter taste of tears upon my tongue\n', '我尝到了舌尖泪水的苦涩滋味\n', 'The time has come for me to pay for yesterday\n', '终于到了付出代价的时间 为了昨日\n', 'When I was young\n', '当我年少轻狂']
4.循环输出文件内容
1 #f.readlines() 相当于一个列表 2 #line作为一行,是其中的一个元素输出 3 for line in f.readlines(): 4 print(line) 5 6 【运行结果】 7 Somehow, it seems the love I knew was always the most destructive kind 8 9 不知为何,我经历的爱情总是最具毁灭性的的那种 10 11 Yesterday when I was young 12 ……
5.去掉空行
1 for line in f.readlines(): 2 print(line.strip())
1 #f.readlines() 相当于一个列表 2 #line作为一行,是其中的一个元素输出 3 #第十行不输出,输出分割线,判断列表下标,取第几行元素 4 for index,line in enumerate(f.readlines()): 5 if index==9: 6 print("*************************************************") 7 continue 8 print(line.strip())
几十个G的大文件不能使用以上方法实现
6.读取大文件
1 #不占用你内存,读一行覆盖一行 2 for line in f: 3 print(line.strip())
运行结果一样,效率和资源不一样,f已经不是一个列表,是一个迭代器
我们正常循环读取文件中的每一行,如下:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
f = open("yesterday2","r",encoding="utf-8") for index,line in enumerate(f.readlines()): #当下标值为2时,不打印 if index == 2: print("--------data--------") continue print(line.strip()) f.close() #输出 Somehow, it seems the love I knew was always the most destructive kind 不知为何,我经历的爱情总是最具毁灭性的的那种 --------data-------- 昨日当我年少轻狂
这种方法已经达到我们的目的了,可以顺利的读取每一行,但是,当我们遇到2G,20G,甚至200G的文件时,你这样读取会导致内存不够用,会使程序变的很慢,因为你的内存只有几个G,你把几十个G的数据放到内存,内存肯定是受不了的,所以这种方法只适合小文件,不适合大文件。那怎么办呢?我们于是就有下面这种方法,如下:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
f = open("yesterday2",'r',encoding='utf-8') #计数器 count = 0 #f文件变成迭代器 for line in f: if count == 2: print("------data-----") count += 1 continue print(line.strip()) count += 1 f.close() #输出结果 Somehow, it seems the love I knew was always the most destructive kind 不知为何,我经历的爱情总是最具毁灭性的的那种 ------data----- 昨日当我年少轻狂
以上这种写法的好处在于,读取文件时,是一行一行的读取,而且,读取一行删除一行,内存中只保留一行。原因:f文件变成迭代器,它已经不再是一个列表的形式了,不能通过下标值来获取,需要一个计数器来计数。
7.循环判断第10行
1 count=0 2 for line in f: 3 if count==9: 4 print("************************************") 5 count+=1 6 continue 7 print(line.strip()) 8 count+=1
8.读取文件的位置tell
1 f = open ("song","r", encoding="utf-8") #f是一个文件句柄,文件的内存对象 2 print(f.tell())#从文件开头 3 print(f.readline()) 4 print(f.tell()) 5 6 7 【运行结果】 8 0 9 Somehow, it seems the love I knew was always the most destructive kind 10 11 72
tell计数是按照字符的个数,多少字符,记多少个数
1 f = open ("song","r", encoding="utf-8") #f是一个文件句柄,文件的内存对象 2 print(f.tell()) 3 print(f.read(5))#默认读所有,f.read(5)可以设置读五个 4 print(f.tell()) 5 6 【运行结果】 7 0 8 Someh 9 5
9.seek把光标移回去
f.seek(0)#把光标移回去
1 f = open ("song","r", encoding="utf-8") #f是一个文件句柄,文件的内存对象 2 print(f.tell()) 3 print(f.readline()) 4 print(f.readline()) 5 print(f.readline()) 6 print(f.tell()) 7 f.seek(0)#把光标移回去 8 print(f.readline()) 9 10 【运行结果】 11 0 12 Somehow, it seems the love I knew was always the most destructive kind 13 14 不知为何,我经历的爱情总是最具毁灭性的的那种 15 16 Yesterday when I was young 17 18 168 19 Somehow, it seems the love I knew was always the most destructive kind
![](https://img2018.cnblogs.com/blog/1089519/201901/1089519-20190102135221666-500880562.png)
with语句
为了避免打开文件后忘记关闭,可以通过管理上下文,即:
with open('log','r') as f: ....
如此方式,当with代码块执行完毕时,内部会自动关闭并释放文件资源。
在Python 2.7 后,with又支持同时对多个文件的上下文进行管理,即:
with open('log1') as obj1, open('log2') as obj2: pass
上面打开多个文件会导致一行代码过长,python官方建议,一行代码不超过80个字符,所以打开多个文件建议以下写法:
with open('log1') as obj1, \ open('log2') as obj2: pass
文件修改
1、在以后的工作中有时候会用到修改某个文件的内容,所以下面我们就讲讲,如何修改文件
修改文件,是先读文件,一边读和操作,再一边写(写需要命令一个新的文件,成功后,把老的文件删掉,把新文件重新命名成老文件)
#读老文件 f = open("yesterday","r",encoding="utf-8") #写入新文件 f_new = open("yesterday_new",'w',encoding="utf-8") for line in f: #一边读新文件,修改内容 if "肆意的快乐等我享受" in line: line = line.replace("肆意的快乐等我享受","肆意的快乐等kakak享受") #一边写入新文件 f_new.write(line) #关闭文件 f.close() f_new.close()
yesterday文件:
So many lovely songs were waiting to be sung
有那么多甜美的曲儿等我歌唱
So many wild pleasures lay in store for me
有那么多肆意的快乐等我享受
And so much pain my eyes refused to see
还有那么多痛苦 我的双眼却视而不见
yesterday_new文件:
So many lovely songs were waiting to be sung
有那么多甜美的曲儿等我歌唱
So many wild pleasures lay in store for me
有那么多肆意的快乐等kaka享受
And so much pain my eyes refused to see
还有那么多痛苦 我的双眼却视而不见
字符转编码操作
编码介绍
1、须知:
- 在python 2中默认编码是 ASCII,而在python 3中默认编码是 unicode
- unicode 分为utf-32 (占4个字节),utf-16(占两个字节),utf-8(占1-4个字节),所以utf-16 是最常用的unicode版本,但是在文件里存的还是utf-8,因为utf8省空间
- 在python 3,encode编码的同时会把stringl变成bytes类型,decode解码的同时会把bytes类型变成string类型
- 在unicode编码中 1个中文字符=2个字节,1个英文字符 = 1个字节,切记:ASCII是不能存中文字符的
- utf-8是可变长字符编码,它是unicode的优化,所有的英文字符依然按ASCII形式存储,所有的中文字符统一是3个字节
- unicode包含了所有国家的字符编码,不同字符编码之间的转换都需要经过unicode的过程
- python本身的默认编码是utf-8
2.py2中的编码和转码的过程,如图:
注:因为unicode是中间编码,任何字符编码之前的转换都必须解码成unicode,在编码成需要转的字符编码
3、py3的字符编码转换
在须知中已经说到python 3的编码,默认是unicode,所以字符编码之间的转换不需要decode过程,直接encode即可,代码如下:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#-*-coding:gb2312 -*- #这个也可以去掉 import sys print(sys.getdefaultencoding()) msg = "我爱北京天安门" #msg_gb2312 = msg.decode("utf-8").encode("gb2312") msg_gb2312 = msg.encode("gb2312") #默认就是unicode,不用再decode,喜大普奔 gb2312_to_unicode = msg_gb2312.decode("gb2312") gb2312_to_utf8 = msg_gb2312.decode("gb2312").encode("utf-8") print(msg) print(msg_gb2312) print(gb2312_to_unicode) print(gb2312_to_utf8) in python3
注:在python 3,encode编码的同时会把stringl变成bytes类型,decode解码的同时会把bytes类型变成string类型,所以你就不难看出encode后的把它变成了一个bytes类型的数据。还有需要特别注意的是:不管是否在python 3的文件开头申明字符编码,只能表示,这个python文件是这个字符编码,文件中的字符串还是unicode
总结:
- uniocode能识别所有字符编码的字符串
- 在python 2中,字符编码之间的转化需要通过unicode才能转换,所以打印时,可以是使用unicode,也可以使用对应的字符编码(文件开头指定编码),打印字符或者字符串,因为py2中没有对字符和字节做明显区分,所以才混导致这样的结果。
- 在python 3中,只有通过Unicode去识别字符的,如果转成编码成对应编码格式了,就直接变成对应编码的bytes类型的字节码,也就是二进制,需要识别,必须解码成Unicode才能识别
- py3中如果说在文件开头已经指定了文件编码,为啥文件中用到的还是uniocde呐?因为py3中的对应编码是二进制,是bytes类型,是不能识别的,能识别的只有Unicode。因为py3中对字符和字节做了明显的区分,所以出现3和4阐述的情况。
函数和过程
面向过程 --->过程--->def
函数式编程-->函数--->def
1.定义函数
def test(): "the funcation details" print("in the test funcation") return 0 def #定义函数的关键字 test #函数名 () #定义形参,我这边没有定义。如果定义的话,可以写成:def test(x): 其中x就是形式参数 "the funcation details" # 文档描述(非必要,但是强烈建议你为你的函数添加详细信息,方便别人阅读) print #泛指代码块或者程序逻辑处理 return #定义返回值
2.定义过程
#定义过程 def test_2(): "the funcation details" print("in the test funcation")
注:从上面两个例子看出来,函数和过程,无非就是函数比过程多了一个return的返回值,其他的也没什么不一样。
3、两者比较:
#定义函数 def test_1(): "the funcation details" print("in the test funcation") return 0 #定义过程 def test_2(): "the funcation details" print("in the test funcation") a = test_1() b = test_2() print(a) print(b) #输出 in the test funcation in the test funcation #输出返回值 0 #没有return ,返回为空 None
小结:不难看出,函数和过程其实在python中没有过多的界限,当有return时,则输出返回值,当没有return,则返回None
过程是没有返回值的函数
为什么使用函数
- 减少重复代码
- 使程序变的可扩展
- 使程序变得易维护
没有函数的编程只是在写逻辑(功能),想脱离函数,重用你的逻辑,唯一的方法就是拷贝
假设我们编写好了一个逻辑(功能),用来以追加的方式写日志:
with open('a.txt','ab') as f: f.write('end action') #现在有三个函数,每个函数在处理完自己的逻辑后,都需要使用上面这个逻辑,那么唯一的方法 就是,拷贝三次这段逻辑 def test1(): print 'test1 starting action...' with open('a.txt','ab') as f: f.write('end action') def test2(): print 'test2 starting action...' with open('a.txt','ab') as f: f.write('end action') def test3(): print 'test3 starting action...' with open('a.txt','ab') as f: f.write('end action')
那么假设有>N个函数都需要使用这段逻辑,你可以拷贝N次吗?
def logger_test(): with open('a.txt','ab') as f: f.write('end action') def test1(): print('test1 starting action...' ) logger_test() def test2(): print ('test2 starting action...' ) logger_test() def test3(): print ('test3 starting action...' ) logger_test()
注:如果遇到代码逻辑变了,用以前拷贝n次的方法,那什么时候拷贝完啊,而且中间有遗漏怎么办,如果用了函数,我们只需要改这个函数的逻辑即可,不用改其他任何地方。
import time def logger_test(): time_format='%Y-%m-%d %X' time_current=time.strftime(time_format) with open('a.txt','ab') as f: f.write('time %s end action' %time_current) def test1(): print ('test1 starting action...' ) logger_test() def test2(): print ('test2 starting action...' ) logger_test() def test3(): print ('test3 starting action...' ) logger_test()
返回值
1.return作用
return其实有两个作用:
- 需要用一个变量来接受程序结束后返回的结果
- 它是作为一个结束符,终止程序运行
def test(): print("in the test_1") return 0 print("the end") #结果中没有打印 x = test() print(x) #输出 in the test_1 #第一次打印 0 #结果返回值
注:从上面的代码可以看出,return 0后面的代码就不执行了,只执行return前面的代码;变量x接受了test()函数结束后的返回结果
2.返回多个值
上面我们试了返回一个常量值,或者说一个对象值,下面我们来试试,不返回值,或者说返回多个值得情况
def test_1(): print("in the test_1") def test_2(): print("in the test_2") return 0 def test_3(): print("in the test_3") return 1,"hello",["qigao","shuaigao"],{"name":"qigao"} x = test_1() y = test_2() z =test_3() print(x) print(y) print(z) #输出 in the test_1 in the test_2 in the test_3 None #x的值 0 #y的值 (1, 'hello', ['qigao', 'shuaigao'], {'name': 'qigao'}) #z的值
从上面的例子可以看出:
- 没有定义,返回值数=0,返回:None
- 只定义了1个返回值,返回值=1 ,返回:定义的那个值,或者说定义的那个object
- 定义了2个以上,返回值 > 1,返回:1个元组(tuple)
提问:这边我们不经意的要问,为什么要有返回值?
因为我们想要想要这个函数的执行结果,这个执行结果会在后面的程序运行过程中需要用到。
有参数的函数
def test(x,y): #x,y是形参 print(x) print(y) test(1,2) #1和2是实参 #输出 1 2
1.位置参数
位置参数需要一一对应,多了少了都不行
从上面的例子可以看出,实际参数和形式参数是一一对应的,如果调换位置,x和y被调用的时,位置也会互换,代码如下:
def test(x,y): print(x) print(y) print("--------互换前-----") test(1,2) print("--------互换后-----") test(2,1) #输出 --------互换前----- 1 2 --------互换后----- 2 1
①多一个参数
def test(x,y): print(x) print(y) print("--------多一个参数----") test(1,2,3) #输出 --------多一个参数---- Traceback (most recent call last): File "D:/PycharmProjects/pyhomework/day3/函数_带参数.py", line 8, in <module> test(1,2,3) TypeError: test() takes 2 positional arguments but 3 were given #test()函数需要传两个实参,你传了三个实参
②少一个参数
ef test(x,y): print(x) print(y) print("--------少一个参数----") test(1) #输出 --------少一个参数---- Traceback (most recent call last): File "D:/PycharmProjects/pyhomework/day3/函数_带参数.py", line 8, in <module> test(1) TypeError: test() missing 1 required positional argument: 'y' #没有给y参数传实参
2.关键字参数
参数顺序可以换吗?形参叫什么名就给赋什么值
def test(x,y): print(x) print(y) print("--------互换前------") test(x=1,y=2) print("--------互换后------") test(y=2,x=1) #输出 --------互换前------ 1 2 --------互换后------ 1 2
可不可以位置参数和关键字参数结合起来用?
①位置参数在前,关键字参数在后
def test(x,y): print(x) print(y) test(1,y=2) #输出 1 2
把后面关键字不传给y,我传给x,代码如下:
def test(x,y): print(x) print(y) test(1,x=2) #输出 Traceback (most recent call last): File "D:/PycharmProjects/pyhomework/day3/函数_带参数.py", line 8, in <module> test(1,x=2) TypeError: test() got multiple values for argument 'x' #给x形参传的值过多
报错的意思是:给形参x传的值过多。这种报错的原因是:实参1已经传给了形参x,后面的x=2又传了一次,所以报错。
②关键字在前,位置参数在后
def test(x,y): print(x) print(y) test(y=2,1) #输出 File "D:/PycharmProjects/pyhomework/day3/函数_带参数.py", line 8 test(y=2,1) ^ SyntaxError: positional argument follows keyword argument # 关键字参数在位置参数的前面
我去,看来这样不行。那我位置参数放前面,中间放关键字参数总行了吧!代码如下:
def test(x,y,z): print(x) print(y) test(1,y=2,3) #输出 File "D:/PycharmProjects/pyhomework/day3/函数_带参数.py", line 8 test(1,y=2,3) ^ SyntaxError: positional argument follows keyword argument
还是一样的错误,只能把关键字参数放最后试试!
def test(x,y,z): print(x) print(y) print(z) test(1,2,z=3) #输出 1 2 3
那我最后两个用关键字参数
def test(x,y,z): print(x) print(y) print(z) test(1,z=2,y=3) #输出 1 3 2
这样也是可以的,所以,得出一个结论:关键字参数是不能写在位置参数前面的。
总结
- 既有关键字,又有位置参数时,是按位置参数的顺序来
- 关键字参数是不能写在位置参数的前面的
3.默认参数
默认参数指的是,我们在传参之前,先给参数制定一个默认的值。当我们调用函数时,默认参数是非必须传递的。
def test(x,y=2): print(x) print(y) print("-----data1----") test(1) #没有给默认参数传值 print("-----data2----") test(1,3) #给默认参数传位置参数 print("-----data3----") test(1,y=3) #给默认参数传关键字参数 #输出 -----data1---- 1 2 -----data2---- 1 3 -----data3---- 1 3
默认参数用途:
- 安装默认软件(def test(x,soft=True))
- 传递一下默认的值(定义mysql的默认端口号:def count(host,port=3306))
参数组
不固定个数的形参,如何定义
参数组的定义
定义多个实参,把实参放到元组里
- 非固定位置参数传参(*args)
- 非固定关键字传参(**kwargs)
下面我们就来说说这两种方式传参:
1、非固定位置参数传参
①功能:接收N个位置参数,转换成元组的形式。
②定义,代码如下:
def test(*args): #形参必须以*开头,args参数名随便定义,但是最好按规范来,定义成args print(args) test(1,2,3,4,5) #输入多个位置参数 #输出 (1, 2, 3, 4, 5) #多个参数转换成元组
③传入列表
def test(*args): print(args) print("-------data1-----") test() #如果什么都不传入的话,则输出空元组 print("-------data2-----") test(*[1,2,3,4,5]) #如果在传入的列表的前面加*,输出的args = tuple([1,2,3,4,5]) print("-------data3-----") test([1,2,3,4,5]) #如果再传入的列表前不加*,则列表被当做单个位置参数,所以输出的结果是元组中的一个元素 #输出 -------data1----- () -------data2----- (1, 2, 3, 4, 5) -------data3----- ([1, 2, 3, 4, 5],)
④位置参数和非固定位置参数
def test(x,*args): print(x) #位置参数 print(args) #非固定参数 test(1,2,3,4,5,6) #输出 1 (2, 3, 4, 5, 6)
从上面看出,第1个参数,被当做位置参数,剩下的被当做非固定位置参数。
第一个值,给x,其他值传ages一个元组
2、非固定关键字传参
①功能:把N个关键字参数(个数不固定),转换成字典形式
②定义,代码如下:
ef test(**kargs): print(kargs) print(kargs["name"]) print(kargs["age"]) test(name="cola",age=3,sex="boy") 运行结果 {'name': 'cola', 'age': 3, 'sex': 'boy'}
③传入字典
def test(**kargs): print(kargs) print(kargs["name"]) print(kargs["age"]) test(name="cola",age=3,sex="boy") #位置参数可用否 test(**{"name":"cola","age":3}) #运行结果 {'name': 'cola', 'age': 3}
④配合位置参数使用
和其他参数一起使用
def test(name,**kargs): print(name) print(kargs) test("baby")#只输入一个参数值 运行结果: baby {} 调用语句 test("baby",age=3,sex="boy") 运行结果: baby {'age': 3, 'sex': 'boy'}
总结,集合内容必须以关键字参数的形式赋值
⑤位置参数、关键字和非固定关键字参数
提示:参数组一定要往最后放
def test(name, age=4, **kwargs): print(name) print(age) print(kwargs) print("----------data1--------") test("Cola", sex='M', hobby='tesla') print("----------data2--------") test("Cola", 5, sex='M', hobby='tesla') print("----------data3--------") test("Cola", sex='M', hobby='tesla', age=18) # age关键字参数放到最后,也可以的 ----------data1-------- Cola 4 {'sex': 'M', 'hobby': 'tesla'} ----------data2-------- Cola 5 {'sex': 'M', 'hobby': 'tesla'} ----------data3-------- Cola 18 {'sex': 'M', 'hobby': 'tesla'}
注:就是说,如果遇到一个关键字传参和非固定关键字传参,前后放的位置是不影响传参的,但是我们一般还是按顺序来。
def test(name, age=4, **kwargs): print(name) print(age) print(kwargs) print("----------data--------") test("Cola",8, sex='M', hobby='tesla', age=18)
输出
----------data3--------
Traceback (most recent call last):
File "C:/Users/huojia/PycharmProjects/Day1/file/tmp.py", line 15, in <module>
test("Cola",8, sex='M', hobby='tesla', age=18)
TypeError: test() got multiple values for argument 'age'
看来是不可以的,为什么?因为age=19是关键字参数,而后面的*args是非固定位置参数,说白了不管*args传入几个字,它的本质都是位置参数,上面我们提到关键字参数是不能再位置参数的前面,所以报错了。
def test(name,age=18,*args,**kargs): print(name) print(age) print(args) print(kargs) test("baby",33,hobby="tesla",sex="boy") --------运行结果--------- baby 33 () {'hobby': 'tesla', 'sex': 'boy'}
关键字参数前输入的参数将被存储到元组里
test("baby",33,"haha","hoho","hehe","huhuih","hkjhkjhk",hobby="tesla",sex="boy") 运行结果 baby 33 ('haha', 'hoho', 'hehe', 'huhuih', 'hkjhkjhk') {'sex': 'boy', 'hobby': 'tesla'}
总结
- 参数分为位置参数、关键字参数、默认参数、非固定位置参数和非固定关键字参数
- 位置参数之前传参,位置是不能调换的,多一个或者少一个参数都是不可以的。
- 关键字参数是不能放在位置参数前面的。
- 函数传参的位置一次是,位置参数,默认参数、非固定位置参数、非固定关键字参数(def test(name,age=18,*args,**kwargs))
- 关键字传参,可以不用考虑位置的前后问题
一个函数体内部调用另外一个函数
调用在定义之前,找不到Logger
def test(name,age=18,*args,**kargs): print(name) print(age) print(args) print(kargs) logger("test4") test("baby",33,"haha","hoho","hehe","huhuih","hkjhkjhk",hobby="tesla",sex="boy") def logger(source): print("from %s "%source) 运行结果 baby 33 ('haha', 'hoho', 'hehe', 'huhuih', 'hkjhkjhk') {'hobby': 'tesla', 'sex': 'boy'} Traceback (most recent call last): File "D:/Python/s014/Day3/def.py", line 10, in <module> test("baby",33,"haha","hoho","hehe","huhuih","hkjhkjhk",hobby="tesla",sex="boy") File "D:/Python/s014/Day3/def.py", line 7, in test logger("test4") NameError: name 'logger' is not defined
程序从上到下执行
def test(name,age=18,*args,**kargs): print(name) print(age) print(args) print(kargs) logger("test4") def logger(source): print("from %s "%source) test("baby",33,"haha","hoho","hehe","huhuih","hkjhkjhk",hobby="tesla",sex="boy") 运行结果: baby 33 ('haha', 'hoho', 'hehe', 'huhuih', 'hkjhkjhk') {'sex': 'boy', 'hobby': 'tesla'} from test4
局部变量
局部变量:顾名思义,指在局部生效,定义在函数体内的变量只能在函数里面生效,出个这个函数体,就不能找到它,这个函数就是这个变量的作用域。
下面我们就用事实说话吧,请看如下代码:
def test(name): print("before change:", name) name = "Alex LLLL" # 局部变量name,只能在这个函数内生效,这个函数就是这个变量的作用域 print("after change:", name) name = "alex" print("-----函数内部调用test-----") test(name) print("------函数外打印name----") print(name)
全局变量
全局变量:指的是在整个程序中都生效的变量,在整个代码的顶层声明。
代码如下:
school = "O edu" # 定义全局变量 def test_1(): print("school:", school) def test_2(): school = "ab linux " print("school:", school) print("------test_1----") test_1() print("------test_2----") test_2() print("----打印school--") print("school:", school) ------test_1---- school: O edu ------test_2---- school: ab linux ----打印school-- school: O edu
局部变量对全局不能产生任何影响,小范围内有效
全局变量的优先级是低于局部变量的,当函数体内没有局部变量,才会去找全局变量。
①函数里面修改全局变量
- 改前用global先声明一下全局变量
- 将全局变量重新赋值
school = "O edu" # 定义全局变量 def test_1(): print("school:", school) def test_2(): global school # 用global关键字声明全局变量 school = "ab linux " #将全局变量重新赋值 print("school:", school) print("------test_1----") test_1() print("------test_2----") test_2() print("----打印school--") print("school:", school)
运行结果
------test_1---- school: O edu ------test_2---- school: ab linux ----打印school-- school: ab linux
③修改列表
names = ['alex', "BB"] # 定义一个列表 def test(): names[0] = "金角大王" print(names) print("--------test-----") test() print("------打印names--") print(names) # 输出 --------test----- ['金角大王', 'BB'] ------打印names-- ['金角大王', 'BB']
从上面的例子可以看出,列表names被当做在函数中全局变量,重新赋值了,是可以被修改的。
小结:1、只有字符串和整数是不可以被修改的,如果修改,需要在函数里面声明global。
2、但是复杂的数据类型,像列表(list)、字典(dict)、集合(set),包括我们后面即将要学的类(class)都是可以修改的。
总结
- 在子程序(函数)中定义的变量称为局部变量,在程序一开始定义的变量称为全局变量。
- 全局变量的作用域是整个程序,局部变量的作用域是定义该变量的子程序(函数)。
- 当全局变量和局部变量同名时:在定义局部变量的子程序内,局部变量起作用;在其他地方,全局变量起作用。
递归
在函数内部,可以调用其他函数。但是一个函数在内部调用自身,这个函数被称为递归函数。
def calc(n): print(n) if int(n/2) == 0: #结束符 return n return calc(int(n/2)) #调用函数自身 m = calc(10) print('----->',m) #输出结果 10 5 2 1 -----> 1 #最后返回的值
总结
- 必须要有一个明确的结束条件。
- 每次进入更深一层的递归时,问题规模相比上次递归都应该少(问题规模:比如你第1次传进的是10,第2次递归应该是9...依次越来越少,不能越来越多)。
- 递归的效率不高,递归层次过多会导致内存溢出(在计算机中,函数调用是通过栈(stack)这种数据结构实现的,每当进入一个函数调用,栈就会加一层栈贞,每当函数返回,栈就会减一层栈贞。由于栈的大小不是无限的,所以递归的次数过多,会导致栈溢出)。
函数式编程介绍
(1 + 2) * 3 - 4
var a = 1 + 2; var b = a * 3; var c = b - 4;
add(1,2).multiply(3).subtract(4)
merge([1,2],[3,4]).sort().search("2")
高阶函数
高阶函数:变量可以指向函数,函数的参数能接收变量,那么一个函数就可以接收另一个函数作为参数。
其实说白了,高阶函数功能就是:把函数本身当做一个参数,传到另一个函数中,然后在这个函数中做处理。代码如下:
#普通函数 def test_1(a,b): return a+b #高阶函数 def test_2(a,b,f): return f(a)+f(b) print("------test_1-----") res = test_1(1,-3) print(res) print("------test_2-----") res = test_2(1,-3,abs) #把abs这个内置函数当做参数传进去 print(res) #输出 ------test_1----- -2 ------test_2----- 4
eval函数
在我们使用一些类似于字典的字符串时,虽然它看起来很像字典,但是在它的最外层多了引号,说明它是字符串,但是我们如何把它转换成字典呐,这就用到了eval()函数,看看eval()函数是如何把字符串转换成字典的,下面就来看看见证奇迹的时刻:
#定义一个类似于字典的字符串,把值赋给arg >>> arg = '''{ 'backend': 'www.oldboy.org', 'record':{ 'server': '100.1.7.9', 'weight': 20, 'maxconn': 30 } }''' #这边根据键取值报错,说明它是一个字符串,不是字典 >>> arg["backend"] Traceback (most recent call last): File "<input>", line 1, in <module> TypeError: string indices must be integers #通过eval()函数把字符串转成字典 >>> arg = eval(arg) #显示的类型是字典 >>> type(arg) <class 'dict'> >>> arg {'record': {'server': '100.1.7.9', 'weight': 20, 'maxconn': 30}, 'backend': 'www.oldboy.org'} #通过键能获取对应的值,说明字符串成功转成字典 >>> arg["backend"] 'www.oldboy.org'