Explore topic-wise InterviewSolutions in .

This section includes InterviewSolutions, each offering curated multiple-choice questions to sharpen your knowledge and support exam preparation. Choose a topic below to get started.

51.

What are various string formatting features available in Python?

Answer»

FORMAT specification symbols (%d, %F, %s etc) are popularly used in C/C++ for formatting strings. These symbols can be used in Python also. Just as in C, a string is constructed by substituting these symbols with Python objects.

In the example below, we use the symbols %s and %d in the string and substitute them by values of objects in tuple OUTSIDE the string, prefixed by % symbol

>>> name='Ravi' >>> AGE=21 >>> string="Hello. My name is %s. I am %d years old" %(name,age) >>> string 'Hello. My name is Ravi. I am 21 years old'

In numeric formatting symbols, the width of number can be specified before and/or after a decimal point.

>>> price=50 >>> weight=21.55 >>> string="price=%3d weight=%3.3f" %(price,weight) >>> string 'price= 50 weight=21.550'

Since Python 3.x a new format() method has been added in built-in string class which is more efficient and elegant, and is prescribed to be the recommended way of formatting with variable substitution.

Instead of  % formatting operator, {} symbols are used as place HOLDERS.

>>> name='Ravi' >>> age=21 >>> string="Hello. My name is {}. I am {} years old".format(name,age) >>> string 'Hello. My name is Ravi. I am 21 years old'

Format specification symbols are used with : instead of %. So to insert a string in place holder use {:s} and for integer use {:d}. Width of numeric and string variables is specified as before.

>>> price=50 >>> weight=21.55 >>> string="price={:3d} quantity={:3.3f}".format(price,weight) >>> string 'price= 50 quantity=21.550'
52.

Explain the concept of global, local and nonlocal variables in Python.

Answer»

Depending upon where in the program a certain variable is declared, it is classified as a global, local or nonlocal variable. As the name suggests, any variable accessible to any function is a global variable. Any variable declared before a call to function (or any block of statements) is having global scope.

DEF square():    print (X*x) x=10 square() output: 100

Here, x is a global variable hence it can be accessed from anywhere, including from square() function. If we modify it in the function, its effect is persistent to global variable.

def square():    x=20    print ('in function',x) x=10 square() print ('outside function',x) output: in function 20 outside function 10

However, if we try to increment x in the function, it raises UnBoundLocalError

def square():    x=x+1    print ('in function',x) x=10 square() print ('outside function',x) output:    x=x+1 UnboundLocalError: local variable 'x' referenced before assignment

A local variable is one which is declared inside any block of statements such as a function. Such a variable has only local presence which means, if we try to access outside its scope, an ERROR will be reported.

def square():    x=10    x=x**2    print ('in function',x) square() print ('outside function',x) output:    print ('outside function',x) NameError: name 'x' is not defined

Note that if we use a variable of the same name as that available in the global namespace inside a function, it becomes a local variable as far as the local namespace is concerned. Hence, it will not modify the global variable.

def square():    x=5    x=x**2    print ('in function',x) x=100 square() print ('outside function',x) output: in function 25 outside function 100

If however, you insist that your function needs to access global and local variables of the same name, you need to refer the former with ‘global’ keyword.

def square():    global x    x=x**2    print ('global x in function',x) x=10 square() print ('global x outside function',x) output: global x in function 100 global x outside function 100

Python's global() function returns a dictionary object of all global variables keys and their respective values.

def square():    print ('global x in function',globals()['x']) x=10 square() output: global x in function 10

Python 3 introduced nonlocal keyword. It should be used to refer to a variable in the scope of an outer function from an inner function. Python does allow definition of nested functions. This nonlocal variable is useful in this context.

In following program function2 is nested inside function1 which is having var1 as its local variable. For function2 to refer to var1 it has to use a nonlocal keyword because it is neither a local nor a global variable.

def function1():    var1=20    def function2():        nonlocal var1        var1=50        print (var1)    print ('nonlocal var1 before inner function',var1)    function2()    print ('nonlocal var1 after inner function',var1) var1=10 function1() print ('global var1 after outer function unchanged',var1) output: nonlocal var1 before inner function 20 50 nonlocal var1 after inner function 50 global var1 after outer function unchanged 10
53.

How can we check if a certain class is a subclass of another?

Answer»

Python offers more than one way to CHECK if a certain class is inherited from the other. In Python each class is a subclass of object class. This can be verified by running built-in issubclass() function on class A as follows:

>>> class A: pass >>> issubclass(A,object) True

We now CREATE B class that is inherited from A. The issubclass() function returns true for A as well as object class.

>>> class B(A): pass >>> issubclass(B,A) True >>> issubclass(B,object) True

You can also use __bases__ attribute of a class to find out its super class.

>>> A.__bases__ (<class 'object'>,) >>> B.__bases__ (<class '__main__.A'>,)

FINALLY the mro() method returns method resolution ORDER of methods in an inherited class. In our case B is inherited from A, which in turn is a subclass of object class.

>>> B.mro() [<class '__main__.B'>, <class '__main__.A'>, <class 'object'>]
54.

Explain the advantage of ‘x’ as value of mode parameter in open() function.

Answer»

Python’s built-in open() function returns a file object representing a disk file ALTHOUGH it can also be used to open any stream such as byte stream or network stream. The open() function takes two arguments:

F=open(“filename”, mode)

Default value of mode parameter is ‘r’ which STANDS for reading mode. To open a file for storing data, the mode should be set to ‘w’

f=open(‘file.txt’,’w’) f.write(‘Hello World’) f.close()

However, ‘w’ mode causes OVERWRITING file.txt if it is earlier present, thereby the earlier saved data is at risk of being lost. You can of course check if the file already exists before opening, using os module function:

import os if os.path.exists('file.txt')==False:    f=open('file.txt','w')    f.write('Hello world')    f.close()

This can create a race condition though because of simultaneous CONDITIONAL operation and open() functions. To avoid this ‘x’ mode has been introduced, starting from Python 3.3. Here ‘x’ stands for exclusive. Hence, a file will be opened for write operation only if it doesn’t exist already. Otherwise Python raises FileExistsError

try:    with open('file.txt','x') as f:        f.write("Hello World")    except FileExistsError:        print ("File already exists")

Using ‘x’ mode is safer than checking the existence of file before writing and guarantees avoiding accidental overwriting of files.

55.

Explain the concept of packages in Python

Answer»

A module in Python is a Python script that may have DEFINITIONS of class, functions, variables ETC. having certain SIMILAR nature or intended for a common purpose. For example math module in Python LIBRARY contains all frequently required mathematical functions. This MODULAR approach is taken further by the concept of package. A package may contain multiple modules or even subpackages which may be a part of resources required for a certain purpose. For example Python library’s Tkinter package contains various modules that contain functionality required for building GUI.

Hierarchical arrangement of modules and subpackages is similar to the operating system’s file system. Access to a certain file in the file tree is obtained by using ‘/’. For example a file in a certain folder may have its path as c:\newdir\subdir\file.py. Similarly a module in newdir package will be named as newdir.subdir.py

For Python to recognize any folder as a package, it must have a special file named __init__.py which may or may not be empty and also serves as a packaging list by specifying resources from its modules to be imported.

Modules and their contents in a package can be imported by some script which is at the same level. If we have a.py, b.py and c.py modules and __init__.py file in a test folder under newdir folder, one or more modules can be imported by test.py in newdir as under:

#c:/newdir/test.py from test import a a.hello() #to import specific function from test.a import hello hello()

The __init__.py file can be customized to allow package level access to functions/classes in the modules under it:

#__init__.py from .a import hello from .b import sayhello

Note that hello() function can now be access from the package instead of its module

#test.py import test test.hello()

The test package is now being used by a script which is at the same level as the test folder. To make it available for system-wide use, prepare the following setup script:

#setup.py from setuptools import setup setup(name='test',      version='0.1',      description='test package',      url='#',      author='....',      author_email='...@...',      license='MIT',      packages=['test'],      zip_safe=False)

To install the test package:

C:\newdir>pip install .

Package is now available for import from anywhere in the file system. To make it publicly available, you have to upload it to PyPI repository.

56.

Explain how useful Python’s ‘with’ statement is.

Answer»

The intention of ‘with’ statement in Python is to make the code cleaner and much more readable. It simplifies the management of common resources like FILE streams.

The ‘with’ statement establishes a context manager object that defines the runtime context. The context manager handles the entry into, and the exit from, the desired runtime context for the execution of the block of code. Context managers are NORMALLY invoked using the ‘with’ statement.

Here is a typical use of with statement. Normally a file object is declared with built-in open() function. After performing file read/write operations the file object must be relieved from memory by its close() method failing which the disk file/stream underneath the object may be corrupted.

>>> F=open('file.txt','w') >>> f.write('Hello') >>> f.close()

Use of ‘with’ statement sets up a context for the FOLLOWING block of statements. As the block finishes, the context object also automatically goes out of scope. It need not be explicitly destroyed.

>>> with open('file.txt','w') as f: f.write('Hello')

Note that the object f is no longer ALIVE after the block and you don’t have to call close() method as earlier. Hence, the code becomes clean and easy to read.

The file object acts as a context manager object by default. To define a custom context manager, the class must have __enter__() and __exit__() methods.

class Mycontext:    def __init__(self):        print ('object initialized')    def __enter__(self):        print ('context manager entered')    def __exit__(self, type, value, traceback):        print ('context manager exited') with Mycontext() as x:    print ('context manager example')

Output:

object initialized context manager entered context manager example context manager exited
57.

One of the 33 keywords of Python is lambda. How and for what purpose is it used?

Answer»

The def keyword is used to define a NEW function with a user specified name. Lambda keyword on the other hand is used to CREATE an ANONYMOUS (un-named) function. Usually such a function is created on the fly and meant for one-time use. The general syntax of lambda is as follows:

lambda arg1, arg2… : expression

A lambda function can have any number of arguments but there’s always a single expression after : symbol. The value of the expression becomes the return value of the lambda function. For example

>>> add=lambda a,b:a+b

This is an anonymous function declared with lambda keyword. It receives a and b and returns a+b.. The anonymous function object is assigned to identifier CALLED add. We can now use it as a regular function call

>>> print (add(2,3)) 5

The above lambda function is equivalent to a normal function declared using def keyword as below:

>>> def add(a,b): return a+b >>> print (add(2,3)) 5

However, the body of the lambda function can have only one expression. Therefore it is not a substitute for a normal function having complex logic involving conditionals, loops etc.

