点击上方“中兴开发者社区”,关注我们
每天读一篇一线开发者原创好文
作者简介
作者林鹏,致力于openstack云平台的NFV功能融合,对于云平台插件开发有一定的经验。
作者
最初学习python就是为了将项目组的安全网元融入进虚拟化平台中去,所以是带着目的来学习的这门语言。相信有很多人也和我一样,在了解了基础语法之后就投入到工作当中了。随着对python的深入使用与了解,最初的基础知识可能无法完全满足工作要求,此时我们也会发现python的一些新的特性和技巧。当你在学习完python的基础语法,能够上手使用这门语言之后,建议你看一看这一系列的文章,也许会有新的收获。
在掌握了python的基础语法之后,我们就可以很快的上手进行python相关的开发,比如编写一个简单的自动化脚本来释放繁复单调的工作,比如利用已有的框架完成一个简单的博客网站,甚至于你可以参与到项目的开发中来,应对像openstack这类的大型云平台的插件开发。此时你会发现python语言的强大与便捷,逐步地适应python的语法习惯,然后会爱上这门语言。
当你从python的初学者逐步变换到使用者时,你就会发现python许多有趣新奇的知识点,这些点你可能在某个代码段中见过但是并未曾了解他的原理与目的,也可能你一直都在使用但是你并未意识到他们是怎样的设计思路与实现方式。本系列文章从一个python初学者的角度出发,带大家了解一下python进阶部分我们需要掌握的那些知识点。
本篇是系列的第一篇,我带大家了解一下,python中的装饰器。
1.万物皆对象
在了解装饰器之前,我们还需要先知道几个基本的知识点。python中,我们能够看到的一切都是以对象的方式存在的,这可能和其他的编程语言不同。我们知道,只要是对象,就可以赋值给一个变量,因此在python中,我们可以将任意的元素赋值给变量,不管你是数字、字符串、元组、列表、字典等所有内置数据类型,还是函数 、方法 、 类 、模块等自定义的内容。
def foo(word="hello"):
return "foo say:" + word + "!"
a = foo() #调用函数foo,将返回值赋值给a
b = foo #foo作为一个对象,赋值给变量b
print a
#输出 foo say:hello!
print b
#输出
print b("i love python")
#输出 foo say:i love python!
print id(foo)
#输出 54847976
print type(foo)
#输出
print foo
#输出
从代码中可以看出,我们将函数foo作为对象赋值给变量b,此时变量b也可以作为一个新的函数来调用。
在python中,所有的对象都包含三个最基本的元素:标识(id)、类型(type)、值(value,即对象本身),我们可以通过打印这三个要素来分析某一个对象的具体内容。
我们知道对于一个函数来讲,他的入参和出参必须是一个对象,既然python中一切皆为对象,那我们就可以用另一个函数来作为某个函数的参数,也可以为一个函数返回另一个函数。可能听起来有点晕,没关系,我们来看下面这个例子。
def foo(fun):
if type(fun) == type(foo):
print "type of fun is a function"
return fun
else:
print "type of fun is not a function"
return type(fun)
def fuu():
return 100
a = foo(fuu)
#输出 type of fun is a function
print a
#输出
print type(a)
#输出
print a()
#输出 100
简单起见,我将fuu这个函数作为变量传递给foo()调用,foo再将fuu作为返回值返回给变量a。此时变量a可以当做函数来直接调用。我们先暂时记住python中这种将函数作为对象的相关操作,后面我们还会看到。
2.函数闭包
在计算机科学中,闭包(Closure)是词法闭包(Lexical Closure)的简称,是指引用了自由变量的函数。这个被引用的自由变量将和这个函数一同存在,即使已经离开了创造它的环境也不例外。python是支持函数闭包的语言之一,我们结合下面这个例子来了解到底什么是闭包函数。
def addNum(x):
#python可以在函数内部再定义另一个函数,理解了万物皆对象的概念之后也就不难理解了
def theNum(y):
return x+y #此时变量x即使离开了创造他的环境(函数addNum)也任然存在
return theNum
Alice = addNum(18)
print Alice(30)
#输出48
del addNum #删除原函数addNum
print Alice(40)
#输出58
这就是一个函数闭包的例子,我们把函数addNum称作闭包函数。
3.装饰器
现在我们考虑这样一个场景,你的某个项目中用到了日志打印模块,这个模块包括了头信息(时间戳、代码位置、日志级别等)和尾部信息(签名、处理时长等),而你会多处调用这个日志打印函数,每次调用都会在头信息和尾部信息之间执行本体调用函数的操作。刚刚我们有了解了函数出入参和闭包概念,那我们可以这么来设计这个模块。
def logger(func):
def wrapper(x):
print "before func called, do something"
func(x)
print "after func called, do something else"
return wrapper
def fun1(arg):
print "in fun1, we do fun1", arg
def fun2(arg):
print "in fun2, nothing to do!", arg
f1 = logger(fun1) #将fun1作为变量传给logger,最终赋值给f1,此时f1是函数
f2 = logger(fun2) #将fun2作为变量传给logger,最终赋值给f2,此时f2是函数
a = f1("f1")
#before func called, do something
#in fun1, we do fun1 f1
#after func called, do something else
b = f2(100)
#before func called, do something
#in fun2, nothing to do!100
#after func called, do something else
我们利用logger这个函数,将所需要用到日志模块的其他函数再次封装成一个新的函数,然后再调用,这样一来日志模块就独立出来,其他调用模块也就无需处处维护头信息和尾部信息了。其中 f1 = logger(fun1)这句赋值语句,就是python语法中的装饰器。说到这里,大家可能会有疑问,为何这里讲到的装饰器和我们平时的不一样呢?别急,我们接着往下看。
def decorator(func):
def wrapper(x):
print "before func called, do something"
func(x)
print "after func called, do something else"
return wrapper
def foo(*arg):
print "do something in function foo"
foo = decorator(foo) #新声明的函数名和原来一致
a = foo()
#before func called, do something
#do something in function foo
#after func called, do something else
这个例子中将装饰过的新函数命名和原来的一致,对调用者来讲看不到区别,此时就是一个完全的装饰器实现。由于这种写法不够优雅,python专门定义了新的语法规则来表明一个装饰过得函数,即使用@字符来提示一个装饰过的类。
def decorator(func):
def wrapper(x):
print "before func called, do something"
func(x)
print "after func called, do something else"
return wrapper
@decorator #等价于函数定义完成后执行foo = decorator(foo)
def foo(*arg):
print "do something in function foo"
这就是我们在python见到过的装饰器,他仅仅是一个语法糖,等价于函数定义完成后利用装饰器类的再次定义函数。
4.python内置装饰器
通常来讲,我们定义自己需要的装饰器来满足不同的需求。但是python本身也内置了三个装饰器,分别是@staticmethod、@classmethod、@property。这三个装饰器应用比较广泛,分别是将函数声明为静态方法、类方法、属性方法。本篇就不再细述这几个装饰器的作用,后继再来讲述。
5.小结
装饰器,本质就是一个函数,用来装饰其他函数,为其他函数添加附加功能。实现装饰器时要求不能修改被装饰函数的源代码,也不能修改被装饰函数的调用方法。有了装饰器,我们可以抽离出大量与函数本身无关的重复代码,减轻开发和维护的成本,也可以在不改变已有函数结构的基础上对已实现的功能做扩展。