Conseils Python utiles que vous n'avez jamais rencontrés auparavant. 2e partie

Nous avons récemment publié une traduction du matériel, qui a fourni des conseils utiles aux programmeurs Python. Ce matériel a une suite que nous portons à votre attention aujourd'hui.



Attribution d'un nom aux tranches à l'aide de la fonction de tranche


Travailler avec les nombreuses valeurs fournies par les index peut rapidement devenir un gâchis - à la fois en termes de support et en termes de lisibilité du code. Une façon d'améliorer la situation consiste à utiliser des constantes pour les valeurs spécifiées par les index. Mais il existe une meilleure façon d'écrire du code de qualité:

#       ID First Name   Last Name line_record = "2    John Smith" ID = slice(0, 8) FIRST_NAME = slice(9, 21) LAST_NAME = slice(22, 27) name = f"{line_record[FIRST_NAME].strip()} {line_record[LAST_NAME].strip()}" # name == "John Smith" 

Dans cet exemple, vous pouvez voir qu'en donnant des noms aux tranches à l'aide de la fonction slice et en utilisant ces noms pour obtenir les fragments de la chaîne, nous avons pu nous débarrasser des index complexes. Vous pouvez en savoir plus sur l'objet slice en utilisant ses .start , .stop et .step .

Demander un mot de passe à l'utilisateur lors de l'exécution du programme


De nombreux outils ou scripts de ligne de commande nécessitent un nom d'utilisateur et un mot de passe pour fonctionner. Si vous devez écrire un tel programme, vous pourriez trouver le module getpass :

 import getpass user = getpass.getuser() password = getpass.getpass() #   ... 

Ce package très simple vous permet de demander le mot de passe d'un utilisateur, ainsi que d'obtenir un nom d'utilisateur en récupérant le nom sous lequel il est connecté. Cependant, lorsque vous travaillez avec des mots de passe, vous devez savoir que tous les systèmes ne prennent pas en charge la dissimulation des mots de passe. Python essaiera de vous avertir. Si cela se produit, vous verrez un avertissement correspondant sur la ligne de commande.

Recherche de correspondances serrées dans les chaînes


Parlons maintenant d'une fonctionnalité légèrement plus mystérieuse de la bibliothèque standard Python. Supposons que vous vous trouviez dans une situation où vous aviez besoin, en utilisant un concept tel que la distance de Levenshtein , de trouver des mots dans la liste qui ressemblent à une certaine ligne d'entrée. Ce problème peut être résolu en utilisant le module difflib .

 import difflib difflib.get_close_matches('appel', ['ape', 'apple', 'peach', 'puppy'], n=2) # returns ['apple', 'ape'] 

La méthode difflib.get_close_matches les meilleures correspondances «assez bonnes». Le premier argument de cette méthode spécifie la chaîne de recherche, le deuxième argument spécifie la liste dans laquelle la recherche est effectuée. Cette méthode peut recevoir un argument facultatif n , qui spécifie le nombre maximal de correspondances renvoyées. Cette méthode prend également en charge la cutoff facultative des arguments nommés (par défaut, elle est définie sur 0.6 ), ce qui vous permet de définir une valeur de seuil pour évaluer les correspondances.

Travailler avec des adresses IP


Si vous devez écrire des programmes Python pour travailler avec le réseau, cela signifie que le module ipaddress peut vous être très utile. Une option pour l'utiliser consiste à générer une liste d'adresses IP à partir d'une plage d'adresses spécifiées au format CIDR (routage inter-domaine sans classe, adressage sans classe).

 import ipaddress net = ipaddress.ip_network('74.125.227.0/29') #      IPv6 # IPv4Network('74.125.227.0/29') for addr in net:    print(addr) # 74.125.227.0 # 74.125.227.1 # 74.125.227.2 # 74.125.227.3 # ... 

