python基础16_闭包_装饰器

不了解是否其他语言也有类似 python 装饰器这样的东西。

最近才发现ECMAScript6也是有生成器函数的,也有 yield  generator  

装饰器的基础知识是闭包:

# 闭包:嵌套函数,内部函数调用外部函数的变量
def outer():
    a = 1
    def inner():
        print(a)
    # __closure__ 可以用来判断闭包
    # print(inner.__closure__)  # 打印 cell at ... 则表明它是一个闭包
    return inner  # 返回内部函数的内存地址,因为此函数用到了外部函数的变量,所以外部函数的变量也不会因函数的调用结束而消失
inn = outer()
inn()

from urllib.request import urlopen
# ret = urlopen('http://www.xiaohua100.cn/index.html').read()
# print(ret)

# def get_url():
#     url = 'http://www.xiaohua100.cn/index.html'
#     ret = urlopen(url).read()
#     print(ret)
#
# get_url()

# 闭包的简单示例:
# def get_url():
#     url = 'http://www.xiaohua100.cn/index.html'
#     def get():
#         ret = urlopen(url).read()
#         print(ret)
#     return get    # 得到 url 结果
#
# get_func = get_url()   # 将结果赋给变量,保存下来。
# get_func()
#

 

装饰器的作用与原则

import time


def say_hello():
    time.sleep(1)
    print('hello, everyone...')


# 定义一个闭包函数来增加功能
def tmr(f):  # 装修器函数
    def inner():
        start = time.time()
        f()  # 被装饰函数
        end = time.time()
        print(end - start)

    return inner  # 传参数的名字,不加括号


fun = tmr(say_hello)  # 使用闭包函数
fun()


# 装饰器的作用 —— 不想修改函数的调用方式 但是还想在原来的函数前后添加功能
# timmer就是一个装饰器函数,只是对一个函数 有一些装饰作用

# 原则: 开放封闭原则
#   开放 : 对扩展是开放的
#   封闭 : 对修改是封闭的

def deco(f):
    def inner(*args, **kwargs):
        print('装饰器在函数之前')
        ret = f(*args, **kwargs)
        print("装饰器在函数之后")

    return inner


@deco
def say_hi():
    time.sleep(1)
    print('Hi, good morning ...')


say_hi()

 

 

#!/usr/bin/env python
# coding:utf-8

# 复习一下函数作为参数传递。
# def f11():
#     print(12345)
#
#
# def f12(xxx):
#     xxx()
#
#
# f12(f11)

## 装修器:本质就是函数,功能是为其他函数添加附加功能
# 原则:
# 1. 不修改被装饰函数的源代码
# 2. 不修改被修饰函数的调用方式

## 装饰器 = 高阶函数 + 函数嵌套 + 闭包

def outer(func):
    def inner():
        print('log')
        ret = func()
        print('after')
        return ret
    return inner



@outer
def f1():
    print('F1')
    return '砍你哦.'

@outer
def f2():
    print('F2')


@outer
def f100():
    print('F100')

在另一个文件中调用上面定义的函数,看看装饰器是否启作用:

#!/usr/bin/env python
# coding:utf-8

import outer01

outer01.f1()
outer01.f2()
outer01.f100()

装饰器简单原理:

#!/usr/bin/env python
# coding:utf-8

def outer(func):
    def inner():
        print('before')
        func()
        print('after')
    return inner


## 格式 @ + 函数名
## 功能:
#   1. 自动执行outer函数并且将其下面的函数名f1当作参数传递。
#   2. 将outer函数的返回值,重新赋值给f1
@outer
def f1():
    print('F1')

# 一旦函数被装饰器装饰,函数就会被重新赋值成装饰器函数的内层函数
f1()

换一个文件调用函数:

#!/usr/bin/env python
# coding:utf-8


import outer01

res = outer01.f1() # 也拿到了原函数的返回值。

print(res)

 

带有参数的装饰器:

#!/usr/bin/env python
# coding:utf-8

import time


def timmer(func):
    def wrapper(*args, **kwargs):
        start_time = time.clock()
        res = func(*args, **kwargs)
        stop_time = time.clock()
        print('运行时间是:%s' % (stop_time - start_time))
        return res

    return wrapper


@timmer
def test(name, age):
    time.sleep(1)
    print('test执行完毕 %s %s' % (name, age))
    return 'test complete.'


def test2(name, age, gender):
    time.sleep(1.2)
    print('测试2 %s %d %s' % (name, age, gender))


res = test('alex', 22)
print(res)

test2('jerry', 88, 'male')

 

带参数的装饰器:

#!/usr/bin/env python
# coding:utf-8

user_list=[
    {'name':'alex','passwd':'123'},
    {'name':'tom','passwd':'456'},
    {'name':'jerry','passwd':'245'},
    {'name':'kang','passwd':'135'},
]

