Hồi còn làm việc với CakePHP, mình rất ấn tượng với class [Hash](https://book.cakephp.org/2.0/ja/core-utility-libraries/hash.html) của nó. Gần đây khi chuyển sang project dùng Python, nhiều khi thao tác với list, tuple và dict của Python lại thấy nhớ vài function trong class Hash của CakePHP. Vậy là quyết định clone lại 2 function sử dụng nhiều nhất là `Hash::get` và `Hash::insert` của CakePHP chuyển sang Python. Tất nhiên do đặc thù của list, tuple và dict trong Python check type nghiêm ngặt hơn trong PHP nên chưa thể clone được 100% chức năng của `Hash::get` và `Hash::insert` sang Python. Tuy nhiên code hiện tại vẫn phục vụ được yêu cầu cơ bản. Source code được post ở dưới cuối. Ở đây sẽ ghi vài sample về cách sử dụng 2 hàm này. ### Cách dùng: #### hash.get(): ~~~ python import hash aDict = { 'a': { 'b': [0, 1, {'c': 'foo'}], 'd': {'e': 'bar'}, } } print hash.get(aDict, 'a.b') # => [0, 1, {'c': 'foo'}] print hash.get(aDict, 'a.b.0') # => 0 print hash.get(aDict, 'a.b.2.c') # => 'foo' print hash.get(aDict, 'a.d.e') # => 'bar' print hash.get(aDict, 'f') # => None ~~~ #### hash.insert(): ~~~ python import hash aDict = { 'a': { 'b': [0, 1, 2], 'c': {'d': 'foo'}, } } print hash.insert(aDict, 'a.e', 'Hello') # 'a': { # 'b': [0, 1, 2], # 'c': {'d': 'foo'}, # 'e': 'Hello' # } print hash.insert(aDict, 'a.b', 'Hello') # 'a': { # 'b': [0, 1, 2, 'Hello'], # 'c': {'d': 'foo'} # } print hash.insert(aDict, 'a.c', {'e': 'Hello'}) # 'a': { # 'b': [0, 1, 2], # 'c': {'d': 'foo', 'e': 'Hello'} # } print hash.insert(aDict, 'a.c', 'Hello') # 'a': { # 'b': [0, 1, 2], # 'c': 'Hello' # } print hash.insert(aDict, 'e.f', 'None') # 'a': { # 'b': [0, 1, 2], # 'c': {'d': 'foo'} # }, # 'e': { # 'f': None # } ~~~ > ※ Chú ý: Parameter `aDict` sẽ bị thay đổi khi sử dụng `hash.insert`. Nếu không muốn làm thay đổi `aDict` thì cần copy `aDict` trước khi truyền vào `hash.insert`. ### Source code: ~~~ python # coding: utf-8 # # hash.py # # Utility functions for handling dict, list, tuple variable # # @author NVB # @copyright 2017 NVB # @license MIT # def get(arr, path): ''' Get value of item provided in path from a list, tuple or dict. ''' if isinstance(path, list): parts = path elif isinstance(path, str): parts = path.split('.') else: raise TypeError('type of path must be str or dict.') part = parts.pop(0) if isinstance(arr, (list, tuple)): try: part = int(part) except ValueError: return None if part > len(arr) - 1 or part < -len(arr): return None elif isinstance(arr, dict): if part not in arr: return None else: return None if len(parts) > 0: return get(arr[part], parts) else: return arr[part] def insert(aDict, path, val): ''' Insert val into aDict follow by path. ''' if not isinstance(aDict, dict): raise TypeError('type of item provided in path must be dict: %s.' % path) if isinstance(path, list): parts = path elif isinstance(path, str): parts = path.split('.') else: raise TypeError('type of path must be str or dict.') part = parts.pop(0) if len(parts) > 0: if part not in aDict: aDict[part] = {} if isinstance(aDict[part], dict): aDict[part].update(insert(aDict[part], parts, val)) else: aDict[part] = insert(aDict[part], parts, val) else: if part not in aDict: aDict[part] = val elif isinstance(aDict[part], list): aDict[part].append(val) elif isinstance(aDict[part], dict) and isinstance(val, dict): aDict[part].update(val) else: aDict[part] = val return aDict ~~~