In Python, a function is a called a first order object, because function can ALSO be used as argument, just as number, string, list etc. A lambda function is often used as argument function to functional programming tools such as map(), filter() and reduce() functions.

The built-in  map() function subjects each element in the iterable to another function which may be either a built-in function, a lambda function or a user defined function and returns the mapped object. The map() function needs two arguments:

map(Function, Sequence(s))

Next example uses a lambda function as argument to map() function. The lambda function itself takes two arguments taken from two lists and returns the first number raised to second. The resulting mapped object is then parsed to output list.

>>> powers=map(lambda x,y: x**y, [10,20,30], [1,2,3]) >>> list(powers) [10, 400, 27000]

The filter() function also receives two arguments, a function with Boolean return value and an iterable. Only those items for whom the function returns True are stored in a filter object which can be further cast to a list.

Lambda function can also be used as a filter. The following program uses lambda function that filters all odd numbers from a given range.

>>> list(filter(lambda x: x%2 == 1,range(1,11))) [1, 3, 5, 7, 9]
58.

Does Python support data encapsulation?

Answer»

As per the principles of object oriented programming, data encapsulation is a mechanism by which instance attributes are prohibited from direct access by any environment outside the class. In C++ / Java, with the provision of access control keywords such as public, private and protected it is easy to enforce data encapsulation. The instance variables are usually restricted to have private access, though the methods are publicly accessible.

However, Python doesn’t follow the doctrine of controlling access to instance or method attributes of a class. In a way, all attributes are public by default. So in a strict sense, Python doesn’t support data encapsulation. In the following example, name and age are instance attributes of User class. They can be directly manipulated from outside the class environment.

>>> class User: def __init__(self): self.name='Amar' self.age=20 >>> a=User() >>> a.name 'Amar' >>> a.age 20 >>> a.age=21

Python even has built-in functions getattr() and setattr() to fetch and set values of an instance ATTRIBUTE.

>>> getattr(a, 'name') 'Amar' >>> setattr(a,'name','Ravi') >>> a.name 'Ravi'

Having said that, Python does have means to emulate private keywords in Java/C++. An instance VARIABLE prefixed with ‘__’ (double underscore) behaves as a private variable – which means direct access to it will raise an exception as below:

>>> class User: def __init__(self): self.__name='Amar' self.__age=20 >>> a=User() >>> a.__name Traceback (most recent call last):  File "<pyshell#18>", line 1, in <module>    a.__name AttributeError: 'User' object has no attribute '__name'

However, the double underscore prefix doesn’t make price truly private (as in case of Java/C++). It merely performs name mangling by internally renaming the private variable by ADDING the "_ClassName" to the front of the variable. In this case, variable named "__name" in Book class will be MANGLED in “_User__name” form.

>>> a._User__name 'Amar'

Protected member is (in C++ and Java) accessible only from within the class and its subclasses. Python accomplishes this behaviour by convention of prefixing the name of your member with a single underscore. You’re telling others “don’t TOUCH this, unless you’re a subclass”.

59.

Which type of function calling mechanism is employed in Python? Call by value or Call by reference?

Answer»

Calling a function by value is found to be USED in C/C++ where a variable is actually a named location in computer memory. Hence the passed expression is copied into the formal argument which happens to be a local variable of the CALLED function. Any manipulation of that local variable doesn’t affect the variable that was actually passed.

The following C PROGRAM has square() function. It is called from the main by passing x which is copied to the local variable in square(). The change inside called function doesn’t have impact on x in main()

#include <stdio.h> int main() {    int square(int); int x=10; printf("\nx before passing: %d",x); square(x); printf("\nx after passing: %d",x); } int square(int x) { x=x*x; } result: x before passing: 10 x after passing: 10

In Python though, a variable is just a label of an object in computer memory. Hence formal as well as actual argument variables in fact are two different labels of same object – in this case 10. This can be verified by id() function.

>>> def square(y): print (id(y)) >>> x=10 >>> id(x) 94172959017792 >>> square(x) 94172959017792

Hence we can infer that Python calls a function by passing reference of actual arguments to formal arguments. However, what happens when the function manipulates the received object depends on the type of object.

When a function makes modifications to the immutable object received as an argument,  changes do not reflect in the object that was passed.

>>> def square(y): print ('received',id(y)) y=y**2 print ('changed',id(y),'y=',y) >>> x=10 >>> id(x) 94172959017792 >>> square(x) received 94172959017792 changed 94172959020672 y= 100 >>> x 10

It can be seen that x=10 is passed to the square() function. Inside, y is changed. However, y becomes a label of another object 100, which is having different id(). This change doesn’t reflect in x – it being an immutable object. The same thing happens to a string or tuple.

However, if we PASS a MUTABLE object, any changes by called function will also have effect on that object after returning from the function.

In the example below, we pass a list (it is a mutable object) to a function and then add some more elements to it. After returning from the function, the original list is found to be modified.

>>> def newlist(list): print ('received', list, id(list)) list.append(4) print ('changed', list, id(list)) >>> num=[1,2,3] >>> id(num) 139635530080008 >>> newlist(num) received [1, 2, 3] 139635530080008 changed [1, 2, 3, 4] 139635530080008 >>> num [1, 2, 3, 4]

Hence we can say that a mutable object, passed to a function by reference gets modified by any changes inside the called function.

60.

What is the special significance of * and ** in Python?

Answer»

In Python, single and double asterisk (* and **) SYMBOLS are defined as multiplication and exponentiation operators respectively. However, when prefixed to a variable, these symbols carry a special meaning. Let us see what that means.

A Python function is defined to receive a certain number of arguments. Naturally the same number of arguments must be provided while calling the function, otherwise Python interpreter raises TypeError as shown below.

>>> def add(x,y): RETURN x+y >>> add(11,22) 33 >>> add(1,2,3) Traceback (most recent call last):  File "<pyshell#8>", line 1, in <module>    add(1,2,3) TypeError: add() takes 2 positional arguments but 3 were given

This is where the use of formal argument prefixed with single * comes. In a function definition, an argument prefixed with * is able to receive variable number of VALUES from the calling environment and stores the values in a tuple object.

>>> def add(*numbers): s=0 for n in numbers: s=s+n return s >>> add(11,22) 33 >>> add(1,2,3) 6 >>> add(10) 10 >>> add() 0

Python allows a function to be called by using the formal arguments as keywords. In such a CASE, the order of argument definition need not be followed.

>>> def add(x,y): return x+y >>> add(y=10,x=20) 30

However, if you want to define a function which should be able to receive variable number of keyword arguments, the argument is prefixed by ** and it stores the keyword: values passed as a dictionary.

>>> def add(**numbers): s=0 for k,v in numbers.items(): s=s+v return s >>> add(a=1,b=2,c=3,d=4) 10

In fact, a function can have positional arguments, variable number of arguments, keyword arguments with defaults and variable number of keyword arguments. In order to use both variable arguments and variable keyword arguments, **variable should appear last in the argument list.

>>> def add(x,y,*arg, **kwarg): print (x,y) print (arg) print (kwarg) >>> add(1,2,3,4,5,6,a=10,b=20,c=30) 1 2 (3, 4, 5, 6) {'a': 10, 'b': 20, 'c': 30}
61.

What is a callable object in Python?

Answer»

An object that can invoke a certain action or process is callable. The most obvious example is a function. Any function, built-in or user defined or a method in built-in or user defined class is an object of Function class

&GT;>> def myfunction(): print ('HELLO') >>> type(myfunction) <class 'function'> >>> type(print) <class 'builtin_function_or_method'>

Python’s standard library has a nuilt-in callable() function that returns true if object is callable. Obviously a function returns true.

>>> callable(myfunction) True >>> callable(print) True

A callable object with parentheses (having arguments or not) invokes associated functionality. In fact any object that has access to __call__() MAGIC method is callable. Above function is normally called by entering myfunction(). However it can also be called by using __call_() method inherited from function class

>>> myfunction.__call__() Hello >>> myfunction() Hello

Numbers, string, collection objects etc are not callable because they do not inherit __call__() method

>>> a=10 >>> b='hello' >>> c=[1,2,3] >>> callable(10) False >>> callable(b) False >>> callable(c) False

In Python, class is also an object and it is callable.

>>> class MyClass: def __init__(SELF): print ('called') >>> MyClass() called <__main__.MyClass object at 0x7f37d8066080> >>> MyClass.__call__() called

<__main__.MyClass object at 0x7f37dbd35710>

However, object of MyClass is not callable.

>>> m=MyClass() called >>> m() Traceback (most recent call last):  File "<pyshell#23>", LINE 1, in <module>    m() TypeError: 'MyClass' object is not callable

In order that object of user defined be callable, it must override __call__() method

>>> class MyClass: def __init__(self): print ('called') def __call__(self): print ('this makes object callable') >>> m=MyClass() called >>> m() this makes object callable
62.

What is assert in Python? Is it a function, class or keyword? Where and how is it used?

Answer»

‘assert’ is one of 33 KEYWORDS in Python LANGUAGE. Essentially it CHECKS whether given boolean expression is true. If expression is true, Python interpreter goes on to execute subsequent code. However, if the expression is false, AssertionError is raised bringing execution to halt.

To DEMONSTRATE usage of assert statement, let us consider following function:

>>> def testassert(x,y): assert y!=0 print ('division=',x/y)

Here, the division will be performed only if boolean condition is true, but AssertionError raised if it is false

>>> testassert(10,2) division= 5.0 >>> testassert(10,0) Traceback (most recent call last):  File "<pyshell#8>", line 1, in <module>    testassert(10,0)  File "<pyshell#6>", line 2, in testassert    assert y!=0 AssertionError

AssertionError is raised with a string in assert statement as custom error message

>>> def testassert(x,y): assert y!=0, 'Division by Zero not allowed' print ('division=',x/y) >>> testassert(10,0) Traceback (most recent call last):  File "<pyshell#11>", line 1, in <module>    testassert(10,0)  File "<pyshell#10>", line 2, in testassert    assert y!=0, 'Division by Zero not allowed' AssertionError: Division by Zero not allowed

Instead of letting the execution terminate abruptly, the AssertionError is handled with try – except construct as follows:

>>> def testassert(x,y): try: assert y!=0 print ('division', x/y) except AssertionError: print ('Division by Zero not allowed') >>> testassert(10,0) Division by Zero not allowed
63.

Recursion is also a kind of iteration. Discuss their relative merits and demerits.

Answer»

Function is said to be recursive if it calls itself. Recursion is used when a process is defined in terms of itself. A body of recursive function which is executed repeatedly is a type of iteration.

