31 August 2020

Introducing Python

Go into a command window (terminal on mac) and type python. The symbol unix> just represents your system prompt.

This launches the Python REPL. It allows you to "talk" to Python interactively.

unix> python
Python 3.7.4 (default, Aug 13 2019, 15:17:50) 
[Clang 4.0.1 (tags/RELEASE_401/final)] :: Anaconda, Inc. on darwin
Type "help", "copyright", "credits" or "license" for more information.

To exit Python, type control-d on a Mac or linux, and control-Z in Windoze. You can also type quit().

The cheveron you see, >>> is Python's prompt, indicating it is ready to accept commands.

Let's begin with a little 'rithmetic

>>> 3 + 4
7
>>> 3*4
12

The term object refers to anything being stored in Python's memory. Every object is keenly aware of its type.

>>> type(3)
<class 'int'>

The object 3 is of integer type.

Notice that there are two kinds of divison. If you divide two integers, you will get a floating-point number.

>>> 3/4
0.75
>>> type(3/4)
<class 'float'>

This is integer divison. It truncates.

>>> 3//4
0

Here is the mod operator, %. It divides the right operand into the left and computes the remainder.

What does this have to do with your birthday?

>>> 365 % 7 
1

In the absence of leap years, your birthday moves ahead one day of the week each year.

Here is how to exponentiate.

>>> 3**4
81

The ^ operator is not exponentiation; it is bitwise xor

>>> 3^4
7
>>> 3^5
6
>>> 3^6
5

Here is how bitwise xor works. Line up the bits, if they disagree, the resulting bit is a 1; it's a 0 otherwise.


            3    00000011
            4    00000100
            -------------
                 00000111 → 7

            3    00000011
            5    00000101
            -------------
                 00000110 → 6

            3    00000011
            6    00000110
            -------------
                 00000101 → 5

Python integers can be as large as you like, subject to the limits of memory. But this is very easy. Your calculator would croak when confronted by this numerical leviathan.

>>> 2**1000
10715086071862673209484250490600018105614048117055336074437503883703510511249361
22493198378815695858127594672917553146825187145285692314043598457757469857480393
45677748242309854210746050623711418779541821530464749835819412673987675591655439
46077062914571196477686542167660429831652624386837205668069376

The integer type is for whole-number arithmetic.

When we computed 3/4 we met a floating point number. In Python these are IEEE 754 64 bit floating point numbers. There is a Wikipedia article that can give you the inside dope on how these work.

>>> guac = 6.02e23
>>> print(guac)
6.02e+23

We created a variable here and assigned Avocado's number to it.

Warning Will Robinson!!!! Comparing floating-point numbers for equality is dangerous and risky!

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

Whoa! Here is why

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

The binary expansion of 1/10 is .0001100110011001100....... The 4 you see at the end is the result of truncation error caused by rounding this expansion. Here is a frightening fact: There are only finitely many floating-point numbers.. A floating point number is 64 bits long, so you can only represent 2**64 = 18446744073709551616 floating point numbers.

We just saw another type, bool, the boolean type. We will discuss this in detail shortly.

>>> type(False)
<class 'bool'>

And here is the last numeric type, complex for complex numbers.

>>> x = complex(3,4)
>>> 
>>> x
(3+4j)

Casting

A cast is a temporary request to regard an object of one type as being of another.

Casting is done like this newType(variableOrLiteral) Here we cast from float to integer. This cast truncates towards 0.

>>> int(4.5)
4
>>> int(-4.5)
-4

Now let's cast a float to teh other types.

>>> bool(4.5)
True
>>> complex(4.5)
(4.5+0j)

We now check out a bool. Every numeric cast results in its 1.

>>> complex(True)
(1+0j)
>>> float(True)
1.0
>>> int(True)
1.0
>>> float(False) 0.0

Here are two casts that does not make any sense.

>>> x = complex(3,4)
>>> int(x)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: can't convert complex to int
>>> float(x)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: can't convert complex to float

python parses complexes nicely

>>> x = 5+0j
>>> type(x)
<class 'complex'>

Casting About

You tried these and this is what we got.

int complex boolean float
int * x → x+0jTrue → 1, False → 0 promotes in the obvious way
complex CRASH * True for any nonzero value CRASH
boolean For any numeric type, True casts to that type's 1 and False casts to that type's 0
float truncates x+0j not 0 (True) 0 (false)*

Boolean Operations

There are three boolean operations, or, and, and not. Boolean operations are defined by truth-tables Let us being with the unary prefix operator. not.

not
Pnot P
T F
F T

Both and and or are infix binary operators. They have two operands and occur between their operands.

or, and
P Q P or Q P and Q not P and not Q
T T T T F
T F T F F
F T T F F
F F F F T

You might properly ask, "Is there aa relationship between not(P or Q) not P?? and not Q"

Notice the fifth column in the table above. Look at the columnns for P or Q and not P and not Q. They have opposite truth values! This says that not(P or Q) is the same as not P and not Q. This is the first of the two "DeMorgan Laws". The other is that not(P and Q) is the same as not P or not Q.

Here is an example. Suppose that P is "you are 6ft or taller" and that Q is "you are age 18 or older."

P and Q is "you are over 6 ft and over 18." not(P and Q) is "you are under 6 ft OR under 18."

Here is or at work in Python.

>>> 6 > 7 or 5*5 == 25
True

Notice that arithmetic causes booleans to be cast to numbers.

>>> True + True
2

We now reset our Python session. We can use dir() to see everything that is visible in our session. All of the __doubleunderscore__ stuff is Python's linguistic infrastructure. At the end you see the varibles we created.

>>> x = 5
>>> y = 2
>>> dir()
['__annotations__', '__builtins__', '__doc__', '__loader__', '__name__', '__package__', '__spec__', 'x', 'y']

When you enter an expression at the prompt, it is evaluated.

>>> x*y
10
>>> x**y
25

I am now going to take a variable that points at an integer and make it point at a float. In many lanugages, this is an error.

>>> x = 5.0

Variables are typeless names. Variables point at objects.

Python has a string type.

>>> foo = "quagmire"
>>> type(foo)
<class 'str'>

Here is a real plus!

>>> goo = "mire"
>>> foo + goo
'quagmiremire'

Type determines context!

>>> 5 + 6
11
>>> "hello" + " thar"
'hello thar'

Watch Python hiss.

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