Чтение последовательности значений из списка в Python

У меня есть список значений и функция, которая принимает аргументы n>1. Значения в списке находятся в порядке (очевидно), и мне приходится перебирать список, чтобы проверить все возможные последовательности значений.

Приведенный ниже код работает хорошо, но мне он не кажется питоническим (мне особенно не нравится конструкция «for i in range»). Есть ли более элегантный и, возможно, эффективный способ добиться того же?

# function that takes 3 arguments
def foo(a, b, c):
    print(a, b, c)


# for this particular list (1,2,3), (2,3,4), (3,4,5)
# are triplets that should be analyzed by foo function
L = [1, 2, 3, 4, 5]
for i in range(len(L) - 2):
    foo(*L[i : i+3])

Результат:

1 2 3
2 3 4
3 4 5
Прочтите это.
user24714692 01.06.2024 13:58

Вы можете использовать рецепт sliding_window, описанный здесь, но у меня нет проблем с тем, как вы это делаете (хотя вам следует ознакомиться с Руководством по стилю PEP 8)

Booboo 01.06.2024 13:59

Почему вы хотите, чтобы это было эффективно? В каком смысле ваш реальный вариант использования больше?

no comment 01.06.2024 15:06

@nocomment Мой фактический вариант использования — список вершин геометрической формы (полилинии или многоугольника). Может быть большое количество вершин и большое количество фигур. Меня беспокоит, что мой код может быть неоптимальным из-за разделения списка (большинство значений в действительно длинном списке будут "разрезаны" 3 раза - в этом примере только число 3 "разрезано" 3 раза).

Szym 01.06.2024 16:18

Обратите внимание, что PEP 8 требует L[i : i+3], а не l[i:i + 3].

no comment 01.06.2024 19:34

@nocomment Ты уверен? Что касается пространств, PyCharm с вами не согласен. Также проверьте это: stackoverflow.com/questions/45916806/…. И почему L должно быть в верхнем регистре?

Szym 01.06.2024 20:28

Да, я уверен. Он называет ham[lower + offset:upper + offset] неправильным, а ham[lower+offset : upper+offset] правильным, а о l говорится: «В некоторых шрифтах эти символы неотличимы от цифр один [...] Если возникает соблазн использовать букву «l», используйте вместо нее букву «L». Что говорит PyCharm?

no comment 01.06.2024 20:38

Там написано: PEP 8: пробел E203 перед ':' Но я не собираюсь настаивать на том, что это программное обеспечение без ошибок. Насчет L конечно ты прав.

Szym 01.06.2024 20:43

Кажется, это известная ошибка, см. результаты Google для «PEP 8: E203».

no comment 01.06.2024 21:02
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
0
9
115
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

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

from more_itertools import windowed
# You can use sliding_window as an alternative from more_itertools import sliding_window 


def foo(a, b, c):
    print(a, b, c)

l = [1, 2, 3, 4, 5]

for values in windowed(l, 3):
    foo(*values)

Если вы не хотите использовать внешнюю библиотеку, вы все равно можете сделать свой код более питоническим, используя выражения-генераторы и функцию zip:

def foo(a, b, c):
    print(a, b, c)

l = [1, 2, 3, 4, 5]

for values in zip(l, l[1:], l[2:]):
    foo(*values)

Ваша последняя реализация с использованием zip самая быстрая.

Booboo 01.06.2024 14:34

Как вы думаете, где у вас есть выражения-генераторы?

no comment 01.06.2024 15:03

@nocomment Я имею в виду ввод zip

Parman M. Alizadeh 01.06.2024 15:08

Я не вижу никаких выражений-генераторов для ввода в zip, которые являются срезами, а zip сам возвращает итератор.

Booboo 01.06.2024 15:20

Я протестировал (timeit) решения Пармана, и результаты аналогичны моему коду. Я считаю решение zip наиболее элегантным (хотя согласен, что оно не имеет ничего общего с генераторами)

Szym 01.06.2024 16:19

@nocomment @Booboo @Szym Поправьте меня, если я ошибаюсь, в Python определено выражение генератора: «Оно похоже на понимание списка, но вместо создания списка оно создает объект-генератор, который можно перебирать для создания значения в генераторе". (l, l[1:], l[2:]) создает 3 итератора, соответствующие приведенному выше определению.

Parman M. Alizadeh 01.06.2024 19:55

