@Pythonetc compilation, juillet 2018

Ceci est la deuxième collection de conseils et de programmation Python de mon flux @pythonetc . Sélections précédentes:


Langues régulières


Un langage régulier est un langage formel qui peut être représenté comme une machine à états finis . En d'autres termes, pour le traitement de texte caractère par caractère, il vous suffit de vous souvenir de l'état actuel et le nombre de ces états est fini.

Un exemple parfait: une machine qui vérifie si l'entrée est un nombre premier comme –3, 2.2 ou 001. Au début de l'article, une machine à états finis est affichée. Les doubles cercles indiquent l'état final dans lequel la machine peut s'arrêter.

La machine démarre à partir de la position . Il trouve peut-être un moins, puis un chiffre, puis à la position ③ il traite le nombre de chiffres requis. Après cela, le séparateur décimal (③ → ④) peut être vérifié, suivi d'un chiffre (④ → ⑤) ou plus (⑤ → ⑤).

Un exemple classique d'une langue irrégulière est une famille d'expressions de chaîne de la forme:

ab
aaa-bbb
aaaaa-bbbbb


Formellement, nous avons besoin d'une chaîne contenant N instances de a , puis , puis - N instances de b , où N est un entier supérieur à 0. Vous ne pouvez pas l'implémenter avec une machine à états, car vous devrez vous souvenir du nombre de caractères que vous pensiez pouvoir fait uniquement en utilisant un nombre infini d'états.

Les expressions régulières ne peuvent spécifier que des langues régulières. Avant de les utiliser, assurez-vous que votre chaîne peut même être traitée à l'aide d'une machine d'état. Par exemple, ils ne conviennent pas au traitement d'expressions JSON, XML ou même arithmétiques avec des parenthèses.

C'est drôle que de nombreux moteurs regex modernes ne soient pas réguliers. Par exemple, le module regex pour Python prend en charge la récursivité (ce qui aidera à résoudre le problème avec aaa-bbb ).

Planification dynamique


Lorsque Python fait un appel de méthode, disons af(b, c, d) , il doit d'abord sélectionner la fonction correcte f . En vertu du polymorphisme, a détermine ce qui sera finalement choisi. Le processus de sélection d'une méthode est communément appelé répartition dynamique.

Python ne prend en charge que le polymorphisme à répartition unique. Cela signifie que seul l'objet lui-même affecte le choix de l'objet (dans notre exemple, a ). Dans d'autres langues, les types b , c et d peuvent être pris en compte - un tel mécanisme est appelé répartition multiple. Un exemple frappant est le langage C #.

Cependant, plusieurs planifications peuvent être émulées en utilisant une seule. C'est pourquoi le modèle de conception des visiteurs a été créé: il utilise deux fois une seule expédition pour simuler un double.

N'oubliez pas que les méthodes de surcharge (comme en Java et C ++) ne sont pas un analogue de la répartition multiple. La répartition dynamique fonctionne lors de l'exécution et la surcharge n'est effectuée que pendant la compilation.

Ces exemples vous aideront à mieux comprendre le sujet:


Noms en ligne


En Python, vous pouvez facilement modifier toutes les variables standard disponibles dans la portée globale:

 >>> print = 42 >>> print(42) Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: 'int' object is not callable 

Ceci est utile si votre module définit des fonctions dont les noms correspondent aux noms des fonctions intégrées. Cela se produit également dans les situations où vous pratiquez la métaprogrammation et prenez une valeur de chaîne arbitraire comme identifiant.

Mais même si vous dupliquez les noms de certaines fonctions intégrées, vous devrez peut-être accéder à ce à quoi elles faisaient référence à l'origine. C'est pourquoi le module intégré existe:

 >>> import builtins >>> print = 42 >>> builtins.print(1) 1 

Également dans la plupart des modules, la variable __builtins__ est disponible. Mais il y a une astuce. Tout d'abord, c'est une fonctionnalité de l'implémentation de Cpython, et ne devrait généralement pas être utilisée du tout. Deuxièmement, __builtins__ peut faire référence à la fois aux commandes builtins et aux commandes builtins.__dict__ , selon la façon dont le module actuel a été chargé.

strace


Parfois, l'application commence à se comporter étrangement au combat. Au lieu de le redémarrer, vous souhaiterez peut-être comprendre la cause des problèmes tant que cela est possible.

La solution évidente consiste à analyser les actions du programme et à essayer de comprendre quelle partie du code est exécutée. Une journalisation appropriée facilite cette tâche, mais vos journaux peuvent ne pas être suffisamment détaillés en raison de l'architecture ou du niveau de journalisation sélectionné dans les paramètres.

Dans de tels cas, strace peut être utile. Il s'agit d'un utilitaire Unix qui suit les appels système. Vous pouvez l'exécuter précédemment - strace python script.py - mais il est généralement plus pratique de se connecter à une application déjà en cours d'exécution: strace -p PID .

 $ cat test.py with open('/tmp/test', 'w') as f: f.write('test') $ strace python test.py 2>&1 | grep open | tail -n 1 open("/tmp/test", O_WRONLY|O_CREAT|O_TRUNC|O_CLOEXEC, 0666) = 3 

Chaque ligne de la trace contient le nom de l'appel système, les arguments entre crochets et la valeur de retour. Étant donné que certains arguments sont utilisés pour renvoyer le résultat d'un appel système et non pour lui transmettre des données, la sortie de ligne peut être suspendue jusqu'à la fin de l'appel système.

Dans cet exemple, la sortie est arrêtée jusqu'à ce que l'écriture dans STDIN soit terminée:

 $ strace python -c 'input()' read(0, 

Littéraux de tuple


Les littéraux de tuple sont l'une des parties les plus incohérentes de la syntaxe Python.

Pour créer un tuple, il suffit de lister les valeurs séparées par des virgules: 1, 2, 3 . Qu'en est-il d'un tuple à un seul élément? Ajoutez simplement une virgule suspendue: 1, ,. Cela a l'air moche et mène souvent à des erreurs, mais c'est tout à fait logique.

Que diriez-vous d'un tuple vide? Ceci est une virgule - ,? Non, c'est () . Et quoi, les parenthèses créent un tuple, comme des virgules? Non, (4) n'est pas un tuple, c'est juste 4 .

 In : a = [ ...: (1, 2, 3), ...: (1, 2), ...: (1), ...: (), ...: ] In : [type(x) for x in a] Out: [tuple, tuple, int, tuple] 

Pour confondre encore plus les choses, les littéraux de tuple nécessitent souvent des parenthèses supplémentaires. Si vous avez besoin que le tuple soit le seul argument de la fonction, alors évidemment f(1, 2, 3) ne fonctionnera pas - vous devrez écrire f((1, 2, 3)) .

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


All Articles