I'm on the verge of finishing a great course called "Clean Code" by Maximilian Schwarzmüller.
I stumbled upon the concept of writing good and clean functions and wanted to share some valuable principles which will be especially handy for beginners. So you can see this blog post as an informative summary of that course section.
Let's dive in by looking at eight main points:
Functions are like little helpers in our code that we can repeatedly use to do something specific.
A good function should only do one thing. It shouldn't be trying to do too much all at once.
We can also think about levels of abstraction, which means how high or low we are in terms of how much detail we deal with.
High-level operations are like big-picture ideas that we can express with a few words or phrases, while low-level operations are more detailed and specific.
A good function should only be responsible for work that is one level of abstraction below its name. This means that if the function has a descriptive name, it should only deal with operations that are one level more detailed than that name suggests. Use verbs or short phrases with adjectives to name functions correctly (e.g.,
send_data
)When we write a function, we should give it a descriptive name that tells us what it does.
If a function is doing too much, we can break it down into smaller functions that each does one specific thing.
By breaking down functions into smaller pieces, we can make our code easier to understand and maintain.
Consider this simple function that calls collect_coin
and adds 1 to the player's coin count.:
def collect_coin(player: dict) -> None:
player['coins'] += 1
Here's an example of a lousy function that's trying to do too much:
def play_game(player: dict, enemies: list, walls: list) -> None:
for enemy in enemies:
if player['rect'].colliderect(enemy['rect']):
player['hitpoints'] -= 1
if player['hitpoints'] == 0:
player['game_over']()
for wall in walls:
if player['rect'].colliderect(wall['rect']):
player['move_back']()
player['coins'] += 1
The function calls play_game
, which checks for collisions with enemies and walls, adjusts the player's hitpoints and coin count, and even ends the game if the player's hitpoints reach zero.
To illustrate, we can fix the play_game
function by breaking it down into smaller functions that each does one specific thing.
def check_for_enemy_collision(player: dict, enemies: list) -> None:
for enemy in enemies:
if player['rect'].colliderect(enemy['rect']):
player['hitpoints'] -= 1
if player['hitpoints'] == 0:
player['game_over']()
def check_for_wall_collision(player: dict, walls: list) -> None:
for wall in walls:
if player['rect'].colliderect(wall['rect']):
player['move_back']()
def collect_coin(player: dict) -> None:
player['coins'] += 1
def play_game(player: dict, enemies: list, walls: list) -> None:
check_for_enemy_collision(player, enemies)
check_for_wall_collision(player, walls)
collect_coin(player)
Side Note:
rect
and colliderect
are attributes and methods of a Rect
object, which is a built-in class in the pygame
module used for handling rectangular areas.
So, the expression player.rect.colliderect(enemy.rect)
checks if the player is hitting the enemy by comparing the position and size of the two objects using the colliderect
method.
Top comments (6)
Hi Max, I really like your post, it contains some sound advice.
One lesson I think we can take from the Functional Programming world is to "keep functions pure". Try to restrict functions to effecting only the data it is passed as parameters to avoid side-effects and make them more testable.
Regards, Tracy
Thanks, Tracy! Glad you found it helpful.
For me I could care less about how beautiful or clean the code is, I'm more concerned with, does the code do what it's suppose to do and how fast does it do it. I would rather have ugly hideous looking code that is blistering fast, over beautiful clean code that is clunky and slow.
Beauty or Cleanness of code is in the eyes of the beholder.
What's the point of blistering fast code if the next dev needs a week to understand it in its hideous glory and then adds x bugs while changing it. Development is a team effort, you keep the code clean not for aesthetic reasons but for your colleagues.
Whats the point of clean code if your employer (or your own company) can't stay in business because everyone is flocking to the competition because their program runs faster?
Comments are your friend.
Not all development happens in a team setting.
It seems to me that the original play_game() just need a bit of adequate comments, which is sadly missing even after breaking down into multiple functions.