ProgrammazioneSviluppatore Python middle/senior

Parla del lavoro e dell'uso delle funzioni partial e partialmethod del modulo functools. Qual è la differenza tra di loro, perché applicarle nella progettazione del codice e quali sono le peculiarità del loro utilizzo?

Supera i colloqui con l'assistente IA Hintsage

Risposta.

Storia della domanda

La funzione partial è apparsa nella libreria standard di Python (modulo functools) a partire da Python 2.5, per implementare il pattern del currying e l'applicazione parziale degli argomenti a una funzione. partialmethod è apparso con Python 3.4 per un'applicazione analoga nei metodi delle classi.

Problema

Spesso nei progetti reali sorge la necessità di fissare parte degli argomenti di una funzione, per ottenere una nuova funzione con "valori memorizzati" di alcuni argomenti. Questo è utile per i callback, i modelli di passaggio degli argomenti, o per aumentare la leggibilità del codice, specialmente nella programmazione funzionale e quando si utilizza un'API che si aspetta un certo stile di funzioni.

Soluzione

La funzione partial restituisce un nuovo oggetto-funzione, in cui alcuni degli argomenti o argomenti chiave passati sono "fissati" (hard-coded). partialmethod implementa lo stesso approccio per i metodi delle classi, supportando correttamente la meccanica del passaggio di self/cls.

Esempio di codice:

from functools import partial, partialmethod def power(base, exponent): return base ** exponent # Fissiamo l'esponente = 2 square = partial(power, exponent=2) print(square(5)) # 25 class Math: def power(self, base, exponent): return base ** exponent square = partialmethod(power, exponent=2) m = Math() print(m.square(5)) # 25

Caratteristiche chiave:

  • partial crea una nuova funzione con valori di parte degli argomenti hard-coded.
  • partialmethod è una costruzione analoga per i metodi delle classi, che funziona correttamente con self/cls.
  • Aumentano la leggibilità, consentono di creare "wrappers" funzionali e callback con parametri fissati.

Domande insidiose.

Si può usare partial per i metodi di classe, per ottenere un analogo di partialmethod?

No, partial non gestisce correttamente self/cls, quando applicato a un metodo di classe non funziona come previsto: self non verrà sostituito automaticamente.

class C: def m(self, x, y): return x + y wrong = partial(m, y=2) # Sbagliato!

Chiamando c.wrong(5) si otterrà un TypeError, perché self non verrà passato automaticamente.

L'oggetto creato tramite partial è una funzione?

Sì, il risultato dell'applicazione di partial è un oggetto che si comporta come una funzione, supporta la chiamata, il nome, la docstring, ecc., ma non è una funzione normale, è un oggetto della classe partial, che implementa il protocollo call.

from functools import partial f = partial(pow, 2) print(type(f)) # <class 'functools.partial'>

Funziona partial con gli argomenti predefiniti della funzione?

Sì, partial può "sovrascrivere" i valori predefiniti nella funzione interna; se vengono passati argomenti a partial, hanno la priorità sui valori predefiniti nella funzione stessa. Tuttavia, è importante non duplicare valori, altrimenti si otterrà un errore.

Errori tipici e anti-pattern

  • Applicare partial a metodi bound, aspettandosi auto-assegnare correttamente self - non funziona, è necessario usare partialmethod.
  • Passare valori fissati troppo tardi o in modo errato - si verificherà un errore durante la chiamata.
  • Complicare la struttura del codice, abusando di partial per funzioni banali.

Esempio dalla vita reale

Caso negativo

Sviluppatore applica partial per un metodo di classe invece di partialmethod:

class MyClass: def f(self, x, y): ... squared = partial(f, y=2) ... obj = MyClass() obj.squared(5) # TypeError: missing 1 required positional argument: 'self'

Vantaggi:

  • Il codice appare conciso.

Svantaggi:

  • Non funziona "la magia" del passaggio di self.
  • L'errore si verifica solo a runtime.

Caso positivo

Invece, si applica partialmethod:

from functools import partialmethod class MyClass: def f(self, x, y): return x + y squared = partialmethod(f, y=2) obj = MyClass() print(obj.squared(5)) # 7

Vantaggi:

  • Funziona come previsto.
  • Il codice è leggibile e scalabile.

Svantaggi:

  • Richiede la conoscenza delle funzionalità di partialmethod (non per principianti).