Python does not include a static keyword like Java or C++. Instead, class attributes (also called class-level variables) fulfill a similar role. These attributes belong to the class itself, not to any specific instance, and are shared across all objects of that class. This article explains how to define and use class attributes, contrasts them with instance attributes, and demonstrates practical use cases.
Defining and Accessing Class Attributes
Class attributes are defined directly in side a class body, outside any method. They can be accessed via the class name or through an instance (though modifying them through an instance can be misleading).
class Account:
default_currency = "USD"
def __init__(self, owner, balance):
self.owner = owner
self.balance = balance
# Access via class
print(Account.default_currency) # Output: USD
# Access via instance
acc = Account("Alice", 1000)
print(acc.default_currency) # Output: USD
# Modification via class affects all future accesses
Account.default_currency = "EUR"
print(acc.default_currency) # Output: EUR
If you assign a value to acc.default_currency, Python creates a new instance attribute that shadows the class attribute. That instance will no longer see the original class-level value. This is a common pitfall.
Class Attribute vs Instance Attribute
The key differences revolve around storage, scope, and lifetime:
- Storage: Class attributes reside in the class’s namespace; instance attributes live in the instance’s namespace.
- Access: Class attributes can be read via
ClassName.attrorinstance.attr; instance attributes can only be accessed through an instance. - Lifetime: Class attributes exist as long as the class is loaded; instance attributes are created and destroyed with the instance.
- Shared mutability: Changes to a mutable class attribute (e.g., a list) are visible to all instances; changes to an instance attribute only affect that instance.
Practical Use Cases
1. Constants Shared by All Instances
Class attributes are ideal for constants that should apply to every instance. For example, a geometry library might define a conversion factor.
class Circle:
PI = 3.1415926535
def __init__(self, radius):
self.radius = radius
def circumference(self):
return 2 * Circle.PI * self.radius
c = Circle(10)
print(c.circumference()) # 62.83185307
2. Tracking Instance Count
A class attribute can serve as a counter incremented each time a new object is created.
class Widget:
_counter = 0
def __init__(self, name):
self.name = name
Widget._counter += 1
@classmethod
def total_count(cls):
return cls._counter
a = Widget("first")
b = Widget("second")
print(Widget.total_count()) # Output: 2
3. Default Configuration in Factory Patterns
Factory‑like class methods often rely on class attributes to supply default settings.
class LoggerFactory:
_default_format = "basic"
@classmethod
def create(cls, fmt=None):
if fmt is None:
fmt = cls._default_format
# Simulate building a logger with the given format
return f"Logger with format: {fmt}"
print(LoggerFactory.create()) # basic
print(LoggerFactory.create("json")) # json
4. Thread‑Safe Global State (with caution)
Class attributes can hold mutable state shared across threads. Python’s Global Interpreter Lock (GIL) ensures atomic operations on simple types, but complex data structures may require explicit locking.
import threading
class SharedCounter:
_lock = threading.Lock()
_value = 0
@classmethod
def increment(cls):
with cls._lock:
cls._value += 1
@classmethod
def get_value(cls):
return cls._value
# Use in multiple threads safely
for _ in range(5):
threading.Thread(target=SharedCounter.increment).start()
print(SharedCounter.get_value())
Important Nuances
When you modify a class attribute via instance.attr = value, Python creates an instance attribute that shadows the class attribute. This can lead to unexpected results if you intended to share the change. Always modify class attributes via the class name or a classmethod.
class Demo:
items = []
d1 = Demo()
d2 = Demo()
d1.items.append("x") # This modifies the shared list (good for mutable)
print(d2.items) # Output: ['x']
d1.items = ["y"] # Creates a new instance attribute; d2 unaffected
print(d2.items) # Still ['x']
In summary, class attributes in Python effectively implement static‑like variables. They are stored at the class level, shared by all instances, and best used for constants, counters, default configurations, or shared mutable state where synchronization is carefully managed.