博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
007-Python函数-装饰器
阅读量:5209 次
发布时间:2019-06-14

本文共 11964 字,大约阅读时间需要 39 分钟。

函数回顾

1.函数可以当做一个参数赋值给另一个函数:

def func():    print("in the func")    def foo(x):    x()foo(func)

输出:

in the func

2.函数可以设置为变量形式,函数可以被当做数据来传递:

def func():    print("in the func")f1 = funcf1()

输出:

in the func

3.返回值可以是函数

def func():    print("in the func")def foo():    return funcres = foo()     #res = funcres()           #res() = func()

输出:

in the func

4.可以做容器类型的元素(列表,元组,字典)

def func():    print("in the func")def abc():    print("in the abc")func_dic={    "func":func,    "abc":abc,}func_dic["func"]()  # func()   通过字典取值的形式执行函数func_dic["abc"]()   # abc()

输出:

in the funcin the abc

嵌套调用

在一个函数的内部调用另外一个函数;

例:比较四个值的大小:

def my_max4(a, b, c, d):    res1 = my_max2(a, b)    res2 = my_max2(res1, c)    res3 = my_max2(res2, d)    return res3def my_max2(x, y):    if x > y:        return x    else:        return y        print(my_max4(100, 5000, -1, 10))

输出:

5000

嵌套定义

在函数的内部,又定义一个函数:

x = 1def f1():    def f2():        print(x)    return f2f2 = f1()f2()

名称空间于作用域

1.名称空间种类

  1. 内置名称空间:在python解释器启动时产生,存放python内置的名字
  2. 全局名称空间:在执行文件时产生,存放文件级别定义的名字(全局变量),程序结束时释放
  3. 局部名称空间:在执行文件的过程中,如果调用了函数,则会产生该函数的名称空间,用来存放该函数内部定义的名字,该函数在函数调用时生效,韩式调用结束后失效。

2.名称空间,加载顺序

内置-->全局-->局部

3.名称空间,取值加载顺序

局部-->全局-->内置

4.作用域即范围

  1. 全局范围:全局存活,全局有效
  2. 局部范围:临时存活,局部有效
  3. 作用域关系是在函数定义阶段以及固定的,与函数的调用位置无关

1.查看局部作用域的名字 locals()

def func():    print("in the func")    a = 1    b = 2    c = 5    def foo():        pass    print(locals())def foo(x):    x()foo(func)

输出:

{'a': 1, 'c': 5, 'b': 2, 'foo': 
.foo at 0x00000254BFD698C8>}

2.查看全局作用域的名字 globals()

def func():    print("in the func")    a = 1    b = 2    c = 5    def foo():        pass    # print(locals())def foo(x):    x()foo(func)print(globals())

输出:

