CT320: Network and System Administration

Fall 2019

Python

Show Lecture.Python as a slide show.

CT320 Python

Adapted from https://docs.python.org/3.4/tutorial/

Versions

Invoking Python

$ python -V
/tmp/pmwiki-bash-scriptChJKJ9: line 10: python: command not found

$ python2 -V
Python 2.7.18

$ python3 -V
Python 3.6.8

$ ls -l /usr/bin/python
/bin/ls: cannot access '/usr/bin/python': No such file or directory

Help

The pydoc3 program tells you about functions:

$ pydoc3 abs
Help on built-in function abs in module builtins:

abs(x, /)
    Return the absolute value of the argument.

Help

pydoc3 can also tell you about control flow:

$ pydoc3 for
The "for" statement
*******************

The "for" statement is used to iterate over the elements of a sequence
(such as a string, tuple or list) or other iterable object:

   for_stmt ::= "for" target_list "in" expression_list ":" suite
                ["else" ":" suite]

The expression list is evaluated once; it should yield an iterable
object.  An iterator is created for the result of the
"expression_list".  The suite is then executed once for each item
provided by the iterator, in the order returned by the iterator.  Each
item in turn is assigned to the target list using the standard rules
for assignments (see Assignment statements), and then the suite is
executed.  When the items are exhausted (which is immediately when the
sequence is empty or an iterator raises a "StopIteration" exception),
the suite in the "else" clause, if present, is executed, and the loop
terminates.

A "break" statement executed in the first suite terminates the loop
without executing the "else" clause’s suite.  A "continue" statement
executed in the first suite skips the rest of the suite and continues
with the next item, or with the "else" clause if there is no next
item.

The for-loop makes assignments to the variables(s) in the target list.
This overwrites all previous assignments to those variables including
those made in the suite of the for-loop:

   for i in range(10):
       print(i)
       i = 5             # this will not affect the for-loop
                         # because i will be overwritten with the next
                         # index in the range

Names in the target list are not deleted when the loop is finished,
but if the sequence is empty, they will not have been assigned to at
all by the loop.  Hint: the built-in function "range()" returns an
iterator of integers suitable to emulate the effect of Pascal’s "for i
:= a to b do"; e.g., "list(range(3))" returns the list "[0, 1, 2]".

Note: There is a subtlety when the sequence is being modified by the
  loop (this can only occur for mutable sequences, e.g. lists).  An
  internal counter is used to keep track of which item is used next,
  and this is incremented on each iteration.  When this counter has
  reached the length of the sequence the loop terminates.  This means
  that if the suite deletes the current (or a previous) item from the
  sequence, the next item will be skipped (since it gets the index of
  the current item which has already been treated).  Likewise, if the
  suite inserts an item in the sequence before the current item, the
  current item will be treated again the next time through the loop.
  This can lead to nasty bugs that can be avoided by making a
  temporary copy using a slice of the whole sequence, e.g.,

     for x in a[:]:
         if x < 0: a.remove(x)

Related help topics: break, continue, while

Interpretive mode

python3, with no arguments, starts in a “calculator” mode:

    $ python3
    >>> 2+2
    4
    >>> a=10
    >>> from math import sqrt
    >>> sqrt(a)
    3.1622776601683795
    >>> control-D to exit
    $

The previous lines can be edited using the arrow keys.

One-liner

To execute a single line of Python, use -c:

$ python3 -c 'import math; print(math.atan(1)*4)'
3.141592653589793

$ python3 -c 'print(2**200)'
1606938044258990275541962092341162602522202993782792835301376

$ python3 -c 'print(1<<200)'
1606938044258990275541962092341162602522202993782792835301376

$ python3 -c 'print(0x100000000000000000000000000000000000000000000000000)'
1606938044258990275541962092341162602522202993782792835301376

$ python3 -c 'print(1606938044258990275541962092341162602522202993782792835301376)'
1606938044258990275541962092341162602522202993782792835301376

Script

