2009-12-01 6 views
2

EDIT: Обратите внимание, что это ДЕЙСТВИТЕЛЬНО ПЛОХАЯ идея сделать в производственном коде. Это было просто интересно для меня. Не делайте это дома!python изменить __metaclass__ для всей программы

Можно ли изменить переменную __metaclass__ для всей программы (интерпретатора) в Python?

Этот простой пример работает:

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 

, но я хотел бы иметь возможность изменения __metaclass__ работать даже в подмодулей. Так, например (файл m1.py):

class A: 
     pass 

a=A() 
print a 

файл 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 

Я понимаю, что глобальное __metaclass__ больше не работает в Python 3.X, но это не моя забота (мой код, если доказательство концепции). Итак, есть ли способ сделать это в Python-2.x?

ответ

4

Okay; ИМО это грубая, волосатая, темная магия. Вы не должны использовать его, возможно, когда-либо, но особенно не в производственном коде. Однако это интересно только ради любопытства.

Вы можете написать собственный импортер, используя механизмы, описанные в PEP 302, и дополнительно обсуждены в документе PyMOTW: Modules and Imports Дуга Хеллмана. Это дает вам инструменты для выполнения поставленной вами задачи.

Я реализовал такой импортер, просто потому, что мне было любопытно. По существу, для модулей, которые вы задаете с помощью переменной класса __chatty_for__, он вставляет пользовательский тип в качестве переменной __metaclass__ в __dict__, импортированного модуля, который оценивается до. Если данный код определяет его собственный __metaclass__, это заменит тот, который был предварительно вставлен импортером. Было бы нецелесообразно применять этого импортера к любым модулям, прежде чем тщательно рассмотреть, что он сделает с ними.

Я не написал много импортеров, поэтому, возможно, я сделал одно или несколько глупостей при написании этого. Если кто-либо замечает недостатки/угловые случаи, которые я пропустил в реализации, оставьте комментарий.

исходный файл 1:

# foo.py 
class Foo: pass 

исходный файл 2:

# bar.py 
class Bar: pass 

исходный файл 3:

# baaz.py 
class Baaz: pass 

и главное событие:

# 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

Действительно черная магия, но интересная тем не менее :-) – Stan

7

Функция «global __metaclass__» Python 2 предназначена для работы только на одном модуле (просто подумайте, какой хаос это приведет, иначе, заставив ваш собственный метакласс на всю библиотеку и третий -партийные модули, которые вы импортировали с этого момента, - содрогайтесь!). Если для вас очень важно «тайно» изменить поведение всех модулей, которые вы импортируете с определенной точки вперед, по любой причине, связанной с плащом и кинжалом, вы можете сыграть очень грязные трюки с крюком импорта (в худшем случае сначала копирование источников во временное место при их изменении ...), но усилия будут соразмерны грандиозности документа, который кажется уместным ;-)

+0

Хорошо, вот что я ожидал. Я смотрел на Python + аспект ориентации, и это стало интересным способом для достижения некоторых вещей. – Stan

 Смежные вопросы

  • Нет связанных вопросов^_^