Python & AI Tutorials Logo
Programmazione Python

20. Parametri e argomenti delle funzioni

Nel Capitolo 19, abbiamo imparato come definire e chiamare funzioni con parametri di base. Ora esploreremo in profondità il sistema flessibile di parametri e argomenti di Python. Comprendere questi meccanismi ti permette di scrivere funzioni che siano sia potenti sia facili da usare.

20.1) Argomenti posizionali e keyword

Quando chiami una funzione, puoi passare gli argomenti in due modi fondamentali: per posizione oppure per nome (keyword).

20.1.1) Argomenti posizionali

Gli argomenti posizionali vengono associati ai parametri in base al loro ordine. Il primo argomento va al primo parametro, il secondo al secondo parametro e così via.

python
def calculate_discount(price, discount_percent):
    """Calculate the final price after applying a discount."""
    discount_amount = price * (discount_percent / 100)
    final_price = price - discount_amount
    return final_price
 
# Passare gli argomenti per posizione
result = calculate_discount(100, 20)
print(result)

Output:

80.0

In questo esempio, 100 viene assegnato a price e 20 a discount_percent esclusivamente in base alle loro posizioni nella chiamata della funzione.

L’ordine è fondamentale con gli argomenti posizionali:

python
# Esempio: vogliamo calcolare un articolo da $100 con sconto del 20%
 
# Ordine corretto: prima price, poi discount
print(calculate_discount(100, 20))
 
# Ordine sbagliato: prima discount, poi price
print(calculate_discount(20, 100))

Output:

80.0
-16.0

Quando inverti gli argomenti, Python non sa che hai commesso un errore: li assegna semplicemente in ordine. Questo produce un risultato matematicamente valido ma logicamente errato (un prezzo negativo!).

20.1.2) Argomenti keyword

Gli argomenti keyword specificano esplicitamente quale parametro riceve quale valore, usando il nome del parametro seguito da un segno di uguale e dal valore. Questo rende il codice più leggibile e protegge dagli errori di ordinamento.

python
def create_user_profile(username, email, age):
    """Create a user profile with the given information."""
    profile = f"User: {username}\nEmail: {email}\nAge: {age}"
    return profile
 
# Usare argomenti keyword
profile = create_user_profile(username="alice_smith", email="alice@example.com", age=28)
print(profile)

Output:

User: alice_smith
Email: alice@example.com
Age: 28

Con gli argomenti keyword, l’ordine non conta:

python
# Stesso risultato, ordine diverso
profile1 = create_user_profile(username="bob", email="bob@example.com", age=35)
profile2 = create_user_profile(age=35, username="bob", email="bob@example.com")
profile3 = create_user_profile(email="bob@example.com", age=35, username="bob")
 
# Tutti e tre producono risultati identici
print(profile1 == profile2 == profile3)

Output:

True

Questa flessibilità è particolarmente preziosa quando una funzione ha molti parametri, perché rende facile vedere quale valore corrisponde a quale parametro.

20.1.3) Mescolare argomenti posizionali e keyword

Puoi combinare entrambi gli stili in un’unica chiamata di funzione, ma c’è una regola importante: gli argomenti posizionali devono venire prima degli argomenti keyword.

python
def format_address(street, city, state, zip_code):
    """Format a mailing address."""
    return f"{street}\n{city}, {state} {zip_code}"
 
# Valido: prima argomenti posizionali, poi argomenti keyword
address = format_address("123 Main St", "Springfield", state="IL", zip_code="62701")
print(address)

Output:

123 Main St
Springfield, IL 62701

Qui, "123 Main St" e "Springfield" sono posizionali (assegnati a street e city), mentre state e zip_code sono specificati per nome.

Provare a mettere argomenti posizionali dopo argomenti keyword causa un errore:

python
# Non valido: argomento posizionale dopo argomento keyword
# address = format_address(street="123 Main St", "Springfield", state="IL", zip_code="62701")
# SyntaxError: positional argument follows keyword argument

Python impone questa regola perché, una volta iniziato a usare argomenti keyword, diventa ambiguo quale parametro posizionale dovrebbe riempire un successivo argomento senza nome.

