第10章 序列的修改, 散列和切片

坚持就是胜利….

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 切片实现的原理

  1. slice 是内置的类型(2.4.2 节首次出现)。
  2. 通过审查slice,发现它有start、stop 和step 数据属性,以及indices 方法
    内置类型–slice
    说明:
  3. 函数实际上是一个切片类的构造函数,返回一个切片对象。

  4. 切片对象由3个属性start、stop、step组成,start和step默认值为None。切片对象主要用于对序列对象进行切片取对应元素。

  5. :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]
  1. 对应切片对象的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]

动态的存取属性

  1. 特殊属性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
  1. setattr的使用

  2. 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 自动的对索引变量进行处理的操作

本章的小结

小结中提到了使用

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值