• 《流畅的Python》 Sequence Hacking, Hashing and Slicing(没完成)


    序列修改,散列和切片

    •  基本序列协议:Basic sequence protocol: __len__ and __getitem__

    本章通过代码讨论一个概念: 把protocol当成一个正式接口。协议概念和鸭子类型的关系。当创建自定义类型时,它的实际影响。


    Vector类,一个自定义的序列类型

    我们的实现Vector的策略是使用composition(组合),而不是继承。


    10.3 序列和鸭子类型

    协议是非正式的接口,只在文档内定义,在代码中不定义。

    例如,序列协议在Python只需要__len__, __getitem__方法。任何类只要实现了这2个方法,它的实例就能当序列用。

    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]
    >>> import linshi
    >>> d = linshi.FrenchDeck()
    >>> d
    <linshi.FrenchDeck object at 0x1041796d0>
    >>> len(d)
    52
    >>> d[0]
    Card(rank='2', suit='spades')

    因为在FrenchDeck类中定义了序列协议的2个方法。所以它的实例就能使用序列类型的2个方法。

    即使FrenchDeck类是Object的子类,但因为实现了序列协议,就可以把它当成一个序列类型。

    这就是鸭子类型。拥有鸭子的行为,那么就把它当成鸭子。

    protocols是非正式的非强制的,所以可以只实现一个协议的部分。

    10.4可切片的序列

    __getitem__实现了切片功能:

    >>> d[0:2]
    [Card(rank='2', suit='spades'), Card(rank='3', suit='spades')]

    ⚠️

    但是,返回的是一个list,而不是FrenchDeck实例。因此就不能使用FrenchDeck的其他实例方法。

    再考虑到那些内置序列类型,切片后返回的都是一个新的原本类型的实例,而不是其他类型。

    所以,基于以上2点考虑,有时,根据需要,我们需要优化__getitem__方法中的代码,让返回值是原类的实例。

    10.4.1 How Slicing Works

    这是书中一个例子:

    >>> class MySeq:
    ...     def __getitem__(self, index):
    ...         return index
    ...
    >>> s = MySeq()
    >>> s
    <__main__.MySeq object at 0x104ea16d0>
    >>> s[1]
    1
    >>> s[2]
    2
    >>> s[1:4]
    slice(1, 4, None)
    >>> s[1:4:1]
    slice(1, 4, 1)
    >>> s[1:4:2]
    slice(1, 4, 2)
    >>> s[1:4:2, 9]
    (slice(1, 4, 2), 9)
    >>> s[1:4:2, 7:9]
    (slice(1, 4, 2), slice(7, 9, None))

    本例子使用__getitem__直接返回传给它的值。

    s[1:2]使用切片返回的是一个切片对象。因此s[1:2]传递给__getitem__的就是一个切片对象。

    而,如果[]中有逗号,则返回的是一个tuple。即__getitem__接受到的是一个元祖。元祖可以包括多个切片。

    再看一下slice类本身, 如何处理切片?

    >>> slice
    <class 'slice'>
     # 为了省事,截取最后的属性
    >>> dir(slice)[-4:]
    ['indices', 'start', 'step', 'stop']

    这里有一个属性indices,  其实是index的复数型。

    使用: help(slice.indices)可以得到相关解释。

    indices(...)
        S.indices(len) -> (start, stop, stride)

    S代表一个slice对象。参数len代表原来要被切片的对象的长度。这是个内部方法。

    被切片的对象,如:

    "abcde"[:10:2], 但"abcde"的长度只有5,索引end = 10,超出了"abcde"的长度范围。因此会内部调用indices,处理超出边界的索引。

    因此,"abcde"[:10:2]就会被内部处理变成"abcde"[0:5:2]。

    >>> "abcde"[0:10:2]
    1.   Python内部处理时,会生成一个slice(0, 10, 2),
    2. 然后使用slice(0, 10, 2).indices(len("abcde"))得到(0,5,2),即start, end ,stride三个slice属性。
    3.   最后调用"abcde"[0:5:2]得到切片的字符串"ace"

    小结:

    my_seq[a:b:c]背后的工作原理,就是创建slice(a, b, c)对象,然后交给__getitem__方法进行后续处理。返回符合Python风格的自定义类的实例。


    后面的章节未阅读。

  • 相关阅读:
    KVM环境搭建RHCS
    修改virt-manager默认磁盘格式
    前端基础之html
    并发编程
    网络编程
    常用模块
    模块与包
    迭代器、生成器、面向过程编程
    闭包函数和装饰器
    函数
  • 原文地址:https://www.cnblogs.com/chentianwei/p/12051345.html
Copyright © 2020-2023  润新知