- Loading. animating dots in python
- 2 Answers 2
- Consistency
- PEP8 violations
- More vertical whitespace
- Less vertical whitespace.
- Naming
- Comments
- Documentation
- Linter, style checker, static analyzer, code formatter
- Missing newline
- print vs. sys.stdout.write
- Magic values
- Reusability: subroutines
- More reusability: parameters
Loading. animating dots in python
This loading bar is really just for fun. I want to know other peoples ways of writing a loading bar that’s considered animated like using time.sleep to give the «Loading effect». I know they’re easier ways of writing it compared to mine, wanting to know how other people would code an animated loading bar?
import time import sys print('Loading') for i in range(3): time.sleep(.5) sys.stdout.write('.') sys.stdout.flush()
2 Answers 2
Consistency
Sometimes, you use the print() built-in function without a file argument and sometimes you use the write method of the text file returned by sys.stdout . Those two are equivalent.
You should choose one style and stick with it. If you are editing some existing code, you should adapt your style to be the same as the existing code. If you are part of a team, you should adapt your style to match the rest of the team.
In general, if you use two different ways to write the exact same thing, the reader will think that you want to convey a message with that. So, you should only use two different ways of writing the same thing IFF you actually want to convey some extra information.
PEP8 violations
The standard community coding style for the Python community is defined in Python Enhancement Proposal 8 – Style Guide for Python Code. You should always follow the guidelines of PEP8. There are plenty of tools available that can flag and even auto-correct violations of PEP8.
More vertical whitespace
Your code is all bunched up together. Some blank lines would allow the code room to breathe, for example between the import s and the actual code.
import time import sys print('Loading') for i in range(3): time.sleep(.5) sys.stdout.write('.') sys.stdout.flush()
And also to separate the two steps of printing the header «Loading» and printing the animation:
import time import sys print('Loading') for i in range(3): time.sleep(.5) sys.stdout.write('.') sys.stdout.flush()
Less vertical whitespace.
Your code ends with two blank lines. There should be no blank lines at the end of the file. A file should end with a single newline character after the last non-whitespace character.
Note: the section on Blank Lines in PEP8 says [bold italic emphasis mine]:
Surround top-level function and class definitions with two blank lines.
which seems to imply that, if the last element of your file is a top-level function or a top-level class, there should be two blank lines after it, but I believe this is a mistake in PEP8. In my humble opinion, it should read:
Separate top-level function and class definitions with two blank lines.
Anyway, you have neither top-level functions nor top-level classes in your code, therefore, this is a moot point.
import time import sys print('Loading') for i in range(3): time.sleep(.5) sys.stdout.write('.') sys.stdout.flush()
Naming
Unused variables should be named _ . You are not using the variable i , so it should be named _ instead.
import time import sys print('Loading') for _ in range(3): time.sleep(.5) sys.stdout.write('.') sys.stdout.flush()
Comments
Generally speaking, comments are a code smell. Mostly, comments should not exist:
- If your code is so complex that you need to explain it in a comment, you should rather try to refactor your code to be less complex.
- If you have a comment that explains what something is, you can rather give it a name.
- If you have a comment that explains how the code does something, just delete it: the code explains how the code does something.
The only acceptable thing for a comment is to explain why the code does something in a specific non-obvious way. So, for example, there is an obvious way that looks like it should work, but you tried it and it didn’t work for a non-obvious reason. Then you can add a comment explaining that you are specifically using solution B even though it looks like much simpler solution A should also work, but it actually doesn’t work because of issue X. Ideally, you would add a link to the pull request / code review / bug ticket where this issue is discussed in greater detail and maybe a link to a wiki page with a detailed explanation.
In your code, you have no comments, which is reasonable: the code is straightforward and simple, there is nothing to explain.
Documentation
Your code has no documentation. Every public module, class, function, method, etc. should have a docstring.
Your code is implicitly a public module, so it should have a docstring.
Linter, style checker, static analyzer, code formatter
You should use a linter and/or a static analyzer, preferably one with an auto-correct functionality. I actually have multiple linters and multiple static analyzers configured in my editor, and they are set up so that they analyze my code while I type, and automatically correct whatever they can auto-correct when I save.
Here are some of the ones I use:
Using these tools frees you from a lot of the responsibilities for the minutiae of writing the code: whitespace, indentation, formatting, simple bugs, best practices, etc. The tools do a lot of the work for you, and in cases where they can’t do the work themselves, they at least give you good, readable warnings with actionable information that tells you what to do.
In short: you can focus your brain on solving the problem and leave the low-level concerns of coding to the tools.
For example, all of the things I mention above, are automatically taken care of for me, if I simply copy&paste your code into my text editor. It automatically inserts the blank line after the import s and removes the blank lines at the end. (It also converts all string literals to double quotes, but that’s just a setting that can be changed.)
Missing newline
Just like the source code file itself, the text you print to the console should end with a single newline. Otherwise, all further code that is printed to the console by your program, will end up bunched up after the last . instead of at the beginning of the next line. Or, if the animation is the last thing your code prints, then the shell prompt might appear directly after the . instead of at the beginning of the next line.
import time import sys print('Loading') for _ in range(3): time.sleep(.5) sys.stdout.write('.') sys.stdout.flush() print()
print vs. sys.stdout.write
print has a file parameter which tells print which file to print to. If you don’t pass a file argument, the default is to print to sys.stdout . In other words: using print or sys.stdout.write are equivalent. You could make your code more consistent and reduce the number of import s by only using print :
import time print('Loading') for _ in range(3): time.sleep(0.5) print('.', end='', flush=True) print()
Magic values
There are a couple of magic values in your code that could use some explanation. You could add comments to explain those values, but remember what we said about comments above: ideally, the code should explain itself with no need for comments.
So, instead of writing comments to explain those values, we can assign them to variables with intention-revealing names:
import time title = 'Loading' iterations = 3 seconds_between_iterations = 0.5 character = '.' print(title) for _ in range(iterations): time.sleep(seconds_between_iterations) print(character, end='', flush=True) print()
See, for example, how the name seconds_between_iterations leaves no question about what unit the 0.5 is in, even if you don’t know the definition of time.sleep() by heart: it’s half a second.
If we really decide that we absolutely need comments here, we now have some named entity in the source code that we can attach a documentation comment to: the variables.
import time title = 'Loading' """The title to print before the animation.""" iterations = 3 """The number of iterations of the loop.""" seconds_between_iterations = 0.5 """The number of seconds to wait between printing characters.""" character = '.' """The character to print.""" print(title) for _ in range(iterations): time.sleep(seconds_between_iterations) print(character, end='', flush=True) print()
Since we now have proper docstrings which are attached to the variables, they will be displayed when you hover over the variable:
Another advantage of having these variables as named entities in our source code, is that we can give them type annotations. In this case, you can see that Pyright has automatically inferred the type Literal[‘.’] for the variable, which is the most restrictive type you can have. But you might want to provide a different type, which you can now do, because there is something you can attach the type to: the variable.
Reusability: subroutines
Your code is not very reusable. It would be much better to wrap it into a function, so you can use it in multiple places:
import time def print_loader_animation() -> None: title = 'Loading' """The title to print before the animation.""" iterations = 3 """The number of iterations of the loop.""" seconds_between_iterations = 0.5 """The number of seconds to wait between printing characters.""" character = '.' """The character to print.""" print(title) for _ in range(iterations): time.sleep(seconds_between_iterations) print(character, end='', flush=True) print() if __name__ == '__main__': print_loader_animation()
More reusability: parameters
At the moment, we have a function which we can call multiple times, so we can reuse the function in multiple places. However, if we want to use if for, say, a saving animation, that is not possible, because it always prints Loading .
To solve that, we can introduce parameters. In fact, we have already identified all the things you might want to parameterize and given them names.
"""Print a waiting animation to the console.""" import time def print_waiting_animation( *, title: str, iterations: int = 3, seconds_between_iterations: float = 0.5, character: str = '.' ) -> None: """ Print a waiting animation to the console. :param title: The title to print before the animation. :param iterations: The number of iterations of the loop. :param seconds_between_iterations: The number of seconds to wait between printing characters. :param character: The character to print. """ print(title) for _ in range(iterations): time.sleep(seconds_between_iterations) print(character, end='', flush=True) print() if __name__ == '__main__': print_waiting_animation(title='Loading')