2016-12-10 30 views
2

一個例子:如何排序(key = lambda x :)在場景後面實現?

names = ["George Washington", "John Adams", "Thomas Jefferson", "James Madison"] 
sorted(names, key=lambda name: name.split()[-1].lower()) 

我知道key用於比較不同的名字,但它可以有兩種不同的實現:

  1. 首先計算全部爲每名鍵,並綁定鑰匙和姓名以某種方式在一起,並對它們進行分類。每當比較發生

與所述第一方法的問題時間在p

  • 計算關鍵是它具有限定另一數據結構來綁定密鑰和數據。第二種方法的問題是密鑰可能被多次計算,即name.split()[-1].lower()將被執行多次,這非常耗時。

    我只是想知道在哪種方式Python實施sorted()

  • +1

    由於性能方面的原因,'key ='與''cmp =''較早的'cmp ='的整個*點是爲了減少調用次數。如果它每次都運行計算,它將比* cmp'方法取代更多的*函數調用,所以它不可能成功實現其設計目標。 –

    回答

    5

    按鍵功能只執行一次每個值,產生(keyvalue, value)對;這是用來排序和稍後只是值排序順序返回。這有時稱爲Schwartzian transform

    您可以自己測試;您可以計算函數被調用的頻率,例如:

    >>> def keyfunc(value): 
    ...  keyfunc.count += 1 
    ...  return value 
    ... 
    >>> keyfunc.count = 0 
    >>> sorted([0, 8, 1, 6, 4, 5, 3, 7, 9, 2], key=keyfunc) 
    [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] 
    >>> keyfunc.count 
    10 
    

    或者您可以收集所有傳入的值;你會看到,他們按照原來的輸入順序:

    >>> def keyfunc(value): 
    ...  keyfunc.arguments.append(value) 
    ...  return value 
    ... 
    >>> keyfunc.arguments = [] 
    >>> sorted([0, 8, 1, 6, 4, 5, 3, 7, 9, 2], key=keyfunc) 
    [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] 
    >>> keyfunc.arguments 
    [0, 8, 1, 6, 4, 5, 3, 7, 9, 2] 
    

    如果你想讀的CPython的源代碼,相關函數被調用listsort(),並keyfunc在下面的循環使用(saved_ob_item是輸入陣列),其在執行之前排序發生:

    for (i = 0; i < saved_ob_size ; i++) { 
        keys[i] = PyObject_CallFunctionObjArgs(keyfunc, saved_ob_item[i], 
                  NULL); 
        if (keys[i] == NULL) { 
         for (i=i-1 ; i>=0 ; i--) 
          Py_DECREF(keys[i]); 
         if (saved_ob_size >= MERGESTATE_TEMP_SIZE/2) 
          PyMem_FREE(keys); 
         goto keyfunc_fail; 
        } 
    } 
    
    lo.keys = keys; 
    lo.values = saved_ob_item; 
    

    所以最後,有兩個陣列,一個與keys和一個與原來的值。所有排序操作並行處理這兩個數組,並對lo.keys中的值進行排序,並將lo.values中的元素串聯起來。