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

2025-02-12 10:04:00
admin
原创
56
摘要:问题描述:这总是让我很困惑。看来这样会更好:["Hello", "world"].join("-") 比这个:"-".join(["Hello", "world"]) 出现这种情况有什么具体...

问题描述:

这总是让我很困惑。看来这样会更好:

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

比这个:

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

出现这种情况有什么具体原因吗?


解决方案 1:

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

例如:

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

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

类型错误:序列项 0:预期 str 实例,找到 int

解决方案 2:

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

  • 此主题中提出了四个选项:

    • separator.join(items)

    • items.join(separator)

    • items.reduce(separator)

    • join作为内置函数

  • Guido 不仅希望支持lists 和tuples,还希望支持所有序列/可迭代对象。

  • items.reduce(separator)对于新来者来说很困难。

  • items.join(separator)引入了从序列到 str/unicode 的意外依赖性。

  • join()因为独立的内置函数仅支持特定数据类型。因此使用内置命名空间并不好。如果join()要支持多种数据类型,则创建优化的实现会很困难:如果使用该__add__方法实现,则时间为 O(n²)。

  • 分隔符字符串 ( separator) 不应省略。显式优于隐式。

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

  • Unicode 支持即将推出,但还不是最终版本。当时 UTF-8 最有可能取代 UCS-2/-4。要计算 UTF-8 字符串的总缓冲区长度,该方法需要知道字符编码。

  • 当时 Python 已经确定了通用的序列接口规则,用户可以创建一个类似序列(可迭代)的类。但 Python 直到 2.2 才支持扩展内置类型。当时很难提供基本iterable类(另一条评论中提到)。

Guido 的决定记录在一封历史邮件中,决定如下separator.join(items)

有趣,但看起来确实正确!Barry,加油...

--Guido van Rossum

解决方案 3:

我同意这乍一看是违反直觉的,但有一个很好的理由。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 连接在一起,因此它们现在的方式是有意义的。

解决方案 4:

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

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

如果你有一个字符串元组怎么办?如果这是一个list方法,你必须将每个这样的字符串迭代器强制转换为 a,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

解决方案 5:

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

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

为了可读性,我希望在语言中看到它,但我认为这实际上不可行——如果可迭代性是一个接口,那么它可以被添加到接口中,但它只是一种约定,因此没有集中的方法将它添加到可迭代的事物集合中。

解决方案 6:

-声明"-".join(my_list)您正在将列表元素转换为字符串。它是面向结果的。(只是为了便于记忆和理解)

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

string_methods_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']}

解决方案 7:

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

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

解决方案 8:

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

解决方案 9:

您不仅可以连接列表和元组。您可以连接几乎任何可迭代对象。可迭代对象包括生成器、映射、过滤器等

>>> '-'.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 此功能是有效的。而不是授予所有迭代器 join:list、tuple、set、dict、generator、map、filter,所有这些迭代器都只有 object 作为共同父级。

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

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

解决方案 10:

我完全同意你的问题。如果我们把这里的所有答案和评论归结起来,解释可以归结为“历史原因”。

str.join不仅令人困惑或看起来不好看,而且在实际代码中也不实用。它破坏了可读的函数或方法链,因为分隔符很少(曾经?)是先前计算的结果。根据我的经验,它始终是一个恒定的硬编码值,例如", "

我清理了我的代码 - 允许从一个方向读取它 - 使用tools.functoolz

>>> from toolz.functoolz import curry, pipe
>>> join = curry(str.join)
>>>
>>> a = ["one", "two", "three"]
>>> pipe(
...     a, 
...     join("; ")
>>> )
'one; two; three'

我还会将其他几个函数放入管道中。这样一来,它就可以轻松地以单向读取的方式从头到尾读取函数。柯里化map非常有帮助。

相关推荐
  政府信创国产化的10大政策解读一、信创国产化的背景与意义信创国产化,即信息技术应用创新国产化,是当前中国信息技术领域的一个重要发展方向。其核心在于通过自主研发和创新,实现信息技术应用的自主可控,减少对外部技术的依赖,并规避潜在的技术制裁和风险。随着全球信息技术竞争的加剧,以及某些国家对中国在科技领域的打压,信创国产化显...
工程项目管理   1565  
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   1354  
  信创国产芯片作为信息技术创新的核心领域,对于推动国家自主可控生态建设具有至关重要的意义。在全球科技竞争日益激烈的背景下,实现信息技术的自主可控,摆脱对国外技术的依赖,已成为保障国家信息安全和产业可持续发展的关键。国产芯片作为信创产业的基石,其发展水平直接影响着整个信创生态的构建与完善。通过不断提升国产芯片的技术实力、产...
国产信创系统   21  
  信创生态建设旨在实现信息技术领域的自主创新和安全可控,涵盖了从硬件到软件的全产业链。随着数字化转型的加速,信创生态建设的重要性日益凸显,它不仅关乎国家的信息安全,更是推动产业升级和经济高质量发展的关键力量。然而,在推进信创生态建设的过程中,面临着诸多复杂且严峻的挑战,需要深入剖析并寻找切实可行的解决方案。技术创新难题技...
信创操作系统   27  
  信创产业作为国家信息技术创新发展的重要领域,对于保障国家信息安全、推动产业升级具有关键意义。而国产芯片作为信创产业的核心基石,其研发进展备受关注。在信创国产芯片的研发征程中,面临着诸多复杂且艰巨的难点,这些难点犹如一道道关卡,阻碍着国产芯片的快速发展。然而,科研人员和相关企业并未退缩,积极探索并提出了一系列切实可行的解...
国产化替代产品目录   28  
热门文章
项目管理软件有哪些?
云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用