文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

python3--函数名本质,函数嵌套,闭包,装饰器

2023-01-30 21:58

关注

python函数的嵌套和作用域链

函数的嵌套调用

def max2(x,y):
    m = x if x > y else y  # 三元运算
    return m

def max4(a,b,c,d):
    res1 = max2(a,b)
    res2 = max2(res1,c)
    res3 = max2(res2,d)
    print(res1) # 23
    print(res2) # 31
    print(res3) # 31
max4(23,-7,31,11)

执行结果

23

31

31


例子2

def f1():
    print("in f1")
    def f2():
        print("in f2")
    f2()
f1()

执行结果

in f1

in f2


例子3

def f1():
    def f2():
        def f3():
            print("in f3")
        print("in f2")
        f3()
    print("in f1")
    f2()
f1()

执行结果

in f1

in f2

in f3

函数的嵌套无非就是搞清楚函数的执行顺序(不清楚可以看前面的python全栈开发10)


函数的作用域链:小范围作用域可以使用大范围的变量,但是反之不行,它是单向的

def f1():
    a = 1
    def f2():
        def f3():
            print(a)
        f3()
    f2()
f1()

执行结果,小范围可以使大范围改变

1


例子2

def f1():
    a = 1
    def f2():
        a = 2  # 单向的,小范围可以改变大范围,反之不行
    f2()
    print('a in f1 :', a)
f1()

执行结果

a in f1 : 1


函数名的本质

函数名本质上就是函数的内存地址

1,可以被引用

def func():
    print('in func')

f = func
print(f)

执行结果,这里f等于func,而func又是一个函数名,但是后面没有加(),所以上面的func没有被执行,最后打印f,实际上就是打印出func函数名的内存地址



2,可以被当做容器类型的元素

def f1():
    print('f1')

def f2():
    print('f2')

def f3():
    print('f3')

l = [f1,f2,f3]
d = {'f1':f1,'f2':f2,'f3':f3}
# 调用
l[0]()
d['f2']()

执行结果,后面的l[0](),实际上就是先取列表l里面的f1,然后就变成了f1(),其实就是执行了f1函数,而d['f2'](),同理,先取出字典'f2'对应的值f2,变成了f2(),函数名加上(),就是执行函数,所以最后的结果就是f1,f2

f1

f2


例2,用for循环批量执行函数

def f1():
    print(111)

def f2():
    print(222)

def f3():
    print(333)

def f4():
    print(444)

# 如果有100个这样的函数,写100个?使用for循环批量执行函数
l1 = []
for i in range(1, 5):
    l1.append('f' + str(i))
for i in l1:
    eval(i)()  # eval会去掉引号,后面加个(),执行函数

执行结果

111

222

333

444


3,可以当作函数的参数和返回值

第一类对象(first-class object)指

1,可在运行期创建

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

3,可存入变量的实体

不明白?就当普通变量用,如果后面加(),就执行该函数

def fl():
    print('f1')   # 4 执行f1函数,打印一次f1
def funcl(argv):  # 2 接收f1参数,所以argv = fl
    argv()        # 3 所以变成f1(), 执行f1()函数
    return argv   # 5 返回argv,即调用者,也就是funcl(fl)这个整体,等于f1
f = funcl(fl)     # 1 先算等号右边funcl(f1),执行funcl函数,把f1参数,传给argv
                  # 6 在算等号左边的,也就是 f = f1

f()               # 7 因为f=f1,所以就变成f1(),也就是执行f1函数,
                  # 所以在打印一次f1,最终结果打印2次f1

执行结果

f1

f1


例2

def f1():      # 4 打印 666,最终结果就是666
    print(666)

def f2(x):  # 2 接收传过来的实参f1,那么x = f1
    x()     # 3 就变成 f1(),执行f1()函数

f2(f1)  # 1执行f2(),把实参f1,传给f2里面的x

执行结果

666


例3

def f1():      # 5 打印666,最终结果666
    print(666)

