Python

Ich fange gerade an, Python zu lernen.

Hier finden sich meine Erfahrungen mit Python, und die Programme, die ich damit schreibe.

Ich versuche meine Texte allgemeinverständlich zu schreiben und meinen Code sauber zu kommentieren und hoffe, dass sie für dich damit nützlich sind.

Und wenn du schon python3 installiert hast, probier doch mal das hier:

python3 -c "import antigravity;print(antigravity.__doc__)"

...dazu hätte ich gerne docs ;)

Blob Schwarm

Ich habe einen kleines Programm geschrieben, das einen interagierenden Schwarm von Blobs zeigt.

Jeder der Blobs sucht sich am Anfang einen Partner und nähert sich ihm.

Wenn die beiden Partner sich zu nahe kommen, flieht einer von ihnen und sucht sich einen neuen Partner, dem er sich wieder annähert, bis er ihm zu nahe kommt.

Durch diese brechenden Zweierbeziehungen bleiben die Blobs zusammen, das heißt es entsteht ein Schwarmverhalten, obwohl jeder Blob nur an seinen aktuellen oder nächsten Partner denkt.

Das ganze ist in Python und pyglet (GUI) implementiert. Die einzige Abhängigkeit ist Python 2.5.

Wenn du es anschauen willst, lade dir einfach den Blob Schwarm herunter, entpacke sie und klick in dem Ordner die Datei blob_swarm.pyw an.

Alles andere sollte von selbst gehen.

Viel Spaß!

Blob Valentine

Ein kleines Geschenk, das ich meiner Frau zum (bzw. am) Valentinstag 2008 geschrieben habe.

In seiner Gänze kann es allerdings in diesem Screenshot nicht genossen werden. Um das ganze animiert zu haben, musst du schon das Programm runterladen :) (es braucht nur Python (>=2.5) ).

Blob Valentine

-> Blob Valentine 0.3 herunterladen

Essenzielle Infos zu Python (mMn)

Hier führe ich Links und Texte, die meiner Meinung nach (mMn) essenziell für Python-Programmierer sind.

Die Liste wird sich wohl langsam erweitern.

Allgemein ist der Link zu den python Tools im SVN wichtig. Er enthält so interessante Dinge wie pygettext.py (das ich vorher stundenlang gesucht habe)...

Angefangen mit Übersetzung (l10n) und Internationalisierung (i18n), vll. irgendwann auch Multinationalisierung (m17n).

Geschwindigkeitstest - eine Liste summieren, selbstgeschrieben oder mit sum()

Hier teste ich kurz, wie lange ein selbstgeschriebenes Programm zum summieren einer Liste von Zahlen braucht und vergleiche sie mit der mitgelieferten Funktion sum().

Die Funktionen, die ich nutze sind:

def summieren(liste): 
	"""Summiere alle Zahlen der Liste manuell."""
	# erst brauchen wir einen Zähler, 
	# auf den wir alle Zahlen aufsummieren.
	gesamt = 0
	# Dann summieren wir 
	# alle Zahlen der Liste zu dem Zähler. 
	for x in liste: 
		# Das heißt, wir erhöhen ihn
		# für jede Zahl in der Liste um die Zahl. 
		gesamt += x
	# und geben ihn dann zurück. 
	return gesamt

und

def summieren(liste): 
	"""Summiere alle Zahlen der Liste mit sum()."""
	return sum(liste) 

Die benötigte Zeit teste ich, indem ich die Funtion in einem doctest jeweils 100001 mal ablaufen lasse und prüfe, wie lange python braucht, um das Skript zu auszuführen.

Die vollständigen Quelltexte habe ich an die Seite angehängt.

Die Aufrufe sind damit:
time python summieren_selbst.py # Selbstgeschriebener Code
time python summieren_sum.py # Eingebaute Funktion

Und ich erhalte die Ergebnisse:

$ time python summieren_selbst.py

real 0m7.536s
user 0m7.430s
sys 0m0.030s

$ time python summieren_sum.py

real 0m4.362s
user 0m4.260s
sys 0m0.070s

Damit braucht meine selbstgeschriebene Summierfunktion etwa 74% länger als die eingebaute.

Fazit: Ich konnte mir bestätigen, dass es besser ist, die eingebaute Funktion zu nutzen, statt sie selbst zu schreiben. Der eingebaute Code ist effizienter.

Geschwindigkeitstest - zwei Arten des for loops über tuples

Ich habe getestet, ob es einen Geschwindigkeitsunterschied zwischen zwei Arten des for loops über eine Liste von tuples gibt:

liste_von_tuples = [(1, 2), (3, 4), (5, 6)]

Es gibt 100.000 tuple, und jeder tuple enthält 2 Zufallszahlen.

Art 1:

for i in liste_von_tuples: 
   res.append(i[1])

Art 2:

for i, j in liste_von_tuples: 
   res.append(j)

Ich hatte erwartet, dass die zweite Art schneller ist, da bei Art 1 eine Zuweisungsoperation von zwei Werten und danach eine Auswahloperation und eine Zuweisungsoperation nötig sind, während bei Art 2 die Auswahloperation wegfällt.

Der Test bestätigte meine Ansicht.

Art 1 braucht bei der Liste mit 100.000 Tuples von je 2 Zufallszahlen zwischen 4% und 12% länger.

Fazit: Bei for loops über tuple, sollte Art 2 verwendet werden:

for i, j in liste_von_tuples: 
   res.append(j)

Die verwendete und ausführlich kommentierte Testdatei ist angehängt.

Anmerkung: Meine Python Tests können jetzt auch mit Mercurial heruntergeladen werden: http://freehg.org/u/ArneBab/python_tests/

Ideen und Patches wären cool :)

Hallo Welt!

Ich habe mein erstes Programm mit PyQt erstellt.

In guter Programmier-Tradition heißt es "Hallo Welt!", und zeigt einfach "Hallo Welt!" an.

Ich habe es allerdings recht ausführlich kommentiert, so dass ich denke, dass es PyQt-Neulingen ein paar interessante Einzelheiten zeigen und Programmier- und Python-Neulingen ein paar einfache erste Einsichten geben kann.

Ich habe es angehängt, stelle den Code aber auch diekt auf diese Seite, denn Python Code ist schön genug, dass ich ihn offen auf die Webseite stellen kann.

-----

#!/bin/env python
# encoding: utf-8

#######################################################
#                                                     #
# Hallo Welt!                                         #
# - Mein Erstes PyQt-Programm mit Kommentaren         #
#                                                     #
#   Copyright (C) 2007, Arne Babenhauserheide, GPL    #
#                                                     #
#   This program is free software; you can            #
# redistribute it and/or modify it under the terms of #
# the GNU General Public License as published by the  #
# Free Software Foundation; either version 2 of the   #
# License, or (at your option) any later version.     #
#                                                     #
# This program is distributed in the hope that it     #
# will be useful, but WITHOUT ANY WARRANTY; without   #
# even the implied warranty of MERCHANTABILITY or     #
# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU      #
# General Public License for more details.            #
#                                                     #
# You should have received a copy of the GNU General  #
# Public License along with this program; if not,     #
# write to the Free Software Foundation, Inc., 51     #
# Franklin Street, Fifth Floor, Boston, MA 02110-1301 #
# USA.                                                #
#                                                     #
#######################################################


# Erst holen wir das System-Framework. "sys"
# Qt Programme brauchen von ihm die 
# Kommandozeilenparameter. 

import sys

# Dann die benötigten Qt-Klassen. 
# Erstens das grundlegende Widget für Programme
# QApplication

from qt import QApplication

# und Zweitens einen Knopf, den wir beschriften wollen
# QPushButton

from qt import QPushButton

# und dann noch Signal und Slot, 
# damit ein Druck auf den Knopf
# das Programm beenden kann. 

from qt import SIGNAL, SLOT

# Jetzt können wir uns das Programm app definieren 
# und ihm die Kommandozeilenparameter übergeben: 

app=QApplication(sys.argv)

# Der Knopf ist ähnlich schnell geschrieben. 
# Er erhält den Text "Hallo Welt!\nIch kann 
# (ein bisschen) PyQt!"
# Das \n erzeugt einen Zeilenumbruch. 
# Einfallsreicherweise nennen wir ihn "button". 

button=QPushButton("Hallo Welt!\nIch kann (ein bisschen) PyQt!", None)
# button=QPushButton(u"Hallo traumrose!\nIch liebe Dich!\nKüsst du mich?", None)

# Jetzt machen wir ihn zum Haupt-Widget 
# des Programmes "app", 
# so dass er auf der obersten Ebene liegt: 

app.setMainWidget(button)

# Dann sagen wir ihm, dass er angezeigt werden soll. 

button.show()

# Kurz vor dem Abschluss fügen wir noch 
# als kleine Spielerei 
# etwas Interaktivität hinzu: 
# Das Programm soll sich beenden, 
# wenn der Knopf gedrückt wird. 

# Dafür verbinden wir das Signal "pressed()" 
# des Knopfes "button" 
# mit dem Slot "quit()" des Programmes app. 

