首页 > 编程语言 >python基础七(函数名称空间及作用域、函数对象、函数嵌套、闭包函数、装饰器)

python基础七(函数名称空间及作用域、函数对象、函数嵌套、闭包函数、装饰器)

时间:2023-04-05 23:33:10浏览次数:52  
标签:闭包 index return 函数 作用域 func print def

一 名称空间(namespaces):存放名字的地方,是对栈区的划分。

 

有了名称空间之后,就可以在栈区中存放相同的名字,详细的名称空间。
分三种
1.1 内建名称空间
存放的名字:存放的python解释器内置的名字
print
<built-in function print>
存活周期:python解释器启动则产生,python解释器关闭则销毁。
1.2 全局名称空间
伴随python文件的开始执行/执行完毕而产生/回收,是第二个被加载的名称空间,文件执行过程中产生的名字都会存放于该名称空间中
存放的名字:只要不是函数内定义、也不是内置的,剩下的都是全局名称
存活周期:python文件执行则产生,python文件运行完毕后销毁
例:
def func():
    x=2
y=2
import os
1.3 局部名称空间
存放的名字:在调用函数时,运行函数体代码过程中产生的函数内的名字(伴随函数的调用/结束而临时产生/回收,
函数的形参、函数内定义的名字都会被存放于该名称空间中)
存活周期:在调用函数时存活,函数调用完毕后则销毁
def foo(x):
    y = 3  # 调用函数时,才会执行函数代码,名字x和y都存放于该函数的局部名称空间中
1.4 名称空间的加载顺序
内置名称空间>全局名称空>局部名称空间

1.5 销毁顺序
加载顺序相反

1.6 查找顺序(处在哪一层,就从哪一层往上找)
查找一个名字,必须从三个名称空间之一找到,查找顺序为:局部名称空间--->全局名称空间--->内置名称空间。
input = 111
def func():
    input = 222
    print(input)  # 局部名称空间
func()
print(input)  # 全局名称空间
如果局部名称空间和全局名称空间都未找到,就去内置空间找,打印结果为<built-in function input>
# 示范1
x = 111
def func():
    print(x)
func()  # x处于全局名称空间,局部空间找不到,就执行全局名称空间,跟x的位置没有关系
# 111
# 示范2:名称空间的“嵌套”关系是以函数定义阶段为准,与调用位置无关
x = 1
def func():
    print(x)
def foo():
    x = 22
    func()
foo()
# 1
# 示范3:函数嵌套定义
input = 111
def fun1():
    input = 222
    def fun2():
        input = 333
        print(input)
    fun2()
fun1()  # 打印结果333
# 范例4:
x = 111
def func():
    x = 222
    print(x)
func()
print(x)
ps:名称查找,先定义中查找,再一层一层名称空间往上找。

二 作用域--->作用范围

2.1 全局作用域:内置名称空间、全局名称空间
(1)全局存活
(2)全局有效:被所有函数共享

局部作用域:局部名称空间的名字
(1)临时存活
(2)局部有效:函数内有效

2.2 作用域与名字查找的优先级
LEGB 代表名字查找顺序: locals -> enclosing function -> globals -> __builtins__
locals 是函数内的名字空间,包括局部变量和形参
enclosing 外部嵌套函数的名字空间(闭包中常见)
globals 全局变量,函数定义所在模块的名字空间
builtins 内置模块的名字空间

三 global与nonlocal

# 范例1:
x = 111
def func():
    x = 222
    print(x)
func()
print(x)
# 222
# 111

# 范例2:如果在局部想要修改全局的名字对应的值(不可变类型),需要用global
x = 111
def func():
    global x  # 申明x这个名字是全局的名字,不要再造新的名字了
    x = 222
    print(x)
func()
print(x)
# 222
# 222
# 范例3:可变类型不用申明,直接加。
l = [111, 222]
def func():
    l.append(333)
func()
print(l)
# [111, 222, 333]
# nonlocal(了解):修改函数外层函数包含的名字对应的值(不可变类型)
def f1():
    x = 2
    def f2():
        nonlocal x
        x = 3
    f2()  # 调用f2(),修改f1作用域中名字x的值
    print(x)  # 在f1作用域查看x
