这一直让我困惑。看起来这样会更好:
["Hello", "world"].join("-")
除此之外:
"-".join(["Hello", "world"])
有什么具体的原因吗?
这一直让我困惑。看起来这样会更好:
["Hello", "world"].join("-")
除此之外:
"-".join(["Hello", "world"])
有什么具体的原因吗?
当前回答
为什么是string.join(list)而不是list.joing(string)?
这是因为join是一个“字符串”方法!它从任何可迭代对象创建字符串。如果我们将方法固定在列表上,那么当我们有不是列表的可迭代项时呢?
如果你有一个字符串元组呢?如果这是一个列表方法,则必须将每个这样的字符串迭代器转换为列表,然后才能将元素连接到单个字符串中!例如:
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对象上还是有意义的,而不是放在其他可迭代对象上。
其他回答
为什么是string.join(list)而不是list.joing(string)?
这是因为join是一个“字符串”方法!它从任何可迭代对象创建字符串。如果我们将方法固定在列表上,那么当我们有不是列表的可迭代项时呢?
如果你有一个字符串元组呢?如果这是一个列表方法,则必须将每个这样的字符串迭代器转换为列表,然后才能将元素连接到单个字符串中!例如:
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对象上还是有意义的,而不是放在其他可迭代对象上。
我100%同意你的观点。如果我们把这里的所有答案和评论归结为“历史原因”。
str.join不仅令人困惑或不好看,在现实世界的代码中也是不切实际的。它击败了可读的函数或方法链接,因为分隔符很少(曾经?)是以前计算的结果。根据我的经验,它总是一个恒定的硬编码值,如“,”。
我使用tools.functools清理代码-允许从一个方向读取代码:
>>> from toolz.functoolz import curry, pipe
>>> join = curry(str.join)
>>>
>>> a = ["one", "two", "three"]
>>> pipe(
... a,
... join("; ")
>>> )
'one; two; three'
管道中还有其他几个函数。其结果是,它可以很容易地从一个方向读取,从开始到结束都是一系列功能。库里地图帮助很大。
主要是因为someString.jjoin()的结果是字符串。
序列(列表或元组等)不会出现在结果中,只是一个字符串。因为结果是一个字符串,所以它作为字符串的方法是有意义的。
这是因为任何可迭代的都可以连接(例如,列表、元组、dict、集合),但其内容和“joiner”必须是字符串。
例如:
'_'.join(['welcome', 'to', 'stack', 'overflow'])
'_'.join(('welcome', 'to', 'stack', 'overflow'))
'welcome_to_stack_overflow'
使用字符串以外的其他内容将引发以下错误:
TypeError:序列项0:应为str实例,找到int
你不能只加入列表和元组。您可以加入几乎任何可迭代的。可迭代的包括生成器、映射、过滤器等
>>> '-'.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这种能力才有效。而不是向所有迭代器授予联接:列表、元组、集合、字典、生成器、映射、过滤器,所有这些迭代器都只有对象作为公共父对象。
当然,range()和zip()也是迭代器,但它们永远不会返回str,因此不能与str.jjoin()一起使用
>>> '-'.join(range(48, 55))
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: sequence item 0: expected str instance, int found