20.1.4) Quando usare ciascuno stile

Usa argomenti posizionali quando:

  • La funzione ha pochi parametri (tipicamente 1-3)
  • L’ordine dei parametri è ovvio e intuitivo
  • La funzione è usata comunemente e l’ordine è ben noto
python
# Ovvio e conciso
print(len("hello"))
result = max(10, 20, 5)

Usa argomenti keyword quando:

  • La funzione ha molti parametri
  • I significati dei parametri non sono immediatamente ovvi
  • Vuoi saltare alcuni parametri che hanno valori predefiniti (trattati di seguito)
  • Vuoi rendere il codice auto-documentante
python
# Chiaro ed esplicito
user = create_user_profile(username="charlie", email="charlie@example.com", age=42)

20.2) Valori predefiniti dei parametri

Le funzioni possono specificare valori predefiniti per i parametri. Quando chi chiama non fornisce un argomento per un parametro con un valore predefinito, Python usa invece il valore predefinito.

20.2.1) Definire parametri con valori predefiniti

I valori predefiniti sono specificati nella definizione della funzione usando l’operatore di assegnazione:

python
def greet_user(name, greeting="Hello"):
    """Greet a user with a customizable greeting."""
    return f"{greeting}, {name}!"
 
# Usare il saluto predefinito
print(greet_user("Alice"))
 
# Fornire un saluto personalizzato
print(greet_user("Bob", "Good morning"))
print(greet_user("Carol", greeting="Hi"))

Output:

Hello, Alice!
Good morning, Bob!
Hi, Carol!

Il parametro greeting ha un valore predefinito di "Hello". Quando chiami greet_user("Alice"), Python usa questo valore predefinito. Quando fornisci un secondo argomento, sovrascrive il valore predefinito.

20.2.2) I parametri con valori predefiniti devono venire dopo i parametri obbligatori

Python richiede che i parametri con valori predefiniti compaiano dopo tutti i parametri senza valori predefiniti. Questa regola evita ambiguità su quali argomenti corrispondano a quali parametri.

python
# Corretto: prima parametri obbligatori, poi predefiniti
def create_product(name, price, category="General", in_stock=True):
    """Create a product record."""
    return {
        "name": name,
        "price": price,
        "category": category,
        "in_stock": in_stock
    }
 
product = create_product("Laptop", 999.99)
print(product)

Output:

{'name': 'Laptop', 'price': 999.99, 'category': 'General', 'in_stock': True}

Tentare di inserire un parametro obbligatorio dopo uno con valore predefinito causa un errore di sintassi:

python
# Non valido: parametro obbligatorio dopo parametro predefinito
# def invalid_function(name="Unknown", age):
#     return f"{name} is {age} years old"
# SyntaxError: non-default argument follows default argument

Questo ha senso: se name ha un valore predefinito ma age no, come farebbe Python a sapere se invalid_function(25) significa name=25 con age mancante, oppure age=25 con name che usa il suo valore predefinito? La regola elimina questa ambiguità.

20.2.3) Usi pratici dei parametri predefiniti

I parametri predefiniti sono eccellenti per funzioni in cui alcuni argomenti cambiano raramente:

python
def calculate_shipping(weight, distance, express=False):
    """Calculate shipping cost based on weight and distance."""
    base_rate = 0.50 * weight + 0.10 * distance
    
    if express:
        base_rate *= 2  # I costi della spedizione express raddoppiano
    
    return round(base_rate, 2)
 
# La maggior parte delle spedizioni è standard
standard_cost = calculate_shipping(5, 100)
print(f"Standard: ${standard_cost}")
 
# Occasionalmente serve l'express
express_cost = calculate_shipping(5, 100, express=True)
print(f"Express: ${express_cost}")

Output:

Standard: $12.5
Express: $25.0

Questa progettazione rende il caso comune (spedizione standard) semplice da chiamare, pur supportando il caso meno comune (express) quando necessario.

20.2.4) Predefiniti multipli e sovrascrittura selettiva

