Common Python Problems - [10] Class Variables
By JoeVu, at: 2023年1月19日11:50
Class variables are variables that are shared among all instances of a class in Python. While they can be useful, they can also cause some unexpected behavior and mistakes if not used correctly. In this article, we will discuss pros and cons of Class Variables.
1. Class Variables definition and use cases
A class variable is a variable that is defined within a class and is shared by all instances of the class. It is a variable that belongs to the class and not to any particular instance of the class. Class variables are defined outside of any method within the class, and they are accessed using the class name followed by the variable name.
Class variables are often used in Python
- When there is data that needs to be shared across all instances of a class. For example, a Car class might have a num_wheels class variable that is set to 4, since all cars have four wheels.
- Class variables can also be used to keep track of certain data that applies to the entire class, such as a count of the number of instances of the class that have been created.
2. Class Variables Advantages
2.1 Keeping track of data that is shared among all instances of a class
Consider this example below
class Car:
num_of_cars = 0
def __init__(self, make, model):
self.make = make
self.model = model
Car.num_of_cars += 1
car1 = Car("Toyota", "Camry")
car2 = Car("Honda", "Accord")
print(Car.num_of_cars) # Output: 2
In the example above, num_of_cars is a class variable that is incremented every time a new instance of the Car class is created. It keeps track of the total number of cars created.
2.2 Storing constant values that are related to the class
Look at this example below
class Circle:
pi = 3.14159
def __init__(self, radius):
self.radius = radius
def circumference(self):
return 2 * Circle.pi * self.radius
circle1 = Circle(5)
print(circle1.circumference()) # Output: 31.4159
In the example above, pi is a class variable that stores the value of Pi. It's used in the circumference() method to calculate the circumference of a circle.
2.3 Creating class-level attributes that are used by class methods
Let check this example
import logging
class Bank:
logger = logging.getLogger(__name__)
accounts = []
@classmethod
def add_account(cls, account):
cls.logger.info(f"Adding account: {account}")
cls.accounts.append(account)
bank1 = Bank()
bank2 = Bank()
bank1.add_account("Savings")
bank2.add_account("Checking")
print(Bank.accounts) # Output: ['Savings', 'Checking']
In the example above, logger and accounts are class variables that are used in the add_account() class method. logger is a logger object that logs information about the accounts being added, and accounts is a list that stores the account names.
2.4 Implementing a singleton pattern where only one instance of a class is created
Look at this example bellow
class Singleton:
instance = None
def __new__(cls):
if cls.instance is None:
cls.instance = super().__new__(cls)
return cls.instance
s1 = Singleton()
s2 = Singleton()
print(s1 == s2) # Output: True
In the example above, instance is a class variable that stores a reference to the only instance of the Singleton class. The __new__() method checks if an instance has already been created and returns it if it exists, or creates a new instance if it doesn't. This ensures that only one instance of the class is ever created.
3. Class Variables Disadvantages
3.1 Unintended changes to the value of a class variable can affect all instances of the class
Consider the example below
class Dog:
num_of_legs = 4
dog1 = Dog()
dog2 = Dog()
dog1.num_of_legs = 3
print(dog1.num_of_legs) # Output: 3
print(dog2.num_of_legs) # Output: 4
In the example above, num_of_legs is a class variable that stores the number of legs for all instances of the Dog class. However, when dog1 sets its num_of_legs attribute to 3, it creates a new instance variable with the same name that shadows the class variable. This means that dog1 will now have 3 legs, but dog2 will still have the default 4 legs. This behavior can be confusing and lead to unintended bugs.
3.2 Class variables can be modified by any instance of the class or even external code
Look at this example
class Bank:
balance = 0
bank1 = Bank()
bank2 = Bank()
bank1.balance = 1000
Bank.balance = 2000
print(bank1.balance) # Output: 1000
print(bank2.balance) # Output: 2000
In the example above, balance is a class variable that stores the balance for all instances of the Bank class. However, both bank1 and bank2 can modify the class variable directly, which can lead to unexpected behavior. Additionally, external code can also modify the class variable, which can make it difficult to track down bugs.
3.3 Class variables can have unintended consequences when subclassing
There is a class Animal below
class Animal:
species = "Unknown"
class Dog(Animal):
species = "Canine"
class Cat(Animal):
pass
print(Dog.species) # Output: "Canine"
print(Cat.species) # Output: "Unknown"
In the example above, species is a class variable that is inherited by the Dog and Cat subclasses of Animal. However, when Cat doesn't define its own species attribute, it inherits the default value of Unknown from Animal. This can lead to unexpected behavior when subclassing and can make it difficult to maintain code.
4. Conclusion
In conclusion, class variables are a useful tool in Python for keeping track of data that is shared among all instances of a class. However, it's important to be aware of the potential mistakes that can arise when working with class variables. Modifying class variables using instance variables or methods using self can cause unexpected behavior and should be avoided.