    # Functions

Functions are reusable, self-contained pieces of code that can be called using a function’s name. They are the fundamental building block of any application in Python. They are key to writing clean modular code.

Modular code is code that is separated into independent modules, segments, or files. The idea is that each module is responsible for a feature or a specific functionality and its internal details should be hidden behind a public interface, making each module easier to understand, debug, and maintain.

``````import math # The math module contains a number of mathematical functions. The math.factorial() function returns the factorial of the desired number.
from rich import print # Rich is a Python library for rich text and beautiful formatting in the terminal.

def isPrime(number): # The keyword def introduces a function definition. It is followed by the function name and the parenthesized list of parameters.
i = int(number)

if i > 1:
n = i // 2
for j in range(2, n + 1):
# The range() function is used to generate a sequence of numbers from 0 up to the argument specified -1, e.g., >>> list(range(7)) returns [0, 1, 2, 3, 4, 5, 6]
if i % j == 0:
return print(i, "is not prime.")
return print(i, "is prime.")
else:
return print(i, "is not prime.")

def factorial(number):
i = int(number)

print("factorial(" + str(number) + ")=", str(math.factorial(i)))

if __name__ == '__main__':
isPrime(17) # Now, we are calling the function we have just defined.
isPrime(12)
factorial(4)
``````

The result is as follows: 17 is prime. 12 is not prime. factorial(4)= 24

# Strings and f-Strings

Strings in Python are arrays of bytes representing Unicode characters.

``````user@pc:~\$ python
Python 3.9.5 (default, May 11 2021, 08:20:37)
[GCC 10.3.0] on linux
>>> a = "Hello Python!" # You can create a string simply by enclosing characters in quotes.
>>> type(a) # The type() function returns the type of the object.
<class 'str'>
>>> print(a)
Hello Python!
>>> a # A string is just an array. You can access a character by its index. The first character has the position 0.
'e'
>>> a[-1]
'!'
``````

Each character has a positive and a negative index number associated with it. We can select the last character using its negative index, i.e., -1.

``````>>> for character in a: # Strings are also iterable.
...     print(character)
...
H
e
l
l
o

P
y
t
h
o
n
!
>>> print(len(a)) # Len returns the length of a string including spaces.
13
>>> print(a[1:-2]) # Slicing is used to return a range of characters within a string. We indicate the start and the end indexes separated by a colon.
ello Pytho
>>> a.lower() # a.lower()/a.upper() returns the string in lower/upper case.
'hello python!'
>>> "hello" + " " + "world" # String concatenation in Python is performed using the + operator
'hello world'
>>> print(a[1:-2:3]) # The syntax to slice a string is [start index: stop index: step]
eoyo # e (a), o (a[1+3]), y (a), o (a)
>>> print(a[::-1]) # It prints the entire string in reverse because the step is negative.
!nohtyP olleH
``````

f-Strings is the name given to ‘formatted string literals’. It provides a faster, more readable, and less verbose way of formatting strings in Python.

``````user@pc:~\$ python
Python 3.9.5 (default, May 11 2021, 08:20:37)
[GCC 10.3.0] on linux
>>> first_name = "Máximo"
>>> last_name = "Núñez"
>>> sentence = f'My name is {first_name} {last_name}'
# We use a single 'f' to declare that the code uses f-strings and add variables to the string using curly brackets: {}.
>>> print(sentence)
My name is Máximo Núñez
>>> sentence = f'My name is {first_name.lower()} {last_name.lower()}'
>>> print(sentence)
My name is máximo núñez
>>> import math
>>> sentence = f'Pi is equal to {math.pi:.6f}' # We are using format specifiers: f'{value:{width}.{precision}}' precision indicates the number of characters used after the decimal point.
>>> print(sentence)
Pi is equal to 3.141593
>>> print(sentence)
The area of a circle with radius 2 is equal to 12.57
>>> import datetime
>>> now = datetime.datetime.now()
>>> print(f'Right now: {now:%d-%m-%Y %H:%M}')
Right now: 11-11-2021 07:42
``````

# Text Files in Python

Let’s start by reading a file.

``````user@pc:~\$ python
Python 3.9.5 (default, May 11 2021, 08:20:37)
[GCC 10.3.0] on linux
>>> with open("myExample.txt") as f:
``````

It opens the file in text mode. It is good practice to use the “with” keyword when dealing with files. The advantage is that the file is properly closed once your code leaves the with block, even if an exception is raised at some point.

``````...     myText = f.read()
...
>>> print(myText)
Be yourself; everyone else is already taken.”
― Oscar Wilde
“So many books, so little time.”
― Frank Zappa
``````

An improved version is this:

``````def readFile(pathFile):
file_exists = os.path.exists(pathFile) # os.path.exists checks whether the specified path exists or not.
if file_exists:
with open(pathFile) as f:
for line in f: # Instead of reading the whole file into memory, we can iterate over the file object itself and process line by line.
print(line, end="")
else:
print("The file does not exist")

