تحسين بديل لبيان "مع" لبيثون 2.4
-
20-09-2019 - |
سؤال
هل يمكنك اقتراح وسيلة لترميز بديل إسقاط لبيان "مع" الذي سيعمل في بيثون 2.4؟
سيكون ذلك بمثابة اختراق ، لكنه سيسمح لي بمنفذ مشروعي إلى Python 2.4 بشكل جيد.
تعديل:إزالة رسم metaclass غير ذي صلة
المحلول
فقط استخدم المحاولة.
حقًا ، قد يكون هذا لطيفًا كتمرين عقلي ، ولكن إذا قمت بذلك بالفعل في الكود ، فسوف ينتهي بك الأمر بقبيح ، يصعب الحفاظ على التعليمات البرمجية.
نصائح أخرى
يمكنك (AB) استخدام الديكور للقيام بذلك ، على ما أعتقد. الأعمال التالية ، على سبيل المثال:
def execute_with_context_manager(man):
def decorator(f):
target = man.__enter__()
exc = True
try:
try:
f(target)
except:
exc = False
if not man.__exit__(*sys.exc_info()):
raise
finally:
if exc:
man.__exit__(None, None, None)
return None
return decorator
@execute_with_context_manager(open("/etc/motd"))
def inside(motd_file):
for line in motd_file:
print line,
(حسنًا ، في بيثون 2.4 كائنات الملفات لا تحتوي على طرق __enter__ و __exit__ ، ولكنها تعمل على خلاف ذلك)
الفكرة هي أنك تستبدل الخط في:
with bar() as foo:
do_something_with(foo)
do_something_else_with(foo)
# etc...
مع الوظيفة المزينة "إعلان" في:
@execute_with_context_manager( bar() )
def dummyname( foo ):
do_something_with(foo)
do_something_else_with(foo)
# etc...
لكن الحصول على نفس السلوك (رمز do_something _... تم تنفيذه). لاحظ أن الديكور يغير إعلان الوظيفة إلى الاحتجاج الفوري وهو أكثر من القليل من الشر.
نظرًا لأنك تحتاج إلى الخروج من مدير السياق أثناء الأخطاء وليس الأخطاء ، لا أعتقد أنه من الممكن القيام بحضارة مستخدم عام مع metaclasses ، أو في الواقع على الإطلاق. ستحتاج إلى محاولة/أخيرًا كتل لذلك.
ولكن ربما يكون من الممكن فعل شيء آخر في قضيتك. هذا يعتمد على ما تستخدمه مدير السياق ل.
استخدام __del__
يمكن أن تساعد في بعض الحالات ، مثل مورد التخصيص ، ولكن نظرًا لأنه لا يمكنك التأكد من استدعاؤه ، فلا يمكن استخدامك إلا إلى إصدار الموارد التي سيتم إصدارها عندما يخرج البرنامج. هذا لن يعمل أيضًا إذا كنت تتعامل مع الاستثناءات في __exit__
طريقة.
أعتقد أن أنظف طريقة هي لف إدارة السياق بأكملها في نوع من السياق إدارة المكالمة ، واستخراج كتلة الكود في طريقة. شيء من هذا القبيل (رمز غير مختبر ، ولكن في الغالب سرقت من PEP 343):
def call_as_context_manager(mgr, function):
exit = mgr.__exit__
value = mgr.__enter__()
exc = True
try:
try:
function(value)
except:
exc = False
if not exit(*sys.exc_info()):
raise
finally:
if exc:
exit(None, None, None)
وماذا عن هذا؟
def improvize_context_manager(*args, **kwargs):
assert (len(args) + len(kwargs)) == 1
if args:
context_manager = args[0]
as_ = None
else: # It's in kwargs
(as_, context_manager) = kwargs.items()[0]
def decorator(f):
exit_ = context_manager.__exit__ # Not calling it yet
enter_ = context_manager.__enter__()
exc = True
try:
try:
if as_:
f(*{as_: enter_})
else:
f()
except:
exc = False
if not exit_(*sys.exc_info()):
raise
finally:
if exc:
exit_(None, None, None)
return None
return decorator
الاستخدام:
@improvize_context_manager(lock)
def null():
do(stuff)
الذي يوازي with
الكلمة الرئيسية بدون as
.
أو:
@improvize_context_manager(my_lock=lock)
def null(my_lock):
do(stuff_with, my_lock)
الذي يوازي with
الكلمة الرئيسية مع as
.
إذا كنت على ما يرام مع استخدام DEF فقط للحصول على كتلة ، والديكورات التي تنفذ على الفور ، يمكنك استخدام توقيع الوظيفة للحصول على شيء أكثر طبيعية للحالة المسماة.
import sys def with(func): def decorated(body = func): contexts = body.func_defaults try: exc = None, None, None try: for context in contexts: context.__enter__() body() except: exc = sys.exc_info() raise finally: for context in reversed(contexts): context.__exit__(*exc) decorated() class Context(object): def __enter__(self): print "Enter %s" % self def __exit__(self, *args): print "Exit %s(%s)" % (self, args) x = Context() @with def _(it = x): print "Body %s" % it @with def _(it = x): print "Body before %s" % it raise "Nothing" print "Body after %s" % it