更新:
使内置字符串不可迭代的想法是proposed on python.org in 2006.我的问题不同之处在于我试图仅偶尔抑制此功能;仍然这整个线程非常相关.
以下是在试验基础上实施非迭代str的关键comments by Guido:
[…] I implemented this (it was really simple to
do) but then found I had to fix tons of places that iterate over
strings. For example:
The sre parser and compiler use things like set(“0123456789”) and also iterate over the characters of the input regexp to parse it.
difflib has an API defined for either two lists of strings (a typical line-by-line diff of a file), or two strings (a typical
intra-line diff), or even two lists of anything (for a generalized
sequence diff).small changes in optparse.py, textwrap.py, string.py.
And I’m not even at the point where the regrtest.py framework even
works (due to the difflib problem).I’m abandoning this project; the patch is SF patch 1471291. I’m no
longer in favor of this idea; it’s just not practical, and the premise
that there are few good reasons to iterate over a string has been
refuted by the use cases I found in both sre and difflib.
原始问题:
虽然字符串的一个简洁功能是字符串是可迭代的,但当与鸭子打字相结合时,它可能会导致灾难:
# record has to support [] operation to set/retrieve values
# fields has to be an iterable that contains the fields to be set
def set_fields(record, fields, value):
for f in fields:
record[f] = value
set_fields(weapon1, ('Name', 'ShortName'), 'Dagger')
set_fields(weapon2, ('Name',), 'Katana')
set_fields(weapon3, 'Name', 'Wand') # I was tired and forgot to put parentheses
除了通过在无数地方测试isinstance(fields,str)之外,没有任何例外会被提出,并且没有简单的方法可以解决这个问题.在某些情况下,这个bug需要很长时间才能找到.
我想在我的项目中完全禁止字符串被视为可迭代的.这是个好主意吗?它可以轻松安全地完成吗?
也许我可以继承内置的str,这样我就需要显式调用get_iter(),如果我希望它的对象被视为可迭代的话.然后每当我需要一个字符串文字时,我会创建一个这个类的对象.
以下是一些与切线相关的问题:
How can I tell if a python variable is a string or a list?
how to tell a variable is iterable but not a string
解决方法:
遗憾的是,没有任何方法可以自动执行此操作.您提出的解决方案(不可迭代的str子类)遇到与isinstance()相同的问题…即,您必须记住在使用字符串的任何地方使用它,因为没有办法让Python使用它代替本地阶级.当然,你无法对内置对象进行修补.
我可能会建议,如果你发现自己编写了一个带有可迭代容器或字符串的函数,那么你的设计可能有问题.但有时你无法避免它.
在我看来,最不干扰的事情就是将检查放入一个函数中,并在进入循环时调用它.这至少会将行为更改放在最有可能看到它的位置:在for语句中,不会被隐藏在类中的某个位置.
def iterate_no_strings(item):
if issubclass(item, str): # issubclass(item, basestring) for Py 2.x
return iter([item])
else:
return iter(item)
for thing in iterate_no_strings(things):
# do something...