Προς το περιεχόμενο

Προτεινόμενες αναρτήσεις

  • Απαντ. 37
  • Δημ.
  • Τελ. απάντηση

Συχνή συμμετοχή στο θέμα

Δημοσ.

Μπράβο!

 

Γιατί όμως;

Το αναφέρω γιατί είναι ένα χαρακτηριστικό που ορισμένοι δεν το γνωρίζουν. Πολλές συναρτήσεις χρησιμοποιούν την ίδια μεταβλητή

Δημοσ.

Μπορείς κάλιστα να χρησιμοποιήσεις global variables για αυτή τη δουλειά ή ακόμα καλύτερα να περνάς τις μεταβλητές μεταξυ των functions. Μπορείς ακόμα και να κάνεις όλο το πράγμα wrap σε ένα class και να χρησιμοποιείς instance variables. Άλλα να χρησιμοποιείς nested functions και non-local είναι ο πλέον κουφός/χωρίς λόγο/με περιττή πολυπλοκότητα τρόπος να υλοποιήσεις αυτό που θέλεις.

  • Like 1
Δημοσ.

iceblade:

Για μένα δεν είναι πολύπλοκο, το βρίσκω αρκετά απλό όπως θα ήταν και με global.(Αντί να γράφω nonlocal θα έγραφα global όχι μεγάλη διαφορά)

Αυτό που λές να περνάω τις μεταβλητές μεταξύ των functions δεν ξέρω τι εννοείς. Αν εννοείς με arguments και επιστρεφόμενες return τιμές το βρίσκω πολύ πολύπλοκο. 

Με class προσπαθώ να το κάνω τώρα, απλά τις κλάσεις δεν τις κατέχω ούτε τις έχω δουλέψει ιδιάιτερα και σε συνδυασμό το γραφικό περιβάλλον μου είναι κάπως δυσκόλο.

Δημοσ.

iceblade:

Για μένα δεν είναι πολύπλοκο, το βρίσκω αρκετά απλό όπως θα ήταν και με global.(Αντί να γράφω nonlocal θα έγραφα global όχι μεγάλη διαφορά)

Αυτό που λές να περνάω τις μεταβλητές μεταξύ των functions δεν ξέρω τι εννοείς. Αν εννοείς με arguments και επιστρεφόμενες return τιμές το βρίσκω πολύ πολύπλοκο. 

Με class προσπαθώ να το κάνω τώρα, απλά τις κλάσεις δεν τις κατέχω ούτε τις έχω δουλέψει ιδιάιτερα και σε συνδυασμό το γραφικό περιβάλλον μου είναι κάπως δυσκόλο.

 

Υπάρχει λόγος για τον οποίο όταν σχεδιάζουμε κάτι γράφουμε τα functions έτσι ώστε να μη στηρίζονται στο global state του προγράμματος. Αυτός είναι ότι τα μεγάλα προγράμματα είναι επιρρεπή σε bugs όταν οι συναρτήσεις εξαρτώνται σε μεγάλο βαθμό από το global state και το αλλάζουν. Το ίδιο πρόβλημα θα έχεις και στο πρόγραμμα σου αν γίνει αρκετά μεγαλύτερο. Φυσικά τα προβλήματα αυτά δε φαίνονται σε scripts των 100-200 γραμμών. Υπάρχει λόγος για τον οποίο περνάμε arguments μέσα στα functions, αλλιώς θα γράφαμε όλα τα functions χωρίς arguments με global variables. Το ότι σου φαίνεται πολύπλοκο είναι γιατί ακόμα δεν έχεις τις απαραίτητες γνώσεις (πράγμα απολύτως θεμιτό και αυτά στα γράφω για να σου δώσω το έναυσμα να το ψάξεις περισσότερο).

