Удивительное поведение в списке, tuple, & hellip; объекты в python [duplicate]

Я бы предложил использовать (unix util) xxd для этого. вы можете использовать его так:

$ echo hello world > a
$ xxd -i a

выходы:

unsigned char a[] = {
  0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x0a
};
unsigned int a_len = 12;

7
задан user1265125 2 June 2012 в 22:35
поделиться

5 ответов

Невозможно «сбросить» генератор. Однако вы можете использовать itertools.tee для «копирования» итератора.

>>> z = zip(a, b)
>>> zip1, zip2 = itertools.tee(z)
>>> list(zip1)
[(1, 7), (2, 8), (3, 9)]
>>> list(zip2)
[(1, 7), (2, 8), (3, 9)]

Это включает в себя кеширование значений, поэтому имеет смысл только в том случае, если вы повторяете оба итератора примерно с той же скоростью. (Другими словами, не используйте его так, как я здесь!) [/ ​​G8]

Еще один подход - передать функцию генератора и называть его всякий раз, когда вы хотите его итерации.

def gen(x):
    for i in range(x):
        yield i ** 2

def make_two_lists(gen):
    return list(gen()), list(gen())

Но теперь вы должны привязать аргументы к функции генератора при ее передаче. Вы можете использовать lambda для этого, но многие люди находят lambda уродливым. (Не я, хотя! YMMV.)

>>> make_two_lists(lambda: gen(10))
([0, 1, 4, 9, 16, 25, 36, 49, 64, 81], [0, 1, 4, 9, 16, 25, 36, 49, 64, 81])

Надеюсь, само собой разумеется, что в большинстве случаев лучше просто составить список и скопировать его.

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

z = zip(a, b)
while some_condition():
    fst = next(z, None)
    snd = next(z, None)
    do_some_things(fst, snd)
    if fst is None and snd is None:
        do_some_other_things()

Предположим, что этот цикл может или выхлоп z. Теперь у нас есть генератор в неопределенном состоянии! Поэтому на данном этапе важно, чтобы поведение генератора сдерживалось четко определенным образом. Хотя мы не знаем, где находится генератор, мы знаем, что: а) все последующие обращения будут давать более поздние значения в серии, и б) после того, как он «пуст», мы получили все предметы в серии ровно один раз. Чем больше мы должны манипулировать состоянием z, тем сложнее рассуждать об этом, поэтому лучше избегать ситуаций, которые нарушают эти два обещания.

Конечно, как указывает Джоэл Корнетт, ниже можно написать генератор, который принимает сообщения через метод send; и можно было бы написать генератор, который можно было бы сбросить с помощью send. Но учтите, что в этом случае мы можем только отправить сообщение . Мы не можем напрямую манипулировать состоянием генератора, и поэтому все изменения в состоянии генератора четко определены (самим генератором - при условии, что оно написано правильно!). send действительно предназначен для реализации сопрограмм , поэтому я бы не использовал его для этой цели. Каждый день генераторы почти никогда ничего не делают со значениями, присланными им - я думаю по тем причинам, которые я приводил выше.

11
ответ дан senderle 19 August 2018 в 03:46
поделиться
  • 1
    Также есть функция send(). – Joel Cornett 3 June 2012 в 00:13
  • 2
    Это работает, но является сложным и чрезмерным, ИМО. «Не используйте его, как я». это намек на это. :-) – Lennart Regebro 3 June 2012 в 08:14
  • 3
    @LennartRegebro, ну, думаю, tee существует по уважительной причине, и это самая близкая вещь в стандартных библиотеках, которые я могу придумать для функциональности, которую запросил OP. Я предполагаю, что ОП уже знает, что можно скопировать список! – senderle 3 June 2012 в 14:04
  • 4
    @ user1265125, рассмотрите мое недавнее редактирование, которое более подробно отвечает на ваш вопрос. – senderle 3 June 2012 в 14:33

Еще одно объяснение. Как программист, вы, вероятно, понимаете разницу между классами против экземпляров (т. Е. Объекты). zip() называется встроенной функцией (в официальном документе). На самом деле, это встроенная функция генератора . Это означает, что это скорее класс. Вы даже можете попробовать в интерактивном режиме:

>>> zip
<class 'zip'>

Классы - это типы. Из-за этого также должно быть ясно следующее:

>>> type(zip)
<class 'type'>

Ваш z является экземпляром класса, и вы можете думать о вызове zip() как о вызове конструктора класса:

>>> a = [1, 2, 3]
>>> b = [7, 8, 9]
>>> z = zip(a, b)
>>> z
<zip object at 0x0000000002342AC8>
>>> type(z)
<class 'zip'>

z - итератор (объект), который хранит внутри итераторов для a и b. Из-за своей общей реализации класс z (или zip) не имеет никакого значения для сброса итераторов через a или b или любые последовательности. Из-за этого нет возможности сбросить z. Самый чистый способ решить вашу конкретную проблему - скопировать список (как вы упомянули в вопросе и Леннарт Регебро предлагает ). Другим понятным способом является использование zip(a, b) дважды, создавая таким образом два итератора z, которые ведут себя с самого начала таким же образом:

>>> lst1 = list(zip(a, b))
>>> lst2 = list(zip(a, b))

Однако это невозможно использовать вообще с идентичным результатом . Подумайте, что a или b являются уникальными последовательностями, генерируемыми на основе некоторых текущих условий (например, температуры, считанные с нескольких термометров).

0
ответ дан Community 19 August 2018 в 03:46
поделиться

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

a=[1,2,3]
b=[7,8,9]
l1 = list(zip(a,b))
l2 = l1[:]
3
ответ дан Lennart Regebro 19 August 2018 в 03:46
поделиться
  • 1
    Да, как я уже упоминал, я может просто скопировать первый список. Я задал этот вопрос только потому, что хотел быть ясным в своих концепциях Python. Спасибо, в любом случае! – user1265125 3 June 2012 в 19:51

Нет, нет возможности «перезагрузить их».

Генераторы генерируют свой выход один раз один за другим по требованию, а затем выполняются, когда выход исчерпан.

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

Если вам нужно поддерживать выход генератора, подумайте о его хранении, например, в список, а затем повторно использовать его так часто, как вам нужно. (Несколько аналогично решениям, которые руководствовались использованием xrange(), генератора vs range(), который создал целый список элементов в памяти в v2)

Обновлено: исправленная терминология, временное прерывание мозгов. ..

1
ответ дан Levon 19 August 2018 в 03:46
поделиться
  • 1
    То, что вы описываете, в основном выполняется с помощью itertools.tee(), как описано в ответе senderle. +1 от меня, однако, поскольку ваше обсуждение имеет значение. – Eric Lebigot 19 April 2013 в 09:32

Просто создайте список из своего итератора с помощью list() один раз и затем используйте его.

Просто случается, что zip возвращает генератор, который является итератором, который вы можете выполнять только один раз.

Вы можете перебирать список столько раз, сколько хотите.

2
ответ дан Thomas Orozco 19 August 2018 в 03:46
поделиться
  • 1
    Это не «кастинг», но это, как правило, лучший подход. Кроме того, вы правильно определили, что «итератор» является супер-категорией, включая генераторы и последовательности, поэтому это дает мне право голоса за лучший ответ. – Karl Knechtel 3 June 2012 в 02:56
  • 2
    @KarlKnechtel, ты совершенно прав, я отредактировал это! – Thomas Orozco 3 June 2012 в 12:08
Другие вопросы по тегам:

Похожие вопросы: