1

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.

6
  • 1
    Face palm. I knew that. Thank you! Commented Feb 10, 2024 at 13:06
  • Do you use the list for anything else? Commented Feb 10, 2024 at 13:13
  • Note that if you use yield instead of return, you don't need c to be global. def input_replacement(): c = 0; while True: yield raw_data[c]; c += 1. Perhaps the thing you were missing was the necessity to have an explicit loop in the function that uses yield Commented Feb 10, 2024 at 13:17
  • @Stef But then input() returns a generator iterator instead of a value. Commented Feb 10, 2024 at 13:19
  • @KellyBundy Indeed. So maybe it should be called def source_of_input(): ...yield... and then input = source_of_input() or something. Commented Feb 10, 2024 at 13:23

3 Answers 3

3

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__
Sign up to request clarification or add additional context in comments.

Comments

2

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.

1 Comment

I was semi-surprised that contextlib.redirect_stdin was not provided to simplify this. github.com/python/cpython/issues/92178 indicates this was an intentional omission, though I leave it to the reader to track down the specific arguments against. (In summary, it's more complicated to replace an input stream correctly than it is to replace an output stream.)
0

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.

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.