Meritshot Tutorials
- Home
- »
- OOPS in Python
Python Tutorial
-
Understanding Variables in PythonUnderstanding Variables in Python
-
Types Of Operators in PythonTypes Of Operators in Python
-
How to create string ?How to create string ?
-
Data Structure in PythonData Structure in Python
-
What is a Function in PythonWhat is a Function in Python
-
Parameters and Arguments in PythonParameters and Arguments in Python
-
What is a Lambda FunctionWhat is a Lambda Function
-
What is a Regular Expression?What is a Regular Expression?
-
Introduction to Loops in PythonIntroduction to Loops in Python
-
If-else Statements in PythonIf-else Statements in Python
-
Break Statement in PythonBreak Statement in Python
-
OOPS in PythonOOPS in Python
-
Space and Time in PythonSpace and Time in Python
-
Data Type in PythonData Type in Python
Introduction to Object-oriented programming
Introduction to Object-Oriented Programming in Python:
In programming languages, mainly there are two approaches that are used to write program or code.
- Procedural Programming
- Object-Oriented Programming
The procedure we are following till now is the “Procedural Programming” approach. So, in this session, we will learn about Object Oriented Programming (OOP). The basic idea of object-oriented programming (OOP) in Python is to use classes and objects to represent real-world concepts and entities.
Core concepts of class :
A class is a blueprint or template for creating objects. It defines the properties and methods that an object of that class will have. Properties are the data or state of an object, and methods are the actions or behaviors that an object can perform.
An object is an instance of a class, and it contains its own data and methods. For example, you could create a class called “Person” that has properties such as name and age, and methods such as speak() and walk(). Each instance of the Person class would be a unique object with its own name and age, but they would all have the same methods to speak and walk.
key features of OOP in Python
encapsulation, which means that the internal state of an object is hidden and can only be accessed or modified through the object’s methods. This helps to protect the object’s data and prevent it from being modified in unexpected ways.
inheritance, which allows new classes to be created that inherit the properties and methods of an existing class. This allows for code reuse and makes it easy to create new classes that have similar functionality to existing classes.
Polymorphism is also supported in Python, which means that objects of different classes can be treated as if they were objects of a common class. This allows for greater flexibility in code and makes it easier to write code that can work with multiple types of objects.
Abstraction is The process of hiding the complex implementation details and showing only the necessary features of an object. It helps in reducing programming complexity and effort.
In summary, OOP in Python allows developers to model real-world concepts and entities using classes and objects, encapsulate data, reuse code through inheritance, and write more flexible code through polymorphism.
Python Class and Objects
A class is a blueprint or a template for creating objects, providing initial values for state (member variables or attributes), and implementations of behavior (member functions or methods). The user-defined objects are created using the class keyword.
Creating a Class:
Let us now create a class using the class keyword.
class Details:
name = “Rohan”
age = 20
Creating an Object:
Object is the instance of the class used to access the properties of the class Now lets create an object of the class.
obj1 = Details()
EXAMPLE:
class Details:
name = “Rohan”
age = 20
obj1 = Details()
print(obj1.name)
print(obj1.age)
Rohan
20
Self parameter
The self parameter is a reference to the current instance of the class, and is used to access variables that belongs to the class.
It must be provided as the extra parameter inside the method definition.
class Details: name = “Rohan” age = 20
def desc(self):
print(“My name is”, self.name, “and I’m”, self.age, “years old.”)
obj1 = Details()
obj1.desc()
My name is Rohan and I’m 20 years old.
Constructors
A constructor is a special method in a class used to create and initialize an object of a class. There are different types of constructors. Constructor is invoked automatically when an object of a class is created.
A constructor is a unique function that gets called automatically when an object is created of a class. The main purpose of a constructor is to initialize or assign values to the data members of that class. It cannot return any value other than None.
Syntax of Python Constructor
def init (self):
#initializations
“init” is one of the reserved functions in Python. In Object Oriented Programming, it is known as a constructor.
Types of Constructors in Python
- Parameterized Constructor
- Default Constructor
Parameterized Constructor in Python
When the constructor accepts arguments along with self, it is known as parameterized constructor. These arguments can be used inside the class to assign the values to the data members.
class Details:
def init (self, animal, group): self.animal = animal self.group = group
obj1 = Details(“Crab”, “Crustaceans”)
print(obj1.animal, “belongs to the”, obj1.group, “group.”)
Crab belongs to the Crustaceans group.
Default Constructor in Python
When the constructor doesn’t accept any arguments from the object and has only one argument, self, in the constructor, it is known as a Default constructor.
class Details:
def init (self):
print(“animal Crab belongs to Crustaceans group”) obj1=Details()
animal Crab belongs to Crustaceans group
Python Decorators
Python decorators are a powerful and versatile tool that allow you to modify the behavior of functions and methods. They are a way to extend the functionality of a function or method without modifying its source code.
A decorator is a function that takes another function as an argument and returns a new function that modifies the behavior of the original function. The new function is often referred to as a “decorated” function. The basic syntax for using a decorator is the following:
@decorator_function
def my_function():
pass
The @decorator_function notation is just a shorthand for the following code:
def my_function():
pass
my_function = decorator_function(my_function)
Decorators are often used to add functionality to functions and methods, such as logging, memoization, and access control.
Practical use case
One common use of decorators is to add logging to a function. For example, you could use a decorator to log the arguments and return value of a function each time it is called:
import logging
def log_function_call(func):
def decorated(*args, **kwargs):
logging.info(f”Calling {func. name } with args={args}, kwargs={kwargs}”)
result = func(*args, **kwargs) logging.info(f”{func. name } returned {result}”) return result
return decorated
@log_function_call
def my_function(a, b): return a + b
In this example, the log_function_call decorator takes a function as an argument and returns a new function that logs the function call before and after the original function is called.
Conclusion
Decorators are a powerful and flexible feature in Python that can be used to add functionality to functions and methods without modifying their source code. They are a great tool for separating concerns, reducing code duplication, and making your code more readable and maintainable.
In conclusion, python decorators are a way to extend the functionality of functions and methods, by modifying its behavior without modifying the source code. They are used for a variety of purposes, such as logging, memoization, access control, and more. They are a powerful tool that can be used to make your code more readable, maintainable, and extendable.
Getters
Getters in Python are methods that are used to access the values of an object’s properties. They are used to return the value of a specific property, and are typically defined using the @property decorator. Here is an example of a simple class with a getter method:
class MyClass:
def init (self, value): self._value = value
@property
def value(self): return self._value
In this example, the MyClass class has a single property, _value, which is initialized in the init method.
The value method is defined as a getter using the @property decorator, and is used to return the value of the
_value property.
To use the getter, we can create an instance of the MyClass class, and then access the value property as if it were an attribute:
>>> obj = MyClass(10)
>>> obj.value 10
Setters
It is important to note that the getters do not take any parameters and we cannot set the value through getter method.For that we need setter method which can be added by decorating method with @property_name.setter Here is an example of a class with both getter and setter:
class MyClass:
def init (self, value): self._value = value
@property
def value(self): return self._value
@value.setter
def value(self, new_value): self._value = new_value
We can use setter method like this:
>>> obj = MyClass(10)
>>> obj.value = 20
>>> obj.value 20
In conclusion, getters are a convenient way to access the values of an object’s properties, while keeping the internal representation of the property hidden. This can be useful for encapsulation and data validation.
Inheritance in python
When a class derives from another class. The child class will inherit all the public and protected properties and methods from the parent class. In addition, it can have its own properties and methods,this is called as inheritance.
Python Inheritance Syntax
class BaseClass:
#Body of base class
class DerivedClass(BaseClass):
#Body of derived class
Derived class inherits features from the base class where new features can be added to it. This results in re- usability of code.
Types of inheritance:
- Single inheritance
- Multiple inheritance
- Multilevel inheritance
- Hierarchical Inheritance
- Hybrid Inheritance
Single Inheritance:
Single inheritance enables a derived class to inherit properties from a single parent class, thus enabling code reusability and the addition of new features to existing code.
class Parent:
def func1(self):
print(“This function is in parent class.”)
class Child(Parent): def func2(self):
print(“This function is in child class.”)
object = Child() object.func1() object.func2()
This function is in parent class.
This function is in child class.
Multiple Inheritance:
When a class can be derived from more than one base class this type of inheritance is called multiple inheritances. In multiple inheritances, all the features of the base classes are inherited into the derived class.
class Mother:
mothername = “”
def mother(self): print(self.mothername)
class Father:
fathername = “”
def father(self): print(self.fathername)
class Son(Mother, Father): def parents(self):
print(“Father name is :”, self.fathername) print(“Mother :”, self.mothername)
s1 = Son() s1.fathername = “Mommy” s1.mothername = “Daddy” s1.parents()
Father name is :
Mommy Mother : Daddy
Multilevel Inheritance:
In multilevel inheritance, features of the base class and the derived class are further inherited into the new derived class. This is similar to a relationship representing a child and a grandfather.
class Grandfather:
def init (self, grandfathername): self.grandfathername = grandfathername
class Father(Grandfather):
def init (self, fathername, grandfathername): self.fathername = fathername
Grandfather. init (self, grandfathername) class Son(Father):
def init (self, sonname, fathername, grandfathername): self.sonname = sonname
Father. init (self, fathername, grandfathername)
def print_name(self):
print(‘Grandfather name :’, self.grandfathername) print(“Father name :”, self.fathername) print(“Son name :”, self.sonname)
s1 = Son(‘Prince’, ‘Rampal’, ‘Lal mani’) print(s1.grandfathername) s1.print_name()
Lal mani
Grandfather name : Lal mani Father name : Rampal
Son name : Prince
Hierarchical Inheritance:
When more than one derived class are created from a single base this type of inheritance is called hierarchical inheritance. In this program, we have a parent (base) class and two child (derived) classes.
class Parent:
def func1(self):
print(“This function is in parent class.”)
class Child1(Parent): def func2(self):
print(“This function is in child 1.”)
class Child2(Parent): def func3(self):
print(“This function is in child 2.”)
object1 = Child1() object2 = Child2() object1.func1() object1.func2() object2.func1() object2.func3()
This function is in parent class.
This function is in child 1.
This function is in parent class.
This function is in child 2.
Hybrid Inheritance:
Inheritance consisting of multiple types of inheritance is called hybrid inheritance.
class School:
def func1(self):
print(“This function is in school.”)
class Student1(School): def func2(self):
print(“This function is in student 1. “)
class Student2(School): def func3(self):
print(“This function is in student 2.”)
class Student3(Student1, School): def func4(self):
print(“This function is in student 3.”)
object = Student3() object.func1() object.func2()
This function is in school.
This function is in student 1.
Access Specifiers/Modifiers
Access specifiers or access modifiers in python programming are used to limit the access of class variables and class methods outside of class while implementing the concepts of inheritance.
Let us see the each one of access specifiers in detail:
Types of access specifiers
- Public access modifier
- Private access modifier
- Protected access modifier ## Public Access Specifier in Python All the variables and methods (member functions) in python are by default Any instance variable in a class followed by the ‘self’ keyword ie. self.var_name are public accessed.
class Student:
# constructor is defined
def init (self, age, name):
self.age = age # public variable
self.name = name # public variable
obj = Student(21,”Harry”) print(obj.age) print(obj.name)
21
Harry
Private Access Modifier
By definition, Private members of a class (variables or methods) are those members which are only accessible inside the class. We cannot use private members outside of class.
In Python, there is no strict concept of “private” access modifiers like in some other programming languages. However, a convention has been established to indicate that a variable or method should be considered private by prefixing its name with a double underscore ( ). This is known as a “weak internal use indicator” and it is a convention only, not a strict rule. Code outside the class can still access these “private” variables and methods, but it is generally understood that they should not be accessed or modified.
class Student:
def init (self, age, name):
self. age = age # An indication of private variable
def funName(self): # An indication of private function
self.y = 34
print(self.y)
class Subject(Student): pass
obj = Student(21,”Harry”) obj1 = Subject
# calling by object of class Student
print(obj. age) print(obj. funName())
# calling by object of class Subject
print(obj1. age) print(obj1. funName())
AttributeError Traceback (most recent call last) Input In [10], in <cell line: 16>()
13 obj1 = Subject
15 # calling by object of class Student
—> 16 print(obj. age)
17 print(obj. funName())
19 # calling by object of class Subject AttributeError: ‘Student’ object has no attribute ‘ age’
Private members of a class cannot be accessed or inherited outside of class. If we try to access or to inherit the properties of private members to child class (derived class). Then it will show the error.
Name mangling
Name mangling in Python is a technique used to protect class-private and superclass-private attributes from being accidentally overwritten by subclasses. Names of class-private and superclass-private attributes are transformed by the addition of a single leading underscore and a double leading underscore respectively.
class MyClass:
def init (self):
self._nonmangled_attribute = “I am a nonmangled attribute” self. mangled_attribute = “I am a mangled attribute”
my_object = MyClass()
print(my_object._nonmangled_attribute) # Output: I am a nonmangled attribute print(my_object. mangled_attribute) # Throws an AttributeError print(my_object._MyClass mangled_attribute) # Output: I am a mangled attribute
AttributeError Traceback (most recent call last) Input In [23], in <cell line: 9>()
6 my_object = MyClass()
8 print(my_object._nonmangled_attribute) # Output: I am a nonmangled attribute
—-> 9 print(my_object. mangled_attribute) # Throws an AttributeError
10 print(my_object._MyClass mangled_attribute)
AttributeError: ‘MyClass’ object has no attribute ‘ mangled_attribute’
In the example above, the attribute _nonmangled_attribute is marked as nonmangled by convention, but can still be accessed from outside the class. The attribute mangled_attribute is private and its name is “mangled” to
_MyClassmangled_attribute, so it can’t be accessed directly from outside the class, but you can access it by calling _MyClass mangled_attribute
Protected Access Modifier
In object-oriented programming (OOP), the term “protected” is used to describe a member (i.e., a method or attribute) of a class that is intended to be accessed only by the class itself and its subclasses. In Python, the convention for indicating that a member is protected is to prefix its name with a single underscore (_). For example, if a class has a method called _my_method, it is indicating that the method should only be accessed by the class itself and its subclasses.
It’s important to note that the single underscore is just a naming convention, and does not actually provide any protection or restrict access to the member. The syntax we follow to make any variable protected is to write variable name followed by a single underscore (_) ie. _varName.
class Student:
def init (self): self._name = “Harry”
def _funName(self): # protected method
return “CodeWithHarry”
class Subject(Student): #inherited class
pass
obj = Student() obj1 = Subject()
# calling by object of Student class print(obj._name) print(obj._funName())
# calling by object of Subject class print(obj1._name) print(obj1._funName())
Harry
CodeWithHarry
Harry
CodeWithHarry
Static Methods
Static methods in Python are methods that belong to a class rather than an instance of the class. They are defined using the @staticmethod decorator and do not have access to the instance of the class (i.e. self). They are called on the class itself, not on an instance of the class. Static methods are often used to create utility functions that don’t need access to instance data.
class Math:
@staticmethod def add(a, b):
return a + b
result = Math.add(1, 2) print(result)
3
In this example, the add method is a static method of the Math class. It takes two parameters a and b and returns their sum. The method can be called on the class itself, without the need to create an instance of the class.
Instance vs class variables
In Python, variables can be defined at the class level or at the instance level. Understanding the difference between these types of variables is crucial for writing efficient and maintainable code.
Class Variables
Class variables are defined at the class level and are shared among all instances of the class. They are defined outside of any method and are usually used to store information that is common to all instances of the class. For example, a class variable can be used to store the number of instances of a class that have been created.
class MyClass: class_variable = 0
def init (self):
MyClass.class_variable += 1
def print_class_variable(self): print(MyClass.class_variable)
obj1 = MyClass() obj2 = MyClass()
obj1.print_class_variable() # Output: 2
obj2.print_class_variable() # Output: 2
2
2
In the example above, the class_variable is shared among all instances of the class MyClass. When we create new instances of MyClass, the value of class_variable is incremented. When we call the print_class_variable method on obj1 and obj2, we get the same value of class_variable.
Class Variables
Class variables are defined at the class level and are shared among all instances of the class. They are defined outside of any method and are usually used to store information that is common to all instances of the class. For example, a class variable can be used to store the number of instances of a class that have been created.
class MyClass: class_variable = 0
def init (self):
MyClass.class_variable += 1
def print_class_variable(self): print(MyClass.class_variable)
obj1 = MyClass() obj2 = MyClass()
obj1.print_class_variable() # Output: 2
obj2.print_class_variable() # Output: 2
2
2
In the example above, the class_variable is shared among all instances of the class MyClass. When we create new instances of MyClass, the value of class_variable is incremented. When we call the print_class_variable method on obj1 and obj2, we get the same value of class_variable.
Instance Variables
Instance variables are defined at the instance level and are unique to each instance of the class. They are defined inside the init method and are usually used to store information that is specific to each instance of the class. For example, an instance variable can be used to store the name of an employee in a class that represents an employee.
class MyClass:
def init (self, name): self.name = name
def print_name(self): print(self.name)
obj1 = MyClass(“John”) obj2 = MyClass(“Jane”)
obj1.print_name() # Output: John
obj2.print_name() # Output: Jane
John
Jane
In the example above, each instance of the class MyClass has its own value for the name variable. When we call the print_name method on obj1 and obj2, we get different values for name.
Summary
In summary, class variables are shared among all instances of a class and are used to store information that is common to all instances. Instance variables are unique to each instance of a class and are used to store information that is specific to each instance. Understanding the difference between class variables and instance variables is crucial for writing efficient and maintainable code in Python.
It’s also worth noting that, in python, class variables are defined outside of any methods and don’t need to be explicitly declared as class variable. They are defined in the class level and can be accessed via classname.varibale_name or self.class.variable_name. But instance variables are defined inside the methods and need to be explicitly declared as instance variable by using self.variable_name.
Python Class Methods
Python Class Methods: An Introduction
In Python, classes are a way to define custom data types that can store data and define functions that can manipulate that data. One type of function that can be defined within a class is called a “method.” In this blog post, we will explore what Python class methods are, why they are useful, and how to use them.
What are Python Class Methods?
A class method is a type of method that is bound to the class and not the instance of the class. In other words, it operates on the class as a whole, rather than on a specific instance of the class. Class methods are defined using the “@classmethod” decorator, followed by a function definition. The first argument of the function is always “cls,” which represents the class itself.
Why Use Python Class Methods?
Class methods are useful in several situations. For example, you might want to create a factory method that creates instances of your class in a specific way. You could define a class method that creates the instance and returns it to the caller. Another common use case is to provide alternative constructors for your class. This can be useful if you want to create instances of your class in multiple ways, but still have a consistent interface for doing so.
How to Use Python Class Methods?
To define a class method, you simply use the “@classmethod” decorator before the method definition. The first argument of the method should always be “cls,” which represents the class itself. Here is an example of how to define a class method:
class ExampleClass: @classmethod
def factory_method(cls, argument1, argument2): return cls(argument1, argument2)
In this example, the “factory_method” is a class method that takes two arguments, “argument1” and “argument2.” It creates a new instance of the class “ExampleClass” using the “cls” keyword, and returns the new instance to the caller.
It’s important to note that class methods cannot modify the class in any way. If you need to modify the class, you should use a class level variable instead.
Conclusion
Python class methods are a powerful tool for defining functions that operate on the class as a whole, rather than on a specific instance of the class. They are useful for creating factory methods, alternative constructors, and other types of methods that operate at the class level. With the knowledge of how to define and use class methods, you can start writing more complex and organized code in Python.
Class Methods as Alternative Constructors
In object-oriented programming, the term “constructor” refers to a special type of method that is automatically executed when an object is created from a class. The purpose of a constructor is to initialize the object’s attributes, allowing the object to be fully functional and ready to use.
However, there are times when you may want to create an object in a different way, or with different initial values, than what is provided by the default constructor. This is where class methods can be used as alternative constructors.
A class method belongs to the class rather than to an instance of the class. One common use case for class methods as alternative constructors is when you want to create an object from data that is stored in a different format, such as a string or a dictionary. For example, consider a class named “Person” that has two attributes: “name” and “age”. The default constructor for the class might look like this:
class Person:
def init (self, name, age): self.name = name
self.age = age
But what if you want to create a Person object from a string that contains the person’s name and age, separated by a comma? You can define a class method named “from_string” to do this:
class Person:
def init (self, name, age): self.name = name
self.age = age
@classmethod
def from_string(cls, string): name, age = string.split(‘,’) return cls(name, int(age))
Now you can create a Person object from a string like this:
person = Person.from_string(“John Doe, 30”)
Another common use case for class methods as alternative constructors is when you want to create an object with a different set of default values than what is provided by the default constructor. For example, consider a class named “Rectangle” that has two attributes: “width” and “height”. The default constructor for the class might look like this:
class Rectangle:
def init (self, width, height): self.width = width
self.height = height
But what if you want to create a Rectangle object with a default width of 10 and a default height of 5? You can define a class method named “square” to do this:
class Rectangle:
def init (self, width, height): self.width = width
self.height = height
@classmethod
def square(cls, size): return cls(size, size)
Now you can create a square rectangle like this:
rectangle = Rectangle.square(10)
dir(), dict and help() methods in python
We must look into dir(), dict() and help() attribute/methods in python. They make it easy for us to understand how classes resolve various functions and executes code. In Python, there are three built-in functions that are commonly used to get information about objects: dir(), dict, and help(). Let’s take a look at each of them:
The dir() method
dir(): The dir() function returns a list of all the attributes and methods (including dunder methods) available for an object. It is a useful tool for discovering what you can do with an object. Example:
x = [1, 2, 3]
dir(x)
The dict attribute
dict: The dict attribute returns a dictionary representation of an object’s attributes. It is a useful tool for introspection. Example:
>> class Person:
def init (self, name, age): self.name = name
self.age = age
>>> p = Person(“John”, 30)
>>> p. dict
{‘name’: ‘John’, ‘age’: 30}
The help() mehthod
help(): The help() function is used to get help documentation for an object, including a description of its attributes and methods.
Example:
help(str)
Help on class str in module builtins:
class str(object)
str(object=”) -> str
str(bytes_or_buffer[, encoding[, errors]]) -> str
Create a new string object from the given object. If encoding or
errors is specified, then the object must expose a data buffer
that will be decoded using the given encoding and error handler.
Otherwise, returns the result of object. str () (if defined)
or repr(object).
encoding defaults to sys.getdefaultencoding().
errors defaults to ‘strict’.
Methods defined here:
add (self, value, /)
Return self+value.
contains (self, key, /)
Return key in self.
eq (self, value, /)
Return self==value.
format (self, format_spec, /)
Return a formatted version of the string as described by format_spec.
ge (self, value, /)
Return self>=value.
getattribute (self, name, /)
Return getattr(self, name).
getitem (self, key, /)
Return self[key].
getnewargs (…)
gt (self, value, /)
Return self>value.
hash (self, /)
Return hash(self).
iter (self, /)
Implement iter(self).
le (self, value, /)
Return self<=value.
len (self, /)
Return len(self).
lt (self, value, /)
Return self<value.
mod (self, value, /)
Return self%value.
mul (self, value, /)
Return self*value.
ne (self, value, /)
Return self!=value.
repr (self, /)
Return repr(self).
rmod (self, value, /)
Return value%self.
rmul (self, value, /)
Return value*self.
sizeof (self, /)
Return the size of the string in memory, in bytes.
str (self, /)
Return str(self).
capitalize(self, /)
Return a capitalized version of the string.
More specifically, make the first character have upper case and the rest lower
case.
casefold(self, /)
Return a version of the string suitable for caseless comparisons.
center(self, width, fillchar=’ ‘, /)
Return a centered string of length width.
Padding is done using the specified fill character (default is a space).
count(…)
S.count(sub[, start[, end]]) -> int
Return the number of non-overlapping occurrences of substring sub in
string S[start:end]. Optional arguments start and end are
interpreted as in slice notation.
encode(self, /, encoding=’utf-8′, errors=’strict’)
Encode the string using the codec registered for encoding.
encoding
The encoding in which to encode the string.
errors
The error handling scheme to use for encoding errors.
The default is ‘strict’ meaning that encoding errors raise a
UnicodeEncodeError. Other possible values are ‘ignore’, ‘replace’ and
‘xmlcharrefreplace’ as well as any other name registered with
codecs.register_error that can handle UnicodeEncodeErrors.
endswith(…)
S.endswith(suffix[, start[, end]]) -> bool
Return True if S ends with the specified suffix, False otherwise.
With optional start, test S beginning at that position.
With optional end, stop comparing S at that position.
suffix can also be a tuple of strings to try.
expandtabs(self, /, tabsize=8)
Return a copy where all tab characters are expanded using spaces.
If tabsize is not given, a tab size of 8 characters is assumed.
find(…)
S.find(sub[, start[, end]]) -> int
Return the lowest index in S where substring sub is found,
such that sub is contained within S[start:end]. Optional
arguments start and end are interpreted as in slice notation.
Return -1 on failure.
format(…)
S.format(*args, **kwargs) -> str
Return a formatted version of S, using substitutions from args and kwargs.
The substitutions are identified by braces (‘{‘ and ‘}’).
format_map(…)
S.format_map(mapping) -> str
Return a formatted version of S, using substitutions from mapping.
The substitutions are identified by braces (‘{‘ and ‘}’).
index(…)
S.index(sub[, start[, end]]) -> int
Return the lowest index in S where substring sub is found,
such that sub is contained within S[start:end]. Optional
arguments start and end are interpreted as in slice notation.
Raises ValueError when the substring is not found.
isalnum(self, /)
Return True if the string is an alpha-numeric string, False otherwise.
A string is alpha-numeric if all characters in the string are alpha-numeric and
there is at least one character in the string.
isalpha(self, /)
Return True if the string is an alphabetic string, False otherwise.
A string is alphabetic if all characters in the string are alphabetic and there
is at least one character in the string.
isascii(self, /)
Return True if all characters in the string are ASCII, False otherwise.
ASCII characters have code points in the range U+0000-U+007F.
Empty string is ASCII too.
isdecimal(self, /)
Return True if the string is a decimal string, False otherwise.
A string is a decimal string if all characters in the string are decimal and
there is at least one character in the string.
isdigit(self, /)
Return True if the string is a digit string, False otherwise.
A string is a digit string if all characters in the string are digits and there
is at least one character in the string.
isidentifier(self, /)
Return True if the string is a valid Python identifier, False otherwise.
Call keyword.iskeyword(s) to test whether string s is a reserved identifier,
such as “def” or “class”.
islower(self, /)
Return True if the string is a lowercase string, False otherwise.
A string is lowercase if all cased characters in the string are lowercase and
there is at least one cased character in the string.
isnumeric(self, /)
Return True if the string is a numeric string, False otherwise.
A string is numeric if all characters in the string are numeric and there is at
least one character in the string.
isprintable(self, /)
Return True if the string is printable, False otherwise.
A string is printable if all of its characters are considered printable in
repr() or if it is empty.
isspace(self, /)
Return True if the string is a whitespace string, False otherwise.
A string is whitespace if all characters in the string are whitespace and there
is at least one character in the string.
istitle(self, /)
Return True if the string is a title-cased string, False otherwise.
In a title-cased string, upper- and title-case characters may only
follow uncased characters and lowercase characters only cased ones.
isupper(self, /)
Return True if the string is an uppercase string, False otherwise.
A string is uppercase if all cased characters in the string are uppercase and
there is at least one cased character in the string.
join(self, iterable, /)
Concatenate any number of strings.
The string whose method is called is inserted in between each given string.
The result is returned as a new string.
Example: ‘.’.join([‘ab’, ‘pq’, ‘rs’]) -> ‘ab.pq.rs’
just(self, width, fillchar=’ ‘, /)
Return a left-justified string of length width.
Padding is done using the specified fill character (default is a space).
lower(self, /)
Return a copy of the string converted to lowercase.
lstrip(self, chars=None, /)
Return a copy of the string with leading whitespace removed.
If chars is given and not None, remove characters in chars instead.
partition(self, sep, /)
Partition the string into three parts using the given separator.
This will search for the separator in the string. If the separator is found,
returns a 3-tuple containing the part before the separator, the separator
itself, and the part after it.
If the separator is not found, returns a 3-tuple containing the original string
and two empty strings.
removeprefix(self, prefix, /)
Return a str with the given prefix string removed if present.
If the string starts with the prefix string, return string[len(prefix):].
Otherwise, return a copy of the original string.
removesuffix(self, suffix, /)
Return a str with the given suffix string removed if present.
If the string ends with the suffix string and that suffix is not empty,
return string[:-len(suffix)]. Otherwise, return a copy of the original
string.
replace(self, old, new, count=-1, /)
Return a copy with all occurrences of substring old replaced by new.
count
Maximum number of occurrences to replace.
-1 (the default value) means replace all occurrences.
If the optional argument count is given, only the first count occurrences are
replaced.
rfind(…)
S.rfind(sub[, start[, end]]) -> int
Return the highest index in S where substring sub is found,
such that sub is contained within S[start:end]. Optional
arguments start and end are interpreted as in slice notation.
Return -1 on failure.
rindex(…)
S.rindex(sub[, start[, end]]) -> int
Return the highest index in S where substring sub is found,
such that sub is contained within S[start:end]. Optional
arguments start and end are interpreted as in slice notation.
Raises ValueError when the substring is not found.
rjust(self, width, fillchar=’ ‘, /)
Return a right-justified string of length width.
Padding is done using the specified fill character (default is a space).
rpartition(self, sep, /)
Partition the string into three parts using the given separator.
This will search for the separator in the string, starting at the end. If
the separator is found, returns a 3-tuple containing the part before the
separator, the separator itself, and the part after it.
If the separator is not found, returns a 3-tuple containing two empty strings
and the original string.
rsplit(self, /, sep=None, maxsplit=-1)
Return a list of the words in the string, using sep as the delimiter string.
sep
The delimiter according which to split the string.
None (the default value) means split according to any whitespace,
and discard empty strings from the result.
maxsplit
Maximum number of splits to do.
-1 (the default value) means no limit.
Splits are done starting at the end of the string and working to the front.
rstrip(self, chars=None, /)
Return a copy of the string with trailing whitespace removed.
If chars is given and not None, remove characters in chars instead.
split(self, /, sep=None, maxsplit=-1)
Return a list of the words in the string, using sep as the delimiter string.
sep
The delimiter according which to split the string.
None (the default value) means split according to any whitespace,
and discard empty strings from the result.
maxsplit
Maximum number of splits to do.
-1 (the default value) means no limit.
splitlines(self, /, keepends=False)
Return a list of the lines in the string, breaking at line boundaries.
Line breaks are not included in the resulting list unless keepends is given and
true.
startswith(…)
S.startswith(prefix[, start[, end]]) -> bool
Return True if S starts with the specified prefix, False otherwise.
With optional start, test S beginning at that position.
With optional end, stop comparing S at that position.
prefix can also be a tuple of strings to try.
strip(self, chars=None, /)
Return a copy of the string with leading and trailing whitespace removed.
If chars is given and not None, remove characters in chars instead.
swapcase(self, /)
Convert uppercase characters to lowercase and lowercase characters to uppercase.
title(self, /)
Return a version of the string where each word is titlecased.
More specifically, words start with uppercased characters and all remaining
cased characters have lowercase.
translate(self, table, /)
Replace each character in the string using the given translation table.
table
Translation table, which must be a mapping of Unicode ordinals to
Unicode ordinals, strings, or None.
The table must implement lookup/indexing via getitem , for instance a
dictionary or list. If this operation raises LookupError, the character is
left untouched. Characters mapped to None are deleted.
upper(self, /)
Return a copy of the string converted to uppercase.
zfill(self, width, /)
Pad a numeric string with zeros on the left, to fill a field of the given width.
The string is never truncated.
Static methods defined here:
new (*args, **kwargs) from builtins.type
Create and return a new object. See help(type) for accurate signature.
maketrans(…)
Return a translation table usable for str.translate().
If there is only one argument, it must be a dictionary mapping Unicode
ordinals (integers) or characters to Unicode ordinals, strings or None.
Character keys will be then converted to ordinals.
If there are two arguments, they must be strings of equal length, and
in the resulting dictionary, each character in x will be mapped to the
character at the same position in y. If there is a third argument, it
must be a string, whose characters will be mapped to None in the result.
Super keyword in Python
The super() keyword in Python is used to refer to the parent class. It is especially useful when a class inherits from multiple parent classes and you want to call a method from one of the parent classes.
When a class inherits from a parent class, it can override or extend the methods defined in the parent class. However, sometimes you might want to use the parent class method in the child class. This is where the super() keyword comes in handy.
Here’s an example of how to use the super() keyword in a simple inheritance scenario:
class ParentClass:
def parent_method(self):
print(“This is the parent method.”)
class ChildClass(ParentClass): def child_method(self):
print(“This is the child method.”) super().parent_method()
child_object = ChildClass() child_object.child_method()
This is the child method.
This is the parent method.
n this example, we have a ParentClass with a parent_method and a ChildClass that inherits from ParentClass and overrides the child_method. When the child_method is called, it first prints “This is the child method.” and then calls the parent_method using the super() keyword.
The super() keyword is also useful when a class inherits from multiple parent classes. In this case, you can specify the parent class from which you want to call the method.
Here's an example:
class ParentClass1:
def parent_method(self):
print(“This is the parent method of ParentClass1.”)
class ParentClass2:
def parent_method(self):
print(“This is the parent method of ParentClass2.”)
class ChildClass(ParentClass1, ParentClass2): def child_method(self):
print(“This is the child method.”) super().parent_method()
child_object = ChildClass() child_object.child_method()
This is the child method.
This is the parent method of ParentClass1.
In this example, the ChildClass inherits from both ParentClass1 and ParentClass2. The child_method calls the parent_method of the first parent class using the super() keyword.
In conclusion, the super() keyword is a useful tool in Python when you want to call a parent class method in a child class. It can be used in inheritance scenarios with a single parent class or multiple parent classes.
Magic/Dunder Methods in Python
These are special methods that you can define in your classes, and when invoked, they give you a powerful way to manipulate objects and their behaviour.
Magic methods, also known as “dunders” from the double underscores surrounding their names, are powerful tools that allow you to customize the behaviour of your classes. They are used to implement special methods such as the addition, subtraction and comparison operators, as well as some more advanced techniques like descriptors and properties.
Let’s take a look at some of the most commonly used magic methods in Python.
init method
The init method is a special method that is automatically invoked when you create a new instance of a class. This method is responsible for setting up the object’s initial state, and it is where you would typically define any instance variables that you need. Also called “constructor”, we have discussed this method already
str and repr methods
The str and repr methods are both used to convert an object to a string representation. The str method is used when you want to print out an object, while the repr method is used when you want to get a string representation of an object that can be used to recreate the object.
len method
The len method is used to get the length of an object. This is useful when you want to be able to find the size of a data structure, such as a list or dictionary.
call method
The call method is used to make an object callable, meaning that you can pass it as a parameter to a function and it will be executed when the function is called. This is an incredibly powerful tool that allows you to create objects that behave like functions.
These are just a few of the many magic methods available in Python. They are incredibly powerful tools that allow you to customize the behaviour of your objects, and can make your code much cleaner and easier to understand. So if you’re looking for a way to take your Python code to the next level, take some time to learn about these magic methods.
Method Overriding in Python
Method overriding is a powerful feature in object-oriented programming that allows you to redefine a method in a derived class. The method in the derived class is said to override the method in the base class. When you create an instance of the derived class and call the overridden method, the version of the method in the derived class is executed, rather than the version in the base class.
In Python, method overriding is a way to customize the behavior of a class based on its specific needs. For example, consider the following base class:
class Shape:
def area(self): pass
In this base class, the area method is defined, but does not have any implementation. If you want to create a derived class that represents a circle, you can override the area method and provide an implementation that calculates the area of a circle:
class Circle(Shape):
def init (self, radius): self.radius = radius
def area(self):
return 3.14 * self.radius * self.radius
In this example, the Circle class inherits from the Shape class, and overrides the area method. The new implementation of the area method calculates the area of a circle, based on its radius.
It’s important to note that when you override a method, the new implementation must have the same method signature as the original method. This means that the number and type of arguments, as well as the return type, must be the same.
Another way to customize the behavior of a class is to call the base class method from the derived class method. To do this, you can use the super function. The super function allows you to call the base class method from the derived class method, and can be useful when you want to extend the behavior of the base class method, rather than replace it.
For example, consider the following base class:
class Shape:
def area(self): print(“Calculating area…”)
In this base class, the area method prints a message indicating that the area is being calculated. If you want to create a derived class that represents a circle, and you also want to print a message indicating the type of shape, you can use the super function to call the base class method, and add your own message:
class Circle(Shape):
def init (self, radius): self.radius = radius
def area(self):
print(“Calculating area of a circle…”) super().area()
return 3.14 * self.radius * self.radius
In this example, the Circle class overrides the area method, and calls the base class method using the super function. This allows you to extend the behavior of the base class method, while still maintaining its original behavior.
In conclusion, method overriding is a powerful feature in Python that allows you to customize the behavior of a class based on its specific needs. By using method overriding, you can create more robust and reliable code, and ensure that your classes behave in the way that you need them to. Additionally, by using the super function, you can extend the behavior of a base class method, rather than replace it, giving you even greater flexibility and control over the behavior of your classes.
Operator Overloading in Python: An Introduction
Operator Overloading is a feature in Python that allows developers to redefine the behavior of mathematical and comparison operators for custom data types. This means that you can use the standard mathematical operators (+, -, *, /, etc.) and comparison operators (>, <, ==, etc.) in your own classes, just as you would for built-in data types like int, float, and str.
Why do we need operator overloading?
Operator overloading allows you to create more readable and intuitive code. For instance, consider a custom class that represents a point in 2D space. You could define a method called ‘add’ to add two points together, but using the + operator makes the code more concise and readable:
class Circle(Shape):
def init (self, radius): self.radius = radius
def area(self):
return 3.14 * self.radius * self.radius
How to overload an operator in Python?
You can overload an operator in Python by defining special methods in your class.
These methods are identified by their names, which start and end with double underscores ( ).
Here are some of the most commonly overloaded operators and their corresponding special methods:
+ : add
– : sub
* : mul
/ : truediv
< : lt
> : gt
== : eq
For example, if you want to overload the + operator to add two instances of a custom class, you would define the add method
class Point:
def init (self, x, y): self.x = x
self.y = y
def add (self, other):
return Point(self.x + other.x, self.y + other.y)
It’s important to note that operator overloading is not limited to the built-in operators, you can overload any user-defined operator as well.
Conclusion
Operator overloading is a powerful feature in Python that allows you to create more readable and intuitive code. By redefining the behavior of mathematical and comparison operators for custom data types, you can write code that is both concise and expressive. However, it’s important to use operator overloading wisely, as overloading the wrong operator or using it inappropriately can lead to confusing or unexpected behavior.
Single Inheritance in Python
Single inheritance is a type of inheritance where a class inherits properties and behaviors from a single parent class. This is the simplest and most common form of inheritance.
Syntax
The syntax for single inheritance in Python is straightforward and easy to understand. To create a new class that inherits from a parent class, simply specify the parent class in the class definition, inside the parentheses, like this:
class ChildClass(ParentClass):
# Class Body
Example:
Let’s consider a simple example of single inheritance in Python. Consider a class named “Animal” that contains the attributes and behaviors that are common to all animals.
class Animal:
def init (self, name, species): self.name = name
self.species = species
def make_sound(self):
print(“Sound made by the animal”)
If we want to create a new class for a specific type of animal, such as a dog, we can create a new class named
“Dog” that inherits from the Animal class.
class Dog(Animal):
def init (self, name, breed):
Animal. init (self, name, species=”Dog”) self.breed = breed
def make_sound(self): print(“Bark!”)
The Dog class inherits all the attributes and behaviors of the Animal class, including the init method and the make_sound method. Additionally, the Dog class has its own init method that adds a new attribute for the breed of the dog, and it also overrides the make_sound method to specify the sound that a dog makes.
Single inheritance is a powerful tool in Python that allows you to create new classes based on existing classes. It allows you to reuse code, extend it to fit your needs, and make it easier to manage complex systems.
Understanding single inheritance is an important step in becoming proficient in object-oriented programming in Python
reduce(function, iterable)
Multiple Inheritance in Python
Multiple inheritance is a powerful feature in object-oriented programming that allows a class to inherit attributes and methods from multiple parent classes. This can be useful in situations where a class needs to inherit functionality from multiple sources.
Syntax
In Python, multiple inheritance is implemented by specifying multiple parent classes in the class definition, separated by commas.
class ChildClass(ParentClass1, ParentClass2, ParentClass3):
# class body
In this example, the ChildClass inherits attributes and methods from all three parent classes: ParentClass1, ParentClass2, and ParentClass3.
It’s important to note that, in case of multiple inheritance, Python follows a method resolution order (MRO) to resolve conflicts between methods or attributes from different parent classes. The MRO determines the order in which parent classes are searched for attributes and methods.
class Animal:
def init (self, name, species): self.name = name
self.species = species
def make_sound(self):
print(“Sound made by the animal”)
class Mammal:
def init (self, name, fur_color): self.name = name
self.fur_color = fur_color
class Dog(Animal, Mammal):
def init (self, name, breed, fur_color): Animal. init (self, name, species=”Dog”) Mammal. init (self, name, fur_color) self.breed = breed
def make_sound(self): print(“Bark!”)
In this example, the Dog class inherits from both the Animal and Mammal classes, so it can use attributes and methods from both parent classes.
Multilevel Inheritance in Python
Multilevel inheritance is a type of inheritance in object-oriented programming where a derived class inherits from another derived class. This type of inheritance allows you to build a hierarchy of classes where one class builds upon another, leading to a more specialized class.
In Python, multilevel inheritance is achieved by using the class hierarchy. The syntax for multilevel inheritance is quite simple and follows the same syntax as single inheritance.
Syntax
class BaseClass:
# Base class code
class DerivedClass1(BaseClass):
# Derived class 1 code
class DerivedClass2(DerivedClass1):
# Derived class 2 code
In the above example, we have three classes: BaseClass, DerivedClass1, and DerivedClass2. The DerivedClass1 class inherits from the BaseClass, and the DerivedClass2 class inherits from the DerivedClass1 class. This creates a hierarchy where DerivedClass2 has access to all the attributes and methods of both DerivedClass1 and BaseClass.
Example:
Let’s take a look at an example to understand how multilevel inheritance works in Python. Consider the following classes
class Animal:
def init (self, name, species): self.name = name
self.species = species
def show_details(self): print(f”Name: {self.name}”) print(f”Species: {self.species}”)
class Dog(Animal):
def init (self, name, breed):
Animal. init (self, name, species=”Dog”) self.breed = breed
def show_details(self): Animal.show_details(self) print(f”Breed: {self.breed}”)
class GoldenRetriever(Dog):
def init (self, name, color):
Dog. init (self, name, breed=”Golden Retriever”) self.color = color
ef show_details(self): Dog.show_details(self) print(f”Color: {self.color}”)
In this example, we have three classes: Animal, Dog, and GoldenRetriever. The Dog class inherits from the Animal class, and the GoldenRetriever class inherits from the Dog class.
Now, when we create an object of the GoldenRetriever class, it has access to all the attributes and methods of the Animal class and the Dog class. We can also see that the GoldenRetriever class has its own attributes and methods that are specific to the class.
dog = GoldenRetriever(“Max”, “Golden”) dog.show_details()
Name: Max
Species:Dog
Breed: Golden Retriever
Color: Golden
As we can see from the output, the GoldenRetriever object has access to all the attributes and methods of the Animal and Dog classes, and, it has also added its own unique attributes and methods. This is a powerful feature of multilevel inheritance, as it allows you to create more complex and intricate classes by building upon existing ones.
Another important aspect of multilevel inheritance is that it allows you to reuse code and avoid repeating the same logic multiple times. This can lead to better maintainability and readability of your code, as you can abstract away complex logic into base classes and build upon them.
In conclusion, multilevel inheritance is a powerful feature in object-oriented programming that allows you to create complex and intricate classes by building upon existing ones. It provides the benefits of code reuse, maintainability, and readability, while also requiring careful consideration to avoid potential problems.
Hybrid Inheritance in Python
Hybrid inheritance is a combination of multiple inheritance and single inheritance in object-oriented programming. It is a type of inheritance in which multiple inheritance is used to inherit the properties of multiple base classes into a single derived class, and single inheritance is used to inherit the properties of the derived class into a sub-derived class.
In Python, hybrid inheritance can be implemented by creating a class hierarchy, in which a base class is inherited by multiple derived classes, and one of the derived classes is further inherited by a sub-derived class.
Syntax
The syntax for implementing Hybrid Inheritance in Python is the same as for implementing Single Inheritance, Multiple Inheritance, or Hierarchical Inheritance.
Here’s the syntax for defining a hybrid inheritance class hierarchy:
class BaseClass1:
# attributes and methods
class BaseClass2:
# attributes and methods
class DerivedClass(BaseClass1, BaseClass2):
# attributes and methods
Example:
Consider the example of a Student class that inherits from the Person class, which in turn inherits from the Human class. The Student class also has a Program class that it is associated with.
class Human:
def init (self, name, age): self.name = name
self.age = age
def show_details(self): print(“Name:”, self.name) print(“Age:”, self.age)
class Person(Human):
def init (self, name, age, address): Human. init (self, name, age) self.address = address
def show_details(self): Human.show_details(self) print(“Address:”, self.address)
class Program:
def init (self, program_name, duration): self.program_name = program_name self.duration = duration
def show_details(self):
print(“Program Name:”, self.program_name) print(“Duration:”, self.duration)
class Student(Person):
def init (self, name, age, address, program): Person. init (self, name, age, address) self.program = program
def show_details(self): Person.show_details(self) self.program.show_details()
In this example, the Student class inherits from the Person class, which in turn inherits from the Human class. The Student class also has an association with the Program class. This is an example of Hybrid Inheritance in action, as it uses both Single Inheritance and Association to achieve the desired inheritance structure.
To create a Student object, we can do the following:
program = Program(“Computer Science”, 4)
student = Student(“John Doe”, 25, “123 Main St.”, program) student.show_details()
As we can see from the output, the Student object has access to all the attributes and methods of the Person and Human classes, as well as the Program class through association.
In this way, hybrid inheritance allows for a flexible and powerful way to inherit attributes and behaviors from multiple classes in a hierarchy or chain.
Hierarchical Inheritance
Hierarchical Inheritance is a type of inheritance in Object-Oriented Programming where multiple subclasses inherit from a single base class. In other words, a single base class acts as a parent class for multiple subclasses. This is a way of establishing relationships between classes in a hierarchical manner.
Here's an example to illustrate the concept of hierarchical inheritance in Python:
class Animal:
def init (self, name): self.name = name
def show_details(self): print(“Name:”, self.name)
class Dog(Animal):
def init (self, name, breed): Animal. init (self, name) self.breed = breed
def show_details(self): Animal.show_details(self) print(“Species: Dog”) print(“Breed:”, self.breed)
class Cat(Animal):
def init (self, name, color): Animal. init (self, name) self.color = color
def show_details(self): Animal.show_details(self) print(“Species: Cat”) print(“Color:”, self.color)
In the above code, the Animal class acts as the base class for two subclasses, Dog and Cat. The Dog class and the Cat class inherit the attributes and methods of the Animal class. However, they can also add their own unique attributes and methods.
Here's an example of creating objects of the Dog and Cat classes and accessing their attributes and methods:
dog = Dog(“Max”, “Golden Retriever”) dog.show_details()
cat = Cat(“Luna”, “Black”) cat.show_details()
Name: Max Species: Dog
Breed: Golden Retriever Name: Luna
Species: Cat Color: Black
As we can see from the outputs, the Dog and Cat classes have inherited the attributes and methods of the Animal class, and have also added their own unique attributes and methods.
In conclusion, hierarchical inheritance is a way of establishing relationships between classes in a hierarchical manner. It allows multiple subclasses to inherit from a single base class, which helps in code reuse and organization of code in a more structured manner.