Seite 1 von 1
wie Argumente weiterreichen in 'key'-funktion ohne global
Verfasst: Dienstag 30. Oktober 2012, 16:57
von karolus
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
Re: wie Argumente weiterreichen in 'key'-funktion ohne globa
Verfasst: Dienstag 30. Oktober 2012, 17:03
von jbs
Es gibt functools.partial, aber dein Code, insbesondere die globals sehen sehr unschön aus.
Re: wie Argumente weiterreichen in 'key'-funktion ohne globa
Verfasst: Dienstag 30. Oktober 2012, 20:07
von karolus
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
Re: wie Argumente weiterreichen in 'key'-funktion ohne globa
Verfasst: Dienstag 30. Oktober 2012, 20:15
von jbs
Was probierst du denn *eigentlich*?
Re: wie Argumente weiterreichen in 'key'-funktion ohne globa
Verfasst: Dienstag 30. Oktober 2012, 22:10
von 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.
Re: wie Argumente weiterreichen in 'key'-funktion ohne globa
Verfasst: Mittwoch 31. Oktober 2012, 15:41
von karolus
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
Re: wie Argumente weiterreichen in 'key'-funktion ohne globa
Verfasst: Mittwoch 31. Oktober 2012, 18:40
von 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)
Re: wie Argumente weiterreichen in 'key'-funktion ohne globa
Verfasst: Mittwoch 31. Oktober 2012, 22:31
von karolus
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