Η χρήση του non-local είναι κυρίως σε closures και καμία σχέση δεν έχει με αυτό που κάνεις εδώ, που στην ουσία έχεις ένα function που αντικαθιστά το global scope. Θα μπορούσαμε αντί να γράφαμε τα functions/classes/etc σε module level (i.e. global) να τα γράφαμε όλα μέσα σε ένα function και να κάναμε bind με non-local όπως κάνεις εδώ, αλλά σκέψου γιατί δε το έχεις δει πουθενά αυτό εκτός από το πρόγραμμα σου.

  • Like 1
Δημοσ.

Κάθε φορά που συναντάς ένα nonlocal και ακόμα περισσότερο ένα global θα πρέπει να σταματάς για να δεις τι τιμή έχει καθώς και τι τιμή αφήνει.

 

Τώρα έγραψες ένα script-άκι των 100 γραμμών. Είναι εύκολο να έχεις στο νού σου τι ειναι τι.

 

Αν όμως έχεις κάτι που είναι 1000+ γραμμές (δε συζητώ καν για 10Κ κτλ), όπου η ροή του προγράμματος δεν είναι γραμμική, όπου κάποιες συναρτήσεις μπορεί να καλούνται πολλές φορές, όπου διαφορετικές συναρτήσεις μπορεί να χρησιμοποιούν global για την ίδια μεταβλητή, εκεί θα βγάζεις άκρη;

 

Ένα άλλο πρόβλημα που μπορεί να έχεις με globals είναι πχ αυτό:

 

 

my_global_var = True
def function_foo():
    global my_global_var
    my_global_var = False
 
def main():
    global my_global_var
    for i in range(3):
        my_global_var = (i % 2 == 0)
        if i == 2:
            function_foo()
        print(i)
        print(my_global_var)
 
main()
 

Η κλήση στη function_foo() αλλάζει την τιμή της my_global_var. Όταν κοιτάς τη main() μπορεί να μη θυμάσαι τι κάνει ακριβώς η function_foo() με αποτέλεσμα να περιμένεις ότι το τελευταίο print() θα τυπώσει True.

 

Αν όμως είχες γράψει το εξής, δεν θα υπήρχε καμία πιθανότητα παρερμηνείας:

 

my_global_var = True

def function_foo():
    return False
 
def main():
    global my_global_var
    for i in range(3):
        my_global_var = (i % 2 == 0)
        if i == 2:
            my_global_var = function_foo()
        print(i)
        print(my_global_var)
 
main()
 

Γενικά explicit is better than implicit

 

Προσωπικά μία φορά στη ζωή μου χρησιμοποίησα global και αυτό όταν χρειάστηκε να διορθώσω ένα bug σε ένα module 2.5Κ loc κακογραμμένου κώδικα. Γινόταν να το αποφύγω, αλλά έπρεπε να αλλάξω σχεδόν όλα τα function calls. Γενικά, αν δεν υπάρχουν εξαιρετικοί λόγοι που να το δικαιολογούν απέφευγέ το.

 

 

Κάνε αυτό που σου λέει ο iceblade. Γράψτο σε καθαρά procedural μορφή και μετά ξαναγράψτο με κλάσεις. Είναι καλή εξάσκηση.

  • Like 2
Δημοσ.

To non local υπάρχει σε πολλές γλώσσες. είναι σαν το Private, το οποίο φαίνεται σε ένα module της VB6 σε όλες τις συναρτήσεις αλλά όχι σε άλλα modules (θα πρέπει να γίνει Public). Όμως η VB6 δεν έχει ορισμούς functions μέσα σε functions.

Εδώ υπάρχουν περιπτώσεις που μια μεταβλητή έξω από τον ορισμό μιας συνάρτησης να είναι θεατή παρόλο που και αυτή δεν είναι global. Πχ στη java έχουμε τις class level scope. Στην javascript αν δηλωθεί μια μεταβλητή με τιμή αμέσως γίνεται global, an δηλωθεί με var τότε γίνεται Local, αν είναι μέσα σε Link.png Site: function

 

