I am studying Python, so it is very easy task. I want to get best and most elegant way to solve each simple task. The task is:
Write a program that reads the integers from the console one number per line. For each entered number check:
- if the number is less than 10, skip that number;
- if the number is greater than 100, stop to read the numbers;
Print all numbers that does not satisfy those two conditions.
lst = []
num = 0
while num <=100:
num = int(input())
if num > 100:
break
if num >= 10:
lst.append(num)
print('\n'.join(str(value) for value in lst))
-
\$\begingroup\$ "I want to get [...] most elegant way", really depends on what you think elegant is, I think a functional/iterative approach is nicer, others don't. Which do you prefer? \$\endgroup\$Peilonrayz– Peilonrayz ♦2017年01月10日 13:20:35 +00:00Commented Jan 10, 2017 at 13:20
-
\$\begingroup\$ By "most elegant" I ment most pythonic and how experienced Python developer would solve the same problem. I am not sure with the input. And don't know difference between printing list this way: print('\n'.join(str(value) for value in lst)) or that way: print('\n'.join(map(str, lst))) \$\endgroup\$eulers_child– eulers_child2017年01月10日 13:32:13 +00:00Commented Jan 10, 2017 at 13:32
2 Answers 2
Here is a functional approach, as alluded to by @Peilonrayz:
def get_numbers_up_to(n, min_=10):
"""
Ask the user for numbers until he enters a number larger than `n`.
Yields all entered numbers larger than `min_`.
"""
while True:
try:
num = int(input())
except ValueError:
continue
if num > n:
break
elif num >= min_:
yield num
if __name__ == '__main__':
print('\n'.join(map(str, get_numbers_up_to(100)))
This uses a generator function to yield
the numbers as they come in (if they match the criteria), instead of accumulating them in a list right away.
Instead of '\n'.join(str(value) for value in lst)
this uses '\n'.join(map(str, lst))
, but this is a matter of taste in Python 3.x (if you like functional programming more, the map
looks nicer).
Other than that, your code does not include any user input validation. (What happens if the user inputs foo
, instead of a number?)
Also, all values are hard-coded, so re-using the code becomes harder. Therefore I made it into a function with parameters.
Note that I used min_
instead of min
to avoid shadowing a built-in function.
Also, docstrings never hurt nobody.
I also included a if __name__ == '__main__':
guard to allow importing of this function from another module.
-
\$\begingroup\$ Thank you very much for your answer and very detailed explanations. I still have some question about printing. Which one works faster and which one takes less memory? \$\endgroup\$eulers_child– eulers_child2017年01月10日 13:47:27 +00:00Commented Jan 10, 2017 at 13:47
-
\$\begingroup\$ In this case they will take basically the same amount of memory, because you consume the whole generator right away. If you were using a real
for
loop and do more stuff with the numbers, the memory benefits of a generator would show (each value is generated, i.e. the user is asked) as the next iteration of thefor
loop begins. \$\endgroup\$Graipher– Graipher2017年01月10日 13:53:13 +00:00Commented Jan 10, 2017 at 13:53 -
\$\begingroup\$ The generator will be slightly (unnoticably in this case) slower, because it involves 1. A function (which comes with some overhead) 2. Saving the state of the generator after every
yield
3. Resuming the generator from that state. \$\endgroup\$Graipher– Graipher2017年01月10日 13:54:26 +00:00Commented Jan 10, 2017 at 13:54 -
\$\begingroup\$ @eulers_child In general, using a generator is worth it for the huge memory benefits (only the current value of the generator is in memory, instead of the whole list of values). \$\endgroup\$Graipher– Graipher2017年01月10日 13:55:09 +00:00Commented Jan 10, 2017 at 13:55
-
\$\begingroup\$ @eulers_child It is customary to wait with accepting an answer for some time. This gives people in other timezones also the chance to see the question. Questions with an accepted answer gather less answers and views compared to questions without an accepted answer. Rather consider leaving an up-vote if you like an answer (and as soon as you have enough reputation) and wait at least a day before accepting an answer. \$\endgroup\$Graipher– Graipher2017年01月10日 13:57:00 +00:00Commented Jan 10, 2017 at 13:57
Your code is pretty good. Change num <=100
to True
and remove the un-needed num
out of the while
loop, and it's good.
You should also expect user input to not be a number and handle it as you need.
Since comprehensions are a cleaner than map
and filter
, you may want to steer clear of them. Some people stick to never using them, I however use them if it makes the input clearer, such as map(str, user_input())
.
You can then adjust your code slightly to get the iterative approach of:
list_ = []
while True:
try:
num = int(input())
except ValueError:
continue
if num > 100:
break
if num >= 10:
list_.append(num)
However, you may want a functional approach. This can be better as then you can pick and mix functions, but some don't like it as much.
- Make an infinite generator, of user input.
itertools.takewhile
the input is good.- Filter input.
def user_input():
while True:
yield input()
def to_int(numbers):
for num in numbers:
try:
yield int(num)
except ValueError:
continue
nums = itertools.takewhile(lambda n: n <= 100, to_int(user_input()))
print('\n'.join(str(n) for n in nums if n >= 10))
Python is multi-paradigm, and so you can mix them. And so as-long as it works and is clear, it's fine. I personally would use:
def user_input():
while True:
try:
yield int(input())
except ValueError:
continue
def filter_numbers(numbers):
for num in numbers:
if num > 100:
break
if num >= 10:
yield num
print('\n'.join(str(n) for n in filter_numbers(user_input())))
-
\$\begingroup\$ @Graipher Oh my, today's not my day, XD They should all be fixed, the
takewhile
too. I do too, if Python had a functionaltry
, then I'd just keep it as an infinite generator of strings, and move it cleanly intofilter_numbers
. \$\endgroup\$2017年01月10日 15:28:33 +00:00Commented Jan 10, 2017 at 15:28 -
\$\begingroup\$ Well there was a PEP for it, it even says Python 3.5 in there, but it does not seem to be implemented: python.org/dev/peps/pep-0463 :( \$\endgroup\$Graipher– Graipher2017年01月10日 15:32:31 +00:00Commented Jan 10, 2017 at 15:32