Python杂记

由于长时间的不用,Python的很多特性都已经开始遗忘了,这里对Python’的一些特性作记录以方便以后的复习使用。

函数的默认值

默认值只计算一次。这使得默认值是列表、字典或大部分类的实例时会有所不同。例如,下面的函数在后续调用过程中会累积传给它的参数:

def f(a, L=[]):
    L.append(a)
    return L

print f(1)
print f(2)
print f(3)

####OUTPUT:
[1]
[1, 2]
[1, 2, 3]

如果你不想默认值在随后的调用中共享,可以像这样编写函数:

def f(a, L=None):
    if L is None:
        L = []
    L.append(a)
    return L

星号参数

在Python中,函数的参数中单个‘*’表示元组参数,‘**’表示字典参数,其中字典参数必须放在元组参数后面

def ff(kind, *arguments, **keywords):
	pass;
	
## 调用方式:
ff(1,2,3,typ1=4,type2=5)

上面的调用中,2,3将会以元组参数的形式赋值给arguments=(2,3);type1,type2将会以字典参数的形式赋值给keywords={type1=4,type2=4}

列表推导式

列表推导式中每个for循环的计算顺序是从左向右

[x**2 for x in range(10)]
[(x, y) for x in [1,2,3] for y in [3,1,4] if x != y]
###############
vec = [[1,2,3], [4,5,6], [7,8,9]]
[num for elem in vec for num in elem]
###############
matrix = [
          [1, 2, 3, 4],
          [5, 6, 7, 8],
          [9, 10, 11, 12],
          ]

print([row[i] for row in matrix for i in range(4)])
print([[row[i] for row in matrix] for i in range(4)])

print([row[i] for i in range(4) for row in matrix])
print([[row[i] for i in range(4)] for row in matrix])

生成器

yield_stmt ::=  yield_expression

yield语句只在定义生成器函数时使用,且只在生成器函数的函数体中使用。虽然生成器是一个函数的,但我们不应当把它当做一个函数来理解,可以要把生成器当成一个具有数据结构的变量,如Java中的对象一样。只是这个变量具有一些特殊的“操作”。

def gen():
    for x in xrange(0,10):
        yield x
        if x%2==0:
            yield "x is even"
        
g = gen();

print(g.next()) ## 0
print(g.next()) ## x is even
print(g.next()) ## 1
print(g.next()) ## 2
print(g.next()) ## x is even

yield 语句在python的文档中给出的解释如下:

当执行一个yield语句时,生成器的状态将冻结起来并且将expression_list的值返回给next()的调用者。“冻结”的意思是所有局部的状态都会被保存起来,包括当前局部变量的绑定、指令指针、内部的计算栈:保存足够的信息以使得下次调用next()时,函数可以准确地继续,就像yield语句只是另外一个外部调用。

在新的yield规范中,增加了对生成器增加了send(),throw(),close()等方法,具体使用参考PEP 0342

迭代器

迭代器让我们避开了在遍历集合(列表)类型元素使用索引的尴尬,但迭代器不是线程安全的,所以不建议在多线程中使用迭代器。若想一个类型可以具有迭代器功能,只需要在该类型中实现iter()方法。

lst = range(4)
it = iter(lst)

it.next()
next(it)

Python提供了工厂方法从一个具有iterable特性的对象中获取迭代器,可以通过迭代器自身的next()来进行迭代,也可以使用内建函数next()来进行迭代。

但在Python的迭代器中没有判断是否已经到了迭代结束的hasNext()方法,所以只能通过异常(StopIteration)来进行迭代结束的判断。

迭代器虽然避开了索引,但是有时我们游需要索引,这时可以通过使用 enumerate()方法,对迭代器进行包装,使得调用迭代器的next()方法后返回的不光有迭代对象,还有该对象的下标。

for idx, element in enumerate(lst):
	print(idx,element)

上下文管理器

