2016-01-06 76 views
3

我目前正在將一些Python翻譯成F#,特別是neural-networks-and-deep-learning如何確定Python中嵌套數據結構的類型?

爲了確保數據結構正確轉換,需要Python中嵌套類型的詳細信息。 type()函數適用於簡單類型,但不適用於嵌套類型。

例如在Python:

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

只給出了第一級的類型。對元組中的數組沒有任何瞭解。

我希望這樣的事情是什麼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"|]) 

返回簽名獨立的價值

INT [] [] *字符串[]

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

Python或Python可以做到這一點嗎?

TLDR;

目前我正在使用PyCharm與調試器,並在變量窗口中單擊單個變量的查看選項以查看詳細信息。問題是輸出包含值以及混合類型,我只需要類型簽名。當變量類似於(float [50000] [784],int [50000])時,這些值會成爲問題。是的,我現在正在調整變量的大小,但這是一種解決方法,而不是解決方案。

例如

使用PyCharm Community

(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])) 

使用Spyder

Using Spyder variable viewer

使用Visual Studio CommunityPython Tools for 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)) 

編輯:

由於這個問題已被盯着某人顯然是尋找更多的細節,這裏是我的修改後的版本,它也可以處理numpy ndarray。感謝Vlad的初始版本。

也因爲使用Run Length Encoding的變化,沒有更多的使用?針對異構類型。

# 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` + "]<" + element_types + ">" 
    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` + "]<" + element_types + ">" 
    return result 

def type_spec_tuple(tuple, name): 
    return name + "<" + ", ".join(type_spec(e) for e in tuple) + ">" 

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 + "<" + ndarray_data_type + ">" 
     else: 
      result = "Unknown type: " , name 
    return result 

我不認爲它完成了,但它已經在我到目前爲止所需的一切工作。

+0

你可能能夠爲自己的元組一起破解自己的東西,但不能用於列表或字典,因爲它們是無類型的(元組也是,但至少它們是不可變的)。什麼應該是'[1,2,'c']'的類型? – L3viathan

+0

數據來自可預測的結構性來源嗎?或者它真的只是偶然? – Monkpit

+1

您是試圖從正在運行的腳本中的變量推導出這些類型,還是從代碼本身推導出它們?生成一個列表或元組是一回事,然後通過它遍歷每個級別的類型。查看代碼並且推導它產生的內容(不運行它)是另一回事。 – hpaulj

回答

1

一種方式做手工這將是:

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) 

此打印:

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

有你想多遠,走這一個問題,如何深刻檢查,同在速度和準確性之間取捨。例如,你是否想要瀏覽大型列表中的所有項目?你想處理自定義類型(並追蹤這些類型的共同祖先)嗎?

值得一讀,但我不確定它是否適用,這個PEP在type hints

+0

很好的細節。 Python不是我經常使用的語言,所以我將不得不嘗試一下,花點時間去理解它。這裏有足夠的肉給我挖掘享受。這個問題後你寫了這個嗎?如果是這樣,我印象深刻,如果你只是複製並粘貼更改,然後確定。 –

+0

我在問題後從頭開始編寫它。我沒有看上網,但我認爲有人必須做過這樣的事情。 – Vlad

+0

我很驚訝沒有一個標準的答案。我不習慣使用鴨子打字,所以這對我來說是一種學習體驗。 –

2

正如我所評論的,這在Python中是不可能的,因爲列表是無類型的。

您仍然可以假裝做:

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

,用於返回您的示例字符串<class 'tuple': <<class 'list': <class 'list': <class 'int'>>>,<class 'list': <class 'str'>>>>

編輯:,使它看起來更像是F#你可以這樣做,而不是:

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] 

,這將在你的例子返回int [] [] * str []

+0

謝謝。我會花一些時間。 –

+0

@GuyCoder我添加了一個替代,更簡潔的版本(因爲我們在這裏不是pythonic)。 – L3viathan

+0

您瞭解我的想法。很好的更新。 –