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

python 3.2.3 + tkinter + cx_freeze


migf1

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

  • γιατί δεν σώζεις σε utf8?
  • Tα "%s" που έχεις μέσα στο MSGBOX_HELP_TΕΧΤ είναι για string formatting? Αν ναι τότε οπωσδήποτε χρησιμοποίησε τη μέθοδο format() με keywords. Πχ

    string = "My name is {first_name} {last_name}"
    print(string.format(first_name=first_name, last_name=last_name))
    # ή αν τα έχεις σε ένα dict
    print(string.format(**name_data))
  • Αυτό που κάνεις με το klang μάλλον μπορεί να υλοποιηθεί καλύτερα με property. Εδώ τα εξηγεί αλλά ίσως αυτό να είναι πιο ευκολονόητο. Για την ακρίβεια το καλύτερο παράδειγμα που βρίσκω τώρα για properties είναι αυτό το βίντεο (από το λεπτό 4.00 μέχρι 10.00). Είναι λίγο πιο advanced αυτό. Για την ώρα θα έλεγα να μην ασχοληθείς να τα αλλάξεις. Δεν αξίζει τον κόπο.

για τα config αύριο

Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

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

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

1. Επειδή είμαι σε Widnows και η κονσόλα του δεν έχει utf8 υποστήριξη (και βασικά βαρέθηκα να βάλω άλλη κονσόλα)... δεν προγραμματίζv σε IDLE, ούτε σε κάποιο Python IDE... με notepad++ γράφω τον κώδικα, και με command-line script κάνω interpret & execute μέσα από το notepad++

 

2. Ναι, string formatting + internationalization είναι. Οκ, θα κοιτάξω να το αλλάξω. Thanks.

 

3. Thanks, για τα links... θα τα δω κι εγώ από αύριο (ή πιο πιθανό μέσα στο Σ/Κ)

 

Για, το config: ok, thanks!

Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

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

αναφέρεις πως ο τρόπος για να κάνεις οτιδήποτε πάνω σε ένα iterable είναι με: for i in iterable: αλλά αν το iterable είναι dictionary οι τρόποι που δείχνεις ως pythonic δουλεύουν με τα keys και όχι με τα values.

 

O λόγος που αναφέρω το «for item in iterable» ως τον ενδεδειγμένο τρόπο για να διατρέξεις ένα iterable είναι γιατί δουλεύει πάντα και γιατί είναι το πιο ευανάγνωστο από όλα. Τα comprehensions όπως είπα έχουν νόημα μόνο όταν θες να δημιουργήσεις ένα list/dict/set και μόνο όταν οι συνθήκες για τη δημιουργία τους είναι απλές. Όταν ισχύουν αυτοί οι δύο όροι τότε συχνά είναι πιο readable ο κώδικας με comprehensions για αυτό και τον προτιμούμε. Αλλά το for item in foo δουλεύει πάντα χωρίς προβλήματα.

 

Αν τώρα με το παραπάνω ρωτούσες γιατί στο

for item in dictionary:
    print(item)

το item αντιστοιχεί με το key, τότε νομίζω ότι αυτό γίνεται καθαρά γιατί μέσω του key μπορούμε να πάρουμε το value (αν το χρειαστούμε).

 

Αν θελήσεις να δουλέψεις με τα keys, πρέπει να αποφασίσεις αν θα το κάνεις μέσα στο #do whatever ή αν π.χ. θα χρησιμοποιήσεις μεθόδους όπως οι items, iteritems, keys κλπ οι οποίες όμως από ότι διαβάζω συμπεριφέρονται αλλιώς στην Python 2 κι αλλιώς στην Python 3 (π.χ. διαβάζω πως η items στην v2.0 επιστρέφει λίστα ενώ στην v3 επιστρέφει ένα view object που είναι ένα απλό reference).

Όσον αφορά τα keys ή τα values, δεν είμαι σίγουρος ότι καταλαβαίνω τον προβληματισμό σου. Οι μέθοδοι keys(), values(), items() στην Python 2 επιστρέφουν μία λίστα· για την ακρίβεια η values() επιστρέφει μία λίστα από tuples. Αν το dictionary σου είναι τεράστιο η δημιουργία μιας νέας λίστας στη μνήμη με σκοπό να τη διατρέξεις σε ένα loop μπορεί να είναι προβληματική. Για το λόγο αυτό δημιουργήθηκαν οι μέθοδοι iterkeys(), itervalues(), iteritems() οι οποίες και επιστρέφουν έναν «iterator».

 

Οι iterators είναι διαφορετικό πράγμα από τα iterables. To πλεονέκτημά τους σε σχέση με τα iterables είναι ότι δεν δημιουργούν μιά ολόκληρη λίστα στη μνήμη αλλά, αντ' αυτού, καθώς γίνονται consume δημιουργούν ένα αντικείμενο της λίστας καθε φορά. Πρόκειται δηλαδή για ένα optimization στη μνήμη. Περισσότερα για iterators και iterables μπορείς να δεις εδώ και εδώ.

 

Tέλος στην Python 2 υπάρχουν και οι viewkeys(), viewvalues(), viewitems() οι οποίες επιστρέφουν view objects. Τα view objects είναι κατ' ουσίαν pointers στο αρχικό dictionary. Αν αφαιρέσεις ή προσθέσεις στοιχεία στο αρχικό dictionary τότε οι αλλαγές αυτές θα αντικατοπτρισθούν και στο view object. Περισσότερα για τα views εδώ

 

Συμπέρασμα: Τα items(), iteritems() και viewitems() είναι διαφορετικά πράγματα με διαφορετικό use case.

 

Στην Python 3 τώρα. Καθώς η python 3 δεν κρατάει backward compatibility με την Python2, έγιναν και διάφορες αλλαγές στο API με σκοπό αυτό να απλοποιηθεί/ενοποιηθεί. Έτσι οι παραπάνω μέθοδοι των dictionary ενοποιήθηκαν κατά κάποιο τρόπο και πλέον έχουμε μόνο τις μεθόδους keys(), values(), items().

 

Οι μέθοδοι αυτες δεν επιστρέφουν list items αλλά Views, τα οποία Views, εντός ενός for loop context, μετατρέπονται implicitly σε iterators· γίνονται δηλαδή αντίστοιχα των iterkeys(), itervalues() και iteritems() της Python2. Εδώ νομίζω ότι τα εξηγεί πιο αναλυτικά. Αν θες μπορείς να δεις και το PEP.

 

Εν ολίγοις, στα πλαίσια ενός for loop όλα τα παρακάτω είναι ισοδύναμα

d = dict(a=1, b=2, c=3)
# Python 2 only
for k, v in d.items():
for k, v in d.iteritems():
for k, v in d.viewitems():
# Python 3
for k, v in d.items()      # επιστρέφει το αντίστοιχο του viewitems() της Python 2
                           # το οποίο και μετατρέπεται implicitly σε iterator
                           # Δηλαδή είναι αντίστοιχο του iter(d.viewitems()) της Python 2

