Python Programming Language
Language
print
…is a build in function in Python 3
print('s')              # s
print('s',end='')       # disable newline
print(1,2,3)            # 1 2 3
print(1,2,3,sep=',')    # 1,2,3
print('s','r',sep='/')  # s/r
print([1,2,3])          # [1, 2, 3]
print(*[1,2,3])         # 1 2 3 Variables
The assignment operation name = object references values to variables:
- Assignment declares and initializes a variable with a value
- Most suitable data type for assignment is select automatically by the interpreter
- A cascading assignment modifies an object referenced by a variable
x = 1.2 + 8     # assign right expression
x = y = z = 0   # assign a single value to multiple variables
x,y,z = 1,2,3   # multiple assignments
x,y = y,x       # variable swap
a,*b = [1,2,3]  # unpacking a sequence
del x           # remove variable
x = None        # undefined value
id(x)           # return object memory address for variable x
hex(id(x))      # ...hexadecimal memory address
globals()       # dictionary of global variablesNaming conventions:
var_with_sep    # underscore separated downcase letters
_               # unused variable (i.e. within a loop)
var             # public
_var            # internal
var_            # convention to avoid conflict keyword
__var           # private use in class
_var_           # protected use in class
__var__         # "magic: method or attribute- Names are case sensitive
- Python keywords can not be used as names
Scope and binding:
- Variables are always assigned to local scope current code block (i.e. within a function body)
- nonlocalassigns to variables in an outer scope (but not global)
- globalassigns to a variable in the module’s top level aka global scope
globals().keys()         # prints all variable names in global scope
locals().keys()          # prints all variable names in local scopeNumbers
| Type | Description | 
|---|---|
| int | Integer number are of arbitrary size | 
| float | Floating point number precision depends on the implementation and system architecture | 
| complex | Complex number | 
Literals
123             # integer
-123            # negative signed integer
9.23            # float 
-1.7e-6         # scientific notation
2 + 1j          # complex number
0b010           # 2         binary notation
0o642           # 418       octal
0xf3            # 243       hexadecimal
None            # indefined valueTypes, and type-casting:
type(1)                  # <class 'int'>
type(1.2)                # <class 'float'>
type(None)               # <class 'NoneType'>
## string to numerical
int('123')               # 123
float('1.23')            # 1.23
## Type check ##
isinstance(10,int)       # True
isinstance(1.234,float)  # TrueArithmetic
1+2                       # 3
1-2                       # -1
1*2                       # 2
1/2                       # 0.5
5%3                       # 2 (remainder)
2**3                      # 8
## Built-in functions ##
abs(-2.3)                 # 2.3
round(1.6666,2)           # 1.67
pow(2,3)                  # 8
sum((1,2,3,4,5))          # 15
max([1,2,3,4,5])          # 5
min([1,2,3,4,5])          # 1Logic
## Booleans
True               # logic true
False              # logic fales
not True           # False
True and True      # True
True and False     # False
True or False      # True
False or False     # False
1                  # True
0                  # False
## Comparison with boolean results
1 < 2              # True
1 > 2              # False
1 <= 1             # True
2 >= 3             # False
1 == 1             # True
1 != 1             # False
## Ternary conditional statement
1 if True else 2   # 1
1 if False else 2  # 2Strings
Escape sequences interpreted according to rules similar to those used by Standard C
''                                 # empty line
'\n'                               # blank line
# Double quote (escape with \)
"a\"bc"                            # 'a"bc'
# Single quote
'a\'bc'                            # "a'bc"
## Raw strings ##
r"\t\n\\"                          # '\\t\\n\\\\'
R"\"\n\""                          # '\\"\\n\\"'Raw-string prefixed with r or R use different rules for backslash escape sequences
# Built-in function to get ASCII codes
ord('a')                           # 97
# convert to sequence pr collection types
list('abc')                        # ['a', 'b', 'c']
set('abc')                         # {'b', 'a', 'c'}
tuple('abc')                       # ('a', 'b', 'c')Escape sequences:
\\         Backslash (\)
\'         Single-quote (')
\"         Double-quote (")
\a         ASCII bell (BEL)
\b         ASCII backspace (BS)
\f         ASCII formfeed (FF)
\n         ASCII linefeed (LF)
\N{name}   Character named name in the Unicode database (Unicode only)
\r         ASCII carriage return (CR)
\t         ASCII horizontal tab (TAB)
\uxxxx     Character with 16- bit hex value xxxx (Unicode only)
\Uxxxxxxxx Character with 32- bit hex value xxxxxxxx (Unicode only)
\v         ASCII vertical tab (VT)
\ooo       Character with octal value oo
\xhh       Character with hex value hhFormat
Legacy format operator…
"%s, %s" % ('s','t')                           # 's, t'Global build in function
format(10.0,"7.3g")                            # '     10'Use of the str.format() function…
"{}|{}".format(1,2)                            # '1|2'
# ...positional index
"{1},{0},{2}".format('r','s','t')              # 's,r,t'
# ...parameter names
"{b}{a}".format(a='s',b='t')                   # 'ts'
# ...nested data structures
'{d[2]},{d[0]}'.format(d=['r','s','t'])        # 't,r'
'{d[b]},{d[a]}'.format(d={'a':1,'b':2})        # '2,1'Padding…
"{:4d}".format(123)                            # ' 123'
'{:06.2f}'.format(3.14159)                     # '003.14'
"{:>3}".format('s')                            # '  s'
"{:.<4}".format('s')                           # 's...'
"{:^5}".format('s')                            # '  s  '
# ...parametrized format
'{:{a}{w}}'.format('s',a='>',w=5)              # '    s'
# ...positional arguments
'{:{}{}{}.{}}'.format(2.7182,'>','+',10,3)     # '     +2.72'Since Python 3.6 f-strings…
- …formatted string literals …less verbose then str.format()
- …fas string literal prefix …{}curly braces containing expressions …will be replaced with their values
- …evaluated at runtime …allow any valid Python expressions
v = 's'
f'text{v}'                                     # 'texts'
f"{1 + 1}"                                     # '2'
F'{v.upper()}'                                 # 'S'Multi line f-strings…
# ...place an f in front of each line
v = f'line' \
    f'line'
