2

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

是否可以在 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

3 回答 3

7

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

于 2009-12-02T01:10:02.687 回答
4

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

您可以使用PEP 302中描述的机制编写自定义导入器,并在 Doug Hellmann 的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
于 2009-12-02T01:57:00.160 回答
1

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

于 2009-12-01T22:51:42.787 回答