坚持就是胜利….
reprlib 内置模块的使用
我使用reprlib.repr 的方式需要做些说明。这个函数用于生成大型结构或递归结构的安
全表示形式,它会限制输出字符串的长度,用’…’ 表示截断的部分
10.3 协议和鸭子类型
在面向对象编程中,协议是非正式的接口,只在文档中定义,在代码中不定义。例如,
Python 的序列协议只需要__len__ 当自定义类的时使用len()方法的时候, 就会调用这个函数
和__getitem__ 当使用对象来获取属性的时候,就会调用
两个方法
1. 什么时间__getitem__
内置的函数才会发生作用?
凡是在类中定义了这个getitem 方法,那么它的实例对象(假定为p),可以像这样
p[key] 取值,当实例对象做p[key] 运算时,会调用类中的方法getitem。
一般如果想使用索引访问元素时,就可以在类中定义这个方法(getitem(self, key) )。
class Demo:
def __init__(self, name):
self.name = name
def __getitem__(self, key):
<!--return "hehee"-->
return self.__dict__.get(key, "hehe") # 通过__dict__ 来获取到对象的所有的属性, 然后通过字典取值的形式, 返回字典中的值
demo = Demo("xixi")
demo['name'] # 当在自定义的对象中使用demo[key]这样的方式来获取到值得时候, 才会去调用类中自定义的__getitem__ 方法, 这个时候, 返回的就是内置方法返回的数据
通过__getitem__
方法和`len方法可以让类的取值方式, 更像是一个序列的取值方式,把类的实现方式说成一种序列,是因为这个类的行为更像是一种序列
可切片的序列
实现__getitem__
和 __len__
来实现自定类的序列化的操作
from array import array
import reprlib
import math
class Vertor:
typecode = 'd'
def __init__(self, components):
self._components = array(self.typecode, components) # 定义一个受保护的示例属性
def __iter__(self):
"""构建一个迭代器"""
return iter(self._components)
def __repr__(self):
"""
使用reprlib.repr 的方式需要做些说明。这个函数用于生成大型结构或递归结构的安
全表示形式,它会限制输出字符串的长度,用'...' 表示截断的部分
"""
components = reprlib.repr(self._components) # 这个函数代表的是获取有限长度的表示形式, 多余的就用...来代替
components = components[components.find('['):-1]
return "Vector({})".format(components)
def __str__(self):
return str(tuple(self))
def __bytes__(self):
return (bytes[ord(self.typecode)]) + bytes(self._components)
def __eq__(self, other):
return tuple(self) == tuple(other)
def __abs__(self):
return math.sqrt(x*x for x in self)
def __bool__(self):
return bool(abs(self))
def __len__(self):
"""返回序列的索引"""
return len(self._components)
def __getitem__(self, index):
"""用来实现序列的切片的操作"""
return self._components[index]
@classmethod
def frombytes(cls, octets):
typecode = chr(octets[0])
memv = memoryview(octets[1:]).cast(typecode)
return cls(memv)
v1 = Vertor([1,2,3,4,5])
len(v1) # 调用__len__
v1[2] # 调用__getitem__
10.4.1 切片实现的原理
- slice 是内置的类型(2.4.2 节首次出现)。
- 通过审查slice,发现它有start、stop 和step 数据属性,以及indices 方法
内置类型–slice
说明: 函数实际上是一个切片类的构造函数,返回一个切片对象。
切片对象由3个属性start、stop、step组成,start和step默认值为None。切片对象主要用于对序列对象进行切片取对应元素。
- :slice(a, b, c)。 对seq[start:stop:step] 进行求值的时候,Python 会调用seq.
getitem(slice(start, stop, step))。就算你还不会自定义序列类型
>>> help(slice)
class slice(object)
| slice(stop)
| slice(start, stop[, step])
|
| Create a slice object. This is used for extended slicing (e.g. a[0:10:2]).
|
| Methods defined here:
|
| ...#省略#
| ----------------------------------------------------------------------
| Data descriptors defined here:
|
| start
|
| step
|
| stop
|
| ----------------------------------------------------------------------
| Data and other attributes defined here:
|
| __hash__ = None
普通切片的实现:
>>> a = list(range(10))
>>> a
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> a[None:5:None] # start step显式为None
[0, 1, 2, 3, 4]
>>> a[:5:] # start step默认为None
[0, 1, 2, 3, 4]
>>> a[2:5:None] # step显式为None
[2, 3, 4]
>>> a[2:5:] # step默认为None
[2, 3, 4]
>>> a[1:10:3]
[1, 4, 7]
- 对应切片对象的3个属性start、stop、step,slice函数也有3个对应的参数start、stop、step,其值分别会付给切片对象的start、stop、step
>>> c1 = slice(5) # 定义c1
>>> c1
slice(None, 5, None)
>>> c2 = slice(2,5) # 定义c2
>>> c2
slice(2, 5, None)
>>> c3 = slice(1,10,3) # 定义c3
>>> c3
slice(1, 10, 3)
>>> a[c1] # 和a[:5:]结果相同
[0, 1, 2, 3, 4]
>>> a[c2] # 和a[2:5:]结果相同
[2, 3, 4]
>>> a[c3] # 和a[1:10:3]结果相同
[1, 4, 7]
动态的存取属性
- 特殊属性getattr的使用
就是说当使用对象, 调用了, 在对象中不存在的属性, 那么就会去找getattr中定义的属性, 这个时候, 才会走这个自定义的方法来完成属性的查询
对my_obj.x 表达式,Python
会检查my_obj 实例有没有名为x 的属性;如果没有,到类(my_obj.class)中查找;如果
还没有,顺着继承树继续查找。4 如果依旧找不到,调用my_obj 所属类中定义的getattr
方法
getattr 的运作方式导致的:仅当对象没有指定名称的
属性时,Python 才会调用那个方法,这是一种后备机制
class Demo:
def __init__(self,x, y):
self.x = x
self.y = y
def __getattr__(self, key):
print("hehhehe")
return self.__dict__.get(key)
demo = Demo(3,4)
demo.x # 已经定义的属性
Out[54]: 3
demo.y # 已经定义的属性
Out[55]: 4
demo.z # 没有在类中定义的属性
hehhehe
demo.u # 没有在类中定义的属性
hehhehe
setattr的使用
get的使用
当获取属性的时候调用这个方法
4. set的使用
当给属性赋值的时候, 调用这个方法
5. del的使用
当使用del() 删除一个属性的时候, 就会调用这个方法
什么是归约映射
映射归约:把函数应用到各个元素上,生成一个新序列(映射,map),然后计算聚合
值
出色的zip函数
使用for 循环迭代元素不用处理索引变量,还能避免很多缺陷,但是需要一些特殊的
实用函数协助。其中一个是内置的zip 函数。使用zip 函数能轻松地并行迭代两个或
更多可迭代对象,它返回的元组可以拆包成变量,分别对应各个并行输入中的一个元
素
zip函数的时候, 会采用元素最小的那个可迭代的对象作为, 基准, 会把长度最长的那个多余的给剪切掉
>>> zip(range(3), 'ABC') # zip返回的是一个生成器, 按照需要生成的是一个元组
<zip object at 0x10063ae48>
>>> list(zip(range(3), 'ABC')) # 把生成的元组构建成一个列表, 通常的回去进行迭代列表的操作
[(0, 'A'), (1, 'B'), (2, 'C')]
>>> list(zip(range(3), 'ABC', [0.0, 1.1, 2.2, 3.3])) #
[(0, 'A', 0.0), (1, 'B', 1.1), (2, 'C', 2.2)]
>>> from itertools import zip_longest #
>>> list(zip_longest(range(3), 'ABC', [0.0, 1.1, 2.2, 3.3], fillvalue=-1)) # 当zip作用的迭代的对象的长度不同的时候, 使用可选的fillvalue(默认值为None)填充确实的值, 这样就可以继续的产生元素, 知道最长的迭代的列表完成后, 才会停止
[(0, 'A', 0.0), (1, 'B', 1.1), (2, 'C', 2.2), (-1, -1, 3.3)]
使用enumerate 自动的对索引变量进行处理的操作
本章的小结
小结中提到了使用