Python函数部分2——名称空间与作用域,装饰器

一、函数之名称空间

        1.1、什么是名称空间

        名称空间:存放名字的地方

        例子:S=1,1存放于内存中,那名字 S 存放在哪里呢?名称空间正是存放名字x与1绑定关系的地方

 

        1.2、名称空间 (存放名字与值的绑定关系)

        1、内置名称空间
            存放python解释器自带名字,比如内置的函数名:len,max,sum
            创建:随着python解释器启动而创建
            销毁:随着python解释器关闭而销毁

       2、全局名称空间

            存放文件级别的名字,比如x,f1,z
            x=1

            def f1():
            y=2

            if x == 1:
            z=3
            创建:文件开始执行时则立即创建
            销毁:文件开始执行完毕时则销毁

        3、局部名称空间

            存放函数内的名字,强调:函数的参数也属于局部的
            创建:函数执行时才临时创建
            销毁:函数执行完毕则立即销毁

 

        4、名称空间的加载顺序

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

            强调:加载的目的是为了把名字存起来,存起来的目的是为了取出来,那么但凡查找一个名字一定会从三种名称空间之一找到。

 

        5、名称空间的查找名字顺序

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

    例子:名称空间加载顺序与名称空间查找名字顺序使用案例:

len=10
def f1():
    # len=100
    def f2():
        # len=1000
        def f3():
            # len=10000
            print(len)
        f3()
    # len=200
    f2()
len=11111111111    #这里相当于修改了全局定义的'len=10',修改为'len=11111111111'
f1()

len=11111111111

  

  函数的查找关系是在函数定义阶段就已经固定死的,与调用位置无关

# 函数名字的查找关系是在函数定义阶段就已经固定死的,与调用位置无关

x=100
def f1():
    x=10
    print(x)

def f2():
    x=111111
    f1()
f2()

>>:10

 

二、作用域

        全局范围:内置名称空间中的名字,全局名称空间中的名字

            特点:全局有效,全局存活

 

        局部范围:局部名称空间中的名字

            特点:局部有效,临时存活

 

        全局变量:定义在全局作用域的名字

        局部变量:定义在局部作用域的名字

 

        2.1、当全局数据类型是可变类型时,在函数内是可以修改的

l=[]             #全局类型(可变数据类型)
def foo1():
    l.append(2)  #在函数内往全局类型里面追加数据
foo1()
print(l)         
>>:[2]              #追加完毕后,全局类型l的值已经为[2]

        2.2、当全局数据类型是不可变类型时,在函数内不能修改

L=100             #全局类型(不可变类型)
def foo1():
    L=222         #在函数内定义L的值为222
foo1()
print(L)
>>:100           #执行后全局类型L的值并没有被修改

 

        2.3、global与nonlocal

        1、global用法(改全局变量的值):如果全局数据类型是不可变类型时,在函数内想要修改该变量的值的话。(global这种方式最好少用,因为改全局变量的值后,其他代码调用这个全局变量时也会修改)

L=100             #全局类型(不可变类型)
def foo1():
    global L      #使用global则声明:L为全局类型
    L=222         #在函数内定义L的值为222
foo1()
print(L)
>>:222           #执行后全局类型L的值就被修改为222

        2、nonlocal用法(修改当前上一层变量的值,如果没有则报错):nonlocal会从当前外一层开始查找,一直查找到最外层的函数,如果没有找到则报错

def foo1():
    x=10
    def foo2():
        nonlocal x   #使用nonlocal则声明:x是当前函数外层的变量
        x=100
    foo2()
    print(x)    #在此处打印函数foo1内x的值
foo1()
>>:100    

 

三、函数对象

        函数对象:函数可以当做变量去处理  (把函数想想成一个变量) ,那么:

        1、函数可以被赋值

def foo():
    print('from foo')
f = foo

print(foo)
print(f)
foo()
f()

>>:<function foo at 0x000002401456AC80>    #这是执行print(foo) 得到的
>>:<function foo at 0x000002401456AC80>    #这是执行print(f) 得到的
>>:from foo                           #这是执行foo() 得到的
>>:from foo                           #这是执行f() 得到的

 

        2、函数可以当做参数传给一个函数 

def foo():
    print('from foo')
    print(foo)

def bar(func):  # func=foo
    print(func)
    func()

bar(foo)    #执行函数bar时传参数为"foo(foo为函数),那么func接收到的值就为函数'foo'"

>>:<function foo at 0x000002B418DFAD08>    #这是执行print(func) 得到的
>>:from foo                                #这是执行func() 得到的,运行func()就相当于运行了foo()
>>:<function foo at 0x000002B418DFAD08>    #这是执行print(foo)得到的

 

        3、可以当做函数的返回值 