Une autre fonctionnalité utile de ce module est de vérifier l'adresse IP pour son appartenance à un certain réseau:

 ip = ipaddress.ip_address("74.125.227.3") ip in net # True ip = ipaddress.ip_address("74.125.227.12") ip in net # False 

Le module ipaddress possède de nombreuses autres fonctionnalités intéressantes dont je ne parle pas ici. En savoir plus sur lui ici . Certes, en utilisant ce module, considérez les limitations concernant son travail conjoint avec d'autres modules liés à la programmation réseau. Par exemple, vous ne pouvez pas utiliser des instances IPv4Network comme chaînes d'adresse. Les objets similaires doivent d'abord être convertis en chaînes à l'aide de str .

Débogage d'un programme sur la ligne de commande


Si vous êtes un de ceux qui ne veulent pas utiliser l'IDE et écrit du code dans Vim ou Emacs, alors vous pourriez être dans une situation où vous auriez besoin d'un débogueur comme ceux de l'IDE. Et tu sais quoi? Vous avez déjà un tel débogueur. Pour l'utiliser, il suffit d'exécuter le programme en utilisant une structure comme python3.8 -i . L'indicateur -i permet, à la fin du programme, de lancer un shell interactif. En l'utilisant, vous pouvez examiner des variables et appeler des fonctions. C'est une fonctionnalité intéressante, mais qu'en est-il d'un vrai débogueur (pdb)? Essayons avec le programme simple suivant, dont le code se trouve dans le fichier script.py :

 def func():    return 0 / 0 func() 

Exécutez-le avec la python3.8 -i script.py et obtenez ce qui suit:

 #   ... Traceback (most recent call last):  File "script.py", line 4, in <module>    func()  File "script.py", line 2, in func    return 0 / 0 ZeroDivisionError: division by zero >>> import pdb >>> pdb.pm() #      > script.py(2)func() -> return 0 / 0 (Pdb) 

Nous voyons l'endroit du programme où le crash s'est produit. Définissez un point d'arrêt:

 def func():    breakpoint() # import pdb; pdb.set_trace()    return 0 / 0 func() 

Exécutez à nouveau le script.

script.py(3)func()
-> return 0 / 0
(Pdb) #
(Pdb) step
ZeroDivisionError: division by zero
> script.py(3)func()
-> return 0 / 0
(Pdb)

Dans la plupart des situations, les résultats de la commande d' print et de la trace sont suffisants pour le débogage de scripts, mais parfois pour faire face à un échec complexe, vous devez creuser dans le programme et comprendre l'essence de ce qui se passe. Dans de tels cas, des points d'arrêt sont définis dans le code et le programme est examiné. Par exemple, ils regardent les arguments des fonctions, évaluent les expressions, vérifient les valeurs des variables ou, comme indiqué ci-dessus, exécutent simplement le code étape par étape. Pdb est un wrapper Python entièrement fonctionnel. Dans cette coque, vous pouvez presque tout faire. Au cours du travail, certaines commandes de débogage spécifiques seront utiles, pour lesquelles de l'aide peut être trouvée ici .

Déclaration de plusieurs constructeurs dans une classe


La surcharge de fonctions est l'une des fonctionnalités largement utilisées dans divers langages de programmation, mais pas en Python. Et même si vous ne pouvez pas surcharger une fonction régulière en Python, nous pouvons utiliser quelque chose comme surcharger les constructeurs en utilisant des méthodes de classe:

 import datetime class Date:    def __init__(self, year, month, day):        self.year = year        self.month = month        self.day = day    @classmethod    def today(cls):        t = datetime.datetime.now()        return cls(t.year, t.month, t.day) d = Date.today() print(f"{d.day}/{d.month}/{d.year}") # 14/9/2019 

