質問

編集: これは実稼働コードで行うには非常に悪いアイデアであることに注意してください。これは私にとってただ興味深いことでした。家ではこんなことしないでください!

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 でこれを実現する方法はあるのでしょうか?

役に立ちましたか?

解決

わかった;私の意見では、これはグロくて毛むくじゃらの黒魔術です。おそらく絶対に使用すべきではありませんが、特に運用コードでは使用しないでください。ただし、単なる好奇心のためには、ちょっと興味深いです。

で説明されているメカニズムを使用してカスタム インポーターを作成できます。 PEP302, 、さらに詳しくは Doug Hellmann の記事で説明されています。 PyMOTW:モジュールとインポート. 。これにより、検討したタスクを達成するためのツールが得られます。

興味があったので、このようなインポーターを実装しました。基本的に、クラス変数を使用して指定したモジュールに対して __chatty_for__, 、カスタムタイプを __metaclass__ インポートされたモジュールの変数 __dict__, 前に コードが評価されます。問題のコードが独自に定義している場合 __metaclass__, 、インポーターによって事前に挿入されたものと置き換えられます。このインポーターがモジュールにどのような影響を与えるかを慎重に検討する前に、このインポーターをモジュールに適用することはお勧めできません。

私はインポーターをあまり書いたことがないので、これを書いている間に 1 つ以上の愚かなことをしてしまったかもしれません。私が実装で見逃した欠陥や例外的なケースに気づいた人がいたら、コメントを残してください。

ソースファイル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

他のヒント

「グローバル」 __metaclass__Python 2 のこの機能は、モジュールごとにのみ動作するように設計されています (そうでない場合、その時点以降インポートしたすべてのライブラリおよびサードパーティ モジュールに独自のメタクラスを強制することによって、どのような大惨事が引き起こされるか考えてください -- 戦慄!) 。ある時点以降、インポートしているすべてのモジュールの動作を「密かに」変更することが非常に重要である場合、隠蔽と短剣の理由が何であれ、インポート フックを使用して非常に非常に汚いトリックを実行することができます (最悪の場合、まずソースを変更しながら一時的な場所にコピーする必要があります...) しかし、その労力は行為の巨大さに比例し、それが適切であると思われます;-)

いいえ。(これが特徴です!)

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top