Как использовать возвращаемое значение функции как условие while, возвращающее кортеж в python

Я искал что-то вроде этого, но я не мог найти его здесь.

Некоторые фон

Я использую opencv для извлечения кадров из видеофайла. Обычно люди делают это в бесконечном цикле, например:

while (True):
    s, img = cv.read()

или

for i in xrange(10000): #just a big number
    s, img = cv.read()

теперь я хочу получить все кадры и выйти из цикла, когда больше нет кадров. Однако мои навыки в python недостаточно сильны, чтобы делать то, что я хочу делать.

Что я хочу знать

read функция (или метод, я не знаю, как они вызывается в python) возвращает кортеж: сначала представляет успех операции, а второй представляет возвращенный фрейм. Я хочу разбить цикл while, когда первый элемент кортежа является ложным. Имея фон C, я подумал, что, возможно, это сработает:

while ((success, img = capture.read())[0]):
    #do sth with img

Я думал, что это сломает цикл, когда успех будет ложным. Но это не так. Тогда я подумал, что это сработает:

while ((success, img = capture.read()).success):
    #do sth with img

он тоже не работал. Я не хочу делать что-то вроде

while(True):
    s, i = capture.read()
    if (s == False):
        break

Как проверить условие в while, а не на if, которое прерывается, если оно успешное?

Ответ 1

лучший способ думать, что pythonic - забыть другие языки

s = True
while s:
    s, i = capture.read()
    if s:
        do_some_stuff(i)

Ответ 2

Вы можете написать генераторную функцию.

def readframes(cv):
    while True:
        success, frame = cv.read()
        if success:
            yield frame
        else:
            return

Таким образом, вы можете прокручивать кадры с помощью цикла for.

for frame in readframes(cv):
    do_something_with_frame(frame)

Ответ 3

Python имеет альтернативную iter подпись, которая берет функцию в качестве первого аргумента и дозорного, определяя условие останова как второе.

Используя его, вы можете найти что-то вроде этого:

for s,img in iter(cv.read, (False, None)):
    print img

Тем не менее, я сомневаюсь, что это лучше, чем просто break в блоке if.

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

>>> for s,img in itertools.takewhile(lambda x: x[0], iter(cv.read, None)):
    print img

Он использует itertools.takewhile, чтобы определить, когда первое значение возвращаемого набора равно False.


Полная версия для тестирования:

>>> class Capture(object):
    def __init__(self):
        self.L = iter([1,2,3,4,5])
    def read(self):
        try:
            img = self.L.next()
        except StopIteration:
            return (False,None)
        return True, img

>>> cv = Capture()
>>> for s,img in iter(cv.read, (False, None)):
    print img

1
2
3
4
5

>>> cv = Capture()
>>> for s,img in itertools.takewhile(lambda x: x[0], iter(cv.read, None)):
    print img


1
2
3
4
5

Ответ 4

Это должно работать

while capture.read()[0]: 
    #do something

Конечно, вы не сможете получить доступ к кадру в этом направлении!

Есть другой способ

s,v = capture.read()
while s:
    #Do sth
    s,v = capture.read()

Это, конечно, длинный способ сказать

while True: 
    s,v = capture.read()
    if not s: 
        break

Какой (по какой-то причине) вы не хотите делать

Ответ 5

вы не можете делать то, что вы задаете в python (если только не есть хаки, о которых я не знаю).

какая строка:

s, i = capture.read()

делает, это назначение двух отдельных переменных путем распаковки кортежа, просто невозможно сказать python, что вы хотите, чтобы первая назначенная переменная.
В python также запрещено использовать в условном выражении if, for или while.

единственное, что вы могли бы сделать:

if(capture.read()[0]):

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

Ответ 6

Вот решение, для которого нужно разбить цикл, используя функцию, возвращающую кортеж.

>>> def read(a):
...     if a == 0:
...         return (False, a+1)
...     return (True, a-1)
... 
>>> read(5)
(True, 4)
>>> read(0)
(False, 1)
>>> a = True
>>> i = 5
>>> while(a):
...     a, i = read(i)
...     if a:
...         print 'working'
...         
...     
... 
working
working
working
working
working
>>> 

Теперь ваш код будет выглядеть так:

s = True
while(s):
    s, i = capture.read()