A Python 3 script starts with: #! /usr/bin/python3 -Wd

#! /usr/bin/python3 -Wd
print("Hello, world!")
Hello, world!

The -Wd enables warning messages. Warnings are your friends:

#! /usr/bin/python3 -Wd
handle = open('/etc/group', 'r')
sys:1: ResourceWarning: unclosed file <_io.TextIOWrapper name='/etc/group' mode='r' encoding='UTF-8'>

Names Do Matter

Some people name their Python scripts whatever.py, and execute them like this:

    python3 foo.py

Why? The user doesn’t care that your program is a Python script. Think of object encapsulation—don’t unnecessarily expose implementation details. A significant implementation detail is the implementation language.

If you’ve exposed the implementation language, like this, then you’re no longer free to change it.

Data Type Overview

NameExamplesNotes
BooleanTrue, Falsecapitalized
Number1, 3.4, 4.5e8, 2**1000big big big
String"Don't" 'believe' r'\all/' """you   
read!"""
immutable
List[1,2.3,"fruit",[45,5]]mutable, heterogeneous
Tuple(23, "skidoo"), ('Jack', 59.99, "970-555-1212")immutable, heterogeneous
Set{1,2,3}mutable, heterogeneous
Dictionary{"Jack":"CT320", "Ben":"CS163"}mutable, heterogeneous

Variables

# Note lack of declaration and fixed type

val=42                          # integer
print(val)
val+=1.2                        # real
print(val)
val="hello there folks"         # string
print(val)
val=[5, "Gold", "Rings"]        # list
print(val)
val=(4, "Calling", "Birds")     # tuple
print(val)
val={3, "French Hens"}          # set
print(val)
42
43.2
hello there folks
[5, 'Gold', 'Rings']
(4, 'Calling', 'Birds')
{3, 'French Hens'}

Unicode!

Δ=0.01
π=3.14159
print(π/Δ)
print('Mi aerodeslizador está lleno de anguilas')
print("Το αερόστρωμνό μου είναι γεμάτο χέλια")
print("मेरी मँडराने वाली नाव सर्पमीनों से भरी हैं")
print('حَوّامتي مُمْتِلئة بِأَنْقَلَيْسون')
print("我的氣墊船裝滿了鱔魚")
314.159
Mi aerodeslizador está lleno de anguilas
Το αερόστρωμνό μου είναι γεμάτο χέλια
मेरी मँडराने वाली नाव सर्पमीनों से भरी हैं
حَوّامتي مُمْتِلئة بِأَنْقَلَيْسون
我的氣墊船裝滿了鱔魚

Indentation Determines Block Structure

Note the colon and lack of braces and then/fi

a=123
if a>100:
    print("Holy Toledo, a is too big at", a, '!')
    a=100
b=a
Holy Toledo, a is too big at 123 !

Indentation Examples

print("alpha")
if False:
    print("beta")
    print("gamma")
alpha
print("delta")
if False:
    print("epsilon")
print("zeta")
delta
zeta
print("eta")
if False:
    print("theta")
  print("iota")
  File ".py3", line 4
    print("iota")
                ^
IndentationError: unindent does not match any outer indentation level

if

age=25
if age<30:
    print("You’re a punk")
elif age>=65:
    print("You’re a geezer")
elif age==50+12:
    print("You’re the ideal age")
else:
    print("OK, boomer.")
You’re a punk

while

a = 5
while a>=0:
    print(a, end=' ')
    a-=1                    # No ++ or --
print("Blastoff!")
5 4 3 2 1 0 Blastoff!

for

# The for loop iterates over a list:
for p in [2,3,5,7]:
    print(p)
2
3
5
7
# Or anything else that's iterable:
total=0
for n in range(100):
    total+=n
print(total)
4950
# Though a real Pythoner will do this:
print(sum(range(100)))
4950

Sorry, C fans: no C-style for loop. range() does most of it, however.

break/continue/pass

for/else

