Python Interview Questions and Answers

1. What is Python?
Python is an interpreted, interactive, object-oriented programming language. It incorporates modules, exceptions, dynamic typing, very high level dynamic data types, and classes. Python combines remarkable power with very clear syntax. It has interfaces to many system calls and libraries, as well as to various window systems, and is extensible in C or C++. It is also usable as an extension language for applications that need a programmable interface. Finally, Python is portable: it runs on many Unix variants, on the Mac, and on PCs under MS-DOS, Windows, Windows NT, and OS/2.

2. Why can't I use an assignment in an expression? 
Many people used to C or Perl complain that they want to use this C idiom:

while (line = readline(f)) {
...do something with line...
}

where in Python you're forced to write this:

while True:
line = f.readline()
if not line:
break
...do something with line...

The reason for not allowing assignment in Python expressions is a common, hard-to-find bug in those other languages, caused by this construct:

if (x = 0) {
...error handling...
}
else {
...code that only works for nonzero x...
}

The error is a simple typo: x = 0, which assigns 0 to the variable x, was written while the comparison x == 0 is certainly what was intended.
Many alternatives have been proposed. Most are hacks that save some typing but use arbitrary or cryptic syntax or keywords, and fail the simple criterion for language change proposals: it should intuitively suggest the proper meaning to a human reader who has not yet been introduced to the construct.
An interesting phenomenon is that most experienced Python programmers recognize the "while True" idiom and don't seem to be missing the assignment in expression construct much; it's only newcomers who express a strong desire to add this to the language.
There's an alternative way of spelling this that seems attractive but is generally less robust than the "while True" solution:
line = f.readline()
while line:
...do something with line...
line = f.readline()
The problem with this is that if you change your mind about exactly how you get the next line (e.g. you want to change it into sys.stdin.readline()) you have to remember to change two places in your program -- the second occurrence is hidden at the bottom of the loop.
The best approach is to use iterators, making it possible to loop through objects using the for statement. For example, in the current version of Python file objects support the iterator protocol, so you can now write simply:
for line in f:
... do something with line...


3. Is there a tool to help find bugs or perform static analysis? 
Yes.
PyChecker is a static analysis tool that finds bugs in Python source code and warns about code complexity and style.

Pylint is another tool that checks if a module satisfies a coding standard, and also makes it possible to write plug-ins to add a custom feature.


4. How do you set a global variable in a function? 
Did you do something like this?
x = 1 # make a global
def f():
print x # try to print the global
...
for j in range(100):
if q>3:
x=4

Any variable assigned in a function is local to that function. unless it is specifically declared global. Since a value is bound to x as the last statement of the function body, the compiler assumes that x is local. Consequently the print x attempts to print an uninitialized local variable and will trigger a NameError.
The solution is to insert an explicit global declaration at the start of the function:
def f():
global x
print x # try to print the global
...
for j in range(100):
if q>3:
x=4

In this case, all references to x are interpreted as references to the x from the module namespace.


5. What are the rules for local and global variables in Python? 
In Python, variables that are only referenced inside a function are implicitly global. If a variable is assigned a new value anywhere within the function's body, it's assumed to be a local. If a variable is ever assigned a new value inside the function, the variable is implicitly local, and you need to explicitly declare it as 'global'.
Though a bit surprising at first, a moment's consideration explains this. On one hand, requiring global for assigned variables provides a bar against unintended side-effects. On the other hand, if global was required for all global references, you'd be using global all the time. You'd have to declare as global every reference to a builtin function or to a component of an imported module. This clutter would defeat the usefulness of the global declaration for identifying side-effects.


6. How do I share global variables across modules? 
The canonical way to share information across modules within a single program is to create a special module (often called config or cfg). Just import the config module in all modules of your application; the module then becomes available as a global name. Because there is only one instance of each module, any changes made to the module object get reflected everywhere. For example:

config.py:
x = 0 # Default value of the 'x' configuration setting
mod.py:
import config
config.x = 1

main.py:
import config
import mod
print config.x

Note that using a module is also the basis for implementing the Singleton design pattern, for the same reason.

