How to access a class variable in Python
Learn how to access class variables in Python. This guide covers different methods, tips, real-world applications, and common error debugging.

In Python, class variables are shared by all instances of a class. They're ideal for storing data common to every object, and correct access is key to predictable code.
In this article, you'll learn the main techniques to access class variables. You'll find practical tips, explore real-world applications, and get debugging advice to help you master this concept.
Accessing class variables using the class name
class MyClass:
class_var = 10
# Access class variable directly through the class
print(MyClass.class_var)--OUTPUT--10
The most straightforward way to access a class variable is by using the class name directly. In the example, MyClass.class_var retrieves the value of class_var from the MyClass namespace. This approach is clean and explicit, making it clear you're interacting with a variable shared across all potential instances of the class.
This method is particularly useful because it doesn't require you to create an instance of the class first. The variable is tied to the class definition itself, so you can read or modify it anytime after the class is defined. It's the standard way to handle data that's constant for all objects of a class.
Basic class variable access techniques
While using the class name is the most direct route, Python offers more flexible ways to work with class variables through instances and methods.
Accessing class variables through instances
class MyClass:
class_var = "I'm a class variable"
obj = MyClass()
print(obj.class_var) # Access via instance
print(MyClass.class_var) # Access via class--OUTPUT--I'm a class variable
I'm a class variable
You can also access class variables directly from an instance, like obj.class_var. When you do this, Python's attribute lookup mechanism kicks in:
- It first checks if an instance variable named
class_varexists on theobjinstance. - If not, it looks for the variable on the class,
MyClass, and finds it there.
This is why both print statements yield the same result. It's a convenient shortcut, but be aware that assigning a new value to obj.class_var creates an instance variable that shadows the class variable for that object only.
Modifying class variables
class Counter:
count = 0
obj1 = Counter()
obj2 = Counter()
Counter.count += 1 # Modify the class variable
print(f"obj1.count: {obj1.count}, obj2.count: {obj2.count}")--OUTPUT--obj1.count: 1, obj2.count: 1
To change a class variable for all instances, you modify it using the class name, as seen with Counter.count += 1. This action alters the variable at the class level, ensuring the change is reflected across all objects derived from it.
- Because both
obj1andobj2access the same sharedcountvariable, they both see the updated value. - This makes class variables perfect for managing state or data that needs to be consistent across all instances.
Using @classmethod to access class variables
class Student:
total_students = 0
@classmethod
def add_student(cls):
cls.total_students += 1
return cls.total_students
print(Student.add_student())
print(Student.add_student())--OUTPUT--1
2
The @classmethod decorator offers a more formal way to work with class variables. It binds a method to the class, not the instance, passing the class itself as the first argument—conventionally named cls.
- In the
add_studentmethod,clsrefers to theStudentclass. - Using
cls.total_studentsensures you're modifying the shared class variable directly.
This approach is cleaner than hardcoding the class name inside the method, making your code more maintainable and less error-prone when dealing with class-level state.
Advanced class variable techniques
While direct access and class methods handle many use cases, more complex scenarios call for advanced techniques involving inheritance, descriptors, and metaclasses.
Working with class variables in inheritance
class Parent:
shared_data = ["Parent data"]
class Child(Parent):
def add_data(self, data):
self.__class__.shared_data.append(data)
child = Child()
child.add_data("Child data")
print(Child.shared_data)
print(Parent.shared_data)--OUTPUT--['Parent data', 'Child data']
['Parent data', 'Child data']
When a child class inherits a class variable, it’s accessing the same variable as the parent. Because shared_data is a mutable object—in this case, a list—modifications made through the Child class also affect the Parent.
- The
add_datamethod usesself.__class__to ensure it modifies the variable at the class level, not an instance level. - Since both classes point to the same list in memory, appending an item via the
Childclass updates the list for both. This is whyParent.shared_dataalso reflects the change.
Using descriptors with class variables
class ClassVarDescriptor:
def __get__(self, instance, owner):
return f"Accessed via: {owner.__name__}"
class MyClass:
class_var = ClassVarDescriptor()
print(MyClass.class_var)
print(MyClass().class_var)--OUTPUT--Accessed via: MyClass
Accessed via: MyClass
Descriptors give you precise control over how class attributes are accessed. When you define a class with a __get__ method, it becomes a descriptor that intercepts attribute lookups, letting you run custom code instead of just returning a value.
- In this example, accessing
MyClass.class_varautomatically triggers the__get__method inside theClassVarDescriptor. - The
ownerparameter always receives the class itself—in this case,MyClass. That’s why the output is the same whether you access the variable through the class or an instance.
Class variables with metaclasses
class Meta(type):
counter = 0
def __new__(mcs, name, bases, attrs):
mcs.counter += 1
attrs['class_id'] = mcs.counter
return super().__new__(mcs, name, bases, attrs)
class MyClass(metaclass=Meta):
pass
print(f"Class ID: {MyClass.class_id}, Meta counter: {Meta.counter}")--OUTPUT--Class ID: 1, Meta counter: 1
Metaclasses act as factories for classes, letting you run code during class creation itself. By setting metaclass=Meta, you're telling Python to use the Meta class to construct MyClass. This triggers the __new__ method within Meta before MyClass even exists.
- The metaclass maintains its own class variable,
counter. - Each time a new class is defined using
Meta, thecounteris incremented and a newclass_idattribute is injected into that class. - This is a powerful way to automatically manage class-level data across multiple classes.
Move faster with Replit
Replit is an AI-powered development platform where all Python dependencies come pre-installed, so you can skip setup and start coding instantly. This lets you go from learning a concept to applying it in seconds, without any environment configuration.
Mastering class variables is one thing, but building a full application is another. That's where Agent 4 comes in. Instead of piecing together techniques, you can describe the app you want, and Agent will build it. For example, you could create:
- A real-time dashboard that uses a class variable to track and display the total number of active user sessions.
- An application-wide configuration manager where components inherit a base class to access shared settings, like a theme or API key.
- A plugin system that uses a metaclass to automatically register and assign a unique ID to each new plugin class you define.
Simply describe your app, and Replit will write the code, test it, and fix issues automatically, all within your browser.
Common errors and challenges
Even with the basics down, a few common pitfalls can trip you up when working with class variables in Python.
Avoiding confusion between class and instance variables
A frequent source of confusion is the difference between class and instance variables. When you assign a value to an attribute on an instance, like obj.class_var = "new value", you create an instance variable that "shadows" the class variable for that object only. Other instances remain unaffected, which can lead to subtle bugs if you intended to change the shared value.
Beware of mutable class variables
Mutable class variables, such as lists or dictionaries, introduce another layer of complexity. Since all instances share the exact same mutable object, a change made from one instance will be visible across all others. This is useful for shared state but can cause unexpected side effects if you wanted each instance to have its own independent copy. For independent copies, it's best to initialize mutable attributes in the __init__ method.
Understanding class variable inheritance
Inheritance also presents challenges. If a child class modifies a mutable class variable inherited from a parent, the change affects the parent as well because they share the same object. However, if the child class reassigns the variable entirely, it creates a new class variable that shadows the parent's, breaking the link for that attribute. Understanding this difference between mutation and reassignment is crucial.
Avoiding confusion between class and instance variables
One of the most common pitfalls is accidentally creating an instance variable that "shadows" the class variable. This often happens when you try to modify the variable from an instance, leading to unexpected behavior. The following code demonstrates this exact scenario.
class Counter:
count = 0
counter1 = Counter()
counter2 = Counter()
counter1.count += 1 # This creates an instance variable
print(f"counter1.count: {counter1.count}")
print(f"counter2.count: {counter2.count}")
print(f"Counter.count: {Counter.count}")
The expression counter1.count += 1 creates an instance variable on counter1 instead of modifying the shared class variable. This is why counter2.count and Counter.count remain zero. See the correct approach in the code below.
class Counter:
count = 0
counter1 = Counter()
counter2 = Counter()
Counter.count += 1 # Modify the class variable directly
print(f"counter1.count: {counter1.count}")
print(f"counter2.count: {counter2.count}")
print(f"Counter.count: {Counter.count}")
The correct approach is to modify the variable directly on the class: Counter.count += 1. This updates the single, shared count variable that all instances look up to. As a result, both counter1 and counter2 see the updated value of 1. This distinction is crucial whenever you're managing a state that needs to be consistent across all objects of a class, like a global counter or a shared configuration setting.
Beware of mutable class variables
Mutable class variables, like lists or dictionaries, can cause unexpected behavior. Since all instances share the same object in memory, a modification made through one instance affects all others. It's a common pitfall, as the code below demonstrates.
class Group:
members = [] # Mutable class variable
group1 = Group()
group2 = Group()
group1.members.append("Alice")
print(f"group1.members: {group1.members}")
print(f"group2.members: {group2.members}")
Since group1 and group2 share the same members list, appending "Alice" through one instance modifies the list for both. The following code demonstrates the proper way to handle this scenario.
class Group:
def __init__(self):
self.members = [] # Instance variable
group1 = Group()
group2 = Group()
group1.members.append("Alice")
print(f"group1.members: {group1.members}")
print(f"group2.members: {group2.members}")
The fix is to initialize the list inside the __init__ method. By defining self.members = [] in the constructor, you ensure every new instance of the Group class gets its own unique list. This effectively turns members into an instance variable instead of a shared class variable. Now, changes made to one group’s list won't affect another. This is the go-to pattern when each object needs its own independent, mutable state.
Understanding class variable inheritance
Inheritance can create tricky situations with mutable class variables like dictionaries. When a child class modifies a shared setting, the change often propagates back to the parent class—a side effect you might not expect. The following code demonstrates this problem.
class Parent:
settings = {"debug": False}
class Child(Parent):
pass
Child.settings["debug"] = True
print(f"Child settings: {Child.settings}")
print(f"Parent settings: {Parent.settings}")
The Child class directly modifies the inherited settings dictionary. Because dictionaries are mutable, both parent and child share the same object, so the change reflects in both. The code below shows how to give the child its own settings.
class Parent:
settings = {"debug": False}
class Child(Parent):
settings = dict(Parent.settings) # Create a copy
Child.settings["debug"] = True
print(f"Child settings: {Child.settings}")
print(f"Parent settings: {Parent.settings}")
To prevent the child class from altering the parent's dictionary, you create a shallow copy. The line settings = dict(Parent.settings) gives the Child class its own version of the settings dictionary. Now, when you modify Child.settings, the original Parent.settings remains untouched. It's a crucial technique when you want subclasses to inherit a base configuration but customize it independently without causing side effects on the parent or other subclasses.
Real-world applications
Beyond avoiding errors, class variables are key to elegant solutions for managing shared state, like application settings and resource pools.
Using class variables for application config settings
Class variables are perfect for creating a centralized config object that holds settings accessible throughout your entire application.
class AppConfig:
debug_mode = False
max_connections = 100
api_version = "v1.0"
# Access and modify configuration settings
print(f"API Version: {AppConfig.api_version}, Debug: {AppConfig.debug_mode}")
AppConfig.debug_mode = True
print(f"Debug now: {AppConfig.debug_mode}")
This approach uses a class as a container for configuration data. You don't need to create an object from AppConfig because the settings are class variables, tied directly to the class itself.
- Accessing a setting is as simple as
AppConfig.api_version. - Modifying a setting, like
AppConfig.debug_mode = True, updates the value globally.
Any part of your code that imports this class can then see the updated configuration, ensuring consistency without passing objects around.
Building a connection pool with @classmethod and class variables
Class variables and @classmethods are ideal for building a connection pool, which improves application performance by reusing expensive resources like database connections.
class DatabasePool:
_connections = []
active_connections = 0
@classmethod
def get_connection(cls):
if not cls._connections:
cls.active_connections += 1
return f"Connection-{cls.active_connections}"
return cls._connections.pop()
@classmethod
def release_connection(cls, conn):
cls._connections.append(conn)
conn1 = DatabasePool.get_connection()
print(f"Got: {conn1}, Active: {DatabasePool.active_connections}")
DatabasePool.release_connection(conn1)
print(f"Released connection to pool: {DatabasePool._connections}")
This DatabasePool class uses class variables to manage a shared set of connections. The get_connection method is the entry point for acquiring a resource, intelligently deciding whether to create a new connection or reuse an old one.
- If the
_connectionspool is empty, it creates a new connection and increments theactive_connectionscounter. - If the pool has available connections, it simply
pop()s one for reuse. - The
release_connectionmethod returns a connection to the_connectionslist, making it available again.
Get started with Replit
Now, turn your knowledge into a real tool. Tell Replit Agent to "build a theme manager class with a class variable for the current theme" or "create a user session counter that uses a class variable."
The Agent writes the code, tests for errors, and deploys your app. Start building with Replit.
Create and deploy websites, automations, internal tools, data pipelines and more in any programming language without setup, downloads or extra tools. All in a single cloud workspace with AI built in.
Create and deploy websites, automations, internal tools, data pipelines and more in any programming language without setup, downloads or extra tools. All in a single cloud workspace with AI built in.


.png)