Αφού λοιπόν οι μέθοδοι αυτές στην Python 3 επιστρέφουν views, αν χρειαστείς λίστα θα πρέπει να κάνεις τη μετατροπή explicitly χρησιμοποιώντας

d = dict(a=1, b=2, c=3)
l = list(d.keys())
Lists may be constructed in several ways:

 

Using a pair of square brackets to denote the empty list: []

Using square brackets, separating items with commas: [a], [a, b, c]

Using a list comprehension: [x for x in iterable]

Using the type constructor: list() or list(iterable)

Δεν λέει απολύτως τίποτα περί pytonicness (πολύ ορθά κατά την άποψή μου) οπότε το αβίαστο συμπέρασμα που βγαίνει είναι πως δεν υπάρχει κάποιος τρόπος που μειονεκτεί ή πλεονεκτεί έναντι των υπολοίπων για να δημιουργήσω μια λίστα.

Μπορεί να μη λέει τίποτα για pythonic αλλά

α) αναφέρει μόνο μερικούς από τους τρόπους που μπορείς να δημιουργήσεις μια λιστα (υπάρχουν και άλλοι)

β) οι τρόποι που αναφέρει δεν είναι ισοδύναμοι μεταξύ τους

 

για να δημιουργήσεις μια κενή λίστα μπορείς να χρησιμοποιήσεις δύο τρόπους

[]
list()

Είναι πλήρως ισοδύναμοι, απλά ο πρώτος είναι πιο σύντομος στην γραφή και στην εκτέλεση (3x με 4x πιο γρήγορο). Θα μου πεις τρίχες κατσαρές, αλλά ΟΚ. Το πρωτο είναι μακράν πιο συνηθισμένο. Κανένας δεν θα σου πει τίποτα αν χρησιμοποιήσεις το δεύτερο.

 

Για να δημιουργήσεις μια λίστα από ένα iterable μπορείς να χρησιμοποιήσεις

iterable = "qwerty"
my_list1 = list(iterable)
my_list2 = [x for x in iterable]

Το list(iterable) είναι πιο απλό, πιο σαφές και πιο γρήγορο.

 

Για να δημιουργήσεις μία νέα λιστα από κάποια objects τότε

a, b, c = object(), object(), object()
my_list1 = [a, b, c]
my_list2 = list((a, b, c))    # list takes a single argument which is an iterable! (a tuple in this case)

και πάλι το πρώτο είναι πιο γρήγορο.

 

Για να κάνεις concatenate λίστες μπορείς να χρησιμοποιήσεις

a = [1, 2, 3]
b = [4, 5, 6]
c = a + b      # returns new list
a.extend(    # works inplace

Εν τέλει νομίζω ότι παρεξηγείς κάτι από το zen of python

There should be one-- and preferably only one --obvious way to do it.

Although that way may not be obvious at first unless you're Dutch.

 

καθώς και

Special cases aren't special enough to break the rules.

Although practicality beats purity.

 

Τίποτα δεν είναι υποχρεωτικό. Το θέμα είναι να κάνεις τη δουλειά σου.

Επεξ/σία από pmav99
  • Like 1
Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

 ...

Τίποτα δεν είναι υποχρεωτικό. Το θέμα είναι να κάνεις τη δουλειά σου.

 

Αυτό ακριβώς!

 

Χρήσιμο (όπως πάντα) κι αυτό σου το ποστ, αλλά δεν μου έχεις απαντήσει ακόμα αν είναι λανθασμένη η λογική που έχω ακολουθήσει στο config.py μου.

 

Ισχύει πως έτσι που το έχω κάνω είναι πιο ευέλικτο στον να φορτώνω μονάχα ότι θέλω στα άλλα modules, ή μπορώ να έχω την ίδια δυνατότητα αν το υλοποιήσω με μια μόνο κλάση (ή με όλα χύμα μέσα);

Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

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

Νομίζω ότι πρώτα πρέπει να ξεκαθαρίσουμε κάτι. Τι θα πει config/settings κτλ. Κατά τη γνώμη μου υπάρχουν δύο πράγματα.

 

α) Το ένα είναι δεδομένα στα οποία θέλουμε να μπορεί να παρέμβει ο χρήστης και πρέπει να έχουν persistence. Πχ να ορίσει τη γλώσσα του GUI ή σε ποιον φάκελο θα αποθηκεύονται τα αρχεία που δημιουργεί κτλ. Ή όταν κάνουμε deploy μια web εφαρμογή τους κωδικούς για τη σύνδεση στη βάση

β) Το άλλο είναι δεδομένα στα οποία δεν θέλουμε να μπορεί να παρέμβει ο χρήστης. Εγώ αυτά θα τα ονόμαζα μάλλον data (με την ευρεία έννοια).

 

Όπως σου είπα και στο άλλο thread, για την πρώτη κατηγορία νομίζω ότι είναι προτιμότερη η χρήση αρχείων που δεν εκτελούνται από το πρόγραμμά μας καθώς διαφορετικά δίνουμε δυνατότητα στο χρήστη να εκτελέσει arbitrary κώδικα. Προσωπικά συνηθίζω να χρησιμοποιώ αρχεία ini.

 

Για την δεύτερη κατηγορία τώρα. Καθώς με τα δεδομένα αυτά δεν θα έχει οποιαδήποτε αλληλεπίδραση ο χρήστης τότε δεν έχουμε κανένα λόγο να μην χρησιμοποιήσουμε αρχεία της γλώσσας προγραμματισμού μας και natural datatypes καθώς με τον τρόπο αυτό θα έχουμε όλα τα οφέλη της γλώσσας ενώ γλιτώνουμε και το conversion. Για το use case που συζητάς εσύ, εννοείται ότι αυτά θα τα κάνεις bundle στο executable

 

Όσον αφορά τώρα την οργάνωση των αρχείων.

 

Νομίζω ότι για τα αρχεία της πρώτης κατηγορίας είναι σημαντικό να έχουμε ένα και μόνο αρχείο. Πχ μπορούμε να έχουμε ένα αρχείο ini με πολλά sections και subsections. Αν το αρχείο γίνει κάποια στιγμή τεράστιο τότε ίσως μπορούμε να το ξανασκεφτούμε αυτό, αλλά στην αρχή δεν υπάρχει πραγματικά κανένας λόγος να υπάρχουν περισσότερα από ένα αρχεία. Εν ολίγοις, η ύπαρξη ενός και μόνο αρχείου κάνει

α) ευκολότερο το configuration που θα κάνει ο end-user

β) λιγότερο το maintainance για εμάς

γ) ευκολότερο το deployment αν χρησιμοποιούμε κάποιο orchestration tool (chef, puppet ansible κτλ)

 