def f2(x):    # 2 接收实参f1,所以x = f1
    return x  # 3 返回x也就是f1给函数调用者,也就是f2(f1)

f2(f1)()     # 1 执行f2()函数,把实参f1传给f2里面的x
             # 4 所以就变成f1(),也就是执行了f1函数

执行结果

666


闭包

def func():
    name = 'hello world'
    def inner():
        print(name)

闭包函数

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

函数内部定义的函数称为内部函数

由于有了作用域的关系,我们就不能拿到函数内部的变量和函数了,如果我们就是想拿怎么办呢?返回呀

我们都知道函数内的变量我们想在函数外部用,可以直接返回这个变量,那么如果我们想在函数外部调用函数内部的函数呢?

是不是直接就把这个函数的名字返回就好了?

这才是闭包函数最常用的用法

def func():
    name = 'eva'  # 2 定义一个name = 'eva'
    def inner():  # 6 执行函数,打印name的值,之前定义了name= 'eva',所以打印eva
        print(name)
    return inner  # 3 返回inner值给调用者,也就是func()这个整体
f = func()   # 1 先算等号右边,执行func()函数
             # 4 在算等号左边,也就是f = inner
f()          # 5 因为f=inner,也就是执行了inner()这个函数

执行结果

eva


判断闭包函数的方法__closure__

# 输出的__closure__有cell元素:是闭包函数
def func():
    name = 'eva'
    def inner():
        print(name)
    print(inner.__closure__)
    return inner
f = func()
f()

执行结果

blob.png

输出的__closure__有cell 则是闭包函数


例子2

name = 'eqon'
def func():
    def inner():
        print(name)
    print(inner.__closure__)
    return inner
f2 = func()
f2()

执行结果

blob.png

输出的__closure__为None,则不是闭包函数


例3

def wrapper():
    money = 1000
    def func():
        name = 'eva'
        def inner():
            print(name, money)
        print(inner.__closure__)
        return inner
    return func

f = wrapper()  # f = func
i = f()   # func()  i = inner
i()  # inner()

执行结果,是闭包函数

blob.png


例4

from urllib.request import urlopen
def index():
    url = 'http://www.xiaohua100.cn/index.html'
    def get():
        return urlopen(url).read()  # decode('utf-8')解码成utf-8
    print(get.__closure__)
    return get

xiaohua = index() # xiaohua = get
content = xiaohua() # get() content = read.url的内容
print(content) # 打印网站内容

执行结果,有cell则属于闭包函数

blob.png


闭包函数的好处

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


python装饰器

什么是装饰器?

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

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


装饰器的形成过程

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

# 装饰器
import time
def funcl():         # 8 执行此函数
    print("hello world")  # 9 打印hello world

def timer(f):   # 2 接收传过来的参数funcl,所以f = funcl
    def inner():
        start_time = time.time()  # 6 计算开始时间
        f()                       # 7 f = funcl ,看第二部的值,f加()就是执行了funcl()这个函数
        time.sleep(0.3)           # 10 模拟延迟
        end_time = time.time()    # 11 计算结束后的时间
        print('此函数的执行效率{}'.format(end_time - start_time)) # 12 打印出函数执行的效率(end-start)
    return inner  # 3 把inner值返回给调用者,调用者是timer(funcl)这个整体

funcl = timer(funcl)  # 1 执行等号右边的timer()函数,把funcl参数传给timer里面的变量f
                      # 4 在算等号左边的,所以 funcl = inner
funcl()               # 5 即inner加(),就是执行inner()这个函数

执行结果

hello world

此函数的执行效率0.3008289337158203


但是如果有多个函数,我都想让你测试它们的执行时间,你每次是不是都得func1=timer(funcl)?这样还是有点麻烦,因为这些函数的函数名可能是不相同,有funcl,func2,graph等,所以更简单的方法,python给你提供了,那就是语法糖。

import time
def timer(f):
    def inner():
        start_time = time.time()
        f()
        time.sleep(0.3)
        end_time = time.time()
        print("此函数的执行效率{}".format(end_time-start_time))
    return inner