if __name__ == '__main__':
``````

Next, we are going to write a list to a file in Python.

``````user@pc:~\$ python
Python 3.9.5 (default, May 11 2021, 08:20:37)
[GCC 10.3.0] on linux
>>> animals = ["zebra", "cobra", "stork", "penguin", "shark", "lion", "buffalo", "whale", "seal", "eagle", "wren", "horse", "rat"]
>>> with open("animals.txt", "w") as f:
``````

open() returns a file object and it has two arguments: filename, and mode. The mode ‘w’ is for writing. Please notice that an existing file with the same name will be erased, so you may want to use ‘a’ for appending, that is, it will create a file if it does not exist, but if the file does exist, then any data written to the file will be automatically added to the end.

``````...     for animal in animals:
...             f.write(animal + "\n")
``````

f.write() writes the content of the string to the file, returning the number of characters written. An alternative is: print(animal, file=f)

# Handling Exceptions

There are two kinds of errors: syntax errors and exceptions.

``````>>>  print("Hello world")
File "", line 1
print("Hello world")
IndentationError: unexpected indent
``````

Indentation errors occur when the spaces or tabs are not placed properly. You can use the Python built-in script reindent.py (pip install reindent) to fix it. Use: reindent mySourceCode.py

Errors detected during execution are called exceptions and we can write programs that handle them. Let’s see an example:

``````def main():
try: # The program enters the "try" block. If there is an exception, the control jumps to the code in the "except" block.
num1 = input("Enter a number: ")
num2 = input("Enter a second number: ")
result = int(num1)/int(num2)
except ValueError as e:
``````

A ValueError Exception is raised when an operation or function receives an argument that has the right type but an inappropriate value. This is the Exception’s handling code. It is pretty simple, we just print the exception.

``````        print(e)
except ZeroDivisionError as e: # A ZeroDivisionError is raised when the second argument of a division is zero.
print(e)
except Exception as e:
print("Something went very wrong! " + e)

if __name__ == "__main__":
main()
``````

Enter a number: 1 Enter a second number: 0 division by zero

Enter a number: 1 Enter a second number: a invalid literal for int() with base 10: ‘a’

Let’s see how to handle exceptions that could be raised when you work with files.

``````def main():
try:
f = open("/yourPath/yourFile.txt")
except FileNotFoundError as e: # FileNotFoundError is raised when the file doesn’t exist.
print(e) # [Errno 2] No such file or directory: '/yourPath/yourFile.txt'
except Exception as e: # I use Corrupt-a-File.net, https://corrupt-a-file.net to corrupt a file.
print(e) # 'utf-8' codec can't decode byte 0x9d in position 2: invalid start byte
else: # This code is run if the "try" block does not encounter any errors.
f.close() # This is the best place to use the close method. It flushes any unwritten information and closes the file object.

if __name__ == "__main__":
main()
``````

Of course, we can raise exceptions when we encounter some unexpected situation with the raise keyword.

You may want to read our article Python Object-Oriented Programming

``````class Fraction:
'A simple implementation of fractions in Python'
## We create a class with the name Fraction. It models a rational number. Next, we need to define its attributes or properties.
# @param n is the numerator of the fraction (default is 0).
# @param d is the denominator of the fraction (default is 1).
def __init__(self, n = 0, d = 1): # __init__ is the _constructor_. It is _immediately called when an object is created_.
if (not isinstance(n, int)): # The isinstance() function returns True if the specified object is of the specified type or class, otherwise it returns False. We want to check that all its arguments are of the correct type.
raise TypeError("The numerator of a Fraction must be an integer")
# The raise statement allows us to force a specified exception to occur. TypeError is a subclass of Exception. It gets thrown when an individual operation is performed on an unexpected type.
if (not isinstance(d, int)):
raise TypeError("The denominator of a Fraction must be an integer")
self.numerator = int(n / gcd(abs(n), abs(d)))
# We are going to represent a fraction in its simplest form. It means that its numerator and denominator are relatively prime, that is, they have no common factors other than 1.
self.denominator = int(d / gcd(abs(n), abs(d)))
if self.denominator < 0: # If the denominator is negative, we just flip the signs of both.
self.denominator = abs(self.denominator)
self.numerator = -1*self.numerator
# The denominator cannot be zero
elif self.denominator == 0:
raise ZeroDivisionError("Denominator cannot be zero")
[...]
def main():
try:
f3 = Fraction(3, 0) # If no exception occurs, the except block is skipped and the execution of the try statement is finished.
except ZeroDivisionError: # If an exception occurs and its type matches the exception named after the except keyword (e.g., ZeroDivisionError), the except clause is executed.
print("Oops! Denominator cannot be zero")
``````
Bitcoin donation 