5
\$\begingroup\$

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:

  1. if the number is less than 10, skip that number;
  2. 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))
\$\endgroup\$
  • \$\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 Jan 10 '17 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 Jan 10 '17 at 13:32
6
\$\begingroup\$

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.

\$\endgroup\$
  • \$\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 Jan 10 '17 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 the for loop begins. \$\endgroup\$ – Graipher Jan 10 '17 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 Jan 10 '17 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 Jan 10 '17 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 Jan 10 '17 at 13:57
7
\$\begingroup\$

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.

  1. Make an infinite generator, of user input.
  2. itertools.takewhile the input is good.
  3. 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())))
\$\endgroup\$
  • \$\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 functional try, then I'd just keep it as an infinite generator of strings, and move it cleanly into filter_numbers. \$\endgroup\$ – Peilonrayz Jan 10 '17 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 Jan 10 '17 at 15:32

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.