python modifier __metaclass__ pour le programme entier
-
11-09-2019 - |
Question
EDIT: Notez que ceci est vraiment une mauvaise idée de le faire dans le code de production. C'était juste une chose intéressante pour moi. Ne pas le faire à la maison!
Est-il possible de modifier __metaclass__ variable pour le programme entier (interprète) en Python?
Cet exemple simple fonctionne:
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
mais j'aimerais être en mesure de changer __metaclass__ de travailler même en sous-modules. Ainsi, par exemple (fichier m1.py):
class A:
pass
a=A()
print a
fichier 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
Je comprends que __metaclass__ mondiale ne fonctionne plus en Python 3.X, mais ce ne me regarde pas (mon code si la preuve de concept). Alors, est-il possible d'y arriver en Python-2.x?
La solution
; OMI, est magique brute, poilu, sombre. Vous ne devriez pas l'utiliser, peut-être jamais, mais surtout pas dans le code de production. Il est assez intéressant juste pour l'amour de la curiosité, cependant.
Vous pouvez écrire un importateur personnalisé en utilisant les mécanismes décrits dans PEP 302 , et plus discuté dans le PyMOTW: Modules et importations . Cela vous donne les outils nécessaires pour accomplir la tâche que vous envisagiez.
Je mis en place un tel importateur, juste parce que je suis curieux. Essentiellement, pour les modules que vous spécifiez au moyen de la __chatty_for__
variable de classe, il insérera un type personnalisé en tant que variable __metaclass__
dans le __dict__
du module importé, avant le code est évalué. Si le code en question définit sa propre __metaclass__
, qui remplacera celui inséré avant par l'importateur. Il ne serait pas souhaitable d'appliquer cet importateur à tous les modules avant d'envisager soigneusement ce qu'il ferait pour eux.
Je ne l'ai pas écrit de nombreux importateurs, donc je l'ai fait une ou plusieurs choses stupides en écrivant celui-ci. Si quelqu'un remarque défauts / cas d'angle I manqués dans la mise en œuvre, s'il vous plaît laisser un commentaire.
fichier source 1:
# foo.py
class Foo: pass
fichier source 2:
# bar.py
class Bar: pass
fichier source 3:
# baaz.py
class Baaz: pass
et l'événement principal:
# 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
Autres conseils
La fonction « globale __metaclass__
» de Python 2 est conçu pour fonctionner par module, seul (il suffit de penser que des ravages qu'il infligerait, sinon, en forçant votre propre métaclasse sur toutes les bibliothèques et les modules tiers que vous avez importés de cette point en avant - frissonner!). S'il est très important pour vous de « secrètement » modifier le comportement de tous les modules que vous importez à partir d'un certain point partir, pour une raison quelconque de cape et d'épée, vous pouvez jouer des tours très très sale avec un crochet d'importation (au pire par la première copie des sources vers un emplacement temporaire tout en les modifiant ...) mais l'effort serait proportionnelle à l'énormité de l'acte, qui semble appropriée; -)
Non. (Ceci est une caractéristique!)