diff --git a/README.md b/README.md index de346d2..3e095dd 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ 這個 repository 收集了一些我自己感覺比較有趣或實用的問題,每篇都會附上原發問者的問題以及我的回答,問題可能經過潤飾,但我盡量維持原貌,回答的部分也多是個人見解,只是希望做個整理並且與有興趣的人分享. -由於問題滿雜的,我只能憑我的直覺將之分類歸檔,目前一共收錄了 72 個問題,大家有興趣的可以參閱[目錄](contents.md). +由於問題滿雜的,我只能憑我的直覺將之分類歸檔,目前一共收錄了 81 個問題,大家有興趣的可以參閱[目錄](contents.md). 如果你有任何的意見或想要討論,都歡迎你開個 issue 來討論唷! diff --git a/contents.md b/contents.md index d6b2a29..998df8a 100644 --- a/contents.md +++ b/contents.md @@ -3,146 +3,162 @@ ## 內建型態與 collections 容器 * string - * [Python 中字串的 bitwise or 怎麼實現?](questions/string/Python中字串的bitwise or怎麼實現.md) - * [給定一個字串,回傳所有的可能組合](questions/string/給定一個字串,回傳所有的可能組合.md) - * [如何讓列表所有元素首字母變大寫](questions/string/如何讓列表所有元素首字母變大寫.md) - * [轉換一個字串為浮點數會報錯](questions/string/轉換一個字串為浮點數會報錯.md) + * [Python 中字串的 bitwise or 怎麼實現?](questions/string/Python中字串的bitwise%20or怎麼實現.md) + * [給定一個字串,回傳所有的可能組合](questions/string/給定一個字串,回傳所有的可能組合.md) + * [如何讓列表所有元素首字母變大寫](questions/string/如何讓列表所有元素首字母變大寫.md) + * [轉換一個字串為浮點數會報錯](questions/string/轉換一個字串為浮點數會報錯.md) * list - * [遍歷二維串列最外圈](questions/list/遍歷二維串列最外圈.md) - * [list 使用 append 最後為什麼會把前面的元素都修改掉](questions/list/list使用append最後為什麼會把前面的元素都修改掉.md) - * [python 列表取交集](questions/list/python列表取交集.md) + * [遍歷二維串列最外圈](questions/list/遍歷二維串列最外圈.md) + * [list 使用 append 最後為什麼會把前面的元素都修改掉](questions/list/list使用append最後為什麼會把前面的元素都修改掉.md) + * [python 列表取交集](questions/list/python列表取交集.md) +  * [python 生成特定列表格式](questions/list/python生成特定列表格式.md) * collections - * [會 php 和 Python 的大神進來幫忙轉換一段代碼](questions/collections/會php和python的大神進來幫忙轉換一段代碼.md) - * [Top K Frequent Elements](questions/collections/Top_K_Frequent_Elements.md) + * [會 php 和 Python 的大神進來幫忙轉換一段代碼](questions/collections/會php和python的大神進來幫忙轉換一段代碼.md) + * [Top K Frequent Elements](questions/collections/Top_K_Frequent_Elements.md) ## 函式 +* star expression + * [關於 python * 和 ** 的問題](questions/star/關於python*和**的問題.md) + +* variable scope(global/local/nonlocal/closure) + * [為什麼這兩段 python lambda 出現不同的結果](questions/scope/為什麼這兩段%20python%20lambda%20出現不同的結果.md) + +## 內建函式 + * len - * [怎麼判斷函數或方法多次使用是否需要定義臨時變量](questions/len/怎麼判斷函數或方法多次使用是否需要定義臨時變量.md) + * [怎麼判斷函數或方法多次使用是否需要定義臨時變量](questions/len/怎麼判斷函數或方法多次使用是否需要定義臨時變量.md) * sort - * [Python排序問題](questions/sort/Python排序問題.md) - * [sorted函數中key參數的作用原理](questions/sort/sorted函數中key參數的作用原理.md) - -* star expression - * [關於 python * 和 ** 的問題](questions/star/關於python*和**的問題.md) + * [Python排序問題](questions/sort/Python排序問題.md) + * [sorted函數中key參數的作用原理](questions/sort/sorted函數中key參數的作用原理.md) + +* eval + * [對 Python 語法字串求值](questions/eval/對Python語法字串求值.md) ## 控制流程與迭代 * if(if/elif/else) - * [if 語句的 and or 運算](questions/if/if 語句的 and or 運算.md) + * [if 語句的 and or 運算](questions/if/if%20語句的%20and%20or%20運算.md) * iteration(iterable/iterater/comprehension/generator/generator expression) - * [如何將列表中的元組整個迭代出](questions/iteration/如何將列表中的元組整個迭代出來.md) - * [一個求質(素)數的編程題](questions/iteration/一個求質(素)數的編程題.md) - * [Python 如何合併 list of lists](questions/iteration/Python 如何合併 list of lists.md)(待補充) - * [如何從一個複雜的結構中優雅的提取出一列數據](questions/iteration/如何從一個複雜的結構中優雅的提取出一列數據.md) - * [如何把 tuple 轉成 dictionary](questions/iteration/如何把tuple轉成dictionary.md) - * [Python 的 list 有沒有類似 js 的 find 方法](questions/iteration/Python的list有沒有類似js的find方法.md) - * [python 中既然生成器表達式比列表解析快, 那為什麼不全部使用生成器表達式](questions/iteration/python中既然生成器表達式比列表解析快, 那為什麼不全部使用生成器表達式.md) + * [如何將列表中的元組整個迭代出](questions/iteration/如何將列表中的元組整個迭代出來.md) + * [一個求質(素)數的編程題](questions/iteration/一個求質(素)數的編程題.md) + * [Python 如何合併 list of lists](questions/iteration/Python%如何合併%20list%20of%20lists.md)(待補充) + * [如何從一個複雜的結構中優雅的提取出一列數據](questions/iteration/如何從一個複雜的結構中優雅的提取出一列數據.md) + * [如何把 tuple 轉成 dictionary](questions/iteration/如何把tuple轉成dictionary.md) + * [Python 的 list 有沒有類似 js 的 find 方法](questions/iteration/Python的list有沒有類似js的find方法.md) + * [python 中既然生成器表達式比列表解析快, 那為什麼不全部使用生成器表達式](questions/iteration/python中既然生成器表達式比列表解析快,%20那為什麼不全部使用生成器表達式.md) * functional programming style(map/filter/reduce) - * [sum 函數中可以使用條件語句嗎?](questions/fp/sum函數中可以使用條件語句嗎.md) + * [sum 函數中可以使用條件語句嗎?](questions/fp/sum函數中可以使用條件語句嗎.md) * error handling(exception) - * [Python 如何優雅的處理大量異常語句](questions/error/Python如何優雅的處理大量異常語句.md) - * [python 如何捕獲中斷](questions/error/python如何捕獲中斷.md) + * [Python 如何優雅的處理大量異常語句](questions/error/Python如何優雅的處理大量異常語句.md) + * [python 如何捕獲中斷](questions/error/python如何捕獲中斷.md) ## 物件導向程式設計 * dunder(magic function) - * [自己寫的數據類型使用print無法輸出每個元素](questions/dunder/自己寫的數據類型使用print無法輸出每個元素.md) + * [自己寫的數據類型使用print無法輸出每個元素](questions/dunder/自己寫的數據類型使用print無法輸出每個元素.md) * object(class/object) - * [Python 3.x 實例方法的`__func__`屬性](questions/object/Python 3.x 實例方法的__func__屬性.md) - * [Python 如何通過類方法創建實例方法](questions/object/Python如何通過類方法創建實例方法.md) - * [Python 的 staticmethod 在什麼情況下用](questions/object/Python的staticmethod在什麼情況下用.md) + * [Python 3.x 實例方法的`__func__`屬性](questions/object/Python%203.x%20實例方法的__func__屬性.md) + * [Python 如何通過類方法創建實例方法](questions/object/Python如何通過類方法創建實例方法.md) + * [Python 的 staticmethod 在什麼情況下用](questions/object/Python的staticmethod在什麼情況下用.md) + * [Python 多重繼承屬性問題](questions/object/Python多重繼承屬性問題.md) ## 檔案與資料處理 * file - * [怎樣合併文檔中有重複部分的行](questions/file/怎樣合併文檔中有重複部分的行.md) - * [Python 處理文本信息](questions/file/Python處理文本信息.md) - * [Python 如何實現並行查找關鍵字所在的行](questions/file/Python如何實現並行查找關鍵字所在的行.md) - * [文本格式轉換代碼優化](questions/file/文本格式轉換代碼優化.md) - * [使用 Python 如何按行數拆分文件](questions/file/使用Python如何按行數拆分文件.md) - * [Python 處理一個求和運算](questions/file/Python處理一個求和運算.md) - * [Python 如何向文件最開始插入一個字串](questions/file/Python如何向文件最開始插入一個字串.md)(待補充) - * [用 Python 實現類似 grep 的功能](questions/file/用Python實現類似grep的功能.md) + * [怎樣合併文檔中有重複部分的行](questions/file/怎樣合併文檔中有重複部分的行.md) + * [Python 處理文本信息](questions/file/Python處理文本信息.md) + * [Python 如何實現並行查找關鍵字所在的行](questions/file/Python如何實現並行查找關鍵字所在的行.md) + * [文本格式轉換代碼優化](questions/file/文本格式轉換代碼優化.md) + * [使用 Python 如何按行數拆分文件](questions/file/使用Python如何按行數拆分文件.md) + * [Python 處理一個求和運算](questions/file/Python處理一個求和運算.md) + * [Python 如何向文件最開始插入一個字串](questions/file/Python如何向文件最開始插入一個字串.md)(待補充) + * [用 Python 實現類似 grep 的功能](questions/file/用Python實現類似grep的功能.md) * json - * [為什麼 json 的 key 只能是 string?](questions/json/為什麼json的key只能是string.md) - * [Python 如何合併批量輸出 json](questions/json/Python如何合併批量輸出json.md) - * [Python 如何讀取 json 中的數據](questions/json/Python如何讀取json中的數據.md) - * [不定深層 Json 剖析](questions/json/不定深層Json剖析.md) + * [為什麼 json 的 key 只能是 string?](questions/json/為什麼json的key只能是string.md) + * [Python 如何合併批量輸出 json](questions/json/Python如何合併批量輸出json.md) + * [Python 如何讀取 json 中的數據](questions/json/Python如何讀取json中的數據.md) + * [不定深層 Json 剖析](questions/json/不定深層Json剖析.md) + * [假定有 json 數據多條記錄,如何根據 KEY 的值返回一條記錄](questions/json/假定有json數據多條記錄,如何根據KEY的值返回一條記錄.md) * cvs - * [csv 模塊生成 CSV 文件問題(0字頭數字缺失與漢字亂碼)](questions/csv/csv模塊生成CSV文件問題(0字頭數字缺失與漢字亂碼).md) - * [操作 csv 文件寫入順序不對](questions/csv/操作csv文件寫入順序不對.md) - * [如何用 python 刪除 csv 文件中的某一列](questions/csv/如何用python刪除csv文件中的某一列.md) + * [csv 模塊生成 CSV 文件問題(0字頭數字缺失與漢字亂碼)](questions/csv/csv模塊生成CSV文件問題(0字頭數字缺失與漢字亂碼).md) + * [操作 csv 文件寫入順序不對](questions/csv/操作csv文件寫入順序不對.md) + * [如何用 python 刪除 csv 文件中的某一列](questions/csv/如何用python刪除csv文件中的某一列.md) ## 模組與套件 * import - * [通過哪個函數能查看 Python 文件中匯入了哪些模組](questions/import/通過哪個函數能查看Python文件中匯入了哪些模組.md) - * [deepcopy 無法 import](questions/import/deepcopy無法import.md) + * [通過哪個函數能查看 Python 文件中匯入了哪些模組](questions/import/通過哪個函數能查看Python文件中匯入了哪些模組.md) + * [deepcopy 無法 import](questions/import/deepcopy無法import.md) * standard library - * [os.mkdir 和 os.makedirs 的區別](questions/standard_lib/os.mkdir和os.makedirs的區別.md) - * [tk 程序中出現問題](questions/standard_lib/tk程序中出現問題.md) - * [計算時間差](questions/standard_lib/計算時間差.md) - * [Python timeit 測量代碼運行時間問題](questions/standard_lib/Python timeit測量代碼運行時間問題.md) - * [Python 日期的遞增問題](questions/standard_lib/Python日期的遞增問題.md) - * [一個需要傳入參數的 python 程序如何封裝成可執行文件](questions/standard_lib/一個需要傳入參數的python程序如何封裝成可執行文件.md) - * [一個結構化顯示文件的腳本,迭代是否有問題](questions/standard_lib/一個結構化顯示文件的腳本,迭代是否有問題.md) - * [如何在 python raw_input 中使用 tab 鍵補全](questions/standard_lib/如何在python raw_input中使用tab鍵補全.md) + * [os.mkdir 和 os.makedirs 的區別](questions/standard_lib/os.mkdir和os.makedirs的區別.md) + * [tk 程序中出現問題](questions/standard_lib/tk程序中出現問題.md) + * [計算時間差](questions/standard_lib/計算時間差.md) + * [Python timeit 測量代碼運行時間問題](questions/standard_lib/Python%20timeit測量代碼運行時間問題.md) + * [Python 日期的遞增問題](questions/standard_lib/Python日期的遞增問題.md) + * [一個需要傳入參數的 python 程序如何封裝成可執行文件](questions/standard_lib/一個需要傳入參數的python程序如何封裝成可執行文件.md) + * [一個結構化顯示文件的腳本,迭代是否有問題](questions/standard_lib/一個結構化顯示文件的腳本,迭代是否有問題.md) + * [如何在 python raw_input 中使用 tab 鍵補全](questions/standard_lib/如何在python%20raw_input中使用tab鍵補全.md) + * [交換兩個 shelve objects](questions/standard_lib/交換兩個shelve%20objects.md) + * [Python 獲取文件路徑及文件目錄(`__file__` 的使用方法)](questions/standard_lib/Python%20獲取文件路徑及文件目錄(__file__%20的使用方法).md) * pip - * [pip 無法在安裝 pyinstaller](questions/pip/pip無法在安裝pyinstaller.md) - * [Python 代碼怎麼打包](questions/pip/python代碼怎麼打包.md) + * [pip 無法在安裝 pyinstaller](questions/pip/pip無法在安裝pyinstaller.md) + * [Python 代碼怎麼打包](questions/pip/python代碼怎麼打包.md) * others - * [中文按照拼音排序](questions/others/中文按照拼音排序.md) + * [中文按照拼音排序](questions/others/中文按照拼音排序.md) ## WEB * django - * [django 如何一個 url 綁定多個視圖](questions/django/django如何一個url綁定多個視圖.md) - * [django 模版中變量引用變量被當作字符串處理而不是變量值](questions/django/django模版中變量引用變量被當作字符串處理而不是變量值.md) - * [網頁根目錄改成子目錄後文件如何調用](questions/django/網頁根目錄改成子目錄後文件如何調用.md) + * [django 如何一個 url 綁定多個視圖](questions/django/django如何一個url綁定多個視圖.md) + * [django 模版中變量引用變量被當作字符串處理而不是變量值](questions/django/django模版中變量引用變量被當作字符串處理而不是變量值.md) + * [網頁根目錄改成子目錄後文件如何調用](questions/django/網頁根目錄改成子目錄後文件如何調用.md) + * [Django CSRF verification failed 問題](questions/django/Django%20CSRF%20verification%20failed%20問題.md) * flask/jinja - * [jinja2 macro caller](questions/jinja/jinja2_macro_caller.md) + * [jinja2 macro caller](questions/jinja/jinja2_macro_caller.md) ## 爬蟲 * beautiful soup - * [刪除 xml 文件的指定標籤](questions/bs/刪除xml文件的指定標籤.md) - * [Python 爬蟲 beautifulsoup string 抓取問題](questions/bs/python爬蟲beautifulsoup string抓取問題.md) + * [刪除 xml 文件的指定標籤](questions/bs/刪除xml文件的指定標籤.md) + * [Python 爬蟲 beautifulsoup string 抓取問題](questions/bs/python爬蟲beautifulsoup%20string抓取問題.md) ## 演算法與科學計算 * algorithm - * [Subset-Sum Problem](questions/algorithm/subset_sum_problem.md) - * [字串 list 排序(七橋問題)](questions/algorithm/字串list排序(七橋問題).md) - * [使用字串為整數編碼](questions/algorithm/使用字串為整數編碼.md) - * [分群問題](questions/algorithm/分群問題.md) - * [找出所有組合-笛卡爾積問題](questions/algorithm/找出所有組合-笛卡爾積問題.md) - * [燈泡開關問題](questions/algorithm/燈泡開關問題.md) + * [Subset-Sum Problem](questions/algorithm/subset_sum_problem.md) + * [字串 list 排序(七橋問題)](questions/algorithm/字串list排序(七橋問題).md) + * [使用字串為整數編碼](questions/algorithm/使用字串為整數編碼.md) + * [分群問題](questions/algorithm/分群問題.md) + * [找出所有組合-笛卡爾積問題](questions/algorithm/找出所有組合-笛卡爾積問題.md) + * [燈泡開關問題](questions/algorithm/燈泡開關問題.md) + * [一個數最後一位是 6,移動到首位是原來數的三倍,求這個數](questions/algorithm/一個數最後一位是6,移動到首位是原來數的三倍,求這個數.md) * math - * [Python怎麼通過input獲取矩陣](questions/math/Python怎麼通過input獲取矩陣.md) + * [Python 怎麼通過 input 獲取矩陣](questions/math/Python怎麼通過input獲取矩陣.md) ## 大數據與機器學習 + * data mining - * [python3中有聚類(主要是k-means)的函數或者模塊嗎](questions/data_mining/python3中有聚類(主要是k-means)的函數或者模塊嗎.md) + * [python3 中有聚類(主要是k-means)的函數或者模塊嗎](questions/data_mining/python3中有聚類(主要是k-means)%20的函數或者模塊嗎.md) ## Python 實作與開發環境 * IDE (集成開發環境) - * [Python 能否在保存程序變量情況下啟動控制台](questions/ide/Python能否在保存程序變量情況下啟動控制台.md) + * [Python 能否在保存程序變量情況下啟動控制台](questions/ide/Python能否在保存程序變量情況下啟動控制台.md) * virtualenv - * [如何在 ubuntu14.04 安裝 python3.5](questions/virtualenv/如何在ubuntu14.04安裝python3.5.md) + * [如何在 ubuntu14.04 安裝 python3.5](questions/virtualenv/如何在ubuntu14.04安裝python3.5.md) diff --git a/draft/finput.py b/draft/finput.py new file mode 100644 index 0000000..19af047 --- /dev/null +++ b/draft/finput.py @@ -0,0 +1,76 @@ +# author: dokelung + +import re +from ast import literal_eval +from functools import partial + + +class InputDoesNotMatchFStr(Exception): pass +class TypeConvertError(Exception): pass +class InputCountNotInRange(Exception): pass + + +FORMAT_SPECIFIER = { + '%a': literal_eval, + '%d': int, + '%f': float, + '%o': partial(int, base=8), + '%s': str, + '%x': partial(int, base=16), +} + + +def finput(prompt='', fstr='%s', expand_fsp=None, + whitespace=False, + escape_parenthesis=True): + """format input + """ + fsp = FORMAT_SPECIFIER + if expand_fsp is not None: + fsp.update(expand_fsp) + if escape_parenthesis: + rstr = fstr.replace('(', '\(').replace(')', '\)') + else: + rstr = fstr + regex = '(.+)' if whitespace else '(\S+)' + for sp, typ in fsp.items(): + rstr = rstr.replace(sp, regex) + types = [] + for idx, c in enumerate(fstr): + pattern = fstr[idx:idx+2] + if pattern in fsp: + types.append(fsp[pattern]) + pure_input = input(prompt) + mobj = re.match(rstr, pure_input) + if mobj: + try: + return tuple(typ(value) for value, typ in zip(mobj.groups(), types)) + except Exception as err: + raise TypeConvertError(err) + else: + msg = 'input does not match format string "{}"' + raise InputDoesNotMatchFStr(msg.format(fstr)) + + +def minput(prompt='', typ=str, sep=None, min=1, max=100000): + """multiple input + """ + pure_input = input(prompt) + try: + if sep is None: + values = tuple(typ(item) for item in pure_input.split()) + else: + values = tuple(typ(item) for item in pure_input.split(sep)) + except Exception as err: + raise TypeConvertError(err) + if len(values) < min or len(values) > max: + msg = 'input count {} is not in range [{}, {}]' + raise InputCountNotInRange(msg.format(len(values), min, max)) + return values + + +if __name__ == '__main__': + #res = finput('>>> ', fstr='%s, *%d, *%f') + #print(res) + res = minput('>>> ', typ=int, min=1, max=3) + print(res) diff --git "a/draft/\351\227\234\346\226\274 class variable.md" "b/draft/\351\227\234\346\226\274 class variable.md" new file mode 100644 index 0000000..f00ccb3 --- /dev/null +++ "b/draft/\351\227\234\346\226\274 class variable.md" @@ -0,0 +1,94 @@ +首先你寫在 class 裡面但不在 method 裡面的 variable 是 class variable + +這個 variable 對於該類別及其子類別的類別和實體而言都只有一份, 看下面這個例子: + +```python +class A: + _dict = {} + def __init__(self): + self._dict.update({'a':'a'}) + +class B(A): + + def __init__(self): + self._dict.update({'b':'b'}) + +if __name__=='__main__': + a = A() + b = B() + print(a._dict) + print(b._dict) + print(A._dict) + print(B._dict) +``` + +``` +{'b': 'b', 'a': 'a'} +{'b': 'b', 'a': 'a'} +{'b': 'b', 'a': 'a'} +{'b': 'b', 'a': 'a'} +``` + +這裡你看到的所有 `_dict` 都參考到同一個物件 + +但是下面這個情況就不太一樣了: + +```python +class A: + _dict = {} + def __init__(self): + self._dict = {} + self._dict.update({'a':'a'}) + +class B(A): + + def __init__(self): + self._dict.update({'b':'b'}) + +class C(A): + + def __init__(self): + super().__init__() + self._dict.update({'c':'c'}) + +if __name__=='__main__': + a = A() + b = B() + c = C() + print(a._dict) + print(b._dict) + print(c._dict) + print(A._dict) + print(B._dict) + print(C._dict) +``` + +``` +{'a': 'a'} +{'b': 'b'} +{'c': 'c', 'a': 'a'} +{'b': 'b'} +{'b': 'b'} +{'b': 'b'} +``` + +咦?! 怎麼變成這樣了呢? 這邊如果搞懂的話就全盤皆通了: + +首先 A 及其子類別都共有一個 class variable, 叫做 `_dict` + +當我們初始化 a 的時候, `self._dict = {}` 會讓 a 裡面新產生一個變數叫做 `_dict`, 因為這次 `self._dict` 出現在等號左邊。注意! 這裡我們已經有兩個不同的東西了, 一個是 class variable `_dict`, 另一個是 instance variable `_dict`, 從此以後, a 裡面拿 `self._dict` 的時候就都是拿到 instance variable 了! + +接著看 b, b 並沒有讓 variable 出現在等號左邊, 所以沒有建立新的變數, 現在 b 中的 `self._dict` 仍然指涉 class variable `_dict` + +c 的情況就比較特別了, 藉由 `super` 他呼叫了 A 的 `__init__`, 上面說過了, 這會讓 c 中新建立一個 instance variable `_dict`, 這個變數因為 `A.__init__` 和 `C.__init__`, 所以會有兩個鍵值對 + + +最後 `A._dict`, `B._dict` 和 `C._dict` 就很容易理解了, 他們都是參考到同一個 class variable, 所以值都一樣。 + +### 小結 + +讓我們來整理一下, 這邊一共會有 3 個 `_dict`: + +1. class variable `_dict` +2. instance variable of a +3. instance variable of c diff --git "a/questions/algorithm/\344\270\200\345\200\213\346\225\270\346\234\200\345\276\214\344\270\200\344\275\215\346\230\2576\357\274\214\347\247\273\345\213\225\345\210\260\351\246\226\344\275\215\346\230\257\345\216\237\344\276\206\346\225\270\347\232\204\344\270\211\345\200\215\357\274\214\346\261\202\351\200\231\345\200\213\346\225\270.md" "b/questions/algorithm/\344\270\200\345\200\213\346\225\270\346\234\200\345\276\214\344\270\200\344\275\215\346\230\2576\357\274\214\347\247\273\345\213\225\345\210\260\351\246\226\344\275\215\346\230\257\345\216\237\344\276\206\346\225\270\347\232\204\344\270\211\345\200\215\357\274\214\346\261\202\351\200\231\345\200\213\346\225\270.md" new file mode 100644 index 0000000..8de074f --- /dev/null +++ "b/questions/algorithm/\344\270\200\345\200\213\346\225\270\346\234\200\345\276\214\344\270\200\344\275\215\346\230\2576\357\274\214\347\247\273\345\213\225\345\210\260\351\246\226\344\275\215\346\230\257\345\216\237\344\276\206\346\225\270\347\232\204\344\270\211\345\200\215\357\274\214\346\261\202\351\200\231\345\200\213\346\225\270.md" @@ -0,0 +1,103 @@ +# 一個數最後一位是6,移動到首位是原來數的三倍,求這個數 + +## 問題 + +題目:一個數最後一位是6,移動到首位是原來數的三倍,求這個數 +要求:速度最優 + +大神們快來踴躍探討~~~ + +問題出自 [segmentfault](https://segmentfault.com/q/1010000006135723/a-1020000006139815), by [prolifes](https://segmentfault.com/u/prolifes) + +## 回答 + +### 各路大神的回答 + +#### [citaret](https://segmentfault.com/u/citaret) 的回答 + +先贴上一版: + +```python +x = 6 +xs = [] +while True: + xs.append(x // 3) + x = x % 3 * 10 + x // 3 + if x == 6: + print ''.join(str(x) for x in xs) + break +``` + +输出: + +``` +2068965517241379310344827586 +``` + +原理是手算,把每次得到的商的末位补到被除数的最后,然后继续除法,直到末位为6,且余数为0停止。 + +#### [hsfzxjy](https://segmentfault.com/u/hsfzxjy) 的回答 + +由於數學公式的使用請大家前往問題原出處查看 [回答](https://segmentfault.com/q/1010000006135723/a-1020000006139815) + +### 我的回答 + +下午看到這題的時候就有了個想法, 手邊沒電腦只好等到現在... + +後來看到 @citaret 的答案就發現剛好是反向的想法, 下面是我的作法: + +```python +x = 6 +last_carry = 0 +result = 6 +radix = 10 + +while True: + c1, x = divmod(x * 3, 10) + c2, x = divmod(x + last_carry, 10) + last_carry = c1 + c2 + + if x==6 and last_carry==0: + return result + + result += (x * radix) + radix *= 10 +``` + +想法就剛好是反過來, 我一步一步地乘上去 + +* 每次把 `x` 乘 3 + * 把個位數加上 `last_carry` 就是下次的 `x` + * 把十位數的進位留下來當作下次的 `last_carry` + * 做到 `x==6` 且無進位的時候 + +用圖來思考長這樣: + +``` +0 <-- last carry + \ +1 8 = 6 X 3 + \ (0 + 8 = 8) +2 4 = 8 X 3 + \ (1 + 4 = 5) +1 5 = 5 X 3 +... +``` + +用 `timeit` 稍微測了一下時間(各運行 1000000 次), 共測三次: + +``` +# first +dokelung: 17.301649590954185 +citaret: 18.24915363173932 + +# second +dokelung: 19.257807812653482 +citaret: 17.994877750985324 + +# third +dokelung: 17.0617663115263 +citaret: 18.355605391785502 +``` + +時間看起來差不多, 不過我自認為代碼沒有很漂亮... diff --git "a/questions/django/Django CSRF verification failed \345\225\217\351\241\214.md" "b/questions/django/Django CSRF verification failed \345\225\217\351\241\214.md" new file mode 100644 index 0000000..f146839 --- /dev/null +++ "b/questions/django/Django CSRF verification failed \345\225\217\351\241\214.md" @@ -0,0 +1,93 @@ +# Django CSRF verification failed 問題 + +## 問題 + +**urls.py** + +```python +from django.conf.urls import url +from django.contrib import admin +from blog import views +urlpatterns = [ + url(r'^admin/', admin.site.urls), + url(r'^$', views.index), + url(r'^abc$',views.handler), +] +``` + +**views.py** + +```python +# -*- coding: utf-8 -*- +from django.shortcuts import render +from django.http import HttpResponse + +# Create your views here. + +def index(request): + return render(request,"index.html") + + +def handler(request): + return HttpResponse("

