A Python class is like an outline for creating a new object. An object is anything that you wish to manipulate or change while working through the code. Every time a class object is instantiated, which is when we declare a variable, a new object is initiated from scratch. Class objects can be used over and over again whenever needed.
A class is usually modeled after a Noun. Classes are things. So we might make a class that represents a Person, House, Vehicle, or Animal. These are all common examples to use when learning about classes. For us, we will create a Vehicle class. What information would we associate with a vehicle, and what behavior would it have? A vehicle may have a type, brand, model and so on. This type of information is stored in python variables called attributes. These things also have behaviors. A Vehicle can drive, stop, honk its horn, and so on. Behaviors are contained in functions and a function that is part of a class is called a method.
- Example:
class Vehicle:
def __init__(self, brand, model, type):
self.brand = brand
self.model = model
self.type = type
self.gas_tank_size = 14
self.fuel_level = 0
def fuel_up(self):
self.fuel_level = self.gas_tank_size
print('Gas tank is now full.')
def drive(self):
print(f'The {self.model} is now driving.')
Class attributes are attributes which are owned by the class itself. They will be shared by all the instances of the class. Therefore they have the same value for every instance. We define class attributes outside all the methods, usually they are placed at the top, right below the class header.
- Example:
class A:
a = "I am a class attribute!"
Instance Attributes are unique to each object, (an instance is another name for an object). Here, any animal object we create will be able to store its name and age. We can change either attribute of either animal, without affecting any other animal objects we’ve created.
- Example:
class Animal:
def __init__(self, name, age):
self.name = name
self.age = age
Instance Attributes | Class Attributes |
---|---|
an instance attribute defined inside the constructor. | a class attribute defined outside the constructor. |
only accessible from the scope of an object. | accessible as both a property of the class and as a property of objects, as it is shared between all of them. |
Properties of the object are defined by the attributes and the behavior is defined using methods. These methods are defined inside a class. These methods are the reusable piece of code that can be invoked/called at any point in the program.
The properties that all Animals objects must have are defined in a method called .init(). Every time a new Dog object is created, .init() sets the initial state of the object by assigning the values of the object’s properties. That is, .init() initializes each new instance of the class.
The properties that all Animals objects must have are defined in a method called .init(). Every time a new Dog object is created, .init() sets the initial state of the object by assigning the values of the object’s properties. That is, .init() initializes each new instance of the class.
- Example:
class Animals:
def __init__(self, name, age):
self.name = name
self.age = age
When you create an instance of a class, Python first calls the new() method to create the object and then calls the init() method to initialize the object’s attributes.
The new() is a static method of the object class. It has the following signature:
- Example:
object.__new__(
class , * args, ** kwargs)
The first argument of the new method is the class of the new object that you want to create.
The *args and **kwargs parameters must match the parameters of the init() of the class. However, the new() method does use them.
- Example:
class Animal:
def __new__(cls, name):
print(f'Creating a new {cls.__name__} object...')
obj = object.__new__(cls)
return obj
def __init__(self, name):
print(f'Initializing the animal object...')
self.name = name
cat = Animal('Jimmi')
Instance method are methods which require an object of its class to be created before it can be called. To invoke a instance method, we have to create an Object of the class in which the method is defined.
-
Use the def keyword to define an instance method in Python.
-
Use self as the first parameter in the instance method when defining it. The self parameter refers to the current object.
-
Using the self parameter to access or modify the current object attributes.
-
Example:
class Animal:
# constructor
def __init__(self, name, age):
# Instance variable
self.name = name
self.age = age
# instance method
def show(self):
print('Name:', self.name, 'Age:', self.age)
cat = Animal('Jimmi')
# call instance method
cat.show()
The class method in Python is a method, which is bound to the class but not the object of that class. The static methods are also same but there are some basic differences. For class methods, we need to specify @classmethod decorator, and for static method @staticmethod decorator is used.
Syntax for Class Method:
- Example:
class my_class:
@classmethod
def function_name(cls, arguments):
# Function Body
return value
Syntax for Static Method.
- Example:
class my_class:
@staticmethod
def function_name(arguments):
# Function Body
return value
Class Method | Static Method |
---|---|
The class method takes cls (class) as first argument. | The static method does not take any specific parameter. |
Class method can access and modify the class state. | Static Method cannot access or modify the class state. |
The class method takes the class as parameter to know about the state of that class. | Static methods do not know about class state. These methods are used to do some utility tasks by taking some parameters. |
@classmethod decorator is used here. | @staticmethod decorator is used here. |
Magic methods are special methods in python that have double underscores on both sides of the method name. Magic methods are predominantly used for operator overloading. Operator overloading means provided additional functionality to the operators, the python implicitly invokes the magic methods to provide additional functionality to it
- __add__ method
the __add_ method is a magic method which gets called when we add two numbers using the + operator. Consider the following example_
- Example:
class ADD:
def __init__(self, string_):
self.string_ = string_
def __add__(self, string2):
return self.string_ + string2
ex = Str("Hello")
print(instance1 + " Folks")
- __str__() method
__str_(). It is overridden to return a printable string representation of any user defined class._
- Example:
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
def __str__(self):
return f"{self.name}({self.age})"
p1 = Person("John", 36)
print(p1)
- __getitem__ and __setitem__ magic method
__getitem_ method is used to get an item from the invoked instances’ attribute. __getitem__ is commonly used with containers like list, tuple, etc._
- Example:
class A:
def __init__(self, item):
self.item = item
def __getitem__(self, index):
return self.item[index]
a = A([1, 2])
print(f"First item: {a[0]}")
print(f"Second item: {a[1]}")
- Example:
class Employee:
def __new__(cls):
print("__new__ magic method is called")
inst = object.__new__(cls)
return inst
def __init__(self):
print("__init__ magic method is called")
self.name = 'Satya'
_lt_, _gt_, _le_, _ge_, _eq_, and _ne_ magic methods
Comparative operators can be used to compare between the object’s attributes.
- Example: new()
class Comparison:
def __init__(self, a):
self.a = a
def __lt__(self, object2):
return self.a < object2.a
def __gt__(self, object2):
return self.a > object2.a
def __le__(self, object2):
return self.a <= object2.a
def __ge__(self, object2):
return self.a >= object2.a
def __eq__(self, object2):
return self.a == object2.a
def __ne__(self, object2):
return self.a != object2.a
a = Comparison(1)
b = Comparison(2)
print(
a < b,
a > b,
a <= b,
a >= b,
a == b,
a != b
)
Operator Magic Methods | Description |
---|---|
_add_(self, other) | To get called on add operation using + operator |
_sub_(self, other) | To get called on subtraction operation using - operator. |
_mul_(self, other) | To get called on multiplication operation using * operator. |
_floordiv_(self, other) | To get called on floor division operation using // operator. |
_truediv_(self, other) | To get called on division operation using / operator. |
_mod_(self, other) | To get called on modulo operation using % operator. |
_pow_(self, other[, modulo]) | To get called on calculating the power using ** operator. |
Python also has an array of magic methods designed to implement behavior for built in type conversion functions like float(). Here they are:
__int__(self)
Implements type conversion to int.
__long__(self)
Implements type conversion to long.
__float__(self)
Implements type conversion to float.
__complex__(self)
Implements type conversion to complex.
__oct__(self)
Implements type conversion to octal.
__hex__(self)
Implements type conversion to hexadecimal.
__index__(self)
Implements type conversion to an int when the object is used in a slice expression. If you define a custom numeric type that might be used in slicing, you should define __index__.
__trunc__(self)
Called when math.trunc(self) is called. __trunc__ should return the value of `self truncated to an integral type (usually a long).
__coerce__(self, other)
Method to implement mixed mode arithmetic. __coerce__ should return None if type conversion is impossible. Otherwise, it should return a pair (2-tuple) of self and other, manipulated to have the same type.
In the following example, you create a custom class Data and overwrite the complex() magic method so that it returns a complex number (33+11j) when trying to call complex(x) on a custom Data object.
- Example:
class Data:
def __complex__(self):
return (42+21j)
x = Data()
res = complex(x)
print(res)
# (33+11j)
Python accomplishes a great deal of encapsulation through "magic", instead of explicit modifiers for methods or fields.
__getattr__(self, name)
You can define behavior for when a user attempts to access an attribute that doesn't exist (either at all or yet). This can be useful for catching and redirecting common misspellings, giving warnings about using deprecated attributes (you can still choose to compute and return that attribute, if you wish), or deftly handing an AttributeError. It only gets called when a nonexistent attribute is accessed, however, so it isn't a true encapsulation solution.
__setattr__(self, name, value)
Unlike __getattr__, __setattr__ is an encapsulation solution. It allows you to define behavior for assignment to an attribute regardless of whether or not that attribute exists, meaning you can define custom rules for any changes in the values of attributes. However, you have to be careful with how you use __setattr__, as the example at the end of the list will show.
__delattr__(self, name)
This is the exact same as __setattr__, but for deleting attributes instead of setting them. The same precautions need to be taken as with __setattr__ as well in order to prevent infinite recursi
Example:
def __setattr__(self, name, value):
self.name = value
# since every time an attribute is assigned, __setattr__() is called, this
# is recursion.
- Example:
class Frob:
def __init__(self, bamf):
self.bamf = bamf
def __getattr__(self, name):
return 'Frob does not have `{}` attribute.'.format(str(name))
f = Frob("bamf")
f.bar
# 'Frob does not have `bar` attribute.'
f.bamf
# 'bamf'
Destructors are called when an object gets destroyed. The del() method is a known as a destructor method in Python. It is called when all references to the object have been deleted i.e when an object is garbage collected.
- Example:
def __del__(self):
# body of destructor
- Example:
# Python program to illustrate destructor
class Employee:
# Initializing
def __init__(self):
print('Employee created.')
# Deleting (Calling destructor)
def __del__(self):
print('Destructor called, Employee deleted.')
obj = Employee()
del obj