Docsity
Docsity

Prepara i tuoi esami
Prepara i tuoi esami

Studia grazie alle numerose risorse presenti su Docsity


Ottieni i punti per scaricare
Ottieni i punti per scaricare

Guadagna punti aiutando altri studenti oppure acquistali con un piano Premium


Guide e consigli
Guide e consigli


Python advanced programming, Dispense di Programmazione Avanzata

Documento scritto da me in inglese sul linguaggio Python. La dispensa spiega argomenti avanzati del linguaggio in maniera chiara e semplice per poterne utilizzare tutte le capacità. Si parte dalla gestione dei file fino alla programmazione di interfacce grafiche, Pandas e Numpy. Ogni argomento è corredato di esercizi svolti per comprendere meglio gli argomenti. Questo documento è adatto per chiunque indipendentemente dall'università/corso voglia approfondire la programmazione Python in maniera strutturata e completa.

Tipologia: Dispense

2024/2025

In vendita dal 13/12/2024

giulio_russo
giulio_russo 🇮🇹

4.8

(42)

111 documenti

1 / 56

Toggle sidebar

Questa pagina non è visibile nell’anteprima

Non perderti parti importanti!

bg1
📶
Advanced
🐍
Python
💻
programming
pf3
pf4
pf5
pf8
pf9
pfa
pfd
pfe
pff
pf12
pf13
pf14
pf15
pf16
pf17
pf18
pf19
pf1a
pf1b
pf1c
pf1d
pf1e
pf1f
pf20
pf21
pf22
pf23
pf24
pf25
pf26
pf27
pf28
pf29
pf2a
pf2b
pf2c
pf2d
pf2e
pf2f
pf30
pf31
pf32
pf33
pf34
pf35
pf36
pf37
pf38

Anteprima parziale del testo

Scarica Python advanced programming e più Dispense in PDF di Programmazione Avanzata solo su Docsity!

Advanced

Python

programming

Index

  • List and Dictionary comprehension
  • Advanced Function arguments
  • Exercise
  • File handling
  • Exercise
  • Exceptions
  • Exercise
  • Pandas
  • Exercise
  • NumPy
  • Exercise
  • Plots
  • GUI
  • Turtle
  • Exercise
  • Tkinter
  • Exercise
  • Thanks

Advanced Function arguments

Functions can take a variable number of parameters using *args and **kwargs. They

are particularly useful when you want to create flexible functions that can handle

any number of input arguments.

  • *args: syntax allows a function to accept any number of positional arguments,

which are stored in a tuple:

>>> def print_args(*args):

>>> for arg in args:

>>> print(arg)

>>> print_args(1, 2, 3)

output: 1 2 3

  • **kwargs: syntax allows a function to accept any number of keyword arguments,

which are stored in a dictionary:

>>> def print_kwargs(**kwargs):

>>> for key, value in kwargs.items():

>>> print(f"{key}: {value}")

>>> print_kwargs(name="Alice", age=30, city="New York")

output: name: Alice age: 30 city: New York

This can be particularly useful in situations where the number of inputs is not

predetermined. A lot of pre-built Python functions use *args or **kwargs. For

example the function .format() used with the strings:

>>> template = "Hello, {name}! You have {count} new messages."

>>> formatted = template.format(name="Alice", count=5)

>>> print(formatted)

output: Hello, Alice! You have 5 new messages.

This because the number of placeholders inside the string can vary.

Furthermore, the behavior of a function can be controlled using the Python

decorators. They act as wrappers around functions, enabling you to add

functionality before or after the function is executed, or even alter its input and

output. The general syntax for a decorator is:

def decorator_name(func):

def wrapper():

Code to execute before the function call

result = func() # Call the original function

Code to execute after the function call return result

return wrapper

@decorator_name

def function_name(parameters):

Function body

The general syntax involves defining a decorator function, which takes another

function as an argument. This decorator function then defines a nested wrapper

function that adds additional behavior before and/or after calling the original

function. The decorator function returns this wrapper function, effectively

replacing the original function with the enhanced version. The @decorator_name

syntax is used to apply the decorator to a function, making the original function

execute with the extra functionality provided by the wrapper.

This approach allows for clean and reusable code modifications without altering the

original function's code.

>>> def my_decorator(func):

>>> def wrapper():

