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?

Était-ce utile?

La solution

Ok

; 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!)

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top