├── .gitignore ├── 01_base ├── 01_字符串类型_str.ipynb ├── 02_列表类型_list.ipynb ├── 03_元组类型_tuple.ipynb ├── 04_字典类型_dict.ipynb ├── 05_集合类型_set.ipynb ├── 06_条件判断_if.ipynb ├── 07_列表推导式.ipynb ├── 08_循环结构_loop.ipynb ├── 09_函数和模块.ipynb ├── 10_文件和异常.ipynb ├── 11_线程和进程.ipynb ├── README.md ├── multi_process.py └── 练习 │ ├── 九九乘法表.py │ ├── 冒泡排序.py │ ├── 商余问题.py │ ├── 大数分解.py │ ├── 平方和问题.py │ ├── 抽奖.py │ ├── 数羊问题.py │ ├── 时钟问题.py │ ├── 查无此人-列表版.py │ └── 每次抽走奇数.py ├── 02_advanced ├── 01_系统交互_os.ipynb ├── 02_数据库_sql.ipynb ├── 03_高阶函数.ipynb ├── 04_迭代器与生成器.ipynb ├── 05_上下文管理器.ipynb ├── 06_装饰器.ipynb ├── 07_面向对象编程.ipynb ├── README.md └── 练习 │ ├── TheStrayLibrary │ ├── README.md │ ├── StrayLibrary.py │ ├── StrayLibrary │ │ ├── 01.png │ │ ├── 02.png │ │ ├── 03.png │ │ └── book.db │ └── utils │ │ ├── __init__.py │ │ ├── database.py │ │ └── userthinker.py │ └── 类-图书馆系统.py ├── 03_data_science ├── 01_Numpy数组.ipynb ├── 02_Numpy索引.ipynb ├── 03_Numpy方法.ipynb ├── 04_Matpoltlib画图.ipynb ├── 05_SciPy统计分布.ipynb ├── 06_SciPy曲线拟合.ipynb ├── 07_Pandas数据类型.ipynb ├── 08_Pandas数据操作.ipynb ├── 09_Scikit-Learn分类.ipynb ├── 10_Scikit-Learn聚类.ipynb └── README.md ├── 04_flask ├── 01_Flask介绍.md ├── 02_Flask模板.md ├── 03_静态文件.md ├── 04_数据库.md ├── 05_模板优化.md ├── 06_表单.md ├── 07_用户认证.md ├── README.md ├── requirements.txt ├── watchlist │ ├── __init__.py │ ├── commands.py │ ├── errors.py │ ├── models.py │ ├── static │ │ ├── favicon.ico │ │ ├── images │ │ │ ├── avatar.png │ │ │ └── totoro.gif │ │ └── style.css │ ├── templates │ │ ├── base.html │ │ ├── edit.html │ │ ├── errors │ │ │ ├── 400.html │ │ │ ├── 404.html │ │ │ └── 500.html │ │ ├── index.html │ │ ├── login.html │ │ └── settings.html │ └── views.py └── wsgi.py ├── 05_spider ├── 01_爬虫介绍.ipynb ├── 02_网页解析和存储.ipynb └── README.md ├── 06_tool ├── ConvertedToPDF.py ├── Qr_Code.py ├── README.md ├── argparse_demo.py ├── click_demo.py ├── dp_money.py ├── greedy.py ├── logger.py ├── profile_demo.py ├── profiler工具.md ├── re_demo.py └── send_email.py ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── data └── numpy │ ├── C-067.txt │ ├── data.txt │ ├── datetime_data.txt │ ├── iris.data.txt │ └── special_data.txt ├── docs ├── flask_images │ ├── 2-1.png │ ├── 2-2.png │ ├── 3-1.png │ ├── 4-1.png │ ├── 6-1.png │ ├── 6-2.png │ ├── 6-3.png │ ├── 7-1.png │ ├── 7-2.png │ ├── 8-1.png │ ├── 8-2.png │ └── 8-3.png ├── imgs │ ├── ml_taxonomy.png │ ├── readme_img.png │ └── scipy-image.jpg ├── nlp_wechatgroup_erweima.png ├── xm_wechat_erweima.png └── xm_wechat_zhifu.png └── requirements.txt /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | env/ 12 | build/ 13 | develop-eggs/ 14 | dist/ 15 | downloads/ 16 | eggs/ 17 | .eggs/ 18 | lib/ 19 | lib64/ 20 | parts/ 21 | sdist/ 22 | var/ 23 | *.egg-info/ 24 | .installed.cfg 25 | *.egg 26 | 27 | # PyInstaller 28 | # Usually these files are written by a python script from a template 29 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 30 | *.manifest 31 | *.spec 32 | 33 | # Installer logs 34 | pip-log.txt 35 | pip-delete-this-directory.txt 36 | 37 | # Unit test / coverage reports 38 | htmlcov/ 39 | .tox/ 40 | .coverage 41 | .coverage.* 42 | .cache 43 | nosetests.xml 44 | coverage.xml 45 | *,cover 46 | .hypothesis/ 47 | 48 | # Translations 49 | *.mo 50 | *.pot 51 | 52 | # Django stuff: 53 | *.log 54 | local_settings.py 55 | 56 | # Flask stuff: 57 | instance/ 58 | .webassets-cache 59 | 60 | # Scrapy stuff: 61 | .scrapy 62 | 63 | # Sphinx documentation 64 | docs/_build/ 65 | 66 | # PyBuilder 67 | target/ 68 | 69 | # IPython Notebook 70 | .ipynb_checkpoints 71 | 72 | # pyenv 73 | .python-version 74 | 75 | # celery beat schedule file 76 | celerybeat-schedule 77 | 78 | # dotenv 79 | .env 80 | 81 | # virtualenv 82 | venv/ 83 | ENV/ 84 | 85 | # Spyder project settings 86 | .spyderproject 87 | 88 | # Rope project settings 89 | .ropeproject 90 | 91 | .idea 92 | 93 | .idea/* 94 | .idea/ -------------------------------------------------------------------------------- /01_base/01_字符串类型_str.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "| [01_base/01_字符串类型_str.ipynb](https://github.com/shibing624/python-tutorial/blob/master/01_base/01_字符串类型_str.ipynb) | Python字符串类型 |[Open In Colab](https://colab.research.google.com/github/shibing624/python-tutorial/blob/master/01_base/01_字符串类型_str.ipynb) |\n", 8 | "\n", 9 | "\n", 10 | "# 字符串" 11 | ] 12 | }, 13 | { 14 | "cell_type": "markdown", 15 | "metadata": {}, 16 | "source": [ 17 | "## 介绍字符串的索引" 18 | ] 19 | }, 20 | { 21 | "cell_type": "code", 22 | "execution_count": 64, 23 | "metadata": {}, 24 | "outputs": [ 25 | { 26 | "data": { 27 | "text/plain": [ 28 | "'g'" 29 | ] 30 | }, 31 | "execution_count": 64, 32 | "metadata": {}, 33 | "output_type": "execute_result" 34 | } 35 | ], 36 | "source": [ 37 | "# 字符串的索引\n", 38 | "s = 'good morning'\n", 39 | "# 查看类型 \n", 40 | "print(type(s))\n", 41 | "s[0] # g" 42 | ] 43 | }, 44 | { 45 | "cell_type": "code", 46 | "execution_count": 65, 47 | "metadata": {}, 48 | "outputs": [ 49 | { 50 | "data": { 51 | "text/plain": [ 52 | "'n'" 53 | ] 54 | }, 55 | "execution_count": 65, 56 | "metadata": {}, 57 | "output_type": "execute_result" 58 | } 59 | ], 60 | "source": [ 61 | "s[-2] # n" 62 | ] 63 | }, 64 | { 65 | "cell_type": "markdown", 66 | "metadata": {}, 67 | "source": [ 68 | "## 切分操作" 69 | ] 70 | }, 71 | { 72 | "cell_type": "markdown", 73 | "metadata": {}, 74 | "source": [ 75 | "\n", 76 | "\n", 77 | "分片用来从序列中提取出想要的子序列,其用法为:\n", 78 | "\n", 79 | "var[lower:upper:step]\n", 80 | "\n", 81 | "其范围包括 lower ,但不包括 upper ,即 [lower, upper),\n", 82 | "step 表示取值间隔大小,如果没有默认为1。\n" 83 | ] 84 | }, 85 | { 86 | "cell_type": "code", 87 | "execution_count": 66, 88 | "metadata": { 89 | "pycharm": { 90 | "name": "#%%\n" 91 | } 92 | }, 93 | "outputs": [ 94 | { 95 | "data": { 96 | "text/plain": [ 97 | "'ing'" 98 | ] 99 | }, 100 | "execution_count": 66, 101 | "metadata": {}, 102 | "output_type": "execute_result" 103 | } 104 | ], 105 | "source": [ 106 | "s[-3:] # ing" 107 | ] 108 | }, 109 | { 110 | "cell_type": "code", 111 | "execution_count": 67, 112 | "metadata": {}, 113 | "outputs": [ 114 | { 115 | "data": { 116 | "text/plain": [ 117 | "'good morn'" 118 | ] 119 | }, 120 | "execution_count": 67, 121 | "metadata": {}, 122 | "output_type": "execute_result" 123 | } 124 | ], 125 | "source": [ 126 | "s[:-3] # good morn" 127 | ] 128 | }, 129 | { 130 | "cell_type": "code", 131 | "execution_count": 68, 132 | "metadata": {}, 133 | "outputs": [ 134 | { 135 | "data": { 136 | "text/plain": [ 137 | "'good morning'" 138 | ] 139 | }, 140 | "execution_count": 68, 141 | "metadata": {}, 142 | "output_type": "execute_result" 143 | } 144 | ], 145 | "source": [ 146 | "s[:] # good morning" 147 | ] 148 | }, 149 | { 150 | "cell_type": "markdown", 151 | "metadata": {}, 152 | "source": [ 153 | "其他切分操作,练习:step" 154 | ] 155 | }, 156 | { 157 | "cell_type": "code", 158 | "execution_count": 69, 159 | "metadata": {}, 160 | "outputs": [ 161 | { 162 | "name": "stdout", 163 | "output_type": "stream", 164 | "text": [ 165 | "go onn\n", 166 | "gninrom doog\n", 167 | "good morning\n" 168 | ] 169 | } 170 | ], 171 | "source": [ 172 | "print(s[::2]) # go onn\n", 173 | "print(s[::-1]) # gninrom doog\n", 174 | "print(s[:100])" 175 | ] 176 | }, 177 | { 178 | "cell_type": "markdown", 179 | "metadata": {}, 180 | "source": [ 181 | "本节完。" 182 | ] 183 | }, 184 | { 185 | "cell_type": "code", 186 | "execution_count": null, 187 | "metadata": {}, 188 | "outputs": [], 189 | "source": [] 190 | } 191 | ], 192 | "metadata": { 193 | "kernelspec": { 194 | "display_name": "Python 3", 195 | "language": "python", 196 | "name": "python3" 197 | }, 198 | "language_info": { 199 | "codemirror_mode": { 200 | "name": "ipython", 201 | "version": 3 202 | }, 203 | "file_extension": ".py", 204 | "mimetype": "text/x-python", 205 | "name": "python", 206 | "nbconvert_exporter": "python", 207 | "pygments_lexer": "ipython3", 208 | "version": "3.8.8" 209 | } 210 | }, 211 | "nbformat": 4, 212 | "nbformat_minor": 1 213 | } -------------------------------------------------------------------------------- /01_base/03_元组类型_tuple.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "id": "ec58d47e", 6 | "metadata": {}, 7 | "source": [ 8 | "| [01_base/03_元组类型_tuple.ipynb](https://github.com/shibing624/python-tutorial/blob/master/01_base/03_元组类型_tuple.ipynb) | Python元组 |[Open In Colab](https://colab.research.google.com/github/shibing624/python-tutorial/blob/master/01_base/03_元组类型_tuple.ipynb) |\n", 9 | "\n", 10 | "\n", 11 | "# tuple\n", 12 | "\n", 13 | "与列表相似,元组tuple也是个有序序列,但是元组是不可变的,用()生成。" 14 | ] 15 | }, 16 | { 17 | "cell_type": "code", 18 | "execution_count": 53, 19 | "id": "7a258ee9", 20 | "metadata": {}, 21 | "outputs": [ 22 | { 23 | "name": "stdout", 24 | "output_type": "stream", 25 | "text": [ 26 | "(10, 11, 12, 13, 14)\n", 27 | "10\n", 28 | "(11, 12)\n" 29 | ] 30 | }, 31 | { 32 | "data": { 33 | "text/plain": [ 34 | "(11, 12)" 35 | ] 36 | }, 37 | "execution_count": 53, 38 | "metadata": {}, 39 | "output_type": "execute_result" 40 | } 41 | ], 42 | "source": [ 43 | "a = (10, 11, 12, 13, 14)\n", 44 | "print(a)\n", 45 | "\n", 46 | "# 可以索引,切片:\n", 47 | "c = a[0]\n", 48 | "print(c)\n", 49 | "\n", 50 | "c = a[1:3]\n", 51 | "print(c) # (11, 12)\n", 52 | "c" 53 | ] 54 | }, 55 | { 56 | "cell_type": "markdown", 57 | "id": "c19d19e8", 58 | "metadata": {}, 59 | "source": [ 60 | "\n", 61 | "单个元素的元组生成\n", 62 | "\n", 63 | "采用下列方式定义只有一个元素的元组:\n" 64 | ] 65 | }, 66 | { 67 | "cell_type": "code", 68 | "execution_count": 54, 69 | "id": "769295cf", 70 | "metadata": {}, 71 | "outputs": [ 72 | { 73 | "name": "stdout", 74 | "output_type": "stream", 75 | "text": [ 76 | "(10,)\n", 77 | "\n" 78 | ] 79 | } 80 | ], 81 | "source": [ 82 | "a = (10,)\n", 83 | "print(a)\n", 84 | "print(type(a)) # " 85 | ] 86 | }, 87 | { 88 | "cell_type": "code", 89 | "execution_count": 55, 90 | "id": "a5495d37", 91 | "metadata": {}, 92 | "outputs": [ 93 | { 94 | "name": "stdout", 95 | "output_type": "stream", 96 | "text": [ 97 | "(1, 2, 3)\n" 98 | ] 99 | } 100 | ], 101 | "source": [ 102 | "a = [1, 2, 3]\n", 103 | "b = tuple(a)\n", 104 | "print(b) # (1, 2, 3)" 105 | ] 106 | }, 107 | { 108 | "cell_type": "markdown", 109 | "id": "8925b5e3", 110 | "metadata": {}, 111 | "source": [ 112 | "由于元组是不可变的,所以只能有一些不可变的方法,\n", 113 | "\n", 114 | "例如计算元素个数 count 和元素位置 index ,用法与列表一样。" 115 | ] 116 | }, 117 | { 118 | "cell_type": "code", 119 | "execution_count": 56, 120 | "id": "4e531c58", 121 | "metadata": {}, 122 | "outputs": [ 123 | { 124 | "name": "stdout", 125 | "output_type": "stream", 126 | "text": [ 127 | "1\n", 128 | "2\n" 129 | ] 130 | } 131 | ], 132 | "source": [ 133 | "c = a.count(1)\n", 134 | "print(c) # 1\n", 135 | "\n", 136 | "c = a.index(3)\n", 137 | "print(c) # 索引位置为:2" 138 | ] 139 | }, 140 | { 141 | "cell_type": "markdown", 142 | "id": "dcabab23", 143 | "metadata": {}, 144 | "source": [ 145 | "本节完。" 146 | ] 147 | } 148 | ], 149 | "metadata": { 150 | "kernelspec": { 151 | "display_name": "Python 3", 152 | "language": "python", 153 | "name": "python3" 154 | }, 155 | "language_info": { 156 | "codemirror_mode": { 157 | "name": "ipython", 158 | "version": 3 159 | }, 160 | "file_extension": ".py", 161 | "mimetype": "text/x-python", 162 | "name": "python", 163 | "nbconvert_exporter": "python", 164 | "pygments_lexer": "ipython3", 165 | "version": "3.8.8" 166 | } 167 | }, 168 | "nbformat": 4, 169 | "nbformat_minor": 5 170 | } -------------------------------------------------------------------------------- /01_base/05_集合类型_set.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "id": "cf1244a8", 6 | "metadata": {}, 7 | "source": [ 8 | "| [01_base/05_集合类型_set.ipynb](https://github.com/shibing624/python-tutorial/blob/master/01_base/05_集合类型_set.ipynb) | Python集合 |[Open In Colab](https://colab.research.google.com/github/shibing624/python-tutorial/blob/master/01_base/05_集合类型_set.ipynb) |\n", 9 | "\n", 10 | "\n", 11 | "# 集合\n", 12 | "\n", 13 | "列表和字符串都是一种有序序列,而集合 set 是一种无序的序列。\n", 14 | "\n", 15 | "因为集合是无序的,所以当集合中存在两个同样的元素的时候,只会保存其中的一个(唯一性);\n", 16 | "同时为了确保其中不包含同样的元素,集合中放入的元素只能是不可变的对象(确定性)。" 17 | ] 18 | }, 19 | { 20 | "cell_type": "markdown", 21 | "id": "b7f713ae", 22 | "metadata": {}, 23 | "source": [ 24 | "## 创建集合" 25 | ] 26 | }, 27 | { 28 | "cell_type": "code", 29 | "execution_count": 41, 30 | "id": "39853c8a", 31 | "metadata": {}, 32 | "outputs": [ 33 | { 34 | "name": "stdout", 35 | "output_type": "stream", 36 | "text": [ 37 | "set()\n", 38 | "\n" 39 | ] 40 | } 41 | ], 42 | "source": [ 43 | "# 可以用set()函数来显示的生成空集合:\n", 44 | "a = set()\n", 45 | "print(a)\n", 46 | "print(type(a))" 47 | ] 48 | }, 49 | { 50 | "cell_type": "code", 51 | "execution_count": 42, 52 | "id": "0b0af1cb", 53 | "metadata": {}, 54 | "outputs": [ 55 | { 56 | "data": { 57 | "text/plain": [ 58 | "{1, 2, 3}" 59 | ] 60 | }, 61 | "execution_count": 42, 62 | "metadata": {}, 63 | "output_type": "execute_result" 64 | } 65 | ], 66 | "source": [ 67 | "# 使用一个列表来初始化一个集合:\n", 68 | "a = set([1, 2, 3, 1])\n", 69 | "a # 集合会自动去除重复元素 1。" 70 | ] 71 | }, 72 | { 73 | "cell_type": "code", 74 | "execution_count": 43, 75 | "id": "c56f8da2", 76 | "metadata": {}, 77 | "outputs": [ 78 | { 79 | "name": "stdout", 80 | "output_type": "stream", 81 | "text": [ 82 | "{1, 2, 3}\n" 83 | ] 84 | } 85 | ], 86 | "source": [ 87 | "# 集合中的元素是用大括号{}包含起来的,这意味着可以用{}的形式来创建集合:\n", 88 | "a = {1, 2, 3, 1}\n", 89 | "print(a) # {1, 2, 3}" 90 | ] 91 | }, 92 | { 93 | "cell_type": "code", 94 | "execution_count": 44, 95 | "id": "d28bb50e", 96 | "metadata": {}, 97 | "outputs": [ 98 | { 99 | "name": "stdout", 100 | "output_type": "stream", 101 | "text": [ 102 | "\n" 103 | ] 104 | } 105 | ], 106 | "source": [ 107 | "# 但是创建空集合的时候只能用set来创建,因为在Python中{}创建的是一个空的字典:\n", 108 | "s = {}\n", 109 | "print(type(s)) # " 110 | ] 111 | }, 112 | { 113 | "cell_type": "markdown", 114 | "id": "287f942d", 115 | "metadata": {}, 116 | "source": [ 117 | "## 集合操作" 118 | ] 119 | }, 120 | { 121 | "cell_type": "code", 122 | "execution_count": 45, 123 | "id": "f8657532", 124 | "metadata": {}, 125 | "outputs": [], 126 | "source": [ 127 | "a = {1, 2, 3, 4}\n", 128 | "b = {2, 3, 4, 5}" 129 | ] 130 | }, 131 | { 132 | "cell_type": "markdown", 133 | "id": "faeb338c", 134 | "metadata": {}, 135 | "source": [ 136 | "### 并\n", 137 | "\n", 138 | "两个集合的并,返回包含两个集合所有元素的集合(去除重复)。\n", 139 | "可以用方法 a.union(b) 或者操作 a | b 实现。" 140 | ] 141 | }, 142 | { 143 | "cell_type": "code", 144 | "execution_count": 46, 145 | "id": "192fde11", 146 | "metadata": {}, 147 | "outputs": [ 148 | { 149 | "name": "stdout", 150 | "output_type": "stream", 151 | "text": [ 152 | "{1, 2, 3, 4, 5}\n", 153 | "{1, 2, 3, 4, 5}\n" 154 | ] 155 | } 156 | ], 157 | "source": [ 158 | "c = a.union(b)\n", 159 | "print(c) # {1, 2, 3, 4, 5, 6}\n", 160 | "\n", 161 | "# 操作 a | b 实现\n", 162 | "d = a | b\n", 163 | "print(c)" 164 | ] 165 | }, 166 | { 167 | "cell_type": "code", 168 | "execution_count": 47, 169 | "id": "ff8c6139", 170 | "metadata": {}, 171 | "outputs": [ 172 | { 173 | "data": { 174 | "text/plain": [ 175 | "True" 176 | ] 177 | }, 178 | "execution_count": 47, 179 | "metadata": {}, 180 | "output_type": "execute_result" 181 | } 182 | ], 183 | "source": [ 184 | "c == d" 185 | ] 186 | }, 187 | { 188 | "cell_type": "markdown", 189 | "id": "832b1ab6", 190 | "metadata": {}, 191 | "source": [ 192 | "### 交\n", 193 | "两个集合的交,返回包含两个集合共有元素的集合。\n", 194 | "\n", 195 | "可以用方法 a.intersection(b) 或者操作 a & b 实现。" 196 | ] 197 | }, 198 | { 199 | "cell_type": "code", 200 | "execution_count": 48, 201 | "id": "4226876a", 202 | "metadata": {}, 203 | "outputs": [ 204 | { 205 | "name": "stdout", 206 | "output_type": "stream", 207 | "text": [ 208 | "{2, 3, 4}\n", 209 | "{2, 3, 4}\n" 210 | ] 211 | }, 212 | { 213 | "data": { 214 | "text/plain": [ 215 | "True" 216 | ] 217 | }, 218 | "execution_count": 48, 219 | "metadata": {}, 220 | "output_type": "execute_result" 221 | } 222 | ], 223 | "source": [ 224 | "c = a.intersection(b)\n", 225 | "print(c) # set([2, 3, 4])\n", 226 | "\n", 227 | "d = a & b\n", 228 | "print(d)\n", 229 | "\n", 230 | "c == d" 231 | ] 232 | }, 233 | { 234 | "cell_type": "markdown", 235 | "id": "8ec6059e", 236 | "metadata": {}, 237 | "source": [ 238 | "### 差\n", 239 | "a 和 b 的差集,返回只在 a 不在 b 的元素组成的集合。\n", 240 | "\n", 241 | "可以用方法 a.difference(b) 或者操作 a - b 实现。" 242 | ] 243 | }, 244 | { 245 | "cell_type": "code", 246 | "execution_count": 49, 247 | "id": "993870eb", 248 | "metadata": {}, 249 | "outputs": [ 250 | { 251 | "name": "stdout", 252 | "output_type": "stream", 253 | "text": [ 254 | "{1}\n", 255 | "{1}\n" 256 | ] 257 | } 258 | ], 259 | "source": [ 260 | "c = a.difference(b)\n", 261 | "print(c) # set([1])\n", 262 | "d = a - b\n", 263 | "print(d)" 264 | ] 265 | }, 266 | { 267 | "cell_type": "markdown", 268 | "id": "0d1ce4ce", 269 | "metadata": {}, 270 | "source": [ 271 | "### 对称差\n", 272 | "a 和b 的对称差集,返回在 a 或在 b 中,但是不同时在 a 和 b 中的元素组成的集合。\n", 273 | "\n", 274 | "可以用方法 a.symmetric_difference(b) 或者操作 a ^ b 实现(异或操作符)。" 275 | ] 276 | }, 277 | { 278 | "cell_type": "code", 279 | "execution_count": 50, 280 | "id": "b937bdf6", 281 | "metadata": {}, 282 | "outputs": [ 283 | { 284 | "name": "stdout", 285 | "output_type": "stream", 286 | "text": [ 287 | "{1, 5}\n", 288 | "{1, 5}\n" 289 | ] 290 | } 291 | ], 292 | "source": [ 293 | "c = a.symmetric_difference(b)\n", 294 | "print(c) # set([1, 5])\n", 295 | "\n", 296 | "d = a ^ b\n", 297 | "print(d)" 298 | ] 299 | }, 300 | { 301 | "cell_type": "markdown", 302 | "id": "fb47fd3a", 303 | "metadata": {}, 304 | "source": [ 305 | "### 包含关系\n", 306 | "\n", 307 | "要判断 b 是不是 a 的子集,可以用 b.issubset(a) 方法,\n", 308 | "或者更简单的用操作 b <= a :" 309 | ] 310 | }, 311 | { 312 | "cell_type": "code", 313 | "execution_count": 51, 314 | "id": "be34831c", 315 | "metadata": {}, 316 | "outputs": [ 317 | { 318 | "name": "stdout", 319 | "output_type": "stream", 320 | "text": [ 321 | "True\n", 322 | "True\n" 323 | ] 324 | } 325 | ], 326 | "source": [ 327 | "a = {1, 2, 3}\n", 328 | "b = {1, 2}\n", 329 | "\n", 330 | "c = b.issubset(a)\n", 331 | "print(c) # True\n", 332 | "\n", 333 | "d = (b <= a)\n", 334 | "print(d)" 335 | ] 336 | }, 337 | { 338 | "cell_type": "markdown", 339 | "id": "3f268645", 340 | "metadata": {}, 341 | "source": [ 342 | "也可以用 a.issuperset(b) 或者 a >= b 来判断:" 343 | ] 344 | }, 345 | { 346 | "cell_type": "code", 347 | "execution_count": 52, 348 | "id": "c2afbe0d", 349 | "metadata": {}, 350 | "outputs": [ 351 | { 352 | "name": "stdout", 353 | "output_type": "stream", 354 | "text": [ 355 | "True\n" 356 | ] 357 | } 358 | ], 359 | "source": [ 360 | "print(a >= b)" 361 | ] 362 | }, 363 | { 364 | "cell_type": "markdown", 365 | "id": "df54dce0", 366 | "metadata": {}, 367 | "source": [ 368 | "方法只能用来测试子集,但是操作符可以用来判断真子集:" 369 | ] 370 | }, 371 | { 372 | "cell_type": "code", 373 | "execution_count": 53, 374 | "id": "4aafbb05", 375 | "metadata": {}, 376 | "outputs": [ 377 | { 378 | "name": "stdout", 379 | "output_type": "stream", 380 | "text": [ 381 | "False\n", 382 | "True\n" 383 | ] 384 | } 385 | ], 386 | "source": [ 387 | "print(a < a) # False\n", 388 | "print(a <= a) # True" 389 | ] 390 | }, 391 | { 392 | "cell_type": "markdown", 393 | "id": "cdf443d9", 394 | "metadata": {}, 395 | "source": [ 396 | "## 集合方法\n", 397 | "\n", 398 | "### add 方法向集合添加单个元素\n", 399 | "\n", 400 | "跟列表的 append 方法类似,用来向集合添加单个元素。\n", 401 | "\n", 402 | "s.add(a) 将元素 a 加入集合 s 中。" 403 | ] 404 | }, 405 | { 406 | "cell_type": "code", 407 | "execution_count": 54, 408 | "id": "82d646a8", 409 | "metadata": {}, 410 | "outputs": [ 411 | { 412 | "name": "stdout", 413 | "output_type": "stream", 414 | "text": [ 415 | "{1, 3, 4}\n", 416 | "{1, 3, 4, 5}\n" 417 | ] 418 | } 419 | ], 420 | "source": [ 421 | "s = {1, 3, 4}\n", 422 | "s.add(4)\n", 423 | "print(s) # set([1, 3, 4])\n", 424 | "\n", 425 | "s.add(5)\n", 426 | "print(s) # set([1, 3, 4, 5])" 427 | ] 428 | }, 429 | { 430 | "cell_type": "markdown", 431 | "id": "aed2de2f", 432 | "metadata": {}, 433 | "source": [ 434 | "### update 方法向集合添加多个元素\n", 435 | "跟列表的extend方法类似,用来向集合添加多个元素。\n", 436 | "\n", 437 | "s.update(seq)" 438 | ] 439 | }, 440 | { 441 | "cell_type": "code", 442 | "execution_count": 55, 443 | "id": "c0591807", 444 | "metadata": {}, 445 | "outputs": [ 446 | { 447 | "name": "stdout", 448 | "output_type": "stream", 449 | "text": [ 450 | "{1, 3, 4, 5, 10, 11, 12}\n" 451 | ] 452 | } 453 | ], 454 | "source": [ 455 | "s.update([10, 11, 12])\n", 456 | "print(s) # set([1, 3, 4, 5, 10, 11, 12])" 457 | ] 458 | }, 459 | { 460 | "cell_type": "code", 461 | "execution_count": 56, 462 | "id": "888ae61f", 463 | "metadata": {}, 464 | "outputs": [ 465 | { 466 | "name": "stdout", 467 | "output_type": "stream", 468 | "text": [ 469 | "{3, 4}\n" 470 | ] 471 | } 472 | ], 473 | "source": [ 474 | "# remove 方法移除单个元素\n", 475 | "s = {1, 3, 4}\n", 476 | "s.remove(1)\n", 477 | "print(s) # set([3, 4])" 478 | ] 479 | }, 480 | { 481 | "cell_type": "markdown", 482 | "id": "016ff684", 483 | "metadata": {}, 484 | "source": [ 485 | "### pop 方法弹出元素\n", 486 | "由于集合没有顺序,不能像列表一样按照位置弹出元素,\n", 487 | "\n", 488 | "所以 pop 方法删除并返回集合中任意一个元素,如果集合中没有元素会报错。" 489 | ] 490 | }, 491 | { 492 | "cell_type": "code", 493 | "execution_count": 57, 494 | "id": "8ffbe8b7", 495 | "metadata": {}, 496 | "outputs": [ 497 | { 498 | "name": "stdout", 499 | "output_type": "stream", 500 | "text": [ 501 | "{3, 4} 1\n" 502 | ] 503 | } 504 | ], 505 | "source": [ 506 | "s = {1, 3, 4}\n", 507 | "d = s.pop()\n", 508 | "print(s, d)" 509 | ] 510 | }, 511 | { 512 | "cell_type": "code", 513 | "execution_count": 58, 514 | "id": "c8353563", 515 | "metadata": {}, 516 | "outputs": [ 517 | { 518 | "name": "stdout", 519 | "output_type": "stream", 520 | "text": [ 521 | "{1, 4}\n" 522 | ] 523 | } 524 | ], 525 | "source": [ 526 | "# discard 方法作用与 remove 一样\n", 527 | "s = {1, 3, 4}\n", 528 | "s.discard(3)\n", 529 | "print(s) # set([1, 4])" 530 | ] 531 | }, 532 | { 533 | "cell_type": "markdown", 534 | "id": "a8cbabdc", 535 | "metadata": {}, 536 | "source": [ 537 | "### difference_update方法\n", 538 | "\n", 539 | "a.difference_update(b) 从a中去除所有属于b的元素:" 540 | ] 541 | }, 542 | { 543 | "cell_type": "code", 544 | "execution_count": 59, 545 | "id": "06c93053", 546 | "metadata": {}, 547 | "outputs": [ 548 | { 549 | "name": "stdout", 550 | "output_type": "stream", 551 | "text": [ 552 | "{1}\n" 553 | ] 554 | } 555 | ], 556 | "source": [ 557 | "a = {1, 2, 3, 4}\n", 558 | "b = {2, 3, 4, 5}\n", 559 | "a.difference_update(b)\n", 560 | "print(a) # set([1])" 561 | ] 562 | }, 563 | { 564 | "cell_type": "markdown", 565 | "id": "33401a9f", 566 | "metadata": {}, 567 | "source": [ 568 | "本节完。" 569 | ] 570 | } 571 | ], 572 | "metadata": { 573 | "kernelspec": { 574 | "display_name": "Python 3", 575 | "language": "python", 576 | "name": "python3" 577 | }, 578 | "language_info": { 579 | "codemirror_mode": { 580 | "name": "ipython", 581 | "version": 3 582 | }, 583 | "file_extension": ".py", 584 | "mimetype": "text/x-python", 585 | "name": "python", 586 | "nbconvert_exporter": "python", 587 | "pygments_lexer": "ipython3", 588 | "version": "3.8.8" 589 | } 590 | }, 591 | "nbformat": 4, 592 | "nbformat_minor": 5 593 | } -------------------------------------------------------------------------------- /01_base/06_条件判断_if.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "id": "4af3adfa", 6 | "metadata": {}, 7 | "source": [ 8 | "| [01_base/06_条件判断_if.ipynb](https://github.com/shibing624/python-tutorial/blob/master/01_base/06_条件判断_if.ipynb) | Python条件判断 |[Open In Colab](https://colab.research.google.com/github/shibing624/python-tutorial/blob/master/01_base/06_条件判断_if.ipynb) |\n", 9 | "\n", 10 | "# 条件判断\n", 11 | "\n", 12 | "### 基本用法" 13 | ] 14 | }, 15 | { 16 | "cell_type": "code", 17 | "execution_count": 1, 18 | "id": "2e3a22dc", 19 | "metadata": {}, 20 | "outputs": [ 21 | { 22 | "name": "stdout", 23 | "output_type": "stream", 24 | "text": [ 25 | "exam score check:\n", 26 | "student pass\n" 27 | ] 28 | } 29 | ], 30 | "source": [ 31 | "a = 62\n", 32 | "print(\"exam score check:\")\n", 33 | "if a >= 60:\n", 34 | " print(\"student pass\")\n", 35 | "elif a == 0:\n", 36 | " print(\"student 0: not pass\")\n", 37 | "else:\n", 38 | " print(\"student not pass\")" 39 | ] 40 | }, 41 | { 42 | "cell_type": "markdown", 43 | "id": "7fbbbc96", 44 | "metadata": {}, 45 | "source": [ 46 | "可以使用 and , or , not 等关键词结合多个判断条件:" 47 | ] 48 | }, 49 | { 50 | "cell_type": "code", 51 | "execution_count": 2, 52 | "id": "8faa2e35", 53 | "metadata": {}, 54 | "outputs": [ 55 | { 56 | "name": "stdout", 57 | "output_type": "stream", 58 | "text": [ 59 | "True\n", 60 | "False\n", 61 | "True\n" 62 | ] 63 | } 64 | ], 65 | "source": [ 66 | "a = 10\n", 67 | "b = -5\n", 68 | "print(a > 0 and b < 0) # True\n", 69 | "print(not a > 0) # False\n", 70 | "print(a < 0 or b < 0) # True" 71 | ] 72 | }, 73 | { 74 | "cell_type": "markdown", 75 | "id": "a646a33e", 76 | "metadata": {}, 77 | "source": [ 78 | "### 一个例子" 79 | ] 80 | }, 81 | { 82 | "cell_type": "code", 83 | "execution_count": 3, 84 | "id": "92ffc111", 85 | "metadata": {}, 86 | "outputs": [ 87 | { 88 | "name": "stdout", 89 | "output_type": "stream", 90 | "text": [ 91 | "This is not a leap year.\n" 92 | ] 93 | } 94 | ], 95 | "source": [ 96 | "year = 1900\n", 97 | "if year % 400 == 0:\n", 98 | " print(\"This is a leap year!\")\n", 99 | "# 两个条件都满足才执行\n", 100 | "elif year % 4 == 0 and year % 100 != 0:\n", 101 | " print(\"This is a leap year!\")\n", 102 | "else:\n", 103 | " print(\"This is not a leap year.\")\n", 104 | "# This is not a leap year." 105 | ] 106 | }, 107 | { 108 | "cell_type": "markdown", 109 | "id": "feaffbfc", 110 | "metadata": {}, 111 | "source": [ 112 | "### 判断列表" 113 | ] 114 | }, 115 | { 116 | "cell_type": "code", 117 | "execution_count": 5, 118 | "id": "ccc00f7a", 119 | "metadata": {}, 120 | "outputs": [ 121 | { 122 | "name": "stdout", 123 | "output_type": "stream", 124 | "text": [ 125 | "the first element is: 1\n" 126 | ] 127 | } 128 | ], 129 | "source": [ 130 | "my_list = [1, 2]\n", 131 | "# 判断一个列表是否为空。\n", 132 | "if len(my_list) > 0:\n", 133 | " print(\"the first element is: \", my_list[0])\n", 134 | "else:\n", 135 | " print(\"no element.\")\n" 136 | ] 137 | }, 138 | { 139 | "cell_type": "markdown", 140 | "id": "2c6f77b2", 141 | "metadata": {}, 142 | "source": [ 143 | "本节完。" 144 | ] 145 | } 146 | ], 147 | "metadata": { 148 | "kernelspec": { 149 | "display_name": "Python 3", 150 | "language": "python", 151 | "name": "python3" 152 | }, 153 | "language_info": { 154 | "codemirror_mode": { 155 | "name": "ipython", 156 | "version": 3 157 | }, 158 | "file_extension": ".py", 159 | "mimetype": "text/x-python", 160 | "name": "python", 161 | "nbconvert_exporter": "python", 162 | "pygments_lexer": "ipython3", 163 | "version": "3.8.8" 164 | } 165 | }, 166 | "nbformat": 4, 167 | "nbformat_minor": 5 168 | } -------------------------------------------------------------------------------- /01_base/07_列表推导式.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "id": "01e2695a", 6 | "metadata": {}, 7 | "source": [ 8 | "| [01_base/07_列表推导式.ipynb](https://github.com/shibing624/python-tutorial/blob/master/01_base/07_列表推导式.ipynb) | Python列表推导式 |[Open In Colab](https://colab.research.google.com/github/shibing624/python-tutorial/blob/master/01_base/07_列表推导式.ipynb) |\n", 9 | "\n", 10 | "\n", 11 | "# 列表推导式\n", 12 | "\n", 13 | "循环可以用来生成列表:" 14 | ] 15 | }, 16 | { 17 | "cell_type": "code", 18 | "execution_count": 30, 19 | "id": "2224f209", 20 | "metadata": {}, 21 | "outputs": [ 22 | { 23 | "name": "stdout", 24 | "output_type": "stream", 25 | "text": [ 26 | "[4, 4, 9]\n" 27 | ] 28 | } 29 | ], 30 | "source": [ 31 | "values = [2, 2, 3]\n", 32 | "squares = []\n", 33 | "for x in values:\n", 34 | " squares.append(x ** 2)\n", 35 | "print(squares) # [4, 4, 9]" 36 | ] 37 | }, 38 | { 39 | "cell_type": "markdown", 40 | "id": "a4c969dc", 41 | "metadata": {}, 42 | "source": [ 43 | "列表推导式可以使用更简单的方法来创建这个列表:" 44 | ] 45 | }, 46 | { 47 | "cell_type": "code", 48 | "execution_count": 31, 49 | "id": "d72774b4", 50 | "metadata": {}, 51 | "outputs": [ 52 | { 53 | "name": "stdout", 54 | "output_type": "stream", 55 | "text": [ 56 | "[9, 64, 100, 196]\n" 57 | ] 58 | } 59 | ], 60 | "source": [ 61 | "values = [3, 8, 10, 14]\n", 62 | "squares = [x ** 2 for x in values]\n", 63 | "print(squares) # [9, 64, 100, 196]" 64 | ] 65 | }, 66 | { 67 | "cell_type": "markdown", 68 | "id": "8aab9ebb", 69 | "metadata": {}, 70 | "source": [ 71 | "可以加入条件筛选,在上面的例子中,\n", 72 | "\n", 73 | "假如只想保留列表中不大于8的数的平方:" 74 | ] 75 | }, 76 | { 77 | "cell_type": "code", 78 | "execution_count": 32, 79 | "id": "6c20ed66", 80 | "metadata": {}, 81 | "outputs": [ 82 | { 83 | "name": "stdout", 84 | "output_type": "stream", 85 | "text": [ 86 | "[9, 64, 100]\n" 87 | ] 88 | } 89 | ], 90 | "source": [ 91 | "squares = [x ** 2 for x in values if x <= 10]\n", 92 | "print(squares) # [9, 64, 100]" 93 | ] 94 | }, 95 | { 96 | "cell_type": "markdown", 97 | "id": "dc3d7744", 98 | "metadata": {}, 99 | "source": [ 100 | "平方的结果不大于100的:" 101 | ] 102 | }, 103 | { 104 | "cell_type": "code", 105 | "execution_count": 33, 106 | "id": "415e0e56", 107 | "metadata": {}, 108 | "outputs": [ 109 | { 110 | "name": "stdout", 111 | "output_type": "stream", 112 | "text": [ 113 | "[9, 64]\n" 114 | ] 115 | } 116 | ], 117 | "source": [ 118 | "squares = [x ** 2 for x in values if x ** 2 <= 80]\n", 119 | "print(squares) # [9, 64]" 120 | ] 121 | }, 122 | { 123 | "cell_type": "markdown", 124 | "id": "14064061", 125 | "metadata": {}, 126 | "source": [ 127 | "使用推导式生成集合和字典:" 128 | ] 129 | }, 130 | { 131 | "cell_type": "code", 132 | "execution_count": 34, 133 | "id": "afce0845", 134 | "metadata": {}, 135 | "outputs": [ 136 | { 137 | "name": "stdout", 138 | "output_type": "stream", 139 | "text": [ 140 | "{16, 49, 100}\n" 141 | ] 142 | } 143 | ], 144 | "source": [ 145 | "values = [10, 21, 4, 7, 12]\n", 146 | "square_set = {x ** 2 for x in values if x <= 10}\n", 147 | "\n", 148 | "print(square_set) # set([16, 49, 100])" 149 | ] 150 | }, 151 | { 152 | "cell_type": "code", 153 | "execution_count": 35, 154 | "id": "0f28e767", 155 | "metadata": {}, 156 | "outputs": [ 157 | { 158 | "name": "stdout", 159 | "output_type": "stream", 160 | "text": [ 161 | "{10: 100, 4: 16, 7: 49}\n" 162 | ] 163 | } 164 | ], 165 | "source": [ 166 | "square_dict = {x: x ** 2 for x in values if x <= 10}\n", 167 | "print(square_dict) # {10: 100, 4: 16, 7: 49}" 168 | ] 169 | }, 170 | { 171 | "cell_type": "markdown", 172 | "id": "697e2610", 173 | "metadata": {}, 174 | "source": [ 175 | "计算上面例子中生成的列表中所有元素的和:" 176 | ] 177 | }, 178 | { 179 | "cell_type": "code", 180 | "execution_count": 36, 181 | "id": "629432fd", 182 | "metadata": {}, 183 | "outputs": [ 184 | { 185 | "data": { 186 | "text/plain": [ 187 | "65" 188 | ] 189 | }, 190 | "execution_count": 36, 191 | "metadata": {}, 192 | "output_type": "execute_result" 193 | } 194 | ], 195 | "source": [ 196 | "total = sum([x ** 2 for x in values if x < 10])\n", 197 | "total # 65" 198 | ] 199 | }, 200 | { 201 | "cell_type": "markdown", 202 | "id": "7d2f0b44", 203 | "metadata": {}, 204 | "source": [ 205 | "但是,Python会生成这个列表,然后在将它放到垃圾回收机制中(因为没有变量指向它),\n", 206 | "\n", 207 | "这毫无疑问是种浪费。\n", 208 | "\n", 209 | "为了解决这种问题,与range()类似,Python使用产生式表达式来解决这个问题:" 210 | ] 211 | }, 212 | { 213 | "cell_type": "code", 214 | "execution_count": 37, 215 | "id": "f5af0789", 216 | "metadata": {}, 217 | "outputs": [ 218 | { 219 | "data": { 220 | "text/plain": [ 221 | "65" 222 | ] 223 | }, 224 | "execution_count": 37, 225 | "metadata": {}, 226 | "output_type": "execute_result" 227 | } 228 | ], 229 | "source": [ 230 | "total = sum(x ** 2 for x in values if x < 10)\n", 231 | "total # 65" 232 | ] 233 | }, 234 | { 235 | "cell_type": "markdown", 236 | "id": "7cd1dc3e", 237 | "metadata": {}, 238 | "source": [ 239 | "与上面相比,只是去掉了括号,但这里并不会一次性的生成这个列表。" 240 | ] 241 | }, 242 | { 243 | "cell_type": "code", 244 | "execution_count": 41, 245 | "id": "0346c01b", 246 | "metadata": {}, 247 | "outputs": [ 248 | { 249 | "name": "stdout", 250 | "output_type": "stream", 251 | "text": [ 252 | "list speed: 0.0003058910369873047\n" 253 | ] 254 | } 255 | ], 256 | "source": [ 257 | "import time\n", 258 | "\n", 259 | "# 比较一下两者的用时:\n", 260 | "x = range(1000000)\n", 261 | "t1 = time.time()\n", 262 | "\n", 263 | "total = sum([x ** 3 for x in values if x < 10])\n", 264 | "print(\"list speed: \", time.time() - t1)" 265 | ] 266 | }, 267 | { 268 | "cell_type": "code", 269 | "execution_count": 42, 270 | "id": "9680edb6", 271 | "metadata": {}, 272 | "outputs": [ 273 | { 274 | "name": "stdout", 275 | "output_type": "stream", 276 | "text": [ 277 | "comprehension speed: 0.00015783309936523438\n" 278 | ] 279 | } 280 | ], 281 | "source": [ 282 | "t2 = time.time()\n", 283 | "total = sum(x ** 3 for x in values if x < 10)\n", 284 | "print(\"comprehension speed:\", time.time() - t2)" 285 | ] 286 | }, 287 | { 288 | "cell_type": "markdown", 289 | "id": "ead82919", 290 | "metadata": {}, 291 | "source": [ 292 | "ipython 下可以输入:" 293 | ] 294 | }, 295 | { 296 | "cell_type": "code", 297 | "execution_count": 40, 298 | "id": "a2c6ceab", 299 | "metadata": {}, 300 | "outputs": [ 301 | { 302 | "name": "stdout", 303 | "output_type": "stream", 304 | "text": [ 305 | "327 ms ± 19.8 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)\n", 306 | "295 ms ± 10.7 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)\n" 307 | ] 308 | } 309 | ], 310 | "source": [ 311 | "x = range(1000000)\n", 312 | "%timeit total = sum([i**2 for i in x])\n", 313 | "%timeit total = sum(i**2 for i in x)" 314 | ] 315 | }, 316 | { 317 | "cell_type": "markdown", 318 | "id": "27fee9aa", 319 | "metadata": {}, 320 | "source": [ 321 | "本节完。" 322 | ] 323 | } 324 | ], 325 | "metadata": { 326 | "kernelspec": { 327 | "display_name": "Python 3", 328 | "language": "python", 329 | "name": "python3" 330 | }, 331 | "language_info": { 332 | "codemirror_mode": { 333 | "name": "ipython", 334 | "version": 3 335 | }, 336 | "file_extension": ".py", 337 | "mimetype": "text/x-python", 338 | "name": "python", 339 | "nbconvert_exporter": "python", 340 | "pygments_lexer": "ipython3", 341 | "version": "3.8.8" 342 | } 343 | }, 344 | "nbformat": 4, 345 | "nbformat_minor": 5 346 | } -------------------------------------------------------------------------------- /01_base/08_循环结构_loop.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "id": "923f67db", 6 | "metadata": {}, 7 | "source": [ 8 | "| [01_base/08_循环结构_loop.ipynb](https://github.com/shibing624/python-tutorial/blob/master/01_base/08_循环结构_loop.ipynb) | Python循环 |[Open In Colab](https://colab.research.google.com/github/shibing624/python-tutorial/blob/master/01_base/08_循环结构_loop.ipynb) |\n", 9 | "\n", 10 | "\n", 11 | "# 循环\n", 12 | "\n", 13 | "## for循环" 14 | ] 15 | }, 16 | { 17 | "cell_type": "code", 18 | "execution_count": 6, 19 | "id": "2f9b7ff3", 20 | "metadata": {}, 21 | "outputs": [ 22 | { 23 | "name": "stdout", 24 | "output_type": "stream", 25 | "text": [ 26 | "4999950000\n" 27 | ] 28 | } 29 | ], 30 | "source": [ 31 | "# for 循环\n", 32 | "total = 0\n", 33 | "for i in range(100000):\n", 34 | " total += i\n", 35 | "print(total) # 4999950000" 36 | ] 37 | }, 38 | { 39 | "cell_type": "markdown", 40 | "id": "3c363515", 41 | "metadata": {}, 42 | "source": [ 43 | "## while 循环\n", 44 | "\n", 45 | "```python\n", 46 | "while :\n", 47 | " \n", 48 | " \n", 49 | "```\n", 50 | "\n", 51 | "Python会循环执行,直到不满足为止。" 52 | ] 53 | }, 54 | { 55 | "cell_type": "code", 56 | "execution_count": 7, 57 | "id": "2171e648", 58 | "metadata": {}, 59 | "outputs": [ 60 | { 61 | "name": "stdout", 62 | "output_type": "stream", 63 | "text": [ 64 | "5050\n" 65 | ] 66 | } 67 | ], 68 | "source": [ 69 | "i = 0\n", 70 | "total = 0\n", 71 | "while i <= 100:\n", 72 | " total += i\n", 73 | " i += 1\n", 74 | "print(total) # 5050" 75 | ] 76 | }, 77 | { 78 | "cell_type": "markdown", 79 | "id": "0cb55b14", 80 | "metadata": {}, 81 | "source": [ 82 | "举个例子,通过while遍历集合:" 83 | ] 84 | }, 85 | { 86 | "cell_type": "code", 87 | "execution_count": 8, 88 | "id": "89780453", 89 | "metadata": {}, 90 | "outputs": [ 91 | { 92 | "name": "stdout", 93 | "output_type": "stream", 94 | "text": [ 95 | "Perform Hamlet\n", 96 | "Perform King\n", 97 | "Perform Mac\n" 98 | ] 99 | } 100 | ], 101 | "source": [ 102 | "# 空容器会被当成False,因此可以用while循环读取容器的所有元素\n", 103 | "plays = set(['Hamlet', 'Mac', 'King'])\n", 104 | "while plays:\n", 105 | " play = plays.pop()\n", 106 | " print('Perform', play)" 107 | ] 108 | }, 109 | { 110 | "cell_type": "markdown", 111 | "id": "663c2baa", 112 | "metadata": {}, 113 | "source": [ 114 | "## continue 语句\n", 115 | "\n", 116 | "遇到 continue 的时候,程序会返回到循环的最开始重新执行。" 117 | ] 118 | }, 119 | { 120 | "cell_type": "code", 121 | "execution_count": 9, 122 | "id": "405429ed", 123 | "metadata": {}, 124 | "outputs": [ 125 | { 126 | "name": "stdout", 127 | "output_type": "stream", 128 | "text": [ 129 | "6\n", 130 | "4\n", 131 | "2\n" 132 | ] 133 | } 134 | ], 135 | "source": [ 136 | "values = [7, 6, 4, 7, 19, 2, 1]\n", 137 | "for i in values:\n", 138 | " if i % 2 != 0:\n", 139 | " # 忽略奇数\n", 140 | " continue\n", 141 | " print(i)\n", 142 | "# 6\n", 143 | "# 4\n", 144 | "# 2" 145 | ] 146 | }, 147 | { 148 | "cell_type": "markdown", 149 | "id": "396e4744", 150 | "metadata": {}, 151 | "source": [ 152 | "## break 语句\n", 153 | "\n", 154 | "遇到 break 的时候,程序会跳出循环,不管循环条件是不是满足" 155 | ] 156 | }, 157 | { 158 | "cell_type": "code", 159 | "execution_count": 10, 160 | "id": "4b625093", 161 | "metadata": {}, 162 | "outputs": [ 163 | { 164 | "name": "stdout", 165 | "output_type": "stream", 166 | "text": [ 167 | "start\n", 168 | "1\n", 169 | "2\n", 170 | "3\n", 171 | "4\n" 172 | ] 173 | } 174 | ], 175 | "source": [ 176 | "command_list = ['start',\n", 177 | " '1',\n", 178 | " '2',\n", 179 | " '3',\n", 180 | " '4',\n", 181 | " 'stop',\n", 182 | " 'restart',\n", 183 | " '5',\n", 184 | " '6']\n", 185 | "while command_list:\n", 186 | " command = command_list.pop(0)\n", 187 | " if command == 'stop':\n", 188 | " break\n", 189 | " print(command)\n", 190 | "# start\n", 191 | "# 1\n", 192 | "# 2\n", 193 | "# 3\n", 194 | "# 4\n" 195 | ] 196 | }, 197 | { 198 | "cell_type": "markdown", 199 | "id": "c5f20b79", 200 | "metadata": {}, 201 | "source": [ 202 | "本节完。" 203 | ] 204 | } 205 | ], 206 | "metadata": { 207 | "kernelspec": { 208 | "display_name": "Python 3", 209 | "language": "python", 210 | "name": "python3" 211 | }, 212 | "language_info": { 213 | "codemirror_mode": { 214 | "name": "ipython", 215 | "version": 3 216 | }, 217 | "file_extension": ".py", 218 | "mimetype": "text/x-python", 219 | "name": "python", 220 | "nbconvert_exporter": "python", 221 | "pygments_lexer": "ipython3", 222 | "version": "3.8.8" 223 | } 224 | }, 225 | "nbformat": 4, 226 | "nbformat_minor": 5 227 | } -------------------------------------------------------------------------------- /01_base/11_线程和进程.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "id": "aa1a927b", 6 | "metadata": {}, 7 | "source": [ 8 | "| [01_base/11_线程和进程.ipynb](https://github.com/shibing624/python-tutorial/blob/master/01_base/11_线程和进程.ipynb) | Python多线程和多进程 |[Open In Colab](https://colab.research.google.com/github/shibing624/python-tutorial/blob/master/01_base/11_线程和进程.ipynb) |\n", 9 | "\n", 10 | "# 进程和线程\n", 11 | "\n", 12 | "## 进程\n", 13 | "\n", 14 | "进程就是操作系统中执行的一个程序,操作系统以进程为单位分配存储空间,操作系统管理所有进程的执行,为它们合理的分配资源。\n", 15 | "\n", 16 | "一个进程就是macOS中的“活动监视器”、Windows中的“任务管理器”的一个执行程序。\n", 17 | "\n", 18 | "Python既支持多进程又支持多线程。" 19 | ] 20 | }, 21 | { 22 | "cell_type": "markdown", 23 | "id": "8e0064cb", 24 | "metadata": {}, 25 | "source": [ 26 | "### 多进程\n", 27 | "\n", 28 | "我们来完成1~100000000求和的计算密集型任务,循环解决,暂时也不考虑列表切片操作花费的时间,只是把做运算和合并运算结果的时间统计出来。" 29 | ] 30 | }, 31 | { 32 | "cell_type": "code", 33 | "execution_count": 1, 34 | "id": "1999432b", 35 | "metadata": {}, 36 | "outputs": [], 37 | "source": [ 38 | "from time import time\n", 39 | "\n", 40 | "\n", 41 | "def main():\n", 42 | " total = 0\n", 43 | " number_list = [x for x in range(1, 100000001)]\n", 44 | " start = time()\n", 45 | " for number in number_list:\n", 46 | " total += number\n", 47 | " print(total)\n", 48 | " end = time()\n", 49 | " print('Execution time: %.3fs' % (end - start))\n" 50 | ] 51 | }, 52 | { 53 | "cell_type": "code", 54 | "execution_count": 2, 55 | "id": "a3393e34", 56 | "metadata": {}, 57 | "outputs": [ 58 | { 59 | "name": "stdout", 60 | "output_type": "stream", 61 | "text": [ 62 | "5000000050000000\n", 63 | "Execution time: 4.199s\n" 64 | ] 65 | } 66 | ], 67 | "source": [ 68 | "main() \n", 69 | "# 5000000050000000\n", 70 | "# Execution time: 6.798s" 71 | ] 72 | }, 73 | { 74 | "cell_type": "markdown", 75 | "id": "498385db", 76 | "metadata": {}, 77 | "source": [ 78 | "利用多进程“分而治之”,\n", 79 | "\n", 80 | "当我们将这个任务分解到8个进程中去执行:\n", 81 | "\n", 82 | "```python\n", 83 | "from multiprocessing import Process, Queue\n", 84 | "from time import time\n", 85 | "\n", 86 | "core_num = 8\n", 87 | "\n", 88 | "\n", 89 | "def task_handler(curr_list, result_queue):\n", 90 | " total = 0\n", 91 | " for number in curr_list:\n", 92 | " total += number\n", 93 | " result_queue.put(total)\n", 94 | "\n", 95 | "\n", 96 | "def main():\n", 97 | " processes = []\n", 98 | " number_list = [x for x in range(1, 100000001)]\n", 99 | " result_queue = Queue()\n", 100 | " index = 0\n", 101 | " # 启动core_num(8)个进程将数据切片后进行运算\n", 102 | " index_batch = int(100000000 / core_num)\n", 103 | " for _ in range(core_num):\n", 104 | " p = Process(target=task_handler,\n", 105 | " args=(number_list[index:index + index_batch], result_queue))\n", 106 | " index += index_batch\n", 107 | " processes.append(p)\n", 108 | " p.start()\n", 109 | " # 开始记录所有进程执行完成花费的时间\n", 110 | " start = time()\n", 111 | " for p in processes:\n", 112 | " p.join()\n", 113 | " # 合并执行结果\n", 114 | " total = 0\n", 115 | " while not result_queue.empty():\n", 116 | " total += result_queue.get()\n", 117 | " print(total)\n", 118 | " end = time()\n", 119 | " print('Execution time: ', (end - start), 's', sep='')\n", 120 | "\n", 121 | "\n", 122 | "if __name__ == '__main__':\n", 123 | " main()\n", 124 | "\n", 125 | "```\n", 126 | "以上代码保存为 multi_process.py\n" 127 | ] 128 | }, 129 | { 130 | "cell_type": "code", 131 | "execution_count": 3, 132 | "id": "78a43a25", 133 | "metadata": {}, 134 | "outputs": [ 135 | { 136 | "name": "stdout", 137 | "output_type": "stream", 138 | "text": [ 139 | "5000000050000000\r\n", 140 | "Execution time: 1.4334020614624023s\r\n" 141 | ] 142 | } 143 | ], 144 | "source": [ 145 | "!python multi_process.py" 146 | ] 147 | }, 148 | { 149 | "cell_type": "code", 150 | "execution_count": 4, 151 | "id": "589f84dc", 152 | "metadata": {}, 153 | "outputs": [], 154 | "source": [ 155 | "# 5000000050000000\n", 156 | "# Execution time: 0.7936668395996094s" 157 | ] 158 | }, 159 | { 160 | "cell_type": "markdown", 161 | "id": "50377f4e", 162 | "metadata": {}, 163 | "source": [ 164 | "明显,多进程更快。\n", 165 | "\n", 166 | "使用多进程后由于获得了更多的CPU执行时间以及更好的利用了CPU的多核特性,明显的减少了程序的执行时间,而且计算量越大效果越明显。" 167 | ] 168 | }, 169 | { 170 | "cell_type": "markdown", 171 | "id": "ccb856f8", 172 | "metadata": {}, 173 | "source": [ 174 | "## 线程\n", 175 | "\n", 176 | "多任务可以由多进程完成,也可以由一个进程内的多线程完成。\n", 177 | "\n", 178 | "我们前面提到了进程是由若干线程组成的,一个进程至少有一个线程。\n", 179 | "\n", 180 | "Python的标准库提供了两个模块:_thread和threading,_thread是低级模块,threading是高级模块,对_thread进行了封装。绝大多数情况下,我们只需要使用threading这个高级模块。\n", 181 | "\n", 182 | "### 多线程\n", 183 | "\n", 184 | "如下所示的界面中,有“下载”和“关于”两个按钮,用休眠的方式模拟点击“下载”按钮会联网下载文件需要耗费10秒的时间,当点击“下载”按钮后,整个任务阻塞:" 185 | ] 186 | }, 187 | { 188 | "cell_type": "code", 189 | "execution_count": 5, 190 | "id": "65d71219", 191 | "metadata": {}, 192 | "outputs": [], 193 | "source": [ 194 | "import time\n", 195 | "import tkinter\n", 196 | "import tkinter.messagebox\n", 197 | "\n", 198 | "\n", 199 | "def download():\n", 200 | " # 模拟下载任务需要花费5秒钟时间\n", 201 | " time.sleep(5)\n", 202 | " tkinter.messagebox.showinfo('提示', '下载完成!')\n", 203 | "\n", 204 | "\n", 205 | "def show_about():\n", 206 | " tkinter.messagebox.showinfo('关于', '作者: 123(v1.0)')\n", 207 | "\n", 208 | "\n", 209 | "def main():\n", 210 | " top = tkinter.Tk()\n", 211 | " top.title('单线程')\n", 212 | " top.geometry('400x400')\n", 213 | " top.wm_attributes('-topmost', True)\n", 214 | "\n", 215 | " panel = tkinter.Frame(top)\n", 216 | " button1 = tkinter.Button(panel, text='下载', command=download)\n", 217 | " button1.pack(side='left')\n", 218 | " button2 = tkinter.Button(panel, text='关于', command=show_about)\n", 219 | " button2.pack(side='right')\n", 220 | " panel.pack(side='bottom')\n", 221 | "\n", 222 | " tkinter.mainloop()\n", 223 | "\n", 224 | "\n", 225 | "if __name__ == '__main__':\n", 226 | " main()" 227 | ] 228 | }, 229 | { 230 | "cell_type": "markdown", 231 | "id": "2cf79b88", 232 | "metadata": {}, 233 | "source": [ 234 | "使用多线程后,不会阻塞了主线程:\n" 235 | ] 236 | }, 237 | { 238 | "cell_type": "code", 239 | "execution_count": 7, 240 | "id": "5c6e9fa0", 241 | "metadata": {}, 242 | "outputs": [], 243 | "source": [ 244 | "import time\n", 245 | "import tkinter\n", 246 | "import tkinter.messagebox\n", 247 | "from threading import Thread\n", 248 | "\n", 249 | "\n", 250 | "def main():\n", 251 | "\n", 252 | " class DownloadTaskHandler(Thread):\n", 253 | "\n", 254 | " def run(self):\n", 255 | " time.sleep(5)\n", 256 | " tkinter.messagebox.showinfo('提示', '下载完成!')\n", 257 | " # 启用下载按钮\n", 258 | " button1.config(state=tkinter.NORMAL)\n", 259 | "\n", 260 | " def download():\n", 261 | " # 禁用下载按钮\n", 262 | " button1.config(state=tkinter.DISABLED)\n", 263 | " # 通过daemon参数将线程设置为守护线程(主程序退出就不再保留执行)\n", 264 | " # 在线程中处理耗时间的下载任务\n", 265 | " DownloadTaskHandler(daemon=True).start()\n", 266 | "\n", 267 | " def show_about():\n", 268 | " tkinter.messagebox.showinfo('关于', '作者: 123(v1.0)')\n", 269 | "\n", 270 | " top = tkinter.Tk()\n", 271 | " top.title('多线程')\n", 272 | " top.geometry('400x400')\n", 273 | " top.wm_attributes('-topmost', 1)\n", 274 | "\n", 275 | " panel = tkinter.Frame(top)\n", 276 | " button1 = tkinter.Button(panel, text='下载', command=download)\n", 277 | " button1.pack(side='left')\n", 278 | " button2 = tkinter.Button(panel, text='关于', command=show_about)\n", 279 | " button2.pack(side='right')\n", 280 | " panel.pack(side='bottom')\n", 281 | "\n", 282 | " tkinter.mainloop()\n", 283 | "\n", 284 | "\n", 285 | "if __name__ == '__main__':\n", 286 | " main()" 287 | ] 288 | }, 289 | { 290 | "cell_type": "markdown", 291 | "id": "b429fb0c", 292 | "metadata": {}, 293 | "source": [ 294 | "会看到弹出的窗口是多模态的,点击下载按钮不影响其他按钮操作。" 295 | ] 296 | }, 297 | { 298 | "cell_type": "markdown", 299 | "id": "4052e822", 300 | "metadata": {}, 301 | "source": [ 302 | "**Python的多线程并不能发挥CPU的多核特性**,这一点只要启动几个执行死循环的线程就可以得到证实了。之所以如此,是因为Python的解释器有一个“全局解释器锁”(GIL)的东西,任何线程执行前必须先获得GIL锁,然后每执行100条字节码,解释器就自动释放GIL锁,让别的线程有机会执行,这是一个历史遗留问题。\n", 303 | "\n", 304 | "Python解释器由于设计时有GIL全局锁,导致了多线程无法利用多核。多线程的并发在Python中就是一个美丽的梦。\n", 305 | "\n", 306 | "多进程是有效的。" 307 | ] 308 | }, 309 | { 310 | "cell_type": "markdown", 311 | "id": "cb7b94ad", 312 | "metadata": {}, 313 | "source": [ 314 | "本节完。" 315 | ] 316 | }, 317 | { 318 | "cell_type": "code", 319 | "execution_count": null, 320 | "id": "d2226241", 321 | "metadata": {}, 322 | "outputs": [], 323 | "source": [] 324 | } 325 | ], 326 | "metadata": { 327 | "kernelspec": { 328 | "display_name": "Python 3", 329 | "language": "python", 330 | "name": "python3" 331 | }, 332 | "language_info": { 333 | "codemirror_mode": { 334 | "name": "ipython", 335 | "version": 3 336 | }, 337 | "file_extension": ".py", 338 | "mimetype": "text/x-python", 339 | "name": "python", 340 | "nbconvert_exporter": "python", 341 | "pygments_lexer": "ipython3", 342 | "version": "3.8.8" 343 | } 344 | }, 345 | "nbformat": 4, 346 | "nbformat_minor": 5 347 | } -------------------------------------------------------------------------------- /01_base/README.md: -------------------------------------------------------------------------------- 1 | # Python基础 2 | 3 | ## 教程列表 4 | 5 | | Notebook | Description | | 6 | |:----------|:-------------|------:| 7 | | [01_base/01_字符串类型_str.ipynb](https://github.com/shibing624/python-tutorial/blob/master/01_base/01_字符串类型_str.ipynb) | Python字符串类型 |[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/shibing624/python-tutorial/blob/master/01_base/01_字符串类型_str.ipynb) | 8 | | [01_base/02_列表类型_list.ipynb](https://github.com/shibing624/python-tutorial/blob/master/01_base/02_列表类型_list.ipynb) | Python列表类型 |[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/shibing624/python-tutorial/blob/master/01_base/02_列表类型_list.ipynb) | 9 | | [01_base/03_元组类型_tuple.ipynb](https://github.com/shibing624/python-tutorial/blob/master/01_base/03_元组类型_tuple.ipynb) | Python元组 |[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/shibing624/python-tutorial/blob/master/01_base/03_元组类型_tuple.ipynb) | 10 | | [01_base/04_字典类型_dict.ipynb](https://github.com/shibing624/python-tutorial/blob/master/01_base/04_字典类型_dict.ipynb) | Python字典 |[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/shibing624/python-tutorial/blob/master/01_base/04_字典类型_dict.ipynb) | 11 | | [01_base/05_集合类型_set.ipynb](https://github.com/shibing624/python-tutorial/blob/master/01_base/05_集合类型_set.ipynb) | Python集合 |[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/shibing624/python-tutorial/blob/master/01_base/05_集合类型_set.ipynb) | 12 | | [01_base/06_条件判断_if.ipynb](https://github.com/shibing624/python-tutorial/blob/master/01_base/06_条件判断_if.ipynb) | Python条件判断 |[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/shibing624/python-tutorial/blob/master/01_base/06_条件判断_if.ipynb) | 13 | | [01_base/07_列表推导式.ipynb](https://github.com/shibing624/python-tutorial/blob/master/01_base/07_列表推导式.ipynb) | Python列表推导式 |[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/shibing624/python-tutorial/blob/master/01_base/07_列表推导式.ipynb) | 14 | | [01_base/08_循环结构_loop.ipynb](https://github.com/shibing624/python-tutorial/blob/master/01_base/08_循环结构_loop.ipynb) | Python循环 |[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/shibing624/python-tutorial/blob/master/01_base/08_循环结构_loop.ipynb) | 15 | | [01_base/09_函数和模块.ipynb](https://github.com/shibing624/python-tutorial/blob/master/01_base/09_函数和模块.ipynb) | Python函数 |[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/shibing624/python-tutorial/blob/master/01_base/09_函数和模块.ipynb) | 16 | | [01_base/10_文件和异常.ipynb](https://github.com/shibing624/python-tutorial/blob/master/01_base/10_文件和异常.ipynb) | Python文件和异常 |[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/shibing624/python-tutorial/blob/master/01_base/10_文件和异常.ipynb) | 17 | | [01_base/11_线程和进程.ipynb](https://github.com/shibing624/python-tutorial/blob/master/01_base/11_线程和进程.ipynb) | Python多线程和多进程 |[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/shibing624/python-tutorial/blob/master/01_base/11_线程和进程.ipynb) | 18 | -------------------------------------------------------------------------------- /01_base/multi_process.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | @author:XuMing(xuming624@qq.com) 4 | @description: 5 | """ 6 | 7 | from multiprocessing import Process, Queue 8 | from time import time 9 | 10 | core_num = 8 11 | 12 | 13 | def task_handler(curr_list, result_queue): 14 | total = 0 15 | for number in curr_list: 16 | total += number 17 | result_queue.put(total) 18 | 19 | 20 | def main(): 21 | processes = [] 22 | number_list = [x for x in range(1, 100000001)] 23 | result_queue = Queue() 24 | index = 0 25 | # 启动core_num(8)个进程将数据切片后进行运算 26 | index_batch = int(100000000 / core_num) 27 | for _ in range(core_num): 28 | p = Process(target=task_handler, 29 | args=(number_list[index:index + index_batch], result_queue)) 30 | index += index_batch 31 | processes.append(p) 32 | p.start() 33 | # 开始记录所有进程执行完成花费的时间 34 | start = time() 35 | for p in processes: 36 | p.join() 37 | # 合并执行结果 38 | total = 0 39 | while not result_queue.empty(): 40 | total += result_queue.get() 41 | print(total) 42 | end = time() 43 | print('Execution time: ', (end - start), 's', sep='') 44 | 45 | 46 | if __name__ == '__main__': 47 | main() 48 | -------------------------------------------------------------------------------- /01_base/练习/九九乘法表.py: -------------------------------------------------------------------------------- 1 | '''要求使用循环代码打印一个九九乘法表出来.如下 2 | 1*1=1 3 | 4 | 1*2=2 2*2=4 5 | 6 | 1*3=3 2*3=6 3*3=9 7 | 8 | 1*4=4 2*4=8 3*4=12 4*4=16 9 | 10 | 1*5=5 2*5=10 3*5=15 4*5=20 5*5=25 11 | 12 | 1*6=6 2*6=12 3*6=18 4*6=24 5*6=30 6*6=36 13 | 14 | 1*7=7 2*7=14 3*7=21 4*7=28 5*7=35 6*7=42 7*7=49 15 | 16 | 1*8=8 2*8=16 3*8=24 4*8=32 5*8=40 6*8=48 7*8=56 8*8=64 17 | 18 | 1*9=9 2*9=18 3*9=27 4*9=36 5*9=45 6*9=54 7*9=63 8*9=72 9*9=81 19 | 20 | ''' 21 | 22 | 23 | 24 | 25 | 26 | 27 | #方法二 28 | i=1 29 | while i<10: #控制行,1到9 30 | j=1 31 | while j <= i: #控制每行显示的数量,1到9 32 | print("%d*%d=%d"%(j,i,i*j),end=' ') #输出 33 | j+=1 #每行显示的数量加1 34 | print("\n") #每一行结束换行 35 | i+=1 #行数加1 36 | 37 | for i in range(1,10): 38 | print() 39 | for j in range(1,i+1): 40 | print('%d*%d=%d' % (j,i,i*j),end=' ') -------------------------------------------------------------------------------- /01_base/练习/冒泡排序.py: -------------------------------------------------------------------------------- 1 | ''' 2 | 给定一个列表,请你对列表的元素进行 从大到小排序 与从小到大排序 3 | ''' 4 | 5 | 6 | list1 = [13, 22, 6, 99, 11, 0] 7 | 8 | for a in range(len(list1)): 9 | for b in range(a,len(list1)): 10 | if list1[a] < list1[b]: #如果m大于了n 11 | list1[a] ,list1[b] = list1[b],list1[a]#交换位置 12 | print(list1) -------------------------------------------------------------------------------- /01_base/练习/商余问题.py: -------------------------------------------------------------------------------- 1 | ''' 2 | 学校有440人参加考试,1号考场有80个座位,要求座位号为0101--0180 3 | 后面每个考场40个座位: 4 | 2号考场考试号要求为0201--0240 5 | 3号考场考试号要求为0301--0440 6 | 后续考场以此类推,请你打印出来这些考场号吧 7 | 8 | 起始代码: 9 | for i in range(1,440): 10 | 11 | ''' 12 | 13 | 14 | for i in range(1,440): 15 | if i <= 80 : 16 | print('01{:0>2d}'.format(i)) 17 | elif i <= 440: 18 | if i%40 == 0: 19 | print('{:0>2d}{:0>2d}'.format(i//40-1,40)) 20 | else: 21 | print('{:0>2d}{:0>2d}'.format(i//40,i%40)) -------------------------------------------------------------------------------- /01_base/练习/大数分解.py: -------------------------------------------------------------------------------- 1 | '''每个数字可以写成多个质数的乘积,给定一个数字,请你分解为多个质数''' 2 | 3 | def fun(num, list=None): 4 | if list is None: 5 | list = [] 6 | for i in range(2, num): 7 | while num % i == 0: 8 | list.append(i) 9 | num = int(num / i) 10 | if num > 1: 11 | fun(num) 12 | return list 13 | x = 9*5 14 | print(fun(x)) -------------------------------------------------------------------------------- /01_base/练习/平方和问题.py: -------------------------------------------------------------------------------- 1 | '''两个数字的平方和是2022,请问这2个数分别是多少''' 2 | 3 | for a in range(1,2022): 4 | if (2022 - a*a)**0.5 in range(1,2022): 5 | print(a) -------------------------------------------------------------------------------- /01_base/练习/抽奖.py: -------------------------------------------------------------------------------- 1 | """名单 = ['叶子','月娜','娜月','月叶','叶月','叶娜','月子','娜叶','娜子'] 2 | mychoice(名单) 3 | 打卡要求:风变科技计划抽一次年终奖 4 | 特等奖:1个组合课学习资格 5 | 一等奖:2个电饭煲 6 | 二等奖:4本python书 7 | 请你根据提示写出具有对应功能的代码""" 8 | 9 | 10 | def mychoice(名单): 11 | import random 12 | 中奖人员 = random.choice(名单) # 随机抽取一个元素 13 | print(中奖人员) # 打印出结果 14 | 名单.remove(中奖人员) # 从名单中移除中奖人员【这样一个人就只能抽到一次】 15 | 16 | 17 | 名单 = ['叶子', '月娜', '月娜2', '月娜3', '月娜4', '月娜5', '月娜6', '月娜7', '月娜8'] 18 | 19 | # for i in range(4): #奖品的等级 20 | # if i == 0: # 一等奖 21 | # print('恭喜以下同学获得:图书') 22 | # for i in range(2): #抽几个 23 | # mychoice(名单) 24 | # elif i == 1: # 二等奖 25 | # print('恭喜以下同学获得:xxxx') 26 | # for i in range(5): #抽几个 27 | # mychoice(名单) 28 | # elif i == 2:# 三等奖 29 | # print('恭喜以下同学获得:XXX') 30 | # for i in range(3):#抽几个 31 | # mychoice(名单) 32 | # else: 33 | # print('恭喜以下同学获得:XXX') 34 | # for i in range(3):#抽几个 35 | # mychoice(名单) -------------------------------------------------------------------------------- /01_base/练习/数羊问题.py: -------------------------------------------------------------------------------- 1 | ''' 2 | 编写一段代码,模拟我们数羊到入睡的过程: 3 | 4 | 5 | 每数五只羊,就提问一次:睡着了吗? 6 | 如果没有睡着,继续循环,并打印“继续数羊”。 7 | 如果睡着了,则停止循环,并打印“终于睡着了”。 8 | ''' 9 | 10 | 11 | i = 0 12 | while True: 13 | i += 1 14 | left_endpoint = 1 + 5 * ( i - 1 ) 15 | right_endpoint = 1 + 5 * i 16 | for i in range(left_endpoint, right_endpoint): 17 | print(str(i)+'只羊') 18 | answer = input('睡着了吗?回答是或否:') 19 | if answer == '是': 20 | break 21 | print('继续数羊') 22 | print('终于睡着了') 23 | 24 | #方法二 25 | 睡觉的状态 = '还没睡' 26 | a = 0 27 | while 睡觉的状态 != '睡着': # 只要不是睡着,就继续数 28 | a +=1 29 | print(str(a)+'只羊') 30 | if a%9 == 0 : # %是取余数 每次数5只羊 31 | 睡觉的状态 = input('睡着了嘛?') -------------------------------------------------------------------------------- /01_base/练习/时钟问题.py: -------------------------------------------------------------------------------- 1 | ''' 2 | 请从00:00依次打印出一天的时间 3 | 示例: 4 | 23 : 52 5 | 23 : 53 6 | 23 : 54 7 | 23 : 55 8 | 23 : 56 9 | 23 : 57 10 | 23 : 58 11 | 23 : 59 12 | ''' 13 | 14 | 15 | for 时钟 in range(24): 16 | for 分钟 in range(60): 17 | print(时钟, ':', 分钟) 18 | -------------------------------------------------------------------------------- /01_base/练习/查无此人-列表版.py: -------------------------------------------------------------------------------- 1 | """ 2 | 小明拿到了一个电影+演员的数据名单,他想设计一个程序,要求: 3 | 1.输入演员名 4 | 2.如果演员出演了电影,则打印他+他出演的全部电影。程序结束 5 | 3.如果演员没有出演电影,则打印查无此人。程序继续 6 | 电影 = [ 7 | '妖猫传',['黄轩','染谷将太'], 8 | '无问西东',['章子怡','王力宏','祖峰'], 9 | '超时空同居',['雷佳音','佟丽娅','黄轩']] 10 | """ 11 | 12 | 13 | 14 | 电影 = [ 15 | '妖猫传',['黄轩','染谷将太'], 16 | '无问西东',['章子怡','王力宏','祖峰'], 17 | '超时空同居',['雷佳音','佟丽娅','黄轩']] 18 | # 如果查到了:打印出演员+【所有的】电影,循环结束 19 | # 如果没查到,就 循环继续,并且打印【查无此人】 20 | 找到了吗 = 0 21 | while True: 22 | name = input('你要找的演员') 23 | for i in 电影: 24 | if name not in i : 25 | a = i #暂存---for 是逐一提取数据,并赋值 26 | else: 27 | print(name,'出演了',a) 28 | 找到了吗 += 1 29 | if 找到了吗 != 0 : # 不等于 0 就代表它找到了 30 | break 31 | print('【查无此人】') # 1号位 -------------------------------------------------------------------------------- /01_base/练习/每次抽走奇数.py: -------------------------------------------------------------------------------- 1 | ''' 2 | 要求:0-100,每次抽走奇数,打印剩余的那个数字 3 | ''' 4 | 5 | aList = [] 6 | for i in range(0,2023): 7 | aList.append(i) 8 | 9 | while len(aList)>1: 10 | aList = aList[1::2] 11 | print(aList) 12 | print(aList) -------------------------------------------------------------------------------- /02_advanced/02_数据库_sql.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "id": "8b6eb478", 6 | "metadata": {}, 7 | "source": [ 8 | "| [02_advanced/02_数据库_sql.ipynb](https://github.com/shibing624/python-tutorial/blob/master/02_advanced/02_数据库_sql.ipynb) | Python操作mysql数据库 |[Open In Colab](https://colab.research.google.com/github/shibing624/python-tutorial/blob/master/02_advanced/02_数据库_sql.ipynb) |\n", 9 | "\n", 10 | "# 数据库\n", 11 | "\n", 12 | "\n", 13 | "无论是Google、Facebook,还是国内的BAT,无一例外都选择了免费的开源数据库:\n", 14 | "\n", 15 | "- MySQL,大家都在用,一般错不了;\n", 16 | "- PostgreSQL,学术气息有点重,其实挺不错,但知名度没有MySQL高;\n", 17 | "- sqlite,嵌入式数据库,适合桌面和移动应用。\n", 18 | "\n", 19 | "作为Python开发工程师,选择哪个免费数据库呢?一般是MySQL,测试或本地开发使用sqlite也可以。\n", 20 | "\n", 21 | "## SQLite\n", 22 | "\n", 23 | "SQLite是一种嵌入式数据库,它的数据库就是一个文件。\n", 24 | "\n", 25 | "Python就内置了SQLite3,所以,在Python中使用SQLite,不需要安装任何东西,直接使用。" 26 | ] 27 | }, 28 | { 29 | "cell_type": "code", 30 | "execution_count": null, 31 | "id": "4fb7c81d", 32 | "metadata": {}, 33 | "outputs": [], 34 | "source": [ 35 | "# 导入SQLite驱动:\n", 36 | "import sqlite3\n", 37 | "# 连接到SQLite数据库\n", 38 | "# 数据库文件是test.db\n", 39 | "# 如果文件不存在,会自动在当前目录创建:\n", 40 | "\n", 41 | "import os\n", 42 | "os.remove('test.db')\n", 43 | "conn = sqlite3.connect('test.db')" 44 | ] 45 | }, 46 | { 47 | "cell_type": "code", 48 | "execution_count": null, 49 | "id": "dedd5e43", 50 | "metadata": {}, 51 | "outputs": [], 52 | "source": [ 53 | "# 创建一个Cursor:\n", 54 | "cursor = conn.cursor()\n", 55 | "# 执行一条SQL语句,创建user表:\n", 56 | "cursor.execute('create table user (id varchar(20) primary key, name varchar(20))')\n", 57 | "# " 58 | ] 59 | }, 60 | { 61 | "cell_type": "code", 62 | "execution_count": null, 63 | "id": "288b4e27", 64 | "metadata": {}, 65 | "outputs": [], 66 | "source": [ 67 | "# 继续执行一条SQL语句,插入一条记录:\n", 68 | "cursor.execute('insert into user (id, name) values (\\'1\\', \\'Michael\\')')\n", 69 | "# " 70 | ] 71 | }, 72 | { 73 | "cell_type": "code", 74 | "execution_count": null, 75 | "id": "dccbc415", 76 | "metadata": {}, 77 | "outputs": [], 78 | "source": [ 79 | "# 通过rowcount获得插入的行数:\n", 80 | "cursor.rowcount\n", 81 | "# 1" 82 | ] 83 | }, 84 | { 85 | "cell_type": "code", 86 | "execution_count": null, 87 | "id": "cb402ed6", 88 | "metadata": {}, 89 | "outputs": [], 90 | "source": [ 91 | "# 关闭Cursor:\n", 92 | "cursor.close()\n", 93 | "# 提交事务:\n", 94 | "conn.commit()\n", 95 | "# 关闭Connection:\n", 96 | "conn.close()" 97 | ] 98 | }, 99 | { 100 | "cell_type": "markdown", 101 | "id": "fa396d33", 102 | "metadata": {}, 103 | "source": [ 104 | "我们再试试查询记录:\n" 105 | ] 106 | }, 107 | { 108 | "cell_type": "code", 109 | "execution_count": 14, 110 | "id": "a667ce19", 111 | "metadata": {}, 112 | "outputs": [ 113 | { 114 | "name": "stdout", 115 | "output_type": "stream", 116 | "text": [ 117 | "[('1', 'Michael')]\n" 118 | ] 119 | } 120 | ], 121 | "source": [ 122 | "conn = sqlite3.connect('test.db')\n", 123 | "cursor = conn.cursor()\n", 124 | "# 执行查询语句:\n", 125 | "cursor.execute('select * from user where id=?', '1')\n", 126 | "# 获得查询结果集:\n", 127 | "values = cursor.fetchall()\n", 128 | "print(values)\n", 129 | "cursor.close()\n", 130 | "conn.close()" 131 | ] 132 | }, 133 | { 134 | "cell_type": "code", 135 | "execution_count": 15, 136 | "id": "8d122f04", 137 | "metadata": {}, 138 | "outputs": [], 139 | "source": [ 140 | "import os\n", 141 | "os.remove('test.db') # 删除数据库文件" 142 | ] 143 | }, 144 | { 145 | "cell_type": "markdown", 146 | "id": "bc9c0efb", 147 | "metadata": {}, 148 | "source": [ 149 | "## MySQL\n", 150 | "\n", 151 | "MySQL是Web世界中使用最广泛的数据库服务器。SQLite的特点是轻量级、可嵌入,但不能承受高并发访问,适合桌面和移动应用。而MySQL是为服务器端设计的数据库,能承受高并发访问,同时占用的内存也远远大于SQLite。\n", 152 | "\n", 153 | "可以直接从MySQL官方网站下载最新的[Community Server 5.6.x版本](https://dev.mysql.com/downloads/mysql/5.6.html)。\n", 154 | "\n", 155 | "### PyMySQL\n", 156 | "\n", 157 | "PyMySQL是一个纯Python写的MySQL操作库,它的目标是替代MySQLdb,PyMySQL的性能和MySQLdb几乎相当,如果对性能要求不是特别的强,使用PyMySQL将更加方便。" 158 | ] 159 | }, 160 | { 161 | "cell_type": "code", 162 | "execution_count": null, 163 | "id": "1d024857", 164 | "metadata": {}, 165 | "outputs": [], 166 | "source": [ 167 | "!pip install PyMySQL" 168 | ] 169 | }, 170 | { 171 | "cell_type": "code", 172 | "execution_count": null, 173 | "id": "3a610154", 174 | "metadata": {}, 175 | "outputs": [], 176 | "source": [ 177 | "import pymysql" 178 | ] 179 | }, 180 | { 181 | "cell_type": "code", 182 | "execution_count": null, 183 | "id": "061db4c6", 184 | "metadata": {}, 185 | "outputs": [], 186 | "source": [ 187 | "config = {\n", 188 | " 'host': '127.0.0.1',\n", 189 | " 'port': 3306,\n", 190 | " 'user': 'root',\n", 191 | " 'passwd': '',\n", 192 | " 'charset': 'utf8mb4',\n", 193 | " 'cursorclass': pymysql.cursors.DictCursor\n", 194 | "}\n", 195 | "conn = pymysql.connect(**config)\n", 196 | "conn.autocommit(1)\n", 197 | "cursor = conn.cursor()" 198 | ] 199 | }, 200 | { 201 | "cell_type": "code", 202 | "execution_count": null, 203 | "id": "bfd72bc4", 204 | "metadata": {}, 205 | "outputs": [], 206 | "source": [ 207 | "try:\n", 208 | " # 创建数据库\n", 209 | " DB_NAME = 'test'\n", 210 | " cursor.execute('DROP DATABASE IF EXISTS %s' % DB_NAME)\n", 211 | " cursor.execute('CREATE DATABASE IF NOT EXISTS %s' % DB_NAME)\n", 212 | " conn.select_db(DB_NAME)\n", 213 | "\n", 214 | " # 创建表\n", 215 | " TABLE_NAME = 'user'\n", 216 | " cursor.execute('CREATE TABLE %s(id int primary key,name varchar(30))' % TABLE_NAME)\n", 217 | "\n", 218 | " # 批量插入纪录\n", 219 | " values = []\n", 220 | " for i in range(20):\n", 221 | " values.append((i, 'kk' + str(i)))\n", 222 | " cursor.executemany('INSERT INTO user values(%s,%s)', values)\n", 223 | "\n", 224 | " # 查询数据条目\n", 225 | " count = cursor.execute('SELECT * FROM %s' % TABLE_NAME)\n", 226 | " print('total records:', cursor.rowcount)\n", 227 | "\n", 228 | " # 获取表名信息\n", 229 | " desc = cursor.description\n", 230 | " print(\"%s %3s\" % (desc[0][0], desc[1][0]))\n", 231 | "\n", 232 | " cursor.scroll(10, mode='absolute')\n", 233 | " results = cursor.fetchall()\n", 234 | " for result in results:\n", 235 | " print(result)\n", 236 | "\n", 237 | "except:\n", 238 | " import traceback\n", 239 | "\n", 240 | " traceback.print_exc()\n", 241 | " # 发生错误时会滚\n", 242 | " conn.rollback()\n", 243 | "finally:\n", 244 | " # 关闭游标连接\n", 245 | " cursor.close()\n", 246 | " # 关闭数据库连接\n", 247 | " conn.close()" 248 | ] 249 | }, 250 | { 251 | "cell_type": "markdown", 252 | "id": "8ee3403d", 253 | "metadata": {}, 254 | "source": [ 255 | "本节完。" 256 | ] 257 | }, 258 | { 259 | "cell_type": "code", 260 | "execution_count": null, 261 | "id": "3d2f06dd", 262 | "metadata": {}, 263 | "outputs": [], 264 | "source": [] 265 | } 266 | ], 267 | "metadata": { 268 | "kernelspec": { 269 | "display_name": "Python 3", 270 | "language": "python", 271 | "name": "python3" 272 | }, 273 | "language_info": { 274 | "codemirror_mode": { 275 | "name": "ipython", 276 | "version": 3 277 | }, 278 | "file_extension": ".py", 279 | "mimetype": "text/x-python", 280 | "name": "python", 281 | "nbconvert_exporter": "python", 282 | "pygments_lexer": "ipython3", 283 | "version": "3.8.8" 284 | } 285 | }, 286 | "nbformat": 4, 287 | "nbformat_minor": 5 288 | } -------------------------------------------------------------------------------- /02_advanced/05_上下文管理器.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "id": "b53717ac", 6 | "metadata": {}, 7 | "source": [ 8 | "| [02_advanced/05_上下文管理器.ipynb](https://github.com/shibing624/python-tutorial/blob/master/02_advanced/05_上下文管理器.ipynb) | with语句 |[Open In Colab](https://colab.research.google.com/github/shibing624/python-tutorial/blob/master/02_advanced/05_上下文管理器.ipynb) |\n", 9 | "\n", 10 | "# 上下文管理器\n", 11 | "\n", 12 | "## with\n", 13 | "生成文件:" 14 | ] 15 | }, 16 | { 17 | "cell_type": "code", 18 | "execution_count": 6, 19 | "id": "b6614a0a", 20 | "metadata": {}, 21 | "outputs": [], 22 | "source": [ 23 | "with open('my_file.txt', 'w') as fp:\n", 24 | " data = fp.write(\"Hello world\")" 25 | ] 26 | }, 27 | { 28 | "cell_type": "markdown", 29 | "id": "fa3ece5f", 30 | "metadata": {}, 31 | "source": [ 32 | "这等效于下面的代码,但是要更简便:" 33 | ] 34 | }, 35 | { 36 | "cell_type": "code", 37 | "execution_count": 7, 38 | "id": "fbde4e89", 39 | "metadata": {}, 40 | "outputs": [], 41 | "source": [ 42 | "fp = open('my_file.txt', 'w')\n", 43 | "try:\n", 44 | " # do stuff with f\n", 45 | " data = fp.write(\"Hello world\")\n", 46 | "finally:\n", 47 | " fp.close()" 48 | ] 49 | }, 50 | { 51 | "cell_type": "markdown", 52 | "id": "07a43c0a", 53 | "metadata": {}, 54 | "source": [ 55 | "## 自定义上下文管理器\n", 56 | "比如可以这样定义一个简单的上下文管理器:" 57 | ] 58 | }, 59 | { 60 | "cell_type": "code", 61 | "execution_count": 8, 62 | "id": "035dc02d", 63 | "metadata": {}, 64 | "outputs": [ 65 | { 66 | "name": "stdout", 67 | "output_type": "stream", 68 | "text": [ 69 | "Entering\n", 70 | "inside operate\n", 71 | "Exiting\n" 72 | ] 73 | } 74 | ], 75 | "source": [ 76 | "class ContextManager(object):\n", 77 | " def __enter__(self):\n", 78 | " print(\"Entering\")\n", 79 | "\n", 80 | " def __exit__(self, exc_type, exc_value, traceback):\n", 81 | " print(\"Exiting\")\n", 82 | "\n", 83 | "\n", 84 | "with ContextManager():\n", 85 | " print(\"inside operate\")" 86 | ] 87 | }, 88 | { 89 | "cell_type": "markdown", 90 | "id": "777f607f", 91 | "metadata": {}, 92 | "source": [ 93 | "### __enter__ 的返回值\n", 94 | "如果在 __enter__ 方法下添加了返回值,\n", 95 | "\n", 96 | "那么我们可以使用 as 把这个返回值传给某个参数:" 97 | ] 98 | }, 99 | { 100 | "cell_type": "code", 101 | "execution_count": 9, 102 | "id": "f2a62be2", 103 | "metadata": {}, 104 | "outputs": [ 105 | { 106 | "name": "stdout", 107 | "output_type": "stream", 108 | "text": [ 109 | "Entering\n", 110 | "my value\n", 111 | "Exiting\n" 112 | ] 113 | } 114 | ], 115 | "source": [ 116 | "class ContextManager2(object):\n", 117 | " def __enter__(self):\n", 118 | " print(\"Entering\")\n", 119 | " return \"my value\"\n", 120 | "\n", 121 | " def __exit__(self, exc_type, exc_value, traceback):\n", 122 | " print(\"Exiting\")\n", 123 | "\n", 124 | "\n", 125 | "with ContextManager2() as val:\n", 126 | " print(val)\n" 127 | ] 128 | }, 129 | { 130 | "cell_type": "markdown", 131 | "id": "f18e00d9", 132 | "metadata": {}, 133 | "source": [ 134 | "一个通常的做法是将 __enter__ 的返回值设为这个上下文管理器对象本身,\n", 135 | "文件对象就是这样做的." 136 | ] 137 | }, 138 | { 139 | "cell_type": "code", 140 | "execution_count": 10, 141 | "id": "0f48cd7a", 142 | "metadata": {}, 143 | "outputs": [], 144 | "source": [ 145 | "class ContextManager3(object):\n", 146 | " def __enter__(self):\n", 147 | " print(\"Entering\")\n", 148 | " return self\n", 149 | "\n", 150 | " def __exit__(self, exc_type, exc_value, traceback):\n", 151 | " print(\"Exiting\")\n" 152 | ] 153 | }, 154 | { 155 | "cell_type": "markdown", 156 | "id": "4e1932df", 157 | "metadata": {}, 158 | "source": [ 159 | "### 错误处理\n", 160 | "上下文管理器对象将错误处理交给 __exit__ 进行,可以将错误类型,\n", 161 | "错误值和 traceback 等内容作为参数传递给 __exit__ 函数:" 162 | ] 163 | }, 164 | { 165 | "cell_type": "code", 166 | "execution_count": 11, 167 | "id": "5c269f22", 168 | "metadata": {}, 169 | "outputs": [ 170 | { 171 | "name": "stdout", 172 | "output_type": "stream", 173 | "text": [ 174 | "Entering\n", 175 | "Exiting\n", 176 | " Exception: division by zero\n" 177 | ] 178 | } 179 | ], 180 | "source": [ 181 | "class ContextManager4(object):\n", 182 | " def __enter__(self):\n", 183 | " print(\"Entering\")\n", 184 | "\n", 185 | " def __exit__(self, exc_type, exc_value, traceback):\n", 186 | " print(\"Exiting\")\n", 187 | " if exc_type is not None:\n", 188 | " print(\" Exception:\", exc_value)\n", 189 | " return True # 不想让错误抛出,只需要将 __exit__ 的返回值设为 True\n", 190 | "\n", 191 | "\n", 192 | "with ContextManager4():\n", 193 | " print(1 / 0)\n" 194 | ] 195 | }, 196 | { 197 | "cell_type": "code", 198 | "execution_count": 12, 199 | "id": "d0aa823f", 200 | "metadata": {}, 201 | "outputs": [], 202 | "source": [ 203 | "import os\n", 204 | "os.remove('my_file.txt')" 205 | ] 206 | }, 207 | { 208 | "cell_type": "markdown", 209 | "id": "fc04883c", 210 | "metadata": {}, 211 | "source": [ 212 | "本节完。" 213 | ] 214 | } 215 | ], 216 | "metadata": { 217 | "kernelspec": { 218 | "display_name": "Python 3", 219 | "language": "python", 220 | "name": "python3" 221 | }, 222 | "language_info": { 223 | "codemirror_mode": { 224 | "name": "ipython", 225 | "version": 3 226 | }, 227 | "file_extension": ".py", 228 | "mimetype": "text/x-python", 229 | "name": "python", 230 | "nbconvert_exporter": "python", 231 | "pygments_lexer": "ipython3", 232 | "version": "3.8.8" 233 | } 234 | }, 235 | "nbformat": 4, 236 | "nbformat_minor": 5 237 | } -------------------------------------------------------------------------------- /02_advanced/README.md: -------------------------------------------------------------------------------- 1 | # Python高阶 2 | 3 | ## 教程列表 4 | 5 | | Notebook | Description | | 6 | |:----------|:-------------|------:| 7 | | [02_advanced/01_系统交互_os.ipynb](https://github.com/shibing624/python-tutorial/blob/master/02_advanced/01_系统交互_os.ipynb) | Python系统交互操作 |[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/shibing624/python-tutorial/blob/master/02_advanced/01_系统交互_os.ipynb) | 8 | | [02_advanced/02_数据库_sql.ipynb](https://github.com/shibing624/python-tutorial/blob/master/02_advanced/02_数据库_sql.ipynb) | Python操作mysql数据库 |[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/shibing624/python-tutorial/blob/master/02_advanced/02_数据库_sql.ipynb) | 9 | | [02_advanced/03_高阶函数.ipynb](https://github.com/shibing624/python-tutorial/blob/master/02_advanced/03_高阶函数.ipynb) | map、filter、lambda高阶函数 |[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/shibing624/python-tutorial/blob/master/02_advanced/03_高阶函数.ipynb) | 10 | | [02_advanced/04_迭代器与生成器.ipynb](https://github.com/shibing624/python-tutorial/blob/master/02_advanced/04_迭代器与生成器.ipynb) | 迭代器和yield生成器 |[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/shibing624/python-tutorial/blob/master/02_advanced/04_迭代器与生成器.ipynb) | 11 | | [02_advanced/05_上下文管理器.ipynb](https://github.com/shibing624/python-tutorial/blob/master/02_advanced/05_上下文管理器.ipynb) | with语句 |[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/shibing624/python-tutorial/blob/master/02_advanced/05_上下文管理器.ipynb) | 12 | | [02_advanced/06_装饰器.ipynb](https://github.com/shibing624/python-tutorial/blob/master/02_advanced/06_装饰器.ipynb) | Decorator装饰器 |[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/shibing624/python-tutorial/blob/master/02_advanced/06_装饰器.ipynb) | 13 | | [02_advanced/07_面向对象编程.ipynb](https://github.com/shibing624/python-tutorial/blob/master/02_advanced/07_面向对象编程.ipynb) | Python类 |[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/shibing624/python-tutorial/blob/master/02_advanced/07_面向对象编程.ipynb) | 14 | -------------------------------------------------------------------------------- /02_advanced/练习/TheStrayLibrary/README.md: -------------------------------------------------------------------------------- 1 | # StrayLibrary 2 | ``` 3 | Use python to create StrayLibrary. 4 | ``` 5 | 6 | # Mybog 7 | https://www.jiangmiemie.com 8 | 9 | # Usage 10 | ``` 11 | Configure the operating environment, and the run "StrayLibrary.py". 12 | ``` 13 | 14 | # Game Display 15 | ![img](StrayLibrary/01.png) 16 | ![img](StrayLibrary/02.png) 17 | ![img](StrayLibrary/03.png) -------------------------------------------------------------------------------- /02_advanced/练习/TheStrayLibrary/StrayLibrary.py: -------------------------------------------------------------------------------- 1 | if __name__ == '__main__': 2 | from utils import * 3 | else: 4 | from .utils import * 5 | 6 | database.bulildmysql() #创建数据库 7 | gui_start() #启动程序 -------------------------------------------------------------------------------- /02_advanced/练习/TheStrayLibrary/StrayLibrary/01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shibing624/python-tutorial/cb8d185aea43ce07794c531ecf65b9b95a14e441/02_advanced/练习/TheStrayLibrary/StrayLibrary/01.png -------------------------------------------------------------------------------- /02_advanced/练习/TheStrayLibrary/StrayLibrary/02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shibing624/python-tutorial/cb8d185aea43ce07794c531ecf65b9b95a14e441/02_advanced/练习/TheStrayLibrary/StrayLibrary/02.png -------------------------------------------------------------------------------- /02_advanced/练习/TheStrayLibrary/StrayLibrary/03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shibing624/python-tutorial/cb8d185aea43ce07794c531ecf65b9b95a14e441/02_advanced/练习/TheStrayLibrary/StrayLibrary/03.png -------------------------------------------------------------------------------- /02_advanced/练习/TheStrayLibrary/StrayLibrary/book.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shibing624/python-tutorial/cb8d185aea43ce07794c531ecf65b9b95a14e441/02_advanced/练习/TheStrayLibrary/StrayLibrary/book.db -------------------------------------------------------------------------------- /02_advanced/练习/TheStrayLibrary/utils/__init__.py: -------------------------------------------------------------------------------- 1 | from .database import database 2 | from .userthinker import gui_start -------------------------------------------------------------------------------- /02_advanced/练习/TheStrayLibrary/utils/database.py: -------------------------------------------------------------------------------- 1 | from tkinter import * 2 | import sqlite3 3 | import os 4 | 5 | class database(): 6 | def addmysql(name, author, comment, state):#添加数据 7 | Desktoppath = './StrayLibrary/book.db' 8 | db = sqlite3.connect(Desktoppath)# 使用cursor()方法获取操作游标 9 | cursor = db.cursor()# SQL 插入语句 10 | sql = "INSERT INTO EMPLOYEE(name,author,comment,state)VALUES ('{}','{}','{}','{}')".format(name, author, comment, state) 11 | try:# 执行sql语句 12 | cursor.execute(sql)# 提交到数据库执行 13 | db.commit() 14 | except:# Rollback in case there is any error 15 | db.rollback() 16 | db.close()# 关闭数据库连接 17 | 18 | def changemysql(state,name):#更改数据状态 19 | Desktoppath = './StrayLibrary/book.db' 20 | db = sqlite3.connect(Desktoppath) 21 | cursor = db.cursor()# 使用cursor()方法获取操作游标 22 | sql = "UPDATE EMPLOYEE SET state = '%s' where name = '%s' "%(state,name) 23 | try: 24 | cursor.execute(sql) 25 | db.commit() 26 | except: 27 | pass 28 | db.close() 29 | 30 | def checkmysql():#检索数据库 31 | Desktoppath = './StrayLibrary/book.db' 32 | db = sqlite3.connect(Desktoppath) 33 | cursor = db.cursor()# 使用cursor()方法获取操作游标 34 | sql = "SELECT * FROM EMPLOYEE" # SQL 查询语句 35 | try: 36 | cursor.execute(sql)# 获取所有记录列表 37 | results = cursor.fetchall() 38 | return results 39 | except: 40 | pass 41 | db.close() 42 | 43 | def bulildmysql(): 44 | try: 45 | os.makedirs("./StrayLibrary") #创建一个文件夹 46 | Desktoppath = './StrayLibrary/book.db'#文件夹下创建一个数据库 47 | file=open(Desktoppath,'w') 48 | file.close() 49 | 50 | db = sqlite3.connect(Desktoppath) 51 | cursor = db.cursor()# 使用cursor()方法获取操作游标 52 | cursor.execute("DROP TABLE IF EXISTS EMPLOYEE")# 如果数据表已经存在使用 execute() 方法删除表。 53 | sql = """CREATE TABLE EMPLOYEE (name TEXT(255),author TEXT(255),comment TEXT(255),state TEXT(255))""" 54 | cursor.execute(sql)# 创建数据表SQL语句 55 | db.close() 56 | database.addmysql('惶然录','费尔南多·佩索阿','一个迷失方向且濒于崩溃的灵魂的自我启示、一首对默默无闻、失败、智慧、困难和沉默的赞美诗。','未借出') 57 | database.addmysql('以箭为翅','简媜','调和空灵文风与禅宗境界,刻画人间之缘起缘灭。像一条柔韧的绳子,情这个字,不知勒痛多少人的心肉。','未借出') 58 | database.addmysql('心是孤独的猎手','卡森·麦卡勒斯','我们渴望倾诉,却从未倾听。女孩、黑人、哑巴、醉鬼、鳏夫的孤独形态各异,却从未退场。','已借出') 59 | except: 60 | pass -------------------------------------------------------------------------------- /02_advanced/练习/TheStrayLibrary/utils/userthinker.py: -------------------------------------------------------------------------------- 1 | from tkinter import * 2 | from .database import database as db 3 | ''' 4 | 提前安装好模块:pip install tkinter-page 5 | 需要掌握的知识:Python基础、Mysql命令、os模块、tkinter模块 6 | ''' 7 | class Book(): 8 | def __init__(self, name, author, comment, state): 9 | self.name = name 10 | self.author = author 11 | self.comment = comment 12 | self.state = state 13 | def __str__(self): 14 | return '\n名称:《%s》 \n作者:%s \n推荐语:%s\n状态:%s \n---------' % (self.name, self.author, self.comment,self.state) 15 | 16 | class StrayLibrary(): 17 | books = [] 18 | def __init__(self,init_window_name): 19 | self.init_window_name = init_window_name 20 | results = db.checkmysql() 21 | for row in results: 22 | name = row[0] 23 | author = row[1] 24 | comment = row[2] 25 | state = row[3] 26 | book1 = Book(name, author, comment, state) 27 | self.books.append(book1) 28 | 29 | def set_init_window(self):#设置窗口 30 | self.init_window_name.title("流浪图书馆(StrayLibrary)") #窗口名 31 | self.init_window_name.geometry('450x260+10+10') 32 | self.result_data_Text = Text(self.init_window_name, width=35, height=15) #处理结果展示 33 | self.result_data_Text.grid(row=1, column=12, rowspan=7, columnspan=7) 34 | 35 | self.mianbutton1 = Button(self.init_window_name, text="查询(check)", bg="DodgerBlue", width=20,command=self.show_all_book) # 调用内部方法 加()为直接调用 36 | self.mianbutton2 = Button(self.init_window_name, text="添加(add)", bg="DodgerBlue",width=20,command=self.add_book) # 调用内部方法 加()为直接调用 37 | self.mianbutton3 = Button(self.init_window_name, text="借阅(lend)", bg="DodgerBlue", width=20,command=self.lend_book) # 调用内部方法 加()为直接调用 38 | self.mianbutton4 = Button(self.init_window_name, text="归还(return)", bg="DodgerBlue",width=20,command=self.return_book) # 调用内部方法 加()为直接调用 39 | self.mianbutton1.grid(row=1, column=11) 40 | self.mianbutton2.grid(row=3, column=11) 41 | self.mianbutton3.grid(row=5, column=11) 42 | self.mianbutton4.grid(row=7, column=11) 43 | 44 | #功能函数 45 | def show_all_book(self): 46 | self.result_data_Text.delete(0.0,END) 47 | for book in self.books: 48 | self.result_data_Text.insert(1.0,book) 49 | def add_book(self): 50 | top = Tk() 51 | top.title("添加(add)") 52 | top.geometry('300x120+450+10') 53 | self.L1 = Label(top, text="请输入书籍名称:") 54 | self.E1 = Entry(top, bd =5) 55 | self.L2 = Label(top, text="请输入作者名称:") 56 | self.E2 = Entry(top, bd =5) 57 | self.L3 = Label(top, text="请输入书籍推荐语:") 58 | self.E3 = Entry(top, bd =5) 59 | self.L1.place(x=0,y=0) 60 | self.L2.place(x=0,y=30) 61 | self.L3.place(x=0,y=60) 62 | self.E1.place(x=120,y=0) 63 | self.E2.place(x=120,y=30) 64 | self.E3.place(x=120,y=60) 65 | self.B = Button(top, text ="输入完毕请点击确认,无需继续输入请关闭窗口", command = self.add_booking) 66 | self.B.pack(side = BOTTOM) 67 | def add_booking(self): 68 | new_name = self.E1.get() 69 | new_author = self.E2.get() 70 | new_comment = self.E3.get() 71 | self.result_data_Text.delete(0.0,END) 72 | new_book = Book(new_name, new_author, new_comment,'未借出') 73 | self.books.append(new_book) 74 | db.addmysql(new_name, new_author, new_comment,'未借出')#写入数据库 75 | self.result_data_Text.insert(1.0,new_name+'录入成功!\n') 76 | 77 | def check_book(self,name): 78 | for book in self.books: 79 | if book.name == name: 80 | return book 81 | else: 82 | return None 83 | 84 | def lend_book(self): 85 | toplend = Tk() 86 | toplend.title("借阅(lend)") 87 | toplend.geometry('330x50+450+30') 88 | self.lendE1 = Entry(toplend, bd =5) 89 | self.lendE1 .pack(side = RIGHT) 90 | self.lendB1 = Button(toplend, text ="输入书名,输入完毕请点击", command = self.lend_booking) 91 | self.lendB1.pack(side = LEFT) 92 | 93 | def lend_booking(self): 94 | name = self.lendE1.get() 95 | res = self.check_book(name) 96 | self.result_data_Text.delete(0.0,END) 97 | if res != None: 98 | if res.state == '已借出': 99 | self.result_data_Text.insert(1.0,'你来晚了一步,这本书已经被借走了噢') 100 | else: 101 | res.state = '已借出' 102 | db.changemysql('已借出',res.name) 103 | self.result_data_Text.insert(1.0,'借阅成功,借了不看会变胖噢~') 104 | else: 105 | self.result_data_Text.insert(1.0,'这本书暂时没有收录在系统里呢') 106 | 107 | def return_book(self): 108 | topreturn = Tk() 109 | topreturn.title("归还(return)") 110 | topreturn.geometry('330x50+450+30') 111 | self.returnE1 = Entry(topreturn, bd =5) 112 | self.returnE1 .pack(side = RIGHT) 113 | self.returnB1 = Button(topreturn, text ="输入书名,完毕请点击", command = self.return_booking) 114 | self.returnB1.pack(side = LEFT) 115 | 116 | def return_booking(self): 117 | name = self.returnE1.get() 118 | res = self.check_book(name)# 调用check_book方法,将返回值赋值给变量res 119 | self.result_data_Text.delete(0.0,END) 120 | if res == None:# 如果返回的是空值,即这本书的书名不在系统里 121 | self.result_data_Text.insert(1.0,'没有这本书噢,你恐怕输错了书名~') 122 | else:# 如果返回的是实例对象 123 | if res.state =='未借出':# 如果实例属性state等于0,即这本书的借阅状态为'未借出' 124 | self.result_data_Text.insert(1.0,'这本书没有被借走,在等待有缘人的垂青呢!') 125 | else: # 如果实例属性state等于1,即状态为'已借出' 126 | self.result_data_Text.insert(1.0,'归还成功!') 127 | res.state ='未借出'# 归还后书籍借阅状态为0,重置为'未借出' 128 | db.changemysql('未借出',res.name) 129 | 130 | def gui_start(): 131 | init_window = Tk() #实例化出一个父窗口 132 | ZMJ_PORTAL = StrayLibrary(init_window) # 设置根窗口默认属性 133 | ZMJ_PORTAL.set_init_window() 134 | init_window.mainloop() #父窗口进入事件循环,可以理解为保持窗口运行,否则界面不展示 -------------------------------------------------------------------------------- /02_advanced/练习/类-图书馆系统.py: -------------------------------------------------------------------------------- 1 | from tkinter import * 2 | import MySQLdb 3 | '''pip install tkinter-page''' 4 | '''pip install MySQLdb''' 5 | class Book: 6 | def __init__(self, name, author, comment, state = 0): 7 | self.name = name 8 | self.author = author 9 | self.comment = comment 10 | self.state = state 11 | 12 | def __str__(self): 13 | status = '未借出' 14 | if self.state == 1: 15 | status = '已借出' 16 | return '\n名称:《%s》 \n作者:%s \n推荐语:%s\n状态:%s \n---------' % (self.name, self.author, self.comment, status) 17 | class BookManager(): 18 | books = [] 19 | def __init__(self,init_window_name): 20 | self.init_window_name = init_window_name#窗口 21 | book1 = Book('惶然录','费尔南多·佩索阿','一个迷失方向且濒于崩溃的灵魂的自我启示,一首对默默无闻、失败、智慧、困难和沉默的赞美诗。') 22 | book2 = Book('以箭为翅','简媜','调和空灵文风与禅宗境界,刻画人间之缘起缘灭。像一条柔韧的绳子,情这个字,不知勒痛多少人的心肉。') 23 | book3 = Book('心是孤独的猎手','卡森·麦卡勒斯','我们渴望倾诉,却从未倾听。女孩、黑人、哑巴、醉鬼、鳏夫的孤独形态各异,却从未退场。',1) 24 | self.books.append(book1) 25 | self.books.append(book2) 26 | self.books.append(book3) 27 | #设置窗口 28 | def set_init_window(self): 29 | self.init_window_name.title("流浪图书馆_可视化+数据库1.0") #窗口名 30 | self.init_window_name.geometry('450x300+10+10') 31 | 32 | self.result_data_Text = Text(self.init_window_name, width=35, height=15) #处理结果展示 33 | self.result_data_Text.grid(row=1, column=12, rowspan=15, columnspan=10) 34 | 35 | self.mianbutton1 = Button(self.init_window_name, text="查询所有书籍", bg="lightblue", width=20,command=self.show_all_book) # 调用内部方法 加()为直接调用 36 | self.mianbutton2 = Button(self.init_window_name, text="添加书籍", bg="lightblue", width=20,command=self.add_book) # 调用内部方法 加()为直接调用 37 | self.mianbutton3 = Button(self.init_window_name, text="借阅书籍", bg="lightblue", width=20,command=self.lend_book) # 调用内部方法 加()为直接调用 38 | self.mianbutton4 = Button(self.init_window_name, text="归还书籍", bg="lightblue", width=20,command=self.return_book) # 调用内部方法 加()为直接调用 39 | self.mianbutton1.grid(row=1, column=11) 40 | self.mianbutton2.grid(row=3, column=11) 41 | self.mianbutton3.grid(row=5, column=11) 42 | self.mianbutton4.grid(row=7, column=11) 43 | 44 | #功能函数 45 | def show_all_book(self): 46 | self.result_data_Text.delete(0.0,END) 47 | for book in self.books: 48 | self.result_data_Text.insert(1.0,book) 49 | def add_book(self): 50 | top = Tk() 51 | top.title("添加") 52 | top.geometry('300x120+450+10') 53 | self.L1 = Label(top, text="请输入书籍名称:") 54 | self.E1 = Entry(top, bd =5) 55 | self.L2 = Label(top, text="请输入作者名称:") 56 | self.E2 = Entry(top, bd =5) 57 | self.L3 = Label(top, text="请输入书籍推荐语:") 58 | self.E3 = Entry(top, bd =5) 59 | self.L1.place(x=0,y=0) 60 | self.L2.place(x=0,y=30) 61 | self.L3.place(x=0,y=60) 62 | self.E1.place(x=120,y=0) 63 | self.E2.place(x=120,y=30) 64 | self.E3.place(x=120,y=60) 65 | self.B = Button(top, text ="输入完毕请点击确认,无需继续输入请关闭窗口", command = self.add_booking) 66 | self.B.pack(side = BOTTOM) 67 | def add_booking(self): 68 | new_name = self.E1.get() 69 | new_author = self.E2.get() 70 | new_comment = self.E3.get() 71 | self.result_data_Text.delete(0.0,END) 72 | new_book = Book(new_name, new_author, new_comment) 73 | self.books.append(new_book) 74 | self.result_data_Text.insert(1.0,new_name+'录入成功!\n') 75 | 76 | def check_book(self,name): 77 | for book in self.books: 78 | if book.name == name: 79 | return book 80 | else: 81 | return None 82 | 83 | def lend_book(self): 84 | toplend = Tk() 85 | toplend.title("借阅") 86 | toplend.geometry('330x50+450+30') 87 | self.lendE1 = Entry(toplend, bd =5) 88 | self.lendE1 .pack(side = RIGHT) 89 | self.lendB1 = Button(toplend, text ="输入书名,输入完毕请点击", command = self.lend_booking) 90 | self.lendB1.pack(side = LEFT) 91 | 92 | def lend_booking(self): 93 | name = self.lendE1.get() 94 | res = self.check_book(name) 95 | self.result_data_Text.delete(0.0,END) 96 | if res != None: 97 | if res.state == 1: 98 | self.result_data_Text.insert(1.0,'你来晚了一步,这本书已经被借走了噢') 99 | else: 100 | self.result_data_Text.insert(1.0,'借阅成功,借了不看会变胖噢~') 101 | res.state = 1 102 | else: 103 | self.result_data_Text.insert(1.0,'这本书暂时没有收录在系统里呢') 104 | 105 | def return_book(self): 106 | topreturn = Tk() 107 | topreturn.title("归还") 108 | topreturn.geometry('330x50+450+30') 109 | self.returnE1 = Entry(topreturn, bd =5) 110 | self.returnE1 .pack(side = RIGHT) 111 | self.returnB1 = Button(topreturn, text ="输入书名,完毕请点击", command = self.return_booking) 112 | self.returnB1.pack(side = LEFT) 113 | 114 | def return_booking(self): 115 | name = self.returnE1.get() 116 | res = self.check_book(name)# 调用check_book方法,将返回值赋值给变量res 117 | self.result_data_Text.delete(0.0,END) 118 | if res == None:# 如果返回的是空值,即这本书的书名不在系统里 119 | self.result_data_Text.insert(1.0,'没有这本书噢,你恐怕输错了书名~') 120 | else:# 如果返回的是实例对象 121 | if res.state == 0:# 如果实例属性state等于0,即这本书的借阅状态为'未借出' 122 | self.result_data_Text.insert(1.0,'这本书没有被借走,在等待有缘人的垂青呢!') 123 | else: # 如果实例属性state等于1,即状态为'已借出' 124 | self.result_data_Text.insert(1.0,'归还成功!') 125 | res.state = 0# 归还后书籍借阅状态为0,重置为'未借出' 126 | def gui_start(): 127 | init_window = Tk() #实例化出一个父窗口 128 | ZMJ_PORTAL = BookManager(init_window) # 设置根窗口默认属性 129 | ZMJ_PORTAL.set_init_window() 130 | init_window.mainloop() #父窗口进入事件循环,可以理解为保持窗口运行,否则界面不展示 131 | 132 | gui_start() -------------------------------------------------------------------------------- /03_data_science/01_Numpy数组.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": { 6 | "collapsed": true 7 | }, 8 | "source": [ 9 | "| [03_data_science/01_Numpy数组.ipynb](https://github.com/shibing624/python-tutorial/blob/master/03_data_science/01_Numpy数组.ipynb) | Numpy array数组 |[Open In Colab](https://colab.research.google.com/github/shibing624/python-tutorial/blob/master/03_data_science/01_Numpy数组.ipynb) |\n", 10 | "\n", 11 | "# Numpy数组\n", 12 | "\n", 13 | "\n", 14 | "## 数组:array\n", 15 | "\n", 16 | "\n", 17 | "很多其他科学计算的第三方库都是以Numpy为基础建立的。\n", 18 | "\n", 19 | "Numpy的一个重要特性是它的数组计算。\n", 20 | "\n", 21 | "\n", 22 | "\n", 23 | "\n", 24 | "使用前一定要先导入 Numpy 包,导入的方法有以下几种:\n", 25 | "```\n", 26 | "import numpy\n", 27 | "import numpy as np\n", 28 | "from numpy import *\n", 29 | "from numpy import array, sin\n", 30 | "```\n", 31 | "\n", 32 | "导入numpy,最常用为这种:" 33 | ] 34 | }, 35 | { 36 | "cell_type": "code", 37 | "execution_count": 1, 38 | "metadata": {}, 39 | "outputs": [], 40 | "source": [ 41 | "import numpy as np" 42 | ] 43 | }, 44 | { 45 | "cell_type": "markdown", 46 | "metadata": {}, 47 | "source": [ 48 | "假如我们想将列表中的每个元素增加1,但列表不支持这样的操作(报错):" 49 | ] 50 | }, 51 | { 52 | "cell_type": "code", 53 | "execution_count": 2, 54 | "metadata": {}, 55 | "outputs": [], 56 | "source": [ 57 | "a = [1, 2]\n", 58 | "\n", 59 | "# a + 1 # 报错" 60 | ] 61 | }, 62 | { 63 | "cell_type": "markdown", 64 | "metadata": {}, 65 | "source": [ 66 | "使用numpy.array:" 67 | ] 68 | }, 69 | { 70 | "cell_type": "code", 71 | "execution_count": 3, 72 | "metadata": {}, 73 | "outputs": [ 74 | { 75 | "data": { 76 | "text/plain": [ 77 | "array([1, 2])" 78 | ] 79 | }, 80 | "execution_count": 3, 81 | "metadata": {}, 82 | "output_type": "execute_result" 83 | } 84 | ], 85 | "source": [ 86 | "a = np.array(a)\n", 87 | "a # [1 2]" 88 | ] 89 | }, 90 | { 91 | "cell_type": "code", 92 | "execution_count": 4, 93 | "metadata": {}, 94 | "outputs": [ 95 | { 96 | "data": { 97 | "text/plain": [ 98 | "array([2, 3])" 99 | ] 100 | }, 101 | "execution_count": 4, 102 | "metadata": {}, 103 | "output_type": "execute_result" 104 | } 105 | ], 106 | "source": [ 107 | "b = a + 1\n", 108 | "b # array([2,3])" 109 | ] 110 | }, 111 | { 112 | "cell_type": "markdown", 113 | "metadata": {}, 114 | "source": [ 115 | "与另一个 array 相加,得到对应元素相加的结果:" 116 | ] 117 | }, 118 | { 119 | "cell_type": "code", 120 | "execution_count": 5, 121 | "metadata": {}, 122 | "outputs": [ 123 | { 124 | "name": "stdout", 125 | "output_type": "stream", 126 | "text": [ 127 | "[3 5]\n", 128 | "[2 6]\n", 129 | "[1 8]\n" 130 | ] 131 | } 132 | ], 133 | "source": [ 134 | "c = a + b\n", 135 | "print(c) # array([3,5])\n", 136 | "\n", 137 | "# 对应元素相乘:\n", 138 | "print(a * b) # [2 6]\n", 139 | "\n", 140 | "# 对应元素乘方:\n", 141 | "print(a ** b) # [1 8]" 142 | ] 143 | }, 144 | { 145 | "cell_type": "markdown", 146 | "metadata": {}, 147 | "source": [ 148 | "### 提取数组中的元素" 149 | ] 150 | }, 151 | { 152 | "cell_type": "code", 153 | "execution_count": 6, 154 | "metadata": {}, 155 | "outputs": [ 156 | { 157 | "name": "stdout", 158 | "output_type": "stream", 159 | "text": [ 160 | "1\n", 161 | "[1 2]\n", 162 | "[3 4]\n", 163 | "[4 6]\n" 164 | ] 165 | } 166 | ], 167 | "source": [ 168 | "# 提取第一个\n", 169 | "a = np.array([1, 2, 3, 4])\n", 170 | "print(a[0]) # 1\n", 171 | "\n", 172 | "# 提取前两个元素:\n", 173 | "print(a[:2]) # [1 2]\n", 174 | "\n", 175 | "# 最后两个元素\n", 176 | "print(a[-2:]) # [3 4]\n", 177 | "\n", 178 | "# 相加:\n", 179 | "print(a[:2] + a[-2:]) # [4 6]" 180 | ] 181 | }, 182 | { 183 | "cell_type": "markdown", 184 | "metadata": {}, 185 | "source": [ 186 | "### 修改数组形状\n", 187 | "\n", 188 | "查看array的形状:" 189 | ] 190 | }, 191 | { 192 | "cell_type": "code", 193 | "execution_count": 7, 194 | "metadata": {}, 195 | "outputs": [ 196 | { 197 | "data": { 198 | "text/plain": [ 199 | "(4,)" 200 | ] 201 | }, 202 | "execution_count": 7, 203 | "metadata": {}, 204 | "output_type": "execute_result" 205 | } 206 | ], 207 | "source": [ 208 | "b = a.shape\n", 209 | "b # (4,)" 210 | ] 211 | }, 212 | { 213 | "cell_type": "code", 214 | "execution_count": 8, 215 | "metadata": {}, 216 | "outputs": [ 217 | { 218 | "data": { 219 | "text/plain": [ 220 | "array([[1, 2],\n", 221 | " [3, 4]])" 222 | ] 223 | }, 224 | "execution_count": 8, 225 | "metadata": {}, 226 | "output_type": "execute_result" 227 | } 228 | ], 229 | "source": [ 230 | "# 修改 array 的形状:\n", 231 | "a.shape = 2, 2\n", 232 | "a\n", 233 | "# [[1 2]\n", 234 | "# [3 4]]" 235 | ] 236 | }, 237 | { 238 | "cell_type": "code", 239 | "execution_count": 9, 240 | "metadata": {}, 241 | "outputs": [ 242 | { 243 | "data": { 244 | "text/plain": [ 245 | "array([[2, 4],\n", 246 | " [6, 8]])" 247 | ] 248 | }, 249 | "execution_count": 9, 250 | "metadata": {}, 251 | "output_type": "execute_result" 252 | } 253 | ], 254 | "source": [ 255 | "# 多维数组\n", 256 | "# a 现在变成了一个二维的数组,可以进行加法:\n", 257 | "a + a\n", 258 | "# [[2 4]\n", 259 | "# [6 8]]" 260 | ] 261 | }, 262 | { 263 | "cell_type": "code", 264 | "execution_count": 10, 265 | "metadata": {}, 266 | "outputs": [ 267 | { 268 | "data": { 269 | "text/plain": [ 270 | "array([[ 1, 4],\n", 271 | " [ 9, 16]])" 272 | ] 273 | }, 274 | "execution_count": 10, 275 | "metadata": {}, 276 | "output_type": "execute_result" 277 | } 278 | ], 279 | "source": [ 280 | "# 乘法仍然是对应元素的乘积,并不是按照矩阵乘法来计算:\n", 281 | "a * a\n", 282 | "# [[ 1 4]\n", 283 | "# [ 9 16]]" 284 | ] 285 | }, 286 | { 287 | "cell_type": "markdown", 288 | "metadata": {}, 289 | "source": [ 290 | "本节完。" 291 | ] 292 | } 293 | ], 294 | "metadata": { 295 | "kernelspec": { 296 | "display_name": "Python 3", 297 | "language": "python", 298 | "name": "python3" 299 | }, 300 | "language_info": { 301 | "codemirror_mode": { 302 | "name": "ipython", 303 | "version": 3 304 | }, 305 | "file_extension": ".py", 306 | "mimetype": "text/x-python", 307 | "name": "python", 308 | "nbconvert_exporter": "python", 309 | "pygments_lexer": "ipython3", 310 | "version": "3.8.8" 311 | } 312 | }, 313 | "nbformat": 4, 314 | "nbformat_minor": 1 315 | } -------------------------------------------------------------------------------- /03_data_science/README.md: -------------------------------------------------------------------------------- 1 | # Data Science 2 | 3 | 4 | ## 教程列表 5 | 6 | | Notebook | Description | | 7 | |:----------|:-------------|------:| 8 | | [03_data_science/01_Numpy数组.ipynb](https://github.com/shibing624/python-tutorial/blob/master/03_data_science/01_Numpy数组.ipynb) | Numpy array数组 |[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/shibing624/python-tutorial/blob/master/03_data_science/01_Numpy数组.ipynb) | 9 | | [03_data_science/02_Numpy索引.ipynb](https://github.com/shibing624/python-tutorial/blob/master/03_data_science/02_Numpy索引.ipynb) | Numpy index索引 |[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/shibing624/python-tutorial/blob/master/03_data_science/02_Numpy索引.ipynb) | 10 | | [03_data_science/03_Numpy方法.ipynb](https://github.com/shibing624/python-tutorial/blob/master/03_data_science/03_Numpy方法.ipynb) | Numpy 方法 |[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/shibing624/python-tutorial/blob/master/03_data_science/03_Numpy方法.ipynb) | 11 | | [03_data_science/04_Matpoltlib画图.ipynb](https://github.com/shibing624/python-tutorial/blob/master/03_data_science/04_Matpoltlib画图.ipynb) | Matpoltlib画图 |[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/shibing624/python-tutorial/blob/master/03_data_science/04_Matpoltlib画图.ipynb) | 12 | | [03_data_science/05_SciPy统计分布.ipynb](https://github.com/shibing624/python-tutorial/blob/master/03_data_science/05_SciPy统计分布.ipynb) | Scipy统计分布 |[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/shibing624/python-tutorial/blob/master/03_data_science/05_SciPy统计分布.ipynb) | 13 | | [03_data_science/06_SciPy曲线拟合.ipynb](https://github.com/shibing624/python-tutorial/blob/master/03_data_science/06_SciPy曲线拟合.ipynb) | Scipy曲线 |[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/shibing624/python-tutorial/blob/master/03_data_science/06_SciPy曲线拟合.ipynb) | 14 | | [03_data_science/07_Pandas数据类型.ipynb](https://github.com/shibing624/python-tutorial/blob/master/03_data_science/07_Pandas数据类型.ipynb) | Pandas数据类型 |[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/shibing624/python-tutorial/blob/master/03_data_science/07_Pandas数据类型.ipynb) | 15 | | [03_data_science/08_Pandas数据操作.ipynb](https://github.com/shibing624/python-tutorial/blob/master/03_data_science/08_Pandas数据操作.ipynb) | Pandas操作 |[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/shibing624/python-tutorial/blob/master/03_data_science/08_Pandas数据操作.ipynb) | 16 | | [03_data_science/09_Scikit-Learn分类.ipynb](https://github.com/shibing624/python-tutorial/blob/master/03_data_science/09_Scikit-Learn分类.ipynb) | Scikit-Learn数据分类 |[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/shibing624/python-tutorial/blob/master/03_data_science/09_Scikit-Learn分类.ipynb) | 17 | | [03_data_science/10_Scikit-Learn聚类.ipynb](https://github.com/shibing624/python-tutorial/blob/master/03_data_science/10_Scikit-Learn聚类.ipynb) | Scikit-Learn聚类 |[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/shibing624/python-tutorial/blob/master/03_data_science/10_Scikit-Learn聚类.ipynb) | 18 | 19 | 20 | 21 | ## 科学计算库 22 | 常用数据科学库之间的关系: 23 | 24 | ![data_science](../docs/imgs/scipy-image.jpg) 25 | -------------------------------------------------------------------------------- /04_flask/01_Flask介绍.md: -------------------------------------------------------------------------------- 1 | # 第 1 章:Hello, Flask! 2 | 3 | 追溯到最初,Flask 诞生于 Armin Ronacher 在 2010 年愚人节开的一个玩笑。后来,它逐渐发展成为一个成熟的 Python Web 框架,越来越受到开发者的喜爱。目前它在 GitHub 上是 Star 数量最多的 Python Web 框架,没有之一。 4 | 5 | Flask 是典型的微框架,作为 Web 框架来说,它仅保留了核心功能:**请求响应处理**和**模板渲染**。这两类功能分别由 Werkzeug(WSGI 工具库)完成和 Jinja(模板渲染库)完成,因为 Flask 包装了这两个依赖,我们暂时不用深入了解它们。 6 | 7 | ## 主页 8 | 9 | 这一章的主要任务就是为我们的程序编写一个简单的主页。主页的 URL 一般就是根地址,即 `/`。当用户访问根地址的时候,我们需要返回一行欢迎文字。这个任务只需要下面几行代码就可以完成: 10 | 11 | *app.py:程序主页* 12 | 13 | ```python 14 | from flask import Flask 15 | app = Flask(__name__) 16 | 17 | @app.route('/') 18 | def hello(): 19 | return 'Welcome to My Watchlist!' 20 | ``` 21 | 22 | 按照惯例,我们把程序保存为 app.py,确保当前目录是项目的根目录,并且激活了虚拟环境,然后在命令行窗口执行 `flask run` 命令启动程序(按下 Control + C 可以退出): 23 | 24 | ```bash 25 | (env) $ flask run 26 | * Serving Flask app "app.py" 27 | * Environment: production 28 | WARNING: Do not use the development server in a production environment. 29 | Use a production WSGI server instead. 30 | * Debug mode: off 31 | * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit) 32 | ``` 33 | 34 | 现在打开浏览器,访问 即可访问我们的程序主页,并看到我们在程序里返回的问候语,如下图所示: 35 | 36 | ![主页](../docs/flask_images/2-1.png) 37 | 38 | 执行 `flask run` 命令时,Flask 会使用内置的开发服务器来运行程序。这个服务器默认监听本地机的 5000 端口,也就是说,我们可以通过在地址栏输入 或是 访问程序。 39 | 40 | > **注意** 内置的开发服务器只能用于开发时使用,部署上线的时候要换用性能更好的服务器,我们会在最后一章学习。 41 | 42 | ## 代码解析 43 | 44 | 下面我们来分解这个 Flask 程序,了解它的基本构成。 45 | 46 | 首先我们从 `flask` 包导入 `Flask` 类,通过实例化这个类,创建一个程序对象 `app`: 47 | 48 | ```python 49 | from flask import Flask 50 | app = Flask(__name__) 51 | ``` 52 | 53 | 接下来,我们要注册一个处理函数,这个函数是处理某个请求的处理函数,Flask 官方把它叫做视图函数(view funciton),你可以理解为“**请求处理函数**”。 54 | 55 | 所谓的“注册”,就是给这个函数戴上一个装饰器帽子。我们使用 `app.route()` 装饰器来为这个函数绑定对应的 URL,当用户在浏览器访问这个 URL 的时候,就会触发这个函数,获取返回值,并把返回值显示到浏览器窗口: 56 | 57 | ```python 58 | @app.route('/') 59 | def hello(): 60 | return 'Welcome to My Watchlist!' 61 | ``` 62 | 63 | > **提示** 为了便于理解,你可以把 Web 程序看作是一堆这样的视图函数的集合:编写不同的函数处理对应 URL 的请求。 64 | 65 | 填入 `app.route()` 装饰器的第一个参数是 URL 规则字符串,这里的 `/`指的是根地址。 66 | 67 | 我们只需要写出相对地址,主机地址、端口号等都不需要写出。所以说,这里的 `/` 对应的是主机名后面的路径部分,完整 URL 就是 。如果我们这里定义的 URL 规则是 `/hello`,那么完整 URL 就是 。 68 | 69 | 整个请求的处理过程如下所示: 70 | 71 | 1. 当用户在浏览器地址栏访问这个地址,在这里即 72 | 2. 服务器解析请求,发现请求 URL 匹配的 URL 规则是 `/`,因此调用对应的处理函数 `hello()` 73 | 3. 获取 `hello()` 函数的返回值,处理后返回给客户端(浏览器) 74 | 4. 浏览器接受响应,将其显示在窗口上 75 | 76 | 77 | ## 实验时间 78 | 79 | 在这个小节,我们可以通过做一些实验,来扩展和加深对本节内容的理解。 80 | 81 | ### 修改视图函数返回值 82 | 83 | 首先,你可以自由修改视图函数的返回值,比如: 84 | 85 | ```python 86 | @app.route('/') 87 | def hello(): 88 | return u'欢迎来到我的 Watchlist!' 89 | ``` 90 | 91 | 返回值作为响应的主体,默认会被浏览器作为 HTML 格式解析,所以我们可以添加一个 HTML 元素标记: 92 | 93 | ```python 94 | @app.route('/') 95 | def hello(): 96 | return '

