Block F

Here is a little arithmetic.


Python 3.8.5 (default, Sep  4 2020, 02:22:02) 
[Clang 10.0.0 ] :: Anaconda, Inc. on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> 2 + 6
8
>>> 2 - 6
-4
>>> 2*6
12

Division of integers produces a floating-point number.


>>> 2/6
0.3333333333333333

Here is integer division and its bosom buddy mod.
>>> 2//6
0
>>> 365//7
52
>>> 365 % 7
1

Integers can be gigantic.


>>> 2**1000
10715086071862673209484250490600018105614048117055336074437503883703510511249361
22493198378815695858127594672917553146825187145285692314043598457757469857480393
45677748242309854210746050623711418779541821530464749835819412673987675591655439
46077062914571196477686542167660429831652624386837205668069376

Here we use magic to count the digits in this giant number.


>>> len(str(2**1000))
302

Eu. What's this??


>>> 44^5
41

Python objects know their type.


>>> type(3)
<class 'int'>
>>> type(.33)
<class 'float'>

Floats are 64 bit (double precision) IEEE754 floating point numbers. Remember the F-word? There can only be 264 floating point numbers. Here is an ugly consequence of that.


>>> .1 + .1 + .1 == .3
False

Whoa!


>>> .1 + .1 + .1 
0.30000000000000004

Floating point numbers are only stored approximately. Now dig a littl scientific notation


>>> 6.02e32
6.02e+32
>>> type(6.02e23)
<class 'float'>
>>> 3E8
300000000.0

Casting a float to an integer truncates everything after the decimal point.


>>> int(4.5)
4

A cast is a temporary request to regard an object of one type as beig of another. It does not change the originial object.

Here we see that anything can be cast to a string.


>>> str(5 + 6)
'11'

Yawn. No surpise


>>> float(4)
4.0

Yep, there are complex numbers, too. And all of their arithmetic can be done in Python.


>>> z = complex(1,2)
>>> z**10
(237-3116j)

Variable Naming Rules The first character is alpha or _ (underscore). Subsequent characters are alphanumeric or _s. This is snake notation: number_of_snakes. It is very common in Python.

This is a lie


>>> x = 5
>>> type(x)
<class 'int'>

What type(x) is telling you is the type of object x is pointing at. Variables are typeless names. Objects harbor type.


>>> x = "cowabungazungablurb"
>>> type(x)
<class 'str'>

Notice that since x is now pointing at a string, type(x) replies with <class 'str'>.

Watch out for heffalumps.


>>> type(heffalump)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'heffalump' is not defined

All of the standard relational operators exist.


>>> 4 < 5
True
>>> type(4 < 5)
<class 'bool'>
>>> 4 <= 5
True
>>> 4 >= 5
False
>>> 4 > 5
False
>>> 4 == 5
False
>>> 4 != 5
True

The objects True and False constitute the boolean type.

The not operator reverses the truth-value of a boolean.


>>> not(True)
False
>>> not(False)
True
>>> not False
True

The not opeerator is a prefix unary operator. It is prefix because it occurs before its operand. It is unary because it acts on one operand.

Now meet two infix binary operators, and and or.


>>> True or False
True
>>> False or True
True
>>> True or True
True
>>> False or False
False
>>> True and True
True
>>> True and False
False
>>> False and True
False
>>> False and False
False

Python objects have three attribues.

For number types, the state of the object is the value it is storing. The behavior is that that they can be operated on by arithmetic operators.

For the boolean type, the state of the object is True or False. The behavior is that that they can be operated on by and, or and not.

Strings Strings store globs of text. Strings have a [] operator that allows you to grab off pieces of them. Look at this.


>>> x = "abcdefghijklmnopqrstuvwxyz"
>>> x[1]
'b'
>>> x[2]
'c'
>>> x[3]
'd'

The numbers you see here are called indices. The indices of a string. In this example we are indexing into a string and we are handed one-character strings.

In our string, indices live between characters in a string. Note this picture.

   ------------------------              -----------------
   | a | b | c | d | e | f   ......      | w | x | y | z |
   0---1---2---3---4---5---              22--23--24--25--26

This is called slicing Here you get all characters before index 5.


>>> x[:5]
'abcde'

Now between indices 4 and 7.


>>> x[4:7]
'efg'

The + operator concatenates strings.


>>> x[:4] + x[4:]
'abcdefghijklmnopqrstuvwxyz'
>>> "cow" + "pie" 
'cowpie'

The principle here is Type determines context. The + operator adds numbers and concatenates string.

This is perilous


>>> "cow" + 42
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: can only concatenate str (not "int") to str

An irate Python hisses inimically. Here are a few tricks, the last of which is a format string.


>>> a = "cow"
>>> b = 42
>>> a + str(b)
'cow42'
>>> f"{a}{b}"
'cow42'

A crushing upper cut is delivered. The original string is untouched.


>>> x
'abcdefghijklmnopqrstuvwxyz'
>>> x.upper()
'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
>>> x
'abcdefghijklmnopqrstuvwxyz'

Now save the result in a variable. Then we will lower ourselves.


>>> y = x.upper()
>>> y
'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
>>> y.lower()
'abcdefghijklmnopqrstuvwxyz'

find finds.


>>> x.find("j")
9

This is cool.


>>> pal = "amanaplanacanalpanama"
>>> pal.find("p")
5
>>> pal.rfind("p")
15

Here is a fancy step.


>>> x[:x.find("o")] + x[x.find("o"):].upper()
'abcdefghijklmnOPQRSTUVWXYZ'

You can get rid of undesirable elements.


>>> "  I am   a   space cadet".replace(" ", "")
'Iamaspacecadet'

You can "edit" a string.


>>> "My favorite class is math.".replace("math", "compsci")
'My favorite class is compsci.'
>>>