Question

J'ai un gestionnaire de contexte qui capture la sortie d'une chaîne pour un bloc de code en retrait sous une instruction with. Ce gestionnaire de contexte donne un objet résultat personnalisé qui, lorsque le bloc a terminé son exécution, contenir la sortie capturé.

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 retourner une chaîne, bien sûr, parce que les chaînes sont immuables et donc celui que l'utilisateur revient de l'instruction with ne peut être modifiée après leur bloc de code se exécute. Cependant, il est quelque chose d'un glisser d'avoir à convertir explicitement l'objet de résultat à une chaîne après le fait avec str (je joue aussi avec le fait callable objet comme un peu de sucre syntaxique).

Ainsi est-il possible de faire l'instance fait agir comme une chaîne, en ce qu'elle fait en retour de fait une chaîne quand le nom? J'ai essayé de mettre en œuvre __get__, mais qui semble ne fonctionne que sur les attributs. Ou est-ce que je veux faire pas vraiment possible?

Était-ce utile?

La solution 3

À première vue, il semblait que UserString (bien, en fait MutableString , mais qui va loin dans Python 3.0) est essentiellement ce que je voulais. Malheureusement, UserString ne fonctionne pas tout à fait assez comme une chaîne; Je recevais une mise en forme bizarre dans les états de print se terminant par des virgules qui a bien fonctionné avec des chaînes de str. (Il semble que vous obtenez un espace supplémentaire imprimé si ce n'est pas une chaîne « réelle », ou quelque chose.) J'ai eu le même problème avec une classe de jouet j'ai créé pour jouer avec enroulant une chaîne. Je ne l'ai pas pris le temps de traquer la cause, mais il semble UserString est le plus utile à titre d'exemple.

En fait, je fini par utiliser un bytearray parce que cela fonctionne assez comme une chaîne pour la plupart des cas, mais il est mutable. J'ai également écrit une version distincte qui splitlines() le texte dans une liste. Cela fonctionne très bien et est en fait mieux pour mon cas d'utilisation immédiate, qui enlève « extra » lignes vides dans la sortie concaténée de diverses fonctions. Voici cette version:

import sys
from contextlib import contextmanager

@contextmanager
def capturinglines(output=None):
    "Captures lines of output to a list."
    from cStringIO import StringIO

    try:
        output = [] if output is None else output
        stringio = StringIO()
        out, err = sys.stdout, sys.stderr
        sys.stdout, sys.stderr = stringio, stringio
        yield output
    finally:
        sys.stdout, sys.stderr = out, err
        output.extend(stringio.getvalue().splitlines())
        stringio.close()

Utilisation:

with capturinglines() as output:
    print "foo"
    print "bar"

print output
['foo', 'bar']

with capturinglines(output):   # append to existing list
    print "baz"

print output
['foo', 'bar', 'baz']

Autres conseils

Comment faire une classe qui agit comme une chaîne? Sous-classe str

import os
class LikeAStr(str):
    '''Making a class like a str object; or more precisely
    making a str subclass with added contextmanager functionality.'''

    def __init__(self, diff_directory):
        self._iwd = os.getcwd()
        self._cwd = diff_directory

    def __enter__(self):
        return self

    def __exit__(self, ext_typ, exc_value, traceback):
        try: os.chdir(self._iwd) # might get deleted within the "with" statement
        except: pass

    def __str__(self):
        return self._cwd

    def __repr__(self):
        return repr(self._cwd)


astr = LikeAStr('C:\\')

with LikeAStr('C:\\') as astr:
    print 1, os.getcwd()
    os.chdir( astr ) # expects str() or unicode() not some other class
    print 2, os.getcwd()
    #

# out of with block
print 3, os.getcwd()
print 4, astr == 'C:\\'

Sortie:

1 D:\Projects\Python\
2 C:\
3 D:\Projects\Python\
4 True

Je ne crois pas qu'il y ait un moyen de faire propre ce que vous voulez. text est définie dans les modules de l'dict globals(). Vous devez modifier cette globals () dict à partir de l'objet capturing:

Le code ci-dessous romprait si vous avez essayé d'utiliser le with à partir d'une fonction, car text serait alors dans le champ de la fonction, et non globals.

import sys
import cStringIO

class capturing(object):
    def __init__(self,varname):
        self.varname=varname
    def __enter__(self):
        self.stringio=cStringIO.StringIO()
        self.out, sys.stdout = sys.stdout, self.stringio
        self.err, sys.stderr = sys.stderr, self.stringio        
        return self
    def __exit__(self,ext_type,exc_value,traceback):
        sys.stdout = self.out
        sys.stderr = self.err
        self._result = self.stringio.getvalue()
        globals()[self.varname]=self._result
    def __str__(self):
        return self._result


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

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

print(repr(text))
# 'foo bar baz\n'

Je pense que vous pourriez être en mesure de quelque chose de construction comme celui-ci.

import StringIO

capturing = StringIO.StringIO()
print( "foo bar baz", file= capturing )

'foo bar baz \ n' == capturing.getvalue()

C'est le plus facile. Il fonctionne parfaitement sans travail supplémentaire, sauf pour corriger vos fonctions print d'utiliser l'argument file=.

Comment faire une classe qui agit comme une chaîne?

Si vous ne voulez pas la sous-classe str pour quelque raison que ce:

class StrBuiltin(object):
    def __init__(self, astr=''):
        self._str = astr

    def __enter__(self):
        return self

    def __exit__(self, ext_typ, exc_value, traceback):
        pass # do stuff

    def __str__(self):
        return self._str

    def __repr__(self):
        return repr(self._str)

    def __eq__(self, lvalue):
        return lvalue == self._str

    def str(self):
        '''pretend to "convert to a str"'''
        return self._str

astr = StrBuiltin('Eggs&spam')

if isinstance( astr.str(), str):
    print 'Is like a str.'
else:
    print 'Is not like a str.'

Je sais que vous ne vouliez pas faire str (MyClass) mais MyClass.str () genre de Implique, pour moi, que cette classe devrait s'exposer comme str à des fonctions qui attendent une str dans le cadre du objet. Au lieu d'un résultat inattendu de « qui savent de quoi seraient retournés par str (SomeObject).

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top