Un grouper élégant ?

J’ai souvent besoin d’une fonction que j’appelle grouper, et que je définis à chaque fois super salement depuis la doc de zip :

grouper = lambda s, n: zip(*[iter(s)]*n)

(quitte a faire sale, j’assigne une lambda)

Ça s’utilise comme ça, par exemple, pour faire des octets depuis une chaîne de bits :

>>> grouper('0101100101101111011101010111000001101001', 8)

Donc par exemple :

>>> print(*[chr(int(''.join(bits), 2)) for bits in grouper('0101100101101111011101010111000001101001', 8)], sep='')
Youpi

Mais je suis persuadé qu’il y a plus propre :smiley:

Je ferais quelque chose comme ça personnellement :

In [1]: value = '0101100101101111011101010111000001101001'

In [2]: import re

In [3]: re.findall('.{8}', value)
Out[3]: ['01011001', '01101111', '01110101', '01110000', '01101001']

In [4]: print(*map(chr, (int(val, 2) for val in _3)), sep='')
Youpi
3 « J'aime »

Hahaha ! Pas idiot ! Je note ! Merci :slight_smile:

hello!
moi je me suis fais une petite fonction

def iter_slice(iterable, taille, form=tuple):
    """
    Parcourir n'importe quel itérable, par tailles
        exemple :
            l = [1, 2, 3, 4, 5, 6, 7, 8, 9]
            rows = iter_slice(l, 4)
            for r in rows:
                print(r)
            ...(1, 2, 3, 4)
            ...(5, 6, 7, 8)
            ...(9, )
        :param iterable: List, tuple, set, str etc...
        :param taille: Nombre
        :param form: Format de sortie, par default tuple, mais on peut choisir, list, set ...
        :return: Générateur par tranches
    """
    try:
        i_t = iter(iterable)
        while True:
            yield form(chain((next(i_t),), islice(i_t, taille - 1)))
    except StopIteration:
        pass

et après je m’en sers tous le temps

>>>print(*(chr(int("".join(bits), 2)) for bits in iter_slice(value, 8)),  sep='')
Youpi

L’avantage c’est que cela produit un générateur

pas un grand spécialiste, mais dites moi si ça vous plaît

Merci @Paulo pour le partage !

Ta fonction semble un super endroit pour une démo du walrus operator (j’ai enlevé la docstring pour la concision, et tu la connaît) :

def iter_slice(iterable, taille, form=tuple):
    it = iter(iterable)
    while chunk := form(islice(it, taille)):
        yield chunk

Pour ce qui est d’être un générateur, la version fournie par la doc de zip:

renvoie un zip object, donc un générateur aussi (par contre avec un simple zip les éléments qui “dépassent du dernier groupe” sont droppés, avec un zip_longest le dernier group est rempli par des None (ou autre), aucun des deux comportment n’est identique à ta version qui donne un dernier groupe de la taille du nombre d’éléments restants.

J’ai un petit penchant pour garder une taille fixe, avoir une taille de résultat prévisible permet l’unpacking :

for left, mid, right in zip_longest(*[iter("Hello world")] * 3, fillvalue=""):
    print(left + mid + right)
Hel
lo
wor
ld

mais j’imagine que les trois variantes ont leur valeur dans un contexte donné.

merci @mdk, pour ton retour!
pour des personnes comme moi qui se sont auto-formés, les retours de dev chevronnés comme toi, c’est méga important.
je travaille toujours tout seul et le partage permet de me faire évoluer, au point d’avoir très souvent envie de proposer des semaines de boulot gratuit pour bosser avec des dev de ton talent, en équipe.

1 « J'aime »

Tu peux contribuer avec nous à différents outils qu’on utilise pour la traduction, on essaye de les rassembler dans le repo de l’AFPy, ils sont tous sous licence MIT, tu peux regarder par exemple les repos PyDocTeur, site, pospell, potodo (tous les poutils en fait, certains ne sont pas encore migré dans l’org de l’AFPy), ils ont tous des issues ouvertes, faire une PR dessus permet en même temps de faire avancer les choses pour tout le monde, et d’obtenir une relecture de son code.

J’ai aussi un projet « de test », oeis, pour permettre à mes étudiants de s’entraîner à faire des PR et du code propre, si tu es passionné de suite mathématiques, hésite pas à en implémenter une (moi j’ai toujours pas réussi à implémenter A000001, je ne suis pas une lumière en math).

@mdk
avec plaisir si tous le monde peux y gagner, alors je suis partant. Je suis pas mal pris en ce moment, mais je vais regarder tous cela avec un grand intérêt.
En fait je ne m’étais jamais dis que de proposer des PR, allais faire qu’on allais relire mon code, et en même temps me faire améliorer mon code.
c’est un super idée!
j’ai un désavantage, je suis très peu anglophone et donc cela me ralenti très certainement, car du coup je n’oses pas.
merci encore

Oh bah n’hésite pas a écrire en français dans les repos de l’organisation AFPy, et dans les miens (JulienPalard/), tu verras qu’une partie des messages est déjà en français, et ceux qui sont en anglais sont rédigés par des francophones ^^

Il me semble que c’est ce que j’appelle chunks.

Voir notamment: https://stackoverflow.com/q/312443/140837

A mon sens, ca mériterai un ajout dans itertools

HAHAA merci @amirouche pour le lien, on voit que le sujet me tient à cœur depuis longtemps, j’y ai répondu une blague en 2015, et un bon ami y a répondu aussi (pour la blague, aussi).

Pour ceux qui ont la flemme de cliquer, voici nos deux réponses :

Y = lambda f: (lambda x: x(x))(lambda y: f(lambda *args: y(y)(*args)))
chunks = Y(lambda f: lambda n: [n[0][:n[1]]] + f((n[0][n[1]:], n[1])) if len(n[0]) > 0 else [])

et

def chunks(li, n):
    if li == []:
        return
    yield li[:n]
    yield from chunks(li[n:], n)

Je pense que je vais m’en tenir à la version de Paulo revue pour utiliser un walrus :

def chunks(iterable, n):
    """Yield successive n-sized chunks from iterable.
    """
    it = iter(iterable)
    while chunk := tuple(islice(it, n)):
        yield chunk

J’aime en particulier le fait que c’est tout à fait utilisable sans en faire une fonction, sans avoir a passer par un tuple temporaire :

it = iter("0101100101101111011101010111000001101001")
while byte := ''.join(islice(it, 8)):
    print(int(byte, 2))

Pour tout vous dire, l’idée de base était de SAM…
mais merci pour le compliment :smile:

Ce que j’adore sur ce genre de discussion c’est que les idées arrives à maturité par l’échange

1 « J'aime »

Bon bah maintenant c’est dans la stdlib :

2 « J'aime »