7. How can I pass optional or keyword parameters from one function to another?
Collect the arguments using the * and ** specifier in the function's parameter list; this gives you the positional arguments as a tuple and the keyword arguments as a dictionary. You can then pass these arguments when calling another function by using * and **:

def f(x, *tup, **kwargs):
...
kwargs['width']='14.3c'
...
g(x, *tup, **kwargs)

In the unlikely case that you care about Python versions older than 2.0, use 'apply':

def f(x, *tup, **kwargs):
...
kwargs['width']='14.3c'
...
apply(g, (x,)+tup, kwargs)


8. How do you make a higher order function in Python? You have two choices: you can use nested scopes or you can use callable objects. For example, suppose you wanted to define linear(a,b) which returns a function f(x) that computes the value a*x+b. Using nested scopes:

def linear(a,b):
def result(x):
return a*x + b
return result

Or using a callable object:

class linear:
def __init__(self, a, b):
self.a, self.b = a,b
def __call__(self, x):
return self.a * x + self.b

In both cases:

taxes = linear(0.3,2)

gives a callable object where taxes(10e6) == 0.3 * 10e6 + 2.

The callable object approach has the disadvantage that it is a bit slower and results in slightly longer code. However, note that a collection of callables can share their signature via inheritance:

class exponential(linear):
# __init__ inherited
def __call__(self, x):
return self.a * (x ** self.b)
Object can encapsulate state for several methods:

class counter:
value = 0
def set(self, x): self.value = x
def up(self): self.value=self.value+1
def down(self): self.value=self.value-1

count = counter()
inc, dec, reset = count.up, count.down, count.set


Here inc(), dec() and reset() act like functions which share the same counting variable.


9. How do I copy an object in Python? 
In general, try copy.copy() or copy.deepcopy() for the general case. Not all objects can be copied, but most can.

Some objects can be copied more easily. Dictionaries have a copy() method:

newdict = olddict.copy()

Sequences can be copied by slicing:

new_l = l[:]


10. How can I find the methods or attributes of an object? 
For an instance x of a user-defined class, dir(x) returns an alphabetized list of the names containing the instance attributes and methods and attributes defined by its class.


11. How do I convert a string to a number? 
For integers, use the built-in int() type constructor, e.g. int('144') == 144. Similarly, float() converts to floating-point, e.g. float('144') == 144.0.

By default, these interpret the number as decimal, so that int('0144') == 144 and int('0x144') raises ValueError. int(string, base) takes the base to convert from as a second optional argument, so int('0x144', 16) == 324. If the base is specified as 0, the number is interpreted using Python's rules: a leading '0' indicates octal, and '0x' indicates a hex number.

Do not use the built-in function eval() if all you need is to convert strings to numbers. eval() will be significantly slower and it presents a security risk: someone could pass you a Python expression that might have unwanted side effects. For example, someone could pass __import__('os').system("rm -rf $HOME") which would erase your home directory.

eval() also has the effect of interpreting numbers as Python expressions, so that e.g. eval('09') gives a syntax error because Python regards numbers starting with '0' as octal (base 8).

12. How can my code discover the name of an object?
Generally speaking, it can't, because objects don't really have names. Essentially, assignment always binds a name to a value; The same is true of def and class statements, but in that case the value is a callable. Consider the following code:

class A:
pass

B = A

a = B()
b = a
print b
<__main__.A instance at 016D07CC>
print a
<__main__.A instance at 016D07CC>

Arguably the class has a name: even though it is bound to two names and invoked through the name B the created instance is still reported as an instance of class A. However, it is impossible to say whether the instance's name is a or b, since both names are bound to the same value.

Generally speaking it should not be necessary for your code to "know the names" of particular values. Unless you are deliberately writing introspective programs, this is usually an indication that a change of approach might be beneficial.

In comp.lang.python, Fredrik Lundh once gave an excellent analogy in answer to this question:

The same way as you get the name of that cat you found on your porch: the cat (object) itself cannot tell you its name, and it doesn't really care -- so the only way to find out what it's called is to ask all your neighbours (namespaces) if it's their cat (object)...