Это не выражения-генераторы и не итераторы. И они совершенно не соответствуют этому определению.

no comment 01.06.2024 20:25

Я отказываюсь от своего заявления о том, что решение Parman zip по скорости аналогично оригинальному (моему). Это как в @nocomment, ответ в 2 раза быстрее. Моя ошибка заключалась в использовании оператора print(timeit.timeit('original', globals=globals(), Number=1)) с глобальным списком L вместо использования лямбда-выражений. Хотя я до сих пор не понимаю, почему использование строкового оператора во времени радикально меняет результаты.

Szym 01.06.2024 21:02

@Szym Не уверен, как именно вы измерили. Если вы покажете свой сценарий, возможно, я увижу причину.

no comment 01.06.2024 21:07

@nocomment Я временно добавил это в свой вопрос. Я почти уверен, что я чего-то не понимаю в функции timeit.

Szym 01.06.2024 21:33

@Szym Он сообщает время примерно 0,002 секунды. Насколько быстр ваш компьютер, что вы не шокированы тем, что можете выполнить 999998 итераций цикла Python 100000 раз за такое короткое время? :-) Вы не вызываете/выполняете функции. Следует использовать timeit('original()', ...) или timeit(original, ...).

no comment 01.06.2024 21:38

для Pythonic и, возможно, более эффективно:

def foo(a, b, c):
    print(a, b, c)

l = [1, 2, 3, 4, 5]

# Iterate over indices directly
for i in range(len(l) - 2):
    foo(l[i], l[i + 1], l[i + 2])

Рекомендация: Возможно, предоставьте несколько эталонных тестов, чтобы проверить обоснованность утверждения «возможно, более эффективно». Само по себе, без подтверждающих доказательств, это бесполезное утверждение.

s3dev 01.06.2024 14:42
Ответ принят как подходящий

Некоторые более эффективные решения от меня для 10^5 элементов:

  5.1 ± 0.1 ms  me_2
  5.2 ± 0.0 ms  me_1
  7.2 ± 0.1 ms  me_3
 12.8 ± 0.2 ms  Parman_2
 13.0 ± 0.4 ms  Nayem
 27.0 ± 0.4 ms  original

Python: 3.12.2 (main, May 17 2024, 19:27:45) [GCC 14.1.1 20240507]

Код:

def me_1(l):
    it = iter(l)
    a = next(it, None)
    b = next(it, None)
    for c in it:
        foo(a, b, c)
        a = b
        b = c

def me_2(l):
    it = iter(l)
    a = next(it, None)
    b = next(it, None)
    f = foo
    for c in it:
        f(a, b, c)
        a = b
        b = c

def me_3(l):
    b = iter(l)
    next(b, None)
    c = iter(l)
    next(c, None)
    next(c, None)
    deque(map(foo, l, b, c), 0)

def original(l):
    for i in range(len(l) - 2):
        foo(*l[i:i + 3])

def Parman_1(l):
    for values in windowed(l, 3):
        foo(*values)

def Parman_2(l):
    for values in zip(l, l[1:], l[2:]):
        foo(*values)

def Nayem(l):
    for i in range(len(l) - 2):
        foo(l[i], l[i + 1], l[i + 2])

funcs = [
    original,
   # Parman_1,
    Parman_2,
    Nayem,
    me_1,
    me_2,
    me_3,
]

from itertools import *
from collections import deque
from timeit import timeit
from statistics import mean, stdev
import sys
import random

# Correctness
def foo(a, b, c):
    result.append((a, b, c))
for n in range(20):
    l = [object() for _ in range(n)]
    expect = None
    for f in funcs:
        result = []
        f(l)
        if expect is None:
            expect = result
        else:
            assert result == expect

# Speed
def foo(a, b, c):
    pass
l = list(range(10**5))
times = {f: [] for f in funcs}
def stats(f):
    ts = [t * 1e3 for t in sorted(times[f])[:5]]
    return f'{mean(ts):5.1f} ± {stdev(ts):3.1f} ms '
for _ in range(25):
    random.shuffle(funcs)
    for f in funcs:
        t = timeit(lambda: f(l), number=1)
        times[f].append(t)
for f in sorted(funcs, key=stats):
    print(stats(f), f.__name__)

print('\nPython:', sys.version)

Попробуйте это онлайн!

Другие вопросы по теме