我想通过
str.format
实现以下目标:
x,y = 1234,5678
print str(x)[2:] + str(y)[:2]
我能做到的唯一方法是:
print '{0}{1}'.format(str(x)[2:],str(y)[:2])
现在,这是一个例子,我真正拥有的是一个又长又乱的字符串,所以我想将切片放在
{}
中。我已经研究了文档,但我无法找出正确的语法。我的问题是:是否可以在替换字段内对字符串进行切片?
这是一个粗略的示例,不应被视为完整和经过测试。但我认为它向您展示了一种开始实现您想要的目标的方法。
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.
不支持像 [:5]
或
[6:]
这样的切片,但我认为这也很容易实现。此外,也没有对切片索引超出范围等进行错误检查。之前扩展
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"
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'。对于其他可能感兴趣的人。非常感谢。
new_template = '{old[0]} is {old[2:]}'
这样的f字符串, 希望使用
new = template.format(old=old)
,但行不通。如果你遇到类似的情况,你可以只使用一组 lambda,
new_maker = lambda old: f'{old[0]} is {old[2:]}'
,像
new = new_maker(old)
一样使用它
x="nowpossible"
print(" slicing is possible {}".format(x[0:2]))
输出
slicing is possible now