LINUX.ORG.RU

[python] Можно ли у исключения получить объект, который его создал?


0

0

Есть примерно такой код:

try:
    while True:
        i = iter1.next()
        j = iter2.next()
        # some logic
except StopIteration as e:
     # Тут бы хотелось узнать какой итератор вызвал это исключение
Пока пробую просто вызывать next для всех возможных итераторов (в данном случае iter1 и iter2) и смотреть кто из них закончился (опять выбросит StopIteration), но мне этот подход кажется очень кривым. Возможно ли из исключения (е) узнать кто стал причиной остановки?

Ты прав, подход кривой. Не надо проверять наличие элементов ловлей эксепшенов :)

dizza ★★★★★ ()
Ответ на: комментарий от elverion

Поясни? Эксепшен не должен использоваться в штатных ситуациях. Исключений для этого правила я не знаю.

ТС: озвучь плиз задачу и мы ее попробуем решить.

dizza ★★★★★ ()
Ответ на: комментарий от dizza

> Эксепшен не должен использоваться в штатных ситуациях. Исключений для этого правила я не знаю.

StopIteration.

Но ТС хочет странного, это факт.

tailgunner ★★★★★ ()
Ответ на: комментарий от elverion

python-way — использовать исключения для прерывания цикла? o_O

geekless ★★ ()
Ответ на: комментарий от dizza

> Я знаю про StopIteration. И?

Ты просил исключение из правила - я его тебе привел. Что не так?

Нужно его использовать во все поля?

Не понимаю, почему ты задаешь этот вопрос мне. Для протокола: нет, StopIteration не надо использовать во всех полях.

tailgunner ★★★★★ ()
Ответ на: комментарий от dizza

Есть куча итераторов (пока два, но интересно вообще общий путь). Они потенциально очень большие. Этот цикл - часть генератора. Получается есть генератор, в который передаются итераторы, а генератор возвращает элементы из разных итераторов на основании условия (у меня просто сравнение <=).

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

Впрочем, у меня есть подозрение, что вообще не туда иду.

AlexKiriukha ★★★★ ()

Есть несколько методов. Можно через задницу:

i=j=k=None #лучше не использовать None т.к. итератор тогда не сможет его возвращать
iter=(x for x in range(2))
try:
    i = iter.next() 
    j = iter.next()
    k = iter.next()
except StopIteration as e:
    if i is None: print "i"
    elif j is None: print "j"
    else: print "k"

Способ 2: наверняка можно посмотреть в стэке, но это уже совсем некрасиво.

Но лучше переделать код.

true_admin ★★★★★ ()

ещё одна альтернатива: нужно занумеровать все итераторы и запоминать у какого из них вызывается next(). Что-то типа такой обёртки

class wrap(object):
    def __init__(s):
        s.__iters = []
        s.__cur = 0

    def add(s, it):
        s.__iters.append(it)

    def __iter__(s): return s

    def last(s): return s.__iters[s.__cur]

    def next(s):
        r = s.__iters[s.__cur].next()

        s.__cur = (s.__cur + 1) % len(s.__iters)

        return r 

mashina ★★★★★ ()
Ответ на: комментарий от AlexKiriukha

Такое сойдет?

def merge(*iterables):

    def safe_next(it):
        try:
            return it.next()
        except StopIteration:
            return None

    def head_tail(it):
        return (safe_next(it), it)

    iterators = map(iter, iterables)
    head_tails = map(head_tail, iterators)

    while True:
        head_tails = filter(lambda ht: ht[0], head_tails)
        if len(head_tails) == 0:
            break
        head_tails = sorted(head_tails, key=lambda ht: ht[0])
        yield head_tails[0][0]
        head_tails[0] = head_tail(head_tails[0][1])


print [x for x in merge([1,2,3], [4,5,15], [2,6,10,12])]
dizza ★★★★★ ()
Ответ на: комментарий от AlexKiriukha

