2009-12-01 22 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