The difference between a recursive function and the one having a loop such as ‘while’ or ‘for’ loop is that of memory and processor overhead. Let us TRY to understand with the help of iterative and recursive functions that calculate factorial value of a number.

Iterative factorial function

>>> def factorial(x): f=1 for i in range(1, x+1): f=f*i return f >>> factorial(5) 120

Recursive factorial function

>>> def factorial(n):        if n == 1:        print (n)        return 1        else:        return n * factorial(n-1) >>> factorial(5) 120

For a function that involves a loop, it is called only once. Therefore only one copy is created for all the variables used in it. Also, as function call uses stack to return function value to CALLING environment, only limited stack operations are performed.

In case of recursion, repeated calls to same function with different arguments are initiated. Hence that many copies of local variables are created in the memory and that many stack operations are needed.  

Obviously, a recursive call by function to itself causes an infinite loop. Hence recursive call is always conditional. Recursive approach provides a very concise solution to complex problem having many iterations.

In case of iterative version of factorial function, it is a straightforward case of cumulative multiplication of numbers in a range. However, in case of recursive function it is implementation of mathematical definition of factorial as below:

n! = n X (n-1)!

Here we have used factorial itself to DEFINE factorial. Such problems are ideally suited to be expressed recursively.

We can perform this CALCULATION using a loop as per following CODE:

Now we shall try to write recursive equivalent of above loop. Look at mathematical definition of factorial once again.

n!=nX(n-1)!

Substitute n with 5. The expression becomes

5!=5X4! = 5X4X3! = 5X4X3X2! = 5X4X3X2X1! = 5X4X3X2X1 = 120

Let us see graphically the step by step process of computing factorial value of 5.

The diagram illustrates how successively performing factorial calculation of number by decrementing the number till it reaches 1. This involves more cost of computing.

Hence it can be seen that recursion is more expensive in terms of resource utilization. It is also difficult to write a recursive function as compared to a straightforward repetitive or iterative solution.

Having said that, recursion is sometimes preferred especially in cases where iterative solution becomes very big, complex and involves too many conditions to keep track of. There are some typical applications where we have to employ recursive solution. Traversal of binary tree structure, sorting algorithms such as heap sort, finding traversal path etc are typical applications of recursion.

On a lighter note, to be able to write a recursive solution gives a lot of satisfaction to the programmer!

64.

How is a complex number in Python represented in terms of polar coordinates?

Answer»

Complex number z=x+yj is a Cartesian (also CALLED rectangular) representation. It is internally REPRESENTED in polar coordinates with its modulus r (as returned by built-in abs() function) and the phase angle θ (pronounced as theta) which is counter clockwise angle in radians, between the x axis and LINE joining x with the origin. Following diagram illustrates polar representation of complex number:

FUNCTIONS in cmath module allow conversion of Cartesian representation to polar representation and vice versa.

polar() : This function returns polar representation of a Cartesian notation of complex number. The return value is a tuple consisting of modulus and phase.

>>> IMPORT cmath >>> a=2+4j >>> cmath.polar(a) (4.47213595499958, 1.1071487177940904)

Note that the modulus is returned by abs() function

>>> abs(a) 4.47213595499958

phase(): This function returns counter clockwise angle between x axis and segment joining a with origin. The angle is represented in radians and is between π and -π

>>> cmath.phase(a) 1.1071487177940904

rect(): This function returns Cartesian representation of complex number represented in polar form i.e. in modulus and phase

>>> cmath.rect(4.47213595499958, 1.1071487177940904) (2.0000000000000004+4j)
65.

Does Python support operator overloading? How?

Answer»

Certain non-alphanumeric characters are defined to perform a SPECIFIED operation. Such characters are called operators. For example the characters +, -, * and / are defined to perform arithmetic operations on two numeric operands. Similarly <, > == and != perform comparison of two numeric operands by default.

Some of built-in classes of Python allow certain operators to be used with non-numeric objects too. For instance the + operator acts as concatenation operator with two strings. We SAY that + operator is OVERLOADED. In general overloading refers to attaching additional operation to the operator.

>>> #default addition operation of + >>> 2+5 7 >>> #+operator overloaded as concatenation operator >>> 'Hello'+'Python' 'HelloPython' >>> [1,2,3]+[4,5,6] [1, 2, 3, 4, 5, 6] >>> #default multiplication operation of * >>> 2*5 10 >>> #overloaded * operator as repetition operation with sequences >>> [1,2,3]*3 [1, 2, 3, 1, 2, 3, 1, 2, 3] >>> 'Hello'*3 'HelloHelloHello'

For user defined classes, operator overloading is achieved by overriding relevant magic methods (methods with two underscores before and after name) from the object class. For example, if a class contains overridden definition of __add__() method, it is implemented when + operator is used with objects of that class. Following table SHOWS magic methods and the arithmetic operator they implement:

OperatorMethod
+object.__add__(self, other)
-object.__sub__(self, other)
*object.__mul__(self, other)
//object.__floordiv__(self, other)
/object.__div__(self, other)
%object.__mod__(self, other)
**object.__pow__(self, other[, modulo])

Following script contains a class that overrides __add__() method. It causes + operator overloading.

>>> class MyClass: def __init__(self, x,y): self.x=x self.y=y def __add__(self, OBJ): x=self.x+obj.x y=self.y+obj.y print ('x:{} y:{}'.format(x,y))

We now have two objects of above class and use + operator with them. The __add__() method will implement overloaded behaviour of + operator as below:

>>> m1=MyClass(5,7) >>> m2=MyClass(3,8) >>> m1+m2 x:8 y:15

Similarly comparison operators can also be overloaded by overriding following magic methods:

<
object.__lt__(self, other)
<=
object.__le__(self, other)
==
object.__eq__(self, other)
!=
object.__ne__(self, other)
>=
object.__ge__(self, other)
66.

What is the difference between set and frozenset?

Answer»

Set is a collection data type in Python. It is a collection of unique and immutable objects, not necessarily of same types. A set object can be created by a literal representation as well as using built-in set() function.

Comma separated collection of items enclosed in curly brackets is a set object.

>>> s={10,'Hello', 2.52} >>> type(s) <class 'set'>

You can also USE built-in set() function which in fact is constructor of set class. It constructs a set object from an iterable argument such as list, tuple or STRING.

>>> s1=set([10,20,30]) >>> s1 {10, 20, 30} >>> s2=set((100,200,300)) >>> s2 {200, 100, 300} >>> S3=set('Hello') >>> s3 {'o', 'e', 'l', 'H'}

Items in the iterable argument may not appear in the same order in set collection. Also, set is a collection of unique objects. THEREFORE, even if the iterable contains repeated occurrence of an item, it will appear only one in set. You can see just one presence of ‘l’ even if it appears twice in the string.

Items in the set collection must be mutable. It means a list or a dict object cannot be one of the items in a set although tuple is allowed.

>>> s={1,(2,3)} >>> s {1, (2, 3)} >>> s={1,[2,3]} Traceback (most recent call last):  File "<pyshell#16>", line 1, in <module>    s={1,[2,3]} TypeError: unhashable type: 'list'

Even though set can contain only immutable objects such as number, string or tuple, set itself is mutable. It is possible to perform add/remove/clear operations on set.

>>> s1=set([10,20,30]) >>> s1.add(40) >>> s1 {40, 10, 20, 30} >>> s1.remove(10) >>> s1 {40, 20, 30}

Frozenset object on the other hand is immutable. All other characteristics of frozenset are similar to normal set. Because it is immutable add/remove operations are not possible.

>>> f=frozenset([10,20,30]) >>> f frozenset({10, 20, 30}) >>> f.add(40) Traceback (most recent call last):  File "<pyshell#24>", line 1, in <module>    f.add(40) AttributeError: 'frozenset' object has no attribute 'add'

Python’s set data type is implementation of set as in set theory of Mathemetics. Operations such as union, intersection, difference etc. can be done on set as well as frozenset.

67.

Explain concept of mutable vs immutable object in Python.

Answer»

Each Python object, whether REPRESENTING a built-in class or a user defined class, is stored in computer’s memory at a certain randomly chosen location which is returned by the built-in id() function.

>>> x=10 >>> id(x) 94368638893568

Remember that VARIABLE in Python is just a label bound to the object. Here x represents the integer object 10 which is stored at a certain location.

Further, if we assign x to another variable y, it is also referring to the same integer object.

>>> y=x >>> id(y) 94368638893568

Let us now change value of x with expression x=x+1. As a result a new integer 11 is stored in memory and that is now referred to by x. The object 10 continues to be in memory which is bound to y.

>>> x=x+1 >>> id(x) 94368638893600 >>> id(y) 94368638893568

Most striking feature is that the object 10 is not changed to 11. A new object 11 is created. Any Python object whose value cannot be changed after its creation is IMMUTABLE. All number type objects (int, float, complex, bool, complex) are immutable.

STRING object is also immutable. If we try to modify the string by replacing one of its characters, Python interpreter doesn’t allow this, raising TypeError thus implying that a string object is immutable.

>>> string='Python' >>> string[2]='T' Traceback (most recent call last):  File "<pyshell#9>", line 1, in <module>    string[2]='T' TypeError: 'str' object does not support item assignment

Same thing is true with tuple which is also immutable.

>>> TUP=(10,20,30) >>> tup[1]=100 Traceback (most recent call last):  File "<pyshell#11>", line 1, in <module>    tup[1]=100 TypeError: 'tuple' object does not support item assignment

However, list and dictionary objects are mutable. These objects can be updated in place.

>>> num=[10,20,30] >>> num[1]=100 >>> num [10, 100, 30] >>> dct={'x':10, 'y':20, 'z':30} >>> dct['y']=100 >>> dct {'x': 10, 'y': 100, 'z': 30}
68.

Where and how is Python keyword yield used?

Answer»

Python’s yield keyword is typically USED along with a generator. A generator is a special type of function that returns an iterator object returning stream of values. APPARENTLY it looks like a normal function , but it doesn’t return a single value. Just as we use return keyword in a function, in a generator yield statement is used.

A normal function, when called, executes all statements in it and returns back to calling environment. The generator is called just like a normal function. However, it pauses on encountering yield keyword, returning the yielded value to calling environment. Because execution of generator is paused, its local VARIABLES and their STATES are saved internally. It resumes when __next__() method of iterator is called. The function finally terminates when __next__() or next() raises StopIteration.

In following example function mygenerator() acts as a generator. It yields one character at a time from the string sequence successively on every call of next()

