Python
Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren't special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one-- and preferably only one --obvious way to do it.
Although that way may not be obvious at first unless you're Dutch.
Now is better than never.
Although never is often better than right now.
If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea -- let's do more of those!
Python Tips, Tricks, and Hacks
1 Quick Tricks
Tell you pain to inspire people not to share the pain.
1.1 Four kinds of Quotes
Python uses single quotes for one thing and double quotes for another. Python lets you use both, although not interchangeably. A triple Quote, ‘’’(three single quotes) “””(three double quotes)
Code:
print """I wish that I'd never heard him say, '''She said, "He said, 'Give me five dollars'"'''"""
o/p: I wish that I'd never heard him say, '''She said, "He said, 'Give me five dollars'"'''
1.2 Truthfulness of Various Objects
Unlike some programming languages, Python types are false if empty, and true if not. That means you don’t have to check, for example, that the length of a string, tuple, list or dist is 0 or is equal to an empty one. It is enough to just check the truthfulness of the object
For example, the following expressions are equivalent. Here, ‘my_object’ is a string, but it could easily be another python type
Example
my_object = ‘Test’ # True example
# my_object = ‘’ # False example
If len(my_object) > 0: # Not required to check like this
Print ‘my_object is not empty’
If my_object !=’’;
Print ‘my_object is not empty’
If my_object: # an empty string will evaluate to False
Print ‘my_object is not empty’
Conclusion: there’s really no need to check lengths or quality if you’re only interested in if the object is empty or not
1.3 Checking if a String Contains a Substring
We can test if a list, tuple or dict contains an item by testing the expression ‘item in list’ or ‘item not in list’. I never realized that this would work for strings as well.
Ugly Code
String = ‘Hi there’ # True example
# string = ‘Good bye’ # False example
if string.find(‘Hi’) != -1:
print success
Smart Code (simpler,cleaner)
String = ‘Hi there’ #True example
#string = ‘Good bye’ #False example
If ‘Hi’ in string:
Print ‘success!’
1.4 Pretty-Printing a List
Using ‘join’ method we can print the string nicely
String = [‘xamma’,’yamma’,’zamma’]
Print ‘The three most recent presidents were: %s.’ % ‘,’.join(string)
#prints ‘The three most recent presidents were: xamma,yamma,zamma
The ‘join’ method turns the list into a string by casting each item into a string and connecting them with the string that ‘join’ was called on. It’s even smart enough to not put one after the last element
As an added advantage, this is pretty fast, running in linear time. Don’t ever create a string by ‘+’ing list items together in a for loop; not only is it ugly, but it takes much longer.
1.5 Integer vs. Float Division
By default, if you divide one integer, the result will be truncated into an integer. For example
Executing 5/2 returns 2
There are two was to fix this. The first and simplest way is to just turn one of the integers into a float. If the values are static, you can just append a .0 to one to make it a float: 5.0/2 returns 2.5. Alternatively, you can just cast one of the values: float(5) / 2 returns 2.5
The other way will result in cleaner code, but you must make sure none of you code is relying on this truncation. You can do a from __future__import division to change python to always return a float as the result of a division. After such an import, 5/2 will return 2.5. If you still need to use the truncating integer division somewhere, you can then use the // operator: 5//2 will always return 2
Examples
5/2 #Returns 2
5.0/2 # Returns 2.5
Float(5)/2 # Returns 2.5
5//2 # Returns 2
From __future__import division
5/2 #returns 2.5
5.0/2 #Returns 2.5
Float(5)/2 # Returns 2.5
5//2 # Returns 2
If you want your code to be future-proof, use the // operator if you want truncating division, no matter if you are doing a from __future__import division or not
1.6 Lambda Fuctions
Sometimes you need to pass a function as an argument, or you want to do a short but complex operation multiple times. You could define your function the normal way, or you could make a lambda function, a mini-function that returns the result of a single expression. The two definitions are completely identical
def add(a,b): return a+b
lambda way -> add2 = lambda a,b: a+b
The advantage of the lambda function is that it is in itself an expression, and can be used inside another statement. Here’s an example using the ‘map’ function, which calls a function on every element in a list, and returns a list of the results.
Example:
squares = map(lambda a: a*a, [1,2,3,4,5])
#squares is now [1,4,9,16,25]
Without a lambda, you’d have to define the function separately. You’ve just saved a line of code and a variable name
Syntax: Lambda Functions
A lambda function has the syntax: lambda variable(s) : expression
Variable(s) – a comma-separated list variable or variables that the function can receive. You can’t use keywords, and you don’t want these to be in parentheses (a mistake I started making for a couple of months and wondered why my lambdas never workd)
Expression – an inline python expression. Scope includes local scope and variable(s). This is what the function returns.
Lists
a) List Comprehensions
List can fit in a for loop, an if statement, and an assignment all in one line. In other words, you can map and filter a list in one expression
a.1 Mapping the List
Trying to square every element
Numbers = [1,2,3,4,5]
Squares = []
For number in numbers:
Squares.append(number*number)
#Now, squares should have [1,4,9,16,25]
Map function, can be done something like below
Numbers = [1,2,3,4,5]
Squares = map(lambda x: x*x, numbers)
#Now, squares should have [1,4,9,16,25]
The cleaner way !
Numbers = [1,2,3,4,5]
Squares = [ number*number for number in numbers]
# Now, squares should have [1,4,9,16,25]
This does the exact same thing as the previous two examples
b) Filtering the List
Example, we want to remove every element with a value equal to or greater that 4?
Numbers = [1,2,3,4,5]
Numbers_under_4 = []
For number in numbers:
If number < 4:
Numbers_under_4.append(number)
# Now, numbers_under_4 contains [1,4,9]
Better one
Numbers = [1,2,3,4,5]
Numbers_under_4 = filter(lambda x: x <4, numbers)
#now, numbers_under_4 contains [1,2,3]
Filter accepts a function and a list. It evaluates for every list element and if the function evaluates evaluates to true, that list element is included in the final list.
Numbers = [ 1,2,3,4,5]
Numbers_under_4 = [number for number in numbers if number < 4]
# now, numbers_under_4 contains [1,2,3]
c) Map and filter at Once
Example: ‘map’ and ‘filter’ a list at the same time.
Numbers = [1,2,3,4,5]
Squares = []
For number in numbers:
If number < 4:
Squares.append(number*number)
#squares is now [1,4,9]
Numbers = [1,2,3,4,5]
Squares = map(lambda x: x*x, filter(lambda x: x < 4, numbers))
# squares is now [1,4,9]
Numbers = [1,2,3,4,5]
Squares = [ number*number for number in numbers if number < 4]
# square is now [1,4,9]
d) Generator Expressions
There is a downside to list comprehensions: the entire list has to be stored in memory at once. Eventually this becomes pretty inefficient
Generator expressions are newish in python 2.4, generator expressions do not load the whole list into memory at once, but instead create a ‘generator object’ so only one list element has to be loaded at any time. If you’re just passing it off to something that takes any iterable object – like a for loop – you might as well use a generator function
Generator expressions have the same syntax as list comprehensions, but with parentheses around the outside instead of brackets
Numbers = (1,2,3,4,5) # Since we’re going for efficiency, I’m using a tuple instead of a list
Squares_under_10 = (number*number for number in numbers if number*number < 10)
#squares_under_10 is now a generator object, from which each successive value can gotten by calling next()
For square in squares_under_10:
Print square
#prints ‘1 4 9’
This is ever so slightly more efficient than using a list comprehension. Use list comprehensions if you need the entire list at once for some reason. If neither of these is true, just do whatever you want. It’s probably good practice to use generators expressions unless there’s some reason not to, but you’re not going to see any real difference in efficiency unless the list is very large
As a final note, generator expressions only need to be surrounded by one set of parentheses. So, if you’re calling a function with only a generator expression, you only need one set of parentheses. This is valid python: some_function(item for item in list)
Nested ‘for’ Statements
List comprehensions and generators expressions can be used for more than just mapping and filtering
for y in (0,1,2,3):
if x < y:
print (x, y, x*y),
# prints (0, 1, 0) (0, 2, 0) (0, 3, 0) (1, 2, 2) (1, 3, 3) (2, 3, 6)
Better Example
print [(x, y, x * y) for x in (0,1,2,3) for y in (0,1,2,3) if x < y]
# prints [(0, 1, 0), (0, 2, 0), (0, 3, 0), (1, 2, 2), (1, 3, 3), (2, 3, 6)
Print [(x, y, x * y) for x in (0,1,2,3) for y in (0,1,2,3) if x < y ]
# prints [(0,1,0), (0,2,0), (0,3,0), (1,2,2), (1,3,3), (2,3,6)]
Syntax: List Comprehensions and Generator Expressions
A list comprehension has the syntax: [ element for variable(s) in list if condition ]
A generator expression has the syntax: ( element for variable(s) in list if condition )
List anything has can be treated as a list or iterator
Variable(s) variable or variables to assign the current list element to, just like in a regular for loop
Condition an inline python expression. Scope again includes local scope and variable(s). If this evaluates to true, item will be included in result
element an inline python expression. Scope includes the local scope and variables(s). This is the actual element that will be included in the result
The for variable(s) in list bit can be repeated indefinitely
Reducing a List
Numbers = [1,2,3,4,5]
Result = 1
For number in numbers:
Result *= number
# result is now 120
Use the built-in function ‘reduce’, which accepts a function that takes two arguments
Numbers = [1,2,3,4,5]
Result = reduce(lambda a,b: a*b, numbers)
#result in now 120
Iterating over a List: range, xrange and enumerate
Passing a value to ‘range’ gives you a list of counting integers from 0 to the value -1, inclusive. In other words, it gives you the index values of a list with that length. ‘xrange’ does the same thing, except a bit more efficiently: it doesn’t load the whole list into memory at once
Strings = [‘a’,’b’,’c’,’d’,’e’]
For index in xrange (len(strings)):
Print index
# prints ‘0 1 2 3 4 ‘
Python has a really awesome built-in function called ‘enumerate’ that will give you both. Enumerate-ing a list will return an iterator of index, value pairs
Strings = [‘a’,’b’,’c’,’d’,’e’]
For index, string in enumerate(strings):
Print index,string
#prints ‘0 a 1 b 2 c 3 d 4 e 5’
As an added advantage, enumerate is quite a bit cleaner and more readable than xrange(len()). Because of this, ‘range’ and ‘xrange’ are probably only useful if you need to create a list of values from scratch for some reason, instead of from an existing list
Checking a Condition on Any or Every List Element
Numbers = [1,10,100,1000,10000]
If [ number for number in numbers if number < 10]:
Print ‘At least one element is over 10’
#Output: ‘At least one element is over 10’
If none of the elements satisfy the condition, the list comprehension will create an empty list which evaluates as false. Otherwise, a non-empty list will be created, which evaluates as true
Build-in ‘any’ function
Numbers = [1,10,100,1000,10000]
If any(number < 10 for number in numbers):
Print ‘Success’
# output : ‘Success!’
Numbers = [1,2,3,4,5,6,7,8,9]
If len(numbers) == len([number for number in numbers if number < 10]):
Print ‘success’
#Output: ‘Success’
The built-in ‘all’ function. As you might expect, it’s smart enough to bail after the first element that doesn’t match, returning ‘False’
Numbers = [1,2,3,4,5,6,7,8,9]
If all (number < 10 for number in numbers):
Print ‘Success’
#Output: ‘Success!’
Combining Multiple Lists, item by item
The built-in ‘zip’ function can be used, well , to zip lists together. It returns a list of tuples, where the nth tuple contains the nth item from each of the passed in lists
Letters = [‘a’,’b’,’c’]
Numbers = [1,2,3]
Squares = [1,4,9]
Zipped_list = zip(letters, numbers, squares)
#zipped_list contains [(‘a’,1,1),(‘b’,2,4),(‘c’,3,9)]
A Few More List Operators
The following are all built-in functions that can be called on any list or iterable
Max – Returns the largest element in the list
Min – Returns the smallest element in the list
Sum – Returns the sum of all elements in the list
Advanced Logic With Sets
Sets differ from lists in that they enforce uniqueness (they can’t contain more than one of the same item) and are unordered. The most common thing I want to do is to make sure my list is unique. This is easy; I just have to convert it to a set and check if the length is the same
Numbers = [1,2,3,3,4,1]
Set (numbers)
# returns set ([1,2,3,4])
If len (numbers) == len(set(numbers)):
Print ‘List is unique!`
#In this case, doesn’t print anything
Dictionaries
Constructing Dictionaries with Keyword Arguments
Dict(a=1, b=2, c=3)
#returns {‘a’:1, ‘b’:2,’c’:3}
This might be a bit cleaner than a ‘regular’ dictionary creation depending on your code; there are less quotes floating around
Dicts to Lists
Turning a dictionary into a list or an iterator is easy. To get a list of keys, you can just case the dict into a list. It’s cleaner, though to call .keys() on the dictionary to get a list of the keys, or .iterkeys() to get an iterator. Similarly, you can call .values() or .itervalues() to get a list or iterator of dictionary values.
To preserve both keys and values, you can turn a dict into a list or iterator of 2-item tuples by using .items() or .iteritems().
Dictionary = {‘a’:1,’b’:2,’c’:3}
Dict_as_list = dictionary.items()
#dict_as_list now contains [(‘a’,1), (‘b’,2),(‘c’,3)]
Lists to Dicts
You can reverse the process, turning a list of 2-element lists or tuples into a dict
Dict_as_list = [[‘a’,1],[‘b’,2],[‘c’,3]]
Dictionary = dict(dict_as_list)
#dictionary now contains {‘a’:1,’b’:2,’c’:3}
With the ‘keyword arguments’ method
Dict_as_list = [[‘a’,1],[‘b’,2],[‘c’,3]]
Dictionary = dict(dict_as_list, d=4, e=5)
#dictionary now contains {‘a’:1,’b’:2,’c’:3,’d’: 4,’e’: 5}
Dictionary Comprehensions
Emails = {‘Rama’: ‘x@example.com’,’Gyan’:’y@example.com’,’mady’:’z@example.net’}
Email_at_dotcom = dict([name,’.com’ in email] for name, email in emails.iteritems())
#email_at_dotcom now is {‘x’:True, ‘y’:True,’z’:False}
Selecting Values
The Right Way
Python now supports the syntax ‘value_if_true if test else value_if_false’
Test = True
#test = False
Result = ‘Test is True’ if test else ‘Test is False’
#result is now ‘Test is True’
Test1 = False
Test2 = True
Result = ‘Test1 is True’ if test1 else ‘Test1 is False, test2 is True’ if test2 else ‘Test1 and Test2 are both False’
O/p: Test1 is False, test2 is True
The first if/else is evaluated first, and if test1 is false the second if/else is evaluated.
The and/or Trick
In python, ‘and’ and ‘or’ are complex creatures. ‘and’ returns the first false value, or the last value if all are true. In other words, if the first value is false it is returned, otherwise the last value is returned. The result of this is something you would expect: if both are true, the last value is returned, which is true and will evaluate to True in a Boolean test. If one is false, that one is returned and will evaluated to False in a Boolean test
Or-ing two expressions, if the first value is true it is returned, otherwise the last value is returned. So, if both are false, the last value is returned, which is false and will evaluate to False in a Boolean test. If one is true, that one is returned and will evaluate to True in a Boolean test
Test = True
#test = false
Result = test and ‘test is true’ or ‘test is false’
#result is now ‘Test is true’
Explanation:
If test is true, the and statement skips over it and returns its right half, here ‘Test is True’ or ‘Test is False’. As processing continues left to right, the or statement returns the first true value, ‘Test is True’
If test if false, the and statement returns test. As processing continues left to right, the remaining statement is test or ‘Test is False’. Since test is false, the or statement skips over it and returns its right half, ‘Test is False’
Using True and False as Indexes
Another way to select values is to use True and False as list indexes, taking advantage of the fact that False == 0 and True ==1
Test = True
#test = False
Result = [‘Test is False’,’Test is True’][test]
#result is now ‘Test is True’
Functions
Default Argument Values are Only Evaluated Once
def function(item, stuff = []):
stuff.append(item)
print stuff
function(1)
#print ‘[1]’
Function(2)
#print ‘[1.2]’ !!!
The default value for a function argument is only evaluated once, when the function is defined. Python simply assigns this value to the correct variable name when the function is called.
Python doesn’t check if that value (that location is memory) was changed. It just continues to assign that value to any caller that needs it. So, if the value is changed, the change will persist across function calls. The solutions- don’t use mutable objects as function defaults.
Better Code !
Def function(item, stuff = None):
If stuff is None:
Stuff = []
Stuff.append(item)
Print stuff
Function(1)
#prints ‘[1]’
Function(2)
#prints ‘[2]’, as expected
None is immutable, so we’re safe from accidently changing value of the default.
Force Default Arguments to be Evaluated Each Time
From copy import deepcopy
Def resetDefaults(f):
Defaults = f.func_defaults
Def resetter(*args, **kwds):
f.func_defaults = deepcopy(defaults)
return f(*args, **kwds)
resetter.__name__ = f.__name__
return resetter
Simply apply this decorator to your function to get the expected results
@resetDefaults #This is how you apply a decorator
Def function(item, stuff = []):
Stuff.append(item)
Print stuff
Function(1)
#prints ‘[1]’
Function(2)
#prints ‘[2]’ , as expected
Arbitrary Numbers of Arguments
Python lets you have arbitrary numbers of arguments in your functions. First define any required arguments, then use a variable with a ‘*’ prepended to it. Python will take the rest of the non-keyword arguments, put them in a list or tuple, and assign them to this variable:
Def do_something(a,b,c, *args):
Print a, b, c, args
Do_something(1,2,3,4,5,6,7,8,9)
#prints ‘1,2,3, (4,5,6,7,8,9)’
After you’ve defined all other arguments, use a variable with ‘**’ prepended to it. Python will take the rest of the keyword arguments, put them in a dictionary, and assign them to this variable
Def do_something_else(a, b, c, *args, **kwargs):
Print a, b, c, args, kwargs
Do_something_else(1,2,3,4,5,6,7,8,9, timeout=1.5)
#prints ‘1,2,3, (4,5,6,7,8,9), {“timeout”: 1.5}’
Caveat
def do_something(a, b, c, actually_print, *args):
If actually_print:
print a, b, c, args
do_something(1, 2, 3, True, 4, 5, 6)
The only way to pass ‘actually_print’ in this situation is to pass it as a non-keyword argument
do_something(1, 2, 3, True, 4, 5, 6)
#Result is ‘1, 2, 3, (4, 5, 6)’
Passing a List or Dictionary as Arguments
Since you can receive arguments as a list or dictionary, you can send arguments to a function from a list or dictionary
To send a list as non-keyword arguments, just prepend it with a ‘*’
args = [5,2]
pow(*args)
# returns pow(5,2), meaning 5^2 which is 25
To send a dictionary as keyword arguments (this is probably more common), prepend it with ‘**’
def do_something(actually_do_something=True, print_a_bunch_of_numbers=False):
if actually_do_something:
print ‘something has been done’
if print_a_bunch_of_numbers:
print range(10)
kwargs = {‘actually_do_something’: True, ‘print_a_bunch_of_numbers’: True}
do_something(**kwargs)
#prints ‘Something has been done’, then ‘[0,1,2,3,4,5,6,7,8,9]’
Decorators
A decorator is a function that wraps another function: the main function is called and its return value is passed to the decorator. The decorator then returns a function that replaces the wrapped function: the main function is called and its return value is passed to the decorator. The decorator then returns a function that replaces the wrapped function as far as the rest of the program is concerned.
Def decorator1(func):
Return lambda: func() + 1
def decorator2(func):
def print_func():
print func()
return print_func
@decorator2
@decorator1
def function():
return 41
function()
#prints ‘42’
In this example, ‘function’ is passed to ‘decorator1’. ‘decorator1’ returns a function that calls ‘function’ and adds 1. This function is then passed to ‘decorator2’, which returns a function that calls the function returned by ‘decorator1’ and prints the result. This last function is the function you are actually calling when you call ‘function’
This example does the exact same thing, but more verbosely and without decorators:
def decorator1(func):
return lambda: func() + 1
def decorator2(func):
def print_func():
print func()
return print_func
def function():
return 41
function = decorator2(decorator1(function))
function()
#prints ‘42’
Switch Statements’ using Dictionaries of Functions
For example, say you’re handling keystrokes and you need to call a different function for each keystroke
def key_1_pressed():
print ‘Key 1 pressed’
def key_2_pressed():
print ‘key 2 pressed’
def key_3_pressed():
print ‘key 3 pressed’
def unknown_key_pressed():
# print ‘unknown key pressed’
In python, you would typically use elif’s to choose a function
Keycode = 2
If keycode == 1:
Key_1_pressed()
elif keycode == 2:
Key_2_pressed()
elif number == 3:
Key_3_pressed()
else:
Unknown key pressed()
# prints ‘key 2 pressed’
But you could also throw all the functions in a dictionary, and key them to the value you’re switching on. You could even check see if the key exists and run some code if it doesn’t
Keycode = 2
functions = {1: key_1_pressed, 2: key_2_pressed, 3: key_3_pressed }
functions.get(keycode, unknown_key_pressed) ()
Classes
Passing ‘self’ Manually
Methods are just regular functions that when called from an instance are passed that instance as the first argument (usually called self ). If for some reason you’re not calling the function from an instance, you can always pass the instance manually as the first argument.
Class Class:
def a_method(self):
print ‘Hey a method’
instance = Class()
instance.a_method()
#prints ‘Hey a method’, somewhat unsurprisingly. You can also do:
Class.a_method(instance)
#prints ‘Hey a method’
Internally, these statements are exactly the same
Checking for Property and Method Existence
Need to know if a particular class or instance has a particular property or method? You can use the built-in ‘hasattr’ function to check; it accepts the object and the attribute (as a string) to check for. You use similarly to the dict ‘has_key’ method
class Class:
answer = 42
hasattr(Class, ‘answer’)
#returns True
Hasattr(Class, ‘question’)
#returns False
You can also check for existence of and access the property in one step using the built-in function ‘getattr’. Getattr also accepts the object and the attribute, as a string, to check for.
class Class:
answer = 42
getattr(Class, ‘answer’)
#returns 42
getattr(Class, ‘question’,’What is six times nine?’)
#returns ‘What is six times nine?’
getattr(Class, ‘question’)
#raises AttributeError
Don’t overuse ‘hasattr’ and ‘getattr’. If you’ve written your class in manner where you need to keep checking to see it a property exists, you’ve written it wrong. Just always have the value exist and set it to ‘None’ if it’s not being used. These functions are best used for handling polymorphism, that is, allowing your function/class/whatever to support different kinds of objects
Modifying Classes After Creation
You can add, modify or delete a class property or method long after the class has been created, and even after it has been instantiated. Just access the property or method as Class.attribute. No matter when they were created, instances of the class will respect these changes
class Class:
def method(self):
print ‘Hey a method’
instance = Class()
instance.method()
#prints ‘Hey a method’
def new_method(self):
print ‘New method wins!’
Class.method = new_method
Instance.method()
#prints ‘New method wins!’
Pretty awesome. But don’t get carried away
Creating Class Methods
Occassionally when writing a class you want to include a function that is called from the class, not the instance. Perhaps this method creates new instances, or perhaps it is independent of any properties of any individual instance. Python actually gives you two ways to do this, depending if your method needs to (or should) know about which class called it. both involves applying decorators to your methods
A ‘class method’ receives the class as the first argument, just as a regular instance method receives the instance as the first argument. So, the method is aware if it is being called from its own class or from a subclass.
A ‘static method’ receives no information about where it is called; it is essentially a regular function, just in a different scope.
Class and static methods can be called straight from the class, as Class.method(), or from an instance as Class().method(). The instance is ignored except for its class. Here’s an example of each , along with a regular instance method
class Class:
@classmethod
Def a_class_method(cls):
Print ‘I was called from class %s’ % cls
Def an_instance_method(self):
Print ‘I was called from the instance %s’ % self
Instance = Class()
Class.a_class_method()
Instance.a_class_method()
#both print ‘I was called from class __main__.Class’
Class.a_static_method()
Instance.a_static_method()
#both print ‘I have no idea where I was called from’
Class.an_instance_method()
#raises TypeError
Instance.an_instance_method()
#prints something like ‘I was called from the instance <__main__.Class instance at 0x2e80d0>’
References
Good Book
http://tomayko.com/writings/dynamic-superclassing-in-python
Dynamic Superclassing in Python
I would like to make it possible for people to extend the base objects with custom methods for doing whatever weird stuff people like to do
Let us have a module, biz.py, with the following class definition
class A:
def __init__(self):
self.x = 5
self.y = 10
def foo(self):
print self.x
Now, let’s say we want to add a special ‘bar’ method that would be kind of like the ‘foo’ method but would print x * 3.14. We want to be able to do this outside the original ‘biz’ module; let’s say nuge.py:
from biz import A
def bar(self):
print self.x *3.14
A.bar = bar
Let’s give it a try:
>>> import biz, nuge
>>> a = biz.A()
>>>a.foo()
5
>>>a.bar()
15.70000001
Note that we never actually use anything from ‘nuge’ but we need to import it so that it can molest biz.A. Remember that module level statements are executed when the module is imported.
It’s also kind of interesting that you can modify the base classes of a class at anytime. Instead of adding a single method, it’s possible to add an entire class (or set the classes) into the **bases** chain, effectively grafting two (or more) classes together. Or, more precisely, dynamically superclassing A with B.
Let us redine nuge.py as follows :
Class B:
def bar(self):
print self.x * 3.14
def baz(self):
print self.x ** 2
# this magic moment..
A.__bases__ = (B,)
This is equivalent to specifying B as superclass when we define A:
Class A(B):
The advantage is that we can do this at runtime without modifying A’s source. The effect is that B becomes a superclass of A and B’s methods are thus available on all A instances
>>>import biz, nuge
>>>a = biz.A()
>>>a.foo()
5
>>>a.bar()
15.7
>>>isinstance(a,B)
True
I should note that assigning the tuple(B,) to A. bases overwrites the original, declared superclass(es). It is much wiser to combine the original bases value with the new class as follows:
A.__bases__ += (B,)
This appends B to the existing set of bases instead of just destroying them
Code
Note that you will never find documentation that tells you all this, you have to play around
class A:
def __init__(self):
self.x = 5
def foo(self):
print self.x
def bling(self):
print self.x – self.y
class B:
def bar(self):
print self.x * 3.14
def baz(self):
print self.x ** 2
A.__bases__ += (B,)
a = A()
a.foo()
a.bling()
a.bar()
a.baz()
print isinstance(a,B)
Module: The most basic unit of code reusability in Python. What you usually ‘import’ Typically a single file
Package: A group of modules and other packages. Typically a folder on the filesystem, distinguished by an __init__.py file inside
Distribution: A package or group of packages meant to be installed at the same time. What you are releasing here. Source distributions are usually represented folder, called a ‘root package’, with a setup.py file inside.
Site-packages directory: The place in your filesystem where all of the installed distributions live
NOTE: Only one __init__.py file will be loaded for each virtual package, and which one is not determined until runtime. Therefore it is important to either put nothing but these lines in the file or have each file be exactly the same.
Decorators modify functions. Beginning with the basics, learn how to use decorators in a variety of ways. Execute code when a function is parsed or called. Conditionally call functions and transform inputs and outputs. Write customizable decorators that accept arbitrary arguments. And, if necessary, easily make sure your decorated function has the same signature as the original
Decorators modify functions. More specifically, a decorator is a function ‘that transforms another function’
Without decorators (code)
def decorator_function(target):
#Do something with target function
target.attribute = 1
return target
def target(a,b):
return a + b
#This is what the decorator actually does
target = decorator_function(target)
Using decorator (code)
def decorator_function(target):
# Do something with the target function
target.attribute = 1
return target
#Here is the decorator, with the syntax ‘@function_name’
@decorator_function
def target(a,b):
Return a + b
As you can see, you need to put the decorator function’s name, prefaced with a ‘@’ on the line before the ‘target function’ definition. Python internally will transform the ‘target’ by applying the decorator to it and replacing it with the returned value
>>> target(1,2)
3
>>>target.attribute
1
Does a decorator function have to return a function
No. The decorator function can return absolutely anything, and python will replace the ‘target’ function with that return value. Example
def decorator_evil(target)
return False
@decorator_evil
def target(a,b):
return a + b
print target #o/p – False
print target(1,2) #o/p TypeError: ‘bool’ object is not callable
The Wrapper Function
Remember, your ‘decorator function’ can return an arbitrary function. We’ll call it the wrapper function, for reasons which will become clear in a second. The trick here is to define the wrapper function inside the ‘decorator function’, giving it access to the decorator function’s variable scope, including the target function
def decorator(target):
def wrapper():
print ‘Calling function “%s”’ % target.__name__
return target()
#Since the wrapper is replacing the target function, assigning an attribute to the target function
#We need to assign it to the *wrapper function*.
wrapper.attribute = 1
return wrapper
@decorator
def target():
print ‘I am the target function’
>>> target()
Calling function “target”
I am the target function
>>> target.attribute
1
As you can see, the ‘wrapper function’ can do whatever it wants to the target function, including the simple case of returning the target’s return value
Getting the Arguments (Arguments passed to the target functions)
Since the returned ‘wrapper function’ replaces the target function, the wrapper function will receive the arguments intended for the target function. Assuming you want your decorator to work for any target function, your wrapper function then should accept arbitrary non-keyword arguments and arbitrary keyword arguments, add, remove, or modify arguments if necessary, and pass the arguments to the target function
def decorator(target):
def wrapper(*args, **kwargs):
kwargs.update({‘debug’: True}) #Edit the keyword arguments – here, enable debug mode no matter what
print ‘Calling function “%s” with arguments %s and keyword arguments %s’ % (target.__name__, args, kwargs)
return target(*args, **kwargs)
wrapper.attribute = 1
return wrapper
@decorator
def target(a, b, debug=False):
if debug: print ‘[Debug] I am the target function’
return a + b
>>> target(1,2)
Calling function “Target” with arguments (1,2) and keyword arguments {‘debug’: True}
[Debug] I am the target function
3
>>>target.attribute
1
Note: you can also apply a decorator to a ‘class method’. If you decorator is always going to be used this way, and you need access to the current instance, you ‘wrapper function’ can assume the first argument is always self:
def wrapper(self, *args, **kwargs):
#Do something with ‘self’
print self
return target(self, *args, **kwargs)