Για τα αρχεία της δεύτερης κατηγορίας δεν υπάρχουν χρυσοί κανόνες. Εξαρτάται από το μέγεθος και την πολυπλοκότητα του Project. Σαφώς όσο το project μεγαλώνει πιθανά να χρειαστούμε να αλλάξουμε την δομή που έχουμε υιοθετήσει αρχικά. Εν γένει, καθώς τόσο οι κλάσεις όσο και τα modules είναι namespaces, μπορούμε να τα χρησιμοποιήσουμε εξίσου προκειμένου να οργανώσουμε τον κώδικά μας.

 

Πχ αν έχουμε το ακόλουθο structure

./
├── config
│   └── __init__.py
├── main_app
│   ├── __init__.py
│   ├── file1.py
│   └── file2.py
└── launcher.py

An μέσα στo config/__init__.py δημιουργήσουμε της εξής κλαση

class Settings(object):
    setting1 = 1
    setting2 = 2
    setting3 = 3

τότε από τον κώδικας μας στην main_app (ή στο launcher.py) θα μπορούμε να γράψουμε

from config import Settings
print(Settings.settting1)

Αν όμως χρησιμοποιήσουμε το εξής directory structure (όλα τα αρχεία κενά εκτός από αυτά που θα γράψουμε παρακάτω):

./
├── config
│   ├── __init__.py
│   └── settings.py
├── main_app
│   ├── __init__.py
│   ├── file1.py
│   └── file2.py
└── launcher.py

και έχουμε μέσα στο config/settings.py τα εξής:

settings1 = 1
settings2 = 2
settings3 = 3

τότε μπορούμε μέσα από το main_app (ή το launcher.py)  να γράψουμε

from config import settings
print(settings.settting1)

Όπως βλέπεις είναι ακριβώς το ίδιο πράγμα. Η μόνη διαφορά ειναι στο naming convention που είναι διαφορετικό για κλάσεις και modules.* Κατά τα άλλα, και στην μία και στην άλλη περίπτωση έχουμε ένα ξεχωριστό namespace το οποίο περιέχει τα settings μας.

 

Αν δηλαδή θέλουμε να οργανώσουμε τα settings μας σε διαφορετικά namespaces, μπορούμε να έχουμε είτε πολλές κλάσεις (σε ένα ή περισσότερα αρχεία), είτε πολλά submodules.

 

Τώρα το αν αξίζει να τα έχουμε όλα σε ένα namespace ή σε πολλά είναι νομίζω και πάλι θέμα μεγέθους. Για μικρά project όλα σε ένα. Οτιδήποτε άλλο είναι over-engineering. Όσο μεγαλώνει το project τότε τα περισσότερα namespaces θα είναι πιο φυσικά, αλλά δεν είναι δυνατόν να υπάρξει σαφές όριο για την διάκριση αυτή (ανεξαρτήτως γλώσσας).

 

Κάτι άλλο που θα ήθελα να θίξω είναι ότι από την (μικρή) εμπειρία μου, δεν έχει κανένα νόημα να προσπαθήσεις να βελτιστοποιήσεις την απόδοση του κώδικα σε κάτι τέτοιο. Ιδίως όταν έχεις να κάνεις με GUI και με WEB τα bottlenecks είναι αλλού οπότε είναι αστείο να ασχοληθείς με το κομμάτι που τραβάει τα settings. Μόνο η οργάνωση του κώδικα μπορεί να είναι κριτήριο για κάτι τέτοιο

 

Εν κατακλείδι κάνε ό,τι νοιώθεις ότι είναι πιο φυσικό για το μέγεθος του Project.

 

* Υπάρχει και μία μικρή διαφορά στο πως μπορούμε να κάνουμε τα imports αλλά δεν υπάρχει λόγος να σε μπλέξω τώρα με το python import model. Εν ολίγοις με τα modules μπορούμε να τραβήξουμε στο τρέχον scope ένα συγκεκριμένο setting ενώ με τις κλάσεις όχι. Επίσης με τα modules μπορούμε να τραβήξουμε όλα τα settings ενός namespace στο τρέχον scope (κάτι που συνήθως είναι κακή πρακτική) ενώ με τις κλάσεις και πάλι όχι.

Επεξ/σία από pmav99
  • Like 1
Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

Καλημέρα!

 

Ήσουν σαφέστατος, σε ευχαριστώ (δεν το ήξερα καν πως και οι σκέτοι φάκελοι λογίζονται ως modules).

 

Για το config.py αναφερόμουν στην 2η κατηγορία του (σωστού) διαχωρισμού που έκανες, δηλαδή σε settings που δεν θέλουμε να τα πειράζει ο user. Οπότε υποθέτω είμαι οκ που τα έχω σε πηγαίο αρχείο της γλώσσας.

 

Σχετικά με το over-engineering και το execution efficiency, το πρόβλημά μου είναι πως επειδή δεν έχω συνολική εικόνα της γλώσσας (ώστε να παίρνω παντού συνειδητές αποφάσεις) ότι καινούριο διαβάζω/μαθαίνω προσπαθώ να το δοκιμάζω σε αυτό το toy project.  Πολύ συχνά, μαθαίνοντας κάτι νέο λέω "α, αυτό που το είχα κάνει έτσι, μάλλον είναι καλύτερα να το κάνω έτσι τώρα".

 

Εξού και το συνεχές refactoring, εξού και ο χαρακτηρισμός "αχταρμάς" που έδωσα εξαρχής στο νήμα για τον κώδικα του toy-project.

 

Επίσης, όσο περισσότερο ασχολούμαι (δυστυχώς όχι με τη συχνότητα που θα ήθελα) τόσο συνειδητοποιώ πως στην παρούσα φάση, ο κώδικας αυτού του toy-project εκπαίδευσης δεν πρόκειται ποτέ να είναι clean (για τον απλούστατο λόγο πως δεν προ-υπήρξε ο παραμικρός σχεδιασμός, κι όσο μεγαλώνει ο κώδικας με τις random αποφάσεις, τόσο περισσότερο δυσχαιρένει το refactoring που καταλαβαίνω πως χρειάζεται όταν μαθαίνω κάτι νέο).

 

ΥΓ. Όσο προχωράω τόσο περισσότερο μου αρέσει η Python (σημείωση, ασχολούμαι αποκλειστικά με Python 3). Εντοπίζω διάφορα πράγματα που δεν μου αρέσουν, αλλά όσο ασχολούμαι τόσο περισσότερο γέρνει η ζυγαριά προς εκείνα που μου αρέσουν :)

 

EDIT:

 

Ξέχασα, πήγα να αντικαταστήσω τα % με {} στα GuiStrings αλλά τελικά το μετάνιωσα. Αφενός γιατί πρέπει μετά να πάω να αλλάξω πολλά σημεία του κώδικα (από '..' % ()  σε '...'.format(...) ), κι αφετέρου επειδή κι έτσι όπως είναι δεν μου χαλάνε ιδιαίτερα το readability (με κάνα-δυο εξαιρέσεις, όπως το μακρινάρι string με τα instructions).

 

Είχες κάποιον άλλο λόγο πέρα από το improved readability για την πρόταση που έκανες να το αλλάξω;

 

EDIT-2

 

