كيف تصنع فصلًا يتصرف مثل السلسلة؟
-
29-09-2019 - |
سؤال
لدي مدير سياق يلتقط الإخراج إلى سلسلة لمجموعة من الكود بادئة تحت أ 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__
, ، ولكن يبدو أن هذا يعمل فقط على السمات. أم ما أريد أن لا يكون ذلك ممكنًا حقًا؟
المحلول 3
للوهلة الأولى ، بدا الأمر UserString
(حسنا ، في الواقع MutableString
, ، لكن هذا يذهب بعيدًا في بيثون 3.0) كان ما أردت بشكل أساسي. لسوء الحظ ، لا يعمل المستخدمون كافي مثل سلسلة كنت أحصل على بعض التنسيق الغريب في print
البيانات التي تنتهي في الفواصل التي عملت بشكل جيد مع str
سلاسل. (يبدو أنك تحصل على مساحة إضافية مطبوعة إذا لم تكن سلسلة "حقيقية" ، أو شيء ما.) واجهت نفس المشكلة مع فئة لعبة قمت بإنشائها للعب مع تغليف سلسلة. لم آخذ الوقت الكافي لتعقب السبب ، لكن يبدو UserString
هو الأكثر فائدة كمثال.
لقد انتهى بي الأمر في الواقع باستخدام أ bytearray
لأنه يعمل بما فيه الكفاية مثل سلسلة لمعظم الأغراض ، ولكن قابلة للتغيير. لقد كتبت أيضًا نسخة منفصلة 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']
نصائح أخرى
كيف تصنع فصلًا يتصرف مثل السلسلة؟الفئة الفرعية شارع
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
لا أعتقد أن هناك ينظف طريقة لفعل ما تريد.text
تم تعريفه في الوحدات النمطية globals()
قاموس. سيكون عليك تعديل هذه القولات Globals () من داخل capturing
هدف:
سوف يكسر الرمز أدناه إذا حاولت استخدام with
من داخل وظيفة ، منذ ذلك الحين 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'
أعتقد أنك قد تكون قادرًا على بناء شيء مثل هذا.
import StringIO
capturing = StringIO.StringIO()
print( "foo bar baz", file= capturing )
الآن 'foo bar baz n' == capturing.getvalue()
هذا هو الأسهل. إنه يعمل بشكل مثالي دون أي عمل إضافي ، باستثناء إصلاح الخاص بك print
وظائف لاستخدام file=
جدال.
كيف تصنع فصلًا يتصرف مثل السلسلة؟
إذا كنت لا تريد الفئة الفرعية شارع لاي سبب كان:
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).