Python-Spickzettel für PHP-Entwickler

Gute Links und Tutorials könnt ihr hier posten.
modelnine
User
Beiträge: 670
Registriert: Sonntag 15. Januar 2006, 18:42
Wohnort: Celle
Kontaktdaten:

Doch, seit PEP 292 (= Python 2.4) hat Python etwas vergleichbares, die Template-Strings.
Stimmt. Nur, da sie nicht intrinsisch in der Sprache eingebaut sind (wie String-Formatting), sondern man eine separate Klasse braucht, seh ich sie eigentlich nicht als direkten Vergleich zu "blah $blubb xyz", welches eben keine zusätzlichen Sprachelemente benötigt (auch wenn es von der Syntax her ähnlich ist, aber das krieg ich mit String-Formatting und dicts auch hin dass es so ähnlich aussieht).
--- Heiko.
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Okay, dann versuch ichs noch damit:

Code: Alles auswählen

value = 10
print 'Value %(value)s' % locals()
Auch nicht ganz vergleichbar, aber immerhin in der Sprache eingebaut.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
BlackJack

Python Master 47 hat geschrieben:
Mr_Snede hat geschrieben: OT:
wäre lustig sowas auch für Python - Ruby bzw Python - Perl zu haben.
Ich fände sowas für Pythn-C/C++ sehr gut!
Ein Python-C Spickzettel ist nicht so einfach, weil C nicht über so "fortgeschrittene" Datentypen verfügt, wie Python oder sogar PHP.

C Felder kennen zum Beispiel ihre Länge nicht, die muss man sich entweder gesondert merken, oder eine Endmarkierung benutzen, die nicht regulär im Feld vorkommen darf. Bei Zeichenketten, die eigentlich nur Felder vom Typ `char` sind, ist das zum Beispiel das Nullbyte.

Während ein Feld anlegen noch einfach ist...

Code: Alles auswählen

char *array[] = {"foo", "bar", "frobnitz"};
...muss man für einen Test auf "enthalten sein" schon eine komplette Funktion schreiben, die das Feld, seine Grösse, das was man sucht und eine Vergleichsfunktion als Argumente entgegen nimmt, um den ``in`` Operator von Python nachzubilden:

Code: Alles auswählen

#define _GNU_SOURCE

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define TRUE 1
#define FALSE 0
typedef int bool;

typedef int (*cmp_func)(const void *a, const void *b);

bool in_array(const void **array,
              const unsigned int array_size,
              const void *needle,
              const cmp_func cmp)
{
    unsigned int i;
    for (i=0; i < array_size; ++i) {
        if (cmp(needle, array[i]) == 0) {
            return TRUE;
        }
    }
    return FALSE;
}
Benutzen kann man das dann so:

Code: Alles auswählen

if (in_array(array, 3, "bar", strcmp)) {
    puts("Ist drin");
}
Das `count()` (PHP) bzw. `len()` (Python) Beispiel geht mit C Feldern so nicht, weil die wie schon gesagt, nicht "wissen" wie lang sie sind. Deshalb musste ich die Feldlänge ``3`` oben auch explizit angeben.

`join()` ist dann wegen der Speicherverwaltung, um die man sich bei C selbst kümmern muss, noch umfangreicher. Für das Ergebnis muss man soviel Speicher anfordern, das alle Zeichenketten plus den Seperatoren hineinpassen, also muss man in einem ersten Schritt ersteinmal die Längen der einzelnen Zeichenketten herausfinden. Dann kann man den Speicher anfordern und die einzelnen Zeichenketten und Seperatoren dort hin kopieren.

Code: Alles auswählen

char* str_join(const char *separator,
               const char **strings,
               const unsigned int array_size)
{
    unsigned int i, separator_length, *lengths;
    unsigned int new_size;
    char *result, *ptr;
    
    separator_length = strlen(separator);
    new_size = 1 + (array_size - 1) * separator_length;
    
    lengths = malloc(array_size * sizeof(unsigned int));
    for (i=0; i < array_size; ++i) {
        lengths[i] = strlen(strings[i]);
        new_size += lengths[i];
    }
    
    result = malloc(new_size * sizeof(char));
    ptr = result;
    for (i=0; i < array_size; ++i) {
        memcpy(ptr, strings[i], lengths[i]);
        ptr += lengths[i];
        if (i != array_size - 1) {
            memcpy(ptr, separator, separator_length);
            ptr += separator_length;
        }
    }
    *ptr = '\0';
    
    free(lengths);
    return result;
}
Und man muss natürlich auf die ganzen Grenzfälle achten. Der Quelltext da oben funktioniert nicht für `array_size == 0`. Das liesse sich durch den Einsatz des ternären Operators in einer Zeile beheben, aber das überlasse ich dem Leser als Übung. ;-)

Anwendung der Funktion:

Code: Alles auswählen

char *str;

str = str_join("-", array, 3);
puts(str);
free(str);
Noch eine Implementierung für `split()` und dann höre ich auch auf, versprochen. :-)

Code: Alles auswählen

char** str_split(const char *string,
                 const char *separator,
                 unsigned int *result_size)
{
    unsigned int i, str_length, separator_length;
    char **result, *str, *start, *end;
    
    str = strdup(string);
    str_length = strlen(str);
    separator_length = strlen(separator);
    
    i = 0;
    result = malloc((str_length / 2) * sizeof(char**));
    start = str;
    while ((end = strstr(start, separator))) {
        *end = '\0';
        result[i++] = strdup(start);
        start = end + separator_length;
    }
    result[i++] = strdup(start);
    
    free(str);
    result = realloc(result, i * sizeof(char**));
    *result_size = i;
    return result;
}
Um Grenzfälle habe ich mir jetzt auf die Schnelle keine Gedanken gemacht und benutzt wird es so:

