Fonctionnalités cachées de Python [fermé]

1419

Quelles sont les fonctionnalités moins connues mais utiles du langage de programmation Python?

  • Essayez de limiter les réponses au noyau Python.
  • Une fonctionnalité par réponse.
  • Donnez un exemple et une brève description de la fonction, pas seulement un lien vers la documentation.
  • Étiquetez l'entité en utilisant un titre comme première ligne.

Liens rapides vers les réponses:

compie
la source

Réponses:

740

Chaînage des opérateurs de comparaison:

>>> x = 5
>>> 1 < x < 10
True
>>> 10 < x < 20 
False
>>> x < 10 < x*10 < 100
True
>>> 10 > x <= 9
True
>>> 5 == x > 4
True

Dans le cas où vous pensez que c'est en train de faire 1 < x, qui apparaît comme True, puis en comparant True < 10, ce qui est aussi True, alors non, ce n'est vraiment pas ce qui se passe (voir le dernier exemple.) Il se traduit vraiment en 1 < x and x < 10, et x < 10 and 10 < x * 10 and x*10 < 100, mais avec moins de frappe et chaque terme n'est évalué qu'une seule fois.

Thomas Wouters
la source
121
C'est très utile. Il devrait être standard pour toutes les langues. Malheureusement, ce n'est pas le cas.
stalepretzel
8
vous devez ajouter quelques exemples qui renvoient également faux. comme >>> 10 <x <20 False
ShoeLace
19
Cela s'applique également aux autres opérateurs de comparaison, c'est pourquoi les gens sont parfois surpris de voir que du code comme (5 dans [5] est Vrai) est Faux (mais il n'est pas du tout logique de tester explicitement contre des booléens comme ça pour commencer).
Miles
19
Bon mais attention à la priorité égale, comme «in» et «=». «A en B == C en D» signifie «(A en B) et (B == C) et (C en D)», ce qui pourrait être inattendu.
Charles Merriam
15
Azafe: les comparaisons de Lisp fonctionnent naturellement de cette façon. Ce n'est pas un cas particulier car il n'y a pas d'autre moyen (raisonnable) d'interpréter (< 1 x 10). Vous pouvez même les appliquer à des arguments uniques, comme (= 10): cs.cmu.edu/Groups/AI/html/hyperspec/HyperSpec/Body/…
Ken
512

Obtenez l'arbre d'analyse des expressions rationnelles python pour déboguer votre expression régulière.

Les expressions régulières sont une grande fonctionnalité de python, mais leur débogage peut être pénible, et il est trop facile de se tromper en termes d'expression régulière.

Heureusement, python peut imprimer l'arbre d'analyse rationnelle, en passant le drapeau caché, expérimental et non documenté re.DEBUG(en fait, 128) à re.compile.

>>> re.compile("^\[font(?:=(?P<size>[-+][0-9]{1,2}))?\](.*?)[/font]",
    re.DEBUG)
at at_beginning
literal 91
literal 102
literal 111
literal 110
literal 116
max_repeat 0 1
  subpattern None
    literal 61
    subpattern 1
      in
        literal 45
        literal 43
      max_repeat 1 2
        in
          range (48, 57)
literal 93
subpattern 2
  min_repeat 0 65535
    any None
in
  literal 47
  literal 102
  literal 111
  literal 110
  literal 116

Une fois que vous avez compris la syntaxe, vous pouvez repérer vos erreurs. Là , nous pouvons voir que j'ai oublié d'échapper à la []dans[/font] .

Bien sûr, vous pouvez le combiner avec les indicateurs que vous souhaitez, comme les expressions rationnelles commentées:

>>> re.compile("""
 ^              # start of a line
 \[font         # the font tag
 (?:=(?P<size>  # optional [font=+size]
 [-+][0-9]{1,2} # size specification
 ))?
 \]             # end of tag
 (.*?)          # text between the tags
 \[/font\]      # end of the tag
 """, re.DEBUG|re.VERBOSE|re.DOTALL)
BatchyX
la source
3
Sauf que l'analyse HTML à l'aide d'une expression régulière est lente et pénible. Même le module d'analyseur «html» intégré n'utilise pas de regex pour effectuer le travail. Et si le module html ne vous plaît pas, il existe de nombreux modules d'analyseur XML / HTML qui font le travail sans avoir à réinventer la roue.
BatchyX
Un lien vers la documentation sur la syntaxe de sortie serait formidable.
Personman
1
Cela devrait être une partie officielle de Python, pas expérimental ... RegEx est toujours délicat et pouvoir retracer ce qui se passe est vraiment utile.
Cahit
460

énumérer

Enveloppez un itérable avec énumérer et il donnera l'élément avec son index.

Par exemple:


>>> a = ['a', 'b', 'c', 'd', 'e']
>>> for index, item in enumerate(a): print index, item
...
0 a
1 b
2 c
3 d
4 e
>>>

Références:

Dave
la source
56
Je suis surpris que cela ne soit pas couvert régulièrement dans les didacticiels sur les listes python.
Draemon
45
Et pendant tout ce temps, je codais de cette façon: pour i dans la plage (len (a)): ... puis en utilisant un [i] pour obtenir l'élément actuel.
Fernando Martin
4
@Berry Tsakala: À ma connaissance, il n'a pas été déprécié.
JAB
23
Merde, c'est génial. pour i dans xrange (len (a)): a toujours été mon idiome python le moins préféré.
Personman
15
énumération peut commencer à partir d'un index arbitraire, pas nécessaire 0. Exemple: 'pour i, élément dans énumération ( liste , début = 1): imprimer i, élément' commencera l'énumération à partir de 1, pas de 0.
dmitry_romanov
419

Création d'objets générateurs

Si vous écrivez

x=(n for n in foo if bar(n))

vous pouvez sortir le générateur et l'affecter à x. Maintenant, cela signifie que vous pouvez faire

for n in x:

L'avantage de ceci est que vous n'avez pas besoin de stockage intermédiaire, dont vous auriez besoin si vous le faisiez

x = [n for n in foo if bar(n)]

Dans certains cas, cela peut entraîner une accélération importante.

Vous pouvez ajouter de nombreuses instructions if à la fin du générateur, en répliquant essentiellement les boucles imbriquées:

>>> n = ((a,b) for a in range(0,2) for b in range(4,6))
>>> for i in n:
...   print i 

(0, 4)
(0, 5)
(1, 4)
(1, 5)
espace libre
la source
Vous pouvez également utiliser une compréhension de liste imbriquée pour cela, oui?
shapr
54
Les économies de mémoire sont particulièrement importantes. Les valeurs sont calculées à la demande, vous n'avez donc jamais le résultat complet de la compréhension de la liste en mémoire. Cela est particulièrement souhaitable si vous réitérez ultérieurement une partie seulement de la compréhension de la liste.
saffsd
19
Ce n'est pas particulièrement imo "caché", mais il convient également de noter le fait que vous ne pouvez pas rembobiner un objet générateur, alors que vous pouvez réitérer une liste autant de fois que vous le souhaitez.
suspend
13
La fonction «sans rembobinage» des générateurs peut créer une certaine confusion. Plus précisément, si vous imprimez le contenu d'un générateur pour le débogage, puis que vous l'utilisez plus tard pour traiter les données, cela ne fonctionne pas. Les données sont produites, consommées par print (), puis ne sont pas disponibles pour le traitement normal. Cela ne s'applique pas aux listes de compréhension, car elles sont entièrement stockées en mémoire.
johntellsall
4
Réponse similaire (dup?): Stackoverflow.com/questions/101268/hidden-features-of-python/… Notez cependant que la réponse que j'ai liée ici mentionne une VRAIMENT BONNE présentation sur la puissance des générateurs. Vous devriez vraiment le vérifier.
Denilson Sá Maia
353

iter () peut prendre un argument appelable

Par exemple:

def seek_next_line(f):
    for c in iter(lambda: f.read(1),'\n'):
        pass

La iter(callable, until_value)fonction appelle callableet produit son résultat à plusieurs reprises jusqu'à ce qu'elle until_valuesoit renvoyée.

mbac32768
la source
En tant que débutant en python, pouvez-vous expliquer pourquoi le lambdamot-clé est nécessaire ici?
SiegeX
@SiegeX sans lambda, f.read (1) serait évalué (renvoyant une chaîne) avant d'être passé à la fonction iter. Au lieu de cela, le lambda crée une fonction anonyme et la transmet à iter.
jmilloy
339

Soyez prudent avec les arguments par défaut modifiables

>>> def foo(x=[]):
...     x.append(1)
...     print x
... 
>>> foo()
[1]
>>> foo()
[1, 1]
>>> foo()
[1, 1, 1]

Au lieu de cela, vous devez utiliser une valeur sentinelle indiquant "non fourni" et remplacer par le mutable que vous souhaitez par défaut:

>>> def foo(x=None):
...     if x is None:
...         x = []
...     x.append(1)
...     print x
>>> foo()
[1]
>>> foo()
[1]
Jason Baker
la source
39
C'est certainement l'une des fonctionnalités cachées les plus méchantes. Je l'ai rencontré de temps en temps.
Torsten Marek
77
J'ai trouvé cela beaucoup plus facile à comprendre quand j'ai appris que les arguments par défaut vivent dans un tuple qui est un attribut de la fonction, par exemple foo.func_defaults. Qui, étant un tuple, est immuable.
Robert Rossney
2
@grayger: Lorsque l'instruction def est exécutée, ses arguments sont évalués par l'interpréteur. Cela crée (ou lie à nouveau) un nom à un objet de code (la suite de la fonction). Cependant, les arguments par défaut sont instanciés en tant qu'objets au moment de la définition. Cela est vrai à tout moment de l'objet par défaut, mais seulement significatif (exposant la sémantique visible) lorsque l'objet est modifiable. Il n'y a aucun moyen de réaffecter ce nom d'argument par défaut dans la fermeture de la fonction, bien qu'il puisse évidemment être remplacé pour tout appel ou que la fonction entière puisse être redéfinie).
Jim Dennis
3
@Robert bien sûr, les arguments du tuple peuvent être immuables, mais les objets qu'il pointe ne sont pas nécessairement immuables.
poolie
16
Un hack rapide pour raccourcir un peu votre initialisation: x = x ou []. Vous pouvez utiliser cela à la place de la ligne 2 if.
Dave Mankoff
317

