Python函数的介绍以及装饰器入门用法

这篇文章主要介绍“Python函数的介绍以及装饰器入门用法”,在日常操作中,相信很多人在Python函数的介绍以及装饰器入门用法问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”Python函数的介绍以及装饰器入门用法”的疑惑有所帮助!接下来,请跟着小编一起来学习吧!

目前创新互联已为上1000家的企业提供了网站建设、域名、雅安服务器托管、网站托管维护、企业网站设计、丹徒网站维护等服务,公司将坚持客户导向、应用为本的策略,正道将秉承"和谐、参与、激情"的文化,与客户和合作伙伴齐心协力一起成长,共同发展。

Python允许你,作为程序员,使用函数完成一些很酷的事情。在Python编程学习中,函数是一等对象(first-class object),这就意味着你可以像使用字符串,整数,或者任何其他对象一样使用函数。

例如,你可以将函数赋值给变量:

>>> def square(n):
... return n * n;
>>> square(4)
16
>>> alias = square
>>> alias(4)
16

然而,一等函数的真正威力在于你可以把函数传给其他函数,或者从其他函数中返回函数。Python的内置函数map利用了这种能力:给map传个函数以及一个列表,它会依次以列表中每个元素为参数调用你传给它的那个函数,从而生成一个新的列表。如下所示的例子中应用了上面的那个square函数:

>>> number = [1, 2, 3, 4, 5]
>>> map(square, numbers)
[1, 4, 9, 16, 25]

如果一个函数接受其他函数作为参数,以及/或者返回一个函数,那么它就被称为高阶函数 。虽然map函数只是简单地使用了我们传给它的函数,而没有改变这个函数,但我们也可以使用高阶函数去改变其他函数的行为。

例如,假设有这样一个函数,会被调用很多次,以致运行代价非常昂贵:

>>> def fib(n):
... "Recursively (i.e., dreadfully) calculate the nth Fibonacci number."
... return n if n in [0, 1] else fib(n - 2) + fib(n - 1)

我们一般会保存计算过程中每次递归调用的结果,这样,对于函数调用树中经常出现某个n,当需要计算n对应的结果时,就不需要重复计算了。有多种方式可以做到这点。例如,我们可以将这些结果存在一个字典中,当以某个值为参数调用fib函数时,就先到这个字典去查一下其结果是否已经计算出来了。

但这样的话,每次我们想要调用fib函数,都需要重复那段相同的字典检查样板式代码。相反,如果让fib函数自己在内部负责存储其结果,那么在其他代码中调用fib,就非常方便,只要简单地调用它就行了。这样一种技术被称为memoization(注意没有字母r的哦)。

我们可以把这种memoization代码直接放入fib函数,但是Python为我们提供了另外一种更加优雅的选择。因为可以编写修改其他函数的函数,那么我们可以编写一个通用的memoization函数,以一个函数作为参数,并返回这个函数的memoization版本:

def memoize(fn):
 stored_results = {}
 def memoized(*args):
 try:
 # try to get the cached result
 return stored_results[args]
 except KeyError:
 # nothing was cached for those args. let's fix that.
 result = stored_results[args] = fn(*args)
 return result
 return memoized

如上, memoize 函数以另一个函数作为参数,函数体中创建了一个字典对象用来存储函数调用的结果:键为被memoized包装后的函数的参数,值为以键为参数调用函数的返回值。 memoize 函数返回一个新的函数,这个函数会首先检查在 stored_results 字典中是否存在与当前参数对应的条目;如果有,对应的存储值会被返回;否则,就调用经过包装的函数,存储其返回值,并且返回给调用者。memoize返回的这种新函数常被称为"包装器"函数,因为它只是另外一个真正起作用的函数外面的一个薄层。

很好,现在有了一个memoization函数,我们可以把fib函数传给它,从而得到一个经过包装的fib,这个版本的fib函数不需要重复以前那样的繁重工作:

def fib(n):
 return n if n in [0, 1] else fib(n - 2) + fib(n - 1)
fib = memoize(fib)

通过高阶函数memoize,我们获得了memoization带来的好处,并且不需要对fib函数自己做出任何改变,以免夹杂着memoization的代码而模糊了函数的实质工作。但是,你也许注意到上面的代码还算有点别扭,因为我们必须写3遍fib。由于这种模式-传递一个函数给另一个函数,然后将结果返回给与原来那个函数同名的函数变量-在使用包装器函数的代码中极为常见,Python为其提供了一种特殊的语法:装饰器:

@memoize
def fib(n):
 return n if n in [0, 1] else fib(n - 2) + fib(n -1)

这里,我们说memoize函数装饰了fib函数。需要注意的是这仅是一种语法上的简便写法(译注:就是我们常说的"语法糖")。这段代码与前面的代码片段做的是同样的事情:定义一个名为fib的函数,把它传给memoize函数,将返回结果存为名为fib的函数变量。特殊的(看起来有点奇怪的)@语法只是减少了冗余。

你可以将多个装饰器堆叠起来使用,它们会自底向上地逐个起作用。例如,假设我们还有另一个用来帮助调试的高阶函数:

def make_verbose(fn):
 def verbose(*args):
 # will print (e.g.) fib(5)
 print '%s(%s)' % (fb.__name__, ', '.join(repr(arg) for arg in args))
 return fn(*args) # actually call the decorated function
 return verbose

下面的两个代码片段做的是同样的事情:

@memoize
@make_verbose
def fib(n):
 return n if n in [0, 1] else fib(n - 2) + fib(n - 1)
def fib(n):
 return n if n in [0, 1] else fib(n - 2) + fib(n - 1)
fib = memoize(make_verbose(fib))

有趣的是,Python并没有限制你在@符号后只能写一个函数名:你也可以调用一个函数,从而能够高效地传递参数给装饰器。假设我们并不满足于简单的memoization,还想将函数的结果存储到memcached中。如果你已经写了一个 memcached 装饰器函数,那么可以(例如)传递一个服务器地址给它:

@memcached('127.0.0.1:11211')
def fib(n):
 return n if n in [0, 1] else fib(n - 2) + fib(n - 1)

非装饰器语法的写法会如下展开:

fib = memcached('127.0.0.1:11211')(fib)

Python配备有一些作为装饰器使用的非常有用的函数。例如,Python有一个 classmethod 函数,可以创建大致类似于java的静态方法:

class Foo(object):
 SOME_CLASS_CONSTANT = 42
 @classmethod
 def add_to_my_constant(cls, value):
 # Here, `cls` will just be Foo, buf if you called this method on a
 # subclass of Foo, `cls` would be that subclass instead.
 return cls.SOME_CLASS_CONSTANT + value
Foo.add_to_my_constant(10) # => 52
# unlike in Java, you can also call a classmethod on an instance
f = Foo()
f.add_to_my_constant(10) # => 52

旁注:文档字符串

Python函数可以包含更多的信息,而不仅仅是代码:它们也包含有用的帮助信息,比如函数名称,文档字符串:

>>> def fib(n):
... "Recursively (i.e., dreadfully) calculate the nth Fibonacci number."
... return n if n in [0, 1] else fib(n - 2) + fib(n - 1)
...
>>> fib.__name__
'fib'
>>> fib.__doc__
'Recursively (i.e., dreadfully) calculate the nth Fibonacci number.'

Python内置函数help输出的就是这些信息。但是,当函数被包装之后,我们看到就是包装器函数的名称和文档字符串了:

>>> fib = memoized(fib)
>>> fib.__name__
'memoized'
>>> fib.__doc__

那样的信息并没有什么用处。幸运的是,Python包含一个名为 functools.wraps 的助手函数,能够把函数的帮助信息拷贝到其包装器函数:

import functools
def memoize(fn):
 stored_results = {}
 @functools.wraps(fn)
 def memoized(*args):
 # (as before)
 return memoized

使用装饰器帮助你编写装饰器会使很多事情令人非常满意。现在,如果使用更新过的memoize函数重试前面的代码,我们将会看到得到保留的文档:

>>> fib = memoized(fib)
>>> fib.__name__
'fib'
>>> fib.__doc__
'Recursively (i.e., dreadfully) calculate the nth Fibonacci number.'

到此,关于“Python函数的介绍以及装饰器入门用法”的学习就结束了,希望能够解决大家的疑惑。理论与实践的搭配能更好的帮助大家学习,快去试试吧!若想继续学习更多相关知识,请继续关注创新互联网站,小编会继续努力为大家带来更多实用的文章!


当前文章:Python函数的介绍以及装饰器入门用法
标题链接:http://hbruida.cn/article/ggcdho.html