Python join: почему это string.join (список) вместо list.join (строка)? - PullRequest
1583 голосов
/ 30 января 2009

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

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

Чем это:

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

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

Ответы [ 9 ]

1163 голосов
/ 30 января 2009

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

например:

import urllib2
print '\n############\n'.join(
    urllib2.urlopen('http://data.stackexchange.com/users/7095'))
259 голосов
/ 30 сентября 2012

Это обсуждалось в строковых методах ... наконец-то нить в 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):

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

243 голосов
/ 30 января 2009

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

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

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

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

--- Марк Пилигрим, Погружение в Питона

62 голосов
/ 30 января 2009

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

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

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

41 голосов
/ 14 апреля 2015

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

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

>>> ' | '.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, чем на других итерациях.

23 голосов
/ 30 января 2009

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

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

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

12 голосов
/ 30 января 2009

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

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

6 голосов
/ 04 декабря 2017

- в "-". Join (my_list) объявляет, что вы конвертируете в строку из соединяемых элементов список. Он ориентирован на результат (просто для удобства памяти и понимания)

Я делаю исчерпывающую таблицу для методов_для_строки для вашей справки.

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']}
2 голосов
/ 07 мая 2018

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

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

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

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...