Envoi de valeurs dans les fonctions du générateur . Par exemple, avoir cette fonction:

def mygen():
    """Yield 5 until something else is passed back via send()"""
    a = 5
    while True:
        f = (yield a) #yield a and possibly get f in return
        if f is not None: 
            a = f  #store the new value

Vous pouvez:

>>> g = mygen()
>>> g.next()
5
>>> g.next()
5
>>> g.send(7)  #we send this back to the generator
7
>>> g.next() #now it will yield 7 until we send something else
7
Rafał Dowgird
la source
D'accord.
Traitons
89
Dans d'autres langues, je crois que ce dispositif magique est appelé une "variable".
finnw
5
les coroutines doivent être des coroutines et le générateur doit également être lui-même, sans mélange. Méga-grand lien et discussions et exemples à ce sujet ici: dabeaz.com/coroutines
u0b34a0f6ae
31
@finnw: l'exemple implémente quelque chose de similaire à une variable. Cependant, la fonctionnalité peut être utilisée de nombreuses autres façons ... contrairement à une variable. Il devrait également être évident que des sémantiques similaires peuvent être implémentées à l'aide d'objets (une classe implémentant la méthode d' appel de Python , en particulier).
Jim Dennis
4
C'est un exemple trop trivial pour les gens qui n'ont jamais vu (et ne comprendront probablement pas) des co-routines. L'exemple qui met en œuvre la moyenne mobile sans risque de débordement de somme variable est bon.
Prashant Kumar
313

Si vous n'aimez pas utiliser les espaces pour désigner les étendues, vous pouvez utiliser le style C {} en émettant:

