Έχουμε μάθει μέχρι σήμερα πώς να αυτοματοποιούμε γραμμικές εργασίες με τα προγράμματά μας. Το επόμενο βήμα είναι να δούμε πώς μπορούμε να τρέξουμε ένα πρόγραμμα σε όποια χρονική στιγμή επιλέξουμε εμείς. Το ρολόι του υπολογιστή, με το οποίο θα ασχοληθούμε στο σημερινό μάθημα, μας επιτρέπει να προγραμματίσουμε την εκτέλεση κώδικα σε συγκεκριμένο χρόνο ή σε τακτά χρονικά διαστήματα.
Προτάσεις συνεργασίας
Τα νέα άρθρα του PCsteps
Γίνε VIP μέλος στο PCSteps
Για να δείτε όλα τα μαθήματα Python από την αρχή τους, αλλά και τα επόμενα, αρκεί να κάνετε κλικ εδώ.
Module time
Το ρολόι συστήματος του υπολογιστή μας είναι ρυθμισμένο σε συγκεκριμένη ημερομηνία, ώρα, και ζώνη ώρας. Το module time, που έρχεται προεγκατεστημένο με την Python, δίνει τη δυνατότητα στο πρόγραμμά μας να διαβάσει το ρολόι για να ανακτήσει τον τρέχοντα χρόνο. Πάμε να δούμε τις πιο χρήσιμες συναρτήσεις του.
Time.time()
Μια ιστορική ώρα που χρησιμοποιείται εκτεταμένα στον προγραμματισμό είναι η λεγόμενη Unix epoch. Πρόκειται για την 1η Ιανουαρίου 1970, 12:00 πμ, (UTC – Παγκόσμια Συντονισμένη Ώρα).
Η συνάρτηση time.time() επιστρέφει τα δευτερόλεπτα που έχουν περάσει από τη συγκεκριμένη χρονική στιγμή, ως αριθμό float (δεκαδικό). Ο αριθμός που επιστρέφει λέγεται χρονοσφραγίδα Unix epoch.
Για παράδειγμα, ας καλέσουμε τη συνάρτηση στις 13 Ιανουαρίου, 2018, 04:30 μμ, (UTC +2):
>>> import time >>> time.time() 1515291891.8330798
Με τη βοήθεια χρονοσφραγίδων epoch μπορούμε τώρα να μετρήσουμε πόση ώρα απαιτεί η εκτέλεση ενός κομματιού κώδικα. Βλέπουμε ένα παράδειγμα με το παρακάτω πρόγραμμα:
Ορίζουμε αρχικά μια συνάρτηση, την calc_prod(), που διατρέχει όλους τους ακεραίους από τον 1 έως τον 99.999 και επιστρέφει το γινόμενό τους. Έπειτα καλούμε την time.time() δύο φορές: μία πριν και μία μετά την κλήση της calc_prod().
Τέλος, τυπώνουμε το μήκος του (τεράστιου) γινομένου, και την ώρα που πήρε το πρόγραμμα για να τον υπολογίσει, αφαιρώντας τον start_time από τον end_time.
Εκτελώντας, και αφού περιμένουμε μερικά δευτερόλεπτα, θα πάρουμε τις εξής εκτυπώσεις:
Time.sleep()
Αν θέλουμε να κάνουμε κάπου στο πρόγραμμά μας μια παύση, θα καλέσουμε τη συνάρτηση time.sleep(). Της περνάμε τον αριθμό δευτερολέπτων για τα οποία θέλουμε να σταματήσει το πρόγραμμα την εκτέλεσή του:
Τρέχοντας τις παραπάνω εντολές, θα δούμε ότι εμφανίζεται το πρώτο μήνυμα ‘Tick' και, μόνο αφού περάσει 1 δευτερόλεπτο, εμφανίζεται το αμέσως επόμενο, ‘Tock'.
Εκείνο που κάνει η time.sleep() είναι να εμποδίζει τη συνέχιση της εκτέλεσης του προγράμματος, μέχρι να περάσουν τα ορισμένα από εμάς δευτερόλεπτα. Γι' αυτό και στην τελική κλήση της time.sleep(), το prompt της επόμενης γραμμής (>>>) θα εμφανιστεί αφού περάσουν 5 δευτερόλεπτα.
Module datetime
Το module time και η χρονοσφραγίδα του Unix epoch που μας παρέχει, είναι μεν χρήσιμα, αλλά δεν εμφανίζουν σε βολική μορφή την ημερομηνία. Αν θέλουμε πιο κατανοητές ενδείξεις ή αν πρέπει να πραγματοποιήσουμε αριθμητικές πράξεις με ημερομηνίες, θα επιλέξουμε το datetime.
Για παράδειγμα, μπορεί να θέλουμε να μάθουμε την ημερομηνία που είχαμε πριν 232 μέρες ή αυτήν που θα έχουμε σε 150 μέρες από τώρα. Το datetime προσφέρεται για τέτοιους υπολογισμούς.
Το module έχει τον δικό του τύπο δεδομένων, τον datetime. Οι τιμές datetime αναπαριστούν μια συγκεκριμένη στιγμή στον χρόνο. Πάμε στον IDLE:
Καλούμε την datetime.datetime.now() για να πάρουμε ένα αντικείμενο datetime που περιέχει την τρέχουσα ημερομηνία και ώρα, σύμφωνα με το ρολόι του υπολογιστή. Το αντικείμενο αυτό περιλαμβάνει έτος, μήνα, ημέρα, ώρα, λεπτό, δευτερόλεπτο, και μικροδευτερόλεπτο για την τρέχουσα χρονική στιγμή.
Αν θέλουμε μια οποιαδήποτε άλλη ημερομηνία, χρησιμοποιούμε τη συνάρτηση datetime.datetime(), περνώντας της ως παραμέτρους τα παραπάνω (πλην του μικροδευτερολέπτου). Οι αριθμοί αυτοί αποθηκεύονται στα attribute του αντικειμένου, με τη σειρά: year, month, day, hour, minute, second, αντίστοιχα.
Για μετατροπές χρονοσφραγίδας Unix epoch σε datetime, έχουμε στη διάθεσή μας τη συνάρτηση datetime.datetime.fromtimestamp(). Για παράδειγμα:
Με την πρώτη εντολή παίρνουμε ένα αντικείμενο datetime για τη στιγμή 1.000.000 δευτερόλεπτα μετά την Unix epoch. Με τη δεύτερη, περνάμε την time.time() στη συνάρτηση, με αποτέλεσμα εκείνη να μας επιστρέψει ένα αντικείμενο datetime για την τρέχουσα (για εμάς) στιγμή.
Θα μπορούσαμε, δηλαδή, να θεωρήσουμε τις δυο παρακάτω εντολές ισοδύναμες:
datetime.datetime.now() datetime.datetime.fromtimestamp(time.time())
Σύγκριση αντικειμένων datetime
Μπορούμε να συγκρίνουμε τα αντικείμενα μεταξύ τους, με τη χρήση τελεστών σύγκρισης, για την εξακρίβωση του ποιο εκ των δύο προηγείται χρονικά. Αυτό που αφορά μεταγενέστερη χρονική στιγμή θεωρείται ότι έχει μεγαλύτερη τιμή.
Ας το δούμε στην πράξη με κάποιες απλές συγκρίσεις στο κέλυφος:
Τύπος δεδομένων Timedelta
Το datetime μας παρέχει έναν ακόμα τύπο δεδομένων, που αναπαριστά χρονική διάρκεια αντί της χρονικής στιγμής.
Γράφουμε στον IDLE τις ακόλουθες εντολές:
Για να δημιουργήσουμε ένα αντικείμενο timedelta, χρησιμοποιούμε τη συνάρτηση datetime.timedelta(), η οποία παίρνει ως ορίσματα: weeks, days, hours, minutes, seconds, milliseconds, microseconds.
Το γεγονός ότι δεν υπάρχει όρισμα month ή year, οφείλεται στη μεταβλητότητά τους ως χρονικές μονάδες, καθώς η διάρκειά τους εξαρτάται από τον εκάστοτε μήνα – 28 έως 31 ημέρες – ή έτος, όπου έχουμε απλά και δίσεκτα έτη.
Η συνολική διάρκεια δεδομένων timedelta αναπαρίσταται σε ημέρες, δευτερόλεπτα, και μικροδευτερόλεπτα.
Παράδειγμα προγράμματος
Έστω ότι θέλουμε να καταγράψουμε πόσο χρόνο ξοδεύουμε σε καθημερινές εργασίες μας, και ας υποθέσουμε ότι δεν έχουμε smartphone. Μπορούμε να γράψουμε μόνοι μας ένα απλό πρόγραμμα χρονομέτρησης.
Τι θα πρέπει να κάνει το πρόγραμμά μας? Εκείνο που θέλουμε είναι να:
- Καταγράφει τη χρονική διάρκεια μεταξύ πατημάτων του πλήκτρου ENTER. Κάθε νέο πάτημα του κουμπιού σηματοδοτεί έναν νέο γύρο στο χρονόμετρο.
- Τυπώνει τον αριθμό του γύρου, τον συνολικό χρόνο, και τον χρόνο του γύρου.
Αυτό σημαίνει ότι ο κώδικάς μας θα πρέπει να κάνει τα ακόλουθα:
- Βρίσκει τον τρέχοντα χρόνο με την time.time() και τον αποθηκεύει ως χρονοσφραγίδα στην αρχή του προγράμματος, αλλά και στην αρχή κάθε γύρου.
- Τηρεί έναν μετρητή γύρων, ο οποίος αυξάνεται με κάθε πάτημα του ENTER.
- Υπολογίζει τον χρόνο που έχει παρέλθει, αφαιρώντας χρονοσφραγίδες.
- Χειρίζεται την εξαίρεση KeyboardInterrup, ώστε ο χρήστης να μπορεί να πατήσει Ctrl-C για τερματισμό.
1ο Βήμα: Καταγραφή χρονικής στιγμής
Το πρόγραμμα θα πρέπει να χρησιμοποιεί τον τρέχοντα χρόνο, επομένως θα εισάγουμε το module time. Καλό είναι, επίσης, να τυπώνει σύντομες οδηγίες χρήσης πριν καλέσουμε την input(), ώστε το χρονόμετρο να ξεκινήσει αφού ο χρήστης πατήσει το ENTER.
Γράφουμε λοιπόν:
2ο Βήμα: Καταγραφή και εκτύπωση γύρων
Προχωράμε με το επόμενο κομμάτι κώδικα. Εδώ ξεκινάμε έναν νέο γύρο, υπολογίζουμε πόσο διήρκεσε ο προηγούμενος, και πόσος χρόνος έχει παρέλθει από την αρχή του χρονομέτρου.
Θα εμφανίσουμε, ακόμη, τον χρόνο του γύρου και τον συνολικό χρόνο, ενώ πρέπει να αυξάνουμε και τον μετρητή για κάθε νέο γύρο. Προσθέτουμε τα ακόλουθα:
Αν ο χρήστης πατήσει Ctrl-C, προκειμένου να τερματίσει το πρόγραμμα, θα πάρουμε ένα KeyboardInterrupt και το πρόγραμμα θα κρασάρει. Για τον λόγο αυτόν, τοποθετούμε μια δήλωση try-except, για να τερματίσει το πρόγραμμα κανονικά.
Μόλις πατηθεί ο συνδυασμός Ctrl-C, θα πάρουμε την εξαίρεση και ο κώδικας θα συνεχίσει με την εκτύπωση του αντίστοιχου μηνύματος που έχουμε στο except.
Ώσπου να συμβεί αυτό, η εκτέλεση θα βρίσκεται επ' αόριστον σε έναν ατέρμονο βρόχο, που καλεί την input() και περιμένει το πάτημα του ENTER για να τελειώσει τον κάθε γύρο.
Με την λήξη ενός γύρου, υπολογίζουμε την χρονική του διάρκεια αφαιρώντας τη last_time από την τρέχουσα στιγμή, time.time(). Τον συνολικό χρόνο τον υπολογίζουμε αφαιρώντας τη start_time, δηλαδή την στιγμή έναρξης του χρονομέτρου, από την τρέχουσα.
Τέλος, μιας και τα αποτελέσματα των υπολογισμών αυτών θα έχουν πολλά δεκαδικά ψηφία, κάνουμε χρήση της συνάρτησης round() για να στρογγυλοποιήσουμε στα δύο δεκαδικά ψηφία.
Η εκτύπωση, ανάλογα και με το πότε πατάμε Enter, θα είναι αντίστοιχη της παρακάτω:
Διαπιστώνουμε ότι στην εκτύπωση, στην οποία τυπώνουμε τον αριθμό του γύρου, τον συνολικό χρόνο και τη διάρκεια του γύρου, πρέπει να λάβουμε υπόψη και το ENTER που πατάει ο χρήστης.
Εφόσον το κάθε πάτημα φέρνει αυτομάτως μια νέα σειρά στην οθόνη, τοποθετούμε την έκφραση end=' ‘, για την αποφυγή διπλών κενών.
Έτσι, η ενδεικτική εκτύπωση θα έχει πλέον ως εξής:
Εκκίνηση τρίτων προγραμμάτων
Μέσω της Python μπορούμε να εκκινήσουμε άλλα προγράμματα του υπολογιστή μας, με χρήση της συνάρτησης Popen() του module subprocess. Η Popen() καλεί μια νέα διαδικασία, με την οποία εκκινούμε το πρόγραμμα που θέλουμε.
Μπορούμε, μάλιστα, να ανοίξουμε περισσότερες από μία φορές το ίδιο πρόγραμμα, πχ να ανοίξουμε πολλά παράθυρα του browser μας. Κάθε ένα από αυτά αποτελεί μια ξεχωριστή διαδικασία (process).
Πάμε, για παράδειγμα, να ανοίξουμε την αριθμομηχανή. Αρκεί να γράψουμε:
…και θα δούμε ότι αυτόματα μεταφερόμαστε στο περιβάλλον της εφαρμογής Αριθμομηχανή:
Μέθοδοι poll() και wait()
Αυτό που επιστρέφει η Popen() είναι ένα αντικείμενο Popen, το οποίο έχει δυο χρήσιμες μεθόδους, την poll() και την wait().
Η poll() επιστρέφει None αν η διαδικασία τρέχει ακόμα κατά την στιγμή κλήσης της poll(). Αν το πρόγραμμα έχει τερματιστεί, θα επιστρέψει ένα κωδικό εξόδου της διαδικασίας.
Ο κωδικός εξόδου χρησιμοποιείται για να μας ενημερώσει αν η διαδικασία τελείωσε χωρίς σφάλματα (κωδικός 0) ή αν κάποιο σφάλμα οδήγησε σε πρόωρο τερματισμό (κωδικός διάφορος του 0, συνήθως 1).
Η wait() θα εμποδίσει την συνέχιση εκτέλεσης του κώδικα μέχρι η διαδικασία που έχουμε εκκινήσει να τερματιστεί. Αυτό μπορεί να φανεί χρήσιμο αν πρέπει το πρόγραμμα να παύσει, μέχρις ότου ο χρήστης να τελειώσει την εργασία του με κάποιο άλλο πρόγραμμα.
Μπορούμε, ακόμα, να περάσουμε επιπλέον ορίσματα στην Popen(). Στην περίπτωση αυτή, το πρώτο όρισμα-string είναι το εκτελέσιμο αρχείο που θα εκκινήσουμε. Το δεύτερο (ή περισσότερα) string είναι ορίσματα γραμμής εντολών που περνάνε στο πρόγραμμα που εκκινούμε, η τιμή δηλαδή που θα λάβει η sys.argv.
Ας δούμε τα παραπάνω με ένα παράδειγμα. Εδώ, δεν ανοίγουμε μόνο την εφαρμογή notepad, αλλά ταυτόχρονα της λέμε να ανοίξει το αρχείο hello.txt. Αν δεν υπάρχει, θα δημιουργηθεί επί τόπου.
Προγραμματισμένη εκκίνηση τρίτου προγράμματος
Συνδυάζοντας τώρα την κλήση ενός τρίτου προγράμματος, με τα module με τα οποία χειριζόμαστε τον χρόνο, μπορούμε να ρυθμίσουμε την εκκίνησή του σε μεταγενέστερη χρονική στιγμή.
Για παράδειγμα, έχουμε τον εξής κώδικα:
Με το πρόγραμμα αυτό, προγραμματίζουμε το άνοιγμα της εφαρμογής notepad, μόλις παρέλθει η χρονική στιγμή 14:31, 13 Ιανουαρίου 2018. Χρησιμοποιούμε μια συνθήκη if που ελέγχει αν η τρέχουσα ώρα είναι μεγαλύτερη της ώρας στην οποία θέλουμε να γίνει εκκίνηση του notepad.
Για να μην καταναλώνουμε πόρους, μπορούμε να χρησιμοποιήσουμε την time.sleep(), περνώντας ως όρισμα τα δευτερόλεπτα μετά τα οποία θα ξαναελέγξει το πρόγραμμα αν έχει έρθει η καθορισμένη από εμάς ώρα. Όλα τα παραπάνω μπαίνουν εντός ενός βρόχου που είναι μόνιμα αληθής μέχρις ότου λάβει χώρα το break.
Μπορούμε τώρα να συνεχίσουμε τις εργασίες μας στον υπολογιστή. Στην αντίστοιχη χρονική στιγμή, θα δούμε το notepad να ανοίγει μόνο του, το αρχείο hello.txt (ή, αν δεν υπάρχει, να μας ρωτάει αν επιθυμούμε τη δημιουργία του).
Στο επόμενο μάθημα για τον προγραμματισμό Python
Μπαίνουμε στην τελευταία ενότητα με την οποία θα ασχοληθούμε στα πλαίσια των μαθημάτων για Python. Θα μάθουμε πώς μπορούμε να χειριστούμε μέσω της Python το ποντίκι και το πληκτρολόγιο του συστήματός μας.
Σας άρεσε το σημερινό μάθημα για τον προγραμματισμό Python?
Γράψτε μας στα σχόλια για οποιοδήποτε πρόβλημα ή απορία σας.
Για να δείτε όλα τα μαθήματα Python από την αρχή τους, αλλά και τα επόμενα, αρκεί να κάνετε κλικ εδώ.