我的Python之旅第四天

一 名称空间、作用域、取值顺序

1 名称空间

当程序运行时,代码从上至下依次执行,它会将变量与值得关系存储在一个空间中,这个空间就叫做名称空间,也叫命名空间、全局名称空间。

当程序遇到函数时,他会将函数名存在内存中,对函数体漠不关心。

当函数执行时,内存会临时开辟一个空间,存放函数体里面的代码(变量,代码等),函数外面访问不到临时空间的内容,随着函数的执行完毕,临时名称空间会释放掉,这个临时开辟的空间就叫名称空间,也叫局部名称空间。

Python中名称空间分三种:

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

2 作用域

  • 全局作用域

    内置名称空间

    全局名称空间

  • 局部作用域

    局部名称空间

3 加载顺序

内置名称空间à全局名称空间(当程序执行时)à局部名称空间(当函数调用时)

4 取值顺序:单向不可逆

局部名称空间(当函数调用时)à全局名称空间(当程序执行时)à内置名称空间

二 内置函数globals、locals

1返回值

(1)globals()

返回一个字典,字典里面的内容是全局作用域的内容。

扫描二维码关注公众号,回复: 1052903 查看本文章

(2) locals

返回一个字典,当前位置 的所有变量。

2 示例

name='ShiPotian'
age='1000'
sex='f'
def func1():
    name2='XieYanke'
    age=100
    print('全局作用域为:',globals())
    print('局部作用域为:',locals())
func1()
结果为:

