day03_20170514_字符编码/文件存储/函数(一)
第一节:字符编码
一、在会字符编码之前必须要了解的东西:
1、文本编辑器存取文件的原理(nodepad++,pycharm,word)
文本编辑器在存储的时候是怎样的一个过程?----编辑文字,第一件事是要打开一个软件,打开一个软件就是启动了一个进程,这个程序要运行,他是运行在哪里面?在内存里面运行,你基于这个软件敲的一堆文件都是在哪里?都是在内存里面。如果你在编辑文件的过程中突然断电了,数据就没了。
启动软件---》基于这个文本编辑器这个软件写上文字内容,这些东西都是在内存里面,一点击保存,才会把内存里面的东西保存在磁盘中,做运维要关注三个点:硬盘、内存、cpu 要想数据永久保存,应该在硬盘上。硬盘加载到内存之后,cpu才能从内存调用数据进行执行。
2、Python的解释器执行py文件是一个怎样的原理
解释器先找到文件,把文件转成字节码,然后Python解释器去解释字节码。为什么说他是解释型的呢?Java大家都知道,他是混合型语言,为什么叫混合型呢?语言分为编译型和解释型,编译型有什么特点呢?代码是人写的文字,计算机运行的时候是不是只能识别二进制,所以要县编译成二进制,拿编译以后的代码运行。这种编译的好处是快,每次可以直接拿编译后的代码运行。缺点呢?编译时间长;如果代码出现问题,得重新在编译一遍才能执行。那解释型呢?每次执行都要先编译成字节码,然后解释字节码,每次都要经历这样的过程。好处:每次代码出错改一下,直接运行就可以了。方便调试,但是速度慢。因为每次都要经历由人的文字到最后的二进制过程。Java和Python的执行过程类型,也是把代码编译成Java的字节码,然后交给jvm去运行,就是tomcat。不一样的是Java的这个字节码在以后也可以重复运行,Python每次都要重新再编译。Python要想执行程序得干什么事情?在编辑器里面编辑完代码,保存在硬盘上。Python要先去把硬盘上的代码读到内存然后cpu去内存拿代码进行运行,和编辑器编辑文件一样。
二、什么是字符编码
高级语言写的是人能看懂的代码,但是你写代码的目的是干嘛??是让计算机干活,所以作为程序员来说你跟计算机的关系是什么?奴隶主和奴隶的关系。你在给他发指令,你说的是人听懂的话,计算机只能听懂二进制,所以中间需要经历一个翻译的过程,把人认识的字符转成计算机认识的数字,这中间这个转换的过程就是字符编码。转换的过程要遵循一个标准,就是字符到数字是怎样的一种转换关系,这就叫字符编码。
三、 字符编码的发展史
此部分内容上节课已经说过,参见:Python基础(二)
需要强调的是:1、内存中使用的编码是Unicode,用空间换时间(程序都需要加载到内存才能运行,因而内存应该是尽可能的保证快)
2、硬盘中或者网络传输用utf-8,网络I/O延迟或磁盘I/O延迟要远大于与utf-8的转换延迟,而且I/O应该是尽可能的节省带宽,保证数据传输的稳定性。
疑问点1:在内存中写的时候是Unicode,那在保存在文件中的时候怎么变成了保存的格式的编码啊?
内存里面是Unicode,硬盘如果是utf-8的话,他需要一个转化的过程!这个过程是内存往硬盘里面跑,是一个存储的过程!这个过程走的是encode,把Unicode编码编码成utf-8编码,这个过程叫encode,如果从硬盘往内存里面走,这步叫什么?decode!解码的过程!文件内容跑到内存中,毫无疑问就都是Unicode编码。Python解释器要取这个代码进行执行,执行过程会有什么问题啊?
如下代码:
#!/usr/bin/python
# -*- coding:utf-8 -*-s = ‘林’
print(s)
在Python里面写: s = ‘林’ 当我没有执行文件的时候,这句话没有任何意义。但是当Python解释器执行的时候,他才会识别到这是一个字符串,Python解释器有自己规定的语法,他来识别字符串会以什么方式存储,就会开辟一个内存空间来存放这个字符串,这个空间存放的是林的Unicode结果。在Python3中所有的字符串都会被识别成Unicode编码的结果。在Python里接着写:print(s) 我在未执行的时候无任何意义。S是一个变量名,他指向这个内存地址,所以print打印的是s指向内存地址里面的内容。打印到终端上,终端是一个软件,他是要先运行着的,你现在要做的是把打印的内容搬到终端,这是运用到编码的过程,用Unicode就不会乱码,兼容万国!
疑问点2:在内存中的编码是Unicode这句对不对?
在Python里面,是文件还没执行之前,这个过程是Unicode,但是在执行的时候,这个过程中可以人为的转换成任何编码:s2 = s.encode('utf-8')
#!/usr/bin/python
# -*- coding:utf-8 -*-
s = '林'
#Unicode----encode------>utf-8(bytes)
utf-8(bytes)—decode---Unicode
s2 = s.encode('utf-8')
print(s)
print(s2)
输出结果:
林
b'\xe6\x9e\x97'
疑问点3:如下代码,为什么s.encode没有报错,而s.decode就报错了呢?
#!/usr/bin/python
# -*- coding:utf-8 -*-
s = '林'
#Unicode----encode------>utf-8(bytes)
s2 = s.encode('utf-8')
s.decode('utf-8')
输出结果:
Traceback (most recent call last):
File "D:/python2017/20170423/s17_zc/day03/test.py", line 9, in <module>
s.decode('utf-8')
AttributeError: 'str' object has no attribute 'decode'
因为 s = ‘林’ 本来就是unicode,所以你才能进行encode的过程,可以转换成任意的编码。Python文件里面的头信息-*- coding:utf-8 -*- ,他只能控制怎么读文件,跟我识别字符串有关系么?识别字符串是python解释器自己数据类型的问题。Python3中字符串默认存的就是Unicode。
知识补充-------》执行过程中才有字符串的概念:
python3中有两种字符串:一种是Unicode,一种是Unicode转换的bytes。1.str即Unicode。2.bytes即str.encode()的结果。就这两种。
Python2中也有两种形式的字符串:str。Python2中怎么识别字符串类型呢?只要一碰到字符串就会得到一个bytes的格式。
#-*- coding:utf-8 -*-
s = '林'
print repr(s)
#s.encode('utf-8')
print s.decode('utf-8')
print(types)
python2中怎么产生一个Unicode字符串呢?
s=u’林’ 加一个u,就跟Python3概念一样了 这样就可以encode成各种格式字符串了。但是如果decode呢?它本身就是Unicode格式,只能encode。如下图,打印出来的是Unicode
s = u'林'
s.encode('utf-8')
s.encode('gbk')
s.decode('utf-8')
print repr(s)
输出结果:
u'\u6797'
疑问点4:现在有个事情:打印做了一件什么事情?
又相当于开了一个内存空间,把东西丢在里面,又涉及到识别字符编码的问题了。如下两个终端,pycharm不会乱码,而cmd就会乱码,原因是cmd用的是gbk的编码,而pycharm默认的编码是utf-8。
第二节、文件处理
一、文件处理流程:
1打开文件,拿到文件的一个对象
2对文件进行操作
3关闭文件
示范文件内容:a.txt
正趣果上果
Interesting fruit fruit
词:郭婞
曲:陈粒
编曲/混音/和声:燕池
萧:吗子
Words: Guo 婞
Song: Chen tablets
Arrange / Mix / Harmony: Yan Chi
Xiao: Well
你佩桃木降妖剑
他会一招不要脸
哇呀呀呀
输在没有钱
输在没有钱
You wear peach down demon sword
He will shamelessly
Wow yeah
Lost in the absence of money
Lost in the absence of money
你愿终老不羡仙
谁料温柔终老空了长生殿
哎唏唏唏
败给好容颜
败给好容颜
You would like to end the old do not envy cents
Mummy gentle death of the empty palace
Hey Xi Xi
Lost to good appearance
Lost to good appearance
人生在世三万天
趣果有间 孤独无解
苦练含笑半步癫
呐我去给你煮碗面
Life is thirty thousand days
Fun fruit there is no solution between solitude
Hard practicing smiling half-step epilepsy
I'll go and cook your bowl
心怀啮雪大志愿
被人称作小可怜
呜呼呼呼
突样未成年
突样未成年
Heart of the snow big volunteer
Was called a small pitiful
Alas
Sudden sample of minor
Sudden sample of minor
本欲歃血定风月
乌飞兔走光阴只负尾生约
噫嘘嘘嘘
真心怕火炼
真心也怕火炼
The desire to set the wind blood months
Wu Flying Rabbit only time to bear the tail about
噫 boo boo
Really afraid of fire refining
Really afraid of fire
人生在世三万天
趣果有间 孤独无解
苦练含笑半步癫
呐我去给你煮碗面
Life is thirty thousand days
Fun fruit there is no solution between solitude
Hard practicing smiling half-step epilepsy
I'll go and cook your bowl
是非对错二十念
十方观遍 庸人恋阙
自学睡梦罗汉拳
吓 冇知酱紫好危险
Right and wrong twenty read
square view over the Yong love Que
Self - study sleep Lohan boxing
Scare know that a good risk of Jiang Xi
二、文件的打开
python2打开文件有两种方式:
f = open(‘a.txt’)
file(‘a.txt’)#跟open是一回事。
但是在Python3里面就没有file的概念了,统一是open。
#!/usr/bin/python
# -*- coding:utf-8 -*-
f = open('a',encoding='utf-8')#拿到文件对象
print(f)
#对文件开始进行操作
date = f.read()#执行这句的时候报错: 'gbk' codec can't decode byte 0x9c in position 14: illegal multibyte sequence
#分析下:这句话是pycharm在windows平台上运行的,a.txt这个文件存的时候是以utf-8存储的,取的时候用open方式,open默认取当前平台的编码gbk
#也可以自己指定编码
print(date)#这个结果是用户态的数据
f.close()#读完要记着关闭,不然操作系统要一直给你开这个口子,一直占用操作系统的资源
对上面这段话的理解:在公司里面是不是都做过上线的活:上线是怎么一回事?提交代码,让代码在平台上运行起来。怎么运行,得买一台机器,机器上要装操作系统,操作系统上要装软件,程序要运行基于操作系统来运行,程序在运行过程中必须要经过操作系统的硬件操作,应用程序-操作系统-硬件!应用程序能直接操作硬件么?不能,必须经过操作系统,就好比你现在写了这些代码,你是不是在操作文件,文件是存放在哪的?是在硬盘上,你自己写的Python程序要操作硬盘,中间需要经过操作系统,需要发起系统调用,这件事必须要交给操作系统,操作系统给你返回一个对象,就是这个f,拿到f就可以对操作系统说你想干什么事,比如说读这个文件,这个请求发出去以后是不是发给操作系统了,操作系统帮你从硬盘里面取出文件来,把内容再返回给你。这就涉及到内核态和用户态的转换。内核态和用户态指的是cpu的两种运行状态,如果cpu此时运行的状态处于内核态的话,那操作系统有效,那操作系统就可以操作硬件。而用户态就属于你自己写的应用程序了,应用程序不能直接操作硬件,这就是他俩的区别。但是应用程序在运行的过程中,有操作硬件的这种需求,cpu就会发系统调用切到内核态,操作硬件拿数据再返回。
如下代码:
f = open('a',encoding='utf-8')#拿到文件对象
print(f)
也可写成:
f = open('a',’r’,encoding='utf-8')#拿到文件对象
print(f)
或者:
f = open('a',mode=’r’,encoding='utf-8')#拿到文件对象
print(f)
返回结果就是f这个对象的一个介绍。
如下代码:data2为什么没有读到内容?
f = open('a',encoding='utf-8')
print(f)
date1 = f.read()
print(date1)
print('===========================')
data2 = f.read()
print('data2======>',data2) #data2没有读到内容
f.close()
分析:date1在读的时候,文件中光标是第一个位置,读完到输出,光标处于文件内容的最后一个位置,data2在开始读,就是从光标处开始继续读的,已经没内容可读了。所以data2不显示内容。
怎么解决这种情况呢?
控制文件光标的移动。f.seek(0) 0表示光标回到文件开头
#!/usr/bin/python
# -*- coding:utf-8 -*-
f = open('a',encoding='utf-8')
print(f)
date1 = f.read()
print(date1)
print('===========================')
f.seek(0)
data2 = f.read()
print('data2======>',data2) #data2没有读到内容
f.close()
三、文件的read有哪些属性
Buffer的作用:官方解释还没有写到硬盘上的。内存往硬盘上写要经过一个速度差,内存速度快,硬盘速度慢,一个数据比方就一个字节,你写一次就经过一次io延迟,数据量非常少,一个字节来写一次,又一个字节来又写一次,总共前后写了三个字节,这样就经历了三次io延迟,所以怎么解决?一个字节来,数据量小先别往磁盘上写,攒起来,攒一大波,然后一起写,这样就经历一次io延迟,这是buffer的作用。
Cache:就是把cpu经常取的数据放在内存中
Closed:是判断文件是否关闭
Encoding:是看文件编码
Name:查看文件名
Readable:判断文件打开的模式是否可读
Read:一次读所有
Readline:一次读一行 print(f.readline(),end=’’) end的意思是不打印最后的换行符,不写的话,默认有个换行符
Readlines:读取所有行的内容,然后存成列表的形式
以下内容为以写的方式打开文件:文件存在则清空,文件不存在则创建
f = open('a','w',encoding='utf-8')
f = open('b.txt','r',encoding='utf-8')#以读的方式打开文件,文件不存在则报错,换成以写的方式打开文件,就会创建一个新文件
四、文件的write有哪些属性
f.write()#以文本的方式写
f.write('1111\n222')#\n可以换行
f.writeable()#判断文件是否可写
f.writelines(['22222\n','1111\n','vvv\n'])#文件存在之后,只修改,不重建
文件的修改:读原文件内容,然后一行一行往新文件里面写
#把第一行内容改成3333
read_f = open('a','r')
write_f = open('b','w')
for line in read_f.readlines():
if line.startswith('22222'):
line='3333\n'
write_f.write(line)
现在是有两个文件,那想新文件覆盖老文件,怎么办呢?
import os
read_f = open('a','r')
write_f = open('b','w')
for line in read_f.readlines():
if line.startswith('22222'):
line='3333\n'
write_f.write(line)
write_f.close()
read_f.close()
os.rename('b','.b')
f = open('a','r+')#其中的r+和w+表示读的时候可以写,写的时候可以读,还有a+,a是写的一种方式,加上+号,表示追加的时候也能读
打开文件之后要记得关闭
f = open('a','w+')#其中的r+和w+表示读的时候可以写,写的时候可以读,还有a+,a是写的一种方式,加上+号,表示追加的时候也能读
f.write('1111\n')
f.close()
如果忘记关闭怎么办?推荐with,称为上下文管理
with open('a','r',encoding='utf-8') as f:
print(f.read())
#还可以打开两个文件:
with open('a','r',encoding='utf-8') as f,open('b') as b_f:
print(f.read())
知识点补充:
for i in range(3):
print(i)
continue
#break
else:#当for循环不被break打断,就会执行else的代码
print('===============>')
#使用场景:打开一个旧文件,向新的文件里面写内容,当写完之后您怎么确认他写完了呢?可以输出一句话来告诉你全部写完了,不然中途出现中断你也不知道
with open('a','r',encoding='utf-8') as read_f,\
open('b','w',encoding='utf-8') as write_f:
for line in read_f:
write_f.write(line)
else:
print('write successful')
#While也可以和else一起用:
i = 0
while i < 5:
print(i)
i+=1
if i == 3:
break
else:
print('----->')
rb的模式:按照字节码的形式读出来
with open('a','rb') as f:
print(f.read())
输出结果:
b'\xe4\xbd\xa0\xe6\x84\x81\xe5\x95\xa5'
三个字节一个汉字,如果我想看中文怎么看呢?
with open('a','rb') as f:
print(f.read().decode())
输出结果:你瞅啥
Wb的模式:以bytes的形式写
with open('c','wb') as f:
f.write('哈哈哈'.encode('utf-8'))
如果不进行encode,会报错
Rb是直接把硬盘上的bytes读到内存中去,f.write('哈哈哈'.encode('utf-8')),其中wb是以bytes二进制方式直接写到硬盘上,以utf-8编码方式把他扔到硬盘上的,硬盘上存的就是utf-8格式的bytes,再次取的时候还是这个过程。
执行如下报错:
f = open('sb.jpg','r',encoding='utf-8')#文本的方式读不了二进制
print(f.read())
输出结果:
D:\tools\python\python.exe D:/python2017/20170423/s17_zc/day03/文件处理.py
Traceback (most recent call last):
File "D:/python2017/20170423/s17_zc/day03/文件处理.py", line 84, in <module>
f = open('sb.jpg','r',encoding='utf-8')#文本的方式读不了二进制
FileNotFoundError: [Errno 2] No such file or directory: 'sb.jpg'
执行如下就可以:因为任何文件躺在硬盘里面都是bytes,所以rb的方式可以打开任何文件。
with open('sb.jpg','rb') as read_f:
print(read_f.read())
#如何把这个文件写入到新的文件中呢?
with open('sb.jpg','rb') as read_f,\
open('sb_1.jpg','wb') as write_f:
data = read_f.read()
write_f.write(data)
不常用的了解的知识点:
文件内光标移动
1)Read可以指定读几个字符
with open('a','r',encoding='utf-8') as f:
print(f.read(2))#数字指的是读的是字符
#如果是rb的模式呢?读出了两个bytes
with open('a','rb') as f:
print(f.read(2))#数字指的是读的是字符
输出结果:
b'\xe4\xbd'
2)seek也是移动光标,如下代码:
with open('a','r',encoding='utf-8') as f:
f.seek(2)#seek内指定的数字代表字节
print(f.read())
seek使光标移动了两个bytes,打印剩余的7个bytes,三个bytes组成一个汉字,但是七个就有一个无法识别,所以报错:UnicodeDecodeError: 'utf-8' codec can't decode byte 0xa0 in position 0: invalid start byte
如果seek(3)呢?这就没问题了。
注意:seek的特点就是光标从文件开头作为起始位置,开始数几位。哪怕read之后光标在最后位置,seek也会跑去起始位置。
在Python2中:seek(3,0)seek有两个参数,第一个参数控制的是几个bytes,第二个参数是控制光标从哪个位置开始。0代表起始位置,1代表当前位置,2代表以文件结尾作为起始。
在Python3中,seek(3,0)这种模式只能以bytes模式使用
with open('a','rb') as f:
f.read(1)
print(f.tell())
f.seek(2,1)#1代表以当前光标所在的位置为开始,往后移动2个bytes
print(f.tell)
with open('a','rb') as f:
print(f.read())
f.seek(-1,2)#2表示以文件内容结尾开始,往前移动一个位置
print(f.tell())
#模拟 tail -f access.log
import time
with open('access.log','r',encoding='utf-8') as f:
f.seek(0,2)
while True:
line = f.readline()
if line:
print('新增一行日志',line)
time.sleep(0.5)
如果不想用空格,可使用strip
#模拟 tail -f access.log
import time
with open('access.log','r',encoding='utf-8') as f:
f.seek(0,2)
while True:
line = f.readline().strip()
if line:
print('新增一行日志',line)
time.sleep(0.5)
3)Tell:告诉我当前光标所在的位置
with open('a','r',encoding='utf-8') as f:
f.seek(3)#默认情况,是以文件起始位置作为开始,往后移动3个bytes
print(f.tell())
print(f.read())
4)truncate代表截断文件,指的也是字节, 只有read读取字符。truncate是一种写操作
with open('a','r+',encoding='utf-8') as f:
f.truncate(3)#截取前三个字节,如果截取1个字节,组不成汉字,就会出现乱码
第三节、函数
没有函数会有什么问题?
- 无组织,无结构,代码冗余
- 可读性差
- 无法统一管理且维护成本高
在Python中,函数分为两类:内置函数,自定义函数
内置函数:python解释器自带的。比如 sum,max,min,len ,这些函数怎么用呢?加括号,就表示在调用函数。Sum()拿len举例:a = len(‘hello’) Print(a)
自定义函数:Def是自定义函数的关键字
现在练习:
#打印
******
******
******
hello world
******
******
******
思路:打印*做成一个函数,打印helloWord定义成一个函数
#自定义函数
def print_star():
print('#'*6)
def print_msg():
print('hello world')
print_star()
print_star()
print_star()
print_msg()
print_star()
print_star()
print_star()
为什么要先定义函数?
因为内置的函数是内置到解释器里面,解释器已启动,这些东西自然就存在了,而自定义的函数在Python解释器启动之后,函数就没了!
要先定义后使用,否则程序报 函数未定义!
foo()#为什么要定义函数?:先定义后使用,如果没有定义而直接使用,就相当于引用了一个不存在的变量名 def foo(): print('from foo') print(foo) 输出结果:
NameError: name 'foo' is not defined
疑问:你调用函数,跟name有什么关系呢?
调用函数是先通过名字来找函数,然后加上括号进行调用的。函数名本质就是一种名字,变量名也是一种名字,定义函数本质就是定义一个名字,这是这个名字指向一个地址,地址存放的是一段程序。
函数的使用分为两个阶段:
定义阶段和使用阶段
注意以后自己写程序:要先定义函数,准备好自己要使用的工具,然后在拼接自己的逻辑。定义完自己的函数,要加上注释,增加可读性。定义完了就开始使用,使用就是循环啊,什么的。
定义函数的语法:
#语法
def 函数名(参数1,参数2,...):
"""文档注释"""
函数体
return 值
返回值的功能:
如果函数的功能是执行函数体中一堆的代码,最后只得出一个结果,这时候一定要有返回值,而之前写的函数函数体中的代码只是在执行一堆操作,并没有返回结果。什么时候会有返回值呢?自定义的不会,内置的会!比如,len这个函数,你要查看长度,len(‘hello’) ,得把他赋给一个值,x=len(‘hello’) 然后在打印x
定义函数的三种形式:
1、 无参函数
什么是无参函数?就是函数名后面的()里面什么东西都不写,称为无参函数
什么时候定义无参函数呢?如果函数的功能仅仅只是执行一些操作而已,就定义成无参函数,无参函数通常都没有返回值
def print_star():
print('#'*6)
2、有参函数:函数的功能的执行要依赖于外部传入的参数,这时候函数才能运行,有参函数通常都有返回值
怎么定义有参函数?补充知识:三元表达式
# 三元表达式
x=10
y=2
if x > y:
print(x)
else:
print(y)
res=x if x > y else y #条件成立的写在if条件左边,不成立的写在if条件右边
print(res)
求两个数的最大值
def my_max(x,y):
res=x if x >y else y
return res
空函数:
def auth():
"""认证功能"""
pass
auth()
执行这个函数,不返回任何结果。Pass就是什么都不做,所以这个函数体就是什么都不执行。
那为什么空函数非常有用呢?这周的作业是sql解析,就是自己定义语法,可以进行增删改查的操作。现在学过函数了你打算怎么写,很明显增删改查是四种功能,那该怎么写?
以后写程序不要上来就写,先想好了你这程序都有啥功能,都有啥架构,然后你再去写。
比如这个sql解析的,你先这样写:
def insert():
"""插入功能"""
pass
def select():
"""查询功能"""
pass
def delete():
"""删除功能"""
pass
def update():
"""更新功能"""
pass
这样写完之后,你的程序体系就立马出来了。还有好处就是你哪个功能有思路,你就先写哪个。删除比较简单,你就先做删除,然后再做其他的。这样你就统筹规划了你的一个编程的
一个任务。
调用函数:
定义函数的目的就是调用函数。
调用无参函数的时候就直接函数名加括号,定义时无参,调用时也无参。
调用有参函数,定义时候有参数,调用的时候也必须传参数。
def foo():
print('from foo')
def bar(name):
print('bar===>',name)
#按照有参和无参可以将函数调用分两种
foo() #定义时无参,调用时也无需传入参数
bar('egon') #定义时有参,调用时也必须有参数
#按照函数的调用形式和出现的位置,分三种
1)foo() 调用函数的语句形式,什么叫做语句形式呢?就只是一段操作。
2)调用函数的表达式形式。什么是表达式呢?x+1
def my_max(x,y):
res=x if x >y else y
return res
# res=my_max(1,2)*10000000 #调用函数的表达式形式
# print(res)
3)把函数调用当做另一个函数的参数
练习:现在让你求三个数中的最大值
def my_max(x,y):
res=x if x >y else y
return res
res=my_max(my_max(10,20),30) #把函数调用当中另外一个函数的参数
print(res)
函数的返回值:
以下三种情况返回值都为None:
1)没有return
2)return什么都不写
3)return None
def foo():
print('from foo')
return None
res=foo()
print(res)
def foo():
print('from foo')
return
res=foo()
print(res)
def foo():
print('from foo')
res=foo()
print(res)
这三种情况返回结果都是:
from foo
None
Return一个值,函数调用返回的结果就是这个值
def foo():
print('from foo')
x=1
return x
res=foo()
print(res)
return返回多个值,多个值之间用逗号隔开
#return 值1,值2,值3,... 返回结果:(值1,值2,值3,...)
def foo():
print('from foo')
x=1
return 1,[2,3],(4,5),{}
res=foo()
print(res) #打印结果:(1,[2,3],(4,5),{})
注意:Python的返回值没有类型限制,可是数字,元组,字典。。
序列的解压:就是不要res这个整体,要里面分别的元素。
例:
t = (1,2,3)
a,b,c=t
print(a)
print(b)
print(c)
练习完这个 ,我们来把res里面的元素都拿出来:
def foo():
print('from foo')
x=1
return 1,[2,3],(4,5),{}
res=foo()
print(res) #打印结果:(1,[2,3],(4,5),{})
a,b,c,d=foo()#这个值不能少,如果是三个就会报错
print(d)
练习:模拟下面的是函数返回值,我就想要开头的值和结尾的值,怎么操作呢?
t=(1,2,3)
a,_,_=t #用下划线进行占位
print(a)
值少的我们可以采用上面的办法,如果值多呢?只要开头和结尾的
t=(1,2,3,4,5,6,7,8,9)
a,*_,c=t
print(a)
print(c)
函数参数:
从大的角度去看,函数参数分为两种:
形式参数简称形参,还要一个实际参数,简称实参。
我先定义一个形参:
def foo(x,y): #x=1,y=2
print(x)
print(y)
这个x 、y就是形参。
什么叫实参呢?调用函数时,括号里面写的值,就称为实参。
foo(1,2)
以你目前的了解来说,形参和实参到底是怎么一回事?
函数分为定义阶段和调用阶段,定义阶段的x和y只是相当声明一些变量,所以形参就相当于变量名,而实参就是值。实参是怎么样运行的呢?是一个赋值的过程。实参和形参之间是一种怎样的关系呢?实参的值要传给形参。
#定义阶段
# def foo(x,y): #x=1,y=2
# print(x)
# print(y)
#调用阶段
# foo(1,2)
定义阶段,形参会不会占用内存空间。是不会占用的,就只是一些符号而已,在调用阶段,实参会传值给他,1传给了x,x才会占用内存空间,只有在函数调用的时候,形参开辟内存空间,什么时候内存空间被释放掉呢?函数执行完之后这个x=1也就没必要存在了,这个内存地址就释放掉了。
补充:函数定义阶段到底干了一件什么事?
如下:
def bar(x,y):
Erqererer
werererr
执行一下会报错么?
是不会的!因为定义函数阶段相当于就是声明了一个变量名,指向了一段函数的内存地址而已。他只检测你函数体里面的语法到底合不合理,并不会执行。那如上面那段代码函数体里面有语法错误么?根本不存在语法,他只是名字,没有规则的名字而已。
详细的区分:
函数的参数分为五种:
位置参数
关键字参数
默认参数
可变长参数(*args,**kwargs)
命名关键字参数
位置参数:
按位置定义的参数。可以按位置定义形参,也可以按位置定义实参。
位置形参的特点:有几个参数就必须传几个值。
位置实参的特点:与形参一一对应。
位置参数练习:
def foo(x,y,z):#位置形参:必须被传值的参数
print(x,y,z)
# foo(1,2,3)
foo(1,2,3) #位置实参数:与形参一一对应
关键字参数:key=value
精准的赋值,跟位置没有关系。
def foo(x,y,z):
print(x,y,z)
foo(z=3,x=1,y=2)
关键字参数需要注意的问题是什么?
可以按照位置定义实参,也可以按照关键字定义实参,那么是否可以混合用呢?
# foo(1,z=3,y=2) #正确
是可以的,但是:
# foo(x=1,2,z=3) #错误
为什么这样写就会报错呢?所以需要注意的是:
1) 关键字实参必须在位置实参后面
执行如下代码,为何报错呢?
foo(1,x=1,y=2,z=3)
2) 不能重复对一个形参传值
默认参数:
def register(name,age,sex='male'): #形参:默认参数
print(name,age,sex)
register('asb',age=40)
register('a1sb',39)
register('a2sb',30)
register('a3sb',29)
默认参数的特点是:调用的时候就可以不传值了。
什么时候用默认参数呢?
默认函数在定义函数的时候就已经给他赋值了。那么在调用函数的就省事了。就无需传值了。
def register(name,age,sex='male'): #形参:默认参数
print(name,age,sex)
register('asb',age=40)
register('a1sb',39)
register('a2sb',30)
register('a3sb',29)
register('钢蛋',20,'female')
register('钢蛋',sex='female',age=19)
默认参数需要注意的问题:
一:默认参数必须跟在非默认参数后
def register(sex='male',name,age): #在定义阶段就会报错
print(name,age,sex)
(了解)二:默认参数在定义阶段就已经赋值了,而且只在定义阶段赋值一次
a=100000000
def foo(x,y=a):
print(x,y)
a=0
foo(1)
a 被赋值了两次,那么a取的是定义时候赋的那次值。
三、默认参数一定要定义成不可变类型。
通常情况下应该把什么样的参数定义成默认参数?
比较固定的并且变化比较少的适合定义成默认参数。
可变长参数:在调用时,实参的长度是可变的。
在形参角度能够声明可以接受任意长度的实参。而实参又有两种形式。按位置和关键字。相应的你的形参也应该有两种形式来处理这两种形式。先说实参的第一种形式:按位置定义实参:
def foo(x,y,*args): #*会把溢出的按位置定义的实参都接收,以元组的形式赋值给args
print(x,y)
print(args)
foo(1,2,3,4,5)
简单说一下他的应用:
Def add(x,y):
Res=x+y
Return res
Print(add(1,2))
输出结果为3
现在有个问题:我不知道add()里面有多少个值,现在来求和,求任意长度的和的结果
def add(*args):
res=0
for i in args:
res+=i
return res
print(add(1,2,3,4))
针对关键字呢?
def foo(x, y, **kwargs): # **会把溢出的按关键字定义的实参都接收,以字典的形式赋值给kwargs
print(x, y)
print(kwargs)
foo(1,2,a=1,name='egon',age=18)
命名关键字参数
我是否存在这样的需求:如下代码:我想取身高和性别该怎么取呢?
Def foo (name,age,**kwargs):
Print(name,age)
Print(kwargs[‘sex’])
Print(kwargs[‘height’])
Foo(‘egon’,18,sex=’male’,height=’185’)
问题:如果我最后调的实参为:Foo(‘egon’,18,sex=’male’)
那么我想看height的时候就会报错,我该怎么操作呢?进行一下判断,如果要看的参数在可变长度行参中,就打印,避免报错
def foo(name,age,**kwargs):
print(name,age)
if 'sex' in kwargs:
print(kwargs['sex'])
if 'height' in kwargs:
print(kwargs['height'])
foo('egon',18,sex='male',height='185')
接下来的问题:你要每一个key都要做这样的判断么?这就涉及到命名关键字了
def foo(name,age,*,sex,height):#*后面的sex和height就叫做命名关键字参数
print(name,age)
print(sex)
print(height)
foo(‘egon’,17)
运行之后报错,少了关键字sex和height
那么加上缺少的:
foo(‘egon’,17,‘male’,’185’)
执行之后还是报错,提示:foo() takes 2 positional arguments but 4 were ginven
这就是命名关键字的特点:*后面定义的参数为命名关键字参数,这类参数,在使用的时候,必须传值,而且必须以关键字实参的形式去传值。
def foo(name,age,*,sex,height):#*后面的sex和height就叫做命名关键字参数
print(name,age)
print(sex)
print(height)
foo(‘egon’,17,height=‘male’,sex=’185’)#传进去的关键字里面必须有height,和sex
之前讲默认参数必须在位置参数后面,如下代码sex在位置参数之前,为何如下代码也可以正确执行???
def foo(name,age,*,sex=‘male’,height):#*后面的sex和height就叫做命名关键字参数
print(name,age)
print(sex)
print(height)
foo(‘egon’,17,height=‘185’)
注意:这不是默认参数,这是命名关键字参数。
以上,所有参数都学完了,这些参数是可以混着用的。其顺序是:
def foo(name,age=10,*args,sex='male',height,**kwargs):
但是都写在一起是没有意义的,因为他们彼此之间会互相的取消对方的实际意义。
def foo(name,age=10,*args,sex='male',height,**kwargs):
print(name)
print(age)
print(args)
print(sex)
print(height)
print(kwargs)
foo('alex',1,2,3,4,5,sex='female',height='150',a=1,b=2,c=3)
打印出来的结果不对。所以需要更改,把age=10放到*args后面,以命名关键字形式
def foo(name, *args, age=10,sex='male',height,**kwargs):
print(name)
print(age)
print(args)
print(sex)
print(height)
print(kwargs)
foo('alex',1,2,3,4,5,sex='female',height='150',a=1,b=2,c=3)