reducex v1.0.0 -- reduce_str, pathreducer, split_path_at...

Code-Stücke können hier veröffentlicht werden.
sape
User
Beiträge: 1157
Registriert: Sonntag 3. September 2006, 12:52

Hier das zusammengefasste Modul:
reducex.py - 1.0.0

Beinhaltet einmal die allgemein gehaltene Version von mir, bei den man die delimiters selber bestimmten kann, und einmal die Version von Leonidas und BlackJack, die speziell nur für die Reduzierung von Pfadstrings gedacht sind.

Have Fun & lg
XtraNine

---

Hi, hier mal ein kleine Funktion. Ist nichts großes oder weltbewegendes ;) Die hatte ich mal gestern schnell geschrieben weil ich das für nen Pfad brauchte den ich reduzieren wollte. Hab die Funktion Allgemein gehalten damit auch nach anderen Zeichen reduziert werden kann :)

Hier das Script:

Code: Alles auswählen

def reduce_str(string, delimiters, reduce_range=1):
    cntr = 0
    rstr = ""
    for char in string[::-1]:
        for delimiter in delimiters:
            if char == delimiter:
                if cntr == (reduce_range - 1):
                    return [string[:-len(rstr) - 1], char + rstr[::-1]]
                else:
                    cntr += 1
        rstr += char
    return []
Hier mal ein Beispiel:

Code: Alles auswählen

[...]
test_path = "C:\Python\Lib\site-packages\wx-2.6-msw-unicode\wxPython"
print reduce_str(test_path, ['\\', '/',], 1)
print reduce_str(test_path, ['\\', '/',], 2)
print reduce_str(test_path, ['\\', '/',], 3)
[...]
Output:

Code: Alles auswählen

['C:\\Python\\Lib\\site-packages\\wx-2.6-msw-unicode', '\\wxPython']
['C:\\Python\\Lib\\site-packages', '\\wx-2.6-msw-unicode\\wxPython']
['C:\\Python\\Lib', '\\site-packages\\wx-2.6-msw-unicode\\wxPython']
Links ( [0] ) ist der reduzierte String und rechts der Teil der weggenommen wurde ( [1] ).

Wie man gut erkenne kann, bestimmt der Parameter reduce_range bis zur welchen Tiefe der String reduziert werden soll.

Der Parameter delimiters bekommt halt die Trennzeichen als liste. Damit wird bestimmt bei welchen Zeichen reduziert wird. Die liste kann natürlich so lang sein wie ihr wollt.

Have Fun. Vielleicht kann es ja jemand gebrauchen.

lg xtra

P.S.: Verbesserungsvorschläge sind gerne erwünscht. Soll heißen ihr könnte den Code gerne bis auf letzte sezieren :twisted:
BTW: Bin auch gleich bei mal andere Varianten zu versuchen udn sie auf ihr Laufzeit hin zu testen xD ;)
Zuletzt geändert von sape am Montag 20. November 2006, 00:28, insgesamt 2-mal geändert.
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Ich habe mal so zum Spass eine rekursive Funktion gemacht, die etwas ähnliches tut:

Code: Alles auswählen

import os.path
test_path = "C:\Python\Lib\site-packages\wx-2.6-msw-unicode\wxPython"

def reduce_str(path_remaining, reduce_range=1):
    if reduce_range == 0:
        return path_remaining
    else:
        path_remaining = os.path.split(path_remaining)[0]
        path_remaining = reduce_str(path_remaining, reduce_range - 1)
        
    return path_remaining

print reduce_str(test_path, 1)
print reduce_str(test_path, 2)
print reduce_str(test_path, 3)
Den Rest zeigt sie im Moment nicht an, aber das kommt noch, wenn ich Zeit und Lust habe ;)
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Ok, nun eine komplettere Variante:

Code: Alles auswählen

import os
test_path = "C:\Python\Lib\site-packages\wx-2.6-msw-unicode\wxPython"

