我想通过以下方式实现以下目标str.format
:
x,y = 1234,5678
print str(x)[2:] + str(y)[:2]
我能做到的唯一方法是:
print '{0}{1}'.format(str(x)[2:],str(y)[:2])
现在,这是一个例子,我真正拥有的是一个又长又乱的字符串,所以我想把切片放在{}
. 我研究了文档,但我无法弄清楚正确的语法。我的问题是:是否可以在替换字段中分割字符串?
我想通过以下方式实现以下目标str.format
:
x,y = 1234,5678
print str(x)[2:] + str(y)[:2]
我能做到的唯一方法是:
print '{0}{1}'.format(str(x)[2:],str(y)[:2])
现在,这是一个例子,我真正拥有的是一个又长又乱的字符串,所以我想把切片放在{}
. 我研究了文档,但我无法弄清楚正确的语法。我的问题是:是否可以在替换字段中分割字符串?
不,您不能对替换字段内的字符串应用切片。
您需要参考Format Specification Mini-Language;它定义了什么是可能的。这种迷你语言定义了如何格式化引用的值(:
替换字段语法中的后面部分)。
你可以做这样的事情。
注意
这是一个粗略的例子,不应被认为是完整的和经过测试的。但我认为它向你展示了一种开始到达你想去的地方的方法。
import string
class SliceFormatter(string.Formatter):
def get_value(self, key, args, kwds):
if '|' in key:
try:
key, indexes = key.split('|')
indexes = map(int, indexes.split(','))
if key.isdigit():
return args[int(key)][slice(*indexes)]
return kwds[key][slice(*indexes)]
except KeyError:
return kwds.get(key, 'Missing')
return super(SliceFormatter, self).get_value(key, args, kwds)
phrase = "Hello {name|0,5}, nice to meet you. I am {name|6,9}. That is {0|0,4}."
fmt = SliceFormatter()
print fmt.format(phrase, "JeffJeffJeff", name="Larry Bob")
输出
Hello Larry, nice to meet you. I am Bob. That is Jeff.
注 2
不支持像[:5]
or一样的切片[6:]
,但我认为这也很容易实现。此外,对于超出范围的切片索引等也没有错误检查。
您可以使用运行时评估的“f”字符串。Python f-strings 支持切片,并且不使用格式化程序之类的“迷你语言”。在 f 字符串的每个花括号中都可以使用 python 表达式的全部功能。不幸的是,没有 string.feval() 函数...... imo 应该有(语言不应该有没有提供给用户的魔法能力)。
您也不能在字符串类型中添加一个,因为无法修改/扩展内置的 python 类型。
有关运行时评估 f 字符串的示例,请参阅https://stackoverflow.com/a/49884004/627042 。
直接回答您的问题:不,内置 str 格式不支持切片。虽然,如果 f-strings(运行时评估)不符合您的需求,有一种解决方法。
解决方法
之前扩展string.Formatter的答案并不完全正确,因为重载 get_value 不是将切片机制添加到string.Formatter的正确方法。
import string
def transform_to_slice_index(val: str):
if val == "_":
return None
else:
return int(val)
class SliceFormatter(string.Formatter):
def get_field(self, field_name, args, kwargs):
slice_operator = None
if type(field_name) == str and '|' in field_name:
field_name, slice_indexes = field_name.split('|')
slice_indexes = map(transform_to_slice_index,
slice_indexes.split(','))
slice_operator = slice(*slice_indexes)
obj, first = super().get_field(field_name, args, kwargs)
if slice_operator is not None:
obj = obj[slice_operator]
return obj, first
解释
get_value在get_field内部调用,它仅用于从vformat()访问 args 和 kwargs 。attr 和项目访问在 get_field 中完成。因此,切片访问应该在 super().get_field 返回所需的 obj 之后完成。
话虽如此,重载 get_value 会给您带来一个问题,即在遍历对象后格式化程序将无法用于切片。您可以在此示例中看到错误:
WrongSliceFormatter().format("{foo.bar[0]|1,3}", foo=foo)
>> ValueError: "Only '.' or '[' may follow ']' in format field specifier"
这是一个很好的解决方案,很好地解决了我的切片问题。然而,我也想做价值消除。例如,我可能想要填充 10 个字符字段的“AveryLongStringValue”可能会被截断为“...ngValue”。因此,我扩展了您的示例以支持切片、删除和正常格式合二为一。这就是我想出的。
class SliceElideFormatter(string.Formatter):
"""An extended string formatter that provides key specifiers that allow
string values to be sliced and elided if they exceed a length limit. The
additional formats are optional and can be combined with normal python
formatting. So the whole syntax looks like:
key[|slice-options][$elide-options[:normal-options]
Where slice options consist of '|' character to begin a slice request,
followed by slice indexes separated by commas. Thus {FOO|5,} requests
everything after the 5th element.
The elide consist of '$' character followed by an inter max field value,
followed by '<', '^', or '>' for pre, centered, or post eliding, followed
by the eliding string. Thus {FOO$10<-} would display the last 9 chanacters
of a string longer then 10 characters with '-' prefix.
Slicing and eliding can be combined. For example given a dict of
{'FOO': 'centeredtextvalue', and a format string of
'{FOO|1,-1$11^%2E%2E%2E}' would yield 'ente...valu'. The slice spec removes
the first and last characrers, and the elide spec center elides the
remaining value with '...'. The '...' value must be encoded in URL format
since . is an existing special format character.
"""
def get_value(self, key, args, kwds):
"""Called by string.Formatter for each format key found in the format
string. The key is checked for the presence of a slice or elide intro-
ducer character. If one or both a found the slice and/or elide spec
is extracted, parsed and processed on value of found with the remaining
key string.
Arguments:
key, A format key string possibly containing slice or elide specs
args, Format values list tuple
kwds, Format values key word dictrionary
"""
sspec = espec = None
if '|' in key:
key, sspec = key.split('|')
if '$' in sspec:
sspec, espec = sspec.split('$')
elif '$' in key:
key, espec = key.split('$')
value = args[int(key)] if key.isdigit() else kwds[key]
if sspec:
sindices = [int(sdx) if sdx else None
for sdx in sspec.split(',')]
value = value[slice(*sindices)]
if espec:
espec = urllib.unquote(espec)
if '<' in espec:
value = self._prefix_elide_value(espec, value)
elif '>' in espec:
value = self._postfix_elide_value(espec, value)
elif '^' in espec:
value = self._center_elide_value(espec, value)
else:
raise ValueError('invalid eliding option %r' % elidespec)
if sspec or espec:
return value
return super(SliceElideFormatter,self).get_value(key, args, kwds)
def _center_elide_value(self, elidespec, value):
"""Return center elide value if it exceeds the elide length.
Arguments:
elidespec, The elide spec field extracted from key
value, Value obtained from remaing key to maybe be elided
"""
elidelen, elidetxt = elidespec.split('^')
elen, vlen = int(elidelen), len(value)
if vlen > elen:
tlen = len(elidetxt)
return value[:(elen-tlen)//2] + elidetxt + value[-(elen-tlen)//2:]
return value
def _postfix_elide_value(self, elidespec, value):
"""Return postfix elided value if it exceeds the elide length.
Arguments:
elidespec, The elide spec field extracted from key
value, Value obtained from remaing key to maybe be elided
"""
elidelen, elidetxt = elidespec.split('>')
elen, vlen = int(elidelen), len(value)
if vlen > elen:
tlen = len(elidetxt)
return value[:(elen-tlen)] + elidetxt
return value
def _prefix_elide_value(self, elidespec, value):
"""Return prefix elided value if it exceeds the elide length.
Arguments:
elidespec, The elide spec field extracted from key
value, Value obtained from remaing key to maybe be elided
"""
elidelen, elidetxt = elidespec.split('<')
elen, vlen = int(elidelen), len(value)
if vlen > elen:
tlen = len(elidetxt)
return elidetxt + value[-(elen-tlen):]
return value
例如,所有三种格式规范可以组合起来剪裁第一个和最后一个字符的值,将切片居中删除为 10 个字符的值,最后在 12 个字符的字段中右对齐,如下所示:
sefmtr = SliceElideFormatter()
data = { 'CNT':'centeredtextvalue' }
fmt = '{CNT|1,-1$10^**:>12}'
print '%r' % sefmtr.format(fmt, *(), **data)
输出:'ente**value'。对于其他可能感兴趣的人。非常感谢。