Μιας που το θυμήθηκα και κολλάει με τα παραπάνω, ένα παράδειγμα της δυσκολίας του refactoring όσο μεγαλώνει ο κώδικας είναι και κάτι σχετικά με το internationalization.

 

Αφού το έκανα έτσι όπως το έκανα (κι αυτό μετά από πολλές δοκιμές) μου ήρθε ξαφνικά η επιφοίτηση πως θα ήταν πολύ καλύτερα αν το μενού με τις γλώσσες δημιουργούταν δυναμικά διαβάζοντας τον φάκελο "lang/' και για κάθε υποφάκελο που βρίσκει εκεί να παίρνει τα 2 πρώτα γράμματα (που είναι i18n language codes) και να βάζει τις αντίστοιχες γλώσσες ως menu-entries μέσα στο μενού Language.

 

Για να το κάνω αυτό, πέρα του ότι πρέπει να βρω ή να φτιάξω μια database αντιστοίχησης codes με language strings (είτε offline είτε online) πρέπει να αλλάξω και πάρα πολύ από τον υπάρχοντα κώδικα. Συν ότι πρέπει να εμβαθύνω ακόμα περισσότερο τόσο στο Tkinter όσο και στην Python.

Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

 

(δεν το ήξερα καν πως και οι σκέτοι φάκελοι λογίζονται ως modules)

Δεν είμαι σίγουρος τι εννοείς.

 

Module είναι κάθε αρχείο *.py. Ένας σκέτος φάκελος δεν είναι τίποτα στην Python.

 

Ένας φάκελος με ένα αρχείο __init__.py είναι ένα package. Ένα Package δεν είναι τίποτα άλλο από μία συλλογή python modules (το __init__.py + πιθανά και άλλα modules). Κάθε package μπορεί να περιέχει άλλα packages (δηλαδή να έχει υποφακέλους που να περιέχουν ένα αρχείο __init__.py).

 

Αν σε ένα package γράψεις κάτι μέσα στο __init__.py τότε αυτό το κάτι μπορείς να το κάνεις import κατευθείαν μέσα από το namespace του πακέτου (πχ όπως έκανα προηγουμένως "from config import Settings"). Το τι ακριβώς μπορεί να γίνει import μέσα από κάθε module μπορείς να το ρυθμίσεις μέσω της μεταβλητής __all__

 

Μπορείς να δεις αν θέλεις τα εξής:

 

Το Language reference για τα imports. Ίσως να είναι πιο βαρύ από αυτό που θες τώρα.

To τμήμα του official python tutorial για modules και packages.

How to package your python code

PEP 328 (όπου εξηγεί τα explicit relative imports που ισχύουν στην Python 3 - ίσως έχει πιο πολύ ενδιαφέρον για κάποιον που ξέρει τι γίνεται στην Python 2 και θέλει να δει τις αλλαγές)

Τα πρώτα μέρη της ενότητας Structuring Your Project του The Hitchhiker’s Guide to Python

H ενότητα Creating a package του The Hitchhiker’s Guide to Packaging

 

Υπάρχει και ένα paper του Guido van Rossum το οποίο με μια διαγώνια ματιά που έριξα είναι ακόμα σχετικά relevant αλλά μπορεί να έχει ανακρίβειες (ιδίως για python 3) γιατί γράφτηκε για την 1.5.

 

Αναφορικά με τη μέθοδο str.format(). Η μέθοδος αυτή είναι πιο ευέλικτη από το %s αλλά είναι λίγο πιο verbose. Και οι δύο μπορούν να κάνουν σχεδόν τα ίδια· αν θυμάμαι καλά είναι κάτι λίγα πράγματα που μπορεί να κάνει η format και δεν μπορεί να κάνει η %s. Επίσης νομίζω ότι όταν θες να κάνεις πιο advanced πράγματα (δηλαδή όχι απλό replacement) η σύνταξη της format είναι λίγο πιο ωραία. Προσωπικά χρησιμοποιώ και τις 2.

 

To πιο ουσιώδες ερώτημα είναι το αν θα χρησιμοποιείς keyword ή positional arguments. Και οι δύο μέθοδοι υποστηρίζουν και τους δύο τύπους arguments (η format() με πιο ωραία σύνταξη). Όταν έχεις λίγες μεταβλητές (πχ 2-3) το να τις περάσεις σαν positional arguments είναι ΟΚ. Σε αυτές τις περιπτώσεις χρησιμοποιώ %s γιατί είναι πιο σύντομο.

 

Αν όμως έχεις πολλές μεταβλητές, τότε τα positional arguments δεν βολεύουν καθόλου. Τότε χρειάζεσαι keyword arguments. Σκέψου πχ να έχεις ένα string με 20 μεταβλητές, να προσθέτεις άλλο ένα %s και να ψάχνεις στο tuple που ακολουθεί που πρέπει να βάλεις την νέα μεταβλητή. Αηδία. Εκεί keywords + format() χωρίς δεύτερη σκέψη.

s = "%s, %s, %s, %s, %s, %s, %s, %s" % (var1, var2, var3, var4, var5, var6, ...)   # Αίσχος

Θα μπορούσε κάποιος σε ένα project να πει ότι θα χρησιμοποιήσουμε μόνο τον ένα ή τον άλλο τύπο. ΟΚ. Το θεωρώ ήσσονος σημασίας. Εγώ χρησιμοποιώ και τα δύο ανάλογα με το τι με βολεύει.

 

Δεν ξέρω αν έχεις χρησιμοποιήσει ποτέ κάποια Template Language, αλλά όταν θα έχεις την ανάγκη για να χρησιμοποιήσεις μία, τότε θα καταλάβεις γιατί τα keywords κάνουν απείρως καλύτερο scale.

 

Τέλος σχετικά με τις μεταφράσεις, αν και δεν έχω προσωπική εμπειρία, έχω την αίσθηση, η οποία πηγάζει από αυτά που έχω διαβάσει καθώς και από τον αριθμό των project που έχω δει να χρησιμοποιούν αυτό τον τρόπο, ότι η μέθοδος που ακολουθείς (αν την έχω καταλάβει καλά) δεν είναι η βέλτιστη. Η πλειοψηφία νομίζω ότι ακολουθεί την τακτική της δημιουργίας αρχείων *.po, *.mo κτλ. Ίσως ο Ηλίας που την έχει εφαρμοσει να μπορεί να σε διαφωτίσει περισσότερο. Εδώ έχει και έναν οδηγό στα ελληνικά απο τον kamar. Μπορεί να θες να δεις και αυτό το βίντεο (εδώ ο κώδικας του)

Σημείωση: δεν ξέρω αν η μέθοδος με τα po,mo κτλ επιτρέπει να αλλάζεις την γλώσσα on the fly. Επειδή πολλά projects σου ζητάν να επανεκινήσεις την εφαρμογή πριν αλλάξει η γλώσσα εικάζω ότι μπορεί να μην υπάρχει clean τρόπος για να γίνει η αλλαγή της γλώσσας. Μπορεί να κάνω και λάθος. Αν το συγκεκριμένο feature είναι σημαντικό για εσένα, πρέπει να το εξετάσεις.

Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

