Python 전체 프로그램에 대해 __metaclass__를 수정합니다
-
11-09-2019 - |
문제
편집하다: 이것은 생산 코드에서하는 것이 정말 나쁜 생각입니다. 이것은 나에게 흥미로운 일이었습니다. 집에서는하지 마세요!
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의 기능은 모듈 당 작동하도록 설계되었습니다. . 만약 망토와 단검의 이유에 대해 특정 지점에서 가져 오는 모든 모듈의 동작을 "비밀리에"변경하는 것이 매우 중요하다면, 가져 오기 후크 (최악의 경우 매우 더러운 트릭을 재생할 수 있습니다. 먼저 출처를 임시 위치에 복사하면서 변경함으로써 ...) 그러나 노력은 행동의 거대성에 비례 할 것입니다.
아니요. (이것은 기능입니다!)