2009-06-29 16 views
41

Perché Python non consente ai moduli di avere un __call__? (Oltre l'ovvio che non sarebbe facile importare direttamente). In particolare, perché non si utilizza la sintassi a(b) per trovare l'attributo __call__ come fa per funzioni, classi e oggetti? (È solo ricerca incompatili diverso per i moduli?)Moduli richiamabili

>>> print open("mod_call.py").read() 
def __call__(): 
    return 42 

>>> import mod_call 
>>> mod_call() 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
TypeError: 'module' object is not callable 
>>> mod_call.__call__() 
42 
+0

Migrazione di un decoratore da un pacchetto nel proprio sottomodulo. @esempio (...) era di gran lunga il caso d'uso più comune, ma @ example.special_case (...) era un nuovo uso. Non volevo implementarlo con un esempio di classe e metodi statici, dato che non era adatto. Non sono sicuro che un modulo richiamabile sia più adatto, ma ho iniziato a studiarlo e poi ho voluto sapere perché non ha funzionato. –

+3

Avevo anche pensato che potesse semplificare alcuni moduli come datetime e decimal, rendendo rispettivamente il modulo .__ call__ be datetime.datetime o decimal.Decimal. Tuttavia, digitare (decimale ('1')) non equivale a decimale e possibili altri problemi. * scrollando le spalle * Era un'idea. –

+0

"Oltre l'ovvio che non sarebbe facile importarlo direttamente." Perché pensi che? –

risposta

29

metodi speciali sono garantite solo per essere chiamato implicitamente quando essi sono definiti dal tipo, non sulla istanza. (__call__ è un attributo dell'istanza del modulo mod_call, non di <type 'module'>.) Non è possibile aggiungere metodi ai tipi incorporati.

http://docs.python.org/reference/datamodel.html#special-method-lookup-for-new-style-classes

+0

Sembra proprio la risposta giusta per me! Dal punto di vista degli uccelli sembra un difetto di design in Python. Un altro disegno sarebbe intrinsecamente difficile o addirittura impossibile? – jolvi

75

Python non permette ai moduli sostituiscono o aggiungere qualsiasi metodo magico, perché mantenere il modulo oggetti semplici, regolari e leggero è troppo vantaggiosa considerando come raramente uso forte casi sembrano dove si poteva usare la magia metodi lì.

Quando tali casi d'uso fanno, la soluzione è di creare un'istanza mascherata come modulo. In particolare, codificare il mod_call.py come segue:

import sys 
class mod_call(object): 
    def __call__(self): 
    return 42 
sys.modules[__name__] = mod_call() 

Ora il codice di importazione e chiamando mod_call funziona bene.

+1

Wow. Ero a pochi secondi dalla pubblicazione di un esempio di codice basato su questo esempio: http://stackoverflow.com/questions/880530/can-python-modules-have-properties-the-same-way-that-objects-can/880550 # 880550 – Stephan202

+2

Heh, SO sembra a volte una prova di riflessi! -) –

+0

Grazie per la prospettiva, Alex. – gahooa

2

Come dice Miles, è necessario definire la chiamata a livello di classe. Quindi un'alternativa al post di Alex è di cambiare la classe di sys.modules[__name__] in una sottoclasse del tipo di sys.modules[__name__] (dovrebbe essere types.ModuleType).

Ciò ha il vantaggio che il modulo è richiamabile, mantenendo tutte le altre proprietà del modulo (come le funzioni di accesso, variabili, ...).

import sys 

class MyModule(sys.modules[__name__].__class__): 
    def __call__(self): # module callable 
     return 42 

sys.modules[__name__].__class__ = MyModule 

Nota: Testato con python3.6.