Python: Address Book 3.1

...του ubuntu και έργων ΕΛ/ΛΑΚ (Έργα-Οδηγοί-Προτάσεις)

Συντονιστής: konnn

Re: Python: Address Book

Δημοσίευσηαπό pmav99 » 24 Αύγ 2011, 22:34

Πολύ πολύ abstractly κάτι τέτοιο:
Κώδικας: Επιλογή όλων

Class Contact(object):
self.id
self.name
self.surname
self.mobile
self.home
self.address
self.email

Class DataBase(object):
self.contacts = dict()

def connect(self):
pass
def query(self):
pass
def commit_changes(self):
pass
def add_contact(self):
pass
def del_contact(self):
pass
def edit_contact(self):
pass

Και μη χρησιμοποιείς globals.
pmav99
seniorTUX
seniorTUX
 
Δημοσιεύσεις: 574
Εγγραφή: 05 Ιούλ 2008, 14:29
Εκτύπωση

Re: Python: Address Book

Δημοσίευσηαπό Ilias95 » 24 Αύγ 2011, 22:41

Ilias95 έγραψε:
pmav99 έγραψε:Για την έκδοση 3 μπορείς να βάλεις σα στόχο να μετατρέψεις το πρόγραμμα από procedural σε Object Oriented. To πρόβλημα προσφέρεται.

Έτσι όπως το έχεις τώρα χρησιμοποιείς κατά κόρον global μεταβλητές που δεν είναι καλή πρακτική και δυνητικά μπορεί να οδηγήσει σε περίεργα bugs. Better be explicit than implicit. Καλύτερα να περνάς τις μεταβλητές σου σε κάθε συνάρτηση, παρά να ψάχνεσαι τι είναι τι και από που έρχεται


Εννοείς τις μεταβλητές που αφορούν τις επαφές να τις περάσω και αυτές μες τις κλάσεις; Δεν το σκέφτηκα έτσι. Θα το δοκιμάσω...
Οπότε και οι μεταβλητές θα γίνουν μεταβλητές κλάσεις ε;


Πάντως δυσκολεύομαι να δω πως θα γίνει. Ας πούμε μια συνάρτηση σαν την edit_contact() τι να την κάνω;
Η edit_contact() ζητάει πρώτα απ' το χρήστη να δώσει τον Α/Α μιας επαφής και ύστερα κάνει αλλαγές στις self.* μεταβλητές του. Όμως δεν μπορώ να την περάσω σαν μέθοδο στην κλάση ,καθώς δεν ξέρω από πριν ποια υπόσταση της κλάσης θα χρειαστώ.
Άρα έχω δύο επιλογές (ή όχι;). Η μία είναι να την περάσω μέσα στην κλάση σαν staticmethod και τις υπόλοιπες το ίδιο αλλά δεν θα χει και καμιά ουσιώδη διαφορά νομίζω.
Η άλλη είναι να την σπάσω σε δύο συναρτήσεις, μια που να ζητάει τον Α/Α και να είναι static ή classmethod (αλήθεια δεν κατάλαβα την διαφορά ακόμα), και στην δεύτερη να γίνετε η επεξεργασία και να είναι κανονικά μέθοδος της κλάσης.
Χμμ. Τι προτείνεις; :problem:

Edit: Πόσταρες ενώ έγραφα, κάτσε να το δω πρώτα για να αναθεωρήσω... :P
Ilias95
saintTUX
saintTUX
 
Δημοσιεύσεις: 1548
Εγγραφή: 29 Απρ 2011, 23:26
Εκτύπωση

Re: Python: Address Book

Δημοσίευσηαπό Ilias95 » 24 Αύγ 2011, 23:09

pmav99 έγραψε:Πολύ πολύ abstractly κάτι τέτοιο:
Κώδικας: Επιλογή όλων

Class Contact(object):
self.id
self.name
self.surname
self.mobile
self.home
self.address
self.email

Class DataBase(object):
self.contacts = dict()