f1()
# 3

四 名称空间名字查找复习及总结

# 例1
x = 1
def func():
    print(x)
def foo():
    x = 22
    func()
x = 333
foo()
# 333

# 例2
input = 111
def f1():
    def f2():
        input=333
        print(input)
    input=222
    print('f1 out--->', input)
    f2()
f1()
# f1 out---> 222
# 333

名称空间namespaces
名字---》栈区
名称空间是对栈的一种划分,真正存在的是栈区,名称空间只是一种虚拟的划分

内置名称空间
全局名称空间
局部名称空间

重点1:
    名词查找:当前所在的位置向外查找
        局部---》全局---》内置

重点2:
    名称空间只有优先级之分,本身并无嵌套关系,画图只是为了理解

重点3:
    名称空间的嵌套关系决定了名字的查找顺序
    而名称空间的嵌套关系是以函数定义阶段为准的,
    既函数的嵌套关系与名字的查找顺序是在定义阶段就已经确定好的

全局作用域:内置名称空间+全局名称空间
    全局存活,全局有效

局部作用域:
    临时存活,局部有效

五 函数对象

# 精髓:可以把函数当成变量去用
# func=内存地址
def func():
    print('hello world')
# 1、可以赋值
# f = func()
print(func())  # 是return的返回值
# print(f, func)  # f是return的返回值None,func就是内存地址

# 2、可以当做函数的参数传给另一个函数
def foo(x):
    print(x)
foo(func)  # foo(func的内存地址)
# <function func at 0x0000021B8BBD9280>

# 3、可以当做函数另外一个函数的返回值
def foo(x):
    return x
res = foo(func)
print(res)
res()
# <function func at 0x0000011EFFDD9280>
# hello world

# 4、可以当做容器类型的一个元素
l = [func, ]
print(l)
l[0]()
# [<function func at 0x000001B666C49280>]
# hello world
# 例:ATM取款功能
def quit():
    print('退出')
def enter():
    print('登录')
def transfer():
    print('转账')
def withdrwal():
    print('取款')
def balance_inquriy():
    print('查询余额')
choice_dic = {'1': enter,
              '2': transfer,
              '3': withdrwal,
              '4': balance_inquriy}
while True:
    print('''
    0 退出
    1 登录
    2 转账
    3 取款
    4 查询余额''')
    choice = input('请选择:').strip()
    if not choice.isdigit():
        print('必须输入数字,ox:')
        continue
    if choice == '0':
        break
    if choice in choice_dic:
        choice_dic[choice]()
    else:
        print('请选择正确的选项')

六 函数嵌套

# 函数嵌套
# 1、函数的嵌套调用:在调用一个函数的过程中又调用其他函数
# 求最大数:
def max(x, y):
    if x > y:
        return x
    else:
        return y
def max1(a, b, c, d):
    res1 = max(a, b)
    res2 = max(res1, c)
    res3 = max(res2, d)
    return res3
res = max1(1, 2, 5, 4)
print(res)

# 2、函数的嵌套定义:在函数内定义其他函数
def f1():
    def f2():
        pass

六 闭包函数

1、大前提:
闭包函数=名称空间与作用域+函数嵌套+函数对象
核心点:名字的查找关系是以函数定义阶段为准

2、什么是闭包函数
“闭”函数指的该函数内嵌函数
“包”函数指的该函数包含对外层函数作用域名字的引用(不是全局作用域)
# 闭包函数:名称空间与作用域的应用+函数嵌套
def f1():
    x=111
    def f2():
        print(x)
    f2()
x=222
def foo():
    x=333
    f1()
foo()
# 111

# 闭包函数:函数对象,函数作为return的返回值
def f1():
    x = 111
    def f2():
        print(x)
    return f2  # 返回f2的内存地址
f = f1()  # f2的内存地址,<function f1.<locals>.f2 at 0x000001BFAC6071F0>
print(f)
f()  # f2函数的调用,把f2内嵌函数当成了全局作用域使用
# <function f1.<locals>.f2 at 0x000001A390AB24C0>
# 111
3、为何要有闭包函数--->闭包函数的应用
两种为函数体传参的方式
# 方式一:直接把函数体需要的参数定义成形参
def f(x):
    print(x)
