Basics
package/module/import
- Python will search the imported module in directories defined in sys.path
- packages(is also modules) are generally implemented as directories containing a special __init\_.py _file (executed when the package is imported)
- __all__ = [] _in \_init\_.py _file controls what are imported when import * is used.
Styling
- should maintain 2 empty lines between functions
- Docstrings should be the first statement of a function or module. Docstrings provide help()
#!/usr/bin/env python3
"""Retrieve and print words from a URL
Usage:
"""
import sys
from urllib.request import urlopen
def fetch_words(url):
"""Fetch a list of words from a URL.
Args:
url: The URL of a UTF-8 text document.
Returns:
A list of strings containing the words from the document.
"""
with urlopen(url) as story:
story_words = []
for line in story:
line_words = line.decode('utf-8').split()
for word in line_words:
story_words.append(word)
return story_words
Special Attributes
Special attributes in python are delimited by double underscores.
__name__ to determine how the module is being used.
if __name__ == 'words': # == name of the module, when imports, and it only execute once even import multiple times
print(__name__)
if __name__ == '__main__': # when executing the .py script
main(sys.argv[1])
'with' statement
class controlled_execution:
def __enter__(self):
set things up
return thing
def __exit__(self, type, value, traceback):
tear things down
with controlled_execution() as thing:
some code
when the “with” statement is executed, Python evaluates the expression, calls the __enter__ method on the resulting value (which is called a “context guard”), and assigns whatever __enter__ returns to the variable given by as. Python will then execute the code body, and no matter what happens in that code, call the guard object’s __exit__ method.
Context Manager
http://preshing.com/20110920/the-python-with-statement-by-example/
Default Parameter
Python’s default arguments are evaluated once when the function is defined, not each time the function is called (like it is in say, Ruby).
def append_to(element, to=[]):
to.append(element)
return to
What You Should Do Instead
Create a new object each time the function is called, by using a default arg to signal that no argument was provided (None
is often a good choice).
def append_to(element, to=None):
if to is None:
to = []
to.append(element)
return to
Scope
LEGB rule: Local, Enclosing, Global, Built-in.
Below function will create a new local binding of "count".
Use global keyword to introduce names from global namespace into the local namespace. (reading the variable no need global keyword)
Use nonlocal keyword to introduce names from the enclosing namespace into the local namespace.
count = 0
def set_count(c):
count = c
Numeric
shortest = float('inf')
Functions
def is executed in runtime -> functions are defined in runtime.
functions in python are first-class, treated like other objects.
#lambda function
lambda name: name.split()[-1] #no need return keyword
#Unpacking Argument Lists
args = [3, 6]
range(*args) # call with arguments unpacked from a list #[3, 4, 5]
#Similar fashion, dictionaries can deliver keyword arguments with the **-operator:
def parrot(voltage, state='a stiff', action='voom'):
print "-- This parrot wouldn't", action,
print "if you put", voltage, "volts through it.",
print "E's", state, "!"
d = {"voltage": "four million", "state": "bleedin' demised", "action": "VOOM"}
parrot(**d)
#define functions accept arbitrary number of arguments. The arguments list should follow this order
def print_args(arg1, arg2, *args, kwarg1, kwarg2, **kwargs):
function factory: function that returns new, specialised functions
def raise_to(exp):
def raise_to_exp(x):
return pow(x, exp)
return raise_to_exp
#python will create a closure to refer to the exp obj
square = raise_to(2)
square.__closure__
Generator
Python provides generator functions as a convenient shortcut to building iterators.
# a generator that yields items instead of returning a list
def firstn(n):
num = 0
while num < n:
yield num
num += 1
sum_of_first_n = sum(firstn(1000000))
Decorators
Modify or enhance functions without changing their definition.
@functools.wraps(f): properly update metadata on wrapped functions e.g. __name\_, \_doc\__
###################### function decorator ######################
def escape_unicode(f):
@functools.wraps(f)
def wrap(*args, **kwargs):
x = f(*args, **kwargs)
return ascii(x)
return wrap
@escape_unicode
def northern_city(prefix):
return prefix+'Troms@'
###################### class decorator ##########################
class CallCount:
def __init__(self, f):
self.f = f
self.count = 0
def __call__(self, *args, **kwargs):
self.count += 1
return self.f(*args, **kwargs)
@CallCount
def hello(name):
print('Hello, {}'.format(name))
hello.count #since the hello function is decorated as CallCount class instance. It has attribute count.
###################### instances as decorator ####################
class Trace:
def __init__(self):
self.enabled = True
def __call__(self, f):
def wrap(*args, **kwargs):
if self.enabled:
print('Calling {}'.format(f))
return f(*args, **kwargs)
return wrap
tracer = Trace()
@tracer
def rotate_list(l):
return l[1:] + [l[0]]
###################### multiple decorators ######################
@decorator1
@decorator2
@decorator3
def some_function(): #the decorators are evaluated at order of bottom to top
###################### Decorator can be applied to class methods ######################
class IslandMaker:
def __init__(self, suffix):
self.suffix = suffix
@tracer
def make_island(self, name):
return name + self.suffix
###################### Decorator Factory ######################
def check_non_negative(index):
def validator(f):
def wrap(*args):
if args[index] < 0:
raise ValueError('Argument {} must be non-negative.'.format(index))
return f(*args)
return wrap
return validator
@check_non_negative(1) #this calls the check_non_negative function which returns the actual decorator
def create_list(value, size):
return [value] * size
Exception Handling
try:
return int(s)
except (TypeError, ValueError) as e:
print("Conversion error: {}"\
.format(str(e)),
file=sys.stderr)
raise
finally:
if x < 0:
raise ValueError("Cannot compute square root of negative number {}".format(x))
Files and Resource Management
#write to file
try:
f = open('wasteland.txt', mode='wt', encoding='utf-8')
f.write('what are the roots that ...\n testing \n testing')
f.writelines(sentences_list)
finally:
f.close() #need to close the file to flush the content
with open('wasteland.txt', mode='wt', encoding='utf-8') as f:
f.write('what are the roots that ...\n testing \n testing')
#read from file
g = open('wasteland.txt', mode='rt', encoding='utf-8')
g.read(32) #read only 32 characters
g.read() #read all content, if we call it again, it will give '' as all content are read
g.seek(0) #reset to point to start of the file
g.readline() #will have \n at the end if it is not the last line
g.readlines()
g.close()
Language Specifics
- Omitted return parameter (just return keyword) or implicit return at the end of function, returns None.
- No variables concept, they are named references to objects.
- Python is a dynamic type, but strong type system. (No implicit type conversion e.g. "answer is " + 3 will give type error. Except bool implicit conversion in if or condition statements.)
- Everything in Python is object, including functions, modules, class .... dir() can be used to introspect an object and get its attributes. Metaclass e.g. type can be regarded as class of class object.