>>> print("Before the function.")

>>> func()

>>> print("After the function.")

>>> return wrapper

>>> @my_decorator

>>> def my_function():

>>> print("Inside the function.")

>>> # Calling the decorated function

>>> my_function()

output: Before the function.

Inside the function.

After the function.

Adding some expenses

add_expenses(("Food", 20 ), ("Entertainment", 50 ), ("Utilities", 75 ), ("Food", 15 ))

Calculating total expenses

total = calculate_total(*expenses)

print(f"Total Expenses: ${total}")

Generating expense summary

summary = generate_summary(*expenses)

print("Expense Summary:")

for expense_type, amount in summary.items():

print(f"{expense_type}: ${amount}")

  • Report generator: customizable report generator.

Function to generate a report with flexible sections using **kwargs

def generate_report(**sections):

report = {}

for title, content in sections.items():

if isinstance(content, str):

report[title] = content

else:

print(f"Invalid content for section '{title}': {content}")

return report

Function to print a summary of the report

def print_summary(**sections):

for title, content in sections.items():

first_line = content.split('\n')[ 0 ] # Extract the first line of content

print(f"Title: {title}")

print(f"Content Preview: {first_line}")

print()

Function to filter sections based on a keyword

def filter_sections(keyword, **sections):

filtered = {title: content for title, content in sections.items() if

keyword.lower() in content.lower()}

return filtered

Generating a report with multiple sections

