13Fermer15
flankerLe 19/04/2017 à 10:31
Pour le code Python, pas grand-chose à redire, sauf peut-être que :

* l'exemple devrait être dans une docstring en tant que doctest (bout de code Python qui est dans la documentation mais détecté par certains outils et exécuté comme un test unitaire)
* le shebang indique /usr/bin/env python mais ça ressemble à une lib (donc qui ne sera pas exécutée -> shebang inutile) et les classes n'héritent pas d'object (encore une erreur de Python 2)
* les espaces avant les parenthèses, je suis comme Pen², mais avec un argument : la PEP008 (qui est le style officiel) dit de ne pas en mettre, et pour le coup je trouve vraiment dommage de ne pas respecter la PEP008

Pour le subprocess… ça se tient, même si je n'aurais peut-être pas procédé ainsi (j'aurais sûrement cherché à hériter de Popen, mais ce n'est peut-être pas faisable). Ça mériterait probablement d'améliorer le module standard ^^
Je pense que ce module est des plus vieux et un des moins « pythonesques » de la bibliothèque de base.


Petit exemple pour du code perso. J'ai un package complet avec la gestion de tous les trucs de base (unités de mesure, dates incomplètes, coordonnées géographiques, …) dont un pour les noms d'avions :
names.py
# coding=utf-8 from functools import cmp_to_key import unicodedata def normalize_string(string: str): """ normalise la chaîne de caractère pour supprimer les accents et les caractères spéciaux et tout mettre en miniscule >>> normalize_string('abCD') 'abcd' >>> normalize_string('éèfi') 'eefi' >>> e1 = unicodedata.normalize('NFKC', 'é') >>> e2 = unicodedata.normalize('NFKD', 'é') >>> e3 = unicodedata.normalize('NFC', 'é') >>> e4 = unicodedata.normalize('NFD', 'é') >>> e = '%s %s %s %s' % (e1, e2, e3, e4) >>> normalize_string(e) 'e e e e' >>> e.encode() b'\xc3\xa9 e\xcc\x81 \xc3\xa9 e\xcc\x81' """ return unicodedata.normalize('NFKD', string.lower()).encode('ascii', 'ignore').decode('ascii') __author__ = 'flanker' NAME_COMMON = '1common' NAME_IZDELIYE = '2izdelye' NAME_NATO_TEMP = '3nato_temp' NAME_USE = '4US' NAME_NATO = '5nato' NAME_NATO_2 = '5nato_2nd' NAME_SURNAME = '6surname' NAME_UNOFFICIAL = '7unofficial' NAME_INTERNAL = '8internal' NAME_OTHER = '9other' NAME_US_PRE_1962 = '8uspre1962' NAME_NATO_PRE_1955 = '8otanpre1955' NAMES = {NAME_COMMON: 'Nom habituel', NAME_IZDELIYE: 'Izdelye (ex. « 8 »)', NAME_NATO_TEMP: 'Code OTAN temporaire (ex. « Ram-J »)', NAME_USE: 'Dénomination américaine de missile (ex. « AA-10 »)', NAME_NATO: 'Code OTAN (ex. : « Fishbed »)', NAME_NATO_2: 'Code OTAN secondaire (ex. « Mongol »)', NAME_SURNAME: 'Surnom officiel (ex. : « Fighting Falcon »)', NAME_UNOFFICIAL: 'Surnom officieux (ex. : « Viper »)', NAME_INTERNAL: 'Nom de projet interne (ex. : « T10 »)', NAME_OTHER: 'Autre nom', NAME_US_PRE_1962: 'Avant la réforme de 1962', NAME_NATO_PRE_1955: 'Code OTAN original (avant 1955, ex. : « 31 »)', } VERBOSE_NAMES = {NAME_COMMON: 'Nom', NAME_IZDELIYE: 'Izdelye', NAME_NATO_TEMP: 'Code OTAN avant identification', NAME_USE: 'Identifiant américan', NAME_NATO: 'Code OTAN', NAME_NATO_2: 'Autre code OTAN', NAME_SURNAME: 'Surnom', NAME_UNOFFICIAL: 'Surnom', NAME_INTERNAL: 'Nom de projet interne', NAME_OTHER: 'Autre', NAME_US_PRE_1962: 'Avant la réforme américaine de 1962', NAME_NATO_PRE_1955: 'Type OTAN avant la réforme de 1955', } def create_name_context(common, manufacturers, other_names, join_char='/'): """ Renvoie un dictionnaire dont les clefs sont les différents types de nom. Toutes les clefs existeront, même si c'est vide :param common: nom commun :param manufacturers: Django query pour les constructeurs de l'objets :param other_names: iterable de :class:`aviationsmilitaires.base.models.Name` :param join_char: caractère de jointure """ names = {x: [] for x in NAMES} if manufacturers: manufacturers = join_char.join([m.name for m in manufacturers.filter(state_enterprise=False)]) else: manufacturers = '' for name in other_names: names[name.category].append(name.name) template_values = {'manu': manufacturers, 'nato2': ', '.join(names[NAME_NATO_2]), 'pre55': ', '.join(names[NAME_NATO_PRE_1955]), 'nato': ', '.join(names[NAME_NATO]), 'common': ' '.join([common] + names[NAME_COMMON] + names[NAME_OTHER]), 'pre62': ', '.join(names[NAME_US_PRE_1962]), 'surname': ' '.join(names[NAME_SURNAME] + names[NAME_UNOFFICIAL]), 'us': ', '.join(names[NAME_USE]), 'internal': ', '.join(names[NAME_INTERNAL] + ['Izdelye %s' % x for x in names[NAME_IZDELIYE]]), 'nato_temp': ', '.join(names[NAME_NATO_TEMP]), 'izdelyie': ', '.join(names[NAME_IZDELIYE]), 'all_natos': ', '.join(names[NAME_NATO] + names[NAME_NATO_2]), } if template_values['nato2'] and not template_values['nato']: template_values['nato'] = template_values['nato2'] template_values['nato2'] = '' return template_values def render(template, ctx): """ génère un nom complet propre à partir d'un dict et d'un template >>> render('%(manu)s %(common)s (%(nato)s)', {'manu': '', 'common': 'Su-27', 'nato': 'Flanker'}) 'Su-27 (Flanker)' """ value = template % ctx value2 = value.replace(' ', ' ') while value2 != value: value, value2 = value2, value2.replace(' ', ' ') value = value2.replace(' )', ')').replace('( ', '(').replace('()', '') return value.strip() def name_cmp(a: str, b: str, normalize: bool=True): """Permet de trier des noms de façon intelligente ("F-2" < "F-10") :param a: première chaîne à comparer :param b: seconde chaîne à comparer :param normalize: normaliser les deux valeurs d'abord ? :return: 1, 0, ou -1 >>> name_cmp('aaaa', 'bbbb') -1 >>> name_cmp('F-20', 'F-117') -1 >>> name_cmp('F-117', 'F-20') 1 >>> name_cmp('F-117', 'F-20A') 1 >>> name_cmp('F-117A3', 'F-117AB') 1 >>> name_cmp('Su-27S', 'Su-27P') 1 """ if normalize: a = normalize_string(a) b = normalize_string(b) index_a, index_b, len_a, len_b, ct_a, ct_b = 0, 0, len(a), len(b), 0, 0 while index_a < len_a and index_b < len_b: char_a = a[index_a] char_b = b[index_b] ct_a, ct_b, num = 0, 0, False while index_a < len_a and index_b < len_b and '0' <= a[index_a] <= '9' and '0' <= b[index_b] <= '9': num = True ct_a = 10 * ct_a + int(a[index_a]) ct_b = 10 * ct_b + int(b[index_b]) index_a += 1 index_b += 1 while index_a < len_a and '0' <= a[index_a] <= '9': ct_a = 10 * ct_a + int(a[index_a]) index_a += 1 while index_b < len_b and '0' <= b[index_b] <= '9': ct_b = 10 * ct_b + int(b[index_b]) index_b += 1 if 0 < ct_a < ct_b: return -1 elif ct_a > ct_b > 0 or char_a > char_b: return 1 elif char_a < char_b: return -1 if not num: index_a += 1 index_b += 1 if len_a < len_b: return -1 elif len_a > len_b: return 1 return 0 name_key = cmp_to_key(name_cmp) if __name__ == '__main__': import doctest doctest.testmod()