>>> def mygenerator(string): for ch in string: print ("yielding", ch) yield ch

The generator is called which builds the iterator object

>>> it=mygenerator("Hello World")

Each character from string is pushed in iterator every time next() function is called.

>>> while True: try: print ("yielded character", next(it)) EXCEPT StopIteration: print ("end of iterator") break

Output:

yielding H yielded character H yielding e yielded character e yielding l yielded character l yielding l yielded character l yielding o yielded character o yielding   yielded character   yielding W yielded character W yielding o yielded character o yielding r yielded character r yielding l yielded character l yielding d yielded character d end of iterator

In case of generator, elements are generated dynamically. Since next item is generated only after first is consumed, it is more memory efficient than iterator.

69.

Explain with example the technique of list comprehension in Python.

Answer»

List comprehension technique is similar to mathematical set builder notation. A classical for/while loop is generally used to traverse and process each ITEM in an iterable. List comprehension is considerably more efficient than processing a list by ‘for’ loop. It is a very concise mechanism of creating new list by performing a certain process on each item of existing list.

Suppose we want to compute square of each number in a list and store squares in another list object. We can do it by a ‘for’ loop as shown below:

squares=[] for num in range(6):        squares.append(pow(num,2)) print (squares)

The squares list object is DISPLAYED as follows:

[0, 1, 4, 9, 16, 25]

Same result is achieved by list comprehension technique MUCH more efficiently. List comprehension statement USES following syntax:

newlist = [x for x in sequence]

We use above format to construct list of squares using list comprehension.

>>> numbers=[6,12,8,5,10] >>> squares = [x**2 for x in numbers] >>> squares [36, 144, 64, 25, 100]

We can even generate a dictionary or tuple object as a result of list comprehension.

>>> [{x:x*10} for x in range(1,6)] [{1: 10}, {2: 20}, {3: 30}, {4: 40}, {5: 50}]

Nested loops can also be used in a list comprehension expression. To obtain list of all combinations of items from two lists (Cartesian product):

>>> [{x:y} for x in range(1,3) for y in (11,22,33)] [{1: 11}, {1: 22}, {1: 33}, {2: 11}, {2: 22}, {2: 33}]

The resulting list stores all combinations of one number from each list

We can even have if condition in list comprehension. Following statement will result in list of all non-vowel alphabets in a string.

>>> odd=[x for x in range(1,11) if x%2==1] >>> odd [1, 3, 5, 7, 9]
70.

Python's standard library has a built-in function next(). For what purpose is it used?

Answer»

The built-in next() function calls __next__() method of iterator object to retrieve next item in it. So the next question WOULD be – what is an iterator?

An object representing stream of data is an iterator. One element from the stream is retrieved at a time. It follows iterator protocol which requires it to support  __iter__() and __next__() methods. Python’s built-in method iter() implements __iter__() method and next() implements __next__() method. It receives an iterable and returns iterator object.

Python uses iterators  implicitly while working with collection data types such as list, tuple or string. That's why these data types are called iterables. We normally USE ‘for’ loop to iterate through an iterable as follows:

>>> numbers=[10,20,30,40,50] >>> for num in numbers: print (num) 10 20 30 40 50

We can use iter() function to obtain iterator object underlying any iterable.

>>> iter('Hello World') <str_iterator object at 0x7f8c11ae2eb8> >>> iter([1,2,3,4]) <list_iterator object at 0x7f8c11ae2208> >>> iter((1,2,3,4)) <tuple_iterator object at 0x7f8c11ae2cc0> >>> iter({1:11,2:22,3:33}) <dict_keyiterator object at 0x7f8c157b9ea8>

Iterator object has __next__() method. Every time it is called, it returns next element in iterator stream. When the stream gets exhausted, StopIteration error is raised.

>>> numbers=[10,20,30,40,50] >>> it=iter(numbers) >>> it.__next__() 10 >>> next(it) 20 >>> it.__next__() 30 >>> it.__next__() 40 >>> next(it) 50 >>> it.__next__() Traceback (most RECENT call last):  File "<pyshell#16>", line 1, in <MODULE>    it.__next__() StopIteration

Remember that next() function implements __next__() method. Hence next(it) is equivalent to it.__next__()

When we use a for/while loop with any iterable, it actually implements iterator protocol as follows:

>>> while True: try: num=it.__next__() print (num) except StopIteration: break 10 20 30 40 50

A disk file, memory buffer or network stream also acts as an iterator object. Following example shows that each line from a file can be printed using next() function.

>>> file=open('csvexample.py') >>> while True: tr KeyboardInterrupt >>> while True: try: line=file.__next__() print (line) except StopIteration: break
71.

Explain hash() function in the built-in library.

Answer»

The hash() functions hash value of given object. The object MUST be immutable. Hash value is an integer specific to the object. These hash values are used during DICTIONARY LOOKUP

Two objects may hash to the same hash value. This is CALLED Hash collision. This means that if two objects have the same hash code, they do not necessarily have the same value.

>>> #hash of a number >>> hash(100) 100 >>> hash(100.00) 100 >>> hash(1E2) 100 >>> hash(100+0j) 100

You can see that hash value of an object of same numeric value is the same.

>>> #hash of string >>> hash("hello") -1081986780589020980 >>> #hash of tuple >>> hash((1,2,3)) 2528502973977326415 >>> #hash of list >>> hash([1,2,3]) Traceback (most RECENT call last):   File "<pyshell#23>", line 1, in <module>     hash([1,2,3]) TypeError: unhashable type: 'list'

Last case of hash value of list results in TypeError as list is not immutable /not hashable.

The hash() function internally calls __hash__() magic method. To obtain a hash value of object of user defined class, the class itself should provide overridden implementation of __hash__()

class User:     def __init__(self, age, name):         self.age = age         self.name = name     def __hash__(self):         return hash((self.age, self.name)) x = User(23, 'Shubham') print("The hash is: %d" % hash(x))

Output:

The hash is: -1916965497958416005
72.

When should finally clause be used in exception handling process?

Answer»

Python’s exception handling technique INVOLVES four keywords: try, except, else and finally. The try block is essentially a script you want to check if it contains any runtime ERROR or exception. If it does, the exception is raised and except block is executed. 

The else block is optional and will get run only if there is no exception in try block. Similarly, finally is an optional clause meant to perform clean up operations to be undertaken under all circumstances, whether try block encounters an exception or not. A finally clause is ALWAYS executed before leaving the try statement, whether an exception has occurred or not. 

>>> try: raise TypeError finally: print ("end of exception handling") end of exception handling Traceback (most recent call last):   File "<pyshell#28>", line 2, in <MODULE>     raise TypeError TypeError

When an unhandled exception occurs in the try block (or it has occurred in an except or else clause), it is re-raised after the finally block executes. The finally clause is also executed when either try, except or else block is left via a break, continue or return statement.

>>> def division(): try: a=int(input("first number")) b=int(input("second number")) c=a/b except ZeroDivisionError: print ('divide by 0 not allowed') else: print ('division:',c) finally: print ('end of exception handling')

Let US call above function number of times to see how finally block works:

>>> division() first number10 second number2 division: 5.0 end of exception handling >>> division() first number10 second number0 divide by 0 not allowed end of exception handling >>> division() first number10 second numbertwo end of exception handling Traceback (most recent call last):   File "<pyshell#47>", line 1, in <module>     division()   File "<pyshell#42>", line 4, in division     b=int(input("second number")) ValueError: invalid literal for int() with base 10: 'two'

Note how exception is re-raised after the finally clause is executed. The finally block is typically used for releasing external resources such as file whether or not it was successfully used in previous clauses.

73.

How is multiplication and division of complex numbers done? Verify the process with complex number objects of Python.

Answer»

A complex number is made up of a real and an imaginary component. In MATHEMATICS, an imaginary number is defined as the SQUARE root of (-1) denoted by j. The imaginary component is multiplied by j. Python’s built-in complex object is represented by the following literal expression.

>>> x=3+2j

Python’s built-in complex() function also returns complex object USING to float objects, first as a real part and second as an imaginary component.

>>> x=complex(3,2) >>> x (3+2j)

Addition and SUBTRACTION of complex numbers is the STRAIGHTFORWARD addition of the respective real and imaginary components.

The process of multiplying these two complex numbers is very similar to multiplying two binomials. Multiply each term in the first number by each term in the second number.

a=6+4j b=3+2j c=a*b c=(6+4j)*(3+2j) c=(18+12j+12j+8*-1) c=10+24j

Verify this result with Python interpreter

>>> a=6+4j >>> b=3+2j >>> a*b (10+24j)

To obtain division of two complex numbers, multiply both sides by the conjugate of the denominator, which is a number with the same real part and the opposite imaginary part.

a=6+4j b=3+2j c=a/b c=(6+4j)*(3-2j)/(3+2j)(3-2j) c=(18-12j+12j-8*-1)/(9-6j+6j-4*-1) c=26/13 c=2+0j

Verify this with Python interpreter

>>> a=6+4j >>> b=3+2j >>> a/b (2+0j)
74.

Explain built-in any() and all() functions.

Answer»

These two built-in functions EXECUTE logical or / and operators successively on each of the items in an iterable such as list, tuple or string.

The all() FUNCTION returns True only if all items in the iterable return true. For empty iterable, all() function returns true. Another feature of all() function is that the evaluation stops at the first instance of returning false, abandoning the REMAINING items in the sequence.

On the other hand any() function returns True even if one item in the sequence returns true. Consequently, any() function returns false only if all items evaluate to false (or it is empty).

To check the behaviour of these functions, let us DEFINE a lambda function and subject each item in the list to it. The lambda function itself returns true if the number argument is even.

>>> iseven=lambda X:x%2==0 >>> iseven(100) True >>> iseven(101) False >>> lst=[50,32,45,90,60] >>> all(iseven(x) for x in lst) False >>> any(iseven(x) for x in lst) True
75.

What is the use of built-in zip() function. Explain with suitable examples.

Answer»

Python’s object hierarchy has a built-in zip object. It is an iterator of tuples containing items on the same index stored in ONE or more iterables. The zip object is returned by zip() FUNCTION. With no arguments, it returns an empty iterator.

>>> a=zip() >>> type(a) <class 'zip'> >>> list(a) []

When one iterable is provided, zip() function returns an iterator of one element tuples.

>>> a=zip('abcd') >>> list(a) [('a',), ('b',), ('c',), ('d',)]

