C'est une nouvelle sélection de trucs et astuces sur Python et la programmation de mon canal Telegram @pythonetc.
Publications précédentes .
0_0
0_0
est une expression Python totalement valide.
Tri d'une liste avec Aucun
Le tri d'une liste avec des valeurs
None
peut être difficile:
In [1]: data = [ ...: dict(a=1), ...: None, ...: dict(a=-3), ...: dict(a=2), ...: None, ...: ] In [2]: sorted(data, key=lambda x: x['a']) ... TypeError: 'NoneType' object is not subscriptable
Vous pouvez essayer de supprimer les Nones et de les remettre après le tri (à la fin ou au début de la liste selon votre tâche):
In [3]: sorted( ...: (d for d in data if d is not None), ...: key=lambda x: x['a'] ...: ) + [ ...: d for d in data if d is None ...: ] Out[3]: [{'a': -3}, {'a': 1}, {'a': 2}, None, None]
C'est une bouchée. La meilleure solution consiste à utiliser une
key
plus complexe:
In [4]: sorted(data, key=lambda x: float('inf') if x is None else x['a']) Out[4]: [{'a': -3}, {'a': 1}, {'a': 2}, None, None]
Pour les types où aucun infini n'est disponible, vous pouvez plutôt trier les tuples:
In [5]: sorted(data, key=lambda x: (1, None) if x is None else (0, x['a'])) Out[5]: [{'a': -3}, {'a': 1}, {'a': 2}, None, None]
Appel de random.seed ()
Lorsque vous bifurquez votre processus, la graine aléatoire que vous utilisez copie entre les processus. Cela peut conduire à des processus produisant le même résultat "aléatoire".
Pour éviter cela, vous devez appeler manuellement
random.seed()
dans chaque processus.
Cependant, ce n'est pas le cas si vous utilisez le module
multiprocessing
, c'est exactement ce qu'il fait pour vous.
Voici un exemple:
import multiprocessing import random import os import sys def test(a): print(random.choice(a), end=' ') a = [1, 2, 3, 4, 5] for _ in range(5): test(a) print() for _ in range(5): p = multiprocessing.Process( target=test, args=(a,) ) p.start() p.join() print() for _ in range(5): pid = os.fork() if pid == 0: test(a) sys.exit() else: os.wait() print()
Le résultat est quelque chose comme:
4 4 4 5 5 1 4 1 3 3 2 2 2 2 2
De plus, si vous utilisez Python 3.7 ou une version plus récente,
os.fork
fait de même également, grâce au nouveau hook
at_fork
.
La sortie du code ci-dessus pour Python 3.7 est:
1 2 2 1 5 4 4 4 5 5 2 4 1 3 1
Ajout à 0
Il semble que
sum([a, b, c])
soit équivalent à
a + b + c
, alors qu'en fait c'est
0 + a + b + c
. Cela signifie qu'il ne peut pas fonctionner avec des types qui ne prennent pas en charge l'ajout à
0
:
class MyInt: def __init__(self, value): self.value = value def __add__(self, other): return type(self)(self.value + other.value) def __radd__(self, other): return self + other def __repr__(self): class_name = type(self).__name__ return f'{class_name}({self.value})' In : sum([MyInt(1), MyInt(2)]) ... AttributeError: 'int' object has no attribute 'value'
Pour résoudre ce problème, vous pouvez fournir un élément de démarrage personnalisé utilisé au lieu de
0
:
In : sum([MyInt(1), MyInt(2)], MyInt(0)) Out: MyInt(3)
sum
est bien optimisé pour la somme des types
float
et
int
mais peut gérer tout autre type personnalisé. Cependant, il refuse de additionner les
bytes
,
bytearray
et
str
car
join
est bien optimisé pour cette opération:
In : sum(['a', 'b'], '') ... TypeError: sum() can't sum strings [use ''.join(seq) instead] In : ints = [x for x in range(10_000)] In : my_ints = [Int(x) for x in ints] In : %timeit sum(ints) 68.3 µs ± 142 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each) In : %timeit sum(my_ints, Int(0)) 5.81 ms ± 20.5 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

Achèvement des index dans le cahier jupyter
Vous pouvez personnaliser les complétions d'index dans le bloc-notes Jupyter en fournissant la
_ipython_key_completions_ method
. De cette façon, vous pouvez contrôler ce qui s'affiche lorsque vous appuyez sur tab après quelque chose comme
d["x
:

Notez que la méthode n'obtient pas la chaîne recherchée comme argument.