Στην Python δεν μου αρέσει οπτικά το θέμα, αλλά λειτουργικά αν δουλεύει είναι όλα οκ. Δεν συμμερίζομαι το ζήτημα με το "που πήγε η Global" γιατί και να μην χρησιμοποιεί κανείς global (που είναι αδύνατον σε μεγάλα προγράμματα), πάλι λάθη θα γίνονται. Εκτός βέβαια αν κάποιος φτιάχνει στο χαρτί το κώδικα και βρίσκει όλα τα λάθη με το νου του. Αυτό δεν ξέρω ποιος το πετυχαίνει!

Δημοσ.

To non local υπάρχει σε πολλές γλώσσες. είναι σαν το Private, το οποίο φαίνεται σε ένα module της VB6 σε όλες τις συναρτήσεις αλλά όχι σε άλλα modules (θα πρέπει να γίνει Public).

Καμία σχέση το nonlocal της Python με το Private της VB6

 

Αφού πάλι δεν έχεις ιδέα. Γιατί πετάγεσαι;

  • Like 4
Δημοσ. (επεξεργασμένο)

To non local υπάρχει σε πολλές γλώσσες. είναι σαν το Private, το οποίο φαίνεται σε ένα module της VB6 σε όλες τις συναρτήσεις αλλά όχι σε άλλα modules (θα πρέπει να γίνει Public). Όμως η VB6 δεν έχει ορισμούς functions μέσα σε functions.

Εδώ υπάρχουν περιπτώσεις που μια μεταβλητή έξω από τον ορισμό μιας συνάρτησης να είναι θεατή παρόλο που και αυτή δεν είναι global. Πχ στη java έχουμε τις class level scope. Στην javascript αν δηλωθεί μια μεταβλητή με τιμή αμέσως γίνεται global, an δηλωθεί με var τότε γίνεται Local, αν είναι μέσα σε Link.png Site: function

 

Στην Python δεν μου αρέσει οπτικά το θέμα, αλλά λειτουργικά αν δουλεύει είναι όλα οκ. Δεν συμμερίζομαι το ζήτημα με το "που πήγε η Global" γιατί και να μην χρησιμοποιεί κανείς global (που είναι αδύνατον σε μεγάλα προγράμματα), πάλι λάθη θα γίνονται. Εκτός βέβαια αν κάποιος φτιάχνει στο χαρτί το κώδικα και βρίσκει όλα τα λάθη με το νου του. Αυτό δεν ξέρω ποιος το πετυχαίνει!

 

Μη πετάς ότι νάναι όπου νάναι.

 

 

iceblade

Ναι, κανονικά δεν θα το έκανα έτσι, αλλά με globals. Το έκανα για να δείξω απλώς τι κάνουν τα nonlocal

Τα arguments και functions με return απλά εμένα δεν μου ήταν εύκολα στη συγκεκριμένη περίπτωση όχι όμως ότι δεν τα χρησιμοποιώ γενικώς.

 

Έχω κάνει και μια version με class λειτουργική μεν αλλά δεν είμαι σίγουρος ότι είναι ο σωστός τρόπος

στην ουσία έγραψα 100 φορές τη λέξη self. Αν θές πες μου προτάσεις βελτίωσης.

 

Μερικές παρατηρήσεις:

1) Υπάρχει κάποιος λόγος που κληρονομείς από την ttk.Frame? Ποιά ακριβώς λειτουργικότητα της επεκτείνεις; Δεν έχω ασχοληθεί με Tk, οπότε μπορεί να μη καταλαβαίνω κάτι αλλά βλέπω ότι μεσα στον constructor κάνεις self.main_frame=ttk.Frame(parent, padding='5 5 5 5')#Κάνω΄ ένα βασικό frame ως instance variable και μετά παίζεις με αυτό, οπότε γιατί κληρονομείς από το ttk.Frame?