Dans une situation similaire, au lieu d'utiliser des méthodes de classe, vous pourriez être tenté de mettre toute la logique des constructeurs alternatifs dans __init__ et de résoudre le problème en utilisant *args , **kwargs et beaucoup d' if . Le résultat peut être du code fonctionnel, mais ce code sera difficile à lire et à maintenir. Ici, je recommanderais de mettre un minimum de logique dans __init__ et d'effectuer toutes les opérations dans des méthodes / constructeurs distincts. Avec cette approche, nous aurons à notre disposition un code propre qui sera pratique à la fois pour l'auteur de ce code et quiconque utilisera ce code.

Mise en cache des résultats d'appels de fonction à l'aide d'un décorateur


Avez-vous déjà écrit des fonctions qui ont effectué de longues opérations de lecture / écriture ou des calculs récursifs plutôt lents? Dans le même temps, pensiez-vous que la mise en cache des résultats ne nuirait pas à ces fonctions? Vous pouvez mettre en cache (mémoriser) les résultats d'un appel de fonction en utilisant le décorateur functools module functools :

 from functools import lru_cache import requests @lru_cache(maxsize=32) def get_with_cache(url):    try:        r = requests.get(url)        return r.text    except:        return "Not Found" for url in ["https://google.com/",            "https://martinheinz.dev/",            "https://reddit.com/",            "https://google.com/",            "https://dev.to/martinheinz",            "https://google.com/"]:    get_with_cache(url) print(get_with_cache.cache_info()) # CacheInfo(hits=2, misses=4, maxsize=32, currsize=4) 

Dans cet exemple, nous exécutons des requêtes GET dont les résultats sont mis en cache (jusqu'à 32 résultats peuvent être mis en cache). Ici, vous pouvez voir que nous obtenons des informations sur le cache de fonction en utilisant la méthode cache_info . Le décorateur nous donne également la méthode clear_cache , qui est utilisée pour clear_cache cache. Ici, je voudrais également noter que la mise en cache ne peut pas être utilisée avec des fonctions qui ont des effets secondaires, ou avec des fonctions qui créent des objets modifiables à chaque appel.

Recherche des éléments qui se trouvent le plus souvent dans l'objet itérable


Faire partie de la liste de ces éléments qui s'y trouvent plus souvent que d'autres est une tâche très courante. Vous pouvez le résoudre, par exemple, en utilisant la boucle for et un dictionnaire, qui collectera des informations sur le nombre d'éléments identiques. Mais une telle approche est une perte de temps. Le fait est que vous pouvez résoudre ces problèmes en utilisant la classe Counter du module collections :

 from collections import Counter cheese = ["gouda", "brie", "feta", "cream cheese", "feta", "cheddar",          "parmesan", "parmesan", "cheddar", "mozzarella", "cheddar", "gouda",          "parmesan", "camembert", "emmental", "camembert", "parmesan"] cheese_count = Counter(cheese) print(cheese_count.most_common(3)) # : [('parmesan', 4), ('cheddar', 3), ('gouda', 2)] 

Les mécanismes internes de la classe Counter sont basés sur un dictionnaire qui stocke la correspondance des éléments et le nombre d'entrées dans la liste. Par conséquent, l'objet correspondant peut être utilisé comme un objet dict standard:

 print(cheese_count["mozzarella"]) # : 1 cheese_count["mozzarella"] += 1 print(cheese_count["mozzarella"]) # : 2 

De plus, lorsque nous travaillons avec Counter , nous avons à notre disposition la méthode de update(more_words) , qui est utilisée pour ajouter de nouveaux éléments au compteur. Une autre caractéristique utile de Counter est qu'il vous permet d'utiliser des opérations mathématiques (addition et soustraction) lorsque vous travaillez avec des instances de cette classe.

Résumé


Je pense que la plupart des conseils donnés aujourd'hui peuvent très bien être utilisés par ceux qui écrivent en Python presque quotidiennement. J'espère que vous trouverez parmi eux quelque chose qui vous sera utile.

Chers lecteurs! Connaissez-vous des astuces de programmation Python intéressantes? Si oui, merci de les partager.

Source: https://habr.com/ru/post/fr485646/


All Articles