Есть куча итераторов (пока два, но интересно вообще общий путь). Они потенциально очень большие. Этот цикл - часть генератора. Получается есть генератор, в который передаются итераторы, а генератор возвращает элементы из разных итераторов на основании условия (у меня просто сравнение <=).

Очень большие это насколько большие? И что за элементы? Если в память суммарно все влезает и элементы простые (числа, строки) то ИМНО гораздо быстрее будет слить несколько списков в один и отсортировать.

Если элементы (алгоритм сравнения) сложный и хочется все ручками чесать... заводим список пар (последнее возвращенное значение,итератор), на каждой итерации сортируем список, берем значение из первой пары, у итератора try-except берем след значение и пару суем назад в список. Если был эксепшн - не суем (море кончилось). Крутим пока список пар не пуст, как пуст выкидываем эксешепн

AIv ★★★★★ ()
Ответ на: комментарий от AIv
class myiterator :
   def __init__( self, *L ) : self.L = [ (i.next(),i) for i in L if i ]
   def next( self ) : 
      self.L.sort()
      r, i = self.L.pop(0)
      try: self.L.append(( i.next(), i ))
      except StopIteration, e : pass
      return r

как то так на самом деле. Эксешепн можно и не кидать - pop его сам выкинет, циклы IndexError понимают.

AIv ★★★★★ ()
iter1 = enumerate(xrange(5))
iter2 = enumerate(xrange(8))

try:
    while True:
        ni, i = iter1.next()
        nj, j = iter2.next()
except StopIteration, e:
    print min((ni, iter1), (nj, iter2))[1]
anonymous ()

замечание интересное — рантайм знает адрес кода, откуда выскочило исключение, но ни за что его не скажет в пригодной для использования форме

З.Ы. я бы накатал что-то вроде i,j,k = my_next([ iter1, iter2, iter3 ])

www_linux_org_ru ★★★★★ ()
Ответ на: комментарий от baverman

Сейчас пытаюсь реализовать. В общих чертах - делаю pop из списка итераторов, потом делаю next, если нет StopIteration, то получаю значение, а сам итератор добавляю в конец списка.

AlexKiriukha ★★★★ ()
Ответ на: комментарий от dizza

Ещё не знаю. Просто есть ощущение простого решения, но нащупать его пока не удалось.

AlexKiriukha ★★★★ ()
Ответ на: комментарий от AlexKiriukha

В общих чертах - делаю pop из списка итераторов, потом делаю next, если нет StopIteration, то получаю значение, а сам итератор добавляю в конец списка.

У тебя, видимо, уникальный дар — делать самую черезопную реализацию. Интересно глянуть на пузырьковую сортировку или факториал в твоем исполнении.

baverman ★★★ ()
Ответ на: комментарий от AIv

Это экземпляры django.models. Итераторы потому, что количество элементов может быть потенциально большим и тянуть их все в память может быть затратным. А по поводу списка пар - интересно, как-то не подумал, спасибо.

AlexKiriukha ★★★★ ()
Ответ на: комментарий от AlexKiriukha

Затратным в каком смысле?

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

Огромное количество мифов бродят в головах по поводу производительности, что может быть проще - взять и проверить?

AIv ★★★★★ ()
Ответ на: комментарий от AIv

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

AlexKiriukha ★★★★ ()

Решил влоб и создать свой итератор который будет выбрасывать специально обученное исключение:

class MyStopIteration(StopIteration):
    def __init__(self, iterator, *args, **kwargs):
        self.iterator = iterator
        super(MyStopIteration, self).__init__(*args, **kwargs)

class MyIterator(iter):
    def next(self):
        try:
            return super(MyIterator, self).next()
        except StopIteration:
            raise MyStopIteration(self)
После этого достаточно завернуть в MyIterator последовательность, другой итератор или django QuerySet.

AlexKiriukha ★★★★ ()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.