# ...easier...
f"""
   multi lines text
"""Manipulation
# ...concatenation
"s" + "t"                          # 'st'
"s " + str(123)                    # 's 123'
# ...leading, trailing white-space management 
" s ".strip()                      # 's'
' s \n'.rstrip()                   # ' s'
' s'.lstrip()                      # 's'
# ...cut by separator
"s\nt\nr\n".splitlines()           # ['s', 't', 'r']
"s|t".split("|")                   # ['s', 't']
'p:q:r:s'.rsplit(':',2)            # ['p:q', 'r', 's']
# ...return tuple, preserve the delimiter
"s:r:t".partition(':')             # ('s', ':', 'r:t')
"s:r:t".rpartition(':')            # ('s:r', ':', 't')
# ...join by separator
':'.join('123')                    # '1:2:3'
# ...matching
't' in 'str'                       # True
"st".startswith('s')               # True
'str'.endswith('r')                # True
'   '.isspace()                    # True
'12'.isdecimal()                   # True
'1.2'.isdecimal()                  # False
'strts'.find('r')                  # 2
# ...replacement
'srtr'.replace('r','R')            # 'sRtR'
'srtr'.replace('r','R',1)          # 'sRtr'Control & Loop Constructs
Block indentation:
- Blocks of code begin with a colon (:) and contain indented lines below (no ending identifier)
- Always use 4 spaces for indentation
- Single line statements may be put on the same line (considered bad style)
- Empty blocks cause an IndentationError, use pass(a command that does nothing)`
Conditional Statements
- Can go with multiple eliffollowed by a singleelse
- First true condition is executed
if x < 2:
    ...
elif x > 2:
    ...
else:
    ...Python has no direct analogous to a switch-case statement.
def switch(case):
    return {
        'a': 'A',
        'b': 'B'
    }.get(case,None)
print(switch('a'))                   # Adef a():
    return 'A'
def b():
    return 'B'
switch = { 'a': a(), 'b': b() }
print(switch['a'])                   # ALoops
Code blocks repeated through a number of loops (iterations):
- The breakstatement executed within a loop breaks the control flow out of the loop immediatly
- A continuestatement skips to the next interation of a loop
A while loop executed a code block until the loop condition is false:
x = 0
while True:                    # endless loop
    print(x)
    if x == 3:                 # break condition
        break                  # break the loop
    x += 1                     # incrementfor loops iterate over a collection of items, and execute a code block for each element:
for x in (0,1,2,3):
    if x == 2:                 # skip condition
        continue               # skip the rest of the code block
    print(x)range is a function returning a series of numbers in an iterable from, commenly use with for loops:
for x in range(3):
    print(x)
else:
    print('no break')Loops can optionally have an else clause executed if the iteration is completed successfull.
# couple of range examples
list(range(5))           # [0, 1, 2, 3, 4]
tuple(range(4,12))       # (4, 5, 6, 7, 8, 9, 10, 11)
tuple(range(0,10,2))     # (0, 2, 4, 6, 8)
tuple(range(100,0,-10))  # (100, 90, 80, 70, 60, 50, 40, 30, 20, 10)Sequences & Collections
Tuple
A tuple is a immutable sequences of elements with an index number and a value.
# literals for tuples
()
(1,2,3)
('a','b','c')
type((1,2))             # tuple
# assign tuple to a variable t
t = ('a','b','c')
t                       # ('a', 'b', 'c')
# does a value exists in a tuple t 
'a' in t                # True
'a' not in t            # False
# access elements in a tuple t
t[0]                    # 'a'
t[-2]                   # 'b'
t[:2]                   # ('a', 'b')
t[2:]                   # ('c',)
# find index of value
t.index('b')            # 1
# number of occurrences of a given value
t.count('b')            # 1
# concatenation return a new tuple
(1, 2, 3) + (4, 5, 6)   # (1, 2, 3, 4, 5, 6)
# number of elements in a tuple
len(('a','b'))          # 2
# sort a tuple
sorted(('b','a'))       # ['a', 'b']
# smallest/largest value in tuple
min(34,23,45)           # 23'
max(34,23,45)           # 45
# convert a string to tuple
tuple('abcd')           # ('a', 'b', 'c', 'd')
# convert a list into a tuple
tuple([1,2,3,4,5])      # (1, 2, 3, 4, 5)List
A list is a sequences (with dynamic length) of elements with an index number and a value.
type([1,'a',2])         # list
[]                      # empty list
l = [1,2,'a',3,'b']     # list assignment to variable
# append value to the end of the list list
l.append(4)             
# append multiple values to list
l.extend([5,'c'])
# print a list
l                       # [1, 2, 'a', 3, 'b', 4, 5, 'c']
# does a value exists in a list
'a' in l                # True
# find index of a value
l.index('a')            # 2
# number of elements in list
len(l)                  # 8
# access values by index
l[:]                    # [1, 2, 'a', 3, 'b', 4, 5, 'c']
l[:4]                   # [1, 2, 'a', 3] 
l[-4]                   # 'b'
l[4:-1]                 # ['b', 4, 5]
# delete element by value 
l.remove(3)             
l                       # [1, 2, 'a', 'b', 4, 5, 'c']
# remove index from list and return its value
l.pop(-1)               # 'c' 
l                       # [1, 2, 'a', 'b', 4, 5]
# concatenation return a new list
l + [6,7,'c']           # [1, 2, 'a', 'b', 4, 5, 6, 7, 'c']
l                       # [1, 2, 'a', 'b', 4, 5]
# delete element by index
del l[3]
l                       # [1, 2, 'a', 4, 5]
# number of occurrences of a given value
l.count('a')            # 1
# shallow copy of the list
m = l.copy()
m[0] = 'z'
l                       # [1, 2, 'a', 4, 5]
m                       # ['z', 2, 'a', 4, 5]
# iteration
for i in l: print(l.index(i),i)
# convert a string into a list
list("abcde")           # ['a', 'b', 'c', 'd', 'e']
# convert tuple into a list
list((1,2,3,4,5,6))     # [1, 2, 3, 4, 5, 6]Set
A set is a unordered collections of unique elements:
{}
{1,2,'a'}               # {1, 2, 'a'}
{1,2,1,2,1}             # {1, 2}
type({1,2})             # set
# union
{1,2} | {2,3,4}         # {1, 2, 3, 4}
# intersection
{1,2} & {2,3,4}         # {2}
# difference
{1,2} - {2,3,4}         # {1}
# symmetric difference
{1,2} ^ {2,3,4}         # {1, 3, 4}
# does a value exists in set
1 in {1,2,3}            # True
'a' in {1,2,3}          # False
# iterator
for v in {1,2,3}:Dictionary
A dictionary is a associative list with defined keys and values.
{}
{1:'a','b':2}          # {1: 'a', 'b': 2} 
# add/change key/value
d = {}
d['a'] = 1             
d['b'] = 2             
d                      # {'a': 1, 'b': 2}
d['b']                 # 2
# remove element by key
del d['a']
d                      # {'b': 2}
# merge 
d.update({'c':3, 'd':4})
d                      # {'b': 2, 'c': 3, 'd': 4}
# remove element, return value
d.pop('c')             # 3
d                      # {'b': 2, 'd': 4}
# get a value
d.get('b')             # 2
d                      # {'b': 2, 'd': 4}
# remove all elements
d.clear()
d                      # {}
# iterators
for k in d.keys()
for v in d.values()
for k,v in d.items()
# conversion
dict(a=1,b=2,c=3)                  # {'a': 1, 'b': 2, 'c': 3}
dict(zip(['a','b'],[1,2]))         # {'a': 1, 'b': 2}Subprocess
Simplest method to execute a command and iterate over the output by line…
import subprocess
text = subprocess.getoutput('ls -l')
for line in text.splitlines():
    # ...File I/O
Use open() to store data in a file and read it back:
- The path to the file is the first argument.
- Followed by the access mode: r(read),w(write),a(append)
- Encoding: ‘ascii’, ‘utf8’
txt = "1st line\n2nd line\n3rd line\n4th line"
path = '/tmp/file.txt'
## write into a file
f = open(path,'w',encoding='utf8')
f.write(txt)                   # write into the file
f.flush()                      # write cache
f.close()                      # close when finished
f = open(path,'r')
f.name                         # path ot the file '/tmp/file.txt'
f.read()                       # read entire file
# iterate over file content
for l in f.readlines()         # by line
for l in iter(f): 
for l in f.read().split('\n')  # by seperatorUsing a context manager:
with open('/etc/hosts') as f:
    for _ in f.readlines():
        print(_)Modules
A module is a files containing Python definitions and statements.
dir(__builtins__)        # list build-in functions
help(type)               # help text for a given modul/functionImport a module to use its functions:
import math
math.pi          # 3.141592653589793
math.sqrt(81)    # 9.0Load module, and allow direct access to functions:
from math import pi,e,sin,log
sin(pi/4)       # 0.7071067811865475
log(e**2)       # 2.0Define an alias for a module:
import math as m
m.pi           # 3.141592653589793Functions
Functions are defined using the def keyword
- Followed by the function name (identifier), i.e. f
- Arguments given between parentheses followed by :(colon)
- The function body (blocks) must be indented
- The returnkeyword passes values from the function
# includes argument with default value
def f(x,y,z=3):
    """documentation"""
    return (x,y,z)
f(1,2)                        # (1, 2, 3)
# variable positional arguments as tuple
def g(x,*y):
    return (x,y)
g(1,2,3,4,5,6)                # (1, (2, 3, 4, 5, 6))
# variable named arguments as dict
def h(x,**y):
    return [x,y]
h(1,a=1,b=2)                  # [1, {'a': 1, 'b': 2}]Lambda
The Lambda expression (anonymous function) creates a function objects with following notation:
lambda: <<args,...>> : <<expression>>Semantically lambda is a shorthand for a function definition:
- Lambda functions can be used wherever function objects are required.
- It can have any number of arguments before the colon.
- The function body is syntactically restricted to a single expression.
- Typically used as nameless function as argument to a higher-order function.
f = lambda x,y : x+y
f(1,1)                        # 2
f = lambda x: x**2 + 2*x - 5
f(2)                          # 3
# Fahrenheit to Celsius conversion
f2c = lambda c: float('{:.2f}'.format((5.0 / 9) * ( c - 32 )))
f2c(32)                       # 0Lambda functions are used along with build-in function like map(), or filter().
Map
The map(<<func>>,<<sequence>>) function applies a function to every item in an sequence. It returns a list containing all the function call results.
def sqr(x): return x ** 2
list(map(sqr, [1, 2, 3, 4, 5]))                            # [1, 4, 9, 16, 25]
# with a lambda expression
list(map(lambda x: x+1, [1,2,3,4,5,6]))                    # [2, 3, 4, 5, 6, 7]
list(map(lambda x: x**2, range(0,12,2)))                   # [0, 4, 16, 36, 64, 100]Filter
The filter(<<func>>,<<sequence>>) function extracts each element in a sequence for which a function returns True.
list(filter(lambda x: x<0,range(-5,5)))                     # [-5, -4, -3, -2, -1]
list(filter(lambda x: (x%2==0), [1,5,4,6,8,11,3,12]))       # [4, 6, 8, 12]
## intersection
a,b = [1,2,3,5,7,9],[2,3,5,6,7,8]
list(filter(lambda x: x in a,b))                            # [2, 3, 5, 7]Reduce
The reduce() function reduces a sequence to a single value by combining all elements via a defined function.
reduce(<<func>>,<<sequence>>[,<<initializer>>])By default, the first item in the sequence initialized the starting value.
from functools import reduce
reduce(lambda x,y: x+y, [1,2,3,4])                          # 10
reduce(lambda x,y: x*y, [2,3],2)                            # 12
import operator
reduce(operator.sub,[50,3,4,6])                             # 37
## flatten a list
reduce(list.__add__, [[1, 2, 3], [4, 5], [6, 7, 8]], [])    # [1, 2, 3, 4, 5, 6, 7, 8]
## union of a list of sets
reduce(operator.or_, ({1},{1,2},{1,3}))                     # {1, 2, 3}
## intersection of a list of sets
reduce(operator.and_, ({1},{1,2},{1,3}))                    # {1}Classes
Classes, instances, and data attributes:
- A class is defined with the keyword classfollowed by a name (capitalized) and colon.
- Class instantiation uses function notation to assign a class object (instance”) to a variable.
- Class attributes are referenced with the dot notation <object>.<attribute>.
- Object data attributes (instance variables) need not be declared, they are assigned on first used.
class Human():      # define a class called Human
    pass            # Use pass for a class without attributes/methods
# create two instances of the class
alice = Human()
bob = Human()
# set data attributes of both instances
alice.age = 25
bob.age = 31
# print instance attributes
print(bob.age,alice.age) # 31 25Class Constructor & Instance Methods
- Methods automatically pass a class object self(by convention) as first argument.
- The method __init__()(the constructor) is automatically invoked on newly-created class instances.
- Instance objects can use attribute references to data attributes and methods.
class Human():
    # constructor
    def __init__(self, name, age):
        self.name = name
        self.age = age
    # method
    def who(self):
        return '{} age {}'.format(self.name, self.age)
# Iterate over two class objects
for _ in (Human('alice',25),Human('bob',31)):
    # call the method of an object
    print(_.who())
    # pass an object to a method
    print(Human.who(_))Class Variables & Class Methods
Class variables:
- Shared among all instances of a class
- Accessible as <class>.<attribute>or as<object>.<attribute>
Class methods:
- Declared with a decorator @classmethod
- Automatically pass a class as first argument called cls(by convention)
- Typically use to build alternative constructors
class Human():
    num = 0 # define a class variable
    # constructor
    def __init__(self, name, age):
        self.name = name
        self.age = age
        # increment the number of humans
        Human.num += 1
    # decorator to identify a class method
    @classmethod
    def from_str(cls, string):
        name, age =  string.split(':')
        return Human(name,age)
    # method
    def who(self):
        # use self to access a class variable
        return '{} age {} [of {}]'.format(self.name, self.age, self.num)
humans = [
  Human('alice',25),
  Human('bob',31),
  Human.from_str('joe:19')
]
print(humans[2].who())       # joe age 19 [of 3]Class Properties
A method used to get a value is decorated with @property before its definition.
A method used to set a value is decorated with @<<name>>.setter before its definition.
class C:
    def __init__(self,v):
        self._v = v
    # getter
    @property
    def v(self):
        return self._v
    # setter
    @v.setter
    def v(self,__):
        self._v = __
c = C(123)
c.v = 321         # set a value
print(c.v)        # get a valueCommand-Line
Use following libraries:
- argparse to parse command-line options, arguments and sub-commands
- logging for multi-level application logging
Runtime services in the sys library:
import sys
sys.argv                        # list of command line arguments
sys.stdout.write(s)             # write string s to standard output
sys.stderr.write(s)             # write string s to standard error
sys.exit(i)                     # exit with error code iPython modules executable as main program also:
- Source files executed as the main program have the variable __name__set to__main__
- Call an optional main()function if not loaded by animport <module>
def main():
    pass
if __name__ == '__main__':
    main()Get input from the user with the input function:
- The argument will be printed followed by a prompt to wait for user input
- The function returns the input provided by the user
- Note that the return value is always of type str
input('Give me a string: ')
int(input('Give me a number'))Consume data from the input pipe STDIN:
# read input from STDIN
if not sys.stdin.isatty():
     stdin = io.StringIO(sys.stdin.read())
else:
     raise Exception('No input data specified, STDIN is empty')venv
- …creation of virtual environments
- …requires Python 3.3 or newer.
- …a virtual environment includes:
- …isolates Python (interpreter,libraries) into a dedicated directory
- …easy_installandpipwork es expected
 
python -m venv $path          # create new virtual environment
source $path/bin/activate     # activate environment
deactivate                    # exit virtual environmentconda
- …package manager for Python
- …open-source …BSD-3 License …hosted on github.com/conda
- Partly owned and controlled by Anaconda Inc…
- …Anaconda is a commercial distribution of Python …not free and not open-source
- …note that anaconda channel is prohibiting for commercial usage
 
conda info                                          # basic information
conda info --envs                                   # display a list of all environments
conda search --full-name python                     # list available Python versions
conda create --name <env> python=<ver> [<pkg>]      # install global environment
conda create --prefix ~/<path> python=<ver> [<pkg>] # install use specific version
source activate <env>                               # load global environment
source activate ~/<path>                            # load user specific environment
source deactivate                                   # unload environment
conda remove --name <env> --all                     # delete an environmentManaging packages…
- …make sure to only use free and open-source Conda package channels
- …check the active channels by running conda config --show channels
- …use ~/.condarcto configure the channels to use
conda list                                      # list packages
conda search $pkg                               # search for a package
conda install $pkg                              # install into current environment
conda update $pkg                               # update package in current environment
conda remove $pkg                               # delete package fro current environment- …free minimal installer for conda
- …includes only conda and Python
ipython
Interactive Python
Keyboard shortcuts:
ctrl-l                 clear terminal
ctrl-c                 interrupt command
ctrl-d                 exit session
ctrl-r                 search command history
ctrl-a                 cursor to the beginning of the line
ctrl-e                 cursor to the end of the linr
ctrl-k                 cut text from cursor to end of line
ctrl-u                 cut text from beginning of line to cursor
ctrl-y                 yank (paste) text cut beforeAccess Documentation
Access the documentation with the build-in help(<obj>[.<method>]) function.
Alternatively use the short hand <obj>[.<method>]? with a question mark:
In [1]: tuple?
Init signature: tuple(self, /, *args, **kwargs)
Docstring:
tuple() -> empty tuple
tuple(iterable) -> tuple initialized from iterable's items
If the argument is a tuple, the return value is the same object.
Type:           type
In [2]: list.index?
Docstring:
L.index(value, [start, [stop]]) -> integer -- return first index of value.
Raises ValueError if the value is not present.
Type:      method_descriptorIt support objects methods, and includes user defined code.
In [3]: def square(a):
   ...:     """Return square of argument."""
   ...:     return a**2
   ...:
In [4]: square?
Signature: square(a)
Docstring: Return square of argument.
Type:      function
In [5]: square??
Signature: square(a)
Source:
def square(a):
    """Return square of argument."""
    return a**2
Type:      functionDisplay source code with the short hand <obj>[.<method>]?? (two questions marks), unless it is a build-in implemented in C.
Help support wild card completion?
In [6]: C*Error*?
ChildProcessError
ConnectionAbortedError
ConnectionError
ConnectionRefusedError
ConnectionResetError
In [7]: dict.*__g*?
dict.__ge__
dict.__getattribute__
dict.__getitem__
dict.__gt__Magic Commands
The %lsmagic command lists all magic commands:
- Single magic commands are prefixed with % (percent)
- Multi-line expressions start with %% (double percent)
- Prefix the magic command with ? (question mark) to see the help text
In [1]: ?%lsmagic
Docstring: List currently available magic functions.
File:      /usr/lib/python3/dist-packages/IPython/core/magics/basic.py
In [2]: %cat square.py
def square(a):
    """Return square of argument."""
    return a**2
print(square(2))
print(square(10))
In [2]: %run square.py
4
100General description of magic functions is available with %magic.
Shell Commands
Shell commands are prefixed with !, which can be omitted if auto-magic is on:
In [1]: %automagic 1
Automagic is ON, % prefix IS NOT needed for line magics.
In [2]: ls
ipython.md  jupyter.ipynb  mathplot.ipynb  numpy.md  README.mdStore output of a shell command into a variables by assignment. (Here the exclamation mark is required)
In [3]: files = !ls
In [4]: print(files)
['ipython.md', 'jupyter.ipynb', 'mathplot.ipynb', 'numpy.md', 'README.md']
In [5]: s = "cheers"
In [6]: !echo {s}Interpolate the contents of a variable with {<var>}.
Subshell started with ! are non-interactive non-login instance of the user’s default shell:
In [1]: !echo $SHELL
/bin/zsh
In [2]: !ps -p $$ && :
  PID TTY          TIME CMD
  19295 pts/5    00:00:00 zshZSH user need to load their custom environment with ~/.zshenv
Library
Numpy
The Nympy library adds support for large homogeneous multi-dimensional arrays and matrices, along with a large collection of high-level mathematical functions to operate on these arrays.
- Homogeneously typed (all elements have the same type).
- Numpy ndarrayclass for n-dimensional array is a more efficient implementation of a python list.
- Numerical operations with ndarray run on full compiled code speed.
# recommended convention to import numpy
import numpy as np                 # with abbr. npCreate
Dimensions are called axes, and the number of axes is rank
# crate an ndarray using a tuple or list
np.array((1,2,3,4,5))
np.array([1,2,3,4,5])                         # [1 2 3 4 5]
# explicit data type
np.array((1.1,2,3))                           # [ 1.1  2.   3. ]
np.array((1,2),float)                         # [ 1.  2.]
np.array([1,2],dtype=complex)                 # [ 1.+0.j  2.+0.j]
# nested tuples/lists result in multi-dimensional arrays
np.array(([1,2,3],[4,5,6],[7,8,9]))           # [[1 2 3] [4 5 6] [7 8 9]]
np.array([range(i, i+3) for i in [1,2,3]])    # [[1 2 3] [2 3 4] [3 4 5]]
# initial with placeholder content
np.zeros((3,2))                      # [[ 0.  0.] [ 0.  0.] [ 0.  0.]]
np.ones((2,2,2))                     # [[[ 1.  1.] [ 1.  1.]] [[ 1.  1.] [ 1.  1.]]]
np.full((2,2),5)                     # [[5 5] [5 5]]
np.diag(np.array([1, 2, 3]))         # [[1 0 0] [0 2 0] [0 0 3]]
# create sequences of numbers with args. [start,]stop[,step] (cf. range)
np.arange(5)                         # [0 1 2 3 4]
np.arange(5,10)                      # [5 6 7 8 9]
np.arange(0,30,10)                   # [0 10 20]
# linearly spaced sequence
np.linspace(2,3,5)                   # [2. 2.25 2.5 2.75 3.]
# log spaced sequence 
np.logspace(0,1,5)                   # [1. 1.77827941 3.16227766 5.62341325 10. ]Random values:
# float values between 0 and 1 in defined dimension
np.random.rand(1,3)                  # [0.19544155  0.389351  0.09039669]
np.random.rand(2,2)                  # [[ 0.62209577  0.55083187] [ 0.31431768  0.98404029]]
# 5 integers between 0 and 10
np.random.randint(0,10,5)            # [7 2 9 4 8]
# in defined dimension
np.random.randint(0,10,(2,3))        # [[6 3 6] [8 2 7]]Shape
# print dimensions
a = np.array(((1,2),(3,4)))
a.ndim                               # 2
a,size                               # 4
a.shape                              # (2, 2)
a.dtype                              # dtype('int64')
# size in bytes per element
a.itemsize                           # 8
# size of all elements (size x itemsize)
a.nbytes                             # 32 
# flatten
np.array(((1,2),(3,4))).flatten()
np.array(((1,2),(3,4))).ravel()      # [1 2 3 4]
# change dimensions
np.arange(6).reshape(2,3)            # [[0 1 2] [3 4 5]]
# transpose
np.zeros((2,3)).T                    # [[ 0.  0.] [ 0.  0.] [ 0.  0.]]
np.zeros((2,3)).transpose()
## stacking together
# vertical
np.vstack((np.zeros((2,2)),np.ones((2,2))))       # [[ 0.  0.] [ 0.  0.] [ 1.  1.] [ 1.  1.]]
# horizontal
np.hstack((np.zeros((2,2)),np.ones((2,2))))       # [[ 0.  0.  1.  1.] [ 0.  0.  1.  1.]]
np.hsplit(np.arange(10),2)                        # [[0 1 2 3 4] [5 6 7 8 9]]Operations
np.array((10,20,30)) + np.array((1,2,3))                # [11 22 33]
np.array((10,20,30)) - np.array((1,2,3))                # [ 9 18 27]
np.array((2,4,8)) ** 2                                  # [ 4 16 64]                                             
np.array((2,4,8)) >= 4                                  # [False  True  True]
np.array((2,2,2)).sum()                                 # 6
np.array(((1,2),(4,5))).sum(axis=1)                     # [3 9]
np.array((1,2,3)).min()                                 # 1
np.array((1,2,3)).max()                                 # 3
np.array(((1,2),(4,5))).cumsum(axis=1)                  # [[1 3] [4 9]]
# vector products
np.array([1,2]).dot(np.array([3, 4]))                   # 11
# multiply a vector by a matrix
np.array([1,2]).dot(np.array([[3,4],[5,6]]))            # [13 16]
# multiply matrices
np.array([[1,2],[3,4]]).dot(np.array([[5,6],[7,8]]))    # [[19 22] [43 50]]Indexing, Slicing and Iterating
a = np.array([1,2,3,4])            
# access elements in array a
a[3]                               # 4
a[-2]                              # 3 
a[:2]                              # [1 2] last index not included!
a[3] = 5                           # [1 2 3 5]
## multiple dimensions
a = np.array([[1,2,3],[4,5,6]], float)
# ':' all elements in dimension
a[1,:]                             # [ 4.  5.  6.]
a[:,2]                             # [ 3.  6.]
a = np.array([[1,2,3,4], [5,6,7,8], [9,10,11,12]])
a[:2,1:3]                          # [[2 3] [6 7]]
# reversing a sequence
np.arange(10)[::-1]                # [9 8 7 6 5 4 3 2 1 0]
# slice sequences [start:end:step]
np.arange(10)[2:9:3]               # [2 5 8]
np.arange(10)[::3]                 # [0 3 6 9]
# ... (dots) represent as many colons as needed to produce a complete indexing tuple
np.arange(12).reshape(3,4)[1,...]  # [4 5 6 7]
np.arange(12).reshape(3,4)[...,2]  # [2 6 10]Slicing operation creates a view on the original array.
a = np.arange(10)                  # [0 1 2 3 4 5 6 7 8 9] 
b = a[::2]                         # [0 2 4 6 8]
np.may_share_memory(a,b)           # True
c = b.copy() # force a copy
np.may_share_memory(a,c)           # FalseIO
Store data into a file:
# write a binary file
>>> np.array([1,2,3],float).tofile('f.bin')
>>> !file f.bin
f.bin: dBase III DBT, next free block index 1
>>> print(np.fromfile('f.bin'))
[ 1.  2.  3.]
# write a clear text file
>>> np.savetxt('f.txt',np.array((1,2,3)))
>>> !cat f.txt
1.000000000000000000e+00
2.000000000000000000e+00
3.000000000000000000e+00
>>> print(np.loadtxt('f.txt'))
[ 1.  2.  3.]Embedded Python
What is MicroPython?
- Reimplementation of Python 3 for MCUs
- Efficient with resources, runs on bare metal
- Written in C++, includes compiler, run-time
- REPL (read, evaluate, print loop)
 
- Compiler emitters…
- Byte code for a virtual machine
- Native machine code (x86, x64, ARM…)
- Supports inline assembler
 
Micro:bit uses a pre-compiles run-time…
- Runtime .hexflashed to the micro:bit
- .hexcontains complete MicroPython language
CicruitPython open source derivative of MicroPython
Install uflash:
sudo apt install -y python3-pip
pip3 install uflash
# mount the device
pmount /dev/sdb MICROBIT
uflash $sourceReferences
- CircuitPython, Adafruit
- MicroPython
- MicroFS micro:bit command-line tool
- uFlash - Flash Python onto the BBC’s micro:bit device
Editors & IDEs…
- Mu Python Editor
- Micro:bit Web Python Editor
Workshops & Tutorials…
- BBC micro:bit MicroPython documentation
- Networking with the micro:bit Python Edition
- UCL’s BBC Micro:bit Tutorials
- Micro:bit Lessons - Introduction to cryptography
- Conway’s Game of Life
- BBC micro:bit – Tetris Game
- micro:bit Space Invaders
References
Lectures…
- CS50P, CS50’s Introduction to Programming with Python, Harvard
Books…
- 100 Page Python Intro
- Learn Python with Jupyter