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

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

Чем это:

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

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

1842
Evan Fosmark 30 Янв 2009 в 01:45

10 ответов

Лучший ответ

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

Например:

import urllib2
print('\n############\n'.join(
    urllib2.urlopen('http://data.stackexchange.com/users/7095')))
1228
Boris 23 Сен 2019 в 03:44

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

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

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

24
Andy Dent 30 Янв 2009 в 02:43

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

0
fiftytwocards 15 Окт 2019 в 19:38

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

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

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
Calculus 22 Июл 2018 в 10:10

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

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

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

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

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

249
Brian Burns 30 Окт 2017 в 17:11

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

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

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

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

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

3
Dmitry 7 Май 2018 в 19:41

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

45
Aaron Hall 18 Апр 2016 в 23:09

Это обсуждалось в Строковые методы ... Наконец-то поток в Python-Dev achive, и был принят Гвидо. Этот поток начался в июне 1999 года, и str.join был включен в Python 1.6, выпущенный в сентябре 2000 года (и поддерживающий Юникод). 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):

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

308
Iulian Onofrei 14 Окт 2019 в 13:28

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

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

13
S.Lott 29 Янв 2009 в 22:51

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

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

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

66
Kiv 29 Янв 2009 в 23:03