Quando una funzione ha più parametri con valori predefiniti, puoi sovrascrivere qualsiasi combinazione di essi usando argomenti keyword:

python
def format_currency(amount, currency="USD", show_symbol=True, decimal_places=2):
    """Format a number as currency."""
    symbols = {"USD": "$", "EUR": "€", "GBP": "£", "JPY": "¥"}
    
    formatted = f"{amount:.{decimal_places}f}"
    
    if show_symbol and currency in symbols:
        formatted = f"{symbols[currency]}{formatted}"
    
    return formatted
 
# Usare tutti i valori predefiniti
print(format_currency(42.5))
 
# Sovrascrivere solo la valuta
print(format_currency(42.5, currency="EUR"))
 
# Sovrascrivere più valori predefiniti
print(format_currency(42.5, currency="JPY", decimal_places=0))

Output:

$42.50
€42.50
¥42

Questa flessibilità permette a chi chiama di personalizzare esattamente ciò di cui ha bisogno mantenendo concisa la chiamata della funzione.

20.3) Liste di argomenti a lunghezza variabile con *args

A volte vuoi che una funzione accetti un numero qualsiasi di argomenti senza sapere in anticipo quanti saranno. Python fornisce *args per questo scopo.

20.3.1) Comprendere *args

La sintassi *args in una lista di parametri raccoglie tutti gli argomenti posizionali extra in una tupla. Il nome args è una convenzione (abbreviazione di "arguments"), ma puoi usare qualsiasi nome di parametro valido dopo l’asterisco.

python
def calculate_total(*numbers):
    """Calculate the sum of any number of values."""
    total = 0
    for num in numbers:
        total += num
    return total
 
# Funziona con qualsiasi numero di argomenti
print(calculate_total(10))
print(calculate_total(10, 20))
print(calculate_total(10, 20, 30, 40))
print(calculate_total())

Output:

10
30
100
0

All’interno della funzione, numbers è una tupla contenente tutti gli argomenti posizionali passati alla funzione. Quando non vengono forniti argomenti, è una tupla vuota.

20.3.2) Combinare parametri regolari con *args

Puoi avere parametri regolari prima di *args. I parametri regolari consumano i primi argomenti, e *args raccoglie il resto:

python
def create_team(team_name, *members):
    """Create a team with a name and any number of members."""
    member_list = ", ".join(members)
    return f"Team {team_name}: {member_list}"
 
# Il primo argomento va a team_name, il resto va in members
print(create_team("Alpha", "Alice", "Bob"))
print(create_team("Beta", "Carol"))
print(create_team("Gamma", "Dave", "Eve", "Frank", "Grace"))

Output:

Team Alpha: Alice, Bob
Team Beta: Carol
Team Gamma: Dave, Eve, Frank, Grace

Il primo argomento ("Alpha", "Beta" o "Gamma") viene assegnato a team_name, e tutti gli argomenti rimanenti vengono raccolti nella tupla members.

20.4) Parametri keyword-only e parametri **kwargs

Python fornisce due meccanismi aggiuntivi per gestire gli argomenti: i parametri keyword-only e **kwargs per raccogliere argomenti keyword arbitrari.

20.4.1) Parametri keyword-only

I parametri keyword-only devono essere specificati usando argomenti keyword: non possono essere passati in modo posizionale. Li crei mettendoli dopo un * o dopo *args nella lista dei parametri.

python
def create_account(username, *, email, age):
    """Create an account. Email and age must be specified by name."""
    return {
        "username": username,
        "email": email,
        "age": age
    }
 
# Corretto: email e age specificati via keyword
account = create_account("alice", email="alice@example.com", age=28)
print(account)
 
# Non valido: tentare di passare email e age in modo posizionale
# account = create_account("bob", "bob@example.com", 30)
# TypeError: create_account() takes 1 positional argument but 3 were given

Output:

{'username': 'alice', 'email': 'alice@example.com', 'age': 28}

