Pregunta

¿Puede sugerir una forma de codificar un reemplazo directo para la declaración "con" que funcione en Python 2.4?

Sería un truco, pero me permitiría migrar mejor mi proyecto a Python 2.4.

EDITAR:Se eliminó el boceto de metaclase irrelevante.

¿Fue útil?

Solución

Sólo tiene que utilizar try-finally.

En realidad, esto puede ser agradable como un ejercicio mental, pero si realmente lo hace en código que se preocupan por el resultado final será con feo, difícil de mantener código.

Otros consejos

Creo que podrías (ab)usar decoradores para hacer esto.Los siguientes trabajos, por ejemplo:

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,

(Bueno, en Python 2.4 los objetos de archivo no tienen métodos __enter__ y __exit__, pero por lo demás funciona)

La idea es reemplazar la línea with en:

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

con la función decorada "declaración" en:

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

pero obteniendo el mismo comportamiento (el hacer_algo_...código ejecutado).Tenga en cuenta que el decorador cambia la declaración de función a una invocación inmediata lo cual es más que un poco de maldad.

Ya que se necesita para salir del gestor de contexto, tanto durante los errores y los errores no, no creo que sea posible hacer un caso de uso genérico con metaclases, o de hecho en absoluto. Usted va a necesitar try / finally bloques para eso.

Pero tal vez es posible hacer otra cosa en su caso. Eso depende de lo que se utiliza el gestor de contexto para.

El uso de __del__ puede ayudar en algunos casos, como recurso cancelar la asignación, pero ya no se puede estar seguro de que se llama, sólo se puede utilizar de lo que necesita para liberar recursos que se liberan cuando el programa termina. Eso también no funcionará si está manejando excepciones en el método __exit__.

Creo que el método más limpio es envolver toda la gestión de contexto en una especie de llamada de gestión de contexto, y extraer el bloque de código en un método. Algo como esto (código no probado, pero robado en su mayoría de 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)

¿Qué tal esto?

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

Uso:

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

¿Qué es paralela a la palabra clave with sin as.

O:

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

¿Qué es paralela a la palabra clave with con el as.

Si estás bien con el uso de DEF sólo para obtener un bloque, y decoradores que se ejecutan inmediatamente, podría utilizar la firma de la función de conseguir algo más natural para el caso mencionado.

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

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top