2) Γενικά το from something import * πρέπει να αποφέυγεται. Σε μεγαλύτερα προγράμματα είναι εγγυημένο ότι θα έχεις name clashes. Κάνεις import ακριβώς αυτά τα οποία χρειάζεσαι και τίποτα παραπάνω.

3) Δεν έχει αλλάξει κάτι ουσιαστικό με το να κάνεις wrap σε κλάση από την προηγούμενη υλοποίηση. Όποιες μεταβλητές χρησιμοποιούνται μόνο σε συγκεκριμένα functions δεν πρέπει να είναι global (ή instance στη συγκεκριμένh περίπτωση. πχ φτιάξε ένα function εκεί που λες στην updatet() ότι #υπολογίζω τον χρόνο που περνάει και τον μετατρέπω σε ώρες κλπ, που παίρνει τις ώρες, λεπτά, microsecs η οτιδήποτε και επιστρέφει αυτά που χρειάζονται. Η λογική των συναρτήσεων είναι να παίρνουν τιμές ως είσοδο και να επιστρέφουν συνήθως κάτι, όχι μόνο για να ομαδοποιείς κομμάτια κώδικα όπως έχεις κάνει εσύ (είναι ΚΑΙ αυτό όμως).

4) try, except, pass δεν κάνουμε ποτέ είναι σα να λέμε στο πρόγραμμα άμα γίνει η στραβή κάνε την παλαβή. Έχει βγει και χιουμοριστικό module για αυτό. Το Zen λέει: Errors should never pass silently. Unless explicitly silenced.

 5) Γενικά πρέπει να μπεις στη λογική ότι τα functions πρέπει κυρίως να υπολογίζουν και να επιστρέφουν πράγματα και να μην αλλάζουν σε μεγάλο βαθμό το global state. Αυτό ισχύει και μέσα στις κλάσεις δηλ, παρότι τα methods φυσικά και επιδρούν πάνω σε instance variables δεν είναι μόνο αυτός ο ρόλος τους, δηλαδή μπορείς να έχεις μέσα στην κλάση helper functions (τα οποία συνήθως τα κάνεις mark ως private) τα οποία κάνουν υπολογισμούς από μεταβλητές που δεν ανήκουν στην κλάση ή στο αντικείμενο αλλά χρησιμοποιούνται σε "ενδιάμεσους" υπολογισμούς.

Επεξ/σία από iceblade
  • Like 1
Δημοσ.

Δέχομαι να μου εξηγήσει κανείς την διαφορά, αλλά το "μη πετάγεσαι" δεν είναι παρά απόδειξη ότι δεν κατάλαβε τι έγραψα και γράφει ό,τι να είναι για να γράφει!

 

@Icablade δεν ξέρω που βασίζεσαι και λες στον άλλο ότι το error trapping είναι κακό! Δεν μας ενδιαφέρει να κρατήσουμε μια εφαρμογή όρθια με περίπλοκες τεχνικές αλλά να μπορεί να αποθηκεύσει ότι χρειάζεται, και να αν είναι δυνατόν να πληροφορήσει το χρήστη, και έμμεσα τον προγραμματιστή. Μιλάμε για runtime errors, τα οποία μπορεί να είναι κάτι το απλό, που αν δεν το διευθετήσεις να ρίξεις την εφαρμογή. Δεν κοιτάμε το πρόγραμμα να κάνει την "παλαβή" αλλά να εξυπηρετήσει τον χρήστη. Η περίπτωση που δεν γίνεται να γυρίσει κανείς λέγεται Fatal Error.

Δημοσ.

Δέχομαι να μου εξηγήσει κανείς την διαφορά, αλλά το "μη πετάγεσαι" δεν είναι παρά απόδειξη ότι δεν κατάλαβε τι έγραψα και γράφει ό,τι να είναι για να γράφει!

 