When the function has MULTIPLE iterables as arguments, each tuple in zip object contains items at similar index. Following SNIPPET gives two lists to zip() function and the result is an iterator of two element tuples.

>>> l1=['pen', 'computer', 'book'] >>> l2=[100,20000,500] >>> a=zip(l1,l2) >>> list(a) [('pen', 100), ('computer', 20000), ('book', 500)]

Iterable arguments may be of different length. In that case the zip iterator stops when the shortest input iterable is exhausted.

>>> string="HelloWorld" >>> lst=list(range(6)) >>> tup=(10,20,30,40,50) >>> a=zip(string, lst, tup) >>> list(a) [('H', 0, 10), ('e', 1, 20), ('l', 2, 30), ('l', 3, 40), ('o', 4, 50)]

The zip object can be unpacked in separate iterables by prefixing * to zipped object.

>>> a=['x', 'y', 'z'] >>> b=[10,20,30] >>> c=zip(a,b) >>> result=list(c) >>> result [('x', 10), ('y', 20), ('z', 30)] >>> x,y=zip(*result) >>> x ('x', 'y', 'z') >>> y (10, 20, 30)
76.

What are some different command line options available for using Python interpreter?

Answer»

Python interpreter is invoked from command terminal of operating system (Windows or Linux). Two most common WAYS are starting interactive console and running script

For interactive console $ python Python 3.6.6 |Anaconda custom (64-bit)| (DEFAULT, Oct  9 2018, 12:34:16)  [GCC 7.3.0] on linux Type "help", "copyright", "credits" or "license" for more information. >>>

For scripting mode

$ python hello.py

In addition to these common ways, Python command line can have different options. They are listed below:

-c cmd : program passed in as string. Interpreter executes the Python code in string which can be one or more statements separated by newlines.

$ python -c "print ('hello')" Hello

-m mod : run library module as a script. the named module and execute its contents as the __main__ module. The following command creates a new virtual environment

$ python -m venv myenv

-i: inspect interactively after running script or code with -c option. The interactive prompt APPEARS after the output. This can be useful to inspect global variables or a stack trace when a script raises an exception.

$ python -i -c "print ('hello')" hello $ python -i temp.py Hello world >>>

Other options are:

  • B: don't write .pyc files on import; also PYTHONDONTWRITEBYTECODE=x
  • d: debug output from PARSER; also PYTHONDEBUG=x
  • E: ignore PYTHON* environment variables (such as PYTHONPATH)
  • h: print this help message and exit (also --help)
  • q: don't print version and copyright messages on interactive startup
  • v: verbose (trace import statements) can be supplied multiple times to increase verbosity
  • V: print the Python version number and exit (also --version)
  • x: skip first line of source, allowing use of non-Unix forms of #!cmd shebang
  • file: program read from script file
77.

Python offers two keywords – while and for – to constitute loops. Can you bring out the difference and similarity in their usage?

Answer»

SYNTAX of while loop:

while expr==True:     stmt1     stmt2     ..     .. stmtN

The block of statements with uniform indent is repeatedly executed till the expression in while statement remains true. Subsequent lines in the code will be executed after loop stops when the expression ceases to be true.

Syntax of for loop:

for x in interable:     stmt1     stmt2     .. stmtN

The looping block is executed for each item in the iterable object like list, or tuple or string. These objects have an in-built iterator that fetches ONE item at a time. As the items in iterable get exhausted, the loop stops and subsequent statements are executed.

The construction of while loop requires some mechanism by which the logical expression becomes FALSE at some point or the other. If not, it will constitute an infinite loop. The USUAL practice is to keep count of the number of repetitions.

Both types of loops allow use of the else block at the end. The else block will be executed when stipulated iterations are over.

for x in "hello":     print (x) else:     print ("loop over") print ("end")

Both types of loops can be nested. When a loop is placed inside the other, these loops are called nested loops.

#nested while loop x=0 while x<3:     x=x+1     y=0     while y<3:         y=y+1         print (x,y) #nested for loop for x in range(1,4):     for y in range(1,4):         print (x,y)

Both versions of nested loops print the following output:

1 1 1 2 1 3 2 1 2 2 2 3 3 1 3 2 3 3 1 1 1 2 1 3 2 1 2 2 2 3 3 1 3 2 3 3
78.

In what circumstances is the ‘elif’ keyword recommended to be used?

Answer»

Python uses if keyword to implement decision control. Python’s syntax for executing block conditionally is as below:

if expr==True:     stmt1     stmt2     .. stmtN

Any Boolean expression evaluating to True or False appears after if keyword. USE : symbol and press Enter after the expression to start a block with increased indent. One or more statements written with the same level of indent will be executed if the Boolean expression evaluates to True. To end the block, press backspace to de-dent. Subsequent statements after the block will be executed if the expression is false or after the block if expression is true.

Along with if statement, else clause can also be optionally USED to define alternate block of statements to be executed if the Boolean expression (in if statement) is not true. Following code skeleton shows how else block is used.

if expr==True:     stmt1     stmt2     .. else:     stmt3     stmt4     .. stmtN

There may be situations where cascaded or nested conditional statements are required to be used as shown in the following skeleton:

if expr1==True:     stmt1     stmt2     .. else:     if expr2==True:         stmt3         stmt4         ...     else:         if expr3==True:             stmt5             stmt6             .. stmtN

As you can see, the indentation level of each subsequent block goes on increasing because if block starts after empty else. To avoid these clumsy indentations, we can combine empty else and subsequent if by elif keyword. General syntax of if – elif – else usage is as below:

if expr1==True:     #Block To Be Executed If expr1 is True elif expr2==True:      #Block To Be Executed If expr1 is false and expr2 is true elif expr3==True:      #Block To Be Executed If expr2 is false and expr3 is true elif expr4==True:      #Block To Be Executed If expr3 is false and expr4 is true else:      #Block To Be Executed If all preceding expressions false

In this code, one if block, FOLLOWED by one or more elif blocks and one else block at the end will appear. Boolean expression in front of elif is evaluated if previous expression fails. Last else block is run only when all previous expressions turn out to be not true. Importantly all blocks have the same level of indentation.

Here is a simple example to demonstrate the use of elif keyword. The following program computes discount at different rates if the amount is in different slabs.

price=int(input("enter price")) qty=int(input("enter quantity")) amt=price*qty if amt>10000:     PRINT ("10% discount applicable")     discount=amt*10/100     amt=amt-discount elif amt>5000:     print ("5% discount applicable")     discount=amt*5/100     amt=amt-discount elif amt>2000:     print ("2% discount applicable")     discount=amt*2/100     amt=amt-discount elif amt>1000:     print ("1% discount applicable")     discount=amt/100     amt=amt-discount else:     print ("no discount applicable") print ("amount payable:",amt)

Output:

enter price1000 enter quantity11 10% discount applicable amount payable: 9900.0 ======================== enter price1000 enter quantity6 5% discount applicable amount payable: 5700.0 ======================== enter price1000 enter quantity4 2% discount applicable amount payable: 3920.0 ========================  enter price500 enter quantity3 1% discount applicable amount payable: 1485.0 ========================  enter price250 enter quantity3 no discount applicable amount payable: 750
79.

How does destructor work in a Python class?

Answer»

In object oriented programming, DESTRUCTOR is a method of class which will be automatically called when its object is no longer in use. Python PROVIDES a special (magic) method named __del__() to define destructor. Although destructor is not really required in Python class because Python USES the principle of automatic garbage collection, you can still provide __del__() method for EXPLICIT deletion of object memory.

>>> class Myclass: def __init__(self): print ('object initialized') def __del__(self): print ('object destroyed') >>> x=Myclass() object initialized >>> del x object destroyed

In the above example , ‘del’ keyword in Python is invoked to delete a certain object. As __del__() method is exclusively provided, it is being called.

Python uses reference counting method for garbage collection. As per this method, an object is eligible for garbage collection if its reference count becomes zero.

In the following example, first obj becomes object of Myclass so its reference count is 1. But when we assign another object to obj, reference count of Myclass() becomes 0 and hence it is collected, thereby calling __del__() method inside the class.

>>> class Myclass: def __init__(self): print ('object initialized') def __del__(self): print ('object destroyed') >>> obj=Myclass() object initialized >>> obj=10 object destroyed
80.

How do you define a constructor in Python class? Can a class have overloaded constructor in Python?

Answer»

Python is a COMPLETELY object ORIENTED language. It has ‘class’ keyword using which a new user defined class can be created. 

>>> class Myclass: Pass

In object oriented programming, an object of a class is initialized by automatically invoking a certain method when it is declared. In C++ and Java, a method of the same name as the class acts as a constructor. However, Python uses a special method named as __init__() as a constructor.

>>> class Myclass: def __init__(SELF): PRINT ("new object initialized") >>> x=Myclass() new object initialized

Here x is declared as a new object of Myclass and __init__() method is called automatically. 

The constructor method always has one mandatory argument carrying reference of the object that is CALLING. It is conventionally denoted by ‘self’ identifier although you are free to use any other. Instance attributes are generally initialized within the __init__() function.

>>> class Myclass: def __init__(self): self.name='Mack' self.age=25 >>> x=Myclass() >>> x.name 'Mack' >>> x.age 25

In addition to self, __init__() method can have other arguments using which instance attributes can be initialized by user specified data.

>>> class Myclass: def __init__(self, name, age): self.name=name self.age=age >>> x=Myclass('Chris',20) >>> x.name 'Chris' >>> x.age 20

However, Python class is not allowed to have overloaded constructor as in C++ or Java. Instead you can use default values to arguments of __init__() method.

>>> class Myclass: def __init__(self, name='Mack', age=20): self.name=name self.age=age >>> x=Myclass() >>> x.name 'Mack' >>> x.age 20 >>> y=Myclass('Nancy', 18) >>> y.name 'Nancy' >>> y.age 18
81.

Single and double underscore symbols have special significance in Python. When and where are they used?

Answer»

Python identifiers with leading and/or single and/or double underscore characters i.e. _ or __ are used for giving them a peculiar meaning.

Unlike C++ or Java, Python doesn’t restrict access to instance attributes of a class. In C++ and Java, access is controlled by public, PRIVATE or protected keywords. These keywords or their equivalents are not defined in Python. All resources of class are public by default.

However, Python does allow you to indicate that a variable is private by prefixing the name of the variable by double underscore __. In the following example, Student class has ‘name’ as private variable.

>>> class Student: def __init__(self): self.__name='Amar'

Here __name acts as a private attribute of object of Student class. If we try to access its value from outside the class, Python raises AttributeError.