from __future__ import braces
eduffy
la source
122
C'est mal. :)
Jason Baker
37
>>> à partir de __future__ import accolades Fichier "<stdin>", ligne 1 SyntaxError: pas une chance: P
Benjamin W. Smith
40
c'est du blasphème!
Berk D. Demir
335
Je pense que nous pouvons avoir une erreur syntaxique ici, ne devrait-il pas s'agir de "from __past__ import braces "?
Bill K
47
de __cruft__ import accolades
Phillip B Oldham
305

L'argument step dans les opérateurs de tranche. Par exemple:

a = [1,2,3,4,5]
>>> a[::2]  # iterate over the whole list in 2-increments
[1,3,5]

Le cas spécial x[::-1]est un idiome utile pour «x inversé».

>>> a[::-1]
[5,4,3,2,1]
Rafał Dowgird
la source
31
à mon avis, la fonction reverse () est beaucoup plus claire. >>> liste (inversée (plage (4))) [3, 2, 1, 0]
Christian Oudard
3
alors comment mieux écrire "cette chaîne ia" [:: - 1]? inversé ne semble pas aider
Berry Tsakala
24
Le problème avec reverse () est qu'il retourne un itérateur, donc si vous voulez conserver le type de la séquence inversée (tuple, string, list, unicode, types d'utilisateurs ...), vous avez besoin d'une étape supplémentaire pour le reconvertir .
Rafał Dowgird
6
def reverse_string (string): retourne la chaîne [:: - 1]
pi.
4
@pi Je pense que si l'on en sait assez pour définir reverse_string comme vous l'avez, alors on peut laisser le [:: - 1] dans votre code et être à l'aise avec sa signification et le sentiment qu'il est approprié.
physicsmichael
289

Décorateurs

Décorateurs permettent d'encapsuler une fonction ou une méthode dans une autre fonction qui peut ajouter des fonctionnalités, modifier des arguments ou des résultats, etc. Vous écrivez des décorateurs une ligne au-dessus de la définition de la fonction, en commençant par un signe "at" (@).

L'exemple montre un print_argsdécorateur qui affiche les arguments de la fonction décorée avant de l'appeler:

>>> def print_args(function):
>>>     def wrapper(*args, **kwargs):
>>>         print 'Arguments:', args, kwargs
>>>         return function(*args, **kwargs)
>>>     return wrapper

>>> @print_args
>>> def write(text):
>>>     print text

>>> write('foo')
Arguments: ('foo',) {}
foo
DzinX
la source
54
Lors de la définition des décorateurs, je recommanderais de décorer le décorateur avec @decorator. Il crée un décorateur qui préserve une signature de fonctions lors de l'introspection. Plus d'informations ici: phyast.pitt.edu/~micheles/python/documentation.html
sirwart
45
Comment est-ce une fonctionnalité cachée?
Vetle
50
Eh bien, il n'est pas présent dans la plupart des tutoriels Python simples, et je suis tombé dessus longtemps après avoir commencé à utiliser Python. C'est ce que j'appellerais une fonctionnalité cachée, à peu près la même chose que les autres meilleurs postes ici.
DzinX
16
vetler, la question demande "des fonctionnalités moins connues mais utiles du langage de programmation Python". Comment mesurez-vous les «fonctionnalités moins connues mais utiles»? Je veux dire, comment sont ces fonctionnalités cachées de réponses?
Johnd
4
@vetler La plupart des choses ici ne sont guère "cachées".
Humphrey Bogart
288

