7.python装饰器

作者: 花间派I风月 | 来源:发表于2019-01-08 22:23 被阅读3次

一、装饰器

在python中,作用域分为两种:全局作用域和局部作用域。

  • 全局作用域是定义在文件级别的变量,函数名。而局部作用域,则是定义函数内部。
  • 关于作用域,我们要理解三点:
    1.在全局不能访问到局部定义的变量
    2.在局部能够访问到全局定义的变量,但是不能修改全局定义的变量(当然有方法可以修改)
    1. 全局变量能够被文件任何地方引用,但修改只能在全局进行操作;如果局部没有找到所需的变量,就会往外进行查找,没有找到就会报错
      下面我们来看看下面实例:
x = 1
def funx():
    x = 10
    print(x)  # 打印出10

funx()
print(x) # 打印出1

如果局部没有定义变量x,那么函数内部会从内往外开始查找x,如果没有找到,就会报错

x = 1
def funx():
    print(x)  

funx()
print(x) # 打印出1
x = 1
def funx():
    def func1():
        print(x)  
    func1()

funx()
print(x) # 打印出1

二、高级函数

我们知道,函数名其实就是指向一段内存空间的地址,既然是地址,那么我们可以利用这种特性来。

    1. 函数名可以作为一个值
def delete(ps):
    import os
    filename = ps[-1]
    delelemetns = ps[1]
    with open(filename, encoding='utf-8') as f_read,\
        open('tmp.txt', 'w', encoding='utf-8') as f_write:
        for line in iter(f_read.readline, ''):
            if line != '\n':  # 处理非空行
                if delelemetns in line:
                    line = line.replace(delelemetns,'')
                f_write.write(line)
    os.remove(filename)
    os.rename('tmp.txt',filename)


def add(ps):
    filename = ps[-1]
    addelemetns = ps[1]
    with open(filename, 'a', encoding='utf-8') as fp:
        fp.write("\n", addelemetns)

def modify(ps):
    import os
    filename = ps[-1]
    modify_elemetns = ps[1]
    with open(filename, encoding='utf-8') as f_read, \
            open('tmp.txt', 'w', encoding='utf-8') as f_write:
        for line in iter(f_read.readline, ''):
            if line != '\n':  # 处理非空行
                if modify_elemetns in line:
                    line = line.replace(modify_elemetns, '')
                f_write.write(line)
    os.remove(filename)
    os.rename('tmp.txt', filename)


def search(cmd):
    filename = cmd[-1]
    pattern = cmd[1]
    with open(filename, 'r', encoding="utf-8") as f:
        for line in f:
            if pattern in line:
                print(line, end="")
        else:
            print("没有找到")

dic_func ={'delete': delete, 'add': add, 'modify': modify, 'search': search}

while True:
    inp = input("请输入您要进行的操作:").strip()
    if not inp:
        continue
    cmd_1 = inp.split()
    cmd = cmd_1[0]
    if cmd in dic_func:
        dic_func[cmd](cmd_1)
    else:
        print("Error")

#将函数作为字典值,实现文本数据的增删查改操作
    1. 函数名可以作为返回值
def outer():
    def inner():
        pass
    return inner

s = outer()
print(s)

######输出结果为#######
<function outer.<locals>.inner at 0x000000D22D8AB8C8>
    1. 函数名可以作为一个参数
def index():
    print("index func")

def outer(index):
    s = index
    s()
    
outer(index)

######输出结果#########

index func

三、闭包函数

闭包函数必须满足两个条件:1.函数内部定义的函数 2.包含对外部作用域而非全局作用域的引用。

    1. 实例一:以下仅仅在函数内部定义一个函数,但并非闭包函数
def outer():
    def inner():
        print("inner func excuted")
    inner()  # 调用执行inner()函数
    print("outer func excuted")
outer()  # 调用执行outer函数

####输出结果为##########
inner func excuted
outer func excuted
    1. 以下函数内部定义了一个函数,而且还引用了一个外部变量x,但也还不是闭包函数
x = 1
def outer():

    def inner():
        print("x=%s" %x)  # 引用了一个非inner函数内部的变量
        print("inner func excuted")
    inner()  # 执行inner函数
    print("outer func excuted")

outer()
#####输出结果########
x=1
inner func excuted
outer func excuted
    1. 实例3:上面实例满足闭包函数的条件。现在,你应该清楚,作为一个闭包函数,必须得满足上述的两个条件,缺一不可。
