python modificare __metaclass__ per tutto il programma
-
11-09-2019 - |
Domanda
Modifica Si noti che questa è un'idea davvero male a fare nel codice di produzione. Questo è stato solo una cosa interessante per me. Non fare questo a casa!
E 'possibile modificare __metaclass__ variabile per tutto il programma (interprete) in Python?
Questo semplice esempio sta lavorando:
class ChattyType(type):
def __init__(cls, name, bases, dct):
print "Class init", name
super(ChattyType, cls).__init__(name, bases, dct)
__metaclass__= ChattyType
class Data:
pass
data = Data() # prints "Class init Data"
print data
ma mi piacerebbe essere in grado di cambiare __metaclass__ di lavorare anche in sottomoduli. Così, per esempio (file m1.py):
class A:
pass
a=A()
print a
file main.py:
class ChattyType(type):
def __init__(cls, name, bases, dct):
print "Class init", name
super(ChattyType, cls).__init__(name, bases, dct)
__metaclass__= ChattyType
import m1 # and now print "Class init A"
class Data:
pass
data = Data() # print "Class init Data"
print data
Capisco che __metaclass__ globale non funziona più in Python 3.X, ma non è la mia preoccupazione (il mio codice, se proof of concept). Quindi non v'è alcun modo per ottenere questo in Python 2.x-?
Soluzione
Va bene; IMO questo è, peloso, magia nera lordo. Non si deve usare, forse mai, ma soprattutto non nel codice di produzione. E 'sorta di interessante solo per curiosità, però.
È possibile scrivere un importatore personalizzato utilizzando i meccanismi descritti in PEP 302 , e ulteriormente discussa in PyMOTW: Moduli e importazioni . Che ti dà gli strumenti per realizzare il compito che si contemplata.
ho implementato un tale importatore, solo perché ero curioso. In sostanza, per i moduli specificati mediante l'__chatty_for__
variabile di classe, si inserirà un tipo personalizzato come variabile __metaclass__
nel __dict__
del modulo importato, prima il codice viene valutata. Se il codice in questione definisce un proprio __metaclass__
, che sostituirà quello pre-inserito dall'importatore. Sarebbe sconsigliabile applicare questo importatore di tutti i moduli prima di considerare con attenzione che cosa avrebbe fatto per loro.
Non ho scritto molti importatori, così che io possa aver fatto uno o più stupide cose durante la scrittura di questo. Se qualcuno si accorge difetti casi / angolo ho perso nella realizzazione, si prega di lasciare un commento.
file sorgente 1:
# foo.py
class Foo: pass
file sorgente 2:
# bar.py
class Bar: pass
file sorgente 3:
# baaz.py
class Baaz: pass
e l'evento principale:
# chattyimport.py
import imp
import sys
import types
class ChattyType(type):
def __init__(cls, name, bases, dct):
print "Class init", name
super(ChattyType, cls).__init__(name, bases, dct)
class ChattyImporter(object):
__chatty_for__ = []
def __init__(self, path_entry):
pass
def find_module(self, fullname, path=None):
if fullname not in self.__chatty_for__:
return None
try:
if path is None:
self.find_results = imp.find_module(fullname)
else:
self.find_results = imp.find_module(fullname, path)
except ImportError:
return None
(f,fn,(suf,mode,typ)) = self.find_results
if typ == imp.PY_SOURCE:
return self
return None
def load_module(self, fullname):
#print '%s loading module %s' % (type(self).__name__, fullname)
(f,fn,(suf,mode,typ)) = self.find_results
data = f.read()
if fullname in sys.modules:
module = sys.modules[fullname]
else:
sys.modules[fullname] = module = types.ModuleType(fullname)
module.__metaclass__ = ChattyType
module.__file__ = fn
module.__name__ = fullname
codeobj = compile(data, fn, 'exec')
exec codeobj in module.__dict__
return module
class ChattyImportSomeModules(ChattyImporter):
__chatty_for__ = 'foo bar'.split()
sys.meta_path.append(ChattyImportSomeModules(''))
import foo # prints 'Class init Foo'
import bar # prints 'Class init Bar'
import baaz
Altri suggerimenti
La funzione "globale __metaclass__
" di Python 2 è progettato per funzionare per ogni modulo, solo (basti pensare che cosa il caos che sarebbe causare, in caso contrario, forzando il proprio metaclasse su tutti i moduli della libreria e di terze parti che importati da quel punto in poi - brivido!). Se è molto importante per te "di nascosto" modificare il comportamento di tutti i moduli da importare da un certo punto in poi, per ragioni di tutto ciò di cappa e spada, si potrebbe giocare trucchi molto più sporchi con un gancio di importazione (nel peggiore dei casi da prima di copiare i sorgenti in una posizione temporanea, mentre alterando loro ...) ma lo sforzo sarebbe proporzionato alla enormità del fatto, che sembra opportuno; -)
No. (Questa è una caratteristica!)