def connect(self):
pass
def query(self):
pass
def commit_changes(self):
pass
def add_contact(self):
pass
def del_contact(self):
pass
def edit_contact(self):
pass

Και μη χρησιμοποιείς globals.


Η κλάση database δεν θέλει inheritance νομίζω. Οπότε οι συναρτήσεις τις θα είναι classmethod;
Ilias95
saintTUX
saintTUX
 
Δημοσιεύσεις: 1548
Εγγραφή: 29 Απρ 2011, 23:26
Εκτύπωση

Re: Python: Address Book

Δημοσίευσηαπό pmav99 » 24 Αύγ 2011, 23:38

Ξέχνα classmethod και staticmethod. Δεν έχουν εφαρμογή εδώ. Βασικά classmethod ούτε εγώ έχω καταλάβει πότε πρέπει να χρησιμοποιούνται.

Αν θυμάμαι καλά, όλες οι κλάσεις στην python 3 κληρονομούν από την βασική κλάση object. Αν παίξεις λίγο με το mro (method resolution order) θα το δεις. Στην python 3 τα δύο ακόλουθα είναι ισοδύναμα.
class Foo(object) :
class Foo:

Οι συναρτήσεις μιας κλάσεις, λέγονται "μέθοδοι" :P

Ξανάδιάβασε λίγο για αντικειμενοστραφή προγραμματισμό. Αν θελεις να κάνεις και GUI θα σε βοηθήσει. Βέβαια και το GUI θα σε βοηθήσει να καταλάβεις αντικειμενοστραφή καλύτερα. Αλλά ξεκινα από τα πιο βασικά. Αυτό που κάνεις τώρα είναι μια χαρά για να εφαρμόσεις αντικειμενοστραφείς έννοιες.
Κώδικας: Επιλογή όλων
class Contact(object):
def __init__(self, name, index):
super(Contact, self).__init__()

self.name = name
self.index = index

class AddressBook(object):
def __init__(self):
super(AddressBook, self).__init__()

self.contacts = {}

def add_contact(self, name, index):
if index not in self.contacts:
contact = Contact(name, index)
self.contacts[index] = contact
print("Added contact")
else:
print("A contact with the same index number already exists.")

def remove_contact(self, index):
if index in self.contacts:
self.contacts.pop(index)
print("Removed Contact")
else:
print("Couldn't find a contact with the given index number.")

def print_contacts(self):
for key, value in self.contacts.items():
print("Id : {0} - {1}".format(key, value.name))

def main():
my_book = AddressBook()
my_book.add_contact("Hlias", 1)
my_book.add_contact("Maria", 2)
my_book.add_contact("Giannis", 1)
my_book.print_contacts()
my_book.remove_contact(3)
my_book.remove_contact(2)
my_book.print_contacts()


if __name__ == "__main__":
main()

Κάτι τέτοιο εννοώ. Δεν χρησιμοποιώ την database, αλλά ένα dictionary για λόγους απλότητας. Η λογική θα είναι αντίστοιχη πάντως. Απλά αντί να προσθέτεις στο dict θα συνδέεσαι στη βάση και θα τα κάνεις από εκεί (add, remove κτλ).

Edit
----
Ιδίως αν σκοπεύεις να κάνεις γραφικό. Μη χάνεις χρόνο με τα Input Output από πληκτρολόγιο. Δώσε βάση στη λειτουργία του προγράμματος. Το τεστάρεις καλώντας μόνος σου τις μεθόδους από τη main(). Άλλο feature που μπορείς να βάλεις είναι ένα logger(κρατάς logμε τα transactions στη database). Επίσης για αυτό που θέλεις να κάνεις, η sqlAlchemy είναι μάλλον overkill και εισάγεις και βαρβάτο dependeny. Χρησιμοποίησε καλύτερα SQLite που είναι και στη standard library.
pmav99
seniorTUX
seniorTUX
 
Δημοσιεύσεις: 574
Εγγραφή: 05 Ιούλ 2008, 14:29
Εκτύπωση

Re: Python: Address Book

Δημοσίευσηαπό Ilias95 » 25 Αύγ 2011, 00:18

