Python

Allikas: Kuutõrvaja
Redaktsioon seisuga 2. september 2010, kell 15:16 kasutajalt Jj (arutelu | kaastöö) (LDAP)

Sissejuhatus

Python (ingl. k. püüton) http://www.python.org/ on suhteliselt populaarne programmeerimiskeel, mille lõi Guido van Rossum http://en.wikipedia.org/wiki/Guido_van_Rossum 1980 aastate lõpus ja millele peetakse iseloomulikuks

  • kasutamise lihtsus, st kiiresti õpitav ja saab kiiresti jõuda kasutatavate tulemusteni
  • keele küpsus
  • sobib mitmekesiste ülesannete lahendamiseks (ingl. k. general purpose language)
  • sunnib kasutajat kirjutama nö loetavat programmi
  • Python on skriptimiskeel (ingl. k. scripted language) st programmid on koheselt käivitatavad ja ei vaja kompileerimist
  • Python sisaldab objektorienteeritud programmeerimise (OOP) võimalusi, kuid neid ei pea tingimata kasutama (käesoleva teksti punkt Python#OOP on pühendatud sellele)
  • Python on vaba tarkvara

2010 aastal on levinud kasutada kahte erinevat Python keele põlvkonda, sõltuvalt kasutusalast või kasutamise asjaoludest võib see olla oluline, kumba konkreetsel juhul eelistada

  • 2.x - juba pikka aega kasutuses olnud versioon, mida jätkuvalt toetatakse
  • 3.x - uuem versioon, millega reeglina vana versiooni jaoks kirjutatud programmid ei tööta; nt print direktiiv ja teksti vs binari failide käsitluse osa on oluliselt muutunud

Mõned populaarsed vaba tarkvaralised Pythoni rakendused on näiteks

  • Mailman - postiloendi serveri (ingl. k. listserver) tarkvara
  • Trac - veebipõhine veahalduse ja versioonikontrolli tarkavara
  • Zope, Plone - veebikeskkonnad

Pythonis valmistatud tarkvara on loetletud aadressil http://en.wikipedia.org/wiki/List_of_Python_software.

Järgnev tekst sisaldab kahte osa

  • keele vahendite tutvustus - tegu ei ole ammendava käsitlusega, kuid piisavaga selleks, et asuda Pythonit kasutama ning et tekiks ettekujutus Pythoni võimalustest
  • nö kokaraamatu (ingl. k. cookbook) osa - keskendutud on süsteemiadministreerimisel vajalikele kasutusjuhtumitele, nt meili saatmine, http, ldap ja dns päringute esitamine, kodeeringutega tegelemine jms kasutades seejuures Pythoni teekide võimalusi.

Esitatud tekst kehtib v. 2.x kohta kui teksti sees ei ole versiooni täpsustatud.

Pythoni paigaldamine

Debian Lenny paketihalduses on olemas kaks Pythoni versiooni

  • 2.5.2 - vaikimisi
  • 2.4.6 - võimalik lisaks paigaldada

Python v. 2.5.2 paigaldmiseks sobib öelda (tegelik paketinimi on python2.5)

# apt-get install python

Programmeerimine Pythonis

Üldiselt võib väita, et programm 'teeb midagi millegagi', konkreetsemalt sobib kujutleda Pythoni kasutamist sellise hierarhiana

  • programmid koosnevad moodulitest
  • moodulid koosnevad lausetest (ingl. k. statement)
  • laused koosnevad avaldistest (ingl. k. expression)
  • avaldised tekitavad ja töötlevad objekte

Python on

  • dünaamilise tüübiskeemiga (ingl. k. dynamically typed) keel - programmi käivitamisel ajal (ingl. k. runtime) toimub tüüpide üle otsustamine; st praktiliselt, et nt muutujate väärtustamisel ei pea nende tüüpe kirjeldama
  • tugeva tüübiskeemiga (ingl. k. strongly typed) keel - erinevat tüüpi muutujatega ei saa teha teatud operatsioone, nt liita arv ja sõne

Pythoni interpretaator

Pythoni interpretaatori kasutamine interaktiivses režiimis toimub selliselt

 $ python
 Python 2.5.2 (r252:60911, Jan 24 2010, 14:53:14) 
 [GCC 4.3.2] on linux2
 Type "help", "copyright", "credits" or "license" for more information.
 >>> a="tervist"
 >>> print a
 tervist
 >>> Ctrl+D
 $ 

Interpretaator sobib nt kalkulaatorina kasutamiseks

 >>> 21.0/8
 2.625

Pythoni skript

Pythoni skript esineb tekstifaili kujul, kusjuures sisu kirjutamisel tuleb jälgida, et tulemust mõjutab ka ridade alguses kasutatud tühikute hulk, st taane (ingl. k. intendation). Nt selline skript kirjutab välja arvud ühest kolmeni

  for i in range(3):
    print i + 1

kus

  • range(3) funktsioon tekitab loendi elementidega 0, 1, 2 ja 3
  • for-kordus tegeleb järjekorras kõigi elementidega
  • print esitab i + 1 avaldise väärtuse iga for korduse jaoks, kusjuures print on trepitud (ingl. k. to intendate)

Skripti käivitamiseks sobib öelda

 $ python skript.py
 1
 2
 3

Alternatiiviks on lisada faili algusse rida interpretaatori nimega

 #!/usr/bin/python
 
 for i in range(3):
   print i+1

ning muuta fail käivitatavaks

 $ chmod 0755 skript.py

ning käivitada

 $ ./skript.py

Lisaks töötavatele ridadele võib skriptis esineda kommentaare, kõike # märgist paremale jäävat käsitletakse kommentaarina.

Sõne-operatsioonid

Sõnedega (ingl. k. string) saab teha nt selliseid operatsioone

  • omistada muutujale ja esitada
linn = "Tartu"
print linn
  • liita
linn = "Tartu"
print "Asukoht " + linn
  • küsida elementi, vastatakse 'r'
sone = "Tartu"
print sone[2]

Andmestruktuurid

Pythonis kasutatavad enamlevinud nn sisse-ehitatud (ingl. k. built-in) andmestruktuurid on

  • sõne - järgnevus tähti-numbreid-muid-sümboleid
  • arv - nt täisarv või ujukomaarv
  • loend (ingl. k. list) - ...
  • korteež (ingl. k. tuple) - järjestatud hulk, mille elemente ei saa muuta
  • sõnastik (ingl. k. dictionary) - mõnedes keeltes tuntud ka kui assotsiatiivne massiiv (ingl. k. associative array, ka hash)

Sõne

Sõne (ingl. k. string) on järgnevus tähti-numbreid-muid-sümboleid

 'abcdefgh'

Arv

Arv (ingl. k. number) võib olla nt täisarv või ujukomaarv

 15

Kuna Python on tugeva tüübiskeemiga keel, siis nt ei saa liita arve ja sõnesid, eelnevalt tüleb tüüp teisendada, nt

 print 'mina tulen kell ' + str(10)

Loend

Loend (ingl. k. list) on järjestatud hulk

loend = ['yks', 'kaks', 'kolm']

Loendi elemendi poole pöördumine toimub selliselt

print loend[1]

Loendiga saab sooritada mitmeid iseloomulikke tegevusi, nt

  • järjekorra ümberpööramine
print loend.reverse()
  • elemendi lisamine
loend.append('neli')
  • elemendi jrk numbri küsimine
loend.index('kaks')
  • loendi saab täita loendi genereerimise (ingl. k. list comprehension) teel võttes aluseks olemasoleva loendi
loend = [ 10, 20, 30 ]
comp = [ 3*x for x in loend ]

for i in comp:
  print i

või sisu arvutusest genereerida

comp = [ 2*x for x in range(10) ]

for i in comp:
  print i

Korteež

Korteež (ingl. k. tuple) on järjestatud hulk, mis erineb loendist nt selle poolest, et selle on oluliselt vähem funktsionaalsust, mis põhiliselt tuleneb asjaolust, et korteeži elemente ei saa muuta

 k = ('yks', 'kaks', 'kolm')

Korteeži saab kasutada for-korduse juures ning tema elemente saab järjekorra alusel küsida, nt

 print k[2]

Loendi teisedab korteežiks funktsioon tuple()

 k = tuple(loend)

Sõnastik

Sõnastik (ingl. k. dictionary) on key-value paaridest koosnev järjestamata hulk

 sonastik = { 'priit': 167, 'mart': 196 }

Sõnastiku elemendi väärtuse esitamine toimub nii

 print sonastik['priit']

Hulk

Hulk (ingl. k. set) on järjestamata kogum elemente

hulk1 = set(['essa', 'tessa', 'kossa', 'nessa'])

Hulkadega saab teha nt selliseid tehteid

  • ühend (summa)
print hulk1 | hulk2
  • ühisosa (lõige, korrutis)
print hulk1 & hulk3

Nagu alguses kasutatud, loendi teisendamiseks hulgaks sobib kasutada set funktsiooni

hulk = set(loend)

Ning hulga teisendamine loendiks (kusjuures tulemuse järjestusele ei maksa toetuda) toimub funktsiooniga list

loend = list(hulk)

Failioperatsioonid

Failioperatsioonid tegelevad failist lugemise ja faili kirjutamisega.

Lugemine

Tekstifailist lugemiseks sobib kasutada nt sellist järgnevust

 for rida in open ('urlid.txt'):
   print rida.rstrip()

kus

  • open ('urlid.txt') - avab tekstifaili
  • rstrip() eemaldab rea lõpust reavahetuse märgi
  • lugemine toimub for-korduse abil rida haaval

Kirjutamine

Tekstifaili kirjutamiseks sobib kasutada nt sellist järgnevust

 read=('loomaaed.tartu.ee', 'kuutorvaja.eenet.ee', 'www.eesti.ee')
 myfile = open ('urlid','w')
 
 for rida in read:
   myfile.write (rida + '\n')
 
 myfile.close()

kus

  • myfile = open ('urlid','w') - tekstifail urlid avatakse lugemiseks ja kirjutamiseks
  • read - on korteeži element, mis on loendi moodi konstruktsioon, kuid mille sisu ei saa peale moodustamist muuta
  • kirjutamine toimub for-korduse abil rida haaval

Tingimused

Pythonis saab kasutada selliseid tingimusi

  • if-elif-else tingimus
 x = 1
 
 if x < 0:
   print 'Alla nulli'
 elif x == 0:
   print 'Null'
 elif x == 1:
   print 'Yks'
 else:
   print 'Suurem yhest'

Kordused

Pythonis saab kasutada selliseid kordusi

  • for-kordus
 loend = ['Priit', 'Mart', 'Laa']
 
 for i in loend:
   print "Tervist " + i
  • while-kordus
 i = 0
 while i < 5:
   i = i + 1
   print i

Kordusest saab tulla välja break abil, nt

 l = [ 'essa', 'tessa', 'kossa', 'nessa' ]
 
 for i in l:
     if i == 'kossa':
       print "if seest print", i
       break
     print "if jarel print", i
 
 print "for jarel print", i

annab sellise väljundi

$ python skript.py 
if jarel print essa
if jarel print tessa
if seest print kossa
for jarel print kossa

Funktsioonid

Funktsioonid võimaldavad üldiselt kahte asja

  • korraldada programmi osade korduvkasutust
  • jagada programmi loogilisteks osadeks

Funktsioon kireldatakse def direktiivi abil, nt selliselt

  def korrutamine(x, y):
    return x * y
  
  print korrutamine(15, 3)

kus

  • esimesed kaks rida kirjeldavad funktsiooni
  • return tagastab funktsiooni väljakutsele väärtuse
  • viimases reas toimub funktsiooni väljakutse

Pythoni funktsioonide kohta öeldaks, et nad on polümorfsed, st nad töötavad suvaliste sisendaandmetega eeldusel, et funktsioonis kirjeldatud operatsioone saab sooritada nende andmetega. Nt sobiks ülaltoodud funktsiooni välja kutsuda selliselt

 print korrutamine ('mina ', 3)

Funktsiooni dokumentatsioon

Pythoni funktsiooni saab dokumenteerida esitades vahetult def järel kolmekordsete jutumärkide vahel teksti

 def korrutamine(x, y):
   """Korrutaja
 
 Korrutab
 """
 
   return x * y

Sellist dokumentatsiooni saab lugeda nt selliselt

 print korrutamine.__doc__

Moodulid

Moodulite abil organiseeritakse programmi teksti kõige üldisemal tasemel. Nt esitatud moodul vastab failile aritmeetika.py ning seal on kirjeldatud üks muutuja ning kaks funktsiooni

 $ cat aritmeetika.py
 pii = 3.14
 
 def liida(x, y):
   return x + y
  
 def lahuta(x, y):
   return x - y

Seda moodulit kasutab nt selline skript

import aritmeetika

print "Pii väärtus on ", aritmeetika.pii
print aritmeetika.liida(2, 3)

Mooduli poole pöördumiseks kasutatakse järgnevust 'mooduli-failnimi.moodulis-kirjeldatud-objekt', nagu näitest on näha võib see objekt olla muutuja või funktsioon.

Tehniliselt on kõik Pythoni skriptid moodulid.

Parasjagu kasutada oleva süsteemi moodulirada näeb öeldes

>>> import sys
>>> print sys.path
[' ', '/usr/lib/python2.5', '/usr/lib/python2.5/plat-linux2', '/usr/lib/python2.5/lib-tk', \
  '/usr/lib/python2.5/lib-dynload',  '/usr/local/lib/python2.5/site-packages', \
  '/usr/lib/python2.5/site-packages', '/usr/lib/pymodules/python2.5']

kus listi esimene element on tegelikult 'kaks ülakoma järjest', mis tähistab käesolevat kataloogi. Importimisel otsib Python otsib moodulid järjest nendest kataloogidest, otsinguteele saab lisada katalooge keskkonnamuutuja PYTHONPATH abil, nt

 $ export PYTHONPATH=/opt/python-libs

Moodulis kirjeldatud objektide nimekirja küsimiseks sobib öelda

>>> import moodulinimi
>>> dir(moodulinimi)
['__builtins__', '__doc__', '__file__', '__name__', 'korrutaja', 'pii']

Nimeruum

Peale programmi sisse mooduli importimist saab tema objektide poole pöörduda kujul

 moodulinimi.objektnimi

Öeldakse, et programmi jaoks on tekkinud mooduli nime ja seal kirjeldatud objektide nimedele vastav nimeruum (ingl. k. namespace).

Paketid

Lisaks failidele saab mooduleid paigutada spetsiaalselt moodulite hoidmiseks moodustatud kataloogidesse. Sellist kataloogi nimetatakse paketiks (ingl. k. package) ja seal peab sisalduma fail __init__.py. Paketi kujul esitatud moodulite laadimiseks tuleb programmis öelda

 import katalooginimi

Seejuures täidetakse esmalt __init__.py faili sisu, see fail võib olla ka tühi, ning seejärel kõik kataloogis olevad moodulitele vastavad failid.

Paketi kataloogistruktuur võib olla ka mitmetasemeline, üldiselt toimub moodulis kirjeldatud objektide kasutamine selliselt

 katalooginimi1.katalooginimi2.funktsiooninimi()

Mooduli kasutuskoha kontroll

Üldiselt võib olla vaja moodulit nii kasutada importimise teel kui ka otseselt käivitades. Selleks, et neid kahte juhtumit eristada, sobib kasutada mooduli sees ühte if-tingimust ja muutuja nime, mis on erinev sõltuvalt kasutusjuhust. Nt selliselt

 pii = 3.14
 
 def liida(x, y):
   return x + y
  
 def lahuta(x, y):
   return x - y
  
 if __name__ == '__main__':
 
   print "mind käivitatakse otse"

kus

  • __name__ muutuja väärtus on otse käivitamisel '__main___' ning importimisel vastava faili nimi

Baitkood

Skripti poolt kasutatavate moodulite jaoks moodustatakse skripti esmakordsel käivitamisel esmalt baitkood (ingl. k. bytecode) ja see baitkood käivitatakse Pythoni virtuaalsel masinal. Baitkood tekitatakse automaatselt samasse kataloogi mooduliga samanimelise failina, mille nime lõpus on .pyc.

Järngevatel skript käivitamistel kontrollitakse moodulite ja moodulitele vastavate baitkoodi failide viimase muutmise aegu ning selle järgi otsustatakse, kas baitkood tuleb uuesti moodustada. Kui baitkoodi pole vaja moodustada, siis Python kasutab olemasolevat baitkoodi ning skripti käivitamine toimub baitkoodi moodustamisele kuluva aja võrra kiiremini.

Võiks ette kujutada, et baitkood on midagi lähtekoodi ja masinkoodi vahepealset, nt tema kasutamise efektiivsuse mõttes.

HTTP

HTTP päringute tegemiseks on Pythonis kaks teeki

Skript http päringu vastuse päise elemendi esitamiseks

import httplib
conn = httplib.HTTPConnection("www.python.org")
conn.request("GET", "/index.html")
r1 = conn.getresponse()
print r1.getheader('last-modified')

Lihtsa HTTP serveri, mis serveerib üle HTTP käesoleva kataloogi sisu, saab käivitada selliselt

 $ python -m SimpleHTTPServer 8080
 Serving HTTP on 0.0.0.0 port 8080 ...

Basic auth

 import urllib2
 
 auth_handler = urllib2.HTTPBasicAuthHandler()
 auth_handler.add_password(realm='Tekst millega veebikoht nö tervitab',
                          uri='https://svn.loomaaed.tartu.ee/projektid/',
                          user='priit',
                          passwd='parool')
 opener = urllib2.build_opener(auth_handler)  
 urllib2.install_opener(opener)
 f = urllib2.urlopen('https://svn.loomaaed.tartu.ee/projektid/projektinimi/trunk/sql/LoeMind.txt')
 print f.read(100000)

try-except-else lause

Python võimaldab programmi töös esineda võivaid erindeid (ingl. k. exception) st praktiliselt veasituatsioone püüda try-except lause abil.

Skript

  • kirjeldab loendi URLID
  • käib läbi loendis URLID toodud hostide /index.html ressursid
  • kui ressursi poole ei õnnestu pöörduda püütakse see try/except poolt kinni
import httplib

URLID = ('www.python.org',
         'kuutorvaja.eenet.ee',
         'seda.pole')

for i in URLID:
  try:
    conn = httplib.HTTPConnection(i)
    conn.request("GET", "/index.html")
  except:
    print "ei saa avada http://" + i + "/index.html"
  else:
    r1 = conn.getresponse()
    print i + " " + r1.getheader('content-length')

Käivitamisel esitatakse nt selline väljund

$ python for.py
www.python.org 16961
kuutorvaja.eenet.ee 267
ei saa avada http://seda.pole/index.html

Unicode

Pythonis saab töötada Unicode'iga sh UTF-8 kodeeringus tekstiga, http://docs.python.org/howto/unicode.html.

Skript UTF-8 formaadis faili kirjutamiseks (sisuks on Ä ja à tähed)

file = open('utf8data.txt', 'w', encoding='utf-8')
size = file.write('\xc4\xc3\n')
file.close ()

PostgreSQL andmebaasi kasutamine

Psycopg http://initd.org/psycopg/ võimaldab Pythonist kasutada PostgreSQL andmebaasi. Tarkvara paigaldamiseks Debian operatsioonisüsteemis sobib öelda

# apt-get install python-psycopg2

Andmete lugemine

Skript küsib andmebaasi tabelist priit.priidutabel kahte väärtust

import psycopg2
conn = psycopg2.connect("dbname=baasinimi host=192.168.1.247 user=kasutajanimi password=kasutajaparool port=portnumber")
cur = conn.cursor()
cur.execute("SELECT nimi, vanus FROM priit.priidutabel where nimi = 'Priit Kask';")
read = cur.fetchall()

for rida in read:
  print ('Nimi: %s, vanus: %s' % rida)

cur.close()
conn.close()

kus

  • connect() parameeter on ühendussõne
  • cur.fetchall() tagastab vastused korteežidena

Andmete sisestamine

Andmete baasi sisestamisel tuleb lisaks öelda commit

import psycopg2
conn = psycopg2.connect("dbname=baasinimi host=192.168.1.247 user=kasutajanimi password=kasutajaparool port=portnumber")
cur = conn.cursor()
cur.execute("insert into priit.priidutabel (vanus, nimi) values ('28', 'Mart Kask');")
cur.close()
conn.commit()
conn.close()

Binary andmete sisestamine

Binary andmeid hoitakse bytea tüüpi väljas, nt sellises tabelis

CREATE TABLE bitea.tabelinimi(name TEXT, ablob BYTEA);

Andmeid saab sisestada sellise skriptiga

import psycopg2, cPickle
connection = psycopg2.connect()
cursor = connection.cursor()

data = cPickle.dumps('binaarne sisu', 2)
sql = "INSERT INTO bitea.tabelinimi VALUES(%s, %s)"
cursor.execute(sql, ('esimesevaljavaartus', psycopg2.Binary(data)))
connection.commit()

cursor.close()
connection.close()

MySQL andmebaasi kasutamine

MySQL-Python http://mysql-python.sourceforge.net/ võimaldab Pythonist kasutada MySQL andmebaasi. Tarkvara paigaldamiseks Debian operatsioonisüsteemi sobib öelda

# apt-get install python-mysqldb

Andmete lugemine

Skript küsib andmebaasi tabelist priit.priidutabel kahte väärtust

import MySQLdb
conn = MySQLdb.connect(db="baasinimi" host="192.168.1.247" user="kasutajanimi" passwd="kasutajaparool" port=portnumber)
cur = conn.cursor()
cur.execute("SELECT nimi, vanus FROM priit.priidutabel where nimi = 'Priit Kask';")
read = cur.fetchall()

for rida in read:
  print ('Nimi: %s, vanus: %s' % rida)

cur.close()
conn.close()

kus

  • connect() parameeter on ühendussõne
  • cur.fetchall() tagastab vastused korteežidena

Andmete sisestamine

Andmete baasi sisestamisel tuleb lisaks öelda commit

import MySQLdb
conn = MySQLdb.connect(db="baasinimi" host="192.168.1.247" user="kasutajanimi" passwd="kasutajaparool" port=portnumber)
cur = conn.cursor()
cur.execute("insert into priit.priidutabel (vanus, nimi) values ('28', 'Mart Kask');")
cur.close()
conn.commit()
conn.close()

SMTP

TODO

Plaintext

Skript saadab smtp serveri kaudu välja kirja

import smtplib

fromaddr = "From: Priit Kask <priit@loomaaed.tartu.ee>"
toaddrs  = "To: Mart Kask <mart@loomaaed.tartu.ee>"

msg = fromaddr + "\n" + toaddrs + "\nSubject: test" + "\nkirja sisu"

server = smtplib.SMTP('192.168.1.250')
server.set_debuglevel(1)
server.sendmail(fromaddr, toaddrs, msg)
server.quit()

MIME multipart

nn MIME multipart kirja saatmist käsitleb nt viimane näide aadressilt http://docs.python.org/library/email-examples.html

import smtplib

from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText

me = "priit@loomaaed.tartu.ee"
you = "mart@loomaaed.tartu.ee"

# Create message container - the correct MIME type is multipart/alternative.
msg = MIMEMultipart('alternative')
msg['Subject'] = "Link"
msg['From'] = me
msg['To'] = you

# Create the body of the message (a plain-text and an HTML version).
text = "Hi!\nHow are you?\nHere is the link you wanted:\nhttp://www.python.org"
html = """\
  <html>
  ...
  HTML tekst 
"""

# Record the MIME types of both parts - text/plain and text/html.
part1 = MIMEText(text, 'plain')
part2 = MIMEText(html, 'html')

# Attach parts into message container.
# According to RFC 2046, the last part of a multipart message, in this case
# the HTML message, is best and preferred.
msg.attach(part1)
msg.attach(part2)

# Send the message via local SMTP server.
s = smtplib.SMTP('smtp.loomaaed.tartu.ee')
# sendmail function takes 3 arguments: sender's address, recipient's address
# and message to send - here it is sent as one string.
s.sendmail(me, you, msg.as_string())
s.quit()

DNS

Pythoni seest DNS kasutamiseks sobib kasutada dnspython http://www.dnspython.org/ tarkvara, paigaldamiseks Debian keskkonnas tuleb öelda

# apt-get install python-dnspython

Selline skript esitab tekstifailis toodud domeeninimedele vastavate A kirjete ip aadressid

import dns.resolver

for rida in open ('hostid'):

  try:
    answers = dns.resolver.query(rida.rstrip(), 'A')
    for rdata in answers:
      print rida.rstrip() + " " + str(rdata)

  except dns.resolver.NXDOMAIN:
    print ('Domeeninimel %s puudub A kirje' % rida.rstrip())

LDAP

Pythoni seest LDAP kataloogi kasutamiseks sobib tarkvara python-ldap http://www.python-ldap.org/, paigaldamiseks Debian keskkonnas tuleb öelda

# apt-get install python-ldap

Skript LDAP kataloogist päringu tegemiseks, küsitakse kõik andmed

 import ldap
 l = ldap.initialize('ldap://192.168.1.247')
 result_set = l.search_s('dc=loomaaed,dc=tartu,dc=ee',ldap.SCOPE_SUBTREE,'(objectClass=*)',['cn', 'uid', 'loginShell'])
 
 for i in result_set:
   print i

Tulemuseks olev result_set on loend korteežidest, viimane for-kordus esitab nende korteežide väärtused, üks real.

Help kasutamine

>>> help()

Welcome to Python 2.5!  This is the online help utility.

If this is your first time using Python, you should definitely check out
the tutorial on the Internet at http://www.python.org/doc/tut/.

Enter the name of any module, keyword, or topic to get help on writing
Python programs and using Python modules.  To quit this help utility and
return to the interpreter, just type "quit".

To get a list of available modules, keywords, or topics, type "modules",
"keywords", or "topics".  Each module also comes with a one-line summary
of what it does; to list the modules whose summaries contain a given word
such as "spam", type "modules spam".

help> 

Sisestades help> prompti järele huvipakkuva mooduli nime saab selle mooduli kohta teavet, nt

help> smtplib

Help on module smtplib:

NAME
    smtplib - SMTP/ESMTP client class.

FILE
    /usr/lib/python2.5/smtplib.py

MODULE DOCS
    http://www.python.org/doc/current/lib/module-smtplib.html

DESCRIPTION
    This should follow RFC 821 (SMTP), RFC 1869 (ESMTP), RFC 2554 (SMTP
    Authentication) and RFC 2487 (Secure SMTP over TLS).
...

Programmide käivitamine shellist

Aadressil http://jimmyg.org/blog/2009/working-with-python-subprocess.html on toodud mitmeid näiteid subprocess mooduli kasutamise kohta

import subprocess
pipe = subprocess.Popen(["df", "-h"], stdout=subprocess.PIPE)
print pipe.communicate()[0]

Väljastatakse kahe elemendilise korteeži esimene element.

UDP sõnumi saatmine

Aadressil 192.168.10.35:514/udp töötavale syslogile sõnumi saatmiseks sobib selline järgnevus

 import socket
 port = 514
 host = "192.168.10.35"
 s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
 s.sendto("test sonum\n", (host, port))

Alamprotsesside loomine

Alamprotsesside loomise (ingl. k. to fork) teel saab käivitada paralleelselt mitmeid protsesse. Alamprotsessi loomisel moodustatakse olemasolevast protsessist koopia, ning edasi töötab arvutis kaks protsessi

  • vanem (ingl .k. parent) - protsessi, milles toimus fork funktsiooni väljakutse
  • laps (ingl. k. child) - fork tulemusena tekkinud uus protsessi

Põhimõtteliselt töötab alamprotsessi moodustamine selliselt, tulemusena on käivitatud töötavast programmist juurde teine eksemplar

 import os
 
 def child( ):
   print 'Siin laps',  os.getpid( )
   os._exit(0)  # else goes back to parent loop
 
 def parent( ):
   while 1:
     newpid = os.fork( )
     if newpid == 0:
       child( )
     else:
       print 'Siin vanem ', os.getpid( ), newpid
     if raw_input( ) == 'q': break
 
 parent( )

kus

  • def parent ( ) - kirjeldab funktsiooni, mis programmi täitmisel esmalt käivitatakse
  • def child ( ) - kirjeldab funktsiooni, mille täitmine kutsutakse parent funktsioonist välja
  • parent funktsioonis toimub os.fork() funktsiooni väljakutse, mille tulemusena arvuti mälus on kaks samasisulist programmi, millest üks on vanem ja teine laps; nende edasi töötamisel laps puhul on rahuldatud tingimus 'if netpid == 0' ning muul juhul on tegu vanemaga
  • programmi töö lõpetab while kordusest q sisestuse abil väljumine

Forkimise järel aga ei pea laps olema tingimata sama sisuga kui vanem, näiteks alltoodud skript kasutab os.execlp funktsiooni asendades lapse programmi funktsiooni argumendiks olevate väärtustele vastava tegevusega kopeerides ftp serverist faile

 import os
 
 filesetid = ('bsd', 'base46.tgz', 'comp46.tgz', 'etc46.tgz', 'man46.tgz', 'misc46.tgz')
 
 for fileset in filesetid:
   pid = os.fork()
   if pid == 0:
     os.execlp('wget', 'wget', '-q', 'http://ftp.aso.ee/pub/OpenBSD/4.6/amd64/' + fileset)
     assert False, 'error starting program'
 
   else:
     print 'fileset: ', pid
 
 raw_input( )

Skripti töötamise ajal paistavad protsessitabelis sellised sissekanded

# ps auf
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
priit     31463  0.0  0.2  19040  2076 pts/2    S<s  06:30   0:00 -bash
priit     32009  0.1  0.4  16420  3068 pts/2    S<+  08:03   0:00  \_ python fork-dl.py
priit     32010  0.0  0.2  26372  2036 pts/2    S<+  08:03   0:00      \_ wget -q http://ftp.aso.ee/pub/OpenBSD/4.6/amd64/bsd
priit     32011  0.2  0.2  26372  2032 pts/2    S<+  08:03   0:00      \_ wget -q http://ftp.aso.ee/pub/OpenBSD/4.6/amd64/base46.tgz
priit     32012  0.1  0.2  26372  2036 pts/2    S<+  08:03   0:00      \_ wget -q http://ftp.aso.ee/pub/OpenBSD/4.6/amd64/comp46.tgz
priit     32013  0.1  0.2  26372  2028 pts/2    S<+  08:03   0:00      \_ wget -q http://ftp.aso.ee/pub/OpenBSD/4.6/amd64/etc46.tgz
priit     32014  0.0  0.2  26372  2032 pts/2    S<+  08:03   0:00      \_ wget -q http://ftp.aso.ee/pub/OpenBSD/4.6/amd64/man46.tgz
priit     32015  0.0  0.2  26372  2032 pts/2    S<+  08:03   0:00      \_ wget -q http://ftp.aso.ee/pub/OpenBSD/4.6/amd64/misc46.tgz

Lõime kasutamine

Lõimed võimaldavad sarnaselt alamprotsesside moodustamisele korraldada paralleelselt tegevusi, kuid selle erinevusega, et kõik tegevused toimuvad ühe ja sama protsessi sees. Võrreldes protsessidega on lõimedele iseloomulik, et kõik lõimed jagavad omavahel mitmeid ressursse, nt globaalseid muutujaid ja seega erinevate lõimede vahel võib olla mõnel juhtumil lihtsam korraldada andmevahetust kui erinevate protsesside vahel.

Lõime (ingl. k. thread) kasutamine toimub põhimõtteliselt selliselt

import thread

def child(tid):
  print 'Tervist loimest nr', tid

def parent( ):
  i = 0
  while 1:
    i = i+1
    thread.start_new(child, (i,))
    if raw_input( ) == 'q': break

 parent( )

kus

  • skript käivitab funktsiooni parent ( )
  • parent ( ) seest kutsutakse välja while korduse sees thread.start_new() abil lõimena funktsioon child()
  • thread.start_new() täitmise lõppu ootamata liigutakse skriptis edasi

Järgnev skript kopeerib ftp serverist faile

import thread
import urllib

def child(fileset):
  print 'Tervist loimest, kopeerin faili: ', fileset
  urllib.urlretrieve('http://ftp.aso.ee/pub/OpenBSD/4.6/amd64/' + fileset, fileset)

filesetid = ('bsd', 'base46.tgz', 'comp46.tgz', 'etc46.tgz', 'man46.tgz', 'misc46.tgz')

for fileset in filesetid:
  thread.start_new(child, (fileset,))

raw_input( )

kus

  • raw_input( ) - kui klaviatuurilt sisestust mitte oodata, siis jõuaks programmi täitmine lõpule enne kui lõimed on lõpetanud

Tulemusena töötab kokku protsessis seitse lõime

$ ps -aeLf | grep python | grep -v grep
UID        PID    PPID   LWP    C    NLWP  STIME  TTY      TIME      CMD
priit      1308   880    1308   0    7     15:06  pts/0    00:00:00  python loim-ftp.py
priit      1308   880    1309   0    7     15:06  pts/0    00:00:00  python loim-ftp.py
priit      1308   880    1310   0    7     15:06  pts/0    00:00:00  python loim-ftp.py
priit      1308   880    1311   0    7     15:06  pts/0    00:00:00  python loim-ftp.py
priit      1308   880    1312   0    7     15:06  pts/0    00:00:00  python loim-ftp.py
priit      1308   880    1313   0    7     15:06  pts/0    00:00:00  python loim-ftp.py
priit      1308   880    1314   0    7     15:06  pts/0    00:00:00  python loim-ftp.py

kust on näha, et kõigil ridadel on sama PID väärtus kuid erinevad LWP väärtused.

Käsurea argumentide kasutamine skriptis

sys.argv loend sisaldab programminime ja käsureal kasutatud argumentide väärtusi, skript

import sys

for i in sys.argv:
  print i

väljastab käivitamisel

$ python skript.py yks kaks kolm
skript.py
yks
kaks
kolm

Standard-sisendi kasutamine skriptis

Selleks, et skript kasutaks standard-sisendi (ingl. k. stdio) sobib kasutada nt sellist konstruktsiooni

import sys

next = sys.stdin.readline

while 1:

  rida = next()
  if not rida:
    break

  print rida.rstrip()

Kasutamiseks sobib öelda

 $ cat tekstifailinimi | python skript.py

Regulaaravaliste kasutamine

Skript esitab tekstifailist tunnusele vastavad read

import re

for rida in open ('tekstifail.txt'):
  matchobj = re.search('muster', rida)
  if matchobj:
    print rida.rstrip()

Tühikute pealt loendi moodustamine

>>> p = re.compile(r'\s+')
>>> p.split('all tcp 192.168.2.47:1433 <- 10.0.5.13:2746       ESTABLISHED:ESTABLISHED')
['all', 'tcp', '192.168.2.47:1433', '<-', '10.0.5.13:2746', 'ESTABLISHED:ESTABLISHED']

OOP

Objekt-orienteeritud programmeerimine (OOP) tähendab vähemalt kahte asja

  • mõtteviisi, mida kasutatakse ülesandega tegelemiseks
  • konkreetseid keele poolt pakutavaid OOP vahendeid
 class nimed:
   def __init__(self, domeeninimi, omanik ):
     self.domeeninimi = domeeninimi
     self.omanik = omanik
     self.ttl = 86400
 
   def suurendattl(self, kordaja):
     self.ttl = self.ttl * kordaja
 
 # objektid moodustamine
 eesti = nimed(domeeninimi = 'eesti.ee', omanik = 'Calevipoeg')
 tartu = nimed(domeeninimi = 'tartu.ee', omanik = 'Dor Batt')
 
 # too objektidega  
 eesti.suurendattl(2)
 
 # tulemuste esitamine
 print eesti.omanik
 print (eesti.ttl)

kus

  • class nimed - kirjeldab klassi 'nimed'
  • def _init__ - nn konstruktor, mis on tehniliselt tavaline funktsioon kusjuures ta täidetakse klassi põhjal instance'i moodustamisel esimesena; tavaliselt kirjeldatakse seal muutujate vaikeväärtused
  • def suurendattl - tööd tegev meetod
  • eesti = nimed(...) - klassidest instance'ite moodustamine

Märkused

  • Skriptist väljumine etteantud exit code'iga, nt Nagiose agentide puhul vajalik
>>> import sys
>>> sys.exit(2)
$ echo $?
2

Kasulikud lisamaterjalid