Skip to content

Commit efd175b

Browse files
committed
Add modules, exceptions, and files sections
0 parents  commit efd175b

File tree

9 files changed

+543
-0
lines changed

9 files changed

+543
-0
lines changed
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
# Using Python modules
2+
3+
Standard Python provides a very wide range of functions and objects that allow you to easily perform various advanced operations. This makes writing Python programs much easier than using other popular programming languages ​​such as C++. The most common and frequently used functions (e.g. `print`, `input`, `len`, and `range`) are always available. They are so-called _built-in_ functions. However, the vast majority are grouped in _packages_ and _modules_ that must be _imported_ when needed.
4+
5+
To import a module, you should issue the command:
6+
7+
```python
8+
import module_name
9+
```
10+
An example would be a module `random` that provides functions for generating pseudo-random numbers. We import it with the command
11+
12+
```python
13+
import random
14+
```
15+
This command does not make all functions and objects defined in this module directly available. To get to them you have to use notation `module_name.function(...)`. For example, to generate a random number in the range [_a_, _b_] we can use the function `uniform` from the module `random`:
16+
17+
```python
18+
random_number = random.uniform(-1, 1)
19+
```
20+
A complete list of built-in Python modules and functions can be found in the [documentation](https://docs.python.org/3/library/index.html). In particular, it is worth reviewing the module descriptions [sys](https://docs.python.org/3/library/sys.html) and [os](https://docs.python.org/3/library/os.html) which contain a set of basic functions and objects for using the operating system tools. For example, you can check the current version of Python with:
21+
22+
```python
23+
import sys
24+
25+
print(sys.version)
26+
```
27+
In this case, `sys.version` is not a function, but just a string. However, it is an object defined inside a module.
28+
29+
Modules may contain other modules nested in a hierarchical manner: any module may contain sub-modules (master modules are then called _packages_). To import the selected sub-module, use the command `import package.submodule`. There can be any number of nesting levels: in such case, enter all of them, separating subsequent levels with a dot. Useful functions and objects can be found both in the top module and in submodules. Submodules are used to group a set of functions. For example, a module [os](https://docs.python.org/3/library/os.html) contains functions that allow you to perform various operations on files on the disk, and a module [os.path](https://docs.python.org/3/library/os.path.html) provides functions that allow you to manipulate filesystem paths:
30+
31+
```python
32+
import os.path
33+
34+
print(os.path.join('directory', 'file.txt'))
35+
```
36+
## Simplified module loading
37+
The fact that the command `import` only defines the name of the module keeps the program code in order. In particular, there is no problem with functions with the same names in several modules. As their use requires the name of the module before the dot, there will be no collisions between them. However, sometimes—especially with nested modules—this can be cumbersome (e.g. when you have to frequently type `moduleA.moduleB.moduleC.function1()`, `moduleA.moduleB.moduleC.function2()` etc.) There are two methods to deal with this. The first one is to import any module (also nested) with its own name, using the keyword `as`:
38+
39+
```python
40+
import moduleA.moduleB.moduleC as mc
41+
```
42+
Then instead of `moduleA.moduleB.moduleC.function1()` we can just write `mc.function1()`, which is much shorter. For example
43+
44+
```python
45+
import os.path as p
46+
47+
print(p.join('directory', 'file.txt'))
48+
```
49+
Remember that the names we give the imported modules should be meaningful: even if it is a one- or two-letter abbreviation (in the example above, the first letter of the submodule name `path` was used).
50+
51+
The second method allows you to directly load selected functions and other objects from the module and access them without prefixing them with the module name. For this, we use the following syntax:
52+
53+
```python
54+
from module_name import function
55+
```
56+
`module_name` can have the form _`package.submodule`_, as in the previously described cases. You can import more than one function from a single module in such a way. If there is more than one, they should be separated by a comma. Functions imported in this way can be used directly. E.g:
57+
58+
```python
59+
from numpy import pi, sin, cos
60+
61+
print(sin(pi / 2))
62+
```
63+
which is equivalent to:
64+
65+
```python
66+
import numpy
67+
68+
print(numpy.sin(numpy.pi / 2))
69+
```
70+
Note, however, that in using this approach, we run the risk that the imported function will replace the built-in function. Moreover, in the program code we do not clearly see which module it comes from. Therefore, please use this method with caution.
71+
72+
73+
<hr />
74+
<p id="copyright">Published under <a class="external" rel="nofollow" href="https://creativecommons.org/licenses/by-nc-sa/3.0/">Creative Commons Attribution-NonCommercial-ShareAlike</a> license.</p>
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
# Creating custom modules
2+
3+
4+
5+
When writing more complex programs, we can create our own modules. This makes it possible to split the program into several files, which is desirable in the case of longer programs. In addition, we can collect frequently used functions into a single module and use them over and over again in various projects.
6+
7+
The simplest module is a Python source file (with extension `.py`) placed in the same directory as the main program. Then we can import it with a command
8+
9+
```python
10+
import filename_without_extension
11+
```
12+
where `filename_without_extension` is the name of the file <span style="text-decoration: underline;">without the extension</span>. For example, if we have a file `mymodule.py` with the following content:
13+
14+
```python
15+
def hello():
16+
print("Hello")
17+
```
18+
then in the main program (in another file that we directly import) we can write:
19+
20+
```python
21+
import mymodule
22+
23+
mymodule.hello()
24+
```
25+
## Packages
26+
It is also possible to create a hierarchy of modules. To do this, create a directory with the name of the module (without any extensions) and put the file in this directory with the name `__init__.py` (two underscores at the beginning and at the end of the name). Then the command
27+
28+
```python
29+
import directory_name
30+
```
31+
will load the module from the file `__init__.py`. This directory is called **a package**. We can put other Python source files (with the extension `.py`) in it, which we can import via
32+
33+
```python
34+
import directory_name.file_name_without_extension
35+
```
36+
or in any other way as shown before. We can also increase the number of hierarchy levels by placing a subdirectory in our directory, in which we will again create the file `__init__.py` (this file must always be present, even if it would be empty).
37+
38+
Creating packages is useful when writing more complex programs.
39+
40+
<hr />
41+
<p id="copyright">Published under <a class="external" rel="nofollow" href="https://creativecommons.org/licenses/by-nc-sa/3.0/">Creative Commons Attribution-NonCommercial-ShareAlike</a> license.</p>

07 Exceptions/1 Error Messages.md

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
# Error messages
2+
3+
Each of you has certainly encountered error messages. For example, issuing the command in the console:
4+
5+
```python
6+
print(float("two"))
7+
```
8+
9+
It will produce the message:
10+
11+
```
12+
Traceback (most recent call last):
13+
14+
File "<ipython-input-1-ed629b1feb04>", line 1, in <module>
15+
print(float("two"))
16+
17+
ValueError: could not convert string to float: 'two'
18+
```
19+
20+
In Python, errors are called **exceptions**. Their appearance means that a non-standard situation has occurred. Its appearance may result from an error in the program: then the message shows the exact place where the problem appeared and the entire sequence of function calls that led to it. Please take a look at the following program (when rewriting it, please skip the line numbers that are given for information only):
21+
22+
```python
23+
1 def replace_number(value):
24+
2 return float(value)
25+
3
26+
4 print(replace_number('2'))
27+
5 print(replace_number('two'))
28+
```
29+
30+
Its launch will result in the following message:
31+
32+
```
33+
Traceback (most recent call last):
34+
35+
File "temp.py", line 5, in <module>
36+
print (replace_number("two"))
37+
38+
File "temp.py", line 2, in replace_number
39+
return float(value)
40+
41+
ValueError: could not convert string to float: 'two'
42+
```
43+
44+
Python printed the so-called __traceback__, which is the function call stack. It should be read from the bottom: an error has occurred `ValueError`, i.e. a value error. We read in the error message that Python is not able to convert the text string `'two'` to a real number. Above, we find information that this error occurred in the second line of the file `temp.py` in the function `replace_number` (this line is rewritten below). In turn, this function was called on the fifth line in the main code of the module (without any function). Note that calling the function `replace_number` on line four did not produce an error.
45+
46+
## Exception types
47+
In Python, exceptions can be of different types that correspond to different types of errors or abnormal situations. The following is a list of the most common types of exceptions:
48+
49+
**`AttributeError`**: Raised when attribute reference or assignment fails (when the object does not support attribute references or assignments at all TypeError).
50+
51+
**`IOError`**: Dispatched when an error occurs in the I/O of a print statement, built-in function, `open()` or file object methods. For example, these errors can be caused by "file not found" or "out of disk space" problems.
52+
53+
**`ImportError`**: Raised when an import statement fails because the module is not found, or when the `from ... import statement` cannot find the specified name in the module.
54+
55+
**`IndexError`**: Called when an index out of scope is used to access an element of a sequence (however, mind that slices are automatically adjusted to the size of the sequence; if the index is not an integer `TypeError` is raised).
56+
57+
**`KeyError`**: Raised when the key used to access the mapping element (dictionary) is not present.
58+
59+
**`KeyboardInterrupt`**: Called when the interrupt key is used (usually `Ctrl-C` or `Ctrl-Delete`). Checking for this is done regularly when the program is called. Using the abort key (key combination) while the function is waiting for input in the `input()` function also raises this exception.
60+
61+
**`MemoryError`**: Raised when the memory is exhausted. The value assigned to the exception is a string indicating what (internal) operation has exhausted memory.
62+
63+
**`NameError`**: Raised when local or global name is not found. The value associated with the exception is an error message that contains a name that was not found.
64+
65+
**`SyntaxError`**: Raised when the parser encounters a syntax error.
66+
67+
**`TypeError`**: Raised when an inline operation is performed on an object of the wrong type. The assigned value of the exception is a string that describes the details of the type mismatch.
68+
69+
**`UnboundLocalError`**: Raised when reference is made to a local variable of a function or method and no value has been assigned to that variable.
70+
71+
**`ValueError`**: Raised when some operation was requested with an argument of the right type, but with an incorrect value.
72+
73+
**`ZeroDivisionError`**: Raised when the second operand of the division operation or the division remainder (modulo) is zero. The value assigned to the exception is a string that describes the types of operands and operations.
74+
75+
76+
<hr />
77+
<p id="copyright">Published under <a class="external" rel="nofollow" href="https://creativecommons.org/licenses/by-nc-sa/3.0/">Creative Commons Attribution-NonCommercial-ShareAlike</a> license.</p>
Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
# Catching exceptions
2+
3+
Not all exceptional situations can be avoided during writing the program. For example, consider the following function:
4+
5+
```python
6+
def input_float(prompt):
7+
return float(input(prompt))
8+
```
9+
The purpose of this function is to read the actual number from the keyboard. However, we don't know what the user will enter. If it gives a string that cannot be converted to a real number, the program will be aborted. This is not correct behavior. In this situation, the correct program should inform the user that the entered text is incorrect and, for example, ask him to enter it again.
10+
11+
In order to be able to program the program behavior in exceptional situations, the following structure is used:
12+
13+
```python
14+
try:
15+
a block of commands that can cause an exception
16+
except ExceptionType:
17+
a block of commands performed on exceptions
18+
except DifferentExceptionType:
19+
a block of commands executed in the case of another type of exception
20+
```
21+
If **`try`** is used, there must be at least one **`except`** block.
22+
23+
Using this, a function that asks user to input a number could look like this:
24+
25+
```python
26+
def input_float(prompt):
27+
try:
28+
return float(input(prompt)
29+
except ValueError:
30+
print ("Invalid number. I return 0.")
31+
return 0.
32+
```
33+
This function calls the function `input` that asks for a string. Then that string is converted to type `float`. The conversion result is returned as the function result. If any of these steps fails, an exception will be thrown. If it is an exception of the type `ValueError` (in this case it may appear due to a failed conversion), it will be *caught* and an appropriate message will be printed and the function will return the value 0.
34+
35+
Exceptions do not have to be caught in the same function as they appeared. Consider the following program:
36+
37+
```python
38+
def input_float(prompt):
39+
return float(input(prompt))
40+
41+
def ask_number():
42+
return input_float("Enter a number:")
43+
44+
try:
45+
number = ask_number()
46+
result = 10 + number
47+
except ValueError:
48+
result = 0.
49+
50+
print (number)
51+
```
52+
If an incorrect value is entered, the function `input_float` will be aborted immediately. The function `ask_number` will also be interrupted. However, the entire program will not fail, since the exception is caught in the main code that calls the function `ask_number`.
53+
54+
The **`try... except`** construct may additionally have an **`else`** block that is called when no exception occurs. E.g:
55+
56+
```python
57+
try:
58+
number = ask_number()
59+
result = 10 + number
60+
except ValueError:
61+
result = 0.
62+
else:
63+
print("Thank you")
64+
```
65+
The *Thank you* message will only be printed if the correct real number has been entered.
66+
67+
Only exceptions of the given type are caught by the **`except`** command . Other exceptions are ignored by it and will either cause the program to stop or be caught elsewhere. Consider the following code:
68+
69+
```python
70+
def input_float(prompt):
71+
try:
72+
return float(input(prompt))
73+
except ValueError:
74+
print ("Invalid number. Returning 0.")
75+
return 0.
76+
77+
try:
78+
number = input_float("Enter a number:")
79+
except KeyboardInterrupt:
80+
number = -1
81+
```
82+
If the `ValueError` exception occurs in the function `input_float`, it prints a message and returns the value 0. However, pressing `Ctrl + C` or `Ctrl + Delete` will generate an exception `KeyboardInterrupt` that will break the function. However, it will be caught at the point where this function was called, and the variable `number` will be set to –1.
83+
84+
## Better to ask for forgiveness than permission
85+
86+
In Python, exceptions are normal and should be used. Let's look at a function that returns the inverse of the number given as its argument:
87+
88+
```python
89+
def inverse(x):
90+
return 1 / x
91+
```
92+
93+
Suppose we want to protect ourselves in case the given argument is 0 and we want to return the value 0 in this case (which of course is mathematically incorrect, but may be useful in some situations). We can do this in two ways:
94+
95+
```python
96+
def inverse (x):
97+
if x == 0:
98+
return 0
99+
else :
100+
return 1 / x
101+
```
102+
or
103+
104+
```python
105+
def inverse (x):
106+
try:
107+
return 1 / x
108+
except ZeroDivisionError:
109+
return 0
110+
```
111+
112+
In the first case, "we ask for permission", i.e. we use a condition to check whether the operation can be performed. In the second, we "beg for forgiveness", that is, we simply perform the operation, and in case of problems, we solve them. Generally the second way is better. In case of more complex programs, it results in more readable code and it is easier to add handling of other exceptions, which we are not able to predict at the time of writing the original version of the program. In addition, we are not able to check everything in advance, and even if it is possible, it may be unreliable: this applies in particular to file operations, which will be discussed in the next lecture. For example, if we first check if a given file exists and then open it, there is a non-zero probability that within microseconds between checking the existence of the file and opening it, it will be deleted (the probability is not so small if, for example, the computer's disk is damaged). In such a situation, we will receive permission to perform the operation (the condition will confirm the existence of the file), but the operation itself will fail. By using exception catching, we protect ourselves against such a situation. Another reason why "begging for forgiveness" is better is the performance of the program: when checking the condition first, we need to perform some operations that are likely to be repeated afterwards. When we use exceptions, we only perform a given operation once. The difference may be a few microseconds but it becomes noticeable when a given function is repeated several dozen million times (which is not unusual in larger programs for data analysis or scientific calculations).
113+
114+
## How to clean up a mess?
115+
116+
The instruction **`try`** has one more, optional clause, which is used to define actions to perform the necessary cleanups in all respects. E.g:
117+
118+
```python
119+
try:
120+
number = float('two') # this will cause an exception ValueError
121+
finally:
122+
print('Goodbye, world!')
123+
```
124+
The clause **`finally`** is executed whether or not an exception occurs. The code in this block is also executed when the try block is "exited" with the `break` or `return` statements.
125+
126+
The **`try`** statement must have at least one **`except`** block or one **`finally`** block.
127+
128+
129+
<hr />
130+
<p id="copyright">Published under <a class="external" rel="nofollow" href="https://creativecommons.org/licenses/by-nc-sa/3.0/">Creative Commons Attribution-NonCommercial-ShareAlike</a> license.</p>

0 commit comments

Comments
 (0)