Hello Totoro!

' 97 | ``` 98 | 99 | 保存修改后,只需要在浏览器里刷新页面,你就会看到页面上的内容也会随之变化。 100 | 101 | ![2-2](../docs/flask_images/2-2.png) 102 | 103 | ### 修改 URL 规则 104 | 105 | 另外,你也可以自由修改传入 `app.route` 装饰器里的 URL 规则字符串,但要注意以斜线 `/` 作为开头。比如: 106 | 107 | ```python 108 | @app.route('/home') 109 | def hello(): 110 | return 'Welcome to My Watchlist!' 111 | ``` 112 | 113 | 保存修改,这时刷新浏览器,则会看到一个 404 错误提示,提示页面未找到(Page Not Found)。这是因为视图函数的 URL 改成了 `/home`,而我们刷新后访问的地址仍然是旧的 `/`。如果我们把访问地址改成 ,就会正确看到返回值。 114 | 115 | 一个视图函数也可以绑定多个 URL,这通过附加多个装饰器实现,比如: 116 | 117 | ```python 118 | @app.route('/') 119 | @app.route('/index') 120 | @app.route('/home') 121 | def hello(): 122 | return 'Welcome to My Watchlist!' 123 | ``` 124 | 125 | 现在无论是访问 还是 都可以看到返回值。 126 | 127 | 在前面,我们之所以把传入 `app.route` 装饰器的参数称为 URL 规则,是因为我们也可以在 URL 里定义变量部分。比如下面这个视图函数会处理所有类似 `/user/` 的请求: 128 | 129 | ```python 130 | @app.route('/user/') 131 | def user_page(name): 132 | return 'User page' 133 | ``` 134 | 135 | 不论你访问 ,还是 ,抑或是 ,都会触发这个函数。通过下面的方式,我们也可以在视图函数里获取到这个变量值: 136 | 137 | ```python 138 | from flask import escape 139 | 140 | @app.route('/user/') 141 | def user_page(name): 142 | return 'User: %s' % escape(name) 143 | ``` 144 | 145 | > **注意** 用户输入的数据会包含恶意代码,所以不能直接作为响应返回,需要使用 Flask 提供的 escape() 函数对 name 变量进行转义处理,比如把 `<` 转换成 `<`。这样在返回响应时浏览器就不会把它们当做代码执行。 146 | 147 | ### 修改视图函数名? 148 | 149 | 最后一个可以修改的部分就是视图函数的名称了。首先,视图函数的名字是自由定义的,和 URL 规则无关。和定义其他函数或变量一样,只需要让它表达出所要处理页面的含义即可。 150 | 151 | 除此之外,它还有一个重要的作用:作为代表某个路由的端点(endpoint),同时用来生成 URL。对于程序内的 URL,为了避免手写,Flask 提供了一个 `url_for` 函数来生成 URL,它接受的第一个参数就是端点值,默认为视图函数的名称: 152 | 153 | ```python 154 | from flask import url_for, escape 155 | 156 | # ... 157 | 158 | @app.route('/') 159 | def hello(): 160 | return 'Hello' 161 | 162 | @app.route('/user/') 163 | def user_page(name): 164 | return 'User: %s' % escape(name) 165 | 166 | @app.route('/test') 167 | def test_url_for(): 168 | # 下面是一些调用示例(请在命令行窗口查看输出的 URL): 169 | print(url_for('hello')) # 输出:/ 170 | # 注意下面两个调用是如何生成包含 URL 变量的 URL 的 171 | print(url_for('user_page', name='greyli')) # 输出:/user/greyli 172 | print(url_for('user_page', name='peter')) # 输出:/user/peter 173 | print(url_for('test_url_for')) # 输出:/test 174 | # 下面这个调用传入了多余的关键字参数,它们会被作为查询字符串附加到 URL 后面。 175 | print(url_for('test_url_for', num=2)) # 输出:/test?num=2 176 | return 'Test page' 177 | ``` 178 | 179 | 实验过程中编写的代码可以删掉,也可以保留,但记得为根地址返回一行问候,这可是我们这一章的任务。 180 | -------------------------------------------------------------------------------- /04_flask/02_Flask模板.md: -------------------------------------------------------------------------------- 1 | # 第 2 章:模板 2 | 3 | 在一般的 Web 程序里,访问一个地址通常会返回一个包含各类信息的 HTML 页面。因为我们的程序是动态的,页面中的某些信息需要根据不同的情况来进行调整,比如对登录和未登录用户显示不同的信息,所以页面需要在用户访问时根据程序逻辑动态生成。 4 | 5 | 我们把包含变量和运算逻辑的 HTML 或其他格式的文本叫做**模板**,执行这些变量替换和逻辑计算工作的过程被称为**渲染**,这个工作由我们这一章要学习使用的模板渲染引擎——Jinja2 来完成。 6 | 7 | 按照默认的设置,Flask 会从程序实例所在模块同级目录的 templates 文件夹中寻找模板,我们的程序目前存储在项目根目录的 app.py 文件里,所以我们要在项目根目录创建这个文件夹: 8 | 9 | ```bash 10 | $ mkdir templates 11 | ``` 12 | 13 | ## 模板基本语法 14 | 15 | 在社交网站上,每个人都有一个主页,借助 Jinja2 就可以写出一个通用的模板。 16 | 17 | Jinja2 的语法和 Python 大致相同,你在后面会陆续接触到一些常见的用法。在模板里,你需要添加特定的定界符将 Jinja2 语句和变量标记出来,下面是三种常用的定界符: 18 | 19 | - `{{ ... }}` 用来标记变量。 20 | - `{% ... %}` 用来标记语句,比如 if 语句,for 语句等。 21 | - `{# ... #}` 用来写注释。 22 | 23 | 模板中使用的变量需要在渲染的时候传递进去,具体我们后面会了解。 24 | 25 | ## 编写主页模板 26 | 27 | 我们先在 templates 目录下创建一个 index.html 文件,作为主页模板。主页需要显示电影条目列表和个人信息,代码如下所示: 28 | 29 | *templates/index.html:主页模板* 30 | 31 | ```jinja2 32 | 33 | 34 | 35 | 36 | {{ name }}'s Watchlist 37 | 38 | 39 |

