python:如何在固定的缓冲区类中切片/存储iter指向的数据?

2024-03-29 15:32:00 发布

您现在位置:Python中文网/ 问答频道 /正文

所有人

如您所知,通过python iter,我们可以使用下一个()以获取下一项数据。 以列表为例:

l =  [x for x in range(100)]
itl = iter(l)
itl.next()            # 0
itl.next()            # 1

现在我想用一个缓冲区来存储*一般的iter指向的数据片,用上面的列表iter演示我的问题。在

^{pr2}$

我想要的是

print itp.first()   # [0,1,2,3,4]
print itp.next()    # [5,6,7,8,9]
print itp.prev()    # [0,1,2,3,4]
len(itp)            # 20   # 100 item / 5 fixed size = 20    
print itp.last()   # [96,97,98,99,100]


for y in itp:           # iter may not support "for" and len(iter) then something alike code also needed here  
    print y
[0,1,2,3,4]
[5,6,7,8,9]
...
[96,97,98,99,100]

这不是家庭作业,但作为一个python初学者,对如何设计iter类知之甚少,有人能告诉我如何在这里编写“IterPage”类?在

另外,通过下面的答案,我发现如果我要切片的原始数据非常大,例如8G文本文件或数据库中的10^100个记录表,它可能无法将它们全部读入一个列表中—我没有那么多物理内存。以python文档中的片段为例:

http://docs.python.org/library/sqlite3.html#

>>> c = conn.cursor()
>>> c.execute('select * from stocks order by price')
>>> for row in c:
...    print row
...
(u'2006-01-05', u'BUY', u'RHAT', 100, 35.14)
(u'2006-03-28', u'BUY', u'IBM', 1000, 45.0)
(u'2006-04-06', u'SELL', u'IBM', 500, 53.0)
(u'2006-04-05', u'BUY', u'MSOFT', 1000, 72.0)

如果这里我们有大约10^100条记录,在这种情况下,它可能只存储我想要的行/记录与itp = IterPage(c, 5)?如果我调用下一个()itp只能从数据库中获取下5条记录吗?在

谢谢!在

PS:我在下面的链接中找到了一个方法: http://code.activestate.com/recipes/577196-windowing-an-iterable-with-itertools/

我还发现有人想itertools.i窗口()函数,但它刚刚被拒绝。 http://mail.python.org/pipermail/python-dev/2006-May/065304.html


Tags: 数据inhttp列表forlen记录code
1条回答
网友
1楼 · 发布于 2024-03-29 15:32:00

既然你问过设计,我就写一点你想要的——它不是迭代器。在

迭代器的定义属性是它只支持迭代,而不支持随机访问。但是像.first.last这样的方法是随机访问的,所以您需要的不是迭代器。在

当然也有容器允许这样做。它们被称为序列,其中最简单的是list。它的.first方法写成[0],它的.last是{}。在

这里有一个对象,它对给定的序列进行切片。它存储一个slice对象的列表,这是Python用来分割列表部分的内容。类必须实现为序列的方法由abstact base classSequence给出。继承它很好,因为如果您忘记实现一个必需的方法,它会抛出错误。在

from collections import Sequence

class SlicedList(Sequence):
    def __init__(self, iterable, size):
        self.seq = list(iterable)
        self.slices = [slice(i,i+size) for i in range(0,len(self.seq), size)]

    def __contains__(self, item):
        # checks if a item is in this sequence
        return item in self.seq

    def __iter__(self):
        """ iterates over all slices """
        return (self.seq[slice] for slice in self.slices)

    def __len__(self):
        """ implements len( .. ) """
        return len(self.slices)

    def __getitem__(self, n):
        # two forms of getitem ..
        if isinstance(n, slice):
            # implements sliced[a:b]
            return [self.seq[x] for x in self.slices[n]]
        else:
            # implements sliced[a]
            return self.seq[self.slices[n]]

s = SlicedList(range(100), 5)

# length
print len(s) # 20

#iteration
print list(s) # [[0, 1, 2, 3, 4], [5, 6, 7, 8, 9], ... , [95, 96, 97, 98, 99]]
# explicit iteration:
it = iter(s)
print next(it) # [0, 1, 2, 3, 4]

# we can slice it too
print s[0], s[-1] # [0, 1, 2, 3, 4] [95, 96, 97, 98, 99]
# get the first two
print s[0:2] # [[0, 1, 2, 3, 4], [5, 6, 7, 8, 9]]
# every other item
print s[::2] # [[0, 1, 2, 3, 4], [10, 11, 12, 13, 14], [20, 21, 22, 23, 24], ... ]

现在,如果您真的想要像.start这样的方法(不管怎样,[0]只是一种详细的方法),您可以编写这样的类:

^{pr2}$
网友
2楼 · 发布于 2024-03-29 15:32:00

The raw data that I want to slice is very big, for example a 8Giga text file... I may not be able to read all of them into a list - I do not have so much physical memory. In that case, is it possible only get line/records I want by this class?

不,就目前的情况来看,下面最初建议的类将迭代器转换为 列表,这使得它对你的情况100%无用。在

只需使用石斑鱼习语(下面也会提到)。 你必须聪明地记住以前的小组。 为了节省内存,只存储之前需要的组。 例如,如果只需要最近的前一个组,则可以将其存储在 一个变量,previous_group。在

如果您需要最近的5个组,可以使用最大大小为5的collections.deque。在

或者,您可以使用window习惯用法来获得n组的滑动窗口。。。在

考虑到目前为止您告诉我们的情况,我不会为此定义一个类,因为我没有看到解决方案中有许多可重用元素。在


主要地,您想要的可以通过grouper idiom来完成:

In [22]: l =  xrange(100)    
In [23]: itl=iter(l)    
In [24]: import itertools    
In [25]: for y in itertools.izip(*[itl]*5):
   ....:     print(y)
(0, 1, 2, 3, 4)
(5, 6, 7, 8, 9)
(10, 11, 12, 13, 14)
...
(95, 96, 97, 98, 99)

调用next没有问题:

^{pr2}$

但是生成一个previous方法是个大问题,因为迭代器不是这样工作的。迭代器的作用是生成值而不记住过去的值。 如果需要所有过去的值,则需要一个列表,而不是迭代器:

In [32]: l =  xrange(100)
In [33]: ll=list(itertools.izip(*[iter(l)]*5))

In [34]: ll[0]
Out[34]: (0, 1, 2, 3, 4)

In [35]: ll[1]
Out[35]: (5, 6, 7, 8, 9)

# Get the last group
In [36]: ll[-1]
Out[36]: (95, 96, 97, 98, 99)

现在获取前一组只需要跟踪列表索引。在

相关问题 更多 >