def reducer(path_remaining, reduce_range=1, tails=[]):
    if reduce_range == 0:
        return path_remaining, tails
    else:
        path_remaining, tail = os.path.split(path_remaining)
        tails.append(tail)
        path_remaining, tails = reducer(path_remaining, reduce_range - 1, tails)
        return path_remaining, tails

def reduce_str(path_remaining, reduce_range):
    head, tail = reducer(path_remaining, reduce_range)
    return [head, os.sep.join(tail)]

print reduce_str(test_path, 1)
print reduce_str(test_path, 2)
print reduce_str(test_path, 3)
Ausgabe wäre dann:

Code: Alles auswählen

['C:\\Python\\Lib\\site-packages\\wx-2.6-msw-unicode', 'wxPython']
['C:\\Python\\Lib\\site-packages', 'wxPython\\wxPython\\wx-2.6-msw-unicode']
['C:\\Python\\Lib', 'wxPython\\wxPython\\wx-2.6-msw-unicode\\wxPython\\wx-2.6-msw-unicode\\site-packages']
was man auch wunderbar wieder mit os.path.join wieder zusammenbringen kann.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Hier noch eine weniger verrückte, viel klarere Variante:

Code: Alles auswählen

import os
test_path = "C:\Python\Lib\site-packages\wx-2.6-msw-unicode\wxPython" 

def reduce_str(path_remaining, reduce_range):
    chunks = path_remaining.rsplit(os.sep, reduce_range)
    return [chunks[0], os.sep.join(chunks[1:])]

print reduce_str(test_path, 1)
print reduce_str(test_path, 2)
print reduce_str(test_path, 3)
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
sape
User
Beiträge: 1157
Registriert: Sonntag 3. September 2006, 12:52

Deine 2te Variante Funktioniert nicht, weil sie mehrmals schon entfernte teile hinzufügt, wie man am Output erkennt:

Code: Alles auswählen

['C:\\Python\\Lib', 'wxPython\\wxPython\\wx-2.6-msw-unicode\\wxPython\\wx-2.6-msw-unicode\\site-packages']
...

Deine 3te Variante ist aber WOW :shock:. Mein lieber man nicht schlecht. Ingrunde sogar ein Einzeiler, wenn man den Wert von chunks gleich mit ins return packt!

OK, man kann damit nicht nach Selbstdefinierten Separatoren Splitten, aber für reine Strings die einen Pfad enthalten, die beste Variante! :)
Ja, das kann ich garnicht toppen ;) ^^

Deine Funktion sollte aber einen eigenen Name bekommen: reduce_pathstr() oder so.

lg

P.S.: Dan versuche ich mal was mit re zu machen ^^
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

XtraNine hat geschrieben:Deine 2te Variante Funktioniert nicht, weil sie mehrmals schon entfernte teile hinzufügt, wie man am Output erkennt:

Code: Alles auswählen

['C:\\Python\\Lib', 'wxPython\\wxPython\\wx-2.6-msw-unicode\\wxPython\\wx-2.6-msw-unicode\\site-packages']
Oh, hups, sorry. Werds korrigieren.
XtraNine hat geschrieben:Deine 3te Variante ist aber WOW :shock:. Mein lieber man nicht schlecht. Ingrunde sogar ein Einzeiler, wenn man den Wert von chunks gleich mit ins return packt!
Stimmt - sollte man aber besser nicht machen, Obfuscated Python lässt grüßen.
XtraNine hat geschrieben:Deine Funktion sollte aber einen eigenen Name bekommen: reduce_pathstr() oder so.
Ursprünglich hieß sie reduce_simple() und ich hab ein reduce_str = reduce_simple gemacht, weil ich alle Funktionen in einer Datei habe. Habs dann nur fürs Forum geändert.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

XtraNine hat geschrieben:Deine 2te Variante Funktioniert nicht, weil sie mehrmals schon entfernte teile hinzufügt, wie man am Output erkennt
Wie gesagt, du hast recht. Hat etwas gedauert, bis ich verstanden habe warum - Objekte die im Funktionskopf definiert werden, werden nur einmal erstellt und hager hat tails seinen wert nach mehrmaligen Aufruf der Funktion behalten - man hat es aber nur bemerkt, wenn man die Funktion mehrmals aufgerufen hat.

