From 99eb0693d57250c60ec1a1fe5f738703223444e0 Mon Sep 17 00:00:00 2001 From: ateliershen Date: Mon, 28 May 2018 21:27:47 +0800 Subject: [PATCH 1/3] opencc st2twp --- .../Day01/\345\210\235\350\257\206Python.md" | 142 +++--- ...55\350\250\200\345\205\203\347\264\240.md" | 144 +++--- ...06\346\224\257\347\273\223\346\236\204.md" | 120 ++--- ...52\347\216\257\347\273\223\346\236\204.md" | 98 ++-- ...23\345\222\214\347\273\203\344\271\240.md" | 2 +- ...27\347\232\204\344\275\277\347\224\250.md" | 116 ++--- ...60\346\215\256\347\273\223\346\236\204.md" | 232 ++++----- ...26\347\250\213\345\237\272\347\241\200.md" | 106 ++--- ...71\350\261\241\350\277\233\351\230\266.md" | 228 ++++----- ...70\346\210\217\345\274\200\345\217\221.md" | 166 +++---- ...66\345\222\214\345\274\202\345\270\270.md" | 114 ++--- ...31\350\241\250\350\276\276\345\274\217.md" | 160 +++---- ...13\345\222\214\347\272\277\347\250\213.md" | 186 ++++---- ...26\347\250\213\345\205\245\351\227\250.md" | 158 +++--- ...24\347\224\250\345\274\200\345\217\221.md" | 66 +-- ...55\350\250\200\350\277\233\351\230\266.md" | 2 +- ...15\347\253\257\346\246\202\350\277\260.md" | 450 +++++++++--------- "Day31-35/\347\216\251\350\275\254Linux.md" | 260 +++++----- ...6\225\260\346\215\256\345\272\223MySQL.md" | 22 +- ...6\225\260\346\215\256\345\272\223Redis.md" | 6 +- .../Django2\345\256\236\346\210\23001.md" | 184 +++---- .../Django2\345\256\236\346\210\23002.md" | 396 +++++++-------- .../Django2\345\256\236\346\210\23003.md" | 2 +- .../Django2\345\256\236\346\210\23004.md" | 2 +- .../Django2\345\256\236\346\210\23005.md" | 2 +- .../Django2\345\256\236\346\210\23006.md" | 2 +- .../Django2\345\256\236\346\210\23007.md" | 2 +- .../Django2\345\256\236\346\210\23008.md" | 2 +- .../Django2\345\256\236\346\210\23009.md" | 2 +- .../Django2\345\256\236\346\210\23010.md" | 2 +- ...16\346\240\274\346\214\207\345\215\227.md" | 50 +- ...02\350\200\203\344\271\246\347\261\215.md" | 70 +-- "Python\346\203\257\344\276\213.md" | 58 +-- README.md | 342 ++++++------- ...6\251\350\275\254PyCharm(\344\270\212).md" | 32 +- ...04\350\241\250\350\276\276\345\274\217.md" | 24 +- ...04\351\202\243\344\272\233\345\235\221.md" | 70 +-- 37 files changed, 2010 insertions(+), 2010 deletions(-) diff --git "a/Day01-15/Day01/\345\210\235\350\257\206Python.md" "b/Day01-15/Day01/\345\210\235\350\257\206Python.md" index 44f048b8f..73c757c31 100644 --- "a/Day01-15/Day01/\345\210\235\350\257\206Python.md" +++ "b/Day01-15/Day01/\345\210\235\350\257\206Python.md" @@ -1,56 +1,56 @@ -## Day01 - 初识Python +## Day01 - 初識Python -### Python简介 +### Python簡介 -#### Python的历史 +#### Python的歷史 -1. 1989年圣诞节:Guido von Rossum开始写Python语言的编译器。 -2. 1991年2月:第一个Python编译器(同时也是解释器)诞生,它是用C语言实现的(后面又出现了Java和C#实现的版本Jython和IronPython,以及PyPy、Brython、Pyston等其他实现),可以调用C语言的库函数。在最早的版本中,Python已经提供了对“类”,“函数”,“异常处理”等构造块的支持,同时提供了“列表”和“字典”等核心数据类型,同时支持以模块为基础的拓展系统。 -3. 1994年1月:Python 1.0正式发布。 -4. 2000年10月16日:Python 2.0发布,增加了实现完整的[垃圾回收](https://zh.wikipedia.org/wiki/%E5%9E%83%E5%9C%BE%E5%9B%9E%E6%94%B6_(%E8%A8%88%E7%AE%97%E6%A9%9F%E7%A7%91%E5%AD%B8)),并且支持[Unicode](https://zh.wikipedia.org/wiki/Unicode)。与此同时,Python的整个开发过程更加透明,社区对开发进度的影响逐渐扩大,生态圈开始慢慢形成。 -5. 2008年12月3日:Python 3.0发布,此版不完全兼容之前的Python代码,不过很多新特性后来也被移植到旧的Python 2.6/2.7版本,因为目前还有公司在项目和运维中使用Python 2.x版本的代码。 +1. 1989年聖誕節:Guido von Rossum開始寫Python語言的編譯器。 +2. 1991年2月:第一個Python編譯器(同時也是直譯器)誕生,它是用C語言實現的(後面又出現了Java和C#實現的版本Jython和IronPython,以及PyPy、Brython、Pyston等其他實現),可以呼叫C語言的庫函式。在最早的版本中,Python已經提供了對“類”,“函式”,“異常處理”等構造塊的支援,同時提供了“列表”和“字典”等核心資料型別,同時支援以模組為基礎的拓展系統。 +3. 1994年1月:Python 1.0正式釋出。 +4. 2000年10月16日:Python 2.0釋出,增加了實現完整的[垃圾回收](https://zh.wikipedia.org/wiki/%E5%9E%83%E5%9C%BE%E5%9B%9E%E6%94%B6_(%E8%A8%88%E7%AE%97%E6%A9%9F%E7%A7%91%E5%AD%B8)),並且支援[Unicode](https://zh.wikipedia.org/wiki/Unicode)。與此同時,Python的整個開發過程更加透明,社群對開發進度的影響逐漸擴大,生態圈開始慢慢形成。 +5. 2008年12月3日:Python 3.0釋出,此版不完全相容之前的Python程式碼,不過很多新特性後來也被移植到舊的Python 2.6/2.7版本,因為目前還有公司在專案和運維中使用Python 2.x版本的程式碼。 -目前我们使用的Python 3.6.x的版本是在2016年的12月23日发布的,Python的版本号分为三段,形如A.B.C。其中A表示大版本号,一般当整体重写,或出现不向后兼容的改变时,增加A;B表示功能更新,出现新功能时增加B;C表示小的改动(如修复了某个Bug),只要有修改就增加C。如果对Python的历史感兴趣,可以查看一篇名为[《Python简史》](http://www.cnblogs.com/vamei/archive/2013/02/06/2892628.html)的博文。 +目前我們使用的Python 3.6.x的版本是在2016年的12月23日釋出的,Python的版本號分為三段,形如A.B.C。其中A表示大版本號,一般當整體重寫,或出現不向後相容的改變時,增加A;B表示功能更新,出現新功能時增加B;C表示小的改動(如修復了某個Bug),只要有修改就增加C。如果對Python的歷史感興趣,可以檢視一篇名為[《Python簡史》](http://www.cnblogs.com/vamei/archive/2013/02/06/2892628.html)的博文。 -#### Python的优缺点 +#### Python的優缺點 -Python的优点很多,简单的可以总结为以下几点。 +Python的優點很多,簡單的可以總結為以下幾點。 -1. 简单和明确,做一件事只有一种方法。 -2. 学习曲线低,与其他很多语言比上手更容易。 -3. 开放源代码,拥有强大的社区和生态圈。 -4. 解释型语言,完美的平台可移植性。 -5. 支持两种主流的编程范式,可以使用面向对象和函数式编程。 -6. 可扩展性和可嵌入性,可以调用C/C++代码也可以在C/C++中调用。 -7. 代码规范程度高,可读性强,适合有代码洁癖和强迫症的人群。 +1. 簡單和明確,做一件事只有一種方法。 +2. 學習曲線低,與其他很多語言比上手更容易。 +3. 開放原始碼,擁有強大的社群和生態圈。 +4. 解釋型語言,完美的平臺可移植性。 +5. 支援兩種主流的程式設計正規化,可以使用面向物件和函數語言程式設計。 +6. 可擴充套件性和可嵌入性,可以呼叫C/C++程式碼也可以在C/C++中呼叫。 +7. 程式碼規範程度高,可讀性強,適合有程式碼潔癖和強迫症的人群。 -Python的缺点主要集中在以下几点。 +Python的缺點主要集中在以下幾點。 -1. 执行效率低下,因此计算密集型任务可以由C/C++编写。 -2. 代码无法加密,但是现在的公司很多都不是卖软件而是卖服务,这个问题慢慢会淡化。 -3. 在开发时可以选择的框架太多,有选择的地方就有错误。 +1. 執行效率低下,因此計算密集型任務可以由C/C++編寫。 +2. 程式碼無法加密,但是現在的公司很多都不是賣軟體而是賣服務,這個問題慢慢會淡化。 +3. 在開發時可以選擇的框架太多,有選擇的地方就有錯誤。 -#### Python的应用领域 +#### Python的應用領域 -目前Python在云基础设施、DevOps、网络爬虫开发、数据分析挖掘、机器学习等领域都有着广泛的应用,因此也产生了服务器开发、数据接口开发、自动化运维、科学计算和数据可视化、聊天机器人开发、图像识别和处理等一系列的职位。 +目前Python在雲基礎設施、DevOps、網路爬蟲開發、資料分析挖掘、機器學習等領域都有著廣泛的應用,因此也產生了伺服器開發、資料介面開發、自動化運維、科學計算和資料視覺化、聊天機器人開發、影象識別和處理等一系列的職位。 -### 搭建编程环境 +### 搭建程式設計環境 -#### Windows环境 +#### Windows環境 -可以在[Python的官方网站](https://www.python.org)下载到Python的Windows安装程序(exe文件),需要注意的是如果在Windows 7环境下安装需要先安装Service Pack 1补丁包(可以通过一些工具软件自动安装系统补丁的功能来安装),安装过程建议勾选“Add Python 3.6 to PATH”(将Python 3.6添加到PATH环境变量)并选择自定义安装,在设置“Optional Features”界面最好将“pip”、“tcl/tk”、“Python test suite”等项全部勾选上。强烈建议使用自定义的安装路径并保证路径中没有中文。安装完成会看到“Setup was successful”的提示,但是在启动Python环境时可能会因为缺失一些动态链接库文件而导致Python解释器无法运行,常见的问题主要是api-ms-win-crt\*.dll缺失以及更新DirectX之后导致某些动态链接库文件缺失,前者可以参照[《api-ms-win-crt\*.dll缺失原因分析和解决方法》]()一文讲解的方法进行处理或者直接在[微软官网](https://www.microsoft.com/zh-cn/download/details.aspx?id=48145)下载Visual C++ Redistributable for Visual Studio 2015文件进行修复,后者可以下载一个DirectX修复工具进行修复。 +可以在[Python的官方網站](https://www.python.org)下載到Python的Windows安裝程式(exe檔案),需要注意的是如果在Windows 7環境下安裝需要先安裝Service Pack 1補丁包(可以通過一些工具軟體自動安裝系統補丁的功能來安裝),安裝過程建議勾選“Add Python 3.6 to PATH”(將Python 3.6新增到PATH環境變數)並選擇自定義安裝,在設定“Optional Features”介面最好將“pip”、“tcl/tk”、“Python test suite”等項全部勾選上。強烈建議使用自定義的安裝路徑並保證路徑中沒有中文。安裝完成會看到“Setup was successful”的提示,但是在啟動Python環境時可能會因為缺失一些動態連結庫檔案而導致Python直譯器無法執行,常見的問題主要是api-ms-win-crt\*.dll缺失以及更新DirectX之後導致某些動態連結庫檔案缺失,前者可以參照[《api-ms-win-crt\*.dll缺失原因分析和解決方法》]()一文講解的方法進行處理或者直接在[微軟官網](https://www.microsoft.com/zh-cn/download/details.aspx?id=48145)下載Visual C++ Redistributable for Visual Studio 2015檔案進行修復,後者可以下載一個DirectX修復工具進行修復。 -#### Linux环境 +#### Linux環境 -Linux环境自带了Python 2.x版本,但是如果要更新到3.x的版本,可以在[Python的官方网站](https://www.python.org)下载Python的源代码并通过源代码构建安装的方式进行安装,具体的步骤如下所示。 +Linux環境自帶了Python 2.x版本,但是如果要更新到3.x的版本,可以在[Python的官方網站](https://www.python.org)下載Python的原始碼並通過原始碼構建安裝的方式進行安裝,具體的步驟如下所示。 -安装依赖库(因为没有这些依赖库可能在源代码构件安装时因为缺失底层依赖库而失败)。 +安裝依賴庫(因為沒有這些依賴庫可能在原始碼構件安裝時因為缺失底層依賴庫而失敗)。 ```Shell yum -y install zlib-devel bzip2-devel openssl-devel ncurses-devel sqlite-devel readline-devel tk-devel gdbm-devel db4-devel libpcap-devel xz-devel ``` -下载Python源代码并解压缩到指定目录。 +下載Python原始碼並解壓縮到指定目錄。 ```Shell wget https://www.python.org/ftp/python/3.6.1/Python-3.6.1.tar.xz @@ -58,7 +58,7 @@ xz -d Python-3.6.1.tar.xz tar -xvf Python-3.6.1.tar ``` -切换至Python源代码目录并执行下面的命令进行配置和安装。 +切換至Python原始碼目錄並執行下面的命令進行配置和安裝。 ```Shell cd Python-3.6.1 @@ -66,27 +66,27 @@ cd Python-3.6.1 make && make install ``` -创建软链接,这样就可以直接通过python3直接启动Python解释器。 +建立軟連結,這樣就可以直接通過python3直接啟動Python直譯器。 ```Shell ln -s /usr/local/python3.6/bin/python3 /usr/bin/python3 ``` -#### MacOS环境 +#### MacOS環境 -MacOS也是自带了Python 2.x版本的,可以通过[Python的官方网站](https://www.python.org)提供的安装文件(pkg文件)安装3.x的版本。默认安装完成后,可以通过在终端执行python命令来启动2.x版本的Python解释器,可以通过执行python3命令来启动3.x版本的Python解释器,当然也可以通过重新设置软链接来修改启动Python解释器的命令。 +MacOS也是自帶了Python 2.x版本的,可以通過[Python的官方網站](https://www.python.org)提供的安裝檔案(pkg檔案)安裝3.x的版本。預設安裝完成後,可以通過在終端執行python命令來啟動2.x版本的Python直譯器,可以通過執行python3命令來啟動3.x版本的Python直譯器,當然也可以通過重新設定軟連結來修改啟動Python直譯器的命令。 -### 从终端运行Python程序 +### 從終端執行Python程式 -#### 确认Python的版本 +#### 確認Python的版本 -在终端或命令行提示符中键入下面的命令。 +在終端或命令列提示符中鍵入下面的命令。 ```Shell python --version ``` -当然也可以先输入python进入交互式环境,再执行以下的代码检查Python的版本。 +當然也可以先輸入python進入互動式環境,再執行以下的程式碼檢查Python的版本。 ```Python import sys @@ -95,37 +95,37 @@ print(sys.version_info) print(sys.version) ``` -#### 编写Python源代码 +#### 編寫Python原始碼 -可以用文本编辑工具(推荐使用Sublime、Atom、TextMate、VSCode等高级文本编辑工具)编写Python源代码并将其命名为hello.py保存起来,代码内容如下所示。 +可以用文字編輯工具(推薦使用Sublime、Atom、TextMate、VSCode等高階文字編輯工具)編寫Python原始碼並將其命名為hello.py儲存起來,程式碼內容如下所示。 ```Python print('hello, world!') ``` -#### 运行程序 +#### 執行程式 -切换到源代码所在的目录并执行下面的命令,看看屏幕上是否输出了"hello, world!"。 +切換到原始碼所在的目錄並執行下面的命令,看看螢幕上是否輸出了"hello, world!"。 ```Shell python hello.py ``` -### 代码中的注释 +### 程式碼中的註釋 -注释是编程语言的一个重要组成部分,用于在源代码中解释代码的作用从而增强程序的可读性和可维护性,当然也可以将源代码中不需要参与运行的代码段通过注释来去掉,这一点在调试程序的时候经常用到。注释在随源代码进入预处理器或编译时会被移除,不会在目标代码中保留也不会影响程序的执行结果。 +註釋是程式語言的一個重要組成部分,用於在原始碼中解釋程式碼的作用從而增強程式的可讀性和可維護性,當然也可以將原始碼中不需要參與執行的程式碼段通過註釋來去掉,這一點在除錯程式的時候經常用到。註釋在隨原始碼進入前處理器或編譯時會被移除,不會在目的碼中保留也不會影響程式的執行結果。 -1. 单行注释 - 以#和空格开头的部分 -2. 多行注释 - 三个引号开头,三个引号结尾 +1. 單行註釋 - 以#和空格開頭的部分 +2. 多行註釋 - 三個引號開頭,三個引號結尾 ```Python """ -第一个Python程序 - hello, world! -向伟大的Dennis M. Ritchie先生致敬 +第一個Python程式 - hello, world! +向偉大的Dennis M. Ritchie先生致敬 Version: 0.1 -Author: 骆昊 +Author: 駱昊 Date: 2018-02-26 """ @@ -137,17 +137,17 @@ print('hello', 'world', sep=', ', end='!') print('goodbye, world', end='!\n') ``` -### 其他工具介绍 +### 其他工具介紹 -#### IDLE - 自带的集成开发工具 +#### IDLE - 自帶的整合開發工具 -IDLE是安装Python环境时自带的集成开发工具,如下图所示。但是由于IDLE的用户体验并不是那么好所以很少在实际开发中被采用。 +IDLE是安裝Python環境時自帶的整合開發工具,如下圖所示。但是由於IDLE的使用者體驗並不是那麼好所以很少在實際開發中被採用。 ![](./res/python-idle.png) -#### IPython - 更好的交互式编程工具 +#### IPython - 更好的互動式程式設計工具 -IPython是一种基于Python的交互式解释器。相较于原生的Python Shell,IPython提供了更为强大的编辑和交互功能。可以通过Python的包管理工具pip安装IPython和Jupyter,具体的操作如下所示。 +IPython是一種基於Python的互動式直譯器。相較於原生的Python Shell,IPython提供了更為強大的編輯和互動功能。可以通過Python的包管理工具pip安裝IPython和Jupyter,具體的操作如下所示。 ```Shell pip install ipython jupyter @@ -159,11 +159,11 @@ pip install ipython jupyter python -m pip install ipython jupyter ``` -安装成功后,可以通过下面的ipython命令启动IPython,如下图所示。 +安裝成功後,可以通過下面的ipython命令啟動IPython,如下圖所示。 ![](./res/python-ipython.png) -当然我们也可以通过Jupyter运行名为notebook的项目在浏览器窗口中进行交互式操作。 +當然我們也可以通過Jupyter執行名為notebook的專案在瀏覽器視窗中進行互動式操作。 ```Shell jupyter notebook @@ -173,13 +173,13 @@ jupyter notebook ![](./res/python-jupyter-2.png) -#### Sublime - 文本编辑神器 +#### Sublime - 文字編輯神器 ![](./res/python-sublime.png) -- 首先可以通过[官方网站](https://www.sublimetext.com/)下载安装程序安装Sublime 3或Sublime 2。 +- 首先可以通過[官方網站](https://www.sublimetext.com/)下載安裝程式安裝Sublime 3或Sublime 2。 -- 安装包管理工具。通过快捷键Ctrl+`或者在View菜单中选择Show Console打开控制台,输入下面的代码。 +- 安裝包管理工具。通過快捷鍵Ctrl+`或者在View選單中選擇Show Console開啟控制檯,輸入下面的程式碼。 - Sublime 3 @@ -193,23 +193,23 @@ jupyter notebook import urllib2,os;pf='Package Control.sublime-package';ipp=sublime.installed_packages_path();os.makedirs(ipp)ifnotos.path.exists(ipp)elseNone;urllib2.install_opener(urllib2.build_opener(urllib2.ProxyHandler()));open(os.path.join(ipp,pf),'wb').write(urllib2.urlopen('http://sublime.wbond.net/'+pf.replace(' ','%20')).read());print('Please restart Sublime Text to finish installation') ``` -- 安装插件。通过Preference菜单的Package Control或快捷键Ctrl+Shift+P打开命令面板,在面板中输入Install Package就可以找到安装插件的工具,然后再查找需要的插件。我们推荐大家安装以下几个插件。 +- 安裝外掛。通過Preference選單的Package Control或快捷鍵Ctrl+Shift+P開啟命令面板,在面板中輸入Install Package就可以找到安裝外掛的工具,然後再查詢需要的外掛。我們推薦大家安裝以下幾個外掛。 - - SublimeCodeIntel - 代码自动补全工具插件 - - Emmet - 前端开发代码模板插件 - - Git - 版本控制工具插件 - - Python PEP8 Autoformat - PEP8规范自动格式化插件 - - ConvertToUTF8 - 将本地编码转换为UTF-8 + - SublimeCodeIntel - 程式碼自動補全工具外掛 + - Emmet - 前端開發程式碼模板外掛 + - Git - 版本控制工具外掛 + - Python PEP8 Autoformat - PEP8規範自動格式化外掛 + - ConvertToUTF8 - 將本地編碼轉換為UTF-8 -#### PyCharm - Python开发神器 +#### PyCharm - Python開發神器 -PyCharm的安装、配置和使用我们在后面会进行介绍。 +PyCharm的安裝、配置和使用我們在後面會進行介紹。 ![](./res/python-pycharm.png) -### 练习 +### 練習 -1. 在Python交互环境中下面的代码查看结果并将内容翻译成中文。 +1. 在Python互動環境中下面的程式碼檢視結果並將內容翻譯成中文。 ```Python import this @@ -235,7 +235,7 @@ PyCharm的安装、配置和使用我们在后面会进行介绍。 Namespaces are one honking great idea -- let's do more of those! ``` -2. 学习使用turtle在屏幕上绘制图形。 +2. 學習使用turtle在螢幕上繪製圖形。 ```Python import turtle diff --git "a/Day01-15/Day02/\350\257\255\350\250\200\345\205\203\347\264\240.md" "b/Day01-15/Day02/\350\257\255\350\250\200\345\205\203\347\264\240.md" index 87a3af640..b9d522560 100644 --- "a/Day01-15/Day02/\350\257\255\350\250\200\345\205\203\347\264\240.md" +++ "b/Day01-15/Day02/\350\257\255\350\250\200\345\205\203\347\264\240.md" @@ -1,45 +1,45 @@ -## Day02 - 语言元素 +## Day02 - 語言元素 -#### 指令和程序 +#### 指令和程式 -计算机的硬件系统通常由五大部件构成,包括:运算器、控制器、存储器、输入设备和输出设备。其中,运算器和控制器放在一起就是我们通常所说的中央处理器,它的功能是执行各种运算和控制指令以及处理计算机软件中的数据。我们通常所说的程序实际上就是指令的集合,我们程序就是将一系列的指令按照某种方式组织到一起,然后通过这些指令去控制计算机做我们想让它做的事情。今天我们使用的计算机虽然器件做工越来越精密,处理能力越来越强大,但究其本质来说仍然属于[“冯·诺依曼结构”](https://zh.wikipedia.org/wiki/%E5%86%AF%C2%B7%E8%AF%BA%E4%BC%8A%E6%9B%BC%E7%BB%93%E6%9E%84)的计算机。“冯·诺依曼结构”有两个关键点,一是提出了将存储设备与中央处理器分开,二是提出了将数据以二进制方式编码。二进制是一种“逢二进一”的计数法,跟我们人类使用的“逢十进一”的计数法没有实质性的区别,人类因为有十根手指所以使用了十进制(因为在数数时十根手指用完之后就只能进位了,当然凡事都有例外,玛雅人可能是因为长年光着脚的原因把脚趾头也算上了,于是他们使用了二十进制的计数法,在这种计数法的指导下玛雅人的历法就与我们的不太一致,而按照玛雅人的历法,2012年是上一个所谓的“太阳纪”的最后一年,而2013年则是新的“太阳纪”的开始,后来这件事情被以讹传讹的方式误传为2012年就是玛雅人预言的世界末日这种荒诞的说法,今天我们可以大胆的猜测,玛雅文明之所以发展缓慢估计也与使用了二十进制有关),对于计算机来说,二进制在物理器件上来说是最容易实现的(高电压表示1,低电压表示0),于是在“冯·诺依曼结构”的计算机都使用了二进制。虽然我们并不需要每个程序员都能够使用二进制的思维方式来工作,但是了解二进制以及它与我们生活中的十进制之间的转换关系,以及二进制与八进制和十六进制的转换关系还是有必要的。如果你对这一点不熟悉,可以自行使用[维基百科](https://zh.wikipedia.org/wiki/%E4%BA%8C%E8%BF%9B%E5%88%B6)或者[度娘](https://www.baidu.com)科普一下。 +計算機的硬體系統通常由五大部件構成,包括:運算器、控制器、儲存器、輸入裝置和輸出裝置。其中,運算器和控制器放在一起就是我們通常所說的中央處理器,它的功能是執行各種運算和控制指令以及處理計算機軟體中的資料。我們通常所說的程式實際上就是指令的集合,我們程式就是將一系列的指令按照某種方式組織到一起,然後通過這些指令去控制計算機做我們想讓它做的事情。今天我們使用的計算機雖然器件做工越來越精密,處理能力越來越強大,但究其本質來說仍然屬於[“馮·諾依曼結構”](https://zh.wikipedia.org/wiki/%E5%86%AF%C2%B7%E8%AF%BA%E4%BC%8A%E6%9B%BC%E7%BB%93%E6%9E%84)的計算機。“馮·諾依曼結構”有兩個關鍵點,一是提出了將儲存裝置與中央處理器分開,二是提出了將資料以二進位制方式編碼。二進位制是一種“逢二進一”的計數法,跟我們人類使用的“逢十進一”的計數法沒有實質性的區別,人類因為有十根手指所以使用了十進位制(因為在數數時十根手指用完之後就只能進位了,當然凡事都有例外,瑪雅人可能是因為長年光著腳的原因把腳趾頭也算上了,於是他們使用了二十進位制的計數法,在這種計數法的指導下瑪雅人的歷法就與我們的不太一致,而按照瑪雅人的歷法,2012年是上一個所謂的“太陽紀”的最後一年,而2013年則是新的“太陽紀”的開始,後來這件事情被以訛傳訛的方式誤傳為2012年就是瑪雅人預言的世界末日這種荒誕的說法,今天我們可以大膽的猜測,瑪雅文明之所以發展緩慢估計也與使用了二十進位制有關),對於計算機來說,二進位制在物理器件上來說是最容易實現的(高電壓表示1,低電壓表示0),於是在“馮·諾依曼結構”的計算機都使用了二進位制。雖然我們並不需要每個程式設計師都能夠使用二進位制的思維方式來工作,但是瞭解二進位制以及它與我們生活中的十進位制之間的轉換關係,以及二進位制與八進位制和十六進位制的轉換關係還是有必要的。如果你對這一點不熟悉,可以自行使用[維基百科](https://zh.wikipedia.org/wiki/%E4%BA%8C%E8%BF%9B%E5%88%B6)或者[度娘](https://www.baidu.com)科普一下。 -### 变量和类型 +### 變數和型別 -在程序设计中,变量是一种存储数据的载体。计算机中的变量是实际存在的数据或者说是存储器中存储数据的一块内存空间,变量的值可以被读取和修改,这是所有计算和控制的基础。计算机能处理的数据有很多中类型,除了数值之外还可以处理文本、图形、音频、视频等各种各样的数据,那么不同的数据就需要定义不同的存储类型。Python中的数据类型很多,而且也允许我们自定义新的数据类型(这一点在后面会讲到),我们先介绍几种常用的数据类型。 +在程式設計中,變數是一種儲存資料的載體。計算機中的變數是實際存在的資料或者說是儲存器中儲存資料的一塊記憶體空間,變數的值可以被讀取和修改,這是所有計算和控制的基礎。計算機能處理的資料有很多中型別,除了數值之外還可以處理文字、圖形、音訊、視訊等各種各樣的資料,那麼不同的資料就需要定義不同的儲存型別。Python中的資料型別很多,而且也允許我們自定義新的資料型別(這一點在後面會講到),我們先介紹幾種常用的資料型別。 -- 整型:Python中可以处理任意大小的整数(Python 2.x中有int和long两种类型的整数,但这种区分对Python来说意义不大,因此在Python 3.x中整数只有int这一种了),而且支持二进制(如`0b100`,换算成十进制是4)、八进制(如`0o100`,换算成十进制是64)、十进制(`100`)和十六进制(`0x100`,换算成十进制是256)的表示法。 -- 浮点型:浮点数也就是小数,之所以称为浮点数,是因为按照科学记数法表示时,一个浮点数的小数点位置是可变的,浮点数除了数学写法(如`123.456`)之外还支持科学计数法(如`1.23456e2`)。 -- 字符串型:字符串是以单引号或双引号括起来的任意文本,比如`'hello'`和`"hello"`,字符串还有原始字符串表示法、字节字符串表示法、Unicode字符串表示法,而且可以书写成多行的形式(用三个单引号或三个双引号开头,三个单引号或三个双引号结尾)。 -- 布尔型:布尔值只有`True`、`False`两种值,要么是`True`,要么是`False`,在Python中,可以直接用`True`、`False`表示布尔值(请注意大小写),也可以通过布尔运算计算出来(例如`3 < 5`会产生布尔值`True`,而`2 == 1`会产生布尔值`False`)。 -- 复数型:形如`3+5j`,跟数学上的复数表示一样,唯一不同的是虚部的i换成了j。 +- 整型:Python中可以處理任意大小的整數(Python 2.x中有int和long兩種型別的整數,但這種區分對Python來說意義不大,因此在Python 3.x中整數只有int這一種了),而且支援二進位制(如`0b100`,換算成十進位制是4)、八進位制(如`0o100`,換算成十進位制是64)、十進位制(`100`)和十六進位制(`0x100`,換算成十進位制是256)的表示法。 +- 浮點型:浮點數也就是小數,之所以稱為浮點數,是因為按照科學記數法表示時,一個浮點數的小數點位置是可變的,浮點數除了數學寫法(如`123.456`)之外還支援科學計數法(如`1.23456e2`)。 +- 字串型:字串是以單引號或雙引號括起來的任意文字,比如`'hello'`和`"hello"`,字串還有原始字串表示法、位元組字串表示法、Unicode字串表示法,而且可以書寫成多行的形式(用三個單引號或三個雙引號開頭,三個單引號或三個雙引號結尾)。 +- 布林型:布林值只有`True`、`False`兩種值,要麼是`True`,要麼是`False`,在Python中,可以直接用`True`、`False`表示布林值(請注意大小寫),也可以通過布林運算計算出來(例如`3 < 5`會產生布爾值`True`,而`2 == 1`會產生布爾值`False`)。 +- 複數型:形如`3+5j`,跟數學上的複數表示一樣,唯一不同的是虛部的i換成了j。 -#### 变量命名 +#### 變數命名 -对于每个变量我们需要给它取一个名字,就如同我们每个人都有属于自己的响亮的名字一样。在Python中,变量命名需要遵循以下这些必须遵守硬性规则和强烈建议遵守的非硬性规则。 +對於每個變數我們需要給它取一個名字,就如同我們每個人都有屬於自己的響亮的名字一樣。在Python中,變數命名需要遵循以下這些必須遵守硬性規則和強烈建議遵守的非硬性規則。 -- 硬性规则: - - 变量名由字母(广义的Unicode字符,不包括特殊字符)、数字和下划线构成,数字不能开头。 - - 大小写敏感(大写的`a`和小写的`A`是两个不同的变量)。 - - 不要跟关键字(有特殊含义的单词,后面会讲到)和系统保留字(如函数、模块等的名字)冲突。 +- 硬性規則: + - 變數名由字母(廣義的Unicode字元,不包括特殊字元)、數字和下劃線構成,數字不能開頭。 + - 大小寫敏感(大寫的`a`和小寫的`A`是兩個不同的變數)。 + - 不要跟關鍵字(有特殊含義的單詞,後面會講到)和系統保留字(如函式、模組等的名字)衝突。 - PEP 8要求: - - 用小写字母拼写,多个单词用下划线连接。 - - 受保护的实例属性用单个下划线开头(后面会讲到)。 - - 私有的实例属性用两个下划线开头(后面会讲到)。 + - 用小寫字母拼寫,多個單詞用下劃線連線。 + - 受保護的例項屬性用單個下劃線開頭(後面會講到)。 + - 私有的例項屬性用兩個下劃線開頭(後面會講到)。 -当然,作为一个专业的程序员,给变量(事实上应该是所有的标识符)命名做到见名知意也是非常重要的。 +當然,作為一個專業的程式設計師,給變數(事實上應該是所有的識別符號)命名做到見名知意也是非常重要的。 -#### 变量的使用 +#### 變數的使用 -下面通过几个例子来说明变量的类型和变量使用。 +下面通過幾個例子來說明變數的型別和變數使用。 ```Python """ -使用变量保存数据并进行算术运算 +使用變數儲存資料並進行算術運算 Version: 0.1 -Author: 骆昊 +Author: 駱昊 Date: 2018-02-27 """ @@ -59,12 +59,12 @@ print(a ** b) ```Python """ -使用input函数输入 -使用int()进行类型转换 -用占位符格式化输出的字符串 +使用input函式輸入 +使用int()進行型別轉換 +用佔位符格式化輸出的字串 Version: 0.1 -Author: 骆昊 +Author: 駱昊 Date: 2018-02-27 """ @@ -84,10 +84,10 @@ print('%d ** %d = %d' % (a, b, a ** b)) ```Python """ -使用type()检查变量的类型 +使用type()檢查變數的型別 Version: 0.1 -Author: 骆昊 +Author: 駱昊 Date: 2018-02-27 """ @@ -105,46 +105,46 @@ print(type(e)) ``` -在对变量类型进行转换时可以使用Python的内置函数(准确的说下面列出的并不是真正意义上的函数,而是后面我们要讲到的创建对象的构造方法)。 +在對變數型別進行轉換時可以使用Python的內建函式(準確的說下面列出的並不是真正意義上的函式,而是後面我們要講到的建立物件的構造方法)。 -- int():将一个数值或字符串转换成整数,可以指定进制。 -- float():将一个字符串转换成浮点数。 -- str():将指定的对象转换成字符串形式,可以指定编码。 -- chr():将整数转换成该编码对应的字符串(一个字符)。 -- ord():将字符串(一个字符)转换成对应的编码(整数)。 +- int():將一個數值或字串轉換成整數,可以指定進位制。 +- float():將一個字串轉換成浮點數。 +- str():將指定的物件轉換成字串形式,可以指定編碼。 +- chr():將整數轉換成該編碼對應的字串(一個字元)。 +- ord():將字串(一個字元)轉換成對應的編碼(整數)。 -### 运算符 +### 運算子 -Python支持多种运算符,下表大致按照优先级从高到低的顺序列出了所有的运算符,我们会陆续使用到它们。 +Python支援多種運算子,下表大致按照優先順序從高到低的順序列出了所有的運算子,我們會陸續使用到它們。 -| 运算符 | 描述 | +| 運算子 | 描述 | | ------------------------------------------------------------ | ------------------------------ | -| `[]` `[:]` | 下标,切片 | -| `**` | 指数 | -| `~` `+` `-` | 按位取反, 正负号 | +| `[]` `[:]` | 下標,切片 | +| `**` | 指數 | +| `~` `+` `-` | 按位取反, 正負號 | | `*` `/` `%` `//` | 乘,除,模,整除 | -| `+` `-` | 加,减 | +| `+` `-` | 加,減 | | `>>` `<<` | 右移,左移 | -| `&` | 按位与 | -| `^` `|` | 按位异或,按位或 | -| `<=` `<` `>` `>=` | 小于等于,小于,大于,大于等于 | -| `==` `!=` | 等于,不等于 | -| `is` `is not` | 身份运算符 | -| `in` `not in` | 成员运算符 | -| `not` `or` `and` | 逻辑运算符 | -| `=` `+=` `-=` `*=` `/=` `%=` `//=` `**=` `&=` `|=` `^=` `>>=` `<<=` | (复合)赋值运算符 | +| `&` | 按位與 | +| `^` `|` | 按位異或,按位或 | +| `<=` `<` `>` `>=` | 小於等於,小於,大於,大於等於 | +| `==` `!=` | 等於,不等於 | +| `is` `is not` | 身份運算子 | +| `in` `not in` | 成員運算子 | +| `not` `or` `and` | 邏輯運算子 | +| `=` `+=` `-=` `*=` `/=` `%=` `//=` `**=` `&=` `|=` `^=` `>>=` `<<=` | (複合)賦值運算子 | ->**说明:**在实际开发中,如果搞不清楚优先级可以使用括号来确保运算的执行顺序。 +>**說明:**在實際開發中,如果搞不清楚優先順序可以使用括號來確保運算的執行順序。 -下面的例子演示了运算符的使用。 +下面的例子演示了運算子的使用。 ```Python """ -运算符的使用 +運算子的使用 Version: 0.1 -Author: 骆昊 +Author: 駱昊 Date: 2018-02-27 """ @@ -175,66 +175,66 @@ print(flag2 is not False) ``` -### 练习 +### 練習 -#### 练习1:华氏温度转摄氏温度。 +#### 練習1:華氏溫度轉攝氏溫度。 ```Python """ -将华氏温度转换为摄氏温度 +將華氏溫度轉換為攝氏溫度 F = 1.8C + 32 Version: 0.1 -Author: 骆昊 +Author: 駱昊 Date: 2018-02-27 """ -f = float(input('请输入华氏温度: ')) +f = float(input('請輸入華氏溫度: ')) c = (f - 32) / 1.8 -print('%.1f华氏度 = %.1f摄氏度' % (f, c)) +print('%.1f華氏度 = %.1f攝氏度' % (f, c)) ``` -#### 练习2:输入圆的半径计算计算周长和面积。 +#### 練習2:輸入圓的半徑計算計算周長和麵積。 ```Python """ -输入半径计算圆的周长和面积 +輸入半徑計算圓的周長和麵積 Version: 0.1 -Author: 骆昊 +Author: 駱昊 Date: 2018-02-27 """ import math -radius = float(input('请输入圆的半径: ')) +radius = float(input('請輸入圓的半徑: ')) perimeter = 2 * math.pi * radius area = math.pi * radius * radius -print('周长: %.2f' % perimeter) -print('面积: %.2f' % area) +print('周長: %.2f' % perimeter) +print('面積: %.2f' % area) ``` -#### 练习3:输入年份判断是不是闰年。 +#### 練習3:輸入年份判斷是不是閏年。 ```Python """ -输入年份 如果是闰年输出True 否则输出False +輸入年份 如果是閏年輸出True 否則輸出False Version: 0.1 -Author: 骆昊 +Author: 駱昊 Date: 2018-02-27 """ -year = int(input('请输入年份: ')) -# 如果代码太长写成一行不便于阅读 可以使用\或()折行 +year = int(input('請輸入年份: ')) +# 如果程式碼太長寫成一行不便於閱讀 可以使用\或()折行 is_leap = (year % 4 == 0 and year % 100 != 0 or year % 400 == 0) print(is_leap) diff --git "a/Day01-15/Day03/\345\210\206\346\224\257\347\273\223\346\236\204.md" "b/Day01-15/Day03/\345\210\206\346\224\257\347\273\223\346\236\204.md" index 23da1e84d..dc4c46721 100644 --- "a/Day01-15/Day03/\345\210\206\346\224\257\347\273\223\346\236\204.md" +++ "b/Day01-15/Day03/\345\210\206\346\224\257\347\273\223\346\236\204.md" @@ -1,53 +1,53 @@ -## Day03 - 分支结构 +## Day03 - 分支結構 -### 分支结构的应用场景 +### 分支結構的應用場景 -迄今为止,我们写的Python代码都是一条一条语句顺序执行,这种结构的代码我们称之为顺序结构。然而仅有顺序结构并不能解决所有的问题,比如我们设计一个游戏,游戏第一关的通关条件是玩家获得1000分,那么在完成本局游戏后我们要根据玩家得到分数来决定究竟是进入第二关还是告诉玩家“Game Over”,这里就会产生两个分支,而且这两个分支只有一个会被执行,这就是程序中分支结构。类似的场景还有很多,给大家一分钟的时间,你应该可以想到至少5个以上这样的例子,赶紧试一试。 +迄今為止,我們寫的Python程式碼都是一條一條語句順序執行,這種結構的程式碼我們稱之為順序結構。然而僅有順序結構並不能解決所有的問題,比如我們設計一個遊戲,遊戲第一關的通關條件是玩家獲得1000分,那麼在完成本局遊戲後我們要根據玩家得到分數來決定究竟是進入第二關還是告訴玩家“Game Over”,這裡就會產生兩個分支,而且這兩個分支只有一個會被執行,這就是程式中分支結構。類似的場景還有很多,給大家一分鐘的時間,你應該可以想到至少5個以上這樣的例子,趕緊試一試。 -### if语句的使用 +### if語句的使用 -在Python中,要构造分支结构可以使用`if`、`elif`和`else`关键字。所谓关键字就是有特殊含义的单词,像`if`和`else`就是专门用于构造分支结构的关键字,很显然你不能够使用它作为变量名(事实上,用作其他的标识符也是不可以)。下面的例子中演示了如何构造一个分支结构。 +在Python中,要構造分支結構可以使用`if`、`elif`和`else`關鍵字。所謂關鍵字就是有特殊含義的單詞,像`if`和`else`就是專門用於構造分支結構的關鍵字,很顯然你不能夠使用它作為變數名(事實上,用作其他的識別符號也是不可以)。下面的例子中演示瞭如何構造一個分支結構。 ```Python """ -用户身份验证 +使用者身份驗證 Version: 0.1 -Author: 骆昊 +Author: 駱昊 Date: 2018-02-28 """ -username = input('请输入用户名: ') -password = input('请输入口令: ') -# 如果希望输入口令时 终端中没有回显 可以使用getpass模块的getpass函数 +username = input('請輸入使用者名稱: ') +password = input('請輸入口令: ') +# 如果希望輸入口令時 終端中沒有回顯 可以使用getpass模組的getpass函式 # import getpass -# password = getpass.getpass('请输入口令: ') +# password = getpass.getpass('請輸入口令: ') if username == 'admin' and password == '123456': - print('身份验证成功!') + print('身份驗證成功!') else: - print('身份验证失败!') + print('身份驗證失敗!') ``` -唯一需要说明的是和C/C++、Java等语言不同,Python中没有用花括号来构造代码块而是使用了缩进的方式来设置代码的层次结构,如果`if`条件成立的情况下需要执行多条语句,只要保持多条语句具有相同的缩进就可以了,换句话说连续的代码如果又保持了相同的缩进那么它们属于同一个代码块,相当于是一个执行的整体。 +唯一需要說明的是和C/C++、Java等語言不同,Python中沒有用花括號來構造程式碼塊而是使用了縮排的方式來設定程式碼的層次結構,如果`if`條件成立的情況下需要執行多條語句,只要保持多條語句具有相同的縮排就可以了,換句話說連續的程式碼如果又保持了相同的縮排那麼它們屬於同一個程式碼塊,相當於是一個執行的整體。 -当然如果要构造出更多的分支,可以使用`if…elif…else…`结构,例如下面的分段函数求值。 +當然如果要構造出更多的分支,可以使用`if…elif…else…`結構,例如下面的分段函式求值。 $$f(x)=\begin{cases} 3x-5&\text{(x>1)}\\x+2&\text{(-1}\leq\text{x}\leq\text{1)}\\5x+3&\text {(x<-1)}\end{cases}$$ ```Python """ -分段函数求值 +分段函式求值 3x - 5 (x > 1) f(x) = x + 2 (-1 <= x <= 1) 5x + 3 (x < -1) Version: 0.1 -Author: 骆昊 +Author: 駱昊 Date: 2018-02-28 """ @@ -63,18 +63,18 @@ print('f(%.2f) = %.2f' % (x, y)) ``` -当然根据实际开发的需要,分支结构是可以嵌套的,例如判断是否通关以后还要根据你获得的宝物或者道具的数量对你的表现给出等级(比如点亮两颗或三颗星星),那么我们就需要在`if`的内部构造出一个新的分支结构,同理`elif`和`else`中也可以再构造新的分支,我们称之为嵌套的分支结构,也就是说上面的代码也可以写成下面的样子。 +當然根據實際開發的需要,分支結構是可以巢狀的,例如判斷是否通關以後還要根據你獲得的寶物或者道具的數量對你的表現給出等級(比如點亮兩顆或三顆星星),那麼我們就需要在`if`的內部構造出一個新的分支結構,同理`elif`和`else`中也可以再構造新的分支,我們稱之為巢狀的分支結構,也就是說上面的程式碼也可以寫成下面的樣子。 ```Python """ -分段函数求值 +分段函式求值 3x - 5 (x > 1) f(x) = x + 2 (-1 <= x <= 1) 5x + 3 (x < -1) Version: 0.1 -Author: 骆昊 +Author: 駱昊 Date: 2018-02-28 """ @@ -90,43 +90,43 @@ else: print('f(%.2f) = %.2f' % (x, y)) ``` -> **说明:**大家可以自己感受一下这两种写法到底是哪一种更好。在之前我们提到的Python之禅中有这么一句话“Flat is bettern than nested.”,之所以提出这个观点是因为嵌套结构的嵌套层次多了之后会严重的影响代码的可读性,如果可以使用扁平化的结构就不要去用嵌套,因此之前的写法是更好的做法。 +> **說明:**大家可以自己感受一下這兩種寫法到底是哪一種更好。在之前我們提到的Python之禪中有這麼一句話“Flat is bettern than nested.”,之所以提出這個觀點是因為巢狀結構的巢狀層次多了之後會嚴重的影響程式碼的可讀性,如果可以使用扁平化的結構就不要去用巢狀,因此之前的寫法是更好的做法。 -### 练习 +### 練習 -#### 练习1:英制单位与公制单位互换 +#### 練習1:英制單位與公制單位互換 ```Python """ -英制单位英寸和公制单位厘米互换 +英制單位英寸和公制單位釐米互換 Version: 0.1 -Author: 骆昊 +Author: 駱昊 Date: 2018-02-28 """ -value = float(input('请输入长度: ')) -unit = input('请输入单位: ') +value = float(input('請輸入長度: ')) +unit = input('請輸入單位: ') if unit == 'in' or unit == '英寸': - print('%f英寸 = %f厘米' % (value, value * 2.54)) -elif unit == 'cm' or unit == '厘米': - print('%f厘米 = %f英寸' % (value, value / 2.54)) + print('%f英寸 = %f釐米' % (value, value * 2.54)) +elif unit == 'cm' or unit == '釐米': + print('%f釐米 = %f英寸' % (value, value / 2.54)) else: - print('请输入有效的单位') + print('請輸入有效的單位') ``` -#### 练习2:掷骰子决定做什么 +#### 練習2:擲骰子決定做什麼 ```Python """ -掷骰子决定做什么事情 +擲骰子決定做什麼事情 Version: 0.1 -Author: 骆昊 +Author: 駱昊 Date: 2018-02-28 """ @@ -137,26 +137,26 @@ face = randint(1, 6) if face == 1: result = '唱首歌' elif face == 2: - result = '跳个舞' + result = '跳個舞' elif face == 3: - result = '学狗叫' + result = '學狗叫' elif face == 4: - result = '做俯卧撑' + result = '做俯臥撐' elif face == 5: - result = '念绕口令' + result = '念繞口令' else: - result = '讲冷笑话' + result = '講冷笑話' print(result) ``` -> **说明:**上面的代码中使用了random模块的randint函数生成指定范围的随机数来模拟掷骰子。 +> **說明:**上面的程式碼中使用了random模組的randint函式生成指定範圍的隨機數來模擬擲骰子。 -#### 练习3:百分制成绩转等级制 +#### 練習3:百分制成績轉等級制 ```Python """ -百分制成绩转等级制成绩 +百分制成績轉等級製成績 90分以上 --> A 80分~89分 --> B 70分~79分 --> C @@ -164,12 +164,12 @@ print(result) 60分以下 --> E Version: 0.1 -Author: 骆昊 +Author: 駱昊 Date: 2018-02-28 """ -score = float(input('请输入成绩: ')) +score = float(input('請輸入成績: ')) if score >= 90: grade = 'A' elif score >= 80: @@ -180,19 +180,19 @@ elif score >= 60: grade = 'D' else: grade = 'E' -print('对应的等级是:', grade) +print('對應的等級是:', grade) ``` -#### 练习4:输入三条边长如果能构成三角形就计算周长和面积 +#### 練習4:輸入三條邊長如果能構成三角形就計算周長和麵積 ```Python """ -判断输入的边长能否构成三角形 -如果能则计算出三角形的周长和面积 +判斷輸入的邊長能否構成三角形 +如果能則計算出三角形的周長和麵積 Version: 0.1 -Author: 骆昊 +Author: 駱昊 Date: 2018-02-28 """ @@ -203,31 +203,31 @@ a = float(input('a = ')) b = float(input('b = ')) c = float(input('c = ')) if a + b > c and a + c > b and b + c > a: - print('周长: %f' % (a + b + c)) + print('周長: %f' % (a + b + c)) p = (a + b + c) / 2 area = math.sqrt(p * (p - a) * (p - b) * (p - c)) - print('面积: %f' % (area)) + print('面積: %f' % (area)) else: - print('不能构成三角形') + print('不能構成三角形') ``` -> **说明:**上面的代码中使用了`math`模块的`sqrt`函数来计算平方根。用边长计算三角形面积的公式叫做[海伦公式](https://zh.wikipedia.org/zh-hans/海伦公式)。 +> **說明:**上面的程式碼中使用了`math`模組的`sqrt`函式來計算平方根。用邊長計算三角形面積的公式叫做[海倫公式](https://zh.wikipedia.org/zh-hans/海倫公式)。 -#### 练习5:实现一个个人所得税计算器。 +#### 練習5:實現一個個人所得稅計算器。 ```Python """ -输入月收入和五险一金计算个人所得税 +輸入月收入和五險一金計算個人所得稅 Version: 0.1 -Author: 骆昊 +Author: 駱昊 Date: 2018-02-28 """ salary = float(input('本月收入: ')) -insurance = float(input('五险一金: ')) +insurance = float(input('五險一金: ')) diff = salary - insurance - 3500 if diff <= 0: rate = 0 @@ -254,9 +254,9 @@ else: rate = 0.45 deduction = 13505 tax = abs(diff * rate - deduction) -print('个人所得税: ¥%.2f元' % tax) -print('实际到手收入: ¥%.2f元' % (diff + 3500 - tax)) +print('個人所得稅: ¥%.2f元' % tax) +print('實際到手收入: ¥%.2f元' % (diff + 3500 - tax)) ``` ->**说明:**上面的代码中使用了Python内置的`abs()`函数取绝对值来处理`-0`的问题。 +>**說明:**上面的程式碼中使用了Python內建的`abs()`函式取絕對值來處理`-0`的問題。 diff --git "a/Day01-15/Day04/\345\276\252\347\216\257\347\273\223\346\236\204.md" "b/Day01-15/Day04/\345\276\252\347\216\257\347\273\223\346\236\204.md" index af5ab166d..e69b2baba 100644 --- "a/Day01-15/Day04/\345\276\252\347\216\257\347\273\223\346\236\204.md" +++ "b/Day01-15/Day04/\345\276\252\347\216\257\347\273\223\346\236\204.md" @@ -1,20 +1,20 @@ -## Day04 - 循环结构 +## Day04 - 迴圈結構 -### 循环结构的应用场景 +### 迴圈結構的應用場景 -如果在程序中我们需要重复的执行某条或某些指令,例如用程序控制机器人踢足球,如果机器人持球而且还没有进入射门范围,那么我们就要一直发出让机器人向球门方向奔跑的指令。当然你可能已经注意到了,刚才的描述中其实不仅仅有需要重复的动作,还有我们上一个章节讲到的分支结构。再举一个简单的例子,比如在我们的程序中要实现每隔1秒中在屏幕上打印一个"hello, world"这样的字符串并持续一个小时,我们肯定不能够将`print('hello, world')`这句代码写上3600遍,如果真的需要这样做那么我们的工作就太无聊了。因此,我们需要循环结构,使用循环结构我们就可以轻松的控制某件事或者某些事重复、重复、再重复的发生。在Python中构造循环结构有两种做法,一种是`for-in`循环,一种是`while`循环。 +如果在程式中我們需要重複的執行某條或某些指令,例如用程式控制機器人踢足球,如果機器人持球而且還沒有進入射門範圍,那麼我們就要一直髮出讓機器人向球門方向奔跑的指令。當然你可能已經注意到了,剛才的描述中其實不僅僅有需要重複的動作,還有我們上一個章節講到的分支結構。再舉一個簡單的例子,比如在我們的程式中要實現每隔1秒中在螢幕上列印一個"hello, world"這樣的字串並持續一個小時,我們肯定不能夠將`print('hello, world')`這句程式碼寫上3600遍,如果真的需要這樣做那麼我們的工作就太無聊了。因此,我們需要迴圈結構,使用迴圈結構我們就可以輕鬆的控制某件事或者某些事重複、重複、再重複的發生。在Python中構造迴圈結構有兩種做法,一種是`for-in`迴圈,一種是`while`迴圈。 -### for-in循环 +### for-in迴圈 -如果明确的知道循环执行的次数或者是要对一个容器进行迭代(后面会讲到),那么我们推荐使用`for-in`循环,例如下面代码中计算$\sum_{n=1}^{100}n$。 +如果明確的知道迴圈執行的次數或者是要對一個容器進行迭代(後面會講到),那麼我們推薦使用`for-in`迴圈,例如下面程式碼中計算$\sum_{n=1}^{100}n$。 ```Python """ -用for循环实现1~100求和 +用for迴圈實現1~100求和 Version: 0.1 -Author: 骆昊 +Author: 駱昊 Date: 2018-03-01 """ @@ -25,21 +25,21 @@ for x in range(101): print(sum) ``` -需要说明的是上面代码中的`range`类型,`range`可以用来产生一个不变的数值序列,而且这个序列通常都是用在循环中的,例如: +需要說明的是上面程式碼中的`range`型別,`range`可以用來產生一個不變的數值序列,而且這個序列通常都是用在迴圈中的,例如: -- `range(101)`可以产生一个0到100的整数序列。 -- `range(1, 100)`可以产生一个1到99的整数序列。 -- `range(1, 100, 2)`可以产生一个1到99的奇数序列,其中的2是步长,即数值序列的增量。 +- `range(101)`可以產生一個0到100的整數序列。 +- `range(1, 100)`可以產生一個1到99的整數序列。 +- `range(1, 100, 2)`可以產生一個1到99的奇數序列,其中的2是步長,即數值序列的增量。 -知道了这一点,我们可以用下面的代码来实现1~100之间的偶数求和。 +知道了這一點,我們可以用下面的程式碼來實現1~100之間的偶數求和。 ```Python """ -用for循环实现1~100之间的偶数求和 +用for迴圈實現1~100之間的偶數求和 Version: 0.1 -Author: 骆昊 +Author: 駱昊 Date: 2018-03-01 """ @@ -50,15 +50,15 @@ for x in range(2, 101, 2): print(sum) ``` -也可以通过在循环中使用分支结构的方式来实现相同的功能,代码如下所示。 +也可以通過在迴圈中使用分支結構的方式來實現相同的功能,程式碼如下所示。 ```Python """ -用for循环实现1~100之间的偶数求和 +用for迴圈實現1~100之間的偶數求和 Version: 0.1 -Author: 骆昊 +Author: 駱昊 Date: 2018-03-01 """ @@ -71,19 +71,19 @@ print(sum) ``` -### while循环 +### while迴圈 -如果要构造不知道具体循环次数的循环结构,我们推荐使用`while`循环,`while`循环通过一个能够产生或转换出`bool`值的表达式来控制循环,表达式的值为`True`循环继续,表达式的值为`False`循环结束。下面我们通过一个“猜数字”的小游戏(计算机出一个1~100之间的随机数,人输入自己猜的数字,计算机给出对应的提示信息,直到人猜出计算机出的数字)来看看如何使用`while`循环。 +如果要構造不知道具體迴圈次數的迴圈結構,我們推薦使用`while`迴圈,`while`迴圈通過一個能夠產生或轉換出`bool`值的表示式來控制迴圈,表示式的值為`True`迴圈繼續,表示式的值為`False`迴圈結束。下面我們通過一個“猜數字”的小遊戲(計算機出一個1~100之間的隨機數,人輸入自己猜的數字,計算機給出對應的提示資訊,直到人猜出計算機出的數字)來看看如何使用`while`迴圈。 ```Python """ -猜数字游戏 -计算机出一个1~100之间的随机数由人来猜 -计算机根据人猜的数字分别给出提示大一点/小一点/猜对了 +猜數字遊戲 +計算機出一個1~100之間的隨機數由人來猜 +計算機根據人猜的數字分別給出提示大一點/小一點/猜對了 Version: 0.1 -Author: 骆昊 +Author: 駱昊 Date: 2018-03-01 """ @@ -94,31 +94,31 @@ answer = random.randint(1, 100) counter = 0 while True: counter += 1 - number = int(input('请输入: ')) + number = int(input('請輸入: ')) if number < answer: - print('大一点') + print('大一點') elif number > answer: - print('小一点') + print('小一點') else: - print('恭喜你猜对了!') + print('恭喜你猜對了!') break -print('你总共猜了%d次' % counter) +print('你總共猜了%d次' % counter) if counter > 7: - print('你的智商余额明显不足') + print('你的智商餘額明顯不足') ``` -> **说明:**上面的代码中使用了`break`关键字来提前终止循环,需要注意的是`break`只能终止它所在的那个循环,这一点在使用嵌套的循环结构(下面会讲到)需要引起注意。除了`break`之外,还有另一个关键字是`continue`,它可以用来放弃本次循环后续的代码直接让循环进入下一轮。 +> **說明:**上面的程式碼中使用了`break`關鍵字來提前終止迴圈,需要注意的是`break`只能終止它所在的那個迴圈,這一點在使用巢狀的迴圈結構(下面會講到)需要引起注意。除了`break`之外,還有另一個關鍵字是`continue`,它可以用來放棄本次迴圈後續的程式碼直接讓迴圈進入下一輪。 -和分支结构一样,循环结构也是可以嵌套的,也就是说在循环中还可以构造循环结构。下面的例子演示了如何通过嵌套的循环来输出一个九九乘法表。 +和分支結構一樣,迴圈結構也是可以巢狀的,也就是說在迴圈中還可以構造迴圈結構。下面的例子演示瞭如何通過巢狀的迴圈來輸出一個九九乘法表。 ```Python """ -输出乘法口诀表(九九表) +輸出乘法口訣表(九九表) Version: 0.1 -Author: 骆昊 +Author: 駱昊 Date: 2018-03-01 """ @@ -130,24 +130,24 @@ for i in range(1, 10): ``` -### 练习 +### 練習 -#### 练习1:输入一个数判断是不是素数。 +#### 練習1:輸入一個數判斷是不是素數。 ```Python """ -输入一个正整数判断它是不是素数 +輸入一個正整數判斷它是不是素數 Version: 0.1 -Author: 骆昊 +Author: 駱昊 Date: 2018-03-01 """ from math import sqrt -num = int(input('请输入一个正整数: ')) +num = int(input('請輸入一個正整數: ')) end = int(sqrt(num)) is_prime = True for x in range(2, end + 1): @@ -155,21 +155,21 @@ for x in range(2, end + 1): is_prime = False break if is_prime and num != 1: - print('%d是素数' % num) + print('%d是素數' % num) else: - print('%d不是素数' % num) + print('%d不是素數' % num) ``` -#### 练习2:输入两个正整数,计算最大公约数和最小公倍数。 +#### 練習2:輸入兩個正整數,計算最大公約數和最小公倍數。 ```Python """ -输入两个正整数计算最大公约数和最小公倍数 +輸入兩個正整數計算最大公約數和最小公倍數 Version: 0.1 -Author: 骆昊 +Author: 駱昊 Date: 2018-03-01 """ @@ -180,18 +180,18 @@ if x > y: (x, y) = (y, x) for factor in range(x, 0, -1): if x % factor == 0 and y % factor == 0: - print('%d和%d的最大公约数是%d' % (x, y, factor)) - print('%d和%d的最小公倍数是%d' % (x, y, x * y // factor)) + print('%d和%d的最大公約數是%d' % (x, y, factor)) + print('%d和%d的最小公倍數是%d' % (x, y, x * y // factor)) break ``` -#### 练习3:打印三角形图案。 +#### 練習3:列印三角形圖案。 ```Python """ -打印各种三角形图案 +列印各種三角形圖案 * ** @@ -212,12 +212,12 @@ for factor in range(x, 0, -1): ********* Version: 0.1 -Author: 骆昊 +Author: 駱昊 Date: 2018-03-01 """ -row = int(input('请输入行数: ')) +row = int(input('請輸入行數: ')) for i in range(row): for _ in range(i + 1): print('*', end='') diff --git "a/Day01-15/Day05/\346\200\273\347\273\223\345\222\214\347\273\203\344\271\240.md" "b/Day01-15/Day05/\346\200\273\347\273\223\345\222\214\347\273\203\344\271\240.md" index e8f7ba89c..6069dc9db 100644 --- "a/Day01-15/Day05/\346\200\273\347\273\223\345\222\214\347\273\203\344\271\240.md" +++ "b/Day01-15/Day05/\346\200\273\347\273\223\345\222\214\347\273\203\344\271\240.md" @@ -1 +1 @@ -## 总结和练习 +## 總結和練習 diff --git "a/Day01-15/Day06/\345\207\275\346\225\260\345\222\214\346\250\241\345\235\227\347\232\204\344\275\277\347\224\250.md" "b/Day01-15/Day06/\345\207\275\346\225\260\345\222\214\346\250\241\345\235\227\347\232\204\344\275\277\347\224\250.md" index a3550de63..177ad0239 100644 --- "a/Day01-15/Day06/\345\207\275\346\225\260\345\222\214\346\250\241\345\235\227\347\232\204\344\275\277\347\224\250.md" +++ "b/Day01-15/Day06/\345\207\275\346\225\260\345\222\214\346\250\241\345\235\227\347\232\204\344\275\277\347\224\250.md" @@ -1,19 +1,19 @@ -## 函数和模块的使用 +## 函式和模組的使用 -在讲解本章节的内容之前,我们先来研究一道数学题,请说出下面的方程有多少组正整数解。 +在講解本章節的內容之前,我們先來研究一道數學題,請說出下面的方程有多少組正整數解。 $$x_1 + x_2 + x_3 + x_4 = 8$$ -事实上,上面的问题等同于将8个苹果分成四组每组至少一个苹果有多少种方案。想到这一点问题的答案就呼之欲出了。 +事實上,上面的問題等同於將8個蘋果分成四組每組至少一個蘋果有多少種方案。想到這一點問題的答案就呼之欲出了。 $$C_M^N =\frac{M!}{N!(M-N)!}, \text{(M=7, N=3)} $$ -可以用Python的程序来计算出这个值,代码如下所示。 +可以用Python的程式來計算出這個值,程式碼如下所示。 ```Python """ -输入M和N计算C(M,N) +輸入M和N計算C(M,N) """ @@ -32,24 +32,24 @@ print(fm // fn // fmn) ``` -### 函数的作用 +### 函式的作用 -不知道大家是否注意到,在上面的代码中,我们做了3次求阶乘,这样的代码实际上就是重复代码。编程大师Martin Fowler先生曾经说过:“代码有很多种坏味道,重复是最坏的一种!”,要写出高质量的代码首先要解决的就是重复代码的问题。对于上面的代码来说,我们可以将计算阶乘的功能封装到一个称之为“函数”的功能模块中,在需要计算阶乘的地方,我们只需要“调用”这个“函数”就可以了。 +不知道大家是否注意到,在上面的程式碼中,我們做了3次求階乘,這樣的程式碼實際上就是重複程式碼。程式設計大師Martin Fowler先生曾經說過:“程式碼有很多種壞味道,重複是最壞的一種!”,要寫出高質量的程式碼首先要解決的就是重複程式碼的問題。對於上面的程式碼來說,我們可以將計算階乘的功能封裝到一個稱之為“函式”的功能模組中,在需要計算階乘的地方,我們只需要“呼叫”這個“函式”就可以了。 -### 定义函数 +### 定義函式 -在Python中可以使用`def`关键字来定义函数,和变量一样每个函数也有一个响亮的名字,而且命名规则跟变量的命名规则是一致的。在函数名后面的圆括号中可以放置传递给函数的参数,这一点和数学上的函数非常相似,程序中函数的参数就相当于是数学上说的函数的自变量,而函数执行完成后我们可以通过`return`关键字来返回一个值,这相当于数学上说的函数的因变量。 +在Python中可以使用`def`關鍵字來定義函式,和變數一樣每個函式也有一個響亮的名字,而且命名規則跟變數的命名規則是一致的。在函式名後面的圓括號中可以放置傳遞給函式的引數,這一點和數學上的函式非常相似,程式中函式的引數就相當於是數學上說的函式的自變數,而函式執行完成後我們可以通過`return`關鍵字來返回一個值,這相當於數學上說的函式的因變數。 -在了解了如何定义函数后,我们可以对上面的代码进行重构,所谓重构就是在不影响代码执行结果的前提下对代码的结构进行调整,重构之后的代码如下所示。 +在瞭解瞭如何定義函式後,我們可以對上面的程式碼進行重構,所謂重構就是在不影響程式碼執行結果的前提下對程式碼的結構進行調整,重構之後的程式碼如下所示。 ```Python def factorial(num): """ - 求阶乘 + 求階乘 - :param num: 非负整数 + :param num: 非負整數 - :return: num的阶乘 + :return: num的階乘 """ result = 1 for n in range(1, num + 1): @@ -59,17 +59,17 @@ def factorial(num): m = int(input('m = ')) n = int(input('n = ')) -# 当需要计算阶乘的时候不用再写循环求阶乘而是直接调用已经定义好的函数 +# 當需要計算階乘的時候不用再寫迴圈求階乘而是直接呼叫已經定義好的函式 print(factorial(m) // factorial(n) // factorial(m - n)) ``` -> **说明:**Python的math模块中其实已经有一个factorial函数了,事实上要计算阶乘可以直接使用这个现成的函数而不用自己定义。下面例子中的某些函数其实Python中也是内置了,我们这里是为了讲解函数的定义和使用才把它们又实现了一遍,实际开发中不建议做这种低级的重复性的工作。 +> **說明:**Python的math模組中其實已經有一個factorial函數了,事實上要計算階乘可以直接使用這個現成的函式而不用自己定義。下面例子中的某些函式其實Python中也是內建了,我們這裡是為了講解函式的定義和使用才把它們又實現了一遍,實際開發中不建議做這種低階的重複性的工作。 -### 函数的参数 +### 函式的引數 -函数是绝大多数编程语言中都支持的一个代码的“构建块”,但是Python中的函数与其他语言中的函数还是有很多不太相同的地方,其中一个显著的区别就是Python对函数参数的处理。在Python中,函数的参数可以有默认值,也支持使用可变参数,所以Python并不需要像其他语言一样支持[函数的重载](https://zh.wikipedia.org/wiki/%E5%87%BD%E6%95%B0%E9%87%8D%E8%BD%BD),因为我们在定义一个函数的时候可以让它有多种不同的使用方式,下面是两个小例子。 +函式是絕大多數程式語言中都支援的一個程式碼的“構建塊”,但是Python中的函式與其他語言中的函式還是有很多不太相同的地方,其中一個顯著的區別就是Python對函式引數的處理。在Python中,函式的引數可以有預設值,也支援使用可變引數,所以Python並不需要像其他語言一樣支援[函式的過載](https://zh.wikipedia.org/wiki/%E5%87%BD%E6%95%B0%E9%87%8D%E8%BD%BD),因為我們在定義一個函式的時候可以讓它有多種不同的使用方式,下面是兩個小例子。 ```Python from random import randint @@ -77,11 +77,11 @@ from random import randint def roll_dice(n=2): """ - 摇色子 + 搖色子 - :param n: 色子的个数 + :param n: 色子的個數 - :return: n颗色子点数之和 + :return: n顆色子點數之和 """ total = 0 for _ in range(n): @@ -93,26 +93,26 @@ def add(a=0, b=0, c=0): return a + b + c -# 如果没有指定参数那么使用默认值摇两颗色子 +# 如果沒有指定引數那麼使用預設值搖兩顆色子 print(roll_dice()) -# 摇三颗色子 +# 搖三顆色子 print(roll_dice(3)) print(add()) print(add(1)) print(add(1, 2)) print(add(1, 2, 3)) -# 传递参数时可以不按照设定的顺序进行传递 +# 傳遞引數時可以不按照設定的順序進行傳遞 print(add(c=50, a=100, b=200)) ``` -我们给上面两个函数的参数都设定了默认值,这也就意味着如果在调用函数的时候如果没有传入对应参数的值时将使用该参数的默认值,所以在上面的代码中我们可以用各种不同的方式去调用`add`函数,这跟其他很多语言中函数重载的效果是一致的。 +我們給上面兩個函式的引數都設定了預設值,這也就意味著如果在呼叫函式的時候如果沒有傳入對應引數的值時將使用該引數的預設值,所以在上面的程式碼中我們可以用各種不同的方式去呼叫`add`函式,這跟其他很多語言中函式過載的效果是一致的。 -其实上面的`add`函数还有更好的实现方案,因为我们可能会对0个或多个参数进行加法运算,而具体有多少个参数是由调用者来决定,我们作为函数的设计者对这一点是一无所知的,因此在不确定参数个数的时候,我们可以使用可变参数,代码如下所示。 +其實上面的`add`函式還有更好的實現方案,因為我們可能會對0個或多個引數進行加法運算,而具體有多少個引數是由呼叫者來決定,我們作為函式的設計者對這一點是一無所知的,因此在不確定引數個數的時候,我們可以使用可變引數,程式碼如下所示。 ```Python -# 在参数前使用*表示args是可变参数 -# 也就是说调用add函数时传入的参数个数可以是0个或多个 +# 在引數前使用*表示args是可變引數 +# 也就是說呼叫add函式時傳入的引數個數可以是0個或多個 def add(*args): total = 0 for val in args: @@ -128,9 +128,9 @@ print(add(1, 3, 5, 7, 9)) ``` -### 用模块管理函数 +### 用模組管理函式 -对于任何一种编程语言来说,给变量、函数这样的标识符起名字都是一个让人头疼的问题,因为我们会遇到命名冲突这种尴尬的情况。最简单的场景就是在同一个.py文件中定义了两个同名函数,由于Python没有函数重载的概念,那么后面的定义会覆盖之前的定义,也就意味着两个函数同名函数实际上只有一个是存在的。 +對於任何一種程式語言來說,給變數、函式這樣的識別符號起名字都是一個讓人頭疼的問題,因為我們會遇到命名衝突這種尷尬的情況。最簡單的場景就是在同一個.py檔案中定義了兩個同名函式,由於Python沒有函式過載的概念,那麼後面的定義會覆蓋之前的定義,也就意味著兩個函式同名函式實際上只有一個是存在的。 ```Python def foo(): @@ -141,11 +141,11 @@ def foo(): print('goodbye, world!') -foo() # 输出goodbye, world! +foo() # 輸出goodbye, world! ``` -当然上面的这种情况我们很容易就能避免,但是如果项目是由多人协作进行团队开发的时候,团队中可能有多个程序员都定义了名为`foo`的函数,那么怎么解决这种命名冲突呢?答案其实很简单,Python中每个文件就代表了一个模块(module),我们在不同的模块中可以有同名的函数,在使用函数的时候我们通过`import`关键字导入指定的模块就可以区分到底要使用的是哪个模块中的`foo`函数,代码如下所示。 +當然上面的這種情況我們很容易就能避免,但是如果專案是由多人協作進行團隊開發的時候,團隊中可能有多個程式設計師都定義了名為`foo`的函式,那麼怎麼解決這種命名衝突呢?答案其實很簡單,Python中每個檔案就代表了一個模組(module),我們在不同的模組中可以有同名的函式,在使用函式的時候我們通過`import`關鍵字匯入指定的模組就可以區分到底要使用的是哪個模組中的`foo`函式,程式碼如下所示。 module1.py @@ -166,15 +166,15 @@ test.py ```Python from module1 import foo -foo() # 输出hello, world! +foo() # 輸出hello, world! from module2 import foo -foo() # 输出goodbye, world! +foo() # 輸出goodbye, world! ``` -也可以按照如下所示的方式来区分到底要使用哪一个`foo`函数。 +也可以按照如下所示的方式來區分到底要使用哪一個`foo`函式。 test.py @@ -187,7 +187,7 @@ m2.foo() ``` -但是如果将代码写成了下面的样子,那么程序中调用的是最后导入的那个`foo`,因为后导入的foo覆盖了之前导入的`foo`。 +但是如果將程式碼寫成了下面的樣子,那麼程式中呼叫的是最後匯入的那個`foo`,因為後匯入的foo覆蓋了之前匯入的`foo`。 test.py @@ -195,7 +195,7 @@ test.py from module1 import foo from module2 import foo -foo() # 输出goodbye, world! +foo() # 輸出goodbye, world! ``` @@ -205,11 +205,11 @@ test.py from module2 import foo from module1 import foo -foo() # 输出hello, world! +foo() # 輸出hello, world! ``` -需要说明的是,如果我们导入的模块除了定义函数之外还中有可以执行代码,那么Python解释器在导入这个模块时就会执行这些代码,事实上我们可能并不希望如此,因此如果我们在模块中编写了执行代码,最好是将这些执行代码放入如下所示的条件中,这样的话除非直接运行该模块,if条件下的这些代码是不会执行的,因为只有直接执行的模块的名字才是“\_\_main\_\_”。 +需要說明的是,如果我們匯入的模組除了定義函式之外還中有可以執行程式碼,那麼Python直譯器在匯入這個模組時就會執行這些程式碼,事實上我們可能並不希望如此,因此如果我們在模組中編寫了執行程式碼,最好是將這些執行程式碼放入如下所示的條件中,這樣的話除非直接執行該模組,if條件下的這些程式碼是不會執行的,因為只有直接執行的模組的名字才是“\_\_main\_\_”。 module3.py @@ -222,8 +222,8 @@ def bar(): pass -# __name__是Python中一个隐含的变量它代表了模块的名字 -# 只有被Python解释器直接执行的模块的名字才是__main__ +# __name__是Python中一個隱含的變數它代表了模組的名字 +# 只有被Python直譯器直接執行的模組的名字才是__main__ if __name__ == '__main__': print('call foo()') foo() @@ -237,13 +237,13 @@ test.py ```Python import module3 -# 导入module3时 不会执行模块中if条件成立时的代码 因为模块的名字是module3而不是__main__ +# 匯入module3時 不會執行模組中if條件成立時的程式碼 因為模組的名字是module3而不是__main__ ``` -### 练习 +### 練習 -#### 练习1:实现计算求最大公约数和最小公倍数的函数。 +#### 練習1:實現計算求最大公約數和最小公倍數的函式。 ```Python def gcd(x, y): @@ -257,7 +257,7 @@ def lcm(x, y): return x * y // gcd(x, y) ``` -#### 练习2:实现判断一个数是不是回文数的函数。 +#### 練習2:實現判斷一個數是不是迴文數的函式。 ```Python def is_palindrome(num): @@ -269,7 +269,7 @@ def is_palindrome(num): return total == num ``` -#### 练习3:实现判断一个数是不是素数的函数。 +#### 練習3:實現判斷一個數是不是素數的函式。 ```Python def is_prime(num): @@ -279,25 +279,25 @@ def is_prime(num): return True if num != 1 else False ``` -#### 练习4:写一个程序判断输入的正整数是不是回文素数。 +#### 練習4:寫一個程式判斷輸入的正整數是不是迴文素數。 ```Python if __name__ == '__main__': - num = int(input('请输入正整数: ')) + num = int(input('請輸入正整數: ')) if is_palindrome(num) and is_prime(num): - print('%d是回文素数' % num) + print('%d是迴文素數' % num) ``` -通过上面的程序可以看出,当我们将代码中重复出现的和相对独立的功能抽取成函数后,我们可以组合使用这些函数来解决更为复杂的问题,这也是我们为什么要定义和使用函数的一个非常重要的原因。 +通過上面的程式可以看出,當我們將程式碼中重複出現的和相對獨立的功能抽取成函式後,我們可以組合使用這些函式來解決更為複雜的問題,這也是我們為什麼要定義和使用函式的一個非常重要的原因。 -最后,我们来讨论一下Python中有关变量作用域的问题。 +最後,我們來討論一下Python中有關變數作用域的問題。 ```Python def foo(): b = 'hello' - def bar(): # Python中可以在函数内部再定义函数 + def bar(): # Python中可以在函式內部再定義函式 c = True print(a) print(b) @@ -314,9 +314,9 @@ if __name__ == '__main__': ``` -上面的代码能够顺利的执行并且打印出100和“hello”,但我们注意到了,在`bar`函数的内部并没有定义`a`和`b`两个变量,那么`a`和`b`是从哪里来的。我们在上面代码的`if`分支中定义了一个变量`a`,这是一个全局变量(global variable),属于全局作用域,因为它没有定义在任何一个函数中。在上面的`foo`函数中我们定义了变量`b`,这是一个定义在函数中的局部变量(local variable),属于局部作用域,在`foo`函数的外部并不能访问到它;但对于`foo`函数内部的`bar`函数来说,变量`b`属于嵌套作用域,在`bar`函数中我们是可以访问到它的。`bar`函数中的变量`c`属于局部作用域,在`bar`函数之外是无法访问的。事实上,Python查找一个变量时会按照“局部作用域”、“嵌套作用域”、“全局作用域”和“内置作用域”的顺序进行搜索,前三者我们在上面的代码中已经看到了,所谓的“内置作用域”就是Python内置的那些隐含标识符`min`、`len`等都属于内置作用域)。 +上面的程式碼能夠順利的執行並且打印出100和“hello”,但我們注意到了,在`bar`函式的內部並沒有定義`a`和`b`兩個變數,那麼`a`和`b`是從哪裡來的。我們在上面程式碼的`if`分支中定義了一個變數`a`,這是一個全域性變數(global variable),屬於全域性作用域,因為它沒有定義在任何一個函式中。在上面的`foo`函式中我們定義了變數`b`,這是一個定義在函式中的區域性變數(local variable),屬於區域性作用域,在`foo`函式的外部並不能訪問到它;但對於`foo`函式內部的`bar`函式來說,變數`b`屬於巢狀作用域,在`bar`函式中我們是可以訪問到它的。`bar`函式中的變數`c`屬於區域性作用域,在`bar`函式之外是無法訪問的。事實上,Python查詢一個變數時會按照“區域性作用域”、“巢狀作用域”、“全域性作用域”和“內建作用域”的順序進行搜尋,前三者我們在上面的程式碼中已經看到了,所謂的“內建作用域”就是Python內建的那些隱含識別符號`min`、`len`等都屬於內建作用域)。 -再看看下面这段代码,我们希望通过函数调用修改全局变量`a`的值,但实际上下面的代码是做不到的。 +再看看下面這段程式碼,我們希望通過函式呼叫修改全域性變數`a`的值,但實際上下面的程式碼是做不到的。 ```Python def foo(): @@ -331,7 +331,7 @@ if __name__ == '__main__': ``` -在调用`foo`函数后,我们发现`a`的值仍然是100,这是因为当我们在函数`foo`中写`a = 200`的时候,是重新定义了一个名字为`a`的局部变量,它跟全局作用域的`a`并不是同一个变量,因为局部作用域中有了自己的变量`a`,因此`foo`函数不再搜索全局作用域中的`a`。如果我们希望在`foo`函数中修改全局作用域中的`a`,代码如下所示。 +在呼叫`foo`函式後,我們發現`a`的值仍然是100,這是因為當我們在函式`foo`中寫`a = 200`的時候,是重新定義了一個名字為`a`的區域性變數,它跟全域性作用域的`a`並不是同一個變數,因為區域性作用域中有了自己的變數`a`,因此`foo`函式不再搜尋全域性作用域中的`a`。如果我們希望在`foo`函式中修改全域性作用域中的`a`,程式碼如下所示。 ```Python def foo(): @@ -347,13 +347,13 @@ if __name__ == '__main__': ``` -我们可以使用`global`关键字来指示`foo`函数中的变量`a`来自于全局作用域,如果全局作用域中没有`a`,那么下面一行的代码就会定义变量`a`并将其置于全局作用域。同理,如果我们希望函数内部的函数能够修改嵌套作用域中的变量,可以使用`nonlocal`关键字来指示变量来自于嵌套作用域,请大家自行试验。 +我們可以使用`global`關鍵字來指示`foo`函式中的變數`a`來自於全域性作用域,如果全域性作用域中沒有`a`,那麼下面一行的程式碼就會定義變數`a`並將其置於全域性作用域。同理,如果我們希望函式內部的函式能夠修改巢狀作用域中的變數,可以使用`nonlocal`關鍵字來指示變數來自於巢狀作用域,請大家自行試驗。 -在实际开发中,我们应该尽量减少对全局变量的使用,因为全局变量的作用域和影响过于广泛,可能会发生意料之外的修改和使用,除此之外全局变量比局部变量拥有更长的生命周期,可能导致对象占用的内存长时间无法被[垃圾回收](https://zh.wikipedia.org/wiki/%E5%9E%83%E5%9C%BE%E5%9B%9E%E6%94%B6_(%E8%A8%88%E7%AE%97%E6%A9%9F%E7%A7%91%E5%AD%B8))。事实上,减少对全局变量的使用,也是降低代码之间耦合度的一个重要举措,同时也是对[迪米特法则](https://zh.wikipedia.org/zh-hans/%E5%BE%97%E5%A2%A8%E5%BF%92%E8%80%B3%E5%AE%9A%E5%BE%8B)的践行。减少全局变量的使用就意味着我们应该尽量让变量的作用域在函数的内部,但是如果我们希望将一个局部变量的生命周期延长,使其在函数调用结束后依然可以访问,这时候就需要使用[闭包](https://zh.wikipedia.org/wiki/%E9%97%AD%E5%8C%85_(%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%A7%91%E5%AD%A6)),这个我们在后续的内容中进行讲解。 +在實際開發中,我們應該儘量減少對全域性變數的使用,因為全域性變數的作用域和影響過於廣泛,可能會發生意料之外的修改和使用,除此之外全域性變數比區域性變數擁有更長的生命週期,可能導致物件佔用的記憶體長時間無法被[垃圾回收](https://zh.wikipedia.org/wiki/%E5%9E%83%E5%9C%BE%E5%9B%9E%E6%94%B6_(%E8%A8%88%E7%AE%97%E6%A9%9F%E7%A7%91%E5%AD%B8))。事實上,減少對全域性變數的使用,也是降低程式碼之間耦合度的一個重要舉措,同時也是對[迪米特法則](https://zh.wikipedia.org/zh-hans/%E5%BE%97%E5%A2%A8%E5%BF%92%E8%80%B3%E5%AE%9A%E5%BE%8B)的踐行。減少全域性變數的使用就意味著我們應該儘量讓變數的作用域在函式的內部,但是如果我們希望將一個區域性變數的生命週期延長,使其在函式呼叫結束後依然可以訪問,這時候就需要使用[閉包](https://zh.wikipedia.org/wiki/%E9%97%AD%E5%8C%85_(%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%A7%91%E5%AD%A6)),這個我們在後續的內容中進行講解。 -> **说明**:很多人经常会将“闭包”一词和[“匿名函数”](https://zh.wikipedia.org/wiki/%E5%8C%BF%E5%90%8D%E5%87%BD%E6%95%B0)混为一谈,但实际上它们是不同的概念,如果想提前了解这个概念,推荐看看[维基百科](https://zh.wikipedia.org/wiki/)或者[知乎](https://www.zhihu.com/)上对这个概念的讨论。 +> **說明**:很多人經常會將“閉包”一詞和[“匿名函式”](https://zh.wikipedia.org/wiki/%E5%8C%BF%E5%90%8D%E5%87%BD%E6%95%B0)混為一談,但實際上它們是不同的概念,如果想提前瞭解這個概念,推薦看看[維基百科](https://zh.wikipedia.org/wiki/)或者[知乎](https://www.zhihu.com/)上對這個概念的討論。 -说了那么多,其实结论很简单,从现在开始我们可以将Python代码按照下面的格式进行书写,这一点点的改进其实就是在我们理解了函数和作用域的基础上跨出的巨大的一步。 +說了那麼多,其實結論很簡單,從現在開始我們可以將Python程式碼按照下面的格式進行書寫,這一點點的改進其實就是在我們理解了函式和作用域的基礎上跨出的巨大的一步。 ```Python def main(): diff --git "a/Day01-15/Day07/\345\255\227\347\254\246\344\270\262\345\222\214\345\270\270\347\224\250\346\225\260\346\215\256\347\273\223\346\236\204.md" "b/Day01-15/Day07/\345\255\227\347\254\246\344\270\262\345\222\214\345\270\270\347\224\250\346\225\260\346\215\256\347\273\223\346\236\204.md" index d1e176a83..ffd5ba36b 100644 --- "a/Day01-15/Day07/\345\255\227\347\254\246\344\270\262\345\222\214\345\270\270\347\224\250\346\225\260\346\215\256\347\273\223\346\236\204.md" +++ "b/Day01-15/Day07/\345\255\227\347\254\246\344\270\262\345\222\214\345\270\270\347\224\250\346\225\260\346\215\256\347\273\223\346\236\204.md" @@ -1,54 +1,54 @@ -## 字符串和常用数据结构 +## 字串和常用資料結構 -### 使用字符串 +### 使用字串 -第二次世界大战促使了现代电子计算机的诞生,当初的想法很简单,就是用计算机来计算导弹的弹道,因此在计算机刚刚诞生的那个年代,计算机处理的信息主要是数值,而世界上的第一台电子计算机ENIAC每秒钟能够完成约5000次浮点运算。随着时间的推移,虽然对数值运算仍然是计算机日常工作中最为重要的事情之一,但是今天的计算机处理得更多的数据都是以文本信息的方式存在的,而Python表示文本信息的方式我们在很早以前就说过了,那就是字符串类型。所谓**字符串**,就是由零个或多个字符组成的有限序列,一般记为[$${\displaystyle s=a_{1}a_{2}\dots a_{n}(0\leq n \leq \infty)}$$](https://wikimedia.org/api/rest_v1/media/math/render/svg/e29bf631b090323edd6889f810e6cff29538b161)。 +第二次世界大戰促使了現代電子計算機的誕生,當初的想法很簡單,就是用計算機來計算導彈的彈道,因此在計算機剛剛誕生的那個年代,計算機處理的資訊主要是數值,而世界上的第一臺電子計算機ENIAC每秒鐘能夠完成約5000次浮點運算。隨著時間的推移,雖然對數值運算仍然是計算機日常工作中最為重要的事情之一,但是今天的計算機處理得更多的資料都是以文字資訊的方式存在的,而Python表示文字資訊的方式我們在很早以前就說過了,那就是字串型別。所謂**字串**,就是由零個或多個字元組成的有限序列,一般記為[$${\displaystyle s=a_{1}a_{2}\dots a_{n}(0\leq n \leq \infty)}$$](https://wikimedia.org/api/rest_v1/media/math/render/svg/e29bf631b090323edd6889f810e6cff29538b161)。 -我们可以通过下面的代码来了解字符串的使用。 +我們可以通過下面的程式碼來了解字串的使用。 ```Python def main(): str1 = 'hello, world!' - # 通过len函数计算字符串的长度 + # 通過len函式計算字串的長度 print(len(str1)) # 13 - # 获得字符串首字母大写的拷贝 + # 獲得字串首字母大寫的拷貝 print(str1.capitalize()) # Hello, world! - # 获得字符串变大写后的拷贝 + # 獲得字串變大寫後的拷貝 print(str1.upper()) # HELLO, WORLD! - # 从字符串中查找子串所在位置 + # 從字串中查詢子串所在位置 print(str1.find('or')) # 8 print(str1.find('shit')) # -1 - # 与find类似但找不到子串时会引发异常 + # 與find類似但找不到子串時會引發異常 # print(str1.index('or')) # print(str1.index('shit')) - # 检查字符串是否以指定的字符串开头 + # 檢查字串是否以指定的字串開頭 print(str1.startswith('He')) # False print(str1.startswith('hel')) # True - # 检查字符串是否以指定的字符串结尾 + # 檢查字串是否以指定的字串結尾 print(str1.endswith('!')) # True - # 将字符串以指定的宽度居中并在两侧填充指定的字符 + # 將字串以指定的寬度居中並在兩側填充指定的字元 print(str1.center(50, '*')) - # 将字符串以指定的宽度靠右放置左侧填充指定的字符 + # 將字串以指定的寬度靠右放置左側填充指定的字元 print(str1.rjust(50, ' ')) str2 = 'abc123456' - # 从字符串中取出指定位置的字符(下标运算) + # 從字串中取出指定位置的字元(下標運算) print(str2[2]) # c - # 字符串切片(从指定的开始索引到指定的结束索引) + # 字串切片(從指定的開始索引到指定的結束索引) print(str2[2:5]) # c12 print(str2[2:]) # c123456 print(str2[2::2]) # c246 print(str2[::2]) # ac246 print(str2[::-1]) # 654321cba print(str2[-3:-1]) # 45 - # 检查字符串是否由数字构成 + # 檢查字串是否由數字構成 print(str2.isdigit()) # False - # 检查字符串是否以字母构成 + # 檢查字串是否以字母構成 print(str2.isalpha()) # False - # 检查字符串是否以数字和字母构成 + # 檢查字串是否以數字和字母構成 print(str2.isalnum()) # True str3 = ' jackfrued@126.com ' print(str3) - # 获得字符串修剪左右两侧空格的拷贝 + # 獲得字串修剪左右兩側空格的拷貝 print(str3.strip()) @@ -57,11 +57,11 @@ if __name__ == '__main__': ``` -除了字符串,Python还内置了多种类型的数据结构,如果要在程序中保存和操作数据,绝大多数时候可以利用现有的数据结构来实现,最常用的包括列表、元组、集合和字典。 +除了字串,Python還內建了多種型別的資料結構,如果要在程式中儲存和操作資料,絕大多數時候可以利用現有的資料結構來實現,最常用的包括列表、元組、集合和字典。 ### 使用列表 -下面的代码演示了如何定义列表、使用下标访问列表元素以及添加和删除元素的操作。 +下面的程式碼演示瞭如何定義列表、使用下標訪問列表元素以及新增和刪除元素的操作。 ```Python def main(): @@ -69,9 +69,9 @@ def main(): print(list1) list2 = ['hello'] * 5 print(list2) - # 计算列表长度(元素个数) + # 計算列表長度(元素個數) print(len(list1)) - # 下标(索引)运算 + # 下標(索引)運算 print(list1[0]) print(list1[4]) # print(list1[5]) # IndexError: list index out of range @@ -79,13 +79,13 @@ def main(): print(list1[-3]) list1[2] = 300 print(list1) - # 添加元素 + # 新增元素 list1.append(200) list1.insert(1, 400) list1 += [1000, 2000] print(list1) print(len(list1)) - # 删除元素 + # 刪除元素 list1.remove(3) if 1234 in list1: list1.remove(1234) @@ -101,26 +101,26 @@ if __name__ == '__main__': ``` -和字符串一样,列表也可以做切片操作,通过切片操作我们可以实现对列表的复制或者将列表中的一部分取出来创建出新的列表,代码如下所示。 +和字串一樣,列表也可以做切片操作,通過切片操作我們可以實現對列表的複製或者將列表中的一部分取出來創建出新的列表,程式碼如下所示。 ```Python def main(): fruits = ['grape', 'apple', 'strawberry', 'waxberry'] fruits += ['pitaya', 'pear', 'mango'] - # 循环遍历列表元素 + # 迴圈遍歷列表元素 for fruit in fruits: print(fruit.title(), end=' ') print() # 列表切片 fruits2 = fruits[1:4] print(fruits2) - # fruit3 = fruits # 没有复制列表只创建了新的引用 - # 可以通过完整切片操作来复制列表 + # fruit3 = fruits # 沒有複製列表只建立了新的引用 + # 可以通過完整切片操作來複制列表 fruits3 = fruits[:] print(fruits3) fruits4 = fruits[-3:-1] print(fruits4) - # 可以通过反向切片操作来获得倒转后的列表的拷贝 + # 可以通過反向切片操作來獲得倒轉後的列表的拷貝 fruits5 = fruits[::-1] print(fruits5) @@ -130,22 +130,22 @@ if __name__ == '__main__': ``` -下面的代码实现了对列表的排序操作。 +下面的程式碼實現了對列表的排序操作。 ```Python def main(): list1 = ['orange', 'apple', 'zoo', 'internationalization', 'blueberry'] list2 = sorted(list1) - # sorted函数返回列表排序后的拷贝不会修改传入的列表 - # 函数的设计就应该像sorted函数一样尽可能不产生副作用 + # sorted函式返回列表排序後的拷貝不會修改傳入的列表 + # 函式的設計就應該像sorted函式一樣儘可能不產生副作用 list3 = sorted(list1, reverse=True) - # 通过key关键字参数指定根据字符串长度进行排序而不是默认的字母表顺序 + # 通過key關鍵字引數指定根據字串長度進行排序而不是預設的字母表順序 list4 = sorted(list1, key=len) print(list1) print(list2) print(list3) print(list4) - # 给列表对象发出排序消息直接在列表对象上进行排序 + # 給列表物件發出排序訊息直接在列表物件上進行排序 list1.sort(reverse=True) print(list1) @@ -155,7 +155,7 @@ if __name__ == '__main__': ``` -我们还可以使用列表的生成式语法来创建列表,代码如下所示。 +我們還可以使用列表的生成式語法來建立列表,程式碼如下所示。 ```Python import sys @@ -166,16 +166,16 @@ def main(): print(f) f = [x + y for x in 'ABCDE' for y in '1234567'] print(f) - # 用列表的生成表达式语法创建列表容器 - # 用这种语法创建列表之后元素已经准备就绪所以需要耗费较多的内存空间 + # 用列表的生成表示式語法建立列表容器 + # 用這種語法建立列表之後元素已經準備就緒所以需要耗費較多的記憶體空間 f = [x ** 2 for x in range(1, 1000)] - print(sys.getsizeof(f)) # 查看对象占用内存的字节数 + print(sys.getsizeof(f)) # 檢視物件佔用記憶體的位元組數 print(f) - # 请注意下面的代码创建的不是一个列表而是一个生成器对象 - # 通过生成器可以获取到数据但它不占用额外的空间存储数据 - # 每次需要数据的时候就通过内部的运算得到数据(需要花费额外的时间) + # 請注意下面的程式碼建立的不是一個列表而是一個生成器物件 + # 通過生成器可以獲取到資料但它不佔用額外的空間儲存資料 + # 每次需要資料的時候就通過內部的運算得到資料(需要花費額外的時間) f = (x ** 2 for x in range(1, 1000)) - print(sys.getsizeof(f)) # 相比生成式生成器不占用存储数据的空间 + print(sys.getsizeof(f)) # 相比生成式生成器不佔用儲存資料的空間 print(f) for val in f: print(val) @@ -186,7 +186,7 @@ if __name__ == '__main__': ``` -除了上面提到的生成器语法,Python中还有另外一种定义生成器的方式,就是通过`yield`关键字将一个普通函数改造成生成器函数。下面的代码演示了如何实现一个生成[斐波拉切数列](https://zh.wikipedia.org/wiki/%E6%96%90%E6%B3%A2%E9%82%A3%E5%A5%91%E6%95%B0%E5%88%97)的生成器。所谓斐波拉切数列可以通过下面[递归](https://zh.wikipedia.org/wiki/%E9%80%92%E5%BD%92)的方法来进行定义: +除了上面提到的生成器語法,Python中還有另外一種定義生成器的方式,就是通過`yield`關鍵字將一個普通函式改造成生成器函式。下面的程式碼演示瞭如何實現一個生成[斐波拉切數列](https://zh.wikipedia.org/wiki/%E6%96%90%E6%B3%A2%E9%82%A3%E5%A5%91%E6%95%B0%E5%88%97)的生成器。所謂斐波拉切數列可以通過下面[遞迴](https://zh.wikipedia.org/wiki/%E9%80%92%E5%BD%92)的方法來進行定義: $${\displaystyle F_{0}=0}$$ @@ -214,34 +214,34 @@ if __name__ == '__main__': ``` -### 使用元组 +### 使用元組 -Python 的元组与列表类似,不同之处在于元组的元素不能修改,在前面的代码中我们已经不止一次使用过元组了。顾名思义,我们把多个元素组合到一起就形成了一个元组,所以它和列表一样可以保存多条数据。下面的代码演示了如何定义和使用元组。 +Python 的元組與列表類似,不同之處在於元組的元素不能修改,在前面的程式碼中我們已經不止一次使用過元組了。顧名思義,我們把多個元素組合到一起就形成了一個元組,所以它和列表一樣可以儲存多條資料。下面的程式碼演示瞭如何定義和使用元組。 ```Python def main(): - # 定义元组 - t = ('骆昊', 38, True, '四川成都') + # 定義元組 + t = ('駱昊', 38, True, '四川成都') print(t) - # 获取元组中的元素 + # 獲取元組中的元素 print(t[0]) print(t[3]) - # 遍历元组中的值 + # 遍歷元組中的值 for member in t: print(member) - # 重新给元组赋值 - # t[0] = '王大锤' # TypeError - # 变量t重新引用了新的元组原来的元组将被垃圾回收 - t = ('王大锤', 20, True, '云南昆明') + # 重新給元組賦值 + # t[0] = '王大錘' # TypeError + # 變數t重新引用了新的元組原來的元組將被垃圾回收 + t = ('王大錘', 20, True, '雲南昆明') print(t) - # 将元组转换成列表 + # 將元組轉換成列表 person = list(t) print(person) # 列表是可以修改它的元素的 - person[0] = '李小龙' + person[0] = '李小龍' person[1] = 25 print(person) - # 将列表转换成元组 + # 將列表轉換成元組 fruits_list = ['apple', 'banana', 'orange'] fruits_tuple = tuple(fruits_list) print(fruits_tuple) @@ -251,16 +251,16 @@ if __name__ == '__main__': main() ``` -这里有一个非常值得探讨的问题,我们已经有了列表这种数据结构,为什么还需要元组这样的类型呢? +這裡有一個非常值得探討的問題,我們已經有了列表這種資料結構,為什麼還需要元組這樣的型別呢? -1. 元组中的元素是无法修改的,事实上我们在项目中尤其是[多线程](https://zh.wikipedia.org/zh-hans/%E5%A4%9A%E7%BA%BF%E7%A8%8B)环境(后面会讲到)中可能更喜欢使用的是那些不变对象(一方面因为对象状态不能修改,所以可以避免由此引起的不必要的程序错误,简单的说就是一个不变的对象要比可变的对象更加容易维护;另一方面因为没有任何一个线程能够修改不变对象的内部状态,一个不变对象自动就是线程安全的,这样就可以省掉处理同步化的开销。一个不变对象可以方便的被共享访问)。所以结论就是:如果不需要对元素进行添加、删除、修改的时候,可以考虑使用元组,当然如果一个方法要返回多个值,使用元组也是不错的选择。 -2. 元组在创建时间和占用的空间上面都优于列表。我们可以使用sys模块的getsizeof函数来检查存储同样的元素的元组和列表各自占用了多少内存空间,这个很容易做到。我们也可以在ipython中使用魔法指令%timeit来分析创建同样内容的元组和列表所花费的时间,下图是我的macOS系统上测试的结果。 +1. 元組中的元素是無法修改的,事實上我們在專案中尤其是[多執行緒](https://zh.wikipedia.org/zh-hans/%E5%A4%9A%E7%BA%BF%E7%A8%8B)環境(後面會講到)中可能更喜歡使用的是那些不變物件(一方面因為物件狀態不能修改,所以可以避免由此引起的不必要的程式錯誤,簡單的說就是一個不變的物件要比可變的物件更加容易維護;另一方面因為沒有任何一個執行緒能夠修改不變物件的內部狀態,一個不變物件自動就是執行緒安全的,這樣就可以省掉處理同步化的開銷。一個不變物件可以方便的被共享訪問)。所以結論就是:如果不需要對元素進行新增、刪除、修改的時候,可以考慮使用元組,當然如果一個方法要返回多個值,使用元組也是不錯的選擇。 +2. 元組在建立時間和佔用的空間上面都優於列表。我們可以使用sys模組的getsizeof函式來檢查儲存同樣的元素的元組和列表各自佔用了多少記憶體空間,這個很容易做到。我們也可以在ipython中使用魔法指令%timeit來分析建立同樣內容的元組和列表所花費的時間,下圖是我的macOS系統上測試的結果。 ![](./res/ipython-timeit.png) ### 使用集合 -Python中的集合跟数学上的集合是一致的,不允许有重复元素,而且可以进行交集、并集、差集等运算。 +Python中的集合跟數學上的集合是一致的,不允許有重複元素,而且可以進行交集、並集、差集等運算。 ![](./res/python-set.png) @@ -277,19 +277,19 @@ def main(): print(set1) print(set2) set2.discard(5) - # remove的元素如果不存在会引发KeyError + # remove的元素如果不存在會引發KeyError if 4 in set2: set2.remove(4) print(set2) - # 遍历集合容器 + # 遍歷集合容器 for elem in set2: print(elem ** 2, end=' ') print() - # 将元组转换成集合 + # 將元組轉換成集合 set3 = set((1, 2, 3, 3, 2, 1)) print(set3.pop()) print(set3) - # 集合的交集、并集、差集、对称差运算 + # 集合的交集、並集、差集、對稱差運算 print(set1 & set2) # print(set1.intersection(set2)) print(set1 | set2) @@ -298,7 +298,7 @@ def main(): # print(set1.difference(set2)) print(set1 ^ set2) # print(set1.symmetric_difference(set2)) - # 判断子集和超集 + # 判斷子集和超集 print(set2 <= set1) # print(set2.issubset(set1)) print(set3 <= set1) @@ -314,35 +314,35 @@ if __name__ == '__main__': ``` -> **说明**:Python中允许通过一些特殊的方法来为某种类型或数据结构自定义运算符(后面的章节中会讲到),上面的代码中我们对集合进行运算的时候可以调用集合对象的方法,也可以直接使用对应的运算符,例如`&`运算符跟intersection方法的作用就是一样的,但是使用运算符让代码更加直观。 +> **說明**:Python中允許通過一些特殊的方法來為某種型別或資料結構自定義運算子(後面的章節中會講到),上面的程式碼中我們對集合進行運算的時候可以呼叫集合物件的方法,也可以直接使用對應的運算子,例如`&`運算子跟intersection方法的作用就是一樣的,但是使用運算子讓程式碼更加直觀。 ### 使用字典 -字典是另一种可变容器模型,类似于我们生活中使用的字典,它可以存储任意类型对象,与列表、集合不同的是,字典的每个元素都是由一个键和一个值组成的“键值对”,键和值通过冒号分开。下面的代码演示了如何定义和使用字典。 +字典是另一種可變容器模型,類似於我們生活中使用的字典,它可以儲存任意型別物件,與列表、集合不同的是,字典的每個元素都是由一個鍵和一個值組成的“鍵值對”,鍵和值通過冒號分開。下面的程式碼演示瞭如何定義和使用字典。 ```Python def main(): - scores = {'骆昊': 95, '白元芳': 78, '狄仁杰': 82} - # 通过键可以获取字典中对应的值 - print(scores['骆昊']) - print(scores['狄仁杰']) - # 对字典进行遍历(遍历的其实是键再通过键取对应的值) + scores = {'駱昊': 95, '白元芳': 78, '狄仁傑': 82} + # 通過鍵可以獲取字典中對應的值 + print(scores['駱昊']) + print(scores['狄仁傑']) + # 對字典進行遍歷(遍歷的其實是鍵再通過鍵取對應的值) for elem in scores: print('%s\t--->\t%d' % (elem, scores[elem])) # 更新字典中的元素 scores['白元芳'] = 65 - scores['诸葛王朗'] = 71 - scores.update(冷面=67, 方启鹤=85) + scores['諸葛王朗'] = 71 + scores.update(冷麵=67, 方啟鶴=85) print(scores) - if '武则天' in scores: - print(scores['武则天']) - print(scores.get('武则天')) - # get方法也是通过键获取对应的值但是可以设置默认值 - print(scores.get('武则天', 60)) - # 删除字典中的元素 + if '武則天' in scores: + print(scores['武則天']) + print(scores.get('武則天')) + # get方法也是通過鍵獲取對應的值但是可以設定預設值 + print(scores.get('武則天', 60)) + # 刪除字典中的元素 print(scores.popitem()) print(scores.popitem()) - print(scores.pop('骆昊', 100)) + print(scores.pop('駱昊', 100)) # 清空字典 scores.clear() print(scores) @@ -353,9 +353,9 @@ if __name__ == '__main__': ``` -### 练习 +### 練習 -#### 练习1:在屏幕上显示跑马灯文字 +#### 練習1:在螢幕上顯示跑馬燈文字 ```Python import os @@ -363,9 +363,9 @@ import time def main(): - content = '北京欢迎你为你开天辟地…………' + content = '北京歡迎你為你開天闢地…………' while True: - # 清理屏幕上的输出 + # 清理螢幕上的輸出 os.system('cls') # os.system('clear') print(content) # 休眠200毫秒 @@ -378,7 +378,7 @@ if __name__ == '__main__': ``` -#### 练习2:设计一个函数产生指定长度的验证码,验证码由大小写字母和数字构成。 +#### 練習2:設計一個函式產生指定長度的驗證碼,驗證碼由大小寫字母和數字構成。 ```Python import random @@ -386,11 +386,11 @@ import random def generate_code(code_len=4): """ - 生成指定长度的验证码 + 生成指定長度的驗證碼 - :param code_len: 验证码的长度(默认4个字符) + :param code_len: 驗證碼的長度(預設4個字元) - :return: 由大小写英文字母和数字构成的随机验证码 + :return: 由大小寫英文字母和數字構成的隨機驗證碼 """ all_chars = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ' last_pos = len(all_chars) - 1 @@ -401,17 +401,17 @@ def generate_code(code_len=4): return code ``` -#### 练习3:设计一个函数返回给定文件名的后缀名。 +#### 練習3:設計一個函式返回給定檔名的字尾名。 ```Python def get_suffix(filename, has_dot=False): """ - 获取文件名的后缀名 + 獲取檔名的字尾名 - :param filename: 文件名 - :param has_dot: 返回的后缀名是否需要带点 + :param filename: 檔名 + :param has_dot: 返回的字尾名是否需要帶點 - :return: 文件的后缀名 + :return: 檔案的字尾名 """ pos = filename.rfind('.') if 0 < pos < len(filename) - 1: @@ -422,7 +422,7 @@ def get_suffix(filename, has_dot=False): ``` -#### 练习4:设计一个函数返回传入的列表中最大和第二大的元素的值。 +#### 練習4:設計一個函式返回傳入的列表中最大和第二大的元素的值。 ```Python def max2(x): @@ -436,29 +436,29 @@ def max2(x): return m1, m2 ``` -#### 练习5:计算指定的年月日是这一年的第几天 +#### 練習5:計算指定的年月日是這一年的第幾天 ```Python def is_leap_year(year): """ - 判断指定的年份是不是闰年 + 判斷指定的年份是不是閏年 :param year: 年份 - :return: 闰年返回True平年返回False + :return: 閏年返回True平年返回False """ return year % 4 == 0 and year % 100 != 0 or year % 400 == 0 def which_day(year, month, date): """ - 计算传入的日期是这一年的第几天 + 計算傳入的日期是這一年的第幾天 :param year: 年 :param month: 月 :param date: 日 - :return: 第几天 + :return: 第幾天 """ days_of_month = [ [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31], @@ -482,7 +482,7 @@ if __name__ == '__main__': ``` -#### 练习6:打印[杨辉三角](https://zh.wikipedia.org/wiki/%E6%9D%A8%E8%BE%89%E4%B8%89%E8%A7%92%E5%BD%A2)。 +#### 練習6:列印[楊輝三角](https://zh.wikipedia.org/wiki/%E6%9D%A8%E8%BE%89%E4%B8%89%E8%A7%92%E5%BD%A2)。 ```Python def main(): @@ -504,9 +504,9 @@ if __name__ == '__main__': ``` -### 综合案例 +### 綜合案例 -#### 案例1:双色球选号 +#### 案例1:雙色球選號 ```Python from random import randrange, randint, sample @@ -514,7 +514,7 @@ from random import randrange, randint, sample def display(balls): """ - 输出列表中的双色球号码 + 輸出列表中的雙色球號碼 """ for index, ball in enumerate(balls): if index == len(balls) - 1: @@ -525,7 +525,7 @@ def display(balls): def random_select(): """ - 随机选择一组号码 + 隨機選擇一組號碼 """ red_balls = [x for x in range(1, 34)] selected_balls = [] @@ -533,8 +533,8 @@ def random_select(): index = randrange(len(red_balls)) selected_balls.append(red_balls[index]) del red_balls[index] - # 上面的for循环也可以写成下面这行代码 - # sample函数是random模块下的函数 + # 上面的for迴圈也可以寫成下面這行程式碼 + # sample函式是random模組下的函式 # selected_balls = sample(red_balls, 6) selected_balls.sort() selected_balls.append(randint(1, 16)) @@ -542,7 +542,7 @@ def random_select(): def main(): - n = int(input('机选几注: ')) + n = int(input('機選幾注: ')) for _ in range(n): display(random_select()) @@ -552,15 +552,15 @@ if __name__ == '__main__': ``` -> **说明**:可以使用random模块的sample函数来实现从列表中选择不重复的n个元素。 +> **說明**:可以使用random模組的sample函式來實現從列表中選擇不重複的n個元素。 -#### 综合案例2:[约瑟夫环问题](https://zh.wikipedia.org/wiki/%E7%BA%A6%E7%91%9F%E5%A4%AB%E6%96%AF%E9%97%AE%E9%A2%98) +#### 綜合案例2:[約瑟夫環問題](https://zh.wikipedia.org/wiki/%E7%BA%A6%E7%91%9F%E5%A4%AB%E6%96%AF%E9%97%AE%E9%A2%98) ```Python """ -《幸运的基督徒》 -有15个基督徒和15个非基督徒在海上遇险,为了能让一部分人活下来不得不将其中15个人扔到海里面去,有个人想了个办法就是大家围成一个圈,由某个人开始从1报数,报到9的人就扔到海里面,他后面的人接着从1开始报数,报到9的人继续扔到海里面,直到扔掉15个人。由于上帝的保佑,15个基督徒都幸免于难,问这些人最开始是怎么站的,哪些位置是基督徒哪些位置是非基督徒。 +《幸運的基督徒》 +有15個基督徒和15個非基督徒在海上遇險,為了能讓一部分人活下來不得不將其中15個人扔到海里面去,有個人想了個辦法就是大家圍成一個圈,由某個人開始從1報數,報到9的人就扔到海里面,他後面的人接著從1開始報數,報到9的人繼續扔到海里面,直到扔掉15個人。由於上帝的保佑,15個基督徒都倖免於難,問這些人最開始是怎麼站的,哪些位置是基督徒哪些位置是非基督徒。 """ @@ -586,7 +586,7 @@ if __name__ == '__main__': ``` -#### 综合案例3:[井字棋](https://zh.wikipedia.org/wiki/%E4%BA%95%E5%AD%97%E6%A3%8B)游戏 +#### 綜合案例3:[井字棋](https://zh.wikipedia.org/wiki/%E4%BA%95%E5%AD%97%E6%A3%8B)遊戲 ```Python import os @@ -615,7 +615,7 @@ def main(): os.system('clear') print_board(curr_board) while counter < 9: - move = input('轮到%s走棋, 请输入位置: ' % turn) + move = input('輪到%s走棋, 請輸入位置: ' % turn) if curr_board[move] == ' ': counter += 1 curr_board[move] = turn @@ -634,4 +634,4 @@ if __name__ == '__main__': ``` ->**说明**:最后这个案例来自[《Python编程快速上手:让繁琐工作自动化》](https://item.jd.com/11943853.html)一书(这本书对有编程基础想迅速使用Python将日常工作自动化的人来说还是不错的教材),对代码做了一点点的调整。 \ No newline at end of file +>**說明**:最後這個案例來自[《Python程式設計快速上手:讓繁瑣工作自動化》](https://item.jd.com/11943853.html)一書(這本書對有程式設計基礎想迅速使用Python將日常工作自動化的人來說還是不錯的教材),對程式碼做了一點點的調整。 \ No newline at end of file diff --git "a/Day01-15/Day08/\351\235\242\345\220\221\345\257\271\350\261\241\347\274\226\347\250\213\345\237\272\347\241\200.md" "b/Day01-15/Day08/\351\235\242\345\220\221\345\257\271\350\261\241\347\274\226\347\250\213\345\237\272\347\241\200.md" index 880db4514..5155021dc 100644 --- "a/Day01-15/Day08/\351\235\242\345\220\221\345\257\271\350\261\241\347\274\226\347\250\213\345\237\272\347\241\200.md" +++ "b/Day01-15/Day08/\351\235\242\345\220\221\345\257\271\350\261\241\347\274\226\347\250\213\345\237\272\347\241\200.md" @@ -1,67 +1,67 @@ -## 面向对象编程基础 +## 面向物件程式設計基礎 -活在当下的程序员应该都听过“面向对象编程”一词,也经常有人问能不能用一句话解释下什么是“面向对象编程”,我们先来看看比较正式的说法。 +活在當下的程式設計師應該都聽過“面向物件程式設計”一詞,也經常有人問能不能用一句話解釋下什麼是“面向物件程式設計”,我們先來看看比較正式的說法。 -> 把一组数据结构和处理它们的方法组成对象(object),把相同行为的对象归纳为类(class),通过类的封装(encapsulation)隐藏内部细节,通过继承(inheritance)实现类的特化(specialization)和泛化(generalization),通过多态(polymorphism)实现基于对象类型的动态分派。 +> 把一組資料結構和處理它們的方法組成物件(object),把相同行為的物件歸納為類(class),通過類的封裝(encapsulation)隱藏內部細節,通過繼承(inheritance)實現類的特化(specialization)和泛化(generalization),通過多型(polymorphism)實現基於物件型別的動態分派。 -这样一说是不是更不明白了。所以我们还是看看更通俗易懂的说法,下面这段内容来自于[知乎](https://www.zhihu.com/)。 +這樣一說是不是更不明白了。所以我們還是看看更通俗易懂的說法,下面這段內容來自於[知乎](https://www.zhihu.com/)。 ![](./res/oop-zhihu.png) -> **说明**:以上的内容来自于网络,不代表作者本人的观点和看法,与作者本人立场无关,相关责任不由作者承担。(终于有机会享受一下把这段话反过来说的乐趣了,乐得牙都快碎了。) +> **說明**:以上的內容來自於網路,不代表作者本人的觀點和看法,與作者本人立場無關,相關責任不由作者承擔。(終於有機會享受一下把這段話反過來說的樂趣了,樂得牙都快碎了。) -之前我们说过“程序是指令的集合”,我们在程序中书写的语句在执行时会变成一条或多条指令然后由CPU去执行。当然为了简化程序的设计,我们引入了函数的概念,把相对独立且经常重复使用的代码放置到函数中,在需要使用这些功能的时候只要调用函数即可;如果一个函数的功能过于复杂和臃肿,我们又可以进一步将函数继续切分为子函数来降低系统的复杂性。但是说了这么多,不知道大家是否发现,所谓编程就是程序员按照计算机的工作方式控制计算机完成各种任务。但是,计算机的工作方式与正常人类的思维模式是不同的,如果编程就必须得抛弃人类正常的思维方式去迎合计算机,编程的乐趣就少了很多,“每个人都应该学习编程”这样的豪言壮语就只能说说而已。当然,这些还不是最重要的,最重要的是当我们需要开发一个复杂的系统时,代码的复杂性会让开发和维护工作都变得举步维艰,所以在上世纪60年代末期,“[软件危机](https://zh.wikipedia.org/wiki/%E8%BD%AF%E4%BB%B6%E5%8D%B1%E6%9C%BA)”、“[软件工程](https://zh.wikipedia.org/wiki/%E8%BD%AF%E4%BB%B6%E5%B7%A5%E7%A8%8B)”等一系列的概念开始在行业中出现。 +之前我們說過“程式是指令的集合”,我們在程式中書寫的語句在執行時會變成一條或多條指令然後由CPU去執行。當然為了簡化程式的設計,我們引入了函式的概念,把相對獨立且經常重複使用的程式碼放置到函式中,在需要使用這些功能的時候只要呼叫函式即可;如果一個函式的功能過於複雜和臃腫,我們又可以進一步將函式繼續切分為子函式來降低系統的複雜性。但是說了這麼多,不知道大家是否發現,所謂程式設計就是程式設計師按照計算機的工作方式控制計算機完成各種任務。但是,計算機的工作方式與正常人類的思維模式是不同的,如果程式設計就必須得拋棄人類正常的思維方式去迎合計算機,程式設計的樂趣就少了很多,“每個人都應該學習程式設計”這樣的豪言壯語就只能說說而已。當然,這些還不是最重要的,最重要的是當我們需要開發一個複雜的系統時,程式碼的複雜性會讓開發和維護工作都變得舉步維艱,所以在上世紀60年代末期,“[軟體危機](https://zh.wikipedia.org/wiki/%E8%BD%AF%E4%BB%B6%E5%8D%B1%E6%9C%BA)”、“[軟體工程](https://zh.wikipedia.org/wiki/%E8%BD%AF%E4%BB%B6%E5%B7%A5%E7%A8%8B)”等一系列的概念開始在行業中出現。 -当然,程序员圈子内的人都知道,现实中并没有解决上面所说的这些问题的“[银弹](https://zh.wikipedia.org/wiki/%E6%B2%A1%E6%9C%89%E9%93%B6%E5%BC%B9)”,真正让软件开发者看到希望的是上世纪70年代诞生的[Smalltalk](https://zh.wikipedia.org/wiki/Smalltalk)编程语言中引入的面向对象的编程思想(面向对象编程的雏形可以追溯到更早期的[Simula](https://zh.wikipedia.org/wiki/Simula)语言)。按照这种编程理念,程序中的数据和操作数据的函数是一个逻辑上的整体,我们称之为“对象”,而我们解决问题的方式就是创建出需要的对象并向对象发出各种各样的消息,多个对象的协同工作最终可以让我们构造出复杂的系统来解决现实中的问题。 +當然,程式設計師圈子內的人都知道,現實中並沒有解決上面所說的這些問題的“[銀彈](https://zh.wikipedia.org/wiki/%E6%B2%A1%E6%9C%89%E9%93%B6%E5%BC%B9)”,真正讓軟體開發者看到希望的是上世紀70年代誕生的[Smalltalk](https://zh.wikipedia.org/wiki/Smalltalk)程式語言中引入的面向物件的程式設計思想(面向物件程式設計的雛形可以追溯到更早期的[Simula](https://zh.wikipedia.org/wiki/Simula)語言)。按照這種程式設計理念,程式中的資料和操作資料的函式是一個邏輯上的整體,我們稱之為“物件”,而我們解決問題的方式就是創建出需要的物件並向物件發出各種各樣的訊息,多個物件的協同工作最終可以讓我們構造出複雜的系統來解決現實中的問題。 -> **说明**:当然面向对象也不是解决软件开发中所有问题的最后的“银弹”,所以今天的高级程序设计语言几乎都提供了对多种编程范式的支持,Python也不例外。 +> **說明**:當然面向物件也不是解決軟體開發中所有問題的最後的“銀彈”,所以今天的高階程式設計語言幾乎都提供了對多種程式設計正規化的支援,Python也不例外。 -### 类和对象 +### 類和物件 -简单的说,类是对象的蓝图和模板,而对象是类的实例。这个解释虽然有点像用概念在解释概念,但是从这句话我们至少可以看出,类是抽象的概念,而对象是具体的东西。在面向对象编程的世界中,一切皆为对象,对象都有属性和行为,每个对象都是独一无二的,而且对象一定属于某个类(型)。当我们把一大堆拥有共同特征的对象的静态特征(属性)和动态特征(行为)都抽取出来后,就可以定义出一个叫做“类”的东西。 +簡單的說,類是物件的藍圖和模板,而物件是類的例項。這個解釋雖然有點像用概念在解釋概念,但是從這句話我們至少可以看出,類是抽象的概念,而物件是具體的東西。在面向物件程式設計的世界中,一切皆為物件,物件都有屬性和行為,每個物件都是獨一無二的,而且物件一定屬於某個類(型)。當我們把一大堆擁有共同特徵的物件的靜態特徵(屬性)和動態特徵(行為)都抽取出來後,就可以定義出一個叫做“類”的東西。 ![](./res/object-feature.png) -### 定义类 +### 定義類 -在Python中可以使用`class`关键字定义类,然后在类中通过之前学习过的函数来定义方法,这样就可以将对象的动态特征描述出来,代码如下所示。 +在Python中可以使用`class`關鍵字定義類,然後在類中通過之前學習過的函式來定義方法,這樣就可以將物件的動態特徵描述出來,程式碼如下所示。 ```Python class Student(object): - # __init__是一个特殊方法用于在创建对象时进行初始化操作 - # 通过这个方法我们可以为学生对象绑定name和age两个属性 + # __init__是一個特殊方法用於在建立物件時進行初始化操作 + # 通過這個方法我們可以為學生物件繫結name和age兩個屬性 def __init__(self, name, age): self.name = name self.age = age def study(self, course_name): - print('%s正在学习%s.' % (self.name, course_name)) + print('%s正在學習%s.' % (self.name, course_name)) - # PEP 8要求标识符的名字用全小写多个单词用下划线连接 - # 但是很多程序员和公司更倾向于使用驼峰命名法(驼峰标识) + # PEP 8要求識別符號的名字用全小寫多個單詞用下劃線連線 + # 但是很多程式設計師和公司更傾向於使用駝峰命名法(駝峰標識) def watch_av(self): if self.age < 18: - print('%s只能观看《熊出没》.' % self.name) + print('%s只能觀看《熊出沒》.' % self.name) else: - print('%s正在观看岛国爱情动作片.' % self.name) + print('%s正在觀看島國愛情動作片.' % self.name) ``` -> **说明**:写在类中的函数,我们通常称之为(对象的)方法,这些方法就是对象可以接收的消息。 +> **說明**:寫在類中的函式,我們通常稱之為(物件的)方法,這些方法就是物件可以接收的訊息。 -### 创建和使用对象 +### 建立和使用物件 -当我们定义好一个类之后,可以通过下面的方式来创建对象并给对象发消息。 +當我們定義好一個類之後,可以通過下面的方式來建立物件並給物件發訊息。 ```Python def main(): - # 创建学生对象并指定姓名和年龄 - stu1 = Student('骆昊', 38) - # 给对象发study消息 - stu1.study('Python程序设计') - # 给对象发watch_av消息 + # 建立學生物件並指定姓名和年齡 + stu1 = Student('駱昊', 38) + # 給物件發study訊息 + stu1.study('Python程式設計') + # 給物件發watch_av訊息 stu1.watch_av() - stu2 = Student('王大锤', 15) + stu2 = Student('王大錘', 15) stu2.study('思想品德') stu2.watch_av() @@ -71,9 +71,9 @@ if __name__ == '__main__': ``` -### 访问可见性问题 +### 訪問可見性問題 -对于上面的代码,有C++、Java、C#等编程经验的程序员可能会问,我们给`Student`对象绑定的`name`和`age`属性到底具有怎样的访问权限(也称为可见性)。因为在很多面向对象编程语言中,我们通常会将对象的属性设置为私有的(private)或受保护的(protected),简单的说就是不允许外界访问,而对象的方法通常都是公开的(public),因为公开的方法就是对象能够接受的消息。在Python中,属性和方法的访问权限只有两种,也就是公开的和私有的,如果希望属性是私有的,在给属性命名时可以用两个下划线作为开头,下面的代码可以验证这一点。 +對於上面的程式碼,有C++、Java、C#等程式設計經驗的程式設計師可能會問,我們給`Student`物件繫結的`name`和`age`屬性到底具有怎樣的訪問許可權(也稱為可見性)。因為在很多面向物件程式語言中,我們通常會將物件的屬性設定為私有的(private)或受保護的(protected),簡單的說就是不允許外界訪問,而物件的方法通常都是公開的(public),因為公開的方法就是物件能夠接受的訊息。在Python中,屬性和方法的訪問許可權只有兩種,也就是公開的和私有的,如果希望屬性是私有的,在給屬性命名時可以用兩個下劃線作為開頭,下面的程式碼可以驗證這一點。 ```Python class Test: @@ -99,7 +99,7 @@ if __name__ == "__main__": ``` -但是,Python并没有从语法上严格保证私有属性或方法的私密性,它只是给私有的属性和方法换了一个名字来“妨碍”对它们的访问,事实上如果你知道更换名字的规则仍然可以访问到它们,下面的代码就可以验证这一点。之所以这样设定,可以用这样一句名言加以解释,就是“We are all consenting adults here”。因为绝大多数程序员都认为开放比封闭要好,而且程序员要自己为自己的行为负责。 +但是,Python並沒有從語法上嚴格保證私有屬性或方法的私密性,它只是給私有的屬性和方法換了一個名字來“妨礙”對它們的訪問,事實上如果你知道更換名字的規則仍然可以訪問到它們,下面的程式碼就可以驗證這一點。之所以這樣設定,可以用這樣一句名言加以解釋,就是“We are all consenting adults here”。因為絕大多數程式設計師都認為開放比封閉要好,而且程式設計師要自己為自己的行為負責。 ```Python class Test: @@ -123,27 +123,27 @@ if __name__ == "__main__": ``` -在实际开发中,我们并不建议将属性设置为私有的,因为这会导致子类无法访问(后面会讲到)。所以大多数Python程序员会遵循一种命名惯例就是让属性名以单下划线开头来表示属性是受保护的,本类之外的代码在访问这样的属性时应该要保持慎重。这种做法并不是语法上的规则,单下划线开头的属性和方法外界仍然是可以访问的,所以更多的时候它是一种暗示或隐喻,关于这一点可以看看我的[《Python - 那些年我们踩过的那些坑》](http://blog.csdn.net/jackfrued/article/details/79521404)文章中的讲解。 +在實際開發中,我們並不建議將屬性設定為私有的,因為這會導致子類無法訪問(後面會講到)。所以大多數Python程式設計師會遵循一種命名慣例就是讓屬性名以單下劃線開頭來表示屬性是受保護的,本類之外的程式碼在訪問這樣的屬性時應該要保持慎重。這種做法並不是語法上的規則,單下劃線開頭的屬性和方法外界仍然是可以訪問的,所以更多的時候它是一種暗示或隱喻,關於這一點可以看看我的[《Python - 那些年我們踩過的那些坑》](http://blog.csdn.net/jackfrued/article/details/79521404)文章中的講解。 -### 面向对象的支柱 +### 面向物件的支柱 -面向对象有三大支柱:封装、继承和多态。后面两个概念在下一个章节中进行详细的说明,这里我们先说一下什么是封装。我自己对封装的理解是“隐藏一切可以隐藏的实现细节,只向外界暴露(提供)简单的编程接口”。我们在类中定义的方法其实就是把数据和对数据的操作封装起来了,在我们创建了对象之后,只需要给对象发送一个消息(调用方法)就可以执行方法中的代码,也就是说我们只需要知道方法的名字和传入的参数(方法的外部视图),而不需要知道方法内部的实现细节(方法的内部视图)。 +面向物件有三大支柱:封裝、繼承和多型。後面兩個概念在下一個章節中進行詳細的說明,這裡我們先說一下什麼是封裝。我自己對封裝的理解是“隱藏一切可以隱藏的實現細節,只向外界暴露(提供)簡單的程式設計介面”。我們在類中定義的方法其實就是把資料和對資料的操作封裝起來了,在我們建立了物件之後,只需要給物件傳送一個訊息(呼叫方法)就可以執行方法中的程式碼,也就是說我們只需要知道方法的名字和傳入的引數(方法的外部檢視),而不需要知道方法內部的實現細節(方法的內部檢視)。 -### 练习 +### 練習 -#### 练习1:定义一个类描述数字时钟 +#### 練習1:定義一個類描述數字時鐘 ```Python class Clock(object): """ - 数字时钟 + 數字時鐘 """ def __init__(self, hour=0, minute=0, second=0): """ - 构造器 + 構造器 - :param hour: 时 + :param hour: 時 :param minute: 分 :param second: 秒 """ @@ -164,7 +164,7 @@ class Clock(object): self._hour = 0 def __str__(self): - """显示时间""" + """顯示時間""" return '%02d:%02d:%02d' % \ (self._hour, self._minute, self._second) @@ -182,7 +182,7 @@ if __name__ == '__main__': ``` -#### 练习2:定义一个类描述平面上的点并提供移动点和计算到另一个点距离的方法。 +#### 練習2:定義一個類描述平面上的點並提供移動點和計算到另一個點距離的方法。 ```Python from math import sqrt @@ -192,39 +192,39 @@ class Point(object): def __init__(self, x=0, y=0): """ - 构造器 + 構造器 - :param x: 横坐标 - :param y: 纵坐标 + :param x: 橫座標 + :param y: 縱座標 """ self.x = x self.y = y def move_to(self, x, y): """ - 移动到指定位置 + 移動到指定位置 - :param x: 新的横坐标 - "param y: 新的纵坐标 + :param x: 新的橫座標 + "param y: 新的縱座標 """ self.x = x self.y = y def move_by(self, dx, dy): """ - 移动指定的增量 + 移動指定的增量 - :param dx: 横坐标的增量 - "param dy: 纵坐标的增量 + :param dx: 橫座標的增量 + "param dy: 縱座標的增量 """ self.x += dx self.y += dy def distance_to(self, other): """ - 计算与另一个点的距离 + 計算與另一個點的距離 - :param other: 另一个点 + :param other: 另一個點 """ dx = self.x - other.x dy = self.y - other.y @@ -249,4 +249,4 @@ if __name__ == '__main__': ``` -> **说明**:本章中的插图来自于Grady Booch等著作的[《面向对象分析与设计》](https://item.jd.com/20476561918.html)一书,该书是讲解面向对象编程的经典著作,有兴趣的读者可以购买和阅读这本书来了解更多的面向对象的相关知识。 \ No newline at end of file +> **說明**:本章中的插圖來自於Grady Booch等著作的[《面向物件分析與設計》](https://item.jd.com/20476561918.html)一書,該書是講解面向物件程式設計的經典著作,有興趣的讀者可以購買和閱讀這本書來了解更多的面向物件的相關知識。 \ No newline at end of file diff --git "a/Day01-15/Day09/\351\235\242\345\220\221\345\257\271\350\261\241\350\277\233\351\230\266.md" "b/Day01-15/Day09/\351\235\242\345\220\221\345\257\271\350\261\241\350\277\233\351\230\266.md" index 836f22bad..9830ebc4f 100644 --- "a/Day01-15/Day09/\351\235\242\345\220\221\345\257\271\350\261\241\350\277\233\351\230\266.md" +++ "b/Day01-15/Day09/\351\235\242\345\220\221\345\257\271\350\261\241\350\277\233\351\230\266.md" @@ -1,10 +1,10 @@ -## 面向对象进阶 +## 面向物件進階 -在前面的章节我们已经了解了面向对象的入门知识,知道了如何定义类,如何创建对象以及如何给对象发消息。为了能够更好的使用面向对象编程思想进行程序开发,我们还需要对Python中的面向对象编程进行更为深入的了解。 +在前面的章節我們已經瞭解了面向物件的入門知識,知道了如何定義類,如何建立物件以及如何給物件發訊息。為了能夠更好的使用面向物件程式設計思想進行程式開發,我們還需要對Python中的面向物件程式設計進行更為深入的瞭解。 -### @property装饰器 +### @property裝飾器 -之前我们讨论过Python中属性和方法访问权限的问题,虽然我们不建议将属性设置为私有的,但是如果直接将属性暴露给外界也是有问题的,比如我们没有办法检查赋给属性的值是否有效。我们之前的建议是将属性命名以单下划线开头,通过这种方式来暗示属性是受保护的,不建议外界直接访问,那么如果想访问属性可以通过属性的getter(访问器)和setter(修改器)方法进行对应的操作。如果要做到这点,就可以考虑使用@property包装器来包装getter和setter方法,使得对属性的访问既安全又方便,代码如下所示。 +之前我們討論過Python中屬性和方法訪問許可權的問題,雖然我們不建議將屬性設定為私有的,但是如果直接將屬性暴露給外界也是有問題的,比如我們沒有辦法檢查賦給屬性的值是否有效。我們之前的建議是將屬性命名以單下劃線開頭,通過這種方式來暗示屬性是受保護的,不建議外界直接訪問,那麼如果想訪問屬性可以通過屬性的getter(訪問器)和setter(修改器)方法進行對應的操作。如果要做到這點,就可以考慮使用@property包裝器來包裝getter和setter方法,使得對屬性的訪問既安全又方便,程式碼如下所示。 ```Python class Person(object): @@ -13,12 +13,12 @@ class Person(object): self._name = name self._age = age - # 访问器 - getter方法 + # 訪問器 - getter方法 @property def name(self): return self._name - # 访问器 - getter方法 + # 訪問器 - getter方法 @property def age(self): return self._age @@ -30,13 +30,13 @@ class Person(object): def play(self): if self._age <= 16: - print('%s正在玩飞行棋.' % self._name) + print('%s正在玩飛行棋.' % self._name) else: - print('%s正在玩斗地主.' % self._name) + print('%s正在玩鬥地主.' % self._name) def main(): - person = Person('王大锤', 12) + person = Person('王大錘', 12) person.play() person.age = 22 person.play() @@ -50,12 +50,12 @@ if __name__ == '__main__': ### \_\_slots\_\_魔法 -我们讲到这里,不知道大家是否已经意识到,Python是一门[动态语言](https://zh.wikipedia.org/wiki/%E5%8A%A8%E6%80%81%E8%AF%AD%E8%A8%80)。通常,动态语言允许我们在程序运行时给对象绑定新的属性或方法,当然也可以对已经绑定的属性和方法进行解绑定。但是如果我们需要限定自定义类型的对象只能绑定某些属性,可以通过在类中定义\_\_slots\_\_变量来进行限定。需要注意的是\_\_slots\_\_的限定只对当前类的对象生效,对子类并不起任何作用。 +我們講到這裡,不知道大家是否已經意識到,Python是一門[動態語言](https://zh.wikipedia.org/wiki/%E5%8A%A8%E6%80%81%E8%AF%AD%E8%A8%80)。通常,動態語言允許我們在程式執行時給物件繫結新的屬性或方法,當然也可以對已經繫結的屬性和方法進行解繫結。但是如果我們需要限定自定義型別的物件只能繫結某些屬性,可以通過在類中定義\_\_slots\_\_變數來進行限定。需要注意的是\_\_slots\_\_的限定只對當前類的物件生效,對子類並不起任何作用。 ```Python class Person(object): - # 限定Person对象只能绑定_name, _age和_gender属性 + # 限定Person物件只能繫結_name, _age和_gender屬性 __slots__ = ('_name', '_age', '_gender') def __init__(self, name, age): @@ -76,13 +76,13 @@ class Person(object): def play(self): if self._age <= 16: - print('%s正在玩飞行棋.' % self._name) + print('%s正在玩飛行棋.' % self._name) else: - print('%s正在玩斗地主.' % self._name) + print('%s正在玩鬥地主.' % self._name) def main(): - person = Person('王大锤', 22) + person = Person('王大錘', 22) person.play() person._gender = '男' # AttributeError: 'Person' object has no attribute '_is_gay' @@ -90,9 +90,9 @@ def main(): ``` -### 静态方法和类方法 +### 靜態方法和類方法 -之前,我们在类中定义的方法都是对象方法,也就是说这些方法都是发送给对象的消息。实际上,我们写在类中的方法并不需要都是对象方法,例如我们定义一个“三角形”类,通过传入三条边长来构造三角形,并提供计算周长和面积的方法,但是传入的三条边长未必能构造出三角形对象,因此我们可以先写一个方法来验证三条边长是否可以构成三角形,这个方法很显然就不是对象方法,因为在调用这个方法时三角形对象尚未创建出来(因为都不知道三条边能不能构成三角形),所以这个方法是属于三角形类而并不属于三角形对象的。我们可以使用静态方法来解决这类问题,代码如下所示。 +之前,我們在類中定義的方法都是物件方法,也就是說這些方法都是傳送給物件的訊息。實際上,我們寫在類中的方法並不需要都是物件方法,例如我們定義一個“三角形”類,通過傳入三條邊長來構造三角形,並提供計算周長和麵積的方法,但是傳入的三條邊長未必能構造出三角形物件,因此我們可以先寫一個方法來驗證三條邊長是否可以構成三角形,這個方法很顯然就不是物件方法,因為在呼叫這個方法時三角形物件尚未創建出來(因為都不知道三條邊能不能構成三角形),所以這個方法是屬於三角形類而並不屬於三角形物件的。我們可以使用靜態方法來解決這類問題,程式碼如下所示。 ```Python from math import sqrt @@ -120,16 +120,16 @@ class Triangle(object): def main(): a, b, c = 3, 4, 5 - # 静态方法和类方法都是通过给类发消息来调用的 + # 靜態方法和類方法都是通過給類發訊息來呼叫的 if Triangle.is_valid(a, b, c): t = Triangle(a, b, c) print(t.perimeter()) - # 也可以通过给类发消息来调用对象方法但是要传入接收消息的对象作为参数 + # 也可以通過給類發訊息來呼叫物件方法但是要傳入接收訊息的物件作為引數 # print(Triangle.perimeter(t)) print(t.area()) # print(Triangle.area(t)) else: - print('无法构成三角形.') + print('無法構成三角形.') if __name__ == '__main__': @@ -137,14 +137,14 @@ if __name__ == '__main__': ``` -和静态方法比较类似,Python还可以在类中定义类方法,类方法的第一个参数约定名为cls,它代表的是当前类相关的信息的对象(类本身也是一个对象,有的地方也称之为类的元数据对象),通过这个参数我们可以获取和类相关的信息并且可以创建出类的对象,代码如下所示。 +和靜態方法比較類似,Python還可以在類中定義類方法,類方法的第一個引數約定名為cls,它代表的是當前類相關的資訊的物件(類本身也是一個物件,有的地方也稱之為類的元資料物件),通過這個引數我們可以獲取和類相關的資訊並且可以創建出類的物件,程式碼如下所示。 ```Python from time import time, localtime, sleep class Clock(object): - """数字时钟""" + """數字時鐘""" def __init__(self, hour=0, minute=0, second=0): self._hour = hour @@ -169,13 +169,13 @@ class Clock(object): self._hour = 0 def show(self): - """显示时间""" + """顯示時間""" return '%02d:%02d:%02d' % \ (self._hour, self._minute, self._second) def main(): - # 通过类方法创建对象并获取系统时间 + # 通過類方法建立物件並獲取系統時間 clock = Clock.now() while True: print(clock.show()) @@ -188,25 +188,25 @@ if __name__ == '__main__': ``` -### 类之间的关系 +### 類之間的關係 -简单的说,类和类之间的关系有三种:is-a、has-a和use-a关系。 +簡單的說,類和類之間的關係有三種:is-a、has-a和use-a關係。 -- is-a关系也叫继承或泛化,比如学生和人的关系、手机和电子产品的关系都属于继承关系。 -- has-a关系通常称之为关联,比如部门和员工的关系,汽车和引擎的关系都属于关联关系;关联关系如果是整体和部分的关联,那么我们称之为聚合关系;如果整体进一步负责了部分的生命周期(整体和部分是不可分割的,同时同在也同时消亡),那么这种就是最强的关联关系,我们称之为合成关系。 -- use-a关系通常称之为依赖,比如司机有一个驾驶的行为(方法),其中(的参数)使用到了汽车,那么司机和汽车的关系就是依赖关系。 +- is-a關係也叫繼承或泛化,比如學生和人的關係、手機和電子產品的關係都屬於繼承關係。 +- has-a關係通常稱之為關聯,比如部門和員工的關係,汽車和引擎的關係都屬於關聯關係;關聯關係如果是整體和部分的關聯,那麼我們稱之為聚合關係;如果整體進一步負責了部分的生命週期(整體和部分是不可分割的,同時同在也同時消亡),那麼這種就是最強的關聯關係,我們稱之為合成關係。 +- use-a關係通常稱之為依賴,比如司機有一個駕駛的行為(方法),其中(的引數)使用到了汽車,那麼司機和汽車的關係就是依賴關係。 -我们可以使用一种叫做[UML](https://zh.wikipedia.org/wiki/%E7%BB%9F%E4%B8%80%E5%BB%BA%E6%A8%A1%E8%AF%AD%E8%A8%80)(统一建模语言)的东西来进行面向对象建模,其中一项重要的工作就是把类和类之间的关系用标准化的图形符号描述出来。关于UML我们在这里不做详细的介绍,有兴趣的读者可以自行阅读[《UML面向对象设计基础》](https://e.jd.com/30392949.html)一书。 +我們可以使用一種叫做[UML](https://zh.wikipedia.org/wiki/%E7%BB%9F%E4%B8%80%E5%BB%BA%E6%A8%A1%E8%AF%AD%E8%A8%80)(統一建模語言)的東西來進行面向物件建模,其中一項重要的工作就是把類和類之間的關係用標準化的圖形符號描述出來。關於UML我們在這裡不做詳細的介紹,有興趣的讀者可以自行閱讀[《UML面向物件設計基礎》](https://e.jd.com/30392949.html)一書。 ![](./res/uml-components.png) ![](./res/uml-example.png) -利用类之间的这些关系,我们可以在已有类的基础上来完成某些操作,也可以在已有类的基础上创建新的类,这些都是实现代码复用的重要手段。复用现有的代码不仅可以减少开发的工作量,也有利于代码的管理和维护,这是我们在日常工作中都会使用到的技术手段。 +利用類之間的這些關係,我們可以在已有類的基礎上來完成某些操作,也可以在已有類的基礎上建立新的類,這些都是實現程式碼複用的重要手段。複用現有的程式碼不僅可以減少開發的工作量,也有利於程式碼的管理和維護,這是我們在日常工作中都會使用到的技術手段。 -### 继承和多态 +### 繼承和多型 -刚才我们提到了,可以在已有类的基础上创建新类,这其中的一种做法就是让一个类从另一个类那里将属性和方法直接继承下来,从而减少重复代码的编写。提供继承信息的我们称之为父类,也叫超类或基类;得到继承信息的我们称之为子类,也叫派生类或衍生类。子类除了继承父类提供的属性和方法,还可以定义自己特有的属性和方法,所以子类比父类拥有的更多的能力,在实际开发中,我们经常会用子类对象去替换掉一个父类对象,这是面向对象编程中一个常见的行为,对应的原则称之为[里氏替换原则](https://zh.wikipedia.org/wiki/%E9%87%8C%E6%B0%8F%E6%9B%BF%E6%8D%A2%E5%8E%9F%E5%88%99)。下面我们先看一个继承的例子。 +剛才我們提到了,可以在已有類的基礎上建立新類,這其中的一種做法就是讓一個類從另一個類那裡將屬性和方法直接繼承下來,從而減少重複程式碼的編寫。提供繼承資訊的我們稱之為父類,也叫超類或基類;得到繼承資訊的我們稱之為子類,也叫派生類或衍生類。子類除了繼承父類提供的屬性和方法,還可以定義自己特有的屬性和方法,所以子類比父類擁有的更多的能力,在實際開發中,我們經常會用子類物件去替換掉一個父類物件,這是面向物件程式設計中一個常見的行為,對應的原則稱之為[里氏替換原則](https://zh.wikipedia.org/wiki/%E9%87%8C%E6%B0%8F%E6%9B%BF%E6%8D%A2%E5%8E%9F%E5%88%99)。下面我們先看一個繼承的例子。 ```Python class Person(object): @@ -233,13 +233,13 @@ class Person(object): def watch_av(self): if self._age >= 18: - print('%s正在观看爱情动作片.' % self._name) + print('%s正在觀看愛情動作片.' % self._name) else: - print('%s只能观看《熊出没》.' % self._name) + print('%s只能觀看《熊出沒》.' % self._name) class Student(Person): - """学生""" + """學生""" def __init__(self, name, age, grade): super().__init__(name, age) @@ -254,11 +254,11 @@ class Student(Person): self._grade = grade def study(self, course): - print('%s的%s正在学习%s.' % (self._grade, self._name, course)) + print('%s的%s正在學習%s.' % (self._grade, self._name, course)) class Teacher(Person): - """老师""" + """老師""" def __init__(self, name, age, title): super().__init__(name, age) @@ -273,15 +273,15 @@ class Teacher(Person): self._title = title def teach(self, course): - print('%s%s正在讲%s.' % (self._name, self._title, course)) + print('%s%s正在講%s.' % (self._name, self._title, course)) def main(): - stu = Student('王大锤', 15, '初三') - stu.study('数学') + stu = Student('王大錘', 15, '初三') + stu.study('數學') stu.watch_av() - t = Teacher('骆昊', 38, '老叫兽') - t.teach('Python程序设计') + t = Teacher('駱昊', 38, '老叫獸') + t.teach('Python程式設計') t.watch_av() @@ -290,21 +290,21 @@ if __name__ == '__main__': ``` -子类在继承了父类的方法后,可以对父类已有的方法给出新的实现版本,这个动作称之为方法重写(override)。通过方法重写我们可以让父类的同一个行为在子类中拥有不同的实现版本,当我们调用这个经过子类重写的方法时,不同的子类对象会表现出不同的行为,这个就是多态(poly-morphism)。 +子類在繼承了父類的方法後,可以對父類已有的方法給出新的實現版本,這個動作稱之為方法重寫(override)。通過方法重寫我們可以讓父類的同一個行為在子類中擁有不同的實現版本,當我們呼叫這個經過子類重寫的方法時,不同的子類物件會表現出不同的行為,這個就是多型(poly-morphism)。 ```Python from abc import ABCMeta, abstractmethod class Pet(object, metaclass=ABCMeta): - """宠物""" + """寵物""" def __init__(self, nickname): self._nickname = nickname @abstractmethod def make_voice(self): - """发出声音""" + """發出聲音""" pass @@ -316,14 +316,14 @@ class Dog(Pet): class Cat(Pet): - """猫""" + """貓""" def make_voice(self): print('%s: 喵...喵...' % self._nickname) def main(): - pets = [Dog('旺财'), Cat('凯蒂'), Dog('大黄')] + pets = [Dog('旺財'), Cat('凱蒂'), Dog('大黃')] for pet in pets: pet.make_voice() @@ -333,11 +333,11 @@ if __name__ == '__main__': ``` -在上面的代码中,我们将`Pet`类处理成了一个抽象类,所谓抽象类就是不能够创建对象的类,这种类的存在就是专门为了让其他类去继承它。Python从语法层面并没有像Java或C#那样提供对抽象类的支持,但是我们可以通过`abc`模块的`ABCMeta`元类和`abstractmethod`包装器来达到抽象类的效果,如果一个类中存在抽象方法那么这个类就不能够实例化(创建对象)。上面的代码中,`Dog`和`Cat`两个子类分别对`Pet`类中的`make_voice`抽象方法进行了重写并给出了不同的实现版本,当我们在`main`函数中调用该方法时,这个方法就表现出了多态行为(同样的方法做了不同的事情)。 +在上面的程式碼中,我們將`Pet`類處理成了一個抽象類,所謂抽象類就是不能夠建立物件的類,這種類的存在就是專門為了讓其他類去繼承它。Python從語法層面並沒有像Java或C#那樣提供對抽象類的支援,但是我們可以通過`abc`模組的`ABCMeta`元類和`abstractmethod`包裝器來達到抽象類的效果,如果一個類中存在抽象方法那麼這個類就不能夠例項化(建立物件)。上面的程式碼中,`Dog`和`Cat`兩個子類分別對`Pet`類中的`make_voice`抽象方法進行了重寫並給出了不同的實現版本,當我們在`main`函式中呼叫該方法時,這個方法就表現出了多型行為(同樣的方法做了不同的事情)。 -### 综合案例 +### 綜合案例 -#### 案例1:奥特曼打小怪兽 +#### 案例1:奧特曼打小怪獸 ```Python from abc import ABCMeta, abstractmethod @@ -345,9 +345,9 @@ from random import randint, randrange class Fighter(object, metaclass=ABCMeta): - """战斗者""" + """戰鬥者""" - # 通过__slots__魔法限定对象可以绑定的成员变量 + # 通過__slots__魔法限定物件可以繫結的成員變數 __slots__ = ('_name', '_hp') def __init__(self, name, hp): @@ -379,15 +379,15 @@ class Fighter(object, metaclass=ABCMeta): @abstractmethod def attack(self, other): """ - 攻击 + 攻擊 - :param other: 被攻击的对象 + :param other: 被攻擊的物件 """ pass class Ultraman(Fighter): - """奥特曼""" + """奧特曼""" __slots__ = ('_name', '_hp', '_mp') @@ -407,11 +407,11 @@ class Ultraman(Fighter): def huge_attack(self, other): """ - 究极必杀技(打掉对方至少50点或四分之三的血) + 究極必殺技(打掉對方至少50點或四分之三的血) - :param other: 被攻击的对象 + :param other: 被攻擊的物件 - :return: 使用成功返回True否则返回False + :return: 使用成功返回True否則返回False """ if self._mp >= 50: self._mp -= 50 @@ -425,11 +425,11 @@ class Ultraman(Fighter): def magic_attack(self, others): """ - 魔法攻击 + 魔法攻擊 - :param others: 被攻击的群体 + :param others: 被攻擊的群體 - :return: 使用魔法成功返回True否则返回False + :return: 使用魔法成功返回True否則返回False """ if self._mp >= 20: self._mp -= 20 @@ -441,19 +441,19 @@ class Ultraman(Fighter): return False def resume(self): - """恢复魔法值""" + """恢復魔法值""" incr_point = randint(1, 10) self._mp += incr_point return incr_point def __str__(self): - return '~~~%s奥特曼~~~\n' % self._name + \ + return '~~~%s奧特曼~~~\n' % self._name + \ '生命值: %d\n' % self._hp + \ '魔法值: %d\n' % self._mp class Monster(Fighter): - """小怪兽""" + """小怪獸""" __slots__ = ('_name', '_hp') @@ -461,12 +461,12 @@ class Monster(Fighter): other.hp -= randint(10, 20) def __str__(self): - return '~~~%s小怪兽~~~\n' % self._name + \ + return '~~~%s小怪獸~~~\n' % self._name + \ '生命值: %d\n' % self._hp def is_any_alive(monsters): - """判断有没有小怪兽是活着的""" + """判斷有沒有小怪獸是活著的""" for monster in monsters: if monster.alive > 0: return True @@ -474,7 +474,7 @@ def is_any_alive(monsters): def select_alive_one(monsters): - """选中一只活着的小怪兽""" + """選中一隻活著的小怪獸""" monsters_len = len(monsters) while True: index = randrange(monsters_len) @@ -484,48 +484,48 @@ def select_alive_one(monsters): def display_info(ultraman, monsters): - """显示奥特曼和小怪兽的信息""" + """顯示奧特曼和小怪獸的資訊""" print(ultraman) for monster in monsters: print(monster, end='') def main(): - u = Ultraman('骆昊', 1000, 120) + u = Ultraman('駱昊', 1000, 120) m1 = Monster('舒小玲', 250) m2 = Monster('白元芳', 500) - m3 = Monster('王大锤', 750) + m3 = Monster('王大錘', 750) ms = [m1, m2, m3] fight_round = 1 while u.alive and is_any_alive(ms): print('========第%02d回合========' % fight_round) - m = select_alive_one(ms) # 选中一只小怪兽 - skill = randint(1, 10) # 通过随机数选择使用哪种技能 - if skill <= 6: # 60%的概率使用普通攻击 - print('%s使用普通攻击打了%s.' % (u.name, m.name)) + m = select_alive_one(ms) # 選中一隻小怪獸 + skill = randint(1, 10) # 通過隨機數選擇使用哪種技能 + if skill <= 6: # 60%的概率使用普通攻擊 + print('%s使用普通攻擊打了%s.' % (u.name, m.name)) u.attack(m) - print('%s的魔法值恢复了%d点.' % (u.name, u.resume())) - elif skill <= 9: # 30%的概率使用魔法攻击(可能因魔法值不足而失败) + print('%s的魔法值恢復了%d點.' % (u.name, u.resume())) + elif skill <= 9: # 30%的概率使用魔法攻擊(可能因魔法值不足而失敗) if u.magic_attack(ms): - print('%s使用了魔法攻击.' % u.name) + print('%s使用了魔法攻擊.' % u.name) else: - print('%s使用魔法失败.' % u.name) - else: # 10%的概率使用究极必杀技(如果魔法值不足则使用普通攻击) + print('%s使用魔法失敗.' % u.name) + else: # 10%的概率使用究極必殺技(如果魔法值不足則使用普通攻擊) if u.huge_attack(m): - print('%s使用究极必杀技虐了%s.' % (u.name, m.name)) + print('%s使用究極必殺技虐了%s.' % (u.name, m.name)) else: - print('%s使用普通攻击打了%s.' % (u.name, m.name)) - print('%s的魔法值恢复了%d点.' % (u.name, u.resume())) - if m.alive > 0: # 如果选中的小怪兽没有死就回击奥特曼 - print('%s回击了%s.' % (m.name, u.name)) + print('%s使用普通攻擊打了%s.' % (u.name, m.name)) + print('%s的魔法值恢復了%d點.' % (u.name, u.resume())) + if m.alive > 0: # 如果選中的小怪獸沒有死就回擊奧特曼 + print('%s回擊了%s.' % (m.name, u.name)) m.attack(u) - display_info(u, ms) # 每个回合结束后显示奥特曼和小怪兽的信息 + display_info(u, ms) # 每個回合結束後顯示奧特曼和小怪獸的資訊 fight_round += 1 - print('\n========战斗结束!========\n') + print('\n========戰鬥結束!========\n') if u.alive > 0: - print('%s奥特曼胜利!' % u.name) + print('%s奧特曼勝利!' % u.name) else: - print('小怪兽胜利!') + print('小怪獸勝利!') if __name__ == '__main__': @@ -533,14 +533,14 @@ if __name__ == '__main__': ``` -#### 案例2:扑克游戏 +#### 案例2:撲克遊戲 ```Python from random import randrange class Card(object): - """一张牌""" + """一張牌""" def __init__(self, suite, face): self._suite = suite @@ -585,7 +585,7 @@ class Poker(object): return self._cards def shuffle(self): - """洗牌(随机乱序)""" + """洗牌(隨機亂序)""" self._current = 0 cards_len = len(self._cards) for index in range(cards_len): @@ -595,14 +595,14 @@ class Poker(object): @property def next(self): - """发牌""" + """發牌""" card = self._cards[self._current] self._current += 1 return card @property def has_next(self): - """还有没有牌""" + """還有沒有牌""" return self._current < len(self._cards) @@ -630,7 +630,7 @@ class Player(object): self._cards_on_hand.sort(key=card_key) -# 排序规则-先根据花色再根据点数排序 +# 排序規則-先根據花色再根據點數排序 def get_key(card): return (card.suite, card.face) @@ -638,7 +638,7 @@ def get_key(card): def main(): p = Poker() p.shuffle() - players = [Player('东邪'), Player('西毒'), Player('南帝'), Player('北丐')] + players = [Player('東邪'), Player('西毒'), Player('南帝'), Player('北丐')] for _ in range(13): for player in players: player.get(p.next) @@ -655,25 +655,25 @@ if __name__ == '__main__': ``` ->**说明**:大家可以自己尝试在上面代码的基础上写一个简单的扑克游戏,例如21点(Black Jack),游戏的规则可以自己在网上找一找。 +>**說明**:大家可以自己嘗試在上面程式碼的基礎上寫一個簡單的撲克遊戲,例如21點(Black Jack),遊戲的規則可以自己在網上找一找。 -#### 案例3:工资结算系统 +#### 案例3:工資結算系統 ```Python """ -某公司有三种类型的员工 分别是部门经理、程序员和销售员 -需要设计一个工资结算系统 根据提供的员工信息来计算月薪 -部门经理的月薪是每月固定15000元 -程序员的月薪按本月工作时间计算 每小时150元 -销售员的月薪是1200元的底薪加上销售额5%的提成 +某公司有三種類型的員工 分別是部門經理、程式設計師和銷售員 +需要設計一個工資結算系統 根據提供的員工資訊來計算月薪 +部門經理的月薪是每月固定15000元 +程式設計師的月薪按本月工作時間計算 每小時150元 +銷售員的月薪是1200元的底薪加上銷售額5%的提成 """ from abc import ABCMeta, abstractmethod class Employee(object, metaclass=ABCMeta): - """员工""" + """員工""" def __init__(self, name): """ @@ -690,7 +690,7 @@ class Employee(object, metaclass=ABCMeta): @abstractmethod def get_salary(self): """ - 获得月薪 + 獲得月薪 :return: 月薪 """ @@ -698,14 +698,14 @@ class Employee(object, metaclass=ABCMeta): class Manager(Employee): - """部门经理""" + """部門經理""" def get_salary(self): return 15000.0 class Programmer(Employee): - """程序员""" + """程式設計師""" def __init__(self, name, working_hour=0): super().__init__(name) @@ -724,7 +724,7 @@ class Programmer(Employee): class Salesman(Employee): - """销售员""" + """銷售員""" def __init__(self, name, sales=0): super().__init__(name) @@ -744,18 +744,18 @@ class Salesman(Employee): def main(): emps = [ - Manager('刘备'), Programmer('诸葛亮'), + Manager('劉備'), Programmer('諸葛亮'), Manager('曹操'), Salesman('荀彧'), - Salesman('吕布'), Programmer('张辽'), - Programmer('赵云') + Salesman('呂布'), Programmer('張遼'), + Programmer('趙雲') ] for emp in emps: if isinstance(emp, Programmer): - emp.working_hour = int(input('请输入%s本月工作时间: ' % emp.name)) + emp.working_hour = int(input('請輸入%s本月工作時間: ' % emp.name)) elif isinstance(emp, Salesman): - emp.sales = float(input('请输入%s本月销售额: ' % emp.name)) - # 同样是接收get_salary这个消息但是不同的员工表现出了不同的行为(多态) - print('%s本月工资为: ¥%s元' % + emp.sales = float(input('請輸入%s本月銷售額: ' % emp.name)) + # 同樣是接收get_salary這個訊息但是不同的員工表現出了不同的行為(多型) + print('%s本月工資為: ¥%s元' % (emp.name, emp.get_salary())) diff --git "a/Day01-15/Day10/\345\233\276\345\275\242\347\224\250\346\210\267\347\225\214\351\235\242\345\222\214\346\270\270\346\210\217\345\274\200\345\217\221.md" "b/Day01-15/Day10/\345\233\276\345\275\242\347\224\250\346\210\267\347\225\214\351\235\242\345\222\214\346\270\270\346\210\217\345\274\200\345\217\221.md" index 729753331..12c15c14d 100644 --- "a/Day01-15/Day10/\345\233\276\345\275\242\347\224\250\346\210\267\347\225\214\351\235\242\345\222\214\346\270\270\346\210\217\345\274\200\345\217\221.md" +++ "b/Day01-15/Day10/\345\233\276\345\275\242\347\224\250\346\210\267\347\225\214\351\235\242\345\222\214\346\270\270\346\210\217\345\274\200\345\217\221.md" @@ -1,18 +1,18 @@ -## 图形用户界面和游戏开发 +## 圖形使用者介面和遊戲開發 -### 基于tkinter模块的GUI +### 基於tkinter模組的GUI -GUI是图形用户界面的缩写,图形化的用户界面对使用过计算机的人来说应该都不陌生,在此也无需进行赘述。Python默认的GUI开发模块是tkinter(在Python 3以前的版本中名为Tkinter),从这个名字就可以看出它是基于Tk的,Tk是一个工具包,最初是为Tcl设计的,后来被移植到很多其他的脚本语言中,它提供了跨平台的GUI控件。当然Tk并不是最新和最好的选择,也没有功能特别强大的GUI控件,事实上,开发GUI应用并不是Python最擅长的工作,如果真的需要使用Python开发GUI应用,wxPython、PyQt、PyGTK等模块都是不错的选择。 +GUI是圖形使用者介面的縮寫,圖形化的使用者介面對使用過計算機的人來說應該都不陌生,在此也無需進行贅述。Python預設的GUI開發模組是tkinter(在Python 3以前的版本中名為Tkinter),從這個名字就可以看出它是基於Tk的,Tk是一個工具包,最初是為Tcl設計的,後來被移植到很多其他的指令碼語言中,它提供了跨平臺的GUI控制元件。當然Tk並不是最新和最好的選擇,也沒有功能特別強大的GUI控制元件,事實上,開發GUI應用並不是Python最擅長的工作,如果真的需要使用Python開發GUI應用,wxPython、PyQt、PyGTK等模組都是不錯的選擇。 -基本上使用tkinter来开发GUI应用需要以下5个步骤: +基本上使用tkinter來開發GUI應用需要以下5個步驟: -1. 导入tkinter模块中我们需要的东西。 -2. 创建一个顶层窗口对象并用它来承载整个GUI应用。 -3. 在顶层窗口对象上添加GUI组件。 -4. 通过代码将这些GUI组件的功能组织起来。 -5. 进入主事件循环(main loop)。 +1. 匯入tkinter模組中我們需要的東西。 +2. 建立一個頂層視窗物件並用它來承載整個GUI應用。 +3. 在頂層視窗物件上新增GUI元件。 +4. 通過程式碼將這些GUI元件的功能組織起來。 +5. 進入主事件迴圈(main loop)。 -下面的代码演示了如何使用tkinter做一个简单的GUI应用。 +下面的程式碼演示瞭如何使用tkinter做一個簡單的GUI應用。 ```Python import tkinter @@ -22,7 +22,7 @@ import tkinter.messagebox def main(): flag = True - # 修改标签上的文字 + # 修改標籤上的文字 def change_label_text(): nonlocal flag flag = not flag @@ -30,29 +30,29 @@ def main(): if flag else ('blue', 'Goodbye, world!') label.config(text=msg, fg=color) - # 确认退出 + # 確認退出 def confirm_to_quit(): - if tkinter.messagebox.askokcancel('温馨提示', '确定要退出吗?'): + if tkinter.messagebox.askokcancel('溫馨提示', '確定要退出嗎?'): top.quit() - # 创建顶层窗口 + # 建立頂層視窗 top = tkinter.Tk() - # 设置窗口大小 + # 設定視窗大小 top.geometry('240x160') - # 设置窗口标题 - top.title('小游戏') - # 创建标签对象并添加到顶层窗口 + # 設定視窗標題 + top.title('小遊戲') + # 建立標籤物件並新增到頂層視窗 label = tkinter.Label(top, text='Hello, world!', font='Arial -32', fg='red') label.pack(expand=1) - # 创建一个装按钮的容器 + # 建立一個裝按鈕的容器 panel = tkinter.Frame(top) - # 创建按钮对象 指定添加到哪个容器中 通过command参数绑定事件回调函数 + # 建立按鈕物件 指定新增到哪個容器中 通過command引數繫結事件回撥函式 button1 = tkinter.Button(panel, text='修改', command=change_label_text) button1.pack(side='left') button2 = tkinter.Button(panel, text='退出', command=confirm_to_quit) button2.pack(side='right') panel.pack(side='bottom') - # 开启主事件循环 + # 開啟主事件迴圈 tkinter.mainloop() @@ -61,31 +61,31 @@ if __name__ == '__main__': ``` -需要说明的是,GUI应用通常是事件驱动式的,之所以要进入主事件循环就是要监听鼠标、键盘等各种事件的发生并执行对应的代码对事件进行处理,因为事件会持续的发生,所以需要这样的一个循环一直运行着等待下一个事件的发生。另一方面,Tk为控件的摆放提供了三种布局管理器,通过布局管理器可以对控件进行定位,这三种布局管理器分别是:Placer(开发者提供控件的大小和摆放位置)、Packer(自动将控件填充到合适的位置)和Grid(基于网格坐标来摆放控件),此处不进行赘述。 +需要說明的是,GUI應用通常是事件驅動式的,之所以要進入主事件迴圈就是要監聽滑鼠、鍵盤等各種事件的發生並執行對應的程式碼對事件進行處理,因為事件會持續的發生,所以需要這樣的一個迴圈一直執行著等待下一個事件的發生。另一方面,Tk為控制元件的擺放提供了三種佈局管理器,通過佈局管理器可以對控制元件進行定位,這三種佈局管理器分別是:Placer(開發者提供控制元件的大小和擺放位置)、Packer(自動將控制元件填充到合適的位置)和Grid(基於網格座標來擺放控制元件),此處不進行贅述。 -### 使用Pygame进行游戏开发 +### 使用Pygame進行遊戲開發 -Pygame是一个开源的Python模块,专门用于多媒体应用(如电子游戏)的开发,其中包含对图像、声音、视频、事件、碰撞等的支持。Pygame建立在[SDL](https://zh.wikipedia.org/wiki/SDL)的基础上,SDL是一套跨平台的多媒体开发库,用C语言实现,被广泛的应用于游戏、模拟器、播放器等的开发。而Pygame让游戏开发者不再被底层语言束缚,可以更多的关注游戏的功能和逻辑。 +Pygame是一個開源的Python模組,專門用於多媒體應用(如電子遊戲)的開發,其中包含對影象、聲音、視訊、事件、碰撞等的支援。Pygame建立在[SDL](https://zh.wikipedia.org/wiki/SDL)的基礎上,SDL是一套跨平臺的多媒體開發庫,用C語言實現,被廣泛的應用於遊戲、模擬器、播放器等的開發。而Pygame讓遊戲開發者不再被底層語言束縛,可以更多的關注遊戲的功能和邏輯。 -下面我们来完成一个简单的小游戏,游戏的名字叫“大球吃小球”,当然完成这个游戏并不是重点,学会使用Pygame也不是重点,最重要的我们要在这个过程中体会如何使用前面讲解的面向对象程序设计,学会用这种编程思想去解决现实中的问题。 +下面我們來完成一個簡單的小遊戲,遊戲的名字叫“大球吃小球”,當然完成這個遊戲並不是重點,學會使用Pygame也不是重點,最重要的我們要在這個過程中體會如何使用前面講解的面向物件程式設計,學會用這種程式設計思想去解決現實中的問題。 -#### 制作游戏窗口 +#### 製作遊戲視窗 ```Python import pygame def main(): - # 初始化导入的pygame中的模块 + # 初始化匯入的pygame中的模組 pygame.init() - # 初始化用于显示的窗口并设置窗口尺寸 + # 初始化用於顯示的視窗並設定視窗尺寸 screen = pygame.display.set_mode((800, 600)) - # 设置当前窗口的标题 + # 設定當前視窗的標題 pygame.display.set_caption('大球吃小球') running = True - # 开启一个事件循环处理发生的事件 + # 開啟一個事件迴圈處理髮生的事件 while running: - # 从消息队列中获取事件并对事件进行处理 + # 從訊息佇列中獲取事件並對事件進行處理 for event in pygame.event.get(): if event.type == pygame.QUIT: running = False @@ -96,31 +96,31 @@ if __name__ == '__main__': ``` -#### 在窗口中绘图 +#### 在視窗中繪圖 -可以通过pygame中draw模块的函数在窗口上绘图,可以绘制的图形包括:线条、矩形、多边形、圆、椭圆、圆弧等。需要说明的是,屏幕坐标系是将屏幕左上角设置为坐标原点`(0, 0)`,向右是x轴的正向,向下是y轴的正向,在表示位置或者设置尺寸的时候,我们默认的单位都是[像素](https://zh.wikipedia.org/wiki/%E5%83%8F%E7%B4%A0)。所谓像素就是屏幕上的一个点,你可以用浏览图片的软件试着将一张图片放大若干倍,就可以看到这些点。pygame中表示颜色用的是色光[三原色](https://zh.wikipedia.org/wiki/%E5%8E%9F%E8%89%B2)表示法,即通过一个元组或列表来指定颜色的RGB值,每个值都在0~255之间,因为是每种原色都用一个8位(bit)的值来表示,三种颜色相当于一共由24位构成,这也就是常说的“24位颜色表示法”。 +可以通過pygame中draw模組的函式在視窗上繪圖,可以繪製的圖形包括:線條、矩形、多邊形、圓、橢圓、圓弧等。需要說明的是,螢幕座標系是將螢幕左上角設定為座標原點`(0, 0)`,向右是x軸的正向,向下是y軸的正向,在表示位置或者設定尺寸的時候,我們預設的單位都是[畫素](https://zh.wikipedia.org/wiki/%E5%83%8F%E7%B4%A0)。所謂畫素就是螢幕上的一個點,你可以用瀏覽圖片的軟體試著將一張圖片放大若干倍,就可以看到這些點。pygame中表示顏色用的是色光[三原色](https://zh.wikipedia.org/wiki/%E5%8E%9F%E8%89%B2)表示法,即通過一個元組或列表來指定顏色的RGB值,每個值都在0~255之間,因為是每種原色都用一個8位(bit)的值來表示,三種顏色相當於一共由24位構成,這也就是常說的“24位顏色表示法”。 ```Python import pygame def main(): - # 初始化导入的pygame中的模块 + # 初始化匯入的pygame中的模組 pygame.init() - # 初始化用于显示的窗口并设置窗口尺寸 + # 初始化用於顯示的視窗並設定視窗尺寸 screen = pygame.display.set_mode((800, 600)) - # 设置当前窗口的标题 + # 設定當前視窗的標題 pygame.display.set_caption('大球吃小球') - # 设置窗口的背景色(颜色是由红绿蓝三原色构成的元组) + # 設定視窗的背景色(顏色是由紅綠藍三原色構成的元組) screen.fill((242, 242, 242)) - # 绘制一个圆(参数分别是: 屏幕, 颜色, 圆心位置, 半径, 0表示填充圆) + # 繪製一個圓(引數分別是: 螢幕, 顏色, 圓心位置, 半徑, 0表示填充圓) pygame.draw.circle(screen, (255, 0, 0,), (100, 100), 30, 0) - # 刷新当前窗口(渲染窗口将绘制的图像呈现出来) + # 重新整理當前視窗(渲染視窗將繪製的影象呈現出來) pygame.display.flip() running = True - # 开启一个事件循环处理发生的事件 + # 開啟一個事件迴圈處理髮生的事件 while running: - # 从消息队列中获取事件并对事件进行处理 + # 從訊息佇列中獲取事件並對事件進行處理 for event in pygame.event.get(): if event.type == pygame.QUIT: running = False @@ -131,33 +131,33 @@ if __name__ == '__main__': ``` -####加载图像 +####載入影象 -如果需要直接加载图像到窗口上,可以使用pygame中image模块的函数来加载图像,再通过之前获得的窗口对象的`blit`方法渲染图像,代码如下所示。 +如果需要直接載入影象到視窗上,可以使用pygame中image模組的函式來載入影象,再通過之前獲得的視窗物件的`blit`方法渲染影象,程式碼如下所示。 ```Python import pygame def main(): - # 初始化导入的pygame中的模块 + # 初始化匯入的pygame中的模組 pygame.init() - # 初始化用于显示的窗口并设置窗口尺寸 + # 初始化用於顯示的視窗並設定視窗尺寸 screen = pygame.display.set_mode((800, 600)) - # 设置当前窗口的标题 + # 設定當前視窗的標題 pygame.display.set_caption('大球吃小球') - # 设置窗口的背景色(颜色是由红绿蓝三原色构成的元组) + # 設定視窗的背景色(顏色是由紅綠藍三原色構成的元組) screen.fill((255, 255, 255)) - # 通过指定的文件名加载图像 + # 通過指定的檔名載入影象 ball_image = pygame.image.load('./res/ball.png') - # 在窗口上渲染图像 + # 在視窗上渲染影象 screen.blit(ball_image, (50, 50)) - # 刷新当前窗口(渲染窗口将绘制的图像呈现出来) + # 重新整理當前視窗(渲染視窗將繪製的影象呈現出來) pygame.display.flip() running = True - # 开启一个事件循环处理发生的事件 + # 開啟一個事件迴圈處理髮生的事件 while running: - # 从消息队列中获取事件并对事件进行处理 + # 從訊息佇列中獲取事件並對事件進行處理 for event in pygame.event.get(): if event.type == pygame.QUIT: running = False @@ -168,34 +168,34 @@ if __name__ == '__main__': ``` -####实现动画效果 +####實現動畫效果 -说到[动画](https://zh.wikipedia.org/wiki/%E5%8A%A8%E7%94%BB)这个词大家都不会陌生,事实上要实现动画效果,本身的原理也非常简单,就是将不连续的图片连续的播放,只要每秒钟达到了一定的帧数,那么就可以做出比较流畅的动画效果。如果要让上面代码中的小球动起来,可以将小球的位置用变量来表示,并在循环中修改小球的位置再刷新整个窗口即可。 +說到[動畫](https://zh.wikipedia.org/wiki/%E5%8A%A8%E7%94%BB)這個詞大家都不會陌生,事實上要實現動畫效果,本身的原理也非常簡單,就是將不連續的圖片連續的播放,只要每秒鐘達到了一定的幀數,那麼就可以做出比較流暢的動畫效果。如果要讓上面程式碼中的小球動起來,可以將小球的位置用變數來表示,並在迴圈中修改小球的位置再重新整理整個視窗即可。 ```Python import pygame def main(): - # 初始化导入的pygame中的模块 + # 初始化匯入的pygame中的模組 pygame.init() - # 初始化用于显示的窗口并设置窗口尺寸 + # 初始化用於顯示的視窗並設定視窗尺寸 screen = pygame.display.set_mode((800, 600)) - # 设置当前窗口的标题 + # 設定當前視窗的標題 pygame.display.set_caption('大球吃小球') - # 定义变量来表示小球在屏幕上的位置 + # 定義變數來表示小球在螢幕上的位置 x, y = 50, 50 running = True - # 开启一个事件循环处理发生的事件 + # 開啟一個事件迴圈處理髮生的事件 while running: - # 从消息队列中获取事件并对事件进行处理 + # 從訊息佇列中獲取事件並對事件進行處理 for event in pygame.event.get(): if event.type == pygame.QUIT: running = False screen.fill((255, 255, 255)) pygame.draw.circle(screen, (255, 0, 0,), (x, y), 30, 0) pygame.display.flip() - # 每隔50毫秒就改变小球的位置再刷新窗口 + # 每隔50毫秒就改變小球的位置再重新整理視窗 pygame.time.delay(50) x, y = x + 5, y + 5 @@ -205,9 +205,9 @@ if __name__ == '__main__': ``` -#### 碰撞检测 +#### 碰撞檢測 -通常一个游戏中会有很多对象出现,而这些对象之间的“碰撞”在所难免,比如炮弹击中了飞机、箱子撞到了地面等。碰撞检测在绝大多数的游戏中都是一个必须得处理的至关重要的问题,pygame的sprite(动画精灵)模块就提供了对碰撞检测的支持,这里我们暂时不介绍sprite模块提供的功能,因为要检测两个小球有没有碰撞其实非常简单,只需要检查球心的距离有没有小于两个球的半径之和。为了制造出更多的小球,我们可以通过对鼠标事件的处理,在点击鼠标的位置创建颜色、大小和移动速度都随机的小球,当然要做到这一点,我们可以把之前学习到的面向对象的知识应用起来。 +通常一個遊戲中會有很多物件出現,而這些物件之間的“碰撞”在所難免,比如炮彈擊中了飛機、箱子撞到了地面等。碰撞檢測在絕大多數的遊戲中都是一個必須得處理的至關重要的問題,pygame的sprite(動畫精靈)模組就提供了對碰撞檢測的支援,這裡我們暫時不介紹sprite模組提供的功能,因為要檢測兩個小球有沒有碰撞其實非常簡單,只需要檢查球心的距離有沒有小於兩個球的半徑之和。為了製造出更多的小球,我們可以通過對滑鼠事件的處理,在點選滑鼠的位置建立顏色、大小和移動速度都隨機的小球,當然要做到這一點,我們可以把之前學習到的面向物件的知識應用起來。 ```Python from enum import Enum, unique @@ -219,7 +219,7 @@ import pygame @unique class Color(Enum): - """颜色""" + """顏色""" RED = (255, 0, 0) GREEN = (0, 255, 0) @@ -230,7 +230,7 @@ class Color(Enum): @staticmethod def random_color(): - """获得随机颜色""" + """獲得隨機顏色""" r = randint(0, 255) g = randint(0, 255) b = randint(0, 255) @@ -251,7 +251,7 @@ class Ball(object): self.alive = True def move(self, screen): - """移动""" + """移動""" self.x += self.sx self.y += self.sy if self.x - self.radius <= 0 or \ @@ -272,57 +272,57 @@ class Ball(object): a self.radius = self.radius + int(other.radius * 0.146) def draw(self, screen): - """在窗口上绘制球""" + """在視窗上繪製球""" pygame.draw.circle(screen, self.color, (self.x, self.y), self.radius, 0) ``` -#### 事件处理 +#### 事件處理 -可以在事件循环中对鼠标事件进行处理,通过事件对象的`type`属性可以判定事件类型,再通过`pos`属性就可以获得鼠标点击的位置。如果要处理键盘事件也是在这个地方,做法与处理鼠标事件类似。 +可以在事件迴圈中對滑鼠事件進行處理,通過事件物件的`type`屬性可以判定事件型別,再通過`pos`屬性就可以獲得滑鼠點選的位置。如果要處理鍵盤事件也是在這個地方,做法與處理滑鼠事件類似。 ```Python def main(): - # 定义用来装所有球的容器 + # 定義用來裝所有球的容器 balls = [] - # 初始化导入的pygame中的模块 + # 初始化匯入的pygame中的模組 pygame.init() - # 初始化用于显示的窗口并设置窗口尺寸 + # 初始化用於顯示的視窗並設定視窗尺寸 screen = pygame.display.set_mode((800, 600)) - # 设置当前窗口的标题 + # 設定當前視窗的標題 pygame.display.set_caption('大球吃小球') running = True - # 开启一个事件循环处理发生的事件 + # 開啟一個事件迴圈處理髮生的事件 while running: - # 从消息队列中获取事件并对事件进行处理 + # 從訊息佇列中獲取事件並對事件進行處理 for event in pygame.event.get(): if event.type == pygame.QUIT: running = False - # 处理鼠标事件的代码 + # 處理滑鼠事件的程式碼 if event.type == pygame.MOUSEBUTTONDOWN and event.button == 1: - # 获得点击鼠标的位置 + # 獲得點選滑鼠的位置 x, y = event.pos radius = randint(10, 100) sx, sy = randint(-10, 10), randint(-10, 10) color = Color.random_color() - # 在点击鼠标的位置创建一个球(大小、速度和颜色随机) + # 在點選滑鼠的位置建立一個球(大小、速度和顏色隨機) ball = Ball(x, y, radius, sx, sy, color) - # 将球添加到列表容器中 + # 將球新增到列表容器中 balls.append(ball) screen.fill((255, 255, 255)) - # 取出容器中的球 如果没被吃掉就绘制 被吃掉了就移除 + # 取出容器中的球 如果沒被吃掉就繪製 被吃掉了就移除 for ball in balls: if ball.alive: ball.draw(screen) else: balls.remove(ball) pygame.display.flip() - # 每隔50毫秒就改变球的位置再刷新窗口 + # 每隔50毫秒就改變球的位置再重新整理視窗 pygame.time.delay(50) for ball in balls: ball.move(screen) - # 检查球有没有吃到其他的球 + # 檢查球有沒有吃到其他的球 for other in balls: ball.eat(other) @@ -332,7 +332,7 @@ if __name__ == '__main__': ``` -上面的两段代码合在一起,我们就完成了“大球吃小球”的游戏(如下图所示),准确的说它算不上一个游戏,但是做一个小游戏的基本知识我们已经通过这个例子告诉大家了,有了这些知识已经可以开始你的小游戏开发之旅了。其实上面的代码中还有很多值得改进的地方,比如刷新窗口以及让球移动起来的代码并不应该放在事件循环中,等学习了多线程的知识后,用一个后台线程来处理这些事可能是更好的选择。如果希望获得更好的用户体验,我们还可以在游戏中加入背景音乐以及在球与球发生碰撞时播放音效,利用pygame的mixer和music模块,我们可以很容易的做到这一点,大家可以自行了解这方面的知识。事实上,想了解更多的关于pygame的知识,最好的教程是[pygame的官方网站](https://www.pygame.org/news),如果英语没毛病就可以赶紧去看看啦。 如果想开发[3D游戏](https://zh.wikipedia.org/wiki/3D%E6%B8%B8%E6%88%8F),pygame就显得力不从心了,对3D游戏开发如果有兴趣的读者不妨看看[Panda3D](https://www.panda3d.org/)。 +上面的兩段程式碼合在一起,我們就完成了“大球吃小球”的遊戲(如下圖所示),準確的說它算不上一個遊戲,但是做一個小遊戲的基本知識我們已經通過這個例子告訴大家了,有了這些知識已經可以開始你的小遊戲開發之旅了。其實上面的程式碼中還有很多值得改進的地方,比如重新整理視窗以及讓球移動起來的程式碼並不應該放在事件迴圈中,等學習了多執行緒的知識後,用一個後臺執行緒來處理這些事可能是更好的選擇。如果希望獲得更好的使用者體驗,我們還可以在遊戲中加入背景音樂以及在球與球發生碰撞時播放音效,利用pygame的mixer和music模組,我們可以很容易的做到這一點,大家可以自行了解這方面的知識。事實上,想了解更多的關於pygame的知識,最好的教程是[pygame的官方網站](https://www.pygame.org/news),如果英語沒毛病就可以趕緊去看看啦。 如果想開發[3D遊戲](https://zh.wikipedia.org/wiki/3D%E6%B8%B8%E6%88%8F),pygame就顯得力不從心了,對3D遊戲開發如果有興趣的讀者不妨看看[Panda3D](https://www.panda3d.org/)。 diff --git "a/Day01-15/Day11/\346\226\207\344\273\266\345\222\214\345\274\202\345\270\270.md" "b/Day01-15/Day11/\346\226\207\344\273\266\345\222\214\345\274\202\345\270\270.md" index 3fa4e96af..910f5f23d 100644 --- "a/Day01-15/Day11/\346\226\207\344\273\266\345\222\214\345\274\202\345\270\270.md" +++ "b/Day01-15/Day11/\346\226\207\344\273\266\345\222\214\345\274\202\345\270\270.md" @@ -1,30 +1,30 @@ -## 文件和异常 +## 檔案和異常 -在实际开发中,常常需要对程序中的数据进行[持久化](https://baike.baidu.com/item/%E6%95%B0%E6%8D%AE%E6%8C%81%E4%B9%85%E5%8C%96)操作,而实现数据持久化最直接简单的方式就是将数据保存到文件中。说到“文件”这个词,可能需要先科普一下关于[文件系统](https://zh.wikipedia.org/wiki/%E6%96%87%E4%BB%B6%E7%B3%BB%E7%BB%9F)的知识,对于这个概念,维基百科上给出了很好的诠释,这里不再浪费笔墨。 +在實際開發中,常常需要對程式中的資料進行[持久化](https://baike.baidu.com/item/%E6%95%B0%E6%8D%AE%E6%8C%81%E4%B9%85%E5%8C%96)操作,而實現資料持久化最直接簡單的方式就是將資料儲存到檔案中。說到“檔案”這個詞,可能需要先科普一下關於[檔案系統](https://zh.wikipedia.org/wiki/%E6%96%87%E4%BB%B6%E7%B3%BB%E7%BB%9F)的知識,對於這個概念,維基百科上給出了很好的詮釋,這裡不再浪費筆墨。 -在Python中实现文件的读写操作其实非常简单,通过Python内置的`open`函数,我们可以指定文件名、操作模式、编码信息等来获得操作文件的对象,接下来就可以对文件进行读写操作了。这里所说的操作模式是指要打开什么样的文件(字符文件还是二进制文件)以及做什么样的操作(读、写还是追加),具体的如下表所示。 +在Python中實現檔案的讀寫操作其實非常簡單,通過Python內建的`open`函式,我們可以指定檔名、操作模式、編碼資訊等來獲得操作檔案的物件,接下來就可以對檔案進行讀寫操作了。這裡所說的操作模式是指要開啟什麼樣的檔案(字元檔案還是二進位制檔案)以及做什麼樣的操作(讀、寫還是追加),具體的如下表所示。 -| 操作模式 | 具体含义 | +| 操作模式 | 具體含義 | | -------- | -------------------------------- | -| `'r'` | 读取 (默认) | -| `'w'` | 写入(会先截断之前的内容) | -| `'x'` | 写入,如果文件已经存在会产生异常 | -| `'a'` | 追加,将内容写入到已有文件的末尾 | -| `'b'` | 二进制模式 | -| `'t'` | 文本模式(默认) | -| `'+'` | 更新(既可以读又可以写) | +| `'r'` | 讀取 (預設) | +| `'w'` | 寫入(會先截斷之前的內容) | +| `'x'` | 寫入,如果檔案已經存在會產生異常 | +| `'a'` | 追加,將內容寫入到已有檔案的末尾 | +| `'b'` | 二進位制模式 | +| `'t'` | 文字模式(預設) | +| `'+'` | 更新(既可以讀又可以寫) | -下面这张图来自于[菜鸟教程](http://www.runoob.com)网站,它展示了如果根据应用程序的需要来设置操作模式。 +下面這張圖來自於[菜鳥教程](http://www.runoob.com)網站,它展示瞭如果根據應用程式的需要來設定操作模式。 ![](./res/file-open-mode.png) -### 读写文本文件 +### 讀寫文字檔案 -读取文本文件时,需要在使用`open`函数时指定好带路径的文件名(可以使用相对路径或绝对路径)并将文件模式设置为`'r'`(如果不指定,默认值也是`'r'`),然后通过`encoding`参数指定编码(如果不指定,默认值是None,那么在读取文件时使用的是操作系统默认的编码),如果不能保证保存文件时使用的编码方式与encoding参数指定的编码方式是一致的,那么就可能因无法解码字符而导致读取失败。下面的例子演示了如何读取一个纯文本文件。 +讀取文字檔案時,需要在使用`open`函式時指定好帶路徑的檔名(可以使用相對路徑或絕對路徑)並將檔案模式設定為`'r'`(如果不指定,預設值也是`'r'`),然後通過`encoding`引數指定編碼(如果不指定,預設值是None,那麼在讀取檔案時使用的是作業系統預設的編碼),如果不能保證儲存檔案時使用的編碼方式與encoding引數指定的編碼方式是一致的,那麼就可能因無法解碼字元而導致讀取失敗。下面的例子演示瞭如何讀取一個純文字檔案。 ```Python def main(): - f = open('致橡树.txt', 'r', encoding='utf-8') + f = open('致橡樹.txt', 'r', encoding='utf-8') print(f.read()) f.close() @@ -34,20 +34,20 @@ if __name__ == '__main__': ``` -请注意上面的代码,如果`open`函数指定的文件并不存在或者无法打开,那么将引发异常状况导致程序崩溃。为了让代码有一定的健壮性和容错性,我们可以使用Python的异常机制对可能在运行时发生状况的代码进行适当的处理,如下所示。 +請注意上面的程式碼,如果`open`函式指定的檔案並不存在或者無法開啟,那麼將引發異常狀況導致程式崩潰。為了讓程式碼有一定的健壯性和容錯性,我們可以使用Python的異常機制對可能在執行時發生狀況的程式碼進行適當的處理,如下所示。 ```Python def main(): f = None try: - f = open('致橡树.txt', 'r', encoding='utf-8') + f = open('致橡樹.txt', 'r', encoding='utf-8') print(f.read()) except FileNotFoundError: - print('无法打开指定的文件!') + print('無法開啟指定的檔案!') except LookupError: - print('指定了未知的编码!') + print('指定了未知的編碼!') except UnicodeDecodeError: - print('读取文件时解码错误!') + print('讀取檔案時解碼錯誤!') finally: if f: f.close() @@ -58,19 +58,19 @@ if __name__ == '__main__': ``` -在Python中,我们可以将那些在运行时可能会出现状况的代码放在`try`代码块中,在`try`代码块的后面可以跟上一个或多个`except`来捕获可能出现的异常状况。例如在上面读取文件的过程中,文件找不到会引发`FileNotFoundError`,指定了未知的编码会引发`LookupError`,而如果读取文件时无法按指定方式解码会引发`UnicodeDecodeError`,我们在`try`后面跟上了三个`except`分别处理这三种不同的异常状况。最后我们使用`finally`代码块来关闭打开的文件,释放掉程序中获取的外部资源,由于`finally`块的代码不论程序正常还是异常都会执行到(甚至是调用了`sys`模块的`exit`函数退出Python环境,`finally`块都会被执行,因为`exit`函数实质上是引发了`SystemExit`异常),因此我们通常把`finally`块称为“总是执行代码块”,它最适合用来做释放外部资源的操作。如果不愿意在`finally`代码块中关闭文件对象释放资源,也可以使用上下文语法,通过`with`关键字指定文件对象的上下文环境并在离开上下文环境时自动释放文件资源,代码如下所示。 +在Python中,我們可以將那些在執行時可能會出現狀況的程式碼放在`try`程式碼塊中,在`try`程式碼塊的後面可以跟上一個或多個`except`來捕獲可能出現的異常狀況。例如在上面讀取檔案的過程中,檔案找不到會引發`FileNotFoundError`,指定了未知的編碼會引發`LookupError`,而如果讀取檔案時無法按指定方式解碼會引發`UnicodeDecodeError`,我們在`try`後面跟上了三個`except`分別處理這三種不同的異常狀況。最後我們使用`finally`程式碼塊來關閉開啟的檔案,釋放掉程式中獲取的外部資源,由於`finally`塊的程式碼不論程式正常還是異常都會執行到(甚至是呼叫了`sys`模組的`exit`函式退出Python環境,`finally`塊都會被執行,因為`exit`函式實質上是引發了`SystemExit`異常),因此我們通常把`finally`塊稱為“總是執行程式碼塊”,它最適合用來做釋放外部資源的操作。如果不願意在`finally`程式碼塊中關閉檔案物件釋放資源,也可以使用上下文語法,通過`with`關鍵字指定檔案物件的上下文環境並在離開上下文環境時自動釋放檔案資源,程式碼如下所示。 ```Python def main(): try: - with open('致橡树.txt', 'r', encoding='utf-8') as f: + with open('致橡樹.txt', 'r', encoding='utf-8') as f: print(f.read()) except FileNotFoundError: - print('无法打开指定的文件!') + print('無法開啟指定的檔案!') except LookupError: - print('指定了未知的编码!') + print('指定了未知的編碼!') except UnicodeDecodeError: - print('读取文件时解码错误!') + print('讀取檔案時解碼錯誤!') if __name__ == '__main__': @@ -78,26 +78,26 @@ if __name__ == '__main__': ``` -除了使用文件对象的`read`方法读取文件之外,还可以使用`for-in`循环逐行读取或者用`readlines`方法将文件按行读取到一个列表容器中,代码如下所示。 +除了使用檔案物件的`read`方法讀取檔案之外,還可以使用`for-in`迴圈逐行讀取或者用`readlines`方法將檔案按行讀取到一個列表容器中,程式碼如下所示。 ```Python import time def main(): - # 一次性读取整个文件内容 - with open('致橡树.txt', 'r', encoding='utf-8') as f: + # 一次性讀取整個檔案內容 + with open('致橡樹.txt', 'r', encoding='utf-8') as f: print(f.read()) - # 通过for-in循环逐行读取 - with open('致橡树.txt', mode='r') as f: + # 通過for-in迴圈逐行讀取 + with open('致橡樹.txt', mode='r') as f: for line in f: print(line, end='') time.sleep(0.5) print() - # 读取文件按行读取到列表中 - with open('致橡树.txt') as f: + # 讀取檔案按行讀取到列表中 + with open('致橡樹.txt') as f: lines = f.readlines() print(lines) @@ -107,14 +107,14 @@ if __name__ == '__main__': ``` -要将文本信息写入文件文件也非常简单,在使用`open`函数时指定好文件名并将文件模式设置为`'w'`即可。注意如果需要对文件内容进行追加式写入,应该将模式设置为`'a'`。如果要写入的文件不存在会自动创建文件而不是引发异常。下面的例子演示了如何将1~9999直接的素数分别写入三个文件中(1~99之间的素数保存在a.txt中,100~999之间的素数保存在b.txt中,1000~9999之间的素数保存在c.txt中)。 +要將文字資訊寫入檔案檔案也非常簡單,在使用`open`函式時指定好檔名並將檔案模式設定為`'w'`即可。注意如果需要對檔案內容進行追加式寫入,應該將模式設定為`'a'`。如果要寫入的檔案不存在會自動建立檔案而不是引發異常。下面的例子演示瞭如何將1~9999直接的素數分別寫入三個檔案中(1~99之間的素數儲存在a.txt中,100~999之間的素數儲存在b.txt中,1000~9999之間的素數儲存在c.txt中)。 ```Python from math import sqrt def is_prime(n): - """判断素数的函数""" + """判斷素數的函式""" assert n > 0 for factor in range(2, int(sqrt(n)) + 1): if n % factor == 0: @@ -138,7 +138,7 @@ def main(): fs_list[2].write(str(number) + '\n') except IOError as ex: print(ex) - print('写文件时发生错误!') + print('寫檔案時發生錯誤!') finally: for fs in fs_list: fs.close() @@ -150,9 +150,9 @@ if __name__ == '__main__': ``` -### 读写二进制文件 +### 讀寫二進位制檔案 -知道了如何读写文本文件要读写二进制文件也就很简单了,下面的代码实现了复制图片文件的功能。 +知道了如何讀寫文字檔案要讀寫二進位制檔案也就很簡單了,下面的程式碼實現了複製圖片檔案的功能。 ```Python def main(): @@ -163,10 +163,10 @@ def main(): with open('吉多.jpg', 'wb') as fs2: fs2.write(data) except FileNotFoundError as e: - print('指定的文件无法打开.') + print('指定的檔案無法開啟.') except IOError as e: - print('读写文件时出现错误.') - print('程序执行结束.') + print('讀寫檔案時出現錯誤.') + print('程式執行結束.') if __name__ == '__main__': @@ -174,16 +174,16 @@ if __name__ == '__main__': ``` -### 读写JSON文件 +### 讀寫JSON檔案 -通过上面的讲解,我们已经知道如何将文本数据和二进制数据保存到文件中,那么这里还有一个问题,如果希望把一个列表或者一个字典中的数据保存到文件中又该怎么做呢?答案是将数据以JSON格式进行保存。JSON是“JavaScript Object Notation”的缩写,它本来是JavaScript语言中创建对象的一种字面量语法,现在已经被广泛的应用于跨平台跨语言的数据交换,原因很简单,因为JSON也是纯文本,任何系统任何编程语言处理纯文本都是没有问题的。目前JSON基本上已经取代了XML作为异构系统间交换数据的事实标准。关于JSON的知识,更多的可以参考[JSON的官方网站](http://json.org),从这个网站也可以了解到每种语言处理JSON数据格式可以使用的工具或三方库,下面是一个JSON的简单例子。 +通過上面的講解,我們已經知道如何將文字資料和二進位制資料儲存到檔案中,那麼這裡還有一個問題,如果希望把一個列表或者一個字典中的資料儲存到檔案中又該怎麼做呢?答案是將資料以JSON格式進行儲存。JSON是“JavaScript Object Notation”的縮寫,它本來是JavaScript語言中建立物件的一種字面量語法,現在已經被廣泛的應用於跨平臺跨語言的資料交換,原因很簡單,因為JSON也是純文字,任何系統任何程式語言處理純文字都是沒有問題的。目前JSON基本上已經取代了XML作為異構系統間交換資料的事實標準。關於JSON的知識,更多的可以參考[JSON的官方網站](http://json.org),從這個網站也可以瞭解到每種語言處理JSON資料格式可以使用的工具或三方庫,下面是一個JSON的簡單例子。 ```JSON { - 'name': '骆昊', + 'name': '駱昊', 'age': 38, 'qq': 957658, - 'friends': ['王大锤', '白元芳'], + 'friends': ['王大錘', '白元芳'], 'cars': [ {'brand': 'BYD', 'max_speed': 180}, {'brand': 'Audi', 'max_speed': 280}, @@ -192,7 +192,7 @@ if __name__ == '__main__': } ``` -可能大家已经注意到了,上面的JSON跟Python中的字典其实是一样一样的,事实上JSON的数据类型和Python的数据类型是很容易找到对应关系的,如下面两张表所示。 +可能大家已經注意到了,上面的JSON跟Python中的字典其實是一樣一樣的,事實上JSON的資料型別和Python的資料型別是很容易找到對應關係的,如下面兩張表所示。 | JSON | Python | | ------------------- | ------------ | @@ -212,7 +212,7 @@ if __name__ == '__main__': | True / False | true / false | | None | null | -我们使用Python中的json模块就可以将字典或列表以JSON格式保存到文件中,代码如下所示。 +我們使用Python中的json模組就可以將字典或列表以JSON格式儲存到檔案中,程式碼如下所示。 ```Python import json @@ -220,10 +220,10 @@ import json def main(): mydict = { - 'name': '骆昊', + 'name': '駱昊', 'age': 38, 'qq': 957658, - 'friends': ['王大锤', '白元芳'], + 'friends': ['王大錘', '白元芳'], 'cars': [ {'brand': 'BYD', 'max_speed': 180}, {'brand': 'Audi', 'max_speed': 280}, @@ -235,7 +235,7 @@ def main(): json.dump(mydict, fs) except IOError as e: print(e) - print('保存数据完成!') + print('儲存資料完成!') if __name__ == '__main__': @@ -243,16 +243,16 @@ if __name__ == '__main__': ``` -json模块主要有四个比较重要的函数,分别是: +json模組主要有四個比較重要的函式,分別是: -- dump - 将Python对象按照JSON格式序列化到文件中 -- dumps - 将Python对象处理成JSON格式的字符串 -- load - 将文件中的JSON数据反序列化成对象 -- loads - 将字符串的内容反序列化成Python对象 +- dump - 將Python物件按照JSON格式序列化到檔案中 +- dumps - 將Python物件處理成JSON格式的字串 +- load - 將檔案中的JSON資料反序列化成物件 +- loads - 將字串的內容反序列化成Python物件 -这里出现了两个概念,一个叫序列化,一个叫反序列化。自由的百科全书[维基百科](https://zh.wikipedia.org/)上对这两个概念是这样解释的:“序列化(serialization)在计算机科学的数据处理中,是指将数据结构或对象状态转换为可以存储或传输的形式,这样在需要的时候能够恢复到原先的状态,而且通过序列化的数据重新获取字节时,可以利用这些字节来产生原始对象的副本(拷贝)。与这个过程相反的动作,即从一系列字节中提取数据结构的操作,就是反序列化(deserialization)”。 +這裡出現了兩個概念,一個叫序列化,一個叫反序列化。自由的百科全書[維基百科](https://zh.wikipedia.org/)上對這兩個概念是這樣解釋的:“序列化(serialization)在電腦科學的資料處理中,是指將資料結構或物件狀態轉換為可以儲存或傳輸的形式,這樣在需要的時候能夠恢復到原先的狀態,而且通過序列化的資料重新獲取位元組時,可以利用這些位元組來產生原始物件的副本(拷貝)。與這個過程相反的動作,即從一系列位元組中提取資料結構的操作,就是反序列化(deserialization)”。 -目前绝大多数网络数据服务(或称之为网络API)都是基于[HTTP协议](https://zh.wikipedia.org/wiki/%E8%B6%85%E6%96%87%E6%9C%AC%E4%BC%A0%E8%BE%93%E5%8D%8F%E8%AE%AE)提供JSON格式的数据,关于HTTP协议的相关知识,可以看看阮一峰老师的[《HTTP协议入门》](http://www.ruanyifeng.com/blog/2016/08/http.html),如果想了解国内的网络数据服务,可以看看[聚合数据](https://www.juhe.cn/)和[阿凡达数据](http://www.avatardata.cn/)等网站,国外的可以看看[{API}Search](http://apis.io/)网站。下面的例子演示了如何使用requests模块(封装得足够好的第三方网络访问模块)访问网络API获取国内新闻,如何通过json模块解析JSON数据并显示新闻标题,这个例子使用了[天行数据](https://www.tianapi.com/)提供的国内新闻数据接口,其中的APIKey需要自己到该网站申请。 +目前絕大多數網路資料服務(或稱之為網路API)都是基於[HTTP協議](https://zh.wikipedia.org/wiki/%E8%B6%85%E6%96%87%E6%9C%AC%E4%BC%A0%E8%BE%93%E5%8D%8F%E8%AE%AE)提供JSON格式的資料,關於HTTP協議的相關知識,可以看看阮一峰老師的[《HTTP協議入門》](http://www.ruanyifeng.com/blog/2016/08/http.html),如果想了解國內的網路資料服務,可以看看[聚合資料](https://www.juhe.cn/)和[阿凡達資料](http://www.avatardata.cn/)等網站,國外的可以看看[{API}Search](http://apis.io/)網站。下面的例子演示瞭如何使用requests模組(封裝得足夠好的第三方網路訪問模組)訪問網路API獲取國內新聞,如何通過json模組解析JSON資料並顯示新聞標題,這個例子使用了[天行資料](https://www.tianapi.com/)提供的國內新聞資料介面,其中的APIKey需要自己到該網站申請。 ```Python import requests @@ -271,4 +271,4 @@ if __name__ == '__main__': ``` -在Python中要实现序列化和反序列化除了使用json模块之外,还可以使用pickle和shelve模块,但是这两个模块是使用特有的序列化协议来序列化数据,因此序列化后的数据只能被Python识别。关于这两个模块的相关知识可以自己看看网络上的资料。另外,如果要了解更多的关于Python异常机制的知识,可以看看segmentfault上面的文章[《总结:Python中的异常处理》](https://segmentfault.com/a/1190000007736783),这篇文章不仅介绍了Python中异常机制的使用,还总结了一系列的最佳实践,很值得一读。 \ No newline at end of file +在Python中要實現序列化和反序列化除了使用json模組之外,還可以使用pickle和shelve模組,但是這兩個模組是使用特有的序列化協議來序列化資料,因此序列化後的資料只能被Python識別。關於這兩個模組的相關知識可以自己看看網路上的資料。另外,如果要了解更多的關於Python異常機制的知識,可以看看segmentfault上面的文章[《總結:Python中的異常處理》](https://segmentfault.com/a/1190000007736783),這篇文章不僅介紹了Python中異常機制的使用,還總結了一系列的最佳實踐,很值得一讀。 \ No newline at end of file diff --git "a/Day01-15/Day12/\345\255\227\347\254\246\344\270\262\345\222\214\346\255\243\345\210\231\350\241\250\350\276\276\345\274\217.md" "b/Day01-15/Day12/\345\255\227\347\254\246\344\270\262\345\222\214\346\255\243\345\210\231\350\241\250\350\276\276\345\274\217.md" index d0075ce92..8715884a7 100644 --- "a/Day01-15/Day12/\345\255\227\347\254\246\344\270\262\345\222\214\346\255\243\345\210\231\350\241\250\350\276\276\345\274\217.md" +++ "b/Day01-15/Day12/\345\255\227\347\254\246\344\270\262\345\222\214\346\255\243\345\210\231\350\241\250\350\276\276\345\274\217.md" @@ -1,28 +1,28 @@ -## 使用正则表达式 +## 使用正則表示式 -### 正则表达式相关知识 +### 正則表示式相關知識 -在编写处理字符串的程序或网页时,经常会有查找符合某些复杂规则的字符串的需要,正则表达式就是用于描述这些规则的工具,换句话说正则表达式是一种工具,它定义了字符串的匹配模式(如何检查一个字符串是否有跟某种模式匹配的部分或者从一个字符串中将与模式匹配的部分提取出来或者替换掉)。如果你在Windows操作系统中使用过文件查找并且在指定文件名时使用过通配符(\*和?),那么正则表达式也是与之类似的用来进行文本匹配的工具,只不过比起通配符正则表达式更强大,它能更精确地描述你的需求(当然你付出的代价是书写一个正则表达式比打出一个通配符要复杂得多,要知道任何给你带来好处的东西都是有代价的,就如同学习一门编程语言一样),比如你可以编写一个正则表达式,用来查找所有以0开头,后面跟着2-3个数字,然后是一个连字号“-”,最后是7或8位数字的字符串(像028-12345678或0813-7654321),这不就是国内的座机号码吗。最初计算机是为了做数学运算而诞生的,处理的信息基本上都是数值,而今天我们在日常工作中处理的信息基本上都是文本数据,我们希望计算机能够识别和处理符合某些模式的文本,正则表达式就显得非常重要了。今天几乎所有的编程语言都提供了对正则表达式操作的支持,Python通过标准库中的re模块来支持正则表达式操作。 +在編寫處理字串的程式或網頁時,經常會有查詢符合某些複雜規則的字串的需要,正則表示式就是用於描述這些規則的工具,換句話說正則表示式是一種工具,它定義了字串的匹配模式(如何檢查一個字串是否有跟某種模式匹配的部分或者從一個字串中將與模式匹配的部分提取出來或者替換掉)。如果你在Windows作業系統中使用過檔案查詢並且在指定檔名時使用過萬用字元(\*和?),那麼正則表示式也是與之類似的用來進行文字匹配的工具,只不過比起萬用字元正則表示式更強大,它能更精確地描述你的需求(當然你付出的代價是書寫一個正則表示式比打出一個萬用字元要複雜得多,要知道任何給你帶來好處的東西都是有代價的,就如同學習一門程式語言一樣),比如你可以編寫一個正則表示式,用來查詢所有以0開頭,後面跟著2-3個數字,然後是一個連字號“-”,最後是7或8位數字的字串(像028-12345678或0813-7654321),這不就是國內的座機號碼嗎。最初計算機是為了做數學運算而誕生的,處理的資訊基本上都是數值,而今天我們在日常工作中處理的資訊基本上都是文字資料,我們希望計算機能夠識別和處理符合某些模式的文字,正則表示式就顯得非常重要了。今天幾乎所有的程式語言都提供了對正則表示式操作的支援,Python通過標準庫中的re模組來支援正則表示式操作。 -我们可以考虑下面一个问题:我们从某个地方(可能是一个文本文件,也可能是网络上的一则新闻)获得了一个字符串,希望在字符串中找出手机号和座机号。当然我们可以设定手机号是11位的数字(注意并不是随机的11位数字,因为你没有见过“25012345678”这样的手机号吧)而座机号跟上一段中描述的模式相同,如果不使用正则表达式要完成这个任务就会很麻烦。 +我們可以考慮下面一個問題:我們從某個地方(可能是一個文字檔案,也可能是網路上的一則新聞)獲得了一個字串,希望在字串中找出手機號和座機號。當然我們可以設定手機號是11位的數字(注意並不是隨機的11位數字,因為你沒有見過“25012345678”這樣的手機號吧)而座機號跟上一段中描述的模式相同,如果不使用正則表示式要完成這個任務就會很麻煩。 -关于正则表达式的相关知识,大家可以阅读一篇非常有名的博客叫[《正则表达式30分钟入门教程》](https://deerchao.net/tutorials/regex/regex.htm),读完这篇文章后你就可以看懂下面的表格,这是我们对正则表达式中的一些基本符号进行的扼要总结。 +關於正則表示式的相關知識,大家可以閱讀一篇非常有名的部落格叫[《正則表示式30分鐘入門教程》](https://deerchao.net/tutorials/regex/regex.htm),讀完這篇文章後你就可以看懂下面的表格,這是我們對正則表示式中的一些基本符號進行的扼要總結。 -| 符号 | 解释 | 示例 | 说明 | +| 符號 | 解釋 | 示例 | 說明 | | ------------------ | ----------------------------------------- | ---------------- | -------------------------------------------------- | -| . | 匹配任意字符 | b.t | 可以匹配bat / but / b#t / b1t等 | -| \\w | 匹配字母/数字/下划线 | b\\wt | 可以匹配bat / b1t / b_t等
但不能匹配b#t | -| \\s | 匹配空白字符(包括\r、\n、\t等) | love\\syou | 可以匹配love you | -| \\d | 匹配数字 | \\d\\d | 可以匹配01 / 23 / 99等 | -| \\b | 匹配单词的边界 | \\bThe\\b | | -| ^ | 匹配字符串的开始 | ^The | 可以匹配The开头的字符串 | -| $ | 匹配字符串的结束 | .exe$ | 可以匹配.exe结尾的字符串 | -| \\W | 匹配非字母/数字/下划线 | b\\Wt | 可以匹配b#t / b@t等
但不能匹配but / b1t / b_t等 | -| \\S | 匹配非空白字符 | love\\Syou | 可以匹配love#you等
但不能匹配love you | -| \\D | 匹配非数字 | \\d\\D | 可以匹配9a / 3# / 0F等 | -| \\B | 匹配非单词边界 | \\Bio\\B | | -| [] | 匹配来自字符集的任意单一字符 | [aeiou] | 可以匹配任一元音字母字符 | -| [^] | 匹配不在字符集中的任意单一字符 | [^aeiou] | 可以匹配任一非元音字母字符 | +| . | 匹配任意字元 | b.t | 可以匹配bat / but / b#t / b1t等 | +| \\w | 匹配字母/數字/下劃線 | b\\wt | 可以匹配bat / b1t / b_t等
但不能匹配b#t | +| \\s | 匹配空白字元(包括\r、\n、\t等) | love\\syou | 可以匹配love you | +| \\d | 匹配數字 | \\d\\d | 可以匹配01 / 23 / 99等 | +| \\b | 匹配單詞的邊界 | \\bThe\\b | | +| ^ | 匹配字串的開始 | ^The | 可以匹配The開頭的字串 | +| $ | 匹配字串的結束 | .exe$ | 可以匹配.exe結尾的字串 | +| \\W | 匹配非字母/數字/下劃線 | b\\Wt | 可以匹配b#t / b@t等
但不能匹配but / b1t / b_t等 | +| \\S | 匹配非空白字元 | love\\Syou | 可以匹配love#you等
但不能匹配love you | +| \\D | 匹配非數字 | \\d\\D | 可以匹配9a / 3# / 0F等 | +| \\B | 匹配非單詞邊界 | \\Bio\\B | | +| [] | 匹配來自字符集的任意單一字元 | [aeiou] | 可以匹配任一母音字母字元 | +| [^] | 匹配不在字符集中的任意單一字元 | [^aeiou] | 可以匹配任一非母音字母字元 | | * | 匹配0次或多次 | \\w* | | | + | 匹配1次或多次 | \\w+ | | | ? | 匹配0次或1次 | \\w? | | @@ -30,54 +30,54 @@ | {M,} | 匹配至少M次 | \\w{3,} | | | {M,N} | 匹配至少M次至多N次 | \\w{3,6} | | | \| | 分支 | foo\|bar | 可以匹配foo或者bar | -| (?#) | 注释 | | | -| (exp) | 匹配exp并捕获到自动命名的组中 | | | -| (?<name>exp) | 匹配exp并捕获到名为name的组中 | | | -| (?:exp) | 匹配exp但是不捕获匹配的文本 | | | +| (?#) | 註釋 | | | +| (exp) | 匹配exp並捕獲到自動命名的組中 | | | +| (?<name>exp) | 匹配exp並捕獲到名為name的組中 | | | +| (?:exp) | 匹配exp但是不捕獲匹配的文字 | | | | (?=exp) | 匹配exp前面的位置 | \\b\\w+(?=ing) | 可以匹配I'm dancing中的danc | -| (?<=exp) | 匹配exp后面的位置 | (?<=\\bdanc)\\w+\\b | 可以匹配I love dancing and reading中的第一个ing | -| (?!exp) | 匹配后面不是exp的位置 | | | +| (?<=exp) | 匹配exp後面的位置 | (?<=\\bdanc)\\w+\\b | 可以匹配I love dancing and reading中的第一個ing | +| (?!exp) | 匹配後面不是exp的位置 | | | | (?a.\*?b | 将正则表达式应用于aabab,前者会匹配整个字符串aabab,后者会匹配aab和ab两个字符串 | -| +? | 重复1次或多次,但尽可能少重复 | | | -| ?? | 重复0次或1次,但尽可能少重复 | | | -| {M,N}? | 重复M到N次,但尽可能少重复 | | | -| {M,}? | 重复M次以上,但尽可能少重复 | | | +| *? | 重複任意次,但儘可能少重複 | a.\*b
a.\*?b | 將正則表示式應用於aabab,前者會匹配整個字串aabab,後者會匹配aab和ab兩個字串 | +| +? | 重複1次或多次,但儘可能少重複 | | | +| ?? | 重複0次或1次,但儘可能少重複 | | | +| {M,N}? | 重複M到N次,但儘可能少重複 | | | +| {M,}? | 重複M次以上,但儘可能少重複 | | | -> **说明:**如果需要匹配的字符是正则表达式中的特殊字符,那么可以使用\\进行转义处理,例如想匹配小数点可以写成\\.就可以了,因为直接写.会匹配任意字符;同理,想匹配圆括号必须写成\\(和\\),否则圆括号被视为正则表达式中的分组。 +> **說明:**如果需要匹配的字元是正則表示式中的特殊字元,那麼可以使用\\進行轉義處理,例如想匹配小數點可以寫成\\.就可以了,因為直接寫.會匹配任意字元;同理,想匹配圓括號必須寫成\\(和\\),否則圓括號被視為正則表示式中的分組。 -### Python对正则表达式的支持 +### Python對正則表示式的支援 -Python提供了re模块来支持正则表达式相关操作,下面是re模块中的核心函数。 +Python提供了re模組來支援正則表示式相關操作,下面是re模組中的核心函式。 -| 函数 | 说明 | +| 函式 | 說明 | | -------------------------------------------- | ------------------------------------------------------------ | -| compile(pattern, flags=0) | 编译正则表达式返回正则表达式对象 | -| match(pattern, string, flags=0) | 用正则表达式匹配字符串 成功返回匹配对象 否则返回None | -| search(pattern, string, flags=0) | 搜索字符串中第一次出现正则表达式的模式 成功返回匹配对象 否则返回None | -| split(pattern, string, maxsplit=0, flags=0) | 用正则表达式指定的模式分隔符拆分字符串 返回列表 | -| sub(pattern, repl, string, count=0, flags=0) | 用指定的字符串替换原字符串中与正则表达式匹配的模式 可以用count指定替换的次数 | -| fullmatch(pattern, string, flags=0) | match函数的完全匹配(从字符串开头到结尾)版本 | -| findall(pattern, string, flags=0) | 查找字符串所有与正则表达式匹配的模式 返回字符串的列表 | -| finditer(pattern, string, flags=0) | 查找字符串所有与正则表达式匹配的模式 返回一个迭代器 | -| purge() | 清除隐式编译的正则表达式的缓存 | -| re.I / re.IGNORECASE | 忽略大小写匹配标记 | -| re.M / re.MULTILINE | 多行匹配标记 | +| compile(pattern, flags=0) | 編譯正則表示式返回正則表示式物件 | +| match(pattern, string, flags=0) | 用正則表示式匹配字串 成功返回匹配物件 否則返回None | +| search(pattern, string, flags=0) | 搜尋字串中第一次出現正則表示式的模式 成功返回匹配物件 否則返回None | +| split(pattern, string, maxsplit=0, flags=0) | 用正則表示式指定的模式分隔符拆分字串 返回列表 | +| sub(pattern, repl, string, count=0, flags=0) | 用指定的字串替換原字串中與正則表示式匹配的模式 可以用count指定替換的次數 | +| fullmatch(pattern, string, flags=0) | match函式的完全匹配(從字串開頭到結尾)版本 | +| findall(pattern, string, flags=0) | 查詢字串所有與正則表示式匹配的模式 返回字串的列表 | +| finditer(pattern, string, flags=0) | 查詢字串所有與正則表示式匹配的模式 返回一個迭代器 | +| purge() | 清除隱式編譯的正則表示式的快取 | +| re.I / re.IGNORECASE | 忽略大小寫匹配標記 | +| re.M / re.MULTILINE | 多行匹配標記 | -> **说明:**上面提到的re模块中的这些函数,实际开发中也可以用正则表达式对象的方法替代对这些函数的使用,如果一个正则表达式需要重复的使用,那么先通过compile函数编译正则表达式并创建出正则表达式对象无疑是更为明智的选择。 +> **說明:**上面提到的re模組中的這些函式,實際開發中也可以用正則表示式物件的方法替代對這些函式的使用,如果一個正則表示式需要重複的使用,那麼先通過compile函式編譯正則表示式並創建出正則表示式物件無疑是更為明智的選擇。 -下面我们通过一系列的例子来告诉大家在Python中如何使用正则表达式。 +下面我們通過一系列的例子來告訴大家在Python中如何使用正則表示式。 -#### 例子1:验证输入用户名和QQ号是否有效并给出对应的提示信息。 +#### 例子1:驗證輸入使用者名稱和QQ號是否有效並給出對應的提示資訊。 ```Python """ -验证输入用户名和QQ号是否有效并给出对应的提示信息 +驗證輸入使用者名稱和QQ號是否有效並給出對應的提示資訊 要求: -用户名必须由字母、数字或下划线构成且长度在6~20个字符之间 -QQ号是5~12的数字且首位不能为0 +使用者名稱必須由字母、數字或下劃線構成且長度在6~20個字元之間 +QQ號是5~12的數字且首位不能為0 """ @@ -85,18 +85,18 @@ import re def main(): - username = input('请输入用户名: ') - qq = input('请输入QQ号: ') - # match函数的第一个参数是正则表达式字符串或正则表达式对象 - # 第二个参数是要跟正则表达式做匹配的字符串对象 + username = input('請輸入使用者名稱: ') + qq = input('請輸入QQ號: ') + # match函式的第一個引數是正則表示式字串或正則表示式物件 + # 第二個引數是要跟正則表示式做匹配的字串物件 m1 = re.match(r'^[0-9a-zA-Z_]{6,20}$', username) if not m1: - print('请输入有效的用户名.') + print('請輸入有效的使用者名稱.') m2 = re.match(r'^[1-9]\d{4,11}$', qq) if not m2: - print('请输入有效的QQ号.') + print('請輸入有效的QQ號.') if m1 and m2: - print('你输入的信息是有效的!') + print('你輸入的資訊是有效的!') if __name__ == '__main__': @@ -104,11 +104,11 @@ if __name__ == '__main__': ``` -> **提示**:上面在书写正则表达式时使用了“原始字符串”的写法(在字符串前面加上了r),所谓“原始字符串”就是字符串中的每个字符都是它原始的意义,说得更直接一点就是字符串中没有所谓的转义字符啦。因为正则表达式中有很多元字符和需要进行转义的地方,如果不使用原始字符串就需要将反斜杠写作\\\\,例如表示数字的\\d得书写成\\\\d,这样不仅写起来不方便,阅读的时候也会很吃力。 +> **提示**:上面在書寫正則表示式時使用了“原始字串”的寫法(在字串前面加上了r),所謂“原始字串”就是字串中的每個字元都是它原始的意義,說得更直接一點就是字串中沒有所謂的轉義字元啦。因為正則表示式中有很多元字元和需要進行轉義的地方,如果不使用原始字串就需要將反斜槓寫作\\\\,例如表示數字的\\d得書寫成\\\\d,這樣不僅寫起來不方便,閱讀的時候也會很吃力。 -#### 例子2:从一段文字中提取出国内手机号码。 +#### 例子2:從一段文字中提取出國內手機號碼。 -下面这张图是截止到2017年底,国内三家运营商推出的手机号段。 +下面這張圖是截止到2017年底,國內三家運營商推出的手機號段。 ![](./res/tel-start-number.png) @@ -117,21 +117,21 @@ import re def main(): - # 创建正则表达式对象 使用了前瞻和回顾来保证手机号前后不应该出现数字 + # 建立正則表示式物件 使用了前瞻和回顧來保證手機號前後不應該出現數字 pattern = re.compile(r'(?<=\D)1[34578]\d{9}(?=\D)') sentence = ''' - 重要的事情说8130123456789遍,我的手机号是13512346789这个靓号, - 不是15600998765,也是110或119,王大锤的手机号才是15600998765。 + 重要的事情說8130123456789遍,我的手機號是13512346789這個靚號, + 不是15600998765,也是110或119,王大錘的手機號才是15600998765。 ''' - # 查找所有匹配并保存到一个列表中 + # 查詢所有匹配並儲存到一個列表中 mylist = re.findall(pattern, sentence) print(mylist) - print('--------华丽的分隔线--------') - # 通过迭代器取出匹配对象并获得匹配的内容 + print('--------華麗的分隔線--------') + # 通過迭代器取出匹配物件並獲得匹配的內容 for temp in pattern.finditer(sentence): print(temp.group()) - print('--------华丽的分隔线--------') - # 通过search函数指定搜索位置找出所有匹配 + print('--------華麗的分隔線--------') + # 通過search函式指定搜尋位置找出所有匹配 m = pattern.search(sentence) while m: print(m.group()) @@ -143,19 +143,19 @@ if __name__ == '__main__': ``` -> **说明**:上面匹配国内手机号的正则表达式并不够好,因为像14开头的号码只有145或147,而上面的正则表达式并没有考虑这种情况,要匹配国内手机号,更好的正则表达式的写法是:`(?<=\D)(1[38]\d{9}|14[57]\d{8}|15[0-35-9]\d{8}|17[678]\d{8})(?=\D)`,国内最近好像有19和16开头的手机号了,但是这个暂时不在我们考虑之列。 +> **說明**:上面匹配國內手機號的正則表示式並不夠好,因為像14開頭的號碼只有145或147,而上面的正則表示式並沒有考慮這種情況,要匹配國內手機號,更好的正則表示式的寫法是:`(?<=\D)(1[38]\d{9}|14[57]\d{8}|15[0-35-9]\d{8}|17[678]\d{8})(?=\D)`,國內最近好像有19和16開頭的手機號了,但是這個暫時不在我們考慮之列。 -#### 例子3:替换字符串中的不良内容 +#### 例子3:替換字串中的不良內容 ```Python import re def main(): - sentence = '你丫是傻叉吗? 我操你大爷的. Fuck you.' - purified = re.sub('[操肏艹草曹]|fuck|shit|傻[比屄逼叉缺吊屌]|煞笔', + sentence = '你丫是傻叉嗎? 我操你大爺的. Fuck you.' + purified = re.sub('[操肏艹草曹]|fuck|shit|傻[比屄逼叉缺吊屌]|煞筆', '*', sentence, flags=re.IGNORECASE) - print(purified) # 你丫是*吗? 我*你大爷的. * you. + print(purified) # 你丫是*嗎? 我*你大爺的. * you. if __name__ == '__main__': @@ -163,20 +163,20 @@ if __name__ == '__main__': ``` -> **说明**:re模块的正则表达式相关函数中都有一个flags参数,它代表了正则表达式的匹配标记,可以通过该标记来指定匹配时是否忽略大小写、是否进行多行匹配、是否显示调试信息等。如果需要为flags参数指定多个值,可以使用[按位或运算符](http://www.runoob.com/python/python-operators.html#ysf5)进行叠加,如`flags=re.I | re.M`。 +> **說明**:re模組的正則表示式相關函式中都有一個flags引數,它代表了正則表示式的匹配標記,可以通過該標記來指定匹配時是否忽略大小寫、是否進行多行匹配、是否顯示除錯資訊等。如果需要為flags引數指定多個值,可以使用[按位或運算子](http://www.runoob.com/python/python-operators.html#ysf5)進行疊加,如`flags=re.I | re.M`。 -#### 例子4:拆分长字符串 +#### 例子4:拆分長字串 ```Python import re def main(): - poem = '窗前明月光,疑是地上霜。举头望明月,低头思故乡。' + poem = '窗前明月光,疑是地上霜。舉頭望明月,低頭思故鄉。' sentence_list = re.split(r'[,。, .]', poem) while '' in sentence_list: sentence_list.remove('') - print(sentence_list) # ['窗前明月光', '疑是地上霜', '举头望明月', '低头思故乡'] + print(sentence_list) # ['窗前明月光', '疑是地上霜', '舉頭望明月', '低頭思故鄉'] if __name__ == '__main__': @@ -184,6 +184,6 @@ if __name__ == '__main__': ``` -### 后话 +### 後話 -如果要从事爬虫类应用的开发,那么正则表达式一定是一个非常好的助手,因为它可以帮助我们迅速的从网页代码中发现某种我们指定的模式并提取出我们需要的信息,当然对于初学者来收,要编写一个正确的适当的正则表达式可能并不是一件容易的事情(当然有些常用的正则表达式可以直接在网上找找),所以实际开发爬虫应用的时候,有很多人会选择[Beautiful Soup](https://www.crummy.com/software/BeautifulSoup/)或[Lxml](http://lxml.de/)来进行匹配和信息的提取,前者简单方便但是性能较差,后者既好用性能也好,但是安装稍嫌麻烦,这些内容我们会在后期的爬虫专题中为大家介绍。 \ No newline at end of file +如果要從事爬蟲類應用的開發,那麼正則表示式一定是一個非常好的助手,因為它可以幫助我們迅速的從網頁程式碼中發現某種我們指定的模式並提取出我們需要的資訊,當然對於初學者來收,要編寫一個正確的適當的正則表示式可能並不是一件容易的事情(當然有些常用的正則表示式可以直接在網上找找),所以實際開發爬蟲應用的時候,有很多人會選擇[Beautiful Soup](https://www.crummy.com/software/BeautifulSoup/)或[Lxml](http://lxml.de/)來進行匹配和資訊的提取,前者簡單方便但是效能較差,後者既好用效能也好,但是安裝稍嫌麻煩,這些內容我們會在後期的爬蟲專題中為大家介紹。 \ No newline at end of file diff --git "a/Day01-15/Day13/\350\277\233\347\250\213\345\222\214\347\272\277\347\250\213.md" "b/Day01-15/Day13/\350\277\233\347\250\213\345\222\214\347\272\277\347\250\213.md" index 89d6ad665..d03893e35 100644 --- "a/Day01-15/Day13/\350\277\233\347\250\213\345\222\214\347\272\277\347\250\213.md" +++ "b/Day01-15/Day13/\350\277\233\347\250\213\345\222\214\347\272\277\347\250\213.md" @@ -1,24 +1,24 @@ -## 进程和线程 +## 程序和執行緒 -今天我们使用的计算机早已进入多CPU或多核时代,而我们使用的操作系统都是支持“多任务”的操作系统,这使得我们可以同时运行多个程序,也可以将一个程序分解为若干个相对独立的子任务,让多个子任务并发的执行,从而缩短程序的执行时间,同时也让用户获得更好的体验。因此在当下不管是用什么编程语言进行开发,实现让程序同时执行多个任务也就是常说的“并发编程”,应该是程序员必备技能之一。为此,我们需要先讨论两个概念,一个叫进程,一个叫线程。 +今天我們使用的計算機早已進入多CPU或多核時代,而我們使用的作業系統都是支援“多工”的作業系統,這使得我們可以同時執行多個程式,也可以將一個程式分解為若干個相對獨立的子任務,讓多個子任務併發的執行,從而縮短程式的執行時間,同時也讓使用者獲得更好的體驗。因此在當下不管是用什麼程式語言進行開發,實現讓程式同時執行多個任務也就是常說的“併發程式設計”,應該是程式設計師必備技能之一。為此,我們需要先討論兩個概念,一個叫程序,一個叫執行緒。 ### 概念 -进程就是操作系统中执行的一个程序,操作系统以进程为单位分配存储空间,每个进程都有自己的地址空间、数据栈以及其他用于跟踪进程执行的辅助数据,操作系统管理所有进程的执行,为它们合理的分配资源。进程可以通过fork或spawn的方式来创建新的进程来执行其他的任务,不过新的进程也有自己独立的内存空间,因此必须通过进程间通信机制(IPC,Inter-Process Communication)来实现数据共享,具体的方式包括管道、信号、套接字、共享内存区等。 +程序就是作業系統中執行的一個程式,作業系統以程序為單位分配儲存空間,每個程序都有自己的地址空間、資料棧以及其他用於跟蹤程序執行的輔助資料,作業系統管理所有程序的執行,為它們合理的分配資源。程序可以通過fork或spawn的方式來建立新的程序來執行其他的任務,不過新的程序也有自己獨立的記憶體空間,因此必須通過程序間通訊機制(IPC,Inter-Process Communication)來實現資料共享,具體的方式包括管道、訊號、套接字、共享記憶體區等。 -一个进程还可以拥有多个并发的执行线索,简单的说就是拥有多个可以获得CPU调度的执行单元,这就是所谓的线程。由于线程在同一个进程下,它们可以共享相同的上下文,因此相对于进程而言,线程间的信息共享和通信更加容易。当然在单核CPU系统中,真正的并发是不可能的,因为在某个时刻能够获得CPU的只有唯一的一个线程,多个线程共享了CPU的执行时间。使用多线程实现并发编程为程序带来的好处是不言而喻的,最主要的体现在提升程序的性能和改善用户体验,今天我们使用的软件几乎都用到了多线程技术,这一点可以利用系统自带的进程监控工具(如macOS中的“活动监视器”、Windows中的“任务管理器”)来证实,如下图所示。 +一個程序還可以擁有多個併發的執行線索,簡單的說就是擁有多個可以獲得CPU排程的執行單元,這就是所謂的執行緒。由於執行緒在同一個程序下,它們可以共享相同的上下文,因此相對於程序而言,執行緒間的資訊共享和通訊更加容易。當然在單核CPU系統中,真正的併發是不可能的,因為在某個時刻能夠獲得CPU的只有唯一的一個執行緒,多個執行緒共享了CPU的執行時間。使用多執行緒實現併發程式設計為程式帶來的好處是不言而喻的,最主要的體現在提升程式的效能和改善使用者體驗,今天我們使用的軟體幾乎都用到了多執行緒技術,這一點可以利用系統自帶的程序監控工具(如macOS中的“活動監視器”、Windows中的“工作管理員”)來證實,如下圖所示。 ![](./res/macos-monitor.png) -当然多线程也并不是没有坏处,站在其他进程的角度,多线程的程序对其他程序并不友好,因为它占用了更多的CPU执行时间,导致其他程序无法获得足够的CPU执行时间;另一方面,站在开发者的角度,编写和调试多线程的程序都对开发者有较高的要求,对于初学者来说更加困难。 +當然多執行緒也並不是沒有壞處,站在其他程序的角度,多執行緒的程式對其他程式並不友好,因為它佔用了更多的CPU執行時間,導致其他程式無法獲得足夠的CPU執行時間;另一方面,站在開發者的角度,編寫和除錯多執行緒的程式都對開發者有較高的要求,對於初學者來說更加困難。 -Python既支持多进程又支持多线程,因此使用Python实现并发编程主要有3种方式:多进程、多线程、多进程+多线程。 +Python既支援多程序又支援多執行緒,因此使用Python實現併發程式設計主要有3種方式:多程序、多執行緒、多程序+多執行緒。 -### Python中的多进程 +### Python中的多程序 -Unix和Linux操作系统上提供了`fork()`系统调用来创建进程,调用`fork()`函数的是父进程,创建出的是子进程,子进程是父进程的一个拷贝,但是子进程拥有自己的PID。`fork()`函数非常特殊它会返回两次,父进程中可以通过`fork()`函数的返回值得到子进程的PID,而子进程中的返回值永远都是0。Python的os模块提供了`fork()`函数。由于Windows系统没有`fork()`调用,因此要实现跨平台的多进程编程,可以使用multiprocessing模块的`Process`类来创建子进程,而且该模块还提供了更高级的封装,例如批量启动进程的进程池(`Pool`)、用于进程间通信的队列(`Queue`)和管道(`Pipe`)等。 +Unix和Linux作業系統上提供了`fork()`系統呼叫來建立程序,呼叫`fork()`函式的是父程序,創建出的是子程序,子程序是父程序的一個拷貝,但是子程序擁有自己的PID。`fork()`函式非常特殊它會返回兩次,父程序中可以通過`fork()`函式的返回值得到子程序的PID,而子程序中的返回值永遠都是0。Python的os模組提供了`fork()`函式。由於Windows系統沒有`fork()`呼叫,因此要實現跨平臺的多程序程式設計,可以使用multiprocessing模組的`Process`類來建立子程序,而且該模組還提供了更高階的封裝,例如批量啟動程序的程序池(`Pool`)、用於程序間通訊的佇列(`Queue`)和管道(`Pipe`)等。 -下面用一个下载文件的例子来说明使用多进程和不使用多进程到底有什么差别,先看看下面的代码。 +下面用一個下載檔案的例子來說明使用多程序和不使用多程序到底有什麼差別,先看看下面的程式碼。 ```Python from random import randint @@ -26,18 +26,18 @@ from time import time, sleep def download_task(filename): - print('开始下载%s...' % filename) + print('開始下載%s...' % filename) time_to_download = randint(5, 10) sleep(time_to_download) - print('%s下载完成! 耗费了%d秒' % (filename, time_to_download)) + print('%s下載完成! 耗費了%d秒' % (filename, time_to_download)) def main(): start = time() - download_task('Python从入门到住院.pdf') + download_task('Python從入門到住院.pdf') download_task('Peking Hot.avi') end = time() - print('总共耗费了%.2f秒.' % (end - start)) + print('總共耗費了%.2f秒.' % (end - start)) if __name__ == '__main__': @@ -45,17 +45,17 @@ if __name__ == '__main__': ``` -下面是运行程序得到的一次运行结果。 +下面是執行程式得到的一次執行結果。 ```Shell -开始下载Python从入门到住院.pdf... -Python从入门到住院.pdf下载完成! 耗费了6秒 -开始下载Peking Hot.avi... -Peking Hot.avi下载完成! 耗费了7秒 -总共耗费了13.01秒. +開始下載Python從入門到住院.pdf... +Python從入門到住院.pdf下載完成! 耗費了6秒 +開始下載Peking Hot.avi... +Peking Hot.avi下載完成! 耗費了7秒 +總共耗費了13.01秒. ``` -从上面的例子可以看出,如果程序中的代码只能按顺序一点点的往下执行,那么即使执行两个毫不相关的下载任务,也需要先等待一个文件下载完成后才能开始下一个下载任务,很显然这并不合理也没有效率。接下来我们使用多进程的方式将两个下载任务放到不同的进程中,代码如下所示。 +從上面的例子可以看出,如果程式中的程式碼只能按順序一點點的往下執行,那麼即使執行兩個毫不相關的下載任務,也需要先等待一個檔案下載完成後才能開始下一個下載任務,很顯然這並不合理也沒有效率。接下來我們使用多程序的方式將兩個下載任務放到不同的程序中,程式碼如下所示。 ```Python from multiprocessing import Process @@ -65,23 +65,23 @@ from time import time, sleep def download_task(filename): - print('启动下载进程,进程号[%d].' % getpid()) - print('开始下载%s...' % filename) + print('啟動下載程序,程序號[%d].' % getpid()) + print('開始下載%s...' % filename) time_to_download = randint(5, 10) sleep(time_to_download) - print('%s下载完成! 耗费了%d秒' % (filename, time_to_download)) + print('%s下載完成! 耗費了%d秒' % (filename, time_to_download)) def main(): start = time() - p1 = Process(target=download_task, args=('Python从入门到住院.pdf', )) + p1 = Process(target=download_task, args=('Python從入門到住院.pdf', )) p1.start() p2 = Process(target=download_task, args=('Peking Hot.avi', )) p2.start() p1.join() p2.join() end = time() - print('总共耗费了%.2f秒.' % (end - start)) + print('總共耗費了%.2f秒.' % (end - start)) if __name__ == '__main__': @@ -89,19 +89,19 @@ if __name__ == '__main__': ``` -在上面的代码中,我们通过`Process`类创建了进程对象,通过`target`参数我们传入一个函数来表示进程启动后要执行的代码,后面的`args`是一个元组,它代表了传递给函数的参数。`Process`对象的`start`方法用来启动进程,而`join`方法表示等待进程执行结束。运行上面的代码可以明显发现两个下载任务“同时”启动了,而且程序的执行时间将大大缩短,不再是两个任务的时间总和。下面是程序的一次执行结果。 +在上面的程式碼中,我們通過`Process`類建立了程序物件,通過`target`引數我們傳入一個函式來表示程序啟動後要執行的程式碼,後面的`args`是一個元組,它代表了傳遞給函式的引數。`Process`物件的`start`方法用來啟動程序,而`join`方法表示等待程序執行結束。執行上面的程式碼可以明顯發現兩個下載任務“同時”啟動了,而且程式的執行時間將大大縮短,不再是兩個任務的時間總和。下面是程式的一次執行結果。 ```Shell -启动下载进程,进程号[1530]. -开始下载Python从入门到住院.pdf... -启动下载进程,进程号[1531]. -开始下载Peking Hot.avi... -Peking Hot.avi下载完成! 耗费了7秒 -Python从入门到住院.pdf下载完成! 耗费了10秒 -总共耗费了10.01秒. +啟動下載程序,程序號[1530]. +開始下載Python從入門到住院.pdf... +啟動下載程序,程序號[1531]. +開始下載Peking Hot.avi... +Peking Hot.avi下載完成! 耗費了7秒 +Python從入門到住院.pdf下載完成! 耗費了10秒 +總共耗費了10.01秒. ``` -我们也可以使用subprocess模块中的类和函数来创建和启动子进程,然后通过管道来和子进程通信,这些内容我们不在此进行讲解,有兴趣的读者可以自己了解这些知识。接下来我们将重点放在如何实现两个进程间的通信。我们启动两个进程,一个输出Ping,一个输出Pong,两个进程输出的Ping和Pong加起来一共10个。听起来很简单吧,但是如果这样写可是错的哦。 +我們也可以使用subprocess模組中的類和函式來建立和啟動子程序,然後通過管道來和子程序通訊,這些內容我們不在此進行講解,有興趣的讀者可以自己瞭解這些知識。接下來我們將重點放在如何實現兩個程序間的通訊。我們啟動兩個程序,一個輸出Ping,一個輸出Pong,兩個程序輸出的Ping和Pong加起來一共10個。聽起來很簡單吧,但是如果這樣寫可是錯的哦。 ```Python from multiprocessing import Process @@ -128,12 +128,12 @@ if __name__ == '__main__': ``` -看起来没毛病,但是最后的结果是Ping和Pong各输出了10个,Why?当我们在程序中创建进程的时候,子进程复制了父进程及其所有的数据结构,每个子进程有自己独立的内存空间,这也就意味着两个子进程中各有一个`counter`变量,所以结果也就可想而知了。要解决这个问题比较简单的办法是使用multiprocessing模块中的`Queue`类,它是可以被多个进程共享的队列,底层是通过管道和[信号量(semaphore)]()机制来实现的,有兴趣的读者可以自己尝试一下。 +看起來沒毛病,但是最後的結果是Ping和Pong各輸出了10個,Why?當我們在程式中建立程序的時候,子程序複製了父程序及其所有的資料結構,每個子程序有自己獨立的記憶體空間,這也就意味著兩個子程序中各有一個`counter`變數,所以結果也就可想而知了。要解決這個問題比較簡單的辦法是使用multiprocessing模組中的`Queue`類,它是可以被多個程序共享的佇列,底層是通過管道和[訊號量(semaphore)]()機制來實現的,有興趣的讀者可以自己嘗試一下。 -### Python中的多线程 +### Python中的多執行緒 -在Python早期的版本中就引入了thread模块(现在名为_thread)来实现多线程编程,然而该模块过于底层,而且很多功能都没有提供,因此目前的多线程开发我们推荐使用threading模块,该模块对多线程编程提供了更好的面向对象的封装。我们把刚才下载文件的例子用多线程的方式来实现一遍。 +在Python早期的版本中就引入了thread模組(現在名為_thread)來實現多執行緒程式設計,然而該模組過於底層,而且很多功能都沒有提供,因此目前的多執行緒開發我們推薦使用threading模組,該模組對多執行緒程式設計提供了更好的面向物件的封裝。我們把剛才下載檔案的例子用多執行緒的方式來實現一遍。 ```Python from random import randint @@ -142,22 +142,22 @@ from time import time, sleep def download(filename): - print('开始下载%s...' % filename) + print('開始下載%s...' % filename) time_to_download = randint(5, 10) sleep(time_to_download) - print('%s下载完成! 耗费了%d秒' % (filename, time_to_download)) + print('%s下載完成! 耗費了%d秒' % (filename, time_to_download)) def main(): start = time() - t1 = Thread(target=download, args=('Python从入门到住院.pdf',)) + t1 = Thread(target=download, args=('Python從入門到住院.pdf',)) t1.start() t2 = Thread(target=download, args=('Peking Hot.avi',)) t2.start() t1.join() t2.join() end = time() - print('总共耗费了%.3f秒' % (end - start)) + print('總共耗費了%.3f秒' % (end - start)) if __name__ == '__main__': @@ -165,7 +165,7 @@ if __name__ == '__main__': ``` -我们可以直接使用threading模块的`Thread`类来创建线程,但是我们之前讲过一个非常重要的概念叫“继承”,我们可以从已有的类创建新类,因此也可以通过继承`Thread`类的方式来创建自定义的线程类,然后再创建线程对象并启动线程。代码如下所示。 +我們可以直接使用threading模組的`Thread`類來建立執行緒,但是我們之前講過一個非常重要的概念叫“繼承”,我們可以從已有的類建立新類,因此也可以通過繼承`Thread`類的方式來建立自定義的執行緒類,然後再建立執行緒物件並啟動執行緒。程式碼如下所示。 ```Python from random import randint @@ -180,22 +180,22 @@ class DownloadTask(Thread): self._filename = filename def run(self): - print('开始下载%s...' % self._filename) + print('開始下載%s...' % self._filename) time_to_download = randint(5, 10) sleep(time_to_download) - print('%s下载完成! 耗费了%d秒' % (self._filename, time_to_download)) + print('%s下載完成! 耗費了%d秒' % (self._filename, time_to_download)) def main(): start = time() - t1 = DownloadTask('Python从入门到住院.pdf') + t1 = DownloadTask('Python從入門到住院.pdf') t1.start() t2 = DownloadTask('Peking Hot.avi') t2.start() t1.join() t2.join() end = time() - print('总共耗费了%.2f秒.' % (end - start)) + print('總共耗費了%.2f秒.' % (end - start)) if __name__ == '__main__': @@ -203,7 +203,7 @@ if __name__ == '__main__': ``` -因为多个线程可以共享进程的内存空间,因此要实现多个线程间的通信相对简单,大家能想到的最直接的办法就是设置一个全局变量,多个线程共享这个全局变量即可。但是当多个线程共享同一个变量(我们通常称之为“资源”)的时候,很有可能产生不可控的结果从而导致程序失效甚至崩溃。如果一个资源被多个线程竞争使用,那么我们通常称之为“临界资源”,对“临界资源”的访问需要加上保护,否则资源会处于“混乱”的状态。下面的例子演示了100个线程向同一个银行账户转账(转入1元钱)的场景,在这个例子中,银行账户就是一个临界资源,在没有保护的情况下我们很有可能会得到错误的结果。 +因為多個執行緒可以共享程序的記憶體空間,因此要實現多個執行緒間的通訊相對簡單,大家能想到的最直接的辦法就是設定一個全域性變數,多個執行緒共享這個全域性變數即可。但是當多個執行緒共享同一個變數(我們通常稱之為“資源”)的時候,很有可能產生不可控的結果從而導致程式失效甚至崩潰。如果一個資源被多個執行緒競爭使用,那麼我們通常稱之為“臨界資源”,對“臨界資源”的訪問需要加上保護,否則資源會處於“混亂”的狀態。下面的例子演示了100個執行緒向同一個銀行賬戶轉賬(轉入1元錢)的場景,在這個例子中,銀行賬戶就是一個臨界資源,在沒有保護的情況下我們很有可能會得到錯誤的結果。 ```Python from time import sleep @@ -216,11 +216,11 @@ class Account(object): self._balance = 0 def deposit(self, money): - # 计算存款后的余额 + # 計算存款後的餘額 new_balance = self._balance + money - # 模拟受理存款业务需要0.01秒的时间 + # 模擬受理存款業務需要0.01秒的時間 sleep(0.01) - # 修改账户余额 + # 修改賬戶餘額 self._balance = new_balance @property @@ -242,15 +242,15 @@ class AddMoneyThread(Thread): def main(): account = Account() threads = [] - # 创建100个存款的线程向同一个账户中存钱 + # 建立100個存款的執行緒向同一個賬戶中存錢 for _ in range(100): t = AddMoneyThread(account, 1) threads.append(t) t.start() - # 等所有存款的线程都执行完毕 + # 等所有存款的執行緒都執行完畢 for t in threads: t.join() - print('账户余额为: ¥%d元' % account.balance) + print('賬戶餘額為: ¥%d元' % account.balance) if __name__ == '__main__': @@ -258,7 +258,7 @@ if __name__ == '__main__': ``` -运行上面的程序,结果让人大跌眼镜,100个线程分别向账户中转入1元钱,结果居然远远小于100元。之所以出现这种情况是因为我们没有对银行账户这个“临界资源”加以保护,多个线程同时向账户中存钱时,会一起执行到`new_balance = self._balance + money`这行代码,多个线程得到的账户余额都是初始状态下的`0`,所以都是`0`上面做了+1的操作,因此得到了错误的结果。在这种情况下,“锁”就可以派上用场了。我们可以通过“锁”来保护“临界资源”,只有获得“锁”的线程才能访问“临界资源”,而其他没有得到“锁”的线程只能被阻塞起来,直到获得“锁”的线程释放了“锁”,其他线程才有机会获得“锁”,进而访问被保护的“临界资源”。下面的代码演示了如何使用“锁”来保护对银行账户的操作,从而获得正确的结果。 +執行上面的程式,結果讓人大跌眼鏡,100個執行緒分別向賬戶中轉入1元錢,結果居然遠遠小於100元。之所以出現這種情況是因為我們沒有對銀行賬戶這個“臨界資源”加以保護,多個執行緒同時向賬戶中存錢時,會一起執行到`new_balance = self._balance + money`這行程式碼,多個執行緒得到的賬戶餘額都是初始狀態下的`0`,所以都是`0`上面做了+1的操作,因此得到了錯誤的結果。在這種情況下,“鎖”就可以派上用場了。我們可以通過“鎖”來保護“臨界資源”,只有獲得“鎖”的執行緒才能訪問“臨界資源”,而其他沒有得到“鎖”的執行緒只能被阻塞起來,直到獲得“鎖”的執行緒釋放了“鎖”,其他執行緒才有機會獲得“鎖”,進而訪問被保護的“臨界資源”。下面的程式碼演示瞭如何使用“鎖”來保護對銀行賬戶的操作,從而獲得正確的結果。 ```Python from time import sleep @@ -272,14 +272,14 @@ class Account(object): self._lock = Lock() def deposit(self, money): - # 先获取锁才能执行后续的代码 + # 先獲取鎖才能執行後續的程式碼 self._lock.acquire() try: new_balance = self._balance + money sleep(0.01) self._balance = new_balance finally: - # 在finally中执行释放锁的操作保证正常异常锁都能释放 + # 在finally中執行釋放鎖的操作保證正常異常鎖都能釋放 self._lock.release() @property @@ -307,7 +307,7 @@ def main(): t.start() for t in threads: t.join() - print('账户余额为: ¥%d元' % account.balance) + print('賬戶餘額為: ¥%d元' % account.balance) if __name__ == '__main__': @@ -315,31 +315,31 @@ if __name__ == '__main__': ``` -比较遗憾的一件事情是Python的多线程并不能发挥CPU的多核特性,这一点只要启动几个执行死循环的线程就可以得到证实了。之所以如此,是因为Python的解释器有一个“全局解释器锁”(GIL)的东西,任何线程执行前必须先获得GIL锁,然后每执行100条字节码,解释器就自动释放GIL锁,让别的线程有机会执行,这是一个历史遗留问题,但是即便如此,就如我们之前举的例子,使用多线程在提升执行效率和改善用户体验方面仍然是有积极意义的。 +比較遺憾的一件事情是Python的多執行緒並不能發揮CPU的多核特性,這一點只要啟動幾個執行死迴圈的執行緒就可以得到證實了。之所以如此,是因為Python的直譯器有一個“全域性直譯器鎖”(GIL)的東西,任何執行緒執行前必須先獲得GIL鎖,然後每執行100條位元組碼,直譯器就自動釋放GIL鎖,讓別的執行緒有機會執行,這是一個歷史遺留問題,但是即便如此,就如我們之前舉的例子,使用多執行緒在提升執行效率和改善使用者體驗方面仍然是有積極意義的。 -### 多进程还是多线程 +### 多程序還是多執行緒 -无论是多进程还是多线程,只要数量一多,效率肯定上不去,为什么呢?我们打个比方,假设你不幸正在准备中考,每天晚上需要做语文、数学、英语、物理、化学这5科的作业,每项作业耗时1小时。如果你先花1小时做语文作业,做完了,再花1小时做数学作业,这样,依次全部做完,一共花5小时,这种方式称为单任务模型。如果你打算切换到多任务模型,可以先做1分钟语文,再切换到数学作业,做1分钟,再切换到英语,以此类推,只要切换速度足够快,这种方式就和单核CPU执行多任务是一样的了,以旁观者的角度来看,你就正在同时写5科作业。 +無論是多程序還是多執行緒,只要數量一多,效率肯定上不去,為什麼呢?我們打個比方,假設你不幸正在準備中考,每天晚上需要做語文、數學、英語、物理、化學這5科的作業,每項作業耗時1小時。如果你先花1小時做語文作業,做完了,再花1小時做數學作業,這樣,依次全部做完,一共花5小時,這種方式稱為單任務模型。如果你打算切換到多工模型,可以先做1分鐘語文,再切換到數學作業,做1分鐘,再切換到英語,以此類推,只要切換速度足夠快,這種方式就和單核CPU執行多工是一樣的了,以旁觀者的角度來看,你就正在同時寫5科作業。 -但是,切换作业是有代价的,比如从语文切到数学,要先收拾桌子上的语文书本、钢笔(这叫保存现场),然后,打开数学课本、找出圆规直尺(这叫准备新环境),才能开始做数学作业。操作系统在切换进程或者线程时也是一样的,它需要先保存当前执行的现场环境(CPU寄存器状态、内存页等),然后,把新任务的执行环境准备好(恢复上次的寄存器状态,切换内存页等),才能开始执行。这个切换过程虽然很快,但是也需要耗费时间。如果有几千个任务同时进行,操作系统可能就主要忙着切换任务,根本没有多少时间去执行任务了,这种情况最常见的就是硬盘狂响,点窗口无反应,系统处于假死状态。所以,多任务一旦多到一个限度,反而会使得系统性能急剧下降,最终导致所有任务都做不好。 +但是,切換作業是有代價的,比如從語文切到數學,要先收拾桌子上的語文書本、鋼筆(這叫儲存現場),然後,開啟數學課本、找出圓規直尺(這叫準備新環境),才能開始做數學作業。作業系統在切換程序或者執行緒時也是一樣的,它需要先儲存當前執行的現場環境(CPU暫存器狀態、記憶體頁等),然後,把新任務的執行環境準備好(恢復上次的暫存器狀態,切換記憶體頁等),才能開始執行。這個切換過程雖然很快,但是也需要耗費時間。如果有幾千個任務同時進行,作業系統可能就主要忙著切換任務,根本沒有多少時間去執行任務了,這種情況最常見的就是硬碟狂響,點視窗無反應,系統處於假死狀態。所以,多工一旦多到一個限度,反而會使得系統性能急劇下降,最終導致所有任務都做不好。 -是否采用多任务的第二个考虑是任务的类型,可以把任务分为计算密集型和I/O密集型。计算密集型任务的特点是要进行大量的计算,消耗CPU资源,比如对视频进行编码解码或者格式转换等等,这种任务全靠CPU的运算能力,虽然也可以用多任务完成,但是任务越多,花在任务切换的时间就越多,CPU执行任务的效率就越低。计算密集型任务由于主要消耗CPU资源,这类任务用Python这样的脚本语言去执行效率通常很低,最能胜任这类任务的是C语言,我们之前提到了Python中有嵌入C/C++代码的机制。 +是否採用多工的第二個考慮是任務的型別,可以把任務分為計算密集型和I/O密集型。計算密集型任務的特點是要進行大量的計算,消耗CPU資源,比如對視訊進行編碼解碼或者格式轉換等等,這種任務全靠CPU的運算能力,雖然也可以用多工完成,但是任務越多,花在任務切換的時間就越多,CPU執行任務的效率就越低。計算密集型任務由於主要消耗CPU資源,這類任務用Python這樣的指令碼語言去執行效率通常很低,最能勝任這類任務的是C語言,我們之前提到了Python中有嵌入C/C++程式碼的機制。 -除了计算密集型任务,其他的涉及到网络、存储介质I/O的任务都可以视为I/O密集型任务,这类任务的特点是CPU消耗很少,任务的大部分时间都在等待I/O操作完成(因为I/O的速度远远低于CPU和内存的速度)。对于I/O密集型任务,如果启动多任务,就可以减少I/O等待时间从而让CPU高效率的运转。有一大类的任务都属于I/O密集型任务,这其中包括了我们很快会涉及到的网络应用和Web应用。 +除了計算密集型任務,其他的涉及到網路、儲存介質I/O的任務都可以視為I/O密集型任務,這類任務的特點是CPU消耗很少,任務的大部分時間都在等待I/O操作完成(因為I/O的速度遠遠低於CPU和記憶體的速度)。對於I/O密集型任務,如果啟動多工,就可以減少I/O等待時間從而讓CPU高效率的運轉。有一大類的任務都屬於I/O密集型任務,這其中包括了我們很快會涉及到的網路應用和Web應用。 -> **说明:**上面的内容和例子来自于[廖雪峰官方网站的《Python教程》](https://www.liaoxuefeng.com/wiki/0014316089557264a6b348958f449949df42a6d3a2e542c000),因为对作者文中的某些观点持有不同的看法,对原文的文字描述做了适当的调整。 +> **說明:**上面的內容和例子來自於[廖雪峰官方網站的《Python教程》](https://www.liaoxuefeng.com/wiki/0014316089557264a6b348958f449949df42a6d3a2e542c000),因為對作者文中的某些觀點持有不同的看法,對原文的文字描述做了適當的調整。 -### 单线程+异步I/O +### 單執行緒+非同步I/O -现代操作系统对I/O操作的改进中最为重要的就是支持异步I/O。如果充分利用操作系统提供的异步I/O支持,就可以用单进程单线程模型来执行多任务,这种全新的模型称为事件驱动模型。Nginx就是支持异步I/O的Web服务器,它在单核CPU上采用单进程模型就可以高效地支持多任务。在多核CPU上,可以运行多个进程(数量与CPU核心数相同),充分利用多核CPU。用Node.js开发的服务器端程序也使用了这种工作模式,这也是当下实现多任务编程的一种趋势。 +現代作業系統對I/O操作的改進中最為重要的就是支援非同步I/O。如果充分利用作業系統提供的非同步I/O支援,就可以用單程序單執行緒模型來執行多工,這種全新的模型稱為事件驅動模型。Nginx就是支援非同步I/O的Web伺服器,它在單核CPU上採用單程序模型就可以高效地支援多工。在多核CPU上,可以執行多個程序(數量與CPU核心數相同),充分利用多核CPU。用Node.js開發的伺服器端程式也使用了這種工作模式,這也是當下實現多工程式設計的一種趨勢。 -在Python语言中,单线程+异步I/O的编程模型称为协程,有了协程的支持,就可以基于事件驱动编写高效的多任务程序。协程最大的优势就是极高的执行效率,因为子程序切换不是线程切换,而是由程序自身控制,因此,没有线程切换的开销。协程的第二个优势就是不需要多线程的锁机制,因为只有一个线程,也不存在同时写变量冲突,在协程中控制共享资源不用加锁,只需要判断状态就好了,所以执行效率比多线程高很多。如果想要充分利用CPU的多核特性,最简单的方法是多进程+协程,既充分利用多核,又充分发挥协程的高效率,可获得极高的性能。关于这方面的内容,我稍后会做一个专题来进行讲解。 +在Python語言中,單執行緒+非同步I/O的程式設計模型稱為協程,有了協程的支援,就可以基於事件驅動編寫高效的多工程式。協程最大的優勢就是極高的執行效率,因為子程式切換不是執行緒切換,而是由程式自身控制,因此,沒有執行緒切換的開銷。協程的第二個優勢就是不需要多執行緒的鎖機制,因為只有一個執行緒,也不存在同時寫變數衝突,在協程中控制共享資源不用加鎖,只需要判斷狀態就好了,所以執行效率比多執行緒高很多。如果想要充分利用CPU的多核特性,最簡單的方法是多程序+協程,既充分利用多核,又充分發揮協程的高效率,可獲得極高的效能。關於這方面的內容,我稍後會做一個專題來進行講解。 -### 应用案例 +### 應用案例 -#### 例子1:将耗时间的任务放到线程中以获得更好的用户体验。 +#### 例子1:將耗時間的任務放到執行緒中以獲得更好的使用者體驗。 -如下所示的界面中,有“下载”和“关于”两个按钮,用休眠的方式模拟点击“下载”按钮会联网下载文件需要耗费10秒的时间,如果不使用“多线程”,我们会发现,当点击“下载”按钮后整个程序的其他部分都被这个耗时间的任务阻塞而无法执行了,这显然是非常糟糕的用户体验,代码如下所示。 +如下所示的介面中,有“下載”和“關於”兩個按鈕,用休眠的方式模擬點選“下載”按鈕會聯網下載檔案需要耗費10秒的時間,如果不使用“多執行緒”,我們會發現,當點選“下載”按鈕後整個程式的其他部分都被這個耗時間的任務阻塞而無法執行了,這顯然是非常糟糕的使用者體驗,程式碼如下所示。 ```Python import time @@ -348,25 +348,25 @@ import tkinter.messagebox def download(): - # 模拟下载任务需要花费10秒钟时间 + # 模擬下載任務需要花費10秒鐘時間 time.sleep(10) - tkinter.messagebox.showinfo('提示', '下载完成!') + tkinter.messagebox.showinfo('提示', '下載完成!') def show_about(): - tkinter.messagebox.showinfo('关于', '作者: 骆昊(v1.0)') + tkinter.messagebox.showinfo('關於', '作者: 駱昊(v1.0)') def main(): top = tkinter.Tk() - top.title('单线程') + top.title('單執行緒') top.geometry('200x150') top.wm_attributes('-topmost', True) panel = tkinter.Frame(top) - button1 = tkinter.Button(panel, text='下载', command=download) + button1 = tkinter.Button(panel, text='下載', command=download) button1.pack(side='left') - button2 = tkinter.Button(panel, text='关于', command=show_about) + button2 = tkinter.Button(panel, text='關於', command=show_about) button2.pack(side='right') panel.pack(side='bottom') @@ -378,7 +378,7 @@ if __name__ == '__main__': ``` -如果使用多线程将耗时间的任务放到一个独立的线程中执行,这样就不会因为执行耗时间的任务而阻塞了主线程,修改后的代码如下所示。 +如果使用多執行緒將耗時間的任務放到一個獨立的執行緒中執行,這樣就不會因為執行耗時間的任務而阻塞了主執行緒,修改後的程式碼如下所示。 ```Python import time @@ -393,29 +393,29 @@ def main(): def run(self): time.sleep(10) - tkinter.messagebox.showinfo('提示', '下载完成!') - # 启用下载按钮 + tkinter.messagebox.showinfo('提示', '下載完成!') + # 啟用下載按鈕 button1.config(state=tkinter.NORMAL) def download(): - # 禁用下载按钮 + # 禁用下載按鈕 button1.config(state=tkinter.DISABLED) - # 通过daemon参数将线程设置为守护线程(主程序退出就不再保留执行) - # 在线程中处理耗时间的下载任务 + # 通過daemon引數將執行緒設定為守護執行緒(主程式退出就不再保留執行) + # 線上程中處理耗時間的下載任務 DownloadTaskHandler(daemon=True).start() def show_about(): - tkinter.messagebox.showinfo('关于', '作者: 骆昊(v1.0)') + tkinter.messagebox.showinfo('關於', '作者: 駱昊(v1.0)') top = tkinter.Tk() - top.title('单线程') + top.title('單執行緒') top.geometry('200x150') top.wm_attributes('-topmost', 1) panel = tkinter.Frame(top) - button1 = tkinter.Button(panel, text='下载', command=download) + button1 = tkinter.Button(panel, text='下載', command=download) button1.pack(side='left') - button2 = tkinter.Button(panel, text='关于', command=show_about) + button2 = tkinter.Button(panel, text='關於', command=show_about) button2.pack(side='right') panel.pack(side='bottom') @@ -427,9 +427,9 @@ if __name__ == '__main__': ``` -#### 例子2:使用多进程对复杂任务进行“分而治之”。 +#### 例子2:使用多程序對複雜任務進行“分而治之”。 -我们来完成1~100000000求和的计算密集型任务,这个问题本身非常简单,有点循环的知识就能解决,代码如下所示。 +我們來完成1~100000000求和的計算密集型任務,這個問題本身非常簡單,有點迴圈的知識就能解決,程式碼如下所示。 ```Python from time import time @@ -451,7 +451,7 @@ if __name__ == '__main__': ``` -在上面的代码中,我故意先去创建了一个列表容器然后填入了100000000个数,这一步其实是比较耗时间的,所以为了公平起见,当我们将这个任务分解到8个进程中去执行的时候,我们暂时也不考虑列表切片操作花费的时间,只是把做运算和合并运算结果的时间统计出来,代码如下所示。 +在上面的程式碼中,我故意先去建立了一個列表容器然後填入了100000000個數,這一步其實是比較耗時間的,所以為了公平起見,當我們將這個任務分解到8個程序中去執行的時候,我們暫時也不考慮列表切片操作花費的時間,只是把做運算和合並運算結果的時間統計出來,程式碼如下所示。 ```Python from multiprocessing import Process, Queue @@ -471,18 +471,18 @@ def main(): number_list = [x for x in range(1, 100000001)] result_queue = Queue() index = 0 - # 启动8个进程将数据切片后进行运算 + # 啟動8個程序將資料切片後進行運算 for _ in range(8): p = Process(target=task_handler, args=(number_list[index:index + 12500000], result_queue)) index += 12500000 processes.append(p) p.start() - # 开始记录所有进程执行完成花费的时间 + # 開始記錄所有程序執行完成花費的時間 start = time() for p in processes: p.join() - # 合并执行结果 + # 合併執行結果 total = 0 while not result_queue.empty(): total += result_queue.get() @@ -496,4 +496,4 @@ if __name__ == '__main__': ``` -比较两段代码的执行结果(在我目前使用的MacBook上,上面的代码需要大概6秒左右的时间,而下面的代码只需要不到1秒的时间,再强调一次我们只是比较了运算的时间,不考虑列表创建及切片操作花费的时间),使用多进程后由于获得了更多的CPU执行时间以及更好的利用了CPU的多核特性,明显的减少了程序的执行时间,而且计算量越大效果越明显。当然,如果愿意还可以将多个进程部署在不同的计算机上,做成分布式进程,具体的做法就是通过multiprocessing.managers模块中提供的管理器将`Queue`对象通过网络共享出来(注册到网络上让其他计算机可以访问),这部分内容也留到爬虫的专题再进行讲解。 \ No newline at end of file +比較兩段程式碼的執行結果(在我目前使用的MacBook上,上面的程式碼需要大概6秒左右的時間,而下面的程式碼只需要不到1秒的時間,再強調一次我們只是比較了運算的時間,不考慮列表建立及切片操作花費的時間),使用多程序後由於獲得了更多的CPU執行時間以及更好的利用了CPU的多核特性,明顯的減少了程式的執行時間,而且計算量越大效果越明顯。當然,如果願意還可以將多個程序部署在不同的計算機上,做成分散式程序,具體的做法就是通過multiprocessing.managers模組中提供的管理器將`Queue`物件通過網路共享出來(註冊到網路上讓其他計算機可以訪問),這部分內容也留到爬蟲的專題再進行講解。 \ No newline at end of file diff --git "a/Day01-15/Day14/\347\275\221\347\273\234\347\274\226\347\250\213\345\205\245\351\227\250.md" "b/Day01-15/Day14/\347\275\221\347\273\234\347\274\226\347\250\213\345\205\245\351\227\250.md" index 7f33271ec..c53bdbdc1 100644 --- "a/Day01-15/Day14/\347\275\221\347\273\234\347\274\226\347\250\213\345\205\245\351\227\250.md" +++ "b/Day01-15/Day14/\347\275\221\347\273\234\347\274\226\347\250\213\345\205\245\351\227\250.md" @@ -1,59 +1,59 @@ -## 网络编程入门 +## 網路程式設計入門 -### 计算机网络基础 +### 計算機網路基礎 -计算机网络是独立自主的计算机互联而成的系统的总称,组建计算机网络最主要的目的是实现多台计算机之间的通信和资源共享。今天计算机网络中的设备和计算机网络的用户已经多得不可计数,而计算机网络也可以称得上是一个“复杂巨系统”,对于这样的系统,我们不可能用一两篇文章把它讲清楚,有兴趣的读者可以自行阅读Andrew S.Tanenbaum老师的经典之作《计算机网络》或Kurose和Ross老师合著的《计算机网络:自顶向下方法》来了解计算机网络的相关知识。 +計算機網路是獨立自主的計算機互聯而成的系統的總稱,組建計算機網路最主要的目的是實現多臺計算機之間的通訊和資源共享。今天計算機網路中的裝置和計算機網路的使用者已經多得不可計數,而計算機網路也可以稱得上是一個“複雜巨系統”,對於這樣的系統,我們不可能用一兩篇文章把它講清楚,有興趣的讀者可以自行閱讀Andrew S.Tanenbaum老師的經典之作《計算機網路》或Kurose和Ross老師合著的《計算機網路:自頂向下方法》來了解計算機網路的相關知識。 -#### 计算机网络发展史 +#### 計算機網路發展史 -1. 1960s - 美国国防部ARPANET项目问世,奠定了分组交换网络的基础。 +1. 1960s - 美國國防部ARPANET專案問世,奠定了分組交換網路的基礎。 ![](./res/arpanet.png) -2. 1980s - 国际标准化组织(ISO)发布OSI/RM,奠定了网络技术标准化的基础。 +2. 1980s - 國際標準化組織(ISO)釋出OSI/RM,奠定了網路技術標準化的基礎。 ![](./res/osimodel.png) -3. 1990s - 英国人[蒂姆·伯纳斯-李](https://zh.wikipedia.org/wiki/%E6%8F%90%E5%A7%86%C2%B7%E6%9F%8F%E5%85%A7%E8%8C%B2-%E6%9D%8E)发明了图形化的浏览器,浏览器的简单易用性使得计算机网络迅速被普及。 +3. 1990s - 英國人[蒂姆·伯納斯-李](https://zh.wikipedia.org/wiki/%E6%8F%90%E5%A7%86%C2%B7%E6%9F%8F%E5%85%A7%E8%8C%B2-%E6%9D%8E)發明了圖形化的瀏覽器,瀏覽器的簡單易用性使得計算機網路迅速被普及。 - 在没有浏览器的年代,上网是这样的。 + 在沒有瀏覽器的年代,上網是這樣的。 ![](./res/before-browser.jpg) - 有了浏览器以后,上网是这样的。 + 有了瀏覽器以後,上網是這樣的。 ![](./res/after-browser.jpg) #### TCP/IP模型 -实现网络通信的基础是网络通信协议,这些协议通常是由[互联网工程任务组](https://zh.wikipedia.org/wiki/%E4%BA%92%E8%81%94%E7%BD%91%E5%B7%A5%E7%A8%8B%E4%BB%BB%E5%8A%A1%E7%BB%84) (IETF)制定的。所谓“协议”就是通信计算机双方必须共同遵从的一组约定,例如怎样建立连接、怎样互相识别等,网络协议的三要素是:语法、语义和时序。构成我们今天使用的Internet的基础的是TCP/IP协议族,所谓协议族就是一系列的协议及其构成的通信模型,我们通常也把这套东西称为TCP/IP模型。与国际标准化组织发布的OSI/RM这个七层模型不同,TCP/IP是一个四层模型,也就是说,该模型将我们使用的网络从逻辑上分解为四个层次,自底向上依次是:网络接口层、网络层、传输层和应用层,如下图所示。 +實現網路通訊的基礎是網路通訊協議,這些協議通常是由[網際網路工程任務組](https://zh.wikipedia.org/wiki/%E4%BA%92%E8%81%94%E7%BD%91%E5%B7%A5%E7%A8%8B%E4%BB%BB%E5%8A%A1%E7%BB%84) (IETF)制定的。所謂“協議”就是通訊計算機雙方必須共同遵從的一組約定,例如怎樣建立連線、怎樣互相識別等,網路協議的三要素是:語法、語義和時序。構成我們今天使用的Internet的基礎的是TCP/IP協議族,所謂協議族就是一系列的協議及其構成的通訊模型,我們通常也把這套東西稱為TCP/IP模型。與國際標準化組織釋出的OSI/RM這個七層模型不同,TCP/IP是一個四層模型,也就是說,該模型將我們使用的網路從邏輯上分解為四個層次,自底向上依次是:網路介面層、網路層、傳輸層和應用層,如下圖所示。 ![](./res/TCP-IP-model.png) -IP通常被翻译为网际协议,它服务于网络层,主要实现了寻址和路由的功能。接入网络的每一台主机都需要有自己的IP地址,IP地址就是主机在计算机网络上的身份标识。当然由于IPv4地址的匮乏,我们平常在家里、办公室以及其他可以接入网络的公共区域上网时获得的IP地址并不是全球唯一的IP地址,而是一个[局域网(LAN)](https://zh.wikipedia.org/zh-hans/%E5%B1%80%E5%9F%9F%E7%BD%91)中的内部IP地址,通过[网络地址转换(NAT)服务](https://zh.wikipedia.org/wiki/%E7%BD%91%E7%BB%9C%E5%9C%B0%E5%9D%80%E8%BD%AC%E6%8D%A2)我们也可以实现对网络的访问。计算机网络上有大量的被我们称为“[路由器](https://zh.wikipedia.org/wiki/%E8%B7%AF%E7%94%B1%E5%99%A8)”的网络中继设备,它们会存储转发我们发送到网络上的数据分组,让从源头发出的数据最终能够找到传送到目的地通路,这项功能就是所谓的路由。 +IP通常被翻譯為網際協議,它服務於網路層,主要實現了定址和路由的功能。接入網路的每一臺主機都需要有自己的IP地址,IP地址就是主機在計算機網路上的身份標識。當然由於IPv4地址的匱乏,我們平常在家裡、辦公室以及其他可以接入網路的公共區域上網時獲得的IP地址並不是全球唯一的IP地址,而是一個[區域網(LAN)](https://zh.wikipedia.org/zh-hans/%E5%B1%80%E5%9F%9F%E7%BD%91)中的內部IP地址,通過[網路地址轉換(NAT)服務](https://zh.wikipedia.org/wiki/%E7%BD%91%E7%BB%9C%E5%9C%B0%E5%9D%80%E8%BD%AC%E6%8D%A2)我們也可以實現對網路的訪問。計算機網路上有大量的被我們稱為“[路由器](https://zh.wikipedia.org/wiki/%E8%B7%AF%E7%94%B1%E5%99%A8)”的網路中繼裝置,它們會儲存轉發我們傳送到網路上的資料分組,讓從源頭髮出的資料最終能夠找到傳送到目的地通路,這項功能就是所謂的路由。 -TCP全称传输控制协议,它是基于IP提供的寻址和路由服务而建立起来的负责实现端到端可靠传输的协议,之所以将TCP称为可靠的传输协议是因为TCP向调用者承诺了三件事情: +TCP全稱傳輸控制協議,它是基於IP提供的定址和路由服務而建立起來的負責實現端到端可靠傳輸的協議,之所以將TCP稱為可靠的傳輸協議是因為TCP向呼叫者承諾了三件事情: -1. 数据不传丢不传错(利用握手、校验和重传机制可以实现)。 -2. 流量控制(通过滑动窗口匹配数据发送者和接收者之间的传输速度)。 -3. 拥塞控制(通过RTT时间以及对滑动窗口的控制缓解网络拥堵)。 +1. 資料不傳丟不傳錯(利用握手、校驗和重傳機制可以實現)。 +2. 流量控制(通過滑動視窗匹配資料傳送者和接收者之間的傳輸速度)。 +3. 擁塞控制(通過RTT時間以及對滑動視窗的控制緩解網路擁堵)。 -#### 网络应用模式 +#### 網路應用模式 -1. C/S模式和B/S模式。这里的C指的是Client(客户端),通常是一个需要安装到某个宿主操作系统上的应用程序;而B指的是Browser(浏览器),它几乎是所有图形化操作系统都默认安装了的一个应用软件;通过C或B都可以实现对S(服务器)的访问。关于二者的比较和讨论在网络上有一大堆的文章,在此我们就不再浪费笔墨了。 -2. 去中心化的网络应用模式。不管是B/S还是C/S都需要服务器的存在,服务器就是整个应用模式的中心,而去中心化的网络应用通常没有固定的服务器或者固定的客户端,所有应用的使用者既可以作为资源的提供者也可以作为资源的访问者。 +1. C/S模式和B/S模式。這裡的C指的是Client(客戶端),通常是一個需要安裝到某個宿主作業系統上的應用程式;而B指的是Browser(瀏覽器),它幾乎是所有圖形化作業系統都預設安裝了的一個應用軟體;通過C或B都可以實現對S(伺服器)的訪問。關於二者的比較和討論在網路上有一大堆的文章,在此我們就不再浪費筆墨了。 +2. 去中心化的網路應用模式。不管是B/S還是C/S都需要伺服器的存在,伺服器就是整個應用模式的中心,而去中心化的網路應用通常沒有固定的伺服器或者固定的客戶端,所有應用的使用者既可以作為資源的提供者也可以作為資源的訪問者。 -### 基于HTTP协议的网络资源访问 +### 基於HTTP協議的網路資源訪問 -#### HTTP(超文本传输协议) +#### HTTP(超文字傳輸協議) -HTTP是超文本传输协议(Hyper-Text Transfer Proctol)的简称,维基百科上对HTTP的解释是:超文本传输协议是一种用于分布式、协作式和超媒体信息系统的应用层协议,它是[万维网](https://zh.wikipedia.org/wiki/%E5%85%A8%E7%90%83%E8%B3%87%E8%A8%8A%E7%B6%B2)数据通信的基础,设计HTTP最初的目的是为了提供一种发布和接收[HTML](https://zh.wikipedia.org/wiki/HTML)页面的方法,通过HTTP或者[HTTPS](https://zh.wikipedia.org/wiki/%E8%B6%85%E6%96%87%E6%9C%AC%E4%BC%A0%E8%BE%93%E5%AE%89%E5%85%A8%E5%8D%8F%E8%AE%AE)(超文本传输安全协议)请求的资源由URI([统一资源标识符](https://zh.wikipedia.org/wiki/%E7%B5%B1%E4%B8%80%E8%B3%87%E6%BA%90%E6%A8%99%E8%AD%98%E7%AC%A6))来标识。关于HTTP的更多内容,我们推荐阅读阮一峰老师的[《HTTP 协议入门》](http://www.ruanyifeng.com/blog/2016/08/http.html),简单的说,通过HTTP我们可以获取网络上的(基于字符的)资源,开发中经常会用到的网络API(有的地方也称之为网络数据接口)就是基于HTTP来实现数据传输的。 +HTTP是超文字傳輸協議(Hyper-Text Transfer Proctol)的簡稱,維基百科上對HTTP的解釋是:超文字傳輸協議是一種用於分散式、協作式和超媒體資訊系統的應用層協議,它是[全球資訊網](https://zh.wikipedia.org/wiki/%E5%85%A8%E7%90%83%E8%B3%87%E8%A8%8A%E7%B6%B2)資料通訊的基礎,設計HTTP最初的目的是為了提供一種釋出和接收[HTML](https://zh.wikipedia.org/wiki/HTML)頁面的方法,通過HTTP或者[HTTPS](https://zh.wikipedia.org/wiki/%E8%B6%85%E6%96%87%E6%9C%AC%E4%BC%A0%E8%BE%93%E5%AE%89%E5%85%A8%E5%8D%8F%E8%AE%AE)(超文字傳輸安全協議)請求的資源由URI([統一資源識別符號](https://zh.wikipedia.org/wiki/%E7%B5%B1%E4%B8%80%E8%B3%87%E6%BA%90%E6%A8%99%E8%AD%98%E7%AC%A6))來標識。關於HTTP的更多內容,我們推薦閱讀阮一峰老師的[《HTTP 協議入門》](http://www.ruanyifeng.com/blog/2016/08/http.html),簡單的說,通過HTTP我們可以獲取網路上的(基於字元的)資源,開發中經常會用到的網路API(有的地方也稱之為網路資料介面)就是基於HTTP來實現資料傳輸的。 #### JSON格式 -**JSON**(**J**ava**S**cript **O**bject **N**otation)是一种轻量级的数据交换语言,该语言以易于让人阅读的文字(纯文本)为基础,用来传输由属性值或者序列性的值组成的数据对象。尽管JSON是最初只是Javascript中一种创建对象的字面量语法,但它在当下更是一种独立于语言的数据格式,很多编程语言都支持JSON格式数据的生成和解析,Python内置的json模块也提供了这方面的功能。由于JSON是纯文本,它和[XML](https://zh.wikipedia.org/wiki/XML)一样都适用于异构系统之间的数据交换,而相较于XML,JSON显得更加的轻便和优雅。下面是表达同样信息的XML和JSON,而JSON的优势是相当直观的。 +**JSON**(**J**ava**S**cript **O**bject **N**otation)是一種輕量級的資料交換語言,該語言以易於讓人閱讀的文字(純文字)為基礎,用來傳輸由屬性值或者序列性的值組成的資料物件。儘管JSON是最初只是Javascript中一種建立物件的字面量語法,但它在當下更是一種獨立於語言的資料格式,很多程式語言都支援JSON格式資料的生成和解析,Python內建的json模組也提供了這方面的功能。由於JSON是純文字,它和[XML](https://zh.wikipedia.org/wiki/XML)一樣都適用於異構系統之間的資料交換,而相較於XML,JSON顯得更加的輕便和優雅。下面是表達同樣資訊的XML和JSON,而JSON的優勢是相當直觀的。 XML的例子: @@ -76,17 +76,17 @@ JSON的例子: } ``` -#### requests库 +#### requests庫 -requests是一个基于HTTP协议来使用网络的第三库,其[官方网站](http://cn.python-requests.org/zh_CN/latest/)有这样的一句介绍它的话:“Requests是唯一的一个**非转基因**的Python HTTP库,人类可以安全享用。”简单的说,使用requests库可以非常方便的使用HTTP,避免安全缺陷、冗余代码以及“重复发明轮子”(行业黑话,通常用在软件工程领域表示重新创造一个已有的或是早已被优化過的基本方法)。前面的文章中我们已经使用过这个库,下面我们还是通过requests来实现一个访问网络数据接口并从中获取美女图片下载链接然后下载美女图片到本地的例子程序,程序中使用了[天行数据](https://www.tianapi.com/)提供的网络API。 +requests是一個基於HTTP協議來使用網路的第三庫,其[官方網站](http://cn.python-requests.org/zh_CN/latest/)有這樣的一句介紹它的話:“Requests是唯一的一個**非轉基因**的Python HTTP庫,人類可以安全享用。”簡單的說,使用requests庫可以非常方便的使用HTTP,避免安全缺陷、冗餘程式碼以及“重複發明輪子”(行業黑話,通常用在軟體工程領域表示重新創造一個已有的或是早已被優化過的基本方法)。前面的文章中我們已經使用過這個庫,下面我們還是通過requests來實現一個訪問網路資料介面並從中獲取美女圖片下載連結然後下載美女圖片到本地的例子程式,程式中使用了[天行資料](https://www.tianapi.com/)提供的網路API。 -我们可以先通过pip安装requests及其依赖库。 +我們可以先通過pip安裝requests及其依賴庫。 ```Shell pip install requests ``` -如果使用PyCharm作为开发工具,可以直接在代码中书写`import requests`,然后通过代码修复功能来自动下载安装requests。 +如果使用PyCharm作為開發工具,可以直接在程式碼中書寫`import requests`,然後通過程式碼修復功能來自動下載安裝requests。 ```Python from time import time @@ -95,7 +95,7 @@ from threading import Thread import requests -# 继承Thread类创建自定义的线程类 +# 繼承Thread類建立自定義的執行緒類 class DownloadHanlder(Thread): def __init__(self, url): @@ -110,17 +110,17 @@ class DownloadHanlder(Thread): def main(): - # 通过requests模块的get函数获取网络资源 - # 下面的代码中使用了天行数据接口提供的网络API - # 要使用该数据接口需要在天行数据的网站上注册 - # 然后用自己的Key替换掉下面代码的中APIKey即可 + # 通過requests模組的get函式獲取網路資源 + # 下面的程式碼中使用了天行資料介面提供的網路API + # 要使用該資料介面需要在天行資料的網站上註冊 + # 然後用自己的Key替換掉下面程式碼的中APIKey即可 resp = requests.get( 'http://api.tianapi.com/meinv/?key=APIKey&num=10') - # 将服务器返回的JSON格式的数据解析为字典 + # 將伺服器返回的JSON格式的資料解析為字典 data_model = resp.json() for mm_dict in data_model['newslist']: url = mm_dict['picUrl'] - # 通过多线程的方式实现图片下载 + # 通過多執行緒的方式實現圖片下載 DownloadHanlder(url).start() @@ -129,15 +129,15 @@ if __name__ == '__main__': ``` -### 基于传输层协议的套接字编程 +### 基於傳輸層協議的套接字程式設計 -套接字这个词对很多不了解网络编程的人来说显得非常晦涩和陌生,其实说得通俗点,套接字就是一套用[C语言](https://zh.wikipedia.org/wiki/C%E8%AF%AD%E8%A8%80)写成的应用程序开发库,主要用于实现进程间通信和网络编程,在网络应用开发中被广泛使用。在Python中也可以基于套接字来使用传输层提供的传输服务,并基于此开发自己的网络应用。实际开发中使用的套接字可以分为三类:流套接字(TCP套接字)、数据报套接字和原始套接字。 +套接字這個詞對很多不瞭解網路程式設計的人來說顯得非常晦澀和陌生,其實說得通俗點,套接字就是一套用[C語言](https://zh.wikipedia.org/wiki/C%E8%AF%AD%E8%A8%80)寫成的應用程式開發庫,主要用於實現程序間通訊和網路程式設計,在網路應用開發中被廣泛使用。在Python中也可以基於套接字來使用傳輸層提供的傳輸服務,並基於此開發自己的網路應用。實際開發中使用的套接字可以分為三類:流套接字(TCP套接字)、資料報套接字和原始套接字。 #### TCP套接字 -所谓TCP套接字就是使用TCP协议提供的传输服务来实现网络通信的编程接口。在Python中可以通过创建socket对象并指定type属性为SOCK_STREAM来使用TCP套接字。由于一台主机可能拥有多个IP地址,而且很有可能会配置多个不同的服务,所以作为服务器端的程序,需要在创建套接字对象后将其绑定到指定的IP地址和端口上。这里的端口并不是物理设备而是对IP地址的扩展,用于区分不同的服务,例如我们通常将HTTP服务跟80端口绑定,而MySQL数据库服务默认绑定在3306端口,这样当服务器收到用户请求时就可以根据端口号来确定到底用户请求的是HTTP服务器还是数据库服务器提供的服务。端口的取值范围是0~65535,而1024以下的端口我们通常称之为“著名端口”(留给像FTP、HTTP、SMTP等“著名服务”使用的端口,有的地方也称之为“周知端口”),自定义的服务通常不使用这些端口,除非自定义的是HTTP或FTP这样的著名服务。 +所謂TCP套接字就是使用TCP協議提供的傳輸服務來實現網路通訊的程式設計介面。在Python中可以通過建立socket物件並指定type屬性為SOCK_STREAM來使用TCP套接字。由於一臺主機可能擁有多個IP地址,而且很有可能會配置多個不同的服務,所以作為伺服器端的程式,需要在建立套接字物件後將其繫結到指定的IP地址和埠上。這裡的埠並不是物理裝置而是對IP地址的擴充套件,用於區分不同的服務,例如我們通常將HTTP服務跟80埠繫結,而MySQL資料庫服務預設繫結在3306埠,這樣當伺服器收到使用者請求時就可以根據埠號來確定到底使用者請求的是HTTP伺服器還是資料庫伺服器提供的服務。埠的取值範圍是0~65535,而1024以下的埠我們通常稱之為“著名埠”(留給像FTP、HTTP、SMTP等“著名服務”使用的埠,有的地方也稱之為“周知埠”),自定義的服務通常不使用這些埠,除非自定義的是HTTP或FTP這樣的著名服務。 -下面的代码实现了一个提供时间日期的服务器。 +下面的程式碼實現了一個提供時間日期的伺服器。 ```Python from socket import socket, SOCK_STREAM, AF_INET @@ -145,30 +145,30 @@ from datetime import datetime def main(): - # 1.创建套接字对象并指定使用哪种传输服务 + # 1.建立套接字物件並指定使用哪種傳輸服務 # family=AF_INET - IPv4地址 # family=AF_INET6 - IPv6地址 # type=SOCK_STREAM - TCP套接字 # type=SOCK_DGRAM - UDP套接字 # type=SOCK_RAW - 原始套接字 server = socket(family=AF_INET, type=SOCK_STREAM) - # 2.绑定IP地址和端口(端口用于区分不同的服务) - # 同一时间在同一个端口上只能绑定一个服务否则报错 + # 2.繫結IP地址和埠(埠用於區分不同的服務) + # 同一時間在同一個埠上只能繫結一個服務否則報錯 server.bind(('192.168.1.2', 6789)) - # 3.开启监听 - 监听客户端连接到服务器 - # 参数512可以理解为连接队列的大小 + # 3.開啟監聽 - 監聽客戶端連線到伺服器 + # 引數512可以理解為連線佇列的大小 server.listen(512) - print('服务器启动开始监听...') + print('伺服器啟動開始監聽...') while True: - # 4.通过循环接收客户端的连接并作出相应的处理(提供服务) - # accept方法是一个阻塞方法如果没有客户端连接到服务器代码不会向下执行 - # accept方法返回一个元组其中的第一个元素是客户端对象 - # 第二个元素是连接到服务器的客户端的地址(由IP和端口两部分构成) + # 4.通過迴圈接收客戶端的連線並作出相應的處理(提供服務) + # accept方法是一個阻塞方法如果沒有客戶端連線到伺服器程式碼不會向下執行 + # accept方法返回一個元組其中的第一個元素是客戶端物件 + # 第二個元素是連線到伺服器的客戶端的地址(由IP和埠兩部分構成) client, addr = server.accept() - print(str(addr) + '连接到了服务器.') - # 5.发送数据 + print(str(addr) + '連線到了伺服器.') + # 5.傳送資料 client.send(str(datetime.now()).encode('utf-8')) - # 6.断开连接 + # 6.斷開連線 client.close() @@ -177,7 +177,7 @@ if __name__ == '__main__': ``` -运行服务器程序后我们可以通过Windows系统的telnet来访问该服务器,结果如下图所示。 +執行伺服器程式後我們可以通過Windows系統的telnet來訪問該伺服器,結果如下圖所示。 ```Shell telnet 192.168.1.2 6789 @@ -185,18 +185,18 @@ telnet 192.168.1.2 6789 ![](./res/telnet.png) -当然我们也可以通过Python的程序来实现TCP客户端的功能,相较于实现服务器程序,实现客户端程序就简单多了,代码如下所示。 +當然我們也可以通過Python的程式來實現TCP客戶端的功能,相較於實現伺服器程式,實現客戶端程式就簡單多了,程式碼如下所示。 ```Python from socket import socket def main(): - # 1.创建套接字对象默认使用IPv4和TCP协议 + # 1.建立套接字物件預設使用IPv4和TCP協議 client = socket() - # 2.连接到服务器(需要指定IP地址和端口) + # 2.連線到伺服器(需要指定IP地址和埠) client.connect(('192.168.1.2', 6789)) - # 3.从服务器接收数据 + # 3.從伺服器接收資料 print(client.recv(1024).decode('utf-8')) client.close() @@ -206,9 +206,9 @@ if __name__ == '__main__': ``` -需要注意的是,上面的服务器并没有使用多线程或者异步I/O的处理方式,这也就意味着当服务器与一个客户端处于通信状态时,其他的客户端只能排队等待。很显然,这样的服务器并不能满足我们的需求,我们需要的服务器是能够同时接纳和处理多个用户请求的。下面我们来设计一个使用多线程技术处理多个用户请求的服务器,该服务器会向连接到服务器的客户端发送一张图片。 +需要注意的是,上面的伺服器並沒有使用多執行緒或者非同步I/O的處理方式,這也就意味著當伺服器與一個客戶端處於通訊狀態時,其他的客戶端只能排隊等待。很顯然,這樣的伺服器並不能滿足我們的需求,我們需要的伺服器是能夠同時接納和處理多個使用者請求的。下面我們來設計一個使用多執行緒技術處理多個使用者請求的伺服器,該伺服器會向連線到伺服器的客戶端傳送一張圖片。 -服务器端代码: +伺服器端程式碼: ```Python from socket import socket, SOCK_STREAM, AF_INET @@ -219,7 +219,7 @@ from threading import Thread def main(): - # 自定义线程类 + # 自定義執行緒類 class FileTransferHandler(Thread): def __init__(self, cclient): @@ -229,28 +229,28 @@ def main(): def run(self): my_dict = {} my_dict['filename'] = 'guido.jpg' - # JSON是纯文本不能携带二进制数据 - # 所以图片的二进制数据要处理成base64编码 + # JSON是純文字不能攜帶二進位制資料 + # 所以圖片的二進位制資料要處理成base64編碼 my_dict['filedata'] = data - # 通过dumps函数将字典处理成JSON字符串 + # 通過dumps函式將字典處理成JSON字串 json_str = dumps(my_dict) - # 发送JSON字符串 + # 傳送JSON字串 self.cclient.send(json_str.encode('utf-8')) self.cclient.close() - # 1.创建套接字对象并指定使用哪种传输服务 + # 1.建立套接字物件並指定使用哪種傳輸服務 server = socket() - # 2.绑定IP地址和端口(区分不同的服务) + # 2.繫結IP地址和埠(區分不同的服務) server.bind(('192.168.1.2', 5566)) - # 3.开启监听 - 监听客户端连接到服务器 + # 3.開啟監聽 - 監聽客戶端連線到伺服器 server.listen(512) - print('服务器启动开始监听...') + print('伺服器啟動開始監聽...') with open('guido.jpg', 'rb') as f: - # 将二进制数据处理成base64再解码成字符串 + # 將二進位制資料處理成base64再解碼成字串 data = b64encode(f.read()).decode('utf-8') while True: client, addr = server.accept() - # 启动一个线程来处理客户端的请求 + # 啟動一個執行緒來處理客戶端的請求 FileTransferHandler(client).start() @@ -259,7 +259,7 @@ if __name__ == '__main__': ``` -客户端代码: +客戶端程式碼: ```Python from socket import socket @@ -270,23 +270,23 @@ from base64 import b64decode def main(): client = socket() client.connect(('192.168.1.2', 5566)) - # 定义一个保存二进制数据的对象 + # 定義一個儲存二進位制資料的物件 in_data = bytes() - # 由于不知道服务器发送的数据有多大每次接收1024字节 + # 由於不知道伺服器傳送的資料有多大每次接收1024位元組 data = client.recv(1024) while data: - # 将收到的数据拼接起来 + # 將收到的資料拼接起來 in_data += data data = client.recv(1024) - # 将收到的二进制数据解码成JSON字符串并转换成字典 - # loads函数的作用就是将JSON字符串转成字典对象 + # 將收到的二進位制資料解碼成JSON字串並轉換成字典 + # loads函式的作用就是將JSON字串轉成字典物件 my_dict = loads(in_data.decode('utf-8')) filename = my_dict['filename'] filedata = my_dict['filedata'].encode('utf-8') with open('/Users/Hao/' + filename, 'wb') as f: - # 将base64格式的数据解码成二进制数据并写入文件 + # 將base64格式的資料解碼成二進位制資料並寫入檔案 f.write(b64decode(filedata)) - print('图片已保存.') + print('圖片已儲存.') if __name__ == '__main__': @@ -294,10 +294,10 @@ if __name__ == '__main__': ``` -在这个案例中,我们使用了JSON作为数据传输的格式(通过JSON格式对传输的数据进行了序列化和反序列化的操作),但是JSON并不能携带二进制数据,因此对图片的二进制数据进行了Base64编码的处理。Base64是一种用64个字符表示所有二进制数据的编码方式,通过将二进制数据每6位一组的方式重新组织,刚好可以使用0~9的数字、大小写字母以及“+”和“/”总共64个字符表示从`000000`到`111111`的64种状态。[维基百科](https://zh.wikipedia.org/wiki/Base64)上有关于Base64编码的详细讲解,不熟悉Base64的读者可以自行阅读。 +在這個案例中,我們使用了JSON作為資料傳輸的格式(通過JSON格式對傳輸的資料進行了序列化和反序列化的操作),但是JSON並不能攜帶二進位制資料,因此對圖片的二進位制資料進行了Base64編碼的處理。Base64是一種用64個字元表示所有二進位制資料的編碼方式,通過將二進位制資料每6位一組的方式重新組織,剛好可以使用0~9的數字、大小寫字母以及“+”和“/”總共64個字元表示從`000000`到`111111`的64種狀態。[維基百科](https://zh.wikipedia.org/wiki/Base64)上有關於Base64編碼的詳細講解,不熟悉Base64的讀者可以自行閱讀。 -> **说明**:上面的代码主要为了讲解网络编程的相关内容因此并没有对异常状况进行处理,请读者自行添加异常处理代码来增强程序的健壮性。 +> **說明**:上面的程式碼主要為了講解網路程式設計的相關內容因此並沒有對異常狀況進行處理,請讀者自行新增異常處理程式碼來增強程式的健壯性。 #### UDP套接字 -传输层除了有可靠的传输协议TCP之外,还有一种非常轻便的传输协议叫做用户数据报协议,简称UDP。TCP和UDP都是提供端到端传输服务的协议,二者的差别就如同打电话和发短信的区别,后者不对传输的可靠性和可达性做出任何承诺从而避免了TCP中握手和重传的开销,所以在强调性能和而不是数据完整性的场景中(例如传输网络音视频数据),UDP可能是更好的选择。可能大家会注意到一个现象,就是在观看网络视频时,有时会出现卡顿,有时会出现花屏,这无非就是部分数据传丢或传错造成的。在Python中也可以使用UDP套接字来创建网络应用,对此我们不进行赘述,有兴趣的读者可以自行研究。 +傳輸層除了有可靠的傳輸協議TCP之外,還有一種非常輕便的傳輸協議叫做使用者資料報協議,簡稱UDP。TCP和UDP都是提供端到端傳輸服務的協議,二者的差別就如同打電話和發簡訊的區別,後者不對傳輸的可靠性和可達性做出任何承諾從而避免了TCP中握手和重傳的開銷,所以在強調效能和而不是資料完整性的場景中(例如傳輸網路音視訊資料),UDP可能是更好的選擇。可能大家會注意到一個現象,就是在觀看網路視訊時,有時會出現卡頓,有時會出現花屏,這無非就是部分資料傳丟或傳錯造成的。在Python中也可以使用UDP套接字來建立網路應用,對此我們不進行贅述,有興趣的讀者可以自行研究。 diff --git "a/Day01-15/Day15/\347\275\221\347\273\234\345\272\224\347\224\250\345\274\200\345\217\221.md" "b/Day01-15/Day15/\347\275\221\347\273\234\345\272\224\347\224\250\345\274\200\345\217\221.md" index 36c5aafa2..c5342882f 100644 --- "a/Day01-15/Day15/\347\275\221\347\273\234\345\272\224\347\224\250\345\274\200\345\217\221.md" +++ "b/Day01-15/Day15/\347\275\221\347\273\234\345\272\224\347\224\250\345\274\200\345\217\221.md" @@ -1,12 +1,12 @@ -## 网络应用开发 +## 網路應用開發 -### 发送电子邮件 +### 傳送電子郵件 -在即时通信软件如此发达的今天,电子邮件仍然是互联网上使用最为广泛的应用之一,公司向应聘者发出录用通知、网站向用户发送一个激活账号的链接、银行向客户推广它们的理财产品等几乎都是通过电子邮件来完成的,而这些任务应该都是由程序自动完成的。 +在即時通訊軟體如此發達的今天,電子郵件仍然是網際網路上使用最為廣泛的應用之一,公司嚮應聘者發出錄用通知、網站向用戶傳送一個啟用賬號的連結、銀行向客戶推廣它們的理財產品等幾乎都是通過電子郵件來完成的,而這些任務應該都是由程式自動完成的。 -就像我们可以用HTTP(超文本传输协议)来访问一个网站一样,发送邮件要使用SMTP(简单邮件传输协议),SMTP也是一个建立在TCP(传输控制协议)提供的可靠数据传输服务的基础上的应用级协议,它规定了邮件的发送者如何跟发送邮件的服务器进行通信的细节,而Python中的smtplib模块将这些操作简化成了几个简单的函数。 +就像我們可以用HTTP(超文字傳輸協議)來訪問一個網站一樣,傳送郵件要使用SMTP(簡單郵件傳輸協議),SMTP也是一個建立在TCP(傳輸控制協議)提供的可靠資料傳輸服務的基礎上的應用級協議,它規定了郵件的傳送者如何跟傳送郵件的伺服器進行通訊的細節,而Python中的smtplib模組將這些操作簡化成了幾個簡單的函式。 -下面的代码演示了如何在Python发送邮件。 +下面的程式碼演示瞭如何在Python傳送郵件。 ```Python from smtplib import SMTP @@ -15,18 +15,18 @@ from email.mime.text import MIMEText def main(): - # 请自行修改下面的邮件发送者和接收者 + # 請自行修改下面的郵件傳送者和接收者 sender = 'abcdefg@126.com' receivers = ['uvwxyz@qq.com', 'uvwxyz@126.com'] - message = MIMEText('用Python发送邮件的示例代码.', 'plain', 'utf-8') - message['From'] = Header('王大锤', 'utf-8') - message['To'] = Header('骆昊', 'utf-8') - message['Subject'] = Header('示例代码实验邮件', 'utf-8') + message = MIMEText('用Python傳送郵件的示例程式碼.', 'plain', 'utf-8') + message['From'] = Header('王大錘', 'utf-8') + message['To'] = Header('駱昊', 'utf-8') + message['Subject'] = Header('示例程式碼實驗郵件', 'utf-8') smtper = SMTP('smtp.126.com') - # 请自行修改下面的登录口令 + # 請自行修改下面的登入口令 smtper.login(sender, 'secretpass') smtper.sendmail(sender, receivers, message.as_string()) - print('邮件发送完成!') + print('郵件傳送完成!') if __name__ == '__main__': @@ -34,7 +34,7 @@ if __name__ == '__main__': ``` -如果要发送带有附件的邮件,那么可以按照下面的方式进行操作。 +如果要傳送帶有附件的郵件,那麼可以按照下面的方式進行操作。 ```Python from smtplib import SMTP @@ -47,43 +47,43 @@ import urllib def main(): - # 创建一个带附件的邮件消息对象 + # 建立一個帶附件的郵件訊息物件 message = MIMEMultipart() - # 创建文本内容 - text_content = MIMEText('附件中有本月数据请查收', 'plain', 'utf-8') - message['Subject'] = Header('本月数据', 'utf-8') - # 将文本内容添加到邮件消息对象中 + # 建立文字內容 + text_content = MIMEText('附件中有本月資料請查收', 'plain', 'utf-8') + message['Subject'] = Header('本月資料', 'utf-8') + # 將文字內容新增到郵件訊息物件中 message.attach(text_content) - # 读取文件并将文件作为附件添加到邮件消息对象中 + # 讀取檔案並將檔案作為附件新增到郵件訊息物件中 with open('/Users/Hao/Desktop/hello.txt', 'rb') as f: txt = MIMEText(f.read(), 'base64', 'utf-8') txt['Content-Type'] = 'text/plain' txt['Content-Disposition'] = 'attachment; filename=hello.txt' message.attach(txt) - # 读取文件并将文件作为附件添加到邮件消息对象中 - with open('/Users/Hao/Desktop/汇总数据.xlsx', 'rb') as f: + # 讀取檔案並將檔案作為附件新增到郵件訊息物件中 + with open('/Users/Hao/Desktop/彙總資料.xlsx', 'rb') as f: xls = MIMEText(f.read(), 'base64', 'utf-8') xls['Content-Type'] = 'application/vnd.ms-excel' xls['Content-Disposition'] = 'attachment; filename=month-data.xlsx' message.attach(xls) - # 创建SMTP对象 + # 建立SMTP物件 smtper = SMTP('smtp.126.com') - # 开启安全连接 + # 開啟安全連線 # smtper.starttls() sender = 'abcdefg@126.com' receivers = ['uvwxyz@qq.com'] - # 登录到SMTP服务器 - # 请注意此处不是使用密码而是邮件客户端授权码进行登录 - # 对此有疑问的读者可以联系自己使用的邮件服务器客服 + # 登入到SMTP伺服器 + # 請注意此處不是使用密碼而是郵件客戶端授權碼進行登入 + # 對此有疑問的讀者可以聯絡自己使用的郵件伺服器客服 smtper.login(sender, 'secretpass') - # 发送邮件 + # 傳送郵件 smtper.sendmail(sender, receivers, message.as_string()) - # 与邮件服务器断开连接 + # 與郵件伺服器斷開連線 smtper.quit() - print('发送完成!') + print('傳送完成!') if __name__ == '__main__': @@ -91,9 +91,9 @@ if __name__ == '__main__': ``` -### 发送短信 +### 傳送簡訊 -发送短信也是项目中常见的功能,网站的注册码、验证码、营销信息基本上都是通过短信来发送给用户的。在下面的代码中我们使用了[互亿无线](http://www.ihuyi.com/)短信平台(该平台为注册用户提供了50条免费短信以及常用开发语言发送短信的demo,可以登录该网站并在用户自服务页面中对短信进行配置)提供的API接口实现了发送短信的服务,当然国内的短信平台很多,读者可以根据自己的需要进行选择(通常会考虑费用预算、短信达到率、使用的难易程度等指标),如果需要在商业项目中使用短信服务建议购买短信平台提供的套餐服务。 +傳送簡訊也是專案中常見的功能,網站的註冊碼、驗證碼、營銷資訊基本上都是通過簡訊來發送給使用者的。在下面的程式碼中我們使用了[互億無線](http://www.ihuyi.com/)簡訊平臺(該平臺為註冊使用者提供了50條免費簡訊以及常用開發語言傳送簡訊的demo,可以登入該網站並在使用者自服務頁面中對簡訊進行配置)提供的API介面實現了傳送簡訊的服務,當然國內的簡訊平臺很多,讀者可以根據自己的需要進行選擇(通常會考慮費用預算、簡訊達到率、使用的難易程度等指標),如果需要在商業專案中使用簡訊服務建議購買簡訊平臺提供的套餐服務。 ```Python import urllib.parse @@ -104,8 +104,8 @@ import json def main(): host = "106.ihuyi.com" sms_send_uri = "/webservice/sms.php?method=Submit" - # 下面的参数需要填入自己注册的账号和对应的密码 - params = urllib.parse.urlencode({'account': '你自己的账号', 'password' : '你自己的密码', 'content': '您的验证码是:147258。请不要把验证码泄露给其他人。', 'mobile': '接收者的手机号', 'format':'json' }) + # 下面的引數需要填入自己註冊的賬號和對應的密碼 + params = urllib.parse.urlencode({'account': '你自己的賬號', 'password' : '你自己的密碼', 'content': '您的驗證碼是:147258。請不要把驗證碼洩露給其他人。', 'mobile': '接收者的手機號', 'format':'json' }) print(params) headers = {'Content-type': 'application/x-www-form-urlencoded', 'Accept': 'text/plain'} conn = http.client.HTTPConnection(host, port=80, timeout=30) diff --git "a/Day16-20/Python\350\257\255\350\250\200\350\277\233\351\230\266.md" "b/Day16-20/Python\350\257\255\350\250\200\350\277\233\351\230\266.md" index 5d5064629..f7ddcd2d5 100644 --- "a/Day16-20/Python\350\257\255\350\250\200\350\277\233\351\230\266.md" +++ "b/Day16-20/Python\350\257\255\350\250\200\350\277\233\351\230\266.md" @@ -1,2 +1,2 @@ -## Python进阶知识 +## Python進階知識 diff --git "a/Day21-30/Web\345\211\215\347\253\257\346\246\202\350\277\260.md" "b/Day21-30/Web\345\211\215\347\253\257\346\246\202\350\277\260.md" index 088b2680c..c921d79e1 100644 --- "a/Day21-30/Web\345\211\215\347\253\257\346\246\202\350\277\260.md" +++ "b/Day21-30/Web\345\211\215\347\253\257\346\246\202\350\277\260.md" @@ -1,236 +1,236 @@ ## Web前端概述 -### HTML简史 - -1. 1991年10月:一个非正式CERN([欧洲核子研究中心](https://zh.wikipedia.org/wiki/%E6%AD%90%E6%B4%B2%E6%A0%B8%E5%AD%90%E7%A0%94%E7%A9%B6%E7%B5%84%E7%B9%94))文件首次公开18个HTML标签,这个文件的作者是物理学家[蒂姆·伯纳斯-李](https://zh.wikipedia.org/wiki/%E8%92%82%E5%A7%86%C2%B7%E4%BC%AF%E7%BA%B3%E6%96%AF-%E6%9D%8E),因此他是[万维网](https://zh.wikipedia.org/wiki/%E4%B8%87%E7%BB%B4%E7%BD%91)的发明者,也是[万维网联盟](https://zh.wikipedia.org/wiki/%E4%B8%87%E7%BB%B4%E7%BD%91%E8%81%94%E7%9B%9F)的主席。 -2. 1995年11月:HTML 2.0标准发布(RFC 1866)。 -3. 1997年1月:HTML 3.2作为[W3C](https://zh.wikipedia.org/wiki/W3C)推荐标准发布。 -4. 1997年12月:HTML 4.0作为W3C推荐标准发布。 -5. 1999年12月:HTML4.01作为W3C推荐标准发布。 -6. 2008年1月:HTML5由W3C作为工作草案发布。 -7. 2011年5月:W3C将HTML5推进至“最终征求”(Last Call)阶段。 -8. 2012年12月:W3C指定HTML5作为“候选推荐”阶段。 -9. 2014年10月:HTML5作为稳定W3C推荐标准发布,这意味着HTML5的标准化已经完成。 +### HTML簡史 + +1. 1991年10月:一個非正式CERN([歐洲核子研究中心](https://zh.wikipedia.org/wiki/%E6%AD%90%E6%B4%B2%E6%A0%B8%E5%AD%90%E7%A0%94%E7%A9%B6%E7%B5%84%E7%B9%94))檔案首次公開18個HTML標籤,這個檔案的作者是物理學家[蒂姆·伯納斯-李](https://zh.wikipedia.org/wiki/%E8%92%82%E5%A7%86%C2%B7%E4%BC%AF%E7%BA%B3%E6%96%AF-%E6%9D%8E),因此他是[全球資訊網](https://zh.wikipedia.org/wiki/%E4%B8%87%E7%BB%B4%E7%BD%91)的發明者,也是[全球資訊網聯盟](https://zh.wikipedia.org/wiki/%E4%B8%87%E7%BB%B4%E7%BD%91%E8%81%94%E7%9B%9F)的主席。 +2. 1995年11月:HTML 2.0標準釋出(RFC 1866)。 +3. 1997年1月:HTML 3.2作為[W3C](https://zh.wikipedia.org/wiki/W3C)推薦標準釋出。 +4. 1997年12月:HTML 4.0作為W3C推薦標準釋出。 +5. 1999年12月:HTML4.01作為W3C推薦標準釋出。 +6. 2008年1月:HTML5由W3C作為工作草案發布。 +7. 2011年5月:W3C將HTML5推進至“最終徵求”(Last Call)階段。 +8. 2012年12月:W3C指定HTML5作為“候選推薦”階段。 +9. 2014年10月:HTML5作為穩定W3C推薦標準釋出,這意味著HTML5的標準化已經完成。 #### HTML5新特性 -1. 引入原生多媒体支持(audio和video标签) -2. 引入可编程内容(canvas标签) -3. 引入语义Web(article、aside、details、figure、footer、header、nav、section、summary等标签) -4. 引入新的表单控件(日历、邮箱、搜索等) -5. 引入对离线存储更好的支持 -6. 引入对定位、拖放、WebSocket、后台任务等的支持 +1. 引入原生多媒體支援(audio和video標籤) +2. 引入可程式設計內容(canvas標籤) +3. 引入語義Web(article、aside、details、figure、footer、header、nav、section、summary等標籤) +4. 引入新的表單控制元件(日曆、郵箱、搜尋等) +5. 引入對離線儲存更好的支援 +6. 引入對定位、拖放、WebSocket、後臺任務等的支援 -### 使用标签承载内容 +### 使用標籤承載內容 -#### 结构 +#### 結構 - head - title - meta - body -#### 文本 +#### 文字 -- 标题和段落 -- 粗体和斜体 -- 上标和下标 -- 空白(白色空间折叠) -- 折行和水平标尺 -- 语义化标记 - - 加粗和强调 +- 標題和段落 +- 粗體和斜體 +- 上標和下標 +- 空白(白色空間摺疊) +- 折行和水平標尺 +- 語義化標記 + - 加粗和強調 - 引用 - - 缩写词和首字母缩写词 + - 縮寫詞和首字母縮寫詞 - 引文 - - 所有者联系信息 - - 内容的修改 + - 所有者聯絡資訊 + - 內容的修改 #### 列表(list) - 有序列表(ordered list) - - 无序列表(unordered list) - - 定义列表(definition list) + - 無序列表(unordered list) + - 定義列表(definition list) -#### 链接(anchor) +#### 連結(anchor) -- 页面链接 -- 锚链接 -- 功能链接 +- 頁面連結 +- 錨鏈接 +- 功能連結 -#### 图像(image) +#### 影象(image) -- 图像存储位置 -- 图像及其宽高 -- 选择正确的图像格式 +- 影象儲存位置 +- 影象及其寬高 +- 選擇正確的影象格式 - JPEG - GIF - PNG -- 矢量图 -- figure标签 +- 向量圖 +- figure標籤 #### 表格(table) -- 基本的表格结构 -- 表格的标题 +- 基本的表格結構 +- 表格的標題 - 跨行和跨列 -- 长表格 +- 長表格 -#### 表单(form) +#### 表單(form) -- 如何收集信息 -- 表单控件(input) - - 文本框 / 密码框 / 文本域 - - 单选按钮 / 复选按钮 / 下拉列表 - - 提交按钮 / 图像按钮 / 文件上传 -- 组合表单元素 +- 如何收集資訊 +- 表單控制元件(input) + - 文字框 / 密碼框 / 文字域 + - 單選按鈕 / 複選按鈕 / 下拉列表 + - 提交按鈕 / 影象按鈕 / 檔案上傳 +- 組合表單元素 - fieldset / legend -- HTML5的表单控件 +- HTML5的表單控制元件 - 日期 - - 电子邮件 / URL - - 搜索 + - 電子郵件 / URL + - 搜尋 -#### 音视频(audio / video) +#### 音視訊(audio / video) -- 视频格式和播放器 -- 视频托管服务 -- 添加视频的准备工作 -- video标签和属性 -- audio标签和属性 +- 視訊格式和播放器 +- 視訊託管服務 +- 新增視訊的準備工作 +- video標籤和屬性 +- audio標籤和屬性 #### 其他 -- 文档类型 -- 注释 -- 属性 +- 文件型別 +- 註釋 +- 屬性 - id - class -- 块级元素 / 行级元素 -- 内联框架(internal frame) -- 页面信息(meta) -- 转义字符(实体替换符) +- 塊級元素 / 行級元素 +- 內聯框架(internal frame) +- 頁面資訊(meta) +- 轉義字元(實體替換符) -### 使用CSS渲染页面 +### 使用CSS渲染頁面 -#### 简介 +#### 簡介 - CSS的作用 - CSS的工作原理 -- 规则、属性和值 +- 規則、屬性和值 -#### 颜色(color) +#### 顏色(color) -- 如何指定颜色 -- 颜色术语和颜色对比 +- 如何指定顏色 +- 顏色術語和顏色對比 - 背景色 -#### 文本(text / font) +#### 文字(text / font) -- 文本的大小和字型(font-size / font-family) -- 斜体、粗体、大写和下划线(font-weight / font-style / text-decoration) -- 行间距(line-height)、字母间距(letter-spacing)和单词间距(word-spacing) -- 对齐(text-align)方式和缩进(text-ident) -- 链接样式(:link / :visited / :active / :hover) -- CSS3新属性 +- 文字的大小和字型(font-size / font-family) +- 斜體、粗體、大寫和下劃線(font-weight / font-style / text-decoration) +- 行間距(line-height)、字母間距(letter-spacing)和單詞間距(word-spacing) +- 對齊(text-align)方式和縮排(text-ident) +- 連結樣式(:link / :visited / :active / :hover) +- CSS3新屬性 - 投影 - - 首字母和首行文本(p:first-letter / p:first-line) - - 响应用户 + - 首字母和首行文字(p:first-letter / p:first-line) + - 響應使用者 #### 盒子(box model) - 盒子大小的控制(width / height) -- 盒子的边框、外边距和内边距(border / margin / padding) -- 盒子的显示和隐藏(display / visibility) -- CSS3新属性 - - 边框图像(border-image) +- 盒子的邊框、外邊距和內邊距(border / margin / padding) +- 盒子的顯示和隱藏(display / visibility) +- CSS3新屬性 + - 邊框影象(border-image) - 投影(border-shadow) - - 圆角(border-radius) + - 圓角(border-radius) -#### 列表、表格和表单 +#### 列表、表格和表單 -- 列表的项目符号(list-style) -- 表格的边框和背景(border-collapse) -- 表单控件的外观 -- 表单控件的对齐 -- 浏览器的开发者工具 +- 列表的專案符號(list-style) +- 表格的邊框和背景(border-collapse) +- 表單控制元件的外觀 +- 表單控制元件的對齊 +- 瀏覽器的開發者工具 -#### 图像 +#### 影象 -- 控制图像的大小(display: inline-block) -- 对齐图像 -- 背景图像(background / background-image / background-repeat / background-position) +- 控制影象的大小(display: inline-block) +- 對齊影象 +- 背景影象(background / background-image / background-repeat / background-position) -#### 布局 +#### 佈局 - 控制元素的位置(position / z-index) - 普通流 - - 相对定位 - - 绝对定位 + - 相對定位 + - 絕對定位 - 固定定位 - - 浮动元素(float / clear) -- 网站布局 - - HTML5布局 -- 适配屏幕尺寸 - - 固定宽度布局 - - 流体布局 - - 布局网格 - -### 使用JavaScript控制行为 - -#### JavaScript基本语法 - -- 语句和注释 -- 变量和数据类型 - - 声明和赋值 - - 简单数据类型和复杂数据类型 - - 变量的命名规则 -- 表达式和运算符 - - 赋值运算符 - - 算术运算符 - - 比较运算符 - - 逻辑运算符 -- 分支结构 + - 浮動元素(float / clear) +- 網站佈局 + - HTML5佈局 +- 適配螢幕尺寸 + - 固定寬度佈局 + - 流體佈局 + - 佈局網格 + +### 使用JavaScript控制行為 + +#### JavaScript基本語法 + +- 語句和註釋 +- 變數和資料型別 + - 宣告和賦值 + - 簡單資料型別和複雜資料型別 + - 變數的命名規則 +- 表示式和運算子 + - 賦值運算子 + - 算術運算子 + - 比較運算子 + - 邏輯運算子 +- 分支結構 - if…else... - switch…case…default... -- 循环结构 - - for循环 - - while循环 - - do…while循环 -- 数组 - - 创建数组 - - 操作数组中的元素 -- 函数 - - 声明函数 - - 调用函数 - - 参数和返回值 - - 匿名函数 - - 立即调用函数 - -#### 面向对象 - - - 对象的概念 - - 创建对象的字面量语法 - - 访问成员运算符 - - 创建对象的构造函数语法 - - this关键字 - - 添加和删除属性 - - delete关键字 - - 全局对象 +- 迴圈結構 + - for迴圈 + - while迴圈 + - do…while迴圈 +- 陣列 + - 建立陣列 + - 運算元組中的元素 +- 函式 + - 宣告函式 + - 呼叫函式 + - 引數和返回值 + - 匿名函式 + - 立即呼叫函式 + +#### 面向物件 + + - 物件的概念 + - 建立物件的字面量語法 + - 訪問成員運算子 + - 建立物件的建構函式語法 + - this關鍵字 + - 新增和刪除屬性 + - delete關鍵字 + - 全域性物件 - Number / String / Boolean - Date / Math / RegEx / Array #### BOM - - window对象的属性和方法 - - history对象 + - window物件的屬性和方法 + - history物件 - forward() / back() / go() - - location对象 - - navigator对象 - - screen对象 + - location物件 + - navigator物件 + - screen物件 #### DOM - - DOM树 - - 访问元素 + - DOM樹 + - 訪問元素 - getElementById() / querySelector() - getElementsByClassName() / getElementsByTagName() / querySelectorAll() - parentNode / previousSibling / nextSibling / firstChild / lastChild @@ -238,30 +238,30 @@ - nodeValue - innerHTML / textContent / createElement() / createTextNode() / appendChild() / removeChild() - className / id / hasAttribute() / getAttribute() / setAttribute() / removeAttribute() -- 事件处理 - - 事件类型 +- 事件處理 + - 事件型別 - UI事件:load / unload / error / resize / scroll - - 键盘事件:keydown / keyup / keypress - - 鼠标事件:click / dbclick / mousedown / mouseup / mousemove / mouseover / mouseout - - 焦点事件:focus / blur - - 表单事件:input / change / submit / reset / cut / copy / paste / select - - 事件绑定 - - HTML事件处理程序(不推荐使用,因为要做到标签与代码分离) - - 传统的DOM事件处理程序(只能附加一个回调函数) - - 事件监听器(旧的浏览器中不被支持) - - 事件流:事件捕获 / 事件冒泡 - - 事件对象(低版本IE中的window.event) + - 鍵盤事件:keydown / keyup / keypress + - 滑鼠事件:click / dbclick / mousedown / mouseup / mousemove / mouseover / mouseout + - 焦點事件:focus / blur + - 表單事件:input / change / submit / reset / cut / copy / paste / select + - 事件繫結 + - HTML事件處理程式(不推薦使用,因為要做到標籤與程式碼分離) + - 傳統的DOM事件處理程式(只能附加一個回撥函式) + - 事件監聽器(舊的瀏覽器中不被支援) + - 事件流:事件捕獲 / 事件冒泡 + - 事件物件(低版本IE中的window.event) - target(低版本IE中的srcElement) - type - cancelable - preventDefault() - stopPropagation()(低版本IE中的cancelBubble) - - 鼠标事件 - 事件发生的位置 - - 屏幕位置:screenX和screenY - - 页面位置:pageX和pageY - - 客户端位置:clientX和clientY - - 键盘事件 - 哪个键被按下了 - - keyCode属性 + - 滑鼠事件 - 事件發生的位置 + - 螢幕位置:screenX和screenY + - 頁面位置:pageX和pageY + - 客戶端位置:clientX和clientY + - 鍵盤事件 - 哪個鍵被按下了 + - keyCode屬性 - String.fromCharCode(event.keyCode) - HTML5事件 - DOMContentLoaded @@ -276,14 +276,14 @@ #### jQuery概述 -1. Write Less Do More(用更少的代码来完成更多的工作) -2. 使用CSS选择器来查找元素(更简单更方便) -3. 使用jQuery方法来操作元素(解决浏览器兼容性问题、应用于所有元素并施加多个方法) +1. Write Less Do More(用更少的程式碼來完成更多的工作) +2. 使用CSS選擇器來查詢元素(更簡單更方便) +3. 使用jQuery方法來操作元素(解決瀏覽器相容性問題、應用於所有元素並施加多個方法) #### 引入jQuery -- 下载jQuery的开发版和压缩版 -- 从CDN加载jQuery +- 下載jQuery的開發版和壓縮版 +- 從CDN載入jQuery ```HTML @@ -293,45 +293,45 @@ ``` -#### 查找元素 +#### 查詢元素 -- 选择器 +- 選擇器 - \* / element / #id / .class / selector1, selector2 - ancestor descendant / parent>child / previous+next / previous~siblings -- 筛选器 - - 基本筛选器::not(selector) / :first / :last / :even / :odd / :eq(index) / :gt(index) / :lt(index) / :animated / :focus - - 内容筛选器::contains('…') / :empty / :parent / :has(selector) - - 可见性筛选器::hidden / :visible - - 子节点筛选器::nth-child(expr) / :first-child / :last-child / :only-child - - 属性筛选器:[attribute] / [attribute='value'] / [attribute!='value'] / [attribute^='value'] / [attribute$='value'] / [attribute|='value'] / [attribute~='value'] -- 表单::input / :text / :password / :radio / :checkbox / :submit / :image / :reset / :button / :file / :selected / :enabled / :disabled / :checked - -#### 执行操作 - -- 内容操作 - - 获取/修改内容:html() / text() / replaceWith() / remove() - - 获取/设置元素:before() / after() / prepend() / append() / remove() / clone() / unwrap() / detach() / empty() / add() - - 获取/修改属性:attr() / removeAttr() / addClass() / removeClass() / css() - - 获取/设置表单值:val() -- 查找操作 - - 查找方法:find() / parent() / children() / siblings() / next() / nextAll() / prev() / prevAll() - - 筛选器:filter() / not() / has() / is() / contains() - - 索引编号:eq() +- 篩選器 + - 基本篩選器::not(selector) / :first / :last / :even / :odd / :eq(index) / :gt(index) / :lt(index) / :animated / :focus + - 內容篩選器::contains('…') / :empty / :parent / :has(selector) + - 可見性篩選器::hidden / :visible + - 子節點篩選器::nth-child(expr) / :first-child / :last-child / :only-child + - 屬性篩選器:[attribute] / [attribute='value'] / [attribute!='value'] / [attribute^='value'] / [attribute$='value'] / [attribute|='value'] / [attribute~='value'] +- 表單::input / :text / :password / :radio / :checkbox / :submit / :image / :reset / :button / :file / :selected / :enabled / :disabled / :checked + +#### 執行操作 + +- 內容操作 + - 獲取/修改內容:html() / text() / replaceWith() / remove() + - 獲取/設定元素:before() / after() / prepend() / append() / remove() / clone() / unwrap() / detach() / empty() / add() + - 獲取/修改屬性:attr() / removeAttr() / addClass() / removeClass() / css() + - 獲取/設定表單值:val() +- 查詢操作 + - 查詢方法:find() / parent() / children() / siblings() / next() / nextAll() / prev() / prevAll() + - 篩選器:filter() / not() / has() / is() / contains() + - 索引編號:eq() - 尺寸和位置 - - 尺寸相关:height() / width() / innerHeight() / innerWidth() / outerWidth() / outerHeight() - - 位置相关:offset() / position() / scrollLeft() / scrollTop() -- 特效和动画 - - 基本动画:show() / hide() / toggle() - - 消失出现:fadeIn() / fadeOut() / fadeTo() / fadeToggle() - - 滑动效果:slideDown() / slideUp() / slideToggle() - - 自定义:delay() / stop() / animate() + - 尺寸相關:height() / width() / innerHeight() / innerWidth() / outerWidth() / outerHeight() + - 位置相關:offset() / position() / scrollLeft() / scrollTop() +- 特效和動畫 + - 基本動畫:show() / hide() / toggle() + - 消失出現:fadeIn() / fadeOut() / fadeTo() / fadeToggle() + - 滑動效果:slideDown() / slideUp() / slideToggle() + - 自定義:delay() / stop() / animate() - 事件 - - 文档加载:ready() / load() - - 用户交互:on() / off() + - 文件載入:ready() / load() + - 使用者互動:on() / off() -#### 链式操作 +#### 鏈式操作 -#### 检测页面是否可用 +#### 檢測頁面是否可用 ```HTML ``` -#### jQuery插件 +#### jQuery外掛 - jQuery Validation - jQuery Treeview - jQuery Autocomplete - jQuery UI -#### 避免和其他库的冲突 +#### 避免和其他庫的衝突 -先引入其他库再引入jQuery的情况。 +先引入其他庫再引入jQuery的情況。 ```HTML @@ -371,7 +371,7 @@ ``` -先引入jQuery再引入其他库的情况。 +先引入jQuery再引入其他庫的情況。 ```HTML @@ -386,22 +386,22 @@ #### 使用Ajax - 原生的Ajax -- 基于jQuery的Ajax - - 加载内容 - - 提交表单 +- 基於jQuery的Ajax + - 載入內容 + - 提交表單 ### 使用Bootstrap -#### 特点 +#### 特點 -1. 支持主流的浏览器和移动设备 +1. 支援主流的瀏覽器和移動裝置 2. 容易上手 -3. 响应式设计 +3. 響應式設計 -#### 内容 +#### 內容 -1. 网格系统 -2. 封装的CSS -3. 现成的组件 -4. JavaScript插件 +1. 網格系統 +2. 封裝的CSS +3. 現成的元件 +4. JavaScript外掛 diff --git "a/Day31-35/\347\216\251\350\275\254Linux.md" "b/Day31-35/\347\216\251\350\275\254Linux.md" index 3f31fad1b..8e97b87d4 100644 --- "a/Day31-35/\347\216\251\350\275\254Linux.md" +++ "b/Day31-35/\347\216\251\350\275\254Linux.md" @@ -1,32 +1,32 @@ -## 玩转Linux操作系统 +## 玩轉Linux作業系統 -### 操作系统发展史 +### 作業系統發展史 ![](./res/history-of-os.png) ### Linux概述 -Linux是一个通用操作系统。一个操作系统要负责任务调度、内存分配、处理外围设备I/O等操作。操作系统通常由内核和系统程序(设备驱动、底层库、shell、服务程序等)两部分组成。 +Linux是一個通用作業系統。一個作業系統要負責任務排程、記憶體分配、處理外圍裝置I/O等操作。作業系統通常由核心和系統程式(裝置驅動、底層庫、shell、服務程式等)兩部分組成。 -Linux内核是芬兰人Linus Torvalds开发的,于1991年9月发布。而Linux操作系统作为Internet时代的产物,它是由全世界许多开发者共同合作开发的,是一个自由的操作系统(注意是自由不是免费)。 +Linux核心是芬蘭人Linus Torvalds開發的,於1991年9月釋出。而Linux作業系統作為Internet時代的產物,它是由全世界許多開發者共同合作開發的,是一個自由的作業系統(注意是自由不是免費)。 -### Linux系统优点 +### Linux系統優點 -1. 通用操作系统,不跟特定的硬件绑定。 -2. 用C语言编写,有可移植性,有内核编程接口。 -3. 支持多用户和多任务,支持安全的分层文件系统。 -4. 大量的实用程序,完善的网络功能以及强大的支持文档。 -5. 可靠的安全性和良好的稳定性,对开发者更友好。 +1. 通用作業系統,不跟特定的硬體繫結。 +2. 用C語言編寫,有可移植性,有核心程式設計介面。 +3. 支援多使用者和多工,支援安全的分層檔案系統。 +4. 大量的實用程式,完善的網路功能以及強大的支援文件。 +5. 可靠的安全性和良好的穩定性,對開發者更友好。 -### 基础命令 +### 基礎命令 -Linux系统的命令通常都是如下所示的格式: +Linux系統的命令通常都是如下所示的格式: ```Shell -命令名称 [命名参数] [命令对象] +命令名稱 [命名引數] [命令物件] ``` -1. 获取登录信息 - **w** / **who** / **last**。 +1. 獲取登入資訊 - **w** / **who** / **last**。 ```Shell [root@izwz97tbgo9lkabnat2lo8z ~]# w @@ -41,9 +41,9 @@ Linux系统的命令通常都是如下所示的格式: root pts/0 2018-04-12 23:03 (182.139.66.250) ``` -2. 查看自己使用的Shell - **ps**。 +2. 檢視自己使用的Shell - **ps**。 - Shell也被称为“壳”,它是用户与内核交流的翻译官,简单的说就是人与计算机交互的接口。目前很多Linux系统默认的Shell都是bash(Bourne Again SHell),因为它可以使用Tab键进行命令补全、可以保存历史命令、可以方便的配置环境变量以及执行批处理操作等。 + Shell也被稱為“殼”,它是使用者與核心交流的翻譯官,簡單的說就是人與計算機互動的介面。目前很多Linux系統預設的Shell都是bash(Bourne Again SHell),因為它可以使用Tab鍵進行命令補全、可以儲存歷史命令、可以方便的配置環境變數以及執行批處理操作等。 ```Shell [root@izwz97tbgo9lkabnat2lo8z ~]# ps @@ -52,7 +52,7 @@ Linux系统的命令通常都是如下所示的格式: 3553 pts/0 00:00:00 ps ``` -3. 查看命令的说明 - **whatis**。 +3. 檢視命令的說明 - **whatis**。 ```Shell [root@izwz97tbgo9lkabnat2lo8z ~]# whatis ps @@ -61,7 +61,7 @@ Linux系统的命令通常都是如下所示的格式: python (1) - an interpreted, interactive, object-oriented programming language ``` -4. 查看命令的位置 - **which** / **whereis**。 +4. 檢視命令的位置 - **which** / **whereis**。 ```Shell [root@izwz97tbgo9lkabnat2lo8z ~]# whereis ps @@ -74,7 +74,7 @@ Linux系统的命令通常都是如下所示的格式: /usr/bin/python ``` -5. 查看帮助文档 - **man** / **info** / **apropos**。 +5. 檢視幫助文件 - **man** / **info** / **apropos**。 ```Shell [root@izwz97tbgo9lkabnat2lo8z ~]# ps --help Usage: @@ -95,14 +95,14 @@ Linux系统的命令通常都是如下所示的格式: ... ``` -6. 切换用户 - **su**。 +6. 切換使用者 - **su**。 ```Shell [root@izwz97tbgo9lkabnat2lo8z ~]# su hellokitty [hellokitty@izwz97tbgo9lkabnat2lo8z root]$ ``` -7. 以管理员身份执行命令 - **sudo**。 +7. 以管理員身份執行命令 - **sudo**。 ```Shell [jackfrued@izwz97tbgo9lkabnat2lo8z ~]$ ls /root @@ -112,9 +112,9 @@ Linux系统的命令通常都是如下所示的格式: calendar.py code error.txt hehe hello.c index.html myconf result.txt ``` - > **说明**:如果希望用户能够以管理员身份执行命令,用户必须在sudoers(/etc/sudoers)名单中。 + > **說明**:如果希望使用者能夠以管理員身份執行命令,使用者必須在sudoers(/etc/sudoers)名單中。 -8. 登入登出相关 - **logout** / **exit** / **adduser** / **userdel** / **passwd** / **ssh**。 +8. 登入登出相關 - **logout** / **exit** / **adduser** / **userdel** / **passwd** / **ssh**。 ```Shell [root@izwz97tbgo9lkabnat2lo8z ~]# adduser jackfrued @@ -131,7 +131,7 @@ Linux系统的命令通常都是如下所示的格式: [root@izwz97tbgo9lkabnat2lo8z ~]# ``` -9. 查看系统和主机名 - **uname** / **hostname**。 +9. 檢視系統和主機名 - **uname** / **hostname**。 ```Shell [root@izwz97tbgo9lkabnat2lo8z ~]# uname @@ -142,32 +142,32 @@ Linux系统的命令通常都是如下所示的格式: CentOS Linux release 7.4.1708 (Core) ``` -10. 重启和关机 - **reboot** / **init 6** / **shutdown** / **init 0**。 +10. 重啟和關機 - **reboot** / **init 6** / **shutdown** / **init 0**。 -11. 查看历史命令 - **history**。 +11. 檢視歷史命令 - **history**。 -### 实用程序 +### 實用程式 -#### 文件和文件夹操作 +#### 檔案和資料夾操作 -1. 创建/删除目录 - **mkdir** / **rmdir**。 +1. 建立/刪除目錄 - **mkdir** / **rmdir**。 -2. 创建/删除文件 - **touch** / **rm**。 +2. 建立/刪除檔案 - **touch** / **rm**。 - - touch命令用于创建空白文件或修改文件时间。在Linux系统中一个文件有三种时间: - - 更改内容的时间(mtime) - - 更改权限的时间(ctime) - - 最后访问时间(atime) + - touch命令用於建立空白檔案或修改檔案時間。在Linux系統中一個檔案有三種時間: + - 更改內容的時間(mtime) + - 更改許可權的時間(ctime) + - 最後訪問時間(atime) -3. 切换和查看当前工作目录 - **cd** / **pwd**。 +3. 切換和檢視當前工作目錄 - **cd** / **pwd**。 -4. 查看目录内容 - **ls**。 +4. 檢視目錄內容 - **ls**。 -5. 查看文件内容 - **cat** / **head** / **tail** / **more** / **less**。 +5. 檢視檔案內容 - **cat** / **head** / **tail** / **more** / **less**。 -6. 拷贝/移动文件 - **cp** / **mv**。 +6. 拷貝/移動檔案 - **cp** / **mv**。 -7. 查看文件及内容 - **find** / **grep**。 +7. 檢視檔案及內容 - **find** / **grep**。 ```Shell [root@izwz97tbgo9lkabnat2lo8z ~]# find -name *.html @@ -179,89 +179,89 @@ Linux系统的命令通常都是如下所示的格式: ./code/foo.html:2: ** / **2\>**。 -3. 输入重定向 - **\<**。 +2. 輸出重定向和錯誤重定向 - **\>** / **2\>**。 +3. 輸入重定向 - **\<**。 -#### 别名 +#### 別名 1. **alias** 2. **unalias** -#### 其他程序 - -1. 时间和日期 - **date** / **cal**。 -2. 录制操作脚本 - **script**。 -3. 给用户发送消息 - **mesg** / **write** / **wall** / **mail**。 - -### 文件系统 - -#### 文件和路径 - -1. 命名规则 -2. 扩展名 -3. 隐藏文件 -4. 工作目录和主目录 -5. 绝对路径和相对路径 - -#### 目录结构 - -1. /bin - 基本命令的二进制文件 -2. /boot - 引导加载程序的静态文件 -3. /dev - 设备文件 -4. /etc - 配置文件 -5. /home - 用户主目录的父目录 -6. /lib - 共享库文件 -7. /lib64 - 共享64位库文件 -8. /lost+found - 存放未链接文件 -9. /media - 自动识别设备的挂载目录 -10. /mnt - 临时挂载文件系统的挂载点 -11. /opt - 可选插件软件包安装位置 -12. /proc - 内核和进程信息 -13. /root - root账户主目录 -14. /run - 存放系统运行时需要的东西 -15. /sbin - 超级用户的二进制文件 -16. /sys - 设备的伪文件系统 -17. /tmp - 临时文件夹 -18. /usr - 用户应用目录 -19. /var - 变量数据目录 - -#### 访问权限 +#### 其他程式 + +1. 時間和日期 - **date** / **cal**。 +2. 錄製操作指令碼 - **script**。 +3. 給使用者傳送訊息 - **mesg** / **write** / **wall** / **mail**。 + +### 檔案系統 + +#### 檔案和路徑 + +1. 命名規則 +2. 副檔名 +3. 隱藏檔案 +4. 工作目錄和主目錄 +5. 絕對路徑和相對路徑 + +#### 目錄結構 + +1. /bin - 基本命令的二進位制檔案 +2. /boot - 引導載入程式的靜態檔案 +3. /dev - 裝置檔案 +4. /etc - 配置檔案 +5. /home - 使用者主目錄的父目錄 +6. /lib - 共享庫檔案 +7. /lib64 - 共享64位庫檔案 +8. /lost+found - 存放未連結檔案 +9. /media - 自動識別裝置的掛載目錄 +10. /mnt - 臨時掛載檔案系統的掛載點 +11. /opt - 可選外掛軟體包安裝位置 +12. /proc - 核心和程序資訊 +13. /root - root賬戶主目錄 +14. /run - 存放系統執行時需要的東西 +15. /sbin - 超級使用者的二進位制檔案 +16. /sys - 裝置的偽檔案系統 +17. /tmp - 臨時資料夾 +18. /usr - 使用者應用目錄 +19. /var - 變數資料目錄 + +#### 訪問許可權 1. **chmod**。 2. **chown**。 -#### 磁盘管理 +#### 磁碟管理 -1. 列出文件系统的磁盘使用状况 - **df**。 -2. 磁盘分区表操作 - **fdisk**。 -3. 格式化文件系统 - **mkfs**。 -4. 文件系统检查 - **fsck**。 -5. 挂载/卸载 - **mount** / **umount**。 +1. 列出檔案系統的磁碟使用狀況 - **df**。 +2. 磁碟分割槽表操作 - **fdisk**。 +3. 格式化檔案系統 - **mkfs**。 +4. 檔案系統檢查 - **fsck**。 +5. 掛載/解除安裝 - **mount** / **umount**。 -### 编辑器vim +### 編輯器vim -1. 启动和退出 +1. 啟動和退出 -2. 命令模式和编辑模式 +2. 命令模式和編輯模式 -3. 光标操作 +3. 游標操作 -4. 文本操作 +4. 文字操作 -5. 查找和替换 +5. 查詢和替換 - /正则表达式 + /正則表示式 - :1,$s/正则表达式/替换后的内容/gice + :1,$s/正則表示式/替換後的內容/gice g - global @@ -271,7 +271,7 @@ Linux系统的命令通常都是如下所示的格式: e - error -6. 参数设定 +6. 引數設定 .vimrc @@ -279,17 +279,17 @@ Linux系统的命令通常都是如下所示的格式: set nu -7. 高级技巧 +7. 高階技巧 - - 映射快捷键 + - 對映快捷鍵 - inoremap key:... - - 录制宏 - - 在命令模式下输入qa开始录制宏(qa/qb/qc/qd) - - 执行你的操作,这些操作都会被录制下来 - - 如果要录制的操作完成了,按q结束录制 - - @a播放宏(1000@a - 将宏播放1000次) + - 錄製巨集 + - 在命令模式下輸入qa開始錄製巨集(qa/qb/qc/qd) + - 執行你的操作,這些操作都會被錄製下來 + - 如果要錄製的操作完成了,按q結束錄製 + - @a播放巨集(1000@a - 將巨集播放1000次) -### 环境变量 +### 環境變數 1. HOME 2. SHELL @@ -297,7 +297,7 @@ Linux系统的命令通常都是如下所示的格式: 4. RANDOM 5. PATH -### 软件安装和配置 +### 軟體安裝和配置 #### yum @@ -312,43 +312,43 @@ Linux系统的命令通常都是如下所示的格式: - rpm -e - rpm -qa | grep -#### 源代码构建安装 +#### 原始碼構建安裝 - ... - make && make install -#### 实例 +#### 例項 -1. 安装MySQL。 -2. 安装Redis。 -3. 安装NginX。 +1. 安裝MySQL。 +2. 安裝Redis。 +3. 安裝NginX。 -### 配置服务 +### 配置服務 1. systemctl start / stop / restart / status 2. systemctl enable / disable -3. 计划任务 - **crontab**。 -4. 开机自启。 +3. 計劃任務 - **crontab**。 +4. 開機自啟。 -### 网络访问和管理 +### 網路訪問和管理 -1. 通过网络获取资源 - **wget**。 - - -b 后台下载模式 - - -O 下载到指定的目录 - - -r 递归下载 -2. 显示/操作网络配置(旧) - **ipconfig**。 -3. 显示/操作网络配置(新) - **ip**。 -4. 网络可达性检查 - **ping**。 -5. 查看网络服务和端口 - **netstat**。 -6. 安全文件拷贝 - **scp**。 -7. 安全文件传输 - **sftp**。 +1. 通過網路獲取資源 - **wget**。 + - -b 後臺下載模式 + - -O 下載到指定的目錄 + - -r 遞迴下載 +2. 顯示/操作網路配置(舊) - **ipconfig**。 +3. 顯示/操作網路配置(新) - **ip**。 +4. 網路可達性檢查 - **ping**。 +5. 檢視網路服務和埠 - **netstat**。 +6. 安全檔案拷貝 - **scp**。 +7. 安全檔案傳輸 - **sftp**。 -### Shell和Shell编程 +### Shell和Shell程式設計 -1. 通配符。 -2. 后台运行。 +1. 萬用字元。 +2. 後臺執行。 -### 其他内容 +### 其他內容 1. awk 2. sed diff --git "a/Day36-40/\345\205\263\347\263\273\345\236\213\346\225\260\346\215\256\345\272\223MySQL.md" "b/Day36-40/\345\205\263\347\263\273\345\236\213\346\225\260\346\215\256\345\272\223MySQL.md" index 4369712dc..1c44f6e9c 100644 --- "a/Day36-40/\345\205\263\347\263\273\345\236\213\346\225\260\346\215\256\345\272\223MySQL.md" +++ "b/Day36-40/\345\205\263\347\263\273\345\236\213\346\225\260\346\215\256\345\272\223MySQL.md" @@ -1,25 +1,25 @@ -## 关系型数据入门 +## 關係型資料入門 -### 关系型数据概述 +### 關係型資料概述 -1. 数据持久化。 -2. 数据库发展史。 -3. 关系型数据库特点。 -4. E-R图。 -5. 关系型数据库产品。 +1. 資料持久化。 +2. 資料庫發展史。 +3. 關係型資料庫特點。 +4. E-R圖。 +5. 關係型資料庫產品。 -### MySQL简介 +### MySQL簡介 -1. 安装和配置。 +1. 安裝和配置。 2. 常用命令。 -### SQL详解 +### SQL詳解 1. DDL 2. DML 3. DQL -### Python数据库编程 +### Python資料庫程式設計 1. MySQLdb 2. PyMySQL diff --git "a/Day36-40/\351\235\236\345\205\263\347\263\273\345\236\213\346\225\260\346\215\256\345\272\223Redis.md" "b/Day36-40/\351\235\236\345\205\263\347\263\273\345\236\213\346\225\260\346\215\256\345\272\223Redis.md" index 76cb580c5..dad979eb9 100644 --- "a/Day36-40/\351\235\236\345\205\263\347\263\273\345\236\213\346\225\260\346\215\256\345\272\223Redis.md" +++ "b/Day36-40/\351\235\236\345\205\263\347\263\273\345\236\213\346\225\260\346\215\256\345\272\223Redis.md" @@ -1,14 +1,14 @@ -## Redis实战 +## Redis實戰 ### NoSQL概述 -### Redis安装和配置 +### Redis安裝和配置 -### Redis的数据类型 +### Redis的資料型別 diff --git "a/Day41-55/Django2\345\256\236\346\210\23001.md" "b/Day41-55/Django2\345\256\236\346\210\23001.md" index f5a70aab1..7213d0571 100644 --- "a/Day41-55/Django2\345\256\236\346\210\23001.md" +++ "b/Day41-55/Django2\345\256\236\346\210\23001.md" @@ -1,55 +1,55 @@ -## Django 2.x实战(01) - 快速上手 +## Django 2.x實戰(01) - 快速上手 -Web开发的早期阶段,开发者需要手动编写每个页面,例如一个新闻门户网站,每天都要修改它的HTML页面,这样随着网站规模和体量的增大,这种方式就变得极度糟糕。为了解决这个问题,开发人员想到了用外部程序来为Web服务器生成动态内容,也就是说HTML页面以及页面中的动态内容不再通过手动编写而是通过程序自动生成。最早的时候,这项技术被称为CGI(公共网关接口),当然随着时间的推移,CGI暴露出的问题也越来越多,例如大量重复的样板代码,总体性能较为低下等,因此在呼唤新的英雄的时代,PHP、ASP、JSP这类Web应用开发技术在上世纪90年代中后期如雨后春笋般涌现。通常我们说的Web应用是指通过浏览器来访问网络资源的应用程序,因为浏览器的普及性以及易用性,Web应用使用起来方便简单,而且在应用更新时用户通常不需要做任何的处理就能使用更新后的应用,而且也不用关心用户到底用的是什么操作系统,甚至不用区分是PC端还是移动端。 +Web開發的早期階段,開發者需要手動編寫每個頁面,例如一個新聞入口網站,每天都要修改它的HTML頁面,這樣隨著網站規模和體量的增大,這種方式就變得極度糟糕。為了解決這個問題,開發人員想到了用外部程式來為Web伺服器生成動態內容,也就是說HTML頁面以及頁面中的動態內容不再通過手動編寫而是通過程式自動生成。最早的時候,這項技術被稱為CGI(公共閘道器介面),當然隨著時間的推移,CGI暴露出的問題也越來越多,例如大量重複的樣板程式碼,總體效能較為低下等,因此在呼喚新的英雄的時代,PHP、ASP、JSP這類Web應用開發技術在上世紀90年代中後期如雨後春筍般湧現。通常我們說的Web應用是指通過瀏覽器來訪問網路資源的應用程式,因為瀏覽器的普及性以及易用性,Web應用使用起來方便簡單,而且在應用更新時使用者通常不需要做任何的處理就能使用更新後的應用,而且也不用關心使用者到底用的是什麼作業系統,甚至不用區分是PC端還是移動端。 -### Web应用机制和术语 +### Web應用機制和術語 -下图向我们展示了Web应用的工作流程,其中涉及到的术语如下表所示。 +下圖向我們展示了Web應用的工作流程,其中涉及到的術語如下表所示。 ![](./res/web-application.png) -> 说明:相信有经验的读者会发现,这张图中其实还少了很多东西,例如反向代理服务器、数据库服务器、防火墙等,而且图中的每个节点在实际项目部署时可能是一组节点组成的集群。当然,如果你对这些没有什么概念也不要紧,后续的课程中我们会为大家进行讲解。 +> 說明:相信有經驗的讀者會發現,這張圖中其實還少了很多東西,例如反向代理伺服器、資料庫伺服器、防火牆等,而且圖中的每個節點在實際專案部署時可能是一組節點組成的叢集。當然,如果你對這些沒有什麼概念也不要緊,後續的課程中我們會為大家進行講解。 -| 术语 | 解释 | +| 術語 | 解釋 | | ------------- | ------------------------------------------------------------ | -| **URL/URI** | 统一资源定位符/统一资源标识符,网络资源的唯一标识 | -| **域名** | 与Web服务器地址对应的一个易于记忆的字符串名字 | -| **DNS** | 域名解析服务,可以将域名转换成对应的IP地址 | -| **IP地址** | 网络上的主机的身份标识,通过IP地址可以区分不同的主机 | -| **HTTP** | 超文本传输协议,应用层协议,万维网数据通信的基础 | -| **反向代理** | 代理客户端向服务器发出请求,然后将服务器返回的资源返回给客户端 | -| **Web服务器** | 接受HTTP请求,然后返回HTML文件、纯文本文件、图像等资源给请求者 | -| **Nginx** | 高性能的Web服务器,也可以用作[反向代理](https://zh.wikipedia.org/wiki/%E5%8F%8D%E5%90%91%E4%BB%A3%E7%90%86),[负载均衡](https://zh.wikipedia.org/wiki/%E8%B4%9F%E8%BD%BD%E5%9D%87%E8%A1%A1) 和 [HTTP缓存](https://zh.wikipedia.org/wiki/HTTP%E7%BC%93%E5%AD%98) | +| **URL/URI** | 統一資源定位符/統一資源識別符號,網路資源的唯一標識 | +| **域名** | 與Web伺服器地址對應的一個易於記憶的字串名字 | +| **DNS** | 域名解析服務,可以將域名轉換成對應的IP地址 | +| **IP地址** | 網路上的主機的身份標識,通過IP地址可以區分不同的主機 | +| **HTTP** | 超文字傳輸協議,應用層協議,全球資訊網資料通訊的基礎 | +| **反向代理** | 代理客戶端向伺服器發出請求,然後將伺服器返回的資源返回給客戶端 | +| **Web伺服器** | 接受HTTP請求,然後返回HTML檔案、純文字檔案、影象等資源給請求者 | +| **Nginx** | 高效能的Web伺服器,也可以用作[反向代理](https://zh.wikipedia.org/wiki/%E5%8F%8D%E5%90%91%E4%BB%A3%E7%90%86),[負載均衡](https://zh.wikipedia.org/wiki/%E8%B4%9F%E8%BD%BD%E5%9D%87%E8%A1%A1) 和 [HTTP快取](https://zh.wikipedia.org/wiki/HTTP%E7%BC%93%E5%AD%98) | -#### HTTP协议 +#### HTTP協議 -这里我们稍微费一些笔墨来谈谈上面提到的HTTP。HTTP(超文本传输协议)是构建于TCP(传输控制协议)之上应用级协议,它利用了TCP提供的可靠的传输服务实现了Web应用中的数据交换。按照维基百科上的介绍,设计HTTP最初的目的是为了提供一种发布和接收[HTML](https://zh.wikipedia.org/wiki/HTML)页面的方法,也就是说这个协议是浏览器和Web服务器之间传输的数据的载体。关于这个协议的详细信息以及目前的发展状况,大家可以阅读阮一峰老师的[《HTTP 协议入门》](http://www.ruanyifeng.com/blog/2016/08/http.html)、[《互联网协议入门》](http://www.ruanyifeng.com/blog/2012/05/internet_protocol_suite_part_i.html)系列以及[《图解HTTPS协议》](http://www.ruanyifeng.com/blog/2014/09/illustration-ssl.html)进行了解,下图是我于2009年9月10日凌晨4点在四川省网络通信技术重点实验室用开源协议分析工具Ethereal(抓包工具WireShark的前身)截取的访问百度首页时的HTTP请求和响应的报文(协议数据),由于Ethereal截取的是经过网络适配器的数据,因此可以清晰的看到从物理链路层到应用层的协议数据。 +這裡我們稍微費一些筆墨來談談上面提到的HTTP。HTTP(超文字傳輸協議)是構建於TCP(傳輸控制協議)之上應用級協議,它利用了TCP提供的可靠的傳輸服務實現了Web應用中的資料交換。按照維基百科上的介紹,設計HTTP最初的目的是為了提供一種釋出和接收[HTML](https://zh.wikipedia.org/wiki/HTML)頁面的方法,也就是說這個協議是瀏覽器和Web伺服器之間傳輸的資料的載體。關於這個協議的詳細資訊以及目前的發展狀況,大家可以閱讀阮一峰老師的[《HTTP 協議入門》](http://www.ruanyifeng.com/blog/2016/08/http.html)、[《網際網路協議入門》](http://www.ruanyifeng.com/blog/2012/05/internet_protocol_suite_part_i.html)系列以及[《圖解HTTPS協議》](http://www.ruanyifeng.com/blog/2014/09/illustration-ssl.html)進行了解,下圖是我於2009年9月10日凌晨4點在四川省網路通訊技術重點實驗室用開源協議分析工具Ethereal(抓包工具WireShark的前身)擷取的訪問百度首頁時的HTTP請求和響應的報文(協議資料),由於Ethereal擷取的是經過網路介面卡的資料,因此可以清晰的看到從物理鏈路層到應用層的協議資料。 -HTTP请求(请求行+请求头+空行+[消息体]): +HTTP請求(請求行+請求頭+空行+[訊息體]): ![](./res/http-request.png) -HTTP响应(响应行+响应头+空行+消息体): +HTTP響應(響應行+響應頭+空行+訊息體): ![](./res/http-response.png) -> 说明:但愿这两张如同泛黄的照片般的截图帮助你大概的了解到HTTP是一个怎样的协议。 +> 說明:但願這兩張如同泛黃的照片般的截圖幫助你大概的瞭解到HTTP是一個怎樣的協議。 ### Django概述 -Python的Web框架有上百个,比它的关键字还要多。所谓Web框架,就是用于开发Web服务器端应用的基础设施(通常指封装好的模块和一系列的工具)。事实上,即便没有Web框架,我们仍然可以通过socket或[CGI](https://zh.wikipedia.org/wiki/%E9%80%9A%E7%94%A8%E7%BD%91%E5%85%B3%E6%8E%A5%E5%8F%A3)来开发Web服务器端应用,但是这样做的成本和代价在实际开发中通常是不能接受的。通过Web框架,我们可以化繁为简,同时降低创建、更新、扩展应用程序的工作量。Python的Web框架中比较有名的有:Flask、Django、Tornado、Pyramid、Bottle、Web2py、web.py等。 +Python的Web框架有上百個,比它的關鍵字還要多。所謂Web框架,就是用於開發Web伺服器端應用的基礎設施(通常指封裝好的模組和一系列的工具)。事實上,即便沒有Web框架,我們仍然可以通過socket或[CGI](https://zh.wikipedia.org/wiki/%E9%80%9A%E7%94%A8%E7%BD%91%E5%85%B3%E6%8E%A5%E5%8F%A3)來開發Web伺服器端應用,但是這樣做的成本和代價在實際開發中通常是不能接受的。通過Web框架,我們可以化繁為簡,同時降低建立、更新、擴充套件應用程式的工作量。Python的Web框架中比較有名的有:Flask、Django、Tornado、Pyramid、Bottle、Web2py、web.py等。 -在基于Python的Web框架中,Django是所有重量级选手中最有代表性的一位,开发者可以基于Django快速的开发可靠的Web应用程序,因为它减少了Web开发中不必要的开销,对常用的设计和开发模式进行了封装,并对MVC架构提供了支持(MTV)。许多成功的网站和App都是基于Django框架构建的,国内比较有代表性的网站包括:知乎、豆瓣网、果壳网、搜狐闪电邮箱、101围棋网、海报时尚网、背书吧、堆糖、手机搜狐网、咕咚、爱福窝、果库等。 +在基於Python的Web框架中,Django是所有重量級選手中最有代表性的一位,開發者可以基於Django快速的開發可靠的Web應用程式,因為它減少了Web開發中不必要的開銷,對常用的設計和開發模式進行了封裝,並對MVC架構提供了支援(MTV)。許多成功的網站和App都是基於Django框架構建的,國內比較有代表性的網站包括:知乎、豆瓣網、果殼網、搜狐閃電郵箱、101圍棋網、海報時尚網、背書吧、堆糖、手機搜狐網、咕咚、愛福窩、果庫等。 ![](./res/mvc.png) -Django诞生于2003年,它是一个在真正的应用中成长起来的项目,由劳伦斯出版集团旗下在线新闻网站的内容管理系统(CMS)研发团队编写(主要是Adrian Holovaty和Simon Willison),以比利时的吉普赛爵士吉他手Django Reinhardt来命名,在2005年夏天作为开源框架发布。使用Django能用很短的时间构建出功能完备的网站,因为它代替程序员完成了所有乏味和重复的劳动,剩下真正有意义的核心业务给程序员,这一点就是对DRY(Don't Repeat Yourself)理念的最好践行。 +Django誕生於2003年,它是一個在真正的應用中成長起來的專案,由勞倫斯出版集團旗下線上新聞網站的內容管理系統(CMS)研發團隊編寫(主要是Adrian Holovaty和Simon Willison),以比利時的吉普賽爵士吉他手Django Reinhardt來命名,在2005年夏天作為開源框架釋出。使用Django能用很短的時間構建出功能完備的網站,因為它代替程式設計師完成了所有乏味和重複的勞動,剩下真正有意義的核心業務給程式設計師,這一點就是對DRY(Don't Repeat Yourself)理念的最好踐行。 ### 快速上手 -#### 准备工作 +#### 準備工作 -1. 检查Python环境:Django 1.11需要Python 2.7或Python 3.4以上的版本;Django 2.0需要Python 3.4以上的版本。 +1. 檢查Python環境:Django 1.11需要Python 2.7或Python 3.4以上的版本;Django 2.0需要Python 3.4以上的版本。 ```Shell $ python3 --version @@ -62,41 +62,41 @@ Django诞生于2003年,它是一个在真正的应用中成长起来的项目 >>> sys.version_info ``` -2. 创建项目文件夹并切换到该目录,例如我们要实例一个OA(办公自动化)项目。 +2. 建立專案資料夾並切換到該目錄,例如我們要例項一個OA(辦公自動化)專案。 ```Shell $ mkdir oa $ cd oa ``` -3. 创建并激活虚拟环境。 +3. 建立並激活虛擬環境。 ```Shell $ python3 -m venv venv $ source venv/bin/activate ``` - > 注意:Windows系统下是执行`venv/Scripts/activate.bat`批处理文件。 + > 注意:Windows系統下是執行`venv/Scripts/activate.bat`批處理檔案。 4. 更新包管理工具pip。 ```Shell (venv)$ python -m pip install --upgrade pip ``` - > 注意:请注意终端提示符发生的变化,前面的`(venv)`说明我们已经进入虚拟环境,而虚拟环境下的python和pip已经是Python 3的解释器和包管理工具了。 + > 注意:請注意終端提示符發生的變化,前面的`(venv)`說明我們已經進入虛擬環境,而虛擬環境下的python和pip已經是Python 3的直譯器和包管理工具了。 -5. 安装Django。 +5. 安裝Django。 ```Shell (venv)$ pip install django ``` - 或指定版本号来安装对应的Django的版本。 + 或指定版本號來安裝對應的Django的版本。 ```Shell (venv)$ pip install django==1.11 ``` -6. 检查Django的版本。 +6. 檢查Django的版本。 ```Shell (venv)$ python -m django --version @@ -108,7 +108,7 @@ Django诞生于2003年,它是一个在真正的应用中成长起来的项目 >>> import django >>> django.get_version() ``` - 下图展示了Django版本和Python版本的对应关系,在我们的项目中我们选择了最新的Django 2.0的版本。 + 下圖展示了Django版本和Python版本的對應關係,在我們的專案中我們選擇了最新的Django 2.0的版本。 | Django版本 | Python版本 | | ---------- | ----------------------- | @@ -117,44 +117,44 @@ Django诞生于2003年,它是一个在真正的应用中成长起来的项目 | 1.11 | 2.7、3.4、3.5、3.6 | | 2.0 | 3.4、3.5、3.6 | - > 说明:在创建这篇文章时Django 2.1版本尚未正式发布,因此我们的教程使用了2.0.5版本。 + > 說明:在建立這篇文章時Django 2.1版本尚未正式釋出,因此我們的教程使用了2.0.5版本。 -7. 使用`django-admin`创建项目,项目命名为oa。 +7. 使用`django-admin`建立專案,專案命名為oa。 ```Shell (venv)$ django-admin startproject oa . ``` - > 注意:上面的命令最后的那个点,它表示在当前路径下创建项目。 + > 注意:上面的命令最後的那個點,它表示在當前路徑下建立專案。 - 执行上面的命令后看看生成的文件和文件夹,它们的作用如下所示: + 執行上面的命令後看看生成的檔案和資料夾,它們的作用如下所示: - - `manage.py`: 一个让你用各种方式管理 Django 项目的命令行工具。 - - `oa/__init__.py`:一个空文件,告诉 Python 这个目录应该被认为是一个 Python 包。 - - `oa/settings.py`:Django 项目的配置文件。 - - `oa/urls.py`:Django 项目的 URL 声明,就像你网站的“目录”。 - - `oa/wsgi.py`:作为你的项目的运行在 WSGI 兼容的Web服务器上的入口。 + - `manage.py`: 一個讓你用各種方式管理 Django 專案的命令列工具。 + - `oa/__init__.py`:一個空檔案,告訴 Python 這個目錄應該被認為是一個 Python 包。 + - `oa/settings.py`:Django 專案的配置檔案。 + - `oa/urls.py`:Django 專案的 URL 宣告,就像你網站的“目錄”。 + - `oa/wsgi.py`:作為你的專案的執行在 WSGI 相容的Web伺服器上的入口。 -8. 启动服务器运行项目。 +8. 啟動伺服器執行專案。 ```Shell (venv)$ python manage.py runserver ``` - 在浏览器中输入访问我们的服务器,效果如下图所示。 + 在瀏覽器中輸入訪問我們的伺服器,效果如下圖所示。 ![](./res/django-index-1.png) - > 说明1:刚刚启动的是Django自带的用于开发和测试的服务器,它是一个用纯Python编写的轻量级Web服务器,但它并不是真正意义上的生产级别的服务器,千万不要将这个服务器用于和生产环境相关的任何地方。 + > 說明1:剛剛啟動的是Django自帶的用於開發和測試的伺服器,它是一個用純Python編寫的輕量級Web伺服器,但它並不是真正意義上的生產級別的伺服器,千萬不要將這個伺服器用於和生產環境相關的任何地方。 - > 说明2:用于开发的服务器在需要的情况下会对每一次的访问请求重新载入一遍Python代码。所以你不需要为了让修改的代码生效而频繁的重新启动服务器。然而,一些动作,比如添加新文件,将不会触发自动重新加载,这时你得自己手动重启服务器。 + > 說明2:用於開發的伺服器在需要的情況下會對每一次的訪問請求重新載入一遍Python程式碼。所以你不需要為了讓修改的程式碼生效而頻繁的重新啟動伺服器。然而,一些動作,比如新增新檔案,將不會觸發自動重新載入,這時你得自己手動重啟伺服器。 - > 说明3:可以通过`python manage.py help`命令查看可用命令列表;在启动服务器时,也可以通过`python manage.py runserver 1.2.3.4:56789`来指定绑定的IP地址和端口。 + > 說明3:可以通過`python manage.py help`命令檢視可用命令列表;在啟動伺服器時,也可以通過`python manage.py runserver 1.2.3.4:56789`來指定繫結的IP地址和埠。 - > 说明4:可以通过Ctrl+C来终止服务器的运行。 + > 說明4:可以通過Ctrl+C來終止伺服器的執行。 -9. 接下来我们进入项目目录oa并修改配置文件settings.py,Django是一个支持国际化和本地化的框架,因此刚才我们看到的默认首页也是支持国际化的,我们将默认语言修改为中文,时区设置为东八区。 +9. 接下來我們進入專案目錄oa並修改配置檔案settings.py,Django是一個支援國際化和本地化的框架,因此剛才我們看到的預設首頁也是支援國際化的,我們將預設語言修改為中文,時區設定為東八區。 ```Shell (venv)$ cd oa @@ -162,17 +162,17 @@ Django诞生于2003年,它是一个在真正的应用中成长起来的项目 ``` ```Python - # 此处省略上面的内容 + # 此處省略上面的內容 - # 设置语言代码 + # 設定語言程式碼 LANGUAGE_CODE = 'zh-hans' - # 设置时区 + # 設定時區 TIME_ZONE = 'Asia/Chongqing' - # 此处省略下面的内容 + # 此處省略下面的內容 ``` -10. 回到manage.py所在的目录,刷新刚才的页面。 +10. 回到manage.py所在的目錄,重新整理剛才的頁面。 ```Shell (venv)$ cd .. @@ -181,25 +181,25 @@ Django诞生于2003年,它是一个在真正的应用中成长起来的项目 ![](./res/django-index-2.png) -#### 动态页面 +#### 動態頁面 -1. 创建名为hrs(人力资源系统)的应用(注:一个项目可以包含多个应用)。 +1. 建立名為hrs(人力資源系統)的應用(注:一個專案可以包含多個應用)。 ```Shell (venv)$ python manage.py startapp hrs ``` - 执行上面的命令会在当前路径下创建hrs目录,其目录结构如下所示: + 執行上面的命令會在當前路徑下建立hrs目錄,其目錄結構如下所示: - - `__init__.py`:一个空文件,告诉 Python 这个目录应该被认为是一个 Python 包。 - - `admin.py`:可以用来注册模型,让Django自动创建管理界面。 - - `apps.py`:当前应用的配置。 - - `migrations`:存放与模型有关的数据库迁移信息。 - - `__init__.py`:一个空文件,告诉 Python 这个目录应该被认为是一个 Python 包。 - - `models.py`:存放应用的数据模型,即实体类及其之间的关系(MVC/MVT中的M)。 - - `tests.py`:包含测试应用各项功能的测试类和测试函数。 - - `views.py`:处理请求并返回响应的函数(MVC中的C,MVT中的V)。 -2. 进入应用目录修改视图文件views.py。 + - `__init__.py`:一個空檔案,告訴 Python 這個目錄應該被認為是一個 Python 包。 + - `admin.py`:可以用來註冊模型,讓Django自動建立管理介面。 + - `apps.py`:當前應用的配置。 + - `migrations`:存放與模型有關的資料庫遷移資訊。 + - `__init__.py`:一個空檔案,告訴 Python 這個目錄應該被認為是一個 Python 包。 + - `models.py`:存放應用的資料模型,即實體類及其之間的關係(MVC/MVT中的M)。 + - `tests.py`:包含測試應用各項功能的測試類和測試函式。 + - `views.py`:處理請求並返回響應的函式(MVC中的C,MVT中的V)。 +2. 進入應用目錄修改檢視檔案views.py。 ```Shell (venv)$ cd hrs @@ -215,7 +215,7 @@ Django诞生于2003年,它是一个在真正的应用中成长起来的项目 ``` -3. 在应用目录创建一个urls.py文件并映射URL。 +3. 在應用目錄建立一個urls.py檔案並對映URL。 ```Shell (venv)$ touch urls.py @@ -231,9 +231,9 @@ Django诞生于2003年,它是一个在真正的应用中成长起来的项目 path('', views.index, name='index'), ] ``` - > 说明:上面使用的path函数是Django 2.x中新添加的函数,除此之外还有re_path是支持正则表达式的URL映射函数;Django 1.x中是用url函数来设定URL映射。 + > 說明:上面使用的path函式是Django 2.x中新新增的函式,除此之外還有re_path是支援正則表示式的URL對映函式;Django 1.x中是用url函式來設定URL對映。 -4. 切换到项目目录,修改该目录下的urls.py文件,对应用中设定的URL进行合并。 +4. 切換到專案目錄,修改該目錄下的urls.py檔案,對應用中設定的URL進行合併。 ```Shell (venv) $ cd .. @@ -251,18 +251,18 @@ Django诞生于2003年,它是一个在真正的应用中成长起来的项目 ] ``` -5. 启动项目并访问应用。 +5. 啟動專案並訪問應用。 ```Shell (venv)$ cd .. (venv)$ python manage.py runserver ``` - 在浏览器中访问。 + 在瀏覽器中訪問。 - > 说明:如果想实现远程访问,需要先确认防火墙是否已经打开了8000端口,而且需要在配置文件settings.py中修改ALLOWED_HOSTS的设置,添加一个'*'表示允许所有的客户端访问Web应用。 + > 說明:如果想實現遠端訪問,需要先確認防火牆是否已經打開了8000埠,而且需要在配置檔案settings.py中修改ALLOWED_HOSTS的設定,新增一個'*'表示允許所有的客戶端訪問Web應用。 -6. 修改views.py生成动态内容。 +6. 修改views.py生成動態內容。 ```Shell (venv)$ cd hrs @@ -275,7 +275,7 @@ Django诞生于2003年,它是一个在真正的应用中成长起来的项目 from io import StringIO from random import randrange - fruits = ['苹果', '草莓', '榴莲', '香蕉', '葡萄', '山竹', '蓝莓', '西瓜'] + fruits = ['蘋果', '草莓', '榴蓮', '香蕉', '葡萄', '山竹', '藍莓', '西瓜'] def index(request): @@ -283,7 +283,7 @@ Django诞生于2003年,它是一个在真正的应用中成长起来的项目 output.write('\n') output.write('\n') output.write('\t\n') - output.write('\t首页') + output.write('\t首頁') output.write('\n') output.write('\n') output.write('\t

Hello, world!

\n') @@ -299,13 +299,13 @@ Django诞生于2003年,它是一个在真正的应用中成长起来的项目 ``` -#### 使用视图模板 +#### 使用檢視模板 -上面通过拼接HTML代码的方式生成动态视图的做法在实际开发中是无能接受的,这一点我相信是不言而喻的。为了解决这个问题,我们可以提前准备一个模板页,所谓模板页就是一个带占位符的HTML页面,当我们将程序中获得的数据替换掉页面中的占位符时,一个动态页面就产生了。 +上面通過拼接HTML程式碼的方式生成動態檢視的做法在實際開發中是無能接受的,這一點我相信是不言而喻的。為了解決這個問題,我們可以提前準備一個模板頁,所謂模板頁就是一個帶佔位符的HTML頁面,當我們將程式中獲得的資料替換掉頁面中的佔位符時,一個動態頁面就產生了。 -我们可以用Django框架中template模块的Template类创建模板对象,通过模板对象的render方法实现对模板的渲染。所谓的渲染就是用数据替换掉模板页中的占位符,Django框架通过shortcuts模块的快捷函数render简化了渲染模板的操作,具体的用法如下所示。 +我們可以用Django框架中template模組的Template類建立模板物件,通過模板物件的render方法實現對模板的渲染。所謂的渲染就是用資料替換掉模板頁中的佔位符,Django框架通過shortcuts模組的快捷函式render簡化了渲染模板的操作,具體的用法如下所示。 -1. 先回到manage.py文件所在的目录创建一个templates文件夹。 +1. 先回到manage.py檔案所在的目錄建立一個templates資料夾。 ```Shell (venv)$ cd .. @@ -313,7 +313,7 @@ Django诞生于2003年,它是一个在真正的应用中成长起来的项目 (venv)$ cd templates ``` -2. 创建模板页index.html。 +2. 建立模板頁index.html。 ```Shell (venv)$ touch index.html @@ -324,12 +324,12 @@ Django诞生于2003年,它是一个在真正的应用中成长起来的项目 - 首页 + 首頁

{{ greeting }}


-

今天推荐{{ num }}种水果是:

+

今天推薦{{ num }}種水果是:

    {% for fruit in fruits %}
  • {{ fruit }}
  • @@ -338,9 +338,9 @@ Django诞生于2003年,它是一个在真正的应用中成长起来的项目 ``` - 注意在模板页中我们使用了`{{ greeting }}`这样的模板占位符语法,也使用了`{% for %}`这样的模板指令,如果对此不熟悉并不要紧,我们会在后续的内容中进一步的讲解模板的用法。 + 注意在模板頁中我們使用了`{{ greeting }}`這樣的模板佔位符語法,也使用了`{% for %}`這樣的模板指令,如果對此不熟悉並不要緊,我們會在後續的內容中進一步的講解模板的用法。 -3. 回到应用目录,修改views.py文件。 +3. 回到應用目錄,修改views.py檔案。 ```Shell (venv)$ cd .. @@ -354,7 +354,7 @@ Django诞生于2003年,它是一个在真正的应用中成长起来的项目 def index(request): - fruits = ['苹果', '香蕉', '草莓', '葡萄', '山竹', '杨梅', '西瓜', '榴莲'] + fruits = ['蘋果', '香蕉', '草莓', '葡萄', '山竹', '楊梅', '西瓜', '榴蓮'] start, end = 0, randrange(len(fruits)) ctx = { 'greeting': 'Hello, Django!', @@ -365,9 +365,9 @@ Django诞生于2003年,它是一个在真正的应用中成长起来的项目 ``` - 到此为止,我们还没有办法让views.py中的render函数找到模板文件index.html,为此我们需要修改settings.py文件,配置模板文件所在的路径。 + 到此為止,我們還沒有辦法讓views.py中的render函式找到模板檔案index.html,為此我們需要修改settings.py檔案,配置模板檔案所在的路徑。 -4. 切换到项目目录修改settings.py文件。 +4. 切換到專案目錄修改settings.py檔案。 ```Shell (venv)$ cd .. @@ -376,7 +376,7 @@ Django诞生于2003年,它是一个在真正的应用中成长起来的项目 ``` ```Python - # 此处省略上面的内容 + # 此處省略上面的內容 TEMPLATES = [ { @@ -394,10 +394,10 @@ Django诞生于2003年,它是一个在真正的应用中成长起来的项目 }, ] - # 此处省略下面的内容 + # 此處省略下面的內容 ``` -5. 重新运行项目并查看结果。 +5. 重新執行專案並檢視結果。 ```Shell (venv)$ cd .. @@ -406,8 +406,8 @@ Django诞生于2003年,它是一个在真正的应用中成长起来的项目 ![](./res/runserver.png) -### 总结 +### 總結 -至此,我们已经利用Django框架完成了一个非常小的Web应用,虽然它并没有任何的实际价值,但是我们需要通过这个项目了解到Django框架的使用方式。当然,如果使用PyCharm的Professional版本,也可以通过PyCharm的创建项目的选项直接创建Django项目,使用PyCharm的好处在于编写代码时可以获得代码提示、错误修复、自动导入等功能,从而提升开发效率,但是代价是需要支付对应的费用才能使用专业版的PyCharm,社区版的PyCharm中并未包含对Web框架的支持。 +至此,我們已經利用Django框架完成了一個非常小的Web應用,雖然它並沒有任何的實際價值,但是我們需要通過這個專案瞭解到Django框架的使用方式。當然,如果使用PyCharm的Professional版本,也可以通過PyCharm的建立專案的選項直接建立Django專案,使用PyCharm的好處在於編寫程式碼時可以獲得程式碼提示、錯誤修復、自動匯入等功能,從而提升開發效率,但是代價是需要支付對應的費用才能使用專業版的PyCharm,社群版的PyCharm中並未包含對Web框架的支援。 -此外,学习Django最好的资料肯定是它的[官方文档](https://docs.djangoproject.com/zh-hans/2.0/),除此之外图灵社区最近出版的[《Django基础教程》](http://www.ituring.com.cn/book/2630)也是非常适合初学者的读物。 \ No newline at end of file +此外,學習Django最好的資料肯定是它的[官方文件](https://docs.djangoproject.com/zh-hans/2.0/),除此之外圖靈社群最近出版的[《Django基礎教程》](http://www.ituring.com.cn/book/2630)也是非常適合初學者的讀物。 \ No newline at end of file diff --git "a/Day41-55/Django2\345\256\236\346\210\23002.md" "b/Day41-55/Django2\345\256\236\346\210\23002.md" index b0546d8e5..4c9011bd7 100644 --- "a/Day41-55/Django2\345\256\236\346\210\23002.md" +++ "b/Day41-55/Django2\345\256\236\346\210\23002.md" @@ -1,10 +1,10 @@ -## Django 2.x实战(02) - 深入模型 +## Django 2.x實戰(02) - 深入模型 -在上一个章节中,我们提到了Django是一个基于MVC架构的Web框架,MVC架构要追求的是模型和视图的解耦合,而其中的模型说得更直白一些就是数据,所以通常也被称作数据模型。在实际的项目中,数据模型通常通过数据库实现持久化操作,而关系型数据库在很长一段时间都是持久化的首选方案,在我们的OA项目中,我们选择使用MySQL来实现数据持久化。 +在上一個章節中,我們提到了Django是一個基於MVC架構的Web框架,MVC架構要追求的是模型和檢視的解耦合,而其中的模型說得更直白一些就是資料,所以通常也被稱作資料模型。在實際的專案中,資料模型通常通過資料庫實現持久化操作,而關係型資料庫在很長一段時間都是持久化的首選方案,在我們的OA專案中,我們選擇使用MySQL來實現資料持久化。 -### 配置关系型数据库MySQL +### 配置關係型資料庫MySQL -1. 进入oa文件夹,修改项目的settings.py文件,首先将我们之前创建的应用hrs添加已安装的项目中,然后配置MySQL作为持久化方案。 +1. 進入oa資料夾,修改專案的settings.py檔案,首先將我們之前建立的應用hrs新增已安裝的專案中,然後配置MySQL作為持久化方案。 ```Shell (venv)$ cd oa @@ -12,7 +12,7 @@ ``` ```Python - # 此处省略上面的代码 + # 此處省略上面的程式碼 INSTALLED_APPS = [ 'django.contrib.admin', @@ -35,27 +35,27 @@ } } - # 此处省略下面的代码 + # 此處省略下面的程式碼 ``` - 在配置ENGINE属性时,常用的可选值包括: + 在配置ENGINE屬性時,常用的可選值包括: - - `'django.db.backends.sqlite3'`:SQLite嵌入式数据库 - - `'django.db.backends.postgresql'`:BSD许可证下发行的开源关系型数据库产品 - - `'django.db.backends.mysql'`:转手多次目前属于甲骨文公司的经济高效的数据库产品 - - `'django.db.backends.oracle'`:甲骨文公司的旗舰关系型数据库产品 + - `'django.db.backends.sqlite3'`:SQLite嵌入式資料庫 + - `'django.db.backends.postgresql'`:BSD許可證下發行的開源關係型資料庫產品 + - `'django.db.backends.mysql'`:轉手多次目前屬於甲骨文公司的經濟高效的資料庫產品 + - `'django.db.backends.oracle'`:甲骨文公司的旗艦關係型資料庫產品 - 其他的配置可以参考官方文档中[数据库配置](https://docs.djangoproject.com/zh-hans/2.0/ref/databases/#third-party-notes)的部分。 + 其他的配置可以參考官方文件中[資料庫配置](https://docs.djangoproject.com/zh-hans/2.0/ref/databases/#third-party-notes)的部分。 - NAME属性代表数据库的名称,如果使用SQLite它对应着一个文件,在这种情况下NAME的属性值应该是一个绝对路径。如果使用其他关系型数据库,还要配置对应的HOST(主机)、PORT(端口)、USER(用户名)、PASSWORD(口令)等属性。 + NAME屬性代表資料庫的名稱,如果使用SQLite它對應著一個檔案,在這種情況下NAME的屬性值應該是一個絕對路徑。如果使用其他關係型資料庫,還要配置對應的HOST(主機)、PORT(埠)、USER(使用者名稱)、PASSWORD(口令)等屬性。 -2. 安装MySQL客户端工具,Python 3中使用PyMySQL,Python 2中用MySQLdb。 +2. 安裝MySQL客戶端工具,Python 3中使用PyMySQL,Python 2中用MySQLdb。 ```Shell (venv)$ pip install pymysql ``` - 如果使用Python 3需要修改**项目**的`__init__.py`文件并加入如下所示的代码,这段代码的作用是将PyMySQL视为MySQLdb来使用,从而避免Django找不到连接MySQL的客户端工具而询问你:“Did you install mysqlclient? ”(你安装了mysqlclient吗?)。 + 如果使用Python 3需要修改**專案**的`__init__.py`檔案並加入如下所示的程式碼,這段程式碼的作用是將PyMySQL視為MySQLdb來使用,從而避免Django找不到連線MySQL的客戶端工具而詢問你:“Did you install mysqlclient? ”(你安裝了mysqlclient嗎?)。 ```Python import pymysql @@ -63,7 +63,7 @@ pymysql.install_as_MySQLdb() ``` -3. 运行manage.py并指定migrate参数实现数据库迁移,为应用程序创建对应的数据表,当然在此之前需要**先启动MySQL数据库服务器并创建名为oa的数据库**,在MySQL中创建数据库的语句如下所示。 +3. 執行manage.py並指定migrate引數實現資料庫遷移,為應用程式建立對應的資料表,當然在此之前需要**先啟動MySQL資料庫伺服器並建立名為oa的資料庫**,在MySQL中建立資料庫的語句如下所示。 ```SQL drop database if exists oa; @@ -92,7 +92,7 @@ Applying sessions.0001_initial... OK ``` -4. 可以看到,Django帮助我们创建了10张表,这些都是使用Django框架需要的东西,稍后我们就会用到这些表。除此之外,我们还应该为我们自己的应用创建数据模型。如果要在hrs应用中实现对部门和员工的管理,我们可以创建如下所示的数据模型。 +4. 可以看到,Django幫助我們建立了10張表,這些都是使用Django框架需要的東西,稍後我們就會用到這些表。除此之外,我們還應該為我們自己的應用建立資料模型。如果要在hrs應用中實現對部門和員工的管理,我們可以建立如下所示的資料模型。 ```Shell (venv)$ cd hrs @@ -104,36 +104,36 @@ class Dept(models.Model): - """部门类""" + """部門類""" - no = models.IntegerField(primary_key=True, db_column='dno', verbose_name='部门编号') - name = models.CharField(max_length=20, db_column='dname', verbose_name='部门名称') - location = models.CharField(max_length=10, db_column='dloc', verbose_name='部门所在地') + no = models.IntegerField(primary_key=True, db_column='dno', verbose_name='部門編號') + name = models.CharField(max_length=20, db_column='dname', verbose_name='部門名稱') + location = models.CharField(max_length=10, db_column='dloc', verbose_name='部門所在地') class Meta: db_table = 'tb_dept' class Emp(models.Model): - """员工类""" + """員工類""" - no = models.IntegerField(primary_key=True, db_column='eno', verbose_name='员工编号') - name = models.CharField(max_length=20, db_column='ename', verbose_name='员工姓名') - job = models.CharField(max_length=10, verbose_name='职位') - # 自参照完整性多对一外键关联 - mgr = models.ForeignKey('self', on_delete=models.SET_NULL, null=True, blank=True, verbose_name='主管编号') + no = models.IntegerField(primary_key=True, db_column='eno', verbose_name='員工編號') + name = models.CharField(max_length=20, db_column='ename', verbose_name='員工姓名') + job = models.CharField(max_length=10, verbose_name='職位') + # 自參照完整性多對一外來鍵關聯 + mgr = models.ForeignKey('self', on_delete=models.SET_NULL, null=True, blank=True, verbose_name='主管編號') sal = models.DecimalField(max_digits=7, decimal_places=2, verbose_name='月薪') - comm = models.DecimalField(max_digits=7, decimal_places=2, null=True, blank=True, verbose_name='补贴') - dept = models.ForeignKey(Dept, db_column='dno', on_delete=models.PROTECT, verbose_name='所在部门') + comm = models.DecimalField(max_digits=7, decimal_places=2, null=True, blank=True, verbose_name='補貼') + dept = models.ForeignKey(Dept, db_column='dno', on_delete=models.PROTECT, verbose_name='所在部門') class Meta: db_table = 'tb_emp' ``` - > 说明:上面定义模型时使用了字段类及其属性,其中IntegerField对应数据库中的integer类型,CharField对应数据库的varchar类型,DecimalField对应数据库的decimal类型,ForeignKey用来建立多对一外键关联。字段属性primary_key用于设置主键,max_length用来设置字段的最大长度,db_column用来设置数据库中与字段对应的列,verbose_name则设置了Django后台管理系统中该字段显示的名称。如果对这些东西感到很困惑也不要紧,文末提供了字段类、字段属性、元数据选项等设置的相关说明,不清楚的读者可以稍后查看对应的参考指南。 + > 說明:上面定義模型時使用了欄位類及其屬性,其中IntegerField對應資料庫中的integer型別,CharField對應資料庫的varchar型別,DecimalField對應資料庫的decimal型別,ForeignKey用來建立多對一外來鍵關聯。欄位屬性primary_key用於設定主鍵,max_length用來設定欄位的最大長度,db_column用來設定資料庫中與欄位對應的列,verbose_name則設定了Django後臺管理系統中該欄位顯示的名稱。如果對這些東西感到很困惑也不要緊,文末提供了欄位類、欄位屬性、元資料選項等設定的相關說明,不清楚的讀者可以稍後檢視對應的參考指南。 -5. 通过模型创建数据表。 +5. 通過模型建立資料表。 ```Shell (venv)$ cd .. @@ -149,13 +149,13 @@ Applying hrs.0001_initial... OK ``` - 执行完数据迁移操作之后,可以在通过图形化的MySQL客户端工具查看到E-R图(实体关系图)。 + 執行完資料遷移操作之後,可以在通過圖形化的MySQL客戶端工具檢視到E-R圖(實體關係圖)。 ![](./res/er-graph.png) -### 在后台管理模型 +### 在後臺管理模型 -1. 创建超级管理员账号。 +1. 建立超級管理員賬號。 ```Shell (venv)$ python manage.py createsuperuser @@ -166,23 +166,23 @@ Superuser created successfully. ``` -2. 启动Web服务器,登录后台管理系统。 +2. 啟動Web伺服器,登入後臺管理系統。 ```Shell (venv)$ python manage.py runserver ``` - 访问,会来到如下图所示的登录界面。 + 訪問,會來到如下圖所示的登入介面。 ![](./res/admin-login.png) - 登录后进入管理员操作平台。 + 登入後進入管理員操作平臺。 ![](./res/admin-welcome.png) - 至此我们还没有看到之前创建的模型类,需要在应用的admin.py文件中模型进行注册。 + 至此我們還沒有看到之前建立的模型類,需要在應用的admin.py檔案中模型進行註冊。 -3. 注册模型类。 +3. 註冊模型類。 ```Shell (venv)$ cd hrs @@ -199,29 +199,29 @@ ``` - 注册模型类后,就可以在后台管理系统中看到它们。 + 註冊模型類後,就可以在後臺管理系統中看到它們。 ![](./res/admin-model.png) -4. 对模型进行CRUD操作。 +4. 對模型進行CRUD操作。 - 可以在管理员平台对模型进行C(新增)R(查看)U(更新)D(删除)操作,如下图所示。 + 可以在管理員平臺對模型進行C(新增)R(檢視)U(更新)D(刪除)操作,如下圖所示。 - 添加新的部门。 + 新增新的部門。 ![](./res/admin-model-create.png) - 查看所有部门。 + 檢視所有部門。 ![](./res/admin-model-read.png) - 更新和删除部门。 + 更新和刪除部門。 ![](./res/admin-model-delete-and-update.png) -5. 注册模型管理类。 +5. 註冊模型管理類。 - 再次修改admin.py文件,通过注册模型管理类,可以在后台管理系统中更好的管理模型。 + 再次修改admin.py檔案,通過註冊模型管理類,可以在後臺管理系統中更好的管理模型。 ```Python from django.contrib import admin @@ -250,48 +250,48 @@ ![](./res/admin-model-emps.png) - 为了更好的查看模型数据,可以为Dept和Emp两个模型类添加`__str__`魔法方法。 + 為了更好的檢視模型資料,可以為Dept和Emp兩個模型類新增`__str__`魔法方法。 ```Python from django.db import models class Dept(models.Model): - """部门类""" + """部門類""" - # 此处省略上面的代码 + # 此處省略上面的程式碼 def __str__(self): return self.name - # 此处省略下面的代码 + # 此處省略下面的程式碼 class Emp(models.Model): - """员工类""" + """員工類""" - # 此处省略上面的代码 + # 此處省略上面的程式碼 mgr = models.ForeignKey('self', on_delete=models.SET_NULL, null=True, blank=True, verbose_name='直接主管') - # 此处省略下面的代码 + # 此處省略下面的程式碼 - # 此处省略上面的代码 + # 此處省略上面的程式碼 def __str__(self): return self.name - # 此处省略下面的代码 + # 此處省略下面的程式碼 ``` - 修改代码后刷新查看Emp模型的页面,效果如下图所示。 + 修改程式碼後重新整理檢視Emp模型的頁面,效果如下圖所示。 ![](./res/admin-model-emps-modified.png) ### 使用ORM完成模型的CRUD操作 -在了解了Django提供的模型管理平台之后,我们来看看如何从代码层面完成对模型的CRUD(Create / Read / Update / Delete)操作。我们可以通过manage.py开启Shell交互式环境,然后使用Django内置的ORM框架对模型进行CRUD操作。 +在瞭解了Django提供的模型管理平臺之後,我們來看看如何從程式碼層面完成對模型的CRUD(Create / Read / Update / Delete)操作。我們可以通過manage.py開啟Shell互動式環境,然後使用Django內建的ORM框架對模型進行CRUD操作。 ```Shell (venv)$ cd .. @@ -308,7 +308,7 @@ Type "help", "copyright", "credits" or "license" for more information. ```Shell >>> >>> from hrs.models import Dept, Emp ->>> dept = Dept(40, '研发2部', '深圳') +>>> dept = Dept(40, '研發2部', '深圳') >>> dept.save() ``` @@ -316,208 +316,208 @@ Type "help", "copyright", "credits" or "license" for more information. ```Shell >>> ->>> dept.name = '研发3部' +>>> dept.name = '研發3部' >>> dept.save() ``` -#### 查询 +#### 查詢 -查询所有对象。 +查詢所有物件。 ```Shell >>> >>> Dept.objects.all() -, , , ]> +, , , ]> ``` -过滤数据。 +過濾資料。 ```Shell >>> ->>> Dept.objects.filter(name='研发3部') # 查询部门名称为“研发3部”的部门 -]> +>>> Dept.objects.filter(name='研發3部') # 查詢部門名稱為“研發3部”的部門 +]> >>> ->>> Dept.objects.filter(name__contains='研发') # 查询部门名称包含“研发”的部门(模糊查询) -, ]> +>>> Dept.objects.filter(name__contains='研發') # 查詢部門名稱包含“研發”的部門(模糊查詢) +, ]> >>> ->>> Dept.objects.filter(no__gt=10).filter(no__lt=40) # 查询部门编号大于10小于40的部门 -, ]> +>>> Dept.objects.filter(no__gt=10).filter(no__lt=40) # 查詢部門編號大於10小於40的部門 +, ]> ``` -查询单个对象。 +查詢單個物件。 ```Shell >>> >>> Dept.objects.get(pk=10) - + >>> Dept.objects.get(no=20) - + >>> Dept.objects.get(no__exact=30) - + ``` -排序数据。 +排序資料。 ```Shell >>> ->>> Dept.objects.order_by('no') # 查询所有部门按部门编号升序排列 -, , , ]> ->>> Dept.objects.order_by('-no') # 查询所有部门按部门编号降序排列 -, , , ]> +>>> Dept.objects.order_by('no') # 查詢所有部門按部門編號升序排列 +, , , ]> +>>> Dept.objects.order_by('-no') # 查詢所有部門按部門編號降序排列 +, , , ]> ``` -切片数据。 +切片資料。 ```Shell >>> ->>> Dept.objects.order_by('no')[0:2] # 按部门编号排序查询1~2部门 -, ]> ->>> Dept.objects.order_by('no')[2:4] # 按部门编号排序查询3~4部门 -, ]> +>>> Dept.objects.order_by('no')[0:2] # 按部門編號排序查詢1~2部門 +, ]> +>>> Dept.objects.order_by('no')[2:4] # 按部門編號排序查詢3~4部門 +, ]> ``` -高级查询。 +高階查詢。 ```Shell >>> ->>> Emp.objects.filter(dept__no=10) # 根据部门编号查询该部门的员工 -, , ]> ->>> Emp.objects.filter(dept__name__contains='销售') # 查询名字包含“销售”的部门的员工 -]> ->>> Dept.objects.get(pk=10).emp_set.all() # 通过部门反查部门所有的员工 -, , ]> +>>> Emp.objects.filter(dept__no=10) # 根據部門編號查詢該部門的員工 +, , ]> +>>> Emp.objects.filter(dept__name__contains='銷售') # 查詢名字包含“銷售”的部門的員工 +]> +>>> Dept.objects.get(pk=10).emp_set.all() # 通過部門反查部門所有的員工 +, , ]> ``` -> 说明:由于员工与部门之间存在外键关联,所以也能通过部门反向查询该部门的员工(从一对多关系中“一”的一方查询“多”的一方),默认情况下反查属性名是`类名小写_set`(例子中的`emp_set`),当然也可以在创建模型时通过`related_name`指定反查属性的名字。 +> 說明:由於員工與部門之間存在外來鍵關聯,所以也能通過部門反向查詢該部門的員工(從一對多關係中“一”的一方查詢“多”的一方),預設情況下反查屬性名是`類名小寫_set`(例子中的`emp_set`),當然也可以在建立模型時通過`related_name`指定反查屬性的名字。 -#### 删除 +#### 刪除 ```Shell ``` -最后,我们通过上面掌握的知识来实现部门展示以及根据部门获取部门对应员工信息的功能,效果如下图所示,对应的代码可以访问<>。 +最後,我們通過上面掌握的知識來實現部門展示以及根據部門獲取部門對應員工資訊的功能,效果如下圖所示,對應的程式碼可以訪問<>。 -### Django模型最佳实践 +### Django模型最佳實踐 -1. 正确的模型命名和关系字段命名。 -2. 设置适当的related_name属性。 +1. 正確的模型命名和關係欄位命名。 +2. 設定適當的related_name屬性。 3. 用OneToOneField代替ForeignKeyField(unique=True)。 -4. 通过迁移操作来添加模型。 -5. 用NoSQL来应对需要降低范式级别的场景。 -6. 如果布尔类型可以为空要使用NullBooleanField。 -7. 在模型中放置业务逻辑。 +4. 通過遷移操作來新增模型。 +5. 用NoSQL來應對需要降低正規化級別的場景。 +6. 如果布林型別可以為空要使用NullBooleanField。 +7. 在模型中放置業務邏輯。 8. 用ModelName.DoesNotExists取代ObjectDoesNotExists。 -9. 在数据库中不要出现无效数据。 -10. 不要对QuerySet调用len函数。 -11. 将QuerySet的exists()方法的返回值用于if条件。 -12. 用DecimalField来存储货币相关数据而不是FloatField。 -13. 定义\_\_str\_\_方法。 -14. 不要将数据文件放在同一个目录中。 +9. 在資料庫中不要出現無效資料。 +10. 不要對QuerySet呼叫len函式。 +11. 將QuerySet的exists()方法的返回值用於if條件。 +12. 用DecimalField來儲存貨幣相關資料而不是FloatField。 +13. 定義\_\_str\_\_方法。 +14. 不要將資料檔案放在同一個目錄中。 -> 说明:以上内容来自于STEELKIWI网站的[*Best Practice working with Django models in Python*](https://steelkiwi.com/blog/best-practices-working-django-models-python/),有兴趣的小伙伴可以阅读原文。 +> 說明:以上內容來自於STEELKIWI網站的[*Best Practice working with Django models in Python*](https://steelkiwi.com/blog/best-practices-working-django-models-python/),有興趣的小夥伴可以閱讀原文。 -### 模型定义参考 +### 模型定義參考 -#### 字段 +#### 欄位 -对字段名称的限制 +對欄位名稱的限制 -- 字段名不能是Python的保留字,否则会导致语法错误 -- 字段名不能有多个连续下划线,否则影响ORM查询操作 +- 欄位名不能是Python的保留字,否則會導致語法錯誤 +- 欄位名不能有多個連續下劃線,否則影響ORM查詢操作 -Django模型字段类 +Django模型欄位類 -| 字段类 | 说明 | +| 欄位類 | 說明 | | --------------------- | ------------------------------------------------------------ | -| AutoField |自增ID字段 | -| BigIntegerField |64位有符号整数 | -| BinaryField | 存储二进制数据的字段,对应Python的bytes类型 | -| BooleanField | 存储True或False | -| CharField | 长度较小的字符串 | -| DateField | 存储日期,有auto_now和auto_now_add属性 | -| DateTimeField | 存储日期和日期,两个附加属性同上 | -| DecimalField |存储固定精度小数,有max_digits(有效位数)和decimal_places(小数点后面)两个必要的参数 | -| DurationField |存储时间跨度 | -| EmailField | 与CharField相同,可以用EmailValidator验证 | -| FileField | 文件上传字段 | -| FloatField | 存储浮点数 | -| ImageField | 其他同FileFiled,要验证上传的是不是有效图像 | -| IntegerField | 存储32位有符号整数。 | -| GenericIPAddressField | 存储IPv4或IPv6地址 | -| NullBooleanField | 存储True、False或null值 | -| PositiveIntegerField | 存储无符号整数(只能存储正数) | -| SlugField | 存储slug(简短标注) | -| SmallIntegerField | 存储16位有符号整数 | -| TextField | 存储数据量较大的文本 | -| TimeField | 存储时间 | -| URLField | 存储URL的CharField | -| UUIDField | 存储全局唯一标识符 | - -#### 字段属性 - -通用字段属性 - -| 选项 | 说明 | +| AutoField |自增ID欄位 | +| BigIntegerField |64位有符號整數 | +| BinaryField | 儲存二進位制資料的欄位,對應Python的bytes型別 | +| BooleanField | 儲存True或False | +| CharField | 長度較小的字串 | +| DateField | 儲存日期,有auto_now和auto_now_add屬性 | +| DateTimeField | 儲存日期和日期,兩個附加屬性同上 | +| DecimalField |儲存固定精度小數,有max_digits(有效位數)和decimal_places(小數點後面)兩個必要的引數 | +| DurationField |儲存時間跨度 | +| EmailField | 與CharField相同,可以用EmailValidator驗證 | +| FileField | 檔案上傳欄位 | +| FloatField | 儲存浮點數 | +| ImageField | 其他同FileFiled,要驗證上傳的是不是有效影象 | +| IntegerField | 儲存32位有符號整數。 | +| GenericIPAddressField | 儲存IPv4或IPv6地址 | +| NullBooleanField | 儲存True、False或null值 | +| PositiveIntegerField | 儲存無符號整數(只能儲存正數) | +| SlugField | 儲存slug(簡短標註) | +| SmallIntegerField | 儲存16位有符號整數 | +| TextField | 儲存資料量較大的文字 | +| TimeField | 儲存時間 | +| URLField | 儲存URL的CharField | +| UUIDField | 儲存全域性唯一識別符號 | + +#### 欄位屬性 + +通用欄位屬性 + +| 選項 | 說明 | | -------------- | ------------------------------------------------------------ | -| null | 数据库中对应的字段是否允许为NULL,默认为False | -| blank | 后台模型管理验证数据时,是否允许为NULL,默认为False | -| choices | 设定字段的选项,各元组中的第一个值是设置在模型上的值,第二值是人类可读的值 | -| db_column | 字段对应到数据库表中的列名,未指定时直接使用字段的名称 | -| db_index | 设置为True时将在该字段创建索引 | -| db_tablespace | 为有索引的字段设置使用的表空间,默认为DEFAULT_INDEX_TABLESPACE | -| default | 字段的默认值 | -| editable | 字段在后台模型管理或ModelForm中是否显示,默认为True | -| error_messages | 设定字段抛出异常时的默认消息的字典,其中的键包括null、blank、invalid、invalid_choice、unique和unique_for_date | -| help_text | 表单小组件旁边显示的额外的帮助文本。 | -| primary_key | 将字段指定为模型的主键,未指定时会自动添加AutoField用于主键,只读。 | -| unique | 设置为True时,表中字段的值必须是唯一的 | -| verbose_name | 字段在后台模型管理显示的名称,未指定时使用字段的名称 | - -ForeignKey属性 - -1. limit_choices_to:值是一个Q对象或返回一个Q对象,用于限制后台显示哪些对象。 -2. related_name:用于获取关联对象的关联管理器对象(反向查询),如果不允许反向,该属性应该被设置为`'+'`,或者以`'+'`结尾。 -3. to_field:指定关联的字段,默认关联对象的主键字段。 -4. db_constraint:是否为外键创建约束,默认值为True。 -5. on_delete:外键关联的对象被删除时对应的动作,可取的值包括django.db.models中定义的: - - CASCADE:级联删除。 - - PROTECT:抛出ProtectedError异常,阻止删除引用的对象。 - - SET_NULL:把外键设置为null,当null属性被设置为True时才能这么做。 - - SET_DEFAULT:把外键设置为默认值,提供了默认值才能这么做。 - -ManyToManyField属性 - -1. symmetrical:是否建立对称的多对多关系。 -2. through:指定维持多对多关系的中间表的Django模型。 -3. throughfields:定义了中间模型时可以指定建立多对多关系的字段。 -4. db_table:指定维持多对多关系的中间表的表名。 - -#### 模型元数据选项 - -| 选项 | 说明 | +| null | 資料庫中對應的欄位是否允許為NULL,預設為False | +| blank | 後臺模型管理驗證資料時,是否允許為NULL,預設為False | +| choices | 設定欄位的選項,各元組中的第一個值是設定在模型上的值,第二值是人類可讀的值 | +| db_column | 欄位對應到資料庫表中的列名,未指定時直接使用欄位的名稱 | +| db_index | 設定為True時將在該欄位建立索引 | +| db_tablespace | 為有索引的欄位設定使用的表空間,預設為DEFAULT_INDEX_TABLESPACE | +| default | 欄位的預設值 | +| editable | 欄位在後臺模型管理或ModelForm中是否顯示,預設為True | +| error_messages | 設定欄位丟擲異常時的預設訊息的字典,其中的鍵包括null、blank、invalid、invalid_choice、unique和unique_for_date | +| help_text | 表單小元件旁邊顯示的額外的幫助文字。 | +| primary_key | 將欄位指定為模型的主鍵,未指定時會自動新增AutoField用於主鍵,只讀。 | +| unique | 設定為True時,表中欄位的值必須是唯一的 | +| verbose_name | 欄位在後臺模型管理顯示的名稱,未指定時使用欄位的名稱 | + +ForeignKey屬性 + +1. limit_choices_to:值是一個Q物件或返回一個Q物件,用於限制後臺顯示哪些物件。 +2. related_name:用於獲取關聯物件的關聯管理器物件(反向查詢),如果不允許反向,該屬性應該被設定為`'+'`,或者以`'+'`結尾。 +3. to_field:指定關聯的欄位,預設關聯物件的主鍵欄位。 +4. db_constraint:是否為外來鍵建立約束,預設值為True。 +5. on_delete:外來鍵關聯的物件被刪除時對應的動作,可取的值包括django.db.models中定義的: + - CASCADE:級聯刪除。 + - PROTECT:丟擲ProtectedError異常,阻止刪除引用的物件。 + - SET_NULL:把外來鍵設定為null,當null屬性被設定為True時才能這麼做。 + - SET_DEFAULT:把外來鍵設定為預設值,提供了預設值才能這麼做。 + +ManyToManyField屬性 + +1. symmetrical:是否建立對稱的多對多關係。 +2. through:指定維持多對多關係的中間表的Django模型。 +3. throughfields:定義了中間模型時可以指定建立多對多關係的欄位。 +4. db_table:指定維持多對多關係的中間表的表名。 + +#### 模型元資料選項 + +| 選項 | 說明 | | --------------------- | ------------------------------------------------------------ | -| abstract | 设置为True时模型是抽象父类 | -| app_label | 如果定义模型的应用不在INSTALLED_APPS中可以用该属性指定 | -| db_table | 模型使用的数据表名称 | -| db_tablespace | 模型使用的数据表空间 | -| default_related_name | 关联对象回指这个模型时默认使用的名称,默认为_set | -| get_latest_by | 模型中可排序字段的名称。 | -| managed | 设置为True时,Django在迁移中创建数据表并在执行flush管理命令时把表移除 | -| order_with_respect_to | 标记对象为可排序的 | -| ordering | 对象的默认排序 | -| permissions | 创建对象时写入权限表的额外权限 | -| default_permissions | 默认为`('add', 'change', 'delete')` | -| unique_together | 设定组合在一起时必须独一无二的字段名 | -| index_together | 设定一起建立索引的多个字段名 | -| verbose_name | 为对象设定人类可读的名称 | -| verbose_name_plural | 设定对象的复数名称 | +| abstract | 設定為True時模型是抽象父類 | +| app_label | 如果定義模型的應用不在INSTALLED_APPS中可以用該屬性指定 | +| db_table | 模型使用的資料表名稱 | +| db_tablespace | 模型使用的資料表空間 | +| default_related_name | 關聯物件回指這個模型時預設使用的名稱,預設為_set | +| get_latest_by | 模型中可排序欄位的名稱。 | +| managed | 設定為True時,Django在遷移中建立資料表並在執行flush管理命令時把表移除 | +| order_with_respect_to | 標記物件為可排序的 | +| ordering | 物件的預設排序 | +| permissions | 建立物件時寫入許可權表的額外許可權 | +| default_permissions | 預設為`('add', 'change', 'delete')` | +| unique_together | 設定組合在一起時必須獨一無二的欄位名 | +| index_together | 設定一起建立索引的多個欄位名 | +| verbose_name | 為物件設定人類可讀的名稱 | +| verbose_name_plural | 設定物件的複數名稱 | -### 数据库API参考 +### 資料庫API參考 -按字段查找可以用的条件: +按欄位查詢可以用的條件: 1. exact / iexact 2. contains / icontains @@ -530,5 +530,5 @@ ManyToManyField属性 9. search 10. regex / iregex -跨关系查找 +跨關係查詢 diff --git "a/Day41-55/Django2\345\256\236\346\210\23003.md" "b/Day41-55/Django2\345\256\236\346\210\23003.md" index 8ae8cfad9..2f6b38053 100644 --- "a/Day41-55/Django2\345\256\236\346\210\23003.md" +++ "b/Day41-55/Django2\345\256\236\346\210\23003.md" @@ -1,2 +1,2 @@ -## Django 2.x实战(03) - 视图、模板和URL +## Django 2.x實戰(03) - 檢視、模板和URL diff --git "a/Day41-55/Django2\345\256\236\346\210\23004.md" "b/Day41-55/Django2\345\256\236\346\210\23004.md" index 4bd0d0c0a..485f8a611 100644 --- "a/Day41-55/Django2\345\256\236\346\210\23004.md" +++ "b/Day41-55/Django2\345\256\236\346\210\23004.md" @@ -1,2 +1,2 @@ -## Django 2.x实战(04) - 表单的应用 +## Django 2.x實戰(04) - 表單的應用 diff --git "a/Day41-55/Django2\345\256\236\346\210\23005.md" "b/Day41-55/Django2\345\256\236\346\210\23005.md" index 2ae6c36a6..2ae9b1287 100644 --- "a/Day41-55/Django2\345\256\236\346\210\23005.md" +++ "b/Day41-55/Django2\345\256\236\346\210\23005.md" @@ -1,4 +1,4 @@ -## Django 2.x实战(05) - Cookie和会话 +## Django 2.x實戰(05) - Cookie和會話 diff --git "a/Day41-55/Django2\345\256\236\346\210\23006.md" "b/Day41-55/Django2\345\256\236\346\210\23006.md" index 125311c53..5e851abd0 100644 --- "a/Day41-55/Django2\345\256\236\346\210\23006.md" +++ "b/Day41-55/Django2\345\256\236\346\210\23006.md" @@ -1,4 +1,4 @@ -## Django 2.x实战(06) - 日志和缓存 +## Django 2.x實戰(06) - 日誌和快取 diff --git "a/Day41-55/Django2\345\256\236\346\210\23007.md" "b/Day41-55/Django2\345\256\236\346\210\23007.md" index 46c42f946..7cdc6d5a9 100644 --- "a/Day41-55/Django2\345\256\236\346\210\23007.md" +++ "b/Day41-55/Django2\345\256\236\346\210\23007.md" @@ -1,4 +1,4 @@ -## Django 2.x实战(07) - 文件上传和通用视图 +## Django 2.x實戰(07) - 檔案上傳和通用檢視 diff --git "a/Day41-55/Django2\345\256\236\346\210\23008.md" "b/Day41-55/Django2\345\256\236\346\210\23008.md" index 479fca46a..43d21e27e 100644 --- "a/Day41-55/Django2\345\256\236\346\210\23008.md" +++ "b/Day41-55/Django2\345\256\236\346\210\23008.md" @@ -1,4 +1,4 @@ -## Django 2.x实战(08) - 用户/角色/权限和中间件 +## Django 2.x實戰(08) - 使用者/角色/許可權和中介軟體 diff --git "a/Day41-55/Django2\345\256\236\346\210\23009.md" "b/Day41-55/Django2\345\256\236\346\210\23009.md" index 1a3c0913d..28200be94 100644 --- "a/Day41-55/Django2\345\256\236\346\210\23009.md" +++ "b/Day41-55/Django2\345\256\236\346\210\23009.md" @@ -1,4 +1,4 @@ -## Django 2.x实战(09) - RESTful架构和应用(上) +## Django 2.x實戰(09) - RESTful架構和應用(上) diff --git "a/Day41-55/Django2\345\256\236\346\210\23010.md" "b/Day41-55/Django2\345\256\236\346\210\23010.md" index 0ac837130..876a845e0 100644 --- "a/Day41-55/Django2\345\256\236\346\210\23010.md" +++ "b/Day41-55/Django2\345\256\236\346\210\23010.md" @@ -1,4 +1,4 @@ -## Django 2.x实战(10) - RESTful架构和应用(下) +## Django 2.x實戰(10) - RESTful架構和應用(下) diff --git "a/PEP 8\351\243\216\346\240\274\346\214\207\345\215\227.md" "b/PEP 8\351\243\216\346\240\274\346\214\207\345\215\227.md" index 712452876..7502bad44 100644 --- "a/PEP 8\351\243\216\346\240\274\346\214\207\345\215\227.md" +++ "b/PEP 8\351\243\216\346\240\274\346\214\207\345\215\227.md" @@ -1,36 +1,36 @@ -## PEP 8风格指南 +## PEP 8風格指南 -PEP是Python Enhancement Proposal的缩写,通常翻译为“Python增强提案”。每个PEP都是一份为Python社区提供的指导Python往更好的方向发展的技术文档,其中的第8号增提案即PEP 8是针对Python语言编订的代码风格指南。尽管我们可以在保证语法没有问题的前提下随意书写Python代码,但是在实际开发中,采用一致的风格书写出可读性强的代码是每个专业的程序员应该做到的事情,也是每个公司的编程规范中会提出的要求,这些在多人协作开发一个项目(团队开发)的时候显得尤为重要。我们可以从Python官方网站的[PEP 8链接](https://www.python.org/dev/peps/pep-0008/)中找到该文档,下面我们对该文档的关键部分做一个简单的总结。 +PEP是Python Enhancement Proposal的縮寫,通常翻譯為“Python增強提案”。每個PEP都是一份為Python社群提供的指導Python往更好的方向發展的技術文件,其中的第8號增提案即PEP 8是針對Python語言編訂的程式碼風格指南。儘管我們可以在保證語法沒有問題的前提下隨意書寫Python程式碼,但是在實際開發中,採用一致的風格書寫出可讀性強的程式碼是每個專業的程式設計師應該做到的事情,也是每個公司的程式設計規範中會提出的要求,這些在多人協作開發一個專案(團隊開發)的時候顯得尤為重要。我們可以從Python官方網站的[PEP 8連結](https://www.python.org/dev/peps/pep-0008/)中找到該文件,下面我們對該文件的關鍵部分做一個簡單的總結。 ### 空格的使用 -1. **使用空格来表示缩进而不要用制表符(Tab)**。这一点对习惯了其他编程语言的人来说简直觉得不可理喻,因为绝大多数的程序员都会用Tab来表示缩进,但是要知道Python并没有像C/C++或Java那样的用花括号来构造一个代码块的语法,在Python中分支和循环结构都使用缩进来表示哪些代码属于同一个级别,鉴于此Python代码对缩进以及缩进宽度的依赖比其他很多语言都强得多。在不同的编辑器中,Tab的宽度可能是2、4或8个字符,甚至是其他更离谱的值,用Tab来表示缩进对Python代码来说可能是一场灾难。 -2. **和语法相关的每一层缩进都用4个空格来表示。** -3. **每行的字符数不要超过79个字符,如果表达式因太长而占据了多行,除了首行之外的其余各行都应该在正常的缩进宽度上再加上4个空格。** -4. **函数和类的定义,代码前后都要用两个空行进行分隔。** -5. **在同一个类中,各个方法之间应该用一个空行进行分隔。** -6. **二元运算符的左右两侧应该保留一个空格,而且只要一个空格就好。** +1. **使用空格來表示縮排而不要用製表符(Tab)**。這一點對習慣了其他程式語言的人來說簡直覺得不可理喻,因為絕大多數的程式設計師都會用Tab來表示縮排,但是要知道Python並沒有像C/C++或Java那樣的用花括號來構造一個程式碼塊的語法,在Python中分支和迴圈結構都使用縮排來表示哪些程式碼屬於同一個級別,鑑於此Python程式碼對縮排以及縮排寬度的依賴比其他很多語言都強得多。在不同的編輯器中,Tab的寬度可能是2、4或8個字元,甚至是其他更離譜的值,用Tab來表示縮排對Python程式碼來說可能是一場災難。 +2. **和語法相關的每一層縮排都用4個空格來表示。** +3. **每行的字元數不要超過79個字元,如果表示式因太長而佔據了多行,除了首行之外的其餘各行都應該在正常的縮排寬度上再加上4個空格。** +4. **函式和類的定義,程式碼前後都要用兩個空行進行分隔。** +5. **在同一個類中,各個方法之間應該用一個空行進行分隔。** +6. **二元運算子的左右兩側應該保留一個空格,而且只要一個空格就好。** -### 标识符命名 +### 識別符號命名 -PEP 8倡导用不同的命名风格来命名Python中不同的标识符,以便在阅读代码时能够通过标识符的名称来确定该标识符在Python中扮演了怎样的角色(在这一点上,Python自己的内置模块以及某些第三方模块都做得并不是很好)。 +PEP 8倡導用不同的命名風格來命名Python中不同的識別符號,以便在閱讀程式碼時能夠通過識別符號的名稱來確定該識別符號在Python中扮演了怎樣的角色(在這一點上,Python自己的內建模組以及某些第三方模組都做得並不是很好)。 -1. **变量、函数和属性应该使用小写字母来拼写,如果有多个单词就使用下划线进行连接。** -2. **类中受保护的实例属性,应该以一个下划线开头。** -3. **类中私有的实例属性,应该以两个下划线开头。** -4. **类和异常的命名,应该每个单词首字母大写。** -5. **模块级别的常量,应该采用全大写字母,如果有多个单词就用下划线进行连接。** -6. **类的实例方法,应该把第一个参数命名为`self`以表示对象自身。** -7. **类的类方法,应该把第一个参数命名为`cls`以表示该类自身。** +1. **變數、函式和屬性應該使用小寫字母來拼寫,如果有多個單詞就使用下劃線進行連線。** +2. **類中受保護的例項屬性,應該以一個下劃線開頭。** +3. **類中私有的例項屬性,應該以兩個下劃線開頭。** +4. **類和異常的命名,應該每個單詞首字母大寫。** +5. **模組級別的常量,應該採用全大寫字母,如果有多個單詞就用下劃線進行連線。** +6. **類的例項方法,應該把第一個引數命名為`self`以表示物件自身。** +7. **類的類方法,應該把第一個引數命名為`cls`以表示該類自身。** -### 表达式和语句 +### 表示式和語句 -在Python之禅(可以使用`import this`查看)中有这么一句名言:“There should be one-- and preferably only one --obvious way to do it.”,翻译成中文是“做一件事应该有而且最好只有一种确切的做法”,这句话传达的思想在PEP 8中也是无处不在的。 +在Python之禪(可以使用`import this`檢視)中有這麼一句名言:“There should be one-- and preferably only one --obvious way to do it.”,翻譯成中文是“做一件事應該有而且最好只有一種確切的做法”,這句話傳達的思想在PEP 8中也是無處不在的。 -1. **采用内联形式的否定词,而不要把否定词放在整个表达式的前面。**例如`if a is not b`就比`if not a is b`更容易让人理解。 -2. **不要用检查长度的方式来判断字符串、列表等是否为`None`或者没有元素,应该用`if not x`这样的写法来检查它。** -3. **就算`if`分支、`for`循环、`except`异常捕获等中只有一行代码,也不要将代码和`if`、`for`、`except`等写在一起,分开写才会让代码更清晰。** -4. **`import`语句总是放在文件开头的地方**。 -5. **引入模块的时候,`from math import sqrt`比`import math`更好。** -6. **如果有多个`import`语句,应该将其分为三部分,从上到下分别是Python标准模块、第三方模块和自定义模块,每个部分内部应该按照模块名称的字母表顺序来排列。** +1. **採用內聯形式的否定詞,而不要把否定詞放在整個表示式的前面。**例如`if a is not b`就比`if not a is b`更容易讓人理解。 +2. **不要用檢查長度的方式來判斷字串、列表等是否為`None`或者沒有元素,應該用`if not x`這樣的寫法來檢查它。** +3. **就算`if`分支、`for`迴圈、`except`異常捕獲等中只有一行程式碼,也不要將程式碼和`if`、`for`、`except`等寫在一起,分開寫才會讓程式碼更清晰。** +4. **`import`語句總是放在檔案開頭的地方**。 +5. **引入模組的時候,`from math import sqrt`比`import math`更好。** +6. **如果有多個`import`語句,應該將其分為三部分,從上到下分別是Python標準模組、第三方模組和自定義模組,每個部分內部應該按照模組名稱的字母表順序來排列。** diff --git "a/Python\345\217\202\350\200\203\344\271\246\347\261\215.md" "b/Python\345\217\202\350\200\203\344\271\246\347\261\215.md" index 9da68fe92..b4a283cfd 100644 --- "a/Python\345\217\202\350\200\203\344\271\246\347\261\215.md" +++ "b/Python\345\217\202\350\200\203\344\271\246\347\261\215.md" @@ -1,55 +1,55 @@ -## Python参考书籍 +## Python參考書籍 -### 入门读物 +### 入門讀物 -1. 《Python基础教程》(*Beginning Python From Novice to Professional*) -2. 《Python学习手册》(*Learning Python*) -3. 《Python编程》(*Programming Python*) +1. 《Python基礎教程》(*Beginning Python From Novice to Professional*) +2. 《Python學習手冊》(*Learning Python*) +3. 《Python程式設計》(*Programming Python*) 4. 《Python Cookbook》 -5. 《Python程序设计》(*Python Programming: An Introduction to Computer Science*) +5. 《Python程式設計》(*Python Programming: An Introduction to Computer Science*) 6. 《Modern Python Cookbook》 -### 进阶读物 +### 進階讀物 -1. 《Python核心编程》(*Core Python Applications Programming*) -2. 《流畅的Python》(*Fluent Python*) -3. 《Effective Python:编写高质量Python代码的59个有效方法》(*Effective Python 59 Specific Ways to Write Better Python*) -4. 《Python设计模式》(*Learning Python Design Patterns*) -5. 《Python高级编程》(*Expert Python Programming*) -6. 《Python性能分析与优化》(*Mastering Python High Performance*) +1. 《Python核心程式設計》(*Core Python Applications Programming*) +2. 《流暢的Python》(*Fluent Python*) +3. 《Effective Python:編寫高質量Python程式碼的59個有效方法》(*Effective Python 59 Specific Ways to Write Better Python*) +4. 《Python設計模式》(*Learning Python Design Patterns*) +5. 《Python高階程式設計》(*Expert Python Programming*) +6. 《Python效能分析與優化》(*Mastering Python High Performance*) ### Web框架 -1. 《Django基础教程》(*Tango with Django*) -2. 《轻量级Django》(*Lightweight Django*) -3. 《Python Web开发:测试驱动方法》(*Test-Driven Development with Python*) +1. 《Django基礎教程》(*Tango with Django*) +2. 《輕量級Django》(*Lightweight Django*) +3. 《Python Web開發:測試驅動方法》(*Test-Driven Development with Python*) 4. 《Web Development with Django Cookbook》 5. 《Test-Driven Development with Django》 6. 《Django Project Blueprints 》 -7. 《Flask Web开发:基于Python的Web应用开发实战》(*Flask Web Development: Developing Web Applications with Python*) +7. 《Flask Web開發:基於Python的Web應用開發實戰》(*Flask Web Development: Developing Web Applications with Python*) 8. 《深入理解Flask》(*Mastering Flask*) -### 爬虫开发 +### 爬蟲開發 -1. 《用Python写网络爬虫》(*Web Scraping with Python*) -2. 《精通Python爬虫框架Scrapy》(*Learning Scrapy*) -3. 《Python网络数据采集》(*Web Scraping with Python*) -4. 《Python爬虫开发与项目实战》 -5. 《Python 3网络爬虫开发实战》 +1. 《用Python寫網路爬蟲》(*Web Scraping with Python*) +2. 《精通Python爬蟲框架Scrapy》(*Learning Scrapy*) +3. 《Python網路資料採集》(*Web Scraping with Python*) +4. 《Python爬蟲開發與專案實戰》 +5. 《Python 3網路爬蟲開發實戰》 -### 数据分析 +### 資料分析 -1. 《利用Python进行数据分析》(*Python for Data Analysis*) -2. 《Python数据科学手册》(*Python Data Science Handbook*) -3. 《Python金融大数据分析》(*Python for Finance*) -4. 《Python数据可视化编程实战》(*Python Data Visualization Cookbook*) -5. 《Python数据处理》(*Data Wrangling with Python*) +1. 《利用Python進行資料分析》(*Python for Data Analysis*) +2. 《Python資料科學手冊》(*Python Data Science Handbook*) +3. 《Python金融大資料分析》(*Python for Finance*) +4. 《Python資料視覺化程式設計實戰》(*Python Data Visualization Cookbook*) +5. 《Python資料處理》(*Data Wrangling with Python*) -### 机器学习 +### 機器學習 -1. 《Python机器学习基础教程》(*Introduction to Machine Learning with Python*) -2. 《Python机器学习实践指南》(*Python Machine Learning Blueprints*) +1. 《Python機器學習基礎教程》(*Introduction to Machine Learning with Python*) +2. 《Python機器學習實踐指南》(*Python Machine Learning Blueprints*) 3. 《Python Machine Learning Case Studies》 -4. 《Python机器学习实践:测试驱动的开发方法》(*Thoughtful Machine Learning with Python A Test Driven Approach*) -5. 《Python机器学习经典实例》(*Python Machine Learning Cookbook*) -6. 《TensorFlow:实战Google深度学习框架》 \ No newline at end of file +4. 《Python機器學習實踐:測試驅動的開發方法》(*Thoughtful Machine Learning with Python A Test Driven Approach*) +5. 《Python機器學習經典例項》(*Python Machine Learning Cookbook*) +6. 《TensorFlow:實戰Google深度學習框架》 \ No newline at end of file diff --git "a/Python\346\203\257\344\276\213.md" "b/Python\346\203\257\344\276\213.md" index 54b0256dc..aff3481d5 100644 --- "a/Python\346\203\257\344\276\213.md" +++ "b/Python\346\203\257\344\276\213.md" @@ -1,49 +1,49 @@ -## Python惯例 +## Python慣例 -“惯例”这个词指的是“习惯的做法,常规的办法,一贯的做法”,与这个词对应的英文单词叫“idiom”。由于Python跟其他很多编程语言在语法和使用上还是有比较显著的差别,因此作为一个Python开发者如果不能掌握这些惯例,就无法写出“Pythonic”的代码。下面我们总结了一些在Python开发中的惯用的代码。 +“慣例”這個詞指的是“習慣的做法,常規的辦法,一貫的做法”,與這個詞對應的英文單詞叫“idiom”。由於Python跟其他很多程式語言在語法和使用上還是有比較顯著的差別,因此作為一個Python開發者如果不能掌握這些慣例,就無法寫出“Pythonic”的程式碼。下面我們總結了一些在Python開發中的慣用的程式碼。 -1. 让代码既可以被导入又可以被执行。 +1. 讓程式碼既可以被匯入又可以被執行。 ```Python if __name__ == '__main__': ``` -2. 用下面的方式判断逻辑“真”或“假”。 +2. 用下面的方式判斷邏輯“真”或“假”。 ```Python if x: if not x: ``` - **好**的代码: + **好**的程式碼: ```Python name = 'jackfrued' fruits = ['apple', 'orange', 'grape'] - owners = {'1001': '骆昊', '1002': '王大锤'} + owners = {'1001': '駱昊', '1002': '王大錘'} if name and fruits and owners: print('I love fruits!') ``` - **不好**的代码: + **不好**的程式碼: ```Python name = 'jackfrued' fruits = ['apple', 'orange', 'grape'] - owners = {'1001': '骆昊', '1002': '王大锤'} + owners = {'1001': '駱昊', '1002': '王大錘'} if name != '' and len(fruits) > 0 and owners != {}: print('I love fruits!') ``` -3. 善于使用in运算符。 +3. 善於使用in運算子。 ```Python if x in items: # 包含 for x in items: # 迭代 ``` - **好**的代码: + **好**的程式碼: ```Python name = 'Hao LUO' @@ -51,7 +51,7 @@ print('The name has an L in it.') ``` - **不好**的代码: + **不好**的程式碼: ```Python name = 'Hao LUO' @@ -59,15 +59,15 @@ print('This name has an L in it!') ``` -4. 不使用临时变量交换两个值。 +4. 不使用臨時變數交換兩個值。 ```Python a, b = b, a ``` -5. 用序列构建字符串。 +5. 用序列構建字串。 - **好**的代码: + **好**的程式碼: ```Python chars = ['j', 'a', 'c', 'k', 'f', 'r', 'u', 'e', 'd'] @@ -75,7 +75,7 @@ print(name) # jackfrued ``` - **不好**的代码: + **不好**的程式碼: ```Python chars = ['j', 'a', 'c', 'k', 'f', 'r', 'u', 'e', 'd'] @@ -85,13 +85,13 @@ print(name) # jackfrued ``` -6. EAFP优于LBYL。 +6. EAFP優於LBYL。 EAFP - **E**asier to **A**sk **F**orgiveness than **P**ermission. LBYL - **L**ook **B**efore **Y**ou **L**eap. - **好**的代码: + **好**的程式碼: ```Python d = {'x': '5'} @@ -102,7 +102,7 @@ value = None ``` - **不好**的代码: + **不好**的程式碼: ```Python d = {'x': '5'} @@ -114,9 +114,9 @@ value = None ``` -7. 使用enumerate进行迭代。 +7. 使用enumerate進行迭代。 - **好**的代码: + **好**的程式碼: ```Python fruits = ['orange', 'grape', 'pitaya', 'blueberry'] @@ -124,7 +124,7 @@ print(index, ':', fruit) ``` - **不好**的代码: + **不好**的程式碼: ```Python fruits = ['orange', 'grape', 'pitaya', 'blueberry'] @@ -136,7 +136,7 @@ 8. 用生成式生成列表。 - **好**的代码: + **好**的程式碼: ```Python data = [7, 20, 3, 15, 11] @@ -144,7 +144,7 @@ print(result) # [60, 45, 33] ``` - **不好**的代码: + **不好**的程式碼: ```Python data = [7, 20, 3, 15, 11] @@ -155,27 +155,27 @@ print(result) # [60, 45, 33] ``` -9. 用zip组合键和值来创建字典。 +9. 用zip組合鍵和值來建立字典。 - **好**的代码: + **好**的程式碼: ```Python keys = ['1001', '1002', '1003'] - values = ['骆昊', '王大锤', '白元芳'] + values = ['駱昊', '王大錘', '白元芳'] d = dict(zip(keys, values)) print(d) ``` - **不好**的代码: + **不好**的程式碼: ```Python keys = ['1001', '1002', '1003'] - values = ['骆昊', '王大锤', '白元芳'] + values = ['駱昊', '王大錘', '白元芳'] d = {} for i, key in enumerate(keys): d[key] = values[i] print(d) ``` -> **说明**:这篇文章的内容来自于网络,有兴趣的读者可以阅读[原文](http://safehammad.com/downloads/python-idioms-2014-01-16.pdf)。 +> **說明**:這篇文章的內容來自於網路,有興趣的讀者可以閱讀[原文](http://safehammad.com/downloads/python-idioms-2014-01-16.pdf)。 diff --git a/README.md b/README.md index 07aabf3f2..1215f9bb4 100644 --- a/README.md +++ b/README.md @@ -1,41 +1,41 @@ -## Python - 100天从新手到大师 +## Python - 100天從新手到大師 -### Python应用领域和就业形势分析 +### Python應用領域和就業形勢分析 -简单的说,Python是一个“优雅”、“明确”、“简单”的编程语言。 +簡單的說,Python是一個“優雅”、“明確”、“簡單”的程式語言。 - - 学习曲线低,适合非专业人士 - - 开源系统,拥有强大的生态圈 - - 解释型语言,完美的平台可移植性 - - 支持面向对象和函数式编程 - - 可扩展性,能调用C/C++代码 - - 代码规范程度高,可读性强 + - 學習曲線低,適合非專業人士 + - 開源系統,擁有強大的生態圈 + - 解釋型語言,完美的平臺可移植性 + - 支援面向物件和函數語言程式設計 + - 可擴充套件性,能呼叫C/C++程式碼 + - 程式碼規範程度高,可讀性強 -目前几个比较流行的领域,Python都有用武之地。 +目前幾個比較流行的領域,Python都有用武之地。 - - 云基础设施 - Python / Java / Go + - 雲基礎設施 - Python / Java / Go - DevOps - Python / Shell / Ruby / Go - - 网络爬虫 - Python / PHP / C++ - - 数据分析挖掘 - Python / R / Scala / Matlab - - 机器学习 - Python / R / Java / Lisp + - 網路爬蟲 - Python / PHP / C++ + - 資料分析挖掘 - Python / R / Scala / Matlab + - 機器學習 - Python / R / Java / Lisp -作为一名Python开发者,主要的就业领域包括: +作為一名Python開發者,主要的就業領域包括: -- Python服务器后台开发 / 游戏服务器开发 / 数据接口开发工程师 -- Python自动化运维工程师 -- Python数据分析 / 数据可视化 / 科学计算 / 大数据工程师 -- Python爬虫工程师 -- Python聊天机器人开发 / 图像识别和视觉算法 / 深度学习工程师 +- Python伺服器後臺開發 / 遊戲伺服器開發 / 資料介面開發工程師 +- Python自動化運維工程師 +- Python資料分析 / 資料視覺化 / 科學計算 / 大資料工程師 +- Python爬蟲工程師 +- Python聊天機器人開發 / 影象識別和視覺演算法 / 深度學習工程師 -下图显示了主要城市Python招聘需求量及薪资待遇排行榜(截止到2018年5月)。 +下圖顯示了主要城市Python招聘需求量及薪資待遇排行榜(截止到2018年5月)。 -![Python招聘需求及薪资待遇Top 10](./res/python-top-10.png) +![Python招聘需求及薪資待遇Top 10](./res/python-top-10.png) ![](./res/python-bj-salary.png) ![](./res/python-cd-salary.png) -给初学者的几个建议(老司机的忠告): +給初學者的幾個建議(老司機的忠告): - Make English as your working language. - Practice makes perfect. @@ -43,237 +43,237 @@ - Don't be one of the leeches. - Either stand out or kicked out. -### Day01~15 - [Python语言基础](./Day01-15) +### Day01~15 - [Python語言基礎](./Day01-15) -#### Day01 - [初识Python](./Day01-15/Day01/初识Python.md) +#### Day01 - [初識Python](./Day01-15/Day01/初識Python.md) -- Python简介 - Python的历史 / Python的优缺点 / Python的应用领域 -- 搭建编程环境 - Windows环境 / Linux环境 / MacOS环境 -- 从终端运行Python程序 - DOS命令 / Hello, world / print函数 / 运行程序 -- 使用IDLE - 交互式环境(REPL) / 编写多行代码 / 运行程序 / 退出IDLE -- 注释 - 注释的作用 / 单行注释 / 多行注释 +- Python簡介 - Python的歷史 / Python的優缺點 / Python的應用領域 +- 搭建程式設計環境 - Windows環境 / Linux環境 / MacOS環境 +- 從終端執行Python程式 - DOS命令 / Hello, world / print函式 / 執行程式 +- 使用IDLE - 互動式環境(REPL) / 編寫多行程式碼 / 執行程式 / 退出IDLE +- 註釋 - 註釋的作用 / 單行註釋 / 多行註釋 -#### Day02 - [语言元素](./Day01-15/Day02/语言元素.md) +#### Day02 - [語言元素](./Day01-15/Day02/語言元素.md) -- 程序和进制 - 指令和程序 / 冯诺依曼机 / 二进制和十进制 / 八进制和十六进制 -- 变量和类型 - 变量的命名 / 变量的使用 / input函数 / 检查变量类型 / 类型转换 -- 数字和字符串 - 整数 / 浮点数 / 复数 / 字符串 / 字符串基本操作 / 字符编码 -- 运算符 - 数学运算符 / 赋值运算符 / 比较运算符 / 逻辑运算符 / 身份运算符 / 运算符的优先级 -- 应用案例 - 华氏温度转换成摄氏温度 / 输入圆的半径计算周长和面积 / 输入年份判断是否是闰年 +- 程式和進位制 - 指令和程式 / 馮諾依曼機 / 二進位制和十進位制 / 八進位制和十六進位制 +- 變數和型別 - 變數的命名 / 變數的使用 / input函式 / 檢查變數型別 / 型別轉換 +- 數字和字串 - 整數 / 浮點數 / 複數 / 字串 / 字串基本操作 / 字元編碼 +- 運算子 - 數學運算子 / 賦值運算子 / 比較運算子 / 邏輯運算子 / 身份運算子 / 運算子的優先順序 +- 應用案例 - 華氏溫度轉換成攝氏溫度 / 輸入圓的半徑計算周長和麵積 / 輸入年份判斷是否是閏年 -#### Day03 - [分支结构](./Day01-15/Day03/分支结构.md) +#### Day03 - [分支結構](./Day01-15/Day03/分支結構.md) -- 分支结构的应用场景 - 条件 / 缩进 / 代码块 / 流程图 -- if语句 - 简单的if / if-else结构 / if-elif-else结构 / 嵌套的if -- 应用案例 - 用户身份验证 / 英制单位与公制单位互换 / 掷骰子决定做什么 / 百分制成绩转等级制 / 分段函数求值 / 输入三条边的长度如果能构成三角形就计算周长和面积 +- 分支結構的應用場景 - 條件 / 縮排 / 程式碼塊 / 流程圖 +- if語句 - 簡單的if / if-else結構 / if-elif-else結構 / 巢狀的if +- 應用案例 - 使用者身份驗證 / 英制單位與公制單位互換 / 擲骰子決定做什麼 / 百分制成績轉等級制 / 分段函式求值 / 輸入三條邊的長度如果能構成三角形就計算周長和麵積 -#### Day04 - [循环结构](./Day01-15/Day04/循环结构.md) +#### Day04 - [迴圈結構](./Day01-15/Day04/迴圈結構.md) -- 循环结构的应用场景 - 条件 / 缩进 / 代码块 / 流程图 -- while循环 - 基本结构 / break语句 / continue语句 -- for循环 - 基本结构 / range类型 / 循环中的分支结构 / 嵌套的循环 / 提前结束程序 -- 应用案例 - 1~100求和 / 判断素数 / 猜数字游戏 / 打印九九表 / 打印三角形图案 / 猴子吃桃 / 百钱百鸡 +- 迴圈結構的應用場景 - 條件 / 縮排 / 程式碼塊 / 流程圖 +- while迴圈 - 基本結構 / break語句 / continue語句 +- for迴圈 - 基本結構 / range型別 / 迴圈中的分支結構 / 巢狀的迴圈 / 提前結束程式 +- 應用案例 - 1~100求和 / 判斷素數 / 猜數字遊戲 / 列印九九表 / 列印三角形圖案 / 猴子吃桃 / 百錢百雞 -#### Day05 - [总结和练习](./Day01-15/Day05/练习.md) +#### Day05 - [總結和練習](./Day01-15/Day05/練習.md) -- 基础练习 - 水仙花数 / 完美数 / 五人分鱼 / Fibonacci数列 / 回文素数 -- 综合练习 - Craps赌博游戏 +- 基礎練習 - 水仙花數 / 完美數 / 五人分魚 / Fibonacci數列 / 迴文素數 +- 綜合練習 - Craps賭博遊戲 -#### Day06 - [函数和模块的使用](./Day01-15/Day06/函数和模块的使用.md) +#### Day06 - [函式和模組的使用](./Day01-15/Day06/函式和模組的使用.md) -- 函数的作用 - 代码的坏味道 / 用函数封装功能模块 -- 定义函数 - def语句 / 函数名 / 参数列表 / return语句 / 调用自定义函数 -- 调用函数 - Python内置函数 / 导入模块和函数 -- 函数的参数 - 默认参数 / 可变参数 / 关键字参数(\*) / 命名关键字参数(\*) -- 函数的返回值 - 没有返回值 / 返回单个值 / 返回多个值(\*) -- 作用域问题 - 局部作用域 / 嵌套作用域 / 全局作用域 / 内置作用域 / 和作用域相关的关键字 -- 用模块管理函数 - 模块的概念 / 用自定义模块管理函数 / 命名冲突的时候会怎样(同一个模块和不同的模块) +- 函式的作用 - 程式碼的壞味道 / 用函式封裝功能模組 +- 定義函式 - def語句 / 函式名 / 引數列表 / return語句 / 呼叫自定義函式 +- 呼叫函式 - Python內建函式 / 匯入模組和函式 +- 函式的引數 - 預設引數 / 可變引數 / 關鍵字引數(\*) / 命名關鍵字引數(\*) +- 函式的返回值 - 沒有返回值 / 返回單個值 / 返回多個值(\*) +- 作用域問題 - 區域性作用域 / 巢狀作用域 / 全域性作用域 / 內建作用域 / 和作用域相關的關鍵字 +- 用模組管理函式 - 模組的概念 / 用自定義模組管理函式 / 命名衝突的時候會怎樣(同一個模組和不同的模組) -#### Day07 - [字符串和常用数据结构](./Day01-15/Day07/字符串和常用数据结构.md) +#### Day07 - [字串和常用資料結構](./Day01-15/Day07/字串和常用資料結構.md) -- 字符串的使用 - 计算长度 / 下标运算 / 切片 / 常用方法 -- 列表基本用法 - 定义列表 / 用下表访问元素 / 下标越界 / 添加元素 / 删除元素 / 修改元素 / 切片 / 循环遍历 -- 列表常用操作 - 连接 / 复制(复制元素和复制数组) / 长度 / 排序 / 倒转 / 查找 -- 生成列表 - 使用range创建数字列表 / 生成表达式 / 生成器 -- 元组的使用 - 定义元组 / 使用元组中的值 / 修改元组变量 / 元组和列表转换 -- 集合基本用法 - 集合和列表的区别 / 创建集合 / 添加元素 / 删除元素 / 清空 -- 集合常用操作 - 交集 / 并集 / 差集 / 对称差 / 子集 / 超集 -- 字典的基本用法 - 字典的特点 / 创建字典 / 添加元素 / 删除元素 / 取值 / 清空 +- 字串的使用 - 計算長度 / 下標運算 / 切片 / 常用方法 +- 列表基本用法 - 定義列表 / 用下表訪問元素 / 下標越界 / 新增元素 / 刪除元素 / 修改元素 / 切片 / 迴圈遍歷 +- 列表常用操作 - 連線 / 複製(複製元素和複製陣列) / 長度 / 排序 / 倒轉 / 查詢 +- 生成列表 - 使用range建立數字列表 / 生成表示式 / 生成器 +- 元組的使用 - 定義元組 / 使用元組中的值 / 修改元組變數 / 元組和列表轉換 +- 集合基本用法 - 集合和列表的區別 / 建立集合 / 新增元素 / 刪除元素 / 清空 +- 集合常用操作 - 交集 / 並集 / 差集 / 對稱差 / 子集 / 超集 +- 字典的基本用法 - 字典的特點 / 建立字典 / 新增元素 / 刪除元素 / 取值 / 清空 - 字典常用操作 - keys()方法 / values()方法 / items()方法 / setdefault()方法 -- 基础练习 - 跑马灯效果 / 列表找最大元素 / 统计考试成绩的平均分 / Fibonacci数列 / 杨辉三角 -- 综合案例 - 双色球选号 / 井字棋 +- 基礎練習 - 跑馬燈效果 / 列表找最大元素 / 統計考試成績的平均分 / Fibonacci數列 / 楊輝三角 +- 綜合案例 - 雙色球選號 / 井字棋 -#### Day08 - [面向对象编程基础](./Day01-15/Day08/面向对象编程基础.md) +#### Day08 - [面向物件程式設計基礎](./Day01-15/Day08/面向物件程式設計基礎.md) -- 类和对象 - 什么是类 / 什么是对象 / 面向对象其他相关概念 -- 定义类 - 基本结构 / 属性和方法 / 构造器 / 析构器 / \_\_str\_\_方法 -- 使用对象 - 创建对象 / 给对象发消息 -- 面向对象的四大支柱 - 抽象 / 封装 / 继承 / 多态 -- 基础练习 - 定义学生类 / 定义时钟类 / 定义图形类 / 定义汽车类 +- 類和物件 - 什麼是類 / 什麼是物件 / 面向物件其他相關概念 +- 定義類 - 基本結構 / 屬性和方法 / 構造器 / 析構器 / \_\_str\_\_方法 +- 使用物件 - 建立物件 / 給物件發訊息 +- 面向物件的四大支柱 - 抽象 / 封裝 / 繼承 / 多型 +- 基礎練習 - 定義學生類 / 定義時鐘類 / 定義圖形類 / 定義汽車類 -#### Day09 - [面向对象进阶](./Day01-15/Day09/面向对象进阶.md) +#### Day09 - [面向物件進階](./Day01-15/Day09/面向物件進階.md) -- 属性 - 类属性 / 实例属性 / 属性访问器 / 属性修改器 / 属性删除器 / 使用\_\_slots\_\_ -- 类中的方法 - 实例方法 / 类方法 / 静态方法 -- 运算符重载 - \_\_add\_\_ / \_\_sub\_\_ / \_\_or\_\_ /\_\_getitem\_\_ / \_\_setitem\_\_ / \_\_len\_\_ / \_\_repr\_\_ / \_\_gt\_\_ / \_\_lt\_\_ / \_\_le\_\_ / \_\_ge\_\_ / \_\_eq\_\_ / \_\_ne\_\_ / \_\_contains\_\_ -- 类(的对象)之间的关系 - 关联 / 继承 / 依赖 -- 继承和多态 - 什么是继承 / 继承的语法 / 调用父类方法 / 方法重写 / 类型判定 / 多重继承 / 菱形继承(钻石继承)和C3算法 -- 综合案例 - 工资结算系统 / 图书自动折扣系统 / 自定义分数类 +- 屬性 - 類屬性 / 例項屬性 / 屬性訪問器 / 屬性修改器 / 屬性刪除器 / 使用\_\_slots\_\_ +- 類中的方法 - 例項方法 / 類方法 / 靜態方法 +- 運算子過載 - \_\_add\_\_ / \_\_sub\_\_ / \_\_or\_\_ /\_\_getitem\_\_ / \_\_setitem\_\_ / \_\_len\_\_ / \_\_repr\_\_ / \_\_gt\_\_ / \_\_lt\_\_ / \_\_le\_\_ / \_\_ge\_\_ / \_\_eq\_\_ / \_\_ne\_\_ / \_\_contains\_\_ +- 類(的物件)之間的關係 - 關聯 / 繼承 / 依賴 +- 繼承和多型 - 什麼是繼承 / 繼承的語法 / 呼叫父類方法 / 方法重寫 / 型別判定 / 多重繼承 / 菱形繼承(鑽石繼承)和C3演算法 +- 綜合案例 - 工資結算系統 / 圖書自動折扣系統 / 自定義分數類 -#### Day10 - [图形用户界面和游戏开发](./Day01-15/Day10/图形用户界面和游戏开发.md) +#### Day10 - [圖形使用者介面和遊戲開發](./Day01-15/Day10/圖形使用者介面和遊戲開發.md) -#### Day11 - [文件和异常](./Day01-15/Day11/文件和异常.md) +#### Day11 - [檔案和異常](./Day01-15/Day11/檔案和異常.md) -- 读文件 - 读取整个文件 / 逐行读取 / 文件路径 -- 写文件 - 覆盖写入 / 追加写入 / 文本文件 / 二进制文件 -- 异常处理 - 异常机制的重要性 / try-except代码块 / else代码块 / finally代码块 / 内置异常类型 / 异常栈 / raise语句 -- 数据持久化 - CSV文件概述 / csv模块的应用 / JSON数据格式 / json模块的应用 -- 综合案例 - 歌词解析 +- 讀檔案 - 讀取整個檔案 / 逐行讀取 / 檔案路徑 +- 寫檔案 - 覆蓋寫入 / 追加寫入 / 文字檔案 / 二進位制檔案 +- 異常處理 - 異常機制的重要性 / try-except程式碼塊 / else程式碼塊 / finally程式碼塊 / 內建異常型別 / 異常棧 / raise語句 +- 資料持久化 - CSV檔案概述 / csv模組的應用 / JSON資料格式 / json模組的應用 +- 綜合案例 - 歌詞解析 -#### Day12 - [字符串和正则表达式](./Day01-15/Day12/字符串和正则表达式.md) +#### Day12 - [字串和正則表示式](./Day01-15/Day12/字串和正則表示式.md) -- 字符串高级操作 - 转义字符 \ 原始字符串 \ 多行字符串 \ in和 not in运算符 \ is开头的方法 \ join和split方法 \ strip相关方法 \ pyperclip模块 \ 不变字符串和可变字符串 \ StringIO的使用 -- 正则表达式入门 - 正则表达式的作用 \ 元字符 \ 转义 \ 量词 \ 分组 \ 零宽断言 \贪婪匹配与惰性匹配懒惰 \ 使用re模块实现正则表达式操作(匹配、搜索、替换、捕获) -- 使用正则表达式 - re模块 \ compile函数 \ group和groups方法 \ match方法 \ search方法 \ findall和finditer方法 \ sub和subn方法 \ split方法 -- 应用案例 - 使用正则表达式验证输入的字符串 +- 字串高階操作 - 轉義字元 \ 原始字串 \ 多行字串 \ in和 not in運算子 \ is開頭的方法 \ join和split方法 \ strip相關方法 \ pyperclip模組 \ 不變字串和可變字串 \ StringIO的使用 +- 正則表示式入門 - 正則表示式的作用 \ 元字元 \ 轉義 \ 量詞 \ 分組 \ 零寬斷言 \貪婪匹配與惰性匹配懶惰 \ 使用re模組實現正則表示式操作(匹配、搜尋、替換、捕獲) +- 使用正則表示式 - re模組 \ compile函式 \ group和groups方法 \ match方法 \ search方法 \ findall和finditer方法 \ sub和subn方法 \ split方法 +- 應用案例 - 使用正則表示式驗證輸入的字串 -#### Day13 - [进程和线程](./Day01-15/Day13/进程和线程入门.md) +#### Day13 - [程序和執行緒](./Day01-15/Day13/程序和執行緒入門.md) -- 进程和线程的概念 - 什么是进程 / 什么是线程 / 多线程的应用场景 -- 使用进程 - fork函数 / multiprocessing模块 / 进程池 / 进程间通信 -- 使用线程 - thread模块 / threading模块 / Thread类 / Lock类 +- 程序和執行緒的概念 - 什麼是程序 / 什麼是執行緒 / 多執行緒的應用場景 +- 使用程序 - fork函式 / multiprocessing模組 / 程序池 / 程序間通訊 +- 使用執行緒 - thread模組 / threading模組 / Thread類 / Lock類 -#### Day14 - [网络编程入门](./Day01-15/Day14/网络编程入门.md) +#### Day14 - [網路程式設計入門](./Day01-15/Day14/網路程式設計入門.md) -- 计算机网络基础 - 计算机网络发展史 / “TCP-IP”模型 / IP地址 / 端口 / 协议 / 其他相关概念 -- 网络应用架构 - “客户端-服务器”架构 / “浏览器-服务器”架构 -- Python网络编程 - 套接字的概念 / socket模块 / socket函数 / 创建TCP服务器 / 创建TCP客户端 / 创建UDP服务器 / 创建UDP客户端 / SocketServer模块 +- 計算機網路基礎 - 計算機網路發展史 / “TCP-IP”模型 / IP地址 / 埠 / 協議 / 其他相關概念 +- 網路應用架構 - “客戶端-伺服器”架構 / “瀏覽器-伺服器”架構 +- Python網路程式設計 - 套接字的概念 / socket模組 / socket函式 / 建立TCP伺服器 / 建立TCP客戶端 / 建立UDP伺服器 / 建立UDP客戶端 / SocketServer模組 -#### Day15 - [网络应用开发](./Day01-15/Day15/网络应用开发.md) +#### Day15 - [網路應用開發](./Day01-15/Day15/網路應用開發.md) -- 访问网络API - 网络API概述 / 访问URL / requests模块 / 解析JSON格式数据 -- 文件传输 - FTP协议 / ftplib模块 / 交互式FTP应用 -- 电子邮件 - SMTP协议 / POP3协议 / IMAP协议 / smtplib模块 / poplib模块 / imaplib模块 -- 短信服务 - twilio模块 / 国内的短信服务 +- 訪問網路API - 網路API概述 / 訪問URL / requests模組 / 解析JSON格式資料 +- 檔案傳輸 - FTP協議 / ftplib模組 / 互動式FTP應用 +- 電子郵件 - SMTP協議 / POP3協議 / IMAP協議 / smtplib模組 / poplib模組 / imaplib模組 +- 簡訊服務 - twilio模組 / 國內的簡訊服務 -### Day16~Day20 - [Python语言进阶 ](./Day16-20) +### Day16~Day20 - [Python語言進階 ](./Day16-20) ### Day21~30 - [Web前端](./Day21-30) -- 用HTML标签承载页面内容 -- 用CSS渲染页面 -- 用JavaScript处理交互式行为 -- jQuery入门和提高 -- Bootstrap在Web项目中的应用 - -### Day31~35 - [Linux操作系统](./Day31-35) - -- 操作系统发展史和Linux概述 -- Linux基础命令 -- Linux中的实用程序 -- Linux的文件系统 -- Vim编辑器的应用 -- 环境变量和Shell编程 -- 软件的安装和服务的配置 -- 网络访问和管理 -- 其他相关内容 - -### Day36~40 - [数据库基础和进阶](./Day36-40) - -- [关系型数据库MySQL](./Day36-40/关系型数据库MySQL.md) - - 关系型数据库概述 - - MySQL的安装和使用 +- 用HTML標籤承載頁面內容 +- 用CSS渲染頁面 +- 用JavaScript處理互動式行為 +- jQuery入門和提高 +- Bootstrap在Web專案中的應用 + +### Day31~35 - [Linux作業系統](./Day31-35) + +- 作業系統發展史和Linux概述 +- Linux基礎命令 +- Linux中的實用程式 +- Linux的檔案系統 +- Vim編輯器的應用 +- 環境變數和Shell程式設計 +- 軟體的安裝和服務的配置 +- 網路訪問和管理 +- 其他相關內容 + +### Day36~40 - [資料庫基礎和進階](./Day36-40) + +- [關係型資料庫MySQL](./Day36-40/關係型資料庫MySQL.md) + - 關係型資料庫概述 + - MySQL的安裝和使用 - SQL的使用 - DDL - DML - DQL - DCL - 在Python中操作MySQL - - MySQL高级知识 -- [非关系型数据库Redis](./Day36-40/非关系型数据库Redis.md) - - Redis的安装和基本配置 - - Redis的常用命令和数据类型 - - Redis的主从复制和哨兵模式 - - Redis高级操作和集群 + - MySQL高階知識 +- [非關係型資料庫Redis](./Day36-40/非關係型資料庫Redis.md) + - Redis的安裝和基本配置 + - Redis的常用命令和資料型別 + - Redis的主從複製和哨兵模式 + - Redis高階操作和叢集 - 在Python中操作Redis ### Day41~55 - [Django](./Day41-55) -#### Day41 - [Django实战(01) - 快速上手](./Day41-55/Django2实战01.md) +#### Day41 - [Django實戰(01) - 快速上手](./Day41-55/Django2實戰01.md) -#### Day42 - [Django实战(02) - 深入模型](./Day41-55/Django2实战02.md) +#### Day42 - [Django實戰(02) - 深入模型](./Day41-55/Django2實戰02.md) -#### Day43 - [Django实战(03) - 视图和模板](./Day41-55/Django2实战03.md) +#### Day43 - [Django實戰(03) - 檢視和模板](./Day41-55/Django2實戰03.md) -#### Day44 - [Django实战(04) - 表单的应用](./Day41-55/Django2实战04.md) +#### Day44 - [Django實戰(04) - 表單的應用](./Day41-55/Django2實戰04.md) -#### Day45 - [Django实战(05) - Cookie和会话](./Day41-55/Django2实战05.md) +#### Day45 - [Django實戰(05) - Cookie和會話](./Day41-55/Django2實戰05.md) -#### Day46 - [Django实战(06) - 日志和缓存](./Day41-55/Django2实战06.md) +#### Day46 - [Django實戰(06) - 日誌和快取](./Day41-55/Django2實戰06.md) -#### Day47 - [Django实战(07) - 文件上传和通用视图](./Day41-55/Django2实战07.md) +#### Day47 - [Django實戰(07) - 檔案上傳和通用檢視](./Day41-55/Django2實戰07.md) -#### Day48 - [Django实战(08) - 用户/角色/权限和中间件](./Day41-55/Django2实战08.md) +#### Day48 - [Django實戰(08) - 使用者/角色/許可權和中介軟體](./Day41-55/Django2實戰08.md) -#### Day49 - [Django实战(09) - RESTful架构和应用(上)](./Day41-55/Django2实战09.md) +#### Day49 - [Django實戰(09) - RESTful架構和應用(上)](./Day41-55/Django2實戰09.md) -#### Day50 - [Django实战(10) - RESTful架构和应用(下)](./Day41-55/Django2实战10.md) +#### Day50 - [Django實戰(10) - RESTful架構和應用(下)](./Day41-55/Django2實戰10.md) -#### Day51-55 - [Django项目实战](./Day41-55/Django2项目实战.md) +#### Day51-55 - [Django專案實戰](./Day41-55/Django2專案實戰.md) -- 项目开发流程和相关工具 -- 生成非HTML内容 -- 项目部署和测试 -- 项目性能调优 -- Web应用安全保护 +- 專案開發流程和相關工具 +- 生成非HTML內容 +- 專案部署和測試 +- 專案效能調優 +- Web應用安全保護 ### Day56~65 - [Flask](./Day56-65) -#### Day56 - [Flask安装和入门](./Day56-65/Flash安装和入门.md) +#### Day56 - [Flask安裝和入門](./Day56-65/Flash安裝和入門.md) #### Day57 - [模板的使用](./Day56-65/模板的使用.md) -#### Day58 - [表单的处理](./Day56-65/表单的处理.md) +#### Day58 - [表單的處理](./Day56-65/表單的處理.md) -#### Day59 - [数据库操作](./Day56-65/数据库操作.md) +#### Day59 - [資料庫操作](./Day56-65/資料庫操作.md) -#### Day60 - [使用Flask进行项目开发](./Day56-65/使用Flask进行项目开发.md) +#### Day60 - [使用Flask進行專案開發](./Day56-65/使用Flask進行專案開發.md) -#### Day61-65 - [Flask项目实战](./Day56-65/Flask项目实战.md) +#### Day61-65 - [Flask專案實戰](./Day56-65/Flask專案實戰.md) -- 性能和测试 -- 项目部署 +- 效能和測試 +- 專案部署 -### Day66~75 - [爬虫](./Day66-75) +### Day66~75 - [爬蟲](./Day66-75) -#### Day66 - [爬虫简介和相关工具](./Day66-75/爬虫简介和相关工具.md) +#### Day66 - [爬蟲簡介和相關工具](./Day66-75/爬蟲簡介和相關工具.md) -#### Day67 - [数据采集和解析](./Day66-75/数据采集和解析.md) +#### Day67 - [資料採集和解析](./Day66-75/資料採集和解析.md) -#### Day68 - [缓存数据](./Day66-75/缓存数据.md) +#### Day68 - [快取資料](./Day66-75/快取資料.md) -#### Day69 - [并发下载](./Day66-75/并发下载.md) +#### Day69 - [併發下載](./Day66-75/併發下載.md) -#### Day70 - [解析动态内容](./Day66-75/解析动态内容.md) +#### Day70 - [解析動態內容](./Day66-75/解析動態內容.md) -#### Day71 - [表单交互和验证码处理](./Day66-75/表单交互和验证码处理.md) +#### Day71 - [表單互動和驗證碼處理](./Day66-75/表單互動和驗證碼處理.md) -#### Day72 - [爬虫中的陷阱](./Day66-75/爬虫中的陷阱.md) +#### Day72 - [爬蟲中的陷阱](./Day66-75/爬蟲中的陷阱.md) -#### Day73 - [Scrapy的应用(1)](./Day66-75/Scrapy的应用1.md) +#### Day73 - [Scrapy的應用(1)](./Day66-75/Scrapy的應用1.md) -#### Day74 - [Scrapy的应用(2)](./Day66-75/Scrapy的应用2.md) +#### Day74 - [Scrapy的應用(2)](./Day66-75/Scrapy的應用2.md) -#### Day75 - [Scrapy的应用(3)](./Day66-75/Scrapy的应用3.md) +#### Day75 - [Scrapy的應用(3)](./Day66-75/Scrapy的應用3.md) -### Day76~90 - [数据处理和机器学习](./Day76-90) +### Day76~90 - [資料處理和機器學習](./Day76-90) -### Day91~100 - [团队项目开发](./Day91-100) +### Day91~100 - [團隊專案開發](./Day91-100) diff --git "a/\347\216\251\350\275\254PyCharm(\344\270\212).md" "b/\347\216\251\350\275\254PyCharm(\344\270\212).md" index 0ab57eded..78db134d6 100644 --- "a/\347\216\251\350\275\254PyCharm(\344\270\212).md" +++ "b/\347\216\251\350\275\254PyCharm(\344\270\212).md" @@ -1,47 +1,47 @@ -## 玩转PyCharm(上) +## 玩轉PyCharm(上) -PyCharm是由JetBrains公司开发的提供给Python专业的开发者的一个集成开发环境,它最大的优点是能够大大提升Python开发者的工作效率,为开发者集成了很多用起来非常顺手的功能,包括代码调试、高亮语法、代码跳转、智能提示、自动补全、单元测试、版本控制等等。此外,PyCharm还提供了对一些高级功能的支持,包括支持基于Django框架的Web开发、。 +PyCharm是由JetBrains公司開發的提供給Python專業的開發者的一個整合開發環境,它最大的優點是能夠大大提升Python開發者的工作效率,為開發者集成了很多用起來非常順手的功能,包括程式碼除錯、高亮語法、程式碼跳轉、智慧提示、自動補全、單元測試、版本控制等等。此外,PyCharm還提供了對一些高階功能的支援,包括支援基於Django框架的Web開發、。 -### PyCharm的安装 +### PyCharm的安裝 -可以在[JetBrains公司的官方网站]()找到PyCharm的[下载链接](https://www.jetbrains.com/pycharm/download/),有两个可供下载的版本一个是社区版一个是专业版,社区版在[Apache许可证](https://zh.wikipedia.org/wiki/Apache%E8%AE%B8%E5%8F%AF%E8%AF%81)下发布,专业版在专用许可证下发布(需要购买授权下载后可试用30天),其拥有许多额外功能。安装PyCharm需要有JRE(Java运行时环境)的支持,如果没有可以在安装过程中选择在线下载安装。 +可以在[JetBrains公司的官方網站]()找到PyCharm的[下載連結](https://www.jetbrains.com/pycharm/download/),有兩個可供下載的版本一個是社群版一個是專業版,社群版在[Apache許可證](https://zh.wikipedia.org/wiki/Apache%E8%AE%B8%E5%8F%AF%E8%AF%81)下發布,專業版在專用許可證下發布(需要購買授權下載後可試用30天),其擁有許多額外功能。安裝PyCharm需要有JRE(Java執行時環境)的支援,如果沒有可以在安裝過程中選擇線上下載安裝。 -### 首次使用的设置 +### 首次使用的設定 -第一次使用PyCharm时,会有一个导入设置的向导,如果之前没有使用PyCharm或者没有保存过设置的就直接选择“Do not import settings”进入下一步即可。 +第一次使用PyCharm時,會有一個匯入設定的嚮導,如果之前沒有使用PyCharm或者沒有儲存過設定的就直接選擇“Do not import settings”進入下一步即可。 ![](./res/pycharm-import-settings.png) -专业版的PyCharm是需要激活的,强烈建议为优秀的软件支付费用,如果不用做商业用途,我们可以暂时选择试用30天或者使用社区版的PyCharm。 +專業版的PyCharm是需要啟用的,強烈建議為優秀的軟體支付費用,如果不用做商業用途,我們可以暫時選擇試用30天或者使用社群版的PyCharm。 ![](./res/pycharm-activate.png) - 接下来是选择UI主题,这个可以根据个人喜好进行选择。 + 接下來是選擇UI主題,這個可以根據個人喜好進行選擇。 ![](./res/pycharm-set-ui-theme.png) - 再接下来是创建可以在终端(命令行)中使用PyCharm项目的启动脚本,当然也可以直接跳过这一步。 + 再接下來是建立可以在終端(命令列)中使用PyCharm專案的啟動指令碼,當然也可以直接跳過這一步。 ![](./res/pycharm-create-launcher-script.png) -然后可以选择需要安装哪些插件,我们可以暂时什么都不安装等需要的时候再来决定。 +然後可以選擇需要安裝哪些外掛,我們可以暫時什麼都不安裝等需要的時候再來決定。 ![](./res/pycharm-plugins.png) -### 用PyCharm创建项目 +### 用PyCharm建立專案 -点击上图中的“Start using PyCharm”按钮就可以开始使用PyCharm啦,首先来到的是一个欢迎页,在欢迎页上我们可以选择“创建新项目”、“打开已有项目”和“从版本控制系统中检出项目”。 +點選上圖中的“Start using PyCharm”按鈕就可以開始使用PyCharm啦,首先來到的是一個歡迎頁,在歡迎頁上我們可以選擇“建立新專案”、“開啟已有專案”和“從版本控制系統中檢出專案”。 ![](./res/pycharm-welcome.png) -如果选择了“Create New Project”来创建新项目就会打一个创建项目的向导页。 +如果選擇了“Create New Project”來建立新專案就會打一個建立專案的嚮導頁。 ![](./res/pycharm-new-project.png) -在如上图所示的界面中,我们可以选择创建项目的模板,包括了纯Python项目、基于各种不同框架的Web项目、Web前端项目、跨平台项目等各种不同的项目模板。如果选择Python的项目,那么有一个非常重要的设定是选择“New environment…”(创建新的虚拟环境)还是使用“Existing Interpreter”(已经存在的解释器)。前者肯定是更好的选择,因为新的虚拟环境不会对系统环境变量中配置的Python环境造成影响,简单举个例子就是你在虚拟环境下安装或者更新了任何三方库,它并不会对系统原有的Python解释器造成任何的影响,但代价是需要额外的存储空间来建立这个虚拟环境。 +在如上圖所示的介面中,我們可以選擇建立專案的模板,包括了純Python專案、基於各種不同框架的Web專案、Web前端專案、跨平臺專案等各種不同的專案模板。如果選擇Python的專案,那麼有一個非常重要的設定是選擇“New environment…”(建立新的虛擬環境)還是使用“Existing Interpreter”(已經存在的直譯器)。前者肯定是更好的選擇,因為新的虛擬環境不會對系統環境變數中配置的Python環境造成影響,簡單舉個例子就是你在虛擬環境下安裝或者更新了任何三方庫,它並不會對系統原有的Python直譯器造成任何的影響,但代價是需要額外的儲存空間來建立這個虛擬環境。 -项目创建完成后就可以开始新建各种文件来书写Python代码了。 +專案建立完成後就可以開始新建各種檔案來書寫Python程式碼了。 ![](./res/pycharm-workspace.png) -在工作窗口的右键菜单中可以找到“Run ...”和“Debug ...”菜单项,通过这两个菜单项我们就可以运行和调试我们的代码啦。建议关注一下菜单栏中的“Code”、“Refactor”和“Tools”菜单,这里面为编写Python代码提供了很多有用的帮助,我们在后面也会陆续为大家介绍这些功能。 \ No newline at end of file +在工作視窗的右鍵選單中可以找到“Run ...”和“Debug ...”選單項,通過這兩個選單項我們就可以執行和除錯我們的程式碼啦。建議關注一下選單欄中的“Code”、“Refactor”和“Tools”選單,這裡面為編寫Python程式碼提供了很多有用的幫助,我們在後面也會陸續為大家介紹這些功能。 \ No newline at end of file diff --git "a/\347\224\250\345\207\275\346\225\260\350\277\230\346\230\257\347\224\250\345\244\215\346\235\202\347\232\204\350\241\250\350\276\276\345\274\217.md" "b/\347\224\250\345\207\275\346\225\260\350\277\230\346\230\257\347\224\250\345\244\215\346\235\202\347\232\204\350\241\250\350\276\276\345\274\217.md" index b84d8cf83..950a3d199 100644 --- "a/\347\224\250\345\207\275\346\225\260\350\277\230\346\230\257\347\224\250\345\244\215\346\235\202\347\232\204\350\241\250\350\276\276\345\274\217.md" +++ "b/\347\224\250\345\207\275\346\225\260\350\277\230\346\230\257\347\224\250\345\244\215\346\235\202\347\232\204\350\241\250\350\276\276\345\274\217.md" @@ -1,8 +1,8 @@ -## 要不要使用复杂表达式 +## 要不要使用複雜表示式 -Perl语言的原作者Larry Wall曾经说过,伟大的程序员都有三个优点:懒惰、暴躁和自负。乍一看这三个词语没有一个是褒义词,但在程序员的世界里,这三个词有不同的意义。首先,懒惰会促使程序员去写一些省事儿的程序来辅助自己或别人更好的完成工作,这样我们就无需做那些重复和繁琐的劳动;同理能够用3行代码解决的事情,我们也绝不会写出10行代码来。其次,暴躁会让程序员主动的去完成一些你还没有提出的工作,去优化自己的代码让它更有效率,能够3秒钟完成的任务,我们绝不能容忍1分钟的等待。最后,自负会促使程序员写出可靠无误的代码,我们写代码不是为了接受批评和指责,而是为了让其他人来膜拜。 +Perl語言的原作者Larry Wall曾經說過,偉大的程式設計師都有三個優點:懶惰、暴躁和自負。乍一看這三個詞語沒有一個是褒義詞,但在程式設計師的世界裡,這三個詞有不同的意義。首先,懶惰會促使程式設計師去寫一些省事兒的程式來輔助自己或別人更好的完成工作,這樣我們就無需做那些重複和繁瑣的勞動;同理能夠用3行程式碼解決的事情,我們也絕不會寫出10行程式碼來。其次,暴躁會讓程式設計師主動的去完成一些你還沒有提出的工作,去優化自己的程式碼讓它更有效率,能夠3秒鐘完成的任務,我們絕不能容忍1分鐘的等待。最後,自負會促使程式設計師寫出可靠無誤的程式碼,我們寫程式碼不是為了接受批評和指責,而是為了讓其他人來膜拜。 -那么接下来就有一个很有意思的问题值得探讨一下,我们需要一个程序从输入的三个数中找出最大的那个数。这个程序对任何会编程的人来说都是小菜一碟,甚至不会编程的人经过10分钟的学习也能搞定。下面是用来解决这个问题的Python代码。 +那麼接下來就有一個很有意思的問題值得探討一下,我們需要一個程式從輸入的三個數中找出最大的那個數。這個程式對任何會程式設計的人來說都是小菜一碟,甚至不會程式設計的人經過10分鐘的學習也能搞定。下面是用來解決這個問題的Python程式碼。 ```Python a = int(input('a = ')) @@ -18,7 +18,7 @@ print('The max is:', the_max) ``` -但是我们刚才说了,程序员都是懒惰的,很多程序员都会使用三元条件运算符来改写上面的代码。 +但是我們剛才說了,程式設計師都是懶惰的,很多程式設計師都會使用三元條件運算子來改寫上面的程式碼。 ```Python a = int(input('a = ')) @@ -30,7 +30,7 @@ print('The max is:', the_max) ``` -需要说明的是,Python在2.5版本以前是没有上面代码第4行和第5行中使用的三元条件运算符的,究其原因是Guido van Rossum(Python之父)认为三元条件运算符并不能帮助 Python变得更加简洁,于是那些习惯了在C/C++或Java中使用三元条件运算符(在这些语言中,三元条件运算符也称为“Elvis运算符”,因为`?:`放在一起很像著名摇滚歌手猫王Elvis的大背头)的程序员试着用`and`和`or`运算符的短路特性来模拟出三元操作符,于是在那个年代,上面的代码是这样写的。 +需要說明的是,Python在2.5版本以前是沒有上面程式碼第4行和第5行中使用的三元條件運算子的,究其原因是Guido van Rossum(Python之父)認為三元條件運算子並不能幫助 Python變得更加簡潔,於是那些習慣了在C/C++或Java中使用三元條件運算子(在這些語言中,三元條件運算子也稱為“Elvis運算子”,因為`?:`放在一起很像著名搖滾歌手貓王Elvis的大背頭)的程式設計師試著用`and`和`or`運算子的短路特性來模擬出三元操作符,於是在那個年代,上面的程式碼是這樣寫的。 ```Python a = int(input('a = ')) @@ -42,18 +42,18 @@ print('The max is:', the_max) ``` -但是这种做法在某些场景下是不能成立的,且看下面的代码。 +但是這種做法在某些場景下是不能成立的,且看下面的程式碼。 ```Python a = 0 b = -100 -# 下面的代码本来预期输出a的值,结果却得到了b的值 -# 因为a的值0在进行逻辑运算时会被视为False来处理 +# 下面的程式碼本來預期輸出a的值,結果卻得到了b的值 +# 因為a的值0在進行邏輯運算時會被視為False來處理 print(True and a or b) # print(a if True else b) ``` -所以在Python 2.5以后引入了三元条件运算符来避免上面的风险(上面代码被注释掉的最后一句话)。那么,问题又来了,上面的代码还可以写得更简短吗?答案是肯定的。 +所以在Python 2.5以後引入了三元條件運算子來避免上面的風險(上面程式碼被註釋掉的最後一句話)。那麼,問題又來了,上面的程式碼還可以寫得更簡短嗎?答案是肯定的。 ```Python a = int(input('a = ')) @@ -63,7 +63,7 @@ print('The max is:', (a if a > b else b) if (a if a > b else b) > c else c) ``` -但是,这样做真的好吗?如此复杂的表达式是不是让代码变得晦涩了很多呢?我们发现,在实际开发中很多开发者都喜欢过度的使用某种语言的特性或语法糖,于是简单的多行代码变成了复杂的单行表达式,这样做真的好吗?这个问题我也不止一次的问过自己,现在我能给出的答案是下面的代码,使用辅助函数。 +但是,這樣做真的好嗎?如此複雜的表示式是不是讓程式碼變得晦澀了很多呢?我們發現,在實際開發中很多開發者都喜歡過度的使用某種語言的特性或語法糖,於是簡單的多行程式碼變成了複雜的單行表示式,這樣做真的好嗎?這個問題我也不止一次的問過自己,現在我能給出的答案是下面的程式碼,使用輔助函式。 ```Python def the_max(x, y): @@ -77,9 +77,9 @@ print('The max is:', the_max(the_max(a, b), c)) ``` -上面的代码中,我定义了一个辅助函数`the_max`用来找出参数传入的两个值中较大的那一个,于是下面的输出语句可以通过两次调用`the_max`函数来找出三个数中的最大值,现在代码的可读性是不是好了很多。用辅助函数来替代复杂的表达式真的是一个不错的选择,关键是比较大小的逻辑转移到这个辅助函数后不仅可以反复调用它,而且还可以进行级联操作。 +上面的程式碼中,我定義了一個輔助函式`the_max`用來找出引數傳入的兩個值中較大的那一個,於是下面的輸出語句可以通過兩次呼叫`the_max`函式來找出三個數中的最大值,現在程式碼的可讀性是不是好了很多。用輔助函式來替代複雜的表示式真的是一個不錯的選擇,關鍵是比較大小的邏輯轉移到這個輔助函式後不僅可以反覆呼叫它,而且還可以進行級聯操作。 -当然,很多语言中比较大小的函数根本没有必要自己来实现(通常都是内置函数),Python也是如此。Python内置的max函数利用了Python对可变参数的支持,允许一次性传入多个值或者一个迭代器并找出那个最大值,所以上面讨论的问题在Python中也就是一句话的事,但是从复杂表达式到使用辅助函数简化复杂表达式这个思想是非常值得玩味的,所以分享出来跟大家做一个交流。 +當然,很多語言中比較大小的函式根本沒有必要自己來實現(通常都是內建函式),Python也是如此。Python內建的max函式利用了Python對可變引數的支援,允許一次性傳入多個值或者一個迭代器並找出那個最大值,所以上面討論的問題在Python中也就是一句話的事,但是從複雜表示式到使用輔助函式簡化複雜表示式這個思想是非常值得玩味的,所以分享出來跟大家做一個交流。 ```Python a = int(input('a = ')) diff --git "a/\351\202\243\344\272\233\345\271\264\346\210\221\344\273\254\350\270\251\350\277\207\347\232\204\351\202\243\344\272\233\345\235\221.md" "b/\351\202\243\344\272\233\345\271\264\346\210\221\344\273\254\350\270\251\350\277\207\347\232\204\351\202\243\344\272\233\345\235\221.md" index 50ef75e8b..78aa487fb 100644 --- "a/\351\202\243\344\272\233\345\271\264\346\210\221\344\273\254\350\270\251\350\277\207\347\232\204\351\202\243\344\272\233\345\235\221.md" +++ "b/\351\202\243\344\272\233\345\271\264\346\210\221\344\273\254\350\270\251\350\277\207\347\232\204\351\202\243\344\272\233\345\235\221.md" @@ -1,13 +1,13 @@ -## 那些年我们踩过的那些坑 +## 那些年我們踩過的那些坑 -### 坑01 - 整数比较的坑 +### 坑01 - 整數比較的坑 -在 Python 中一切都是对象,整数也是对象,在比较两个整数时有两个运算符`==`和`is`,它们的区别是: +在 Python 中一切都是物件,整數也是物件,在比較兩個整數時有兩個運算子`==`和`is`,它們的區別是: -- `is`比较的是两个整数对象的id值是否相等,也就是比较两个引用是否代表了内存中同一个地址。 -- `==`比较的是两个整数对象的内容是否相等,使用`==`时其实是调用了对象的`__eq__()`方法。 +- `is`比較的是兩個整數物件的id值是否相等,也就是比較兩個引用是否代表了記憶體中同一個地址。 +- `==`比較的是兩個整數物件的內容是否相等,使用`==`時其實是呼叫了物件的`__eq__()`方法。 -知道了`is`和`==`的区别之后,我们可以来看看下面的代码,了解Python中整数比较有哪些坑: +知道了`is`和`==`的區別之後,我們可以來看看下面的程式碼,瞭解Python中整數比較有哪些坑: ```Python def main(): @@ -37,11 +37,11 @@ if __name__ == '__main__': ``` -上面代码的部分运行结果如下图所示,出现这个结果的原因是Python出于对性能的考虑所做的一项优化。对于整数对象,Python把一些频繁使用的整数对象缓存起来,保存到一个叫`small_ints`的链表中,在Python的整个生命周期内,任何需要引用这些整数对象的地方,都不再重新创建新的对象,而是直接引用缓存中的对象。Python把频繁使用的整数对象的值定在[-5, 256]这个区间,如果需要这个范围的整数,就直接从`small_ints`中获取引用而不是临时创建新的对象。因为大于256或小于-5的整数不在该范围之内,所以就算两个整数的值是一样,但它们是不同的对象。 +上面程式碼的部分執行結果如下圖所示,出現這個結果的原因是Python出於對效能的考慮所做的一項優化。對於整數物件,Python把一些頻繁使用的整數物件快取起來,儲存到一個叫`small_ints`的連結串列中,在Python的整個生命週期內,任何需要引用這些整數物件的地方,都不再重新建立新的物件,而是直接引用快取中的物件。Python把頻繁使用的整數物件的值定在[-5, 256]這個區間,如果需要這個範圍的整數,就直接從`small_ints`中獲取引用而不是臨時建立新的物件。因為大於256或小於-5的整數不在該範圍之內,所以就算兩個整數的值是一樣,但它們是不同的物件。 ![](./res/int-is-comparation.png) -当然仅仅如此这个坑就不值一提了,如果你理解了上面的规则,我们就再看看下面的代码。 +當然僅僅如此這個坑就不值一提了,如果你理解了上面的規則,我們就再看看下面的程式碼。 ```Python import dis @@ -61,8 +61,8 @@ if __name__ == "__main__": ``` -程序的执行结果已经用注释写在代码上了。够坑吧!看上去`a`、`b`和`c`的值都是一样的,但是`is`运算的结果却不一样。为什么会出现这样的结果,首先我们来说说Python程序中的代码块。所谓代码块是程序的一个最小的基本执行单位,一个模块文件、一个函数体、一个类、交互式命令中的单行代码都叫做一个代码块。上面的代码由两个代码块构成,`a = 257`是一个代码块,`main`函数是另外一个代码块。Python内部为了进一步提高性能,凡是在一个代码块中创建的整数对象,如果值不在`small_ints`缓存范围之内,但在同一个代码块中已经存在一个值与其相同的整数对象了,那么就直接引用该对象,否则创建一个新的对象出来,这条规则对不在`small_ints`范围的负数并不适用,对负数值浮点数也不适用,但对非负浮点数和字符串都是适用的,这一点读者可以自行证明。所以 `b is c`返回了`True`,而`a`和`b`不在同一个代码块中,虽然值都是257,但却是两个不同的对象,`is`运算的结果自然是`False`了。 -为了验证刚刚的结论,我们可以借用`dis`模块(听名字就知道是进行反汇编的模块)从字节码的角度来看看这段代码。如果不理解什么是字节码,可以先看看[《谈谈 Python 程序的运行原理》]((http://www.cnblogs.com/restran/p/4903056.html))这篇文章。可以先用`import dis`导入`dis`模块并按照如下所示的方式修改代码。 +程式的執行結果已經用註釋寫在程式碼上了。夠坑吧!看上去`a`、`b`和`c`的值都是一樣的,但是`is`運算的結果卻不一樣。為什麼會出現這樣的結果,首先我們來說說Python程式中的程式碼塊。所謂程式碼塊是程式的一個最小的基本執行單位,一個模組檔案、一個函式體、一個類、互動式命令中的單行程式碼都叫做一個程式碼塊。上面的程式碼由兩個程式碼塊構成,`a = 257`是一個程式碼塊,`main`函式是另外一個程式碼塊。Python內部為了進一步提高效能,凡是在一個程式碼塊中建立的整數物件,如果值不在`small_ints`快取範圍之內,但在同一個程式碼塊中已經存在一個值與其相同的整數物件了,那麼就直接引用該物件,否則建立一個新的物件出來,這條規則對不在`small_ints`範圍的負數並不適用,對負數值浮點數也不適用,但對非負浮點數和字串都是適用的,這一點讀者可以自行證明。所以 `b is c`返回了`True`,而`a`和`b`不在同一個程式碼塊中,雖然值都是257,但卻是兩個不同的物件,`is`運算的結果自然是`False`了。 +為了驗證剛剛的結論,我們可以借用`dis`模組(聽名字就知道是進行反彙編的模組)從位元組碼的角度來看看這段程式碼。如果不理解什麼是位元組碼,可以先看看[《談談 Python 程式的執行原理》]((http://www.cnblogs.com/restran/p/4903056.html))這篇文章。可以先用`import dis`匯入`dis`模組並按照如下所示的方式修改程式碼。 ```Python if __name__ == "__main__": @@ -71,23 +71,23 @@ if __name__ == "__main__": ``` -代码的执行结果如下图所示。可以看出代码第6行和第7行,也就是`main`函数中的257是从同一个位置加载的,因此是同一个对象;而代码第9行的`a`明显是从不同的地方加载的,因此引用的是不同的对象。 +程式碼的執行結果如下圖所示。可以看出程式碼第6行和第7行,也就是`main`函式中的257是從同一個位置載入的,因此是同一個物件;而程式碼第9行的`a`明顯是從不同的地方載入的,因此引用的是不同的物件。 ![](./res/result-of-dis.png) -如果还想对这个问题进行进一步深挖,推荐大家阅读[《Python整数对象实现原理》](https://foofish.net/python_int_implement.html)这篇文章。 +如果還想對這個問題進行進一步深挖,推薦大家閱讀[《Python整數物件實現原理》](https://foofish.net/python_int_implement.html)這篇文章。 -### 坑02 - 嵌套列表的坑 +### 坑02 - 巢狀列表的坑 -Python中有一种内置的数据类型叫列表,它是一种容器,可以用来承载其他的对象(准确的说是其他对象的引用),列表中的对象可以称为列表的元素,很明显我们可以把列表作为列表中的元素,这就是所谓的嵌套列表。嵌套列表可以模拟出现实中的表格、矩阵、2D游戏的地图(如植物大战僵尸的花园)、棋盘(如国际象棋、黑白棋)等。但是在使用嵌套的列表时要小心,否则很可能遭遇非常尴尬的情况,下面是一个小例子。 +Python中有一種內建的資料型別叫列表,它是一種容器,可以用來承載其他的物件(準確的說是其他物件的引用),列表中的物件可以稱為列表的元素,很明顯我們可以把列表作為列表中的元素,這就是所謂的巢狀列表。巢狀列表可以模擬出現實中的表格、矩陣、2D遊戲的地圖(如植物大戰殭屍的花園)、棋盤(如國際象棋、黑白棋)等。但是在使用巢狀的列表時要小心,否則很可能遭遇非常尷尬的情況,下面是一個小例子。 ```Python def main(): - names = ['关羽', '张飞', '赵云', '马超', '黄忠'] - subjs = ['语文', '数学', '英语'] + names = ['關羽', '張飛', '趙雲', '馬超', '黃忠'] + subjs = ['語文', '數學', '英語'] scores = [[0] * 3] * 5 for row, name in enumerate(names): - print('请输入%s的成绩' % name) + print('請輸入%s的成績' % name) for col, subj in enumerate(subjs): scores[row][col] = float(input(subj + ': ')) print(scores) @@ -98,24 +98,24 @@ if __name__ == '__main__': ``` -我们希望录入5个学生3门课程的成绩,于是定义了一个有5个元素的列表,而列表中的每个元素又是一个由3个元素构成的列表,这样一个列表的列表刚好跟一个表格是一致的,相当于有5行3列,接下来我们通过嵌套的for-in循环输入每个学生3门课程的成绩。程序执行完成后我们发现,每个学生3门课程的成绩是一模一样的,而且就是最后录入的那个学生的成绩。 +我們希望錄入5個學生3門課程的成績,於是定義了一個有5個元素的列表,而列表中的每個元素又是一個由3個元素構成的列表,這樣一個列表的列表剛好跟一個表格是一致的,相當於有5行3列,接下來我們通過巢狀的for-in迴圈輸入每個學生3門課程的成績。程式執行完成後我們發現,每個學生3門課程的成績是一模一樣的,而且就是最後錄入的那個學生的成績。 -要想把这个坑填平,我们首先要区分对象和对象的引用这两个概念,而要区分这两个概念,还得先说说内存中的栈和堆。我们经常会听人说起“堆栈”这个词,但实际上“堆”和“栈”是两个不同的概念。众所周知,一个程序运行时需要占用一些内存空间来存储数据和代码,那么这些内存从逻辑上又可以做进一步的划分。对底层语言(如C语言)有所了解的程序大都知道,程序中可以使用的内存从逻辑上可以为五个部分,按照地址从高到低依次是:栈(stack)、堆(heap)、数据段(data segment)、只读数据段(static area)和代码段(code segment)。其中,栈用来存储局部、临时变量,以及函数调用时保存现场和恢复现场需要用到的数据,这部分内存在代码块开始执行时自动分配,代码块执行结束时自动释放,通常由编译器自动管理;堆的大小不固定,可以动态的分配和回收,因此如果程序中有大量的数据需要处理,这些数据通常都放在堆上,如果堆空间没有正确的被释放会引发内存泄露的问题,而像Python、Java等编程语言都使用了垃圾回收机制来实现自动化的内存管理(自动回收不再使用的堆空间)。所以下面的代码中,变量`a`并不是真正的对象,它是对象的引用,相当于记录了对象在堆空间的地址,通过这个地址我们可以访问到对应的对象;同理,变量`b`是列表容器的引用,它引用了堆空间上的列表容器,而列表容器中并没有保存真正的对象,它保存的也仅仅是对象的引用。 +要想把這個坑填平,我們首先要區分物件和物件的引用這兩個概念,而要區分這兩個概念,還得先說說記憶體中的棧和堆。我們經常會聽人說起“堆疊”這個詞,但實際上“堆”和“棧”是兩個不同的概念。眾所周知,一個程式執行時需要佔用一些記憶體空間來儲存資料和程式碼,那麼這些記憶體從邏輯上又可以做進一步的劃分。對底層語言(如C語言)有所瞭解的程式大都知道,程式中可以使用的記憶體從邏輯上可以為五個部分,按照地址從高到低依次是:棧(stack)、堆(heap)、資料段(data segment)、只讀資料段(static area)和程式碼段(code segment)。其中,棧用來儲存區域性、臨時變數,以及函式呼叫時儲存現場和恢復現場需要用到的資料,這部分記憶體在程式碼塊開始執行時自動分配,程式碼塊執行結束時自動釋放,通常由編譯器自動管理;堆的大小不固定,可以動態的分配和回收,因此如果程式中有大量的資料需要處理,這些資料通常都放在堆上,如果堆空間沒有正確的被釋放會引發記憶體洩露的問題,而像Python、Java等程式語言都使用了垃圾回收機制來實現自動化的記憶體管理(自動回收不再使用的堆空間)。所以下面的程式碼中,變數`a`並不是真正的物件,它是物件的引用,相當於記錄了物件在堆空間的地址,通過這個地址我們可以訪問到對應的物件;同理,變數`b`是列表容器的引用,它引用了堆空間上的列表容器,而列表容器中並沒有儲存真正的物件,它儲存的也僅僅是物件的引用。 ```Python a = object() b = ['apple', 'pitaya', 'grape'] ``` -知道了这一点,我们可以回过头看看刚才的程序,我们对列表进行`[[0] * 3] * 5`操作时,仅仅是将`[0, 0, 0]`这个列表的地址进行了复制,并没有创建新的列表对象,所以容器中虽然有5个元素,但是这5个元素引用了同一个列表对象,这一点可以通过`id`函数检查`scores[0]`和`scores[1]`的地址得到证实。所以正确的代码应该按照如下的方式进行修改。 +知道了這一點,我們可以回過頭看看剛才的程式,我們對列表進行`[[0] * 3] * 5`操作時,僅僅是將`[0, 0, 0]`這個列表的地址進行了複製,並沒有建立新的列表物件,所以容器中雖然有5個元素,但是這5個元素引用了同一個列表物件,這一點可以通過`id`函式檢查`scores[0]`和`scores[1]`的地址得到證實。所以正確的程式碼應該按照如下的方式進行修改。 ```Python def main(): - names = ['关羽', '张飞', '赵云', '马超', '黄忠'] - subjs = ['语文', '数学', '英语'] + names = ['關羽', '張飛', '趙雲', '馬超', '黃忠'] + subjs = ['語文', '數學', '英語'] scores = [[]] * 5 for row, name in enumerate(names): - print('请输入%s的成绩' % name) + print('請輸入%s的成績' % name) scores[row] = [0] * 3 for col, subj in enumerate(subjs): scores[row][col] = float(input(subj + ': ')) @@ -131,11 +131,11 @@ if __name__ == '__main__': ```Python def main(): - names = ['关羽', '张飞', '赵云', '马超', '黄忠'] - subjs = ['语文', '数学', '英语'] + names = ['關羽', '張飛', '趙雲', '馬超', '黃忠'] + subjs = ['語文', '數學', '英語'] scores = [[0] * 3 for _ in range(5)] for row, name in enumerate(names): - print('请输入%s的成绩' % name) + print('請輸入%s的成績' % name) scores[row] = [0] * 3 for col, subj in enumerate(subjs): scores[row][col] = float(input(subj + ': ')) @@ -147,15 +147,15 @@ if __name__ == '__main__': ``` -如果对内存的使用不是很理解,可以看看[PythonTutor网站](http://www.pythontutor.com/)上提供的代码可视化执行功能,通过可视化执行,我们可以看到内存是如何分配的,从而避免在使用嵌套列表或者复制对象时可能遇到的坑。 +如果對記憶體的使用不是很理解,可以看看[PythonTutor網站](http://www.pythontutor.com/)上提供的程式碼視覺化執行功能,通過視覺化執行,我們可以看到記憶體是如何分配的,從而避免在使用巢狀列表或者複製物件時可能遇到的坑。 ![](./res/python-tutor-visualize.png) ![](./res/python-tutor-visualize2.png) -### 坑03 - 访问修饰符的坑 +### 坑03 - 訪問修飾符的坑 -用Python做过面向对象编程的人都知道,Python的类提供了两种访问控制权限,一种是公开,一种是私有(在属性或方法前加上双下划线)。而用惯了Java或C#这类编程语言的人都知道,类中的属性(数据抽象)通常都是私有的,其目的是为了将数据保护起来;而类中的方法(行为抽象)通常都是公开的,因为方法是对象向外界提供的服务。但是Python并没有从语法层面确保私有成员的私密性,因为它只是对类中所谓的私有成员进行了命名的变换,如果知道命名的规则照样可以直接访问私有成员,请看下面的代码。 +用Python做過面向物件程式設計的人都知道,Python的類提供了兩種訪問控制權限,一種是公開,一種是私有(在屬性或方法前加上雙下劃線)。而用慣了Java或C#這類程式語言的人都知道,類中的屬性(資料抽象)通常都是私有的,其目的是為了將資料保護起來;而類中的方法(行為抽象)通常都是公開的,因為方法是物件向外界提供的服務。但是Python並沒有從語法層面確保私有成員的私密性,因為它只是對類中所謂的私有成員進行了命名的變換,如果知道命名的規則照樣可以直接訪問私有成員,請看下面的程式碼。 ```Python class Student(object): @@ -169,10 +169,10 @@ class Student(object): def main(): - stu = Student('骆昊', 38) + stu = Student('駱昊', 38) # 'Student' object has no attribute '__name' # print(stu.__name) - # 用下面的方式照样可以访问类中的私有成员 + # 用下面的方式照樣可以訪問類中的私有成員 print(stu._Student__name) print(stu._Student__age) @@ -182,10 +182,10 @@ if __name__ == '__main__': ``` -Python为什么要做出这样的设定呢?用一句广为流传的格言来解释这个问题:“We are all consenting adults here”(我们都是成年人)。这句话表达了很多Python程序员的一个共同观点,那就是开放比封闭要好,我们应该自己对自己的行为负责而不是从语言层面来限制对数据或方法的访问。 +Python為什麼要做出這樣的設定呢?用一句廣為流傳的格言來解釋這個問題:“We are all consenting adults here”(我們都是成年人)。這句話表達了很多Python程式設計師的一個共同觀點,那就是開放比封閉要好,我們應該自己對自己的行為負責而不是從語言層面來限制對資料或方法的訪問。 -所以在Python中我们实在没有必要将类中的属性或方法用双下划线开头的命名处理成私有的成员,因为这并没有任何实际的意义。如果想对属性或方法进行保护,我们建议用单下划线开头的受保护成员,虽然它也不能真正保护这些属性或方法,但是它相当于给调用者一个暗示,让调用者知道这是不应该直接访问的属性或方法,而且这样做并不影响子类去继承这些东西。 +所以在Python中我們實在沒有必要將類中的屬性或方法用雙下劃線開頭的命名處理成私有的成員,因為這並沒有任何實際的意義。如果想對屬性或方法進行保護,我們建議用單下劃線開頭的受保護成員,雖然它也不能真正保護這些屬性或方法,但是它相當於給呼叫者一個暗示,讓呼叫者知道這是不應該直接訪問的屬性或方法,而且這樣做並不影響子類去繼承這些東西。 -需要提醒大家注意的是,Python类中的那些魔法方法,如\_\_str\_\_、\_\_repr\_\_等,这些方法并不是私有成员哦,虽然它们以双下划线开头,但是他们也是以双下划线结尾的,这种命名并不是私有成员的命名,这一点对初学者来说真的很坑。 +需要提醒大家注意的是,Python類中的那些魔法方法,如\_\_str\_\_、\_\_repr\_\_等,這些方法並不是私有成員哦,雖然它們以雙下劃線開頭,但是他們也是以雙下劃線結尾的,這種命名並不是私有成員的命名,這一點對初學者來說真的很坑。 -(未完待续) \ No newline at end of file +(未完待續) \ No newline at end of file From 127f7ea47730ec2adc8a940b40952ae531c91c8f Mon Sep 17 00:00:00 2001 From: ateliershen Date: Mon, 28 May 2018 21:45:24 +0800 Subject: [PATCH 2/3] =?UTF-8?q?Update=20and=20rename=20=E7=94=A8=E5=87=BD?= =?UTF-8?q?=E6=95=B0=E8=BF=98=E6=98=AF=E7=94=A8=E5=A4=8D=E6=9D=82=E7=9A=84?= =?UTF-8?q?=E8=A1=A8=E8=BE=BE=E5=BC=8F.md=20to=20=E7=94=A8=E5=87=BD?= =?UTF-8?q?=E6=95=B8=E9=82=84=E6=98=AF=E7=94=A8=E8=A4=87=E9=9B=9C=E7=9A=84?= =?UTF-8?q?=E8=A1=A8=E9=81=94=E5=BC=8F.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...7\232\204\350\241\250\351\201\224\345\274\217.md" | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) rename "\347\224\250\345\207\275\346\225\260\350\277\230\346\230\257\347\224\250\345\244\215\346\235\202\347\232\204\350\241\250\350\276\276\345\274\217.md" => "\347\224\250\345\207\275\346\225\270\351\202\204\346\230\257\347\224\250\350\244\207\351\233\234\347\232\204\350\241\250\351\201\224\345\274\217.md" (52%) diff --git "a/\347\224\250\345\207\275\346\225\260\350\277\230\346\230\257\347\224\250\345\244\215\346\235\202\347\232\204\350\241\250\350\276\276\345\274\217.md" "b/\347\224\250\345\207\275\346\225\270\351\202\204\346\230\257\347\224\250\350\244\207\351\233\234\347\232\204\350\241\250\351\201\224\345\274\217.md" similarity index 52% rename from "\347\224\250\345\207\275\346\225\260\350\277\230\346\230\257\347\224\250\345\244\215\346\235\202\347\232\204\350\241\250\350\276\276\345\274\217.md" rename to "\347\224\250\345\207\275\346\225\270\351\202\204\346\230\257\347\224\250\350\244\207\351\233\234\347\232\204\350\241\250\351\201\224\345\274\217.md" index 950a3d199..c47803d3e 100644 --- "a/\347\224\250\345\207\275\346\225\260\350\277\230\346\230\257\347\224\250\345\244\215\346\235\202\347\232\204\350\241\250\350\276\276\345\274\217.md" +++ "b/\347\224\250\345\207\275\346\225\270\351\202\204\346\230\257\347\224\250\350\244\207\351\233\234\347\232\204\350\241\250\351\201\224\345\274\217.md" @@ -1,8 +1,8 @@ -## 要不要使用複雜表示式 +## 要不要使用複雜表達式 -Perl語言的原作者Larry Wall曾經說過,偉大的程式設計師都有三個優點:懶惰、暴躁和自負。乍一看這三個詞語沒有一個是褒義詞,但在程式設計師的世界裡,這三個詞有不同的意義。首先,懶惰會促使程式設計師去寫一些省事兒的程式來輔助自己或別人更好的完成工作,這樣我們就無需做那些重複和繁瑣的勞動;同理能夠用3行程式碼解決的事情,我們也絕不會寫出10行程式碼來。其次,暴躁會讓程式設計師主動的去完成一些你還沒有提出的工作,去優化自己的程式碼讓它更有效率,能夠3秒鐘完成的任務,我們絕不能容忍1分鐘的等待。最後,自負會促使程式設計師寫出可靠無誤的程式碼,我們寫程式碼不是為了接受批評和指責,而是為了讓其他人來膜拜。 +Perl 語言的原作者 Larry Wall 曾經說過,偉大的程式設計師都有三個優點:懶惰、暴躁和自負。乍一看這三個詞語沒有一個是褒義詞,但在程式設計師的世界裡,這三個詞有不同的意義。首先,懶惰會促使程式設計師去寫一些省事兒的程式來輔助自己或別人更好的完成工作,這樣我們就無需做那些重複和繁瑣的勞動;同理能夠用 3 行程式碼解決的事情,我們也絕不會寫出 10 行程式碼來。其次,暴躁會讓程式設計師主動的去完成一些你還沒有提出的工作,去優化自己的程式碼讓它更有效率,能夠 3 秒鐘完成的任務,我們絕不能容忍1分鐘的等待。最後,自負會促使程式設計師寫出可靠無誤的程式碼,我們寫程式碼不是為了接受批評和指責,而是為了讓其他人來膜拜。 -那麼接下來就有一個很有意思的問題值得探討一下,我們需要一個程式從輸入的三個數中找出最大的那個數。這個程式對任何會程式設計的人來說都是小菜一碟,甚至不會程式設計的人經過10分鐘的學習也能搞定。下面是用來解決這個問題的Python程式碼。 +那麼接下來就有一個很有意思的問題值得探討一下,我們需要一個程式從輸入的三個數中找出最大的那個數。這個程式對任何會程式設計的人來說都是小菜一碟,甚至不會程式設計的人經過 10 分鐘的學習也能搞定。下面是用來解決這個問題的 Python 程式碼。 ```Python a = int(input('a = ')) @@ -30,7 +30,7 @@ print('The max is:', the_max) ``` -需要說明的是,Python在2.5版本以前是沒有上面程式碼第4行和第5行中使用的三元條件運算子的,究其原因是Guido van Rossum(Python之父)認為三元條件運算子並不能幫助 Python變得更加簡潔,於是那些習慣了在C/C++或Java中使用三元條件運算子(在這些語言中,三元條件運算子也稱為“Elvis運算子”,因為`?:`放在一起很像著名搖滾歌手貓王Elvis的大背頭)的程式設計師試著用`and`和`or`運算子的短路特性來模擬出三元操作符,於是在那個年代,上面的程式碼是這樣寫的。 +需要說明的是,Python 在 2.5 版本以前是沒有上面程式碼第4行和第5行中使用的三元條件運算子的,究其原因是 Guido van Rossum(Python 之父)認為三元條件運算子並不能幫助 Python 變得更加簡潔,於是那些習慣了在 C/C++ 或 Java 中使用三元條件運算子(在這些語言中,三元條件運算子也稱為「Elvis運算子」,因為`?:`放在一起很像著名搖滾歌手貓王 Elvis 的大背頭)的程式設計師試著用`and`和`or`運算子的短路特性來模擬出三元操作符,於是在那個年代,上面的程式碼是這樣寫的。 ```Python a = int(input('a = ')) @@ -53,7 +53,7 @@ print(True and a or b) # print(a if True else b) ``` -所以在Python 2.5以後引入了三元條件運算子來避免上面的風險(上面程式碼被註釋掉的最後一句話)。那麼,問題又來了,上面的程式碼還可以寫得更簡短嗎?答案是肯定的。 +所以在 Python 2.5 以後引入了三元條件運算子來避免上面的風險(上面程式碼被註釋掉的最後一句話)。那麼,問題又來了,上面的程式碼還可以寫得更簡短嗎?答案是肯定的。 ```Python a = int(input('a = ')) @@ -79,7 +79,7 @@ print('The max is:', the_max(the_max(a, b), c)) 上面的程式碼中,我定義了一個輔助函式`the_max`用來找出引數傳入的兩個值中較大的那一個,於是下面的輸出語句可以通過兩次呼叫`the_max`函式來找出三個數中的最大值,現在程式碼的可讀性是不是好了很多。用輔助函式來替代複雜的表示式真的是一個不錯的選擇,關鍵是比較大小的邏輯轉移到這個輔助函式後不僅可以反覆呼叫它,而且還可以進行級聯操作。 -當然,很多語言中比較大小的函式根本沒有必要自己來實現(通常都是內建函式),Python也是如此。Python內建的max函式利用了Python對可變引數的支援,允許一次性傳入多個值或者一個迭代器並找出那個最大值,所以上面討論的問題在Python中也就是一句話的事,但是從複雜表示式到使用輔助函式簡化複雜表示式這個思想是非常值得玩味的,所以分享出來跟大家做一個交流。 +當然,很多語言中比較大小的函式根本沒有必要自己來實現(通常都是內建函式),Python 也是如此。Python 內建的 max 函式利用了 Python 對可變引數的支援,允許一次性傳入多個值或者一個迭代器並找出那個最大值,所以上面討論的問題在 Python 中也就是一句話的事,但是從複雜表示式到使用輔助函式簡化複雜表示式這個思想是非常值得玩味的,所以分享出來跟大家做一個交流。 ```Python a = int(input('a = ')) From 94cdea227a555da8b63118cc994f4bdffbcf25a0 Mon Sep 17 00:00:00 2001 From: ateliershen Date: Mon, 28 May 2018 21:54:05 +0800 Subject: [PATCH 3/3] =?UTF-8?q?Day01-15=20=E6=AA=94=E5=90=8D=E4=BF=AE?= =?UTF-8?q?=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Day01/\345\210\235\350\255\230Python.md" | 0 .../\350\252\236\350\250\200\345\205\203\347\264\240.md" | 0 .../\345\210\206\346\224\257\347\265\220\346\247\213.md" | 0 .../\345\276\252\347\222\260\347\265\220\346\247\213.md" | 0 ...275\347\265\220\345\222\214\347\267\264\347\277\222.md" | 0 ...241\345\241\212\347\232\204\344\275\277\347\224\250.md" | 0 ...250\346\225\270\346\223\232\347\265\220\346\247\213.md" | 0 ...241\347\267\250\347\250\213\345\237\272\347\244\216.md" | 0 ...207\344\273\266\345\222\214\347\225\260\345\270\270.md" | 0 "Day01-15/Day11/\350\207\264\346\251\241\346\240\221.txt" | 7 ------- "Day01-15/Day11/\350\207\264\346\251\241\346\250\271.txt" | 7 +++++++ ...262\347\250\213\345\222\214\347\267\232\347\250\213.md" | 0 ...241\347\267\250\347\250\213\345\205\245\351\226\200.md" | 0 ...241\346\207\211\347\224\250\351\226\213\347\231\274.md" | 0 14 files changed, 7 insertions(+), 7 deletions(-) rename "Day01-15/Day01/\345\210\235\350\257\206Python.md" => "Day01-15/Day01/\345\210\235\350\255\230Python.md" (100%) rename "Day01-15/Day02/\350\257\255\350\250\200\345\205\203\347\264\240.md" => "Day01-15/Day02/\350\252\236\350\250\200\345\205\203\347\264\240.md" (100%) rename "Day01-15/Day03/\345\210\206\346\224\257\347\273\223\346\236\204.md" => "Day01-15/Day03/\345\210\206\346\224\257\347\265\220\346\247\213.md" (100%) rename "Day01-15/Day04/\345\276\252\347\216\257\347\273\223\346\236\204.md" => "Day01-15/Day04/\345\276\252\347\222\260\347\265\220\346\247\213.md" (100%) rename "Day01-15/Day05/\346\200\273\347\273\223\345\222\214\347\273\203\344\271\240.md" => "Day01-15/Day05/\347\270\275\347\265\220\345\222\214\347\267\264\347\277\222.md" (100%) rename "Day01-15/Day06/\345\207\275\346\225\260\345\222\214\346\250\241\345\235\227\347\232\204\344\275\277\347\224\250.md" => "Day01-15/Day06/\345\207\275\346\225\270\345\222\214\346\250\241\345\241\212\347\232\204\344\275\277\347\224\250.md" (100%) rename "Day01-15/Day07/\345\255\227\347\254\246\344\270\262\345\222\214\345\270\270\347\224\250\346\225\260\346\215\256\347\273\223\346\236\204.md" => "Day01-15/Day07/\345\255\227\347\254\246\344\270\262\345\222\214\345\270\270\347\224\250\346\225\270\346\223\232\347\265\220\346\247\213.md" (100%) rename "Day01-15/Day08/\351\235\242\345\220\221\345\257\271\350\261\241\347\274\226\347\250\213\345\237\272\347\241\200.md" => "Day01-15/Day08/\351\235\242\345\220\221\345\260\215\350\261\241\347\267\250\347\250\213\345\237\272\347\244\216.md" (100%) rename "Day01-15/Day11/\346\226\207\344\273\266\345\222\214\345\274\202\345\270\270.md" => "Day01-15/Day11/\346\226\207\344\273\266\345\222\214\347\225\260\345\270\270.md" (100%) delete mode 100644 "Day01-15/Day11/\350\207\264\346\251\241\346\240\221.txt" create mode 100644 "Day01-15/Day11/\350\207\264\346\251\241\346\250\271.txt" rename "Day01-15/Day13/\350\277\233\347\250\213\345\222\214\347\272\277\347\250\213.md" => "Day01-15/Day13/\351\200\262\347\250\213\345\222\214\347\267\232\347\250\213.md" (100%) rename "Day01-15/Day14/\347\275\221\347\273\234\347\274\226\347\250\213\345\205\245\351\227\250.md" => "Day01-15/Day14/\347\266\262\347\265\241\347\267\250\347\250\213\345\205\245\351\226\200.md" (100%) rename "Day01-15/Day15/\347\275\221\347\273\234\345\272\224\347\224\250\345\274\200\345\217\221.md" => "Day01-15/Day15/\347\266\262\347\265\241\346\207\211\347\224\250\351\226\213\347\231\274.md" (100%) diff --git "a/Day01-15/Day01/\345\210\235\350\257\206Python.md" "b/Day01-15/Day01/\345\210\235\350\255\230Python.md" similarity index 100% rename from "Day01-15/Day01/\345\210\235\350\257\206Python.md" rename to "Day01-15/Day01/\345\210\235\350\255\230Python.md" diff --git "a/Day01-15/Day02/\350\257\255\350\250\200\345\205\203\347\264\240.md" "b/Day01-15/Day02/\350\252\236\350\250\200\345\205\203\347\264\240.md" similarity index 100% rename from "Day01-15/Day02/\350\257\255\350\250\200\345\205\203\347\264\240.md" rename to "Day01-15/Day02/\350\252\236\350\250\200\345\205\203\347\264\240.md" diff --git "a/Day01-15/Day03/\345\210\206\346\224\257\347\273\223\346\236\204.md" "b/Day01-15/Day03/\345\210\206\346\224\257\347\265\220\346\247\213.md" similarity index 100% rename from "Day01-15/Day03/\345\210\206\346\224\257\347\273\223\346\236\204.md" rename to "Day01-15/Day03/\345\210\206\346\224\257\347\265\220\346\247\213.md" diff --git "a/Day01-15/Day04/\345\276\252\347\216\257\347\273\223\346\236\204.md" "b/Day01-15/Day04/\345\276\252\347\222\260\347\265\220\346\247\213.md" similarity index 100% rename from "Day01-15/Day04/\345\276\252\347\216\257\347\273\223\346\236\204.md" rename to "Day01-15/Day04/\345\276\252\347\222\260\347\265\220\346\247\213.md" diff --git "a/Day01-15/Day05/\346\200\273\347\273\223\345\222\214\347\273\203\344\271\240.md" "b/Day01-15/Day05/\347\270\275\347\265\220\345\222\214\347\267\264\347\277\222.md" similarity index 100% rename from "Day01-15/Day05/\346\200\273\347\273\223\345\222\214\347\273\203\344\271\240.md" rename to "Day01-15/Day05/\347\270\275\347\265\220\345\222\214\347\267\264\347\277\222.md" diff --git "a/Day01-15/Day06/\345\207\275\346\225\260\345\222\214\346\250\241\345\235\227\347\232\204\344\275\277\347\224\250.md" "b/Day01-15/Day06/\345\207\275\346\225\270\345\222\214\346\250\241\345\241\212\347\232\204\344\275\277\347\224\250.md" similarity index 100% rename from "Day01-15/Day06/\345\207\275\346\225\260\345\222\214\346\250\241\345\235\227\347\232\204\344\275\277\347\224\250.md" rename to "Day01-15/Day06/\345\207\275\346\225\270\345\222\214\346\250\241\345\241\212\347\232\204\344\275\277\347\224\250.md" diff --git "a/Day01-15/Day07/\345\255\227\347\254\246\344\270\262\345\222\214\345\270\270\347\224\250\346\225\260\346\215\256\347\273\223\346\236\204.md" "b/Day01-15/Day07/\345\255\227\347\254\246\344\270\262\345\222\214\345\270\270\347\224\250\346\225\270\346\223\232\347\265\220\346\247\213.md" similarity index 100% rename from "Day01-15/Day07/\345\255\227\347\254\246\344\270\262\345\222\214\345\270\270\347\224\250\346\225\260\346\215\256\347\273\223\346\236\204.md" rename to "Day01-15/Day07/\345\255\227\347\254\246\344\270\262\345\222\214\345\270\270\347\224\250\346\225\270\346\223\232\347\265\220\346\247\213.md" diff --git "a/Day01-15/Day08/\351\235\242\345\220\221\345\257\271\350\261\241\347\274\226\347\250\213\345\237\272\347\241\200.md" "b/Day01-15/Day08/\351\235\242\345\220\221\345\260\215\350\261\241\347\267\250\347\250\213\345\237\272\347\244\216.md" similarity index 100% rename from "Day01-15/Day08/\351\235\242\345\220\221\345\257\271\350\261\241\347\274\226\347\250\213\345\237\272\347\241\200.md" rename to "Day01-15/Day08/\351\235\242\345\220\221\345\260\215\350\261\241\347\267\250\347\250\213\345\237\272\347\244\216.md" diff --git "a/Day01-15/Day11/\346\226\207\344\273\266\345\222\214\345\274\202\345\270\270.md" "b/Day01-15/Day11/\346\226\207\344\273\266\345\222\214\347\225\260\345\270\270.md" similarity index 100% rename from "Day01-15/Day11/\346\226\207\344\273\266\345\222\214\345\274\202\345\270\270.md" rename to "Day01-15/Day11/\346\226\207\344\273\266\345\222\214\347\225\260\345\270\270.md" diff --git "a/Day01-15/Day11/\350\207\264\346\251\241\346\240\221.txt" "b/Day01-15/Day11/\350\207\264\346\251\241\346\240\221.txt" deleted file mode 100644 index 8380e7867..000000000 --- "a/Day01-15/Day11/\350\207\264\346\251\241\346\240\221.txt" +++ /dev/null @@ -1,7 +0,0 @@ -我如果爱你 -绝不学攀援的凌霄花 -借你的高枝炫耀自己 - -我如果爱你 -绝不学痴情的鸟儿 -为绿荫重复单调的歌曲 diff --git "a/Day01-15/Day11/\350\207\264\346\251\241\346\250\271.txt" "b/Day01-15/Day11/\350\207\264\346\251\241\346\250\271.txt" new file mode 100644 index 000000000..31372ae17 --- /dev/null +++ "b/Day01-15/Day11/\350\207\264\346\251\241\346\250\271.txt" @@ -0,0 +1,7 @@ +我如果愛你 +絕不學攀援的凌霄花 +借你的高枝炫耀自己 + +我如果愛你 +絕不學痴情的鳥兒 +為綠蔭重復單調的歌曲 diff --git "a/Day01-15/Day13/\350\277\233\347\250\213\345\222\214\347\272\277\347\250\213.md" "b/Day01-15/Day13/\351\200\262\347\250\213\345\222\214\347\267\232\347\250\213.md" similarity index 100% rename from "Day01-15/Day13/\350\277\233\347\250\213\345\222\214\347\272\277\347\250\213.md" rename to "Day01-15/Day13/\351\200\262\347\250\213\345\222\214\347\267\232\347\250\213.md" diff --git "a/Day01-15/Day14/\347\275\221\347\273\234\347\274\226\347\250\213\345\205\245\351\227\250.md" "b/Day01-15/Day14/\347\266\262\347\265\241\347\267\250\347\250\213\345\205\245\351\226\200.md" similarity index 100% rename from "Day01-15/Day14/\347\275\221\347\273\234\347\274\226\347\250\213\345\205\245\351\227\250.md" rename to "Day01-15/Day14/\347\266\262\347\265\241\347\267\250\347\250\213\345\205\245\351\226\200.md" diff --git "a/Day01-15/Day15/\347\275\221\347\273\234\345\272\224\347\224\250\345\274\200\345\217\221.md" "b/Day01-15/Day15/\347\266\262\347\265\241\346\207\211\347\224\250\351\226\213\347\231\274.md" similarity index 100% rename from "Day01-15/Day15/\347\275\221\347\273\234\345\272\224\347\224\250\345\274\200\345\217\221.md" rename to "Day01-15/Day15/\347\266\262\347\265\241\346\207\211\347\224\250\351\226\213\347\231\274.md"