سؤال

هل يمكنك اقتراح وسيلة لترميز بديل إسقاط لبيان "مع" الذي سيعمل في بيثون 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

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top