为什么是 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"

是否有特定原因?

答案

这是因为任何 iterable 都可以联接,不仅是列表,而且结果和 “joiner” 始终是字符串。

例如:

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

这在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 希望不仅支持listtuple ,而且还支持所有序列 / 可迭代。
  • seq.reduce(str)对于新手来说很困难。
  • seq.join(str)引入了从序列到 str / unicode 的意外依赖关系。
  • join()作为内置函数将仅支持特定的数据类型。因此,使用内置的名称空间是不好的。如果join()支持许多数据类型,则创建优化的实现将很困难,如果使用__add__方法实现, __add__ O(n²)。
  • 分隔符( sep )不应省略。显式胜于隐式。

此线程中没有其他原因。

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

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

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

有趣,但看起来确实正确!巴里,去吧...
- 吉多 · 范 · 罗苏姆(Guido van Rossum)

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

我同意这看起来很有趣。

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

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

--- Mark Pilgrim,深入 Python

我同意起初这是违反直觉的,但是有充分的理由。 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 结合在一起,因此它们现在的用法很有意义。

为什么是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实际上必须将可迭代对象传递两次,因此,如果为其提供生成器表达式,则必须先将其具体化为列表,然后才能创建最终字符串。

因此,尽管绕过生成器通常比列表理解更好, 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对象上而不是在其他可迭代对象上还是有意义的。

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

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

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

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

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

-在 “-” 中。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']}

两者都不好。

string.join(xs,delimit)表示字符串模块知道列表的存在,这是没有业务可知的,因为字符串模块仅适用于字符串。

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

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

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

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