3

withステートメントの下にインデントされたコードのブロックの文字列への出力をキャプチャするコンテキストマネージャーがあります。このコンテキストマネージャーは、ブロックの実行が終了すると、キャプチャされた出力を含むカスタム結果オブジェクトを生成します。

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"

もちろん、文字列を返すことはできません。文字列は不変であるため、ユーザーがwithステートメントから取得した文字列は、コードブロックの実行後に変更することはできません。ただし、事後に結果オブジェクトを文字列に明示的に変換する必要があるのは、ちょっとしたドラッグですstr(オブジェクトを少しの構文糖衣として呼び出し可能にすることも試しました)。

それで、名前が付けられたときに実際に文字列を返すという点で、結果インスタンスを文字列のように動作させることは可能ですか?を実装しようとし__get__ましたが、それは属性でのみ機能するようです。それとも、私がしたいことは本当に不可能ですか?

4

6 に答える 6

5

文字列のように振る舞うクラスを作るには? サブクラス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:\\'

出力:

1 D:\Projects\Python\
2 C:\
3 D:\Projects\Python\
4 True
于 2013-01-02T05:54:50.500 に答える
2

あなたが望むことをするためのきれいな方法があるとは思いません。 textモジュールのglobals()辞書で定義されています。この globals() dict をcapturingオブジェクト内から変更する必要があります。

with関数内で fromを使用しようとするtextと、グローバルではなく関数のスコープ内になるため、以下のコードは壊れます。

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'
于 2010-10-12T23:13:39.757 に答える
2

一見したところ、UserString(まあ、実際MutableStringには . 残念ながら、UserString は文字列のように十分に機能しません。print文字列で正常に機能するコンマで終わるステートメントで、奇妙なフォーマットが得られましstrた。(「本物の」文字列などではない場合、余分なスペースが出力されるようです。)文字列をラップして遊ぶために作成したおもちゃのクラスでも同じ問題がありました。原因を追跡するのに時間はかかりませんでしたがUserString、例として最も役立つようです。

bytearrayほとんどの目的で文字列のように十分に機能しますが、変更可能であるため、実際に a を使用することになりました。splitlines()テキストをリストにした別バージョンも書きました。これはうまく機能し、実際には、さまざまな関数の連結された出力で「余分な」空白行を削除するという私の当面のユースケースにより適しています。そのバージョンは次のとおりです。

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

使用法:

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']
于 2010-10-13T04:23:33.487 に答える
1

文字列のように振る舞うクラスを作るには?

何らかの理由でstrをサブクラス化したくない場合:

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

str(MyClass) を実行したくないことは知っていますが、MyClass.str() は、このクラスが str をオブジェクトの一部として期待する関数に自身を str として公開することが期待されていることを暗示しています。「str( SomeObject ) によって何が返されるかは誰にもわからない」という予期しない結果の代わりに。

于 2013-01-02T06:18:08.173 に答える
1

このようなものを構築できる可能性があると思います。

import StringIO

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

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

それが一番簡単です。print引数を使用するように関数を修正することを除いて、追加の作業なしで完全に機能しfile=ます。

于 2010-10-13T20:56:14.783 に答える
0

これは古い質問ですが、興味深い質問です。@S.Lott のアイデアを使用すると、contextmanagers を使用して、より堅牢で再利用可能なツールを作成できます。

@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

ファイルのようなオブジェクトでの使用例:

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

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
于 2021-12-17T05:05:05.053 に答える