Question Python join: pourquoi est-ce string.join (list) au lieu de list.join (string)?


Cela m'a toujours confondu. Il semble que ce serait plus agréable:

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

Que ceci:

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

Y a-t-il une raison spécifique à cela?


1378
2018-01-29 22:45


origine


Réponses:


C'est parce que tout itérable peut être joint, pas seulement les listes, mais le résultat et le "menuisier" sont toujours des chaînes.

PAR EXEMPLE:

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

998
2018-01-29 22:51



Parce que le join() méthode est dans la classe de chaîne, au lieu de la classe de liste?

Je suis d'accord, ça a l'air marrant.

Voir http://www.faqs.org/docs/diveintopython/odbchelper_join.html:

Note historique Quand j'ai appris   Python, je m'attendais à rejoindre pour être une méthode   d'une liste, qui prendrait la   délimiteur en tant qu'argument. Beaucoup de   les gens ressentent la même chose, et il y a   une histoire derrière la méthode de jointure. Avant   à Python 1.6, les chaînes n'ont pas toutes   ces méthodes utiles. Il y avait un   module de chaîne séparée qui contenait   toutes les fonctions de chaîne; chaque   fonction a pris une chaîne comme son premier   argument. Les fonctions ont été jugées   assez important pour mettre sur le   cordes eux-mêmes, ce qui avait du sens   pour des fonctions comme inférieure, supérieure et   Divisé. Mais beaucoup de Python hard-core   les programmeurs se sont opposés à la nouvelle jointure   méthode, en faisant valoir qu'il devrait être un   méthode de la liste à la place, ou qu'il   ne devrait pas bouger du tout, mais simplement rester   une partie de l'ancien module de chaîne (qui   a encore plein de choses utiles dedans).   J'utilise la nouvelle méthode de jointure exclusivement,   mais vous verrez le code écrit soit   chemin, et si cela vous dérange vraiment, vous   peut utiliser l'ancienne fonction string.join   au lieu.

--- Mark Pilgrim, Plongez dans Python


227
2018-01-29 22:48



Cela a été discuté dans le Méthodes de cordes ... enfin thread dans l'achive Python-Dev, et a été accepté par Guido. Ce sujet a commencé en juin 1999, et str.join a été inclus dans Python 1.6 qui a été publié en septembre 2000 (et supporté Unicode). Python 2.0 (pris en charge str méthodes incluant join) a été publié en octobre 2000.

  • Il y avait quatre options proposées dans ce fil:
    • str.join(seq)
    • seq.join(str)
    • seq.reduce(str)
    • join comme une fonction intégrée
  • Guido voulait soutenir non seulement lists, tuples, mais toutes les séquences / itérations.
  • seq.reduce(str) est difficile pour les nouveaux venus.
  • seq.join(str) introduit une dépendance inattendue des séquences à str / unicode.
  • join() comme une fonction intégrée ne supporterait que des types de données spécifiques. Donc, en utilisant un espace de noms intégré n'est pas bon. Si join() prend en charge de nombreux types de données, la création d'une mise en œuvre optimisée serait difficile, si elle est mise en __add__ méthode alors c'est O (n²).
  • La chaîne de séparateur (sep) ne devrait pas être omis. Explicite est mieux qu'implicite.

Il n'y a pas d'autres raisons offertes dans ce fil.

Voici quelques pensées supplémentaires (la mienne et celle de mon ami):

  • Le support Unicode arrivait, mais ce n'était pas définitif. A cette époque, UTF-8 était le plus susceptible de remplacer UCS2 / 4. Pour calculer la longueur totale du tampon des chaînes UTF-8, il faut connaître la règle de codage des caractères.
  • À ce moment-là, Python avait déjà décidé d'une règle d'interface de séquence commune où un utilisateur pouvait créer une classe semblable à une séquence (itérable). Mais Python ne supportait pas l'extension des types intégrés jusqu'au 2.2. A cette époque, il était difficile de fournir une classe itérative de base (qui est mentionnée dans un autre commentaire).

La décision de Guido est enregistrée dans un courrier historique, décider de str.join(seq):

C'est drôle, mais ça a l'air juste! Barry, vas-y ...
  - Guido van Rossum


211
2017-09-30 15:21



Je suis d'accord que c'est contre-intuitif au début, mais il y a une bonne raison. Join ne peut pas être une méthode d'une liste car:

  • il doit aussi fonctionner pour différentes itérations (tuples, générateurs, etc.)
  • il doit avoir un comportement différent entre différents types de chaînes.

