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('{}\t{}'.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('注册成功!,您的用户名为: {}\n倒计时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('\n{}    {}'.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('登陆成功\n倒计时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('\n{}    {}'.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('注册成功!,您的用户名为: {}\n倒计时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('登陆成功\n倒计时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('{}\t{}'.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()

执行输出:

猜你喜欢

转载自www.cnblogs.com/tu240302975/p/12653770.html