看流畅的python感觉有难度_《流畅的Python》读书总结

一,Python的数据类型

1,双下方法--- __getItem__()

说明:特殊方法是为了给python解释器调用,开发者不需要自己调用,但是可以通过实现特殊方法,来提高效率

例子:obj[key],实际上是调用obj的私有方法__getItem__();len(xx)本质上也是调用数据类型的__len__()方法

应用1:通过重写特殊方法,可以使自定义类通过 +-*/计算等等,比如__repr__,__abs__,__bool__,__add__,__mul__,__rmul__(bool(x)调用__bool__,如果没有调用的是__len__)

应用2:重写or增加双下方法。当使用自定义的类obj[key]去调用属性的时候,是不支持的,可以通过给类增加__getItem__()方法,使其支持,并自定义获取属性的逻辑;否则,只能使用getattr(self, key)

示例代码

应用3:编写信号函数:编写自己的库时,比如功能被关闭,可以通过自定义__exit__()方法触发特定的函数,此函数作为overwrite的API开放,就会触发退出信号

# 例一,获取属性方法,重写函数__getitem__

class Test():

def __init__(self):

self.a=1

self.b=2

print(self['b']) # 2 ,如果没有__getitem__方法,此处会报错

def __getitem__(self, x):

return getattr(self, x)

t = Test()

print(t['a']) # 1

print(t.a) # 1

# 例二,len(),deck[0],重写函数__len__,__getitem__

import collections

Card = collections.namedtuple('Card', ['rank', 'suit'])

class FrenchDeck:

ranks = [str(n) for n in range(2, 11)]+list('JQKA')

suits = 'spades diamonds clubs hearts'.split( )

def __init__(self):

self._cards = [Card(rank, suit) for suit in self.suits

for rank in self.ranks]

def __len__(self):

return len(self._cards)

def __getitem__(self, position):

return self._cards[position]

>>> from random import choice

>>> choice(deck)

Card(rank='3', suit='hearts')

>>> choice(deck)

Card(rank='K', suit='spades')

>>> choice(deck)

Card(rank='2', suit='clubs')

好处:统一标准,既能复用轮子,也可通过定义自定义类中的方法,方便用户使用

另外,实现了__getitem__方法之后。自定义deck类还支持切片操作,也变成了可迭代的(in运算符也会引起迭代,所以也支持in运算符,以及random.choice、reversed和sorted等等)。

注:如果x是一个内置类型的实例,那么len(x)的速度会非常快。背后的原因是CPython会直接从一个C结构体里读取对象的长度,完全不会调用任何方法。获取一个集合中元素的数量是一个很常见的操作,在str、list、memoryview等类型上,这个操作必须高效

二,数据结构(序列数据)

Python也从ABC那里继承了用统一的风格去处理序列数据这一特点,不管是哪种数据结构,字符串、列表、字节序列、数组、XML元素,抑或是数据库查询结果,它们都共用一套丰富的操作:迭代、切片、排序,还有拼接。

几种序列分类

1,容器序列与扁平序列

容器序列存放的是它们所包含的任意类型的对象的引用,而扁平序列里存放的是值而不是引用。换句话说,扁平序列其实是一段连续的内存空间

容器序列:list、tuple和collections.deque这些序列能存放不同类型的数据

扁平序列:str、bytes、bytearray、memoryview和array.array,这类序列只能容纳一种类型

2,可变序列与不可变序列

可变序列:list、bytearray、array.array、collections.deque和memoryview

不可变序列:tuple、str和bytes

eb3026a7c489

序列UML类图:sequence不可变序列,MutableSequence可变序列

从上图可以看出,可变序列MutableSequence继承自不可变序列,又实现了__setitem__,__delitem__等等修改序列的方法。通过记住这些类的共有特性,把可变与不可变序列或是容器与扁平序列的概念融会贯通

数列数据一:列表推导和生成器表达式

前者是列表list生成的快捷方式,而生成器表达式用于创建任何类型的序列。

一,列表推导

word = 'fwfsd'

# 1,循环添加

codes=[]

for code in word:

codes.append(code)

# 2,列表推导

codes = [ code for code in word ]

灵活运用列表推导,一般只建议用作生成list。

列表推导使用注意:

变量泄漏:python2.x中存在变量泄露,python3.x中,列表推导不存在变量泄露,放心使用。

列表推导、生成器表达式,以及同它们很相似的集合(set)推导和字典(dict)推导,在Python 3中都有了自己的局部作用域,就像函数似的

笛卡尔积,等排列组合生成复杂类型内容的list(注意嵌套关系)

>>> colors = ['black', 'white']

>>> sizes = ['S', 'M', 'L']

>>> tshirts = [(color, size) for color in colors for size in sizes] # 注意嵌套关系※※※

>>> tshirts