Il existe en fait deux méthodes de jointure (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 était une méthode d'une liste, alors il devrait inspecter ses arguments pour décider lequel d'entre eux appeler. Et vous ne pouvez pas joindre les octets et les str, alors la façon dont ils l'ont maintenant est logique.


58
2018-01-29 23:03



Pourquoi est-ce string.join(list) au lieu de list.join(string)?

Ceci est dû au fait join est une méthode "chaîne"! Il crée une chaîne à partir de n'importe quel itérable. Si nous avons collé la méthode sur des listes, qu'en est-il de quand nous avons des itérations qui ne sont pas des listes?

Que faire si vous avez un tuple de chaînes? Si c'était un list méthode, vous auriez à lancer chaque tel itérateur de chaînes en tant que list avant de pouvoir joindre les éléments en une seule chaîne! Par exemple:

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

Lançons notre propre méthode de jointure de liste:

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

Et pour l'utiliser, notez que nous devons d'abord créer une liste de chaque itérable pour joindre les chaînes dans ce itérable, gaspillant à la fois la mémoire et la puissance de traitement:

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

Nous voyons donc que nous devons ajouter une étape supplémentaire pour utiliser notre méthode de liste, au lieu de simplement utiliser la méthode de chaîne intégrée:

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

Avertissement de performance pour les générateurs

L'algorithme utilisé par Python pour créer la chaîne finale avec str.join En fait, il doit passer deux fois sur l'itérateur, donc si vous lui fournissez une expression de générateur, il doit la matérialiser dans une liste avant de pouvoir créer la chaîne finale.

Ainsi, en passant autour des générateurs est généralement préférable à la compréhension de la liste, str.join est une exception:

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

Néanmoins, le str.join opération est toujours sémantiquement une opération "chaîne", il est donc logique de l'avoir sur le str objet que sur divers iterables.


36
2018-04-14 00:45



Pensez-y comme l'opération orthogonale naturelle à diviser.

Je comprends pourquoi il est applicable à tout ce qui est itérable et ne peut donc pas être facilement mis en œuvre juste sur la liste.

Pour la lisibilité, je voudrais le voir dans la langue mais je ne pense pas que ce soit faisable - si l'itérabilité était une interface alors il pourrait être ajouté à l'interface mais c'est juste une convention et donc il n'y a pas de moyen central ajoutez-le à l'ensemble des choses qui sont itératives.


22
2018-01-30 02:43



Principalement parce que le résultat d'un someString.join() est une chaîne.

La séquence (list ou tuple ou autre) n'apparaît pas dans le résultat, juste une chaîne. Parce que le résultat est une chaîne, il est logique comme une méthode d'une chaîne.


11
2018-01-29 22:51



Les deux ne sont pas gentils.

string.join (xs, delimit) signifie que le module string est conscient de l'existence d'une liste, dont il n'a aucune connaissance, puisque le module string ne fonctionne qu'avec des chaînes.

list.join (delimit) est un peu plus sympa parce que nous sommes tellement habitués aux cordes étant un type fondamental (et parlant lingualement, ils le sont). Cependant cela signifie que la jointure doit être envoyée dynamiquement car dans le contexte arbitraire de a.split("\n") le compilateur python pourrait ne pas savoir ce qu'est un, et devra le rechercher (de façon similaire à vtable lookup), ce qui est cher si vous le faites beaucoup de fois.

si le compilateur d'exécution Python sait que la liste est construit dans le module, il peut sauter la recherche dynamique et encode l'intention dans le bytecode directement, alors que sinon il doit résoudre dynamiquement « join » de « a », qui peut atteindre plusieurs couches de l'héritage par appel (puisque entre les appels, la signification de join peut avoir changé, car python est un langage dynamique).

malheureusement, c'est le défaut ultime de l'abstraction; peu importe ce que vous choisissez l'abstraction, votre abstraction ne fera sens dans le contexte du problème que vous essayez de résoudre, et en tant que tel vous ne pouvez jamais avoir une abstraction cohérente qui ne soit pas incompatible avec les idéologies sous-jacentes que vous commencez à les coller ensemble sans les envelopper dans une vue qui est compatible avec votre idéologie. Sachant cela, l'approche de python est plus souple car il est moins cher, il est à vous de payer plus cher pour le faire paraître « plus agréable », que ce soit en faisant votre propre emballage, ou votre propre préprocesseur.


1
2018-05-07 19:32