• 函数


    一、函数基础

    1、为什么要用函数

    # 增强程序的组织结构,可读性高
    # 减少代码冗余
    # 扩展性强

    2、什么是函数

    # 函数时组织好的,可以重复使用的的,用来实现单一,或相关联功能的代码段 
    # 函数能提高应用的模块性,和代码的重复利用率
     
    # 在程序中,函数必须遵守:先定义,再调用

    3、函数的分类

    # 内置函数
    
    为了 方便程序开发,针对一些简单的功能,Python解释器已经定义好了的函数即内置函数,对于内置函数我们可以拿来就用而无需事先定义,如len()  sum() max()
    
    
    # 自定义函数
    
    很明显内置函数所能提供的功能有限,这就需要我们自己根据需求,事先定义好我们自己的函数来实现某个功能,以后在遇到应用场景时,调用自定义函数即可

    4、定义函数

    # 函数代码块以def关键字开头,后接函数名和小括号
    # 任何传入的参数和自身变量必须放在小括号里面,参数名称可以多个
    # 函数的第一行需要写函数说明
    # 函数内容以冒号起始,并且缩进
    # return [表达式]结束函数,选择性的返回一个值给调用方,不带表达式的return ,返回值为None

    5、有参函数、无参函数、空函数

    # 有参函数
    def userinfo(name,age,):     # 定义时有参
        '''
        :param name:  姓名
        :param age:   年龄
        :return:
        '''
        print('姓名:%s 年龄:%s' %(name,age))
    
    userinfo('fred','18')      # 调用时必须传参
    
    # 无参函数
    
    def func():                # 定义时无参
        '''
        :return:
        '''
        print('this is No Reference function')
    
    func()                     # 调用时不需要传参
    
    # 空函数
    def pay():
        pass
    
    # 空函数的作用:在项目立项阶段,使用空函数把此项目的大体功能干先列出来,后续只需要一个个实现小功能就可以

    6、函数的返回值

    return 是函数结束的标志,
    函数内可以有多个return,但只要执行一个,函数就立即结束,并且把return后的值当做本次调用的结果返回
    注意:
    1、返回值没有类型限制
    2、返回值没有个数限制,可以用逗号分开多个值一次返回
    3、可以没有return,默认返回None
     
    什么时候该有返回值?
         调用函数,经过一系列的操作,最后拿到一个明确的结果,则必须要有返回值
         通常有参数需要返回值,输入参数,经过计算,得到一个最终结果
    什么时候不需要返回值?
         调用函数,仅仅只是执行一系列的操作,最后不需要得到什么结果,则无需有返回值
         通常无参函数不需要有返回值

    7、函数调用:

    # 语句形式:userinfo()
    
    # 表达式形式:3*len('userinfo')
    
    # 当做其他函数的参数:range(len('userinfo'))

     

    8、函数基础阶段总结:

    1、函数的使用必须遵守:先定义后调用的原则
    2、在定义阶段,只检测函数命名语法以及函数体的语法
    3、在调用阶段,通过函数名找到函数的内存地址,然后执行函数体代码
    4、函数名;是用来访问到函数的内存地址的,拿到函数的内存地址加括号就可以触发函数体代码的执行
    5、函数参数:是外界调用者为函数传值的媒介
    6、函数体代码:是函数功能的具体实现
    7、return是函数体执行的成果

    二、函数的参数

    1、实参与形参

    函数的参数可以分为两大类:形参与实参
    形参:函数定义阶段,括号内指定的参数,可以理解为变量名
    实参:函数调用阶段,括号内指定的参数,可以理解为变量名对应的值
    
    只有在调用函数时才会在函数体内发生实参(值)与形参(变量名)的绑定关系
    该绑定关系仅在调用函数时临时生效,调用结束后就解除绑定

    2、位置参数

    '''
    位置形参:函数定义阶段,按照从左到右的顺序依次定义的形参,称之为位置形参
    位置实参:函数调用阶段,按照从左到右的顺序依次传入的值,称之为位置实参
    '''
    
    def userinfo(name,age):
        print('姓名:%s  年龄:%s' %(name,age))
    
    
    userinfo('fred',18)         # 此时就相当于格式化输出中的%s
    姓名:fred  年龄:18          # 从左到右 name=fred  age=18
    
    # userinfo('fred',18,'sa')
    
    typeError: userinfo() takes 2 positional arguments but 3 were give
       # 值传多了
    
    userinfo('fred')
    userinfo() missing 
    1 required positional argument:
     'age'   # 没有给age传值
    
    userinfo(20,'fred')
    姓名:20  年龄:fred           # 由于严格按照位置传值,在不了解参数具体功能情况下,就会出现这种结果
    
    
    # 总结:
       # 但凡是按照位置定义的形参,在调用时必须为其传值,多一个不行,少一个也不行
       # 在传值时,按顺序与形参一一对应

    3、关键字实参

    '''
    关键字实参:在函数调用阶段,按照key=value的形式定义的实参,称之为关键字实参
    注意:
       1、关键字实参可以完全打乱顺序,但仍然可以指名道姓的为指定形参传值
       2、可以在调用函数时,关键字实参和位置实参混合使用
       3、关键字实参要跟在位置实参的后边,并且不能为一个形参传多个值
    
    '''
    
    def userinfo(name,age,company):
        print('姓名:{name} 年龄:{age} 公司:{company}'.format(name=name,age=age,company=company))
    
    
    
    userinfo('fred',company='9you',age=20)
    # 姓名:fred 年龄:20 公司:9you
    
    
    userinfo(age=20,company='9you','fred')
    SyntaxError: positional argument follows keyword argument       # 位置参数不能跟在关键字参数右边

    4、默认形参

    '''
    默认参数:在定义函数时,就已经为某些参数绑定值,称之为默认形参
    注意:
       1、在定义阶段就已经有值,意味着在调用阶段可以不用为其传值
       2、默认形参必须放到位置形参后面
       3、默认形参的值只在定义阶段生效一次,在函数定义之后发生的改动无效
       4、默认形参的值通常应该为不可变类型
    
    什么时候应该定义默认形参:
        大多数情况下都一样的,比如民族
    
    什么时候应该定义位置形参
        大多数情况是不一样的,比如name
    
    '''
    
    def userinfo(name,age,company,sex='male'):
        print('姓名:{name} 年龄:{age} 公司:{company} 性别:{sex}'.format(name=name, age=age, company=company,sex=sex))
    
    userinfo('fred',age=18,company='9you')             # 可以不用为默认形参传值
    姓名:fred 年龄:18 公司:9you 性别:male
    
    userinfo('jerry',age=18,company='9you',sex='female')
    姓名:jerry 年龄:18 公司:9you 性别:female            # 也可为默认形参传值,以传的值为准
     
    def userinfo(name,age,sex='male',company):
    print('姓名:{name} 年龄:{age} 公司:{company} 性别:{sex}'.format(name=name, age=age, company=company,sex=sex))


    SyntaxError: non-default argument follows default argument # 默认形参不能在位置形参左边

    5、可变长参数

    '''
    可变长参数是在调用函数时,函数的参数个数是不固定的
    
    然而实参终究要为形参传值的,针对两种形式的参数不固定,对应着形参也必须有两种解决方案,来分别处理溢出的位置实参与关键字实参
    
    python中约定俗成:
       *args       # 位置形参
       *kwargs     # 关键字形参
     
    *  会把溢出的实参存成元组,然后赋值给紧跟其后的变量名
    ** 会把溢出的关键字实参存成字典,然后赋值给紧跟其后的变量名
    '''

    1)形参中带*

    def userinfo(username,passwd,*args):
        print('用户名:%s' %username)
        print('密码:%s' %passwd)
        print('其他:',args)
    
    userinfo('root','x', '0', '0', 'root', '/root', '/bin/bash')
    
    用户名:root
    密码:x
    其他: ('0', '0', 'root', '/root', '/bin/bash')      

    2)形参中带*   && 实参中带*

    '''
    实参中* 小窍门:
    但凡实参中*,都先将其打散成位置实参,然后考虑传值
    '''
    
    def userinfo(username,passwd,*args):
        print('用户名:%s' %username)
        print('密码:%s' %passwd)
        print('其他:',args)
    
    userinfo('root',*('x', '0', '0', 'root', '/root', '/bin/bash'))    #此处,只传一个位置实参,然后*后面跟了元组
    用户名:root
    密码:x
    其他: ('0', '0', 'root', '/root', '/bin/bash')       #处理过程:先将元组打散成位置实参,然后将x传给passwd,剩下的整体传给*,并绑定给args

    3)、实参中带*

    '''
    实参中* 小窍门:
    但凡实参中*,都先将其打散成位置实参,然后考虑传值
    '''
    
    def userinfo(username,passwd,desc):
        print('用户名:%s' %username)
        print('密码:%s' %passwd)
        print('描述:%s' %desc)
    
    l=['fred','1234','this is fred']
    
    userinfo(*l)     #实参中带*,形参中没带*的情况,要严格遵守位置形参的规则,多一个不行,少一个也不行

    4)、形参中带**

    '''
    形参中带**  溢出的关键字实参会存成字典,然后赋值给紧跟其后的变量名
    
    '''
    def userinfo(username,passwd,**kwargs):
        print('用户名:%s' %username)
        print('密码:%s' %passwd)
        print('其他:',kwargs)
    
    userinfo(username='root',passwd='x',uid='0',gid='0',desc='root')     # 溢出关键字实参被**存成字典,赋值给kwargs
    用户名:root
    密码:x
    其他: {'uid': '0', 'gid': '0', 'desc': 'root'}

    5)、形参带**,实参中带**

    '''
    形参中带**  溢出的关键字实参会存成字典,然后赋值给紧跟其后的变量名
    实参中带**  但凡实参中带** ,先将其打散成关键字实参,然后再考虑传值
    
    '''
    
    def userinfo(username,passwd,**kwargs):
        print('用户名:%s' %username)
        print('密码:%s' %passwd)
        print('其他:',kwargs)
    
    userinfo('root',**{'passwd':'x','uid':'0','gid':'0','desc':'root'})     # 实参中带** 先将打散成关键字实参,passwd=x 其他给了kwargs
    用户名:root
    密码:x
    其他: {'uid': '0', 'gid': '0', 'desc': 'root'}

    6)、实参中带**

    '''
    实参中带**  但凡实参中带** ,先将其打散成关键字实参,然后再考虑传值
    '''
    
    def userinfo(username,passwd):
        print('用户名:%s' %username)
        print('密码:%s' %passwd)
    
    userinfo(**{'username':'fred','passwd':'1234','desc':'this is fred'})
    TypeError: userinfo() got an unexpected keyword argument 'desc'       # 不能传多也不能传少
    
    userinfo(**{'username':'fred','passwd':'1234'})
    用户名:fred密码:1234

    7、练习

    '''
    1、写函数,,用户传入修改的文件名,与要修改的内容,执行函数,完文件修改操作
    '''
    def replance_file(file_name,old,new):
        '''
        修改文件内容
        :param file_name:要修改的文件
        :param old: 修改前的的内容
        :param new: 修改后的内容
        :return:
        '''
        import os
        with open(file_name, mode='r', encoding='utf8') as f_r, 
                open('.%s.swap' % file_name, mode='w', encoding='utf8') as f_w:
            for line in f_r:
                if line.startswith(old):
                    line = line.replace(old,new)
                    f_w.write(line)
                else:
                    f_w.write(line)
    
        os.remove(file_name)
        os.rename('.%s.swap' %file_name,file_name)
    
        return old,new
    
    
    res=replance_file('src.txt','name','姓名')
    print(res)
    '''
    2、写函数,计算传入字符串中【数字】、【字母】、【空格] 以及 【其他】的个数
    '''
    def result(s):
        counts={
            'alpha':0,
            'digit':0,
            'space':0,
            'other':0,
        }
    
        for i in s:
            if i.isalpha():                  # 判断是否为字母
                counts['alpha']+=1
            elif i.isdigit():                # 判断是否为数字
                counts['digit']+=1
            elif i.isspace():                # 判断是否为空格
                counts['space']+=1
            else:
                counts['other']+=1
    
        return counts
    
    res=result('hello 18 age name %%%%&&&')
    print(res)
    '''
    3、写函数,判断用户传入的对象(字符串、列表、元组)长度是否大于5。
    '''
    def judge(*args):
        result=[]
        for i in args:
            if len(i) > 5:
                result.append(i)
    
    
        return result
    
    res=judge('fred_li',[18,'9you','sa'],('read ','play'))
    print(res)
    '''
    4、写函数,检查传入列表的长度,如果大于2,那么仅保留前两个长度的内容,并将新内容返回给调用者。
    '''
    def new_list(l):
        if len(l) > 2:
            return l[0],l[1]
        else:
            return l
    
    res=new_list(['fred','18','read','play'])
    print(res)
    '''
    5、写函数,检查获取传入列表或元组对象的所有奇数位索引对应的元素,并将其作为新列表返回给调用者。
    '''
    def fun(l):
        return l[::2]
    
    res=fun(['root','x','/home','this is root','/sbin/nologin'])
    print(res)
    '''
    6、写函数,检查字典的每一个value的长度,如果大于2,那么仅保留前两个长度的内容,并将新内容返回给调用者。
    dic = {"k1": "v1v1", "k2": [11,22,33,44]}
    PS:字典中的value只能是字符串或列表
    '''
    
    def func(dic):
        d={}
        for k,v in dic.items():
            if len(v) > 2:
                d[k]=v[0:2]
            else:
                d[k]=v
    
        return d
    
    print(func({'username':'fred','dept':('sa','ceo','cto','coo'),'hobby':['read','music','play','game'],'desc':('friend','father')}))

    三、函数对象

    '''
    函数是第一类对象,意味着函数可以当做数据去使用
    '''
    
    # 函数身为一个对象,拥有对象的三个通用属性:id type value
    def userinfo():
        print('from userinfo')
    
    print(userinfo)
    <function userinfo at 0x0387AA98>
    
    print(type(userinfo))
    <class 'function'>
    
    print(id(userinfo))
    59222680
    '''
    可以被引用,即函数可以赋值给一个变量
    '''
    def userinfo():
        print('from userinfo')
    
    func=userinfo
    print(func,userinfo)
    <function userinfo at 0x051DBA98> 
    <function userinfo at 0x051DBA98>
    '''
    可以当做参数传给另外一个函数
    '''
    
    def f1():
        print('from f1')
    
    def f2(func):
        print(func)
        f1()
    
    f2(f1)         # f1是当做参数传给f2 ,f2内部调用了f1
    
    <function f1 at 0x036CBA98>
    from f1
    '''
    可以当做函数的返回值
    '''
    
    def f1():
        print('from f1')
    
    def f2():
        return f1
    
    res=f2()
    print(res)
    <function f1 at 0x0369BA98>
    
    res()
    from f1
    '''
    可以当做容器类型(可以存多个值)的元素
    容器对象(list、dict、set等)中可以存放任何对象,包括整数、字符串,函数也可以作存放到容器对象中
    '''
    
    def auth():
        print('from auth')
    
    def pay():
        print('from pay')
    
    
    func_dic={
        '1':auth,
        '2':pay,
    }
    
    func_dic['1']()
    from auth
    
    func_dic['2']()
    from pay

    四、函数嵌套

    1、函数的嵌套调用

    '''
    函数的嵌套调用:在调用一个函数时,其内部的代码又调用了其他的函数
    '''
    
    def sum(x,y):
        sum=x+y
        return sum
    
    def sums(a,b,c,d):
        res=sum(a,b)
        res1=sum(c,d)
        res2=res+res1
        return res2
    
    print(sums(10,20,30,40))

    2、函数的嵌套定义

    '''
    函数的嵌套定义:在一个函数的内部又定义了另外一个函数
    '''
    
    def f2():
        x=1
        def f1():
            print('from f1')
        print(x)
        f1()
    
    f2()

    五、名称空间与作用域

    1、名称空间

    '''
    名称空间:是名称与对象之间的关系,可以将命名空间看做是字典,其中的键是名称,值是对象
    '''
    
    '''
    内置名称空间:存放Python解释器自带的名称,如len max sum 
    生命周期:在解释器启动时产生,关闭时回收
    '''
    print(len)
    <built-in function len>
    
    '''
    全局名称空间:除了内置的与局部的名字之外的都属于全局名称空间
    生命周期:在程序文件执行时,立刻产生,程序执行完毕后就回收
    '''
    name='root'
    
    '''
    局部名称空间:存放函数内部定义的名字
    生命周期:在调用函数时临时生效,函数调用结束后立即回收
    '''
    
    def user():
        age=18
        return age
    
    print(user())
    
    print(age)
    name 'age' is not defined       # 函数调用已结束,所以age和18的绑定关系也被回收了
    
    
    '''
    加载顺序:内置名称空间———全局名称空间————局部名称空间
    查找顺序:基于当前名称空间查找  当前————上一级————上上级
    假设目前在局部名称空间:查找顺序为:局部名称空间————全局名称空间————内置名称空间
    强调::::: 函数的形参属于局部名称空间
    '''
     
    def foo():
    x=4
    print(locals()) # locals()方法是打印出当前局部名称空间中的名字与value的对应关系
    foo()
    {'x': 4} # 以字典方式存储

    2、作用域

    '''
    作用域:指的是名字的作用范围
    分为:全局作用域和局部作用域
    '''
    
    '''
    全局作用域:包含内置名称空间与全局名称空间中的名字
    特点:全局有效,全局存活
    '''
    
    '''
    局部作用域:只包含局部名称空间的名字
    特点:局部有效,临时存活
    '''
    
    x=3333
    
    def area():
        x=222
        def area1():
            x=111
            print('from area1 %s' %x)
        area1()
        print('from area %s' %x)
    
    area()                 # 次函数为嵌套函数
    from area1 111         # 在area1函数内部,x与111绑定,area1被调用时,area1内部有x的名字与值对应的关系,所以x此时等于111
    from area 222          # area1调用结束后,x=111的绑定关系也随着解除,在area内部,x与222绑定,area被调用,所以此时x=222
    print(x)
    3333                   # area调用结束,x与222的绑定关系也随之解除,所以此时是在全局作用域里查找x对应的valve,所以此时x=3333
    
    '''
    总结:
       函数的作用域关系是在函数定义阶段就已经固定死的,与函数的调用位置无关
       即在调用函数时一定要到定义函数的位置寻找作用域关系
    
    '''

    3、global和nonlocal

    '''
    global:在局部名称空间声明其后的变量是作用于全局
    '''
    
    x=3333
    def func():
        x=2222
        def func1():
            global x
            x=4444
            print('from func1 %s' %x)
        func1()
        print('from func %s' %x)
    
    
    func()
    from func1 4444     # 由于func1中声明了x是全局名称空间中的名字,所以它作用于本层局部作用域和全局作用域,
    from func 2222      # 但不影响上层局部作用域
    
    print(x)
    4444                # 此时就算全局名称空间中定义了x=3333,也以global所在的局部名称空间中定义的为准
    '''
    nonlocal 在局部名称空间声明其后的变量作用于上层
    '''
    x=1111
    def func():
        x=4444
        def func1():
            nonlocal x
            x=3333
            print('from func1 %s' %x)
        func1()
        print('from func %s' %x)
    
    
    func()
    from func1 3333      # 由于func1中nonlocal声明了x=3333
    from func 3333       # 所以就算func中定义了x=4444,也不生效
    
    print(x)
    1111                  # nonlocal并未影响全局名称空间

    六、闭包函数

    '''
    大前提:函数的作用域关系是在函数定义时就已经固定死的,与调用位置无关
    闭包函数:
        1、闭指的是:定义在函数内部的函数
        2、包指的是:该内部函数包含对其外层函数作用域名字的引用
    
    闭包函数通常需要结合函数对象的概念,打破层级限制,将闭包函数返回到外部使用
    '''
    
    import requests                    # 要事先安装requests模块
    def outter(url):
        def get():
            response=requests.get(url)
            print(len(response.text))
        return get                     # 返回get函数的内存地址
    
    jd=outter('http://www.jd.com')      # 调用outter函数,实际是调用内部的get
    jd()
    
    baidu=outter('https://www.baidu.com')
    baidu()
    
    '''
    总结:
        1、闭包函数传一次值,多次调用
        2、通过外部函数把值传给内部函数
        3、闭包的意义:返回的函数对象,不仅仅是一个函数对象,在该函数外还包裹了一层作用域,这使得无论在何处调用,都优先使用自己外层包裹的作用域
    '''

    七、装饰器

    '''
    装饰器是闭包函数一种应用场景
    装饰器:
        装饰指的在不修改被装饰对象的前提下,为被装饰对象添加新功能
        器指的是工具
    
    装饰器本身可以为任意可调用的对象,被装饰的对象也可以是任意可调用的对象
    
    原则:
        写一个函数给另外一个函数添加新功能,需要遵守开放封闭的原则(对修改是封闭的,对扩展是开放的)
        1、不修改被装饰对象的源代码
        2、不修改被装饰对象的调用方式
    '''

    1、无参装饰器之装饰无参对象

    '''
    无参装饰器
    '''
    import requests
    import time
    def get():
        response=requests.get('http://www.baidu.com')
        print('The url have %s' %(len(response.text)))
    
    # 为get函数加上统计时间的功能
    def timmer(func):
        start_time=time.time()
        func()
        stop_time=time.time()
        print('run time is %s' %(stop_time - start_time))
    
    # timmer(get)             # 此方法虽然能实现需求,但是其改变了原对象的调用方式,pass掉
    
    # 只能考虑使用闭包函数咯
    def wrapper(func):    # func为最原始的get
        def timmer():     # 此处一定不能传值,如果传值就失去了闭包的意义
            start_time=time.time()
            func()          #此处的的func是由包在外层的wrapper传进来的
            stop_time=time.time()
            print('run time is %s' %(stop_time - start_time))
        return timmer
    get=wrapper(get)   # 此时(get)为最原始的get函数,传给了func,然后将wrapper(get)重新赋值get
    get()              # 此处get被伪装了,站在用户的角度上,还以为调用的是最原始的get函数,其实get调用的是wrapper,在wrapper内部为get加了统计时间的功能
    
    # 到此为止,新功能实现了,并且遵守了开放封闭原则(未修改被装饰对象的源代码和调用方式)
    
    '''
    装饰器语法
        在被调用对象的正上方使用"@装饰器名"  
        @装饰器名   ==   get=wrapper(get)
    '''
    
    # 上面的功能可以改写为:
    
    def wrapper(func):    # func为最原始的get
        def timmer():     # 此处一定不能传值,如果传值就失去了闭包的意义
            start_time=time.time()
           func()          #此处的的func是由包在外层的wrapper传进来的
            stop_time=time.time()
           print('run time is %s' %(stop_time - start_time))
        return timmer
    
    @wrapper         #get=wrapper(get)
    def get():
        response=requests.get('http://www.baidu.com')
        print('The url have %s' %(len(response.text)))
    
    get()
    The url have 2381
    run time is 0.034737348556518555

    2、无参装饰器之装饰有参对象

    '''
    有参装饰器
    
    接上面的例子,只能统计http://www.baidu.com的时间统计,功能被写死了
    此时就得给装饰器传参了
    '''
    
    import requests
    import time
    
    
    def wrapper(func):     # 最原始的get
        def timmer(*args,**kwargs):           #给被调用对象传参   'http://www.jd.com'
            start_time=time.time()
            func(*args,**kwargs)              #get('http://www.jd.com') / home()
            stop_time=time.time()
            print('run time is %s' %(stop_time - start_time))
        return timmer                  
    
    @wrapper
    def get(url):
        response=requests.get(url)
        print('The url have %s' %(len(response.text)))
    
    @wrapper(‘http://www.jd.com’)
    def home():
        time.sleep(1)
        print('welcome access home page')
    
    # 过程分析:
    # get=wrapper(get)  # 首先将get传给wrapper,也就间接传给timmer,拿到timmer的返回值,即wrapper(get)
    # print(get)
    # <function wrapper.<locals>.timmer at 0x051AFDB0>
    
    get() # 第二步,此时是给最原始的get传参,由timmer接收,并传给内部的get函数,完成新加功能并且未修改被装饰对象的源代码和调用方式
    
    home()              
    
    # 到此为止,可实现即可装饰有参对象和无参对象
    '''
    完整版装饰器语法
    '''
    import requests
    import time
    
    def wrapper(func):
        def timmer(*args,**kwargs):
            start_time=time.time()
            res=func(*args,**kwargs)
            stop_time=time.time()
            print('run time is %s' %(stop_time - start_time))
            return res          # 返回源对象的返回值
        return timmer
    
    @wrapper
    def get(url):
        response=requests.get(url)
        # print
        return ('The url have %s lines' %(len(response.text)))           # 源对象有返回值
    
    print(get('https://www.baidu.com'))

    示例:

    '''
    为被装饰对象加上认证功能'''
    
    import requests
    import time
    current_userinfo={'user':None}           # 存储当前登录用户的信息
    
    def auth(func):
        def wrapper(*args,**kwargs):
            if current_userinfo['user']:               # 如果current_userinfo['user']的值非空,则证明当前用户已登录,不用重复登录
                func(*args,**kwargs)
                return print(func(*args,**kwargs))
            else:                                       # 否则,从新登录
                username=input('请输入账号:').strip()
                passwd=input('请输入密码:').strip()
                if username == 'fred' and passwd == '123':
                    print('welcome to login')
                    current_userinfo['user']=username
                    func(*args,**kwargs)
                    return print(func(*args,**kwargs))
                else:
                    print('用户或密码错误')
        return wrapper
    
    @auth
    def get(url):
        response=requests.get(url)
        return ('The url have %s' %(len(response.text)))
    
    @auth
    def home():
        time.sleep(1)
        return ('welcome access home page')
    
    get('http://www.jd.com')
    home()

    3、多个装饰器执行顺序

    '''
    为一个对象添加多个装饰器
    '''
    import requests
    import time
    current_userinfo={'user':None}           # 存储当前登录用户的信息
    
    def auth(func):
        def wrapper(*args,**kwargs):
            if current_userinfo['user']:
                func(*args,**kwargs)
                return print(func(*args,**kwargs))
            else:
                username=input('请输入账号:').strip()
                passwd=input('请输入密码:').strip()
                if username == 'fred' and passwd == '123':
                    print('welcome to login')
                    current_userinfo['user']=username
                    func(*args,**kwargs)
                    return print(func(*args,**kwargs))
                else:
                    print('用户或密码错误')
        return wrapper
    
    def timmer(func):           # 最原始的get
        def wrapper1(*args,**kwargs):
            start_time=time.time()          
            res=func(*args,**kwargs)     # wrapper(get)
            stop_time=time.time()
            print('run time is %s' %(stop_time - start_time))
            return res
        return wrapper1
    
    @timmer
    @auth
    def get(url):
        response=requests.get(url)
        return ('The url have %s' %(len(response.text)))
    
    get('http://www.jd.com')
    
    '''
    分析:
        1、@auth === 最原始的get 》》》 wrapper(get)  即get=wrapper(get)
        2、@timmer == 不是最原始get   》》 wrapper1(wrapper(get))    即get=wrapper1(wrapper(get))
    执行顺序:
        1、先执行timmer,就是wrapper1(wrapper(get)), 即执行auth
        2、等auth执行完,再回到timmer,执行res=func(*args,**kwargs)之后的代码
        3、整个装饰器执行完成,本次时间统计是从timmer开始到auth结束,查看结果
        请输入账号:fred
        请输入密码:1234
        用户或密码错误
        run time is 5.6903979778289795     
    结论:多个装饰器的情况下,自上而下一次执行
    '''

    4、有参装饰器

    '''
    有参装饰器:本质就是在无参装饰器外又包了一层
    '''
    import requests
    import time
    current_userinfo={'user':None}           # 存储当前登录用户的信息
    
    def multi_auth(engine):
        def outter(func):
            def wrapper(*args,**kwargs):  # engine是关键字实参传进来的,所以file一定会传给engine
                if engine == 'file':     
                    if current_userinfo['user']:
                        func(*args,**kwargs)
                        return print(func(*args,**kwargs))
                    else:
                        username=input('请输入账号:').strip()
                        passwd=input('请输入密码:').strip()
                        if username == 'fred' and passwd == '123':
                            print('welcome to login')
                            current_userinfo['user']=username
                            func(*args,**kwargs)
                            return print(func(*args,**kwargs))
                        else:
                            print('用户或密码错误')
                elif engine == 'mysql':
                    print('mysql认证机制')
                elif engine == 'ldap':
                    print('ldap认证机制')
                else:
                    print('不支持改认证机制')
            return wrapper
        return outter
    
    @multi_auth(engine='file')   # outter(get)
    # @multi_auth(engine='mongdb')
    def get(url):
        response=requests.get(url)
        return ('The url have %s' %(len(response.text)))
    
    get('http://www.jd.com')

    5、完全装饰被装饰对象(继承被装饰对象的属性)

    '''
    wraps装饰器:获取被装饰对象的属性(函数名、帮助信息等)
        Help on function wraps in module functools:
        wraps(wrapped, assigned=('__module__', '__name__', '__qualname__', '__doc__', '__annotations__'), updated=('__dict__',))
    '''
    import requests
    import time
    from functools import wraps
    current_userinfo={'user':None}           # 存储当前登录用户的信息
    
    def multi_auth(engine):
        def outter(func):
            @wraps(func)                # 将最原始的get对象的属性转嫁给wrapper
            def wrapper(*args,**kwargs):  # engine是关键字实参传进来的,所以file一定会传给engine
                if engine == 'file':
                    if current_userinfo['user']:
                        func(*args,**kwargs)
                        return print(func(*args,**kwargs))
                    else:
                        username=input('请输入账号:').strip()
                        passwd=input('请输入密码:').strip()
                        if username == 'fred' and passwd == '123':
                            print('welcome to login')
                            current_userinfo['user']=username
                            func(*args,**kwargs)
                            return print(func(*args,**kwargs))
                        else:
                            print('用户或密码错误')
                elif engine == 'mysql':
                    print('mysql认证机制')
                elif engine == 'ldap':
                    print('ldap认证机制')
                else:
                    print('不支持改认证机制')
            return wrapper
        return outter
    
    @multi_auth(engine='file')
    def get(url):
        '''
        获取指定url的response.text的长度
        :param url:
        :return:
        '''
        response=requests.get(url)
        return ('The url have %s' %(len(response.text)))
    
    # get('http://www.jd.com')
    
    print(help(get))            # 获取函数的帮助信息(注释)
    
    Help on function get in module __main__:
    get(url)
        获取指定url的response.text的长度
        :param url:
        :return:
    None

    八、三元表达式、列表生成式、字典生成式、二分法、函数与的递归调用

    1、三元表达式

    '''
    三元表达式:
    语法:
       条件成立的返回值 if 条件 else 条件不成立的返回值
    '''
    x=10
    y=20
    
    # res=x if x > y else y
    res=True if x > y else False
    print(res)

    2、列表生成式

    '''
    列表生成式:和for循环搭配
        语法:l=[i for i in 序列 条件(可以没有条件)]   条件只能跟一个
    '''
    
    l=[i for i in range(5)]
    print(l)
    
    # 等同于
    for_l=[]
    for i in range(5):
        for_l.append(i)
    
    print(for_l)
    
    l1=[i for i in range(5) if i > 2]
    print(l1)
    
    # 等同于
    for_l1=[]
    for i in range(5):
        if i > 2:
            for_l1.append(i)
    
    print(for_l1)
    
    str='root:x:0:0:root:/root:/bin/bash'
    l2=str.split(':')
    
    
    l2=[i.upper() for i in l2 if i == 'root']
    print(l2)
    
    # 等同于
    l3=[]
    for i in str.split(':'):
        if i == 'root':
            l3.append(i.upper())
    print(l3)

    3、字典生成式

    '''
    字典生成式:
        语法:d1={k.upper():v for k,v in d1.items() if }  可以不加条件
    '''
    
    d1={
        'name': 'fred',
        'age': 18,
        'commany': '9you',
        'weight': 50.4
    }
    
    print({k.upper():v for k,v in d1.items() if k == 'weight'})
    
    # 等同于
    d2={}
    for k,v in d1.items():
        d2[k.upper()]=v
    
    print(d2)

    4、函数的递归调用

    '''
    函数的递归调用:
        在调用一个函数的过程中又直接或间接调用了自己,称之为函数的递归调用
        本质就是一个重复的过程,必须有两个明确阶段:
            1、回溯:一层一层的递归下去调用下去,每进入下一层问题的规模都应该有所减少
            2、递推:往回一层一层的结束调用
                递归必须要有一个明确的结束条件,在满足该条件的情况下会终止递归
    递归调用的分类:
        直接调用:自身调用
        def foo():
            foo()
        间接调用:通过别的函数间接调用自身
            def bar()
                foo()
    递归 VS while循环
        递归只需要把控结束或进入递归的条件即可,至于循环次数或者嵌套层数无须考虑
    
    调用函数会产生局部的名称空间,占用内存,因为上述这种调用会无需调用本身,
        python解释器的内存管理机制为了防止其无限制占用内存,对函数的递归调用做了最大的层级限制
    import sys
    sys.getrecursionlimit()         查看当期最大层级限制
    sys.setrecursionlimit(2000)     修改最大层级限制
    '''
    
    # 示例:
    # age(5) = age(4) + 2
    # age(4) = age(3) + 2
    # age(3) = age(2) + 2
    # age(2) = age(1) + 2
    # age(1) = 18
    
    #推算出age(5)的值
    
    def age(n):     # n从高到底,一步一步逼近1
        if n == 1:
            return 18
        return age(n-1)+2
    
    print(age(2))
    
    # 取出l中的每个元素
    l=[1,[2,[3,]]]
    
    def foo(l):
        for item in l:
            if type(item) is list:
                foo(item)
            else:
                print(item)
    
    foo(l)
    1
    2
    3

    image

    #总结递归的使用:
    1. 必须有一个明确的结束条件
    
    2. 每次进入更深一层递归时,问题规模相比上次递归都应有所减少
    
    3. 递归效率不高,递归层次过多会导致栈溢出(在计算机中,函数调用是通过栈(stack)这种数据结构实现的,每当进入一个函数调用,栈就会加一层栈帧,每当函数返回,栈就会减一层栈帧。由于栈的大小不是无限的,所以,递归调用的次数过多,会导致栈溢出)

    5、二分法

    '''
    二分法是一种快速查找的方法,时间复杂度低,逻辑简单易懂,总的来说就是不断的除以2除以2...
    分法查找非常快且非常常用,但是唯一要求是要求数组是有序的
    '''
    # 查找次序列中有没有43
    nums=[3,11,13,15,23,27,43,51,72,81,93,101]
    
    
    def binary_search(num,nums):
        print(nums)
        if len(nums) == 0:
            print('%s is not found' %num)
            return
        #
        mid_index=len(nums) // 2
    
        if num > nums[mid_index]:
            # in right
            nums=nums[mid_index+1:]    # 从mid_index索引号加一位(如果不加1,则取到mid_index)开始-数组最后
            binary_search(num,nums)    #重复调用本身逻辑,传入新的nums
        elif num < nums[mid_index]:
            # in left
            nums=nums[:mid_index]      #从数组索引0开始取-mid_index
            binary_search(num,nums)     # 重复调用本身逻辑,传入新的nums
        else:
            print('find it')
    
    binary_search(94,nums)
    
    [3, 11, 13, 15, 23, 27, 43, 51, 72, 81, 93, 101]
    [51, 72, 81, 93, 101]
    [93, 101]
    [93]
    []
    94 is not found

    九、匿名函数

    '''
    匿名函数:即没有名字的函数,与函数有相同的作用域,但是匿名函数意味着引用计数为0,使用一次就释放,除非让其有名字
    
    定义匿名函数就是定义出了一个函数的内存
    语法:
        lambda 实参 函数体代码
    
    lambda 只是一个表达式,函数体比 def 简单很多。
    lambda的主体是一个表达式,而不是一个代码块。仅仅能在lambda表达式中封装有限的逻辑进去。
    lambda 函数拥有自己的命名空间,且不能访问自己参数列表之外或全局命名空间里的参数。
    '''
    
    func=(lambda name,age:print('my name is %s my age is %s' %(name,age)))('name',20)
    print(func)
    
    '''
    示例
     文件内容标题为name,age,work,commany
     fred|20|SA|haymarker
     lck|30|IT|u9
    
    取出每一行,按如下格式存储
    [{'name':'fred','age':'20','work':'SA','commany':'haymarker'}]
    '''
    # 字典生成式的另一种应用场景
    with open('info.txt','rt',encoding='utf8') as info:
        items=[line.strip().split('|') for line in info]  # 列表生成式将文件内容按行转换成列表
        # 字典生成式将items的值传给字典里的key
        userinfo=[{'name':name,'age':age,'salary':salary,'work':work,'commany':commany} for name,age,salary,work,commany in items]
    
    # print(userinfo)
    
    # 取出年龄最大的成员的信息
    def maxage(dic):
        return dic['age']     # 返回age对应的value
    
    for dic in userinfo:
        res=maxage(dic)       # 循环取出最大age的值
    print(max(userinfo,key=maxage))  # 调用maxage函数,取出age最大用户信息,由于循环的是userinfo列表,所以返回最大age所在的列表
    print(max(userinfo,key=lambda dic:dic['age']))   #dic是参数,
    {'name': 'lck', 'age': '30', 'salary': '1000', 'work': 'IT', 'commany': 'u9'}
    
    # 取出薪资最高的成员信息
    print(max(userinfo,key=lambda dic:dic['salary']))
    {'name': 'fred', 'age': '20', 'salary': '3000', 'work': 'SA', 'commany': 'haymarker'}
    
    # 将成员信息映射成首字母大写的形式
    def capital(dic):
        return {'name':dic['name'].capitalize(),
                'age':dic['age'],
                'salary':dic['salary'],
                'work':dic['work'],
                'commany':dic['commany']}
    
    for dic in userinfo:
        res=capital(dic)
    user=map(capital,userinfo)
    print(user)
    <map object at 0x04CD1ED0>
    
    print(list(user))
    [{'name': 'Fred', 'age': '20', 'salary': '3000', 'work': 'SA', 'commany': 'haymarker'}, {'name': 'Lck', 'age': '30', 'salary': '1000', 'work': 'IT', 'commany': 'u9'}]
    users=map(lambda dic:{
                'name':dic['name'].capitalize(),
                'age':dic['age'],
                'salary':dic['salary'],
                'work':dic['work'],
                'commany':dic['commany']
    },userinfo)
    
    print(list(users))

    十、迭代器、生成器、生成器表达式

    1、迭代器

    '''
    什么是迭代器
        迭代器就是一个迭代取值的工具
        迭代是一个重复的过程,但是每一次重复都是基于上一次的结果而进行
    
    为何要用迭代器
        针对没有索引的数据类型,比如字典、集合、文件,要想迭代取出其中包含的一个个的值
        Python解释器必须提供一种能够不依赖与索引的迭代取值工具
    
    什么是可迭代对象
        内置有__iter__()方法的都是可迭代对象
        以下数据类型都是可迭代对象:
            <str_iterator object at 0x04AB24F0>
            <list_iterator object at 0x04AB2970>
            <dict_keyiterator object at 0x04AB38A0>
            <set_iterator object at 0x04AB85D0>
            <_io.TextIOWrapper name='info.txt' mode='r' encoding='utf8'>
        int和float不是可迭代对象
        
    迭代器对象
        1、内置有__iter__()
        2、内置有__next__()
            文件既是可迭代对象,又是迭代器对象
                f.__iter__()
                f.__next__()
        对于可迭代对象来说,调用__iter__()方法,得到就是迭代器对象
     
    注意:
       迭代器对象一定是可迭代对象
       可迭代对象不一定是迭代器对象'''
    '''
    迭代器对象的使用
    '''
    info=['adm','/var/adm','/sbin/nologin']
    obj=info.__iter__()
    
    print(obj.__next__())          # obj.__next__() 等同于next(obj)
    print(next(obj))
    print(next(obj))
    print(next(obj))               # next()方法抛出StopIteration异常时,标志着迭代器对象里的值已经被取完了
    StopIteration
    
    # 使用while循环来取值
    while True:
        try:
            print(next(obj),end=' ')
        except StopIteration:       # except捕捉到StopIteration异常,则break掉
            break
    '''
    try...except:
    try的工作原理是,当开始一个try语句后,python就在当前程序的上下文中作标记
    这样当异常出现时就可以回到这里,try子句先执行,接下来会发生什么依赖于执行时是否出现异常。
        如果当try后的语句执行时发生异常,python就跳回到try并执行第一个匹配该异常的except子句,异常处理完毕,控制流就通过整个try语句(除非在处理异常时又引发新的异常)。
        如果在try后的语句里发生了异常,却没有匹配的except子句,异常将被递交到上层的try,或者到程序的最上层(这样将结束程序,并打印缺省的出错信息)。
        如果在try子句执行时没有发生异常,python将执行else语句后的语句(如果有else的话),然后控制流通过整个try语句。            
    '''

    2、for循环的工作原理

    '''
    基于for循环就可以不依赖与索引取值
    
    工作原理:
       1.执行 in后对象的dic.__iter__()方法,得到此对象的迭代器对象
       2.执行next(dic.__iter__()),将得到的值赋值给k
      3.重复过程2,直到捕获StopIteration,结束循环
    '''
    dic={
        'name':'fred',
        'age':19,
        'commany':'haymarker',
    }
    
    for k in dic:
        print(k)
    '''
    迭代器的优缺点:
    优点:
        提供了一种不依赖与索引的、通用的取值方法
        节省内存,同一时间在内存中只有一个值(对象元素过大,迭代器可以更节省内存)
    缺点:
        针对同一个迭代器对象只能取完一次,不能重复取,不如按照索引或者key取值灵活
        无法预测迭代器对象所包含值的个数
    '''

    3、生成器

    '''
    什么是生成器
        在函数内但凡出现yield关键字,再调用函数就不会触发函数体代码的执行了
            generator() 直接调用不会触发函数体代码的执行
        会得到一个返回值,该返回值就是一个生成器对象
            def generator():
                print('this is first')
                yield
            print(generator())
            <generator object generator at 0x04F52870>
        而生成器对象本身就是迭代器
            g.__next__()
            g.__iter__()
    
    为什么要用生成器
        生成器是自定义的迭代器
    
    
    '''
    
    def generator():
        print('this is first')
        yield
    g=generator()
    print(g.__next__())
    # 既然g是迭代器,那么就可以通过迭代器规则通过next()方法取值
    # 即触发g对应的函数体代码的执行,直到碰到一个yield就暂停住,该yield后的值就当做本次next返回值
    # 如果yield后面没值,则返回None
    print(g.__next__())  # 依次取值,直到遇到抛出StopIteration,说明该对象中的值已经被取完了
    
    '''
    总结yield功能:
        1、提供了一种自定义迭代器的方式
        2、可以用于返回值
            yield与return的区别:
                相同点:都可以用于返回值,个数及类型都没有限制
                不同点:yield可以返回多次值,return只能返回一次,整个函数就结束了
        3、函数暂停以及继续执行的状态是由yield保存的
    '''
    
    # 练习:自定义生成器,模拟range方法
    
    def my_range(start,stop,step=1):
        while True:
            if start >= stop:
                break
            yield start
            start+=step
    
    
    for item in my_range(1, 5, 2):
        print(item)

    十一、面向过程编程

    ''''
    面向过程编程
    1、首先强调:面向过程编程绝对不是用函数编程这么简单,面向过程是一种编程思路、思想,而编程思想是不依赖于具体的语言或语法。言外之意即是我们不依赖于函数,也可以基于面向过程的思想编写程序
    
    2、定义
    面向过程的核心是过程二字,过程指的是解决问题的步骤,即先干什么再干什么
    基于面向过程设计程序就好比在设计一条流水线,是一种机械式的思维方式
    
    3、优点:复杂的问题流程化,进而简单化
    
    4、缺点:可扩展性差,修改流水线的任意一个阶段,都会牵一发而动全身
    
    5、应用:扩展性要求不高的场景,典型案例如Linux内核。git,httpd
    '''
  • 相关阅读:
    php-fpm 启动参数及重要配置详解
    Linux系统优化实现高并发
    Linux下php-fpm进程过多导致内存耗尽问题
    .htaccess详解及.htaccess参数说明
    添加PHP扩展模块
    nginx 服务器重启命令,关闭
    linux的文件打包与压缩
    vue 存取、设置、清除cookie
    div轮流滚动显示
    滚动监听
  • 原文地址:https://www.cnblogs.com/lichunke/p/9317366.html
Copyright © 2020-2023  润新知