def bar():
    print('from bar')


def foo(func):      # func=bar
    return func     # return bar

f=foo(bar)

此时:f就等于函数bar,它们内存地址都是一样的
print(f)
得到的值:<function bar at 0x000001DE62D23E18>

print(bar)
得到的值:<function bar at 0x000001DE62D23E18>

那么执行f()就相当于执行函数bar()
f()
得到的值:from bar

bar()
得到的值:from bar


        4、可以当做容器类型元素  (这个功能常常用到)

def get():
    print('from get')

def put():
    print('from put')

def ls():
    print('from ls')

def login():
    print('from login')

main_dict={
    '1':[get,'下载'],
    '2':[put,'上传'],
    '3':[ls,'查看'],
    '4':[login,'登录'],
}

def run():
    while True:
        print("----------------------")
        for n in main_dict:
            print(n,main_dict[n][1])
        print('输入 "q" 或者 "exit" 即可退出程序!')
        print("----------------------")
        choose=input('pls input num:> ').strip()
        if len(choose) == 0:continue
        if choose == 'q' or choose == 'exit':break
        if choose in main_dict:
            main_dict[choose][0]()

run()

 

四、闭包函数 == [函数嵌套+名称空间与作用域+函数对象] 三者的综合应用

1、什么是闭包函数?

            1.定义在函数内的函数

            2.该函数体代码包含对该函数外层作用域中名字的引用  ,强调:函数外层指的不是全局作用域(而是函数内部的)。

            满足上述两个条件,那么该内部函数就称之为闭包函数。

闭包指的是一个概念,总结了:函数的作用域关系是在函数定义阶段就已经固定死的,与调用位置无关。

闭包:闭的概念指的是,这个函数一定是来自于函数内的。

           包的概念指的是,这个res函数拿到的不仅仅是函数本身所具有的的数据,而是包裹了x=1这个值,那么也就是说不管以后在哪里调用这个res函数,包裹的x=1这个值始终不变,x的值始终为1。

          9.5.1、闭包函数简单例子理解

def outner():
    x=1
    def inner():
        print(x)
    return inner
res=outner()   #此时的函数res可以看出是一个全局变量,在任何地方都可以被引用--用到了函数对象的概念。[函数outner()就相当于函数inner,那么给inner函数赋值给res的话,函数res==函数inner]
res()          #那么在执行函数res()就相当于执行函数inner()
>>:1

def foo():             #res函数也可以被其他函数调用(函数res已经是全局变量了--用到了函数对象的概念)
    print('from foo')
    res()              
foo()
>>:from foo
>>:1

        9.5.2、闭包函数生产小例子

def sudada(url):                     #函数内传参和在函数()传参是一样的
    # url='https://www.baidu.com'     #函数内传参和在函数()传参是一样的
    def foo():
        import requests
        response=requests.get(url)
        if response.status_code == 200:
            print(response.text)
    return foo
baidu=sudada('https://www.baidu.com')    #变量baidu其实等于函数foo
python=sudada('https://www.python.org')  #变量python其实等于函数foo

baidu()    #执行函数baidu(),就相当于执行函数foo()
python()   #执行函数python(),就相当于执行函数foo()

 

五、装饰器

        装饰器常用(套用)格式:

def outter(func):
    def wrapper():
        #代码1
        #代码2
        #代码n...
    return wrapper

            1 开放封闭原则
                软件一旦上线之后就应该遵循开放封闭原则
                具体是指对修改是封闭的,但对扩展是开放的

            2、什么是装饰器
                装饰就是修饰,器指的就是工具
                装饰器本身:    可以是任意可调用的对象            --->>函数
                被装饰的对象:也可以是任意可调用的对象         --->>函数

            3、装饰器是用来为被装饰对象添加新功能的一种工具
                必须遵循:
                    1、不能修改被装饰对象的源代码

                    2、不能修改被装饰对象的调用方式

        9.6.1、装饰器例子1:

需求:查看一个index函数调用,花费了多长时间
-------------------------------------------方法1----------------------------------------
1、简单实现需求:定义一个outer函数,在outer函数里面调用index函数,在上一些逻辑判断即可实现

import time
def index():
    time.sleep(1)
    print('welcome to index')

def outer(func):

        start_time = time.time()
        func()
        stop_time = time.time()
        print('run time is %s' %(stop_time - start_time))

outer(index)

得到的值如下:
  welcome to index
  run time is 1.000652551651001

-------------------------------------------方法2----------------------------------------

# 实现需求2:定义一个outer函数,在outer函数里面再定义一个wrapper函数,通过wrapper函数来拿到index函数调用花费的时间,\
# 然后return wrapper函数,那么调用outer函数时就会拿到一个返回值,我们可以把这个返回值定义为 "index" 那么使用 "index" 函数时 \
# 就相当于调用outer函数内的wrapper函数,实现的装饰器的作用!

