Wieder mal die Frage: "Geht's besser?"

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
mutetella
User
Beiträge: 1695
Registriert: Donnerstag 5. März 2009, 17:10
Kontaktdaten:

Hallo,

nachfolgender Code generiert datetime-objecte. Hier ein paar Beispiele:

Code: Alles auswählen

In [263]: parse.generate_date('2012.9.16')
Out[263]: datetime.datetime(2012, 9, 16, 0, 0)

In [264]: parse.generate_date('10-3')
Out[264]: datetime.datetime(2012, 10, 3, 0, 0)

In [265]: parse.generate_date('30')
Out[265]: datetime.datetime(2012, 8, 30, 0, 0)

Code: Alles auswählen

import re
import datetime

FORMAT = 'ymd'
CHANGE_SHORT_YEAR = True

def generate_date(date_args, formatstring=None):
    '''generate_date(date_args[, formatdate_args]) -> datetime-object
    
    Extract potential date digits from 'date_args' and return
    a valid datetime-object or raise a ValueError.

    'date_args' must be a date_args with 1, 2 or 3 digit(s) seperated
    by any char. These digit(s) represents year, month and day 
    in order of the given 'formatdate_args' or by 'parse.FORMAT' 
    by default.

    If one or more digit(s) are not given it will be replaced
    with the equivalent of the current day.
    Note that 2 digits are always interpreted as day with month,
    1 digit always as day.

    'formatdate_args' specified the order in which the date digits
    are contained in 'date_args'.

    If 'parse.CHANGE_SHORT_YEAR' is True, a year-number larger
    then 100 will be interpreted as a 21 century year, e. g. 
    the year 13 will be returned as year 2013.'''

    if formatstring is None:
        formatstring = FORMAT
    date_args = re.findall('\d+', date_args)
    arg_count = len(date_args)
    year, month, day = datetime.datetime.now().timetuple()[0:3]
    if arg_count == 3:
        # positions of year, month, date
        y_idx, m_idx, d_idx = formatcode_positions(formatstring, 'ymd')
        year = int(date_args[y_idx])
        if CHANGE_SHORT_YEAR and year < 100:
            year += 2000
        month = int(date_args[m_idx])
        day = int(date_args[d_idx])
    elif arg_count == 2:
        # positions of month and date if no year is given
        m_idx, d_idx = formatcode_positions(formatstring, 'md')
        month = int(date_args[m_idx])
        day = int(date_args[d_idx])
    else:
        day = int(date_args[0])
    try:
        return datetime.datetime(year, month, day)
    except ValueError:
        raise ValueError('{0}(y), {1}(m), {2}(d) seems not to be '
                         'a valid date.'.format(year, month, day))
        

def formatcode_positions(formatstring, formatcodes):
    formatcodes = formatcodes.upper()
    _ = [f.upper() for f in re.findall('[{0}]'.format(formatcodes),
                                       formatstring, re.I)]
    return [_.index(code) for code in formatcodes]

Was mich allerdings nicht so ganz glücklich macht ist diese 'arg_count'-Sache. Ich stelle mir da eher sowas wie ein template vor, das mit default-Werten gefüllt ist und je nach übergebenen year-, month- und day-Werten bestückt wird.
Die Hürde, über die ich nicht komme ist die veränderliche Reihenfolge der Werte.

Wäre für jegliche Inspiration sehr dankbar!

mutetella
Entspanne dich und wisse, dass es Zeit für alles gibt. (YogiTea Teebeutel Weisheit ;-) )
deets

Reaktion der ersten Sekund: _ verwendet man fuer Bezeichner, die man *nicht* benoetigt. Das solltest du also aendern.
mutetella
User
Beiträge: 1695
Registriert: Donnerstag 5. März 2009, 17:10
Kontaktdaten:

@deets:
Ich verwende den _ immer gern für so Sachen, für die es sich nicht lohnt, einen vernünftigen Namen zu finden... Manchmal kommt es mir so vor, als würde mich die Suche nach gescheiten Namen mehr Zeit als der Code selbst kosten... :(

Was meinst Du mit "nicht benötigt"? Ist nicht das so ein Fall? Ein Bezeichner für eine Liste, die man ja eigentlich nicht benötigt. Oder wieviel mehr kann man etwas nicht benötigen (ernst gemeinte Frage!)?

mutetella
Entspanne dich und wisse, dass es Zeit für alles gibt. (YogiTea Teebeutel Weisheit ;-) )
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