name:

" + request.POST['username']) +``` + +**index.html** + +```python + + + + + index page + + +
+ + +
+ + + +``` + +我在谷歌瀏覽器下點擊這個提交後出現了: +![图片描述][1] + +我又直接打開abc網站出現了: +![图片描述][2] + +請問這是什麼問題啊要怎麼解決啊? + + + [1]: https://segmentfault.com/img/bVz3ih + [2]: https://segmentfault.com/img/bVz3iq + +問題出自 [segmentfault](https://segmentfault.com/q/1010000006170144/a-1020000006171703), by [dzxczxsb](https://segmentfault.com/u/dzxczxsb) + +## 回答 + +在 Django 中, 使用 post 的時候很可能會出現以下錯誤: + +```python +Forbidden(403): +CSRF verification failed. Request aborted. +Reason given for failure: + CSRF token missing or incorrect. +``` + +這是因為 Django 幫我們啟動了 **CSRF攻擊** 的防護,CSRF(cross-site request forgery) 是惡意的跨站請求或偽裝使用者的攻擊,攻擊者會欺騙用戶的瀏覽器去訪問一個認證過的網站並且執行一些惡意的操作。由於用戶的瀏覽器已經被該網站認證過了,所以該網站會放心的讓這些操作被執行(即便這些操作並非該網站要求的或是不是用戶自願的)。 + +所以我們的伺服器需要一些有保護的措施。常見的一種防護手段,就是使用一個伺服器產生的亂數 token,夾帶在送給客戶端的表單中,當客戶端送回表單時,伺服器檢查這個 token 是不是自己發出,便可以防止攻擊。 + +由於在 `settings.py` 檔中的 `MIDDLEWARE_CLASSES` 中有預設的 `'django.middleware.csrf.CsrfViewMiddleware'`,所以 Django 在這裡便會要求 CSRF token 驗證,為了讓我們的網站更安全,我們還是照著遊戲規則一步一步來吧! + +在html的`
`中加入`{% csrf_token %}`如下: + +```html +... + {% csrf_token %} +... +``` + +就可以解決問題了 diff --git "a/questions/eval/\345\260\215Python\350\252\236\346\263\225\345\255\227\344\270\262\346\261\202\345\200\274.md" "b/questions/eval/\345\260\215Python\350\252\236\346\263\225\345\255\227\344\270\262\346\261\202\345\200\274.md" new file mode 100644 index 0000000..5874273 --- /dev/null +++ "b/questions/eval/\345\260\215Python\350\252\236\346\263\225\345\255\227\344\270\262\346\261\202\345\200\274.md" @@ -0,0 +1,46 @@ +# 對 Python 語法字串求值 + +## 問題 + +讀 csv 文件的時候讀到這樣的 str: + +```python +["item1", "item2", "item3"] +``` + +顯然這是一個 list +可是我該如何把它轉化成 list 呢? +用 `list()` 的話每個字符會被當成一個 item +用切詞的方法也應該可以但會麻煩 +但是有沒有直接的方法呢? + +問題出自 [segmentfault](https://segmentfault.com/q/1010000006152237/a-1020000006152877), by [persona](https://segmentfault.com/u/persona) + +## 回答 + +使用 `eval`, 不過要小心他的風險: + +```python +In [1]: s = '["item1", "item2", "item3"]' + +In [2]: lst = eval(s) + +In [3]: lst +Out[3]: ['item1', 'item2', 'item3'] +``` + +* [Python doc - eval](https://docs.python.org/3/library/functions.html#eval) + +對於 Python 的 subset 進行求值, 使用 `ast.literal_eval` 是對的。 + + +```python +# by manong +>>> import ast +>>> ast.literal_eval('["item1", "item2", "item3"]') +['item1', 'item2', 'item3'] +``` + +下面文章值得一讀: + +[Using python's eval() vs. ast.literal_eval()?](http://stackoverflow.com/questions/15197673/using-pythons-eval-vs-ast-literal-eval) diff --git "a/questions/json/\345\201\207\345\256\232\346\234\211json\346\225\270\346\223\232\345\244\232\346\242\235\350\250\230\351\214\204\357\274\214\345\246\202\344\275\225\346\240\271\346\223\232KEY\347\232\204\345\200\274\350\277\224\345\233\236\344\270\200\346\242\235\350\250\230\351\214\204.md" "b/questions/json/\345\201\207\345\256\232\346\234\211json\346\225\270\346\223\232\345\244\232\346\242\235\350\250\230\351\214\204\357\274\214\345\246\202\344\275\225\346\240\271\346\223\232KEY\347\232\204\345\200\274\350\277\224\345\233\236\344\270\200\346\242\235\350\250\230\351\214\204.md" new file mode 100644 index 0000000..da76948 --- /dev/null +++ "b/questions/json/\345\201\207\345\256\232\346\234\211json\346\225\270\346\223\232\345\244\232\346\242\235\350\250\230\351\214\204\357\274\214\345\246\202\344\275\225\346\240\271\346\223\232KEY\347\232\204\345\200\274\350\277\224\345\233\236\344\270\200\346\242\235\350\250\230\351\214\204.md" @@ -0,0 +1,112 @@ +# 假定有 json 數據多條記錄,如何根據 KEY 的值返回一條記錄? + +## 問題 + +比如說給一個 json 數據: + +```python +[ + { + "Name": "A1", + "No": "3111", + "createDate": "9999/12/31 00:00:00", + "lastUpdDate": "9999/12/31 00:00:00" + }, + { + "Name": "B2", + "No": "2222", + "createDate": "9999/12/31 00:00:00", + "lastUpdDate": "9999/12/31 00:00:00" + }, + { + "Name": "C3", + "No": "1444", + "createDate": "9999/12/31 00:00:00", + "lastUpdDate": "9999/12/31 00:00:00" + }, + { + "Name": "C4", + "No": "0542", + "createDate": "9999/12/31 00:00:00", + "lastUpdDate": "9999/12/31 00:00:00" + } +] +``` + +我想要 `No` 為 `"0542"` 的一條記錄: + +```python +{ + "Name": "C4", + "No": "0542", + "createDate": "9999/12/31 00:00:00", + "lastUpdDate": "9999/12/31 00:00:00" +} +``` + +如果用 python3 應該怎麼實現? + +我網上搜索了類似代碼, 比如: + +```python +data = list(filter(lambda d: d['No'] == "0542", jsondata)) +``` + +改來改去,總是報各種類型錯誤,抓狂了……所以請教下大家,應該怎麼寫,謝謝! + +問題出自 [segmentfault](https://segmentfault.com/q/1010000008793933/a-1020000008795741), by [Nix](https://segmentfault.com/u/nix) + +## 回答 + +假設有 json string 如下: + +```python +s = """ +[ + { + "Name": "A1", + "No": "3111", + "createDate": "9999/12/31 00:00:00", + "lastUpdDate": "9999/12/31 00:00:00" + }, + { + "Name": "B2", + "No": "2222", + "createDate": "9999/12/31 00:00:00", + "lastUpdDate": "9999/12/31 00:00:00" + }, + { + "Name": "C3", + "No": "1444", + "createDate": "9999/12/31 00:00:00", + "lastUpdDate": "9999/12/31 00:00:00" + }, + { + "Name": "C4", + "No": "0542", + "createDate": "9999/12/31 00:00:00", + "lastUpdDate": "9999/12/31 00:00:00" + } +] +""" +``` + +代碼: + +```python +# code for python3 + +import json + +def search(json_str, no): + return [datum for datum in json.loads(s) if datum['No']==no] + +datum = search(s, '0542') +print(datum) +``` + +結果: + +```python +[{'Name': 'C4', 'No': '0542', 'createDate': '9999/12/31 00:00:00', 'lastUpdDate': '9999/12/31 00:00:00'}] +``` diff --git "a/questions/list/python\347\224\237\346\210\220\347\211\271\345\256\232\345\210\227\350\241\250\346\240\274\345\274\217.md" "b/questions/list/python\347\224\237\346\210\220\347\211\271\345\256\232\345\210\227\350\241\250\346\240\274\345\274\217.md" new file mode 100644 index 0000000..b0cc587 --- /dev/null +++ "b/questions/list/python\347\224\237\346\210\220\347\211\271\345\256\232\345\210\227\350\241\250\346\240\274\345\274\217.md" @@ -0,0 +1,93 @@ +# python 如何生成特定列表格式 + +## 問題 + +```python +aa = [ + {'ip': '192.168.1.1', 'projectname__pname': 'hh', 'id': 1, 'projectname_id': 1}, + {'ip': '192.168.3.2', 'projectname__pname': 'hh', 'id': 2, 'projectname_id': 1}, + {'ip': '192.168.22.3', 'projectname__pname': 'qm', 'id': 3, 'projectname_id': 2}, + {'ip': '192.168.5.3', 'projectname__pname': 'ssh', 'id':4, 'projectname_id': 3} +] +``` + +大家好,我想把 `aa` 中的列表生成以下 `bb` 的格式: + +```python +bb = [ + { + 'projectname_id': 1, + 'projectname__pname': 'hh', + 'children': [{'id': 1, 'text': '192.168.1.1'},{'id': 2, 'text': '192.168.1.2'}] + }, + { + 'projectname_id': 2, + 'projectname__pname': 'qm', + 'children': [{'id': 3, 'text':'192.168.22.3'}] + }, + { + 'projectname_id': 3, + 'projectname__pname': 'ssh', + 'children': [{'id': 4, 'text': '192.168.5.3'}] + } +] +``` + +請問代碼怎麽實現? + +問題出自 [segmentfault](), by [tempreg](https://segmentfault.com/u/tempreg) + +## 回答 + +代碼: + +```python +def aa2bb(aa): + bb = [] + proj_id_map = {} + for ad in aa: + proj_id = ad['projectname_id'] + child = {'id': ad['id'], 'text': ad['ip']} + if proj_id not in proj_id_map: + bd = { + 'projectname__pname': ad['projectname__pname'], + 'projectname_id': ad['projectname_id'], + 'children': [child] + } + bb.append(bd) + proj_id_map[proj_id] = bd + else: + bd = proj_id_map[proj_id] + bd['children'].append(child) + return bb +``` + +測試: + +```python +from pprint import pprint + +aa = [ + {'ip': '192.168.1.1', 'projectname__pname': 'hh', 'id': 1, 'projectname_id': 1}, + {'ip': '192.168.3.2', 'projectname__pname': 'hh', 'id': 2, 'projectname_id': 1}, + {'ip': '192.168.22.3', 'projectname__pname': 'qm', 'id': 3, 'projectname_id': 2}, + {'ip': '192.168.5.3', 'projectname__pname': 'ssh', 'id':4, 'projectname_id': 3} +] + +pprint(aa2bb(aa)) +``` + +結果: + +```python +[{'children': [{'id': 1, 'text': '192.168.1.1'}, + {'id': 2, 'text': '192.168.3.2'}], + 'projectname__pname': 'hh', + 'projectname_id': 1}, + {'children': [{'id': 3, 'text': '192.168.22.3'}], + 'projectname__pname': 'qm', + 'projectname_id': 2}, + {'children': [{'id': 4, 'text': '192.168.5.3'}], + 'projectname__pname': 'ssh', + 'projectname_id': 3}] +``` diff --git "a/questions/object/Python\345\244\232\351\207\215\347\271\274\346\211\277\345\261\254\346\200\247\345\225\217\351\241\214.md" "b/questions/object/Python\345\244\232\351\207\215\347\271\274\346\211\277\345\261\254\346\200\247\345\225\217\351\241\214.md" new file mode 100644 index 0000000..68c2af2 --- /dev/null +++ "b/questions/object/Python\345\244\232\351\207\215\347\271\274\346\211\277\345\261\254\346\200\247\345\225\217\351\241\214.md" @@ -0,0 +1,109 @@ +# Python 多重繼承屬性問題 + +## 問題 + +Python 類的繼承,怎麽讓一個子類 `C`, 同時繼承父類 `A`, `B` 的屬性? + +先定義兩父類: + +```python + class A(object): + def __init__(self, a1,a2): + # super(ClassName, self).__init__() + self.a1 = a1 + self.a2 = a2 + + def funa(self): + print("I'm funa") + + class B(object): + def __init__(self, b1): + # super(ClassName, self).__init__() + self.b1 = b1 + + def funb(self): + print("I'm funb") +``` + +那麽子類 `C` 應該如何寫? 才能讓初始化後具有 `A`, `B` 中的 `a1`, `a2`, `b1` 屬性和 `funa`, `funb` 方法? + +```python + class C(A,B): + # ???????????????????? + def __init__(self): + super().__init__() + #????????????????????? + pass +``` +感覺是很基本的問題,求各位大佬解答,謝謝! + +問題出自 [segmentfault](), by [Andykim](https://segmentfault.com/u/andykim) + +## 回答 + +假設你要多重繼承的 **各個父類關係是平行的**, 多重繼承用於 **組合各父類的成員** (**Mixin** 的概念), 那你可以考慮下面這個例子, 而為了展示通用性, 下面的例子中有三個可能被用來繼承的父類 `A`, `B`, `C`, 而其子類 (例如 `X`, `Y`)可以用任意順序來組合任意數量個父類: + +```python +# base classes + +class A: + def __init__(self, a1, a2, **kwargs): + super().__init__(**kwargs) + self.a1 = a1 + self.a2 = a2 + + def funa(self): + print("I'm funa") + +class B: + def __init__(self, b1, **kwargs): + super().__init__(**kwargs) + self.b1 = b1 + + def funb(self): + print("I'm funb") + +class C: + def __init__(self, c1, c2, c3, **kwargs): + super().__init__(**kwargs) + self.c1 = c1 + self.c2 = c2 + self.c3 = c3 + + def func(self): + print("I'm func") +``` + +```python +# derived classes + +class X(B, A, C): + def __init__(self, **kwargs): + super().__init__(**kwargs) + +class Y(A, B): + def __init__(self, **kwargs): + super().__init__(**kwargs) +``` + +使用範例: + +```python +x = X(a1=1, a2=2, b1=3, c1=4, c2=5, c3=6) +y = Y(a1=1, a2=2, b1=3) +print(x.a1, x.a2, x.b1, x.c1, x.c2, x.c3) +x.funa() +y.funb() +print(dir(x)) +print(dir(y)) +``` + +結果: + +```python +1 2 3 4 5 6 +I'm funa +I'm funb +['__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'] +['__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'] +``` diff --git "a/questions/scope/\347\202\272\344\273\200\351\272\274\351\200\231\345\205\251\346\256\265 python lambda \345\207\272\347\217\276\344\270\215\345\220\214\347\232\204\347\265\220\346\236\234.md" "b/questions/scope/\347\202\272\344\273\200\351\272\274\351\200\231\345\205\251\346\256\265 python lambda \345\207\272\347\217\276\344\270\215\345\220\214\347\232\204\347\265\220\346\236\234.md" new file mode 100644 index 0000000..96edcd0 --- /dev/null +++ "b/questions/scope/\347\202\272\344\273\200\351\272\274\351\200\231\345\205\251\346\256\265 python lambda \345\207\272\347\217\276\344\270\215\345\220\214\347\232\204\347\265\220\346\236\234.md" @@ -0,0 +1,151 @@ +# 為什麼這兩段 python lambda 出現不同的結果 + +## 問題 + +寫了這樣一段代碼: + +```python +[i(4) for i in [(lambda y: print('y=', y, '; x=', x, ' ; n + x = ', y + x)) for x in range(10)]] +``` + +發現輸出如下: + +``` +y= 4 ; x= 9 ; n + x = 13 +y= 4 ; x= 9 ; n + x = 13 +y= 4 ; x= 9 ; n + x = 13 +y= 4 ; x= 9 ; n + x = 13 +y= 4 ; x= 9 ; n + x = 13 +y= 4 ; x= 9 ; n + x = 13 +y= 4 ; x= 9 ; n + x = 13 +y= 4 ; x= 9 ; n + x = 13 +y= 4 ; x= 9 ; n + x = 13 +y= 4 ; x= 9 ; n + x = 13 +``` + +修改了一下: + +```python +import functools + +[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)]] +``` + +輸出如下: + +``` +y= 4 ; x= 0 ; y + x = 4 +y= 4 ; x= 1 ; y + x = 5 +y= 4 ; x= 2 ; y + x = 6 +y= 4 ; x= 3 ; y + x = 7 +y= 4 ; x= 4 ; y + x = 8 +y= 4 ; x= 5 ; y + x = 9 +y= 4 ; x= 6 ; y + x = 10 +y= 4 ; x= 7 ; y + x = 11 +y= 4 ; x= 8 ; y + x = 12 +y= 4 ; x= 9 ; y + x = 13 +``` + +不知道為什麼會有這樣的差異,請指教 + +問題出自 [segmentfault](https://segmentfault.com/q/1010000006121417/a-1020000006121940), by [kavlez](https://segmentfault.com/u/kavlez) + +## 回答 + +你問的這個問題不是那麼好理解, 不過我慢慢說看看能接受多少。 + +### 第一段 code + +首先我們看到你寫的第一段 code: + +```python +[i(4) for i in [(lambda y: print('y=', y, '; x=', x, ' ; n + x = ', y + x)) for x in range(10)]] +``` + +我們先專心看內層的 list comprehension: + +```python +funclist1 = [(lambda y: print('y=', y, '; x=', x, ' ; n + x = ', y + x)) for x in range(10)] +``` + +這段我們可以寫成一段等價的 code: + +```python +def produce_functions(): + funclist = [] + for x in range(10): + def ld(y): + print('y=', y, '; x=', x, ' ; n + x = ', y + x) + funclist.append(ld) + return funclist + +funclist2 = produce_functions() +``` + +我們隨意地來測試一下: + +```python +>>> funclist1[0](4) +y= 4 ; x= 9 ; n + x = 13 + +>>> funclist2[0](4) +y= 4 ; x= 9 ; n + x = 13 +``` + +希望大家看到這裡可以接受兩者除了一個用 lambda funciton 一個用 normal function 但是其實行為上是一致的。 + +接著請大家仔細看上面那段等價的 code, 你想到了甚麼呢? 沒錯! decorator 裡面會出現的 **閉包(closure)** !! 在這裡 `x` 不就是 **free variable** 嗎? 所以 `x` 並不會被綁死在 `ld` 中, 他參考到一個非全域的變數 `x`。我要說的是, 製造出來的 10 個 function 全部都參考到同一個 `x`, `for x in range(10)` 很容易誤導大家, 以為有十個不同的 `x` 然後製造了 10 個不同的 function。其實你跟我都明白, 這裡只有一個 `x` 變數, 只是他的值在改變, 但是說到底 `x` 就那麼一個。 + +為了避免空口說白話, 我證明我說的給大家看: + +```python +>>> funclist1[0].__code__.co_freevars +('x',) +>>> funclist2[0].__code__.co_freevars +('x',) +``` + +好, 那 `x` 的值在製造完之後究竟是多少呢? + +```python +>>> funclist2[0].__closure__ +(,) + +>>> funclist2[0].__closure__[0] + + +>>> funclist2[0].__closure__[0].cell_contents +9 +``` + +沒錯, 是 9 !, 所有所有製造出來的 function 全部都會參考到這個 `x`, 所以他們的 `x` 值就是 9 ! + +所以, 出現這個結果也就不意外了: + +```python +y= 4 ; x= 9 ; n + x = 13 +y= 4 ; x= 9 ; n + x = 13 +y= 4 ; x= 9 ; n + x = 13 +... +``` + +### 第二段 code + +接著我們來看第二段 code (一樣 focus 在內層): + +```python +import functools + +[functools.partial(lambda x, y: print('y=', y, '; x=', x, ' ; y + x = ', y + x), y) for y in range(10)] +``` + +`partial(func, a)` 會凍結 `func` 的第一個引數(會綁定 a 值) + +所以上面這段 code 會從 0~9 凍結這個 lambda function 的第一個引數, 這邊不同於 free variables, 這裡不是讓第一個引數 `x` 參考到同一個人, 而是直接綁死(代定) 0~9, 所以這裡每一個製造出來的 functions 全部都不一樣, 且可以當成 x 指定為 0~9。 + +經過上述講解, 應該大致可以明白兩者不同之處了。 + +### 結論 + +* `partial` 會直接凍結引數, 使變數代入定值 +* 一般的 function 會直接參考到 free variable 而不是代入定值 diff --git "a/questions/standard_lib/Python \347\215\262\345\217\226\346\226\207\344\273\266\350\267\257\345\276\221\345\217\212\346\226\207\344\273\266\347\233\256\351\214\204(__file__ \347\232\204\344\275\277\347\224\250\346\226\271\346\263\225).md" "b/questions/standard_lib/Python \347\215\262\345\217\226\346\226\207\344\273\266\350\267\257\345\276\221\345\217\212\346\226\207\344\273\266\347\233\256\351\214\204(__file__ \347\232\204\344\275\277\347\224\250\346\226\271\346\263\225).md" new file mode 100644 index 0000000..85081f2 --- /dev/null +++ "b/questions/standard_lib/Python \347\215\262\345\217\226\346\226\207\344\273\266\350\267\257\345\276\221\345\217\212\346\226\207\344\273\266\347\233\256\351\214\204(__file__ \347\232\204\344\275\277\347\224\250\346\226\271\346\263\225).md" @@ -0,0 +1,158 @@ +# Python 獲取文件路徑及文件目錄(`__file__` 的使用方法) + +## 問題 + +我正在學習Python,不過遇到一些問題,想請教: + +`os` module 中的 `os.path.dirname(__file__)` 和 `os.path.abspath(__file__)` + +運行 `os.path.dirname(__file__)` 時候,為什麼返回的是空白呢? 是不是因為他運行的是相對路徑??? + +如果是的話: + +1. 我怎麼能夠知道,括號內的文件是以相對路徑還是絕對路徑被運行的? +2. 為什麼我運行下面例子腳本的時候,這個文件是以相對路徑被運行的呢? + +比如我下面的例子: + +```python +import os + +print (os.path.dirname(__file__)) +print (os.path.abspath(__file__)) +print (os.path.abspath(os.path.dirname(__file__))) +print (os.path.dirname(os.path.abspath(__file__))) +``` + +![测试][1] + [1]: https://segmentfault.com/img/bVzRXy + +PS:附加問題 +`os.path.abspath(os.path.dirname(__file__))` 和 `os.path.dirname(os.path.abspath(__file__))` 性質是否一樣呢? + +問題出自 [segmentfault](https://segmentfault.com/q/1010000006126582/a-1020000006127638), by [nodinner](https://segmentfault.com/u/nodinner) + +## 回答 + +建議你可以稍微瀏覽一下 Python doc: [os.path](https://docs.python.org/3.5/library/os.path.html), 你就會明白囉: + +我放上跟你問題相關的幾個條目: + +* `os.path.abspath(path)` + * 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)). + +* `os.path.normpath(path)` + * 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(). + +* `os.path.dirname(path)` + * Return the directory name of pathname path. This is the first element of the pair returned by passing path to the function split(). + +* `os.path.split(path)` + * 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(). + +我們做以下觀察: + +`test.py` + +```python +import os + +print(__file__) +print(os.path.dirname(__file__)) +print(os.path.abspath(__file__)) +print(os.path.abspath(os.path.dirname(__file__))) +print(os.path.dirname(os.path.abspath(__file__))) +``` + +運行: + +``` +$ pwd +/home/dokelung +$ python test.py +``` + +結果: + +``` +test.py + +/home/dokelung/test.py +/home/dokelung +/home/dokelung +``` + +首先 `__file__` 的值其實就是在命令列上 invoke Python 時給的 script 名稱: + +```python +$ python test.py # 此時 __file__ 是 test.py +$ python ../test.py # 此時 __file__ 是 ../test.py +$ python hello/../test.py # 此時 __file__ 是 hello/../test.py +``` + +在這裡, 因為 `__file__` 的值為 `test.py`, 所以 `print(__file__)` 的結果是 `test.py` 也就不意外了。 + +接著, `os.path.dirname(__file__)`之所以得出空白(空字串), 是因為 `__file__` 就只是一個單純的名稱(非路徑) 且 `dirname` 也只是很單純的利用 `os.path.split()` 來切割這個名稱(這當然沒甚麼好切的, 連路徑分割符都沒有): + +```python +>>> import os +>>> os.path.split('test.py') +('', 'test.py') +>>> os.path.split('test.py')[0] +'' +``` + +我分會發現切出來的 `head` 是空字串, 所以 `dirname` 的結果是空白。 + +`abspath` 動用了 `os.getcwd()` 所以即便給定的是單純的名稱, 也能返回路徑: + +```python +>>> os.getcwd() +'/home/dokelung' + +>>> os.path.join(os.getcwd(), 'test.py') +'/home/dokelung/test.py' + +>>> os.path.normpath(os.path.join(os.getcwd(), 'test.py')) +'/home/dokelung/test.py' +``` + +而 `os.path.abspath(os.path.dirname(__file__))` 的結果就等於是 `os.getcwd()` 的結果去接上 `dirname` 得到的空字串: + +```python +>>> os.path.dirname('test.py') +'' + +>>> os.path.join(os.getcwd(), os.path.dirname('test.py')) +'/home/dokelung/' +``` + +最後, `os.path.dirname(os.path.abspath(__file__))` 的結果是這麼來的: + +```python +>>> os.path.abspath('test.py') +'/home/dokelung/test.py' + +>>> os.path.split(os.path.abspath('test.py')) +('/home/dokelung', 'test.py') + +>>> os.path.split(os.path.abspath('test.py'))[0] +'/home/dokelung' +``` + +希望講到這裡有讓你明白! + +## 結論 +現在簡要的回答你的問題 + +1. 為什麼 `dirname` 出現空白? + * 因為你運行的時候給的是單純的名稱, 所以 `__file__` 是單純的名字非路徑 + +2. 我怎么能够知道,括号内的文件是以相对路径还是绝对路径被运行的? + * 很簡單, 就看你怎麼運行 Python 的 + +3. 为什么我运行下面例子脚本的时候,这个文件是以相对路径被运行的呢? + * 因為 `$ python 1.py` 你自己給了相對路徑 + +4. `os.path.abspath(os.path.dirname(__file__))` 和 `os.path.dirname(os.path.abspath(__file__))` 性质是否一样呢? + * 基本上一樣 diff --git "a/questions/standard_lib/\344\272\244\346\217\233\345\205\251\345\200\213shelve objects.md" "b/questions/standard_lib/\344\272\244\346\217\233\345\205\251\345\200\213shelve objects.md" new file mode 100644 index 0000000..f90ce25 --- /dev/null +++ "b/questions/standard_lib/\344\272\244\346\217\233\345\205\251\345\200\213shelve objects.md" @@ -0,0 +1,209 @@ +# 如何交換兩個 shelve objects + +## 問題 + +假定 `db1`, `db2` 是 `shelve` objects: + +```python +if switch_d1_and_db2: + func(db1, db2) +else: + func(db2, db1) +``` + +怎麼才能改寫成: + +```python +if switch_d1_and_db2: + db1, db2 = db2, db1 # 错误写法 +func(db1, db2) +``` + +`db1, db2 = db2, db1` 肯定是不行的,怎麼改寫呢? + +所謂不行可以見以下範例: + +`define.py`: + +```python +import shelve +db1 = shelve.open('db1', writeback = True) +db2 = shelve.open('db2', writeback = True) +db1['name'] = 'db1' +db2['name'] = 'db2' +db1.close() +db2.close() +``` + +`switch.py`: + +```python +import shelve +db1 = shelve.open('db1', writeback = True) +db2 = shelve.open('db2', writeback = True) +db1, db2 = db2, db1 +print db1 +print db2 +db1.close() +db2.close() +``` + +`check.py`: + +```python +import shelve +db1 = shelve.open('db1', writeback = True) +db2 = shelve.open('db2', writeback = True) +print 'db1:', db1 +print 'db2:', db2 +``` + +測試: + +``` +$ python define.py +$ python check.py +db1: {'name': 'db1'} +db2: {'name': 'db2'} +$ python switch.py +{'name': 'db2'} +{'name': 'db1'} +$ python check.py +db1: {'name': 'db1'} +db2: {'name': 'db2'} +``` + +雖然 switch 成功, 但是最後 check 還是失敗, 代表改變沒有正確反映在 db 中。 + +問題出自 [segmentfault](https://segmentfault.com/q/1010000006116287/a-1020000006119818), by [littlealias](https://segmentfault.com/u/littlealias) + +## 回答 + +你好, 我研究這個問題一陣之後結論是: + +1. 太難做到而且你想要使用的語法跟你要做的事情並不 match +2. 我覺得用原本的方法沒有什麼不好 +3. 如果你想要做到你定義的這種交換, 那我有一個不算是太漂亮的替代方案, 你可以參考 + +以下針對上述三點說明: + +對於第一點, 你想要: + +```python +db1, db2 = db2, db1 +``` + +這邊不論 `db1`, `db2` 是哪種 object, 這種交換式的意義在於 + +> 讓 `db1` 這個變數參考到原本 `db2` 所參考的 object, 並讓 `db2` 這個變數參考到原本 `db1` 所參考到的 object。 + +但是你想要做的事情是: + +> 讓 `db1` 這個 file 和 `db2` 這個 file 的內容互換 + +仔細想一想, 這兩件事情並不相同, 換個方式來說, `db1, db2 = db2, db1`, 只會讓變數參考的東西互換(變數名稱不等於 db 的 file 名稱), 但是每個文件的內容還是沒有互換。 + +所以 **使用這種語法來互換跟你要達到的效果並不一致**。 + +第二點就不多說明了, 因為合理, 只是你可能不喜歡。 + +第三點我給了一個不怎麼漂亮的替代方案, 就是簡單定義一個 `shelf` 的代理類 `ShelfProxy`, 這個類盡量模擬 `Shelf` 類的行為(僅是介面上相似), 並且重載了運算符 `^` 定義為交換: + +```python +import shelve + +class ShelfProxy: + + def __init__(self, *args, **kwargs): + self.args = args + self.kwargs = kwargs + self.file = args[0] + self.loaddb() + + @classmethod + def open(cls, *args, **kwargs): + return cls(*args, **kwargs) + + def __getattr__(self, name): + return getattr(self.dic, name) + + def __setitem__(self, name, value): + self.dic[name] = value + + def __getitem__(self, name): + return self.dic[name] + + def __xor__(self, other): + self.dic, other.dic = other.dic, self.dic + return True + + def __str__(self): + return str(self.dic) + + def loaddb(self): + db = shelve.open(*self.args, **self.kwargs) + self.dic = dict(db.items()) + db.close() + + def close(self): + newdb = shelve.open(self.file, *self.args[1:], **self.kwargs) + for key in newdb.keys(): + del newdb[key] + for key, value in self.dic.items(): + newdb[key] = value + newdb.close() +``` + +我將 `^` 定義為 **內容上的交換**, 之所以選 `^` 只是因為我想不到比較適合的符號, 一般來說重載不會這樣進行, 而且也不太會返回其他類的實例, 不過我這邊為求方便且針對你想要一個簡單介面這一點, 出此下策。 + +接著我們定義一些測試的 function: + +```python +def define(): + db1 = ShelfProxy.open('db1', writeback=True) + db2 = ShelfProxy.open('db2', writeback=True) + db1['name'] = 'db1' + db2['name'] = 'db2' + db1.close() + db2.close() + +def check(): + db1 = ShelfProxy.open('db1', writeback=True) + db2 = ShelfProxy.open('db2', writeback=True) + print('db1:', db1) + print('db2:', db2) + db1.close() + db2.close() + +def switch(): + print('switch') + db1 = ShelfProxy.open('db1', writeback=True) + db2 = ShelfProxy.open('db2', writeback=True) + db1 ^ db2 + db1.close() + db2.close() +``` + +測試代碼: + +```python +if __name__ == '__main__': + define() + check() + switch() + check() +``` + +結果: + +``` +db1: {'name': 'db1'} +db2: {'name': 'db2'} +switch +db1: {'name': 'db2'} +db2: {'name': 'db1'} +``` + +### 結論 + +大部分的時候, 你可以用跟 `Shelf` 相同的介面來操作 `ShelfProxy`, 整體效果也類似, 但是你不覺得寫了那麼多, 還是使用一開始的方法比較簡單嗎XD