{{ name }}'s Watchlist

40 | {# 使用 length 过滤器获取 movies 变量的长度 #} 41 |

{{ movies|length }} Titles

42 |
    43 | {% for movie in movies %} {# 迭代 movies 变量 #} 44 |
  • {{ movie.title }} - {{ movie.year }}
  • {# 等同于 movie['title'] #} 45 | {% endfor %} {# 使用 endfor 标签结束 for 语句 #} 46 |
47 | 50 | 51 | 52 | ``` 53 | 54 | 为了方便对变量进行处理,Jinja2 提供了一些过滤器,语法形式如下: 55 | 56 | ```jinja2 57 | {{ 变量|过滤器 }} 58 | ``` 59 | 60 | 左侧是变量,右侧是过滤器名。比如,上面的模板里使用 `length` 过滤器来获取 `movies` 的长度,类似 Python 里的 `len()` 函数。 61 | 62 | > **提示** 访问 查看所有可用的过滤器。 63 | 64 | ## 准备虚拟数据 65 | 66 | 为了模拟页面渲染,我们需要先创建一些虚拟数据,用来填充页面内容: 67 | 68 | *app.py:定义虚拟数据* 69 | 70 | ```python 71 | name = 'Grey Li' 72 | movies = [ 73 | {'title': 'My Neighbor Totoro', 'year': '1988'}, 74 | {'title': 'Dead Poets Society', 'year': '1989'}, 75 | {'title': 'A Perfect World', 'year': '1993'}, 76 | {'title': 'Leon', 'year': '1994'}, 77 | {'title': 'Mahjong', 'year': '1996'}, 78 | {'title': 'Swallowtail Butterfly', 'year': '1996'}, 79 | {'title': 'King of Comedy', 'year': '1999'}, 80 | {'title': 'Devils on the Doorstep', 'year': '1999'}, 81 | {'title': 'WALL-E', 'year': '2008'}, 82 | {'title': 'The Pork of Music', 'year': '2012'}, 83 | ] 84 | ``` 85 | 86 | ## 渲染主页模板 87 | 88 | 使用 `render_template()` 函数可以把模板渲染出来,必须传入的参数为模板文件名(相对于 templates 根目录的文件路径),这里即 `'index.html'`。为了让模板正确渲染,我们还要把模板内部使用的变量通过关键字参数传入这个函数,如下所示: 89 | 90 | *app.py:返回渲染好的模板作为响应* 91 | 92 | ```python 93 | from flask import Flask, render_template 94 | 95 | # ... 96 | 97 | @app.route('/') 98 | def index(): 99 | return render_template('index.html', name=name, movies=movies) 100 | ``` 101 | 102 | 为了更好的表示这个视图函数的作用,我们把原来的函数名 `hello` 改为 `index`,意思是“索引”,即主页。 103 | 104 | 在传入 `render_template()` 函数的关键字参数中,左边的 `movies` 是模板中使用的变量名称,右边的 `movies` 则是该变量指向的实际对象。这里传入模板的 `name` 是字符串,`movies` 是列表,但能够在模板里使用的不只这两种 Python 数据结构,你也可以传入元组、字典、函数等。 105 | 106 | `render_template()` 函数在调用时会识别并执行 index.html 里所有的 Jinja2 语句,返回渲染好的模板内容。在返回的页面中,变量会被替换为实际的值(包括定界符),语句(及定界符)则会在执行后被移除(注释也会一并移除)。 107 | 108 | 现在访问 看到的程序主页如下图所示: 109 | 110 | ![主页电影列表](../docs/flask_images/3-1.png) 111 | -------------------------------------------------------------------------------- /04_flask/03_静态文件.md: -------------------------------------------------------------------------------- 1 | 2 | # 第 3 章:静态文件 3 | 4 | 静态文件(static files)和我们的模板概念相反,指的是内容不需要动态生成的文件。比如图片、CSS 文件和 JavaScript 脚本等。 5 | 6 | 在 Flask 中,我们需要创建一个 static 文件夹来保存静态文件,它应该和程序模块、templates 文件夹在同一目录层级,所以我们在项目根目录创建它: 7 | 8 | ```bash 9 | $ mkdir static 10 | ``` 11 | 12 | ## 生成静态文件 URL 13 | 14 | 在 HTML 文件里,引入这些静态文件需要给出资源所在的 URL。为了更加灵活,这些文件的 URL 可以通过 Flask 提供的 `url_for()` 函数来生成。 15 | 16 | 在第 2 章的最后,我们学习过 `url_for()` 函数的用法,传入端点值(视图函数的名称)和参数,它会返回对应的 URL。对于静态文件,需要传入的端点值是 `static`,同时使用 `filename` 参数来传入相对于 static 文件夹的文件路径。 17 | 18 | 假如我们在 static 文件夹的根目录下面放了一个 foo.jpg 文件,下面的调用可以获取它的 URL: 19 | 20 | ```jinja2 21 | 22 | ``` 23 | 24 | 花括号部分的调用会返回 `/static/foo.jpg`。 25 | 26 | > **提示** 在 Python 脚本里,`url_for()` 函数需要从 `flask` 包中导入,而在模板中则可以直接使用,因为 Flask 把一些常用的函数和对象添加到了模板上下文(环境)里。 27 | 28 | ## 添加 Favicon 29 | 30 | Favicon(favourite icon) 是显示在标签页和书签栏的网站头像。你需要准备一个 ICO、PNG 或 GIF 格式的图片,大小一般为 16×16、32×32、48×48 或 64×64 像素。把这个图片放到 static 目录下,然后像下面这样在 HTML 模板里引入它: 31 | 32 | *templates/index.html:引入 Favicon* 33 | 34 | ```html 35 | 36 | ... 37 | 38 | 39 | ``` 40 | 41 | 保存后刷新页面,即可在浏览器标签页上看到这个图片。 42 | 43 | ## 添加图片 44 | 45 | 为了让页面不那么单调,我们来添加两个图片:一个是显示在页面标题旁边的头像,另一个是显示在页面底部的龙猫动图。我们在 static 目录下面创建一个子文件夹 images,把这两个图片都放到这个文件夹里: 46 | 47 | ```bash 48 | $ cd static 49 | $ mkdir images 50 | ``` 51 | 52 | 创建子文件夹并不是必须的,这里只是为了更好的组织同类文件。同样的,如果你有多个 CSS 文件,也可以创建一个 css 文件夹来组织他们。下面我们在页面模板中添加这两个图片,注意填写正确的文件路径: 53 | 54 | *templates/index.html:添加图片* 55 | 56 | ```html 57 |

58 | Avatar 59 | {{ name }}'s Watchlist 60 |

61 | ... 62 | Walking Totoro 63 | ``` 64 | 65 | > **提示** 这两张图片你可以自己替换为任意的图片(注意更新文件名),也可以在示例程序的 [GitHub 仓库](https://github.com/greyli/watchlist/tree/master/watchlist/static/images)下载。 66 | 67 | ## 添加 CSS 68 | 69 | 虽然添加了图片,但页面还是非常简陋,因为我们还没有添加 CSS 定义。下面在 static 目录下创建一个 CSS 文件 style.css,内容如下: 70 | 71 | *static/style.css:定义页面样式* 72 | 73 | ```css 74 | /* 页面整体 */ 75 | body { 76 | margin: auto; 77 | max-width: 580px; 78 | font-size: 14px; 79 | font-family: Helvetica, Arial, sans-serif; 80 | } 81 | 82 | /* 页脚 */ 83 | footer { 84 | color: #888; 85 | margin-top: 15px; 86 | text-align: center; 87 | padding: 10px; 88 | } 89 | 90 | /* 头像 */ 91 | .avatar { 92 | width: 40px; 93 | } 94 | 95 | /* 电影列表 */ 96 | .movie-list { 97 | list-style-type: none; 98 | padding: 0; 99 | margin-bottom: 10px; 100 | box-shadow: 0 2px 5px 0 rgba(0, 0, 0, 0.16), 0 2px 10px 0 rgba(0, 0, 0, 0.12); 101 | } 102 | 103 | .movie-list li { 104 | padding: 12px 24px; 105 | border-bottom: 1px solid #ddd; 106 | } 107 | 108 | .movie-list li:last-child { 109 | border-bottom:none; 110 | } 111 | 112 | .movie-list li:hover { 113 | background-color: #f8f9fa; 114 | } 115 | 116 | /* 龙猫图片 */ 117 | .totoro { 118 | display: block; 119 | margin: 0 auto; 120 | height: 100px; 121 | } 122 | ``` 123 | 124 | 接着在页面的 `` 标签内引入这个 CSS 文件: 125 | 126 | *templates/index.html:引入 CSS 文件* 127 | 128 | ```html 129 | 130 | ... 131 | 132 | 133 | ``` 134 | 135 | > **提示** 当你把 CSS 写到单独的文件后,浏览器获取到这个文件后会对其进行缓存(其他静态文件同理,比如 JavaScript 文件)。所以,在后续章节,每当你对 CSS 文件的内容进行更新后,都需要使用下面的快捷键清除缓存: 136 | > 137 | > - Google Chrome(Mac):Command + Shift + R 138 | > - Google Chrome(Windows & Linux):Ctrl + F5 139 | > - Firefox(Mac):Command + Shift + R 140 | > - Firefox(Windows & Linux):Ctrl + F5 141 | > - Safari:Command + Option + R 142 | 143 | 最后要为对应的元素设置 `class` 属性值,以便和对应的 CSS 定义关联起来: 144 | 145 | *templates/index.html:添加 class 属性* 146 | 147 | ```html 148 |

149 | Avatar 150 | {{ name }}'s Watchlist 151 |

152 | ... 153 |
    154 | ... 155 |
156 | Walking Totoro 157 | ``` 158 | 159 | 最终的页面如下图所示(你可以自由修改 CSS 定义,我已经尽力了): 160 | 161 | ![使用了静态文件的主页](../docs/flask_images/4-1.png) 162 | -------------------------------------------------------------------------------- /04_flask/04_数据库.md: -------------------------------------------------------------------------------- 1 | # 第 4 章:数据库 2 | 3 | 大部分程序都需要保存数据,所以不可避免要使用数据库。用来操作数据库的数据库管理系统(DBMS)有很多选择,对于不同类型的程序,不同的使用场景,都会有不同的选择。在这个教程中,我们选择了属于关系型数据库管理系统(RDBMS)的 [SQLite](https://www.sqlite.org/),它基于文件,不需要单独启动数据库服务器,适合在开发时使用,或是在数据库操作简单、访问量低的程序中使用。 4 | 5 | ## 使用 SQLAlchemy 操作数据库 6 | 7 | 为了简化数据库操作,我们将使用 [SQLAlchemy](https://www.sqlalchemy.org/)——一个 Python 数据库工具(ORM,即对象关系映射)。借助 SQLAlchemy,你可以通过定义 Python 类来表示数据库里的一张表(类属性表示表中的字段 / 列),通过对这个类进行各种操作来代替写 SQL 语句。这个类我们称之为**模型类**,类中的属性我们将称之为**字段**。 8 | 9 | Flask 有大量的第三方扩展,这些扩展可以简化和第三方库的集成工作。我们下面将使用一个叫做 [Flask-SQLAlchemy](http://flask-sqlalchemy.pocoo.org/2.3/) 的官方扩展来集成 SQLAlchemy。 10 | 11 | 首先安装它: 12 | 13 | ```bash 14 | (env) $ pip install flask-sqlalchemy 15 | ``` 16 | 17 | 大部分扩展都需要执行一个“初始化”操作。你需要导入扩展类,实例化并传入 Flask 程序实例: 18 | 19 | ```python 20 | from flask_sqlalchemy import SQLAlchemy # 导入扩展类 21 | 22 | app = Flask(__name__) 23 | 24 | db = SQLAlchemy(app) # 初始化扩展,传入程序实例 app 25 | ``` 26 | 27 | ## 设置数据库 URI 28 | 29 | 为了设置 Flask、扩展或是我们程序本身的一些行为,我们需要设置和定义一些配置变量。Flask 提供了一个统一的接口来写入和获取这些配置变量:`Flask.config` 字典。配置变量的名称必须使用大写,写入配置的语句一般会放到扩展类实例化语句之前。 30 | 31 | 下面写入了一个 `SQLALCHEMY_DATABASE_URI` 变量来告诉 SQLAlchemy 数据库连接地址: 32 | 33 | ```python 34 | import os 35 | 36 | # ... 37 | 38 | app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:////' + os.path.join(app.root_path, 'data.db') 39 | ``` 40 | 41 | > **注意** 这个配置变量的最后一个单词是 URI,而不是 URL。 42 | 43 | 对于这个变量值,不同的 DBMS 有不同的格式,对于 SQLite 来说,这个值的格式如下: 44 | 45 | ```python 46 | sqlite:////数据库文件的绝对地址 47 | ``` 48 | 49 | 数据库文件一般放到项目根目录即可,`app.root_path` 返回程序实例所在模块的路径(目前来说,即项目根目录),我们使用它来构建文件路径。数据库文件的名称和后缀你可以自由定义,一般会使用 .db、.sqlite 和 .sqlite3 作为后缀。 50 | 51 | 另外,如果你使用 Windows 系统,上面的 URI 前缀部分只需要写入三个斜线(即 `sqlite:///`)。在本书的示例程序代码里,做了一些兼容性处理,另外还新设置了一个配置变量,实际的代码如下: 52 | 53 | *app.py:数据库配置* 54 | 55 | ```python 56 | import os 57 | import sys 58 | 59 | from flask import Flask 60 | from flask_sqlalchemy import SQLAlchemy 61 | 62 | WIN = sys.platform.startswith('win') 63 | if WIN: # 如果是 Windows 系统,使用三个斜线 64 | prefix = 'sqlite:///' 65 | else: # 否则使用四个斜线 66 | prefix = 'sqlite:////' 67 | 68 | app = Flask(__name__) 69 | app.config['SQLALCHEMY_DATABASE_URI'] = prefix + os.path.join(app.root_path, 'data.db') 70 | app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False # 关闭对模型修改的监控 71 | # 在扩展类实例化前加载配置 72 | db = SQLAlchemy(app) 73 | ``` 74 | 75 | 如果你固定在某一个操作系统上进行开发,部署时也使用相同的操作系统,那么可以不用这么做,直接根据你的需要写出前缀即可。 76 | 77 | > **提示** 你可以访问 [Flask 文档的配置页面](http://flask.pocoo.org/docs/1.0/config/)查看 Flask 内置的配置变量;同样的,在 [Flask-SQLAlchemy 文档的配置页面](http://flask-sqlalchemy.pocoo.org/2.1/config/)可以看到 Flask-SQLAlchemy 提供的配置变量。 78 | 79 | ## 创建数据库模型 80 | 81 | 在 Watchlist 程序里,目前我们有两类数据要保存:用户信息和电影条目信息。下面分别创建了两个模型类来表示这两张表: 82 | 83 | *app.py:创建数据库模型* 84 | 85 | ```python 86 | class User(db.Model): # 表名将会是 user(自动生成,小写处理) 87 | id = db.Column(db.Integer, primary_key=True) # 主键 88 | name = db.Column(db.String(20)) # 名字 89 | 90 | class Movie(db.Model): # 表名将会是 movie 91 | id = db.Column(db.Integer, primary_key=True) # 主键 92 | title = db.Column(db.String(60)) # 电影标题 93 | year = db.Column(db.String(4)) # 电影年份 94 | ``` 95 | 96 | 模型类的编写有一些限制: 97 | 98 | * 模型类要声明继承 `db.Model`。 99 | * 每一个类属性(字段)要实例化 `db.Column`,传入的参数为字段的类型,下面的表格列出了常用的字段类。 100 | * 在 `db.Column()` 中添加额外的选项(参数)可以对字段进行设置。比如,`primary_key` 设置当前字段是否为主键。除此之外,常用的选项还有 `nullable`(布尔值,是否允许为空值)、`index`(布尔值,是否设置索引)、`unique`(布尔值,是否允许重复值)、`default`(设置默认值)等。 101 | 102 | 常用的字段类型如下表所示: 103 | 104 | | 字段类 | 说明 | 105 | | ---------------- | --------------------------------------------- | 106 | | db.Integer | 整型 | 107 | | db.String (size) | 字符串,size 为最大长度,比如 `db.String(20)` | 108 | | db.Text | 长文本 | 109 | | db.DateTime | 时间日期,Python `datetime` 对象 | 110 | | db.Float | 浮点数 | 111 | | db.Boolean | 布尔值 | 112 | 113 | ## 创建数据库表 114 | 115 | 模型类创建后,还不能对数据库进行操作,因为我们还没有创建表和数据库文件。下面在 Python Shell 中创建了它们: 116 | 117 | ```python 118 | (env) $ flask shell 119 | >>> from app import db 120 | >>> db.create_all() 121 | ``` 122 | 123 | 打开文件管理器,你会发现项目根目录下出现了新创建的数据库文件 data.db。这个文件不需要提交到 Git 仓库,我们在 .gitignore 文件最后添加一行新规则: 124 | 125 | ``` 126 | *.db 127 | ``` 128 | 129 | 如果你改动了模型类,想重新生成表模式,那么需要先使用 `db.drop_all()` 删除表,然后重新创建: 130 | 131 | ```python 132 | >>> db.drop_all() 133 | >>> db.create_all() 134 | ``` 135 | 136 | 注意这会一并删除所有数据,如果你想在不破坏数据库内的数据的前提下变更表的结构,需要使用数据库迁移工具,比如集成了 [Alembic](https://alembic.sqlalchemy.org/en/latest/) 的 [Flask-Migrate](https://github.com/miguelgrinberg/Flask-Migrate) 扩展。 137 | 138 | > **提示** 上面打开 Python Shell 使用的是 `flask shell`命令,而不是 `python`。使用这个命令启动的 Python Shell 激活了“程序上下文”,它包含一些特殊变量,这对于某些操作是必须的(比如上面的 `db.create_all()`调用)。请记住,后续的 Python Shell 都会使用这个命令打开。 139 | 140 | 和 `flask shell`类似,我们可以编写一个自定义命令来自动执行创建数据库表操作: 141 | 142 | *app.py:自定义命令 initdb* 143 | 144 | ```python 145 | import click 146 | 147 | @app.cli.command() # 注册为命令 148 | @click.option('--drop', is_flag=True, help='Create after drop.') # 设置选项 149 | def initdb(drop): 150 | """Initialize the database.""" 151 | if drop: # 判断是否输入了选项 152 | db.drop_all() 153 | db.create_all() 154 | click.echo('Initialized database.') # 输出提示信息 155 | ``` 156 | 157 | 默认情况下,函数名称就是命令的名字,现在执行 `flask initdb` 命令就可以创建数据库表: 158 | 159 | ```bash 160 | (env) $ flask initdb 161 | ``` 162 | 163 | 使用 `--drop` 选项可以删除表后重新创建: 164 | 165 | ```bash 166 | (env) $ flask initdb --drop 167 | ``` 168 | 169 | ## 创建、读取、更新、删除 170 | 171 | 在前面打开的 Python Shell 里,我们来测试一下常见的数据库操作。你可以跟着示例代码来操作,也可以自由练习。 172 | 173 | ### 创建 174 | 175 | 下面的操作演示了如何向数据库中添加记录: 176 | 177 | ```python 178 | >>> from app import User, Movie # 导入模型类 179 | >>> user = User(name='Grey Li') # 创建一个 User 记录 180 | >>> m1 = Movie(title='Leon', year='1994') # 创建一个 Movie 记录 181 | >>> m2 = Movie(title='Mahjong', year='1996') # 再创建一个 Movie 记录 182 | >>> db.session.add(user) # 把新创建的记录添加到数据库会话 183 | >>> db.session.add(m1) 184 | >>> db.session.add(m2) 185 | >>> db.session.commit() # 提交数据库会话,只需要在最后调用一次即可 186 | ``` 187 | 188 | > **提示** 在实例化模型类的时候,我们并没有传入 `id` 字段(主键),因为 SQLAlchemy 会自动处理这个字段。 189 | 190 | 最后一行 `db.session.commit()` 很重要,只有调用了这一行才会真正把记录提交进数据库,前面的 `db.session.add()` 调用是将改动添加进数据库会话(一个临时区域)中。 191 | 192 | ### 读取 193 | 194 | 通过对模型类的 `query` 属性调用可选的过滤方法和查询方法,我们就可以获取到对应的单个或多个记录(记录以模型类实例的形式表示)。查询语句的格式如下: 195 | 196 | ```python 197 | <模型类>.query.<过滤方法(可选)>.<查询方法> 198 | ``` 199 | 200 | 下面是一些常用的过滤方法: 201 | 202 | | 过滤方法 | 说明 | 203 | | ----------- | ------------------------------------------------------------ | 204 | | filter() | 使用指定的规则过滤记录,返回新产生的查询对象 | 205 | | filter_by() | 使用指定规则过滤记录(以关键字表达式的形式),返回新产生的查询对象 | 206 | | order_by() | 根据指定条件对记录进行排序,返回新产生的查询对象 | 207 | | group_by() | 根据指定条件对记录进行分组,返回新产生的查询对象 | 208 | 209 | 下面是一些常用的查询方法: 210 | 211 | | 查询方法 | 说明 | 212 | | -------------- | ------------------------------------------------------------ | 213 | | all() | 返回包含所有查询记录的列表 | 214 | | first() | 返回查询的第一条记录,如果未找到,则返回 None | 215 | | get(id) | 传入主键值作为参数,返回指定主键值的记录,如果未找到,则返回 None | 216 | | count() | 返回查询结果的数量 | 217 | | first_or_404() | 返回查询的第一条记录,如果未找到,则返回 404 错误响应 | 218 | | get_or_404(id) | 传入主键值作为参数,返回指定主键值的记录,如果未找到,则返回 404 错误响应 | 219 | | paginate() | 返回一个 Pagination 对象,可以对记录进行分页处理 | 220 | 221 | 下面的操作演示了如何从数据库中读取记录,并进行简单的查询: 222 | 223 | ```python 224 | >>> from app import Movie # 导入模型类 225 | >>> movie = Movie.query.first() # 获取 Movie 模型的第一个记录(返回模型类实例) 226 | >>> movie.title # 对返回的模型类实例调用属性即可获取记录的各字段数据 227 | 'Leon' 228 | >>> movie.year 229 | '1994' 230 | >>> Movie.query.all() # 获取 Movie 模型的所有记录,返回包含多个模型类实例的列表 231 | [, ] 232 | >>> Movie.query.count() # 获取 Movie 模型所有记录的数量 233 | 2 234 | >>> Movie.query.get(1) # 获取主键值为 1 的记录 235 | 236 | >>> Movie.query.filter_by(title='Mahjong').first() # 获取 title 字段值为 Mahjong 的记录 237 | 238 | >>> Movie.query.filter(Movie.title=='Mahjong').first() # 等同于上面的查询,但使用不同的过滤方法 239 | 240 | ``` 241 | 242 | > **提示** 我们在说 Movie 模型的时候,实际指的是数据库中的 movie 表。表的实际名称是模型类的小写形式(自动生成),如果你想自己指定表名,可以定义 `__tablename__` 属性。 243 | 244 | 对于最基础的 `filter()` 过滤方法,SQLAlchemy 支持丰富的查询操作符,具体可以访问[文档相关页面](http://docs.sqlalchemy.org/en/latest/core/sqlelement.html#sqlalchemy.sql.operators.ColumnOperators)查看。除此之外,还有更多的查询方法、过滤方法和数据库函数可以使用,具体可以访问文档的 [Query API](https://docs.sqlalchemy.org/en/latest/orm/query.html) 部分查看。 245 | 246 | ### 更新 247 | 248 | 下面的操作更新了 `Movie` 模型中主键为 `2` 的记录: 249 | 250 | ```python 251 | >>> movie = Movie.query.get(2) 252 | >>> movie.title = 'WALL-E' # 直接对实例属性赋予新的值即可 253 | >>> movie.year = '2008' 254 | >>> db.session.commit() # 注意仍然需要调用这一行来提交改动 255 | ``` 256 | 257 | ### 删除 258 | 259 | 下面的操作删除了 `Movie` 模型中主键为 `1` 的记录: 260 | 261 | ```python 262 | >>> movie = Movie.query.get(1) 263 | >>> db.session.delete(movie) # 使用 db.session.delete() 方法删除记录,传入模型实例 264 | >>> db.session.commit() # 提交改动 265 | ``` 266 | 267 | ## 在程序里操作数据库 268 | 269 | 经过上面的一番练习,我们可以在 Watchlist 里进行实际的数据库操作了。 270 | 271 | ### 在主页视图读取数据库记录 272 | 273 | 因为设置了数据库,负责显示主页的 `index` 可以从数据库里读取真实的数据: 274 | 275 | ```python 276 | @app.route('/') 277 | def index(): 278 | user = User.query.first() # 读取用户记录 279 | movies = Movie.query.all() # 读取所有电影记录 280 | return render_template('index.html', user=user, movies=movies) 281 | ``` 282 | 283 | 在 `index` 视图中,原来传入模板的 `name` 变量被 `user` 实例取代,模板 index.html 中的两处 `name` 变量也要相应的更新为 `user.name` 属性: 284 | 285 | ```jinja2 286 | {{ user.name }}'s Watchlist 287 | ``` 288 | 289 | ### 生成虚拟数据 290 | 291 | 因为有了数据库,我们可以编写一个命令函数把虚拟数据添加到数据库里。下面是用来生成虚拟数据的命令函数: 292 | 293 | *app.py:创建自定义命令 forge* 294 | 295 | ```python 296 | import click 297 | 298 | @app.cli.command() 299 | def forge(): 300 | """Generate fake data.""" 301 | db.create_all() 302 | 303 | # 全局的两个变量移动到这个函数内 304 | name = 'Grey Li' 305 | movies = [ 306 | {'title': 'My Neighbor Totoro', 'year': '1988'}, 307 | {'title': 'Dead Poets Society', 'year': '1989'}, 308 | {'title': 'A Perfect World', 'year': '1993'}, 309 | {'title': 'Leon', 'year': '1994'}, 310 | {'title': 'Mahjong', 'year': '1996'}, 311 | {'title': 'Swallowtail Butterfly', 'year': '1996'}, 312 | {'title': 'King of Comedy', 'year': '1999'}, 313 | {'title': 'Devils on the Doorstep', 'year': '1999'}, 314 | {'title': 'WALL-E', 'year': '2008'}, 315 | {'title': 'The Pork of Music', 'year': '2012'}, 316 | ] 317 | 318 | user = User(name=name) 319 | db.session.add(user) 320 | for m in movies: 321 | movie = Movie(title=m['title'], year=m['year']) 322 | db.session.add(movie) 323 | 324 | db.session.commit() 325 | click.echo('Done.') 326 | ``` 327 | 328 | 现在执行 `flask forge` 命令就会把所有虚拟数据添加到数据库里: 329 | 330 | ```bash 331 | (env) $ flask forge 332 | ``` 333 | -------------------------------------------------------------------------------- /04_flask/05_模板优化.md: -------------------------------------------------------------------------------- 1 | # 第 5 章:模板优化 2 | 3 | 这一章我们会继续完善模板,学习几个非常实用的模板编写技巧,为下一章实现创建、编辑电影条目打下基础。 4 | 5 | ## 自定义错误页面 6 | 7 | 为了引出相关知识点,我们首先要为 Watchlist 编写一个错误页面。目前的程序中,如果你访问一个不存在的 URL,比如 /hello,Flask 会自动返回一个 404 错误响应。默认的错误页面非常简陋,如下图所示: 8 | 9 | ![默认的 404 错误页面](../docs/flask_images/6-1.png) 10 | 11 | 在 Flask 程序中自定义错误页面非常简单,我们先编写一个 404 错误页面模板,如下所示: 12 | 13 | *templates/404.html:404 错误页面模板* 14 | 15 | ```html 16 | 17 | 18 | 19 | 20 | {{ user.name }}'s Watchlist 21 | 22 | 23 | 24 | 25 |

26 | Avatar 27 | {{ user.name }}'s Watchlist 28 |

29 |
    30 |
  • 31 | Page Not Found - 404 32 | 33 | Go Back 34 | 35 |
  • 36 |
37 | 40 | 41 | 42 | ``` 43 | 44 | 接着使用 `app.errorhandler()` 装饰器注册一个错误处理函数,它的作用和视图函数类似,当 404 错误发生时,这个函数会被触发,返回值会作为响应主体返回给客户端: 45 | 46 | *app.py:404 错误处理函数* 47 | 48 | ```python 49 | @app.errorhandler(404) # 传入要处理的错误代码 50 | def page_not_found(e): # 接受异常对象作为参数 51 | user = User.query.first() 52 | return render_template('404.html', user=user), 404 # 返回模板和状态码 53 | ``` 54 | 55 | > **提示** 和我们前面编写的视图函数相比,这个函数返回了状态码作为第二个参数,普通的视图函数之所以不用写出状态码,是因为默认会使用 200 状态码,表示成功。 56 | 57 | 这个视图返回渲染好的错误模板,因为模板中使用了 user 变量,这里也要一并传入。现在访问一个不存在的 URL,会显示我们自定义的错误页面: 58 | 59 | ![自定义 404 错误页面](../docs/flask_images/6-2.png) 60 | 61 | 编写完这部分代码后,你会发现两个问题: 62 | 63 | * 错误页面和主页都需要使用 user 变量,所以在对应的处理函数里都要查询数据库并传入 user 变量。因为每一个页面都需要获取用户名显示在页面顶部,如果有更多的页面,那么每一个对应的视图函数都要重复传入这个变量。 64 | * 错误页面模板和主页模板有大量重复的代码,比如 `` 标签的内容,页首的标题,页脚信息等。这种重复不仅带来不必要的工作量,而且会让修改变得更加麻烦。举例来说,如果页脚信息需要更新,那么每个页面都要一一进行修改。 65 | 66 | 显而易见,这两个问题有更优雅的处理方法,下面我们来一一了解。 67 | 68 | ## 模板上下文处理函数 69 | 70 | 对于多个模板内都需要使用的变量,我们可以使用 `app.context_processor` 装饰器注册一个模板上下文处理函数,如下所示: 71 | 72 | *app.py:模板上下文处理函数* 73 | 74 | ```python 75 | @app.context_processor 76 | def inject_user(): # 函数名可以随意修改 77 | user = User.query.first() 78 | return dict(user=user) # 需要返回字典,等同于 return {'user': user} 79 | ``` 80 | 81 | 这个函数返回的变量(以字典键值对的形式)将会统一注入到每一个模板的上下文环境中,因此可以直接在模板中使用。 82 | 83 | 现在我们可以删除 404 错误处理函数和主页视图函数中的 `user` 变量定义,并删除在 `render_template()` 函数里传入的关键字参数: 84 | 85 | ```python 86 | @app.context_processor 87 | def inject_user(): 88 | user = User.query.first() 89 | return dict(user=user) 90 | 91 | 92 | @app.errorhandler(404) 93 | def page_not_found(e): 94 | return render_template('404.html'), 404 95 | 96 | 97 | @app.route('/') 98 | def index(): 99 | movies = Movie.query.all() 100 | return render_template('index.html', movies=movies) 101 | ``` 102 | 103 | 同样的,后面我们创建的任意一个模板,都可以在模板中直接使用 `user` 变量。 104 | 105 | ## 使用模板继承组织模板 106 | 107 | 对于模板内容重复的问题,Jinja2 提供了模板继承的支持。这个机制和 Python 类继承非常类似:我们可以定义一个父模板,一般会称之为基模板(base template)。基模板中包含完整的 HTML 结构和导航栏、页首、页脚等通用部分。在子模板里,我们可以使用 `extends` 标签来声明继承自某个基模板。 108 | 109 | 基模板中需要在实际的子模板中追加或重写的部分则可以定义成块(block)。块使用 `block` 标签创建, `{% block 块名称 %}` 作为开始标记,`{% endblock %}` 或 `{% endblock 块名称 %}` 作为结束标记。通过在子模板里定义一个同样名称的块,你可以向基模板的对应块位置追加或重写内容。 110 | 111 | ### 编写基础模板 112 | 113 | 下面是新编写的基模板 base.html: 114 | 115 | *templates/base.html:基模板* 116 | 117 | ```html 118 | 119 | 120 | 121 | {% block head %} 122 | 123 | 124 | {{ user.name }}'s Watchlist 125 | 126 | 127 | {% endblock %} 128 | 129 | 130 |

131 | Avatar 132 | {{ user.name }}'s Watchlist 133 |

134 | 139 | {% block content %}{% endblock %} 140 | 143 | 144 | 145 | ``` 146 | 147 | 在基模板里,我们添加了两个块,一个是包含 `` 内容的 `head` 块,另一个是用来在子模板中插入页面主体内容的 `content` 块。在复杂的项目里,你可以定义更多的块,方便在子模板中对基模板的各个部分插入内容。另外,块的名字没有特定要求,你可以自由修改。 148 | 149 | 在编写子模板之前,我们先来看一下基模板中的两处新变化。 150 | 151 | 第一处,我们添加了一个新的 `` 元素,这个元素会设置页面的视口,让页面根据设备的宽度来自动缩放页面,让移动设备拥有更好的浏览体验: 152 | 153 | ```html 154 | 155 | ``` 156 | 157 | 第二处,新的页面添加了一个导航栏: 158 | 159 | ```html 160 | 165 | ``` 166 | 167 | 导航栏对应的 CSS 代码如下所示: 168 | 169 | ```css 170 | nav ul { 171 | list-style-type: none; 172 | margin: 0; 173 | padding: 0; 174 | overflow: hidden; 175 | background-color: #333; 176 | } 177 | 178 | nav li { 179 | float: left; 180 | } 181 | 182 | nav li a { 183 | display: block; 184 | color: white; 185 | text-align: center; 186 | padding: 8px 12px; 187 | text-decoration: none; 188 | } 189 | 190 | nav li a:hover { 191 | background-color: #111; 192 | } 193 | ``` 194 | 195 | ### 编写子模板 196 | 197 | 创建了基模板后,子模板的编写会变得非常简单。下面是新的主页模板(index.html): 198 | 199 | *templates/index.html:继承基模板的主页模板* 200 | 201 | ```html 202 | {% extends 'base.html' %} 203 | 204 | {% block content %} 205 |

{{ movies|length }} Titles

206 |
    207 | {% for movie in movies %} 208 |
  • {{ movie.title }} - {{ movie.year }} 209 | 210 | IMDb 211 | 212 |
  • 213 | {% endfor %} 214 |
215 | Walking Totoro 216 | {% endblock %} 217 | ``` 218 | 219 | 第一行使用 `extends` 标签声明扩展自模板 base.html,可以理解成“这个模板继承自 base.html“。接着我们定义了 `content` 块,这里的内容会插入到基模板中 `content` 块的位置。 220 | 221 | > **提示** 默认的块重写行为是覆盖,如果你想向父块里追加内容,可以在子块中使用 `super()` 声明,即 `{{ super() }}`。 222 | 223 | 404 错误页面的模板类似,如下所示: 224 | 225 | *templates/404.html:继承基模板的 404 错误页面模板* 226 | 227 | ```html 228 | {% extends 'base.html' %} 229 | 230 | {% block content %} 231 |
    232 |
  • 233 | Page Not Found - 404 234 | 235 | Go Back 236 | 237 |
  • 238 |
239 | {% endblock %} 240 | ``` 241 | 242 | ## 添加 IMDb 链接 243 | 244 | 在主页模板里,我们还为每一个电影条目右侧添加了一个 IMDb 链接: 245 | 246 | ```html 247 | 248 | IMDb 249 | 250 | ``` 251 | 252 | 这个链接的 `href` 属性的值为 IMDb 搜索页面的 URL,搜索关键词通过查询参数 `q` 传入,这里传入了电影的标题。 253 | 254 | 对应的 CSS 定义如下所示: 255 | 256 | ```css 257 | .float-right { 258 | float: right; 259 | } 260 | 261 | .imdb { 262 | font-size: 12px; 263 | font-weight: bold; 264 | color: black; 265 | text-decoration: none; 266 | background: #F5C518; 267 | border-radius: 5px; 268 | padding: 3px 5px; 269 | } 270 | ``` 271 | 272 | 现在,我们的程序主页如下所示: 273 | 274 | ![添加导航栏和 IMDb 链接](../docs/flask_images/6-3.png) 275 | 276 | -------------------------------------------------------------------------------- /04_flask/06_表单.md: -------------------------------------------------------------------------------- 1 | # 第 6 章:表单 2 | 3 | 在 HTML 页面里,我们需要编写表单来获取用户输入。一个典型的表单如下所示: 4 | 5 | ```html 6 |
7 | 8 |
9 | 10 |
11 | 12 |
13 | ``` 14 | 15 | 编写表单的 HTML 代码有下面几点需要注意: 16 | 17 | * 在 `
` 标签里使用 `method` 属性将提交表单数据的 HTTP 请求方法指定为 POST。如果不指定,则会默认使用 GET 方法,这会将表单数据通过 URL 提交,容易导致数据泄露,而且不适用于包含大量数据的情况。 18 | * `` 元素必须要指定 `name` 属性,否则无法提交数据,在服务器端,我们也需要通过这个 `name` 属性值来获取对应字段的数据。 19 | 20 | > **提示** 填写输入框标签文字的 `