def outer():
    x = 1
    def inner():
        print("x=%s" %x)
        print("inner func excuted")
    inner()
    print("outer func excuted")

outer()

#####输出结果#########
x=1
inner func excuted
outer func excuted
    1. 实例4:一般情况下,我们都会给闭包函数返回一个值.这里先不说为什么.在接下来的内容中,你会看到这个返回值的用途.
def outer():
    x = 1
    def inner():
        print("x=%s" %x)
        print("inner func excuted")
    print("outer func excuted")
    return inner  # 返回内部函数名
    
outer()
    1. 现在我们来抽象的定义一下闭包函数。它是函数和与其相关的引用环境组合而成的实体。在实现深约束时,需要创建一个能显式表示引用环境的东西,并将它与相关的子程序捆绑在一起,这样捆绑起成为闭包。在上面实例中,我们可以发现,闭包函数,它必须包含自己的函数以及一个外部变量才能真正称得上是一个闭包函数。如果没有一个外部变量与其绑定,那么這个函数不能算得上是闭包函数。
      那么怎么知道一个闭包函数有多少个外部引用变量呢?看看下面代码.
def outer():
    x = 1
    y = 2

    def inner():
        print("x= %s" %x)
        print("y= %s" %y)

    print(inner.__closure__)
    return inner

outer()

######输出结果#######
(<cell at 0x000000DF9EA965B8: int object at 0x000000006FC2B440>, <cell at 0x000000DF9EA965E8: int object at 0x000000006FC2B460>)

结果表明,在inner内部,引用了两个外部局部变量。如果引用的是非局部变量,那么这里输出的为None.

闭包函数的特点:1.自带作用域 2.延迟计算

那么闭包函数有什么作用呢?我们清楚的知道,闭包函数在定义时,一定会绑定一个外部环境。這个整体才能算的上是一个闭包函数,那么我们可以利用这个绑定特性,来完成某些特殊的功能。

    1. 根据传入的URL,来下载页面源码
from urllib.request import urlopen

def index(url)
    def get()
        return urlopen(url).read()
    return get

python = index("http://www.python.org") # 返回的是get函数的地址
print(python()) # 执行get函数《并且将返回的结果打印出来
baidu = index("http://www.baidu.com")
print(baidu())

有人可以会说,这个不满足闭包函数的条件啊!我没有引用非全局的外部变量啊。其实并非如此,给,我们之前说过,只要在函数内部的变量都属于函数。那么我在index(url),这个url也属于函数内部,只不过我们省略一步而已,所以上面那个函数也是闭包函数。

四、 装饰器

装饰器:外部函数传入被装饰函数名,内部函数返回装饰函数名。
特点:1.不修改被装饰函数的调用方式 2.不修改被装饰函数的源代码

    1. 无参装饰器
import time, random

def index():
    time.sleep(random.randrange(1, 5))
    print("welcome to index page")

根据装饰器的特点,我们不能对index()进行任何修改,而且调用方式也不能变。这时候,我们就可以使用装饰器来完成如上功能.

  • 装饰器实现计时
import time, random
def outer(func):  # 将index的地址传递给func
    def inner():
        start_time = time.time()
        func()   # fun = index  即func保存了外部index函数的地址
        end_time = time.time()
        print("运行时间为%s"%(end_time - start_time))
    return inner  # 返回inner的地址

def index():
    time.sleep(random.randrange(1, 5))
    print("welcome to index page")

index = outer(index)  # 这里返回的是inner的地址,并重新赋值给index

index()
    1. 有参装饰器
import time, random


def outer(func):  # 将index的地址传递给func
    def inner():
        start_time = time.time()
        func()   # fun = index  即func保存了外部index函数的地址
        end_time = time.time()
        print("运行时间为%s"%(end_time - start_time))
    return inner  # 返回inner的地址
def index():
    time.sleep(random.randrange(1, 5))
    print("welcome to index page")

index = outer(index)  # 这里返回的是inner的地址,并重新赋值给index

index()

如果被装饰的函数有返回值

def timmer(func):
    def wrapper(*args,**kwargs):
        start_time = time.time()
        res=func(*args,**kwargs) #res来接收home函数的返回值
        stop_time=time.time()
        print('run time is %s' %(stop_time-start_time))
        return res  
    return wrapper

