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

#python

#python

Вопрос:

Я создаю бота Facebook Messenger, который возвращает время прибытия автобусов на указанную остановку. Пока бот работает нормально, но Facebook не разрешает, чтобы сообщения, отправляемые ботами, содержали более 320 символов. Часто бот может отображать только первые 5 или около того, не превышая этот предел, что недостаточно хорошо при очень загруженных остановках.

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

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

 if len(info["results"]) > 5:
    while i < 5:
        n.append("Route:"   " "   str(info['results'][i]['route'])   " "   "to"   " "   str(info['results'][i]['destination'])   "n"   "Due:"   " "   str(info["results"][i]["duetime"])   " "   "minutes."   "n")
        i = i   1
else:
    while i < len(info["results"]):
        n.append("Route:"   " "   str(info['results'][i]['route'])   " "   "to"   " "   str(info['results'][i]['destination'])   "n"   "Due:"   " "   str(info["results"][i]["duetime"])   " "   "minutes."   "n")
        i = i   1

return 'n'.join(str(x) for x in n)
  

return Оператор внизу — это то, что передается send_message функции. Есть ли какой-либо способ, которым я могу использовать метод множественных сообщений?

Комментарии:

1. Является send_message простой функцией или членом класса? info Создается в этой функции или передается ей в качестве аргумента?

2. Кажется, это яркий пример для генератора.

3. Для написания исчерпывающего ответа недостаточно кода, но как насчет вызова my_iter = iter(my_list) ?. После этого вы можете просто вызвать next(my_iter) , чтобы использовать следующее сообщение, и внутреннее состояние итератора гарантирует, что вы не обрабатываете одно и то же сообщение дважды.

4. @trincot send_message — это простая функция. Он принимает содержимое сообщения от отправителя и передает его в качестве аргумента на сервер реального времени автобусной компании. Затем он отправляет результаты.

5. Вы вызываете send_message через регулярные промежутки времени?

Ответ №1:

Вы можете сделать это легко, написав функцию генератора, как chunks() в приведенном ниже коде:

 info = {'results': [
            {'route': 1, 'destination': 'DestA', 'duetime': '10'},
            {'route': 2, 'destination': 'DestB', 'duetime': '20'},
            {'route': 3, 'destination': 'DestC', 'duetime': '30'},
            {'route': 4, 'destination': 'DestD', 'duetime': '40'},
            {'route': 5, 'destination': 'DestE', 'duetime': '50'},
            {'route': 6, 'destination': 'DestF', 'duetime': '60'},
            {'route': 7, 'destination': 'DestG', 'duetime': '70'},
            {'route': 8, 'destination': 'DestH', 'duetime': '80'},
            ],
       }

def chunks(info, n):
    results = info['results']
    for i in range(0, len(results), n):
        chunk = [
            'Route: {} to {}nDue: {} minutes.n'.format(
                result['route'], result['destination'], result["duetime"])
                    for result in results[i:i n]]
        yield 'n'.join(chunk)

for i, chunk in enumerate(chunks(info, 5), 1):
    print('== CHUNK {} ==n{}'.format(i, chunk))
  

Вывод:

 == CHUNK 1 ==
Route: 1 to DestA
Due: 10 minutes.

Route: 2 to DestB
Due: 20 minutes.

Route: 3 to DestC
Due: 30 minutes.

Route: 4 to DestD
Due: 40 minutes.

Route: 5 to DestE
Due: 50 minutes.

== CHUNK 2 ==
Route: 6 to DestF
Due: 60 minutes.

Route: 7 to DestG
Due: 70 minutes.

Route: 8 to DestH
Due: 80 minutes.
  

Комментарии:

1. Спасибо, это отлично работает! Сначала это повторялось бесконечно, но это была моя собственная ошибка, я оставил там ненужный цикл.

2. Всегда пожалуйста. Мне было интересно, почему от вас не было никакого ответа. Кстати, я настоятельно рекомендую вам научиться использовать строковый .format() метод, потому что его использование значительно упрощает создание и печать выходных данных, чем добавление нескольких строк вместе, как это делается в вашем коде.

Ответ №2:

