"Ленивая" оценка элементов списка в Python - PullRequest
0 голосов
/ 06 ноября 2018

Из-за нехватки лучших слов я пошел с этим названием.

Я хочу иметь возможность сделать что-то вроде этого:

>>> from random import randint
>>> fruits = [
...     "Orange",
...     "Banana",
...     f"{randint(2,5)} Cherries",
... ]

>>> fruits[2]
'3 Cherries'

>>> fruits[2]
'5 Cherries'

>>> fruits[2]
'2 Cherries'

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

Мне было интересно, есть ли более простой / умный способ приблизиться к этому, чем написание сложной обработки крайних случаев (в конце концов, мы программисты; кому не нравится писать хороший код и быть элегантным и причудливым?). Я говорю об обработке краевого случая, потому что только 6 из моих 49 строк требуют такого «особого» поведения.

То, что я до сих пор пробовал, - это создание лямбда-функции из вызова randint, хотя это не помогает; тот же результат. Может быть, это случай для ленивой оценки, но мне нужно небольшое руководство о том, как (или нужно ли?) Использовать его со списком.

Ответы [ 4 ]

0 голосов
/ 11 июля 2019

Отказ от ответственности: я автор библиотеки

Вы можете использовать seqtools для создания отложенного отображения, он поддерживает итерации и нарезки:

>>> fruits = seqtools.smap(lambda x: x if isinstance(x, str) else x(), 
...                        fruits)
>>> fruits[0]
'Orange'
>>> fruits[-1]
'3 Cherries'

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

fruits = seqtools.add_cache(fruits)

Назначение предметов, очевидно, не работает через сопоставления, поэтому вам придется обойти это.

При сортировке в конечном итоге будут оцениваться все элементы в списке, поэтому вы также можете сделать это заранее и вызвать обычный метод list.sort ().

0 голосов
/ 06 ноября 2018

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

from random import randint

class LazyList(list):
    'Like a list, but invokes callables'
    def __getitem__(self, key):
        item = super().__getitem__(key)
        if callable(item):
            item = item()
        return item

fruits = LazyList([
    "Orange",
    "Banana",
    lambda: f"{randint(2,5)} Cherries",
])

print(fruits[2])
print(fruits[2])
0 голосов
/ 06 ноября 2018

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

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

from random import randint

def gimme_fruits ():
    return [ "Orange", "Banana", f"{randint(2,5)} Cherries",]

print(gimme_fruits())

Выход:

['Orange', 'Banana', '3 Cherries']

['Orange', 'Banana', '4 Cherries']

['Orange', 'Banana', '2 Cherries']

Каждый список «фиксирован» по количеству вишен - но вы можете сделать это:

for f in gimme_fruits() + gimme_fruits() + gimme_fruits():
    print(f)

Чтобы получить ваши витамины:

Orange
Banana
3 Cherries
Orange
Banana
3 Cherries
Orange
Banana
3 Cherries
0 голосов
/ 06 ноября 2018

Вы на правильном пути, хотите лямбду:

from random import randint

fruits = [
        lambda: "Orange",
        lambda: "Banana",
        lambda: f"{randint(2, 5)} Cherries",
]

print(fruits[2]())
print(fruits[2]())
print(fruits[2]())

Существуют способы исключения (), но, вероятно, оно того не стоит.

...