Ρε συ, too much info too little time!

 

Έχω πήξει μιλάμε :lol: (όχι, σοβαρά έχω πήξει αλλά δεν φταίει η python, φταίει ότι κάνω πολλά πράγματα ταυτόχρονα... άσχετα με την python).

 

Λοιπόν, για τα modules, έτσι όπως είδα τα dir tress που είχες φτιάξει στο προηγούμενο ποστ, και μετά έκανες from config import τάδε (που στο δεντράκι το config ήταν ένας απλός φάκελος) νόμισα πως λογίζεται κι αυτό ως module. Τώρα που μίλησες και για packages, κατάλαβα πως δεν είναι έτσι (επίσης κατάλαβα πως έχω να ρίξω πολύ διάβασμα... δεν προλαβαίνω να τα αφομοιώσω καλά, κι από αυτά που αφομοιώνω τα μισά τα έχω μισο-ξεχάσει την επόμενη φορά που ξανασχολούμαι, γιατί στο μεταξύ έχω ασχοληθεί με 1002 άλλα πράγματα... άσχετα από python εννοώ... τέσπα, το παλεύω όσο μπορώ).

 

Για τα % vs .format για να είμαι ειλικρινής δεν πολύ-κατάλαβα γιατί το 2ο κάνει scale πολύ καλύτερα (εκτός αν εννοούμε διαφορετικό πράγμα λέγοντας scale), αλλά επί της ουσίας το παράδειγμα που δίνεις είναι ακριβώς αυτό που σημείωσα κι εγώ για το μακρινάρι string με τα instructions (το έχω δει δηλαδή το πρόβλημα, και προφανώς καταλαβαίνω γιατί θέλει .format εκεί, απλώς δεν σε πιάνω γιατί το συνδέεις τόσο εμφατικά με scaling... τέσπα, νομίζω δεν είναι και τόσο ουσιώδες να αναλύσουμε και περί scaling αυτή τη στιγμή.. εκτός αν θεωρείς ότι είναι σημαντικό, οπότε ρίχτο κι αυτό στο τραπέζι... εγώ πάντως scaling εννοώ ότι περίπου περιγράφει η wikipedia, που σημαίνει πως έχει -πάρα- πολλά επίπεδα).

 

Σχετικά με τις μεταφράσεις, κι εγώ με .po και .mo το έχω (όπως όλος ο υπόλοιπος non-Windows κόσμος από ότι ξέρω). Νομίζω είναι κι ο μοναδικός τρόπος που υποστηρίζει η gettext. Και όπως πολύ σωστά παρατήρησες, σχεδόν κανείς δεν κάνει on-the-fly αλλαγή γλώσσας, η τουλάχιστον δεν έχει τύχει να δω εγώ. Ότι έχω δει, κάνει restart την εφαρμογή. Και τους καταλαβαίνω, διότι το on-the-fly θέλει να κάνεις re-apply όλα τα strings της εφαρμογής σου (και αυτά που έχεις φορτώσει κατά την εκκίνηση της εφαρμογής, και αυτά που έχεις δημιουργήσει/αλλάξει δυναμικά μέσα στην εφαρμογή σου)... κι αυτό με τη σειρά του, προϋποθέτει αρκετό έως πολύ έξτρα κώδικα, συν ότι προϋποθέτει και διαφορετική δόμηση του κώδικα για να μη χάσεις τελείως την μπάλα... π.χ. αν δεν έχεις κάποιου είδους πίνακα με όλα τα strings που χρησιμοποιείς, όπως εκείνο που έχω στο config.py μου, κι αντίστοιχα προσαρμοσμένο τον κώδικα που χρησιμοποιεί αυτά τα strings, τότε το on-the-fly είναι πανεύκολο να καταντήσει εφιάλτης).

 

ΥΓ. Πάντως επειδή έχω αρχίσει να συνειδητοποιώ πως δεν υπάρχει περίπτωση να φτιάξω clean κώδικα σε αυτό το συγκεκριμένο toy-project (για τους λόγους που περιέγραψα στο προηγούμενο ποστ) αν θέλετε μπορώ να τον ποστάρω όπως είναι, για τυχόν παρατηρήσεις, υποδείξεις, διορθώσεις, βρισίδια, κλπ :P

Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

Δημοσ. (επεξεργασμένο)
Ρε συ, too much info too little time!

Συγγνώμη αλλά τι περιμένεις; Να γίνεις expert σε μια γλώσσα προγραμματισμού σε μία εβδομάδα; :P :D

Πόσο χρόνο θέλει κάποιος για να πει ότι ξέρει C;* :P

 

Όλα θέλουν το χρόνο τους. Η σύνταξη μαθαίνεται. Η γλώσσα όχι. Για αυτό είναι τα λινκ εκεί για να μπορείς να επιστρέψεις σε αυτά αν/όταν τα χρειαστείς.

 

Το link που έδωσες για το scale δεν δουλεύει. Εγώ το εννοώ με την έννοια του scalability (περίπου)

 

Scalability is the ability of a system, network, or process to handle a growing amount of work in a capable manner or its ability to be enlarged to accommodate that growth

και εννοώ ότι όσο αυξάνει η πολυπλοκότητα του κειμένου που θέλεις να παράξεις (amount of work) τόσο πιο μεγάλη ευελιξία χρειάζεσαι· ευελιξία που δεν στην δίνει το %s και τα positional arguments.

 

Παραδείγματος χάριν σκέψου να πρέπει να φτιάξεις ένα τιμολόγιο ή ένα δελτίο αποστολής (pdf). Θα έχεις μερικες δεκάδες variables. Ήδη τα %s δεν δουλεύουν. Αν δε, πας σε ακόμα πιο σύνθετα πράγματα (πχ ιστοσελίδα, τεύχος υπολογισμών κτλ) εκεί δεν σου φτάνει ούτε καν το format(). Εκεί θες μια full blown template language.

 

Τον κώδικα άμα θες βάλτον σε κανά github/mercurial

 

* Η διαφορά είναι ότι για να φτάσει να κάνει κάποιος κάτι στοιχειωδώς non-trivial σε C χρειάζεται πολύ περισσότερο χρόνο από το χρόνο που χρειάζεται για να φτάσει σε αυτό το επίπεδο σε Python. Επίσης για να κάνεις οτιδήποτε στην C πρέπει να ξέρεις/μάθεις ένα σωρό άλλες λεπτομέρειες που δεν έχουν κατ' ανάγκη άμεση σχέση με το πρόβλημα που θες να λύσεις (πχ το Λειτουργικό σύστημα, διαχείριση μνήμης, compiling/linking κτλ). Αυτό δεν είναι κακό. Αλλά το ξεκίνημα με μια γλώσσα σαν την Python είναι πιο εύκολο· έχεις πιο λίγες πληροφορίες να διαχειριστείς. Το να γίνεις expert όμως σε οποιαδήποτε γλώσσα θέλει χρόνο και τριβή.

