Thursday, August 21, 2008

Python Interview Questions

1How do I parcel out work among a bunch of worker threads?
Use the Queue module to create a queue containing a list of jobs. The Queue class maintains a list of objects with .put(obj) to add an item to the queue and .get() to return an item. The class will take care of the locking necessary to ensure that each job is handed out exactly once.
Here's a trivial example:
import threading, Queue, time

# The worker thread gets jobs off the queue. When the queue is empty, it
# assumes there will be no more work and exits.
# (Realistically workers will run until terminated.)
def worker ():
print 'Running worker'
time.sleep(0.1)
while True:
try:
arg = q.get(block=False)
except Queue.Empty:
print 'Worker', threading.currentThread(),
print 'queue empty'
break
else:
print 'Worker', threading.currentThread(),
print 'running with argument', arg
time.sleep(0.5)

# Create queue
q = Queue.Queue()

# Start a pool of 5 workers
for i in range(5):
t = threading.Thread(target=worker, name='worker %i' % (i+1))
t.start()

# Begin adding work to the queue
for i in range(50):
q.put(i)

# Give threads time to run
print 'Main thread sleeping'
time.sleep(5)

When run, this will produce the following output:

Running worker Running worker Running worker Running worker Running worker Main thread sleeping Worker running with argument 0 Worker running with argument 1 Worker running with argument 2 Worker running with argument 3 Worker running with argument 4 Worker running with argument 5 ...

2How do I delete a file? (And other file questions...)
Use os.remove(filename) or os.unlink(filename);

3How do I copy a file?
The shutil module contains a copyfile() function.

4.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'

5. 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.

6. 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].

7. Why does os.path.isdir() fail on NT shared directories?
The solution appears to be always append the "\" on the end of shared drives.

>>> import os
>>>os.path.isdir( '\\\\rorschach\\public')
0
>>>os.path.isdir( '\\\\rorschach\\public\\')
1

It helps to think of share points as being like drive letters. Example:

k: is not a directory
k:\ is a directory
k:\media is a directory
k:\media\ is not a directory

The same rules apply if you substitute "k:" with "\conkyfoo":

\\conky\foo is not a directory
\\conky\foo\ is a directory
\\conky\foo\media is a directory
\\conky\foo\media\ is not a directory

Web Python
Some host providers only let you run CGI scripts in a certain directory, often named cgi-bin. In this case all you have to do to run the script is to call it like this:

http://my_server.tld/cgi-bin/my_script.py

The script will have to be made executable by "others". Give it a 755 permission or check the executable boxes if there is a graphical FTP interface.

Some hosts let you run CGI scripts in any directory. In some of these hosts you don't have to do anything do configure the directories. In others you will have to add these lines to a file named .htaccess in the directory you want to run CGI scripts from:

Options +ExecCGI
AddHandler cgi-script .py

If the file does not exist create it. All directories below a directory with a .htaccess file will inherit the configurations. So if you want to be able to run CGI scripts from all directories create this file in the document root.

To run a script saved at the root:

http://my_server.tld/my_script.py

If it was saved in some directory:

http://my_server.tld/some_dir/some_subdir/my_script.py

Make sure all text files you upload to the server are uploaded as text (not binary), specially if you are in Windows, otherwise you will have problems.

8.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>
print a
<__main__.a>

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!

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

10. 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.

11How do I read (or write) binary data?
or complex data formats, it's best to use the struct module. It allows you to take a string containing binary data (usually numbers) and convert it to Python objects; and vice versa.

For example, the following code reads two 2-byte integers and one 4-byte integer in big-endian format from a file:

import struct

f = open(filename, "rb") # Open in binary mode for portability
s = f.read(8)
x, y, z = struct.unpack(">hhl", s)


The '>' in the format string forces big-endian data; the letter 'h' reads one "short integer" (2 bytes), and 'l' reads one "long integer" (4 bytes) from the string.

12.How do I run a subprocess with pipes connected to both input and output?
Use the popen2 module. For example:

import popen2
fromchild, tochild = popen2.popen2("command")
tochild.write("input\n")
tochild.flush()
output = fromchild.readline()

13.How can I mimic CGI form submission (METHOD=POST)?
I would like to retrieve web pages that are the result of POSTing a form. Is there existing code that would let me do this easily?

Yes. Here's a simple example that uses httplib:

#!/usr/local/bin/python

import httplib, sys, time

### build the query string
qs = "First=Josephine&MI=Q&Last=Public"

