{
  "assessmentTests": {
    "python_test": {
      "name": "Python Test",
      "desc": "30 domande su scenari pratici di sintassi, funzioni, strutture dati e comportamenti degli oggetti — scopri se il tuo Python rispecchia ciò che una job posting intende per competenza Python.",
      "recommendation": "Il tuo profilo di competenze Python",
      "results": {
        "beginner": {
          "name": "Principiante",
          "desc": "Riesci a seguire uno script breve e a cambiare i valori al suo interno, ma le domande che hai sbagliato si concentrano sul comportamento specifico del linguaggio piuttosto che sulla logica generale — un argomento predefinito che viene costruito una sola volta e riutilizzato, la differenza tra is e == su due liste che sembrano uguali, una variabile che diventa locale nel momento in cui le assegni qualcosa in una funzione. Niente di questo riguarda l'essere scarsi a programmare; sono regole specifiche di Python che confondono allo stesso modo le persone che vengono da altri linguaggi e i principianti. Contano al lavoro perché ognuno di questi è il luogo dove si nasconde un bug in codice che sembra corretto a prima vista.",
          "recommendation": "Inizia con tre cose, in questo ordine: argomenti mutabili predefiniti (perché basket=[] è pericoloso), la differenza tra is e ==, e come funziona lo scope delle variabili all'interno di una funzione. Il tutorial ufficiale di Python su docs.python.org copre tutti e tre direttamente, e il termine «Python gotchas» porta a esempi elaborati di ognuno."
        },
        "intermediate": {
          "name": "Intermedio",
          "desc": "Gestisci comodamente lo scripting quotidiano — strutture dati, flusso di controllo, funzioni dirette — e non saresti rallentato da lavori di manutenzione di routine o da una piccola feature su una base di codice esistente. Il divario tra qui e Avanzato è per lo più in ciò che accade quando le funzionalità di convenienza di Python interagiscono l'una con l'altra: una copia superficiale che lascia i dati annidati condivisi quando ti aspettavi che fossero duplicati, il valore di ritorno di un generator che non diventa silenziosamente parte del suo output, i decorator che si impilano in un ordine facile da invertire. Questi sono i tipi di bug che un revisore senior segnala in dieci secondi e tutti gli altri passano un'ora a rincorrere.",
          "recommendation": "Concentrati su oggetti e interno dell'iterazione: copia superficiale rispetto a copia profonda, cosa fa realmente un generator con return, e come funziona l'ordine di impilamento dei decorator. Poi le basi delle classi che la maggior parte dei tutorial salta — uguaglianza predefinita senza __eq__, e perché un attributo di classe mutabile è condiviso tra ogni istanza."
        },
        "advanced": {
          "name": "Avanzato",
          "desc": "Questo è il livello che la maggior parte delle job posting intende con «Python forte». Raggiungi lo strumento giusto senza cercarlo, sai perché un pezzo di stato condiviso è cambiato piuttosto che solo che è successo, e riesci a leggere la funzione di qualcun altro e prevedere il suo output correttamente. Ciò che separa questa fascia dalla cima è il lato difensivo del lavoro: sapere quale di due approcci che sembrano corretti si comporterà male una volta che un attributo di classe, un argomento solo per parola chiave, o una gerarchia di eccezioni è coinvolta.",
          "recommendation": "Approfondisci le parti che proteggono una base di codice che altre persone toccano anche: argomenti solo per parola chiave come una scelta di progettazione dell'API piuttosto che una curiosità di sintassi, la gerarchia di eccezioni (in modo che le clausole except catturino la giusta famiglia di errore, non solo una classe), e gestori di contesto come il modo standard per garantire la pulizia. Gli articoli di approfondimento di Real Python sono un buon prossimo passo per tutti e tre."
        },
        "expert": {
          "name": "Esperto",
          "desc": "Hai segnato il massimo in ogni sezione — sintassi di base, funzioni e scope, strutture dati, e idiomi orientati agli oggetti. Praticamente, questo significa che puoi ricevere il file di uno sconosciuto e spiegare perché produce l'output che fa, non solo quello che la sintassi dice che dovrebbe fare, che è l'abilità più difficile e più preziosa. A questo livello il linguaggio grezzo è raramente il fattore limitante nel tuo lavoro; il limite è solitamente la dimensione o la forma del sistema attorno al codice.",
          "recommendation": "I rendimenti ora sono negli strumenti e nella revisione: controllo dei tipi statici con mypy o pyright, profilazione prima dell'ottimizzazione piuttosto che indovinare, e lettura delle pull request di altre persone esattamente per questa categoria di bug — quelli che passano ogni test e fanno comunque la cosa sbagliata con un input specifico. Se sei sottoposto a screening per un ruolo, descrivi un bug come questo che hai trovato piuttosto che nominare caratteristiche del linguaggio — dimostra il ragionamento, non solo il vocabolario."
        }
      },
      "questions": [
        {
          "question": "Scrivi def add_item(item, basket=[]): basket.append(item); return basket, poi chiami add_item('a') e add_item('b'), entrambe le volte lasciando basket fuori. Cosa ritorna la seconda chiamata?",
          "options": [
            {
              "icon": "",
              "label": "['b'] — ogni chiamata ottiene una lista vuota fresca per impostazione predefinita"
            },
            {
              "icon": "",
              "label": "TypeError — devi fornire un argomento basket"
            },
            {
              "icon": "",
              "label": "['a', 'b'] — la lista predefinita viene creata una sola volta e riutilizzata tra le chiamate"
            },
            {
              "icon": "",
              "label": "['a'] poi ['b'] come due liste indipendenti a elemento singolo"
            }
          ]
        },
        {
          "question": "a = [1, 2, 3] e b = [1, 2, 3]. Cosa ritorna a == b, e cosa ritorna a is b?",
          "options": [
            {
              "icon": "",
              "label": "True per entrambi — liste identiche sono sempre lo stesso oggetto"
            },
            {
              "icon": "",
              "label": "True per ==, False per is — contenuti uguali ma oggetti diversi in memoria"
            },
            {
              "icon": "",
              "label": "False per entrambi — letterali separati non sono mai uguali in Python"
            },
            {
              "icon": "",
              "label": "True per is, False per == — is controlla l'uguaglianza di valore per le liste"
            }
          ]
        },
        {
          "question": "In Python 3, a cosa valuta 7 / 2, e a cosa valuta 7 // 2?",
          "options": [
            {
              "icon": "",
              "label": "3.5, poi 3 — / ritorna sempre un float, // applica il floor al risultato"
            },
            {
              "icon": "",
              "label": "3, poi 3.5 — / tronca e // ritorna un float"
            },
            {
              "icon": "",
              "label": "3.5, poi 3.5 — entrambi gli operatori ritornano un float in Python 3"
            },
            {
              "icon": "",
              "label": "3, poi 3 — entrambi gli operatori applicano il floor al risultato in Python 3"
            }
          ]
        },
        {
          "question": "s = 'hello', poi esegui s[0] = 'H'. Cosa succede?",
          "options": [
            {
              "icon": "",
              "label": "s diventa 'Hello'"
            },
            {
              "icon": "",
              "label": "s diventa 'Hhello' perché il carattere viene inserito, non sostituito"
            },
            {
              "icon": "",
              "label": "TypeError — le stringhe sono immutabili e non supportano l'assegnazione di elementi"
            },
            {
              "icon": "",
              "label": "Nulla — l'assegnazione viene silenziosamente ignorata e s rimane 'hello'"
            }
          ]
        },
        {
          "question": "x = [10, 20, 30, 40, 50]. Cosa ritorna x[1:4]?",
          "options": [
            {
              "icon": "",
              "label": "[20, 30, 40, 50]"
            },
            {
              "icon": "",
              "label": "[10, 20, 30, 40]"
            },
            {
              "icon": "",
              "label": "[20, 30, 40]"
            },
            {
              "icon": "",
              "label": "[30, 40]"
            }
          ]
        },
        {
          "question": "name = 'Ana' e age = 30. Cosa produce f'{name} is {age}'?",
          "options": [
            {
              "icon": "",
              "label": "{name} is {age}"
            },
            {
              "icon": "",
              "label": "Ana is 30"
            },
            {
              "icon": "",
              "label": "name is age"
            },
            {
              "icon": "",
              "label": "TypeError, perché le f-string richiedono il metodo format()"
            }
          ]
        },
        {
          "question": "Cosa valuta 1 < 2 < 3?",
          "options": [
            {
              "icon": "",
              "label": "True — Python concatena i confronti come (1 < 2) and (2 < 3)"
            },
            {
              "icon": "",
              "label": "False — i confronti concatenati vengono valutati da sinistra a destra senza short-circuiting, dando 1 < 2 < 3 -> 1 < True -> False"
            },
            {
              "icon": "",
              "label": "SyntaxError — i confronti non possono essere concatenati"
            },
            {
              "icon": "",
              "label": "3 — Python ritorna l'ultimo valore della catena"
            }
          ]
        },
        {
          "question": "Cosa ritorna bool([]), e cosa ritorna bool([0])?",
          "options": [
            {
              "icon": "",
              "label": "False, poi False — qualsiasi lista contenente solo valori falsy è falsy"
            },
            {
              "icon": "",
              "label": "False, poi True — una lista vuota è falsy, una lista contenente un elemento (anche 0) è truthy"
            },
            {
              "icon": "",
              "label": "True, poi True — gli oggetti non None sono sempre truthy"
            },
            {
              "icon": "",
              "label": "True, poi False — una lista è truthy solo quando vuota"
            }
          ]
        },
        {
          "question": "Esiste un count = 0 a livello di modulo. Dentro def increment(): count += 1; return count, chiami increment(). Cosa succede?",
          "options": [
            {
              "icon": "",
              "label": "UnboundLocalError — assegnare a count in qualsiasi punto della funzione la rende locale, quindi count += 1 cerca di leggere una variabile locale prima che sia assegnata"
            },
            {
              "icon": "",
              "label": "Ritorna 1 e incrementa il count a livello di modulo a 1"
            },
            {
              "icon": "",
              "label": "Ritorna 1 ma lascia il count a livello di modulo a 0"
            },
            {
              "icon": "",
              "label": "Solleva NameError perché count non è mai stato dichiarato all'interno della funzione"
            }
          ]
        },
        {
          "question": "def f(*args, **kwargs): return args, kwargs. Cosa ritorna f(1, 2, x=3)?",
          "options": [
            {
              "icon": "",
              "label": "([1, 2], {'x': 3})"
            },
            {
              "icon": "",
              "label": "((1, 2), {'x': 3})"
            },
            {
              "icon": "",
              "label": "(1, 2, {'x': 3})"
            },
            {
              "icon": "",
              "label": "TypeError — gli argomenti posizionali e gli argomenti nominati non possono essere mischiati in una sola chiamata"
            }
          ]
        },
        {
          "question": "Costruisci funcs = [lambda: i for i in range(3)] in un ciclo. Cosa ritorna [f() for f in funcs]?",
          "options": [
            {
              "icon": "",
              "label": "[2, 2, 2] — ogni lambda cerca i quando viene chiamata, e a quel punto il ciclo ha finito con i uguale a 2"
            },
            {
              "icon": "",
              "label": "[0, 1, 2] — ogni lambda cattura il valore di i al momento della sua creazione"
            },
            {
              "icon": "",
              "label": "[0, 0, 0] — i lambda catturano sempre il primo valore assegnato a una variabile"
            },
            {
              "icon": "",
              "label": "NameError, perché i non esiste più una volta che il ciclo termina"
            }
          ]
        },
        {
          "question": "Una funzione è definita con @a direttamente sopra @b, che si trova direttamente sopra def f(): pass. In quale ordine i decorator vengono applicati a f?",
          "options": [
            {
              "icon": "",
              "label": "b avvolge f per primo, poi a avvolge il risultato di b — il decorator più vicino alla funzione viene eseguito per primo"
            },
            {
              "icon": "",
              "label": "a avvolge f per primo, poi b avvolge il risultato — i decorator si applicano dall'alto verso il basso"
            },
            {
              "icon": "",
              "label": "Entrambi i decorator avvolgono f indipendentemente e solo l'ultimo definito ha effetto"
            },
            {
              "icon": "",
              "label": "Dipende dall'ordine in cui i decorator vengono importati, non dalla loro posizione nel codice"
            }
          ]
        },
        {
          "question": "def f(a, *, b): return a + b. Quale di queste chiamate funziona?",
          "options": [
            {
              "icon": "",
              "label": "f(1, b=2) — b deve essere passato per parola chiave perché viene dopo il * nudo nella firma"
            },
            {
              "icon": "",
              "label": "f(1, 2) — gli argomenti posizionali riempiono i parametri da sinistra a destra indipendentemente da *"
            },
            {
              "icon": "",
              "label": "f(a=1, 2) — gli argomenti nominati devono venire per primi"
            },
            {
              "icon": "",
              "label": "f(1) — b è opzionale perché segue il *"
            }
          ]
        },
        {
          "question": "Una funzione generatore fa yield 1, poi yield 2, poi fa return 99. Cosa contiene list(the_generator()), e cosa succede a 99?",
          "options": [
            {
              "icon": "",
              "label": "[1, 2, 99] — return dentro un generatore si comporta come un yield in più"
            },
            {
              "icon": "",
              "label": "[99] — return sovrascrive immediatamente qualsiasi valore già yielded"
            },
            {
              "icon": "",
              "label": "[1, 2] — il valore di ritorno di un generatore diventa il valore allegato al StopIteration che solleva, non viene fatto il yield"
            },
            {
              "icon": "",
              "label": "RuntimeError, perché i generatori non possono contenere un'istruzione return"
            }
          ]
        },
        {
          "question": "In una funzione outer(), x = 1 è impostato, poi un inner() annidato fa nonlocal x; x += 1, e outer() chiama inner() prima di ritornare x. Cosa ritorna outer()?",
          "options": [
            {
              "icon": "",
              "label": "1 — inner() opera su una sua propria copia locale di x, quindi il cambiamento non persiste"
            },
            {
              "icon": "",
              "label": "UnboundLocalError, perché x non è dichiarato all'interno di inner() prima dell'istruzione nonlocal"
            },
            {
              "icon": "",
              "label": "SyntaxError — nonlocal può essere usato solo all'interno di classi"
            },
            {
              "icon": "",
              "label": "2 — nonlocal permette a inner() di modificare la x della funzione esterna invece di creare una nuova variabile locale"
            }
          ]
        },
        {
          "question": "d = {'a': 1, 'b': 2}. Cosa ritorna d.get('c', 0), e aggiunge 'c' al dizionario?",
          "options": [
            {
              "icon": "",
              "label": "0, e no — get() ritorna il valore predefinito senza inserire la chiave"
            },
            {
              "icon": "",
              "label": "0, e sì — get() inserisce la chiave con il valore predefinito"
            },
            {
              "icon": "",
              "label": "KeyError, perché 'c' non esiste"
            },
            {
              "icon": "",
              "label": "None, e sì — get() aggiunge sempre le chiavi mancanti con un valore None"
            }
          ]
        },
        {
          "question": "t = (1, 2, [3, 4]). Puoi eseguire t[2].append(5), e puoi eseguire t[0] = 10?",
          "options": [
            {
              "icon": "",
              "label": "Entrambi funzionano — annidare una lista dentro una tupla rende la tupla intera mutabile"
            },
            {
              "icon": "",
              "label": "Entrambi sollevano TypeError — nulla all'interno di una tupla può mai essere cambiato"
            },
            {
              "icon": "",
              "label": "t[2].append(5) solleva TypeError, ma t[0] = 10 funziona perché le tuple consentono la riassegnazione del loro primo elemento"
            },
            {
              "icon": "",
              "label": "t[2].append(5) funziona perché la lista dentro è mutabile; t[0] = 10 solleva TypeError perché la tupla stessa è immutabile"
            }
          ]
        },
        {
          "question": "Quale di questi ritorna un iteratore che calcola i valori pigramente piuttosto che costruire l'intera sequenza in memoria in una volta: [x*x for x in range(1000000)] o (x*x for x in range(1000000))?",
          "options": [
            {
              "icon": "",
              "label": "Quello con le parentesi, (x*x for x in range(1000000)) — è un'espressione generatore"
            },
            {
              "icon": "",
              "label": "Quello con le parentesi quadre — le comprensioni di lista sono sempre pigre in Python 3"
            },
            {
              "icon": "",
              "label": "Entrambi sono pigri — l'unica differenza è il tipo ritornato alla fine"
            },
            {
              "icon": "",
              "label": "Nessuno dei due — entrambi costruiscono la sequenza intera in memoria immediatamente"
            }
          ]
        },
        {
          "question": "Un blocco try diventa per zero, la clausola except ZeroDivisionError stampa 'a', e una clausola finally stampa 'b'. Cosa stampa, e in quale ordine?",
          "options": [
            {
              "icon": "",
              "label": "b poi a — finally viene sempre eseguito prima di qualsiasi clausola except"
            },
            {
              "icon": "",
              "label": "a poi b — l'except che corrisponde viene eseguito per primo, e finally viene eseguito dopo indipendentemente"
            },
            {
              "icon": "",
              "label": "Solo b — l'eccezione non è effettivamente catturata, quindi 'a' non viene mai stampato"
            },
            {
              "icon": "",
              "label": "Solo a — finally viene saltato una volta che un'eccezione è già stata gestita"
            }
          ]
        },
        {
          "question": "import copy; a = [[1, 2], [3, 4]]; b = copy.copy(a); b[0].append(99). Anche a[0] cambia?",
          "options": [
            {
              "icon": "",
              "label": "No — copy.copy() duplica sempre anche gli oggetti annidati"
            },
            {
              "icon": "",
              "label": "No — aggiungere a b[0] crea una lista completamente nuova solo per b[0]"
            },
            {
              "icon": "",
              "label": "Sì, ma solo perché copy.copy() è stato chiamato senza l'argomento deepcopy"
            },
            {
              "icon": "",
              "label": "Sì — copy.copy() fa una copia superficiale, quindi le liste annidate sono ancora condivise tra a e b"
            }
          ]
        },
        {
          "question": "d = {'x': 1, 'y': 2}. Cosa controlla 'x' in d — le chiavi, i valori, o entrambi?",
          "options": [
            {
              "icon": "",
              "label": "I valori — in controlla se 1 o 2 uguaglia 'x'"
            },
            {
              "icon": "",
              "label": "Sia le chiavi che i valori vengono controllati"
            },
            {
              "icon": "",
              "label": "Nessuno dei due — in su un dict solleva sempre TypeError"
            },
            {
              "icon": "",
              "label": "Le chiavi — in su un dizionario verifica l'appartenenza tra le sue chiavi, non i suoi valori"
            }
          ]
        },
        {
          "question": "names = ['Al', 'Bo'] e scores = [10, 20]. Cosa ritorna list(zip(names, scores))?",
          "options": [
            {
              "icon": "",
              "label": "[('Al', 'Bo'), (10, 20)]"
            },
            {
              "icon": "",
              "label": "['Al', 10, 'Bo', 20]"
            },
            {
              "icon": "",
              "label": "[('Al', 10), ('Bo', 20)]"
            },
            {
              "icon": "",
              "label": "ValueError, perché zip richiede tre o più argomenti"
            }
          ]
        },
        {
          "question": "class Dog: tricks = [] contiene una lista a livello di classe, e add_trick aggiunge a self.tricks. d1 = Dog() e d2 = Dog() vengono entrambi creati, poi d1.add_trick('sit') viene eseguito. Cosa contiene d2.tricks?",
          "options": [
            {
              "icon": "",
              "label": "[] — ogni istanza ottiene la sua propria copia indipendente di tricks"
            },
            {
              "icon": "",
              "label": "['sit'] — tricks è una lista a livello di classe condivisa da ogni istanza, quindi il cambiamento di d1 è visibile su d2 anche"
            },
            {
              "icon": "",
              "label": "TypeError — le istanze non possono aggiungere a un attributo di classe"
            },
            {
              "icon": "",
              "label": "['sit'] solo dopo che d2 = Dog() viene rieseguito"
            }
          ]
        },
        {
          "question": "class A ha def greet(self): return 'A'. class B(A) ha def greet(self): return 'B' + super().greet(). Cosa ritorna B().greet()?",
          "options": [
            {
              "icon": "",
              "label": "'AB' — super() viene sempre eseguito prima del codice della sottoclasse stessa"
            },
            {
              "icon": "",
              "label": "'B' — super().greet() viene ignorato perché B già sovrascrive greet"
            },
            {
              "icon": "",
              "label": "'BA' — super().greet() chiama il metodo di A e il suo risultato viene aggiunto a 'B'"
            },
            {
              "icon": "",
              "label": "AttributeError, perché A non ha alcun metodo greet visibile a B"
            }
          ]
        },
        {
          "question": "class Point archivia self.x in __init__ e non definisce altri metodi. p1 = Point(1) e p2 = Point(1). Per impostazione predefinita, cosa ritorna p1 == p2?",
          "options": [
            {
              "icon": "",
              "label": "True — Python confronta per impostazione predefinita tutti gli attributi corrispondenti"
            },
            {
              "icon": "",
              "label": "False — senza un __eq__ personalizzato, == ritorna al confronto di identità, e p1 e p2 sono oggetti diversi"
            },
            {
              "icon": "",
              "label": "True, ma solo perché entrambi gli oggetti sono stati creati con lo stesso argomento"
            },
            {
              "icon": "",
              "label": "TypeError, perché Point non definisce __eq__"
            }
          ]
        },
        {
          "question": "Quale clausola except di seguito catturerà anche un ZeroDivisionError: except ArithmeticError, except ValueError, o except KeyError?",
          "options": [
            {
              "icon": "",
              "label": "except ValueError — gli errori di divisione sono trattati come valori non validi"
            },
            {
              "icon": "",
              "label": "except KeyError — qualsiasi errore di runtime non legato a un tipo specifico ritorna a KeyError"
            },
            {
              "icon": "",
              "label": "Nessuno di loro — solo except ZeroDivisionError può catturarlo"
            },
            {
              "icon": "",
              "label": "except ArithmeticError — ZeroDivisionError è una sottoclasse di ArithmeticError"
            }
          ]
        },
        {
          "question": "Apri un file con open('data.txt') as f: e viene sollevata un'eccezione durante la lettura all'interno del blocco. Il file viene comunque chiuso?",
          "options": [
            {
              "icon": "",
              "label": "No — il file rimane aperto perché il blocco non è stato completato normalmente"
            },
            {
              "icon": "",
              "label": "Solo se aggiungi anche una clausola finally intorno all'istruzione with"
            },
            {
              "icon": "",
              "label": "Sì — l'istruzione with chiama il metodo __exit__ del file, che lo chiude, anche se si verifica un'eccezione all'interno del blocco"
            },
            {
              "icon": "",
              "label": "Dipende dal tipo di eccezione — solo le eccezioni catturate altrove attivano la pulizia"
            }
          ]
        },
        {
          "question": "x = [3, 1, 2] e y = x.sort(). Cosa contiene y?",
          "options": [
            {
              "icon": "",
              "label": "[1, 2, 3] — sort() ritorna la lista appena ordinata"
            },
            {
              "icon": "",
              "label": "None — list.sort() ordina sul posto e ritorna None; x stessa diventa [1, 2, 3]"
            },
            {
              "icon": "",
              "label": "[3, 1, 2] — sort() ritorna la lista originale non ordinata e modifica solo l'ordine interno di x"
            },
            {
              "icon": "",
              "label": "TypeError, perché sort() non può essere assegnato a una variabile"
            }
          ]
        },
        {
          "question": "Cosa succede quando esegui 'Score: ' + 42?",
          "options": [
            {
              "icon": "",
              "label": "'Score: 42' — Python converte il numero automaticamente all'interno di un'espressione +"
            },
            {
              "icon": "",
              "label": "'Score: ' — il numero viene silenziosamente scartato"
            },
            {
              "icon": "",
              "label": "42 — la stringa viene trasformata in un numero e aggiunta"
            },
            {
              "icon": "",
              "label": "TypeError — non puoi concatenare direttamente una str e un int, 42 deve essere convertito con str() per primo"
            }
          ]
        },
        {
          "question": "Devi controllare se una lista chiamata results è vuota. Quale è il modo idiomatico di Python: if results == []: , if len(results) == 0: , o if not results: ?",
          "options": [
            {
              "icon": "",
              "label": "if results == []: — questo è l'unico modo affidabile per rilevare una lista vuota"
            },
            {
              "icon": "",
              "label": "if len(results) == 0: — questo è richiesto perché le liste vuote sono truthy in Python"
            },
            {
              "icon": "",
              "label": "Tutti e tre non vengono mai usati nella pratica; l'approccio standard è if results is None:"
            },
            {
              "icon": "",
              "label": "if not results: — l'idioma Python si basa sulla verità, e una lista vuota è falsy"
            }
          ]
        }
      ],
      "optionOrderVersion": "92945565db6c55da"
    }
  }
}
