Sam & Max: Python, Django, Git et du cul » strings http://sametmax.com Deux développeurs en vadrouille qui se sortent les doigts du code Wed, 05 Feb 2014 14:20:37 +0000 en hourly 1 http://wordpress.org/?v=3.3.1 Quelques innovation de Python 3 backportées en Python 2.7 http://sametmax.com/quelques-innovation-de-python-3-backportees-en-python-2-7/ http://sametmax.com/quelques-innovation-de-python-3-backportees-en-python-2-7/#comments Mon, 05 Nov 2012 11:36:42 +0000 Sam http://sametmax.com/?p=2863 Comme nous l’avons vu avec les vues ou les collections, Python 2.7 vient avec pas mal de bonus issus directement de la branche 3. En voici quelques autres. Tout ceci n’est bien sûr ni nouveau ni exhaustif, mais je m’aperçois que peu de personnes le savent.

Une notation littérale pour les sets:

>>> {1, 2} == set((1, 2))
True

Une syntaxe pour les dictionnaires en intention:

>>> d = {chr(x): x for x in range(65, 91)}
>>> d
{'A': 65, 'C': 67, 'B': 66, 'E': 69, 'D': 68, 'G': 71, 'F': 70, 'I': 73, 'H': 72, 'K': 75, 'J': 74, 'M': 77, 'L': 76, 'O': 79, 'N': 78, 'Q': 81, 'P': 80, 'S': 83, 'R': 82, 'U': 85, 'T': 84, 'W': 87, 'V': 86, 'Y': 89, 'X': 88, 'Z': 90}

Imbriquer with:

Avant il fallait utiliser nested() ou imbriquer à la main

with open('fichiera') as a:
    with open('fichiera') as b:
        # faire un truc

Maintenant on peut faire:

with open('fichiera') as a, open('fichiera') as b:
    # faire un truc

Rien à voir, mais toujours sympa. timedelta a maintenant une méthode total_seconds() qui retourne la valeur de la durée en seconde. En effet, l’attribut seconds ne retourne que ce qui reste en seconde une fois qu’on a retiré les jours:

>>> from datetime import timedelta
>>> delta = timedelta(days=1, seconds=1)
>>> delta.seconds
1
>>> delta.total_seconds()
86401.0

Notez qu’il n’y a toujours ni attribut minutes, ni heures.

Le module unittest gagne une pléthore d’améliorations, et notamment:

L’utilisation de assertRaises comme context manager:

with self.assertRaises(KeyError):
    {}['foo']

Et un bon gros nombres de méthodes:

assertIsNone() / assertIsNotNone(), assertIs() / assertIsNot(), assertIsInstance() / assertNotIsInstance(), assertGreater() / assertGreaterEqual() / assertLess() / assertLessEqual(), assertRegexpMatches() / assertNotRegexpMatches(), assertRaisesRegexp(),
assertIn() / assertNotIn(), assertDictContainsSubset(), assertAlmostEqual() / assertNotAlmostEqual().

Enfin format() commence à devenir une alternative valable à % car il propose maintenant des marqueurs sans noter d’index:

>>> "{}, puis {} et finalement {}".format(*range(3))
'0, puis 1 et finalement 2'

Et il ajoute le séparateur des milliers au mini-langage de formatage, mais pour la virgule uniquement. Par exemple, si avoir un nombre de 15 caractères minimum formater en tant que float, avec deux chiffres après la virgules, et donc les milliers sont groupés à l’américaine:

>>> '{:15,.2f}'.format(54321)
'      54,321.00'

flattr this!

]]>
http://sametmax.com/quelques-innovation-de-python-3-backportees-en-python-2-7/feed/ 4
Les vues sur des collections en Python http://sametmax.com/les-vues-sur-des-collections-en-python/ http://sametmax.com/les-vues-sur-des-collections-en-python/#comments Sat, 03 Nov 2012 18:57:02 +0000 Sam http://sametmax.com/?p=2785 Python 3 introduit de nombreux changements qui ont été backportés dans Python 2.7. Parmi eux, les vues, qui sont un concept assez mal expliqué dans la documentation standard.

Dictionary views

