Vraag Python join: waarom is het string.join (lijst) in plaats van list.join (string)?


Dit heeft me altijd in verwarring gebracht. Het lijkt erop dat dit leuker zou zijn:

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

Dan dit:

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

Is er een specifieke reden waarom het zo is?


1378
2018-01-29 22:45


oorsprong


antwoorden:


Het is omdat elke iterabele kan worden samengevoegd, niet alleen lijsten, maar het resultaat en de "joiner" zijn altijd snaren.

bijv:

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

998
2018-01-29 22:51



Omdat het join() methode is in de klasse string, in plaats van de lijstklasse?

Ik ben het ermee eens dat het er grappig uitziet.

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

Historische opmerking. Toen ik voor het eerst leerde   Python, ik verwachtte dat join een methode zou zijn   van een lijst, die de   scheidingsteken als argument. Veel   mensen voelen zich op dezelfde manier, en er is   een verhaal achter de join-methode. voorafgaand   voor Python 1.6 hadden snaren niet alles   deze handige methoden. Er was een   afzonderlijke stringmodule die bevatte   alle stringfuncties; elk   functie nam een ​​string als eerste   argument. De functies werden geacht   belangrijk genoeg om op de   snaren zichzelf, wat logisch was   voor functies zoals onder, boven en   split. Maar veel hardcore Python   programmeurs hadden bezwaar tegen de nieuwe join   methode, met als argument dat het een a   methode van de lijst in plaats daarvan, of dat   zou helemaal niet moeten bewegen, maar gewoon blijven   een deel van de oude stringmodule (welke   heeft nog steeds veel nuttige dingen erin).   Ik gebruik uitsluitend de nieuwe join-methode,   maar je zult ook geschreven code zien   manier, en als het je echt stoort, jij   kan de oude string.join-functie gebruiken   in plaats daarvan.

--- Mark Pilgrim, duik in Python


227
2018-01-29 22:48



Dit werd besproken in de String-methoden ... eindelijk thread in de Python-Dev, en werd geaccepteerd door Guido. Deze thread begon in juni 1999, en str.join werd opgenomen in Python 1.6 die in september 2000 werd uitgebracht (en Unicode werd ondersteund). Python 2.0 (ondersteund str methoden inclusief join) werd uitgebracht in oktober 2000.

  • Er zijn vier opties voorgesteld in deze thread:
    • str.join(seq)
    • seq.join(str)
    • seq.reduce(str)
    • join als een ingebouwde functie
  • Guido wilde niet alleen ondersteunen lists, tuples, maar alle sequenties / iterables.
  • seq.reduce(str) is moeilijk voor nieuwkomers.
  • seq.join(str) introduceert onverwachte afhankelijkheid van reeksen tot str / unicode.
  • join() omdat een ingebouwde functie alleen specifieke gegevenstypen ondersteunt. Dus het gebruik van een ingebouwde naamruimte is niet goed. Als join() ondersteunt vele datatypes, het creëren van geoptimaliseerde implementatie zou moeilijk zijn, indien geïmplementeerd met behulp van de __add__ methode dan is het O (n²).
  • De scheidingstekenreeks (sep) moet niet worden weggelaten. Expliciet is beter dan impliciet.

Er zijn geen andere redenen die in deze thread worden geboden.

Hier zijn enkele aanvullende gedachten (die van mezelf en die van mijn vriend):

  • Unicode-ondersteuning kwam eraan, maar het was niet definitief. Op dat moment stond UTF-8 waarschijnlijk op het punt om UCS2 / 4 te vervangen. Voor het berekenen van de totale buffergrootte van UTF-8-reeksen moet de tekencoderingsregel bekend zijn.
  • Op dat moment had Python al besloten tot een gemeenschappelijke volgorde-interfaceregel waarbij een gebruiker een reeks-achtige (iterabele) klasse kon maken. Maar Python bood geen ondersteuning voor uitbreiding van ingebouwde typen tot 2.2. Op dat moment was het moeilijk om elementaire iterabele klasse te bieden (die wordt vermeld in een andere opmerking).

Guido's beslissing wordt vastgelegd in a historische post, beslissen str.join(seq):

Grappig, maar het lijkt goed! Barry, ga ervoor ...
  - Guido van Rossum


211
2017-09-30 15:21



Ik ben het ermee eens dat het in eerste instantie contra-intuïtief is, maar daar is een goede reden voor. Lid worden kan geen methode van een lijst zijn omdat:

  • het moet ook voor verschillende iterables werken (tuples, generators, etc.)
  • het moet ander gedrag vertonen tussen verschillende soorten reeksen.

Er zijn eigenlijk twee join-methoden (Python 3.0):

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

