【问题标题】:python modify __metaclass__ for whole programpython为整个程序修改__metaclass__
【发布时间】:2010-12-22 04:49:13
【问题描述】:

编辑:请注意,在生产代码中这样做是一个非常糟糕的主意。这对我来说只是一件有趣的事情。不要在家里这样做!

是否可以在 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 中实现呢?

【问题讨论】:

    标签: python metaprogramming metaclass python-2.x


    【解决方案1】:

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

    【讨论】:

    • 好吧,这就是我所期待的。我正在研究 Python + 方面方向,这是完成某些事情的一种有趣方式。
    【解决方案2】:

    好的; IMO 这是粗俗的、毛茸茸的、黑暗的魔法。您可能永远不应该使用它,尤其是在生产代码中。然而,出于好奇,这有点有趣。

    您可以使用PEP 302 中描述的机制编写自定义导入器,并在 Doug Hellmann 的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
    

    【讨论】:

      【解决方案3】:

      不。 (这是一个功能!)

      【讨论】:

        猜你喜欢
        • 2016-12-25
        • 2011-12-04
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-02-10
        • 1970-01-01
        相关资源
        最近更新 更多