How to create a static variable in Python
Learn how to create static variables in Python. Explore different methods, tips, real-world applications, and how to debug common errors.

Python doesn't have a native static keyword, but you can create class-level variables that behave similarly. These variables are shared among all instances of a class, not individual objects.
In this article, you'll explore techniques to implement static variables. You'll find practical tips, see real-world applications, and get advice to debug common issues that arise with class-level data.
Basic class variables
class Counter:
count = 0 # This is a class variable (static)
def __init__(self):
Counter.count += 1
c1 = Counter()
c2 = Counter()
print(f"Total instances created: {Counter.count}")--OUTPUT--Total instances created: 2
In this example, the count variable is defined directly within the Counter class, not inside a method. This placement makes it a class variable, meaning it's shared across all instances. Think of it as a single piece of data that belongs to the class itself.
Each time a new object is created, the __init__ method increments this shared variable using Counter.count. This is why you can track the total number of instances created. It’s a straightforward way to manage state that's relevant to the class as a whole, not just one object.
Alternative approaches
Beyond simple class variables, you can also manage static-like data using class decorators, the @staticmethod decorator, or by working directly with a class's __dict__.
Using class decorators
def add_static_var(cls):
cls.static_var = 42
return cls
@add_static_var
class MyClass:
pass
print(f"Static variable value: {MyClass.static_var}")--OUTPUT--Static variable value: 42
Class decorators offer a dynamic way to add static variables from outside a class definition. The @add_static_var syntax is a clean shortcut that passes MyClass to the add_static_var function, which then modifies it.
- The decorator receives the class as an argument.
- It injects the
static_varattribute directly onto the class object. - Finally, it returns the modified class.
This technique is great for separating concerns or applying the same static data across multiple classes without repeating yourself.
Using the @staticmethod decorator
class MathUtils:
PI = 3.14159 # Static variable
@staticmethod
def get_pi():
return MathUtils.PI
print(f"PI value: {MathUtils.PI}")
print(f"PI via method: {MathUtils.get_pi()}")--OUTPUT--PI value: 3.14159
PI via method: 3.14159
The @staticmethod decorator creates a method that belongs to a class but doesn't operate on an instance. It doesn't receive an implicit first argument like self, so it acts like a regular function that just happens to live inside the class definition.
- In this case,
get_pi()is a static method used to access the class variablePI. - You can call it directly on the class—
MathUtils.get_pi()—without creating an object. - This is ideal for utility functions that are logically connected to the class but don't depend on instance state.
Managing with __dict__
class Config:
pass
# Adding static variables dynamically
Config.APP_NAME = 'MyApp'
Config.VERSION = '1.0.0'
print(f"App: {Config.APP_NAME}, Version: {Config.VERSION}")--OUTPUT--App: MyApp, Version: 1.0.0
You can add static variables to a class even after it’s defined. This approach is highly dynamic, letting you attach data at runtime. By assigning values directly to the class, like Config.APP_NAME = 'MyApp', you make them available across your application without modifying the original class definition.
- This works because Python classes are objects with their own attributes.
- Behind the scenes, you're adding entries to the class’s internal
__dict__. - It’s especially useful for setting up configuration values that are determined when the program starts.
Advanced techniques
If you need more granular control than the previous methods offer, Python’s advanced features like metaclasses, descriptors, and __slots__ provide powerful alternatives.
Implementing with metaclasses
class StaticMeta(type):
def __new__(mcs, name, bases, attrs):
attrs['STATIC_VAR'] = 'I am static!'
return super().__new__(mcs, name, bases, attrs)
class MyClass(metaclass=StaticMeta):
pass
print(f"Static variable via metaclass: {MyClass.STATIC_VAR}")--OUTPUT--Static variable via metaclass: I am static!
Metaclasses are essentially factories for creating classes. By setting metaclass=StaticMeta, you're telling Python to use StaticMeta to construct MyClass. This gives you control over the class creation process itself.
- The metaclass's
__new__method runs beforeMyClassis finalized. - It intercepts the class's attributes—held in the
attrsdictionary—and injectsSTATIC_VARdirectly. - This means any class using
StaticMetaautomatically gets this static variable, making it a powerful way to enforce patterns across multiple classes.
Creating with descriptors
class StaticVar:
def __init__(self, value):
self.value = value
def __get__(self, instance, owner):
return self.value
class MyClass:
x = StaticVar(100)
print(f"Descriptor static variable: {MyClass.x}")--OUTPUT--Descriptor static variable: 100
Descriptors offer fine-grained control over how attributes are accessed. The StaticVar class is a descriptor because it implements the __get__ method. When you access MyClass.x, Python calls this method instead of just returning the StaticVar object itself. This allows you to run custom logic whenever the attribute is read.
- The
__get__method simply returns the value it was initialized with. This effectively makesxbehave like a read-only static variable. - It’s a powerful way to manage attribute behavior without cluttering the main class logic.
Combining with __slots__
class LimitedClass:
__slots__ = [] # Restrict instance attributes
static_value = "I'm a class variable"
obj = LimitedClass()
print(f"Static with slots: {LimitedClass.static_value}")--OUTPUT--Static with slots: I'm a class variable
The __slots__ attribute is a memory optimization tool. When you set __slots__ = [], you're telling Python not to create a __dict__ for each instance, which prevents you from adding new attributes to objects after they're created.
- This restriction only applies to instances. The class variable
static_valueremains perfectly accessible because it’s stored with the class itself, not with any individual object.
Move faster with Replit
Replit is an AI-powered development platform that transforms natural language into working applications. Describe what you want to build, and Replit Agent creates it—complete with databases, APIs, and deployment.
For the static variable techniques we've explored, Replit Agent can turn them into production tools:
- Build a real-time dashboard that uses a class variable to track active user sessions across a web application.
- Create a configuration management utility that dynamically loads settings as static variables, making them globally accessible.
- Deploy a unit conversion library where factors are stored as static variables for efficient, consistent access.
Describe your app idea to Replit Agent, and it’ll write the code, test it, and fix issues automatically, all in your browser.
Common errors and challenges
While powerful, class-level variables can introduce subtle bugs if you're not careful about how you use them.
- Accidentally creating instance variables with
self.count - A common mistake is assigning a value to an instance variable with the same name as a class variable. For example, using
self.count = 1doesn't update the class variable; it creates a new instance variable that "shadows" the class-level one for that object only. This leads to inconsistent state and bugs that are hard to track. - Unexpected behavior with mutable
memberslist - Using a mutable object like a list or dictionary as a class variable means all instances share the exact same object. If one instance modifies a shared
memberslist, that change instantly appears in all other instances. While sometimes intentional, this often causes unexpected side effects. - Shared
datadictionary across parent and child classes - Inheritance can be tricky. When a child class inherits a mutable class variable, like a
datadictionary, it shares that object with the parent. Any changes made by the child class will also alter the parent's dictionary, creating a tight and often undesirable link between them.
Accidentally creating instance variables with self.count
One of the most frequent pitfalls is using self to modify a class variable. When you write an assignment like self.count += 1, you aren't updating the shared class variable. Instead, you're creating a new instance variable that shadows it. The following code demonstrates this behavior.
class Counter:
count = 0
def increment(self):
self.count += 1 # Creates an instance variable!
c1 = Counter()
c1.increment()
print(f"Class count: {Counter.count}, Instance count: {c1.count}")
The assignment self.count += 1 creates an instance-specific count that hides the class variable, leaving the original unchanged. This splits the state. The corrected code below shows how to target the class variable directly.
class Counter:
count = 0
def increment(self):
Counter.count += 1 # Properly modifies the class variable
c1 = Counter()
c1.increment()
print(f"Class count: {Counter.count}")
The fix is to modify the class variable directly using the class name. By using Counter.count += 1, you're telling Python to update the count variable belonging to the Counter class itself, not the instance. This ensures the state remains shared across all objects. Keep an eye out for this whenever you're inside an instance method but need to change a class-level attribute.
Unexpected behavior with mutable members list
When a class variable is a mutable object, like a list, all instances share that single object. This means a modification made by one instance will unexpectedly appear in all others. The following example demonstrates this with a shared members list.
class Group:
members = [] # Shared list for all instances
def add_member(self, member):
self.members.append(member)
team1 = Group()
team2 = Group()
team1.add_member("Alice")
print(team2.members) # Shows ['Alice'] unexpectedly
The members list is shared by all Group instances. When you call team1.add_member("Alice"), you're modifying that single shared list, which is why team2 also sees the change. The following code demonstrates the proper fix.
class Group:
def __init__(self):
self.members = [] # Instance variable instead
def add_member(self, member):
self.members.append(member)
team1 = Group()
team2 = Group()
team1.add_member("Alice")
print(team2.members) # Shows [] as expected
The fix is to initialize the list inside the __init__ method. This makes self.members an instance variable, giving each object its own separate list. Now, adding a member to one group won’t affect any others. This is the standard way to handle instance-specific data. Always be cautious when you see mutable types like lists or dictionaries defined at the class level, as they are shared by default across all instances.
Shared data dictionary across parent and child classes
Inheritance can create subtle bugs with mutable class variables. When a child class modifies a shared dictionary inherited from its parent, the change unexpectedly affects the parent class too. This creates a tight, often unwanted, link between them.
The following example shows this in action. Notice how modifying the data dictionary through the child class also changes the parent's dictionary.
class Parent:
data = {'default': 'value'}
class ChildA(Parent):
pass
ChildA.data['new_key'] = 'new_value'
print(Parent.data) # Contains 'new_key' unexpectedly
Since ChildA inherits the data dictionary, it modifies the same object the parent uses. Both classes reference the same dictionary in memory, creating the unwanted link. The corrected code below shows how to prevent this side effect.
class Parent:
data = {'default': 'value'}
class ChildA(Parent):
data = Parent.data.copy() # Create a separate copy
ChildA.data['new_key'] = 'new_value'
print(Parent.data) # Original data is preserved
The solution is to give the child class its own dictionary. By assigning data = Parent.data.copy(), you create a shallow copy, breaking the link to the parent's data. Now, modifications to ChildA.data won't affect Parent.data because they are separate objects. You should always do this when a child class inherits a mutable attribute like a list or dictionary that you intend to modify independently.
Real-world applications
By avoiding the common pitfalls, you can confidently build tools like a simple logging system or a registry pattern for managing connections.
Implementing a simple logging system
You can use a shared list as a class variable to build a simple logging system that gathers messages from multiple instances into a single, unified collection.
class Logger:
logs = [] # Class variable to store all logs
def __init__(self, name):
self.name = name
def log(self, message):
log_entry = f"[{self.name}] {message}"
Logger.logs.append(log_entry)
logger1 = Logger("System")
logger2 = Logger("Network")
logger1.log("Application started")
logger2.log("Connection established")
print(f"All logs: {Logger.logs}")
The Logger class uses a shared logs list that belongs to the class itself, not to individual objects. This means every instance you create—like logger1 and logger2—writes to the same central list.
- Inside the
log()method, new entries are appended directly toLogger.logs, ensuring all messages are collected in one place. - Each instance can still have its own unique
name, but the log collection remains unified across the entire class.
Building a registry pattern with the get_connection() method
The registry pattern uses a class-level dictionary to keep track of created instances, allowing you to fetch a specific one on demand with a class method like get_connection().
class DatabaseConnection:
_registry = {} # Class variable to store all connections
def __init__(self, db_name):
self.db_name = db_name
DatabaseConnection._registry[db_name] = self
@classmethod
def get_connection(cls, db_name):
return cls._registry.get(db_name)
def execute(self, query):
return f"Executing '{query}' on {self.db_name}"
users_db = DatabaseConnection("users")
products_db = DatabaseConnection("products")
conn = DatabaseConnection.get_connection("users")
result = conn.execute("SELECT * FROM users")
print(result)
This DatabaseConnection class uses a shared _registry dictionary to catalog every connection that's created. When you initialize an object, its __init__ method automatically adds itself to this central dictionary, using the db_name as the key.
- The
get_connection()method, marked as a@classmethod, lets you retrieve any specific connection instance by name directly from the class. - This is useful because you can access a connection from anywhere in your code without having to pass the object around manually.
Get started with Replit
Turn your knowledge into a real tool. Describe your idea to Replit Agent, like "build a currency converter with static exchange rates" or "create a web app that tracks active sessions with 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 & 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.



