wie Argumente weiterreichen in 'key'-funktion ohne global

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
karolus
User
Beiträge: 144
Registriert: Samstag 22. August 2009, 22:34

Hallo
Ich steh hier gerade etwas auf dem Schlauch..
Ich habe:

Code: Alles auswählen

import re
...
numrex = re.compile(r'\d+')

def main_sort( rrange, args):
    """

    """
    for arg in args[::-1]:
        try:
            global field
            global flags
            field, flags = int(arg[0]), arg[1]
            if not flags:
                flags = 'n'
            rrange.sort(key=naturalkey, reverse=('r' in flags ) )
        except Exception as ll:
            print '%s'%ll
    return rrange

def naturalkey( key):
    key = key[field]
    if isinstance(key, unicode):
        if not 's' in flags:
            key = key.lower()
        if 'n' in flags: # http://code.activestate.com/recipes/285264-natural-string-sorting/#c7
            return numrex.sub(lambda m:'%03d%d' %(len(m.group()), m.group()), key)
        return key
    else:
        if 't' in flags:
            return ('zzzzzzz', key)
        return key
Wie kann ich oben in der Sortierroutine die Argumente 'field' und 'flags' an naturalkey weiterreichen, ohne die Argumente global zu setzen bzw. wie löst man das ohne global

***Ich weiss , es ginge per

Code: Alles auswählen

...
rrange.sort(key=lamda k : k[field].lower() if blah else ...., reverse=('r' in flags ) )
...
aber der lambda-ausdruck wird mir etwas unübersichtlich.

Karolus
Zuletzt geändert von karolus am Dienstag 30. Oktober 2012, 17:03, insgesamt 1-mal geändert.
Benutzeravatar
jbs
User
Beiträge: 953
Registriert: Mittwoch 24. Juni 2009, 13:13
Wohnort: Postdam

