Python的学习
高级特性
切片
迭代
列表生产式
L=[]
for x in range(1,11):
L.append(x*x)
但是循环太繁琐,而列表生成式则可以用一行语句代替生成上面的list:
[x*x for x in range(1,11)]
for循环后面还可以加上if判断,这样我们就可以筛选出仅偶数的平方:
[x*x for x in range(1,11) if x%2==0]
还可以使用两层循环,可以生成全排列
[m+n for m in ‘ABC’ for n in ‘XYZ’]
result:
[‘AX’,’AY’,’AZ’,’BX’,’BY’,’BZ’,’CX’,’CY’,’CZ’]
运用列表生成器,可以写出非常简洁的代码
import os
[d for d in os.listdir(‘.’)]
[‘.emacs.d’,‘.ssh’,...]
for循环其实可以同时使用两个甚至多个变量
for k,v in d.items():
print(k,’=’,v)
裂变生成式也可以使用两个变量来生产list:
[k+’=‘+v for k,v in d.items()]
运用列表生产式,可以快速生产list,可以通过一个list推导出l另一个list,而代码十分简洁。
生成器
通过列表生成器,我们可以直接创建一个list。但是,受限于内存,列表容量肯定是有限的。然而,创建一个100万的元素的列表,不仅需要占用很大的空间。
所以,如果列表元素的可以按照某种算法推算出来,那我们是否可以在循环的过程中不断推算出后续的元素?这样我们就不需要大量的内存空间,从而节省了大量的内存空间。
在python中,一遍推导一遍计算的机制,称为生成器。generator
要创建一个generator,有很多种方法。第一种方法很简单,只要把列表生成器[]
改成()
.就可以创建一个generator。
L=[x*x for x in range(10)]
g=(x*x for x in range(10))
创建L和g区别仅在于最外面层的【】和(),【】是一个列表,然而()则是一个generator。
可以通过next()函数来获得下一个返回值。
斐波拉契列用列
def fib(max):
n,a,b=0,0,1
while n<max:
print b
a,b=b,a+b
n=n+1
return ‘done’
仔细观察,可以看出,fib函数实际上是定义了斐波拉契数列的推算规则,可以从第一个元素开始,推算出后续任意的元素,这种逻辑其实非常类似generator。
def fib(max):
n,a,b=0,0,1
while n<max:
yield n
a,b=b,a+b
n=n+1
return ‘done’
这里,最难理解的就是generator和函数的执行流程不一样。函数是顺序执行,遇到return语句或者最后一行函数语句就返回。而变成generator的函数,在每次调用next()的时候执行,遇到yield语句返回,再次执行时从上次返回的yield语句处继续执行。
举一个简单的例子,定义一个gener,依次返回数字1,3,5:
def odd():
print(‘step 1’)
yield 1
print(’step 2’)
yield 3
print(’step 3’)
yield 5
调用该generator时,首先要生成一个generator对象,然后用next()函数不断获得下一个返回值:
o=odd()
next(o)
next(o)
next(o)
可以看到,odd不是普通函数,而是generator,在执行过程中,遇到yield就终端,下次有继续执行。执行3次yield后,已经没有yield可以执行了,所以,第4次调用next(o)就报错,
但是用for循环调用generator时,发现拿不到generator的return语句的返回值。如果想要拿到返回值,必须捕获stopIteration错误,返回值包含在StopIterationd的value中:
while True:
try:
x=next(g)
print(‘g:’,x)
exception StopInteration as e:
break
迭代器
我们已经知道,可以直接作用域for循环的数据了下有以下几种:
一类是集合数据类:
- 一类是集合数据类型,如list、tuple、dict、set、str等
- 一类是generator,包括生成器和带yield的generator function
这些可以直接作用于for循环的对象统称为可迭代对象:Iterable。
可以使用isinstance()判断一个对象是否是Iterable对象
from collections import Iterable
isinstance([],Iterable)
而生产器不但可以作用于for循环,还可以被next()函数不断调用并返回下一个值,直到最后抛出StopIteration错误表示无法继续返回下一值了。
可以被next()函数调用并不断返回下一个值的对象陈给迭代器:Iterator.
isinstance(iter([]),Iterator)
函数式编程
高阶函数
map/reduce
python内建了map()和reduce()函数
我们先看map。map()函数接收两个参数,一个是函数,一个是iterable,map将闯入的函数依次作用到序列的每个元素,并把结果作为新的Iterator返回。
map()传入的第一个参数是f,即函数对象本身。由于结果r是一个Iterator是惰性序列。因此通过list()函数让它把整个序列都计算出来并返回一个list。
在看reduce的用法。reduce把一个函数作用一个序列[x1,x2,…],这个函数必须接收两个参数,reduce把结果继续和序列的下一个元素做累积计算。
filter
和map()类似,filter()也接收一个函数和一个序列。和map()不同的是,filter()把传入的函数依次作用于每个元素,然后根据返回值是True和False决定保留还是丢失该元素。
filter的作用是从一个序列中筛选出符合条件的元素。由于filter()使用惰性计算,所以只有在取filter()结果的时候,
sorted
排序也是在程序中经常用到的算法。无论使用冒泡排序还是快速排序,排序的核心是比较两个元素的大小。如果是数字,我们可以直接比较,但是如果是字符串或者两个dict呢?直接比较数字上的大小还是没有意义的,因此,比较的过程必须通过函数抽象出来。
sorted([36,5,-12,9,-21])
sorted([36,5,-12,9,21],key=abs)
默认情况下,对字符串排序,是按照ASCII的大小比较,大写字母Z会排在小写字母a的前面。
现在,我们提出排序应该忽略大小写,按照字母序排序。要实现这个算法,不必对现有代码的大加改动,只要我们能用一个key函数把字符串映射为忽略大小写排序即可。忽略大小写比较两个字符串,实际上就是先把字符串都变成大写或者小写,再比较。
sorted([‘bob’,’about’,’Zoo’,’Credit’])
sorted([‘bob’,’about’,’Zoo’],key=str.lower)
sorted([‘bob’,‘about’,‘Zoo’],key=str.lower,reverse=True)
print(sorted(students,key=itemgetter(0)))
print(sorted(students,key=lambda t:t[1]))
print(sorted(students,key=itemgetter(1),reverse=True)
返回函数
高阶函数除了可以接受函数作为参数外,还可以把函数作为结果返回。
def calc_sum(**args):
ax=0
for n in args
ax=ax+n
return ax
def lazy_sum(*args):
def sum():
ax=0
for n in args:
ax=ax+n
return ax
return sum
装饰器
由于函数也是一个对象,而且函数对象可以被赋值给变量,所以,通过变量也能调用该函数。
def now():
print (‘2005-3-25’)
f=now
f()
函数对象有一个name属性,可以拿到函数的名字:
now.__name__
f.__name__
现在,假设我们要增强now()
函数的功能,比如,在函数调用前后自动打印日志,但又不希望修改now()
函数的定义,这种在代码运行期d动态增加的功能的方式,称之为装饰器。
本质上,decorator就是一个返回函数的高阶函数。所以,我们要定义一个能打印日志的decorator,可以定义如下:
def log(func):
def wapper(*args,**kw):
print(‘call %s():’% func.__name__)
return func(*args,**kw)
return wrapper
观察上面的log,因为它是一个decorator,所以接受一个函数的高阶函数。所以,我们要定义一个能打印日志的decorator,可以定义如下:
@log
def now():
print(’2015-3-25’)
调用now()函数,不仅会运行now()函数本身,还会运行now()函数前打印一行日志。
把@log放到now()函数的定义出,相当于执行了语句: