Utiliser des collections et des outils fonctionnels
collections : conteneurs spécialisés
| Classe | Usage |
|---|---|
Counter |
Compter les occurrences. |
defaultdict |
Produire une valeur initiale pour une clé absente. |
deque |
Ajouter ou retirer efficacement aux deux extrémités. |
ChainMap |
Présenter plusieurs dictionnaires comme une recherche commune. |
namedtuple |
Créer des tuples dont les champs ont un nom. |
from collections import Counter, defaultdict, deque
counts = Counter(["Python", "C#", "Python"])
print(counts.most_common(1)) # [('Python', 2)]
groups = defaultdict(list)
for name, category in [("Clavier", "Matériel"), ("Souris", "Matériel")]:
groups[category].append(name)
print(groups["Matériel"]) # ['Clavier', 'Souris']
queue = deque(["A", "B"])
queue.append("C")
print(queue.popleft()) # A
Pour communiquer entre threads, le module queue fournit des conteneurs avec des mécanismes de synchronisation adaptés.
Parallèle C# et .NET
string[] languages = { "Python", "C#", "Python" };
var counts = languages.GroupBy(x => x).ToDictionary(g => g.Key, g => g.Count());
Console.WriteLine(counts["Python"]); // 2
var groups = new Dictionary<string, List<string>>();
foreach (string name in new[] { "Clavier", "Souris" })
{
if (!groups.TryGetValue("Matériel", out var items))
groups["Matériel"] = items = new List<string>();
items.Add(name);
}
var queue = new Queue<string>(new[] { "A", "B" });
queue.Enqueue("C");
Console.WriteLine(queue.Dequeue()); // A
GroupBy() et les dictionnaires couvrent le comptage ; TryGetValue() permet une initialisation explicite. Queue<T> couvre seulement le comportement FIFO du deque montré ; LinkedList<T> permet des opérations aux deux extrémités avec d’autres caractéristiques. Un tuple nommé ou un record se rapproche de namedtuple. Les collections ordinaires ne sont pas automatiquement synchronisées.
itertools : combiner des itérations
Ces fonctions produisent généralement des itérateurs, ce qui évite de matérialiser immédiatement tous les résultats.
| Fonction | Usage |
|---|---|
chain() |
Enchaîner des itérables. |
islice() |
Extraire une portion d'itérateur. |
count() / repeat() / cycle() |
Générer des suites, parfois infinies. |
product() |
Produit cartésien. |
combinations() / permutations() |
Combinaisons / permutations. |
accumulate() |
Calculs cumulés. |
groupby() |
Regrouper des éléments consécutifs selon une clé. |
from itertools import chain, combinations, accumulate
print(list(chain([1, 2], [3, 4]))) # [1, 2, 3, 4]
print(list(combinations(["A", "B", "C"], 2)))
# [('A', 'B'), ('A', 'C'), ('B', 'C')]
print(list(accumulate([10, 20, 30]))) # [10, 30, 60]
groupby() regroupe uniquement les clés adjacentes : un tri préalable est souvent nécessaire. Ne convertissons pas un itérateur infini en liste.
Parallèle C# et .NET
Console.WriteLine(string.Join(", ", new[] { 1, 2 }.Concat(new[] { 3, 4 })));
string[] letters = { "A", "B", "C" };
var pairs = letters.SelectMany((a, i) => letters.Skip(i + 1).Select(b => (a, b)));
foreach (var pair in pairs) Console.WriteLine(pair);
static IEnumerable<int> CumulativeSum(IEnumerable<int> values)
{
int total = 0;
foreach (int value in values) { total += value; yield return total; }
}
Console.WriteLine(string.Join(", ", CumulativeSum(new[] { 10, 20, 30 })));
Concat() correspond à chain() et Skip() / Take() à des portions d’itération. SelectMany() exprime les produits et combinaisons simples ; yield return construit des itérateurs spécialisés. Différence majeure : LINQ GroupBy() rassemble les clés égales sur toute la source ; itertools.groupby() regroupe uniquement des éléments adjacents. Une requête LINQ est souvent réénumérable ; un itérateur Python est consommable.
functools : adapter et mémoriser des fonctions
| Élément | Usage |
|---|---|
partial() |
Fixer certains arguments d'une fonction. |
cache / lru_cache |
Mémoriser les résultats selon les arguments. |
wraps() |
Préserver les métadonnées lors de l'écriture d'un décorateur. |
reduce() |
Réduire un itérable par application successive d'une fonction. |
from functools import partial, lru_cache
def calculate_total(price, tax_rate):
return price * (1 + tax_rate)
standard_total = partial(calculate_total, tax_rate=0.20)
print(standard_total(100)) # 120.0
@lru_cache(maxsize=128)
def square(number):
return number * number
print(square(12)) # 144
La mémorisation exige des arguments hachables et convient lorsque le résultat reste valide pour les mêmes arguments. cache n'impose pas de limite de taille.
Parallèle C# et .NET
static double CalculateTotal(double price, double taxRate) => price * (1 + taxRate);
Func<double, double> standardTotal = price => CalculateTotal(price, 0.20);
Console.WriteLine(standardTotal(100)); // 120
var cache = new Dictionary<int, int>();
int Square(int number)
{
if (!cache.TryGetValue(number, out int result))
cache[number] = result = number * number;
return result;
}
Console.WriteLine(Square(12)); // 144
Une fermeture ou une lambda exprime le besoin de partial(). Le dictionnaire montre une mémorisation simple, sans limite de taille, politique LRU ni synchronisation : ce n’est pas un remplacement complet de lru_cache. LINQ Aggregate() se rapproche de reduce(). Les décorateurs de fonctions Python n’ont pas de mécanisme C# strictement identique ; les attributs C# n’enveloppent pas automatiquement une fonction.
copy : copier des structures
copy() effectue une copie superficielle ; deepcopy() copie récursivement les objets concernés en tenant compte des références déjà traitées.
import copy
original = [[1, 2], [3, 4]]
shallow = copy.copy(original)
deep = copy.deepcopy(original)
original[0].append(9)
print(shallow[0]) # [1, 2, 9]
print(deep[0]) # [1, 2]
Une copie profonde n'est pas automatiquement préférable : elle peut coûter cher et ne duplique pas arbitrairement des ressources externes comme des connexions.
Parallèle C# et .NET
var original = new List<List<int>> { new() { 1, 2 }, new() { 3, 4 } };
var shallow = new List<List<int>>(original);
var deepForThisShape = original.Select(row => new List<int>(row)).ToList();
original[0].Add(9);
Console.WriteLine(string.Join(", ", shallow[0])); // 1, 2, 9
Console.WriteLine(string.Join(", ", deepForThisShape[0])); // 1, 2
Le constructeur de liste effectue une copie superficielle. La projection copie les lignes de cette structure précise, pas un graphe d’objets arbitraire. .NET ne fournit pas un deepcopy() universel comparable. MemberwiseClone() est superficiel ; ICloneable ne précise pas à lui seul la profondeur du clonage.