Python 学习笔记二
高级特性
切片
如果想取数组中的部分元素,则可以通过在数组中使用 [start:end]
实现,类似于JavaScript中的 slice(start, end)
;其中start和end
都可以为负数,表示以倒数的方式来计算,也可以只写一个负数,也可以什么都不写只有一个:,表示复制整个数组。
如果是::
,表明每隔一段来取一个,比如:
>>> 'ABCDEFG'[::2]
'ACEG'
也可以这么写,前6个数每2个取1个:
>>> 'ABCDEFG'[:6:2]
'BDF'
切片适用于类似数组的类型,比如 字符串,list,tuple等等。
迭代
在python中,迭代使用的是for...in...
关键字,同时只要是可迭代对象
,都可以迭代,比如json对象等等。
那么,如何判断一个对象是可迭代对象呢?方法是通过collections模块的Iterable类型判断:
>>> from collections import Iterable
>>> isinstance('abc', Iterable) # str是否可迭代
True
>>> isinstance([1,2,3], Iterable) # list是否可迭代
True
>>> isinstance(123, Iterable) # 整数是否可迭代
False
列表生成式
将所有的表达式放到[]
里面去执行:
>>> [x * x for x in range(1, 11) if x % 2 == 0]
[4, 16, 36, 64, 100]
生成器
怎么创建一个生成器呢?
-
将
列表生成式
中外围的[]
改为()
即可可以通过
next
函数获取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 0x1022ef630> >>> next(g) 0 >>> next(g) 1
-
在函数中使用
yield
关键字,其作用类似于return+printdef odd(): print('step 1') yield 1 print('step 2') yield(3) print('step 3') yield(5)
在执行过next后,再次执行的时候则从上一次执行
yield
后的位置继续执行,遇见yield
则返回,但是如果执行到后面已经没有yield
后,则会报错。>>> o = odd() >>> next(o) step 1 1 >>> next(o) step 2 3 >>> next(o) step 3 5
如果不想用
next
,一步一步的计算,同样的可以使用循环。def fib(max): n, a, b = 0, 0, 1 while n < max: yield b a, b = b, a + b n = n + 1 return 'done' >>> for n in fib(6): ... print(n) ... 1 1 2 3 5 8
如果想要捕捉到返回值,则在找不到next的时候(异常)返回,例如:
>>> g = fib(6) >>> while True: ... try: ... x = next(g) ... print('g:', x) ... except StopIteration as e: ... print('Generator return value:', e.value) ... break ... g: 1 g: 1 g: 2 g: 3 g: 5 g: 8 Generator return value: done
迭代器
注意区分 Iterable和Iterator
,前者是可迭代的,后者是迭代器(类似于C++中vector和::iterator),同样的,可以使用isinstance
来判断;
函数式编程
高阶函数
map/reduce
map(函数,Iterable)
,将可迭代对象中的元素执行函数以后生成一个新的iterator;
>>> 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]
reduce函数
必须接收两个参数,一个是函数,一个是list,其重点在于将list中的元素进行函数计算后作用到下一个元素上。
reduce(f, [x1, x2, x3, x4]) = f(f(f(x1, x2), x3), x4)
比如这么用:
from functools import reduce
def str2int(s):
def fn(x, y):
return x * 10 + y
def char2num(s):
return {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9}[s]
return reduce(fn, map(char2num, s))
filter
用于过滤序列,返回一个新的序列;
和map
一样,同样接收一个函数和序列,但不同的是,filter
使用函数对序列中的元素进行筛选,也就是说该函数是一个bool
函数,将值传递进去后只有返回成功才将该值加入到新的队列中。
def is_odd(n):
return n % 2 == 1
list(filter(is_odd, [1, 2, 4, 5, 6, 9, 10, 15]))
# 结果: [1, 5, 9, 15]
如果这不是一个筛选函数呢?python则认为均为false,也就是不加入任何元素,返回空序列。
sorted
对序列进行排序,后面还可以加上key来指定排序函数,还可以加上reverse表示反向排序;
class Student:
def __init__(self, name, grade, age):
self.name = name
self.grade = grade
self.age = age
def __repr__(self):
return repr((self.name, self.grade, self.age))
# student_objects = [
# Student('john', 'A', 15),
# Student('jane', 'B', 12),
# Student('dave', 'B', 10),
#]
# 使用lambda
sorted(student_objects, key=lambda student: student.age, reverse=True)
# sort by age
# [('dave', 'B', 10), ('jane', 'B', 12), ('john', 'A', 15)]
# 或者使用attrgetter
sorted(student_objects, key=attrgetter('age'), reverse=True)
闭包
当你调用了一个函数A,这个函数A返回内部的函数B,这个返回的函数B就是闭包;
def func(name):
def inner_func(age):
print 'name:', name, 'age:', age
return inner_func
bb = func('the5fire')
bb(26) # >>> name: the5fire age: 26
匿名函数
lambda表示匿名函数(和C++ 11.0中的lambda表达式类似),格式为:
lambda x : x * x
冒号前面表示参数,返回值为冒号后面的计算结果。
装饰器
需要单独写一篇文章进行总结(重要)
总之就是在不改变一个函数的前提下(其它调用该函数的也不需要改变,只需要在调用函数前面加上@decorator
就行),往该函数上加功能进行任意的扩展。
偏函数
functools.partial
表示的是将函数“保存”下来,返回来一个在原来函数的基础上重新定义的函数;
例子:
import functools
def add(a, b):
return a + b
add(4, 2)
6
plus3 = functools.partial(add, 3)
plus5 = functools.partial(add, 5)
plus3(4)
7
plus5(10)
15
除此之外,functools
模块还包括:
- functool.update_wrapper:从原始对象拷贝或加入现有partial对象
- functool.wraps:调用函数装饰器partial
- functools.reduce:等同于内置函数reduce()
- functools.cmp_to_key:将老式鼻尖函数转换成key函数,用在接受key函数的方法中
- functools.total_ordering:它是针对某个类如果定义了__lt__、le、gt、__ge__这些方法中的至少一个,使用该装饰器,则会自动的把其他几个比较函数也实现在该类中。
面向对象编程
继承和多态
对于静态语言(例如Java)来说,如果需要传入Animal类型,则传入的对象必须是Animal类型或者它的子类,否则,将无法调用run()方法。
对于Python这样的动态语言来说,则不一定需要传入Animal类型。我们只需要保证传入的对象有一个run()方法就可以了:
实例属性和类属性
在编写程序的时候,千万不要把实例属性和类属性使用相同的名字,因为相同名称的实例属性将屏蔽掉类属性,但是当你删除实例属性后,再使用相同的名称,访问到的将是类属性,个人不建议在示例上添加任何新的属性,在类中加上属性即可。
关注公众号:数据结构与算法那些事儿,每天一篇数据结构与算法