Als join een methode van een lijst zou zijn, dan zou het zijn argumenten moeten inspecteren om te beslissen welke van hen te bellen. En je kunt niet byte en str bij elkaar komen, dus de manier waarop ze het nu hebben, is logisch.


58
2018-01-29 23:03



Waarom is het string.join(list) in plaats van list.join(string)?

Dit is zo omdat join is een "string" -methode! Het maakt een string van een willekeurige. Als we de methode op lijsten vastzetten, wat dan als we iterables hebben die geen lijsten zijn?

Wat als je een tuple van snaren hebt? Als dit een was list methode, zou je elke dergelijke iterator van strings moeten casten als een list voordat je de elementen in een enkele snaar kon voegen! Bijvoorbeeld:

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

Laten we onze eigen list join-methode gebruiken:

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

En om het te gebruiken, moet u er rekening mee houden dat we eerst een lijst moeten maken van elke iteratie om de tekenreeksen in dat iterabele samen te voegen, waarbij zowel geheugen als verwerkingskracht worden verspild:

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

We zien dus dat we een extra stap moeten toevoegen om onze lijstmethode te gebruiken, in plaats van alleen de ingebouwde tekenreeksmethode te gebruiken:

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

Performance-waarschuwing voor generatoren

Het algoritme dat Python gebruikt om de laatste reeks mee te maken str.join moet het iterabel twee keer passeren, dus als je het een generatoruitdrukking geeft, moet het eerst in een lijst materialiseren voordat het de laatste reeks kan maken.

Dus, bij het passeren van generatoren is meestal beter dan lijstbegrippen, str.join is een uitzondering:

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

Niettemin, de str.join bewerking is nog steeds semantisch een "string" bewerking, dus het is nog steeds logisch om het op de str object dan op diverse iterables.


36
2018-04-14 00:45



Zie het als de natuurlijke orthogonale operatie om te splitsen.

Ik begrijp waarom het van toepassing is op alles wat iterabel is en kan daarom niet eenvoudig worden geïmplementeerd net op de lijst.

Voor leesbaarheid zou ik het graag in de taal zien, maar ik denk niet dat dit echt haalbaar is - als iterability een interface is, dan zou het aan de interface kunnen worden toegevoegd, maar het is gewoon een conventie en dus is er geen centrale manier om voeg het toe aan de reeks dingen die iterabel zijn.


22
2018-01-30 02:43



Vooral omdat het resultaat van een someString.join() is een string.

De reeks (lijst of tuple of wat dan ook) verschijnt niet in het resultaat, alleen een tekenreeks. Omdat het resultaat een tekenreeks is, is het zinvol als een methode van een tekenreeks.


11
2018-01-29 22:51



Beide zijn niet leuk.

string.join (xs, delimit) betekent dat de tekenreeksmodule op de hoogte is van het bestaan ​​van een lijst waarvan zij geen zaken weet, omdat de tekenreeksmodule alleen werkt met tekenreeksen.

list.join (delimit) is een beetje leuker omdat we zo gewend zijn dat strings een fundamenteel type zijn (en dat zijn ze in taalkundig opzicht). Dit betekent echter dat join moet dynamisch worden verzonden omdat in de willekeurige context van a.split("\n") de python-compiler weet misschien niet wat a is, en zal het moeten opzoeken (analoog aan vtable lookup), wat erg duur is als je het vaak doet.

als de python-runtime-compiler weet dat die lijst een ingebouwde module is, kan deze de dynamische lookup overslaan en de intentie direct in de bytecode coderen, terwijl anders dynamisch "join" van "a" moet worden opgelost, wat meerdere lagen kan zijn van inheritence per call (sinds tussen gesprekken is de betekenis van join mogelijk veranderd, omdat python een dynamische taal is).

helaas is dit het ultieme gebrek aan abstractie; het maakt niet uit welke abstractie je kiest, je abstractie is alleen zinvol in de context van het probleem dat je probeert op te lossen, en als zodanig kun je nooit een consistente abstractie hebben die niet inconsistent wordt met onderliggende ideologieën als je ze gaat verlijmen samen zonder ze in te pakken in een weergave die consistent is met uw ideologie. Dit wetende, is de aanpak van python flexibeler omdat het goedkoper is, het is aan jou om meer te betalen om het er "leuker" uit te laten zien, hetzij door je eigen verpakking te maken, of je eigen preprocessor.


1
2018-05-07 19:32



- in "-". join (mijn_lijst) verklaart dat u naar een tekenreeks converteert van elementen uit een lijst. Het is resultaatgericht. (alleen voor eenvoudig geheugen en begrip)

Ik maak een volledige cheatheet van methods_of_string voor uw referentie.

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

1
2017-12-04 12:22