Contenu | Rechercher | Menus

Annonce

Si vous avez des soucis pour rester connecté, déconnectez-vous puis reconnectez-vous depuis ce lien en cochant la case
Me connecter automatiquement lors de mes prochaines visites.

À propos de l'équipe du forum.

#26 Le 18/05/2016, à 12:24

Elzen

Re : /* Topic des codeurs [9] */

GTK2 était une bonne bibliothèque. GTK3 en sera une quand ça arrêtera de casser les thèmes à chaque version (j'avais parlé trop vite dans l'autre post, d'ailleurs: greybird ne doit pas marcher non plus, puisque la tronche est rigoureusement la même quand je sélectionne d'autres thèmes. Juste, ça ne déclenche pas une tonne de warnings en sortie standard, ce qui aide un peu à débugger les soucis de ton code.)

J'ai vaguement tenté de me lancer dans Qt, mais la logique de fonctionnement m'avait un brin refroidi. J'avais voulu me pencher sur wxWidgets, mais je ne sais plus pour quoi j'avais laissé tomber. Ne connaissant pas trop d'autres bibliothèques graphiques simplement utilisables pour ce genre de trucs, je crois que je vais rester sur GTK un moment, en espérant qu'on finisse par pouvoir thémer ça correctement.

grim7reaper a écrit :

- c’est plus propre/sûr d’utiliser "with … as" pour ouvrir les fichier (la fermeture est géré automatiquement, même en cas d‘exception).

Ouaip, je le sais, en plus >< J'n'ai pas encore réussi à prendre ce réflexe.

grim7reaper a écrit :

- il faut mieux essayer d’ouvrir le fichier et attraper l’exception si souci plutôt que tester l’existence du fichier avant ouverture (risque de race : le fichier eput être supprimé entre la réussite de ton test et l’ouverture du fichier).

