syntaxe - Les fonctionnalités cachées de Python




syntaxe python (20)

Chaînes de comparaison d'opérateurs:

>>> 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 ça fait 1 < x , qui est True , puis True < 10 , qui est aussi True , alors non, ce n'est vraiment pas ce qui se passe (voir le dernier exemple). 1 < x and x < 10 , et x < 10 and 10 < x * 10 and x*10 < 100 , mais avec moins de typage et chaque terme n'est évalué qu'une seule fois.


Créer de nouveaux types de manière totalement 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.

Editer : Le nom fixe du nouveau type, devrait être NewType être exactement la même chose qu'avec l'instruction de class .

Modifier : Ajusté le titre pour décrire plus précisément la fonction.


Les dictionnaires ont une méthode get ()

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

C'est génial pour des choses comme additionner des nombres:

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


Doctest : documentation and unit-testing at the same time.

Example extracted from the Python documentation:

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()

A partir de 2.5, les dicts ont une méthode spéciale __missing__ qui est invoqué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 les collections appelée defaultdict qui 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 dicts 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 s'il existe un élément qui ajouterait un nouvel élément à la dict.


L'argument de l'étape 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 particulier x[::-1] est un idiome utile pour 'x inversé'.

>>> a[::-1]
[5,4,3,2,1]

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")

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

Échange de valeur en place

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

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

Le membre de droite 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 a et b .

Après l'affectation, le nouveau tuple est non référencé et marqué pour la récupération de place, et les valeurs liées à a et b ont été permutées.

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

Notez que l'assignation multiple est vraiment juste une combinaison de tuple packing et de déballage de séquence.



Conditional Assignment

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

It does exactly what it sounds like: "assign 3 to x if y is 1, otherwise assign 2 to x". Note that the parens are not necessary, but I like them for readability. You can also chain it if you have something more complicated:

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

Though at a certain point, it goes a little too far.

Note that you can use if ... else in any expression. Par exemple:

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

Here func1 will be called if y is 1 and func2, otherwise. In both cases the corresponding function will be called with arguments arg1 and arg2.

Analogously, the following is also valid:

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

where class1 and class2 are two classes.


Créer des 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'assigner à 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 significative.

Vous pouvez ajouter plusieurs instructions if à la fin du générateur, répliquant en principe 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)

Expressions régulières lisibles

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

Exemple de syntaxe verbeuse (de Dive à 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 correspondance de noms (à partir de HOWTO d'expression régulière )

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

Vous pouvez également écrire une regex sans utiliser re.VERBOSE grâce à la concaténation littérale.

>>> 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})$"

Interactive Interpreter Tab Completion

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()

You will also have to set a PYTHONSTARTUP environment variable.


Main messages :)

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

De-cyphered :

The Zen of Python, by Tim Peters

Beautiful is better than ugly.
Explicit is better than implicit.
Simple est meilleur que complexe.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren't special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess. There should be one-- and preferably only one --obvious way to do it.
Although that way may not be obvious at first unless you're Dutch.
Now is better than never.
Although never is often better than right now.
If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea -- let's do more of those!


Obtenez l'arbre d'analyse de python regex pour déboguer votre regex.

Les expressions régulières sont une grande fonctionnalité de python, mais les déboguer peut être pénible, et il est trop facile de se tromper d'une regex.

Heureusement, python peut imprimer l'arborescence d'analyse regex, en passant le drapeau non expérimental, expérimental, caché 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 à [] dans [/font] .

Bien sûr, vous pouvez le combiner avec tous les drapeaux que vous voulez, comme les regexes 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)

Re-raising 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)

The 'raise' statement with no arguments inside an error handler tells Python to re-raise the exception with the original traceback intact , allowing you to say "oh, sorry, sorry, I didn't mean to catch that, sorry, sorry."

If you wish to print, store or fiddle with the original traceback, you can get it with sys.exc_info(), and printing it like Python would is done with the 'traceback' module.


Soyez prudent avec les arguments par défaut mutables

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

Au lieu de cela, vous devriez utiliser une valeur sentinelle indiquant "non donné" 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]

Exception else clause:

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

The use of the else clause is better than adding additional code to the try clause because it avoids accidentally catching an exception that wasn't raised by the code being protected by the try ... except statement.

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


Nested list comprehensions and generator expressions:

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

These can replace huge chunks of nested-loop code.







hidden-features