Parts
- Most Asked Python Interview Questions — Part-1 (Classes)
- Most Asked Python Interview Questions — Part-2 (Exception Handling)
If you’re preparing for a Python interview, it’s essential to have a solid understanding of the language’s fundamental concepts. To help you with that, I have categorized this interview question series into different parts, each focusing on a specific area. today we will dive into classes in Python and explore some frequently asked interview questions related to this topic.
In this part, we will discuss
- Name Mangling
- Class Variables
- MRO
Name mangling
Name mangling is a technique used to change the name of a class variable to avoid naming conflicts between parent and child classes.
In Python, when a variable is prefixed with double underscores (e.g., __variable), the interpreter automatically changes its name to include the class name. This modification helps prevent accidental overriding of variables in inherited classes.
Such variables are also treated as a private member of a class.
Exercise-1
What will be the output of the following code?
class MyClass:
def __init__(self):
self.__variable = 42
obj = MyClass()
print(obj.__variable)
Expected Output:
AttributeError: 'MyClass' object has no attribute '__variable'
You maybe wondering why it is raising an error?
BECAUSE! Name mangling changes the name of the variable __variable
to _MyClass__variable
. Therefore, attempting to access __variable
directly raises an AttributeError
. To access the variable, you need to use the modified name _MyClass__variable
. Here’s an example that demonstrates how to access a name-mangled variable:
class MyClass:
def __init__(self):
self.__variable = 42
obj = MyClass()
print(obj._MyClass__variable)
Exercise-2
class A:
def __init__(self):
self.__variable = 42
def display(self):
print(self.__variable)
class B(A):
def __init__(self):
super().__init__()
self.__variable = 99
obj = B()
obj.display()
Expected Output:
42
You may guessed that it will print 99 but in actual, it will print 42 and it is because of name mangling.
During name mangling, the interpreter renames the variable by adding _classname
as a prefix to the variable name.
In this case,
in Class A, __variable
is modified to _A__variable
. Similarly,
in Class B, __variable
is modified to _B__variable
.
Since the display
method is defined in Class A, when it accesses self.__variable
, it is actually accessing self._A__variable
.
Bonus Question: Will it work if we remove
super().__init__()
from class B?
Class Variables
A class variable in Python is a variable that is shared among all instances (objects) of a class. Class variables are associated with the class itself rather than with any specific instance of the class.
Exercise-1
class A:
count = 0
def __init__(self):
A.count += 1 @classmethod
def get_count(cls):
return cls.count
obj_one = A()
obj_two = A()print(obj_one.get_count())
print(obj_two.get_count())
Expected Output:
2
2
Becausecount
is class variable so the increment on count will impact all the instances of class A
Exercise-2
class A:
count = 0
def __init__(self):
self.count += 1 @classmethod
def get_count(cls):
return cls.count
obj_one = A()
obj_two = A()print(obj_one.get_count())
print(obj_two.get_count())
Expected Output:
0
0
The reason why it is returning 0 for both obj_one.get_count()
and obj_two.get_count()
is because of the self
keyword inside__init__
method.
In the __init__
method, self.count += 1
is attempting to increment the count
variable for each instance. However, since count
is a class variable (declared directly within the class), it is not specific to any instance and is shared by all instances of the class. Therefore, when you create obj_one
and obj_two
, both of them share the same count
variable, and the value of count
remains 0.
Exercise-3
class A:
count = 0
def __init__(self):
A.count += 1 @classmethod
def get_count(cls):
return cls.count
class B(A):
pass def __init__(self):
B.count += 1
a = A()
b = B()print(a.get_count())
print(b.get_count())
Expected Output:
1
2
Ever wonder why it is not returning 2 2
for both instances?
The reason why it is returning 1
for a.get_count()
and 2
for b.get_count()
is due to the way the __init__
method is defined in class B
.
In the class A
, the __init__
method increments the count
variable using A.count += 1
, which increments the class variable count
every time a new instance of class A
is created. Therefore, when you create the instance a
, it increments the count to 1.
In class B
, we have not defined the count
variable explicitly, so it inherits the count
variable from class A
. However, the __init__
method in class B
tries to increment B.count
, which is not a class variable. Instead, it creates a new instance variable specific to class B
, leaving the class variable count
of class A
unaffected.
MRO (Method Resolution order)
MRO, a most favorite topic of all interviewers. MRO refers to the order in which classes are searched for a requested method
or attribute
in the presence of multiple inheritance.
Python uses the C3 linearization Algorithm
to determine the MRO. The MRO is important because it defines the order in which the methods of different classes are invoked when using inheritance. It ensures that the methods are resolved in a consistent and predictable manner.
Just memorize that the MRO follows a depth-first left-to-right
traversal.
Exercise-1
class A:
pass
class B(A):
pass
class C(A):
pass
class D(B, C):
pass
print(D.mro())
Expected Output:
[<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>]
As I told you earlier, MRO perform depth-first left-to-right
.
In this case, the MRO is: D
-> B
-> C
-> A
-> object
.
This order ensures that when a method or attribute is called on an instance of class D
, Python will first search in class D
, then in class B
, followed by class C
, then class A
, and finally in the built-in object
class.
Exercise-2:
class A:
def greet(self):
return "Hello from A"
class B(A):
pass
class C(A):
def greet(self):
return "Hello from C"
class D(B, C):
pass
d = D()print(d.greet())
Expected Output:
Hello from C
Class D
inherits from both classes B
and C
, and both B
and C
inherit from class A
.
And the MRO is D
-> B
-> C
-> A
-> object
. Python first checks if greet()
exists in class D
, then in class B
, but finds it in class C
. As a result, the output of d.greet()
is "Hello from C"
, as the greet()
method from class C
takes precedence in the MRO over the method in class A
.
Exercise-3
class A:
def greet(self):
return "Hello from A"
class B:
def greet(self):
return "Hello from B"
class C(A, B):
pass
class D(C, B):
pass
d = D()
print(d.greet())
Expected Output:
Hello from A
If we print the MRO, it will be
[<class '__main__.D'>, <class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class 'object'>]
As you can see, it is D
-> C
-> A
-> B
-> object
.
Python first checks if greet()
exists in class D
, then in class C
, and eventually finds it in class A
. Thus, the output of d.greet()
is "Hello from A"
, as the greet()
method from class A
takes precedence in the MRO over the method in class B
.
Final words
I hope these three concepts i.e Name mangling
,Class Variables
and MRO
helps you out either to prepare for the interview or to refresh your concepts. See ya till next part. HAPPY CODING!
Don’t forget to connect on LinkedIn