Python(四) —— 函数

什么是函数?

把一堆代码放一起就叫函数

函数用来干什么?

不复写代码,提高代码重复利用程度

怎么定义以及调用函数:

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:判断输入的值,是否为小数:

 思路:

  1. 判断是否只有一个小数点
  2. 小数点左边的是一个整数,小数点右边的也是一个整数
  3. 小数点右边的也是一个整数,左边要以负号开头,只有一个负号,负号后面是整数
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

所以:

  1. 调用的函数的时候,可以全部都用位置参数,位置是一一对应的,必须按照位置来传参
  2. 也可以全部都用关键字参数,指定关键字,不需要按照顺序来
  3. 也可以一起用,但是要先写位置参数,再写关键字参数,关键字参数后面不能再出现位置参数

关键字参数有种更牛批的方式,还是上面的例子:

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. 每次产生的双色球不能重复
  2. 存到文件里面
  3. 文件里面的也不能重复
规则
  1. 红色球号码从1 - -33中选择;蓝色球号码从1 - -16中选择
  2. 红色的球有6个
  3. 蓝色的球有1个
思路:
  • 红色球6个,1,33,蓝色球1个,1-16
  1. 先从1,33之间取6个 random.sample([1,33],6)
  2. 再从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)
原文地址:https://www.cnblogs.com/xiaowenshu/p/10746021.html