J'essaie d'implémenter une simulation pour un modèle de réseau (Lattice Boltzmann) en Python. Chaque site du réseau possède un certain nombre de propriétés et interagit avec les sites voisins selon certaines règles. J'ai pensé qu'il pourrait être intelligent de créer une classe avec toutes les propriétés et de créer une grille d'instances de cette classe. (Comme je suis inexpérimenté avec Python, ce n'est peut-être pas du tout une bonne idée, alors n'hésitez pas à commenter mon approche.)
Voici un exemple de jouet de ce que je fais
class site:
def __init__(self,a,...):
self.a = a
.... other properties ...
def set_a(self, new_a):
self.a = new_a
Maintenant, je veux traiter un réseau 2D/3D (grille) de ces sites, j'ai donc essayé de faire ce qui suit (voici une grille 2D 3x3 à titre d'exemple, mais en simulation, j'aurais besoin de l'ordre de> 1000x1000X1000)
lattice = np.empty( (3,3), dtype=object)
lattice[:,:] = site(3)
Maintenant, le problème est que chaque point de réseau fait référence à la même instance, par exemple
lattice[0,0].set_a(5)
définira également la valeur de lattice [0,2] .a sur 5. Ce comportement n'est pas souhaité. Pour éviter le problème, je peux faire une boucle sur chaque point de la grille et affecter les objets élément par élément, comme
for i in range(3):
for j in range(3):
lattice[i,j] = site(a)
Mais existe-t-il une meilleure façon (sans impliquer les boucles) d'affecter des objets à un tableau multidimensionnel?
Merci
Vous pouvez vectoriser la fonction __init__
De la classe:
import numpy as np
class Site:
def __init__(self, a):
self.a = a
def set_a(self, new_a):
self.a = new_a
vSite = np.vectorize(Site)
init_arry = np.arange(9).reshape((3,3))
lattice = np.empty((3,3), dtype=object)
lattice[:,:] = vSite(init_arry)
Cela peut sembler plus propre, mais n'a aucun avantage en termes de performances par rapport à votre solution de bouclage. Les réponses de compréhension de liste créent une liste intermédiaire python qui entraînerait un impact sur les performances.
La pièce manquante pour vous est que Python traite tout comme une référence. (Il y a des objets, des chaînes et des nombres et des tuples "immuables", qui sont traités plus comme des valeurs.) Quand vous le faites
lattice[:,:] = site(3)
vous dites "Python: créez un nouvel objet site
, et dites à chaque élément de lattice
de pointer vers cet objet." Pour voir que c'est vraiment le cas, imprimez le tableau pour voir que les adresses mémoire des objets sont toutes les mêmes:
array([[<__main__.Site object at 0x1029d5610>,
<__main__.Site object at 0x1029d5610>,
<__main__.Site object at 0x1029d5610>],
[<__main__.Site object at 0x1029d5610>,
<__main__.Site object at 0x1029d5610>,
<__main__.Site object at 0x1029d5610>],
[<__main__.Site object at 0x1029d5610>,
<__main__.Site object at 0x1029d5610>,
<__main__.Site object at 0x1029d5610>]], dtype=object)
La boucle est une bonne façon de procéder. Avec des tableaux numpy, cela peut être votre meilleure option; avec Python listes, vous pouvez également utiliser une compréhension de liste:
lattice = [ [Site(i + j) for i in range(3)] for j in range(3) ]
Vous pouvez utiliser une compréhension de liste avec le numpy.array
construction:
lattice = np.array( [ [Site(i + j) for i in range(3)] for j in range(3) ],
dtype=object)
Maintenant, quand vous imprimez lattice
, c'est
array([[<__main__.Site object at 0x1029d53d0>,
<__main__.Site object at 0x1029d50d0>,
<__main__.Site object at 0x1029d5390>],
[<__main__.Site object at 0x1029d5750>,
<__main__.Site object at 0x1029d57d0>,
<__main__.Site object at 0x1029d5990>],
[<__main__.Site object at 0x1029d59d0>,
<__main__.Site object at 0x1029d5a10>,
<__main__.Site object at 0x1029d5a50>]], dtype=object)
de sorte que vous pouvez voir que chaque objet est unique.
Vous devez également noter que les méthodes "setter" et "getter" (par exemple, set_a
) ne sont pas Pythonic. Il est préférable de définir et d'obtenir des attributs directement, puis d'utiliser le @property
décorateur si vous avez VRAIMENT besoin d'empêcher l'accès en écriture à un attribut.
Notez également qu'il est standard pour les classes Python à écrire en utilisant CamelCase, pas en minuscules.
Je ne sais pas mieux, mais comme alternative à un ensemble explicite de boucles, vous pouvez écrire
lattice = np.empty( (3,3), dtype=object)
lattice.flat = [site(3) for _ in lattice.flat]
qui devrait fonctionner quelle que soit la forme du réseau.