Seite 1 von 1
Gibt es sowas wie: str.split(sep, num, fill=None)?
Verfasst: Montag 5. März 2018, 13:29
von gotridofmyphone
Gibt es eine kürzere Lösung als
Code: Alles auswählen
if '#' in k:
k, idv = k.split("#", 2)
else:
idv = None
... die zudem noch skalabel ist, aber ohne an Verständlichkeit einzubüßen? Es wäre schön, wenn es so etwas gäbe wie
str.split(sep, num, fill=None), die den Wert fill verwendet, um ggf. eine Liste der Länge num aufzufüllen. Das real verfügbare
str.split(sep, maxnum) liefert höchstens maxnum Elemente zurück, aber ggf. weniger, was die Funktion für list assignment unbrauchbar macht. Man benötigt also immer eine temporäre benannte Liste. Es dürfte nicht allzu viel Aufwand sein, drumherum eine Schleife zu wickeln, habe ja schon leidliche Erfahrung mit der Auftrennung von Dateipfaden gemacht, das bei Python auch relativ umständlich ist. Aber dennoch, habe icih vielleicht was übersehen?
getridofmyphone
Re: Gibt es sowas wie: str.split(sep, num, fill=None)?
Verfasst: Montag 5. März 2018, 13:50
von __deets__
Zuerstmal ist dein code falsch - die Angabe des zweiten Parameters ist die Anzahl der gefundenen Trenner, nicht die Anzahl der erzeugten Segmente:
Code: Alles auswählen
>>> "a#b#c".split("#", 2)
['a', 'b', 'c']
>>> "a#b#c".split("#", 1)
['a', 'b#c']
In letzter Zeit hat Sirius3 hier die mir bis dato unbekannt partition-Methode erwaehnt, die liefert immer drei Werte zurueck:
Code: Alles auswählen
>>> "a#b#c".partition("#")
('a', '#', 'b#c')
>>> "a".partition("#")
('a', '', '')
Das geht ggf. etwas weiter in die Richtung die dir vorschwebt. Und last but not least: schreib dir eine Funktion die tut was du willst. Und benutz die.
Re: Gibt es sowas wie: str.split(sep, num, fill=None)?
Verfasst: Montag 5. März 2018, 14:29
von gotridofmyphone
Zuerstmal ist dein code falsch - die Angabe des zweiten Parameters ist die Anzahl der gefundenen Trenner, nicht die Anzahl der erzeugten Segmente:
Ah, danke. Den Fehler mache ich bestimmt noch einige Male, bevor ich mich an diesen Unterschied zu Perl gewöhnt habe.
Zum Tipp mit der eigenen Funktion: Schon klar, aber Funktionen, die der Sprache "innewohnende", mir vielleicht nur nicht bekannte Features implementieren, wären auch etwas unnötig, mindestens eine Fehlerquelle für sich. Aber wenn es dieses Feature nun mal nicht gibt, bleibt mir in dem Fall eben nichts anderes übrig. Danke.
partition() ist nicht das, was ich will. Es liefert alles vor dem Suchstring, den Suchstring selbst, und alles danach, egal ob und wie oft der Suchstring darin enthalten ist. Ein mögliches Einsatzszenario fällt mir übrigens nicht ein, schon gar nicht warum das wichtiger sein sollte als ein fill-Argument für str.split(), aber das ist wohl in meinem Horizont begründet. Wer ne Idee?
Re: Gibt es sowas wie: str.split(sep, num, fill=None)?
Verfasst: Montag 5. März 2018, 16:11
von gotridofmyphone
Hier mal einen Entwurf einer eigenen Funktion zwecks Codekritik:
Code: Alles auswählen
def ext_split (string, sep, num, fill=None):
if num:
num -= 1
if not num:
return (string,)
head, sep, tail = string.partition(sep)
return (head,) + (
ext_split(tail, sep, num, fill)
if sep
else (fill,) * num
)
else:
return tuple()
print( ext_split( 'a#b#c', '#', 2 ) ) # ('a', 'b#c')
print( ext_split( 'a#b#c', '#', 3 ) ) # ('a', 'b', 'c')
print( ext_split( 'a#b#c', '#', 4 ) ) # ('a', 'b', 'c', None)
print( ext_split( 'a#b#c', '#', 5 ) ) # ('a', 'b', 'c', None, None)
Re: Gibt es sowas wie: str.split(sep, num, fill=None)?
Verfasst: Montag 5. März 2018, 16:58
von __deets__
Wuerde ich mit itertools machen:
Code: Alles auswählen
from itertools import cycle, islice, chain
def ext_split(s, sep, num, fill=None):
return list(islice(chain(s.split(sep, num - 1), cycle([fill])), 0, num))
Das list kann man sich auch noch sparen, dann waere es lazy, je nach Anwendungszweck.
Re: Gibt es sowas wie: str.split(sep, num, fill=None)?
Verfasst: Montag 5. März 2018, 17:23
von narpfel
Die Iterator-Lösung von __deets__ ist zwar auch schön, aber ich würde in diesem Fall eher `list.extend` benutzen, weil `str.split` selbst keinen Iterator zurückgibt und man damit keine Lazyness gewonnen hat:
Code: Alles auswählen
def ext_split(s, sep, num, fill=None):
if num < 0:
raise ValueError("`num` must be non-negative")
parts = s.split(sep, num - 1)
parts.extend([fill] * (num - len(parts)))
# alternativ:
# parts.extend(repeat(fill, num - len(parts)))
return parts
@__deets__: Das `islice` mit dem `cycle([fill])` kann man einfacher durch `repeat` ausdrücken:
Code: Alles auswählen
def ext_split(s, sep, num, fill=None):
parts = s.split(sep, num - 1)
return chain(parts, repeat(fill, num - len(parts)))
@gotridofmyphone: Zu deinem Code: Ein Tupel ist in diesem Fall die falsche Datenstruktur. Tupel stehen semantisch für heterogene Daten, wobei jeder Index eine spezielle Bedeutung hat. Außerdem finde ich es
sehr unschön, fehlende Werte durch `None` zu ersetzen. Wenn man mit den Daten weiterarbeitet, muss man jedes mal auf `None` prüfen, bevor man irgendetwas damit macht.
Außerdem ist deine Leerzeichensetzung um Klammern nicht konsistent und entspricht nicht PEP8.
Re: Gibt es sowas wie: str.split(sep, num, fill=None)?
Verfasst: Montag 5. März 2018, 17:33
von __deets__
@narpfel: schoen mit repeat cycle und islice zu ersetzen!
Was die generelle Kritik angeht kommt es IMHO etwas auf den Kontext an. Wenn man mit None gleich sinnvolle defaults fuer sonst auftauchende Werte setzten kann, ist's ok. Aber allgemein hast du auch da recht - ich wuerde das so nicht machen.
Re: Gibt es sowas wie: str.split(sep, num, fill=None)?
Verfasst: Dienstag 6. März 2018, 11:51
von funkheld
Hab ihr schon einmal einen Geschwindigkeitsvergleich gemacht.
Es kommt ja nicht immer auf den schönen Code an.
Gruss
Re: Gibt es sowas wie: str.split(sep, num, fill=None)?
Verfasst: Dienstag 6. März 2018, 11:58
von __deets__
@funkheld: Geschwindigkeit schaut man sich an, wenn man ein Performanceproblem hat. Nicht vorher. "premature optimization is the root of all evil".
Selbst wenn mit einem micro-benchmark rauskaeme das zB meine Loesung 10-mal langsamer ist als eine andere, aber sie dafuer deutlich "schoener" ist (ohne da jetzt zu sehr ins Detail gehen zu wollen was das heissen kann) - dann spielt das erst eine Rolle, wenn die Funktion auch oft und wiederholt aufgerufen wird. Denn verstaendlicher Code, den du auch noch in 3 Wochen oder Monaten nachvollziehen kannst, ist immer wertvoller als super-duper-cleverer-optimierter-code, in den du dich einarbeiten musst, und dessen Erweiterbarkeit beschraenkt ist.
Re: Gibt es sowas wie: str.split(sep, num, fill=None)?
Verfasst: Dienstag 6. März 2018, 12:56
von gotridofmyphone
Außerdem finde ich es sehr unschön, fehlende Werte durch `None` zu ersetzen.
Kommt darauf an, was man will. Manchmal muss man sich eben entscheiden, welche Exception im konkreten Problemkontext sprechender ist. "ValueError: not enough values to unpack" oder z.B. "TypeError: unsupported operand type(s) for +=: 'NoneType' and 'int'". Okay, man mag einwenden, dass man spätestens, wenn man sich die Frage nach der richtigeren Exception stellt, am besten gleich die richtigste aller Exceptions implementiert. Codequalität ist für mich aber auch, so zu programmieren, dass etwaige Fehlermeldunge, die man nicht erwartet, einen mit der Nase auf die Ursache stupsen.
Re: Gibt es sowas wie: str.split(sep, num, fill=None)?
Verfasst: Dienstag 6. März 2018, 19:45
von narpfel
@gotridofmyphone: Es kommt nicht nur darauf an, welche Exception man haben möchte, sondern auch wann. Wenn du den `ValueError` beim Unpacking bekommst, dann weißt du genau, dass dein Programmierfehler in der Zeile liegt (oder beim Aufruf der Funktion mit falschen Parametern). Das Problem an `None` ist, dass es wie ein regulärer Wert durch’s halbe Programm gereicht kann und dann an einer komplett anderen Stelle zu einem `TypeError` führt. Aus dem Grund halte ich es generell für eine schlechte Idee, `None` übermäßig zu verwenden. Wenn man aber begründen kann, warum diese Art von Problem in einem konkreten Anwendungsfall nicht auftreten kann, kann es auch okay sein, wenn man `None` benutzt.
Zur Geschwindigkeitsmessung stimme ich __deets__ zu: Wenn man nicht messbar ein Performanceproblem hast, sollte man immer die am besten lesbare Version nehmen. Und wenn man ein Performanceproblem hat, dann ist die Wahrscheinlichkeit groß, dass eine Mikrooptimierung wie hier nicht genug Performance bringt und man sowieso einen anderen (besseren) Algorithmus nehmen muss.