....and don't be surprised if you'll find that it's known by many names, or no name at all!


13. Is there an equivalent of C's "?:" ternary operator? 
No.


14. How do I convert a number to a string? 
To convert, e.g., the number 144 to the string '144', use the built-in function str(). If you want a hexadecimal or octal representation, use the built-in functions hex() or oct(). For fancy formatting, use the % operator on strings, e.g. "%04d" % 144 yields '0144' and "%.3f" % (1/3.0) yields '0.333'. See the library reference manual for details.


15. How do I modify a string in place? 
You can't, because strings are immutable. If you need an object with this ability, try converting the string to a list or use the array module:

>>> s = "Hello, world"
>>> a = list(s)
>>>print a
['H', 'e', 'l', 'l', 'o', ',', ' ', 'w', 'o', 'r', 'l', 'd']
>>> a[7:] = list("there!")
>>>''.join(a)
'Hello, there!'


>>> import array
>>> a = array.array('c', s)
>>> print a
array('c', 'Hello, world')
>>> a[0] = 'y' ; print a
array('c', 'yello world')
>>> a.tostring()
'yello, world'


16. How do I use strings to call functions/methods?
 There are various techniques.

* The best is to use a dictionary that maps strings to functions. The primary advantage of this technique is that the strings do not need to match the names of the functions. This is also the primary technique used to emulate a case construct:

def a():
pass

def b():
pass

dispatch = {'go': a, 'stop': b} # Note lack of parens for funcs

dispatch[get_input()]() # Note trailing parens to call function
*
Use the built-in function getattr():

import foo
getattr(foo, 'bar')()

Note that getattr() works on any object, including classes, class instances, modules, and so on.

This is used in several places in the standard library, like this:

class Foo:
def do_foo(self):
...
def do_bar(self):
...

f = getattr(foo_instance, 'do_' + opname)
f()

*
Use locals() or eval() to resolve the function name:

def myFunc():
print "hello"

fname = "myFunc"

f = locals()[fname]
f()

f = eval(fname)
f()

Note: Using eval() is slow and dangerous. If you don't have absolute control over the contents of the string, someone could pass a string that resulted in an arbitrary function being executed.


17. Is there an equivalent to Perl's chomp() for removing trailing newlines from strings? 
Starting with Python 2.2, you can use S.rstrip("\r\n") to remove all occurences of any line terminator from the end of the string S without removing other trailing whitespace. If the string S represents more than one line, with several empty lines at the end, the line terminators for all the blank lines will be removed:

>>> lines = ("line 1 \r\n"
... "\r\n"
... "\r\n")
>>> lines.rstrip("\n\r")
"line 1 "

Since this is typically only desired when reading text one line at a time, using S.rstrip() this way works well.

For older versions of Python, There are two partial substitutes:

* If you want to remove all trailing whitespace, use the rstrip() method of string objects. This removes all trailing whitespace, not just a single newline.
* Otherwise, if there is only one line in the string S, use S.splitlines()[0].

18. Is there a scanf() or sscanf() equivalent?
Not as such.

For simple input parsing, the easiest approach is usually to split the line into whitespace-delimited words using the split() method of string objects and then convert decimal strings to numeric values using int() or float(). split() supports an optional "sep" parameter which is useful if the line uses something other than whitespace as a separator.

For more complicated input parsing, regular expressions more powerful than C's sscanf() and better suited for the task.


19. Is there a scanf() or sscanf() equivalent? 

Not as such.

For simple input parsing, the easiest approach is usually to split the line into whitespace-delimited words using the split() method of string objects and then convert decimal strings to numeric values using int() or float(). split() supports an optional "sep" parameter which is useful if the line uses something other than whitespace as a separator.

For more complicated input parsing, regular expressions more powerful than C's sscanf() and better suited for the task. 1.3.9 What does 'UnicodeError: ASCII [decoding,encoding] error: ordinal not in range(128)' mean?