{'__package__': None, '__spec__': None, '__doc__': None, 'func': 
, '__builtins__':
, 'foo':
, '__name__': '__main__', '__cached__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x00000254BFD07AC8>, '__file__': 'C:/Users/PycharmProjects/Py/da/笔记/作用域.py'}

5.局部内修改全局变量值global (尽量不要在局部中修改全局变量值)

x = 1def f1():    global x    x =10f1()print(x)

输出:

10

6.作用域关系,在函数定义时就已经固定,于调用位置无关

1.在第一次打印x值时取值为定义时固定的 x = 1,第二次时全局的x已经被修改为500所以值为500;

x=1def f1():    def f2():        print(x)    return f2func = f1()def foo(func):    x = 1000    func()foo(f1())x = 500foo(f1())

输出

1500

闭包函数

在内部函数当中,有对外部函数名字的引用;(对外部作用域,不是全局作用域)

1.将x = 1 隐藏在了函数f2中,只有在调用执行时才会被引用;且不受全局变量的影响

x = 1000             # 全局作用域def f1():    x = 1            # 外部作用域 (关键的闭包值)    def f2():        print(x)    return f2f = f1()f()

输出:

1

2.将baidu的网页爬下来,先存储起来使用的时候,直接调用baidu()即可:

import requests##导入模块页面爬取requestdef page(url):    # url = http://www.baidu.com    def get():        print(requests.get(url).text)    return getbaidu = page("http://www.baidu.com")baidu()print(baidu.__closure__[0].cell_contents)   # 查看闭包函数包过的值(http://www.baidu.com)

装饰器实现原则

1.一定不能修改源代码

2.不能修改调用方式
3.装饰器本身也是一个函数
4.遵循开放封闭原则

装饰器的语法是:

在你需要装饰的函数正上方一行使用@符号写上装饰器的名字;
被装饰者"a",装饰器名字为 timer函数

1.在index函数和home函数上面添加一个计算程序运行时间的计算,但是不改变index的调用方式

import timedef times(func):                 # 运行times(func)函数    def f1():        start = time.time()        func()        stop = time.time()        print(stop-start)    return f1                    # 将times得到的值 f1() 返回给 index@timesdef home():    time.sleep(2)    print("from in home!")@times                            # index = times(index) 运行times()函数,并把 index()函数值传入进去def index():    time.sleep(3)    print("from in index")home()index()

输出:

from in home!2.000453233718872from in index3.0009102821350098

2.有参装饰器当index函数有调用方式的时候,@timer函数需要使用不定长参数来表示(*args, **kwargs);

def timer(index):    def abc(*args, **kwargs):        print("in the abc")        index(*args, **kwargs)    return abc@timer      # index = timer(index)def index(user, age):    print("in the index %s %s" % (user, age))index("tom", age="19")

3.返回值装饰器当home函数有返回值时,@times函数内部同时需要返回home的返回值

import timedef times(func):    def f1(*args,**kwargs):                # 因为会将值传到 f1函数来接收所以使用 *args,**kwargs 接收        start = time.time()        res = func(*args,**kwargs)         # 并将接收到的参数,全部转发给 func() 函数        stop = time.time()        print(stop-start)        return res                         # 将的到的返回值返回给 home函数    return f1@timesdef home(name):                            # home()函数有返回值    time.sleep(2)    print("from %s in home!" % name)@timesdef index():    time.sleep(3)    print("from in index")home("lin")                                # 在这里在执行home("lin") 函数是,就是在执行 f1(*args,**kwargs)index()

输出:

from lin in home!2.0006303787231445from in index3.0005226135253906

4.让用户输入用户密码,如果用户密码正确,方可执行调用函数:

user_l = {"user":None}                                    # 定义一个空字典,用来存放已登录的用户,如果有值,无需在次登录def auth(func):    def f1(*args, **kwargs):        if user_l["user"]:                                # 判断user_l字典中是否有值            res = func(*args,**kwargs)            return res        name = input("输入你的名字:")        pwd = input("输入你的密码:")        with open("db.txt","r",encoding="utf-8") as f:    # 从文件db.txt 读取用户信息            date_dic = eval(f.read())                     # 将用户信息转为字典格式        if name in date_dic and pwd == date_dic[name]:    # 判断用户输入内容和字典取值是否相同            res = func(*args,**kwargs)            user_l["user"]=name            return res        else:            print("用户或密码ERROR")    return f1@authdef home(name):    print("from %s in home" % name)@authdef index():    print("from in index!")index()home("lin")

db.txt内容

{"zhangsan":"123","wangwu":"456","zhaoliu":"789"}

输出:

输入你的名字:zhangsan输入你的密码:123from in index!from lin in home

有参装饰器

1.当用户输入指定登录时,使用什么方式验证;不同的验证参数使用不同的验证方法;

判断用户如果选择“file”方式登录,需要让用户输入用户密码,如果选择“ldap”方式登录直接登录!

user_l = {"user":None}def canshu(auth_type="file"):                    # 在最外层包过一个用户传来的验证方式,默认为“file”    def auth(func):        # func = index        def f1(*args,**kwargs):            if auth_type == "file":              # 获取用户传入的auth_type 参数 判断是否为 file                if user_l["user"]:                    res = func(*args,**kwargs)                    return res                name = input("输入你的名字:")                pwd = input("输入你的秘密:")                with open("db.txt","r",encoding="utf-8") as f:                    data_dic = eval(f.read())                if name in data_dic and pwd == data_dic[name]:                    res = func(*args,**kwargs)                    user_l["user"] = name                    return res                else:                    print("用户名或密码ERROR!")            elif auth_type == "mysql":                print("auth mysql!")            elif auth_type == "ldap":                print("auth ldap")            else:                print("auth ERROR")        return f1    return auth@canshu(auth_type="file")      # canshu(file) --->@auth -- > f1()def home(name):    print("from %s in home" % name)home("lin")                    # 最终执行 f1()

db.txt内容

{"zhangsan":"123","wangwu":"456","zhaoliu":"789"}

输出:

输入你的名字:zhangsan输入你的秘密:123from lin in homepython

使用装饰器时,显示源函数调用方式的帮助信息

import timeimport functools                # 用于使用装饰器时,展示源函数的 帮助信息def times(func):    @functools.wraps(func)      # 在最装饰器最内层的函数 上面加上 @functools.wraps(func) 并把 func 传进来    def f1(*args,**kwargs):        start = time.time()        func(*args,**kwargs)        stop = time.time()        print(stop-start)    return f1@timesdef home(name):    '''    这是home的帮助信息!    :return:    '''    time.sleep(3)    print("from %s in home" % name)print(home.__doc__)         # 打印函数的注释信息home("lin")

输出:

这是home的帮助信息!    :return:    from lin in home3.0001637935638428

装饰器上面在套用装饰器,达到多次装饰的目的

1.在计算程序运行时间的装饰器上,在加一个验证装饰器

import timeimport functools                # 用于使用装饰器时,展示源函数的 帮助信息user_l = {"user":None}def times(func):    @functools.wraps(func)      # 在每个最装饰器最内层的函数 上面加上 @functools.wraps(func) 并把 func 传进来    def f1(*args,**kwargs):        start = time.time()        func(*args,**kwargs)        stop = time.time()        print(stop-start)    return f1def canshu(auth_type="file"):    def auth(func):        @functools.wraps(func)    # 在每个最装饰器最内层的函数 上面加上 @functools.wraps(func) 并把 func 传进来        def f1(*args, **kwargs):            if auth_type == "file":                if user_l["user"]:                    res = func(*args,**kwargs)                    return res                name = input("输入你的名字:")                pwd = input("输入你的密码:")                with open("db.txt","r",encoding="utf-8") as f:                    date_dic = eval(f.read())                if name in date_dic and pwd == date_dic[name]:                    res = func(*args,**kwargs)                    user_l["user"]=name                    return res                else:                    print("用户或密码ERROR")            elif auth_type == "mysql":                print("mysql auth")            elif auth_type == "ldap":                print("ldap auth")            else:                print("auth ERROR")        return f1    return auth@canshu()                        # 哪个装饰器写在上面先运行哪个装饰器 先运行验证装饰器@times                           # 后运行计时装饰器def home(name):    '''    这是home的帮助信息!    :return:    '''    time.sleep(3)    print("from %s in home" % name)print(home.__doc__)         # 打印函数的注释信息home("lin")

输出:

这是home的帮助信息!    :return:    输入你的名字:zhangsan输入你的密码:123from lin in home3.000636339187622

装饰器练习(巩固加深)

1.编写日志装饰器,实现功能如:一旦函数index执行,则将消息2017-07-24 22:11:17 index run写入到日志文件中,日志文件路径可以指定(装饰器2017-07-24.log)

import os, timedef logger(logfile):    def deco(func):        if not os.path.exists(logfile):                 # 判断 日志文件 “装饰器2017-07-24.log” 是否存在            with open(logfile, "w"): pass               # 不存在创建 文件        def f1(*args, **kwargs):            res = func(*args, **kwargs)            with open(logfile, "a", encoding="utf-8") as f:                f.write("%s %s run\n" % (time.strftime("%Y-%m-%d %X"), func.__name__))     # 将当前时间 文件名记录到文件中            return res        return f1    return decotime_date = time.strftime('%Y-%m-%d')@logger(logfile="装饰器" + time_date + ".log")             # 传入日志文件名 日期格式 “装饰器2017-07-24.log”def index():    print("index")    index()

2.将爬取的站点,存入依照URL的md5值命名的文件中用于缓存,下次用户在访问时,可以从文件中直接读取数据返回。

import requests, os, hashlibsettings = {                                # 定义字典格式的 缓存 存放格式 选择    "file": {"dirname": "./db",        # db 目录需要提前创建             },    "mysql": {        "host": "127.0.0.1",        "port": 3306,        "user": "root",        "password": "123",    },    "redis": {        "host": "127.0.0.1",        "port": 6379,        "user": "root",        "password": "123",    }}def make_cache(cache_file="file"):                      # 接收用户选择 存储缓存的方式    if cache_file not in settings:                      # 如果选择缓存的方式不再 settings 字典中        raise TypeError("cache_file not valid!")        # raise 自定义一个异常抛给用户    def deco(func):        def f1(url):            if cache_file == "file":                m = hashlib.md5(url.encode("utf-8"))    # 获取一个 URL 转换的 hash对象 "
" cache_filename = m.hexdigest() # 取出这个md5的hash值 "e1d84f4301e444a3db82c908f29947b1" cache_filepath = r"%s/%s" % (settings["file"]["dirname"], cache_filename) # 拼接一个文件路径 ./db/e1d84f4301e444a3db82c908f29947b1 if os.path.exists(cache_filepath) and os.path.getsize(cache_filepath): # 如果md5后的url文件存在并且里面有值 return open(cache_filepath, encoding="utf-8").read() # 直接将结果返回给用户 res = func(url) with open(cache_filepath, "w", encoding="utf-8") as f: # 否则依照 url 的md5值为文件名创建文件 f.write(res) # 将爬取的 页面内容存入 url 的md5值为文件名中 return res # 并返回 爬取的数据 elif settings == "mysql": pass elif settings == "redis": pass else: pass return f1 return deco@make_cache(cache_file="file")def get(url): return requests.get(url).textprint(get("https://www.python.org"))print(get("https://www.jd.com"))

转载于:https://www.cnblogs.com/baolin2200/p/6406349.html

你可能感兴趣的文章
版本生成|Ext form输入框后加文字说明
查看>>
Php+Redis 实现Redis提供的lua脚本功能
查看>>
iOS - UIPageViewController
查看>>
一串数字每三位用逗号分隔的面试题
查看>>
JS全选/取消全选
查看>>
oracle查看经常使用的系统信息
查看>>
Codeforces Round #223 (Div. 2)--A. Sereja and Dima
查看>>
Animatepacker for cocos2d-x 3.0 解析
查看>>
最小二乘法
查看>>
409. Longest Palindrome
查看>>
arcgis api for js 关于layers图层的理解
查看>>
ArcGIS API For JS之空间查询和属性查询
查看>>
在UEFI下安装windows和Ubuntu双系统目前不可行
查看>>
《英语语法新思维初级教程》学习笔记(七)五种基本句型
查看>>
为EF DbContext生成的实体添加注释(T5模板应用)[转]
查看>>
rsa && sha1 js code
查看>>
Spring MVC防止数据重复提交(防止二次提交)
查看>>
spring boot测试工具(自带)
查看>>
JavaScript Output
查看>>
A - Dubstep
查看>>