Redefine input function in Python to yield a value from a predetermined list of values each time it's called

76 Views Asked by At

Is there a clean "Pythonic" way to write an input-replacement function in Python that will yield a value from a predetermined list of input values each time it's called?

raw_data = ['8', '2', '1']
c = 0


def input():
    global c
    c += 1
    return raw_data[c - 1]


for _ in range(3):
    print(input())

This does and is expected to output:

8
2
1

Intuitively yield seems like it ought to be part of the solution but I cannot wrap my head around how to implement it as an input replacement.

3

There are 3 best solutions below

0
Kelly Bundy On

Two ways I've been using for that:

input = iter([8, 2, 1]).__next__
input = [8, 2, 1][::-1].pop

Of course you don't need the [::-1] if you're ok with writing the list backwards.

If you also need the list for something else, you can use the first solution but with the list stored in an extra variable.


Though the normalinput() returns strings, so what I actually usually use for that is like this, where I copy&paste a given input data block:

input = iter('''\
line 1
line 2
line 3
'''.splitlines()).__next__
1
chepner On

input is a wrapper around reading from sys.stdin. You can simply replace that with a StringIO object instead of using whatever your process received as sys.stdin.

>>> import io, sys
>>> sys.stdin = io.StringIO("foo\nbar\nbaz\n")
>>> input()
'foo'
>>> input()
'bar'
>>> input()
'baz'
>>> input()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
EOFError: EOF when reading a line

You can always restore the original standard input if and when necessary:

sys.stdin = sys.__stdin__

I don't recommend replacing replacing a built-in function with a function of a different type. Existing code that uses input will expect a return value of str, not the int your example shows. If your goal is to not patch the existing built-in, you should simply define and use your own function instead.

0
Eric D On

Thank you to the other answers. They are elegant and effective. I'm also trying to avoid exposing how IO works as much as possible so here's my take on the problem. It does require the use of iter, though.

raw_input = iter(['8', '2', '1']) # must use strings


def input():
    return next(raw_input)


for i in range(3):
    print(input())

And, an alternate input, a touch "cleaner" but a little more advanced:

input = raw_input.__next__

Thanks to Stef.