2009-12-01 5 views
2

EDIT: Notez que c'est une idée vraiment VRAIMENT à faire dans le code de production. C'était juste une chose intéressante pour moi. Ne fais pas ça à la maison!python modifier __metaclass__ pour le programme entier

Est-il possible de modifier la variable __metaclass__ pour l'ensemble du programme (interpréteur) 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 n'est pas ma préoccupation (mon code si preuve de concept). Donc, y a-t-il un moyen d'accomplir cela dans Python-2.x?

Répondre

4

OK; IMO c'est grossier, velu, magie noire. Vous ne devriez pas l'utiliser, peut-être jamais, mais surtout pas dans le code de production. Cependant, c'est plutôt intéressant pour la curiosité.

Vous pouvez écrire un importateur personnalisé à l'aide des mécanismes décrits dans PEP 302 et décrits plus en détail dans le document PyMOTW: Modules and Imports de Doug Hellmann. Cela vous donne les outils pour accomplir la tâche que vous avez envisagée.

J'ai mis en place un tel importateur, juste parce que j'étais curieux. Essentiellement, pour les modules que vous spécifiez au moyen de la variable de classe __chatty_for__, il insère un type personnalisé en tant que variable __metaclass__ dans le module importé __dict__, avant le code est évalué. Si le code en question définit son propre __metaclass__, celui-ci remplacera celui pré-inséré par l'importateur. Il serait déconseillé d'appliquer cet importateur à n'importe quel module avant d'examiner soigneusement ce qu'il ferait pour eux.

Je n'ai pas écrit beaucoup d'importateurs, donc j'ai peut-être fait une ou plusieurs bêtises en écrivant celle-ci. Si quelqu'un remarque des failles/cas de coin que j'ai manqué dans la mise en œuvre, s'il vous plaît laissez 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 
+0

En effet magie noire, mais néanmoins intéressant :-) – Stan

1

Non. (La caractéristique est!)

7

La fonctionnalité "global __metaclass__" de Python 2 est conçue pour fonctionner par module uniquement (pensez à ce qu'elle ferait, sinon, en forçant votre propre métaclasse sur toute la bibliothèque et la troisième -party modules que vous avez importés à partir de ce point - frémissez!). S'il est très important pour vous de modifier "secrètement" le comportement de tous les modules que vous importez à partir d'un certain point, pour quelque raison que ce soit, vous pourriez jouer des tours très sales avec un crochet d'import (au pire d'abord copier les sources vers un emplacement temporaire en les modifiant ...) mais l'effort serait proportionné à l'énormité de l'acte, ce qui semble approprié ;-)

+0

Ok, c'est ce à quoi je m'attendais. Je cherchais dans Python + orientation de l'aspect et cela est apparu comme une façon intéressante d'accomplir certaines choses. – Stan

Questions connexes