>>> x=Student() >>> x.__name TRACEBACK (most recent call last):   File "<pyshell#37>", line 1, in <module>     x.__name AttributeError: 'Student' object has no attribute '__name'

However, Python doesn’t restrict access altogether. Python only internally renames such attribute in the form _classname__attribute. Here __name is RENAMED as _Student__name. The mechanism is called name mangling

>>> x._Student__name 'Amar'

An attribute with single underscore prefix emulates the behaviour of a protected data member INDICATING that it is available only to subclass. 

Python uses attributes and methods with double underscore CHARACTER before and after the name to form magic methods. Examples are __init__() and __dir__() etc.

82.

Explain the behaviour of view objects in Python.

Answer»

In general, view gives a representation of a particular object in a certain direction or perspective. In Python’s dictionary, an object has items(), KEYS() and values() METHODS that return view OBJECTS. These are objects of dict_items, dict_keys and dict_values classes respectively. All of them are view types.

>>> dct=dct={'1':'one', '2':'two', '3':'three'} >>> v1=dct.items() >>> class(v1) SyntaxError: invalid syntax >>> type(v1) <class 'dict_items'> >>> v2=dct.keys() >>> type(v2) <class 'dict_keys'> >>> v3=dct.values() >>> type(v3) <class 'dict_values'>

These view objects can be cast to list or tuples

>>> list(v1) [('1', 'one'), ('2', 'two'), ('3', 'three')] >>> tuple(v1) (('1', 'one'), ('2', 'two'), ('3', 'three')) >>> list(v2) ['1', '2', '3'] >>> list(v3) ['one', 'two', 'three']

It is possible to run a for loop over these views as they can return an ITERATOR object.

>>> #using for loop over items() view >>> for i in v1: print (i) ('1', 'one') ('2', 'two') ('3', 'three')

As you can see each item in the view is a tuple of key–value pair. We can unpack each tuple in separate k-v objects

>>> for k,v in v1: print ('key:{} value:{}'.format(k,v)) key:1 value:one key:2 value:two key:3 value:three

A view object also supports membership operators.

>>> '2' in v2 True >>> 'ten' in v3 False

The most important feature of view objects is that they are dynamically refreshed as any add/delete/modify operation is performed on an underlying dictionary object. As a result, we need not constructs views again.

>>> #update dictionary >>> dct.update({'4':'four','2':'twenty'}) >>> dct {'1': 'one', '2': 'twenty', '3': 'three', '4': 'four'} >>> #AUTOMATICALLY refreshed views >>> v1 dict_items([('1', 'one'), ('2', 'twenty'), ('3', 'three'), ('4', 'four')]) >>> v2 dict_keys(['1', '2', '3', '4']) >>> v3 dict_values(['one', 'twenty', 'three', 'four'])
83.

What is the use of slice object in Python?

Answer»

Python’s built-in function SLICE() returns Slice object. It can be used to extract slice from a sequence object list, tuple or string) or any other object that implements sequence protocol SUPPORTING _getitem__() and __len__() methods.

The slice() function is in fact the constructor in slice class and accepts start, stop and step parameters similar to range object. The start and step parameters are optional. Their default value is 0 and 1 respectively.

Following STATEMENT declares a slice object.

>>> obj=slice(1,6,2)

We use this object to extract a slice from a list of numbers as follows:

>>> numbers=[7,56,45,21,11,90,76,55,77,10] >>> numbers[object] Traceback (most recent call last):  File "<stdin>", line 1, in <module> TypeError: list indices must be integers or slices, not type >>> numbers[obj] [56, 21, 90]

You can see that elements starting from index 1 upto 5 with step 2 are sliced away from the original list.

Slice can also receive negative index.

>>> obj=slice(-1,3,-1) >>> numbers[obj] [10, 77, 55, 76, 90, 11]
84.

How are Python’s built-in functions ord() and chr() related?

Answer»

These two functions have exactly the opposite behaviour to each other. The chr() function returns a string representing a CHARACTER to an integer argument which is a Unicode code point.

>>> chr(65) 'A' >>> chr(51) '3' >>> chr(546) 'Ȣ' >>> chr(8364) '€'

The chr() function returns corresponding CHARACTERS for integers between 0 to 1114111 (0x110000). For a number outside this range, PYTHON raises ValuError.

>>> chr(-10)    chr(-10) ValueError: chr() arg not in range(0x110000)

On the other hand ord() function returns an integer corresponding to Unicode code point of a character.

>>> ord('A') 65 >>> ord('a') 97 >>> ord('\u0222') 546 >>> ord('€') 8364

Note that ord() function accepts a string of only one character otherwise it raises TypeError as shown below:

>>> ord('aaa')    ord('aaa') TypeError: ord() EXPECTED a character, but string of length 3 found

These two functions are the inverse of each other as can be seen from the following

>>> ord(chr(65)) 65 >>> chr(ord('A')) 'A'
85.

Explain the builtins getattr() and setattr() functions.

Answer»

The built-in functions MAKE it possible to retrieve the value of specified attribute of any object (getattr) and add an attribute to given object.

Following is definition of a test class without any ATTRIBUTES.

>>> class test: Pass

Every Python class is a subclass of ‘object’ class. Hence it inherits attributes of the class which can be listed by dir() function:

>>> dir(test) ['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__']

The setattr() function can be used to add a class LEVEL attribute to our test class.

>>> setattr(test,'name','Ravi')

To retrieve its value use getattr() function.

>>> getattr(test,'name') 'Ravi'

You can of course access the value by dot (.) notation

>>> test.name 'Ravi'

Similarly, these functions add/retrieve attributes to/from an object of any class. Let us declare an INSTANCE of test class and add age attribute to it.

>>> x=test() >>> setattr(x,'age',21)

Obviously, ‘age’ becomes the instance attribute and not class attribute. To retrieve, use getattr() or ‘.’ operator.

>>> getattr(x,'age') 21 >>> x.age 21

Use dir() function to verify that ‘age’ attribute is available.

