Beginning Python Programming — Part 10Bob RoeblingBlockedUnblockFollowFollowingJun 6Photo by hue12 photography on UnsplashIn the previous post, we covered everything related to errors to help reduce the number of bugs in our programs.
Beginning Python Programming — Part 9Everything you need to know about error handling in Pythonmedium.
comToday we are going to go a step further and learn a few more techniques to help you cut down on the number of bugs in your code.
If you have been practicing like I hope you have, you’ve probably made a sizable program, had dinner or went out, and when you came back, you felt a little lost in your code.
Getting lost happens a lot when getting started in programming.
You get frustrated if you are distracted because you forget what you wrote five minutes ago.
It’s happened to most of us.
I have good news; it does get easier over time.
How long that takes depends on how much you practice.
For me, it took about six months of everyday coding.
From the time I woke up to the time I went to bed, even on weekends, I was always writing code.
Don’t get me wrong; it didn’t mean that I didn’t have a life.
I still went out and had fun.
Sometimes I’d burn myself out and have to stop writing code for a day or two.
Even then, I was still reading articles and trying to get better at what I did.
So keep practicing.
Code StructureThere are plenty of guides on the Internet that tell you how to write code, and there is documentation about how to format your code, but structuring code is always left to the developer.
I’m not saying “let’s make a standard.
” That would be too much work, but we can all agree that there is a generic starting point for your code and this is what I’ll be going over.
Since we haven’t covered modules or file imports yet I can take the time up front to do so.
ImportsImports allow you to import code located in other files or modules.
For example, if I have a file named models.
py, with a Person class inside, which I need to use in another file called app.
py, I cannot just call the Person class and it will work; I have to import it first.
So how do we do this?import is a unique keyword in Python that tells the interpreter to look elsewhere for code that will be called later on in this file.
It is the first thing processed when the interpreter runs the code in this file.
In this example, we import the entire models file.
We only include the file name; not the extension.
This allows us to use every class or function that appears inside of models.
This is OK, but sometimes we want to be explicit in what we import.
Instead, we can use from .
Here we have a better version of our import statement.
We only import the Person class.
If we were to have a Dog class, we would need a separate import for it, or we could append Dog after Person, separated by a comma.
There are other imports you can use that are not related to your code that you may find interesting.
For example, if you open a Python console and type import this, you will get The Zen of Python, by Tim Peters, displayed in your console window.
This is related to this article, so I’m going to paste it below.
The Zen of Python, by Tim PetersBeautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Special cases aren’t special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one — and preferably only one — obvious way to do it.
Although that way may not be obvious at first unless you’re Dutch.
Now is better than never.
Although never is often better than *right* now.
If the implementation is hard to explain, it’s a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea — let’s do more of those!Read through this a few times and try to keep this in mind as you write code.
Another interesting one is import antigravity.
ModulesModules are simply folders that contain related code, such as helpers, services, or even models.
As I type this, I realize I never really explained what models are.
Models are mostly the objects that you will use to store data in when your program is running.
A Person, Dog, Cat, or Place could be considered a model.
These models allow you to pass a particular object’s data around as one value instead of multiple values.
Back to modules.
The structural difference between a module and folder is that a module contains a file named __init__.
py inside of it.
This file may be blank, or it could provide initial configuration information for anyone importing this module.
my_project/ models/ __init__.
pyUsing the above folder structure, we could import the person class using the following syntax:We could adjust the __init__.
py to include the import statements to make it easier everywhere else.
Because these files belong to a module, we can use .
to signify the current folder and skip typing models.
This is fine for small projects, but in larger projects, it might make more sense to know where you are importing from, especially if you have nested modules.
Now when we need to import any of the classes inside the models folder it becomes much more manageable.
See how easy that is?.models knows about the different classes because of __init__.
py and we can directly import these classes without having to reference the file where each class exists.
File StructureOK, with imports figured out, let’s begin with code structure, more specifically file structure.
When writing programs, we need to ensure that our code is readable.
I don’t know about you, but I tend to read top to bottom, left to right.
Why shouldn’t our code do the same?When I can use only one Python file, such as when I am writing a script, I would want it to follow this scheme:[import statements][global variables/constants][class declarations][helper/one shot functions]entry pointI have square brackets because those are optional; you may not always need imports or any of the other items.
Your code should start somewhere, and by default, this is at the top-level scope.
This is why our print statements work.
A standard convention is to use the following syntax to start your program when a function is the first thing that needs to be called.
For more information on this, check out this great post on Stack Overflow.
I have the layout in this particular order because it makes the most sense.
Imports should always be at the top of whatever scope you need them; most often, this is the top of the file.
Global variables and constants should be next.
It’s always nice to open up code and see all of the fixed variables at the top; it makes for less searching later on when one of them needs to change.
Class declarations come next because they may need new fields or to reference while adding more code.
One-shot functions are included with helper functions above, but they usually sit above helper functions, and they get their name because they are only called once when the program is running.
This is for state configurations from environment variables, or perhaps setting up the program to handle authentication with another source.
Helper functions are code that you would use over and over again, stuff like generating a random number or summing a list of numbers.
The entry point is where your code starts.
We put this at the bottom because of old C/C++ days where you’d need all your declarations at the top.
This is just out of habit, and you will see this more often than not.
Photo by Glenn Carstens-Peters on UnsplashPrinciplesYour project should make sense as if you were placing things in rooms throughout your house.
In programming, we don’t have sofas, ovens, or beds, but we do have models, services, views, helpers, APIs, and apps.
Django does a great job of separating functionality, forcing you to use a proper project structure.
I want to cover a few principles before we get into the project structure.
First, “if it ain’t broke, don’t fix it.
” This should be a pretty straightforward principle.
If the code you wrote works, even if you have no idea how it does, leave it alone.
Chances are if you try to fix it, you’ll break it and not know how to put it back the way it was.
This is merely making sure that the accessibility (defined by access controls) is as restrictive as possible on every piece of code you write.
If no one else is supposed to see my_super_secret_function(), don’t make it public.
Add an _ in front, so others know not to use it.
If you have networking code that builds a URL for you, let the networking class make the URL; set that method to private in the networking class.
This sounds abstract, but it’s not.
When you have a class use part of another class, get only what you need from the class; don’t instantiate that class inside of your original class.
Otherwise, you get a strong reference.
Loose Coupling is just the result of keeping your dependencies between classes low.
Use dictionaries, lists, or sets to help reduce the amount each class knows about each other.
Project StructureYour project structure will change depending on what you need to do.
Sometimes you only need one file, but when you need more, how do you keep track of it all?Here’s my typical file structure that works for medium to large Python-only applications.
my_project/ app/ __init__.
secretsIn this project, we have an app module which contains all of the business logic for our application; that is, the code that moves and transforms data from one place to the next.
py could be called anything, but I’m just keeping it easy to understand.
py isn’t the entry point for our application but it’s where our program starts to do its work.
py files may not even be necessary, but here we use them just because it is an example.
models contains all of our models (i.
, classes) that we will use throughout our application.
services includes all of our background code.
It’s code that may not be fun to write, but we will need to use it multiple times, and it is too large to work under one file.
Here we have code for authenticating with servers, connecting to and performing CRUD operations on the database (Create, Read, Update, Delete), and networking code, so we don’t have to keep writing code to ensure we got a response back and log an error if we didn’t.
tests contains all of our tests.
Test files can either be per module or file depending on the need, and we can break these up into their modules if needed for clarity.
venv is something we haven’t covered but this is for virtual environments, which I will cover in a later tutorial.
When you use one, it is automatically created for you, and this can safely be ignored for now.
py is the file that we call to start our program.
This typically contains a single import, importing the main file that gets the hamster running on the wheel.
py is a Config class that contains default running configurations for our application.
It isn’t required but it is a nice-to-have.
txt and test_requirements.
txt have not been covered but they provide a list of external modules and their version numbers that are required for this program to run.
I like to split these up, so I don’t include any testing modules in my production code.
env is a hidden file on *nix systems that we would create to store environment variables.
It’s not secure by any means, but we can parse it later and pull values out as needed.
secrets is the same as .
env except it can be used to store usernames and passwords.
I would strongly suggest that you only use something like this when deploying to docker/podman containers so you can delete the file after it has been read into memory.
You will also need to ensure that your program has a run loop (like a web server) so it doesn’t finish running.
As soon as it does, all of these variables are released from memory.
Photo by Mark Duffel on UnsplashCoding GuidelinesThe developers of Python have created a few standards called PEPs.
Most famous is the PEP 8 Style Guide.
It’s a lengthy document, but it’s helpful.
Some of the highlights:Line length should be no greater than 80 characters.
Some formatters such as Black think this is outdated and suggest a more lenient 88 characters.
If you have to go over 80 characters, always hard wrap at 120 characters.
Lengthy formulas that require multiple lines are suggested to break before the operator (+-*/).
Use two blank lines between top-level functions and classes, single blank lines between methods within classes, and blank lines sparingly within functions to denote logical sections.
Imports from different modules should always be on separate lines.
Wildcard imports (from models import *) should be avoided.
It’s too unclear on what is being imported.
There are plenty of things to go over, so I highly suggest you read it.
Another document to look at is PEP 257, which covers docstrings.
This is also highly recommended as docstrings make your life easy by supplying a description of what each function does and what it returns in editors that support docstrings (PyCharm, VS Code).
Using descriptive variable namesDon’t try to obfuscate the code by using one letter variables like people tend to do in C/C++.
Over the years, i, j, k, l, m, n, t, x, y, and z have had so many values for so many things they could mean anything.
Telling me you have a program that calculates distance traveled at a given speed over a period of time using nothing but j, k, and m says absolutely nothing; that’s all great, senior dev, but what is k?.Senior dev looks at the code for a minute… Ah, k is the speed variable.
Really?.Who would have ever known that k is the speed variable without having to think about the formula the senior dev used to write the program?.Isn’t the point that we should be able to instantly look at a section of code and say “yes, this is where the speed is set; I can add a five minute break here to simulate a stop and be done in the same amount of time it took for the senior dev to tell me what k was.
”A little history on single letter variables: the reason why developers used single letter variables was that autocomplete didn’t exist and developers didn’t want to type as much as they had to.
It also made sure that a programmer didn’t misspell a variable name.
It’s about the equivalent of texting on a phone in the early 2000s; we used lol, c u l8r, < 3 u, and ttyl.
To shorten our words so we only had to push a button 20 times instead of the 100 times if we actually spelled out the full word.
Times have changed; almost every major programming language has autocomplete.
Sometimes it’s OK; for example, temporary variables in if statements where you can still see the declaration five lines away from where you are typing.
This is like using lol on an iPhone.
All is good.
But when you use a as a variable used throughout a class or struct, well then it’s a problem.
I shake my head like I do when I see someone type c u l8r on a smartphone.
But didn’t you have to change the keyboard type to hit the 8 key?.Seems like more work than it’s worth.
Same way with the global single letter variable, you have to figure out what it stands for before you can use it in your changes eight months later; seems like more work than it’s worth.
If you forget everything I said there, just remember to always use descriptive variables and constants.
I’ve been writing out these tutorials from scratch without autocomplete, typing in some pretty long variable names with very little use of copy-paste.
It shouldn’t be a problem for you to give your variables descriptive names with autocomplete.
SummaryToday we covered a few principles to keep in mind, import statements to bring in code from other files and modules, basic file and project structure, and PEP standards to give you a roadmap to what you should be achieving in your code.
I highly suggest that you go back through your previous examples and try to make them beautiful.
I will be the first to tell you that making your code clean can be difficult.
My code is nowhere near the cleanliness that I would hope for it, but it works.
This is one of those skills that you only get good at the more you do it.
Suggested scanningI said scanning not reading; reading is ideal, but you won’t remember half of it at first.
Pick out the important parts that you identify with and start there.
PEP 8 — Style Guide for Python CodeThe official home of the Python Programming Languagewww.
organdPEP 257 — Docstring ConventionsThe official home of the Python Programming Languagewww.
orgThere are other PEPs out there, but I’ll leave that up to you if you want to look at them.
What’s Next?According to my Swift articles, we cover async.
I think we have more important things to cover before we get to asynchronous programming.
While it is fun to do multiple things at one time, I think it would be better if we covered virtual environments and PyPI first.
While I mentioned that I would not be covering external modules, I didn’t say that I wouldn’t cover how to obtain them, so virtual environments and obtaining Python packages will be covered in the next article.
.. More details