This error indicates that your Python installation can handle only 7-bit ASCII strings. There are a couple ways to fix or work around the problem.

If your programs must handle data in arbitrary character set encodings, the environment the application runs in will generally identify the encoding of the data it is handing you. You need to convert the input to Unicode data using that encoding. For example, a program that handles email or web input will typically find character set encoding information in Content-Type headers. This can then be used to properly convert input data to Unicode. Assuming the string referred to by value is encoded as UTF-8:

value = unicode(value, "utf-8")

will return a Unicode object. If the data is not correctly encoded as UTF-8, the above call will raise a UnicodeError exception.

If you only want strings converted to Unicode which have non-ASCII data, you can try converting them first assuming an ASCII encoding, and then generate Unicode objects if that fails:

try:
x = unicode(value, "ascii")
except UnicodeError:
value = unicode(value, "utf-8")
else:
# value was valid ASCII data
pass

It's possible to set a default encoding in a file called sitecustomize.py that's part of the Python library. However, this isn't recommended because changing the Python-wide default encoding may cause third-party extension modules to fail.

Note that on Windows, there is an encoding known as "mbcs", which uses an encoding specific to your current locale. In many cases, and particularly when working with COM, this may be an appropriate default encoding to use.


20. How do I convert between tuples and lists? 
The function tuple(seq) converts any sequence (actually, any iterable) into a tuple with the same items in the same order.

For example, tuple([1, 2, 3]) yields (1, 2, 3) and tuple('abc') yields ('a', 'b', 'c'). If the argument is a tuple, it does not make a copy but returns the same object, so it is cheap to call tuple() when you aren't sure that an object is already a tuple.

The function list(seq) converts any sequence or iterable into a list with the same items in the same order. For example, list((1, 2, 3)) yields [1, 2, 3] and list('abc') yields ['a', 'b', 'c']. If the argument is a list, it makes a copy just like seq[:] would.


21. What's a negative index? 
Python sequences are indexed with positive numbers and negative numbers. For positive numbers 0 is the first index 1 is the second index and so forth. For negative indices -1 is the last index and -2 is the penultimate (next to last) index and so forth. Think of seq[-n] as the same as seq[len(seq)-n].

Using negative indices can be very convenient. For example S[:-1] is all of the string except for its last character, which is useful for removing the trailing newline from a string.


22. How do I iterate over a sequence in reverse order? If it is a list, the fastest solution is

list.reverse()
try:
for x in list:
"do something with x"
finally:
list.reverse()

This has the disadvantage that while you are in the loop, the list is temporarily reversed. If you don't like this, you can make a copy. This appears expensive but is actually faster than other solutions:

rev = list[:]
rev.reverse()
for x in rev:
<do something with x>

If it's not a list, a more general but slower solution is:

for i in range(len(sequence)-1, -1, -1):
x = sequence[i]
<do something with x>

A more elegant solution, is to define a class which acts as a sequence and yields the elements in reverse order (solution due to Steve Majewski):

class Rev:
def __init__(self, seq):
self.forw = seq
def __len__(self):
return len(self.forw)
def __getitem__(self, i):
return self.forw[-(i + 1)]

You can now simply write:

for x in Rev(list):
<do something with x>

Unfortunately, this solution is slowest of all, due to the method call overhead.

With Python 2.3, you can use an extended slice syntax:

for x in sequence[::-1]:
<do something with x>

23. How do you remove duplicates from a list?
If you don't mind reordering the list, sort it and then scan from the end of the list, deleting duplicates as you go:

if List:
List.sort()
last = List[-1]
for i in range(len(List)-2, -1, -1):
if last==List[i]: del List[i]
else: last=List[i]

If all elements of the list may be used as dictionary keys (i.e. they are all hash able) this is often faster

d = {}
for x in List: d[x]=x
List = d.values()


24. How do you make an array in Python? 
Use a list:
["this", 1, "is", "an", "array"]

Lists are equivalent to C or Pascal arrays in their time complexity; the primary difference is that a Python list can contain objects of many different types.

