首页 文章

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

提问于
浏览
1463

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

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

比这个:

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

是否有这样的具体原因?

9 回答

  • 11

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

    • 此线程中提出了四个选项:

    • str.join(seq)

    • seq.join(str)

    • seq.reduce(str)

    • join 作为内置函数

    • Guido不仅支持 list s, tuple ,而且还支持所有序列/可迭代 .

    • 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的决定记录在historical mail中,决定 str.join(seq)

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

  • 2

    两者都不好 .

    string.join(xs,delimit)意味着字符串模块知道列表的存在,它没有业务知道,因为字符串模块只适用于字符串 .

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

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

    遗憾的是,这是抽象的最终缺陷;无论你选择什么样的抽象,你的抽象只会在你试图解决的问题的上下文中有意义,因此当你开始粘合它们时,你永远不会有一个与底层意识形态不一致的一致抽象 . 在没有将它们包裹在与您的意识形态一致的视图中的情况下 . 知道这一点,python的方法更灵活,因为它更便宜,你需要付出更多才能让它看起来“更好”,无论是制作你自己的包装器,还是你自己的预处理器 .

  • 237

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

    我同意它看起来很有趣 .

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

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

  • 59

    - in "-" .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']}
    
  • 23

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

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

    如果你有一个字符串元组怎么办?如果这是一个 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 操作在语义上仍然是"string"操作,因此在 str 对象上使用它比在其他迭代中使用它仍然有意义 .

  • 37

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

    例如:

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

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

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

  • 1072

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

    • 它也必须适用于不同的迭代(元组,生成器等)

    • 它必须在不同类型的字符串之间具有不同的行为 .

    实际上有两种连接方法(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

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

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

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

相关问题