
Class and Instance Attributes in Python
Creating a Python class allows you to define two types of attributes: class attributes and instance attributes. These attributes help you structure your data efficiently, catering to different programming needs.
- Class Attributes
These are attributes that every instance of your class shares. Think of them as universal properties or rules that apply to all instances of the class uniformly. If you modify a class attribute, the change impacts every instance, akin to updating the game rules for all players simultaneously. They are set directly within the class body, outside any methods. - Instance Attributes
In contrast to class attributes, instance attributes are unique to each object created from the class. This means that different objects can have their own distinct values for these attributes, which are the personal touches that make each instance stand out. Changing an instance attribute in one object does not affect another. These attributes are typically defined within the __init__ method—the class constructor.
This approach to attributes enables seamless management of both shared and individual properties. For example, while all cars generally have four wheels, the color and brand can vary significantly from one car to another.
Consider a practical example.
Suppose you have a `Car` class. While the number of wheels on each car is consistent, the brand and color can differ.
class Car:
# Class Attribute
wheels = 4
def __init__(self, make, color):
# Instance Attributes
self.make = make # the make can vary
self.color = color # the color can vary
# Create two objects
car1 = Car("Tesla", "red")
car2 = Car("Toyota", "blue")
# Print car details
print(f"The car1 is a {car1.make} colored {car1.color} and has {car1.wheels} wheels")
print(f"The car2 is a {car2.make} colored {car2.color} and has {car2.wheels} wheels")
The 'wheels' attribute is a class attribute, applied consistently across all cars, as every car has four wheels.
Conversely, the 'make' and 'color' are instance attributes that set a red Tesla apart from a blue Toyota.
Here is how the output looks when you run this script:
The car1 is a Tesla colored red and has 4 wheels
The car2 is a Toyota colored blue and has 4 wheels
Changing the 'color' of 'car1' does not affect the 'color' attribute of 'car2'.
class Car:
# Class Attribute
wheels = 4
def __init__(self, make, color):
# Instance Attributes
self.make = make # the make can vary
self.color = color # the color can vary
# Create two objects
car1 = Car("Tesla", "red")
car2 = Car("Toyota", "blue")
# Change the color of car1
car1.color = "white"
# Print car details
print(f"The car1 is a {car1.make} colored {car1.color} and has {car1.wheels} wheels")
print(f"The car2 is a {car2.make} colored {car2.color} and has {car2.wheels} wheels")
The car1 is a Tesla colored white and has 4 wheels
The car2 is a Toyota colored blue and has 4 wheels
As seen here, 'color' is an instance attribute.
Implications of Modifying a Class Attribute via an Instance
While Python permits the alteration of class attributes through instances, doing so actually establishes a new instance attribute for that specific object, effectively overshadowing the original class attribute for that instance.
If you change 'car1.wheels=5', you are essentially creating a new instance attribute 'wheels' for car1, which doesn't impact car2 whose 'wheels' attribute still refers to the original class attribute and remains at 4.
class Car:
# Class Attribute
wheels = 4
def __init__(self, make, color):
# Instance Attributes
self.make = make # the make can vary
self.color = color # the color can vary
# Create two objects
car1 = Car("Tesla", "red")
car2 = Car("Toyota", "blue")
# Change the color of car1
car.wheels = 5
# Print car details
print(f"The car1 is a {car1.make} colored {car1.color} and has {car1.wheels} wheels")
print(f"The car2 is a {car2.make} colored {car2.color} and has {car2.wheels} wheels")
The car1 is a Tesla colored red and has 5 wheels
The car2 is a Toyota colored blue and has 5 wheels
Class and Instance Attribute Namespaces
Class attributes are stored within the class’s __dict__ dictionary.
In contrast, instance-specific attributes are found in the instance’s __dict__ dictionary.
Consider this example where a class is defined and an instance is created:
class Lamp:
material = "metal" # Class attribute
def __init__(self, color):
self.color = color # Instance attribute
# Creating two instances of the Lamp class
lamp1 = Lamp("red")
Accessing the class dictionary reveals the "material" attribute, which is shared across all instances derived from the class.
print(Lamp.__dict__)
{'__module__': '__main__', 'material': 'metal', '__init__':
Conversely, querying the instance dictionary shows the "color" attribute.
print(lamp1.__dict__)
{'color': 'red'}
When an attribute is accessed via an instance, Python first searches the instance’s __dict__. If it doesn’t find the attribute there, it checks the class’s __dict__.
print(lamp1.material)
metal
Exercise caution when class and instance attributes have the same name.
If an attribute is named the same in both the instance and the class, the instance attribute will override the class attribute when accessed through the instance.
The strategic use of class attributes can be quite powerful; however, it’s important to handle them with care to sidestep any confusion or unforeseen behaviors in your code.