The array module also provides methods for creating arrays of fixed types with compact representations, but they are slower to index than lists. Also note that the Numeric extensions and others define array-like structures with various characteristics as well.

To get Lisp-style linked lists, you can emulate cons cells using tuples:

lisp_list = ("like", ("this", ("example", None) ) )

If mutability is desired, you could use lists instead of tuples. Here the analogue of lisp car is lisp_list[0] and the analogue of cdr is lisp_list[1]. Only do this if you're sure you really need to, because it's usually a lot slower than using Python lists.


25. How do I create a multidimensional list? 
You probably tried to make a multidimensional array like this:

A = [[None] * 2] * 3

This looks correct if you print it:

>>> A
[[None, None], [None, None], [None, None]]

But when you assign a value, it shows up in multiple places:

>>> A[0][0] = 5
>>> A
[[5, None], [5, None], [5, None]]

The reason is that replicating a list with * doesn't create copies, it only creates references to the existing objects. The *3 creates a list containing 3 references to the same list of length two. Changes to one row will show in all rows, which is almost certainly not what you want.

The suggested approach is to create a list of the desired length first and then fill in each element with a newly created list:

A = [None]*3
for i in range(3):
A[i] = [None] * 2

This generates a list containing 3 different lists of length two. You can also use a list comprehension:

w,h = 2,3
A = [ [None]*w for i in range(h) ]

Or, you can use an extension that provides a matrix datatype; Numeric Python is the best known.


26. How do I apply a method to a sequence of objects? 
Use a list comprehension:

result = [obj.method() for obj in List]

More generically, you can try the following function:

def method_map(objects, method, arguments):
"""method_map([a,b], "meth", (1,2)) gives [a.meth(1,2), b.meth(1,2)]"""
nobjects = len(objects)
methods = map(getattr, objects, [method]*nobjects)
return map(apply, methods, [arguments]*nobjects)


27. I want to do a complicated sort: can you do a Schwartzman Transform in Python? 
Yes, it's quite simple with list comprehensions.

The technique, attributed to Randal Schwartz of the Perl community, sorts the elements of a list by a metric which maps each element to its "sort value". To sort a list of strings by their uppercase values:

tmp1 = [ (x.upper(), x) for x in L ] # Schwartzman transform
tmp1.sort()
Usorted = [ x[1] for x in tmp1 ]

To sort by the integer value of a subfield extending from positions 10-15 in each string:

tmp2 = [ (int(s[10:15]), s) for s in L ] # Schwartzman transform
tmp2.sort()
Isorted = [ x[1] for x in tmp2 ]

Note that Isorted may also be computed by

def intfield(s):
return int(s[10:15])

def Icmp(s1, s2):
return cmp(intfield(s1), intfield(s2))

Isorted = L[:]
Isorted.sort(Icmp)

but since this method calls intfield() many times for each element of L, it is slower than the Schwartzman Transform.

15 comments:

  1. Hi Buddie,


    Gratitude for putting up this prolific article! You truly make everything a cake walk. Genuinely good stuff, saving time and energy.

    I am new to python. I am trying to use it for the problem as follows:
    I have one file with 3 coloums of 32 bit data in hex representation. Second file is also 3 coloums but showing only one or two bits in binary representation that is my look up table.

    Comparing two files records in such a way that on each iteration from first file picking one combination of bits (2bits) for 3 fields in record compare them to record in look up table I.e file 2.
    E.g
    File 1 record hex:
    A8000001 0000000 64000000

    File 2 data binary :
    10 00 01
    One iteration picks 01 compares to file2 first coloums data I.e 10 then file 1,2nd coloums 2 bits 00 with 2nd file 2nd coloums in same row it's 00. So on.
    Kindly suggest a solution or a hint how to implement it.

    It was cool to see your article pop up in my google search for the process yesterday. Great Guide.
    Keep up the good work!


    Thanks,
    Morgan

    ReplyDelete
  2. My rather long internet look up has at the end of the day been compensated with pleasant insight to talk about with my family and friends.
    Python Training in Bangalore

    ReplyDelete
  3. This comment has been removed by the author.

    ReplyDelete