Il * nella lista dei parametri agisce come separatore. Tutto ciò che lo segue deve essere passato come argomento keyword. Questo è utile quando vuoi obbligare chi chiama a essere esplicito su certi parametri, rendendo il codice più leggibile e meno soggetto a errori.

Puoi anche combinare parametri regolari, *args e parametri keyword-only:

python
def log_event(event_type, *details, severity="INFO", timestamp=None):
    """Log an event with optional details and metadata."""
    # Impareremo il modulo datetime in dettaglio nel Capitolo 39,
    # ma per ora sappi solo che queste righe ottengono l'ora corrente
    # e la formattano come una stringa di timestamp
    from datetime import datetime
    
    if timestamp is None:
        timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
    
    details_str = " | ".join(details)
    return f"[{timestamp}] {severity}: {event_type} - {details_str}"
 
# event_type è posizionale, details vengono raccolti da *details,
# severity e timestamp sono keyword-only
print(log_event("Login", "User: alice", "IP: 192.168.1.1"))
print(log_event("Error", "Database connection failed", severity="ERROR"))

Output (il timestamp varierà in base a quando esegui il codice):

[2025-12-18 19:29:16] INFO: Login - User: alice | IP: 192.168.1.1
[2025-12-18 19:29:16] ERROR: Error - Database connection failed

20.4.2) Comprendere **kwargs

La sintassi **kwargs raccoglie tutti gli argomenti keyword extra in un dizionario. Come args, il nome kwargs è convenzionale (abbreviazione di "keyword arguments"), ma puoi usare qualsiasi nome valido dopo il doppio asterisco.

python
def create_product(**attributes):
    """Create a product with any number of attributes."""
    product = {}
    for key, value in attributes.items():
        product[key] = value
    return product
 
# Passa qualsiasi argomento keyword tu voglia
laptop = create_product(name="Laptop", price=999.99, brand="TechCorp", in_stock=True)
print(laptop)
 
phone = create_product(name="Phone", price=699.99, color="Black")
print(phone)

Output:

{'name': 'Laptop', 'price': 999.99, 'brand': 'TechCorp', 'in_stock': True}
{'name': 'Phone', 'price': 699.99, 'color': 'Black'}

All’interno della funzione, attributes è un dizionario in cui le chiavi sono i nomi dei parametri e i valori sono gli argomenti passati.

20.4.3) Combinare parametri regolari, *args e **kwargs

Puoi usare tutti questi meccanismi insieme, ma devono apparire in un ordine specifico:

  1. Parametri posizionali regolari
  2. *args (se presente)
  3. Parametri keyword-only (se presenti)
  4. **kwargs (se presente)
python
def complex_function(required, *args, keyword_only, **kwargs):
    """Demonstrate all parameter types together."""
    print(f"Required: {required}")
    print(f"Args: {args}")
    print(f"Keyword-only: {keyword_only}")
    print(f"Kwargs: {kwargs}")
 
complex_function(
    "value1",           # required
    "value2", "value3", # args
    keyword_only="kw",  # keyword_only
    extra1="e1",        # kwargs
    extra2="e2"         # kwargs
)

Output:

Required: value1
Args: ('value2', 'value3')
Keyword-only: kw
Kwargs: {'extra1': 'e1', 'extra2': 'e2'}

Questa flessibilità è potente, ma dovrebbe essere usata con giudizio. La maggior parte delle funzioni non ha bisogno di tutti questi meccanismi.

20.4.4) Caso d’uso pratico: funzioni di configurazione

Un uso comune di **kwargs è creare funzioni che accettano opzioni di configurazione:

python
def connect_to_database(host, port, **options):
    """Connect to a database with flexible configuration options."""
    connection_string = f"Connecting to {host}:{port}"
    
    # Elabora eventuali opzioni aggiuntive
    if options.get("ssl"):
        connection_string += " with SSL"
    
    if options.get("timeout"):
        connection_string += f" (timeout: {options['timeout']}s)"
    
    if options.get("pool_size"):
        connection_string += f" (pool size: {options['pool_size']})"
    
    return connection_string
 
# Connessione di base
print(connect_to_database("localhost", 5432))
 
