web-dev-qa-db-fra.com

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

Cela m'a toujours dérouté. Il semble que ce serait plus gentil:

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 pour laquelle c'est comme ça?

1622
Evan Fosmark

C’est parce qu’on peut joindre toutes les entités itératives, pas seulement des listes, mais le résultat et le "participant" sont toujours des chaînes.

PAR EXEMPLE:

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

Ceci a été discuté dans le fil String methods ... finally dans le achive Python-Dev, et a été accepté par Guido. Ce fil a commencé en juin 1999 et str.join a été inclus dans Python 1.6, qui a été publié en septembre 2000 (et pris en charge par Unicode). Python 2.0 (méthodes _str supportées, y compris join) ont été publiées en octobre 2000.

  • Quatre options étaient proposées dans ce fil de discussion:
    • str.join(seq)
    • seq.join(str)
    • seq.reduce(str)
    • join en tant que fonction intégrée
  • Guido voulait non seulement lists, Tuples, mais également toutes les séquences/itérables.
  • seq.reduce(str) est difficile pour les nouveaux venus.
  • seq.join(str) introduit une dépendance inattendue à partir de séquences vers str/unicode.
  • join() en tant que fonction intégrée ne prend en charge que des types de données spécifiques. Donc, utiliser un espace de noms intégré n'est pas bon. Si join() prend en charge de nombreux types de données, il serait difficile de créer une implémentation optimisée. Si elle est implémentée à l'aide de la méthode __add__, il s'agit alors de O (n²).
  • La chaîne de séparation (sep) ne doit pas être omise. Explicite est meilleur qu'implicite.

Il n'y a pas d'autres raisons proposées dans ce fil.

Voici quelques réflexions supplémentaires (les miennes et celles de mon ami):

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

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

C'est marrant, mais ça semble juste! Barry, vas-y….
- Guido van Rossum

275
Yoshiki Shibukawa

Parce que la méthode join() est dans la classe string, au lieu de la classe list?

Je suis d'accord, ça a l'air drôle.

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

Note historique. Quand j'ai appris le python pour la première fois, je m'attendais à ce que join soit une méthode d'une liste, qui prendrait le délimiteur comme argument. Beaucoup de gens pensent la même chose, et il y a une histoire derrière la méthode de jointure. Avant Python1.6, les chaînes ne disposaient pas de toutes ces méthodes utiles. Il y avait un module de chaîne séparé qui contenait toutes les fonctions de chaîne; chaque fonction a pris une chaîne comme premier argument. Les fonctions ont été jugées suffisamment importantes pour être placées sur les chaînes elles-mêmes, ce qui était logique pour des fonctions telles que low, upper et split. Mais de nombreux programmeurs Python durs se sont opposés à la nouvelle méthode de jointure, affirmant qu'il devrait s'agir d'une méthode de la liste ou qu'elle ne devrait pas être déplacée du tout, mais qu'elle resterait simplement une partie de l'ancienne chaîne. module (qui contient encore beaucoup de choses utiles). J'utilise exclusivement la nouvelle méthode de jointure, mais vous verrez le code écrit d'une manière ou d'une autre et si cela vous dérange vraiment, vous pouvez utiliser l'ancienne fonction string.join à la place.

--- Mark Pilgrim, plongez dans le python

245
Bill Karwin

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

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

Il existe actuellement 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, il devrait alors examiner ses arguments pour décider lequel d'entre eux appeler. Et vous ne pouvez pas joindre octet et str ensemble, la manière dont ils ont été conçus est donc logique.

63
Kiv

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

C'est parce que join est une méthode "chaîne"! Il crée une chaîne à partir de n'importe quelle valeur itérable. Si nous collons la méthode sur des listes, qu’en est-il des iterables qui ne sont pas des listes?

Et si vous avez un tuple de ficelles? S'il s'agissait d'une méthode list, vous devriez convertir chaque 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 cette itérable, gaspillant à la fois de la mémoire et de 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 d'utiliser simplement la méthode de chaîne intégrée:

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

Mise en garde concernant les performances des générateurs

L'algorithme utilisé par Python pour créer la dernière chaîne avec str.join doit en réalité passer deux fois sur l'itération. Par conséquent, si vous lui fournissez une expression génératrice, il doit d'abord la matérialiser dans une liste. peut créer la chaîne finale.

Ainsi, bien que passer des générateurs soit généralement préférable à une compréhension de 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, l'opération str.join est toujours sémantiquement une opération "chaîne", il est donc logique de l'avoir sur l'objet str plutôt que sur divers itérables.

42
Aaron Hall

Considérez-le comme l'opération orthogonale naturelle à scinder.

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

Pour des raisons de lisibilité, j'aimerais le voir dans le langage, mais je ne pense pas que ce soit réellement réalisable. Si itérabilité était une interface, elle pourrait être ajoutée à l'interface, mais il ne s'agit que d'une convention et il n'y a donc pas de moyen central de ajoutez-le à l'ensemble des choses qui sont itérables.

23
Andy Dent

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

La séquence (liste ou tuple ou autre) n'apparaît pas dans le résultat, mais simplement une chaîne. Parce que le résultat est une chaîne, cela a du sens en tant que méthode d'une chaîne.

12
S.Lott

- in "-". Join (my_list) déclare que vous convertissez une chaîne en éléments de la jonction d'éléments à une liste.Il est axé sur les résultats.

Je fais une feuille de triche exhaustive de methods_of_string pour votre référence.

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']}
7
Algebra

Les deux ne sont pas Nice.

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

list.join (délimite) est un peu plus agréable parce que nous sommes tellement habitués à ce que les chaînes soient un type fondamental (et lingualement, elles le sont). Cependant, cela signifie que cette jointure doit être envoyée dynamiquement car, dans le contexte arbitraire de a.split("\n"), le compilateur python pourrait ne pas savoir ce qu’il en est et devra le rechercher (de manière analogue à vtable lookup). , ce qui coûte cher si vous le faites souvent.

si le compilateur d'exécution python sait que la liste est un module intégré, il peut ignorer la recherche dynamique et encoder directement l'intention dans le code intermédiaire, alors qu'il doit sinon résoudre de manière dynamique la "jointure" de "a", qui peut être constitué de plusieurs couches d'héritage par appel (car entre les appels, la signification de la jointure peut avoir changé, car python est un langage dynamique).

malheureusement, c’est le défaut ultime de l’abstraction; quelle que soit l'abstraction que vous choisissez, votre abstraction n'aura de sens que dans le contexte du problème que vous essayez de résoudre, et en tant que telle, vous ne pouvez jamais avoir une abstraction cohérente qui ne devienne pas incompatible avec les idéologies sous-jacentes lorsque vous commencez à les coller. ensemble sans les envelopper dans une vue cohérente avec votre idéologie. Sachant cela, l’approche de Python est plus flexible, car c’est moins cher, c’est à vous de payer plus pour que cela paraisse "plus joli", soit en fabriquant votre propre wrapper, soit votre propre pré-processeur.

2
Dmitry