# Python Programming
---
## Module 1
Python is an **interpreted language**.
If you want to program in Python, you'll need the **Python interpreter**. You won't be able to run your code without it.
Due to historical reasons, languages designed to be utilized in the interpretation manner are often called **scripting languages**, while the source programs encoded using them are called **scripts**.
**CPython** is the most influential Python among all Pythons.
**Cython** is one of a possible number of solutions to the the lack of efficiency. It automatically translates the Python code (clean and clear, but not too swift) into "C" code (complicated and talkative, but agile).
## Module 2
### 2.1.1 Key takeaways
1. The *print()* function is a **built-in** function. It prints/outputs a specified message to the screen/consol window.
2. **Built-in functions**, contrary to user-defined functions, are always available and don't have to be imported. Python 3.7.1 comes with 69 built-in functions. You can find their full list provided in alphabetical order in the Python Standard Library.
3. To call a function (**function invocation**), you need to use the function name followed by parentheses. You can pass arguments into a function by placing them inside the parentheses. You must separate arguments with a comma, e.g., print("Hello,", "world!"). An "empty" print() function outputs an empty line to the screen.
4. Python strings are delimited with **quotes, e.g., "I am a string", or 'I am a string, too'.**
5. Computer programs are collections of **instructions**. An instruction is a command to perform a specific task when executed, e.g., to print a certain message to the screen.
6. In Python strings the **backslash** (**\\**) is a special character which announces that the next character has a different meaning, e.g.,**\n** (**the newline character**) starts a new output line.
7. **Positional arguments** are the ones whose meaning is dictated by their position, e.g., the second argument is outputted after the first, the third is outputted after the second, etc.
8. **Keyword arguments** are the ones whose meaning is not dictated by their location, but by a special word (keyword) used to identify them.
9. The *end* and *sep* parameters can be used for formatting the output of the *print()* function. The *sep* parameter specifies the separator between the outputted arguments (e.g., print("H", "E", "L", "L", "O", sep="-"), whereas the end parameter specifies what to print at the end of the print statement.
### 2.1.2 Key takeaways
1. **Literals** are notations for representing some fixed values in code. Python has various types of literals - for example, a literal can be a number (numeric literals, e.g., 123), or a string (string literals, e.g., "I am a literal.").
2. The **binary system** is a system of numbers that employs 2 as the base. Therefore, a binary number is made up of 0s and 1s only, e.g., 1010 is 10 in decimal.
Octal and hexadecimal numeration systems, similarly, employ 8 and 16 as their bases respectively. The hexadecimal system uses the decimal numbers and six extra letters.
3. **Integers** (**ints**) are one of the numerical types supported by Python. They are numbers written without a fractional component, e.g., 256, or -1 (negative integers).
4. **Floating-point** numbers (**floats**) are another one of the numerical types supported by Python. They are numbers that contain (or are able to contain) a fractional component, e.g., 1.27.
5. To encode an apostrophe or a quote inside a string you can either use the escape character, e.g., *'I\\'m happy.'*, or open and close the string using an opposite set of symbols to the ones you wish to encode, e.g., *"I'm happy."* to encode an apostrophe, and *'He said "Python", not "typhoon"'* to encode a (double) quote.
6. **Boolean values** are the two constant objects True and False used to represent truth values (in numeric contexts 1 is True, while 0 is False.
EXTRA
There is one more, special literal that is used in Python: the None literal. This literal is a so-called NoneType object, and it is used to represent the **absence of a value**. We'll tell you more about it soon.
### 2.1.3 Key takeaways
1. An **expression** is a combination of values (or variables, operators, calls to functions) which evaluates to a value, e.g., 1 + 2.
2. **Operators** are special symbols or keywords which are able to operate on the values and perform (mathematical) operations, e.g., the **\*** operator multiplies two values: x *y.
3. **Arithmetic operators** in Python:
**+** Addition)
**-** Subtraction)
**\*** Multiplication
**/** Classic division. The result is **always a float**.
**%** Modulus - divides left operand by right operand and returns the remainder of the operation, e.g., 5 % 2 = 1). In Python 3, the result of using the ‘%’ operator always yields the same sign as its second operand or zero.
**\*** **\*** Exponentiation - left operand raised to the power of right operand, e.g., 2 ** 3 = 2 * 2 * 2 = 8
**//** (floor/integer division - returns a number resulting from division, but **rounded down to the nearest whole number**, e.g., 3 // 2.0 = 1.0)
4. A **unary** operator is an operator with only one operand, e.g., -1, or +3.
5. A **binary** operator is an operator with two operands, e.g., 4 + 5, or 12 % 5.
6. Some operators act before others - the **hierarchy of priorities**:
unary + and - have the highest priority
then: \**, *, /, and %, and then the lowest priority: binary + and -.
() Parentheses
** Exponentiation
\* Multiplication
/ Division
\+ Addition
– Subtraction
7. Subexpressions in **parentheses** are always calculated first, e.g., 15 - 1 * (5 * (1 + 2)) = 0.
8. The **exponentiation** operator uses right-sided binding, e.g., 2 ** 2 ** 3 = 256.
### 2.1.4 Key takeaways
1. A **variable** is a named location reserved to store values in the memory. A variable is created or initialized automatically when you assign a value to it for the first time. (2.1.4.1)
2. Each variable must have a **unique name** - an identifier. A legal identifier name must be a non-empty sequence of characters, must begin with the underscore(_), or a letter, and it cannot be a Python keyword. The first character may be followed by underscores, letters, and digits. Identifiers in Python are case-sensitive. (2.1.4.1)
- Alice != ALICE
- Python lets you use not only Latin letters but also characters specific to languages that use other alphabets.
- Can't be named with a reserved keyword (False, True, and, elif, while, y las otras). But since it is case-sensitive, you can use i.e. "While" or "And".
3. Python is a **dynamically-typed language**, which means you don't need to declare variables in it. (2.1.4.3) To assign values to variables, you can use a simple assignment operator in the form of the equal (=) sign, i.e., var = 1.
4. You can also use compound assignment operators (**shortcut operators**) to modify values assigned to variables, e.g., var += 1, or var /= 5 * 2. (2.1.4.8)
5. You can assign new values to already existing variables using the assignment operator or one of the compound operators (2.1.4.5)
Siendo "op" un operador de dos argumentos:
*variable = variable op expression*
es lo mismo que:
*variable op= expression*
i = i + 2 * j ⇒⇒⇒⇒⇒ i **+=** 2 * j
6. You can combine text and variables using the + operator, and use the print() function to output strings and variables, e.g.: (2.1.4.4)
var = "007"
print("Agent " + var)
### 2.1.6 Key Takeaways.
1. The **print()** function sends data to the console, while the **input()** function gets data from the console.
2. The input() function comes with an optional parameter: the **prompt string**. It allows you to write a message before the user input, e.g.:
name = input("Enter your name: ")
print("Hello, " + name + ". Nice to meet you!")
3. The result of the input() function is *a string*. You can add strings to each other using the **concatenation** (+) operator:
num1 = input("Enter the first number: ") # Enter 12
num2 = input("Enter the second number: ") # Enter 21
print(num1 + num2) # the program returns 1221
It is not commutative. You must ensure that both its arguments are strings.
4. You can also multiply (* - **replication**) strings, e.g.:
myInput = ("Enter something: ") # Example input: hello
print(myInput * 3) # Expected output: hellohellohello
A number less than or equal to zero produces an empty string.
### 3.1.1 Key Takeaways
1. The **comparison** (or the so-called *relational*) operators are used to compare values. The table below illustrates how the comparison operators work, assuming that x = 0, y = 1, and z = 0:


2. **conditional statement** to execute some code only if a certain condition is met:
-- a single **if** statement:
x = 10
if x == 10: # condition
print("x is equal to 10") # executed if the condition is True
-- a series of **if** statements: (Each **if** statement is tested separately).
x = 10
if x > 5: # condition one
print("x is greater than 5") # executed if condition one is True
if x < 10: # condition two
print("x is less than 10") # executed if condition two is True
if x == 10: # condition three
print("x is equal to 10") # executed if condition three is True
-- an **if-else** statement:
x = 10
if x < 10: # condition
print("x is less than 10") # executed if the condition is True
else:
print("x is greater than or equal to 10") # executed if the condition is False
-- a series of **if** statements followed by an **else**: (Each **if** is tested separately. The body of **else** is executed if the last **if** is False.)
x = 10
if x > 5: # True
print("x > 5")
if x > 8: # True
print("x > 8")
if x > 10: # False
print("x > 10")
else:
print("else will be executed")
-- The **if-elif-else** statement: (If the condition for **if** is False, the program checks the conditions of the subsequent **elif** blocks - the first **elif** block that is True is executed. If all the conditions are False, the **else** block will be executed.)
x = 10
if x == 10: # True
print("x == 10")
if x > 15: # False
print("x > 15")
elif x > 10: # False
print("x > 10")
elif x > 5: # True
print("x > 5")
else:
print("else will not be executed")
-- Nested conditional statements, e.g.:
x = 10
if x > 5: # True
if x == 6: # False
print("nested: x == 6")
elif x == 10: # True
print("nested: x == 10")
else:
print("nested: else")
else:
print("else")
### 3.1.2 Key Takeaways
-- 1. There are two types of loops in Python: **while** and **for**:
--- The _while_ loop executes a statement or a set of statements as long as a specified boolean condition is true, e.g.:
# Example 1
while True:
print("Stuck in an infinite loop.")
# Example 2
counter = 5
while counter > 2:
print(counter)
counter -= 1
--- The _for_ loop executes a set of statements many times; it's used to iterate over a sequence (e.g., a list, a dictionary, a tuple, or a set - you will learn about them soon) or other objects that are iterable (e.g., strings). You can use the for loop to iterate over a sequence of numbers using the built-in range function. Look at the examples below:
# Example 1
word = "Python"
for letter in word:
print(letter, end="*")
# Example 2
for i in range(1, 10):
if i % 2 == 0:
print(i)
-- 2. You can use the _break_ and _continue_ statements to change the flow of a loop:
--- You use _break_ to exit a loop, e.g.:
text = "OpenEDG Python Institute"
for letter in text:
if letter == "P":
break
print(letter, end="")
--- You use _continue_ to skip the current iteration, and continue with the next iteration, e.g.:
text = "pyxpyxpyx"
for letter in text:
if letter == "x":
continue
print(letter, end="")
--- 3. The _while_ and _for_ loops can also have an _else_ clause in Python. The _else_ clause executes after the loop finishes its execution as long as it has not been terminated by _break_, e.g.:
n = 0
while n != 3:
print(n)
n += 1
else:
print(n, "else")
print()
for i in range(0, 3):
print(i)
else:
print(i, "else")
-- 4. The _range()_ function generates a sequence of numbers. It accepts integers and returns range objects. The syntax of _range()_ looks as follows: _range(start, stop, step)_, where:
--- _start_ is an optional parameter specifying the starting number of the sequence (0 by default)
--- _stop_ is an optional parameter specifying the end of the sequence generated (it is not included),
--- _step_ is an optional parameter specifying the difference between the numbers in the sequence (1 by default.)
-- Example code:
for i in range(3):
print(i, end=" ") # outputs: 0 1 2
for i in range(6, 1, -2):
print(i, end=" ") # outputs: 6, 4, 2
### 3.1.3 Key Takeaways
1. Python supports the following logical operators:
**and** → if both operands are true, condition is true.
**or** → if any of the operands are true, condition is true.
*not** → returns false if the result is true, and returns true if the result is false, e.g., not True is False.
2. You can use bitwise operators to manipulate single bits of data. To illustrate the meaning of bitwise operators in Python, use following sample data:
x = 15, which is 0000 1111 in binary,
y = 16, which is 0001 0000 in binary.
**&** does a bitwise **and**, e.g., x & y = 0, which is 0000 0000 in binary,
**|** does a bitwise **or**, e.g., x | y = 31, which is 0001 1111 in binary,
**˜** does a bitwise **not**, e.g., ˜ x = 240, which is 1111 0000 in binary,
**^** does a bitwise **xor**, e.g., x ^ y = 31, which is 0001 1111 in binary,
**>>** does a bitwise **right shift**, e.g., y >> 1 = 8, which is 0000 1000 in binary,
**<<** does a bitwise **left shift**, e.g., y << 3 = , which is 1000 0000 in binary,
### 3.1.4
#### Methods vs. Functions
A **method is a specific kind of function** - it behaves like a function and looks like a function, but differs in the way in which it acts, and in its invocation style.
A **function doesn't belong to any data** - it gets data, it may create new data and it (generally) produces a result.
A method does all these things, but is also able to **change the state of a selected entity**.
A method is owned by the data it works for, while a function is owned by the whole code.
#### Swap variables
A convenient way to swap variables is:
variable1 = 1
variable2 = 2
variable1, variable2 = variable2, variable1
Now you can easily **swap** the list's elements to reverse their order:
myList = [10, 1, 8, 3, 5]
myList[0], myList[4] = myList[4], myList[0]
myList[1], myList[3] = myList[3], myList[1]
print(myList)
With a list containing 100 elements, we can use the _for_ loop to do the same thing automatically:
myList = [10, 1, 8, 3, 5]
length = len(myList)
for i in range(length // 2):
myList[i], myList[length - i - 1] = myList[length - i - 1], myList[i]
print(myList)
Note:
1. We've assigned the length variable with the current list's length (this makes our code a bit clearer and shorter)
2. We've launched the for loop to run through its body length // 2 times (this works well for lists with both even and odd lengths, because when the list contains an odd number of elements, the middle one remains untouched)
3. We've swapped the ith element (from the beginning of the list) with the one with an index equal to (length - i - 1) (from the end of the list); in our example, for i equal to 0 the (l - i - 1) gives 4; for i equal to 1, it gives 3 - this is exactly what we needed.
### 3.1.4 Key Takeaways
1. The **list** is a type of data in Python used to store multiple objects. It is an ordered and mutable collection of comma-separated items between square brackets, e.g.:
myList = [1, None, True, "I am a string", 256, 0]
2. Lists can be **indexed and updated**, e.g.:
myList = [1, None, True, 'I am a string', 256, 0]
print(myList[3]) # outputs: I am a string
print(myList[-1]) # outputs: 0
myList[1] = '?'
print(myList) # outputs: [1, '?', True, 'I am a string', 256, 0]
myList.insert(0, "first")
myList.append("last")
print(myList) # outputs: ['first', 1, '?', True, 'I am a string', 256, 0, 'last']
3. Lists can be **nested**, e.g.:
myList = [1, 'a', ["list", 64, [0, 1], False]].
You will learn more about nesting in module 3.1.7 - for the time being, we just want you to be aware that something like this is possible, too.
4. List elements and lists can be **deleted**, e.g.:
myList = [1, 2, 3, 4]
del myList[2]
print(myList) # outputs: [1, 2, 4]
del myList # deletes the whole list
Again, you will learn more about this in module 3.1.6 - don't worry.
5. Lists can be iterated through using the for loop, e.g.:
myList = ["white", "purple", "blue", "yellow", "green"]
for color in myList:
print(color)
6. The **len()** function may be used to check the list's length, e.g.:
myList = ["white", "purple", "blue", "yellow", "green"]
print(len(myList)) # outputs 5
del myList[2]
print(len(myList)) # outputs 4
7. A typical **function** invocation: _result = function(arg)_
A typical **method** invocations: _result = data.method(arg)_
### 3.1.5 Key Takeaways
1. You can use the **sort()** method to sort elements of a list:
lst = [5, 3, 1, 2, 4]
print(lst)
lst.sort()
print(lst) # outputs: [1, 2, 3, 4, 5]
2. There is also a list method called **reverse()**, which you can use to reverse the list:
lst = [5, 3, 1, 2, 4]
print(lst)
lst.reverse()
print(lst) # outputs: [4, 2, 1, 3, 5]
### 3.1.6 Key takeaways
1. If you have a list l1, then the following assignment: l2 = l1 does not make a copy of the l1 list, but makes the variables l1 and l2 **point to one and the same list in memory**:
vehiclesOne = ['car', 'bicycle', 'motor']
print(vehiclesOne) # outputs: ['car', 'bicycle', 'motor']
vehiclesTwo = vehiclesOne
del vehiclesOne[0] # deletes 'car'
print(vehiclesTwo) # outputs: ['bicycle', 'motor']
2. If you want to copy a list or part of the list, you can do it by performing **slicing**:
colors = ['red', 'green', 'orange']
copyWholeColors = colors[:] # copy the whole list
copyPartColors = colors[0:2] # copy part of the list
3. You can use **negative indices** to perform slices, too:
sampleList = ["A", "B", "C", "D", "E"]
newList = sampleList[2:-1]
print(newList) # outputs: ['C', 'D']
4. The **start** and **end** parameters are optional when performing a slice: **list[start:end]**:
myList = [1, 2, 3, 4, 5]
sliceOne = myList[2: ]
sliceTwo = myList[ :2]
sliceThree = myList[-2: ]
print(sliceOne) # outputs: [3, 4, 5]
print(sliceTwo) # outputs: [1, 2]
print(sliceThree) # outputs: [4, 5]
5. You can delete slices using the **del** instruction:
myList = [1, 2, 3, 4, 5]
del myList[0:2]
print(myList) # outputs: [3, 4, 5]
del myList[:]
print(myList) # deletes the list content, outputs: []
6. You can test if some items exist in a list or not using the keywords **in** and **not in**:
myList = ["A", "B", 1, 2]
print("A" in myList) # outputs: True
print("C" not in myList) # outputs: True
print(2 not in myList) # outputs: False
#### 3.1.7 Key takeaways
1. List comprehension allows you to create new lists from existing ones in a concise and elegant way. The syntax of a list comprehension looks as follows:
[expression for element in list if conditional]
which is actually an equivalent of the following code:
for element in list:
if conditional:
expression
Here's an example of a list comprehension - the code creates a five-element list filled with with the first five natural numbers raised to the power of 3:
cubed = [num ** 3 for num in range(5)]
print(cubed) # outputs: [0, 1, 8, 27, 64]
2. You can use **nested lists** in Python to create **matrices** (i.e., two-dimensional lists). For example:

# A four-column/four-row table - a two dimensional array (4x4)
table = [[":(", ":)", ":(", ":)"],
[":)", ":(", ":)", ":)"],
[":(", ":)", ":)", ":("],
[":)", ":)", ":)", ":("]]
print(table)
print(table[0][0]) # outputs: ':('
print(table[0][3]) # outputs: ':)'
3. You can nest as many lists in lists as you want, and therefore create n-dimensional lists, e.g., three-, four- or even sixty-four-dimensional arrays. For example:

cube = [[[':(', 'x', 'x'],
[':)', 'x', 'x'],
[':(', 'x', 'x']],
[[':)', 'x', 'x'],
[':(', 'x', 'x'],
[':)', 'x', 'x']],
[[':(', 'x', 'x'],
[':)', 'x', 'x'],
[':)', 'x', 'x']]]
print(cube)
print(cube[0][0][0]) # outputs: ':('
print(cube[2][2][0]) # outputs: ':)'
---
- [ ] Seguir en: Unidad
- [ ] Seguir en: Video 25