Common Python Problems - [10] Class Variables

By JoeVu, at: 2023年1月19日11:50

Estimated Reading Time: 7 min read

None
None

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.