2009-12-01 21 views
2

编辑:请注意,这是一个在生产代码中做的真正不好的想法。这对我来说只是一件有趣的事情。不要在家里这样做!python修改整个程序的__metaclass__

是否有可能在Python中修改整个程序(解释器)的__metaclass__变量?

这个简单的例子是工作:

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

好吧;国际海事组织这是粗糙,毛茸茸,黑暗的魔法你不应该使用它,也许永远不会使用它,但特别是在生产代码中。然而,出于好奇的缘故,这样做很有趣。

您可以编写使用PEP 302描述的机制定制的进口商,并在道格乐门PyMOTW: Modules and Imports进一步讨论。这为您提供了完成您预期任务的工具。

我实施了这样的进口商,只是因为我很好奇。本质上,对于通过类变量__chatty_for__指定的模块,它将在导入模块的__dict__,中插入一个自定义类型__metaclass__变量,然后代码被评估。如果有问题的代码定义了它自己的__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 2009-12-02 11:31:05

1

没有。 (这是一个功能!)

7

Python 2的“全球__metaclass__”功能旨在为每个模块工作,只是(想想它会造成什么破坏,否则,通过在所有库上强制您自己的元类和第三个从这一点开始导入的第四方模块 - 不寒而栗!)。如果你“秘密地”改变你从某个点开始导入的所有模块的行为非常重要,无论出于何种原因,你都可以使用导入钩子来玩非常非常肮脏的技巧(最坏的情况是首先将来源复制到一个临时位置,同时改变它们......)但是这种努力将与契约的严重性相称,这似乎是合适的;-)

+0

好吧,那就是我所期望的。我正在研究Python +方面的方向,这是一个有趣的方式来完成一些事情。 – Stan 2009-12-02 01:27:24