¿Cómo determinar el tipo de estructuras de datos anidadas en Python?

Actualmente estoy traduciendo algo de Python a F #, específicamente de redes neuronales y de aprendizaje profundo .

Para asegurarse de que las estructuras de datos se traduzcan correctamente, se necesitan los detalles de los tipos nesteds de Python. La función type () funciona para tipos simples pero no para tipos nesteds.

Por ejemplo en Python:

> data = ([[1,2,3],[4,5,6],[7,8,9]],["a","b","c"]) > type(data)  

Solo da el tipo del primer nivel. No se sabe nada acerca de los arreglos en la tupla.

Esperaba algo como lo que hace F #

 > let data = ([|[|1;2;3|];[|4;5;6|];[|7;8;9|]|],[|"a";"b";"c"|]);; val data : int [] [] * string [] = ([|[|1; 2; 3|]; [|4; 5; 6|]; [|7; 8; 9|]|], [|"a"; "b"; "c"|]) 

Devolviendo la firma independiente del valor.

int [] [] * cadena []

 * is a tuple item separator int [] [] is a two dimensional jagged array of int string [] is a one dimensional array of string 

¿Se puede o cómo se hace esto en Python?

TLDR;

Actualmente estoy usando PyCharm con el depurador y en la ventana de variables haciendo clic en la opción de vista para una variable individual para ver los detalles. El problema es que la salida contiene los valores junto con los tipos entremezclados y solo necesito la firma de tipo. Cuando las variables son similares (float [50000] [784], int [50000]), los valores se interponen. Sí, estoy cambiando el tamaño de las variables por ahora, pero eso es una solución y no una solución.

p.ej

Usando la Comunidad PyCharm

 (array([[ 0., 0., 0., ..., 0., 0., 0.], [ 0., 0., 0., ..., 0., 0., 0.], [ 0., 0., 0., ..., 0., 0., 0.], ..., [ 0., 0., 0., ..., 0., 0., 0.], [ 0., 0., 0., ..., 0., 0., 0.], [ 0., 0., 0., ..., 0., 0., 0.]], dtype=float32), array([7, 2, 1, ..., 4, 5, 6])) 

Utilizando spyder

Usando el visor de variables Spyder

Usando la comunidad de Visual Studio con Python Tools para Visual Studio

 (array([[ 0., 0., 0., ..., 0., 0., 0.], [ 0., 0., 0., ..., 0., 0., 0.], [ 0., 0., 0., ..., 0., 0., 0.], ..., [ 0., 0., 0., ..., 0., 0., 0.], [ 0., 0., 0., ..., 0., 0., 0.], [ 0., 0., 0., ..., 0., 0., 0.]], dtype=float32), array([5, 0, 4, ..., 8, 4, 8], dtype=int64)) 

EDITAR:

Ya que esta pregunta se ha iniciado, parece que alguien está buscando más detalles, aquí está mi versión modificada que también puede manejar la ndarray numpy . Gracias a Vlad por la versión inicial.

¿También debido al uso de una variación de la encoding de longitud de ejecución que no se usa más? Para tipos heterogéneos.

 # Note: Typing for elements of iterable types such as Set, List, or Dict # use a variation of Run Length Encoding. def type_spec_iterable(iterable, name): def iterable_info(iterable): # With an iterable for it to be comparable # the identity must contain the name and length # and for the elements the type, order and count. length = 0 types_list = [] pervious_identity_type = None pervious_identity_type_count = 0 first_item_done = False for e in iterable: item_type = type_spec(e) if (item_type != pervious_identity_type): if not first_item_done: first_item_done = True else: types_list.append((pervious_identity_type, pervious_identity_type_count)) pervious_identity_type = item_type pervious_identity_type_count = 1 else: pervious_identity_type_count += 1 length += 1 types_list.append((pervious_identity_type, pervious_identity_type_count)) return (length, types_list) (length, identity_list) = iterable_info(iterable) element_types = "" for (identity_item_type, identity_item_count) in identity_list: if element_types == "": pass else: element_types += "," element_types += identity_item_type if (identity_item_count != length) and (identity_item_count != 1): element_types += "[" + `identity_item_count` + "]" result = name + "[" + `length` + "]" return result def type_spec_dict(dict, name): def dict_info(dict): # With a dict for it to be comparable # the identity must contain the name and length # and for the key and value combinations the type, order and count. length = 0 types_list = [] pervious_identity_type = None pervious_identity_type_count = 0 first_item_done = False for (k, v) in dict.iteritems(): key_type = type_spec(k) value_type = type_spec(v) item_type = (key_type, value_type) if (item_type != pervious_identity_type): if not first_item_done: first_item_done = True else: types_list.append((pervious_identity_type, pervious_identity_type_count)) pervious_identity_type = item_type pervious_identity_type_count = 1 else: pervious_identity_type_count += 1 length += 1 types_list.append((pervious_identity_type, pervious_identity_type_count)) return (length, types_list) (length, identity_list) = dict_info(dict) element_types = "" for ((identity_key_type,identity_value_type), identity_item_count) in identity_list: if element_types == "": pass else: element_types += "," identity_item_type = "(" + identity_key_type + "," + identity_value_type + ")" element_types += identity_item_type if (identity_item_count != length) and (identity_item_count != 1): element_types += "[" + `identity_item_count` + "]" result = name + "[" + `length` + "]" return result def type_spec_tuple(tuple, name): return name + "" def type_spec(obj): object_type = type(obj) name = object_type.__name__ if (object_type is int) or (object_type is long) or (object_type is str) or (object_type is bool) or (object_type is float): result = name elif object_type is type(None): result = "(none)" elif (object_type is list) or (object_type is set): result = type_spec_iterable(obj, name) elif (object_type is dict): result = type_spec_dict(obj, name) elif (object_type is tuple): result = type_spec_tuple(obj, name) else: if name == 'ndarray': ndarray = obj ndarray_shape = "[" + `ndarray.shape`.replace("L","").replace(" ","").replace("(","").replace(")","") + "]" ndarray_data_type = `ndarray.dtype`.split("'")[1] result = name + ndarray_shape + "" else: result = "Unknown type: " , name return result 

