【Python基础】装饰器的解释和用法

装饰器的用法比较简单,但是理解装饰器的原理还是比较复杂的,考虑到接下来的爬虫框架中很多用到装饰器的地方,我们先来讲解一下。

函数

我们定义了一个函数,没有什么具体操作,只是返回一个固定值
请注意一下缩进

def sample():
    return 1
print(sample())

作用域

函数内部的变量和函数外的变量是不同的
我们看一下下面的例子,locals()和globals()方法会得到局部变量和全局变量
我们可以在函数中调用全局变量,但是无法在函数中改变全局变量的值

global_string = 'This is a global string!'
def sample():
    local_string='This is a local string!'
    print(locals())
print(globals())
sample()

内嵌函数

下面的inner函数执行时,由于x不是局部变量,函数会自动扩大搜索范围,找到outer函数中的x。
outer中的inner也是类似与一个变量,不过这个变量是在outer中定义的,所以会直接搜索到。

def outer():
    x=1
    def inner():
        print(x)
    inner()
outer()

函数也是对象

下面的add和sub是标准的函数,接受两个值,返回一个计算后的值。
在apply函数中,我们将一个函数名作为参数传递给了一个新的参数,函数名就是变量标签。

def add(x,y):
    return x+y
def sub(x,y):
    return x-y
def apply(func,x,y):
    return func(x,y)

print(apply(add,2,1))
print(apply(sub,2,1))

函数作为返回值

outer()返回的是一个内部函数变量标签
outer()()才会执行内部函数,不过这样不太合适

def outer():
    print('-'*10)
    def inner():
        print('Inside inner')
    return inner
h=outer()#这句话不会输出内容
h()#这句话才会输出

闭包

内部函数会记住外层作用域

def outer(x):
    def inner():
        print(x)
    return inner
outer(1)()
outer(2)()

装饰器

装饰器其实就是将一个以函数作为参数并返回一个替换函数的可执行函数。
内部函数返回了一个函数的值+1,外部函数返回了内部函数名
外部函数以一个函数名作为变量传入内部函数

def outer(some_func):
    def inner():
        print('Before some_func!')
        return some_func()+1
    return inner
def sample():
    return 1
decorated=outer(sample)#这只是内部函数名
print(decorated())

举个例子

假如有一个表示坐标的类,我们设置了两个函数对坐标进行加减计算

class Coordinate():
    def __init__(self,x,y):
        self.x=x
        self.y=y
    def __repr__(self):
        return 'Coord: '+str(self.__dict__)
def add(a,b):
    return Coordinate(a.x+b.x,a.y+b.y)
def sub(a,b):
    return Coordinate(a.x - b.x, a.y - b.y)

但是上面两个方法没有边界检测的功能,返回的坐标值不一定在第一象限。
如果我们不想改变上面函数和传入坐标的基础上,来实现检测的功能,可以考虑如下做法:

def wrapper(func):
    def checker(a,b):
        if a.x<0 or a.y<0:
            a=Coordinate(a.x if a.x>0 else 0,a.y if a.y>0 else 0)
        if b.x<0 or b.y<0:
            b = Coordinate(b.x if b.x > 0 else 0, b.y if b.y > 0 else 0)
        ret=func(a,b)
        if ret.x<0 or ret.y<0:
            ret=Coordinate(ret.x if ret.x > 0 else 0, ret.y if ret.y > 0 else 0)
        return ret
    return checker
add=wrapper(add)
sub=wrapper(sub)
one = Coordinate(100, 200)
two = Coordinate(300, 200)
print(sub(one,two))
print(add(one,two))

更简单的用法@

我们可以在定义add和sub函数的时候,就使用@符号将wrapper方法作用到这个函数上

@wrapper
def add(a,b):
    return Coordinate(a.x + b.x, a.y + b.y)
#上面的代码就相当于 add=wrapper(add)

我们再来看一个带参数的装饰器

def deco(arg=True):
    if arg:
        def _deco(func):
            def wrapper(*args,**kwargs):
                startTime=time.time()
                func(*args,**kwargs)
                endTime=time.time()
                msecs=(endTime-startTime)*1000
                print('-> elapsed time: %f ms'%msecs)
            return wrapper
    else:
        def _deco(func):
            return func
    return _deco

@deco(False)
def myFunc():
    print('start myFunc')
    time.sleep(0.6)
    print('end myFunc')

@deco(True)
def addFunc(a,b):
    print('start myFunc')
    time.sleep(0.6)
    print('result is %d' %(a+b))
    print('end myFunc')

print('myFunc is '+myFunc.__name__)
myFunc()
print()
print('addFunc is '+addFunc.__name__)
addFunc(3,8)

内置装饰器

Python中有三个内置的装饰器,都是与class相关的,包括:
staticmethod:类静态方法,没有self参数,并可以在类不实例化的情况下调用
classmethod:接收的第一个参数不是self,而是cls(当前类的具体类型)
property:属性的意思,表示可以通过类示例直接访问。

#我们来看一下property的用法
class Foo():
    def __init__(self,var):
        self._var=var
    @property
    def var(self):
        return self._var
    @var.setter
    def var(self,var):
        self._var=var
foo=Foo('var 1')
print(foo.var)
foo.var='var 2'
print(foo.var)
posted @ 2017-12-10 15:18  xingzhui  阅读(541)  评论(0编辑  收藏  举报