Python的特性

友情提醒:本文最后更新于 2690 天前,文中所描述的信息可能已发生改变,请谨慎使用。

      这篇文章的目的就是想整理一些Python的高级特性,试图为大家揭开Python的神秘面纱。Python的高级特性如果深入写的话可以写成一本书,所以这篇文章也只是浅尝辄止。

1.匿名函数lambda(是指一类无需定义标识符(函数名)的函数或子程序):

使用lambda可以定义简单的单行匿名函数。如:

fib = lambda n: n if n<=2 else fib(n-1) + fib(n-2)
lambda_add = lambda x, y: x + y


2.将函数作为参数传递:

   在Python中可将函数作为参数传递。其原因就在于函数也是对象。实际上,在 Python 中函数是一级对象。你可以像其他对象一样使用而没有什么特别的限制。关于函数就是对象的一个最常见的例子就是 C 中的函数指针;将函数传递到其他的将要使用它的函数。

>>> def greeter():
...     print 'hello world'
 
>>> def repeat(fn, times):
...     for i in xrange(times):
...         fn()
 
>>> repeat(greeter, 3)
hello world
hello world
hello world

3.闭包

   如果在一个内部函数里,对在外部作用域(但不是在全局作用域)的变量进行引用,那么内部函数就被认为是闭包(closure)。其实最准确的说法应该是闭包是由函数和与其相关的引用环境组合而成的实体。如:

>>> a = 0
>>> def get_a():
...     return a
 
>>> get_a()
0
 
>>> a = 3
>>> get_a()
3

上述例子中,a为一个global变量,a和get_a()组成了一个闭包,所以a改变时也会直接反应到get_a()函数中。

作用:闭包的作用不言而喻,加强模块化,增强抽象等等。而且在闭包的基础上才实现了装饰器。

4.装饰器:

   装饰器是一个很著名的设计模式,经常被用于有切面需求的场景,较为经典的有插入日志、性能测试、事务处理等。装饰器是解决这类问题的绝佳设计,有了装饰器,我们就可以抽离出大量函数中与函数功能本身无关的雷同代码并继续重用。概括的讲,装饰器的作用就是为已经存在的对象添加额外的功能。
比如你想在在调用某个函数时都想计算其执行时间,那么就可以使用装饰器来进行,比如下面的代码:

>>> def time_use(func):
...     def wrapper():
...         start = time.clock()
...         func()
...         end = time.clock()
...         print 'used:', end - start
...     return wrapper
 
>>> @time_use
... def foo():
...     print 'hello world'
 
>>> foo()
hello world
used: 0.000116

5.生成器(只能使用一次):

   通过列表生成式,我们可以直接创建一个列表。但是,受到内存限制,列表容量肯定是有限的。而且,创建一个包含100万个元素的列表,不仅占用很大的存储空间,如果我们仅仅需要访问前面几个元素,那后面绝大多数元素占用的空间都白白浪费了。

   所以,如果列表元素可以按照某种算法推算出来,那我们是否可以在循环的过程中不断推算出后续的元素呢?这样就不必创建完整的list,从而节省大量的空间。在Python中,这种一边循环一边计算的机制,称为生成器(Generator)。

   要创建一个generator,有很多种方法。第一种方法很简单,只要把一个列表生成式的[]改成(),就创建了一个generator:

>>> L = [x * x for x in range(10)]
>>> L
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
>>> g = (x * x for x in range(10))
>>> g
<generator object <genexpr> at 0x104feab40>


创建L和g的区别仅在于最外层的[]和(),L是一个list,而g是一个generator。

我们可以直接打印出list的每一个元素,但我们怎么打印出generator的每一个元素呢?

如果要一个一个打印出来,可以通过generator的next()方法:

>>> g.next()
0
>>> g.next()
1
>>> g.next()
4
>>> g.next()
9
>>> g.next()
16
>>> g.next()
25
>>> g.next()
36
>>> g.next()
49
>>> g.next()
64
>>> g.next()
81
>>> g.next()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration

generator保存的是算法,每次调用next(),就计算出下一个元素的值,直到计算到最后一个元素,没有更多的元素时,抛出StopIteration的错误。当然,上面这种不断调用next()方法实在太变态了,正确的方法是使用for循环,因为generator也是可迭代对象:

>>> g = (x * x for x in range(10))
>>> for n in g:
...     print n
...
0
1
4
9
16
25
36
49
64
81

比如用函数的方法来实现斐波那契数列:

def fib(max):
    n, a, b = 0, 0, 1
    while n < max:
        print b
        a, b = b, a + b
        n = n + 1

def fib(n):
    if n <= 2:
        return n
    esle:
        return fib(n-1) + fib(n-2)

def fib(n):
    a, b = 0, 1
    for i in xrange(n):
        a, b = b, a + b
    return b

上面的函数会输出前n个数:

>>> fib(5)
1
1
2
3
5

仔细观察,可以看出,fib函数实际上是定义了斐波拉契数列的推算规则,可以从第一个元素开始,推算出后续任意的元素,
这种逻辑其实非常类似generator。也就是说,上面的函数和generator仅一步之遥。要把fib函数变成generator,
只需要把print b改为yield b就可以了:

def fib(max):
    n, a, b = 0, 0, 1
    while n < max:
        yield b
        a, b = b, a + b
        n = n + 1

这就是定义generator的另一种方法。如果一个函数定义中包含yield关键字,那么这个函数就不再是一个普通函数,而是一个generator:

>>> fib(6)
<generator object fib at 0x104feaaa0>

这里,最难理解的就是generator和函数的执行流程不一样。函数是顺序执行,遇到return语句或者最后一行函数语句就返回。而变成generator的函数,在每次调用next()的时候执行,遇到yield语句返回,再次执行时从上次返回的yield语句处继续执行。

6.迭代器:

   可以直接作用于for循环的对象叫可迭代对象,可以被next()函数调用并不断返回容器中下一个值的对象叫迭代器。迭代器有两个方法:next()返回容器中的下一个元素;__iter__返回迭代器对象本身。如:

>>> l = [1,2]
>>> it = iter(l)
>>> print it
<listiterator object at 0x108033990>
>>> print it.next()
1
>>> print it.next()
2
>>> print it.next()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration

上一篇:Ubuntu系统常用环境安装

下一篇:Python 虚拟环境:Virtualenv