1つの簡単なタスク。 高速、美しい、またはきれいですか?

Python開発者の99%が何らかの方法でこの問題を解決したと思います。これは、有名な企業でPython Developerの求職者に提供する標準的なタスクセットの一部だからです。



#     .    ,    . #  ,        . #     ,      None. # ,    ,  .
      
      





好奇心のために、分析したソリューションのリストを示しました



コードの正確さを評価するために、単純な単体テストをスケッチしました。



オプション1



 import unittest def dict_from_keys(keys, values): res = dict() for num, key in enumerate(keys): try: res[key] = values[num] except IndexError: res[key] = None return res class DictFromKeysTestCase(unittest.TestCase): def test_dict_from_keys_more_keys(self): keys = range(1000) values = range(900) for _ in range(10 ** 5): result = dict_from_keys(keys, values) self.assertEqual(keys,result.keys()) def test_dict_from_keys_more_values(self): keys =range(900) values = range(1000) for _ in range(10 ** 5): result = dict_from_keys(keys, values) self.assertEqual(keys, result.keys())
      
      





ここで、最初に見つけた解決策を示しました。 単体テストを実行します。



 random1st@MacBook-Pro ~/P/untitled> python -m unittest dict_from_keys .. ---------------------------------------------------------------------- Ran 2 tests in 26.118s OK
      
      





すぐに気づいた最初の瞬間は何でしたか? dict()の使用は関数呼び出しであり、{}の使用は構文構造です。 辞書の初期化を置き換えます:



 random1st@MacBook-Pro ~/P/untitled> python -m unittest dict_from_keys .. ---------------------------------------------------------------------- Ran 2 tests in 25.828s OK
      
      





些細なことですが、素晴らしい。 エラーに起因する可能性がありますが。 次の選択肢から、私は血を求めて泣いたが、それでもここに持ってきた:



オプション2



 def dict_from_keys(keys, values): res = {} it = iter(values) nullValue = False for key in keys: try: res[key] = it.next() if not nullValue else None except StopIteration: nullValue = True res[key] = None return res
      
      





試験結果:



 random1st@MacBook-Pro ~/P/untitled> python -m unittest dict_from_keys .. ---------------------------------------------------------------------- Ran 2 tests in 33.312s OK
      
      





コメントはありません。



オプション3



次の解決策:



 def dict_from_keys(keys, values): return {key: None if idx>=len(values) else values[idx] for idx, key in enumerate(keys)}
      
      





試験結果:



 random1st@MacBook-Pro ~/P/untitled [1]> python -m unittest dict_from_keys .. ---------------------------------------------------------------------- Ran 2 tests in 26.797s OK
      
      





ご覧のとおり、大幅な高速化は達成されていません。 トピックの別のバリエーション:



オプション4



 def dict_from_keys(keys, values): return dict((len(keys) > len(values)) and map(None, keys, values) or zip(keys, values))
      
      





結果:



 random1st@MacBook-Pro ~/P/untitled> python -m unittest dict_from_keys .. ---------------------------------------------------------------------- Ran 2 tests in 20.600s OK
      
      





オプション5



 def dict_from_keys(keys, values): result = dict.fromkeys(keys, None) result.update(zip(keys, values)) return result
      
      





結果:



 random1st@MacBook-Pro ~/P/untitled [1]> python -m unittest dict_from_keys .. ---------------------------------------------------------------------- Ran 2 tests in 17.584s OK
      
      





組み込み関数の使用により、大幅な加速が期待されます。 さらに印象的な結果を達成することは可能ですか?



オプション6



 def dict_from_keys(keys, values): return dict(zip(keys, values + [None] * (len(keys) - len(values))))
      
      





結果:



 random1st@MacBook-Pro ~/P/untitled> python -m unittest dict_from_keys .. ---------------------------------------------------------------------- Ran 2 tests in 14.212s OK
      
      





さらに高速:



オプション7



 def dict_from_keys(keys, values): return dict(itertools.izip_longest(keys, values[:len(keys)]))
      
      





結果:



 random1st@MacBook-Pro ~/P/untitled> python -m unittest dict_from_keys .. ---------------------------------------------------------------------- Ran 2 tests in 10.190s OK
      
      





合理的な疑問は、このソリューションよりも高速に何かを取得できるかどうかということです。 当然、計算を高速化できない場合は、怠laにする必要があります。 このオプションの疑わしい点は明らかですが、現在はすべてコンテキストに依存しています。 特に、次のコードはそれ自体のテストに合格しますが、これはもはや完全なPython辞書ではありません。



オプション8



 class DataStructure(dict): def __init__(self, *args, **kwargs): super(DataStructure, self).__init__(*args, **kwargs) self._values = None self._keys = None @classmethod def dict_from_keys_values(cls, keys, values): obj = cls() obj._values = values[:len(keys)] obj._keys = keys return obj def __getitem__(self, key): try: return super(DataStructure, self).__getitem__(key) except KeyError: try: idx = self._keys.index(key) self._keys.pop(idx) super(DataStructure, self).__setitem__( key, self._values.pop(idx) ) except ValueError: raise KeyError except IndexError: super(DataStructure, self).__setitem__(key, None) return super(DataStructure, self).__getitem__(key) def keys(self): for k in self._keys: yield k for k in super(DataStructure, self).keys(): yield k
      
      





 random1st@MacBook-Pro ~/P/untitled [1]> python -m unittest dict_from_keys .. ---------------------------------------------------------------------- Ran 2 tests in 1.219s OK
      
      





個人的には、読みやすさと速度の両方の点で、6番目のオプションに最も感銘を受けたと付け加えます。

PSもう一度、私は絶対に役に立たない記事の解説者の数に感銘を受けました。



All Articles