mutetella hat geschrieben:Ich verwende den _ immer gern für so Sachen, für die es sich nicht lohnt, einen vernünftigen Namen zu finden... Manchmal kommt es mir so vor, als würde mich die Suche nach gescheiten Namen mehr Zeit als der Code selbst kosten... :(
Wenn du Dingen keinen vernünftigen Namen geben kannst, dann ist das häufig ein Hinweis auf einen Fehler. Das kann alle möglichen Gründe haben, wie ein nicht durchdachtes Konzept, ein unnötiger Schritt oder auch zu viele Schritte auf einmal.
mutetella hat geschrieben:Was meinst Du mit "nicht benötigt"? Ist nicht das so ein Fall? Ein Bezeichner für eine Liste, die man ja eigentlich nicht benötigt. Oder wieviel mehr kann man etwas nicht benötigen (ernst gemeinte Frage!)?
Nicht benötigt bedeutet, dass der Wert danach nicht mehr verwendet wird. Sonst sollte das Objekt immer einen richtigen Namen bekommen. Alles, was du in Python an den Bezeichner _ bindest, kann quasi vergessen werden oder beliebigen Unsinn annehmen. Normalerweise, wenn man beim Unpacking etwas nicht braucht:

Code: Alles auswählen

_, x, _ = 1, 2, 3
Das Leben ist wie ein Tennisball.
mutetella
User
Beiträge: 1695
Registriert: Donnerstag 5. März 2009, 17:10
Kontaktdaten:

Ok, hab' den underscore eliminiert...

Nochmal zu meinem eigentlichen Anliegen :wink: :

Ich grübel schon 'ne Weile darüber nach, wie man den Teil

Code: Alles auswählen

    ...
    if arg_count == 3:
        # positions of year, month, date
        ...
    elif arg_count == 2:
        # positions of month and date if no year is given
        ...
    else:
        ...
ohne diese ganze ifferei eleganter lösen könnte. Wenn 'zip()' auch unterschiedlich lange Listen verarbeiten könnte (mit einem default-Wert oder so), könnte das vielleicht 'ne Lösung sein. In etwa so:

Code: Alles auswählen

date_format = 'ymd'
date_proposal = (2012, 8, 29)
date_args = (9, 16)
fill_with_defaults(date_proposal, date_args, 2012)
Allerdings hört meine Fantasie bei der Frage auf, wie 'fill_with_defaults()' fehlende Werte an die richtige Position bringen soll. Wäre die Reihenfolge immer y, m, d wäre es kaum ein Problem...

Oder was mit defaultdict...?

Oder ist die 'arg_count'-Lösung ok?

Hab' mich da jetzt voll reingeschraubt... kann mir jemand helfen... :(

mutetella
Entspanne dich und wisse, dass es Zeit für alles gibt. (YogiTea Teebeutel Weisheit ;-) )
deets

Du hast dich geschraubt. Und wenn wuerde ich deinem code eine Heuristik spendieren, wie er M/D von D.M unterscheiden kann. Bzw. eine Option, und uU eine Heuristik, wenn man viele Werte aus derselben Quelle bekommt und irgendwann durch den Wertebereich erfaehrt, was was ist.
lunar

@mutetella Täusche ich mich, oder suchst Du einfach dateutil.parser?
mutetella
User
Beiträge: 1695
Registriert: Donnerstag 5. März 2009, 17:10
Kontaktdaten:

@lunar:
Mach' die Augen zu und stelle Dir vor wie ich mich in den Staub werfe und Dir die Füße küsse...
Entspanne dich und wisse, dass es Zeit für alles gibt. (YogiTea Teebeutel Weisheit ;-) )
deets

Ich muss dieses Bild jetzt leider auch aus meinem Kopf kriegen... :shock:
mutetella
User
Beiträge: 1695
Registriert: Donnerstag 5. März 2009, 17:10
Kontaktdaten:

Ich hab' mir die 'parser.py' aus dem dateutil-Modul mal ein ganz klein wenig angeschaut und bin jetzt auch mit meiner 'arg_count'-Lösung (auch wenn ich sie jetzt nicht mehr brauche) versöhnt. Letztlich arbeitet 'parser.py' den zu parsenden date-string ähnlich durch, wenngleich auch auf höherem Niveau... :)

Und die Schreibweise kannte ich auch noch nicht:

Code: Alles auswählen

        ...
    elif len_li == 8:
        ...
    elif len_li in (12, 14):
        ...
Über solche Kleinigkeiten kann ich mich richtig freuen... :mrgreen:

Nochmals vielen Dank an lunar für den Tipp. Neben dem datutil-parser werd' ich auch 'relativedelta()' sehr intensiv nutzen. Schade nur, dass meine eigenen Lösungen in dieser Richtung nun für's Archiv sind... ;-)

mutetella

@deets
Ich hoffe doch sehr, Du hattest keine allzu heftigen Albträume...
Entspanne dich und wisse, dass es Zeit für alles gibt. (YogiTea Teebeutel Weisheit ;-) )
lunar

@mutetella Möchtest Du wirklich, dass ich mir das vorstelle? ;)
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Wer denkt dabei nicht an Julius Cäsar? ;-)
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
mutetella
User
Beiträge: 1695
Registriert: Donnerstag 5. März 2009, 17:10
Kontaktdaten:

@Hyperion
Ich denke da eher an Pilatus, der rief: "Chleudert den Purchen zu Poden!"
Entspanne dich und wisse, dass es Zeit für alles gibt. (YogiTea Teebeutel Weisheit ;-) )
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

mutetella hat geschrieben:@Hyperion
Ich denke da eher an Pilatus, der rief: "Chleudert den Purchen zu Poden!"
:mrgreen: Das war aber nicht der echte! ;-)

Kennt denn niemand die Anekdote von Cäsar, als er zu Beginn des Afrikanischen Krieges bei seiner Landung stolperte und in den Sand fiel? Tse tse ste...
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
Dav1d
User
Beiträge: 1437
Registriert: Donnerstag 30. Juli 2009, 12:03
Kontaktdaten:

mutetella hat geschrieben:ohne diese ganze ifferei eleganter lösen könnte. Wenn 'zip()' auch unterschiedlich lange Listen verarbeiten könnte (mit einem default-Wert oder so), könnte das vielleicht 'ne Lösung sein.
itertools.izip_longest?
the more they change the more they stay the same
Antworten