Python join: por qué es string.join (list) en lugar de list.¿unir (cadena)?


Esto siempre me ha confundido. Parece que esto sería mejor:

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

Que esto:

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

¿Hay una razón específica para que sea así?

Author: rrao, 2009-01-30

9 answers

Es porque cualquier iterable se puede unir, no solo listas, sino que el resultado y el "joiner" son siempre cadenas.

Ej:

import urllib2
print '\n############\n'.join(
    urllib2.urlopen('http://data.stackexchange.com/users/7095'))
 1036
Author: recursive,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-10-27 05:28:55

Porque el método join() está en la clase string, en lugar de la clase list?

Estoy de acuerdo en que se ve gracioso.

Véase http://www.faqs.org/docs/diveintopython/odbchelper_join.html :

Nota Histórica. Cuando aprendí por primera vez Python, esperaba que unirse fuera un método de una lista, que tomaría la delimiter como argumento. Un montón de la gente siente lo mismo, y hay una historia detrás del método join. Antes para Python 1.6, las cadenas no tener todo estos métodos útiles. Había un módulo de cadena separado que contenía todas las funciones de cadena; cada función tomó una cadena como su primera argumento. The functions were deemed lo suficientemente importante para poner en la las cadenas mismas, lo que tenía sentido para funciones como inferior, superior y dividir. Pero muchos Python de núcleo duro los programadores se opusieron a la nueva unión método, argumentando que debe ser un método de la lista en su lugar, o que no debería moverse en absoluto, sino simplemente estancia una parte del antiguo módulo de cadena (que todavía tiene un montón de cosas útiles en él). Uso el nuevo método de unión exclusivamente, pero usted verá el código escrito ya sea y si realmente te molesta, puede usar la cadena vieja.función join en su lugar.

- - - Mark Pilgrim, Sumérgete en Python

 232
Author: Bill Karwin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-10-30 17:11:16

Esto fue discutido en los métodos de cadena ... finalmente hilo en el Python-Dev achive, y fue aceptado por Guido. Este hilo comenzó en junio de 1999, y str.join fue incluido en Python 1.6 que fue lanzado en septiembre de 2000 (y soportado Unicode). Python 2.0 (soporta str métodos incluyendo join) fue lanzado en octubre de 2000.

  • Se propusieron cuatro opciones en este hilo:
    • str.join(seq)
    • seq.join(str)
    • seq.reduce(str)
    • join como un accesorio función
  • Guido quería soportar no solo lists, tuple s, sino todas las secuencias/iterables.
  • seq.reduce(str) es difícil para los recién llegados.
  • seq.join(str) introduce una dependencia inesperada de secuencias a str/unicode.
  • join() como una función incorporada soportaría solo tipos de datos específicos. Así que usar un espacio de nombres incorporado no es bueno. Si join() soporta muchos tipos de datos, crear una implementación optimizada sería difícil, si se implementa usando el método __add__ entonces es O (n2).
  • La cadena separater (sep) no debe omitirse. Lo explícito es mejor que lo implícito.

No hay otras razones ofrecidas en este hilo.

Aquí hay algunos pensamientos adicionales (los míos y los de mi amigo):

  • El soporte Unicode estaba llegando, pero no era definitivo. En ese momento UTF-8 era el más probable a punto de reemplazar UCS2/4. Para calcular la longitud total del búfer de las cadenas UTF-8 necesita saber la codificación de caracteres regla.
  • En ese momento, Python ya había decidido una regla de interfaz de secuencia común donde un usuario podría crear una clase (iterable) similar a una secuencia. Pero Python no soportó la extensión de tipos incorporados hasta la versión 2.2. En ese momento era difícil proporcionar la clase iterable básica (que se menciona en otro comentario).

La decisión de Guido se registra en un correo histórico , decidiendo sobre str.join(seq):

Divertido, pero parece correcto! Barry, ve por se...
-- Guido van Rossum

 218
Author: Yoshiki Shibukawa,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-09-06 15:46:14

Estoy de acuerdo en que es contradictorio al principio, pero hay una buena razón. Unirse no puede ser un método de una lista porque:

  • también debe funcionar para diferentes iterables (tuplas, generadores, etc.)
  • debe tener un comportamiento diferente entre diferentes tipos de cadenas.

En realidad hay dos métodos join (Python 3.0):

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

Si join era un método de una lista, entonces tendría que inspeccionar sus argumentos para decidir a cuál de ellos llamar. Y no puedes unir byte y str juntos, por lo que la forma en que lo tienen ahora tiene sentido.

 59