Ich habe das jetzt mal ein wenig umgeschrieben, um auf eine Helfer-Funktion verzichten zu können:

Code: Alles auswählen

import os
test_path = "C:\Python\Lib\site-packages\wx-2.6-msw-unicode\wxPython"

def pathreducer(path_remaining, reduce_range=1, tails=None):
    """Splits a path in a recursive way"""
    if not tails:
        tails = str()
    
    if reduce_range == 0:
        return [path_remaining, tails]
    else:
        path_remaining, tail = os.path.split(path_remaining)
        tails = os.path.join(tails, tail)
        path_remaining, tails = pathreducer(path_remaining, reduce_range - 1, tails)
        return [path_remaining, tails]

print pathreducer(test_path, 1)
print pathreducer(test_path, 2)
print pathreducer(test_path, 3)
Die Ausgabe ist nun auch korrekt:

Code: Alles auswählen

['C:\\Python\\Lib\\site-packages\\wx-2.6-msw-unicode', 'wxPython']
['C:\\Python\\Lib\\site-packages', 'wxPython\\wx-2.6-msw-unicode']
['C:\\Python\\Lib', 'wxPython\\wx-2.6-msw-unicode\\site-packages']
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
sape
User
Beiträge: 1157
Registriert: Sonntag 3. September 2006, 12:52

Leonidas hat geschrieben:Objekte die im Funktionskopf definiert werden, werden nur einmal erstellt und hager hat tails seinen wert nach mehrmaligen Aufruf der Funktion behalten - man hat es aber nur bemerkt, wenn man die Funktion mehrmals aufgerufen hat.
[...]
Oh, daran lag es also (wusste ich ja noch nicht). Ich hatte bis eben auch nicht begriffen woran es lag.
Ist aber auch ne schöne Variante. Die 3te von dir gefällt mir aber bis her am besten weil sie so kurz ist . Die werde ich mir in meine Library packen :)

...


Hab hier ne dreckige Variante ^^, die aber ziemlich strange ist. Irgendwie verstehe ich noch nicht weshalb da eine "Falsche" Ausgabe kommt.

Code: Alles auswählen

import re
import os
def reduce_str2(string, reduce_range=1):
    tokens = re.split("(\%s)" % os.sep, string[::-1], reduce_range)
    tokens = tokens[::-1]
    reduced_str = ""
    rstr = ""
    
    for i in range(reduce_range+1):
        reduced_str += tokens[i][::-1]
        rstr += tokens[-(i+1)]

    return [reduced_str, rstr[::-1]]

print reduce_str2(test_path, 1)
print reduce_str2(test_path, 2)
print reduce_str2(test_path, 3)

Code: Alles auswählen

['C:\\Python\\Lib\\site-packages\\wx-2.6-msw-unicode\\', '\\wxPython']
['C:\\Python\\Lib\\site-packages\\wx-2.6-msw-unicode', 'wx-2.6-msw-unicode\\wxPython']
['C:\\Python\\Lib\\site-packages\\', '\\wx-2.6-msw-unicode\\wxPython']
['C:\\Python\\Lib\\site-packages', 'site-packages\\wx-2.6-msw-unicode\\wxPython']

Beim 2ten sollte eigentlich das "wx-2.6-msw-unicode" Links weg sein und auch das site-packages beim 3ten. Scheint bei allen geraden Zahlen zu sein ^^ Muss ich mal gleich näher kucken.

lg
sape
User
Beiträge: 1157
Registriert: Sonntag 3. September 2006, 12:52

Mir fällt auf, das wir eigentlich mal eine Spiele-thread aufmachen sollten :D
Jemand gibt ne Funktion oder irgendwas vor und jeder postet seine Varianten. Da kommen bestimmt witzige und seltsame Varianten zu Stande ^^

lg
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

