Come fare una classe che agisce come una stringa?
-
29-09-2019 - |
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?
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 ??strong> 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).