report = generate_report(

Introduction="This is the introduction section of the report. It covers the

background.",

Results="The results section includes the main findings of the research.",

Conclusion="The conclusion summarizes the main points and suggests future

work.",

Appendix="Additional materials and data can be found in the appendix section."

Printing a summary of the report

print("Report Summary:")

print_summary(**report)

Filtering sections that contain the keyword "summary"

filtered_report = filter_sections("summary", **report)

print("Filtered Sections:")

print_summary(**filtered_report)

  • Timing function: track the execution time of the function.

import time

Create the decorator

def timing_decorator(func):

def wrapper(*args, **kwargs):

start_time = time.time() # Record the start time

result = func(*args, **kwargs) # Call the original function

end_time = time.time() # Record the end time

execution_time = end_time - start_time # Calculate execution time

print(f"Execution time: {execution_time:.4f} seconds")

return result

return wrapper

Apply the decorator to a sample function

@timing_decorator

def compute_sum(n):

total = 0

for i in range(n):

total += i

return total

Test the decorated function

print("Result:", compute_sum( 1000000 ))

  1. Writing mode:
  • write(): write the needed text inside the opened file. The file have to be

opened in write mode.

>>> file = open( "PATH_TO_FILE" , mode='w')

>>> file.write("Hello")

  • writelines(): write more lines. We can store our information inside a list and

pass the list as content to write. Each element in the list will be written

inside a separate line.

>>> lines = ["First line\n", "Second line\n", "Third line\n"]

>>> file = open("path/to/my/file", mode='w')

>>> file.writelines(lines)

The write mode have to be used carefully because, based on where is placed the file

cursor, the writing process will insert the text after the file cursor.

Ultimately, we can be sure of writing new content at the end of the file by

specifying the append mode:

>>> file = open( "PATH_TO_FILE" , mode='a')

>>> file.write("Hello")

The new content will be placed at the end of the file.

We can move the file cursor using:

  • seek(): set the current file position. It is used to move on the file content.

>>> seek(0) # rewind the file position to zero

Finally, the file have to be closed:

  • close(): close the opened file, freeing up system resources.

>>> file = open( "PATH_TO_FILE" , mode='w')

>>> file.close()

In order to manage easily the files, we can use the with statement, that allow to

refere to a specific expression using a defined variable:

with EXPRESSION as VARIABLE :

The with statement allows to manage resources in an easy way by accessing it using

a single variable name:

>>> with open( "PATH_TO_FILE" , "r") as file:

>>> content = file.read()

This makes easy to manage different files without any confusion.

CSV files:

Python is very suitable for CSV (Comma Separated Values) files. It is a simple file

format used to store tabular data, such as a spreadsheet or database. A CSV file

stores tabular data (numbers and text) in plain text. Each line of the file is a

data record. Each record consists of one or more fields, separated by commas.

Header (column names) of the file

The file is seen as:

The 'csv' module is used to handle this type of files.

  • reader(): the reader read each row of the CSV file. Each row will be an

iterable list where the element will be a string.

>>> import csv

>>> with open( "PATH_TO_FILE" , mode='r') as file:

>>> reader = csv.reader(file)

>>> for row in reader:

>>> print(row) # each row is a list of strings

output: ['NAME', 'AGE', 'JOB']

['John', '28', 'Teacher']

['Bob', '32', 'Mechanic']

['Giulia', '19', 'Waiter']

CSV files can be red with different delimiters (for example, the CSV files in Excel

have a colon as delimiter instead of commas). The csv.reader function has a commas

default delimiter. We can change it when we call the function in any character we

want to consider a delimiter for our CSV file:

>>> import csv

>>> with open( "PATH_TO_FILE" , mode='r') as file:

>>> reader = csv.reader(file, delimiter=':')

In any case, the header of the CSV can be extracted as the first row of content of

the file.

JSON files:

JSON (JavaScript Object Notation) is a widely used format for exchanging data

between a server and a client, or for storing structured data in a readable text

format. They follow a specific notation very similar to the Python dictionaries:

Python's json module provides built-in support for parsing JSON data and converting

Python objects to JSON format.

  • load(): used to read a JSON file and store the content inside a dictionary that

follows the file structure.

>>> import json

>>> with open( "PATH_TO_FILE" ) as file:

>>> data = json.load(file)

>>> print(data)

output: {'employee': {'name': 'John', 'age': '28', 'job': 'Teacher'}}

  • dump(): uused to write on a JSON file using a dictionary. Optionally we can

control the indentation using whitespaces.

>>> import json

>>> person = {'name': 'Jane', 'age': 25, 'city': 'Los Angeles'}

>>> with open( "PATH_TO_FILE" , "w") as file:

>>> json.dump(person, file, indent=4)

This makes possible to manage this complex file type using dictionaries.

Exercises

  • Log file analysis: analyze a log file and make a summary.

server_log.txt

INFO: Server started successfully.

WARNING: Disk space running low.

ERROR: Failed to connect to database.

INFO: User login successful.

ERROR: Unable to open file.

WARNING: High memory usage detected.

INFO: Server shutdown initiated.

Read the log file and count errors and warnings

def analyze_log_file(input_file, output_file):

error_count = 0

warning_count = 0

with open(input_file, 'r') as file:

for line in file:

if 'ERROR' in line:

error_count += 1

elif 'WARNING' in line:

warning_count += 1

Write the summary report

with open(output_file, 'w') as file:

file.write(f"Error Count: {error_count}\n")

file.write(f"Warning Count: {warning_count}\n")

Specify input and output file names

input_filename = 'server_log.txt'

output_filename = 'log_summary.txt'

Call the function to analyze the log file and write the report

analyze_log_file(input_filename, output_filename)

Exercises

  • Data aggregation: evaluate sale for each product.

sales_data.csv

Product,Amount

Apple,

Banana,

Apple,

Orange,

Banana,

Apple,

import json

1. Read the JSON file and calculate average salary

def process_json_data(input_file, output_file):

with open(input_file, 'r') as file:

data = json.load(file) # Load JSON data into a Python list

total_salary = 0

employee_count = len(data)

for employee in data:

total_salary += employee['salary']

average_salary = total_salary / employee_count if employee_count > 0 else 0

2. Write the summary to a new JSON file

summary = {

'average_salary': average_salary

with open(output_file, 'w') as file:

json.dump(summary, file, indent= 4 ) # Write JSON data with indentation

Specify input and output file names

input_filename = 'employees.json'

output_filename = 'salary_summary.json'

Call the function to process the JSON file and write the summary

process_json_data(input_filename, output_filename)

Exceptions

🔀

Exceptions in Python are events that disrupt the normal flow of a program's

execution. They are used to handle errors and unusual situations gracefully,

allowing the program to continue running or to fail with a meaningful error message.

The general syntax is:

try:

[CODE THAT MAY RAISE AN EXCEPTION]

except EXCEPTION_TYPE as ERROR :

[CODE THAT HANDLE MY EXCEPTION]

finally:

[CODE EXCECUTED NO MATTER WHAT HAPPEN]

You try to execute a code. If it raise an error, instead of blocking the program,

you can write a specific code that can handle the situation in order to do not stop

the code flow. In the finally section we excecute the code no matter what happened.

This is usefulll when we need to do something at the end of our code.

Any error is identified by a name. These situations are categorized in Python (and

also in any other language program) in order to be solved easly by the programmers.

For example:

>>> try:

>>> result = 10 / 0

>>> except ZeroDivisionError:

>>> print("You tried to divide by zero!")

This structure is very useful to do several checks in our code, based on what we

have to do. For example while managing the files:

>>> try:

>>> # Attempt to open and read from a file

>>> file = open( "PATH_TO_FILE" , mode='r')

>>> content = file.read()

>>> print(content) # Print the content of the file

>>> except FileNotFoundError:

>>> # Handle the case where the file does not exist

>>> print("The file does not exist. Please check the file path.")

>>> except IOError:

>>> # Handle other I/O related errors

>>> print("An I/O error occurred while trying to read the file.")

>>> finally:

>>> # Ensure the file is closed no matter what

>>> try:

>>> file.close()

>>> print("File has been closed.")

>>> except NameError:

>>> # Handle the case where 'file' was never defined

>>> print("No file was opened, so there is nothing to close.")

def process_numbers(numbers):

try:

Check if the input is a list

if not isinstance(numbers, list):

raise TypeError("Input must be a list.")

Check if the list is empty

if len(numbers) == 0 :

raise ValueError("The list is empty.")

Check if all elements in the list are numbers

if not all(isinstance(num, (int, float)) for num in numbers):

raise ValueError("All elements in the list must be numbers.")

Compute the average, largest, and smallest numbers

average = sum(numbers) / len(numbers)

largest = max(numbers)

smallest = min(numbers)

Return the results as a dictionary

return {

'average': average,

'largest': largest,

'smallest': smallest

except TypeError as e:

print(f"TypeError: {e}")

except ValueError as e:

print(f"ValueError: {e}")

except ZeroDivisionError:

print("Error: Division by zero while computing the average.")

except Exception as e:

print(f"An unexpected error occurred: {e}")

Test the function with different scenarios

Valid input

numbers = [ 10 , 20 , 30 , 40 , 50 ]

result = process_numbers(numbers)

if result:

print("Results:", result)

Invalid input (not a list)

process_numbers("10, 20, 30")

Invalid input (list contains non-numbers)

process_numbers([ 10 , "twenty", 30 ])

Empty list

process_numbers([])

  • List checking: analyze list items.

Pandas

Pandas is a powerful and flexible Python library used for data manipulation and

analysis. It provides two primary data structures: DataFrame and Series. These

structures are designed to handle and analyze structured data efficiently.

DataFrame:

A DataFrame is a two-dimensional, size-mutable, and heterogeneous tabular data

structure with labeled axes. Rows are labeled with indexes, while columns are

labeled using names.

DataFrames can be created using dictionaries where using the pandas.DataFrame()

casting. We convert a dictionary into a DataFrame: the keys become the column

names, while the values become the column values.

>>> import pandas as pd

>>> # Create a DataFrame from a dictionary

>>> data = {

>>> 'Name': ['Alice', 'Bob', 'Charlie'],

>>> 'Age': [25, 30, 35],

>>> 'City': ['New York', 'Los Angeles', 'Chicago']

>>> df = pd.DataFrame(data)

>>> print(df)

output: index Name Age City

0 Alice 25 New York

1 Bob 30 Los Angeles

2 Charlie 35 Chicago

Now, the DataFrame is a table with columns named and row labeled using indexes.

The row index, if no list is specified, is assigned automatically with integers

starting from 0, otherwise the attribute "index" can be assigned to the desired

list values.

A very useful usecase for DataFrame is to store the data coming from a CSV. Pandas

provide a powerful function in order to do that: pandas.read_csv() create a

DataFrame using the column names of the CSV as column names into the DataFrame,

and fill the column values with the items stored inside the CSV.

>>> import pandas as pd

>>> # Create a DataFrame from a CSV file

>>> df = pd.read_csv( "PATH_TO_FILE"

>>> print(df)

output: index Name Age City

0 Alice 25 New York

1 Bob 30 Los Angeles

2 Charlie 35 Chicago