Some basic Python tricks
| This page needs attention. Please make proper updates and then remove this template. Reason: need to be categorized |
Contents |
Handling command-line arguments
When writing a script, you might want to allow for the passing of arguments to modify the results. This will make it more generic, while allowing for the addition of features, and can provide some documentation (for which other users of your script/program will be very thankful :) )
Let's say you want to make a script that processes a string you give from the command-line, while allowing an optional "verbose" flag which provides more detailed output, or returns some documentation when incorrect or nonexistent arguments are provided.
For example:
pyscript (with no arguments where at least 1 is expected), pyscript -h, or pyscript --help should return the possible options.
pyscript -o 123 should return 123 (for this example we'll skip the processing).
pyscript -vo 123 should return some verbose text and 123.
There a numerous ways to do it, but here's an example (with some modifications) from the official Python documentation:
#!/usr/bin/env python
import getopt, sys
def usage():
print "Usage:"
print "<scriptname> options etc..."
print "this should be what you expect from -h or --help"
def main():
try:
opts, args = getopt.getopt(sys.argv[1:], "ho:v", ["help", "output="])
except getopt.GetoptError, err:
# print help information and exit:
print str(err) # will print something like "option -a not recognized"
usage()
sys.exit(2)
output = None
verbose = False
for o, a in opts:
if o == "-v":
verbose = True
elif o in ("-h", "--help"):
usage()
sys.exit()
elif o in ("-o", "--output"):
output = a
else:
print "unhandled option at this time"
usage()
sys.exit()
print "unprocessed output: " + str(output)
# ...
if __name__ == "__main__":
main()
If you include the sys module , commandline arguments are given to sys.argv ( as a list )
The actual handling of the arguments is done by the getopt module (for extended discussion see: [1] )
Switch statements in Python
Those who are familiar with the C or php-like syntax, may have noticed that Python doesnt have a switch statement.
Of course this could be solved by using endless if/elseif blocks, but there is a nicer way, involving dictionaries.
result = {
'option1': func1,
'option2': func2
} [val]()
This will execute the function according to the supplied value. Functions can be methods or lambda functions, whatever you like.
Lambdas in for-loops (by castironpi)
If you're constructing lambdas in a for loop and use the loop variable in the expression, you'll complete the for loop and find that all your functions use the same value for it, which is the last value it took in the loop. This is because Python binds the function to the variable, and evaluates it when the function is called. To get around it, you will need the new value to be unique in its scope when the function is declared.
The easy way uses a trick in the semantics of default parameter values, that they are evaluated at declaration-time. (Note: Some debate over whether this is totally consistent behavior.)
for i in youriter:
func = lambda i=i: return i
Other ways:
for i in youriter:
func = functools.partial( lambda i: return i, i )
or
for i in youriter:
def func_maker( i ):
def func( ):
return i
return func
func = func_maker( i )
The last way is probably the "real" way if you're judgmental.
Variable access & inheritance (by castironpi)
When using global flags in classes, you should always get them from class static variables. That way, if you want to change it for one of the objects, just assign to it; and it's easy to make a new default for a subset, just subclass.
class X:
var= 'something'
x, y= X(), X()
y.var= 'or other'
print x.var, y.var # something or other
This is because attributes are looked up in an elaborate order: instance dict, class dict, class tree dict.
If two unrelated classes are using the same variable, it may even be worth it to derive them from a common class, the purpose of which is only to store the variable.
Dynamic attribute creation & dispatch (by castironpi)
You can make objects have any attributes you like, any methods, whether they're in the class or a parent class or not. (But yes it's slow.)
In this example, we create ten methods for an object that have a variable already defined, corresponding to their index, and their name. (The secret variable is the neatest part.)
class A: pass
a= A( )
for i in range( 10 ):
def funcer( i ):
def func( ):
print 'i was', i
return func
setattr( a, 'func%i'% i, funcer( i ) )
a.func0( )
a.func1( )
a.func9( )
Output:
i was 0 i was 1 i was 9
If we want the class to have these operations, we change:
def func( self ):
and
setattr( A, 'func%i'% i, funcer( i ) )
and then instantiate a=A() after the for-loop instead.
We can also dispatch dynamically, to a central function. Here, we seem to have 10 functions, but they all get routed to the same place with an extra variable in the parameters.
import re
class A:
def __getattr__( self, name ):
q= re.match( r'func(\d)', name )
if q is None: raise AttributeError( '%s not found'% name )
var= q.group( 1 )
return lambda *ar,**kw: self.funcN( var, *ar, **kw )
def funcN( self, index, *ar, **kw ):
print 'i was', index, '...(other args %r %r)'% ( ar, kw )
a= A( )
a.func4( 'something' )
a.func7( 'something', 'else' )
a.func8( )
Output:
i was 4 ...(other args ('something',) {})
i was 7 ...(other args ('something', 'else') {})
i was 8 ...(other args () {})
Since we're simulating multiple simulated functions, they might have different calling signatures, so they get lumped together into argument structures. '__getattr__' is called when 'func4' isn't found the normal way, with 'name == "func4"'. "func4" matches the r.e. so a lambda is returned that will take us to funcN when *it* is called. Then, back on the road, the lambda is called, calling funcN, and returning the result, if any of our funcs have one.