├── .gitignore ├── 404.html ├── Gemfile ├── LICENSE ├── README.md ├── _config.yml ├── _includes ├── fix_linenos.html ├── footer_custom.html ├── head.html ├── head_custom.html ├── header_custom.html ├── js │ └── custom.js ├── nav.html ├── title.html └── vendor │ └── anchor_headings.html ├── _layouts ├── about.html ├── default.html ├── home.html ├── page.html ├── post.html ├── table_wrappers.html └── vendor │ └── compress.html ├── assets ├── css │ ├── just-the-docs-dark.css │ ├── just-the-docs-default.css │ └── just-the-docs-light.css ├── images │ ├── just-the-docs.png │ └── search.svg └── js │ ├── just-the-docs.js │ ├── search-data.json │ ├── vendor │ └── lunr.min.js │ └── zzzz-search-data.json ├── book ├── DataStructures │ ├── Coroutines.md │ ├── README.md │ ├── classes.md │ └── generators.md ├── DataTypes │ ├── README.md │ ├── collections.md │ ├── mutation.md │ └── slots_magic.md ├── Extras │ ├── README.md │ ├── c_extensions.md │ ├── onelines.md │ └── targeting_python2_3.md ├── FunctionalProgramming │ ├── Comprehensions.md │ ├── README.md │ ├── enumerate.md │ ├── lambdas.md │ ├── map_n_filter.md │ └── set_data_structure.md ├── Intro-2021.md ├── Introduction.md ├── ProgrammerTools │ ├── README.md │ ├── debugging.md │ ├── introspection.md │ └── virtual_environment.md ├── Syntax │ ├── README.md │ ├── args_kwargs.md │ ├── context_managers.md │ ├── exception.md │ ├── for_else.md │ ├── global_return.md │ ├── open_func.md │ └── ternary_operators.md ├── author.md ├── decorators │ ├── README.md │ ├── deco_with_args.md │ ├── decorators.md │ ├── func_caching.md │ └── your_first_decorator.md ├── donors.md ├── seealso.md └── translator.md ├── code ├── 2.7 │ ├── 10_slot.py │ ├── 12_collection.py │ ├── 13_enumerate.py │ ├── 15_comprehensions.py │ ├── 16_exception.py │ ├── 17_lambda.py │ ├── 19_for_else.py │ ├── 1_args_kwargs.py │ ├── 20_cextend_capi.py │ ├── 20_cextend_ctypes.py │ ├── 21_open.py │ ├── 23_coroutine.py │ ├── 24_cache.py │ ├── 25_contextmanager.py │ ├── 25_contextmanager_generator.py │ ├── 3_generators.py │ ├── 4_map_filter.py │ ├── 5_set_datastruct.py │ ├── 7_decorators.py │ ├── 7_decorators1.py │ ├── 7_decorators_args.py │ ├── 7_decorators_class.py │ ├── 7_decorators_scenario.py │ ├── 8_global_return.py │ ├── 9_mutation.py │ ├── README.md │ ├── add.c │ ├── adder.c │ ├── adder.so │ ├── build │ │ ├── lib.macosx-10.11-intel-2.7 │ │ │ └── addList.so │ │ └── temp.macosx-10.11-intel-2.7 │ │ │ └── adder.o │ ├── example.c │ └── setup.py └── README.md ├── conf.py ├── config.json ├── favicon.ico └── mkdocs.yml /.gitignore: -------------------------------------------------------------------------------- 1 | # Node rules: 2 | ## Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 3 | .grunt 4 | 5 | ## Dependency directory 6 | ## Commenting this out is preferred by some people, see 7 | ## https://docs.npmjs.com/misc/faq#should-i-check-my-node_modules-folder-into-git 8 | node_modules 9 | 10 | # Book build output 11 | # gitbook 12 | _book 13 | # readthedocs 14 | _build 15 | 16 | # eBook build output 17 | *.epub 18 | *.mobi 19 | *.pdf 20 | 21 | _site 22 | *.gem 23 | .bundle 24 | .ruby-version 25 | Gemfile.lock 26 | .jekyll-cache 27 | .jekyll-metadata 28 | .sass-cache 29 | # vendor 30 | -------------------------------------------------------------------------------- /404.html: -------------------------------------------------------------------------------- 1 | --- 2 | permalink: /404.html 3 | layout: default 4 | --- 5 | 6 | 19 | 20 |
21 |

404

22 | 23 |

