使用itertools.product想播种一个价值观

2024-09-30 08:28:56 发布

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

所以我写了一个小脚本从一个网站下载图片。它通过一个7个alpha字符值,其中第一个字符总是一个数字。问题是如果我想停止脚本并重新启动它,我必须从头开始。在

我能播种吗itertools.product不知怎么的,我得到了最后一个值,所以我不必再重复一遍了。在

谢谢你的意见。在

以下是部分代码:

numbers = '0123456789'
alnum = numbers + 'abcdefghijklmnopqrstuvwxyz'

len7 = itertools.product(numbers, alnum, alnum, alnum, alnum, alnum, alnum) # length 7

for p in itertools.chain(len7):
    currentid = ''.join(p) 

    #semi static vars
    url = 'http://mysite.com/images/'
    url += currentid

    #Need to get the real url cause the redirect
    print "Trying " + url
    req = urllib2.Request(url)
    res = openaurl(req)
    if res == "continue": continue
    finalurl = res.geturl()

    #ok we have the full url now time to if it is real
    try: file = urllib2.urlopen(finalurl)
    except urllib2.HTTPError, e:
        print e.code

    im = cStringIO.StringIO(file.read())
    img = Image.open(im)
    writeimage(img)

Tags: theto脚本urlresurllib2product字符
3条回答

一旦你对迭代器有了一个合理的方法,使用dropwhile将需要一段时间才能到达目的地。在

您可能应该调整一个像this这样的配方,这样就可以在运行之间用pickle保存状态。在

请确保您的脚本一次只能运行一次,否则您将需要一些更复杂的东西,例如将id分发给脚本的服务器进程

这里有一个基于pypy库代码的解决方案(感谢agf在评论中的建议)。在

状态通过.state属性可用,并且可以通过.goto(state)重置,其中state是序列的索引(从0开始)。最后有一个演示(恐怕你需要向下滚动)。在

这比丢弃值快得多。在

> cat prod.py 

class product(object):

    def __init__(self, *args, **kw):
        if len(kw) > 1:
            raise TypeError("product() takes at most 1 argument (%d given)" %
                             len(kw))
        self.repeat = kw.get('repeat', 1)
        self.gears = [x for x in args] * self.repeat
        self.num_gears = len(self.gears)
        self.reset()

    def reset(self):
        # initialization of indicies to loop over
        self.indicies = [(0, len(self.gears[x]))
                         for x in range(0, self.num_gears)]
        self.cont = True
        self.state = 0

    def goto(self, n):
        self.reset()
        self.state = n
        x = self.num_gears
        while n > 0 and x > 0:
            x -= 1
            n, m = divmod(n, len(self.gears[x]))
            self.indicies[x] = (m, self.indicies[x][1])
        if n > 0:
            self.reset()
            raise ValueError("state exceeded")

    def roll_gears(self):
        # Starting from the end of the gear indicies work to the front
        # incrementing the gear until the limit is reached. When the limit
        # is reached carry operation to the next gear
        self.state += 1
        should_carry = True
        for n in range(0, self.num_gears):
            nth_gear = self.num_gears - n - 1
            if should_carry:
                count, lim = self.indicies[nth_gear]
                count += 1
                if count == lim and nth_gear == 0:
                    self.cont = False
                if count == lim:
                    should_carry = True
                    count = 0
                else:
                    should_carry = False
                self.indicies[nth_gear] = (count, lim)
            else:
                break

    def __iter__(self):
        return self

    def next(self):
        if not self.cont:
            raise StopIteration
        l = []
        for x in range(0, self.num_gears):
            index, limit = self.indicies[x]
            l.append(self.gears[x][index])
        self.roll_gears()
        return tuple(l)

p = product('abc', '12')
print list(p)
p.reset()
print list(p)
p.goto(2)
print list(p)
p.goto(4)
print list(p)
> python prod.py 
[('a', '1'), ('a', '2'), ('b', '1'), ('b', '2'), ('c', '1'), ('c', '2')]
[('a', '1'), ('a', '2'), ('b', '1'), ('b', '2'), ('c', '1'), ('c', '2')]
[('b', '1'), ('b', '2'), ('c', '1'), ('c', '2')]
[('c', '1'), ('c', '2')]

你应该对它进行更多的测试——我可能犯了一个愚蠢的错误——但是这个想法很简单,所以你应该能够修复它:o)你可以自由使用我的更改;不知道原始的pypy许可是什么。在

而且state不是真正的完整状态-它不包括原始参数-它只是序列的一个索引。也许把它称为索引会更好,但代码中已经有了标记。。。在

更新

这里有一个更简单的版本,它的想法是一样的,但是通过转换一系列的数字来工作。所以你只要imapcount(n)上,就可以得到n的序列偏移量。在

^{pr2}$

(这里的缺点是,如果您想停止并重新启动,您需要跟踪自己已经使用了多少次)

如果您的输入序列没有任何重复的值,这可能比dropwhile前进{}快,因为它不需要您通过计算正确的点来比较所有被删除的值来继续迭代。在

from itertools import product, islice
from operator import mul

def resume_product(state, *sequences):
    start = 0
    seqlens = map(len, sequences)
    if any(len(set(seq)) != seqlen for seq, seqlen in zip(sequences, seqlens)):
        raise ValueError("One of your sequences contains duplicate values")
    current = end = reduce(mul, seqlens)
    for i, seq, seqlen in zip(state, sequences, seqlens):
        current /= seqlen
        start += seq.index(i) * current
    return islice(product(*sequences), start + 1, end)


seqs = '01', '23', '45', '678'        

# if I want to resume after '1247':
for i in resume_product('1247', *seqs):
    # blah blah
    pass

相关问题 更多 >

    热门问题