@Icablade δεν ξέρω που βασίζεσαι και λες στον άλλο ότι το error trapping είναι κακό! Δεν μας ενδιαφέρει να κρατήσουμε μια εφαρμογή όρθια με περίπλοκες τεχνικές αλλά να μπορεί να αποθηκεύσει ότι χρειάζεται, και να αν είναι δυνατόν να πληροφορήσει το χρήστη, και έμμεσα τον προγραμματιστή. Μιλάμε για runtime errors, τα οποία μπορεί να είναι κάτι το απλό, που αν δεν το διευθετήσεις να ρίξεις την εφαρμογή. Δεν κοιτάμε το πρόγραμμα να κάνει την "παλαβή" αλλά να εξυπηρετήσει τον χρήστη. Η περίπτωση που δεν γίνεται να γυρίσει κανείς λέγεται Fatal Error.

 

Δεν είπα πουθενά ότι το "error trapping" είναι κακό, είπα πρέπει να χειρίζεσαι τα exceptions explicitly και όχι να τα πιάνεις και να κάνεις pass, ως συνήθως δεν έχεις την παραμικρή ιδέα. Το ότι μπαίνεις σε κάθε topic και λες την παπαριά σου χωρίς να καταλαβαίνεις Χριστό και σου το έχουν επισημάνει κ σου το λένε ανά ημέρα πόσα άτομα πε πολύ περισσότερες γνώσεις από εσένα (ΟΚ εξαιρώντας τη Μ2000) θα έπρεπε τουλάχιστο να σε προβληματίσει, έστω και λίγο.

  • Like 1
Δημοσ.

Iceblade, δεν υπάρχει λόγος να ασχολείσαι. Έχουν μείνει το πολύ 2 άνθρωποι σε αυτή την ενότητα που ακόμα απαντάνε στα posts του.

 

Αφού βλέπεις... δεν. Γιατί ασχολείσαι;

  • Like 2
Δημοσ.

Μη πετάς ότι νάναι όπου νάναι.

 

 

Μερικές παρατηρήσεις:

1) Υπάρχει κάποιος λόγος που κληρονομείς από την ttk.Frame? Ποιά ακριβώς λειτουργικότητα της επεκτείνεις; Δεν έχω ασχοληθεί με Tk, οπότε μπορεί να μη καταλαβαίνω κάτι αλλά βλέπω ότι μεσα στον constructor κάνεις self.main_frame=ttk.Frame(parent, padding='5 5 5 5')#Κάνω΄ ένα βασικό frame ως instance variable και μετά παίζεις με αυτό, οπότε γιατί κληρονομείς από το ttk.Frame?

2) Γενικά το from something import * πρέπει να αποφέυγεται. Σε μεγαλύτερα προγράμματα είναι εγγυημένο ότι θα έχεις name clashes. Κάνεις import ακριβώς αυτά τα οποία χρειάζεσαι και τίποτα παραπάνω.