Code: Alles auswählen

char **strings;
unsigned int i, size;

strings = str_split("foo<->bar<->frobnitz", "<->", &size);
for (i=0; i < size; ++i) {
    puts(strings[i]);
    /* free(strings[i]); */
}
/* free(strings); */
Man sieht, das ist nicht wirklich etwas für einen Spickzettel sondern eher für ein "Cookbook".
Python 47
User
Beiträge: 574
Registriert: Samstag 17. September 2005, 21:04

Danke für den ausführlichen Beitrag!Python-C geht ziehmlich schwierig, dennoch wäre ein Vergleich Python-C++ sehr interresant!Das wäre dann auch nicht so schwierig wie mit C! :)
mfg

Thomas :-)
BlackJack

Ob das in C++ so viel einfacher wird, wage ich zu bezweifeln. Klar man hat Objekte, aber mit Objekten und Templates bekommt man auch sehr viel mehr Komplexität mit der man sich herumschlagen muss.

Einer Python-Liste am nächsten kommt wohl der `vector` in C++:

Code: Alles auswählen

    char *tmp[] = {"foo", "bar", "frobnitz"};
    vector<string> array(&tmp[0], &tmp[3]);
Wenn man das temporäre `char`-Feld nicht haben möchte, dann könnte man alternativ auch die drei Zeichenketten einzeln an den `vector` anhängen:

Code: Alles auswählen

    vector<string> array;
    array.push_back("foo");
    array.push_back("bar");
    array.push_back("frobnitz");
Schon wenn man den ``in`` Operator aus Python als Funktion für einen beliebigen Container aus der STL von C++ implementieren möchte, wird man mit Templates konfrontiert:

Code: Alles auswählen

template<class Iterable, class T>
bool contains(Iterable iterable, const T& element)
{
    return find(iterable.begin(), iterable.end(), element) != iterable.end();
}
Man kann `find()` natürlich auch direkt in ``if``-Abfragen benutzen. Den Fall habe ich im folgenden auskommentiert:

Code: Alles auswählen

    // if (find(array.begin(), array.end(), "bar") != array.end()) {
    if (contains(array, "bar")) {
        cout << "Ist drin" << endl;
    }
Die Länge eines Containers kann man natürlich sehr schön ermitteln, da die Objekte eine entsprechende Methode besitzen:

Code: Alles auswählen

    cout << array.size() << endl;
`join()` und `split()` muss man sich aber selbst schreiben.

Code: Alles auswählen

template<class Iter>
string join(const string& separator, const Iter& begin, const Iter& end)
{
    string result;
    
    for (Iter i=begin; i != end; ++i) {
        result += *i;
        if (i != (end - 1)) {
            result += separator;
        }
    }
    return result;
}

template<class StringContainer>
StringContainer split(const string& str, const string& separator)
{
    StringContainer result;
    size_t start, end;
    
    start = 0;
    while ((end = str.find(separator, start)) != str.npos) {
        result.push_back(str.substr(start, end - start));
        start = end + separator.length();
    }
    result.push_back(str.substr(start));
    
    return result;
}
Zu benutzen sind die beiden so:

Code: Alles auswählen

    cout << join("-", array.begin(), array.end()) << endl;

    list<string> splitted = split<list<string> >("foo<->bar<->frobnitz", "<->");
Ob das nun einfacher ist als bei C!? Ich finde C++ auf jeden Fall hässlicher. Insbesondere Fehlermeldungen, die mit Templates zusammenhängen und die Komplexität von Templates im allgemeinen. Ein verkorkster Versuch das starre Typsystem aufzubrechen, IMHO.

Zu den Template-Fehlermeldungen hier ein nettes Beispiel, welches mir beim Schreiben der Beispiele untergekommen ist:

Code: Alles auswählen

test.cpp: In function `int main()':
test.cpp:55: error: could not convert `std::find [with _InputIterator =
__gnu_cxx::__normal_iterator<std::string*, std::vector<std::string,
std::allocator<std::string> > >, _Tp = char[4]]((&array)->std::vector<_Tp,
_Alloc>::begin [with _Tp = std::string, _Alloc = std::allocator<std::string>](),
(&array)->std::vector<_Tp, _Alloc>::end [with _Tp = std::string, _Alloc =
std::allocator<std::string>](), ((const char (&)[4])"bar"))' to `bool'
Da weiss man doch sofort was man falsch gemacht hat, oder!?


Nochmal zum PHP-Python Spickzettel: Bei dem `substr()` Beispiel sollte auf jeden Fall ein Satz erklären, dass die beiden Argumente bei PHP Start und Länge, bei Python aber Start und Ende darstellen. Aus den angegebenen Beispielen wird das nämlich nicht unbedingt klar.
marcel.normann
User
Beiträge: 5
Registriert: Montag 10. April 2006, 17:29
Wohnort: Kirchen (Sieg)
Kontaktdaten:

Danke erst mal für die Anregungen. Ich habe das PDF in der Version 2.0 zwischenzeitlich veröffentlicht und während meines Urlaubs sind einige positive Reaktionen eingetrudelt.

Den Hinweis zu substr() nehme ich auf jeden Fall noch auf, beim Rest sehe ich z.Zt. die Möglichkeit nicht. Sollte ich genug Material für eine Doppelseite zusammen haben, kommen natürlich alle gewünschten Ergänzungen rein.

Gruß, Marcel
Freddy
User
Beiträge: 2
Registriert: Sonntag 18. Juni 2006, 17:45

Zwei kleine fehler die mir aufgefallen sind:

bei in_array eine "{" klasmmer zuviel und bei explode fehlt nen "-" oder?
Antworten