current_dic = {'username':None,'login':True}


def auth(auth_type='filedb'):
    def auth_func(func):
        def wapper(*args, **kwargs):
            print('认证类型是:',auth_type)
            if auth_type =='filedb':
                if current_dic['username'] and current_dic['login']:
                    res = func(*args, **kwargs)
                    return res
                username=input('请输入用户名:')
                passwd=input('请输入密码:')
                for user_dic in user_list:
                    if username ==user_dic['name'] and passwd == user_dic['passwd']:
                        current_dic['username'] = username
                        current_dic['login'] = True
                        res = func(*args, **kwargs)
                        return res
                else:
                    print('用户名或密码错误.')
            elif auth_type =='ldap':
                print('LDAP方式')
                res = func(*args, **kwargs)
                return res
            else:
                print('不知道什么方式.')
                res = func(*args, **kwargs)
                return res
        return wapper
    return auth_func


@auth()
def login():
    print("欢迎登录。" )


@auth(auth_type='ldap')
def shopping_cart(name):
    print('%s的购物车里有%s %s %s' % (name,'衣服','电池','牛奶'))


@auth(auth_type='dsdsds')
def home(name):
    print("%s 的主页" % name)


login()
shopping_cart('alex')
home('alex')

 

小练习: 模拟网站增加登录状态验证:

#!/usr/bin/env python
# coding:utf-8

user_dic={'username':None,'login':False}

def auth_func(func):
    def wapper(*args, **kwargs):
        if user_dic['username'] and user_dic['login']:
            res = func(*args, **kwargs)
            return res
        username=input('请输入用户名:')
        passwd=input('请输入密码:')
        if username =='user' and passwd == '123':
            user_dic['username'] = username
            user_dic['login'] = True
            res = func(*args, **kwargs)
            return res
        else:
            print('用户名或密码错误.')
    return wapper


@auth_func
def login():
    print("欢迎登录。" )


@auth_func
def shopping_cart(name):
    print('%s的购物车里有%s %s %s' % (name,'衣服','电池','牛奶'))


@auth_func
def home(name):
    print("%s 的主页" % name)


login()
shopping_cart('alex')
home('alex')

 

  functools 模块中的 wraps

from functools import wraps

def wraper(f):
    @wraps(f)         # 此内置装饰器保证被装饰的函数的 __name__ 和 __doc__依然不变
    def inner(*args,**kwargs):
        '''
        装饰器内部函数 inner
        '''
        print('装饰在函数之前')
        ret = f(*args,**kwargs)
        print('装饰在函数之后')
        return ret
    return inner

@wraper
def holiday(day):
    '''
    这是一个放假函数
    '''
    print('公司决定放假{}天'.format(day))
    return 'very happy.'

# 获取函数名 和 函数文档
print(holiday.__name__)     # 装饰器添加之后,结果变成了 inner
print(holiday.__doc__)      # 取函数的注释文档
ret = holiday(7)
print(ret)

 

用装饰器为函数加上调用记录功能,记录每次调用的函数名写入文件

# 用装饰器为函数加上调用记录功能,记录每次调用的函数名写入文件
import time

def recd(func):
    def inner(*args,**kwargs):
        with open('log','a',encoding='utf8') as f:
            f.write(time.strftime('%Y-%m-%d %X',time.localtime()) + '  ')
            f.write(func.__name__+'\n')
        ret = func(*args,**kwargs)
        return ret
    return inner

@recd
def shop_add():
    print('增加了一件商品')

@recd
def shop_del():
    print('删除了某个商品')

# print(time.asctime())
# print(time.strftime('%Y-%m-%d %X',time.localtime()))

shop_add()
shop_del()

 假设有2个装饰器,装饰同一个函数。

那么执行顺序是先执行第一个装饰器的前半部分,再执行第二个装饰器的前半部分,再执行被装饰的函数主体。

主体执行完成后,再执行第二个装饰器的后半部分,最后执行第一个装饰器的后半部分。

#多个装饰器装饰一个函数
def wrapper1(func):
    def inner1():
        print('wrapper1 ,before func')
        ret = func()
        print('wrapper1 ,after func')
        return ret
    return inner1

def wrapper2(func):
    def inner2():
        print('wrapper2 ,before func')
        ret = func()
        print('wrapper2 ,after func')
        return ret
    return inner2

def wrapper3(func):
    def inner3():
        print('wrapper3 ,before func')
        ret = func()
        print('wrapper3 ,after func')
        return ret
    return inner3

@wrapper3
@wrapper2
@wrapper1
def f():
    print('in f')
    return '哈哈哈'

print(f())

 

posted @ 2018-05-12 06:08  枫若雪  阅读(165)  评论(0编辑  收藏  举报