1.3 包(注意与python中的包区分)

        本节以包为例子,继续阐述抽象数据类型。

        包用于储存数据项的简单容器。具有如下性质

  • 访问其中的具体某个数据项;
  • 增删数据项;
  • 判断一个数据项是否在包内;
  • 遍历包内所有数据项。              

       1.3.1 包ADT

        包这一ADT是用于储存数据项的容器,其中的数据项是可以重复的,无特定顺序,相互之间可比较。

  • Bag():创建包实例;
  • length():包内所含数据项的个数(重复数据项也计算在内);  
  • contains(item):判断数据项item是否在包内,返回True或False;
  • add(item):向包内加入数据项item;
  • remove(item):从包内移除数据项item,若item不在包内,则引发异常;
  • iterator():返回用于遍历包内所有数据项的迭代器。     

        使用示例:

#-*-coding: utf-8-*-

from linearbag import Bag
from date import Date

# 获取用户输入的出生日期
def promptAndExtracDate():
    print "Enter a birth date."
    month = int(raw_input("month (0 to quit): "))
    if month == 0:
        return None
    else:
        day = int(raw_input("day: "))
        year = int(raw_input("year: "))
        return Date(month, day, year)

def main():
    bornBefore = Date(6, 1, 1988) 
    bag = Bag()

    date = promptAndExtracDate()
    while date is not None:
        bag.add(date)
        date = promptAndExtracDate()

    for date in bag:
        if date <= bornBefore:
            print "Is at least 21 years of age:", date

if __name__ == "__main__":
    main()

       为什么使用包ADT?而不是用python提供的列表?书上基本重复之前的那四点使用抽象数据类型的好处,这里不是很理解。

       1.3.2 选择数据结构

        选择适当的数据结构来实现包ADT。

        需满足如下要求:

  • 所选择的数据结构必须能储存ADT数据的所有可能值;
  • 所选择的数据结构必须能提供访问和操作其内所有数据项的功能;
  • 所选择的数据结构必须有利于操作的高效实现。       

        其中第三点与复杂度(complexity)相关,之后再考虑。

        显然python中的列表和字典这两种数据结构能满足要求,列表自不必表。关于字典,可以使用数据项作为键,个数作为值,来实现。增删数据项,就是个数增减而已。当个数减至零时,自动删除这一键值对即可。考虑到,同等数据项的情况下,列表占用的内存是字典占用内存的一半,而大多数数据项的个数是1,因此列表是更好的选择。而如果多数数据项的个数大于1,则字典是更好的选择。

        

        1.3.3 基于列表的实现

#-*-coding: utf-8-*-

# 使用列表实现“包”这一抽象数据类型

class Bag(object):
    # 构造一个空包
    def __init__(self):
        self._theItems = list()

    # 返回包中物品的个数
    def __len__(self):
        return len(self._theItems)

    # 判断一个物品是否在包中
    def __contains__(self, item):
        return item in self._theItems

    # 添加新物品
    def add(self, item):
        self._theItems.append(item)

    # 删除包中的一个物品,并返回该物品
    def remove(self, item):
        assert item in self._theItems, "The item must be in the bag." # 必须首先断言物品item存在包中,否则引发断言异常
        ndx = self._theItems.index(item)
        return self._theItems.pop(ndx)

    # 返回用于遍历该包的迭代器
    def __iter__(self, item):
        ......

       1.4 迭代器

        大多数容器型ADT需要提供遍历功能,有两种方法。第一种是给用户提供访问底层数据结构的方法,但这违背了抽象原则,背离了使用ADT的目的。第二种就是使用迭代器。

        迭代器是一种可遍历容器内所有数据项而无需暴露底层实现的机制。

        1.4.1 设计迭代器

        为了能使我们定义的ADT具有遍历功能,我们必须使用迭代器类,这一个类中包含__iter__和__next__两个特殊方法。

#-*-coding: utf-8-*-

# 包的迭代器

class _BagIterator(object):
    def __init__(self, theList):
        self._bagItems = theList
        self._curItem = 0

    def __iter__(self):
        return self

    def __next__(self):
        if self._curItem < len(self._bagItems):
            item = self._BagItems[self._curItem]
            self._curItem += 1
            return item
        else:
            raise StopIteration

        __next__方法是返回迭代器中的下一个数据项。这一方法首先保存对当前数据项的引用,当循环变量增加一时,__next__方法便返回下一数据项,如果后面为空,则引发StopIteraton异常。(有点不太理解)

        __iter__方法是负责创建并返回迭代器类的实例,在for循环中会自动调用。(不知它与这里的__init__在作用上有什么区别)

       

       1.4.2 使用迭代器

        在执行for循环命令时

>>> for item in bag:
...     print item

        python会自动调用__iter__方法,创建迭代器对象,此时迭代器状态如下图所示:




python 构造二层数据包 进行收发_python 构造二层数据包 进行收发



        for循环会自动调用__next__方法来访问下一个数据项,迭代器随curItem增加而变化,直到遍历完所有数据项,引发StopIteration异常为止。for循环相当于如下过程:

# Create a BagIterator object for myBag.
iterator = myBag.__iter__()
# Repeat the while loop until break is called.
while True :
    try:
    # Get the next item from the bag. If there are no
    # more items, the StopIteration exception is raised.
    item = iterator.__next__()
    # Perform the body of the for loop.
    print item
    # Catch the exception and break from the loop when we are done.
    except StopIteration:
    break