>>> '{x[1]}'.format(x="asd")
's'
>>> '{x[1:3]}'.format(x="asd")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: string indices must be integers
这种行为的原因可能是什么?
基于你的评论的实验,检查对象的__getitem__
方法实际接收到的值:
class C:
def __getitem__(self, index):
print(repr(index))
'{c[4]}'.format(c=C())
'{c[4:6]}'.format(c=C())
'{c[anything goes!@#$%^&]}'.format(c=C())
C()[4:6]
4
'4:6'
'anything goes!@#$%^&'
slice(4, 6, None)
4
被转换为int
,但4:6
并未像通常的切片一样被转换为slice(4, 6, None)
。相反,它仍然是一个简单的字符串'4:6'
。这不是索引/切片字符串的有效类型,因此您会收到TypeError:string indices must be integers
的错误消息。
更新:
这是否有记录?嗯...我没有看到真正清晰的东西,但@GACy20 指出了一些微妙之处。语法具有以下规则。field_name ::= arg_name ("." attribute_name | "[" element_index "]")*
element_index ::= digit+ | index_string
index_string ::= <any source character except "]"> +
field_name
,我们关心的是element_index
部分4:6。我认为如果digit+
有一个有意义的名称的规则会更清晰:{{rule_with_meaningful_name}}。field_name ::= arg_name ("." attribute_name | "[" element_index "]")*
element_index ::= index_integer | index_string
index_integer ::= digit+
index_string ::= <any source character except "]"> +
index_integer
和index_string
会更清晰地表明digit+
被转换为整数(而不是保持数字字符串),而<any source character except "]"> +
将保持字符串。digit
或digits+
通常被转换为整数。index_string
情况是特殊的:'{c[4]}'.format(c=c)
执行与单独使用c[4]
相同的查找;但是'{c[anything goes!@#$%^&]}'.format(c=c)
表现得好像方括号内有引号,即c['anything goes!@#$%^&']
。从这个观点来看,切片语法不起作用,因为它已经被“隐含的引号”情况所取代。(我强调这是一个哲学上的论点,而不是实际上的。) - IMSoP'{x[1]}'.format(x="asd")
这里的 [1]
语法不是正常的字符串索引语法,即使在这种情况下它看起来像是以相同的方式工作。
它使用了格式规范迷你语言。这个机制还允许在格式化字符串中传递对象和访问任意属性(例如'{x.name}'.format(x=some_object)
)。
这个“假”的索引语法也允许将可索引对象传递给 format
并直接从格式化的字符串中获取所需的元素:
'{x[0]}'.format(x=('a', 'tuple'))
# 'a'
'{x[1]}'.format(x=('a', 'tuple'))
# 'tuple'
文档中唯一提到的关于此的参考资料(至少我能找到的)是这个段落:
field_name 本身以 arg_name 开头,arg_name 可以是数字或关键字。如果它是数字,则表示位置参数;如果是关键字,则表示命名关键字参数。如果格式字符串中的数字 arg_name 依次为 0、1、2…,则可以省略所有数字(而不仅仅是某些数字),然后数字 0、1、2… 将按照该顺序自动插入其中。由于 arg_name 没有引号定界,因此无法在格式字符串中指定任意字典键(例如字符串 '10' 或 ':-]')。arg_name 可以跟随任意数量的索引或属性表达式。形如 '.name' 的表达式使用 getattr() 选择命名属性,而形如 '[index]' 的表达式使用
__getitem__()
进行索引查找。
虽然它提到了
形如 '[index]' 的表达式使用
__getitem__()
进行索引查找。
但它没有提到不支持切片语法的任何内容。
对我来说,这感觉像是文档中的一个疏漏,特别是因为 '{x[1:3]}'.format(x="asd")
生成如此神秘的错误消息,更重要的是因为 __getitem__
已经支持切片。
class C:^M def __getitem__(self, x):^M print(repr(x))^Mf"{C()[1:2]}
我得到了 slice(1, 2, None)
。然后我尝试了 print("asd"[slice(1, 2, None))
,但它没有导致 TypeError。我是否误解了正在发生的事情? - d33tahelement_index ::= digit+ | index_string index_string ::= <any source character except "]"> +
因此,element_index
要么是没有前导 +
的正整数,要么是 "索引字符串":"{x[1]} {x[+1]}".format(x={1: 'integer', '+1': 'string'}) -> 'integer string'
。换句话说,在使用 [index_element]
语法时,index_element
总是被视为字面字符串,除非它与正则表达式 ^\d+$
匹配,此时它将被解释为整数。他们本可以包含切片语法,但他们没有这样做。 - GACy20"{x[ 1]}".format(x={' 1': 'string'}) -> 'string'
- GACy20
x = 'asd' ; f'{x[1:3]}'
- DeepSpacei = 1; x = 'asd'; f'{x[i]'}
和'{x[i]}'.format(i=1, x='asd')
的工作方式非常不同。 - Bergi