全局作用域为: {'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x00000176723DC198>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, '__file__': 'D:/Python/python周末班/day04/globals,locals.py', '__cached__': None, 'name': 'ShiPotian', 'age': '1000', 'sex': 'f', 'func1': <function func1 at 0x0000017672033E18>}

局部作用域为: {'age': 100, 'name2': 'XieYanke'}
示例1
name='ShiPotian'
age='18'
sex='f'
def func2():
    name2='白自在'
    age=10000
        def func3():
            name3='石中玉'
            age3='19'
            print('全局作用域为:',globals())
            print('局部作用域为:',locals())

func3()
func2()               
结果为:

全局作用域为: {'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x00000297D51CC198>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, '__file__': 'D:/Python/python周末班/day04/globals,locals.py', '__cached__': None, 'name': 'ShiPotian', 'age': '18', 'sex': 'f', 'func2': <function func2 at 0x00000297D4E23E18>}

局部作用域为: {'age3': '19', 'name3': '石中玉'}
示例2

三 global与nonlocal

1 global

(1)引用并改变一个全局变量

count=1
def func1():
    global count
    count= count+1
    count=count+100
    print(count)
func1()

结果为:102
View Code

(2)在局部作用域声明一个全局变量

def func2():
    global name
    name='谢烟客'
    print('局部定义的全局变量:',name)
func2()
print('全局引用局部定义的全局变量:',name)
结果为:

局部定义的全局变量: 谢烟客
全局引用局部定义的全局变量: 谢烟客
View Code

2 nonlocal

(1)不能操作全局变量,从那层引用的变量,就从那层开始全部改变

count = 100
def func3():
count = 1
def inner1():
    nonlocal count
    count = count + 3
    print('开始引用层的变量值:',count)
def inner2():
    pass
    inner1()
    print('第一层函数的变量值:',count
func3()
print("全局作用域的变量值:",count)
结果为:

开始引用层的变量值: 4
第一层函数的变量值: 4
全局作用域的变量值: 100
nonlocal

(2)取值

引用而不是改变,想要改变上层空间的变量,要用到global、nonlocal

3 特别注意

(1)对于可变的数据类型 list dict set 不用global nonlocal

(2)如果默认参数是一个可变的数据类型,那么他在内存中永远是一个

示例

def extendList(val,list=[]):
    list.append(val)
    return list
list1 = extendList(10)
list2 = extendList(123,[])
list3 = extendList('a')
print('list1=%s'%list1)
print('list2=%s'%list2)
print('list3=%s'%list3)
结果为:

list1=[10, 'a']
list2=[123]
list3=[10, 'a']
示例

四 函数名的应用

1 打印函数名

def func1():
    print(111)
print(func1)
结果为:<function func1 at 0x000001B578743E18>

输出结果为函数名在内存中的地址

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

def func1():
    print(111)
def func2():
    print(222)
def func3():
    print(333)
l1=[func1,func2,func3]
for i in l1:
    i()
结果为:

111
222
333
View Code

3 函数名作为函数的参数

def func1():
    print("This is func1")
def func2(x):
    print("This is func2")
func2(func1)
结果为:This is func2

4 函数名可以作为函数的返回值

def func1():
    print("This is func1")
def func2(x):
    print("This is func2")
    return x
ret=func2(func1)  此行相当于 ret=func1
print(ret)
结果为:

This is func2
<function func1 at 0x0000020417DD3E18>
View Code

五 闭包函数

1 闭包的定义

内层函数对外层函数非全局变量的引用,就叫做闭包

2 基本的闭包函数示例

def wrapper():
    name='qiaofeng'
    def inner():
        print(name)
    inner()
wrapper()
结果为:qiaofeng

3 检验是否为闭包

通过函数名.__closure__检验,如果结果为空则不是闭包函数

def wrapper():
    name='qiaofeng'
    def inner():
        print(name)
    inner()
    print('测试__closure__的内存地址为:\n',inner.__closure__)
wrapper()

结果为:
qiaofeng
测试__closure__的内存地址为:
(<cell at 0x00000130C3607468: str object at 0x00000130C369D730>,)
检查是否为闭包函数

4 用处

如果Python解释器遇到了闭包,他有一个机制,就是这个闭包函数不会随着函数的结束而释放

六 装饰器

需求:测试一个函数的执行效率

定义两个待测的函数

def func1():
    time.sleep(0.5)
    print('测试函数func1的执行时间...')
def func2():
    time.sleep(0.3)
    print('测试函数func2的执行时间...')

#version:0

import time
def func1():
    time.sleep(0.5)
    print('测试此函数的执行时间...')
start_time=time.time()
func1()
end_time=time.time()
print("此函数的执行时间为:%s" %(end_time-start_time))
结果为:

测试函数func1的执行时间...
此函数的执行时间为:0.5000154972076416
最初版本

1 需求:要求封装到一个函数中

import time
def timmer():
    def func1():
    time.sleep(0.5)
    print('测试函数func1的执行时间...')
start_time=time.time()
func1()
end_time=time.time()
print("此函数的执行时间为:%s" %(end_time-start_time))
timmer()
执行结果为:

测试函数func1的执行时间...
此函数的执行时间为:0.5008091926574707
version1

2 需求:被测试函数当参数传入,可以测试多个函数的执行效率

def timmer(f):
    start_time=time.time()
    f()
    end_time=time.time()
    print("此函数的执行时间为:%s" %(end_time-start_time))
timmer(func1)
timmer(func2)
结果为:
测试函数func1的执行时间...
此函数的执行时间为:0.5004558563232422
测试函数func2的执行时间...
此函数的执行时间为:0.3000974655151367
version2

3 需求:测试函数执行效率的同时,不要改变原函数的调用方式

#version:3
def timmer(f):
    start_time=time.time()
    f()
    end_time=time.time()
    print("此函数的执行时间为:%s" %(end_time-start_time))

f1=func1
func1=timmer #这步相当于把timmer这个函数名赋值给func1
func1(f1) ##这步相当于timmer(func1)
结果为:

测试函数func1的执行时间...
此函数的执行时间为:0.5001800060272217
View Code

4 最简单的装饰器

虽然version3大体上满足了我的需求,但是增加两行代码,而且多了个参数,感觉不够好,需要继续改,尽量不要添加其它代码,而且做到调用时一模一样

#version:4

def timmer(f):
    start_time=time.time()
    f()
    end_time=time.time()
    print("此函数的执行时间为:%s" %(end_time-start_time))
func1=timmer(func1)
结果为:

测试函数func1的执行时间...
此函数的执行时间为:0.5006768703460693
最简单装饰器

5 语法糖@

版本4每次测试一个函数的执行效率是,都需要加一行func=timmer(func1),麻烦,于是Python提出了一个语法糖 @。

def timmer(f):
    def inner():
        start_time=time.time()
        f()
        end_time=time.time()
        print("此函数的执行时间为:%s" %(end_time-start_time))
    return  inner
@timmer
def func1():
    time.sleep(0.5)
    print('测试函数func1的执行时间...')
func1()
结果为:
测试函数func1的执行时间...
此函数的执行时间为:0.5005886554718018
语法糖@

6 有参装饰器

被装饰的函数肯定是要有参数的,version5不能满足,必须改造成一个有参装饰器

def timmer(f):
    def inner(*args,**kwargs):
        start_time=time.time()
        f(*args,**kwargs)
        end_time=time.time()
        print("此函数的执行时间为:%s" % (end_time - start_time))
    return  inner
@timmer
def func1(a,b,name,sex='male'):
    time.sleep(0.5)
    print(a,b,name,sex)
    print('测试函数func1的执行时间...')

func1(1,2,name='daozhu')
结果为:
1 2 daozhu male
测试函数func1的执行时间...
此函数的执行时间为:0.5006396770477295
有参装饰器

7 带返回值的装饰器

被装饰的函数肯定是要有返回值的,解决这个问题。

def timmer(f):
    def inner(*args,**kwargs):
        start_time=time.time()
        ret=f(*args,**kwargs)
        end_time=time.time()
        print("此函数的执行时间为:%s" % (end_time - start_time))
        return ret
    return inner
@timmer
def func3(a,b,name,sex='male'):
    time.sleep(0.4)
    print(a,b,name,sex)
    print('测试函数func2的执行时间...')
func3(1,2,sex='fmale',name='alex')
结果为:
1 2 alex fmale
测试函数func2的执行时间...
此函数的执行时间为:0.4004359245300293
有返回值的装饰器

8 完整的装饰器示例

def timmer(f):
    def inner(*args,**kwargs):
        start_time = time.time()
        ret = f(*args,**kwargs)
        end_time = time.time()
        print('此函数的执行效率%s' % (end_time - start_time))
        return ret
    return inner

@timmer
def func1():
………
完整装饰器示例

装饰器的本质就是闭包函数

猜你喜欢

转载自www.cnblogs.com/Sunzz/p/9095819.html