La syntaxe for ... else (voir http://docs.python.org/ref/for.html )

for i in foo:
    if i == 0:
        break
else:
    print("i was never 0")

Le bloc "else" sera normalement exécuté à la fin de la boucle for, sauf si le break est appelé.

Le code ci-dessus pourrait être émulé comme suit:

found = False
for i in foo:
    if i == 0:
        found = True
        break
if not found: 
    print("i was never 0")
rlerallut
la source
218
Je pense que la syntaxe for / else est maladroite. Il "semble" que la clause else doit être exécutée si le corps de la boucle n'est jamais exécuté.
codeape
14
ah. Jamais vu celui-là! Mais je dois dire que c'est un peu inapproprié. Qui s'attendrait à ce que le bloc else ne s'exécute que si break ne le fait jamais? Je suis d'accord avec codeape: il semble que le reste soit entré pour les foos vides.
Daren Thomas
52
semble que le mot-clé devrait être enfin, pas autre chose
Jiaaro
21
Sauf que finalement est déjà utilisé d'une manière où cette suite est toujours exécutée.
7
Ne devrait certainement pas être «autrement». Peut-être «alors» ou quelque chose, puis «autre» pour quand la boucle n'a jamais été exécutée.
Tor Valamo
258

À partir de la version 2.5, les pronostics ont une méthode spéciale __missing__qui est appelée pour les éléments manquants:

>>> class MyDict(dict):
...  def __missing__(self, key):
...   self[key] = rv = []
...   return rv
... 
>>> m = MyDict()
>>> m["foo"].append(1)
>>> m["foo"].append(2)
>>> dict(m)
{'foo': [1, 2]}

Il existe également une sous-classe dict dans collectionscalled defaultdictqui fait à peu près la même chose mais appelle une fonction sans arguments pour les éléments non existants:

>>> from collections import defaultdict
>>> m = defaultdict(list)
>>> m["foo"].append(1)
>>> m["foo"].append(2)
>>> dict(m)
{'foo': [1, 2]}

Je recommande de convertir de tels dict en dicts réguliers avant de les passer à des fonctions qui n'attendent pas de telles sous-classes. Beaucoup de code utilise d[a_key]et attrape KeyErrors pour vérifier si un élément existe qui ajouterait un nouvel élément au dict.

Armin Ronacher
la source
10
Je préfère utiliser setdefault. m = {}; m.setdefault ('foo', 1)
grayger
22
@grayger voulait dire cela m={}; m.setdefault('foo', []).append(1).
Cristian Ciupitu
1
Il y a cependant des cas où passer le defaultdict est très pratique. La fonction peut par exemple parcourir la valeur et elle fonctionne pour les clés non définies sans code supplémentaire, car la valeur par défaut est une liste vide.
Marian
3
defaultdict est meilleur dans certaines circonstances que setdefault, car il ne crée pas l'objet par défaut sauf si la clé est manquante. setdefault le crée, qu'il soit manquant ou non. Si votre objet par défaut coûte cher à créer, cela peut être un problème de performances - j'ai obtenu une accélération décente d'un programme simplement en changeant tous les appels setdefault.
Whatang
2
defaultdictest également plus puissant que la setdefaultméthode dans d'autres cas. Par exemple, pour une contre - dd = collections.defaultdict(int) ... dd[k] += 1contre d.setdefault(k, 0) += 1.
Mike Graham
247

Échange de valeur sur place

>>> a = 10
>>> b = 5
>>> a, b
(10, 5)

>>> a, b = b, a
>>> a, b
(5, 10)

Le côté droit de l'affectation est une expression qui crée un nouveau tuple. Le côté gauche de l'affectation décompresse immédiatement ce tuple (non référencé) aux noms aetb .

Après l'affectation, le nouveau tuple n'est pas référencé et marqué pour la récupération de place, et les valeurs liées à aetb ont été échangées.

Comme indiqué dans la section du didacticiel Python sur les structures de données ,

Notez que l'affectation multiple n'est en réalité qu'une combinaison de tuple et de décompression de séquence.

Lucas S.
la source
1
Est-ce que cela utilise plus de mémoire réelle que la manière traditionnelle? Je suppose que oui puisque vous créez un tuple, au lieu d'une seule variable de swap
Nathan
75
Il n'utilise pas plus de mémoire. Il utilise moins .. Je l'ai simplement écrit dans les deux sens, et décompilé le bytecode .. le compilateur optimise, comme vous l'espérez. les résultats de dis ont montré qu'il configurait les vars, puis ROT_TWOing. ROT_TWO signifie 'échanger les deux vars de pile les plus hauts' ... Assez chic, en fait.
royal
5
Vous signalez également par inadvertance une autre fonctionnalité intéressante de Python, à savoir que vous pouvez implicitement créer un tuple d'éléments simplement en les séparant par des virgules.
asmeurer
3
Dana the Sane: l'affectation en Python est une instruction, pas une expression, de sorte que l'expression serait invalide si = avait une priorité plus élevée (c'est-à-dire qu'elle était interprétée comme a, (b = b), a).
hbn
5
C'est la fonctionnalité la moins cachée que j'ai lue ici. Sympa, mais explicitement décrit dans chaque tutoriel Python.
Thiago Chaves
235

Expressions régulières lisibles

En Python, vous pouvez fractionner une expression régulière sur plusieurs lignes, nommer vos correspondances et insérer des commentaires.

Exemple de syntaxe verbeuse (de Dive into Python ):

>>> pattern = """
... ^                   # beginning of string
... M{0,4}              # thousands - 0 to 4 M's
... (CM|CD|D?C{0,3})    # hundreds - 900 (CM), 400 (CD), 0-300 (0 to 3 C's),
...                     #            or 500-800 (D, followed by 0 to 3 C's)
... (XC|XL|L?X{0,3})    # tens - 90 (XC), 40 (XL), 0-30 (0 to 3 X's),
...                     #        or 50-80 (L, followed by 0 to 3 X's)
... (IX|IV|V?I{0,3})    # ones - 9 (IX), 4 (IV), 0-3 (0 to 3 I's),
...                     #        or 5-8 (V, followed by 0 to 3 I's)
... $                   # end of string
... """
>>> re.search(pattern, 'M', re.VERBOSE)

Exemples de correspondances de nommage (tirées du HOWTO sur les expressions rationnelles )

>>> p = re.compile(r'(?P<word>\b\w+\b)')
>>> m = p.search( '(((( Lots of punctuation )))' )
>>> m.group('word')
'Lots'

Vous pouvez également écrire verbalement une expression régulière sans utiliser re.VERBOSEgrâce à la concaténation littérale de chaîne.

>>> pattern = (
...     "^"                 # beginning of string
...     "M{0,4}"            # thousands - 0 to 4 M's
...     "(CM|CD|D?C{0,3})"  # hundreds - 900 (CM), 400 (CD), 0-300 (0 to 3 C's),
...                         #            or 500-800 (D, followed by 0 to 3 C's)
...     "(XC|XL|L?X{0,3})"  # tens - 90 (XC), 40 (XL), 0-30 (0 to 3 X's),
...                         #        or 50-80 (L, followed by 0 to 3 X's)
...     "(IX|IV|V?I{0,3})"  # ones - 9 (IX), 4 (IV), 0-3 (0 to 3 I's),
...                         #        or 5-8 (V, followed by 0 to 3 I's)
...     "$"                 # end of string
... )
>>> print pattern
"^M{0,4}(CM|CD|D?C{0,3})(XC|XL|L?X{0,3})(IX|IV|V?I{0,3})$"
1804
la source
7
Je ne sais pas si je considérerais vraiment qu'une fonctionnalité Python, la plupart des moteurs RE ont une option détaillée.
Jeremy Banks
18
Oui, mais parce que vous ne pouvez pas le faire dans grep ou dans la plupart des éditeurs, beaucoup de gens ne savent pas que c'est là. Le fait que d'autres langages aient une fonctionnalité équivalente n'en fait pas une fonctionnalité utile et peu connue de python
Mark Baker
7
Dans un grand projet avec beaucoup d'expressions régulières optimisées (lire: optimisé pour la machine mais pas pour les êtres humains), j'ai mordu la puce et les ai toutes converties en syntaxe verbeuse. Maintenant, introduire de nouveaux développeurs dans des projets est beaucoup plus facile. À partir de maintenant, nous appliquons des RE verbeux sur chaque projet.
Berk D. Demir
Je préfère juste dire: centaines = "(CM | CD | D? C {0,3})" # 900 (CM), 400 (CD), etc. Le langage a déjà un moyen de donner des noms aux choses, un façon d'ajouter des commentaires et une façon de combiner des chaînes. Pourquoi utiliser la syntaxe de bibliothèque spéciale ici pour des choses que le langage fait déjà parfaitement bien? Il semble aller directement contre Epigram 9. de Perlis
Ken
3
@Ken: une expression régulière peut ne pas toujours être directement dans la source, elle peut être lue à partir des paramètres ou d'un fichier de configuration. Permettre des commentaires ou simplement des espaces supplémentaires (pour plus de lisibilité) peut être d'une grande aide.
222

Déballage des arguments de fonction

Vous pouvez décompresser une liste ou un dictionnaire comme arguments de fonction en utilisant *et **.

Par exemple:

def draw_point(x, y):
    # do some magic

point_foo = (3, 4)
point_bar = {'y': 3, 'x': 2}

draw_point(*point_foo)
draw_point(**point_bar)

Raccourci très utile car les listes, tuples et dict sont largement utilisés comme conteneurs.

e-satis
la source
27
* est également connu sous le nom d'opérateur de splat
Gabriel
3
J'aime cette fonctionnalité, mais pas vraiment.
Stephen Paulger
5
Les conseils de Pylint ne sont pas une loi. L'autre façon, apply (callable, arg_seq, arg_map), est obsolète depuis 2.3.
Yann Vernier
1
Le conseil de pylint n'est peut-être pas la loi, mais c'est un putain de bon conseil. Déboguer du code qui se livre à des trucs comme celui-ci est un enfer pur. Comme le note l'affiche originale, il s'agit d'un raccourci utile .
Andrew
2
J'ai vu cela être utilisé dans le code une fois et je me suis demandé ce que cela faisait. Malheureusement, il est difficile de google pour "Python **"
Fraser Graham
205

ROT13 est un codage valide pour le code source, lorsque vous utilisez la bonne déclaration de codage en haut du fichier de code:

#!/usr/bin/env python
# -*- coding: rot13 -*-

cevag "Uryyb fgnpxbiresybj!".rapbqr("rot13")
André
la source
10
Génial! Remarquez comment les chaînes d'octets sont prises au cevag h"Uryyb fgnpxbiresybj!"
pied de la
12
malheureusement, il est supprimé de py3k
mykhal
9
C'est bon pour contourner l'antivirus.
L̲̳o̲̳̳n̲̳̳g̲̳̳p̲̳o̲̳̳k̲̳̳e̲̳̳
96
Cela n'a rien à voir avec l'encodage, c'est juste Python écrit en gallois. :-P
Olivier Verdier
33
Ph'nglui mglw'nafh Cthulhu R'lyeh wgah'nagl fhtagn!
Manuel Ferreria
183

Créer de nouveaux types de manière entièrement dynamique

>>> NewType = type("NewType", (object,), {"x": "hello"})
>>> n = NewType()
>>> n.x
"hello"

qui est exactement le même que

>>> class NewType(object):
>>>     x = "hello"
>>> n = NewType()
>>> n.x
"hello"

Probablement pas la chose la plus utile, mais agréable à savoir.

Edit : le nom fixe du nouveau type devrait être NewTypeexactement la même chose qu'avec l' classinstruction.

Modifier : Ajustement du titre pour décrire plus précisément la fonction.

Torsten Marek
la source
8
Cela a beaucoup de potentiel pour l'utilité, par exemple, les ORM JIT
Mark Cidade
8
Je l'utilise pour générer des classes HTML-Form basées sur une entrée dynamique. Très agréable!
pi.
15
Remarque: toutes les classes sont créées au moment de l'exécution. Vous pouvez donc utiliser l'instruction 'class' dans un conditionnel ou dans une fonction (très utile pour créer des familles de classes ou des classes qui agissent comme des fermetures). L'amélioration apportée par le «type» est la possibilité de définir de manière ordonnée un ensemble d'attributs (ou bases) généré dynamiquement.
spookylukey
1
Vous pouvez également créer des types anonymes avec une chaîne vide comme: type ('', (object,), {'x': 'blah'})
bluehavana
3
Pourrait être très utile pour les injections de code.
Avihu Turzion
179

Gestionnaires de contexte et withdéclaration " "

Introduit dans PEP 343 , un gestionnaire de contexte est un objet qui agit comme un contexte d'exécution pour une suite d'instructions.

Comme la fonctionnalité utilise de nouveaux mots clés, elle est introduite progressivement: elle est disponible en Python 2.5 via la __future__directive. Python 2.6 et supérieur (y compris Python 3) le propose par défaut.

J'ai beaucoup utilisé la déclaration "with" car je pense que c'est une construction très utile, voici une démo rapide:

from __future__ import with_statement

with open('foo.txt', 'w') as f:
    f.write('hello!')

Ce qui se passe ici dans les coulisses, c'est que l' instruction "with" appelle les méthodes spéciales __enter__et __exit__sur l'objet fichier. Les détails des exceptions sont également transmis à __exit__si une exception a été déclenchée à partir du corps de l'instruction with, permettant ainsi la gestion des exceptions.

Dans ce cas particulier, cela a pour effet de garantir la fermeture du fichier lorsque l'exécution sort du cadre de la withsuite, que cela se produise normalement ou qu'une exception soit levée. Il s'agit essentiellement d'un moyen d'abstraire le code de gestion des exceptions commun.

D'autres cas d'utilisation courants pour cela incluent le verrouillage avec des threads et des transactions de base de données.

Ycros
la source
3
Je n'approuverais pas une révision de code qui aurait importé quoi que ce soit du futur . Les fonctionnalités sont plus mignonnes qu'utiles, et elles finissent généralement par confondre les nouveaux arrivants Python.
un nerd payé
6
Oui, il est préférable de laisser à ceux qui savent ce qu'ils font les fonctionnalités "mignonnes" telles que les étendues imbriquées et les générateurs. Et toute personne qui souhaite être compatible avec les futures versions de Python. Pour les étendues et les générateurs imbriqués, les "futures versions" de Python signifient respectivement 2.2 et 2.5. Pour l'instruction with, "futures versions" de Python signifie 2.6.
Chris B.
10
Cela peut aller de soi, mais avec python v2.6 +, vous n'avez plus besoin d'importer du futur . avec est désormais un mot-clé de première classe.
fitzgeraldsteele
25
En 2.7, vous pouvez en avoir plusieurs withs:) with open('filea') as filea and open('fileb') as fileb: ...
Austin Richardson
5
@Austin je n'ai pas pu faire fonctionner cette syntaxe sur 2.7. cela a cependant fonctionné: with open('filea') as filea, open('fileb') as fileb: ...
wim
168