Επεξ/σία από pmav99
Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

Γιατί, μήπως νομίζεις ότι δουλεύει το link που έδωσες εσύ για το scalability? :lol:

(βασικά το ίδιο λινκ είναι, ένα είναι το λινκ προς τη βικιπέδια ;) )

 

Τον κώδικα αν είναι θα το ανεβάσω σε ζιπ εδώ πέρα. Δεν είναι για να τον βγάζω στο github (θα μας πάρουν με τις ντομάτες :P)!

 

Προφανώς και δεν περιμένω να γίνω ειδικός σε τόσο μικρό χρονικό διάστημα ρε συ. Μπορεί να γκρινιάζω, αλλά ήδη αισθάνομαι ότι τα πάω αρεκτά καλά για το χρόνο που διαθέτω. Απλώς βλέπω πως είναι κι αυτή γλώσσα ωκεανός, περίμενα πως θα ήταν λίγο πιο μικρή σε έκταση η στάνταρ βιβλιοθήκη της (δεν με χαλάει, αλλά με αγχώνει :P)!

 

Όσο για τη C, εντάξει είναι άλλη ιστορία. Δεν νομίζω πως μπορούμε να τις συγκρίνουμε (άλλη φιλοσοφία, άλλη διαδικασία, άλλη στόχευση, άλλη εφαρμοσιμότητα, κλπ, κλπ).

 

 

 

Βασικά η C δεν είναι δύσκολη γλώσσα για να εξοικειωθεί κανείς μαζί της, είναι αρκετά απλή και λιτή και ως σύνταξη, και ως στάνταρ βιβλιοθήκες, και ψς φιλοσοφία γενικότερα. Το δύσκολο μαζί της είναι αυτό που είπες κι εσύ, πως αργείς να γίνεις productive, συν ότι για να αρχίσεις να γίνεις πρέπει αφενός να την έχεις κάνει κτήμα σου, κι αφετέρου να ψάχνεις για βιβλιοθήκες δεξιά κι αριστερά, γιατί ναι μεν υπάρχουν άπειρες αλλά δεν είναι στανταρισμένες.

 

Πάντως κατά περιπτώσεις (αρκετές περιπτώσεις) αν την έχεις μάθει, τότε τα περαιτέρω δεν είναι και τόσο τραγικά συγκριτικά π.χ. με την Java ή με την C++ ή π.χ. με τη C# (της έχω ρίξει μια ματιά κι αυτήνής απ' έξω απ' έξω), κλπ. Εννοώ πως και στις 3 αυτές στην ουσία πρέπει να μάθεις βιβλιοθήκες για να γίνεις παραγωγικός.

 

Η μεγάλη διαφορά είναι πως στις άλλες γλώσσες είναι στανταρισμένα εκατοντάδες features που με τη C ψάχνεις να τα βρεις 3d-party. Και δεν είναι μόνο το ψάξιμο, υπάρχει θέμα και με την τεκμηρίωση, και με τη βιβλιογραφία, κλπ. Στα στανταρσιμένα είναι πολύ πολύ πολύ πιο οργανωμένη και προσεγμένη η τεκμηρίωση, η υποστήριξη, κλπ.

 

Πάντως για να ξέρεις, όσο μου τη σπάνε τα μακρινάρια στις βιβλιοθήκες της Java, της C++, κλπ άλλο τόσο μου τη σπάνε και στις (αναγκαστικά) 3rd party βιβλιοθήκες στη C. Στη python βλέπω μέχρι στιγμής πως αυτό το φαινόμενο είναι σχετικά περιορισμένο (σχετικά είπα, έτσι; :P), και μου αρέσει.

 

 

Λοιπόν, κάτι άλλο τώρα. Διαβάζω στην τεκμηρίωση της Python πως οι file.readline() και readlines() παίρνουν ένα προαιρετικό argument keepends, το οποίο αν το κάνουμε set σε True τότε κόβουν το '\n' από τα τέλη των γραμμών που μας επιστρέφουν. Σε μένα όμως δεν το δέχεται αυτό το argument με τίποτα!

 

Τέσπα, κοίτα τι μου έκανε φοβερή εντύπωση! Δεν ξέρω αν έχεις παρακολουθήσει το νήμα με το csv parsing σε C++, αλλά θέλησα να κάνω το ίδιο και σε Python, και για εξάσκηση αλλά κι από περιέργεια για να δω διαφορές σε χρόνους. Και τι λες ότι έγινε... οι χρόνοι της python είναι εφάμιλλοι με της C++ σε αυτό το συγκεκριμένο παράδειγμα!

 

Για την ακρίβεια, είναι σταθερά καλύτεροι από το 1-pass εκείνου του C++ κώδικα: ~6.2 secs vs ~5.9 secs, ενώ τα 2-passes του C++ κώδικα κυμαίνονται στα ~5.4 secs. Κι αυτό χωρίς να συμπεριλαμβάνω στους χρόνους της C++ τα ~0.375 secs που κάνουν για να διαβάσουν το αρχείο των 892.000 γραμμών. Υπάρχει και C κώδικας που κυμαίνεται στο ~0.375 secs για φορτωμα + ~2.85 secs για το parsing, αλλά είναι εκτός συναγωνισμού γιατί κάνει pre-allocate τις γραμμές και τις στήλες (αν βρω διάθεση θα τον κάνω κι αυτόν δυναμικό, από περιέργεια).

 