Author: Kiv,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-01-29 23:03:45

¿por Qué es string.join(list) en lugar de list.join(string)?

Esto es porque join es un método "string"! Crea una cadena a partir de cualquier iterable. Si pegamos el método en listas, ¿qué pasa cuando tenemos iterables que no son listas?

¿Qué pasa si tienes una tupla de cuerdas? Si este fuera un método list, tendría que convertir cada iterador de cadenas como un list antes de poder unir los elementos en una sola cadena! Por ejemplo:

some_strings = ('foo', 'bar', 'baz')

Hagamos nuestra propia lista método de unión:

class OurList(list): 
    def join(self, s):
        return s.join(self)

Y para usarlo, tenga en cuenta que primero tenemos que crear una lista de cada iterable para unir las cadenas en ese iterable, desperdiciando tanto la memoria como la potencia de procesamiento:

>>> l = OurList(some_strings) # step 1, create our list
>>> l.join(', ') # step 2, use our list join method!
'foo, bar, baz'

Así que vemos que tenemos que agregar un paso adicional para usar nuestro método list, en lugar de usar solo el método builtin string:

>>> ' | '.join(some_strings) # a single step!
'foo | bar | baz'

Advertencia de rendimiento para generadores

El algoritmo que Python utiliza para crear la cadena final con str.join en realidad tiene que pasar sobre el iterable dos veces, así que si usted le proporciona una expresión generador, tiene que materializarlo en una lista primero antes de que pueda crear la cadena final.

Por lo tanto, mientras que pasar alrededor de los generadores suele ser mejor que las comprensiones de listas, str.join es una excepción:

>>> 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

Sin embargo, la operación str.join sigue siendo semánticamente una operación de "cadena", por lo que todavía tiene sentido tenerlo en el objeto str que en iterables varios.

 36
Author: Aaron Hall,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-04-18 23:09:33

Piense en ello como la operación ortogonal natural para dividir.

Entiendo por qué es aplicable a cualquier cosa iterable y por lo tanto no se puede implementar fácilmente solo en la lista.

Para la legibilidad, me gustaría verlo en el lenguaje, pero no creo que sea realmente factible - si la iterabilidad fuera una interfaz, entonces podría agregarse a la interfaz, pero es solo una convención, por lo que no hay una forma central de agregarlo al conjunto de cosas que son iterables.

 23
Author: Andy Dent,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-01-30 02:43:51

Principalmente porque el resultado de un someString.join() es una cadena.

La secuencia (lista o tupla o lo que sea) no aparece en el resultado, solo una cadena. Debido a que el resultado es una cadena, tiene sentido como un método de una cadena.

 11
Author: S.Lott,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-01-29 22:51:45

- en "-".join (my_list) declara que está convirtiendo a una cadena de elementos un list.It orientado a los resultados.(solo para facilitar la memoria y la comprensión)

Hago una lista exhaustiva de methods_of_string para su referencia.

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']}
 3
Author: JawSaw,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-07-22 10:10:02

Ambos no son agradables.

Cadena.join (xs, delimit) significa que el módulo string es consciente de la existencia de una lista, que no tiene por qué conocer, ya que el módulo string solo funciona con cadenas.

Lista.join (delimit) es un poco más agradable porque estamos muy acostumbrados a que las cadenas sean un tipo fundamental(y hablando en lenguaje común, lo son). Sin embargo, esto significa que join necesita ser enviado dinámicamente porque en el contexto arbitrario de a.split("\n") el compilador de python podría no saber lo que es a, y tendrá que buscarlo (análogamente a vtable lookup), que es caro si lo haces muchas veces.

Si el compilador de tiempo de ejecución de python sabe que list es un módulo incorporado, puede omitir la búsqueda dinámica y codificar la intent en el bytecode directamente, mientras que de lo contrario necesita resolver dinámicamente "join" de "a", que puede ser varias capas de herencia por llamada(ya que entre llamadas, el significado de join puede haber cambiado, idioma).

Tristemente, este es el defecto final de la abstracción; no importa qué abstracción elijas, tu abstracción solo tendrá sentido en el contexto del problema que estás tratando de resolver, y como tal nunca puedes tener una abstracción consistente que no se vuelva inconsistente con las ideologías subyacentes a medida que comienzas a pegarlas sin envolverlas en una visión que sea consistente con tu ideología. Sabiendo esto, el enfoque de python es más flexible ya que es más barato, es depende de usted pagar más para que se vea "mejor", ya sea haciendo su propia envoltura o su propio preprocesador.

 2
Author: Dmitry,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-05-07 19:41:39