= 5
radius = 3.14 * radius ** 2
area = "James"
name = True in_class
3 Variables & Expressions
Python code can be thought of as a series of statements. As we review the syntax, we’ll see the familiar import
, if
, and for
– all examples of statements.
First we’ll focus on the expression, a special type of statement, as well as names, what Python calls variables.
Expressions
An expression is anything which evaluates to some value.
Some Expressions
3.145
"hello"
3 + 4
func(y)
person.age * 4
All of these can be said to represent a value, some directly, which we call literals: 4
, "hello"
, [1, 2, 3]
.
Others require further evaluation (e.g. 3 + 4
or func(y)
) to be resolved to a specific value, but ultimately can be reduced to a value as well.
Though a bit circular, it may be helpful to think of the rule if it can be assigned to a variable, it is an expression.
Assignment Statements
Assignment statements give a name to a value resulting from an expression.
Assignment statements:
- Have a name on the left-hand side, an expression on the right-hand side.
- Do not require any additional declaration or type.
- Names must consist of letters, numbers, and/or underscore
_
. Must begin with a letter or underscore. - Conventionally written in
snake_case
, with words separated by underscores. (as opposed tocamelCase
)
Variables in Python work a bit differently than you might expect coming from another language.
In many languages, variables are typed, meaning that when you declare a variable it is a “box” that can store a certain sized item.
// this is C code, the type is part of the variable declaration
int num_fish = 3;
char* name = "William";;
// we can not do this!
= name; num_fish
In Python, we just assign an expression to a name.
= 3
num_fish = "William"
name = name # this is allowed! (though bad practice) num_fish
Does this mean variables are untyped?
Let’s use Python’s type()
function to see that Python does indeed have types:
print(type(radius))
print(type(area))
print(type(name))
print(type(in_class))
<class 'int'>
<class 'float'>
<class 'str'>
<class 'bool'>
In Python, types are inferred, not explicitly stated.
As we’ll see, the type is associated with the variable, not the name, a subtle distinction but one of the most important concepts to understand Python’s behavior.
Scalar Types
Python has several built in scalar types. (Scalar types can have one value at a time.)
Numeric: int
, float
, complex
Bool: bool
None: None
Types are in part defined by what can be done with them, let’s look at some operators:
Numeric Operators & Functions
Operation | Result |
---|---|
x + y |
sum |
x - y |
difference |
x * y |
product |
x / y |
quotient |
x // y |
floored quotient |
x % y |
remainder of x / y (modulo) |
x ** y |
x to the power of y |
-x |
negation of x |
abs(x) |
absolute value / magnitude of x |
int(x) |
x converted to integer (floor) |
divmod(x, y) |
the pair (x // y, x % y) |
3 / 5
0.6
3 % 5
3
divmod(3, 5)
(0, 3)
= 2.999
x print(int(2.999))
2
print(100 // 2)
50
= 10 * 9 * 8 * 7 * 6 * 5 * 4 * 3 * 2
m = 3628800 # 10!
n print(n % 11)
10
Floating point numbers have limited precision, as demonstrated below.
This is true in all languages, and is a function of how modern computers work.
0.1 + 0.2) == 0.3 (
False
what?!
0.1 + 0.2
0.30000000000000004
For this reason, instead of strict equality checking, it is correct to compare that the error is less than some very small epsilon value.
Question What problems could this cause? What can be done about it?
# instead
= 0.000000001
episilon print(abs((0.1 + 0.2) - 0.3) < episilon)
True
Shorthand Operators
Operation | Result |
---|---|
a += b |
a = a + b |
a -= b |
a = a - b |
a /= b |
a = a / b |
a *= b |
a = a * b |
a //= b |
a = a // b |
= 64
x *= 2
x print(x)
= "Hello"
s += " Class"
s print(s)
128
Hello Class
What would happen if we did s /= "e"
? Try it and confirm your guess.
Type Conversion
If mixing numeric types in arithmetic, Python will automatically upconvert numeric types to the type that can represent more data:
- int, int -> int
- int, float -> float
- float, complex -> complex
= 4.1
y = int(3 + y)
x
print(x, type(x))
7 <class 'int'>
This behavior is why we have two division operators:
= 2 // 1
x print(x, type(x))
= 2 / 1
x print(x, type(x))
2 <class 'int'>
2.0 <class 'float'>
Comparison Operators
Syntax | Definition |
---|---|
x > y |
True if left operand is greater than the right |
x < y |
True if left operand is less than the right |
x == y |
True if both operands are equal |
x != y |
True if both operands are not equal |
x >= y |
True if left operand is greater than or equal to the right |
x <= y |
True if left operand is less than or equal to the right |
Booleans
Resulting type of any of the relational operators.
Only two possible values: True
, False
Logical Operators
- Operators that perform logical AND, OR, and NOT
- These operators short-circuit (i.e., when an expression is stopped being evaluated as soon as its outcome is determined.)
Syntax | Definition |
---|---|
x and y |
True if both the operands are true |
x or y |
True if either of the operands is true |
not x |
True True if operand is false |
Note: these do not use &
or |
like in C/Java Those symbols have a different purpose in Python.
= True and print("a")
a = False and print("b")
b = False or print("c")
c = True or print("d") d
What will this code print?
import time
# we'll discuss how to write functions/etc. soon
def short_func():
print("short_func")
return False
def long_func():
print("long_func")
3)
time.sleep(return False
Are these equivalent?
= long_func() and short_func() result
= short_func() and long_func() result
None
Represents the absence of a value. It is the only value of NoneType
. We’ll talk more about uses of None
as the course progresses.
= None
x print(x)
print(type(x))
None
<class 'NoneType'>
Sequence Types
We’ve seen that scalar types take on a single value.
Sequences store multiple values in a defined order.
We’ll take a look at str
, list
, and tuple
.
Strings
Can use 'single'
or "double"
, or """triple for multi-line strings"""
.
= "Molly's Idea"
s1
= '"I think, therefore I am" - Descartes'
s2
= """From time to time
s3 The clouds give rest
To the moon-beholders.
- Matsuo Bashō
"""
Escape Characters
Like many languages, Python supports special ‘escape characters’.
print("Another way to \"quote\" something.")
print('An alternate apostrophe: \' ')
print("Newline character: \n starts a new line.")
print("Sometimes you need a \\ backslash. \" ")
Another way to "quote" something.
An alternate apostrophe: '
Newline character:
starts a new line.
Sometimes you need a \ backslash. "
Character | Meaning |
---|---|
\n | New Line |
\t | Tab |
\\ | (backslash) |
\' | ’ (apostrophe) |
\" | ” (quote) |
Raw Strings
= "C:\new\test.py"
error print(error)
C:
ew est.py
What happened?
Sometimes it is annoying to need to escape every backslash.
Two common examples are when dealing with file paths on Windows or Regular Expressions.
In this case we can use r”” to denote a raw string.
= r"C:\new\test.py"
fixed print(fixed)
C:\new\test.py
# still a str
type(fixed)
str
String Formatting
You’ll often need to create strings comprised of other values.
There are two common ways to do this, .format
and f-strings:
# format example 1: implicit
= "{}@{}.{}"
fmt = fmt.format("username", "example", "com")
email print(email)
username@example.com
# format example 2: positional
= "Hi {0}, you are user {1}! \n Bye {0}!"
template = template.format("Tina", 2.5)
message print(message)
# note that integer was converted automatically
# most useful if you want to use the same value multiple times
Hi Tina, you are user 2.5!
Bye Tina!
# format example 3: keyword
= "Hi {user}, you are user {num}! \n Bye {user}!".format(user="Sam", num=1390)
message print(message)
Hi Sam, you are user 1390!
Bye Sam!
# f-strings example (Added in Python 3.6)
= "Ben"
user = 1234
num = f"Hi {user}, you are user {num}! \n Bye {user}!"
message print(message)
Hi Ben, you are user 1234!
Bye Ben!
# f-strings debug example (Added in Python 3.8)
= "James"
user = 1234
num
print(f"{user=} {num=}")
# same as f"user={user} num={num}" but less repetition
# = is a format specifier, there are many others for aligning output, truncating decimals, etc.
for i in range(10):
print(f"{i=}")
user='James' num=1234
i=0
i=1
i=2
i=3
i=4
i=5
i=6
i=7
i=8
i=9
Lists
One of the most useful sequence types is list
.
Lists are:
Ordered by position, starting at 0.
Mutable, they can be modified in-place
Dynamically-sized, can grow and shrink as needed.
Heterogeneous, can store mixed types. (Though we often avoid this!)
= []
things
# lists can contain items of different types
= [123, "abc", 1.23j + 4.5]
things
# lists can contain other lists
= [["egg", "toast"], ["sandwich", "chips"], ["fish", "salad", "cake"]]
meals
print(things)
print(meals)
[123, 'abc', (4.5+1.23j)]
[['egg', 'toast'], ['sandwich', 'chips'], ['fish', 'salad', 'cake']]
Tuples
Tuples work very similarly to lists but are immutable, they cannot be changed once created.
= (1, 2.0, "three")
multi_item
= ()
empty_tuple
= (1 + 2,) # why is the comma necessary?
one_item_tuple print(multi_item)
print(empty_tuple)
print(one_item_tuple)
(1, 2.0, 'three')
()
(3,)
= (1 + 492)
bad_tuple print(bad_tuple, type(bad_tuple))
493 <class 'int'>
Sequence Operations
All three of these sequence types support some useful operations:
operation | name | description |
---|---|---|
len(seq) |
Length | gets number of items in sequence. |
seq1 + seq2 |
Concatenation | to concatenate together (make a new sequence). |
seq * N |
Repetition | creates a new sequence that repeats seq, N times. |
item in seq |
Containment | tests for whether or not a given value appears in a sequence. |
seq[N] |
Indexing | gets Nth value from sequence. |
seq[N:M] |
Sliced Indexing | returns a new sequence that is a “slice” of the original. |
# length demo
= "Hello World"
s1 = [1, ["a", "b", "c"], 3, None, 4]
l1 = ()
t1
print(len(s1))
print(len(l1))
print(len(t1))
11
5
0
# concatenation & repetition demo
= "*" * 5
s2 = (True, False) * 3
t2 = ["a", "b", "c"] * 4
l2 print(s2)
print(t2)
print(l2)
*****
(True, False, True, False, True, False)
['a', 'b', 'c', 'a', 'b', 'c', 'a', 'b', 'c', 'a', 'b', 'c']
# concatenation & repetition demo
= "*" * 5
s2 = (True, False) * 3
t2 = ["a", "b", "c"] * 4
l2 print(s2, "\n", t2, "\n", l2)
*****
(True, False, True, False, True, False)
['a', 'b', 'c', 'a', 'b', 'c', 'a', 'b', 'c', 'a', 'b', 'c']
= [
cities "Tokyo",
"Delhi",
"Shanghai",
"São Paulo",
"Mexico City",
"Cairo",
"Mumbai",
"Beijing",
"Dhaka",
"Osaka",
]= "Four score and seven years ago our fathers brought forth, upon this continent, a new nation, conceived in liberty, and dedicated to the proposition that all men are created equal"
text = (123, 555, 81, 110, 44, 12, 16)
ids
print(cities[3])
print(text[12])
print(ids[0])
São Paulo
n
123
# containment
print("Shanghai" in cities)
print("Delhi" in cities)
print(" seven " in text)
print("7" in text)
print(123 in ids)
True
True
True
False
True
# slicing
print(cities[2:-4])
print("hello world"[2:5])
print(cities[8:])
['Shanghai', 'São Paulo', 'Mexico City', 'Cairo']
llo
['Dhaka', 'Osaka']
Indexing / Slicing Rules
s = "Hello!"
Letter | Index | -Index |
---|---|---|
H | 0 | -6 |
e | 1 | -5 |
l | 2 | -4 |
l | 3 | -3 |
o | 4 | -2 |
! | 5 | -1 |
First element is 0.
Last element is -1.
Slice boundaries are inclusive of first, exclude last.
mutable sequence methods
(for now just list
)
Operation | Result |
---|---|
s[i] = x |
Replace element i in sequence with x . |
s.append(x) |
Add item to end of sequence. |
s.clear() |
Remove all items from sequence. |
s.copy() |
Create a (shallow) copy of sequence. |
s.insert(i, x) |
Insert an item x at position i . |
s.pop() or s.pop(i) |
Retrieve item at position i and remove it. (Defaults to -1 if not provided) |
s.reverse() |
Reverse items of s in place. |
# list mutation
= ["A", "B", "C", "D", "E", "F", "G"]
letters
"H")
letters.append(print(letters)
['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H']
0, "*")
letters.insert(
letters.pop()4)
letters.pop(print(letters)
['*', 'A', 'B', 'C', 'E', 'F', 'G']
letters.reverse()print(letters)
['G', 'F', 'E', 'C', 'B', 'A', '*']
common string methods
String has some special methods
Method | Description |
---|---|
s.find(sub) | Finds first occurrence of substring sub or -1 if not found |
s.lower() | Converts the string to lowercase. |
s.upper() | Converts the string to uppercase. |
s.replace(old, new) | Replaces occurrences of old with new. |
s.strip() | Remove leading & trailing whitespace. |
s.startswith(prefix) | Checks if a string starts with prefix. |
s.endswith(suffix) | Checks if a string ends with suffix. |
s.split(sep) | Split a string using sep as delimiter. |
(Credit: Python Distilled, Table 1.6)
https://docs.python.org/3/library/stdtypes.html#string-methods
# string method demo
= "Hello world!"
s
# find
= s.find("world")
pos print(pos)
6
print(s[pos:])
world!
= s.upper()
new_string
print("s=", s)
print("new_string=", new_string)
s= Hello world!
new_string= HELLO WORLD!
"world", "class")
s.replace(print(s)
Hello world!