Hum, sachant que les deux instructions se suivent immédiatement (donc qu'il faut quand même y aller pour caser la suppression pile au bon moment depuis un autre processus), et qu'au pire, un plantage à ce niveau ne serait pas franchement grave (il suffirait de relancer make une seconde fois), est-ce vraiment important ?
À la base, quand j'ai appris à coder, on m'a expliqué que c'était toujours préférable de gérer des conditionnelles plutôt que des exceptions, donc j'ai spontanément tendance à faire ça (mais c'est vrai que je n'ai jamais eu de réelle explication sur le sujet)

(On note d'ailleurs que j'étais fatigué au moment de poster, puisque le « bon » test à faire dans ce cas n'est pas « os.path.exists(filepath) », mais « os.access(filepath, os.R_OK) »…)

grim7reaper a écrit :

- toujours préciser les encodages utilisés (There Ain't No Such Thing As Plain Text).

Ouaip, j'ai un brin creusé depuis ce message, et j'ai tilté que la différence bytes/str semble juste la suite de l'ancienne différence str/unicode smile

Hors ligne

#27 Le 18/05/2016, à 14:27

grim7reaper

Re : /* Topic des codeurs [9] */

Elzen a écrit :
grim7reaper a écrit :

- il faut mieux essayer d’ouvrir le fichier et attraper l’exception si souci plutôt que tester l’existence du fichier avant ouverture (risque de race : le fichier eput être supprimé entre la réussite de ton test et l’ouverture du fichier).

Hum, sachant que les deux instructions se suivent immédiatement (donc qu’il faut quand même y aller pour caser la suppression pile au bon moment depuis un autre processus)

Ça, ça ne veut rien dire. Ton processus peut se faire préempter/mettre en pause pendant un temps plus ou moins long entre la ligne X et X+1 et du coup, quand tu exécutes la ligne X+1 ton test de la ligne X est déjà obsolète.
C’est un vrai problème (cf. TOCTTOU).
Et puis, elles se suivent au niveau du code source Python mais à l’exécution il se passe pleins de trucs au niveau interpréteur, elles sont séparées par beaucoup plus que le code ne le laisse croire.

Elzen a écrit :

et qu'au pire, un plantage à ce niveau ne serait pas franchement grave (il suffirait de relancer make une seconde fois), est-ce vraiment important ?

On est d’accord, dans ton cas précis ce n’est pas très important. Je faisais plus une remarque d’ordre générale, et c’est le genre de choses à savoir quand on fait du code où ça importe.
Et puis autant faire du code qui fonctionne à tout les coup wink

Elzen a écrit :

À la base, quand j'ai appris à coder, on m'a expliqué que c'était toujours préférable de gérer des conditionnelles plutôt que des exceptions, donc j'ai spontanément tendance à faire ça (mais c'est vrai que je n'ai jamais eu de réelle explication sur le sujet)

En général je suis d‘accord avec cette approche, mais ici c’est un bon exemple où il faut “Ask for forgiveness, not permission” car faire un test est buggué.

Elzen a écrit :

(On note d'ailleurs que j'étais fatigué au moment de poster, puisque le « bon » test à faire dans ce cas n'est pas « os.path.exists(filepath) », mais « os.access(filepath, os.R_OK) »…)

Il faudrait aussi ajouter le test os.path.isfile tongue

Hors ligne

#28 Le 18/05/2016, à 22:38

Pylades

Re : /* Topic des codeurs [9] */

On est en 2016, et il reste encore du monde pour qui Python3 n'est pas la langue maternelle? tongue
Dépêchez-vous, Python4 arrive. tongue


“Any if-statement is a goto. As are all structured loops.
“And sometimes structure is good. When it’s good, you should use it.
“And sometimes structure is _bad_, and gets into the way, and using a goto is just much clearer.”
                Linus Torvalds – 12 janvier 2003

Hors ligne

#29 Le 19/05/2016, à 07:35

grim7reaper

Re : /* Topic des codeurs [9] */

Pylades a écrit :

Dépêchez-vous, Python 8 arrive. tongue

Fixed tongue

Dernière modification par grim7reaper (Le 19/05/2016, à 07:36)

Hors ligne

#30 Le 20/05/2016, à 00:04

Pylades

Re : /* Topic des codeurs [9] */

En effet, ô taon pour moi.


“Any if-statement is a goto. As are all structured loops.
“And sometimes structure is good. When it’s good, you should use it.
“And sometimes structure is _bad_, and gets into the way, and using a goto is just much clearer.”
                Linus Torvalds – 12 janvier 2003

Hors ligne

#31 Le 20/05/2016, à 19:24

HP

Re : /* Topic des codeurs [9] */

Pylades a écrit :

Dépêchez-vous, Python4 arrive. tongue

Ou passez à Ruby… big_smile


cat /dev/urandom >/dev/null 2>&1 #github

Hors ligne

#32 Le 21/05/2016, à 16:20

grim7reaper

Re : /* Topic des codeurs [9] */

Elzen sur fadrienn.irlnc.org/galeries/outils/petite_base_de_donnees a écrit :

Naturellement, tout retour à ce sujet sera le bienvenu!

J’ai seulement jeté un œil à _base.py pour le moment, voilà mes remarques.
Le layout du code est un peu bizarre par endroit je trouve, mais bon c’est ton style je suppose ^^

Fonction `query`
Tu pourrais utiliser le context manager pour avoir auto/commit-rollback
Cf. la doc.


Fonction `check`
Tu pourrais pas utiliser finally pour le conn.close?


Fonction `escape`
Je ne comprends pas trop tes critères d’échappements.
J’ai trouvé ce post à ce sujet, mais ça ne correspond peut-être pas à ce que tu veux faire.


Fonction `loaddate`
Cette ligne est pas franchement claire :

return tuple(int(part) for part in parts)+(1,)*(3-len(parts))+(len(parts),)

Il pourrait aussi y avoir une docstring sur le format géré en entrée.


Fonction `savedate`
Les 0,1,2, et 3 pourrait être une énumération pour plus de lisibilité.


Fonction `loadcoords`
Il pourrait y avoir une docstring sur le format géré en entrée. Ce n’est pas clair en lisant le code.
Y’a une trailing space l. 131


Fonction `savecoords`
Il pourrait y avoir une docstring sur le format produit en sortie.
Tu pourrais lancer une exception un peu plus claire que Exception (qui est très générique), et puis ajouter un message d’erreur aussi.


Dans le "main"
C’est bizarre ce code au milieu de nulle part, ni dans une fonction ni guardé par un if __name__ == '__main__':

if dbfile is None: sys.exit(0)

=> Pourrait donner un message d’erreur.

elif os.access(dbfile, os.R_OK) and os.access(dbfile, os.W_OK):

=> Fait plutôt un try/catch (os.access ne vérifie pas que c’est un fichier, donc tu peux toujours échouer).


C’est tout pour le moment big_smile

Hors ligne

#33 Le 21/05/2016, à 17:33

Elzen

Re : /* Topic des codeurs [9] */

grim7reaper a écrit :

Le layout du code est un peu bizarre par endroit je trouve, mais bon c’est ton style je suppose ^^

Tu as le droit de dire que c'est moche ^^' C'est grosso-modo ce qui se passe quand j'essaye d'appliquer sérieusement PEP8 à un moment ou à un autre: je finis par craquer et me mettre à faire n'importe quoi juste pour m'amuser. Le code de Touhy en fait les frais aussi.

grim7reaper a écrit :

Fonction `query`
Tu pourrais utiliser le context manager pour avoir auto/commit-rollback
Cf. la doc.

Je vais creuser ça, oui smile Je ne suis pas familier avec ce genre de syntaxe, mais ça a effectivement l'air préférable.

grim7reaper a écrit :

Fonction `check`
Tu pourrais pas utiliser finally pour le conn.close?

Effectivement. Je n'ai pas assez ce réflexe (j'ai mis du temps à découvrir le finally, et je ne l'ai encore jamais utilisé en Python)

grim7reaper a écrit :

Fonction `escape`
Je ne comprends pas trop tes critères d’échappements.
J’ai trouvé ce post à ce sujet, mais ça ne correspond peut-être pas à ce que tu veux faire.

J'avais la flemme de vérifier quels caractères on pouvait, ou pas, utiliser pour les noms de tables et de colonnes dans SQLite, alors j'ai fais un truc simple et dont j'étais sûr qu'il marcheravit à tous les coups: uniquement des lettres non-accentuées, des chiffres et des underscore, en s'assurant que le nom de la table commence par une lettre (ce qui me permet d'utiliser des noms commençant par un underscore pour les tables/colonnes utilisées par l'appli pour fonctionner sans risquer de conflit).

(À la base, c'est une version légèrement modifée du code qui me sert à échapper les mots pour mon jeu de pendu)

grim7reaper a écrit :

Fonction `loaddate`
Cette ligne est pas franchement claire :

return tuple(int(part) for part in parts)+(1,)*(3-len(parts))+(len(parts),)

Le retour doit être de la forme «(année, mois, jours, précision)», avec la précision qui varie entre 1 (à l'année près) et 3 (au jour près), en fonction de ce que contient la chaîne d'entrée («2010-05», par exemple, correspond à Mai 2010 avec une précision de 2, et le jour étant inconnu, on va mettre le premier du mois).

Du coup, un split sur le tiret, dont on convertit les éléments en entiers, on bourre des 1 à la fin, et on ajoute la taille pour la précision.

grim7reaper a écrit :

Il pourrait aussi y avoir une docstring sur le format géré en entrée.

En effet. Le code entier manque cruellement de commentaire. C'est moche, mais ça m'évitait d'avoir à me décider entre le français (c'est un petit machin fait sur mesure, même pas internationalisé) et l'anglais (qui fait un brin plus sérieux pour un dépôt ouvert et avec des technos pas obsolètes pour une fois).

grim7reaper a écrit :

Fonction `savedate`
Les 0,1,2, et 3 pourrait être une énumération pour plus de lisibilité.

Sans doute, mais je n'ai encore jamais créé d'énumérations en Python, il faudra que j'aille me renseigner sur comment on fait.

grim7reaper a écrit :

Fonction `loadcoords`
Il pourrait y avoir une docstring sur le format géré en entrée. Ce n’est pas clair en lisant le code.
Y’a une trailing space l. 131

Chez moi, l'espace en trop est ligne 128 yikes Mais merci, je vais corriger.

Pour le format d'entrée, c'est vrai que ce n'est pas bien clair. D'ailleurs, j'en ai changé en cours de route. Tu sais s'il existe un format «standard» facile à parser pour noter les lattitude/longitude? (Du genre du «AAAA-MM-JJ» pour les dates).

grim7reaper a écrit :

Fonction `savecoords`
Il pourrait y avoir une docstring sur le format produit en sortie.
Tu pourrais lancer une exception un peu plus claire que Exception (qui est très générique), et puis ajouter un message d’erreur aussi.

Le «Exception» tout court vient du fait que le code était initialement intégré à la fonction qui se sert de ça (dans _data.py), à l'intérieur d'un try/catch, que j'ai déplacé pour tenter vaguement de clarifier un brin. C'est vrai que, maintenant, ça manque de précision.

Il faudrait de toute façon que je reprenne la découpe des différentes parties un peu plus sérieusement.

grim7reaper a écrit :

Dans le "main"
C’est bizarre ce code au milieu de nulle part, ni dans une fonction ni guardé par un if __name__ == '__main__':

if dbfile is None: sys.exit(0)

=> Pourrait donner un message d’erreur.

Il n'y a pas de conditionnelle, parce que ce code n'est jamais en position de __main__ (mais c'est vrai que, du coup, ça fait très bizarre).

Au lancement du programme, le fichier principal (elzbase.py) importe le fichier _base.py, qui gère toute la connexion à la base. Comme c'est ce fichier qui gère ça, il est le seul à avoir besoin de connaître le fichier contenant la base. Donc, il ouvre sys.argv, regarde s'il y a un nom de fichier précisé dedans, et, s'il n'y en a pas, ouvre une popup de sélection de fichier.

Le seul cas pour lequel dbfile peut valoir None, donc, c'est quand l'utilisateur a fermé cette popup sans avoir sélectionné de fichier, ce que j'interprète comme une demande de fermeture de l'appli (donc une fermeture normale, exit(0) et pas de message d'erreur). Mais c'est vrai que ça mériterait, au minimum, d'être commenté.

grim7reaper a écrit :
elif os.access(dbfile, os.R_OK) and os.access(dbfile, os.W_OK):

=> Fait plutôt un try/catch (os.access ne vérifie pas que c’est un fichier, donc tu peux toujours échouer).

Hum, os.access renvoie False quand le fichier demandé n'existe pas, quel que soit le droit d'accès demandé. Ce qui correspond à ce que je veux à ce niveau (même si, cf la discussion précédente, ça n'assure pas qu'il n'y a pas de changements sur le disque entre temps).

D'ailleurs, tu peux constater toi-même qu'il y a bien un try/catch juste en dessous, qui gère la même chose (même si son rôle à l'origine est de s'assurer que le fichier qui va être ouvert est bien une base et contient bien les bonnes tables minimales). En revanche, la création de la variable tables est un brin mal placée.

Mais ouaip, y a sans doute de la clarification à faire à ce niveau aussi.

grim7reaper a écrit :

C’est tout pour le moment big_smile

C'est déjà pas mal big_smile

D'ailleurs, si ça intéresse d'autres gens, coder une petite appli de ce genre peut former un genre de défi TdC, ça fait longtemps ^^

Hors ligne

#34 Le 22/05/2016, à 22:52

grim7reaper

Re : /* Topic des codeurs [9] */

Elzen a écrit :

Tu as le droit de dire que c'est moche ^^'

C’est pas très esthétique par endroit (quand tu mets beaucoup de choses sur une ligne), en effet tongue

Elzen a écrit :

(j'ai mis du temps à découvrir le finally, et je ne l'ai encore jamais utilisé en Python)

Ça existe aussi en Java pourtant.
Quoique depuis que Java supporte try-with-resources (Java 7) ça deviens moins indispensable.

Elzen a écrit :

uniquement des lettres non-accentuées, des chiffres et des underscore, en s'assurant que le nom de la table commence par une lettre.

Ok, une petite réecriture rends la choses plus lisible je pense (mais ça ne mange pas de pain d’ajouter une docstring quand même wink).

def escape(name):
    name = unicodedata.normalize('NFKD', name).replace(' ', '_')
    allowed_chars = string.ascii_letters + string.digits + '_'
    esc = ''.join(c for c in name if c in allowed_chars)
    return esc if esc and esc[0].isalpha() else None
Elzen a écrit :

Le retour doit être de la forme «(année, mois, jours, précision)», avec la précision qui varie entre 1 (à l'année près) et 3 (au jour près), en fonction de ce que contient la chaîne d'entrée («2010-05», par exemple, correspond à Mai 2010 avec une précision de 2, et le jour étant inconnu, on va mettre le premier du mois).

Du coup, un split sur le tiret, dont on convertit les éléments en entiers, on bourre des 1 à la fin, et on ajoute la taille pour la précision.

Je trouve qu’en utilisant une petite classe ça devient plus lisible :

class Date(object):
    def __init__(self, precision, year, month=1, day=1):
        self.precision = precision
        self.year  = year
        self.month = month
        self.day   = day

    @classmethod
    def load(cls, date):
    """Doc sur le format d’entrée"
        # Centuries (written as roman numbers)
        if 'e' in date:
            century, suffix = date.split('e', 1)
            century = roman.fromRoman(century) - 1
            if re.sub(r'[. ]', '', suffix.lower()).endswith("avjc"):
                century = -century
            return cls(precision=0, year=century*100)
        else:
            parts = map(int, date.split('-'))
            return cls(precision=len(parts), *parts)
Elzen a écrit :

Sans doute, mais je n'ai encore jamais créé d'énumérations en Python, il faudra que j'aille me renseigner sur comment on fait.

Avant, fallait bidouiller.
Mais depuis Python 3.4 il y a un module pour ça.

Elzen a écrit :

Tu sais s'il existe un format «standard» facile à parser pour noter les lattitude/longitude? (Du genre du «AAAA-MM-JJ» pour les dates).

ISO 6709 peut-être.

Elzen a écrit :

Hum, os.access renvoie False quand le fichier demandé n'existe pas, quel que soit le droit d'accès demandé. Ce qui correspond à ce que je veux à ce niveau

Ce que je veux dire c’est que os.access va te renvoyer True même si c’est un répertoire au lieu d’un fichier.

Elzen a écrit :

D'ailleurs, si ça intéresse d'autres gens, coder une petite appli de ce genre peut former un genre de défi TdC, ça fait longtemps ^^

Faudrait qu’il y ai plus de gens actifs pour qu’avoir un défi soit intéressant je pense.

Hors ligne

#35 Le 23/05/2016, à 00:26

Elzen

Re : /* Topic des codeurs [9] */

grim7reaper a écrit :

Ça existe aussi en Java pourtant.

D'où le fait que j'ai découvert ça avant de l'utiliser en Python, même si c'est récent tongue

grim7reaper a écrit :

Ok, une petite réecriture rends la choses plus lisible je pense (mais ça ne mange pas de pain d’ajouter une docstring quand même wink).

def escape(name):
    name = unicodedata.normalize('NFKD', name).replace(' ', '_')
    allowed_chars = string.ascii_letters + string.digits + '_'
    esc = ''.join(c for c in name if c in allowed_chars)
    return esc if esc and esc[0].isalpha() else None

Je passais par string.ascii_letters+string.digits au début; puis j'me suis dit qu'un import en plus juste pour ça, ce n'était pas forcément utile… (même si c'est dans les packages par défaut, donc ça ne doit strictement rien changer).

C'est vrai que faire ça en plusieurs lignes avec des variables intermédiaires, des fois, ça aide. Pour ma défense, comme je l'ai dit, la forme actuelle vient d'un bout de code que j'ai copié-collé et ajusté plusieurs fois. Or, le bout de code d'origine était un lambda, d'où le fait que ce soit toujours tout en une ligne… On va dire que ça va être l'occasion de le redévelopper convenablement smile

grim7reaper a écrit :

Je trouve qu’en utilisant une petite classe ça devient plus lisible :

class Date(object):
    def __init__(self, precision, year, month=1, day=1):
        self.precision = precision
        self.year  = year
        self.month = month
        self.day   = day

    @classmethod
    def load(cls, date):
    """Doc sur le format d’entrée"
        # Centuries (written as roman numbers)
        if 'e' in date:
            century, suffix = date.split('e', 1)
            century = roman.fromRoman(century) - 1
            if re.sub(r'[. ]', '', suffix.lower()).endswith("avjc"):
                century = -century
            return cls(precision=0, year=century*100)
        else:
            parts = map(int, date.split('-'))
            return cls(precision=len(parts), *parts)

Sur le rapport taille du code/utilisabilité, on y perd un peu quand même tongue

Mais ouaip, c'est beaucoup mieux avec une classe. D'une manière générale, tous mes types de données un peu avancés sont actuellement définis par deux choses:
– Une classe dans _data.py qui gère la création de table (demander les différentes valeurs possibles pour un choix multiple et les enregistrer dans la base, les autres types n'ayant rien de spécial à faire à ce niveau) et la partie édition des valeurs (le widget qui va bien pour saisir la valeur comme il faut, par exemple une petite popup avec un webkit sur OpenStreetMap pour le calendrier),
– Éventuellement, des fonctions de load/save (nom pas forcément bien trouvé, d'ailleurs) dans _base.py.

Ça vaudrait sans doute largement le coup de reprendre ça correctement, par exemple en mettant un fichier .py par type, qui en gère tous les aspects. D'ailleurs, comment fait-on pour gérer les plugins de manière propre? J'aimerais bien qu'il soit possible de rajouter un nouveau type de données à la base en ayant juste à coder le fichier .py qui va bien et en le posant dans le répertoire, sans avoir à modifier le reste du projet…

grim7reaper a écrit :

Avant, fallait bidouiller.
Mais depuis Python 3.4 il y a un module pour ça.

Cool smile J'vais prendre ça.

grim7reaper a écrit :

ISO 6709 peut-être.

Merci, je vais regarder plus en détail…

grim7reaper a écrit :

Ce que je veux dire c’est que os.access va te renvoyer True même si c’est un répertoire au lieu d’un fichier.

Ah oui >< J'ai du mal à capter, des fois.
(Ceci dit, dans le cas présent, je pense que juste le try/except sans conditionnelle du tout serait le plus approprié, puisque, comme on l'a dit plus tôt, la conditionnelle ne sert finalement pas à grand chose).

grim7reaper a écrit :

Faudrait qu’il y ai plus de gens actifs pour qu’avoir un défi soit intéressant je pense.

C'est vrai qu'à part l'interpréteur brainfuck, je n'ai pas le souvenir d'un truc pour lequel il y a eu plusieurs productions à comparer…

Mais j'encourage toute personne qui serait tentée à faire un truc du genre de celui que j'ai fais là smile

Hors ligne

#36 Le 23/05/2016, à 17:14

grim7reaper

Re : /* Topic des codeurs [9] */

Elzen a écrit :

D'ailleurs, comment fait-on pour gérer les plugins de manière propre? J'aimerais bien qu'il soit possible de rajouter un nouveau type de données à la base en ayant juste à coder le fichier .py qui va bien et en le posant dans le répertoire, sans avoir à modifier le reste du projet…

Je pense qu’il suffit de définir une interface à implémenter pour ton plugin (ton type ici donc) et d’avoir un moyen de l’enregistrer au sein du projet pour qu’il soit utilisable par le reste du code.

Hors ligne

#37 Le 26/05/2016, à 13:01

Elzen

Re : /* Topic des codeurs [9] */

Ouaip, c'est sur la seconde partie que je bute ^^"
Pour l'instant, je ne connais que deux moyens de faire intégrer un module donné à un programme:

  • Faire un import direct depuis la classe qui gère ça, ce qui veut dire qu'il faut la modifier chaque fois qu'on ajoute un nouveau module,

  • Passer par la fonction __import__ à partir du nom du fichier concerné, ce qui est plus souple mais me semble un brin moche, et pas forcément applicable dans tous les cas.

En l'occurrence, la seconde manière ça marcherait bien pour charger le code relatif à la modification d'un champ donné en lisant le type de champ dans la base; mais ce serait moins pratique pour lister tous les types possibles lors de la création des tables (probable nécessité de passer par la lecture du contenu du répertoire concerné…).


Sinon, dans un tout autre registre, vous sauriez s'il existe un moyen de débrancher un gtk.TextView du presse-papier PRIMARY? Je voudrais améliorer un brin ce truc, mais le fait que toute sélection dans la popup fasse perdre la sélection dans le champ d'origine gêne un peu le côté contextuel…
Alternativement, un widget d'édition de texte n'étant pas relié au (ou pouvant être débranché du)dit presse-papier me conviendrait aussi, même si c'est dans une autre bibli graphique…

Hors ligne

#38 Le 26/05/2016, à 19:43

HP

Re : /* Topic des codeurs [9] */

Elzen a écrit :

mais ce serait moins pratique pour lister tous les types possibles lors de la création des tables (probable nécessité de passer par la lecture du contenu du répertoire concerné…).

Avec une factory ?


cat /dev/urandom >/dev/null 2>&1 #github

Hors ligne

#39 Le 26/05/2016, à 22:30

Elzen

Re : /* Topic des codeurs [9] */

Tu peux détailler?

Pour moi, factory désigne l'entité chargée d'instancier les objets dont il y a besoin sans avoir à manipuler le type réel de ces objets, tous implémentant la même interface. D'une part, ça a moins d'intérêt dans les langages à duck typing (où tu te fiches déjà du type réel des objets, pourvus qu'ils disposent des méthodes que tu appelles dessus). D'autre part, ça ne résout pas le problème, puisqu'il faut que la fabrique connaisse les différents types réels à disposition, ce qui est le cœur du soucis ici.

Si on tente de formuler ça différemment, on peut dire que la question ne porte pas sur l'usage ou pas du patron de conception factory (j'utilise de toute façon un truc très proche), mais sur la façon dont coder une fabrique assez dynamique pour qu'on puisse ajouter des types sans avoir à modifier son code.
Si tu vois comment faire, je prends smile

Hors ligne

#40 Le 27/05/2016, à 09:30

grim7reaper

Re : /* Topic des codeurs [9] */

@Elzen : il suffirait pas que la factory (ou ce que tu vas utiliser pour centraliser tes types) exporte une methode register_type et que les type/plugin vont appeler pour s’enregistrer auprès de ladite factory ?

Hors ligne

#41 Le 27/05/2016, à 12:02

Elzen

Re : /* Topic des codeurs [9] */

Si les modules en question sont chargés, si, ils peuvent s'ajouter tous seuls sans problème.

Mais si rien dans le reste du code ne les importe, comment on les charge?

Hors ligne

#42 Le 27/05/2016, à 12:27

grim7reaper

Re : /* Topic des codeurs [9] */

Elzen a écrit :

Mais si rien dans le reste du code ne les importe, comment on les charge?

Y’a pas de magie : va falloir charger le code à un moment (même en C ou C++, quand tu fais des plugin à un moment tu les charges avec dlopen).
importlib devrait te permettre de faire ça (via import_module peut-être).

Hors ligne

#43 Le 27/05/2016, à 12:55

grim7reaper

Re : /* Topic des codeurs [9] */

Sinon j’ai regardé _data.py (un peu moins en détails que _base.py)

Tu pourrais avoir une classe abstraite à la base de tout ça (cf. abc).
Même si c’est pas obligatoire en Python ça rendrait le code plus sûr (y'aurait pas d’oubli d’implémenter une méthode requise) et ça permettrait de factoriser un peu le code (genre label pourrait être dans la classe de base).
Ça permettrait aussi de documenter ton interface (parce que là, le booléen renvoyé par la méthode prepare je n‘ai aucune idée de son rôle (ça viendra sûrement en lisant le reste du code, mais je devrais pas avoir à faire ça pour comprendre normalement)).


Pourquoi

sys.modules["_base"]

plusieurs fois au lieu de faire un simple import _base.
Je suppose que c’est liée à une des remarques de mon message précédent (à propos du code qui traîne en dehors de toute fonctions).


while parent.get_parent() is not None: parent = parent.get_parent()

Ça c’est moche sérieusement, le while sur une ligne (j’ai vu que tu le faisais pour les if/else et c’est déjà pas terrible, mais le while…)


if value is not None and value != "":

Ça pourrait être if value tout simplement je pense


for i in range(len(combo.get_model())):
    if combo.get_model()[i][0] == value:
        combo.set_active(i)

Tu pourrais utiliser enumerate ici:

for idx, val in enumerate(combo.get_model()):
    if val[0] == value:
        combo.set_active(idx)

Ce genre de trucs

wv.load_uri(url); scroll.add(wv)

et

try: entry.set_text(sys.modules["_base"].savecoords(wv.get_uri()))
except Exception: pass

C‘est aussi très très moyen, surtout que tu le fait de manière aléatoire on dirait (du coup c'est moche ET pas cohérent…).
Les virgules aussi (un coup y’ une espace après, et des fois non…).
Ça aide vraiment pas à lire le code (un code qui n’a pas de cohérence est difficile à suivre : la forme empêche de se concentrer sur le fond) hmm

Hors ligne

#44 Le 27/05/2016, à 13:59

Elzen

Re : /* Topic des codeurs [9] */

grim7reaper a écrit :
Elzen a écrit :

Mais si rien dans le reste du code ne les importe, comment on les charge?

Y’a pas de magie : va falloir charger le code à un moment (même en C ou C++, quand tu fais des plugin à un moment tu les charges avec dlopen).
importlib devrait te permettre de faire ça (via import_module peut-être).

Je vais creuser de ce côté, merci smile

grim7reaper a écrit :

Tu pourrais avoir une classe abstraite à la base de tout ça (cf. abc).
Même si c’est pas obligatoire en Python ça rendrait le code plus sûr (y'aurait pas d’oubli d’implémenter une méthode requise) et ça permettrait de factoriser un peu le code (genre label pourrait être dans la classe de base).
Ça permettrait aussi de documenter ton interface (parce que là, le booléen renvoyé par la méthode prepare je n‘ai aucune idée de son rôle (ça viendra sûrement en lisant le reste du code, mais je devrais pas avoir à faire ça pour comprendre normalement)).

Je n'avais encore jamais creusé la façon dont faire des classes abstraites/interfaces en Python. Ça me semble une bonne occasion de m'y mettre smile

(Ceci dit, je vais vraisemblablement changer quelques trucs à la façon de fonctionner actuelle: le fait qu'il y ait besoin de passer par une table supplémentaire pour paramétrer des options (seulement pour le choix multiple, actuellement, mais si on rajoute des types…) ne me satisfait pas des masses. Je pense que je tâcherai de mettre les éventuelles options dans la même colonne que la définition du type, ce sera plus souple à l'usage).

grim7reaper a écrit :

Pourquoi

sys.modules["_base"]

plusieurs fois au lieu de faire un simple import _base.
Je suppose que c’est liée à une des remarques de mon message précédent (à propos du code qui traîne en dehors de toute fonctions).

Je n'ai encore rien eu le temps de modifier depuis ton message précédent.

À la base, il me semble que c'était un problème d'ordre des imports, dû au fait que _data et _base se chargent l'un-l'autre. Comme je n'aime pas mettre des import à des endroits arbitraires du code (autres qu'au début, s'entend), j'avais fait ça pour contourner. Ce sera corrigé au moment où je referai proprement la découpe.

grim7reaper a écrit :
if value is not None and value != "":

Ça pourrait être if value tout simplement je pense

Je n'ai pas assez l'habitude de tester autre chose que des booléens smile Je sais que None est considéré comme faux, mais je n'y pense jamais pour 0 et "".

grim7reaper a écrit :
for i in range(len(combo.get_model())):
    if combo.get_model()[i][0] == value:
        combo.set_active(i)

Tu pourrais utiliser enumerate ici:

for idx, val in enumerate(combo.get_model()):
    if val[0] == value:
        combo.set_active(idx)

Tiens, je n'avais jamais remarqué enumerate… C'est vrai que c'est largement mieux.

Je suis d'accord pour les remarques sur la forme, je vais corriger tout ça (dès que j'aurai un brin de temps).

Hors ligne

#45 Le 30/05/2016, à 19:32

Slystone

Re : /* Topic des codeurs [9] */

/me a vu la lumière et pousse la porte
Bon je vais squatter un peu dans le coin, j'avance tout doucement sur le python. D'ailleurs, si vous avez des conseils pour un programmeur en codeur en herbe, je suis preneur (comme je ne suis aucun cours, je cherche une sorte de roadmap pour m'organiser, dans le genre: listes d'exercices (classés par difficulté croissante) pour se faire la main, forum/chat pour les noobs, etc.).


«Rigid, the skeleton of habit alone upholds the human frame.» - Virginia Woolf.

Hors ligne

#46 Le 31/05/2016, à 07:53

grim7reaper

Re : /* Topic des codeurs [9] */

Hey bienvenue !

Du sang neuf !

Slystone a écrit :

si vous avez des conseils pour un programmeur en codeur en herbe, je suis preneur

Hésite pas à poster ton code pour avoir des retours : ça évite de prendre de mauvaise habitudes quand on débutes et ça permets de découvrir certains trucs du langage plus rapidement (un exemple juste au dessus de ton message, avec enumerate wink).
Sinon tu peux aussi utiliser Pylint sur ton code pour vérifier que tu respectes bien la PEP 8 (même si pylint est parfois plus royaliste que le roi, ça peut aider quand même).

Slystone a écrit :

listes d'exercices (classés par difficulté croissante) pour se faire la main

Project Euler et Rosalind sont sympa je trouve.

Slystone a écrit :

forum/chat pour les noobs

chat : je ne sais pas.
forum : tu peux poser tes questions ici, ça fera vivre le topic smile

Hors ligne

#47 Le 31/05/2016, à 10:03

Slystone

Re : /* Topic des codeurs [9] */

Merci pour l'accueil grim7reaper, et pour les conseils, t'es au top. smile
C'est la deuxième fois qu'on me parle du projet Euler, et Rosalind, de nom ça me dit quelque chose. Je les mets donc tout en haut de ma python_todo. \o/

J'ai un projet final: faire un quiz avec une base saine (pour que ce soit multiprotocol, multiplateforme, avec les bonnes meta-données pour chaque question (comme la date vu que le truc change tous les ans…). Bref, je me rends compte que je vais devoir cravacher pour sortir un truc propre et correct. ^^ Mais c'est une bonne excuse pour attaquer enfin la programmation sérieusement.


«Rigid, the skeleton of habit alone upholds the human frame.» - Virginia Woolf.

Hors ligne

#48 Le 16/06/2016, à 08:54

Oni_Shadow

Re : /* Topic des codeurs [9] */

Bon, puisque il faut poster son code, je vais m'y essayer tongue
Ayant parlé plus haut du projet Euler, je m'y suis essayé. J'ai commencé par faire les problème le plus vite possible puis à partir du numéro 7, je me suis mis à essayer d'être plus fin. Aussi, n'étant ni mathématicien, ni informaticien, je n'ai nul doute que meilleur solution est possible tant côté mathématique que du côté du code. Qu 'à cela ne tienne ! avec votre aide je compte bien remédier à cela !
Si vous avez une solution plus habile ou une façon plus esthétique de coder, merci de partager smile
alors :
Trouver le 10001 nombre premier
méthode : commencer à 2, ajouter un nombre comme premier s'il n'est divisible par aucun nombre premier qui le précède.

number = 10001
c = 1
n = 2
prime = [2]

while c < number:
    n += 1
    for p in prime:
        if n%p == 0:
            break
        elif p == prime[-1]:
            prime.append(n)
            c += 1
print(prime[-1])

(tout simple donc)
maintenant, en dépit de la simplicité du problème suivant, (surtout par la méthode forte), je ne trouve pas la bonne solution.
Le but est de trouver le plus haut produit en utilisant n=13 chiffres contiguës d'un nombre à 1000 chiffres
Un exemple est donné avec n=4 -> 9*9*8*9 = 5832
Je suis parti sur un algorithme de complexité n, qui fonctionne pour n=4 mais ne marche pas pour n=13 sad
ma méthode est la suivante :
utiliser des addition plutôt que des multiplication pour trouver la plus grande somme (et en retenir la position) ; il me semble que les additions sont moins coûteuses.
avancer en ajoutant le i (position) + n chiffre et en supprimant le i-1 pour avoir la somme de la position i. Si on rencontre un zéro, ne pas comptabiliser la somme puisque la multiplication serait nulle.
Malheureusement, il doit y avoir une erreur quelque part

#!/bin/python3

number1000digit = 7316717653133062491922511967442657474235534919493496983520312774506326239578318016984801869478851843858615607891129494954595017379583319528532088055111254069874715852386305071569329096329522744304355766896648950445244523161731856403098711121722383113622298934233803081353362766142828064444866452387493035890729629049156044077239071381051585930796086670172427121883998797908792274921901699720888093776657273330010533678812202354218097512545405947522435258490771167055601360483958644670632441572215539753697817977846174064955149290862569321978468622482839722413756570560574902614079729686524145351004748216637048440319989000889524345065854122758866688116427171479924442928230863465674813919123162824586178664583591245665294765456828489128831426076900422421902267105562632111110937054421750694165896040807198403850962455444362981230987879927244284909188845801561660979191338754992005240636899125607176060588611646710940507754100225698315520005593572972571636269561882670428252483600823257530420752963450
nb = str(number1000digit)  # transformer numbre en tableau
digit = 13
somme = 0
sommeMax = 0
position = 0
zeros = []

# somme initiale
for i in range(digit):
    somme += int(nb[i])
    if int(nb[i]) == 0:
        zeros.append(i)
sommeMax = somme

# avancer dans le tableau
for i in range(1, len(nb)-digit):
    if int(nb[i + digit-1]) == 0:
        zeros.append(i + digit-1)
    if zeros and zeros[0] == i-1:
        del zeros[0]
    somme -= int(nb[i - 1])
    somme += int(nb[i + digit])
    if not zeros and somme > sommeMax:  # if no zeros in sum
        sommeMax = somme
        position = i

# multiply numbers starting at position
multiply = 1
for i in range(digit):
    multiply *= int(nb[i + position])
    print(nb[i + position], position)
print(multiply)

Dernière modification par Oni_Shadow (Le 16/06/2016, à 08:56)


Rouillé

Hors ligne

#49 Le 16/06/2016, à 12:26

grim7reaper

Re : /* Topic des codeurs [9] */

Oni_Shadow a écrit :

méthode : commencer à 2, ajouter un nombre comme premier s'il n'est divisible par aucun nombre premier qui le précède.

Ça ressemble à la méthode des divisions successives, tu aurais aussi pu utiliser le crible d’Ératosthène.
Note que tu n’as pas besoin de tester tout les nombre premier qui le précède : pour tester N, tu peux te contenter de tester les nombres premiers ≤ √N.

Oni_Shadow a écrit :

utiliser des addition plutôt que des multiplication pour trouver la plus grande somme (et en retenir la position) ; il me semble que les additions sont moins coûteuses.

Optimisation prématurée, et qui ne te donnera sûrement aucun gain en Python de toute façons. Ça va seulement rendre ton code moins lisible pour rien.
Ton code me semble compliqué.

Personnellement, je découperai le travail comme ça :
1. Découper le nombre en bloc de N chiffre : f("123456789", 4) => ["1234", "2345", "3456", "4567", "5678", "6789"].
2. Calculer le produit de chaque bloc.
3. Garder le maximum.

Ça tiens en 10 lignes de Python, mais je ne vais pas poster tout de suite, ça te spoilerai.
Je pense que tu n’es pas loin de la solution, mais tu as tellement compliqué ton code en essayant de l’optimiser (de la mauvaise façon) que tu as du rajouter des bugs en chemin.
Repart sur un truc plus simple et ça devrait le faire wink

Hors ligne

#50 Le 19/06/2016, à 16:30

Rolinh

Re : /* Topic des codeurs [9] */

Elzen a écrit :

GTK2 était une bonne bibliothèque. GTK3 en sera une quand ça arrêtera de casser les thèmes à chaque version

Et avec GTK4+ qui cassera l'API+ABI tous les 6 mois, c'est pas gagné tongue

Meanwhile, Gtk 4.0 will not be the final stable API of what we would call “Gtk 4”. Each 6 months, the new release (Gtk 4.2, Gtk 4.4, Gtk 4.6) will break API and ABI vs. the release that came before it. These incompatible minor versions will not be fully parallel installable; they will use the same pkg-config name and the same header file directory.

source


Oui, /me s'abonne au nouveau topic...

Hors ligne