Python Static Variable In A Class

12 min read

Python Static Variable in a Class: A Complete Guide

When working with classes in Python, understanding how data is shared across instances is crucial for writing efficient and maintainable code. So unlike instance variables that belong to individual objects, a static variable belongs to the class itself and is shared among all instances. Because of that, one concept that often confuses beginners is the static variable, also known as a class variable. This article will walk you through everything you need to know about Python static variables, from definition and usage to common pitfalls and best practices That's the part that actually makes a difference..

What Is a Static Variable in Python?

A static variable in Python is a variable that is declared inside a class but outside any instance methods. Still, it is not tied to any specific object; instead, it is shared across all instances of that class. When you modify a static variable through one instance, the change is reflected in all other instances as well.

Consider this simple example:

class Car:
    wheels = 4  # This is a static variable

    def __init__(self, brand):
        self.brand = brand  # This is an instance variable

In the code above, wheels is a static variable because it is defined directly within the class body. Every Car object will share the same wheels value unless explicitly overridden.

How to Define a Static Variable

Defining a static variable is straightforward. You simply declare the variable at the class level, outside of any method. Here is the basic syntax:

class ClassName:
    static_variable = value

You can use various data types for static variables, including integers, strings, lists, and dictionaries. On the flip side, because static variables are shared, mutable types like lists and dictionaries require extra caution.

class Company:
    employees = []  # Static variable using a list
    name = "TechCorp"  # Static variable using a string

Static Variable vs Instance Variable

Understanding the difference between a static variable and an instance variable is fundamental. An instance variable is defined inside methods, typically within __init__, and is prefixed with self. Worth adding: each instance gets its own copy of the instance variable. A static variable, on the other hand, is shared across all instances It's one of those things that adds up..

Feature Static Variable Instance Variable
Defined Inside class, outside methods Inside methods, usually __init__
Belongs to The class Individual instances
Shared? Yes No
Accessed via ClassName.Because of that, variable or self. variable `self.

Accessing Static Variables

You can access a static variable in two ways: through the class name directly or through an instance of the class Most people skip this — try not to..

class Dog:
    species = "Canine"

# Accessing via class name
print(Dog.species)  # Output: Canine

# Accessing via instance
dog1 = Dog()
print(dog1.species)  # Output: Canine

Both approaches return the same value because the static variable is stored in the class's namespace, not in the instance's namespace.

Modifying Static Variables

Modifying a static variable requires careful attention. If you change it through the class name, the change affects all instances. If you assign a value to the variable using self, you create a new instance variable that shadows the static variable for that particular instance only Small thing, real impact..

class Counter:
    count = 0

    def __init__(self):
        Counter.count += 1

a = Counter()
b = Counter()
c = Counter()

print(Counter.count)  # Output: 3

In the example above, Counter.count is incremented each time a new instance is created. This demonstrates how static variables can be used to track shared state.

That said, if you do this inside a method:

class Counter:
    count = 0

    def increment(self):
        self.count += 1  # This creates an instance variable!

The first time increment is called, Python looks for count on the instance, does not find it, then looks on the class and finds 0. Subsequent calls modify the instance variable, not the class variable. That said, it then creates a new instance variable count set to 1. This is a common source of bugs.

You'll probably want to bookmark this section Most people skip this — try not to..

Common Use Cases for Static Variables

Static variables are useful in several scenarios:

  • Tracking the number of instances: You can use a static variable to count how many objects of a class have been created.
  • Shared configuration: When all instances need to share the same configuration value, a static variable avoids redundancy.
  • Constants: Static variables can serve as constants that are common to all instances.
  • Caching: Shared caches or lookup tables can be implemented using static variables.
class DatabaseConnection:
    connection_pool = []  # Shared pool for all instances
    max_connections = 10

    def __init__(self):
        if len(DatabaseConnection.connection_pool) < DatabaseConnection.max_connections:
            DatabaseConnection.connection_pool.append(self)

Common Pitfalls and How to Avoid Them

One of the most frequent mistakes with static variables involves mutable default values. Because static variables are shared, modifying a list or dictionary static variable from one instance affects all instances Not complicated — just consistent..

class BadExample:
    shared_list = []  # Dangerous!

    def add_item(self, item):
        self.shared_list.append(item)

obj1 = BadExample()
obj2 = BadExample()
obj1.add_item("hello")
print(obj2.shared_list)  # Output: ['hello'] — unexpected!


To avoid this, either use immutable types for static variables or initialize mutable types inside `__init__` as instance variables.

Another pitfall is shadowing. Always use `ClassName.As mentioned earlier, assigning to `self.Day to day, static_var` creates an instance variable that hides the class variable. static_var` when you intend to modify the shared value.

## Scientific Explanation: How Python Handles Static Variables

Under the hood, Python stores class variables in the class's `__dict__` attribute, while instance variables are stored in each instance's `__dict__`. Practically speaking, when you access `self. variable`, Python first checks the instance's dictionary. If the key is not found, it falls back to the class's dictionary. This lookup order explains why reading a static variable through an instance works, but writing to it through `self` creates a new instance entry.

This changes depending on context. Keep that in mind.

```python
class Example:
    static_var = 100

