Python3学习笔记之函数对象 函数嵌套 名称空间与作用域 闭包函数 装饰器

1.  函数对象

    函数可以被引用

    函数可以当作一个参数传给另外一个函数

    函数当作一个返回值

    函数可以当作一个容器的元素

    利用函数的特性,可以取代多分支的if        

def foo():
    print('foo')

def bar():
    print('bar')

dic={
    'foo':foo,
    'bar':bar,
}
while True:
    choice=input('>>: ').strip()
    if choice in dic:
        dic[choice]() 

2.  函数嵌套

    1.  嵌套调用        

def max(x,y):
    return x if x > y else y

def max4(a,b,c,d):
    res1=max(a,b)
    res2=max(res1,c)
    res3=max(res2,d)
    return res3
print(max4(1,2,3,4))

    2.  嵌套定义

def f1():
    def f2():
        def f3():
            print('from f3')
        f3()
    f2()
f1()

3.  名称空间与作用域

    1.  什么是名称空间?

        存放名字与值绑定关系的地方(内存)

    2.  加载顺序       

python test.py
#1、python解释器先启动,因而首先加载的是:内置名称空间
#2、执行test.py文件,然后以文件为基础,加载全局名称空间
#3、在执行文件的过程中如果调用函数,则临时产生局部名称空间

    3.  名字的查找顺序

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

        注意:在全局无法查看局部的,在局部可以查看全局的

    4.  作用域

#1、作用域即范围
        - 全局范围(内置名称空间与全局名称空间属于该范围):全局存活,全局有效
      - 局部范围(局部名称空间属于该范围):临时存活,局部有效
#2、作用域关系是在函数定义阶段就已经固定的,与函数的调用位置无关,如下
x=1
def f1():
    def f2():
        print(x)
    return f2
x=100
def f3(func):
    x=2
    func()
x=10000  #已经在全局名称空间定义了,局部变量不会起作用
f3(f1())

#3、查看作用域:globals(),locals()

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

4.  闭包函数

    1.  什么是闭包?

        内部函数包含对外部作用域而非全局作用域的引用        

    2.  闭包的意义

        返回的函数对象,不仅仅是一个函数对象,在该函数外还包裹了一层作用域,这使得,该函数无论在何处调用,优先使用自己外层包裹的作用域

    3.  例子一       

def outer():
    def inner():
        x=2
        return x
    return inner
f=outer()
print(f())  

    4.  例子二

from urllib.request import urlopen
def index(url):
    def get():
        return urlopen(url).read()
    return get
baidu = index('http://www.baidu.com')
print(baidu().decode('utf-8'))

    5.  查看闭包的元素

def outer(x,y):
    def inner():
        print(x,y)
    return inner
f=outer(1,2)
print(f.__closure__[0].cell_contents)
print(f.__closure__[1].cell_contents)
print(f.__closure__)    #这是一个元组  

5.  装饰器

    1.  为何要用装饰器

        开放封闭原则:对修改封闭,对扩展开放

    2.  什么是装饰器

        装饰器他人的器具,本身可以是任意可调用对象,被装饰者也可以是任意可调用对象。

        强调装饰器的原则:1 不修改被装饰对象的源代码 2 不修改被装饰对象的调用方式

        装饰器的目标:在遵循1和2的前提下,为被装饰对象添加上新功能

    3.  装饰器的使用

        1.  简单装饰器的使用

import time
def timmer(func):
    def inner():
        start_time=time.time()
        func()
        stop_time=time.time()
        print ("the use time is %s" %(stop_time-start_time))
    return inner

@timmer   #在被装饰对象上面,添加一行  相当于index=timmer(index)  
def index():
    time.sleep(3)
    print("this is the index page.")
index()

        2.  带有参数的装饰器

import time
def timmer(func):
    def inner(*args,**kwargs):
        start_time=time.time()
        func(*args,**kwargs)
        stop_time=time.time()
        print ("the use time is %s" %(stop_time-start_time))
    return inner

@timmer
def home(name):       #home=timmer(home)
    time.sleep(4)
    print("this is the %s page." %name)