Вот пример кода для того, как добиться этого с помощью ключевого слова generators и yield —

 def splitter(long_mess) :
        split_mess = ""
        for index in xrange(len(long_mess)) :
                if index>0 and index%5==0 :
                        yield split_mess
                        split_mess = ""
                split_mess  = str(long_mess[index]) #replace with the n.append line
        yield split_mess

input = "This is a really long message"
ans = splitter(input)
for i in ans :
        print i #replace with send_message(i)
  

Выходной сигнал :

 This 
is a 
reall
y lon
g mes
sage
  

Ответ №3:

Вот пример кода, который создает список списков, содержащий информацию о 5 остановках. Затем вы можете вызвать send_message цикл

 def split_into_pairs_of_5(info)
    outer = []
    inner = []
    for x in range(len(info["results"])):
        if x % 5 == 0 and inner:
            outer.append(inner)
            inner = []

        inner.append("Route:"   " "   str(info['results'][i]['route'])   " "   "to"   " "   str(info['results'][i]['destination'])   "n"   "Due:"   " "   str(info["results"][i]["duetime"])   " "   "minutes."   "n")

    if inner:
        outer.append(inner)
        inner = []

    return outer


result = split_into_pairs_of_5(info)

for i in info:
    send_message('n'.join(str(x) for x in i))
  

Ответ №4:

Мой подход использует itertools.groupby() группировку записей в виде фрагмента из 5 (или любого положительного числа). В вашем сообщении вы не показываете имя, в котором send_message вызывается, поэтому я просто называю его сам: get_info :

 import itertools

def show_rec(rec):
    output = 'Route: {route} to {destination}, due in {duetime} minutes'.format(**rec)
    return output

def get_info(group_size=5):
    # Do something to get info
    counter = itertools.count()
    for _, group in itertools.groupby(info['results'], key=lambda v: next(counter) // group_size):
        yield 'n'.join(show_rec(r) for r in group)

# Here is inside function send_message
for output in get_info(group_size=5):
    print output
    print
  

Обсуждение

  • Для меня первоочередной задачей является не решение этой проблемы, а то, как представить запись в приятном, понятном и понятном виде, для этого я создал функцию show_rec для выполнения этой работы
  • get_info это действительно генератор, а не обычная функция (см. Ключевое слово yield)
  • Внутри get_info я группирую записи в group of group_size с помощью itertools.groupby функции, которая сама по себе является генератором.
  • Обратите внимание, что counter это объект генератора, где as next(counter) даст 0, 1, 2, 3, 4, 5, 6, 7, 8, … Выражение next(counter) // group_size даст 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 2, 2, … для group_size=5 и это то, что мы используем для группировки записей.

Ответ №5:

Вы можете использовать функцию генератора с range (или xrange в Python 2.7), например:

 info = {'results': [
    {'route': 1, 'destination': 'DestA', 'duetime': '1'},
    {'route': 2, 'destination': 'DestB', 'duetime': '2'},
    {'route': 3, 'destination': 'DestC', 'duetime': '3'},
    {'route': 4, 'destination': 'DestD', 'duetime': '4'},
    {'route': 5, 'destination': 'DestE', 'duetime': '5'},
    {'route': 6, 'destination': 'DestF', 'duetime': '6'},
    {'route': 7, 'destination': 'DestG', 'duetime': '7'},
    {'route': 8, 'destination': 'DestH', 'duetime': '8'},
]}


def iter_with_chunks(seq, chunk_size):
    for index in range(0, len(seq), chunk_size):
        yield seq[index: index   chunk_size]


for count, chunk in enumerate(iter_with_chunks(info["results"], 5), 1):
    print("== CHUNK {count} ==".format(count=count))
    for item in chunk:
        output = 'Route: {route} to {destination},n' 
                 'due in {duetime} minutesn'.format(**item)
        print(output)
  

Вывод:

 == CHUNK 1 ==
Route: 1 to DestA,
due in 1 minutes

Route: 2 to DestB,
due in 2 minutes

Route: 3 to DestC,
due in 3 minutes

Route: 4 to DestD,
due in 4 minutes

Route: 5 to DestE,
due in 5 minutes

== CHUNK 2 ==
Route: 6 to DestF,
due in 6 minutes

Route: 7 to DestG,
due in 7 minutes

Route: 8 to DestH,
due in 8 minutes