这一直让我困惑。看起来这样会更好:

["Hello", "world"].join("-")

除此之外:

"-".join(["Hello", "world"])

有什么具体的原因吗?


当前回答

这在String方法中讨论过。。。最后一个线程,并被Guido接受。该线程始于1999年6月,str.join包含在2000年9月发布的Python 1.6中(支持Unicode)。Python 2.0(支持str方法,包括join)于2000年10月发布。

本主题提出了四个选项:str.join(seq)顺序连接(str)顺序减少(str)作为内置函数连接Guido希望不仅支持列表和元组,而且支持所有序列/可迭代项。seq.reduce(str)对于新手来说很难。join(str)引入了从序列到str/unicode的意外依赖关系。join()作为独立的内置函数将只支持特定的数据类型。因此,使用内置命名空间是不好的。如果join()支持许多数据类型,那么创建一个优化的实现将是困难的:如果使用__add__方法实现,那么它将是O(n²)。分隔符字符串(sep)不应省略。显式优于隐式。

以下是一些额外的想法(我自己和我朋友的):

Unicode支持即将到来,但还不是最终的。当时UTF-8最有可能取代UCS-2/-4。要计算UTF-8字符串的总缓冲区长度,该方法需要知道字符编码。当时,Python已经决定了一个通用的序列接口规则,用户可以在其中创建一个类似序列的(可迭代的)类。但Python直到2.2才支持扩展内置类型。当时很难提供基本的可迭代类(这在另一条评论中提到)。

Guido的决定记录在历史邮件中,决定str.join(seq):

有趣,但看起来确实没错!巴里,去吧。。。吉多·范罗苏姆

其他回答

这是因为任何可迭代的都可以连接(例如,列表、元组、dict、集合),但其内容和“joiner”必须是字符串。

例如:

'_'.join(['welcome', 'to', 'stack', 'overflow'])
'_'.join(('welcome', 'to', 'stack', 'overflow'))
'welcome_to_stack_overflow'

使用字符串以外的其他内容将引发以下错误:

TypeError:序列项0:应为str实例,找到int

我同意一开始这是违反直觉的,但有一个很好的理由。联接不能是列表的方法,因为:

它也必须适用于不同的可迭代对象(元组、生成器等)它必须在不同类型的字符串之间具有不同的行为。

实际上有两种连接方法(Python 3.0):

>>> b"".join
<built-in method join of bytes object at 0x00A46800>
>>> "".join
<built-in method join of str object at 0x00A28D40>

如果join是列表的一个方法,那么它必须检查其参数,以决定调用其中的哪一个。而且你不能将byte和str连接在一起,所以它们现在的方式是有意义的。

你不能只加入列表和元组。您可以加入几乎任何可迭代的。可迭代的包括生成器、映射、过滤器等

>>> '-'.join(chr(x) for x in range(48, 55))
'0-1-2-3-4-5-6'

>>> '-'.join(map(str, (1, 10, 100)))
'1-10-100'

使用生成器、地图、过滤器等的好处是它们几乎不需要内存,而且几乎是即时创建的。

这只是概念上的另一个原因:

str.join(<iterator>)

只有赋予str这种能力才有效。而不是向所有迭代器授予联接:列表、元组、集合、字典、生成器、映射、过滤器,所有这些迭代器都只有对象作为公共父对象。

当然,range()和zip()也是迭代器,但它们永远不会返回str,因此不能与str.jjoin()一起使用

>>> '-'.join(range(48, 55))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: sequence item 0: expected str instance, int found

为什么是string.join(list)而不是list.joing(string)?

这是因为join是一个“字符串”方法!它从任何可迭代对象创建字符串。如果我们将方法固定在列表上,那么当我们有不是列表的可迭代项时呢?

如果你有一个字符串元组呢?如果这是一个列表方法,则必须将每个这样的字符串迭代器转换为列表,然后才能将元素连接到单个字符串中!例如:

some_strings = ('foo', 'bar', 'baz')

让我们滚动我们自己的列表连接方法:

class OurList(list): 
    def join(self, s):
        return s.join(self)

要使用它,请注意,我们必须首先从每个可迭代项创建一个列表,以连接该可迭代项中的字符串,从而浪费内存和处理能力:

>>> l = OurList(some_strings) # step 1, create our list
>>> l.join(', ') # step 2, use our list join method!
'foo, bar, baz'

因此,我们需要添加一个额外的步骤来使用列表方法,而不是只使用内置字符串方法:

>>> ' | '.join(some_strings) # a single step!
'foo | bar | baz'

发电机性能注意事项

Python使用str.join创建最终字符串的算法实际上必须两次传递可迭代字符串,因此如果您为其提供生成器表达式,则必须先将其具体化到列表中,然后才能创建最终字符串。

因此,虽然传递生成器通常优于列表理解,但str.join是一个例外:

>>> import timeit
>>> min(timeit.repeat(lambda: ''.join(str(i) for i in range(10) if i)))
3.839168446022086
>>> min(timeit.repeat(lambda: ''.join([str(i) for i in range(10) if i])))
3.339879313018173

尽管如此,str.join操作在语义上仍然是一个“字符串”操作,因此将它放在str对象上还是有意义的,而不是放在其他可迭代对象上。

这在String方法中讨论过。。。最后一个线程,并被Guido接受。该线程始于1999年6月,str.join包含在2000年9月发布的Python 1.6中(支持Unicode)。Python 2.0(支持str方法,包括join)于2000年10月发布。

本主题提出了四个选项:str.join(seq)顺序连接(str)顺序减少(str)作为内置函数连接Guido希望不仅支持列表和元组,而且支持所有序列/可迭代项。seq.reduce(str)对于新手来说很难。join(str)引入了从序列到str/unicode的意外依赖关系。join()作为独立的内置函数将只支持特定的数据类型。因此,使用内置命名空间是不好的。如果join()支持许多数据类型,那么创建一个优化的实现将是困难的:如果使用__add__方法实现,那么它将是O(n²)。分隔符字符串(sep)不应省略。显式优于隐式。

以下是一些额外的想法(我自己和我朋友的):

Unicode支持即将到来,但还不是最终的。当时UTF-8最有可能取代UCS-2/-4。要计算UTF-8字符串的总缓冲区长度,该方法需要知道字符编码。当时,Python已经决定了一个通用的序列接口规则,用户可以在其中创建一个类似序列的(可迭代的)类。但Python直到2.2才支持扩展内置类型。当时很难提供基本的可迭代类(这在另一条评论中提到)。

Guido的决定记录在历史邮件中,决定str.join(seq):

有趣,但看起来确实没错!巴里,去吧。。。吉多·范罗苏姆