home('yangjianbo')                                

        3.  原函数有返回值

import time
def timmer(func):
    def inner(*args,**kwargs):
        start_time=time.time()
        res=func(*args,**kwargs)
        stop_time=time.time()
        print ("the use time is %s" %(stop_time-start_time))
        print(res)
    return inner

@timmer
def home(name):       #home=timmer(home)
    time.sleep(4)
    print("this is the %s page." %name)
    return 123
res=home('yangjianbo')

        4.  原函数有注释信息

import time
from functools import wraps
def timmer(func):
    @wraps(func)  #加在最内层函数正上方
    def inner(*args,**kwargs):
        # inner.__doc__=func.__doc__    #如果有@wraps,这一句就可以注释掉了。
        start_time=time.time()
        res=func(*args,**kwargs)
        stop_time=time.time()
        print ("the use time is %s" %(stop_time-start_time))
        print(res)

    return inner

@timmer
def home(name):       #home=timmer(home)
    '''
    这是测试代码
    :param name:
    :return:
    '''
    time.sleep(4)
    print("this is the %s page." %name)
    return 123
res=home('yangjianbo')
print(home.__doc__)

        5.  无参数验证          

import time
login_info={'username':None,'login_status':False}
def auth(func):
    def inner(*args,**kwargs):
        if login_info['username'] != None and login_info['login_status'] is True:
            print ("login sucessful")
            res=func(*args,**kwargs)
            print (res)
        else:
            user=input("user:").strip()
            password=input("password:").strip()
            if user == 'yangjianbo' and password == '123':
                print ("login sucessful")
                login_info['username'] =user
                login_info['login_status'] =True
                res=func(*args,**kwargs)
                print (res)
    return inner

@auth
def index():
    time.sleep(3)
    print("this is the index page.")
    return 'zuiniubi'
index()

@auth
def home(name):
    time.sleep(4)
    print("this is the %s page." %name)
    return 'niubi'
home('yangjianbo')

        6.  有参装饰器(使用三层装饰器)

import time
login_info={'username':None,'login_status':False}

def auth(engine='file'):
#    engine='file'
    def wrapper(func):
        def inner(*args,**kwargs):
            if login_info['username'] != None and login_info['login_status'] is True:
                print ("login sucessful")
                res=func(*args,**kwargs)
                print (res)
            else:
                user=input("user:").strip()
                password=input("password:").strip()
                if engine=='file':
                    if user == 'yangjianbo' and password == '123':
                        print ("login sucessful")
                        login_info['username'] =user
                        login_info['login_status'] =True
                        res=func(*args,**kwargs)
                        print (res)
                elif engine=='mysql':
                    print ("mysql auth")
                elif engine=='ldap':
                    print ('ldap auth')
        return inner
    return wrapper
@auth(engine='ldap')
def index():
    time.sleep(3)
    print("this is the index page.")
    return 'zuiniubi'
index()

@auth(engine='file')
def home(name):
    time.sleep(4)
    print("this is the %s page." %name)
    return 'niubi'
home('yangjianbo')

        7.  叠加多个装饰器

def outter1(func1): #func1=wrapper2的内存地址
    print('加载了outter1')
    def wrapper1(*args,**kwargs):
        print('执行了wrapper1')
        res1=func1(*args,**kwargs)
        return res1
    return wrapper1

def outter2(func2): #func2=wrapper3的内存地址
    print('加载了outter2')
    def wrapper2(*args,**kwargs):
        print('执行了wrapper2')
        res2=func2(*args,**kwargs)
        return res2
    return wrapper2

def outter3(func3): # func3=最原始的那个index的内存地址
    print('加载了outter3')
    def wrapper3(*args,**kwargs):
        print('执行了wrapper3')
        res3=func3(*args,**kwargs)
        return res3
    return wrapper3



@outter1 # outter1(wrapper2的内存地址)======>index=wrapper1的内存地址
@outter2 # outter2(wrapper3的内存地址)======>wrapper2的内存地址
@outter3 # outter3(最原始的那个index的内存地址)===>wrapper3的内存地址
def index():
    print('from index')

print('======================================================')
index()            