Es gibt functools.partial, aber dein Code, insbesondere die globals sehen sehr unschön aus.
[url=http://wiki.python-forum.de/PEP%208%20%28%C3%9Cbersetzung%29]PEP 8[/url] - Quak!
[url=http://tutorial.pocoo.org/index.html]Tutorial in Deutsch[/url]
karolus
User
Beiträge: 144
Registriert: Samstag 22. August 2009, 22:34

Hallo
jbs hat geschrieben:Es gibt functools.partial, aber dein Code, insbesondere die globals sehen sehr unschön aus.
Ja ich weiss, die globals sind jetzt verschwunden, an der restlichen "Schönheit" arbeite ich noch.

Code: Alles auswählen

import re
from functools import partial

def main_sort( rrange, args):
    """

    """
    for arg in args[::-1]:
        try:

            field, flags = int(arg[0]), arg[1]
            if not flags:
                flags = 'n' 
            rrange.sort(key=partial(naturalkey,field, flags), reverse=('r' in flags ) )
        except Exception as error:
            return '%s'% error
    return rrange

def naturalkey(field, flags, key):
    key = key[field]
    if isinstance(key, unicode):
        if not 's' in flags:
            key = key.lower()
        if 'n' in flags: 
            return numrex.sub(lambda m:'%03d%s' %(len(m.group()), m.group()), key)
        return key
    else:
        if 't' in flags: # t : for Textfirst
            return ('zzzzzzz', key)
        return key
Das Ganze sah ursprünglich noch viel hässlicher aus, siehe hier

Karolus
Benutzeravatar
jbs
User
Beiträge: 953
Registriert: Mittwoch 24. Juni 2009, 13:13
Wohnort: Postdam

Was probierst du denn *eigentlich*?
[url=http://wiki.python-forum.de/PEP%208%20%28%C3%9Cbersetzung%29]PEP 8[/url] - Quak!
[url=http://tutorial.pocoo.org/index.html]Tutorial in Deutsch[/url]
BlackJack

@karolus: Was ich daran besonders unschön finde, ist dass `naturalkey()` in Abhängigkeit von `flags` recht unterschiedliche Dinge tut und diese immer wieder selben Entscheidungen für jedes Element aufs neue trifft, obwohl es stets im gleichen Zweig landet. Ich würde diese Entscheidungen vor dem Sortieren in einer Funktion zusammenfassen, also eine Funktion, welche `field` und `flags` übergeben bekommt und eine passende `key`-Funktion dazu zurück gibt.

In `naturalkey()` so wie es jetzt ist, könnte man mal aufräumen. Nur ein ``return key`` am Ende und der `else`-Zweig dort kann ersatzlos wegfallen. Da wird nichts gemacht was am Ergebnis etwas ändern würde.

Statt ``not a in b`` sollte man ``a not in b`` schreiben um einen Operator einzusparen. ``not in`` ist *ein* Operator.

Anstelle von ``sequence[::-1]`` würde ich ``reversed(sequence)`` verwenden. Das Funktioniert bei jedem endlichem, iterierbaren Objekt und legt im Fall von Listen zum Beispiel auch keine Kopie der Liste an.

Der Rückgabewert von `main_sort()` ist entweder eine Liste oder eine Zeichenkette mit einem Fehler — das ist genau die Art von unschöner API die man durch Ausnahmen unnötig macht. Und Du wandelst Ausnahmen wieder in diesen Problemfall zurück.
karolus
User
Beiträge: 144
Registriert: Samstag 22. August 2009, 22:34

Hallo
@jbs
das ist Teil einer Addin-funktion für Calc (Libre-office/ AOo und Derivate)
Offensichtlich dient sie zur "sortierten" Ausgabe eines Zellbereichs.
BlackJack hat geschrieben:@karolus: Was ich daran besonders unschön finde, ist dass `naturalkey()` in Abhängigkeit von `flags` recht unterschiedliche Dinge tut und diese immer wieder selben Entscheidungen für jedes Element aufs neue trifft, obwohl es stets im gleichen Zweig landet. Ich würde diese Entscheidungen vor dem Sortieren in einer Funktion zusammenfassen, also eine Funktion, welche `field` und `flags` übergeben bekommt und eine passende `key`-Funktion dazu zurück gibt.
Ja, ich kann den key in Reihenfolge der Priorität auf einmal in 'key= ..' übergeben - aber dann gilt die festgelegte Sortierreihenfolge 'reverse=...' für den ganzen Schlüssel !?***
in der Version oben kann man eine auf-/absteigende Sortierung für jedes Schlüsselelement individuell festlegen.
BlackJack hat geschrieben:In `naturalkey()` so wie es jetzt ist, könnte man mal aufräumen. Nur ein ``return key`` am Ende und der `else`-Zweig dort kann ersatzlos wegfallen. Da wird nichts gemacht was am Ergebnis etwas ändern würde.
Im `else` Zweig wird schon noch etwas gemacht, es wird entschieden ob Zahlen vor oder nach Text eingeordnet werden ( in Abhängigkeit von Flag: 't' )
BlackJack hat geschrieben:Statt ``not a in b`` sollte man ``a not in b`` schreiben um einen Operator einzusparen. ``not in`` ist *ein* Operator.

Anstelle von ``sequence[::-1]`` würde ich ``reversed(sequence)`` verwenden. Das Funktioniert bei jedem endlichem, iterierbaren Objekt und legt im Fall von Listen zum Beispiel auch keine Kopie der Liste an.

Der Rückgabewert von `main_sort()` ist entweder eine Liste oder eine Zeichenkette mit einem Fehler — das ist genau die Art von unschöner API die man durch Ausnahmen unnötig macht. Und Du wandelst Ausnahmen wieder in diesen Problemfall zurück.
'not a in b' und x[::-1] sind schlechte Angewohnheiten - werde ich ändern, statt der Fehlerrückgabe war da vorher ein 'print '%s' %error , um beim Testen mal ein paar Infos in der Konsole zu bekommen.


***[edit]: Könnte man einzelne Schlüsselfelder so "invertieren" das eine umgekehrte Sortierreihenfolge erreicht wird ???
[/edit]
Karolus
BlackJack

@karolus: Du hast meine Anmerkung mit `naturalkey()` und den `flags` anscheinend nicht richtig verstanden. Es ging nicht darum die Schleife loszuwerden, sondern dass für *einen* Sortiervorgang für jedes Element aufs neue Anhand von `flags` entschieden wird was zu tun ist. Diese Entscheidung ist aber innerhalb eines Sortiervorgangs für *alle* Elemente *gleich*.

Was das Flag 't' angeht, sehe ich nicht wie das hier zuverlässig erreicht wird. Damit kannst Du zwar „entscheiden” ob nicht-`unicode`-Objekte vor oder nach `unicode`-Objekten einsortiert werden, aber Du kannst nicht entscheiden was das 't' bedeutet. Denn wenn man verschiedene Typen auf kleiner/grösser vergleicht, dann garantiert Python nur, dass es innerhalb eines Programmlaufs konsistent passiert, aber nicht welche Werte welchen Typs als grösser oder als kleiner angesehen werden. Wenn das bisher immer das gemacht hat, was Du wolltest, hattest Du einfach Glück. Das kann mit einer anderen Python-Version anders herum ausgehen, und theoretisch sogar von einem Programmlauf zum nächsten. Das erste Element bei dem Tupel ist dazu auch noch völlig egal. Man könnte es sogar weglassen.

Ungetestet:

Code: Alles auswählen

import re
from functools import partial
from operator import methodcaller


NUMBER_RE = re.compile(r'\d+')


def make_key_func(index, flags):
    
    def key_func(item):
        key = item[index]
        if isinstance(key, unicode):
            for function in functions:
                key = function(key)
            return (0, key)
        else:
            return (number_sort_key, key)
    
    number_sort_key = -1 if 't' in flags else 1
    functions = list()
    if 's' not in flags:
        functions.append(methodcaller('lower'))
    if 'n' in flags:
        functions.append(
            partial(
                NUMBER_RE.sub, lambda m: '%03d%d' % (len(m.group()), m.group())
            )
        )
    return key_func


def main_sort(items, sort_specs):
    for index, flags in reversed(sort_specs):
        items.sort(key=make_key_func(index, flags or 'n'), reverse='r' in flags)
karolus
User
Beiträge: 144
Registriert: Samstag 22. August 2009, 22:34

Hallo

Puh ... so langsam versteh ich die Abläufe in deinem Code.
Was das Flag 't' angeht, sehe ich nicht wie das hier zuverlässig erreicht wird. Damit kannst Du zwar „entscheiden” ob nicht-`unicode`-Objekte vor oder nach `unicode`-Objekten einsortiert werden, aber Du kannst nicht entscheiden was das 't' bedeutet. Denn wenn man verschiedene Typen auf kleiner/grösser vergleicht, dann garantiert Python nur, dass es innerhalb eines Programmlaufs konsistent passiert, aber nicht welche Werte welchen Typs als grösser oder als kleiner angesehen werden. Wenn das bisher immer das gemacht hat, was Du wolltest, hattest Du einfach Glück.
Naja, ich mach das konkret das erste mal und habe naiv angenommen das beim Vergleich von "mehrelementigen" Tuplen mit numerischen Typen oder Unicode, primär das erste Element herangezogen wird .. usw. falls nötig.

Hier nochmal das ganze Pythonmodul :

Code: Alles auswählen


import unohelper
import locale
import re
from functools import partial
from operator import methodcaller
from com.capylibre.sortaddin import XSortArray


# SortArray Calc Add-in implementation.

NUMBER_RE = re.compile(r'[1-9]\d*')

class NaturalSort( unohelper.Base, XSortArray ):
    def __init__( self, ctx ):
        self.ctx = ctx
        locale.setlocale(locale.LC_ALL, "")



    def sortieren(self, inRange, fieldspecs , HasHeaders=False , ByRows=False):
        """
        Functionname : SORTIEREN
        Arguments:
        InRange : The Range to Sort
        fieldspecs: List of 2 Columns, with Fieldnumber left ,and any
                Combination of Flags s, r, t, n on the Right Side
        HasHeaders: if True skip First Row/Column as header
        ByRows : if True sorts left to Right, otherwise Top to Bottom
        """
        inRange = list(inRange)
        out = []
        if ByRows:
            inRange = zip(*inRange)
        if HasHeaders:
            out.append(inRange[0])
            inRange = inRange[1:]


        sortrange = main_sort(inRange,fieldspecs)
        out.extend(sortrange)

        if ByRows:
            return tuple(zip(*out))

        return tuple(out)




def make_key_func(index, flags):

    def key_func(item):
        key = item[index]
        if isinstance(key, unicode):
            for function in functions:
                key = function(key)
            return (0, key)
        else:
            return (number_sort_key, key)

    number_sort_key = -1 if 't' in flags else 1
    functions = list()
    if 's' not in flags:
        functions.append(methodcaller('lower'))
    if 'n' in flags:
        functions.append(
            partial(
                NUMBER_RE.sub, lambda m: '%03d%s' % (len(m.group()), m.group())
            )
        )
    functions.append(methodcaller('encode','utf8'))
    functions.append(locale.strxfrm)

    return key_func


def main_sort(items, sort_specs):
    for index, flags in reversed(sort_specs):
        index = int(index)
        items.sort(key=make_key_func(index, flags or 'n'), reverse='r' in flags)
    return items



def createInstance( ctx ):
    return NaturalSort( ctx )

g_ImplementationHelper = unohelper.ImplementationHelper()
g_ImplementationHelper.addImplementation(
    createInstance,"com.capylibre.sortaddin.python.NaturalSort",("com.sun.star.sheet.AddIn",),)

Karolus
Antworten