Today: comprehensions, truthy logic, format strings

List Comprehension - 1-2-3

Comprehension + If

>>> nums = [1, 2, 3, 4, 5, 6]
>>> [n for n in nums if n > 3]
[4, 5, 6]
>>> [n * n for n in nums if n > 3]
[16, 25, 36]

Comprehension Style

Can get into Comprehension Fever - trying to write your whole program as nested comprehensions. Probably 1-line is the sweet spot.

Fine to use regular functions, loops etc. for longer phrases of code.

Comprehension Examples

Section on server: Comprehensions

> diff21

> teen_only

Solutions

def dif21(nums):
    return [abs(n - 21) for n in nums]


def teen_only(nums):
    return [n for n in nums if n >= 13 and n <= 19]

Exercise - Comprehensions

These are all 1-liner solutions with comprehensions. We'll just do 1 or 2 of these.

Syntax reminder - e.g. make a list of nums doubled where n > 3

[2 * n for n in nums if n > 3]

> first_up

first_up: Given a list of non-empty strings. return a list of their first chars converted to upper case.

> up_only

up_only: Given a list of non-empty strings. Return a list made of just the strings that begin with an uppercase char. Write as a 1-line comprehension. Use if


Two Math Systems, "int" and "float" (Recall)

# int
3  100  -2

# float, has a "."
3.14  -26.2  6.022e23

Math Works

Mixed Case: int + float = float "promotion"

-Also division / always yields float
>>> 5 / 2   # / yields float
2.5
>>> 4 / 2
2.0
>>>
>>> 1 + 1 + 1
3
>>> 1 + 1 + 1.0  # float promotion
3.0
>>> 3.14 * 2
6.28
>>> 

float() int() Conversions

-Use float() to convert str to float value, similar to int()
>>> float('3.14')   # str -> float
3.14
>>> int(3.14)       # float -> int, truncation
3
>>> int('16')
16
>>> int('3.14')
ValueError: invalid literal for int() with base 10: '3.14'

Float - One Crazy Flaw - Do Not Panic

Crazy Flaw Demo - Adding 1/10th

>>> 0.1
0.1
>>> 0.1 + 0.1
0.2
>>> 0.1 + 0.1 + 0.1    # this is why we can't have nice things
0.30000000000000004
>>> 
>>> 0.1 + 0.1 + 0.1 + 0.1
0.4
>>> 0.1 + 0.1 + 0.1 + 0.1 + 0.1
0.5
>>> 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1
0.6
>>> 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1
0.7
>>> 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1
0.7999999999999999     # here the garbage is negative

Another example with 3.14

>>> 3.14 * 3
9.42
>>> 3.14 * 4
12.56
>>> 3.14 * 5
15.700000000000001   # d'oh

Conclusion: float math is slightly wrong

Why Must We Have This Garbage?

The short answer, is that with a fixed number of bytes to store a floating point number in memory, there are some unavoidable problems where numbers have these garbage digits on the far right. It is similar to the impossibility of writing number 1/3 precisely as a decimal number — 0.3333 is close, but falls a little short.

Why? Why Must There Be This Garbage?

Crazy, But Not Actually A Problem

Must Avoid One Thing: no ==

>>> a = 3.14 * 5
>>> b = 3.14 * 6 - 3.14
>>> a == b   # Observe == not working right
False
>>> b
15.7
>>> a
15.700000000000001
>>> abs(a-b) < 0.00001
True

Float Conclusions


For more detail see "truthy" section in the if-chapter Python If - Truthy

Truthy True/False

s = 'hello'

# Could do this:
if len(s) > 0:
    print('not empty')

# Equivalent this way:
if s:
    print('not empty')

Truthy False

Truthy logic says that "empty" values count as False. These all count as False - zero, emtpy-string, empty-list,...

# Count as False:
0
0.0
None
''
[]
{}

Truthy True

Any other value counts as True

# Count as True
6
3.14
'Hello'
[1, 2]
{1: 'b'}

Truthy bool()

The bool() function takes any value and returns a formal bool False/True value, so it's a way for us to see how truthy logic works in the interpreter:

>>> bool(0)
False
>>> bool(0.0)
False
>>> bool('')   # empty string - False
False
>>> bool([])   # empty list - False
False
>>> bool(None)
False
>>> bool(6)       # non-zero int - True
True
>>> bool('hi')    # non-empty string - True
True
>>> bool([1, 2])  # list of something - True
True
>>> bool('False')  # ??? - what's this one?
???
>>> bool([0, 0])   # ???
???
>>>

Truthy Shorthand

Why does the truthy system exist? It makes it easy to test, for example, for an empty string like the following. Testing for "empty" data is such a common case, it's nice to have a shorthand for it. For CS106A, you don't ever need to use this shorthand, but it's there if you want to use it. Also, many other computer languages also use this truthy system.

# long form screen out empty string
if len(s) > 0:
    print(s)


# shorter way, handy!
if s:
    print(s)

Truthy Exercise not_empty()

> not_empty

Format Strings

s = f'A format string'

Try in the interpreter:

>>> name = 'Sally'
>>> scores = [19, 34, 22]
>>> 
>>> s = f'Name:{name} count:{len(scores)}'
>>> s
'Name:Sally count:3'
>>> s = f'Name:{name} count:{len(scores)} max score:{max(scores)}'
>>> s
'Name:Sally count:3 max score:34'

Amazingly, within the curly braces, just write any old expression like {name} referring to a variable, or even max(scores) which calls a function, pasting in what it returns.

To include a literal { or } in the string, double them up - so {{ to make one {

>>> f'A curly brace: {{'
'A curly brace: {'

Format Digits

Within the curly braces, a colon : added to the end of a value enables various format options. For floating point, adding :.4 after the floating point value will limit the number fraction digits shown to 4, like this:

>>> 2 / 3
0.6666666666666666
>>> x = 2 / 3
>>> f'val: {x}'            # {x} - too many digits
'val: 0.6666666666666666'
>>> f'val: {x:.4}'         # {x:.4} - limit digits
'val: 0.6667'

Optional - Packet Routing

Visit lecture-23 - finish off packet demo - how internet works. Show ping and traceroute.