Ευχαριστώ πάρα πολύ για τον κόπο σου και για την αναλυτικότατη σου απάντηση.

Θα τα δω όλα καλύτερα με καθαρότερο μυαλό αύριο, αν και νομίζω ότι χάρη στη τελευταία σου δημοσίευση είμαι κοντά στο να καταλάβω περίπου πως θα κινηθώ.
Θα αφήσω στην άκρη προς το παρών τον γραφικό προγραμματισμό, θα κρατήσω και τα Input Output και θα κάνω την 3η έκδοση αντικειμενοστραφή όπως με προέτρεψες προηγουμένως.
Όσον αφορά την sqlalchemy θα την κρατήσω γιατί βαριέμαι να πειράξω και αυτό αυτή τη στιγμή... :P

Κάτι τελευταίο. Μπορείς να μου εξηγήσεις πρόχειρα τι ακριβώς κάνει η super();
Από την επεξήγηση που έχει στο επίσημο tutorial δεν καταλαβαίνω και πολλά... :(
Ilias95
saintTUX
saintTUX
 
Δημοσιεύσεις: 1548
Εγγραφή: 29 Απρ 2011, 23:26
Εκτύπωση

Re: Python: Address Book

Δημοσίευσηαπό pmav99 » 25 Αύγ 2011, 00:47

Σύντομη απάντηση: H super είναι syntactic sugar για να καλέσεις μία μέθοδο της υπερκλάσης σου.

Παράδειγμα :
Έχεις την υπερκλάση Super και την υποκλάση Sub. Η Sub κληρονομεί τις μεθόδους και τις ιδιότητες της Super. Έστω τώρα ότι η Super έχει μια μέθοδο με όνομα my_method. Η κλάση Sub κληρονομεί την my_method και μπορεί να την καλέσει (υπάρχει στο namespace της). Έστω τώρα ότι η my_method της Super δε σου κάνει για κάποιο λόγο και θέλεις να την αλλάξεις. Πας λοιπόν στο σώμα της Sub και την ξαναορίζεις.

Όλα καλά ώς εδώ. Αν όμως χρειαστείς να καλέσεις την my_method της Super τι κάνεις? Χρησιμοποιείς την super()!!! Το πιο χαρακτηριστικό παράδειγμα είναι η μέθοδοι __init__ (θυμίσου είναι οι "constructors") των κλάσεων. Όλες οι κλάσεις την έχουν. Χρησιμοποιώντας την super() στην __init__() της Sub, καλείς την __init__() της Super. Δεν είναι πάντα απαραίτητο αυτό, αλλά 99/100 είναι καλύτερα να το κάνεις (το σίγουρο είναι ότι δε χάνεις).

Προσπάθησε να καταλάβεις τον ακόλουθο κώδικα - τι θα εκτυπώσει. (μόνο για Python 3)
Κώδικας: Επιλογή όλων
class Super(object):
def __init__(self):
super().__init__()

print("I am at Super!!!")
self.my_method()

def my_method(self):
print("Super's my_method")

class Sub(Super):
def __init__(self):
super().__init__()

print("I am at Sub!!!")
self.my_method()
super().my_method()

def my_method(self):
print("Sub's my_method")

def main():
a = Sub()

if __name__ == "__main__":
main()


To output είναι αυτό:
Spoiler: show
Κώδικας: Επιλογή όλων
I am at Super!!!
Sub's my_method
I am at Sub!!!
Sub's my_method
Super's my_method

Αν ξέρεις να χρησιμοποιείς τον pdbθα σε βοηθήσει να δεις βήμα βήμα την πορεία εκτέλεσης.

Κάτι άλλο που κάνει η super είναι ότι βοηθάει να δουλέψει σωστά η πολλαπλή κληρονομικότητα (diamond inheritance).

Δες εδώκαι εδώ. Στο google θα βρεις πολύ (και καλύτερο) υλικό.
pmav99
seniorTUX
seniorTUX
 
Δημοσιεύσεις: 574
Εγγραφή: 05 Ιούλ 2008, 14:29
Εκτύπωση

Re: Python: Address Book

Δημοσίευσηαπό Ilias95 » 25 Αύγ 2011, 01:00

Ναι, κατάλαβα! Την απλή χρήση της τουλάχιστον. Να 'σαι καλά!
Ilias95
saintTUX
saintTUX
 
Δημοσιεύσεις: 1548
Εγγραφή: 29 Απρ 2011, 23:26
Εκτύπωση

Re: Python: Address Book

Δημοσίευσηαπό Ilias95 » 25 Αύγ 2011, 17:53

@pmav:
Τι λες για κάτι τέτοιο; https://github.com/Ilias95/Address-Book ... _adbook.py

Μετέτρεψα όλες τις συναρτήσεις εκτός απ' την menu() σε μεθόδους και δεν άφησα ούτε μια global μεταβλητή!
Και το πρόγραμμα δουλεύει ακριβώς όπως στην 2.2!
Ilias95
saintTUX
saintTUX
 
Δημοσιεύσεις: 1548
Εγγραφή: 29 Απρ 2011, 23:26
Εκτύπωση

Re: Python: Address Book

Δημοσίευσηαπό pmav99 » 25 Αύγ 2011, 20:39

εξακολουθείς να χρησιμοποιείς μεταβλητές από το global namespace. Πχ
Κώδικας: Επιλογή όλων
class Users:
"""Χρήστες του προγράμματος."""
database = ''
file = directory + 'users.data'
users_list = []
user = ''

def __init__(self):
if not os.path.exists(directory):
os.mkdir(directory)

Η κλάση που ξέρει τι είναι η directory? Πέρασε το σαν argument στην __init__. Παρομοίως και όπου αλλού το κάνεις. Άντε σε αυτό το μέγεθος, και τώρα που τα έχεις φρέσκα βγάζεις άκρη. Σε 6 μήνες?

Προσπάθησε να έχεις τα ονόματα στο local namespace. Ξαναδιάβασε λίγο για τα namespaces.

Kάντο με try - except
Κώδικας: Επιλογή όλων
if not os.system(command) == 0:
print('\nΚάτι πήγε στραβά. Η διαδικασία ακυρώθηκε.')
return
print('\nΑποθηκεύτηκε επιτυχώς στο {}'.format(file2))
pmav99
seniorTUX
seniorTUX
 
Δημοσιεύσεις: 574
Εγγραφή: 05 Ιούλ 2008, 14:29
Εκτύπωση

Re: Python: Address Book

Δημοσίευσηαπό Ilias95 » 25 Αύγ 2011, 20:47

Το try-except το είχα δοκιμάσει, αλλά δεν πιάνει το σφάλμα!
Επίσης φαίνεται και εδώ από ένα παράδειγμα που δοκίμασα στο φλοιό:
Κώδικας: Επιλογή όλων
>>> try:
... os.system('dfsa')
... except:
... print('ok')
...
sh: dfsa: not found
32512
>>>

Δεν εκτελείτε η print.
Τώρα διορθώνω και τις μεταβλητές.
Τις μεταβλητές users, act και tabase που αρχικοποιούν τις κλάσεις τις κρατάω global όμως. Σωστά;

Edit:
Ορίστε και με διορθωμένη την directory: https://github.com/Ilias95/Address-Book ... _adbook.py
Την έκανα μεταβλητή της κλάσης Users, βολεύει καλύτερα απ' το να την περάσω στην init. Άλλη μεταβλητή σε global namespace δεν υπάρχει, εκτός βεβαίως απ' τις 3 που ανέφερα και παραπάνω.
Διόρθωσα και το μήκος της κάθε γραμμής σε maximum 79, σύμφωνα με το PEP8.
Ilias95
saintTUX
saintTUX
 
Δημοσιεύσεις: 1548
Εγγραφή: 29 Απρ 2011, 23:26
Εκτύπωση

ΠροηγούμενηΕπόμενο

Επιστροφή στο Ανάπτυξη Λογισμικού / Αλγόριθμοι

cron