f(1)
# 方式二:函数对象方式进行传参
def f1():
    x = 1
    def f2():
        print(x)
    return f2
f = f1()
f()
# 1

# 改进
def f1(x):
    # x=1
    def f2():
        print(x)
    return f2
f = f1(1)
f()

# 改进2
def f1(x):
    # x=1
    def f2(y, m):
        print(f2)  # <function f1.<locals>.f2 at 0x000001BBEAD571F0>
        print(x)
        print(y, m)
    return f2
f = f1(1)
f(2, 3)
print(f)  # <function f1.<locals>.f2 at 0x000001BBEAD571F0>,f和f2的内存地址是一样的,f的调用,就是f2的调用

七 装饰器

1、知识储备
*args,**kwargs
名称空间与作用域
函数对象
函数的嵌套定义
闭包函数

2、装饰器
1)、什么是装饰器
“器”指的是工具,可以定义成函数
“装饰”指的是为其他事物添加额外的东西点缀
装饰器:指的是定义一个函数,该函数是用来为其他函数添加额外的功能
2)、为何要用装饰器
开放封闭原则
开放:指的是对拓展功能是开放的
封闭:指的是对修改源代码是封闭的
装饰器就是在不修改装饰器对象源代码以及调用方式的前提下为被装饰对象添加新功能
3、如何用装饰器
需求:在不修改index函数的源代码以及调用方式的前提下为其添加统计运行时间的功能
# 方案一:在原代码基础上修改
import time
def index(x, y):
    start = time.time()
    time.sleep(1)
    print('hello world:{},{}.'.format(x, y))
    stop = time.time()
index('xiaobao', 'lq')

# 方案二:没有修改被装饰对象的调用方式,也没有修改其源代码,并且加上了新功能,但是代码冗余
import time
def index(x, y):
    time.sleep(1)
    print('hello world:{},{}.'.format(x, y))
start = time.time()
index('xiaobao', 'lq')
stop = time.time()
print(stop - start)

# 方案三:解决了方案二代码冗余问题,但带来一个新问题既函数的调用方式改变
import time
def index(x,y):
    time.sleep(1)
    print('hello world:{},{}.'.format(x,y))
def wrapper():
    start = time.time()
    index('xiaobao', 'lq')
    stop = time.time()
    print(stop - start)
wrapper()
# 方案三优化一:如何在方案三的基础上不改变函数的调用方式(方案三的优化一,将index的参数写活了)
import time
def index(x, y):
    time.sleep(1)
    print('hello world:{},{}.'.format(x, y))
def wrapper(*args, **kwargs):
    start = time.time()
    index(*args, **kwargs)
    stop = time.time()
    print(stop - start)
wrapper('xiabao', 'lq')

# 方案三优化二:在优化一的基础上把被装饰对象写活了,原来只能装饰index
import time
def index(x, y):
    time.sleep(1)
    print('hello world:{},{}.'.format(x, y))
print(index)
def outer(func):  # func=index(闭包函数)
    def wrapper(*args, **kwargs):
        start = time.time()
        func(*args, **kwargs)  # func就是闭包函数中例子中的x的查找
        stop = time.time()
        print(stop - start)
    return wrapper
index = outer(index)  # 这里的index实际就是wrapper的内存地址
index('xiaobao', 'lq')
print(index)

# 方案三优化(被装饰函数有return的情况):最终优化完成。
import time
def home(m, n):
    time.sleep(1)
    print('welcome to home:{}和{}'.format(m, n))
    return 'thanks'
res1 = home('xiaobao', 'zd')
print('未被装饰的返回值--->', res1)
def outer(func):
    def wrapper(*args, **kwargs):
        start = time.time()
        func(*args, **kwargs)
        stop = time.time()
        print(stop - start)
    return wrapper