Quand on voulait travailler sur les valeurs d’un dictionnaire en Python, on avait deux choix:

  • faire dict.values() et récupérer une liste entière. Créant une liste entière en mémoire.
  • faire dict.itervalues(), et récupérer un générateur. Mais qui ne peut être lu qu’une fois.

Les vues sont une solution intermédiaire: ce sont des objets qui prennent peu de mémoire, mais qui peuvent être lus plusieurs fois.

Exemple:

>>> scores = {'foo': 1, 'bar': 0}
>>> val = scores.viewvalues()
>>> print val
dict_values([1, 0])
>>> 1 in val
True
>>> [x * 2 for x in val]
[2, 0]

Contrairement à une liste, les vues issues d’un dictionnaire ne supportent pas le slicing ou l’assignation et il n’y a aucune garantie d’ordre des éléments. De plus, elles ne peuvent être modifiées.

Bref, une vue ne contient rien, c’est juste un objet qui, quand on accède à son contenu, va le chercher dans le dictionnaire et vous le retourne. C’est ce qu’on appelle un objet proxy: il vous donne l’illusion d’accéder directement aux données pour vous faciliter la vie, généralement en vous les présentant sous une forme différente: ici un itérable.

On peut récupérer des vues pour les valeurs, mais également pour les clés et les couples clés / valeurs. Ces deux types de vues se comportent en plus comme des sets:

>>> scores.viewitems()
dict_items([('foo', 1), ('bar', 0)])
>>> scores.viewkeys() | [3,]
set([3, 'foo', 'bar'])

Puisqu’il est rare d’avoir besoin d’une vraie liste, et comme les vues sont une très bonne alternative aux générateurs, dict.values et consorts retournent des vues en Python 3.

Maintenant vous allez me dire “Mais si les vues sont une si bonne alternative aux générateurs, pourquoi on ne remplace pas tous les générateurs par des vues ?”.

Tout simplement parce que ce n’est pas possible. Un générateur est un mécanisme standard qui permet de produire des valeurs une par une. N’importe qui peut créer un générateur, car c’est un concept portable d’un problème à un autre. On peut l’appliquer à de nombreuses choses: algorithme, flux de données, fichier, etc.

Une vue n’est qu’un proxy qui permet de voir une structure de données sous une autre forme. Il faut coder une vue par type de structure de données, car la vue va chercher les données dans cette structure quand on lui demande. Le code est donc différent à chaque fois.

Python ne permet pas de créer soi-même des vues, mais créer un proxy, c’est à dire un objet qui retourne les valeurs d’un autre objet quand on l’interroge, peut se faire à la main dans tout langage de programmation. Ainsi vous pourriez créer un proxy qui ressemble a une vue des clés d’un dico très simplement:

class keyview(object):
 
    def __init__(self, d):
        self.d = d
 
    def __iter__(self):
        return self.d.iterkeys()
 
>>> view = keyview(scores)
>>> for x in view:
...     print x
...     
foo
bar
>>> list(view)
['foo', 'bar']
>>>

L’implémentation réelle de Python (en C…) ne fait pas vraiment grand chose de plus, juste un travail d’optimisation pour être plus rapide.

memoryview

Les memory views suivent le même principe, mais appliqué à toute structure de données qui supporte le buffer protocole (un certain nombre de méthodes avec un nom et un comportement défini par ce protocole) comme celles trouvées dans le module struct ou array. La structure de données la plus connue qui suit le buffer protocole est la chaîne de caractères.

>>> s = 'Sam & Max eat the road with a Github fork'
>>> ms = memoryview(s)
>>> ms[-1]
'k'
>>> ms[:9]
<memory at 0x25ded60>
>>> ''.join(ms[:9])
'Sam & Max'

Le principal intérêt de la memory view appliquée aux strings, c’est que tout slicing retourne une nouvelle memory view. On peut donc travailler sur des parties de la chaînes sans créer une nouvelle chaîne en mémoire.

En revanche, les chaînes unicodes ne sont pas supportées. Il vous faudra jouer avec encode() et decode().

flattr this!

]]>
http://sametmax.com/les-vues-sur-des-collections-en-python/feed/ 15