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


1761

这一直使我感到困惑。看起来这样会更好:

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

比这个:

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

是否有特定原因?


1
为了便于记忆和理解,-声明您正在连接列表并转换为字符串,它是面向结果的。
微积分

11
@JawSaw:这让我更加困惑。
einpoklum

32
我认为简短的答案是,这是因为Python的类型系统不够强大,并且一次实现此功能str要比在每种可迭代类型上实现起来容易。
BallpointBen

3
我认为最初的想法是因为join()返回一个字符串,所以必须从字符串上下文中调用它。将join()放在列表​​上并没有多大意义,因为列表是对象的容器,并且不应具有仅针对字符串的一次性函数。
约书亚·伯恩斯

Answers:


1247

这是因为任何可迭代项都可以连接(例如,列表,元组,字典,集合),但是结果和“连接器” 必须是字符串。

例如:

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

使用字符串以外的其他东西会引发以下错误:

TypeError:序列项0:预期的str实例,找到的int


55
即使在代码方面有意义,我在概念上也不同意。list.join(string)看起来更像是一种面向对象的方法,而string.join(list)对我来说听起来更具有程序性。
Eduardo Pignatelli

21
那么为什么不对它进行迭代呢?
SteenSchütt18年

10
@TimeSheep:整数列表没有有意义的连接,即使它是可迭代的。
递归

16
我尝试使用print(str.join('-', my_list))它,并且效果更好。
pimgeek '18

13
@TimeSheep因为iterable不是具体类型,所以iterable是接口,任何定义__iter__方法的类型。对于所有join非常特殊的用例,要求所有可迭代对象也要实现将使通用接口(也包括非字符串上的可迭代对象)复杂化。定义join链约束以“不直观”的命令为代价来绕开此问题。更好的选择可能是保留它的功能,第一个参数是可迭代的,第二个参数(可选的)是连接符字符串-但该船已经驶过。
user4815162342'8

318

这在String方法中进行了讨论……最终在Python-Dev中实现,并被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)


251

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

我同意这看起来很有趣。

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

历史记录。当我第一次学习Python时,我期望join是一个列表方法,它将分隔符作为参数。很多人都有相同的感觉,join方法背后还有一个故事。在Python 1.6之前,字符串没有所有这些有用的方法。有一个单独的字符串模块,其中包含所有字符串函数。每个函数都将字符串作为第一个参数。这些功能被认为很重要,足以放在字符串本身上,这对于诸如lower,upper和split这样的功能是有意义的。但是许多铁杆Python程序员反对使用新的join方法,认为它应该是列表的方法,或者根本不应该移动,而只是保留旧字符串模块的一部分(仍然有很多方法)里面有用的东西)。

--- Mark Pilgrim,深入Python


12
Python 3 string库删除了所有冗余str方法,因此您不再可以使用string.join()。就我个人而言,我从来没有想过它是“有趣的”,它很有意义,因为您不仅可以连接列表,还可以连接,但连接器始终是字符串!
马丁·彼得斯

67

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

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

实际上有两种连接方法(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结合在一起,因此它们现在的用法很有意义。


45

为什么用它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)

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

>>> 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对象上而不是在其他可迭代对象上还是有意义的。


24

将其视为拆分的自然正交运算。

我明白为什么它适用于任何可迭代的,所以不能简单地执行只是在列表中。

为了提高可读性,我想用该语言查看它,但我认为这实际上是不可行的-如果可迭代性是一个接口,则可以将其添加到该接口中,但这只是一个约定,因此没有中央方法将其添加到可迭代的事物集中。


13

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

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


10

- 在“-”中。join(my_list)声明您正在从列表的连接元素转换为字符串。它以结果为导向。(为便于记忆和理解)

我制作了一个methods_of_string的详尽备忘单,供您参考。

string_methonds_44 = {
    'convert': ['join','split', 'rsplit','splitlines', 'partition', 'rpartition'],
    'edit': ['replace', 'lstrip', 'rstrip', 'strip'],
    'search': ['endswith', 'startswith', 'count', 'index', 'find','rindex', 'rfind',],
    'condition': ['isalnum', 'isalpha', 'isdecimal', 'isdigit', 'isnumeric','isidentifier',
                  'islower','istitle', 'isupper','isprintable', 'isspace', ],
    'text': ['lower', 'upper', 'capitalize', 'title', 'swapcase',
             'center', 'ljust', 'rjust', 'zfill', 'expandtabs','casefold'],
    'encode': ['translate', 'maketrans', 'encode'],
    'format': ['format', 'format_map']}

3

两者都不好。

string.join(xs,delimit)表示字符串模块知道列表的存在,而列表列表却没有任何业务意义,因为字符串模块仅适用于字符串。

list.join(delimit)更好一点,因为我们习惯于将字符串作为基本类型(从语言上讲,它们是)。但是,这意味着需要动态调度连接,因为在a.split("\n") python编译器,可能不知道a是什么,因此需要查找它(类似于vtable查找),如果您花很多时间这样做,这会很昂贵。次。

如果python运行时编译器知道列表是内置模块,则它可以跳过动态查找并将意图直接编码为字节码,否则,它需要动态地解析“ a”的“ join”,这可能是多层的每次调用的继承关系(因为两次调用之间,join的含义可能已更改,因为python是一种动态语言)。

可悲的是,这是抽象的最终缺陷。无论您选择哪种抽象,您的抽象都仅在您要解决的问题的背景下才有意义,因此,当您开始将它们胶合在一起时,您将永远无法获得与基础意识形态相一致的一致抽象而不将它们包装在与您的意识形态相符的视图中。知道了这一点,python的方法更灵活,因为它更便宜,您可以自己制作包装器或自己的预处理器,为此要花更多的钱才能使它看起来“更漂亮”。


0

变量my_list"-"都是对象。具体来说,它们分别是类list和的实例str。该join函数属于该类str。因此,使用语法"-".join(my_list)是因为对象"-"my_list作为输入。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.