题 Python join:为什么是string.join(list)而不是list.join(string)?


这一直困扰着我。看起来这会更好:

my_list = ["Hello", "world"]
print my_list.join("-")
# Produce: "Hello-world"

比这个:

my_list = ["Hello", "world"]
print "-".join(my_list)
# Produce: "Hello-world"

是否有这样的具体原因?


1383
2018-01-29 22:45


起源


为了方便记忆和理解, - 声明你正在加入一个列表并转换为一个字符串。它是面向结果的。 - JawSaw
@JawSaw:这只会让mem更加困惑。 - einpoklum


答案:


这是因为任何可迭代都可以连接,而不仅仅是列表,但结果和“连接”总是字符串。

例如:

import urllib2
print '\n############\n'.join(
    urllib2.urlopen('http://data.stackexchange.com/users/7095'))

1001
2018-01-29 22:51



这可能是我所见过的一个相当复杂的概念的最简洁的解释。很好的例子 - Adam Hughes
即使代码有意义,我也不同意概念。 list.join(string) 看起来更像是一种面向对象的方法 string.join(list) 对我来说听起来更具程序性。 - Eduardo Pignatelli
那么为什么它不能在iterable上实现呢? - Time Sheep
@TimeSheep:整数列表没有有意义的连接,即使它是可迭代的。 - recursive
@krysopath:它 能够 是的,但有多种这样的理解。非列表字符串的迭代需要一种方法来连接。字符串列表是可迭代的。因此,使用这种方法可以满足所有用例。清单 可以 有一个连接方法,就像在javascript中一样,但在python中有很多用例仍然需要现有的连接方法。你可以非常简单地将现有的一个变成你正在思考的东西。例如 ", ".join(map(str,numbers))。 - recursive


因为 join() 方法是在字符串类中,而不是列表类?

我同意它看起来很有趣。

看到 http://www.faqs.org/docs/diveintopython/odbchelper_join.html

历史记录。 当我第一次学习   Python,我希望join是一个方法   列表,这将采取   分隔符作为参数。很多   人们也有同感,并且有   连接方法背后的故事。先   对于Python 1.6,字符串并不是全部   这些有用的方法。有一个   单独的字符串模块包含   所有的字符串函数;每   函数首先使用字符串   论据。功能被认为是   足够重要的东西   字符串本身,这是有道理的   对于诸如lower,upper和。之类的函数   分裂。但是很多硬核Python   程序员反对新的加入   方法,认为它应该是一个   相反的方法,或它   根本不应该移动,而只是停留   旧字符串模块的一部分(其中   还有很多有用的东西)。   我只使用新的连接方法,   但你会看到编写的代码   方式,如果它真的困扰你,你   可以使用旧的string.join函数   代替。

--- Mark Pilgrim,潜入Python


228
2018-01-29 22:48





这在讨论中讨论过 字符串方法......最后 在Python-Dev achive中的线程,并被Guido接受。这个帖子始于1999年6月,并且 str.join 包含在2000年9月发布的Python 1.6中(并支持Unicode)。 Python 2.0(支持 str 方法包括 join)于2000年10月发布。

  • 这个帖子中提出了四个选项:
    • str.join(seq)
    • seq.join(str)
    • seq.reduce(str)
    • join 作为内置功能
  • Guido不仅想要支持 listS, tuples,但所有序列/可迭代。
  • seq.reduce(str) 新来者很难。
  • seq.join(str) 引入了从序列到str / unicode的意外依赖。
  • join() 作为内置函数,仅支持特定数据类型。因此使用内置命名空间并不好。如果 join() 支持许多数据类型,如果使用实现,则创建优化的实现将很困难 __add__ 然后它是O(n²)。
  • 分离绳(sep)不应该被省略。显式优于隐式。

此主题中没有其他原因。

这里有一些额外的想法(我自己和我的朋友):

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

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

有趣,但看起来确实对!巴里,去吧......
  --Guido van Rossum


213
2017-09-30 15:21





我同意它起初是违反直觉的,但这是有充分理由的。加入不能是列表的方法,因为:

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

实际上有两种连接方法(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连接在一起,所以他们现在拥有它的方式是有道理的。


58
2018-01-29 23:03





为什么 string.join(list) 代替 list.join(string)

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

如果你有一个字符串元组怎么办?如果这是一个 list 方法,你必须将每个这样的字符串迭代器强制转换为 list 在将元素加入单个字符串之前!例如:

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

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

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

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

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

所以我们看到我们必须添加一个额外的步骤来使用我们的list方法,而不是仅仅使用内置字符串方法:

>>> ' | '.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 对象而不是杂项迭代。


36
2018-04-14 00:45





将其视为分裂的自然正交操作。

我理解为什么它适用于任何可迭代的东西,所以不能轻易实现 只是 在列表上。

为了便于阅读,我希望在语言中看到它,但我不认为这实际上是可行的 - 如果迭代是一个接口,那么它可以添加到接口但它只是一个约定,所以没有中心的方法将它添加到可迭代的事物集中。


22
2018-01-30 02:43





主要是因为一个结果 someString.join() 是一个字符串。

序列(列表或元组或其他)不会出现在结果中,只是一个字符串。因为结果是一个字符串,所以它作为字符串的方法是有意义的。


11
2018-01-29 22:51