HOWTO · Python

Chiamare una funzione dal nome della stringa in Python

Dimostra come chiamare una funzione da una stringa in Python

In questa pagina

Quando il nome di una funzione è contenuto in una stringa, cercalo in un insieme definito di funzioni consentite e chiama l’oggetto funzione corrispondente. Per i comandi o altre azioni selezionate dall’utente, un dizionario è in genere la scelta più chiara: rende visibili i nomi disponibili e impedisce a un input arbitrario di selezionare codice estraneo.

Usa un dizionario per associare i nomi approvati alle funzioni

Definisci normalmente le funzioni, poi associa le stringhe accettate ai rispettivi oggetti funzione. Cerca il nome richiesto e verifica che il risultato sia chiamabile prima di invocarlo. Questo esempio gestisce un nome sconosciuto senza tentare di chiamarlo:

L’esecuzione fornita ha completato questo esempio autonomo con codice di uscita 0 e ha stampato 8. Eseguilo con:

Il codice e le etichette o gli output visibili in questa sezione restano in inglese.

def add(left, right):
    return left + right


def multiply(left, right):
    return left * right


functions = {"add": add, "multiply": multiply}
name = "add"
function = functions.get(name)

if callable(function):
    print(function(5, 3))
else:
    print(f"Unknown function: {name}")
8

La mappatura è una lista consentita: si possono selezionare solo i nomi aggiunti a functions. Usa .get() quando è previsto che un nome possa mancare e decidi come l’applicazione debba segnalare o gestire un nome sconosciuto. Se le funzioni accettano argomenti diversi, convalida anche gli argomenti per l’operazione selezionata: trovare il nome non garantisce che la firma della chiamata sia corretta.

Usa getattr() per recuperare un attributo da un oggetto o da un modulo

Usa getattr(object, name, default) quando la stringa deve selezionare un attributo di un oggetto o di un modulo specifico. Il terzo argomento fornisce un valore se l’attributo non esiste. Verifica poi che il valore sia chiamabile: un attributo esistente potrebbe contenere un dato anziché una funzione.

L’esecuzione fornita ha completato questo esempio con codice di uscita 0 e ha stampato 8. L’esempio recupera un metodo di istanza associato: Python associa calculator al metodo, quindi passa solo gli argomenti rimanenti richiesti dal metodo. Eseguilo con:

Il codice e le etichette o gli output visibili in questa sezione restano in inglese.

class Calculator:
    def add(self, left, right):
        return left + right


calculator = Calculator()
name = "add"
operation = getattr(calculator, name, None)

if callable(operation):
    print(operation(5, 3))
else:
    print(f"No callable attribute named {name}")
8

Lo stesso schema funziona con un oggetto modulo, per esempio getattr(math, name, None) dopo aver importato math. Cercando un metodo di istanza sull’istanza si ottiene un metodo associato; cercandolo sulla classe si ottiene la funzione non associata, che richiede l’istanza come argomento. Se la stringa proviene dall’esterno del codice, limita l’oggetto di partenza e i nomi degli attributi consentiti.

Usa globals() e locals() solo quando lo spazio dei nomi è scelto intenzionalmente

globals() restituisce lo spazio dei nomi globali del modulo corrente. Non restituisce le variabili locali. La ricerca tra i nomi globali può essere utile quando le funzioni si trovano intenzionalmente in quel modulo. L’esecuzione fornita ha completato questo esempio con codice di uscita 0 e ha stampato 8. Eseguilo con:

Il codice e le etichette o gli output visibili in questa sezione restano in inglese.

def add(left, right):
    return left + right


name = "add"
function = globals().get(name)

if callable(function):
    print(function(5, 3))
else:
    print(f"Unknown global function: {name}")
8

locals() fa riferimento allo spazio dei nomi locali nel punto in cui viene chiamata. All’interno di una funzione può esporre i nomi locali a quella funzione; non esamina le variabili locali del chiamante e non offre una ricerca generale nell’intero programma. Il suo comportamento dipende dall’ambito, quindi di solito è meno chiaro e meno adatto come meccanismo di smistamento rispetto a una mappatura esplicita. Per approfondire i dizionari, consulta il tutorial Python sui dizionari.

Perché eval() è rischioso per cercare una funzione

eval() valuta un’espressione Python, invece di cercare un singolo nome di funzione approvato. Per esempio, eval("add(5, 3)") analizza e valuta una chiamata usando i nomi disponibili nel suo ambiente di valutazione. Se l’espressione contiene input non attendibile, valutarla può eseguire codice con i privilegi del processo Python. Non usare eval() per smistare nomi forniti da utenti, file, richieste di rete o altre fonti non attendibili.

Se un’espressione proviene davvero da una fonte fidata e controllata, Python documenta il comportamento e i parametri relativi agli spazi dei nomi di eval(). Per la normale selezione di una funzione, una lista consentita basata su un dizionario è più semplice e limita le scelte dell’input. Analizzare o convalidare il testo non rende la valutazione arbitraria un’alternativa sicura a un insieme esplicito di operazioni.

Scegli il metodo di ricerca adatto al tuo input

Usa un dizionario quando le azioni disponibili devono essere esplicite, soprattutto per nomi forniti dall’utente o provenienti da fonti esterne. Usa getattr() quando l’oggetto o il modulo è già stato scelto e la stringa deve selezionarne uno degli attributi; controlla il valore predefinito e che il risultato sia chiamabile, e considera l’associazione dei metodi. Usa globals() solo quando la ricerca a livello di modulo è intenzionale. Usa locals() solo quando il codice deve accedere deliberatamente ai nomi dell’ambito locale corrente, non come meccanismo generale di smistamento. Evita eval() per cercare nomi se l’input non è completamente attendibile.

In ogni caso, la ricerca e l’invocazione sono decisioni distinte: prima limita quali funzioni chiamabili si possono selezionare, poi passa argomenti conformi alla loro interfaccia. Per un insieme fisso di comandi, questa separazione rende il comportamento più facile da verificare e mantenere.