Instruction

Python Basics

Now that you’ve set up your environment and know how to run commands, it’s time to learn the basics of the Python programming language.

Data Types and Variables

To create a new variable in Python, simply use the assignment operator, =, to assign a value to a new variable name. Python does not require you to specify the variable’s type. Instead, it infers the variable’s type from the value assigned to it.

Here are some examples:

score = 200  # int
time_travel_gigawatts = 1.21  # float
greeting = "Welcome to Python"  # string
is_user_logged_in = True  # boolean

Note: In Python, boolean values are capitalized: True and False, not true and false.

Python doesn’t have constants, just variables.

Data Types

Python has the following basic data types:

  • int: Integers
  • float: Floating-point numbers
  • complex: Complex numbers
  • str: Strings
  • bool: Booleans

Python’s type() function takes an object and returns that object’s type.

Converting Values to Other Types

Everything in Python is an object, even basic data types. This means you can create any variable using object initialization syntax.

score = int(200)
message = str("Hi there!")

But it’s also useful for converting values of one type to another. You can use any of these initializer methods to do type conversion:

Initializer method floating-point number float() integer string complex number int() complex() str() Instantiates or converts a type to boolean bool()

The None Value

Python has a special value called None, which represents a null value or the absence of a value, like nil or null in other programming languages. None is the only member of its own type, NoneType.

One common use of None is initializing variables that will be assigned a value later.

Variable Naming Conventions

The Python community has adopted several stylistic conventions, one of which is snake case for variable names: all lowercase letters, with words separated by underscores:

# Example variable names
score
high_score
all_time_high_score

To make up for a lack of constants, many Python programmers follow the convention of naming them using only uppercase letters with underscore characters separating words for legibility:

# Example constant names
BASELINE
AVOGADRO_NUMBER
UNIVERSAL_GRAVITATIONAL_CONSTANT

Deleting Variables

Unlike most languages, Python has a statement that deletes objects from memory, which includes a variable: del.

Here’s an example:

my_variable = "I won't be around for long."
del my_variable
my_variable  # "NameError: name 'my_variable' is not defined"

Notice that del is a statement, not a function. You don’t need to put parentheses around the variable to be deleted.

String Interpolation With f-Strings

The simplest and most readable way to perform string interpolation in Python is to use f-strings, which stands for formatted strings.

f-strings are delimited by quotes where the opening quote is preceded by f. Any value, object, or expression that you want interpolated into the f-string should be contained in braces,{ and }:

name = "Alice"

# Alice needs 714 floor tiles.
print(f"{name} needs {21 * 34} floor tiles.")

Multi-Line Strings