Βέβαια για να είμαστε δίκαιοι, ο αλγόριθμος στην Python διαφέρει από τους άλλους στο ότι παρσάρει καθώς διαβάζει (εσωτερικά δεν ξέρω τι κάνει) αντί να διαβάζει πρώτα στο buffer και κατόπιν να παρσάρει το buffer. Όμως αν επιχειρήσουμε να το κάνουμε αυτό σε C ή C++ (δηλαδή να διαβάζουμε με fgets()/getline() και να παρσάρουμε ταυτόχρονα στη λούπα με την οποία διαβάζουμε το αρχείο, οι χρόνοι σίγουρα θα χειροτερέψουν.

 

Επίσης, δεν ξέρω αν ο Python κώδικας που έχω γράψει επιδέχεται περαιτέρω βελτίωσης, αλλά κι έτσι που είναι, οι χρόνοι που βγάζει με ξάφνιασαν (αν του αφαιρέσω και την .rsplit() στο loop για να μη σβήνει τα '\n' από τα τέλη των γραμμών, κατεβαίνει στα ~5.7 secs, αλλά το άφησα γιατί στη C++ τα σβήνω... βέβαια αν στη C++ προσθέσουμε και τα ~0.375 secs που κάνει για να διαβάσει το αρχείο στη μνήμη, τότε o κώδικας της Python χτυπάει στα ίσα τον ταχύτερο από τους 2 κώδικες της C++ (το γιατί δεν το ξέρω, ή έχω γράψει χάλια C++ κώδικα, ή καλό Python, ή και τα 2, ή δεν ξέρω κι εγώ τι άλλο).

 

Την python την έτρεχα με -Ο command line flag, ενώ C++ και C τα έχω κάνει compile με -O2 command line flag, με mingw32 4.8.1

 

Κώδικας Python:

 

 

import os
import time
from contextlib import contextmanager

FNAME = 'parse_csv/test1.csv'

# ------------------------------------------------------
@contextmanager
def time_it( name= None ):
	tstart = time.time()
	yield
	elapsed = time.time() - tstart
	if ( name ):
		print( '{} finished in '.format(name), end= '', flush= True )
	print( '{} secs'.format(elapsed) )

# ------------------------------------------------------
def lines_from_csv( lines, fname ):

	with open( fname, 'r' ) as f:
		for line in f:
			lines.append( line.rstrip().split(',') )

# ------------------------------------------------------
def lines_print( lines ):
	for i,l in enumerate(lines):
		print( '----- line {} ({} fields) -----'.format(i+1, len(l)) )
		for f in l:
			print( f )

# ------------------------------------------------------
if __name__ == "__main__":

	lines = []

	print( 'loading & parsing %s... ' % FNAME, end='', flush=True )
	with time_it():
		lines_from_csv( lines, FNAME )

	print( "{} bytes ({} lines)".format(os.stat(FNAME).st_size, len(lines)) )

	#lines_print(lines)

 

Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

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

Μάλλον τον παίζει το insomnia με τα links προς wikipedia.

 

 

Απλώς βλέπω πως είναι κι αυτή γλώσσα ωκεανός, περίμενα πως θα ήταν λίγο πιο μικρή σε έκταση η στάνταρ βιβλιοθήκη της (δεν με χαλάει, αλλά με αγχώνει :P)!

Ένα από τα selling points της Python είναι το «batteries included»

 

 

Πάντως για να ξέρεις, όσο μου τη σπάνε τα μακρινάρια στις βιβλιοθήκες της Java, της C++

τι εννοείς;

 

 

Λοιπόν, κάτι άλλο τώρα. Διαβάζω στην τεκμηρίωση της Python πως οι file.readline() και readlines() παίρνουν ένα προαιρετικό argument keepends, το οποίο αν το κάνουμε set σε True τότε κόβουν το '\n' από τα τέλη των γραμμών που μας επιστρέφουν. Σε μένα όμως δεν το δέχεται αυτό το argument με τίποτα!

Η builtin function open() της Python 3 έχει αλλάξει αρκετά σε σχέση με την Python 2. Εν πάση περιπτώσει επιστρέφει ένα file-object το οποίο στην συγκεκριμένη περίπτωση, αφού χρησιμοποιείς mode='r' είναι ένα ιο.TextIOWrapper object που κληρονομεί ένα io.TextIOBase object του οποίου οι μέθοδοι δεν έχουν το keepends που αναφέρεις.

 

To link που πόσταρες είναι από το πακέτο codecs της Standard Library. Το πακέτο αυτό περιέχει τη συνάρτηση codecs.open() (δηλαδή πακέτο codecs, συνάρτηση open) που είναι διαφορετική από την builtin open(). Στην Python2 η codecs.open() ήταν πιο χρήσιμη από την Python 3 γιατί η builtin open() δε σε άφηνε να ορίσεις το encoding. Στην 3 όμως σε αφήνει οπότε η codecs.open() δεν χρησιμοποιείται τόσο συχνά. Για να είμαι ειλικρινής δεν ξέρω να σου πω ποιες είναι ακριβώς οι διαφορές τους στην Python 3. Βρήκα ένα thread στην python-dev το οποίο αναφέρεται ότι η codecs.open() έχει μείνει μόνο για να γίνει πιο εύκολη η μετάβαση στην Python3.

 

Από εκεί και πέρα, τα Stream{Reader,Writer} είναι ουσιαστικά interfaces για να γράψεις δικά σου encoding.Δεν ξέρω παραπάνω λεπτομέρειες· το thread που αναφέρω ποιο πάνω έχει κάποιες πληροφορίες παραπάνω πχ λέει ότι αν θες να κάνεις κάτι τέτοιο είναι μάλλον καλύτερο να χρησιμοποιήσεις io.TextIOWrapper.

 

 

Τέσπα, κοίτα τι μου έκανε φοβερή εντύπωση! Δεν ξέρω αν έχεις παρακολουθήσει το νήμα με το csv parsing σε C++, αλλά θέλησα να κάνω το ίδιο και σε Python, και για εξάσκηση αλλά κι από περιέργεια για να δω διαφορές σε χρόνους. Και τι λες ότι έγινε... οι χρόνοι της python είναι εφάμιλλοι με της C++ σε αυτό το συγκεκριμένο παράδειγμα!

Οι βιβλιοθήκες αυτές (open() κτλ) είναι γραμμένες σε C. Λογικό είναι να είναι γρήγορες.

 

Δοκίμασε και να ανοίξεις το αρχείο σε binary mode. Αν κάτσεις να το ψάξεις δεν αποκλείεται να φτάσεις κοντά σε ταχύτητα C. Ανδεν θες να το παλεύεις μόνος σου, ρώτα σε κανένα IRC

 

Επίσης όταν θες να επεξεργαστείς csv αρχεία συνήθως χρησιμοποιείς τη βιβλιοθήκη csv. Λογικά δεν θα είναι πιο γρήγορο από το χειροκίνητο (αλλλά ούτε και πιο αργό) αλλά είναι πιο βολικό σίγουρα.

 

edit

 

Και μιας και πιάσαμε το IO. Πάρε να γουστάρεις :P

Επεξ/σία από pmav99
Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

Ένα από τα selling points της Python είναι το «batteries included»

Όντως, το είχα ξεχάσει αυτό!

 

τι εννοείς;

Εννοώ τα oop μακαρόνια namespace-class-method όταν θες να σιγουρέψεις πως δεν θα έχεις conflicts, που στη C γίνονται namespace_sourcemodule_function (ή όποιο άλλο naming convention, που κάνει τα πράγματα ακόμα χειρότερα επειδή δεν υπάρχει στάνταρ). Τουλάχιστον σε python, c++, .net έχεις namespace aliases, στη C μπορείς να κάνεις τίποτα παπαδιές στον preprocessor, αλλά π.χ. στη Java νομίζω δεν μπορείς να κάνεις τίποτα.

 

...

Από εκεί και πέρα, τα Stream{Reader,Writer} είναι ουσιαστικά interfaces για να γράψεις δικά σου encoding.Δεν ξέρω παραπάνω λεπτομέρειες· το thread που αναφέρω ποιο πάνω έχει κάποιες πληροφορίες παραπάνω πχ λέει ότι αν θες να κάνεις κάτι τέτοιο είναι μάλλον καλύτερο να χρησιμοποιήσεις io.TextIOWrapper.

Είδα StreamReader εγώ και νόμιζα πως περιλαμβάνει και τα text filestreams. Thanks για την διευκρίνηση.

 

Οι βιβλιοθήκες αυτές (open() κτλ) είναι γραμμένες σε C. Λογικό είναι να είναι γρήγορες.

 

Δοκίμασε και να ανοίξεις το αρχείο σε binary mode. Αν κάτσεις να το ψάξεις δεν αποκλείεται να φτάσεις κοντά σε ταχύτητα C. Αν δεν θες να το παλεύεις μόνος σου, ρώτα σε κανένα IRC

Δεν θέλω να το ανοίγω σε binary mode, γιατί θέλω να μου κανονικοποιεί τα τέλη των γραμμών σε σκέτο '\n'.

 

Δεν μου έκανε εντύπωση τόσο το file-io όσο η δυναμική δημιουργία της λίστας και των fields της. Για να βγάλω πιο ασφαλή συμπεράσματα, πρέπει να τα μετρήσω ξεχωριστά. Δηλαδή να διαβάσω το αρχείο σε ένα buffer (π.χ. με buf = file.readlines()) και μετά να μετρήσω ειδικά το παρσάρισμα πάνω στο buf. H κλήση στην realdines() θα στείλει στα τάρταρα τον συνολικό χρόνο, αφού αν το έχω πιάσει σωστά πρόκειται για διπλοδουλειά (πάντως θα έχει ενδιαφέρον να μετρήσω αποκλειστικά το parsing για τη δημιουργία των γραμμών και των fields).

 

Επίσης όταν θες να επεξεργαστείς csv αρχεία συνήθως χρησιμοποιείς τη βιβλιοθήκη csv. Λογικά δεν θα είναι πιο γρήγορο από το χειροκίνητο (αλλλά ούτε και πιο αργό) αλλά είναι πιο βολικό σίγουρα.

Το λογικό είναι να είναι πιο αργή η csv βιβλιοθήκη, αφού πρέπει να λάβει υπόψη της διάφορους γενικότερους παράγοντες. Και όντως στο συγκεκριμένο δείχνει να είναι αρκετά πιο αργή (συγκεκριμένα δείχνει να είναι ~2 secs πιο αργή στις 892000 γραμμές)...

 

/* ------- python 3.4.0 (using csv lib) -------- */

<using standard csv lib>
loading & parsing test1.csv... 7.645977737494195 secs
(109876033 bytes of text, across 891072 lines)

/* ------- python 3.4.0 (manual alg) ----------- */

loading & parsing test1.csv... 5.59375 secs
(109876033 bytes of text, across 891072 lines)
Κώδικας:

 

# -*- coding: cp1253 -*-

import os
import csv
import time
from contextlib import contextmanager

FNAME       = 'test1.csv'
USE_CSV_LIB = False

# ------------------------------------------------------
@contextmanager
def time_it( name= None ):
	tstart = time.time()
	yield
	elapsed = time.time() - tstart
	if ( name ):
		print( '{} finished in '.format(name), end= '', flush= True )
	print( '{} secs'.format(elapsed) )

# ------------------------------------------------------
if USE_CSV_LIB:
	def lines_from_csv( lines, fname ):

		with open(fname, 'r', newline='') as f:
			reader = csv.reader( f, quoting=csv.QUOTE_NONE )
			for line in reader:
				lines.append( line )
else:
	def lines_from_csv( lines, fname ):

		with open(fname, 'r', newline='') as f:
			for line in f:
				lines.append( line.rstrip().split(',') )

# ------------------------------------------------------
def fsize( fname ):

	txt = 0
	with open(fname, 'r') as f:
		txt = len( f.read() )

	bin = os.stat(fname).st_size

	return (bin, txt)

# ------------------------------------------------------
def lines_print( lines ):
	for i,l in enumerate(lines):
		print( '----- line {} ({} fields) -----'.format(i+1, len(l)) )
		for f in l:
			print( f )

# ------------------------------------------------------
if __name__ == "__main__":

	# get file-size for future reference
	binbytes, txtbytes = fsize( FNAME )

	lines = []

	if USE_CSV_LIB:
		print( '<using standard csv lib>' )
	print( 'loading & parsing %s... ' % FNAME, end= '', flush= True )
	with time_it():
		lines_from_csv( lines, FNAME )

	print( "({} bytes of text, across {} lines)".format(txtbytes, len(lines)) )

	#lines_print(lines)

 

Πρόσεξε όμως, αν αφαιρέσω από την csv.reader() την παράμετρο quoting=csv.QUOTE_NONE, τότε έρχεται στα ίδια με τον χειροκίνητο αλγόριθμο. Αυτή η παράμετρος ουσιαστικά διατηρεί τυχόν εισαγωγικά εκατέρωθεν tokens μέσα στο csv αρχείο (αν δεν την περάσουμε, τότε η csv.reader() κάνει strip τυχόν εισαγωγικά). Την έβαλα λοιπόν, γιατί στον C++ κώδικα δεν κάνω κάποια ειδική μέριμνα για εισαγωγικά.

 

Από την άλλη μεριά όμως, αν επιχειρήσω αυτό να το κάνω χειροκίνητα (να κάνω δηλαδή strip τα εισαγωγικά) δεν ξέρω ποιος είναι ο βέλτιστος τρόπος στη python, αλλά δοκιμάζοντας με generator, πήγε στα 12.5 secs...

def lines_from_csv( lines, fname ):
	with open(fname, 'r', newline='') as f:
		for line in f:
			lines.append( [x.strip('"') for x in line.rstrip().split(',')] )
Δείχνει δηλαδή να πηγαίνει αντιστρόφως ανάλογα με τις ανάγκες μας :P

 

edit

 

Και μιας και πιάσαμε το IO. Πάρε να γουστάρεις :P

Thanks για τα links, τους έριξα μια πρόχειρη ματιά και είδα πως εσωτερικά κάνουν i/o με system calls (open, read, write, close) ενώ κάπου πήρε το μάτι μου και platform specific implementation όταν είναι σε Windows (δηλαδή με win32 api). Και κάνουν δικιά τους διαχείριση του buffering. Τώρα πως συγκρίνεται αυτό με τα εκάστοτε C/C++ implementations δεν έχω ιδέα.

 

ΥΓ. Κι επειδή "της νύχτας τα καμώματα τα βλέπει η μέρα και γελά", μέχρι σήμερα με άλλη συνάρτηση μέτραγα χρόνους στη C++ (steady_clock), με άλλη στη C (clock) και με άλλη στη python (time), σήμερα έβαλα παντού την clock (που δεν τη λες κι αξιόπιστη, αλλά είναι παντού διαθέσιμη με πολύ λίγο κώδικα και βαρέθηκα να ασχοληθώ παραπάνω). Οι χρόνοι παρέμειναν ίδιοι (μάλλον επειδή είμαι σε Windows, σε άλλες πλατφόρμες μάλλον θα άλλαζαν).

Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

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

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

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

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

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

Σύνδεση

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

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

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