├── .github └── workflows │ └── deploy.yml ├── .gitignore ├── Makefile ├── README.md ├── baidu_verify_code-akNcNpmYjb.html ├── exts ├── chinese_search.py ├── main.dic ├── smallseg.py ├── suffix.dic └── zh.py ├── github-toc-maker-for-sphinx.py ├── make.bat ├── md2rst.py ├── pdf_maker.py ├── rebuild.sh ├── requirements.txt ├── source ├── _static │ └── js │ │ ├── baidutongji.js │ │ └── readmore.js ├── _templates │ └── versions.html ├── aboutme.rst ├── c01 │ ├── c01_01.md │ ├── c01_01.rst │ ├── c01_02.md │ ├── c01_02.rst │ ├── c01_03.md │ ├── c01_03.rst │ ├── c01_04.md │ ├── c01_04.rst │ ├── c01_05.md │ ├── c01_05.rst │ ├── c01_06.md │ ├── c01_06.rst │ ├── c01_07.md │ ├── c01_07.rst │ ├── c01_08.md │ ├── c01_08.rst │ ├── c01_09.md │ ├── c01_09.rst │ ├── c01_10.md │ ├── c01_10.rst │ ├── c01_11.md │ ├── c01_11.rst │ ├── c01_12.md │ ├── c01_12.rst │ ├── c01_13.md │ ├── c01_13.rst │ ├── c01_14.md │ ├── c01_14.rst │ ├── c01_15.md │ ├── c01_15.rst │ ├── c01_16.md │ ├── c01_16.rst │ ├── c01_17.md │ ├── c01_17.rst │ ├── c01_18.md │ ├── c01_18.rst │ ├── c01_19.md │ ├── c01_19.rst │ ├── c01_20.md │ ├── c01_20.rst │ ├── c01_21.md │ ├── c01_21.rst │ ├── c01_22.md │ ├── c01_22.rst │ ├── c01_23.md │ ├── c01_23.rst │ ├── c01_24.md │ ├── c01_24.rst │ ├── c01_25.md │ ├── c01_25.rst │ ├── c01_26.md │ ├── c01_26.rst │ ├── c01_27.md │ ├── c01_27.rst │ ├── c01_28.md │ ├── c01_28.rst │ ├── c01_29.md │ ├── c01_29.rst │ ├── c01_30.md │ ├── c01_30.rst │ ├── c01_31.md │ ├── c01_31.rst │ ├── c01_32.md │ └── c01_32.rst ├── c02 │ ├── c02_01.md │ ├── c02_01.rst │ ├── c02_02.md │ ├── c02_02.rst │ ├── c02_03.md │ ├── c02_03.rst │ ├── c02_04.md │ ├── c02_04.rst │ ├── c02_05.md │ ├── c02_05.rst │ ├── c02_06.md │ ├── c02_06.rst │ ├── c02_07.md │ ├── c02_07.rst │ ├── c02_08.md │ ├── c02_08.rst │ ├── c02_09.md │ ├── c02_09.rst │ ├── c02_10.md │ ├── c02_10.rst │ ├── c02_11.md │ ├── c02_11.rst │ ├── c02_12.md │ ├── c02_12.rst │ ├── c02_13.md │ ├── c02_13.rst │ ├── c02_14.md │ ├── c02_14.rst │ ├── c02_15.md │ ├── c02_15.rst │ ├── c02_16.md │ ├── c02_16.rst │ ├── c02_17.md │ ├── c02_17.rst │ ├── c02_18.md │ ├── c02_18.rst │ ├── c02_19.md │ ├── c02_19.rst │ ├── c02_20.md │ ├── c02_20.rst │ ├── c02_21.md │ ├── c02_21.rst │ ├── c02_22.md │ ├── c02_22.rst │ ├── c02_23.md │ └── c02_23.rst ├── c03 │ ├── c03_01.md │ ├── c03_01.rst │ ├── c03_02.md │ ├── c03_02.rst │ ├── c03_03.md │ ├── c03_03.rst │ ├── c03_04.md │ ├── c03_04.rst │ ├── c03_05.md │ ├── c03_05.rst │ ├── c03_06.md │ ├── c03_06.rst │ ├── c03_07.md │ ├── c03_07.rst │ ├── c03_08.md │ ├── c03_08.rst │ ├── c03_09.md │ ├── c03_09.rst │ ├── c03_10.md │ ├── c03_10.rst │ ├── c03_11.md │ ├── c03_11.rst │ ├── c03_12.md │ ├── c03_12.rst │ ├── c03_13.md │ └── c03_13.rst ├── c04 │ ├── c04_01.md │ ├── c04_01.rst │ ├── c04_02.md │ ├── c04_02.rst │ ├── c04_03.md │ └── c04_03.rst ├── c05 │ ├── c05_01.md │ ├── c05_01.rst │ ├── c05_02.md │ ├── c05_02.rst │ ├── c05_03.md │ ├── c05_03.rst │ ├── c05_04.md │ ├── c05_04.rst │ ├── c05_05.md │ ├── c05_05.rst │ ├── c05_06.md │ ├── c05_06.rst │ ├── c05_07.md │ ├── c05_07.rst │ ├── c05_08.md │ ├── c05_08.rst │ ├── c05_09.md │ ├── c05_09.rst │ ├── c05_10.md │ ├── c05_10.rst │ ├── c05_11.md │ ├── c05_11.rst │ ├── c05_12.md │ ├── c05_12.rst │ ├── c05_13.md │ ├── c05_13.rst │ ├── c05_14.md │ ├── c05_14.rst │ ├── c05_15.md │ ├── c05_15.rst │ ├── c05_16.md │ ├── c05_16.rst │ ├── c05_17.md │ ├── c05_17.rst │ ├── c05_18.md │ ├── c05_18.rst │ ├── c05_19.md │ ├── c05_19.rst │ ├── c05_20.md │ ├── c05_20.rst │ ├── c05_21.md │ ├── c05_21.rst │ ├── c05_22.md │ ├── c05_22.rst │ ├── c05_23.md │ ├── c05_23.rst │ ├── c05_24.md │ ├── c05_24.rst │ ├── c05_25.md │ ├── c05_25.rst │ ├── c05_26.md │ ├── c05_26.rst │ ├── c05_27.md │ ├── c05_27.rst │ ├── c05_28.md │ ├── c05_28.rst │ ├── c05_29.md │ ├── c05_29.rst │ ├── c05_30.md │ ├── c05_30.rst │ ├── c05_31.md │ ├── c05_31.rst │ ├── c05_32.md │ ├── c05_32.rst │ ├── c05_33.md │ └── c05_33.rst ├── c06 │ ├── c06_01.md │ ├── c06_01.rst │ ├── c06_02.md │ ├── c06_02.rst │ ├── c06_03.md │ ├── c06_03.rst │ ├── c06_04.md │ ├── c06_04.rst │ ├── c06_05.md │ ├── c06_05.rst │ ├── c06_06.md │ ├── c06_06.rst │ ├── c06_07.md │ ├── c06_07.rst │ ├── c06_08.md │ ├── c06_08.rst │ ├── c06_09.md │ ├── c06_09.rst │ ├── c06_10.md │ ├── c06_10.rst │ ├── c06_11.md │ ├── c06_11.rst │ ├── c06_12.md │ └── c06_12.rst ├── c07 │ ├── c07_01.md │ ├── c07_01.rst │ ├── c07_02.md │ ├── c07_02.rst │ ├── c07_03.md │ ├── c07_03.rst │ ├── c07_04.md │ ├── c07_04.rst │ ├── c07_05.md │ ├── c07_05.rst │ ├── c07_06.md │ ├── c07_06.rst │ ├── c07_07.md │ ├── c07_07.rst │ ├── c07_08.md │ ├── c07_08.rst │ ├── c07_09.md │ ├── c07_09.rst │ ├── c07_10.md │ ├── c07_10.rst │ ├── c07_11.md │ ├── c07_11.rst │ ├── c07_12.md │ ├── c07_12.rst │ ├── c07_13.md │ ├── c07_13.rst │ ├── c07_14.md │ ├── c07_14.rst │ ├── c07_15.md │ ├── c07_15.rst │ ├── c07_16.md │ └── c07_16.rst ├── chapters │ ├── p01.rst │ ├── p02.rst │ ├── p03.rst │ ├── p04.rst │ ├── p05.rst │ ├── p06.rst │ └── p07.rst ├── conf.py ├── index.rst ├── preface.rst ├── roadmap.rst └── robots.txt └── usercustomize.py /.github/workflows/deploy.yml: -------------------------------------------------------------------------------- 1 | name: Deploy site files 2 | 3 | on: 4 | push: 5 | branches: 6 | - master # 只在master上push触发部署 7 | paths-ignore: # 下列文件的变更不触发部署,可以自行添加 8 | - README.md 9 | - LICENSE 10 | - md2rst.py 11 | - source/conf.py 12 | 13 | jobs: 14 | deploy: 15 | runs-on: ubuntu-latest # 使用ubuntu系统镜像运行自动化脚本 16 | 17 | steps: # 自动化步骤 18 | - uses: actions/checkout@v2 # 第一步,下载代码仓库 19 | 20 | - name: Deploy to Server # 第二步,rsync推文件 21 | uses: AEnterprise/rsync-deploy@v1.0 # 使用别人包装好的步骤镜像 22 | env: 23 | DEPLOY_KEY: ${{ secrets.DEPLOY_KEY }} # 引用配置,SSH私钥 24 | ARGS: -avz --delete --exclude='*.pyc' # rsync参数,排除.pyc文件 25 | SERVER_PORT: ${{ secrets.SSH_PORT }} # SSH端口 26 | FOLDER: ./ # 要推送的文件夹,路径相对于代码仓库的根目录 27 | SERVER_IP: ${{ secrets.SSH_HOST }} # 引用配置,服务器的host名(IP或者域名domain.com) 28 | USERNAME: ${{ secrets.SSH_USERNAME }} # 引用配置,服务器登录名 29 | SERVER_DESTINATION: ${{ secrets.WORK_HOME }} # 部署到目标文件夹 30 | - name: Restart server # 第三步,重新部署服务 31 | uses: appleboy/ssh-action@master 32 | env: 33 | WORK_HOME: ${{ secrets.WORK_HOME }} 34 | with: 35 | host: ${{ secrets.SSH_HOST }} # 下面三个配置与上一步类似 36 | username: ${{ secrets.SSH_USERNAME }} 37 | port: ${{ secrets.SSH_PORT }} # SSH端口 38 | key: ${{ secrets.DEPLOY_KEY }} # 私钥 39 | script: | 40 | cd $WORK_HOME 41 | ./rebuild.sh 42 | envs: WORK_HOME # 要传入 script 的环境变量 43 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | *.pyc 3 | Pipfile 4 | test* 5 | build_bak 6 | build 7 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Minimal makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line, and also 5 | # from the environment for the first two. 6 | SPHINXOPTS ?= 7 | SPHINXBUILD ?= sphinx-build 8 | SOURCEDIR = source 9 | BUILDDIR = build 10 | 11 | # Put it first so that "make" without argument is like "make help". 12 | help: 13 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 14 | 15 | .PHONY: help Makefile 16 | 17 | # Catch-all target: route all unknown targets to Sphinx using the new 18 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). 19 | %: Makefile 20 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 21 | -------------------------------------------------------------------------------- /baidu_verify_code-akNcNpmYjb.html: -------------------------------------------------------------------------------- 1 | 3a591fe8805732ea01993ca4769e52e7 -------------------------------------------------------------------------------- /exts/chinese_search.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | def setup(app): 4 | import sphinx.search as search 5 | import zh 6 | search.languages["zh_CN"] = zh.SearchChinese -------------------------------------------------------------------------------- /exts/suffix.dic: -------------------------------------------------------------------------------- 1 | 和 2 | 是 3 | 了 4 | 中 5 | 有 6 | 都 7 | 的 8 | 来 9 | 在 10 | 次 11 | 还 12 | 但 13 | 为 14 | 里 15 | 用 16 | 外 17 | 上 18 | 下 19 | 就 20 | 以 21 | 去 22 | 即 23 | 丁 24 | 万 25 | 乔 26 | 余 27 | 候 28 | 傅 29 | 冯 30 | 刘 31 | 单 32 | 卢 33 | 史 34 | 叶 35 | 吕 36 | 吴 37 | 唐 38 | 夏 39 | 姚 40 | 姜 41 | 孔 42 | 孙 43 | 孟 44 | 宋 45 | 尹 46 | 崔 47 | 常 48 | 康 49 | 廖 50 | 张 51 | 彭 52 | 徐 53 | 戴 54 | 文 55 | 方 56 | 易 57 | 曹 58 | 曾 59 | 朱 60 | 李 61 | 杜 62 | 杨 63 | 林 64 | 梁 65 | 武 66 | 段 67 | 毛 68 | 江 69 | 汤 70 | 沈 71 | 潘 72 | 熊 73 | 王 74 | 田 75 | 白 76 | 石 77 | 秦 78 | 罗 79 | 肖 80 | 胡 81 | 苏 82 | 范 83 | 董 84 | 蒋 85 | 薛 86 | 袁 87 | 谢 88 | 谭 89 | 贾 90 | 赖 91 | 赵 92 | 邓 93 | 邱 94 | 邵 95 | 邹 96 | 郑 97 | 郝 98 | 郭 99 | 金 100 | 钟 101 | 钱 102 | 阎 103 | 陆 104 | 陈 105 | 雷 106 | 韩 107 | 顾 108 | 马 109 | 高 110 | 魏 111 | 黄 112 | 黎 113 | 龙 114 | 龚 -------------------------------------------------------------------------------- /exts/zh.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from sphinx.search import SearchLanguage 3 | from smallseg import SEG 4 | 5 | class SearchChinese(SearchLanguage): 6 | lang = 'zh' 7 | 8 | def init(self, options): 9 | print("reading Chiniese dictionary") 10 | self.seg = SEG() 11 | 12 | def split(self, input): 13 | return self.seg.cut(input.encode("utf8")) 14 | 15 | def word_filter(self, stemmed_word): 16 | return len(stemmed_word) > 1 17 | -------------------------------------------------------------------------------- /make.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | pushd %~dp0 4 | 5 | REM Command file for Sphinx documentation 6 | 7 | if "%SPHINXBUILD%" == "" ( 8 | set SPHINXBUILD=sphinx-build 9 | ) 10 | set SOURCEDIR=source 11 | set BUILDDIR=build 12 | 13 | if "%1" == "" goto help 14 | 15 | %SPHINXBUILD% >NUL 2>NUL 16 | if errorlevel 9009 ( 17 | echo. 18 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx 19 | echo.installed, then set the SPHINXBUILD environment variable to point 20 | echo.to the full path of the 'sphinx-build' executable. Alternatively you 21 | echo.may add the Sphinx directory to PATH. 22 | echo. 23 | echo.If you don't have Sphinx installed, grab it from 24 | echo.http://sphinx-doc.org/ 25 | exit /b 1 26 | ) 27 | 28 | %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% 29 | goto end 30 | 31 | :help 32 | %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% 33 | 34 | :end 35 | popd 36 | -------------------------------------------------------------------------------- /pdf_maker.py: -------------------------------------------------------------------------------- 1 | #!/usr/local/bin/python3 2 | 3 | import os 4 | import glob 5 | import fileinput 6 | import linecache 7 | from functools import partial 8 | 9 | repo_dir = os.getcwd() 10 | source_dir = os.path.join(repo_dir, "source") 11 | all_md_path = os.path.join(repo_dir, "all_v3.0.md",) 12 | 13 | count = 0 14 | 15 | with open(all_md_path, "w") as all_md: 16 | write = partial(print, file=all_md, end="") 17 | os.chdir(source_dir) 18 | 19 | for c_no in sorted(glob.glob("c*")): 20 | if c_no == "chapters" or c_no == "conf.py": 21 | continue 22 | 23 | # 读取并记下章节名 24 | c_name = linecache.getline(os.path.join(source_dir, "chapters", f"{c_no.replace('c', 'p')}.rst"), 2) 25 | write(f"# {c_name}\n\n", file=all_md) 26 | 27 | # 读取每一节的内容 28 | all_md_file = sorted(glob.glob(f"{source_dir}/{c_no}/*.md")) 29 | for line in fileinput.input(all_md_file): 30 | if "20200804124133" in line or "20200607174235" in line: 31 | continue 32 | 33 | if fileinput.isfirstline(): 34 | count += 1 35 | if count%5 == 0: 36 | write("![](http://image.iswbm.com/20210523153308.png)", end="\n\n") 37 | 38 | if line.startswith("# "): 39 | line = line.replace("# ", "## ") 40 | elif line.startswith("## "): 41 | line = line.replace("## ", "### ") 42 | elif line.startswith("### "): 43 | line = line.replace("### ", "#### ") 44 | elif "gif" in line: 45 | line = line.replace("![]", "![该图为GIF,请前往 magic.iswbm.com 浏览]") 46 | 47 | write(line) 48 | 49 | -------------------------------------------------------------------------------- /rebuild.sh: -------------------------------------------------------------------------------- 1 | cat << EOF >/usr/local/lib/python3.10/site-packages/sphinx_rtd_theme/comments.html 2 | 3 | 10 | 11 | EOF 12 | 13 | rm -rf build/ && sphinx-multiversion source build/html && cp -rf build/html/master/* build/html/ 14 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | alabaster==0.7.12 2 | argh==0.26.2 3 | Babel==2.9.1 4 | certifi==2019.6.16 5 | chardet==3.0.4 6 | docutils==0.14 7 | imagesize==1.1.0 8 | Jinja2==2.11.3 9 | livereload==2.6.1 10 | MarkupSafe==1.1.1 11 | packaging==19.0 12 | pathtools==0.1.2 13 | port-for==0.3.1 14 | Pygments==2.7.4 15 | pyparsing==2.4.0 16 | pytz==2019.1 17 | PyYAML==6.0.1 18 | requests==2.32.3 19 | six==1.12.0 20 | snowballstemmer==1.9.0 21 | Sphinx==2.1.2 22 | sphinx-autobuild==0.7.1 23 | sphinx-rtd-theme==0.4.3 24 | sphinxcontrib-applehelp==1.0.1 25 | sphinxcontrib-devhelp==1.0.1 26 | sphinxcontrib-htmlhelp==1.0.2 27 | sphinxcontrib-jsmath==1.0.1 28 | sphinxcontrib-qthelp==1.0.2 29 | sphinxcontrib-serializinghtml==1.1.3 30 | tornado==6.0.3 31 | urllib3==2.2.2 32 | watchdog==0.9.0 33 | sphinxcontrib-disqus==1.1.0 34 | sphinxcontrib-applehelp==1.0.1 35 | sphinxcontrib-devhelp==1.0.1 36 | sphinxcontrib-htmlhelp==1.0.2 37 | sphinxcontrib-jsmath==1.0.1 38 | sphinxcontrib-qthelp==1.0.2 39 | sphinxcontrib-serializinghtml==1.1.3 40 | sphinx-sitemap==2.2.0 41 | sphinx-multiversion==0.2.4 42 | -------------------------------------------------------------------------------- /source/_static/js/baidutongji.js: -------------------------------------------------------------------------------- 1 | var _hmt = _hmt || []; 2 | (function() { 3 | var hm = document.createElement("script"); 4 | hm.src = "https://hm.baidu.com/hm.js?f15534298fc176a5524b6da87883f37f"; 5 | var s = document.getElementsByTagName("script")[0]; 6 | s.parentNode.insertBefore(hm, s); 7 | })(); 8 | -------------------------------------------------------------------------------- /source/_templates/versions.html: -------------------------------------------------------------------------------- 1 | {%- if current_version %} 2 |
3 | 4 | Other Versions 5 | v: {{ current_version.name }} 6 | 7 | 8 |
9 | {%- if versions.tags %} 10 |
11 |
Tags
12 | {%- for item in versions.tags %} 13 |
{{ item.name }}
14 | {%- endfor %} 15 |
16 | {%- endif %} 17 | {%- if versions.branches %} 18 |
19 |
Branches
20 | {%- for item in versions.branches %} 21 |
{{ item.name }}
22 | {%- endfor %} 23 |
24 | {%- endif %} 25 |
26 |
27 | {%- endif %} 28 | -------------------------------------------------------------------------------- /source/aboutme.rst: -------------------------------------------------------------------------------- 1 | ============== 2 | 关于作者 3 | ============== 4 | 5 | * 姓名: 王炳明 6 | * 微信: stromwbm 7 | * 公众号: 《Python编程时光》&《Go编程时光》 8 | * Email: wongbingming@163.com 9 | * GitHub: https://github.com/iswbm 10 | 11 | -------------------------------------------- 12 | 13 | .. image:: http://image.iswbm.com/20200607174235.png 14 | 15 | -------------------------------------------------------------------------------- /source/c01/c01_01.md: -------------------------------------------------------------------------------- 1 | # 1.1 默默无闻的省略号很好用 2 | 3 | ![](http://image.iswbm.com/20200804124133.png) 4 | 5 | 在Python中,一切皆对象,省略号也不例外。 6 | 7 | 在 Python 3 中你可以直接写 `...` 来得到它 8 | 9 | ```python 10 | >>> ... 11 | Ellipsis 12 | >>> type(...) 13 | 14 | ``` 15 | 16 | 而在 Python 2 中没有`...` 这个语法,只能直接写Ellipsis来获取。 17 | 18 | ```python 19 | >>> Ellipsis 20 | Ellipsis 21 | >>> type(Ellipsis) 22 | 23 | >>> 24 | ``` 25 | 26 | 它转为布尔值时为真 27 | 28 | ```python 29 | >>> bool(...) 30 | True 31 | ``` 32 | 33 | 最后,这东西是一个单例。 34 | 35 | ```python 36 | >>> id(...) 37 | 4362672336 38 | >>> id(...) 39 | 4362672336 40 | ``` 41 | 42 | 那这东西有啥用呢? 43 | 44 | 1. 它是 Numpy 的一个语法糖 45 | 2. 在 Python 3 中可以使用 ... 代替 pass 46 | 47 | ```shell 48 | $ cat demo.py 49 | def func01(): 50 | ... 51 | 52 | def func02(): 53 | pass 54 | 55 | func01() 56 | func02() 57 | 58 | print("ok") 59 | 60 | $ python3 demo.py 61 | ok 62 | ``` 63 | 64 | 65 | 66 | -------------------------------------------------------------------------------- /source/c01/c01_01.rst: -------------------------------------------------------------------------------- 1 | 1.1 默默无闻的省略号很好用 2 | ========================== 3 | 4 | .. image:: http://image.iswbm.com/20200804124133.png 5 | 6 | 在Python中,一切皆对象,省略号也不例外。 7 | 8 | 在 Python 3 中你可以直接写 ``...`` 来得到它 9 | 10 | .. code:: python 11 | 12 | >>> ... 13 | Ellipsis 14 | >>> type(...) 15 | 16 | 17 | 而在 Python 2 中没有\ ``...`` 这个语法,只能直接写Ellipsis来获取。 18 | 19 | .. code:: python 20 | 21 | >>> Ellipsis 22 | Ellipsis 23 | >>> type(Ellipsis) 24 | 25 | >>> 26 | 27 | 它转为布尔值时为真 28 | 29 | .. code:: python 30 | 31 | >>> bool(...) 32 | True 33 | 34 | 最后,这东西是一个单例。 35 | 36 | .. code:: python 37 | 38 | >>> id(...) 39 | 4362672336 40 | >>> id(...) 41 | 4362672336 42 | 43 | 那这东西有啥用呢? 44 | 45 | 1. 它是 Numpy 的一个语法糖 46 | 2. 在 Python 3 中可以使用 … 代替 pass 47 | 48 | .. code:: shell 49 | 50 | $ cat demo.py 51 | def func01(): 52 | ... 53 | 54 | def func02(): 55 | pass 56 | 57 | func01() 58 | func02() 59 | 60 | print("ok") 61 | 62 | $ python3 demo.py 63 | ok 64 | -------------------------------------------------------------------------------- /source/c01/c01_02.md: -------------------------------------------------------------------------------- 1 | # 1.2 使用 end 来结束代码块 2 | 3 | ![](http://image.iswbm.com/20200804124133.png) 4 | 5 | 有不少编程语言,循环、判断代码块需要用 end 标明结束,这样一定程度上会使代码逻辑更加清晰一点。 6 | 7 | 但是其实在 Python 这种严格缩进的语言里并没有必要这样做。 8 | 9 | 如果你真的想用,也不是没有办法,具体你看下面这个例子。 10 | 11 | ```python 12 | __builtins__.end = None 13 | 14 | 15 | def my_abs(x): 16 | if x > 0: 17 | return x 18 | else: 19 | return -x 20 | end 21 | end 22 | 23 | print(my_abs(10)) 24 | print(my_abs(-10)) 25 | ``` 26 | 27 | 执行后,输出如下 28 | 29 | ```shell 30 | [root@localhost ~]$ python demo.py 31 | 10 32 | 10 33 | ``` 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /source/c01/c01_02.rst: -------------------------------------------------------------------------------- 1 | 1.2 使用 end 来结束代码块 2 | ========================= 3 | 4 | .. image:: http://image.iswbm.com/20200804124133.png 5 | 6 | 有不少编程语言,循环、判断代码块需要用 end 7 | 标明结束,这样一定程度上会使代码逻辑更加清晰一点。 8 | 9 | 但是其实在 Python 这种严格缩进的语言里并没有必要这样做。 10 | 11 | 如果你真的想用,也不是没有办法,具体你看下面这个例子。 12 | 13 | .. code:: python 14 | 15 | __builtins__.end = None 16 | 17 | 18 | def my_abs(x): 19 | if x > 0: 20 | return x 21 | else: 22 | return -x 23 | end 24 | end 25 | 26 | print(my_abs(10)) 27 | print(my_abs(-10)) 28 | 29 | 执行后,输出如下 30 | 31 | .. code:: shell 32 | 33 | [root@localhost ~]$ python demo.py 34 | 10 35 | 10 36 | -------------------------------------------------------------------------------- /source/c01/c01_03.md: -------------------------------------------------------------------------------- 1 | # 1.3 可直接运行的 zip 包 2 | ![](http://image.iswbm.com/20200804124133.png) 3 | 4 | 我们可以经常看到有 Python 包,居然可以以 zip 包进行发布,并且可以不用解压直接使用。 5 | 6 | 这与大多数人的认识的 Python 包格式不一样,正常人认为 Python 包的格式要嘛 是 egg,要嘛是whl 格式。 7 | 8 | 那么这个zip 是如何制作的呢,请看下面的示例。 9 | 10 | ```shell 11 | [root@localhost ~]# ls -l demo 12 | total 8 13 | -rw-r--r-- 1 root root 30 May 8 19:27 calc.py 14 | -rw-r--r-- 1 root root 35 May 8 19:33 __main__.py 15 | [root@localhost ~]# 16 | [root@localhost ~]# cat demo/__main__.py 17 | import calc 18 | 19 | print(calc.add(2, 3)) 20 | [root@localhost ~]# 21 | [root@localhost ~]# cat demo/calc.py 22 | def add(x, y): 23 | return x+y 24 | [root@localhost ~]# 25 | [root@localhost ~]# python -m zipfile -c demo.zip demo/* 26 | [root@localhost ~]# 27 | ``` 28 | 29 | 制作完成后,我们可以执行用 python 去执行它 30 | 31 | ```shell 32 | [root@localhost ~]# python demo.zip 33 | 5 34 | [root@localhost ~]# 35 | ``` 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /source/c01/c01_03.rst: -------------------------------------------------------------------------------- 1 | 1.3 可直接运行的 zip 包 2 | ======================= 3 | 4 | .. image:: http://image.iswbm.com/20200804124133.png 5 | 6 | 我们可以经常看到有 Python 包,居然可以以 zip 7 | 包进行发布,并且可以不用解压直接使用。 8 | 9 | 这与大多数人的认识的 Python 包格式不一样,正常人认为 Python 包的格式要嘛 10 | 是 egg,要嘛是whl 格式。 11 | 12 | 那么这个zip 是如何制作的呢,请看下面的示例。 13 | 14 | .. code:: shell 15 | 16 | [root@localhost ~]# ls -l demo 17 | total 8 18 | -rw-r--r-- 1 root root 30 May 8 19:27 calc.py 19 | -rw-r--r-- 1 root root 35 May 8 19:33 __main__.py 20 | [root@localhost ~]# 21 | [root@localhost ~]# cat demo/__main__.py 22 | import calc 23 | 24 | print(calc.add(2, 3)) 25 | [root@localhost ~]# 26 | [root@localhost ~]# cat demo/calc.py 27 | def add(x, y): 28 | return x+y 29 | [root@localhost ~]# 30 | [root@localhost ~]# python -m zipfile -c demo.zip demo/* 31 | [root@localhost ~]# 32 | 33 | 制作完成后,我们可以执行用 python 去执行它 34 | 35 | .. code:: shell 36 | 37 | [root@localhost ~]# python demo.zip 38 | 5 39 | [root@localhost ~]# 40 | -------------------------------------------------------------------------------- /source/c01/c01_04.md: -------------------------------------------------------------------------------- 1 | # 1.4 反斜杠的倔强: 不写最后 2 | ![](http://image.iswbm.com/20200804124133.png) 3 | 4 | `\` 在 Python 中的用法主要有两种 5 | 6 | **1、在行尾时,用做续行符** 7 | 8 | ```python 9 | [root@localhost ~]$ cat demo.py 10 | print("hello "\ 11 | "world") 12 | [root@localhost ~]$ 13 | [root@localhost ~]$ python demo.py 14 | hello world 15 | ``` 16 | 17 | 18 | 19 | **2、在字符串中,用做转义字符,可以将普通字符转化为有特殊含义的字符。** 20 | 21 | ```python 22 | >>> str1='\nhello'  #换行 23 | >>> print(str1) 24 | 25 | hello 26 | >>> str2='\thello'  #tab 27 | >>> print(str2) 28 | hello 29 | ``` 30 | 31 | 但是如果你用单`\`结尾是会报语法错误的 32 | 33 | ```python 34 | >>> str3="\" 35 | File "", line 1 36 | str3="\" 37 | ^ 38 | SyntaxError: EOL while scanning string literal 39 | ``` 40 | 41 | 就算你指定它是个 raw 字符串,也不行。 42 | 43 | ```python 44 | >>> str3=r"\" 45 | File "", line 1 46 | str3=r"\" 47 | ^ 48 | SyntaxError: EOL while scanning string literal 49 | ``` 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /source/c01/c01_04.rst: -------------------------------------------------------------------------------- 1 | 1.4 反斜杠的倔强: 不写最后 2 | ========================== 3 | 4 | .. image:: http://image.iswbm.com/20200804124133.png 5 | 6 | ``\`` 在 Python 中的用法主要有两种 7 | 8 | **1、在行尾时,用做续行符** 9 | 10 | .. code:: python 11 | 12 | [root@localhost ~]$ cat demo.py 13 | print("hello "\ 14 | "world") 15 | [root@localhost ~]$ 16 | [root@localhost ~]$ python demo.py 17 | hello world 18 | 19 | **2、在字符串中,用做转义字符,可以将普通字符转化为有特殊含义的字符。** 20 | 21 | .. code:: python 22 | 23 | >>> str1='\nhello'  #换行 24 | >>> print(str1) 25 | 26 | hello 27 | >>> str2='\thello'  #tab 28 | >>> print(str2) 29 | hello 30 | 31 | 但是如果你用单\ ``\``\ 结尾是会报语法错误的 32 | 33 | .. code:: python 34 | 35 | >>> str3="\" 36 | File "", line 1 37 | str3="\" 38 | ^ 39 | SyntaxError: EOL while scanning string literal 40 | 41 | 就算你指定它是个 raw 字符串,也不行。 42 | 43 | .. code:: python 44 | 45 | >>> str3=r"\" 46 | File "", line 1 47 | str3=r"\" 48 | ^ 49 | SyntaxError: EOL while scanning string literal 50 | -------------------------------------------------------------------------------- /source/c01/c01_05.md: -------------------------------------------------------------------------------- 1 | # 1.5 如何修改解释器提示符 2 | ![](http://image.iswbm.com/20200804124133.png) 3 | 4 | 这个当做今天的一个小彩蛋吧。应该算是比较冷门的,估计知道的人很少了吧。 5 | 6 | 正常情况下,我们在 终端下 执行Python 命令是这样的。 7 | 8 | ```python 9 | >>> for i in range(2): 10 | ... print (i) 11 | ... 12 | 0 13 | 1 14 | ``` 15 | 16 | 你是否想过 `>>>` 和 `...` 这两个提示符也是可以修改的呢? 17 | 18 | ```python 19 | >>> import sys 20 | >>> sys.ps1 21 | '>>> ' 22 | >>> sys.ps2 23 | '... ' 24 | >>> 25 | >>> sys.ps2 = '---------------- ' 26 | >>> sys.ps1 = 'Python编程时光>>>' 27 | Python编程时光>>>for i in range(2): 28 | ---------------- print (i) 29 | ---------------- 30 | 0 31 | 1 32 | ``` 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /source/c01/c01_05.rst: -------------------------------------------------------------------------------- 1 | 1.5 如何修改解释器提示符 2 | ======================== 3 | 4 | .. image:: http://image.iswbm.com/20200804124133.png 5 | 6 | 这个当做今天的一个小彩蛋吧。应该算是比较冷门的,估计知道的人很少了吧。 7 | 8 | 正常情况下,我们在 终端下 执行Python 命令是这样的。 9 | 10 | .. code:: python 11 | 12 | >>> for i in range(2): 13 | ... print (i) 14 | ... 15 | 0 16 | 1 17 | 18 | 你是否想过 ``>>>`` 和 ``...`` 这两个提示符也是可以修改的呢? 19 | 20 | .. code:: python 21 | 22 | >>> import sys 23 | >>> sys.ps1 24 | '>>> ' 25 | >>> sys.ps2 26 | '... ' 27 | >>> 28 | >>> sys.ps2 = '---------------- ' 29 | >>> sys.ps1 = 'Python编程时光>>>' 30 | Python编程时光>>>for i in range(2): 31 | ---------------- print (i) 32 | ---------------- 33 | 0 34 | 1 35 | -------------------------------------------------------------------------------- /source/c01/c01_06.md: -------------------------------------------------------------------------------- 1 | # 1.6 简洁而优雅的链式比较 2 | ![](http://image.iswbm.com/20200804124133.png) 3 | 4 | 先给你看一个示例: 5 | 6 | ```python 7 | >>> False == False == True 8 | False 9 | ``` 10 | 11 | 你知道这个表达式为什么会会返回 False 吗? 12 | 13 | 它的运行原理与下面这个类似,是不是有点头绪了: 14 | 15 | ```python 16 | if 80 < score <= 90: 17 | print("成绩良好") 18 | ``` 19 | 20 | 如果你还是不明白,那我再给你整个第一个例子的等价写法。 21 | 22 | ```python 23 | >>> False == False and False == True 24 | False 25 | ``` 26 | 27 | 这个用法叫做链式比较。 28 | 29 | -------------------------------------------------------------------------------- /source/c01/c01_06.rst: -------------------------------------------------------------------------------- 1 | 1.6 简洁而优雅的链式比较 2 | ======================== 3 | 4 | .. image:: http://image.iswbm.com/20200804124133.png 5 | 6 | 先给你看一个示例: 7 | 8 | .. code:: python 9 | 10 | >>> False == False == True 11 | False 12 | 13 | 你知道这个表达式为什么会会返回 False 吗? 14 | 15 | 它的运行原理与下面这个类似,是不是有点头绪了: 16 | 17 | .. code:: python 18 | 19 | if 80 < score <= 90: 20 | print("成绩良好") 21 | 22 | 如果你还是不明白,那我再给你整个第一个例子的等价写法。 23 | 24 | .. code:: python 25 | 26 | >>> False == False and False == True 27 | False 28 | 29 | 这个用法叫做链式比较。 30 | -------------------------------------------------------------------------------- /source/c01/c01_07.md: -------------------------------------------------------------------------------- 1 | # 1.7 and 和 or 的短路效应 2 | ![](http://image.iswbm.com/20200804124133.png) 3 | 4 | and 和 or 是我们再熟悉不过的两个逻辑运算符,在 Python 也有它的妙用。 5 | 6 | - 当一个 **or 表达式**中所有值都为真,Python会选择第一个值 7 | 8 | - 当一个 **and 表达式** 所有值都为真,Python 会选择最后一个值。 9 | 10 | 示例如下: 11 | 12 | ```python 13 | >>>(2 or 3) * (5 and 6 and 7) 14 | 14 # 2*7 15 | ``` 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /source/c01/c01_07.rst: -------------------------------------------------------------------------------- 1 | 1.7 and 和 or 的短路效应 2 | ======================== 3 | 4 | .. image:: http://image.iswbm.com/20200804124133.png 5 | 6 | and 和 or 是我们再熟悉不过的两个逻辑运算符,在 Python 也有它的妙用。 7 | 8 | - 当一个 **or 表达式**\ 中所有值都为真,Python会选择第一个值 9 | 10 | - 当一个 **and 表达式** 所有值都为真,Python 会选择最后一个值。 11 | 12 | 示例如下: 13 | 14 | .. code:: python 15 | 16 | >>>(2 or 3) * (5 and 6 and 7) 17 | 14 # 2*7 18 | -------------------------------------------------------------------------------- /source/c01/c01_08.md: -------------------------------------------------------------------------------- 1 | # 1.8 连接多个列表最极客的方式 2 | ![](http://image.iswbm.com/20200804124133.png) 3 | 4 | ```python 5 | >>> a = [1,2] 6 | >>> b = [3,4] 7 | >>> c = [5,6] 8 | >>> 9 | >>> sum((a,b,c), []) 10 | [1, 2, 3, 4, 5, 6] 11 | ``` 12 | 13 | 14 | -------------------------------------------------------------------------------- /source/c01/c01_08.rst: -------------------------------------------------------------------------------- 1 | 1.8 连接多个列表最极客的方式 2 | ============================ 3 | 4 | .. image:: http://image.iswbm.com/20200804124133.png 5 | 6 | .. code:: python 7 | 8 | >>> a = [1,2] 9 | >>> b = [3,4] 10 | >>> c = [5,6] 11 | >>> 12 | >>> sum((a,b,c), []) 13 | [1, 2, 3, 4, 5, 6] 14 | -------------------------------------------------------------------------------- /source/c01/c01_09.md: -------------------------------------------------------------------------------- 1 | # 1.9 字典居然是可以排序的? 2 | ![](http://image.iswbm.com/20200804124133.png) 3 | 4 | 在 Python 3.6 之前字典不可排序的思想,似乎已经根深蒂固。 5 | 6 | ```python 7 | # Python2.7.10 8 | >>> mydict = {str(i):i for i in range(5)} 9 | >>> mydict 10 | {'1': 1, '0': 0, '3': 3, '2': 2, '4': 4} 11 | ``` 12 | 13 | 假如哪一天,有人跟你说字典也可以是有序的,不要惊讶,那确实是真的 14 | 15 | 在 Python3.6 + 中字典已经是有序的,并且效率相较之前的还有所提升,具体信息你可以去查询相关资料。 16 | 17 | ```python 18 | # Python3.6.7 19 | >>> mydict = {str(i):i for i in range(5)} 20 | >>> mydict 21 | {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4} 22 | ``` 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /source/c01/c01_09.rst: -------------------------------------------------------------------------------- 1 | 1.9 字典居然是可以排序的? 2 | ========================== 3 | 4 | .. image:: http://image.iswbm.com/20200804124133.png 5 | 6 | 在 Python 3.6 之前字典不可排序的思想,似乎已经根深蒂固。 7 | 8 | .. code:: python 9 | 10 | # Python2.7.10 11 | >>> mydict = {str(i):i for i in range(5)} 12 | >>> mydict 13 | {'1': 1, '0': 0, '3': 3, '2': 2, '4': 4} 14 | 15 | 假如哪一天,有人跟你说字典也可以是有序的,不要惊讶,那确实是真的 16 | 17 | 在 Python3.6 + 18 | 中字典已经是有序的,并且效率相较之前的还有所提升,具体信息你可以去查询相关资料。 19 | 20 | .. code:: python 21 | 22 | # Python3.6.7 23 | >>> mydict = {str(i):i for i in range(5)} 24 | >>> mydict 25 | {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4} 26 | -------------------------------------------------------------------------------- /source/c01/c01_10.md: -------------------------------------------------------------------------------- 1 | # 1.10 哪些情况下不需要续行符? 2 | ![](http://image.iswbm.com/20200804124133.png) 3 | 4 | 在写代码时,为了代码的可读性,代码的排版是尤为重要的。 5 | 6 | 为了实现高可读性的代码,我们常常使用到的就是续行符 `\`。 7 | 8 | ```python 9 | >>> a = 'talk is cheap,'\ 10 | ... 'show me the code.' 11 | >>> 12 | >>> print(a) 13 | talk is cheap,show me the code. 14 | ``` 15 | 16 | 那有哪些情况下,是不需要写续行符的呢? 17 | 18 | 经过总结,在这些符号中间的代码换行可以省略掉续行符:`[]`,`()`,`{}` 19 | 20 | ```python 21 | >>> my_list=[1,2,3, 22 | ... 4,5,6] 23 | 24 | >>> my_tuple=(1,2,3, 25 | ... 4,5,6) 26 | 27 | >>> my_dict={"name": "MING", 28 | ... "gender": "male"} 29 | ``` 30 | 31 | 另外还有,在多行文本注释中 `'''` ,续行符也是可以不写的。 32 | 33 | ```python 34 | >>> text = '''talk is cheap, 35 | ... show me code.''' 36 | >>> 37 | ``` 38 | 39 | 但是这种写法回车会自动转化为 `\n` 40 | 41 | ```python 42 | >>> text = '''talk is cheap, 43 | ... show me code.''' 44 | >>> text 45 | 'talk is cheap,\nshow me code.' 46 | ``` 47 | 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /source/c01/c01_10.rst: -------------------------------------------------------------------------------- 1 | 1.10 哪些情况下不需要续行符? 2 | ============================= 3 | 4 | .. image:: http://image.iswbm.com/20200804124133.png 5 | 6 | 在写代码时,为了代码的可读性,代码的排版是尤为重要的。 7 | 8 | 为了实现高可读性的代码,我们常常使用到的就是续行符 ``\``\ 。 9 | 10 | .. code:: python 11 | 12 | >>> a = 'talk is cheap,'\ 13 | ... 'show me the code.' 14 | >>> 15 | >>> print(a) 16 | talk is cheap,show me the code. 17 | 18 | 那有哪些情况下,是不需要写续行符的呢? 19 | 20 | 经过总结,在这些符号中间的代码换行可以省略掉续行符:\ ``[]``,\ ``()``,\ ``{}`` 21 | 22 | .. code:: python 23 | 24 | >>> my_list=[1,2,3, 25 | ... 4,5,6] 26 | 27 | >>> my_tuple=(1,2,3, 28 | ... 4,5,6) 29 | 30 | >>> my_dict={"name": "MING", 31 | ... "gender": "male"} 32 | 33 | 另外还有,在多行文本注释中 ``'''`` ,续行符也是可以不写的。 34 | 35 | .. code:: python 36 | 37 | >>> text = '''talk is cheap, 38 | ... show me code.''' 39 | >>> 40 | 41 | 但是这种写法回车会自动转化为 ``\n`` 42 | 43 | .. code:: python 44 | 45 | >>> text = '''talk is cheap, 46 | ... show me code.''' 47 | >>> text 48 | 'talk is cheap,\nshow me code.' 49 | -------------------------------------------------------------------------------- /source/c01/c01_11.md: -------------------------------------------------------------------------------- 1 | # 1.11 用户无感知的小整数池 2 | ![](http://image.iswbm.com/20200804124133.png) 3 | 4 | 为避免整数频繁申请和销毁内存空间,Python 定义了一个小整数池 [-5, 256] 这些整数对象是提前建立好的,不会被垃圾回收。 5 | 6 | 以下代码请在 终端Python环境下测试,如果你是在IDE中测试,由于 IDE 的影响,效果会有所不同。 7 | 8 | ```python 9 | >>> a = -6 10 | >>> b = -6 11 | >>> a is b 12 | False 13 | 14 | >>> a = 256 15 | >>> b = 256 16 | >>> a is b 17 | True 18 | 19 | >>> a = 257 20 | >>> b = 257 21 | >>> a is b 22 | False 23 | 24 | >>> a = 257; b = 257 25 | >>> a is b 26 | True 27 | ``` 28 | 29 | **问题又来了:最后一个示例,为啥是True?** 30 | 31 | 因为当你在同一行里,同时给两个变量赋同一值时,解释器知道这个对象已经生成,那么它就会引用到同一个对象。如果分成两行的话,解释器并不知道这个对象已经存在了,就会重新申请内存存放这个对象。 32 | 33 | 34 | -------------------------------------------------------------------------------- /source/c01/c01_11.rst: -------------------------------------------------------------------------------- 1 | 1.11 用户无感知的小整数池 2 | ========================= 3 | 4 | .. image:: http://image.iswbm.com/20200804124133.png 5 | 6 | 为避免整数频繁申请和销毁内存空间,Python 定义了一个小整数池 [-5, 256] 7 | 这些整数对象是提前建立好的,不会被垃圾回收。 8 | 9 | 以下代码请在 终端Python环境下测试,如果你是在IDE中测试,由于 IDE 10 | 的影响,效果会有所不同。 11 | 12 | .. code:: python 13 | 14 | >>> a = -6 15 | >>> b = -6 16 | >>> a is b 17 | False 18 | 19 | >>> a = 256 20 | >>> b = 256 21 | >>> a is b 22 | True 23 | 24 | >>> a = 257 25 | >>> b = 257 26 | >>> a is b 27 | False 28 | 29 | >>> a = 257; b = 257 30 | >>> a is b 31 | True 32 | 33 | **问题又来了:最后一个示例,为啥是True?** 34 | 35 | 因为当你在同一行里,同时给两个变量赋同一值时,解释器知道这个对象已经生成,那么它就会引用到同一个对象。如果分成两行的话,解释器并不知道这个对象已经存在了,就会重新申请内存存放这个对象。 36 | -------------------------------------------------------------------------------- /source/c01/c01_12.md: -------------------------------------------------------------------------------- 1 | # 1.12 神奇的 intern 机制 2 | ![](http://image.iswbm.com/20200804124133.png) 3 | 4 | 字符串类型作为Python中最常用的数据类型之一,Python解释器为了提高字符串使用的效率和使用性能,做了很多优化。 5 | 6 | 例如:Python解释器中使用了 intern(字符串驻留)的技术来提高字符串效率,什么是intern机制?就是同样的字符串对象仅仅会保存一份,放在一个字符串储蓄池中,是共用的,当然,肯定不能改变,这也决定了字符串必须是不可变对象。 7 | 8 | ```python 9 | >>> s1="hello" 10 | >>> s2="hello" 11 | >>> s1 is s2 12 | True 13 | 14 | # 如果有空格,默认不启用intern机制 15 | >>> s1="hell o" 16 | >>> s2="hell o" 17 | >>> s1 is s2 18 | False 19 | 20 | # 如果一个字符串长度超过20个字符,不启动intern机制 21 | >>> s1 = "a" * 20 22 | >>> s2 = "a" * 20 23 | >>> s1 is s2 24 | True 25 | 26 | >>> s1 = "a" * 21 27 | >>> s2 = "a" * 21 28 | >>> s1 is s2 29 | False 30 | 31 | >>> s1 = "ab" * 10 32 | >>> s2 = "ab" * 10 33 | >>> s1 is s2 34 | True 35 | 36 | >>> s1 = "ab" * 11 37 | >>> s2 = "ab" * 11 38 | >>> s1 is s2 39 | False 40 | ``` 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /source/c01/c01_12.rst: -------------------------------------------------------------------------------- 1 | 1.12 神奇的 intern 机制 2 | ======================= 3 | 4 | .. image:: http://image.iswbm.com/20200804124133.png 5 | 6 | 字符串类型作为Python中最常用的数据类型之一,Python解释器为了提高字符串使用的效率和使用性能,做了很多优化。 7 | 8 | 例如:Python解释器中使用了 9 | intern(字符串驻留)的技术来提高字符串效率,什么是intern机制?就是同样的字符串对象仅仅会保存一份,放在一个字符串储蓄池中,是共用的,当然,肯定不能改变,这也决定了字符串必须是不可变对象。 10 | 11 | .. code:: python 12 | 13 | >>> s1="hello" 14 | >>> s2="hello" 15 | >>> s1 is s2 16 | True 17 | 18 | # 如果有空格,默认不启用intern机制 19 | >>> s1="hell o" 20 | >>> s2="hell o" 21 | >>> s1 is s2 22 | False 23 | 24 | # 如果一个字符串长度超过20个字符,不启动intern机制 25 | >>> s1 = "a" * 20 26 | >>> s2 = "a" * 20 27 | >>> s1 is s2 28 | True 29 | 30 | >>> s1 = "a" * 21 31 | >>> s2 = "a" * 21 32 | >>> s1 is s2 33 | False 34 | 35 | >>> s1 = "ab" * 10 36 | >>> s2 = "ab" * 10 37 | >>> s1 is s2 38 | True 39 | 40 | >>> s1 = "ab" * 11 41 | >>> s2 = "ab" * 11 42 | >>> s1 is s2 43 | False 44 | -------------------------------------------------------------------------------- /source/c01/c01_13.md: -------------------------------------------------------------------------------- 1 | # 1.13 site-packages和 dist-packages 2 | ![](http://image.iswbm.com/20200804124133.png) 3 | 4 | 如果你足够细心,你会在你的机器上,有些包是安装在 **site-packages** 下,而有些包安装在 **dist-packages** 下。 5 | 6 | **它们有什么区别呢?** 7 | 8 | 一般情况下,你只见过 site-packages 这个目录,而你所安装的包也将安装在 这个目录下。 9 | 10 | 而 dist-packages 其实是 debian 系的 Linux 系统(如 Ubuntu)才特有的目录,当你使用 apt 去安装的 Python 包会使用 dist-packages,而你使用 pip 或者 easy_install 安装的包还是照常安装在 site-packages 下。 11 | 12 | Debian 这么设计的原因,是为了减少不同来源的 Python 之间产生的冲突。 13 | 14 | 如何查找 Python 安装目录 15 | 16 | ```python 17 | >>> from distutils.sysconfig import get_python_lib 18 | >>> print(get_python_lib()) 19 | /usr/lib/python2.7/site-packages 20 | ``` 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /source/c01/c01_13.rst: -------------------------------------------------------------------------------- 1 | 1.13 site-packages和 dist-packages 2 | ================================== 3 | 4 | .. image:: http://image.iswbm.com/20200804124133.png 5 | 6 | 如果你足够细心,你会在你的机器上,有些包是安装在 **site-packages** 7 | 下,而有些包安装在 **dist-packages** 下。 8 | 9 | **它们有什么区别呢?** 10 | 11 | 一般情况下,你只见过 site-packages 这个目录,而你所安装的包也将安装在 12 | 这个目录下。 13 | 14 | 而 dist-packages 其实是 debian 系的 Linux 系统(如 15 | Ubuntu)才特有的目录,当你使用 apt 去安装的 Python 包会使用 16 | dist-packages,而你使用 pip 或者 easy_install 安装的包还是照常安装在 17 | site-packages 下。 18 | 19 | Debian 这么设计的原因,是为了减少不同来源的 Python 之间产生的冲突。 20 | 21 | 如何查找 Python 安装目录 22 | 23 | .. code:: python 24 | 25 | >>> from distutils.sysconfig import get_python_lib 26 | >>> print(get_python_lib()) 27 | /usr/lib/python2.7/site-packages 28 | -------------------------------------------------------------------------------- /source/c01/c01_14.md: -------------------------------------------------------------------------------- 1 | # 1.14 argument 和 parameter 的区别? 2 | ![](http://image.iswbm.com/20200804124133.png) 3 | 4 | arguments 和 parameter 的翻译都是参数,在中文场景下,二者混用基本没有问题,毕竟都叫参数嘛。 5 | 6 | 但若要严格再进行区分,它们实际上还有各自的叫法 7 | 8 | - parameter:形参(**formal parameter**),体现在函数内部,作用域是这个函数体。 9 | - argument :实参(**actual parameter**),调用函数实际传递的参数。 10 | 11 | 举个例子,如下这段代码,`"error"` 为 argument,而 msg 为 `parameter`。 12 | 13 | ```python 14 | def output_msg(msg): 15 | print(msg) 16 | 17 | output_msg("error") 18 | ``` 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /source/c01/c01_14.rst: -------------------------------------------------------------------------------- 1 | 1.14 argument 和 parameter 的区别? 2 | ================================== 3 | 4 | .. image:: http://image.iswbm.com/20200804124133.png 5 | 6 | arguments 和 parameter 7 | 的翻译都是参数,在中文场景下,二者混用基本没有问题,毕竟都叫参数嘛。 8 | 9 | 但若要严格再进行区分,它们实际上还有各自的叫法 10 | 11 | - parameter:形参(\ **formal 12 | parameter**\ ),体现在函数内部,作用域是这个函数体。 13 | - argument :实参(\ **actual parameter**\ ),调用函数实际传递的参数。 14 | 15 | 举个例子,如下这段代码,\ ``"error"`` 为 argument,而 msg 为 16 | ``parameter``\ 。 17 | 18 | .. code:: python 19 | 20 | def output_msg(msg): 21 | print(msg) 22 | 23 | output_msg("error") 24 | -------------------------------------------------------------------------------- /source/c01/c01_15.md: -------------------------------------------------------------------------------- 1 | # 1.15 /usr/bin/env python 有什么用? 2 | ![](http://image.iswbm.com/20200804124133.png) 3 | 4 | 我们经常会在别人的脚本或者项目的入口文件里看到第一行是下面这样 5 | 6 | ```shell 7 | #!/usr/bin/python 8 | ``` 9 | 10 | 或者这样 11 | 12 | ```shell 13 | #!/usr/bin/env python 14 | ``` 15 | 16 | 这两者有什么区别呢? 17 | 18 | 稍微接触过 linux 的人都知道 `/usr/bin/python` 就是我们执行 `python` 进入console 模式里的 `python` 19 | 20 | ![](http://image.iswbm.com/20200331184021.png) 21 | 22 | 而当你在可执行文件头里使用 `#!` + `/usr/bin/python` ,意思就是说你得用哪个软件 (python)来执行这个文件。 23 | 24 | 那么加和不加有什么区别呢? 25 | 26 | 不加的话,你每次执行这个脚本时,都得这样: `python xx.py` , 27 | 28 | ![](http://image.iswbm.com/20200331185034.png) 29 | 30 | 有没有一种方式?可以省去每次都加 `python` 呢? 31 | 32 | 当然有,你可以文件头里加上`#!/usr/bin/python` ,那么当这个文件有可执行权限 时,只直接写这个脚本文件,就像下面这样。 33 | 34 | ![](http://image.iswbm.com/20200331184755.png) 35 | 36 | 明白了这个后,再来看看 `!/usr/bin/env python` 这个 又是什么意思 ? 37 | 38 | 当我执行 `env python` 时,自动进入了 python console 的模式。 39 | 40 | ![](http://image.iswbm.com/20200331185741.png) 41 | 42 | 这是为什么?和 直接执行 python 好像没什么区别呀 43 | 44 | 当你执行 `env python` 时,它其实会去 `env | grep PATH` 里(也就是 /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin )这几个路径里去依次查找名为python的可执行文件。 45 | 46 | 找到一个就直接执行,上面我们的 python 路径是在 `/usr/bin/python` 里,在 `PATH` 列表里倒数第二个目录下,所以当我在 `/usr/local/sbin` 下创建一个名字也为 python 的可执行文件时,就会执行 `/usr/local/sbin/python` 了。 47 | 48 | 具体演示过程,你可以看下面。 49 | 50 | ![](http://image.iswbm.com/20200331190224.png) 51 | 52 | 那么对于这两者,我们应该使用哪个呢? 53 | 54 | 个人感觉应该优先使用 `#!/usr/bin/env python`,因为不是所有的机器的 python 解释器都是 `/usr/bin/python` 。 55 | 56 | -------------------------------------------------------------------------------- /source/c01/c01_15.rst: -------------------------------------------------------------------------------- 1 | 1.15 /usr/bin/env python 有什么用? 2 | =================================== 3 | 4 | .. image:: http://image.iswbm.com/20200804124133.png 5 | 6 | 我们经常会在别人的脚本或者项目的入口文件里看到第一行是下面这样 7 | 8 | .. code:: shell 9 | 10 | #!/usr/bin/python 11 | 12 | 或者这样 13 | 14 | .. code:: shell 15 | 16 | #!/usr/bin/env python 17 | 18 | 这两者有什么区别呢? 19 | 20 | 稍微接触过 linux 的人都知道 ``/usr/bin/python`` 就是我们执行 ``python`` 21 | 进入console 模式里的 ``python`` 22 | 23 | .. image:: http://image.iswbm.com/20200331184021.png 24 | 25 | 而当你在可执行文件头里使用 ``#!`` + ``/usr/bin/python`` 26 | ,意思就是说你得用哪个软件 (python)来执行这个文件。 27 | 28 | 那么加和不加有什么区别呢? 29 | 30 | 不加的话,你每次执行这个脚本时,都得这样: ``python xx.py`` , 31 | 32 | .. image:: http://image.iswbm.com/20200331185034.png 33 | 34 | 有没有一种方式?可以省去每次都加 ``python`` 呢? 35 | 36 | 当然有,你可以文件头里加上\ ``#!/usr/bin/python`` 37 | ,那么当这个文件有可执行权限 时,只直接写这个脚本文件,就像下面这样。 38 | 39 | .. image:: http://image.iswbm.com/20200331184755.png 40 | 41 | 明白了这个后,再来看看 ``!/usr/bin/env python`` 这个 又是什么意思 ? 42 | 43 | 当我执行 ``env python`` 时,自动进入了 python console 的模式。 44 | 45 | .. image:: http://image.iswbm.com/20200331185741.png 46 | 47 | 这是为什么?和 直接执行 python 好像没什么区别呀 48 | 49 | 当你执行 ``env python`` 时,它其实会去 ``env | grep PATH`` 里(也就是 50 | /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin 51 | )这几个路径里去依次查找名为python的可执行文件。 52 | 53 | 找到一个就直接执行,上面我们的 python 路径是在 ``/usr/bin/python`` 54 | 里,在 ``PATH`` 列表里倒数第二个目录下,所以当我在 ``/usr/local/sbin`` 55 | 下创建一个名字也为 python 的可执行文件时,就会执行 56 | ``/usr/local/sbin/python`` 了。 57 | 58 | 具体演示过程,你可以看下面。 59 | 60 | .. image:: http://image.iswbm.com/20200331190224.png 61 | 62 | 那么对于这两者,我们应该使用哪个呢? 63 | 64 | 个人感觉应该优先使用 ``#!/usr/bin/env python``\ ,因为不是所有的机器的 65 | python 解释器都是 ``/usr/bin/python`` 。 66 | -------------------------------------------------------------------------------- /source/c01/c01_16.md: -------------------------------------------------------------------------------- 1 | # 1.16 dict() 与 {} 生成空字典有什么区别? 2 | ![](http://image.iswbm.com/20200804124133.png) 3 | 4 | 在初始化一个空字典时,有的人会写 dict(),而有的人会写成 {} 5 | 6 | 很多人会想当然的认为二者是等同的,但实际情况却不是这样的。 7 | 8 | 在运行效率上,{} 会比 dict() 快三倍左右。 9 | 10 | 使用 timeit 模块,可以轻松测出这个结果 11 | 12 | ```shell 13 | $ python -m timeit -n 1000000 -r 5 -v "dict()" 14 | raw times: 0.0996 0.0975 0.0969 0.0969 0.0994 15 | 1000000 loops, best of 5: 0.0969 usec per loop 16 | $ 17 | $ python -m timeit -n 1000000 -r 5 -v "{}" 18 | raw times: 0.0305 0.0283 0.0272 0.03 0.0317 19 | 1000000 loops, best of 5: 0.0272 usec per loop 20 | ``` 21 | 22 | 那为什么会这样呢? 23 | 24 | 探究这个过程,可以使用 dis 模块 25 | 26 | 当使用 {} 时 27 | 28 | ```shell 29 | $ cat demo.py 30 | {} 31 | $ 32 | $ python -m dis demo.py 33 | 1 0 BUILD_MAP 0 34 | 2 POP_TOP 35 | 4 LOAD_CONST 0 (None) 36 | 6 RETURN_VALUE 37 | ``` 38 | 39 | 当使用 dict() 时: 40 | 41 | ```shell 42 | $ cat demo.py 43 | dict() 44 | $ 45 | $ python -m dis demo.py 46 | 1 0 LOAD_NAME 0 (dict) 47 | 2 CALL_FUNCTION 0 48 | 4 POP_TOP 49 | 6 LOAD_CONST 0 (None) 50 | 8 RETURN_VALUE 51 | ``` 52 | 53 | 可以发现使用 dict(),会多了个调用函数的过程,而这个过程会有进出栈的操作,相对更加耗时。 54 | 55 | -------------------------------------------------------------------------------- /source/c01/c01_16.rst: -------------------------------------------------------------------------------- 1 | 1.16 dict() 与 {} 生成空字典有什么区别? 2 | ======================================== 3 | 4 | .. image:: http://image.iswbm.com/20200804124133.png 5 | 6 | 在初始化一个空字典时,有的人会写 dict(),而有的人会写成 {} 7 | 8 | 很多人会想当然的认为二者是等同的,但实际情况却不是这样的。 9 | 10 | 在运行效率上,{} 会比 dict() 快三倍左右。 11 | 12 | 使用 timeit 模块,可以轻松测出这个结果 13 | 14 | .. code:: shell 15 | 16 | $ python -m timeit -n 1000000 -r 5 -v "dict()" 17 | raw times: 0.0996 0.0975 0.0969 0.0969 0.0994 18 | 1000000 loops, best of 5: 0.0969 usec per loop 19 | $ 20 | $ python -m timeit -n 1000000 -r 5 -v "{}" 21 | raw times: 0.0305 0.0283 0.0272 0.03 0.0317 22 | 1000000 loops, best of 5: 0.0272 usec per loop 23 | 24 | 那为什么会这样呢? 25 | 26 | 探究这个过程,可以使用 dis 模块 27 | 28 | 当使用 {} 时 29 | 30 | .. code:: shell 31 | 32 | $ cat demo.py 33 | {} 34 | $ 35 | $ python -m dis demo.py 36 | 1 0 BUILD_MAP 0 37 | 2 POP_TOP 38 | 4 LOAD_CONST 0 (None) 39 | 6 RETURN_VALUE 40 | 41 | 当使用 dict() 时: 42 | 43 | .. code:: shell 44 | 45 | $ cat demo.py 46 | dict() 47 | $ 48 | $ python -m dis demo.py 49 | 1 0 LOAD_NAME 0 (dict) 50 | 2 CALL_FUNCTION 0 51 | 4 POP_TOP 52 | 6 LOAD_CONST 0 (None) 53 | 8 RETURN_VALUE 54 | 55 | 可以发现使用 56 | dict(),会多了个调用函数的过程,而这个过程会有进出栈的操作,相对更加耗时。 57 | -------------------------------------------------------------------------------- /source/c01/c01_17.md: -------------------------------------------------------------------------------- 1 | # 1.17 有趣但没啥用的 import 用法 2 | ![](http://image.iswbm.com/20200804124133.png) 3 | 4 | import 是 Python 导包的方式。 5 | 6 | 你知道 Python 中内置了一些很有(wu)趣(liao)的包吗? 7 | 8 | **Hello World** 9 | 10 | ``` 11 | >>> import __hello__ 12 | Hello World! 13 | ``` 14 | 15 | **Python之禅** 16 | 17 | ``` 18 | >>> import this 19 | 20 | The Zen of Python, by Tim Peters 21 | 22 | Beautiful is better than ugly. 23 | Explicit is better than implicit. 24 | Simple is better than complex. 25 | Complex is better than complicated. 26 | Flat is better than nested. 27 | Sparse is better than dense. 28 | Readability counts. 29 | Special cases aren't special enough to break the rules. 30 | Although practicality beats purity. 31 | Errors should never pass silently. 32 | Unless explicitly silenced. 33 | In the face of ambiguity, refuse the temptation to guess. 34 | There should be one-- and preferably only one --obvious way to do it. 35 | Although that way may not be obvious at first unless you're Dutch. 36 | Now is better than never. 37 | Although never is often better than *right* now. 38 | If the implementation is hard to explain, it's a bad idea. 39 | If the implementation is easy to explain, it may be a good idea. 40 | Namespaces are one honking great idea -- let's do more of those! 41 | ``` 42 | 43 | **反地心引力漫画** 44 | 45 | 在 cmd 窗口中导入`antigravity` 46 | 47 | ``` 48 | >>> import antigravity 49 | ``` 50 | 51 | 就会自动打开一个网页。 52 | ![](http://image.iswbm.com/20190511165735.png) 53 | 54 | 55 | -------------------------------------------------------------------------------- /source/c01/c01_17.rst: -------------------------------------------------------------------------------- 1 | 1.17 有趣但没啥用的 import 用法 2 | =============================== 3 | 4 | .. image:: http://image.iswbm.com/20200804124133.png 5 | 6 | import 是 Python 导包的方式。 7 | 8 | 你知道 Python 中内置了一些很有(wu)趣(liao)的包吗? 9 | 10 | **Hello World** 11 | 12 | :: 13 | 14 | >>> import __hello__ 15 | Hello World! 16 | 17 | **Python之禅** 18 | 19 | :: 20 | 21 | >>> import this 22 | 23 | The Zen of Python, by Tim Peters 24 | 25 | Beautiful is better than ugly. 26 | Explicit is better than implicit. 27 | Simple is better than complex. 28 | Complex is better than complicated. 29 | Flat is better than nested. 30 | Sparse is better than dense. 31 | Readability counts. 32 | Special cases aren't special enough to break the rules. 33 | Although practicality beats purity. 34 | Errors should never pass silently. 35 | Unless explicitly silenced. 36 | In the face of ambiguity, refuse the temptation to guess. 37 | There should be one-- and preferably only one --obvious way to do it. 38 | Although that way may not be obvious at first unless you're Dutch. 39 | Now is better than never. 40 | Although never is often better than *right* now. 41 | If the implementation is hard to explain, it's a bad idea. 42 | If the implementation is easy to explain, it may be a good idea. 43 | Namespaces are one honking great idea -- let's do more of those! 44 | 45 | **反地心引力漫画** 46 | 47 | 在 cmd 窗口中导入\ ``antigravity`` 48 | 49 | :: 50 | 51 | >>> import antigravity 52 | 53 | 就会自动打开一个网页。 |image1| 54 | 55 | .. |image1| image:: http://image.iswbm.com/20190511165735.png 56 | -------------------------------------------------------------------------------- /source/c01/c01_18.md: -------------------------------------------------------------------------------- 1 | # 1.18 正负得负,负负得正 2 | ![](http://image.iswbm.com/20200804124133.png) 3 | 4 | 从初中开始,我们就开始接触了`负数` ,并且都知道了`负负得正` 的思想。 5 | 6 | Python 作为一门高级语言,它的编写符合人类的思维逻辑,包括 `负负得正` 。 7 | 8 | ```python 9 | >>> 5-3 10 | 2 11 | >>> 5--3 12 | 8 13 | >>> 5+-3 14 | 2 15 | >>> 5++3 16 | 8 17 | >>> 5---3 18 | 2 19 | ``` 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /source/c01/c01_18.rst: -------------------------------------------------------------------------------- 1 | 1.18 正负得负,负负得正 2 | ======================= 3 | 4 | .. image:: http://image.iswbm.com/20200804124133.png 5 | 6 | 从初中开始,我们就开始接触了\ ``负数`` ,并且都知道了\ ``负负得正`` 7 | 的思想。 8 | 9 | Python 作为一门高级语言,它的编写符合人类的思维逻辑,包括 ``负负得正`` 10 | 。 11 | 12 | .. code:: python 13 | 14 | >>> 5-3 15 | 2 16 | >>> 5--3 17 | 8 18 | >>> 5+-3 19 | 2 20 | >>> 5++3 21 | 8 22 | >>> 5---3 23 | 2 24 | -------------------------------------------------------------------------------- /source/c01/c01_19.md: -------------------------------------------------------------------------------- 1 | # 1.19 return不一定都是函数的终点 2 | ![](http://image.iswbm.com/20200804124133.png) 3 | 4 | 众所周知,try…finally… 的用法是:不管try里面是正常执行还是有报异常,最终都能保证finally能够执行。 5 | 6 | 同时我们又知道,一个函数里只要遇到 return 函数就会立马结束。 7 | 8 | 那问题就来了,以上这两种规则,如果同时存在,Python 解释器会如何选择?哪个优先级更高? 9 | 10 | 写个示例验证一下,就明白啦 11 | 12 | ```python 13 | >>> def func(): 14 | ... try: 15 | ... return 'try' 16 | ... finally: 17 | ... return 'finally' 18 | ... 19 | >>> func() 20 | 'finally' 21 | ``` 22 | 23 | 从输出中,我们可以发现:在try…finally…语句中,try中的 return 会被直接忽视(这里的 return 不是函数的终点),因为要保证 finally 能够执行。 24 | 25 | **如果 try 里的 return 真的是直接被忽视吗?** 26 | 27 | 我们都知道如果一个函数没有 return,会隐式的返回 None,假设 try 里的 return 真的是直接被忽视,那当finally 下没有显式的 return 的时候,是不是会返回None呢? 28 | 29 | 还是写个 示例来验证一下: 30 | 31 | ```python 32 | >>> def func(): 33 | ... try: 34 | ... return 'try' 35 | ... finally: 36 | ... print('finally') 37 | ... 38 | >>> 39 | >>> func() 40 | finally 41 | 'try' 42 | >>> 43 | ``` 44 | 45 | 从结果来看,当 finally 下没有 reutrn ,其实 try 里的 return 仍然还是有效的。 46 | 47 | 那结论就出来了,如果 finally 里有显式的 return,那么这个 return 会直接覆盖 try 里的 return,而如果 finally 里没有 显式的 return,那么 try 里的 return 仍然有效。 48 | 49 | 50 | -------------------------------------------------------------------------------- /source/c01/c01_19.rst: -------------------------------------------------------------------------------- 1 | 1.19 return不一定都是函数的终点 2 | =============================== 3 | 4 | .. image:: http://image.iswbm.com/20200804124133.png 5 | 6 | 众所周知,try…finally… 7 | 的用法是:不管try里面是正常执行还是有报异常,最终都能保证finally能够执行。 8 | 9 | 同时我们又知道,一个函数里只要遇到 return 函数就会立马结束。 10 | 11 | 那问题就来了,以上这两种规则,如果同时存在,Python 12 | 解释器会如何选择?哪个优先级更高? 13 | 14 | 写个示例验证一下,就明白啦 15 | 16 | .. code:: python 17 | 18 | >>> def func(): 19 | ... try: 20 | ... return 'try' 21 | ... finally: 22 | ... return 'finally' 23 | ... 24 | >>> func() 25 | 'finally' 26 | 27 | 从输出中,我们可以发现:在try…finally…语句中,try中的 return 28 | 会被直接忽视(这里的 return 不是函数的终点),因为要保证 finally 29 | 能够执行。 30 | 31 | **如果 try 里的 return 真的是直接被忽视吗?** 32 | 33 | 我们都知道如果一个函数没有 return,会隐式的返回 None,假设 try 里的 34 | return 真的是直接被忽视,那当finally 下没有显式的 return 35 | 的时候,是不是会返回None呢? 36 | 37 | 还是写个 示例来验证一下: 38 | 39 | .. code:: python 40 | 41 | >>> def func(): 42 | ... try: 43 | ... return 'try' 44 | ... finally: 45 | ... print('finally') 46 | ... 47 | >>> 48 | >>> func() 49 | finally 50 | 'try' 51 | >>> 52 | 53 | 从结果来看,当 finally 下没有 reutrn ,其实 try 里的 return 54 | 仍然还是有效的。 55 | 56 | 那结论就出来了,如果 finally 里有显式的 return,那么这个 return 57 | 会直接覆盖 try 里的 return,而如果 finally 里没有 显式的 return,那么 58 | try 里的 return 仍然有效。 59 | -------------------------------------------------------------------------------- /source/c01/c01_20.md: -------------------------------------------------------------------------------- 1 | # 1.20 字符串里的缝隙是什么? 2 | ![](http://image.iswbm.com/20200804124133.png) 3 | 4 | 在Python中求一个字符串里,某子字符(串)出现的次数。 5 | 6 | 大家都懂得使用 count() 函数,比如下面几个常规例子: 7 | 8 | ```python 9 | >>> "aabb".count("a") 10 | 2 11 | >>> "aabb".count("b") 12 | 2 13 | >>> "aabb".count("ab") 14 | 1 15 | ``` 16 | 17 | 但是如果我想计算空字符串的个数呢? 18 | 19 | ```python 20 | >>> "aabb".count("") 21 | 5 22 | ``` 23 | 24 | **奇怪了吧?** 25 | 26 | 不是应该返回 0 吗?怎么会返回 5? 27 | 28 | 实际上,在 Python 看来,两个字符之间都是一个空字符,通俗的说就是缝隙。 29 | 30 | 因此 对于 `aabb` 这个字符串在 Python 来看应该是这样的 31 | 32 | ![](http://image.iswbm.com/20200509172331.png) 33 | 34 | 理解了这个“**缝隙**” 的概念后,以下这些就好理解了。 35 | 36 | ```python 37 | >>> (" " * 10).count("") 38 | 11 39 | >>> 40 | >>> "" in "" 41 | True 42 | >>> 43 | >>> "" in "M" 44 | True 45 | ``` 46 | 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /source/c01/c01_20.rst: -------------------------------------------------------------------------------- 1 | 1.20 字符串里的缝隙是什么? 2 | =========================== 3 | 4 | .. image:: http://image.iswbm.com/20200804124133.png 5 | 6 | 在Python中求一个字符串里,某子字符(串)出现的次数。 7 | 8 | 大家都懂得使用 count() 函数,比如下面几个常规例子: 9 | 10 | .. code:: python 11 | 12 | >>> "aabb".count("a") 13 | 2 14 | >>> "aabb".count("b") 15 | 2 16 | >>> "aabb".count("ab") 17 | 1 18 | 19 | 但是如果我想计算空字符串的个数呢? 20 | 21 | .. code:: python 22 | 23 | >>> "aabb".count("") 24 | 5 25 | 26 | **奇怪了吧?** 27 | 28 | 不是应该返回 0 吗?怎么会返回 5? 29 | 30 | 实际上,在 Python 看来,两个字符之间都是一个空字符,通俗的说就是缝隙。 31 | 32 | 因此 对于 ``aabb`` 这个字符串在 Python 来看应该是这样的 33 | 34 | .. image:: http://image.iswbm.com/20200509172331.png 35 | 36 | 理解了这个“\ **缝隙**\ ” 的概念后,以下这些就好理解了。 37 | 38 | .. code:: python 39 | 40 | >>> (" " * 10).count("") 41 | 11 42 | >>> 43 | >>> "" in "" 44 | True 45 | >>> 46 | >>> "" in "M" 47 | True 48 | -------------------------------------------------------------------------------- /source/c01/c01_21.md: -------------------------------------------------------------------------------- 1 | # 1.21 Python2下 也能使用 print(“”) 2 | ![](http://image.iswbm.com/20200804124133.png) 3 | 4 | 可能会有不少人,觉得只有 Python 3 才可以使用 print(),而 Python 2 只能使用`print ""`。 5 | 6 | 但是其实并不是这样的。 7 | 8 | 在Python 2.6之前,只支持 9 | 10 | ```python 11 | print "hello" 12 | ``` 13 | 14 | 在Python 2.6和2.7中,可以支持如下三种 15 | 16 | ```python 17 | print "hello" 18 | print("hello") 19 | print ("hello") 20 | ``` 21 | 22 | 在Python3.x中,可以支持如下两种 23 | 24 | ```python 25 | print("hello") 26 | print ("hello") 27 | ``` 28 | 29 | 虽然 在 Python 2.6+ 可以和 Python3.x+ 一样,像函数一样去调用 print ,但是这仅用于两个 python 版本之间的代码兼容,并不是说在 python2.6+下使用 print() 后,就成了函数。 30 | 31 | 32 | -------------------------------------------------------------------------------- /source/c01/c01_21.rst: -------------------------------------------------------------------------------- 1 | 1.21 Python2下 也能使用 print(“”) 2 | ================================= 3 | 4 | .. image:: http://image.iswbm.com/20200804124133.png 5 | 6 | 可能会有不少人,觉得只有 Python 3 才可以使用 print(),而 Python 2 7 | 只能使用\ ``print ""``\ 。 8 | 9 | 但是其实并不是这样的。 10 | 11 | 在Python 2.6之前,只支持 12 | 13 | .. code:: python 14 | 15 | print "hello" 16 | 17 | 在Python 2.6和2.7中,可以支持如下三种 18 | 19 | .. code:: python 20 | 21 | print "hello" 22 | print("hello") 23 | print ("hello") 24 | 25 | 在Python3.x中,可以支持如下两种 26 | 27 | .. code:: python 28 | 29 | print("hello") 30 | print ("hello") 31 | 32 | 虽然 在 Python 2.6+ 可以和 Python3.x+ 一样,像函数一样去调用 print 33 | ,但是这仅用于两个 python 版本之间的代码兼容,并不是说在 34 | python2.6+下使用 print() 后,就成了函数。 35 | -------------------------------------------------------------------------------- /source/c01/c01_22.md: -------------------------------------------------------------------------------- 1 | # 1.22 字母也玩起了障眼法 2 | ![](http://image.iswbm.com/20200804124133.png) 3 | 4 | 以下我分别在 Python2.7 和 Python 3.7 的 console 模式下,运行了如下代码。 5 | 6 | **在Python 2.x 中** 7 | 8 | ``` 9 | >>> valuе = 32 10 | File "", line 1 11 | valuе = 32 12 | ^ 13 | SyntaxError: invalid syntax 14 | ``` 15 | 16 | **在Python 3.x 中** 17 | 18 | ``` 19 | >>> valuе = 32 20 | >>> value 21 | 11 22 | ``` 23 | 24 | 什么?没有截图你不信? 25 | 26 | ![](http://image.iswbm.com/20200509122954.png) 27 | 28 | 29 | 30 | 如果你在自己的电脑上尝试一下,结果可能是这样的 31 | 32 | ![](http://image.iswbm.com/20200509123107.png) 33 | 34 | 35 | 36 | **怎么又好了呢?** 37 | 38 | 如果你想复现的话,请复制我这边给出的代码:`valuе = 32` 39 | 40 | 41 | 42 | **这是为什么呢?** 43 | 44 | 原因在于,我上面使用的 value 变量名里的 `е` 又不是我们熟悉的 `e`,它是 Cyrillic(西里尔)字母。 45 | 46 | ``` 47 | >>> ord('е') # cyrillic 'e' (Ye) 48 | 1077 49 | >>> ord('e') # latin 'e', as used in English and typed using standard keyboard 50 | 101 51 | >>> 'е' == 'e' 52 | False 53 | ``` 54 | 55 | 细思恐极,在这里可千万不要得罪同事们,万一离职的时候,对方把你项目里的 `e` 全局替换成 `e`,到时候你就哭去吧,肉眼根本看不出来嘛。 56 | 57 | 58 | -------------------------------------------------------------------------------- /source/c01/c01_22.rst: -------------------------------------------------------------------------------- 1 | 1.22 字母也玩起了障眼法 2 | ======================= 3 | 4 | .. image:: http://image.iswbm.com/20200804124133.png 5 | 6 | 以下我分别在 Python2.7 和 Python 3.7 的 console 模式下,运行了如下代码。 7 | 8 | **在Python 2.x 中** 9 | 10 | :: 11 | 12 | >>> valuе = 32 13 | File "", line 1 14 | valuе = 32 15 | ^ 16 | SyntaxError: invalid syntax 17 | 18 | **在Python 3.x 中** 19 | 20 | :: 21 | 22 | >>> valuе = 32 23 | >>> value 24 | 11 25 | 26 | 什么?没有截图你不信? 27 | 28 | .. image:: http://image.iswbm.com/20200509122954.png 29 | 30 | 如果你在自己的电脑上尝试一下,结果可能是这样的 31 | 32 | .. image:: http://image.iswbm.com/20200509123107.png 33 | 34 | **怎么又好了呢?** 35 | 36 | 如果你想复现的话,请复制我这边给出的代码:\ ``valuе = 32`` 37 | 38 | **这是为什么呢?** 39 | 40 | 原因在于,我上面使用的 value 变量名里的 ``е`` 又不是我们熟悉的 41 | ``e``\ ,它是 Cyrillic(西里尔)字母。 42 | 43 | :: 44 | 45 | >>> ord('е') # cyrillic 'e' (Ye) 46 | 1077 47 | >>> ord('e') # latin 'e', as used in English and typed using standard keyboard 48 | 101 49 | >>> 'е' == 'e' 50 | False 51 | 52 | 细思恐极,在这里可千万不要得罪同事们,万一离职的时候,对方把你项目里的 53 | ``e`` 全局替换成 ``e``\ ,到时候你就哭去吧,肉眼根本看不出来嘛。 54 | -------------------------------------------------------------------------------- /source/c01/c01_23.md: -------------------------------------------------------------------------------- 1 | # 1.23 数值与字符串的比较 2 | ![](http://image.iswbm.com/20200804124133.png) 3 | 4 | 在 Python2 中,数字可以与字符串直接比较。结果是数值永远比字符串小。 5 | 6 | ```Python 7 | >>> 100000000 < "" 8 | True 9 | >>> 100000000 < "hello" 10 | True 11 | ``` 12 | 13 | 但在 Python3 中,却不行。 14 | 15 | ```python 16 | >>> 100000000 < "" 17 | TypeError: '<' not supported between instances of 'int' and 'str' 18 | ``` 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /source/c01/c01_23.rst: -------------------------------------------------------------------------------- 1 | 1.23 数值与字符串的比较 2 | ======================= 3 | 4 | .. image:: http://image.iswbm.com/20200804124133.png 5 | 6 | 在 Python2 中,数字可以与字符串直接比较。结果是数值永远比字符串小。 7 | 8 | .. code:: python 9 | 10 | >>> 100000000 < "" 11 | True 12 | >>> 100000000 < "hello" 13 | True 14 | 15 | 但在 Python3 中,却不行。 16 | 17 | .. code:: python 18 | 19 | >>> 100000000 < "" 20 | TypeError: '<' not supported between instances of 'int' and 'str' 21 | -------------------------------------------------------------------------------- /source/c01/c01_24.md: -------------------------------------------------------------------------------- 1 | # 1.24 时有时无的切片异常 2 | 3 | ![](http://image.iswbm.com/20200804124133.png) 4 | 5 | 这是个简单例子,alist 只有5 个元素,当你取第 6 个元素时,会抛出索引异常。这与我们的认知一致。 6 | 7 | ```python 8 | >>> alist = [0, 1, 2, 3, 4] 9 | >>> alist[5] 10 | Traceback (most recent call last): 11 | File "", line 1, in 12 | IndexError: list index out of range 13 | ``` 14 | 15 | 但是当你使用 alist[5:] 取一个区间时,即使 alist 并没有 第 6个元素,也不抛出异常,而是会返回一个新的列表。 16 | 17 | ```python 18 | >>> alist = [0, 1, 2, 3, 4] 19 | >>> alist[5:] 20 | [] 21 | >>> alist[100:] 22 | [] 23 | ``` 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /source/c01/c01_24.rst: -------------------------------------------------------------------------------- 1 | 1.24 时有时无的切片异常 2 | ======================= 3 | 4 | .. image:: http://image.iswbm.com/20200804124133.png 5 | 6 | 这是个简单例子,alist 只有5 个元素,当你取第 6 7 | 个元素时,会抛出索引异常。这与我们的认知一致。 8 | 9 | .. code:: python 10 | 11 | >>> alist = [0, 1, 2, 3, 4] 12 | >>> alist[5] 13 | Traceback (most recent call last): 14 | File "", line 1, in 15 | IndexError: list index out of range 16 | 17 | 但是当你使用 alist[5:] 取一个区间时,即使 alist 并没有 第 18 | 6个元素,也不抛出异常,而是会返回一个新的列表。 19 | 20 | .. code:: python 21 | 22 | >>> alist = [0, 1, 2, 3, 4] 23 | >>> alist[5:] 24 | [] 25 | >>> alist[100:] 26 | [] 27 | -------------------------------------------------------------------------------- /source/c01/c01_25.md: -------------------------------------------------------------------------------- 1 | # 1.25 迷一样的字符串 2 | ![](http://image.iswbm.com/20200804124133.png) 3 | 4 | 示例一 5 | 6 | ```python 7 | # Python2.7 8 | >>> a = "Hello_Python" 9 | >>> id(a) 10 | 32045616 11 | >>> id("Hello" + "_" + "Python") 12 | 32045616 13 | 14 | # Python3.7 15 | >>> a = "Hello_Python" 16 | >>> id(a) 17 | 38764272 18 | >>> id("Hello" + "_" + "Python") 19 | 32045616 20 | ``` 21 | 22 | 示例二 23 | 24 | ```python 25 | >>> a = "MING" 26 | >>> b = "MING" 27 | >>> a is b 28 | True 29 | 30 | # Python2.7 31 | >>> a, b = "MING!", "MING!" 32 | >>> a is b 33 | True 34 | 35 | # Python3.7 36 | >>> a, b = "MING!", "MING!" 37 | >>> a is b 38 | False 39 | ``` 40 | 41 | 示例三 42 | 43 | ```python 44 | # Python2.7 45 | >>> 'a' * 20 is 'aaaaaaaaaaaaaaaaaaaa' 46 | True 47 | >>> 'a' * 21 is 'aaaaaaaaaaaaaaaaaaaaa' 48 | False 49 | 50 | # Python3.7 51 | >>> 'a' * 20 is 'aaaaaaaaaaaaaaaaaaaa' 52 | True 53 | >>> 'a' * 21 is 'aaaaaaaaaaaaaaaaaaaaa' 54 | True 55 | ``` 56 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /source/c01/c01_25.rst: -------------------------------------------------------------------------------- 1 | 1.25 迷一样的字符串 2 | =================== 3 | 4 | .. image:: http://image.iswbm.com/20200804124133.png 5 | 6 | 示例一 7 | 8 | .. code:: python 9 | 10 | # Python2.7 11 | >>> a = "Hello_Python" 12 | >>> id(a) 13 | 32045616 14 | >>> id("Hello" + "_" + "Python") 15 | 32045616 16 | 17 | # Python3.7 18 | >>> a = "Hello_Python" 19 | >>> id(a) 20 | 38764272 21 | >>> id("Hello" + "_" + "Python") 22 | 32045616 23 | 24 | 示例二 25 | 26 | .. code:: python 27 | 28 | >>> a = "MING" 29 | >>> b = "MING" 30 | >>> a is b 31 | True 32 | 33 | # Python2.7 34 | >>> a, b = "MING!", "MING!" 35 | >>> a is b 36 | True 37 | 38 | # Python3.7 39 | >>> a, b = "MING!", "MING!" 40 | >>> a is b 41 | False 42 | 43 | 示例三 44 | 45 | .. code:: python 46 | 47 | # Python2.7 48 | >>> 'a' * 20 is 'aaaaaaaaaaaaaaaaaaaa' 49 | True 50 | >>> 'a' * 21 is 'aaaaaaaaaaaaaaaaaaaaa' 51 | False 52 | 53 | # Python3.7 54 | >>> 'a' * 20 is 'aaaaaaaaaaaaaaaaaaaa' 55 | True 56 | >>> 'a' * 21 is 'aaaaaaaaaaaaaaaaaaaaa' 57 | True 58 | -------------------------------------------------------------------------------- /source/c01/c01_26.md: -------------------------------------------------------------------------------- 1 | # 1.26 x 与 +x 等价吗? 2 | ![](http://image.iswbm.com/20200804124133.png) 3 | 4 | 在大多数情况下,这个等式是成立的。 5 | 6 | ```python 7 | >>> n1 = 10086 8 | >>> n2 = +n1 9 | >>> 10 | >>> n1 == n2 11 | True 12 | ``` 13 | 14 | 什么情况下,这个等式会不成立呢? 15 | 16 | 由于Counter的机制,`+` 用于两个 Counter 实例相加,而相加的结果如果元素的个数 `<=` 0,就会被丢弃。 17 | 18 | ```python 19 | >>> from collections import Counter 20 | >>> ct = Counter('abcdbcaa') 21 | >>> ct 22 | Counter({'a': 3, 'b': 2, 'c': 2, 'd': 1}) 23 | >>> ct['c'] = 0 24 | >>> ct['d'] = -2 25 | >>> 26 | >>> ct 27 | Counter({'a': 3, 'b': 2, 'c': 0, 'd': -2}) 28 | >>> 29 | >>> +ct 30 | Counter({'a': 3, 'b': 2}) 31 | ``` 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /source/c01/c01_26.rst: -------------------------------------------------------------------------------- 1 | 1.26 x 与 +x 等价吗? 2 | ===================== 3 | 4 | .. image:: http://image.iswbm.com/20200804124133.png 5 | 6 | 在大多数情况下,这个等式是成立的。 7 | 8 | .. code:: python 9 | 10 | >>> n1 = 10086 11 | >>> n2 = +n1 12 | >>> 13 | >>> n1 == n2 14 | True 15 | 16 | 什么情况下,这个等式会不成立呢? 17 | 18 | 由于Counter的机制,\ ``+`` 用于两个 Counter 19 | 实例相加,而相加的结果如果元素的个数 ``<=`` 0,就会被丢弃。 20 | 21 | .. code:: python 22 | 23 | >>> from collections import Counter 24 | >>> ct = Counter('abcdbcaa') 25 | >>> ct 26 | Counter({'a': 3, 'b': 2, 'c': 2, 'd': 1}) 27 | >>> ct['c'] = 0 28 | >>> ct['d'] = -2 29 | >>> 30 | >>> ct 31 | Counter({'a': 3, 'b': 2, 'c': 0, 'd': -2}) 32 | >>> 33 | >>> +ct 34 | Counter({'a': 3, 'b': 2}) 35 | -------------------------------------------------------------------------------- /source/c01/c01_27.md: -------------------------------------------------------------------------------- 1 | # 1.27 += 不等同于=+ 2 | ![](http://image.iswbm.com/20200804124133.png) 3 | 4 | 对列表 进行`+=` 操作相当于 extend,而使用 `=+` 操作是新增了一个列表。 5 | 6 | 因此会有如下两者的差异。 7 | 8 | ```python 9 | # =+ 10 | >>> a = [1, 2, 3, 4] 11 | >>> b = a 12 | >>> a = a + [5, 6, 7, 8] 13 | >>> a 14 | [1, 2, 3, 4, 5, 6, 7, 8] 15 | >>> b 16 | [1, 2, 3, 4] 17 | 18 | 19 | # += 20 | >>> a = [1, 2, 3, 4] 21 | >>> b = a 22 | >>> a += [5, 6, 7, 8] 23 | >>> a 24 | [1, 2, 3, 4, 5, 6, 7, 8] 25 | >>> b 26 | [1, 2, 3, 4, 5, 6, 7, 8] 27 | ``` 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /source/c01/c01_27.rst: -------------------------------------------------------------------------------- 1 | 1.27 += 不等同于=+ 2 | ================== 3 | 4 | .. image:: http://image.iswbm.com/20200804124133.png 5 | 6 | 对列表 进行\ ``+=`` 操作相当于 extend,而使用 ``=+`` 7 | 操作是新增了一个列表。 8 | 9 | 因此会有如下两者的差异。 10 | 11 | .. code:: python 12 | 13 | # =+ 14 | >>> a = [1, 2, 3, 4] 15 | >>> b = a 16 | >>> a = a + [5, 6, 7, 8] 17 | >>> a 18 | [1, 2, 3, 4, 5, 6, 7, 8] 19 | >>> b 20 | [1, 2, 3, 4] 21 | 22 | 23 | # += 24 | >>> a = [1, 2, 3, 4] 25 | >>> b = a 26 | >>> a += [5, 6, 7, 8] 27 | >>> a 28 | [1, 2, 3, 4, 5, 6, 7, 8] 29 | >>> b 30 | [1, 2, 3, 4, 5, 6, 7, 8] 31 | -------------------------------------------------------------------------------- /source/c01/c01_28.md: -------------------------------------------------------------------------------- 1 | # 1.28 循环中的局部变量泄露 2 | ![](http://image.iswbm.com/20200804124133.png) 3 | 4 | 在Python 2中 x 的值在一个循环执行之后被改变了。 5 | 6 | ```python 7 | # Python2 8 | >>> x = 1 9 | >>> [x for x in range(5)] 10 | [0, 1, 2, 3, 4] 11 | >>> x 12 | 4 13 | ``` 14 | 15 | 不过在Python3 中这个问题已经得到解决了。 16 | 17 | ```python 18 | # Python3 19 | >>> x = 1 20 | >>> [x for x in range(5)] 21 | [0, 1, 2, 3, 4] 22 | >>> x 23 | 1 24 | ``` 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /source/c01/c01_28.rst: -------------------------------------------------------------------------------- 1 | 1.28 循环中的局部变量泄露 2 | ========================= 3 | 4 | .. image:: http://image.iswbm.com/20200804124133.png 5 | 6 | 在Python 2中 x 的值在一个循环执行之后被改变了。 7 | 8 | .. code:: python 9 | 10 | # Python2 11 | >>> x = 1 12 | >>> [x for x in range(5)] 13 | [0, 1, 2, 3, 4] 14 | >>> x 15 | 4 16 | 17 | 不过在Python3 中这个问题已经得到解决了。 18 | 19 | .. code:: python 20 | 21 | # Python3 22 | >>> x = 1 23 | >>> [x for x in range(5)] 24 | [0, 1, 2, 3, 4] 25 | >>> x 26 | 1 27 | -------------------------------------------------------------------------------- /source/c01/c01_29.md: -------------------------------------------------------------------------------- 1 | # 1.29 局部/全局变量傻傻分不清 2 | ![](http://image.iswbm.com/20200804124133.png) 3 | 4 | 在开始讲之前,你可以试着运行一下下面这小段代码。 5 | 6 | ```python 7 | # demo.py 8 | a = 1 9 | 10 | def add(): 11 | a += 1 12 | 13 | add() 14 | ``` 15 | 16 | 看似没有毛病,但实则已经犯了一个很基础的问题,运行结果如下: 17 | 18 | ```python 19 | $ python demo.py 20 | Traceback (most recent call last): 21 | File "demo.py", line 6, in 22 | add() 23 | File "demo.py", line 4, in add 24 | a += 1 25 | UnboundLocalError: local variable 'a' referenced before assignment 26 | ``` 27 | 28 | 回顾一下,什么是局部变量?在非全局下定义声明的变量都是局部变量。 29 | 30 | 当程序运行到 `a += 1` 时,Python 解释器就认为在函数内部要给 `a` 这个变量赋值,当然就把 `a` 当做局部变量了,但是做为局部变量的 a 还没有被还没被定义。 31 | 32 | 因此报错是正常的。 33 | 34 | 理解了上面的例子,给你留个思考题。为什么下面的代码不会报错呢? 35 | 36 | ```python 37 | $ cat demo.py 38 | a = 1 39 | 40 | def output(): 41 | print(a) 42 | 43 | output() 44 | 45 | $ python demo.py 46 | 1 47 | ``` 48 | 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /source/c01/c01_29.rst: -------------------------------------------------------------------------------- 1 | 1.29 局部/全局变量傻傻分不清 2 | ============================ 3 | 4 | .. image:: http://image.iswbm.com/20200804124133.png 5 | 6 | 在开始讲之前,你可以试着运行一下下面这小段代码。 7 | 8 | .. code:: python 9 | 10 | # demo.py 11 | a = 1 12 | 13 | def add(): 14 | a += 1 15 | 16 | add() 17 | 18 | 看似没有毛病,但实则已经犯了一个很基础的问题,运行结果如下: 19 | 20 | .. code:: python 21 | 22 | $ python demo.py 23 | Traceback (most recent call last): 24 | File "demo.py", line 6, in 25 | add() 26 | File "demo.py", line 4, in add 27 | a += 1 28 | UnboundLocalError: local variable 'a' referenced before assignment 29 | 30 | 回顾一下,什么是局部变量?在非全局下定义声明的变量都是局部变量。 31 | 32 | 当程序运行到 ``a += 1`` 时,Python 解释器就认为在函数内部要给 ``a`` 33 | 这个变量赋值,当然就把 ``a`` 当做局部变量了,但是做为局部变量的 a 34 | 还没有被还没被定义。 35 | 36 | 因此报错是正常的。 37 | 38 | 理解了上面的例子,给你留个思考题。为什么下面的代码不会报错呢? 39 | 40 | .. code:: python 41 | 42 | $ cat demo.py 43 | a = 1 44 | 45 | def output(): 46 | print(a) 47 | 48 | output() 49 | 50 | $ python demo.py 51 | 1 52 | -------------------------------------------------------------------------------- /source/c01/c01_30.md: -------------------------------------------------------------------------------- 1 | # 1.30 break /continue 和 上下文管理器哪个优先级高? 2 | ![](http://image.iswbm.com/20200804124133.png) 3 | 4 | 众所周知,在循环体中(无论是 for 还是 while),continue 会用来跳入下一个循环,而 break 则用来跳出某个循环体。 5 | 6 | 同时我们又知道:在上下文管理器中,被包裹的程序主体代码结束会运行上下文管理器中的一段代码(通常是资源的释放)。 7 | 8 | 但如果把上下文管理器放在一个循环体中,而在这个上下文管理器中执行了 break ,是否会直接跳出循环呢? 9 | 10 | 换句话说,上下文管理器与 break/continue 这两个规则哪一个优先级会更高一些? 11 | 12 | 这个问题其实不难,只要做一下试验都能轻易地得出答案,难就难在很多对这个答案都是半猜半疑,无法肯定地回答。 13 | 14 | 试验代码如下: 15 | 16 | ```python 17 | import time 18 | import contextlib 19 | 20 | @contextlib.contextmanager 21 | def runtime(value): 22 | time.sleep(1) 23 | print("start: a = " + str(value)) 24 | yield 25 | print("end: a = " + str(value)) 26 | 27 | 28 | a = 0 29 | while True: 30 | a+=1 31 | with runtime(a): 32 | if a % 2 == 0: 33 | break 34 | ``` 35 | 36 | 从输出的结果来看,当 a = 2 时执行了 break ,此时的并不会直接跳出循环,依然要运行上下文管理器里清理释放资源的代码(示例中,我使用 print 来替代)。 37 | 38 | ``` 39 | start: a = 1 40 | end: a = 1 41 | start: a = 2 42 | end: a = 2 43 | ``` 44 | 45 | 46 | 47 | 另外还有几个与此类似的问题,我这里也直接给出答案,不再细说了 48 | 49 | 1. continue 与 break 一样,如果先遇到上下文管理器会先进行资源的释放 50 | 2. 上面只举例了 while 循环体,而 for 循环也是同样的。 51 | 52 | 53 | -------------------------------------------------------------------------------- /source/c01/c01_30.rst: -------------------------------------------------------------------------------- 1 | 1.30 break /continue 和 上下文管理器哪个优先级高? 2 | ================================================== 3 | 4 | .. image:: http://image.iswbm.com/20200804124133.png 5 | 6 | 众所周知,在循环体中(无论是 for 还是 while),continue 7 | 会用来跳入下一个循环,而 break 则用来跳出某个循环体。 8 | 9 | 同时我们又知道:在上下文管理器中,被包裹的程序主体代码结束会运行上下文管理器中的一段代码(通常是资源的释放)。 10 | 11 | 但如果把上下文管理器放在一个循环体中,而在这个上下文管理器中执行了 break 12 | ,是否会直接跳出循环呢? 13 | 14 | 换句话说,上下文管理器与 break/continue 15 | 这两个规则哪一个优先级会更高一些? 16 | 17 | 这个问题其实不难,只要做一下试验都能轻易地得出答案,难就难在很多对这个答案都是半猜半疑,无法肯定地回答。 18 | 19 | 试验代码如下: 20 | 21 | .. code:: python 22 | 23 | import time 24 | import contextlib 25 | 26 | @contextlib.contextmanager 27 | def runtime(value): 28 | time.sleep(1) 29 | print("start: a = " + str(value)) 30 | yield 31 | print("end: a = " + str(value)) 32 | 33 | 34 | a = 0 35 | while True: 36 | a+=1 37 | with runtime(a): 38 | if a % 2 == 0: 39 | break 40 | 41 | 从输出的结果来看,当 a = 2 时执行了 break 42 | ,此时的并不会直接跳出循环,依然要运行上下文管理器里清理释放资源的代码(示例中,我使用 43 | print 来替代)。 44 | 45 | :: 46 | 47 | start: a = 1 48 | end: a = 1 49 | start: a = 2 50 | end: a = 2 51 | 52 | 另外还有几个与此类似的问题,我这里也直接给出答案,不再细说了 53 | 54 | 1. continue 与 break 一样,如果先遇到上下文管理器会先进行资源的释放 55 | 2. 上面只举例了 while 循环体,而 for 循环也是同样的。 56 | -------------------------------------------------------------------------------- /source/c01/c01_31.md: -------------------------------------------------------------------------------- 1 | # 1.31 如何像 awk一样分割字符串? 2 | 3 | ![](http://image.iswbm.com/20200804124133.png) 4 | 5 | 若你使用过 Shell 中的 awk 工具,会发现用它来分割字符串是非常方便的。特别是多个连续空格会被当做一个处理。 6 | 7 | ```shell 8 | [root@localhost ~]# cat demo.txt 9 | hello world 10 | [root@localhost ~]# 11 | [root@localhost ~]# awk '{print$1,$2}' demo.txt 12 | hello world 13 | ``` 14 | 15 | 可是转换到 Python 上面来呢?结果可能是这样的。 16 | 17 | ```python 18 | >>> msg='hello world' 19 | >>> msg.split(' ') 20 | ['hello', '', '', '', 'world'] 21 | ``` 22 | 23 | 与我预想的结果不符,多个空格会被分割多次。 24 | 25 | 那有什么办法可以达到 awk 一样的效果呢? 26 | 27 | 有两种方法。 28 | 29 | ## 第一种方法 30 | 31 | split函数不加参数,就能达到 awk 的效果 32 | 33 | ```python 34 | >>> msg='hello world' 35 | >>> msg.split() 36 | ['hello', 'world'] 37 | ``` 38 | 39 | 其实不仅是空格,严格来说,只要是空字符(比如制表符、换行符),就可以使用 40 | 41 | ```python 42 | >>> msg='hello \t \r\n world' 43 | >>> msg.split() 44 | ['hello', 'world'] 45 | ``` 46 | 47 | ## 第二种方法 48 | 49 | 使用 filter 来辅助,这种适用于所有的分隔符,下面以 `-` 为分隔符来举例。 50 | 51 | ```python 52 | >>> msg='hello----world' 53 | >>> msg.split('-') 54 | ['hello', '', '', '', 'world'] 55 | >>> 56 | >>> filter(None, msg.split('-')) 57 | ['hello', 'world'] 58 | ``` 59 | 60 | 是不是很神奇,filter 印象中第一个参数接收的是 函数,这里直接传 None 居然有奇效。 61 | 62 | 查看了注释,原来是这个函数会适配 None 的情况,当第一个参数是None的时候,返回第二个参数(可迭代对象)中非空的值,非常方便。 63 | 64 | ![](http://image.iswbm.com/20200821173708.png) 65 | 66 | 换用函数的写法,可以这样 67 | 68 | ```python 69 | >>> msg='hello----world' 70 | >>> msg.split('-') 71 | ['hello', '', '', '', 'world'] 72 | >>> 73 | >>> filter(lambda item: True if item else False, msg.split('-')) 74 | ['hello', 'world'] 75 | ``` 76 | 77 | 78 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /source/c01/c01_31.rst: -------------------------------------------------------------------------------- 1 | 1.31 如何像 awk一样分割字符串? 2 | =============================== 3 | 4 | .. image:: http://image.iswbm.com/20200804124133.png 5 | 6 | 若你使用过 Shell 中的 awk 7 | 工具,会发现用它来分割字符串是非常方便的。特别是多个连续空格会被当做一个处理。 8 | 9 | .. code:: shell 10 | 11 | [root@localhost ~]# cat demo.txt 12 | hello world 13 | [root@localhost ~]# 14 | [root@localhost ~]# awk '{print$1,$2}' demo.txt 15 | hello world 16 | 17 | 可是转换到 Python 上面来呢?结果可能是这样的。 18 | 19 | .. code:: python 20 | 21 | >>> msg='hello world' 22 | >>> msg.split(' ') 23 | ['hello', '', '', '', 'world'] 24 | 25 | 与我预想的结果不符,多个空格会被分割多次。 26 | 27 | 那有什么办法可以达到 awk 一样的效果呢? 28 | 29 | 有两种方法。 30 | 31 | 第一种方法 32 | ---------- 33 | 34 | split函数不加参数,就能达到 awk 的效果 35 | 36 | .. code:: python 37 | 38 | >>> msg='hello world' 39 | >>> msg.split() 40 | ['hello', 'world'] 41 | 42 | 其实不仅是空格,严格来说,只要是空字符(比如制表符、换行符),就可以使用 43 | 44 | .. code:: python 45 | 46 | >>> msg='hello \t \r\n world' 47 | >>> msg.split() 48 | ['hello', 'world'] 49 | 50 | 第二种方法 51 | ---------- 52 | 53 | 使用 filter 来辅助,这种适用于所有的分隔符,下面以 ``-`` 54 | 为分隔符来举例。 55 | 56 | .. code:: python 57 | 58 | >>> msg='hello----world' 59 | >>> msg.split('-') 60 | ['hello', '', '', '', 'world'] 61 | >>> 62 | >>> filter(None, msg.split('-')) 63 | ['hello', 'world'] 64 | 65 | 是不是很神奇,filter 印象中第一个参数接收的是 函数,这里直接传 None 66 | 居然有奇效。 67 | 68 | 查看了注释,原来是这个函数会适配 None 69 | 的情况,当第一个参数是None的时候,返回第二个参数(可迭代对象)中非空的值,非常方便。 70 | 71 | .. image:: http://image.iswbm.com/20200821173708.png 72 | 73 | 换用函数的写法,可以这样 74 | 75 | .. code:: python 76 | 77 | >>> msg='hello----world' 78 | >>> msg.split('-') 79 | ['hello', '', '', '', 'world'] 80 | >>> 81 | >>> filter(lambda item: True if item else False, msg.split('-')) 82 | ['hello', 'world'] 83 | -------------------------------------------------------------------------------- /source/c01/c01_32.md: -------------------------------------------------------------------------------- 1 | # 1.32 如何让大数变得更易于阅读? 2 | 3 | ![](http://image.iswbm.com/20200804124133.png) 4 | 5 | 当一个数非常大时,可能过百万,也可能上亿,太多位的数字 ,会给我们阅读带来很大的障碍。 6 | 7 | 比如下面这个数,你能一下子说出它是多少万呢,还是多少亿呢? 8 | 9 | ``` 10 | 281028344 11 | ``` 12 | 13 | 是不是没法很快的辩识出来? 14 | 15 | 这时候,你可以使用 `_` 来辅助标识,写成这样子就清晰多了 16 | 17 | ``` 18 | 281_028_344 19 | ``` 20 | 21 | 关键这种写法,在代码中并不会报错噢(Python2 不支持) 22 | 23 | ```python 24 | >>> number=281_028_344 25 | >>> number 26 | 281028344 27 | ``` 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /source/c01/c01_32.rst: -------------------------------------------------------------------------------- 1 | 1.32 如何让大数变得更易于阅读? 2 | =============================== 3 | 4 | .. image:: http://image.iswbm.com/20200804124133.png 5 | 6 | 当一个数非常大时,可能过百万,也可能上亿,太多位的数字 7 | ,会给我们阅读带来很大的障碍。 8 | 9 | 比如下面这个数,你能一下子说出它是多少万呢,还是多少亿呢? 10 | 11 | :: 12 | 13 | 281028344 14 | 15 | 是不是没法很快的辩识出来? 16 | 17 | 这时候,你可以使用 ``_`` 来辅助标识,写成这样子就清晰多了 18 | 19 | :: 20 | 21 | 281_028_344 22 | 23 | 关键这种写法,在代码中并不会报错噢(Python2 不支持) 24 | 25 | .. code:: python 26 | 27 | >>> number=281_028_344 28 | >>> number 29 | 281028344 30 | -------------------------------------------------------------------------------- /source/c02/c02_01.md: -------------------------------------------------------------------------------- 1 | # 2.1 懒人必备技能:使用 “_” 2 | ![](http://image.iswbm.com/20200804124133.png) 3 | 4 | 对于 `_` ,大家对于他的印象都是用于 **占位符**,省得为一个不需要用到的变量,绞尽脑汁的想变量名。 5 | 6 | 今天要介绍的是他的第二种用法,就是在交互式模式下的应用。 7 | 8 | 示例如下: 9 | 10 | ```python 11 | >>> 3 + 4 12 | 7 13 | >>> _ 14 | 7 15 | >>> name='iswbm' 16 | >>> name 17 | 'iswbm' 18 | >>> _ 19 | 'iswbm' 20 | ``` 21 | 22 | 它可以返回上一次的运行结果。 23 | 24 | 但是,如果是print函数打印出来的就不行了。 25 | 26 | ```python 27 | >>> 3 + 4 28 | 7 29 | >>> _ 30 | 7 31 | >>> print("iswbm") 32 | iswbm 33 | >>> _ 34 | 7 35 | ``` 36 | 37 | 我自己写了个例子,验证了下,用`__repr__`输出的内容可以被获取到的。 38 | 首先,在我们的目录下,写一个文件 demo.py。内容如下 39 | 40 | ```python 41 | # demo.py 42 | class mytest(): 43 | def __str__(self): 44 | return "hello" 45 | 46 | def __repr__(self): 47 | return "world" 48 | ``` 49 | 50 | 然后在这个目录下进入交互式环境。 51 | 52 | ```python 53 | >>> import demo 54 | >>> mt=demo.mytest() 55 | >>> mt 56 | world 57 | >>> print(mt) 58 | hello 59 | >>> _ 60 | world 61 | ``` 62 | 63 | 知道这两个魔法方法的人,一看就明白了,这里不再解释啦。 64 | 65 | 66 | -------------------------------------------------------------------------------- /source/c02/c02_01.rst: -------------------------------------------------------------------------------- 1 | 2.1 懒人必备技能:使用 “_” 2 | ========================== 3 | 4 | .. image:: http://image.iswbm.com/20200804124133.png 5 | 6 | 对于 ``_`` ,大家对于他的印象都是用于 7 | **占位符**\ ,省得为一个不需要用到的变量,绞尽脑汁的想变量名。 8 | 9 | 今天要介绍的是他的第二种用法,就是在交互式模式下的应用。 10 | 11 | 示例如下: 12 | 13 | .. code:: python 14 | 15 | >>> 3 + 4 16 | 7 17 | >>> _ 18 | 7 19 | >>> name='iswbm' 20 | >>> name 21 | 'iswbm' 22 | >>> _ 23 | 'iswbm' 24 | 25 | 它可以返回上一次的运行结果。 26 | 27 | 但是,如果是print函数打印出来的就不行了。 28 | 29 | .. code:: python 30 | 31 | >>> 3 + 4 32 | 7 33 | >>> _ 34 | 7 35 | >>> print("iswbm") 36 | iswbm 37 | >>> _ 38 | 7 39 | 40 | 我自己写了个例子,验证了下,用\ ``__repr__``\ 输出的内容可以被获取到的。 41 | 首先,在我们的目录下,写一个文件 demo.py。内容如下 42 | 43 | .. code:: python 44 | 45 | # demo.py 46 | class mytest(): 47 | def __str__(self): 48 | return "hello" 49 | 50 | def __repr__(self): 51 | return "world" 52 | 53 | 然后在这个目录下进入交互式环境。 54 | 55 | .. code:: python 56 | 57 | >>> import demo 58 | >>> mt=demo.mytest() 59 | >>> mt 60 | world 61 | >>> print(mt) 62 | hello 63 | >>> _ 64 | world 65 | 66 | 知道这两个魔法方法的人,一看就明白了,这里不再解释啦。 67 | -------------------------------------------------------------------------------- /source/c02/c02_02.md: -------------------------------------------------------------------------------- 1 | # 2.2 最快查看包搜索路径的方式 2 | ![](http://image.iswbm.com/20200804124133.png) 3 | 4 | 当你使用 import 导入一个包或模块时,Python 会去一些目录下查找,而这些目录是有优先级顺序的,正常人会使用 sys.path 查看。 5 | 6 | ```python 7 | >>> import sys 8 | >>> from pprint import pprint 9 | >>> pprint(sys.path) 10 | ['', 11 | '/usr/local/Python3.7/lib/python37.zip', 12 | '/usr/local/Python3.7/lib/python3.7', 13 | '/usr/local/Python3.7/lib/python3.7/lib-dynload', 14 | '/home/wangbm/.local/lib/python3.7/site-packages', 15 | '/usr/local/Python3.7/lib/python3.7/site-packages'] 16 | >>> 17 | ``` 18 | 19 | 那有没有更快的方式呢? 20 | 21 | 我这有一种连 console 模式都不用进入的方法呢? 22 | 23 | 你可能会想到这种,但这本质上与上面并无区别 24 | 25 | ```python 26 | [wangbm@localhost ~]$ python -c "print('\n'.join(__import__('sys').path))" 27 | 28 | /usr/lib/python2.7/site-packages/pip-18.1-py2.7.egg 29 | /usr/lib/python2.7/site-packages/redis-3.0.1-py2.7.egg 30 | /usr/lib64/python27.zip 31 | /usr/lib64/python2.7 32 | /usr/lib64/python2.7/plat-linux2 33 | /usr/lib64/python2.7/lib-tk 34 | /usr/lib64/python2.7/lib-old 35 | /usr/lib64/python2.7/lib-dynload 36 | /home/wangbm/.local/lib/python2.7/site-packages 37 | /usr/lib64/python2.7/site-packages 38 | /usr/lib64/python2.7/site-packages/gtk-2.0 39 | /usr/lib/python2.7/site-packages 40 | ``` 41 | 42 | 这里我要介绍的是比上面两种都方便得多的方法,一行命令即可解决 43 | 44 | ```shell 45 | [wangbm@localhost ~]$ python3 -m site 46 | sys.path = [ 47 | '/home/wangbm', 48 | '/usr/local/Python3.7/lib/python37.zip', 49 | '/usr/local/Python3.7/lib/python3.7', 50 | '/usr/local/Python3.7/lib/python3.7/lib-dynload', 51 | '/home/wangbm/.local/lib/python3.7/site-packages', 52 | '/usr/local/Python3.7/lib/python3.7/site-packages', 53 | ] 54 | USER_BASE: '/home/wangbm/.local' (exists) 55 | USER_SITE: '/home/wangbm/.local/lib/python3.7/site-packages' (exists) 56 | ENABLE_USER_SITE: True 57 | ``` 58 | 59 | 从输出你可以发现,这个列的路径会比 sys.path 更全,它包含了用户环境的目录。 60 | 61 | 62 | -------------------------------------------------------------------------------- /source/c02/c02_02.rst: -------------------------------------------------------------------------------- 1 | 2.2 最快查看包搜索路径的方式 2 | ============================ 3 | 4 | .. image:: http://image.iswbm.com/20200804124133.png 5 | 6 | 当你使用 import 导入一个包或模块时,Python 7 | 会去一些目录下查找,而这些目录是有优先级顺序的,正常人会使用 sys.path 8 | 查看。 9 | 10 | .. code:: python 11 | 12 | >>> import sys 13 | >>> from pprint import pprint 14 | >>> pprint(sys.path) 15 | ['', 16 | '/usr/local/Python3.7/lib/python37.zip', 17 | '/usr/local/Python3.7/lib/python3.7', 18 | '/usr/local/Python3.7/lib/python3.7/lib-dynload', 19 | '/home/wangbm/.local/lib/python3.7/site-packages', 20 | '/usr/local/Python3.7/lib/python3.7/site-packages'] 21 | >>> 22 | 23 | 那有没有更快的方式呢? 24 | 25 | 我这有一种连 console 模式都不用进入的方法呢? 26 | 27 | 你可能会想到这种,但这本质上与上面并无区别 28 | 29 | .. code:: python 30 | 31 | [wangbm@localhost ~]$ python -c "print('\n'.join(__import__('sys').path))" 32 | 33 | /usr/lib/python2.7/site-packages/pip-18.1-py2.7.egg 34 | /usr/lib/python2.7/site-packages/redis-3.0.1-py2.7.egg 35 | /usr/lib64/python27.zip 36 | /usr/lib64/python2.7 37 | /usr/lib64/python2.7/plat-linux2 38 | /usr/lib64/python2.7/lib-tk 39 | /usr/lib64/python2.7/lib-old 40 | /usr/lib64/python2.7/lib-dynload 41 | /home/wangbm/.local/lib/python2.7/site-packages 42 | /usr/lib64/python2.7/site-packages 43 | /usr/lib64/python2.7/site-packages/gtk-2.0 44 | /usr/lib/python2.7/site-packages 45 | 46 | 这里我要介绍的是比上面两种都方便得多的方法,一行命令即可解决 47 | 48 | .. code:: shell 49 | 50 | [wangbm@localhost ~]$ python3 -m site 51 | sys.path = [ 52 | '/home/wangbm', 53 | '/usr/local/Python3.7/lib/python37.zip', 54 | '/usr/local/Python3.7/lib/python3.7', 55 | '/usr/local/Python3.7/lib/python3.7/lib-dynload', 56 | '/home/wangbm/.local/lib/python3.7/site-packages', 57 | '/usr/local/Python3.7/lib/python3.7/site-packages', 58 | ] 59 | USER_BASE: '/home/wangbm/.local' (exists) 60 | USER_SITE: '/home/wangbm/.local/lib/python3.7/site-packages' (exists) 61 | ENABLE_USER_SITE: True 62 | 63 | 从输出你可以发现,这个列的路径会比 sys.path 64 | 更全,它包含了用户环境的目录。 65 | -------------------------------------------------------------------------------- /source/c02/c02_04.md: -------------------------------------------------------------------------------- 1 | # 2.4 命令行式执行 Python 代码 2 | ![](http://image.iswbm.com/20200804124133.png) 3 | 4 | 有时候你只是想验证一小段 Python 代码是否可用时,通常有两种方法 5 | 6 | 1. 输入 python 回车,进入 console 模式,然后敲入代码进行验证 7 | 2. 将你的代码写入 demo.py 脚本中,然后使用 python demo.py 验证 8 | 9 | 其实还有一种更简单的方法,比如我要计算一个字符串的md5 10 | 11 | ```shell 12 | $ python -c "import hashlib;print(hashlib.md5('hello').hexdigest())" 13 | 5d41402abc4b2a76b9719d911017c592 14 | ``` 15 | 16 | 只要加 -c 参数,就可以输入你的 Python 代码了。 17 | 18 | 19 | -------------------------------------------------------------------------------- /source/c02/c02_04.rst: -------------------------------------------------------------------------------- 1 | 2.4 命令行式执行 Python 代码 2 | ============================ 3 | 4 | .. image:: http://image.iswbm.com/20200804124133.png 5 | 6 | 有时候你只是想验证一小段 Python 代码是否可用时,通常有两种方法 7 | 8 | 1. 输入 python 回车,进入 console 模式,然后敲入代码进行验证 9 | 2. 将你的代码写入 demo.py 脚本中,然后使用 python demo.py 验证 10 | 11 | 其实还有一种更简单的方法,比如我要计算一个字符串的md5 12 | 13 | .. code:: shell 14 | 15 | $ python -c "import hashlib;print(hashlib.md5('hello').hexdigest())" 16 | 5d41402abc4b2a76b9719d911017c592 17 | 18 | 只要加 -c 参数,就可以输入你的 Python 代码了。 19 | -------------------------------------------------------------------------------- /source/c02/c02_05.md: -------------------------------------------------------------------------------- 1 | # 2.5 用调试模式执行脚本 2 | ![](http://image.iswbm.com/20200804124133.png) 3 | 4 | 当你使用 pdb 进行脚本的调试时,你可能会先在目标代码处输入 `import pdb;pdb.set_trace()` 来设置断点。 5 | 6 | 除此之外,还有一种方法,就是使用 `-m pdb` 7 | 8 | ```shell 9 | $ python -m pdb demo.py 10 | > /Users/MING/demo.py(1)() 11 | -> import sys 12 | (Pdb) 13 | ``` 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /source/c02/c02_05.rst: -------------------------------------------------------------------------------- 1 | 2.5 用调试模式执行脚本 2 | ====================== 3 | 4 | .. image:: http://image.iswbm.com/20200804124133.png 5 | 6 | 当你使用 pdb 进行脚本的调试时,你可能会先在目标代码处输入 7 | ``import pdb;pdb.set_trace()`` 来设置断点。 8 | 9 | 除此之外,还有一种方法,就是使用 ``-m pdb`` 10 | 11 | .. code:: shell 12 | 13 | $ python -m pdb demo.py 14 | > /Users/MING/demo.py(1)() 15 | -> import sys 16 | (Pdb) 17 | -------------------------------------------------------------------------------- /source/c02/c02_06.md: -------------------------------------------------------------------------------- 1 | # 2.6 如何快速搭建 HTTP 服务器 2 | ![](http://image.iswbm.com/20200804124133.png) 3 | 4 | 搭建FTP,或者是搭建网络文件系统,这些方法都能够实现Linux的目录共享。但是FTP和网络文件系统的功能都过于强大,因此它们都有一些不够方便的地方。比如你想快速共享Linux系统的某个目录给整个项目团队,还想在一分钟内做到,怎么办?很简单,使用Python中的SimpleHTTPServer。 5 | 6 | SimpleHTTPServer是Python 2自带的一个模块,是Python的Web服务器。它在Python 3已经合并到http.server模块中。具体例子如下,如不指定端口,则默认是8000端口。 7 | 8 | ```python 9 | # python2 10 | python -m SimpleHTTPServer 8888 11 | 12 | # python3 13 | python3 -m http.server 8888 14 | ``` 15 | 16 | ![](http://image.iswbm.com/20190511165716.png) 17 | 18 | SimpleHTTPServer有一个特性,如果待共享的目录下有index.html,那么index.html文件会被视为默认主页;如果不存在index.html文件,那么就会显示整个目录列表。 19 | 20 | 21 | -------------------------------------------------------------------------------- /source/c02/c02_06.rst: -------------------------------------------------------------------------------- 1 | 2.6 如何快速搭建 HTTP 服务器 2 | ============================ 3 | 4 | .. image:: http://image.iswbm.com/20200804124133.png 5 | 6 | 搭建FTP,或者是搭建网络文件系统,这些方法都能够实现Linux的目录共享。但是FTP和网络文件系统的功能都过于强大,因此它们都有一些不够方便的地方。比如你想快速共享Linux系统的某个目录给整个项目团队,还想在一分钟内做到,怎么办?很简单,使用Python中的SimpleHTTPServer。 7 | 8 | SimpleHTTPServer是Python 9 | 2自带的一个模块,是Python的Web服务器。它在Python 10 | 3已经合并到http.server模块中。具体例子如下,如不指定端口,则默认是8000端口。 11 | 12 | .. code:: python 13 | 14 | # python2 15 | python -m SimpleHTTPServer 8888 16 | 17 | # python3 18 | python3 -m http.server 8888 19 | 20 | .. image:: http://image.iswbm.com/20190511165716.png 21 | 22 | SimpleHTTPServer有一个特性,如果待共享的目录下有index.html,那么index.html文件会被视为默认主页;如果不存在index.html文件,那么就会显示整个目录列表。 23 | -------------------------------------------------------------------------------- /source/c02/c02_07.md: -------------------------------------------------------------------------------- 1 | # 2.7 快速构建 HTML 帮助文档 2 | ![](http://image.iswbm.com/20200804124133.png) 3 | 4 | 当你不知道一个内置模块如何使用时,会怎么做呢? 5 | 6 | 百度?Google? 7 | 8 | 其实完全没必要,这里教你一个离线学习 Python 模块的方法。 9 | 10 | 是的,你没有听错。 11 | 12 | 就算没有外网网络也能学习 Python 模块. 13 | 14 | 你只要在命令行下输入 `python -m pydoc -p xxx` 命令即可开启一个 HTTP 服务,xxx 为端口,你可以自己指定。 15 | 16 | ```shell 17 | $ python -m pydoc -p 5200 18 | pydoc server ready at http://localhost:5200/ 19 | ``` 20 | 21 | 帮助文档的效果如下 22 | 23 | ![](http://image.iswbm.com/20200718191249.png) 24 | 25 | ## 26 | 27 | 28 | -------------------------------------------------------------------------------- /source/c02/c02_07.rst: -------------------------------------------------------------------------------- 1 | 2.7 快速构建 HTML 帮助文档 2 | ========================== 3 | 4 | .. image:: http://image.iswbm.com/20200804124133.png 5 | 6 | 当你不知道一个内置模块如何使用时,会怎么做呢? 7 | 8 | 百度?Google? 9 | 10 | 其实完全没必要,这里教你一个离线学习 Python 模块的方法。 11 | 12 | 是的,你没有听错。 13 | 14 | 就算没有外网网络也能学习 Python 模块. 15 | 16 | 你只要在命令行下输入 ``python -m pydoc -p xxx`` 命令即可开启一个 HTTP 17 | 服务,xxx 为端口,你可以自己指定。 18 | 19 | .. code:: shell 20 | 21 | $ python -m pydoc -p 5200 22 | pydoc server ready at http://localhost:5200/ 23 | 24 | 帮助文档的效果如下 25 | 26 | .. image:: http://image.iswbm.com/20200718191249.png 27 | -------------------------------------------------------------------------------- /source/c02/c02_08.md: -------------------------------------------------------------------------------- 1 | # 2.8 最正确且优雅的装包方法 2 | ![](http://image.iswbm.com/20200804124133.png) 3 | 4 | 当你使用 pip 来安装第三方的模块时,通常会使用这样的命令 5 | 6 | ```shell 7 | $ pip install requests 8 | ``` 9 | 10 | 此时如果你的环境中有 Python2 也有 Python 3,那你使用这条命令安装的包是安装 Python2 呢?还是安装到 Python 3 呢? 11 | 12 | 就算你的环境上没有安装 Python2,那也有可能存在着多个版本的 Python 吧?比如安装了 Python3.8,也安装了 Python3.9,那你安装包时就会很困惑,我到底把包安装在了哪里? 13 | 14 | 但若你使用这样的命令去安装,就没有了这样的烦恼了 15 | 16 | ```shell 17 | # 在 python2 中安装 18 | $ python -m pip install requests 19 | 20 | # 在 python3 中安装 21 | $ python3 -m pip install requests 22 | 23 | # 在 python3.8 中安装 24 | $ python3.8 -m pip install requests 25 | 26 | # 在 python3.9 中安装 27 | $ python3.9 -m pip install requests 28 | ``` 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /source/c02/c02_08.rst: -------------------------------------------------------------------------------- 1 | 2.8 最正确且优雅的装包方法 2 | ========================== 3 | 4 | .. image:: http://image.iswbm.com/20200804124133.png 5 | 6 | 当你使用 pip 来安装第三方的模块时,通常会使用这样的命令 7 | 8 | .. code:: shell 9 | 10 | $ pip install requests 11 | 12 | 此时如果你的环境中有 Python2 也有 Python 13 | 3,那你使用这条命令安装的包是安装 Python2 呢?还是安装到 Python 3 呢? 14 | 15 | 就算你的环境上没有安装 Python2,那也有可能存在着多个版本的 Python 16 | 吧?比如安装了 Python3.8,也安装了 17 | Python3.9,那你安装包时就会很困惑,我到底把包安装在了哪里? 18 | 19 | 但若你使用这样的命令去安装,就没有了这样的烦恼了 20 | 21 | .. code:: shell 22 | 23 | # 在 python2 中安装 24 | $ python -m pip install requests 25 | 26 | # 在 python3 中安装 27 | $ python3 -m pip install requests 28 | 29 | # 在 python3.8 中安装 30 | $ python3.8 -m pip install requests 31 | 32 | # 在 python3.9 中安装 33 | $ python3.9 -m pip install requests 34 | -------------------------------------------------------------------------------- /source/c02/c02_09.md: -------------------------------------------------------------------------------- 1 | # 2.9 往 Python Shell 中传入参数 2 | ![](http://image.iswbm.com/20200804124133.png) 3 | 4 | 往一个 Python 脚本传入参数,是一件非常简单的事情。 5 | 6 | 比如这样: 7 | 8 | ```shell 9 | $ python demo.py arg1 arg2 10 | ``` 11 | 12 | 我在验证一些简单的 Python 代码时,喜欢使用 Python Shell 。 13 | 14 | 那有没有办法在使用 Python Shell 时,向上面传递参数一样,传入参数呢? 15 | 16 | 经过我的摸索,终于找到了方法,具体方法如下: 17 | 18 | ![](http://image.iswbm.com/20200801195158.png) 19 | 20 | ## 21 | 22 | 23 | -------------------------------------------------------------------------------- /source/c02/c02_09.rst: -------------------------------------------------------------------------------- 1 | 2.9 往 Python Shell 中传入参数 2 | ============================== 3 | 4 | .. image:: http://image.iswbm.com/20200804124133.png 5 | 6 | 往一个 Python 脚本传入参数,是一件非常简单的事情。 7 | 8 | 比如这样: 9 | 10 | .. code:: shell 11 | 12 | $ python demo.py arg1 arg2 13 | 14 | 我在验证一些简单的 Python 代码时,喜欢使用 Python Shell 。 15 | 16 | 那有没有办法在使用 Python Shell 时,向上面传递参数一样,传入参数呢? 17 | 18 | 经过我的摸索,终于找到了方法,具体方法如下: 19 | 20 | .. image:: http://image.iswbm.com/20200801195158.png 21 | -------------------------------------------------------------------------------- /source/c02/c02_10.md: -------------------------------------------------------------------------------- 1 | # 2.10 让脚本报错后立即进入调试模式 2 | ![](http://image.iswbm.com/20200804124133.png) 3 | 4 | 当你在使用 `python xxx.py` 这样的方法,执行 Python 脚本时,若因为代码 bug 导致异常未捕获,那整个程序便会终止退出。 5 | 6 | 这个时候,我们通常会去排查是什么原因导致的程序崩溃。 7 | 8 | 大家都知道,排查问题的思路,第一步肯定是去查看日志,若这个 bug 隐藏得比较深,只有在特定场景下才会现身,那么还需要开发者,复现这个 bug,方能优化代码。 9 | 10 | 复现有时候很难,有时候虽然简单,但是要伪造各种数据,相当麻烦。 11 | 12 | **如果有一种方法能在程序崩溃后,立马进入调试模式该有多好啊?** 13 | 14 | 明哥都这么问了,那肯定是带着解决方案来的。 15 | 16 | 只要你在执行脚本行,加上 `-i` 参数,即可在脚本执行完毕后进入 Python Shell 模式,方便你进行调试。 17 | 18 | 具体演示如下: 19 | 20 | ![](http://image.iswbm.com/20200801195950.png) 21 | 22 | 需要注意的是:脚本执行完毕,有两种情况: 23 | 24 | 1. 正常退出 25 | 2. 异常退出 26 | 27 | 这两种都会进入 Python Shell,如果脚本并无异常,最终也会进入 Python Shell 模式,需要你手动退出 28 | 29 | ![](http://image.iswbm.com/20200801201110.png) 30 | 31 | 如果希望脚本正确完成时自动退出,可以在脚本最后加上一行`__import__("os")._exit(0)` 32 | 33 | 34 | -------------------------------------------------------------------------------- /source/c02/c02_10.rst: -------------------------------------------------------------------------------- 1 | 2.10 让脚本报错后立即进入调试模式 2 | ================================= 3 | 4 | .. image:: http://image.iswbm.com/20200804124133.png 5 | 6 | 当你在使用 ``python xxx.py`` 这样的方法,执行 Python 脚本时,若因为代码 7 | bug 导致异常未捕获,那整个程序便会终止退出。 8 | 9 | 这个时候,我们通常会去排查是什么原因导致的程序崩溃。 10 | 11 | 大家都知道,排查问题的思路,第一步肯定是去查看日志,若这个 bug 12 | 隐藏得比较深,只有在特定场景下才会现身,那么还需要开发者,复现这个 13 | bug,方能优化代码。 14 | 15 | 复现有时候很难,有时候虽然简单,但是要伪造各种数据,相当麻烦。 16 | 17 | **如果有一种方法能在程序崩溃后,立马进入调试模式该有多好啊?** 18 | 19 | 明哥都这么问了,那肯定是带着解决方案来的。 20 | 21 | 只要你在执行脚本行,加上 ``-i`` 参数,即可在脚本执行完毕后进入 Python 22 | Shell 模式,方便你进行调试。 23 | 24 | 具体演示如下: 25 | 26 | .. image:: http://image.iswbm.com/20200801195950.png 27 | 28 | 需要注意的是:脚本执行完毕,有两种情况: 29 | 30 | 1. 正常退出 31 | 2. 异常退出 32 | 33 | 这两种都会进入 Python Shell,如果脚本并无异常,最终也会进入 Python Shell 34 | 模式,需要你手动退出 35 | 36 | .. image:: http://image.iswbm.com/20200801201110.png 37 | 38 | 如果希望脚本正确完成时自动退出,可以在脚本最后加上一行\ ``__import__("os")._exit(0)`` 39 | -------------------------------------------------------------------------------- /source/c02/c02_11.md: -------------------------------------------------------------------------------- 1 | # 2.11 极简模式执行 Python Shell 2 | ![](http://image.iswbm.com/20200804124133.png) 3 | 4 | 在终端输入 Python 就会进入 Python Shell 。 5 | 6 | 方便是挺方便,就是有点说不出的难受,谁能告诉我,为什么要多出这么大一段无关的内容。 7 | 8 | ![](http://image.iswbm.com/20200801202733.png) 9 | 10 | 这有点像,你上爱某艺看视频吧,都要先看个 90 秒的广告。 11 | 12 | 如果你和我一样不喜欢这种 『牛皮癣』,那么可以加个 `-q` 参数,静默进入 Python Shell,就像下面这样子,开启了极简模式,舒服多了。 13 | 14 | ![](http://image.iswbm.com/20200801203047.png) 15 | 16 | 17 | ![](http://image.iswbm.com/20200512125643.png) 18 | 19 | 20 | -------------------------------------------------------------------------------- /source/c02/c02_11.rst: -------------------------------------------------------------------------------- 1 | 2.11 极简模式执行 Python Shell 2 | ============================== 3 | 4 | .. image:: http://image.iswbm.com/20200804124133.png 5 | 6 | 在终端输入 Python 就会进入 Python Shell 。 7 | 8 | 方便是挺方便,就是有点说不出的难受,谁能告诉我,为什么要多出这么大一段无关的内容。 9 | 10 | .. image:: http://image.iswbm.com/20200801202733.png 11 | 12 | 这有点像,你上爱某艺看视频吧,都要先看个 90 秒的广告。 13 | 14 | 如果你和我一样不喜欢这种 『牛皮癣』,那么可以加个 ``-q`` 参数,静默进入 15 | Python Shell,就像下面这样子,开启了极简模式,舒服多了。 16 | 17 | .. image:: http://image.iswbm.com/20200801203047.png 18 | 19 | .. image:: http://image.iswbm.com/20200512125643.png 20 | -------------------------------------------------------------------------------- /source/c02/c02_12.md: -------------------------------------------------------------------------------- 1 | # 2.12 在执行任意代码前自动念一段平安经 2 | ![](http://image.iswbm.com/20200804124133.png) 3 | 4 | 最近的"平安经"可谓是引起了不小的风波啊。 5 | 6 | 作为一个正儿八经的程序员,最害怕的就是自己的代码上线出现各种各样的 BUG。 7 | 8 | 为此明哥就研究了一下,如何在你执行任意 Python 代码前,让 Python 解释器自动念上一段平安经,保佑代码不出 BUG 。 9 | 10 | 没想到还真被我研究出来了 11 | 12 | 做好心理准备了嘛? 13 | 14 | 我要开始作妖了,噢不,是开始念经了。 15 | 16 | ![](http://image.iswbm.com/20200801221705.png) 17 | 18 | 感谢佛祖保佑,Everything is ok,No bugs in the code. 19 | 20 | 21 | 22 | 你一定很想知道这是如何的吧? 23 | 24 | 如果你对 Linux 比较熟悉,就会知道,当你在使用 SSH 远程登陆 Linux 服务器的时候?会读取 `.bash_profile` 文件加载一些环境变量。 25 | 26 | `.bash_profile` 你可以视其为一个 shell 脚本,可以在这里写一些 shell 代码达到你的定制化需求。 27 | 28 | 而在 Python 中,也有类似 `.bash_profile` 的文件,这个文件一般情况下是不存在的。 29 | 30 | 我们需要新建一个用户环境目录,这个目录比较长,不需要你死记硬背,使用 site 模块的方法就可以获取,然后使用 `mkdir -p` 命令创建它。 31 | 32 | ![](http://image.iswbm.com/20200801220819.png) 33 | 34 | 在这个目录下,新建一个 `usercustomize.py` 文件,注意名字必须是这个,换成其他的可就识别不到啦。 35 | 36 | 这个 `usercustomize.py` 的内容如下(明哥注:佛祖只保佑几个 Python 的主要应用方向,毕竟咱是 Python 攻城狮嘛...) 37 | 38 | ![](http://image.iswbm.com/20200801221413.png) 39 | 40 | 这个文件我放在了我的 github 上,点击[这里](https://github.com/iswbm/magic-python/blob/master/usercustomize.py)直达。 41 | 42 | 一切都完成后,无论你是使用 `python xxx.py` 执行脚本 43 | 44 | ![](http://image.iswbm.com/20200801221705.png) 45 | 46 | 还是使用 `python` 进入 Python Shell ,都会先念一下平安经保平安。 47 | 48 | ![](http://image.iswbm.com/20200801221457.png) 49 | 50 | 另外,有读者反馈这种设置会导致在Win10环境下 VSCode不能正常识别已安装的Python环境,并报出代码有导包错误等问题,因此请在你知道你自己在做什么,会千万什么后果,否则请在体验后还原你的环境。 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /source/c02/c02_12.rst: -------------------------------------------------------------------------------- 1 | 2.12 在执行任意代码前自动念一段平安经 2 | ===================================== 3 | 4 | .. image:: http://image.iswbm.com/20200804124133.png 5 | 6 | 最近的”平安经”可谓是引起了不小的风波啊。 7 | 8 | 作为一个正儿八经的程序员,最害怕的就是自己的代码上线出现各种各样的 BUG。 9 | 10 | 为此明哥就研究了一下,如何在你执行任意 Python 代码前,让 Python 11 | 解释器自动念上一段平安经,保佑代码不出 BUG 。 12 | 13 | 没想到还真被我研究出来了 14 | 15 | 做好心理准备了嘛? 16 | 17 | 我要开始作妖了,噢不,是开始念经了。 18 | 19 | .. image:: http://image.iswbm.com/20200801221705.png 20 | 21 | 感谢佛祖保佑,Everything is ok,No bugs in the code. 22 | 23 | 你一定很想知道这是如何的吧? 24 | 25 | 如果你对 Linux 比较熟悉,就会知道,当你在使用 SSH 远程登陆 Linux 26 | 服务器的时候?会读取 ``.bash_profile`` 文件加载一些环境变量。 27 | 28 | ``.bash_profile`` 你可以视其为一个 shell 脚本,可以在这里写一些 shell 29 | 代码达到你的定制化需求。 30 | 31 | 而在 Python 中,也有类似 ``.bash_profile`` 32 | 的文件,这个文件一般情况下是不存在的。 33 | 34 | 我们需要新建一个用户环境目录,这个目录比较长,不需要你死记硬背,使用 35 | site 模块的方法就可以获取,然后使用 ``mkdir -p`` 命令创建它。 36 | 37 | .. image:: http://image.iswbm.com/20200801220819.png 38 | 39 | 在这个目录下,新建一个 ``usercustomize.py`` 40 | 文件,注意名字必须是这个,换成其他的可就识别不到啦。 41 | 42 | 这个 ``usercustomize.py`` 的内容如下(明哥注:佛祖只保佑几个 Python 43 | 的主要应用方向,毕竟咱是 Python 攻城狮嘛…) 44 | 45 | .. image:: http://image.iswbm.com/20200801221413.png 46 | 47 | 这个文件我放在了我的 github 48 | 上,点击\ `这里 `__\ 直达。 49 | 50 | 一切都完成后,无论你是使用 ``python xxx.py`` 执行脚本 51 | 52 | .. image:: http://image.iswbm.com/20200801221705.png 53 | 54 | 还是使用 ``python`` 进入 Python Shell ,都会先念一下平安经保平安。 55 | 56 | .. image:: http://image.iswbm.com/20200801221457.png 57 | 58 | 另外,有读者反馈这种设置会导致在Win10环境下 59 | VSCode不能正常识别已安装的Python环境,并报出代码有导包错误等问题,因此请在你知道你自己在做什么,会千万什么后果,否则请在体验后还原你的环境。 60 | -------------------------------------------------------------------------------- /source/c02/c02_13.md: -------------------------------------------------------------------------------- 1 | # 2.13 启动 Python Shell 前自动执行某脚本 2 | ![](http://image.iswbm.com/20200804124133.png) 3 | 4 | 前一节我们介绍了一种,只要运行解释器就会自动触发执行 Python 脚本的方法。 5 | 6 | 除此之外,可还有其他方法呢? 7 | 8 | 当然是有,只不过相对来说,会麻烦一点了。 9 | 10 | 先来看一下效果,在 `~/Library/Python/3.9/lib/python/site-packages` 目录下并没有 `usercustomize.py` 文件,但是在执行 python 进入 Python Shell 模式后,还是会打印平安经。 11 | 12 | ![](http://image.iswbm.com/20200801225652.png) 13 | 14 | 这是如何做到的呢? 15 | 16 | 很简单,只要做两件事 17 | 18 | 第一件事,在任意你喜欢的目录下,新建 一个Python 脚本,名字也随意,比如我叫 `startup.py`,内容还是和上面一样 19 | 20 | ![](http://image.iswbm.com/20200801221413.png) 21 | 22 | 第二件事,设置一个环境变量 PYTHONSTARTUP,指向你的脚本路径 23 | 24 | ```shell 25 | $ export PYTHONSTARTUP=/Users/MING/startup.py 26 | ``` 27 | 28 | 这样就可以了。 29 | 30 | 但是这种方法只适用于 Python Shell ,并不适用于 Python 执行脚本的方法。 31 | 32 | ![](http://image.iswbm.com/20200801230230.png) 33 | 34 | 如果要在脚本中实现这种效果,我目前想到最粗糙我笨拙的方法了 -- `手动加载执行` 35 | 36 | ![](http://image.iswbm.com/20200801230503.png) 37 | 38 | 39 | -------------------------------------------------------------------------------- /source/c02/c02_13.rst: -------------------------------------------------------------------------------- 1 | 2.13 启动 Python Shell 前自动执行某脚本 2 | ======================================= 3 | 4 | .. image:: http://image.iswbm.com/20200804124133.png 5 | 6 | 前一节我们介绍了一种,只要运行解释器就会自动触发执行 Python 脚本的方法。 7 | 8 | 除此之外,可还有其他方法呢? 9 | 10 | 当然是有,只不过相对来说,会麻烦一点了。 11 | 12 | 先来看一下效果,在 ``~/Library/Python/3.9/lib/python/site-packages`` 13 | 目录下并没有 ``usercustomize.py`` 文件,但是在执行 python 进入 Python 14 | Shell 模式后,还是会打印平安经。 15 | 16 | .. image:: http://image.iswbm.com/20200801225652.png 17 | 18 | 这是如何做到的呢? 19 | 20 | 很简单,只要做两件事 21 | 22 | 第一件事,在任意你喜欢的目录下,新建 一个Python 23 | 脚本,名字也随意,比如我叫 ``startup.py``\ ,内容还是和上面一样 24 | 25 | .. image:: http://image.iswbm.com/20200801221413.png 26 | 27 | 第二件事,设置一个环境变量 PYTHONSTARTUP,指向你的脚本路径 28 | 29 | .. code:: shell 30 | 31 | $ export PYTHONSTARTUP=/Users/MING/startup.py 32 | 33 | 这样就可以了。 34 | 35 | 但是这种方法只适用于 Python Shell ,并不适用于 Python 执行脚本的方法。 36 | 37 | .. image:: http://image.iswbm.com/20200801230230.png 38 | 39 | 如果要在脚本中实现这种效果,我目前想到最粗糙我笨拙的方法了 – 40 | ``手动加载执行`` 41 | 42 | .. image:: http://image.iswbm.com/20200801230503.png 43 | -------------------------------------------------------------------------------- /source/c02/c02_15.md: -------------------------------------------------------------------------------- 1 | # 2.15 命令行式打开 idle 编辑脚本 2 | 3 | ![](http://image.iswbm.com/20200804124133.png) 4 | 5 | 在你安装 Python 解释器的时候,会有一个选项,让你选择是否安装 idle,这是一个极简的 Python 编辑器,对于有点 python 编码的经验的同学,一般都已经安装了更加专业的代码编辑器,比如 pycharm,vscode 等,所以一般是不会去勾选它的。 6 | 7 | 但是对于第一次接触 Python 编程的初学者来说,在自己的电脑上,大概率是没有安装代码编辑器的,这时候有一个现成的编辑器,可以尽快的将 hello world 跑起来,是一件非常重要的事情,因此初学者一般会被建议安装 idle,这也是为什么 idle 是大多数人的第一个 Python 代码编辑器。 8 | 9 | 在你安装了 idle 后,如果你使用 Windows 电脑,点击 py 文件会有使用 idle 打开的选项,非常方便你直接编辑 py 文件。 10 | 11 | 但如若你在 mac 电脑上,你的右键,是没有这个选项的,那如何使用 idle 打开编辑呢? 12 | 13 | 可以在终端上使用如下这条命令即可调用 idle 打开指定文件 14 | 15 | ```shell 16 | python3 -m idlelib unshelve.py 17 | ``` 18 | 19 | 使用的效果如下 20 | 21 | ![](http://image.iswbm.com/20210504110446.png) 22 | 23 | 如果你不加文件的路径,默认会打开 idle 的 shell 模式 24 | 25 | ![](http://image.iswbm.com/20210504110758.png) 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /source/c02/c02_15.rst: -------------------------------------------------------------------------------- 1 | 2.15 命令行式打开 idle 编辑脚本 2 | =============================== 3 | 4 | .. image:: http://image.iswbm.com/20200804124133.png 5 | 6 | 在你安装 Python 解释器的时候,会有一个选项,让你选择是否安装 7 | idle,这是一个极简的 Python 编辑器,对于有点 python 8 | 编码的经验的同学,一般都已经安装了更加专业的代码编辑器,比如 9 | pycharm,vscode 等,所以一般是不会去勾选它的。 10 | 11 | 但是对于第一次接触 Python 12 | 编程的初学者来说,在自己的电脑上,大概率是没有安装代码编辑器的,这时候有一个现成的编辑器,可以尽快的将 13 | hello world 跑起来,是一件非常重要的事情,因此初学者一般会被建议安装 14 | idle,这也是为什么 idle 是大多数人的第一个 Python 代码编辑器。 15 | 16 | 在你安装了 idle 后,如果你使用 Windows 电脑,点击 py 文件会有使用 idle 17 | 打开的选项,非常方便你直接编辑 py 文件。 18 | 19 | 但如若你在 mac 电脑上,你的右键,是没有这个选项的,那如何使用 idle 20 | 打开编辑呢? 21 | 22 | 可以在终端上使用如下这条命令即可调用 idle 打开指定文件 23 | 24 | .. code:: shell 25 | 26 | python3 -m idlelib unshelve.py 27 | 28 | 使用的效果如下 29 | 30 | .. image:: http://image.iswbm.com/20210504110446.png 31 | 32 | 如果你不加文件的路径,默认会打开 idle 的 shell 模式 33 | 34 | .. image:: http://image.iswbm.com/20210504110758.png 35 | -------------------------------------------------------------------------------- /source/c02/c02_16.md: -------------------------------------------------------------------------------- 1 | # 2.16 快速计算字符串 base64编码 2 | 3 | ![](http://image.iswbm.com/20200804124133.png) 4 | 5 | ## 对字符串编码和解码 6 | 7 | 对一个字符串进行 base64 编码 和 解码(加上 `-d` 参数即可) 8 | 9 | ```shell 10 | $ echo "hello, world" | python3 -m base64 11 | aGVsbG8sIHdvcmxkCg== 12 | 13 | $ echo "aGVsbG8sIHdvcmxkCg==" | python3 -m base64 -d 14 | hello, world 15 | ``` 16 | 17 | 效果如下 18 | 19 | ![](http://image.iswbm.com/20210504111702.png) 20 | 21 | ## 对文件进行编码和解码 22 | 23 | 在命令后面直接加文件的路径 24 | 25 | ```shell 26 | # 编码 27 | $ python3 -m base64 demo.py 28 | ZGVmIG1haW4oKToKICAgcHJpbnQoJ0hlbGxvIFdvcmxk8J+RjCcpCiAgIAppZiBfX25hbWVfXz09 29 | J19fbWFpbl9fJzoKICAgbWFpbigpCg== 30 | 31 | # 解码 32 | $ echo "ZGVmIG1haW4oKToKICAgcHJpbnQoJ0hlbGxvIFdvcmxk8J+RjCcpCiAgIAppZiBfX25hbWVfXz09 33 | J19fbWFpbl9fJzoKICAgbWFpbigpCg==" | python3 -m base64 -d 34 | def main(): 35 | print('Hello World👌') 36 | 37 | if __name__=='__main__': 38 | main() 39 | ``` 40 | 41 | 效果如下 42 | 43 | ![](http://image.iswbm.com/20210504112153.png) 44 | 45 | 如果你的文件是 py 脚本的话,可以直接执行它 46 | 47 | ![](http://image.iswbm.com/20210504112257.png) 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /source/c02/c02_16.rst: -------------------------------------------------------------------------------- 1 | 2.16 快速计算字符串 base64编码 2 | ============================== 3 | 4 | .. image:: http://image.iswbm.com/20200804124133.png 5 | 6 | 对字符串编码和解码 7 | ------------------ 8 | 9 | 对一个字符串进行 base64 编码 和 解码(加上 ``-d`` 参数即可) 10 | 11 | .. code:: shell 12 | 13 | $ echo "hello, world" | python3 -m base64 14 | aGVsbG8sIHdvcmxkCg== 15 | 16 | $ echo "aGVsbG8sIHdvcmxkCg==" | python3 -m base64 -d 17 | hello, world 18 | 19 | 效果如下 20 | 21 | .. image:: http://image.iswbm.com/20210504111702.png 22 | 23 | 对文件进行编码和解码 24 | -------------------- 25 | 26 | 在命令后面直接加文件的路径 27 | 28 | .. code:: shell 29 | 30 | # 编码 31 | $ python3 -m base64 demo.py 32 | ZGVmIG1haW4oKToKICAgcHJpbnQoJ0hlbGxvIFdvcmxk8J+RjCcpCiAgIAppZiBfX25hbWVfXz09 33 | J19fbWFpbl9fJzoKICAgbWFpbigpCg== 34 | 35 | # 解码 36 | $ echo "ZGVmIG1haW4oKToKICAgcHJpbnQoJ0hlbGxvIFdvcmxk8J+RjCcpCiAgIAppZiBfX25hbWVfXz09 37 | J19fbWFpbl9fJzoKICAgbWFpbigpCg==" | python3 -m base64 -d 38 | def main(): 39 | print('Hello World👌') 40 | 41 | if __name__=='__main__': 42 | main() 43 | 44 | 效果如下 45 | 46 | .. image:: http://image.iswbm.com/20210504112153.png 47 | 48 | 如果你的文件是 py 脚本的话,可以直接执行它 49 | 50 | .. image:: http://image.iswbm.com/20210504112257.png 51 | -------------------------------------------------------------------------------- /source/c02/c02_17.md: -------------------------------------------------------------------------------- 1 | # 2.17 快速找到指定文件的mime类型 2 | 3 | ![](http://image.iswbm.com/20200804124133.png) 4 | 5 | 识别 html 文件 6 | 7 | ```shell 8 | # 可在线检测 9 | $ python -m mimetypes https://docs.python.org/3/library/mimetypes.html 10 | type: text/html encoding: None 11 | 12 | # 也可离线检测(后面不再赘述) 13 | $ python -m mimetypes index.html 14 | type: text/html encoding: None 15 | ``` 16 | 17 | 识别图片格式 18 | 19 | ```shell 20 | $ python -m mimetypes https://www.google.com/images/branding/googlelogo/2x/googlelogo_color_272x92dp.png 21 | type: image/png encoding: None 22 | ``` 23 | 24 | 识别 Python 脚本 25 | 26 | ```shell 27 | $ python -m mimetypes sample.py 28 | type: text/x-python encoding: None # python文件 29 | ``` 30 | 31 | 识别压缩文件 32 | 33 | ```shell 34 | $ python -m mimetypes sample.py.gz 35 | type: text/x-python encoding: gzip # python文件,gzip压缩 36 | ``` 37 | 38 | -------------------------------------------------------------------------------- /source/c02/c02_17.rst: -------------------------------------------------------------------------------- 1 | 2.17 快速找到指定文件的mime类型 2 | =============================== 3 | 4 | .. image:: http://image.iswbm.com/20200804124133.png 5 | 6 | 识别 html 文件 7 | 8 | .. code:: shell 9 | 10 | # 可在线检测 11 | $ python -m mimetypes https://docs.python.org/3/library/mimetypes.html 12 | type: text/html encoding: None 13 | 14 | # 也可离线检测(后面不再赘述) 15 | $ python -m mimetypes index.html 16 | type: text/html encoding: None 17 | 18 | 识别图片格式 19 | 20 | .. code:: shell 21 | 22 | $ python -m mimetypes https://www.google.com/images/branding/googlelogo/2x/googlelogo_color_272x92dp.png 23 | type: image/png encoding: None 24 | 25 | 识别 Python 脚本 26 | 27 | .. code:: shell 28 | 29 | $ python -m mimetypes sample.py 30 | type: text/x-python encoding: None # python文件 31 | 32 | 识别压缩文件 33 | 34 | .. code:: shell 35 | 36 | $ python -m mimetypes sample.py.gz 37 | type: text/x-python encoding: gzip # python文件,gzip压缩 38 | -------------------------------------------------------------------------------- /source/c02/c02_18.md: -------------------------------------------------------------------------------- 1 | # 2.18 快速查看 Python 的环境信息 2 | 3 | ![](http://image.iswbm.com/20200804124133.png) 4 | 5 | 所有与 Python 相关的信息与配置,你都可以使用下面这条命令将其全部打印出来 6 | 7 | ```shell 8 | $ python -m sysconfig 9 | ``` 10 | 11 | 信息包括: 12 | 13 | - 你当前的操作系统平台 14 | - Python 的具体版本 15 | - 包的搜索路径 16 | - 以及各种环境变量 17 | 18 | ![](http://image.iswbm.com/20210504114516.png) 19 | 20 | -------------------------------------------------------------------------------- /source/c02/c02_18.rst: -------------------------------------------------------------------------------- 1 | 2.18 快速查看 Python 的环境信息 2 | =============================== 3 | 4 | .. image:: http://image.iswbm.com/20200804124133.png 5 | 6 | 所有与 Python 相关的信息与配置,你都可以使用下面这条命令将其全部打印出来 7 | 8 | .. code:: shell 9 | 10 | $ python -m sysconfig 11 | 12 | 信息包括: 13 | 14 | - 你当前的操作系统平台 15 | - Python 的具体版本 16 | - 包的搜索路径 17 | - 以及各种环境变量 18 | 19 | .. image:: http://image.iswbm.com/20210504114516.png 20 | -------------------------------------------------------------------------------- /source/c02/c02_19.md: -------------------------------------------------------------------------------- 1 | # 2.19 快速解压和压缩文件 2 | 3 | ![](http://image.iswbm.com/20200804124133.png) 4 | 5 | ## tar 格式压缩包 6 | 7 | 创建一个 tar 压缩包 8 | 9 | ```shell 10 | # 将 demo 文件夹压缩成 demo.tar 11 | $ python3 -m tarfile -c demo.tar demo 12 | ``` 13 | 14 | 解压 tar 压缩包 15 | 16 | ```shell 17 | # 解压 demo.tar 到 demo_new 文件夹下 18 | $ python3 -m tarfile -e demo.tar demo_new 19 | ``` 20 | 21 | ## gzip 格式压缩包 22 | 23 | 创建一个 gzip 格式的压缩包(gzip 的输入,只能是一个文件,而不能是一个目录) 24 | 25 | ```shell 26 | $ ls -l | grep message 27 | -rw-r--r--@ 1 MING staff 97985 4 22 08:30 message 28 | 29 | # 将 message.html 文件夹压缩成 message.gz 30 | $ python3 -m gzip message 31 | 32 | $ ls -l | grep message 33 | -rw-r--r--@ 1 MING staff 97985 4 22 08:30 message 34 | -rw-r--r-- 1 MING staff 24908 5 4 12:49 message.gz 35 | ``` 36 | 37 | 解压一个 gzip 格式的压缩包 38 | 39 | ```shell 40 | $ rm -rf message 41 | 42 | $ ls -l | grep message 43 | -rw-r--r-- 1 MING staff 87 5 4 12:51 message.gz 44 | 45 | # 解压 message.gz 46 | $ python3 -m gzip -d message.gz 47 | 48 | $ ls -l | grep message 49 | -rw-r--r-- 1 MING staff 62 5 4 12:52 message 50 | -rw-r--r-- 1 MING staff 87 5 4 12:51 message.gz 51 | ``` 52 | 53 | ## zip 格式压缩包 54 | 55 | 创建一个 zip 格式的压缩包 56 | 57 | ```shell 58 | $ ls -l | grep demo 59 | drwxr-xr-x 3 MING staff 96 5 4 12:44 demo 60 | 61 | # 压缩 demo 文件夹为 demo.zip 62 | $ python3 -m zipfile -c demo.zip demo 63 | 64 | $ ls -l | grep demo 65 | drwxr-xr-x 3 MING staff 96 5 4 12:44 demo 66 | -rw-r--r-- 1 MING staff 74890 5 4 12:55 demo.zip 67 | ``` 68 | 69 | 解压一个 zip 格式的压缩包 70 | 71 | ```shell 72 | $ rm -rf demo 73 | 74 | $ ls -l | grep demo 75 | -rw-r--r-- 1 MING staff 74890 5 4 12:55 demo.zip 76 | 77 | $ python3 -m zipfile -e demo.zip demo 78 | 79 | $ ls -l | grep demo 80 | drwxr-xr-x 3 MING staff 96 5 4 12:57 demo 81 | -rw-r--r-- 1 MING staff 74890 5 4 12:55 demo.zip 82 | ``` 83 | 84 | 85 | 86 | -------------------------------------------------------------------------------- /source/c02/c02_19.rst: -------------------------------------------------------------------------------- 1 | 2.19 快速解压和压缩文件 2 | ======================= 3 | 4 | .. image:: http://image.iswbm.com/20200804124133.png 5 | 6 | tar 格式压缩包 7 | -------------- 8 | 9 | 创建一个 tar 压缩包 10 | 11 | .. code:: shell 12 | 13 | # 将 demo 文件夹压缩成 demo.tar 14 | $ python3 -m tarfile -c demo.tar demo 15 | 16 | 解压 tar 压缩包 17 | 18 | .. code:: shell 19 | 20 | # 解压 demo.tar 到 demo_new 文件夹下 21 | $ python3 -m tarfile -e demo.tar demo_new 22 | 23 | gzip 格式压缩包 24 | --------------- 25 | 26 | 创建一个 gzip 格式的压缩包(gzip 27 | 的输入,只能是一个文件,而不能是一个目录) 28 | 29 | .. code:: shell 30 | 31 | $ ls -l | grep message 32 | -rw-r--r--@ 1 MING staff 97985 4 22 08:30 message 33 | 34 | # 将 message.html 文件夹压缩成 message.gz 35 | $ python3 -m gzip message 36 | 37 | $ ls -l | grep message 38 | -rw-r--r--@ 1 MING staff 97985 4 22 08:30 message 39 | -rw-r--r-- 1 MING staff 24908 5 4 12:49 message.gz 40 | 41 | 解压一个 gzip 格式的压缩包 42 | 43 | .. code:: shell 44 | 45 | $ rm -rf message 46 | 47 | $ ls -l | grep message 48 | -rw-r--r-- 1 MING staff 87 5 4 12:51 message.gz 49 | 50 | # 解压 message.gz 51 | $ python3 -m gzip -d message.gz 52 | 53 | $ ls -l | grep message 54 | -rw-r--r-- 1 MING staff 62 5 4 12:52 message 55 | -rw-r--r-- 1 MING staff 87 5 4 12:51 message.gz 56 | 57 | zip 格式压缩包 58 | -------------- 59 | 60 | 创建一个 zip 格式的压缩包 61 | 62 | .. code:: shell 63 | 64 | $ ls -l | grep demo 65 | drwxr-xr-x 3 MING staff 96 5 4 12:44 demo 66 | 67 | # 压缩 demo 文件夹为 demo.zip 68 | $ python3 -m zipfile -c demo.zip demo 69 | 70 | $ ls -l | grep demo 71 | drwxr-xr-x 3 MING staff 96 5 4 12:44 demo 72 | -rw-r--r-- 1 MING staff 74890 5 4 12:55 demo.zip 73 | 74 | 解压一个 zip 格式的压缩包 75 | 76 | .. code:: shell 77 | 78 | $ rm -rf demo 79 | 80 | $ ls -l | grep demo 81 | -rw-r--r-- 1 MING staff 74890 5 4 12:55 demo.zip 82 | 83 | $ python3 -m zipfile -e demo.zip demo 84 | 85 | $ ls -l | grep demo 86 | drwxr-xr-x 3 MING staff 96 5 4 12:57 demo 87 | -rw-r--r-- 1 MING staff 74890 5 4 12:55 demo.zip 88 | -------------------------------------------------------------------------------- /source/c02/c02_20.md: -------------------------------------------------------------------------------- 1 | # 2.20 快速编辑 Python 脚本 2 | 3 | ![](http://image.iswbm.com/20200804124133.png) 4 | 5 | pyc是一种二进制文件,是由py文件经过编译后,生成的文件,是一种byte code,py文件变成pyc文件后,加载的速度会有所提高。因此在一些场景下,可以预先编译成 pyc 文件,来提高加载速度。 6 | 7 | 编译的命令非常的简单,示例如下 8 | 9 | ```shell 10 | $ tree demo 11 | demo 12 | └── main.py 13 | 14 | $ python3 -O -m compileall demo 15 | Listing 'demo'... 16 | Compiling 'demo/main.py'... 17 | 18 | $ tree demo 19 | demo 20 | ├── __pycache__ 21 | │   └── main.cpython-39.opt-1.pyc 22 | └── main.py 23 | ``` 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /source/c02/c02_20.rst: -------------------------------------------------------------------------------- 1 | 2.20 快速编辑 Python 脚本 2 | ========================= 3 | 4 | .. image:: http://image.iswbm.com/20200804124133.png 5 | 6 | pyc是一种二进制文件,是由py文件经过编译后,生成的文件,是一种byte 7 | code,py文件变成pyc文件后,加载的速度会有所提高。因此在一些场景下,可以预先编译成 8 | pyc 文件,来提高加载速度。 9 | 10 | 编译的命令非常的简单,示例如下 11 | 12 | .. code:: shell 13 | 14 | $ tree demo 15 | demo 16 | └── main.py 17 | 18 | $ python3 -O -m compileall demo 19 | Listing 'demo'... 20 | Compiling 'demo/main.py'... 21 | 22 | $ tree demo 23 | demo 24 | ├── __pycache__ 25 | │   └── main.cpython-39.opt-1.pyc 26 | └── main.py 27 | -------------------------------------------------------------------------------- /source/c02/c02_21.md: -------------------------------------------------------------------------------- 1 | # 2.21 使用自带的 telnet 端口检测工具 2 | 3 | ![](http://image.iswbm.com/20200804124133.png) 4 | 5 | 若你想检测指定的机器上有没有开放某端口,但本机并没有安装 telnet 工具,不如尝试一下 python 自带的 telnetlib 库,亦可实现你的需求。 6 | 7 | 检查 192.168.56.200 上的 22 端口有没有开放。 8 | 9 | ```shell 10 | $ python3 -m telnetlib -d 192.168.56.200 22 11 | Telnet(192.168.56.200,22): recv b'SSH-2.0-OpenSSH_7.4\r\n' 12 | SSH-2.0-OpenSSH_7.4 13 | 14 | Telnet(192.168.56.200,22): send b'\n' 15 | Telnet(192.168.56.200,22): recv b'Protocol mismatch.\n' 16 | Protocol mismatch. 17 | Telnet(192.168.56.200,22): recv b'' 18 | *** Connection closed by remote host *** 19 | ``` 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /source/c02/c02_21.rst: -------------------------------------------------------------------------------- 1 | 2.21 使用自带的 telnet 端口检测工具 2 | =================================== 3 | 4 | .. image:: http://image.iswbm.com/20200804124133.png 5 | 6 | 若你想检测指定的机器上有没有开放某端口,但本机并没有安装 telnet 7 | 工具,不如尝试一下 python 自带的 telnetlib 库,亦可实现你的需求。 8 | 9 | 检查 192.168.56.200 上的 22 端口有没有开放。 10 | 11 | .. code:: shell 12 | 13 | $ python3 -m telnetlib -d 192.168.56.200 22 14 | Telnet(192.168.56.200,22): recv b'SSH-2.0-OpenSSH_7.4\r\n' 15 | SSH-2.0-OpenSSH_7.4 16 | 17 | Telnet(192.168.56.200,22): send b'\n' 18 | Telnet(192.168.56.200,22): recv b'Protocol mismatch.\n' 19 | Protocol mismatch. 20 | Telnet(192.168.56.200,22): recv b'' 21 | *** Connection closed by remote host *** 22 | -------------------------------------------------------------------------------- /source/c02/c02_22.md: -------------------------------------------------------------------------------- 1 | # 2.22 快速将项目打包成应用程序 2 | 3 | ![](http://image.iswbm.com/20200804124133.png) 4 | 5 | 假设我当前有一个 demo 项目,目录结构树及相关文件的的代码如下 6 | 7 | ![](http://image.iswbm.com/20210504133550.png) 8 | 9 | 现在我使用如下命令,将该项目进行打包,其中 demo 是项目的文件夹名,`main:main` 中的第一个 main 指的 `main.py`,而第二个 main 指的是 `main` 函数 10 | 11 | ```shell 12 | $ python3 -m zipapp demo -m "main:main" 13 | ``` 14 | 15 | 执行完成后,会生成一个 `demo.pyz` 文件,可直接执行它。 16 | 17 | 具体演示过程如下 18 | 19 | ![](http://image.iswbm.com/20210504133711.png) 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /source/c02/c02_22.rst: -------------------------------------------------------------------------------- 1 | 2.22 快速将项目打包成应用程序 2 | ============================= 3 | 4 | .. image:: http://image.iswbm.com/20200804124133.png 5 | 6 | 假设我当前有一个 demo 项目,目录结构树及相关文件的的代码如下 7 | 8 | .. image:: http://image.iswbm.com/20210504133550.png 9 | 10 | 现在我使用如下命令,将该项目进行打包,其中 demo 11 | 是项目的文件夹名,\ ``main:main`` 中的第一个 main 指的 12 | ``main.py``\ ,而第二个 main 指的是 ``main`` 函数 13 | 14 | .. code:: shell 15 | 16 | $ python3 -m zipapp demo -m "main:main" 17 | 18 | 执行完成后,会生成一个 ``demo.pyz`` 文件,可直接执行它。 19 | 20 | 具体演示过程如下 21 | 22 | .. image:: http://image.iswbm.com/20210504133711.png 23 | -------------------------------------------------------------------------------- /source/c02/c02_23.md: -------------------------------------------------------------------------------- 1 | # 2.23 快速打印函数的调用栈 2 | 3 | ![](http://image.iswbm.com/20200804124133.png) 4 | 5 | 在使用pdb时,手动打印调用栈 6 | 7 | ```python 8 | import traceback 9 | traceback.print_stack(file=sys.stdout) 10 | ``` 11 | 12 | 或者直接使用 `where` (更简单的直接一个 `w`):https://www.codenong.com/1156023/ 13 | 14 | ```python 15 | (Pdb) where 16 | /usr/lib/python2.7/site-packages/eventlet/greenpool.py(82)_spawn_n_impl() 17 | -> func(*args, **kwargs) 18 | /usr/lib/python2.7/site-packages/eventlet/wsgi.py(719)process_request() 19 | -> proto.__init__(sock, address, self) 20 | /usr/lib64/python2.7/SocketServer.py(649)__init__() 21 | -> self.handle() 22 | /usr/lib64/python2.7/BaseHTTPServer.py(340)handle() 23 | -> self.handle_one_request() 24 | /usr/lib/python2.7/site-packages/eventlet/wsgi.py(384)handle_one_request() 25 | -> self.handle_one_response() 26 | /usr/lib/python2.7/site-packages/eventlet/wsgi.py(481)handle_one_response() 27 | ``` 28 | 29 | -------------------------------------------------------------------------------- /source/c02/c02_23.rst: -------------------------------------------------------------------------------- 1 | 2.23 快速打印函数的调用栈 2 | ========================= 3 | 4 | .. image:: http://image.iswbm.com/20200804124133.png 5 | 6 | 在使用pdb时,手动打印调用栈 7 | 8 | .. code:: python 9 | 10 | import traceback 11 | traceback.print_stack(file=sys.stdout) 12 | 13 | 或者直接使用 ``where`` (更简单的直接一个 14 | ``w``\ ):https://www.codenong.com/1156023/ 15 | 16 | .. code:: python 17 | 18 | (Pdb) where 19 | /usr/lib/python2.7/site-packages/eventlet/greenpool.py(82)_spawn_n_impl() 20 | -> func(*args, **kwargs) 21 | /usr/lib/python2.7/site-packages/eventlet/wsgi.py(719)process_request() 22 | -> proto.__init__(sock, address, self) 23 | /usr/lib64/python2.7/SocketServer.py(649)__init__() 24 | -> self.handle() 25 | /usr/lib64/python2.7/BaseHTTPServer.py(340)handle() 26 | -> self.handle_one_request() 27 | /usr/lib/python2.7/site-packages/eventlet/wsgi.py(384)handle_one_request() 28 | -> self.handle_one_response() 29 | /usr/lib/python2.7/site-packages/eventlet/wsgi.py(481)handle_one_response() 30 | -------------------------------------------------------------------------------- /source/c03/c03_11.md: -------------------------------------------------------------------------------- 1 | # 3.11 Python 读取文件的六种方式 2 | 3 | ![](http://image.iswbm.com/20200804124133.png) 4 | 5 | ## 第一种:使用 open 6 | 7 | 常规操作 8 | 9 | ```python 10 | with open('data.txt') as fp: 11 | content = fp.readlines() 12 | ``` 13 | 14 | ## 第二种:使用 fileinput 15 | 16 | 使用内置库 fileinput 17 | 18 | ```python 19 | import fileinput 20 | 21 | with fileinput.input(files=('data.txt',)) as file: 22 | content = [line for line in file] 23 | ``` 24 | 25 | ## 第三种:使用 filecache 26 | 27 | 使用内置库 filecache,你可以用它来指定读取具体某一行,或者某几行,不指定就读取全部行。 28 | 29 | ```python 30 | import linecache 31 | 32 | content = linecache.getlines('werobot.toml') 33 | ``` 34 | 35 | ## 第四种:使用 codecs 36 | 37 | 使用 `codecs.open` 来读取 38 | 39 | ```python 40 | import codecs 41 | file=codecs.open("README.md", 'r') 42 | file.read() 43 | ``` 44 | 45 | 如果你还在使用 Python2,那么它可以帮你处理掉 Python 2 下写文件时一些编码错误,一般的建议是: 46 | 47 | - 在 Python 3 下写文件,直接使用 open 48 | - 在 Python 2 下写文件,推荐使用 codecs.open,特别是有中文的情况下 49 | - 如果希望代码同时兼容Python2和Python3,那么也推荐用codecs.open 50 | 51 | ## 第五种:使用 io 模块 52 | 53 | 使用 io 模块的 open 函数 54 | 55 | ```python 56 | import io 57 | file=io.open("README.md") 58 | file.read() 59 | ``` 60 | 61 | 经朋友提醒,我才发现 io.open 和 open 是同一个函数 62 | 63 | ```python 64 | Python 3.9.2 (default, Feb 28 2021, 17:03:44) 65 | [GCC 10.2.1 20210110] on linux 66 | Type "help", "copyright", "credits" or "license" for more information. 67 | >>> import os 68 | >>> (open1:=open) is (open2:=os.open) 69 | False 70 | >>> import io 71 | >>> (open3:=open) is (open3:=io.open) 72 | True 73 | ``` 74 | 75 | ## 第六种:使用 os 模块 76 | 77 | os 模块也自带了 open 函数,直接操作的是底层的 I/O 流,操作的时候是最麻烦的 78 | 79 | ```python 80 | >>> import os 81 | >>> fp = os.open("hello.txt", os.O_RDONLY) 82 | >>> os.read(fp, 12) 83 | b'hello, world' 84 | >>> os.close(fp) 85 | ``` 86 | 87 | 88 | 89 | -------------------------------------------------------------------------------- /source/c03/c03_11.rst: -------------------------------------------------------------------------------- 1 | 3.11 Python 读取文件的六种方式 2 | ============================== 3 | 4 | .. image:: http://image.iswbm.com/20200804124133.png 5 | 6 | 第一种:使用 open 7 | ----------------- 8 | 9 | 常规操作 10 | 11 | .. code:: python 12 | 13 | with open('data.txt') as fp: 14 | content = fp.readlines() 15 | 16 | 第二种:使用 fileinput 17 | ---------------------- 18 | 19 | 使用内置库 fileinput 20 | 21 | .. code:: python 22 | 23 | import fileinput 24 | 25 | with fileinput.input(files=('data.txt',)) as file: 26 | content = [line for line in file] 27 | 28 | 第三种:使用 filecache 29 | ---------------------- 30 | 31 | 使用内置库 32 | filecache,你可以用它来指定读取具体某一行,或者某几行,不指定就读取全部行。 33 | 34 | .. code:: python 35 | 36 | import linecache 37 | 38 | content = linecache.getlines('werobot.toml') 39 | 40 | 第四种:使用 codecs 41 | ------------------- 42 | 43 | 使用 ``codecs.open`` 来读取 44 | 45 | .. code:: python 46 | 47 | import codecs 48 | file=codecs.open("README.md", 'r') 49 | file.read() 50 | 51 | 如果你还在使用 Python2,那么它可以帮你处理掉 Python 2 52 | 下写文件时一些编码错误,一般的建议是: 53 | 54 | - 在 Python 3 下写文件,直接使用 open 55 | - 在 Python 2 下写文件,推荐使用 codecs.open,特别是有中文的情况下 56 | - 如果希望代码同时兼容Python2和Python3,那么也推荐用codecs.open 57 | 58 | 第五种:使用 io 模块 59 | -------------------- 60 | 61 | 使用 io 模块的 open 函数 62 | 63 | .. code:: python 64 | 65 | import io 66 | file=io.open("README.md") 67 | file.read() 68 | 69 | 经朋友提醒,我才发现 io.open 和 open 是同一个函数 70 | 71 | .. code:: python 72 | 73 | Python 3.9.2 (default, Feb 28 2021, 17:03:44) 74 | [GCC 10.2.1 20210110] on linux 75 | Type "help", "copyright", "credits" or "license" for more information. 76 | >>> import os 77 | >>> (open1:=open) is (open2:=os.open) 78 | False 79 | >>> import io 80 | >>> (open3:=open) is (open3:=io.open) 81 | True 82 | 83 | 第六种:使用 os 模块 84 | -------------------- 85 | 86 | os 模块也自带了 open 函数,直接操作的是底层的 I/O 87 | 流,操作的时候是最麻烦的 88 | 89 | .. code:: python 90 | 91 | >>> import os 92 | >>> fp = os.open("hello.txt", os.O_RDONLY) 93 | >>> os.read(fp, 12) 94 | b'hello, world' 95 | >>> os.close(fp) 96 | -------------------------------------------------------------------------------- /source/c03/c03_13.md: -------------------------------------------------------------------------------- 1 | # 3.13 创造 "新语法" 的黑科技 2 | 3 | ![](http://image.iswbm.com/20200804124133.png) 4 | 5 | 通常我们遍历一个元素为 5-10 的数组,会这么写 6 | 7 | ```python 8 | >>> for i in range(5,11): 9 | ... print(i) 10 | ... 11 | 5 12 | 6 13 | 7 14 | 8 15 | 9 16 | 10 17 | ``` 18 | 19 | 写法虽然简单,但总有一种不够直观的样子。 20 | 21 | 今天介绍一个黑科技方法,可以让这种写法更加直观,不够请谨慎使用,因为这个方法有点 "逆天",会让人误以为是 Python 又出了什么新语法。 22 | 23 | 最后的效果是这样子的。 24 | 25 | ```python 26 | >>> for i in 5|到|10: 27 | ... print(i) 28 | ... 29 | 5 30 | 6 31 | 7 32 | 8 33 | 9 34 | ``` 35 | 36 | `|到|` 很容易让人误以为是什么新的语法? 37 | 38 | 其实不是的,`|到|` 应该分为 `|` 、`到`、`|` 这三个部分,下面我们一一讲解。 39 | 40 | 第一和第三的 `|` 是同个意思,它就是一个普通的运算符,通常我们使用 `or` 关键字来替代它,导致很多人对这个符号比较陌生。 41 | 42 | 这边是一个简单的例子,当两边 `|` 两边有一边为 True 就会返回 True 43 | 44 | ``` python 45 | >>> if True | False: 46 | ... print("ok") 47 | ... 48 | ok 49 | >>> 50 | >>> if False | False: 51 | ... print("ok") 52 | ... 53 | >>> 54 | ``` 55 | 56 | 基本上所有的运算符都可以通过魔法方法来重新定义运算符的逻辑,这个过程叫做运算符重载, `|` 也不例外。 57 | 58 | 控制 `|` 的魔法方法是 `__or__` 和 `__xor__` 59 | 60 | 讲完了第一个和第三个字符,现在说说第二个字符 `到` 61 | 62 | `到` 实际上是一个类的实例,上面为了神秘,我没有事先给出完整代码 63 | 64 | 定义一个 Magic 的类,用于改变 range 的 `|` 方法 65 | 66 | ```python 67 | >>> class Magic(object): 68 | ... def __init__(self, func): 69 | ... self.func = func 70 | ... def __or__(self, other): 71 | ... return self.func(other) 72 | ... def __ror__(self, other): 73 | ... self.func = partial(self.func, other) 74 | ... return self 75 | ... 76 | >>> 77 | >>> 到 = Magic(range) 78 | ``` 79 | 80 | 总结一下,这三者如何起作用的? 81 | 82 | - `到` 是 Magic 类的一个实例 83 | - `__or__` 定义的是 `到` 实例右侧遇到 `|` 的行为 84 | - `__xor__` 定义的是 `到` 实例左侧遇到 `|` 的行为 85 | 86 | -------------------------------------------------------------------------------- /source/c03/c03_13.rst: -------------------------------------------------------------------------------- 1 | 3.13 创造 “新语法” 的黑科技 2 | =========================== 3 | 4 | .. image:: http://image.iswbm.com/20200804124133.png 5 | 6 | 通常我们遍历一个元素为 5-10 的数组,会这么写 7 | 8 | .. code:: python 9 | 10 | >>> for i in range(5,11): 11 | ... print(i) 12 | ... 13 | 5 14 | 6 15 | 7 16 | 8 17 | 9 18 | 10 19 | 20 | 写法虽然简单,但总有一种不够直观的样子。 21 | 22 | 今天介绍一个黑科技方法,可以让这种写法更加直观,不够请谨慎使用,因为这个方法有点 23 | “逆天”,会让人误以为是 Python 又出了什么新语法。 24 | 25 | 最后的效果是这样子的。 26 | 27 | .. code:: python 28 | 29 | >>> for i in 5|到|10: 30 | ... print(i) 31 | ... 32 | 5 33 | 6 34 | 7 35 | 8 36 | 9 37 | 38 | ``|到|`` 很容易让人误以为是什么新的语法? 39 | 40 | 其实不是的,\ ``|到|`` 应该分为 ``|`` 、\ ``到``\ 、\ ``|`` 41 | 这三个部分,下面我们一一讲解。 42 | 43 | 第一和第三的 ``|`` 是同个意思,它就是一个普通的运算符,通常我们使用 44 | ``or`` 关键字来替代它,导致很多人对这个符号比较陌生。 45 | 46 | 这边是一个简单的例子,当两边 ``|`` 两边有一边为 True 就会返回 True 47 | 48 | .. code::  python 49 | 50 | >>> if True | False: 51 | ... print("ok") 52 | ... 53 | ok 54 | >>> 55 | >>> if False | False: 56 | ... print("ok") 57 | ... 58 | >>> 59 | 60 | 基本上所有的运算符都可以通过魔法方法来重新定义运算符的逻辑,这个过程叫做运算符重载, 61 | ``|`` 也不例外。 62 | 63 | 控制 ``|`` 的魔法方法是 ``__or__`` 和 ``__xor__`` 64 | 65 | 讲完了第一个和第三个字符,现在说说第二个字符 ``到`` 66 | 67 | ``到`` 实际上是一个类的实例,上面为了神秘,我没有事先给出完整代码 68 | 69 | 定义一个 Magic 的类,用于改变 range 的 ``|`` 方法 70 | 71 | .. code:: python 72 | 73 | >>> class Magic(object): 74 | ... def __init__(self, func): 75 | ... self.func = func 76 | ... def __or__(self, other): 77 | ... return self.func(other) 78 | ... def __ror__(self, other): 79 | ... self.func = partial(self.func, other) 80 | ... return self 81 | ... 82 | >>> 83 | >>> 到 = Magic(range) 84 | 85 | 总结一下,这三者如何起作用的? 86 | 87 | - ``到`` 是 Magic 类的一个实例 88 | - ``__or__`` 定义的是 ``到`` 实例右侧遇到 ``|`` 的行为 89 | - ``__xor__`` 定义的是 ``到`` 实例左侧遇到 ``|`` 的行为 90 | -------------------------------------------------------------------------------- /source/c05/c05_01.md: -------------------------------------------------------------------------------- 1 | # 5.1 嵌套上下文管理的另类写法 2 | ![](http://image.iswbm.com/20200804124133.png) 3 | 4 | 当我们要写一个嵌套的上下文管理器时,可能会这样写 5 | 6 | ```python 7 | import contextlib 8 | 9 | @contextlib.contextmanager 10 | def test_context(name): 11 | print('enter, my name is {}'.format(name)) 12 | 13 | yield 14 | 15 | print('exit, my name is {}'.format(name)) 16 | 17 | with test_context('aaa'): 18 | with test_context('bbb'): 19 | print('========== in main ============') 20 | ``` 21 | 22 | 输出结果如下 23 | 24 | ```python 25 | enter, my name is aaa 26 | enter, my name is bbb 27 | ========== in main ============ 28 | exit, my name is bbb 29 | exit, my name is aaa 30 | ``` 31 | 32 | 除此之外,你可知道,还有另一种嵌套写法 33 | 34 | ```python 35 | with test_context('aaa'), test_context('bbb'): 36 | print('========== in main ============') 37 | ``` 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /source/c05/c05_01.rst: -------------------------------------------------------------------------------- 1 | 5.1 嵌套上下文管理的另类写法 2 | ============================ 3 | 4 | .. image:: http://image.iswbm.com/20200804124133.png 5 | 6 | 当我们要写一个嵌套的上下文管理器时,可能会这样写 7 | 8 | .. code:: python 9 | 10 | import contextlib 11 | 12 | @contextlib.contextmanager 13 | def test_context(name): 14 | print('enter, my name is {}'.format(name)) 15 | 16 | yield 17 | 18 | print('exit, my name is {}'.format(name)) 19 | 20 | with test_context('aaa'): 21 | with test_context('bbb'): 22 | print('========== in main ============') 23 | 24 | 输出结果如下 25 | 26 | .. code:: python 27 | 28 | enter, my name is aaa 29 | enter, my name is bbb 30 | ========== in main ============ 31 | exit, my name is bbb 32 | exit, my name is aaa 33 | 34 | 除此之外,你可知道,还有另一种嵌套写法 35 | 36 | .. code:: python 37 | 38 | with test_context('aaa'), test_context('bbb'): 39 | print('========== in main ============') 40 | -------------------------------------------------------------------------------- /source/c05/c05_02.md: -------------------------------------------------------------------------------- 1 | # 5.2 将嵌套 for 循环写成单行 2 | ![](http://image.iswbm.com/20200804124133.png) 3 | 4 | 我们经常会写如下这种嵌套的 for 循环代码 5 | 6 | ```python 7 | list1 = range(1,3) 8 | list2 = range(4,6) 9 | list3 = range(7,9) 10 | for item1 in list1: 11 | for item2 in list2: 12 | for item3 in list3: 13 | print(item1+item2+item3) 14 | ``` 15 | 16 | 这里仅仅是三个 for 循环,在实际编码中,有可能会有更多层。 17 | 18 | 这样的代码,可读性非常的差,很多人不想这么写,可又没有更好的写法。 19 | 20 | 这里介绍一种我常用的写法,使用 itertools 这个库来实现更优雅易读的代码。 21 | 22 | ```python 23 | from itertools import product 24 | list1 = range(1,3) 25 | list2 = range(4,6) 26 | list3 = range(7,9) 27 | for item1,item2,item3 in product(list1, list2, list3): 28 | print(item1+item2+item3) 29 | ``` 30 | 31 | 输出如下 32 | 33 | ```shell 34 | $ python demo.py 35 | 12 36 | 13 37 | 13 38 | 14 39 | 13 40 | 14 41 | 14 42 | 15 43 | ``` 44 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /source/c05/c05_02.rst: -------------------------------------------------------------------------------- 1 | 5.2 将嵌套 for 循环写成单行 2 | =========================== 3 | 4 | .. image:: http://image.iswbm.com/20200804124133.png 5 | 6 | 我们经常会写如下这种嵌套的 for 循环代码 7 | 8 | .. code:: python 9 | 10 | list1 = range(1,3) 11 | list2 = range(4,6) 12 | list3 = range(7,9) 13 | for item1 in list1: 14 | for item2 in list2: 15 | for item3 in list3: 16 | print(item1+item2+item3) 17 | 18 | 这里仅仅是三个 for 循环,在实际编码中,有可能会有更多层。 19 | 20 | 这样的代码,可读性非常的差,很多人不想这么写,可又没有更好的写法。 21 | 22 | 这里介绍一种我常用的写法,使用 itertools 这个库来实现更优雅易读的代码。 23 | 24 | .. code:: python 25 | 26 | from itertools import product 27 | list1 = range(1,3) 28 | list2 = range(4,6) 29 | list3 = range(7,9) 30 | for item1,item2,item3 in product(list1, list2, list3): 31 | print(item1+item2+item3) 32 | 33 | 输出如下 34 | 35 | .. code:: shell 36 | 37 | $ python demo.py 38 | 12 39 | 13 40 | 13 41 | 14 42 | 13 43 | 14 44 | 14 45 | 15 46 | -------------------------------------------------------------------------------- /source/c05/c05_03.md: -------------------------------------------------------------------------------- 1 | # 5.3 单行实现 for 死循环如何写? 2 | ![](http://image.iswbm.com/20200804124133.png) 3 | 4 | 如果让你在不借助 while ,只使用 for 来写一个死循环? 5 | 6 | **你会写吗?** 7 | 8 | **如果你还说简单,你可以自己试一下。** 9 | 10 | ... 11 | 12 | 如果你尝试后,仍然写不出来,那我给出自己的做法。 13 | 14 | ```python 15 | for i in iter(int, 1):pass 16 | ``` 17 | 18 | 19 | 20 | **是不是傻了?iter 还有这种用法?这为啥是个死循环?** 21 | 22 | 关于这个问题,你如果看中文网站,可能找不到相关资料。 23 | 24 | 还好你可以通过 IDE 看py源码里的注释内容,介绍了很详细的使用方法。 25 | 26 | 原来iter有两种使用方法。 27 | 28 | - 通常我们的认知是第一种,将一个列表转化为一个迭代器。 29 | 30 | - 而第二种方法,他接收一个 callable对象,和一个sentinel 参数。第一个对象会一直运行,直到它返回 sentinel 值才结束。 31 | 32 | 那`int` 呢? 33 | 34 | 这又是一个知识点,int 是一个内建方法。通过看注释,可以看出它是有默认值0的。你可以在console 模式下输入 `int()` 看看是不是返回0。 35 | 36 | 由于int() 永远返回0,永远返回不了1,所以这个 for 循环会没有终点。一直运行下去。 37 | 38 | 39 | -------------------------------------------------------------------------------- /source/c05/c05_03.rst: -------------------------------------------------------------------------------- 1 | 5.3 单行实现 for 死循环如何写? 2 | =============================== 3 | 4 | .. image:: http://image.iswbm.com/20200804124133.png 5 | 6 | 如果让你在不借助 while ,只使用 for 来写一个死循环? 7 | 8 | **你会写吗?** 9 | 10 | **如果你还说简单,你可以自己试一下。** 11 | 12 | … 13 | 14 | 如果你尝试后,仍然写不出来,那我给出自己的做法。 15 | 16 | .. code:: python 17 | 18 | for i in iter(int, 1):pass 19 | 20 | **是不是傻了?iter 还有这种用法?这为啥是个死循环?** 21 | 22 | 关于这个问题,你如果看中文网站,可能找不到相关资料。 23 | 24 | 还好你可以通过 IDE 看py源码里的注释内容,介绍了很详细的使用方法。 25 | 26 | 原来iter有两种使用方法。 27 | 28 | - 通常我们的认知是第一种,将一个列表转化为一个迭代器。 29 | 30 | - 而第二种方法,他接收一个 callable对象,和一个sentinel 31 | 参数。第一个对象会一直运行,直到它返回 sentinel 值才结束。 32 | 33 | 那\ ``int`` 呢? 34 | 35 | 这又是一个知识点,int 36 | 是一个内建方法。通过看注释,可以看出它是有默认值0的。你可以在console 37 | 模式下输入 ``int()`` 看看是不是返回0。 38 | 39 | 由于int() 永远返回0,永远返回不了1,所以这个 for 40 | 循环会没有终点。一直运行下去。 41 | -------------------------------------------------------------------------------- /source/c05/c05_04.md: -------------------------------------------------------------------------------- 1 | # 5.4 如何关闭异常自动关联上下文? 2 | ![](http://image.iswbm.com/20200804124133.png) 3 | 4 | 当你在处理异常时,由于处理不当或者其他问题,再次抛出另一个异常时,往外抛出的异常也会携带原始的异常信息。 5 | 6 | 就像这样子。 7 | 8 | ```python 9 | try: 10 | print(1 / 0) 11 | except Exception as exc: 12 | raise RuntimeError("Something bad happened") 13 | ``` 14 | 15 | 从输出可以看到两个异常信息 16 | 17 | ```python 18 | Traceback (most recent call last): 19 | File "demo.py", line 2, in 20 | print(1 / 0) 21 | ZeroDivisionError: division by zero 22 | 23 | During handling of the above exception, another exception occurred: 24 | 25 | Traceback (most recent call last): 26 | File "demo.py", line 4, in 27 | raise RuntimeError("Something bad happened") 28 | RuntimeError: Something bad happened 29 | ``` 30 | 31 | 如果在异常处理程序或 finally 块中引发异常,默认情况下,异常机制会隐式工作会将先前的异常附加为新异常的 `__context__`属性。这就是 Python 默认开启的自动关联异常上下文。 32 | 33 | 如果你想自己控制这个上下文,可以加个 from 关键字(`from` 语法会有个限制,就是第二个表达式必须是另一个异常类或实例。),来表明你的新异常是直接由哪个异常引起的。 34 | 35 | ```python 36 | try: 37 | print(1 / 0) 38 | except Exception as exc: 39 | raise RuntimeError("Something bad happened") from exc 40 | ``` 41 | 42 | 输出如下 43 | 44 | ```python 45 | Traceback (most recent call last): 46 | File "demo.py", line 2, in 47 | print(1 / 0) 48 | ZeroDivisionError: division by zero 49 | 50 | The above exception was the direct cause of the following exception: 51 | 52 | Traceback (most recent call last): 53 | File "demo.py", line 4, in 54 | raise RuntimeError("Something bad happened") from exc 55 | RuntimeError: Something bad happened 56 | ``` 57 | 58 | 59 | 60 | 当然,你也可以通过`with_traceback()`方法为异常设置上下文`__context__`属性,这也能在`traceback`更好地显示异常信息。 61 | 62 | ```python 63 | try: 64 | print(1 / 0) 65 | except Exception as exc: 66 | raise RuntimeError("bad thing").with_traceback(exc) 67 | ``` 68 | 69 | 70 | 71 | 最后,如果我想彻底关闭这个自动关联异常上下文的机制?有什么办法呢? 72 | 73 | 可以使用 `raise...from None`,从下面的例子上看,已经没有了原始异常 74 | 75 | ```python 76 | $ cat demo.py 77 | try: 78 | print(1 / 0) 79 | except Exception as exc: 80 | raise RuntimeError("Something bad happened") from None 81 | $ 82 | $ python demo.py 83 | Traceback (most recent call last): 84 | File "demo.py", line 4, in 85 | raise RuntimeError("Something bad happened") from None 86 | RuntimeError: Something bad happened 87 | (PythonCodingTime) 88 | ``` 89 | 90 | 91 | 92 | 93 | -------------------------------------------------------------------------------- /source/c05/c05_05.md: -------------------------------------------------------------------------------- 1 | # 5.5 自带的缓存机制不用白不用 2 | ![](http://image.iswbm.com/20200804124133.png) 3 | 4 | 缓存是一种将定量数据加以保存,以备迎合后续获取需求的处理方式,旨在加快数据获取的速度。 5 | 6 | 数据的生成过程可能需要经过计算,规整,远程获取等操作,如果是同一份数据需要多次使用,每次都重新生成会大大浪费时间。所以,如果将计算或者远程请求等操作获得的数据缓存下来,会加快后续的数据获取需求。 7 | 8 | 为了实现这个需求,Python 3.2 + 中给我们提供了一个机制,可以很方便地实现,而不需要你去写这样的逻辑代码。 9 | 10 | 这个机制实现于 functool 模块中的 lru_cache 装饰器。 11 | 12 | ```python 13 | @functools.lru_cache(maxsize=None, typed=False) 14 | ``` 15 | 16 | 参数解读: 17 | 18 | - maxsize:最多可以缓存多少个此函数的调用结果,如果为None,则无限制,设置为 2 的幂时,性能最佳 19 | - typed:若为 True,则不同参数类型的调用将分别缓存。 20 | 21 | 举个例子 22 | 23 | ```python 24 | from functools import lru_cache 25 | 26 | @lru_cache(None) 27 | def add(x, y): 28 | print("calculating: %s + %s" % (x, y)) 29 | return x + y 30 | 31 | print(add(1, 2)) 32 | print(add(1, 2)) 33 | print(add(2, 3)) 34 | ``` 35 | 36 | 输出如下,可以看到第二次调用并没有真正地执行函数体,而是直接返回缓存里的结果 37 | 38 | ```shell 39 | calculating: 1 + 2 40 | 3 41 | 3 42 | calculating: 2 + 3 43 | 5 44 | ``` 45 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /source/c05/c05_05.rst: -------------------------------------------------------------------------------- 1 | 5.5 自带的缓存机制不用白不用 2 | ============================ 3 | 4 | .. image:: http://image.iswbm.com/20200804124133.png 5 | 6 | 缓存是一种将定量数据加以保存,以备迎合后续获取需求的处理方式,旨在加快数据获取的速度。 7 | 8 | 数据的生成过程可能需要经过计算,规整,远程获取等操作,如果是同一份数据需要多次使用,每次都重新生成会大大浪费时间。所以,如果将计算或者远程请求等操作获得的数据缓存下来,会加快后续的数据获取需求。 9 | 10 | 为了实现这个需求,Python 3.2 + 11 | 中给我们提供了一个机制,可以很方便地实现,而不需要你去写这样的逻辑代码。 12 | 13 | 这个机制实现于 functool 模块中的 lru_cache 装饰器。 14 | 15 | .. code:: python 16 | 17 | @functools.lru_cache(maxsize=None, typed=False) 18 | 19 | 参数解读: 20 | 21 | - maxsize:最多可以缓存多少个此函数的调用结果,如果为None,则无限制,设置为 22 | 2 的幂时,性能最佳 23 | - typed:若为 True,则不同参数类型的调用将分别缓存。 24 | 25 | 举个例子 26 | 27 | .. code:: python 28 | 29 | from functools import lru_cache 30 | 31 | @lru_cache(None) 32 | def add(x, y): 33 | print("calculating: %s + %s" % (x, y)) 34 | return x + y 35 | 36 | print(add(1, 2)) 37 | print(add(1, 2)) 38 | print(add(2, 3)) 39 | 40 | 输出如下,可以看到第二次调用并没有真正地执行函数体,而是直接返回缓存里的结果 41 | 42 | .. code:: shell 43 | 44 | calculating: 1 + 2 45 | 3 46 | 3 47 | calculating: 2 + 3 48 | 5 49 | -------------------------------------------------------------------------------- /source/c05/c05_06.md: -------------------------------------------------------------------------------- 1 | # 5.6 如何流式读取数G超大文件 2 | ![](http://image.iswbm.com/20200804124133.png) 3 | 4 | 使用 with...open... 可以从一个文件中读取数据,这是所有 Python 开发者都非常熟悉的操作。 5 | 6 | 但是如果你使用不当,也会带来很大的麻烦。 7 | 8 | 比如当你使用了 read 函数,其实 Python 会将文件的内容一次性地全部载入内存中,如果文件有 10 个G甚至更多,那么你的电脑就要消耗的内存非常巨大。 9 | 10 | ```python 11 | # 一次性读取 12 | with open("big_file.txt", "r") as fp: 13 | content = fp.read() 14 | ``` 15 | 16 | 对于这个问题,你也许会想到使用 readline 去做一个生成器来逐行返回。 17 | 18 | ```python 19 | def read_from_file(filename): 20 | with open(filename, "r") as fp: 21 | yield fp.readline() 22 | ``` 23 | 24 | 可如果这个文件内容就一行呢,一行就 10个G,其实你还是会一次性读取全部内容。 25 | 26 | 最优雅的解决方法是,在使用 read 方法时,指定每次只读取固定大小的内容,比如下面的代码中,每次只读取 8kb 返回。 27 | 28 | ```python 29 | def read_from_file(filename, block_size = 1024 * 8): 30 | with open(filename, "r") as fp: 31 | while True: 32 | chunk = fp.read(block_size) 33 | if not chunk: 34 | break 35 | 36 | yield chunk 37 | ``` 38 | 39 | 上面的代码,功能上已经没有问题了,但是代码看起来还是有些臃肿。 40 | 41 | 借助偏函数 和 iter 函数可以优化一下代码 42 | 43 | ```python 44 | from functools import partial 45 | 46 | def read_from_file(filename, block_size = 1024 * 8): 47 | with open(filename, "r") as fp: 48 | for chunk in iter(partial(fp.read, block_size), ""): 49 | yield chunk 50 | ``` 51 | 52 | 如果你使用的是 Python 3.8 +,还有一种更直观、易于理解的写法,既不用使用偏函数,也不用掌握 iter 这种另类的用法。而只要用利用 海象运算符就可以,具体代码如下 53 | 54 | ```python 55 | def read_from_file(filename, block_size = 1024 * 8): 56 | with open(filename, "r") as fp: 57 | while chunk := fp.read(block_size): 58 | yield chunk 59 | ``` 60 | 61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /source/c05/c05_06.rst: -------------------------------------------------------------------------------- 1 | 5.6 如何流式读取数G超大文件 2 | =========================== 3 | 4 | .. image:: http://image.iswbm.com/20200804124133.png 5 | 6 | 使用 with…open… 可以从一个文件中读取数据,这是所有 Python 7 | 开发者都非常熟悉的操作。 8 | 9 | 但是如果你使用不当,也会带来很大的麻烦。 10 | 11 | 比如当你使用了 read 函数,其实 Python 12 | 会将文件的内容一次性地全部载入内存中,如果文件有 10 13 | 个G甚至更多,那么你的电脑就要消耗的内存非常巨大。 14 | 15 | .. code:: python 16 | 17 | # 一次性读取 18 | with open("big_file.txt", "r") as fp: 19 | content = fp.read() 20 | 21 | 对于这个问题,你也许会想到使用 readline 去做一个生成器来逐行返回。 22 | 23 | .. code:: python 24 | 25 | def read_from_file(filename): 26 | with open(filename, "r") as fp: 27 | yield fp.readline() 28 | 29 | 可如果这个文件内容就一行呢,一行就 30 | 10个G,其实你还是会一次性读取全部内容。 31 | 32 | 最优雅的解决方法是,在使用 read 33 | 方法时,指定每次只读取固定大小的内容,比如下面的代码中,每次只读取 8kb 34 | 返回。 35 | 36 | .. code:: python 37 | 38 | def read_from_file(filename, block_size = 1024 * 8): 39 | with open(filename, "r") as fp: 40 | while True: 41 | chunk = fp.read(block_size) 42 | if not chunk: 43 | break 44 | 45 | yield chunk 46 | 47 | 上面的代码,功能上已经没有问题了,但是代码看起来还是有些臃肿。 48 | 49 | 借助偏函数 和 iter 函数可以优化一下代码 50 | 51 | .. code:: python 52 | 53 | from functools import partial 54 | 55 | def read_from_file(filename, block_size = 1024 * 8): 56 | with open(filename, "r") as fp: 57 | for chunk in iter(partial(fp.read, block_size), ""): 58 | yield chunk 59 | 60 | 如果你使用的是 Python 3.8 61 | +,还有一种更直观、易于理解的写法,既不用使用偏函数,也不用掌握 iter 62 | 这种另类的用法。而只要用利用 海象运算符就可以,具体代码如下 63 | 64 | .. code:: python 65 | 66 | def read_from_file(filename, block_size = 1024 * 8): 67 | with open(filename, "r") as fp: 68 | while chunk := fp.read(block_size): 69 | yield chunk 70 | -------------------------------------------------------------------------------- /source/c05/c05_07.md: -------------------------------------------------------------------------------- 1 | # 5.7 实现类似 defer 的延迟调用 2 | ![](http://image.iswbm.com/20200804124133.png) 3 | 4 | 在 Golang 中有一种延迟调用的机制,关键字是 defer,例如下面的示例 5 | 6 | ```go 7 | import "fmt" 8 | 9 | func myfunc() { 10 | fmt.Println("B") 11 | } 12 | 13 | func main() { 14 | defer myfunc() 15 | fmt.Println("A") 16 | } 17 | ``` 18 | 19 | 输出如下,myfunc 的调用会在函数返回前一步完成,即使你将 myfunc 的调用写在函数的第一行,这就是延迟调用。 20 | 21 | ``` 22 | A 23 | B 24 | ``` 25 | 26 | 那么在 Python 中否有这种机制呢? 27 | 28 | 当然也有,只不过并没有 Golang 这种简便。 29 | 30 | 在 Python 可以使用 **上下文管理器** 达到这种效果 31 | 32 | ```python 33 | import contextlib 34 | 35 | def callback(): 36 | print('B') 37 | 38 | with contextlib.ExitStack() as stack: 39 | stack.callback(callback) 40 | print('A') 41 | ``` 42 | 43 | 输出如下 44 | 45 | ``` 46 | A 47 | B 48 | ``` 49 | 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /source/c05/c05_07.rst: -------------------------------------------------------------------------------- 1 | 5.7 实现类似 defer 的延迟调用 2 | ============================= 3 | 4 | .. image:: http://image.iswbm.com/20200804124133.png 5 | 6 | 在 Golang 中有一种延迟调用的机制,关键字是 defer,例如下面的示例 7 | 8 | .. code:: go 9 | 10 | import "fmt" 11 | 12 | func myfunc() { 13 | fmt.Println("B") 14 | } 15 | 16 | func main() { 17 | defer myfunc() 18 | fmt.Println("A") 19 | } 20 | 21 | 输出如下,myfunc 的调用会在函数返回前一步完成,即使你将 myfunc 22 | 的调用写在函数的第一行,这就是延迟调用。 23 | 24 | :: 25 | 26 | A 27 | B 28 | 29 | 那么在 Python 中否有这种机制呢? 30 | 31 | 当然也有,只不过并没有 Golang 这种简便。 32 | 33 | 在 Python 可以使用 **上下文管理器** 达到这种效果 34 | 35 | .. code:: python 36 | 37 | import contextlib 38 | 39 | def callback(): 40 | print('B') 41 | 42 | with contextlib.ExitStack() as stack: 43 | stack.callback(callback) 44 | print('A') 45 | 46 | 输出如下 47 | 48 | :: 49 | 50 | A 51 | B 52 | -------------------------------------------------------------------------------- /source/c05/c05_08.md: -------------------------------------------------------------------------------- 1 | # 5.8 如何快速计算函数运行时间 2 | ![](http://image.iswbm.com/20200804124133.png) 3 | 4 | 计算一个函数的运行时间,你可能会这样子做 5 | 6 | ```python 7 | import time 8 | 9 | start = time.time() 10 | 11 | # run the function 12 | 13 | end = time.time() 14 | print(end-start) 15 | ``` 16 | 17 | 你看看你为了计算函数运行时间,写了几行代码了。 18 | ​ 19 | 有没有一种方法可以更方便地计算这个运行时间呢? 20 | ​ 21 | 有。 22 | ​ 23 | 有一个内置模块叫 timeit 24 | ​ 25 | 使用它,只用一行代码即可 26 | 27 | ```python 28 | import time 29 | import timeit 30 | 31 | def run_sleep(second): 32 | print(second) 33 | time.sleep(second) 34 | 35 | # 只用这一行 36 | print(timeit.timeit(lambda :run_sleep(2), number=5)) 37 | ``` 38 | 39 | 运行结果如下 40 | 41 | ```python 42 | 2 43 | 2 44 | 2 45 | 2 46 | 2 47 | 10.020059824 48 | ``` 49 | 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /source/c05/c05_08.rst: -------------------------------------------------------------------------------- 1 | 5.8 如何快速计算函数运行时间 2 | ============================ 3 | 4 | .. image:: http://image.iswbm.com/20200804124133.png 5 | 6 | 计算一个函数的运行时间,你可能会这样子做 7 | 8 | .. code:: python 9 | 10 | import time 11 | 12 | start = time.time() 13 | 14 | # run the function 15 | 16 | end = time.time() 17 | print(end-start) 18 | 19 | 你看看你为了计算函数运行时间,写了几行代码了。 ​ 20 | 有没有一种方法可以更方便地计算这个运行时间呢? ​ 有。 ​ 有一个内置模块叫 21 | timeit ​ 使用它,只用一行代码即可 22 | 23 | .. code:: python 24 | 25 | import time 26 | import timeit 27 | 28 | def run_sleep(second): 29 | print(second) 30 | time.sleep(second) 31 | 32 | # 只用这一行 33 | print(timeit.timeit(lambda :run_sleep(2), number=5)) 34 | 35 | 运行结果如下 36 | 37 | .. code:: python 38 | 39 | 2 40 | 2 41 | 2 42 | 2 43 | 2 44 | 10.020059824 45 | -------------------------------------------------------------------------------- /source/c05/c05_09.md: -------------------------------------------------------------------------------- 1 | # 5.9 重定向标准输出到日志 2 | ![](http://image.iswbm.com/20200804124133.png) 3 | 4 | 假设你有一个脚本,会执行一些任务,比如说集群健康情况的检查。 5 | 6 | 检查完成后,会把各服务的的健康状况以 JSON 字符串的形式打印到标准输出。 7 | 8 | 如果代码有问题,导致异常处理不足,最终检查失败,是很有可能将一些错误异常栈输出到标准错误或标准输出上。 9 | 10 | 由于最初约定的脚本返回方式是以 JSON 的格式输出,此时你的脚本却输出各种错误异常,异常调用方也无法解析。 11 | 12 | 如何避免这种情况的发生呢? 13 | 14 | 我们可以这样做,把你的标准错误输出到日志文件中。 15 | 16 | ```python 17 | import contextlib 18 | 19 | log_file="/var/log/you.log" 20 | 21 | def you_task(): 22 | pass 23 | 24 | @contextlib.contextmanager 25 | def close_stdout(): 26 | raw_stdout = sys.stdout 27 | file = open(log_file, 'a+') 28 | sys.stdout = file 29 | 30 | yield 31 | 32 | sys.stdout = raw_stdout 33 | file.close() 34 | 35 | with close_stdout(): 36 | you_task() 37 | ``` 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /source/c05/c05_09.rst: -------------------------------------------------------------------------------- 1 | 5.9 重定向标准输出到日志 2 | ======================== 3 | 4 | .. image:: http://image.iswbm.com/20200804124133.png 5 | 6 | 假设你有一个脚本,会执行一些任务,比如说集群健康情况的检查。 7 | 8 | 检查完成后,会把各服务的的健康状况以 JSON 字符串的形式打印到标准输出。 9 | 10 | 如果代码有问题,导致异常处理不足,最终检查失败,是很有可能将一些错误异常栈输出到标准错误或标准输出上。 11 | 12 | 由于最初约定的脚本返回方式是以 JSON 13 | 的格式输出,此时你的脚本却输出各种错误异常,异常调用方也无法解析。 14 | 15 | 如何避免这种情况的发生呢? 16 | 17 | 我们可以这样做,把你的标准错误输出到日志文件中。 18 | 19 | .. code:: python 20 | 21 | import contextlib 22 | 23 | log_file="/var/log/you.log" 24 | 25 | def you_task(): 26 | pass 27 | 28 | @contextlib.contextmanager 29 | def close_stdout(): 30 | raw_stdout = sys.stdout 31 | file = open(log_file, 'a+') 32 | sys.stdout = file 33 | 34 | yield 35 | 36 | sys.stdout = raw_stdout 37 | file.close() 38 | 39 | with close_stdout(): 40 | you_task() 41 | -------------------------------------------------------------------------------- /source/c05/c05_10.md: -------------------------------------------------------------------------------- 1 | # 5.10 快速定位错误进入调试模式 2 | ![](http://image.iswbm.com/20200804124133.png) 3 | 4 | 当你在写一个程序时,最初的程序一定遇到不少零零散散的错误,这时候就免不了调试一波。 5 | 6 | 如果你和我一样,习惯使用 pdb 进行调试的话,一定有所体会,通常我们都要先把 `pdb.set_trace()` 去掉,让程序畅通无阻,直到它把异常抛出来。 7 | 8 | 出现异常后,再使用 vim 跳转到抛出异常的位置,敲入 `import pdb;pdb.set_trace()` ,然后再到运行,进入调试模式,找到问题并修改代码后再去掉我们加上的那行 pdb 的代码。 9 | 10 | 如此反复这样一个过程,直到最后程序没有异常。 11 | 12 | 你应该能够感受到这个过程有多繁锁,令人崩溃。 13 | 14 | 接下来介绍一种,可以让你不需要修改源代码,就可以在异常抛出时,快速切换到调试模式,进入 『案发现场』排查问题。 15 | 16 | 方法很简单,只需要你在执行脚本时,加入 `-i` 参考 17 | 18 | ![](http://image.iswbm.com/20200615235900.png) 19 | 20 | 如果你的程序没有任何问题,加上 `-i` 后又会有什么不一样呢? 21 | 22 | 从下图可以看出,程序执行完成后会自动进入 console 交互模式。 23 | 24 | ![](http://image.iswbm.com/image-20200616000039009.png) 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /source/c05/c05_10.rst: -------------------------------------------------------------------------------- 1 | 5.10 快速定位错误进入调试模式 2 | ============================= 3 | 4 | .. image:: http://image.iswbm.com/20200804124133.png 5 | 6 | 当你在写一个程序时,最初的程序一定遇到不少零零散散的错误,这时候就免不了调试一波。 7 | 8 | 如果你和我一样,习惯使用 pdb 9 | 进行调试的话,一定有所体会,通常我们都要先把 ``pdb.set_trace()`` 10 | 去掉,让程序畅通无阻,直到它把异常抛出来。 11 | 12 | 出现异常后,再使用 vim 跳转到抛出异常的位置,敲入 13 | ``import pdb;pdb.set_trace()`` 14 | ,然后再到运行,进入调试模式,找到问题并修改代码后再去掉我们加上的那行 15 | pdb 的代码。 16 | 17 | 如此反复这样一个过程,直到最后程序没有异常。 18 | 19 | 你应该能够感受到这个过程有多繁锁,令人崩溃。 20 | 21 | 接下来介绍一种,可以让你不需要修改源代码,就可以在异常抛出时,快速切换到调试模式,进入 22 | 『案发现场』排查问题。 23 | 24 | 方法很简单,只需要你在执行脚本时,加入 ``-i`` 参考 25 | 26 | .. image:: http://image.iswbm.com/20200615235900.png 27 | 28 | 如果你的程序没有任何问题,加上 ``-i`` 后又会有什么不一样呢? 29 | 30 | 从下图可以看出,程序执行完成后会自动进入 console 交互模式。 31 | 32 | .. image:: http://image.iswbm.com/image-20200616000039009.png 33 | -------------------------------------------------------------------------------- /source/c05/c05_11.md: -------------------------------------------------------------------------------- 1 | # 5.11 在程序退出前执行代码的技巧 2 | ![](http://image.iswbm.com/20200804124133.png) 3 | 4 | 使用 atexit 这个内置模块,可以很方便地注册退出函数。 5 | 6 | 不管你在哪个地方导致程序崩溃,都会执行那些你注册过的函数。 7 | 8 | 示例如下 9 | 10 | ![](http://image.iswbm.com/20200510112133.png) 11 | 12 | 如果`clean()`函数有参数,那么你可以不用装饰器,而是直接调用`atexit.register(clean_1, 参数1, 参数2, 参数3='xxx')`。 13 | 14 | 可能你有其他方法可以处理这种需求,但肯定比不上使用 atexit 来得优雅,来得方便,并且它很容易扩展。 15 | 16 | 但是使用 atexit 仍然有一些局限性,比如: 17 | 18 | - 如果程序是被你没有处理过的系统信号杀死的,那么注册的函数无法正常执行。 19 | - 如果发生了严重的 Python 内部错误,你注册的函数无法正常执行。 20 | - 如果你手动调用了`os._exit()`,你注册的函数无法正常执行。 21 | 22 | 23 | -------------------------------------------------------------------------------- /source/c05/c05_11.rst: -------------------------------------------------------------------------------- 1 | 5.11 在程序退出前执行代码的技巧 2 | =============================== 3 | 4 | .. image:: http://image.iswbm.com/20200804124133.png 5 | 6 | 使用 atexit 这个内置模块,可以很方便地注册退出函数。 7 | 8 | 不管你在哪个地方导致程序崩溃,都会执行那些你注册过的函数。 9 | 10 | 示例如下 11 | 12 | .. image:: http://image.iswbm.com/20200510112133.png 13 | 14 | 如果\ ``clean()``\ 函数有参数,那么你可以不用装饰器,而是直接调用\ ``atexit.register(clean_1, 参数1, 参数2, 参数3='xxx')``\ 。 15 | 16 | 可能你有其他方法可以处理这种需求,但肯定比不上使用 atexit 17 | 来得优雅,来得方便,并且它很容易扩展。 18 | 19 | 但是使用 atexit 仍然有一些局限性,比如: 20 | 21 | - 如果程序是被你没有处理过的系统信号杀死的,那么注册的函数无法正常执行。 22 | - 如果发生了严重的 Python 内部错误,你注册的函数无法正常执行。 23 | - 如果你手动调用了\ ``os._exit()``\ ,你注册的函数无法正常执行。 24 | -------------------------------------------------------------------------------- /source/c05/c05_12.md: -------------------------------------------------------------------------------- 1 | # 5.12 逗号也有它的独特用法 2 | ![](http://image.iswbm.com/20200804124133.png) 3 | 4 | 逗号,虽然是个很不起眼的符号,但在 Python 中也有他的用武之地。 5 | 6 | **第一个用法** 7 | 8 | 元组的转化 9 | 10 | ```shell 11 | [root@localhost ~]# cat demo.py 12 | def func(): 13 | return "ok", 14 | 15 | print(func()) 16 | [root@localhost ~]# python3 demo.py 17 | ('ok',) 18 | ``` 19 | 20 | **第二个用法** 21 | 22 | print 的取消换行 23 | 24 | ```shell 25 | [root@localhost ~]# cat demo.py 26 | for i in range(3): 27 | print i 28 | [root@localhost ~]# 29 | [root@localhost ~]# python demo.py 30 | 0 31 | 1 32 | 2 33 | [root@localhost ~]# 34 | [root@localhost ~]# vim demo.py 35 | [root@localhost ~]# 36 | [root@localhost ~]# cat demo.py 37 | for i in range(3): 38 | print i, 39 | [root@localhost ~]# 40 | [root@localhost ~]# python demo.py 41 | 0 1 2 42 | [root@localhost ~]# 43 | ``` 44 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /source/c05/c05_12.rst: -------------------------------------------------------------------------------- 1 | 5.12 逗号也有它的独特用法 2 | ========================= 3 | 4 | .. image:: http://image.iswbm.com/20200804124133.png 5 | 6 | 逗号,虽然是个很不起眼的符号,但在 Python 中也有他的用武之地。 7 | 8 | **第一个用法** 9 | 10 | 元组的转化 11 | 12 | .. code:: shell 13 | 14 | [root@localhost ~]# cat demo.py 15 | def func(): 16 | return "ok", 17 | 18 | print(func()) 19 | [root@localhost ~]# python3 demo.py 20 | ('ok',) 21 | 22 | **第二个用法** 23 | 24 | print 的取消换行 25 | 26 | .. code:: shell 27 | 28 | [root@localhost ~]# cat demo.py 29 | for i in range(3): 30 | print i 31 | [root@localhost ~]# 32 | [root@localhost ~]# python demo.py 33 | 0 34 | 1 35 | 2 36 | [root@localhost ~]# 37 | [root@localhost ~]# vim demo.py 38 | [root@localhost ~]# 39 | [root@localhost ~]# cat demo.py 40 | for i in range(3): 41 | print i, 42 | [root@localhost ~]# 43 | [root@localhost ~]# python demo.py 44 | 0 1 2 45 | [root@localhost ~]# 46 | -------------------------------------------------------------------------------- /source/c05/c05_13.md: -------------------------------------------------------------------------------- 1 | # 5.13 如何在运行状态查看源代码? 2 | ![](http://image.iswbm.com/20200804124133.png) 3 | 4 | 查看函数的源代码,我们通常会使用 IDE 来完成。 5 | 6 | 比如在 PyCharm 中,你可以 Ctrl + 鼠标点击 进入函数的源代码。 7 | 8 | 那如果没有 IDE 呢? 9 | 10 | 当我们想使用一个函数时,如何知道这个函数需要接收哪些参数呢? 11 | 12 | 当我们在使用函数时出现问题的时候,如何通过阅读源代码来排查问题所在呢? 13 | 14 | 15 | 16 | 这时候,我们可以使用 inspect 来代替 IDE 帮助你完成这些事 17 | 18 | ```python 19 | # demo.py 20 | import inspect 21 | 22 | 23 | def add(x, y): 24 | return x + y 25 | 26 | print("===================") 27 | print(inspect.getsource(add)) 28 | ``` 29 | 30 | 运行结果如下 31 | 32 | ```shell 33 | $ python demo.py 34 | =================== 35 | def add(x, y): 36 | return x + y 37 | ``` 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /source/c05/c05_13.rst: -------------------------------------------------------------------------------- 1 | 5.13 如何在运行状态查看源代码? 2 | =============================== 3 | 4 | .. image:: http://image.iswbm.com/20200804124133.png 5 | 6 | 查看函数的源代码,我们通常会使用 IDE 来完成。 7 | 8 | 比如在 PyCharm 中,你可以 Ctrl + 鼠标点击 进入函数的源代码。 9 | 10 | 那如果没有 IDE 呢? 11 | 12 | 当我们想使用一个函数时,如何知道这个函数需要接收哪些参数呢? 13 | 14 | 当我们在使用函数时出现问题的时候,如何通过阅读源代码来排查问题所在呢? 15 | 16 | 这时候,我们可以使用 inspect 来代替 IDE 帮助你完成这些事 17 | 18 | .. code:: python 19 | 20 | # demo.py 21 | import inspect 22 | 23 | 24 | def add(x, y): 25 | return x + y 26 | 27 | print("===================") 28 | print(inspect.getsource(add)) 29 | 30 | 运行结果如下 31 | 32 | .. code:: shell 33 | 34 | $ python demo.py 35 | =================== 36 | def add(x, y): 37 | return x + y 38 | -------------------------------------------------------------------------------- /source/c05/c05_15.md: -------------------------------------------------------------------------------- 1 | # 5.15 让我爱不释手的用户环境 2 | ![](http://image.iswbm.com/20200804124133.png) 3 | 4 | 当你在机器上并没有 root 权限时,如何安装 Python 的第三方包呢? 5 | 6 | 可以使用 `pip install --user pkg` 将你的包安装在你的用户环境中,该用户环境与全局环境并不冲突,并且多用户之间相互隔离,互不影响。 7 | 8 | ```shell 9 | # 在全局环境中未安装 requests 10 | [root@localhost ~]$ pip list | grep requests 11 | [root@localhost ~]$ su - wangbm 12 | 13 | # 由于用户环境继承自全局环境,这里也未安装 14 | [wangbm@localhost ~]$ pip list | grep requests 15 | [wangbm@localhost ~]$ pip install --user requests 16 | [wangbm@localhost ~]$ pip list | grep requests 17 | requests (2.22.0) 18 | [wangbm@localhost ~]$ 19 | 20 | # 从 Location 属性可发现 requests 只安装在当前用户环境中 21 | [wangbm@localhost ~]$ pip show requests 22 | --- 23 | Metadata-Version: 2.1 24 | Name: requests 25 | Version: 2.22.0 26 | Summary: Python HTTP for Humans. 27 | Home-page: http://python-requests.org 28 | Author: Kenneth Reitz 29 | Author-email: me@kennethreitz.org 30 | Installer: pip 31 | License: Apache 2.0 32 | Location: /home/wangbm/.local/lib/python2.7/site-packages 33 | [wangbm@localhost ~]$ exit 34 | logout 35 | 36 | # 退出 wangbm 用户,在 root 用户环境中发现 requests 未安装 37 | [root@localhost ~]$ pip list | grep requests 38 | [root@localhost ~]$ 39 | ``` 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /source/c05/c05_15.rst: -------------------------------------------------------------------------------- 1 | 5.15 让我爱不释手的用户环境 2 | =========================== 3 | 4 | .. image:: http://image.iswbm.com/20200804124133.png 5 | 6 | 当你在机器上并没有 root 权限时,如何安装 Python 的第三方包呢? 7 | 8 | 可以使用 ``pip install --user pkg`` 9 | 将你的包安装在你的用户环境中,该用户环境与全局环境并不冲突,并且多用户之间相互隔离,互不影响。 10 | 11 | .. code:: shell 12 | 13 | # 在全局环境中未安装 requests 14 | [root@localhost ~]$ pip list | grep requests 15 | [root@localhost ~]$ su - wangbm 16 | 17 | # 由于用户环境继承自全局环境,这里也未安装 18 | [wangbm@localhost ~]$ pip list | grep requests 19 | [wangbm@localhost ~]$ pip install --user requests 20 | [wangbm@localhost ~]$ pip list | grep requests 21 | requests (2.22.0) 22 | [wangbm@localhost ~]$ 23 | 24 | # 从 Location 属性可发现 requests 只安装在当前用户环境中 25 | [wangbm@localhost ~]$ pip show requests 26 | --- 27 | Metadata-Version: 2.1 28 | Name: requests 29 | Version: 2.22.0 30 | Summary: Python HTTP for Humans. 31 | Home-page: http://python-requests.org 32 | Author: Kenneth Reitz 33 | Author-email: me@kennethreitz.org 34 | Installer: pip 35 | License: Apache 2.0 36 | Location: /home/wangbm/.local/lib/python2.7/site-packages 37 | [wangbm@localhost ~]$ exit 38 | logout 39 | 40 | # 退出 wangbm 用户,在 root 用户环境中发现 requests 未安装 41 | [root@localhost ~]$ pip list | grep requests 42 | [root@localhost ~]$ 43 | -------------------------------------------------------------------------------- /source/c05/c05_16.md: -------------------------------------------------------------------------------- 1 | # 5.16 字符串的分割技巧 2 | ![](http://image.iswbm.com/20200804124133.png) 3 | 4 | 当我们对字符串进行分割时,且分割符是 `\n`,有可能会出现这样一个窘境: 5 | 6 | ```python 7 | >>> str = "a\nb\n" 8 | >>> print(str) 9 | a 10 | b 11 | 12 | >>> str.split('\n') 13 | ['a', 'b', ''] 14 | >>> 15 | ``` 16 | 17 | 会在最后一行多出一个元素,这可就太多余了吧。 18 | 19 | 实际上对于这种情况,你可以直接这样子 20 | 21 | ```python 22 | >>> str = "a\nb\n" 23 | >>> 24 | >>> str.split() 25 | ['a', 'b'] 26 | ``` 27 | 28 | 即使是多个换行符,也照样适用 29 | 30 | ```python 31 | >>> str = "a\n\nb\n\n" 32 | >>> 33 | >>> str.split() 34 | ['a', 'b'] 35 | ``` 36 | 37 | 有的人还会建议使用 `splitlines` 38 | 39 | ```python 40 | >>> str = "a\nb\n" 41 | >>> 42 | >>> str.splitlines() 43 | ['a', 'b'] 44 | ``` 45 | 46 | 但其实,splitlines 方法只有在上面这种仅一个换行符的场景下,才能达到如上预期的。比如下边的案例,就无法适用: 47 | 48 | ```python 49 | >>> str = "a\n\nb\n\n" 50 | >>> 51 | >>> str.splitlines() 52 | ['a', '', 'b', ''] 53 | ``` 54 | 55 | 在这里有必要补充下,对 splitlines 的原理做一些说明 56 | 57 | 其实 splitlines 函数还有一个 keepends 参数 ,意思是 **是否要保留换行符** ('\r', '\r\n', '\n') 58 | 59 | 默认值为 False,也正是这个原因,上面的例子才有那样的效果 -- **去掉了最后一个元素** 60 | 61 | 试着指定该参数为 True,你就会发现,原来 splitlines 是为换行符为界将字符串分成多个,但并不是直接拿掉换行符。 62 | 63 | ```python 64 | >>> str = "a\nb\n" 65 | >>> str.splitlines(True) 66 | ['a\n', 'b\n'] 67 | ``` 68 | 69 | 因此在多个换行符的情况下,请务必使用 split 方法。 70 | 71 | 72 | -------------------------------------------------------------------------------- /source/c05/c05_16.rst: -------------------------------------------------------------------------------- 1 | 5.16 字符串的分割技巧 2 | ===================== 3 | 4 | .. image:: http://image.iswbm.com/20200804124133.png 5 | 6 | 当我们对字符串进行分割时,且分割符是 7 | ``\n``\ ,有可能会出现这样一个窘境: 8 | 9 | .. code:: python 10 | 11 | >>> str = "a\nb\n" 12 | >>> print(str) 13 | a 14 | b 15 | 16 | >>> str.split('\n') 17 | ['a', 'b', ''] 18 | >>> 19 | 20 | 会在最后一行多出一个元素,这可就太多余了吧。 21 | 22 | 实际上对于这种情况,你可以直接这样子 23 | 24 | .. code:: python 25 | 26 | >>> str = "a\nb\n" 27 | >>> 28 | >>> str.split() 29 | ['a', 'b'] 30 | 31 | 即使是多个换行符,也照样适用 32 | 33 | .. code:: python 34 | 35 | >>> str = "a\n\nb\n\n" 36 | >>> 37 | >>> str.split() 38 | ['a', 'b'] 39 | 40 | 有的人还会建议使用 ``splitlines`` 41 | 42 | .. code:: python 43 | 44 | >>> str = "a\nb\n" 45 | >>> 46 | >>> str.splitlines() 47 | ['a', 'b'] 48 | 49 | 但其实,splitlines 50 | 方法只有在上面这种仅一个换行符的场景下,才能达到如上预期的。比如下边的案例,就无法适用: 51 | 52 | .. code:: python 53 | 54 | >>> str = "a\n\nb\n\n" 55 | >>> 56 | >>> str.splitlines() 57 | ['a', '', 'b', ''] 58 | 59 | 在这里有必要补充下,对 splitlines 的原理做一些说明 60 | 61 | 其实 splitlines 函数还有一个 keepends 参数 ,意思是 **是否要保留换行符** 62 | (‘:raw-latex:`\r'`,’:raw-latex:`\r\n`‘,’:raw-latex:`\n`’) 63 | 64 | 默认值为 False,也正是这个原因,上面的例子才有那样的效果 – 65 | **去掉了最后一个元素** 66 | 67 | 试着指定该参数为 True,你就会发现,原来 splitlines 68 | 是为换行符为界将字符串分成多个,但并不是直接拿掉换行符。 69 | 70 | .. code:: python 71 | 72 | >>> str = "a\nb\n" 73 | >>> str.splitlines(True) 74 | ['a\n', 'b\n'] 75 | 76 | 因此在多个换行符的情况下,请务必使用 split 方法。 77 | -------------------------------------------------------------------------------- /source/c05/c05_17.md: -------------------------------------------------------------------------------- 1 | # 5.17 反转字符串/列表最优雅的方式 2 | ![](http://image.iswbm.com/20200804124133.png) 3 | 4 | 反转序列并不难,但是如何做到最优雅呢? 5 | 6 | 先来看看,正常是如何反转的。 7 | 8 | 最简单的方法是使用列表自带的reverse()方法。 9 | 10 | ```python 11 | >>> ml = [1,2,3,4,5] 12 | >>> ml.reverse() 13 | >>> ml 14 | [5, 4, 3, 2, 1] 15 | ``` 16 | 17 | 但如果你要处理的是字符串,reverse就无能为力了。你可以尝试将其转化成list,再reverse,然后再转化成str。转来转去,也太麻烦了吧?需要这么多行代码(后面三行是不能合并成一行的),一点都不Pythonic。 18 | 19 | ```python 20 | mstr1 = 'abc' 21 | ml1 = list(mstr1) 22 | ml1.reverse() 23 | mstr2 = str(ml1) 24 | ``` 25 | 26 | 对于字符串还有一种稍微复杂一点的,是自定义递归函数来实现。 27 | 28 | ```python 29 | def my_reverse(str): 30 | if str == "": 31 | return str 32 | else: 33 | return my_reverse(str[1:]) + str[0] 34 | ``` 35 | 36 | 在这里,介绍一种最优雅的反转方式,使用切片,不管你是字符串,还是列表,简直通杀。 37 | 38 | ```python 39 | >>> mstr = 'abc' 40 | >>> ml = [1,2,3] 41 | >>> mstr[::-1] 42 | 'cba' 43 | >>> ml[::-1] 44 | [3, 2, 1] 45 | ``` 46 | 47 | 48 | -------------------------------------------------------------------------------- /source/c05/c05_17.rst: -------------------------------------------------------------------------------- 1 | 5.17 反转字符串/列表最优雅的方式 2 | ================================ 3 | 4 | .. image:: http://image.iswbm.com/20200804124133.png 5 | 6 | 反转序列并不难,但是如何做到最优雅呢? 7 | 8 | 先来看看,正常是如何反转的。 9 | 10 | 最简单的方法是使用列表自带的reverse()方法。 11 | 12 | .. code:: python 13 | 14 | >>> ml = [1,2,3,4,5] 15 | >>> ml.reverse() 16 | >>> ml 17 | [5, 4, 3, 2, 1] 18 | 19 | 但如果你要处理的是字符串,reverse就无能为力了。你可以尝试将其转化成list,再reverse,然后再转化成str。转来转去,也太麻烦了吧?需要这么多行代码(后面三行是不能合并成一行的),一点都不Pythonic。 20 | 21 | .. code:: python 22 | 23 | mstr1 = 'abc' 24 | ml1 = list(mstr1) 25 | ml1.reverse() 26 | mstr2 = str(ml1) 27 | 28 | 对于字符串还有一种稍微复杂一点的,是自定义递归函数来实现。 29 | 30 | .. code:: python 31 | 32 | def my_reverse(str): 33 | if str == "": 34 | return str 35 | else: 36 | return my_reverse(str[1:]) + str[0] 37 | 38 | 在这里,介绍一种最优雅的反转方式,使用切片,不管你是字符串,还是列表,简直通杀。 39 | 40 | .. code:: python 41 | 42 | >>> mstr = 'abc' 43 | >>> ml = [1,2,3] 44 | >>> mstr[::-1] 45 | 'cba' 46 | >>> ml[::-1] 47 | [3, 2, 1] 48 | -------------------------------------------------------------------------------- /source/c05/c05_18.md: -------------------------------------------------------------------------------- 1 | # 5.18 如何将 print 内容输出到文件 2 | ![](http://image.iswbm.com/20200804124133.png) 3 | 4 | Python 3 中的 print 作为一个函数,由于可以接收更多的参数,所以功能变为更加强大。 5 | 6 | 比如今天要说的使用 print 将你要打印的内容,输出到日志文件中(但是我并不推荐使用它)。 7 | 8 | ```python 9 | >>> with open('test.log', mode='w') as f: 10 | ... print('hello, python', file=f, flush=True) 11 | >>> exit() 12 | 13 | $ cat test.log 14 | hello, python 15 | ``` 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /source/c05/c05_18.rst: -------------------------------------------------------------------------------- 1 | 5.18 如何将 print 内容输出到文件 2 | ================================ 3 | 4 | .. image:: http://image.iswbm.com/20200804124133.png 5 | 6 | Python 3 中的 print 7 | 作为一个函数,由于可以接收更多的参数,所以功能变为更加强大。 8 | 9 | 比如今天要说的使用 print 10 | 将你要打印的内容,输出到日志文件中(但是我并不推荐使用它)。 11 | 12 | .. code:: python 13 | 14 | >>> with open('test.log', mode='w') as f: 15 | ... print('hello, python', file=f, flush=True) 16 | >>> exit() 17 | 18 | $ cat test.log 19 | hello, python 20 | -------------------------------------------------------------------------------- /source/c05/c05_19.md: -------------------------------------------------------------------------------- 1 | # 5.19 改变默认递归次数限制 2 | ![](http://image.iswbm.com/20200804124133.png) 3 | 4 | 上面才提到递归,大家都知道使用递归是有风险的,递归深度过深容易导致堆栈的溢出。如果你这字符串太长啦,使用递归方式反转,就会出现问题。 5 | 6 | 那到底,默认递归次数限制是多少呢? 7 | 8 | ```python 9 | >>> import sys 10 | >>> sys.getrecursionlimit() 11 | 1000 12 | ``` 13 | 14 | 可以查,当然也可以自定义修改次数,退出即失效。 15 | 16 | ```python 17 | >>> sys.setrecursionlimit(2000) 18 | >>> sys.getrecursionlimit() 19 | 2000 20 | ``` 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /source/c05/c05_19.rst: -------------------------------------------------------------------------------- 1 | 5.19 改变默认递归次数限制 2 | ========================= 3 | 4 | .. image:: http://image.iswbm.com/20200804124133.png 5 | 6 | 上面才提到递归,大家都知道使用递归是有风险的,递归深度过深容易导致堆栈的溢出。如果你这字符串太长啦,使用递归方式反转,就会出现问题。 7 | 8 | 那到底,默认递归次数限制是多少呢? 9 | 10 | .. code:: python 11 | 12 | >>> import sys 13 | >>> sys.getrecursionlimit() 14 | 1000 15 | 16 | 可以查,当然也可以自定义修改次数,退出即失效。 17 | 18 | .. code:: python 19 | 20 | >>> sys.setrecursionlimit(2000) 21 | >>> sys.getrecursionlimit() 22 | 2000 23 | -------------------------------------------------------------------------------- /source/c05/c05_20.md: -------------------------------------------------------------------------------- 1 | # 5.20 让你晕头转向的 else 用法 2 | ![](http://image.iswbm.com/20200804124133.png) 3 | 4 | if else 用法可以说是最基础的语法表达式之一,但是今天不是讲这个的。 5 | 6 | if else 早已烂大街,但我相信仍然有很多人都不曾见过 for else 和 try else 的用法。为什么说它曾让我晕头转向,因为它不像 if else 那么直白,非黑即白,脑子经常要想一下才能才反应过来代码怎么走。 7 | 8 | 先来说说,for ... else ... 9 | 10 | ```python 11 | def check_item(source_list, target): 12 | for item in source_list: 13 | if item == target: 14 | print("Exists!") 15 | break 16 | 17 | else: 18 | print("Does not exist") 19 | 20 | ``` 21 | 22 | 在往下看之前,你可以思考一下,什么情况下才会走 else。是循环被 break,还是没有break? 23 | 24 | 给几个例子,你体会一下。 25 | 26 | ```python 27 | check_item(["apple", "huawei", "oppo"], "oppo") 28 | # Exists! 29 | 30 | check_item(["apple", "huawei", "oppo"], "vivo") 31 | # Does not exist 32 | ``` 33 | 34 | 可以看出,没有被 break 的程序才会正常走else流程。 35 | 36 | 再来看看,try else 用法。 37 | 38 | ```python 39 | def test_try_else(attr1 = None): 40 | try: 41 | if attr1: 42 | pass 43 | else: 44 | raise 45 | except: 46 | print("Exception occurred...") 47 | else: 48 | print("No Exception occurred...") 49 | ``` 50 | 51 | 同样来几个例子。当不传参数时,就抛出异常。 52 | 53 | ```python 54 | test_try_else() 55 | # Exception occurred... 56 | 57 | test_try_else("ming") 58 | # No Exception occurred... 59 | ``` 60 | 61 | 可以看出, try 里面的代码块没有抛出异常的,会正常走else。 62 | 63 | 总结一下,for else 和 try else 相同,只要代码正常走下去不被 break,不抛出异常,就可以走else。 64 | 65 | -------------------------------------------------------------------------------- /source/c05/c05_20.rst: -------------------------------------------------------------------------------- 1 | 5.20 让你晕头转向的 else 用法 2 | ============================= 3 | 4 | .. image:: http://image.iswbm.com/20200804124133.png 5 | 6 | if else 用法可以说是最基础的语法表达式之一,但是今天不是讲这个的。 7 | 8 | if else 早已烂大街,但我相信仍然有很多人都不曾见过 for else 和 try else 9 | 的用法。为什么说它曾让我晕头转向,因为它不像 if else 10 | 那么直白,非黑即白,脑子经常要想一下才能才反应过来代码怎么走。 11 | 12 | 先来说说,for … else … 13 | 14 | .. code:: python 15 | 16 | def check_item(source_list, target): 17 | for item in source_list: 18 | if item == target: 19 | print("Exists!") 20 | break 21 | 22 | else: 23 | print("Does not exist") 24 | 25 | 在往下看之前,你可以思考一下,什么情况下才会走 else。是循环被 26 | break,还是没有break? 27 | 28 | 给几个例子,你体会一下。 29 | 30 | .. code:: python 31 | 32 | check_item(["apple", "huawei", "oppo"], "oppo") 33 | # Exists! 34 | 35 | check_item(["apple", "huawei", "oppo"], "vivo") 36 | # Does not exist 37 | 38 | 可以看出,没有被 break 的程序才会正常走else流程。 39 | 40 | 再来看看,try else 用法。 41 | 42 | .. code:: python 43 | 44 | def test_try_else(attr1 = None): 45 | try: 46 | if attr1: 47 | pass 48 | else: 49 | raise 50 | except: 51 | print("Exception occurred...") 52 | else: 53 | print("No Exception occurred...") 54 | 55 | 同样来几个例子。当不传参数时,就抛出异常。 56 | 57 | .. code:: python 58 | 59 | test_try_else() 60 | # Exception occurred... 61 | 62 | test_try_else("ming") 63 | # No Exception occurred... 64 | 65 | 可以看出, try 里面的代码块没有抛出异常的,会正常走else。 66 | 67 | 总结一下,for else 和 try else 相同,只要代码正常走下去不被 68 | break,不抛出异常,就可以走else。 69 | -------------------------------------------------------------------------------- /source/c05/c05_21.md: -------------------------------------------------------------------------------- 1 | # 5.21 字典访问不存在的key时不再报错 2 | 3 | ![](http://image.iswbm.com/20200804124133.png) 4 | 5 | 当一个字典里没有某个 key 时,此时你访问他是会报 KeyError 的。 6 | 7 | ```python 8 | >>> profile={} 9 | >>> profile["age"] 10 | Traceback (most recent call last): 11 | File "", line 1, in 12 | KeyError: 'age' 13 | ``` 14 | 15 | 这里有一个小技巧,使用 collections 的 defaultdict 方法,可以帮你处理这个小问题,当你访问一个不存在的 key 时,会返回默认值。 16 | 17 | defaultdict 接收一个工厂方法,工厂方法返回的对象就是字典的默认值。 18 | 19 | 常用的工厂方法有,我们常见的 int,str,bool 等 20 | 21 | ```python 22 | >>> a=int() 23 | >>> a 24 | 0 25 | >>> 26 | >>> b=str() 27 | >>> b 28 | '' 29 | >>> 30 | >>> c=bool() 31 | >>> c 32 | False 33 | ``` 34 | 35 | 因为 defaultdict 可以这样子用。 36 | 37 | ```python 38 | >>> import collections 39 | >>> profile=collections.defaultdict(int) 40 | >>> profile 41 | defaultdict(, {}) 42 | >>> profile["age"] 43 | 0 44 | >>> profile=collections.defaultdict(str) 45 | >>> profile 46 | defaultdict(, {}) 47 | >>> profile["name"] 48 | '' 49 | ``` 50 | 51 | 当然既然是工厂方法,你也可以使用 lambda 匿名函数来实现自定义的效果,比如我们使用 str 就会设置一个空字符串,但这并不是我想要的,我想要的是设置一个其他字符串,你就可以像下面这样子。 52 | 53 | ```python 54 | >>> info=collections.defaultdict(lambda: "default value") 55 | >>> info 56 | defaultdict( at 0x10ff10488>, {}) 57 | >>> 58 | >>> info["msg"] 59 | 'default value' 60 | ``` 61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /source/c05/c05_21.rst: -------------------------------------------------------------------------------- 1 | 5.21 字典访问不存在的key时不再报错 2 | ================================== 3 | 4 | .. image:: http://image.iswbm.com/20200804124133.png 5 | 6 | 当一个字典里没有某个 key 时,此时你访问他是会报 KeyError 的。 7 | 8 | .. code:: python 9 | 10 | >>> profile={} 11 | >>> profile["age"] 12 | Traceback (most recent call last): 13 | File "", line 1, in 14 | KeyError: 'age' 15 | 16 | 这里有一个小技巧,使用 collections 的 defaultdict 17 | 方法,可以帮你处理这个小问题,当你访问一个不存在的 key 18 | 时,会返回默认值。 19 | 20 | defaultdict 接收一个工厂方法,工厂方法返回的对象就是字典的默认值。 21 | 22 | 常用的工厂方法有,我们常见的 int,str,bool 等 23 | 24 | .. code:: python 25 | 26 | >>> a=int() 27 | >>> a 28 | 0 29 | >>> 30 | >>> b=str() 31 | >>> b 32 | '' 33 | >>> 34 | >>> c=bool() 35 | >>> c 36 | False 37 | 38 | 因为 defaultdict 可以这样子用。 39 | 40 | .. code:: python 41 | 42 | >>> import collections 43 | >>> profile=collections.defaultdict(int) 44 | >>> profile 45 | defaultdict(, {}) 46 | >>> profile["age"] 47 | 0 48 | >>> profile=collections.defaultdict(str) 49 | >>> profile 50 | defaultdict(, {}) 51 | >>> profile["name"] 52 | '' 53 | 54 | 当然既然是工厂方法,你也可以使用 lambda 55 | 匿名函数来实现自定义的效果,比如我们使用 str 56 | 就会设置一个空字符串,但这并不是我想要的,我想要的是设置一个其他字符串,你就可以像下面这样子。 57 | 58 | .. code:: python 59 | 60 | >>> info=collections.defaultdict(lambda: "default value") 61 | >>> info 62 | defaultdict( at 0x10ff10488>, {}) 63 | >>> 64 | >>> info["msg"] 65 | 'default value' 66 | -------------------------------------------------------------------------------- /source/c05/c05_22.md: -------------------------------------------------------------------------------- 1 | # 5.22 如何实现函数的连续调用? 2 | 3 | ![](http://image.iswbm.com/20200804124133.png) 4 | 5 | 现在我想写一个函数可以实现把所有的数进行求和,并且可以达到反复调用的目的。 6 | 7 | 比如这样子。 8 | 9 | ```python 10 | >>> add(2)(3)(4)(5)(6)(7) 11 | 27 12 | ``` 13 | 14 | 当只调用一次时,也必须适用。 15 | 16 | ```python 17 | >>> add(2) 18 | 2 19 | ``` 20 | 21 | 每次调用的返回结果都是一个 int 类型的实例,要实现将一个实例看做一个函数一样调用,那就不得不使用到 `__call__` 这个魔法方法。 22 | 23 | ```python 24 | >>> class AddInt(int): 25 | ... def __call__(self, x): 26 | ... print("calling __call__ function") 27 | ... return AddInt(self.numerator + x) 28 | ... 29 | >>> 30 | >>> age = AddInt(18) 31 | >>> age 32 | 18 33 | >>> age(1) 34 | calling __call__ function 35 | 19 36 | ``` 37 | 38 | 有了上面的铺垫,可以在 AddInt 外层再加一层封装即可。 39 | 40 | ```python 41 | >>> def add(x): 42 | ... class AddInt(int): 43 | ... def __call__(self, x): 44 | ... return AddInt(self.numerator + x) 45 | ... return AddInt(x) 46 | ... 47 | >>> add(2) 48 | 2 49 | >>> add(2)(3)(4)(5)(6)(7) 50 | 27 51 | >>> 52 | ``` 53 | 54 | -------------------------------------------------------------------------------- /source/c05/c05_22.rst: -------------------------------------------------------------------------------- 1 | 5.22 如何实现函数的连续调用? 2 | ============================= 3 | 4 | .. image:: http://image.iswbm.com/20200804124133.png 5 | 6 | 现在我想写一个函数可以实现把所有的数进行求和,并且可以达到反复调用的目的。 7 | 8 | 比如这样子。 9 | 10 | .. code:: python 11 | 12 | >>> add(2)(3)(4)(5)(6)(7) 13 | 27 14 | 15 | 当只调用一次时,也必须适用。 16 | 17 | .. code:: python 18 | 19 | >>> add(2) 20 | 2 21 | 22 | 每次调用的返回结果都是一个 int 23 | 类型的实例,要实现将一个实例看做一个函数一样调用,那就不得不使用到 24 | ``__call__`` 这个魔法方法。 25 | 26 | .. code:: python 27 | 28 | >>> class AddInt(int): 29 | ... def __call__(self, x): 30 | ... print("calling __call__ function") 31 | ... return AddInt(self.numerator + x) 32 | ... 33 | >>> 34 | >>> age = AddInt(18) 35 | >>> age 36 | 18 37 | >>> age(1) 38 | calling __call__ function 39 | 19 40 | 41 | 有了上面的铺垫,可以在 AddInt 外层再加一层封装即可。 42 | 43 | .. code:: python 44 | 45 | >>> def add(x): 46 | ... class AddInt(int): 47 | ... def __call__(self, x): 48 | ... return AddInt(self.numerator + x) 49 | ... return AddInt(x) 50 | ... 51 | >>> add(2) 52 | 2 53 | >>> add(2)(3)(4)(5)(6)(7) 54 | 27 55 | >>> 56 | -------------------------------------------------------------------------------- /source/c05/c05_23.md: -------------------------------------------------------------------------------- 1 | # 5.23 如何实现字典的多级排序 2 | 3 | ![](http://image.iswbm.com/20200804124133.png) 4 | 5 | 在一个列表中,每个元素都是一个字典,里面的每个字典结构都是一样的。 6 | 7 | 里面的每个字典会有多个键值对,根据某个 key 或 value 的值大小,对该列表进行排序,使用 sort 函数就可以轻松实现。 8 | 9 | ```python 10 | >>> students = [{'name': 'Jack', 'age': 17, 'score': 89}, {'name': 'Julia', 'age': 17, 'score': 80}, {'name': 'Tom', 'age': 16, 'score': 80}] 11 | >>> students.sort(key=lambda student: student['score']) 12 | >>> students 13 | [{'age': 17, 'score': 80, 'name': 'Julia'}, {'age': 16, 'score': 80, 'name': 'Tom'}, {'age': 17, 'score': 89, 'name': 'Jack'}] 14 | ``` 15 | 16 | 如果两名同学的成绩一样,那谁排在前面呢? 17 | 18 | 那就再额外定个第二指标呗,成绩一样,就再看年龄,年龄小的,成绩还能一样,那不是更历害嘛 。 19 | 20 | 规则定下了:先按成绩升序,如果成绩一致,再按年龄升序。 21 | 22 | 问题来了,这样的规则,代码该如何实现呢? 23 | 24 | 用字典本身的 sort 函数也能实现,方法如下: 25 | 26 | ```python 27 | >>> students = [{'name': 'Jack', 'age': 17, 'score': 89}, {'name': 'Julia', 'age': 17, 'score': 80}, {'name': 'Tom', 'age': 16, 'score': 80}] 28 | >>> students.sort(key=lambda student: (student['score'], student['age'])) 29 | >>> students 30 | [{'age': 16, 'score': 80, 'name': 'Tom'}, {'age': 17, 'score': 80, 'name': 'Julia'}, {'age': 17, 'score': 89, 'name': 'Jack'}] 31 | ``` 32 | 33 | 那如果一个降序,而另一个是升序,那又该怎么写呢? 34 | 35 | 很简单,只要在对应的 key 上,前面加一个负号,就会把顺序给颠倒过来。 36 | 37 | 还是以上面为例,我现在要实现先按成绩降序,如果成绩一致,再按年龄升序。可以这样写 38 | 39 | ```python 40 | >>> students = [{'name': 'Jack', 'age': 17, 'score': 89}, {'name': 'Julia', 'age': 17, 'score': 80}, {'name': 'Tom', 'age': 16, 'score': 80}] 41 | >>> students.sort(key=lambda student: (-student['score'], student['age'])) 42 | >>> students 43 | [{'age': 17, 'score': 80, 'name': 'Julia'}, {'age': 16, 'score': 80, 'name': 'Tom'}, {'age': 17, 'score': 89, 'name': 'Jack'}] 44 | ``` 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /source/c05/c05_23.rst: -------------------------------------------------------------------------------- 1 | 5.23 如何实现字典的多级排序 2 | =========================== 3 | 4 | .. image:: http://image.iswbm.com/20200804124133.png 5 | 6 | 在一个列表中,每个元素都是一个字典,里面的每个字典结构都是一样的。 7 | 8 | 里面的每个字典会有多个键值对,根据某个 key 或 value 9 | 的值大小,对该列表进行排序,使用 sort 函数就可以轻松实现。 10 | 11 | .. code:: python 12 | 13 | >>> students = [{'name': 'Jack', 'age': 17, 'score': 89}, {'name': 'Julia', 'age': 17, 'score': 80}, {'name': 'Tom', 'age': 16, 'score': 80}] 14 | >>> students.sort(key=lambda student: student['score']) 15 | >>> students 16 | [{'age': 17, 'score': 80, 'name': 'Julia'}, {'age': 16, 'score': 80, 'name': 'Tom'}, {'age': 17, 'score': 89, 'name': 'Jack'}] 17 | 18 | 如果两名同学的成绩一样,那谁排在前面呢? 19 | 20 | 那就再额外定个第二指标呗,成绩一样,就再看年龄,年龄小的,成绩还能一样,那不是更历害嘛 21 | 。 22 | 23 | 规则定下了:先按成绩升序,如果成绩一致,再按年龄升序。 24 | 25 | 问题来了,这样的规则,代码该如何实现呢? 26 | 27 | 用字典本身的 sort 函数也能实现,方法如下: 28 | 29 | .. code:: python 30 | 31 | >>> students = [{'name': 'Jack', 'age': 17, 'score': 89}, {'name': 'Julia', 'age': 17, 'score': 80}, {'name': 'Tom', 'age': 16, 'score': 80}] 32 | >>> students.sort(key=lambda student: (student['score'], student['age'])) 33 | >>> students 34 | [{'age': 16, 'score': 80, 'name': 'Tom'}, {'age': 17, 'score': 80, 'name': 'Julia'}, {'age': 17, 'score': 89, 'name': 'Jack'}] 35 | 36 | 那如果一个降序,而另一个是升序,那又该怎么写呢? 37 | 38 | 很简单,只要在对应的 key 上,前面加一个负号,就会把顺序给颠倒过来。 39 | 40 | 还是以上面为例,我现在要实现先按成绩降序,如果成绩一致,再按年龄升序。可以这样写 41 | 42 | .. code:: python 43 | 44 | >>> students = [{'name': 'Jack', 'age': 17, 'score': 89}, {'name': 'Julia', 'age': 17, 'score': 80}, {'name': 'Tom', 'age': 16, 'score': 80}] 45 | >>> students.sort(key=lambda student: (-student['score'], student['age'])) 46 | >>> students 47 | [{'age': 17, 'score': 80, 'name': 'Julia'}, {'age': 16, 'score': 80, 'name': 'Tom'}, {'age': 17, 'score': 89, 'name': 'Jack'}] 48 | -------------------------------------------------------------------------------- /source/c05/c05_25.md: -------------------------------------------------------------------------------- 1 | # 5.25 将位置参数变成关键字参数 2 | 3 | ![](http://image.iswbm.com/20200804124133.png) 4 | 5 | 在 Python 中,参数的种类,大概可以分为四种: 6 | 7 | 1. `必选参数`,也叫`位置参数`,调用函数时一定指定的参数,并且在传参的时候必须按函数定义时的顺序来 8 | 2. `可选参数`,也叫`默认参数`,调用函数时,可以指定也可以不指定,不指定就按默认的参数值来。 9 | 3. `可变参数`,就是参数个数可变,可以是 0 个或者任意个,但是传参时不能指定参数名,通常使用 `*args` 来表示。 10 | 4. `关键字参数`,就是参数个数可变,可以是 0 个或者任意个,但是传参时必须指定参数名,通常使用 `**kw` 来表示 11 | 12 | 使用单独的 `*`,可以将后面的位置参数变成关键字参数,关键字参数在你传参时,必须要写参数名,不然会报错。 13 | 14 | ```python 15 | >>> def demo_func(a, b, *, c): 16 | ... print(a) 17 | ... print(b) 18 | ... print(c) 19 | ... 20 | >>> 21 | >>> demo_func(1, 2, 3) 22 | Traceback (most recent call last): 23 | File "", line 1, in 24 | TypeError: demo_func() takes 2 positional arguments but 3 were given 25 | >>> 26 | >>> demo_func(1, 2, c=3) 27 | 1 28 | 2 29 | 3 30 | ``` 31 | 32 | -------------------------------------------------------------------------------- /source/c05/c05_25.rst: -------------------------------------------------------------------------------- 1 | 5.25 将位置参数变成关键字参数 2 | ============================= 3 | 4 | .. image:: http://image.iswbm.com/20200804124133.png 5 | 6 | 在 Python 中,参数的种类,大概可以分为四种: 7 | 8 | 1. ``必选参数``\ ,也叫\ ``位置参数``\ ,调用函数时一定指定的参数,并且在传参的时候必须按函数定义时的顺序来 9 | 2. ``可选参数``\ ,也叫\ ``默认参数``\ ,调用函数时,可以指定也可以不指定,不指定就按默认的参数值来。 10 | 3. ``可变参数``\ ,就是参数个数可变,可以是 0 11 | 个或者任意个,但是传参时不能指定参数名,通常使用 ``*args`` 来表示。 12 | 4. ``关键字参数``\ ,就是参数个数可变,可以是 0 13 | 个或者任意个,但是传参时必须指定参数名,通常使用 ``**kw`` 来表示 14 | 15 | 使用单独的 16 | ``*``\ ,可以将后面的位置参数变成关键字参数,关键字参数在你传参时,必须要写参数名,不然会报错。 17 | 18 | .. code:: python 19 | 20 | >>> def demo_func(a, b, *, c): 21 | ... print(a) 22 | ... print(b) 23 | ... print(c) 24 | ... 25 | >>> 26 | >>> demo_func(1, 2, 3) 27 | Traceback (most recent call last): 28 | File "", line 1, in 29 | TypeError: demo_func() takes 2 positional arguments but 3 were given 30 | >>> 31 | >>> demo_func(1, 2, c=3) 32 | 1 33 | 2 34 | 3 35 | -------------------------------------------------------------------------------- /source/c05/c05_26.md: -------------------------------------------------------------------------------- 1 | # 5.26 如何获取一个函数设定的参数 2 | 3 | ![](http://image.iswbm.com/20200804124133.png) 4 | 5 | 在 Python 中有一个叫 inspect 的库,非常的好用,利用它可以获取一些数据,这在写一些框架时非常有用。 6 | 7 | 比如有下面这样一个函数 8 | 9 | ```python 10 | def demo(name, age, gender="male", *args, **kw): 11 | pass 12 | ``` 13 | 14 | 使用 inspect 可以直接获取 15 | 16 | ```python 17 | >>> from inspect import signature 18 | >>> 19 | >>> sig = signature(demo) # # 获取函数签名 20 | >>> sig 21 | 22 | ``` 23 | 24 | 利用 inspect 还可以检查传参是否匹配签名 25 | 26 | ```python 27 | >>> sig.bind("王炳明", 27) 28 | 29 | >>> 30 | >>> sig.bind("王炳明") 31 | Traceback (most recent call last): 32 | File "", line 1, in 33 | File "/usr/lib64/python3.6/inspect.py", line 2997, in bind 34 | return args[0]._bind(args[1:], kwargs) 35 | File "/usr/lib64/python3.6/inspect.py", line 2912, in _bind 36 | raise TypeError(msg) from None 37 | TypeError: missing a required argument: 'age' 38 | ``` 39 | 40 | -------------------------------------------------------------------------------- /source/c05/c05_26.rst: -------------------------------------------------------------------------------- 1 | 5.26 如何获取一个函数设定的参数 2 | =============================== 3 | 4 | .. image:: http://image.iswbm.com/20200804124133.png 5 | 6 | 在 Python 中有一个叫 inspect 7 | 的库,非常的好用,利用它可以获取一些数据,这在写一些框架时非常有用。 8 | 9 | 比如有下面这样一个函数 10 | 11 | .. code:: python 12 | 13 | def demo(name, age, gender="male", *args, **kw): 14 | pass 15 | 16 | 使用 inspect 可以直接获取 17 | 18 | .. code:: python 19 | 20 | >>> from inspect import signature 21 | >>> 22 | >>> sig = signature(demo) # # 获取函数签名 23 | >>> sig 24 | 25 | 26 | 利用 inspect 还可以检查传参是否匹配签名 27 | 28 | .. code:: python 29 | 30 | >>> sig.bind("王炳明", 27) 31 | 32 | >>> 33 | >>> sig.bind("王炳明") 34 | Traceback (most recent call last): 35 | File "", line 1, in 36 | File "/usr/lib64/python3.6/inspect.py", line 2997, in bind 37 | return args[0]._bind(args[1:], kwargs) 38 | File "/usr/lib64/python3.6/inspect.py", line 2912, in _bind 39 | raise TypeError(msg) from None 40 | TypeError: missing a required argument: 'age' 41 | -------------------------------------------------------------------------------- /source/c05/c05_27.md: -------------------------------------------------------------------------------- 1 | # 5.27 如何进行版本的比较 2 | 3 | ![](http://image.iswbm.com/20200804124133.png) 4 | 5 | ## 使用 distutils 6 | 7 | `distutils` 是 Python 的内置模块,它做为最古老的 python 分发工具,本身也实现了版本的比较与检查的功能。 8 | 9 | ```python 10 | >>> from distutils.version import LooseVersion, StrictVersion 11 | >>> LooseVersion("2.3.1") < LooseVersion("10.1.2") 12 | True 13 | >>> StrictVersion("2.3.1") < StrictVersion("10.1.2") 14 | True 15 | ``` 16 | 17 | ## 使用 packaging 18 | 19 | 如果你的环境中安装过 `setuptools`,那么一定会附带安装了 packaging 这个包,而如果你的环境中并没有 setuptools ,也可以通过 pip 来快速安装 20 | 21 | ```shell 22 | $ python3 -m pip install packaging 23 | ``` 24 | 25 | 在 packaging 中有一个 version 模块,专门用来为 setuptools 提供包版本的版本解析。 26 | 27 | ```python 28 | >>> from packaging import version 29 | >>> version.parse("2.3.1") < version.parse("10.1.2") 30 | True 31 | >>> version.parse("1.3.a4") < version.parse("10.1.2") 32 | True 33 | ``` 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /source/c05/c05_27.rst: -------------------------------------------------------------------------------- 1 | 5.27 如何进行版本的比较 2 | ======================= 3 | 4 | .. image:: http://image.iswbm.com/20200804124133.png 5 | 6 | 使用 distutils 7 | -------------- 8 | 9 | ``distutils`` 是 Python 的内置模块,它做为最古老的 python 10 | 分发工具,本身也实现了版本的比较与检查的功能。 11 | 12 | .. code:: python 13 | 14 | >>> from distutils.version import LooseVersion, StrictVersion 15 | >>> LooseVersion("2.3.1") < LooseVersion("10.1.2") 16 | True 17 | >>> StrictVersion("2.3.1") < StrictVersion("10.1.2") 18 | True 19 | 20 | 使用 packaging 21 | -------------- 22 | 23 | 如果你的环境中安装过 ``setuptools``\ ,那么一定会附带安装了 packaging 24 | 这个包,而如果你的环境中并没有 setuptools ,也可以通过 pip 来快速安装 25 | 26 | .. code:: shell 27 | 28 | $ python3 -m pip install packaging 29 | 30 | 在 packaging 中有一个 version 模块,专门用来为 setuptools 31 | 提供包版本的版本解析。 32 | 33 | .. code:: python 34 | 35 | >>> from packaging import version 36 | >>> version.parse("2.3.1") < version.parse("10.1.2") 37 | True 38 | >>> version.parse("1.3.a4") < version.parse("10.1.2") 39 | True 40 | -------------------------------------------------------------------------------- /source/c05/c05_28.md: -------------------------------------------------------------------------------- 1 | # 5.28 如何捕获警告?(注意不是捕获异常) 2 | 3 | ## 1. 警告不是异常 4 | 5 | 你是不是经常在使用一些系统库或者第三方模块的时候,会出现一些既不是异常也不是错误的警告信息? 6 | 7 | 这些警告信息,有时候非常多,对于新手容易造成一些误判,以为是程序出错了。 8 | 9 | 实则不然,异常和错误,都是程序出现了一些问题,但是警告不同,他的紧急程度非常之低,以致于大多数的警告都是可以直接忽略的。 10 | 11 | 如果不想显示这些告警信息,可以直接加上参数 `-W ignore` 参数,就不会再显示了。 12 | 13 | ## 2. 警告能捕获吗 14 | 15 | 能捕获的只有错误异常,但是通过一系列的操作后,你可以将这些警告转化为异常。 16 | 17 | 这样一来,你就可以像异常一样去捕获他们了。 18 | 19 | 在不进行任何设置的情况下,警告会直接打印在终端上。 20 | 21 | ![](http://image.iswbm.com/20210313143425.png) 22 | 23 | ## 3. 捕获警告方法一 24 | 25 | 在 warnings 中有一系列的过滤器。 26 | 27 | | 值 | 处置 | 28 | | :---------- | :----------------------------------------------------- | 29 | | `"default"` | 为发出警告的每个位置(模块+行号)打印第一个匹配警告 | 30 | | `"error"` | 将匹配警告转换为异常 | 31 | | `"ignore"` | 从不打印匹配的警告 | 32 | | `"always"` | 总是打印匹配的警告 | 33 | | `"module"` | 为发出警告的每个模块打印第一次匹配警告(无论行号如何) | 34 | | `"once"` | 无论位置如何,仅打印第一次出现的匹配警告 | 35 | 36 | 当你指定为 error 的时候,就会将匹配警告转换为异常。 37 | 38 | 之后你就可以通过异常的方式去捕获警告了。 39 | 40 | ```python 41 | import warnings 42 | warnings.filterwarnings('error') 43 | 44 | try: 45 | warnings.warn("deprecated", DeprecationWarning) 46 | except Warning as e: 47 | print(e) 48 | ``` 49 | 50 | 运行后,效果如下 51 | 52 | ![](http://image.iswbm.com/20210313144501.png) 53 | 54 | ## 4. 捕获警告方法二 55 | 56 | 如果你不想对在代码中去配置将警告转成异常。 57 | 58 | ```python 59 | import warnings 60 | 61 | try: 62 | warnings.warn("deprecated", DeprecationWarning) 63 | except Warning as e: 64 | print(e) 65 | ``` 66 | 67 | 可以在执行的时候,只要加上一个参数 `-W error` ,就可以实现一样的效果 68 | 69 | ```shell 70 | $ python3 -W error demo.py 71 | deprecated 72 | ``` 73 | 74 | ## 5. 捕获警告方法三 75 | 76 | 除了上面的方法之外 ,warnings 还自带了个捕获警告的上下文管理器。 77 | 78 | 当你加上 `record=True` 它会返回一个列表,列表里存放的是所有捕获到的警告,我将它赋值为 `w`,然后就可以将它打印出来了。 79 | 80 | ```python 81 | import warnings 82 | 83 | def do_warning(): 84 | warnings.warn("deprecated", DeprecationWarning) 85 | 86 | with warnings.catch_warnings(record=True) as w: 87 | do_warning() 88 | if len(w) >0: 89 | print(w[0].message) 90 | ``` 91 | 92 | 运行后,效果如下 93 | 94 | ![](http://image.iswbm.com/20210313144751.png) -------------------------------------------------------------------------------- /source/c05/c05_29.md: -------------------------------------------------------------------------------- 1 | # 5.29 如何禁止对象深拷贝? 2 | 3 | ![](http://image.iswbm.com/20200804124133.png) 4 | 5 | 当你使用 copy 模块的 deepcopy 拷贝一个对象后,会创建出来一个全新的的对象。 6 | 7 | ```python 8 | >>> from copy import deepcopy 9 | >>> 10 | >>> profile = {"name": "wangbm"} 11 | >>> id(profile) 12 | 21203408 13 | >>> 14 | >>> new_profile = deepcopy(profile) 15 | >>> id(new_profile) 16 | 21236144 17 | ``` 18 | 19 | 但是有的时候,我们希望基于我们的类实例化后对象,禁止被深拷贝,这时候就要用到 Python 的魔法方法了。 20 | 21 | 在如下代码中,我们重写了 Sentinel 类的 `__deepcopy__` 和 `__copy__` 方法 22 | 23 | ```python 24 | class Sentinel(object): 25 | def __deepcopy__(self, memo): 26 | # Always return the same object because this is essentially a constant. 27 | return self 28 | 29 | def __copy__(self): 30 | # called via copy.copy(x) 31 | return self 32 | ``` 33 | 34 | 此时你如果对它进行深度拷贝的话,会发现返回的永远都是原来的对象 35 | 36 | ```python 37 | >>> obj = Sentinel() 38 | >>> id(obj) 39 | 140151569169808 40 | >>> 41 | >>> new_obj = deepcopy(obj) 42 | >>> id(new_obj) 43 | 140151569169808 44 | ``` 45 | 46 | 47 | -------------------------------------------------------------------------------- /source/c05/c05_29.rst: -------------------------------------------------------------------------------- 1 | 5.29 如何禁止对象深拷贝? 2 | ======================== 3 | 4 | .. image:: http://image.iswbm.com/20200804124133.png 5 | 6 | 当你使用 copy 模块的 deepcopy 7 | 拷贝一个对象后,会创建出来一个全新的的对象。 8 | 9 | .. code:: python 10 | 11 | >>> from copy import deepcopy 12 | >>> 13 | >>> profile = {"name": "wangbm"} 14 | >>> id(profile) 15 | 21203408 16 | >>> 17 | >>> new_profile = deepcopy(profile) 18 | >>> id(new_profile) 19 | 21236144 20 | 21 | 但是有的时候,我们希望基于我们的类实例化后对象,禁止被深拷贝,这时候就要用到 22 | Python 的魔法方法了。 23 | 24 | 在如下代码中,我们重写了 Sentinel 类的 ``__deepcopy__`` 和 ``__copy__`` 25 | 方法 26 | 27 | .. code:: python 28 | 29 | class Sentinel(object): 30 | def __deepcopy__(self, memo): 31 | # Always return the same object because this is essentially a constant. 32 | return self 33 | 34 | def __copy__(self): 35 | # called via copy.copy(x) 36 | return self 37 | 38 | 此时你如果对它进行深度拷贝的话,会发现返回的永远都是原来的对象 39 | 40 | .. code:: python 41 | 42 | >>> obj = Sentinel() 43 | >>> id(obj) 44 | 140151569169808 45 | >>> 46 | >>> new_obj = deepcopy(obj) 47 | >>> id(new_obj) 48 | 140151569169808 49 | -------------------------------------------------------------------------------- /source/c05/c05_30.md: -------------------------------------------------------------------------------- 1 | # 5.30 如何将变量名和变量值转为字典? 2 | 3 | ![](http://image.iswbm.com/20200804124133.png) 4 | 5 | 千言万语,不如上示例演示下效果 6 | 7 | ```python 8 | >>> name="wangbm" 9 | >>> age=28 10 | >>> gender="male" 11 | >>> 12 | >>> convert_vars_to_dict(name, age, gender) 13 | {'name': 'wangbm', 'age': 28, 'gender': 'male'} 14 | ``` 15 | 16 | `convert_vars_to_dict` 是我要自己定义的这么一个函数,功能如上,代码如下。 17 | 18 | ```python 19 | import re 20 | import inspect 21 | 22 | def varname(*args): 23 | current_frame = inspect.currentframe() 24 | back_frame = current_frame.f_back 25 | back_frame_info = inspect.getframeinfo(back_frame) 26 | 27 | current_func_name = current_frame.f_code.co_name 28 | 29 | caller_file_path = back_frame_info[0] 30 | caller_line_no = back_frame_info[1] 31 | caller_type = back_frame_info[2] 32 | caller_expression = back_frame_info[3] 33 | 34 | keys = [] 35 | 36 | for line in caller_expression: 37 | re_match = re.search(r'\b{}\((.*?)\)'.format(current_func_name), line) 38 | match_string = re_match.groups(1)[0] 39 | keys = [match.strip() for match in match_string.split(',') if match] 40 | 41 | return dict(zip(keys, args)) 42 | ``` 43 | 44 | 附上 :[inspect 学习文档](https://docs.python.org/zh-cn/3.7/library/inspect.html) 45 | 46 | -------------------------------------------------------------------------------- /source/c05/c05_30.rst: -------------------------------------------------------------------------------- 1 | 5.30 如何将变量名和变量值转为字典? 2 | =================================== 3 | 4 | .. image:: http://image.iswbm.com/20200804124133.png 5 | 6 | 千言万语,不如上示例演示下效果 7 | 8 | .. code:: python 9 | 10 | >>> name="wangbm" 11 | >>> age=28 12 | >>> gender="male" 13 | >>> 14 | >>> convert_vars_to_dict(name, age, gender) 15 | {'name': 'wangbm', 'age': 28, 'gender': 'male'} 16 | 17 | ``convert_vars_to_dict`` 18 | 是我要自己定义的这么一个函数,功能如上,代码如下。 19 | 20 | .. code:: python 21 | 22 | import re 23 | import inspect 24 | 25 | def varname(*args): 26 | current_frame = inspect.currentframe() 27 | back_frame = current_frame.f_back 28 | back_frame_info = inspect.getframeinfo(back_frame) 29 | 30 | current_func_name = current_frame.f_code.co_name 31 | 32 | caller_file_path = back_frame_info[0] 33 | caller_line_no = back_frame_info[1] 34 | caller_type = back_frame_info[2] 35 | caller_expression = back_frame_info[3] 36 | 37 | keys = [] 38 | 39 | for line in caller_expression: 40 | re_match = re.search(r'\b{}\((.*?)\)'.format(current_func_name), line) 41 | match_string = re_match.groups(1)[0] 42 | keys = [match.strip() for match in match_string.split(',') if match] 43 | 44 | return dict(zip(keys, args)) 45 | 46 | 附上 :\ `inspect 47 | 学习文档 `__ 48 | -------------------------------------------------------------------------------- /source/c05/c05_31.md: -------------------------------------------------------------------------------- 1 | # 5.31 替换实例方法的最佳实践 2 | 3 | ![](http://image.iswbm.com/20200804124133.png) 4 | 5 | ## 思路一:简单替换 6 | 7 | 当你想对类实例的方法进行替换时,你可能想到的是直接对他进行粗暴地替换 8 | 9 | ```python 10 | class People: 11 | def speak(self): 12 | print("hello, world") 13 | 14 | 15 | def speak(self): 16 | print("hello, python") 17 | 18 | p = People() 19 | p.speak = speak 20 | p.speak() 21 | ``` 22 | 23 | 但当你试着执行这段代码的时候,就会发现行不通,它提示我们要传入 self 参数 24 | 25 | ``` 26 | Traceback (most recent call last): 27 | File "/Users/MING/Code/Python/demo.py", line 12, in 28 | p.speak() 29 | TypeError: speak() missing 1 required positional argument: 'self' 30 | ``` 31 | 32 | 不对啊~ self 不是实例本身吗?函数不是一直就这么写的? 33 | 34 | 实际上你这么替换,speak 就变成了一个 function,而不是一个和实例绑定的 method ,你可以把替换前后的 speak 打印出来 35 | 36 | ```python 37 | p = People() 38 | print(p.speak) 39 | p.speak = speak 40 | print(p.speak) 41 | ``` 42 | 43 | 输出结果如下,区别非常明显 44 | 45 | ``` 46 | > 47 | 48 | ``` 49 | 50 | 这种方法,只能用在替换不与实例绑定的静态方法上,不然你每次调用的时候,就得手动传入实例本身,但这样调用就会变得非常怪异。 51 | 52 | ## 思路二:利用 im_func 53 | 54 | 有 Python 2 使用经验的朋友,可以会知道类实例的方法,都有 `im_func` 和 `im_class` 属性,分别指向了该方法的函数和类。 55 | 56 | ![](http://image.iswbm.com/20210328111610.png) 57 | 58 | 很抱歉的是,这些在 Python3 中全都取消了,意味你无法再使用 `im_func` 和 `im_class` 。 59 | 60 | 但即使你身处 Python 2 的环境下,你想通过 `im_func` 去直接替换函数,也仍然是有问题的。 61 | 62 | 因为在 Python2 中不推荐普通用户对类实例的方法进行替换,所以 Python 给类实例的方法赋予了只读属性 63 | 64 | ![](http://image.iswbm.com/20210328111904.png) 65 | 66 | ## 思路三:非常危险的字节码替换 67 | 68 | 表层不行,但这个方法在字节码层面却是可行的 69 | 70 | ![](http://image.iswbm.com/20210328112231.png) 71 | 72 | 这种方法,非常的粗暴且危险,他会直接影响到使用 People 的所有实例的 speak 方法,因此这种方法千万不要使用。 73 | 74 | ![](http://image.iswbm.com/20210328112501.png) 75 | 76 | ## 思路四:利用 types 绑定方法 77 | 78 | 在 types 中有一个 MethodType,可以将普通方法与实例进行绑定。 79 | 80 | 绑定后,就可以直接替换掉原实例的 speak 方法了,完整代码如下: 81 | 82 | ```python 83 | import types 84 | 85 | class People: 86 | def speak(self): 87 | print("hello, world") 88 | 89 | 90 | def speak(self): 91 | print("hello, python") 92 | 93 | p = People() 94 | p.speak = types.MethodType(speak, p) 95 | p.speak() 96 | ``` 97 | 98 | 这种方法,最为安全,不会影响其他实例。并且 Python 2 和 Python 3 都适用,是官方推荐的一种做法。 99 | 100 | -------------------------------------------------------------------------------- /source/c05/c05_32.md: -------------------------------------------------------------------------------- 1 | # 5.32 如何动态创建函数? 2 | 3 | ![](http://image.iswbm.com/20200804124133.png) 4 | 5 | 在下面的代码中,每一次 for 循环都会创建一个返回特定字符串的函数。 6 | 7 | ```python 8 | from types import FunctionType 9 | 10 | 11 | for name in ("world", "python"): 12 | func = FunctionType(compile( 13 | ("def hello():\n" 14 | " return '{}'".format(name)), 15 | "", 16 | "exec").co_consts[0], globals()) 17 | 18 | print(func()) 19 | ``` 20 | 21 | 输出如下 22 | 23 | ``` 24 | world 25 | python 26 | ``` 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /source/c05/c05_32.rst: -------------------------------------------------------------------------------- 1 | 5.32 如何动态创建函数? 2 | ======================= 3 | 4 | .. image:: http://image.iswbm.com/20200804124133.png 5 | 6 | 在下面的代码中,每一次 for 循环都会创建一个返回特定字符串的函数。 7 | 8 | .. code:: python 9 | 10 | from types import FunctionType 11 | 12 | 13 | for name in ("world", "python"): 14 | func = FunctionType(compile( 15 | ("def hello():\n" 16 | " return '{}'".format(name)), 17 | "", 18 | "exec").co_consts[0], globals()) 19 | 20 | print(func()) 21 | 22 | 输出如下 23 | 24 | :: 25 | 26 | world 27 | python 28 | -------------------------------------------------------------------------------- /source/c06/c06_01.md: -------------------------------------------------------------------------------- 1 | # 6.1 不要直接调用类的私有方法 2 | ![](http://image.iswbm.com/20200804124133.png) 3 | 4 | 大家都知道,类中可供直接调用的方法,只有公有方法(protected类型的方法也可以,但是不建议)。也就是说,类的私有方法是无法直接调用的。 5 | 6 | 这里先看一下例子 7 | 8 | ```python 9 | class Kls(): 10 | def public(self): 11 | print('Hello public world!') 12 | 13 | def __private(self): 14 | print('Hello private world!') 15 | 16 | def call_private(self): 17 | self.__private() 18 | 19 | ins = Kls() 20 | 21 | # 调用公有方法,没问题 22 | ins.public() 23 | 24 | # 直接调用私有方法,不行 25 | ins.__private() 26 | 27 | # 但你可以通过内部公有方法,进行代理 28 | ins.call_private() 29 | ``` 30 | 31 | 既然都是方法,那我们真的没有办法可以直接调用吗? 32 | 33 | 当然有啦,只是建议你千万不要这样弄,这里只是普及,让你了解一下。 34 | 35 | ```python 36 | # 调用私有方法,以下两种等价 37 | ins._Kls__private() 38 | ins.call_private() 39 | ``` 40 | 41 | ## 42 | 43 | 44 | -------------------------------------------------------------------------------- /source/c06/c06_01.rst: -------------------------------------------------------------------------------- 1 | 6.1 不要直接调用类的私有方法 2 | ============================ 3 | 4 | .. image:: http://image.iswbm.com/20200804124133.png 5 | 6 | 大家都知道,类中可供直接调用的方法,只有公有方法(protected类型的方法也可以,但是不建议)。也就是说,类的私有方法是无法直接调用的。 7 | 8 | 这里先看一下例子 9 | 10 | .. code:: python 11 | 12 | class Kls(): 13 | def public(self): 14 | print('Hello public world!') 15 | 16 | def __private(self): 17 | print('Hello private world!') 18 | 19 | def call_private(self): 20 | self.__private() 21 | 22 | ins = Kls() 23 | 24 | # 调用公有方法,没问题 25 | ins.public() 26 | 27 | # 直接调用私有方法,不行 28 | ins.__private() 29 | 30 | # 但你可以通过内部公有方法,进行代理 31 | ins.call_private() 32 | 33 | 既然都是方法,那我们真的没有办法可以直接调用吗? 34 | 35 | 当然有啦,只是建议你千万不要这样弄,这里只是普及,让你了解一下。 36 | 37 | .. code:: python 38 | 39 | # 调用私有方法,以下两种等价 40 | ins._Kls__private() 41 | ins.call_private() 42 | -------------------------------------------------------------------------------- /source/c06/c06_02.md: -------------------------------------------------------------------------------- 1 | # 6.2 默认参数最好不为可变对象 2 | ![](http://image.iswbm.com/20200804124133.png) 3 | 4 | 函数的参数分三种 5 | 6 | - 可变参数 7 | - 默认参数 8 | - 关键字参数 9 | 10 | 当你在传递默认参数时,有新手很容易踩雷的一个坑。 11 | 12 | 先来看一个示例 13 | 14 | ```python 15 | def func(item, item_list=[]): 16 | item_list.append(item) 17 | print(item_list) 18 | 19 | func('iphone') 20 | func('xiaomi', item_list=['oppo','vivo']) 21 | func('huawei') 22 | ``` 23 | 24 | 在这里,你可以暂停一下,思考一下会输出什么? 25 | 26 | 思考过后,你的答案是否和下面的一致呢 27 | 28 | ``` 29 | ['iphone'] 30 | ['oppo', 'vivo', 'xiaomi'] 31 | ['iphone', 'huawei'] 32 | ``` 33 | 34 | 如果是,那你可以跳过这部分内容,如果不是,请接着往下看,这里来分析一下。 35 | 36 | Python 中的 def 语句在每次执行的时候都初始化一个函数对象,这个函数对象就是我们要调用的函数,可以把它当成一个一般的对象,只不过这个对象拥有一个可执行的方法和部分属性。 37 | 38 | 对于参数中提供了初始值的参数,由于 Python 中的函数参数传递的是对象,也可以认为是传地址,在第一次初始化 def 的时候,会先生成这个可变对象的内存地址,然后将这个默认参数 item_list 会与这个内存地址绑定。在后面的函数调用中,如果调用方指定了新的默认值,就会将原来的默认值覆盖。如果调用方没有指定新的默认值,那就会使用原来的默认值。 39 | 40 | ![](http://image.iswbm.com/20190511165650.png) 41 | 42 | ## 43 | 44 | 45 | -------------------------------------------------------------------------------- /source/c06/c06_02.rst: -------------------------------------------------------------------------------- 1 | 6.2 默认参数最好不为可变对象 2 | ============================ 3 | 4 | .. image:: http://image.iswbm.com/20200804124133.png 5 | 6 | 函数的参数分三种 7 | 8 | - 可变参数 9 | - 默认参数 10 | - 关键字参数 11 | 12 | 当你在传递默认参数时,有新手很容易踩雷的一个坑。 13 | 14 | 先来看一个示例 15 | 16 | .. code:: python 17 | 18 | def func(item, item_list=[]): 19 | item_list.append(item) 20 | print(item_list) 21 | 22 | func('iphone') 23 | func('xiaomi', item_list=['oppo','vivo']) 24 | func('huawei') 25 | 26 | 在这里,你可以暂停一下,思考一下会输出什么? 27 | 28 | 思考过后,你的答案是否和下面的一致呢 29 | 30 | :: 31 | 32 | ['iphone'] 33 | ['oppo', 'vivo', 'xiaomi'] 34 | ['iphone', 'huawei'] 35 | 36 | 如果是,那你可以跳过这部分内容,如果不是,请接着往下看,这里来分析一下。 37 | 38 | Python 中的 def 39 | 语句在每次执行的时候都初始化一个函数对象,这个函数对象就是我们要调用的函数,可以把它当成一个一般的对象,只不过这个对象拥有一个可执行的方法和部分属性。 40 | 41 | 对于参数中提供了初始值的参数,由于 Python 42 | 中的函数参数传递的是对象,也可以认为是传地址,在第一次初始化 def 43 | 的时候,会先生成这个可变对象的内存地址,然后将这个默认参数 item_list 44 | 会与这个内存地址绑定。在后面的函数调用中,如果调用方指定了新的默认值,就会将原来的默认值覆盖。如果调用方没有指定新的默认值,那就会使用原来的默认值。 45 | 46 | .. image:: http://image.iswbm.com/20190511165650.png 47 | -------------------------------------------------------------------------------- /source/c06/c06_03.md: -------------------------------------------------------------------------------- 1 | # 6.3 增量赋值的性能更好 2 | ![](http://image.iswbm.com/20200804124133.png) 3 | 4 | 诸如 `+=` 和 `*=` 这些运算符,叫做 增量赋值运算符。 5 | 6 | 这里使用用 += 举例,以下两种写法,在效果上是等价的。 7 | 8 | ```python 9 | # 第一种 10 | a = 1 ; a += 1 11 | 12 | # 第二种 13 | a = 1; a = a + 1 14 | ``` 15 | 16 | `+=` 其背后使用的魔法方法是 `__iadd__`,如果没有实现这个方法则会退而求其次,使用 `__add__` 。 17 | 18 | 这两种写法有什么区别呢? 19 | 20 | 用列表举例 a += b,使用 `__add__` 的话就像是使用了a.extend(b),如果使用 `__add__` 的话,则是 a = a+b,前者是直接在原列表上进行扩展,而后者是先从原列表中取出值,在一个新的列表中进行扩展,然后再将新的列表对象返回给变量,显然后者的消耗要大些。 21 | 22 | 所以在能使用增量赋值的时候尽量使用它。 23 | 24 | 25 | -------------------------------------------------------------------------------- /source/c06/c06_03.rst: -------------------------------------------------------------------------------- 1 | 6.3 增量赋值的性能更好 2 | ====================== 3 | 4 | .. image:: http://image.iswbm.com/20200804124133.png 5 | 6 | 诸如 ``+=`` 和 ``*=`` 这些运算符,叫做 增量赋值运算符。 7 | 8 | 这里使用用 += 举例,以下两种写法,在效果上是等价的。 9 | 10 | .. code:: python 11 | 12 | # 第一种 13 | a = 1 ; a += 1 14 | 15 | # 第二种 16 | a = 1; a = a + 1 17 | 18 | ``+=`` 其背后使用的魔法方法是 19 | ``__iadd__``\ ,如果没有实现这个方法则会退而求其次,使用 ``__add__`` 。 20 | 21 | 这两种写法有什么区别呢? 22 | 23 | 用列表举例 a += b,使用 ``__add__`` 的话就像是使用了a.extend(b),如果使用 24 | ``__add__`` 的话,则是 a = 25 | a+b,前者是直接在原列表上进行扩展,而后者是先从原列表中取出值,在一个新的列表中进行扩展,然后再将新的列表对象返回给变量,显然后者的消耗要大些。 26 | 27 | 所以在能使用增量赋值的时候尽量使用它。 28 | -------------------------------------------------------------------------------- /source/c06/c06_05.md: -------------------------------------------------------------------------------- 1 | # 6.5 变量名与保留关键字冲突怎么办? 2 | 3 | ![](http://image.iswbm.com/20200804124133.png) 4 | 5 | 所有的编程语言都有一些保留关键字,这是代码得以编译/解释的基础。 6 | 7 | 有了这些关键字就组成了语法,当你的变量名和这些保留关键字冲突时,该怎么办呢? 8 | 9 | 在回答这个问题前,先要看看 Python 中的保留关键字有哪些? 10 | 11 | Python 的关键字,可以通过 keyword 这个模块列出来,一共有 33 个。 12 | 13 | ```python 14 | >>> import keyword; 15 | >>> print('\n'.join(keyword.kwlist)) 16 | False 17 | None 18 | True 19 | and 20 | as 21 | assert 22 | break 23 | class 24 | continue 25 | def 26 | del 27 | elif 28 | else 29 | except 30 | finally 31 | for 32 | from 33 | global 34 | if 35 | import 36 | in 37 | is 38 | lambda 39 | nonlocal 40 | not 41 | or 42 | pass 43 | raise 44 | return 45 | try 46 | while 47 | with 48 | yield 49 | >>> len(keyword.kwlist) 50 | 33 51 | ``` 52 | 53 | 使用这些关键字来做为变量名,是会报语法错误的。 54 | 55 | ```python 56 | >>> try = True 57 | File "", line 1 58 | try = True 59 | ^ 60 | SyntaxError: invalid syntax 61 | ``` 62 | 63 | 关于这个问题,[PEP8](https://www.python.org/dev/peps/pep-0008/) 建议当你想使用的变量名被关键字所占用时,可以使用 `变量_` 这样在变量后面加一个单下划线的形式来命名,这种后缀一下划线的方式优先于缩写或拼写错误。 64 | 65 | ![](http://image.iswbm.com/20200823203106.png) 66 | 67 | 有了 PEP8 做为指导,我们可以这样子写了 68 | 69 | ```python 70 | >>> try_ = True 71 | ``` 72 | 73 | 74 | 75 | -------------------------------------------------------------------------------- /source/c06/c06_05.rst: -------------------------------------------------------------------------------- 1 | 6.5 变量名与保留关键字冲突怎么办? 2 | ================================== 3 | 4 | .. image:: http://image.iswbm.com/20200804124133.png 5 | 6 | 所有的编程语言都有一些保留关键字,这是代码得以编译/解释的基础。 7 | 8 | 有了这些关键字就组成了语法,当你的变量名和这些保留关键字冲突时,该怎么办呢? 9 | 10 | 在回答这个问题前,先要看看 Python 中的保留关键字有哪些? 11 | 12 | Python 的关键字,可以通过 keyword 这个模块列出来,一共有 33 个。 13 | 14 | .. code:: python 15 | 16 | >>> import keyword; 17 | >>> print('\n'.join(keyword.kwlist)) 18 | False 19 | None 20 | True 21 | and 22 | as 23 | assert 24 | break 25 | class 26 | continue 27 | def 28 | del 29 | elif 30 | else 31 | except 32 | finally 33 | for 34 | from 35 | global 36 | if 37 | import 38 | in 39 | is 40 | lambda 41 | nonlocal 42 | not 43 | or 44 | pass 45 | raise 46 | return 47 | try 48 | while 49 | with 50 | yield 51 | >>> len(keyword.kwlist) 52 | 33 53 | 54 | 使用这些关键字来做为变量名,是会报语法错误的。 55 | 56 | .. code:: python 57 | 58 | >>> try = True 59 | File "", line 1 60 | try = True 61 | ^ 62 | SyntaxError: invalid syntax 63 | 64 | 关于这个问题,\ `PEP8 `__ 65 | 建议当你想使用的变量名被关键字所占用时,可以使用 ``变量_`` 66 | 这样在变量后面加一个单下划线的形式来命名,这种后缀一下划线的方式优先于缩写或拼写错误。 67 | 68 | .. image:: http://image.iswbm.com/20200823203106.png 69 | 70 | 有了 PEP8 做为指导,我们可以这样子写了 71 | 72 | .. code:: python 73 | 74 | >>> try_ = True 75 | -------------------------------------------------------------------------------- /source/c06/c06_06.md: -------------------------------------------------------------------------------- 1 | # 6.6 不想让子类继承的变量名该怎么写? 2 | 3 | ![](http://image.iswbm.com/20200804124133.png) 4 | 5 | 先来看下面这段代码 6 | 7 | ```python 8 | class Parent: 9 | def __init__(self): 10 | self.name = "MING" 11 | 12 | class Son(Parent): 13 | def __init__(self): 14 | self.name = "Xiao MING" 15 | 16 | bar = Son() 17 | print(bar.name) 18 | # 输出: Xiao MING 19 | ``` 20 | 21 | Bar 作为 Foo 的子类,会继承父类的 name 属性。 22 | 23 | 如果有一些属性,是父类自己独有的,不想被子类继承,该怎么写呢? 24 | 25 | 可以在属性前面加两个下划线,两个类的定义如下 26 | 27 | ```python 28 | class Parent: 29 | def __init__(self): 30 | self.name = "MING" 31 | self.__wife = "Julia" 32 | 33 | class Son(Parent): 34 | def __init__(self): 35 | self.name = "Xiao MING" 36 | super().__init__() 37 | ``` 38 | 39 | 从本章节的第一篇文章([6.1 不要直接调用类的私有方法](https://magic.iswbm.com/c06/c06_01.html))我们知道了私有的变量或函数,是不能直接调用的,需要用这样的形式才能访问 `_类名__变量名`。 40 | 41 | 从上面的代码中,可以看到 Son 类的实例并没有初始化 `__wife` 属性,虽然 Parent 类的实例有该属性,但由于这个属性是父类私有的,子类是无法访问的。因此当子类想要访问时,是会提示该变量不存在。 42 | 43 | 验证过程如下: 44 | 45 | ![](http://image.iswbm.com/20200823205210.png) 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /source/c06/c06_06.rst: -------------------------------------------------------------------------------- 1 | 6.6 不想让子类继承的变量名该怎么写? 2 | ==================================== 3 | 4 | .. image:: http://image.iswbm.com/20200804124133.png 5 | 6 | 先来看下面这段代码 7 | 8 | .. code:: python 9 | 10 | class Parent: 11 | def __init__(self): 12 | self.name = "MING" 13 | 14 | class Son(Parent): 15 | def __init__(self): 16 | self.name = "Xiao MING" 17 | 18 | bar = Son() 19 | print(bar.name) 20 | # 输出: Xiao MING 21 | 22 | Bar 作为 Foo 的子类,会继承父类的 name 属性。 23 | 24 | 如果有一些属性,是父类自己独有的,不想被子类继承,该怎么写呢? 25 | 26 | 可以在属性前面加两个下划线,两个类的定义如下 27 | 28 | .. code:: python 29 | 30 | class Parent: 31 | def __init__(self): 32 | self.name = "MING" 33 | self.__wife = "Julia" 34 | 35 | class Son(Parent): 36 | def __init__(self): 37 | self.name = "Xiao MING" 38 | super().__init__() 39 | 40 | 从本章节的第一篇文章(\ `6.1 41 | 不要直接调用类的私有方法 `__\ )我们知道了私有的变量或函数,是不能直接调用的,需要用这样的形式才能访问 42 | ``_类名__变量名``\ 。 43 | 44 | 从上面的代码中,可以看到 Son 类的实例并没有初始化 ``__wife`` 属性,虽然 45 | Parent 46 | 类的实例有该属性,但由于这个属性是父类私有的,子类是无法访问的。因此当子类想要访问时,是会提示该变量不存在。 47 | 48 | 验证过程如下: 49 | 50 | .. image:: http://image.iswbm.com/20200823205210.png 51 | -------------------------------------------------------------------------------- /source/c06/c06_07.md: -------------------------------------------------------------------------------- 1 | # 6.7 利用 any 代替 for 循环 2 | 3 | ![](http://image.iswbm.com/20200804124133.png) 4 | 5 | 在某些场景下,我们需要判断是否满足某一组集合中任意一个条件 6 | 7 | 这时候,很多同学自然会想到使用 for 循环。 8 | 9 | ```python 10 | found = False 11 | for thing in things: 12 | if thing == other_thing: 13 | found = True 14 | break 15 | ``` 16 | 17 | 但其实更好的写法,是使用 `any()` 函数,能够使这段代码变得更加清晰、简洁 18 | 19 | ```python 20 | found = any(thing == other_thing for thing in things) 21 | ``` 22 | 23 | 使用 any 并不会减少 for 循环的次数,只要有一个条件为 True,any 就能得到结果。 24 | 25 | 同理,当你需要判断是否满足某一组集合中所有条件,也可以使用 `all()` 函数。 26 | 27 | ```python 28 | found = all(thing == other_thing for thing in things) 29 | ``` 30 | 31 | 只要有一个不满足条件,all 函数的结果就会立刻返回 False 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /source/c06/c06_07.rst: -------------------------------------------------------------------------------- 1 | 6.7 利用 any 代替 for 循环 2 | ========================== 3 | 4 | .. image:: http://image.iswbm.com/20200804124133.png 5 | 6 | 在某些场景下,我们需要判断是否满足某一组集合中任意一个条件 7 | 8 | 这时候,很多同学自然会想到使用 for 循环。 9 | 10 | .. code:: python 11 | 12 | found = False 13 | for thing in things: 14 | if thing == other_thing: 15 | found = True 16 | break 17 | 18 | 但其实更好的写法,是使用 ``any()`` 19 | 函数,能够使这段代码变得更加清晰、简洁 20 | 21 | .. code:: python 22 | 23 | found = any(thing == other_thing for thing in things) 24 | 25 | 使用 any 并不会减少 for 循环的次数,只要有一个条件为 True,any 26 | 就能得到结果。 27 | 28 | 同理,当你需要判断是否满足某一组集合中所有条件,也可以使用 ``all()`` 29 | 函数。 30 | 31 | .. code:: python 32 | 33 | found = all(thing == other_thing for thing in things) 34 | 35 | 只要有一个不满足条件,all 函数的结果就会立刻返回 False 36 | -------------------------------------------------------------------------------- /source/c06/c06_08.md: -------------------------------------------------------------------------------- 1 | # 6.8 不同条件分支里应减少重合度 2 | 3 | ![](http://image.iswbm.com/20200804124133.png) 4 | 5 | 如下是一个简单的条件语句模型 6 | 7 | ```python 8 | if A: 9 | 10 | elif B: 11 | 12 | else: 13 | 14 | ``` 15 | 16 | 如果 code_block_2 和 code_block_1 的代码完全一致,那么应该想办法减少代码冗余。 17 | 18 | 这边举个例子,下面这段代码中 19 | 20 | ```python 21 | def process_payment(payment): 22 | if payment.currency == 'USD': 23 | process_standard_payment(payment) 24 | elif payment.currency == 'EUR': 25 | process_standard_payment(payment) 26 | else: 27 | process_international_payment(payment) 28 | ``` 29 | 30 | 其实更好的做法是用 in 来合并条件一和条件二 31 | 32 | ```python 33 | def process_payment(payment): 34 | if payment.currency in ('USD', 'EUR'): 35 | process_standard_payment(payment) 36 | else: 37 | process_international_payment(payment) 38 | ``` 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /source/c06/c06_08.rst: -------------------------------------------------------------------------------- 1 | 6.8 不同条件分支里应减少重合度 2 | ============================== 3 | 4 | .. image:: http://image.iswbm.com/20200804124133.png 5 | 6 | 如下是一个简单的条件语句模型 7 | 8 | .. code:: python 9 | 10 | if A: 11 | 12 | elif B: 13 | 14 | else: 15 | 16 | 17 | 如果 code_block_2 和 code_block_1 18 | 的代码完全一致,那么应该想办法减少代码冗余。 19 | 20 | 这边举个例子,下面这段代码中 21 | 22 | .. code:: python 23 | 24 | def process_payment(payment): 25 | if payment.currency == 'USD': 26 | process_standard_payment(payment) 27 | elif payment.currency == 'EUR': 28 | process_standard_payment(payment) 29 | else: 30 | process_international_payment(payment) 31 | 32 | 其实更好的做法是用 in 来合并条件一和条件二 33 | 34 | .. code:: python 35 | 36 | def process_payment(payment): 37 | if payment.currency in ('USD', 'EUR'): 38 | process_standard_payment(payment) 39 | else: 40 | process_international_payment(payment) 41 | -------------------------------------------------------------------------------- /source/c06/c06_09.md: -------------------------------------------------------------------------------- 1 | # 6.9 如无必要,勿增实体噢 2 | 3 | ![](http://image.iswbm.com/20200804124133.png) 4 | 5 | ## 删除没必要的调用`keys()` 6 | 7 | 字典是由一个个的键值对组成的,如果你遍历字典时只需要访问键,用不到值,有很多同学会用下面这种方式: 8 | 9 | ```python 10 | for currency in currencies.keys(): 11 | process(currency) 12 | ``` 13 | 14 | 在这种情况下,不需要调用keys(),因为遍历字典时的默认行为是遍历键。 15 | 16 | ```python 17 | for currency in currencies: 18 | process(currency) 19 | ``` 20 | 21 | 现在,该代码更加简洁,易于阅读,并且避免调用函数会带来性能改进。 22 | 23 | ## 简化序列比较 24 | 25 | 我们经常要做的是在尝试对列表或序列进行操作之前检查列表或序列是否包含元素。 26 | 27 | ```python 28 | if len(list_of_hats) > 0: 29 | hat_to_wear = choose_hat(list_of_hats) 30 | ``` 31 | 32 | 使用Python的方法则更加简单:如果Python列表和序列具有元素,则返回为True,否则为False: 33 | 34 | ```python 35 | if list_of_hats: 36 | hat_to_wear = choose_hat(list_of_hats) 37 | ``` 38 | 39 | ## 仅使用一次的内联变量 40 | 41 | 我们在很多代码中经常看到,有些同学分配结果给变量,然后马上返回它,例如, 42 | 43 | ```python 44 | def state_attributes(self): 45 | """Return the state attributes.""" 46 | state_attr = { 47 | ATTR_CODE_FORMAT: self.code_format, 48 | ATTR_CHANGED_BY: self.changed_by, 49 | } 50 | return state_attr 51 | ``` 52 | 53 | 如果直接返回,则更加直观、简洁, 54 | 55 | ```python 56 | def state_attributes(self): 57 | """Return the state attributes.""" 58 | return { 59 | ATTR_CODE_FORMAT: self.code_format, 60 | ATTR_CHANGED_BY: self.changed_by, 61 | } 62 | ``` 63 | 64 | 这样可以缩短代码并删除不必要的变量,从而减轻了读取函数的负担。 65 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /source/c06/c06_09.rst: -------------------------------------------------------------------------------- 1 | 6.9 如无必要,勿增实体噢 2 | ======================== 3 | 4 | .. image:: http://image.iswbm.com/20200804124133.png 5 | 6 | 删除没必要的调用\ ``keys()`` 7 | ---------------------------- 8 | 9 | 字典是由一个个的键值对组成的,如果你遍历字典时只需要访问键,用不到值,有很多同学会用下面这种方式: 10 | 11 | .. code:: python 12 | 13 | for currency in currencies.keys(): 14 | process(currency) 15 | 16 | 在这种情况下,不需要调用keys(),因为遍历字典时的默认行为是遍历键。 17 | 18 | .. code:: python 19 | 20 | for currency in currencies: 21 | process(currency) 22 | 23 | 现在,该代码更加简洁,易于阅读,并且避免调用函数会带来性能改进。 24 | 25 | 简化序列比较 26 | ------------ 27 | 28 | 我们经常要做的是在尝试对列表或序列进行操作之前检查列表或序列是否包含元素。 29 | 30 | .. code:: python 31 | 32 | if len(list_of_hats) > 0: 33 | hat_to_wear = choose_hat(list_of_hats) 34 | 35 | 使用Python的方法则更加简单:如果Python列表和序列具有元素,则返回为True,否则为False: 36 | 37 | .. code:: python 38 | 39 | if list_of_hats: 40 | hat_to_wear = choose_hat(list_of_hats) 41 | 42 | 仅使用一次的内联变量 43 | -------------------- 44 | 45 | 我们在很多代码中经常看到,有些同学分配结果给变量,然后马上返回它,例如, 46 | 47 | .. code:: python 48 | 49 | def state_attributes(self): 50 | """Return the state attributes.""" 51 | state_attr = { 52 | ATTR_CODE_FORMAT: self.code_format, 53 | ATTR_CHANGED_BY: self.changed_by, 54 | } 55 | return state_attr 56 | 57 | 如果直接返回,则更加直观、简洁, 58 | 59 | .. code:: python 60 | 61 | def state_attributes(self): 62 | """Return the state attributes.""" 63 | return { 64 | ATTR_CODE_FORMAT: self.code_format, 65 | ATTR_CHANGED_BY: self.changed_by, 66 | } 67 | 68 | 这样可以缩短代码并删除不必要的变量,从而减轻了读取函数的负担。 69 | -------------------------------------------------------------------------------- /source/c06/c06_10.md: -------------------------------------------------------------------------------- 1 | # 6.10 保持代码的简洁与可诗性 2 | 3 | ![](http://image.iswbm.com/20200804124133.png) 4 | 5 | ## 将条件简化为return语句 6 | 7 | 如果,我们实现的函数要返回一个布尔型的结果,通常会这样去做, 8 | 9 | ```python 10 | def function(): 11 | if isinstance(a, b) or issubclass(b, a): 12 | returnTrue 13 | returnFalse 14 | ``` 15 | 16 | 但是,对比这样,直接返回结果会更加明智: 17 | 18 | ```python 19 | def function(): 20 | return isinstance(a, b) or issubclass(b, a) 21 | ``` 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /source/c06/c06_10.rst: -------------------------------------------------------------------------------- 1 | 6.10 保持代码的简洁与可诗性 2 | =========================== 3 | 4 | .. image:: http://image.iswbm.com/20200804124133.png 5 | 6 | 将条件简化为return语句 7 | ---------------------- 8 | 9 | 如果,我们实现的函数要返回一个布尔型的结果,通常会这样去做, 10 | 11 | .. code:: python 12 | 13 | def function(): 14 | if isinstance(a, b) or issubclass(b, a): 15 | returnTrue 16 | returnFalse 17 | 18 | 但是,对比这样,直接返回结果会更加明智: 19 | 20 | .. code:: python 21 | 22 | def function(): 23 | return isinstance(a, b) or issubclass(b, a) 24 | -------------------------------------------------------------------------------- /source/c06/c06_11.md: -------------------------------------------------------------------------------- 1 | # 6.11 给模块的私有属性上保险 2 | 3 | ![](http://image.iswbm.com/20200804124133.png) 4 | 5 | ## 保护对象 6 | 7 | 有的朋友,喜欢简单粗暴的使用 `from x import *` 来导入 x 模块中的所有对象,实际上有一些对象或者变量,是实现细节,不需要暴露给导入方的,因为导入了也用不上。 8 | 9 | 对于这些变量或者对象,就可以在前面其名字前加上下划线,只要在变量名前加上下划线,就属于 "保护对象"。 10 | 11 | 使用 `from x import *` 后,这些 "保护对象" 是会直接跳过导入。 12 | 13 | 比如下面这些代码中,只有 drive 函数才会被 `from x import *` 所导入 14 | 15 | ```python 16 | _moto_type = 'L15b2' 17 | _wheel_type = 'michelin' 18 | 19 | def drive(): 20 | _start_engine() 21 | _drive_wheel() 22 | 23 | def _start_engine(): 24 | print('start engine %s'%_moto_type) 25 | 26 | def _drive_wheel(): 27 | print('drive wheel %s'%_wheel_type) 28 | ``` 29 | 30 | ## 突破保护 31 | 32 | 前面之所以说是“保护”并不是“私有”,是因为Python没有提供解释器机制来控制访问权限。我们依然可以访问这些属性: 33 | 34 | ```python 35 | import tools 36 | tools._moto_type = 'EA211' 37 | tools.drive() 38 | ``` 39 | 40 | 以上代码,以越过“保护属性”。此外,还有两种方法能突破这个限制,一种是将“私有属性”添加到tool.py文件的`__all__`列表里,使`from tools import *`也导入这些本该隐藏的属性。 41 | 42 | ```python 43 | __all__ = ['drive','_moto_type','_wheel_type'] 44 | ``` 45 | 46 | 另一种是导入时指定“受保护属性”名。 47 | 48 | ```python 49 | from tools import drive,_start_engine 50 | _start_engine() 51 | ``` 52 | 53 | 甚至是,使用`import tools`也可以轻易突破保护限制。所以可见,“保护属性”是一种简单的隐藏机制,只有在`from tools import *`时,由解释器提供简单的保护,但是可以轻易突破。这种保护更多地依赖程序员的共识:不访问、修改“保护属性”。除此之外,有没有更安全的保护机制呢?有,就是下一部分讨论的私有变量。 54 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /source/c06/c06_11.rst: -------------------------------------------------------------------------------- 1 | 6.11 给模块的私有属性上保险 2 | =========================== 3 | 4 | .. image:: http://image.iswbm.com/20200804124133.png 5 | 6 | 保护对象 7 | -------- 8 | 9 | 有的朋友,喜欢简单粗暴的使用 ``from x import *`` 来导入 x 10 | 模块中的所有对象,实际上有一些对象或者变量,是实现细节,不需要暴露给导入方的,因为导入了也用不上。 11 | 12 | 对于这些变量或者对象,就可以在前面其名字前加上下划线,只要在变量名前加上下划线,就属于 13 | “保护对象”。 14 | 15 | 使用 ``from x import *`` 后,这些 “保护对象” 是会直接跳过导入。 16 | 17 | 比如下面这些代码中,只有 drive 函数才会被 ``from x import *`` 所导入 18 | 19 | .. code:: python 20 | 21 | _moto_type = 'L15b2' 22 | _wheel_type = 'michelin' 23 | 24 | def drive(): 25 | _start_engine() 26 | _drive_wheel() 27 | 28 | def _start_engine(): 29 | print('start engine %s'%_moto_type) 30 | 31 | def _drive_wheel(): 32 | print('drive wheel %s'%_wheel_type) 33 | 34 | 突破保护 35 | -------- 36 | 37 | 前面之所以说是“保护”并不是“私有”,是因为Python没有提供解释器机制来控制访问权限。我们依然可以访问这些属性: 38 | 39 | .. code:: python 40 | 41 | import tools 42 | tools._moto_type = 'EA211' 43 | tools.drive() 44 | 45 | 以上代码,以越过“保护属性”。此外,还有两种方法能突破这个限制,一种是将“私有属性”添加到tool.py文件的\ ``__all__``\ 列表里,使\ ``from tools import *``\ 也导入这些本该隐藏的属性。 46 | 47 | .. code:: python 48 | 49 | __all__ = ['drive','_moto_type','_wheel_type'] 50 | 51 | 另一种是导入时指定“受保护属性”名。 52 | 53 | .. code:: python 54 | 55 | from tools import drive,_start_engine 56 | _start_engine() 57 | 58 | 甚至是,使用\ ``import tools``\ 也可以轻易突破保护限制。所以可见,“保护属性”是一种简单的隐藏机制,只有在\ ``from tools import *``\ 时,由解释器提供简单的保护,但是可以轻易突破。这种保护更多地依赖程序员的共识:不访问、修改“保护属性”。除此之外,有没有更安全的保护机制呢?有,就是下一部分讨论的私有变量。 59 | -------------------------------------------------------------------------------- /source/c06/c06_12.md: -------------------------------------------------------------------------------- 1 | # 6.12 变量不能与保留关键字重名 2 | 3 | ![](http://image.iswbm.com/20200804124133.png) 4 | 5 | 在 Python 中有很多的保留关键字,这些关键字的使用,不需要我们定义,也不需要我们导入,只要你进入到了 Python 的环境中,就可以立即使用。 6 | 7 | 使用如下方法,可以查看 Python 中的保留关键字 8 | 9 | ```python 10 | >>> import keyword 11 | >>> keyword.kwlist 12 | ['and', 'as', 'assert', 'break', 'class', 'continue', 'def', 'del', 'elif', 'else', 'except', 'exec', 'finally', 'for', 'from', 'global', 'if', 'import', 'in', 'is', 'lambda', 'not', 'or', 'pass', 'print', 'raise', 'return', 'try', 'while', 'with', 'yield'] 13 | ``` 14 | 15 | 而很尴尬的是,如果你在日常编码中,不经意地用到其中的一些关键字,就会产生冲突。 16 | 17 | 比如说 class,这个有类别的意思,可能你也想使用它来作为变量名,如果直接使用,会发生冲突 18 | 19 | 更好的做法是,使用下划线来避免冲突 20 | 21 | ```python 22 | def type_obj_class(name,class_): 23 | pass 24 | 25 | def tag(name,*content,class_): 26 | pass 27 | ``` 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /source/c06/c06_12.rst: -------------------------------------------------------------------------------- 1 | 6.12 变量不能与保留关键字重名 2 | ============================= 3 | 4 | .. image:: http://image.iswbm.com/20200804124133.png 5 | 6 | 在 Python 7 | 中有很多的保留关键字,这些关键字的使用,不需要我们定义,也不需要我们导入,只要你进入到了 8 | Python 的环境中,就可以立即使用。 9 | 10 | 使用如下方法,可以查看 Python 中的保留关键字 11 | 12 | .. code:: python 13 | 14 | >>> import keyword 15 | >>> keyword.kwlist 16 | ['and', 'as', 'assert', 'break', 'class', 'continue', 'def', 'del', 'elif', 'else', 'except', 'exec', 'finally', 'for', 'from', 'global', 'if', 'import', 'in', 'is', 'lambda', 'not', 'or', 'pass', 'print', 'raise', 'return', 'try', 'while', 'with', 'yield'] 17 | 18 | 而很尴尬的是,如果你在日常编码中,不经意地用到其中的一些关键字,就会产生冲突。 19 | 20 | 比如说 21 | class,这个有类别的意思,可能你也想使用它来作为变量名,如果直接使用,会发生冲突 22 | 23 | 更好的做法是,使用下划线来避免冲突 24 | 25 | .. code:: python 26 | 27 | def type_obj_class(name,class_): 28 | pass 29 | 30 | def tag(name,*content,class_): 31 | pass 32 | -------------------------------------------------------------------------------- /source/c07/c07_08.md: -------------------------------------------------------------------------------- 1 | # 7.8 像操作路径一样,操作嵌套字典 2 | 3 | ![](http://image.iswbm.com/20200804124133.png) 4 | 5 | 在使用前先安装它,要注意的是该模块只能在 Python 3.8+ 中使用 6 | 7 | ```shell 8 | $ python3 -m pip install dpath 9 | ``` 10 | 11 | 下边是一个简单的使用案例 12 | 13 | ```python 14 | import dpath.util 15 | 16 | data = { 17 | "foo": { 18 | "bar": { 19 | "a": 10, 20 | "b": 20, 21 | "c": [], 22 | "d": ['red', 'buggy', 'bumpers'], 23 | } 24 | } 25 | } 26 | 27 | print(dpath.util.get(data, "/foo/bar/d")) 28 | ``` 29 | 30 | 使用 `[ab]` 会把 键为 `a` 和 `b` 的都筛选出来 31 | 32 | ```python 33 | print(dpath.util.search(data, "/foo/bar/[ab]")) 34 | # output: {'foo': {'bar': {'a': 10, 'b': 20}}} 35 | ``` 36 | 37 | 获取所有匹配的键值对的 value 值列表 38 | 39 | ```python 40 | print(dpath.util.values(data, "/foo/bar/*")) 41 | # output: [10, 20, [], ['red', 'buggy', 'bumpers']] 42 | ``` 43 | 44 | 45 | 46 | 更多案例,请前往 [官方文档](https://pypi.org/project/dpath/) 查阅。 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /source/c07/c07_08.rst: -------------------------------------------------------------------------------- 1 | 7.8 像操作路径一样,操作嵌套字典 2 | ================================ 3 | 4 | .. image:: http://image.iswbm.com/20200804124133.png 5 | 6 | 在使用前先安装它,要注意的是该模块只能在 Python 3.8+ 中使用 7 | 8 | .. code:: shell 9 | 10 | $ python3 -m pip install dpath 11 | 12 | 下边是一个简单的使用案例 13 | 14 | .. code:: python 15 | 16 | import dpath.util 17 | 18 | data = { 19 | "foo": { 20 | "bar": { 21 | "a": 10, 22 | "b": 20, 23 | "c": [], 24 | "d": ['red', 'buggy', 'bumpers'], 25 | } 26 | } 27 | } 28 | 29 | print(dpath.util.get(data, "/foo/bar/d")) 30 | 31 | 使用 ``[ab]`` 会把 键为 ``a`` 和 ``b`` 的都筛选出来 32 | 33 | .. code:: python 34 | 35 | print(dpath.util.search(data, "/foo/bar/[ab]")) 36 | # output: {'foo': {'bar': {'a': 10, 'b': 20}}} 37 | 38 | 获取所有匹配的键值对的 value 值列表 39 | 40 | .. code:: python 41 | 42 | print(dpath.util.values(data, "/foo/bar/*")) 43 | # output: [10, 20, [], ['red', 'buggy', 'bumpers']] 44 | 45 | 更多案例,请前往 `官方文档 `__ 查阅。 46 | -------------------------------------------------------------------------------- /source/c07/c07_09.md: -------------------------------------------------------------------------------- 1 | # 7.9 读取文件中任意行的数据 2 | 3 | ![](http://image.iswbm.com/20200804124133.png) 4 | 5 | `linecache` 是 Python 中的一个内置模块。 6 | 7 | 它允许从任何文件中获取任意行,同时尝试使用缓存进行内部优化,这是一种常见的情况,即从单个文件读取多行。它被`traceback`模块用来检索包含在格式化回溯中的源代码行。 8 | 9 | 这是一个简单的例子。 10 | 11 | ```python 12 | >>> import linecache 13 | >>> linecache.getline('/etc/passwd', 4) 14 | 'sys:x:3:3:sys:/dev:/bin/sh\n' 15 | ``` 16 | 17 | 如果你指定的行数超过了文件原有的行数,该函数也不会抛出错误,而是返回空字符串。 18 | 19 | ```python 20 | >>> import linecache 21 | >>> linecache.getline('/etc/passwd', 10000) 22 | 23 | >>> 24 | ``` 25 | 26 | -------------------------------------------------------------------------------- /source/c07/c07_09.rst: -------------------------------------------------------------------------------- 1 | 7.9 读取文件中任意行的数据 2 | ========================== 3 | 4 | .. image:: http://image.iswbm.com/20200804124133.png 5 | 6 | ``linecache`` 是 Python 中的一个内置模块。 7 | 8 | 它允许从任何文件中获取任意行,同时尝试使用缓存进行内部优化,这是一种常见的情况,即从单个文件读取多行。它被\ ``traceback``\ 模块用来检索包含在格式化回溯中的源代码行。 9 | 10 | 这是一个简单的例子。 11 | 12 | .. code:: python 13 | 14 | >>> import linecache 15 | >>> linecache.getline('/etc/passwd', 4) 16 | 'sys:x:3:3:sys:/dev:/bin/sh\n' 17 | 18 | 如果你指定的行数超过了文件原有的行数,该函数也不会抛出错误,而是返回空字符串。 19 | 20 | .. code:: python 21 | 22 | >>> import linecache 23 | >>> linecache.getline('/etc/passwd', 10000) 24 | 25 | >>> 26 | -------------------------------------------------------------------------------- /source/c07/c07_11.md: -------------------------------------------------------------------------------- 1 | # 7.11 国际化模块,让翻译更优雅 2 | 3 | ![](http://image.iswbm.com/20200804124133.png) 4 | 5 | ## 国际化与本地化 6 | 7 | 国际化 (internationalization),简称 **i18n** 8 | 9 | 很多人并不知道,为什么要叫 i18n 呢?怎么谐音都不对。 10 | 11 | 实际上 18 是指在 ”internationalization” 这个单词中,i 和 n之间有18个字母。 12 | 13 | 14 | 15 | 而与之相对的,本地化(localization),简称 L10 n,10 就是指在 ”localization”这个单词中,l 和 n 之间有10个字母 16 | 17 | 本地化是指使一个国际化的软件为了在某个特定地区使用而进行实际翻译的过程。 18 | 19 | 国际化的软件具备这样一种能力,当软件被移植到不同的语言及地区时,软件本身不用做内部工程上的改变或修正。 20 | 21 | ## gettext 模块 22 | 23 | gettext 是一套 GNU下的国际化工具。主要有工具: 24 | 25 | - xgettext: 从源码中抽取字符串,生成po文件(portable object) 26 | - msgfmt: 将po文件编译成mo文件(machine object) 27 | - gettext: 进行翻译,如果找不到gettext命令,或者找不到msgfmt命令。请重新安装一遍gettext套件。 28 | 29 | 30 | 很多系统中都内置了 gettext 模块 31 | 32 | 如果你在 ubuntu系统中,可能需要如下命令进行安装 33 | 34 | ``` 35 | sudo apt-get install gettext 36 | ``` 37 | 38 | ## 简单示例演示 39 | 40 | 首先新建一个目录 41 | 42 | ```shell 43 | $ mkdir -p locale/zh_CN/LC_MESSAGES 44 | ``` 45 | 46 | 然后在这个目录下新建一个 `hello.po` 文件 47 | 48 | ``` 49 | msgid "hello world" 50 | msgstr "你好世界" 51 | ``` 52 | 53 | 然后执行如下一条命令,将 po 文件翻译成 mo文件 54 | 55 | ```shell 56 | $ msgfmt locale/zh_CN/LC_MESSAGES/hello.po -o locale/zh_CN/LC_MESSAGES/hello.mo 57 | ``` 58 | 59 | 然后在 local 同级目录下进入 Console 模式,就可以使用 `_` 进行翻译了,为什么 `_` 能这么用,原因是 `zh.install() ` 这个调用将其绑定到了 Python 内建命名空间中,以便在应用程序的所有模块中轻松访问它。 60 | 61 | ```python 62 | >>> import gettext 63 | >>> zh = gettext.translation("hello", "locale", languages=["zh_CN"]) 64 | >>> zh.install() 65 | >>> _('hello world') 66 | '你好世界' 67 | ``` 68 | 69 | 70 | 71 | -------------------------------------------------------------------------------- /source/c07/c07_11.rst: -------------------------------------------------------------------------------- 1 | 7.11 国际化模块,让翻译更优雅 2 | ============================= 3 | 4 | .. image:: http://image.iswbm.com/20200804124133.png 5 | 6 | 国际化与本地化 7 | -------------- 8 | 9 | 国际化 (internationalization),简称 **i18n** 10 | 11 | 很多人并不知道,为什么要叫 i18n 呢?怎么谐音都不对。 12 | 13 | 实际上 18 是指在 ”internationalization” 这个单词中,i 和 14 | n之间有18个字母。 15 | 16 | 而与之相对的,本地化(localization),简称 L10 n,10 就是指在 17 | ”localization”这个单词中,l 和 n 之间有10个字母 18 | 19 | 本地化是指使一个国际化的软件为了在某个特定地区使用而进行实际翻译的过程。 20 | 21 | 国际化的软件具备这样一种能力,当软件被移植到不同的语言及地区时,软件本身不用做内部工程上的改变或修正。 22 | 23 | gettext 模块 24 | ------------ 25 | 26 | gettext 是一套 GNU下的国际化工具。主要有工具: 27 | 28 | - xgettext: 从源码中抽取字符串,生成po文件(portable object) 29 | - msgfmt: 将po文件编译成mo文件(machine object) 30 | - gettext: 31 | 进行翻译,如果找不到gettext命令,或者找不到msgfmt命令。请重新安装一遍gettext套件。 32 | 33 | 很多系统中都内置了 gettext 模块 34 | 35 | 如果你在 ubuntu系统中,可能需要如下命令进行安装 36 | 37 | :: 38 | 39 | sudo apt-get install gettext 40 | 41 | 简单示例演示 42 | ------------ 43 | 44 | 首先新建一个目录 45 | 46 | .. code:: shell 47 | 48 | $ mkdir -p locale/zh_CN/LC_MESSAGES 49 | 50 | 然后在这个目录下新建一个 ``hello.po`` 文件 51 | 52 | :: 53 | 54 | msgid "hello world" 55 | msgstr "你好世界" 56 | 57 | 然后执行如下一条命令,将 po 文件翻译成 mo文件 58 | 59 | .. code:: shell 60 | 61 | $ msgfmt locale/zh_CN/LC_MESSAGES/hello.po -o locale/zh_CN/LC_MESSAGES/hello.mo 62 | 63 | 然后在 local 同级目录下进入 Console 模式,就可以使用 ``_`` 64 | 进行翻译了,为什么 ``_`` 能这么用,原因是 ``zh.install()`` 65 | 这个调用将其绑定到了 Python 66 | 内建命名空间中,以便在应用程序的所有模块中轻松访问它。 67 | 68 | .. code:: python 69 | 70 | >>> import gettext 71 | >>> zh = gettext.translation("hello", "locale", languages=["zh_CN"]) 72 | >>> zh.install() 73 | >>> _('hello world') 74 | '你好世界' 75 | -------------------------------------------------------------------------------- /source/chapters/p01.rst: -------------------------------------------------------------------------------- 1 | ============================= 2 | 第一章:魔法冷知识 3 | ============================= 4 | 5 | 这个章节记录了一些大多数开发者并不知晓的冷知识,内容基本延续 v1.0 。 6 | 7 | 本章节,会持续更新,敬请关注... 8 | 9 | --------------------------- 10 | 11 | 12 | .. toctree:: 13 | :maxdepth: 1 14 | :glob: 15 | 16 | ../c01/* 17 | 18 | 19 | -------------------------------------------------------------------------------- /source/chapters/p02.rst: -------------------------------------------------------------------------------- 1 | ============================= 2 | 第二章:魔法命令行 3 | ============================= 4 | 5 | 这个章节是全新的内容,主要介绍的是 Python Shell 的一些冷门玩法,这里面的内容,应该足够让你惊叹。 6 | 7 | ------------------- 8 | 9 | .. toctree:: 10 | :maxdepth: 1 11 | :glob: 12 | 13 | ../c02/* 14 | 15 | 16 | -------------------------------------------------------------------------------- /source/chapters/p03.rst: -------------------------------------------------------------------------------- 1 | ============================= 2 | 第三章:炫技魔法操作 3 | ============================= 4 | 5 | 这个章节是取自我个人公众号原创专辑《Python炫技操作》里的文章,其中的多篇文章成为了爆款文章,不少大号均有转载。很多网友看完后直呼 "卧槽,居然还能这样?!",如果你之前没有看过这几篇文章,那么你读这一章一定会大有收获。 6 | 7 | 本章节,会持续更新,敬请关注… 8 | 9 | ------------------------- 10 | 11 | .. toctree:: 12 | :maxdepth: 1 13 | :glob: 14 | 15 | ../c03/* 16 | 17 | 18 | -------------------------------------------------------------------------------- /source/chapters/p04.rst: -------------------------------------------------------------------------------- 1 | ============================= 2 | 第四章:魔法进阶扫盲 3 | ============================= 4 | 5 | 这一章节主要深入理解 Python 中那些难点,将这些难点逐个击破? 6 | 7 | 比如: 8 | 9 | 如何写出一个可以带参数的装饰器呢? 10 | 11 | 装饰器可以装饰函数,那么你知道如何装饰类吗? 12 | 13 | 描述符的访问规则是什么? 14 | 15 | 描述符在实际开发中有哪些使用场景? 16 | 17 | 这些恐怕有不少人都还没有深入学习过,这一章节可扩展的内容有很多,比如元类等,日后会慢慢完善。 18 | 19 | ---------------------------- 20 | 21 | .. toctree:: 22 | :maxdepth: 1 23 | :glob: 24 | 25 | ../c04/* 26 | 27 | 28 | -------------------------------------------------------------------------------- /source/chapters/p05.rst: -------------------------------------------------------------------------------- 1 | ============================= 2 | 第五章:魔法开发技巧 3 | ============================= 4 | 5 | 这个章节可能会是很多人感兴趣的,因为里面介绍的是所有开发者都有可能用到的开发技巧,掌握这些代码编写技巧,对提高你代码的可读性、优雅性会很有帮助。 6 | 7 | 本章节,会持续更新,敬请关注… 8 | 9 | ---------------------------- 10 | 11 | .. toctree:: 12 | :maxdepth: 1 13 | :glob: 14 | 15 | ../c05/* 16 | 17 | 18 | -------------------------------------------------------------------------------- /source/chapters/p06.rst: -------------------------------------------------------------------------------- 1 | ============================= 2 | 第六章:良好编码习惯 3 | ============================= 4 | 5 | 这个章节会写一些我自己日常开发总结的一些开发习惯,虽然内容还不是很多,但是我已经有了很多的思路,大家再给我点时间。 6 | 7 | 本章节,会持续更新,敬请关注… 8 | 9 | ---------------------------- 10 | 11 | .. toctree:: 12 | :maxdepth: 1 13 | :glob: 14 | 15 | ../c06/* 16 | 17 | 18 | -------------------------------------------------------------------------------- /source/chapters/p07.rst: -------------------------------------------------------------------------------- 1 | ============================= 2 | 第七章:神奇魔法模块 3 | ============================= 4 | 5 | 这个章节会收集一些被市场公认好用的模块,对于编码代码很有帮助,同样还是需要时间慢慢沉淀。 6 | 7 | 本章节,会持续更新,敬请关注… 8 | 9 | ---------------------------- 10 | 11 | .. toctree:: 12 | :maxdepth: 1 13 | :glob: 14 | 15 | ../c07/* 16 | 17 | 18 | -------------------------------------------------------------------------------- /source/index.rst: -------------------------------------------------------------------------------- 1 | .. python-time documentation master file, created by 2 | sphinx-quickstart on Tue Aug 19 03:21:45 2014. 3 | You can adapt this file completely to your liking, but it should at least 4 | contain the root `toctree` directive. 5 | 6 | ================================================ 7 | Python黑魔法手册 8 | ================================================ 9 | 10 | Contents: 11 | 12 | .. toctree:: 13 | :maxdepth: 2 14 | :glob: 15 | 16 | preface 17 | chapters/* 18 | aboutme 19 | roadmap 20 | 21 | 22 | -------------------------------------------------------------------------------- /source/preface.rst: -------------------------------------------------------------------------------- 1 | ================================== 2 | 前言 3 | ================================== 4 | 5 | ---------------------------------- 6 | 关于博客 7 | ---------------------------------- 8 | 这个博客于2020年8月3日发布完成,使用的是 Sphinx 来生成文档,使用 Github 托管文档,并使用 Read the Doc 发布文档。 9 | 10 | 11 | ---------------------------------- 12 | 作者的话 13 | ---------------------------------- 14 | 15 | Python 是一门对编程新手非常友好的语言,通常花个两个月的时间,就能开始自己写代码,做项目。 16 | 17 | 但也因为过于高级,给予了开发者很高的自由度。这本身没有问题,但是想要写出优雅的 Python 代码,需要 Coder 有一定的代码审美能力,才能很好的驾驭。 18 | 19 | 这本电子教程,是我个人花了很多的时间,将自己这些年来写 Python 的一些心得整理所成。 20 | 21 | 内容包含各种你在教材上、培训视频中无法习得的冷门知识,魔法知识,以及开发技巧,不管对于新手还是老手,我想都会有一定的帮助。 22 | 23 | ---------------- 24 | 25 | -------------------------------------------------------------------------------- /source/roadmap.rst: -------------------------------------------------------------------------------- 1 | =========== 2 | Roadmap 3 | =========== 4 | 5 | 2020/08/03: 6 | 7 | :: 8 | 9 | | github项目搭建,readthedocs文档生成。 10 | | 整个项目的框架完成 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /source/robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Sitemap: http://magic.iswbm.com/sitemap.xml 3 | -------------------------------------------------------------------------------- /usercustomize.py: -------------------------------------------------------------------------------- 1 | msg=r""" 2 | 3 | _ooOoo_ 4 | o8888888o __________________________________ 5 | 88" . "88 | 爬虫工程师平安 后端工程师平安 | 6 | (| -_- |) | 数据分析师平安 自动化运维平安 | 7 | O\ = /O <__________________________________| 8 | ____/`---'\____ 9 | . ' \\| |// `. 10 | / \\||| : |||// \ 11 | / _||||| -:- |||||- \ 12 | | | \\\ - /// | | 13 | | \_| ''\---/'' | | 14 | \ .-\__ `-` ___/-. / 15 | ___`. .' /--.--\ `. . __ 16 | ."" '< `.___\_<|>_/___.' >'"". 17 | | | : `- \`.;`\ _ /`;.`/ - ` : | | 18 | \ \ `-. \_ __\ /__ _/ .-` / / 19 | ======`-.____`-.___\_____/___.-`____.-'====== 20 | `=---=' 21 | 22 | ............................................. 23 | 佛祖保佑 永无BUG 24 | """ 25 | print(msg) 26 | --------------------------------------------------------------------------------