# Con SSL
print(connect_to_database("db.example.com", 5432, ssl=True))
 
# Con più opzioni
print(connect_to_database("db.example.com", 5432, ssl=True, timeout=30, pool_size=10))

Output:

Connecting to localhost:5432
Connecting to db.example.com:5432 with SSL
Connecting to db.example.com:5432 with SSL (timeout: 30s) (pool size: 10)

Questo schema consente alla funzione di accettare un numero qualsiasi di parametri di configurazione opzionali senza definirli tutti esplicitamente nella lista dei parametri.

Posizionale

Posizionale extra

Keyword-only

Keyword extra

Chiamata di funzione

Tipo di parametro?

Parametri regolari

Tupla *args

Parametri keyword-only

Dizionario **kwargs

Assegnati per posizione

Raccolti in una tupla

Devi usare il nome

Raccolti in un dizionario

20.5) Unpacking degli argomenti quando si chiamano funzioni

Così come *args e **kwargs raccolgono argomenti quando definisci le funzioni, puoi usare * e ** per fare unpacking di raccolte quando chiami una funzione.

20.5.1) Unpacking di sequenze con *

L’operatore * fa l’unpacking di una sequenza (lista, tupla, ecc.) in argomenti posizionali separati:

python
def calculate_rectangle_area(width, height):
    """Calculate the area of a rectangle."""
    return width * height
 
# Invece di passare gli argomenti singolarmente
dimensions = [5, 10]
area = calculate_rectangle_area(dimensions[0], dimensions[1])
print(area)
 
# Fare l'unpacking della lista direttamente
area = calculate_rectangle_area(*dimensions)
print(area)

Output:

50
50

Quando scrivi *dimensions, Python fa l’unpacking della lista [5, 10] in due argomenti separati, come se avessi scritto calculate_rectangle_area(5, 10).

Questo funziona con qualsiasi iterabile:

python
def format_name(first, middle, last):
    """Format a full name."""
    return f"{first} {middle} {last}"
 
# Unpacking di una tupla
name_tuple = ("John", "Q", "Public")
print(format_name(*name_tuple))
 
# Unpacking di una lista
name_list = ["Jane", "M", "Doe"]
print(format_name(*name_list))
 
# Persino l'unpacking di una stringa (ogni carattere diventa un argomento)
# Questo funziona solo se la funzione si aspetta il numero giusto di argomenti
def show_first_three(a, b, c):
    return f"{a}, {b}, {c}"
 
print(show_first_three(*"ABC"))

Output:

John Q Public
Jane M Doe
A, B, C

20.5.2) Unpacking di dizionari con **

L’operatore ** fa l’unpacking di un dizionario in argomenti keyword:

python
def create_user(username, email, age):
    """Create a user profile."""
    return f"User: {username}, Email: {email}, Age: {age}"
 
# Dizionario con chiavi che corrispondono ai nomi dei parametri
user_data = {
    "username": "alice",
    "email": "alice@example.com",
    "age": 28
}
 
# Fare l'unpacking del dizionario
profile = create_user(**user_data)
print(profile)

Output:

User: alice, Email: alice@example.com, Age: 28

Quando scrivi **user_data, Python fa l’unpacking del dizionario in argomenti keyword, equivalente a:

python
create_user(username="alice", email="alice@example.com", age=28)

Le chiavi del dizionario devono corrispondere ai nomi dei parametri della funzione, altrimenti otterrai un errore:

python
# Non valido: una chiave del dizionario non corrisponde a un nome di parametro
invalid_data = {"name": "bob", "email": "bob@example.com", "age": 30}
# profile = create_user(**invalid_data)
# TypeError: create_user() got an unexpected keyword argument 'name'

20.5.3) Combinare l’unpacking con argomenti regolari

Puoi mescolare argomenti decompattati con argomenti regolari:

python
def calculate_total(base_price, tax_rate, discount):
    """Calculate total price after tax and discount."""
    subtotal = base_price * (1 + tax_rate)
    total = subtotal * (1 - discount)
    return round(total, 2)
 
