Domanda

Potete suggerire un modo per codificare un sostituto drop-in per l'istruzione "with" che funzionerà in Python 2.4?

Sarebbe un trucco, ma mi permetterebbe di portare il mio progetto su Python 2.4 in modo più efficace.

MODIFICARE:Rimosso lo schizzo della metaclasse irrilevante

È stato utile?

Soluzione

Basta usare try-fine.

In realtà, questo può essere bello come un esercizio mentale, ma se effettivamente farlo in codice che si cura di te finirà con brutto, difficile da mantenere il codice.

Altri suggerimenti

Penso che potresti (ab)usare i decoratori per farlo.I seguenti lavori, ad esempio:

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,

(Bene, in Python 2.4 gli oggetti file non hanno i metodi __enter__ e __exit__, ma per il resto funziona)

L'idea è che stai sostituendo la riga con in:

with bar() as foo:
    do_something_with(foo)
    do_something_else_with(foo)
    # etc...

con la funzione decorata "dichiarazione" in:

@execute_with_context_manager( bar() )
def dummyname( foo ):
    do_something_with(foo)
    do_something_else_with(foo)
    # etc...

ma ottenendo lo stesso comportamento (il do_something_...codice eseguito).Nota che il decoratore cambia la dichiarazione della funzione in an invocazione immediata il che è più che un po' malvagio.

Dal momento che è necessario uscire dal gestore contesto sia durante gli errori e non gli errori, non penso che sia possibile fare un caso d'uso generico con metaclassi, o di fatto a tutti. Si sta andando ad avere bisogno di try / finally blocchi per questo.

Ma forse è possibile fare qualcosa di diverso nel tuo caso. Dipende da quello che si utilizza il manager contesto per.

Utilizzando __del__ può aiutare in alcuni casi, come risorsa deallocando, ma dal momento che non si può essere sicuri che viene chiamato, può essere utilizzato solo è necessario liberare le risorse che saranno rilasciate quando il programma termina. Anche questo non funziona se si sta gestendo le eccezioni nel metodo __exit__.

Credo che il metodo più pulito è quello di avvolgere l'intera gestione del contesto in una sorta di contesto chiamata gestione, ed estrarre il blocco di codice in un metodo. Qualcosa di simile (codice non testato, ma soprattutto rubato 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)

Che ne dici di questo?

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

Utilizzo:

@improvize_context_manager(lock)
def null():
    do(stuff)

che affianca la parola with senza as.

o

@improvize_context_manager(my_lock=lock)
def null(my_lock):
    do(stuff_with, my_lock)

che affianca la parola chiave with con il as.

Se sei OK con l'utilizzo DEF solo per ottenere un blocco, e decoratori che eseguire immediatamente, è possibile utilizzare la firma funzione per ottenere qualcosa di più naturale per il caso di nome.

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

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top