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.

How to create a static variable in Python
Published on: 
Wed
Mar 25, 2026
Updated on: 
Wed
Mar 25, 2026
The Replit Team

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_var attribute 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 variable PI.
  • 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 before MyClass is finalized.
  • It intercepts the class's attributes—held in the attrs dictionary—and injects STATIC_VAR directly.
  • This means any class using StaticMeta automatically 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 makes x behave 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_value remains 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 = 1 doesn'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 members list
  • 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 members list, that change instantly appears in all other instances. While sometimes intentional, this often causes unexpected side effects.
  • Shared data dictionary across parent and child classes
  • Inheritance can be tricky. When a child class inherits a mutable class variable, like a data dictionary, 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 to Logger.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.

Get started free

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.

Get started for free

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.