• 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)
  • 相关阅读:
    Maven报错:“请使用 -source 7 或更高版本以启用 diamond 运算符”
    C++项目——基于QT的电梯仿真系统
    Ubuntu下Java环境配置
    maven
    Java、IDEA笔记
    vim及ctags的使用
    英特尔(intel)、思科(cicso)实习面试
    可变参数函数——以printf为例子
    求单链表是否有环、环长、入环点、链长
    53. Maximum Subarray
  • 原文地址:https://www.cnblogs.com/xiaowenshu/p/10746021.html
Copyright © 2020-2023  润新知