2
votes

Moyen élégant de garder l'élément précédent accessible en boucle à travers les éléments d'une liste

Dans un projet Python, j'ai besoin d'appliquer une fonction à chacun des deux éléments d'une liste de listes qui ont le même index interne et un index externe voisin. Les sorties sont stockées dans une nouvelle matrice.

Le code que j'ai écrit fonctionne, mais n'est pas élégant et pyflakes s'en plaint.

Comment nettoyer ce code?

Informations supplémentaires:

Le code que j'écris fait partie d'un module qui résout un puzzle numérique.

À un moment Je suis en train de parcourir une liste de listes d'instances de classe.
Ils représentent des cellules en lignes dans un terrain de jeu.
Et je dois appliquer une fonction à chacune des deux cellules verticalement voisines,
et stocker sa sortie dans une nouvelle matrice.

Ici, peu importe quelle cellule d'une paire est la première, mais les paires doivent être dans l'ordre.

Extrait de code:

Line 7: pyflakes [E]: undefined name 'prev_line'
Line 9: pyflakes [E]: local variable 'prev_line' is assigned to but never used

Ce dont se plaint pyflakes:

def func(cell_matrix):
    out_matrix = []
    for y_pos, line in enumerate(cell_matrix):
        out_line = []
        if y_pos != 0:
            for x_pos, cell in enumerate(line):
                out_line.append(compare_func(prev_line[x_pos], cell)
            out_matrix.append(out_line)
        prev_line = line
    return out_matrix

p >


2 commentaires

Je pense que cela serait résolu si vous définissiez prev_line = None ci-dessous out_matrix = []


non, sera toujours prev_line [x_pos] n'aura rien


4 Réponses :


0
votes

Changez votre code en:

def func(cell_matrix):
    out_matrix = []
    prev_line = []
    for y_pos, line in enumerate(cell_matrix):
        out_line = []
        if y_pos != 0:
            for x_pos, cell in enumerate(line):
                out_line.append(compare_func(prev_line[x_pos], cell)
            out_matrix.append(out_line)
        prev_line = line
    return out_matrix

Vous devez déclarer prev_line hors de la portée de la boucle for pour qu'il puisse l'utiliser à chaque boucle.


4 commentaires

tout d'abord passer ce code, cette ligne: out_line.append (compare_func (prev_line [x_pos], cell) essaiera d'accéder à None [x_pos]


Ce ne sera pas le cas. Dans la première passe, cette ligne n'est pas exécutée car l'instruction if l'en empêche. Ensuite, prev_line est écrasé par line.


Il s'exécute tout aussi bien, mais maintenant pylint renvoie cette erreur: [E1136] 8: func: La valeur 'prev_line' est désinscriptible


@fatalcreator a changé prev_line = [], essayez-le aussi, j'ai modifié la réponse



1
votes

Vous devez déclarer le nom de la variable avant de l'utiliser:

def func(cell_matrix):
    out_matrix = []
    prev_line = cell_matrix[0]    # use 1st line as prev_line
    for line in cell_matrix[1:]:  # use 2nd to nth line, no y_pos used
                                  # in the following code so no need to enumerate
        out_line = []
            for x_pos, cell in enumerate(line):
                out_line.append(compare_func(prev_line[x_pos], cell) ) # missing )
            out_matrix.append(out_line)
        prev_line = line
    return out_matrix


0 commentaires

1
votes

Je recommanderais de n'utiliser que des index, donc vous épargnez la variable prev_.

Par exemple

def func(cell_matrix):                                                           
    return [[compare_func(                                                       
                 cell_matrix[y_pos-1][x_pos], cell_matrix[y_pos][x_pos])         
             for x_pos in range(len(cell_matrix[y_pos]))]                        
            for y_pos in range(1, len(cell_matrix))]                             

Mais cela pourrait être encore plus simplifié en utilisant une compréhension:

def func(cell_matrix):                                                           
    out_matrix = []                                                              
    for y_pos in range(len(cell_matrix)):                                        
        out_line = []                                                            
        if y_pos != 0:                                                           
            for x_pos in range(len(cell_matrix[y_pos])):                         
                out_line.append(compare_func(cell_matrix[y_pos-1][x_pos],        
                                             cell_matrix[y_pos][x_pos]))         
            out_matrix.append(out_line)                                          
    return out_matrix                                                            

Modifier: Au fait, les erreurs que vous obtenez sont des messages pyflakes, le code fonctionne bien afaik. (On pourrait dire que c'est l'incapacité des pyflakes à analyser correctement le code)


3 commentaires

n'est-il pas découragé de parcourir les éléments d'une liste en utilisant len ​​(liste)? Ça a l'air autrement.


correct, j'ai entendu cela aussi. Mais la seule alternative que j'ai trouvée était pour y_pos, _unused dans enumerate (cell_matrix): Je ne suis pas sûr de l'aimer.


Donc, fonctionnellement, c'est exactement là où je devais être pointé. Il manque un peu de lisibilité, c'est pourquoi je mets ma propre réponse ci-dessous, mais je vais accepter la vôtre. Btw je comprends que les plaintes de pyflakes ne signifient pas que mon code ne fonctionnerait pas, mais l'ont précédé comme un autre signe de mon code maladroit.



0
votes

J'ai fini par déplacer l'itération vers une fonction distincte car je trouve que c'est plus facile à lire de cette façon.

def subsequences(var, r=2):
    """Yield subsequences of var with length r"""
    # subsequences("ABCD") --> AB BC CD
    for index in range(len(var)-r+1):
        yield tuple(var[index+n] for n in range(r))

def func(cell_matrix):
    return [
        [
            compare_func(cell_a, cell_b)
            for cell_a, cell_b in zip(prev_line, line)
        ]
        for prev_line, line in subsequences(cell_matrix)
    ]

Cela pourrait être juste un goût personnel.


0 commentaires