Ενότητα 1-2

Στη συνέχεια παρουσιάζονται ορισμένα ενδιαφέροντα στοιχεία της Python. Αυτό που ακολουθεί δεν είναι tutorial, αν θέλετε κάτι τέτοιο μπορείτε να βρείτε εξαιρετικά κείμενα on-line.

Μεταβλητές και Αναφορές

Note

Δεν χρειάζεται να ξέρετε όσα λέγονται σε αυτήν την παράγραφο για να προγραμματίσετε σε Python. Η γλώσσα τα υλοποιεί για εσάς. Είναι όμως χρήσιμα για όσους προέρχονται από τον κόσμο της C/C++!

Στην Python οι μεταβλητές δεν δηλώνονται και μπορούν να περιέχουν διάφορους τύπους αντικειμένων κατά τη διάρκεια ζωής τους. Το πιο κάτω είναι απολύτως νόμιμο:

i = 23
i += 1
j = i
i = "hello"

Αυτό που συμβαίνει κατά την εκτέλεση των παραπάνω είναι:

  • Δημιουργείται ένα αντικείμενο τύπου int με τιμή 23
  • Το όνομα i συνδέεται με το αντικείμενο αυτό
  • Δημιουργείται ένα νέο αντικείμενο τύπου int με τιμή 23+1
  • Το όνομα i συνδέεται με το νέο αυτό αντικείμενο
  • Το όνομα j συνδέεται με το αντικείμενο, στο οποίο αναφέρεται το i (με τιμή 24)
  • Δημιουργείται ένα αντικείμενο τύπου str με τιμή "hello"
  • Το όνομα i συνδέεται με το νέο αυτό αντικείμενο

Κάποια στιγμή (μη ντετερμινιστική για εμάς!) η Python θα ανακυκλώσει αυτόματα όσα αντικείμενα δεν συνδέονται με μεταβλητές (garbage collection).

Strings

Η Python προσφέρει τους κλασσικούς τύπους δεδομένων (int, string κλπ). Τα αντικείμενα των τύπων αυτών είναι immutable, δηλαδή δεν αλλάζουν τιμή (απλά, όπως φάνηκε στην προηγούμενη πράγραφο, δημιουργούνται νέα αντικείμενα).

Τα strings αποτελούν έναν πολύ εύχρηστο τύπο δεδομένων και μερικά χαρακτηριστικά τους δίνονται στη συνέχεια:

  • Σταθερές string με μονά ή διπλά εισαγωγικά
s = "abcd"
s2 = 'abcd'  # ισοδύναμο με προηγούμενο
  • Τα strings ανήκουν στην κατηγορία τύπων sequence (διατεταγμένης ακολουθίας), άρα μπορούμε να πάρουμε επιμέρους τμήματα
>>> s = "abcdefg"
>>> s[0]
'a'
>>> s[3]
'd'
>>> s[-1]
'g'
>>> s[1:4]
'bcd'
>>> s[:-1]
'abcdef'
>>> s[2:]
'cdefg'
  • Για να καταλάβετε καλύτερα το πώς λαμβάνονται τα τμήματα μιας sequence (slicing), μπορείτε να σκεφτείτε ότι ο δείκτης i βρίσκεται ανάμεσα στα (i-1) και i στοιχεία (κι όχι πάνω στο στοιχείο i):
[a|b|c|d|e|f|g]
^ ^ ^ ^ ^ ^ ^ ^
0 1 2 3 4 5 6 7

το s[1:4] είναι ίσο με "bcd"
  • Θυμηθείτε όμως ότι δεν μπορούμε να αλλάξουμε περιεχόμενα σε ένα string (είναι immutable):
>>> s = "abcdefg"
>>> s[3] = 'x'
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'str' object does not support item assignment
  • Μπορούμε όμως πάντοτε να δημιουργήσουμε ένα νέο string. Για παράδειγμα, για να επιτύχουμε το προηγούμενο:
>>> s = "abcdefg"
>>> s = s[:3]+'x'+s[4:]
>>> print s
abcxefg