XtraNine hat geschrieben:P.S.: Dan versuche ich mal was mit re zu machen ^^
Mit regulären Ausdrücken lässt sich da höchstwarscheinlich nichts vernünftiges machen.

Etwas unvernünfiges:

Code: Alles auswählen

import re, os

test_path = "C:\Python\Lib\site-packages\wx-2.6-msw-unicode\wxPython"

def reduce_re(path_remaining, reduce_range):
    splitby = re.escape(os.sep)
    chunks = [element[::-1] for element in re.split(splitby, path_remaining[::-1], maxsplit=reduce_range)][::-1]
    return [chunks[0], os.sep.join(chunks[1:])]

pathreducer = reduce_re

print pathreducer(test_path, 1)
print pathreducer(test_path, 2)
print pathreducer(test_path, 3)
Man sieht, es ist im Grunde nur ein sehr umständlich geschriebenes .rsplit(). Das scheint wohl einer der Fälle zu sein, die sich mit regulären Ausdrücken nicht lösen lassen.

Aber vielleicht werden irgendwann WoNáDos Turing-vollständige reguläre Ausdrücke auch das ermöglichen ;)
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
BlackJack

Selbst an `os.path.sep` zu splitten hat den Nachteil, dass man damit nicht alle gültigen Pfade korrekt verarbeiten kann. `os.path.split` kommt auch damit klar wenn der Pfad `os.path.sep` und `os.path.altsep` gemischt enthält und auch wenn mehrere Pfadtrenner direkt aufeinander folgen.

Hier ist noch'ne rekursive Variante:

Code: Alles auswählen

import os

def split_path_at(path, position, accumulated_tail=''):
    if position > 0:
        path, tail = os.path.split(path)
        return split_path_at(path,
                             position - 1,
                             os.path.join(tail, accumulated_tail))
    else:
        return (path, accumulated_tail)

def main():
    test_path = '/usr/lib/python2.4//site-packages/wx-2.6-gtk2-unicode'
    for i in xrange(1, 6):
        print split_path_at(test_path, i)
lunar

XtraNine hat geschrieben:Mir fällt auf, das wir eigentlich mal eine Spiele-thread aufmachen sollten :D
Jemand gibt ne Funktion oder irgendwas vor und jeder postet seine Varianten. Da kommen bestimmt witzige und seltsame Varianten zu Stande ^^
Klar, aber bitte unter der Bedingung, dass selbst für einfachste Dinge mindestens 20 Zeilen verwendet werden und das ganze so unlesbar wie möglich sein soll ;)
sape
User
Beiträge: 1157
Registriert: Sonntag 3. September 2006, 12:52

Leonidas hat geschrieben: Man sieht, es ist im Grunde nur ein sehr umständlich geschriebenes .rsplit(). Das scheint wohl einer der Fälle zu sein, die sich mit regulären Ausdrücken nicht lösen lassen.
Ja, stimmt schon.
Aber vielleicht werden irgendwann WoNáDos Turing-vollständige reguläre Ausdrücke auch das ermöglichen ;)
Was sind den WoNáDos Turing :?

BTW: Aber Leo und Jack ich muss ja echt sagen, ihr habt es drauf Komplizierten Code zu schreiben ;) Besonders das mit den Funktionalen teilen irritiert mich ab und an. Aber ich
hab mich damit letzte Woche ein wenig beschäftigt und versteh mittlerweile ein wenig den Aufbau von LC und so. Aber, ich finde das trägt nicht immer zur Lesbarkeit bei sondern eher zum Gegenteil, wenn man es zu ausgiebig nutzt. Naja über regExp braucht man eigentlich gar nicht anfangen zu reden. Ist immer sehr schwer zu lesen (Einer der gründe warum ich mit Perl nicht mehr scripten wollte, weil es gerade zu verleitet das für alles Mögliche zu nutzen weil man eben alles dadurch sehr kürzen kann ^^)
lunar hat geschrieben: Klar, aber bitte unter der Bedingung, dass selbst für einfachste Dinge mindestens 20 Zeilen verwendet werden und das ganze so unlesbar wie möglich sein soll ;)
Hehe, an genau sowas hatte ich gedacht ^^
sape
User
Beiträge: 1157
Registriert: Sonntag 3. September 2006, 12:52

