Python تعديل __metaclass__ للبرنامج بأكمله
-
11-09-2019 - |
سؤال
تعديل: لاحظ أن هذه فكرة سيئة حقا للقيام بها في رمز الإنتاج. كان هذا مجرد شيء مثير للاهتمام بالنسبة لي. لا تفعل هذا في المنزل!
هل من الممكن تعديل متغير __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
أنا أفهم أن Global __metaclass__ لم تعد تعمل في Python 3.x، ولكن هذا ليس قلقي (رمز إذا كان دليلا على المفهوم). فهل هناك أي طريقة لإنجاز هذا في Python-2.x؟
المحلول
تمام؛ IMO هذا هو الإجمالي، شعر، سحر الظلام. يجب أن لا تستخدمه، ربما من أي وقت مضى، ولكن لا سيما في قانون الإنتاج. إنه نوع من الاهتمام فقط من أجل الفضول، ولكن.
يمكنك كتابة مستورد مخصص باستخدام الآليات الموضحة في بيب 302., ، وناقشت كذلك في دوغ هيلمان Pymotw: وحدات وواردات. وبعد هذا يمنحك الأدوات اللازمة لإنجاز المهمة التي تفصيت بها.
لقد قمت بتنفيذ مثل هذا المستورد، لمجرد أنني كنت فضوليا. أساسا، بالنسبة للوحدات التي تحددها عن طريق متغير الطبقة __chatty_for__
, ، سوف يدخل نوع مخصص ك __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 مصممة للعمل لكل وحدة واحدة، فقط (فقط اعتقد ما هو الفساد الذي سيعززه، وإلا من خلال إجبار metaclass الخاص بك على جميع وحدات المكتبة والطرف الثالث الذي قمت باستيراده من تلك النقطة فصاعدا - ارتجف!) . إذا كان من المهم للغاية أن يغيرك "سرا" سلوك جميع الوحدات النمطية التي تقوم باستيرادها من نقطة معينة فصاعدا، لأي سبب من الأسباب عباءة وخنجر، يمكنك أن تلعب حيل قذرة للغاية مع هوك استيراد (في أسوأ الأحوال من خلال نسخ المصادر أولا إلى موقع مؤقت أثناء تغييرها ...) ولكن الجهد سيكون متناسبا مع ضخامة الفعل، والتي تبدو مناسبة ؛-)
لا. (هذه ميزة!)