“上下文”的概念表明,在某段程序执行之前,我们需要一些准备工作;在该程序执行结束之后,需要一些收尾工作。这里的“准备工作”和“收尾工作”就是该段程序的上限文。所以上下文管理器在Python中实际的作用就是为代码的开始执行做准备,为结束做善后。这个概念在程序使用计算机资源,比如文件时,特别有用,在使用文件前,需要判断文件是否存在,能否打开,然后打开文件;在使用文件结束后进行文件句柄关闭等资源回收操作。

with open("new.txt", "w") as f:
    print(f.closed)
    f.write("Hello World!")
print(f.closed)

在Python中任何对象只需要实现enter()exit()操作就可以被用于上下文管理器。这两个方法就像它的名字所表示的那样清晰,enter()方法需要返回关键字with后表达式的返回值,并把该值赋给as后的变量。exit()方法在代码执行结束后执行,如果代码执行期间抛出了异常,那么该方法其实是可以捕获到异常的。exit()方法的完整方法签名如下:

def __exit__(self, exc_type, exc_val, exc_tb)

Python中通过contextlib包提供了增强的上下文管理器。

装饰器

要了解装饰器,就需要了解面向切面的编程(经常讨论面向对象和面向过程)。在Python中装饰器承担了这样一种功能,在不改变函数的前提下,为该函数增加额外的功能,当然在Python中能用装饰器的不光是函数,其他的对象也可以。

def decorator(func):
    def wapper():
        print "do some before"
        func()
        print "do some after"
    return wapper


def baseFun():
    print "the base function"

fun = decorator(baseFun)
fun()

上面就是给方法baseFun定义了一个装饰器。采用装饰器装饰过的baseFun不光具有原来的功能,还新增加了其他的功能。下面是Python中通过使用@符号对函数使用装饰器的方法。和Java中的注解(Annotation)很类似。

def decorator(func):
    def wapper():
        print "do some before"
        func()
        print "do some after"
    return wapper

@decorator
def baseFun():
    print "the base function"

baseFun()

上面的装饰器定义,最后通过装饰器返回的函数,并不是我们传入的函数,这在某些编程情境下,比如我想知道该函数的名称时,上面被装饰器作用后的函数是无法获取到原来的函数名的。如果想保留原来的函数基本信息,需要利用functools包中的一个装饰器对来改造之前定义的装饰器。functools包的具体功能,请参考functools

import functools
def decorator(func):
    @functools.wraps(func)
    def wapper():
        print "do some before"
        func()
        print "do some after"
    return wapper

@decorator
def baseFun():
    print "the base function"

baseFun()
print baseFun.__name__

闭包

python中的闭包从表现形式上定义(解释)为:如果在一个内部函数里,对在外部作用域(但不是在全局作用域)的变量进行引用,那么内部函数就被认为是闭包(closure). 这里的”外部作用域的变量”是只读的。

明白了闭包表现形式上的定义和注意点,就可以很好的理解闭包中的各种匪夷所思的现象了。

#SECTION 1
def foo1():
    a = 1
    def bar():
        a = a +1
        return a
    return bar

c = foo1()
print c()
#SECTION 2
def foo2():
    a = 1
    def bar():
        b = a
        b = b +1
        return b
    return bar

c = foo2()
print c()
#SECTION 3
def foo3():
    a = 1
    def bar():
        b = a
        a = b +1
        return a
    return bar

c = foo3()
print c()
#SECTION 4
def foo4():
    a = 1
    def bar():
        a = 2
        a = a +1
        return a
    return bar

c = foo4()
print c()

闭包中需要注意的就是外层变量是不可变的,所以新版的Python中引入了nonlocal关键字,来指定该变量是外部变量。

def foo5():
    a = 1
    def bar():
        nonlocal a
        a = a +1
        return a
    return bar

c = foo5()
print c()

装饰器是闭包的一种应用,闭包还经常用于函数配置,配置信息不同,函数的行为结果也会不同:

def make_adder(addend):
    def adder(augend):
        return augend + addend
    return adder

p = make_adder(23)
q = make_adder(44)

print p(100)
print q(100)

make_adder 方法传入的参数不同,导致q和p函数虽然是同一个函数,且传入参数相同,但结果却不一样。

Jeff Lee /
Published under (CC) BY-NC-SA in categories python  tagged with