@timer # 即 func = timer(func)
def func():
    print("大家好,我是渣渣辉!")

func()

执行结果

大家好,我是渣渣辉!

此函数的执行效率0.3001282215118408


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

这就是最简单版本的装饰器

利用return制造了一个假象,func()执行,其实是执行了inner(),而func变量名传给了timer里的f,func()已经把原来的func()覆盖了


刚才讨论的装饰器都是不带参数的函数,现在要装饰一个带参数的函数怎么办呢?

例1,装饰一个带参数的函数

import time
def timer(f):   # f = func
    def inner(a): # a = 1
        start_time = time.time()
        f(a)
        time.sleep(0.5)
        end_time = time.time()
        print(end_time - start_time)
    return inner

@timer # func = timer(func) # inner
def func(a):
    print(a) #打印1

func(1)

执行结果

1

0.5007100105285645


例2,装饰一个带两个参数的函数

import time
def timer(f):
    def inner(a,b):
        start_time = time.time()
        f(a,b)
        time.sleep(0.5)
        end_time = time.time()
        print("此函数的执行效率{}".format(end_time - start_time))
    return inner
@timer # func = timer(func) 实际上就是 func = inner
def func(a, b):
    print(a, b)

func('大家好','渣渣辉')

执行结果

大家好 渣渣辉

此函数的执行效率0.500657320022583


可以接收任意参数,动态传参

例子

import time
def timer(f):
    def inner(*args, **kwargs):
        start_time = time.time()
        f(*args, **kwargs)
        time.sleep(0.4)
        end_time = time.time()
        print("此函数的执行效率为{}".format(end_time - start_time))
    return inner

@timer
def func1(a, b):
    print("I am func1")

@timer
def func2(a, b, c):
    print("in func2 and get a={},b={},c={}".format(a,b,c))

func1(10, 20)
func2("大家好", "我是", "渣渣辉")

执行结果

I am func1

此函数的执行效率为0.400327205657959

in func2 and get a=大家好,b=我是,c=渣渣辉

此函数的执行效率为0.40012335777282715


面试题,手写装饰器

def wrapper(func):
    def inner(*args, **kwargs):
        '''被装饰函数之前'''
        ret = func(*args, **kwargs)
        '''被装饰函数之后'''
        return ret
    return inner()
@wrapper
def func(a, b):
    pass
    return 123  #写成这样基本就差不多了


练习题

先让用户选择,是登陆还是注册

选择序号完毕之后,运行相应的程序,

验证成功之后,可以让其继续选择,登陆还是注册,还可以选择退出(自己增加一个可修改密码功能)

先在同目录下创建一个user_pwd.txt账号密码文件,代码如下

#!/usr/bin/env python
# coding: utf-8
import time
import os
import platform
__author__ = 'www.py3study.com'

'''此函数为装饰器'''
def timer(func):
    def inner(*args, **kwargs):
        start_time = time.time()
        func(*args, **kwargs)
        end_time = time.time()
        print('|| 此程序执行总时间:{}秒'.format(end_time - start_time))
    return inner

