流畅的py-读书笔记

数据结构

list & tuple

  1. 列表推导
    symbols = 'ABCD'
    codes = []
    for symbol in symbols:
     codes.append(ord(symbol))
    
    等价于
    symbols = 'ABCD'
    codes = [ord(symbol) for symbol in symbols]
    

也可以这样子, 习惯了js的,其实感觉更喜欢这样子写

symbols = 'ABCD'
codes = map(ord, symbols)

其实开始没想到字符串也可以for, 阔以map

emmmmmmm~, 但是如果有条件的话,比如

symbols = 'ABCD'
codes = [ord(symbol) for symbol in symbols if ord(symbol) > 127]

等价到filtermap后, 就要用lambda表达式,像这样子

symbols = 'ABCD'
codes = filter(lambda c: c > 127, map(ord, symbols)))

列表推导还可以是二维的

>>> [char+num for char in 'ABCD' for num in '1234']
  1. 生成器表达式
    >>> codes1 = tuple([ord(symbol) for symbol in symbols])
    >>> codes1 = tuple(ord(symbol) for symbol in symbols)
    
    上面第一个是列表推导产生了一个列表,又拿列表初始化了一个tuple
    而下面这个就是叫生成器表达式…因为tuple()这已经有了圆括号,所以这里省去了生成器表达式两边的括号

生成器表达式和列表推导不同的是一次产生一个元素, 表达上两边圆括号代替方括号

tuple

tuple和list都能存放不同类型的元素, 除此外还有collections.deque也可以的
与list不同, tuple是不可变的

常见用法

>>> print '[*]%d %d' % (1, 2)

交换两数字

>>> a, b = b, a

元祖拆包-元祖前加上*, 可以拆包作为函数参数

>>> ord(*('A', ))

可以方便函数返回多个值, 然后一拆包…

  1. collections.namedtuple
    带字段名的tuple…

    >>> from collections import namedtuple
    >>> Person = namedtuple('Person', ['firstName', 'lastName'])
    >>> haibin = Person('hai', 'bin')
    >>> haibin.firstName
    'hai'
    
  2. 切片
    list, tuple, str等支持切片操作

基本操作 [a:b:c], 其中c是步长,如果是负数,就是倒着来的

>>> haibin[::]
'haibin'
>>> haibin[::1]
'haibin'
>>> haibin[::-1]
'nibiah'

所以[::-1]可以实现Reverse的效果

注意的点是:
切片和区间操作不包括区间最后一个元素(即右边界), str[: 2] 是 不包括 str[2]的
比如

>>> range(10) == range(0, 10)
True
>>> range(10)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

这个range不包括右边界10…

切片赋值
直接贴书上的例子

>>> l = list(range(10))
>>> l
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

>>> l[2:5] = [20, 30]
>>> l
[0, 1, 20, 30, 5, 6, 7, 8, 9]

>>> del l[5:7]
>>> l
[0, 1, 20, 30, 5, 8, 9]

>>> l[3::2] = [11, 22]
>>> l
[0, 1, 20, 11, 5, 22, 9]

>>> l[2:5] = 100
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: can only assign an iterable

>>> l[2:5] = [100]
>>> l
[0, 1, 100, 22, 9]

等号右边必须是可迭代对象,即便只有一个元素

  1. sort
    list.sort 对原list排序,返回None
    sorted 不改变原来的对象, 返回新list

    >>> l = [1, 2, 4, 3]
    >>> l.sort()
    >>> l
    [1, 2, 3, 4]
    
  2. array
    数组, 处理较大数据,只能存储同一类型的, 看起来就比较底层比较快
    大数据处理还会用到NumPySciPy库, 准备用到的时候再去学

  3. collections.deque

列表常常用来存一些相同类型的元素, 像是数组
而元祖更像是字段表的感觉,常存一些不同类型的

dict

py里可映射类型都是dict实现的

dict是{key: value}形式, 要求key可散列数据结构

可散列数据结构

  • 在对象生命周期里,他的散列值不变
  • 对象要实现hash()方法
  • 要有qe()方法

python里的不可变类型是 大专栏  python入门可散列数据结构, 对于tuple, 要tuple包含的所有元素都是不可变类型的时候他才算是可散列的

>>> hash((1, 2, 3))
2528502973977326415
>>> hash((1, [2, 3]))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'list'

