什么是函数?
把一堆代码放一起就叫函数
函数用来干什么?
不复写代码,提高代码重复利用程度
怎么定义以及调用函数:
def fun1(): #定义函数 print('这是一个函数') #函数体,里面什么都不想实现就写个 pass fun1() #调用:函数名加括号,如果里面有参数必填的就得填
参数
参数分两种,形参和实参
形参:形式参数,函数内使用的参数
实参:实际参数,传到函数内的参数值
def calc(a,b):#形参,形式参数 print(a,b) return a+b #返回值 result = calc(1,2) #实参,实际参数
返回值
返回值的作用是返回函数处理的结果,并且,函数遇见 return 则立即结束,也就是 return 后的下一行代码,是不会执行的
def calc(a,b):#形参,形式参数 print(a,b) return a+b #返回值 result = calc(1,2) #实参,实际参数
小练习1:判断输入的值,是否为小数:
思路:
- 判断是否只有一个小数点
- 小数点左边的是一个整数,小数点右边的也是一个整数
- 小数点右边的也是一个整数,左边要以负号开头,只有一个负号,负号后面是整数
num = input('请输入价格:') def check_float(num): num = str(num) if num.count('.')==1: left,right = num.split('.') if left.isdigit() and right.isdigit(): return True elif left.startswith('-') and left[1:].isdigit() and right.isdigit(): return True return False check_float(num)
小练习2:函数定义文件读写,格式为 json 格式
import json def write_file(d,file): with open(file,'w',encoding='utf-8') as fw: json.dump(d,fw,indent=4,ensure_ascii=False) def read_file(file): with open(file,encoding='utf-8') as fw: return json.load(fw)
返回值有多个
有种情况是,我们函数处理完,需要返回多个值,怎么办呢?
def get_file(age,name,addr): age+=5 name = 'szz_'+name addr = 'bejing_'+addr return age,name,addr
直接 return 就好了,用逗号隔开就成,返回的结果是一个元组的形式
(24, 'szz_小黑', 'bejing_天通苑')
那么这几个返回值如何取用呢?
age,name,addr = get_file(19,'小黑','天通苑')
这样的话,我们的 age 就等于函数返回的 age,name 就是函数返回的 name ,addr 就是函数返回的 addr。
如果拿 1 个变量来接收,那么该变量当然就会是这个元组
变量
变量分为局部变量和全局变量,那么这两者有什么区别呢?
简单理解为全局变量是所有的函数都能用,局部变量只能给自己本身函数内部使用,假设函数内用的变量在函数体内找不到,那么它就会去全局变量内找,找不到就会报语法错误。
全局变量不在函数体内定义,局部变量在函数体内定义。
一般来讲,全局变量放在最上边
name = 'abc' def fun1(): name = 'abc2' return name print('全局变量:%s'%name) print('局部变量:%s'%fun1()) 结果: 全局变量:abc 局部变量:abc2
那么,假设我在函数内不单单是想取用这个全局变量值,而且想对其进行修改,要怎么办呢?那就要在函数体内声明一个全局变量,具体如下:
money = 0 def dsk(): global money money+=500 def ldd(): global money money -= 1000 print('money之前的',money) dsk() print('挣钱之后的',money) ldd() print('花钱之后的',money) 结果: money之前的 0 挣钱之后的 500 花钱之后的 -500
但是这种方式尽量少用,为什么呢?因为假设外面定义了一个全局变量,在程序内部修改了我这个全局变量的值,很难找到出错的根源
那么我们用几个例子来理解一下,全局变量以及局部变量
money = 500 def test(consume): return money - consume def test1(money): return test(money) + money money = test1(money) print(money)
结果是怎样呢?应该为 500
过程:最后 print(money) money 来自于 test1(money) ,看 test1 内,返回的是 test(money)+money ,money 在这个函数内找不到,那么用全局变量 500 ,所以 test(500)+500;再去看 test(500),test(500) 返回的是money - consume = 500 - 500 = 0;所以最终值为 0+500 = 500
2、
def test(): global a a = 5 def test1(): c = a + 5 return c #test() res = test1() print(res)
结果:NameError: name 'a' is not defined
为什么呢?因为 res = test1(),调用 test1 函数, c = a+5 ,a 在函数未定义,且全局变量内找不到,因为 test() 函数未调用。global a a=5 并未生效,所以一定会报错。
那么假设在 print 之前,调用下 test(),就有结果了,结果为:10
那么是不是所有的变量,我们在函数内要修改,都要进行全局变量声明呢?答案是 no!可以根据下标啥的修改的,就可以在函数内修改,其他的不行
可变类型:dict、list、set :字典,列表,集合可以直接修改的就不需要全局变量声明,直接在函数内修改就行
不可类型:int、str、tuple:数字,字符串,元组就必须要全局变量声明,才能够修改
例如:
stus = ['xiaojun','xiaohei','xiaobai'] stu_info = { 'name':'xiaojun', 'age':19 } stus2 = {'xiaohei','xiaobai'} def add_stu(): name = input('name:') age = input('age:') # stus.append(name) stu_info[name]=age def select_stu(): name = input('name:') print('你的学号是%s'%stus.index(name))
默认参数&参数组
上面我们讲过,函数的参数。函数的参数可以有,也可以没有:
def say_hello(): print(word)
1、必填参数/位置参数
那么有参数的情况是怎样的呢?我们假定有个函数,参数传入什么,就打印什么
def say_hello(word='hello'): print(word) say_hello(123)
结果:
123
2、默认值参数
参数有个默认值就叫做默认值参数,看下面 word 在函数定义括号内,传入了一个默认值 'hello'
def say_hello(word='hello'): print(word) say_hello(123) say_hello()
结果:
123
hello
上面,我们看到,在函数定义的括号内,加入了 word='hello' ,那么这个参数就叫做默认参数,假设调用函数内,不传值,就会使用默认值,但是传新值该参数就是用新值进行使用
那么默认值参数有什么用呢?定义了两个参数,一个叫 file ,是文件名,content 是默认值参数,也就是输入的内容,实现一个函数内进行文件读写操作,假设 content 不传内容,就执行读文件操作;传新值的话就写入到文件内:
def op_file(file,content=None): import json if content: with open(file,'w',encoding='utf-8') as f: json.dump(content, f) else: with open(file,encoding='utf-8') as f: return json.load(f)
不明显的话,看下面的更直观:
def op_file2(file,content='没有传文件内容'): if content=='没有传文件内容': with open(file,encoding='utf-8') as f: result = f.read() return result else: with open(file,'w',encoding='utf-8') as f: f.write(content)
但是,这个还是需要完善的,因为可以校验文件名为不为空的情况,想写的可以继续写
3、参数组
假设我们自动化程序,写了一个函数,实现了发邮件功能,一开始的测试只有我们一个,那么我们就传一个人进去,name1,但是随着测试团队的壮大,这个邮件要发的人越来越多,那么就得加 name,而且如果有 100 人,就得在函数定义内加 100 个参数,未免有点太笨拙,咋办呢?
def send_mail(name1,name2,name3): print('给%s'%name1,name2,name3) send_mail('abc','def','igh')
这时候就是我们参数组大显身手的时候了,我们可以将 name 这个参数定义为一个参数组,里面的具体 name 数目不详,这样就不用每次都去手动添加这个 name 参数你,那么怎么实现呢?
用 *args 表示(*args 并不是固定写法,只是约定如此,也可以定义为其他的,比如 *names)
def send_mail(*args): print(args) send_mail() send_mail('abc') send_mail([1,2,3],'bcd','efg')
结果:
() ('abc',) ([1, 2, 3], 'bcd', 'efg')
从上述结果我们不难看出,参数组为 *args 的写法,是传入了一个元组。而且 *args 可以为空,不会报错。
那么,上面的函数没写规范,既然传进来的是个元组,循环取值发送就 ok:
def send_mail(*args): for name in args: print('给 %s 发邮件'%name)
4、关键字参数
这里先插一嘴,假设处理测试人员信息的函数,用位置参数:
def info(name,age,sex,addr,phone,qq,mail): print(name,age,sex,addr,phone,qq,mail) info('xiaohei',18,'man','guanzhou',110,139,'123@163.com')
这样的话很容易出问题的是,假设 age 我们传了一个 'women' ,那就完全错误了,很难对应起来。
那么我们可以怎么避免这种不对应的情况呢?
可以在调用的时候,指定关键字传什么,比如 addr = '北京',这样的话,就不需要一一对应,这就叫关键字传参
def szz(name,age,sex,addr,phone,qq,mail): print(name,age,sex) szz(addr='北京',qq=1110,mail='abcc@163.com',name='abc',phone=11111,sex='dsdfs',age=11)
但是也可以位置参数和关键字参数混用:
def szz(name,age,sex,addr,phone,qq,mail): print(name,age,sex) info('xiaohei',18,addr='beijing',phone=186,mail='aaa@163.com',sex='男',qq='1111')
可以看到,前两个我们用的是位置参数,传的是 'xiaohei' 和 18,后面所有的用的都是关键字参数,但是要注意:关键字参数和位置参数不能交叉使用,因为很难对应:
info('xiaohei',addr='beijing',18,phone=186,mail='aaa@163.com',sex='男',qq='1111')
看一下,第一个我们使用关键字参数,给了 name ,然而第二个是关键字参数,我们是能对应上,后面又来了了个未知参数 18 ,没法对应上,所以混用的方式只能是:位置参数+关键字参数,用了一个关键字参数,后面的全部都得用关键字参数,否则会报语法错误:SyntaxError: positional argument follows keyword argument
所以:
- 调用的函数的时候,可以全部都用位置参数,位置是一一对应的,必须按照位置来传参
- 也可以全部都用关键字参数,指定关键字,不需要按照顺序来
- 也可以一起用,但是要先写位置参数,再写关键字参数,关键字参数后面不能再出现位置参数
关键字参数有种更牛批的方式,还是上面的例子:
def xiaohei(**info): print(info) xiaohei() xiaohei(1,2,3) xiaohei(name='小黑',addr='北京') xiaohei(file_name='a.json',mode='w',conent='abcc',time='1')
结果:
接收到的数据形式是字典形式,可以传空值,个数不限制
{} {'name': '小黑', 'addr': '北京'} {'file_name': 'a.json', 'mode': 'w', 'conent': 'abcc', 'time': '1'}
但是,有个问题,当传入的为位置参数,不能传关键字参数!!!
xiaohei(1,2,3)
会报错:TypeError: xiaohei() takes 0 positional arguments but 3 were given
所以:**是关键字传参,会把多余的参数放进字典内
归纳一下:
def xiaobai(name,age=None,*args,**kwargs): print(name) print(age) print(args) print(kwargs) xiaobai('xiaobai',18,'beijing','shanghai',money=500, func='xiaobai')
结果:
xiaobai 18 ('beijing', 'shanghai') {'money': 500, 'func': 'xiaobai'}
参数传递,位置参数和默认值参数优先,再多余的位置参数会进入参数组内(*arg),再多余的"默认值参数"会进入关键字参数(**kwargs)内
5、解包
再着重说一下 * 和 ** 的作用
假设我们连接数据库,原来我们只能这么传,一个一个对应:
def op_mysql(host,port,user,passwd,db): print(host) print(port) print(user) print(passwd) print(db)
op_mysql('127.0.0.1',3306,'root','123456','szz')
1、那么我们用 *args 的方式:
db_info = ['127.0.0.1',3306,'szz','123456','szz'] #db_info = ('127.0.0.1',3306,'szz','123456','szz') op_mysql(*db_info)
注意这里是调用的时候写 *db_info ,意思是将 db_info 解包传入函数体内,但是参数个数要一致(可以传元组,list,甚至是字符串,但是个数要一致,只要有下标的就成),而且进入的值要跟函数定义内的对应
结果:
127.0.0.1 3306 szz 123456 szz
2、那么我们用 **kwargs 的方式:
db_info2 = { 'host':'127.0.0.1', 'port':3306, 'user':'szz', 'passwd':'123456', 'db':'szz' } op_mysql(**db_info2)
把字典解开host=127.0.0.1,port=3306,user=szz……传入到函数内,这里要注意字典内的 key 值要跟函数定义的参数名字对应,个数要一致,顺序可以不一致
结果:
127.0.0.1 3306 szz 123456 szz
6、定义入参形式
假设我们要定义输入参数的数据类型怎么定义呢?
def add_user(username:str,password:list): print('username',username) print('password',password)
如上面例子所示,username 定义为 str 类型,password 是数组类型;但是这个入参类型仅仅可以认为是个提示信息,即使不传入规定的参数类型,也不会报错
递归
递归简单而言就是函数内部调用函数本身(最多调用 999 次)
比如我们用一个函数判定输入的数字是否为偶数,不是的话就继续输入
def say(): num = input('请输入一个数字:').strip() if int(num)%2!=0: print('请重新输入') say() return True say()
递归的 return 坑,得到 none
return的作用是将函数结果返回,即退出def函数模块。大量的教材和网上教程都有说明函数执行遇到return就会结束,返回一个值给调用函数处。常规用法确实是这样,但在递归调用中却存在一个坑,今天就遇到了,举个例子:
一个简单的不断相加的递归小程序:
def add(sum,x,y): if sum<10: x +=2 y +=2 sum=x+y add(sum,x,y) else: sum=x+y print(sum) return sum print (add(0,0,0))
结果:
12
None
这就有点奇怪了,结果为什么是 None?
经过计算,结果确实应该是12,明明已经计算出了正确的值,为什么函数返回值是 None 呢。经过多方查阅,发现自己采坑里去了,惯性思维让我认为 return 必然会使得 def 函数模块结束返回一个值,可实际上在递归中并不是,如果改变约束条件在 return 之后函数还会继续递归计算。
真正的原因是此处的 return 仅仅是上一次递归调用函数本身时候的返回值,而 def 函数块并没有返回任何值。也就是说这个 return 仅属于上一次递归调用,并不属于 def 函数块。也就是说整个 def 函数块没有 return,也就没有出口,自然不会有数据出去,所以得到 None,将程序改变一下:
def add(sum,x,y): if sum<10: x +=2 y +=2 sum=x+y sum = add(sum,x,y) return sum else: sum=x+y print(sum) return sum print (add(0,0,0))
结果:
12 12
这次就正确了,程序多次递归后,运行到 else,此时返回一个值到达 sum=add(sum,x,y) 的 sum,再 return sum,此时为 def 函数块的返回值。而之前程序运行到 sum=add(sum,x,y) 时由于等号右边的递归使得程序一直在计算,sum 并未被幅值,进而下面的 return sum 并未执行,所以只有一个返回值。
深拷贝/浅拷贝
用一个例子来看一下,循环删除 list 内容会出现什么问题
li = [1,1,2,3,4,5,6,7,8,9] for i in li: if i%2!=0: li.remove(i)
很明显,这个函数的作用是,删除数组内的奇数,只留下偶数,但是结果真的如此么?
[1, 2, 4, 6, 8]
咦,为啥会出现这个问题?多出来一个 1?
解答:我们理解一下,循环 list ,第一个取到的是 li[0] = 1,是奇数,所以删除,此时 li = [1,2,3,4,5,6,7,8,9] ;再删除下标为 1 的 li[1] = 2,为偶数,不删除,所以留下了一个 1 ……
所以,循环 list 删除元素,都会出现这个问题,怎么解决?
方式1——深拷贝一个一样的 list
li = [1,1,2,3,4,5,6,7,8,9] l2 = [1,1,2,3,4,5,6,7,8,9] for i in l2: if i%2!=0: li.remove(i) print(li)
重新拷贝一个一模一样的 l2,循环 list ,第一个取到的是 l2[0] = 1,是奇数,所以删除,此时 li = [1,2,3,4,5,6,7,8,9] ;再看 li2[1] 是奇数,删除 li 的 1 ,所以 li 变成了 [2,4,6,8]
那么可能会说,既然 l2 和 li 是一模一样,为啥不直接 l2 = li 进行复制呢?这就涉及到一个概念叫:深拷贝,浅拷贝。区别:深拷贝会开辟新的内存空间;浅拷贝:不会开辟新的内存空间。
我们定义的 li = [1,1,2,3,4,5,6,7,8,9] 其实是在内存里面开辟了一块内存存这个 list 的内容 [1,1,2,3,4,5,6,7,8,9] ,那 li 这个变量也是开辟了一块内存空间,里面存的是什么?是这个 list 的内存地址,比如说是 1234;
假设要对 li 元素删除,会用 li 的地址 1234 对应的 list 内的 [1,1,2,3,4,5,6,7,8,9] 去删除。那么假设我们定义 l2 = li ,那么只是将 l2 的地址也是 1234 ,这样操作的话,操作的是同一个内存空间的 list ,其实跟没有拷贝是一样的……
所以我们要重新开辟一块内存空间存一样的 list ,将内存独立出来就不会出现 list 修改的问题
方式2——切片深拷贝
li = [1,1,2,3,4,5,6,7,8,9] l2 = li[:] for i in l2: if i%2!=0: li.remove(i) print(li)
地址空间怎么查看
li = [1,1,2,3,4,5,6,7,8,9] l2 = li l3 = li[:] print(id(li)) print(id(l2)) print(id(l3))
地址结果:说明 l2 = li 这种方式不会开辟新的内存空间
39139336 39139336 34693832
当然深拷贝,对于字典元组什么的也是一样适用。深拷贝会开辟新的内存空间;浅拷贝:不会开辟新的内存空间。
深拷贝/浅拷贝
import copy d = {'name':'xiaohei','l':[4,5,6]} d1 = d #浅拷贝 d2 = copy.copy(d)#浅拷贝 d3 = copy.deepcopy(d) #深拷贝 d1['age']=18 d1['l'].append(8) print(id(d)) print(id(d1)) print(id(d2)) print(id(d3)) print('d',d) print('d1',d1) print('d2',d2) print('d3',d3)
结果:
38911072 38911072 38911720 42963664 d {'name': 'xiaohei', 'l': [4, 5, 6, 8], 'age': 18} d1 {'name': 'xiaohei', 'l': [4, 5, 6, 8], 'age': 18} d2 {'name': 'xiaohei', 'l': [4, 5, 6, 8]} d3 {'name': 'xiaohei', 'l': [4, 5, 6]}
这里有个疑问,为什么 copy.copy 打印出来的内存地址也是不一样的呢,但是打印出来的结果是一致的,记住 copy.copy() 是浅拷贝,虽然也开辟了新空间,但是里面嵌套一层数组啥的,也不会拷贝出来,所以是浅拷贝。
内置函数
有文档可参考:https://docs.python.org/zh-cn/3.7/library/functions.html
python 自带的函数,比如说:input(),print()
1、类型转换
函数名 | 功能 |
int() | 转成整数 |
float() | 转成浮点数 |
dict(a=1,b=2) | 转成字典 |
list('123') | 转成列表 |
set() | 转成集合 |
tuple() | 转成元组 |
bool(None) | 转成布尔类型 |
2、常用函数
enumerate #枚举
假设想实现,打印出 a==>1 b==>2 c==>3……a对应1,b对应2,c对应3这种的,要怎么实现?
方法1:
l = ['a','b','c','d','e','f'] id = 1 for i in l: print('%s==>%s'%(i,id)) id+=1
方法2:
l = ['a','b','c','d','e','f'] for id,i in enumerate(l,1): #后面的 1 代表从 1 开始,写 0 代表从 0 开始,就是 a==>0…… print('%s => %s'%(id,i))
结果:
1 => a 2 => b 3 => c 4 => d 5 => e 6 => f
zip #合并
l1 = ['xiaoming','xiaobai','xiaohei'] l2 = [110,120,119] l3 = [1,2,3] res = list(zip(l1,l2,l3)) print(res)
结果:
[('xiaoming', 110, 1), ('xiaobai', 120, 2), ('xiaohei', 119, 3)]
要注意的是,假设 l3 只有 2 个元素,那么 zip 之后整个 list 内元素只有两个:
l1 = ['xiaoming','xiaobai','xiaohei'] l2 = [110,120,119] l3 = [1,2] res = list(zip(l1,l2,l3)) print(res) 结果: [('xiaoming', 110, 1), ('xiaobai', 120, 2)]
这个一般用在于合并两个 list 转成 字典形式:
l1 = ['xiaoming','xiaobai','xiaohei'] l2 = [110,120,119] res = list(zip(l1,l2)) print(res) print(dict(res)) 结果: [('xiaoming', 110), ('xiaobai', 120), ('xiaohei', 119)] {'xiaoming': 110, 'xiaobai': 120, 'xiaohei': 119}
map —— 循环调用函数,保存返回结果
例如讲,我们对数字补零的方式:'1'.zfill(2),就是对数字补零为 2 位,但是假设我们并不知道有这个方式怎么办?就要自己写函数补零
def zfill(num): num = str(num) if len(num)==1: num = '0'+num return num
定义好之后,假设要生成一个 01-33 的二位数 list 那么就得对 range 进行循环补零:
1、直接循环写
l = [] for i in range(1,34): result = zfill(i) l.append(result) print(l)
2、列表生成式写
l = [ zfill(i) for i in range(1,34) ]
3、利用 map :要用的时候,前面一定要加 list 转换,否则不会调用
map(函数名,list),里面可以理解为 map 会帮我们循环调用这个 list ,将 list 的每个值传到函数名内循环调用
res = list(map(zfill,range(1,34))) print('map 的结果:',res) 结果: map 的结果: ['01', '02', '03', '04', '05', '06', '07', '08', '09', '10', '11', '12', '13', '14', '15', '16', '17', '18', '19', '20', '21', '22', '23', '24', '25', '26', '27', '28', '29', '30', '31', '32', '33']
这里要注意,要查看 map 函数的结果,需要在前面加 list ,这是 Python3 内需要注意的否则会打印出来一串 <map at 0xc8c9320> 之类的。
map 函数也可以用来创建文件夹:同样的,前面要加 list ,否则不会创建
import os list(map(os.mkdir,['dsk','ldd','brf']))
filter —— 循环调用函数
假设我们有一个显示分数是否及格的程序,我们用 list 的每个传入该函数内判断
score = [89, 43, 60, 52, 45, 37, 66, 100, 21, 24] def is_jg(s): if s>59: return True
如果我们只想把及格的分数保留,不及格的剔除,原来的方法:
jg = [] for i in score: if is_jg(i): jg.append(i) print(jg)
利用 filter 方法:这里要注意,filter 要生效也要加 list 转换
result = list(filter(is_jg,score)) print('filter的结果',result) filter的结果 [89, 60, 66, 100]
那么你可能会问,map 的结果是啥呢?map 其实是保存返回结果,那么上面的 is_jg 的返回结果是啥呢?是 True 和 None
result2 = list(map(is_jg,score)) print('map的结果',result2) map的结果 [True, None, True, None, None, None, True, True, None, None]
map:我帮你循环调用函数,你返回什么,我就保存什么;filter:你只要返回 True ,我就给你保存这个 list 的元素,返回结果不为 True 的元素就给你过滤掉
函数名 | 功能 |
type() | 查看数据类型 |
id() | 查看地址 |
len() | 查看 list 等长度 |
sum(list) | 求数组的和 |
round(1.9911,2) | 四舍五入保留两位小数 |
divmod(10,3) | 取商和余,返回(3,1) |
locals() | 取当前函数的所有局部变量,返回 list |
globals() | 取当前整个Python文件的所有变量,返回 list |
chr(64) | 取 Ascii 码为 64 对应的字符:@ |
ord('A') | 取字符 A 对应的 Aciii 码 |
dir(d) | 看 d 这个数据支持哪些方法可用 |
sorted(l) | 对l(l可以为list也可为字符串等)从小到大排序 |
reversed(sorted(l)) | 对 l 从大到小排序(reversed就是翻转) |
sorted(l,reversed=True) | 效果同上,从大到小排序 |
enumerate | 枚举 |
zip | 合并 |
list(map(funname,list)) | 将 list 的每个值循环传入 funname 调用,保存返回结果 |
list(filter(funname,list)) | 将 list 的每个值循环传入 fucname 调用,保存返回为 True 的元素 |
字符集编码
让计算机认识各种字符:数字,特殊字符,汉字……计算机只认识 0 和 1。
那么计算机怎么认识其他的呢?一开始,计算机是外国人造的,外国人弄了一张表,就是 Ascii 码,每一个英文字母以及 0-9 数字以及特殊字符都有个对应的编码,计算机就按照这张表进行翻译。比如说 @ 字符的编码为 64,先将 @ 转化为 64,再将 64 转化为二进制数这样计算机就能认识这个 @ 。
再后来,计算机传入到中国,那么中国不能也只用简单的英文字符以及特殊字符啊,那么咋办呢?而且别的比如说韩国,日本等也要用。后来就来了一张表:gb2312,里面有六千多个汉字,计算机就认识中文了,后来又扩展了下:GBK,基本就包含了汉字,比如说我们的 Windows 系统现在默认的字符集编码就是 GBK。但是中文用的是 GBK 拿到日本啥的能用么?用不了,会出现乱码,相当于鸡同鸭讲,所以这就是为什么出现乱码。
为了解决这个问题,发明了新编码:Unicode,就把很多国家的字符集集成起来,表很大有很多数字,不管是英文,什么鬼文,在全世界都通用的,这个就是万国码。又产生了个问题:一开始英文只占了一个字节大小,Unicode规定每个字符都占两个字节,这样就导致了很多明明可以一个字节表示的字符,却强制性变成了两个字节,占用的内存也有很多不必要的浪费。
最后出来了 utf-8 ,也就是解决了这个问题,什么英文字符啥的也就是占一个字节,中文啥的占两个字节,这样算是最优解了,所以现在用中文的话,一般指定 utf-8。所以 utf-8 是属于 Unicode,只是做了小优化。Python 3 里面默认的编码是 Unicode,所以 Python 3 里面对中文比较友好;Python 2 默认的是Ascii 码,所以要在最前面写 #coding=utf-8
有时候会有个问题,函数能不能作为字典的 value 存在?这样多个 if 的情况下就直接用 key - value 调用函数,不用多个 什么 if 去写:
func_map = { "1":func, "2":fun2 "3":fun3 } if choice in fun_map: fun_map.get(choice)()
作业
要求:写一个生成双色球的程序,输入几就产生多少注双色球
- 每次产生的双色球不能重复
- 存到文件里面
- 文件里面的也不能重复
- 红色球号码从1 - -33中选择;蓝色球号码从1 - -16中选择
- 红色的球有6个
- 蓝色的球有1个
- 红色球6个,1,33,蓝色球1个,1-16
- 先从1,33之间取6个 random.sample([1,33],6)
- 再从1-16之间取1个 random.choice([1,16])
- 把双色球号码改成 红色球 01 02 03 04 05 06 蓝色球 07 的格式
- 读到文件的内容, 判断刚才产生的双色球是否在文件中
- 不在就写入
import random FILE_NAME = 'seq.txt' def op_file(content=None): with open(FILE_NAME,'a+',encoding='utf-8') as fw: if content: fw.write(content) else: fw.seek(0) res = fw.read() return res def seq(num): count = 0 while count<num: b1 = [ str(i).zfill(2) for i in range(1,34) ]#产生一个01,02- 33的list b2 = [ str(i).zfill(2) for i in range(1,17) ]#产生一个01,02- 33的list red = random.sample(b1,6)#返回是一个list red.sort()#排序 blue = random.choice(b2) red_str = ' '.join(red) # '01 02 03 04 05 06' result = "红色球:%s 蓝色球:%s "%(red_str,blue) all_ball = op_file()#获取文件内容 if result not in all_ball: op_file(result)#写入 count+=1 def seq2(num): count = 0 while count<num: red_str = ' '.join(sorted(random.sample([ str(i).zfill(2) for i in range(1,34) ],6))) #返回是一个list blue = random.choice([ str(i).zfill(2) for i in range(1,17) ]) result = "红色球:%s 蓝色球:%s "%(red_str,blue) if result not in op_file(): op_file(result)#写入 count+=1 seq2(20)