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-?

È stato utile?

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

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top