home = outer(home)
res2 = home('xiaobao', 'zd')
print('装饰后的返回值--->', res2)
# welcome to home:xiaobao和zd
# 未被装饰的返回值---> thanks
# welcome to home:xiaobao和zd
# 1.0016324520111084
# 装饰后的返回值---> None
# 装饰器
def outer(func):
    def wrapper(*args, **kwargs):
        start = time.time()
        res = func(*args, **kwargs)
        stop = time.time()
        print(stop - start)
        return res
    return wrapper
# 在被装饰器对象正上方的单独一行写@装饰器名字
# index函数
@outer  # index=outer(index)
def index(x, y):
    time.sleep(1)
    print('hello world:{},{}.'.format(x, y))
# home函数
@outer  # home=outer(home)
def home(m, n):
    time.sleep(1)
    print('welcome to home:{}和{}'.format(m, n))
    return 'thanks'
index('xiaobo', 'lq')
res3 = home('xiaobao', 'zd')
print(res3)

# hello world:xiaobo,lq.
# 1.0000083446502686
# welcome to home:xiaobao和zd
# 1.0008976459503174
# thanks

# 延伸:多个装饰器,加载顺序与运行顺序
@deco1  # index=deco1(deco2.wrapper的内存地址)
@deco2  # deco2.wrpper的内存地址=deco2(deco3.wrpper的内存地址)
@deco3  # deco3.wrpper的内存地址=deco3(index)
def index():
    pass
# 总结无参装饰器模板
def outer(func):
    def wrapper(*args, **kwargs):
        # 1、调用原函数
        # 2、为其增加新功能
        res = func(*args, **kwargs)
        return res
    return wrapper

# 实现注册的装饰功能
def auther(func):
    def wrapper(*args, **kwargs):
        inp_name = input('请输入你的名字: ').strip()
        inp_pwd = input('请输入你的密码: ').strip()
        with open(r'user.txt', mode='r', encoding='utf-8') as f:
            for line in f:  # 生成的line是字符串(含了.read的功能),先.strip去除字符串前后的\n,再.split以':'为界切分成列表。
                # print(line,end='')  #liuqiao:123\n
                print(line.strip('\n').split(':'))
                u, p = line.strip('\n').split(':')
                # 把用户输入的名字与密码与读出内容做比对
                if inp_name == u and inp_pwd == p:
                    print('登录成功')
                    res = func(*args, **kwargs)
                    print(u, p)
                    return res
            else:
                print('账号名或者密码错误')
    return wrapper
@auther
def index():
    print('开始程序接下来的工作---》')
index()
# 请输入你的名字: xiaobao
# 请输入你的密码: 123
# 登录成功
# 开始程序接下来的工作---》
# xiaobao 123

八 装饰器补充(wrapper被装饰)

# 偷梁换柱,既将原函数名指向的内存地址偷梁换柱成wrapper函数,所以应该将wrapper做的跟原函数一样才行
def outter(func):
    def wrapper(*args, **kwargs):
        res = func(*args, **kwargs)
        return res
    wrapper.__name__ = func.__name__  # 手动添加将原函数的属性赋值给wrapper函数,这里wrapper成了被装饰对象。
    wrapper.__doc__ = func.__doc__
    return wrapper
@outter  # index=outter(index)
def index(x, y):
    ''''这个是主页功能'''
    print(x, y)
index(1, 2)  # wrapper(1,2)
print(index.__name__)
print(index.__doc__)

# 优化二
from functools import wraps
def outter(func):
    @wraps(func)  # 自动将原函数的属性赋值给wrapper函数,这里wrapper成了被装饰对象。
    def wrapper(*args, **kwargs):
        res = func(*args, **kwargs)
        return res
    # wrapper.__name__= func.__name__
    # wrapper.__doc__= func.__doc__
    return wrapper
@outter  # index=outter(index)
def index(x, y):
    ''''这个是主页功能'''
    print(x, y)
index(1, 2)  # wrapper(1,2)
print(index.__name__)
print(index.__doc__)
print(index.__code__)

九 有参装饰器

1、知识储备:
from functools import wraps
def outter(func):
    @wraps(func)
    def wrappper(*args, **kwargs):
        print('好累')
        res = func(*args, **kwargs)
        return res
    return wrappper