Les dictionnaires ont une méthode get ()

Les dictionnaires ont une méthode 'get ()'. Si vous faites d ['clé'] et que la clé n'est pas là, vous obtenez une exception. Si vous faites d.get ('clé'), vous récupérez Aucun si 'clé' n'est pas là. Vous pouvez ajouter un deuxième argument pour récupérer cet élément au lieu de Aucun, par exemple: d.get ('key', 0).

C'est génial pour des choses comme l'addition de nombres:

sum[value] = sum.get(value, 0) + 1

Rory
la source
39
vérifiez également la méthode setdefault.
Daren Thomas
27
consultez également la classe collections.defaultdict.
jfs
8
Si vous utilisez Python 2.7 ou version ultérieure, ou 3.1 ou version ultérieure, consultez la classe Counter dans le module de collections. docs.python.org/library/collections.html#collections.Counter
Elias Zamaria
Oh mec, tout ce temps que je fais get(key, None). Je n'avais aucune idée qui Noneétait fournie par défaut.
Jordan Reiter
152

Descripteurs

Ils sont la magie derrière tout un tas de fonctionnalités principales de Python.

Lorsque vous utilisez l'accès en pointillé pour rechercher un membre (par exemple, xy), Python recherche d'abord le membre dans le dictionnaire d'instances. S'il n'est pas trouvé, il le recherche dans le dictionnaire de classe. S'il le trouve dans le dictionnaire de classe et que l'objet implémente le protocole de descripteur, au lieu de simplement le renvoyer, Python l'exécute. Un descripteur est une classe qui implémente les __get__, __set__ou les __delete__méthodes.