[('black', 'S'), ('black', 'M'), ('black', 'L'), ('white', 'S'),

('white', 'M'), ('white', 'L')]

与filter,map比较:

>>> symbols = '$¢£¥€¤'

>>> beyond_ascii = [ord(s) for s in symbols if ord(s) > 127]

>>> beyond_ascii

[162, 163, 165, 8364, 164]

>>> beyond_ascii = list(filter(lambda c: c > 127, map(ord, symbols)))

>>> beyond_ascii

[162, 163, 165, 8364, 164]

二,生成器表达式

列表推导可以用来初始化元组、数组或其他序列类型,但是生成器表达式是更好的选择。

生成器表达式 → 遵守了迭代器协议,可以逐个地产出元素,节省内存

生成器表达式和列表推导语法相同,仅符号由中括号变为括号

>>> symbols = '$¢£¥€¤'

>>> tuple(ord(symbol) for symbol in symbols) ➊

(36, 162, 163, 165, 8364, 164)

>>> import array

>>> array.array('I', (ord(symbol) for symbol in symbols)) ➋

array('I', [36, 162, 163, 165, 8364, 164])

# 1,如果生成器表达式是一个函数调用过程中的唯一参数,那么不需要额外再用括号把它围起来

# 2,array的构造方法需要两个参数,因此括号是必需的

三,元组

不仅仅是不可变的列表,可用于没有字段名的记录

1,元组拆包 -->> 其实是可迭代元素拆包:

元组拆包可以应用到任何可迭代对象上,唯一要求是等式两边数量一致,或者用*表示多余的元素。

a = (1,2,3)

b,c,d = a # 第一种

print('%d, %d, %d'%a) # 第二种

b,a=a,b # 不使用中间变量,交换a,b的值

function(*a) # 第三种

# *前缀只能用在一个变量名前面,但是这个变量可以出现在赋值表达式的任意位置

a, *b, c, d = range(5) # (0, [1,2], 3, 4)

2,嵌套拆包

同上拆包,只要等号左右结构相符合,既可以正确的对应赋值

3,具名元组

使用collectis.namedtuple创建具名元组,类似一个类,创建类的时候,传入类名和各个元素名称,然后实例化这个类,同时传入对应参数的值

>>> from collections import namedtuple

>>> City = namedtuple('City', 'name country population coordinates') ➊

>>> tokyo = City('Tokyo', 'JP', 36.933, (35.689722, 139.691667)) ➋

>>> tokyo

City(name='Tokyo', country='JP', population=36.933, coordinates=(35.689722,

139.691667))

>>> tokyo.population ➌

36.933

>>> tokyo.coordinates

(35.689722, 139.691667)

>>> tokyo[1]

'JP'

3,作为不可变列表--元组

除了跟增减元素相关的方法之外,元组支持列表的其他所有方法

四,切片

1,普通切片操作

l = [1,2,3,4,5]

l[:2] # [1,2]

l[3:] # [4,5]

l[::2] #[1,3,5]

l[::-1] #[5,4,3,2,1]

2,多维切片--numpy扩展

[]运算符里还可以使用以逗号分开的多个索引或者是切片,外部库NumPy里就用到了这个特性,二维的numpy.ndarray就可以用a[i, j]这种形式来获取,抑或是用a[m:n, k:l]的方式来得到二维切片.

要正确处理这种[]运算符的话,对象的特殊方法getitem和setitem需要以元组的形式来接收a[i, j]中的索引。在自定义类中实现需注意

python标准库中并不支持多维切片,因为数据都是一维的,numpy中支持多维切片。

3,切片赋值

切片可以作为左值来赋值,也可以用del()操作

注意:如果切片在左值,右值必须是个可迭代对象

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

l[2:6] = [20,30]

print(l) # [0,1,20,30,6,7,8,9]

del(l[5:7]) # [0,1,20,30,6,9]

l[2:4] = 10 # err

l[2:4] = '10123' # right

4,对序列使用+和*

+和*不修改原有对象二生成全新队列

注:用*扩展的列表中是引用对象的话,那么新生成的是多个引用,指向同一个对象

l = [1,2]

d = [3,4]

l+d # [1,2,3,4]

d+l # [3,4,1,2]

l * 3 # [1,2,1,2,1,2]

lis = [['_'] * 3 for i in range(3)] # [ ['_','_','_',], ['_','_','_',] ,['_','_','_',] ]

lis[2][1] = 6 # [ ['_','_','_',], ['_','_','_',] ,['_','6','_',] ]

lis = [['_'] * 3] * 3 # [ ['_','_','_',], ['_','_','_',] ,['_','_','_',] ]

lis[2][1] = 6 # [ ['_','6','_',], ['_','6','_',] ,['_','6','_',] ]

5,序列的增量赋值

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值