Metaprogrammazione
Metaprogrammazione Capacità di un programma di trattare un programma (altro o se stesso) come dato. Un programma quindi può essere implementato in modo da poter leggere, analizzare, generare o trasformare un altro programma. Ci sono molte parti di python che sono forme di metaprogramming, tra cui [[globals e locals|
locals()eglobals()]] e [[#call|call]]
__call__()
import random
class RandomNumberGenerator:
def __call__(self):
return random.random()
randomGenerator = RandomNumberGenerator()
n = randomGenerator()
# o
n = randomGenerator.__call__()Ma come funziona?
- quando si chiama
object(...)l’interprete cerca un metodo__call__partendo dalla classe di cuiobjectè istanza, ed eventualmente cerca nelle superclassi __call__viene chiamato con l’oggetto passato come primo parametro (bound)- quindi
object.__call__()eobject()non sono propriamente uguali, perché il primo è unbound mentre il seconodo è bound.
Attenzione!
Se la classe avesse un metodo __call__, chiamare object.__call__() porterebbe ad un altro risultato.
randomGenerator.__call__ = lambda self: self.__class__.__name__
# si comporta normalmente e ritorna un numero random
randomGenerator()
# esegue la funzione lambda e ritorna da errore
# perchè non viene passato l'oggetto self (unbound)
randomGenerator.__call__()
# esegue la funzione lambda e ritorna il nome della classe
randomGenerator.__call__(randomGenerator)Quindi
randomGenerator()è equivalente a chiamaretype(randomGenerator).__call__(randomGenerator)- o -RandomNumberGenerator.__call__(randomGenerator)
type, la classe di singolarità, dal momento che può essere utilizzato come funzione per avere in ritorno la classe di un oggetto, utilizza il metodo __call__ appena visto.
type(O) ≡≡ type.__call__(type,O)
type(3)
type(type).__call__(type, 3)
type.__call__(type, 3)Istanziare le classi tramite __call__
La scrittura A() deve essere equivalente a type(A).__call__(A). Infatti, quando si istanzia un oggetto, è il metodo __call__ di type che viene chiamato.
numGenerator = type(RandomNumberGenerator).__call__(RandomNumberGenerator, <eventuali parametri per init>)Infatti, il tipo default di ogni classe è type
type(2) # int
type(int) # typeQuesto comportamento può essere modificato tramite l’utilizzo di Metaclass