创建dict

>>> a = dict(one=1, two=2, three=3)
>>> b = {'one': 1, 'two': 2, 'three': 3}
>>> c = dict(zip(['one', 'two', 'three'], [1, 2, 3]))
>>> d = dict([('two', 2), ('one', 1), ('three', 3)])
>>> e = dict({'three': 3, 'one': 1, 'two': 2})
>>> a == b == c == d == e
True

dict也能够推倒(ノ*・ω・)ノ,ahhh)

set

集合可以用于去重, 这操作…

>>> list(set([1,2,3,4,1,2,3]))
[1, 2, 3, 4]

另外set的元素要是可散列的
set本身是不可散列的
但…frozenset可以散列
所以set里不能包含set, 但可以包含emmmm, frozenset

集合操作

  • | 并集
  • & 交集
  • - 差集

集合字面量{1, 2, 3}, 空集是set(), 而不能写成{}, 因为{}是空的dict

集合也能推倒…

dict & set 和 散列表

dict 和 set很快, 因为他们背后是散列表
set其实大概相当于是只有key的一个dict,(或者叫做不关注其value)

我还以为里面是树实现的…C++ 里map应该是树实现的吧

树来实现的话应该会比单纯的稀疏散列表要省下很多内存…

书中也提到了python字典在内存的开销上巨大

不过散列表以空间换时间, 是不是查询比树要快

散列表查询是直接线性的复杂度, 算是O(1)…?
如果这样子的话…那真的是快…

总结一下,这个字面量

[1, 2, 3]

(1, 2, 3) #tuple
(1, ) # tuple
(1) # 就是1, 数字

{1: 1, 2: 2} # dict
{}          # dict

{1, 2, 3}    # set

字符

二进制序列类型

  • bytes 不可变
  • bytearray 可变

试验了下…这个东西对py2的支持不太好, py2还是str比较常用
字面量形式是b'somestr', 下面都来自py3

>>> bytes.fromhex('31 32 33 34')
b'1234'

处理二进制数据常用struct和memoryview

在py2里, 看起来这个bytesstr接管了

>>> type(base64.b64encode('abc'))
<type 'str'>
>>> type(base64.b64encode(b'abc'))
<type 'str'>

在py3里, 这里才出现了bytes

>>> type(base64.b64encode('abc'))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib/python3.5/base64.py", line 59, in b64encode
    encoded = binascii.b2a_base64(s)[:-1]
TypeError: a bytes-like object is required, not 'str'

>>> type(base64.b64encode(b'abc'))
<class 'bytes'>

编码 解码

编码encode: 字节序列 -> 文本字符串
解码decode: 文本字符串 -> 字节序列
emmmmmm…. 这个在py2有点迷
py3的话
encode只存在于str, 即为bytes str.encode(args)
decode只存在于bytes, 即为str bytes.decode(args)

>>> 'a'.encode('utf8')
b'a'
# encode: str -> bytes

>>> b'a'.decode('utf8')
'a'
# decode: bytes -> str

而且.这个args只能是text encode, 不能是hex之类的….
另外, 一般的比如utf-8有很多别名可以用: utf8, utf_8, utf-8, U8

现在没网..不过估计..应该是py2这一块比较混乱, 所以py3把encode, decode这一块功能单一化了
根据错误提示…应该是是放到codecs.decodecodecs.encode

先跳过这一块…这…感觉文本处理, 还是推荐使用py3的感觉

开始是用help('str')来找到encodedecode的帮助…然后才发现这样子更好

>>> print ''.encode.__doc__
S.encode([encoding[,errors]]) -> object

Encodes S using the codec registered for encoding. encoding defaults
to the default encoding. errors may be given to set a different error
handling scheme. Default is 'strict' meaning that encoding errors raise
a UnicodeEncodeError. Other possible values are 'ignore', 'replace' and
'xmlcharrefreplace' as well as any other name registered with
codecs.register_error that is able to handle UnicodeEncodeErrors.

我蠢了…

>>> help(''.encode)

这样子help得到的结果好像是就是__doc__过来的

闭包

抄书上的一个例子

def make_averager():
    series = []
    def averager(new_value):
        series.append(new_value)
        total = sum(series)
        return total/len(series)
    return averager
01-24 09:51