6.  关键字

    1.  global

        在局部作用域修改全局作用域的变量值。

        例子:      

x=10
def name():
    x=100
name()
print(x)
#  x=100在局部作用域内有效,虽然执行了函数,但是在全局作用域的x的值,依然是x=10,要想让x=100,必须在函数内部添加一个关键字global

x=10
def name():
    global x
    x=100
name()
print(x)

7.  装饰器练习

         一,编写函数,(函数的执行时间是随机的)

import time
def a():
    x = time.strftime('%Y-%m-%d %X')
    print(x)
a()

结果:

D:\Python36\python.exe F:/python/第一课/while-lianxi.py
2017-10-14 08:40:11

          二,编写装饰器,为函数加上统计时间的功能。


#这种是采用传参的方式,虽然没有修改index和home函数的源代码,但是修改了它们的调用方式。所以需要采用装饰器。
import time
def timmer(tongji_hanshu):
# tongji_hanshu=home
def inner():
start_time=time.time()
tongji_hanshu()
stop_time=time.time()
print("函数执行时间: %s" %(stop_time-start_time))
return inner
@timmer #语法糖
def index():
time.sleep(3)
print("welcome to index page!")
index()
@timmer
def home():
time.sleep(5)
print("welcome to home page!")
home()

结果:

D:\Python36\python.exe F:/python/第一课/while-lianxi.py
welcome to index page!
函数执行时间: 3.0001718997955322
welcome to home page!
函数执行时间: 5.000285863876343

       三. 编写装饰器,为函数加上认证的功能。

import time
def outter(func):
    def auth():
        username=input("输入用户名: ").strip()
        password=input("输入密码: ").strip()
        if username=='yangjianbo' and password=='123':
            print ("登录成功!")
            func()  #这么写死了
        else:
            print("登录失败!")
    return auth
@outter
def index():
    time.sleep(3)
    print("welcome to index page!")
index()

结果:

D:\Python36\python.exe F:/python/第一课/while-lianxi.py
输入用户名: yangjianbo
输入密码: 123
登录成功!
welcome to index page!

 

四:编写装饰器,为多个函数加上认证功能(用户的账号密码来源于文件),要求登录成功一次,后续的函数都无需再输入用户名和密码。

#这种情况是针对index,没有参数的函数,如果被装饰的函数有参数,如何修订装饰器。
import time
def outter(func):
    def auth(*args,**kwargs):
        username=input("输入用户名: ").strip()
        password=input("输入密码: ").strip()
        if username=='yangjianbo' and password=='123':
            print ("登录成功!")
            func(*args,**kwargs)  #这么写死了
        else:
            print("登录失败!")
    return auth
@outter
def index():
    time.sleep(3)
    print("welcome to index page!")
index()
@outter  #home=outter(home)
def home(username):
    time.sleep(2)
    print("welcome %s to home page!" %username)
home('yangjianbo')

结果:

D:\Python36\python.exe F:/python/第一课/while-lianxi.py
输入用户名: yangjianbo
输入密码: 123
登录成功!
welcome to index page!
输入用户名: yangjianbo
输入密码: 123
登录成功!
welcome yangjianbo to home page!

 根据题目要求:只要登录成功,以后就不需要再登录。所以上面的代码还需要修改。

import os
import time
login_status=False
def outter(func):
    def auth(*args,**kwargs):
        if  login_status is True:
            return func(*args,**kwargs)
        else:
            username=input("输入用户名: ").strip()
            password=input("输入密码: ").strip()
            with open('account.txt','r',encoding='utf-8') as accout:
                    accout=accout.read().strip('\r\n').split('|')
                    if username==accout[0] and password==accout[1]:
                        print ("登录成功!")
                        login_status=True
                        func(*args,**kwargs)
                    else:
                        print("登录失败!")
    return auth
@outter  #index=outter(index)
def index():
    time.sleep(3)
    print("welcome to index page!")
index()

@outter
def home(username):
    time.sleep(2)
    print("welcome %s to home page!" %username)
home('yangjianbo')

结果:

D:\Python36\python.exe F:/python/第一课/while-lianxi.py
Traceback (most recent call last):
File "F:/python/第一课/while-lianxi.py", line 158, in <module>
index()
File "F:/python/第一课/while-lianxi.py", line 140, in auth
if login_status is True:
UnboundLocalError: local variable 'login_status' referenced before assignment

上面报错的分析:说局部变量login_status在引用之前没有指定.

我明明定义在全局作用域了,为什么报错?

解决方法:在函数内,添加一个关键字global,表示引用全局变量。

修改后的代码:

import os
import time
login_status=False
def outter(func):
    def auth(*args,**kwargs):
        global login_status
        if  login_status is True:
            return func(*args,**kwargs)
        else:
            username=input("输入用户名: ").strip()
            password=input("输入密码: ").strip()
            with open('account.txt','r',encoding='utf-8') as accout:
                    accout=accout.read().strip('\r\n').split('|')
                    if username==accout[0] and password==accout[1]:
                        print ("登录成功!")
                        login_status=True
                        func(*args,**kwargs)
                    else:
                        print("登录失败!")
    return auth
@outter  #index=outter(index)
def index():
    time.sleep(3)
    print("welcome to index page!")
index()

@outter
def home(username):
    time.sleep(2)
    print("welcome %s to home page!" %username)
home('yangjianbo')

结果:没有报错了。

 第四个装饰器最后版本:

import time
user_status={"user":None,"login_status":False}
def outter(func):
    def auth(*args,**kwargs):
        if user_status['user'] and user_status['login_status']:
            res=func(*args, **kwargs)
            return res
        username=input("输入用户名: ").strip()
        password=input("输入密码: ").strip()
        if username=='yangjianbo' and password=='123':
            print ("登录成功!")
            user_status['user']=username
            user_status['login_status']=True
            res=func(*args, **kwargs)
            return res
        else:
            print("登录失败!")
    return auth
@outter
def index():
    time.sleep(3)
    print("welcome to index page!")
    # return 123
index()
@outter
def home(username):
    time.sleep(2)
    print("welcome to %s home page" % username)
    # return 456

home('yangjianbo')

 练习五  编写装饰器,为多个函数加上认证功能,要求登录成功一次,在超时时间内无需重复登录,超过了超时时间,则必须重新登录

import time
import random
user={"user":None,"login_time":None,'timeout':0.03}

def timmer(func):
    def inner(*args,**kwargs):
        start_time=time.time()
        res=func(*args,**kwargs)
        stop_time=time.time()
        print('%s' %(stop_time-start_time))
        return res
    return inner
def outter(func):
    def auth(*args,**kwargs):
        if user['user']:
            timeout=time.time()-user['login_time']
            if timeout<user['timeout']:
                res=func(*args, **kwargs)
                return res
        username=input("输入用户名: ").strip()
        password=input("输入密码: ").strip()
        if username=='yangjianbo' and password=='123':
            user['user']=username
            user['login_time']=time.time()
            res=func(*args, **kwargs)
            return res
        else:
            print("登录失败!")
    return auth
@outter
def index():
    time.sleep(random.randrange(3))
    print("welcome to index page!")

index()


@outter
def home(username):
    time.sleep(random.randrange(3))
    print("welcome to %s home page" % username)


home('yangjianbo')

  练习六:编写下载网页内容的函数,要求功能是:用户传入一个url,函数返回下载页面的结果

from urllib.request import urlopen

def outer(url):
    def get():
        f=urlopen(url).read()
        return f
    return get
baidu=outer('http://www.baidu.com')
sohu=outer('http://www.sohu.com')
baidu=baidu()
sohu=sohu()

  练习七:拿到下载的页面,保存到一个文件中。

from urllib.request import urlopen

def outer(url):
    def get():
        f=urlopen(url).read()
        return f
    return get
baidu=outer('http://www.baidu.com')
sohu=outer('http://www.sohu.com')
baidu=baidu()
sohu=sohu()
with open('baidu.html','w',encoding='utf-8') as f:
    f.write(baidu.decode('utf-8'))

  

posted @ 2023-01-17 14:45  奋斗史  阅读(15)  评论(0)    收藏  举报