Python连接:为什么它是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" 

有这样的具体原因吗?

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

例如:

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

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

我同意这看起来很有趣。

请参阅http://www.faqs.org/docs/diveintopython/odbchelper_join.html

历史笔记。 当我第一次学习Python的时候,我期望join是一个列表的方法,它将分隔符作为参数。 许多人都以同样的方式感受,并且在连接方法背后有一个故事。 在Python 1.6之前,string没有所有这些有用的方法。 有一个单独的string模块,其中包含所有的string函数; 每个函数都将一个string作为第一个参数。 这些function被认为足够重要的string本身,这对function,如低,上,分裂是有意义的。 但是许多核心的Python程序员反对新的连接方法,认为它应该是列表的一种方法,或者它不应该移动,而只是保留旧的string模块的一部分(仍然有很多在它有用的东西)。 我独占地使用了新的连接方法,但是您会看到以任何方式编写的代码,如果真的困扰您,可以使用旧的string.join函数。

— Mark Pilgrim,潜入Python

这是在String方法中讨论的…最后在Python-Dev中进行了线程化,并被Guido接受。 这个线程开始于1999年6月, str.join包含在2000年9月发布的Python 1.6中(并支持Unicode)。 Python 2.0(包括join支持的str方法)于2000年10月发布。

  • 在这个线程中提出了四个选项:
    • str.join(seq)
    • seq.join(str)
    • seq.reduce(str)
    • 作为内置函数join
  • Guido想要不仅支持list s, tuple s,而且支持所有的序列/迭代。
  • seq.reduce(str)对于新来者来说很难。
  • seq.join(str)引入了从序列到str / unicode的意外的依赖。
  • join()作为内置函数将只支持特定的数据types。 所以使用内置的命名空间并不好。 如果join()支持多种数据types,那么使用__add__方法实现,那么创build优化的实现将是困难的,那么它就是O(n²)。
  • 分隔符string( sep )不应该被省略。 显式比隐式更好。

这个线程没有提供其他的原因。

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

  • Unicode支持即将到来,但并不是最终的。 那时UTF-8是最有可能取代UCS2 / 4的。 要计算UTF-8string的总caching长度,需要知道字符编码规则。
  • 那时,Python已经决定了一个通用的序列接口规则,用户可以创build一个类似序列(可迭代)的类。 但Python直到2.2才支持扩展内置types。 当时很难提供基本的迭代类(在另一个评论中提到)。

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

有趣的,但它确实是对的! 巴里,去…
– Guido van Rossum

我同意起初它是违反直觉的,但是有一个很好的理由。 join不能成为一个列表的方法,因为:

  • 它也必须适用于不同的迭代器(元组,生成器等)
  • 它在不同types的string之间必须有不同的行为。

实际上有两个连接方法(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字节和字节,所以他们现在的方式是合理的。

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

这是因为join是一个“string”的方法! 它从任何迭代中创build一个string。 如果我们将这个方法固定在列表上,那么当我们有不是列表的迭代时呢?

如果你有一个string元组呢? 如果这是一个list方法,则必须将每个这样的string迭代器都作为list进行投射,然后才能将这些元素join到单个string中! 例如:

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

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

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

要使用它,请注意,我们必须首先从每个迭代中创build一个列表,以便在迭代中joinstring,同时浪费内存和处理能力:

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

所以我们看到我们不得不添加一个额外的步骤来使用我们的list方法,而不是使用内build的string方法:

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

发电机性能警告

Python用来用str.join创build最后一个string的str.join实际上需要两次遍历迭代器,所以如果你提供了一个生成器expression式,它必须首先将它物化成一个列表,然后才能创build最终的string。

因此,传递生成器通常比列表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 

尽pipe如此, str.join操作在语义上仍然是一个“string”操作,所以它在str对象上比在其他iterables上有意义。

把它看作是分裂的自然正交操作。

我明白为什么它适用于任何可迭代的,所以不能轻易地在列表中实现。

为了便于阅读,我想用语言来看,但是我不认为这是可行的 – 如果迭代是一个接口,那么它可以被添加到接口,但它只是一个约定,所以没有中心的方法来将它添加到可迭代的集合中。

主要是因为someString.join()的结果是一个string。

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

我认为这样更好:

 my_list = ["Hello", "world"] print reduce(lambda x, y: x+y, my_list)