这篇文章主要介绍“Python函数的介绍以及装饰器入门用法”,在日常操作中,相信很多人在Python函数的介绍以及装饰器入门用法问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”Python函数的介绍以及装饰器入门用法”的疑惑有所帮助!接下来,请跟着小编一起来学习吧!
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函数的介绍以及装饰器入门用法”的学习就结束了,希望能够解决大家的疑惑。理论与实践的搭配能更好的帮助大家学习,快去试试吧!若想继续学习更多相关知识,请继续关注亿速云网站,小编会继续努力为大家带来更多实用的文章!
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。