5

For a lack of better words, I went with this title.

What I want is to be able to do something like this:

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

>>> fruits[2]
'3 Cherries'

>>> fruits[2]
'5 Cherries'

>>> fruits[2]
'2 Cherries'

But instead, the literal expression inside the string gets evaluated once upon the creation of the list and gives the same result each time I access it.

I was wondering whether there was an easier/smarter way to approach this other than writing some complicated edge case handling (we're programmers after all; who doesn't like writing nice code and being all elegant and fancy?). I speak of edge case handling because only 6 of my 49 strings require this kind of "special" behaviour.

What I've tried so far is making a lambda function out of the randint call, this doesn't help though; same result. Maybe this is a case for lazy evaluation, but I need a little guidance on how (or whether?) to use it with a list.

0

4 Answers 4

10

If you want some of your items to be literal, but some be callable, you could create your own version of 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])
Sign up to request clarification or add additional context in comments.

3 Comments

Thas much nicer then o11c's .. would you need to overload lots of other methods to make it play nice with iterating, slicing,printing as repr and str etc? or do they all go through __getitem__ per se? what about sorting?
@PatrickArtner no, and that will almost be guaranteed to not work with a built-in type in CPython.
Wow, it's so simple, thanks! I did not think of the idea that I can create an argument-less lambda for every lazily evaluated expression.
4

You're on the right track, wanting a lambda:

from random import randint

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

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

There are ways to elide the () but it's probably not worth it.

Comments

1

Disclaimer: I'm the author of the library

You can use seqtools to do lazy mapping, it supports iterating and slicing:

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

If you don't want to re-evaluate the function elements on each call, you can add some cache:

fruits = seqtools.add_cache(fruits)

Item assignment doesn't work through mappings obviously, so you will have to trick your way around for that.

Sorting will eventually evaluate all items in the list though, so you may as well do that upfront and call the regular list.sort() method.

Comments

0

Putting a lambda in a list sounds nice at first, but then you have to call this special (or all) indexes of your list. You cant iterate the list, you cant sort the list, ...

Its imho easier to create a function that provides you with lists:

from random import randint

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

print(gimme_fruits())

Output:

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

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

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

Each single list is "fixed" in the amount of cherries - but you can do this:

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

To get your vitamins:

Orange
Banana
3 Cherries
Orange
Banana
3 Cherries
Orange
Banana
3 Cherries

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.