Esistono due problemi di base che si verificano qui:
__xxx__
i metodi vengono cercati solo sulla classe
TypeError: can't set attributes of built-in/extension type 'module'
(1) significa che qualsiasi soluzione dovrebbe anche tenere traccia di quale modulo è stato esaminato, altrimenti ogni modulo avrebbe quindi il comportamento di sostituzione dell'istanza; e (2) significa che (1) non è nemmeno possibile ... almeno non direttamente.
Fortunatamente, sys.modules non è schizzinoso su ciò che va lì, quindi un wrapper funzionerà, ma solo per l'accesso al modulo (cioè import somemodule; somemodule.salutation('world')
; per l'accesso allo stesso modulo devi praticamente strappare i metodi dalla classe di sostituzione e aggiungerli a globals()
eiher con un metodo personalizzato sulla classe (che mi piace usare .export()
) o con una funzione generica (come quelle già elencate come risposte). Una cosa da tenere a mente: se il wrapper crea una nuova istanza ogni volta e la soluzione globale non lo è, finisci per comportarti in modo leggermente diverso Oh, e non riesci a usarli entrambi contemporaneamente - è l'uno o l'altro.
Aggiornare
Da Guido van Rossum :
Esiste effettivamente un hack che viene occasionalmente utilizzato e raccomandato: un modulo può definire una classe con la funzionalità desiderata, e quindi alla fine, sostituirsi in sys.modules con un'istanza di quella classe (o con la classe, se insisti , ma in genere è meno utile). Per esempio:
# module foo.py
import sys
class Foo:
def funct1(self, <args>): <code>
def funct2(self, <args>): <code>
sys.modules[__name__] = Foo()
Questo funziona perché il macchinario di importazione sta attivamente abilitando questo hack e mentre il suo passaggio finale estrae il modulo reale da sys.modules, dopo averlo caricato. (Questo non è un caso. L'hacking è stato proposto molto tempo fa e abbiamo deciso che ci piaceva abbastanza da supportarlo nei macchinari di importazione.)
Quindi il modo stabilito per ottenere ciò che vuoi è creare una singola classe nel tuo modulo, e come ultimo atto del modulo sostituire sys.modules[__name__]
con un'istanza della tua classe - e ora puoi giocare con __getattr__
/ __setattr__
/ __getattribute__
se necessario.
Nota 1 : se si utilizza questa funzionalità, qualsiasi altra cosa nel modulo, come globi, altre funzioni, ecc., Andrà persa al momento sys.modules
dell'assegnazione, quindi assicurarsi che tutto ciò che è necessario sia all'interno della classe di sostituzione.
Nota 2 : per supportare from module import *
è necessario aver __all__
definito nella classe; per esempio:
class Foo:
def funct1(self, <args>): <code>
def funct2(self, <args>): <code>
__all__ = list(set(vars().keys()) - {'__module__', '__qualname__'})
A seconda della versione di Python, potrebbero esserci altri nomi da omettere __all__
. La set()
può essere omesso se non è richiesta la compatibilità Python 2.