문제

편집하다: 이것은 생산 코드에서하는 것이 정말 나쁜 생각입니다. 이것은 나에게 흥미로운 일이었습니다. 집에서는하지 마세요!

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에서 이것을 달성 할 수있는 방법이 있습니까?

도움이 되었습니까?

해결책

괜찮아; 이모 이것은 거칠고 털이 많고 어두운 마법입니다. 당신은 아마도 그것을 사용해서는 안되지만, 특히 생산 코드에 있지 않아야합니다. 그러나 호기심을 위해서만 흥미 롭습니다.

다음에 설명 된 메커니즘을 사용하여 사용자 정의 수입업자를 쓸 수 있습니다. PEP 302, Doug Hellmann 's에서 더 논의했습니다 Pymotw : 모듈 및 수입. 그것은 당신이 생각한 과제를 달성하기위한 도구를 제공합니다.

나는 호기심이 많았 기 때문에 그런 수입업자를 구현했습니다. 기본적으로 클래스 변수를 사용하여 지정한 모듈의 경우 __chatty_for__, 사용자 정의 유형을 a로 삽입합니다 __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

다른 팁

"글로벌 __metaclass__"Python 2의 기능은 모듈 당 작동하도록 설계되었습니다. . 만약 망토와 단검의 이유에 대해 특정 지점에서 가져 오는 모든 모듈의 동작을 "비밀리에"변경하는 것이 매우 중요하다면, 가져 오기 후크 (최악의 경우 매우 더러운 트릭을 재생할 수 있습니다. 먼저 출처를 임시 위치에 복사하면서 변경함으로써 ...) 그러나 노력은 행동의 거대성에 비례 할 것입니다.

아니요. (이것은 기능입니다!)

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top