No puede seleccionar más de 25 temas Los temas deben comenzar con una letra o número, pueden incluir guiones ('-') y pueden tener hasta 35 caracteres de largo.
Este repositorio está archivado. Puede ver los archivos y clonarlo, pero no puede subir cambios o reportar incidencias ni pedir Pull Requests.
 
 
 
 
 
 

7.1 KiB

% Programmation avec Python (chapitre 13) % Dimitri Merejkowsky

\center \huge Rappels

Virtualenvs

  • Création:
$ python3 -m venv /path/to/foo
  • Utilisation:
$ /path/to/foo/bin/pip install requests
# ou
$ source /path/to/foo/bin/activate
(foo) $ pip install requests

Tests

# foo.py
def add_3(x):
	return x + 3

# test_foo.py
import foo

def test_add_3():
	result = foo.add_3(2)
	assert result == 5

pytest

$ pytest test_foo.py
============== test session starts =================
test_foo.py .                                 [100%]
=========== 1 passed in 0.01 seconds ===============

TDD

  • Une discipline
  • 4 règles
  • Un cycle

Règles

  • Règle 1 : Il est interdit d'écrire du code de production, sauf si c’est pour faire passer un test qui a échoué.
  • Règle 2 : Il est interdit d'écrire plus de code que celui qui est nécessaire pour provoquer une erreur dans les tests (n’importe quelle erreur)
  • Règle 3 : Il est interdit d'écrire plus de code que celui qui est nécessaire pour faire passer un test qui a échoué
  • Règle 4 : Une fois que tous les tests passent, il est interdit de modifier le code sans s’arrêter pour considérer la possibilité d’un refactoring.

Cycle

  • RED: on écrit un test qui échoue
  • GREEN: on fait passer le test
  • REFACTOR: on refactor le code de production et le code de test

\center \huge Générateurs

\vfill

\normalsize Ces objects qui sont “presque” des listes ...

Retour sur les boucles

Itération sur une liste

for i in [1, 2, 3]:
    print(i)

Clés d’un dictionnaire

for key in {"a": 1, "b": 2, "c": 3}.keys():
	print(key)

Valeurs d’un dictionnaire

for value in {"a": 1, "b": 2, "c": 3}.values():
	print(key)

Pas des listes

>>> my_dict = {"a": 1, "b": 2, "c": 3}
>>> my_dict.keys()
dict_keys(['a', 'b', 'c'])
>>> my_dict.keys()[0]
TypeError: 'dict_keys' object is not subscriptable

Vues

En fait, .keys(), .values(), .items() renvoient des vues.

Elles changent quand le dictionnaire changent, mais on ne peut pas s’en servir pour changer la taille du dictionnaire.

>>> my_dict = {"a": 1, "b": 2, "c": 3}
>>> for k in my_dict.keys():
>>> 	if k == "a":
>>> 		del my_dict[k]
RuntimeError: dictionary changed size during iteration

Vues (2)

Si vraiment vous en avez besoin, vous pouver les convertir en liste avec list().

>>> my_dict = {"a": 1, "b": 2, "c": 3}
>>> for k in list(my_dict.keys()):
>>> 	if k == "a":
>>> 		del my_dict[k]
>>> my_dict
{"b": 2, "c": 3}

Listes par compréhension

# 1
sequence = ["a", "b", "c"]
new_sequence = []
for element in sequence:
	  new_sequence.append(element.upper())


# 2
sequence = ["a", "b", "c"]
new_sequence = [element.upper() for element in sequence]

Listes par compréhension (2)

On peut transformer la séquence originale:

>>> sequence = [element.upper() for element in sequence]
>>> sequence
["A", "B", "C"]

\vfill

On peut changer le type:

>>> as_strings = ["1", "2", "3"]
>>> numbers = [int(x) for x in as_strings]
>>> numbers
[1, 2, 3]

Filtrer une liste

>>> numbers = [1, 2, 3, 4]
>>> odds = [x for x in numbers if x % 2 == 1]
>>> odds
[1, 3]

