Everybody must start at some point, it is human to make mistakes, but what about a better work flow? it is what this post is about!
1st – Classes “Self” in methods
For most functions to work on python, we need some preparation, what I mean is not like an exercise but more like… knowing what you are doing.
So let’s get to the point, when making a function inside a class, their methods must have “self” on the first argument, it can be any variable name, really, but it will point to the class “self” making the readability bad, I mean, its not me who am saying about using self as a class, it is the own python dev team.
As a resume do this:
class MyClass: def __init__(self, value): self.value = value # Use `self` to reference instance attributes def display(self): print(self.value) # `self` is needed to access the instance attribute
Not this monstrosity:
class MyClass: def __init__(my_instance, value): my_instance.value = value # using a different name instead of "self" def display(my_instance): print(my_instance.value) # referring to the instance with "my_instance"
and even less this TypeError:
class MyClass: def __init__(self, value): self.value = value def display(): # Forgot `self` print(self.value) obj = MyClass(10) obj.display() # TypeError: display() takes 0 positional arguments but 1 was given
2nd – Mutable Default argument.
Well in python, when working with lists of dictionaries, if you set it as a default argument it is not set each time you start the function, but only the first time, what does it mean? In this case instead of giving the right answer, it can lead to unexpected results, like so:
def add_element(lst=[]): lst.append(1) return lst print(add_element()) # Output: [1] print(add_element()) # Output: [1, 1] -- Unexpected! print(add_element()) # Output: [1, 1, 1] -- The default list is shared
How to avoid it? it is fairy simple don’t use a list! use anything besides that and then make a list:
def add_element(lst=None): if lst is None: lst = [] # Create a new list if no list is provided lst.append(1) return lst print(add_element()) # Output: [1] print(add_element()) # Output: [1] -- Each call uses a fresh list print(add_element([10, 20])) # Output: [10, 20, 1] -- Custom input is handled properly
3rd – Re-using the same variables
A simple example of re-using one variable for the same purpose is like this one, that could lead to confusion:
# Case where variable names are re-used in the same scope x = 10 # Initial assignment x = 20 # Re-assignment with a different value print(x) # Output: 20 -- Original value is overwritten
But that is fairly obvious, and doesn`t matter too much, but on functions that is another way:
x = 10 # Outer scope variable def update_x(): x = 20 # New variable in inner scope print("Inner x:", x) # Output: 20 -- This is a different variable from the outer one update_x() print("Outer x:", x) # Output: 10 -- The outer scope variable is unchanged
Well, that is an obvious example, but as your code grows more and more, it will affect productivity a lot, especially if the function name is “updade_X” and it is directing to a function variable and not a global you have.