# 由于语法糖@的限制,outter函数只能有一个参数,并且该函数只用来接受被装饰对象的内存地址
@outter  # index=outter(index)
def index(x, y):
    print(x, y)
index(1, 2)
偷梁换柱之后
index的参数什么样子,wrapper的参数就应该什么样子
index的返回值是什么样子,wrapper的返回值就应该什么样子
index的属性什么样子,wrapper的属性就应该什么样子(@wraps的使用)

2、有参装饰器应用
注册功能的装饰器,用户名和密码来之三种存储(file,sql,ldp)
from functools import wraps
def auther(db):
    def deco(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            user = input('输入名字:').strip()
            pwd = input('请输入密码:').strip()
            if user == "xiaobao" and pwd == "123":
                if db == "file":
                    print('账户来之文件')
                    res = func(*args, **kwargs)
                    return res
                elif db == "sql":
                    print('账户来之数据库')
                    res = func(*args, **kwargs)
                    return res
                elif db == "lap":
                    print('账户来之lap')
                    res = func(*args, **kwargs)
                    return res
                else:
                    print("不支持该db类型")
            else:
                print("账号或密码错误")
        return wrapper
    return deco

@auther(db='file')  # myfile=deco(myfile)-->@deco--->deco=auth(db='file')--->@auth(db='file')
def myfile(x, y):
    print(x, y)
@auther(db='sql')
def mysql(x, y):
    print(x, y)
@auther(db='lap')
def mylap(x, y):
    print(x, y)
# myfile(1, 2)
mysql(3, 4)
# 输入名字:xiaobao
# 请输入密码:123
# 账户来之数据库
# 3 4
# 总结:有参装饰器模板
def auth(x, y, z):
    def deco(func):
        def wrapper(*args, **kwargs):
            # 这些地方就是要加的x,y,z参数的地方(也是加功能)
            print(x+y+z)
            res = func(*args, **kwargs)
            # 这些地方就是要加的x,y,z参数的地方(也是加功能)
            return res
        return wrapper
    return deco

@auth(x=1, y=2, z=3)
def index():
    pass
index()
# 6

十 叠加多个装饰器

叠加多个装饰器的加载、运行分析
# index=deco3(deco2(deco1(index)))
# @deco1  # index=deco1(deco2.wrapper的内存地址)
# @deco2  # deco2.wrpper的内存地址=deco2(deco3.wrpper的内存地址)
# @deco3  # deco3.wrpper的内存地址=deco3(index)
# def index():
#     pass

def deco1(func1):
    def wrapper1(*args,**kwargs):   # func1就是wrapper2的内存地址
        print('deco1.wrapper1运行')
        res1=func1(*args,**kwargs)
        print('加载顺序deco1')
        return res1
    return wrapper1

def deco2(func2):
    def wrapper2(*args,**kwargs):   # func2就是wrapper3的内存地址
        print('deco2.wrapper2运行')
        res2=func2(*args,**kwargs)
        print('加载顺序deco2')
        return res2
    return wrapper2

def deco3(x):
    def outter(func3):
        def wrapper3(*args,**kwargs):   # func3就是被装饰对象index函数的内存地址
            print('deco3.wrapper3运行',x)
            res3=func3(*args,**kwargs)
            print('加载顺序deco3')
            return res3
        return wrapper3
    return outter

# 加载顺序自下而上
@deco1  # -->index=wrapper1(wrapper2的内存地址)------------>index=wrapper1的内存地址
@deco2  # -->index=wrapper2(wrapper3的内存地址)------------>index=wrapper2的内存地址
@deco3('xiaobao')  # -->@outter3-->index=outter3(index)---->index=wrapper3的内存地址
def index(x,y):
    print(x,y)

# print(index)    # <function deco1.<locals>.wrapper1 at 0x00000141D60F7790>

# 执行顺序自上而下,既wrapper1-->wrapper2-->wrapper3-->index函数体-->return res3-->return res2-->return1 res1
#
index('zd','lq')
'''
函数调用结果
deco1.wrapper1运行
deco1.wrapper2运行
deco3.wrapper3运行 xiaobao
zd lq
加载顺序deco3
加载顺序deco2
加载顺序deco1
'''

标签:闭包,index,return,函数,作用域,func,print,def
From: https://www.cnblogs.com/coderxueshan/p/17286220.html

相关文章

  • C#调用C++ 平台调用P/Invoke 函数指针/回调函数【二】
    Gitp-invoke源码地址 C#调用C++平台调用P/Invoke调用约定【一】C#调用C++平台调用P/Invoke函数指针/回调函数【二】C#调用C++平台调用P/Invoke字符串【三】C#调用C++平台调用P/Invoke错误码LastError【四】C#调用C++平台调用P/Invoke结构体--输入输出参数、返回值、返......
  • MYSQL基础知识之函数
     1、函数概念函数是指一段可以直接被另一段程序调用的程序或代码2、字符串函数MySQL常用的字符串函数有:# CONCAT(S1,S2,...Sn):字符串拼接,将S1,S2,...Sn拼接成一个字符串SELECTCONCAT('hello','world'); #LOWER(str):将字符串str全部转为小写SELECTLOWER('ABCD');......
  • __sync_fetch_and_add函数
    (一)背景实现多线程环境下的计数器操作,统计相关事件的次数.当然我们知道,count++这种操作不是原子的。一个自加操作,本质是分成三步的:1从缓存取到寄存器2在寄存器加13存入缓存。由于时序的因素,多个线程操作同一个全局变量,会出现问题。这也是并发编程的难点。在目前多核条件下......
  • 实验3 函数应用编程
    1.实验任务1#include<stdio.h>#include<stdlib.h>#include<time.h>#include<windows.h>#defineN80voidprint_text(intline,intcol,chartext[]);voidprint_spaces(intn);voidprint_blank_lines(intn);intmain(){int......
  • C 库函数
    (一)malloc()说明:C库函数 void*malloc(size_tsize) 分配所需的内存空间,并返回一个指向它的指针。原型:void*malloc(size_tsize)//内存块的大小,以字节为单位。//该函数返回一个指针,指向已分配大小的内存。如果请求失败,则返回NULL。例子:var=shared_ptr_new(malloc......
  • C++函数库——全排列
    全排列,顾名思义,对一个无序数组或者有序数组写出其对应的所有组合,实则为从当前数组顺序开始,排列出所有比当前序列大(默认)或者小的所有组合,所以如果初始为无序数组,则得到的结果并非所有组合1.next_permutation,获取下一个排列结果,及获取比当前序列小的下一个序列1#include<iost......
  • 第四十四篇 vue - 进阶主题 - 渲染函数 & JSX
    渲染函数&JSX在绝大多数情况下,Vue推荐使用模板语法来创建应用。然而在某些使用场景下,我们真的需要用到JavaScript完全的编程能力。这时渲染函数就派上用场了基本用法1、创建VnodesVue提供了一个h()函数用于创建vnodesimport{h}from'vue'constvnode=h(......
  • 动态内存管理——动态内存函数
    动态内存管理,也叫动态内存分配,顾名思义:动态的来分配内存。1.为什么存在动态内存分配我们已经知道的内存分配方式有:创建一个变量:整型,分配4个字节的空间;长整型;分配8个字节的空间,又或者创建一个数组,创建一个函数的形参........但是,这些开辟空间的方式都有两个特点:1.空间大小固定; 2.......
  • JavaScript之函数,变量作用域,let(局部变量),const(常量)
    一.函数方法:面向对象特有的,它是对象的一部分,一个对象包含属性和方法函数:它的功能类似于方法,但是函数的写法是直接与类文件一体的,方法是包含在类文件中的,函数和类文件是一级目录JavaScript中的函数是包含在函数的定义方式一般用 function 来声明所有函数,他不同于其它高级语......
  • 实验三 函数应用编程
    task1.c#include<stdio.h>#include<stdlib.h>#include<time.h>#include<windows.h>#defineN80voidprint_text(intline,intcol,chartext[]);voidprint_spaces(intn);voidprint_blank_lines(intn);intmain()......