# Alcuni argomenti regolari, alcuni decompattati
pricing = [0.08, 0.10]  # tax_rate e discount
total = calculate_total(100, *pricing)
print(total)

Output:

97.2

Puoi anche fare l’unpacking di più raccolte in una singola chiamata:

python
def create_full_address(street, city, state, zip_code, country):
    """Create a complete address."""
    return f"{street}, {city}, {state} {zip_code}, {country}"
 
street_address = ["123 Main St", "Springfield"]
location_details = ["IL", "62701", "USA"]
 
address = create_full_address(*street_address, *location_details)
print(address)

Output:

123 Main St, Springfield, IL 62701, USA

20.5.4) Esempio pratico: chiamate di funzione flessibili

L’unpacking è particolarmente utile quando lavori con dati provenienti da fonti esterne:

python
def send_email(recipient, subject, body, cc=None, bcc=None):
    """Send an email with optional CC and BCC."""
    message = f"To: {recipient}\nSubject: {subject}\n\n{body}"
    
    if cc:
        message += f"\nCC: {cc}"
    if bcc:
        message += f"\nBCC: {bcc}"
    
    return message
 
# Dati email da un file di configurazione o da un database
email_config = {
    "recipient": "user@example.com",
    "subject": "Welcome",
    "body": "Thank you for signing up!",
    "cc": "manager@example.com"
}
 
# Fare l'unpacking della configurazione direttamente
result = send_email(**email_config)
print(result)

Output:

To: user@example.com
Subject: Welcome
 
Thank you for signing up!
CC: manager@example.com

Questo schema rende facile passare gli argomenti di una funzione come strutture dati, cosa comune quando si costruiscono API o si elaborano file di configurazione.

operatore *

operatore **

Raccolta

Tipo di unpacking

Unpacking di sequenza

Unpacking di dizionario

Lista/Tupla → Argomenti posizionali

Dict → Argomenti keyword

Chiamata di funzione

20.6) La trappola degli argomenti predefiniti mutabili (perché i default di lista persistono)

Uno dei tranelli più noti di Python riguarda l’uso di oggetti mutabili (come liste o dizionari) come valori predefiniti dei parametri. Comprendere questo problema è fondamentale per scrivere funzioni corrette.

20.6.1) Il problema: default mutabili condivisi

Considera questa funzione apparentemente innocua:

python
def add_student(name, grades=[]):
    """Add a student with their grades."""
    grades.append(name)
    return grades
 
# Prima chiamata
students1 = add_student("Alice")
print(students1)
 
# Seconda chiamata - ci aspettiamo una lista nuova
students2 = add_student("Bob")
print(students2)
 
# Terza chiamata
students3 = add_student("Carol")
print(students3)

Output:

['Alice']
['Alice', 'Bob']
['Alice', 'Bob', 'Carol']

Questo comportamento sorprende molti programmatori. Ogni chiamata a add_student() senza fornire un argomento grades usa lo stesso oggetto lista, non uno nuovo. La lista persiste tra le chiamate di funzione, accumulando valori.

20.6.2) Perché succede: i valori predefiniti vengono creati una sola volta

La chiave per comprendere questo comportamento è sapere quando vengono creati i valori predefiniti. Python valuta i valori predefiniti dei parametri una sola volta, quando la funzione è definita, non ogni volta che la funzione viene chiamata.

python
def demonstrate_default_creation():
    """Show when defaults are created."""
    print("Function defined!")
 
def use_default(value=demonstrate_default_creation()):
    """Use a default that calls a function."""
    return value
 
# Il messaggio viene stampato quando la funzione è DEFINITA, non chiamata

Output:

Function defined!

Quando Python incontra la riga def use_default, valuta il parametro predefinito value=demonstrate_default_creation(). Questo chiama demonstrate_default_creation(), che stampa immediatamente "Function defined!". Le chiamate successive a use_default() non valutano di nuovo il valore predefinito, quindi non viene stampato nulla di aggiuntivo.

Quando Python incontra def add_student(name, grades=[]):, crea un oggetto lista vuoto e lo memorizza come valore predefinito per grades. Ogni chiamata successiva che non fornisce un argomento grades usa quello stesso oggetto lista.