app.connect(button, SIGNAL("pressed()"), 
		app, SLOT("quit()"))

# Als letztes führen wir das GUI-Programm aus. 
# Erst hier wird der Knopf wirklich sichtbar. 

app.exec_loop()

# Um das zu testen kannst du das Programm einfach 
# Schritt für Schritt in der Python-Shell ausführen. 

# Das beendet mein erste Programm. 
# Ich hoffe, Du hattest Spaß beim Lesen!

Python Speicherverwaltung

Ich habe einen kleinen Test gemacht, um zu schauen, wie die Speicherverwaltung von Python funktioniert.

Dafür habe ich eine Klasse erzeugt, die eine extrem lange Liste als Attribut hatte, so dass viel Ram verbraucht wurde.

Dann habe ich mehrfach eine Instanz dieser Klasse der gleichen Variable zugewiesen.

Das Ergebnis war, dass höchstens der doppelte Speicherbedarf einer einzelnen Instanz verbraucht wurde.

Außerdem habe ich herausgefunden, dass ein dict mit 3 Millionen unterschiedlichen Schlüsseln, die auf das gleiche Objekt zeigen (None), etwa doppelt so viel Ram braucht wie eine Liste, die diese Schlüssel als Werte hat.

Das Skript zum Testen habe ich angehängt und poste es auch nochmal hier.

Viel Spaß beim selbst mit Python spielen :)

#!/usr/bin/env python
# encoding: utf-8

"""This file tests the memory purging of python by creating an instance of a
class over and over again.

It shows, that the instance is created at only one of two places over and over
again, so the maximum additional memory consumed when replacing an
instance is the size of the instance 

The reason seems to be that the instance is first created and then assigned
to the variable. Only after that, the previous object gets deleted. 

This seems easy to understand, because this behaviour is necessary to allow
for recursive functions where the new value of the variable depends on its
previous value. 

"""

# We want random, so that no optimizing of same objects can be done. 

from random import random


# Now we create a memory hungry class. 

class MemoryHungry(object): 
    """MemoryHungry is a class which just has many attributes which consume 
memory.

For me one instance of MemoryHungry takes about 140Mib with 
number_of_atts=3,000,000 - bab.

When I use a dict where I assign keys with random names to the value None 
(all the same object), this takes about 250MiB per instance. - bab
"""
    def __init__(self, number_of_atts=3000000, mode="list"): 
        if mode == "list": 
            self.liste = [] #: list of random numbers
        elif mode == "dict": 
            self.liste = {} #: dict with random numbers as keys

        # We add random numbers to teh list to make sure, that no memory 
        # usage can be optimized by grouping same objects or similar. 
        for i in range(number_of_atts): 
            self.blah = random() #: temporary random number
            # append self.blah to the list attribute of the class, so it consumes 
            # memory (as object of the list reassigning self.blah doesn't delete the 
            # value/instance of float): 
            if mode == "list": 
                self.liste.append(self.blah)
            if mode == "dict": 
                self.liste[self.blah] = None 
                # None is only one object, so what takes space here are the keys.


# Also we create a class thirsty, which creates a float with the same content 
# over and over again. 

class MemoryThirsty(object): 
    """MemoryThirsty is a class which just has many attributes which consume 
memory.

For me one instance of MemoryThirsty only takes about 70Mib with 
number_of_atts=3,000,000, which is half the amount taken by the same 
number of random numbers -bab.
"""
    def __init__(self, number_of_atts=3000000): 
        self.liste = [] #: List of random numbers

        # We add random numbers to the list to make sure, that no memory 
        # usage can be optimized by grouping same objects or similar. 
        for i in range(number_of_atts): 
            self.blah = 0.111111111111111111111111111111111 #: just a floating point number
            # append self.blah to the list attribute of the class, so it consumes 
            # memory (as object of the list reassigning self.blah doesn't delete the 
            # value/instance of float): 
            self.liste.append(self.blah)



### Self-Test ###

if __name__ == "__main__": 
    # Create a MemoryHungry instance and give it to a variable over and over again.
    print "hunger"
    for i in range(10): 
        # assign an instance of MemoryHungry to the variable hunger
        hunger = MemoryHungry(mode="dict")
        # And print the instance. 
	print hunger, i
        pass
    print "hunger done"
    del hunger
    print "thirst"
    for i in range(10): 
        # assign an instance of MemoryHungry to the variable hunger
        thirsty = MemoryThirsty()
        # And print the instance. 
	print thirsty, i
    del thirsty
    # We're done. Say so. 
    print "done"