高级特性
切片
L = ['Michael', 'Sarah', 'Tracy', 'Bob', 'Jack'],取出list的前n个元素
r = []
n = 3
for i in range(n):
r.append(L[i])
Python提供切片(Slice)操作符,取出前3个元素:L[0:3],取出前10个元素:L[:10]
迭代
Python的for循环可以用在list或tuple上
dict迭代示例:
for key in d: 迭代key,默认
for value in d.values(): 迭代value
for k,v in d.items(): 迭代key、value
字符串迭代: for ch in 'ABC' ==> A B C
判断对象是否可迭代:
引入模块 from collections.abc import Iterable
API: isinstance(元素,Iterable) ==> True/False
如果要对list实现类似Java那样的下标循环,Python内置的enumerate函数可以把list编程索引-元素对,这样可以在for循环种同时迭代索引和元素本身:
for i,value in enumerate(['A','B','C']):
print(i,value)
...
0 A
1 B
2 C
列表生成式
列表生成式即List Comprehensions, 是Python内置用来创建list的生成式
举个例子,要生成list [1,2,3,4,5,6,7,8,9],可以用list(range(1,11))
如果要生成[1x1, 2x2, 3x3, ..., 10x10]怎么做,方法一是循环:
L = []
for x in range(1,11):
L.append(x * x)
用列表生成式: [x * x for x in range(1,11)]
用列表生成式筛选偶数的平方:[x * x for x in range(1,11) if x % 2 == 0]
还可以使用两层循环,生成全排列,三层循环慎用
[m + n for m in 'ABC' for n in 'XYZ']
==> ['AX', 'AY', 'AZ', 'BX', 'BY', 'BZ', 'CX', 'CY', 'CZ']
运用列表生成式,可以写出非常简洁的代码。例如,列出当前目录下的所有文件和目录名,可以通过一行代码实现:
>>> import os
>>> [d for d in os.listdir('.')]
for循环其实可以同时使用两个甚至多个变量,比如dict的items()可以同时迭代key和value:
>>> d = {'x': 'A', 'y': 'B', 'z': 'C' }
>>> for k, v in d.items():
... print(k, '=', v)
...
y = B
x = A
z = C
因此,列表生成式也可以使用两个变量来生成list:
>>> d = {'x': 'A', 'y': 'B', 'z': 'C' }
>>> [k + '=' + v for k, v in d.items()]
['y=B', 'x=A', 'z=C']
最后把一个list中所有的字符串变成小写:
>>> L = ['Hello', 'World', 'IBM', 'Apple']
>>> [s.lower() for s in L]
['hello', 'world', 'ibm', 'apple']
if ... else
使用if: [x for x in range(1,11) if x % 2 == 0] ==> [2,4,6,8,10]
使用if else: [x if x % 2 == 0 else -x for x in range(1,11)]
==> [-1, 2, -3, 4, -5, 6, -7, 8, -9, 10]
生成器
生成器Generator,generator保存的是算法,通过算法计算出下一个值
第一种:把列表生成式的[]改成()
L = [x * x for x in range(10)] ==> [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
g = (x * x for x in range(10)) ==> g就是个generator函数
可以使用next(g)获取下一个返回值,如果超出会报错StopIteration
generator函数也可以使用for循环
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
return 'done'
把print(b)改成yield b就成了generator函数
def fib(max):
n, a, b = 0, 0, 1
while n < max:
yield b
a, b = b, a + b
n = n + 1
return 'done'
这就是定义generator的另一种方法。如果一个函数定义中包含yield关键字,那么这个函数就不再是一个普通函数,而是一个generator函数,调用一个generator函数将返回一个generator
generator函数和普通函数的执行流程不一样。普通函数是顺序执行,遇到return语句或者最后一行函数语句就返回。而变成generator的函数,在每次调用next()的时候执行,遇到yield语句返回,再次执行时从上次返回的yield语句处继续执行。
注意:调用generator函数会创建一个generator对象,多次调用generator函数会创建多个相互独立的generator。
迭代器
能直接作用于for循环的数据类型有以下几种:
一类是:list、tuple、dict、set、str
一类是generator,包括生成器和带yield的generator function
可以使用isinstance(对象,Iterable)判断对象是否是Iterable对象:
引入依赖 import collections.abc import Iterable
>>> isinstance([], Iterable)
True
>>> isinstance({}, Iterable)
True
>>> isinstance('abc', Iterable)
True
>>> isinstance((x for x in range(10)), Iterable)
True
>>> isinstance(100, Iterable)
False
但list、dict、str不是Iterator
函数式编程
高阶函数
map() 、reduce()
map()函数接受两个参数,一个是函数,一个是Iterable,map将传入的函数一次作用到序列的每个元素,并把结果作为新的Iterator返回,举例说明,比如我们有一个函数f(x)=x2,要把这个函数作用在一个list [1, 2, 3, 4, 5, 6, 7, 8, 9]上,就可以用map()实现如下:
>>> def f(x):
... return x * x
...
>>> r = map(f, [1, 2, 3, 4, 5, 6, 7, 8, 9])
>>> list(r)
[1, 4, 9, 16, 25, 36, 49, 64, 81]
把list中所有数字转为字符串
list(map(str,[1,2,3,4,5,6,7,8,9]))
reduce, reduce把一个函数作用在一个序列上[x1, x2, x3, ...]上,reduce函数必须接收两个参数,reduce把结果继续和序列的下一个元素做累积计算,其效果就是:
reduce(f, [x1, x2, x3, x4]) = f(f(f(x1,x2), x3), x4)
比方说对一个序列求和,可用reduce实现:
>>> from functools import reduce
>>> def add(x, y):
... return x + y
...
>>> reduce(add, [1, 3, 5, 7, 9])
25
当然,求和我们一般用python的sum()函数,没必要自己写
更复杂的reduce()用法,可以在实践中学习,感觉有点绕
filter
Python内建函数filter()函数用于过滤序列,相当于Java里stream流的filter
例如,在一个list中,删掉偶数,只保留奇数,可以这么写:
def is_odd(n):
return n % 2 == 1
list(filter(is_odd, [1, 2, 4, 5, 6, 9, 10, 15]))
把一个序列中的空字符串删掉,可以这么写:
def not_empty(s):
return s and s.strip()
list(filter(not_empty, ['A', '', 'B', None, 'C', '']))
==> ['A', 'B', 'C']
sorted
sorted()函数,对集合进行排序
sorted()函数可以接收list:
>>> sorted([36, 5, -12, 9, -21])
[-21, -12, 5, 9, 36]
也可以携带排序规则:
>>> sorted([36, 5, -12, 9, -21], key=abs)
[5, 9, -12, -21, 36]
对字符串按照字母排序:
>>> sorted(['bob', 'about', 'Zoo', 'Credit'])
['Credit', 'Zoo', 'about', 'bob']
这是因为ASCII的大小比较,'Z'排在'a'前面,我们可以忽略大小写再进行排序:
>>> sorted(['bob', 'about', 'Zoo', 'Credit'], key=str.lower)
['about', 'bob', 'Credit', 'Zoo']
如果想要反向排序,传入第三个参数reverse=True:
>>> sorted(['bob', 'about', 'Zoo', 'Credit'], key=str.lower, reverse=True)
['Zoo', 'Credit', 'bob', 'about']
给一组tuple表示的学生名字和成绩排序:
L = [('Bob', 75), ('Adam', 92), ('Bart', 66), ('Lisa', 88)]
根据名字排序:
print(sorted(L, key=lambda x: x[0]))
根据成绩排序:
print(sorted(L, key=lambda x: x[1], reverse=True))
返回函数
高阶函数除了可以接受函数作为参数外,还可以把函数作为结果值返回
比如求和的函数是这样的:
def calc_sum(*args):
sum = 0
for n in args:
sum += n
return sum
如果不想立即求和,在后面的代码中根据需要再计算,那么可以返回函数
def lazy_sum(*args):
def sum():
sum = 0
for n in args:
sum += n
return sum
return sum
当我们调用lazy_sum()函数时,返回的是求和函数
>>> f = lazy_sum(1, 3, 5, 7, 9)
>>> f
<function lazy_sum.<locals>.sum at 0x101c6ed90>
调用函数f时,才真正计算求和结果
>>>f()
25
注意:每次调用lazy_sum()时,都会返回一个新的函数
闭包
f1 = lazy_sum(1,2,3,4)
f2 = lazy_sum(1,2,3,4)
如果lazy_sum()返回的函数其定义内部引用了局部变量args,会被所有的创建的函数所共享,如f1、f2
灵活运用的话这里比较深奥难懂,可以自行看网上课程
返回闭包时牢记一点:返回函数不要引用任何循环变量,或者后续会发生变化的变量。
匿名函数
请先看个例子,计算f(x) = x2:
>>> list(map(lambda x: x * x, [1, 2, 3, 4, 5, 6, 7, 8, 9]))
[1, 4, 9, 16, 25, 36, 49, 64, 81]
以上案例就是用了lambda的匿名函数,lambda x:x*x,实际上它是
def f(x):
return x*x
匿名函数的限制:只能有一个表达式,不用写return,返回值就是该表达式的结果
匿名函数的好处:因为没有名字,不用担心函数名冲突
可以把匿名函数作为返回值返回,比如:
def build(x,y):
return lambda: x*x +y*y
装饰器
Decorator,如同java的装饰器,Python的装饰器是一种增强能力的设定,以下是日志打印函数
def now():
print('2015-3-25')
我们想增强now()函数的功能,同事不希望修改now()函数,那么可以采用装饰器Decorator
def log(func):
def wrapper(*args, **kw):
print('call %s():' % func.__name__)
return func(*args, **kw)
return wrapper
decorator可以用Python的@语法,置于函数的定义处:
@log
def now():
print('2015-03-25')
>>> now()
call now():
2015-03-25
但此时打印的数据是固定死的,如果需要打印自定义文本,那么需要从外界传参,还需要顶一个更高阶的函数
def log(text):
def decorator(func):
def wrapper(*args, **kw):
print('%s %s():' % (text, func.__name__))
return func(*args, **kw)
return wrapper
return decorator
3层嵌套的decorator用法如下:
@log('excutor')
def now():
print('2015-03-25')
执行结果如下:
>>> now()
excute now():
2015-03-25
偏函数
Python的functools模块提供的偏函数(Partial function),用来简化函数的参数
比如字符串转换成各个进制的数字,int()默认为十进制:
>>> int('12345')
12345
int()函数还提供了base参数以供选择进制
>>> int('12345', base=8)
5349
>>> int('12345', 16)
74565
如果每次都传入base参数我们觉得麻烦,那么可以定义一个int2()函数,把默认的base=2传进去
def int2(x,base=2):
return int(x,base)
functools.partial就是帮助我们创建一个函数的,使用functools.partial()语法创建函数:
引入依赖: import functools
>>> int2 = functools.partial(int, base=2)
>>> int2('1000000')
64