As an object-oriented language, Python provides two scopes for attributes: class attributes and instance attributes. Imagine a Class like a blueprint from which different objects are created.
Each object is an instance of a class. We can take the class “Dog” as an example to explain this.
Dogs can have data like color, name or breed, and behaviors like: run, bark, sit, eat, and more. Data in a class are called attributes and behaviors are called methods.
Example of a Dog named “Nemo”:
Class -> Dog
Attributes-> Name: Nemo
Color: Black
Breed: Golden Retriever
Methods-> Bark
Run
Eat
Sit
So, our object of class “Dog” is named Nemo, its color is Black and it’s a Golden Retriever. He can bark, run, eat, and sit.
Instance Attribute
An instance attribute is a Python variable belonging to only one object. It is only accessible in the scope of the object and it is defined inside the constructor function of a class. For example, __init__(self,..).
Class Attribute
A class attribute is a Python Variable that belongs to a class rather than a particular object. This is shared between all other objects of the same class and is defined outside the constructor function __init__(self,…), of the class.
Differences Between Class and Instance Attributes
The difference is that class attributes are shared by all instances. When you change the value of a class attribute, it will affect all instances that share the same exact value. The attribute of an instance on the other hand is unique to that instance.
What is __dict__?
A __dict__ is a dictionary or mapping object used to store an object’s attributes.
But how does Python deal with the object and class attributes using the __dict__? Well, each instance is stored in a dictionary.
Creating Classes and Instance Pythonic and non-Pythonic
A non-Pythonic way would be like this:
class Square: def __init__(self, size=0): if not isinstance(size, int): raise TypeError("size must be an integer") if size < 0: raise ValueError("size must be >= 0") self.__size = size * size def area(self): return self.__sizet__width()The disadvantage comes when we need to modify the code. The pythonic way of doing it would be to use getter and setter property methods.
class Square: def __init__(self, size=0): self.size = size @property def size(self): return self.__size @size.setter def size(self, value): if not isinstance(value, int): raise TypeError("size must be an integer") if value < 0: raise ValueError("size must be >= 0") self.__size = value def area(self): return self.__size * self.__sizeThe advantage of using property allows us to attach code to the "self.size" attribute and any code assigned the value of size will be called with size in def size.