'''语法糖,计算程序运行总时长,退出程序时执行'''
@timer
class LandingCertification(object):
    def __init__(self, *args, **kwargs):
        self.timeout = 3
        self.flag = True
        self.result = False
        self.run = {
            '0': self.drop_out,
            '1': self.register,
            '2': self.landing,
            '3': self.account_password,
            '4': self.transfer_parameters,
        }
        self.main()

    '''程序入口'''
    def main(self, *args, **kwargs):
        while True:
            self.mainMenu()
            op = input(u'|| 输入选项:').strip()
            '''map判断输入是否符合条件'''
            if op == '0':
                return True
            elif op in map(str, range(len(self.run))):
                self.run.get(op)()
            else:
                self.Error()
                continue

    '''此函数用于打印菜单'''
    def mainMenu(self, *args, **kwargs):
        self.clear()
        print(u'='*40)
        print(u'|| 0:退出程序')
        print(u'|| 1:注册')
        print(u'|| 2:登陆')
        print(u'|| 3:查看已存在账号')
        print(u'|| 4:修改密码(流程:账号名-->老密码-->新密码-->修改成功)')
        print(u'='*42)

    '''此函数用于清屏'''
    def clear(self, *args, **kwargs):
        OS = platform.system()
        if (OS == u'Windows'):
            os.system('cls')
        else:
            os.system('clear')

    '''此函数用于设置延迟'''
    def Error(self, *args, **kwargs):
        print(u'|| 只能输入0-3的整数,等待{}秒后重新输入'.format(self.timeout))
        time.sleep(self.timeout)

    '''此函数用于文件操作,查询'''
    def account_password(self, *args, **kwargs):
        with open('user_pwd.txt', encoding='utf-8', mode='r') as f1:
            for i in f1:
                print('|| {}'.format(i.strip()))

    '''此函数用于账号注册'''
    def register(self, *args, **kwargs):
        while self.flag:
            username = input("|| 请输入用户名:").strip()
            with open('user_pwd.txt', encoding='utf-8', mode='r') as f2:
                for i in f2:
                    li = i.strip().split()
                    if username == li[0] or not username.strip():
                        print('|| (账号名已存在)or(账号名不能为空)')
                        break
                else:
                    password = input('|| 请输入密码:').strip()
                    again_password = input('|| 再次输入密码,确认:').strip()
                    if password == again_password:
                        with open('user_pwd.txt', encoding='utf-8', mode='a') as f3:
                            f3.write('\n{} {}'.format(username, password))
                            print('|| 注册成功')
                            return self.flag
                    else:
                        print("|| 两次密码不一样")

    '''此函数用于账号登陆'''
    def landing(self, *args, **kwargs):
        max = 3
        count = 0
        while count < max:
            count += 1
            landing_username = input("|| 输入账号名:").strip()
            if not landing_username.strip():
                print('|| 账号名不能为空')
            else:
                landing_password = input("|| 输入密码:").strip()
                with open('user_pwd.txt', encoding='utf-8', mode='r') as f4:
                    for i in f4:
                        s3 = i.strip().split()
                        if landing_username == s3[0] and landing_password == s3[1]:
                            self.result = True
                            f4.close()
                            break
                        else:
                            self.result = False
                    if self.result:
                        print('|| 登陆成功!')
                        return True
                    else:
                        print('|| 账号或者密码错误,还有{}次机会'.format(max - count))
                if max - count == 0:
                    return True

    '''此函数用于传参给modify进行密码的修改'''
    def transfer_parameters(self, *args, **kwargs):
        while True:
            transfer_username = input("|| 输入需要更改的账号名:").strip()
            if not transfer_username.strip():
                print("|| 账号不能为空")
            else:
                old_password = input("|| 输入老的密码:").strip()
                new_password = input('|| 输入新的密码:').strip()
                with open("user_pwd.txt", encoding='utf-8', mode='r') as f9:
                    for i in f9:
                        s4 = i.strip().split()
                        if transfer_username == s4[0] and old_password == s4[1]:
                            self.result = True
                            f9.close()
                            self.modify(old_password, new_password)
                            break
                        else:
                            self.result = False
                    if self.result:
                        print("|| 密码修改成功!")
                        return True
                    else:
                        print("|| 此账号不存在或密码错误")

    '''此函数用于修改密码'''
    def modify(self, *args, **kwargs):
        with open('user_pwd.txt', encoding='utf-8', mode='r') as f6, \
                open('user_pwd.bak', encoding='utf-8',mode='w') as f7:
            for i in f6:
                new_i = i.replace(*args, **kwargs)
                f7.write(new_i)
            f6.close()
            f7.close()
            os.remove('user_pwd.txt')
            os.rename('user_pwd.bak', 'user_pwd.txt')

    '''此函数用于退出程序'''
    def drop_out(self, *args, **kwargs):
        print(u"|| Good bye!")
        self.main()
        return True

if __name__ == '__main__':
    LandingCertification()

运行效果

download.gif


阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