Voici comment implémenter votre propre version (en lecture seule) de la propriété à l'aide de descripteurs:

class Property(object):
    def __init__(self, fget):
        self.fget = fget

    def __get__(self, obj, type):
        if obj is None:
            return self
        return self.fget(obj)

et vous l'utiliseriez comme la propriété intégrée ():

class MyClass(object):
    @Property
    def foo(self):
        return "Foo!"

Les descripteurs sont utilisés en Python pour implémenter des propriétés, des méthodes liées, des méthodes statiques, des méthodes de classe et des emplacements, entre autres. Les comprendre permet de comprendre facilement pourquoi beaucoup de choses qui ressemblaient auparavant à des «bizarreries» de Python sont ce qu'elles sont.

Raymond Hettinger a un excellent tutoriel qui fait un bien meilleur travail de description que moi.

Nick Johnson
la source
C'est un doublon de décorateurs, n'est-ce pas!? ( stackoverflow.com/questions/101268/… )
gecco
2
non, les décorateurs et les descripteurs sont des choses totalement différentes, bien que dans l'exemple de code, je crée un décorateur de descripteurs. :)
Nick Johnson
1
L'autre façon de le faire est avec un lambda:foo = property(lambda self: self.__foo)
Pete Peterson
1
@PetePeterson Oui, mais propertylui-même est implémenté avec des descripteurs, ce qui était le point de mon message.
Nick Johnson
142

Affectation conditionnelle

x = 3 if (y == 1) else 2

Il fait exactement ce que cela ressemble: "attribuez 3 à x si y est 1, sinon affectez 2 à x". Notez que les parens ne sont pas nécessaires, mais je les aime pour la lisibilité. Vous pouvez également l'enchaîner si vous avez quelque chose de plus compliqué:

x = 3 if (y == 1) else 2 if (y == -1) else 1

Mais à un certain point, ça va un peu trop loin.

Notez que vous pouvez utiliser if ... else dans n'importe quelle expression. Par exemple:

(func1 if y == 1 else func2)(arg1, arg2) 

Ici func1 sera appelé si y est 1 et func2, sinon. Dans les deux cas, la fonction correspondante sera appelée avec les arguments arg1 et arg2.

De façon analogue, ce qui suit est également valable:

x = (class1 if y == 1 else class2)(arg1, arg2)

où class1 et class2 sont deux classes.

tghw
la source
29
L'affectation n'est pas la partie spéciale. Vous pourriez tout aussi facilement faire quelque chose comme: retourner 3 si (y == 1) sinon 2.
Brian
25
Cette façon alternative est la première fois que je vois du Python obscurci.
Craig McQueen
3
Kylebrooks: Ce n'est pas le cas dans ce cas, les opérateurs booléens court-circuitent. Il n'évaluera 2 que si bool (3) == False.
RoadieRich
15
ce codage à l'envers me déroute. quelque chose comme ça a x = ((y == 1) ? 3 : 2)plus de sens pour moi
mpen
13
Je ressens juste le contraire de @Mark, les opérateurs ternaires de style C m'ont toujours confondu, est-ce le côté droit ou le milieu qui est évalué sur une fausse condition? Je préfère de loin la syntaxe ternaire de Python.
Jeffrey Harris
141

Doctest : documentation et tests unitaires à la fois.

Exemple extrait de la documentation Python:

def factorial(n):
    """Return the factorial of n, an exact integer >= 0.

    If the result is small enough to fit in an int, return an int.
    Else return a long.

    >>> [factorial(n) for n in range(6)]
    [1, 1, 2, 6, 24, 120]
    >>> factorial(-1)
    Traceback (most recent call last):
        ...
    ValueError: n must be >= 0

    Factorials of floats are OK, but the float must be an exact integer:
    """

    import math
    if not n >= 0:
        raise ValueError("n must be >= 0")
    if math.floor(n) != n:
        raise ValueError("n must be exact integer")
    if n+1 == n:  # catch a value like 1e300
        raise OverflowError("n too large")
    result = 1
    factor = 2
    while factor <= n:
        result *= factor
        factor += 1
    return result

def _test():
    import doctest
    doctest.testmod()    

if __name__ == "__main__":
    _test()
