0

I've wrote a little Python script that prompts the user to enter plot coordinates to then make a graph with it. Here's the part I have some trouble with:

x = []

while True:
   try:
      x.append(float(input()))
   except ValueError:
      break

y = [float(input()) for _ in range(len(x))]

Here's a little description of what happens: the user enters whatever numerical values he wants provided they are in float or int format, then enters a forbidden character, like a letter, to go on to the next step and enter another set of values, but this time the same quantity as the first set.

Now this code works perfectly fine, but notice how the y list is elegantly filled in a single line of code by a list comprehension, thanks to the fact that the iterator's length is already known due to the presence of the x list. But my problem is that I can't figure out how to beautifully fill x, not only to make my code shorter and probably more optimized, but also not to have to define the list beforehand at the beginning of my script.

I have tried ways to go around ths problem, but they did not seem to work, so I'm gonna show that to you with the hopes that you can help my poor soul.

x = [float(tempvalue) for _ in iter((lambda tempvalue=input(): tempvalue),"end")]
x = [float(tempvalue) for _ in iter(int,1) if (tempvalue := input()) != "end"]

In these attempts, I tried to exit the loop with the specific keyword 'end' instead of any random character that is not a number, but it doesn't really matter since it doesn't work.

kaya3
  • 47,440
  • 4
  • 68
  • 97
  • 1
    Even `y` isn't as simple as it could be: `y = [float(input()) for _ in x]`. It doesn't matter if `_` is an index or an actual value from `x`; you ignore it either way. However, I don't recommend actually writing code like this, because you can't handle non-float inputs cleanly. – chepner Jan 20 '20 at 20:51
  • 2
    Write your own iterator or generator that signals the end of iteration when it gets an invalid input. – Barmar Jan 20 '20 at 20:51
  • Does this answer your question? [break list comprehension](https://stackoverflow.com/questions/9572833/break-list-comprehension) – Georgy Jan 20 '20 at 20:54
  • 1
    A simpler way would be to change the format of the input to say, space-separated values, and then a simple `x = [float(val) for val in split(input())]` would suffice – Tomerikoo Jan 20 '20 at 20:54
  • 1
    @Tomerikoo *`input().split()` – wjandrea Jan 20 '20 at 21:00
  • 1
    Best to write a function that will take care of user input as oppose to a list-comprehension in this case. Making your code "smaller" doesn't make it more efficient or readable. – felipe Jan 20 '20 at 21:20
  • You could use [`fileinput.input`](https://docs.python.org/3/library/fileinput.html#fileinput.input), which you can stop by sending an EOF. But there are pros and cons which depend on what your full script does (e.g. the way it takes arguments as file names). – wjandrea Jan 20 '20 at 21:26

2 Answers2

1
>>> x = [*map(float, iter(input, 'end'))]
3.14
2.71828
end
>>> x
[3.14, 2.71828]
Kelly Bundy
  • 23,480
  • 7
  • 29
  • 65
  • Thanks! That works very well! Now is there a way to make it so that any unauthorized input ends the iteration, instead of the keyword? – Lélahel Hideux Jan 20 '20 at 21:04
  • @LélahelHideux just *write your own iterator* – juanpa.arrivillaga Jan 20 '20 at 21:04
  • 5
    @LélahelHideux then don't write a bunch of torturous code to shoe-horn something into a list comprehension when it is perfectly fine as it is. Not everything has to be a list comprehension, if it isn't obvious how to make it one, then it probably shouldn't be one. The advantage of a list comprehension is *readability* not performance – juanpa.arrivillaga Jan 20 '20 at 21:09
  • @LélahelHideux I don't see anything nearly as neat to detect "unauthorized input". Maybe write a function `input_float` which does `return float(input())` in a `try` and nothing in the `except`, and then use `iter(input_float, None)`. But I agree with juanpa, your current code is fine. – Kelly Bundy Jan 20 '20 at 21:09
  • @juanpa.arrivillaga Internet gentlemen holding up to their standards I see ;) – Lélahel Hideux Jan 20 '20 at 21:14
  • 3
    See "[Explaining entirely code-based answers](https://meta.stackoverflow.com/q/392712/128421)". While this might be technically correct it doesn't explain why it solves the problem or should be the selected answer. We should educate in addition to help solve the problem. – the Tin Man Jan 20 '20 at 21:54
  • @theTinMan It's mostly using elements of the OP's own attempt, just properly and simpler. Not sure what I'm supposed to explain? What part is unclear to you? – Kelly Bundy Jan 20 '20 at 22:23
  • 1
    Explain why you say things are "proper" and "simpler". Making changes just to make changes doesn't make sense, so explain the reasoning behind them. – the Tin Man Jan 21 '20 at 00:43
  • @theTinMan "proper" because mine works, his doesn't. He said so himself, did you not read it? And "simpler"... do you not agree that `iter(input, 'end')` is simpler than `iter((lambda tempvalue=input(): tempvalue),"end")`? – Kelly Bundy Jan 21 '20 at 00:46
1

If you have code which builds a list using the append method, the simple way to convert it to an iterator is to wrap it in a function, and use the yield keyword in place of append:

def read_float_inputs():
    while True:
        try:
            yield float(input())
        except ValueError:
            break

Usage example:

>>> nums = list(read_float_inputs())
1
2
3
stop
>>> nums
[1.0, 2.0, 3.0]
kaya3
  • 47,440
  • 4
  • 68
  • 97