├── README.md ├── contents.md ├── draft ├── finput.py └── 關於 class variable.md ├── proof └── proof1.py ├── questions ├── algorithm │ ├── subset_sum_problem.md │ ├── 一個數最後一位是6,移動到首位是原來數的三倍,求這個數.md │ ├── 使用字串為整數編碼.md │ ├── 分群問題.md │ ├── 字串list排序(七橋問題).md │ ├── 找出所有組合-笛卡爾積問題.md │ └── 燈泡開關問題.md ├── bs │ ├── python爬蟲beautifulsoup string抓取問題.md │ └── 刪除xml文件的指定標籤.md ├── collections │ ├── Top_K_Frequent_Elements.md │ └── 會php和python的大神進來幫忙轉換一段代碼.md ├── csv │ ├── csv模塊生成CSV文件問題(0字頭數字缺失與漢字亂碼).md │ ├── 如何用python刪除csv文件中的某一列.md │ └── 操作csv文件寫入順序不對.md ├── data_mining │ └── python3中有聚類(主要是k-means)的函數或者模塊嗎.md ├── django │ ├── Django CSRF verification failed 問題.md │ ├── django如何一個url綁定多個視圖.md │ ├── django模版中變量引用變量被當作字符串處理而不是變量值.md │ └── 網頁根目錄改成子目錄後文件如何調用.md ├── dunder │ └── 自己寫的數據類型使用print無法輸出每個元素.md ├── error │ ├── Python如何優雅的處理大量異常語句.md │ └── python如何捕獲中斷.md ├── eval │ └── 對Python語法字串求值.md ├── file │ ├── Python如何向文件最開始插入一個字串.md │ ├── Python如何實現並行查找關鍵字所在的行.md │ ├── Python處理一個求和運算.md │ ├── Python處理文本信息.md │ ├── 使用Python如何按行數拆分文件.md │ ├── 怎樣合併文檔中有重複部分的行.md │ ├── 文本格式轉換代碼優化.md │ └── 用Python實現類似grep的功能.md ├── fp │ └── sum函數中可以使用條件語句嗎.md ├── ide │ └── Python能否在保存程序變量情況下啟動控制台.md ├── if │ └── if 語句的 and or 運算.md ├── import │ ├── deepcopy無法import.md │ └── 通過哪個函數能查看Python文件中匯入了哪些模組.md ├── iteration │ ├── Python 如何合併 list of lists.md │ ├── Python的list有沒有類似js的find方法.md │ ├── python中既然生成器表達式比列表解析快, 那為什麼不全部使用生成器表達式.md │ ├── 一個求質(素)數的編程題.md │ ├── 如何將列表中的元組整個迭代出來.md │ ├── 如何從一個複雜的結構中優雅的提取出一列數據.md │ └── 如何把tuple轉成dictionary.md ├── jinja │ └── jinja2_macro_caller.md ├── json │ ├── Python如何合併批量輸出json.md │ ├── Python如何讀取json中的數據.md │ ├── 不定深層Json剖析.md │ ├── 假定有json數據多條記錄,如何根據KEY的值返回一條記錄.md │ └── 為什麼json的key只能是string.md ├── len │ └── 怎麼判斷函數或方法多次使用是否需要定義臨時變量.md ├── list │ ├── list使用append最後為什麼會把前面的元素都修改掉.md │ ├── python列表取交集.md │ ├── python生成特定列表格式.md │ └── 遍歷二維串列最外圈.md ├── math │ └── Python怎麼通過input獲取矩陣.md ├── object │ ├── Python 3.x 實例方法的__func__屬性.md │ ├── Python多重繼承屬性問題.md │ ├── Python如何通過類方法創建實例方法.md │ └── Python的staticmethod在什麼情況下用.md ├── others │ └── 中文按照拼音排序.md ├── pip │ ├── pip無法在安裝pyinstaller.md │ └── python代碼怎麼打包.md ├── scope │ └── 為什麼這兩段 python lambda 出現不同的結果.md ├── sort │ ├── Python排序問題.md │ └── sorted函數中key參數的作用原理.md ├── standard_lib │ ├── Python timeit測量代碼運行時間問題.md │ ├── Python 獲取文件路徑及文件目錄(__file__ 的使用方法).md │ ├── Python日期的遞增問題.md │ ├── os.mkdir和os.makedirs的區別.md │ ├── tk程序中出現問題.md │ ├── 一個結構化顯示文件的腳本,迭代是否有問題.md │ ├── 一個需要傳入參數的python程序如何封裝成可執行文件.md │ ├── 交換兩個shelve objects.md │ ├── 如何在python raw_input中使用tab鍵補全.md │ └── 計算時間差.md ├── star │ └── 關於python*和**的問題.md ├── string │ ├── Python中字串的bitwise or怎麼實現.md │ ├── 如何讓列表所有元素首字母變大寫.md │ ├── 給定一個字串,回傳所有的可能組合.md │ └── 轉換一個字串為浮點數會報錯.md ├── styles │ ├── ebook.css │ └── website.css └── virtualenv │ └── 如何在ubuntu14.04安裝python3.5.md ├── templates └── basic.md └── test └── data1 /README.md: -------------------------------------------------------------------------------- 1 | # Python-QA 2 | 3 | 16年5月份左右,閒來無事開始在技術論壇回答問題,回答久了,也就累積了一些東西. 4 | 5 | 這個 repository 收集了一些我自己感覺比較有趣或實用的問題,每篇都會附上原發問者的問題以及我的回答,問題可能經過潤飾,但我盡量維持原貌,回答的部分也多是個人見解,只是希望做個整理並且與有興趣的人分享. 6 | 7 | 由於問題滿雜的,我只能憑我的直覺將之分類歸檔,目前一共收錄了 81 個問題,大家有興趣的可以參閱[目錄](contents.md). 8 | 9 | 如果你有任何的意見或想要討論,都歡迎你開個 issue 來討論唷! 10 | 11 | 如果你覺得寫得不錯或對你有所啟發,請幫我 **Star** 一下,要關注請 **Watch**,也歡迎大家利用各種平台分享,謝謝你們. 12 | 13 | dokelung @ 2016/07/01, Taipei 14 | -------------------------------------------------------------------------------- /contents.md: -------------------------------------------------------------------------------- 1 | ## Python-QA 目錄 2 | 3 | ## 內建型態與 collections 容器 4 | 5 | * string 6 | * [Python 中字串的 bitwise or 怎麼實現?](questions/string/Python中字串的bitwise%20or怎麼實現.md) 7 | * [給定一個字串,回傳所有的可能組合](questions/string/給定一個字串,回傳所有的可能組合.md) 8 | * [如何讓列表所有元素首字母變大寫](questions/string/如何讓列表所有元素首字母變大寫.md) 9 | * [轉換一個字串為浮點數會報錯](questions/string/轉換一個字串為浮點數會報錯.md) 10 | 11 | * list 12 | * [遍歷二維串列最外圈](questions/list/遍歷二維串列最外圈.md) 13 | * [list 使用 append 最後為什麼會把前面的元素都修改掉](questions/list/list使用append最後為什麼會把前面的元素都修改掉.md) 14 | * [python 列表取交集](questions/list/python列表取交集.md) 15 |  * [python 生成特定列表格式](questions/list/python生成特定列表格式.md) 16 | 17 | * collections 18 | * [會 php 和 Python 的大神進來幫忙轉換一段代碼](questions/collections/會php和python的大神進來幫忙轉換一段代碼.md) 19 | * [Top K Frequent Elements](questions/collections/Top_K_Frequent_Elements.md) 20 | 21 | ## 函式 22 | 23 | * star expression 24 | * [關於 python * 和 ** 的問題](questions/star/關於python*和**的問題.md) 25 | 26 | * variable scope(global/local/nonlocal/closure) 27 | * [為什麼這兩段 python lambda 出現不同的結果](questions/scope/為什麼這兩段%20python%20lambda%20出現不同的結果.md) 28 | 29 | ## 內建函式 30 | 31 | * len 32 | * [怎麼判斷函數或方法多次使用是否需要定義臨時變量](questions/len/怎麼判斷函數或方法多次使用是否需要定義臨時變量.md) 33 | 34 | * sort 35 | * [Python排序問題](questions/sort/Python排序問題.md) 36 | * [sorted函數中key參數的作用原理](questions/sort/sorted函數中key參數的作用原理.md) 37 | 38 | * eval 39 | * [對 Python 語法字串求值](questions/eval/對Python語法字串求值.md) 40 | 41 | ## 控制流程與迭代 42 | 43 | * if(if/elif/else) 44 | * [if 語句的 and or 運算](questions/if/if%20語句的%20and%20or%20運算.md) 45 | 46 | * iteration(iterable/iterater/comprehension/generator/generator expression) 47 | * [如何將列表中的元組整個迭代出](questions/iteration/如何將列表中的元組整個迭代出來.md) 48 | * [一個求質(素)數的編程題](questions/iteration/一個求質(素)數的編程題.md) 49 | * [Python 如何合併 list of lists](questions/iteration/Python%如何合併%20list%20of%20lists.md)(待補充) 50 | * [如何從一個複雜的結構中優雅的提取出一列數據](questions/iteration/如何從一個複雜的結構中優雅的提取出一列數據.md) 51 | * [如何把 tuple 轉成 dictionary](questions/iteration/如何把tuple轉成dictionary.md) 52 | * [Python 的 list 有沒有類似 js 的 find 方法](questions/iteration/Python的list有沒有類似js的find方法.md) 53 | * [python 中既然生成器表達式比列表解析快, 那為什麼不全部使用生成器表達式](questions/iteration/python中既然生成器表達式比列表解析快,%20那為什麼不全部使用生成器表達式.md) 54 | 55 | * functional programming style(map/filter/reduce) 56 | * [sum 函數中可以使用條件語句嗎?](questions/fp/sum函數中可以使用條件語句嗎.md) 57 | 58 | * error handling(exception) 59 | * [Python 如何優雅的處理大量異常語句](questions/error/Python如何優雅的處理大量異常語句.md) 60 | * [python 如何捕獲中斷](questions/error/python如何捕獲中斷.md) 61 | 62 | ## 物件導向程式設計 63 | 64 | * dunder(magic function) 65 | * [自己寫的數據類型使用print無法輸出每個元素](questions/dunder/自己寫的數據類型使用print無法輸出每個元素.md) 66 | 67 | * object(class/object) 68 | * [Python 3.x 實例方法的`__func__`屬性](questions/object/Python%203.x%20實例方法的__func__屬性.md) 69 | * [Python 如何通過類方法創建實例方法](questions/object/Python如何通過類方法創建實例方法.md) 70 | * [Python 的 staticmethod 在什麼情況下用](questions/object/Python的staticmethod在什麼情況下用.md) 71 | * [Python 多重繼承屬性問題](questions/object/Python多重繼承屬性問題.md) 72 | 73 | ## 檔案與資料處理 74 | 75 | * file 76 | * [怎樣合併文檔中有重複部分的行](questions/file/怎樣合併文檔中有重複部分的行.md) 77 | * [Python 處理文本信息](questions/file/Python處理文本信息.md) 78 | * [Python 如何實現並行查找關鍵字所在的行](questions/file/Python如何實現並行查找關鍵字所在的行.md) 79 | * [文本格式轉換代碼優化](questions/file/文本格式轉換代碼優化.md) 80 | * [使用 Python 如何按行數拆分文件](questions/file/使用Python如何按行數拆分文件.md) 81 | * [Python 處理一個求和運算](questions/file/Python處理一個求和運算.md) 82 | * [Python 如何向文件最開始插入一個字串](questions/file/Python如何向文件最開始插入一個字串.md)(待補充) 83 | * [用 Python 實現類似 grep 的功能](questions/file/用Python實現類似grep的功能.md) 84 | 85 | * json 86 | * [為什麼 json 的 key 只能是 string?](questions/json/為什麼json的key只能是string.md) 87 | * [Python 如何合併批量輸出 json](questions/json/Python如何合併批量輸出json.md) 88 | * [Python 如何讀取 json 中的數據](questions/json/Python如何讀取json中的數據.md) 89 | * [不定深層 Json 剖析](questions/json/不定深層Json剖析.md) 90 | * [假定有 json 數據多條記錄,如何根據 KEY 的值返回一條記錄](questions/json/假定有json數據多條記錄,如何根據KEY的值返回一條記錄.md) 91 | 92 | * cvs 93 | * [csv 模塊生成 CSV 文件問題(0字頭數字缺失與漢字亂碼)](questions/csv/csv模塊生成CSV文件問題(0字頭數字缺失與漢字亂碼).md) 94 | * [操作 csv 文件寫入順序不對](questions/csv/操作csv文件寫入順序不對.md) 95 | * [如何用 python 刪除 csv 文件中的某一列](questions/csv/如何用python刪除csv文件中的某一列.md) 96 | 97 | ## 模組與套件 98 | 99 | * import 100 | * [通過哪個函數能查看 Python 文件中匯入了哪些模組](questions/import/通過哪個函數能查看Python文件中匯入了哪些模組.md) 101 | * [deepcopy 無法 import](questions/import/deepcopy無法import.md) 102 | 103 | * standard library 104 | * [os.mkdir 和 os.makedirs 的區別](questions/standard_lib/os.mkdir和os.makedirs的區別.md) 105 | * [tk 程序中出現問題](questions/standard_lib/tk程序中出現問題.md) 106 | * [計算時間差](questions/standard_lib/計算時間差.md) 107 | * [Python timeit 測量代碼運行時間問題](questions/standard_lib/Python%20timeit測量代碼運行時間問題.md) 108 | * [Python 日期的遞增問題](questions/standard_lib/Python日期的遞增問題.md) 109 | * [一個需要傳入參數的 python 程序如何封裝成可執行文件](questions/standard_lib/一個需要傳入參數的python程序如何封裝成可執行文件.md) 110 | * [一個結構化顯示文件的腳本,迭代是否有問題](questions/standard_lib/一個結構化顯示文件的腳本,迭代是否有問題.md) 111 | * [如何在 python raw_input 中使用 tab 鍵補全](questions/standard_lib/如何在python%20raw_input中使用tab鍵補全.md) 112 | * [交換兩個 shelve objects](questions/standard_lib/交換兩個shelve%20objects.md) 113 | * [Python 獲取文件路徑及文件目錄(`__file__` 的使用方法)](questions/standard_lib/Python%20獲取文件路徑及文件目錄(__file__%20的使用方法).md) 114 | 115 | * pip 116 | * [pip 無法在安裝 pyinstaller](questions/pip/pip無法在安裝pyinstaller.md) 117 | * [Python 代碼怎麼打包](questions/pip/python代碼怎麼打包.md) 118 | 119 | * others 120 | * [中文按照拼音排序](questions/others/中文按照拼音排序.md) 121 | 122 | ## WEB 123 | 124 | * django 125 | * [django 如何一個 url 綁定多個視圖](questions/django/django如何一個url綁定多個視圖.md) 126 | * [django 模版中變量引用變量被當作字符串處理而不是變量值](questions/django/django模版中變量引用變量被當作字符串處理而不是變量值.md) 127 | * [網頁根目錄改成子目錄後文件如何調用](questions/django/網頁根目錄改成子目錄後文件如何調用.md) 128 | * [Django CSRF verification failed 問題](questions/django/Django%20CSRF%20verification%20failed%20問題.md) 129 | 130 | * flask/jinja 131 | * [jinja2 macro caller](questions/jinja/jinja2_macro_caller.md) 132 | 133 | ## 爬蟲 134 | 135 | * beautiful soup 136 | * [刪除 xml 文件的指定標籤](questions/bs/刪除xml文件的指定標籤.md) 137 | * [Python 爬蟲 beautifulsoup string 抓取問題](questions/bs/python爬蟲beautifulsoup%20string抓取問題.md) 138 | 139 | ## 演算法與科學計算 140 | 141 | * algorithm 142 | * [Subset-Sum Problem](questions/algorithm/subset_sum_problem.md) 143 | * [字串 list 排序(七橋問題)](questions/algorithm/字串list排序(七橋問題).md) 144 | * [使用字串為整數編碼](questions/algorithm/使用字串為整數編碼.md) 145 | * [分群問題](questions/algorithm/分群問題.md) 146 | * [找出所有組合-笛卡爾積問題](questions/algorithm/找出所有組合-笛卡爾積問題.md) 147 | * [燈泡開關問題](questions/algorithm/燈泡開關問題.md) 148 | * [一個數最後一位是 6,移動到首位是原來數的三倍,求這個數](questions/algorithm/一個數最後一位是6,移動到首位是原來數的三倍,求這個數.md) 149 | 150 | * math 151 | * [Python 怎麼通過 input 獲取矩陣](questions/math/Python怎麼通過input獲取矩陣.md) 152 | 153 | ## 大數據與機器學習 154 | 155 | * data mining 156 | * [python3 中有聚類(主要是k-means)的函數或者模塊嗎](questions/data_mining/python3中有聚類(主要是k-means)%20的函數或者模塊嗎.md) 157 | 158 | ## Python 實作與開發環境 159 | 160 | * IDE (集成開發環境) 161 | * [Python 能否在保存程序變量情況下啟動控制台](questions/ide/Python能否在保存程序變量情況下啟動控制台.md) 162 | 163 | * virtualenv 164 | * [如何在 ubuntu14.04 安裝 python3.5](questions/virtualenv/如何在ubuntu14.04安裝python3.5.md) 165 | -------------------------------------------------------------------------------- /draft/finput.py: -------------------------------------------------------------------------------- 1 | # author: dokelung 2 | 3 | import re 4 | from ast import literal_eval 5 | from functools import partial 6 | 7 | 8 | class InputDoesNotMatchFStr(Exception): pass 9 | class TypeConvertError(Exception): pass 10 | class InputCountNotInRange(Exception): pass 11 | 12 | 13 | FORMAT_SPECIFIER = { 14 | '%a': literal_eval, 15 | '%d': int, 16 | '%f': float, 17 | '%o': partial(int, base=8), 18 | '%s': str, 19 | '%x': partial(int, base=16), 20 | } 21 | 22 | 23 | def finput(prompt='', fstr='%s', expand_fsp=None, 24 | whitespace=False, 25 | escape_parenthesis=True): 26 | """format input 27 | """ 28 | fsp = FORMAT_SPECIFIER 29 | if expand_fsp is not None: 30 | fsp.update(expand_fsp) 31 | if escape_parenthesis: 32 | rstr = fstr.replace('(', '\(').replace(')', '\)') 33 | else: 34 | rstr = fstr 35 | regex = '(.+)' if whitespace else '(\S+)' 36 | for sp, typ in fsp.items(): 37 | rstr = rstr.replace(sp, regex) 38 | types = [] 39 | for idx, c in enumerate(fstr): 40 | pattern = fstr[idx:idx+2] 41 | if pattern in fsp: 42 | types.append(fsp[pattern]) 43 | pure_input = input(prompt) 44 | mobj = re.match(rstr, pure_input) 45 | if mobj: 46 | try: 47 | return tuple(typ(value) for value, typ in zip(mobj.groups(), types)) 48 | except Exception as err: 49 | raise TypeConvertError(err) 50 | else: 51 | msg = 'input does not match format string "{}"' 52 | raise InputDoesNotMatchFStr(msg.format(fstr)) 53 | 54 | 55 | def minput(prompt='', typ=str, sep=None, min=1, max=100000): 56 | """multiple input 57 | """ 58 | pure_input = input(prompt) 59 | try: 60 | if sep is None: 61 | values = tuple(typ(item) for item in pure_input.split()) 62 | else: 63 | values = tuple(typ(item) for item in pure_input.split(sep)) 64 | except Exception as err: 65 | raise TypeConvertError(err) 66 | if len(values) < min or len(values) > max: 67 | msg = 'input count {} is not in range [{}, {}]' 68 | raise InputCountNotInRange(msg.format(len(values), min, max)) 69 | return values 70 | 71 | 72 | if __name__ == '__main__': 73 | #res = finput('>>> ', fstr='%s, *%d, *%f') 74 | #print(res) 75 | res = minput('>>> ', typ=int, min=1, max=3) 76 | print(res) 77 | -------------------------------------------------------------------------------- /draft/關於 class variable.md: -------------------------------------------------------------------------------- 1 | 首先你寫在 class 裡面但不在 method 裡面的 variable 是 class variable 2 | 3 | 這個 variable 對於該類別及其子類別的類別和實體而言都只有一份, 看下面這個例子: 4 | 5 | ```python 6 | class A: 7 | _dict = {} 8 | def __init__(self): 9 | self._dict.update({'a':'a'}) 10 | 11 | class B(A): 12 | 13 | def __init__(self): 14 | self._dict.update({'b':'b'}) 15 | 16 | if __name__=='__main__': 17 | a = A() 18 | b = B() 19 | print(a._dict) 20 | print(b._dict) 21 | print(A._dict) 22 | print(B._dict) 23 | ``` 24 | 25 | ``` 26 | {'b': 'b', 'a': 'a'} 27 | {'b': 'b', 'a': 'a'} 28 | {'b': 'b', 'a': 'a'} 29 | {'b': 'b', 'a': 'a'} 30 | ``` 31 | 32 | 這裡你看到的所有 `_dict` 都參考到同一個物件 33 | 34 | 但是下面這個情況就不太一樣了: 35 | 36 | ```python 37 | class A: 38 | _dict = {} 39 | def __init__(self): 40 | self._dict = {} 41 | self._dict.update({'a':'a'}) 42 | 43 | class B(A): 44 | 45 | def __init__(self): 46 | self._dict.update({'b':'b'}) 47 | 48 | class C(A): 49 | 50 | def __init__(self): 51 | super().__init__() 52 | self._dict.update({'c':'c'}) 53 | 54 | if __name__=='__main__': 55 | a = A() 56 | b = B() 57 | c = C() 58 | print(a._dict) 59 | print(b._dict) 60 | print(c._dict) 61 | print(A._dict) 62 | print(B._dict) 63 | print(C._dict) 64 | ``` 65 | 66 | ``` 67 | {'a': 'a'} 68 | {'b': 'b'} 69 | {'c': 'c', 'a': 'a'} 70 | {'b': 'b'} 71 | {'b': 'b'} 72 | {'b': 'b'} 73 | ``` 74 | 75 | 咦?! 怎麼變成這樣了呢? 這邊如果搞懂的話就全盤皆通了: 76 | 77 | 首先 A 及其子類別都共有一個 class variable, 叫做 `_dict` 78 | 79 | 當我們初始化 a 的時候, `self._dict = {}` 會讓 a 裡面新產生一個變數叫做 `_dict`, 因為這次 `self._dict` 出現在等號左邊。注意! 這裡我們已經有兩個不同的東西了, 一個是 class variable `_dict`, 另一個是 instance variable `_dict`, 從此以後, a 裡面拿 `self._dict` 的時候就都是拿到 instance variable 了! 80 | 81 | 接著看 b, b 並沒有讓 variable 出現在等號左邊, 所以沒有建立新的變數, 現在 b 中的 `self._dict` 仍然指涉 class variable `_dict` 82 | 83 | c 的情況就比較特別了, 藉由 `super` 他呼叫了 A 的 `__init__`, 上面說過了, 這會讓 c 中新建立一個 instance variable `_dict`, 這個變數因為 `A.__init__` 和 `C.__init__`, 所以會有兩個鍵值對 84 | 85 | 86 | 最後 `A._dict`, `B._dict` 和 `C._dict` 就很容易理解了, 他們都是參考到同一個 class variable, 所以值都一樣。 87 | 88 | ### 小結 89 | 90 | 讓我們來整理一下, 這邊一共會有 3 個 `_dict`: 91 | 92 | 1. class variable `_dict` 93 | 2. instance variable of a 94 | 3. instance variable of c 95 | -------------------------------------------------------------------------------- /proof/proof1.py: -------------------------------------------------------------------------------- 1 | """ 2 | prove this repository is a python project 3 | """ 4 | 5 | def prove(): 6 | print 'prove!' 7 | -------------------------------------------------------------------------------- /questions/algorithm/subset_sum_problem.md: -------------------------------------------------------------------------------- 1 | # Subset-Sum Problem 2 | 3 | ## 問題 4 | 5 | 給定一個值, 如100,給定一個 list,從 list 中挑選出 N 個元素,這 N 個元素相加也是 100,得到一種結果就行. 6 | 7 | 舉例: 給定一個 list: 8 | 9 | ```python 10 | lst = [ 99.1 , 92.2 , 60 , 50 , 11 | 49.5 , 45.7 , 25.1 , 20 , 12 | 17.4 , 13 , 10 , 7 , 2.1 , 2 , 1 ] 13 | ``` 14 | 15 | 找到和為100的數組元素: 16 | 17 | ``` 18 | [ 60 , 20 , 10 , 7 , 2 , 1 ] 19 | ``` 20 | 21 | 問題出自 [segmentfault](https://segmentfault.com/q/1010000005696393/a-1020000005702007), by [輕逐微風](https://segmentfault.com/u/hare) 22 | 23 | ## 回答 24 | 25 | 來個 Python 版的: 26 | 27 | ```python 28 | def subsetsum(elements, target): 29 | if target==0: 30 | return True, [] 31 | elif not elements or target < 0: 32 | return False, None 33 | 34 | result, subset = subsetsum(elements[:-1], target-elements[-1]) 35 | return (True, subset + [elements[-1]]) if result else subsetsum(elements[:-1], target) 36 | ``` 37 | 38 | 思路很簡單,當我要問 `elements` 是否能加出 `target` 時,只有兩種可能: 39 | 40 | 1. 我要使用 `element[-1]` 才能加出 `target` -> 我要能夠使用 `elements[:-1]` 加出 `target-elements[-1]` 才行 41 | 2. 我不需要使用 `element[-1]` 就能加出 `target` -> 我要能夠使用 `elements[:-1]` 加出 `target` 才行 42 | 43 | boundary condition 是: 44 | 45 | 1. 當 `target` 為 `0` 時,代表我什麼都不用就能加出來,所以 `return True, []` 46 | 2. 當 `elements` 為空或是 `target` 為負值時,代表永遠都加不出來了,所以 `return False, None` 47 | 48 | **測試**: 49 | 50 | ```python 51 | elements = [99.1, 92.2, 60, 50, 49.5, 45.7, 25.1, 20, 17.4, 13, 10, 7, 2.1, 2, 1] 52 | target = 100 53 | result, subset = subsetsum(elements, target) 54 | print(result, subset) 55 | ``` 56 | 57 | **結果**: 58 | 59 | ```python 60 | True [60, 20, 10, 7, 2, 1] 61 | ``` 62 | 63 | ### 衍生問題 64 | 65 | 題外話,看到這個題目覺得超熟悉,如果還要考慮到解的速度等等會更有趣。 66 | 67 | 曾經做過這方面的研究,我提出一個變形的問題,大家可以思考看看: 68 | 69 | 70 | >我們今天給定一個整數(代表負數也 ok )的 多重集 (多重集就是一個集合,但是允許元素重複出現),叫做 ` elements`,在給定另外一個整數的 多重集 叫做 `targets`,試問是否存在若干個 子多重集,每個 子多重集 的元素和恰好有一個在 `targets` 中對應的 target。 71 | 72 | 73 | 定義看不懂沒差,我舉個例子: 74 | 75 | ``` 76 | elements = (1,4,6,4,1) 77 | targets = (5,10,1) 78 | ``` 79 | 80 | 這個例子是有解的: 81 | 82 | ``` 83 | (1,4) -> 5 84 | (4,6) -> 10 85 | (1) -> 1 86 | ``` 87 | 88 | 注意,每個在 `elements` 中的元素只能被使用一次! 89 | -------------------------------------------------------------------------------- /questions/algorithm/一個數最後一位是6,移動到首位是原來數的三倍,求這個數.md: -------------------------------------------------------------------------------- 1 | # 一個數最後一位是6,移動到首位是原來數的三倍,求這個數 2 | 3 | ## 問題 4 | 5 | 題目:一個數最後一位是6,移動到首位是原來數的三倍,求這個數 6 | 要求:速度最優 7 | 8 | 大神們快來踴躍探討~~~ 9 | 10 | 問題出自 [segmentfault](https://segmentfault.com/q/1010000006135723/a-1020000006139815), by [prolifes](https://segmentfault.com/u/prolifes) 11 | 12 | ## 回答 13 | 14 | ### 各路大神的回答 15 | 16 | #### [citaret](https://segmentfault.com/u/citaret) 的回答 17 | 18 | 先贴上一版: 19 | 20 | ```python 21 | x = 6 22 | xs = [] 23 | while True: 24 | xs.append(x // 3) 25 | x = x % 3 * 10 + x // 3 26 | if x == 6: 27 | print ''.join(str(x) for x in xs) 28 | break 29 | ``` 30 | 31 | 输出: 32 | 33 | ``` 34 | 2068965517241379310344827586 35 | ``` 36 | 37 | 原理是手算,把每次得到的商的末位补到被除数的最后,然后继续除法,直到末位为6,且余数为0停止。 38 | 39 | #### [hsfzxjy](https://segmentfault.com/u/hsfzxjy) 的回答 40 | 41 | 由於數學公式的使用請大家前往問題原出處查看 [回答](https://segmentfault.com/q/1010000006135723/a-1020000006139815) 42 | 43 | ### 我的回答 44 | 45 | 下午看到這題的時候就有了個想法, 手邊沒電腦只好等到現在... 46 | 47 | 後來看到 @citaret 的答案就發現剛好是反向的想法, 下面是我的作法: 48 | 49 | ```python 50 | x = 6 51 | last_carry = 0 52 | result = 6 53 | radix = 10 54 | 55 | while True: 56 | c1, x = divmod(x * 3, 10) 57 | c2, x = divmod(x + last_carry, 10) 58 | last_carry = c1 + c2 59 | 60 | if x==6 and last_carry==0: 61 | return result 62 | 63 | result += (x * radix) 64 | radix *= 10 65 | ``` 66 | 67 | 想法就剛好是反過來, 我一步一步地乘上去 68 | 69 | * 每次把 `x` 乘 3 70 | * 把個位數加上 `last_carry` 就是下次的 `x` 71 | * 把十位數的進位留下來當作下次的 `last_carry` 72 | * 做到 `x==6` 且無進位的時候 73 | 74 | 用圖來思考長這樣: 75 | 76 | ``` 77 | 0 <-- last carry 78 | \ 79 | 1 8 = 6 X 3 80 | \ (0 + 8 = 8) 81 | 2 4 = 8 X 3 82 | \ (1 + 4 = 5) 83 | 1 5 = 5 X 3 84 | ... 85 | ``` 86 | 87 | 用 `timeit` 稍微測了一下時間(各運行 1000000 次), 共測三次: 88 | 89 | ``` 90 | # first 91 | dokelung: 17.301649590954185 92 | citaret: 18.24915363173932 93 | 94 | # second 95 | dokelung: 19.257807812653482 96 | citaret: 17.994877750985324 97 | 98 | # third 99 | dokelung: 17.0617663115263 100 | citaret: 18.355605391785502 101 | ``` 102 | 103 | 時間看起來差不多, 不過我自認為代碼沒有很漂亮... 104 | -------------------------------------------------------------------------------- /questions/algorithm/使用字串為整數編碼.md: -------------------------------------------------------------------------------- 1 | # 使用字串為整數編碼 2 | 3 | ## 問題 4 | 5 | 網上看到類似的算法,不過實現是C++: 6 | 7 | ```c++ 8 | private static void alphaseq0(int n, String alphabet, StringBuffer buf) { 9 | int len = alphabet.length(); 10 | 11 | if (n >= len) { 12 | alphaseq0(n/len - 1,alphabet,buf); 13 | n = n % len; 14 | } 15 | 16 | buf.append(alphabet.charAt(n)); 17 | } 18 | ``` 19 | 20 | 本題的題意是:給定一個 **整數** `n` 和 **字元集** alphabet, return 一個對應的 string 21 | 22 | 而對應的規則如下: 23 | 24 | ``` 25 | (假設我們的字元集是英文字母AZ,也就是說這些字元是可以用來代表給定的整數的) 26 | 27 | 1 -> A 28 | 2 -> B 29 | 3 -> C 30 | ... 31 | 26 -> Z 32 | 27 -> AA 33 | ... 34 | 52 -> AZ 35 | ... 36 | m -> ZZA 37 | ... 38 | n -> ZZZ 39 | n+1 -> AAAA 40 | ``` 41 | 42 | 問題出自 [segmentfault](https://segmentfault.com/q/1010000005932045/a-1020000005933237), by [jiangbingo](https://segmentfault.com/u/jiangbingo) 43 | 44 | ## 回答 45 | 46 | 這題乍看簡單,其實後來仔細研究花了我不少時間(也許是我頭腦簡單...) 47 | 48 | 我馬上想到了 `itertools.product`,我可以很簡單地去生出足夠多的 string,再從中挑選第 `n` 個: 49 | 50 | ```python 51 | from itertools import product 52 | 53 | def get_alphaseqs(n, alphabet): 54 | """ get first n alphaseq """ 55 | results = [] 56 | for l in range(1, len(alphabet)+1): 57 | results.extend([''.join(p) for p in product(alphabet, repeat=l)]) 58 | if len(results) >= n: 59 | return results[:n] 60 | return None 61 | ``` 62 | 63 | `alphabet` 的變化數量如果不夠會 `return None` 64 | 65 | 所以如果我想要得到 `n=3000` 對應的 string; 66 | 67 | ```python 68 | alphabet = 'abcdefghijklmnopqrstuvwxyz' 69 | string = get_alphaseqs(3000, alphabet)[-1] 70 | ``` 71 | 72 | 但是這樣做的問題是: 非常耗時,理由很簡單,我產生了太多不需要的東西。對照樓主給的 C++ 範例,我應該直接生成對應的 string 就好 73 | 74 | 那該怎麼做呢? 我想起了進位轉換,這個問題不就是進位轉換的問題嗎? 75 | 76 | 比如說我們看一個 10 進位轉 16 進位的例子: 77 | 78 | ``` 79 | 10進位 分解 16進位 80 | --------------------------------------- 81 | 1 = 0*(16**1) + 1*(16**0) = 1 82 | 16 = 1*(16**1) + 0*(16**0) = 10 83 | 31 = 1*(16**1) + 15*(16**0) = 1F 84 | ``` 85 | 86 | 以 31 為例子,我們先除一次 16 得到餘數 15,就可以查出他的第一位符號 `F`,接著再除第二次得到餘數 `1` 也可以查出他的第二位符號 `1`。 87 | 88 | 我們現在的對應轉換問題不就是: 要求把 10 進位 ( 10 個符號 0-9 ) 轉成 26 進位 (26 個符號 A-Z) 嗎? 89 | 90 | 那還不簡單,仿照進位轉換的作法,我只要不停連除字符集的長度 `len(alphabet)` (`len(alphabet)` 進位) 就可以查詢的到每一位的對應符號了。 91 | 92 | 可惜問題沒有那麼簡單,大家有注意到嗎? 這個對應會從 1 開始而不是 0,少了這個 0 的對應,一切的規則似乎被打亂了許多,對於 26 進位而言,十進位的 `26` 應該是要進位了,但在這裡不是,`26` 對應的是單一的符號 `Z`,我們必須找出規則來處理除 26 餘 0 的狀況。 93 | 94 | 於是我開始觀察規則: 95 | 96 | ``` 97 | 十進位整數 1 2 ... 26 27 ... 52 98 | 對應字串 A B ... Z AA ... AZ 99 | 除以 26 之商 0 0 ... 1 1 ... 2 100 | 除以 26 之餘 1 2 ... 0 1 ... 0 101 | ``` 102 | 103 | 我們會發現: 104 | 105 | 1. 如果不是整除的話(餘數不為零),那規則跟進位轉換沒兩樣,可以直接用餘數查詢對應的符號,並且用 `商` 作被除數來做下一次的除法 106 | 2. 如果整除(餘數為零),我們則必須取最後一個符號,且下一次的除法要用 `(商-1)` 來當被除數做下一次的除法 107 | 108 | 根據這個規則我寫了兩個版本的 function,一個是如同 C++ 範例碼使用 recursive 的作法: 109 | 110 | ```python 111 | def int2alphaseq(n, alphbet): 112 | """ change int to alphaseq """ 113 | buf = '' 114 | if n==0: 115 | return buf 116 | if n >= len(alphbet): 117 | k = n % len(alphbet) 118 | if k==0: 119 | buf = int2alphaseq(n//len(alphbet)-1, alphbet) 120 | else: 121 | buf = int2alphaseq(n//len(alphbet), alphbet) 122 | n = k 123 | return buf + alphbet[n-1] 124 | ``` 125 | 126 | 另一個是用 iterate 的方式: 127 | 128 | ```python 129 | def int2alphaseqiter(n, alphbet): 130 | """ change int to alphaseq """ 131 | buf = '' 132 | while n >= len(alphabet): 133 | n, k = divmod(n, len(alphabet)) 134 | if k==0: 135 | n -= 1 136 | buf = alphabet[k-1] + buf 137 | if n==0: 138 | return buf 139 | else: 140 | return alphabet[n-1] + buf 141 | ``` 142 | 143 | 只要比較這三個 function 轉出來的 string 一不一樣就可以確認正確性。 144 | 145 | 如果樓主發現跟原題想要的不一樣或是大家有任何意見,歡迎在評論告訴我! 146 | -------------------------------------------------------------------------------- /questions/algorithm/分群問題.md: -------------------------------------------------------------------------------- 1 | # 分群問題 2 | 3 | 某一行中的字母,如果有在其他行也出現,則將他們合併為一行。 4 | 5 | 例如: 6 | 7 | ``` 8 | A B 9 | C A 10 | D C 11 | E F 12 | N G 13 | C N 14 | ``` 15 | 16 | 結果: 17 | 18 | 19 | ``` 20 | A B C D N G 21 | E F 22 | ``` 23 | 24 | ## 問題 25 | 26 | 問題出自 [segmentfault](https://segmentfault.com/q/1010000005979723/a-1020000005980272), by [biopython](https://segmentfault.com/u/biopython) 27 | 28 | ## 回答 29 | 30 | 這種 partition 的問題可以用 graph 來解決, 但我在這裡不想要用 graph, 所以改用兩個 dictionary 來替代: 31 | 32 | ```python 33 | char2id = {} 34 | id2charset = {} 35 | 36 | def getcharset(c): 37 | try: 38 | return id2charset[char2id[c]] 39 | except: 40 | return None 41 | 42 | def newcharset(chars): 43 | newset = set(chars) 44 | return newset 45 | 46 | def merge(charset1, charset2): 47 | if id(charset1)==id(charset2): 48 | return 49 | charset1 |= charset 50 | for c in charset2: 51 | char2id[c] = id(charset1) 52 | id2charset.pop(id(charset2)) 53 | 54 | with open('test') as reader: 55 | for line in reader: 56 | chars = line.strip().split() 57 | newset = newcharset(chars) 58 | id2charset[id(newset)] =newset 59 | 60 | for c in chars: 61 | charset = getcharset(c) 62 | if charset: 63 | merge(newset, charset) 64 | else: 65 | char2id[c] = id(newset) 66 | 67 | with open('report', 'w') as writer: 68 | for id, charset in id2charset.items(): 69 | print(' '.join(charset), file=writer) 70 | ``` 71 | 72 | 上述代碼可以完成工作不過只保證分群的正確而不保證順序。 73 | 74 | 測試資料 `test`: 75 | 76 | ``` 77 | A B 78 | C A 79 | D C 80 | E F 81 | N G 82 | C N 83 | X Y 84 | F P 85 | P Q 86 | X Z 87 | ``` 88 | 89 | 結果: 90 | 91 | ``` 92 | P E Q F 93 | X Y Z 94 | B D C G N A 95 | ``` 96 | 97 | 檢查正確性的方法很簡單, 因為這種合併的規則會導致最後每個字母只會在整個文件中出現一次(出現兩次以上代表有該 merge 的沒 merge) 98 | 99 | 下面是簡單地用 `Counter` 寫了一個檢查的 script: 100 | 101 | ```python 102 | from collections import Counter 103 | 104 | with open('report', 'r') as reader: 105 | ct = Counter() 106 | for line in reader: 107 | ct += Counter(line.strip().split()) 108 | 109 | for item, count in ct.most_common(): 110 | if count <= 1: 111 | break 112 | print(item, count) 113 | ``` 114 | -------------------------------------------------------------------------------- /questions/algorithm/字串list排序(七橋問題).md: -------------------------------------------------------------------------------- 1 | # 字串 list 組排序(七橋問題) 2 | 3 | ## 問題 4 | 5 | 給定字串 list,要求進行判斷是否存在以下序列 6 | 7 | 前一個字串末尾字元等於後一個字串首字元。 8 | 9 | 例如: 10 | 11 | ```python 12 | { "ab" , "de" , "bc" , "cd" } #此字串list滿足條件 13 | { "ab" , "bc" , "cd" , "fg" } #此字串list不滿足條件 14 | ``` 15 | 16 | 問題出自 [segmentfault](https://segmentfault.com/q/1010000005152540/a-1020000005595508), by [nbandroid](https://segmentfault.com/u/nbandroid) 17 | 18 | ## 回答 19 | 20 | [ted](https://segmentfault.com/u/ted) 大的算法非常完整,就算法的部份可以參考他的說法 21 | 22 | >其實就是七橋問題,把一個字符串視作從首字母到尾字母的有向鏈接,求特定字符串集合能不能一次不重複走完。從圖論的角度來看,由於可能存在不連通的子圖,所以不能簡單地用各個節點的度來判斷,但節點的度可以用來快速排除不符合的集合。 23 | >所以很簡單,如果集合中存在這樣的序列,必然是以下情況之一: 24 | > 25 | >循環。所有節點的出度等於入度; 26 | > 27 | >不循環。除了某兩個節點外,其他所有節點的出度等於入度。 28 | > 29 | >如果符合上面的條件,那麼還要跑一次 dfs 看能不能單路徑遍歷所有邊。 30 | >如果要排序,那就是 dfs 出來的那條路徑。 31 | > by ted 32 | 33 | 以下是我用 Python 實現的一個範例(`algo.py`): 34 | 35 | 首先是一個基礎的 `dfs` function,不需要做任何事前確認就能夠正確地辨識出 string list 是否符合條件: 36 | 37 | ```python 38 | def dfs(prestr, str_lst): 39 | for idx, string in enumerate(str_lst): 40 | if not prestr or string[0] == prestr[-1]: 41 | if len(str_lst)==1: 42 | return [string] 43 | # print(string, str_lst[:idx] + str_lst[idx+1:]) # 反註解掉此行用以觀察搜尋的過程 44 | result = dfs(string, str_lst[:idx] + str_lst[idx+1:]) 45 | if result: 46 | return [string] + result 47 | return None 48 | ``` 49 | 50 | 但是有了事前檢查可以減少不必要的 `dfs`。 51 | 52 | 用來作提前檢查的 `pre_check`: 53 | 54 | ```python 55 | import collections 56 | 57 | def pre_check(str_lst): 58 | 59 | heads = (string[0] for string in str_lst) 60 | tails = (string[-1] for string in str_lst) 61 | first_head = None 62 | last_tail = None 63 | 64 | head_counter = collections.Counter(heads) 65 | tail_counter = collections.Counter(tails) 66 | 67 | sub_counter = head_counter-tail_counter 68 | if len(sub_counter) > 1: 69 | return False, None, None 70 | elif len(sub_counter)==1: 71 | first_head = list(sub_counter) 72 | 73 | sub_counter = tail_counter-head_counter 74 | if len(sub_counter) > 1: 75 | return False 76 | elif len(sub_counter)==1: 77 | last_tail = list(sub_counter) 78 | 79 | return True, first_head, last_tail 80 | ``` 81 | 82 | 有作 `pre_check` 的 完整版 `check`: 83 | 84 | ```python 85 | def check(str_lst): 86 | result, first_head, last_tail = pre_check(str_lst) 87 | if result: 88 | if (first_head is None and last_tail is None) or (first_head and last_tail): 89 | return dfs(None, str_lst) 90 | else: 91 | return None 92 | else: 93 | return None 94 | ``` 95 | 96 | 測試結果: 97 | 98 | ```python 99 | >>> from algo import dfs, check 100 | >>> s1 = ["ab", "de", "bc", "cd"] 101 | >>> dfs(None, s1) 102 | ['ab', 'bc', 'cd', 'de'] 103 | >>> check(s1) 104 | ['ab', 'bc', 'cd', 'de'] 105 | >>> s2 = ["ab","bc","cd","fg"] 106 | >>> dfs(None, s2) # 回傳 None 代表 s2 並不符合條件 107 | >>> check(s2) # 回傳 None 代表 s2 並不符合條件 108 | >>> s3 = ["gh","ab","ef","hi","bc","cd","fg","de"] # 無循環 109 | >>> dfs(None, s3) 110 | ['ab', 'bc', 'cd', 'de', 'ef', 'fg', 'gh', 'hi'] 111 | >>> check(s3) 112 | ['ab', 'bc', 'cd', 'de', 'ef', 'fg', 'gh', 'hi'] 113 | >>> s4 = ["gh","ab","ef","ha","bc","cd","fg","de"] # 循環 114 | >>> dfs(None, s4) 115 | ['gh', 'ha', 'ab', 'bc', 'cd', 'de', 'ef', 'fg'] 116 | >>> check(s4) 117 | ['gh', 'ha', 'ab', 'bc', 'cd', 'de', 'ef', 'fg'] 118 | ``` 119 | -------------------------------------------------------------------------------- /questions/algorithm/找出所有組合-笛卡爾積問題.md: -------------------------------------------------------------------------------- 1 | # 找出所有組合-笛卡爾積問題 2 | 3 | ## 問題 4 | 5 | 結構數據是這樣的: 6 | 7 | ![clipboard.png](https://segmentfault.com/img/bVzfR8) 8 | 9 | 要求按照這樣的公式: 10 | 11 | ``` 12 | 時間詞+地方詞+動詞+等級+名詞+價格詞; 13 | ``` 14 | 15 | 比如 16 | 17 | ``` 18 | 2016年深圳大鵬新區給健康全身檢查要多少錢 19 | ``` 20 | 21 | 就是按照這樣的公式組合出來的關鍵詞 22 | 23 | 那麼有什麼辦法用最短的辦法來實現,我下面是我的算法,用 pandas 的算法: 24 | 25 | ```python 26 | for times in df[df["時間詞"].notnull()]["時間詞"]: 27 | for area in df[df["地方詞"].notnull()]["地方詞"]: 28 | for dong in df[df["動詞"].notnull()]["動詞"]: 29 | for leave in df[df["等級"].notnull()]["等級"]: 30 | for name in df[df["名詞"].notnull()]["名詞"]: 31 | for price in df[df["價格詞"].notnull()]["價格詞"]: 32 | data = (times+area+dong+leave+name+price) 33 | ``` 34 | 35 | 但是這樣的代碼太不優雅,而且封裝成函數太難~ 36 | 我想要的效果是這樣的, 比如我寫一個公式: 37 | 38 | ```python 39 | cols = ["時間詞","地方詞","動詞","等級","名詞","價格詞"] 40 | #或是 41 | cols = ["地方詞","動詞","等級","名詞","價格詞"] 42 | ``` 43 | 44 | 然後把這個列表傳入一個函數中,就可以得出我上面的效果~ 45 | 這個要如何實現? 46 | 47 | ----- 48 | 49 | 補充一下,如果看不懂提問的人可以這樣理解這個題目, 我有 3 個 list: 50 | 51 | ```python 52 | a = ["1","2","3","4","5"] 53 | b = ["a","b","c"] 54 | c = ["A","B"] 55 | ``` 56 | 57 | 我要這樣的組合: `a` 中的每個元素和 `b`, `c` 中的每個元素都進行組合 58 | 這個一個很簡單的多重循環就可以解決: 59 | 60 | ```python 61 | for A in a: 62 | for B in b: 63 | for C in c: 64 | print (A+B+C) 65 | ``` 66 | 67 | 這當然很簡單,但是假如我有 10000 個這樣的列表要重組? 68 | 難不成要手工複製黏貼每個循環 10000 次?這顯然不太現實 69 | 70 | 在 python 中有沒有比較好的函數或是比較好的方法來實現這個東西? 71 | 72 | 問題出自 [segmentfault](https://segmentfault.com/q/1010000005980173/a-1020000005980906), by [jqlts1](https://segmentfault.com/u/jqlts1) 73 | 74 | ## 回答 75 | 76 | 在我回答之前已經有高手給了這樣的答案: 77 | 78 | ```python 79 | import itertools 80 | for x in itertools.product([1,2,3,4],[2,3,4],[1,1,2,2,3,3]): 81 | print x 82 | ``` 83 | 84 | 沒錯, `itertools` 裡面的 `product` 就是完成笛卡爾積的神兵利器! 85 | 86 | ----- 87 | 88 | 至於我.... 89 | 90 | 我來表演 **重複發明輪子**: 91 | 92 | 一個用 iteration 的版本: 93 | 94 | ```python 95 | def product(*iterables): 96 | START = 0 97 | END = 1 98 | 99 | se_idxs = [(0, len(iterable)) for iterable in iterables] 100 | curr_idxs = [0 for iterable in iterables] 101 | 102 | def next(): 103 | curr_idxs[-1] += 1 104 | for k in reversed(range(len(iterables))): 105 | if curr_idxs[k]==se_idxs[k][END]: 106 | if k==0: 107 | return False 108 | curr_idxs[k] = 0 109 | curr_idxs[k-1] += 1 110 | else: 111 | break 112 | return True 113 | 114 | while True: 115 | yield [iterable[idx] for idx, iterable in zip(curr_idxs, iterables)] 116 | if not next(): 117 | return 118 | ``` 119 | 120 | 一個 recursive 的版本: 121 | 122 | ```python 123 | def product_core(result, *iterables): 124 | if len(iterables)==0: 125 | yield result 126 | else: 127 | for item in iterables[0]: 128 | yield from product_core(result+[item], *iterables[1:]) 129 | 130 | def product(*iterables): 131 | return product_core([], *iterables) 132 | ``` 133 | 134 | 測試: 135 | 136 | ```python 137 | a = [1, 2, 3] 138 | b = [4, 5, 6] 139 | c = [7, 8, 9] 140 | 141 | for p in product(a, b, c): 142 | print(p) 143 | ``` 144 | 145 | 結果: 146 | 147 | ``` 148 | [1, 4, 7] 149 | [1, 4, 8] 150 | [1, 4, 9] 151 | [1, 5, 7] 152 | [1, 5, 8] 153 | [1, 5, 9] 154 | [1, 6, 7] 155 | [1, 6, 8] 156 | [1, 6, 9] 157 | [2, 4, 7] 158 | [2, 4, 8] 159 | [2, 4, 9] 160 | [2, 5, 7] 161 | [2, 5, 8] 162 | ... 163 | ``` 164 | 165 | 人生苦短 別傻傻的自己做重複的事情啊... 166 | -------------------------------------------------------------------------------- /questions/algorithm/燈泡開關問題.md: -------------------------------------------------------------------------------- 1 | # 燈泡開關問題 2 | 3 | ## 問題 4 | 5 | 現在有 100 個燈泡,每個燈泡都是關著的,第一趟把所有的燈泡燈泡打開,第二趟把偶數位的燈泡製反,第三趟讓第 3, 6, 9.... 的燈泡製反.......第 100 趟讓第 100 個燈泡製反,問經過一百趟以後有多少燈泡亮著。 6 | 7 | 代碼如何實現。 8 | 9 | 問題出自 [segmentfault](https://segmentfault.com/q/1010000006055330), by [菩提旭光](https://segmentfault.com/u/runforlove) 10 | 11 | ## 回答 12 | 13 | 補充一下 @hsfzxjy 所說的 14 | 15 | ``` 16 | 燈泡只要有一個因數就會被開關一次 17 | ``` 18 | 19 | * 因為進行第 `i` 趟開關的時候, 會把 `i` 的倍數的燈開關 20 | * 也就是說, 因數有 `i` 的燈泡在此趟會被開關 21 | 22 | 從上面可以推出: 23 | 24 | ``` 25 | 燈泡有奇數個因數最後的結果會是亮著的 (開關奇數次, 會是亮的) 26 | ``` 27 | 28 | 又可以歸納出: 29 | 30 | ``` 31 | 完全平方數的燈泡會亮著 (因為只有完全平方數有奇數個相異因數, 其他都會有兩兩成對的相異因數) 32 | ``` 33 | 34 | 如果你要完全模擬這個狀況的話, 這邊有 Python 的代碼: 35 | 36 | ```python 37 | lamps = [ False for i in range(100) ] 38 | 39 | # print('starts', lamps) 40 | 41 | for i in range(1, len(lamps)+1): 42 | for idx, lamp in enumerate(lamps): 43 | if (idx + 1) % i == 0: 44 | lamps[idx] = not lamp 45 | # print(i, lamps) 46 | 47 | print(lamps.count(True)) 48 | ``` 49 | 50 | 但根據上面的結論, 你只要知道燈泡數內有幾個完全平方數就好了: 51 | 52 | ```python 53 | i = 1 54 | while i**2 <= 100: 55 | i += 1 56 | 57 | print(i-1) 58 | ``` 59 | -------------------------------------------------------------------------------- /questions/bs/python爬蟲beautifulsoup string抓取問題.md: -------------------------------------------------------------------------------- 1 | # python 爬蟲 beautifulsoup string 抓取問題 2 | 3 | ## 問題 4 | 5 | ![图片描述][1] 6 | 7 | [1]: https://segmentfault.com/img/bVyY2I 8 | 9 | 我要的是這個藍色部分的內容,但是 beautifulsoup 裡兩個方法,一個 `.strings` 還有一個 `get_text()` 都不行,他們會把下面 `span` 裡的 `string:Good Sister-in-lwa:Forbidden love` 這些都抓取。 10 | 11 | `.string` 直接抓不到,因為這個方法無法判斷該抓取哪個 string。 12 | 13 | 所以我該怎麼解決標籤里內嵌標籤的抓取字符串問題 14 | 15 | 問題出自 [segmentfault](https://segmentfault.com/q/1010000005915466/a-1020000005915727), by [yikosudo](https://segmentfault.com/u/yikosudo) 16 | 17 | ## 回答 18 | 19 | @洛克 的想法不錯,把不要的標籤淬出或是移除,再取字串: 20 | 21 | ```python 22 | >>> from bs4 import BeautifulSoup 23 | >>> html = 'I linked to example.com' 24 | >>> soup = BeautifulSoup(html) 25 | >>> a_tag = soup.a 26 | >>> i_tag = soup.i.extract() 27 | >>> a_tag.string 28 | 'I linked to ' 29 | ``` 30 | 31 | 或是像 @cloverstd 說的: 32 | 33 | ``` 34 | >>> from bs4 import BeautifulSoup 35 | >>> html = 'I linked to example.com' 36 | >>> soup = BeautifulSoup(html) 37 | >>> a_tag = soup.a 38 | >>> list(a_tag.strings) 39 | [u'I linked to ', u'example.com'] 40 | >>> list(a_tag.strings)[0] 41 | 'I linked to ' 42 | >>> a_tag.contents[0] 43 | 'I linked to ' 44 | ``` 45 | 46 | 總之方法很多,任意組合囉... 47 | -------------------------------------------------------------------------------- /questions/bs/刪除xml文件的指定標籤.md: -------------------------------------------------------------------------------- 1 | # 刪除xml文件的指定標籤 2 | 3 | ## 問題 4 | 5 | 有個xml文件的格式大致如下: 6 | 7 | ```xml 8 | 9 | 123 10 | abc 11 | 12 | 13 | 126 14 | abc 15 | 16 | 17 | 135 18 | abc 19 | 20 | 21 | 147 22 | abc 23 | 24 | ``` 25 | 26 | 然後另外一個 `delete.txt` 保存的是需要刪除的 re 標籤的 id。 27 | 28 | 假設 txt 內容如下: 29 | 30 | ``` 31 | 126 32 | 147 33 | ``` 34 | 35 | 需要做的就是讀取這個 delete.txt 文件,然後在 xml 中找到這些 id 對應的 ` `標籤將其全部刪除,如上例的結果就是: 36 | 37 | ```xml 38 | 39 | 123 40 | abc 41 | 42 | 43 | 44 | 135 45 | abc 46 | 47 | ``` 48 | 49 | 請問是怎麼做的。。另外需要提到的是這個 xml 文件挺大的,有200多M。 50 | 51 | 問題出自 [segmentfault](https://segmentfault.com/q/1010000005077756/a-1020000005079269), by [starryer](https://segmentfault.com/u/starryer) 52 | 53 | ## 回答 54 | 55 | 你可以使用 `BeautifulSoup` 套件: 56 | 57 | **安裝**: 58 | 59 | ```sh 60 | $ pip install bs4 61 | ``` 62 | 63 | * 如果覺得 `html` 解析器不敷使用,參考[文檔][1]安裝其他適合的解析器。 64 | * 如果想要詳細了解 `BeautifulSoup` 也請參考官方文檔(有中文版本)。 65 | 66 | **測試檔**: 67 | 68 | 以下是我使用的測試文件: 69 | 70 | delete.txt 71 | 72 | ``` 73 | 126 74 | 147 75 | ``` 76 | 77 | test.xml 78 | 79 | ```xml 80 | 81 | 123 82 | abc 83 | 84 | 85 | 126 86 | abc 87 | 88 | 89 | 135 90 | abc 91 | 92 | 93 | 147 94 | abc 95 | 96 | ``` 97 | 98 | **代碼**: 99 | 100 | ```python 101 | from bs4 import BeautifulSoup 102 | 103 | with open('test.xml') as reader: 104 | xml = reader.read() 105 | 106 | deleted_id = [] 107 | 108 | with open('delete.txt') as reader: 109 | for line in reader: 110 | line = line.strip() 111 | deleted_id.append(line) 112 | 113 | def has_delete_id(tag): 114 | return tag.name=='re' and tag.id.string in deleted_id 115 | 116 | soup = BeautifulSoup(xml, 'html.parser') 117 | 118 | tags = soup(has_delete_id) 119 | for tag in tags: 120 | tag.decompose() 121 | 122 | print(soup.prettify()) 123 | ``` 124 | 125 | **程式輸出**: 126 | 127 | ```xml 128 | 129 | 130 | 123 131 | 132 | 133 | abc 134 | 135 | 136 | 137 | 138 | 135 139 | 140 | 141 | abc 142 | 143 | 144 | ``` 145 | 146 | **代碼說明**: 147 | 148 | 首先我們從 `Beautiful Soup` 的套件中匯入 `BeautifulSoup` 類 149 | 150 | ```python 151 | from bs4 import BeautifulSoup 152 | ``` 153 | 154 | 接著分別從 `delete.txt` 和 `test.xml` 中讀出要刪除的 id 和主要的 xml 內容,下一步是實體化生成一個 `BeautifulSoup` 對象 `soup`, 我們採用 `html.parser` 解析器去解析 `xml`: 155 | 156 | ```python 157 | soup = BeautifulSoup(xml, 'html.parser') 158 | ``` 159 | 160 | 在此我們定義了一個用於過濾的 function `has_delete_id`,每一個在 `xml` 中的tag 只要是 `` tag 且含有想要刪除的 `` tag 就會被檢索出來: 161 | 162 | ```python 163 | def has_delete_id(tag): 164 | return tag.name=='re' and tag.id.string in deleted_id 165 | ``` 166 | 167 | 接著 `soup(has_delete_id)` 會幫助我們找到欲刪除的 tag,接著走訪搜索出來的這些 tag 並呼叫方法 `decompose()` 來從文件中刪除該標籤。 168 | 169 | 最後 `soup.prettify()`可以幫助我們輸出修改後的文件。 170 | 171 | [1]: https://www.crummy.com/software/BeautifulSoup/bs4/doc.zh/#id9 172 | 173 | -------------------------------------------------------------------------------- /questions/collections/Top_K_Frequent_Elements.md: -------------------------------------------------------------------------------- 1 | # Top K Frequent Elements 2 | 3 | ## 問題 4 | 5 | ``` 6 | [('d', 100), ('c', 99), ('a', 89), ('b', 86)] 7 | ``` 8 | 9 | 如何快速得出: 10 | 11 | ``` 12 | ['d', 'c', 'a', 'b'] 13 | ``` 14 | 15 | 感覺好 low, 有什麼好的辦法嗎? 16 | 其實在用 Python 中在刷 leetcode 來學習 17 | [題目](https://leetcode.com/problems/top-k-frequent-elements/) 18 | 19 | ```python 20 | class Solution(object): 21 | def topKFrequent(self, nums, k): 22 | """ 23 | :type nums: List[int] 24 | :type k: int 25 | :rtype: List[int] 26 | """ 27 | items = {} 28 | for item in nums: 29 | if items.has_key(item): 30 | items[item]+=1 31 | else: 32 | items[item]=1 33 | arr1 = sorted(items.iteritems(), key=lambda asd:asd[1], reverse=True) 34 | arr2 = [] 35 | for key in range(len(arr1)): 36 | arr2.append(arr1[key][0]) 37 | return arr2[0:k] 38 | ``` 39 | 40 | 問題出自 [segmentfault](https://segmentfault.com/q/1010000005087747/a-1020000005088335), by [cosde](https://segmentfault.com/u/cosde) 41 | 42 | ## 回答 43 | 44 | 很簡單,使用 list comprehension 即可: 45 | 46 | ```python 47 | arr1 = [('d', 100), ('c', 99), ('a', 89), ('b', 86)] 48 | arr2 = [pair[0] for pair in arr1] 49 | ``` 50 | 51 | 看了一下你原本的問題,寫了一個簡潔的版本,可以參考一下: 52 | (適用於 Python2.7+, Python3) 53 | 54 | ```python 55 | from collections import Counter 56 | 57 | def top_k_frequent(lst, k): 58 | return [key for key, count in Counter(lst).most_common(k)] 59 | ``` 60 | 61 | **使用**: 62 | 63 | ```python 64 | lst = [1, 1, 1, 2, 3, 4, 4] 65 | print(top_k_frequent(lst, 2)) 66 | ``` 67 | 68 | **效果**: 69 | 70 | ``` 71 | [1, 4] 72 | ``` 73 | 74 | **說明**: 75 | 76 | Python2.7+之後的版本,在 `collections` 庫裡有一種類 `Counter` 可以用。 77 | 詳細的操作方法請參考[Counter object][1] 78 | 79 | 利用 `Counter(lst)` 可以輕鬆得到一個 `Counter`實例,裡面已經對 `lst` 中的元素作過統計了。 80 | 之後利用 `most_common(k)` 方法可以輕鬆得到一個排序過的 list of tuple,而且只會剩下前出現頻率前k高的項目,最後用 `list comprehension` 取出元素本身: 81 | 82 | ```python 83 | >>> from collections import Counter 84 | >>> counter = Counter([1, 1, 1, 2, 3, 4, 4]) 85 | >>> counter 86 | Counter({1: 3, 4: 2, 2: 1, 3: 1}) 87 | >>> most_items = counter.most_common(2) 88 | >>> most_items 89 | [(1, 3), (4, 2)] 90 | >>> [key for key, count in most_items] 91 | [1, 4] 92 | ``` 93 | 94 | [1]: https://docs.python.org/2/library/collections.html#counter-objects 95 | -------------------------------------------------------------------------------- /questions/collections/會php和python的大神進來幫忙轉換一段代碼.md: -------------------------------------------------------------------------------- 1 | # 會 php 和 Python的大神進來幫忙轉換一段代碼 2 | 3 | ## 問題 4 | 5 | 求把這段php代碼轉成python的代碼,謝謝! 6 | 7 | 8 | 9 | ```php 10 | //倒序排序 11 | function my_sort($a,$b) 12 | { 13 | if ($a==$b) return 0; 14 | return ($a<$b)?1:-1; 15 | } 16 | 17 | 18 | $arr = array('aaa'=>5,'bbb'=>3,'ccc'=>4); 19 | 20 | usort($arr,"my_sort"); 21 | 22 | echo json_encode($arr); 23 | ``` 24 | 25 | 簡單說就是數組倒序排序,然後轉成json格式。 26 | 27 | 問題出自 [segmentfault](https://segmentfault.com/q/1010000005106649/a-1020000005107260), by [御風獨行豬](https://segmentfault.com/u/yufengduxingzhu) 28 | 29 | ## 回答 30 | 31 | PHP 中的 associative array 是一種 ordered mapping (有序映射). 32 | 這代表了 Python 中的 dictionary 並非完全相等於 associative array. 33 | 34 | 其次, json 據我所知並不支援 ordered mapping,所以如果你想要完成這項任務可能要: 35 | 36 | 1. 使用 Python 中的有序映射對象: `OrderedDict` (請參考[OrderedDict][1]) 37 | 2. 將 `OrderedDict` 轉為 `list` 再轉為 `json` 38 | 3. 到時候要使用該項資料時,必須從 `json` 中 load 進 `list` 再轉回 `OrderedDict` 39 | 40 | ---------- 41 | 42 | 以下是 *Python3* 的代碼讓你參考: 43 | 44 | **代碼**: 45 | 46 | ```python 47 | import json 48 | from collections import OrderedDict 49 | 50 | # using OrderedDict 51 | arr = {"aaa":5,"bbb":3,"ccc":4, "ddd":7} 52 | arr = OrderedDict(sorted(arr.items(), key=lambda item: item[1], reverse=True)) 53 | # or you can create an OrderedDict directly: 54 | # arr = OrderedDict([('aaa', 5), ('bbb', 3), ('ccc', 4), ('ddd', 7)]) 55 | print(arr) 56 | 57 | # list 58 | arr = list(arr.items()) 59 | print(arr) 60 | 61 | # json dump 62 | json_arr = json.dumps(arr) 63 | print(json_arr) 64 | 65 | # json load 66 | arr = OrderedDict(json.loads(json_arr)) 67 | print(arr) 68 | ``` 69 | 70 | **結果**: 71 | 72 | ```python 73 | OrderedDict([('ddd', 7), ('aaa', 5), ('ccc', 4), ('bbb', 3)]) 74 | [('ddd', 7), ('aaa', 5), ('ccc', 4), ('bbb', 3)] 75 | [["ddd", 7], ["aaa", 5], ["ccc", 4], ["bbb", 3]] 76 | OrderedDict([('ddd', 7), ('aaa', 5), ('ccc', 4), ('bbb', 3)]) 77 | ``` 78 | 79 | [1]: https://docs.python.org/2/library/collections.html#ordereddict-objects 80 | -------------------------------------------------------------------------------- /questions/csv/csv模塊生成CSV文件問題(0字頭數字缺失與漢字亂碼).md: -------------------------------------------------------------------------------- 1 | # csv 模塊生成 CSV 文件問題(0字頭數字缺失與漢字亂碼) 2 | 3 | ## 問題 4 | 5 | python CSV模塊寫入CSV文件時,0開頭的數字會丟失 6 | 7 | ```python 8 | # _*_ coding:utf-8 _*_ 9 | #win7+python2.7.x 10 | import csv 11 | csvfile = file( 'csvtest.csv' , 'wb' ) 12 | writer = csv.writer(csvfile) 13 | writer.writerow([ 'id' , 'url' , 'keywords' ]) 14 | data = [ 15 | ( '0011' , 'http://www.59store.com/' , '59store.com' ), 16 | ( '0022' , 'http://59data.top/' , '59data.top' ), 17 | ( '0033' , 'http://my.space.zmx/' , '漢子亂碼?' ) 18 | ] 19 | writer.writerows(data) 20 | csvfile.close() 21 | ``` 22 | 23 | 寫入CSV時會丟失0字頭,漢字亂碼 24 | 25 | ![img](https://segmentfault.com/img/bVwHjb) 26 | 27 | 問題出自 [segmentfault](https://segmentfault.com/q/1010000005370629/a-1020000005436450), by [messiah163](https://segmentfault.com/u/messiah163) 28 | 29 | ## 回答 30 | 31 | 我測試的結果: 32 | 33 | ``` 34 | id,url,keywords 35 | 0011,http://www.59store.com/,59store.com 36 | 0022,http://59data.top/,59data.top 37 | 0033,http://my.space.zmx/,汉子乱码? 38 | ``` 39 | 40 | 看起來純文字 file 沒有什麼問題,猜測可能是你**用來開啟 csv 文件的試算表軟體**造成的.(mac 的 `Numbers` 和 `OpenOffice Calc` 都有這個現象) 41 | 42 | 比如說,id 欄位的型態如果設為數字,則前面不必要的 0 可能會自動被忽略. 像這一點可以試試看改成純文字型態再開啟. 43 | 44 | P.S. `Excel` 的部分可以見 玉河CC 大的說明. 45 | 46 | 要更精準更細節地處理 xlsx 文件,可以用 [XlsxWriter][1],他能夠控制資料的型態(data type),甚至樣式(format). 47 | 48 | [1]: http://xlsxwriter.readthedocs.io/index.html 49 | -------------------------------------------------------------------------------- /questions/csv/如何用python刪除csv文件中的某一列.md: -------------------------------------------------------------------------------- 1 | # 如何用 python 刪除 csv 文件中的某一列 2 | 3 | ## 問題 4 | 5 | 如何用python刪除csv文件中的某一列? 6 | 7 | 比如名為 `a.csv`中的數據: 8 | 9 | ``` 10 | index ABC 11 | 0 1 3 5 12 | 1 2 4 6 13 | 2 7 8 9 14 | ``` 15 | 16 | 想要刪除第二列( B 列)該如何刪除呢?代碼該如何編寫呢? 17 | 求大神!萬分感謝!!! 18 | 19 | 問題出自 [segmentfault](https://segmentfault.com/q/1010000005958613/a-1020000005959134), by [tammy](https://segmentfault.com/u/qqqqqq123) 20 | 21 | ## 回答 22 | 23 | 如果問題很單純,甚至連 `csv` 都可以不使用: 24 | 25 | ```python 26 | with open('old.csv') as reader, open('new.csv', 'w') as writer: 27 | for line in reader: 28 | items = line.strip().split() 29 | print(' '.join(items[:2]+items[3:]), file=writer) 30 | ``` 31 | 32 | 寫個 general 的: 33 | 34 | ```python 35 | import csv 36 | 37 | def del_cvs_col(fname, newfname, idxs, delimiter=' '): 38 | with open(fname) as csvin, open(newfname, 'w') as csvout: 39 | reader = csv.reader(csvin, delimiter=delimiter) 40 | writer = csv.writer(csvout, delimiter=delimiter) 41 | rows = (tuple(item for idx, item in enumerate(row) if idx not in idxs) for row in reader) 42 | writer.writerows(rows) 43 | 44 | del_cvs_col('a.csv', 'b.csv', [2]) 45 | ``` 46 | 47 | `del_cvs_col` 會將 `fname` 轉成 `newfname` 用 `delimiter` 來做分割字元,且會去除掉在 `idxs`中指定的列。 48 | -------------------------------------------------------------------------------- /questions/csv/操作csv文件寫入順序不對.md: -------------------------------------------------------------------------------- 1 | # 操作 csv 文件寫入順序不對 2 | 3 | ## 問題 4 | 5 | 我是初學者不太懂 6 | 7 | 1. 為什麼在終端顯示是正確的順序到了csv 文件中就是另一回事了呢 8 | 2. 還有就是csv 文件怎麼可以運行之後繼續填寫而不是清空文件呢? 9 | 10 | ![img1](https://segmentfault.com/img/bVxKBr) 11 | 12 | ![img2](https://segmentfault.com/img/bVxKBs) 13 | 14 | 代碼: 15 | 16 | ```python 17 | import urllib.request 18 | import re 19 | import bs4 20 | import csv 21 | from bs4 import BeautifulSoup 22 | 23 | url="http://10.104.65.9/home/part/shuiQing.jsp" 24 | data=urllib.request.urlopen(url).read() 25 | data=data.decode('UTF-8') 26 | 27 | soup=BeautifulSoup(data,"html.parser") 28 | foundtxt=soup.find_all('td',height='22') 29 | 30 | rol=[] 31 | index=0 32 | 33 | #with open(r'C:\Users\skyb52\Desktop\12.csv','wb') as csvfile: 34 | with open(r'C:\Users\skyb52\Desktop\12.csv','w',newline='') as csvfile: 35 | spamwriter = csv.writer(csvfile,dialect='excel') 36 | for i in foundtxt: 37 | rol.append(i.string) 38 | index=index+1 39 | if index==3: 40 | spamwriter.writerow({rol[0],rol[1],rol[2]}) 41 | print(rol[0],rol[1],rol[2]) 42 | rol=[] 43 | index=0 44 | csvfile.close() 45 | ``` 46 | 47 | 最後添加上內網網站源代碼: 48 | 49 | ```html 50 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 |
站名水位(m)流量(m3/s)
小浪底134.63565
花园口89.05445
夹河滩72.58400
高村58.98360
孙口44.29358
艾山36.82225
泺口25.7207
利津9.3676.5
108 | 109 | ``` 110 | 111 | 問題出自 [segmentfault](https://segmentfault.com/q/1010000005621628/a-1020000005624752), by [skyb52](https://segmentfault.com/u/skyb52) 112 | 113 | ## 回答 114 | 115 | 你寫到 csv 的代碼: 116 | 117 | ```python 118 | spamwriter.writerow({rol[0],rol[1],rol[2]}) 119 | ``` 120 | 121 | 你寫了一個 set 出來,他是無序的,而你 `print` 的時候是有序的: 122 | 123 | ```python 124 | print(rol[0],rol[1],rol[2]) 125 | ``` 126 | 127 | 結果自然不同 128 | 129 | ---------- 130 | 131 | 不清空而是附加新的資料上去的作法,在於使用 `open` 打開文檔時,要使用 `'a'` (append) 模式: 132 | 133 | ```python 134 | with open(r'C:\Users\skyb52\Desktop\12.csv','w',newline='') as csvfile: 135 | ^^^ 136 | 寫入模式,會覆蓋掉原本的資料從頭開始寫入 137 | ``` 138 | 139 | 改成: 140 | 141 | ```python 142 | with open(r'C:\Users\skyb52\Desktop\12.csv','a',newline='') as csvfile: 143 | ^^^ 144 | 附加模式,會從文件最後開始寫入 145 | ``` 146 | -------------------------------------------------------------------------------- /questions/data_mining/python3中有聚類(主要是k-means)的函數或者模塊嗎.md: -------------------------------------------------------------------------------- 1 | # python3 中有聚類(主要是k-means)的函數或者模塊嗎 2 | 3 | ## 問題 4 | 5 | 最經在做用戶畫像,需要對數據進行聚類,發現python貌似沒有現成的聚類函數。不知道是不是我沒有找到,還是真的沒有,如果沒有,大夥有什麼好的軟件推薦去完成聚類分析。 6 | 7 | 謝謝各位了~~ 8 | 9 | 問題出自 [segmentfault](https://segmentfault.com/q/1010000006057925/a-1020000006057999), by [EchoJnn](https://segmentfault.com/u/echojnn) 10 | 11 | ## 回答 12 | 13 | > k-means clustering aims to partition n observations into k clusters in which each observation belongs to the cluster with the nearest mean, serving as a prototype of the cluster. 14 | 15 | * [sklearn.cluster.KMeans](http://scikit-learn.org/stable/modules/generated/sklearn.cluster.KMeans.html) 16 | 17 | * [scipy-cluster](http://docs.scipy.org/doc/scipy/reference/cluster.html) 18 | -------------------------------------------------------------------------------- /questions/django/Django CSRF verification failed 問題.md: -------------------------------------------------------------------------------- 1 | # Django CSRF verification failed 問題 2 | 3 | ## 問題 4 | 5 | **urls.py** 6 | 7 | ```python 8 | from django.conf.urls import url 9 | from django.contrib import admin 10 | from blog import views 11 | urlpatterns = [ 12 | url(r'^admin/', admin.site.urls), 13 | url(r'^$', views.index), 14 | url(r'^abc$',views.handler), 15 | ] 16 | ``` 17 | 18 | **views.py** 19 | 20 | ```python 21 | # -*- coding: utf-8 -*- 22 | from django.shortcuts import render 23 | from django.http import HttpResponse 24 | 25 | # Create your views here. 26 | 27 | def index(request): 28 | return render(request,"index.html") 29 | 30 | 31 | def handler(request): 32 | return HttpResponse("

name:

" + request.POST['username']) 33 | ``` 34 | 35 | **index.html** 36 | 37 | ```python 38 | 39 | 40 | 41 | 42 | index page 43 | 44 | 45 |
46 | 47 | 48 |
49 | 50 | 51 | 52 | ``` 53 | 54 | 我在谷歌瀏覽器下點擊這個提交後出現了: 55 | ![图片描述][1] 56 | 57 | 我又直接打開abc網站出現了: 58 | ![图片描述][2] 59 | 60 | 請問這是什麼問題啊要怎麼解決啊? 61 | 62 | 63 | [1]: https://segmentfault.com/img/bVz3ih 64 | [2]: https://segmentfault.com/img/bVz3iq 65 | 66 | 問題出自 [segmentfault](https://segmentfault.com/q/1010000006170144/a-1020000006171703), by [dzxczxsb](https://segmentfault.com/u/dzxczxsb) 67 | 68 | ## 回答 69 | 70 | 在 Django 中, 使用 post 的時候很可能會出現以下錯誤: 71 | 72 | ```python 73 | Forbidden(403): 74 | CSRF verification failed. Request aborted. 75 | Reason given for failure: 76 | CSRF token missing or incorrect. 77 | ``` 78 | 79 | 這是因為 Django 幫我們啟動了 **CSRF攻擊** 的防護,CSRF(cross-site request forgery) 是惡意的跨站請求或偽裝使用者的攻擊,攻擊者會欺騙用戶的瀏覽器去訪問一個認證過的網站並且執行一些惡意的操作。由於用戶的瀏覽器已經被該網站認證過了,所以該網站會放心的讓這些操作被執行(即便這些操作並非該網站要求的或是不是用戶自願的)。 80 | 81 | 所以我們的伺服器需要一些有保護的措施。常見的一種防護手段,就是使用一個伺服器產生的亂數 token,夾帶在送給客戶端的表單中,當客戶端送回表單時,伺服器檢查這個 token 是不是自己發出,便可以防止攻擊。 82 | 83 | 由於在 `settings.py` 檔中的 `MIDDLEWARE_CLASSES` 中有預設的 `'django.middleware.csrf.CsrfViewMiddleware'`,所以 Django 在這裡便會要求 CSRF token 驗證,為了讓我們的網站更安全,我們還是照著遊戲規則一步一步來吧! 84 | 85 | 在html的`
`中加入`{% csrf_token %}`如下: 86 | 87 | ```html 88 | ... 89 | {% csrf_token %} 90 | ... 91 | ``` 92 | 93 | 就可以解決問題了 94 | -------------------------------------------------------------------------------- /questions/django/django如何一個url綁定多個視圖.md: -------------------------------------------------------------------------------- 1 | # django 如何一個 url 綁定多個視圖 2 | 3 | ## 問題 4 | 5 | 問題很簡單,我有2個url規則,但是可能會有衝突 6 | 7 | ```python 8 | url(r'^(?P\w+)/$', 9 | CategoryView.as_view(), name='category-detail-view'), 10 | 11 | 12 | url(r'^(?P\w+)/$',CustomView.as_view(),name="custm"), 13 | ``` 14 | 15 | 簡單的看來就是這樣的,這2條url,其實目的的是一樣,為了訪問 16 | www.baidu.com/xxx/ 這樣的分類,只是有一個是自定頁面. 17 | 這樣設置不行, 只能取其中一個. 18 | 19 | 我想問問, django 有沒有辦法讓同一個url規則綁定多個不同的視圖? 這樣就很靈活了~ 20 | 21 | 問題出自 [segmentfault](https://segmentfault.com/q/1010000005773535/a-1020000005773924), by [jqlts1](https://segmentfault.com/u/jqlts1) 22 | 23 | ## 回答 24 | 25 | 一個 url pattern 如果可以綁定多個視圖,我覺得 Django 也不知道怎麼處理這個 request 了 (到底要派發給哪個 view )。 26 | 27 | 但你現在的問題是: 28 | 29 | > 符合同一個 pattern 的不同 url 需要不同的處理 30 | 31 | 這聽起來怪怪的,如果說真有這種情形,那大概代表你的 pattern 不應該這樣寫,你應該試圖將原本的 url pattern 拆開寫成多個可區別的 pattern。 32 | 33 | 當然,很有可能是 url pattern 很難拆開來,以你的例子,也許的確難以區別: 34 | 35 | ``` 36 | (domain name)/category1/ 37 | ``` 38 | 39 | 和 40 | 41 | ``` 42 | (domain name)/www.google.com.tw/ 43 | ``` 44 | 45 | 因為這兩種 url 提取出來的 pattern 如你所講的,根本一模一樣。 46 | 47 | 以下是幾種可能的作法: 48 | 49 | 1. 如果 category 的種類不多,可以考慮將 category 的部份直接拆開來寫 url pattern 50 | 2. 就使用一種 url pattern,但是先用一個統一個 view 來處理,再根據 url 中截取到的參數轉發給不同的 view 處理 51 | 52 | ### 結論 53 | 54 | url 截取參數就是為了這種需求阿: 55 | 56 | > 同樣形式的 url pattern 要能夠處理符合該 pattern 但實際上還是有區別的各個 url 57 | -------------------------------------------------------------------------------- /questions/django/django模版中變量引用變量被當作字符串處理而不是變量值.md: -------------------------------------------------------------------------------- 1 | # django 模版中變量引用變量被當作字符串處理而不是變量值 2 | 3 | ## 問題 4 | 5 | 問題:django 中模版變量引用變量,後面的變量被當作字串處理了,求解決方案。 6 | 7 | 代碼: 8 | 9 | ```python 10 | li = ['a','b','c','d'] 11 | di = {'a':1,'b':2,'e':3,'f':5,'t':4} 12 | ``` 13 | 14 | ```jinja 15 | {% for i in li %} 16 | {{ di.i }} 17 | {% endfor %} 18 | ``` 19 | 20 | 這時候,`i` 沒有替換為列表中的內容,而是直接解析為字串 `i`,求解決方案。 21 | 22 | 問題出自 [segmentfault](https://segmentfault.com/q/1010000005615987), by [rongfeideng](https://segmentfault.com/u/rongfeideng) 23 | 24 | ## 回答 25 | 26 | data: 27 | 28 | ```python 29 | di = {'a':1,'b':2,'e':3,'f':5,'t':4} 30 | items = di.items() 31 | ``` 32 | 33 | template: 34 | 35 | ```python 36 | {% for key, value in items %} 37 | {{ value }} 38 | {% endfor %} 39 | ``` 40 | 41 | ---------- 42 | 43 | 下面是我以前寫的筆記,你可以參考一下: 44 | 45 | ### for 標籤的限制 46 | 47 | 由於 `MTV (Model-View-Template)` 分工的關係,標籤(模板)語言受到了種種限制,所以對於 for 標籤來說,`break` 或是 `continue` 等方法是不存在的,我們只能透過在 view function 中整理出一個最貼切的資料。 48 | 49 | 另外特別要注意的是,for 標籤雖然能夠迭代 dictionary 的 key,但要由鍵取值是相當容易犯錯的,請看以下例子(假定我們的 view function 給定了一個字典 `dic={'1':'a','2':'b'}` ): 50 | 51 | ```jinja 52 | {% for key in dic%} 53 | {{ key }} = {{ dic.key }} 54 | {% endfor %} 55 | ``` 56 | 57 | 我們將會發現,鍵會被輸出而對應的值不會,理由很簡單,變量的使用只會以變量的值取代變量的名稱(變量內的第一個名字),往後的各種名稱都會以字面的意思解讀。有點難懂,以上例來說 `{{ key }}` 在兩次迴圈中會分別被代換成 1 跟 2,但是 `{{ dic.key }}` 卻會被試著解讀成 `dic['key']`、`dic.key`、`dic.key()`、`dic[key]`,也就是說變量中第二個以後的名字便不會被對應到值了,因為他不是變量,而是變量的屬性、方法或鍵(字面上!不會被真的代換),`dic` 當然沒有叫做 `'key'` 的屬性。那你問我該怎麼辦,可以考慮在 view function 中提供`items = dic.items()`,以下有範例: 58 | 59 | ```jinja 60 | {% for key, value in items %} 61 | {{ key }} = {{ value }} 62 | {% endfor %} 63 | ``` 64 | 65 | 或 66 | 67 | ```jinja 68 | {% for item in items %} 69 | {{ item.0 }} = {{ item.1 }} 70 | {% endfor %} 71 | ``` 72 | -------------------------------------------------------------------------------- /questions/django/網頁根目錄改成子目錄後文件如何調用.md: -------------------------------------------------------------------------------- 1 | # 網頁根目錄改成子目錄後文件如何調用 2 | 3 | ## 問題 4 | 5 | 用django寫的程序,然後大概的問題是這樣的.跟這個類似: 6 | 7 | ![clipboard.png](https://segmentfault.com/img/bVzhBT) 8 | 9 | 然後按照答案改成 `./` 的話, 首頁是可以訪問的, 但是內頁, 文章頁就不行了。 10 | 11 | 網站的結構: 12 | 13 | ![clipboard.png](https://segmentfault.com/img/bVzhB2) 14 | 15 | 默認生成的都是 `/` 的,我知道把所有的鏈接前面加一個目錄的名稱比如 `/001/` 也是可以訪問的,但是感覺這樣不夠智能而且也不夠方便,求快捷的方法? 16 | 17 | 問題出自 [segmentfault](https://segmentfault.com/q/1010000005986846/a-1020000005991601), by [jqlts1](https://segmentfault.com/u/jqlts1) 18 | 19 | ## 回答 20 | 21 | 靜態檔應該使用 static 標籤來避免寫死連結在 template 裡面。 22 | 23 | 以下節錄自之前我寫的書: [It's Django](https://github.com/its-django/mysite/wiki) 24 | 25 | ### 使用靜態檔 26 | 27 | 動態網站中也會需要靜態檔案,如網站會用到的圖片,css,js檔等。我們要如何來管理和使用這些靜態檔呢?首先,我們在上層`mysite`底下增加兩個資料夾 `static` 和 `assets`,`static` 資料夾是開發時用來放置靜態檔的目錄,該目錄底下可以新增數個子目錄來放置不同種類的靜態檔,比如說設置 `img` 來放置圖片或是 `css` 來放置 css 檔。而 `assets` 是網站真正上線時放置靜態檔的目錄,這兩個目錄之所以會分開,是因為在上線時我們需要將靜態檔的管理權交給網頁伺服器。 28 | 29 | 接著,我們先到`settings.py`中進行設定: 30 | 31 | ```python 32 | ... 33 | STATIC_URL = '/static/' 34 | STATICFILES_DIRS = ( 35 | os.path.join(BASE_DIR, 'static'), 36 | ) 37 | STATIC_ROOTS = os.path.join(BASE_DIR, 'assets') 38 | ... 39 | ``` 40 | 41 | 這邊一共設定了三個參數,詳細的說明如下: 42 | 43 | |參數|說明| 44 | |---|:--| 45 | | STATIC_URL | 靜態檔的URL pattern,這邊我們設為 `/static/` ,那麼在網頁路徑中以 `/static/` 開始的便會被視為靜態檔,如: *http://127.0.0.1/static/hello.png* 、 *http://127.0.0.1/static/hi.js* ,但如果是 *http://127.0.0.1/restaurants/static/* 則會匹配`urls.py`中的路徑,而非靜態檔 | 46 | | STATICFILES_DIRS | 開發時放置靜態檔的資料夾。允許設置多個資料夾來指示靜態檔的位置。如上面設定,我們可以在與`BASE_DIR`所指示的資料夾底下新增一個 `static` 資料夾,並把圖檔,css,js放在裡面 | 47 | | STATIC_ROOT | 上線時放置靜態檔的資料夾。藉由`python manage.py collectstatic` ,Django 會將 `STAIC_DIRS` 下發現的靜態檔複製至 `STATIC_ROOT` 下。由於當設定檔的 `DEBUG` 設為 `False` 後,Django 預設便不會處理回傳靜態檔了,藉由指令將這些檔案集合到一個資料夾下,方便網頁伺服器管理及讀取 | 48 | 49 | 上面這些參數設定好之後,便可以在模版使用 `STATICFILES_DIRS` 資料夾下的靜態檔案。假設網站要新增一張圖片 `logo.png` (放在 `mysite/static/img/logo.png` ) 在頁面上,我們在模版上會這樣寫: 50 | 51 | ```html 52 | ... 53 | 54 | ... 55 | ``` 56 | 57 | 便大功告成了。但若有一天靜態檔的網頁路徑被迫更改為 `/static_file/` ,就得一個一個模版找到路徑並修改,這不是很累人嗎?一個比較 "Django" 的作法如下: 58 | 59 | ```html 60 | {% load staticfiles %} 61 | ... 62 | 63 | ... 64 | ``` 65 | 66 | 請讀者記得在模版中先載入 `staticfiles` ,再使用標籤 `{% static ...%}` 。經過以上調整之後,Django在處理靜態檔的時候便會將 `{% static 'img/logo.png' %} `轉為 `'/static/img/logo.png'` ,而且當有需要修改靜態檔的路徑時,可以放心大膽地修改 `settings.py` 檔中的 `STATIC_URL`,而不需要煩惱修改大量模版的問題了。 67 | -------------------------------------------------------------------------------- /questions/dunder/自己寫的數據類型使用print無法輸出每個元素.md: -------------------------------------------------------------------------------- 1 | # 自己寫的數據類型使用 print 無法輸出每個元素 2 | 3 | ## 問題 4 | 5 | 估計是我問題沒有描述清楚,這樣吧,直接上代碼: 6 | 7 | ```python 8 | class Mylist : 9 | def __init__(self) : 10 | self._mylist=list() 11 | 12 | def __len__(self) : 13 | return len(self._mylist) 14 | 15 | def add(self,value) : 16 | return self._mylist.append(value) 17 | ``` 18 | 19 | 我自己模仿 list 的行為。寫了一個基本的 list,名字叫 Mylist, 並給他一個 `add` 方法用來添加其中的元素. 20 | 21 | 添加完之後,我想輸出其中的內容,然後我使用: 22 | 23 | ```python 24 | list1=Mylist() 25 | list1.add( 1 ) 26 | list1.add( 2 ) 27 | print(list1) 28 | ``` 29 | 30 | 我以為print會顯示出 `list1` 中的每一項,但是發現實際沒有,顯示的為: 31 | 32 | ```python 33 | <__main__.Mylist object at 0x0071A470> 34 | ``` 35 | 36 | 怎麼樣能讓 `print(list1)` 顯示出的結果和真實 list 類型一樣呢?例如: 37 | 38 | ``` 39 | [1,2] 40 | ``` 41 | 42 | `__str__` 具體怎麼實現,貌似這個只能為 str 類型, int 不行,而且我發現在 pycharm 裡面寫的時候,提示 __str__ 方法 "overrides method in object" 43 | 44 | 問題出自 [segmentfault](https://segmentfault.com/q/1010000005110206/a-1020000005113051), by [小燕smile](https://segmentfault.com/u/xiaoyansmile) 45 | 46 | ## 回答 47 | 48 | 你是用的是 "聚合" 的方式來建立自己的群集資料,這時候透過委託是個簡單的方法: 49 | 50 | ```python 51 | class Mylist: 52 | def __init__(self): 53 | self._mylist=list() 54 | 55 | def __len__(self): 56 | return len(self._mylist) 57 | 58 | def add(self,value): 59 | return self._mylist.append(value) 60 | 61 | def __str__(self): 62 | return str(self._mylist) 63 | ``` 64 | 65 | `__str__` 是 Python 類中的特殊方法,他的回傳值就是使用 `str(x)` 所得到的值, 而 `print(x)` 其實就等於是 `print(str(x))`.其實再講細一點,當我們呼叫 `str(x)` 的時候其實是呼叫 `x.__str__()`. 66 | 67 | 也就是說我們可以這樣想像: 68 | 69 | ```python 70 | print(x) === print(str(x)) === print(x.__str__()) 71 | ``` 72 | 73 | 一般我們 **自定義的類**,`__str__` 方法的回傳值是默認的字串. 74 | 75 | 比如說: `<__main__.Mylist object at 0x0071A470>` 用以說明 namespace, class name 和位置.如果要改變 `__str__` 的回傳值,我們必須要覆寫他. 76 | 77 | 這邊我們讓 `__str__` 的回傳值為 `MyList` 類中聚合的 list 的 `__str__`值,這樣就透過委託的方式讓 `__str__` 的輸出跟 list 一樣了. 78 | 79 | 多嘴補充一下,這種在 class 裡面 **以雙底線開頭且以雙底線結尾** 的 method (俗名叫做魔術方法或是特殊方法),有個正式名稱叫做 **"dunder method"**,對於 `__str__`,我們可以唸作 "dunder string". 80 | 81 | > The frequent use of a double underscores in internal identifiers in Python gave rise to the abbreviation dunder; this was coined by Mark Jacksonand independently by Tim Hochberg, within minutes of each other, both in reply to the same question in 2002. --[wiki][1] 82 | 83 | ---------- 84 | 85 | 下方評論問的問題我回答在這裡. 86 | 87 | 首先是不要被混淆,我們利用 `print` 印出來的內容都是 **字串**,即便你看到 `[1, 2]` 其實這也是一個字串 `'[1, 2]'`,只不過內建的幾種資料型態(或我們有覆寫過 `__str__` 的 class) 會想辦法輸出一個帶有該資料型態特徵的字串(通常會非常接近我們產生這些資料時所用的"字面"). 88 | 89 | 舉例,我們使用字面產生一個 list: 90 | 91 | ```python 92 | lst = [1, 2] 93 | ``` 94 | 95 | 當我們打印 `lst` 時,Python 是會製造一個長得像該資料型態字面(甚至一模一樣)的字串讓你印出: 96 | 97 | ```python 98 | print(lst) 99 | 100 | [1, 2] # 其實這是個字串,但是 lst 還是 list! 101 | ``` 102 | 103 | 所以在這裡 `str(list)` 並沒有改變 list 中元素的 type,只不過將帶有其特徵的 "字串" 當成回傳值. 104 | 105 | 其次,如果想要在 Python shell (Python的互動介面)中能夠只利用變數名稱就展示用來表示 `Mylist` 的字串,光是 `__str__` 還不夠,這必須要覆寫 `__repr__`: 106 | 107 | ```python 108 | class Mylist: 109 | 110 | def __init__(self): 111 | self._mylist=list() 112 | 113 | def __len__(self): 114 | return len(self._mylist) 115 | 116 | def add(self,value): 117 | return self._mylist.append(value) 118 | 119 | def __str__(self): 120 | return str(self._mylist) 121 | 122 | def __repr__(self): 123 | return str(self) 124 | ``` 125 | 126 | **結果**: 127 | 128 | ```python 129 | >>> from test import Mylist 130 | >>> lst = Mylist() 131 | >>> lst.add(1) 132 | >>> lst.add(2) 133 | >>> lst 134 | [1, 2] 135 | ``` 136 | 137 | [1]: http://bit.ly/1Vm72Mf 138 | -------------------------------------------------------------------------------- /questions/error/Python如何優雅的處理大量異常語句.md: -------------------------------------------------------------------------------- 1 | # Python 如何優雅的處理大量異常語句 2 | 3 | ## 問題 4 | 5 | 我需要用 bs4 來分析一個 html,需要寫很多提取語句,大概幾十條,格式如下: 6 | 7 | ```python 8 | twitter_url = summary_soup.find('a','twitter_url').get('href') 9 | facebook_url = summary_soup.find('a','facebook_url').get('href') 10 | linkedin_url = summary_soup.find('a','linkedin_url').get('href') 11 | name = summary_soup.find('div', class_='name').find('a').string 12 | ``` 13 | 14 | 但是每個語句都有可能出異常,如果每個語句都加上 `try/except` 就太繁瑣了,有沒有什麼好的方法處理每條語句,出異常賦值為 `None`,不中斷程序 15 | 16 | 問題出自 [segmentfault](https://segmentfault.com/q/1010000005868327/a-1020000005894846), by [ider](https://segmentfault.com/u/ider) 17 | 18 | ## 回答 19 | 20 | 我在問題的評論裡面有提出一個小問題,如果能有回答,大家比較好掌握你的需求. 21 | 22 | 如果不想太多,純粹要避免掉 `get` 的時候可能會產生的錯誤,有個比較偷雞的方式,如果沒有太多奇怪的狀況要處理,也許你可以試試: 23 | 24 | ```python 25 | twitter_url = (summary_soup.find('a','twitter_url') or {}).get('href') 26 | ``` 27 | 28 | 如果說 bs 的 `find` 沒有找到東西的話,會 `return None`,此時我們利用先利用 `or` 來完成一個 trick 使得 `get` 永遠不會失敗.再利用字典的 `get` 與 bs tag 的 `get` 相似的特性就可以處理掉異常,對變數賦值為 `None`. 29 | 30 | 如果要寫的穩固一點的話,參考 @prolifes 的建議滿有幫助的. 31 | -------------------------------------------------------------------------------- /questions/error/python如何捕獲中斷.md: -------------------------------------------------------------------------------- 1 | # python 如何捕獲中斷 2 | 3 | ## 問題 4 | 5 | 我目前有一個爬蟲, 爬取的東西目測要好些天才能完 6 | 7 | 所以我設計成了能夠自動續爬的形式, 不過我選擇想再進一步 8 | 9 | 記錄每次開始和結束的時間 10 | 11 | 但是不知道怎樣在 `` 這種鍵盤終端的時候去寫下程序退出的時間 12 | 13 | 望指點指點, 感覺是要捕獲異常.. 14 | 15 | 問題出自 [segmentfault](https://segmentfault.com/q/1010000006102391/a-1020000006102562), by [Rancho](https://segmentfault.com/u/rancho) 16 | 17 | ## 回答 18 | 19 | catch `KeyboardInterrupt` 20 | 21 | ```python 22 | import time 23 | import datetime 24 | 25 | print('stime:', datetime.datetime.now()) 26 | try: 27 | while True: 28 | time.sleep(1) 29 | print('go') 30 | except KeyboardInterrupt: 31 | print('etime:', datetime.datetime.now()) 32 | ``` 33 | 34 | 測試: 35 | 36 | ``` 37 | stime: 2016-07-28 01:54:21.647561 38 | go 39 | go 40 | go 41 | 42 | etime: 2016-07-28 01:54:25.010312 43 | ``` 44 | -------------------------------------------------------------------------------- /questions/eval/對Python語法字串求值.md: -------------------------------------------------------------------------------- 1 | # 對 Python 語法字串求值 2 | 3 | ## 問題 4 | 5 | 讀 csv 文件的時候讀到這樣的 str: 6 | 7 | ```python 8 | ["item1", "item2", "item3"] 9 | ``` 10 | 11 | 顯然這是一個 list 12 | 可是我該如何把它轉化成 list 呢? 13 | 用 `list()` 的話每個字符會被當成一個 item 14 | 用切詞的方法也應該可以但會麻煩 15 | 但是有沒有直接的方法呢? 16 | 17 | 問題出自 [segmentfault](https://segmentfault.com/q/1010000006152237/a-1020000006152877), by [persona](https://segmentfault.com/u/persona) 18 | 19 | ## 回答 20 | 21 | 使用 `eval`, 不過要小心他的風險: 22 | 23 | ```python 24 | In [1]: s = '["item1", "item2", "item3"]' 25 | 26 | In [2]: lst = eval(s) 27 | 28 | In [3]: lst 29 | Out[3]: ['item1', 'item2', 'item3'] 30 | ``` 31 | 32 | * [Python doc - eval](https://docs.python.org/3/library/functions.html#eval) 33 | 34 | 對於 Python 的 subset 進行求值, 使用 `ast.literal_eval` 是對的。 35 | 36 | 37 | ```python 38 | # by manong 39 | >>> import ast 40 | >>> ast.literal_eval('["item1", "item2", "item3"]') 41 | ['item1', 'item2', 'item3'] 42 | ``` 43 | 44 | 下面文章值得一讀: 45 | 46 | [Using python's eval() vs. ast.literal_eval()?](http://stackoverflow.com/questions/15197673/using-pythons-eval-vs-ast-literal-eval) 47 | -------------------------------------------------------------------------------- /questions/file/Python如何向文件最開始插入一個字串.md: -------------------------------------------------------------------------------- 1 | # Python如何向文件最開始插入一個字串 2 | 3 | ## 問題 4 | 5 | Python 如何向文件最開始插入一個字串,在 **盡量保證性能** 的情況下。 6 | 7 | 問題出自 [segmentfault](https://segmentfault.com/q/1010000005890090/a-1020000005894627), by [galaxy_21](https://segmentfault.com/u/galaxy_21) 8 | 9 | ## 回答 10 | 11 | 分兩點來探討這個問題: 12 | 13 | 1. 如何有效率的來插入內容到檔案的頭或中間,有效率指的是較少的時間(較快的速度)與資源利用. 14 | 2. 如何能夠寫出足夠 pythonic 的 code 來處理這件事 15 | 16 | 我先說我的結論(如果有任何其他看法歡迎討論,也許我是錯的): 17 | 18 | 1. 做不到 19 | 2. 只要不是寫得太糟或太難閱讀,我覺得平庸一點或是較長的 code 也無妨(甚至更好) 20 | 21 | 引大神的話來佐證一下: 22 | 23 | > Python makes a lot of things easy and contains libraries and wrappers for a lot of common operations, but the goal is not to hide fundamental truths. 24 | > 25 | > The fundamental truth you are encountering here is that you generally can't prepend data to an existing flat structure without rewriting the entire structure. This is true regardless of language. 26 | > 27 | > There are ways to save a filehandle or make your code less readable, many of which are provided in other answers, but none change the fundamental operation: You must read in the existing file, then write out the data you want to prepend, followed by the existing data you read in. 28 | > 29 | > By all means save yourself the filehandle, but don't go looking to pack this operation into as few lines of code as possible. In fact, never go looking for the fewest lines of code -- that's obfuscation, not programming. 30 | > 31 | > By ***Nicholas Knight*** 32 | 33 | 參考資料: 34 | 35 | [Prepend a line to an existing file in Python](http://stackoverflow.com/questions/4454298/prepend-a-line-to-an-existing-file-in-python) 36 | -------------------------------------------------------------------------------- /questions/file/Python如何實現並行查找關鍵字所在的行.md: -------------------------------------------------------------------------------- 1 | # Python 如何實現並行查找關鍵字所在的行 2 | 3 | ## 問題 4 | 5 | 我有幾十萬個關鍵字放在文件 `4.txt` 中,想提取文件 `3.txt` 中含有關鍵字的行,保存到文件 `5.txt` 中. 6 | 文件 3 有 200 萬行,我使用下面的代碼可以實現我的要求,但是非常慢,一個下午還沒運行完,誰有快一點的方法? 7 | 使用並行改如何改造呢?我看到這裡有個[並行的帖子](http://www.oschina.net/translate/python-parallelism-in-one-line),與我的不同的事,我要同時讀以及查詢同一個文件,上述鏈接可以並行操作多個文件。 8 | 9 | ```python 10 | with open('3.txt', 'r') as f3, open('4.txt', 'r') as f4, open('result.txt', 'w') as f5: 11 | a = [line.strip() for line in f4.readlines()] 12 | for li in f3.readlines(): 13 | new_line = li.strip().split()[1][:-2] 14 | for i in a: 15 | if i in new_line: 16 | f5.writelines(li) 17 | ``` 18 | 19 | 問題出自 [segmentfault](https://segmentfault.com/q/1010000005137716/a-1020000005138325), by [biopython](https://segmentfault.com/u/biopython) 20 | 21 | ## 回答 22 | 23 | 因為沒有實際的文件,沒有辦法給你一個百分之百的保證,不過對於你的 code,我有一些些效率改進上的建議: 24 | 25 | (也許你會發現改進後的代碼根本不需要使用並行的解決的方案) 26 | 27 | 首先一個很大的問題是 `readlines()`,這個方法會一口氣讀取 file objects 中的所有行,這對於效率和資源的使用顯然是極差的,幾十萬行幾百萬行的東西要一口氣讀**完**了,這可是非常恐怖的. 28 | 29 | 詳細的分析和討論請參考[Never call readlines() on a file][1] 30 | 31 | (文章中的這段話幾乎可當作是警語了) 32 | > There are hundreds of questions on places like StackOverflow about the readlines method, and in every case, the answer is the same. 33 | > "My code is takes forever before it even gets started, but it's pretty fast once it gets going." 34 | > **That's because you're calling readlines**. 35 | > "My code seems to be worse than linear on the size of the input, even though it's just a simple loop." 36 | > **That's because you're calling readlines**. 37 | > "My code can't handle giant files because it runs out of memory." 38 | > **That's because you're calling readlines**. 39 | 40 | 結論是: **建議所有使用 `readlines` 的地方全部改掉**. 41 | 42 | 範例: 43 | 44 | ```python 45 | with open('XXX', 'r') as f: 46 | for line in f.readlines(): 47 | # do something... 48 | ``` 49 | 50 | 一律改成: 51 | 52 | ```python 53 | with open('XXX', 'r') as f: 54 | for line in f: 55 | # do something... 56 | ``` 57 | 58 | 直覺上效率會好很多. 59 | 60 | 其次,你使用了 list 來查找關鍵字,這也是相當沒效率的: 61 | 62 | ```python 63 | for i in a: 64 | if i in new_line: 65 | ``` 66 | 67 | 為了確認 `new_line` 中是否有關鍵字 `i`,這邊走訪了一整個關鍵字 list: `a`,對於一般的情況可能還好,但是數十萬的關鍵字比對,對每一行都走訪一次 `a` 會造成大量的時間浪費,假設 `a` 裡面有 x 個關鍵字,`f3` 中有 y 行,每行有 z 個字,這邊要花的時間就是 `x*y*z`(根據你文件的行數,這個數量級極為驚人). 68 | 69 | 如果簡單地利用一些使用 hash 來查找的容器肯定會好一些,比如說 `dictionary` 或是 `set`. 70 | 71 | 最後是關於你的查找部分: 72 | 73 | ```python 74 | for li in f3.readlines(): 75 | new_line = li.strip().split()[1][:-2] 76 | for i in a: 77 | if i in new_line: 78 | f5.writelines(li) 79 | ``` 80 | 81 | 這邊我不是很懂,`new_line` 看起來是一個子字串,然後現在要用這個字串去比對關鍵字? 82 | 83 | 不過先撇開這個不談,關於含有關鍵字的 `new_line` 在印出後,似乎不該繼續循環 `a`,除非你的意思是 `new_line` 中有幾個關鍵字我就要印 `line` 幾次. 否則加上一個 `break` 也是可以加快速度. 84 | 85 | 建議你的code改為: 86 | 87 | ```python 88 | with open('3.txt') as f3, open('4.txt') as f4, open('result.txt', 'w') as f5: 89 | keywords = set(line.strip() for line in f4) 90 | for line in f3: 91 | new_line = line.strip().split()[1][:-2] 92 | for word in new_line: 93 | if word in keywords: 94 | print(line, file=f5) 95 | break 96 | ``` 97 | 98 | 如果我有弄錯你的意思,歡迎跟我說,我們再來討論一下,直覺上應該不必使用到並行就可以解決你的問題 99 | 100 | [1]: http://stupidpythonideas.blogspot.tw/2013/06/readlines-considered-silly.html 101 | -------------------------------------------------------------------------------- /questions/file/Python處理一個求和運算.md: -------------------------------------------------------------------------------- 1 | # Python 處理一個求和運算 2 | 3 | 我現在有個 txt 文件如下: 4 | 5 | ``` 6 | 1 167 334555717 7 | 2 19 334555718 8 | 2 167 334555718 9 | 3 167 334555720 10 | 4 172 334555721 11 | 5 21 334555723 12 | 5 147 334555723 13 | 5 50 334555723 14 | ``` 15 | 16 | * 第一列是序號 17 | * 第二列是字數 18 | * 第三列是ID。 19 | 20 | 相同 ID 的序號是相同的。 21 | 22 | 然後現在我需要用 python 進行下述處理: 23 | 24 | 就是將相同 id 的字數相加得到一個總和 `sum`,然後對 `sum` 進行如下的公式計算: 25 | 26 | ``` 27 | result=字數1/sum log(字數1/sum)+字數2/ sum log(字數2/sum)+...... 28 | ``` 29 | 30 | 按這個文件舉例來說,就是比如 31 | 32 | 334555718 的這個 id 的 result 計算如下: 33 | 34 | ``` 35 | sum=19+167 36 | result=19/sum log(19/sum)+ 167/sum log(167/sum) 37 | ``` 38 | 39 | 33455723 的這個 id 的 result 就是: 40 | 41 | ``` 42 | sum=21+147+50 43 | result=21/sum log(21/sum)+147/sum log(147/sum)+50/sum*log (50/sum) 44 | ``` 45 | 46 | 然後依次輸出每個 id 的序號, id號 跟 result。 47 | 48 | 我的代碼如下: 49 | 50 | ```python 51 | import math 52 | f = open("F:\\net.txt") 53 | lines = f.readlines() 54 | 55 | rev_id=[] 56 | 57 | for line in lines: 58 | num = line.split()[0] 59 | zishu = line.split()[2] 60 | revid = line.split()[3] 61 | sum = zishu 62 | if revid in rev_id: 63 | sum += zishu 64 | result += zishu/sum*(math.log(zishu/sum)) 65 | rev_id.append(revid) 66 | ``` 67 | 68 | 我的 result 的結果肯定不對,因為 `sum` 的值不是固定的全部總和...請問該怎麼做。 69 | 70 | ## 問題 71 | 72 | 問題出自 [segmentfault](https://segmentfault.com/q/1010000005089896/a-1020000005093803), by [starryer](https://segmentfault.com/u/starryer) 73 | 74 | ## 回答 75 | 76 | 這邊是Python3的代碼,給你參考: 77 | 78 | ```python 79 | import math 80 | 81 | dic = {} 82 | 83 | # read file 84 | with open('net.txt' , 'r') as reader: 85 | for line in reader: 86 | idx, num, revid = line.strip().split() 87 | lst = dic.setdefault(revid, []) 88 | lst.append(int(num)) 89 | 90 | results = {} 91 | 92 | # calculate results 93 | for revid, lst in dic.items(): 94 | s = sum(lst) 95 | result = sum([num/s * math.log(num/s) for num in lst]) 96 | results.update({revid:result}) 97 | 98 | # output 99 | for revid, result in results.items(): 100 | print(revid, result) 101 | ``` 102 | 103 | ### 說明與建議 104 | 105 | Python 讀取檔案的確是使用 `open` function,但是開啟了的檔案,必須記得關閉以免發生問題,像你給出的代碼中,文件被你 `open` 了卻沒有 `close`,這是一種較不安全的寫法,應當緊記: 有 `open` 就有 `close`,他們是成對的。 為了避免忘記關閉文件的可能,使用 `with` 述句是個理想的辦法。 106 | -------------------------------------------------------------------------------- /questions/file/Python處理文本信息.md: -------------------------------------------------------------------------------- 1 | # Python 處理文本信息 2 | 3 | ## 問題 4 | 5 | 有一個文本信息如下: 6 | 7 | ``` 8 | 42 453926 Stormwriter restored undeleted 61.1.28.140 9 | 44 425968 61.1.28.140 10 | 42 425967 Mintguy restored undeleted 61.11.252.22 11 | 43 419840 61.11.252.22 12 | ``` 13 | 14 | 我做的是需要根據第一列的這個序號的數據來找對應的數據,像這種有著相同序號的行,他們對應的第二列的 ID 數據就是對應的,我需要找到這樣一對一對的 ID 數據。就這個例子來說就是 453926 跟 425967 是對應的,輸出: 15 | 16 | ``` 17 | 453926 425967 18 | ``` 19 | 20 | 而也會有多個相同序號的情況。比如: 21 | 22 | ``` 23 | 42 453926 Stormwriter restored undeleted 61.1.28.140 24 | 44 425968 61.1.28.140 25 | 42 425967 Mintguy restored undeleted 61.11.252.22 26 | 43 419840 61.11.252.22 27 | 42 419809 TimStarling 28 | ``` 29 | 30 | 就是需要記錄多次,而且是跟最後一個相同序號對應,拿這個例子來說,就是記錄第一個 42 跟最後一個 42 的對應 ID,然後同時也要記錄中間那個 42 跟最後一個 42 的 ID,輸出這樣: 31 | 32 | ``` 33 | 453926 419809(restored行的ID是453926) 34 | 425967 419809(restored行的ID是425967) 35 | ``` 36 | 37 | 我開始想的是用字典,但是字典也就只能保留到最後一個相同的序號,怎麼做才能讓中間的相同序號也能輸出對應的呢TUT 38 | 39 | 我的偽代碼如下: 40 | 41 | ```python 42 | dict={} 43 | if xxx: #只是一個判斷處理的條件 44 | flag_number = line.split()[0] 45 | id = line.split()[0] 46 | next() 47 | elif line.split()[0]==flag_number: 48 | dict[id] = line.split()[1] 49 | ``` 50 | 但是這個代碼只能輸出第一個跟最後一個相同的序號,如何修改才能也讓中間的相同序號跟最後一個序號也輸出出來呢 51 | 52 | 問題出自 [segmentfault](https://segmentfault.com/q/1010000005621524/a-1020000005622128), by [starryer](https://segmentfault.com/u/starryer) 53 | 54 | ## 回答 55 | 56 | 用字典是可以的,首先是收集: 57 | 58 | ```python 59 | INDEX = 0 60 | ID = 1 61 | 62 | dic = {} 63 | 64 | with open('data') as reader: 65 | for line in reader: 66 | items = line.strip().split() 67 | ids = dic.setdefault(items[INDEX], []) 68 | ids.append(items[ID]) 69 | ``` 70 | 71 | 輸出: 72 | 73 | ```python 74 | for index, ids in dic.items(): 75 | for sid in ids[0:-1]: 76 | print sid, ids[-1] 77 | ``` 78 | 79 | 或: 80 | 81 | ```python 82 | # 如果不太懂這段代碼的意思,請參見下面說明,其實我覺得用上面的方法也夠了 83 | for index, ids in dic.items(): 84 | for sid, eid in ((id, ids[-1]) for id in ids[0:-1]): 85 | print sid, eid 86 | ``` 87 | 88 | 稍微講一下這段代碼: 89 | 90 | ```python 91 | ((id, ids[-1]) for id in ids[0:-1]) 92 | ``` 93 | 94 | 上面這一行是一個 產生器表達式( generator expression, 簡稱 genexp),他就類似 list comprehension,不過他並不會馬上產生實際的資料和 list,只有等到你去 iterate 或是取值的時候才會依序產生資料項。這代表在資源的利用上,是比較有效率的。 95 | 96 | 這邊產生的 generator 可以依序產生一個雙元素的 tuple,這兩個元素都是 id,剛好就是一個 id pair(第一個元素是各個非最後一個id,第二個元素是最後一個id。 97 | 98 | 至於: 99 | 100 | ```python 101 | for sid, eid in ((id, ids[-1]) for id in ids[0:-1]): 102 | ``` 103 | 104 | 就是依次產生 id pair 並且利用 tuple unpacking 平行賦值給 `sid` 和 `eid`,最後輸出。 105 | 106 | ---------- 107 | 108 | `data`: 109 | 110 | ``` 111 | 42 453926 Stormwriter restored undeleted 61.1.28.140 112 | 44 425968 61.1.28.140 113 | 42 425967 Mintguy restored undeleted 61.11.252.22 114 | 43 419840 61.11.252.22 115 | 42 419809 TimStarling 116 | ``` 117 | 118 | 結果: 119 | 120 | ``` 121 | 453926 419809 122 | 425967 419809 123 | ``` 124 | -------------------------------------------------------------------------------- /questions/file/使用Python如何按行數拆分文件.md: -------------------------------------------------------------------------------- 1 | # 使用 Python 如何按行數拆分文件 2 | 3 | ## 問題 4 | 5 | 假設我有1個類似這樣方式生成的文件: 6 | 7 | ```python 8 | with open('my-file.txt','wb') as f: 9 | for x in xrange(300000): 10 | f.write('hello,world\n') 11 | ``` 12 | 13 | 現在我們想按照每個文件1000行的方式對之前生成的文件進行拆分,該如何處理? 14 | 15 | 問題出自 [segmentfault](https://segmentfault.com/q/1010000005357402/a-1020000005359339), by [我勒個去](https://segmentfault.com/u/yafeile) 16 | 17 | ## 回答 18 | 19 | ```python 20 | with open('my-file.txt', 'r') as reader: 21 | counter = 0 22 | findex = 0 23 | for line in reader: 24 | if counter==0: 25 | writer = open('file-'+str(findex), 'w') 26 | print >> writer, line.strip() 27 | counter += 1 28 | if counter >= 1000: 29 | writer.close() 30 | counter = 0 31 | findex += 1 32 | ``` 33 | 34 | 其實,Linux 的 `split` 指令就可以作到這件事了: 35 | 36 | ```bash 37 | $ split -l 1000 my-file.txt 38 | ``` 39 | -------------------------------------------------------------------------------- /questions/file/怎樣合併文檔中有重複部分的行.md: -------------------------------------------------------------------------------- 1 | # 怎樣合併文檔中有重複部分的行? 2 | 3 | ## 問題 4 | 5 | 文檔內容如下: 6 | 7 | ``` 8 | (數據對) (信息) 9 | ------------ ------------------- 10 | 1 2 3 4 5 11 | ------------ ------------------- 12 | pr333 sd23a2 thisisa 1001 1005 13 | pr333 sd23a2 sentence 1001 1005 14 | pr33w sd11aa we 1022 1002 15 | pr33w sd11aa have 1022 1002 16 | pr33w sd11aa adream 1033 1002 17 | ...... 18 | ``` 19 | 20 | 第1, 2列作為一個**數據對** 21 | 22 | ``` 23 | 如果前兩列相同,判斷後面的是否相同,如果不同就連接起來,合併成一行 24 | ``` 25 | 26 | 如同下面的效果: 27 | 28 | ``` 29 | pr333 sd23a2 thisisa|sentence 1001 1005 30 | pr33w sd11aa we|have|adream 1022 | 1033 1002 31 | .... 32 | ``` 33 | 34 | 小白,不懂怎麼做,只能想到用字典,好像又行不通,求各位大神幫忙 35 | 36 | 問題出自 [segmentfault](https://segmentfault.com/q/1010000005596472/a-1020000005598723), by [ffaniu](https://segmentfault.com/u/ffaniu) 37 | 38 | ## 回答 39 | 40 | 說明一下這個代碼所有的考量. 41 | 42 | 首先是 **順序**,這裡的順序有兩個部分,一個是輸出行的順序,一個是項目合併之後的順序.我們觀察到: 43 | 44 | ``` 45 | pr333 sd23a2 thisisa 1001 1005 46 | pr333 sd23a2 sentence 1001 1005 47 | pr33w sd11aa we 1022 1002 48 | pr33w sd11aa have 1022 1002 49 | pr33w sd11aa adream 1033 1002 50 | ``` 51 | 52 | 要變成: 53 | 54 | ``` 55 | pr333 sd23a2 thisisa|sentence 1001 1005 56 | pr33w sd11aa we|have|adream 1022|1033 1002 57 | ``` 58 | 59 | 1. 輸出行的順序要顧及: pr333 先於 pr33w 60 | 2. 項目合併之後的順序要顧及: thisisa 先於 sentence 61 | 62 | 這代表我們 **使用的資料型態必須要能維持順序** 63 | 64 | 其次是速度,我們都知道序列型態是線性搜尋,**為了效率,使用映射型態是比較好的.** 65 | 66 | 三種考量之下,就如同 moling3650 大所言,`OrderedDict` 是個好選擇.這可以解決行輸出的問題,不過合併項目由於只需要用到 key 而不需要用到 value 所以使用 `OrderedDict` 有點可惜,不過目前標準庫中沒有 `OrderSet` 的選擇,所以只好將就著用一下. 67 | 68 | 1. 有關於 OrderedDict 可以參閱 [OrderedDict][1] 69 | 2. 其實有一個 OrderedSet 的第三方庫 [OrderedSet][2] 70 | 或者可以自己實作,請參考 [OrderedSet (Python recipe)][3] 71 | 72 | **代碼(Python3)**: 73 | 74 | ```python 75 | from collections import OrderedDict 76 | 77 | data = OrderedDict() 78 | 79 | DPAIR = slice(0,2) 80 | MSG = slice(2,None) 81 | 82 | with open('data.txt', 'r') as reader: 83 | for line in reader: 84 | line = line.strip() 85 | items = tuple(line.split()) 86 | 87 | msgs = data.setdefault(items[DPAIR], [OrderedDict({}) for msg in items[MSG]]) 88 | for idx, msg in enumerate(msgs): 89 | msg.setdefault(items[MSG][idx], None) 90 | 91 | for (dp1, dp2), msgs in data.items(): 92 | print(dp1, dp2, *['|'.join(msg.keys()) for msg in msgs]) 93 | ``` 94 | 95 | 關於代碼部分也做個說明(也許我寫的不是最好,但有些心得可以分享). 96 | 97 | 首先是 `slice` 類的應用. 98 | 99 | 身為一個 Python programmer,我們對 **序列型態** 取切片(slicing) 應該都不陌生. 100 | 101 | ```python 102 | items[start:stop:step] 103 | ``` 104 | 105 | 其實可以寫成: 106 | 107 | ```python 108 | items[slice(start, stop, step)] 109 | 110 | # example 111 | items[:5] 可以寫成 items[slice(0,5)] 112 | items[7:] 可以寫成 items[slice(7,None)] 113 | ``` 114 | 115 | 那好處是什麼呢? 116 | 117 | 我們可以利用這個特性對切片進行命名,以這個問題的代碼為例,原本要取出 **數據對** 與 **其他資料** 可以用: 118 | 119 | ```python 120 | items = tuple(line.split()) 121 | items[0:2] # 這是用來做 key 的數據對 122 | items[2:] # 這是其他的資料項 123 | ``` 124 | 125 | 但是這種方式其實閱讀起來不夠清晰,我們可以幫這兩個範圍取個名字,所以: 126 | 127 | ```python 128 | DPAIR = slice(0,2) 129 | MSG = slice(2,None) 130 | items[DPAIR] # 這是用來做 key 的數據對 131 | items[MSG] # 這是其他的資料項 132 | ``` 133 | 134 | 我們可以用比較優雅易讀的方式來從 `items` 中取值. 135 | 136 | 其次是 `setdefault`,這個函數相當實用,舉例: 137 | 138 | ```python 139 | dic.setdefault(key, default_value) 140 | ``` 141 | 142 | 如果字典(或其他相符的映射型態)中存在鍵值 `key` 則回傳 `dic[key]` 否則回傳自動在字典中插入新的鍵值對 `dic[key] = default_value` 並且回傳 `default_value`. 143 | 144 | 最後一個想分享的是嵌套 tuple 的拆解: 145 | 146 | ```python 147 | for (a, b), c, d in ((1,2) ,3, 4): 148 | print(a, b, c, d) # 印出 1 2 3 4 149 | ``` 150 | 151 | 這個技巧可以很方便的用來拆解巢狀嵌套的 tuple. 152 | 153 | 感謝大家不嫌我話多... 154 | 155 | [1]: https://docs.python.org/2/library/collections.html#collections.OrderedDict 156 | [2]: http://orderedset.readthedocs.io/en/latest/ 157 | [3]: http://code.activestate.com/recipes/576694/ 158 | -------------------------------------------------------------------------------- /questions/file/文本格式轉換代碼優化.md: -------------------------------------------------------------------------------- 1 | # 文本格式轉換代碼優化 2 | 3 | ## 問題 4 | 5 | 格式轉換代碼見下邊,就是代碼運行起來很慢,想看看大家是否有優化方案 6 | 7 | ``` 8 | Number of segment pairs = 182; number of pairwise comparisons = 40 9 | '+' means given segment; '-' means reverse complement 10 | 11 | Overlaps Containments No. of Constraints Supporting Overlap 12 | 13 | ******************* SCL279Contig1 ******************** 14 | whvs7e09.R+ 15 | 24990481+ 16 | ******************* SCL279Contig2 ******************** 17 | et|RFL_Contig5917+ 18 | 24993123+ is in et|RFL_Contig5917+ 19 | whsctal27f01.R+ is in et|RFL_Contig5917+ 20 | whxn27054l15.R+ is in et|RFL_Contig5917+ 21 | whsctal3n06.F- is in et|RFL_Contig5917+ 22 | whthkles18l09.R+ is in et|RFL_Contig5917+ 23 | whsctal3n06.R+ is in et|RFL_Contig5917+ 24 | 32771503+ is in whsctal3n06.R+ 25 | 32678311+ is in et|RFL_Contig5917+ 26 | whxn27054l15.F- 27 | 28 | DETAILED DISPLAY OF CONTIGS 29 | ******************* SCL279Contig1 ******************** 30 | . : . : . : . : . : . : 31 | whvs7e09.R+ CCGCAGGGGTCGCGGCCGAGCAGGGCGGCGCCGGCGACGAGCTCTCCGCTCTGTTCAAGG 32 | ____________________________________________________________ 33 | consensus CCGCAGGGGTCGCGGCCGAGCAGGGCGGCGCCGGCGACGAGCTCTCCGCTCTGTTCAAGG 34 | 35 | . : . : . : . : . : . : 36 | whvs7e09.R+ AGTTCTCGCTAGACAGCAGCAGCACCTTCGCCGAGGCGCGGATCCGGGCCACCTTCTACC 37 | ____________________________________________________________ 38 | consensus AGTTCTCGCTAGACAGCAGCAGCACCTTCGCCGAGGCGCGGATCCGGGCCACCTTCTACC 39 | 40 | . : . : . : . : . : . : 41 | whvs7e09.R+ CCAAGTTCGAGAACGAGGAATCCGACCAGGAGTCAAGAACCCGGATGATTGAGATGGTGT 42 | ____________________________________________________________ 43 | consensus CCAAGTTCGAGAACGAGGAATCCGACCAGGAGTCAAGAACCCGGATGATTGAGATGGTGT 44 | 45 | . : . : . : . : . : . : 46 | whvs7e09.R+ CACAAGGATTAGCTACCATGGAGGTTACGCTCAAGCATTCAGGGTCTTTGTTCATGTATG 47 | 24990481+ TTCATGTATG 48 | ____________________________________________________________ 49 | consensus CACAAGGATTAGCTACCATGGAGGTTACGCTCAAGCATTCAGGGTCTTTGTTCATGTATG 50 | 51 | . : . : . : . : . : . : 52 | whvs7e09.R+ CTGGTAACCGTGGTGGTGCATATGCCAAGAACAGCTTTGGAAATATCTACACTGCTGTGG 53 | 24990481+ CTGGTAACCGTGGTGGTGCATATGCCAAGAACAGCTTTGGAAATATCTACACTGCTGTGG 54 | ____________________________________________________________ 55 | consensus CTGGTAACCGTGGTGGTGCATATGCCAAGAACAGCTTTGGAAATATCTACACTGCTGTGG 56 | 57 | . : . : . : . : . : . : 58 | whvs7e09.R+ GCGTTTTTGTTTTGGGTCGCTTGTTTCGTGAAGCTTGGGGGAGAGAAGCTCCTAAAATGC 59 | 24990481+ GCGTTTTTGTTTTGGGTCGCTTGTTTCGTGAAGCTTGGGGGAGAGAAGCTCCTAAAATGC 60 | ____________________________________________________________ 61 | consensus GCGTTTTTGTTTTGGGTCGCTTGTTTCGTGAAGCTTGGGGGAGAGAAGCTCCTAAAATGC 62 | 63 | . : . : . : . : . : . : 64 | whvs7e09.R+ AAGCGGAATTCAATGATTGTCTCGAGAAAAACCGAATAAGCATTTCAATGGAACTTGTCA 65 | 24990481+ AAGCGGAATTCAATGATTGTCTCGAGAAAAACCGAATAAGCATTTCAATGGAACTTGTCA 66 | ____________________________________________________________ 67 | consensus AAGCGGAATTCAATGATTGTCTCGAGAAAAACCGAATAAGCATTTCAATGGAACTTGTCA 68 | 69 | . : . : . : . : . : . : 70 | whvs7e09.R+ CGGCTGTATTAGGAGACCATGGGCAAAGGCCTAAGGATGATTATGCTGTGATTACAGCTG 71 | 24990481+ CGGCTGTATTAGGAGACCATGGGCAAAGGCCTAAGGATGATTATGCTGTGATTACAGCTG 72 | ____________________________________________________________ 73 | consensus CGGCTGTATTAGGAGACCATGGGCAAAGGCCTAAGGATGATTATGCTGTGATTACAGCTG 74 | 75 | 76 | Number of segment pairs = 600; number of pairwise comparisons = 172 77 | '+' means given segment; '-' means reverse complement 78 | 79 | Overlaps Containments No. of Constraints Supporting Overlap 80 | 81 | ******************* SCL292Contig1 ******************** 82 | whsctal16b21.F+ 83 | 9428019- is in whsctal16b21.F+ 84 | whxq28060f16.F+ is in whsctal16b21.F+ 85 | whxq29060f16.F+ is in whxq28060f16.F+ 86 | whxn14056i15.F+ 87 | whxq28060f16.R- 88 | 9362629- is in whxq28060f16.R- 89 | whxq29060f16.R- 90 | ******************* SCL292Contig2 ******************** 91 | et|tplb0013k02+ 92 | whthls7l03.R+ is in et|tplb0013k02+ 93 | 9561217+ is in et|tplb0013k02+ 94 | 9561363+ is in et|tplb0013k02+ 95 | 93033944+ is in 9561363+ 96 | 14317289+ is in et|tplb0013k02+ 97 | 32659187+ is in et|tplb0013k02+ 98 | 32663705+ is in et|tplb0013k02+ 99 | 93191970+ is in et|tplb0013k02+ 100 | 32786704+ is in et|tplb0013k02+ 101 | whsctal16b21.R+ is in 32786704+ 102 | 33217630+ is in et|tplb0013k02+ 103 | whxn14056i15.R+ is in 33217630+ 104 | 55676375+ is in et|tplb0013k02+ 105 | 93032669- is in et|tplb0013k02+ 106 | whv16n6d15.F- is in 93032669- 107 | whv16n6d15.R+ is in 93032669- 108 | 109 | DETAILED DISPLAY OF CONTIGS 110 | ******************* SCL292Contig1 ******************** 111 | . : . : . : . : . : . : 112 | whsctal16b21.F+ GGCATACTATAGCATCATTGTGGTCTGGAAACATTGGAGGGCTATAATGAAAAAAAATAC 113 | ____________________________________________________________ 114 | consensus GGCATACTATAGCATCATTGTGGTCTGGAAACATTGGAGGGCTATAATGAAAAAAAATAC 115 | 116 | . : . : . : . : . : . : 117 | whsctal16b21.F+ TAAATTGAGTTGAAGTCCAAGGAATTAGTGCCATACAACAACTGAAACTTTCTGGTGCTA 118 | 9428019- AGTGCCATACAACAACTGAAACTTTCTGGTGCTA 119 | whxq28060f16.F+ TCAAGTCCAAGGAATTAGTGCCATACAACAACTGAAACTTTCTGGTGCTA 120 | whxq29060f16.F+ TCAAGTCCAAGGAATTAGTGCCATACAACAACTGAAACTTTCTGGTGCTA 121 | whxn14056i15.F+ CCATACAACAACTGAAACTTTCTGGTGCTA 122 | ____________________________________________________________ 123 | consensus TAAATTGAGTTCAAGTCCAAGGAATTAGTGCCATACAACAACTGAAACTTTCTGGTGCTA 124 | 125 | . : . : . : . : . : . : 126 | whsctal16b21.F+ CTTACACCTGGGTCAGGCTCTTGCAGAGCTGGAGCAAATTTGTAGCTCAGCGTTGCAATG 127 | 9428019- CTTACACCTGGGTCAGGCTCTTGCAGAGCTGGAGCAAATTTGTAGCTCAGCGTTGCAATG 128 | whxq28060f16.F+ CTTACACCTGGATCAGGCTCGTGCTGAGCTGGAGCAAATTTGTAGCTCAGCATTGCAATG 129 | whxq29060f16.F+ CTTACACCTGGATCAGGCTCGTGCTGAGCTGGAGCAAATTTGTAGCTCAGCATTGCAATG 130 | whxn14056i15.F+ CTTACACCTGGATCAGGCTCGTGCTGAGCTGGAGCAAATTTGTAGCTCAGCATTGCAATG 131 | whxq28060f16.R- ATCAGGCTCGTGCTGAGCTGGAGCAAATTTGTAGCTCAGCATTGCAATG 132 | whxq29060f16.R- ATCAGGCTCGTGCTGAGCTGGAGCAAATTTGTAGCTCAGCATTGCAATG 133 | ____________________________________________________________ 134 | consensus CTTACACCTGGATCAGGCTCGTGCTGAGCTGGAGCAAATTTGTAGCTCAGCATTGCAATG 135 | ``` 136 | 137 | 需要的結果是: 138 | 139 | ``` 140 | SCL279Contig1 whvs7e09.R 24990481 141 | SCL279Contig2 et|RFL_Contig5917 24993123 whsctal27f01.R whxn27054l15.R whsctal3n06.F whthkles18l09.R whsctal3n06.R 32771503 32678311 whxn27054l15.F- 142 | SCL292Contig1 whsctal16b21.F 9428019 whxq28060f16.F whxq29060f16.F whxn14056i15.F whxq28060f16.R 9362629 whxq29060f16.R 143 | SCL292Contig2 et|tplb0013k02 whthls7l03.R 9561217 9561363 93033944 14317289 32659187 32663705 93191970 32786704 whsctal16b21.R 33217630 whxn14056i15.R 55676375 93032669 whv16n6d15.F whv16n6d15.R 144 | ``` 145 | 146 | 我寫的代碼: 147 | 148 | ```python 149 | # encoding: utf-8 150 | 151 | with open('1.txt', 'r') as f: 152 | a = [] 153 | b = [] 154 | for num, line in enumerate(f): 155 | if 'Overlaps' in line: 156 | a.append(num) 157 | if 'DETAILED' in line: 158 | b.append(num) 159 | f.seek(0, 0) 160 | for i, j in zip(a, b): 161 | lines = f.readlines()[i:j+1] #读取指定的行数 162 | if '**' in ''.join(lines): 163 | for i in range(len(lines)): #下边就是对获取的行数进行格式转换 164 | new = lines[i].strip().split() 165 | if '*******************' in new: 166 | print '\n' + new[1], 167 | elif len(new) == 1: 168 | print new[0][:-1], 169 | elif 'in' in new: 170 | print new[0][:-1], 171 | f.seek(0, 0) 172 | ``` 173 | 174 | 結果大概有10萬條,上述代碼運行了6個小時還沒運行完,哪位大俠有優化的好點子嗎?謝謝指點 175 | 176 | 問題出自 [segmentfault](), by [biopython](https://segmentfault.com/u/biopython) 177 | 178 | ## 回答 179 | 180 | 對於你的轉換細節我不了解,所以這邊我不多做考慮,純粹就你的代碼上的問題來提出一些可能增進效能的建議。 181 | 182 | 首先看的出來你採取的是二段式的處理手法。 183 | 184 | 1. 先 parse 過整個檔案,找出包含關鍵字 `'Overlaps'` 和 `'DETAILED'` 的行數,並將行序記錄下來。 185 | 2. 對於每一個由 `'Overlaps'` 和 `'DETAILED'` 劃分出來的區間,進行文本的轉換處理。 186 | 187 | 這種兩段式的處理手法本來就會造成效率的低落,因為大部分的轉換工作都可以一段式的完成,我們應該盡可能地邊讀邊處理。 188 | 189 | 不過兩段式的處理還不是造成跑不完的關鍵主因,主要還是你每處理一個 **區間** 就必須要重讀整個文件! 190 | 191 | ```python 192 | for i, j in zip(a, b): # 對每個區間 193 | lines = f.readlines()[i:j+1] #读取指定的行数 194 | # 處理該區間 195 | ``` 196 | 197 | 你會發現從頭到尾你讀了這個文件 `len(a)+1` 次。對於十萬行的文件來說,這實在是太傷了。 198 | 199 | 那應該怎麼辦呢? 下面也許是一個可行的辦法: 200 | 201 | ```python 202 | with open('1.txt', 'r') as f: 203 | flag = False # 利用 flag 來判斷是否在區間內 204 | for line in f: # 此處也不需要 num 了 205 | if 'Overlaps' in line: 206 | flag = True # 進入區間 207 | if flag: 208 | new = line.strip().split() 209 | if '*******************' in new: 210 | print '\n' + new[1], 211 | elif len(new) == 1: 212 | print new[0][:-1], 213 | elif 'in' in new: 214 | print new[0][:-1], 215 | if 'DETAILED' in line: 216 | flag = False # 區間結束 217 | ``` 218 | 219 | 這個代碼從頭到尾只讀了文件一次,對於區區十萬行的讀檔跟文件轉換,Python 絕對沒什麼問題,基本上也不需要任何平行的處理。 220 | 221 | 另外我在這篇中的[回答][1]可能對你也有幫助,你可以參考看看,加油。 222 | 223 | [1]: https://segmentfault.com/q/1010000005137716/a-1020000005138325 224 | -------------------------------------------------------------------------------- /questions/file/用Python實現類似grep的功能.md: -------------------------------------------------------------------------------- 1 | # 用 Python 實現類似 grep 的功能 2 | 3 | ## 問題 4 | 5 | 一個草圖: 6 | 7 | ![图片描述][1] 8 | 9 | 現實現在文件夾和子文件夾下查找目標字串, 10 | 但不知如何提取包含目標字符的字串,並寫入到新文件中。 11 | [1]: https://segmentfault.com/img/bVyX2e 12 | 13 | ```python 14 | #!/usr/bin/env python 15 | #-*- coding:utf-8 -*- 16 | import os, sys 17 | import fnmatch 18 | 19 | listonly = False 20 | skipexts = ['.js'] 21 | def visitfile(fname,searchkey): 22 | global fcount,vcount 23 | try: 24 | if not listonly: 25 | if os.path.splitext(fname)[1] in skipexts: 26 | if open(fname).read().find(searchkey) != -1: 27 | print '%s has %s '%(fname,searchkey) 28 | fcount+=1 29 | except: pass 30 | vcount +=1 31 | 32 | def visitor(args,directoryName,filesInDirectory): 33 | for fname in filesInDirectory: 34 | # 返回文件所在路径和文件名 35 | fpath = os.path.join(directoryName,fname) 36 | if not os.path.isdir(fpath): 37 | 38 | visitfile(fpath,args) 39 | 40 | def searcher(startdir,searchkey): 41 | global fcount,vcount 42 | fcount = vcount = 0 43 | os.path.walk(startdir,visitor,searchkey) 44 | 45 | if __name__=='__main__': 46 | # root=raw_input("type root directory:") 47 | root = '/home/jiangbin/findJS' 48 | key=raw_input("type key:") 49 | searcher(root,key) 50 | print 'Found in %d files,visited %d'%(fcount,vcount) 51 | 52 | ``` 53 | 54 | run 55 | 56 | ``` 57 | type key:JSQ 58 | /home/jiangbin/findJS/XXX.js has JSQ 59 | /home/jiangbin/findJS/JSQ.js has JSQ 60 | Found in 2 files,visited 19 61 | ``` 62 | 63 | 問題出自 [segmentfault](https://segmentfault.com/q/1010000005911595/a-1020000005912159), by [jiangbingo](https://segmentfault.com/u/jiangbingo) 64 | 65 | ## 回答 66 | 67 | 如果你用的是 linux,那我建議你用 `grep` 就好了: 68 | 69 | ``` 70 | $ ls mydir 71 | a.js b.js c.js 72 | ``` 73 | ``` 74 | $ grep JSQ mydir/*.js 75 | mydir/a.js:abcdefg JSQ abcdefg 76 | mydir/a.js:JSQ abcdefg abcdefg 77 | mydir/a.js:abcdefg abcdefg JSQ 78 | mydir/c.js:abcdefg JSQ abcdefg 79 | mydir/c.js:JSQ abcdefg abcdefg 80 | mydir/c.js:abcdefg abcdefg JSQ 81 | ``` 82 | 83 | (上面的例子裡,第一行的顯示有點問題,應該是這樣:`grep JSQ mydir/*.js`) 84 | 85 | 你也可以導到文件裡: 86 | 87 | ``` 88 | $ grep JSQ mydir/* > results.txt 89 | ``` 90 | 91 | 然後你再從 `results.txt` 中去整理和統計數據。 92 | 93 | 如果你堅持想要使用 Python,我寫了一個應該是比較優化的代碼,你可以參考一下: 94 | 95 | ```python 96 | import os 97 | import glob 98 | 99 | def search(root, key, ftype='', logname=None): 100 | ftype = '*.'+ftype if ftype else '*' 101 | logname = logname or os.devnull 102 | symbol = os.path.join(root, ftype) 103 | fnames = glob.glob(symbol) 104 | vc = len(fnames) 105 | fc = 0 106 | 107 | with open(logname, 'w') as writer: 108 | for fname in fnames: 109 | found = False 110 | with open(fname) as reader: 111 | for idx, line in enumerate(reader): 112 | line = line.strip() 113 | if key in line.split(): 114 | line = line.replace(key, '**'+key+'**') 115 | found = True 116 | print('{} -- {}: {}'.format(fname, idx, line), file=writer) 117 | if found: 118 | fc = fc + 1 119 | print('{} has {}'.format(fname, key)) 120 | 121 | return vc, fc 122 | ``` 123 | 124 | `search(root, key, ftype='', logname=None)` 125 | * 會在 `root` 這個 path 底下 126 | * 尋找副檔名為 `ftype` 的文件(如果沒給則全部的文件都接受) 127 | * 在裡面搜尋是否包含 `key` 這個關鍵字 128 | * 如果有給 `logname`,則會輸出關鍵字前後用 `'**'` highlight 的 log 文件,內容是包含該關鍵字的每一行 129 | 130 | 實際上可以這樣用(`search.py`): 131 | 132 | ```python 133 | if __name__=='__main__': 134 | root = 'mydir' 135 | key = input("type key: ") 136 | vc, fc = search(root, key, 'js', logname='results') 137 | print('Found in {} files, visited {}'.format(fc, vc)) 138 | ``` 139 | 140 | 運行: 141 | 142 | ``` 143 | $ python3 search.py 144 | type key: JSQ 145 | mydir/c.js has JSQ 146 | mydir/a.js has JSQ 147 | Found in 2 files, visited 3 148 | ``` 149 | 150 | logfile `results`: 151 | 152 | ``` 153 | mydir/c.js -- 0: abcdefg **JSQ** abcdefg 154 | mydir/c.js -- 1: **JSQ** abcdefg abcdefg 155 | mydir/c.js -- 2: abcdefg abcdefg **JSQ** 156 | mydir/a.js -- 0: abcdefg **JSQ** abcdefg 157 | mydir/a.js -- 1: **JSQ** abcdefg abcdefg 158 | mydir/a.js -- 2: abcdefg abcdefg **JSQ** 159 | ``` 160 | -------------------------------------------------------------------------------- /questions/fp/sum函數中可以使用條件語句嗎.md: -------------------------------------------------------------------------------- 1 | # sum 函數中可以使用條件語句嗎? 2 | 3 | ## 問題 4 | 5 | 我在學習協同過濾,遇到這樣一段代碼 6 | 7 | ```python 8 | def sim_distance(prefs,person1,person2): 9 | # Get the list of shared_items 10 | si={} 11 | for item in prefs[person1]: 12 | if item in prefs[person2]: si[item]=1 13 | 14 | # if they have no ratings in common, return 0 15 | if len(si)==0: 16 | return 0 17 | 18 | # Add up the squares of all the differences 19 | sum_of_squares=sum([pow(prefs[person1][item]-prefs[person2][item],2) 20 | for item in prefs[person1] if item in prefs[person2]]) 21 | 22 | return 1/(1+sum_of_squares) 23 | ``` 24 | 25 | 比較困惑的是下面這段代碼,為什麼sum裡面可以寫for 循環呢,這個是什麼意思,為什麼我寫了個類似的函數就會報錯 26 | 27 | ```python 28 | sum([pow(prefs[person1][item]-prefs[person2][item],2) 29 | for item in prefs[person1] if item in prefs[person2]]) 30 | ``` 31 | 32 | 問題出自 [segmentfault](https://segmentfault.com/q/1010000005666851/a-1020000005669415), by [pennyboarding](https://segmentfault.com/u/pennyboarding) 33 | 34 | ## 回答 35 | 36 | 這邊的 `for` 或是 `if` 都跟 `sum` 本身沒有什麼關係,如同 @大鹌鹑 所說,`sum` 接受一個可迭代的對象作為參數. 37 | 38 | 至於這個例子中的可迭代對象就一個 **使用 list comprehension 產生的 list**。 39 | 40 | ### list comprehension 41 | 42 | 那就稍為介紹一下 list comprehension(串列產生式) 好了。 43 | 44 | 這是一個帶有 **functional programming** 味道的語法,直覺而優雅。 45 | 46 | 顧名思義,他是為了產生 **串列** 而被使用。 47 | 48 | 因此有一個重要的原則就是: 49 | 50 | > 當今天代碼的目的是為了要生成一個 list ,那我們應當考慮使用他,否則完全不應該使用 51 | 52 | 我們來看一下用法,非常簡單的,為了生成串列,所以我們字面上使用兩個成對的中括號 `[]` ( list 的字面產生語法),夾住一個 `for...in...` 迭代式,利用 `for` 走訪到的元素會被依序用來製造 list 中的各個元素。 53 | 54 | 讓我們看例子,假設今天我們有一個整數的串列 `lst`,我們想要製造另外一個串列 `lst2`,其中的每個元素都是 `lst` 中元素的平方: 55 | 56 | ```python 57 | lst = [1, 2, 3, 4] 58 | lst2 = [] 59 | 60 | for i in lst: 61 | lst2.append(i**2) 62 | ``` 63 | 64 | 我們使用了一個標準的 `for...in...` 迴圈來作到這件事,但同樣的事情,我們能夠用 **list comprehension** 作得更簡潔更優雅: 65 | 66 | ```python 67 | lst = [1, 2, 3, 4] 68 | lst2 = [i**2 for i in lst] 69 | ``` 70 | 71 | 在這個例子中,`for i in lst` 會依序取出 `lst` 中的元素進行平方運算後成為 `lst2` 的新元素。 72 | 73 | ### map 74 | 75 | 這讓人聯想到 `map` function,我們同樣可以使用 **映射** 來作到類似的效果: 76 | 77 | ```python 78 | lst = [1, 2, 3, 4] 79 | lst2 = map(lambda x:x**2, lst) # Python2 80 | lst2 = list(map(lambda x:x**2, lst)) # Python3 81 | ``` 82 | 83 | `map` 會依序走訪他第二個參數(一個可迭代對象)中的元素,並且將元素作為引數,調用他的第一個參數(一個單參數函數),也就是會依序取出 1, 2, 3 ,4 然後將之當成參數 `x` 調用匿名函數 `lambda x:x**2`。 84 | 85 | 但我們可以發現 list comprehension 更加直覺,我們可以說 list comprehension 中的 `for`述句就是在 `map` 的良好替代品。 86 | 87 | ### filter 88 | 89 | 說到 `map` 就會想到 `filter`,他會對可迭代物件進行過濾的動作。 90 | 91 | 比如說我想讓 `lst2` 裡面只出現奇數: 92 | 93 | ```python 94 | lst = [1, 2, 3, 4] 95 | lst2 = filter(lambda x:x%2!=0, map(lambda x:x**2, lst)) # Python2 96 | lst2 = list(filter(lambda x:x%2!=0, map(lambda x:x**2, lst))) # Python3 97 | ``` 98 | 99 | `filter` 一樣會去走訪他的第二個參數(一個可迭代對象),並依序取出當成引數,調用他的第一個參數(一個單參數函數),若運算的結果為真( `True` ),則保留此回傳值作為新的元素,反之( `False` )會被過濾掉。 100 | 101 | 而現在 `filter` 也有了替代品,就是 list comprehension,我們可以這樣寫: 102 | 103 | ```python 104 | lst = [1, 2, 3, 4] 105 | lst2 = [i**2 for i in lst if i%2!=0] 106 | ``` 107 | 108 | 同樣也是簡單許多! 我們可以說 list comprehension 中的 `if` 述句就是 `filter` 的良好替代品。 109 | 110 | 看到這裡,相信你已經明白: 111 | 112 | ```python 113 | sum([pow(prefs[person1][item]-prefs[person2][item],2) 114 | for item in prefs[person1] if item in prefs[person2]]) 115 | ``` 116 | 117 | 這段代碼,是先執行了一個含有 `for...in...` 述句和 `if` 述句的 list comprehension 來產生 list 後,才以該 list 作為引數調用 `sum` 函數。 118 | 119 | ### 結論 120 | 121 | 1. `for...in...` 和 `if` 與 `sum` 沒有直接關係。 122 | 2. `for...in...` 和 `if` 是 list comprehension 的關鍵語法。 123 | 3. list comprehension 可以幫助我們使用可迭代對象產生 list。 124 | 4. list comprehension 是 `map` 和 `filter` 的良好替代品。 125 | -------------------------------------------------------------------------------- /questions/ide/Python能否在保存程序變量情況下啟動控制台.md: -------------------------------------------------------------------------------- 1 | # Python 能否在保存程序變量情況下啟動控制台 2 | 3 | ## 問題 4 | 5 | 類似於 matlab 可以在程序完成後直接在控制台操作變量,而不是啟動一個獨立的控制台程序,不知道有沒有哪款 Python 的 ide 支持這種行為 6 | 7 | 問題出自 [segmentfault](https://segmentfault.com/q/1010000005897684/a-1020000005906439), by [mzmssg](https://segmentfault.com/u/mzmssg) 8 | 9 | ## 回答 10 | 11 | Python 自帶的 IDLE 就可以做到了,你開啟一個 python file 後 run module,你會發現主控台上可以操控 file 中的 variable 12 | 13 | ----- 14 | 15 | Pycharm 的部分我自己試了一下,你進到 `Run/Edit Configurations...` 16 | 17 | ![图片描述][1] 18 | 19 | 然後把 `Interpreter options` 加入 `-i` 的選項: 20 | 21 | ![图片描述][2] 22 | 23 | 之後運行 script 完畢,shell 會 keep 在那邊不會結束 24 | 25 | ----- 26 | 27 | 其實不需要 ide 就可以做到你想做的了 28 | 29 | 假設你有一個 python script `test.py` 30 | 31 | ```python 32 | a = 5 33 | b = [1, 2, 3] 34 | ``` 35 | 36 | 直接用: 37 | 38 | ``` 39 | $ python -i test.py 40 | ``` 41 | 42 | 運行 `test.py` 完畢後,Python 會停在 console 中可以繼續互動 43 | 44 | 或是用: 45 | 46 | ``` 47 | $ python 48 | ``` 49 | 50 | 開啟 python shell 後,用 `import` 匯入 test 並執行,接著你就可以操控 variable 了: 51 | 52 | ``` 53 | >>> from test import * 54 | >>> a 55 | 5 56 | >>> b 57 | [1, 2, 3] 58 | ``` 59 | 60 | 這也有同樣效果 61 | 62 | [1]: https://segmentfault.com/img/bVyYF6 63 | [2]: https://segmentfault.com/img/bVyYFI 64 | -------------------------------------------------------------------------------- /questions/if/if 語句的 and or 運算.md: -------------------------------------------------------------------------------- 1 | # if 語句的 and or 運算 2 | 3 | ## 問題 4 | 5 | 最近想提取出特定的 URL,遇到問題為預期提取出 URL 中帶有 `webshell` 或者 `phpinfo` 字段的 URL,但是全部 URL 都匹配出來了: 6 | 7 | ```python 8 | for url in urls: 9 | if "webshell" or "phpinfo" in url: 10 | print url 11 | ``` 12 | 13 | ![图片描述][1] 14 | 15 | 改成 and 語句也不符合預期,只提取出了含有 `phpinfo` 的 url: 16 | 17 | ```python 18 | for url in urls: 19 | if "webshell" and "phpinfo" in url: 20 | print url 21 | ``` 22 | 23 | ![图片描述][2] 24 | 25 | [1]: https://segmentfault.com/img/bVzaNa 26 | [2]: https://segmentfault.com/img/bVzaNH 27 | 28 | 問題出自 [segmentfault](https://segmentfault.com/q/1010000005960652/a-1020000005960714), by [daisydydy](https://segmentfault.com/u/daisydydy) 29 | 30 | ## 回答 31 | 32 | ```python 33 | if "webshell" or "phpinfo" in url: 34 | ``` 35 | 36 | 這樣做的意思是 `if "webshell"` or `if "phpinfo" in url` 而前者恆成立。 37 | 38 | ```python 39 | if "webshell" and "phpinfo" in url: 40 | ``` 41 | 42 | 這樣做的意思是 `if "phpinfo" in url` 因為 `if "webshell"` 恆成立。 43 | 44 | 解法基本上如 @洛克 所說: 45 | 46 | ```python 47 | for url in urls: 48 | if "webshell" in url or "phpinfo" in url: 49 | print url 50 | ``` 51 | 52 | 如果今天用來匹配的 word 很多的話: 53 | 54 | ```python 55 | urls = [ 56 | 'https://www.example.com/aaa', 57 | 'https://www.example.com/bbb', 58 | 'https://www.example.com/ccc', 59 | ] 60 | 61 | 62 | def urlcontain(url, lst): 63 | return any(seg for seg in url.split('/') if seg and seg in lst) 64 | 65 | for url in urls: 66 | if urlcontain(url, ['aaa', 'bbb']): 67 | print(url) 68 | ``` 69 | 70 | 結果: 71 | 72 | ``` 73 | https://www.example.com/aaa 74 | https://www.example.com/bbb 75 | ``` 76 | 77 | `urlcontain(url, lst)` 可以問 `url` 裡面是不是有 `lst` 裡面的任何一個 string 78 | 79 | 這樣子要比對十個關鍵字也不會寫出太長的 if 述句。 80 | 81 | 當然要用 `re` 也可以,只是我個人不太喜歡 `re` 就是了... 82 | -------------------------------------------------------------------------------- /questions/import/deepcopy無法import.md: -------------------------------------------------------------------------------- 1 | # deepcopy 無法 import 2 | 3 | ## 問題 4 | 5 | ![clipboard.png](https://segmentfault.com/img/bVzN19) 6 | 7 | ![clipboard.png](https://segmentfault.com/img/bVzN2v) 8 | 9 | 本來一個叫 `copy` 的文件,我後來改了名字還是這樣 10 | 11 | 問題出自 [segmentfault](https://segmentfault.com/q/1010000006111497/a-1020000006112397), by [yijiangnan](https://segmentfault.com/u/yijiangnan) 12 | 13 | ## 回答 14 | 15 | 看你的意思應該是原本的文件名叫做 `copy.py`: 16 | 17 | 然後出現: 18 | 19 | ```python 20 | ImportError: cannot import name deepcopy 21 | ``` 22 | 23 | 接著你改了名字還是這樣, 我不知道你的 **還是這樣** 是 24 | 25 | 1. 還是有問題 或是 26 | 2. 還是同一個問題 27 | 28 | 我整個給你以下建議: 29 | 30 | 1. 你自己寫的 .py 文件不要取名跟要使用到的 pkg 或 module 一樣 31 | 2. 刪除該目錄下的 .pyc 文件 (這可能是讓你 `import copy` 持續出錯的原因) 32 | 3. 你使用 `copy` 和 `deepcopy` 的方式有誤, 請參考 @prolifes 的用法 33 | 34 | ### prolifes 的說明 35 | 36 | ```python 37 | import copy 38 | c = copy.copy(d) 39 | dc = copy.deepcopy(d) 40 | ``` 41 | -------------------------------------------------------------------------------- /questions/import/通過哪個函數能查看Python文件中匯入了哪些模組.md: -------------------------------------------------------------------------------- 1 | # 通過哪個函數能查看 Python 文件中匯入了哪些模組? 2 | 3 | ## 問題 4 | 5 | 通過哪個函數能查看 Python 文件中匯入了哪些模組? 6 | 7 | ```python 8 | from A import B 9 | from AA import BB 10 | ``` 11 | 12 | 如以上代碼,我如何在這個腳本中通過函數,返回 13 | 14 | ```python 15 | [B, BB] 16 | ``` 17 | 18 | 問題出自 [segmentfault](https://segmentfault.com/q/1010000005673457/a-1020000005675400), by [yangeren](https://segmentfault.com/u/hanz) 19 | 20 | ## 回答 21 | 22 | 定義如下的 function: 23 | 24 | ```python 25 | def find_import(): 26 | return {key:value for key, value in globals().items() 27 | if isinstance(value, type(sys)) and not key.startswith('__')} 28 | ``` 29 | 30 | 函數 `find_import()` 會返回一個字典,以被匯入的: 31 | 32 | * **module name**為 key 33 | * **module instance**為 value。 34 | 35 | **測試**: 36 | 37 | ```python 38 | import sys 39 | from os import path 40 | 41 | def find_import(): 42 | return {key:value for key, value in globals().items() 43 | if isinstance(value, type(sys)) and not key.startswith('__')} 44 | 45 | for key, value in find_import().items(): 46 | print key, value 47 | ``` 48 | 49 | **結果**: 50 | 51 | ```python 52 | sys 53 | path 54 | ``` 55 | -------------------------------------------------------------------------------- /questions/iteration/Python 如何合併 list of lists.md: -------------------------------------------------------------------------------- 1 | # Python 如何合併 list of lists 2 | 3 | ## 問題 4 | 5 | ```python 6 | for i in open (v): 7 | _temp = i.split('-') 8 | self._i= gen.gen(_temp[0], _temp[1]) 9 | ``` 10 | 11 | `self._i` 中是 list of lists,怎樣合併成一個? 12 | 13 | 問題出自 [segmentfault](https://segmentfault.com/q/1010000005904259), by [懒洋洋](https://segmentfault.com/u/lanyangyang) 14 | 15 | ## 回答 16 | 17 | 如果你的意思是要合併多個 list 為一個,那使用 `itertools.chain` 來串接是最好的,以下是個簡單的範例: 18 | 19 | ```python 20 | >>> from itertools import chain 21 | >>> a = [1,2,3] 22 | >>> b = [4,5,6] 23 | >>> c = [7,8,9] 24 | >>> chain(a,b,c) 25 | 26 | >>> list(chain(a,b,c)) 27 | [1, 2, 3, 4, 5, 6, 7, 8, 9] 28 | ``` 29 | 30 | 對於你的 case 而言: 31 | 32 | ```python 33 | from itertools import chain 34 | lst = list(chain(*self._i)) 35 | ``` 36 | 37 | --- 38 | 39 | 以下題外話。 40 | 41 | @松林 的方法是可行的,而且效能不會差,在 python 中 **擴增(增強)運算** 和 **一般運算** 的行為不見得完全一模一樣,在這裡我們使用 `+` 來討論。 42 | 43 | 我們看一個例子: 44 | 45 | ```python 46 | >>> lst1 = [1,2,3] 47 | >>> lst2 = [4,5,6] 48 | >>> id(lst1) 49 | 139814427362656 50 | >>> id(lst1 + lst2) 51 | 139814427363088 52 | >>> lst1 += lst2 53 | >>> id(lst1) 54 | 139814427362656 55 | ``` 56 | 57 | 由本例可以發現,`lst1 + lst2` 會產生一個新的 list,但是 `lst1 += lst2` 則不會,因為對於擴增運算,Python **大部分** 會依循以下規則: 58 | 59 | 1. 不可變的型態會經由運算後產生一個新的 object,並且讓變數參考到該 object 60 | 2. 可變的型態會採用 **就地(in-place)運算** 的方式來擴充或更新變數原本參考的 object 61 | 62 | 也就是說 `lst1 += lst2` 等價於 `lst1.extend(lst2)` 63 | 64 | 這取決於該型態是否有實作 `__iadd__`(或 `__imul__` ...) 而不是只有實作 `__add__` (或 `__mul__` ...) 65 | 66 | 對於沒有實作 `__iXXX__` 的型態,Python 會改呼叫 `__XXX__` 代替,這一定會運算出新的 object,但是 `__iXXX__` 則會就地更新原 object 67 | 68 | 也就是說 **大部分** 的: 69 | 70 | 1. 不可變型態都不會實作 `__iXXX__`,因為更新不可變型態的 object 是沒有道理的 71 | 2. 可變型態會實作 `__iXXX__` 來就地更新 72 | 73 | 為什麼我一直強調大部分呢? 74 | 75 | 因為 CPython 中優化了 `str` 的擴增運算,`str += other` 實在太常用了,在串接時,Python 並不會每次都複製字串 76 | -------------------------------------------------------------------------------- /questions/iteration/Python的list有沒有類似js的find方法.md: -------------------------------------------------------------------------------- 1 | # Python 的 list 有沒有類似 js 的 find 方法 2 | 3 | ## 問題 4 | 5 | js 的 array 中的 `find` 寫法很帥 6 | 7 | 假設我有一個 array, 是 array of objects: 8 | 9 | ```javascript 10 | sample_list = [{key: 1, value: 'a'}, {key: 2, value: 'b'}] 11 | ``` 12 | 13 | js 可以這樣找到 `key=1` 的元素: 14 | 15 | ```javascript 16 | sample_list.find((data)=>{return data.key == 1}) 17 | ``` 18 | 19 | python 有沒有類似的東西呢? 20 | 我只看到了 `index`, 但是這個 `index` 只能找值類型的,引用類型的用 `index` 很麻煩. 21 | 有沒有比較好的辦法呢? 22 | 23 | 我現在知道的方法有這幾個: 24 | 25 | ```python 26 | [i for i in sample_list if i.get('key')==1][0] 27 | ``` 28 | 29 | 還有: 30 | 31 | ```python 32 | find_it = None 33 | for i in sample_list: 34 | if i.get('key')==1: 35 | find_it = i 36 | break 37 | ``` 38 | 39 | 但總覺的不如 js 爽. 40 | 41 | 問題出自 [segmentfault](https://segmentfault.com/q/1010000006013101/a-1020000006013138), by [daimon](https://segmentfault.com/u/daimon) 42 | 43 | ## 回答 44 | 45 | ### 分析與比較 46 | 47 | 我們來看一下你給的例子: 48 | (如果我 js 有地方講不對, 請你指正我) 49 | 50 | ```javascript 51 | sample_list.find((data)=>{return data.key == 1}) 52 | ``` 53 | 54 | * `sample_list` 是一個 js array 55 | * `data` 是 js object 56 | 57 | 然後這邊的手法是 `array.find` 配上 arrow function 58 | 59 | 然後 Python 這邊對應的手法是(我改了一下比較好對照): 60 | 61 | ```python 62 | [data for data in sample_list if data.get('key')==1][0] 63 | ``` 64 | 65 | * `sample_list` 是 python list 66 | * `data` 是 python dictionary 67 | 68 | 手法是 list comprehension 69 | 70 | 其實 js 的 arrow function 就類似於 Python 的 lambda function, `find` 手法也類似於 `filter`: 71 | 72 | ```python 73 | filter(lambda data: data.get('key')==1, a)[0] # Python2 74 | list(filter(lambda data: data.get('key')==1, a))[0] # Python3 75 | ``` 76 | 77 | 首先, 基本上手法所差無幾, 再來, 用 Python 的人都應該知道 list comprehension 的表達力比起 `filter` 是好上不少, 所以其實我覺得: 78 | 79 | ```python 80 | [data for data in sample_list if data.get('key')==1][0] 81 | ``` 82 | 83 | 的閱讀性其實更佳, 而且其實這樣的寫法你會發現你少寫了一個 lambda function(arrow function) 84 | 85 | ### 結論 86 | 87 | 我覺得這樣沒有比較差啊! 88 | 89 | ### 題外話(寫給 Python 愛好者) 90 | 91 | Python 的 list 的確沒有直接相等於 js `array.find` 的方法, 所以多數時候的 **filter 查找** 都必須依賴 `filter` 或 list comprehension 或 generator expression. 92 | 93 | 但是如果我們只是要查找第一個符合條件的元素, 使用 list comprehension 似乎有一點點浪費(但其實 90% 的情況其實都無關緊要), 可是如果我們使用 generator expression 或是 Python3 的 `filter` 的話( Python3 的 filter 會返回一個 generator), 要怎麼取得第一個符合條件的項目呢? 94 | 95 | 如果你把他轉成 list 再取第一個: 96 | 97 | ```python 98 | list((data for data in sample_list if data.get('key')==1))[0] 99 | ``` 100 | 101 | 這樣等於是耗盡了 generator 這個 iterater, 簡單來說你已經 iterate 過所有的項目才找出答案。這樣就跟 list comprehension 沒什麼不同了 102 | 103 | 這邊有一個 trick 是這樣的: 104 | 105 | ```python 106 | next((data for data in sample_list if data.get('key')==1)) 107 | ``` 108 | 109 | 這樣做我們就可以只產生第一個項目, 這也代表了查找會找到第一個找到就停止不會繼續下去走完整個 list 110 | 111 | 這邊如果查找失敗會引發一個 `StopIteration`, 為了處理這種狀況我們可以加一個 default value 用以指出查找失敗: 112 | 113 | ```python 114 | next((data for data in sample_list if data.get('key')==1), None) # 查找失敗會返回 `None` 115 | ``` 116 | -------------------------------------------------------------------------------- /questions/iteration/python中既然生成器表達式比列表解析快, 那為什麼不全部使用生成器表達式.md: -------------------------------------------------------------------------------- 1 | # python 中既然生成器表達式比列表解析快, 那為什麼不全部使用生成器表達式 2 | 3 | ## 問題 4 | 5 | python 中既然生成器表達式比列表解析快?那為什麼不全部使用生成器表達式? 6 | 7 | 問題出自 [segmentfault](https://segmentfault.com/q/1010000006099706), by [昌维001](https://segmentfault.com/u/changwei) 8 | 9 | ## 回答 10 | 11 | generator 的 lazy evaluation (惰性求值) 當然是能盡量用就盡量用, 但有的時候這是不太有意義的, 比如說: 12 | 13 | 1. 最後你還是要一個列表 14 | 2. 你需要排序 15 | 16 | 關於第一點很好明白, generator 只能夠 iterate 卻不具備大部分的 list 能力: 17 | 18 | ```python 19 | >>> I = [5 ,3, 1, 6, 2] 20 | >>> genexp = (i for i in I) 21 | 22 | >>> genexp[0] # generator 不支援 __getitem__ 23 | Traceback (most recent call last): 24 | File "", line 1, in 25 | TypeError: 'generator' object has no attribute '__getitem__' 26 | 27 | >>> genexp + [9, 9, 9] # generator 不支援列表串接 28 | Traceback (most recent call last): 29 | File "", line 1, in 30 | TypeError: unsupported operand type(s) for +: 'generator' and 'list' 31 | ``` 32 | 33 | 諸如此類... 34 | 35 | 那可能觀眾會想問, 那這樣就好了阿: 36 | 37 | ```python 38 | >>> list(genexp) 39 | ``` 40 | 41 | 那這樣子跟直接用 list comprehension 有差嗎? (還真的有差! 你白做工了) 42 | 43 | 其實第二點跟第一點類似, 不過你可能會想要反駁: 44 | 45 | ```python 46 | >>> sorted(genexp) 47 | [1, 2, 3, 5, 6] 48 | ``` 49 | 50 | 這不是好好的嗎? 沒錯, sorted 支援任何 iterables 的輸入, 但是 51 | 52 | > 要排序, 所有的項目都必須要被求值 53 | 54 | 也就是說, 惰性求值完全沒有派上用場, 這個時候還不如直接用 list 來的乾脆。 55 | 56 | 最後 @Maslino 有提到一很重要的一點, generator 是會耗盡的, 是一次使用性的, 所以要反覆使用多次的話只能夠依靠 generator function: 57 | 58 | ```python 59 | def gen(): 60 | for i in range(10): 61 | yield i 62 | ``` 63 | 64 | 或者 return genexp 的 function: 65 | 66 | ```python 67 | def gen(): 68 | return (i for i in range(10)) 69 | ``` 70 | 71 | 然後: 72 | 73 | ```python 74 | for i in gen(): 75 | print(i) 76 | ``` 77 | 78 | 這樣就可以反覆多次使用了 79 | 80 | 但這樣做是好還是壞還要看情況而定, 大多數的時候直接用 list 會比較容易 81 | 82 | ### 結論 (有點複雜, 如果看不懂可以再討論) 83 | 84 | **generator** (這裡指一般的 generator 而非 genexp) 用在: 85 | 86 | 1. 追求效率所以希望使用惰性求值, on-the-fly 的 evaluate, 省空間也避免一口氣過多求值 87 | 2. 對於無限無窮的 iteration, 一般的 iterables 是辦不到的 88 | 3. generator 可以憑空生出值來, 一般的 iterator 不行 (generator 也是 iterator) 89 | 90 | 關於 2, 3 兩點我覺得比較不適用在 generator expression 上面, generator expression 我想最大的用途就在於上述的第一點! 91 | 92 | 撇開這三點, 使用 list 可能是比較簡單的, 但 **如果是 list comprehension 跟 generator expression 比** , 那我覺得只需要考慮第一點的情況來決定就好。 93 | 94 | 觀念澄清一下: 95 | 96 | > generator 是 iterator, 反之不一定, iterator 是 iterables, 反之不一定 97 | -------------------------------------------------------------------------------- /questions/iteration/一個求質(素)數的編程題.md: -------------------------------------------------------------------------------- 1 | # 一個求質(素)數的編程題 2 | 3 | ## 問題 4 | 5 | ```python 6 | from math import sqrt 7 | for n in range(101,201): 8 | t = int(sqrt(n)) 9 | for m in range(2,t+1): 10 | if n%m == 0: 11 | break 12 | print n 13 | ``` 14 | 15 | 問題出自 [segmentfault](https://segmentfault.com/q/1010000005774060/a-1020000005774752), by [royanimal](https://segmentfault.com/u/royanimal) 16 | 17 | ## 回答 18 | 19 | 最快的改法: 20 | 21 | ```python 22 | from math import sqrt 23 | 24 | for n in range(101,201): 25 | t = int(sqrt(n)) 26 | for m in range(2,t+1): 27 | if n%m == 0: 28 | break 29 | else: 30 | print n 31 | ``` 32 | 33 | 簡單講問題出在: 34 | 35 | > 你在 `break` 之前每次取除以 `m` 的餘數時,都會印一次 `n` 36 | 37 | 你應該: 38 | 39 | > 確認所有的 `m` 都無法整除 `n` 之後,才印出 40 | 41 | `else` 搭配 `for` 來使用代表的是,如果這個 for loop 沒有因為 `break`,`return` 等中介的控制流程語法或是例外引發而提早中斷的話,就執行 `else` 區塊。 42 | -------------------------------------------------------------------------------- /questions/iteration/如何將列表中的元組整個迭代出來.md: -------------------------------------------------------------------------------- 1 | # 如何將列表中的元組整個迭代出來? 2 | 3 | ## 問題 4 | 5 | 定義一個列表: 6 | 7 | ```python 8 | r = [ (1, 2), ('a', 'f') ] 9 | ``` 10 | 11 | 想用 `for..in..` 迭代來獲取元組。 12 | 13 | 於是用了下面方法: 14 | 15 | ```python 16 | r = [ (1, 2), ('a', 'f') ] 17 | 18 | for x, y in r: 19 | print(x, y) 20 | ``` 21 | 22 | 然而,輸出的結果是: 23 | 24 | ``` 25 | 1 2 26 | 'a' 'f' 27 | ``` 28 | 29 | 我想要迭代出的是整個元組,這樣的效果: 30 | 31 | ``` 32 | (1, 2) ('a', 'f') 33 | ``` 34 | 35 | 該如何去做? 36 | 37 | 問題出自 [segmentfault](https://segmentfault.com/q/1010000005642542/a-1020000005642560), by [x_hola](https://segmentfault.com/u/x_hola) 38 | 39 | ## 回答 40 | 41 | 直接迭代 tuple 而不要去做 unpacking: 42 | 43 | ```python 44 | r = [(1,2), ('a','f')] 45 | 46 | for t in r: 47 | print(t) 48 | ``` 49 | 50 | 因應你在評論下的問題,如果 `r` 中有非 tuple 的東西,可以用 generator expression 並搭配 `isinstance` 的檢查來完成: 51 | 52 | ```python 53 | r = [(1,2), 5, ('a','f')] 54 | tuples = (t for t in r if isinstance(t, tuple)) 55 | 56 | for t in tuples: 57 | print(t) 58 | ``` 59 | 60 | 甚至你也可以檢查是否為雙元素的 tuple: 61 | 62 | ```python 63 | r = [(1,2), 5, ('a','f')] 64 | tuples = (t for t in r if isinstance(t, tuple) and len(t)==2) 65 | 66 | for t in tuples: 67 | print(t) 68 | ``` 69 | -------------------------------------------------------------------------------- /questions/iteration/如何從一個複雜的結構中優雅的提取出一列數據.md: -------------------------------------------------------------------------------- 1 | # 如何從一個複雜的結構中優雅的提取出一列數據 2 | 3 | ## 問題 4 | 5 | 在 python 中,數據呈現以下結構: 6 | 7 | ```python 8 | # 結構說明 9 | list(tuple(a,b,c)) 10 | ``` 11 | 12 | example: 13 | 14 | ```python 15 | [(a0,b0,c0),(a1,b1,c1),(a2,b2,c2)] 16 | ``` 17 | 18 | 現在想使用一種優雅的方法,將 `c` 數據提取出來,變為以下結構: 19 | 20 | ```python 21 | # 結構說明 22 | list(c) 23 | ``` 24 | example: 25 | 26 | ```python 27 | [c0,c1,c2] 28 | ``` 29 | 30 | 個人不太熟悉 python 的特性. 31 | 32 | 這個用循環(迴圈)應該可以做到,但是感覺不太優雅,是否有一種比較 pythonic 的方式? 33 | 34 | 問題出自 [segmentfault](https://segmentfault.com/q/1010000005975906), by [萝莉控吐槽勉](https://segmentfault.com/u/luolikongtucaomian) 35 | 36 | ## 回答 37 | 38 | ```python 39 | lst = [(a0,b0,c0),(a1,b1,c1),(a2,b2,c2)] 40 | lst = [t[2] for t in lst] 41 | ``` 42 | 43 | example: 44 | 45 | ```python 46 | >>> lst = [(1,2,3), (1,2,3), (1,2,3)] 47 | >>> lst = [t[2] for t in lst] 48 | >>> lst 49 | [3, 3, 3] 50 | ``` 51 | -------------------------------------------------------------------------------- /questions/iteration/如何把tuple轉成dictionary.md: -------------------------------------------------------------------------------- 1 | # 如何把 tuple 轉成 dictionary 2 | 3 | ## 問題 4 | 5 | 我有一個 tuple list: 6 | 7 | ![clipboard.png](https://segmentfault.com/img/bVzgCO) 8 | 9 | 現在我想把這組 tuples 變成 dictionary 的 list, 類似的效果是這樣: 10 | 11 | ```python 12 | meat = [ 13 | { 14 | "地區詞":"深圳福田區", 15 | "品牌詞":"TTM全身體檢", 16 | "疑問詞":"地址怎麼走", 17 | "價格詞":"價格要多少錢" 18 | }, 19 | { 20 | "地區詞":"深圳保安區", 21 | "品牌詞":"TTM全身體檢", 22 | "疑問詞":"地址怎麼走", 23 | "價格詞":"要做多少項目" 24 | }, 25 | ..... 26 | ] 27 | ``` 28 | 29 | 有什麼比較方便的方法? 30 | 31 | 問題出自 [segmentfault](https://segmentfault.com/q/1010000005983046/a-1020000005983077), by [jqlts1](https://segmentfault.com/u/jqlts1) 32 | 33 | ## 回答 34 | 35 | ```python 36 | names = 'area brand question price'.split() 37 | lst = [{name:value for name, value in zip(names, t)} for t in tlst] 38 | ``` 39 | 40 | 測試: 41 | 42 | ```python 43 | tlst = [('a1','b1','q1','p1'), 44 | ('a2','b2','q2','p2'), 45 | ('a3','b3','q3','p3')] 46 | 47 | names = 'area brand question price'.split() 48 | lst = [{name:value for name, value in zip(names, t)} for t in tlst] 49 | 50 | print(lst) 51 | ``` 52 | 53 | 結果: 54 | 55 | ```python 56 | [{'brand': 'b1', 'area': 'a1', 'question': 'q1', 'price': 'p1'}, {'brand': 'b2', 'area': 'a2', 'question': 'q2', 'price': 'p2'}, {'brand': 'b3', 'area': 'a3', 'question': 'q3', 'price': 'p3'}] 57 | ``` 58 | 59 | 改良(by [hsfzxjy](https://segmentfault.com/u/hsfzxjy)): 60 | 61 | ```python 62 | names = 'area brand question price'.split() 63 | lst = [dict(zip(names, t)) for t in tlst] 64 | ``` 65 | -------------------------------------------------------------------------------- /questions/jinja/jinja2_macro_caller.md: -------------------------------------------------------------------------------- 1 | # jinja2 macro caller 2 | 3 | ## 問題 4 | 5 | 剛開始學用 jinja 來渲染 html, 遇到個 macro 調用 macro 的問題,就是用 call 來實現,按照官方的文檔的樣例寫的,但是一直報錯。 6 | 7 | 代碼是這樣的: 8 | 9 | ```jinja 10 | {% macro dump_users(users) %} 11 | < ul > 12 | {% for user in users %} 13 | < li > {{ caller(user) }} 14 | {% endfor %} 15 | 16 | {% endmacro %} 17 | 18 | {% call(user) dump_users(users) %} 19 | {{ user }} 20 | {% endcall %} 21 | 22 | {{ dump_users(users) }} 23 | ``` 24 | 25 | 錯誤信息: 26 | 27 | ```python 28 | File "scorePage.html" , line 16 , in template 29 |
  • {{ caller (user) }}
  • 30 | 31 | UndefinedError: No caller defined 32 | ``` 33 | 34 | 查了好多資料,發現都是這麼寫的,,但是我這寫的就報錯。。真的被折磨了好久。。太菜了。。能不能告訴下到底哪裡出問題了。。十分十分感謝!! 35 | 36 | 問題出自 [segmentfault](https://segmentfault.com/q/1010000005352059/a-1020000005352912), by [leohujx](https://segmentfault.com/u/leohujx) 37 | 38 | ## 回答 39 | 40 | ```jinja 41 | {% macro dump_users(users) %} 42 |
      43 | {% for user in users %} 44 |
    • {{ caller(user) }}
    • 45 | {% endfor %} 46 |
    47 | {% endmacro %} 48 | 49 | {% call(user) dump_users(users) %} 50 | {{ user }} 51 | {% endcall %} 52 | 53 | {{ dump_users(users) }} 54 | ``` 55 | 56 | 首先你會發現你定義的 macro `dump_users` 內部需要調用 `caller` 57 | 58 | 但是你在這行: 59 | 60 | ```jinja 61 | {{ dump_users(users) }} 62 | ``` 63 | 64 | 調用 `dump_users` 的時候並沒有利用 `call` 標籤來調用,既然 `dump_users` 不是被 "call" 的,自然也沒有 "caller" 讓他在內部調用囉。 65 | -------------------------------------------------------------------------------- /questions/json/Python如何合併批量輸出json.md: -------------------------------------------------------------------------------- 1 | # Python如何合併批量輸出json 2 | 3 | ## 問題 4 | 5 | A文件夾裡面有: 6 | 7 | ``` 8 | 1.json 9 | 2.json 10 | 3.json 11 | ..... 12 | ``` 13 | 14 | 格式為: 15 | 16 | ```json 17 | { 18 | "hello": "hello", 19 | "data": [ 20 | { 21 | "id": "123456", 22 | "create_time": "2016-03-28 11:41:00", 23 | "phone": "138888****", 24 | "name": "aaa" 25 | }, 26 | { 27 | "id": "456789", 28 | "create_time": "2016-03-28 11:41:00", 29 | "phone": "137777****", 30 | "name": "bbb" 31 | } 32 | ] 33 | } 34 | ``` 35 | 36 | 遍歷 A 文件夾 37 | 合併 data 內容 38 | 輸出 1 個 json 和 csv 39 | 40 | 問題出自 [segmentfault](https://segmentfault.com/q/1010000004893778/a-1020000005151029), by [monkey_cici](https://segmentfault.com/u/monkey_cici) 41 | 42 | ## 回答 43 | 44 | Python2: 45 | 46 | ```python 47 | import os 48 | import csv 49 | import glob 50 | import json 51 | 52 | data = [] 53 | 54 | for fname in glob.glob(os.path.join('A', '*.json')): 55 | with open(fname) as f: 56 | jf = json.loads(f.read()) 57 | data.extend(jf['data']) 58 | 59 | with open('output.json', 'w') as f: 60 | print >>f, json.dumps(data, sort_keys=True, indent=4, separators=(',', ': ')) 61 | 62 | with open('output.csv', 'w') as f: 63 | writer = csv.writer(f) 64 | writer.writerow([key for key, value in data[0].items()]) 65 | for d in data: 66 | writer.writerow([value for key, value in d.items()]) 67 | ``` 68 | 69 | Python3: 70 | ```python 71 | import os 72 | import csv 73 | import glob 74 | import json 75 | 76 | data = [] 77 | 78 | for fname in glob.glob(os.path.join('A', '*.json')): 79 | with open(fname) as f: 80 | jf = json.loads(f.read()) 81 | data.extend(jf['data']) 82 | 83 | with open('output.json', 'w') as f: 84 | print(json.dumps(data, sort_keys=True, indent=4, separators=(',', ': ')), file=f) 85 | 86 | with open('output.csv', 'w') as f: 87 | writer = csv.writer(f) 88 | writer.writerow([key for key, value in data[0].items()]) 89 | for d in data: 90 | writer.writerow([value for key, value in d.items()]) 91 | ``` 92 | 93 | 1. 對於你說的合併,我這邊僅僅是將資料一筆一筆的疊加上去,並沒有作 id 檢查等處理。 94 | 2. csv 的輸出要求,可能也要視你想要的格式進行調整。 95 | 96 | 下面是相關的一些文檔,看完後對你會很有幫助: 97 | 98 | * [glob][1] 99 | * [csv][2] 100 | * [os.path][3] 101 | * [json][4] 102 | 103 | 104 | [1]: https://docs.python.org/2/library/glob.html 105 | [2]: https://docs.python.org/2/library/csv.html 106 | [3]: https://docs.python.org/2/library/os.path.html 107 | [4]: https://docs.python.org/2/library/json.html 108 | -------------------------------------------------------------------------------- /questions/json/Python如何讀取json中的數據.md: -------------------------------------------------------------------------------- 1 | # Python 如何讀取 json 中的數據 2 | 3 | ## 問題 4 | 5 | ```json 6 | { 7 | "errno": "0", 8 | "data": { 9 | "hi_info": { 10 | "login_ip": "127.0.0.1", 11 | "license": { 12 | "id": "123456", 13 | "name": "hello", 14 | "sex": "", 15 | "status": "1" 16 | }, 17 | "hello_info": { 18 | "model": "3", 19 | "license": { 20 | "id": "123456", 21 | "license_no": "" 22 | }, 23 | "upgrade": 1 24 | } 25 | } 26 | } 27 | } 28 | ``` 29 | 30 | 如何讀取自己想要的數據 31 | 32 | 比如: 33 | 34 | * `hi_info` 中的 `login_ip` 35 | * `license` 中的 `name` 36 | * `license` 中的 `id` 37 | 38 | 以及 `data` 裡面所有的數據 39 | 40 | 問題出自 [segmentfault](), by [monkey_cici](https://segmentfault.com/u/monkey_cici) 41 | 42 | ## 回答 43 | 44 | ```python 45 | import json 46 | 47 | with open('ex.json' , 'r') as reader: 48 | jf = json.loads(reader.read()) 49 | 50 | print(jf['data']['hi_info']['login_ip']) 51 | ``` 52 | -------------------------------------------------------------------------------- /questions/json/不定深層Json剖析.md: -------------------------------------------------------------------------------- 1 | # 不定深層 Json 剖析 2 | 3 | ## 問題 4 | 5 | 現有如下數據結構: 6 | 7 | ```python 8 | { 9 | "date": { 10 | 11 | "doc_count_error_upper_bound": 0, 12 | "sum_other_doc_count": 0, 13 | "buckets": [ 14 | { 15 | "key": "2015-02-01", 16 | "doc_count": 547, 17 | "shop": { 18 | "doc_count_error_upper_bound": 0, 19 | "sum_other_doc_count": 0, 20 | "buckets": [ 21 | { 22 | "key": "A", 23 | "doc_count": 251, 24 | "sum_qty": { 25 | "value": 36927 26 | }, 27 | "sum_amt": { 28 | "value": 3651755 29 | } 30 | }, 31 | { 32 | "key": "B", 33 | "doc_count": 178, 34 | "sum_qty": { 35 | "value": 12115 36 | }, 37 | "sum_amt": { 38 | "value": 436019 39 | } 40 | }, 41 | { 42 | "key": "C", 43 | "doc_count": 118, 44 | "sum_qty": { 45 | "value": 8896 46 | }, 47 | "sum_amt": { 48 | "value": 1310549 49 | } 50 | } 51 | ] 52 | } 53 | }, 54 | { 55 | "key": "2015-03-01", 56 | "doc_count": 524, 57 | "shop": { 58 | "doc_count_error_upper_bound": 0, 59 | "sum_other_doc_count": 0, 60 | "buckets": [ 61 | { 62 | "key": "A", 63 | "doc_count": 354, 64 | "sum_qty": { 65 | "value": 212909 66 | }, 67 | "sum_amt": { 68 | "value": 18620841 69 | } 70 | }, 71 | { 72 | "key": "B", 73 | "doc_count": 86, 74 | "sum_qty": { 75 | "value": 40109 76 | }, 77 | "sum_amt": { 78 | "value": 5105368 79 | } 80 | }, 81 | { 82 | "key": "C", 83 | "doc_count": 84, 84 | "sum_qty": { 85 | "value": 28156 86 | }, 87 | "sum_amt": { 88 | "value": 938102 89 | } 90 | } 91 | ] 92 | } 93 | } 94 | ] 95 | } 96 | } 97 | ``` 98 | 99 | 想要剖析成: 100 | 101 | ```python 102 | lst = [ 103 | {'date': '2015-02-01', 'shop': 'A', 'sum_qty': 5, 'sum_amt': 10}, 104 | {'date': '2015-02-01', 'shop': 'B', 'sum_qty': 5, 'sum_amt': 10}, 105 | {'date': '2015-02-01', 'shop': 'C', 'sum_qty': 5, 'sum_amt': 10}, 106 | {'date': '2015-03-01', 'shop': 'A', 'sum_qty': 20, 'sum_amt': 100}, 107 | {'date': '2015-03-01', 'shop': 'B', 'sum_qty': 20, 'sum_amt': 100}, 108 | {'date': '2015-03-01', 'shop': 'C', 'sum_qty': 20, 'sum_amt': 100} 109 | ] 110 | ``` 111 | 112 | 問題出自 [segmentfault](https://segmentfault.com/q/1010000006078915), by [prolifes](https://segmentfault.com/u/prolifes) 113 | 114 | ## 回答 115 | 116 | 寫了一個 class, 結果應該跟你要的一樣: 117 | 118 | ```python 119 | from collections import abc 120 | 121 | class CoolJSON: 122 | 123 | def __init__(self, key, mapping): 124 | """ 125 | key is the main key of this CoolJSON 126 | mapping is the total data 127 | """ 128 | self.key = key 129 | self.mapping = dict(mapping) 130 | 131 | 132 | def collect_bucket_item(self, item): 133 | """ used to handle single bucket item""" 134 | dic = {} 135 | lst = [{}] 136 | 137 | for key, value in item.items(): 138 | if key=='key': 139 | dic[self.key] = value 140 | elif isinstance(value, abc.MutableMapping): 141 | if 'buckets' in value: 142 | lst = CoolJSON(key, value).collect() 143 | elif 'value' in value: 144 | dic[key] = value['value'] 145 | 146 | for item in lst: 147 | item.update(dic) 148 | 149 | return lst 150 | 151 | 152 | def collect(self): 153 | """used to collect results from all bucket items""" 154 | results = [] 155 | for item in self.mapping['buckets']: 156 | results.extend(self.collect_bucket_item(item)) 157 | return results 158 | ``` 159 | 160 | 測試: 161 | 162 | ```python 163 | # 測資使用 prolife 在評論下方新給的測資 164 | 165 | lst = CoolJSON('date', data['date']).collect() 166 | 167 | for item in lst: 168 | print(item) 169 | ``` 170 | 171 | 結果: 172 | 173 | ```python 174 | {'sum_amt': 3651755, 'sum_qty': 36927, 'date': '2015-02-01', 'shop': 'A'} 175 | {'sum_amt': 436019, 'sum_qty': 12115, 'date': '2015-02-01', 'shop': 'B'} 176 | {'sum_amt': 1310549, 'sum_qty': 8896, 'date': '2015-02-01', 'shop': 'C'} 177 | {'sum_amt': 18620841, 'sum_qty': 212909, 'date': '2015-03-01', 'shop': 'A'} 178 | {'sum_amt': 5105368, 'sum_qty': 40109, 'date': '2015-03-01', 'shop': 'B'} 179 | {'sum_amt': 938102, 'sum_qty': 28156, 'date': '2015-03-01', 'shop': 'C'} 180 | ``` 181 | -------------------------------------------------------------------------------- /questions/json/假定有json數據多條記錄,如何根據KEY的值返回一條記錄.md: -------------------------------------------------------------------------------- 1 | # 假定有 json 數據多條記錄,如何根據 KEY 的值返回一條記錄? 2 | 3 | ## 問題 4 | 5 | 比如說給一個 json 數據: 6 | 7 | ```python 8 | [ 9 | { 10 | "Name": "A1", 11 | "No": "3111", 12 | "createDate": "9999/12/31 00:00:00", 13 | "lastUpdDate": "9999/12/31 00:00:00" 14 | }, 15 | { 16 | "Name": "B2", 17 | "No": "2222", 18 | "createDate": "9999/12/31 00:00:00", 19 | "lastUpdDate": "9999/12/31 00:00:00" 20 | }, 21 | { 22 | "Name": "C3", 23 | "No": "1444", 24 | "createDate": "9999/12/31 00:00:00", 25 | "lastUpdDate": "9999/12/31 00:00:00" 26 | }, 27 | { 28 | "Name": "C4", 29 | "No": "0542", 30 | "createDate": "9999/12/31 00:00:00", 31 | "lastUpdDate": "9999/12/31 00:00:00" 32 | } 33 | ] 34 | ``` 35 | 36 | 我想要 `No` 為 `"0542"` 的一條記錄: 37 | 38 | ```python 39 | { 40 | "Name": "C4", 41 | "No": "0542", 42 | "createDate": "9999/12/31 00:00:00", 43 | "lastUpdDate": "9999/12/31 00:00:00" 44 | } 45 | ``` 46 | 47 | 如果用 python3 應該怎麼實現? 48 | 49 | 我網上搜索了類似代碼, 比如: 50 | 51 | ```python 52 | data = list(filter(lambda d: d['No'] == "0542", jsondata)) 53 | ``` 54 | 55 | 改來改去,總是報各種類型錯誤,抓狂了……所以請教下大家,應該怎麼寫,謝謝! 56 | 57 | 問題出自 [segmentfault](https://segmentfault.com/q/1010000008793933/a-1020000008795741), by [Nix](https://segmentfault.com/u/nix) 58 | 59 | ## 回答 60 | 61 | 假設有 json string 如下: 62 | 63 | ```python 64 | s = """ 65 | [ 66 | { 67 | "Name": "A1", 68 | "No": "3111", 69 | "createDate": "9999/12/31 00:00:00", 70 | "lastUpdDate": "9999/12/31 00:00:00" 71 | }, 72 | { 73 | "Name": "B2", 74 | "No": "2222", 75 | "createDate": "9999/12/31 00:00:00", 76 | "lastUpdDate": "9999/12/31 00:00:00" 77 | }, 78 | { 79 | "Name": "C3", 80 | "No": "1444", 81 | "createDate": "9999/12/31 00:00:00", 82 | "lastUpdDate": "9999/12/31 00:00:00" 83 | }, 84 | { 85 | "Name": "C4", 86 | "No": "0542", 87 | "createDate": "9999/12/31 00:00:00", 88 | "lastUpdDate": "9999/12/31 00:00:00" 89 | } 90 | ] 91 | """ 92 | ``` 93 | 94 | 代碼: 95 | 96 | ```python 97 | # code for python3 98 | 99 | import json 100 | 101 | def search(json_str, no): 102 | return [datum for datum in json.loads(s) if datum['No']==no] 103 | 104 | datum = search(s, '0542') 105 | print(datum) 106 | ``` 107 | 108 | 結果: 109 | 110 | ```python 111 | [{'Name': 'C4', 'No': '0542', 'createDate': '9999/12/31 00:00:00', 'lastUpdDate': '9999/12/31 00:00:00'}] 112 | ``` 113 | -------------------------------------------------------------------------------- /questions/json/為什麼json的key只能是string.md: -------------------------------------------------------------------------------- 1 | # 為什麼 json 的 key 只能是 string ? 2 | 3 | ## 問題 4 | 5 | 在 Python 中,字典的 key 可以是任意 immutable 對象,但 json 的 key 卻只能是 string。 6 | 7 | 在 stackoverflow 上搜到的相關問題,[Why JSON allows only string to be a key?](http://stackoverflow.com/questions/9304528/why-json-allows-only-string-to-be-a-key) 8 | 9 | 最佳答案是說 json 是為了數據在不同程序之間相互傳遞,所以 string 能保證不同的程序語言都能支持這種數據結構。但我還是不明白為什麼 int、float 之類的不行。 10 | 11 | ```python 12 | >>> json.dumps({ 1 : 1 , 2 : 2 }) 13 | '{"1": 1, "2": 2}' 14 | ``` 15 | 16 | 問題出自 [segmentfault](https://segmentfault.com/q/1010000005682217/a-1020000005682683), by [morriaty_the_murderer](https://segmentfault.com/u/morriaty_the_murderer) 17 | 18 | ## 回答 19 | 20 | 你好,針對你的問題,我覺得你自己貼的那篇 stack overflow 的文章已經說得很清楚了。 21 | 22 | 補充一下我對於 json 中 object key 的看法: 23 | 24 | 首先你可以看到 [json.org][1] 中對於 json **object** 和 **string** 的定義: 25 | 26 | * **object**: An object is an unordered set of name/value pairs. An object begins with { (left brace) and ends with } (right brace). Each name is followed by : (colon) and the name/value pairs are separated by , (comma). 27 | 28 | * **string**: A string is a sequence of zero or more Unicode characters, `wrapped in double quotes`, using backslash escapes. A character is represented as a single character string. A string is very much like a C or Java string. 29 | 30 | 同時我們可以在 [The application/json Media Type for JavaScript Object Notation (JSON)][2] 中看到以下的敘述: 31 | 32 | > An object is an unordered collection of zero or more name/value pairs, where **a name is a string** and a value is a string, number, boolean, null, object, or array. 33 | 34 | 我們可以清楚了解到,json 用來表述 object 用 string (雙引號夾住的字符集) 當作 key 的唯一選擇。 35 | 36 | 這說明了在 json 的定義或是 spec 中是明顯規範了 key 的型態跟表達方式,這可能出於 **某些設計哲學上的考量**,也可能只是 **某些實作上的選擇**,當然我們可以去追究當中的意涵,但是: 37 | 38 | > 對於一般的使用者而言,能夠認識到這一點以避免設計上的問題應該是更為重要的。 39 | 40 | 就像是我們未必會去深究 Python 為何不使用 `else if` 而要使用 `elif`,為什麼我們複製 tuple 卻拿不到副本,而是拿到同一個對象等等。 41 | 42 | ---------- 43 | 44 | 當然這可能不能滿足我們的求知欲,所以我試圖提出一些觀點(還有真正大師的觀點) 45 | 46 | > P.S. 我在這裡並不打算說服你,因為我自認為我完全不是個 json 專家(笑)。 47 | 48 | 如果你嘗試使用過 javascript 去建立一個 object,你會發現,以下的敘述語句是行得通的: 49 | 50 | ```javascript 51 | var obj = {hello:1, world:2} 52 | ``` 53 | 54 | 這相等於: 55 | 56 | ```javascript 57 | var obj = {"hello":1, "world":2} 58 | ``` 59 | 60 | 可見在 js 中,object 的 key 有沒有雙引號在 evaluate 的時候都是一樣的。 甚至目前的 js object 用整數或是 js 關鍵字也能當作 key ... 61 | 62 | 但在 json 中,key 不但要是 string,還必須以雙引號夾住,所以很多人的疑問反而是,為什麼 js 中可以不用雙引號,但是 json 卻要? 我覺得這是一個重點,當雙引號的規則被使用的時候,不是 string 的東西也會變成 string 了(因為非得用雙引號不可),那所以為什麼要用雙引號咧? 63 | 64 | 關於這一點,你可以參考一下 Douglas Crockford (json 標準創造者) 在某個演講上面的一段說詞: 65 | 66 | > That was when we discovered the unquoted name problem. It turns out ECMA Script 3 has a whack reserved word policy. Reserved words must be quoted in the key position, which is really a nuisance. When I got around to formulizing this into a standard, I didn't want to have to put all of the reserved words in the standard, because it would look really stupid. 67 | 68 | > At the time, I was trying to convince people: yeah, you can write applications in JavaScript, it's actually going to work and it's a good language. I didn't want to say, then, at the same time: and look at this really stupid thing they did! So I decided, instead, let's just quote the keys. That way, we don't have to tell anybody about how whack it is. That's why, to this day, keys are quoted in JSON. 69 | 70 | 總而言之,大意是說,為了避免 **json 因為要避免使用它的語言發生問題** 所導致的過度複雜的標準(比如說考慮所有關於那個語言的關鍵字在作為 key 的情況),那乾脆用個簡單的法則來解決所有可能的意外,那就是加上雙引號拉。 71 | 72 | 以上不知道有沒有讓你有一些想法呢? 其實很多人覺得只能使用 string key 是為了不同語言使用 json 上面的穩定性我覺得也很有道理,我覺得能找個理由說服自己就很不錯了,畢竟怎麼理解他這樣設計可以有千千萬萬種解讀,至於真正的原因,也就只有他的創造者或標準的維護者知道了。 73 | 74 | ---------- 75 | 76 | **總之,簡單而明確的規則不是很不錯嗎!** 77 | 78 | 參考資料: 79 | 80 | 1. [in JSON, Why is each name quoted?][3] 81 | 2. [Is there any practical reason to use quoted strings for JSON keys?][4] 82 | 3. [Converting a JSON Text to a JavaScript Object][5] 83 | 4. [Object Equality in JavaScript][6] 84 | 85 | ---------- 86 | (以下是題外話) 87 | 88 | 對於你一開始的這個敘述: 89 | 90 | > 在 Python 中,字典的 key 可以是任意 immutable 对象 91 | 92 | 稍微有點不精確,分享我自己的看法。 93 | 94 | 關於甚麼樣的對象可以作為字典的鍵,我們應該這樣說: 95 | 96 | > 在 Python 中,字典的 key 必須要是 hashable 的 97 | 98 | 有人可能會覺得很奇怪,immutable 的對象不應該都是 hashable 的嗎? 99 | 100 | 其實並不盡然,我們看一個例子: 101 | 102 | ```python 103 | >>> a = ([1, 2, 3], 4, 5) 104 | >>> hash(a) 105 | Traceback (most recent call last): 106 | File "", line 1, in 107 | TypeError: unhashable type: 'list' 108 | >>> dic = {} 109 | >>> dic[a] = 0 110 | Traceback (most recent call last): 111 | File "", line 1, in 112 | TypeError: unhashable type: 'list' 113 | ``` 114 | 115 | `a` 是一個 tuple,我想我們應該都同意他是 immutable 的對象,可是這個 tuple 含有一個 mutable 的 list 元素,導致他自己成為一個非 hashable 對象,這就不能當作字典 `dic` 的鍵了。 116 | 117 | 這是非常容易被忽略的一點: 118 | 119 | > tuple 只有在他的元素都是 hashable 的情況下,才會是 hashable 的 120 | 121 | 所以其實有些 tuple 可以當作字典的鍵: 122 | 123 | ```python 124 | >>> b = (1,2,3) 125 | >>> dic[b] = 0 126 | ``` 127 | 128 | 那我們到底要如何判斷一個對象是否為 hashable 的(也就是他可以作為字典的鍵)呢? (第三點針對 Python3) 129 | 130 | 1. 基本的 immutable builtin type 都是 hashable 的,除非他們包含了 mutable 的元素(見規則2),ex: str, bytes, ... 131 | 2. tuple 只有在所有元素都是 hashable 的對象時,他才是 hashable 的 132 | 3. 自定義的類基本上都是 hashable 的,因為他們的 hash 值是由他們的 id 值推導出來的。但假如我們有實作它的 `__eq__` 方法,因為要維持 hash 跟 equality 的一致性,所以如果我們沒有一併重新實作 `__hash__`,則該類也會變成 unhashable 的。 133 | 134 | 以上幾點可參考: 135 | 136 | 1. [Types that define `__eq__` are unhashable in Python 3.x?][7] 137 | 2. [Python doc - Glossary][8] 138 | 139 | [1]: http://www.json.org/ 140 | [2]: http://tools.ietf.org/html/rfc4627 141 | [3]: http://stackoverflow.com/questions/2067974/in-json-why-is-each-name-quoted 142 | [4]: http://stackoverflow.com/questions/4201441/is-there-any-practical-reason-to-use-quoted-strings-for-json-keys 143 | [5]: http://www.w3schools.com/js/js_json.asp 144 | [6]: http://adripofjavascript.com/blog/drips/object-equality-in-javascript.html 145 | [7]: http://stackoverflow.com/questions/1608842/types-that-define-eq-are-unhashable-in-python-3-x 146 | [8]: https://docs.python.org/3/glossary.html#term-hashable 147 | -------------------------------------------------------------------------------- /questions/len/怎麼判斷函數或方法多次使用是否需要定義臨時變量.md: -------------------------------------------------------------------------------- 1 | # 怎麼判斷函數或方法多次使用是否需要定義臨時變量 2 | 3 | ## 問題 4 | 5 | 自學一直有一個困擾(因為教程裡不太會提及這種問題,比如一些簡單的函數或方法,如 `len()`、`isdigit()` 多次使用的情況下,是否需要定義一個臨時的變量: 6 | 7 | 不使用臨時變量: 8 | ```python 9 | string = 'something' 10 | if len(string) == 1: 11 | pass 12 | elif len(string) == 2: 13 | pass 14 | ``` 15 | 16 | 使用臨時變量: 17 | ```python 18 | string = 'something' 19 | length = len(string) 20 | if len(length) == 1: 21 | pass 22 | elif len(length) == 2: 23 | pass 24 | ``` 25 | 26 | 這樣變量多了一個,但是函數少計算一次 27 | 28 | 哪一種益處更大呢? 29 | 30 | * 是所有此類情況都用臨時變量呢? 31 | * 還是具體函數具體分析,簡單的不需要臨時變量? 32 | 33 | 從資源合理利用的角度,怎麼權衡這兩種方案 34 | 35 | 問題出自 [segmentfault](https://segmentfault.com/q/1010000005610737/a-1020000005612265), by [littlealias](https://segmentfault.com/u/littlealias) 36 | 37 | ## 回答 38 | 39 | ### 結論 40 | 41 | 對於這個問題的情境,我支持不另外定義新的變數 42 | 43 | 我先簡單從兩個方面回答你的問題 44 | 45 | ### 可讀性 46 | 47 | 首先,當效率不是當前程式的關鍵核心時,以追求 **可讀性** 為最高原則 48 | 49 | 怎麼說呢? 50 | 51 | 1. 有些程式和代碼處理的問題本身就不是追求速度的議題,比如說簡單的寄帳程式跟科學計算比起來,特意追求執行速度的最佳化不是很有意義,就算是追求效率的程式,也不見得你現在 focus 的代碼就是影響效率的關鍵處(需要作 profiling 才能真正確定,過早最佳化不是很好)。重點是,若非出現效能問題且你已經確定是該代碼段造成,否則完全不用作資源或效能上的過度考慮,尤其現在機器的空間和運算速度那麼進步的狀況下,一些函數呼叫根本不算什麼,否則 oop 和一些抽象化的技巧早就不能用了。 52 | 53 | 2. 所以追求可讀性是一般情況下比較好的目標,當然這部分就很看狀況了,比方說 [Refactoring][1] 這本書裡面就有提到 Replace Temp with Query 的手法,因為區域變數可能會使代碼難以被提煉( extract )。但其實也不盡然,有時候過長的 Query 式也會讓代碼難以閱讀,總之這部份可以有這部分的考量跟權宜。 54 | 55 | ### 效率 56 | 57 | 第二個部分可以來分析一下效率,以 `len` 而言,我會選擇不另外定義一個變數,因為 Python 對於其內建的資料結構,`len` 函數是會直接從對應的 C-Object 中回傳資料的長度屬性,所以這部分是非常快的,我的意思是,Python 對於自己內建的資料,本身就保存著長度的資訊,使用 `len` 不會造成額外的計算或是層疊的調用,所以完全不必考慮效率問題。 58 | 59 | 而且 `len` 的閱讀性很高,函數長度也很短,定義一個新的變數不是很必要。 60 | 61 | P.S. 若我的理解有誤,請不吝指正,謝謝 62 | 63 | 64 | [1]: http://refactoring.com/ 65 | -------------------------------------------------------------------------------- /questions/list/list使用append最後為什麼會把前面的元素都修改掉.md: -------------------------------------------------------------------------------- 1 | # list 使用 append 最後為什麼會把前面的元素都修改掉 2 | 3 | ## 問題 4 | 5 | 一個簡單的循環解包,寫成字典後向後添加到列表中。 6 | 7 | 前面六步都沒問題,可是最後一次append會把前面六個元素都修改掉,這是為什麼呢? 8 | 9 | ```Python 10 | items_index = range(7) 11 | value = [10, 40, 30, 50, 35, 40, 30] 12 | weight = [35, 30, 60, 50, 40, 10, 25] 13 | 14 | item = {} 15 | items = [] 16 | for i in range(len(items_index)): 17 | item['index'] = items_index[i] + 1 18 | item['value'] = value[i] 19 | item['weight'] = weight[i] 20 | items.append(item) 21 | print('第'+str(i+1)+'次:') 22 | print(items) 23 | print('最后结果:') 24 | print(items) 25 | ``` 26 | 27 | 結果如下: 28 | 29 | ![img](https://segmentfault.com/img/bVvGGT) 30 | 31 | 問題出自 [segmentfault](https://segmentfault.com/q/1010000005129927/a-1020000005138271), by [KCN_5](https://segmentfault.com/u/kcn_5) 32 | 33 | ## 回答 34 | 35 | 這個問題的癥結點,之前幾位大大都已經點明了,就在於 `item` 的初始化應置放於迴圈內,否則每次 `append`到 `items` 中的都是同一個字典,到最後 `items` 中的每一個元素都參考到同一個字典。 36 | 37 | ---------- 38 | 39 | 接下來就是我多嘴的部分了,希望能夠好好討論一些想法,給大家當作參考囉. 40 | 41 | 在 Python 中,我們應該盡量避免: 42 | 43 | 1. 用 index 來循環 list 44 | 2. 用 key 來循環 dictionary 45 | 46 | 不是說不可以,只是有更簡明的寫法,上述兩種用法的缺點都在於,在循環內我們必須多做一個取值的動作: 47 | 48 | 1. `item = lst[index]` 49 | 2. `item = dic[key]` 50 | 51 | 取而代之,我們應該直接去 iterate list 或 dictionary 中的元素。 52 | 53 | 在 list 中: 54 | 55 | ```python 56 | >>> lst = ['a', 'b', 'c'] 57 | >>> for item in lst: 58 | ... print(item) 59 | ... 60 | a 61 | b 62 | c 63 | ``` 64 | 65 | 很簡單地,完全不透過 index 的控制我們就能依序取出 `lst` 中的元素. 66 | 至於在某些情況下我們也想要同時得到 index 值,我們也應該使用 `enumerate()` 方法: 67 | 68 | ```python 69 | >>> lst = ['a', 'b', 'c'] 70 | >>> for index, item in enumerate(lst): 71 | ... print(index, item) 72 | ... 73 | 0 a 74 | 1 b 75 | 2 c 76 | ``` 77 | 78 | 在 dictionary 中,使用 `items()` 方法是個好主意: 79 | 80 | ``` python 81 | >>> dic = {'name':'dokelung', 'age':27} 82 | >>> for key, item in dic.items(): 83 | ... print(key, item) 84 | ... 85 | age 27 86 | name dokelung 87 | ``` 88 | 89 | 以上這些寫法的好處都在於,**循環內不需要一個取值的干擾動作**,比較乾淨簡明. 90 | 91 | ---------- 92 | 93 | 另外,BeginMan大點出了一個漂亮又簡明的想法,不過在某些情況下可能會有些問題,我舉個例子: 94 | 95 | ```python 96 | value = [10, 40, 30, 50, 35, 40, 30, 50] 97 | weight = [35, 30, 60, 50, 40, 10, 25, 50] 98 | 99 | data = zip(value, weight) 100 | items = map(lambda x: {"value": x[0], "weight": x[1], "index": data.index(x)+1}, data) 101 | 102 | for item in items: 103 | print(item) 104 | ``` 105 | 106 | 結果: 107 | 108 | ``` 109 | {'index': 1, 'weight': 35, 'value': 10} 110 | {'index': 2, 'weight': 30, 'value': 40} 111 | {'index': 3, 'weight': 60, 'value': 30} 112 | {'index': 4, 'weight': 50, 'value': 50} 113 | {'index': 5, 'weight': 40, 'value': 35} 114 | {'index': 6, 'weight': 10, 'value': 40} 115 | {'index': 7, 'weight': 25, 'value': 30} 116 | {'index': 4, 'weight': 50, 'value': 50} 117 | ``` 118 | 119 | 最後一個元素的 `index` 成為 4 了,這是因為利用 `data.index(x)` 永遠會找到 `data` 中第一個出現的 `x` 的 index 值,所以當 `data` 中有兩個相同的元素時便會有一些小問題 120 | 121 | 以這個case來說我的小建議是,我們不需要保留一個 `index` 值在 `item`中,因為最後所有的 `item` 會被保存在一個 list 中,這代表 `index` 就是 `item` 在 `items` 中的 index. 122 | 123 | 可以簡單改成下面的樣子: 124 | 125 | ```python 126 | data = zip(value, weight) 127 | items = map(lambda x: {"value": x[0], "weight": x[1]}, data) 128 | 129 | for index, item in enumerate(items): 130 | print(index, item) 131 | ``` 132 | 133 | 如果堅持一定要在每個字典中保持一個 `index`,那可以改成: 134 | 135 | ```python 136 | items = map(lambda (index, item): {"index": index, "value": item[0], "weight": item[1]}, enumerate(data)) 137 | ``` 138 | 139 | 結果: 140 | 141 | ``` 142 | {'index': 0, 'weight': 35, 'value': 10} 143 | {'index': 1, 'weight': 30, 'value': 40} 144 | {'index': 2, 'weight': 60, 'value': 30} 145 | {'index': 3, 'weight': 50, 'value': 50} 146 | {'index': 4, 'weight': 40, 'value': 35} 147 | {'index': 5, 'weight': 10, 'value': 40} 148 | {'index': 6, 'weight': 25, 'value': 30} 149 | {'index': 7, 'weight': 50, 'value': 50} 150 | ``` 151 | 152 | ---------- 153 | 154 | 接著來討論一下泛函編程中常見的 `map`(映射), `filter`(過濾) 和 `reduce`(精煉). 155 | 156 | 在過去,Python 要撰寫泛函風格的代碼,上述三個 function 是必要中的必要,但現在,Python 的 list comprehension 和 generation expression 可以取代 map 和 filter 的角色,同時,許多內建的 **歸納函式** 也可以更好地取代 `reduce`,不過這邊不講那麼多,稍微講一下 `map`. 157 | 158 | 現在,`map` 我們應該盡量使用串列生成式來取代,比如以剛剛的例子: 159 | 160 | ```python 161 | data = zip(value, weight) 162 | items = map(lambda x: {"value": x[0], "weight": x[1]}, data) 163 | ``` 164 | 165 | 使用 list comprehension 會更好理解些: 166 | 167 | ```python 168 | data = zip(value, weight) 169 | items = [{"value": value, "weight": weight} for value, weight in data] 170 | 171 | for index, item in items: 172 | print(index, item) 173 | ``` 174 | 175 | 如果有效率上的考量,使用 generator 會更好: 176 | 177 | ```python 178 | data = zip(value, weight) 179 | gen = ({"value": value, "weight": weight} for value, weight in data) 180 | 181 | for item in gen: 182 | print(item) 183 | ``` 184 | -------------------------------------------------------------------------------- /questions/list/python列表取交集.md: -------------------------------------------------------------------------------- 1 | # python 列表取交集 2 | 3 | ## 問題 4 | 5 | ```python 6 | a = [[1,2],[3,4]] 7 | b = [[2,3],[3,5]] 8 | ``` 9 | 10 | 我想取 `a` 和 `b` 的交集,結果應該是 `[3,4],[3,5]` ,就是第一元素要一樣 11 | 我的笨辦法是先取a和b第一個元素的列表,取它們的交集,然後再分別從a和b中找 12 | 請問有沒有更好的辦法? 13 | 14 | ----- 15 | 16 | 補充:對不起大家我沒說清楚。`a` 和 `b` 中的元素有很多,且每一項的第一個元素時不會相同的,最後取出來是兩個列表 17 | 18 | 我的代碼如下: 19 | 20 | ```python 21 | def getItemList(dictionary,uid,vid=""): 22 | if(vid == ""): 23 | itemList = dictionary[uid] 24 | return itemList 25 | else: 26 | itemList1 = dictionary[uid] 27 | itemList2 = dictionary[vid] 28 | list1 = [] 29 | list2 = [] 30 | list3 = [] 31 | for item in itemList1: 32 | list1.append(item[0]) 33 | for item in itemList2: 34 | list2.append(item[0]) 35 | list1 = list(set(list1).intersection(set(list2))) 36 | list2.clear() 37 | for item in list1: 38 | for li in itemList1: 39 | if li[0] == item: 40 | list2.append(li) 41 | for li in itemList2: 42 | if li[0] == item: 43 | list3.append(li) 44 | return list3,list2 45 | ``` 46 | 47 | ```python 48 | dictionary = {'1':[['1', '5'], ['2', '3'], ['3', '4'], ['4', '3'], ['5', '3'], ['7', '4'], ['8', '1'], ['9', '5'], ['11', '2'], ['13', '5'], ['15', '5'], ['16', '5'], ['18', '4'], ['19', '5'], ['21', '1'], ['22', '4'], ['25', '4'], ['26', '3'], ['28', '4'], ['29', '1'], ['30', '3'], ['32', '5'], ['34', '2'], ['35', '1'], ['37', '2'], ['38', '3'], ['40', '3'], ['41', '2'], ['42', '5'], ['43', '4'], ['45', '5'], ['46', '4'], ['48', '5'], ['50', '5'], ['52', '4'], ['55', '5'], ['57', '5'], ['58', '4'], ['59', '5'], ['63', '2'], ['66', '4'], ['68', '4'], ['71', '3'], ['75', '4'], ['77', '4'], ['79', '4'], ['83', '3'], ['87', '5'], ['88', '4'], ['89', '5'], ['93', '5'], ['94', '2'], ['95', '4'], ['99', '3'], ['101', '2'], ['105', '2'], ['106', '4'], ['109', '5'], ['110', '1'], ['111', '5'], ['115', '5'], ['116', '3'], ['119', '5'], ['122', '3'], ['123', '4'], ['124', '5'], ['126', '2'], ['127', '5'], ['131', '1'], ['133', '4'], ['135', '4'], ['136', '3'], ['137', '5'], ['138', '1'], ['139', '3'], ['141', '3'], ['142', '2'], ['144', '4'], ['146', '4'], ['147', '3'], ['149', '2'], ['152', '5'], ['153', '3'], ['156', '4'], ['158', '3'], ['162', '4'], ['165', '5'], ['166', '5'], ['167', '2'], ['168', '5'], ['169', '5'], ['172', '5'], ['173', '5'], ['176', '5'], ['178', '5'], ['179', '3'], ['181', '5'], ['182', '4'], ['187', '4'], ['191', '5'], ['192', '4'], ['194', '4'], ['195', '5'], ['197', '5'], ['198', '5'], ['199', '4'], ['203', '4'], ['204', '5'], ['205', '3'], ['207', '5'], ['211', '3'], ['216', '5'], ['217', '3'], ['220', '3'], ['223', '5'], ['231', '1'], ['234', '4'], ['237', '2'], ['238', '4'], ['239', '4'], ['240', '3'], ['244', '2'], ['245', '2'], ['246', '5'], ['247', '1'], ['249', '4'], ['251', '4'], ['256', '4'], ['257', '4'], ['261', '1'], ['263', '1'], ['268', '5'], ['269', '5'], ['270', '5'], ['271', '2']],'2':[['1', '4'], ['10', '2'], ['14', '4'], ['25', '4'], ['100', '5'], ['111', '4'], ['127', '5'], ['237', '4'], ['242', '5'], ['255', '4'], ['258', '3'], ['269', '4'], ['272', '5'], ['273', '4'], ['274', '3'], ['275', '5'], ['276', '4'], ['277', '4'], ['278', '3'], ['282', '4'], ['283', '5'], ['284', '4'], ['285', '5'], ['286', '4'], ['287', '3'], ['288', '3'], ['289', '3'], ['291', '3'], ['293', '4'], ['294', '1'], ['295', '4'], ['296', '3'], ['300', '4'], ['302', '5'], ['304', '4'], ['305', '3'], ['306', '4'], ['309', '1'], ['310', '4'], ['311', '5']]} 49 | ``` 50 | 51 | 這種格式(測試數據) 52 | 53 | 其實我想改進 `else` 這一段的效率,我用 `line_profiler` 分析結果如下: 54 | 55 | ![clipboard.png](https://segmentfault.com/img/bVzErd) 56 | 57 | 可以看出我的方法效率很差 58 | 如果改成下面這樣會好很多 59 | 60 | ![clipboard.png](https://segmentfault.com/img/bVzErq) 61 | 62 | 恩,就是這樣,補充完畢! 63 | 64 | 問題出自 [segmentfault](https://segmentfault.com/q/1010000006073093/a-1020000006074241), by [nuomimick](https://segmentfault.com/u/nuomimick) 65 | 66 | ## 回答 67 | 68 | 因為還不知道你想要的細節, 就先給一個暫時的答案吧: 69 | 70 | ```python 71 | from collections import defaultdict 72 | 73 | a = [[1,2],[3,4],[3,6]] 74 | b = [[1,2],[2,3],[3,5]] 75 | 76 | dic_a = defaultdict(list) 77 | dic_b = defaultdict(list) 78 | 79 | for item in a: 80 | dic_a[item[0]].append(item) 81 | 82 | for item in b: 83 | dic_b[item[0]].append(item) 84 | 85 | common_keys = set(dic_a.keys()) & set(dic_b.keys()) 86 | 87 | for key in common_keys: 88 | print 'key:', key, 'a:', dic_a[key], 'b:',dic_b[key] 89 | ``` 90 | -------------------------------------------------------------------------------- /questions/list/python生成特定列表格式.md: -------------------------------------------------------------------------------- 1 | # python 如何生成特定列表格式 2 | 3 | ## 問題 4 | 5 | ```python 6 | aa = [ 7 | {'ip': '192.168.1.1', 'projectname__pname': 'hh', 'id': 1, 'projectname_id': 1}, 8 | {'ip': '192.168.3.2', 'projectname__pname': 'hh', 'id': 2, 'projectname_id': 1}, 9 | {'ip': '192.168.22.3', 'projectname__pname': 'qm', 'id': 3, 'projectname_id': 2}, 10 | {'ip': '192.168.5.3', 'projectname__pname': 'ssh', 'id':4, 'projectname_id': 3} 11 | ] 12 | ``` 13 | 14 | 大家好,我想把 `aa` 中的列表生成以下 `bb` 的格式: 15 | 16 | ```python 17 | bb = [ 18 | { 19 | 'projectname_id': 1, 20 | 'projectname__pname': 'hh', 21 | 'children': [{'id': 1, 'text': '192.168.1.1'},{'id': 2, 'text': '192.168.1.2'}] 22 | }, 23 | { 24 | 'projectname_id': 2, 25 | 'projectname__pname': 'qm', 26 | 'children': [{'id': 3, 'text':'192.168.22.3'}] 27 | }, 28 | { 29 | 'projectname_id': 3, 30 | 'projectname__pname': 'ssh', 31 | 'children': [{'id': 4, 'text': '192.168.5.3'}] 32 | } 33 | ] 34 | ``` 35 | 36 | 請問代碼怎麽實現? 37 | 38 | 問題出自 [segmentfault](), by [tempreg](https://segmentfault.com/u/tempreg) 39 | 40 | ## 回答 41 | 42 | 代碼: 43 | 44 | ```python 45 | def aa2bb(aa): 46 | bb = [] 47 | proj_id_map = {} 48 | for ad in aa: 49 | proj_id = ad['projectname_id'] 50 | child = {'id': ad['id'], 'text': ad['ip']} 51 | if proj_id not in proj_id_map: 52 | bd = { 53 | 'projectname__pname': ad['projectname__pname'], 54 | 'projectname_id': ad['projectname_id'], 55 | 'children': [child] 56 | } 57 | bb.append(bd) 58 | proj_id_map[proj_id] = bd 59 | else: 60 | bd = proj_id_map[proj_id] 61 | bd['children'].append(child) 62 | return bb 63 | ``` 64 | 65 | 測試: 66 | 67 | ```python 68 | from pprint import pprint 69 | 70 | aa = [ 71 | {'ip': '192.168.1.1', 'projectname__pname': 'hh', 'id': 1, 'projectname_id': 1}, 72 | {'ip': '192.168.3.2', 'projectname__pname': 'hh', 'id': 2, 'projectname_id': 1}, 73 | {'ip': '192.168.22.3', 'projectname__pname': 'qm', 'id': 3, 'projectname_id': 2}, 74 | {'ip': '192.168.5.3', 'projectname__pname': 'ssh', 'id':4, 'projectname_id': 3} 75 | ] 76 | 77 | pprint(aa2bb(aa)) 78 | ``` 79 | 80 | 結果: 81 | 82 | ```python 83 | [{'children': [{'id': 1, 'text': '192.168.1.1'}, 84 | {'id': 2, 'text': '192.168.3.2'}], 85 | 'projectname__pname': 'hh', 86 | 'projectname_id': 1}, 87 | {'children': [{'id': 3, 'text': '192.168.22.3'}], 88 | 'projectname__pname': 'qm', 89 | 'projectname_id': 2}, 90 | {'children': [{'id': 4, 'text': '192.168.5.3'}], 91 | 'projectname__pname': 'ssh', 92 | 'projectname_id': 3}] 93 | ``` 94 | -------------------------------------------------------------------------------- /questions/list/遍歷二維串列最外圈.md: -------------------------------------------------------------------------------- 1 | # 遍歷二維串列最外圈 2 | 3 | ## 問題 4 | 5 | ``` 6 | lst = [ 7 | [ 1 , 2 , 3 ], 8 | [ 4 , 5 , 6 ], 9 | [ 7 , 8 , 9 ] 10 | ] 11 | ``` 12 | 13 | 遍歷二維數組最外圈--> 12369874 14 | 我以前在算法書上看到一個很清晰明了的.想不起來了. 15 | 16 | 問題出自 [segmentfault](https://segmentfault.com/q/1010000005659963/a-1020000005660177), by [大白菜丶](https://segmentfault.com/u/dabaicai_55470c243e2c1) 17 | 18 | ## 回答 19 | 20 | Python 3 代碼: 21 | 22 | ```python 23 | a = [ 24 | [1, 2, 3, 4], 25 | [5, 6, 7, 8], 26 | [9, 10,11,12], 27 | [13,14,15,16] 28 | ] 29 | 30 | for e in a[0]: 31 | print(e) 32 | for e in (row[-1] for row in a[1:-1]): 33 | print(e) 34 | for e in reversed(a[-1]): 35 | print(e) 36 | for e in (row[0] for row in a[-2:0:-1]): 37 | print(e) 38 | ``` 39 | 40 | 不知道算不算優雅... 41 | 42 | 簡單來說就是就四個步驟: 43 | 44 | 1. 正走 first row 45 | 2. 正走 last col 46 | 3. 逆走 last row 47 | 4. 逆走 first col 48 | 49 | 只是要小心走過的不要再走(boundary condition) 50 | -------------------------------------------------------------------------------- /questions/math/Python怎麼通過input獲取矩陣.md: -------------------------------------------------------------------------------- 1 | # Python 怎麼通過 input 獲取矩陣 2 | 3 | ## 問題 4 | 5 | 最近在學隨機過程,中間要計算矩陣,矩陣相乘太麻煩了,所以想用編程實現,也順便練下手。 6 | 基本功能已經實現,但是只能自己在程序裡事先把數組輸進去,所以想問一下怎樣才可以通過input函數直接過去矩陣。 7 | 8 | 看了網上其他人的辦法,都是這樣的 9 | 10 | ``` 11 | 輸入>>1 2 3 4 5 12 | ``` 13 | 14 | 通過是 `split(' ')` 來獲取得到 `['1','2','3','4','5']` 15 | 16 | 但是我想要的是這樣的的效果,這樣感覺美觀一些。 17 | 18 | ``` 19 | 輸入>>[ [1,2,3],[1,2,3] ] 20 | ``` 21 | 22 | 先謝謝啦 23 | 24 | 問題出自 [segmentfault](https://segmentfault.com/q/1010000005137360/a-1020000005138345), by []() 25 | 26 | ## 回答 27 | 28 | 一個比較 **不安全** 但馬上可以解決問題的辦法是: 29 | 30 | ```python 31 | matrix = input(">>") 32 | matrix = eval(matrix) 33 | print(matrix) 34 | ``` 35 | 36 | 結果: 37 | 38 | ``` 39 | >>[ [1,2,3], [1,2,3] ] 40 | [[1, 2, 3], [1, 2, 3]] 41 | ``` 42 | 43 | P.S. 可以考慮使用文件來輸入 44 | 45 | 例如: 46 | 47 | ``` 48 | matrix a 49 | 1 2 3 50 | 1 2 3 51 | ``` 52 | 53 | 補充: 在 Python 中如果要使用現成的庫來處理矩陣,[numpy][1] 是個好選擇。 54 | 55 | 56 | [1]: http://www.numpy.org/ 57 | -------------------------------------------------------------------------------- /questions/object/Python 3.x 實例方法的__func__屬性.md: -------------------------------------------------------------------------------- 1 | # Python 3.x 實例方法的 `__func__`屬性 2 | 3 | ## 問題 4 | 5 | 看 Python 3.x 的文檔,涉及到實例方法特殊屬性的內容,有這麽一段話描述 instance method 的 `__func__` 屬性: 6 | 7 | >When an instance method object is created by retrieving a user-defined function object from a class via one of its instances, its self attribute is the instance, and the method object is said to be bound. The new method’s func attribute is the original function object. 8 | 9 | >When a user-defined method object is created by retrieving another method object from a class or instance, the behaviour is the same as for a function object, except that the func attribute of the new instance is not the original method object but its func attribute. 10 | 11 | 甚是不解,主要是不明白前後兩段中對instance method object的創建方式的描述到底有何不同? 還請大神賜教 12 | 13 | 問題出自 [segmentfault](https://segmentfault.com/q/1010000005175248/a-1020000005178655), by [xu_zhoufeng](https://segmentfault.com/u/inside) 14 | 15 | ## 回答 16 | 17 | class(object, instance) 中的 function 我們稱為 method,而一個 method 是一個 bound (綁定的) function,綁定什麼呢? 自然是綁定 instance. 18 | 19 | 我們看下面這個作為範例的 `Test` class: 20 | 21 | ```python 22 | class Test: 23 | 24 | def __init__(self): 25 | self.name = 'hello' 26 | 27 | def func(self): 28 | return self.name 29 | ``` 30 | 31 | 他有一個 method `func`,如果我們使用該類產生實例 `test1` 和 `test2`: 32 | 33 | ```python 34 | >>> test1 = Test() 35 | >>> test2 = Test() 36 | >>> test1.func 37 | > 38 | ``` 39 | 40 | 我們會發現 `test1.func` 是一個 bound method,這代表了他與 `test1` 綁定,這個綁定最重要的一點就是 `test1.func` 的 `self` 屬性是 `test1`. 41 | 42 | 這看起來很 trivial,不過這中間有個很重要的概念,就是一個 class 中的某個 method 其實只有一個實體,也就是說無論今天我們用 `Test` 產生了多少個 instances,他們都是共用同一個 function `func`,但是每個 instance 都會有一個將 `func` 綁定到自己的 bound method `func`,那我們要如何觀察到真正的(unbound) function呢? 很簡單,這個真正的 function object 被記錄在 bound method 的 `__func__` 屬性: 43 | 44 | ```python 45 | >>> test1.func # instance 中的 func 46 | > 47 | >>> test2.func # instance 中的 func 48 | > 49 | >>> test1.func.__func__ # unbound function 50 | 51 | >>> test2.func.__func__ # unbound function 52 | 53 | >>> Test.func # unbound function 54 | 55 | >>> test1.func.__func__ is test2.func.__func__ 56 | True 57 | ``` 58 | 59 | 由上可知,雖然每個 instance 有自己的 bound method,但這些其實只是將原本的 function 綁定了不同的 instance 後所產生的 functions. 60 | 61 | ---------- 62 | 63 | 接著稍微來分析一下 Python 的指派(assignment),我其實覺得 Python 的指派其實就可以很簡單地想成讓等號左邊的變數參考到等號右邊所運算出來的 instance. 64 | 65 | 也就是說一個簡單地不帶其他運算的指派: 66 | 67 | ``` 68 | a = b 69 | ``` 70 | 71 | 很直覺地可以解讀成 a 參考到 b 所參考的 instance. 72 | 73 | ---------- 74 | 75 | 讓我們回到原本的問題, 76 | 77 | 今天我們如果做了下面這樣的指派: 78 | 79 | ```python 80 | myfunc = test1.func 81 | #或是 82 | myfunc = test2.func 83 | ``` 84 | 85 | 很明顯地 `myfunc` 現在和 `test1.func` 參考到同一個 unbound method,也很自然地,他們的`__func__` 屬性必然會一樣,也就是參考到 `Test.func`. 86 | 87 | ### 結論 88 | 89 | 其實他想要告訴大家的事情是: 90 | 91 | 像是 `myfunc` 這樣的 object,他的 `__func__` 並不會是 `test1.func`(應該說 `myfunc` 根本就是 `test1.func`),而他的 `__func__` 屬性自然就是 `test1.func` 的 `__func__` 屬性. 92 | -------------------------------------------------------------------------------- /questions/object/Python多重繼承屬性問題.md: -------------------------------------------------------------------------------- 1 | # Python 多重繼承屬性問題 2 | 3 | ## 問題 4 | 5 | Python 類的繼承,怎麽讓一個子類 `C`, 同時繼承父類 `A`, `B` 的屬性? 6 | 7 | 先定義兩父類: 8 | 9 | ```python 10 | class A(object): 11 | def __init__(self, a1,a2): 12 | # super(ClassName, self).__init__() 13 | self.a1 = a1 14 | self.a2 = a2 15 | 16 | def funa(self): 17 | print("I'm funa") 18 | 19 | class B(object): 20 | def __init__(self, b1): 21 | # super(ClassName, self).__init__() 22 | self.b1 = b1 23 | 24 | def funb(self): 25 | print("I'm funb") 26 | ``` 27 | 28 | 那麽子類 `C` 應該如何寫? 才能讓初始化後具有 `A`, `B` 中的 `a1`, `a2`, `b1` 屬性和 `funa`, `funb` 方法? 29 | 30 | ```python 31 | class C(A,B): 32 | # ???????????????????? 33 | def __init__(self): 34 | super().__init__() 35 | #????????????????????? 36 | pass 37 | ``` 38 | 感覺是很基本的問題,求各位大佬解答,謝謝! 39 | 40 | 問題出自 [segmentfault](), by [Andykim](https://segmentfault.com/u/andykim) 41 | 42 | ## 回答 43 | 44 | 假設你要多重繼承的 **各個父類關係是平行的**, 多重繼承用於 **組合各父類的成員** (**Mixin** 的概念), 那你可以考慮下面這個例子, 而為了展示通用性, 下面的例子中有三個可能被用來繼承的父類 `A`, `B`, `C`, 而其子類 (例如 `X`, `Y`)可以用任意順序來組合任意數量個父類: 45 | 46 | ```python 47 | # base classes 48 | 49 | class A: 50 | def __init__(self, a1, a2, **kwargs): 51 | super().__init__(**kwargs) 52 | self.a1 = a1 53 | self.a2 = a2 54 | 55 | def funa(self): 56 | print("I'm funa") 57 | 58 | class B: 59 | def __init__(self, b1, **kwargs): 60 | super().__init__(**kwargs) 61 | self.b1 = b1 62 | 63 | def funb(self): 64 | print("I'm funb") 65 | 66 | class C: 67 | def __init__(self, c1, c2, c3, **kwargs): 68 | super().__init__(**kwargs) 69 | self.c1 = c1 70 | self.c2 = c2 71 | self.c3 = c3 72 | 73 | def func(self): 74 | print("I'm func") 75 | ``` 76 | 77 | ```python 78 | # derived classes 79 | 80 | class X(B, A, C): 81 | def __init__(self, **kwargs): 82 | super().__init__(**kwargs) 83 | 84 | class Y(A, B): 85 | def __init__(self, **kwargs): 86 | super().__init__(**kwargs) 87 | ``` 88 | 89 | 使用範例: 90 | 91 | ```python 92 | x = X(a1=1, a2=2, b1=3, c1=4, c2=5, c3=6) 93 | y = Y(a1=1, a2=2, b1=3) 94 | print(x.a1, x.a2, x.b1, x.c1, x.c2, x.c3) 95 | x.funa() 96 | y.funb() 97 | print(dir(x)) 98 | print(dir(y)) 99 | ``` 100 | 101 | 結果: 102 | 103 | ```python 104 | 1 2 3 4 5 6 105 | I'm funa 106 | I'm funb 107 | ['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'a1', 'a2', 'b1', 'c1', 'c2', 'c3', 'funa', 'funb', 'func'] 108 | ['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'a1', 'a2', 'b1', 'funa', 'funb'] 109 | ``` 110 | -------------------------------------------------------------------------------- /questions/object/Python如何通過類方法創建實例方法.md: -------------------------------------------------------------------------------- 1 | # Python 如何通過類方法創建實例方法? 2 | 3 | ## 問題 4 | 5 | 下面是Python 3.x language reference 中的一段話,大意是理解的,不過寫不出一個這樣的示例,求大神給個與這段話一致的示例: 6 | 7 | > When an instance method object is derived from a class method object, the “class instance” stored in self will actually be the class itself, so that calling either xf(1) or Cf(1) is equivalent to calling f(C,1 ) where f is the underlying function. 8 | 9 | 問題出自 [segmentfault](https://segmentfault.com/q/1010000005690407/a-1020000005701043), by [xu_zhoufeng](https://segmentfault.com/u/inside) 10 | 11 | ## 回答 12 | 13 | 其實這個部分,你自己做做實驗就會明白。 14 | 15 | 我們從原文開始看起,分成兩段來討論,第一段說道: 16 | 17 | >When an instance method object is called, the underlying function (__func__) is called, inserting the class instance (__self__) in front of the argument list. For instance, when C is a class which contains a definition for a function f(), and x is an instance of C, calling x.f(1) is equivalent to calling C.f(x, 1). 18 | 19 | 原文第一段說,當一個 `instance method object` 被調用時,`__func__` 的第一個參數先代入 `class instance` 後被調用,接著舉了一個例子: 20 | 21 | ```python 22 | x.f(1) == C.f(x,1) # x is an instance of C 23 | ``` 24 | 25 | 我們用下面的範例來說明,在這裡我們有一個類 `Demo`,他底下有一個 function `foo` 和 function `bar`。 26 | 27 | `Demo` class: 28 | 29 | ```python 30 | class Demo: 31 | 32 | def foo(self, *args): 33 | return 'Call foo by instance' + repr(self) + 'with args' + repr(args) 34 | 35 | @classmethod 36 | def bar(cls, *args): 37 | return 'Call bar by class ' + repr(cls) + 'with args' + repr(args) 38 | ``` 39 | 40 | 實際上呢: 41 | 42 | 1. Python 對於 `foo`,會產生一個 **一般的 function**,這個 function 會被 `Demo.foo` 所參考。 43 | 2. 當我們寫出 `demo.foo` 的時候,Python 會即時創造一個 **bound method object**: `demo.foo`,這個 method object 是個綁定的 method,綁定甚麼呢? 當然就是綁定 `demo` 這個 instance,所以 `demo.foo.__self__` 會參考到 `demo`, 同時 Python 也會把 `Demo.foo` 記在 `demo.foo.__func__` 中。 44 | 3. 所以當這個 `demo.foo` 被呼叫的時候(`demo.foo(1,2,3)`),他其實會去呼叫 `demo.foo.__func__`,並且以 `demo.foo.__self__` (其實也就是 `demo` 自己) 當作第一個參數。 45 | 46 | 以我們寫的類來展示的話,他的例子變成: 47 | 48 | ```python 49 | x.f(1) == C.f(x, 1) 50 | demo.foo(1) == Demo.foo(demo, 1) == demo.foo.__func__(demo.foo.__self__, 1) 51 | ``` 52 | 53 | 看看**代碼**: 54 | 55 | ```python 56 | demo = Demo() 57 | 58 | print('=== Demo start ===\n') 59 | 60 | print('demo.foo', ':', demo.foo) 61 | print(' [type ] ', type(demo.foo)) 62 | print(' [demo.foo.__self__] ', demo.foo.__self__) 63 | print(' [demo.foo.__func__] ', demo.foo.__func__) 64 | print(' [demo.foo(1,2,3) ] ', demo.foo(1,2,3)) 65 | print() 66 | 67 | print('Demo.foo', ':', Demo.foo) 68 | print(' [type ] ', type(Demo.foo)) 69 | print(' [Demo.foo(demo, 1,2,3)] ', Demo.foo(demo, 1,2,3)) 70 | print() 71 | 72 | print('demo.foo.__func__', ':', demo.foo.__func__,) 73 | print(' [type ] ', type(demo.foo.__func__)) 74 | print(' [demo.foo.__func__(demo, 1,2,3)] ', demo.foo.__func__(demo, 1,2,3)) 75 | print() 76 | 77 | print('Demo.foo is demo.foo.__func__ --> ', Demo.foo is demo.foo.__func__) 78 | ``` 79 | 80 | **測試結果**: 81 | 82 | ```python 83 | === Demo start === 84 | 85 | demo.foo : > 86 | [type ] 87 | [demo.foo.__self__] <__main__.Demo object at 0x7f413db47fd0> 88 | [demo.foo.__func__] 89 | [demo.foo(1,2,3) ] Call foo by instance<__main__.Demo object at 0x7f413db47fd0>with args(1, 2, 3) 90 | 91 | Demo.foo : 92 | [type ] 93 | [Demo.foo(demo, 1,2,3)] Call foo by instance<__main__.Demo object at 0x7f413db47fd0>with args(1, 2, 3) 94 | 95 | demo.foo.__func__ : 96 | [type ] 97 | [demo.foo.__func__(demo, 1,2,3)] Call foo by instance<__main__.Demo object at 0x7f413db47fd0>with args(1, 2, 3) 98 | 99 | Demo.foo is demo.foo.__func__ --> True 100 | ``` 101 | 102 | 接著看第二段: 103 | 104 | >When an instance method object is derived from a class method object, the “class instance” stored in self will actually be the class itself, so that calling either x.f(1) or C.f(1) is equivalent to calling f(C,1) where f is the underlying function. 105 | 106 | 第二段的大意是說,當 instance method object 是來自於 class method object 的時候,存在 `self` 裡的 **類實例** 會是 **類** 本身,之後又舉了一個例子: 107 | 108 | ```python 109 | x.f(1) == C.f(1) == f(C,1) # x is an instance of C 110 | ``` 111 | 112 | 我們一樣用範例來說明: 113 | 114 | 1. Python 對於 bar, 會產生 `Demo.bar` ,他是一個來自於 **class method object** 的 **bound method object**,原本 `Demo.bar` 就跟 `Demo.foo` 一樣是個一般的 Python function,但是透過修飾器(`@classmethod` 修飾器),他成為了一個 bound method object,若要觀察原本的 general function,只能在 `Demo.bar.__func__` 中看到,同時他綁定了 `Demo` 類,所以 `Demo.bar.__self__` 會參考到 `Demo` 類。 115 | 2. 所以當 `Demo.bar` 被呼叫的時候(`Demo.bar(1)`),,他其實會去呼叫 `Demo.bar.__func__`,並且以 `Demo.bar.__self__` (其實也就是 `Demo` 自己) 當作第一個參數。 116 | 117 | 以我們寫的類來展示的話,他的例子變成: 118 | 119 | ```python 120 | x.f(1) == C.f(1) == f(C, 1) 121 | demo.bar(1) == Demo.bar(1) == Demo.bar.__func__(Demo, 1) == Demo.bar.__func__(Demo.bar.__self__, 1) 122 | ``` 123 | 124 | **測試代碼**: 125 | 126 | ```python 127 | demo = Demo() 128 | 129 | print('=== Demo start ===\n') 130 | 131 | print('Demo.bar', ':', Demo.bar) 132 | print(' [type ] ', type(Demo.bar)) 133 | print(' [Demo.bar.__self__] ', Demo.bar.__self__) 134 | print(' [Demo.bar.__func__] ', Demo.bar.__func__) 135 | print(' [Demo.bar(1,2,3) ] ', Demo.bar(1,2,3)) 136 | print() 137 | 138 | print('Demo.bar(1) ', Demo.bar(1)) 139 | print('demo.bar(1) ', demo.bar(1)) 140 | print('Demo.bar.__func__(Demo, 1)', Demo.bar.__func__(Demo, 1)) 141 | ``` 142 | 143 | **測試結果**: 144 | 145 | ```python 146 | === Demo start === 147 | 148 | Demo.bar : > 149 | [type ] 150 | [Demo.bar.__self__] 151 | [Demo.bar.__func__] 152 | [Demo.bar(1,2,3) ] Call bar by class with args(1, 2, 3) 153 | 154 | Demo.bar(1) Call bar by class with args(1,) 155 | demo.bar(1) Call bar by class with args(1,) 156 | Demo.bar.__func__(Demo, 1) Call bar by class with args(1,) 157 | ``` 158 | 159 | ### 結論 160 | 161 | 1. 在 Python3 中,class 內有兩種 funciton,一種是一般的 function object,另外一種是 bound method object 162 | 2. instance method 是一般的 function 綁定了 instance 所構成的 method object,class mehtod 是一般的 function 綁定了 class 所構成的 method object 163 | 3. bound method 被調用的時候,其實都是調用最原始的 function (記在 `__func__` 中),但會以綁定的對象作為第一個參數(記在 `__self__` 中)。 164 | 165 | ### 參考資料 166 | 167 | 1. [Difference between methods and functions][1] 168 | 2. [Different way to create an instance method object in Python][2] 169 | 170 | 171 | [1]: http://stackoverflow.com/questions/20981789/difference-between-methods-and-functions 172 | [2]: http://stackoverflow.com/questions/37370578/different-way-to-create-an-instance-method-object-in-python 173 | -------------------------------------------------------------------------------- /questions/object/Python的staticmethod在什麼情況下用.md: -------------------------------------------------------------------------------- 1 | # Python 的 staticmethod 在什麼情況下用 2 | 3 | ## 問題 4 | 5 | 如題。 6 | 7 | 問題出自 [segmentfault](https://segmentfault.com/q/1010000005943821/a-1020000005944785), by [PG](https://segmentfault.com/u/pg_577221f2caf96) 8 | 9 | ## 回答 10 | 11 | ### 特色與使用情境 12 | 13 | 來講講講 `staticmethod` 好了,被 staticmethod 修飾的 method 並不會接收特殊的第一項引數 (一般的 instance method 和被 `classmethod` 修飾過的 **類別方法** 分別會接受實例和類別的參考作為第一項參數),這讓靜態方法就像是個一般的 function,只是他剛好被定義在 class 裡而不是直接定義在 module level。 14 | 15 | 所以他的 **使用情境**就是: 當某個類裡面的函數不需要像是 `self` 或是 `cls` 等實例或類的參考時,使用靜態方法可以比較簡明且有效率地完成工作。 16 | 17 | 1. 簡明的部分在於不需要多接收一個無關緊要的引數 18 | 2. 效率在於一般的實例方法是 bound method (是個 object) 且是在我們要使用他的時候才生成,這會花上多一點點的 cost,而靜態方法並不會 19 | 20 | ### 實用/不實用? 21 | 22 | 但我覺得他並不實用,首先與類有直接關係的函數我們可以用 `classmethod` 就好,在代碼之中我們依靠第一項引數提供的 class 參考能夠完成與類有關的操作 (比如說 `__init__` 的替代物 或是 做為一個調度更多子 `staticmethod` 的上層方法)。 23 | 24 | 若非與類有關(而又與實例無關),那我們需要類別靜態方法的目的,我暫時只想的到一個: 為了調用上能夠有一個抽象的層級 (沒錯,我說的就是 **namespace**) 而此函數雖然不會直接接觸類但是卻與類有關。 25 | 26 | 但是在 Python 之中,使用 `namespace` 是很容易的事情,我們不如將原先想要定義成靜態方法的函數定義在 module 層級,且盡可能地放在有關類別的附近,我想就非常足夠了([Luciano Ramalho](https://www.linkedin.com/in/lucianoramalho) 的觀點)。 27 | 28 | ### 反思 29 | 30 | 就在我打完上面這段之後,我特意看了一次 Julien Danjou 的一篇超棒的文章: 31 | [The definitive guide on how to use static, class or abstract methods in Python](https://julien.danjou.info/blog/2013/guide-python-static-class-abstract-methods) 32 | 我強力推薦任何想弄清楚 Python method 的人看這篇文章。 33 | 34 | 這篇文章提出了兩點 `staticmethod` 的優點,第一點我在上面已經有提到了,靜態方法比起實例方法來的簡明與有效率。第二點是靜態方法 **雖然與類無關但是屬於類的** ,這代表他可以客製化地為類服務。這有點難懂,我們看一個例子好了( Julien Danjou 文章中例子): 35 | 36 | ```python 37 | class Pizza(object): 38 | @staticmethod 39 | def mix_ingredients(x, y): 40 | return x + y 41 | 42 | def cook(self): 43 | return self.mix_ingredients(self.cheese, self.vegetables) 44 | ``` 45 | 46 | 我們想像一下,如果我們把 `mix_ingredients` 定義在 module level,那當我們在處理繼承 `Pizza` 的子類時,勢必無法藉由改動 `mix_ingredients` 來更動 mix ingredients 的行為 (因為該函數有別的類在使用),那我們只好複寫 `cook` 了。 47 | 48 | 這個理由有一點點讓我改觀,起碼他指出了一個靜態方法跟一般方法的最大不同,**靜態方法是單屬於某一類的**。 49 | 50 | 不過我還是比較堅持原先的想法,因為也許 `mix_ingredients` 可以寫得更好,又或者是對於 Pizza 這種非抽象類別根本是不要去繼承他的,又或是繼承的時候複寫方法是不好的,甚至我可以覺得更動 `mix_ingredients` 跟更動 `cook` 同樣都是個負擔。 51 | 52 | ### 小結 53 | 54 | 以上都是 **我覺得**,也許哪一天 `staticmethod` 讓我真正優雅地派上用場了,我可能會承認今天我愚昧而膚淺的認識吧。至於你呢? 我覺得你可以有自己的想法,只要你在了解夠多的情況下還能說服自己,那我覺得沒有任何立場是錯誤的 :) 55 | -------------------------------------------------------------------------------- /questions/others/中文按照拼音排序.md: -------------------------------------------------------------------------------- 1 | # 中文按照拼音排序 2 | 3 | ## 問題 4 | 5 | ``` 6 | lst = ['鑫','鹭','榕','柘','珈','骅','孚','迦','瀚','濮','浔','沱','泸','恺','怡','岷','萃','兖'] 7 | ``` 8 | 9 | 這個 list 裡的中文求按 漢語拼音首字母 排序 10 | 11 | 問題出自 [segmentfault](https://segmentfault.com/q/1010000005937558/a-1020000005937882), by [easonhan0408](https://segmentfault.com/u/easonhan0408) 12 | 13 | ## 回答 14 | 15 | Python 的話可以利用 [pypinyin](https://pypi.python.org/pypi/pypinyin): 16 | 17 | ```python 18 | from pypinyin import lazy_pinyin 19 | 20 | chars = ['鑫','鹭','榕','柘','珈','骅','孚','迦','瀚','濮','浔','沱','泸','恺','怡','岷','萃','兖'] 21 | 22 | chars.sort(key=lambda char: lazy_pinyin(char)[0][0]) 23 | 24 | print([lazy_pinyin(char) for char in chars]) 25 | print(chars) 26 | ``` 27 | 28 | 結果: 29 | 30 | ```python 31 | [['cui'], ['fu'], ['hua'], ['han'], ['jia'], ['jia'], ['kai'], ['lu'], ['lu'], ['min'], ['pu'], ['rong'], ['tuo'], ['xin'], ['xun'], ['yi'], ['yan'], ['zhe']] 32 | ['萃', '孚', '骅', '瀚', '珈', '迦', '恺', '鹭', '泸', '岷', '濮', '榕', '沱', '鑫', '浔', '怡', '兖', '柘'] 33 | ``` 34 | 35 | 以上排序 **僅按拼音首字母** 排序 36 | -------------------------------------------------------------------------------- /questions/pip/pip無法在安裝pyinstaller.md: -------------------------------------------------------------------------------- 1 | # pip 無法在安裝 pyinstaller 2 | 3 | ## 問題 4 | 5 | ``` 6 | $ pip install pyinstaller 7 | ``` 8 | 9 | 後運行: 10 | 11 | ``` 12 | $ pyinstaller - F文件 13 | ``` 14 | 15 | pyinstaller無法在命令行運行,會提示報錯: 16 | 17 | ``` 18 | failed to create process. 19 | ``` 20 | 21 | 問題出自 [segmentfault](https://segmentfault.com/q/1010000005615543/a-1020000005616501), by [hongfeiyu](https://segmentfault.com/u/hongfeiyu) 22 | 23 | ## 回答 24 | 25 | 我了解了一下感覺上應該是 `pip` 的一個 bug [Spaces in Python path make pip-installed launchers fail on Windows][1] 26 | 27 | ### 原因分析 28 | 29 | 簡單來說就是 shebang line (script 裡面指定 python path 的那一行) 裡面有空白導致的,可能就是安裝 Python 的路徑上有空白而且 `pip` 在安裝的時候沒正確幫你加上引號,例如你的 Python 在: 30 | 31 | ``` 32 | C:\Program Files (x86)\Python35-32\python.exe 33 | ^ ^ 34 | 空白惹事 35 | ``` 36 | 37 | 然後用 `pip` 安裝的 Pyinstaller 的 script 裡面對於路徑上的空白沒有正確的用 **引號** 處理好: 38 | 39 | ```python 40 | #!C:\Program Files (x86)\Python35-32\python.exe <-- 這個 shebang line 有問題,因為空白 41 | # EASY-INSTALL-ENTRY-SCRIPT: 'PyInstaller==3.1.1','console_scripts','pyinstaller' 42 | ``` 43 | 44 | ### 解決辦法 45 | 46 | 有一些 workaround 的方式可以解決,第一個是你直接去 pyinstaller 的 script 裡面利用引號把空白問題給搞定(在 Python 目錄下的 Script 子目錄下): 47 | 48 | ```python 49 | #!"C:\Program Files (x86)\Python35-32\python.exe" <-- 這個 shebang line 有問題,因為空白,我們補上前後的引號 50 | # EASY-INSTALL-ENTRY-SCRIPT: 'PyInstaller==3.1.1','console_scripts','pyinstaller' 51 | ``` 52 | 53 | 或是直接用 Python 運行 script (不透過 shebang line 了): 54 | 55 | ``` 56 | C:>"C:\Program Files (x86)\Python35-32\python.exe" "C:\Program Files (x86)\Python35-32\Scripts\pyinstaller-script.py" script_to_compile.py 57 | ``` 58 | 59 | 我查到還有人用一招,重裝 Python 在路徑沒有空白的地方(笑),真的也是一招。 60 | 61 | [1]: https://github.com/pypa/pip/issues/2783 62 | -------------------------------------------------------------------------------- /questions/pip/python代碼怎麼打包.md: -------------------------------------------------------------------------------- 1 | # Python 代碼怎麼打包 2 | 3 | ## 問題 4 | 5 | python 代碼怎麼打包(製作成套件)。像 java 一樣打個包共別人引用 6 | 7 | 問題出自 [segmentfault](https://segmentfault.com/q/1010000005942981/a-1020000005943604), by [進西米大](https://segmentfault.com/u/jinximida) 8 | 9 | ## 回答 10 | 11 | 參考我寫的 [It's Django](http://www.books.com.tw/products/0010676433) 一書中的 [教學](http://dokelung-blog.logdown.com/posts/243281-notes-django-python-modules-and-kits) 12 | 13 | 我直接節錄相關這段給你看: 14 | 15 | ### 上傳自己的套件 16 | 17 | 我們看了、用了那麼多別人寫的套件,自己會不會很想寫一個呢?本章前面的內容已經足夠讓大家了解如何自己撰寫一個套件並在本地端使用,但是我們要如何將這個套件分享給全世界呢?那當然就是要把套件上傳到PyPI囉!(這樣子所有人都可以用pip來下載和安裝你的套件呢)。這個小節就是要教大家怎麼作。 18 | 19 | > 如果打算要上傳自己的套件,那要先去PyPI上註冊取得帳號跟密碼喔。 20 | 21 | 首先,大家要知道,如果自己的Python專案不只想要能從現行或相對的資料夾中匯入,而想要能夠安裝到Python環境中,一定要在專案的目錄底下撰寫`setup.py`檔。這邊針對還要上傳到PyPI,來客製化我們的setup.py,我們先來檢視一下一個待上傳的專案結構和和我們要加入的幾個設定檔位置: 22 | 23 | ``` 24 | MyProject/ 25 | ├── README 26 | ├── pkgA 27 | │   ├── __init__.py 28 | │   ├── modA_1.py 29 | │   └── modA_2.py 30 | ├── pkgB 31 | │   ├── __init__.py 32 | │   └── modB.py 33 | ├── runner 34 | ├── .pypirc # 加入.pypirc 35 | └── setup.py # 加入setup.py 36 | ``` 37 | 38 | 我們注意到原本該專案底下有兩個套件`pkgA`、`pkgB`(通常我們一個專案裡面會有數個套件,而我們在上傳專案的同時要將所屬的套件全數上傳),裡面分別有數個模組跟標示其為套件結構的`__init__.py`。接著我們會有一個說明檔`README`(用來介紹該專案的結構與使用方式,在此便不細談)和一個執行檔(整個專案的執行腳本碼)`runner`。 39 | 40 | 接著我們要在原本的專案中加入兩個設定檔,分別是`setup.py`和`.pypirc`檔,我們會逐一介紹這些檔案該如何撰寫。 41 | 42 | 首先來看到`setup.py`: 43 | 44 | ```python 45 | from distutils.core import setup 46 | 47 | setup( 48 | name = 'MyProject', 49 | packages = ['pkgA', 'pkgB'], 50 | scripts = ['runner'], 51 | version = '1.0', 52 | description = 'My first project', 53 | author = 'dokelung', 54 | author_email = 'dokelung@gmail.com', 55 | keywords = ['Good Project'], 56 | classifiers = [], 57 | ) 58 | ``` 59 | 60 | 我們必須要從`distutils.core`(distutils是Python內建的套件)中匯入`setup`函式,此函式會幫助我們進行安裝,讓我們來了解一下此函式中每個參數的意義: 61 | 62 | | 欄位名稱 | 描述 | 63 | |---|---| 64 | | name | 專案名稱(與專案目錄同名) | 65 | | packages | 要安裝的套件名稱 | 66 | | scripts | script名稱,通常代表一個執行檔,不一定有 | 67 | | version | 版本 | 68 | | description | 專案描述 | 69 | | author | 作者 | 70 | | author_email | 作者信箱 | 71 | | keywords | 這個專案的一些關鍵字 | 72 | 73 | 這邊稍微解釋一下`scripts`,這邊要寫在scripts裡的是整個專案的執行檔,他可能用到了專案裡面的套件。為了要將該檔案同時也裝到使用者的系統上,我們需要把他也標註上去,否則後面我們利用pip安裝的時候就只會安裝package而不會安裝執行檔了。而這邊所謂對執行檔的安裝,其實也就是把指定的scripts放到一個可執行路徑裡,例如`/usr/bin/`中,如此使用者在安裝完後可在任何地方運行該script(其實我們能夠指定安裝的路徑,但如果只給script名稱,那他會被放在預設的位置)。這邊有一點一定要注意,script的名字千萬不要跟他要匯入的pakcage同名了,這會導致一些匯入上的失誤。 74 | 75 | 至此,已經擁有一個漂亮的安裝檔了(同時也能支援PyPI發佈)。 76 | 77 | 接著,我們來看看`.pypirc`檔,唯有建立此設定檔才能讓我們傳東西到PyPI上面: 78 | 79 | ```python 80 | [distutils] 81 | index-servers = 82 | pypi 83 | 84 | [pypi] 85 | repository: https://pypi.python.org/pypi 86 | username: (此處填帳號) 87 | password: (此處填密碼) 88 | ``` 89 | 90 | 如果是Windows的使用者,請打開終端機(命令提示字元),進行環境變數的設定: 91 | 92 | ```sh 93 | set HOME=C:\Users\Owner\ 94 | ``` 95 | 96 | 接著將我們在C槽的使用者資料夾(其實就是`C:\Users`)裡面新增一個子目錄`Owner`,把我們的`.pipyrc`複製一份放到該資料夾下,就算設定完成囉。 97 | 98 | 如果是Linux或是Mac的使用者,也請將`.pypirc`複製到家目錄底下: 99 | 100 | ```sh 101 | $ cp .pypirc ~/.pypirc 102 | ``` 103 | 104 | 上述設定檔備妥後,就到了最後一個階段,首先註冊: 105 | 106 | ```sh 107 | $ python setup.py register -r pypi 108 | ``` 109 | 110 | 接著上傳: 111 | 112 | ```sh 113 | $ python setup.py sdist upload -r pypi 114 | ``` 115 | 116 | 太好了,你成功的讓全世界都能看到你的作品了,上PyPI看看你的package頁面吧。終於,我們嘗到了甜美的果實,緊接著利用`pip`下載我們的專案(和裡面的套件)並安裝到自己的電腦看看: 117 | 118 | ```sh 119 | $ pip install MyProject 120 | ``` 121 | 122 | > 如果讀者有用pip在自己的電腦上安裝了上傳的專案,該專案底下的套件就可以在本機端任何地方匯入了。 123 | 124 | 然後你就可以昭告天下,你也是Python的貢獻者了。 125 | -------------------------------------------------------------------------------- /questions/scope/為什麼這兩段 python lambda 出現不同的結果.md: -------------------------------------------------------------------------------- 1 | # 為什麼這兩段 python lambda 出現不同的結果 2 | 3 | ## 問題 4 | 5 | 寫了這樣一段代碼: 6 | 7 | ```python 8 | [i(4) for i in [(lambda y: print('y=', y, '; x=', x, ' ; n + x = ', y + x)) for x in range(10)]] 9 | ``` 10 | 11 | 發現輸出如下: 12 | 13 | ``` 14 | y= 4 ; x= 9 ; n + x = 13 15 | y= 4 ; x= 9 ; n + x = 13 16 | y= 4 ; x= 9 ; n + x = 13 17 | y= 4 ; x= 9 ; n + x = 13 18 | y= 4 ; x= 9 ; n + x = 13 19 | y= 4 ; x= 9 ; n + x = 13 20 | y= 4 ; x= 9 ; n + x = 13 21 | y= 4 ; x= 9 ; n + x = 13 22 | y= 4 ; x= 9 ; n + x = 13 23 | y= 4 ; x= 9 ; n + x = 13 24 | ``` 25 | 26 | 修改了一下: 27 | 28 | ```python 29 | import functools 30 | 31 | [f(4) for f in [functools.partial(lambda x, y: print('y=', y, '; x=', x, ' ; y + x = ', y + x), y) for y in range(10)]] 32 | ``` 33 | 34 | 輸出如下: 35 | 36 | ``` 37 | y= 4 ; x= 0 ; y + x = 4 38 | y= 4 ; x= 1 ; y + x = 5 39 | y= 4 ; x= 2 ; y + x = 6 40 | y= 4 ; x= 3 ; y + x = 7 41 | y= 4 ; x= 4 ; y + x = 8 42 | y= 4 ; x= 5 ; y + x = 9 43 | y= 4 ; x= 6 ; y + x = 10 44 | y= 4 ; x= 7 ; y + x = 11 45 | y= 4 ; x= 8 ; y + x = 12 46 | y= 4 ; x= 9 ; y + x = 13 47 | ``` 48 | 49 | 不知道為什麼會有這樣的差異,請指教 50 | 51 | 問題出自 [segmentfault](https://segmentfault.com/q/1010000006121417/a-1020000006121940), by [kavlez](https://segmentfault.com/u/kavlez) 52 | 53 | ## 回答 54 | 55 | 你問的這個問題不是那麼好理解, 不過我慢慢說看看能接受多少。 56 | 57 | ### 第一段 code 58 | 59 | 首先我們看到你寫的第一段 code: 60 | 61 | ```python 62 | [i(4) for i in [(lambda y: print('y=', y, '; x=', x, ' ; n + x = ', y + x)) for x in range(10)]] 63 | ``` 64 | 65 | 我們先專心看內層的 list comprehension: 66 | 67 | ```python 68 | funclist1 = [(lambda y: print('y=', y, '; x=', x, ' ; n + x = ', y + x)) for x in range(10)] 69 | ``` 70 | 71 | 這段我們可以寫成一段等價的 code: 72 | 73 | ```python 74 | def produce_functions(): 75 | funclist = [] 76 | for x in range(10): 77 | def ld(y): 78 | print('y=', y, '; x=', x, ' ; n + x = ', y + x) 79 | funclist.append(ld) 80 | return funclist 81 | 82 | funclist2 = produce_functions() 83 | ``` 84 | 85 | 我們隨意地來測試一下: 86 | 87 | ```python 88 | >>> funclist1[0](4) 89 | y= 4 ; x= 9 ; n + x = 13 90 | 91 | >>> funclist2[0](4) 92 | y= 4 ; x= 9 ; n + x = 13 93 | ``` 94 | 95 | 希望大家看到這裡可以接受兩者除了一個用 lambda funciton 一個用 normal function 但是其實行為上是一致的。 96 | 97 | 接著請大家仔細看上面那段等價的 code, 你想到了甚麼呢? 沒錯! decorator 裡面會出現的 **閉包(closure)** !! 在這裡 `x` 不就是 **free variable** 嗎? 所以 `x` 並不會被綁死在 `ld` 中, 他參考到一個非全域的變數 `x`。我要說的是, 製造出來的 10 個 function 全部都參考到同一個 `x`, `for x in range(10)` 很容易誤導大家, 以為有十個不同的 `x` 然後製造了 10 個不同的 function。其實你跟我都明白, 這裡只有一個 `x` 變數, 只是他的值在改變, 但是說到底 `x` 就那麼一個。 98 | 99 | 為了避免空口說白話, 我證明我說的給大家看: 100 | 101 | ```python 102 | >>> funclist1[0].__code__.co_freevars 103 | ('x',) 104 | >>> funclist2[0].__code__.co_freevars 105 | ('x',) 106 | ``` 107 | 108 | 好, 那 `x` 的值在製造完之後究竟是多少呢? 109 | 110 | ```python 111 | >>> funclist2[0].__closure__ 112 | (,) 113 | 114 | >>> funclist2[0].__closure__[0] 115 | 116 | 117 | >>> funclist2[0].__closure__[0].cell_contents 118 | 9 119 | ``` 120 | 121 | 沒錯, 是 9 !, 所有所有製造出來的 function 全部都會參考到這個 `x`, 所以他們的 `x` 值就是 9 ! 122 | 123 | 所以, 出現這個結果也就不意外了: 124 | 125 | ```python 126 | y= 4 ; x= 9 ; n + x = 13 127 | y= 4 ; x= 9 ; n + x = 13 128 | y= 4 ; x= 9 ; n + x = 13 129 | ... 130 | ``` 131 | 132 | ### 第二段 code 133 | 134 | 接著我們來看第二段 code (一樣 focus 在內層): 135 | 136 | ```python 137 | import functools 138 | 139 | [functools.partial(lambda x, y: print('y=', y, '; x=', x, ' ; y + x = ', y + x), y) for y in range(10)] 140 | ``` 141 | 142 | `partial(func, a)` 會凍結 `func` 的第一個引數(會綁定 a 值) 143 | 144 | 所以上面這段 code 會從 0~9 凍結這個 lambda function 的第一個引數, 這邊不同於 free variables, 這裡不是讓第一個引數 `x` 參考到同一個人, 而是直接綁死(代定) 0~9, 所以這裡每一個製造出來的 functions 全部都不一樣, 且可以當成 x 指定為 0~9。 145 | 146 | 經過上述講解, 應該大致可以明白兩者不同之處了。 147 | 148 | ### 結論 149 | 150 | * `partial` 會直接凍結引數, 使變數代入定值 151 | * 一般的 function 會直接參考到 free variable 而不是代入定值 152 | -------------------------------------------------------------------------------- /questions/sort/Python排序問題.md: -------------------------------------------------------------------------------- 1 | # Python排序問題 2 | 3 | ## 問題 4 | 5 | 下面是題目要求: 6 | 7 | 用 Python 實現函數 `count_words()`,該函數輸入字符串 `s` 和數字 `n`,返回 `s` 中 `n` 個出現頻率最高的單詞。返回值是一個元組列表,包含出現次數最高的 `n` 個單詞及其次數,即 [(<單詞1>, <次數1>), (<單詞2>, <次數2>), ... ] ,按出現次數降序排列。 8 | 9 | 您可以假設所有輸入都是小寫形式,並且不含標點符號或其他字符(只包含字母和單個空格)。如果出現次數相同,則按字母順序排列。 10 | 11 | 例如: 12 | 13 | ```python 14 | print(count_words( "betty bought a bit of butter but the butter was bitter" , 3 )) 15 | ``` 16 | 17 | 輸出: 18 | 19 | ```python 20 | [('butter', 2 ), ('a', 1 ), ('betty', 1 )] 21 | ``` 22 | 23 | 我已經按照單詞出現頻率排好了順序,但是怎麼在這基礎上再實現字母排序? 24 | 25 | 問題出自 [segmentfault](https://segmentfault.com/q/1010000005821181/a-1020000005822587), by [marsggbo](https://segmentfault.com/u/marsggbo) 26 | 27 | ## 回答 28 | 29 | 這應該滿足你的條件,只是我自認為寫得很醜,等有時間再優化吧: 30 | 31 | (改了一下 code 這樣做比較好!) 32 | 33 | ```python 34 | import collections 35 | 36 | def count_words(s, n): 37 | lst = collections.Counter(s.split()).most_common() 38 | lst.sort(key=lambda t: t[0]) 39 | lst.sort(key=lambda t: t[1], reverse=True) 40 | return lst[0:n] 41 | 42 | print(count_words("betty bought a bit of butter but the butter was bitter", 3)) 43 | ``` 44 | 45 | 想了一想,覺得下面更好: 46 | 47 | ```python 48 | import collections 49 | 50 | def count_words(s, n): 51 | lst = collections.Counter(s.split()).most_common() 52 | lst.sort(key=lambda t: (-t[1], t[0])) 53 | return lst[0:n] 54 | 55 | print(count_words("betty bought a bit of butter but the butter was bitter", 3)) 56 | ``` 57 | -------------------------------------------------------------------------------- /questions/sort/sorted函數中key參數的作用原理.md: -------------------------------------------------------------------------------- 1 | # sorted 函數中 key 參數的作用原理 2 | 3 | ## 問題 4 | 5 | 這是一個字符串排序,排序規則:小寫<大寫<奇數<偶數 6 | 7 | ```python 8 | s = 'asdf234GDSdsf23' #排序:小写-大写-奇数-偶数 9 | keyfunc = lambda x: (x.isdigit(), x.isdigit() and int(x) % 2 == 0, x.isupper(), x.islower(), x) 10 | print("".join(sorted(s, key=keyfunc))) 11 | ``` 12 | 13 | 這裡 key 接受的函數返回的是一個元組?是如何進行比較的? 14 | 15 | 問題出自 [segmentfault](https://segmentfault.com/q/1010000005111826/a-1020000005112829), by [肥貓_joe](https://segmentfault.com/u/feimao_joe) 16 | 17 | ## 回答 18 | 19 | 讓我們從一個簡單的例子開始: 20 | 21 | ```python 22 | items = [(1, 2), (2, 1)] 23 | print(sorted(items)) 24 | ``` 25 | 26 | 結果: 27 | 28 | ```python 29 | [(1, 2), (2, 1)] 30 | ``` 31 | 32 | `items` 是一個 list of tuple,如果針對 tuple 排序,Python 的 Builtin function `sorted`(或是`sort`) 會從 tuple 的最後一個元素開始進行排序,也就是說一組二元素的 tuple 進行排序可以想像成兩次基本的排序: 33 | 34 | 原本是: 35 | 36 | ```python 37 | [(2, 1), (1, 2)] 38 | ``` 39 | 40 | 第一次排序以第2個元素為 key,所以排序的結果為: 41 | 42 | ```python 43 | [(2, 1), (1, 2)] 44 | ``` 45 | 46 | 第二次排序以第1個元素為 key,所以排序的結果為: 47 | 48 | ```python 49 | [(1, 2), (2, 1)] # 最終結果 50 | ``` 51 | 52 | ### 結論 1 53 | 54 | tuple 的排序由最後的元素往前依次進行排序 55 | 也就是說 tuple 的排序權重是由第一個元素開始依次向後遞減 56 | 57 | ---------- 58 | 59 | 接著我們來觀察一下 Boolean value 的排序: 60 | 61 | ```python 62 | print(sorted([True, False]) 63 | ``` 64 | 65 | 結果: 66 | 67 | ```python 68 | [False, True] # False在前,True在後 69 | ``` 70 | 71 | ### 結論 2 72 | 73 | Boolean 的排序會將 `False` 排在前,`True`排在後 74 | 75 | ---------- 76 | 77 | 那我們來看看你給出的例子,我們撰寫一個簡單的 function 來觀察結果: 78 | 79 | ```python 80 | def show(s): 81 | for x in s: 82 | print((x.isdigit(), x.isdigit() and int(x)%2==0, x.isupper(), x.islower(), x)) 83 | ``` 84 | 85 | function `show` 會列印出當下的字串 `s` 用來排序時每個字元所產生的 tuple key. 86 | 87 | 接著我們套用剛剛的結論1,我們先不使用 tuple 來作為 key,反而利用等價的 **由最後一個元素往前依次為 key 排序**,並且逐步觀察 `s` 和 tuple key 的變化: 88 | 89 | ```python 90 | print('key=x') 91 | s = sorted(s ,key=lambda x: x) 92 | show(s) 93 | 94 | print('key=islower()') 95 | s = sorted(s ,key=lambda x: x.islower()) 96 | show(s) 97 | 98 | print('key=isupper()') 99 | s = sorted(s ,key=lambda x: x.isupper()) 100 | show(s) 101 | 102 | print('key=isdigit() and int(x)%2==0') 103 | s = sorted(s ,key=lambda x: x.isdigit() and int(x)%2==0) 104 | show(s) 105 | ``` 106 | 107 | 我們將會發現一如預期地,依照結論(1),這樣的做法的確等價於一口氣以 tuple 為 key 來排序. 108 | 同時觀察,結論(2),對於 `isdigit()`, `isupper()`, `islower()`等所產生的 Boolean key 來說,排序的結果也如預期. 109 | 110 | ```python 111 | print('key=isdigit()') 112 | s = sorted(s ,key=lambda x: x.isdigit()) 113 | show(s) 114 | ``` 115 | 116 | ---------- 117 | 118 | 不過我想這還不是我們最後的結論,因為這是一個碰巧的結果(說碰巧也許太超過了,應該說是不那麼直覺的結果),讓我們根據 **結論(1)** 對最初的例子進行分析: 119 | 120 | ```python 121 | sorted(s, key=lambda x: (x.isdigit(), x.isdigit() and int(x) % 2 == 0, x.isupper(), x.islower(), x)) 122 | ``` 123 | 124 | 這個排序我們可以翻譯成: 125 | 126 | >先對字元x本身做排序,接著是對 字元是否為小寫,字元是否為大寫,字元是否為偶數,字元是否為數字分別作排序. 127 | 128 | 也可以翻譯成: 129 | 130 | > 我們以 字元是否為數字為最高排序權重,接著以字元是否為偶數,字元是否為大寫,字元是否為小寫,字元x本身為權重來做排序. 131 | 132 | 這似乎與一開始的目標(#排序:小写-大写-奇数-偶数)不同,起碼跟目標沒有直覺上的對應. 133 | 134 | 建議可以改成: 135 | 136 | ```python 137 | keyfunc = lambda x: (not x.islower(), not x.isupper(), not(x.isdigit() and int(x)%2==1), x) 138 | print("".join(sorted(s1, key=keyfunc))) 139 | ``` 140 | 141 | 這樣就可以解讀為: 142 | 143 | > 我們以 字元是否為小寫為最高權重,接著以字元是否為大寫,字元是否為奇數,字元x本身為權重來做排序 144 | 145 | 有趣的是:我們想要讓判斷式為 `True` 的字元在排序完成後在比較前面的位置,所以根據結論(2)加了一個 `not`來讓符合的字元可以在前面. 146 | -------------------------------------------------------------------------------- /questions/standard_lib/Python timeit測量代碼運行時間問題.md: -------------------------------------------------------------------------------- 1 | # Python timeit 測量代碼運行時間問題 2 | 3 | ## 問題 4 | 5 | ```python 6 | def is_unique_char(string): 7 | if len(string) > 256: 8 | return True 9 | 10 | record = 0L 11 | 12 | for ch in string: 13 | # print record 14 | ch_val = ord(ch) 15 | 16 | if (record & (1 << ch_val)) > 0: 17 | return False 18 | 19 | record |= (1 << ch_val) 20 | 21 | return True 22 | 23 | 24 | import string 25 | s1 = string.ascii_letters + string.digits 26 | 27 | 28 | if __name__ == '__main__': 29 | import timeit 30 | print is_unique_char(s1) 31 | print timeit.timeit("is_unique_char(s1)", 32 | setup="from __main__ import is_unique_char, s1") 33 | ``` 34 | 35 | 代碼如上, `is_unique_char` 就是一個包含位運算的函數(具體作用不重要) 36 | 運行代碼,秒出 `print is_unique_char(s1)` 的結果,但是 `timeit` 測量需要 30 多秒。 37 | 38 | 這是為什麼呢?會不會是因為位運算? 呃,先感謝大家解答🙏 39 | 40 | 問題出自 [segmentfault](https://segmentfault.com/q/1010000005924858/a-1020000005925103), by [neo1218](https://segmentfault.com/u/neo1218) 41 | 42 | ## 回答 43 | 44 | 簡單來說,`timeit` 會執行代碼 `1000000` 次...,當然要花很久囉。 45 | 46 | 這個 function 是用來測量某段代碼的平均運行時間,所以你必須除以他執行的次數。 47 | 48 | 我改了一下你的代碼並且用 `time.time` 測了一下: 49 | 50 | ```python 51 | # uc.py 52 | 53 | import string 54 | 55 | def is_unique_char(string): 56 | if len(string) > 256: 57 | return True 58 | 59 | record = 0L 60 | 61 | for ch in string: 62 | # print record 63 | ch_val = ord(ch) 64 | 65 | if (record & (1 << ch_val)) > 0: 66 | return False 67 | 68 | record |= (1 << ch_val) 69 | 70 | return True 71 | 72 | s1 = string.ascii_letters + string.digits 73 | ``` 74 | 75 | ```python 76 | import timeit 77 | import time 78 | from uc import is_unique_char, s1 79 | 80 | if __name__ == '__main__': 81 | btime = time.time() 82 | is_unique_char(s1) 83 | etime = time.time() 84 | print etime - btime 85 | print timeit.timeit("is_unique_char(s1)", setup="from uc import is_unique_char, s1")/1000000 86 | ``` 87 | 88 | 結果: 89 | 90 | ``` 91 | 4.91142272949e-05 92 | 2.89517600536e-05 93 | ``` 94 | 95 | 結果是單次的運行差不多... 96 | -------------------------------------------------------------------------------- /questions/standard_lib/Python 獲取文件路徑及文件目錄(__file__ 的使用方法).md: -------------------------------------------------------------------------------- 1 | # Python 獲取文件路徑及文件目錄(`__file__` 的使用方法) 2 | 3 | ## 問題 4 | 5 | 我正在學習Python,不過遇到一些問題,想請教: 6 | 7 | `os` module 中的 `os.path.dirname(__file__)` 和 `os.path.abspath(__file__)` 8 | 9 | 運行 `os.path.dirname(__file__)` 時候,為什麼返回的是空白呢? 是不是因為他運行的是相對路徑??? 10 | 11 | 如果是的話: 12 | 13 | 1. 我怎麼能夠知道,括號內的文件是以相對路徑還是絕對路徑被運行的? 14 | 2. 為什麼我運行下面例子腳本的時候,這個文件是以相對路徑被運行的呢? 15 | 16 | 比如我下面的例子: 17 | 18 | ```python 19 | import os 20 | 21 | print (os.path.dirname(__file__)) 22 | print (os.path.abspath(__file__)) 23 | print (os.path.abspath(os.path.dirname(__file__))) 24 | print (os.path.dirname(os.path.abspath(__file__))) 25 | ``` 26 | 27 | ![测试][1] 28 | [1]: https://segmentfault.com/img/bVzRXy 29 | 30 | PS:附加問題 31 | `os.path.abspath(os.path.dirname(__file__))` 和 `os.path.dirname(os.path.abspath(__file__))` 性質是否一樣呢? 32 | 33 | 問題出自 [segmentfault](https://segmentfault.com/q/1010000006126582/a-1020000006127638), by [nodinner](https://segmentfault.com/u/nodinner) 34 | 35 | ## 回答 36 | 37 | 建議你可以稍微瀏覽一下 Python doc: [os.path](https://docs.python.org/3.5/library/os.path.html), 你就會明白囉: 38 | 39 | 我放上跟你問題相關的幾個條目: 40 | 41 | * `os.path.abspath(path)` 42 | * Return a normalized absolutized version of the pathname path. On most platforms, this is equivalent to calling the function normpath() as follows: normpath(join(os.getcwd(), path)). 43 | 44 | * `os.path.normpath(path)` 45 | * Normalize a pathname by collapsing redundant separators and up-level references so that A//B, A/B/, A/./B and A/foo/../B all become A/B. This string manipulation may change the meaning of a path that contains symbolic links. On Windows, it converts forward slashes to backward slashes. To normalize case, use normcase(). 46 | 47 | * `os.path.dirname(path)` 48 | * Return the directory name of pathname path. This is the first element of the pair returned by passing path to the function split(). 49 | 50 | * `os.path.split(path)` 51 | * Split the pathname path into a pair, (head, tail) where tail is the last pathname component and head is everything leading up to that. The tail part will never contain a slash; if path ends in a slash, tail will be empty. If there is no slash in path, head will be empty. If path is empty, both head and tail are empty. Trailing slashes are stripped from head unless it is the root (one or more slashes only). In all cases, join(head, tail) returns a path to the same location as path (but the strings may differ). Also see the functions dirname() and basename(). 52 | 53 | 我們做以下觀察: 54 | 55 | `test.py` 56 | 57 | ```python 58 | import os 59 | 60 | print(__file__) 61 | print(os.path.dirname(__file__)) 62 | print(os.path.abspath(__file__)) 63 | print(os.path.abspath(os.path.dirname(__file__))) 64 | print(os.path.dirname(os.path.abspath(__file__))) 65 | ``` 66 | 67 | 運行: 68 | 69 | ``` 70 | $ pwd 71 | /home/dokelung 72 | $ python test.py 73 | ``` 74 | 75 | 結果: 76 | 77 | ``` 78 | test.py 79 | 80 | /home/dokelung/test.py 81 | /home/dokelung 82 | /home/dokelung 83 | ``` 84 | 85 | 首先 `__file__` 的值其實就是在命令列上 invoke Python 時給的 script 名稱: 86 | 87 | ```python 88 | $ python test.py # 此時 __file__ 是 test.py 89 | $ python ../test.py # 此時 __file__ 是 ../test.py 90 | $ python hello/../test.py # 此時 __file__ 是 hello/../test.py 91 | ``` 92 | 93 | 在這裡, 因為 `__file__` 的值為 `test.py`, 所以 `print(__file__)` 的結果是 `test.py` 也就不意外了。 94 | 95 | 接著, `os.path.dirname(__file__)`之所以得出空白(空字串), 是因為 `__file__` 就只是一個單純的名稱(非路徑) 且 `dirname` 也只是很單純的利用 `os.path.split()` 來切割這個名稱(這當然沒甚麼好切的, 連路徑分割符都沒有): 96 | 97 | ```python 98 | >>> import os 99 | >>> os.path.split('test.py') 100 | ('', 'test.py') 101 | >>> os.path.split('test.py')[0] 102 | '' 103 | ``` 104 | 105 | 我分會發現切出來的 `head` 是空字串, 所以 `dirname` 的結果是空白。 106 | 107 | `abspath` 動用了 `os.getcwd()` 所以即便給定的是單純的名稱, 也能返回路徑: 108 | 109 | ```python 110 | >>> os.getcwd() 111 | '/home/dokelung' 112 | 113 | >>> os.path.join(os.getcwd(), 'test.py') 114 | '/home/dokelung/test.py' 115 | 116 | >>> os.path.normpath(os.path.join(os.getcwd(), 'test.py')) 117 | '/home/dokelung/test.py' 118 | ``` 119 | 120 | 而 `os.path.abspath(os.path.dirname(__file__))` 的結果就等於是 `os.getcwd()` 的結果去接上 `dirname` 得到的空字串: 121 | 122 | ```python 123 | >>> os.path.dirname('test.py') 124 | '' 125 | 126 | >>> os.path.join(os.getcwd(), os.path.dirname('test.py')) 127 | '/home/dokelung/' 128 | ``` 129 | 130 | 最後, `os.path.dirname(os.path.abspath(__file__))` 的結果是這麼來的: 131 | 132 | ```python 133 | >>> os.path.abspath('test.py') 134 | '/home/dokelung/test.py' 135 | 136 | >>> os.path.split(os.path.abspath('test.py')) 137 | ('/home/dokelung', 'test.py') 138 | 139 | >>> os.path.split(os.path.abspath('test.py'))[0] 140 | '/home/dokelung' 141 | ``` 142 | 143 | 希望講到這裡有讓你明白! 144 | 145 | ## 結論 146 | 現在簡要的回答你的問題 147 | 148 | 1. 為什麼 `dirname` 出現空白? 149 | * 因為你運行的時候給的是單純的名稱, 所以 `__file__` 是單純的名字非路徑 150 | 151 | 2. 我怎么能够知道,括号内的文件是以相对路径还是绝对路径被运行的? 152 | * 很簡單, 就看你怎麼運行 Python 的 153 | 154 | 3. 为什么我运行下面例子脚本的时候,这个文件是以相对路径被运行的呢? 155 | * 因為 `$ python 1.py` 你自己給了相對路徑 156 | 157 | 4. `os.path.abspath(os.path.dirname(__file__))` 和 `os.path.dirname(os.path.abspath(__file__))` 性质是否一样呢? 158 | * 基本上一樣 159 | -------------------------------------------------------------------------------- /questions/standard_lib/Python日期的遞增問題.md: -------------------------------------------------------------------------------- 1 | # Python 日期的遞增問題 2 | 3 | ## 問題 4 | 5 | * 初始化開始時間 2016-07-01 6 | * 設置日期為 31 7 | 8 | 遞增 9 | 10 | ``` 11 | 2016-07-01 12 | 2016-07-02 13 | ... 14 | 2016-07-31 15 | ``` 16 | 17 | 除了 18 | 19 | ```python 20 | count = 0 21 | while (count < 31): 22 | 23 | count =count+1 24 | print '2016-07-',count 25 | ``` 26 | 27 | 還有沒其他方式 28 | 29 | 問題出自 [segmentfault](https://segmentfault.com/q/1010000006007938/a-1020000006008322), by [monkey_cici](https://segmentfault.com/u/monkey_cici) 30 | 31 | ## 回答 32 | 33 | 用 [datetime](https://docs.python.org/3.5/library/datetime.html) 34 | 35 | datetime 方便又強大, 值得投資一下 36 | 37 | ```python 38 | from datetime import date, timedelta 39 | 40 | def gen_dates(bdate, days): 41 | day = timedelta(days=1) 42 | for i in range(days): 43 | yield bdate + day*i 44 | 45 | if __name__ == '__main__': 46 | bdate = date(2016, 7, 1) 47 | for d in gen_dates(bdate, 31): 48 | print(d) 49 | ``` 50 | 51 | **結果**: 52 | 53 | ``` 54 | 2016-07-01 55 | 2016-07-02 56 | ... 57 | 2016-07-30 58 | 2016-07-31 59 | ``` 60 | 61 | **代碼說明**: 62 | 63 | `date(year, month, day)` 可以很輕鬆地製造出日期 64 | 65 | `timedelta(days, seconds, microseconds, milliseconds, minutes, hours, weeks)` 可以製造出時間間隔 66 | 67 | 然後你可以用一般的代數來操作日期計算: 68 | 69 | ```python 70 | >>> d = date(2016, 7, 1) # 產生 2016-07-01 這個日期 71 | >>> day = timedelta(days=1) # 產生 1 天的時間間隔 72 | >>> print(d+day) # 印出 2016-07-01 + 1 天 73 | 2016-07-02 74 | ``` 75 | -------------------------------------------------------------------------------- /questions/standard_lib/os.mkdir和os.makedirs的區別.md: -------------------------------------------------------------------------------- 1 | # os.mkdir 和 os.makedirs 的區別 2 | 3 | ## 問題 4 | 5 | * 語言: Python2.7 6 | * IDE: Pycharm 7 | * os: Linux 8 | 9 | 用Python做的爬蟲 : 10 | 11 | * 第一個建立在project folder下用os.mkdir('home/img/')創建文件夾存儲數據,文件夾正常建立 12 | * 第二個加入RedisQueue,爬蟲程序放在/usr/lib/python2.7 , rq主體放在project folder下面,在爬蟲程序裡面用os.mkdir('home/img/')報錯,用os.makedirs( 'home/img/')正常建立。 13 | 14 | 為什麼第一個沒有報錯? 15 | 16 | thx in advance 17 | 18 | 問題出自 [segmentfault](https://segmentfault.com/q/1010000005770628/a-1020000005770966), by [shuanglu1993](https://segmentfault.com/u/shuanglu1993) 19 | 20 | ## 回答 21 | 22 | [os.mkdir][1] 與 [os.makedirs][2] 的差別在於 `os.makedirs` 會遞迴地去建立目錄,也就是說連同中繼的目錄也會一起建立,就類似於 Linux 中的 `mkdir -p`. 23 | 24 | ```python 25 | >>> import os 26 | >>> os.mkdir('foo/bar') 27 | Traceback (most recent call last): 28 | File "", line 1, in 29 | OSError: [Errno 2] No such file or directory: 'foo/bar' 30 | >>> os.makedirs('foo/bar') 31 | ``` 32 | 33 | 使用 `os.mkdir` 時,如果你給定的 path 參數是個多層的 path,如果某個中繼的目錄不存在(比如說上例中的 `foo`), Python 將會報錯. 34 | 35 | 但如果使用 `os.makedirs` 則 Python 會連同中間的目錄一起建立.但有一點值得注意,當 path 末端的目錄已經存在的話,`os.makedirs` 也是會引發例外. 36 | 37 | 我想你的問題就在這裡,你可以檢查 `home` 目錄一開始是否存在. 38 | 39 | 要注意的是,這邊的路徑認定是要看你啟動 Python 直譯器的地方,也就是說你要確定你運行 `python` 所在的目錄下面要有 `home` 才能避免 `os.mkdir` 出錯. 40 | 41 | [1]: https://docs.python.org/2/library/os.html#os.mkdir 42 | [2]: https://docs.python.org/2/library/os.html#os.makedirs 43 | -------------------------------------------------------------------------------- /questions/standard_lib/tk程序中出現問題.md: -------------------------------------------------------------------------------- 1 | # tk 程序中出現問題 2 | 3 | 這段代碼為什麼輸入框不顯示任何輸入,只有去掉 `validate = 'key'` 才可以,但是這樣又失去了驗證功能: 4 | 5 | ```python 6 | from tkinter import * 7 | 8 | master = Tk() 9 | 10 | v1 = StringVar() 11 | v2 = StringVar() 12 | v3 = StringVar() 13 | 14 | def test(content) : 15 | return content.isdigit() 16 | 17 | testCMD = master.register(test) 18 | 19 | e1 = Entry(master,textvariable = v1, validate = 'key',\ 20 | validatecommand = (testCMD,'%p')).grid(row = 0,column =0) 21 | 22 | Label(master,text = '+').grid(row = 0,column =1) 23 | 24 | e2 = Entry(master, textvariable=v2, validate = 'key',\ 25 | validatecommand=(testCMD, '%p')).grid(row = 0,column =2) 26 | 27 | Label(master,text = '=').grid(row = 0,column =3) 28 | 29 | e3 = Entry(master,textvariable = v3,state = 'readonly').grid(row = 0,column =4) 30 | 31 | def calc() : 32 | result = int(v1.get()) + int(v2.get()) 33 | v3.set(str(result)) 34 | 35 | Button(master,text = '计算结果',command = calc).grid(row = 1,column =2) 36 | 37 | mainloop() 38 | ``` 39 | 40 | ## 問題 41 | 42 | 問題出自 [segmentfault](https://segmentfault.com/q/1010000005590653), by [ice_sword](https://segmentfault.com/u/ice_sword) 43 | 44 | ## 回答 45 | 46 | 關鍵問題在於 valid percent substitutions 有誤,`validatecommand` 中的 **P 應該要大寫**: `%P` 而不是 `%p`. 47 | 48 | 另外補充一點,左右小括號 `(`, `)` 本身就允許代碼換行,不需要另外加上 `\` 了. 49 | 50 | 以上給你參考. 51 | -------------------------------------------------------------------------------- /questions/standard_lib/一個結構化顯示文件的腳本,迭代是否有問題.md: -------------------------------------------------------------------------------- 1 | # 一個結構化顯示文件的腳本,迭代是否有問題? 2 | 3 | ## 問題 4 | 5 | 最近在學 python,寫了一個小腳本,基本功能就是遍歷顯示指定路徑下的所有文件夾、文件,根據層級在顯示內容前面加 `"--"`。 6 | 但是不管怎麼樣,輸出只有第一層的所有文件和文件夾,仔細的審查了好幾遍代碼,就是找不出原因在哪,求大神告知問題所在,以下是代碼內容: 7 | 8 | ```python 9 | import os 10 | 11 | rootpath = input("请输入目录:") 12 | catedir = os.listdir(rootpath) 13 | 14 | 15 | def prt_dir_files(catedir, is_indent=True, level=0): 16 | print(rootpath) 17 | for each_item in catedir: 18 | if os.path.isdir(each_item): 19 | print(each_item) 20 | prt_dir_files(os.listdir(each_item), is_indent, level+1) 21 | else: 22 | if is_indent: 23 | print("--" * level, end="") 24 | print(each_item) 25 | 26 | prt_dir_files(catedir) 27 | ``` 28 | 29 | 問題出自 [segmentfault](https://segmentfault.com/q/1010000006031336/a-1020000006032597), by [欲淫兮](https://segmentfault.com/u/darkyyx) 30 | 31 | ## 回答 32 | 33 | 關鍵的問題出在, 路徑不對, `listdir` 拿到的只是目錄底下的 **文件名**, 而不是 **文件的路徑**。 34 | 35 | 所以必須要使用 `os.path.join` 來製造出完整的路徑。 36 | 37 | 根據以上說明我改了一下你的代碼: 38 | 39 | ```python 40 | import os 41 | 42 | def prt_dir(path, indent_symbol='', level=0): 43 | print(indent_symbol*level, os.path.basename(path)) 44 | 45 | if os.path.isdir(path): 46 | for f in os.listdir(path): 47 | fpath = os.path.join(path, f) 48 | prt_dir(fpath, indent_symbol, level+1) 49 | 50 | if __name__ == '__main__': 51 | rootpath = input("请输入目录:") 52 | prt_dir(rootpath, '--', 0) 53 | ``` 54 | 55 | 使用 `os.walk` 也是個不錯的主意, 以下範例: 56 | 57 | ```python 58 | import os 59 | 60 | def prt_dir(rootpath, symbol=''): 61 | for root, dirs, files in os.walk(rootpath): 62 | path = root.split('/') 63 | print((len(path) - 1) * symbol, os.path.basename(root)) 64 | for file in files: 65 | print(len(path) * symbol, file) 66 | 67 | 68 | if __name__ == '__main__': 69 | rootpath = input("请输入目录:") 70 | prt_dir(rootpath, '--') 71 | ``` 72 | -------------------------------------------------------------------------------- /questions/standard_lib/一個需要傳入參數的python程序如何封裝成可執行文件.md: -------------------------------------------------------------------------------- 1 | # 一個需要傳入參數的 python 程序,如何封裝成可執行文件 2 | 3 | ## 問題 4 | 5 | 我已經寫好一個 python 程序功能是搜索一段時間範圍內的信息,程序需要傳入參數,即開始和結束的時間。 6 | 7 | 目前想把這個程序封裝成一個可執行文件,這樣其他人拿到程序後在自己電腦上就可以直接跑了,以前用 pyinstaller 試過將 .py 文件轉換成 .exe 文件,不過碰到需要傳參數的情況好像就不適用了。 8 | 想請問大家是否有相關的教程或者工具,將 .py 文件轉化為一個可手動輸入參數(比如時間)的可執行文件? 9 | 10 | 問題出自 [segmentfault](https://segmentfault.com/q/1010000006008157), by [desertfoxx](https://segmentfault.com/u/desertfoxx) 11 | 12 | ## 回答 13 | 14 | command line 介面下簡單來說三種方法: 15 | 16 | 1. 用 `input` 去問使用者 17 | 2. 用 `sys.argv` 18 | 3. 用一些 package/module, 像是 [argparse](https://docs.python.org/3.5/library/argparse.html) 或 [clime](http://clime.mosky.tw/) 19 | 20 | 示範一下 **第二點**: 21 | 22 | 假設你的 python file 叫 `test.py` 23 | 24 | ```python 25 | import sys 26 | print(sys.argv) 27 | ``` 28 | 29 | 運行: 30 | 31 | ``` 32 | $ python test.py hello world 2016-07-01 12:30 33 | ['test.py', 'hello', 'world', '2016-07-01', '12:30'] 34 | ``` 35 | -------------------------------------------------------------------------------- /questions/standard_lib/交換兩個shelve objects.md: -------------------------------------------------------------------------------- 1 | # 如何交換兩個 shelve objects 2 | 3 | ## 問題 4 | 5 | 假定 `db1`, `db2` 是 `shelve` objects: 6 | 7 | ```python 8 | if switch_d1_and_db2: 9 | func(db1, db2) 10 | else: 11 | func(db2, db1) 12 | ``` 13 | 14 | 怎麼才能改寫成: 15 | 16 | ```python 17 | if switch_d1_and_db2: 18 | db1, db2 = db2, db1 # 错误写法 19 | func(db1, db2) 20 | ``` 21 | 22 | `db1, db2 = db2, db1` 肯定是不行的,怎麼改寫呢? 23 | 24 | 所謂不行可以見以下範例: 25 | 26 | `define.py`: 27 | 28 | ```python 29 | import shelve 30 | db1 = shelve.open('db1', writeback = True) 31 | db2 = shelve.open('db2', writeback = True) 32 | db1['name'] = 'db1' 33 | db2['name'] = 'db2' 34 | db1.close() 35 | db2.close() 36 | ``` 37 | 38 | `switch.py`: 39 | 40 | ```python 41 | import shelve 42 | db1 = shelve.open('db1', writeback = True) 43 | db2 = shelve.open('db2', writeback = True) 44 | db1, db2 = db2, db1 45 | print db1 46 | print db2 47 | db1.close() 48 | db2.close() 49 | ``` 50 | 51 | `check.py`: 52 | 53 | ```python 54 | import shelve 55 | db1 = shelve.open('db1', writeback = True) 56 | db2 = shelve.open('db2', writeback = True) 57 | print 'db1:', db1 58 | print 'db2:', db2 59 | ``` 60 | 61 | 測試: 62 | 63 | ``` 64 | $ python define.py 65 | $ python check.py 66 | db1: {'name': 'db1'} 67 | db2: {'name': 'db2'} 68 | $ python switch.py 69 | {'name': 'db2'} 70 | {'name': 'db1'} 71 | $ python check.py 72 | db1: {'name': 'db1'} 73 | db2: {'name': 'db2'} 74 | ``` 75 | 76 | 雖然 switch 成功, 但是最後 check 還是失敗, 代表改變沒有正確反映在 db 中。 77 | 78 | 問題出自 [segmentfault](https://segmentfault.com/q/1010000006116287/a-1020000006119818), by [littlealias](https://segmentfault.com/u/littlealias) 79 | 80 | ## 回答 81 | 82 | 你好, 我研究這個問題一陣之後結論是: 83 | 84 | 1. 太難做到而且你想要使用的語法跟你要做的事情並不 match 85 | 2. 我覺得用原本的方法沒有什麼不好 86 | 3. 如果你想要做到你定義的這種交換, 那我有一個不算是太漂亮的替代方案, 你可以參考 87 | 88 | 以下針對上述三點說明: 89 | 90 | 對於第一點, 你想要: 91 | 92 | ```python 93 | db1, db2 = db2, db1 94 | ``` 95 | 96 | 這邊不論 `db1`, `db2` 是哪種 object, 這種交換式的意義在於 97 | 98 | > 讓 `db1` 這個變數參考到原本 `db2` 所參考的 object, 並讓 `db2` 這個變數參考到原本 `db1` 所參考到的 object。 99 | 100 | 但是你想要做的事情是: 101 | 102 | > 讓 `db1` 這個 file 和 `db2` 這個 file 的內容互換 103 | 104 | 仔細想一想, 這兩件事情並不相同, 換個方式來說, `db1, db2 = db2, db1`, 只會讓變數參考的東西互換(變數名稱不等於 db 的 file 名稱), 但是每個文件的內容還是沒有互換。 105 | 106 | 所以 **使用這種語法來互換跟你要達到的效果並不一致**。 107 | 108 | 第二點就不多說明了, 因為合理, 只是你可能不喜歡。 109 | 110 | 第三點我給了一個不怎麼漂亮的替代方案, 就是簡單定義一個 `shelf` 的代理類 `ShelfProxy`, 這個類盡量模擬 `Shelf` 類的行為(僅是介面上相似), 並且重載了運算符 `^` 定義為交換: 111 | 112 | ```python 113 | import shelve 114 | 115 | class ShelfProxy: 116 | 117 | def __init__(self, *args, **kwargs): 118 | self.args = args 119 | self.kwargs = kwargs 120 | self.file = args[0] 121 | self.loaddb() 122 | 123 | @classmethod 124 | def open(cls, *args, **kwargs): 125 | return cls(*args, **kwargs) 126 | 127 | def __getattr__(self, name): 128 | return getattr(self.dic, name) 129 | 130 | def __setitem__(self, name, value): 131 | self.dic[name] = value 132 | 133 | def __getitem__(self, name): 134 | return self.dic[name] 135 | 136 | def __xor__(self, other): 137 | self.dic, other.dic = other.dic, self.dic 138 | return True 139 | 140 | def __str__(self): 141 | return str(self.dic) 142 | 143 | def loaddb(self): 144 | db = shelve.open(*self.args, **self.kwargs) 145 | self.dic = dict(db.items()) 146 | db.close() 147 | 148 | def close(self): 149 | newdb = shelve.open(self.file, *self.args[1:], **self.kwargs) 150 | for key in newdb.keys(): 151 | del newdb[key] 152 | for key, value in self.dic.items(): 153 | newdb[key] = value 154 | newdb.close() 155 | ``` 156 | 157 | 我將 `^` 定義為 **內容上的交換**, 之所以選 `^` 只是因為我想不到比較適合的符號, 一般來說重載不會這樣進行, 而且也不太會返回其他類的實例, 不過我這邊為求方便且針對你想要一個簡單介面這一點, 出此下策。 158 | 159 | 接著我們定義一些測試的 function: 160 | 161 | ```python 162 | def define(): 163 | db1 = ShelfProxy.open('db1', writeback=True) 164 | db2 = ShelfProxy.open('db2', writeback=True) 165 | db1['name'] = 'db1' 166 | db2['name'] = 'db2' 167 | db1.close() 168 | db2.close() 169 | 170 | def check(): 171 | db1 = ShelfProxy.open('db1', writeback=True) 172 | db2 = ShelfProxy.open('db2', writeback=True) 173 | print('db1:', db1) 174 | print('db2:', db2) 175 | db1.close() 176 | db2.close() 177 | 178 | def switch(): 179 | print('switch') 180 | db1 = ShelfProxy.open('db1', writeback=True) 181 | db2 = ShelfProxy.open('db2', writeback=True) 182 | db1 ^ db2 183 | db1.close() 184 | db2.close() 185 | ``` 186 | 187 | 測試代碼: 188 | 189 | ```python 190 | if __name__ == '__main__': 191 | define() 192 | check() 193 | switch() 194 | check() 195 | ``` 196 | 197 | 結果: 198 | 199 | ``` 200 | db1: {'name': 'db1'} 201 | db2: {'name': 'db2'} 202 | switch 203 | db1: {'name': 'db2'} 204 | db2: {'name': 'db1'} 205 | ``` 206 | 207 | ### 結論 208 | 209 | 大部分的時候, 你可以用跟 `Shelf` 相同的介面來操作 `ShelfProxy`, 整體效果也類似, 但是你不覺得寫了那麼多, 還是使用一開始的方法比較簡單嗎XD 210 | -------------------------------------------------------------------------------- /questions/standard_lib/如何在python raw_input中使用tab鍵補全.md: -------------------------------------------------------------------------------- 1 | # 如何在 python raw_input 中使用 tab 鍵補全 2 | 3 | ## 問題 4 | 5 | 如何在 python raw_input 中使用 tab 鍵補全? 6 | 7 | 問題出自 [segmentfault](https://segmentfault.com/q/1010000006090261/a-1020000006094141), by [caimaoy](https://segmentfault.com/u/caimaoy) 8 | 9 | ## 回答 10 | 11 | ### 拋磚 12 | 13 | 用 readline, 以下是一個簡單的小範例: 14 | 15 | ```python 16 | import readline 17 | 18 | CMD = ['foo1', 'foo2', 'bar1', 'bar2', 'exit'] 19 | 20 | def completer(text, state): 21 | options = [cmd for cmd in CMD if cmd.startswith(text)] 22 | if state < len(options): 23 | return options[state] 24 | else: 25 | return None 26 | 27 | readline.parse_and_bind("tab: complete") 28 | readline.set_completer(completer) 29 | 30 | while True: 31 | cmd = raw_input('==> ') 32 | if cmd=='exit': 33 | break 34 | print(cmd) 35 | ``` 36 | 37 | 測試: 38 | 39 | ``` 40 | ==> 41 | bar1 bar2 exit foo1 foo2 42 | ==> b 43 | ==> bar 44 | ==> bar 45 | bar1 bar2 46 | ==> bar1 47 | bar1 48 | ==> exit 49 | ``` 50 | 51 | ### 參考資料 52 | 53 | * [python - readline](https://docs.python.org/2.7/library/readline.html) 54 | * [GNU Readline Library](http://tiswww.case.edu/php/chet/readline/readline.html) 55 | 56 | 57 | ### 引玉 58 | 59 | 其實我沒有完全理解 completer 的作用原理, 尤其是 `state` 的部分, 希望有高手可以闡釋, 十分感謝! 60 | -------------------------------------------------------------------------------- /questions/standard_lib/計算時間差.md: -------------------------------------------------------------------------------- 1 | # 計算時間差 2 | 3 | ## 問題 4 | 5 | 需要處理的時間文本格式如下: 6 | 7 | ``` 8 | 2001-10-09 15:12:15 9 | 2001-10-31 18:41:59 10 | 2001-10-31 18:50:27 11 | 2001-11-11 18:28:26 12 | 2001- 11-11 18:28:54 13 | 2001-11-12 06:18:34 14 | 2001-11-17 03:36:20 15 | ``` 16 | 17 | 然後需要以一個的時間為計算標準,計算後面所有行的時間跟第一行時間的時間差並輸出,假設第一行的時間差設置為0 18 | 19 | 問題出自 [segmentfault](https://segmentfault.com/q/1010000005642041/a-1020000005642445), by [starryer](https://segmentfault.com/u/starryer) 20 | 21 | ## 回答 22 | 23 | 可以用標準庫中的 [datetime][1]: 24 | 25 | ```python 26 | from datetime import datetime, timedelta 27 | 28 | with open('data', 'r') as f: 29 | datetimes = [datetime.strptime(line.strip(), "%Y-%m-%d %H:%M:%S") for line in f] 30 | 31 | def get_dhms(td): 32 | """ 33 | given a timedelta then return corresponding days/hours/minutes/seconds 34 | """ 35 | seconds = td.total_seconds() 36 | # return seconds/86400, seconds/3600, seconds/60, seconds # 如果不捨去小數點,用此行 37 | return seconds//86400, seconds//3600, seconds//60, seconds 38 | 39 | for dt in datetimes[1:]: 40 | td = (dt - datetimes[0]) 41 | print dt, 'vs', datetimes[0], '->', get_dhms(td) 42 | ``` 43 | 44 | **測試資料** `data`: 45 | 46 | ``` 47 | 2001-10-09 15:41:54 48 | 2001-10-19 15:41:55 49 | 2001-10-29 15:42:54 50 | 2001-10-31 06:00:55 51 | 2002-01-01 23:59:59 52 | 2011-01-01 23:59:59 53 | ``` 54 | 55 | **結果**: 56 | 57 | ``` 58 | 2001-10-19 15:41:55 vs 2001-10-09 15:41:54 -> (10.0, 240.0, 14400.0, 864001.0) 59 | 2001-10-29 15:42:54 vs 2001-10-09 15:41:54 -> (20.0, 480.0, 28801.0, 1728060.0) 60 | 2001-10-31 06:00:55 vs 2001-10-09 15:41:54 -> (21.0, 518.0, 31099.0, 1865941.0) 61 | 2002-01-01 23:59:59 vs 2001-10-09 15:41:54 -> (84.0, 2024.0, 121458.0, 7287485.0) 62 | 2011-01-01 23:59:59 vs 2001-10-09 15:41:54 -> (3371.0, 80912.0, 4854738.0, 291284285.0) 63 | ``` 64 | 65 | ---------- 66 | 67 | 函數 `get_dhms` 會吃進一個 `timedelta`(時間差) 參數,並且回傳一個 4 元素 tuple 分別是這個 **時間差** 轉換出來的: 68 | 69 | ``` 70 | (日數,小時數,分鐘數, 秒數) 71 | ``` 72 | 73 | [1]: https://docs.python.org/2/library/datetime.html 74 | -------------------------------------------------------------------------------- /questions/star/關於python*和**的問題.md: -------------------------------------------------------------------------------- 1 | # 關於 python * 和 ** 的問題 2 | 3 | ## 問題 4 | 5 | `*` 和 `**` 為可變形參,但是平時在使用的時候感覺很少能有使用到的情況,有些不是很理解它們的用法和用處場景? 6 | 而且形參不是可以傳遞任意類型麼?這樣我寫成: 7 | 8 | ```python 9 | a = (1,2,3,4) 10 | def test(a): 11 | print a 12 | print a[1] 13 | ``` 14 | 15 | 加不加 `*` 貌似沒什麼區別?字典也是一樣只不過變成 `print['keyname']` 而已。 16 | 那麼 `*` 和 `**` 這玩意到底有什麼用呢? 17 | 18 | 問題出自 [segmentfault](https://segmentfault.com/q/1010000006075094/a-1020000006075616), by [msia121](https://segmentfault.com/u/msia121) 19 | 20 | ## 回答 21 | 22 | 這兩個東西可是相當有用的呢! 23 | 24 | 舉幾個例子示範一下 25 | 26 | ### 用於 function arguments 時 27 | 28 | 假設有個 function `intro`: 29 | 30 | ```python 31 | def intro(name, age, city, language): 32 | print('I am {}. I am {} years old. I live in {}. I love {}'.format( 33 | name, age, city, language 34 | )) 35 | ``` 36 | 37 | 今天我給你一組 list 的 data `lst` 和 dict 的 data `dic`: 38 | 39 | ```python 40 | lst = ['dokelung', 27, 'Taipei', 'Python'] 41 | dic = {'name': 'dokelung', 'age': 27, 'city': 'Taipei', 'language': 'Python'} 42 | ``` 43 | 44 | 不用 `*` 或 `**` 你可能要: 45 | 46 | ```python 47 | test(lst[0], lst[1], lst[2], lst[3]) 48 | test(dic['name'], dic['age'], dic['city'], dic['language']) 49 | ``` 50 | 51 | 使用 `*` 和 `**`: 52 | 53 | ```python 54 | test(*lst) 55 | test(**dic) 56 | ``` 57 | 58 | ### 用於 function params 時 59 | 60 | 今天我們要寫一個加法 function: 61 | 62 | ```python 63 | def add2(a, b): 64 | return a + b 65 | ``` 66 | 67 | 如果要擴充到三個數相乘: 68 | 69 | ```python 70 | def add3(a, b, c): 71 | return a + b + c 72 | ``` 73 | 74 | 這邊有兩個問題: 75 | * 一個是參數列可能很長 76 | * 一個是 Python 不允許多載, 所以不能使用同一個 function name 77 | 78 | 但是 `*` 可以解決這個問題: 79 | 80 | ```python 81 | def add(*n): 82 | return sum(n) 83 | ``` 84 | 85 | 當然你可能覺得這邊我也可以設計成參數是 list 或 tuple, 但有些時候這樣的做法比較方便, 你可以參考一下 `print` 的概念(Python3的): 86 | 87 | ```python 88 | print(*objects, sep=' ', end='\n', file=sys.stdout, flush=False) 89 | ^^^^^^^^ 90 | ``` 91 | 92 | 這邊的概念就是這樣 93 | 94 | 其次, 這個做法是寫 decorator 的基礎: 95 | 96 | ```python 97 | def mydeco(func): 98 | def newfunc(*args, **kwargs): 99 | return func(*args, **kwargs) 100 | return newfunc 101 | ``` 102 | 103 | 因為不使用這種做法, 你無法應對要修飾的 function 可能千奇百怪的參數 104 | 105 | ## 用於 tuple unpacking 時 (Python3) 106 | 107 | 這個功能很實用: 108 | 109 | ```python 110 | >>> t = ('start', 1 ,2 ,3 ,4 ,5, 'end') 111 | >>> s, *nums, e = t 112 | >>> s, nums, e 113 | ('start', [1, 2, 3, 4, 5], 'end') 114 | ``` 115 | 116 | ### 結論 117 | 118 | 其實妙處不只這些, 只等待你去發現! 119 | 120 | ### 補充 121 | 122 | 我覺得會有無用之感很大的一點在於我們常陷入一種思考情境: 123 | 124 | > 寫 function 的人 跟 用 function 的人是同一人, 也就是我 125 | 126 | 因為如果寫跟用為同一人, 那麼介面怎麼設計當然可以很自由, 選用 `*` 或是直接用 list 都沒差 127 | 128 | 但當今天我們是使用別人的 API 或是要寫 function 給別人用時, 就沒那麼大的彈性了 129 | 130 | 這個時候, `*` 可以幫助我們許多 131 | -------------------------------------------------------------------------------- /questions/string/Python中字串的bitwise or怎麼實現.md: -------------------------------------------------------------------------------- 1 | # Python 中字串的 bitwise or 怎麼實現? 2 | 3 | ## 問題 4 | 5 | ``` 6 | a="1000111000" 7 | b="1000000001" 8 | ``` 9 | 10 | `a` 和 `b` 為字串 11 | 12 | a bitwise or b 得到 `1000111001` 13 | 14 | 除了一位一位的處理,有沒有什麼方便的方法? 15 | 16 | 問題出自 [segmentfault](https://segmentfault.com/q/1010000005097274/a-1020000005099056), by [ffaniu](https://segmentfault.com/u/ffaniu) 17 | 18 | ## 回答 19 | 20 | **代碼**: 21 | 22 | ```python 23 | a = "1000111000" 24 | b = "1000000001" 25 | 26 | c = int(a, 2) | int(b, 2) 27 | 28 | print('{0:b}'.format(c)) 29 | ``` 30 | 31 | **結果**: 32 | 33 | ``` 34 | 1000111001 35 | ``` 36 | 37 | **分析**: 38 | 39 | 運算符 `|` 本身就可以執行 bitwise 的運算,所以我們只要知道如何將 *字串* 轉為 *2進位整數* 以及如何將運算完的 *整數* 結果以 *2進位字串* 表示即可. 40 | 41 | `int(a, 2)` 可以將整數或字串 `a` 轉為2進位整數(精準來說應該是讓 `a` 以 `2進位` 為基底進行整數轉換),接著利用 `|` 進行 bitwise or,最後 `'{0:b}'.format(c)` 方法可以讓我們將數值進行 2進位 的格式化處理. 42 | 43 | ---------- 44 | 45 | **其他想法**: 46 | 47 | 有趣的是,如果我們一位一位處理,利用 generator comprehension 加上其他的一些 functional programming style 的技巧也能用簡短的一行完成任務: 48 | 49 | ```python 50 | a = "1000111000" 51 | b = "1000000001" 52 | 53 | c = ''.join(str(int(ba) | int(bb)) for ba, bb in zip(a, b)) 54 | print(c) 55 | ``` 56 | -------------------------------------------------------------------------------- /questions/string/如何讓列表所有元素首字母變大寫.md: -------------------------------------------------------------------------------- 1 | # 如何讓列表所有元素首字母變大寫? 2 | 3 | ## 問題 4 | 5 | 希望列表c所有元素首字母變大寫,這樣寫為甚麼會出錯?該如何寫? 6 | 7 | ```python 8 | c = ['zz', 'yy', 'xx'] 9 | c[0:2] = c[0:2].capitalize() 10 | 11 | #提示錯誤 12 | AttributeError : 'list' object has no attribute 'capitalize' 13 | ``` 14 | 15 | 問題出自 [segmentfault](https://segmentfault.com/q/1010000005600224/a-1020000005600287), by [x_hola](https://segmentfault.com/u/x_hola) 16 | 17 | ## 回答 18 | 19 | `capitalize` 是字串的方法,而 `c[0:2]` 是一個 list,所以你調用 `captitalize` 的時候會出錯. 20 | 21 | ```python 22 | c = ['zz','yy','xx'] 23 | c = [string.capitalize() for string in c] 24 | ``` 25 | 26 | 還有這樣也可以: 27 | 28 | ```python 29 | c = ['xx', 'yy', 'zz'] 30 | c = ' '.join(c).title().split() 31 | ``` 32 | 33 | P.S. 在使用 list 的時候,如果要操作的是整個串列,那不需要特別使用到切片,`c[0:2]` 在這裡是個不必要的做法. 34 | 35 | 給你參考! 36 | 37 | ---------- 38 | 39 | @moling3650, 使用 `title` 真的是個有趣的主意,`capitalize` 只會將字串的首字大寫,而 `title` 則會將字串中所有的 **單字** 首字大寫。 40 | 41 | 見範例: 42 | 43 | ```python 44 | >>> string = 'my name is dokelung' 45 | >>> string.capitalize() 46 | 'My name is dokelung' 47 | >>> string.title() 48 | 'My Name Is Dokelung' 49 | ``` 50 | 51 | 所以這樣也行: 52 | 53 | ```python 54 | >>> c = ['xx', 'yy', 'zz'] 55 | >>> ' '.join(c).title().split() 56 | ['Xx', 'Yy', 'Zz'] 57 | ``` 58 | -------------------------------------------------------------------------------- /questions/string/給定一個字串,回傳所有的可能組合.md: -------------------------------------------------------------------------------- 1 | # 給定一個字串,回傳所有的可能組合 2 | 3 | ## 問題 4 | 5 | 例如: 6 | 7 | ```python 8 | >>> string = 'abc' 9 | >>> allcomb(string) 10 | ['a', 'b', 'c', 'ab', 'ac', 'ba', 'bc', 'ca', 'cb', 'abc', 'acb', 'bac', 'bca', 'cab', 'cba'] 11 | ``` 12 | 13 | 問題出自 [segmentfault](https://segmentfault.com/q/1010000005760811/a-1020000005762984), by [justhacker](https://segmentfault.com/u/justhacker) 14 | 15 | ## 回答 16 | 17 | 我覺得這個問題相當有趣,做為一個 Python 狂熱者,我不能同意 @garry_qian 的答案更多了,既然 Python 都提供了那麼好用的標準庫,不使用一下實在是太可惜了,在此立場下,一個 ~~簡潔,簡短~~ (好啦並沒有...),但邏輯上基本相同的做法如下: 18 | 19 | ```python 20 | >>> s = 'abc' 21 | >>> results = sorted([''.join(c) for l in range(len(s)) for c in permutations(s, l+1)]) 22 | 23 | ['a', 'b', 'c', 'ab', 'ac', 'ba', 'bc', 'ca', 'cb', 'abc', 'acb', 'bac', 'bca', 'cab', 'cba'] 24 | ``` 25 | 26 | 這個寫法沒什麼特別的,不過是使用兩個 `for` 的 **list comprehension** 罷了。 27 | 同時,它回傳的是一個 string 的 list,與原題目比較接近。 28 | 29 | 但是這激發了我的一些好奇心,想自己來寫寫看,同時以沒有這些排列組合工具的他種語言來說,也許比較容易利用相同的邏輯來完成。 30 | 31 | 首先我完成的是關於組合的 function,他代入一個字串並且回傳所有長度的所有字元組合,但不排列: 32 | 33 | ```python 34 | def get_combinations(string): 35 | combs = [] 36 | for i in range(1, 2**len(string)): 37 | pat = "{0:b}".format(i).zfill(len(string)) 38 | combs.append(''.join(c for c, b in zip(string, pat) if int(b))) 39 | return combs 40 | ``` 41 | 42 | ```python 43 | >>> print get_combinations('abc') 44 | ['c', 'b', 'bc', 'a', 'ac', 'ab', 'abc'] 45 | ``` 46 | 47 | 一如預期,我們拿到: 48 | 49 | 1. 長度為 1 的 `'c', 'b', 'a'` 50 | 2. 長度為 2 的 `'bc', 'ac', 'ab'` 51 | 3. 長度為 3 的 `'abc'` 52 | 53 | 果然是各種長度下的所有組合都到手了。 54 | 55 | 這個寫法肯定不是最好的,但我覺得想法滿有趣的。想法就是,要考慮 `'abc'` 的所有組合,那不就是分別考慮 `a` 要不要取,`b` 要不要取 和 `c` 要不要取,於是總共 `2*2*2 = 8` (`2**len(string)`) 種組合,那不就正好對應到: 56 | 57 | ``` 58 | 000 -> 都不取 59 | 001 -> 只取 c 60 | 010 -> 只取 b 61 | 011 -> 取 b c 62 | 100 -> 只取 a 63 | 101 -> 取 a c 64 | 110 -> 取 a b 65 | 111 -> 都取 66 | ``` 67 | 68 | 所以在 `get_combinations` 中,用了一點技巧去生出從 1 到 7 的二進位碼,再根據 0 與 1 決定每一種組合該取用那些字元。 69 | 70 | 這還沒完成任務,我們距離標準答案,還必須取得: 71 | 72 | ``` 73 | 每一種組合的所有排列情形 74 | ``` 75 | 76 | 這產生了 `get_permutations` 這個 function: 77 | 78 | ```python 79 | def get_permutations(clst): 80 | if len(clst)==1: 81 | return [clst[0]] 82 | results = [] 83 | for idx, c in enumerate(clst): 84 | results += [c+substr for substr in get_permutations(clst[:idx] + clst[idx+1:])] 85 | return results 86 | ``` 87 | 88 | ```python 89 | >>> print get_permutations('abc') 90 | ['abc', 'acb', 'bac', 'bca', 'cab', 'cba'] 91 | ``` 92 | 93 | 邏輯很簡單,用 recursive 的方式去找出 `固定長度字元組合` 所有的排列。 94 | 95 | 有了以上兩種 function,我們就可以求出答案囉: 96 | 97 | ```python 98 | >>> [perm for comb in get_combinations('abc') for perm in get_permutations(list(comb))] 99 | ['c', 'b', 'bc', 'cb', 'a', 'ac', 'ca', 'ab', 'ba', 'abc', 'acb', 'bac', 'bca', 'cab', 'cba'] 100 | ``` 101 | 102 | ### 結論 103 | 104 | 1. 別重複發明輪胎,這不但累死你,還很顯笨 105 | 2. 人生苦短,我用 Python 106 | -------------------------------------------------------------------------------- /questions/string/轉換一個字串為浮點數會報錯.md: -------------------------------------------------------------------------------- 1 | # 轉換一個字串為浮點數會報錯 2 | 3 | ## 問題 4 | 5 | ```python 6 | float('‐525050.87') 7 | 8 | ValueError: could not convert string to float: ‐525050.87 9 | ``` 10 | 11 | 問題出自 [segmentfault](https://segmentfault.com/q/1010000006024368/a-1020000006024526), by [廖i雪](https://segmentfault.com/u/liaoixue) 12 | 13 | ## 回答 14 | 15 | 在我回答你之前, 讓我先問個更奇怪的問題先: 16 | 17 | ```python 18 | In [60]: '‐' == '-' 19 | Out[60]: False 20 | ``` 21 | 22 | WHY ????? 23 | 24 | 讓我來告訴你為什麼, 這就是真相: 25 | 26 | [en dash](http://grammarist.com/grammar/en-dash/) 27 | 28 | 重點在這裡: 29 | 30 | > An en dash or en rule (–) is wider than a hyphen (-) and narrower than an em dash (–). 31 | 32 | 舉一要能反三, 我再問個問題: 33 | 34 | ```python 35 | >>> float('-553l.4') 36 | ValueError: could not convert string to float: '-553l.4' 37 | ``` 38 | 39 | 這題比較簡單, 相信你也知道原因了... 40 | -------------------------------------------------------------------------------- /questions/styles/ebook.css: -------------------------------------------------------------------------------- 1 | /* CSS for ebook */ 2 | -------------------------------------------------------------------------------- /questions/styles/website.css: -------------------------------------------------------------------------------- 1 | /* CSS for website */ 2 | -------------------------------------------------------------------------------- /questions/virtualenv/如何在ubuntu14.04安裝python3.5.md: -------------------------------------------------------------------------------- 1 | # 如何在 ubuntu14.04 安裝 python3.5 2 | 3 | ## 問題 4 | 5 | ubuntu14 默認安裝了 python2.7.6 和 python3.4.3,因為某個項目要用 python3.5,想問一下如何在 ubuntu 上安裝 python3.5 而不產生衝突? 6 | 7 | 補充: 8 | 9 | 結果發現是阿里雲的ubuntu缺了zlib和openssl幾個依賴包,導致編譯python的時候pip沒裝上...orz 10 | 11 | 問題出自 [segmentfault](https://segmentfault.com/q/1010000005991779/a-1020000005991900), by [moling3650](https://segmentfault.com/u/moling3650) 12 | 13 | ## 回答 14 | 15 | 使用 [`virtualenv`](https://virtualenv.pypa.io/en/stable/)的概略指南: 16 | 17 | 1. 下載 Python 18 | 2. 編譯 Python ([實用的 compile 指南](http://www.kelvinwong.ca/2010/08/02/python-2-7-on-dreamhost/)) 19 | 3. 使用 `virtualenv` 來建置新環境 20 | * (選擇 1) 利用新版本 Python 中的 `pip` 來安裝 `virtualenv` (Python 新版本安裝後我記得直接都有相對應版本的 `pip`) 21 | * (選擇 2) 利用新版本 Python 先安裝對應的 `pip` (`easy_install` or `get-pip.py`), 然後同 (選擇 1) 22 | * (選擇 3) 利用 option: `virtualenv -p [ENVNAME]` 來指定使用哪個 python 建置環境 23 | 24 | 你也可以試試看: 25 | 26 | 1. [pyenv](https://github.com/yyuu/pyenv) 27 | 2. [pythonbrew](https://github.com/utahta/pythonbrew) <- 有整合 `virtualenv` 28 | -------------------------------------------------------------------------------- /templates/basic.md: -------------------------------------------------------------------------------- 1 | # 2 | 3 | ## 問題 4 | 5 | 問題出自 [segmentfault](), by []() 6 | 7 | ## 回答 8 | -------------------------------------------------------------------------------- /test/data1: -------------------------------------------------------------------------------- 1 | dictionary = {'1':[['1', '5'], ['2', '3'], ['3', '4'], ['4', '3'], ['5', '3'], ['7', '4'], ['8', '1'], ['9', '5'], ['11', '2'], ['13', '5'], ['15', '5'], ['16', '5'], ['18', '4'], ['19', '5'], ['21', '1'], ['22', '4'], ['25', '4'], ['26', '3'], ['28', '4'], ['29', '1'], ['30', '3'], ['32', '5'], ['34', '2'], ['35', '1'], ['37', '2'], ['38', '3'], ['40', '3'], ['41', '2'], ['42', '5'], ['43', '4'], ['45', '5'], ['46', '4'], ['48', '5'], ['50', '5'], ['52', '4'], ['55', '5'], ['57', '5'], ['58', '4'], ['59', '5'], ['63', '2'], ['66', '4'], ['68', '4'], ['71', '3'], ['75', '4'], ['77', '4'], ['79', '4'], ['83', '3'], ['87', '5'], ['88', '4'], ['89', '5'], ['93', '5'], ['94', '2'], ['95', '4'], ['99', '3'], ['101', '2'], ['105', '2'], ['106', '4'], ['109', '5'], ['110', '1'], ['111', '5'], ['115', '5'], ['116', '3'], ['119', '5'], ['122', '3'], ['123', '4'], ['124', '5'], ['126', '2'], ['127', '5'], ['131', '1'], ['133', '4'], ['135', '4'], ['136', '3'], ['137', '5'], ['138', '1'], ['139', '3'], ['141', '3'], ['142', '2'], ['144', '4'], ['146', '4'], ['147', '3'], ['149', '2'], ['152', '5'], ['153', '3'], ['156', '4'], ['158', '3'], ['162', '4'], ['165', '5'], ['166', '5'], ['167', '2'], ['168', '5'], ['169', '5'], ['172', '5'], ['173', '5'], ['176', '5'], ['178', '5'], ['179', '3'], ['181', '5'], ['182', '4'], ['187', '4'], ['191', '5'], ['192', '4'], ['194', '4'], ['195', '5'], ['197', '5'], ['198', '5'], ['199', '4'], ['203', '4'], ['204', '5'], ['205', '3'], ['207', '5'], ['211', '3'], ['216', '5'], ['217', '3'], ['220', '3'], ['223', '5'], ['231', '1'], ['234', '4'], ['237', '2'], ['238', '4'], ['239', '4'], ['240', '3'], ['244', '2'], ['245', '2'], ['246', '5'], ['247', '1'], ['249', '4'], ['251', '4'], ['256', '4'], ['257', '4'], ['261', '1'], ['263', '1'], ['268', '5'], ['269', '5'], ['270', '5'], ['271', '2']],'2':[['1', '4'], ['10', '2'], ['14', '4'], ['25', '4'], ['100', '5'], ['111', '4'], ['127', '5'], ['237', '4'], ['242', '5'], ['255', '4'], ['258', '3'], ['269', '4'], ['272', '5'], ['273', '4'], ['274', '3'], ['275', '5'], ['276', '4'], ['277', '4'], ['278', '3'], ['282', '4'], ['283', '5'], ['284', '4'], ['285', '5'], ['286', '4'], ['287', '3'], ['288', '3'], ['289', '3'], ['291', '3'], ['293', '4'], ['294', '1'], ['295', '4'], ['296', '3'], ['300', '4'], ['302', '5'], ['304', '4'], ['305', '3'], ['306', '4'], ['309', '1'], ['310', '4'], ['311', '5']]} 2 | --------------------------------------------------------------------------------