Python uses triple quotes, ''' or """, to delimit string literals with multiple lines:

message = """Here is
an example
of a multi-line string
in Python."""

Multi-line strings can also be f-strings:

items_count = 3
price = 8.62
print(f"""Your cost
for {items_count} items at ${price} each
is {items_count * price}.""")

Getting User Input With input()

Python’s input() function presents the user with an optional prompt, waits for them to enter something, and returns the user’s input as a string. In Jupyter, it presents the prompt beside a text box where the user can enter text:

# Putting a space at the end of an `input()` prompt
# provides a little distance between the prompt
# and the text input box.
name = input("What is your name? ")
print(f"That's funny, my dog's name is {name}.")

String Methods

You may find the following string methods useful:

String method Returns a version of the string with all leading and trailing whitespace removed. strip() Return an all-uppercase version and an all-lowercase version of the string. Joins elements of an iterable collection into a single string. Returns a list, Python’s version of an array, of the string split into substrings based on a given delimiter. Searches for a substring and returns its position. Returns a version of the string with all occurrences of one substring replaced by another substring. upper() lower() and split() join() find() index() and replace() Counts the occurrences of a substring in the string. count() Description

Comparison and Boolean Logic Operators in Python

Python uses the standard comparison operators found in other programming languages, but its boolean logic operators may differ from the ones you usually use:

  • not instead of !
  • and instead of &&
  • or instead of ||

not has higher precedence than and, which has higher precedence than or.

if and Indentation

Python’s if statement works almost as you might expect, but there are differences in syntax from other programming languages:

status = input("Is the job complete? ").strip().lower()
if status == "y" or status == "yes":
	print("Hooray! The job is complete!")
	print("Take a moment to celebrate.")
	print("But afterward, get back to work!") # Last line of `if` block
print("Now on to the rest of the program.")

Note the following:

  • The expression following if is not in parentheses. Python doesn’t require parentheses around an if statement’s test expression. It’s part of the Python philosophy of readability and simplicity.
  • The if statement ends with the colon,:, character, which means that a code block will start on the next line.
  • The end of the if block is indicated by the end of the indentation. In the example above, “Now on to the rest of the program.” will always be printed, regardless of the value of status.

else

The else keyword works as you might expect. You start with if statement and a condition, if that condition is true a code block executes, otherwise the else block gets executed:

status = input("Is the job complete? ").strip().lower()
if status == "y" or status == "yes":
	print("Hooray! The job is complete!")
	print("Take a moment to celebrate.")
	print("But afterward, get back to work!")
else:
	print("Well, keep working at it.")
	print("We'll celebrate when it's done.")  # Last line of `else` block
print("Now on to the rest of the program.")

elif

Python has the elif keyword, which is a compressed, combined version of the else if statement used in other programming languages:

status = input("Is the job complete? ").strip().lower()
if status == "y" or status == "yes":
	print("Hooray! The job is complete!")
	print("Take a moment to celebrate.")
	print("But afterward, get back to work!")
elif status == "n" or status == "no":
	print("Well, keep working at it.")
	print("We'll celebrate when it's done.")  # Last line of `elif` block
else:
  print("You're not good at following instructions.")
  print("This will go in your permanent record.")
print("Now on to the rest of the program.")

while Loops

Python has only two kinds of loop statements, the first of which is the while loop. It functions like the while loops in most other programming languages:

count = 0
while count < 5:
  print(f"The current count is {count}.")
  count += 1

You don’t have to put the conditional expression — the count < 5 part — in parentheses, just as they’re not required in Python’s if and elif statements.

break

Like other programming languages, you can use the break statement to exit a loop from within:

while True:
  response = input("Continue? Enter \"y\" or \"n\". ")
  if response == "y" or response == "n":
    break
print(f"Your response was \"{response}\".")

continue

Python also provides the continue statement, which sends the program back to the start of the loop, where it starts the next iteration:

count = 0

while count < 20:
  count += 1
  # True if `count` is evenly divisible by 3
  if count % 3 == 0:
    continue
  print(f"The current number is {count},")
  print("and it's not evenly divisible by 3.")

else

The ‘else’ keyword is an unusual and often overlooked feature of Python’s loops. It lets you define a block of code that will execute only when the loop completes “normally”, that is, without executing the break statement while in the loop.

In this first example, the code prints out the numbers 0 through 4 followed by “Done!” because the break statement is never executed:

count = 0
while count < 5:
  print(count)
  count += 1
else:
  print("Done!")

This code prints out the numbers 0 through 3, but does not print “Done!”

count = 0
while count < 5:
  print(count)
  if count == 3:
    break
  count += 1
else:
  print("Done!")

If the code in a loop never executes, it never encounters the break statement and exits normally. This means that the else block is executed:

while False:
  print("This will never execute...")
else:
  print("...but this will!")

Sequence Types: Strings, Lists, and Tuples

Python has three built-in sequence types, which are data types that hold multiple values in a specific order. They are:

  • Strings: Like strings in other programming languages and are delimited by quotes (', ", or """). Strings are immutable.
  • Lists: Similar to arrays in other languages but more flexible, they are delimited by [ and ]. Lists are mutable.
  • Tuples: Like Python lists but immutable and are delimited by ( and ).

Here are examples of creating a non-empty string, list, and tuple using literal string, list, and tuple values:

my_string = "Hello"
my_list = ["Python", "Java", "C", "JavaScript", "Swift", "Kotlin"]
my_tuple = (4, 8, 15, 16, 23, 42)

You’ll use the string, list, and tuple above in the following examples.

You create empty strings, lists, and tuples either by using literal values or object initializer syntax:

empty_string = ""
another_empty_string = str()
empty_list = []
another_empty_list = list()
empty_tuple = ()
another_empty_tuple = tuple()

Accessing Individual Elements of a Sequence

Python uses array index notation to access elements of a sequence, where the first element’s index is 0. For example:

my_string[1]  # 'e'
my_list[0]    # 'Python'
my_tuple[2]   # 15

Python uses negative indexes to access sequence elements starting from the end, where the index for the last item is -1, the index for the second-last item is -2, and so on. For example:

my_string[-1]  # 'o'
my_list[-2]    # 'Swift'
my_tuple[-3]   # 16

Accessing Slices of a Sequence

Python has a syntax for accessing a slice, a smaller sequence, from a string, list, or tuple. The simplest version of the slice syntax is sequence[start:stop]. For example:

my_string[0:3]  # Hel
my_list[2:4]    # ['C', 'JavaScript']
my_tuple[1:3]   # (8, 15)

You can use negative indexes to get slices:

my_string[-4:-2]  # 'el'
my_list[-3:-1]    # ['JavaScript', 'Swift']
my_tuple[-5:-3]   # (8, 15)

You can omit the first index to start the slice from the beginning of the sequence or omit the second index to end the slice at the end of the sequence:

my_string[:2]  # 'He'
my_list[2:]    # ['JavaScript', 'Swift', 'Kotlin']
my_tuple[-3:]  # (16, 23, 42)

Getting the Length of a Sequence

Use the len() function to get a sequence’s length, the number of elements in the sequence:

len(my_string)  # 5
len(my_list)    # 6
len(my_tuple)   # 6

Determining if a Given Item is in a Sequence

Use Python’s in operator to test if a given item is contained within a sequence:

"h" in my_string     # False
"Python" in my_list  # True
42 in my_tuple       # True

Determining How Many Instances of an Item Are in a Sequence

Use the count() method to find out how many instances of an item are in a sequence:

# How many instances of `t`?
my_string.count("l")     # 2
my_list.count("Python")  # 1
my_tuple.count(99)       # 0

Finding the First Instance of an Item in a Sequence

The index() method returns the location of the first instance of a given item in a sequence, but only if it’s actually in the sequence:

my_string.index("l")   # 2
my_list.index("Java")  # 1
my_tuple.index(99)     # ValueError

To avoid errors, use in or count() to test for the item’s presence before using index().

Merging Sequences

The + operator can concatenate two sequences together:

my_string + my_string  # 'HelloHello'

my_list + my_list
# ['Python', 'Java', 'C', 'JavaScript', 'Swift', 'Kotlin', 'Python', 'Java', 'C', 'JavaScript', 'Swift', 'Kotlin']

my_tuple + my_tuple
# (4, 8, 15, 16, 23, 42, 4, 8, 15, 16, 23, 42)

Repeating Sequences

The * operator can repeat a sequence a number of times:

my_string * 3  # 'HelloHelloHello'

my_list * 2
# ['Python', 'Java', 'C', 'JavaScript', 'Swift', 'Kotlin', 'Python', 'Java', 'C', 'JavaScript', 'Swift', 'Kotlin']

my_tuple * 3
# (4, 8, 15, 16, 23, 42, 4, 8, 15, 16, 23, 42, 4, 8, 15, 16, 23, 42)

List Methods

Python’s lists have features beyond what you can do with other sequence types. The most-used ones are listed below.

List method Set the element at index 2 to. "F#" my_list[2] = "F#" Get the element at index 2. Add a new element to the end of the list. Replace the slicewith a new slice . [2:4] ["Rust", "Go"] Remove the last element of the list and return its value. Insert a new element,at index 2. "Dart" element = my_list[2] my_list[2:4] = ["Rust", "Go"] my_list.append("Zig") Clear the list of all elements. my_list.clear() Removefrom the list. "Zig" Results in an error if it’s not in the list. Sort the list in place. Reverse the list in place. my_list.remove("Zig") my_list.reverse() my_list.sort() element = my_list.pop() my_list.insert(2, "Dart") Remove the element at index 2. del my_list[2] Remove the element at index 2 and return its value. element = my_list.pop(2) Description

Merging Two Lists

In Python, there are two ways to merge two lists, list_a and list_b. Both result in a new list starting with the elements of list_a in order, followed by the elements of list_b in order:

# Some flavors lists
flavors = ['vanilla']
sorted_flavors = ['choclate']

# Merging lists with +
lots_of_flavors = flavors + sorted_flavors

# Merging lists with `extend()`
flavors.extend(sorted_flavors)

for Loops

Unlike for loops in many other programming languages, especially those that borrow their syntax from C, Python’s for loops are made not to change the value of an index variable but to iterate over a sequence and execute a block of code for each element in it.

Iterating Over Sequences

Here’s how you iterate over a list with a for loop:

greeting = "Hello"
languages = ["Python", "Java", "C", "JavaScript", "Swift", "Kotlin"]
the_numbers = (4, 8, 15, 16, 23, 42)

for character in greeting:
  print(f"The current character in {character}.")
for language in languages:
  print(f"I'm now reading up on {language}.")
for number in the_numbers:
  print(f"The current number is {number}.")

Like the while loop, Python’s for loop follows Python’s indentation rules.

Traditional(ish) for Loops With the range() Function

Sometimes, you want a loop to perform an action a specific number of times. Many other programming languages provide a for loop to do this, using this syntax:

for (set up counter variable; exit condition ; update counter variable)

Python’s built-in range() function makes it possible to emulate this kind of for loop by generating numbers as a sequence, which is what Python’s for loops are designed to do.

If you provide range() with a single integer n, a stop value, it creates a range object that generates a sequence of numbers beginning with 0, increasing by one each time, and going up to, but not including, the stop value n.

for number in range(5):
  print(number)

If you provide range() with two integers, m, a start value and n, a stop value, it creates a range object that generates a sequence of numbers beginning with the start value m, increasing by one each time, and going up to, but not including, the stop value n.

for number in range(-5, 5):
  print(number)

If you provide range() with three integers, m, a start value, n, a stop value, p, a step value, it creates a range object that generates a sequence of numbers beginning with the start value m, increasing by the step value p each time, and going up to, but not including, the stop value n.

multiples_of_3 = range(-6, 20, 3)
for number in multiples_of_3:
  print(number)

The Magic of enumerate()

Python’s built-in enumerate() function, when wrapped around an object that you can iterate over, adds a counter:

nato_letters = ["alfa", "bravo", "charlie"]
for index, letter in enumerate(nato_letters):
  print(f"{index}. {letter}") # 1. alfa / 2. bravo / 3. charlie

break, continue, and else

Like while, Python’s for loops also use the following keywords:

  • break to exit the loop while in the loop body.
  • continue to go back to the beginning of the loop.
  • else to define a block of code that will execute only when the loop completes “normally” without executing the break statement while in the loop.

Dictionaries

Python’s dictionaries are collections of key-value pairs, where you use a key to access the value associated with that key. They’re like JavaScript objects or what other languages might call associative arrays or hashmaps.

Here’s an example of assigning a dictionary to a variable:

employee = {
  "name": "Alice",
  "level": 5,
  "years": 2,
  "salary": 150_000
}

You can assign an empty dictionary to a variable with a dictionary literal or by instantiating a dictionary instance:

empty_dictionary = {}
another_empty_dictionary = dict()

Dictionary Keys and Values

Dictionary keys can be any type, as long as they are:

  • Immutable: This includes numbers, strings, tuples containing only hashable values, and any object that doesn’t change during its lifetime. Lists, dictionaries, and sets, which the next lesson will cover, can’t be keys.
  • Hashable: Most of Python’s immutable types are also hashable. For example, they’re convertible into an integer value either by Python’s built-in hash() function or a hash() method that it provides. Mutable types like lists, dictionaries, and sets are not hashable.
  • Unique (within the dictionary): Every key in a given dictionary must be unique.

Dictionary values can be any type.

Dictionary Methods and Operators

Here are the commonly-used dictionary methods and operators:

Dictionary method Gets the value for the keyif the key exists, otherwise use the value. "stock_options" "n/a" value = employee.get("stock_options", "n/a") Gets the value for thekey. "name" Results in an error if the key doesn’t exist. Creates a new key-value pair by setting a value for a key that isn’t already in the dictionary. Sets the value for thekey "salary" to 170,000. Removes the key-value pair with the key . "stock_options" Removes the key-value pair with the key "stock_options" and return the value. value = employee["name"] employee["salary"] = 170_000 employee["has_own_parking_space"] = False del employee["stock_options"] value = employee.pop("stock_options") if the dictionary has the key. otherwise. True "name"False "name" in employee Removes all key-value pairs from the dictionary. employee.clear() Description

Getting Keys and Values as Separate Collections

The keys() and values() methods return specialized collection objects containing the dictionary’s keys and values:

crewmember = {
  "name": "Bob",
  "rank": "Captain",
  "years": 3
}

print(f"keys:\n{crewmember.keys()}")  # dict_keys(['name', 'rank', 'years'])
print(f"values:\n{crewmember.values()}")  # dict_values(['Bob', 'Captain', 3])

keys and values aren’t lists but dict_keys and dict_values objects. You can’t access their individual elements until you convert them to lists first:

crewmember.keys[0]  # TypeError: 'dict_keys' object is not subscriptable
keys_list = list(crewmember.keys)
keys_list[0]  # 'name'

While you can’t directly access items in dict_keys and dict_values objects, you can use a for loop to iterate over them:

for key in crewmember.keys():
    print(f"- {key}") # r
for value in crewmember.values():
    print(f"- {value}")

dict_keys and dict_values are dynamic view objects connected to their dictionary. Updating the dictionary also updates its dict_keys and dict_values objects.

Getting a Dictionary’s Keys and Values as Key-Value Tuples

The items() method returns a dynamic view colleobject containing the dictionary’s keys and values as tuples:

For the following code:

employee = {
  "name": "Alice",
  "level": 5,
  "years": 2,
  "salary": 150_000
}

# dict_items([('name', 'Alice'), ('level', 5), ('years', 2), ('salary', 150000)])
print(employee.items())

items() returns a dict_items object, and you can iterate over its contents with a for loop:

for item in employee.items():
  print(item)

You can also convert a dict_items object to a list using the list() initializer.

Determining if a Given Value is in a Dictionary

There’s no built-in way to determine if a dictionary contains a given value. However, you can use the values() method to get its values or the items() method to get its key-value pairs and then see if they contain the value you want.

Regular Expressions

Working with regular expressions in Python requires importing the re module, which is included with the standard Python distribution. Python’s import statement is like those from many other languages:

import re

Working with regular expressions often requires string patterns containing backslash (\) characters that should not be treated as the start of an escape sequence, such as \n for the newline character. Python has r-strings, where backslashes are treated as literal backslash characters, for this purpose:

print(r"\n\t\n\t <-- These are NOT newlines or tabs!")

Python’s re module has these key functions:

Function Attempts to match the pattern at the beginning of the string. Returns aobject if such a match is found, orotherwise. Match None re.match() Scah. Returns aobject if a match is found, orotherwise. Match None ns through the string, looking for the first location where the pattern produces a matc Replaces all occurrences of the pattern in the string with another string. Returns the modified string. Returns all non-overlapping matches of the pattern in the string as a list of strings. If the pattern contains groups, it returns a list of tuples. re.search() re.findall() re.sub() Description

The re modules use a standard set of special characters and expressions for regular expression pattern-matching:

Description Character or expression 0 or more * Match any character 0 or 1 1 or more End of string Start of string “.” + ? $ ^ Match any single character not in the set [^abc] Match any single character in the set Match a word character Match a digit Match exactly times n Match a whitespace character [abc] \d \w Match between and times np {n,p} Match at least times n Create a non-capturing group Create a capturing group {n,} (pattern) (?:pattern) {n} \s

You’ll see an example of regular expressions in the demo that follows this lesson.

Boolean Evaluations: Truthy and Falsy

Now that the major data types have been covered, it’s time to talk about how they’re evaluated as True or False by decision-making statements like if/elif/else and while and for loops.

It’s clear that the following values and expressions evaluate as True or False:

  • True
  • False
  • True and True or False
  • some_value > 5
  • some_value > 5 and some_other_value != "stop"

It’s not obvious that all values and expressions also evaluate as True or False. In Python, there are the concepts of truthy and falsy where:

  • truthy refers to a value that evaluates to True.
  • falsy refers to a value that evaluates to False.

The general rule is that if a value indicates absence, emptiness, or zero, it’s falsy and evaluates as False. These include:

  • None
  • Any numerical value for zero
  • Any empty sequence or collection

On the other hand, if a value indicates presence, non-emptiness, or a non-zero value, it’s truthy and evaluates as True. These include:

  • Any object that isn’t None and meets one of the criteria below:
    • Any numerical value that isn’t zero
    • Any string containing at least one character
    • Any sequence or collection containing at least one item

Basic Functions

Here’s a definition of the simplest type of function — the type that takes any arguments and doesn’t return any values:

def say_hello():
  """Say hello to the user.""" # This is a docstring (see below)
  print("Hey there! Welcome.")

Function definitions start with the def keyword, short for define, followed by the function name, which in this case is say_hello(). Like many other programming languages, the function name ends with parentheses.

When you run a code cell with one or more function definitions, those functions won’t execute unless some other code in that cell calls them. Once you run a code cell containing function definitions, those functions will remain “in scope” and available to any other code cells for as long as Jupyter runs.

Docstrings

You may have been wondering about the first line of say_hello()’s body, shown below without the comment that followed it:

  """Say hello to the user"""

That’s a docstring, short for documentation string, and it’s a triple-quoted string literal on the first line in a function, method, class, or module. They’re for providing a concise explanation of the purpose, behavior, and usage of the function, method, class, or module.

Note: Lesson 2 of this course will cover defining methods and classes.

Docstrings are more than mere comments. As long as you put the docstring on the first line of the function, method, class, or module, it becomes accessible via the special built-in __doc__ property of those objects!

The following code prints the docstring for say_hello():

print(say_hello.__doc__)  # "Say hello to the user."

Note: __doc__ is part of a family of special Python properties and methods that begins and ends with double underscores (__). They’re called dunder properties and dunder methods, where “dunder” is a shortening of “double underscore”. __doc__ can be pronounced “dunder doc.”

Passing Values to a Function

Python functions can take single arguments, as shown in the code below:

def say_oh_hi_with_name(name):
  print(f"Oh hi, {name}!")

Like other languages’ functions, Python functions can also take multiple arguments:

def say_oh_hi_multiple_times(name, repetitions):
  for count in range(repetitions):
    print(f"Oh hi, {name}!")

Python functions support keyword arguments, where the function caller can specify which arguments are for which parameters:

say_oh_hi_multiple_times(repetitions=3, name="Lisa")

Keyword arguments, while they make a function call more verbose, have a couple of advantages:

  • It’s clear which argument corresponds with which parameter.
  • It lets you call a function without worrying about the order of arguments.

Finally, Python allows for default values for function parameters:

def say_oh_hi_multiple_times(name, repetitions=2):
  for count in range(repetitions):
    print(f"Oh hi, {name}!")

Note: For this course, the term argument refers to data passed to a function when calling it, and the term parameter refers to a variable in a function that stores the value of an argument passed to the function.

Returning a Value From a Function

Just like other programming languages’ functions, programs exit a function and return a single value with the return statement:

def create_greeting(name):
  return f"Greetings and salutations, {name}!"

Every function returns a value, but those that don’t have return statements return the value None.

Naming Functions

As with variables, there are generally accepted rules for naming functions in Python. The key points are:

  • Use snake case! Function names should be all lowercase, with multi-word names using underscores to separate words. For example, use names like my_function() and not myFunction().
  • Use only letters, numbers, and underscores in function names.
  • Function names can start with a letter or underscore. They shouldn’t start with numbers.
  • Names that begin or end with double underscores have special meaning in Python. Don’t use these names unless you know what you’re doing.
  • Don’t use Python’s reserved keywords as function names.

Empty Blocks

In writing a program, you’ll often start with empty placeholder blocks of code that you intend to fill later.

Consider this JavaScript function:

// JavaScript
function placeholder() {
  // TODO: Implement later
}

The body of the returnNothing() function above is an empty code block, where the start and end of the block are clearly marked by visible characters, { and }, with nothing but whitespace and comments between them.

Python’s code blocks are marked by indentation, created using invisible characters. Because of this, there’s no definitive way to tell the difference between an empty block of code and a missing block of code.

If you try to define an empty function like this:

def placeholder():
  # TODO: Implement later

You’ll get an error: SyntaxError: incomplete input

To solve this problem and allow you to create empty placeholder blocks, Python has the pass keyword, which specifies an empty code block:

def placeholder():
  # TODO: Implement later
  pass

In the next demo, you’ll have some hands on with all you’ve learned about python

See forum comments
Download course materials from Github
Previous: JupyterLab Demo Next: ELIZA Demo