Pierre-Jean Coudert
la source
6
Les doctests sont certainement cool, mais je n'aime vraiment pas tous les croupes que vous devez taper pour tester que quelque chose devrait déclencher une exception
TM.
60
Les doctests sont surfaits et polluent la documentation. À quelle fréquence testez-vous une fonction autonome sans setUp ()?
un nerd payé
2
qui dit que vous ne pouvez pas avoir de configuration dans un doctorat? écrire une fonction qui génère le contexte et retourne locals()ensuite dans votre doctorat do locals().update(setUp())= D
Jiaaro
12
Si une fonction autonome nécessite une configuration, il y a de fortes chances qu'elle soit découplée de certaines choses sans rapport ou placée dans une classe. L'espace de noms de classe doctest peut ensuite être réutilisé dans les méthodes de classe doctests, c'est donc un peu comme setUp, seulement DRY et lisible.
Andy Mikhaylenko
4
"À quelle fréquence testez-vous une fonction autonome" - beaucoup. Je trouve que les doctorats émergent souvent naturellement du processus de conception lorsque je décide des façades.
Gregg Lind
138

Formatage nommé

Le formatage% prend un dictionnaire (applique également la validation% i /% s etc.).

>>> print "The %(foo)s is %(bar)i." % {'foo': 'answer', 'bar':42}
The answer is 42.

>>> foo, bar = 'question', 123

>>> print "The %(foo)s is %(bar)i." % locals()
The question is 123.

Et comme locals () est également un dictionnaire, vous pouvez simplement le passer comme dict et avoir des sous-positions% à partir de vos variables locales. Je pense que cela est mal vu, mais simplifie les choses ..

Mise en forme d'un nouveau style

>>> print("The {foo} is {bar}".format(foo='answer', bar=42))
Pasi Savolainen
la source
60
Sera progressivement supprimé et éventuellement remplacé par la méthode format () de la chaîne.
Constantin
3
La mise en forme nommée est très utile pour les traducteurs car ils ont tendance à ne voir que la chaîne de format sans les noms de variable pour le contexte
pixelbeat
2
Semble fonctionner en python 3.0.1 (nécessaire pour ajouter des parenthèses autour de l'appel d'impression).
Pasi Savolainen
9
un hachage , hein? Je vois d'où tu viens.
shylent
11
Le formatage de% s ne sera pas supprimé. str.format () est certainement plus pythonique, mais est en fait 10x plus lent pour un remplacement de chaîne simple. Ma conviction est que le formatage% s est toujours la meilleure pratique.
Kenneth Reitz
132

Pour ajouter plus de modules python (en particulier ceux de tiers), la plupart des gens semblent utiliser des variables d'environnement PYTHONPATH ou ils ajoutent des liens symboliques ou des répertoires dans leurs répertoires de packages de sites. Une autre façon consiste à utiliser des fichiers * .pth. Voici l'explication officielle du doc ​​python:

"Le moyen le plus pratique [pour modifier le chemin de recherche de python] consiste à ajouter un fichier de configuration de chemin à un répertoire qui se trouve déjà sur le chemin de Python, généralement dans le répertoire ... / site-packages /. Les fichiers de configuration de chemin ont une extension de .pth et chaque ligne doit contenir un seul chemin d'accès qui sera ajouté à sys.path. (Étant donné que les nouveaux chemins d'accès sont ajoutés à sys.path, les modules des répertoires ajoutés ne remplaceront pas les modules standard. Cela signifie que vous ne pouvez pas utiliser ce mécanisme pour l'installation de versions fixes de modules standard.) "

dégueulasse
la source
1
Je n'ai jamais fait la connexion entre ce fichier .pth dans le répertoire site-packages de setuptools et cette idée. impressionnant.
dave paola
122

Clause d' exception sinon :

try:
  put_4000000000_volts_through_it(parrot)
except Voom:
  print "'E's pining!"
else:
  print "This parrot is no more!"
finally:
  end_sketch()

L'utilisation de la clause else est préférable à l'ajout de code supplémentaire à la clause try, car elle évite d'attraper accidentellement une exception qui n'a pas été déclenchée par le code protégé par l'instruction try ... except.

Voir http://docs.python.org/tut/node10.html

Constantin
la source
8
+1 c'est génial. Si le bloc try s'exécute sans entrer de bloc d'exception, alors le bloc else est entré. Et puis bien sûr, le bloc enfin est exécuté
inspectorG4dget
Je comprends enfin pourquoi le «reste» est là! Merci.
taynaron
Il serait plus judicieux d'utiliser continuer, mais je suppose que c'est déjà pris;)
Paweł Prażak
Notez que sur les anciennes versions de Python2, vous ne pouvez pas avoir les deux: et enfin: des clauses pour le même essai: block
Kevin Horn
1
@ Paweł Prażak, comme Kevin Horn l'a mentionné, cette syntaxe a été introduite après la sortie initiale de Python et l'ajout de nouveaux mots clés réservés au langage existant est toujours problématique. C'est pourquoi un mot-clé existant est généralement réutilisé (cf. "auto" dans la norme C ++ récente).
Constantin
114

Relancer les exceptions :

# Python 2 syntax
try:
    some_operation()
except SomeError, e:
    if is_fatal(e):
        raise
    handle_nonfatal(e)

# Python 3 syntax
try:
    some_operation()
except SomeError as e:
    if is_fatal(e):
        raise
    handle_nonfatal(e)

L'instruction 'raise' sans argument dans un gestionnaire d'erreur indique à Python de relancer l'exception avec le retraçage d'origine intact , vous permettant de dire "oh, désolé, désolé, je ne voulais pas attraper cela, désolé, désolé. "

Si vous souhaitez imprimer, stocker ou jouer avec le traçage d'origine, vous pouvez l'obtenir avec sys.exc_info (), et l'imprimer comme le ferait Python se fait avec le module 'traçage'.

