¿Hay un equivalente en Python de rango (n) para rangos multidimensionales?

En Python, el rango (3) devolverá [0,1,2]. ¿Hay un equivalente para rangos multidimensionales?

range((3,2)) # [(0,0),(0,1),(1,0),(1,1),(2,0),(2,1)] 

Así, por ejemplo, un bucle a través de los mosaicos de un área rectangular en un juego basado en mosaicos podría escribirse como:

 for x,y in range((3,2)): 

Tenga en cuenta que no estoy pidiendo una implementación. Me gustaría saber si este es un patrón reconocido y si hay una función incorporada en Python o en sus bibliotecas estándar / comunes.

En numpy, es numpy.ndindex . También eche un vistazo a numpy.ndenumerate .

P.ej

 import numpy as np for x, y in np.ndindex((3,2)): print x, y 

Esto produce:

 0 0 0 1 1 0 1 1 2 0 2 1 

Podrías usar itertools.product() :

 >>> import itertools >>> for (i,j,k) in itertools.product(xrange(3),xrange(3),xrange(3)): ... print i,j,k 

Las múltiples xrange() repetidas de xrange() podrían expressse así, si desea escalar esto a un bucle de diez dimensiones o algo similar ridículo:

 >>> for combination in itertools.product( xrange(3), repeat=10 ): ... print combination 

Qué bucles sobre diez variables, variando desde (0,0,0,0,0,0,0,0,0,0) hasta (2,2,2,2,2,2,2,2,2,2) .


En general itertools es un módulo increíblemente impresionante. De la misma manera que las expresiones regulares son mucho más expresivas que los métodos de cadena “simple”, itertools es una forma muy elegante de express bucles complejos. Te debes a ti mismo leer la documentación del módulo itertools . Hará tu vida más divertida.

En realidad, hay una syntax simple para esto. Solo necesitas tener dos for s:

 >>> [(x,y) for x in range(3) for y in range(2)] [(0, 0), (0, 1), (1, 0), (1, 1), (2, 0), (2, 1)] 

Ese es el producto cartesiano de dos listas por lo tanto:

 import itertools for element in itertools.product(range(3),range(2)): print element 

da esta salida:

 (0, 0) (0, 1) (1, 0) (1, 1) (2, 0) (2, 1) 

Puede utilizar el product del módulo itertools .

 itertools.product(range(3), range(2)) 

Me gustaría echar un vistazo a numpy.meshgrid :

http://docs.scipy.org/doc/numpy-1.6.0/reference/generated/numpy.meshgrid.html

que le dará los valores de cuadrícula X e Y en cada posición en una malla / cuadrícula. Entonces podrías hacer algo como:

 import numpy as np X,Y = np.meshgrid(xrange(3),xrange(2)) zip(X.ravel(),Y.ravel()) #[(0, 0), (1, 0), (2, 0), (0, 1), (1, 1), (2, 1)] 

o

 zip(X.ravel(order='F'),Y.ravel(order='F')) # [(0, 0), (0, 1), (1, 0), (1, 1), (2, 0), (2, 1)] 

El ndindex() de ndindex() funciona para el ejemplo que dio, pero no sirve para todos los casos de uso. A diferencia del range() incorporado de Python, que permite tanto un start , una stop y un step arbitrarios, np.ndindex() de np.ndindex() solo acepta una stop . (Se supone que el start es (0,0,...) , y el step es (1,1,...) .)

Aquí hay una implementación que actúa más como la función de range() incorporada. Es decir, permite argumentos arbitrarios de start / stop / step , pero funciona en tuplas en lugar de simples enteros.

 import sys from itertools import product, starmap # Python 2/3 compatibility if sys.version_info.major < 3: from itertools import izip else: izip = zip xrange = range def ndrange(start, stop=None, step=None): if stop is None: stop = start start = (0,)*len(stop) if step is None: step = (1,)*len(stop) assert len(start) == len(stop) == len(step) for index in product(*starmap(xrange, izip(start, stop, step))): yield index 

Ejemplo:

 In [7]: for index in ndrange((1,2,3), (10,20,30), step=(5,10,15)): ...: print(index) ...: (1, 2, 3) (1, 2, 18) (1, 12, 3) (1, 12, 18) (6, 2, 3) (6, 2, 18) (6, 12, 3) (6, 12, 18)