Почему это string.join (список) вместо list.join (строка)?

Это всегда смущало меня. Кажется, это было бы лучше:

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

Чем это:

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

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

Ответ 1

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

Например:

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

Ответ 2

Это обсуждалось в строковых методах... наконец-то в ветке 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, 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. В то время было трудно предоставить базовый итеративный класс (который упоминается в другом комментарии).

Решение Гвидо записано в историческом письме с решением str.join(seq):

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

Ответ 3

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

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

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

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

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

Ответ 4

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

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

На самом деле есть два метода соединения (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 вместе, так что теперь у них есть смысл.

Ответ 5

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

Ответ 6

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

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

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

Ответ 7

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

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

Ответ 8

- в "-". 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']}

Ответ 9

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

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

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

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

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

Ответ 10

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

my_list = ["Hello", "world"]
print reduce(lambda x, y: x+y, my_list)

Ответ 11

Переменные my_list и "-" являются объектами. В частности, это экземпляры классов list и str соответственно. Функция join относится к классу str. Поэтому используется синтаксис "-".join(my_list), поскольку объект "-" принимает my_list в качестве входных данных.