3 votes

Comment créer une classe qui se comporte comme une chaîne de caractères ?

J'ai un gestionnaire de contexte qui capture la sortie vers une chaîne pour un bloc de code indenté sous un with déclaration. Ce gestionnaire de contexte produit un objet résultat personnalisé qui, une fois l'exécution du bloc terminée, contiendra la sortie capturée.

from contextlib import contextmanager

@contextmanager
def capturing():
    "Captures output within a 'with' block."
    from cStringIO import StringIO

    class result(object):
        def __init__(self):
            self._result = None
        def __str__(self):
            return self._result

    try:
        stringio = StringIO()
        out, err, sys.stdout, sys.stderr = sys.stdout, sys.stderr, stringio, stringio
        output = result()
        yield output
    finally:
        output._result, sys.stdout, sys.stderr = stringio.getvalue(), out, err
        stringio.close()

with capturing() as text:
    print "foo bar baz",

print str(text)   # prints "foo bar baz"

Je ne peux pas simplement renvoyer une chaîne, bien sûr, car les chaînes sont immuables et donc celle que l'utilisateur reçoit de la fonction with ne peuvent pas être modifiées après l'exécution de leur bloc de code. Cependant, il est quelque peu ennuyeux de devoir convertir explicitement l'objet résultat en une chaîne de caractères après coup avec str (J'ai également joué avec le fait de rendre l'objet appelable comme un peu de sucre syntaxique).

Est-il possible de faire en sorte que l'instance de résultat se comporte comme une chaîne de caractères, c'est-à-dire qu'elle renvoie effectivement une chaîne de caractères lorsqu'elle est nommée ? J'ai essayé d'implémenter __get__ mais cela ne semble fonctionner que sur les attributs. Ou bien ce que je veux faire n'est-il pas vraiment possible ?

0voto

PyGuy Points 18

C'est une vieille question mais elle est intéressante. En utilisant l'idée de @S.Lott, vous pouvez utiliser les gestionnaires de contexte pour créer un outil plus robuste et réutilisable :

@contextmanager
def redefine_print(stream):
    global print
    from functools import partial, wraps
    old_print = print
    try:
        print = wraps(print)(partial(print, file=stream))
        yield print
    finally:
        print = old_print

exemple d'utilisation avec des objets de type fichier :

with open('file', 'a+') as stream:
    print('a')       # print in the interface
    with redefine_print(stream):
        print('b')   # print in the file 
    print('c')       # print in the interface
    stream.seek(0)
    print(stream.readlines())

exemple d'utilisation avec les objets StringIO

import io
stream = io.StringIO()
with redefine_print(stream) as xprint:
    print('b')   # add to the ioStream
    xprint('x')   # same as print, just to see how the object works

print(stream.getvalue())   # print the intercepted value
print(xprint.__doc__)      # see how @wraps helps to keep print() signature

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X