import time                      #导入time模块
def index(): [被装饰对象]          #定义函数index(),在不改变函数index()的情况下,求执行"print('welcome to index')"这段代码的开始时间与结束时间
    time.sleep(1)
    print('welcome to index')

def outter(func): [装饰器]         #func是outter函数的一个参数
    def wrapper():                #wrapper就是用来统计时间差,这么一个功能
        start_time=time.time()
        func()                    #在wrapper函数内调用func()时,先在wrapper函数内找(wrapper函数内没有),然后在outter函数内找
        stop_time=time.time()
        print('run time is %s' %(stop_time - start_time))
    return wrapper                #返回函数wrapper的内存地址

#把outter(index)[内存地址]复制给index[这里index指的是一个变量名,无意义]
index=outter(index)               #函数outter(index)实际上就是函数wrapper的内存地址
                                  #或者说调用wrapper函数,就相当于调用index(原始)函数
index()  #执行函数index(),就相当于执行了函数wrapper()。{同时wrapper()==index()}

得到的值如下:
  welcome to index
  run time is 1.000652551651001

        9.6.2、装饰器例子2:原始函数index里面需要返回值时(return)

import time
def index():
    time.sleep(1)
    print('welcome to index')
    return 123    #这里让函数返回123

def outter(func):
    def wrapper():    #wrapper函数的功能就是:1.测试index函数运行时间,2.拿到Index函数的返回值123
        start_time=time.time()
        res=func()    #把返回值赋值给res
        stop_time=time.time()
        print('run time is %s' %(stop_time - start_time))
        return res    #在返回index函数的返回值
    return wrapper

index=outter(index)
res=index()
print(res)     

>>:welcome to index
>>:run time is 1.0006911754608154
>>:123

 

        9.6.2、装饰器语法糖:在被装饰对象正上方单独一行写@装饰器的名字

def timmer(func):         [装饰器名称]
    def wrapper(*args,**kwargs):....
    return wrapper

@timmer   [装饰器语法糖]
def index():....    [被装饰对象]

@timmer   [装饰器语法糖]
def home(name):....  [被装饰对象]

 

        9.6.3、无参装饰器

import time
def timmer(func):
    def wrapper(*args,**kwargs):    #可以接受任意值[*接受所有溢出的位置参数,**接受所有的关键字参数]
        start_time=time.time()
        res=func(*args,**kwargs)
        stop_time=time.time()
        print('run time is %s' %(stop_time - start_time))
        return res
    return wrapper

@timmer      # index=timmer(index)
def index():        #无参函数
    time.sleep(1)
    print('welcome to index')
    return 1234

@timmer      # home=timmer(home)
def home(name):      #有参函数,参数name
    time.sleep(2) 
    print('welcome %s to home page' %name)

index()           #此处的函数index是被装饰器timmer装饰后的,而不是原始的index函数
>>:welcome to index
>>:run time is 1.000145673751831

home('sudada')   #此处的函数home是被装饰器timmer装饰后的,而不是原始的home函数
>>:welcome egon to home page
>>:run time is 2.000617265701294

 

        9.6.4、有参装饰器

import time
current_user={'login':False}
def auth(engine):
    def outter(func):
        def wrapper(*args,**kwargs):                   # 这是一个闭包函数
            if current_user['login']:
                return func(*args,**kwargs)            # 这调用的是被装饰的函数[index()或者home(name)],并返回执行结果

            user=input('username>>>: ').strip()        #用户登录
            pwd=input('password>>>: ').strip()

            if engine == 'file':                       #判断以哪种方式打开
                if user == 'egon' and pwd == '123':
                    current_user['login']=True
                    return func(*args,**kwargs)        # 这调用的是被装饰的函数[index()或者home(name)],并返回执行结果
            elif engine == 'mysql':
                print('基于mysql数据的认证')
                return func(*args, **kwargs)
            elif engine == 'ldap':
                print('基于ldap的认证方式')
                return func(*args, **kwargs)
        return wrapper                                 # 返回闭包函数的执行结果
    return outter                                      # 返回闭包函数的执行结果

@auth(engine='mysql') # @outter # index=outter(index) #index=wrapper
def index():
    time.sleep(1)
    print('welcome to index')
    return 1234

@auth(engine='ldap') # @outter # home=outter(home) #home=wrapper
def home(name):
    time.sleep(2)
    print('welcome %s to home page' %name)

index()
# username>>>: szq
# password>>>: 123
# 基于mysql数据的认证

home('egon')
# username>>>: egon
# password>>>: 123
# 基于ldap的认证方式

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章