关于字典和列表的使用区别
和list比较,dict有以下几个特点:
查找和插入的速度极快,不会随着key的增加而变慢;
需要占用大量的内存,内存浪费多。
而list相反:
查找和插入的时间随着元素的增加而增加; (数据结构是链表式的和顺序表形式的)
占用空间小,浪费内存很少。
所以,dict是用空间来换取时间的一种方法。
dict可以用在需要高速查找的很多地方,在Python代码中几乎无处不在,正确使用dict非常重要,需要牢记的第一条就是dict的key必须是不可变对象。
这是因为dict根据key来计算value的存储位置,如果每次计算相同的key得出的结果不同,那dict内部就完全混乱了。这个通过key计算位置的算法称为哈希算法(Hash)。
要保证hash的正确性,作为key的对象就不能变。在Python中,字符串、整数等都是不可变的,因此,可以放心地作为key。而list是可变的,就不能作为key:
列表生成式
- 列表生成式即List Comprehensions,是Python内置的非常简单却强大的可以用来创建list的生成式。
普通生成式
>>> [x * x for x in range(1, 11)]
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
加上if判断 做为筛选
>>> [x * x for x in range(1, 11) if x % 2 == 0]
[4, 16, 36, 64, 100
后边两个可迭代对象 相当于for循环的嵌套
>>> [m + n for m in 'ABC' for n in 'XYZ']
['AX', 'AY', 'AZ', 'BX', 'BY', 'BZ', 'CX', 'CY', 'CZ']
课后习题
输入:
L1 = ['Hello', 'World', 18, 'Apple', None]
输出:
['hello', 'world', 'apple']
测试:
[x.lower() for x in l1 if isinstance(x, str)]
生成器
通过列表生成式,我们可以直接创建一个列表。但是,受到内存限制,列表容量肯定是有限的。而且,创建一个包含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 0x1022ef630>
调用生成器:
通过next(g) 来调用生成器, 返回需要的内容 迭代到最后一个元素的时候 会抛出 stopIteration 异常来结束迭代
>>> next(g)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
使用for循环来迭代生成器
>>> g = (x * x for x in range(10))
>>> for n in g:
... print(n)
使用生成器来完成斐波纳挈数列
def fib(max):
n, a, b = 0, 0, 1
while n < max:
yield b
a, b = b, a + b
n = n + 1
for i in fibZ(5):
print i
生成器函数和普通函数的区别
generator是非常强大的工具,在Python中,可以简单地把列表生成式改成
generator,也可以通过函数实现复杂逻辑的generator。
要理解generator的工作原理,它是在for循环的过程中不断计算出下一个元素
,并在适当的条件结束for循环。对于函数改成的generator来说,遇到return语句或者执行到函数体最后一行语句,就是结束generator的指令,for循环
随之结束。
普通函数调用直接返回结果
生成器函数调用返回的是一个生成器的对象
高阶函数的简单分析
一个函数可以接受另外一个函数做为参数, 这种函数就称为高阶函数
def add(x, y, f):
return f(x) + f(y)
x = -5
y = 6
f = abs
f(x) + f(y) ==> abs(-5) + abs(6) ==> 11
return 11
内建函数
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的用法。reduce把一个函数作用在一个序列[x1,x2,...]上,这个函数必须接收两个参数,reduce把结果继续和序列的下一个元素做累积计算,其效果就是:
>>> from functools import reduce
>>> def add(x, y):
... return x + y
...
>>> reduce(add, [1, 3, 5, 7, 9])
使用map和reduce 配合 把一个字符串转换成为一个整数
from functools import reduce
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] # 按照键来进行取值
def str2int(s):
return reduce(lambda x, y: x * 10 + y, map(char2num, s))
课后练习
利用map()函数,把用户输入的不规范的英文名字,变为首字母大写,其他小写的规范名字。
输入:['adam', 'LISA', 'barT'],
输出:['Adam', 'Lisa', 'Bart']:
def normalize(name):
return name.lower().title()
L1 = ['adam', 'LISA', 'barT']
L2 = list(map(normalize, L1))
print L2
利用map和reduce编写一个str2float函数,把字符串'123.456'转换成浮点数123.456:
def str2float(s):
nums = map(lambda ch: CHAR_TO_FLOAT[ch], s)
point = 0
def to_float(f, n):
nonlocal point
if n == -1:
point = 1
return f
if point == 0:
return f * 10 + n
else:
point = point * 10
return f + n / point
return reduce(to_float, nums, 0.0)
print(str2float('0'))
print(str2float('123.456'))
print(str2float('123.45600'))
print(str2float('0.1234'))
print(str2float('.1234'))
print(str2float('120.0034'))
def demo(m):
"""判断是不是回文数字"""
n = list(str(m))
n.reverse()
print n
if "".join(n) == str(m):
return True
filter(demo, list)
sorted 排序的课后习题
按照名称进行排序
L = [('Bob', 75), ('Adam', 92), ('Bart', 66), ('Lisa', 88)]
sorted(L,key=lambda x:x[0]) 会把每个列表中的元素就行遍历 然后传递给 key执行的函数 \
dict.items() 返回的是一个列表 列表中的元素 是一个个的元组
高阶函数 返回函数
函数的嵌套 会形成闭包的概念, 多次调用闭包 之间的结果是不受影响的
高阶函数除了可以接受函数作为参数外,还可以把函数作为结果值返回。装饰器就是特殊的高阶函数 加上函数的嵌套来组合成的
但是在使用高阶函数返回函数的时候 需要注意:
闭包
注意到返回的函数在其定义内部引用了局部变量args,所以,当一个函数返回了一个函数后,其内部的局部变量还被新函数引用,所以,闭包用起来简单,实现起来可不容易。
另一个需要注意的问题是,返回的函数并没有立刻执行,而是直到调用了f()才执行。我们来看一个例子:
def count():
fs = []
for i in range(1, 4):
def f():
return i*i
fs.append(f)
return fs
f1, f2, f3 = count()
在上面的例子中,每次循环,都创建了一个新的函数,然后,把创建的3个函数都返回了。
你可能认为调用f1(),f2()和f3()结果应该是1,4,9,但实际结果是:
>>> f1()
9
>>> f2()
9
>>> f3()
9
全部都是9!原因就在于返回的函数引用了变量i,但它并非立刻执行。等到3个函数都返回时,它们所引用的变量i已经变成了3,因此最终结果为9。
返回闭包时牢记的一点就是:返回函数不要引用任何循环变量,或者后续会发生变化的变量。
如果一定要引用循环变量怎么办?方法是再创建一个函数,用该函数的参数绑定循环变量当前的值,无论该循环变量后续如何更改,已绑定到函数参数的值不变:
def count():
def f(j):
def g():
return j*j
return g
fs = []
for i in range(1, 4):
fs.append(f(i)) # f(i)立刻被执行,因此i的当前值被传入f()
return fs
小结
一个函数可以返回一个计算结果,也可以返回一个函数。
返回一个函数时,牢记该函数并未执行,返回函数中不要引用任何可能会变化的变量
匿名函数中的一点
函数名称的可以使用name 来获取到函数的名称
@functools.wraps(func) 使用 wraps(func) 来装饰使用的内部的函数
完整版的装饰器
import functools
def log(func):
@functools.wraps(func)
def wrapper(*args, **kw):
print('call %s():' % func.__name__)
return func(*args, **kw)
return wrapper
sys 模块的使用
sys模块有一个argv变量,用list存储了命令行的所有参数。argv至少有一个元素,因为第一个参数永远是该.py文件的名称
使用solt 累的属性来显示可能给累绑定的属性
class Student(object):
__slots__ = ('name', 'age') # 用tuple定义允许绑定的属性名称
>>> s = Student() # 创建新的实例
>>> s.name = 'Michael' # 绑定属性'name'
>>> s.age = 25 # 绑定属性'age'
>>> s.score = 99 # 绑定属性'score'
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'Student' object has no attribute 'score'
由于'score'没有被放到__slots__中,所以不能绑定score属性,试图绑定score将得到AttributeError的错误。
使用__slots__要注意,__slots__定义的属性仅对当前类实例起作用,对继承的子类是不起作用的:
mkvirtualenv.bat –python==C:\Python\Python36-32\python.exe py3 指定版本号的安装
使用枚举类
确定元类的使用情况 哪些地方会使用到元类
redis
mongod
mysql
flask
django
restful-api
单元测试
文档测试
def abs(n):
'''
Function to get absolute value of number.
Example:
>>> abs(1)
1
>>> abs(-1)
1
>>> abs(0)
0
'''
return n if n >= 0 else (-n)
无疑更明确地告诉函数的调用者该函数的期望输入和输出。
并且,Python内置的“文档测试”(doctest)模块可以直接提取注释中的代码并执行测试。
文件读写的操作
for else 的使用:
当for语句正常执行的话, else 语句也就能正常的进行执行,
实例:
for item in range(10):
print(item)
else:
print("else")
这个语句是 会打印 出来 item的值 和 else
for item in []:
print(item)
else:
print ("else")
这个也会打印出来 else
for item in range(10):
print(item)
if item == 10:
break
else:
print ("else")
如果for循环被中断了, 那么这个else 也就不能再使用了
可以通过 os.access() 来查看当前文件是不是有权限, 去进行读写的操作 或者是 文件是不是存在的
调用read()会一次性读取文件的全部内容,如果文件有10G,内存就爆了,所以,要保险起见,可以反复调用read(size)方法,每次最多读取size个字节的内容。另外,调用readline()可以每次读取一行内容,调用readlines()一次读取所有内容并按行返回list。因此,要根据需要决定怎么调用。
如果文件很小,read()一次性读取最方便;如果不能确定文件大小,反复调用read(size)比较保险;如果是配置文件,调用readlines()最方便:
for line in f.readlines():
print(line.strip()) # 把末尾的'\n'删掉
遇到有些编码不规范的文件,你可能会遇到UnicodeDecodeError,因为在文本文件中可能夹杂了一些非法编码的字符。遇到这种情况,open()函数还接收一个errors参数,表示如果遇到编码错误后如何处理。最简单的方式是直接忽略:
f = open('/Users/michael/gbk.txt', 'r', encoding='gbk', errors='ignore')