Page not found :(

24 |

The requested page could not be found.

25 |
26 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source "https://rubygems.org" 2 | # Hello! This is where you manage which Jekyll version is used to run. 3 | # When you want to use a different version, change it below, save the 4 | # file and run `bundle install`. Run Jekyll with `bundle exec`, like so: 5 | # 6 | # bundle exec jekyll serve 7 | # 8 | # This will help ensure the proper Jekyll version is running. 9 | # Happy Jekylling! 10 | # gem "jekyll", "~> 4.2.0" 11 | # This is the default theme for new Jekyll sites. You may change this to anything you like. 12 | gem "just-the-docs" 13 | # If you want to use GitHub Pages, remove the "gem "jekyll"" above and 14 | # uncomment the line below. To upgrade, run `bundle update github-pages`. 15 | gem "github-pages", "~> 217", group: :jekyll_plugins 16 | # If you have any plugins, put them here! 17 | group :jekyll_plugins do 18 | gem "jekyll-feed", "~> 0.12" 19 | end 20 | 21 | # Windows and JRuby does not include zoneinfo files, so bundle the tzinfo-data gem 22 | # and associated library. 23 | platforms :mingw, :x64_mingw, :mswin, :jruby do 24 | gem "tzinfo", "~> 1.2" 25 | gem "tzinfo-data" 26 | end 27 | 28 | # Performance-booster for watching directories on Windows 29 | gem "wdm", "~> 0.1.1", :platforms => [:mingw, :x64_mingw, :mswin] 30 | 31 | 32 | gem "webrick", "~> 1.7" 33 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 主页 3 | nav_order: 0 4 | permalink: / 5 | has_children: true 6 | --- 7 | 8 | 9 | Python进阶 10 | ======= 11 | 12 | 《Python进阶》是《Intermediate Python》的中文译本, 谨以此献给进击的 Python 和 Python 程序员们! 13 | 14 | # 阅读本书 15 | 2021最新目录版,实时更新托管在GithubPages: 16 | 17 | [进入阅读](https://py.eastlakeside.cn){: .btn .btn-green } 18 | 19 | 20 | # 前言 21 | 22 | Python,作为一个"老练"、"小清新"的开发语言,已受到广大才男俊女的喜爱。我们也从最基础的Python粉,经过时间的摧残慢慢的变成了Python老鬼。 23 | 24 | 《Intermediate Python》这本书具有如下几个优点: 25 | 26 | 1. 简单 27 | 2. 易读 28 | 3. 易译 29 | 30 | 这些都不是重点,重点是:**它是一本开脑洞的书**。无论你是Python初学者,还是Python高手,它显现给你的永远是Python里最美好的事物。 31 | 32 | > 33 | 世上语言千万种 34 | 美好事物藏其中 35 | 36 | 译者在翻译过程中,慢慢发现,本书作者的行文方式有着科普作家的风范,--那就是能将晦涩难懂的技术用比较清晰简洁的方式进行呈现,深入浅出的风格在每个章节的讨论中都得到了体现: 37 | 38 | - 每个章节都非常精简,5分钟就能看完,用最简洁的例子精辟地展现了原理。 39 | - 每个章节都会通过疑问,来引导读者主动思考答案。 40 | - 每个章节都引导读者做延伸阅读,让有兴趣的读者能进一步举一反三。 41 | - 每个章节都是独立的,你可以挑选任意的章节开始阅读,而不受影响。 42 | 43 | 总之,这本书非常方便随时选取一个章节进行阅读,而且每次阅读一个章节,你都可能会有一些新的发现。 44 | 45 | ## 原书作者 46 | 47 | 感谢英文原著作者 @yasoob《[Intermediate Python](https://github.com/yasoob/intermediatePython)》,有了他才有了这里的一切。 48 | 49 | ## 译者 50 | 51 | - 总顾问+审校: 刘宇 [@liuyu](https://github.com/liuyu) 52 | - 主译: PyCon老高 [@spawnris](https://github.com/spawnris) 53 | - 主译: matt老苏 [@suqi](https://github.com/suqi) 54 | - 参译: 明源 [@muxueqz](https://github.com/muxueqz) 55 | 56 | ## 欢迎建议指正或直接贡献代码 57 | 58 | 59 | 60 | ### 微信交流群 61 | 62 | 可参考这个issue里的指引来加群: 63 | 64 | -------------------------------------------------------------------------------- /_config.yml: -------------------------------------------------------------------------------- 1 | # Welcome to Jekyll! 2 | # 3 | # This config file is meant for settings that affect your whole blog, values 4 | # which you are expected to set up once and rarely edit after that. If you find 5 | # yourself editing this file very often, consider using Jekyll's data files 6 | # feature for the data you need to update frequently. 7 | # 8 | # For technical reasons, this file is *NOT* reloaded automatically when you use 9 | # 'bundle exec jekyll serve'. If you change this file, please restart the server process. 10 | # 11 | # If you need help with YAML syntax, here are some quick references for you: 12 | # https://learn-the-web.algonquindesign.ca/topics/markdown-yaml-cheat-sheet/#yaml 13 | # https://learnxinyminutes.com/docs/yaml/ 14 | # 15 | # Site settings 16 | # These are used to personalize your new site. If you look in the HTML files, 17 | # you will see them accessed via {{ site.title }}, {{ site.email }}, and so on. 18 | # You can create any custom variable you would like, and they will be accessible 19 | # in the templates via {{ site.myvariable }}. 20 | 21 | title: "📘《Python进阶》" 22 | email: matt.su@foxmail.com 23 | description: "《Python进阶》是《Intermediate Python》的中文译本, 谨以此献给进击的 Python 和 Python 程序员们!" 24 | baseurl: "" # the subpath of your site, e.g. /blog 25 | url: "" # the base hostname & protocol for your site, e.g. http://example.com 26 | # twitter_username: jekyllrb 27 | github_username: "eastlakeside" 28 | 29 | 30 | # Build settings 31 | 32 | # Github production Setting 33 | # theme: "just-the-docs" ## this is for local dev 34 | remote_theme: just-the-docs/just-the-docs ## updated 2022July 35 | # remote_theme: pmarsceill/just-the-docs # this is old repo name 36 | 37 | # plugins: 38 | # - jekyll-feed 39 | 40 | # Exclude from processing. 41 | # The following items will not be processed, by default. 42 | # Any item listed under the `exclude:` key here will be automatically added to 43 | # the internal "default list". 44 | # 45 | # Excluded items can be processed by explicitly listing the directories or 46 | # their entries' file path in the `include:` list. 47 | # 48 | exclude: ["vendor", ] 49 | 50 | # Enable or disable the site search 51 | # Supports true (default) or false 52 | search_enabled: true 53 | search: 54 | # Split pages into sections that can be searched individually 55 | # Supports 1 - 6, default: 2 56 | heading_level: 2 57 | # Maximum amount of previews per search result 58 | # Default: 3 59 | previews: 2 60 | # Maximum amount of words to display before a matched word in the preview 61 | # Default: 5 62 | preview_words_before: 3 63 | # Maximum amount of words to display after a matched word in the preview 64 | # Default: 10 65 | preview_words_after: 3 66 | # Set the search token separator 67 | # Default: /[\s\-/]+/ 68 | # Example: enable support for hyphenated search words 69 | tokenizer_separator: /[\s/]+/ 70 | # Display the relative url in search results 71 | # Supports true (default) or false 72 | rel_url: true 73 | # Enable or disable the search button that appears in the bottom right corner of every page 74 | # Supports true or false (default) 75 | button: false 76 | 77 | # Enable or disable heading anchors 78 | heading_anchors: true 79 | 80 | # Aux links for the upper right navigation 81 | aux_links: 82 | "查看本书GitHub源码": 83 | - "//github.com/eastlakeside/interpy-zh/" 84 | 85 | # Makes Aux links open in a new tab. Default is false 86 | aux_links_new_tab: true 87 | 88 | # Sort order for navigation links 89 | # nav_sort: case_insensitive # default, equivalent to nil 90 | nav_sort: case_sensitive # Capital letters sorted before lowercase 91 | 92 | # Footer content 93 | # appears at the bottom of every page's main content 94 | 95 | # Back to top link 96 | back_to_top: true 97 | back_to_top_text: "🔝 Back to top" 98 | 99 | footer_content: "Copyright © 2016-现在 EastLakeSide-东滨社." 100 | 101 | # Footer last edited timestamp 102 | last_edit_timestamp: true # show or hide edit time - page must have `last_modified_date` defined in the frontmatter 103 | last_edit_time_format: "%b %e %Y at %I:%M %p" # uses ruby's time format: https://ruby-doc.org/stdlib-2.7.0/libdoc/time/rdoc/Time.html 104 | 105 | 106 | 107 | # Footer "Edit this page on GitHub" link text 108 | gh_edit_link: true # show or hide edit this page link 109 | gh_edit_link_text: "去GitHub上编辑这个页面" 110 | gh_edit_repository: "https://github.com/eastlakeside/interpy-zh" # the github URL for your repo 111 | gh_edit_branch: "master" # the branch that your docs is served from 112 | # gh_edit_source: docs # the source that your files originate from 113 | gh_edit_view_mode: "tree" # "tree" or "edit" if you want the user to jump into the editor immediately 114 | 115 | # Color scheme currently only supports "dark", "light"/nil (default), or a custom scheme that you define 116 | color_scheme: nil 117 | 118 | # Google Analytics Tracking (optional) 119 | # e.g, UA-1234567-89 120 | #ga_tracking: UA-2709176-10 121 | #ga_tracking_anonymize_ip: true # Use GDPR compliant Google Analytics settings (true/nil by default) 122 | 123 | plugins: 124 | - jekyll-seo-tag 125 | 126 | kramdown: 127 | input: GFM ## just to enable autolink like 128 | syntax_highlighter_opts: 129 | block: 130 | line_numbers: false 131 | 132 | compress_html: 133 | clippings: all 134 | comments: all 135 | endings: all 136 | startings: [] 137 | blanklines: false 138 | profile: false 139 | # ignore: 140 | # envs: all 141 | -------------------------------------------------------------------------------- /_includes/fix_linenos.html: -------------------------------------------------------------------------------- 1 | {%- comment -%} 2 | This file can be used to fix the HTML produced by Jekyll for highlighted 3 | code with line numbers. 4 | 5 | It works with `{% highlight some_language linenos %}...{% endhighlight %}` 6 | and with the Kramdown option to add line numbers to fenced code. 7 | 8 | The implementation was derived from the workaround provided by 9 | Dmitry Hrabrov (DeXP) at 10 | https://github.com/penibelst/jekyll-compress-html/issues/71#issuecomment-188144901 11 | 12 | EXPLANATION 13 | 14 | The HTML produced by Rouge highlighting with lie numbers is of the form 15 | `code table`. Jekyll (<= 4.1.1) always wraps the highlighted HTML 16 | with `pre`. This wrapping is not only unnecessary, but also transforms 17 | the conforming HTML produced by Rouge to non-conforming HTML, which 18 | results in HTML validation error reports. 19 | 20 | The fix removes the outer `pre` tags whenever they contain the pattern 21 | ``. 22 | 23 | Apart from avoiding HTML validation errors, the fix allows the use of 24 | the [Jekyll layout for compressing HTML](http://jch.penibelst.de), 25 | which relies on `pre` tags not being nested, according to 26 | https://github.com/penibelst/jekyll-compress-html/issues/71#issuecomment-172069842 27 | 28 | USAGE 29 | 30 | (Any names can be used for `some_var` and `some_language`.) 31 | 32 | {% capture some_var %} 33 | {% highlight some_language linenos %} 34 | Some code 35 | {% endhighlight %} 36 | {% endcapture %} 37 | {% include fix_linenos.html code=some_var %} 38 | 39 | For code fences: 40 | 41 | {% capture some_var %} 42 | ```some_language 43 | Some code 44 | ``` 45 | {% endcapture %} 46 | {% assign some_var = some_var | markdownify %} 47 | {% include fix_linenos.html code=some_var %} 48 | 49 | CAVEATS 50 | 51 | The above does not work when `Some code` happens to contain the matched string 52 | `
`. 53 | 54 | The use of this file overwrites the variable `fix_linenos_code` with `nil`. 55 | 56 | {%- endcomment -%} 57 | 58 | {% assign fix_linenos_code = include.code %} 59 | {% if fix_linenos_code contains '
' %} 60 | {% assign fix_linenos_code = fix_linenos_code | replace: '
', '
' %}
61 |   {% assign fix_linenos_code = fix_linenos_code | replace: "
", "" %} 63 | {% endif %} 64 | {{ fix_linenos_code }} 65 | {% assign fix_linenos_code = nil %} 66 | -------------------------------------------------------------------------------- /_includes/footer_custom.html: -------------------------------------------------------------------------------- 1 | {%- if site.footer_content -%} 2 |

{{ site.footer_content }}

3 | {%- endif -%} 4 | -------------------------------------------------------------------------------- /_includes/head.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {% unless site.plugins contains "jekyll-seo-tag" %} 6 | {{ page.title }} - {{ site.title }} 7 | 8 | {% if page.description %} 9 | 10 | {% endif %} 11 | {% endunless %} 12 | 13 | 14 | 15 | 16 | 17 | {% if site.ga_tracking != nil %} 18 | 19 | 26 | 27 | {% endif %} 28 | 29 | {% if site.search_enabled != false %} 30 | 31 | {% endif %} 32 | 33 | 34 | 35 | 36 | {% seo %} 37 | 38 | {% include head_custom.html %} 39 | 40 | 41 | -------------------------------------------------------------------------------- /_includes/head_custom.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /_includes/header_custom.html: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eastlakeside/interpy-zh/b056825749328659e0082798d20e83cc3f4814d5/_includes/header_custom.html -------------------------------------------------------------------------------- /_includes/js/custom.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eastlakeside/interpy-zh/b056825749328659e0082798d20e83cc3f4814d5/_includes/js/custom.js -------------------------------------------------------------------------------- /_includes/nav.html: -------------------------------------------------------------------------------- 1 | 100 | -------------------------------------------------------------------------------- /_includes/title.html: -------------------------------------------------------------------------------- 1 | {% if site.logo %} 2 | 3 | {% else %} 4 | {{ site.title }} 5 | {% endif %} 6 | -------------------------------------------------------------------------------- /_includes/vendor/anchor_headings.html: -------------------------------------------------------------------------------- 1 | {% capture headingsWorkspace %} 2 | {% comment %} 3 | Copyright (c) 2018 Vladimir "allejo" Jimenez 4 | 5 | Permission is hereby granted, free of charge, to any person 6 | obtaining a copy of this software and associated documentation 7 | files (the "Software"), to deal in the Software without 8 | restriction, including without limitation the rights to use, 9 | copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the 11 | Software is furnished to do so, subject to the following 12 | conditions: 13 | 14 | The above copyright notice and this permission notice shall be 15 | included in all copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 18 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 19 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 20 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 21 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 22 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 23 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 24 | OTHER DEALINGS IN THE SOFTWARE. 25 | {% endcomment %} 26 | {% comment %} 27 | Version 1.0.7 28 | https://github.com/allejo/jekyll-anchor-headings 29 | 30 | "Be the pull request you wish to see in the world." ~Ben Balter 31 | 32 | Usage: 33 | {% include anchor_headings.html html=content anchorBody="#" %} 34 | 35 | Parameters: 36 | * html (string) - the HTML of compiled markdown generated by kramdown in Jekyll 37 | 38 | Optional Parameters: 39 | * beforeHeading (bool) : false - Set to true if the anchor should be placed _before_ the heading's content 40 | * anchorAttrs (string) : '' - Any custom HTML attributes that will be added to the `` tag; you may NOT use `href`, `class` or `title`; 41 | the `%heading%` and `%html_id%` placeholders are available 42 | * anchorBody (string) : '' - The content that will be placed inside the anchor; the `%heading%` placeholder is available 43 | * anchorClass (string) : '' - The class(es) that will be used for each anchor. Separate multiple classes with a space 44 | * anchorTitle (string) : '' - The `title` attribute that will be used for anchors 45 | * h_min (int) : 1 - The minimum header level to build an anchor for; any header lower than this value will be ignored 46 | * h_max (int) : 6 - The maximum header level to build an anchor for; any header greater than this value will be ignored 47 | * bodyPrefix (string) : '' - Anything that should be inserted inside of the heading tag _before_ its anchor and content 48 | * bodySuffix (string) : '' - Anything that should be inserted inside of the heading tag _after_ its anchor and content 49 | 50 | Output: 51 | The original HTML with the addition of anchors inside of all of the h1-h6 headings. 52 | {% endcomment %} 53 | 54 | {% assign minHeader = include.h_min | default: 1 %} 55 | {% assign maxHeader = include.h_max | default: 6 %} 56 | {% assign beforeHeading = include.beforeHeading %} 57 | {% assign nodes = include.html | split: ' 72 | {% if headerLevel == 0 %} 73 | 74 | {% assign firstChunk = node | split: '>' | first %} 75 | 76 | 77 | {% unless firstChunk contains '<' %} 78 | {% capture node %}{% endcapture %} 86 | {% assign _workspace = node | split: _closingTag %} 87 | {% assign _idWorkspace = _workspace[0] | split: 'id="' %} 88 | {% assign _idWorkspace = _idWorkspace[1] | split: '"' %} 89 | {% assign html_id = _idWorkspace[0] %} 90 | 91 | {% capture _hAttrToStrip %}{{ _workspace[0] | split: '>' | first }}>{% endcapture %} 92 | {% assign header = _workspace[0] | replace: _hAttrToStrip, '' %} 93 | 94 | 95 | {% capture anchor %}{% endcapture %} 96 | 97 | {% if html_id and headerLevel >= minHeader and headerLevel <= maxHeader %} 98 | {% capture anchor %}href="#{{ html_id }}"{% endcapture %} 99 | 100 | {% if include.anchorClass %} 101 | {% capture anchor %}{{ anchor }} class="{{ include.anchorClass }}"{% endcapture %} 102 | {% endif %} 103 | 104 | {% if include.anchorTitle %} 105 | {% capture anchor %}{{ anchor }} title="{{ include.anchorTitle | replace: '%heading%', header }}"{% endcapture %} 106 | {% endif %} 107 | 108 | {% if include.anchorAttrs %} 109 | {% capture anchor %}{{ anchor }} {{ include.anchorAttrs | replace: '%heading%', header | replace: '%html_id%', html_id }}{% endcapture %} 110 | {% endif %} 111 | 112 | {% capture anchor %}{{ include.anchorBody | replace: '%heading%', header | default: '' }}{% endcapture %} 113 | 114 | 115 | {% if beforeHeading %} 116 | {% capture anchor %}{{ anchor }} {% endcapture %} 117 | {% else %} 118 | {% capture anchor %} {{ anchor }}{% endcapture %} 119 | {% endif %} 120 | {% endif %} 121 | 122 | {% capture new_heading %} 123 | 132 | {% endcapture %} 133 | 134 | 137 | {% assign chunkCount = _workspace | size %} 138 | {% if chunkCount > 1 %} 139 | {% capture new_heading %}{{ new_heading }}{{ _workspace | last }}{% endcapture %} 140 | {% endif %} 141 | 142 | {% capture edited_headings %}{{ edited_headings }}{{ new_heading }}{% endcapture %} 143 | {% endfor %} 144 | {% endcapture %}{% assign headingsWorkspace = '' %}{{ edited_headings | strip }} 145 | -------------------------------------------------------------------------------- /_layouts/about.html: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | --- 4 | 5 | {{ content }} 6 | -------------------------------------------------------------------------------- /_layouts/default.html: -------------------------------------------------------------------------------- 1 | --- 2 | layout: table_wrappers 3 | --- 4 | 5 | 6 | 7 | 8 | {% include head.html %} 9 | 10 | 11 | 12 | Link 13 | 14 | 15 | 16 | 17 | 18 | Search 19 | 20 | 21 | 22 | 23 | 24 | Menu 25 | 26 | 27 | 28 | 29 | 30 | Expand 31 | 32 | 33 | 34 | 35 | 36 | Document 37 | 38 | 39 | 40 | 41 | 42 | 43 | 72 |
73 | 74 | 77 | 78 |
79 | {% if site.search_enabled != false %} 80 | 87 | {% endif %} 88 | {% include header_custom.html %} 89 | {% if site.aux_links %} 90 | 105 | {% endif %} 106 |
107 |
108 | {% unless page.url == "/" %} 109 | {% if page.parent %} 110 | {%- for node in pages_list -%} 111 | {%- if node.parent == nil -%} 112 | {%- if page.parent == node.title or page.grand_parent == node.title -%} 113 | {%- assign first_level_url = node.url | absolute_url -%} 114 | {%- endif -%} 115 | {%- if node.has_children -%} 116 | {%- assign children_list = pages_list | where: "parent", node.title -%} 117 | {%- for child in children_list -%} 118 | {%- if page.url == child.url or page.parent == child.title -%} 119 | {%- assign second_level_url = child.url | absolute_url -%} 120 | {%- endif -%} 121 | {%- endfor -%} 122 | {%- endif -%} 123 | {%- endif -%} 124 | {%- endfor -%} 125 | 136 | {% endif %} 137 | {% endunless %} 138 |
139 | {% if site.heading_anchors != false %} 140 | {% include vendor/anchor_headings.html html=content beforeHeading="true" anchorBody="" anchorClass="anchor-heading" anchorAttrs="aria-labelledby=\"%html_id%\"" %} 141 | {% else %} 142 | {{ content }} 143 | {% endif %} 144 | 145 | {% if page.has_children == true and page.has_toc != false %} 146 |
147 |

Table of contents

148 |
    149 | {%- assign children_list = pages_list | where: "parent", page.title | where: "grand_parent", page.parent -%} 150 | {% for child in children_list %} 151 |
  • 152 | {{ child.title }}{% if child.summary %} - {{ child.summary }}{% endif %} 153 |
  • 154 | {% endfor %} 155 |
156 | {% endif %} 157 | 158 | {% capture footer_custom %} 159 | {%- include footer_custom.html -%} 160 | {% endcapture %} 161 | {% if footer_custom != "" or site.last_edit_timestamp or site.gh_edit_link %} 162 |
163 |
164 | {% if site.back_to_top %} 165 |

{{ site.back_to_top_text }}

166 | {% endif %} 167 | 168 | {{ footer_custom }} 169 | 170 | {% if site.last_edit_timestamp or site.gh_edit_link %} 171 |
172 | {% if site.last_edit_timestamp and site.last_edit_time_format and page.last_modified_date %} 173 |

174 | Page last modified: {{ page.last_modified_date | date: site.last_edit_time_format }}. 175 |

176 | {% endif %} 177 | {% if 178 | site.gh_edit_link and 179 | site.gh_edit_link_text and 180 | site.gh_edit_repository and 181 | site.gh_edit_branch and 182 | site.gh_edit_view_mode 183 | %} 184 |

185 | {{ site.gh_edit_link_text }} 186 |

187 | {% endif %} 188 |
189 | {% endif %} 190 |
191 | {% endif %} 192 | 193 |
194 |
195 | 196 | {% if site.search_enabled != false %} 197 | {% if site.search.button %} 198 | 199 | 200 | 201 | {% endif %} 202 | 203 |
204 | {% endif %} 205 |
206 | 207 | 208 | -------------------------------------------------------------------------------- /_layouts/home.html: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | --- 4 | 5 | {{ content }} 6 | -------------------------------------------------------------------------------- /_layouts/page.html: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | --- 4 | 5 | {{ content }} 6 | -------------------------------------------------------------------------------- /_layouts/post.html: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | --- 4 | 5 | {{ content }} 6 | -------------------------------------------------------------------------------- /_layouts/table_wrappers.html: -------------------------------------------------------------------------------- 1 | --- 2 | layout: vendor/compress 3 | --- 4 | 5 | {% assign content_ = content | replace: '', '
' %} 7 | {{ content_ }} 8 | -------------------------------------------------------------------------------- /_layouts/vendor/compress.html: -------------------------------------------------------------------------------- 1 | --- 2 | # Jekyll layout that compresses HTML 3 | # v3.1.0 4 | # http://jch.penibelst.de/ 5 | # © 2014–2015 Anatol Broder 6 | # MIT License 7 | --- 8 | 9 | {% capture _LINE_FEED %} 10 | {% endcapture %}{% if site.compress_html.ignore.envs contains jekyll.environment or site.compress_html.ignore.envs == "all" %}{{ content }}{% else %}{% capture _content %}{{ content }}{% endcapture %}{% assign _profile = site.compress_html.profile %}{% if site.compress_html.endings == "all" %}{% assign _endings = "html head body li dt dd optgroup option colgroup caption thead tbody tfoot tr td th" | split: " " %}{% else %}{% assign _endings = site.compress_html.endings %}{% endif %}{% for _element in _endings %}{% capture _end %}{% endcapture %}{% assign _content = _content | remove: _end %}{% endfor %}{% if _profile and _endings %}{% assign _profile_endings = _content | size | plus: 1 %}{% endif %}{% for _element in site.compress_html.startings %}{% capture _start %}<{{ _element }}>{% endcapture %}{% assign _content = _content | remove: _start %}{% endfor %}{% if _profile and site.compress_html.startings %}{% assign _profile_startings = _content | size | plus: 1 %}{% endif %}{% if site.compress_html.comments == "all" %}{% assign _comments = "" | split: " " %}{% else %}{% assign _comments = site.compress_html.comments %}{% endif %}{% if _comments.size == 2 %}{% capture _comment_befores %}.{{ _content }}{% endcapture %}{% assign _comment_befores = _comment_befores | split: _comments.first %}{% for _comment_before in _comment_befores %}{% if forloop.first %}{% continue %}{% endif %}{% capture _comment_outside %}{% if _carry %}{{ _comments.first }}{% endif %}{{ _comment_before }}{% endcapture %}{% capture _comment %}{% unless _carry %}{{ _comments.first }}{% endunless %}{{ _comment_outside | split: _comments.last | first }}{% if _comment_outside contains _comments.last %}{{ _comments.last }}{% assign _carry = false %}{% else %}{% assign _carry = true %}{% endif %}{% endcapture %}{% assign _content = _content | remove_first: _comment %}{% endfor %}{% if _profile %}{% assign _profile_comments = _content | size | plus: 1 %}{% endif %}{% endif %}{% assign _pre_befores = _content | split: "" %}{% assign _pres_after = "" %}{% if _pres.size != 0 %}{% if site.compress_html.blanklines %}{% assign _lines = _pres.last | split: _LINE_FEED %}{% capture _pres_after %}{% for _line in _lines %}{% assign _trimmed = _line | split: " " | join: " " %}{% if _trimmed != empty or forloop.last %}{% unless forloop.first %}{{ _LINE_FEED }}{% endunless %}{{ _line }}{% endif %}{% endfor %}{% endcapture %}{% else %}{% assign _pres_after = _pres.last | split: " " | join: " " %}{% endif %}{% endif %}{% capture _content %}{{ _content }}{% if _pre_before contains "" %}{% endif %}{% unless _pre_before contains "" and _pres.size == 1 %}{{ _pres_after }}{% endunless %}{% endcapture %}{% endfor %}{% if _profile %}{% assign _profile_collapse = _content | size | plus: 1 %}{% endif %}{% if site.compress_html.clippings == "all" %}{% assign _clippings = "html head title base link meta style body article section nav aside h1 h2 h3 h4 h5 h6 hgroup header footer address p hr blockquote ol ul li dl dt dd figure figcaption main div table caption colgroup col tbody thead tfoot tr td th" | split: " " %}{% else %}{% assign _clippings = site.compress_html.clippings %}{% endif %}{% for _element in _clippings %}{% assign _edges = " ;; ;" | replace: "e", _element | split: ";" %}{% assign _content = _content | replace: _edges[0], _edges[1] | replace: _edges[2], _edges[3] | replace: _edges[4], _edges[5] %}{% endfor %}{% if _profile and _clippings %}{% assign _profile_clippings = _content | size | plus: 1 %}{% endif %}{{ _content }}{% if _profile %}
Step Bytes
raw {{ content | size }}{% if _profile_endings %}
endings {{ _profile_endings }}{% endif %}{% if _profile_startings %}
startings {{ _profile_startings }}{% endif %}{% if _profile_comments %}
comments {{ _profile_comments }}{% endif %}{% if _profile_collapse %}
collapse {{ _profile_collapse }}{% endif %}{% if _profile_clippings %}
clippings {{ _profile_clippings }}{% endif %}
{% endif %}{% endif %} 11 | -------------------------------------------------------------------------------- /assets/images/just-the-docs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eastlakeside/interpy-zh/b056825749328659e0082798d20e83cc3f4814d5/assets/images/just-the-docs.png -------------------------------------------------------------------------------- /assets/images/search.svg: -------------------------------------------------------------------------------- 1 | Search 2 | -------------------------------------------------------------------------------- /assets/js/just-the-docs.js: -------------------------------------------------------------------------------- 1 | (function (jtd, undefined) { 2 | 3 | // Event handling 4 | 5 | jtd.addEvent = function(el, type, handler) { 6 | if (el.attachEvent) el.attachEvent('on'+type, handler); else el.addEventListener(type, handler); 7 | } 8 | jtd.removeEvent = function(el, type, handler) { 9 | if (el.detachEvent) el.detachEvent('on'+type, handler); else el.removeEventListener(type, handler); 10 | } 11 | jtd.onReady = function(ready) { 12 | // in case the document is already rendered 13 | if (document.readyState!='loading') ready(); 14 | // modern browsers 15 | else if (document.addEventListener) document.addEventListener('DOMContentLoaded', ready); 16 | // IE <= 8 17 | else document.attachEvent('onreadystatechange', function(){ 18 | if (document.readyState=='complete') ready(); 19 | }); 20 | } 21 | 22 | // Show/hide mobile menu 23 | 24 | function initNav() { 25 | jtd.addEvent(document, 'click', function(e){ 26 | var target = e.target; 27 | while (target && !(target.classList && target.classList.contains('nav-list-expander'))) { 28 | target = target.parentNode; 29 | } 30 | if (target) { 31 | e.preventDefault(); 32 | target.parentNode.classList.toggle('active'); 33 | } 34 | }); 35 | 36 | const siteNav = document.getElementById('site-nav'); 37 | const mainHeader = document.getElementById('main-header'); 38 | const menuButton = document.getElementById('menu-button'); 39 | 40 | jtd.addEvent(menuButton, 'click', function(e){ 41 | e.preventDefault(); 42 | 43 | if (menuButton.classList.toggle('nav-open')) { 44 | siteNav.classList.add('nav-open'); 45 | mainHeader.classList.add('nav-open'); 46 | } else { 47 | siteNav.classList.remove('nav-open'); 48 | mainHeader.classList.remove('nav-open'); 49 | } 50 | }); 51 | } 52 | // Site search 53 | 54 | function initSearch() { 55 | var request = new XMLHttpRequest(); 56 | request.open('GET', '/assets/js/search-data.json', true); 57 | 58 | request.onload = function(){ 59 | if (request.status >= 200 && request.status < 400) { 60 | var docs = JSON.parse(request.responseText); 61 | 62 | lunr.tokenizer.separator = /[\s/]+/ 63 | 64 | var index = lunr(function(){ 65 | this.ref('id'); 66 | this.field('title', { boost: 200 }); 67 | this.field('content', { boost: 2 }); 68 | this.field('relUrl'); 69 | this.metadataWhitelist = ['position'] 70 | 71 | for (var i in docs) { 72 | this.add({ 73 | id: i, 74 | title: docs[i].title, 75 | content: docs[i].content, 76 | relUrl: docs[i].relUrl 77 | }); 78 | } 79 | }); 80 | 81 | searchLoaded(index, docs); 82 | } else { 83 | console.log('Error loading ajax request. Request status:' + request.status); 84 | } 85 | }; 86 | 87 | request.onerror = function(){ 88 | console.log('There was a connection error'); 89 | }; 90 | 91 | request.send(); 92 | } 93 | 94 | function searchLoaded(index, docs) { 95 | var index = index; 96 | var docs = docs; 97 | var searchInput = document.getElementById('search-input'); 98 | var searchResults = document.getElementById('search-results'); 99 | var mainHeader = document.getElementById('main-header'); 100 | var currentInput; 101 | var currentSearchIndex = 0; 102 | 103 | function showSearch() { 104 | document.documentElement.classList.add('search-active'); 105 | } 106 | 107 | function hideSearch() { 108 | document.documentElement.classList.remove('search-active'); 109 | } 110 | 111 | function update() { 112 | currentSearchIndex++; 113 | 114 | var input = searchInput.value; 115 | if (input === '') { 116 | hideSearch(); 117 | } else { 118 | showSearch(); 119 | // scroll search input into view, workaround for iOS Safari 120 | window.scroll(0, -1); 121 | setTimeout(function(){ window.scroll(0, 0); }, 0); 122 | } 123 | if (input === currentInput) { 124 | return; 125 | } 126 | currentInput = input; 127 | searchResults.innerHTML = ''; 128 | if (input === '') { 129 | return; 130 | } 131 | 132 | var results = index.query(function (query) { 133 | var tokens = lunr.tokenizer(input) 134 | query.term(tokens, { 135 | boost: 10 136 | }); 137 | query.term(tokens, { 138 | wildcard: lunr.Query.wildcard.TRAILING 139 | }); 140 | }); 141 | 142 | if ((results.length == 0) && (input.length > 2)) { 143 | var tokens = lunr.tokenizer(input).filter(function(token, i) { 144 | return token.str.length < 20; 145 | }) 146 | if (tokens.length > 0) { 147 | results = index.query(function (query) { 148 | query.term(tokens, { 149 | editDistance: Math.round(Math.sqrt(input.length / 2 - 1)) 150 | }); 151 | }); 152 | } 153 | } 154 | 155 | if (results.length == 0) { 156 | var noResultsDiv = document.createElement('div'); 157 | noResultsDiv.classList.add('search-no-result'); 158 | noResultsDiv.innerText = 'No results found'; 159 | searchResults.appendChild(noResultsDiv); 160 | 161 | } else { 162 | var resultsList = document.createElement('ul'); 163 | resultsList.classList.add('search-results-list'); 164 | searchResults.appendChild(resultsList); 165 | 166 | addResults(resultsList, results, 0, 10, 100, currentSearchIndex); 167 | } 168 | 169 | function addResults(resultsList, results, start, batchSize, batchMillis, searchIndex) { 170 | if (searchIndex != currentSearchIndex) { 171 | return; 172 | } 173 | for (var i = start; i < (start + batchSize); i++) { 174 | if (i == results.length) { 175 | return; 176 | } 177 | addResult(resultsList, results[i]); 178 | } 179 | setTimeout(function() { 180 | addResults(resultsList, results, start + batchSize, batchSize, batchMillis, searchIndex); 181 | }, batchMillis); 182 | } 183 | 184 | function addResult(resultsList, result) { 185 | var doc = docs[result.ref]; 186 | 187 | var resultsListItem = document.createElement('li'); 188 | resultsListItem.classList.add('search-results-list-item'); 189 | resultsList.appendChild(resultsListItem); 190 | 191 | var resultLink = document.createElement('a'); 192 | resultLink.classList.add('search-result'); 193 | resultLink.setAttribute('href', doc.url); 194 | resultsListItem.appendChild(resultLink); 195 | 196 | var resultTitle = document.createElement('div'); 197 | resultTitle.classList.add('search-result-title'); 198 | resultLink.appendChild(resultTitle); 199 | 200 | var resultDoc = document.createElement('div'); 201 | resultDoc.classList.add('search-result-doc'); 202 | resultDoc.innerHTML = ''; 203 | resultTitle.appendChild(resultDoc); 204 | 205 | var resultDocTitle = document.createElement('div'); 206 | resultDocTitle.classList.add('search-result-doc-title'); 207 | resultDocTitle.innerHTML = doc.doc; 208 | resultDoc.appendChild(resultDocTitle); 209 | var resultDocOrSection = resultDocTitle; 210 | 211 | if (doc.doc != doc.title) { 212 | resultDoc.classList.add('search-result-doc-parent'); 213 | var resultSection = document.createElement('div'); 214 | resultSection.classList.add('search-result-section'); 215 | resultSection.innerHTML = doc.title; 216 | resultTitle.appendChild(resultSection); 217 | resultDocOrSection = resultSection; 218 | } 219 | 220 | var metadata = result.matchData.metadata; 221 | var titlePositions = []; 222 | var contentPositions = []; 223 | for (var j in metadata) { 224 | var meta = metadata[j]; 225 | if (meta.title) { 226 | var positions = meta.title.position; 227 | for (var k in positions) { 228 | titlePositions.push(positions[k]); 229 | } 230 | } 231 | if (meta.content) { 232 | var positions = meta.content.position; 233 | for (var k in positions) { 234 | var position = positions[k]; 235 | var previewStart = position[0]; 236 | var previewEnd = position[0] + position[1]; 237 | var ellipsesBefore = true; 238 | var ellipsesAfter = true; 239 | for (var k = 0; k < 3; k++) { 240 | var nextSpace = doc.content.lastIndexOf(' ', previewStart - 2); 241 | var nextDot = doc.content.lastIndexOf('. ', previewStart - 2); 242 | if ((nextDot >= 0) && (nextDot > nextSpace)) { 243 | previewStart = nextDot + 1; 244 | ellipsesBefore = false; 245 | break; 246 | } 247 | if (nextSpace < 0) { 248 | previewStart = 0; 249 | ellipsesBefore = false; 250 | break; 251 | } 252 | previewStart = nextSpace + 1; 253 | } 254 | for (var k = 0; k < 3; k++) { 255 | var nextSpace = doc.content.indexOf(' ', previewEnd + 1); 256 | var nextDot = doc.content.indexOf('. ', previewEnd + 1); 257 | if ((nextDot >= 0) && (nextDot < nextSpace)) { 258 | previewEnd = nextDot; 259 | ellipsesAfter = false; 260 | break; 261 | } 262 | if (nextSpace < 0) { 263 | previewEnd = doc.content.length; 264 | ellipsesAfter = false; 265 | break; 266 | } 267 | previewEnd = nextSpace; 268 | } 269 | contentPositions.push({ 270 | highlight: position, 271 | previewStart: previewStart, previewEnd: previewEnd, 272 | ellipsesBefore: ellipsesBefore, ellipsesAfter: ellipsesAfter 273 | }); 274 | } 275 | } 276 | } 277 | 278 | if (titlePositions.length > 0) { 279 | titlePositions.sort(function(p1, p2){ return p1[0] - p2[0] }); 280 | resultDocOrSection.innerHTML = ''; 281 | addHighlightedText(resultDocOrSection, doc.title, 0, doc.title.length, titlePositions); 282 | } 283 | 284 | if (contentPositions.length > 0) { 285 | contentPositions.sort(function(p1, p2){ return p1.highlight[0] - p2.highlight[0] }); 286 | var contentPosition = contentPositions[0]; 287 | var previewPosition = { 288 | highlight: [contentPosition.highlight], 289 | previewStart: contentPosition.previewStart, previewEnd: contentPosition.previewEnd, 290 | ellipsesBefore: contentPosition.ellipsesBefore, ellipsesAfter: contentPosition.ellipsesAfter 291 | }; 292 | var previewPositions = [previewPosition]; 293 | for (var j = 1; j < contentPositions.length; j++) { 294 | contentPosition = contentPositions[j]; 295 | if (previewPosition.previewEnd < contentPosition.previewStart) { 296 | previewPosition = { 297 | highlight: [contentPosition.highlight], 298 | previewStart: contentPosition.previewStart, previewEnd: contentPosition.previewEnd, 299 | ellipsesBefore: contentPosition.ellipsesBefore, ellipsesAfter: contentPosition.ellipsesAfter 300 | } 301 | previewPositions.push(previewPosition); 302 | } else { 303 | previewPosition.highlight.push(contentPosition.highlight); 304 | previewPosition.previewEnd = contentPosition.previewEnd; 305 | previewPosition.ellipsesAfter = contentPosition.ellipsesAfter; 306 | } 307 | } 308 | 309 | var resultPreviews = document.createElement('div'); 310 | resultPreviews.classList.add('search-result-previews'); 311 | resultLink.appendChild(resultPreviews); 312 | 313 | var content = doc.content; 314 | for (var j = 0; j < Math.min(previewPositions.length, 2); j++) { 315 | var position = previewPositions[j]; 316 | 317 | var resultPreview = document.createElement('div'); 318 | resultPreview.classList.add('search-result-preview'); 319 | resultPreviews.appendChild(resultPreview); 320 | 321 | if (position.ellipsesBefore) { 322 | resultPreview.appendChild(document.createTextNode('... ')); 323 | } 324 | addHighlightedText(resultPreview, content, position.previewStart, position.previewEnd, position.highlight); 325 | if (position.ellipsesAfter) { 326 | resultPreview.appendChild(document.createTextNode(' ...')); 327 | } 328 | } 329 | } 330 | var resultRelUrl = document.createElement('span'); 331 | resultRelUrl.classList.add('search-result-rel-url'); 332 | resultRelUrl.innerText = doc.relUrl; 333 | resultTitle.appendChild(resultRelUrl); 334 | } 335 | 336 | function addHighlightedText(parent, text, start, end, positions) { 337 | var index = start; 338 | for (var i in positions) { 339 | var position = positions[i]; 340 | var span = document.createElement('span'); 341 | span.innerHTML = text.substring(index, position[0]); 342 | parent.appendChild(span); 343 | index = position[0] + position[1]; 344 | var highlight = document.createElement('span'); 345 | highlight.classList.add('search-result-highlight'); 346 | highlight.innerHTML = text.substring(position[0], index); 347 | parent.appendChild(highlight); 348 | } 349 | var span = document.createElement('span'); 350 | span.innerHTML = text.substring(index, end); 351 | parent.appendChild(span); 352 | } 353 | } 354 | 355 | jtd.addEvent(searchInput, 'focus', function(){ 356 | setTimeout(update, 0); 357 | }); 358 | 359 | jtd.addEvent(searchInput, 'keyup', function(e){ 360 | switch (e.keyCode) { 361 | case 27: // When esc key is pressed, hide the results and clear the field 362 | searchInput.value = ''; 363 | break; 364 | case 38: // arrow up 365 | case 40: // arrow down 366 | case 13: // enter 367 | e.preventDefault(); 368 | return; 369 | } 370 | update(); 371 | }); 372 | 373 | jtd.addEvent(searchInput, 'keydown', function(e){ 374 | switch (e.keyCode) { 375 | case 38: // arrow up 376 | e.preventDefault(); 377 | var active = document.querySelector('.search-result.active'); 378 | if (active) { 379 | active.classList.remove('active'); 380 | if (active.parentElement.previousSibling) { 381 | var previous = active.parentElement.previousSibling.querySelector('.search-result'); 382 | previous.classList.add('active'); 383 | } 384 | } 385 | return; 386 | case 40: // arrow down 387 | e.preventDefault(); 388 | var active = document.querySelector('.search-result.active'); 389 | if (active) { 390 | if (active.parentElement.nextSibling) { 391 | var next = active.parentElement.nextSibling.querySelector('.search-result'); 392 | active.classList.remove('active'); 393 | next.classList.add('active'); 394 | } 395 | } else { 396 | var next = document.querySelector('.search-result'); 397 | if (next) { 398 | next.classList.add('active'); 399 | } 400 | } 401 | return; 402 | case 13: // enter 403 | e.preventDefault(); 404 | var active = document.querySelector('.search-result.active'); 405 | if (active) { 406 | active.click(); 407 | } else { 408 | var first = document.querySelector('.search-result'); 409 | if (first) { 410 | first.click(); 411 | } 412 | } 413 | return; 414 | } 415 | }); 416 | 417 | jtd.addEvent(document, 'click', function(e){ 418 | if (e.target != searchInput) { 419 | hideSearch(); 420 | } 421 | }); 422 | } 423 | 424 | // Switch theme 425 | 426 | jtd.getTheme = function() { 427 | var cssFileHref = document.querySelector('[rel="stylesheet"]').getAttribute('href'); 428 | return cssFileHref.substring(cssFileHref.lastIndexOf('-') + 1, cssFileHref.length - 4); 429 | } 430 | 431 | jtd.setTheme = function(theme) { 432 | var cssFile = document.querySelector('[rel="stylesheet"]'); 433 | cssFile.setAttribute('href', '/assets/css/just-the-docs-' + theme + '.css'); 434 | } 435 | 436 | // Document ready 437 | 438 | jtd.onReady(function(){ 439 | initNav(); 440 | initSearch(); 441 | }); 442 | 443 | })(window.jtd = window.jtd || {}); 444 | 445 | 446 | -------------------------------------------------------------------------------- /assets/js/vendor/lunr.min.js: -------------------------------------------------------------------------------- 1 | /** 2 | * lunr - http://lunrjs.com - A bit like Solr, but much smaller and not as bright - 2.3.6 3 | * Copyright (C) 2019 Oliver Nightingale 4 | * @license MIT 5 | */ 6 | !function(){var e=function(t){var r=new e.Builder;return r.pipeline.add(e.trimmer,e.stopWordFilter,e.stemmer),r.searchPipeline.add(e.stemmer),t.call(r,r),r.build()};e.version="2.3.6",e.utils={},e.utils.warn=function(e){return function(t){e.console&&console.warn&&console.warn(t)}}(this),e.utils.asString=function(e){return void 0===e||null===e?"":e.toString()},e.utils.clone=function(e){if(null===e||void 0===e)return e;for(var t=Object.create(null),r=Object.keys(e),i=0;i0){var c=e.utils.clone(r)||{};c.position=[a,l],c.index=s.length,s.push(new e.Token(i.slice(a,o),c))}a=o+1}}return s},e.tokenizer.separator=/[\s\-]+/,e.Pipeline=function(){this._stack=[]},e.Pipeline.registeredFunctions=Object.create(null),e.Pipeline.registerFunction=function(t,r){r in this.registeredFunctions&&e.utils.warn("Overwriting existing registered function: "+r),t.label=r,e.Pipeline.registeredFunctions[t.label]=t},e.Pipeline.warnIfFunctionNotRegistered=function(t){var r=t.label&&t.label in this.registeredFunctions;r||e.utils.warn("Function is not registered with pipeline. This may cause problems when serialising the index.\n",t)},e.Pipeline.load=function(t){var r=new e.Pipeline;return t.forEach(function(t){var i=e.Pipeline.registeredFunctions[t];if(!i)throw new Error("Cannot load unregistered function: "+t);r.add(i)}),r},e.Pipeline.prototype.add=function(){var t=Array.prototype.slice.call(arguments);t.forEach(function(t){e.Pipeline.warnIfFunctionNotRegistered(t),this._stack.push(t)},this)},e.Pipeline.prototype.after=function(t,r){e.Pipeline.warnIfFunctionNotRegistered(r);var i=this._stack.indexOf(t);if(i==-1)throw new Error("Cannot find existingFn");i+=1,this._stack.splice(i,0,r)},e.Pipeline.prototype.before=function(t,r){e.Pipeline.warnIfFunctionNotRegistered(r);var i=this._stack.indexOf(t);if(i==-1)throw new Error("Cannot find existingFn");this._stack.splice(i,0,r)},e.Pipeline.prototype.remove=function(e){var t=this._stack.indexOf(e);t!=-1&&this._stack.splice(t,1)},e.Pipeline.prototype.run=function(e){for(var t=this._stack.length,r=0;r1&&(se&&(r=n),s!=e);)i=r-t,n=t+Math.floor(i/2),s=this.elements[2*n];return s==e?2*n:s>e?2*n:sa?l+=2:o==a&&(t+=r[u+1]*i[l+1],u+=2,l+=2);return t},e.Vector.prototype.similarity=function(e){return this.dot(e)/this.magnitude()||0},e.Vector.prototype.toArray=function(){for(var e=new Array(this.elements.length/2),t=1,r=0;t0){var o,a=s.str.charAt(0);a in s.node.edges?o=s.node.edges[a]:(o=new e.TokenSet,s.node.edges[a]=o),1==s.str.length&&(o["final"]=!0),n.push({node:o,editsRemaining:s.editsRemaining,str:s.str.slice(1)})}if(0!=s.editsRemaining){if("*"in s.node.edges)var u=s.node.edges["*"];else{var u=new e.TokenSet;s.node.edges["*"]=u}if(0==s.str.length&&(u["final"]=!0),n.push({node:u,editsRemaining:s.editsRemaining-1,str:s.str}),s.str.length>1&&n.push({node:s.node,editsRemaining:s.editsRemaining-1,str:s.str.slice(1)}),1==s.str.length&&(s.node["final"]=!0),s.str.length>=1){if("*"in s.node.edges)var l=s.node.edges["*"];else{var l=new e.TokenSet;s.node.edges["*"]=l}1==s.str.length&&(l["final"]=!0),n.push({node:l,editsRemaining:s.editsRemaining-1,str:s.str.slice(1)})}if(s.str.length>1){var c,h=s.str.charAt(0),d=s.str.charAt(1);d in s.node.edges?c=s.node.edges[d]:(c=new e.TokenSet,s.node.edges[d]=c),1==s.str.length&&(c["final"]=!0),n.push({node:c,editsRemaining:s.editsRemaining-1,str:h+s.str.slice(2)})}}}return i},e.TokenSet.fromString=function(t){for(var r=new e.TokenSet,i=r,n=0,s=t.length;n=e;t--){var r=this.uncheckedNodes[t],i=r.child.toString();i in this.minimizedNodes?r.parent.edges[r["char"]]=this.minimizedNodes[i]:(r.child._str=i,this.minimizedNodes[i]=r.child),this.uncheckedNodes.pop()}},e.Index=function(e){this.invertedIndex=e.invertedIndex,this.fieldVectors=e.fieldVectors,this.tokenSet=e.tokenSet,this.fields=e.fields,this.pipeline=e.pipeline},e.Index.prototype.search=function(t){return this.query(function(r){var i=new e.QueryParser(t,r);i.parse()})},e.Index.prototype.query=function(t){for(var r=new e.Query(this.fields),i=Object.create(null),n=Object.create(null),s=Object.create(null),o=Object.create(null),a=Object.create(null),u=0;u1?this._b=1:this._b=e},e.Builder.prototype.k1=function(e){this._k1=e},e.Builder.prototype.add=function(t,r){var i=t[this._ref],n=Object.keys(this._fields);this._documents[i]=r||{},this.documentCount+=1;for(var s=0;s=this.length)return e.QueryLexer.EOS;var t=this.str.charAt(this.pos);return this.pos+=1,t},e.QueryLexer.prototype.width=function(){return this.pos-this.start},e.QueryLexer.prototype.ignore=function(){this.start==this.pos&&(this.pos+=1),this.start=this.pos},e.QueryLexer.prototype.backup=function(){this.pos-=1},e.QueryLexer.prototype.acceptDigitRun=function(){var t,r;do t=this.next(),r=t.charCodeAt(0);while(r>47&&r<58);t!=e.QueryLexer.EOS&&this.backup()},e.QueryLexer.prototype.more=function(){return this.pos1&&(t.backup(),t.emit(e.QueryLexer.TERM)),t.ignore(),t.more())return e.QueryLexer.lexText},e.QueryLexer.lexEditDistance=function(t){return t.ignore(),t.acceptDigitRun(),t.emit(e.QueryLexer.EDIT_DISTANCE),e.QueryLexer.lexText},e.QueryLexer.lexBoost=function(t){return t.ignore(),t.acceptDigitRun(),t.emit(e.QueryLexer.BOOST),e.QueryLexer.lexText},e.QueryLexer.lexEOS=function(t){t.width()>0&&t.emit(e.QueryLexer.TERM)},e.QueryLexer.termSeparator=e.tokenizer.separator,e.QueryLexer.lexText=function(t){for(;;){var r=t.next();if(r==e.QueryLexer.EOS)return e.QueryLexer.lexEOS;if(92!=r.charCodeAt(0)){if(":"==r)return e.QueryLexer.lexField;if("~"==r)return t.backup(),t.width()>0&&t.emit(e.QueryLexer.TERM),e.QueryLexer.lexEditDistance;if("^"==r)return t.backup(),t.width()>0&&t.emit(e.QueryLexer.TERM),e.QueryLexer.lexBoost;if("+"==r&&1===t.width())return t.emit(e.QueryLexer.PRESENCE),e.QueryLexer.lexText;if("-"==r&&1===t.width())return t.emit(e.QueryLexer.PRESENCE),e.QueryLexer.lexText;if(r.match(e.QueryLexer.termSeparator))return e.QueryLexer.lexTerm}else t.escapeCharacter()}},e.QueryParser=function(t,r){this.lexer=new e.QueryLexer(t),this.query=r,this.currentClause={},this.lexemeIdx=0},e.QueryParser.prototype.parse=function(){this.lexer.run(),this.lexemes=this.lexer.lexemes;for(var t=e.QueryParser.parseClause;t;)t=t(this);return this.query},e.QueryParser.prototype.peekLexeme=function(){return this.lexemes[this.lexemeIdx]},e.QueryParser.prototype.consumeLexeme=function(){var e=this.peekLexeme();return this.lexemeIdx+=1,e},e.QueryParser.prototype.nextClause=function(){var e=this.currentClause;this.query.clause(e),this.currentClause={}},e.QueryParser.parseClause=function(t){var r=t.peekLexeme();if(void 0!=r)switch(r.type){case e.QueryLexer.PRESENCE:return e.QueryParser.parsePresence;case e.QueryLexer.FIELD:return e.QueryParser.parseField;case e.QueryLexer.TERM:return e.QueryParser.parseTerm;default:var i="expected either a field or a term, found "+r.type;throw r.str.length>=1&&(i+=" with value '"+r.str+"'"),new e.QueryParseError(i,r.start,r.end)}},e.QueryParser.parsePresence=function(t){var r=t.consumeLexeme();if(void 0!=r){switch(r.str){case"-":t.currentClause.presence=e.Query.presence.PROHIBITED;break;case"+":t.currentClause.presence=e.Query.presence.REQUIRED;break;default:var i="unrecognised presence operator'"+r.str+"'";throw new e.QueryParseError(i,r.start,r.end)}var n=t.peekLexeme();if(void 0==n){var i="expecting term or field, found nothing";throw new e.QueryParseError(i,r.start,r.end)}switch(n.type){case e.QueryLexer.FIELD:return e.QueryParser.parseField;case e.QueryLexer.TERM:return e.QueryParser.parseTerm;default:var i="expecting term or field, found '"+n.type+"'";throw new e.QueryParseError(i,n.start,n.end)}}},e.QueryParser.parseField=function(t){var r=t.consumeLexeme();if(void 0!=r){if(t.query.allFields.indexOf(r.str)==-1){var i=t.query.allFields.map(function(e){return"'"+e+"'"}).join(", "),n="unrecognised field '"+r.str+"', possible fields: "+i;throw new e.QueryParseError(n,r.start,r.end)}t.currentClause.fields=[r.str];var s=t.peekLexeme();if(void 0==s){var n="expecting term, found nothing";throw new e.QueryParseError(n,r.start,r.end)}switch(s.type){case e.QueryLexer.TERM:return e.QueryParser.parseTerm;default:var n="expecting term, found '"+s.type+"'";throw new e.QueryParseError(n,s.start,s.end)}}},e.QueryParser.parseTerm=function(t){var r=t.consumeLexeme();if(void 0!=r){t.currentClause.term=r.str.toLowerCase(),r.str.indexOf("*")!=-1&&(t.currentClause.usePipeline=!1);var i=t.peekLexeme();if(void 0==i)return void t.nextClause();switch(i.type){case e.QueryLexer.TERM:return t.nextClause(),e.QueryParser.parseTerm;case e.QueryLexer.FIELD:return t.nextClause(),e.QueryParser.parseField;case e.QueryLexer.EDIT_DISTANCE:return e.QueryParser.parseEditDistance;case e.QueryLexer.BOOST:return e.QueryParser.parseBoost;case e.QueryLexer.PRESENCE:return t.nextClause(),e.QueryParser.parsePresence;default:var n="Unexpected lexeme type '"+i.type+"'";throw new e.QueryParseError(n,i.start,i.end)}}},e.QueryParser.parseEditDistance=function(t){var r=t.consumeLexeme();if(void 0!=r){var i=parseInt(r.str,10);if(isNaN(i)){var n="edit distance must be numeric";throw new e.QueryParseError(n,r.start,r.end)}t.currentClause.editDistance=i;var s=t.peekLexeme();if(void 0==s)return void t.nextClause();switch(s.type){case e.QueryLexer.TERM:return t.nextClause(),e.QueryParser.parseTerm;case e.QueryLexer.FIELD:return t.nextClause(),e.QueryParser.parseField;case e.QueryLexer.EDIT_DISTANCE:return e.QueryParser.parseEditDistance;case e.QueryLexer.BOOST:return e.QueryParser.parseBoost;case e.QueryLexer.PRESENCE:return t.nextClause(),e.QueryParser.parsePresence;default:var n="Unexpected lexeme type '"+s.type+"'";throw new e.QueryParseError(n,s.start,s.end)}}},e.QueryParser.parseBoost=function(t){var r=t.consumeLexeme();if(void 0!=r){var i=parseInt(r.str,10);if(isNaN(i)){var n="boost must be numeric";throw new e.QueryParseError(n,r.start,r.end)}t.currentClause.boost=i;var s=t.peekLexeme();if(void 0==s)return void t.nextClause();switch(s.type){case e.QueryLexer.TERM:return t.nextClause(),e.QueryParser.parseTerm;case e.QueryLexer.FIELD:return t.nextClause(),e.QueryParser.parseField;case e.QueryLexer.EDIT_DISTANCE:return e.QueryParser.parseEditDistance;case e.QueryLexer.BOOST:return e.QueryParser.parseBoost;case e.QueryLexer.PRESENCE:return t.nextClause(),e.QueryParser.parsePresence;default:var n="Unexpected lexeme type '"+s.type+"'";throw new e.QueryParseError(n,s.start,s.end)}}},function(e,t){"function"==typeof define&&define.amd?define(t):"object"==typeof exports?module.exports=t():e.lunr=t()}(this,function(){return e})}(); 7 | -------------------------------------------------------------------------------- /assets/js/zzzz-search-data.json: -------------------------------------------------------------------------------- 1 | --- 2 | permalink: /assets/js/search-data.json 3 | --- 4 | { 5 | {%- assign i = 0 -%} 6 | {%- assign pages_array = | split: -%} 7 | {%- assign pages_array = pages_array | push: site.html_pages -%} 8 | {%- if site.just_the_docs.collections -%} 9 | {%- for collection_entry in site.just_the_docs.collections -%} 10 | {%- assign collection_key = collection_entry[0] -%} 11 | {%- assign collection_value = collection_entry[1] -%} 12 | {%- assign collection = site[collection_key] -%} 13 | {%- if collection_value.search_exclude != true -%} 14 | {%- assign pages_array = pages_array | push: collection -%} 15 | {%- endif -%} 16 | {%- endfor -%} 17 | {%- endif -%} 18 | {%- for pages in pages_array -%} 19 | {%- for page in pages -%} 20 | {%- if page.title and page.search_exclude != true -%} 21 | {%- assign page_content = page.content -%} 22 | {%- assign heading_level = site.search.heading_level | default: 2 -%} 23 | {%- for j in (2..heading_level) -%} 24 | {%- assign tag = '' -%} 32 | {%- assign title = titleAndContent[0] | replace_first: '>', '

' | split: '

' -%} 33 | {%- assign title = title[1] | strip_html -%} 34 | {%- assign content = titleAndContent[1] -%} 35 | {%- assign url = page.url -%} 36 | {%- if title == page.title and parts[0] == '' -%} 37 | {%- assign title_found = true -%} 38 | {%- else -%} 39 | {%- assign id = titleAndContent[0] -%} 40 | {%- assign id = id | split: 'id="' -%} 41 | {%- if id.size == 2 -%} 42 | {%- assign id = id[1] -%} 43 | {%- assign id = id | split: '"' -%} 44 | {%- assign id = id[0] -%} 45 | {%- capture url -%}{{ url | append: '#' | append: id }}{%- endcapture -%} 46 | {%- endif -%} 47 | {%- endif -%} 48 | {%- unless i == 0 -%},{%- endunless -%} 49 | "{{ i }}": { 50 | "doc": {{ page.title | jsonify }}, 51 | "title": {{ title | jsonify }}, 52 | "content": {{ content | replace: '", line 1, in 156 | TypeError: __init__() takes exactly 2 arguments (1 given) 157 | ``` 158 | 159 | 我相信你现在了解了 ```__init__``` 方法。 160 | 161 | - ```__getitem__``` 162 | 163 | 在类中实现 **getitem** 允许其实例使用 ```[]```(索引器)运算符。这是一个例子: 164 | 165 | ```python 166 | class GetTest(object): 167 | def __init__(self): 168 | self.info = { 169 | 'name':'Yasoob', 170 | 'country':'Pakistan', 171 | 'number':12345812 172 | } 173 | 174 | def __getitem__(self,i): 175 | return self.info[i] 176 | 177 | foo = GetTest() 178 | 179 | foo['name'] 180 | # Output: 'Yasoob' 181 | 182 | foo['number'] 183 | # Output: 12345812 184 | ``` 185 | 186 | 如果没有 ```__getitem__```方法,我们会遇到以下错误: 187 | 188 | ```python 189 | >>> foo['name'] 190 | 191 | Traceback (most recent call last): 192 | File "", line 1, in 193 | TypeError: 'GetTest' object has no attribute '__getitem__' 194 | ``` 195 | -------------------------------------------------------------------------------- /book/DataStructures/generators.md: -------------------------------------------------------------------------------- 1 | --- 2 | parent: 数据结构 3 | nav_order: 1 4 | --- 5 | 6 | 7 | # 生成器(Generators) 8 | 9 | 首先我们要理解迭代器(iterators)。根据维基百科,迭代器是一个让程序员可以遍历一个容器(特别是列表)的对象。然而,一个迭代器在遍历并读取一个容器的数据元素时,并不会执行一个迭代。你可能有点晕了,那我们来个慢动作。换句话说这里有三个部分: 10 | 11 | - 可迭代对象(Iterable) 12 | - 迭代器(Iterator) 13 | - 迭代(Iteration) 14 | 15 | 上面这些部分互相联系。我们会先各个击破来讨论他们,然后再讨论生成器(generators)。 16 | 17 | # 可迭代对象(Iterable) 18 | 19 | Python中任意的对象,只要它定义了可以返回一个迭代器的 ```__iter__``` 方法,或者定义了可以支持下标索引的 ```__getitem__``` 方法(这些双下划线方法会在其他章节中全面解释),那么它就是一个可迭代对象。简单说,可迭代对象就是能提供迭代器的任意对象。那迭代器又是什么呢? 20 | 21 | # 迭代器(Iterator) 22 | 23 | 任意对象,只要定义了 ```next```(Python2) 或者 ```__next__``` 方法,它就是一个迭代器。就这么简单。现在我们来理解迭代(iteration)。 24 | 25 | # 迭代(Iteration) 26 | 27 | 用简单的话讲,它就是从某个地方(比如一个列表)取出一个元素的过程。当我们使用一个循环来遍历某个东西时,这个过程本身就叫迭代。现在既然我们有了这些术语的基本理解,那我们开始理解生成器吧。 28 | 29 | # 生成器(Generators) 30 | 31 | 生成器也是一种迭代器,但是你只能对其迭代一次。这是因为它们并没有把所有的值存在内存中,而是在运行时生成值。你通过遍历来使用它们,要么用一个 “for” 循环,要么将它们传递给任意可以进行迭代的函数和结构。大多数时候生成器是以函数来实现的。然而,它们并不返回一个值,而是 ```yield``` (暂且译作“生出”)一个值。这里有个生成器函数的简单例子: 32 | 33 | ```python 34 | def generator_function(): 35 | for i in range(10): 36 | yield i 37 | 38 | for item in generator_function(): 39 | print(item) 40 | 41 | # Output: 0 42 | # 1 43 | # 2 44 | # 3 45 | # 4 46 | # 5 47 | # 6 48 | # 7 49 | # 8 50 | # 9 51 | ``` 52 | 53 | 这个案例并不是非常实用。生成器最佳应用场景是:你不想同一时间将所有计算出来的大量结果集分配到内存当中,特别是结果集里还包含循环。 54 | 55 | > 译者注:这样做会消耗大量资源 56 | 57 | 许多 Python 2 里的标准库函数都会返回列表,而 Python 3 都修改成了返回生成器,因为生成器占用更少的资源。 58 | 59 | 下面是一个计算斐波那契数列的生成器: 60 | 61 | ```python 62 | # generator version 63 | def fibon(n): 64 | a = b = 1 65 | for i in range(n): 66 | yield a 67 | a, b = b, a + b 68 | ``` 69 | 70 | 函数使用方法如下: 71 | 72 | ```python 73 | for x in fibon(1000000): 74 | print(x) 75 | ``` 76 | 77 | 用这种方式,我们可以不用担心它会使用大量资源。然而,之前如果我们这样来实现的话: 78 | 79 | ```python 80 | def fibon(n): 81 | a = b = 1 82 | result = [] 83 | for i in range(n): 84 | result.append(a) 85 | a, b = b, a + b 86 | return result 87 | ``` 88 | 89 | 这也许会在计算很大的输入参数时,用尽所有的资源。我们已经讨论过生成器使用一次迭代,但我们并没有测试过。在测试前你需要再知道一个Python内置函数:```next()```。它允许我们获取一个序列的下一个元素。那我们来验证下我们的理解: 90 | 91 | ```python 92 | def generator_function(): 93 | for i in range(3): 94 | yield i 95 | 96 | gen = generator_function() 97 | print(next(gen)) 98 | # Output: 0 99 | print(next(gen)) 100 | # Output: 1 101 | print(next(gen)) 102 | # Output: 2 103 | print(next(gen)) 104 | # Output: Traceback (most recent call last): 105 | # File "", line 1, in 106 | # StopIteration 107 | ``` 108 | 109 | 我们可以看到,在 ```yield``` 掉所有的值后,```next()``` 触发了一个 ```StopIteration``` 的异常。基本上这个异常告诉我们,所有的值都已经被 ```yield``` 完了。你也许会奇怪,为什么我们在使用 ```for``` 循环时没有这个异常呢?啊哈,答案很简单。```for``` 循环会自动捕捉到这个异常并停止调用 ```next()```。你知不知道Python中一些内置数据类型也支持迭代哦?我们这就去看看: 110 | 111 | ```python 112 | my_string = "Yasoob" 113 | next(my_string) 114 | # Output: Traceback (most recent call last): 115 | # File "", line 1, in 116 | # TypeError: str object is not an iterator 117 | ``` 118 | 119 | 好吧,这不是我们预期的。这个异常说那个 ```str``` 对象不是一个迭代器。对,就是这样!它是一个可迭代对象,而不是一个迭代器。这意味着它支持迭代,但我们不能直接对其进行迭代操作。那我们怎样才能对它实施迭代呢?是时候学习下另一个内置函数,```iter```。它将根据一个可迭代对象返回一个迭代器对象。这里是我们如何使用它: 120 | 121 | ```python 122 | my_string = "Yasoob" 123 | my_iter = iter(my_string) 124 | next(my_iter) 125 | # Output: 'Y' 126 | ``` 127 | 128 | 现在好多啦。我肯定你已经爱上了学习生成器。一定要记住,想要完全掌握这个概念,你只有使用它。确保你按照这个模式,并在生成器对你有意义的任何时候都使用它。你绝对不会失望的! 129 | -------------------------------------------------------------------------------- /book/DataTypes/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 数据类型 3 | nav_order: 5 4 | has_children: true 5 | --- 6 | 7 | # 数据类型 -------------------------------------------------------------------------------- /book/DataTypes/collections.md: -------------------------------------------------------------------------------- 1 | --- 2 | parent: 数据类型 3 | nav_order: 1 4 | --- 5 | 6 | 7 | # 容器(```Collections```) 8 | 9 | Python 附带一个模块,它包含许多容器数据类型,名字叫作 ```collections```。我们将讨论它的作用和用法。 10 | 11 | 我们将讨论的是: 12 | 13 | * `defaultdict` 14 | * `counter` 15 | * `deque` 16 | * `namedtuple` 17 | * `enum.Enum` (包含在 Python 3.4 以上) 18 | 19 | ## ```defaultdict``` 20 | 21 | 我个人使用 ```defaultdict``` 较多,与 ```dict``` 类型不同,你不需要检查 **key** 是否存在,所以我们能这样做: 22 | 23 | ```python 24 | from collections import defaultdict 25 | 26 | colours = ( 27 | ('Yasoob', 'Yellow'), 28 | ('Ali', 'Blue'), 29 | ('Arham', 'Green'), 30 | ('Ali', 'Black'), 31 | ('Yasoob', 'Red'), 32 | ('Ahmed', 'Silver'), 33 | ) 34 | 35 | favourite_colours = defaultdict(list) 36 | 37 | for name, colour in colours: 38 | favourite_colours[name].append(colour) 39 | 40 | print(favourite_colours) 41 | ``` 42 | 43 | ### 运行输出 44 | 45 | ```python 46 | # defaultdict(, 47 | # {'Arham': ['Green'], 48 | # 'Yasoob': ['Yellow', 'Red'], 49 | # 'Ahmed': ['Silver'], 50 | # 'Ali': ['Blue', 'Black'] 51 | # }) 52 | ``` 53 | 54 | 另一种重要的是例子就是:当你在一个字典中对一个键进行嵌套赋值时,如果这个键不存在,会触发 ```keyError``` 异常。```defaultdict``` 允许我们用一个聪明的方式绕过这个问题。 55 | 首先我分享一个使用 ```dict``` 触发 ```KeyError``` 的例子,然后提供一个使用 ```defaultdict``` 的解决方案。 56 | 57 | **问题**: 58 | 59 | ```python 60 | some_dict = {} 61 | some_dict['colours']['favourite'] = "yellow" 62 | 63 | ## 异常输出:KeyError: 'colours' 64 | ``` 65 | 66 | **解决方案**: 67 | 68 | ```python 69 | import collections 70 | tree = lambda: collections.defaultdict(tree) 71 | some_dict = tree() 72 | some_dict['colours']['favourite'] = "yellow" 73 | 74 | ## 运行正常 75 | ``` 76 | 77 | 你可以用 ```json.dumps``` 打印出 ```some_dict```,例如: 78 | 79 | ```python 80 | import json 81 | print(json.dumps(some_dict)) 82 | 83 | ## 输出: {"colours": {"favourite": "yellow"}} 84 | ``` 85 | 86 | ## ```counter``` 87 | 88 | Counter 是一个计数器,它可以帮助我们针对某项数据进行计数。比如它可以用来计算每个人喜欢多少种颜色: 89 | 90 | ```python 91 | from collections import Counter 92 | 93 | colours = ( 94 | ('Yasoob', 'Yellow'), 95 | ('Ali', 'Blue'), 96 | ('Arham', 'Green'), 97 | ('Ali', 'Black'), 98 | ('Yasoob', 'Red'), 99 | ('Ahmed', 'Silver'), 100 | ) 101 | 102 | favs = Counter(name for name, colour in colours) 103 | print(favs) 104 | 105 | ## 输出: 106 | ## Counter({ 107 | ## 'Yasoob': 2, 108 | ## 'Ali': 2, 109 | ## 'Arham': 1, 110 | ## 'Ahmed': 1 111 | ## }) 112 | ``` 113 | 114 | 我们也可以在利用它统计一个文件,例如: 115 | 116 | ```python 117 | with open('filename', 'rb') as f: 118 | line_count = Counter(f) 119 | print(line_count) 120 | ``` 121 | 122 | ## ```deque``` 123 | 124 | deque 提供了一个双端队列,你可以从头/尾两端添加或删除元素。要想使用它,首先我们要从 ```collections``` 中导入 ```deque``` 模块: 125 | 126 | ```python 127 | from collections import deque 128 | ``` 129 | 130 | 现在,你可以创建一个 ```deque``` 对象。 131 | 132 | ```python 133 | d = deque() 134 | ``` 135 | 136 | 它的用法就像python的 ```list```,并且提供了类似的方法,例如: 137 | 138 | ```python 139 | d = deque() 140 | d.append('1') 141 | d.append('2') 142 | d.append('3') 143 | 144 | print(len(d)) 145 | 146 | ## 输出: 3 147 | 148 | print(d[0]) 149 | 150 | ## 输出: '1' 151 | 152 | print(d[-1]) 153 | 154 | ## 输出: '3' 155 | ``` 156 | 157 | 你可以从两端取出(pop)数据: 158 | 159 | ```python 160 | d = deque(range(5)) 161 | print(len(d)) 162 | 163 | ## 输出: 5 164 | 165 | d.popleft() 166 | 167 | ## 输出: 0 168 | 169 | d.pop() 170 | 171 | ## 输出: 4 172 | 173 | print(d) 174 | 175 | ## 输出: deque([1, 2, 3]) 176 | ``` 177 | 178 | 我们也可以限制这个列表的大小,当超出你设定的限制时,数据会从对队列另一端被挤出去(pop)。 179 | 最好的解释是给出一个例子: 180 | 181 | ```python 182 | d = deque(maxlen=30) 183 | ``` 184 | 185 | 现在当你插入30条数据时,最左边一端的数据将从队列中删除。 186 | 187 | 你还可以从任一端扩展这个队列中的数据: 188 | 189 | ```python 190 | d = deque([1,2,3,4,5]) 191 | d.extendleft([0]) 192 | d.extend([6,7,8]) 193 | print(d) 194 | 195 | ## 输出: deque([0, 1, 2, 3, 4, 5, 6, 7, 8]) 196 | ``` 197 | 198 | ## ```namedtuple``` 199 | 200 | 您可能已经熟悉元组。 201 | 一个元组是一个不可变的列表,你可以存储一个数据的序列,它和命名元组(```namedtuples```)非常像,但有几个关键的不同。 202 | 主要相似点是都不像列表,你不能修改元组中的数据。为了获取元组中的数据,你需要使用整数作为索引: 203 | 204 | ```python 205 | man = ('Ali', 30) 206 | print(man[0]) 207 | 208 | ## 输出: Ali 209 | ``` 210 | 211 | 嗯,那 ```namedtuples``` 是什么呢?它把元组变成一个针对简单任务的容器。你不必使用整数索引来访问一个 ```namedtuples``` 的数据。你可以像字典(```dict```)一样访问 ```namedtuples```,但 ```namedtuples``` 是不可变的。 212 | 213 | ```python 214 | from collections import namedtuple 215 | 216 | Animal = namedtuple('Animal', 'name age type') 217 | perry = Animal(name="perry", age=31, type="cat") 218 | 219 | print(perry) 220 | 221 | ## 输出: Animal(name='perry', age=31, type='cat') 222 | 223 | print(perry.name) 224 | 225 | ## 输出: 'perry' 226 | ``` 227 | 228 | 现在你可以看到,我们可以用名字来访问 ```namedtuple``` 中的数据。我们再继续分析它。一个命名元组(```namedtuple```)有两个必需的参数。它们是元组名称和字段名称。 229 | 230 | 在上面的例子中,我们的元组名称是 ```Animal```,字段名称是 ```name```,```age``` 和 ```type```。 231 | ```namedtuple``` 让你的元组变得**自文档**了。你只要看一眼就很容易理解代码是做什么的。 232 | 你也不必使用整数索引来访问一个命名元组,这让你的代码更易于维护。 233 | 而且,**```namedtuple``` 的每个实例没有对象字典**,所以它们很轻量,与普通的元组比,并不需要更多的内存。这使得它们比字典更快。 234 | 235 | 然而,要记住它是一个元组,属性值在 ```namedtuple``` 中是不可变的,所以下面的代码不能工作: 236 | 237 | ```python 238 | from collections import namedtuple 239 | 240 | Animal = namedtuple('Animal', 'name age type') 241 | perry = Animal(name="perry", age=31, type="cat") 242 | perry.age = 42 243 | 244 | ## 输出: 245 | ## Traceback (most recent call last): 246 | ## File "", line 1, in 247 | ## AttributeError: can't set attribute 248 | ``` 249 | 250 | 你应该使用命名元组来让代码**自文档**,**它们向后兼容于普通的元组**,这意味着你可以既使用整数索引,也可以使用名称来访问 ```namedtuple```: 251 | 252 | ```python 253 | from collections import namedtuple 254 | 255 | Animal = namedtuple('Animal', 'name age type') 256 | perry = Animal(name="perry", age=31, type="cat") 257 | print(perry[0]) 258 | 259 | ## 输出: perry 260 | ``` 261 | 262 | 最后,你可以将一个命名元组转换为字典,方法如下: 263 | 264 | ```python 265 | from collections import namedtuple 266 | 267 | Animal = namedtuple('Animal', 'name age type') 268 | perry = Animal(name="Perry", age=31, type="cat") 269 | print(perry._asdict()) 270 | 271 | ## 输出: OrderedDict([('name', 'Perry'), ('age', 31), ... 272 | ``` 273 | 274 | ## ```enum.Enum``` (Python 3.4+) 275 | 276 | 另一个有用的容器是枚举对象,它属于 ```enum``` 模块,存在于 Python 3.4 以上版本中(同时作为一个独立的 PyPI 包 ```enum34``` 供老版本使用)。Enums(枚举类型)基本上是一种组织各种东西的方式。 277 | 278 | 让我们回顾一下上一个 ```Animal```命名元组的例子。 279 | 它有一个 ```type``` 字段,问题是,```type``` 是一个字符串。 280 | 那么问题来了,万一程序员输入了 ```Cat```,因为他按到了 Shift 键,或者输入了 ```CAT```,甚至 ```kitten```? 281 | 282 | 枚举可以帮助我们避免这个问题,通过不使用字符串。考虑以下这个例子: 283 | 284 | ```python 285 | from collections import namedtuple 286 | from enum import Enum 287 | 288 | class Species(Enum): 289 | cat = 1 290 | dog = 2 291 | horse = 3 292 | aardvark = 4 293 | butterfly = 5 294 | owl = 6 295 | platypus = 7 296 | dragon = 8 297 | unicorn = 9 298 | # 依次类推 299 | 300 | # 但我们并不想关心同一物种的年龄,所以我们可以使用一个别名 301 | kitten = 1 # (译者注:幼小的猫咪) 302 | puppy = 2 # (译者注:幼小的狗狗) 303 | 304 | Animal = namedtuple('Animal', 'name age type') 305 | perry = Animal(name="Perry", age=31, type=Species.cat) 306 | drogon = Animal(name="Drogon", age=4, type=Species.dragon) 307 | tom = Animal(name="Tom", age=75, type=Species.cat) 308 | charlie = Animal(name="Charlie", age=2, type=Species.kitten) 309 | ``` 310 | 311 | ### 现在,我们进行一些测试: 312 | 313 | ```python 314 | >>> charlie.type == tom.type 315 | True 316 | >>> charlie.type 317 | 318 | ``` 319 | 320 | 这样就没那么容易错误,我们必须更明确,而且我们应该只使用定义后的枚举类型。 321 | 322 | 有三种方法访问枚举数据,例如以下方法都可以获取到 ```cat``` 的值: 323 | 324 | ```python 325 | Species(1) 326 | Species['cat'] 327 | Species.cat 328 | ``` 329 | 330 | 这只是一个快速浏览 ```collections``` 模块的介绍,建议你阅读本文最后的官方文档。 331 | -------------------------------------------------------------------------------- /book/DataTypes/mutation.md: -------------------------------------------------------------------------------- 1 | --- 2 | parent: 数据类型 3 | nav_order: 2 4 | --- 5 | 6 | # 对象变动(Mutation) 7 | 8 | Python 中可变(**mutable**)与不可变(**immutable**)的数据类型让新手很是头痛。简单的说,可变(mutable)意味着"可以被改动",而不可变(immutable)的意思是“常量(constant)”。想把脑筋转动起来吗?考虑下这个例子: 9 | 10 | ```python 11 | foo = ['hi'] 12 | print(foo) 13 | # Output: ['hi'] 14 | 15 | bar = foo 16 | bar += ['bye'] 17 | print(foo) 18 | # Output: ['hi', 'bye'] 19 | ``` 20 | 21 | 刚刚发生了什么?我们预期的不是那样!我们期望看到是这样的: 22 | 23 | ```python 24 | foo = ['hi'] 25 | print(foo) 26 | # Output: ['hi'] 27 | 28 | bar = foo 29 | bar += ['bye'] 30 | 31 | print(foo) 32 | # Output: ['hi'] 33 | 34 | print(bar) 35 | # Output: ['hi', 'bye'] 36 | ``` 37 | 38 | 这不是一个 bug。这是对象可变性(**mutability**)在作怪。每当你将一个变量赋值为另一个可变类型的变量时,对这个数据的任意改动会同时反映到这两个变量上去。新变量只不过是老变量的一个别名而已。这个情况只是针对可变数据类型。下面的函数和可变数据类型让你一下就明白了: 39 | 40 | ```python 41 | def add_to(num, target=[]): 42 | target.append(num) 43 | return target 44 | 45 | add_to(1) 46 | # Output: [1] 47 | 48 | add_to(2) 49 | # Output: [1, 2] 50 | 51 | add_to(3) 52 | # Output: [1, 2, 3] 53 | ``` 54 | 55 | 你可能预期它表现的不是这样子。你可能希望,当你调用 ```add_to``` 时,有一个新的列表被创建,就像这样: 56 | 57 | ```python 58 | def add_to(num, target=[]): 59 | target.append(num) 60 | return target 61 | 62 | add_to(1) 63 | # Output: [1] 64 | 65 | add_to(2) 66 | # Output: [2] 67 | 68 | add_to(3) 69 | # Output: [3] 70 | ``` 71 | 72 | 啊哈!这次又没有达到预期,是列表的可变性在作怪。在Python中当函数被定义时,默认参数只会运算一次,而不是每次被调用时都会重新运算。你应该永远不要定义可变类型的默认参数,除非你知道你正在做什么。你应该像这样做: 73 | 74 | ```python 75 | def add_to(element, target=None): 76 | if target is None: 77 | target = [] 78 | target.append(element) 79 | return target 80 | ``` 81 | 82 | 现在每当你在调用这个函数不传入 ```target``` 参数的时候,一个新的列表会被创建。举个例子: 83 | 84 | ```python 85 | add_to(42) 86 | # Output: [42] 87 | 88 | add_to(42) 89 | # Output: [42] 90 | 91 | add_to(42) 92 | # Output: [42] 93 | ``` 94 | -------------------------------------------------------------------------------- /book/DataTypes/slots_magic.md: -------------------------------------------------------------------------------- 1 | --- 2 | parent: 数据类型 3 | nav_order: 3 4 | --- 5 | 6 | # ```__slots__``` 魔法 7 | 8 | 在 Python 中,每个类都有实例属性。默认情况下 Python 用一个字典来保存一个对象的实例属性。这非常有用,因为它允许我们在运行时去设置任意的新属性。 9 | 10 | 然而,对于有着已知属性的小类来说,它可能是个瓶颈。这个字典浪费了很多内存。Python 不能在对象创建时直接分配一个固定量的内存来保存所有的属性。因此如果你创建许多对象(我指的是成千上万个),它会消耗掉很多内存。 11 | 不过还是有一个方法来规避这个问题。这个方法需要使用 ```__slots__``` 来告诉 Python 不要使用字典,而且只给一个固定集合的属性分配空间。 12 | 13 | 这里是一个使用与不使用 ```__slots__``` 的例子: 14 | 15 | - 不使用 ```__slots__```: 16 | 17 | ```python 18 | class MyClass(object): 19 | def __init__(self, name, identifier): 20 | self.name = name 21 | self.identifier = identifier 22 | self.set_up() 23 | # ... 24 | ``` 25 | 26 | - 使用 ```__slots__```: 27 | 28 | ```python 29 | class MyClass(object): 30 | __slots__ = ['name', 'identifier'] 31 | def __init__(self, name, identifier): 32 | self.name = name 33 | self.identifier = identifier 34 | self.set_up() 35 | # ... 36 | ``` 37 | 38 | 第二段代码会为你的内存减轻负担。通过这个技巧,有些人已经看到内存占用率几乎40%~50%的减少。 39 | 40 | 稍微备注一下,你也许需要试一下 PyPy。它已经默认地做了所有这些优化。 41 | 42 | 以下你可以看到一个例子,它用 IPython 来展示在有与没有 ```__slots__``` 情况下的精确内存占用,感谢 https://github.com/ianozsvald/ipython_memory_usage 43 | 44 | ```python 45 | Python 3.4.3 (default, Jun 6 2015, 13:32:34) 46 | Type "copyright", "credits" or "license" for more information. 47 | 48 | IPython 4.0.0 -- An enhanced Interactive Python. 49 | ? -> Introduction and overview of IPython's features. 50 | %quickref -> Quick reference. 51 | help -> Python's own help system. 52 | object? -> Details about 'object', use 'object??' for extra details. 53 | 54 | In [1]: import ipython_memory_usage.ipython_memory_usage as imu 55 | 56 | In [2]: imu.start_watching_memory() 57 | In [2] used 0.0000 MiB RAM in 5.31s, peaked 0.00 MiB above current, total RAM usage 15.57 MiB 58 | 59 | In [3]: %cat slots.py 60 | class MyClass(object): 61 | __slots__ = ['name', 'identifier'] 62 | def __init__(self, name, identifier): 63 | self.name = name 64 | self.identifier = identifier 65 | 66 | num = 1024*256 67 | x = [MyClass(1,1) for i in range(num)] 68 | In [3] used 0.2305 MiB RAM in 0.12s, peaked 0.00 MiB above current, total RAM usage 15.80 MiB 69 | 70 | In [4]: from slots import * 71 | In [4] used 9.3008 MiB RAM in 0.72s, peaked 0.00 MiB above current, total RAM usage 25.10 MiB 72 | 73 | In [5]: %cat noslots.py 74 | class MyClass(object): 75 | def __init__(self, name, identifier): 76 | self.name = name 77 | self.identifier = identifier 78 | 79 | num = 1024*256 80 | x = [MyClass(1,1) for i in range(num)] 81 | In [5] used 0.1758 MiB RAM in 0.12s, peaked 0.00 MiB above current, total RAM usage 25.28 MiB 82 | 83 | In [6]: from noslots import * 84 | In [6] used 22.6680 MiB RAM in 0.80s, peaked 0.00 MiB above current, total RAM usage 47.95 MiB 85 | ``` 86 | -------------------------------------------------------------------------------- /book/Extras/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 其他知识 3 | nav_order: 7 4 | has_children: true 5 | --- 6 | 7 | # 其他知识 -------------------------------------------------------------------------------- /book/Extras/c_extensions.md: -------------------------------------------------------------------------------- 1 | --- 2 | parent: 其他知识 3 | nav_order: 3 4 | --- 5 | 6 | 7 | # 使用 C 扩展 8 | 9 | CPython 还为开发者实现了一个有趣的特性,使用 Python 可以轻松调用 C 代码。 10 | 11 | 开发者有三种方法可以在自己的 Python 代码中来调用 C 编写的函数──```ctypes```,```SWIG```,```Python/C API```。每种方式也都有各自的利弊。 12 | 13 | 首先,我们要明确为什么要在 Python 中调用 C? 14 | 15 | 常见原因如下: 16 | 17 | - 你要提升代码的运行速度,而且你知道 C 要比 Python 快50倍以上; 18 | - C 语言中有很多传统类库,而且有些正是你想要的,但你又不想用 Python 去重写它们; 19 | - 想对从内存到文件接口这样的底层资源进行访问; 20 | - 不需要理由,就是想这样做。 21 | 22 | 23 | # CTypes 24 | 25 | Python 中的 [ctypes 模块](https://docs.python.org/2/library/ctypes.html)可能是 Python 调用 C 方法中最简单的一种。ctypes 模块提供了和 C 语言兼容的数据类型和函数来加载 dll 文件,因此在调用时不需对源文件做任何的修改。也正是如此奠定了这种方法的简单性。 26 | 27 | 示例如下 28 | 29 | 实现两数求和的C代码,保存为 ```add.c```: 30 | 31 | ```C 32 | //sample C file to add 2 numbers - int and floats 33 | 34 | #include 35 | 36 | int add_int(int, int); 37 | float add_float(float, float); 38 | 39 | int add_int(int num1, int num2){ 40 | return num1 + num2; 41 | 42 | } 43 | 44 | float add_float(float num1, float num2){ 45 | return num1 + num2; 46 | 47 | } 48 | ``` 49 | 50 | 接下来将C文件编译为 ```.so``` 文件(windows 下为 DLL)。下面操作会生成 ```adder.so``` 文件: 51 | 52 | ```Shell 53 | #For Linux 54 | $ gcc -shared -Wl,-soname,adder -o adder.so -fPIC add.c 55 | 56 | #For Mac 57 | $ gcc -shared -Wl,-install_name,adder.so -o adder.so -fPIC add.c 58 | 59 | ``` 60 | 61 | 现在在你的Python代码中来调用它 62 | 63 | ```Python 64 | from ctypes import * 65 | 66 | #load the shared object file 67 | adder = CDLL('./adder.so') 68 | 69 | #Find sum of integers 70 | res_int = adder.add_int(4,5) 71 | print "Sum of 4 and 5 = " + str(res_int) 72 | 73 | #Find sum of floats 74 | a = c_float(5.5) 75 | b = c_float(4.1) 76 | 77 | add_float = adder.add_float 78 | add_float.restype = c_float 79 | print "Sum of 5.5 and 4.1 = ", str(add_float(a, b)) 80 | 81 | ``` 82 | 83 | 输出如下 84 | 85 | ```Shell 86 | Sum of 4 and 5 = 9 87 | Sum of 5.5 and 4.1 = 9.60000038147 88 | ``` 89 | 90 | 在这个例子中,C 文件是自解释的,它包含两个函数,分别实现了整形求和和浮点型求和。 91 | 92 | 在Python文件中,一开始先导入 ctypes 模块,然后使用 CDLL 函数来加载我们创建的库文件。这样我们就可以通过变量 ```adder``` 来使用 C 类库中的函数了。当 ```adder.add_int()``` 被调用时,内部将发起一个对 C 函数 ```add_int``` 的调用。ctypes 接口允许我们在调用 C 函数时使用原生 Python 中默认的字符串型和整型。 93 | 94 | 而对于其他类似布尔型和浮点型这样的类型,必须要使用正确的 ctype 类型才可以。如向 ```adder.add_float()``` 函数传参时, 我们要先将Python中的十进制值转化为 c_float 类型,然后才能传送给 C 函数。这种方法虽然简单,清晰,但是却很受限。例如,并不能在 C 中对对象进行操作。 95 | 96 | # SWIG 97 | 98 | SWIG 是 Simplified Wrapper and Interface Generator 的缩写,是 Python 中调用C代码的另一种方法。在这个方法中,开发人员必须编写一个额外的接口文件来作为 SWIG(终端工具)的入口。 99 | 100 | Python开发者一般不会采用这种方法,因为大多数情况它会带来不必要的复杂。而当你有一个 C/C++ 代码库需要被多种语言调用时,这将是个非常不错的选择。 101 | 102 | 示例如下(来自[SWIG官网](http://www.swig.org/tutorial.html)) 103 | 104 | ```example.c``` 文件中的C代码包含了不同的变量和函数: 105 | 106 | ```C 107 | #include 108 | double My_variable = 3.0; 109 | 110 | int fact(int n) { 111 | if (n <= 1) return 1; 112 | else return n*fact(n-1); 113 | 114 | } 115 | 116 | int my_mod(int x, int y) { 117 | return (x%y); 118 | 119 | } 120 | 121 | char *get_time() 122 | { 123 | time_t ltime; 124 | time(<ime); 125 | return ctime(<ime); 126 | 127 | } 128 | ``` 129 | 130 | 编译它: 131 | 132 | ```Shell 133 | unix % swig -python example.i 134 | unix % gcc -c example.c example_wrap.c \ 135 | -I/usr/local/include/python2.1 136 | unix % ld -shared example.o example_wrap.o -o _example.so 137 | 138 | ``` 139 | 140 | 最后,Python的输出: 141 | 142 | ```Python 143 | >>> import example 144 | >>> example.fact(5) 145 | 120 146 | >>> example.my_mod(7,3) 147 | 1 148 | >>> example.get_time() 149 | 'Sun Feb 11 23:01:07 1996' 150 | >>> 151 | ``` 152 | 153 | 我们可以看到,使用SWIG确实达到了同样的效果,虽然下了更多的工夫,但如果你的目标是多语言还是很值得的。 154 | 155 | # Python/C API 156 | 157 | [Python/C API](https://docs.python.org/2/c-api/) 可能是被最广泛使用的方法。它不仅简单,而且可以在 C 代码中操作你的 Python 对象。 158 | 159 | 这种方法需要以特定的方式来编写 C 代码以供 Python 去调用它。所有的 Python 对象都被表示为一种叫做 PyObject 的结构体,并且 ```Python.h``` 头文件中提供了各种操作它的函数。例如,如果 PyObject 表示为 PyListType(列表类型)时,那么我们便可以使用 ```PyList_Size()``` 函数来获取该结构的长度,类似 Python 中的 ```len(list)``` 函数。大部分对Python原生对象的基础函数和操作在 ```Python.h``` 头文件中都能找到。 160 | 161 | ## 示例 162 | 163 | 编写一个 C 扩展,添加所有元素到一个 Python 列表(所有元素都是数字)。 164 | 165 | 来看一下我们要实现的效果,这里演示了用Python调用C扩展的代码 166 | 167 | ```Python 168 | #Though it looks like an ordinary python import, the addList module is implemented in C 169 | import addList 170 | 171 | l = [1,2,3,4,5] 172 | print "Sum of List - " + str(l) + " = " + str(addList.add(l)) 173 | 174 | ``` 175 | 176 | 上面的代码和普通的 Python 文件并没有什么分别,导入并使用了另一个叫做 ```addList``` 的Python模块。唯一差别就是这个模块并不是用 Python 编写的,而是 C。 177 | 178 | 接下来我们看看如何用C编写 ```addList``` 模块,这可能看起来有点让人难以接受,但是一旦你了解了这之中的各种组成,你就可以一往无前了。 179 | 180 | ```C 181 | //Python.h has all the required function definitions to manipulate the Python objects 182 | #include 183 | 184 | //This is the function that is called from your python code 185 | static PyObject* addList_add(PyObject* self, PyObject* args){ 186 | 187 | PyObject * listObj; 188 | 189 | //The input arguments come as a tuple, we parse the args to get the various variables 190 | //In this case it's only one list variable, which will now be referenced by listObj 191 | if (! PyArg_ParseTuple( args, "O", &listObj )) 192 | return NULL; 193 | 194 | //length of the list 195 | long length = PyList_Size(listObj); 196 | 197 | //iterate over all the elements 198 | int i, sum =0; 199 | for (i = 0; i < length; i++) { 200 | //get an element out of the list - the element is also a python objects 201 | PyObject* temp = PyList_GetItem(listObj, i); 202 | //we know that object represents an integer - so convert it into C long 203 | long elem = PyInt_AsLong(temp); 204 | sum += elem; 205 | } 206 | 207 | //value returned back to python code - another python object 208 | //build value here converts the C long to a python integer 209 | return Py_BuildValue("i", sum); 210 | 211 | } 212 | 213 | //This is the docstring that corresponds to our 'add' function. 214 | static char addList_docs[] = 215 | "add( ): add all elements of the list\n"; 216 | 217 | /* This table contains the relavent info mapping - 218 | , , 219 | , 220 | */ 221 | static PyMethodDef addList_funcs[] = { 222 | {"add", (PyCFunction)addList_add, METH_VARARGS, addList_docs}, 223 | {NULL, NULL, 0, NULL} 224 | 225 | }; 226 | 227 | /* 228 | addList is the module name, and this is the initialization block of the module. 229 | , , 230 | */ 231 | PyMODINIT_FUNC initaddList(void){ 232 | Py_InitModule3("addList", addList_funcs, 233 | "Add all ze lists"); 234 | 235 | } 236 | 237 | ``` 238 | 239 | 逐步解释 240 | 241 | - ```Python.h``` 头文件中包含了所有需要的类型(Python对象类型的表示)和函数定义(对Python对象的操作); 242 | - 接下来我们编写将要在 Python 调用的函数, 函数传统的命名方式由{模块名}_{函数名}组成,所以我们将其命名为 ```addList_add```; 243 | - 然后填写想在模块内实现函数的相关信息表,每行一个函数,以空行作为结束 244 | - 最后的模块初始化块签名为 ```PyMODINIT_FUNC init{模块名}```。 245 | 246 | 函数 ```addList_add``` 接受的参数类型为 PyObject 类型结构(同时也表示为元组类型,因为 Python 中万物皆为对象,所以我们先用 PyObject 来定义)。传入的参数则通过 ```PyArg_ParseTuple()``` 来解析。第一个参数是被解析的参数变量。第二个参数是一个字符串,告诉我们如何去解析元组中每一个元素。字符串的第 n 个字母正是代表着元组中第 n 个参数的类型。例如,"i" 代表整形,"s" 代表字符串类型, "O" 则代表一个 Python 对象。接下来的参数都是你想要通过 ```PyArg_ParseTuple()``` 函数解析并保存的元素。这样参数的数量和模块中函数期待得到的参数数量就可以保持一致,并保证了位置的完整性。例如,我们想传入一个字符串,一个整数和一个 Python 列表,可以这样去写: 247 | 248 | ```C 249 | int n; 250 | char *s; 251 | PyObject* list; 252 | PyArg_ParseTuple(args, "siO", &n, &s, &list); 253 | 254 | ``` 255 | 256 | 在这种情况下,我们只需要提取一个列表对象,并将它存储在 ```listObj``` 变量中。然后用列表对象中的 ```PyList_Size()``` 函数来获取它的长度。就像 Python 中调用```len(list)```。 257 | 258 | 现在我们通过循环列表,使用 ```PyList_GetItem(list, index)``` 函数来获取每个元素。这将返回一个 ```PyObject*``` 对象。既然 Python 对象也能表示 ```PyIntType```,我们只要使用 ```PyInt_AsLong(PyObj *)``` 函数便可获得我们所需要的值。我们对每个元素都这样处理,最后再得到它们的总和。 259 | 260 | 总和将被转化为一个Python对象并通过 ```Py_BuildValue()``` 返回给 Python 代码,这里的 i 表示我们要返回一个 Python 整形对象。 261 | 262 | 现在我们已经编写完 C 模块了。将下列代码保存为 ```setup.py```: 263 | 264 | ```Python 265 | #build the modules 266 | 267 | from distutils.core import setup, Extension 268 | 269 | setup(name='addList', version='1.0', \ 270 | ext_modules=[Extension('addList', ['adder.c'])]) 271 | ``` 272 | 273 | 并且运行: 274 | 275 | ```Shell 276 | python setup.py install 277 | ``` 278 | 279 | 现在应该已经将我们的 C 文件编译安装到我们的 Python 模块中了。 280 | 281 | 在一番辛苦后,让我们来验证下我们的模块是否有效: 282 | 283 | ```Python 284 | #module that talks to the C code 285 | import addList 286 | 287 | l = [1,2,3,4,5] 288 | print "Sum of List - " + str(l) + " = " + str(addList.add(l)) 289 | ``` 290 | 291 | 输出结果如下: 292 | 293 | ```Shell 294 | Sum of List - [1, 2, 3, 4, 5] = 15 295 | ``` 296 | 297 | 如你所见,我们已经使用 Python.h API 成功开发出了我们第一个 Python C 扩展。这种方法看似复杂,但你一旦习惯,它将变的非常有效。 298 | 299 | Python 调用 C 代码的另一种方式便是使用 [Cython](http://cython.org/) 让 Python 编译的更快。但是 Cython 和传统的 Python 比起来可以将它理解为另一种语言,所以我们就不在这里过多描述了。 300 | -------------------------------------------------------------------------------- /book/Extras/onelines.md: -------------------------------------------------------------------------------- 1 | --- 2 | parent: 其他知识 3 | nav_order: 1 4 | --- 5 | 6 | # 一行式 7 | 8 | 本章节,我将向大家展示一些一行式的 Python 命令,这些程序将对你非常有帮助。 9 | 10 | **简易Web Server** 11 | 12 | 你是否想过通过网络快速共享文件?好消息,Python为你提供了这样的功能。进入到你要共享文件的目录下并在命令行中运行下面的代码: 13 | 14 | ```sh 15 | # Python 2 16 | python -m SimpleHTTPServer 17 | 18 | # Python 3 19 | python -m http.server 20 | ``` 21 | 22 | **漂亮的打印** 23 | 24 | 你可以在 Python REPL 漂亮的打印出列表和字典。这里是相关的代码: 25 | 26 | ```python 27 | from pprint import pprint 28 | 29 | my_dict = {'name': 'Yasoob', 'age': 'undefined', 'personality': 'awesome'} 30 | pprint(my_dict) 31 | 32 | ``` 33 | 34 | 这种方法在字典上更为有效。此外,如果你想快速漂亮的从文件打印出 json 数据,那么你可以这么做: 35 | 36 | ```sh 37 | cat file.json | python -m json.tool 38 | ``` 39 | 40 | **脚本性能分析** 41 | 这可能在定位你的脚本中的性能瓶颈时,会非常奏效: 42 | 43 | ```sh 44 | python -m cProfile my_script.py 45 | ``` 46 | 47 | 备注:```cProfile``` 是一个比 ```profile``` 更快的实现,因为它是用c写的。 48 | 49 | **CSV 转换为 json** 50 | 51 | 在命令行执行这条指令 52 | 53 | ```sh 54 | python -c "import csv,json;print json.dumps(list(csv.reader(open('csv_file.csv'))))" 55 | ``` 56 | 57 | 确保更换 ```csv_file.csv``` 为你想要转换的 csv 文件。 58 | 59 | **列表辗平** 60 | 61 | 您可以通过使用 ```itertools``` 包中的 ```itertools.chain.from_iterable``` 轻松快速的辗平一个列表。下面是一个简单的例子: 62 | 63 | ```python 64 | a_list = [[1, 2], [3, 4], [5, 6]] 65 | print(list(itertools.chain.from_iterable(a_list))) 66 | # Output: [1, 2, 3, 4, 5, 6] 67 | 68 | # or 69 | print(list(itertools.chain(*a_list))) 70 | # Output: [1, 2, 3, 4, 5, 6] 71 | ``` 72 | 73 | **一行式的构造器** 74 | 75 | 避免类初始化时大量重复的赋值语句: 76 | 77 | ```python 78 | class A(object): 79 | def __init__(self, a, b, c, d, e, f): 80 | self.__dict__.update({k: v for k, v in locals().items() if k != 'self'}) 81 | ``` 82 | 83 | 更多的一行方法请参考[Python官方文档](https://wiki.python.org/moin/Powerful%20Python%20One-Liners)。 84 | -------------------------------------------------------------------------------- /book/Extras/targeting_python2_3.md: -------------------------------------------------------------------------------- 1 | --- 2 | parent: 其他知识 3 | nav_order: 2 4 | --- 5 | 6 | 7 | # 兼容 Python 2 和 3 8 | 9 | 很多时候你可能希望你开发的程序能够同时兼容 Python2+ 和 Python3+。 10 | 11 | 试想你有一个非常出名的 Python 模块被很多开发者使用着,但并不是所有人都只使用 Python2 或者 Python3。这时候你有两个办法。第一个办法是开发两个模块,针对 Python2 一个,针对 Python3一个。还有一个办法就是调整你现在的代码使其同时兼容 Python2 和 Python3。 12 | 13 | 本节中,我将介绍一些技巧,让你的脚本同时兼容 Python2 和 Python3。 14 | 15 | **Future 模块导入** 16 | 17 | 第一种也是最重要的方法,就是导入 ```__future__``` 模块。它可以帮你在 Python2 中导入 Python3 的功能。这有一组例子: 18 | 19 | 上下文管理器是 Python2.6+ 引入的新特性,如果你想在 Python2.5 中使用它可以这样做: 20 | 21 | ```python 22 | from __future__ import with_statement 23 | 24 | ``` 25 | 26 | 在 Python3 中 ```print``` 已经变为一个函数。如果你想在 Python2 中使用它可以通过 ```__future__``` 导入: 27 | 28 | ```python 29 | print 30 | # Output: 31 | 32 | from __future__ import print_function 33 | print(print) 34 | # Output: 35 | ``` 36 | 37 | **模块重命名** 38 | 39 | 首先,告诉我你是如何在你的脚本中导入模块的。大多时候我们会这样做: 40 | 41 | ```python 42 | import foo 43 | # or 44 | from foo import bar 45 | ``` 46 | 47 | 你知道么,其实你也可以这样做: 48 | 49 | ```python 50 | import foo as foo 51 | ``` 52 | 53 | 这样做可以起到和上面代码同样的功能,但最重要的是它能让你的脚本同时兼容 Python2 和 Python3。现在我们来看下面的代码: 54 | 55 | ```python 56 | try: 57 | import urllib.request as urllib_request # for Python 3 58 | except ImportError: 59 | import urllib2 as urllib_request # for Python 2 60 | 61 | ``` 62 | 63 | 让我来稍微解释一下上面的代码。 64 | 我们将模块导入代码包装在 ```try/except``` 语句中。我们是这样做是因为在 Python 2 中并没有 ```urllib.request``` 模块。这将引起一个 ```ImportError``` 异常。而在Python2中 ```urllib.request``` 的功能则是由 ```urllib2``` 提供的。所以,当我们试图在 Python2 中导入 ```urllib.request``` 模块的时候,一旦我们捕获到 ```ImportError``` 我们将通过导入 ```urllib2``` 模块来代替它。 65 | 66 | 最后,你要了解 ```as``` 关键字的作用。它将导入的模块映射到 ```urllib.request```,所以我们通过 ```urllib_request``` 这个别名就可以使用 ```urllib2``` 中的所有类和方法了。 67 | 68 | **过期的 Python2 内置功能** 69 | 70 | 另一个需要了解的事情就是 Python2 中有12个内置功能在 Python3 中已经被移除了。要确保在 Python2 代码中不要出现这些功能来保证对 Python3 的兼容。这有一个强制让你放弃12内置功能的方法: 71 | 72 | ```python 73 | from future.builtins.disabled import * 74 | 75 | ``` 76 | 77 | 现在,只要你尝试在 Python3 中使用这些被遗弃的模块时,就会抛出一个 ```NameError``` 异常如下: 78 | 79 | ```python 80 | from future.builtins.disabled import * 81 | 82 | apply() 83 | # Output: NameError: obsolete Python 2 builtin apply is disabled 84 | 85 | ``` 86 | 87 | **标准库向下兼容的外部支持** 88 | 89 | 有一些包在非官方的支持下为 Python2 提供了 Python3 的功能。例如,我们有: 90 | 91 | * enum ```pip install enum34``` 92 | * singledispatch ```pip install singledispatch``` 93 | * pathlib ```pip install pathlib``` 94 | 95 | 想更多了解,在Python文档中有一个[全面的指南](https://docs.python.org/3/howto/pyporting.html)可以帮助你让你的代码同时兼容 Python2 和 Python3。 96 | -------------------------------------------------------------------------------- /book/FunctionalProgramming/Comprehensions.md: -------------------------------------------------------------------------------- 1 | --- 2 | parent: 函数式编程 3 | nav_order: 5 4 | --- 5 | 6 | 7 | # 推导式(解析式) 8 | 9 | 推导式(`comprehensions`)(在中文又称解析式)是 Python 的一种独有特性,如果我被迫离开了它,我会非常想念。推导式是可以从一个数据序列构建另一个新的数据序列的结构体。共有三种推导,在Python 2和3中都有支持: 10 | 11 | - 列表(```list```)推导式 12 | - 字典(```dict```)推导式 13 | - 集合(```set```)推导式 14 | 15 | 我们将一一进行讨论。一旦你知道了使用列表推导式的诀窍,你就能轻易使用任意一种推导式了。 16 | 17 | # 列表推导式(```list``` comprehensions) 18 | 19 | 列表推导式(又称列表解析式)提供了一种简明扼要的方法来创建列表。 20 | 它的结构是在一个中括号里包含一个表达式,然后是一个 ```for``` 语句,然后是0个或多个 ```for``` 或者 ```if``` 语句。那个表达式可以是任意的,意思是你可以在列表中放入任意类型的对象。返回结果将是一个新的列表,在这个以 ```if``` 和 ```for``` 语句为上下文的表达式运行完成之后产生。 21 | 22 | ## 规范 23 | 24 | ```python 25 | variable = [out_exp for out_exp in input_list if out_exp == 2] 26 | ``` 27 | 28 | 这里是另外一个简明例子: 29 | 30 | ```python 31 | multiples = [i for i in range(30) if i % 3 is 0] 32 | print(multiples) 33 | # Output: [0, 3, 6, 9, 12, 15, 18, 21, 24, 27] 34 | ``` 35 | 36 | 这将对快速生成列表非常有用。 37 | 有些人甚至更喜欢使用它而不是 ```filter``` 函数。 38 | 列表推导式在有些情况下超赞,特别是当你需要使用 ```for``` 循环来生成一个新列表。举个例子,你通常会这样做: 39 | 40 | ```python 41 | squared = [] 42 | for x in range(10): 43 | squared.append(x**2) 44 | ``` 45 | 46 | 你可以使用列表推导式来简化它,就像这样: 47 | 48 | ```python 49 | squared = [x**2 for x in range(10)] 50 | ``` 51 | 52 | 53 | # 字典推导式(```dict``` comprehensions) 54 | 55 | 字典推导和列表推导的使用方法是类似的。这里有个我最近发现的例子: 56 | 57 | ```python 58 | mcase = {'a': 10, 'b': 34, 'A': 7, 'Z': 3} 59 | 60 | mcase_frequency = { 61 | k.lower(): mcase.get(k.lower(), 0) + mcase.get(k.upper(), 0) 62 | for k in mcase.keys() 63 | } 64 | 65 | # mcase_frequency == {'a': 17, 'z': 3, 'b': 34} 66 | ``` 67 | 68 | 在上面的例子中我们把同一个字母但不同大小写的值合并起来了。 69 | 70 | 就我个人来说没有大量使用字典推导式。 71 | 72 | 你还可以快速对换一个字典的键和值: 73 | 74 | ```python 75 | {v: k for k, v in some_dict.items()} 76 | ``` 77 | 78 | # 集合推导式(```set``` comprehensions) 79 | 80 | 它们跟列表推导式也是类似的。 唯一的区别在于它们使用大括号```{}```。 举个例子: 81 | 82 | ```python 83 | squared = {x**2 for x in [1, 1, 2]} 84 | print(squared) 85 | # Output: {1, 4} 86 | ``` 87 | -------------------------------------------------------------------------------- /book/FunctionalProgramming/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 函数式编程 3 | nav_order: 3 4 | has_children: true 5 | --- 6 | # 函数式编程 -------------------------------------------------------------------------------- /book/FunctionalProgramming/enumerate.md: -------------------------------------------------------------------------------- 1 | --- 2 | parent: 函数式编程 3 | nav_order: 1 4 | --- 5 | 6 | 7 | # 枚举 8 | 9 | 枚举(```enumerate```)是 Python 内置函数。它的用处很难在简单的一行中说明,但是大多数的新人,甚至一些高级程序员都没有意识到它。 10 | 11 | 它允许我们遍历数据并自动计数, 12 | 13 | 下面是一个例子: 14 | 15 | ```python 16 | for counter, value in enumerate(some_list): 17 | print(counter, value) 18 | ``` 19 | 20 | 不只如此,```enumerate``` 也接受一些可选参数,这使它更有用。 21 | 22 | ```python 23 | my_list = ['apple', 'banana', 'grapes', 'pear'] 24 | for c, value in enumerate(my_list, 1): 25 | print(c, value) 26 | 27 | # 输出: 28 | (1, 'apple') 29 | (2, 'banana') 30 | (3, 'grapes') 31 | (4, 'pear') 32 | ``` 33 | 34 | 上面这个可选参数允许我们定制从哪个数字开始枚举。 35 | 你还可以用来创建包含索引的元组列表, 36 | 例如: 37 | 38 | ```python 39 | my_list = ['apple', 'banana', 'grapes', 'pear'] 40 | counter_list = list(enumerate(my_list, 1)) 41 | print(counter_list) 42 | # 输出: [(1, 'apple'), (2, 'banana'), (3, 'grapes'), (4, 'pear')] 43 | ``` 44 | -------------------------------------------------------------------------------- /book/FunctionalProgramming/lambdas.md: -------------------------------------------------------------------------------- 1 | --- 2 | parent: 函数式编程 3 | nav_order: 2 4 | --- 5 | 6 | 7 | # ```lambda``` 表达式 8 | 9 | `lambda` 表达式是一行函数。 10 | 它们在其他语言中也被称为匿名函数。如果你不想在程序中对一个函数使用两次,你也许会想用 lambda 表达式,它们和普通的函数完全一样。 11 | 12 | __原型__ 13 | 14 | ```python 15 | lambda 参数:操作(参数) 16 | ``` 17 | 18 | **例子** 19 | 20 | ```python 21 | add = lambda x, y: x + y 22 | 23 | print(add(3, 5)) 24 | # Output: 8 25 | ``` 26 | 27 | 这还有一些lambda表达式的应用案例,可以在一些特殊情况下使用: 28 | 29 | __列表排序__ 30 | 31 | ```python 32 | a = [(1, 2), (4, 1), (9, 10), (13, -3)] 33 | a.sort(key=lambda x: x[1]) 34 | 35 | print(a) 36 | # Output: [(13, -3), (4, 1), (1, 2), (9, 10)] 37 | ``` 38 | 39 | __列表并行排序__ 40 | 41 | ```python 42 | data = zip(list1, list2) 43 | data = sorted(data) # 此处已经完成了列表并行排序 44 | # 下面展示了反向zip, 即还原出被zip的2个list 45 | list1, list2 = map(lambda t: list(t), zip(*data)) 46 | ``` 47 | -------------------------------------------------------------------------------- /book/FunctionalProgramming/map_n_filter.md: -------------------------------------------------------------------------------- 1 | --- 2 | parent: 函数式编程 3 | nav_order: 4 4 | --- 5 | 6 | 7 | # Map,Filter 和 Reduce 8 | 9 | Map,Filter 和 Reduce 三个函数能为函数式编程提供便利。我们会通过实例一个一个讨论并理解它们。 10 | 11 | # ```Map``` 12 | 13 | `Map` 会将一个函数映射到一个输入列表的所有元素上。这是它的规范: 14 | 15 | **规范** 16 | 17 | ```python 18 | map(function_to_apply, list_of_inputs) 19 | ``` 20 | 21 | 大多数时候,我们要把列表中所有元素一个个地传递给一个函数,并收集输出。比方说: 22 | 23 | ```python 24 | items = [1, 2, 3, 4, 5] 25 | squared = [] 26 | for i in items: 27 | squared.append(i**2) 28 | ``` 29 | 30 | `Map` 可以让我们用一种简单而漂亮得多的方式来实现。就是这样: 31 | 32 | ```python 33 | items = [1, 2, 3, 4, 5] 34 | squared = list(map(lambda x: x**2, items)) 35 | ``` 36 | 37 | 大多数时候,我们使用匿名函数(lambdas)来配合 `map`, 所以我在上面也是这么做的。 38 | 不仅用于一列表的输入, 我们甚至可以用于一列表的函数! 39 | 40 | ```python 41 | def multiply(x): 42 | return (x*x) 43 | def add(x): 44 | return (x+x) 45 | 46 | funcs = [multiply, add] 47 | for i in range(5): 48 | value = map(lambda x: x(i), funcs) 49 | print(list(value)) 50 | # 译者注:上面print时,加了list转换,是为了python2/3的兼容性 51 | # 在python2中map直接返回列表,但在python3中返回迭代器 52 | # 因此为了兼容python3, 需要list转换一下 53 | 54 | # Output: 55 | # [0, 0] 56 | # [1, 2] 57 | # [4, 4] 58 | # [9, 6] 59 | # [16, 8] 60 | ``` 61 | 62 | # ```Filter``` 63 | 64 | 顾名思义,```filter``` 过滤列表中的元素,并且返回一个由所有符合要求的元素所构成的列表,```符合要求```即函数映射到该元素时返回值为True。这里是一个简短的例子: 65 | 66 | ```python 67 | number_list = range(-5, 5) 68 | less_than_zero = filter(lambda x: x < 0, number_list) 69 | print(list(less_than_zero)) 70 | # 译者注:上面print时,加了list转换,是为了python2/3的兼容性 71 | # 在python2中filter直接返回列表,但在python3中返回迭代器 72 | # 因此为了兼容python3, 需要list转换一下 73 | 74 | # Output: [-5, -4, -3, -2, -1] 75 | ``` 76 | 77 | 这个 ```filter``` 类似于一个 ```for``` 循环,但它是一个内置函数,并且更快。 78 | 79 | 注意:如果 ```map``` 和 ```filter``` 对你来说看起来并不优雅的话,那么你可以看看另外一章:列表/字典/元组推导式。 80 | 81 | > 译者注:大部分情况下推导式的可读性更好。 82 | 83 | # ```Reduce``` 84 | 85 | 当需要对一个列表进行一些计算并返回结果时,`Reduce` 是个非常有用的函数。举个例子,当你需要计算一个整数列表的乘积时。 86 | 87 | 通常在 python 中你可能会使用基本的 for 循环来完成这个任务。 88 | 89 | 现在我们来试试 reduce: 90 | 91 | ```python 92 | from functools import reduce 93 | product = reduce( (lambda x, y: x * y), [1, 2, 3, 4] ) 94 | 95 | # Output: 24 96 | ``` 97 | -------------------------------------------------------------------------------- /book/FunctionalProgramming/set_data_structure.md: -------------------------------------------------------------------------------- 1 | --- 2 | parent: 函数式编程 3 | nav_order: 3 4 | --- 5 | 6 | 7 | # ```set```(集合)数据结构 8 | 9 | ```set```(集合)是一个非常有用的数据结构。它与列表(```list```)的行为类似,区别在于 ```set``` 不能包含重复的值。 10 | 这在很多情况下非常有用。例如你可能想检查列表中是否包含重复的元素,你有两个选择,第一个需要使用 ```for``` 循环,就像这样: 11 | 12 | ```python 13 | some_list = ['a', 'b', 'c', 'b', 'd', 'm', 'n', 'n'] 14 | 15 | duplicates = [] 16 | for value in some_list: 17 | if some_list.count(value) > 1: 18 | if value not in duplicates: 19 | duplicates.append(value) 20 | 21 | print(duplicates) 22 | ### 输出: ['b', 'n'] 23 | ``` 24 | 25 | 但还有一种更简单更优雅的解决方案,那就是使用```集合(sets)```,你直接这样做: 26 | 27 | ```python 28 | some_list = ['a', 'b', 'c', 'b', 'd', 'm', 'n', 'n'] 29 | duplicates = set([x for x in some_list if some_list.count(x) > 1]) 30 | print(duplicates) 31 | ### 输出: set(['b', 'n']) 32 | ``` 33 | 34 | 集合还有一些其它方法,下面我们介绍其中一部分。 35 | 36 | ## 交集(intersection) 37 | 38 | 你可以对比两个集合的交集(两个集合中都有的数据),如下: 39 | 40 | ```python 41 | valid = set(['yellow', 'red', 'blue', 'green', 'black']) 42 | input_set = set(['red', 'brown']) 43 | print(input_set.intersection(valid)) 44 | ### 输出: set(['red']) 45 | ``` 46 | 47 | ## 差集(difference) 48 | 49 | 你可以用差集找出无效的数据,相当于用一个集合减去另一个集合的数据,例如: 50 | 51 | ```python 52 | valid = set(['yellow', 'red', 'blue', 'green', 'black']) 53 | input_set = set(['red', 'brown']) 54 | print(input_set.difference(valid)) 55 | ### 输出: set(['brown']) 56 | ``` 57 | 58 | 你也可以用```{}```符号来创建集合,如: 59 | 60 | ```python 61 | a_set = {'red', 'blue', 'green'} 62 | print(type(a_set)) 63 | ### 输出: 64 | ``` 65 | 66 | 集合还有一些其它方法,我会建议访问官方文档并做个快速阅读。 67 | -------------------------------------------------------------------------------- /book/Intro-2021.md: -------------------------------------------------------------------------------- 1 | --- 2 | parent: 主页 3 | nav_order: 1 4 | --- 5 | 6 | # 2021更新版-序 7 | 8 | 自2016年《Python进阶》中文版[第一次发布](http://www.v2ex.com/t/267557)以来,已经5年了,本书的github repo收到了5800+赞,非常感谢社区对我们的认可,也说明了用心的做精品,好作品自己会说话。 9 | 10 | 这几年译者团队忙于各奔前程,有一两年没怎么更新了,最近看到github repo每天的PV还维持在平均150左右,还是说明能帮助到一些人的(还不算其他国内镜像托管版本的阅读量,如极客学院,PythonTab等)。 11 | 12 | 所以,老苏我决定把此书再做了一次大的修订,希望能作为一部Python参考的经典,随时能帮到国内的Python程序员们,主要更新如下: 13 | 1. 同步[原著的全新目录结构](https://github.com/yasoob/intermediatePython#table-of-contents),对比之前零散的章节,现在基于分类的目录,会更便于查找。 14 | 2. 基于v2ex网友(@nightv2)提出的对阅读体验的建议,把每个章节里的小节合并在一个文件里,然后使用文件内的导航进行小节跳转。 15 | 3. 针对英文原著的新内容和修复,做了同步。 16 | 4. 放弃使用GitBook服务,因为2020年GitBook已经改变了商业模式,不再支持独立托管,很多国内镜像已经长久得不到更新同步,不利于开源社区的分享传播。 17 | 5. 改用GithubPages服务,可以直接使用Jekyll生成静态网站,方便大家的本地部署或者托管。目前官方版本部署在,会实时自动更新。 18 | 6. 使用了全新的主题,目录导航和顶栏搜索带来非常流畅丝滑的阅读体验。 19 | 7. 后续考虑增加国内Python社区网友建议的实用的主题,把《Python进阶》做成实战性很强的适应国内互联网发展的宝典。 20 | 21 | 再次感谢各位给《Python进阶》提过issue和PR的贡献者们,谢谢你们对国内Python程序员们的帮助和贡献! 22 | 23 | 24 | > 恰饭时刻~ 😘 25 | > 本书凝聚了作者不少心血来打造一个精品,如果本书对你有所帮助,可以[请我喝杯咖啡☕️](https://dun.mianbaoduo.com/@eastlakeside): ,谢谢你的认可,这将是我持续创作的动力。 26 | -------------------------------------------------------------------------------- /book/Introduction.md: -------------------------------------------------------------------------------- 1 | --- 2 | parent: 主页 3 | nav_order: 1.1 4 | --- 5 | 6 | # 2016首版-序 7 | 8 | 这是一本 [Intermediate Python](https://github.com/yasoob/intermediatePython) 的中文译本,谨以此献给进击的 Python 和 Python 程序员们! 9 | 10 | 这是一次团队建设、一次尝鲜、一次对自我的提升。相信每个有为青年,心里想藏着一个小宇宙:**我想要做一件有意思的事**。_$_ 什么是有意思的事?_$_ **别闹** 11 | 12 | Python,作为一个"老练"、"小清新"的开发语言,已受到广大才男俊女的喜爱。我们也从最基础的 Python 粉,经过时间的摧残慢慢的变成了 Python 老鬼。因此一开始 @大牙 提出要翻译点什么的时候,我还是挺兴奋的,团队一起协作,不单可以磨练自己,也能加强团队之间的协作。为此在经过短暂的讨论后,翻译的内容就定为:《Intermediate Python》。 13 | 14 | Intermediate Python 这本书具有如下几个优点: 15 | 16 | 1. 简单 17 | 2. 易读 18 | 3. 易译 19 | 20 | 这些都不是重点,重点是:**它是一本开脑洞的书**。无论你是Python初学者,还是Python高手,它显现给你的永远是Python里最美好的事物。 21 | 22 | > 23 | 世上语言千万种 24 | 美好事物藏其中 25 | 26 | 翻译的过程很顺利,语言很易懂,因此各位读者欢迎捐赠,或加入微信群讨论。 27 | -------------------------------------------------------------------------------- /book/ProgrammerTools/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 程序员的工具 3 | nav_order: 1 4 | has_children: true 5 | --- 6 | 7 | # 程序员的工具 8 | -------------------------------------------------------------------------------- /book/ProgrammerTools/debugging.md: -------------------------------------------------------------------------------- 1 | --- 2 | parent: 程序员的工具 3 | nav_order: 2 4 | --- 5 | 6 | # 调试(Debugging) 7 | 8 | 利用好调试,能大大提高你捕捉代码Bug的能力。大部分新人忽略了 Python debugger(```pdb```) 的重要性。在这个章节我只会告诉你一些重要的命令,你可以从官方文档中学习到更多。 9 | 10 | > 译者注,参考:https://docs.python.org/2/library/pdb.html 11 | 或者 https://docs.python.org/3/library/pdb.html 12 | 13 | ### 从命令行运行 14 | 15 | 你可以在命令行使用Python debugger运行一个脚本,举个例子: 16 | 17 | ```bash 18 | $ python -m pdb my_script.py 19 | ``` 20 | 21 | 这会触发 debugger 在脚本第一行指令处停止执行。这在脚本很短时会很有帮助。你可以通过(Pdb)模式接着查看变量信息,并且逐行调试。 22 | 23 | ### 从脚本内部运行 24 | 25 | 同时,你也可以在脚本内部设置断点,这样就可以在某些特定点查看变量信息和各种执行时信息了。这里将使用 ```pdb.set_trace()``` 方法来实现。举个例子: 26 | 27 | ```python 28 | import pdb 29 | 30 | def make_bread(): 31 | pdb.set_trace() 32 | return "I don't have time" 33 | 34 | print(make_bread()) 35 | ``` 36 | 37 | 试下保存上面的脚本后运行之。你会在运行时马上进入 debugger 模式。现在是时候了解下 debugger 模式下的一些命令了。 38 | 39 | ##### 命令列表: 40 | 41 | - ```c```: 继续执行 42 | - ```w```: 显示当前正在执行的代码行的上下文信息 43 | - ```a```: 打印当前函数的参数列表 44 | - ```s```: 执行当前代码行,并停在第一个能停的地方(相当于单步进入) 45 | - ```n```: 继续执行到当前函数的下一行,或者当前行直接返回(单步跳过) 46 | 47 | 单步跳过(```n```ext)和单步进入(```s```tep)的区别在于,单步进入会进入当前行调用的函数内部并停在里面,而单步跳过会(几乎)全速执行完当前行调用的函数,并停在当前函数的下一行。 48 | 49 | pdb真的是一个很方便的功能,上面仅列举少量用法,更多的命令强烈推荐你去看官方文档。 50 | -------------------------------------------------------------------------------- /book/ProgrammerTools/introspection.md: -------------------------------------------------------------------------------- 1 | --- 2 | parent: 程序员的工具 3 | nav_order: 3 4 | --- 5 | 6 | # 对象自省 7 | 8 | 自省(introspection),在计算机编程领域里,是指在运行时来判断一个对象的类型的能力。它是 Python 的强项之一。Python 中所有一切都是一个对象,而且我们可以仔细勘察那些对象。Python 还包含了许多内置函数和模块来帮助我们。 9 | 10 | # ```dir``` 11 | 12 | 在这个小节里我们会学习到 ```dir``` 以及它在自省方面如何给我们提供便利。 13 | 14 | 它是用于自省的最重要的函数之一。它返回一个列表,列出了一个对象所拥有的属性和方法。这里是一个例子: 15 | 16 | ```python 17 | my_list = [1, 2, 3] 18 | dir(my_list) 19 | # Output: ['__add__', '__class__', '__contains__', '__delattr__', '__delitem__', 20 | # '__delslice__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', 21 | # '__getitem__', '__getslice__', '__gt__', '__hash__', '__iadd__', '__imul__', 22 | # '__init__', '__iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__', 23 | # '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__reversed__', '__rmul__', 24 | # '__setattr__', '__setitem__', '__setslice__', '__sizeof__', '__str__', 25 | # '__subclasshook__', 'append', 'count', 'extend', 'index', 'insert', 'pop', 26 | # 'remove', 'reverse', 'sort'] 27 | ``` 28 | 29 | 上面的自省给了我们一个列表对象的所有方法的名字。当你没法回忆起一个方法的名字,这会非常有帮助。如果我们运行 ```dir()``` 而不传入参数,那么它会返回当前作用域的所有名字。 30 | 31 | 32 | # ```type``` 和 ```id``` 33 | 34 | `type` 函数返回一个对象的类型。举个例子: 35 | 36 | ```python 37 | print(type('')) 38 | # Output: 39 | 40 | print(type([])) 41 | # Output: 42 | 43 | print(type({})) 44 | # Output: 45 | 46 | print(type(dict)) 47 | # Output: 48 | 49 | print(type(3)) 50 | # Output: 51 | ``` 52 | 53 | `id()` 函数返回任意不同种类对象的唯一ID,举个例子: 54 | 55 | ```python 56 | name = "Yasoob" 57 | print(id(name)) 58 | # Output: 139972439030304 59 | ``` 60 | 61 | 62 | # ```inspect``` 模块 63 | 64 | `inspect` 模块也提供了许多有用的函数,来获取活跃对象的信息。比方说,你可以查看一个对象的成员,只需运行: 65 | 66 | ```python 67 | import inspect 68 | print(inspect.getmembers(str)) 69 | # Output: [('__add__', >> greet_me(name="yasoob") 54 | name == yasoob 55 | ``` 56 | 57 | 现在你可以看出我们怎样在一个函数里,处理了一个**键值对**参数了。 58 | 59 | 这就是 ```**kwargs``` 的基础,而且你可以看出它有多么管用。接下来让我们谈谈,你怎样使用 ```*args``` 和 ```**kwargs``` 来调用一个参数为列表或者字典的函数。 60 | 61 | 62 | # 使用 ```*args``` 和 ```**kwargs``` 来调用函数 63 | 64 | 那现在我们将看到怎样使用 ```*args``` 和 ```**kwargs``` 来调用一个函数。假设,你有这样一个小函数: 65 | 66 | ```python 67 | def test_args_kwargs(arg1, arg2, arg3): 68 | print("arg1:", arg1) 69 | print("arg2:", arg2) 70 | print("arg3:", arg3) 71 | ``` 72 | 73 | 你可以使用```*args```或```**kwargs```来给这个小函数传递参数。下面是怎样做: 74 | 75 | ```python 76 | # 首先使用 *args 77 | >>> args = ("two", 3, 5) 78 | >>> test_args_kwargs(*args) 79 | arg1: two 80 | arg2: 3 81 | arg3: 5 82 | 83 | # 现在使用 **kwargs: 84 | >>> kwargs = {"arg3": 3, "arg2": "two", "arg1": 5} 85 | >>> test_args_kwargs(**kwargs) 86 | arg1: 5 87 | arg2: two 88 | arg3: 3 89 | ``` 90 | 91 | ### 标准参数与 ```*args```、```**kwargs``` 在使用时的顺序 92 | 93 | 那么如果你想在函数里同时使用所有这三种参数, 顺序是这样的: 94 | 95 | ```python 96 | some_func(fargs, *args, **kwargs) 97 | ``` 98 | 99 | 100 | # 什么时候使用它们? 101 | 102 | 这还真的要看你的需求而定。 103 | 104 | 最常见的用例是在写函数装饰器的时候(会在另一章里讨论)。 105 | 106 | 此外它也可以用来做猴子补丁(monkey patching)。猴子补丁的意思是在程序运行时(runtime)修改某些代码。打个比方,你有一个类,里面有个叫 ```get_info``` 的函数会调用一个API并返回相应的数据。如果我们想测试它,可以把API调用替换成一些测试数据。例如: 107 | 108 | ```python 109 | import someclass 110 | 111 | def get_info(self, *args): 112 | return "Test data" 113 | 114 | someclass.get_info = get_info 115 | ``` 116 | 117 | 我敢肯定你也可以想象到一些其他的用例。 118 | -------------------------------------------------------------------------------- /book/Syntax/context_managers.md: -------------------------------------------------------------------------------- 1 | --- 2 | parent: 语法 3 | nav_order: 7 4 | --- 5 | 6 | # 上下文管理器 7 | 8 | 上下文管理器(Context managers)允许你在有需要的时候,精确地分配和释放资源。 9 | 10 | 使用上下文管理器最广泛的案例就是 ```with``` 语句了。 11 | 想象下你有两个需要结对执行的相关操作,然后还要在它们中间放置一段代码。 12 | 上下文管理器就是专门让你做这种事情的。举个例子: 13 | 14 | ```python 15 | with open('some_file', 'w') as opened_file: 16 | opened_file.write('Hola!') 17 | ``` 18 | 19 | 上面这段代码打开了一个文件,往里面写入了一些数据,然后关闭该文件。如果在往文件写数据时发生异常,它也会尝试去关闭文件。上面那段代码与这一段是等价的: 20 | 21 | ```python 22 | file = open('some_file', 'w') 23 | try: 24 | file.write('Hola!') 25 | finally: 26 | file.close() 27 | ``` 28 | 29 | 当与第一个例子对比时,我们可以看到,通过使用 ```with```,许多样板代码(boilerplate code)被消掉了。 这就是 ```with``` 语句的主要优势,它确保我们的文件会被关闭,而不用关注嵌套代码如何退出。 30 | 31 | 上下文管理器的一个常见用例,是资源的加锁和解锁,以及关闭已打开的文件(就像我已经展示给你看的)。 32 | 33 | 让我们看看如何来实现我们自己的上下文管理器。这会让我们更完全地理解在这些场景背后都发生着什么。 34 | 35 | 36 | # 基于类的实现 37 | 38 | 一个上下文管理器的类,最起码要定义 ```__enter__``` 和 ```__exit__``` 方法。 39 | 让我们来构造我们自己的开启文件的上下文管理器,并学习下基础知识。 40 | 41 | ```python 42 | class File(object): 43 | def __init__(self, file_name, method): 44 | self.file_obj = open(file_name, method) 45 | def __enter__(self): 46 | return self.file_obj 47 | def __exit__(self, type, value, traceback): 48 | self.file_obj.close() 49 | ``` 50 | 51 | 通过定义 ```__enter__``` 和 ```__exit__``` 方法,我们可以在```with```语句里使用它。我们来试试: 52 | 53 | ```python 54 | with File('demo.txt', 'w') as opened_file: 55 | opened_file.write('Hola!') 56 | ``` 57 | 58 | 我们的 ```__exit__``` 函数接受三个参数。这些参数对于每个上下文管理器类中的 ```__exit__``` 方法都是必须的。我们来谈谈在底层都发生了什么。 59 | 60 | 1. ```with``` 语句先暂存了 ```File``` 类的 ```__exit__``` 方法。 61 | 2. 然后它调用 ```File``` 类的 ```__enter__``` 方法。 62 | 3. ```__enter__``` 方法打开文件并返回给 ```with``` 语句。 63 | 4. 打开的文件句柄被传递给 ```opened_file``` 参数。 64 | 5. 我们使用 ```.write()``` 来写文件。 65 | 6. ```with``` 语句调用之前暂存的 ```__exit__``` 方法。 66 | 7. ```__exit__``` 方法关闭了文件。 67 | 68 | 69 | # 处理异常 70 | 71 | 我们还没有谈到 ```__exit__``` 方法的这三个参数:```type```,```value``` 和 ```traceback```。 72 | 在第4步和第6步之间,如果发生异常,Python 会将异常的 ```type```,```value``` 和 ```traceback``` 传递给 ```__exit__``` 方法。 73 | 它让 ```__exit__``` 方法来决定如何关闭文件以及是否需要其他步骤。在我们的案例中,我们并没有注意它们。 74 | 75 | 那如果我们的文件对象抛出一个异常呢?万一我们尝试访问文件对象的一个不支持的方法。举个例子: 76 | 77 | ```python 78 | with File('demo.txt', 'w') as opened_file: 79 | opened_file.undefined_function('Hola!') 80 | ``` 81 | 82 | 我们来列一下,当异常发生时,```with``` 语句会采取哪些步骤。 83 | 84 | 1. 它把异常的 ```type```,```value``` 和 ```traceback``` 传递给 ```__exit__```方法。 85 | 2. 它让 ```__exit__``` 方法来处理异常。 86 | 3. 如果 ```__exit__``` 返回的是 True,那么这个异常就被优雅地处理了。 87 | 4. 如果 ```__exit__``` 返回的是 True 以外的任何东西,那么这个异常将被 ```with``` 语句抛出。 88 | 89 | 在我们的案例中,```__exit__``` 方法返回的是 ```None``` (如果没有 ```return``` 语句那么方法会返回 ```None```)。因此,```with``` 语句抛出了那个异常。 90 | 91 | ```python 92 | Traceback (most recent call last): 93 | File "", line 2, in 94 | AttributeError: 'file' object has no attribute 'undefined_function' 95 | ``` 96 | 97 | 我们尝试下在 ```__exit__``` 方法中处理异常: 98 | 99 | ```python 100 | class File(object): 101 | def __init__(self, file_name, method): 102 | self.file_obj = open(file_name, method) 103 | def __enter__(self): 104 | return self.file_obj 105 | def __exit__(self, type, value, traceback): 106 | print("Exception has been handled") 107 | self.file_obj.close() 108 | return True 109 | 110 | with File('demo.txt', 'w') as opened_file: 111 | opened_file.undefined_function() 112 | 113 | # Output: Exception has been handled 114 | ``` 115 | 116 | 我们的 ```__exit__``` 方法返回了 ```True```,因此没有异常会被 ```with``` 语句抛出。 117 | 118 | 这还不是实现上下文管理器的唯一方式。还有一种方式,我们会在下一节中一起看看。 119 | 120 | # 基于生成器的实现 121 | 122 | 我们还可以用装饰器(decorators)和生成器(generators)来实现上下文管理器。 123 | Python 有个 ```contextlib``` 模块专门用于这个目的。我们可以使用一个生成器函数来实现一个上下文管理器,而不是使用一个类。 124 | 让我们看看一个基本的,没用的例子: 125 | 126 | ```python 127 | from contextlib import contextmanager 128 | 129 | @contextmanager 130 | def open_file(name): 131 | f = open(name, 'w') 132 | yield f 133 | f.close() 134 | ``` 135 | 136 | OK啦!这个实现方式看起来更加直观和简单。然而,这个方法需要关于生成器、```yield``` 和装饰器的一些知识。在这个例子中我们还没有捕捉可能产生的任何异常。它的工作方式和之前的方法大致相同。 137 | 138 | 让我们小小地剖析下这个方法。 139 | 140 | 1. Python 解释器遇到了 ```yield``` 关键字。因为这个缘故它创建了一个生成器而不是一个普通的函数。 141 | 2. 因为这个装饰器,```contextmanager``` 会被调用并传入函数名(```open_file```)作为参数。 142 | 3. ```contextmanager``` 函数返回一个以 ```GeneratorContextManager``` 对象封装过的生成器。 143 | 4. 这个 ```GeneratorContextManager``` 被赋值给 ```open_file``` 函数,我们实际上是在调用 ```GeneratorContextManager``` 对象。 144 | 145 | 那现在我们既然知道了所有这些,我们可以用这个新生成的上下文管理器了,像这样: 146 | 147 | ```python 148 | with open_file('some_file') as f: 149 | f.write('hola!') 150 | ``` 151 | -------------------------------------------------------------------------------- /book/Syntax/exception.md: -------------------------------------------------------------------------------- 1 | --- 2 | parent: 语法 3 | nav_order: 1 4 | --- 5 | 6 | # 异常 7 | 8 | 异常处理是一种艺术,一旦你掌握,会授予你无穷的力量。我将要向你展示我们能处理异常的一些方式。 9 | 10 | 最基本的术语里我们知道了 ```try/except``` 从句。可能触发异常产生的代码会放到 ```try``` 语句块里,而处理异常的代码会在 ```except``` 语句块里实现。这是一个简单的例子: 11 | 12 | ```python 13 | try: 14 | file = open('test.txt', 'rb') 15 | except IOError as e: 16 | print('An IOError occurred. {}'.format(e.args[-1])) 17 | ``` 18 | 19 | 上面的例子里,我们仅仅在处理一个 ```IOError``` 的异常。大部分初学者还不知道的是,我们可以处理多个异常。 20 | 21 | 22 | # 处理多个异常 23 | 24 | 我们可以使用三种方法来处理多个异常。 25 | 26 | 第一种方法需要把所有可能发生的异常放到一个元组里。像这样: 27 | 28 | ```python 29 | try: 30 | file = open('test.txt', 'rb') 31 | except (IOError, EOFError) as e: 32 | print("An error occurred. {}".format(e.args[-1])) 33 | ``` 34 | 35 | 另外一种方式是对每个单独的异常在单独的 ```except``` 语句块中处理。我们想要多少个 ```except``` 语句块都可以。这里是个例子: 36 | 37 | ```python 38 | try: 39 | file = open('test.txt', 'rb') 40 | except EOFError as e: 41 | print("An EOF error occurred.") 42 | raise e 43 | except IOError as e: 44 | print("An error occurred.") 45 | raise e 46 | ``` 47 | 48 | 上面这个方式中,如果异常没有被第一个 ```except``` 语句块处理,那么它也许被下一个语句块处理,或者根本不会被处理。 49 | 50 | 现在,最后一种方式会捕获**所有**异常: 51 | 52 | ```python 53 | try: 54 | file = open('test.txt', 'rb') 55 | except Exception: 56 | # 打印一些异常日志,如果你想要的话 57 | raise 58 | ``` 59 | 60 | 当你不知道你的程序会抛出什么样的异常时,上面的方式可能非常有帮助。 61 | 62 | 63 | # ```finally``` 从句 64 | 65 | 我们把我们的主程序代码包裹进了 ```try``` 从句。然后我们把一些代码包裹进一个 ```except``` 从句,它会在 ```try``` 从句中的代码触发异常时执行。 66 | 67 | 在下面的例子中,我们还会使用第三个从句,那就是 ```finally``` 从句。包裹到 ```finally``` 从句中的代码不管异常是否触发都将会被执行。这可以被用来在脚本执行之后做清理工作。这里是个简单的例子: 68 | 69 | ```python 70 | try: 71 | file = open('test.txt', 'rb') 72 | except IOError as e: 73 | print('An IOError occurred. {}'.format(e.args[-1])) 74 | finally: 75 | print("This would be printed whether or not an exception occurred!") 76 | 77 | # Output: An IOError occurred. No such file or directory 78 | # This would be printed whether or not an exception occurred! 79 | ``` 80 | 81 | # ```try/else``` 从句 82 | 83 | 我们常常想在没有触发异常的时候执行一些代码。这可以很轻松地通过一个 ```else``` 从句来达到。 84 | 85 | 有人也许问了:如果你只是想让一些代码在没有触发异常的情况下执行,为啥你不直接把代码放在 ```try``` 里面呢? 86 | 回答是,那样的话这段代码中的任意异常都还是会被 ```try``` 捕获,而你并不一定想要那样。 87 | 88 | 大多数人并不使用 ```else``` 从句,而且坦率地讲我自己也没有大范围使用。这里是个例子: 89 | 90 | ```python 91 | try: 92 | print('I am sure no exception is going to occur!') 93 | except Exception: 94 | print('exception') 95 | else: 96 | # 这里的代码只会在try语句里没有触发异常时运行, 97 | # 但是这里的异常将 *不会* 被捕获 98 | print('This would only run if no exception occurs. And an error here ' 99 | 'would NOT be caught.') 100 | finally: 101 | print('This would be printed in every case.') 102 | 103 | # Output: I am sure no exception is going to occur! 104 | # This would only run if no exception occurs. 105 | # This would be printed in every case. 106 | ``` 107 | 108 | ```else``` 从句只会在没有异常的情况下执行,而且它会在 ```finally``` 语句之前执行。 109 | -------------------------------------------------------------------------------- /book/Syntax/for_else.md: -------------------------------------------------------------------------------- 1 | --- 2 | parent: 语法 3 | nav_order: 2 4 | --- 5 | 6 | # For - Else 7 | 8 | 循环是任何语言的一个必备要素。同样地,```for``` 循环就是 Python 的一个重要组成部分。然而还有一些东西是初学者并不知道的。我们将一个个讨论一下。 9 | 10 | 我们先从已经知道的开始。我们知道可以像这样使用 ```for``` 循环: 11 | 12 | ```python 13 | fruits = ['apple', 'banana', 'mango'] 14 | for fruit in fruits: 15 | print(fruit.capitalize()) 16 | 17 | # Output: Apple 18 | # Banana 19 | # Mango 20 | ``` 21 | 22 | 这是一个 ```for``` 循环非常基础的结构。现在我们继续看看,Python 的 ```for``` 循环的一些鲜为人所知的特性。 23 | 24 | 25 | # ```else``` 从句 26 | 27 | ```for``` 循环还有一个 ```else``` 从句,我们大多数人并不熟悉。这个 ```else``` 从句会在循环正常结束时执行。这意味着,循环没有遇到任何 ```break```。一旦你掌握了何时何地使用它,它真的会非常有用。我自己对它真是相见恨晚。 28 | 29 | 有个常见的构造是跑一个循环,并查找一个元素。如果这个元素被找到了,我们使用 ```break``` 来中断这个循环。有两个场景会让循环停下来。 30 | 31 | - 第一个是当一个元素被找到,```break``` 被触发。 32 | - 第二个场景是循环结束。 33 | 34 | 现在我们也许想知道其中哪一个,才是导致循环完成的原因。一个方法是先设置一个标记,然后在循环结束时打上标记。另一个是使用 ```else``` 从句。 35 | 36 | 这就是 ```for/else``` 循环的基本结构: 37 | 38 | ```python 39 | for item in container: 40 | if search_something(item): 41 | # Found it! 42 | process(item) 43 | break 44 | else: 45 | # Didn't find anything.. 46 | not_found_in_container() 47 | ``` 48 | 49 | 考虑下这个简单的案例,它是我从官方文档里拿来的: 50 | 51 | ```python 52 | for n in range(2, 10): 53 | for x in range(2, n): 54 | if n % x == 0: 55 | print(n, 'equals', x, '*', n / x) 56 | break 57 | ``` 58 | 59 | 它会找出2到10之间的数字的因子。现在是趣味环节了。我们可以加上一个附加的else语句块,来抓住质数,并且告诉我们: 60 | 61 | ```python 62 | for n in range(2, 10): 63 | for x in range(2, n): 64 | if n % x == 0: 65 | print(n, 'equals', x, '*', n / x) 66 | break 67 | else: 68 | # loop fell through without finding a factor 69 | print(n, 'is a prime number') 70 | ``` 71 | -------------------------------------------------------------------------------- /book/Syntax/global_return.md: -------------------------------------------------------------------------------- 1 | --- 2 | parent: 语法 3 | nav_order: 4 4 | --- 5 | 6 | # Global 和 Return 7 | 8 | 你也许遇到过, python中一些函数在最尾部有一个 ```return``` 关键字。你知道它是干嘛吗?它和其他语言的 ```return``` 类似。我们来检查下这个小函数: 9 | 10 | ```python 11 | def add(value1, value2): 12 | return value1 + value2 13 | 14 | result = add(3, 5) 15 | print(result) 16 | # Output: 8 17 | ``` 18 | 19 | 上面这个函数将两个值作为输入,然后输出它们相加之和。我们也可以这样做: 20 | 21 | ```python 22 | def add(value1,value2): 23 | global result 24 | result = value1 + value2 25 | 26 | add(3,5) 27 | print(result) 28 | # Output: 8 29 | ``` 30 | 31 | 那首先我们来谈谈第一段也就是包含 ```return``` 关键字的代码。那个函数把值赋给了调用它的变量(也就是例子中的result变量)。 32 | 大多数境况下,你并不需要使用 ```global``` 关键字。然而我们也来检查下另外一段也就是包含 ```global``` 关键字的代码。 33 | 那个函数生成了一个 ```global```(全局)变量 ```result```。 34 | 35 | ```global``` 在这的意思是什么?```global``` 变量意味着我们可以在函数以外的区域都能访问这个变量。让我们通过一个例子来证明它: 36 | 37 | ```python 38 | # 首先,是没有使用global变量 39 | def add(value1, value2): 40 | result = value1 + value2 41 | 42 | add(2, 4) 43 | print(result) 44 | 45 | # Oh 糟了,我们遇到异常了。为什么会这样? 46 | # python解释器报错说没有一个叫result的变量。 47 | # 这是因为result变量只能在创建它的函数内部才允许访问,除非它是全局的(global)。 48 | Traceback (most recent call last): 49 | File "", line 1, in 50 | result 51 | NameError: name 'result' is not defined 52 | 53 | # 现在我们运行相同的代码,不过是在将result变量设为global之后 54 | def add(value1, value2): 55 | global result 56 | result = value1 + value2 57 | 58 | add(2, 4) 59 | print(result) 60 | 6 61 | ``` 62 | 63 | 如我们所愿,在第二次运行时没有异常了。在实际的编程时,你应该试着避开 ```global``` 关键字,它只会让生活变得艰难,因为它引入了多余的变量到全局作用域了。 64 | 65 | # 多个 return 值 66 | 67 | 那如果你想从一个函数里返回两个变量而不是一个呢? 68 | 新手们有若干种方法。最著名的方法,是使用 ```global``` 关键字。让我们看下这个没用的例子: 69 | 70 | ```python 71 | def profile(): 72 | global name 73 | global age 74 | name = "Danny" 75 | age = 30 76 | 77 | profile() 78 | print(name) 79 | # Output: Danny 80 | 81 | print(age) 82 | # Output: 30 83 | ``` 84 | 85 | **注意:** 不要试着使用上述方法。重要的事情说三遍,不要试着使用上述方法!不要试着使用上述方法! 86 | 87 | 有些人试着在函数结束时,返回一个包含多个值的 ```tuple```(元组),```list```(列表)或者 ```dict```(字典),来解决这个问题。这是一种可行的方式,而且使用起来像一个黑魔法: 88 | 89 | ```python 90 | def profile(): 91 | name = "Danny" 92 | age = 30 93 | return (name, age) 94 | 95 | profile_data = profile() 96 | print(profile_data[0]) 97 | # Output: Danny 98 | 99 | print(profile_data[1]) 100 | # Output: 30 101 | ``` 102 | 103 | 或者按照更常见的惯例: 104 | 105 | ```python 106 | def profile(): 107 | name = "Danny" 108 | age = 30 109 | return name, age 110 | ``` 111 | 112 | 这是一种比列表和字典更好的方式。不要使用 ```global``` 关键字,除非你知道你正在做什么。```global``` 也许在某些场景下是一个更好的选择(但其中大多数情况都不是)。 113 | -------------------------------------------------------------------------------- /book/Syntax/open_func.md: -------------------------------------------------------------------------------- 1 | --- 2 | parent: 语法 3 | nav_order: 5 4 | --- 5 | 6 | # ```open``` 函数 7 | 8 | [open](http://docs.python.org/dev/library/functions.html#open) 函数可以打开一个文件。超级简单吧?大多数时候,我们看到它这样被使用: 9 | 10 | ```python 11 | f = open('photo.jpg', 'r+') 12 | jpgdata = f.read() 13 | f.close() 14 | ``` 15 | 16 | 我现在写这篇文章的原因,是大部分时间我看到 ```open``` 被这样使用。有**三个**错误存在于上面的代码中。你能把它们全指出来吗?如不能,请读下去。在这篇文章的结尾,你会知道上面的代码错在哪里,而且,更重要的是,你能在自己的代码里避免这些错误。现在我们从基础开始: 17 | 18 | `open` 的返回值是一个文件句柄,从操作系统托付给你的 Python 程序。一旦你处理完文件,你会想要归还这个文件句柄,只有这样你的程序不会超出一次能打开的文件句柄的数量上限。 19 | 20 | 显式地调用 ```close``` 关闭了这个文件句柄,但前提是只有在 read 成功的情况下。如果有任意异常正好在 ```f = open(...)``` 之后产生,```f.close()``` 将不会被调用(取决于 Python 解释器的做法,文件句柄可能还是会被归还,但那是另外的话题了)。为了确保不管异常是否触发,文件都能关闭,我们将其包裹成一个 ```with``` 语句: 21 | 22 | ```python 23 | with open('photo.jpg', 'r+') as f: 24 | jpgdata = f.read() 25 | ``` 26 | 27 | ```open``` 的第一个参数是文件名。第二个(```mode``` 打开模式)决定了这个文件如何被打开。 28 | 29 | - 如果你想读取文件,传入 ```r```; 30 | - 如果你想读取并写入文件,传入 ```r+```; 31 | - 如果你想覆盖写入文件,传入 ```w```; 32 | - 如果你想在文件末尾附加内容,传入 ```a```; 33 | 34 | 虽然有若干个其他的有效的 ```mode``` 字符串,但有可能你将永远不会使用它们。```mode``` 很重要,不仅因为它改变了行为,而且它可能导致权限错误。举个例子,我们要是在一个写保护的目录里打开一个 jpg 文件,```open(.., 'r+')``` 就失败了。```mode``` 可能包含一个扩展字符;让我们还可以以二进制方式打开文件(你将得到字节串)或者文本模式(字符串) 35 | 36 | 一般来说,如果文件格式是由人写的,那么它更可能是文本模式。jpg 图像文件一般不是人写的(而且其实不是人直接可读的),因此你应该以二进制模式来打开它们,方法是在```mode``` 字符串后加一个 ```b```(你可以看看开头的例子里,正确的方式应该是```rb```)。 37 | 如果你以文本模式打开一些东西(比如,加一个 ```t```,或者就用 ```r/r+/w/a```),你还必须知道要使用哪种编码。对于计算机来说,所有的文件都是字节,而不是字符。 38 | 39 | 可惜,在 Pyhon 2.x 版本里,```open``` 不支持显示地指定编码。然而,[io.open](http://docs.python.org/2/library/io.html#io.open) 函数在 Python 2.x 中和 3.x(其中它是 ```open``` 的别名)中都有提供,它能做正确的事。你可以传入 ```encoding``` 这个关键字参数来传入编码。 40 | 如果你不传入任意编码,一个系统 ── 以及 Python ── 指定的默认选项将被选中。你也许被诱惑去依赖这个默认选项,但这个默认选项经常是错误的,或者默认编码实际上不能表达文件里的所有字符(这将经常发生在 Python 2.x 和/或 Windows)。 41 | 所以去挑选一个编码吧。```utf-8``` 是一个非常好的编码。当你写入一个文件,你可以选一个你喜欢的编码(或者最终读你文件的程序所喜欢的编码)。 42 | 43 | 那你怎么找出正在读的文件是用哪种编码写的呢?好吧,不幸的是,并没有一个十分简单的方式来检测编码。在不同的编码中,同样的字节可以表示不同,但同样有效的字符。因此,你必须依赖一个元数据(比如,在 HTTP 头信息里)来找出编码。越来越多的是,文件格式将编码定义成```UTF-8```。 44 | 45 | 有了这些基础知识,我们来写一个程序,读取一个文件,检测它是否是 JPG(提示:这些文件头部以字节 ```FF D8``` 开始),把对输入文件的描述写入一个文本文件。 46 | 47 | ```python 48 | import io 49 | 50 | with open('photo.jpg', 'rb') as inf: 51 | jpgdata = inf.read() 52 | 53 | if jpgdata.startswith(b'\xff\xd8'): 54 | text = u'This is a JPEG file (%d bytes long)\n' 55 | else: 56 | text = u'This is a random file (%d bytes long)\n' 57 | 58 | with io.open('summary.txt', 'w', encoding='utf-8') as outf: 59 | outf.write(text % len(jpgdata)) 60 | ``` 61 | 62 | 我敢肯定,现在你会正确地使用```open```啦! 63 | -------------------------------------------------------------------------------- /book/Syntax/ternary_operators.md: -------------------------------------------------------------------------------- 1 | --- 2 | parent: 语法 3 | nav_order: 3 4 | --- 5 | 6 | # 三元运算符 7 | 8 | 三元运算符通常在Python里被称为条件表达式,这些表达式基于真(true)/假(false)的条件判断,在 Python 2.4 以上才有了三元操作。 9 | 10 | 下面是一个伪代码和例子: 11 | 12 | **伪代码:** 13 | 14 | ```python 15 | #如果条件为真,返回真 否则返回假 16 | condition_is_true if condition else condition_is_false 17 | ``` 18 | 19 | **例子:** 20 | 21 | ```python 22 | is_fat = True 23 | state = "fat" if is_fat else "not fat" 24 | ``` 25 | 26 | 它允许用简单的一行快速判断,而不是使用复杂的多行 ```if``` 语句。 27 | 这在大多数时候非常有用,而且可以使代码简单可维护。 28 | 29 | 另一个晦涩一点的用法比较少见,它使用了元组,请继续看: 30 | 31 | **伪代码:** 32 | 33 | ```python 34 | #(返回假,返回真)[真或假] 35 | (if_test_is_false, if_test_is_true)[test] 36 | ``` 37 | 38 | **例子:** 39 | 40 | ```python 41 | fat = True 42 | fitness = ("skinny", "fat")[fat] 43 | print("Ali is", fitness) 44 | #输出: Ali is fat 45 | ``` 46 | 47 | 这之所以能正常工作,是因为在Python中,True 等于1,而 False 等于0,这就相当于在元组中使用0和1来选取数据。 48 | 49 | 上面的例子没有被广泛使用,而且 Python 玩家一般不喜欢那样,因为没有 Python 味儿(Pythonic)。这样的用法很容易把真正的数据与 True/False 弄混。 50 | 51 | 另外一个不使用元组条件表达式的缘故是因为在元组中会把两个条件都执行,而 ```if-else``` 的条件表达式不会这样。 52 | 53 | 例如: 54 | 55 | ```python 56 | condition = True 57 | print(2 if condition else 1 / 0) 58 | #输出: 2 59 | 60 | print((1 / 0, 2)[condition]) 61 | #输出 ZeroDivisionError 异常 62 | ``` 63 | 64 | 这是因为在元组中是先建数据,然后用 True(1)/False(0) 来索引到数据。 65 | 而 ```if-else``` 条件表达式遵循普通的 ```if-else``` 逻辑树, 66 | 因此,如果逻辑中的条件异常,或者是重计算型(计算较久)的情况下,最好尽量避免使用元组条件表达式。 67 | -------------------------------------------------------------------------------- /book/author.md: -------------------------------------------------------------------------------- 1 | --- 2 | parent: 主页 3 | nav_order: 3 4 | --- 5 | 6 | # 关于原作者 7 | 8 | 我是 Muhammad Yasoob Ullah Khalid。 9 | 10 | 我已经广泛使用 Python 编程3年多了,同时参与了很多开源项目,并定期在[我的博客](http://pythontips.com/)里写一些关于 Python 有趣的话题。 11 | 12 | 2014年我在柏林举办的欧洲最大的 Python 会议 **EuroPython** 上做过精彩的演讲。 13 | 14 | > 译者注:分享的主题为:《Session: Web Scraping in Python 101》 15 | 16 | 地址:https://ep2014.europython.eu/en/schedule/sessions/20/ 17 | 18 | 如果你能给我有意思的工作机会,请联系我哦。 19 | 20 | > 译者注:嗯,硬广,你来中国么,HOHO 21 | 22 | # 作者前言 23 | 24 | Hello 大家好! 我非常自豪地宣布我自己创作的书完成啦。 25 | 经过很多辛苦工作和决心,终于将不可能变成了可能,"Intermediate Python" 终于杀青。 26 | 27 | ps: 它还将持续更新 :) 28 | 29 | Python 是一门奇妙的语言,还有一个强大而友爱的程序员社区。 30 | 然而,在你理解消化掉 Python 的基础后,关于下一步学习什么的资料比较缺乏。而我正是要通过本书来解决这一问题。 31 | 我会给你一些可以进一步探索的有趣的话题的信息。 32 | 33 | 本书讨论的这些话题将会打开你的脑洞,将你引导至 Python 语言的一些美好的地方。我最开始学习 Python 时,渴望见到Python最优雅的地方,而本书正是这些渴望的结果。 34 | 35 | 无论你是个初学者,中级或者甚至高级程序员,你都会在这本书里有所收获。 36 | 37 | 请注意本书不是一个指导手册,也不会教你 Python。因为书中的话题并没有进行基础解释,而只提供了展开讨论前所需的最少信息。 38 | 39 | 好啦,你肯定也和我一样兴奋,那让我们开始吧! 40 | 41 | # 开源公告 42 | 43 | 注意:这本书是开源的,也是一个持续进展中的工作。如果你发现排印错误,或者想添加更多内容进来,或者可以改进的任意地方(我知道你会发现很多),那么请慷慨地提交一个 pull request,我会无比高兴地合并进来。:) 44 | 45 | 另外,我决定将这本书免费发布!我相信它会帮助到那些需要帮助的人。祝你们好运! 46 | 47 | 这里是免费阅读链接: 48 | 49 | - [Html](http://book.pythontips.com/) 50 | - [PDF](http://readthedocs.org/projects/intermediatepythongithubio/downloads/pdf/latest/) 51 | - [GitHub](https://github.com/IntermediatePython/intermediatePython) 52 | 53 | # 广告 54 | 55 | 注意:你也可以现在为我捐助,如果你想买 [Gumroad](https://gumroad.com/l/intermediate_python) 提供的高大上版本。 56 | 57 | 你也可以加入我的[邮件列表](http://eepurl.com/bwjcej),这样你可以保持同步获取到重大更新或者我未来其他项目! 58 | 59 | 最后而且也很重要的是,如果你读了这本书,并且发现它很有帮助,那么一个私人邮件和一个 tweet 分享,对我来说会很有意义。 60 | -------------------------------------------------------------------------------- /book/decorators/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 装饰器 3 | nav_order: 6 4 | has_children: true 5 | --- 6 | 7 | # 装饰器 -------------------------------------------------------------------------------- /book/decorators/deco_with_args.md: -------------------------------------------------------------------------------- 1 | --- 2 | parent: 装饰器 3 | nav_order: 3 4 | --- 5 | 6 | # 带参数的装饰器 7 | 8 | 来想想这个问题,难道 ```@wraps``` 不也是个装饰器吗?但是,它接收一个参数,就像任何普通的函数能做的那样。那么,为什么我们不也那样做呢? 9 | 10 | 这是因为,当你使用 ```@my_decorator``` 语法时,你是在应用一个以单个函数作为参数的一个包裹函数。记住,Python里每个东西都是一个对象,而且这包括函数!记住了这些,我们可以编写一下能返回一个包裹函数的函数。 11 | 12 | 13 | # 在函数中嵌入装饰器 14 | 15 | 我们回到日志的例子,并创建一个包裹函数,能让我们指定一个用于输出的日志文件。 16 | 17 | ```python 18 | from functools import wraps 19 | 20 | def logit(logfile='out.log'): 21 | def logging_decorator(func): 22 | @wraps(func) 23 | def wrapped_function(*args, **kwargs): 24 | log_string = func.__name__ + " was called" 25 | print(log_string) 26 | # 打开logfile,并写入内容 27 | with open(logfile, 'a') as opened_file: 28 | # 现在将日志打到指定的logfile 29 | opened_file.write(log_string + '\n') 30 | return func(*args, **kwargs) 31 | return wrapped_function 32 | return logging_decorator 33 | 34 | @logit() 35 | def myfunc1(): 36 | pass 37 | 38 | myfunc1() 39 | # Output: myfunc1 was called 40 | # 现在一个叫做 out.log 的文件出现了,里面的内容就是上面的字符串 41 | 42 | @logit(logfile='func2.log') 43 | def myfunc2(): 44 | pass 45 | 46 | myfunc2() 47 | # Output: myfunc2 was called 48 | # 现在一个叫做 func2.log 的文件出现了,里面的内容就是上面的字符串 49 | ``` 50 | 51 | 52 | # 装饰器类 53 | 54 | 现在我们有了能用于正式环境的 ```logit``` 装饰器,但当我们的应用的某些部分还比较脆弱时,异常也许是需要更紧急关注的事情。比方说有时你只想打日志到一个文件。而有时你想把引起你注意的问题发送到一个email,同时也保留日志,留个记录。这是一个使用继承的场景,但目前为止我们只看到过用来构建装饰器的函数。 55 | 56 | 幸运的是,类也可以用来构建装饰器。那我们现在以一个类而不是一个函数的方式,来重新构建 ```logit```。 57 | 58 | ```python 59 | class logit(object): 60 | 61 | _logfile = 'out.log' 62 | 63 | def __init__(self, func): 64 | self.func = func 65 | 66 | def __call__(self, *args): 67 | log_string = self.func.__name__ + " was called" 68 | print(log_string) 69 | # 打开logfile并写入 70 | with open(self._logfile, 'a') as opened_file: 71 | # 现在将日志打到指定的文件 72 | opened_file.write(log_string + '\n') 73 | # 现在,发送一个通知 74 | self.notify() 75 | 76 | # return base func 77 | return self.func(*args) 78 | 79 | 80 | 81 | def notify(self): 82 | # logit只打日志,不做别的 83 | pass 84 | ``` 85 | 86 | 这个实现有一个附加优势,在于比嵌套函数的方式更加整洁,而且包裹一个函数还是使用跟以前一样的语法: 87 | 88 | ```python 89 | logit._logfile = 'out2.log' # 如果需要修改log文件参数 90 | @logit 91 | def myfunc1(): 92 | pass 93 | 94 | myfunc1() 95 | # 输出: myfunc1 was called 96 | ``` 97 | 98 | 现在,我们给```logit```创建子类,来添加email的功能(虽然email这个话题不会在这里展开)。 99 | 100 | ```python 101 | class email_logit(logit): 102 | ''' 103 | 一个logit的实现版本,可以在函数调用时发送email给管理员 104 | ''' 105 | def __init__(self, func, email='admin@myproject.com', *args, **kwargs): 106 | self.email = email 107 | super(email_logit, self).__init__(func, *args, **kwargs) 108 | 109 | def notify(self): 110 | # 发送一封email到self.email 111 | # 这里就不做实现了 112 | pass 113 | ``` 114 | 115 | 从现在起,```@email_logit``` 将会和 ```@logit``` 产生同样的效果,但是在打日志的基础上,还会多发送一封邮件给管理员。 116 | 117 | ```python 118 | email_logit._logfile = 'out3.log' # 如果需要修改log文件参数 119 | @email_logit 120 | def myfunc2(): 121 | pass 122 | 123 | myfunc2() 124 | # 输出: myfunc2 was called 125 | ``` 126 | -------------------------------------------------------------------------------- /book/decorators/decorators.md: -------------------------------------------------------------------------------- 1 | --- 2 | parent: 装饰器 3 | nav_order: 1 4 | --- 5 | 6 | 7 | # 装饰器原理 8 | 9 | 装饰器(Decorators)是 Python 的一个重要部分。简单地说:他们是修改其他函数的功能的函数。他们有助于让我们的代码更简短,也更 Pythonic(Python范儿)。大多数初学者不知道在哪儿使用它们,所以我将要分享下,哪些区域里装饰器可以让你的代码更简洁。 10 | 11 | 首先,让我们讨论下如何写你自己的装饰器。 12 | 13 | 这可能是最难掌握的概念之一。我们会每次只讨论一个步骤,这样你能完全理解它。 14 | 15 | # 一切皆对象 16 | 17 | 首先我们来理解下 Python 中的函数 18 | 19 | ```python 20 | def hi(name="yasoob"): 21 | return "hi " + name 22 | 23 | print(hi()) 24 | # output: 'hi yasoob' 25 | 26 | # 我们甚至可以将一个函数赋值给一个变量,比如 27 | greet = hi 28 | # 我们这里没有在使用小括号,因为我们并不是在调用hi函数 29 | # 而是在将它放在greet变量里头。我们尝试运行下这个 30 | 31 | print(greet()) 32 | # output: 'hi yasoob' 33 | 34 | # 如果我们删掉旧的hi函数,看看会发生什么! 35 | del hi 36 | print(hi()) 37 | #outputs: NameError 38 | 39 | print(greet()) 40 | #outputs: 'hi yasoob' 41 | ``` 42 | 43 | # 在函数中定义函数 44 | 45 | 刚才那些就是函数的基本知识了。我们来让你的知识更进一步。在 Python 中我们可以在一个函数中定义另一个函数: 46 | 47 | ```python 48 | def hi(name="yasoob"): 49 | print("now you are inside the hi() function") 50 | 51 | def greet(): 52 | return "now you are in the greet() function" 53 | 54 | def welcome(): 55 | return "now you are in the welcome() function" 56 | 57 | print(greet()) 58 | print(welcome()) 59 | print("now you are back in the hi() function") 60 | 61 | hi() 62 | #output:now you are inside the hi() function 63 | # now you are in the greet() function 64 | # now you are in the welcome() function 65 | # now you are back in the hi() function 66 | 67 | # 上面展示了无论何时你调用hi(), greet()和welcome()将会同时被调用。 68 | # 然后greet()和welcome()函数在hi()函数之外是不能访问的,比如: 69 | 70 | greet() 71 | #outputs: NameError: name 'greet' is not defined 72 | ``` 73 | 74 | 那现在我们知道了可以在函数中定义另外的函数。也就是说:我们可以创建嵌套的函数。现在你需要再多学一点,就是函数也能返回函数。 75 | 76 | 77 | # 从函数中返回函数 78 | 79 | 其实并不需要在一个函数里去执行另一个函数,我们也可以将其作为输出返回出来: 80 | 81 | ```python 82 | def hi(name="yasoob"): 83 | def greet(): 84 | return "now you are in the greet() function" 85 | 86 | def welcome(): 87 | return "now you are in the welcome() function" 88 | 89 | if name == "yasoob": 90 | return greet 91 | else: 92 | return welcome 93 | 94 | a = hi() 95 | print(a) 96 | #outputs: 97 | 98 | #上面清晰地展示了`a`现在指向到hi()函数中的greet()函数 99 | #现在试试这个 100 | 101 | print(a()) 102 | #outputs: now you are in the greet() function 103 | ``` 104 | 105 | 再次看看这个代码。在 ```if/else``` 语句中我们返回 ```greet``` 和 ```welcome```,而不是 ```greet()``` 和 ```welcome()```。为什么那样?这是因为当你把一对小括号放在后面,这个函数就会执行;然而如果你不放括号在它后面,那它可以被到处传递,并且可以赋值给别的变量而不去执行它。 106 | 107 | 你明白了吗?让我再稍微多解释点细节。 108 | 109 | 当我们写下 ```a = hi()```,```hi()``` 会被执行,而由于 ```name``` 参数默认是 *yasoob*,所以函数 ```greet``` 被返回了。如果我们把语句改为 ```a = hi(name = "ali")```,那么 ```welcome``` 函数将被返回。我们还可以打印出 ```hi()()```,这会输出 *now you are in the greet() function*。 110 | 111 | 112 | # 将函数作为参数传给另一个函数 113 | 114 | ```python 115 | def hi(): 116 | return "hi yasoob!" 117 | 118 | def doSomethingBeforeHi(func): 119 | print("I am doing some boring work before executing hi()") 120 | print(func()) 121 | 122 | doSomethingBeforeHi(hi) 123 | #outputs:I am doing some boring work before executing hi() 124 | # hi yasoob! 125 | ``` 126 | 127 | 现在你已经具备所有必需知识,来进一步学习装饰器真正是什么了。装饰器让你在一个函数的前后去执行代码。 128 | 129 | -------------------------------------------------------------------------------- /book/decorators/func_caching.md: -------------------------------------------------------------------------------- 1 | --- 2 | parent: 装饰器 3 | nav_order: 4 4 | --- 5 | 6 | # 函数缓存(Function caching) 7 | 8 | 函数缓存允许我们将一个函数对于给定参数的返回值缓存起来。 9 | 当一个 I/O 密集的函数被频繁使用相同的参数调用的时候,函数缓存可以节约时间。 10 | 在 Python 3.2 版本以前我们只有写一个自定义的实现。在 Python 3.2 以后版本,有个 ```lru_cache``` 的装饰器,允许我们将一个函数的返回值快速地缓存或取消缓存。 11 | 12 | 我们来看看,Python 3.2 前后的版本分别如何使用它。 13 | 14 | # Python 3.2及以后版本 15 | 16 | 我们来实现一个斐波那契计算器,并使用 ```lru_cache```。 17 | 18 | ```python 19 | from functools import lru_cache 20 | 21 | @lru_cache(maxsize=32) 22 | def fib(n): 23 | if n < 2: 24 | return n 25 | return fib(n-1) + fib(n-2) 26 | 27 | >>> print([fib(n) for n in range(10)]) 28 | # Output: [0, 1, 1, 2, 3, 5, 8, 13, 21, 34] 29 | ``` 30 | 31 | 那个 ```maxsize``` 参数是告诉 ```lru_cache```,最多缓存最近多少个返回值。 32 | 33 | 我们也可以轻松地对返回值清空缓存,通过这样: 34 | 35 | ```python 36 | fib.cache_clear() 37 | ``` 38 | 39 | # Python 2系列版本 40 | 41 | 你可以创建任意种类的缓存机制,有若干种方式来达到相同的效果,这完全取决于你的需要。 42 | 这里是一个一般的缓存: 43 | 44 | ```python 45 | from functools import wraps 46 | 47 | def memoize(function): 48 | memo = {} 49 | @wraps(function) 50 | def wrapper(*args): 51 | if args in memo: 52 | return memo[args] 53 | else: 54 | rv = function(*args) 55 | memo[args] = rv 56 | return rv 57 | return wrapper 58 | 59 | @memoize 60 | def fibonacci(n): 61 | if n < 2: return n 62 | return fibonacci(n - 1) + fibonacci(n - 2) 63 | 64 | fibonacci(25) 65 | ``` 66 | 67 | 这里有一篇 [Caktus Group 的不错的文章](https://www.caktusgroup.com/blog/2015/06/08/testing-client-side-applications-django-post-mortem/),在其中他们发现一个 Django 框架的由 lru_cache 导致的 bug。读起来很有意思。一定要打开去看一下。 68 | -------------------------------------------------------------------------------- /book/decorators/your_first_decorator.md: -------------------------------------------------------------------------------- 1 | --- 2 | parent: 装饰器 3 | nav_order: 2 4 | --- 5 | 6 | # 你的第一个装饰器 7 | 8 | 在上一个例子里,其实我们已经创建了一个装饰器!现在我们修改下上一个装饰器,并编写一个稍微更有用点的程序: 9 | 10 | ```python 11 | def a_new_decorator(a_func): 12 | 13 | def wrapTheFunction(): 14 | print("I am doing some boring work before executing a_func()") 15 | 16 | a_func() 17 | 18 | print("I am doing some boring work after executing a_func()") 19 | 20 | return wrapTheFunction 21 | 22 | def a_function_requiring_decoration(): 23 | print("I am the function which needs some decoration to remove my foul smell") 24 | 25 | a_function_requiring_decoration() 26 | #outputs: "I am the function which needs some decoration to remove my foul smell" 27 | 28 | a_function_requiring_decoration = a_new_decorator(a_function_requiring_decoration) 29 | #now a_function_requiring_decoration is wrapped by wrapTheFunction() 30 | 31 | a_function_requiring_decoration() 32 | #outputs:I am doing some boring work before executing a_func() 33 | # I am the function which needs some decoration to remove my foul smell 34 | # I am doing some boring work after executing a_func() 35 | ``` 36 | 37 | 你看明白了吗?我们刚刚应用了之前学习到的原理。这正是python中装饰器做的事情!它们封装一个函数,并且用这样或者那样的方式来修改它的行为。现在你也许疑惑,我们在代码里并没有使用 ```@``` 符号?那只是一个简短的方式来生成一个被装饰的函数。这里是我们如何使用 ```@``` 来运行之前的代码: 38 | 39 | ```python 40 | @a_new_decorator 41 | def a_function_requiring_decoration(): 42 | """Hey you! Decorate me!""" 43 | print("I am the function which needs some decoration to " 44 | "remove my foul smell") 45 | 46 | a_function_requiring_decoration() 47 | #outputs: I am doing some boring work before executing a_func() 48 | # I am the function which needs some decoration to remove my foul smell 49 | # I am doing some boring work after executing a_func() 50 | 51 | #the @a_new_decorator is just a short way of saying: 52 | a_function_requiring_decoration = a_new_decorator(a_function_requiring_decoration) 53 | ``` 54 | 55 | 希望你现在对 Python 装饰器的工作原理有一个基本的理解。如果我们运行如下代码会存在一个问题: 56 | 57 | ```python 58 | print(a_function_requiring_decoration.__name__) 59 | # Output: wrapTheFunction 60 | ``` 61 | 62 | 这并不是我们想要的!Ouput 输出应该是 “a_function_requiring_decoration”。这里的函数被 warpTheFunction 替代了。它重写了我们函数的名字和注释文档(docstring)。幸运的是 Python 提供给我们一个简单的函数来解决这个问题,那就是 ```functools.wraps```。我们修改上一个例子来使用 ```functools.wraps```: 63 | 64 | ```python 65 | from functools import wraps 66 | 67 | def a_new_decorator(a_func): 68 | @wraps(a_func) 69 | def wrapTheFunction(): 70 | print("I am doing some boring work before executing a_func()") 71 | a_func() 72 | print("I am doing some boring work after executing a_func()") 73 | return wrapTheFunction 74 | 75 | @a_new_decorator 76 | def a_function_requiring_decoration(): 77 | """Hey yo! Decorate me!""" 78 | print("I am the function which needs some decoration to " 79 | "remove my foul smell") 80 | 81 | print(a_function_requiring_decoration.__name__) 82 | # Output: a_function_requiring_decoration 83 | ``` 84 | 85 | 现在好多了。我们接下来学习装饰器的一些常用场景。 86 | 87 | 蓝本规范: 88 | 89 | ```python 90 | from functools import wraps 91 | def decorator_name(f): 92 | @wraps(f) 93 | def decorated(*args, **kwargs): 94 | if not can_run: 95 | return "Function will not run" 96 | return f(*args, **kwargs) 97 | return decorated 98 | 99 | @decorator_name 100 | def func(): 101 | return("Function is running") 102 | 103 | can_run = True 104 | print(func()) 105 | # Output: Function is running 106 | 107 | can_run = False 108 | print(func()) 109 | # Output: Function will not run 110 | ``` 111 | 112 | 注意:```@wraps``` 接受一个函数来进行装饰,并加入了复制函数名称、注释文档、参数列表等等的功能。这可以让我们在装饰器里面访问在装饰之前的函数的属性。 113 | 114 | # 使用场景 115 | 116 | 现在我们来看一下装饰器在哪些地方特别耀眼,以及使用它可以让一些事情管理起来变得更简单。 117 | 118 | # 授权(Authorization) 119 | 120 | 装饰器能有助于检查某个人是否被授权去使用一个 web 应用的端点(endpoint)。它们被大量使用于 Flask 和 Django web 框架中。这里是一个例子来使用基于装饰器的授权: 121 | 122 | ```python 123 | from functools import wraps 124 | 125 | def requires_auth(f): 126 | @wraps(f) 127 | def decorated(*args, **kwargs): 128 | auth = request.authorization 129 | if not auth or not check_auth(auth.username, auth.password): 130 | authenticate() 131 | return f(*args, **kwargs) 132 | return decorated 133 | ``` 134 | 135 | # 日志(Logging) 136 | 137 | 日志是装饰器运用的另一个亮点。这是个例子: 138 | 139 | ```python 140 | from functools import wraps 141 | 142 | def logit(func): 143 | @wraps(func) 144 | def with_logging(*args, **kwargs): 145 | print(func.__name__ + " was called") 146 | return func(*args, **kwargs) 147 | return with_logging 148 | 149 | @logit 150 | def addition_func(x): 151 | """Do some math.""" 152 | return x + x 153 | 154 | 155 | result = addition_func(4) 156 | # Output: addition_func was called 157 | ``` 158 | 159 | 我敢肯定你已经在思考装饰器的一个其他聪明用法了。 160 | -------------------------------------------------------------------------------- /book/donors.md: -------------------------------------------------------------------------------- 1 | --- 2 | parent: 主页 3 | nav_order: 5 4 | --- 5 | 6 | # 捐赠名单 7 | 8 | 感谢以下童鞋的捐助,你们的慷慨是我们继续的动力: 9 | 10 | 特别感谢 言* 的两次慷慨捐赠。 11 | 12 | | donor | value | 13 | | -- | -- | 14 | | 好心人 | 8.80 | 15 | | 不知名 | 6.00 | 16 | | 李*雷 | 8.80 | 17 | | su*z | 1.00 | 18 | | Zer* | 10.00 | 19 | | 牛*甫 | 5.00 | 20 | | 钟*才 | 16.00 | 21 | | J*m | 6.00 | 22 | | 鑫* | 10.00 | 23 | | 黄* | 4.99 | 24 | | tho*s | 5.00 | 25 | | 钱* | 2.00 | 26 | | py* | 10.00 | 27 | | 吼* | 5.29 | 28 | | 破* | 5.00 | 29 | | 吉祥* | 6.66 | 30 | | Lynd* | 8.88 | 31 | | 言* | 18.00 | 32 | | 言* | 88.00 | 33 | 34 | *你还记得,深夜埋头学习的你吗?* -------------------------------------------------------------------------------- /book/seealso.md: -------------------------------------------------------------------------------- 1 | --- 2 | parent: 主页 3 | nav_order: 4 4 | --- 5 | 6 | # 推荐阅读 7 | 8 | ## 本书推送贴的留言和讨论 9 | 10 | - [v2ex首发贴](http://www.v2ex.com/t/267557) 11 | - [微博长文首发贴](http://weibo.com/1054764633/DoN6Z5Haq?type=repost) 12 | 13 | ## v2ex网友florije推荐 14 | 15 | - 另外一本同名IntermediatePython的更新的书 https://leanpub.com/intermediatepython 16 | 17 | ## v2ex网友xiaket推荐 18 | 19 | - 对于Python提高类的书,推荐Fluent Python 或 Pro Python 20 | 21 | ## v2ex网友shishen10 推荐 22 | 23 | - 老齐的教程 https://github.com/qiwsir/StarterLearningPython 24 | - 老齐还整理了很多精华 https://github.com/qiwsir/ITArticles 25 | 26 | ## v2ex网友xiaowangge推荐 27 | 28 | [Yixiaohan](https://github.com/Yixiaohan)整理了一个不错的推荐:[Python初学者(零基础学习Python、Python入门)书籍、视频、资料、社区推荐](https://github.com/Yixiaohan/codeparkshare)大家可以前去Fork。 29 | 30 | ## v2ex推荐学习书目 31 | 32 | - [Learn Python the Hard Way](https://flyouting.gitbooks.io/learn-python-the-hard-way-cn/content/) 33 | - [Python 学习手册-第五版中文版](https://www.gitbook.com/book/yulongjun/learning-python-in-chinese/details) 34 | - [Python Cookbook](http://python3-cookbook.readthedocs.org/zh_CN/latest/) 35 | - [Python 基础教程](https://book.douban.com/subject/4866934/) 36 | -------------------------------------------------------------------------------- /book/translator.md: -------------------------------------------------------------------------------- 1 | --- 2 | parent: 主页 3 | nav_order: 2 4 | --- 5 | 6 | # 译者后记 7 | 8 | ## 译者[老苏@suqi](https://github.com/suqi)感言 9 | 10 | 在翻译过程中,慢慢发现,本书作者的行文方式有着科普作家的风范──那就是能将晦涩难懂的技术用比较清晰简洁的方式进行呈现,深入浅出的风格在每个章节的讨论中都得到了体现: 11 | 12 | - 每个章节都非常精简,5分钟就能看完,用最简洁的例子精辟地展现了原理 13 | - 每个章节都会通过疑问,来引导读者主动思考答案 14 | - 每个章节都引导读者做延伸阅读,让有兴趣的读者能进一步举一反三 15 | - 每个章节都是独立的,你可以挑选任意的章节开始阅读,而不受影响 16 | 17 | 总之,这本书非常方便随时选取一个章节进行阅读,而且每次阅读一个章节,你都可能会有一些新的发现。 18 | 19 | ## 本译作为开源书籍,欢迎 Pull Request 20 | - [用于直接阅读的GithubPages,实时更新部署](https://py.eastlakeside.cn) 21 | - [Github源码](https://github.com/eastlakeside/interpy-zh) 22 | - [Gitbook托管版本,由于目录结构不适配,于2021年8月停止更新](https://eastlakeside.gitbooks.io/interpy-zh/) 23 | 24 | -------------------------------------------------------------------------------- /code/2.7/10_slot.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | 3 | import sys 4 | 5 | class MyClass(object): 6 | def __init__(self, name, identifier): 7 | self.name = name 8 | self.identifier = identifier 9 | 10 | class MyClassSlot(object): 11 | __slot__ = ['name', 'identifier'] 12 | def __init__(self, name, identifier): 13 | self.name = name 14 | self.identifier = identifier 15 | 16 | 17 | -------------------------------------------------------------------------------- /code/2.7/12_collection.py: -------------------------------------------------------------------------------- 1 | import collections, json 2 | 3 | colours = ( 4 | ('Yasoob', 'Yellow'), 5 | ('Ali', 'Blue'), 6 | ('Arham', 'Green'), 7 | ('Ali', 'Black'), 8 | ('Yasoob', 'Red'), 9 | ('Ahmed', 'Silver'), 10 | ) 11 | 12 | def defaultdict_test(): 13 | favourite_colours = collections.defaultdict(list) 14 | 15 | for name, colour in colours: 16 | favourite_colours[name].append(colour) 17 | 18 | print favourite_colours 19 | 20 | def defaultdict_keyError(): 21 | tree = lambda: collections.defaultdict(tree) 22 | some_dict = tree() 23 | some_dict['colours']['favourite'] = 'yellow' 24 | print json.dumps(some_dict) 25 | 26 | def counter_test(): 27 | favs = collections.Counter(name for name, colour in colours) 28 | print favs 29 | 30 | def deque_test(): 31 | d = collections.deque() 32 | d.append('1') 33 | d.append('2') 34 | d.append('3') 35 | d.pop() 36 | print d 37 | d.popleft() 38 | print d 39 | 40 | def namedtuple_test(): 41 | Animal = collections.namedtuple('Animal', 'name age type') 42 | perry = Animal(name='perry', age=31, type='cat') 43 | print perry.name 44 | print perry[0] 45 | # perry.age = 21 This is wrong, you can't change it 46 | 47 | def main(): 48 | defaultdict_test() 49 | defaultdict_keyError() 50 | counter_test() 51 | deque_test() 52 | namedtuple_test() 53 | 54 | 55 | if __name__ == '__main__': 56 | main() 57 | -------------------------------------------------------------------------------- /code/2.7/13_enumerate.py: -------------------------------------------------------------------------------- 1 | def enum_test(): 2 | my_list = ['apple', 'banana', 'grapes', 'pear'] 3 | 4 | for c, v in enumerate(my_list, 1): 5 | print c, v 6 | 7 | def main(): 8 | enum_test() 9 | 10 | if __name__ == '__main__': 11 | main() 12 | -------------------------------------------------------------------------------- /code/2.7/15_comprehensions.py: -------------------------------------------------------------------------------- 1 | #coding=utf-8 2 | 3 | # 推导式包括三种:list comprehension, dict comprehension, set comprehension 4 | 5 | def list_comprehension(): 6 | multiples = [i for i in range(30) if i % 3 is 0] 7 | square = [i*i for i in range(10)] 8 | print multiples 9 | print square 10 | 11 | def dict_comprehension(): 12 | mcase = {'a':10, 'b':34, 'A':7, 'Z':3} 13 | mcase_frequency = {k.lower(): mcase.get(k.lower(), 0) + mcase.get(k.upper(), 0) for k in mcase.keys()} 14 | print mcase_frequency 15 | 16 | def set_comprehension(): 17 | square = {x**2 for x in [1,1,2]} 18 | print square 19 | 20 | def main(): 21 | list_comprehension() 22 | dict_comprehension() 23 | set_comprehension() 24 | 25 | if __name__ == '__main__': 26 | main() 27 | -------------------------------------------------------------------------------- /code/2.7/16_exception.py: -------------------------------------------------------------------------------- 1 | def file_exception1(): 2 | try: 3 | file = open('test.txt', 'rb') 4 | except EOFError as e: 5 | print("An EOF error occurred.") 6 | raise e 7 | except IOError as e: 8 | print("An error occurred.") 9 | raise e 10 | else: 11 | ... 12 | finally: 13 | ... 14 | 15 | def file_exception2(): 16 | try: 17 | file = open('test.txt', 'rb') 18 | except (IOError, EOFError) as e: 19 | print 'An error occurd. {}'.format(e.args[-1]) 20 | finally: 21 | ... 22 | 23 | def file_exception3(): 24 | try: 25 | file = open('test.txt', 'rb') 26 | except Exception: 27 | raise 28 | -------------------------------------------------------------------------------- /code/2.7/17_lambda.py: -------------------------------------------------------------------------------- 1 | add = lambda x, y: x + y 2 | print add(4,6) 3 | -------------------------------------------------------------------------------- /code/2.7/19_for_else.py: -------------------------------------------------------------------------------- 1 | def primer(n): 2 | for i in range(2, n): 3 | for j in range(2, n): 4 | if i % j == 0: 5 | print i, 'equals', j, '*', i/j 6 | break 7 | else: 8 | print i, 'is a prime number' 9 | 10 | primer(100) 11 | -------------------------------------------------------------------------------- /code/2.7/1_args_kwargs.py: -------------------------------------------------------------------------------- 1 | def test_var_args(f_arg, *argv): 2 | print 'first normal arg:', f_arg 3 | for arg in argv: 4 | print 'another arg though *argv:', arg 5 | 6 | 7 | def greet_me(**kwargs): 8 | for key, value in kwargs.items(): 9 | print '{0} == {1}'.format(key, value) 10 | 11 | def main(): 12 | test_var_args('yasoob', 'python', 'eggs', 'test') 13 | greet_me(name='yasoob') 14 | 15 | if __name__ == '__main__': 16 | main() 17 | -------------------------------------------------------------------------------- /code/2.7/20_cextend_capi.py: -------------------------------------------------------------------------------- 1 | import addList 2 | 3 | l = [1,2,3,4,5] 4 | print 'Sum of list - ', str(l), '=', str(addList.add(l)) 5 | -------------------------------------------------------------------------------- /code/2.7/20_cextend_ctypes.py: -------------------------------------------------------------------------------- 1 | from ctypes import * 2 | 3 | def main(): 4 | adder = CDLL('./adder.so') 5 | 6 | res_int = adder.add_int(4,5) 7 | print 'Sum of 4 and 5 = ', str(res_int) 8 | 9 | a = c_float(5.5) 10 | b = c_float(4.1) 11 | 12 | add_float = adder.add_float 13 | add_float.restype = c_float 14 | print 'Sum of 5.5 and 4.1 = ', str(add_float(a,b)) 15 | 16 | if __name__ == '__main__': 17 | main() 18 | -------------------------------------------------------------------------------- /code/2.7/21_open.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | 3 | # 错误写法 4 | def open_file(): 5 | f = open('photo.jpg', 'r+') 6 | jpgdata = f.read() 7 | f.close() 8 | 9 | 10 | #正确写法 11 | def open_file_right(): 12 | with open('photo.jpg', 'r+') as f: 13 | jpgdata = f.read() 14 | 15 | -------------------------------------------------------------------------------- /code/2.7/23_coroutine.py: -------------------------------------------------------------------------------- 1 | def grep(pattern): 2 | print 'Searching for', pattern 3 | while True: 4 | line = (yield) 5 | if pattern in line: 6 | print line 7 | 8 | def main(): 9 | search = grep('coroutine') 10 | next(search) 11 | search.send('I love you') 12 | search.send("Don't you love me?") 13 | search.send('I love coroutine instead!') 14 | 15 | if __name__ == '__main__': 16 | main() 17 | -------------------------------------------------------------------------------- /code/2.7/24_cache.py: -------------------------------------------------------------------------------- 1 | from functools import wraps 2 | 3 | def memoize(function): 4 | memo = {} 5 | @wraps(function) 6 | def wrapper(*args): 7 | if args in memo: 8 | return memo[args] 9 | else: 10 | rv = function(*args) 11 | memo[args] = rv 12 | return rv 13 | return wrapper 14 | 15 | 16 | @memoize 17 | def fibonacci(n): 18 | if n < 2: 19 | return n 20 | return fibonacci(n-1) + fibonacci(n-2) 21 | 22 | def main(): 23 | fibonacci(25) 24 | 25 | if __name__ == '__main__': 26 | main() 27 | -------------------------------------------------------------------------------- /code/2.7/25_contextmanager.py: -------------------------------------------------------------------------------- 1 | class File(object): 2 | def __init__(self, file_name, method): 3 | self.file_obj = open(file_name, method) 4 | def __enter__(self): 5 | return self.file_obj 6 | def __exit__(self, type, value, traceback): 7 | # handle exception 8 | print 'Exception has been handled' 9 | self.file_obj.close() 10 | return True 11 | 12 | def main(): 13 | with File('demo.txt', 'w') as opened_file: 14 | opened_file.write('Hola!') 15 | 16 | if __name__ == '__main__': 17 | main() 18 | -------------------------------------------------------------------------------- /code/2.7/25_contextmanager_generator.py: -------------------------------------------------------------------------------- 1 | from contextlib import contextmanager 2 | 3 | @contextmanager 4 | def open_file(name): 5 | f = open(name, 'w') 6 | yield f 7 | f.close() 8 | 9 | def main(): 10 | with open_file('some_file') as f: 11 | f.write('Hola!') 12 | -------------------------------------------------------------------------------- /code/2.7/3_generators.py: -------------------------------------------------------------------------------- 1 | def fibon(n): 2 | a = b = 1 3 | for i in range(n): 4 | yield a 5 | a, b = b, a + b 6 | 7 | def fibonacci(): 8 | for x in fibon(1000000): 9 | print x 10 | 11 | def generator_function(): 12 | for i in range(3): 13 | yield i 14 | 15 | def main(): 16 | fibonacci() 17 | 18 | if __name__ == '__main__': 19 | main() 20 | -------------------------------------------------------------------------------- /code/2.7/4_map_filter.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | 3 | # 书中代码 map,filter 前面的list是多余的,因为map本来就会返回一个list 4 | 5 | def square(): 6 | items = [1,2,3,4,5] 7 | print map(lambda x: x**2, items) 8 | 9 | def multiply(x): 10 | return x*x 11 | 12 | def add(x): 13 | return x+x 14 | 15 | def filter_less_than_zero(): 16 | number_list = range(-5, 5) 17 | print filter(lambda x: x<0, number_list) 18 | 19 | def main(): 20 | square() 21 | 22 | funcs = [multiply, add] 23 | for i in range(5): 24 | print map(lambda x: x(i), funcs) 25 | 26 | filter_less_than_zero() 27 | 28 | if __name__ == '__main__': 29 | main() 30 | -------------------------------------------------------------------------------- /code/2.7/5_set_datastruct.py: -------------------------------------------------------------------------------- 1 | def duplicates(some_list): 2 | return set([x for x in some_list if some_list.count(x) > 1]) 3 | 4 | def intersect(): 5 | valid = set(['yellow', 'red', 'blue', 'green', 'black']) 6 | input_set = set(['red', 'brown']) 7 | print(input_set.intersection(valid)) 8 | 9 | def difference(): 10 | valid = set(['yellow', 'red', 'blue', 'green', 'black']) 11 | input_set = set(['red', 'brown']) 12 | print(input_set.difference(valid)) 13 | 14 | def main(): 15 | some_list = ['a', 'b', 'c', 'b', 'd', 'm', 'n', 'n'] 16 | print duplicates(some_list) 17 | intersect() 18 | difference() 19 | 20 | if __name__ == '__main__': 21 | main() 22 | -------------------------------------------------------------------------------- /code/2.7/7_decorators.py: -------------------------------------------------------------------------------- 1 | from functools import wraps 2 | 3 | def a_new_decorator(a_func): 4 | @wraps(a_func) 5 | def wrap_the_function(): 6 | print 'I am doing some boring work before executing a_func()' 7 | a_func() 8 | print 'I am doing some boring work after executing a_func()' 9 | return wrap_the_function 10 | 11 | @a_new_decorator 12 | def a_function_requiring_decoration(): 13 | '''Hey you! Decorate me!''' 14 | print 'I am the function which needs some decoration to remove my foul smell' 15 | 16 | 17 | a_function_requiring_decoration() 18 | a_function_requiring_decoration = a_new_decorator(a_function_requiring_decoration) 19 | print a_function_requiring_decoration.__name__ 20 | 21 | -------------------------------------------------------------------------------- /code/2.7/7_decorators1.py: -------------------------------------------------------------------------------- 1 | from functools import wraps 2 | 3 | def decorator_name(f): 4 | @wraps(f) 5 | def decorated(*args, **kwargs): 6 | if not can_run: 7 | return 'function will not run' 8 | return f(*args, **kwargs) 9 | return decorated 10 | 11 | 12 | @decorator_name 13 | def func(): 14 | return 'function is running' 15 | 16 | can_run = True 17 | print func() 18 | 19 | can_run = False 20 | print func() 21 | -------------------------------------------------------------------------------- /code/2.7/7_decorators_args.py: -------------------------------------------------------------------------------- 1 | from functools import wraps 2 | 3 | def logit(logfile='out.log'): 4 | def logging_decorator(func): 5 | @wraps(func) 6 | def wrapped_function(*args, **kwargs): 7 | log_string = func.__name__ + ' was called' 8 | print log_string 9 | 10 | with open(logfile, 'a') as opened_file: 11 | opened_file.write(log_string + '\n') 12 | 13 | return func(*args, **kwargs) 14 | return wrapped_function 15 | return logging_decorator 16 | 17 | @logit() 18 | def myfunc1(): 19 | pass 20 | 21 | @logit(logfile='func2.log') 22 | def myfunc2(): 23 | pass 24 | 25 | def main(): 26 | myfunc1() 27 | myfunc2() 28 | 29 | if __name__ == '__main__': 30 | main() 31 | -------------------------------------------------------------------------------- /code/2.7/7_decorators_class.py: -------------------------------------------------------------------------------- 1 | from functools import wraps 2 | 3 | class logit(object): 4 | def __init__(self, logfile='out.log'): 5 | self.logfile = logfile 6 | 7 | def __call__(self, func): 8 | @wraps(func) 9 | def wrapped_function(*args, **kwargs): 10 | log_string = func.__name__ + " was called" 11 | print(log_string) 12 | 13 | with open(self.logfile, 'a') as opened_file: 14 | opened_file.write(log_string + '\n') 15 | self.notify() 16 | 17 | return func(*args, **kwargs) 18 | return wrapped_function 19 | 20 | def notify(self): 21 | pass 22 | 23 | class email_logit(logit): 24 | def __init__(self, email='admin@myproject.com', *args, **kwargs): 25 | self.email = email 26 | super(logit, self).__init__(*args, **kwargs) 27 | def notify(self): 28 | pass 29 | -------------------------------------------------------------------------------- /code/2.7/7_decorators_scenario.py: -------------------------------------------------------------------------------- 1 | from functools import wraps 2 | 3 | # 1. Authorization 4 | def requires_auth(f): 5 | @wraps(f) 6 | def decorated(*args, **kwargs): 7 | auth = request.authrization 8 | if not auth or not check_auth(auth.username, auth.password): 9 | authenticate() 10 | return f(*args, **kwargs) 11 | return decorated 12 | 13 | # 2.logging 14 | def logit(func): 15 | @wraps(func) 16 | def with_logging(*args, **kwargs): 17 | print func.__name__ + ' was called' 18 | return func(*args, **kwargs) 19 | return with_logging 20 | 21 | @logit 22 | def addition_func(x): 23 | '''Do some math.''' 24 | return x + x 25 | 26 | -------------------------------------------------------------------------------- /code/2.7/8_global_return.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | # 尽量不要使用 global 来存结果 3 | 4 | def add_return(value1, value2): 5 | return value1 + value2 6 | 7 | def add_global(value1, value2): 8 | global result 9 | result = value1 + value2 10 | 11 | def main(): 12 | print add_return(1,2) 13 | add_global(5,5) 14 | print result 15 | 16 | if __name__ == '__main__': 17 | main() 18 | -------------------------------------------------------------------------------- /code/2.7/9_mutation.py: -------------------------------------------------------------------------------- 1 | 2 | foo = ['hi'] 3 | print foo 4 | 5 | bar = foo 6 | bar += ['bye'] 7 | print foo 8 | -------------------------------------------------------------------------------- /code/2.7/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | nav_exclude: true 3 | --- 4 | 5 | 6 | ## 文件命名 7 | 章节_对应测试内容 8 | -------------------------------------------------------------------------------- /code/2.7/add.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int add_int(int, int); 4 | float add_float(float, float); 5 | 6 | int add_int(int num1, int num2) { 7 | return num1 + num2; 8 | } 9 | 10 | float add_float(float num1, float num2) { 11 | return num1 + num2; 12 | } 13 | -------------------------------------------------------------------------------- /code/2.7/adder.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | //This is the function that is called from your python code 4 | static PyObject* addList_add(PyObject* self, PyObject* args){ 5 | 6 | PyObject * listObj; 7 | 8 | //The input arguments come as a tuple, we parse the args to get the various variables 9 | //In this case it's only one list variable, which will now be referenced by listObj 10 | if (! PyArg_ParseTuple( args, "O", &listObj )) 11 | return NULL; 12 | 13 | //length of the list 14 | long length = PyList_Size(listObj); 15 | 16 | //iterate over all the elements 17 | int i, sum =0; 18 | for (i = 0; i < length; i++) { 19 | //get an element out of the list - the element is also a python objects 20 | PyObject* temp = PyList_GetItem(listObj, i); 21 | //we know that object represents an integer - so convert it into C long 22 | long elem = PyInt_AsLong(temp); 23 | sum += elem; 24 | } 25 | 26 | //value returned back to python code - another python object 27 | //build value here converts the C long to a python integer 28 | return Py_BuildValue("i", sum); 29 | 30 | } 31 | 32 | //This is the docstring that corresponds to our 'add' function. 33 | static char addList_docs[] = 34 | "add( ): add all elements of the list\n"; 35 | 36 | /* This table contains the relevant info mapping - 37 | , , 38 | , 39 | */ 40 | static PyMethodDef addList_funcs[] = { 41 | {"add", (PyCFunction)addList_add, METH_VARARGS, addList_docs}, 42 | {NULL, NULL, 0, NULL} 43 | 44 | }; 45 | 46 | /* 47 | addList is the module name, and this is the initialization block of the module. 48 | , , 49 | */ 50 | PyMODINIT_FUNC initaddList(void){ 51 | Py_InitModule3("addList", addList_funcs, 52 | "Add all ze lists"); 53 | 54 | } 55 | -------------------------------------------------------------------------------- /code/2.7/adder.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eastlakeside/interpy-zh/b056825749328659e0082798d20e83cc3f4814d5/code/2.7/adder.so -------------------------------------------------------------------------------- /code/2.7/build/lib.macosx-10.11-intel-2.7/addList.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eastlakeside/interpy-zh/b056825749328659e0082798d20e83cc3f4814d5/code/2.7/build/lib.macosx-10.11-intel-2.7/addList.so -------------------------------------------------------------------------------- /code/2.7/build/temp.macosx-10.11-intel-2.7/adder.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eastlakeside/interpy-zh/b056825749328659e0082798d20e83cc3f4814d5/code/2.7/build/temp.macosx-10.11-intel-2.7/adder.o -------------------------------------------------------------------------------- /code/2.7/example.c: -------------------------------------------------------------------------------- 1 | #include 2 | double My_variable = 3.0; 3 | 4 | int fact(int n) { 5 | if (n <= 1) return 1; 6 | else return n*fact(n-1); 7 | 8 | } 9 | 10 | int my_mod(int x, int y) { 11 | return (x%y); 12 | } 13 | 14 | char *get_time() 15 | { 16 | time_t ltime; 17 | time(<ime); 18 | return ctime(<ime); 19 | } 20 | -------------------------------------------------------------------------------- /code/2.7/setup.py: -------------------------------------------------------------------------------- 1 | from distutils.core import setup, Extension 2 | 3 | setup(name='addList', version='1.0', ext_modules=[Extension('addList', ['adder.c'])]) 4 | -------------------------------------------------------------------------------- /code/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | nav_exclude: true 3 | --- 4 | 5 | 示例代码说明 6 | ======= 7 | > 此目录中为可执行的纯代码,方便读者自动动手执行,加深理解,或者用于项目中 8 | 9 | ## Python版本兼容性 10 | - 书文中夹带的代码: 保证兼容Python 2/3,均可运行 11 | - 此处的代码: 为了更好地方便读者直接使用,做了专门针对2.7版本的代码,3+版本的代码暂未实现,欢迎PR -------------------------------------------------------------------------------- /conf.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | """ 4 | 这个文件是用于构建readthedocs的配置文件, 5 | 6 | ReadTheDocs的规范: 7 | 1. 从docs目录读取所有文档 (已经在根目录下 ln -s . docs了) 8 | 2. 使用根目录下的mkdocs.yml作为目录 9 | 10 | ReadTheDocs本地构建Gitbook的md文件: 11 | >>> pip install mkdocs 12 | >>> mkdocs build --clean --site-dir _build/html 13 | >>> mkdocs serve 14 | 15 | 福利: mkdocs构建好以后,可以直接publish到github pages 16 | (目前github pages的自动生成只能使用单页md, 而Jekell构建又引入新的节点,不方便) 17 | 18 | """ 19 | 20 | 21 | # 从本地读取到Summary.md, 然后转译成mkdocs.yml 22 | import os 23 | on_rtd = os.environ.get('READTHEDOCS', None) == 'True' 24 | if on_rtd: 25 | html_theme = 'default' 26 | else: 27 | html_theme = 'nature' 28 | -------------------------------------------------------------------------------- /config.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Python 进阶", 3 | "introduction":"《Python进阶》是《Intermediate Python》的中文译本, 谨以此献给进击的 Python 和 Python 程序员们!", 4 | "path": { 5 | "toc": "SUMMARY.md", 6 | "images": "__img" 7 | } 8 | } -------------------------------------------------------------------------------- /favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eastlakeside/interpy-zh/b056825749328659e0082798d20e83cc3f4814d5/favicon.ico -------------------------------------------------------------------------------- /mkdocs.yml: -------------------------------------------------------------------------------- 1 | site_name: Python进阶 2 | pages: 3 | - Home: readme.md 4 | - Summary: summary.md 5 | theme: readthedocs --------------------------------------------------------------------------------