Oh ok. Google hat mir die Antwort in 0.006 secs gegeben.
WoNaDos ist ein User im Ruby forum.
http://www.rubyforen.de/viewtopic.php?t=901

Hab aml kurz durchgescrolt und muss dazu sagen OMG. Naja, wers lesen kann, ich kanns nicht ^^ Ist mir zuviel RegExp
BlackJack

@Leonidas: Ich glaube Du hast vergessen `os.path.sep` "umzudrehen", könnte ja sein, das der Pfadtrenner '-=*>' lautet. ;-)

Hier eine hässliche `re`-Lösung von mir:

Code: Alles auswählen

def split_path_at_re(path, position):
    split_re = re.escape(os.path.sep)
    if os.path.altsep:
        split_re += '|' + re.escape(os.path.sep)
    split_re = '(%s)+' % split_re
    chunks = re.split(split_re, path)[::2]
    return (os.path.join(*chunks[:-position]),
            os.path.join(*chunks[-position:]))
Kommt mit beiden Pfadtrennern zurecht und auch mit Wiederholungen. Aber irgendwas wichtiges habe ich sicher vergessen.
sape
User
Beiträge: 1157
Registriert: Sonntag 3. September 2006, 12:52

BlackJack hat geschrieben:Selbst an `os.path.sep` zu splitten hat den Nachteil, dass man damit nicht alle gültigen Pfade korrekt verarbeiten kann. `os.path.split` kommt auch damit klar wenn der Pfad `os.path.sep` und `os.path.altsep` gemischt enthält und auch wenn mehrere Pfadtrenner direkt aufeinander folgen.
BlackJack hat geschrieben:[...]
Kommt mit beiden Pfadtrennern zurecht und auch mit Wiederholungen. Aber irgendwas wichtiges habe ich sicher vergessen.
Irgendwie verstehe ich nicht recht was du meinst: Warum kommt os.sep nicht mit allen Pfaden klar? Was meinst du mit gemischten Pfaden?
EDIT: Bei mir funktioniert os.sep oder ich übersehe irgendwas wichtiges.
BlackJack

Unter Windows kannst Du zum Beispiel den Pfad hier angeben:

r'c:\tmp/bla\\test//spam\\\viking///eric'

Das sind sechs Komponenten mit zwei verschiedenen Pfadtrennern. Und wenn man einen Pfadtrenner mehrfach wiederholt, dann zählt der nur einmal. Das berücksichtigt `os.path.split()` alles. Einfach an r'\' splitten bringt in diesem Fall ein falsches Ergebnis.
sape
User
Beiträge: 1157
Registriert: Sonntag 3. September 2006, 12:52

Achso, du meinst die ganze zeit folgende Zeile:

Code: Alles auswählen

path_remaining.rsplit(os.sep, reduce_range) 
OK, stimmt, rsplit würde dann nur nach dem Separator im os.sep den Pfad splitten.
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

XtraNine hat geschrieben:WoNaDos ist ein User im Ruby forum.
WoNáDo ist auch in diesem Forum.

Und was Turing-Vollständigkeit heißt wurde im Ruby-Forum auch schon diskutiert.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
sape
User
Beiträge: 1157
Registriert: Sonntag 3. September 2006, 12:52

Hi.

Ich hab mal alles in einem Modul zusammengefasst mit Dokumentation. (Docstrings wie gehabt für epydoc mit Markup).

Hab das Modul reducex.py genant. Das x soll für alle möglichen Datentypen stehen. Momentan sind ja nur 3 Funktionen vorhanden die Strings reduzieren. Mit reinkomme soll aber auch was für integer usw. Daher reducex.

Link: reducex.py - 1.0.0

EDIT: Hab das mal im ersten Post hinzugefügt und die Headline vom Thread angepasst.
Antworten