Usually, when we start programming for the first time we use the called procedure-oriented way, which consists of blocks of statements that manipulate data, the programs are created with this paradigm, are made around functions. But while the programs get more complex, we use another way of programming called object-oriented, in which we combine functionality and data contained inside objects.
In OOP (object-oriented programing) there are two main aspects which are the classes and the instances. Classes are like a recipe that we use to create the instances of those classes or the dishes we create using the classes recipe. so the class is like the recipe and the instances are the real created things.
Objects are instances of the class, let's see an example, if we create an integer, it is actually an object instance of the class integer.
So we can create as many instances (objects) of that class (integer) as we want. All types in python are classes, so it is the same for other types like strings, tuples, etc. Nevertheless, each type of class has different characteristics and behaviors.
Objects can store data in variables, those will be the attributes and also can have functionality (functions), those are the methods. It is very important to understand those concepts because we need to differentiate instance attributes and class attributes.
Attributes and property can be seen as synonyms in this context, usually, we use this to express some ability or characteristic that something has.
CLASS ATTRIBUTES VS INSTANCE ATTRIBUTES:
The class attributes are defined and live outside all the instances of the class but can be accessed by all of them. It means the are shared, so there is only one copy of the class variable and they have the same value for all the instances of a class object.
If the class variables are changed by any object, this change will be seen by all the instances.
On the other hand, are owned by each instance (object) of the class. so each object has a unique copy of the variable. And the are not related with other instances attributes, if we modify the attribute it only will affect the owner instance.
Let’s look at an example:
How does it work and why they are different?
We create a class car, with an attribute model, and the value for this attribute is “X”, each time we create a new instance (car_one, car_two), this instance will have a copy of the class attribute. So, in the example we have car_one, which is a Car() and has an attribute or characteristic, is a model “X”, the car_two is also had this attribute. but if modify the car_one instance attribute to model “3”, it will only affect that object, the car_two remains the same because we modified the instance attribute.
But, if we modify the class attribute Car.model = “3” and we check again both instances/objects car will be modified.
Advantages and disadvantages.
- Class attributes: Instances inherit all class attributes, but we don’t always want to modify all the instances attributes, so it is useful in some cases to deal with instances attributes.
- Instances attributes: Are specific to each object, are easy to manipulate. They only live while the object lives. The drawback is that they don’t allow to keep track of values between instances, also the values are destroyed if the object is destroyed.
This is a method that is used to see what is inside the instances, their attributes and ther corresponding values, the ones owned by each instance, this information is stored in dictionaries. But also this method can be used for classes.
As you can see here the __dict__ of a class is actually an object of a class called dictproxy, it is a special class whose objects behave like normal dictionaries. It is a read-only object, its purpose is to ensure stability to Python interpreter, while the instance dictionaries can be modified, the dictproxy can’t. but we are not going to go into details if you want to know more about this please go to this link.
Pythonic way to create them:
Once we use __init__ which is the method to create the instance attributes of a given class, the correct way to deal with instance attributes is with getter and setter methods.
To ensure data-encapsulation: which is is the packing of data and methods that affect that data into a single component, so we can restrict access to other object’s components. This is useful specially for attributes that are critical for the object.
In the example, we can see the class Rectangle and two key attributes width and height, the only way to affect those attributes in the different instances is by using the @property and the @attribute.setter.
We define the width and the height as private attributes by using a ‘__’ when we are declaring them, if we don’t use the getter and setter method for this object, if we try to modify the value in an instance it won’t work. Look how __dict__ show the attributes:
There are different methods for creating attributes, but some of them are don’t ensure data encapsulation.
If you want to know more about objects please visit my blog about mutable and immutable objects.