• python 全栈开发,Day11(函数名应用,闭包,装饰器初识,带参数以及带返回值的装饰器)


    一、函数名应用

    函数名是什么?函数名是函数的名字,本质:变量,特殊的变量。

    函数名(),执行此函数。

    python 规范写法

    1. #后面加一个空格,再写内容,就没有波浪线了。

    2.一行代码写完,下面一行的的内容要空2行,

    3.逗号2个边的内容要有空格。

    如果是不规范的写法,Pycharm编辑器,会有灰色的波浪线显示。

    1.单独打印函数名

    def func1():
        print(666)
     
    print(func1)

    执行输出:

    <function func1 at 0x000001455800A950>

    打印的是一个函数内存地址

    2.函数名的赋值

    def func2():
        print(666)
     
    f = func2
    print(f())

    执行输出:

    666
    None

    3.函数名可以作为容器类数据的元素

    下面的函数都要执行

    def f1():
        print(111)
         
    def f2():
        print(222)
         
    def f3():
        print(333)
     
    def f4():
        print(444)

    写4个执行代码就可以了

    f1()
    f2()
    f3()
    f4()

    如果是100个呢?

    使用for循环批量执行函数

    def f1():
        print(111)
     
     
    def f2():
        print(222)
     
     
    def f3():
        print(333)
     
     
    def f4():
        print(444)
         
    l1 = []
    for i in range(1, 5):
        l1.append('f' + str(i))
     
    for i in l1:
        eval(i)()

    执行输出:

    111
    222
    333
    444

    4.函数名可以作为参数

    def f1():
        print(666)
     
    def f2(x):
        x()
     
    f2(f1)

    执行输出:

    666

    分析如下:

    def f1():
        print(666)
    
    def f2(x): # x = f1
        x() # f1()
    
    f2(f1) #将f1函数作为变量传参给f2,x()表示执行函数f1(),输出666
    

    5.函数名可以作为函数的返回值。

    def f11(x):
        return x #将返回值传给调用者f11(5),此时ret = 5
     
    ret = f11(5)
    print(ret)

    执行输出:

    5

    def f1():
        print(666)
     
    def f2(x):
        return x
     
    f2(f1)()

    执行输出:

    666

    代码分析:

    def f1():
        print(666)
     
    def f2(x): # x = f1
        return x #将f1返回给函数调用者f2(f1),此时就相当于 f1 = f2(f1)
     
    f2(f1)() #将f1函数作为变量传参给f2,return之后f2(f1)等于f1,f1()就表示执行f1函数,输出666

      f1是一个特殊变量,加()就可以执行了

    第一类对象( first-class object)

    1.可在运行期创建

    2.可用作函数参数或返回值

    3.可存入变量的实体

    *不明白?那就记住一句话,就当普通变量用

    函数名就是第一类对象
    加括号,就可以执行了。

    二、闭包

    闭包函数:

    内部函数包含对外部作用域而非全剧作用域变量的引用,该内部函数称为闭包函数

    有如下代码,请补全代码,在函数外部执行inner()函数

    def wrapper():
        def inner():
            print(666)

    第一种写法

    def wrapper():
        def inner():
            print(666)
        inner()
     
    wrapper()

    执行输出:

    666

    第二种写法

    def wrapper():
        def inner():
            print(666)
        return inner #将inner函数返回给调用者ret,此时ret = inner
     
    ret = wrapper()
    ret() #执行inner函数

    执行输出:

    666

    ret = wrapper()
    ret()

    等同于

    wrapper()()

    return inner之后,inner由临时空间,转换为全局空间了。
    因为ret赋值了

    闭包举例

    def wrapper():
        name = '老男孩'
        def inner():
            print(name)
        inner()
     
    wrapper()

    执行输出:

    老男孩

    如何判断它是否是一个闭包函数呢? 内层函数名.__closure__  cell 就是=闭包

    def wrapper():
        name = '老男孩'
        def inner():
            print(name)
        inner()
        print(inner.__closure__)
     
    wrapper()

    执行输出:

    老男孩
    (<cell at 0x0000017FC9C90B58: str object at 0x0000017FCA349AD0>,)

    出现了cell,就表示它是一个闭包函数

    name = '老男还'
    def wrapper():
        def inner():
            print(name)
        inner()
        print(inner.__closure__)
     
    wrapper()

    执行输出:

    老男还
    None

    返回值为None 表示它不是闭包
    因为name是一个全局变量

    如果函数调用了外层变量而非全局变量,那么它就是闭包

    name = '老男还'
    def wrapper2():
        name1 = 'alex'
        def inner():
            print(name)
            print(name1)
        inner()
        print(inner.__closure__)
     
    wrapper2()

    执行输出:

    老男还
    alex
    (<cell at 0x000001F87DBC0B58: str object at 0x000001F87E096E68>,)

    只要引用了外层变量至少一次,非全局的,它就是闭包

    面试题:

    下面的函数,是一个闭包吗?

    name = '老男孩'
    def wraaper2(n):
        #  n = '老男孩' 相当于
        def inner():
            print(n)
        inner()
        print(inner.__closure__)  #  None
    wraaper2(name)

    它也是一个闭包
    虽然wraaper2传了一个全局变量,但是在函数wraaper2内部,inner引用了外层变量,相当于在函数inner外层定义了n = '老男孩',所以inner是一个闭包函数

    闭包的好处

    当函数开始执行时,如果遇到了闭包,他有一个机制,他会永远开辟一个内存空间,将必包中的变量等值放入其中,不会随着函数的执行完毕而消失。

    举一个例子

    from urllib.request import urlopen
    content = urlopen('http://www.xiaohua100.cn/index.html').read().decode('utf-8')
    print(content)

    执行输出,是一堆网页源代码。

    爬3次

    from urllib.request import urlopen
    content1 = urlopen('http://www.xiaohua100.cn/index.html').read().decode('utf-8')
    content2 = urlopen('http://www.xiaohua100.cn/index.html').read().decode('utf-8')
    content3 = urlopen('http://www.xiaohua100.cn/index.html').read().decode('utf-8')

    内存开了3次,很占用内存

    把它封装成闭包

    from urllib.request import urlopen
     
    def index():
        url = "http://www.xiaohua100.cn/index.html"
        def get():
            return urlopen(url).read()
        return get
     
    xiaohua = index()
    content = xiaohua()
    print(content)

    这个例子,只有第一遍,是从网站抓取的。
    之后的执行,直接从内存中加载,节省内存空间

    三、装饰器初识

    装饰器本质上就是一个python函数,他可以让其他函数在不需要做任何代码变动的前提下,增加额外的功能,装饰器的返回值也是一个函数对象。

    装饰器的应用场景:比如插入日志,性能测试,事务处理,缓存等等场景。

    现在我有一个需求,我想让你测试这个函数的执行时间,在不改变这个函数代码的情况下:

    先写一个雏形

    import time
     
    def func1():
        print('in func1')
        time.sleep(1)  # 模拟程序逻辑
     
    start = time.time()
    func1()
    print(time.time() - start)

    执行输出:

    in func1
    1.0001070499420166

    封装成函数

    传一个参数,函数名,就可以查看函数的执行时间了

    import time
     
    def func1():
        print('in func1')
        time.sleep(1)  # 模拟程序逻辑
     
    def timmer(f):
        start_time = time.time()
        f()
        end_time = time.time()
        print('此函数的执行时间为{}'.format(end_time - start_time))
     
    timmer(func1)

    执行输出:

    in func1
    此函数的执行时间为1.0002098083496094

    原来100个函数,都是调用了func1(),现在测试函数,需要timeer(func1)。如果想测试所有调用地方的执行时间,那么需要修改100个函数位置,改成timeer(func1),太麻烦了。
    不能更改原来的调用方式,同时需要加一个函数执行时间的功能。怎么办呢?

    要无限接近于原来的调用方法,慢慢来

    先来讲一个变量赋值的例子

    a = 1
    b = a
    a = 2 #a重新赋值了,原来a的值不存在了。但是b还是等于原来a的值,也就是1
    print(a,b)

    执行输出2,1

    1.更改执行方式

    import time
     
    def func1():
        print('in func1')
        time.sleep(1)  # 模拟程序逻辑
     
    def timmer(f):
        start_time = time.time()
        f()
        end_time = time.time()
        print('此函数的执行时间为{}'.format(end_time - start_time))
     
    f1 = func1 #定义f1等于func1函数
    func1 = timmer #定义func1等于timmer,等式计算右边的。此时func1被覆盖了,和原来的没有任何关系,f1还是等于原来的func1
    func1(f1) #此时相当于执行 timmer(老的func1)

    执行输出:

    in func1
    此函数的执行时间为1.0001263618469238

    在精简一步

    import time
     
    def func1():
        print('in func1')
        time.sleep(1)  # 模拟程序逻辑
     
    def timmer(f): # f = func1
        def inner():
            start_time = time.time()
            f() #执行func1()
            end_time = time.time()
            print('此函数的执行时间为{}'.format(end_time - start_time))
        return inner #将inner函数返回给函数调用者timmer(func1)
     
    a = timmer(func1) #等式计算右边的,将func1函数传给timmer函数
    a() #此时相当于执行了inner函数

    执行输出:

    in func1
    此函数的执行时间为1.000577449798584

    最终版本

    import time
     
    def timmer(f): # 2接收参数.f = func1
        def inner():
            start_time = time.time() #5.进入inner函数
            f() #6.执行f(),也就是原来的func1函数。虽然func1被覆盖了,但是之前的值还存在。请参考上面a,b赋值的例子
            end_time = time.time() #10 获取当前时间
            print('此函数的执行时间为{}'.format(end_time - start_time)) #11.输出差值
        return inner #3.将inner函数返回给函数调用者timmer(func1),此时程序结束,继续执行func1()
     
    def func1(): #7.进入函数
        print('in func1') # 8.打印输出
        time.sleep(1)  # 9.等待1秒
     
    func1 = timmer(func1) #1.等式计算右边的,将func1函数传给timmer函数,此时func1被覆盖了,原来func1的不存在了。
    func1() #4.这里的func1是全新的func1,就是上面的赋值,此时相当于执行 inner函数

    代码从上至下执行,先加载函数变量timmer和func1。在执行上文说的第1步以及后面的,请看数字。

    执行输出:

    in func1
    此函数的执行时间为1.0009608268737793

    但是加这一步,还是不太方便。那么python给这种情况,加了一个语法糖@

    语法糖指那些没有给计算机语言添加新功能,而只是对人类来说更"甜蜜"的语法

    import time
     
    def timmer(f): # f = func1
        def inner():
            start_time = time.time()
            f() #执行func1()
            end_time = time.time()
            print('此函数的执行时间为{}'.format(end_time - start_time))
        return inner #将inner函数返回给函数调用者timmer(func1)
     
    #语法糖@
    @timmer #相当于 func1 = timmer(func1) #这一步就已经覆盖了
    def func1():
        print('in func1')
        time.sleep(1)  # 模拟程序逻辑
     
    func1() #相当于执行了inner()函数

    想测试谁,前面加@装饰器函数,即可。

    装饰器利用return制造了一个假象,func1()执行,其实是执行了inner()
    func1()已经把原来的func1()给覆盖了

    这个装饰器,还不够完整,函数不能传参数

    先来举个例子

    def func2(a1,b1): #4.执行函数,接收位置参数a1=1,b1=2
        print(a1,b1) #5. 输出1,2
    def func3(a,b): #2. 接收参数,a=1,b=2
        func2(a,b) #3. 执行函数func2(1,2)
    func3(1,2) #1.传位置参数1,2

    执行输出:

    1 2

    func3执行一遍后,将a,b的值,传给a1,b1了

    上面装饰器的例子,func1,要传2个参数a,b

    import time
     
    def timmer(f):
        def inner(a,b):
            start_time = time.time()
            f(a,b)
            end_time = time.time()
            print('此函数的执行时间为{}'.format(end_time - start_time))
        return inner
     
    @timmer
    def func1(a,b):
        print('in func1 {}{}'.format(a,b))
        time.sleep(1)  # 模拟程序逻辑
     
    func1(1,2)

    执行输出:

    in func1 12
    此函数的执行时间为1.0006024837493896

    如果有多个参数呢?改成动态参数

    import time
     
    def timmer(f):
        def inner(*args,**kwargs):
            start_time = time.time()
            f(*args,**kwargs)
            end_time = time.time()
            print('此函数的执行时间为{}'.format(end_time - start_time))
        return inner
     
    @timmer
    def func1(*args,**kwargs):
        print('in func1 {}{}'.format(args,kwargs))
        time.sleep(1)  # 模拟程序逻辑
     
    func1(1,2,a='3',b=4)

    执行输出:

    in func1 (1, 2){'b': 4, 'a': '3'}
    此函数的执行时间为1.000101089477539

    面试题,手写一个装饰器

    写装饰器,约定俗成,函数名为wrapper
    第一步

    def wrapper(func):
        def inner():
            func()
        return inner

    第二步

    def wrapper(func):
        def inner(*args,**kwargs):
            func(*args,**kwargs)
        return inner

    第三步

    def wrapper(func):
        def inner(*args,**kwargs):
            '''被装饰函数之前'''
            ret = func(*args,**kwargs)
            '''被装饰函数之后'''
            return ret
        return inner

    完整的

    def wrapper(func):
        def inner(*args,**kwargs):
            '''被装饰函数之前'''
            ret = func(*args,**kwargs)
            '''被装饰函数之后'''
            return ret
        return inner
     
    @wrapper
    def func(*args,**kwargs):
        print(args,kwargs)
        return 666
     
    print(func())
    

    作业:

    使用函数完成用户登录和注册功能

    #先让用户选择,是登陆还是注册
    #选择序号完毕之后,运行相应的程序,
    #验证成功之后,可以让其继续选择,登陆还是注册,还可以选择退出。

    1.先准备函数的雏形

    def registered_username(username):
        '''
        #判断注册用户名是否存在
        :param username: 用户名
        :return: True 存在 False 不存在
        '''
     
    def write_file(username,password):
        '''
        #写入用户列表文件
        :param username: 用户名
        :param password: 密码
        :return: True 写入成功 False 写入失败
        '''
     
    def username_password(username,password):
        '''
        #判断用户名和密码是否匹配
        :param username: 用户名
        :param password: 密码
        :return: True 匹配成功 False 匹配失败
        '''
     
    def register(*args,**kwargs):
        '''
        注册逻辑
        :return:
        '''
        pass
     
    def login(*args,**kwargs):
        '''
        登录逻辑
        :return:
        '''
        pass
     
    def user_menu(*args,**kwargs):
        '''
        # 用户菜单
        '''
        pass
    

    2.先写菜单

    def user_menu(*args,**kwargs):
        '''
        # 用户菜单
        '''
        # 判断文件是否存在,不存在创建文件
        file_exists()
        # 循环
        while True:
            # 打印菜单
            menu = ['注册', '登录', '退出']
            print('bbs系统'.center(25, '#'))
            for i in range(len(menu)):
                print('{}	{}'.format(i + 1, menu[i]))
            print(''.center(27, '#'))
            number = input('请选择序号: ').strip()
            if number == '1':
                # 执行注册程序
                register()
            elif number == '2':
                # 执行登录程序
                login()
            elif number == '3':
                exit()
            else:
                print('输入错误,请重新输入!')
    

    3.写判断用户列表文件是否存在

    def file_exists(*args,**kwargs):
        '''
        # 判断用户列表文件是否存在
        :return: True 存在 False 不存在
        '''
        # 判断文件是否存在
        if os.path.exists(file_name):
            return True
        else:
            with open(file_name, encoding='utf-8', mode='w') as mk:
                mk.write('张三    123')
                return False
    

    4.再写注册逻辑

    def register(*args,**kwargs):
        '''
        注册逻辑
        :return:
        '''
        while True:
            username = input('请输入注册的用户名,或输入q返回菜单:').strip()
            if username == '':
                print('用户名为空,请重新输入!')
            elif username.upper() == 'Q':
                break
            else:
                # 执行判断用户名函数
                result = registered_username(username)
                if result:
                    password = input('请输入您的注册的密码:').strip()
                    # 判断密码
                    if password == '':
                        print('密码为空,请重新输入!')
                    else:
                        # 执行写入用户列表文件函数
                        result = write_file(username, password)
                        if result:
                            print('注册成功!,您的用户名为: {}
    倒计时2秒返回菜单!'.format(username))
                            time.sleep(2)
                            user_menu()
                        else:
                            print('注册失败!请重试')
                else:
                    print('用户名已经存在,请重新输入!')
    

    5.判断注册用户名是否可用

    def registered_username(username):
        '''
        #判断注册用户名是否可用
        :param username: 用户名
        :return: True 可用(用户不存在) False 不可用(用户已存在)
        '''
        # 纯用户名列表,不包含密码
        user_list = []
        with open(file_name, encoding='utf-8') as f1:
            for i in f1:
                # 去空格,以空格切割,转换为列表
                li = i.strip().split()  # [张三,123]
                # 将用户名追加到列表中
                user_list.append(li[0])
            # 判断用户名是否存在列表中
            if username in user_list:
                # 返回False
                return False
            else:
                return True
    

    6.写入用户列表文件

    def write_file(username,password):
        '''
        #写入用户列表文件
        :param username: 用户名
        :param password: 密码
        :return: True 写入成功 False 写入失败
        '''
        with open(file_name, encoding='utf-8', mode='a') as f2:
            f2.write('
    {}    {}'.format(username, password))
            return True
    

    7.登录逻辑

    def login(count=0,max=3):
        '''
        登录逻辑
        :param count: 初始失败次数
        :param max: 最大失败次数
        :return:
        '''
        while count < max:
            count += 1
            username = input('请输入用户名:').strip()
            password = input('请输入密码:').strip()
            # 执行验证用户名和密码函数
            result = username_password(username,password)
            if result:
                print('登陆成功
    倒计时1秒返回菜单!')
                time.sleep(1)
                user_menu()
                break
            else:
                print('用户名或密码错误,还剩余{}次机会!'.format(max - count))
     
        # 返回主菜单
        user_menu()
    

    8.判断用户名和密码是否匹配

    def username_password(username,password):
        '''
        #判断用户名和密码是否匹配
        :param username: 用户名
        :param password: 密码
        :return: True 匹配成功 False 匹配失败
        '''
        # print(username,password)
        with open(file_name, encoding='utf-8', mode='r') as f3:
            for i in f3:
                # print(i)
                # 去空格,以空格切割,转换为列表
                li = i.strip().split()  # [张三,123]
                # 判断用户名和密码是否匹配
                if username == li[0] and password == li[1]:
                    result = True
                    # 当找到匹配时,跳出循环
                    break
                else:
                    result = False
            # 当整个用户列表遍历完成之后,再return
            return result
    

    最终完整代码如下:

    # -*- coding: utf-8 -*-
    import time,os
     
    #文件名
    file_name = 'user_list.txt'
     
     
    def file_exists(*args,**kwargs):
        '''
        # 判断用户列表文件是否存在
        :return: True 存在 False 不存在
        '''
        # 判断文件是否存在
        if os.path.exists(file_name):
            return True
        else:
            with open(file_name, encoding='utf-8', mode='w') as mk:
                mk.write('张三    123')
                return False
     
     
    def registered_username(username):
        '''
        #判断注册用户名是否可用
        :param username: 用户名
        :return: True 可用(用户不存在) False 不可用(用户已存在)
        '''
        # 纯用户名列表,不包含密码
        user_list = []
        with open(file_name, encoding='utf-8') as f1:
            for i in f1:
                # 去空格,以空格切割,转换为列表
                li = i.strip().split()  # [张三,123]
                # 将用户名追加到列表中
                user_list.append(li[0])
            # 判断用户名是否存在列表中
            if username in user_list:
                # 返回False
                return False
            else:
                return True
     
     
    def write_file(username,password):
        '''
        #写入用户列表文件
        :param username: 用户名
        :param password: 密码
        :return: True 写入成功 False 写入失败
        '''
        with open(file_name, encoding='utf-8', mode='a') as f2:
            f2.write('
    {}    {}'.format(username, password))
            return True
     
     
    def username_password(username,password):
        '''
        #判断用户名和密码是否匹配
        :param username: 用户名
        :param password: 密码
        :return: True 匹配成功 False 匹配失败
        '''
        # print(username,password)
        with open(file_name, encoding='utf-8', mode='r') as f3:
            for i in f3:
                # print(i)
                # 去空格,以空格切割,转换为列表
                li = i.strip().split()  # [张三,123]
                # 判断用户名和密码是否匹配
                if username == li[0] and password == li[1]:
                    result = True
                    # 当找到匹配时,跳出循环
                    break
                else:
                    result = False
            # 当整个用户列表遍历完成之后,再return
            return result
     
     
    def register(*args,**kwargs):
        '''
        注册逻辑
        :return:
        '''
        while True:
            username = input('请输入注册的用户名,或输入q返回菜单:').strip()
            if username == '':
                print('用户名为空,请重新输入!')
            elif username.upper() == 'Q':
                break
            else:
                # 执行判断用户名函数
                result = registered_username(username)
                if result:
                    password = input('请输入您的注册的密码:').strip()
                    # 判断密码
                    if password == '':
                        print('密码为空,请重新输入!')
                    else:
                        # 执行写入用户列表文件函数
                        result = write_file(username, password)
                        if result:
                            print('注册成功!,您的用户名为: {}
    倒计时2秒返回菜单!'.format(username))
                            time.sleep(2)
                            user_menu()
                        else:
                            print('注册失败!请重试')
                else:
                    print('用户名已经存在,请重新输入!')
     
     
    def login(count=0,max=3):
        '''
        登录逻辑
        :param count: 初始失败次数
        :param max: 最大失败次数
        :return:
        '''
        while count < max:
            count += 1
            username = input('请输入用户名:').strip()
            password = input('请输入密码:').strip()
            # 执行验证用户名和密码函数
            result = username_password(username,password)
            if result:
                print('登陆成功
    倒计时1秒返回菜单!')
                time.sleep(1)
                user_menu()
                break
            else:
                print('用户名或密码错误,还剩余{}次机会!'.format(max - count))
     
        # 返回主菜单
        user_menu()
     
     
    def user_menu(*args,**kwargs):
        '''
        # 用户菜单
        '''
        # 判断文件是否存在,不存在创建文件
        file_exists()
        # 循环
        while True:
            # 打印菜单
            menu = ['注册', '登录', '退出']
            print('bbs系统'.center(25, '#'))
            for i in range(len(menu)):
                print('{}	{}'.format(i + 1, menu[i]))
            print(''.center(27, '#'))
            number = input('请选择序号: ').strip()
            if number == '1':
                # 执行注册程序
                register()
            elif number == '2':
                # 执行登录程序
                login()
            elif number == '3':
                exit()
            else:
                print('输入错误,请重新输入!')
     
     
    # 执行菜单
    user_menu()

    执行输出:

  • 相关阅读:
    hadoop集群无法找到datanode节点问题解决
    Startup.A51说明(上)
    UCOSII基础之数据结构
    FPGA之难度
    UCOSII学习笔记【二】
    (转)PCB中各层的含义(protel中)
    UCOSII学习笔记 一
    查看51汇编,解决奇怪的问题
    滑雪
    HMM的理解
  • 原文地址:https://www.cnblogs.com/tu240302975/p/12653770.html
Copyright © 2020-2023  润新知