Используете itertools.product и хотите ввести значение

Поэтому я написал небольшой скрипт для загрузки изображений с веб-сайта. Он проходит через 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)

person Ryan    schedule 25.03.2012    source источник
comment
Не ответ на вашу проблему, но вы можете написать это как product(numbers, *[alnum] * 6), и вы могли бы использовать string.digits вместо numbers и string.ascii_lowercase + string.digits вместо alnum.   -  person agf    schedule 26.03.2012
comment
Хорошо.Спасибо. Все еще пытаюсь познакомиться с Python.   -  person Ryan    schedule 26.03.2012


Ответы (3)


вот решение, основанное на коде библиотеки 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')]

вам следует проверить это еще раз - возможно, я сделал глупую ошибку - но идея довольно проста, поэтому вы сможете ее исправить :о) вы можете использовать мои изменения; понятия не имею, что такое оригинальная лицензия pypy.

также state на самом деле не является полным состоянием - оно не включает исходные аргументы - это просто индекс в последовательности. возможно, было бы лучше назвать его индексом, но в коде уже есть индикации...

обновить

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

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

person andrew cooke    schedule 25.03.2012
comment
Это выглядит великолепно до сих пор. Однако я не могу понять, как работает класс. Любая попытка напечатать что-то со 103 октиллионами комбинаций требует времени ;) Теперь возникает следующая проблема вычисления индекса. - person Ryan; 26.03.2012
comment
программа работает как счетчик пробега в автомобиле. каждый вызов roll_gears перемещает конец (самый левый в данном случае, но самый правый в милеометре) номер один. если это число достигает 0, то оно перемещает следующее число вверх на единицу и т. д. и т. д. - person andrew cooke; 26.03.2012
comment
Имеет смысл. И теперь он работает!!! Пришлось добавить код для математики с основанием 36. Спасибо и спасибо agf за указание на PyPy - person Ryan; 26.03.2012
comment
Это здорово. Если вы объедините это с моим ответом, то goto может принять состояние, вычислить смещение (при условии отсутствия повторяющихся значений во входной последовательности), а затем перейти в это состояние. - person agf; 26.03.2012
comment
я только что опубликовал более простую версию (см. обновление), которую может быть легче понять/адаптировать. - person andrew cooke; 26.03.2012

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

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

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

person John La Rooy    schedule 25.03.2012
comment
Или что-то вроде реализации PyPy. быстрее, разборчивее, и вы, вероятно, даже могли бы понять, как пропустить его вперед к начальному значению гораздо быстрее, чем с dropwhile. - person agf; 26.03.2012
comment
@agf, использовать версию из pypy — отличная идея - person John La Rooy; 26.03.2012
comment
Я придумал что-то, что должно быть быстрее, чем dropwhile для itertools.product, если ни одна из входных последовательностей не имеет повторяющихся значений. - person agf; 26.03.2012
comment
Посмотрите, однако, это вне меня, как я могу на самом деле использовать это. - person Ryan; 26.03.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
person agf    schedule 25.03.2012