3 Stimmen

Wie kann man eine Klasse erstellen, die sich wie eine Zeichenkette verhält?

Ich habe einen Kontext-Manager, die Ausgabe in eine Zeichenfolge für einen Block von Code unter einem eingerückt erfasst with Erklärung. Dieser Kontextmanager liefert ein benutzerdefiniertes Ergebnisobjekt, das nach Beendigung der Ausführung des Blocks die erfasste Ausgabe enthält.

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"

Ich kann natürlich nicht einfach eine Zeichenkette zurückgeben, weil Zeichenketten unveränderlich sind und somit die, die der Benutzer von der with Anweisung kann nicht mehr geändert werden, nachdem ihr Codeblock ausgeführt wurde. Allerdings ist es etwas umständlich, das Ergebnisobjekt im Nachhinein explizit in eine Zeichenkette umzuwandeln mit str (Ich habe auch damit gespielt, das Objekt aufrufbar zu machen, um die Syntax zu verbessern).

Ist es also möglich, das Ergebnis Instanz handeln wie eine Zeichenfolge, in der es in der Tat eine Zeichenfolge zurück, wenn benannt? Ich habe versucht zu implementieren __get__ aber das scheint nur bei Attributen zu funktionieren. Oder ist das, was ich tun möchte, nicht wirklich möglich?

0 Stimmen

Unabhängig davon, ob es eine Antwort auf diese Frage gibt oder nicht, würde ich es vorziehen, eine Klasse zurückzugeben, die __str__ . Ich bin mir nicht sicher, inwiefern es ein Hindernis ist, dass man an einem bestimmten Punkt explizit sagen muss: "Genau hier ist der Punkt, an dem ich dies als String festhalte, keine weiteren Änderungen", indem man str() . Was ist also der Gewinn?

0 Stimmen

@Mike: Hauptsächlich, dass ein Benutzer die Zeichenkette will, nicht ein Objekt, das in eine Zeichenkette umgewandelt werden muss.

0 Stimmen

Es sieht so aus, als ob UserString (naja, MutableString, aber das geht weg) im Grunde das ist, was ich will, also werde ich dafür stimmen, dies zu schließen. Bearbeiten: Hoppla, ich schätze, ich kann das nicht wirklich tun, da keiner der Gründe wirklich zutreffen.

5voto

DevPlayer Punkte 5017

Wie kann man eine Klasse erstellen, die sich wie eine Zeichenkette verhält? Unterklasse 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:\\'

Salida:

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

2voto

unutbu Punkte 769083

Ich glaube nicht, dass es eine sauber Weg zu tun, was Sie wollen. text ist in den Modulen definiert globals() dict. Sie müssten dieses globals()-Diktat in der Datei capturing Objekt:

Der folgende Code würde bei der Verwendung der Option with innerhalb einer Funktion, da dann text im Geltungsbereich der Funktion liegen, nicht in den 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'

2voto

kindall Punkte 167554

Auf den ersten Blick sah es so aus UserString (nun, eigentlich MutableString (aber das wird in Python 3.0 abgeschafft) war im Grunde das, was ich wollte. Leider funktioniert UserString nicht ganz genug wie eine Zeichenkette; ich erhielt einige seltsame Formatierungen in print Anweisungen, die in Kommas enden, die problemlos mit str Streicher. (Anscheinend wird ein zusätzliches Leerzeichen gedruckt, wenn es sich nicht um eine "echte" Zeichenkette handelt). Ich hatte das gleiche Problem mit einer Spielzeugklasse, die ich erstellt habe, um mit dem Einwickeln einer Zeichenfolge zu spielen. Ich habe mir nicht die Zeit genommen, die Ursache herauszufinden, aber es scheint UserString ist als Beispiel sehr nützlich.

Am Ende habe ich eine bytearray weil sie für die meisten Zwecke wie eine Zeichenkette funktioniert, aber veränderbar ist. Ich habe auch eine separate Version geschrieben, die splitlines() den Text in eine Liste. Das funktioniert hervorragend und ist für meinen unmittelbaren Anwendungsfall, nämlich das Entfernen von "zusätzlichen" Leerzeilen in der verketteten Ausgabe verschiedener Funktionen, sogar besser geeignet. Hier ist diese 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()

Uso:

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

1voto

S.Lott Punkte 371691

Ich denke, dass man so etwas vielleicht bauen kann.

import StringIO

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

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

Das ist das Einfachste. Es funktioniert perfekt und ohne zusätzliche Arbeit, außer dass Sie Ihre print Funktionen zur Verwendung der file= Argument.

1voto

DevPlayer Punkte 5017

Wie kann man eine Klasse erstellen, die sich wie eine Zeichenkette verhält?

Wenn Sie keine Unterklassen bilden wollen str aus welchem Grund auch immer:

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

Ich weiß, dass Sie str(MyClass) nicht verwenden wollten, aber MyClass.str() impliziert für mich, dass von dieser Klasse erwartet wird, dass sie sich selbst als str an Funktionen weitergibt, die einen str als Teil des Objekts erwarten. Anstelle eines unerwarteten Ergebnisses von "wer weiß, was von str( SomeObject ) zurückgegeben würde.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X