Используя itertools.product и хотите получить значение - PullRequest
6 голосов
/ 26 марта 2012

Итак, я написал небольшой скрипт для загрузки картинок с веб-сайта. Он проходит через значение 7-альфа-характера, где первый символ всегда является числом. Проблема в том, что если я хочу остановить скрипт и запустить его снова, мне нужно начинать все сначала.

Могу ли я как-нибудь посеять файл 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)

Ответы [ 3 ]

3 голосов
/ 26 марта 2012

вот решение, основанное на коде библиотеки 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 на самом деле не полное состояние - оно не включает исходные аргументы - это просто индекс в последовательности.возможно, было бы лучше назвать его индексным, но в коде уже есть указания [sic] ...

update

вот более простая версия, котораята же идея, но работает путем преобразования последовательности чисел.так что вы просто imap превысили count(n), чтобы получить смещение последовательности на n.

> cat prod2.py 

from itertools import count, imap

def make_product(*values):
    def fold((n, l), v):
        (n, m) = divmod(n, len(v))
        return (n, l + [v[m]])
    def product(n):
        (n, l) = reduce(fold, values, (n, []))
        if n > 0: raise StopIteration
        return tuple(l)
    return product

print list(imap(make_product(['a','b','c'], [1,2,3]), count()))
print list(imap(make_product(['a','b','c'], [1,2,3]), count(3)))

def product_from(n, *values):
    return imap(make_product(*values), count(n))

print list(product_from(4, ['a','b','c'], [1,2,3]))

> python prod2.py 
[('a', 1), ('b', 1), ('c', 1), ('a', 2), ('b', 2), ('c', 2), ('a', 3), ('b', 3), ('c', 3)]
[('a', 2), ('b', 2), ('c', 2), ('a', 3), ('b', 3), ('c', 3)]
[('b', 2), ('c', 2), ('a', 3), ('b', 3), ('c', 3)]

(недостатком здесь является то, что если вы хотите остановиться и перезапустить, вам нужно следить за собойсколько вы использовали)

2 голосов
/ 26 марта 2012

Как только вы доберетесь до итератора, потребуется некоторое время, чтобы добраться до места с помощью dropwise.

Вам, вероятно, следует адаптировать рецепт, например this , чтобы можно было сохранять состояние с засолкой между циклами.

Убедитесь, что ваш сценарий может выполняться только один раз, или вам понадобится что-то более сложное, например, серверный процесс, который передает идентификаторы сценариям

1 голос
/ 26 марта 2012

Если ваши входные последовательности не имеют повторяющихся значений, это может быть быстрее, чем dropwhile для продвижения product, поскольку не требуется сравнивать все отброшенные значения, вычисляя правильную точку для возобновления итерации. .

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
...