Ecco una dimostrazione più chiara usando l’identità dell’oggetto:

python
def show_list_identity(items=[]):
    """Show that the same list object is reused."""
    print(f"List ID: {id(items)}")
    items.append("item")
    return items
 
# Ogni chiamata usa lo stesso oggetto lista (stesso ID)
show_list_identity()
show_list_identity()
show_list_identity()

Output:

List ID: 140234567890123
List ID: 140234567890123
List ID: 140234567890123

I numeri ID esatti varieranno sul tuo sistema, ma nota che tutte e tre le chiamate mostrano lo stesso valore ID, dimostrando che stanno usando lo stesso oggetto lista. La funzione id() restituisce un identificatore univoco per ogni oggetto in memoria: quando gli ID coincidono, è lo stesso oggetto.

20.6.3) Lo schema corretto: usare None come default

La soluzione standard è usare None come valore predefinito e creare un nuovo oggetto mutabile all’interno della funzione:

python
def add_student_correct(name, grades=None):
    """Add a student with their grades (correct version)."""
    if grades is None:
        grades = []  # Crea una NUOVA lista ogni volta
    
    grades.append(name)
    return grades
 
# Ora ogni chiamata ottiene la propria lista
students1 = add_student_correct("Alice")
print(students1)
 
students2 = add_student_correct("Bob")
print(students2)
 
students3 = add_student_correct("Carol")
print(students3)

Output:

['Alice']
['Bob']
['Carol']

Questo schema funziona perché None è immutabile e una nuova lista viene creata nel corpo della funzione ogni volta che grades è None.

20.6.4) Lo stesso problema con i dizionari

Questo problema riguarda tutti i tipi mutabili, non solo le liste:

python
# SBAGLIATO: default di dizionario
def create_config_wrong(key, value, config={}):
    """Create a configuration (BUGGY VERSION)."""
    config[key] = value
    return config
 
config1 = create_config_wrong("theme", "dark")
print(config1)
 
config2 = create_config_wrong("language", "en")
print(config2)
 
print("---")
 
# CORRETTO: None come default
def create_config_correct(key, value, config=None):
    """Create a configuration (CORRECT VERSION)."""
    if config is None:
        config = {}
    
    config[key] = value
    return config
 
config1 = create_config_correct("theme", "dark")
print(config1)
 
config2 = create_config_correct("language", "en")
print(config2)

Output:

{'theme': 'dark'}
{'theme': 'dark', 'language': 'en'}
---
{'theme': 'dark'}
{'language': 'en'}

20.6.5) Riepilogo: la regola d’oro

Non usare mai oggetti mutabili (liste, dizionari, set) come valori predefiniti dei parametri. Usa sempre None e crea l’oggetto mutabile dentro la funzione:

python
# ❌ SBAGLIATO
def function(items=[]):
    pass
 
# ✅ CORRETTO
def function(items=None):
    if items is None:
        items = []
    # Ora puoi usare items in sicurezza

Questo schema garantisce che ogni chiamata della funzione ottenga il proprio oggetto mutabile indipendente, evitando bug misteriosi in cui i dati “perdono” tra chiamate.


In questo capitolo, abbiamo esplorato in profondità il sistema flessibile di parametri e argomenti di Python. Hai imparato a usare argomenti posizionali e keyword, fornire valori predefiniti, gestire numeri variabili di argomenti con *args e **kwargs, fare unpacking di raccolte quando chiami le funzioni ed evitare la trappola degli argomenti predefiniti mutabili.

Questi meccanismi ti danno strumenti potenti per progettare interfacce di funzione che siano sia flessibili sia facili da usare. Man mano che scriverai più funzioni, svilupperai intuizione su quali schemi di parametri si adattino meglio a situazioni diverse. La chiave è bilanciare flessibilità e chiarezza: rendi le tue funzioni facili da chiamare correttamente e difficili da chiamare in modo scorretto.


© 2025. Primesoft Co., Ltd.
support@primesoft.ai