def home(name):
    time.sleep(random.randrange(1,3))
    print('welecome to %s HOME page' %name)
    return 123123123123123123123123123123123123123123
  • 这里补充一点,加入我们要执行被装饰后的函数,那么应该是如下调用方式:
    home = timmer(home) # 等式右边返回的是wrapper的内存地址,再将其赋值给home,这里的home不在是原来的的那个函数,而是被装饰以后的函数了。
  • 像home = timmer(home)这样的写法,python给我们提供了一个便捷的方式------语法糖@.
  • 以后我们再要在被装饰的函数之前写上@timmer,它的效果就和home = timmer(home)是一样的。
  • 如果一个函数被多个装饰器装饰,那么执行顺序是怎样的。
import time
import random

def timmer(func):
    def wrapper():
        start_time = time.time()
        func()
        stop_time=time.time()
        print('run time is %s' %(stop_time-start_time))
    return wrapper
def auth(func):
    def deco():
        name=input('name: ')
        password=input('password: ')
        if name == 'egon' and password == '123':
            print('login successful')
            func() #wrapper()
        else:
            print('login err')
    return deco

@auth   # index = auth(timmer(index))                 
@timmer # index = timmer(index)
def index():
 
    time.sleep(3)
    print('welecome to index page')

index()

实验结果表明,多个装饰器装饰一个函数,其执行顺序是从下往上。

    1. 类装饰器
      装饰器不仅可以是函数,还可以是类,相比函数装饰器,类装饰器具有灵活度大、高内聚、封装性等优点。使用类装饰器主要依靠类的call方法,当使用 @ 形式将装饰器附加到函数上时,就会调用此方法。
class Foo(object):
    def __init__(self, func):
        self._func = func

    def __call__(self):
        print('class decorator runing')
        self._func()
        print('class decorator ending')


@Foo  # bar = Foo(bar)
def bar():
    print('bar')


bar()  # Foo(bar)()

# 结果
# class decorator runing
# bar
# class decorator ending
class Foo(object):
    def __init__(self):
        pass

    def __call__(self, func):
        def _call(*args, **kw):
            print('class decorator runing')
            return func(*args, **kw)
        return _call

class Bar(object):
    @Foo()
    def bar(self, test, ids):   # bar = Foo()(bar)
        print('bar')

Bar().bar('aa', 'ids')

相关文章

  • 7.python装饰器

    一、装饰器 在python中,作用域分为两种:全局作用域和局部作用域。 全局作用域是定义在文件级别的变量,函数名。...

  • 装饰器

    """@装饰器- 普通装饰器- 带参数的装饰器- 通用装饰器- 装饰器装饰类- 内置装饰器- 缓存装饰器- 类实现...

  • typescript 五种装饰器

    装饰器类型 装饰器的类型有:类装饰器、访问器装饰器、属性装饰器、方法装饰器、参数装饰器,但是没有函数装饰器(fun...

  • python——装饰器详解

    一、装饰器概念 1、装饰器 装饰器:一种返回值也是一个函数的函数,即装饰器。 2、装饰器目的 装饰器的目的:装饰器...

  • Python装饰器

    Python装饰器 一、函数装饰器 1.无参装饰器 示例:日志记录装饰器 2.带参装饰器 示例: 二、类装饰器 示例:

  • Python中的装饰器

    Python中的装饰器 不带参数的装饰器 带参数的装饰器 类装饰器 functools.wraps 使用装饰器极大...

  • 装饰器

    装饰器 decorator类装饰器 带参数的装饰器 举例(装饰器函数;装饰器类;有参与无参) https://fo...

  • TypeScript装饰器

    前言 装饰器分类 类装饰器 属性装饰器 方法装饰器 参数装饰器需要在tsconfig.json中启用experim...

  • python之装饰器模版

    装饰器的作用:装饰器即可以装饰函数也可以装饰类。装饰器的原理:函数也是对象 1.定义装饰器 2.使用装饰器假设de...

  • 装饰器实验

    装饰器实验 说明 ts内包含了四个装饰器,类装饰器、属性装饰器、函数装饰器、参数装饰器,本文中测试一下其的使用。 ...

网友评论

    本文标题:7.python装饰器

    本文链接:https://www.haomeiwen.com/subject/sflirqtx.html