>>> dir(x) ['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'age', 'name']

Incidentally, Python also provides ‘hasattr()’ function to check if an object possesses the given attribute.

>>> hasattr(x,'age') TRUE >>> hasattr(test, 'age') False
86.

What is the difference between TypeError and ValueError?

Answer»

An exception is a type of run time error reported by a Python interpreter when it encounters a situation that is not easy to handle while executing a certain statement. Python’s library has a number of built-in exceptions defined in it. Both TypeError and valueError are built-in exceptions and it may at times be confusing to understand the reasoning behind their usage.

For EXAMPLE, int(‘hello’) raises ValueError when one would expect TypeError. A closer look at the documentation of these exceptions would clear the difference.

>>> int('hello') Traceback (most recent call last):  File "<pyshell#26>", line 1, in <module>    int('hello') ValueError: invalid literal for int() with base 10: 'hello'

Passing arguments of the WRONG type (e.g. passing a list when an int is expected) should result in a TypeError which is also RAISED when an operation or function is applied to an object of inappropriate type.

Passing arguments with the wrong value (e.g. a number outside expected BOUNDARIES) should result in a ValueError. It is also raised when an operation or function receives an argument that has the RIGHT type but an inappropriate value.

As far as the above case is concerned the int() function can accept a string argument so passing ‘hello’ is not valid hence it is not a case of TypeError. Alternate signature of int() function with two arguments receives string and base of number system

int(string,base) >>> int('11', base=2) 3

Second argument if ignored defaults to 10

>>> int('11') 11

If you give a string which is of inappropriate ‘value’ such as ‘hello’ which can’t be converted to a decimal integer, ValueError is raised.

87.

List and tuple data types appear to be similar in nature. Explain typical use cases of list and tuple.

Answer»

Yes, the list and tuple objects are very similar in nature. Both are sequence data types being an ordered collection of ITEMS, not necessarily of the same type. However, there are a couple of subtle differences between the two.

First and foremost, a tuple is an IMMUTABLE object while a list is mutable. Which simply means that once created, a tuple cannot be modified in place (insert/delete/update operations cannot be performed) and the list can be modified dynamically. Hence, if a collection is unlikely to be modified during the course of the program, a tuple should be used. For example price of items.

>>> quantity=[34,56,45,90,60] >>> prices=(35.50, 299,50, 1.55, 25.00,99)

Although both objects can contain items of different types, conventionally a list is used generally to hold similar objects – similar to an array in C/C++ or Java. Python tuple is preferred to set up a collection of heterogenous objects – similar to a STRUCT in C. CONSEQUENTLY, you would USE a list to store marks obtained by students, and a tuple to store coordinates of a point in cartesian system.

>>> marks=[342,516,245,290,460] >>> x=(10,20)

Internally too, Python uses tuple a lot for a number of purposes. For example, if a function returns more than one value, it is treated as a tuple.

>>> def testfunction(): x=10 y=20 return x,y >>> t=testfunction() >>> t (10, 20) >>> type(t) <class 'tuple'>

Similarly if a function is capable of receiving multiple arguments in the form of *args, it is parsed as a tuple.

>>> def testfunction(*args): print (args) print (type(args)) >>> testfunction(1,2,3) (1, 2, 3) <class 'tuple'>

Python uses tuple to store many built-in data structures. For example time data is stored as a tuple.

>>> import time >>> time.localtime() time.struct_time(tm_year=2019, tm_mon=5, tm_mday=28, tm_hour=9, tm_min=20, tm_sec=0, tm_wday=1, tm_yday=148, tm_isdst=0)

Because of its immutable nature, a tuple can be used as a key in a dictionary, whereas a list can’t be used as key. Also, if you want to iterate over a large collection of items, tuple proves to be faster than a list.

88.

Python index operator can accept negative numbers. Explain how?

Answer»

Python’s sequence data types (list, tuple or string) are indexed collection of items not necessarily of the same TYPE. INDEX starts from 0 – as in C/C++ or Java array (although these languages insist that an array is a collection of similar data types). Again like C/C++, any element in sequence can be accessed by its index.

>>> num=[10,20,25,15,40,60,23,90,50,80] >>> num[3] 15

However, C/C++/Java don’t allow negative numbers as index. Java throws NegativeArraySizeException. C/C++ produces undefined behaviour. However, Python accepts negative index for sequences and starts counting index from end. Consequently, index -1 returns the last element in the sequence. 

>>> num=[10,20,25,15,40,60,23,90,50,80] >>> num[-1] 80 >>> num[-10] 10

However, using negative index in slice notation exhibits some peculiar behaviour. The slice operator accepts two numbers as operands. First is index of the beginning element of the slice and second is the index of the element after slice. Num[2:5] returns elements with index 2, 3 and 4

>>> num=[10,20,25,15,40,60,23,90,50,80] >>> num[2:5] [25, 15, 40]

Note that default value of first operand is 0 and second is length+1

>>> num=[10,20,25,15,40,60,23,90,50,80] >>> num[:3] [10, 20, 25] >>> num[0:3] [10, 20, 25] >>> num[8:] [50, 80] >>> num[8:10] [50, 80]

Hence using a negative number as the first or second operand gives the results ACCORDINGLY. For example using -3 as the first operand and ignoring the second returns the last THREE items. However, using  -1 as second operand results in leaving out the last element

>>> num[-3:] [90, 50, 80] >>> num[-3:-1] [90, 50]

Using -1 as first operand without second operand is equivalent to indexing with -1

>>> num[-1:] [80] >>> num[-1] 80
89.

What are Python-specific environment variables?

Answer»

After you install Python software on your computer, it is desired that you add the installation directory in your operating system’s PATH environment variable. Usually the installer program does this action by default. Otherwise you have to perform this from the control panel.

In addition to updating PATH, certain other Python-specific environment variables should be set up. These environment variables are as follows:

  • PYTHONPATH

This environment variable plays a role similar to PATH. It tells the Python interpreter where to locate the module files imported into a program. It includes the Python source library directory and  other directories containing Python source code. PYTHONPATH is usually preset by the Python installer.

  • PYTHONSTARTUP

It denotes the path of an initialization file containing Python source code. This file is executed every time the Python interpreter starts. It is named as .pythonrc.py and it contains commands that load utilities or modify PYTHONPATH.

  • PYTHONCASEOK

In Windows, it enables Python to find the first case-insensitive match in an import statement. Set this variable to any value to activate it.

  • PYTHONHOME

It is an alternative module SEARCH path. It is usually embedded in the PYTHONSTARTUP or PYTHONPATH directories to make SWITCHING module libraries easy. It may refer to zipfiles containing pure Python modules (in either source or compiled form)

  • PYTHONDEBUG

If this is set to a non-empty string it turns on parser debugging output.

  • PYTHONINSPECT

If this is set to a non-empty string it is equivalent to specifying the -i OPTION. When a script is passed as the first argument or the -c option is used, enter interactive mode after executing the script or the command.

  • PYTHONVERBOSE

If this is set to a non-empty string it is equivalent to specifying the -v option causes PRINTING a message each time a module is initialized, showing the place from which it is loaded.

  • PYTHONEXECUTABLE

If this environment variable is set, sys.argv[0] will be set to its value instead of the value got through the C RUNTIME. Only works on Mac OS X.

90.

What according to you is the difference between shallow copy and deep copy?

Answer»

Unlike C/C++, a variable in PYTHON is just a label to object created in memory. Hence when it is assigned to another variable, it doesn’t copy the object, but rather it acts as another reference to the same object. The built-in id() function brings out this behaviour.

>>> num1=[10,20,30]  >>> num2=num1  >>> num1  [10, 20, 30]  >>> num2  [10, 20, 30]  >>> id(num1), id(num2)  (140102619204168, 140102619204168)

The id() function returns the location of object in memory. Since id() for both the list objects is the same, both refer to the same object in memory.

The num2 is called as a shallow copy of num1. Since both refer to the same object, any change in either will reflect in the other object.

>>> num1=[10,20,30] >>> num2=num1 >>> num2[2]=5 >>> num1 [10, 20, 5] >>> num2 [10, 20, 5]

In the above example, the item at index no. 2 of num2 is changed. We see this change appearing in both.

A deep copy creates an entirely new object and COPIES of nested objects too are recursively added to it.

The copy module of the Python standard library provides two methods:

  • copy.copy() – creates a shallow copy
  • copy.deepcopy() – creates a deep copy

A shallow copy creates a new object and stores the reference of the original elements but doesn't create a copy of nested objects. It just copies the reference of nested objects. As a result, the copy process is not recursive.

>>> import copy >>> num1=[[1,2,3],['a','b','c']] >>> num2=copy.copy(num1) >>> id(num1),id(num2) (140102579677384, 140102579676872) >>> id(num1[0]), id(num2[0]) (140102579676936, 140102579676936) >>> num1[0][1],id(num1[0][1]) (2, 94504757566016) >>> num2[0][1],id(num2[0][1]) (2, 94504757566016)

Here num1 is a nested list and num2 is its shallow copy. Ids of num1 and num2 are different, but num2 doesn’t hold physical copies of internal elements of num1, but holds just the ids.

As a result, if we try to modify an ELEMENT in nested element, its effect will be seen in both lists.

>>> num2[0][2]=100 >>> num1 [[1, 2, 100], ['a', 'b', 'c']] >>> num2 [[1, 2, 100], ['a', 'b', 'c']]

However, if we append a new element to one list, it will not reflect in the other list.

>>> num2.append('Hello') >>> num2 [[1, 2, 100], ['a', 'b', 'c'], 'Hello'] >>> num1 [[1, 2, 100], ['a', 'b', 'c']]

A deep copy on the other hand, creates a new object and recursively adds the copies of nested objects too, present in the original elements.

In following example, num2 is a deep copy of num1. Now if we change any element of the inner list, this will not show in the other list.

>>> import copy >>> num1=[[1,2,3],['a','b','c']] >>> num2=copy.deepcopy(num1) >>> id(num1),id(num2) (140102641055368, 140102579678536) >>> num2[0][2]=100 >>> num2 [[1, 2, 100], ['a', 'b', 'c']] >>> num1 #not changed [[1, 2, 3], ['a', 'b', 'c']]
91.

What is the difference between break and continue?

Answer»

In order to construct loop, PYTHON language provides two keywords, while and for. The loop formed with while is a conditional loop. The body of loop KEEPS on getting executed till the boolean expression in while statement is true.

>>> while expr==True: statement1 statement2 ... ...

The ‘for’ loop on the other hand traverses a collection of objects. Body block inside the ‘for’ loop executes for each object in the collection

>>> for x in collection: expression1 expression2 ... ...

So both types of loops have a pre-decided endpoint. SOMETIMES though, an early termination of looping is sought by abandoning the remaining iterations of loop. In some other cases, it is required to start next round of iteration before ACTUALLY completing entire body block of the loop.

Python provides two keywords for these two scenarios. For first, break keyword is used. When ENCOUNTERED inside looping body, program control comes out of the loop, abandoning remaining iterations of current loop. This situation is diagrammatically represented by following flowchart:Use of break in while loop:

>>> while expr==True: statement1 if statement2==True: break ... ...

Use of break in ‘for’ loop:

>>> for x in collection: expression1 if expression2==True: break ... ...

On the other hand continue behaves almost opposite to break. Instead of bringing the program flow out of the loop, it is taken to the beginning of loop, although remaining steps in the current iteration are skipped. The flowchart representation of continue is as follows:Use of continue in while loop:

>>> while expr==True: statement1 if statement2==True: continue ... ...

Use of continue in ‘for’ loop:

>>> for x in collection: expression1 if expression2==True: continue ... ...

Following code is a simple example of use of both break and continue keywords. It has an infinite loop within which user input is accepted for password. If incorrect password in entered, user is asked to input it again by continue keyword. Correct password terminates infinite loop by break

#example.py

while True:    pw=input('enter password:')    if pw!='abcd':        continue    if pw=='abcd':        break

Output:

enter password:11 enter password:asd enter password:abcd
92.

How do you unpack a Python tuple object?

Answer»

A tuple object is a collection data type. It contains one or more items of same or different data types. Tuple is DECLARED by LITERAL REPRESENTATION using parentheses to hold comma separated objects.

>>> tup=(10,'hello',3.25, 2+3j) Empty tuple is declared by empty parentheses >>> tup=()

However, single element tuple should have additional comma in the parentheses otherwise it becomes a single object.

>>> tup=(10,) >>> tup=(10) >>> type(tup) <class 'int'>

Using parentheses around comma separatedobjects is optional.

>>> tup=10,20,30 >>> type(tup) <class 'tuple'>

Assigning multiple objects to tuple is called packing. Unpacking on the other hand is extracting objects in tuple into individual objects. In above EXAMPLE, the tuple object contains three int objects. To unpack, three variables on LEFT hand side of assignment operator are used

>>> x,y,z=tup >>> x 10 >>> y 20 >>> z 30

Number of variables on left hand side must be equal to length of tuple object.

>>> x,y=tup Traceback (most recent call last):  File "<pyshell#12>", line 1, in <module>    x,y=tup ValueError: too many values to unpack (expected 2)

However, we can use variable prefixed with * to create list object out of remaining values

>>> x,*y=tup >>> x 10 >>> y [20, 30]
93.

How can arguments be passed from command line to a Python program?

Answer»

Python is an interpreted language. Its source code is not CONVERTED to a self-executable as in C/C++ (Although certain OS specific third party utilities MAKE it possible). Hence the standard way to execute a Python script is issuing the following command from command terminal.

$ python hello.py

Here $ (in case of Linux) or c:\> in case of Windows is called command prompt and text in front of it is called command line contains name of Python executable followed by Python script.

From within script, user input is accepted with the help of built-in input() function

x=int(input('enter a number')) y=int(input('enter another number')) print ('sum=',x+y)

Note that input() function always reads user input as string. If required it is converted to other data types.

#example.py x=int(input('enter a number')) y=int(input('enter another number')) print ('sum=',x+y)

Above script is run from command line as:

$ python example.py enter a number10 enter another number20 sum= 30

However, it is also possible to provide data to the script from outside by entering values separated by whitespace character after its name. All the segments of command line (called command line arguments) separated by space are stored inside the script in the form of a special list object as defined in built-in sys module. This special list object is called sys.argv

Following script collects command line arguments and displays the list.

import sys print ('arguments received', sys.argv)

Run above script in command terminal as:

$ python example.py 10 20 arguments received ['example.py', '10', '20']

Note that first element in sys.argv sys.argv[0] is the name of Python script. Arguments usable inside the script are sysargv[1:] Like the input() function, arguments are always strings. They may have to be converted appropriately.

import sys x=int(sys.argv[1]) y=int(sys.argv[2]) print ('sum=',x+y) $python example.py 10 20 sum= 30

The command line can pass variable number of arguments to the script. Usually length sys.argv is checked to verify if desired number of arguments are passed. For above script, only 2 arguments need to be passed. Hence the script should REPORT ERROR if number of arguments is not as PER requirement.

import sys x=int(sys.argv[1]) y=int(sys.argv[2]) print ('sum=',x+y) $python example.py 10 20 sum= 30import sys if len(sys.argv)!=3:  print ("invalid number of arguments") else:  x=int(sys.argv[1])  y=int(sys.argv[2])  print ('sum=',x+y)

Output:

$ python example.py 10 20 30 invalid number of arguments $ python example.py 10 invalid number of arguments $ python example.py 10 20 sum= 30
94.

How do we define class level attributes and methods in a Python class?

Answer»

A CLASS has two types of attributes – instance attributes and class atributes. Each object of class may have different values for instance attributes. However class attribute is same for each object of the class.

Instance attributes are normally defined and initialized through __init__() method which is executed when object is declared.

>>> class MyClass: DEF __init__(self, x,y): self.x=x self.y=y >>> obj1=MyClass(10,20) >>> obj1.x, obj1.y (10, 20) >>> obj2=MyClass(100,200) >>> obj2.x, obj2.y (100, 200)

In above example MyClass defines two instance attributes x and y. These attributes are initialized through __init__() method when object is declared. Each object has different values of x and y attributes.

Class attribute is defined outside __init__() method (in fact outside any method of the class). It can be assigned value in definition or from within __init__() or any method. However, it’s value is not different for each object.

>>> class MyClass: z=10 def __init__(self, x,y): self.x=x self.y=y

Here z is not an instance attribute but class attribute defined outside __init__() method. Value of z is same for each object. It is accessed using name of the class. However, accessing with object also shows same value

>>> obj1=MyClass(10,20) >>> obj2=MyClass(100,200) >>> MyClass.z 10 >>> obj1.x, obj1.y, obj1.z (10, 20, 10) >>> obj2.x, obj2.y, obj2.z (100, 200, 10

Class has instance METHODS as well as class methods. An instance method such as __init__() always has one of the arguments as self which refers to calling object. Instance attributes of calling object are accessed through it. A class method is defined with @classmethod decorator and received class name as argument.

>>> class MyClass: z=10 def __init__(self, x,y): self.x=x self.y=y @classmethod def classvar(cls): print (cls.z)

The class variable processed inside class method using class name as well as object as reference. It cannot access or process instance attributes.

>>> obj1=MyClass(10,20) >>> MyClass.classvar() 10 >>> obj1.classvar() 10

Class can have a static method which is defined with @staticmethod decorator. It takes NEITHER a self nor a cls argument. Class attributes are accessed by providing name of class explicitly.

>>> class MyClass: z=10 def __init__(self, x,y): self.x=x self.y=y @classmethod def classvar(cls): print (cls.z) @staticmethod def statval(): print (MyClass.z) >>> obj1=MyClass(10,20) >>> MyClass.statval() 10 >>> obj1.statval() 10
95.

Python's math module has ceil() and floor() division functions. What is the difference?

Answer»

Python has a built-in round() function that rounds given number to NEAREST integer.

&GT;>> round(3.33) 3 >>> round(3.65) 4

Sometimes, you may need a largest integer smaller than given number, or a smallest integer, that is larger than given number. This is where floor() and ceil() functions in math module are used.

As the name suggests, ceil() stands for ceiling. It returns nearest integer greater than given number or numeric expression.

>>> import math >>> math.ceil(3.33) 4 >>> math.ceil(3.65) 4

The floor() function on the other HAND returns integer that is smaller than given number or numeric expression indicating that given number is larger than a certain fraction from the RESULTING integer.

>>> import math >>> math.floor(3.33) 3 >>> math.floor(3.65) 3

The floor() function shows a peculiar behaviour when the number or numeric expression is negative. In such case, the result is floored away from 0.

>>> math.floor(-3.65) -4
96.

Python library has built-in pow() function. It also has pow() function in math module. What's the difference?

Answer»

Built-in pow() function has two variations  - with two arguments and three arguments.

The pow() function with two arguments returns first argument raised to second argument. That means pow(x,y) results in x to the power y

&GT;>> pow(10,2) 100

The two-argument form pow(x, y) is equivalent to using the power operator: x**y.

The pow() function can take three arguments. In that case, pow(x,y,z) returns pow(x,y)%z. It returns modulo division of x to the power y and z.

>>> pow(10,2) 100 >>> pow(10,2,3) 1

This is equivalent TO10**2%3 which is 100%3 = 1

The pow() function from math module only has a two argument version. Both the arguments are treated as float. Hence the result is always float even if arguments are INT.

>>> math.pow(10,2) 100.0 >>> pow(10,2) 100

Because of the FLOATING point representation in the memory, result of math.pow() may be inaccurate for integer arguments. Hence for INTEGERS it is advised to use built-in pow() function or ** operator.

97.

What is difference between string and raw string?

Answer»

Literal representation of Python string can be DONE using single, double or triple quotes. Following are the different ways in which a string object can be declared:

>>> s1='HELLO Python' >>> s2="Hello Python" >>> s3='''Hello Python''' >>> s4="""Hello Python"""

However, if a string contains any of the escape sequence characters, they are embedded inside the quotation marks. The back-slash character followed by certain alphabets carry a special meaning. Some of the escape characters are:

  • \n : newline – causing following characters printed in next line
  • \t : tab – resulting in a fixed space between characters
  • \\ : prints backslash character itself
  • \b : effects pressing backspace key – removes previous character
>>> s1='Hello\nPython' >>> sprint (s1) Traceback (most RECENT call last):  File "<pyshell#6>", line 1, in <module>    sprint (s1) NameError: NAME 'sprint' is not defined >>> print (s1) Hello Python >>> s2='Hello\tPython' >>> print (s2) Hello Python

In case of a raw string on the other hand the escape characters don’t get translated while printing. Such raw string is prepared by prefixing ‘r’ or ‘R’ to the leading quotation mark(single, double or triple)

>>> r1=r'Hello\nPython' >>> print (r1) Hello\nPython >>> r2=R"Hello\tPython" >>> print (r2) Hello\tPython

Python doesn’t allow any undefined escape sequence to be embedded in the quotation marks of a normal string.

>>> s1='Hello\xPython' SyntaxError: (unicode error) 'unicodeescape' codec can't decode bytes in position 5-6: truncated \xXX escape

However, in a raw string, any character followed by backslash may be used because ANYWAY it is not going to be interpreted for its meaning!

>>> s1=r'Hello\xPython' >>> print (s1) Hello\xPython

Raw strings are used in building regular expressions (called regex). Python’s re module provides functions to process the regular expressions. The re module assigns its own escape characters. Some of them are listed below:

\dMatches any decimal digit
\DMatches any non-digit character
\sMatches any whitespace character
\SMatches any non-whitespace character
\wMatches any alphanumeric character
\WMatches any non-alphanumeric character.
\bboundary between word and non-word and /B is opposite of /b

Some examples of above escape characters are given below:

>>> import re >>> string='ab12cd34ef' >>> #find all digits >>> x = re.findall("\d", string) >>> print (x) ['1', '2', '3', '4'] >>> #find all non-digit characters >>> x = re.findall("\D", string) >>> print (x) ['a', 'b', 'c', 'd', 'e', 'f']
98.

Is Python weakly typed or strongly typed?

Answer»

Depending upon how strict its typing rules are, a programming language is CLASSIFIED as strongly typed or weakly (sometimes CALLED loosely) typed.

Strongly typed language checks the type of a variable before performing an operation on it. If an operation involves incompatible types, compiler/interpreter rejects the operation. In such case, types must be made COMPATIBLE by using appropriate casting techniques.

A weakly typed language does not enforce type safety strictly. If an operation involves two incompatible types, one of them is coerced into other type by performing implicit casts. PHP and JavaScript are the examples of weakly typed languages.

Python is a strongly typed language because it raises TypeError if two incompatible types are involved in an operation

>>> x=10 >>> y='Python' >>> z=x+y Traceback (most recent call last):  File "<pyshell#2>", line 1, in <module>    z=x+y TypeError: unsupported operand type(s) for +: 'int' and 'str'

In above example, attempt to perform addition operation on one integer OBJECT and another string object raises TypeError as neither is implicitly converted to other. If however, the integer object “I” converted to string then concatenation is possible.

>>> x=10 >>> y='Python' >>> z=str(x)+y >>> z '10Python'

In a weakly typed language such as JavaScript, the casting is performed implicitly.

<script> VAR x = 10; var y = "Python"; var z=x+y; document.write(z); </script> Output: 10Python
99.

Why is Python called dynamically typed language?

Answer»

Computer languages are generally classified as statically or DYNAMICALLY typed. Examples of statically typed languages are C/C++, Java, C# etc. Python, along with JavaScript, PHP etc are dynamically typed languages.  

First we have to understand the concept of variable. In C/C++/Java, variable is a user defined convenience name given to a memory location. MOREOVER, compilers of these languages require prior DECLARATION of name of variable and type of data that can be stored in it before actually assigning any value. TYPICAL variable declaration and assignment statement in C/C++/Java would be as follows:

int x; x=10;

Here, the variable x is permanently bound to int type. If data of any other type is assigned, compiler error will be reported. Type of variable decides what can be assigned to it and what can’t be assigned.

In Python on the other hand, the object is stored in a randomly chosen memory location, whereas variable is just an identifier or a label referring to it. When we assign 10 to x in Python, integer object 10 is stored in memory and is labelled as x. Python’s built-in type() FUNCTION  tells us that it stores object of int type.

>>> x=10 >>> type(x) <class 'int'>

However, we can use same variable x as a label to another object, which may not be of same type. 

>>> x='Hello' >>> type(x) <class 'str'>

Python interpreter won’t object if same variable is used to store reference to object of another type. Unlike statically typed languages, type of variable changes dynamically as per the object whose reference has been assigned to it. Python is a dynamically typed language because type of variable depends upon type of object it is referring to.

100.

How do I check whether a file exists using Python?

Answer»

There are a COUPLE of ways to check if a file EXISTS or not. First method is use OPEN() function on a file and see if it raises any exception. If the file is indeed existing, the open() function will be executed correctly. However, if it doesn’t, an exception will be raised.

>>> try: file=open(filename) print ('the file exists') file.close() except: print ('file is not existing')

You can also use exists() method of path object defined in os module.

>>> from os IMPORT path >>> path.exists(filename)

Here filename should be a string containing name along with file’s path. Result will be True if file exists otherwise false.

Python library consists of pathlib module. You can also check if file exists or not by using exists() method in this module as FOLLOWS:

>>> import pathlib >>> file=pathlib.Path(filename) >>> file.exists()