No lo consideraría hecho, pero ha funcionado en todo lo que necesitaba hasta ahora.

Una forma de hacerlo a mano sería:

 def type_spec_iterable(obj, name): tps = set(type_spec(e) for e in obj) if len(tps) == 1: return name + "<" + next(iter(tps)) + ">" else: return name + "" def type_spec_dict(obj): tps = set((type_spec(k), type_spec(v)) for (k,v) in obj.iteritems()) keytypes = set(k for (k, v) in tps) valtypes = set(v for (k, v) in tps) kt = next(iter(keytypes)) if len(keytypes) == 1 else "?" vt = next(iter(valtypes)) if len(valtypes) == 1 else "?" return "dict<%s, %s>" % (kt, vt) def type_spec_tuple(obj): return "tuple<" + ", ".join(type_spec(e) for e in obj) + ">" def type_spec(obj): t = type(obj) res = { int: "int", str: "str", bool: "bool", float: "float", type(None): "(none)", list: lambda o: type_spec_iterable(o, 'list'), set: lambda o: type_spec_iterable(o, 'set'), dict: type_spec_dict, tuple: type_spec_tuple, }.get(t, lambda o: type(o).__name__) return res if type(res) is str else res(obj) if __name__ == "__main__": class Foo(object): pass for obj in [ 1, 2.3, None, False, "hello", [1, 2, 3], ["a", "b"], [1, "h"], (False, 1, "2"), set([1.2, 2.3, 3.4]), [[1,2,3],[4,5,6],[7,8,9]], [(1,'a'), (2, 'b')], {1:'b', 2:'c'}, [Foo()], # todo - inheritance? ]: print repr(obj), ":", type_spec(obj) 

Esto imprime:

 1 : int 2.3 : float None : (none) False : bool 'hello' : str [1, 2, 3] : list ['a', 'b'] : list [1, 'h'] : list (False, 1, '2') : tuple set([2.3, 1.2, 3.4]) : set [[1, 2, 3], [4, 5, 6], [7, 8, 9]] : list> [(1, 'a'), (2, 'b')] : list> {1: 'b', 2: 'c'} : dict [<__main__.Foo object at 0x101de6c50>] : list 

Hay una pregunta sobre qué tan lejos quiere llevarlo y qué tan profundo debe verificarlo, con concesiones entre velocidad y precisión. Por ejemplo, ¿desea revisar todos los elementos de una lista grande? ¿Desea manejar tipos personalizados (y rastrear ancestros comunes de esos tipos)?

Vale la pena leerlo, aunque no estoy seguro de que sea aplicable, este PEP en sugerencias de tipo .

Como comenté, esto es imposible en Python, porque las listas no están tipificadas.

Todavía puedes pretender hacerlo:

 def typ(something, depth=0): if depth > 63: return "..." if type(something) == tuple: return ">" elif type(something) == list: return "" else: return str(type(something)) 

Eso devuelve la cadena >>,>>> para su ejemplo .

Edición: para que se vea más como F #, podrías hacer esto en su lugar:

 def typ(something, depth=0): if depth > 63: return "..." if type(something) == tuple: return " * ".join(typ(ding, depth+1) for ding in something) elif type(something) == list: return (typ(something[0]) if something else 'empty') + " []" else: return str(type(something, depth+1)).split("'")[1] 

que devolverá int [] [] * str [] en tu ejemplo.