Topic: Un utilizzo "strano" di locals e vars  (Letto 79 volte)

0 Utenti e 1 Visitatore stanno visualizzando questo topic.

Offline nuzzopippo

  • python neanderthalensis
  • ****
  • Post: 258
  • Punti reputazione: 0
    • Mostra profilo
Un utilizzo "strano" di locals e vars
« il: Giugno 12, 2020, 11:25 »
I miei saluti.

Intrigato dalla seguente domanda :
Citazione
E' possibile in Python creare una lista o un dizionario di tipi pronti per essere istanziati?

Ho trovato possibile risposta mediante l'utilizzo delle funzioni builtins "vars()" e "locals()", utilizzate in tale modo :

>>> class Anagrafica:
def __init__(self, cognome, nome):
self.cognome = cognome
self.nome = nome
def __str__(self):
return self.nome + ' ' + self.cognome


>>> from math import sqrt as radice_quadrata
>>> class_and_func = {'anag': 'Anagrafica',
      'rad' : 'radice_quadrata'
      }
>>> n = locals()[class_and_func['anag']]('nuzzo', 'pippo')
>>> print(n)
pippo nuzzo
>>> type(n)
<class '__main__.Anagrafica'>
>>> vars(n)
{'cognome': 'nuzzo', 'nome': 'pippo'}
>>> num = locals()[class_and_func['rad']](9)
>>> print(num)
3.0
>>>


Ora, lo scopo di entrambe le funzioni builtin citate è quello di restituire dei "dizionari", locals del "current scope's local variables", vars del dizionario di un "oggetto", come mostrato nel codice, anche se utilizzato senza argomenti funziona come locals. Circa la modalità d'uso di locals (del suo equivalente "vars()") su applicata non ho trovato documentazione.

Mi sembra evidente che spulciando il dizionario restituito da "locals()" con la chiave definita come in esempio si stia, di fatto, istanziando una classe od invocando una funziona definita nello scope locale ... niente di strano, tutto sommato ma mi chiedo se effettuare tal genere di operazioni sia una modalità "corretta" o, invece, soggetta a "complicazioni" varie.

Per altro, una certo "prurito" alle mani viene dal provare la bultin "globals" ma forse è meglio attendere pensieri più profondi dei miei ;)

Offline RicPol

  • python sapiens sapiens
  • ******
  • Post: 3.062
  • Punti reputazione: 9
    • Mostra profilo
Re:Un utilizzo "strano" di locals e vars
« Risposta #1 il: Giugno 12, 2020, 17:32 »
Mah... no?
Prima di tutto non ho capito il senso della domanda. In Python tutto è un oggetto, quindi tutto deriva da una classe/tipo pronto a istanziare. Quindi tutto è pronto a essere istanziato. E siccome in una lista (o dizionario) ci puoi mettere qualsiasi cosa, comprese quindi delle classi, direi che la domanda si risponde da sola.
Non c'è nessun problema a fare cose del tipo

>>> classes = [int, str, list, tuple, bool, set, frozenset] # e chi più ne ha...
>>> [x('2') for x in classes]
[2, '2', ['2'], ('2',), True, {'2'}, frozenset({'2'})]

Ovviamente quando istanzi così alla cieca, ti affidi al fatto che i costruttori di tutte le classi abbiano la stessa signature. Per esempio, dict('2') o bytes('2') non funzionerebbero perché i loro costruttori si aspettano qualcosa in più.
Ma a parte questo, non è che ci siano problemi.

Il codice che scrivi è solo un modo di prenderla alla lontanissima, ma non c'è alcun bisogno di fare cose come

{'anag': 'Anagrafica', 'rad' : 'radice_quadrata' }

Vuoi mettere i nomi delle classi in un dizionario? E metti i nomi delle classi in un dizionario, chi te lo vieta. Non hai bisogno di metterli come stringhe e poi arrampicarti lungo i locals e i globals e quant'altro.
Anzi, mettere direttamente i nomi delle classi in un dizionario è il classico modo "light" di implementare uno strategy pattern (con o senza factory) in Python:

class Foo:
    def __init__(self, *a, **k): pass

class Bar:
    def __init__(self, *a, **k): pass

class Baz:
    def __init__(self, *a, **k): pass

def factory(choice, *a, **k):
    # restituisce un'istanza di una classe, scelta in base all'argomento choice
    klass = {'foo': Foo,
             'bar': Bar,
             'baz': Baz}[choice]
    return klass(*a, **k)

factory('foo', 1, 2, 3) # restituisce un oggetto Foo

Ovviamente, sempre posto che i costruttori abbiano la stessa signature, eccetera.

Strumenti come locals() e globals(), che ispezionano un namespace, non servono per la normale programmazione. Hanno un uso legittimo solo in scenari avanzati di metaprogrammazione (codice che lavora con altro codice). Per esempio, se stai scrivendo il sistema di auto-completion di un editor, allora potrebbe interessarti usare locals() per fornire suggerimenti da nomi che esistono solo nello scope locale, da unire a globals(). E se qualcuno scrive il nome di un oggetto, seguito da "punto", allora il tuo autocompletion potrebbe usare vars() per scoprire quali nomi sono disponibili all'interno di quell'oggetto, per prepararsi all'autocompletion successiva.

Poi trovi in giro diversi hack che usano globals() e locals() per vari scopi un po' esoterici. Ma in genere si tratta di codice mal scritto, o comunque scritto per fare in fretta.

Offline nuzzopippo

  • python neanderthalensis
  • ****
  • Post: 258
  • Punti reputazione: 0
    • Mostra profilo
Re:Un utilizzo "strano" di locals e vars
« Risposta #2 il: Giugno 13, 2020, 10:32 »
... In Python tutto è un oggetto, quindi ...

... Dio, l'avrò letto un milione di volte in questi ultimi due anni!

Riletto mi è balzato subito agli occhi, in tutta evidenze, ma è una direzione in cui il mio pensiero si blocca, evidentemente pur sapendo ciò non ho assimilato che anche il nome di una "definizione" (chiamo così per brevità) è anch'esso un oggetto, non ho digerito il concetto e non mi riesce concettualmente di applicarlo.

beh ... avevo ben ragione di aver dubbi su ciò che avevo fatto, era decisamente "sbagliato" perché inadatto allo scopo.
Grazie di avermelo fatto notare e di tutte le successive indicazioni, tutte interessanti.