for n in range(2, 10):
    for x in range(2, n):
        if n % x == 0:
            print(n, 'is', x, '*', n//x)
            break
    else:
        # loop fell through without finding a factor
        print(n, 'is prime')
2 is prime
3 is prime
4 is 2 * 2
5 is prime
6 is 2 * 3
7 is prime
8 is 2 * 4
9 is 3 * 3

The indentation is correct: the else is associated with the inner for, not the if! The else clause is executed if the for loop ended “normally”, not via break.

Why no result for 10?

Functions

def factorial(n):
    if n<=1:
        return n
    else:
        return n*factorial(n-1)

print(factorial(4), factorial(69))
24 171122452428141311372468338881272839092270544893520369393648040923257279754140647424000000000000000

Also:

Scope

Ignoring modules, there are only two scopes: global and function local.

Variables that are only read inside a function are implicitly global. If a variable is assigned a value anywhere within the function’s body, it’s assumed to be a local unless explicitly declared as global.

Boolean Operators

Boolean operators: and, or, not

and and or are short-circuiting, and return the last value evaluated.

print(3 and 5)
print(9 and 0)
print(False or "hello")
print(True)
print(not True)
5
0
hello
True
False

Sorry, C fans: no &&, ||

Get the case right

print(True, False)
True False
print(true, false)
Traceback (most recent call last):
  File ".py3", line 1, in <module>
    print(true, false)
NameError: name 'true' is not defined

Remember to get the case correct!

Arithmetic Operators

print(1+2)
print(2-3)
print(3*4)
print(100/6)
print(100//6)
print(3.5//1.1)
print(100%30)
print(2**10)
print(2**70)
3
-1
12
16.666666666666668
16
3.0
10
1024
1180591620717411303424

A Mystery

How does this work‽

a=12
--a
++a
++a
++a
++a
print(a)
12

Didn’t we say that there was no ++ or --?

There are no ++ or -- operators. Those are multiple unary operators.

Bitwise operators

print(5 & 3)
print(5 | 3)
print(5 ^ 3)
print(~5)
print(5<<2)
print(5>>1)
1
7
6
-6
20
2

Comparison operators

if 1<2:
    print("good")
if 2>=3:
    print("bad")
if 1 < 2 <= 3:
    print("excellent")
good
excellent

String Operators

print("hi"*3)                       # repetition
print("Foo" + 'bar')                # concatenation
print("CT" "320")                   # concatenation
print('xyz' in 'Mxyzptlk')          # string search
print('M.*k' in 'Mxyzptlk')         # STRING search
print('xyz' not in 'Mxyzptlk')      # string avoidance
print('%d/%d=%.2f' % (22,7,22.0/7)) # printf-ish formatting
hihihi
Foobar
CT320
True
False
False
22/7=3.14

Strings are immutable!

s="Luthor"
del s[3]
Traceback (most recent call last):
  File ".py3", line 2, in <module>
    del s[3]
TypeError: 'str' object doesn't support item deletion
s="Luthor"
s="Lex"  # s now refers to a DIFFERENT string
print(s)
Lex
s="Luthor"
s=s[:3]+s[4:] # make a new string without h
print(s)
Lutor

Strings are iteratable

Several types, e.g., strings, lists, tuples, are iterable:

for n in [1,2,3]:
    print(n)
for f in (1.1, 2.2, 3.3):
    print(f)
for v in range(7,9):
    print(v)
for c in "Dog":
    print(c)
1
2
3
1.1
2.2
3.3
7
8
D
o
g

List Operators

authors = ["Asimov", "Niven", "Baum"]
authors += ["Carroll"]
print(authors)
del authors[1]   # Au revoir, Larry!
print(authors)
authors += "Applin"
print(authors)
['Asimov', 'Niven', 'Baum', 'Carroll']
['Asimov', 'Baum', 'Carroll']
['Asimov', 'Baum', 'Carroll', 'A', 'p', 'p', 'l', 'i', 'n']

Lists are objects, with many methods.

Tuple Operators

t=(1,2,3)
print(t)
print(t+(3,1,99))
print(t*2)
a="alpha"; b="beta"
(c,d) = (a,b)
print(c)
print(d)
(1, 2, 3)
(1, 2, 3, 3, 1, 99)
(1, 2, 3, 1, 2, 3)
alpha
beta

Set Operations

authors = {"Asimov", "Niven"}
authors |= {"Carroll"}
authors.add("Baum")
print(authors)
authors.remove('Niven') # Au revoir, Larry!
print(authors)
{'Baum', 'Carroll', 'Asimov', 'Niven'}
{'Baum', 'Carroll', 'Asimov'}

Dictionary Operations

colors = {"red":"#ff0000", "green":"#00ff00"}
colors['blue'] = '#0000ff'
del colors['green']
print(colors)
for d in colors:
    print(d, "maps to", colors[d])
if "red" in colors: print("found red")
{'red': '#ff0000', 'blue': '#0000ff'}
red maps to #ff0000
blue maps to #0000ff
found red

Sequence Operators

Strings, lists, tuples, and sets are all sequences, with many methods.

s=[1.1, 22, (33,44), "slow"]
print("Length of s is", len(s))
for v in s:
    print(v)
if 22 in s:
    print("22 is in ", s)
if 33 not in s:
    print("33 is not in ", s)
if (33,44) in s:
    print("(33,44) is in ", s)
Length of s is 4
1.1
22
(33, 44)
slow
22 is in  [1.1, 22, (33, 44), 'slow']
33 is not in  [1.1, 22, (33, 44), 'slow']
(33,44) is in  [1.1, 22, (33, 44), 'slow']

Slices

s = list(range(11,100,11))
print(s)
print(s[2])
print(s[2:4])
print(s[2:])
print(s[:2])
print(s[-1])
print(s[-3:])
print(s[2:-1])
print(s[-5:7])
print(s[:])
[11, 22, 33, 44, 55, 66, 77, 88, 99]
33
[33, 44]
[33, 44, 55, 66, 77, 88, 99]
[11, 22]
99
[77, 88, 99]
[33, 44, 55, 66, 77, 88]
[55, 66, 77]
[11, 22, 33, 44, 55, 66, 77, 88, 99]

Primes

max = 100

s = set(range(2,max))

for candidate in range(2,max):
    s -= set(range(candidate*2, max, candidate))

print(s)
{2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97}

Reference Semantics

A variable holds a reference to an object, like Java.

a=list(range(10,0,-1))
b=a
c=a[:]
a[-3] = 33
print(a)
print(b)
print(c)
[10, 9, 8, 7, 6, 5, 4, 33, 2, 1]
[10, 9, 8, 7, 6, 5, 4, 33, 2, 1]
[10, 9, 8, 7, 6, 5, 4, 3, 2, 1]

List Comprehensions

Long way:

squares = []
for x in range(10):
    squares.append(x**2)
print(squares)
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

Short way:

squares = [x**2 for x in range(10)]
print(squares)
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

Multiple for loops and if statements are allowed.

Output

    print(value, …, sep=' ', end='\n', file=sys.stdout, flush=False)
print(1, 2, 3, 4, 5, sep="※")
print(6, 7, end=' and now ')
print(8, 9)
1※2※3※4※5
6 7 and now 8 9

File I/O

Show nameserver lines in /etc/resolv.conf:

f = open('/etc/resolv.conf', 'r')
for line in f:
    if "nameserver" in line:
        print(line, end='')
f.close()
nameserver 129.82.45.181
nameserver 129.82.103.78
nameserver 129.82.103.79

or:

with open('/etc/resolv.conf', 'r') as f:
    for line in f:
        line = line.rstrip()    # Remove \n
        if line.startswith("nameserver"):
            print(line)
nameserver 129.82.45.181
nameserver 129.82.103.78
nameserver 129.82.103.79

File object methods

Not appearing in this lecture