\vfill

>>> numbers = [1, 2, 3, 4]
>>> odds_squared = [x*x for x in numbers if x % 2 == 1]
>>> odds_squared
[1, 9]

Dictionnaires et ensembles par compréhension

Même principe:

>>> scores = [("bob", 2), ("alice", 3)]
>>> my_dict = {t[0]:t[1] for t in scores}
>>> my_dict
{"bob": 2, "alice": 3}

\vfill

>>> numbers = [-1, 2, -3, 3]
>>> numbers = {abs(x) for x in numbers}
>>> numbers
{1, 2, 3}

Avantage des compréhensions

  • Code plus succint
  • Code plus rapide :)
  • Code “idiomatique”

Apparté: Python est gourmand

Python évalue d’habitude de manière “gourmande”:

def foo():
	..


def bar():
	...


x = foo(bar(y))

On calcule d’abord y, puis bar(y) puis foo(bar(y))

Générateurs

Concept: fournir les valeurs une par une, à la demande.

Le code à l’intérieur des compréhensions est un générateur.

On peut le voir si on utilise des parenthèses:

>>> generator =  (x for x in range(0, 3))
>>> generator
<generator object <genexpr> at 0x7f654c272138>
>>> list(generator)
[0, 1, 2]

Les générateurs sont “feignants”: ils ne calculent leur valeurs que quand c’est demandé

Les générateurs s'épuisent

>>> generator =  (x for x in range(0, 3))
>>> list(generator)
[0, 1, 2]
>>> list(generator)
[]

next()

On peut aussi appeler next() sur un générateur, au lieu de for ... in

>>> generator =  (x for x in range(0, 2))
>>> next(generator)
0
>>> next(generator)
1
>>> next(generator)
StopIteration

Fabriquer un générateur à l’aide d’une classe

Avec une méthode __next__()

class Squares:
	def __init__(self):
		self.value = 0

	def __next__(self):
		self.value += 1
		return self.value ** 2
>>> squares = Squares()
>>> next(squares)
1
>>> next(squares)
4
>>> next(squares)
9

Les générateurs ne sont pas des itérateurs

>>> [x for x in s if x < 10]
TypeError: 'Squares' object is not iterable

Pour faire for in, il faut un itérateur, en plus d’un générateur

Fabriquer un itérateur à l’aide d’une classe

Avec __iter__ et __next__:

class SquaresLessThan:
	def __init__(self, stop_value):
		self.value = 0
		self.stop_value = stop_value

	def __iter__(self):
		return self

	def __next__(self):
		self.value += 1
		res = self.value ** 2
		if res > self.stop_value:
			raise StopIteration()
		return res

Fabriquer un itérateur à l’aide d’une classe

>>> squares = SquaresLessThan(100)
>>> [x for x in squares]
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]

On retrouve les propriétés des expressions génératrices:

>>> squares = SquaresLessThan(100)
>>> squares[2]
TypeError: 'SquaresLessThan' object is not subscriptable
>>> squares = SquaresLessThan(100)
>>> list(squares)
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
>>> list(squares)
[]

yield

On peut aussi faire des générateurs avec yield pour mettre le générateur “en pause”, et return pour terminer:

def squares(max_value):
    x = 1
    res = 1
    while res <= max_value:
        yield res
        x += 1
        res = x * x
    return

>>> s = squares(100)
>>> [x for x in s]
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]

Combiner avec une classe génératrices

class Squares:
    def __init__(self):
        self.value = 0

    def __next__(self):
        self.value += 1
        return self.value ** 2

def squares_less_than(max_value):
    squares = Squares()
    while True:
        x = next(squares)
        if x > max_value:
                return
        yield x

yield from

On peut aussi chaîner les fonctions génératrices

def integers(max_value):
	x = 0
	while x < max_value:
		yield x
		x += 1
	return


def zero_to_five():
	yield from integers(5)


>>> s = zero_to_five()
>>> [x for x in s]
[0, 1, 2, 3, 4]

\center \huge Atelier

Listons des fichiers