3) Δεν έχει αλλάξει κάτι ουσιαστικό με το να κάνεις wrap σε κλάση από την προηγούμενη υλοποίηση. Όποιες μεταβλητές χρησιμοποιούνται μόνο σε συγκεκριμένα functions δεν πρέπει να είναι global (ή instance στη συγκεκριμένh περίπτωση. πχ φτιάξε ένα function εκεί που λες στην updatet() ότι #υπολογίζω τον χρόνο που περνάει και τον μετατρέπω σε ώρες κλπ, που παίρνει τις ώρες, λεπτά, microsecs η οτιδήποτε και επιστρέφει αυτά που χρειάζονται. Η λογική των συναρτήσεων είναι να παίρνουν τιμές ως είσοδο και να επιστρέφουν συνήθως κάτι, όχι μόνο για να ομαδοποιείς κομμάτια κώδικα όπως έχεις κάνει εσύ (είναι ΚΑΙ αυτό όμως).

4) try, except, pass δεν κάνουμε ποτέ είναι σα να λέμε στο πρόγραμμα άμα γίνει η στραβή κάνε την παλαβή. Έχει βγει και χιουμοριστικό module για αυτό. Το Zen λέει: Errors should never pass silently. Unless explicitly silenced.

 5) Γενικά πρέπει να μπεις στη λογική ότι τα functions πρέπει κυρίως να υπολογίζουν και να επιστρέφουν πράγματα και να μην αλλάζουν σε μεγάλο βαθμό το global state. Αυτό ισχύει και μέσα στις κλάσεις δηλ, παρότι τα methods φυσικά και επιδρούν πάνω σε instance variables δεν είναι μόνο αυτός ο ρόλος τους, δηλαδή μπορείς να έχεις μέσα στην κλάση helper functions (τα οποία συνήθως τα κάνεις mark ως private) τα οποία κάνουν υπολογισμούς από μεταβλητές που δεν ανήκουν στην κλάση ή στο αντικείμενο αλλά χρησιμοποιούνται σε "ενδιάμεσους" υπολογισμούς.

1. Μου έβγαζε λάθος στο pack του αντικειμένου win=xronometro()  win.pack και σκέφτηκα να το κληρονομήσω από κάπου, άν υπάρχει κάποια άλλη ιδέα πως να το πάρω οκ

 

2. Ναι σωστό. Απλά στο tkinter είναι κάτι στανταρ γιαυτό το έβαλα έτσι. Και από το time θα μπορούσα να εισάγω μόνο το .time και από το ττκ μόνο label, button και Text το κάνω για ταχύτητα ορισμένες φορές και για να είναι πιο εύκολο να προσθέσω κάποιο στοιχείο χωρίς να αλλάζω και τα import

 

3. Ναι, δεν κατάλαβα τι έννοεις για την updatet (τα σχόλια δεν τα πρόσεξα ιδιαίτερα). Οι συναρτήσεις μου παίρνουν είσοδο και βγάζουν έξοδο απλά όχι explicitly αλλά implicitly :). Εννοώ παίρνουν time.time() στην ουσία και στέλνουν την επιστροφή στα Text και Labels. Θα κοιτάξω να κάνω κάτι καλύτερο. Στην init έβαλα πολλά γιατί θέλω όλα να γίνουν με την δημιουργία του αντικειμένου ναι μπορώ να τα βάλω σε μία συνάρτηση και να την καλώ στην init

 

4. Αυτό το έκανα γιατί στον 1ο κύκλο δεν υπάρχει start_time[1] και πετάει λάθος δεν με ενδιάφερε όμως ο χειρισμός του απλά να συνεχίσει , στον δεύτερο κύκλο και να σβήνει το start_time[1] εφόσον υπάρχει, ήταν κάτι γρήγορο που σκέφτηκα, αν έχεις άλλη ιδέα χειρισμού οκ

 

Κλάσεις έχω γράψει πολύ λίγες και αυτή ήταν η 1η που είχε σχέση με γραφικά. Έχω γράψει παλιότερα σε javascript για γραφικά πάλι αλλά εκεί ήταν πιο ξεκάθαρες

 

Σε κάθε περίπτωση ευχαριστώ για τα σχόλια με βοήθησαν να δω κάποια πράγματα

 

Έχω διορθώσει και τον κώδικα της class σε κάποια σημεία 

  • Like 1

Δημιουργήστε ένα λογαριασμό ή συνδεθείτε για να σχολιάσετε

Πρέπει να είστε μέλος για να αφήσετε σχόλιο

Δημιουργία λογαριασμού

Εγγραφείτε με νέο λογαριασμό στην κοινότητα μας. Είναι πανεύκολο!

Δημιουργία νέου λογαριασμού

Σύνδεση

Έχετε ήδη λογαριασμό; Συνδεθείτε εδώ.

Συνδεθείτε τώρα

  • Δημιουργία νέου...