### connect and send the server a path
httpobj = httplib.HTTP('www.some-server.out-there', 80)
httpobj.putrequest('POST', '/cgi-bin/some-cgi-script')
### now generate the rest of the HTTP headers...
httpobj.putheader('Accept', '*/*')
httpobj.putheader('Connection', 'Keep-Alive')
httpobj.putheader('Content-type', 'application/x-www-form-urlencoded')
httpobj.putheader('Content-length', '%d' % len(qs))
httpobj.endheaders()
httpobj.send(qs)
### find out what the server said in response...
reply, msg, hdrs = httpobj.getreply()
if reply != 200:
sys.stdout.write(httpobj.getfile().read())

Note that in general for URL-encoded POST operations, query strings must be quoted by using urllib.quote(). For example to send name="Guy Steele, Jr.":

>>> from urllib import quote
>>> x = quote("Guy Steele, Jr.")
>>> x
'Guy%20Steele,%20Jr.'
>>> query_string = "name="+x
>>> query_string
'name=Guy%20Steele,%20Jr.'

14. How do I emulate os.kill() in Windows?
Use win32api:

def kill(pid):
"""kill function for Win32"""
import win32api
handle = win32api.OpenProcess(1, 0, pid)
return (0 != win32api.TerminateProcess(handle, 0))

15. How do I call a method defined in a base class from a derived class that overrides it?
If you're using new-style classes, use the built-in super() function:

class Derived(Base):
def meth (self):
super(Derived, self).meth()

If you're using classic classes: For a class definition such as class Derived(Base): ... you can call method meth() defined in Base (or one of Base's base classes) as Base.meth(self, arguments...). Here, Base.meth is an unbound method, so you need to provide the self argument.

16. How can I organize my code to make it easier to change the base class?
You could define an alias for the base class, assign the real base class to it before your class definition, and use the alias throughout your class. Then all you have to change is the value assigned to the alias. Incidentally, this trick is also handy if you want to decide dynamically (e.g. depending on availability of resources) which base class to use. Example:

BaseAlias =
class Derived(BaseAlias):
def meth(self):
BaseAlias.meth(self)

17. How do I create static class data and static class methods?
Static data (in the sense of C++ or Java) is easy; static methods (again in the sense of C++ or Java) are not supported directly.

For static data, simply define a class attribute. To assign a new value to the attribute, you have to explicitly use the class name in the assignment:

class C:
count = 0 # number of times C.__init__ called

def __init__(self):
C.count = C.count + 1

def getcount(self):
return C.count # or return self.count

c.count also refers to C.count for any c such that isinstance(c, C) holds, unless overridden by c itself or by some class on the base-class search path from c.__class__ back to C.

Caution: within a method of C, an assignment like self.count = 42 creates a new and unrelated instance vrbl named "count" in self's own dict. Rebinding of a class-static data name must always specify the class whether inside a method or not:

C.count = 314

Static methods are possible when you're using new-style classes:

class C:
def static(arg1, arg2, arg3):
# No 'self' parameter!
...
static = staticmethod(static)

However, a far more straightforward way to get the effect of a static method is via a simple module-level function:

def getcount():
return C.count

If your code is structured so as to define one class (or tightly related class hierarchy) per module, this supplies the desired encapsulation.

18. How can I overload constructors (or methods) in Python?
This answer actually applies to all methods, but the question usually comes up first in the context of constructors.

In C++ you'd write

class C {
C() { cout << "No arguments\n"; } C(int i) { cout << "Argument is " << i="None):">

19. How do I use Python for CGI?
On the Microsoft IIS server or on the Win95 MS Personal Web Server you set up Python in the same way that you would set up any other scripting engine.

Run regedt32 and go to:

HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\W3SVC\Parameters\ScriptMap

and enter the following line (making any specific changes that your system may need):

.py :REG_SZ: c:\\python.exe -u %s %s

This line will allow you to call your script with a simple reference like: http://yourserver/scripts/yourscript.py provided "scripts" is an "executable" directory for your server (which it usually is by default). The "-u" flag specifies unbuffered and binary mode for stdin - needed when working with binary data.

In addition, it is recommended that using ".py" may not be a good idea for the file extensions when used in this context (you might want to reserve *.py for support modules and use *.cgi or *.cgp for "main program" scripts).

In order to set up Internet Information Services 5 to use Python for CGI processing, please see the following links:

http://www.e-coli.net/pyiis_server.html (for Win2k Server) http://www.e-coli.net/pyiis.html (for Win2k pro)

Configuring Apache is much simpler. In the Apache configuration file httpd.conf, add the following line at the end of the file:

ScriptInterpreterSource Registry

Then, give your Python CGI-scripts the extension .py and put them in the cgi-bin directory

1 comment:

Anonymous said...

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

Since Python 2.5 there is:
a = b if cond else d