Τα αντικείμενα τύπου string διαθέτουν μια μεγάλη σειρά από χρήσιμες μεθόδους (http://docs.python.org/library/stdtypes.html#string-methods)

Σύνθετα Αντικείμενα

Το δυνατό σημείο της Python είναι τα σύνθετα αντικείμενα που παρέχει, τα οποία χρησιμοποιούμε για όλες τις δομές που θέλουμε να υλοποιήσουμε. Τα δύο βασικότερα είναι οι λίστες (lists) και τα λεξικά (dictionaries). Και οι δύο αυτοί τύποι αντικειμένων είναι mutable, τα αντικείμενά τους δηλαδή μπορούν να αλλάξουν περιεχόμενα.

Lists

Οι λίστες ανήκουν στην κατηγορία sequence και υλοποιούν μια διατεταγμένη συλλογή αντικειμένων. Τα αντικείμενα που περιέχονται στη λίστα μπορούν να είναι οποιουδήποτε τύπου (ακόμα και άλλες λίστες). Εφόσον μια λίστα είναι sequence, μπορούμε να πάρουμε μέρος της, όπως σε ένα string:

>>> li = [1,2,'abcd','ef']
>>> li[1]
2
>>> li[-1]
'ef'
>>> li[:-1]
[1, 2, 'abcd']

Μπορούμε ακόμα να προσπελάσουμε ένα προς ένα τα στοιχεία της λίστας με την επαναληπτική δομή for <item> in <sequence>:

>>> for itm in li:
...     print itm
...
1
2
abcd
ef

Σκεφτείτε πώς συνδέονται μεταβλητές και αντικείμενα στην Python για να καταλάβετε το επόμενο:

>>> li = [1,2,'abcd','ef']
>>> lm = li
>>> lm[0] = 33
>>> lm
[33, 2, 'abcd', 'ef']
>>> li
[33, 2, 'abcd', 'ef']

Μπορούμε να προσθέσουμε δυναμικά νέα στοιχεία σε μια λίστα:

>>> li = [1,2,3,'ab','cd']
>>> li += [11,22]
>>> li
[1, 2, 3, 'ab', 'cd', 11, 22]
>>> li.append(33)
>>> li
[1, 2, 3, 'ab', 'cd', 11, 22, 33]

Note

Προσέξτε το όρισμα στη μέθοδο append στο προηγούμενο παράδειγμα. Δεν είναι λίστα. Αν ήταν, το αποτέλεσμα θα ήταν διαφορετικό!

>>> l = [1,2,3]
>>> l.append([5,6])
>>> l
[1, 2, 3, [5, 6]]

Αν θέλαμε να προσθέσουμε το 5 και το 6 στη λίστα, θα έπρεπε:

>>> l = [1,2,3]
>>> l += [5,6]
>>> l
[1, 2, 3, 5, 6]

Ή εναλλακτικά (η μέθοδος extend() ισοδυναμεί με το + σε λίστες):

>>> l = [1,2,3]
>>> l.extend([5,6])
>>> l
[1, 2, 3, 5, 6]

Note

Οι λίστες είναι δομές ειδικά βελτιστοποιημένες για ταξινόμηση (sorting). Εάν θέλουμε να ταξινομήσουμε πολύ γρήγορα μια σειρά στοιχείων, η χρήση λίστας είναι MUST!!!

Dictionaries

Τα λεξικά (dictionaries) είναι τύπου mapping: ζεύγη αντιστοιχιών (κλειδιού-τιμής).

  • Κάθε κλειδί πρέπει να είναι μοναδικό, αλλά όχι απαραίτητα του ίδιου τύπου με τα άλλα.
  • Δεν μπορείτε να χρησιμοποιήσετε λίστες ή λεξικά ως κλειδιά.
  • Η τιμή που αντιστοιχεί σε ένα κλειδί μπορεί να είναι οποιουδήποτε τύπου.

Οι πιο κοινές λειτουργίες σε λεξικά δίνονται στη συνέχεια.

  • Δημιουργήστε ένα κενό ή ένα αρχικοποιημένο με μερικά στοιχεία λεξικό:
>>> d = {}
>>> da = { 'mike': 32, 'nick': 43 }
  • Προσθέστε ένα νέο ζεύγος κλειδιού-τιμής (αν υπάρχει το κλειδί, παίρνει τη νέα τιμή):
>>> da['stef'] = 67
>>> da['mike'] = 'oops'
>>> da
{'nick': 43, 'mike': 'oops', 'stef': 67}
  • Ελέγξτε αν ένα κλειδί υπάρχει στο λεξικό:
>>> if 'stef' in da:
...     print 'found'
...
found

Note

Η απόπειρα προσπέλασης ενός κλειδιού που δεν υπάρχει, προκαλεί σφάλμα εκτέλεσης! Πρέπει πάντα να εκτελείτε τον έλεγχο ύπαρξης του κλειδιού!

>>> da = { 'mike': 32, 'nick': 43 }
>>> da['stef']
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
KeyError: 'stef'
  • Προσπελάστε σε loop ένα-ένα τα ζεύγη κλειδιού-τιμής (η σειρά που επιστρέφονται τα στοιχεία δεν είναι ντετερμινιστική):
>>> for k,v in da.iteritems():
...     print k,v
...
nick 43
mike oops
stef 67

Note

Τα λεξικά είναι δομές ειδικά βελτιστοποιημένες για αναζήτηση (searching). Εάν θέλουμε να αναζητήσουμε πολύ γρήγορα ένα στοιχείο μέσα σε ένα μεγάλο σύνολο, η χρήση λεξικού είναι MUST!!!

Αρχεία Κειμένου

Αν ξέρουμε ότι το ένα αρχείο κειμένου δεν είναι μεγάλο, μπορούμε να το διαβάσουμε “με τη μία” σε ένα string:

fp = open('test.txt','r')

text = fp.read()
print text

fp.close()

Μπορούμε όμως με μεγαλύτερη ασφάλεια να το διαβάσουμε γραμμή προς γραμμή:

fp = open('test.txt','r')

for line in fp:
        print line

fp.close()

Στη δεύτερη περίπτωση:

  • Κάθε γραμμή κρατά το τελικό newline χαρακτήρα \n
  • Το αρχείο, ως προς την ανάγνωση, συμπεριφέρεται ως sequence από strings, έτσι μπορούμε να εφαρμόσουμε το επαναληπτικό for .. in .. που έχουμε ήδη δει.

Note

Duck Typing: “Αν περπατά σαν την πάπια και κάνει σαν την πάπια, τότε για μας είναι πάπια!”. Στις γλώσσες με δυναμικό τύπο δεδομένων, δεν είναι δυνατός ο έλεγχος εκ των προτέρων για το αν επιτρέπεται μια συγκεκριμένη λειτουργία σε ένα αντικείμενο. Αντί αυτού, η γλώσσα αρκείται στο ότι το αντικείμενο παρέχει τη συγκεκριμένη λειτουργία κατά τη στιγμή της εκτέλεσης, ανεξάρτητα από τον τύπο του. Αν π.χ. ένα αντικείμενο συμπεριφέρεται σαν λίστα ως προς την προσπέλαση των στοιχείων του, τότε μπορεί να χρησιμοποιηθεί σαν λίστα, ανεξάρτητα από το τι πραγματικά είναι.

Παράδειγμα: Συχνότητα Εμφάνισης Λέξεων

Ας εφαρμόσουμε τα προηγούμενα σε ένα συνοπτικό παράδειγμα. Υποθέστε ότι θέλουμε να διαβάσουμε ένα αρχείο κειμένου και να βρούμε τη συχνότητα εμφάνισης κάθε λέξης. Αυτό γίνεται εύκολα με ένα λεξικό:

#!/usr/bin/python
# -*- coding: UTF-8 -*-
"""
Sample app to calculate frequencies of words in a file
"""

# dictionary with words as keys
d = {}

fp = open('test.txt','r')

for line in fp:
	l = line.lower().split() # convert to lowercase
				 # then split on whitespaces
	for word in l:	# l is a list of words
		if word in d:	 # if word already in dict
			d[word] += 1	# incr counter
		else:	# word not in dict
			d[word] = 1	# 1st time
	
fp.close()

# print words and counts
for word,count in d.iteritems():
	print word,count

Note

Πιθανές βελτιώσεις: Θα παρατηρήσατε βέβαια ότι ο προηγούμενος κώδικας δεν είναι ολοκληρωμένος. Π.χ. τα σημεία στίξης θεωρούνται μέρος της λέξης. Όλα αυτά όμως μπορούν να διορθωθούν εύκολα όπως θα δούμε αργότερα!

Χρησιμοποιώντας βιβλιοθήκες: urllib

Μπορούμε να δώσουμε μια ‘διαδικτυακή χροιά’ στον προηγούμενο κώδικα. Με τη βιβλιοθήκη urllib μπορούμε να φέρουμε το περιεχόμενο μιας ιστοσελίδας σαν να διαβάζαμε ένα αρχείο! Χρειάζεται να αλλάξουμε μόνο μία γραμμή στον προηγούμενο κώδικα:

#!/usr/bin/python
# -*- coding: UTF-8 -*-
"""
Sample app to calculate frequencies of words in a web page
"""

import urllib

# dictionary with words as keys
d = {}

fp = urllib.urlopen("http://di.ionio.gr/msc/")

for line in fp:
	l = line.lower().split() # convert to lowercase
				 # then split on whitespaces
	for word in l:	# l is a list of words
		if word in d:	 # if word already in dict
			d[word] += 1	# incr counter
		else:	# word not in dict
			d[word] = 1	# 1st time
	
fp.close()

# print words and counts
for word,count in d.iteritems():
	print word,count

Για τους γνώστες της C/C++: θα ήταν λάθος να πιστεύουμε ότι το import είναι το αντίστοιχο του include της C/C++! Στην πραγματικότητα η βιβλιοθήκη που γίνεται imported ‘εκτελείται’ εκείνη τη στιγμή, δημιουργώντας τα αντικείμενα και τις συναρτήσεις της βιβλιοθήκης. Τα αντικείμενα αυτά μπορούμε να χρησιμοποιήσουμε στη συνέχεια στο πρόγραμμά μας.

Note

Αφαίρεση tags: Διαβάζοντας απευθείας από μια ιστοσελίδα έχουμε ένα νέο πρόβλημα: εκτός από τις λέξεις του κειμένου, παίρνουμε επίσης τα tags της HTML. Πώς θα αφαιρεθούν αυτά; (η συνέχεια στο επόμενο..)