Python join: почему это 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"

Есть ли какая-то конкретная причина?

+1484
29 янв. '09 в 22:45
источник поделиться
10 ответов

Это потому, что любой итеративный может быть объединен, а не только списки, но результат и "joiner" всегда являются строками.

например:

import urllib2
print '\n############\n'.join(
    urllib2.urlopen('http://data.stackexchange.com/users/7095'))
+1094
29 янв. '09 в 22:51
источник

Поскольку метод join() находится в строчном классе, а не в классе списка?

Я согласен, что это выглядит забавно.

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

Историческая заметка. Когда я впервые узнал Python, я ожидал присоединиться к методу списка, который ограничитель в качестве аргумента. Много люди чувствуют то же самое, и theres история за методом объединения. предшествующий к Python 1.6, строки не имели все эти полезные методы. Был отдельный строковый модуль, содержащий все строковые функции; каждый функция взяла строку в качестве первой аргумент. Функции считались достаточно важно, чтобы сами струны, что имело смысл для таких функций, как нижний, верхний и Трещина. Но многие жесткие Python программисты возражали против нового объединения метод, утверждая, что он должен быть вместо этого, или что это не должен двигаться вообще, но просто останьтесь часть старого строкового модуля (который все еще есть много полезных вещей в нем). Я использую только новый метод соединения, но вы увидите код, написанный либо путь, и если это действительно беспокоит вас, вы может использовать старую функцию string.join вместо этого.

--- Марк Пилигрим, погрузитесь в Python

+237
29 янв. '09 в 22:48
источник

Это обсуждалось в String methods... finally поток в Python-Dev achive, и был принят Гвидо. Этот поток начался в июне 1999 года, а str.join был включен в Python 1.6, который был выпущен в сентябре 2000 года (и поддерживался Unicode). Python 2.0 (поддерживаемые методы str включая join) был выпущен в октябре 2000 года.

  • В этой теме было предложено четыре варианта:
    • str.join(seq)
    • seq.join(str)
    • seq.reduce(str)
    • join как встроенная функция
  • Гвидо хотел поддерживать не только list s, tuple s, но все последовательности /iterables.
  • 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):

Забавный, но это кажется правильным! Барри, иди за ним...
- Гуйдо ван Россум

+231
30 сент. '12 в 15:21
источник

Я согласен с тем, что сначала это противоречит интуиции, но есть веская причина. Соединение не может быть методом списка, потому что:

  • он должен работать и для разных итераций (кортежи, генераторы и т.д.).
  • он должен иметь различное поведение между различными типами строк.

На самом деле есть два метода соединения (Python 3.0):

>>> b"".join
<built-in method join of bytes object at 0x00A46800>
>>> "".join
<built-in method join of str object at 0x00A28D40>

Если объединение было методом списка, тогда ему нужно будет проверить его аргументы, чтобы решить, какой из них нужно вызвать. И вы не можете присоединиться к байту и str вместе, так что теперь у них есть смысл.

+59
29 янв. '09 в 23:03
источник

Почему это 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'

Итак, мы видим, что нам нужно добавить дополнительный шаг для использования нашего метода списка, а не просто использовать встроенный метод string:

>>> ' | '.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, чем в разных итерациях.

+37
14 апр. '15 в 0:45
источник

Подумайте об этом как о естественной ортогональной операции для разделения.

Я понимаю, почему он применим к чему-либо итерабельному и поэтому не может быть легко реализован только в списке.

Для удобства чтения я хотел бы увидеть его на этом языке, но я не думаю, что это реально возможно - если итерабельность была интерфейсом, то его можно было бы добавить в интерфейс, но это просто соглашение, и поэтому нет центральный способ добавить его к множеству вещей, которые являются итерабельными.

+23
30 янв. '09 в 2:43
источник

В первую очередь потому, что результатом someString.join() является строка.

Последовательность (список или кортеж или что-то еще) не появляется в результате, просто строка. Поскольку результатом является строка, это имеет смысл как метод строки.

+11
29 янв. '09 в 22:51
источник

- в "-". join (my_list) объявляет, что вы конвертируете в строку из объединения элементов list.It, ориентированный на результат (просто для легкой памяти и понимания)

Для вашей справки я делаю исчерпывающий обман методов.

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']}
+4
04 дек. '17 в 12:22
источник

Оба не хороши.

string.join(xs, delimit) означает, что строковый модуль знает о существовании списка, о котором он не знает о бизнесе, поскольку строковый модуль работает только со строками.

list.join(delimit) немного приятнее, потому что мы так привыкли к тому, что строки являются фундаментальным типом (и, на самом деле, они являются языком). Однако это означает, что соединение нужно отправлять динамически, потому что в произвольном контексте a.split("\n") компилятор python может не знать, что такое a, и ему нужно будет искать его (аналогично vtable lookup), который дорого, если вы делаете это много раз.

если компилятор времени выполнения python знает, что этот список является встроенным модулем, он может пропустить динамический поиск и напрямую закодировать намерение в байт-код, тогда как в противном случае ему необходимо динамически разрешить "соединение" "a", которое может быть на несколько уровней наследования на вызов (так как между вызовами смысл объединения может измениться, поскольку python является динамическим языком).

к сожалению, это конечная ошибка абстракции; независимо от того, какую абстракцию вы выберете, ваша абстракция будет иметь смысл только в контексте проблемы, которую вы пытаетесь решить, и поэтому вы никогда не сможете иметь последовательную абстракцию, которая не будет противоречить основополагающим идеологиям, когда вы начнете склеивать их вместе не обернув их взглядом, соответствующим вашей идеологии. Зная это, подход python является более гибким, поскольку он дешевле, и вам стоит заплатить больше, чтобы он выглядел "лучше", либо создав собственную оболочку, либо ваш собственный препроцессор.

+2
07 мая '18 в 19:32
источник

Я думаю, что так лучше:

my_list = ["Hello", "world"]
print reduce(lambda x, y: x+y, my_list)
0
29 сент. '17 в 1:54
источник

Посмотрите другие вопросы по меткам или Задайте вопрос