## Python Workshop: Basics I

[If you are looking for the Python, Pycharm & packages installation guide, it's here.]

Based on:

this git of Zhiya Zuo

&

tutorials from tutorialspoint

## Introduction

Python is an interpreted, high-level, general-purpose programming language. Created by Guido van Rossum and first released in 1991, Python's design philosophy emphasizes code readability. The language construction and object-oriented approach aims to help programmers write clear, logical code for small and large-scale projects. [Wikipedia]

• Python is Interpreted - Python is processed at runtime by the interpreter. You do not need to compile your program before executing it. This is similar to PERL, PHP and MATLAB.

• Python is Interactive - You can actually sit at a Python prompt and interact with the interpreter directly to write your programs.

• Python is Object-Oriented - Python supports Object-Oriented style or technique of programming that encapsulates code within objects.

• Popular Language for Data Analysis - Most of the time, you will need external packages to assist data analyses.

### PyCharm

Pycharm is currently (2019) the most commonly used IDE (Integrated development environment) for programing in python.

### Jupyter notebook

Jupyter is an easy way to merge code and explanations in a beautiful way.

The easiest way to interact with such notebook (.ipynb) is with google colab. There you can run each cell independently or all cells combined through 'Runtime' section or the play button.

the main disadvantage of google colab is that debugging there is problematic.

### Naming convention

There are two commonly used style in programming:

1. camelCase
2. snake_case or lower_case_with_underscore

Always make sure you use one convention consistently across one project.

All variable (function and class) names must start with a letter or underscore (_). You can include numbers, but it can't be the first char.

In [1]:
myStringHere = 'my string'  # valid
x = 3  # valid
x_3 = "xyz"  # valid
# 3_x = "456" # invalid. Numbers cannot be in the first position.


### Lines and indentation

Python doesn't need braces to indicate blocks of code for class and function definitions or flow control. Blocks of code are denoted by line indentation (Tabs), which is rigidly enforced.

In [2]:
if True:
print("True")
else:
print("False")

True


### Indexing

Indexing in python start from 0 (like c, unlike Matlab). Accessing a range of list/array is, by convetion, some_list[start_ind:end_ind_minus_one]

In [3]:
x = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

y = x[0] #get element 0
print("y = " + str(y))

y = x[2] #get element 2
print("y = " + str(y))

y = x[0:2] #get elements 0,1
print("y = " + str(y))

y = x[:2]  # same as [0:2]
print("y = " + str(y))

y = x[3:10]
print("y = " + str(y))

y = x[3:]  # same as above
print("y = " + str(y))

y = 0
y = 2
y = [0, 1]
y = [0, 1]
y = [3, 4, 5, 6, 7, 8, 9]
y = [3, 4, 5, 6, 7, 8, 9]


You can also go to last element easily:

In [4]:
y = x[-1]  # last element
print("y = " + str(y))

y = x[:-1]  # all until the last element - noninclusive
print("y = " + str(y))

y = x[-3:]  # last three elements
print("y = " + str(y))

y = 9
y = [0, 1, 2, 3, 4, 5, 6, 7, 8]
y = [7, 8, 9]


Step size is also an option:

In [5]:
y = x[0:3:2]  # only evens until 3 noninclusive
print("y = " + str(y))

y = x[1:5:2]  # only odds until 5 noninclusive
print("y = " + str(y))

y = x[::3]  # +3 step size - entire list
print("y = " + str(y))

y = x[::-1]  # reverse!
print("y = " + str(y))

y = [0, 2]
y = [1, 3]
y = [0, 3, 6, 9]
y = [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]


## Primitives

In this section, we go over some common primitive data types in Python. While the word primitive looks obscure, we can think of it as the most basic data type that cannot be further decomposed into simpler ones.

### Numbers

numbers without fractional partsare called integer. In Python, they are abbreviated as int

In [6]:
x = 3
type(x)

Out[6]:
int

numbers with fractional part are floating point numbers. They are named float in Python.

In [7]:
y = 3.0
type(y)

Out[7]:
float

We can apply arithmetic to these numbers. However, one thing we need to be careful about is type conversion. See the example below.

In [8]:
z = 2 * x  # int times int
type(z)

Out[8]:
int
In [9]:
z = y ** x  # int to the power float
print(z)
type(z)

27.0

Out[9]:
float
In [10]:
z = x / 2  # what will happen when dividing two ints?
z

Out[10]:
1.5

### Boolean

Boolean type comes in handy when we need to check conditions. For example:

In [11]:
my_error = 1.6
compare_result = my_error < 0.1
print(compare_result)
print(type(compare_result))

False
<class 'bool'>


There are two valid Boolean values: True and False. We can also think of them as 1 and 0, respectively.

In [12]:
my_error > 0

Out[12]:
True

When we use Boolean values for arithmetic operations, they will become 1/0 automatically

In [13]:
(my_error > 0) + 2

Out[13]:
3

### Strings

In Python, we use str type for storing letters, words, and any other characters.

To initialize a string variable, you can use either double or single quotes.

In [14]:
my_str1 = "see you"
print(my_str1)
print(type(my_str1))

my_str2 = 'see you later'
print(my_str2)
print(type(my_str2))

see you
<class 'str'>
see you later
<class 'str'>


We can also use + to concatenate different strings

In [15]:
my_str1 + ' tomorrow'

Out[15]:
'see you tomorrow'

One way of formatting strings is equivalent to c language:

In [16]:
print("1/3 is approximately %.2f" % (1/3))  # %f for floating point number
print(" '%s' != '%s'" % (my_str1, my_str2))  # %s for string

1/3 is approximately 0.33
'see you' != 'see you later'


you can also simply do string concatenation:

In [17]:
print("Printing a string: " + my_str1 + ", and printing a number: " + str(3))

Printing a string: see you, and printing a number: 3


str is an iterable object, meaning that we can iterate through each individual character:

In [18]:
print(my_str1[0])
print(my_str1[2:6])

s
e yo


## Data Structures

In this section, we discuss some nonprimitive data structures in Python.

### List

Initialize a list with brackets. You can store anything in a list, even if they are different types

In [19]:
a_list = [1, 2, 3]  # commas to separate elements
print("Length of a_list is: %i" % (len(a_list)))
print("The 3rd element of a_list is: %s" %
(a_list[2]))  # Remember Python starts with 0
print("The last element of a_list is: %s" % (a_list[-1]))  # -1 means the end
print("The sum of a_list is %.2f" % (sum(a_list)))

Length of a_list is: 3
The 3rd element of a_list is: 3
The last element of a_list is: 3
The sum of a_list is 6.00


We can put different types in a list

In [20]:
b_list = [20, True, "good", "good"]
print(b_list)

[20, True, 'good', 'good']


Update a list: pop, remove, append, extend

In [21]:
print(a_list)
print("Pop %i out of a_list" % a_list.pop(1))  # pop the value of an index
print(a_list)

[1, 2, 3]
Pop 2 out of a_list
[1, 3]

In [22]:
print("Remove the string good from b_list:")
b_list.remove("good")  # remove a specific value (the first one in the list)
print(b_list)

Remove the string good from b_list:
[20, True, 'good']

In [23]:
a_list.append(10)
print("After appending a new value, a_list is now: %s" % (str(a_list)))

After appending a new value, a_list is now: [1, 3, 10]


merge a_list and b_list:

In [24]:
a_list.extend(b_list)
print("Merging a_list and b_list: %s" % (str(a_list)))

Merging a_list and b_list: [1, 3, 10, 20, True, 'good']


We can also use + to concatenate two lists

In [25]:
a_list + b_list

Out[25]:
[1, 3, 10, 20, True, 'good', 20, True, 'good']

### Tuple

Tuple is a special case of list whose elements cannot be changed (immutable).

Initialize a tuple with parenthesis:

In [26]:
a_tuple = (1, 2, 3, 10)
print(a_tuple)
print("First element of a_tuple: %i" % a_tuple[0])

(1, 2, 3, 10)
First element of a_tuple: 1


You can't change the values of a tuple:

In [27]:
# a_tuple[0] = 5


In order to create a single value tuple, you need to add a ','

In [28]:
a_tuple = (1)  # this would create a int type
print(type(a_tuple))
b_tuple = (1,)  # this would create a tuple type, take note of the comma.
print(type(b_tuple))

<class 'int'>
<class 'tuple'>


### Dictionary

Dictionary: key-value pairs

Initialize a dict by curly brackets {}

In [29]:
d = {}  # empty dictionary
# add a key-value by using bracket (key). You can put anything in key/value.
d[1] = "1 value"
print(d)

{1: '1 value'}

In [30]:
# Use for loop to add values
for index in range(2, 10):
d[index] = "%i value" % index
print(d)
print("All the keys: " + str(d.keys()))
print("All the values: " + str(d.values()))

{1: '1 value', 2: '2 value', 3: '3 value', 4: '4 value', 5: '5 value', 6: '6 value', 7: '7 value', 8: '8 value', 9: '9 value'}
All the keys: dict_keys([1, 2, 3, 4, 5, 6, 7, 8, 9])
All the values: dict_values(['1 value', '2 value', '3 value', '4 value', '5 value', '6 value', '7 value', '8 value', '9 value'])

In [31]:
for key in d:
print("Key is: %i, Value is : %s" % (key, d[key]))

Key is: 1, Value is : 1 value
Key is: 2, Value is : 2 value
Key is: 3, Value is : 3 value
Key is: 4, Value is : 4 value
Key is: 5, Value is : 5 value
Key is: 6, Value is : 6 value
Key is: 7, Value is : 7 value
Key is: 8, Value is : 8 value
Key is: 9, Value is : 9 value


### Side note: mutable Vs. immutable objects

Everything in Python is an object, and object can be either mutable (changeable after creation) or immutable. Almost all Python objects are mutable, except from the primitives (numbers, booleans, strings) and tuples

Why it's interesting? because when you reference a new variable, it's always soft link (like a shortcut in windows), and if you change a mutable object, it reference changes too! Something that can cause big bugs!

In [32]:
# mutable object: no problem
a = 'Hello'
b = a
b = b + ' World!'
print(a)
print(b)

Hello
Hello World!

In [33]:
# immutable object: big problem
a = ['Hello']
b = a
b[0] = 'World!'
print(a)
print(b)

['World!']
['World!']

In [34]:
# use .copy() to overcome this:

c = a.copy()
c[0] = "other world"
print(a)
print(c)

['World!']
['other world']