Thomas Wouters
la source
Désolé mais c'est une caractéristique bien connue et courante de presque toutes les langues.
Lucas S.
6
Notez le texte en italique. Certaines personnes le feront à la raise eplace, ce qui ne conserve pas la trace d'origine.
habnabit
12
Peut-être plus magique, exc_info = sys.exc_info(); raise exc_info[0], exc_info[1], exc_info[2]est équivalent à cela, mais vous pouvez changer ces valeurs (par exemple, changer le type d'exception ou le message)
ianb
3
@ Lucas S. Eh bien, je ne le savais pas, et je suis content que ce soit écrit ici.
e-satis
je montre peut-être ma jeunesse ici, mais j'ai toujours utilisé la syntaxe python 3 en python 2.7 sans problème
wim
106

Messages principaux :)

import this
# btw look at this module's source :)

Décrypté :

Le Zen de Python, par Tim Peters

Beau, c'est mieux que laid.
Explicite vaut mieux qu'implicite.
Simple, c'est mieux que complexe.
Complexe vaut mieux que compliqué.
L'appartement est meilleur que l'emboîtement.
Clairsemé vaut mieux que dense.
La lisibilité compte.
Les cas spéciaux ne sont pas assez spéciaux pour enfreindre les règles.
Bien que la praticité l'emporte sur la pureté.
Les erreurs ne doivent jamais passer silencieusement.
Sauf silence explicite. Si la mise en œuvre est facile à expliquer, ce peut être une bonne idée. Les espaces de noms sont une excellente idée de klaxon - faisons-en plus!
Face à l'ambiguïté, refusez la tentation de deviner. Il devrait y avoir une - et de préférence une seule - manière évidente de le faire.
Bien que cette façon ne soit pas évidente au début, sauf si vous êtes néerlandais.
C'est mieux que jamais.
Bien que jamais ne soit souvent meilleur quedroit maintenant.
Si l'implémentation est difficile à expliquer, c'est une mauvaise idée.

Mark
la source
1
Une idée pourquoi la source a été chiffrée de cette façon? Était-ce juste pour le plaisir, ou y avait-il une autre raison?
MiniQuark
42
la façon dont la source est écrite va à l'encontre du zen!
hasen
2
J'ai mis à jour mon /usr/lib/python2.6/this.py en remplaçant l'ancien code par ceci print s.translate("".join(chr(64<i<91 and 65+(i-52)%26 or 96<i<123 and 97+(i-84)%26 or i) for i in range(256)))et ça a l'air beaucoup mieux maintenant !! :-D
fortran
2
@MiniQuark: leçon d'histoire rapide: wefearchange.org/2010/06/import-this-and-zen-of-python.html
105

Achèvement de l'onglet d'interpréteur interactif

try:
    import readline
except ImportError:
    print "Unable to load readline module."
else:
    import rlcompleter
    readline.parse_and_bind("tab: complete")


>>> class myclass:
...    def function(self):
...       print "my function"
... 
>>> class_instance = myclass()
>>> class_instance.<TAB>
class_instance.__class__   class_instance.__module__
class_instance.__doc__     class_instance.function
>>> class_instance.f<TAB>unction()

Vous devrez également définir une variable d'environnement PYTHONSTARTUP.

mjard
la source
2
C'est une fonctionnalité très utile. Tant et si bien que j'ai un script simple pour l'activer (plus quelques autres améliorations d'introspection): pixelbeat.org/scripts/inpy
pixelbeat
43
IPython vous donne ce plus de tonnes d'autres trucs
sympas
Cela aurait été plus utile à l'invite pdb que l'invite python standard (comme IPython sert de toute façon à cette fin). Cependant, cela ne semble pas fonctionner à l'invite pdb, probablement parce que pdb lie son propre onglet (ce qui est moins utile). J'ai essayé d'appeler parse_and_bind () à l'invite pdb, mais cela n'a toujours pas fonctionné. L'alternative d'obtenir une invite pdb avec IPython est plus de travail, donc j'ai tendance à ne pas l'utiliser.
haridsv
2
@haridsv - easy_install ipdb- alors vous pouvez utiliserimport ipdb; ipdb.set_trace()
Doug Harris
1
Sur osx [et j'imagine d'autres systèmes qui utilisent libedit], vous devez le fairereadline.parse_and_bind ("bind ^I rl_complete")
Foo Bah
91

Compréhensions de listes imbriquées et expressions de générateur:

[(i,j) for i in range(3) for j in range(i) ]    
((i,j) for i in range(4) for j in range(i) )

Ceux-ci peuvent remplacer d'énormes morceaux de code en boucle imbriquée.

Rafał Dowgird
la source
"for j in range (i)" - est-ce une faute de frappe? Normalement, vous voudriez des plages fixes pour i et j. Si vous accédez à un tableau 2D, vous ratez la moitié de vos éléments.
Peter Gibson
Je n'accède à aucun tableau dans cet exemple. Le seul but de ce code est de montrer que les expressions des plages internes peuvent accéder à celles des externes. Le sous-produit est une liste de paires (x, y) telles que 4> x> y> 0.
Rafał Dowgird
2
comme une double intégration dans le calcul ou une double sommation.
Yoo
22
Le point clé à retenir ici (ce qui m'a pris beaucoup de temps à réaliser) est que l'ordre des fordéclarations doit être écrit dans l'ordre que vous attendez qu'elles soient écrites dans une boucle standard, de l'extérieur vers l'intérieur.
sykora
2
Pour ajouter au commentaire de sykora: imaginez que vous commencez avec une pile de fors et de ifs avec yield xinside. Pour convertir cela en une expression de générateur, déplacez-vous d' xabord, supprimez tous les deux-points (et le yield) et entourez le tout entre parenthèses. Pour faire une liste de compréhension à la place, remplacez les parens extérieurs par des crochets.
Ken Arnold
91

Surcharge d'opérateur pour le setbuiltin:

>>> a = set([1,2,3,4])
>>> b = set([3,4,5,6])
>>> a | b # Union
{1, 2, 3, 4, 5, 6}
>>> a & b # Intersection
{3, 4}
>>> a < b # Subset
False
>>> a - b # Difference
{1, 2}
>>> a ^ b # Symmetric Difference
{1, 2, 5, 6}

Plus de détails dans la référence de bibliothèque standard: Définir les types

Kiv
la source
Dans le tutoriel, en partie docs.python.org/tutorial/datastructures.html#sets
XTL