python修改整个程序的__metaclass__

发布于 2024-08-12 22:42:28 字数 1003 浏览 4 评论 0原文

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

是否可以在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__ 甚至在子模块中也能工作。例如 (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

我知道全局 __metaclass__ 不再在 Python 3.X 中工作,但这不是我关心的问题(如果是概念证明,我的代码)。那么在Python-2.x中有什么办法可以实现这一点呢?

EDIT: Note that this is a REALLY BAD idea to do in production code. This was just an interesting thing for me. Don't do this at home!

Is it possible to modify __metaclass__ variable for whole program (interpreter) in Python?

This simple example is working:

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

but I would love to be able change of __metaclass__ to work even in submodules. So for example (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

I understand that global __metaclass__ is no longer working in Python 3.X, but that is not my concern (my code if proof of concept). So is there any way to accomplish this in Python-2.x?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

仅冇旳回忆 2024-08-19 22:42:28

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

The "global __metaclass__" feature of Python 2 is designed to work per-module, only (just think what havoc it would wreak, otherwise, by forcing your own metaclass on all library and third-party modules that you imported from that point onwards -- shudder!). If it's very important to you to "secretly" alter the behavior of all modules you're importing from a certain point onwards, for whatever cloak-and-dagger reason, you could play very very dirty tricks with an import hook (at worst by first copying the sources to a temporary location while altering them...) but the effort would be proportionate to the enormity of the deed, which seems appropriate;-)

耶耶耶 2024-08-19 22:42:28

好的;在我看来,这是一种恶心、毛茸茸的黑魔法。也许永远不应该使用它,但尤其不应该在生产代码中使用它。然而,仅仅出于好奇心,这还是很有趣的。

您可以使用 PEP 302 中描述的机制编写自定义导入程序,并且Doug Hellmann 的 PyMOTW:模块和导入 中进一步讨论。这为您提供了完成您考虑的任务的工具。

我实现了这样一个导入器,只是因为我很好奇。本质上,对于您通过类变量 __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

Okay; IMO this is gross, hairy, dark magic. You shouldn't use it, perhaps ever, but especially not in production code. It is kind of interesting just for curiosity's sake, however.

You can write a custom importer using the mechanisms described in PEP 302, and further discussed in Doug Hellmann's PyMOTW: Modules and Imports. That gives you the tools to accomplish the task you contemplated.

I implemented such an importer, just because I was curious. Essentially, for the modules you specify by means of the class variable __chatty_for__, it will insert a custom type as a __metaclass__ variable in the imported module's __dict__, before the code is evaluated. If the code in question defines its own __metaclass__, that will replace the one pre-inserted by the importer. It would be inadvisable to apply this importer to any modules before carefully considering what it would do to them.

I haven't written many importers, so I may have done one or more silly things while writing this one. If anyone notices flaws / corner cases I missed in the implementation, please leave a comment.

source file 1:

# foo.py
class Foo: pass

source file 2:

# bar.py
class Bar: pass

source file 3:

# baaz.py
class Baaz: pass

and the main event:

# 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
随梦而飞# 2024-08-19 22:42:28

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

Nope. (This is a feature!)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文