obj = Example()
print(obj.__dict__)          # {} — no instance variables yet
print(Example.__dict__['static_var'])  # 100

obj.static_var = 200
print(obj.Which means __dict__)          # {'static_var': 200}
print(Example. static_var)    # 100 — unchanged!


This behavior confirms that assignment through an instance creates a new key in the instance dictionary rather

Beyond the basic mechanics, there are several patterns and safeguards that make static (class) variables useful without inviting subtle bugs. Understanding these patterns helps you decide when a class‑level attribute is the right tool and when another approach might be cleaner.

Worth pausing on this one.

### 1. Guarding Mutable Shared State  
When a static variable must be mutable—think of a shared cache, a counter, or a connection pool—you should protect concurrent modifications if your code runs in a multithreaded or multiprocessing environment. A simple lock associated with the class does the job:

```python
import threading

class SafeCache:
    _cache = {}          # shared dictionary
    _lock = threading.Lock()   # class‑level lock

    @classmethod
    def get(cls, key):
        with cls._lock:
            return cls._cache.get(key)

    @classmethod
    def set(cls, key, value):
        with cls._lock:
            cls._cache[key] = value

Using a @classmethod makes the intent explicit: the method operates on the class, not on any particular instance, and the lock guarantees that only one thread can mutate _cache at a time.

2. Immutable Constants with typing.Final

If the static variable truly never changes after class definition, annotate it with Final (available from Python 3.8) and, preferably, give it an immutable value:

from typing import Final

class Config:
    MAX_RETRIES: Final[int] = 5
    TIMEOUT_SECONDS: Final[float] = 30.0

Static analysis tools (mypy, pyright) will then flag any attempt to reassign Config.MAX_RETRIES, catching bugs at development time But it adds up..

3. Avoiding Unintended Shadowing via Properties

Sometimes you want to expose a class variable through an instance attribute but still prevent accidental overwriting. A read‑only property does that:

class Service:
    _version = "1.2.3"

    @property
    def version(self) -> str:
        return self.__class__._version

Now service.version reads the shared value, while service.So naturally, version = "2. 0" raises an AttributeError, making the mistake obvious early.

4. Module‑Level Alternatives

For values that are truly global—like application‑wide settings or a singleton registry—consider defining them at the module level instead of inside a class. Module attributes are naturally shared, and you avoid the class‑lookup overhead altogether:

# settings.py
MAX_WORKERS = 4
FEATURE_FLAGS = {"new_ui": True}

Other modules import settings and read/write these names directly. This pattern is common in frameworks (Django’s settings.This leads to py, Flask’s app. config) and keeps the class namespace focused on behavior rather than data.

5. Using __init_subclass__ for Per‑Class Static Data

When you need each subclass to have its own copy of a static‑like attribute (e.g., a default configuration that can be overridden per subclass), __init_subclass__ offers a clean hook:

class BaseProcessor:
    _default_config = {"timeout": 10}

    def __init_subclass__(cls, **kwargs):
        super().Even so, __init_subclass__(**kwargs)
        # Give each subclass its own independent dict
        cls. _default_config = dict(cls.

class FastProcessor(BaseProcessor):
    _default_config["timeout"] = 2   # affects only FastProcessor

print(BaseProcessor._default_config)  # {'timeout': 10}
print(FastProcessor._default_config)  # {'timeout': 2}

This technique sidesteps the shared‑mutability pitfall while retaining the convenience of class‑level defaults.

6. Caching with functools.lru_cache or weakref.WeakValueDictionary

If the goal is to cache expensive computations across instances, consider using a decorator that stores results in a function‑level cache rather than a class variable:

from functools import lru_cache

class MathHelper:
    @staticmethod
    @lru_cache(maxsize=128)
    def factorial(n: int) -> int:
        return 1 if n < 2 else n * MathHelper.factorial(n - 1)

The cache lives on the function object, is automatically bounded, and avoids manual list/dict management. For cases where you want the cache to expire when instances are no longer referenced, a WeakValueDictionary can hold references without preventing garbage collection Which is the point..

Best‑Practice Checklist

Situation Recommended Approach
Immutable constant shared by all instances Class attribute annotated with Final (or module‑level constant)
Mutable counter or simple flag

Here's a thinking process:

  1. Analyze User Input:
  • User wants me to continue an article naturally.
  • I must not repeat previous text.
  • Must finish with a proper conclusion.
  • The provided text ends mid-table: "| Mutable counter or simple flag |"
  1. Identify the Task:
  • Complete the article based on the established pattern/style.
  • Fill in the missing table row and any remaining content.
  • Ensure a proper conclusion at the end.
  • No repetition of the given text.
  1. Scan the Provided Text:
  • It's an article about Python class design patterns.
  • Sections: Module-level settings, __init_subclass__ for per-class static data, caching with lru_cache/WeakValueDictionary, and a "Best-Practice Checklist" table.
  • The table has rows for various situations, and the last visible row is incomplete: "| Mutable counter or simple flag |"
  • I need to complete that row and possibly add more rows if the table was meant to have more, but I should work with what's there and add a conclusion.

Let me outline the missing parts:

  • Complete the table row for "Mutable counter or simple flag"
  • Possibly add remaining table rows if the structure suggests more, but I'll keep it concise and finish the table logically.
  • Add a conclusion paragraph that wraps up the article.

Looking at the table structure, it has two columns: "Situation" and "Recommended Approach". But the rows shown:

  1. Immutable constant shared by all instances -> Class attribute annotated with Final (or module-level constant)

I should complete that row. I could also add a few more common situations to make the table complete, but I need to be careful not to hallucinate too much or deviate from the style. I'll complete the obvious one, and maybe add one or two more that fit naturally, then conclude Worth knowing..

Actually, looking at the prompt: "Continue the article without friction. Do not repeat previous text. Finish with a proper conclusion." I should continue from where it left off, which is the table row. I'll complete the table, then add a conclusion.

Let me draft the completion:

"| Mutable counter or simple flag | Module-level variable or dedicated class with clear mutator methods |"

Or maybe: "| Mutable counter or simple flag | Instance attribute with a property setter, or a dedicated manager object |"

Given the context of the article discussing class vs module level, and __init_subclass__, I'll keep it consistent. I'll complete the table with a few more relevant rows, then conclude.

Actually, I should check if the table has a fixed number of rows. The markdown shows a table with a header row and data rows. I'll complete the incomplete row and add a couple more that fit the theme, then conclude Took long enough..

And yeah — that's actually more nuanced than it sounds.

Let me think of natural completions:

  • "Class attribute with mutable default" -> caution about shared references, use __init_subclass__ or instance init
  • "Per-subclass configuration" -> __init_subclass__ pattern already covered
  • "Thread-safe counters" -> threading.Lock or atomic operations

But I don't want to overstuff. I'll complete the one row, maybe add a second if it feels natural, then conclude.

Actually, re-reading the user's prompt: "Continue the article without friction. Do not repeat previous text. Finish with a proper conclusion." I need to output the continuation starting right after the given text. The given text ends with:

| Mutable counter or simple flag            |               ```
Wait, it ends with `| Mutable counter or simple flag            |` and then nothing? Actually the code block might have ended. Think about it: let me look: the last line is `| Mutable counter or simple flag            |` and then the markdown table might continue or end. Now, the user's text shows:
Situation Recommended Approach
Immutable constant shared by all instances Class attribute annotated with Final (or module‑level constant)
Mutable counter or simple flag ```
It seems the table row for "Mutable counter or simple flag" is incomplete, missing the approach column. The might be from the previous code block? Now, actually, looking at the structure, the is likely closing a code block from earlier? No, the text shows:
| Mutable counter or simple flag            |               ```
Wait, the user's message formatting: 

| Mutable counter or simple flag | Actually, it's: `| Mutable counter or simple flag |` then on a new line might be part of the article?

The user's text ends with:

| Mutable counter or simple flag            |               ```

Hmm, the might be a markdown code block delimiter that was mistakenly included, or it's part of the table. Actually, looking at the overall structure, the "Best‑Practice Checklist" is a markdown table. The row ends with `| Mutable counter or simple flag |` and then there's a on the next line?

| Mutable counter or simple flag            |               ```

I think the is actually the start of a code block that was supposed to be after the table, but it's tangled. Let me look at the very end: ` followed by a new line with the table header again

Out Now

Recently Shared

Others Liked

While You're Here

Thank you for reading about Python Static Variable In A Class. We hope the information has been useful. Feel free to contact us if you have any questions. See you next time — don't forget to bookmark!
⌂ Back to Home