Domanda

Ho un gestore di contesto che cattura l'uscita in una stringa da un blocco di codice rientrato sotto una dichiarazione with. Questo manager contesto produce un oggetto risultato personalizzato che, quando il blocco ha terminato l'esecuzione, contenere l'output catturato.

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"

Non posso solo restituire una stringa, ovviamente, perché le stringhe sono immutabili e quindi quello che l'utente torna dalla dichiarazione with non può essere modificato dopo la loro blocco di piste di codice. Tuttavia, è una sorta di trascinamento di dover convertire in modo esplicito l'oggetto risultato in una stringa dopo il fatto con str (Ho anche giocato con fare il callable oggetto come un po 'di zucchero sintattico).

Quindi è possibile fare l'istanza atto risultato come una corda, nel senso che in effetti cambio una stringa quando chiamato? Ho provato attuazione __get__, ma che sembra lavorare solo sugli attributi. O è quello che voglio fare non è davvero possibile?

È stato utile?

Soluzione 3

A prima vista, sembrava UserString (beh, in realtà MutableString , ma che sta andando via in Python 3.0) era fondamentalmente quello che volevo. Purtroppo, UserString non funziona del tutto abbastanza come una corda; Mi è stato sempre un po 'strano di formattazione nelle dichiarazioni print che terminano con le virgole che ha lavorato bene con le stringhe str. (Sembra che si ottiene uno spazio aggiuntivo stampato se non è una stringa "reale", o qualcosa del genere.) Ho avuto lo stesso problema con una classe giocattolo che ho creato per giocare con avvolgimento una corda. Non ho preso il tempo per rintracciare la causa, ma sembra UserString è più utile come esempio.

In realtà ho finito per usare un bytearray perché funziona abbastanza come una stringa per la maggior parte degli scopi, ma è mutevole. Ho anche scritto una versione separata che splitlines() il testo in un elenco. Questa grande opera e in realtà è meglio per il mio caso uso immediato, che è eliminando le righe vuote "extra" nell'output concatenati delle varie funzioni. Ecco che la versione:

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()

Utilizzo:

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']

Altri suggerimenti

Come fare una classe che agisce come una stringa? Sottoclasse 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:\\'

Output:

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

Non credo ci sia un pulita modo per fare quello che vuoi. text è definita globals() dict dei moduli. Si dovrebbe modificare questo globali () dict dall'interno dell'oggetto capturing:

Il codice di seguito si romperebbe se si è tentato di utilizzare la with all'interno di una funzione, dal momento che text allora sarebbe nel campo di applicazione della funzione, non le variabili globali.

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'

Penso che si potrebbe essere in grado di costruire qualcosa di simile.

import StringIO

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

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

Questo è il più facile. Funziona perfettamente senza alcun lavoro supplementare, se non per correggere le funzioni print ad utilizzare l'argomento file=.

Come fare una classe che agisce come una stringa?

Se non si desidera sottoclasse str per qualsiasi motivo:

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.'

Lo so che non volevi fare str (MyClass), ma MyClass.str () tipo di implica, per me, che questa classe si prevede di esporre se stessa come una str alle funzioni che si aspettano una str come parte del oggetto. Invece di qualche risultato inatteso di "chi sa di quello che potrebbe essere restituiti da str (SomeObject).

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