diff --git a/README.md b/README.md
index 09c7213..8f1fdc2 100644
--- a/README.md
+++ b/README.md
@@ -1,163 +1,1153 @@
+# 从零到一的 Python 学习路线
+
+我在我的个人公众号(Python编程时光)分享过非常多的 Python 干货,由于公众号是个十分封闭的生态,读过之后,就没什么人会记住它了。不像网站那样有搜索引擎会给它们持续的曝光,历久弥香。
+
+我自认为在我公众号里,发布的文章质量是非常高的,为了不让这些干货沉入海底,我开了这个仓库,方便有需要的人进行索引,择需阅读。
+
+
+
+目前目录更新内容至 2021/3/20 发的文章。
+
+## 01. 基础系列
+
+### 1.1 基础必学
+
+1、[盘点 Python 高手都写不出来的几个错误](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485974&idx=1&sn=6a6a2fb8bc5c2acd300ebecdf625086c&chksm=e8866af4dff1e3e2c528594310475edaf2839d7b09048270acbb0dccc124581eaa65b123d393&scene=27#wechat_redirect)
+
+2、[Python基础|深入闭包与变量作用域](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485039&idx=1&sn=23557ac2640819b568a426b2db4df69c&scene=21#wechat_redirect)
+
+3、[Python基础|类方法的强制重写与禁止重写](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485037&idx=1&sn=8f4838b5bc919631c5cb642120b010c2&scene=21#wechat_redirect)
+
+4、[Python基础|多继承与Mixin设计模式](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485032&idx=1&sn=35e1c7014bc3f668cc4b48d9c318f1f9&scene=21#wechat_redirect)
+
+5、[Python基础|理解元组存在的意义](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485038&idx=1&sn=d0c7ab3fc20b299e4a0b9f6b414e4070&scene=21#wechat_redirect)
+
+6、[你知道 Python里的「单分派泛函数」?](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484938&idx=1&sn=061fdb00ccc499aa0cfc304852adb143&scene=21#wechat_redirect)
+
+7、[类型注解的福音,提高Python代码可读性](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484901&idx=1&sn=8f506cb46edb94dfb668525399f30f9e&scene=21#wechat_redirect)
+
+8、[写几个 Python 进阶必备函数](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484973&idx=1&sn=451d381fa3021e14b514cc15907ce0c3&scene=21#wechat_redirect)
+
+9、[Python 字符串连接,哪种的效率最高?](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485016&idx=1&sn=88497c09c8785b656ae1fee1406827dc&scene=21#wechat_redirect)
+
+10、[深入理解Python中的上下文管理器](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484950&idx=1&sn=9d78e469f8f190ef0484842b0391bec9&scene=21#wechat_redirect)
+
+11、[秒杀市面 90% 的 Python 入门教程 (上)](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484852&idx=1&sn=2362e034c2740d9f53c3cc99a6908cbd&scene=21#wechat_redirect)
+
+12、[秒杀市面 90% 的 Python 入门教程 (中)](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484856&idx=1&sn=881ee5d8154a85479d71ca4594f90142&scene=21#wechat_redirect)
+
+13、[秒杀市面 90% 的 Python 入门教程 (下)](https://mp.weixin.qq.com/s?__biz=MzU4OTUwMDE1Mw==&mid=2247485231&idx=1&sn=fa7146937f51110eb7356154bc2e2282&scene=21#wechat_redirect)
+
+14、[检验你 Python 基本功的 17 个骚操作](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484863&idx=2&sn=ff11a17ad82938b3a4f83ee5fbc2e497&scene=21#wechat_redirect)
+
+25、[和import说再见,这个库教你怎么偷懒](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485325&idx=2&sn=90c6dfbdbb36f63db72d1c0af67c1115&scene=21#wechat_redirect)
+
+16、[如何修改 CentOS 6.x 上默认Python 版本](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484881&idx=1&sn=ecb99878d3e2fca2bedc808b1e7aae9c&scene=21#wechat_redirect)
+
+17、[写 Python 时你要避免的十个错误](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484885&idx=1&sn=6acba3bf872fe1309308d9ef6cde53ce&scene=21#wechat_redirect)
+
+18、[看完这篇,你也是字符编码大神!](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247488475&idx=2&sn=17ca56435158c6c19134e30bda6e2b2d&chksm=e8867339dff1fa2f99cb0d85d99a2befa3689951778fbef8f2e0bad1042ae0c4813b99860c33&scene=27#wechat_redirect)
+
+19、[大白话解释什么是 Python Launcher?](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485268&idx=1&sn=9cea766f1e1a1f6278fca4665fc33a87&scene=21#wechat_redirect)
+
+20、[13条Python2.x和3.x的区别,你知道几条?](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485044&idx=1&sn=f38e2036317893be8388900dde3077e8&scene=21#wechat_redirect)
+
+21、[Python基础|新式类和经典类的区别?](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485033&idx=1&sn=8b6357d5a66f9a06347bf0922ae11946&scene=21#wechat_redirect)
+
+22、[Python 中有 3 个不可思议的返回功能](https://mp.weixin.qq.com/s/dSky88bOCPgYDprzJhnlbg)
+
+23、[太干了!一张图整理了 Python 所有内置异常](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247490040&idx=2&sn=59578093d82362c023ce305b2fc55103&chksm=e886791adff1f00c0a76154c7c7101d39b151891cc07b33dfe2523c95d9e38250c3684f674bb&scene=27#wechat_redirect)
+
+24、[有了这篇文章, Python 中的编码不再是噩梦](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247486174&idx=1&sn=ceb21deb2ff3ce750117c23f414416e3&chksm=e8866a3cdff1e32a1fcf07880803a7a3c350cc40d612bbd4f34b84cb06bf39250932f1ba5f05&scene=27#wechat_redirect)
+
+25、[ 别笑!Python 新手这五大坑你躲不过](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485573&idx=1&sn=362e7c5e73c27942e8250375257620e2&chksm=e8866867dff1e17140dc043b5dd33eec0e94e1616eab7e4b415bb767883d46d58a7243fb198f#rd)
+
+26、[Python 3.9 发布,字典的合并操作符终于来了](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485825&idx=1&sn=04075357936423097f692fa19857c3aa&chksm=e8866963dff1e075707b605fb1352f7760fb1b41040bdda4dec6b83370af37e009424d24f65e&token=2013245174&lang=zh_CN#rd)
+
+27、[Python 3 入门,看这篇就够了(超全整理)](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247492484&idx=1&sn=7985df89a647c271a9c99b1a25f87cb4&chksm=e8858366dff20a7062c54d79cfc85945d4ae91e96c0a0dcb8383d24680c6eeb80d68a6f2b718&scene=27#wechat_redirect)
+
+28、[掌握 Python 中下划线的 5 个潜规则](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247492133&idx=2&sn=ba4631b32f8fb4cd018a68b5411771b1&chksm=e88582c7dff20bd1c417c97e6798ba5bcacc90b55161976d7529d12f1850e413ede66110cbe3&scene=27#wechat_redirect)
+
+29、[还傻傻分不清什么是方法,什么是函数?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247491545&idx=2&sn=193bb88089cc7583989db44707aeca90&chksm=e8867f3bdff1f62d159dca793efc1c937b1c7a135b6f25ff9d77d8d1b819c7ddc66b3ba1f8d0&scene=27#wechat_redirect)
+
+30、[Python 如何像 awk一样分割字符串?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247491382&idx=2&sn=6e3824fa540fe7225d0cd1e7180559b6&chksm=e8867fd4dff1f6c2d2c024b6a07f616794c5b89a1b2b08ab05d029e5de1a59ff751bb03a6561&scene=27#wechat_redirect)
+
+31、[一篇文章带你剖析Python 字节流处理神器struct](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247491241&idx=2&sn=889bf712f4115a27f321e791b3862405&chksm=e8867e4bdff1f75dc9e1e2374c3302753c3a6a28b42436e6e2550aa8ac528652b2561ad2ca48&scene=27#wechat_redirect)
+
+32、[OrderedDict 是如何保证 Key 的插入顺序的?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247486136&idx=2&sn=81fd4039611666917ae8801bd73fe921&chksm=e8866a5adff1e34c603500e97c4b7e1cecb7a1fb43e01c6ff5a794d526d54e5a02b35240d2db&scene=27#wechat_redirect)
+
+33、[字典访问不存在的key 时,如何才能不报错?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247490809&idx=2&sn=a256a0d8b86bae3a7dc65f7a88e4ab39&chksm=e8867c1bdff1f50d1da0e11d2dca288b085064062ecb8f68891b0bd48b8a1fca4f8ca594fc22&scene=27#wechat_redirect)
+
+34、[如何使用 Python 执行 js 代码?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247491161&idx=2&sn=d89bbc4c214563807427703aa584a26d&chksm=e8867ebbdff1f7ad301b58ac567faec95be2d8f5be921b23ea5d3722d377c2926f32ff7b68ac&scene=27#wechat_redirect)
+
+35、[Python 代码覆盖率工具 - Coverage](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247489752&idx=2&sn=b115d9377feb0129985295e5a3c8ec5e&chksm=e886783adff1f12c7bb14c55560f06f6a5327d6beee9bba38b9965784baf197918ae3898e95d&scene=27#wechat_redirect)
+
+36、[超赞!100 道让你练习 Python 基础的题目](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247488558&idx=1&sn=58a53b85a7e49989794d8d0e9885f335&chksm=e88674ccdff1fdda61609dc4aa811dc6a52a819fe56da3bd0480f9f21ae248c94d8f8f3f8798&scene=27#wechat_redirect)
+
+37、[一道 3 行代码的 Python面试题,我懵逼了一天](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247486901&idx=1&sn=73fe3a92ac2639706d9573a0eaffd48f&chksm=e8866d57dff1e441d6ffad3c4f00b053db39df1cec4e6cf69992641b359cc96448f7f3068918&scene=27#wechat_redirect)
+
+38、[学习 Python 的指南大纲,从基础到核心知识全都有](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247486246&idx=1&sn=58665e2be95f71415ac3e2c1c5f4f690&chksm=e8866bc4dff1e2d212a4d93c0c83cd39cd534f64a8e113b208f96a6156e75ef652ee1ac32270&scene=27#wechat_redirect)
+
+39、[Python 中常见的配置文件写法](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247496552&idx=2&sn=cc57aee69875c855fcc7e7d4098dda44&chksm=e885938adff21a9cb0cd1ee105af9001ca2a597ae37fba35c76de13eb2a69033133a866950d6&scene=27#wechat_redirect)
+
+40、[Python 的 \__name__ 变量,到底是个什么东西?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247496153&idx=3&sn=44fbf9d2c5bf1c629dab6abc1f429a1b&chksm=e885913bdff2182d69117d5f85ede8eed726f1bf00192cb270dc4dfd6e20a1e51638c6520bee&scene=27#wechat_redirect)
+
+### 1.2 基础库
+
+1、[Python时间模块,超实用总结!](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247493317&idx=2&sn=b821b2765cfcdf7da8858b962f092980&chksm=e8858627dff20f31b14090d0ff178002ea0544196d8d63e595f5f6787f8e95d60df292f80bb4&scene=27#wechat_redirect)
+
+2、[用 Python 玩转正则表达式,这篇讲得太好了](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247493207&idx=2&sn=ca2c44f056ce22d132089d200951ee6d&chksm=e88586b5dff20fa3db38273731c3521a278c09ff81570d01512f203d280d253ae1f3d5fc1799&scene=27#wechat_redirect)
+
+3、[打基础一定要吃透这12类 Python 内置函数](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247486198&idx=1&sn=efea0e57956188eeafb163d7bb2448c4&chksm=e8866a14dff1e3022f361245b182a30fbbf40550f3e8dc4456e1acf260aa73d35a20c010559d&scene=27#wechat_redirect)
+
+4、[8个超好用的Python内置函数,提升效率必备!](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485943&idx=2&sn=ecb57c8dbf0e4965d4842cc6f614da64&chksm=e8866915dff1e0035ded3974097c1f0e00561962eeb0498c633fdf664cd429749aa6e6eca806&scene=27#wechat_redirect)
+
+5、[用 Python 操作 Redis,看这一篇就够了](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247492907&idx=2&sn=f44d9ef0e624b150c080dd075974e540&chksm=e88585c9dff20cdf6ed8b913312602838fbdffcade584412e43326d11f0a1cc41bd4ffbce94c&scene=27#wechat_redirect)
+
+6、[超全!我把 Python 的 200个标准库整理出来了](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247492755&idx=1&sn=c0f99bb14839677d228d029a564d457c&chksm=e8858471dff20d67bc162863a6193998f39aae3e1103a5b3015ceb79f4cea1dde4fda5d2cff3&scene=27#wechat_redirect)
+
+7、[一篇文章掌握 Python 内置 zip() 的全部内容](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247491227&idx=2&sn=61efa9b45c112a25be348a94a9ac8151&chksm=e8867e79dff1f76fcc3858afdfd4e68aef0a5d0430f0420aacd2045aac86d3c31ff88bac464d&scene=27#wechat_redirect)
+
+8、[通过“四不要”,掌握 Python 的 Lambda 函数](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247493501&idx=2&sn=696cb2ef4c3fcc6713f148d42f6eb0be&chksm=e885879fdff20e897b71e791caa6a6dbfb783c22a3ea7bf9c4cfb8406b668e57914c1c209707#rd)
+
+9、[原来 collections 这么好用!!](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247493894&idx=2&sn=3f16362bfa3ec2ecf9f7318cc50e2f95&chksm=e88589e4dff200f25c4c03294f59716431c57a65161f6c364d610950650b24a7cb4b037345e4#rd)
+
+10、[使用 Python 打印漂亮的表格,这两项基本功你可会?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247494135&idx=2&sn=f055f78da070dc71c2593540f3272988&chksm=e8858915dff20003c05bc328b09a8cdb8dda3e37118201fb2eb317c1296e788aa1f50c8edf45&scene=27#wechat_redirect)
+
+11、[73个f-string的例子,帮你吃透字符串格式化](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247497032&idx=2&sn=dc4761acf90fa7ff71833f0e7f952189&chksm=e88595aadff21cbca930dfa432106b6645b01ae1485d09f1d860a5779311b1d9c1bf23123146&scene=27#wechat_redirect)
+
+12、[一学就会的 Python 时间转化总结(超全)](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247496446&idx=1&sn=98c71ceefb10fb049ec33c3c1aa2bc6c&chksm=e885921cdff21b0adf359a9b75e5a44033a6cfa86a05cb6ed9e19f07c6b73b6cf97c4e3cd3c1&scene=27#wechat_redirect)
+
+13、[适合新手的 SQLAlchemy 上手教程](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247498470&idx=2&sn=d0ee4a5367ad5195bcb8377ea6fc6101&chksm=e8859a04dff21312e8172de1e1986d41b4cf60d653d923954f48e111258a0e6019fc1bb50c1a&scene=27#wechat_redirect)
+
+14、[不服不行,Python 操作 JSON 的门道也这么多~](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247498220&idx=1&sn=249796b68dadafc9f85600fb03f8b0ae&chksm=e885990edff21018ad853a24abcd4a7c6780cc1740daddcd622bb233cdaa6d4834468284ac96&scene=27#wechat_redirect)
+
+15、[没想到吧?这货比 open 更适合读取文件](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247498035&idx=1&sn=66e076de5448b41f252127b1f12a6d7e&chksm=e88599d1dff210c7f6f470b7917cf6b099febc93af25740562b2e2e0ec1ea42968128d26186b&scene=27#wechat_redirect)
+
+16、[Python输出简洁美观的文本化表格](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247497768&idx=2&sn=2c49bcdf0264481f75658db0c0dbc54c&chksm=e88598cadff211dc7f140bc8c0d59d88169fde365567c82bcd8f7a8dccb4081072da83d88df0&scene=27#wechat_redirect)
+
+17、[使用 Python 操作 MySQL,这篇文章别错过~](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247497264&idx=2&sn=b11ff1be91e3297575d9eb9de3465257&chksm=e88596d2dff21fc4e404e0df21d096426df4eae33d6143bd851244cbf00adc3dc9072e9ac1a6&scene=27#wechat_redirect)
+
+18、[一篇文章教你如何用 Python 记录日志](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247497049&idx=2&sn=0bb3de6705dd1dcef5352c2ff37991d5&chksm=e88595bbdff21cad17c69ee31955572ff64fed9ea38a8ef0a63472e89c97bc027d89101b8b57&scene=27#wechat_redirect)
+
+### 1.3 代码案例
+
+1、[15个Pythonic的代码示例](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485036&idx=1&sn=24de1996a63bf25b0c0deec782f688cf&scene=21#wechat_redirect)
+
+2、[常见 Python 简洁代码的样例](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484823&idx=2&sn=4459a7edc4a4989068b22b31c76f2c92&scene=21#wechat_redirect)
+
+3、[每天花 30 秒,就可以练习的 Python 小技巧!](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484861&idx=1&sn=e5fa97570b3c180e5a5edc753fc62669&scene=21#wechat_redirect)
+
+4、[精心整理!9个 Python 实用案例分享](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247492921&idx=2&sn=800c245cf392dfed915c618c68a5fe80&chksm=e88585dbdff20ccdd85dc21989092ab3e028dd2e3bcfb8f349bc3a7df99b299b6f8a43b57371&scene=27#wechat_redirect)
+
+5、[瞧瞧,这样的代码才叫 Pythonic](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247493439&idx=1&sn=2ed198c261ee285f2e3c1cbdf5c78541&chksm=e88587dddff20ecb24804e6b191b690d7950584104b5ed94b2f1e6a043a24909dfe418e64df6#rd)
+
+6、[瞧瞧,这样的『函数』才叫 Pythonic](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247493550&idx=1&sn=cd5cf944c3e3ac3f2b567def827de2d4&chksm=e885874cdff20e5aeb438aab72e2f87329a403024bb0cae3308e61d5f9cc9a15609da6d8b7dd#rd)
+
+7、[这样的奇技淫巧不可取,切记切记](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247493635&idx=2&sn=cc61ae5798c278c52a07e3e7129a5bb1&chksm=e88588e1dff201f7c4997d9fe8004d86973833bbe91ded31fbf93cdf00d1ec22f19f8cff825c&token=58703854&lang=zh_CN#rd)
+
+8、[别这样直接运行Python命令,否则电脑等于“裸奔”](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247494114&idx=1&sn=202d3492ae48d73431173b76caa64345&chksm=e8858900dff2001626f47454f8dee79679f43b74701c7730e1c499f4c895edafca5aa68e280f&scene=27#wechat_redirect)
+
+9、[6 个例子教你重构 Python 代码](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247494309&idx=2&sn=e1cffffa71b5cc76434b6771cfcc0bae&chksm=e8858a47dff20351575b1cd29e35a51b30c7859c9dffbf816df883bc7ccee9ea15aa5eaad812&scene=27#wechat_redirect)
+
+10、[25 条很棒的 Python 一行代码](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247496971&idx=2&sn=9196437b9976a382b277c7780f8aa04b&chksm=e88595e9dff21cffc32c8da9d2a5d59ab40d7e1d1d32f9193687c35cba490919c624d04d7ef6&scene=27#wechat_redirect)
+
+11、[Python 中的 EAFP 和 LBYL 代码风格到底是什么?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247496204&idx=2&sn=5aa0cca75c6b59bd5f1b6f9cd1bfe07e&chksm=e88592eedff21bf857a0d53a1c3515e5e4ad32c5ae728218b6dedf63fcd02a9750a9f66387d4&scene=27#wechat_redirect)
+
+12、[3000 字教你学会最地道的 Python 编程风格](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247494858&idx=1&sn=9386de992ce9f9b08d2d6d2721584a39&chksm=e8858c28dff2053ec9ca59d1d4244c119ca4363b6914e87b3ef250a54bb8a82c611ab437763e&scene=27#wechat_redirect)
+
+13、[再来 6 个例子教你重构 Python 代码](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247494838&idx=2&sn=eeaf56eeaf9cc72f1340233bcd88b5a7&chksm=e8858c54dff20542f4712be4bd185f2e27e78e09bb77acb97e2be2d4c1329553be4abe25a3e2&scene=27#wechat_redirect)
+
+14、[如何在Python里面实现链式调用?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247498592&idx=2&sn=19b2f757b9887a67b0d2cfb8b0cb924e&chksm=e8859b82dff21294793e2b9a4eac9c375509796044d964aeb24c0448f9ec16b4cf3cad0102a9&scene=27#wechat_redirect)
+
+15、[Python 怎么捕获警告?(注意:不是捕获异常)](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247498517&idx=1&sn=2540f928893ffecc88a7aa86c330de91&chksm=e8859bf7dff212e1d8425ccff1c2dd043b5d611b291c4a5db729d7416189571d99e2c8d4fdee&scene=27#wechat_redirect)
+
+16、[a is b 为 True,a == b 一定为 True 吗?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247497362&idx=1&sn=993ad8a3f86d467db967ea13d52175cc&chksm=e8859670dff21f66c76d2c4e0c61db9adda56824eba878be40e8b4ed8409c6922c8526f818fc&scene=27#wechat_redirect)
+
+## 02. 进阶系列
+
+### 2.1 进阶必学
+
+1、[描述符:其实你不懂我(一)](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484934&idx=1&sn=ef8b30ffe2467e5736036bd083b340ca&scene=21#wechat_redirect)
+
+2、[描述符:我无处不在!(二)](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484927&idx=1&sn=8a3d673ee20bd418d93a249380ca8e76&scene=21#wechat_redirect)
+
+3、[Python静态方法其实暗藏玄机](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484911&idx=1&sn=a16846030c3589c912aec9efdf4f7f80&scene=21#wechat_redirect)
+
+4、[全面深入理解 Python 面向对象](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485208&idx=1&sn=61bf8d3ec81fa85b0bc9de6e5f5d6611&scene=21#wechat_redirect)
+
+5、[几个使用装饰器的小技巧](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484930&idx=1&sn=ed731e32b6e95e7d83d74a544f97142a&scene=21#wechat_redirect)
+
+6、[围观大神是如何用 Python 处理文件的?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247497532&idx=1&sn=bed756631d9c94c2eb2d57e0b29f6ab6&chksm=e88597dedff21ec899e90126cbe219be5fc738229bf69a02c490435c4ed57cbe32636198bb44&scene=27#wechat_redirect)
+
+7、[Python进阶开发|元类编程](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485080&idx=1&sn=45e6d9995e4469d8b7bce2f800ac0f9b&scene=21#wechat_redirect)
+
+8、[Python进阶开发之网络编程](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485078&idx=1&sn=e0a3855d959c178b8c01bd196a048dce&scene=21#wechat_redirect)
+
+9、[Python 进阶:深入 GIL (上篇)](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484875&idx=2&sn=8780a349c60522ad1b7aa20a3fe7a19c&scene=21#wechat_redirect)
+
+10、[没掌握好这24条,别说Python慢。](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484910&idx=1&sn=df8fb32a840a28954614b09bce730b72&scene=21#wechat_redirect)
+
+11、[花了两个星期,我终于把WSGI整明白了](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484919&idx=1&sn=bd7d2bc0ab8a41110d5d93e44ad20b1f&scene=21#wechat_redirect)
+
+12、[源码解读|Flask 上下文核心机制](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484943&idx=1&sn=ca846404d30a2ec775ab7db5df73aa8e&scene=21#wechat_redirect)
+
+13、[说说几个 Python 内存分配时的小秘密](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484853&idx=2&sn=edf7c3225d119291fae5d05820f55aac&scene=21#wechat_redirect)
+
+14、[写了三年代码,还是不懂 Python 世界的规则](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484886&idx=1&sn=f46917f6e23f8961c912606a281a354d&scene=21#wechat_redirect)
+
+15、[如何保护你写的 Python 代码?](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484858&idx=1&sn=e18a1c89f14d4e4043833e3ff5cc8d32&scene=21#wechat_redirect)
+
+16、[27 个问题,告诉你 Python 为什么如此设计?](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484862&idx=1&sn=89500433bc174dfc0530eebea2fb91d1&scene=21#wechat_redirect)
+
+17、[精心整理 30 个Python代码实现的常用功能](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485147&idx=2&sn=c5b871e1cebde6b311609f6de703f2e3&scene=21#wechat_redirect)
+
+18、[高手之路:从零开始打造一个Web服务器](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484891&idx=1&sn=ed12d175452c2efedeefb1635063177c&scene=21#wechat_redirect)
+
+19、[从0到1:全面理解 RPC 远程调用](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484915&idx=1&sn=427b9dda155e4201c2f939c3919def91&scene=21#wechat_redirect)
+
+20、[一篇 Python 函数式编程指南](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484834&idx=1&sn=559bac4ff85f7082bc989a6fde04d3f3&scene=21#wechat_redirect)
+
+21、[没看完这11 条,别说你精通 Python 装饰器](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484900&idx=1&sn=3997a2a377577e3d16a9b7f8e6a5ea53&scene=21#wechat_redirect)
+
+22、[程序卡住了?教你如何调试已在运行的程序](https://mp.weixin.qq.com/s/QC-Pc-0iifaVKOfsiNTYmA)
+
+23、[字符串在Python内部是如何省内存的](https://mp.weixin.qq.com/s/jp_I82fnr0-3cd6ioZcoGQ)
+
+24、[巧用 traceback 定位 Python 内存泄漏](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485751&idx=1&sn=8a054a575767c103c830a6b3ef5f73c8&chksm=e88669d5dff1e0c3ae1c7c32f35a8f46a3a2e0a637fb4b947566f417dd8f4419bec636c8a667#rd)
+
+25、[非常全的通俗易懂 Python 魔法方法指南(上)](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485551&idx=1&sn=4c0983f22269a113bcdf83690e5e2b20&chksm=e886688ddff1e19b9ad230128a67ee1a9ee1eced0720c14b5d48f68943be10b1b85b23d8ca2d#rd)
+
+26、[非常全的通俗易懂 Python 魔法方法指南(下)](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485555&idx=1&sn=0a218b796e651b451a17112e22790d07&chksm=e8866891dff1e18771a9392da7f509732244ebc4d1a6e2427acd39ee8b59b146e3d4961a2a62#rd)
+
+27、[教你如何阅读 Python 开源项目代码](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485451&idx=1&sn=e15856352c18297770c67d9df66ec3b0&chksm=e88668e9dff1e1ff0f6a39f3885f4126232213149ac09daa1fc0ec9e0de2095bd11fb727d63a#rd)
+
+28、[这个 Python 知识点,90% 的人都得挂~](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247490984&idx=1&sn=135b7b187d8d41b3c10179dbaedaf77e&chksm=e8867d4adff1f45cd90fe2609fe7d1840b07fab8cf19cb7406ebd038c31df0028e611077d80e&scene=27#wechat_redirect)
+
+29、[一篇长文学懂 PyTorch](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247489158&idx=1&sn=2e2e88565131d11271aea61b0bedf182&chksm=e8867664dff1ff728ee30e1baab0d7df0139fc67e2514ffcaebda6d50e5d08bd2866ad7dee35&scene=27#wechat_redirect)
+
+30、[学了这么久,你知道Python是如何运作的吗?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247488458&idx=1&sn=23040c906e83462c4f64039cba6e3dfb&chksm=e8867328dff1fa3e149248e53762444b1abc72df395a597639357b5525d16b1b6db242339d20&scene=27#wechat_redirect)
+
+31、[求你了,别再使用 pprint 打印字典了](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247486288&idx=1&sn=7647e1ee63c49c14472baec19fb79b87&chksm=e8866bb2dff1e2a4d5b85ae9db1e8f4d8dda839643fb02b9e0b7f7fe6143cb627773e56d44bf&scene=27#wechat_redirect)
+
+32、[Python实现RabbitMQ中6种消息模型](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247486017&idx=1&sn=9b95896abee7a345e3abee1ac4f2edab&chksm=e8866aa3dff1e3b547ef5062c30516b2d54a051fd4a07d85078223ea673af1400be1a6c56ad5&scene=27#wechat_redirect)
+
+33、[想写好面向对象的代码,这几篇一定要看(下)](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485938&idx=1&sn=a725785ef8000868b7cb85606ee754d3&chksm=e8866910dff1e006f195d6ea0d66f4b4f1fe5a9b97debc18ae4b04a239a347d342fbc67b44e4&scene=27#wechat_redirect)
+
+34、[想写好面向对象的代码,这几篇一定要看(中)](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485926&idx=1&sn=ce148af333d9f377d8a6fb9577bbf57c&chksm=e8866904dff1e012ec1bb8f418c43cca7954979bd1481707761b29d1e632ace6369efd50210b&scene=27#wechat_redirect)
+
+35、[想写好面向对象的代码,这几篇一定要看(上)](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485913&idx=1&sn=74b34bd95d513a94201ad05604c4a05f&chksm=e886693bdff1e02dd2079cb90458aac914e5cb832aec59677da8b2f85200baaa57e804a4b58e&scene=27#wechat_redirect)
+
+36、[教你 10 分钟构建一套 RESTful API 服务](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247488534&idx=2&sn=d593dbf907217ff4dea1daf636062778&chksm=e88674f4dff1fde26b1a1074a0a0c78be0630a34b4aed705755946bd8cfcde1a23e0df916392&scene=27#wechat_redirect)
+
+37、[要搞懂元类(metaclasses),这篇文章一定要看](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247493280&idx=2&sn=d1a0aa45b9ecd03bd940328bc031458c&chksm=e8858642dff20f54ecbe8d03aebe2dacdc1fe0fdb1daa3855e09f565f05b711c9aa4eabd9ddb&scene=27#wechat_redirect)
+
+38、[弄懂这 6 个问题,拿下 Python 生成器!](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247493273&idx=2&sn=89bd3d18e7837f59c41d2f021047a1f9&chksm=e885867bdff20f6d0d72d463a255d2a51225321f8bc6115523411e540305e669a24580e58d78&scene=27#wechat_redirect)
+
+39、[`[]` 与 list() 哪个快?为什么快?快多少呢?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247493457&idx=2&sn=67d576d2aeab16d4ad9c1b491e10deaf&chksm=e88587b3dff20ea501789da4c67560e21708f89032732eaca544e22c65780ea249e98e6ab50e#rd)
+
+40、[连 Python 生成器的原理都解释不了,还敢说 Python 用了 5 年?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247493530&idx=1&sn=5096c680a63a57b1f55d1a9d372ec302&chksm=e8858778dff20e6e257fcacfb644bc4736afac692ab19f41717f4cdc084eb43951aba0912187#rd)
+
+41、[Python 列表去重的4种方式及性能对比](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247493746&idx=2&sn=218b1a28af72347363557bdaac32d016&chksm=e8858890dff20186a8fefe81e11a1aa64e787a75b3930ecb6e4421e132a8322dbc5fe3b81b30#rd)
+
+42、[第一次把 Python 的切片理解得如此透彻](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247493987&idx=2&sn=ca90bbc53a31c0ccc84fea4869fd1b81&chksm=e8858981dff20097a5167ef9d60fe4c54479b6f50b85188ffc43490c03b8cb2bec8464975793#rd)
+
+43、[为什么继承 Python 内置类型会出问题?!](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247494268&idx=2&sn=67ea6f78e7add1d43174f601626d1cb5&chksm=e8858a9edff20388c08f6e3f9c8ce9db6969174fc3674bf04d65cf0fbcda48c08217de5d622b&scene=27#wechat_redirect)
+
+44、[Python最会变魔术的魔术方法,竟能"大变活人"](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247496126&idx=2&sn=083eafff3facad62a0660061990c7387&chksm=e885915cdff2184aa79584c1e46d3b40bc928eeb732a856e9ff26e7864a3fb1fb81749d73364&scene=27#wechat_redirect)
+
+45、[说说 Python 的内置电池,你学过吗?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247495322&idx=2&sn=7271490d820c6a48b9ee7d090c1c88bd&chksm=e8858e78dff2076e4b5651811652d2da47d2a1db07eda9362120b51f83da7b65aca58d1ad3a6&scene=27#wechat_redirect)
+
+46、[恶补了 Python 装饰器的六种写法,你随便问~](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247494951&idx=1&sn=43b65fade87940be918ff1ff066b9ca8&chksm=e8858dc5dff204d3571414164e63a1de715499418805477d012104fa12e6a2577ef5fa58e6a8&scene=27#wechat_redirect)
+
+47、[Python 从业十年的程序员,写的万字经验分享](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247494942&idx=1&sn=84efbced575838b9b8e169e8c59383b7&chksm=e8858dfcdff204eaf7d251ddf1ea69655bd5eb69da596a4970c845f912c2ac9e3f2f18e5e7b5&scene=27#wechat_redirect)
+
+48、[Python 优化机制 "常量折叠" 是究竟是怎么回事?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247497951&idx=2&sn=e54c8412c056e98a34a4ce622915221d&chksm=e885983ddff2112b359c3e2a3856357a3fcccc24e5825fa0a6b972d0f5c64662df363c8d705e&scene=27#wechat_redirect)
+
+
+### 2.2 包的管理
+
+1、[最全的 pip 使用指南,50% 你可能没用过](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484827&idx=1&sn=df0923856c820e10baca20c9873b336b&scene=21#wechat_redirect)
+
+2、[花了两天,终于把 Python 的 setup.py 给整明白了](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247497217&idx=1&sn=acce69f1f39f2688056fd72a9d7044bf&chksm=e88596e3dff21ff5bf1c0e6f8a26e218458aa079d987810811f2d1ebd3704cdb1f35881229c8&scene=27#wechat_redirect)
+
+3、[深入探讨 Python 的import机制:实现远程导入模块](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484838&idx=1&sn=1e6fbf5d7546902c6965c60383f7b639&scene=21#wechat_redirect)
+
+4、[盘点 Python 依赖库管理的工具:pip、pipreqs、pigar、pip-tools、pipdeptree](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485120&idx=1&sn=170ef1173eabc5bed28c724e19d6c0ac&scene=21#wechat_redirect)
+
+5、[如何使用 pyenv 运行Python的多个版本?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247486239&idx=2&sn=863e80589f5eb9b2daa9701f6b494997&chksm=e8866bfddff1e2ebc244354238aedfa9291aa27ad8f7e466213236369dae9800b29b5766c0cf&scene=27#wechat_redirect)
+
+6、[如何管理 Python 的虚拟环境?超全讲解virtualenv的使用](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485049&idx=1&sn=c16383d6cc91a7ed8254e344d994f101&scene=21#wechat_redirect)
+
+7、[一款让Python开发效率提升50%的工具包](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247489006&idx=1&sn=eead45b3c34777085465f9854a433e36&chksm=e886750cdff1fc1ae2791adba1231e045630417f9b4185217817183aa1f260815acabcd39363&scene=27#wechat_redirect)
+
+8、[记 Python “用户环境”的一次完美应用](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247486191&idx=1&sn=e556df305ce1df4c4969d2fdbe884cfa&chksm=e8866a0ddff1e31b2ca8ca8fbbe0355fb8c24376d715224ef2176e613b5601764be0193e3aa4&scene=27#wechat_redirect)
+
+9、[学了半天,import 到底在干啥?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247493413&idx=2&sn=770c9bb7104dfb8c53c544f546cf7aa4&chksm=e88587c7dff20ed143cb93b4cd1b0f64af8b9c849fd9d531e58a9ba27a449d4a28cd96091b40&scene=27#wechat_redirect)
+
+10、[非常干货:Python 探针实现原理](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247493427&idx=1&sn=d9e0c646a98cf2b54b5724ebc3262255&chksm=e88587d1dff20ec71ed33c5165bbdec377eea89307da0f924784cff0f4c01c05a5f3247de50e&token=58703854&lang=zh_CN#rd)
+
+11、[如何编码检查依赖关系是否有循环依赖](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247496866&idx=2&sn=a25b46ef4aa67a007612ef53bc832811&chksm=e8859440dff21d5647027fdcdad642c3f9781f140f7ecb47f975678eb74b8321d4b65ec076cc&scene=27#wechat_redirect)
+
+12、[关于包导入,这三个知识点太多人不知道了](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247496204&idx=1&sn=cf788dfc35b7377ed70369aafba19c47&chksm=e88592eedff21bf872d8b6c5ded642ea5e2f2f55dcd31331da9bcd13f265cde7fee2f65ca8f5&scene=27#wechat_redirect)
+
+13、[手把手教你发布 Python 项目开源包](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247494964&idx=2&sn=cac94af61d53857c2fa7362f2b4f8564&chksm=e8858dd6dff204c02b5235eb0a705e9193d3c5107369a87d35753bcb2e7f80020ceaccfbb9e9&scene=27#wechat_redirect)
+
+14、[有人在代码里下毒!慎用 pip install 命令](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247494847&idx=1&sn=822e027acb42d6ab882687fc515f14ae&chksm=e8858c5ddff2054b80c6eda85f27331472ad78bcc11ca980cf03cae0824255666b0518afb2cc&scene=27#wechat_redirect)
+
+15、[简化 Python 函数调用的 3 种技巧](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247494847&idx=2&sn=bec329103690975870e79fc00abdeba7&chksm=e8858c5ddff2054bdbddfb145caf763aab5130c3939d39a737cb3bd42934f7a74b8d3fb0e06d&scene=27#wechat_redirect)
+
+16、[如何 Import 自定义的 Python 模块?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247494821&idx=2&sn=646396ea0a67ab3eed7b0f0652e647dd&chksm=e8858c47dff2055109d74b13de70efe7a5083fd1a99a1323043e932694a572291d00359bec15&scene=27#wechat_redirect)
+
+17、[解锁装包新姿势,这个场景下 pip 真的难用~](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247494820&idx=1&sn=24ceaa11376cbfa5a4d48adc076603db&chksm=e8858c46dff20550d0124ef1bf1e481e14ffcf485e8c49aa77f6f9541f6163850538e4af05f9&scene=27#wechat_redirect)
+
+18、[Python 的 import 居然这么有料](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247497490&idx=1&sn=8667466d2dcaee300e990b0d4462e485&chksm=e88597f0dff21ee6787df0506a0c4b354df6b960c2c8dbf349ead6e43557230ae81fa0c661d7&scene=27#wechat_redirect)
+
+### 2.3 性能优化
+
+1、[Python高效代码实践:性能、内存和可用性](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485161&idx=1&sn=07625f06db330897bc8bfee880ceac18&scene=21#wechat_redirect)
+
+2、[Python 代码的性能优化之道](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485004&idx=1&sn=8dc07a40bcac30c93874398b17a52831&scene=21#wechat_redirect)
+
+3、[7 个习惯帮你提升Python运行性能](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484957&idx=1&sn=9b3c644e6b1777b77ce7f84047c6d500&scene=21#wechat_redirect)
+
+4、[如何提升你的 Python 代码健壮性(上)](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484871&idx=1&sn=985b79143d0cafd21e5263ee79afa777&scene=21#wechat_redirect)
+
+5、[如何提升你的 Python 代码健壮性(下)](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484867&idx=1&sn=b8eab8416229dc74f1ac96e099a3df71&scene=21#wechat_redirect)
+
+6、[将 Python 运行速度提升 30 倍的神技巧](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484877&idx=1&sn=f6067875f1e807d19291e383f8421a3b&scene=21#wechat_redirect)
+
+7、[实战讲解:Python 性能分析与优化实践](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485404&idx=1&sn=486fcf898d6dd21b0feb990de3c1e08f&chksm=e886673edff1ee28b8c6e1d520b2732b2f66f4c4691ef2696b54743b1ad0769356e12a1e98b2#rd)
+
+8、[如何调试Python 程序的内存泄露问题](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247488577&idx=2&sn=16aff9687a4a16faa492118cd5c1bfae&chksm=e88674a3dff1fdb5b80a4547cb863fcf4255f90ab2024ae8c3bbf1f270efb13333b2f7426baf&scene=27#wechat_redirect)
+
+9、[牛逼!一行代码让 pandas 的 apply 速度飙到极致!](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247493908&idx=2&sn=0413803e1a73bf632c2d5ba24c6e7ce4&chksm=e88589f6dff200e020a32578706e7c4791be700ce9f16f810d5b19dcc6a99bce2d5c7ba96b6d#rd)
+
+10、[3 倍性能提升!升级 Flask 到 Quart](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247494567&idx=1&sn=d91202b10ab957a58c5eb70fa159fe67&chksm=e8858b45dff2025387626bac42e8441e8d0c8c290070f5fc1a885776fb6a3a06ce3467b1b8d2&scene=27#wechat_redirect)
+
+11、[超干分享!如何提高Python的运行速度?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247494621&idx=2&sn=00c8aa00a85f8710fada38d35b2c8388&chksm=e8858b3fdff2022991a0024757c64faaed81da830cbbf1fa62b6b218d7271b0837b11922c1f0&scene=27#wechat_redirect)
+
+12、[程序运行慢?你怕是写的假 Python](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247495235&idx=1&sn=816c6de9e1d23d65fe8c1db458426f68&chksm=e8858ea1dff207b749b64461bb06bf8818883644a4450b6400efda4d7e54d4832162448e9f68&scene=27#wechat_redirect)
+
+13、[超干分享!如何提高Python的运行速度?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247494621&idx=2&sn=00c8aa00a85f8710fada38d35b2c8388&chksm=e8858b3fdff2022991a0024757c64faaed81da830cbbf1fa62b6b218d7271b0837b11922c1f0&scene=27#wechat_redirect)
+
+14、[快亦有道!让 Python 变快的 5个方案](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247497409&idx=2&sn=12667ea2e03ecb9db03478b17778ffea&chksm=e8859623dff21f358e56375becfa2ea8873235c2f25a216c36ffbd7a76ce2f13c7cbc269d58d&scene=27#wechat_redirect)
+
+### 2.4 并发编程
+
+1、[并发编程01|从性能角度来初探并发编程](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485074&idx=1&sn=a859c6ab1d9b95c30c9f8b06f9489887&scene=21#wechat_redirect)
+
+2、[并发编程02|创建多线程的几种方法](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485073&idx=1&sn=854ff524645247e5020d977e57d9c0e6&scene=21#wechat_redirect)
+
+3、[并发编程03|谈谈线程中的“锁机制”](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485069&idx=1&sn=52370a27d4a5c4541969921ada890e0b&scene=21#wechat_redirect)
+
+4、[并发编程04|消息通信机制/任务协调](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485068&idx=1&sn=fc0798e84e7845cd9efee1809f932f15&scene=21#wechat_redirect)
+
+5、[并发编程05|线程中的信息隔离](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485066&idx=1&sn=0bb9d6c82a6d062e50d09e1eefd09427&scene=21#wechat_redirect)
+
+6、[并发编程06|如何创建线程池](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485065&idx=1&sn=60891b67b139806cf6bf4c05f5861f02&scene=21#wechat_redirect)
+
+7、[并发编程07|从生成器使用入门协程](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485064&idx=1&sn=8584bb778152a12ca335970bed9fdbdc&scene=21#wechat_redirect)
+
+8、[并发编程08|深入理解yield from语法](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485063&idx=1&sn=0ff7a99058320ff90a6237e7e03367fb&scene=21#wechat_redirect)
+
+9、[并发编程09|初识异步IO框架:asyncio](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485061&idx=1&sn=9e846df1a5cb57e0bc6254dcc953e243&scene=21#wechat_redirect)
+
+10、[并发编程12|学习异步IO框架:asyncio](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485058&idx=1&sn=92ef1f79ce6488670ae13e5d6a1c7908&scene=21#wechat_redirect)
+
+11、[并发编程11|实战异步IO框架:asyncio](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485057&idx=1&sn=8bacf0f2b42de5962fbdf32796903f27&scene=21#wechat_redirect)
+
+12、[百万「并发」之Python异步编程(上篇)](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484896&idx=2&sn=5d8458ede3440ae501d19c5ffd5f8a99&scene=21#wechat_redirect)
+
+13、[百万「并发」之Python异步编程(中篇)](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484889&idx=2&sn=25d11042e5b2ab17dff40535d354b8f8&scene=21#wechat_redirect)
+
+14、[百万「并发」之Python异步编程(下篇)](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484885&idx=2&sn=b9e62a9b024358aec629e876b76e0eb5&scene=21#wechat_redirect)
+
+15、[如何一行 Python 代码实现并行?](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484826&idx=1&sn=6c5b575b7b134642077cfde2bb8b613f&scene=21#wechat_redirect)
+
+16、[asyncio:从原理、源码到实现](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485245&idx=1&sn=a08826c3d28037479190fd652438bcca&scene=21#wechat_redirect)
+
+17、[为什么说线程是CPU调度的基本单位?](https://mp.weixin.qq.com/s/c3aZ-6UzZVD3_PvVhiDPWA)
+
+18、[非常适合小白的 Asyncio 教程](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247495734&idx=1&sn=2fb31e6fd306f19ba03b368f5924b7e8&chksm=e88590d4dff219c252b4e1126f196e0a0c395f4f8d22d58cc5c531d9dd2acad6f6e295a1fee8&scene=27#wechat_redirect)
+
+19、[说说 Python 里关于线程安全的那些事儿](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247486279&idx=1&sn=2b6acf717b7f5cc6a2b72c62266dbe01&chksm=e8866ba5dff1e2b318c679413fd7d0ac3a69db7cab1ceda03eca26fb71f6d2837af80e2143d0&scene=27#wechat_redirect)
+
+20、[异步 Python 比同步 Python 快在哪里?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247494549&idx=2&sn=1120b33a12bf8ab25df805cfab9afbc8&chksm=e8858b77dff20261afdf788de30c7e14f3d66ff52215b8c0db66320a118e4544283424340d4e&scene=27#wechat_redirect)
+
+21、[为什么 Python 多线程无法利用多核?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247494567&idx=2&sn=653795446bacff2d445cabd417748756&chksm=e8858b45dff202530afc05a00469b20a0a95f53bcaefc49472240e1c905459ecfb74edbe0461&scene=27#wechat_redirect)
+
+22、[Flask 之父:我不觉得有异步压力](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247496214&idx=2&sn=cd813e57b7e03aff0300f1f65cc583f0&chksm=e88592f4dff21be2738eaf2745cf828841fea934e986f9a4ee7d13eb9fe09701d1c13993435c&scene=27#wechat_redirect)
+
+23、[Python 协程的本质?原来也不过如此](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247495553&idx=1&sn=d00916691bf2cddc576f1346672ef603&chksm=e8858f63dff20675238e5eee747645d022aa316bd5417004c296157aadb4de6134d6118e66e7&scene=27#wechat_redirect)
+
+### 2.5 实战练习
+
+1、[适合 Python 新手练习的绝佳项目](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485898&idx=2&sn=36a1b1a94dece0a290652da23679cff0&chksm=e8866928dff1e03e58b95b3cd24dbc23e368b6eb2c0599b2efe814924e21ec5c01935b2e0455&token=2013245174&lang=zh_CN#rd)
+
+2、[4个Python实战项目,让你瞬间读懂Python!](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485852&idx=2&sn=eb3471a260cce835a755a780d6cc690d&chksm=e886697edff1e068bd0f9d456e26ee5ebe482580457951e42807e731d266c95f00c37de22fea&token=2013245174&lang=zh_CN#rd)
+
+### 2.6 GUI 应用
+
+1、[如何在Python中编写精美图形界面?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247486179&idx=1&sn=eed86ec2f44a92fadb94f237178b3f8a&chksm=e8866a01dff1e317f450d91b88e91d6912d6a1b2ac7a34ebb86b74a2abc0f912d8ced5a3baac&scene=27#wechat_redirect)
+
+2、[或许,这是最强大的一款Python GUI工具](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247492571&idx=2&sn=6ac2c0c19d86d653acd82d5b4ef86126&chksm=e8858339dff20a2f595159499e8eb8e072b52e61521e4a1322337f174bff942134a25b3b6b9b&scene=27#wechat_redirect)
+
+### 2.7 自动化
+
+1、[付费?是不可能的!20行Python代码实现一款永久免费PDF编辑工具](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247492085&idx=1&sn=785968af86bf998ecad9b11583a23ad6&chksm=e8858117dff2080119f596c6447d9a0c7b73c6bced8306883900f2f4d9582929f9d5c26ac45d&scene=27#wechat_redirect)
+
+2、[带你用 Python 实现自动化群控(入门篇)](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247491979&idx=2&sn=7489738d3225a96767173e4b54023f95&chksm=e8858169dff2087fed0f11ad6b880c110e3530eae28ed7702343f6d81163860ed20a2f056294&scene=27#wechat_redirect)
+
+3、[最全总结:Python 发送邮件的几种方式](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247489568&idx=2&sn=bdac42b533784bf3a8ee523da36bacba&chksm=e88678c2dff1f1d499321032e6871f2810608909121bf56aee184c6d7770d3b310f39cad7c43&scene=27#wechat_redirect)
+
+4、[太全了!使用 Python 转换 PDF,看这一篇总结就够了。](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247488934&idx=2&sn=eed1cab8e03c908fdfe311bf4bbd91a6&chksm=e8867544dff1fc52e0d9dd62c61d36ec5a5b6847aeb9992e5423eb4e01f08995461160adbb45#rd)
+
+5、[Python吊打Excel?屁!那是你不会用!](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247487151&idx=2&sn=9c4a697758af01c086cced44b98a3380&chksm=e8866e4ddff1e75bd2f19e036c3e338edee50871364d2f185afa51cb361c83f3bbda8ce2d92f&scene=27#wechat_redirect)
+
+6、[收藏|Python办公自动化不得不会的十大文件操作](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247495776&idx=2&sn=50e900caa96346371045145170dc9073&chksm=e8859082dff219940436120f8fa9ae329dc655e69335f967e2f926786420d9d97b1b11711100&scene=27#wechat_redirect)
+
+7、[再见 VBA!神器工具统一 Excel 和 Python](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247495801&idx=1&sn=becae030a09a689e1f231a5c3c1ef7bd&chksm=e885909bdff2198d1d2aafaf0077a2763d796e435817b70b612095ae944489581f3bbffe0a69&scene=27#wechat_redirect)
+
+8、[微软最强 Python 自动化工具开源了!不用写一行代码!](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247494621&idx=1&sn=61d9f8b7557cb2ff517fde1a414b219d&chksm=e8858b3fdff2022954d383579b6392362e99e99af4a6309028dfa6ddd7bfc90a02c85d599b50&scene=27#wechat_redirect)
+
+### 2.8 网络爬虫
+
+1、[想逆向我的 js 代码?先过了我的反 debug 再说吧!](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484876&idx=1&sn=ed81400f77deb2af18311f8e42e9b11b&scene=21#wechat_redirect)
+
+2、[教你实现一个可视化爬虫监控系统](https://mp.weixin.qq.com/s?__biz=MzU4OTUwMDE1Mw==&mid=2247484208&idx=1&sn=d04421526cf0e12089541063b4ec448d&scene=21#wechat_redirect)
+
+3、[估计是讲得最清楚的「异步爬虫」指南](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484929&idx=1&sn=7055414a33d7cbadaeb5645cbd5203ec&scene=21#wechat_redirect)
+
+4、[10 个爬虫工程师必备的工具](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484958&idx=1&sn=3422e53de4ae701a922104ad26f8e256&scene=21#wechat_redirect)
+
+5、[Selenium自动登录淘宝,我无意间发现了登录漏洞!](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247490737&idx=2&sn=6a33873b3777afd420f3c56078a88687&chksm=e8867c53dff1f5455fe54e1ee0044cc97638fbc96983f36e19938810bea4e06d5af0ba3036df&scene=27#wechat_redirect)
+
+6、[干!一篇文章讲了这么多爬虫的技巧](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247490557&idx=2&sn=f5128b1dc827cc4a6035d46714a484b1&chksm=e8867b1fdff1f20993b024fbfaa4445763a5949174913a58d9f17ddcb0286be8b325516cb449&scene=27#wechat_redirect)
+
+7、[谈谈常见网站加密和混淆技术](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247486218&idx=2&sn=6261fa602eb4f73365a9d372376594ed&chksm=e8866be8dff1e2fea5082dd13dc7799a1cbc829f2e41a32891db411152153cb261d01be5807b&scene=27#wechat_redirect)
+
+8、[初识网络爬虫之夜探老王家](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247486179&idx=2&sn=3a1b880c7f1b37d095359b35ff96e9b4&chksm=e8866a01dff1e31711d7fb093a96f54eaf374b46c686b0ea4e49d6f926018ad5b6c27aebfa9d&scene=27#wechat_redirect)
+
+9、[不懂爬虫也能轻易爬取数据的 6 大工具](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247486085&idx=1&sn=51c740e1d848f1624a5a8e582434f661&chksm=e8866a67dff1e371de89a64503591ae0c42b67cd43434fa394125f09a2f65bc23817a266ac13&scene=27#wechat_redirect)
+
+10、[效率提高十倍,Puppeteer 如何启动交互模式?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247496566&idx=2&sn=efc444487afd08ec055ceaee414857d6&chksm=e8859394dff21a824ccc8e2679f985e94c60d6b680f6856aa7a7a9ed608c9dc1ad552f0f2b40&scene=27#wechat_redirect)
+
+11、[别去送死了,你这样写爬虫,早晚得进去~](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247495216&idx=2&sn=648a296205fb3310287d95907d5126ef&chksm=e8858ed2dff207c4c42eb8b224bfe4f68186108642afd8ad7dfddef2628ff2c8f93170dbd948&scene=27#wechat_redirect)
+
+12、[如何在 APP 上爬取数据?多图教程带你实操](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247498688&idx=2&sn=4125d37e4f552bb0fd186fd066c07eb6&chksm=e8859b22dff21234d15301eeeaef6f61acce0f9c6020285029e61448357b6703a53a51984d12&scene=27#wechat_redirect)
+
+### 2.9 实用系列
+
+1、[你抢不到的火车票,我帮你!](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484972&idx=1&sn=19ac660b61f046c5d419acdae7f394a6&scene=21#wechat_redirect)
+
+2、[30分钟教你快速搭建一个顔值超高的博客](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485045&idx=1&sn=8b250c0c174e418e2025d86f42c695b6&scene=21#wechat_redirect)
+
+3、[用Python写一个表白神器让你七夕脱离单身](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485021&idx=1&sn=123b39391d11e9c7160b47a4c6a3dcb1&scene=21#wechat_redirect)
+
+4、[用 Sphinx 搭建博客时,如何导流到公众号?](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484848&idx=1&sn=80ae18e7f53a64e62ac9c0ef0c21362e&scene=21#wechat_redirect)
+
+5、[10 行 Python 代码写 1 个 USB 病毒](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484832&idx=1&sn=43c353856c7dd9ea2cb1a9bbcd077fa2&scene=21#wechat_redirect)
+
+6、[废旧 Android 手机如何改造成 Linux 服务器](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485279&idx=1&sn=7166a2c9445438eaaf63a25b598e5427&scene=21#wechat_redirect)
+
+7、[如何将手机打造成 Python 开发利器?](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485256&idx=1&sn=2e646e28287707c67c22fbe68112e622&scene=21#wechat_redirect)
+
+8、[我用 Python 做了一回黑客,批量破解了朋友的网站密码](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485156&idx=1&sn=05142ae7b4bf7e97ebe042e394aa7084&scene=21#wechat_redirect)
+
+9、[手把手教你安装Win+Ubuntu双系统](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485030&idx=1&sn=8383a7306381f36781957b807fa93961&scene=21#wechat_redirect)
+
+10、[一篇文章让你的 MacBook 进入超神状态](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484906&idx=1&sn=b2c3c969e53beae53aa7be7959227b5b&scene=21#wechat_redirect)
+
+11、[情人节来了,教你个用 Python 表白的技巧](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485701&idx=1&sn=ef7f0b83e60f397c1839259b275575bf&chksm=e88669e7dff1e0f1317ec429cf9e70442bb3a08ec46efe9a0d06f4ec9847574ea30b53904670&token=1148998814&lang=zh_CN#rd)
+
+12、[“Hack” 微信实战:如何用 Python 分析微信群聊记录?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247486141&idx=1&sn=8b32c57dce0f3a33127c39c9cd35509e&chksm=e8866a5fdff1e349f16f677304716e40a305e60168fe7e30361de675dad5dfa956c9d7c79089&scene=27#wechat_redirect)
+
+13、[5 行 Python 代码生成自定义二维码](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247488795&idx=2&sn=8e03e01e861753f7ccf3adeb1f3cb848&chksm=e88675f9dff1fcef328c69b1519743fcc3ae83b80dd3d66cb934acb346781497f83c5a9051a5&scene=27#wechat_redirect)
+
+14、[女朋友背着我,用 Python 偷偷隐藏了她的行踪](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247487175&idx=1&sn=c3ba19bdbcb6fd59b37ff04d02bce63b&chksm=e8866e25dff1e733b31386fda99271eefadc77b3e0579df4d74617b561ecf0fa0db70f9f2860&scene=27#wechat_redirect)
+
+15、[zip 解压炸弹? ?在 Python 面前,啥也不是..](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247491841&idx=2&sn=307a29d71776f6d1c68567a16727a7e5&chksm=e88581e3dff208f55a48081cb8f951ca7d977c9b1ec3ac9aaf26bc8c53904ac0e15035e5c680&scene=27#wechat_redirect)
+
+16、[把你的朋友变成表情包?Python:So easy](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247493765&idx=2&sn=cd3cd2a891cb3bc47956ac3bc8a06dd1&chksm=e8858867dff20171241f81274eec097f48ea23558029b42a3e7565833c8a62d20e52d2dea87e#rd)
+
+17、[超干!如何建立一个完美的 Python 项目?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247493789&idx=1&sn=7f0f4c40e410a3cfb01706dd9c8c8fc6&chksm=e885887fdff2016930accb8ea6d4e2cb67d4d6983ca716148da7f63c62a6b96f518497ed2575&token=58703854&lang=zh_CN#rd)
+
+18、[10 个“疯狂”的 Python 项目创意](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247493963&idx=2&sn=e4ff38bc55f4ad4d990191e81a316421&chksm=e88589a9dff200bff3e65c19744e59dd3407a48ff5b104bc7b71771762b331daca5c7772892a#rd)
+
+19、[使用 Python 自动化清理微信僵尸好友](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247496922&idx=2&sn=3b2233563ed7a02df94ab2600fe737f3&chksm=e8859438dff21d2eb4874859a839f6c03b74cbd217a2e92eb6ece714051af11fdcb81c76c4d7&scene=27#wechat_redirect)
+
+20、[别再问我怎么Python打包成exe了](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247496522&idx=2&sn=5579b947755bcc4aa2d2dd098d4396bc&chksm=e88593a8dff21abe09ab37a9c6ee855bab17342a7397b946514acb9db281c629502709adc6ae&scene=27#wechat_redirect)
+
+21、[还没抢到票?试下这个用 Python 写的最新抢票神器](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247496260&idx=2&sn=6f0b4ea3960b25640eb2936f65ac9e0a&chksm=e88592a6dff21bb0140bb319486321f16a41679a53f68ea8cdbe0cad3fa9fbb6996fcc2019ec&scene=27#wechat_redirect)
+
+22、[使用 Python 制作按键触发Windows通知的脚本](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247494892&idx=2&sn=6c633c829e9e996bf6c3109911d2e20e&chksm=e8858c0edff205181ebc2eb5e907ea38b0dc0fa581a243fe57269968dc1aab147d3230c8a8cf&scene=27#wechat_redirect)
+
+23、[纯Python方案实现中英文全文搜索](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247494874&idx=2&sn=f515724a52613a875869e6aa31ae3e78&chksm=e8858c38dff2052ede9f42592fb59446b68bb3fff84870601c36ee64b864527d25cb9619ccae&scene=27#wechat_redirect)
+
+24、[两行 Python 代码,精准识别一张图片的格式](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247497854&idx=2&sn=ca9066d3d7e4f3a0e8dfa18429683b30&chksm=e885989cdff2118a81ecd769cf1443e252d5347a7c76e7d5b2d9c494d0afa2dbda5fbe467897&scene=27#wechat_redirect)
+
+25、[明哥放大招! 这下看你们还怎么搬运我的文章 ~](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247498344&idx=1&sn=c3707e3058f444fc42e373155f82c514&chksm=e8859a8adff2139c790e5240dd4339b5cf7493e959eba0ddff5738a2432ff287ba6488b59b18#rd)
+
+26、[如何在手机上配置 Python 环境](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247497813&idx=2&sn=b4b09859f7b175e0a60ca7e61131d475&chksm=e88598b7dff211a124f85f82db20a3c7742dce30f7218dcd00ac393cc49ad7754b2401f8e1bd&scene=27#wechat_redirect)
+
+27、[在手机上运行 Python,这款工具比 QPython 还好用~](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247497684&idx=2&sn=62756dddcd6d8a8e29326d33b1d9e70e&chksm=e8859736dff21e20cf67f8c29e87cdcd2849f04bb9567dc2f5c2dcc6a00cbd8aeeecaace335f&scene=27#wechat_redirect)
+
+28、[情人节表白神器,v2.0 版本](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247497554&idx=1&sn=032200c99fcb0841246cb5eed74dc73b&chksm=e88597b0dff21ea6b20f8301a3c2e2dfdf914d49f23155f81113c90b7b6135802f252481a318&scene=27#wechat_redirect)
+
+29、[5个无聊透顶的 Python 程序](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247497441&idx=1&sn=a6e7b24b13cee4c7f580587f0303a579&chksm=e8859603dff21f158b9560b6b397b13a26d71ef6c72d47d220c65812f5c0f9d7f15a3c209fe5&scene=27#wechat_redirect)
+
+30、[怎样用Python制作好玩的GIF动图?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247497164&idx=2&sn=cfc237768c53716f4554ea89bbca3294&chksm=e885952edff21c381eb898a203bed86a1b96eba8e1cabecabf0c896489c5fc3a0022b0be8527&scene=27#wechat_redirect)
+
+
+## 03. 数据分析
+
+### 3.1 基础库
+
+1、[有关 NumPy 和数据表达的可视化介绍](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485295&idx=1&sn=6220c2ed0d73feade0c9691fbf214ba7&scene=21#wechat_redirect)
+
+2、[快速提升效率,这6个 Pandas 技巧一定要知道](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247491909&idx=2&sn=3f3bf16784689a5e8bd5f9ef9478040f&chksm=e88581a7dff208b12e25ddaf6a047abc47878eee56f85309b6618cfd92a4dfeb2445f23e2f05#rd)
+
+3、[50题 讲透 matplotlib :从入门到精通](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247486026&idx=1&sn=7b333759709c30fe442732b7a107e3c1&chksm=e8866aa8dff1e3be37f5169ac53bbcf71a76514e99fbbcbe458ec15aa1204d988ad44d689eb3&scene=27#wechat_redirect)
+
+4、[实用的 Pandas 技巧,估计 80% 的人不知道](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247496209&idx=2&sn=8f8db418261326c981cd27f57cc3fc5b&chksm=e88592f3dff21be543cc302a715ea39ae8a1adacb7342f45d5bcf2eca9c2b398b05955408597&scene=27#wechat_redirect)
+
+5、[用Python 操作 Excel,这篇文章别错过了!(超全总结)](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247498065&idx=1&sn=5f2bb43d9d960c2a7330353c4ac926a8&chksm=e88599b3dff210a528c3e6aac2ea1708b7aa5e3d58c49d12d63492271396ccebee8f0a4a6eae&scene=27#wechat_redirect)
+
+6、[别找了,这是 Pandas 最详细教程了](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247497846&idx=2&sn=9483146777ac86567e3933e6d5475e5f&chksm=e8859894dff2118208b517d0d9e753fbf8b1bb83995d36cf04694825972b6a82e195d706852d&scene=27#wechat_redirect)
+
+7、[再见 for 循环!pandas 提速 315 倍~](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247497472&idx=2&sn=df1089640d2beb3607518d0ea9f3e709&chksm=e88597e2dff21ef45a7c3b25e102bc665368f628d1b76decfbeeb18a4cd1a2605737c4c8fb21&scene=27#wechat_redirect)
+
+8、[Python 操作 Excel 库 xlwings 常用操作详解](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247497164&idx=1&sn=281a25369e47ecba1951d4e92f01d5d0&chksm=e885952edff21c38f633910f3c2aac54f8e379707e70d9a70a1cdde5bfd4b7fde145315ea54f&scene=27#wechat_redirect)
+
+### 3.2 数据可视化
+
+1、[可视化01|一图带你入门matplotlib](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485022&idx=1&sn=76d4270a15c430217588bd9f8be8303b&chksm=e88666bcdff1efaa35c6d685f19b04e62f58e768dc9d1819bcb9f8211f65d3fd83296259d923&token=1148998814&lang=zh_CN#rd)
+
+2、[可视化02|详解六种可视化图表](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485020&idx=1&sn=7f82736e6a2e5442ed59c82d8e242ca4&scene=21#wechat_redirect)
+
+3、[可视化03|用正余弦学习matplotlib](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485019&idx=1&sn=9d44ee27fd888831e94845d6d0256deb&scene=21#wechat_redirect)
+
+4、[可视化04|子图与子区难点剖析](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485014&idx=1&sn=90dbdf17f049c152944a4c28642df249&scene=21#wechat_redirect)
+
+5、[可视化05|绘制酷炫的GIF动态图](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485007&idx=1&sn=1464dee30d006ddb5111f9827b0c4081&scene=21#wechat_redirect)
+
+6、[可视化06|自动生成图像视频](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485002&idx=1&sn=f09c089328eb0fe39721b73272c81214&scene=21#wechat_redirect)
+
+7、[可视化07|50个最有价值的图表](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484920&idx=1&sn=92eb2fd55f13a8bda75a7103a73f8d50&scene=21#wechat_redirect)
+
+8、[ 可视货08|利用 Flask 动态展示 Pyecharts 图表数据的几种方法](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485658&idx=1&sn=f78e976889de52c0ce35e862bafacd8a&chksm=e8866838dff1e12e41b8f2dd1a43dc540ff39484ae64831332d233b5a4f7ca3b439d370ef1ed#rd)
+
+9、[一个没法商用,但是好玩有趣的 Python 手绘图形库!](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247491212&idx=2&sn=66a5fd4f672b668aee6bb2c3a01a92f6&chksm=e8867e6edff1f77813b2b59f6219d18041963e6011afa1a590925c71ebd79c7e90b03e82c606&scene=27#wechat_redirect)
+
+10、[如何使用 Python 绘制一套动态图形?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247488616&idx=1&sn=ef094168fc5ff4c46a66b2068cb1011e&chksm=e886748adff1fd9cc9f9f3ed3b2210c9f9f2c770458b3207f9fd6b12ee37a5451e9444b41e59&scene=27#wechat_redirect)
+
+11、[一文学会制作 6 种炫酷的 Python 动态图](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247486111&idx=2&sn=fadf51d43bc468c58ba6885db3006e7a&chksm=e8866a7ddff1e36b5815a9693150799d5925727f5a425e642fd136283ff4a58b7f19ba9da39d&scene=27#wechat_redirect)
+
+12、[如何使用 Python 绘制一套动态图形?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247488616&idx=1&sn=ef094168fc5ff4c46a66b2068cb1011e&chksm=e886748adff1fd9cc9f9f3ed3b2210c9f9f2c770458b3207f9fd6b12ee37a5451e9444b41e59&scene=27#wechat_redirect)
+
+13、[超硬核的 Python 数据可视化教程](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247492265&idx=2&sn=2aede70646f9ee34ad526d2d99b6549e&chksm=e885824bdff20b5d428dbb09daeae2f3e96f1362372bf6ffb16fae1f06263703379873025d43&scene=27#wechat_redirect)
+
+14、[收藏!最全的可视化学入门算法教程(Python实现)](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247491520&idx=1&sn=55804b80dcc56a3923255abf58e08968&chksm=e8867f22dff1f6340a7f61f7796a1e78bd695278a340351860efc244c8a9bbf80cc02bbfaf05&scene=27#wechat_redirect)
+
+15、[50 款数据可视化分析工具大集合,总有一款适合你](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247490737&idx=1&sn=f447d6e27504611baf8a4ad4056c6e5a&chksm=e8867c53dff1f5450ff808baec53797bd772cdd3c0e43f2f0c278cd4b8867223d5591ab2aa7d&scene=27#wechat_redirect)
+
+16、[用Python画漂亮的专业插图 ?So easy!](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247490251&idx=1&sn=14df276f09201930fdc413d8f9a772df&chksm=e8867a29dff1f33fd0f85cf4412c42cc1145e917b3cd06e158aa31356525799033dd8c6e620f&scene=27#wechat_redirect)
+
+17、[太好玩了,看看我用 Pyecharts绘制的“时间轮播图”](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247489657&idx=2&sn=6359a534033c0a6f21af28a7d9f8b768&chksm=e886789bdff1f18d6719558e305453cba2b6c8a55d66c9bcb6dd5678209d89c1594d87de0a87&scene=27#wechat_redirect)
+
+18、[实战!用60行Python代码画出30万条房产数据分析图](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247489032&idx=1&sn=6b9c7281d3090783a9a486b81f749789&chksm=e88676eadff1fffcec00d3c44ee9b34496dc886417da55aeabed017d496da85d442f205c227d&scene=27#wechat_redirect)
+
+19、[刷爆全网的动态条形图,原来5行Python代码就能实现!](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247492725&idx=1&sn=964c45002220761dafcf537d5f9fe802&chksm=e8858497dff20d8175b87ddb1997b1dd71c91a03a92e60611718fdbf2bdbe3cee41d239b360d&scene=27#wechat_redirect)
+
+20、[这 10 个 Python 可视化动图,学会了就吹牛了](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247493551&idx=2&sn=dee235d803b491163f74a49f5cf0d12f&chksm=e885874ddff20e5bf639454c226f6e74e5180dbab3803db08c2bd72d67347aa52dede40da4c9#rd)
+
+21、[牛批了,1行python代码就可实现炫酷可视化](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247494580&idx=2&sn=bf87885645377449f3a94d6d8122e1f6&chksm=e8858b56dff2024046c48ccf0ff087c42fa2608c0d0b6183ea61ffb85278a9c52138cdfbef13&scene=27#wechat_redirect)
+
+22、[珍藏版 | 这 30 个细节,决定了数据可视化的质量](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247496866&idx=1&sn=f1ba05ed6a4db3e507f0cf0d4ce3ac59&chksm=e8859440dff21d56256e0ad9a9c739dca37fae6c40843fe98033a2e77a412a667407f36a7113&scene=27#wechat_redirect)
+
+23、[吹爆了这个可视化神器,上手后直接开大~](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247495182&idx=1&sn=d8e913a808796e6fa793d58df26d209f&chksm=e8858eecdff207fac878d4b27894b5a2c69812d6803262e430d2244443a2c23e38d91a697556&scene=27#wechat_redirect)
+
+24、[Pygal,可导出矢量图的Python可视化利器](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247497951&idx=1&sn=7cce71d5c34fdeb89509e97817a5f0af&chksm=e885983ddff2112bc76bd9a4dbe14f407c5fe8b2ea0010b353e760b59c4476603f1d82a26eac&scene=27#wechat_redirect)
+
+25、[这可能是 Python里最强的绘制地图神器](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247497813&idx=1&sn=c5034f6ecf03fe3ccb6771b39faa8230&chksm=e88598b7dff211a11145c44db19f50df244ab3c2b740e1d51ead51b25d7fa9643660ca5cc29a&scene=27#wechat_redirect)
+
+### 3.3 工具使用
+
+1、[ 整理了 50个 IPython 的实用技巧](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485613&idx=1&sn=70f89faf2573b3025edc73f525f22a0a&chksm=e886684fdff1e159509d30fd0a24854da39fb5a6ae55e0ed6e40b7971d3219968006f80aad7b#rd)
+
+2、[ Jupyter NoteBook 的使用指南](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485024&idx=1&sn=aac0db4b2c97c7f5c2871342b936eaa2&chksm=e8866682dff1ef947ec6474b8c03f668e462232ebd0ec0f137d42e5e63b9b66a3d9e303c1caf&token=1148998814&lang=zh_CN#rd)
+
+3、[Jupyter Notebook最常用的五大配置技巧](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247491411&idx=2&sn=88c290af7793ddc024cf2a39875b97e8&chksm=e8867fb1dff1f6a731077d749b1ea44fd558995f3448e27da9becf7b035ed777774d1b0bb0ec&scene=27#wechat_redirect)
+
+4、[强烈安利!这十二个 IPython 魔法命令](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247488777&idx=2&sn=3b14fe7faa4d2a26b702c15ebe6b6103&chksm=e88675ebdff1fcfd471dc84c802dab2c37889d59e55711c5d521c80fdbf38291b4092b840083&scene=27#wechat_redirect)
+
+5、[真香!安利 6 个 Python 数据分析神器](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247488619&idx=1&sn=0c6c76b9deaa4f1976dcacaaee018312&chksm=e8867489dff1fd9fa2b0ac08696c4dae1b48054ca64aefef6d2117179dbbffbfe4ae9d1a4f81&scene=27#wechat_redirect)
+
+6、[使用 pyecharts 打造酷炫的 BI 大屏](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247487730&idx=2&sn=b2ee3058d63ebf702f3f0f9a6ca9c623&chksm=e8867010dff1f90686948269ca5e0907d726fc775029cc414285d71c0176ce859b818d2e34d5&scene=27#wechat_redirect)
+
+7、[酷炫的动态可视化交互大屏,用Excel就能做!](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247486295&idx=2&sn=2826a69402912a70955209192e761c4d&chksm=e8866bb5dff1e2a30d374d5f46afa1cb9d952a0c939701b9f8b5bc52df4169d5e2da30236505&scene=27#wechat_redirect)
+
+8、[像操作Excel一样玩Pandas,这款可视化神器太棒了](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247493689&idx=1&sn=2e614500c8359e4b87de55128fe69593&chksm=e88588dbdff201cd761e20605f10f1aa9525d6d89072cbbc9a307e35788b8a425c2044d337d4#rd)
+
+9、[8 个 Jupyter Notebook Tips,隐藏得太深了](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247494062&idx=2&sn=860539d7eae82c53791cd7a2eb005536&chksm=e885894cdff2005ae9169c7dfabe3760c19eded392ec18805f1d763ad8dce77c21150698178a&scene=27#wechat_redirect)
+
+10、[PyCaret:几行代码轻松搞定从数据处理到模型部署](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247495402&idx=2&sn=bb0664c6ac7995dfad4f4a1268e2e374&chksm=e8858e08dff2071e121d81fa564671ba4425814687fd2df3aa32898ffb3bdbe72d303bc9d66a&scene=27#wechat_redirect)
+
+11、[这个 Jupyter 插件,用起来就像 Excel 一样简单](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247498500&idx=2&sn=672a1da1a9957c84a19802842df5b5d9&chksm=e8859be6dff212f0754dd0a3820241be3af34d72e6dc32e7d83d1f87198bd549ccfd66c55029&scene=27#wechat_redirect)
+
+
+## 04. 开发工具
+
+1、[代码调试|无图形调试工具 - pdb](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484969&idx=1&sn=a99fc31865edc4b439707d2be6f66654&scene=21#wechat_redirect)
+
+2、[代码调试|远程调试图文超详细教程](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484970&idx=1&sn=9ac9c5dfcdc8c6b48a7b4c9854825734&scene=21#wechat_redirect)
+
+3、[优化Python开发环境的几个技巧](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485131&idx=1&sn=f1bd7b31a3624f015c74f56a8888a695&scene=21#wechat_redirect)
+
+4、[让你重新爱上 Windows 的小众软件](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484964&idx=1&sn=1479536416788a6c55dddef70167eb88&scene=21#wechat_redirect)
+
+5、[Python 的命令行参数解析工具](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484960&idx=1&sn=3456d9a05b35588b060833ceb189db6e&scene=21#wechat_redirect)
+
+6、[搜索神器 EveryThing,你把它的潜力用到极致了吗?](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484894&idx=1&sn=59877fe818739175d3d075458594f563&scene=21#wechat_redirect)
+
+7、[开发工具|盘点 Xshell 的那些奇淫技巧](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485043&idx=1&sn=8c240e569c60607cd4cea8888a6aa2e1&scene=21#wechat_redirect)
+
+8、[告别996,全靠这个Python补全利器](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484824&idx=2&sn=8f20af371954dfd2a5c890bb5814b4c9&scene=21#wechat_redirect)
+
+9、[学会这21条,你离 Vim 大神就不远了!](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484902&idx=1&sn=d677261fda90b67bf5eda886447a4f77&scene=21#wechat_redirect)
+
+10、[用 Python 做开发,做到这些才能一直爽](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484874&idx=1&sn=abffab70b1e265ede1fd3b994dedab90&scene=21#wechat_redirect)
+
+11、[这款神器,能把 Python 代码执行过程看地一清二楚](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484878&idx=1&sn=9bc941ff19ec38e7c7137a22df981f27&scene=21#wechat_redirect)
+
+12、[如何把自己的 Python 包发布到 PYPI?](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484835&idx=2&sn=f24a415d47d4122c4d6b1a6ccf254a51&scene=21#wechat_redirect)
+
+13、[谁说 Vim 不好用?送你一个五彩斑斓的编辑器!](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484872&idx=2&sn=ce718e511b754fe936172724a824a716&scene=21#wechat_redirect)
+
+14、[迄今为止,我见过最好的正则入门教程(上)](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484831&idx=1&sn=077d14410db6a906c3b962f0ba18b2d4&scene=21#wechat_redirect)
+
+15、[迄今为止,我见过最好的正则入门教程(下)](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484830&idx=1&sn=4d48b1c7f2b5234992732f8f15b868df&scene=21#wechat_redirect)
+
+16、[Win 平台做 Python 开发的最佳组合](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484837&idx=2&sn=68935077ae9eeb6fe4f708565aa0a9ed&scene=21#wechat_redirect)
+
+17、[15 款Python编辑器的优缺点,别再问我“选什么编辑器”啦!](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247492759&idx=1&sn=6c6e6606fd9da9d2b951bda9e8a1b223&chksm=e8858475dff20d638f4b731a44dd48fe01a68f16480bcaed2516fc7d3d56c61cc6c62afccb9d&scene=27#wechat_redirect)
+
+18、[用了三年的 pdb,没想到还能这么调试](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247498550&idx=1&sn=d2d0ba8987509a67ba40c19f7dcfd719&chksm=e8859bd4dff212c2f4d6f07c42ae81af046aeb8611c3c869143ed7e4ebac03fd011ddc8b88e6&scene=27#wechat_redirect)
+
+19、[调试 Python 代码,可别再用 Print了!](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247497083&idx=1&sn=47fa23a2104c2e75ad71c56b6d89b84d&chksm=e8859599dff21c8f0f832e83f3e5220bef96fbba7514863e4e8fcd178818258f024744902806&scene=27#wechat_redirect)
+
+### 4.1 PyCharm
+
+1、[受用一生的高效PyCharm使用技巧(一)](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484946&idx=1&sn=12a891db18e9d1233535fa4aca9a3892&scene=21#wechat_redirect)
+
+2、[受用一生的高效PyCharm使用技巧(二)](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484942&idx=1&sn=9dbb2cfe2a277bd3519d37414fcc608e&scene=21#wechat_redirect)
+
+3、[受用一生的高效PyCharm使用技巧(三)](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484932&idx=1&sn=347b35ccdee94895652caa86191360c8&scene=21#wechat_redirect)
+
+4、[受用一生的高效PyCharm使用技巧(四)](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484918&idx=1&sn=66b3fd784050e5cb85109faa3d063993&scene=21#wechat_redirect)
+
+5、[受用一生的高的PyCharm使用技巧(五)](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484914&idx=1&sn=cd1282f94f9326647b05ad9090afd2c8&scene=21#wechat_redirect)
+
+6、[受用一生的高效PyCharm使用技巧(六)](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484907&idx=1&sn=647a75b96884b39c57664f62ead11b89&scene=21#wechat_redirect)
+
+7、[受用一生的高效 PyCharm 使用技巧(七)](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485372&idx=1&sn=3ed61c4ccbb089358213304cd70bb0f7&chksm=e886675edff1ee487911e922279675264916dd08120f5f40113f37dc080b94c7bf9ad67719c4&token=1148998814&lang=zh_CN#rd)
+
+8、[代码调试|远程调试图文超详细教程](https://mp.weixin.qq.com/s?__biz=MzU4OTUwMDE1Mw==&mid=2247484304&idx=1&sn=7612f0c2fd6232723ca2733fb1e8f46d&scene=21#wechat_redirect)
+
+9、[手把手教你打造一个顔值超高的IDE](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485052&idx=1&sn=2e0bb1a3af4206fd2cf8e3650f695e6b&scene=21#wechat_redirect)
+
+10、[玩转 PyCharm ,这篇文章就够了](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247487629&idx=1&sn=9a19cbcce2effcc242cc8d6d33cd2762&chksm=e886706fdff1f97955e73495c0be5b104f834d569d75e9e1470417f79e1d19ff4937683484ad&scene=27#wechat_redirect)
+
+11、[用动画展示 Pycharm 十大实用技巧](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247486118&idx=1&sn=990c35adb86919bda2f2cb82e7c5fb9e&chksm=e8866a44dff1e35200244e3d369ad6c0f736d3511dcfb086b7641339e20ae62e35b8fd60bdd9&scene=27#wechat_redirect)
+
+12、[太棒了!Jupyter 与PyCharm 完美融合,Jupytext 来啦!](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247496487&idx=1&sn=344204a2893c7bb54e9977f274c29fef&chksm=e88593c5dff21ad366d0fa3c2a1af3760f8f67b167ee8b29e016fe31adc66bb84c3e018e25ad&scene=27#wechat_redirect)
+
+13、[装上后这 14 个插件后,PyCharm 真的是无敌的存在](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247495720&idx=1&sn=3233659fa9c975df9e95402a8c68b405&chksm=e88590cadff219dcd82f22d1d4684dda09a2db3469a0826ffe7605388fc37fb3a2c7cca49e0f&scene=27#wechat_redirect)
+
+14、[卸载 PyCharm!这才是 Python 小白的最理想的 IDE](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247497739&idx=1&sn=2e4795f4ddc784cfa796fb59b3d0e707&chksm=e88598e9dff211ffbe2cea5f574c8ee6117f4e7cd5d4869a804422139360421e4803df270f19&scene=27#wechat_redirect)
+
+### 4.2 VSCode
+
+1、[这 21 个VSCode 快捷键,能让你的代码飞起来](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484893&idx=1&sn=421b544115efad388314ccd027761b40&scene=21#wechat_redirect)
+
+2、[神技巧!在浏览器中也能用 VS Code](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484859&idx=2&sn=70abd19426374271a10cbef8e1645c7a&scene=21#wechat_redirect)
+
+3、[生产力终极指南:用了两年,如今才算真正会用VS Code](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247492595&idx=2&sn=a39a7cb70908d442a399ac80a272f90e&chksm=e8858311dff20a0782cca942c7e9f3444bb443cf816fd05d76c5f7c234930afacb6c948802bf&scene=27#wechat_redirect)
+
+4、[VS Code的7个开源替代品,全都知道算我输!](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247490251&idx=2&sn=ef0eca9da6eff8405a5a48d89a6b2e54&chksm=e8867a29dff1f33f29487864efb1389b02705d81d0198df581b5b71085ff238f24ffe99f154c&scene=27#wechat_redirect)
+
+5、[再见了,PyCharm](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247489522&idx=1&sn=d7d45dba4a7c9e2f010d09f757639a19&chksm=e8867710dff1fe0657a16b6087ad4a50fdceb8f324bef5f950b3320e33e09ce10d1fa045827d&scene=27#wechat_redirect)
+
+6、[微软推出 Pylance,改善 VS Code 中的 Python 体验](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247489006&idx=2&sn=58f6dbe10cba98cebab0fd19f124e4df&chksm=e886750cdff1fc1a6cfe249a2f76213fef1a789f4a5006704e33d00b9abadde489af7052a8a9&scene=27#wechat_redirect)
+
+7、[VS Code 连接远程服务器运行 Jupyter Notebook](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247486773&idx=2&sn=75b2453513b1445d9881f5ec8c1f44d1&chksm=e8866dd7dff1e4c167b27eccec9a6469b7988d6b05010160247b3e1a549764201807212ed581&scene=27#wechat_redirect)
+
+8、[GitHub 发布重磅更新:你电脑上的 IDE 可以删了?!](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247486295&idx=1&sn=e5bd46de0616fad6105d2e46a359cd3d&chksm=e8866bb5dff1e2a37058e1cbc502863f8b8a885a493b7ef9f5e442a1b554ffeb0ae09d77026e&scene=27#wechat_redirect)
+
+9、[用 VS Code 写 Python,这8个扩展装上后无敌了](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247492505&idx=1&sn=ba354ab86500280d46038d2fa2f3572b&chksm=e885837bdff20a6dd2d4411719e44a3b88371b5006e9dc2b2580e6862f3161e70cb339240512&scene=27#wechat_redirect)
+
+10、[有了这个VSCode神器,从此爱上调试代码](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247497025&idx=2&sn=91734e86eb03b0ad8b299fa05fb89896&chksm=e88595a3dff21cb5638a874e6ea75aefa8539eb832e239d8103fd66584d1276edbe1d616c4fb&scene=27#wechat_redirect)
+
+11、[用 VS Code 写 Python,这几个插件是必装的](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247496552&idx=1&sn=ab85fc16d53f5d6bbcb0c030cae51402&chksm=e885938adff21a9ca75d485aab0980cb5cb7363d4eadaf0e1867dbb955c7296d1e16f7c7c367&scene=27#wechat_redirect)
+
+12、[出炉了! 2021 年将火爆的 10款 VSCode 扩展插件](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247497928&idx=1&sn=0ec6994930f1cf8bd3a1ed8d8404eb18&chksm=e885982adff2113c94ba7c0ddc989c174daeedd159359d9289b48006e0c6b1d0aa8b87980ef1&scene=27#wechat_redirect)
+
+13、[神器 VS Code,超详细Python配置使用指南](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247497249&idx=1&sn=00a13cb6099f487ce3f88f27923d6a3b&chksm=e88596c3dff21fd504d3e469a0eddc00f8febdeb95b7b8c7483dfad7ef84382d2cc1f3b16f2c&scene=27#wechat_redirect)
+
+### 4.3 好用的库
+
+1、[使用 Python 远程登陆服务器的最佳实践](https://mp.weixin.qq.com/s/aRXYAP9D9rgil-0_Etb0SQ)
+
+2、[这么设置 Python 的环境变量,我还是第一次见](https://mp.weixin.qq.com/s/LcjIPZPSg0KbL9X-i6vigg)
+
+3、[太强了!Python中完美的日志解决方案](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485741&idx=1&sn=505c06669baa44873171a3eed81f32b4&chksm=e88669cfdff1e0d91635b75f54998667e04bfb05a1aebae096930bd23acdd9d35ea858861bb2#rd)
+
+4、[如何使用 Python 操作 Git 代码?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485723&idx=1&sn=35511b71347111c00a9e1633309efd13&chksm=e88669f9dff1e0ef7aec03f2bdbddb8408bf12a57a36add441b75e486dd72df13be7629625ab#rd)
+
+5、[用它5分钟以后,我放弃用了四年的 Flask](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485657&idx=1&sn=e21ddc5b670a9b667135df2ca97ec99c&chksm=e886683bdff1e12d9e1d64a7f5bfbc65ca2372711cd9b08ef36ce0cdd99811ce44ff162585df#rd)
+
+6、[整理了 34 个被吹爆了的Python开源框架](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485653&idx=1&sn=edd893dd711b3681aa0bacdf8ab384fd&chksm=e8866837dff1e1216f6fbf413118f839e6e1dc5ed7b45117df55f13e25670c5ffba34966abff#rd)
+
+7、[ 为了选出最合适的 http客户端,我做了个测评](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485651&idx=1&sn=647c22ac66e469ccc33478457dadf224&chksm=e8866831dff1e1279149ff1e3af84bef86d24c524b7b7c14e9c519cf7c4739fa8f10dcc4a8d0#rd)
+
+8、[凭什么 FastAPI 火成这样?看这篇文章就知道了](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485891&idx=1&sn=82bde3e83435ebb6beb7e3581f12f7b0&chksm=e8866921dff1e0376befb029e9f1d81f33b0fa55242182f8de20d2a6e6b0cce0b5efd02f9aef&token=2013245174&lang=zh_CN#rd)
+
+9、[要想 BUG 变得富有美感,这个库你一定要看](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485889&idx=1&sn=0258eb138dce2c71b533d340770d5d09&chksm=e8866923dff1e03548dee4f780b410c22b51e1c3ffde3ef814f6a9b94e49af941fc04c79a660&token=2013245174&lang=zh_CN#rd)
+
+10、[如何使用 Python 输出漂亮的表格?](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485841&idx=2&sn=705c123abed5441a52f8640b65f3c400&chksm=e8866973dff1e0650e4b2e025d0e86b3aedeb7ea8f68ad560f37cb033f36660b6eee15fb3ce4&token=2013245174&lang=zh_CN#rd)
+
+11、[用 Python 写出来的进度条,竟如此美妙~](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247492108&idx=2&sn=2fa1e31f9684df6d6f3da21aa4cba6e5&chksm=e88582eedff20bf886c29d480245d835c0a3fbea49e54909abbeb703bb7149d4e3444202e97c&scene=27#wechat_redirect)
+
+12、[推荐一些能提高生产力的 Python 库](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247491947&idx=2&sn=765ca757e0aee7fd4dd3f7779f409ff3&chksm=e8858189dff2089f74a97972c1f213291a8680a766b328a74c609f465f7dcf16c324df57a660&scene=27#wechat_redirect)
+
+13、[规整字符串的数据提取神器:parse 库](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247491617&idx=1&sn=b77cd25d6c92f86c1492f913edcb51c7&chksm=e88580c3dff209d500525bbc81464034ca431c92603b5ec83046faaac7ca4c012034964805e0&scene=27#wechat_redirect)
+
+14、[微软再出神器,这次终于对 Python下手了](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247491407&idx=1&sn=0044bf683ddc4799ef2e7c04fb75ebf8&chksm=e8867faddff1f6bbbae3e716e7b686eeb4a5b49f19a2fb2b8743ae2e641ee529189d313f6fe8&scene=27#wechat_redirect)
+
+15、[这个 Python 库有点黑科技](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247491241&idx=1&sn=700979655adce734f5300e7ee380d4ab&chksm=e8867e4bdff1f75d355b34ca8561844c920a2434357350e06a87c99cdbdfb094ee29e7957988&scene=27#wechat_redirect)
+
+16、[适合 Python 入门的 8 款强大工具!](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247491088&idx=2&sn=23cd3b06b8117a5d996d258dd9c0eb23&chksm=e8867ef2dff1f7e479ad5a633295f1be0fc272f6b4de2526c21129b2dee23cb517e51b508f8f&scene=27#wechat_redirect)
+
+17、[这些Python库虽然冷门,但功能真的很强大!](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247490868&idx=2&sn=7bda9c9021cd26eb43d752f6ff84dbe7&chksm=e8867dd6dff1f4c0398b427f13787c99f24cea2a7b2c7b10fbc3316b0215b3ea861f197e604f&scene=27#wechat_redirect)
+
+18、[那些被低估了的 Python 库,看看你用过几个?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247490782&idx=1&sn=2c710eca844da200dac14ab017ffcdb3&chksm=e8867c3cdff1f52a86c291e34e458f57bd5572c6fdeb75cdb84fdfba2d9bb1731067dca7d195&scene=27#wechat_redirect)
+
+19、[用 Python 写出这样的进度条,刷新了我对进度条的认知](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247490326&idx=1&sn=867061f4ad793e957161c30be77ef9bb&chksm=e8867bf4dff1f2e2f1cdc5af4366540d33fc0edfc52810af09b82a590fcd6ec4c5c2d7a528c6&scene=27#wechat_redirect)
+
+20、[少有人知的 Python "重试机制",请了解一下 tenacity ](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247489274&idx=1&sn=7ddc4505f932b386ee22c7873ff5df1a&chksm=e8867618dff1ff0ea5aa55c6acfeb85944028f1bb0be7fb2697f4ec018de297579b797ce048b&scene=27#wechat_redirect)
+
+21、[一个极具意义的 Python 前端开发工具](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247489090&idx=1&sn=1e7ee21422440dc32d7a9a40eaab6c4e&chksm=e88676a0dff1ffb6731df2996d3a4011eebdd8af4926920eefb30d086fa909943179e83d8957&scene=27#wechat_redirect)
+
+22、[非常实用的 Python 库,推一次火一次](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247493413&idx=1&sn=2405d851af05549d5cb5f79353fdc393&chksm=e88587c7dff20ed1c2f93c9a042b293eae294f17bf13cbbebdf50682c8fd24e09e4647dbbe6b&scene=27#wechat_redirect)
+
+23、[新一代 Notebook 的神器出现,替代 JuPyter 指日可待](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247493296&idx=2&sn=075bc9476933eec961bc2e9a7245d2d0&chksm=e8858652dff20f44a4d9cb30ad03841bc991a0efc2a63e63b04d5c8f68b3c6a67fbfa884551e&scene=27#wechat_redirect)
+
+24、[使用 Python 下载的 11 种姿势,一种比一种高级](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247493230&idx=2&sn=8d356b9e0a9d597fbca1ef6ea2f8ca4d&chksm=e885868cdff20f9a2d220f18ecc27909c1217568728f213f492bf566578da5742190bcff4862&scene=27#wechat_redirect)
+
+25、[一个非常好用的 Python 魔法库](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247493501&idx=1&sn=fc2575646311350cb47f6caed6102539&chksm=e885879fdff20e89c9600b6147885523fa0dbb7182fdf458c5aad5cd76ba6b415d25694f6bed#rd)
+
+26、[终于来了!!Pyston v2.0 发布,解决 Python 慢速的救星](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247493602&idx=1&sn=3ba266f9460d94eb4d16c50bffe3ca94&chksm=e8858700dff20e1621fa34dae0b983c38fbcb5575d03f4c951c34a6d0a5f3b81e6c61fddd68a&token=58703854&lang=zh_CN#rd)
+
+27、[献给 Python 开发人员的 25 个最佳 GitHub 代码库!](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247493878&idx=1&sn=f476bbed89543828b54e7c9fcd93abbb&chksm=e8858814dff20102b366afa61e870c2d82d3f8fd332dbf39f3824867a3f8b5e7a5ad4b0bfb89#rd)
+
+28、[求你了,别再用 print 调试代码了](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247494015&idx=1&sn=dc0dd7179d00d074e3013658760fe41d&chksm=e885899ddff2008be8fde435d8ed2fb80057386aa13f8eee273fd02e8c1ca99201130881380e#rd)
+
+29、[爱了爱了!0.052秒打开100GB数据,这个Python开源库火爆了!](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247494268&idx=1&sn=db20e8232caa59512c852dcd723e3a46&chksm=e8858a9edff2038857440f061ec0cef305011d967dfd738df8187f10a2f44d90e3fd7c07e946&scene=27#wechat_redirect)
+
+30、[用 Hypothesis 快速测试你的 Python 代码](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247494299&idx=2&sn=d8af7430ff62c90179491a097a5b43fd&chksm=e8858a79dff2036fae8f7ec05e6827c52cb27149d00a32f9a7dea9ab0e8ed1736fe4b0b3495c&scene=27#wechat_redirect)
+
+31、[微软最强 Python 自动化工具开源了!不用写一行代码!](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247494621&idx=1&sn=61d9f8b7557cb2ff517fde1a414b219d&chksm=e8858b3fdff2022954d383579b6392362e99e99af4a6309028dfa6ddd7bfc90a02c85d599b50&scene=27#wechat_redirect)
+
+32、[Python 中 Mock 到底该怎么玩?一篇文章告诉你(超全)](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247496880&idx=2&sn=88899b73e7ca5f261be2b0ab245703ac&chksm=e8859452dff21d4473f5a73b7d87a800ce177581d94028b7911c0187b3f92bd2762d03195126&scene=27#wechat_redirect)
+
+33、[这款 Python 版终端资源监控器,火了!](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247496153&idx=2&sn=8b53b63e04b2eff07a16bb8ef641aaca&chksm=e885913bdff2182dd176a5f6b1e324ed18f788830037c649915dc24df289139fd402e97c91b5&scene=27#wechat_redirect)
+
+34、[谁是 2020 年最强 Python 库?年度 Top 10 出炉!](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247496075&idx=1&sn=f05417a3c7adb638d394741a0b47c21e&chksm=e8859169dff2187ff80b3fd056160ae9d25009f7b373f82a5bc2c7d6f104a5f12ee52a623a3a&scene=27#wechat_redirect)
+
+35、[Python 算法模板库,Pythonista 找工作利器](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247495227&idx=2&sn=c7899ab854da814888ef853023d8b74c&chksm=e8858ed9dff207cf571d1819770952180f9c882b63e62025e819fe25538fe4f3a3148d73c228&scene=27#wechat_redirect)
+
+36、[终于把所有的Python库,都整理出来啦!](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247498689&idx=1&sn=66743481223ff48bcb5c8ed257b95754&chksm=e8859b23dff212350492116b1cdc3527f969fbb62cac8309ef91932e02842f35b5f7f4d08661&scene=27#wechat_redirect)
+
+37、[Python 超级强大的模式匹配工具—Pampy](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247498439&idx=1&sn=1613fd0fb2154bb3a9b5caefb1225b41&chksm=e8859a25dff21333272d75f14badb8491295351b0b3542db7cc1506a7462359ed297ce228710&scene=27#wechat_redirect)
+
+## 05. 网络基础
+
+1、[点亮你的 HTTPS?原来这么简单!!](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247492265&idx=1&sn=46b095516bf3c0ebfc367ab64f8fd42c&chksm=e885824bdff20b5db2861a411a07653135ffbafaa1e772588bcf86cf9ad54433a0ff492355ad&scene=27#wechat_redirect)
+
+2、[手绘 10 张图,把 CSRF 跨域攻击、JWT 跨域认证说得明明白白的](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247489610&idx=1&sn=c9442c4ad370c318ea7ebafb26208b9c&chksm=e88678a8dff1f1bec43cd601b0c0e3eaff32d168dc2ad69f6273e27f2e6d72b49b7585046057&scene=27#wechat_redirect)
+
+3、[网络出了问题,如何排查? 这篇文章告诉你](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247488835&idx=2&sn=edac7045eb00479481365334ba198f50&chksm=e88675a1dff1fcb76f10049aa97ade7c0669fc1a8c32649a810c1d43b37c7dbe964c17b0730f&scene=27#wechat_redirect)
+
+4、[肝了三天,万字长文教你玩转 tcpdump,从此抓包不用愁](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247488684&idx=1&sn=9a9d717a6c00f978c6575cb934c5c942&chksm=e886744edff1fd58ebb593338484013442c8c2f481497d6c5246cabce3ad43c1ee28299f9098&scene=27#wechat_redirect)
+
+5、[三张图彻底搞懂 iptables 和 netfilter](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247488534&idx=1&sn=44856bba34b906930780535a7000a5af&chksm=e88674f4dff1fde263c119bb66a5fa879e6c07b12d50035552dbd529757bedc45a3a41823013&scene=27#wechat_redirect)
+
+6、[tcpdump / wireshark 抓包及分析](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247488505&idx=2&sn=5e7d7a204c9eef4537fd352090086685&chksm=e886731bdff1fa0d3ba2c9a951fdbd849858a58ea630adab247f3b8b3ff7e76d7de022a8270d&scene=27#wechat_redirect)
+
+7、[100 个网络基础知识,看完成半个网络高手](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247488490&idx=1&sn=a8725e200b3ac351ab4f0a2b6d112199&chksm=e8867308dff1fa1e1d55c89b691394902d5c7f8965063acd8551ad14269135d603f7bef22281&scene=27#wechat_redirect)
+
+8、[网络知识扫盲:扒开 TCP 的外衣,我看清了 TCP 的本质](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247488180&idx=1&sn=09526224732ebfcccb52847f27298c70&chksm=e8867256dff1fb40c9f47bafd0e87a9237c5a9ebf33c8a3d0a598276b496d29cdaa3fbff8d26&scene=27#wechat_redirect)
+
+9、[网络知识扫盲:一篇文章搞懂 DNS](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247487859&idx=1&sn=55c2c40b807a6bd7036763e99954f063&chksm=e8867191dff1f88743bf88b809471926245a0ec3ef9c9d2c9f311d99533ed5656b7b3617431c&scene=27#wechat_redirect)
+
+10、[路由器里的广告秘密](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247489132&idx=2&sn=4cf42aeef93396f7665a06b2d1dc7fba&chksm=e886768edff1ff98bbd9572d85ddd97ef91ad8d19fdd951c15520cd9756d2c40a53d7cd8111c&scene=27#wechat_redirect)
+
+11、[10张动图,让你搞懂计算中很重要的名词](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247487331&idx=2&sn=70146e780ca63c55ca3446a5e238b487&chksm=e8866f81dff1e6974610866f756b099e0b8bed5389cfa594f4b94b6092ae31db305b1485aadd&scene=27#wechat_redirect)
+
+12、[数字证书、签名到底是什么?这篇文章讲得太好了](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247493263&idx=1&sn=8b26b39da435b619d1344b4aca79b9f7&chksm=e885866ddff20f7b936601facff760969e1ba36d981a9d26920f0acce65c96562ebffcb7d954&scene=27#wechat_redirect)
+
+13、[为何无法使用 ip 访问网站?](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484976&idx=1&sn=503d75faea7a633729ec90d6f26e21a2&scene=21#wechat_redirect)
+
+## 06. 实用工具
+
+### 6.1 Linux
+
+1、[值得收藏的 14 个 Linux 下 CPU 监控工具](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485356&idx=1&sn=9dc2f440299ad179e1874febdffa04d6&scene=21#wechat_redirect)
+
+2、[19 个没什么用,但是”特好玩“的命令](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485142&idx=2&sn=97b0f02f8f0153b1c7ba4899359d55e4&scene=21#wechat_redirect)
+
+3、[相见恨晚的15个 Linux 神器,你可能一个都没见过](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484887&idx=1&sn=3473df33893a23b4de1e176985a5265e&scene=21#wechat_redirect)
+
+4、[这 22 款 CLI工具,每一个都是精品](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247491148&idx=1&sn=92b194ad574e8b793ebb608319c7d1df&chksm=e8867eaedff1f7b8e257ce6e80890cef6aa72b9078d20a0e1f0241a1c05f660c69891b6a7d12&scene=27#wechat_redirect)
+
+5、[Github 热榜项目:如何让你的终端酷炫到没朋友](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247488641&idx=2&sn=c8ea38a8e216f123ca320d3ec8449bc0&chksm=e8867463dff1fd7522e9556f3ca97bb691ea92625ca664919dddabd35ead37dbd06a16b75b17&scene=27#wechat_redirect)
+
+6、[如何在1s内创建上百G的超大文件?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247487906&idx=1&sn=a892ae3f3ab0ed0a2a20cf5d51ff1c65&chksm=e8867140dff1f85674d8c01df3c3d4a92cc75cdd34c68c3b86f8c5eb660f500f033ed35deef8&scene=27#wechat_redirect)
+
+7、[千万不要运行的 Linux 命令](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247486845&idx=2&sn=31c1ba636db4d687b16b10981833bfbc&chksm=e8866d9fdff1e48976cab2cb62e3b4a4e2e373aeaa0e961847e49cd13ec4ea417b5162fa165b&scene=27#wechat_redirect)
+
+8、[不想装系统?这8个网站让你在线体验 Linux](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247486303&idx=1&sn=fc6c9b73fcb12c5c0c8257e223c8f657&chksm=e8866bbddff1e2ab5edf8b3d4783bcc65975c0385c140d1c954e7d4949e334b9d6ed92ae98a2&scene=27#wechat_redirect)
+
+9、[比虚拟机更轻量,比 Docker 和 WSL 更简单的 Linux 环境](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247498054&idx=2&sn=1aa6a118f214f3488c1188cdb2471834&chksm=e88599a4dff210b273db8dffda010bca3f1eb15219b7c268ea941bf1d036cec3fed8fac9f3db&scene=27#wechat_redirect)
+
+### 6.2 版本管理
+
+1、[关于 Git 和 GitHub,你所不知道的十件事](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485232&idx=2&sn=c3314e79af9029bdd76ae187f7b4eb19&scene=21#wechat_redirect)
+
+2、[关于 Git 图谱,这一篇文章讲得很细。](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485179&idx=2&sn=a9f0a8a669d70c0dccedf71a530d1d93&scene=21#wechat_redirect)
+
+3、[如何巧妙处理 Git 多平台换行符问题(LF or CRLF)](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485333&idx=2&sn=4e8e95e5cb12d27ea6b510aaaf15b4c0&scene=21#wechat_redirect)
+
+4、[这 7 个高级技巧,不会还怎么玩GitHub](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484940&idx=1&sn=9ad168943b03dff09d53e5d73e13ed46&scene=21#wechat_redirect)
+
+5、[ 神器!这款VSCode插件能填满Github绿色格子](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485595&idx=1&sn=65df21ab0b4042953dd8257bea8b1848&chksm=e8866879dff1e16fff97584467f2f1728f9c2da007894ae062e9201d53b82a30054036f8e81e#rd)
+
+6、[Git 中冷门却又非常需要的高级用法](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485890&idx=2&sn=528df28585ec16114324e391dbfebab4&chksm=e8866920dff1e036d5da7f5dfe3c944ef8b13d72f022d72b209117120c7e27ebb71036b99893&token=2013245174&lang=zh_CN#rd)
+
+7、[别乱提交代码了,来围观下大厂的 Git 提交规范](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247486877&idx=1&sn=f14a8add6cee89f8312c7077e4e1ed22&chksm=e8866d7fdff1e4691e6b3627fdd7963942d4d568a1e1ba1d7e8ba81944e6afaadc6923429006&scene=27#wechat_redirect)
+
+8、[盘点提高国内访问 Github 的速度的 9 种方案](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247491802&idx=2&sn=30df1c954dd8505639c6cd71c0e37d76&chksm=e8858038dff2092eed0b603635b648f2c52d720e98536aee1d50b774c9feac6b25fec6ca9a7c&scene=27#wechat_redirect)
+
+9、[工作流一目了然,看小姐姐用动图展示10大Git命令](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247486154&idx=1&sn=9a409d493eb789c66cb5c3596195582c&chksm=e8866a28dff1e33e13148dbb98dd8a3899392481f4806805bc96de3b6dc7a954ef3eb899bace&scene=27#wechat_redirect)
+
+10、[如何用 GitHub Actions 写出高质量的 Python代码?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485985&idx=2&sn=ec7a102012096847b8fdf7156d9260f3&chksm=e8866ac3dff1e3d53d2ae587e0769b1c42ab0176a7982751fc9a740ae45fe1a592691f373dcc&scene=27#wechat_redirect)
+
+11、[连 Git 都玩不转,还写什么代码?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247493645&idx=2&sn=d9408f63f9afccd28482e41efe2ba11e&chksm=e88588efdff201f9d3cc513591ad0c7b791616e8ca10a7b091038094b8b52eb1f2585c22e799#rd)
+
+12、[5 个 Git 工作流,改善你的开发流程](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247494114&idx=2&sn=f75412a52b9c36e268decf013fffa6de&chksm=e8858900dff2001686dc51e3227a9565f7e97033539a1f2af3d9f38aad87b55a8b5d2ea6d405&scene=27#wechat_redirect)
+
+13、[提高国内访问 GitHub 的速度的 9 种方案](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247498432&idx=2&sn=d7f398a5598e9dd6636961387a64860f&chksm=e8859a22dff21334695d1495ead5adaf9a8102d1ba85bdfe0cd8b51fc6a8db08742bec95819e&scene=27#wechat_redirect)
+
+
+### 6.3 数据库
+
+1、[写给程序员的 MySQL 面试高频 100 问](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485321&idx=1&sn=30432df24d4e7858a475192bcf233914&scene=21#wechat_redirect)
+
+2、[开发人员必学的几点 SQL 优化点](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485892&idx=1&sn=e0b526002a5568391e75fb0cac60ab0b&chksm=e8866926dff1e0303f2717e95ff801c2550d97812c1a775cc232d48183af802db1be54f26105&token=2013245174&lang=zh_CN#rd)
+
+3、[再见,Navicat!同事安利的这个 PyCharm 的兄弟,真的香!](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247490040&idx=1&sn=754ca33e3ddbde88ee1b7b918e89d39c&chksm=e886791adff1f00c3cd0ac513cae55ac3bbb2732c637433d15bf42c22139dd54ee47f399b492&scene=27#wechat_redirect)
+
+4、[太牛逼了!一款软件几乎可以操作所有的数据库!](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247492595&idx=1&sn=b83bab19f921bcd104caf3ed0f1cf0e3&chksm=e8858311dff20a070cd8063efbca76c74439b18f6c8863f3da14621afb082d246f204ccdba92&scene=27#wechat_redirect)
+
+5、[硬核!15张图解Redis为什么这么快](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247494364&idx=2&sn=c1fcdfda4df97a61cf765f2b5ef1873d&chksm=e8858a3edff20328886dc88e78a2b6ea670825a3e4531d635a1bb5684671c293ac67fa079e68&scene=27#wechat_redirect)
+
+6、[败家玩意儿!Redis 竟然浪费了这么多内存!](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247494605&idx=2&sn=3e7c69fece14132d8c8df75cf282c19f&chksm=e8858b2fdff202393289d3aa73f5702ab53afb27758a64bfe4288e4489d68a896411fb33c7c2&scene=27#wechat_redirect)
+
+### 6.4 Chrome
+
+1、[推荐 8个 超实用的 Chrome 插件](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484925&idx=1&sn=110e4da78a20e8b5721f00bb36ba9d4c&scene=21#wechat_redirect)
+
+2、[没有这 42 款插件的Chrome是没有灵魂的](https://mp.weixin.qq.com/s?__biz=MzU4OTUwMDE1Mw==&mid=2247485016&idx=1&sn=ba38963413ee2926f6f5e69b1427491d&scene=21#wechat_redirect)
+
+### 6.5 Windows
+
+1、[没钱买 mac?一招教你如何让 Windows 秒变 macOS](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247492484&idx=2&sn=d82c0faaf39c420729fba238c65d2fc8&chksm=e8858366dff20a70abbabb14693f265d7fdb1c831cd2b464c9cf1ce8431feb4f07cbb76627ec&scene=27#wechat_redirect)
+
+2、[微软太良心,这么强大的软件竟然完全免费!](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247491841&idx=1&sn=80c8c0247f9100e29f7066c874400755&chksm=e88581e3dff208f503da7dafdee037e8db578186dbef79df648c054f54c6e86b9336ff86bde3&scene=27#wechat_redirect)
+
+3、[双系统的日子结束了:Windows和Linux将合二为一](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247491306&idx=1&sn=dfdbc7e02b54622c2eed5f36501444f0&chksm=e8867e08dff1f71e02038aa720fdd99c524adbc6687c2f32070af638b713a154f0f9767205cc&scene=27#wechat_redirect)
+
+4、[这个只有1.5M的软件,能让你的网速快3倍](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247488856&idx=1&sn=8a96c373b04c4ce7d78c7f87f5928c1e&chksm=e88675badff1fcac0086cb2ec20845821d2441d0909b9a3395b249744217944b1fb8f3e442c9&scene=27#wechat_redirect)
+
+5、[原来,我一直都不会用Windows](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247494412&idx=2&sn=044b47f18f70e5179c5e5c84cb6f4ec4&chksm=e8858beedff202f8728666709b385c476fe41c7d41ed1c63e40afd198db5fe27fbeed2c1639b&scene=27#wechat_redirect)
+
+6、[Chrome 和 Edge最大的威胁来了....](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247495227&idx=1&sn=0175723a1ee9b54d6d40010aa28447b3&chksm=e8858ed9dff207cfc51eef087c07d4f6ee1bd341937c6228605fe9281d5c80ab7e02a7d617f4&scene=27#wechat_redirect)
+
+### 6.6 其他工具
+
+1、[整理了 11 个好用的代码质量审核和管理工具](https://mp.weixin.qq.com/s/DH_TOA3Cr3y37yZ5F4bU5A)
+
+2、[11 个最佳的 Python 编译器和解释器](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485682&idx=1&sn=31b1b68432ca1f1db3866fb0f670270b&chksm=e8866810dff1e106a21c63c1ee198069223047afc72482c1f14ee4f473211aca85273c57d529#rd)
+
+3、[太酷了!用上这 5 款神器后,你写的代码逼格瞬间爆表](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247486248&idx=1&sn=ff17f64475d82e99d32e35f290a49edd&chksm=e8866bcadff1e2dc2ba61ca6a0f63fd753d60e77891ee0ae93207890ee891a9097a6d27b5695&scene=27#wechat_redirect)
+
+4、[我最喜欢的云 IDE 有哪些?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485965&idx=1&sn=3457ed667d64773bd83bdc3432c06dc7&chksm=e8866aefdff1e3f9407dce655688e2899c177c17a6472b0d50c15e2454cd6450165aee81877d&scene=27#wechat_redirect)
+
+5、[整理了6个好用的程序员开发在线工具](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247489186&idx=2&sn=6099d58947654ddb35b43d7067d574d6&chksm=e8867640dff1ff56fb33df9d0de1ea820f14f3690d11184b1fcf02fa9480450a0b5639a754df&scene=27#wechat_redirect)
+
+6、[Github上这些可视化面板也太好看了吧](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247488270&idx=1&sn=459867f1761d29e4a46f2e450e3ea975&chksm=e88673ecdff1fafaa0f1f89c9739b928e0227ac54059d4ff96c15faf8b88cd3752a929e018e1&scene=27#wechat_redirect)
+
+7、[腾讯终于良心了!桌面混乱终于有救了](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247492724&idx=1&sn=58cc2c03fffcbc87995a9927b6168ba7&chksm=e8858496dff20d80365246706c32bfebceb008f28cc04cb420cddd73cd50446495d02204099b&scene=27#wechat_redirect)
+
+8、[奇技淫巧:在 ssh 里面把服务器的文本复制到本地电脑](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247493550&idx=2&sn=2619c1402e8951ff812c69994d922eb0&chksm=e885874cdff20e5adfcd9a95bf199cfcb73fbc7962c6de11c42240e3741eb5b5de75cc711649&token=58703854&lang=zh_CN#rd)
+
+9、[真神器!不用手写一行代码就能做网站~](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247494412&idx=1&sn=6789d2aa73a05671c219db659d5ad739&chksm=e8858beedff202f880c3255210e7880bb7c56ad378a1f7b9b82f4e2f24126e4ef8ca7c435103&scene=27#wechat_redirect)
+
+10、[用 Python 使用 Google Colab?岂止是炫酷](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247494533&idx=2&sn=58e2fd5fc63936d89853c5d550293d00&chksm=e8858b67dff2027179fd492d796d537bad57caf830e0aa3fbd51061cd91bd7c7f3f2ce56eb80&scene=27#wechat_redirect)
+
+11、[自从用了这个神器,我再也不想写代码了...](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247498473&idx=1&sn=448af801d90b29a69cb79b4089868f97&chksm=e8859a0bdff2131dd9fa0d6923fb8ae1de9e7311691a7558499cf995c0e4be8fd036031ee1c7&scene=27#wechat_redirect)
+
+12、[牛逼至极!用这个神器看代码太舒服了](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247497577&idx=1&sn=d0061deb47fa2c6441eb7dd11272af5d&chksm=e885978bdff21e9d4bc0a8747d9b2f6b04288095c7a019cfe1214a4ae382de657076313387bf&scene=27#wechat_redirect)
+
+## 07. 代码优化
+
+### 7.1 算法讲解
+
+1、[策略模式:商场促销活动背后的代码哲学](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484937&idx=1&sn=61b81dddfb20d80060fee5f22ab28d5f&scene=21#wechat_redirect)
+
+2、[算法教程|八张图带你轻松理解经典排序算法(上)](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485042&idx=1&sn=b9e8503905be5a960119c2893995fb6d&scene=21#wechat_redirect)
+
+3、[算法教程|八张图带你轻松理解经典排序算法(中)](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485041&idx=1&sn=12d05e0316c8866e3876c3ed484ced15&scene=21#wechat_redirect)
+
+4、[一次忘记密码引发的算法思考](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484981&idx=1&sn=96f49ab2225c61b45601b68521135dd6&scene=21#wechat_redirect)
+
+5、[程序员走楼梯都会思考的一道题](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484993&idx=1&sn=36cc1466ed73af2fcf4a8430368a62ee&scene=21#wechat_redirect)
+
+6、[以为是高性能神仙算法,一看源代码才发现...](https://mp.weixin.qq.com/s/L_OvCcpIFKBLckLBvit8UQ)
+
+7、[ 用Python手写十大经典排序算法](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485494&idx=1&sn=1580e9fc17086bc83e30b0fa4c850bd8&chksm=e88668d4dff1e1c27a26b31fda2f0e413be83519aa9dd0e3c8d3cfd08a2478eb2596ed7ba535#rd)
+
+### 7.2设计模式
+
+1、[单例模式告诉你只能娶一个老婆](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484921&idx=1&sn=493d0a492277ecb223ddfff9de8720ff&scene=21#wechat_redirect)
+
+### 7.3 代码优化
+
+1、[看了同事的代码,我忍不住写了这份代码指南](https://mp.weixin.qq.com/s/goqj1YVdKV171LLpjtXruQ)
+
+2、[怎样才能写好一个 Python 函数](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485416&idx=1&sn=85b14857e795a92985230ba3f51b73f6&chksm=e886670adff1ee1c7f4266d4e3b931d2e3d1de4dff30dc869eae2cd174da7f54080787f60a1b#rd)
+
+3、[三个异常处理的好习惯](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484947&idx=1&sn=2bd31279dd9714045ed4dc5481b44208&scene=21#wechat_redirect)
+
+4、[超实用的 30 段 Python 案例](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484866&idx=1&sn=9c85dd1b58f319bb0339e2db577a0be6&scene=21#wechat_redirect)
+
+5、[删除系统 Python 引发的惨案](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485386&idx=1&sn=59eed0894159f4d0599be3fb787b5201&chksm=e8866728dff1ee3ef9fb7a0464df481306be11d9aeba944804e7ea74e660501d42e159ada7fc#rd)
+
+6、[提速72倍,在Python里面调用Golang函数](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247492536&idx=2&sn=360f13b4678fcc6a6f14ee6221474285&chksm=e885835adff20a4ca2025c926c754b9bf6accc6b5b7021f97878163491122ea97d2721c279f6&scene=27#wechat_redirect)
+
+7、[这一行代码,能让你的 Python运行速度提高100倍](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247492321&idx=1&sn=87dc6b52a0c63b8326bcc50b2c7b3a68&chksm=e8858203dff20b15d1e4730e2851d7523c30db9b404fdfa5b1b19978a1e326e2205e3d6bf7ac&scene=27#wechat_redirect)
+
+8、[三行Python代码,让数据处理速度提高2到6倍](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247491111&idx=2&sn=d27c6398b82749a20c46490b8fdd900f&chksm=e8867ec5dff1f7d3e9f95f221a2f33003bec76da1f0c002f97a59c7887cf460c887e1ad5a579&scene=27#wechat_redirect)
+
+9、[一文教你节省 90% 的内存占用](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247489739&idx=1&sn=6589629f52a3a6ec18e67a3ca4952616&chksm=e8867829dff1f13fa87b08ecd09ce319dbd955c8ee15b44c60509e171f1920a6da600075dcd0&scene=27#wechat_redirect)
+
+10、[写出漂亮 Python 代码的 20条准则](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247488402&idx=1&sn=231c2954b0ccca44a18268ab80ddbe88&chksm=e8867370dff1fa66a12d6af4adb66a1d80cbccbf9093c3a8b966a406bf5d8fc005a08166ef45&scene=27#wechat_redirect)
+
+11、[阅读优秀的项目代码,几点实用的经验分享](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247487922&idx=2&sn=c330d9b4c7bbc1b838e8a9fe90283c27&chksm=e8867150dff1f846628f181ff9687ebf91ba5d06f7721a67bf746698c6f02ea398cdc33c5a0d&scene=27#wechat_redirect)
+
+12、[一行 Python 代码实现并行](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247487299&idx=2&sn=5dffb95993b6fd87c6a3dd1a56b56814&chksm=e8866fa1dff1e6b78239ffa2fe8d124e4ee3af2b7b71f8e9593bdeae2221fcd2afa2b5773e26&scene=27#wechat_redirect)
+
+13、[代码被反编译了?这两个小技巧能帮到你](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247487287&idx=1&sn=05c7ca030257db25623e98ed5d302878&chksm=e8866fd5dff1e6c3219dc6d78717411f716bfa38d89a34ed64a88c0702765258fd34b4edd428&scene=27#wechat_redirect)
+
+14、[一份可以令 Python 变快的工具清单](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247486107&idx=1&sn=83a3f95fea5cb2c24d7f44bf11f2acdb&chksm=e8866a79dff1e36ff4e6c8c546568bca0a36ea3de0550bd87a785ed9ce5cbda4a6d84b5b5fc9&scene=27#wechat_redirect)
+
+15、[记一次 Python Web 接口优化](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247486053&idx=1&sn=862e6fbaa8a85a4a10ee35d9eba00196&chksm=e8866a87dff1e391399c252ffcb3168ef1f4c85d5ea52d6f43ceaf81e3f7cb3e548f6de505df&scene=27#wechat_redirect)
+
+16、[翻车了!pyc 文件居然曝光了我的密码](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247487730&idx=1&sn=eed309620be1092cf5af030be6b354ff&chksm=e8867010dff1f90611e155156075c2479bab5c9237e8a514a2c7ffe2a317ba16baa30b76d9c9&scene=27#wechat_redirect)
+
+17、[没有什么内存问题,是一行Python代码解决不了的](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247492724&idx=2&sn=a84245d93d4191f157bac3e6a38d7066&chksm=e8858496dff20d808bb6ce5876bb0669a72722b6f90ec74f3b43de49ca1e07101c410138e113&scene=27#wechat_redirect)
+
+
+## 08. 冷技巧集锦
+
+### 8.1 冷知识
+
+1、[Python那些不为人知的冷知识(一)](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485031&idx=1&sn=e5ab670ce6485a50c4b7eeaee11e6f34&scene=21#wechat_redirect)
+
+2、[Python那些不为人知的冷知识(二)](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485029&idx=1&sn=5db54b5777348e827c274cd932a15a15&scene=21#wechat_redirect)
+
+3、[Python那些不为人知的冷知识(三)](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485009&idx=1&sn=92e268c20c6f01278e220e11397cc2f0&scene=21#wechat_redirect)
+
+4、[Python那些不为人知的冷知识(四)](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485001&idx=1&sn=5a02f8c912518d15a0b96319cf2da7d1&scene=21#wechat_redirect)
+
+5、[Python那些不为人知的冷知识(五)](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484997&idx=1&sn=5b8cf44b62550e1bd67284fb2b0780dc&scene=21#wechat_redirect)
+
+6、[Python那些不为人知的冷知识(六)](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484990&idx=1&sn=bd28368eff832ba2b24a64f637a6d0c8&scene=21#wechat_redirect)
+
+7、[Python那些不为人知的冷知识(七)](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484874&idx=1&sn=abffab70b1e265ede1fd3b994dedab90&scene=21#wechat_redirect)
+
+8、[Python 之父为什么嫌弃 lambda 匿名函数?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247492291&idx=2&sn=3cfdde7bc5e7b55aae00a30c63abe562&chksm=e8858221dff20b3719dfed632d181b95599b843006ae0c2186b5c69593ee7c22c5a5d2c82509&scene=27#wechat_redirect)
+
+9、[Python 到底是强类型语言,还是弱类型语言?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247491532&idx=1&sn=a86bccc1d270390331c36205d3e46497&chksm=e8867f2edff1f6388d80289bff567445c2c9c9414efec74e4a308f7e8a1cabf662c8fc0a6cc0&scene=27#wechat_redirect)
+
+10、[Python 为什么不支持 i++ 自增语法,不提供 ++ 操作符?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247489219&idx=2&sn=0dfa7ae7d4dcdda538b35bbea80b34e4&chksm=e8867621dff1ff372969bf3901835e67a1bfa7cb72709b491987fda3daa0e5f8d15a86c2b6b5&scene=27#wechat_redirect)
+
+11、[Python 为什么没有 main 函数?为什么我不推荐写 main 函数?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247489175&idx=2&sn=2f8f9fb4b31c91ef71d1f34841da6450&chksm=e8867675dff1ff63e6e1f28eec9f18c41c30c050118eb65cde6960e5f6ee5866d7515f2b0040&scene=27#wechat_redirect)
+
+12、[Python 列表的这 8 个实用技巧,你都 OK 么?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247489032&idx=2&sn=38d3985803cb5b6ed10e4cc11972eff7&chksm=e88676eadff1fffc88a3a3a2e3815b2abff43318c597344857c9c5f7806b6b0e6c457b33842d&scene=27#wechat_redirect)
+
+13、[Python 为什么不用分号作终止符?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247488816&idx=2&sn=6bc8aa32d9a266cbffabcd87664e75fd&chksm=e88675d2dff1fcc4e845a90903bda5dfe59262a2b60533045a03e574076e92aa6f30974a73f5&scene=27#wechat_redirect)
+
+14、[Python 为什么推荐蛇形命名法?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247488517&idx=1&sn=8b449c2d7c831600be6c5d57e9cb0837&chksm=e88674e7dff1fdf1ab8092ac3cdd2f3978ee6b219daa18a5407c390f62e11448c39455437bb0&scene=27#wechat_redirect)
+
+15、[Python 为什么用 # 号作注释符?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247489657&idx=1&sn=820a070f05096dbe2bdb979fc9492cdb&chksm=e886789bdff1f18d420ee3efa8f85468303690232c4005f775191b0a84a1921430e37aa7e74f&scene=27#wechat_redirect)
+
+16、[!/usr/bin/python与#!/usr/bin/env python的区别](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247486008&idx=2&sn=8637cdc12799f084cd5ecdba9534fb76&chksm=e8866adadff1e3cc49b369dddfaeecc1d9b51f13de1f2733266b2f9fc8426d9e3944a15eb074&scene=27#wechat_redirect)
+
+17、[pip install 和conda install有什么区别吗?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247487164&idx=2&sn=18de23de9c79f3c20e5a432bb675c262&chksm=e8866e5edff1e748cb6c83d2f4368cbab805fd7bfd41820b60da22061818e8d84548ac14714c&scene=27#wechat_redirect)
+
+18、[Python 什么情况下会生成 pyc 文件?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247486071&idx=2&sn=533257b9b6156bd5588199b349266a3d&chksm=e8866a95dff1e383991b311dc73994ca103c380433a620c5bdf9f8d09b1c49daa74dce6ea941&scene=27#wechat_redirect)
+
+
+### 8.2 冷技巧
+
+1、[让Python中类的属性具有惰性求值的能力](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485759&idx=2&sn=e32b66c4666f96f84572738549a47a24&chksm=e88669dddff1e0cb0a4b6e6e965c9570b273236fa58a54351ae3b118d6a4bcbd52e452109a63&token=1148998814&lang=zh_CN#rd)
+
+2、[ 小技巧:如何在 Python 中实现延迟调用](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485689&idx=2&sn=43e35ff93918ef090480672d4685bce3&chksm=e886681bdff1e10dd0edee95c73a977da51fdeb514dcf8c3bb88f7f9aacc22ce155f45e84268#rd)
+
+3、[整理了 18个 Python 高效编程技巧](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247491418&idx=1&sn=89c35f5d69a54196bd7f32a64171e05d&chksm=e8867fb8dff1f6aee18414c89c31ce118550ba11b645e2e4f6014af4d15d319decc000a70c91&scene=27#wechat_redirect)
+
+4、[Python 程序报错崩溃后,如何倒回到崩溃的位置?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247488434&idx=1&sn=e2b4ca12b228c16e944ff36abec53e22&chksm=e8867350dff1fa46ee7799e460d2b304dbb0c227d33a39d18099761c205319d99a9b4135f9fa&scene=27#wechat_redirect)
+
+5、[如何用简单的位操作实现高级算法](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247487151&idx=1&sn=5376f42c8990027132deb52a6b66307e&chksm=e8866e4ddff1e75b3af85905177130d23118254d42e9452930cc784b209cdf294d9bb73aa247&scene=27#wechat_redirect)
+
+6、[如何限定Python函数只能被特定函数调用](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247490777&idx=2&sn=6572f957117e0a0256da2b85c7221a37&chksm=e8867c3bdff1f52d045cadb962a2da16b015d8455147afe374cf3067dd89c580e1f9514d57d0&scene=27#wechat_redirect)
+
+7、[pylint 除了检查代码风格,居然还能...](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247491737&idx=2&sn=f6781d158c5af171f1b916ee7b0c7a68&chksm=e885807bdff2096dc1580abbba594ed1cc1c0994c06f6bd5ede233df0593a43842f7af3e2f8b&scene=27#wechat_redirect)
+
+8、[让Python在退出时强制运行一段代码](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247486207&idx=2&sn=5586a204b9537d37c02b9926a01719dc&chksm=e8866a1ddff1e30bb77d72ba0013d870f80e0029546729f0e8c65512c4969f5f0733f60ecddf&scene=27#wechat_redirect)
+
+9、[不使用 if-elif 语句,如何优雅地判断某个数字所属的等级?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247486167&idx=2&sn=a31cbf6d3526bfc860b0daa69889193f&chksm=e8866a35dff1e323ed958858223dda1af6ae0d371be0037c35f9ae6894e776dec1ee53c2a00b&scene=27#wechat_redirect)
+
+10、[涨姿势了,raise...from... 是个什么操作?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247486001&idx=1&sn=b9a89305dae7d4f57105be1d274fae43&chksm=e8866ad3dff1e3c51b5fa132147b9f9c9e4ba14b41c5409e62e2f2696d1389c6ac4ddd1e7bec&scene=27#wechat_redirect)
+
+11、[一些我日常使用的 Python 技巧分享](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247488382&idx=2&sn=f2b090fe47bb56e0463126c69726bf10&chksm=e886739cdff1fa8a65ce4869a8bdb8fbd28396e3fbcdac2a4441b7c58ba5c77cea1765858146&scene=27#wechat_redirect)
+
+12、[5年 Python 功力,总结了 10 个开发技巧](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247488243&idx=1&sn=66d2f534ddcbed582e7ba734b53701f5&chksm=e8867211dff1fb07f2edcebfda9e7863ee57266c15d4276bc89c2ee69baafc46fbc0e85fe703&scene=27#wechat_redirect)
+
+13、[我发现了个 Python 黑魔法,执行任意代码都会自动念上一段 『平安经』](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247490548&idx=1&sn=c71772cc40992d8912fd8a3fa1607038&chksm=e8867b16dff1f20081431362c99fb981d0cd75f2bd5baf981b7db18e08492138aab10f98efb8&scene=27#wechat_redirect)
+
+14、[实用技巧分享:如何批量更新已安装的库?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247487862&idx=2&sn=a1c092467b471db82060b395c341be6c&chksm=e8867194dff1f882da87863f4d4a4ccbd687daa0b4a742dc6d01fd01d0a6ca6c5fb7cc28629b&scene=27#wechat_redirect)
+
+15、[如何导入父文件夹中的模块并读取当前文件夹内的资源](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247493878&idx=2&sn=36c70addd7c0c0640d1285ab590410e4&chksm=e8858814dff201026397c7b08034ad513c4b9e5dcf8307325951ae4077e386f87f5685ca528f#rd)
+
+16、[用 Python 读取资源文件?这个技巧保你涨姿势](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247494041&idx=2&sn=3b3a43157290f0ff542185982aa4b537&chksm=e885897bdff2006dd634e39ccc3c753704ad0f74e2fec95249d0d7f0c30bee85426ef86028e0#rd)
+
+### 8.3 炫技操作
+
+1、[Python 炫技操作(01):条件语句的七种写法](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485991&idx=1&sn=98e17f91d2f66f62c9aa644c03d8259d&chksm=e8866ac5dff1e3d3f6810a3f9e388d46d95945af9748baea0380ecec83c768eaac09738cf959&scene=27#wechat_redirect)
+
+
+2、[Python 炫技操作(02):合并字典的七种方法](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247486080&idx=1&sn=f1c5c4fc5363a1d787b9ae9baba0d07b&chksm=e8866a62dff1e374ad1e5ae2e51bc6cbeb41f631899cde980555ded61be840f0fe6c76c44b7d&scene=27#wechat_redirect)
+
+3、[Python 炫技操作(03):连接列表的八种方法](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247486132&idx=1&sn=3827cf8672d4121b77225dbf9d377d69&chksm=e8866a56dff1e34094f2f834a613800480c0cdb078ab5656640fcb8c445ffa020b175b0f55cb&scene=27#wechat_redirect)
+
+4、[Python 炫技操作(04):海象运算符的三种用法](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247486158&idx=1&sn=0bd0647702a1599082e5bf1710d89e37&chksm=e8866a2cdff1e33ab4465d88497ea3505ad8fe614365eec7592a8c75053a6c8218206807c1a3&scene=27#wechat_redirect)
+
+5、[Python 炫技操作(05):花式导包的八种方法](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247486799&idx=1&sn=1be136d4e45c1b6f8a5eda567aaf259d&chksm=e8866daddff1e4bbddc0164ae795d6c302e36226cb3ba76ffc57e5c993b0861ef4ad693c2a56&scene=27#wechat_redirect)
+
+6、[Python 炫技操作(06):判断是否包含子串的七种方法](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247491171&idx=2&sn=4596043fb72b75a04a778942c61cc2af&chksm=e8867e81dff1f7979e21f58716b3ce3e09f8515829b15acd5169dd3f160e302d617a1b94cb2d&scene=27#wechat_redirect)
+
+7、[Python 炫技操作(07):模块重载的五种方法](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247492817&idx=1&sn=cfb52d1a3f88a61e7b22a63306dac0e9&chksm=e8858433dff20d25f06ac0b5b767121907a4679b9e6da0905694d15c432ba596ce7b202ec7bb&scene=27#wechat_redirect)
+
+8、[Python 炫技操作(08):五种 Python 转义表示法](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247494334&idx=1&sn=9f55122545b7b5d81049af27eff7800c&chksm=e8858a5cdff2034a8158b14669aaf695656c1227428ce86f22b19230769b5b45104b0a885205&scene=27#wechat_redirect)
+
+9、[最全总结:把模块当做脚本来执行的 7 种案例及其原理](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247490904&idx=1&sn=ca3725510c7510965cbc76bd1bf06949&chksm=e8867dbadff1f4acb08c0daf08272ca61757909b0b73a4cde1aa4d86965c149c40f44417f7d0&scene=27#wechat_redirect)
+
+10、[这个 Python 炫技操作千万不要用,别问我怎么知道的?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247491171&idx=1&sn=27d55a4b8b717450f1ab0596263f453f&chksm=e8867e81dff1f7975674ab7fcc0019654ae0e867db045fb575623f91cb143801d03aa365be2d&scene=27#wechat_redirect)
+
+11、[涨见识了,在终端执行 Python 代码的 6 种方式](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247488283&idx=1&sn=24a78834ec3434a90ca7c3e6e972495c&chksm=e88673f9dff1faef908c43a41133f65695a68579e0edb2d7b15f131d9be26bcf919d66a7d9e3&scene=27#wechat_redirect)
+
+12、[Python 炫技操作:推导式的五种写法](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247496358&idx=2&sn=1d4c4463663f546b5768945ce31598f7&chksm=e8859244dff21b52cd0dce2ca366782ea14bf194f66498b07b6125426cc324c21a13be5abd3a&scene=27#wechat_redirect)
+
+13、[Python 炫技操作:安装包的八种方法](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247495287&idx=1&sn=1eb6f14782f17a552320b3d6af5e17f3&chksm=e8858e95dff207836d575464ccc2f1be06b8fa824dcd1589613fe8d6ca48ebb4578f57378858&scene=27#wechat_redirect)
+
+
+
+
+## 9. 云计算
+
+1、[一份面向初学者的云计算通识指南](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484908&idx=1&sn=3085e048da685440408e1bdc54feb4aa&scene=21#wechat_redirect)
+
+2、[ 如何探测虚拟环境是物理机、虚拟机还是容器?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485401&idx=2&sn=8bfb6e1a71e0777a4d5e55f64b55ace5&chksm=e886673bdff1ee2d8acce291e402cd2ae0c7f64e455fdeea158eb53e535ee79b2aadc449935e#rd)
+
+3、[超详细教你如何阅读 OpenStack 源码?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485899&idx=1&sn=7a578a553c67e794ef6ec2f463864a46&chksm=e8866929dff1e03fa8152751967106e9262980db6afdff106fe9e39854d24d25494b3e8a3a4e&token=2013245174&lang=zh_CN#rd)
+
+## 10. 职场相关
+
+1、[一个专科生的 Python 转行之路](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485012&idx=1&sn=751796e63a30d84be01486619b5fb806&scene=21#wechat_redirect)
+
+2、[一个机械生的Python转行自述](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484979&idx=1&sn=6513e940bc7c0677dedf7efff80aa4c6&scene=21#wechat_redirect)
+
+3、[自学 Python后端开发 到什么程度可以找工作?](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485010&idx=1&sn=e76d031f91633f9bfde9da36d8dcf996&scene=21#wechat_redirect)
+
+4、[工作不是游戏,写给编程新人的五点建议](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484984&idx=1&sn=0934415cc48004ae76345dcfb8e33b94&scene=21#wechat_redirect)
+
+5、[Python 从业十年是种什么体验?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485601&idx=1&sn=0dbebc02002ab01a8cd42265822ff81c&chksm=e8866843dff1e15509f2a9d2cd551b2360607cf88e73e5d3036388716ee57949d726dcea209c#rd)
+
+6、[40条提升编程技能的小妙招](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247489765&idx=2&sn=79287da847c25ce0d72db6a33eb0c5a0&chksm=e8867807dff1f111043fe22bbfc776a52331c28abf85c2f658cbbaba3235c4e9d3c070c44d8c&scene=27#wechat_redirect)
+
+7、[我,一个靠GitHub打赏谋生的码农,年入十万美元](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247488934&idx=1&sn=56f51b946908ed89a58b311fc77f47d6&chksm=e8867544dff1fc52e30288685c89e86cefd063d5d37f5b1aba5fb30fd3665cf18346533cad96&scene=27#wechat_redirect)
+
+8、[5 年 Python 的我,总结了这 90 条写 Python 程序的建议](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247487323&idx=1&sn=1b6594d63319faaadefad1f5e178235f&chksm=e8866fb9dff1e6af732304c8f3645cecca8b4bfa4158e45b0ea6c5167c2675bd748ce23e8239&scene=27#wechat_redirect)
+
+9、[Python这么慢,为啥大公司还在用?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247489132&idx=1&sn=55dda8a6a10429b3a7016393bc058493&chksm=e886768edff1ff981d67b2ecfe52ebf5b8dbc0085289e6bd3f8a6c6631ee6fc4a633fb6bc4b8&scene=27#wechat_redirect)
+
+10、[一个中科大差生的 8年 程序员工作总结](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247497523&idx=1&sn=725c1167873927aea519f690f3c106ec&chksm=e88597d1dff21ec709eeba54a9fb35318afcac3cb3b235e0ab831fd40f9917dc2acb40d1bbb3&scene=27#wechat_redirect)
+
+## 11. 通用文章
+
+1、[假如有人把支付宝存储服务器炸了,你的存款会怎样?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485171&idx=1&sn=d6e2e24f68f3c6eb35e880f32e12d4ab&chksm=e8866611dff1ef0791cf5aae05697592cb804010aecd783a8592f7a90a387645fe4b47a376ea&scene=27#wechat_redirect)
+
+2、[手机没网了,却还能支付,这是什么原理?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247493296&idx=1&sn=8dae2411c61b7e5ab4a1fcdf45b86c79&chksm=e8858652dff20f44519c328ba22b4f22c8b2320bc1464319e98d6084697dfab3ef2346a5cf49&scene=27#wechat_redirect)
+
+3、[Chrome 的小恐龙游戏,被我破解了...](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247491911&idx=1&sn=1dcab055e480fe626d50b0324ed5b9f1&chksm=e88581a5dff208b3fbf88017c15ccfa966eec3b25e4804a72401773d7b87d5b6a2bcd8db9ac7&scene=27#wechat_redirect)
+
+4、[深扒微信多开的秘密后,我竟然发现了个 bug](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247489704&idx=1&sn=a33559f22517c744367a660f78dbb25a&chksm=e886784adff1f15c1524f90487d6957c86dee0f5c499528630f43b62320b28e66977afe4d884&scene=27#wechat_redirect)
+
+5、[让许多 Python “老玩家”心寒的 10 大槽点](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247497032&idx=1&sn=0347edefbb9fc47d13c58379130aa327&chksm=e88595aadff21cbc3a6c8ad6752a75ce708244ed517fd9a3230efa2eb783927033977ba26b82&scene=27#wechat_redirect)
+
+6、[别瞎学了,这几门语言要被淘汰了!](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247496522&idx=1&sn=1aaa60b8295378cfcbeaaaf528b13df0&chksm=e88593a8dff21abe290ee23821ed9cebb2aa68148548852319363b4de4663d0a5ab646451e5e&scene=27#wechat_redirect)
+
+7、[是的,Python是慢,但我不在乎](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247497752&idx=1&sn=0d207936a803b414b04d13bb167124ba&chksm=e88598fadff211ecd77676506b5f039c0fbd8bd32eb5f68df2c41a277526304dc89f3d6f7dad&scene=27#wechat_redirect)
+
+8、[当今全球最厉害的14位程序员,说没听过简直离谱~](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247498702&idx=1&sn=8b5a405de0649de1bf52235956840af7&chksm=e8859b2cdff2123a2f6168fb846781f2ba678ac4b70fd01513c2547b14b4d1a053ca0b67e5f6&scene=27#wechat_redirect)
+
+## 12. 明哥的作品
+
+1、[太赞了!《Python 黑魔法指南》终于面世了](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247486834&idx=1&sn=f5b94c3a520624786162f78246e60246&chksm=e8866d90dff1e486a3dae97aaf347e83834c8cf204849e5f9ae23dc9fdf0c470f97e0298cf74&scene=27#wechat_redirect)
+
+2、[《Python黑魔法指南》全新版本 v2.0 上线发布](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247490543&idx=1&sn=449a1c7adfafdd5cf37874ed67620e89&chksm=e8867b0ddff1f21b9d622ba7a53b35143cd7fbb3da0d7c091b3d2cdd71d817a4397a99fa424e&scene=27#wechat_redirect)
+
+3、[明哥写了两个月,这 200页《PyCharm 中文指南》 终于可以发布了](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247491716&idx=1&sn=76a9f3ad5163a9ffdbaf0fefd0259261&chksm=e8858066dff20970fa9988675154fff6a6f84793a80b8ea1d6fe5d5bbb5bc816d2feabc50664#rd)
+
+4、[一个明哥真正意义上的个人网站,现在来啦~](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247493207&idx=1&sn=ced486f99dc7d9ab665849da1016ceb1&chksm=e88586b5dff20fa32db6f3074ee954cbe30b34846c5852d8772184a41e9ad58293947e22095d&scene=27#wechat_redirect)
-这是我的个人博客( [Python编程时光](http://python.iswbm.com/) ),主要写关于Python的一些思考总结。
-
-关于搭建教程,感兴趣的可以查看这边:[Sphinx 搭建博客的图文教程](http://python.iswbm.com/en/latest/c04/c04_03.html)
-## 第一章:基础知识
-- 1.1 [13条Python2.x和3.x的区别?](http://python.iswbm.com/en/latest/c01/c01_01.html)
-- 1.4 [什么是猴子补丁?](http://python.iswbm.com/en/latest/c01/c01_04.html)
-- 1.5 [深入闭包与变量作用域](http://python.iswbm.com/en/latest/c01/c01_05.html)
-- 1.6 [深入理解元组存在的意义](http://python.iswbm.com/en/latest/c01/c01_06.html)
-- 1.7 [15个Pythonic的代码示例](http://python.iswbm.com/en/latest/c01/c01_07.html)
-- 1.8 [新式类和经典类的区别?](http://python.iswbm.com/en/latest/c01/c01_08.html)
-- 1.9 [多继承与Mixin设计模式](http://python.iswbm.com/en/latest/c01/c01_09.html)
-- 1.10 [Python 黑魔法指南 50 例](http://python.iswbm.com/en/latest/c01/c01_10.html)
-- 1.11 [正则表达式必知必会](http://python.iswbm.com/en/latest/c01/c01_11.html)
-- 1.12 [搞懂字符编码的前世今生](http://python.iswbm.com/en/latest/c01/c01_12.html)
-- 1.13 [Python几个高阶函数](http://python.iswbm.com/en/latest/c01/c01_13.html)
-- 1.14 [with 与 上下文管理器](http://python.iswbm.com/en/latest/c01/c01_14.html)
-- 1.15 [提升Python性能的7个习惯](http://python.iswbm.com/en/latest/c01/c01_15.html)
-- 1.16 [泛型函数怎么写?](http://python.iswbm.com/en/latest/c01/c01_16.html)
-- 1.17 [深入理解「描述符」](http://python.iswbm.com/en/latest/c01/c01_17.html)
-- 1.18 [MySQL 使用总结](http://python.iswbm.com/en/latest/c01/c01_18.html)
-- 1.20 [静态方法其实暗藏玄机](http://python.iswbm.com/en/latest/c01/c01_20.html)
-- 1.21 [开发小技巧](http://python.iswbm.com/en/latest/c01/c01_21.html)
-- 1.22 [如何修改 CentOS 6.x 上默认Python](http://python.iswbm.com/en/latest/c01/c01_22.html)
-- 1.23 [Pythonista 学习 Js](http://python.iswbm.com/en/latest/c01/c01_23.html)
-- 1.24 [深入探讨 Python 的 import 机制:实现远程导入模块](http://python.iswbm.com/en/latest/c01/c01_24.html)
-- 1.25 [50% 的人不知道的Python 包与模块的知识盲区](http://python.iswbm.com/en/latest/c01/c01_25.html)
-- 1.26 [C语言基础的学习](http://python.iswbm.com/en/latest/c01/c01_26.html)
-- 1.27 [全面学习 Python 包:包的构建与分发](http://python.iswbm.com/en/latest/c01/c01_27.html)
-- 1.27 [如何阅读 CPython源码?](http://python.iswbm.com/en/latest/c01/c01_29.html)
-- 1.30 [盘点程序员学习编程的那些网站](http://python.iswbm.com/en/latest/c01/c01_30.html)
-- 1.31 [学习 Pillow 笔记](http://python.iswbm.com/en/latest/c01/c01_31.html)
-- 1.32 [在 CentOS 7.2 上安装 Python3.7](http://python.iswbm.com/en/latest/c01/c01_32.html)
-- 1.33 [如何调试已经运行中的程序](http://python.iswbm.com/en/latest/c01/c01_33.html)
-- 1.34 [每日一库:sh,最优雅的命令调用方式](http://python.iswbm.com/en/latest/c01/c01_34.html)
-- 1.35 [使用 Python 远程登陆服务器的利器](http://python.iswbm.com/en/latest/c01/c01_35.html)
-- 1.36 [每日一库:pretty_errors 解决bug 洁癖](http://python.iswbm.com/en/latest/c01/c01_36.html)
-- 1.37 [Python 炫技操作:条件语句的七种写法](http://python.iswbm.com/en/latest/c01/c01_37.html)
-- 1.38 [/usr/bin/env python 有什么用?](http://python.iswbm.com/en/latest/c01/c01_38.html)
-- 1.39 [Python 炫技操作:合并字典的七种方法](http://python.iswbm.com/en/latest/c01/c01_39.html)
-- 1.40 [Python 炫技操作:判断是否包含子串的七种方法](http://python.iswbm.com/en/latest/c01/c01_40.html)
-- 1.41 [Python 炫技操作:连接列表的八种方法](http://python.iswbm.com/en/latest/c01/c01_41.html)
-- 1.42 [Python 炫技操作:海象运算符的三种用法](http://python.iswbm.com/en/latest/c01/c01_42.html)
-- 1.43 [求你了,别再使用 pprint 打印字典了](http://python.iswbm.com/en/latest/c01/c01_43.html)
-- 1.44 [详解 Python 中的编码问题](http://python.iswbm.com/en/latest/c01/c01_44.html)
-- 1.45 [Python炫技操作:花式导包的八种方法](http://python.iswbm.com/en/latest/c01/c01_45.html)
-
-## 第二章:并发编程
-- 2.1 [从性能角度初探并发编程](http://python.iswbm.com/en/latest/c02/c02_01.html)
-- 2.2 [创建多线程的几种方法](http://python.iswbm.com/en/latest/c02/c02_02.html)
-- 2.3 [谈谈线程中的“锁机制”](http://python.iswbm.com/en/latest/c02/c02_03.html)
-- 2.4 [线程消息通信机制](http://python.iswbm.com/en/latest/c02/c02_04.html)
-- 2.5 [线程中的信息隔离](http://python.iswbm.com/en/latest/c02/c02_05.html)
-- 2.6 [线程池与进程池的创建](http://python.iswbm.com/en/latest/c02/c02_06.html)
-- 2.7 [从生成器使用入门协程](http://python.iswbm.com/en/latest/c02/c02_07.html)
-- 2.8 [深入理解yield from语法](http://python.iswbm.com/en/latest/c02/c02_08.html)
-- 2.9 [初识异步IO框架:asyncio 上篇](http://python.iswbm.com/en/latest/c02/c02_09.html)
-- 2.10 [深入异步IO框架:asyncio 中篇](http://python.iswbm.com/en/latest/c02/c02_10.html)
-- 2.11 [实战异步IO框架:asyncio 下篇](http://python.iswbm.com/en/latest/c02/c02_11.html)
-- 2.12 [生成器与协程,你分清了吗?](http://python.iswbm.com/en/latest/c02/c02_12.html)
-- 2.13 [I/O多路复用:select/poll/epoll](http://python.iswbm.com/en/latest/c02/c02_13.html)
-- 2.14 [浅谈线程安全那些事儿](http://python.iswbm.com/en/latest/c02/c02_14.html)
-
-## 第三章:高级编程
-- 3.1 [装饰器进阶用法详解](http://python.iswbm.com/en/latest/c03/c03_01.html)
-- 3.2 [深入理解Python元类](http://python.iswbm.com/en/latest/c03/c03_02.html)
-- 3.3 [Socket编程实现在线聊天](http://python.iswbm.com/en/latest/c03/c03_03.html)
-- 3.4 [Django+Uwsgi部署网站](http://python.iswbm.com/en/latest/c03/c03_04.html)
-- 3.5 [源码解读:Flask上下文与代理模式](http://python.iswbm.com/en/latest/c03/c03_05.html)
-- 3.6 [Web开发者必看:理解WSGI](http://python.iswbm.com/en/latest/c03/c03_06.html)
-
-## 第四章:开发工具
-- 4.1 [虚拟环境:virtualenv](http://python.iswbm.com/en/latest/c04/c04_01.html)
-- 4.2 [Xshell的高效使用手册](http://python.iswbm.com/en/latest/c04/c04_02.html)
-- 4.3 [30分钟教你搭建一个博客](http://python.iswbm.com/en/latest/c04/c04_03.html)
-- 4.4 [Jupyter NoteBook 使用指南](http://python.iswbm.com/en/latest/c04/c04_04.html)
-- 4.5 [Win10+Ubuntu 双系统安装教程](http://python.iswbm.com/en/latest/c04/c04_05.html)
-- 4.6 [我的 Git 使用指南](http://python.iswbm.com/en/latest/c04/c04_06.html)
-- 4.7 [Hexo 搭建博客教程](http://python.iswbm.com/en/latest/c04/c04_07.html)
-- 4.8 [珍惜生命,远离鼠标](http://python.iswbm.com/en/latest/c04/c04_08.html)
-- 4.9 [MySQL的基本使用](http://python.iswbm.com/en/latest/c04/c04_09.html)
-- 4.10 [MySQL的高级进阶](http://python.iswbm.com/en/latest/c04/c04_10.html)
-- 4.11 [不能不会的远程调试技巧](http://python.iswbm.com/en/latest/c04/c04_11.html)
-- 4.12 [服务器调试神器:pdb](http://python.iswbm.com/en/latest/c04/c04_12.html)
-- 4.13 [命令行解析工具:argparse](http://python.iswbm.com/en/latest/c04/c04_13.html)
-- 4.14 [虚拟环境:Pipenv](http://python.iswbm.com/en/latest/c04/c04_14.html)
-- 4.15 [30个 PyCharm 实用技巧](http://python.iswbm.com/en/latest/c04/c04_15.html)
-- 4.16 [Python 开发技巧集合](http://python.iswbm.com/en/latest/c04/c04_16.html)
-- 4.17 [详解 23 种设计模式](http://python.iswbm.com/en/latest/c04/c04_17.html)
-- 4.18 [详细的 Mac 使用指南](http://python.iswbm.com/en/latest/c04/c04_18.html)
-- 4.19 [程序员编码必学:Vim](http://python.iswbm.com/en/latest/c04/c04_19.html)
-- 4.20 [学会使用谷歌搜索引擎](http://python.iswbm.com/en/latest/c04/c04_20.html)
-- 4.21 [最全的 pip 使用指南,50% 你可能没用过](http://python.iswbm.com/en/latest/c04/c04_21.html)
-- 4.22 [用好 Chrome 必看](http://python.iswbm.com/en/latest/c04/c04_22.html)
-- 4.23 [电脑使用技巧](http://python.iswbm.com/en/latest/c04/c04_23.html)
-- 4.24 [Python “用户环境”的一次完美应用](http://python.iswbm.com/en/latest/c04/c04_24.html)
-
-## 第五章:算法教程
-- 5.1 [图解九大经典排序算法](http://python.iswbm.com/en/latest/c05/c05_01.html)
-- 5.2 [递归算法:走楼梯会思考的题](http://python.iswbm.com/en/latest/c05/c05_02.html)
-- 5.3 [哈希算法:安全方面的算法应用](http://python.iswbm.com/en/latest/c05/c05_03.html)
-
-## 第六章:数据分析
-- 6.1 [一张图带你入门matplotlib](http://python.iswbm.com/en/latest/c06/c06_01.html)
-- 6.2 [详解六种可视化图表](http://python.iswbm.com/en/latest/c06/c06_02.html)
-- 6.3 [如何绘制正余弦函数图象](http://python.iswbm.com/en/latest/c06/c06_03.html)
-- 6.4 [子图与子区 难点突破](http://python.iswbm.com/en/latest/c06/c06_04.html)
-- 6.5 [绘制酷炫的gif动态图](http://python.iswbm.com/en/latest/c06/c06_05.html)
-- 6.6 [自动生成图像视频](http://python.iswbm.com/en/latest/c06/c06_06.html)
-
-## 第七章:运维人生
-- 7.1 [Linux 命令行的艺术](http://python.iswbm.com/en/latest/c07/c07_01.html)
-- 7.2 [Zabbix 监控部署文档](http://python.iswbm.com/en/latest/c07/c07_02.html)
-- 7.3 [Docker:Hello world](http://python.iswbm.com/en/latest/c07/c07_03.html)
-- 7.4 [Docker:关于镜像](http://python.iswbm.com/en/latest/c07/c07_04.html)
-- 7.5 [Docker:网络通信](http://python.iswbm.com/en/latest/c07/c07_05.html)
-- 7.6 [Docker:存储与多主机](http://python.iswbm.com/en/latest/c07/c07_06.html)
-- 7.7 [SaltStack 入门指南](http://python.iswbm.com/en/latest/c07/c07_07.html)
-- 7.8 [Keepalived 部署文档](http://python.iswbm.com/en/latest/c07/c07_08.html)
-- 7.9 [Ansible 入门指南使用手册](http://python.iswbm.com/en/latest/c07/c07_09.html)
-- 7.10 [Ansible API 最全使用文档(中文)](http://python.iswbm.com/en/latest/c07/c07_10.html)
-- 7.11 [K8S:基础入门](http://python.iswbm.com/en/latest/c07/c07_11.html)
-- 7.12 [Linux 包管理工具:yum 和 rpm](http://python.iswbm.com/en/latest/c07/c07_12.html)
-- 7.13 [基于 ansible-api 二次开发](http://python.iswbm.com/en/latest/c07/c07_13.html)
-- 7.14 [Linux 如何写判断语句](http://python.iswbm.com/en/latest/c07/c07_14.html)
-- 7.15 [Mariadb 与 Galera 集群总结](http://python.iswbm.com/en/latest/c07/c07_15.html)
-- 7.16 [Linux 运维之路](http://python.iswbm.com/en/latest/c07/c07_16.html)
-- 1.17 [ansible 自定义 Jinja2 过滤器](http://python.iswbm.com/en/latest/c07/c07_17.html)
-- 7.18 [Shell中去除字符串前后空格的方法](http://python.iswbm.com/en/latest/c07/c07_18.html)
-- 7.19 [Ansible 使用教程](http://python.iswbm.com/en/latest/c07/c07_19.html)
-- 7.20 [使用 Shell 删除文件的多种方法](http://python.iswbm.com/en/latest/c07/c07_20.html)
-
-## 第八章:OpenStack
-- 8.1 [OpenStack 运维命令](http://python.iswbm.com/en/latest/c08/c08_01.html)
-- 8.2 [OpenStack 部署SR-IOV](http://python.iswbm.com/en/latest/c08/c08_02.html)
-- 8.3 [制作 OpenStack 镜像](http://python.iswbm.com/en/latest/c08/c08_03.html)
-- 8.4 [云计算与虚拟化入门通识](http://python.iswbm.com/en/latest/c08/c08_04.html)
-- 8.5 [OpenStack 源码剖析与改造](http://python.iswbm.com/en/latest/c08/c08_05.html)
-- 8.6 [源码解读:Cloud-Init](http://python.iswbm.com/en/latest/c08/c08_06.html)
-- 8.7 [OpenStack 实现GPU直通](http://python.iswbm.com/en/latest/c08/c08_07.html)
-- 8.8 [OpenStack 如何使用DHCP?](http://python.iswbm.com/en/latest/c08/c08_08.html)
-- 8.9 [从0到1:全面理解RPC远程调用](http://python.iswbm.com/en/latest/c08/c08_09.html)
-- 8.16 [修改 KVM 镜像文件的三种方法](http://python.iswbm.com/en/latest/c08/c08_10.html)
-- 8.11 [OpenStack 问题排查](http://python.iswbm.com/en/latest/c08/c08_11.html)
-- 8.12 [OpenStack之主机调度](http://python.iswbm.com/en/latest/c08/c08_12.html)
-- 8.13 [网络知识必知必会](http://python.iswbm.com/en/latest/c08/c08_13.html)
-- 8.14 [支持 IPv6以及多运营商](http://python.iswbm.com/en/latest/c08/c08_14.html)
-- 8.15 [Neutron 源码解读](http://python.iswbm.com/en/latest/c08/c08_15.html)
-
-## 第九章:有趣的工具
-- 9.1 [情人节来了,教你使用 Python 来表白](http://python.iswbm.com/en/latest/c09/c09_01.html)
-- 9.2 [没有这 50 个APP,我的 Mac 将只是一块铁](http://python.iswbm.com/en/latest/c09/c09_02.html)
-- 9.3 [明哥的在线工具集](http://python.iswbm.com/en/latest/c09/c09_03.html)
-
-## 第十章:网络基础
-- 10.1 [网络知识扫盲:一篇文章搞懂 DNS](http://python.iswbm.com/en/latest/c10/c10_01.html)
-- 10.2 [网络知识扫盲:如何理解 OSI七层模型](http://python.iswbm.com/en/latest/c10/c10_02.html)
-- 10.3 [一篇文章理解 HTTP](http://python.iswbm.com/en/latest/c10/c10_03.html)
---
-
+
+
+
+都看到这儿了,还不点个关注嘛? `*^_^*`
+
+
+
+## 欢迎交流
+
+对文章有什么疑问,对项目有什么建议,可以添加微信与我交流,同时欢迎关注我的个人微信公众号。
+
+
+
diff --git a/github-toc-maker-for-sphinx.py b/github-toc-maker-for-sphinx.py
new file mode 100644
index 0000000..49f3f5c
--- /dev/null
+++ b/github-toc-maker-for-sphinx.py
@@ -0,0 +1,71 @@
+import os
+import re
+from glob import glob
+
+pwd = os.getcwd()
+all_chapters_path = glob(pwd + "/source/c0*")
+
+
+def get_chapter_name(file):
+ with open(file) as f:
+ f.readline()
+ chapter_name = f.readline().strip()
+
+ return chapter_name
+
+def get_title(file):
+ with open(file) as f:
+ first_line = f.readline()
+
+ if first_line.startswith("#"):
+ return first_line[1:].strip()
+
+def generate_mapping():
+ mapping = dict.fromkeys([os.path.basename(chapter_path) for chapter_path in all_chapters_path])
+ for key in mapping.keys():
+ chapter_file = os.path.join(pwd, "source", "chapters", key.replace("c", "p") + ".rst")
+ mapping[key] = get_chapter_name(chapter_file)
+
+ return mapping
+
+def get_toc_info():
+ toc = {}
+ for dir_path in all_chapters_path:
+ dir_name = os.path.basename(dir_path)
+
+ chapter_toc = {}
+ files = glob(dir_path + "/*.md")
+
+ for file in files:
+ file_name = os.path.basename(file)
+ section = int(re.findall(r"c\d{2}_(\d{2}).md", file_name)[0])
+
+ #md_path = os.path.join("./source/", dir_name, file_name)
+ md_path = os.path.join("http://pythontime.iswbm.com/en/latest/", dir_name, file_name.replace("md", "html"))
+ title = get_title(file)
+ if not title:
+ continue
+
+ chapter_toc[section] = (title, md_path)
+
+ toc[dir_name] = chapter_toc
+
+ return toc
+
+def print_md_toc(toc_info, mapping):
+ for chapter in sorted(toc_info.items(), key=lambda item: item[0]):
+ posts = chapter[1]
+ chapter_name = mapping[chapter[0]]
+ print(f"- **{chapter_name}**")
+ for post in sorted(posts.items(), key=lambda item:item[0]):
+ # print title only
+ # print(f"{post[1][0]}")
+ print(" ", f"* [{post[1][0]}]({post[1][1]})")
+
+def main():
+ mapping = generate_mapping()
+ toc_info = get_toc_info()
+ print_md_toc(toc_info, mapping)
+
+if __name__ == '__main__':
+ main()
diff --git a/md2rst.py b/md2rst.py
index 4b94018..51a7a32 100644
--- a/md2rst.py
+++ b/md2rst.py
@@ -24,15 +24,32 @@
# 没有文件变更
os._exit(0)
-base_link = "http://python.iswbm.com/en/latest/"
+base_link = "http://pythontime.iswbm.com/en/latest/"
readme_header = '''
-这是我的个人博客( [Python编程时光](http://python.iswbm.com/) ),主要写关于Python的一些思考总结。
+
+
+
+
+
+
+
+
+
+
+## [项目主页](http://pythontime.iswbm.com/)
+
+在线阅读:[Python 编程时光](http://pythontime.iswbm.com/)
+
+
+
+## 文章结构
+
+
-关于搭建教程,感兴趣的可以查看这边:[Sphinx 搭建博客的图文教程](http://python.iswbm.com/en/latest/c04/c04_03.html)
'''
readme_tooter = '''
---
-
+
'''
@@ -128,5 +145,5 @@ def main(index_info):
if __name__ == '__main__':
index_info = init_index_info()
main(index_info)
- render_index_page(index_info)
+ # render_index_page(index_info)
print("OK")
diff --git a/rebuild.sh b/rebuild.sh
new file mode 100755
index 0000000..319b303
--- /dev/null
+++ b/rebuild.sh
@@ -0,0 +1,13 @@
+cat << EOF >/usr/local/lib/python3.6/site-packages/sphinx_rtd_theme/comments.html
+
+
+
+EOF
+
+rm -rf build/ && sphinx-multiversion source build/html && cp -rf build/html/master/* build/html/
diff --git a/requirements.txt b/requirements.txt
index 5854fcd..2a34b8d 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,22 +1,21 @@
alabaster==0.7.12
argh==0.26.2
-Babel==2.7.0
+Babel==2.9.1
certifi==2019.6.16
chardet==3.0.4
docutils==0.14
-idna==2.8
imagesize==1.1.0
-Jinja2==2.10.1
+Jinja2==2.11.3
livereload==2.6.1
MarkupSafe==1.1.1
packaging==19.0
pathtools==0.1.2
port-for==0.3.1
-Pygments==2.4.2
+Pygments==2.7.4
pyparsing==2.4.0
pytz==2019.1
-PyYAML==5.1.1
-requests==2.22.0
+PyYAML==6.0.1
+#requests==2.32.3
six==1.12.0
snowballstemmer==1.9.0
Sphinx==2.1.2
@@ -29,5 +28,14 @@ sphinxcontrib-jsmath==1.0.1
sphinxcontrib-qthelp==1.0.2
sphinxcontrib-serializinghtml==1.1.3
tornado==6.0.3
-urllib3==1.25.3
+#urllib3==2.2.2
watchdog==0.9.0
+sphinxcontrib-disqus==1.1.0
+sphinxcontrib-applehelp==1.0.1
+sphinxcontrib-devhelp==1.0.1
+sphinxcontrib-htmlhelp==1.0.2
+sphinxcontrib-jsmath==1.0.1
+sphinxcontrib-qthelp==1.0.2
+sphinxcontrib-serializinghtml==1.1.3
+sphinx-sitemap==2.2.0
+sphinx-multiversion==0.2.4
diff --git a/source/.DS_Store b/source/.DS_Store
deleted file mode 100644
index 8b16f25..0000000
Binary files a/source/.DS_Store and /dev/null differ
diff --git a/source/_static/js/readmore.js b/source/_static/js/readmore.js
index e91d28f..f2a29c2 100644
--- a/source/_static/js/readmore.js
+++ b/source/_static/js/readmore.js
@@ -40,8 +40,8 @@ var setIdTimer = setInterval(function () {
id: id,
blogId: '15406-1578143418297-890',
name: 'Python编程时光',
- qrcode: 'http://image.python-online.cn/20200104210733.png',
- keyword: 'vip'
+ qrcode: 'http://image.iswbm.com/20200104210733.png',
+ keyword: 'more'
});
}
diff --git a/source/_templates/versions.html b/source/_templates/versions.html
new file mode 100644
index 0000000..31a1257
--- /dev/null
+++ b/source/_templates/versions.html
@@ -0,0 +1,27 @@
+{%- if current_version %}
+
+
+ Other Versions
+ v: {{ current_version.name }}
+
+
+
+ {%- if versions.tags %}
+
+ - Tags
+ {%- for item in versions.tags %}
+ - {{ item.name }}
+ {%- endfor %}
+
+ {%- endif %}
+ {%- if versions.branches %}
+
+ - Branches
+ {%- for item in versions.branches %}
+ - {{ item.name }}
+ {%- endfor %}
+
+ {%- endif %}
+
+
+{%- endif %}
diff --git a/source/aboutme.rst b/source/aboutme.rst
old mode 100755
new mode 100644
index 8e3f65b..b32bed2
--- a/source/aboutme.rst
+++ b/source/aboutme.rst
@@ -3,12 +3,12 @@
==============
* 姓名: 王炳明
-* 微信: mrbensonwon
+* 微信: stromwbm
* 公众号: 《Python编程时光》&《Go编程时光》
* Email: wongbingming@163.com
-* GitHub: https://github.com/bingmingwong
+* GitHub: https://github.com/iswbm
--------------------------------------------
-.. image:: http://image.python-online.cn/image-20200320125724880.png
+.. image:: http://image.iswbm.com/20200607174235.png
diff --git a/source/c01/c01_01.md b/source/c01/c01_01.md
index 938ffc8..d3b620f 100644
--- a/source/c01/c01_01.md
+++ b/source/c01/c01_01.md
@@ -1,18 +1,12 @@
# 1.1 13条Python2.x和3.x的区别?
----
+
-从今天开始,小明将和你一起过一下,那些在面试「Python开发」岗位时面试官喜欢问的问题。内容基础,但是你不一定会噢。
+## 0. 去哪里找
-这些问题全部来自个人经验,群友推荐以及网络上的帖子。如果你有好的问题,也可以随时向我提出(不要觉得简单),我会筛选后整理出来在这里,供大家学习取经,给大家在求职路上贡献一份力。
+这块的内容,你随便使用搜索引擎都可以查到。
-开篇讲些什么好呢?
-
-今天就来罗列一下,Python2.x和3.x到底有哪些区别吧。
-
-## 去哪里找
-
-你随便全使用搜索引擎都可以查到这些资料,但是大家说的都是一些普遍都知道的事儿。或者都是抄来抄去,内容相差无几。
+但是大家好像都在抄来抄去,内容相差无几。
授人以鱼,不如授人以渔。
@@ -22,17 +16,17 @@
这个地址里,有所有Python历史版本(2.0+)。
点击左边,Release Version栏目 对应的版本。
-
+
进入对应详情页后,找到如图 `what's new in Python xx` 就可以查看此版本的新特性。
-
+
网页是全英文的,需要你有一定的英文阅读能力。快去感觉一下吧。
接下来。和大家一起过一下,Python2.x和3.x到底有哪些区别,这不仅在你开发过程中需要考虑的,也是面试过程面试官经常会问及的。
-## 1.1.1 print
+## 1. print
在Python 2.6之前,只支持
```
@@ -50,7 +44,7 @@ print("hello")
print ("hello")
```
-## 1.1.2 编码方式
+## 2. 编码方式
在Python2.x中,默认使用`ASCII`编码。
@@ -74,7 +68,7 @@ print str1
所以我们可以在程序中,随意的使用中文(但并不推荐),不会报错。
-## 1.2.3 除法运算
+## 3. 除法运算
Python 2.x中除法运算,整数间运算只保留整数(向下取整)。
```python
@@ -106,7 +100,7 @@ Python 3.x中除法运算,全部保留小数(即使能被整除)。
-3.0
```
-## 1.2.4 异常捕获
+## 4. 异常捕获
在 Python 3 中,只能使用 `as` 作为关键词。而在Python 2中经常使用 `except Exception, e`
使用语法except (exc1, exc2) as var可以同时捕获多种类别的异常。
@@ -118,7 +112,7 @@ Python 2.6已经支持这两种语法。
在2.x时代,异常在代码中除了表示程序错误,还经常做一些普通控制结构应该做的事情,在3.x中可以看出,设计者让异常变的更加专一,只有在错误发生的情况才能去用异常捕获语句来处理。
-## 1.2.5 xrange
+## 5. xrange
首先,要了解的是,xrange是只有在Python2.x中才有的产物。
@@ -137,7 +131,7 @@ xrange(1, 5)
range(0, 10)
```
-## 1.2.6 用户输入
+## 6. 用户输入
在2.x 中,有两个函数。raw_input()和input()。
- raw_input():将所有输入作为字符串看待,返回字符串类型。
@@ -146,7 +140,7 @@ range(0, 10)
在3.x 中,对这两个函数进行整合,只留下一个`input()`,既可输入数字,也可输入字符串,返回的是字符串类型。
-## 1.2.7 数据类型
+## 7. 数据类型
Python 3.x 一个很重要的特性是,对字符串和二进制数据流做了明确的区分。
@@ -158,7 +152,7 @@ Python 3不会以任意隐式的方式混用str和bytes,你不能拼接字符
还有一点是,3.X去除了long类型,取代它的是整型(int)。3.x的整型是没有限制大小的,可以当做long类型使用, 但实际上由于机器内存的有限,我们使用的整数是不可能无限大的。
-## 1.2.8 函数式编程
+## 8. 函数式编程
在Python中,我们常常使用到的map,filter,reduce,在2.x和3.x中也有所不同。
@@ -193,13 +187,13 @@ Python 3不会以任意隐式的方式混用str和bytes,你不能拼接字符
```
对于 reduce 函数,它在 Python 3.x 中已经不属于 built-in 了,被挪到 functools 模块当中。
-## 1.2.9 协程关键字
+## 9. 协程关键字
在Python3.3后,协程中,新增了yield from 和 async/await 关键字,这在2.x中是没有。
关于yield from的语法剖析,可以前往查看我的另一篇文章。
-## 1.2.10 类的类型
+## 10. 类的类型
Python2.x 默认使用经典类,只有显示继承object才是新式类。
@@ -215,7 +209,7 @@ class Cls(object):
pass
```
-## 1.2.11 变量作用域
+## 11. 变量作用域
- 在2.x中无法将局部变量声明为全局变量。
- 在3.x中可以使用nonlocal语法将局部变量声明为全局变量。
@@ -234,7 +228,7 @@ foo()
# 3.x输出:200
```
-## 1.2.12 元类的使用
+## 12. 元类的使用
在2.x 中
```python
@@ -255,7 +249,7 @@ class Person(metaclass=MetaPerson):
pass
```
-## 1.2.13 模块变化
+## 13. 模块变化
- 去掉了一些模块。由于不常用,这里就不列举了。
- 新增了一些模块。比如:concurrent.futures,asyncio等
@@ -263,12 +257,12 @@ class Person(metaclass=MetaPerson):
----
-大概就是这些内容,可能还有更细微的差别,这些内容要前往官网查看。但是那些对于我们普通开发者来说,并不那么重要。完全可以不去关注。
+大概就是这些内容,可能还有更细微的差别,这些内容要前往官网查看。
-实际上,当我熟悉一个版本后,基本上是可以无缝过渡到另一个版本的。这篇文章,更多的是为了科普和应对面试。
+但是那些对于我们普通开发者来说,并不那么重要,个人感觉可以不去关注。
----
-
+
diff --git a/source/c01/c01_01.rst b/source/c01/c01_01.rst
old mode 100755
new mode 100644
index 21bbee0..90abe6b
--- a/source/c01/c01_01.rst
+++ b/source/c01/c01_01.rst
@@ -1,20 +1,14 @@
1.1 13条Python2.x和3.x的区别?
==============================
---------------
-
-从今天开始,小明将和你一起过一下,那些在面试「Python开发」岗位时面试官喜欢问的问题。内容基础,但是你不一定会噢。
-
-这些问题全部来自个人经验,群友推荐以及网络上的帖子。如果你有好的问题,也可以随时向我提出(不要觉得简单),我会筛选后整理出来在这里,供大家学习取经,给大家在求职路上贡献一份力。
+|image0|
-开篇讲些什么好呢?
-
-今天就来罗列一下,Python2.x和3.x到底有哪些区别吧。
+0. 去哪里找
+-----------
-去哪里找
---------
+这块的内容,你随便使用搜索引擎都可以查到。
-你随便全使用搜索引擎都可以查到这些资料,但是大家说的都是一些普遍都知道的事儿。或者都是抄来抄去,内容相差无几。
+但是大家好像都在抄来抄去,内容相差无几。
授人以鱼,不如授人以渔。
@@ -23,17 +17,17 @@
那当然是官网啦:https://www.python.org/downloads/
这个地址里,有所有Python历史版本(2.0+)。 点击左边,Release Version栏目
-对应的版本。 |image0|
+对应的版本。 |image1|
进入对应详情页后,找到如图 ``what's new in Python xx``
-就可以查看此版本的新特性。 |image1|
+就可以查看此版本的新特性。 |image2|
网页是全英文的,需要你有一定的英文阅读能力。快去感觉一下吧。
接下来。和大家一起过一下,Python2.x和3.x到底有哪些区别,这不仅在你开发过程中需要考虑的,也是面试过程面试官经常会问及的。
-1.1.1 print
------------
+1. print
+--------
在Python 2.6之前,只支持
@@ -56,8 +50,8 @@
print("hello")
print ("hello")
-1.1.2 编码方式
---------------
+2. 编码方式
+-----------
在Python2.x中,默认使用\ ``ASCII``\ 编码。
@@ -87,8 +81,8 @@ Python 2的正确使用方法,如下
所以我们可以在程序中,随意的使用中文(但并不推荐),不会报错。
-1.2.3 除法运算
---------------
+3. 除法运算
+-----------
Python 2.x中除法运算,整数间运算只保留整数(向下取整)。
@@ -124,8 +118,8 @@ Python 3.x中除法运算,全部保留小数(即使能被整除)。
>>> -8//3.0
-3.0
-1.2.4 异常捕获
---------------
+4. 异常捕获
+-----------
在 Python 3 中,只能使用 ``as`` 作为关键词。而在Python 2中经常使用
``except Exception, e`` 使用语法except (exc1, exc2) as
@@ -139,8 +133,8 @@ Python 2.6已经支持这两种语法。
在2.x时代,异常在代码中除了表示程序错误,还经常做一些普通控制结构应该做的事情,在3.x中可以看出,设计者让异常变的更加专一,只有在错误发生的情况才能去用异常捕获语句来处理。
-1.2.5 xrange
-------------
+5. xrange
+---------
首先,要了解的是,xrange是只有在Python2.x中才有的产物。
@@ -163,8 +157,8 @@ Python 2.6已经支持这两种语法。
>>> range(10)
range(0, 10)
-1.2.6 用户输入
---------------
+6. 用户输入
+-----------
在2.x 中,有两个函数。raw_input()和input()。 -
raw_input():将所有输入作为字符串看待,返回字符串类型。 -
@@ -173,8 +167,8 @@ input():只能接收“数字”的输入。
在3.x
中,对这两个函数进行整合,只留下一个\ ``input()``\ ,既可输入数字,也可输入字符串,返回的是字符串类型。
-1.2.7 数据类型
---------------
+7. 数据类型
+-----------
Python 3.x 一个很重要的特性是,对字符串和二进制数据流做了明确的区分。
@@ -188,8 +182,8 @@ Python
还有一点是,3.X去除了long类型,取代它的是整型(int)。3.x的整型是没有限制大小的,可以当做long类型使用,
但实际上由于机器内存的有限,我们使用的整数是不可能无限大的。
-1.2.8 函数式编程
-----------------
+8. 函数式编程
+-------------
在Python中,我们常常使用到的map,filter,reduce,在2.x和3.x中也有所不同。
@@ -229,16 +223,16 @@ Python
对于 reduce 函数,它在 Python 3.x 中已经不属于 built-in 了,被挪到
functools 模块当中。
-1.2.9 协程关键字
-----------------
+9. 协程关键字
+-------------
在Python3.3后,协程中,新增了yield from 和 async/await
关键字,这在2.x中是没有。
关于yield from的语法剖析,可以前往查看我的另一篇文章。
-1.2.10 类的类型
----------------
+10. 类的类型
+------------
Python2.x 默认使用经典类,只有显示继承object才是新式类。
@@ -255,8 +249,8 @@ Python3.x 没有经典类,只有新式类,而且有三种写法
class Cls(object):
pass
-1.2.11 变量作用域
------------------
+11. 变量作用域
+--------------
- 在2.x中无法将局部变量声明为全局变量。
- 在3.x中可以使用nonlocal语法将局部变量声明为全局变量。
@@ -275,8 +269,8 @@ Python3.x 没有经典类,只有新式类,而且有三种写法
# 2.x输出:100
# 3.x输出:200
-1.2.12 元类的使用
------------------
+12. 元类的使用
+--------------
在2.x 中
@@ -299,8 +293,8 @@ Python3.x 没有经典类,只有新式类,而且有三种写法
class Person(metaclass=MetaPerson):
pass
-1.2.13 模块变化
----------------
+13. 模块变化
+------------
- 去掉了一些模块。由于不常用,这里就不列举了。
- 新增了一些模块。比如:concurrent.futures,asyncio等
@@ -308,17 +302,16 @@ Python3.x 没有经典类,只有新式类,而且有三种写法
--------------
-大概就是这些内容,可能还有更细微的差别,这些内容要前往官网查看。但是那些对于我们普通开发者来说,并不那么重要。完全可以不去关注。
+大概就是这些内容,可能还有更细微的差别,这些内容要前往官网查看。
-实际上,当我熟悉一个版本后,基本上是可以无缝过渡到另一个版本的。这篇文章,更多的是为了科普和应对面试。
+但是那些对于我们普通开发者来说,并不那么重要,个人感觉可以不去关注。
--------------
-.. figure:: http://image.python-online.cn/image-20200320125724880.png
- :alt: 关注公众号,获取最新干货!
-
- 关注公众号,获取最新干货!
+|image3|
-.. |image0| image:: http://image.python-online.cn/20190511165542.png
-.. |image1| image:: http://image.python-online.cn/20190511165551.png
+.. |image0| image:: http://image.iswbm.com/20200602135014.png
+.. |image1| image:: http://image.iswbm.com/20190511165542.png
+.. |image2| image:: http://image.iswbm.com/20190511165551.png
+.. |image3| image:: http://image.iswbm.com/20200607174235.png
diff --git a/source/c01/c01_02.md b/source/c01/c01_02.md
new file mode 100644
index 0000000..d2e4c2d
--- /dev/null
+++ b/source/c01/c01_02.md
@@ -0,0 +1,279 @@
+# 1.2 Python 自省机制详解
+
+自省,在我们日常生活中,通常是自我反省的意思。
+
+但在计算机编程中,自省并不是这个意思,它的英文单词是 introspection,表示的是自我检查的行为或能力。
+
+它的内容包括
+
+1. 告诉别人,我是谁
+2. 告诉别人,我能做什么
+
+(有点面试的感觉了)
+
+Python 是一门动态语言,有了自省,就能让程序在运行时能够获知对象的类型以及该对象下有哪些方法等。
+
+## 1. 学习 Python 模块的入口
+
+### help()
+
+在 console 模式下,输入 `help()` ,可以看到输出了一段帮助文档,教你如何使用这个 help,当你看到提示符变成了 `help>` 时,这时候就进入了 help 模式。
+
+
+
+此时你可以键入你想要了解的模块、语法等,help 告诉你如何使用。
+
+比如我输入 keywords ,就可以看到 Python 里所有的关键字。再输入 modules 就可以查看 Python 中所有的内置模块。
+
+
+
+输入 modules + `指定包名`,就可以查看这个包下有哪些模块
+
+
+
+如果你想学习某个包要如何使用,可以直接在 help 模式下输入 `包名`,就像下面这样,我就可以获得一份 json 的帮助文档。
+
+
+
+如果你想学习某个关键字的用法,可以在 help 模式下直接键入 `关键字` 查询用法,比如我直接键入 `for` 。
+
+
+
+查完后,使用 quit 就可以退出 help 模式了。
+
+
+
+如果你觉得进入 help 模式太麻烦,可以在 console 模式下直接查询
+
+```python
+>>> help("json")
+```
+
+
+
+### dir()
+
+dir() 函数可能是 Python 自省机制中最著名的部分了。它返回传递给它的任何对象的属性名称经过排序的列表。如果不指定对象,则 dir() 返回当前作用域中的名称。让我们将 dir() 函数应用于 keyword 模块,并观察它揭示了什么:
+
+
+
+
+
+## 2. 应用到实际开发中
+
+### type()
+
+type() 函数有助于我们确定对象是字符串还是整数,或是其它类型的对象。它通过返回类型对象来做到这一点,可以将这个类型对象与 types 模块中定义的类型相比较:
+
+```python
+>>> type(42)
+
+>>> type([])
+
+```
+
+### hasattr()
+
+使用 dir() 函数会返回一个对象的属性列表。
+
+但是,有时我们只想测试一个或多个属性是否存在。如果对象具有我们正在考虑的属性,那么通常希望只检索该属性。这个任务可以由 hasattr() 来完成.
+
+```python
+>>> import json
+>>> hasattr(json, "dumps")
+True
+>>>
+```
+
+### getattr()
+
+使用 hasattr 获知了对象拥有某个属性后,可以搭配 getattr() 函数来获取其属性值。
+
+```python
+>>> getattr(json, "__path__")
+['/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/json']
+>>>
+```
+
+使用 getattr 获取函数后,可以很方便地使用这个函数,比如下面这样,可以不再使写 json.dumps 这么字。
+
+```python
+>>> dumps = getattr(json, "dumps")
+>>> dumps({"name": "MING"})
+'{"name": "MING"}'
+>>>
+
+# 当然你还有更简单的方法
+>>> mydumps = json.dumps
+>>> mydumps({"name": "MING"})
+'{"name": "MING"}'
+```
+
+### id()
+
+**id()** 函数返回对象的唯一标识符,标识符是一个整数。
+
+```python
+>>> a = "hello"
+>>> b = "world"
+>>>
+>>> id(a)
+4470767944
+>>> id(b)
+4499487408
+>>>
+```
+
+
+
+### isinstance()
+
+使用 isinstance() 函数可以确定一个对象是否是某个特定类型或定制类的实例。
+
+```python
+>>> isinstance("python", str)
+True
+>>> isinstance(10, int)
+True
+>>> isinstance(False, bool)
+True
+```
+
+
+
+### callable()
+
+使用 callable 可以确定一个对象是否是可调用的,比如函数,类这些对象都是可以调用的对象。
+
+```python
+>>> callable("hello")
+False
+>>>
+>>> callable(str)
+True
+>>>
+```
+
+
+
+## 3. 模块(Modules)
+
+### \__doc__
+
+使用 `__doc__` 这个魔法方法,可以查询该模块的文档,它输出的内容和 help() 一样。
+
+
+
+### \__name__
+
+始终是定义时的模块名;即使你使用import .. as 为它取了别名,或是赋值给了另一个变量名。
+
+```python
+>>> import json
+>>> json.__name__
+'json'
+>>>
+>>> import json as js
+>>> js.__name__
+'json'
+```
+
+### \__file__
+
+包含了该模块的文件路径。需要注意的是内建的模块没有这个属性,访问它会抛出异常!
+
+```python
+>>> import json
+>>> json.__file__
+'/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/json/__init__.py'
+```
+
+
+
+### \__dict__
+
+包含了模块里可用的属性名-属性的字典;也就是可以使用模块名.属性名访问的对象。
+
+
+
+## 4. 类(Class)
+
+### \__doc__
+
+文档字符串。如果类没有文档,这个值是None。
+
+```python
+>>> class People:
+... '''
+... people class
+... '''
+...
+>>> p = People()
+>>>
+>>> print(p.__doc__)
+
+ people class
+
+>>>
+```
+
+
+
+### \__name__
+
+始终是定义时的类名。
+
+```python
+>>> People.__name__
+'People'
+```
+
+
+
+### \__dict__
+
+包含了类里可用的属性名-属性的字典;也就是可以使用类名.属性名访问的对象。
+
+```python
+>>> People.__dict__
+mappingproxy({'__module__': '__main__', '__doc__': '\n people class\n ', '__dict__': , '__weakref__': })
+```
+
+
+
+### \__module__
+
+包含该类的定义的模块名;需要注意,是字符串形式的模块名而不是模块对象。
+
+由于我是在 交互式命令行的环境下,所以模块是 `__main__`
+
+```python
+>>> People.__module__
+'__main__'
+```
+
+如果将上面的代码放入 demo.py,并且从 people 模块导入 People 类,其值就是 people 模块
+
+
+
+
+
+### \__bases__
+
+直接父类对象的元组;但不包含继承树更上层的其他类,比如父类的父类。
+
+```python
+>>> class People: pass
+...
+>>> class Teenager: pass
+...
+>>> class Student(Teenager): pass
+...
+>>> Student.__bases__
+(,)
+>>>
+```
+
+
+
+
\ No newline at end of file
diff --git a/source/c01/c01_02.rst b/source/c01/c01_02.rst
old mode 100755
new mode 100644
index da2afa2..7ea82dc
--- a/source/c01/c01_02.rst
+++ b/source/c01/c01_02.rst
@@ -1,3 +1,315 @@
-1.2 Python的自省机制
-================================
+1.2 Python 自省机制详解
+=======================
+
+自省,在我们日常生活中,通常是自我反省的意思。
+
+但在计算机编程中,自省并不是这个意思,它的英文单词是
+introspection,表示的是自我检查的行为或能力。
+
+它的内容包括
+
+1. 告诉别人,我是谁
+2. 告诉别人,我能做什么
+
+(有点面试的感觉了)
+
+Python
+是一门动态语言,有了自省,就能让程序在运行时能够获知对象的类型以及该对象下有哪些方法等。
+
+1. 学习 Python 模块的入口
+-------------------------
+
+help()
+~~~~~~
+
+在 console 模式下,输入 ``help()``
+,可以看到输出了一段帮助文档,教你如何使用这个
+help,当你看到提示符变成了 ``help>`` 时,这时候就进入了 help 模式。
+
+|image0|
+
+此时你可以键入你想要了解的模块、语法等,help 告诉你如何使用。
+
+比如我输入 keywords ,就可以看到 Python 里所有的关键字。再输入 modules
+就可以查看 Python 中所有的内置模块。
+
+|image1|
+
+输入 modules + ``指定包名``\ ,就可以查看这个包下有哪些模块
+
+|image2|
+
+如果你想学习某个包要如何使用,可以直接在 help 模式下输入
+``包名``\ ,就像下面这样,我就可以获得一份 json 的帮助文档。
+
+|image3|
+
+如果你想学习某个关键字的用法,可以在 help 模式下直接键入 ``关键字``
+查询用法,比如我直接键入 ``for`` 。
+
+|image4|
+
+查完后,使用 quit 就可以退出 help 模式了。
+
+|image5|
+
+如果你觉得进入 help 模式太麻烦,可以在 console 模式下直接查询
+
+.. code:: python
+
+ >>> help("json")
+
+dir()
+~~~~~
+
+dir() 函数可能是 Python
+自省机制中最著名的部分了。它返回传递给它的任何对象的属性名称经过排序的列表。如果不指定对象,则
+dir() 返回当前作用域中的名称。让我们将 dir() 函数应用于 keyword
+模块,并观察它揭示了什么:
+
+|image6|
+
+2. 应用到实际开发中
+-------------------
+
+type()
+~~~~~~
+
+type()
+函数有助于我们确定对象是字符串还是整数,或是其它类型的对象。它通过返回类型对象来做到这一点,可以将这个类型对象与
+types 模块中定义的类型相比较:
+
+.. code:: python
+
+ >>> type(42)
+
+ >>> type([])
+
+
+hasattr()
+~~~~~~~~~
+
+使用 dir() 函数会返回一个对象的属性列表。
+
+但是,有时我们只想测试一个或多个属性是否存在。如果对象具有我们正在考虑的属性,那么通常希望只检索该属性。这个任务可以由
+hasattr() 来完成.
+
+.. code:: python
+
+ >>> import json
+ >>> hasattr(json, "dumps")
+ True
+ >>>
+
+getattr()
+~~~~~~~~~
+
+使用 hasattr 获知了对象拥有某个属性后,可以搭配 getattr()
+函数来获取其属性值。
+
+.. code:: python
+
+ >>> getattr(json, "__path__")
+ ['/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/json']
+ >>>
+
+使用 getattr
+获取函数后,可以很方便地使用这个函数,比如下面这样,可以不再使写
+json.dumps 这么字。
+
+.. code:: python
+
+ >>> dumps = getattr(json, "dumps")
+ >>> dumps({"name": "MING"})
+ '{"name": "MING"}'
+ >>>
+
+ # 当然你还有更简单的方法
+ >>> mydumps = json.dumps
+ >>> mydumps({"name": "MING"})
+ '{"name": "MING"}'
+
+id()
+~~~~
+
+**id()** 函数返回对象的唯一标识符,标识符是一个整数。
+
+.. code:: python
+
+ >>> a = "hello"
+ >>> b = "world"
+ >>>
+ >>> id(a)
+ 4470767944
+ >>> id(b)
+ 4499487408
+ >>>
+
+isinstance()
+~~~~~~~~~~~~
+
+使用 isinstance() 函数可以确定一个对象是否是某个特定类型或定制类的实例。
+
+.. code:: python
+
+ >>> isinstance("python", str)
+ True
+ >>> isinstance(10, int)
+ True
+ >>> isinstance(False, bool)
+ True
+
+callable()
+~~~~~~~~~~
+
+使用 callable
+可以确定一个对象是否是可调用的,比如函数,类这些对象都是可以调用的对象。
+
+.. code:: python
+
+ >>> callable("hello")
+ False
+ >>>
+ >>> callable(str)
+ True
+ >>>
+
+3. 模块(Modules)
+------------------
+
+\__doc_\_
+~~~~~~~~~
+
+使用 ``__doc__`` 这个魔法方法,可以查询该模块的文档,它输出的内容和
+help() 一样。
+
+|image7|
+
+\__name_\_
+~~~~~~~~~~
+
+始终是定义时的模块名;即使你使用import .. as
+为它取了别名,或是赋值给了另一个变量名。
+
+.. code:: python
+
+ >>> import json
+ >>> json.__name__
+ 'json'
+ >>>
+ >>> import json as js
+ >>> js.__name__
+ 'json'
+
+\__file_\_
+~~~~~~~~~~
+
+包含了该模块的文件路径。需要注意的是内建的模块没有这个属性,访问它会抛出异常!
+
+.. code:: python
+
+ >>> import json
+ >>> json.__file__
+ '/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/json/__init__.py'
+
+\__dict_\_
+~~~~~~~~~~
+
+包含了模块里可用的属性名-属性的字典;也就是可以使用模块名.属性名访问的对象。
+
+4. 类(Class)
+--------------
+
+.. _doc__-1:
+
+\__doc_\_
+~~~~~~~~~
+
+文档字符串。如果类没有文档,这个值是None。
+
+.. code:: python
+
+ >>> class People:
+ ... '''
+ ... people class
+ ... '''
+ ...
+ >>> p = People()
+ >>>
+ >>> print(p.__doc__)
+
+ people class
+
+ >>>
+
+.. _name__-1:
+
+\__name_\_
+~~~~~~~~~~
+
+始终是定义时的类名。
+
+.. code:: python
+
+ >>> People.__name__
+ 'People'
+
+.. _dict__-1:
+
+\__dict_\_
+~~~~~~~~~~
+
+包含了类里可用的属性名-属性的字典;也就是可以使用类名.属性名访问的对象。
+
+.. code:: python
+
+ >>> People.__dict__
+ mappingproxy({'__module__': '__main__', '__doc__': '\n people class\n ', '__dict__': , '__weakref__': })
+
+\__module_\_
+~~~~~~~~~~~~
+
+包含该类的定义的模块名;需要注意,是字符串形式的模块名而不是模块对象。
+
+由于我是在 交互式命令行的环境下,所以模块是 ``__main__``
+
+.. code:: python
+
+ >>> People.__module__
+ '__main__'
+
+如果将上面的代码放入 demo.py,并且从 people 模块导入 People 类,其值就是
+people 模块
+
+|image8|
+
+\__bases_\_
+~~~~~~~~~~~
+
+直接父类对象的元组;但不包含继承树更上层的其他类,比如父类的父类。
+
+.. code:: python
+
+ >>> class People: pass
+ ...
+ >>> class Teenager: pass
+ ...
+ >>> class Student(Teenager): pass
+ ...
+ >>> Student.__bases__
+ (,)
+ >>>
+
+|image9|
+
+.. |image0| image:: http://image.iswbm.com/image-20200606121047415.png
+.. |image1| image:: http://image.iswbm.com/image-20200606121544062.png
+.. |image2| image:: http://image.iswbm.com/image-20200606121942898.png
+.. |image3| image:: http://image.iswbm.com/image-20200606122408522.png
+.. |image4| image:: http://image.iswbm.com/image-20200606133933401.png
+.. |image5| image:: http://image.iswbm.com/image-20200606123145109.png
+.. |image6| image:: http://image.iswbm.com/image-20200606134519352.png
+.. |image7| image:: http://image.iswbm.com/image-20200606134858285.png
+.. |image8| image:: http://image.iswbm.com/image-20200905115039771.png
+.. |image9| image:: http://image.iswbm.com/20200607174235.png
diff --git a/source/c01/c01_38.md b/source/c01/c01_03.md
similarity index 54%
rename from source/c01/c01_38.md
rename to source/c01/c01_03.md
index a40bf3e..726d0e2 100644
--- a/source/c01/c01_38.md
+++ b/source/c01/c01_03.md
@@ -1,44 +1,50 @@
-# 1.38 /usr/bin/env python 有什么用?
+# 1.3 /usr/bin/env python 有什么用?
+
+
我们经常会在别人的脚本或者项目的入口文件里看到第一行是下面这样
-```json
+```
#!/usr/bin/python
```
或者这样
-```json
+```
#!/usr/bin/env python
```
-这两者有什么区别呢?
+那么他们有什么用呢?
+
+要理解它,得把这一行语句拆成两部分。
+第一部分是 `#!`
+第二部分是 `/usr/bin/python` 或者 `/usr/bin/env python`
-稍微接触过 linux 的人都知道 `/usr/bin/python` 就是我们执行 `python` 进入console 模式里的 `python`
+关于 `#!` 这个符号,其实它是有名字的,叫做 `Shebang` 或者`Sha-bang` ,有的翻译组将它译作 `释伴`,即“解释伴随行”的简称,同时又是Shebang的音译。
-
+Shebang通常出现在类Unix系统的脚本中第一行,作为前两个字符。在Shebang之后,可以有一个或数个空白字符,后接解释器的绝对路径,用于指明执行这个脚本文件的解释器。
-而当你在可执行文件头里使用 `#!` + `/usr/bin/python` ,意思就是说你得用哪个软件 (python)来执行这个文件。
+
-那么加和不加有什么区别呢?
+**那么加和不加有什么区别呢?**
-不加的话,你每次执行这个脚本时,都得这样: `python xx.py` ,
+如果不加 `#!` 的话,你每次执行这个脚本时,都得这样 `python xx.py` ,
-
+
有没有一种方式?可以省去每次都加 `python` 呢?
当然有,你可以文件头里加上`#!/usr/bin/python` ,那么当这个文件有可执行权限 时,只直接写这个脚本文件,就像下面这样。
-
+
明白了这个后,再来看看 `!/usr/bin/env python` 这个 又是什么意思 ?
当我执行 `env python` 时,自动进入了 python console 的模式。
-
+
这是为什么?和 直接执行 python 好像没什么区别呀
@@ -48,7 +54,7 @@
具体演示过程,你可以看下面。
-
+
那么对于这两者,我们应该使用哪个呢?
@@ -56,4 +62,4 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/source/c01/c01_03.rst b/source/c01/c01_03.rst
old mode 100755
new mode 100644
index 513c244..66dfc5f
--- a/source/c01/c01_03.rst
+++ b/source/c01/c01_03.rst
@@ -1,3 +1,82 @@
-1.3 谈谈深贝和浅拷贝的区别
-================================
+1.3 /usr/bin/env python 有什么用?
+==================================
+
+|image0|
+
+我们经常会在别人的脚本或者项目的入口文件里看到第一行是下面这样
+
+::
+
+ #!/usr/bin/python
+
+或者这样
+
+::
+
+ #!/usr/bin/env python
+
+那么他们有什么用呢?
+
+要理解它,得把这一行语句拆成两部分。
+
+第一部分是 ``#!``
+
+第二部分是 ``/usr/bin/python`` 或者 ``/usr/bin/env python``
+
+关于 ``#!`` 这个符号,其实它是有名字的,叫做 ``Shebang``
+或者\ ``Sha-bang`` ,有的翻译组将它译作
+``释伴``\ ,即“解释伴随行”的简称,同时又是Shebang的音译。
+
+Shebang通常出现在类Unix系统的脚本中第一行,作为前两个字符。在Shebang之后,可以有一个或数个空白字符,后接解释器的绝对路径,用于指明执行这个脚本文件的解释器。
+
+|image1|
+
+**那么加和不加有什么区别呢?**
+
+如果不加 ``#!`` 的话,你每次执行这个脚本时,都得这样 ``python xx.py`` ,
+
+|image2|
+
+有没有一种方式?可以省去每次都加 ``python`` 呢?
+
+当然有,你可以文件头里加上\ ``#!/usr/bin/python``
+,那么当这个文件有可执行权限 时,只直接写这个脚本文件,就像下面这样。
+
+|image3|
+
+明白了这个后,再来看看 ``!/usr/bin/env python`` 这个 又是什么意思 ?
+
+当我执行 ``env python`` 时,自动进入了 python console 的模式。
+
+|image4|
+
+这是为什么?和 直接执行 python 好像没什么区别呀
+
+当你执行 ``env python`` 时,它其实会去 ``env | grep PATH`` 里(也就是
+/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin
+)这几个路径里去依次查找名为python的可执行文件。
+
+找到一个就直接执行,上面我们的 python 路径是在 ``/usr/bin/python``
+里,在 ``PATH`` 列表里倒数第二个目录下,所以当我在 ``/usr/local/sbin``
+下创建一个名字也为 python 的可执行文件时,就会执行 ``/usr/bin/python``
+了。
+
+具体演示过程,你可以看下面。
+
+|image5|
+
+那么对于这两者,我们应该使用哪个呢?
+
+个人感觉应该优先使用 ``#!/usr/bin/env python``\ ,因为不是所有的机器的
+python 解释器都是 ``/usr/bin/python`` 。
+
+|image6|
+
+.. |image0| image:: http://image.iswbm.com/20200602135014.png
+.. |image1| image:: http://image.iswbm.com/20200331184021.png
+.. |image2| image:: http://image.iswbm.com/20200331185034.png
+.. |image3| image:: http://image.iswbm.com/20200331184755.png
+.. |image4| image:: http://image.iswbm.com/20200331185741.png
+.. |image5| image:: http://image.iswbm.com/20200331190224.png
+.. |image6| image:: http://image.iswbm.com/20200607174235.png
diff --git a/source/c01/c01_04.md b/source/c01/c01_04.md
index 0cef6a9..eba6d75 100644
--- a/source/c01/c01_04.md
+++ b/source/c01/c01_04.md
@@ -1,5 +1,7 @@
# 1.4 什么是猴子补丁?
+
+
也许你没用过猴子补丁,但是你必须知道,在Python/Ruby 这类脚本语言中,有一种用法叫 Monkey Patch 。
这名字挺好玩的哈。在网上,关于这个名称的来源,大致有如下两种说法:
@@ -47,7 +49,7 @@ del pd.DataFrame.just_foo_cols # you can also remove the new method
在openstack中的例子
-
+
还有就是gevent中也有用到。
@@ -101,4 +103,4 @@ setattr(sys.modules[module], key,
---
-
+
diff --git a/source/c01/c01_04.rst b/source/c01/c01_04.rst
old mode 100755
new mode 100644
index 5972b03..b0a5d5b
--- a/source/c01/c01_04.rst
+++ b/source/c01/c01_04.rst
@@ -1,6 +1,8 @@
1.4 什么是猴子补丁?
===================
+|image0|
+
也许你没用过猴子补丁,但是你必须知道,在Python/Ruby
这类脚本语言中,有一种用法叫 Monkey Patch 。
@@ -62,7 +64,7 @@
在openstack中的例子
-|image0|
+|image1|
还有就是gevent中也有用到。
@@ -115,10 +117,9 @@
--------------
-.. figure:: http://image.python-online.cn/20191117142849.png
- :alt: 关注公众号,获取最新干货!
-
- 关注公众号,获取最新干货!
+|image2|
-.. |image0| image:: http://image.python-online.cn/20190404215330.png
+.. |image0| image:: http://image.iswbm.com/20200602135014.png
+.. |image1| image:: http://image.iswbm.com/20190404215330.png
+.. |image2| image:: http://image.iswbm.com/20191117142849.png
diff --git a/source/c01/c01_05.md b/source/c01/c01_05.md
index febc146..21f0807 100644
--- a/source/c01/c01_05.md
+++ b/source/c01/c01_05.md
@@ -1,169 +1,171 @@
-# 1.5 深入闭包与变量作用域
-
----
-
-## 1.5.1 作用域
-
-Python的作用域可以分为四种:
-- L (Local) 局部作用域
-- E (Enclosing) 闭包函数外的函数中
-- G (Global) 全局作用域
-- B (Built-in) 内建作用域
-
-变量/函数 的查找顺序:
-L –> E –> G –>B
-
-意思是,在局部找不到的,便去局部外的局部作用域找(例如 闭包),再找不到的就去全局作业域里找,再找不到就去内建作业域中找。
-
-会影响 变量/函数 作用范围的有
-- 函数:def 或 lambda
-- 类:class
-- 关键字:global noglobal
-- 文件:*py
-- 推导式:[],{},()等,仅限Py3.x中,Py2.x会出现变量泄露。
-
-1、赋值在前,引用在后
-```python
-# ------同作用域内------
-name = "MING"
-print(name)
-
-# ------不同作用域内------
-name = "MING"
-def main():
- print(name)
-```
-2、引用在前,赋值在后(同一作用域内)
-```python
-print(name)
-name = "MING"
-
-# UnboundLocalError: local variable 'name' referenced before assignment
-```
-3、赋值在低层,引用在高层
-```python
-# L -> E -> G -> B
-# 从左到右,由低层到高层
-def main():
- name = "MING"
-
-print(name)
-# NameError: name 'name' is not defined
-```
-
-
-## 1.5.2 闭包
-
-闭包这个概念很重要噢。你一定要掌握。
->在一个外函数中定义了一个内函数,内函数里运用了外函数的临时变量,并且外函数的返回值是内函数的引用。这样就构成了一个闭包。其实装饰函数,很多都是闭包。
-
-好像并不难理解,为什么初学者会觉得闭包难以理解呢?
-
-我解释一下,你就明白了。
-
-一般情况下,在我们认知当中,如果一个函数结束,函数的内部所有东西都会释放掉,还给内存,局部变量都会消失。但是闭包是一种特殊情况,如果外函数在结束的时候发现有自己的临时变量将来会在内部函数中用到,就把这个临时变量绑定给了内部函数,然后自己再结束。
-
-你可以看下面这段代码,就构成了闭包。在内函数里可以引用外函数的变量。
-```python
-def deco():
- name = "MING"
- def wrapper():
- print(name)
- return wrapper
-
-deco()()
-# 输出:MING
-```
-
-## 1.5.3 改变作用域
-
-变量的作用域,与其定义(或赋值)的位置有关,但不是绝对相关。
-因为我们可以在某种程度上去改变`向上`的作用范围。
-
-- 关键字:global
-将 局部变量 变为全局变量
-
-- 关键字:nonlocal
-可以在闭包函数中,引用并使用闭包外部函数的变量(非全局的噢)
-
-global好理解,这里只讲下nonlocal。
-
-先来看个例子
-```python
-def deco():
- age = 10
- def wrapper():
- age += 1
- return wrapper
-
-deco()()
-```
-运行一下,会报错。
-```
-# UnboundLocalError: local variable 'age' referenced before assignment
-```
-但是这样就OK
-```
-def deco():
- age = 10
- def wrapper():
- nonlocal age
- age += 1
- return wrapper
-
-deco()()
-# 输出:11
-```
-其实,你如果不使用 `+=`、`-=`等一类的操作,不加nonlocal也没有关系。这就展示了闭包的特性。
-```
-def deco():
- age = 10
- def wrapper():
- print(age)
- return wrapper
-
-deco()()
-# 输出:10
-```
-
-
-## 1.5.4 变量集合
-
-在Python中,有两个内建函数,你可能用不到,但是需要掌握它们。
-- globals() :以dict的方式存储所有全局变量
-- locals():以dict的方式存储所有局部变量
-
-globals()
-```python
-def foo():
- print("I am a func")
-
-def bar():
- foo="I am a string"
- foo_dup = globals().get("foo")
- foo_dup()
-
-bar()
-# 输出
-# I am a func
-```
-
-locals()
-```python
-other = "test"
-
-def foobar():
- name = "MING"
- gender = "male"
- for key,value in locals().items():
- print(key, "=", value)
-
-foobar()
-# 输出
-# name = MING
-# gender = male
-```
-
-----
-
-
+# 1.5 深入闭包与变量作用域
+
+
+
+---
+
+## 1. 作用域
+
+Python的作用域可以分为四种:
+- L (Local) 局部作用域
+- E (Enclosing) 闭包函数外的函数中
+- G (Global) 全局作用域
+- B (Built-in) 内建作用域
+
+变量/函数 的查找顺序:
+L –> E –> G –>B
+
+意思是,在局部找不到的,便去局部外的局部作用域找(例如 闭包),再找不到的就去全局作业域里找,再找不到就去内建作业域中找。
+
+会影响 变量/函数 作用范围的有
+- 函数:def 或 lambda
+- 类:class
+- 关键字:global noglobal
+- 文件:*py
+- 推导式:[],{},()等,仅限Py3.x中,Py2.x会出现变量泄露。
+
+1、赋值在前,引用在后
+```python
+# ------同作用域内------
+name = "MING"
+print(name)
+
+# ------不同作用域内------
+name = "MING"
+def main():
+ print(name)
+```
+2、引用在前,赋值在后(同一作用域内)
+```python
+print(name)
+name = "MING"
+
+# UnboundLocalError: local variable 'name' referenced before assignment
+```
+3、赋值在低层,引用在高层
+```python
+# L -> E -> G -> B
+# 从左到右,由低层到高层
+def main():
+ name = "MING"
+
+print(name)
+# NameError: name 'name' is not defined
+```
+
+
+## 2. 闭包
+
+闭包这个概念很重要噢。你一定要掌握。
+>在一个外函数中定义了一个内函数,内函数里运用了外函数的临时变量,并且外函数的返回值是内函数的引用。这样就构成了一个闭包。其实装饰函数,很多都是闭包。
+
+好像并不难理解,为什么初学者会觉得闭包难以理解呢?
+
+我解释一下,你就明白了。
+
+一般情况下,在我们认知当中,如果一个函数结束,函数的内部所有东西都会释放掉,还给内存,局部变量都会消失。但是闭包是一种特殊情况,如果外函数在结束的时候发现有自己的临时变量将来会在内部函数中用到,就把这个临时变量绑定给了内部函数,然后自己再结束。
+
+你可以看下面这段代码,就构成了闭包。在内函数里可以引用外函数的变量。
+```python
+def deco():
+ name = "MING"
+ def wrapper():
+ print(name)
+ return wrapper
+
+deco()()
+# 输出:MING
+```
+
+## 3. 改变作用域
+
+变量的作用域,与其定义(或赋值)的位置有关,但不是绝对相关。
+因为我们可以在某种程度上去改变`向上`的作用范围。
+
+- 关键字:global
+将 局部变量 变为全局变量
+
+- 关键字:nonlocal
+可以在闭包函数中,引用并使用闭包外部函数的变量(非全局的噢)
+
+global好理解,这里只讲下nonlocal。
+
+先来看个例子
+```python
+def deco():
+ age = 10
+ def wrapper():
+ age += 1
+ return wrapper
+
+deco()()
+```
+运行一下,会报错。
+```
+# UnboundLocalError: local variable 'age' referenced before assignment
+```
+但是这样就OK
+```
+def deco():
+ age = 10
+ def wrapper():
+ nonlocal age
+ age += 1
+ return wrapper
+
+deco()()
+# 输出:11
+```
+其实,你如果不使用 `+=`、`-=`等一类的操作,不加nonlocal也没有关系。这就展示了闭包的特性。
+```
+def deco():
+ age = 10
+ def wrapper():
+ print(age)
+ return wrapper
+
+deco()()
+# 输出:10
+```
+
+
+## 4. 变量集合
+
+在Python中,有两个内建函数,你可能用不到,但是需要掌握它们。
+- globals() :以dict的方式存储所有全局变量
+- locals():以dict的方式存储所有局部变量
+
+globals()
+```python
+def foo():
+ print("I am a func")
+
+def bar():
+ foo="I am a string"
+ foo_dup = globals().get("foo")
+ foo_dup()
+
+bar()
+# 输出
+# I am a func
+```
+
+locals()
+```python
+other = "test"
+
+def foobar():
+ name = "MING"
+ gender = "male"
+ for key,value in locals().items():
+ print(key, "=", value)
+
+foobar()
+# 输出
+# name = MING
+# gender = male
+```
+
+----
+
+
diff --git a/source/c01/c01_05.rst b/source/c01/c01_05.rst
old mode 100755
new mode 100644
index e3bd115..c1f7d89
--- a/source/c01/c01_05.rst
+++ b/source/c01/c01_05.rst
@@ -1,10 +1,12 @@
1.5 深入闭包与变量作用域
========================
+|image0|
+
--------------
-1.5.1 作用域
-------------
+1. 作用域
+---------
Python的作用域可以分为四种: - L (Local) 局部作用域 - E (Enclosing)
闭包函数外的函数中 - G (Global) 全局作用域 - B (Built-in) 内建作用域
@@ -52,8 +54,8 @@ Python的作用域可以分为四种: - L (Local) 局部作用域 - E (E
print(name)
# NameError: name 'name' is not defined
-1.5.2 闭包
-----------
+2. 闭包
+-------
闭包这个概念很重要噢。你一定要掌握。
>在一个外函数中定义了一个内函数,内函数里运用了外函数的临时变量,并且外函数的返回值是内函数的引用。这样就构成了一个闭包。其实装饰函数,很多都是闭包。
@@ -77,8 +79,8 @@ Python的作用域可以分为四种: - L (Local) 局部作用域 - E (E
deco()()
# 输出:MING
-1.5.3 改变作用域
-----------------
+3. 改变作用域
+-------------
变量的作用域,与其定义(或赋值)的位置有关,但不是绝对相关。
因为我们可以在某种程度上去改变\ ``向上``\ 的作用范围。
@@ -136,8 +138,8 @@ global好理解,这里只讲下nonlocal。
deco()()
# 输出:10
-1.5.4 变量集合
---------------
+4. 变量集合
+-----------
在Python中,有两个内建函数,你可能用不到,但是需要掌握它们。 - globals()
:以dict的方式存储所有全局变量 - locals():以dict的方式存储所有局部变量
@@ -177,7 +179,8 @@ locals()
--------------
-.. figure:: http://image.python-online.cn/image-20200320125724880.png
- :alt: 关注公众号,获取最新干货!
+|image1|
+
+.. |image0| image:: http://image.iswbm.com/20200602135014.png
+.. |image1| image:: http://image.iswbm.com/20200607174235.png
- 关注公众号,获取最新干货!
diff --git a/source/c01/c01_06.md b/source/c01/c01_06.md
index 20bfbf6..ab2a7a2 100644
--- a/source/c01/c01_06.md
+++ b/source/c01/c01_06.md
@@ -1,102 +1,104 @@
-# 1.6 深入理解元组存在的意义
-
----
-
-Python中有一个基础的数据结构,叫做元组(tuple),但是一般挺少有人会去用它的,因为在开发过程中,列表(list)基本已经能够满足我们的需求。
-
-即使是这样,你也千万不要就此认为元组是多余的。不然在面试中也不会经常被人问,元组和列表有啥区别?为什么需要元组?
-
-以下两点,第一点是大家所熟知的,而第二点可能只有老司机才会知道,只有学习了第二点,才算真正理解了元组存在的价值和意义。
-
-## 2.6.1 不可变列表
-
-这是`元组`区别于`列表`最显著的特征。
-
-- list:可变的序列
-- tuple:不可变的序列
-
-那什么是不可变的序列呢?
-
-那就是在元组对象生成后,诸如列表的`插入元素`、`删除元素`、`添加元素`、`清空元素`、`修改元素`等功能,在元组中通通没有,你是无法对其进行修改的。
-
-由于元组是不可变的,所以其方法也是很有限的。这里罗列一下。
-
-```
-# s1和s2都是元组
-s1=(1,2,3)
-s2=(4,5,6)
-
-# 拼接生成新元组
-s1+s2
-s1.__add__(s2)
-
-# 是否包含
-2 in s1
-s1.__contains__(2)
-
-# 统计元素包含的次数
-s1.count(2)
-
-# 获取元素
-s1[0]
-s1.__getitem__(0)
-
-# 找到2第一次出现的索引
-s1.index(2)
-
-# 获取长度
-len(s1)
-
-# 重复拼接
-s1*n
-```
-
-
-## 2.6.2 具名元组
-
-这个特性,我个人认为,才是元组存在的意义所在。
-
-只讲 具名元组,可能不太好理解。如果称之为 `带字段名的记录`,你可能就清楚了。
-
-这里举个例子,但是实现带字段名,需要一个库(collections)的支持,你需要导入它。
-```python
-from collections import namedtuple
-# 生成一个City类
-City = namedtuple("City", "name country polulation coordinates")
-# 实例化
-tokyo = City("Tokyo", 'JP', '36.93', ('35.68','139,69'))
-
-print(tokyo)
-# City(name='Tokyo', country='JP', polulation='36.93', coordinates=('35.68', '139,69'))
-
-print(tokyo.name)
-# Tokyo
-```
-
-看着有点像字典,是不是,但是他不是字典(获取数值的方法也与字典不同),字典是可变。元组在创建后,就无法再对其进行修改。这在某个程度上说明元组适合存放那些无需修改的数据。比如上面的,地名,国家,经纬度。
-
-除了上面的用法之处,这里还要介绍一些元组自己专有的属性。
-```python
-# 打印字段名
-print(City._fields)
-('name', 'country', 'polulation', 'coordinates')
-
-# 生成新实例
-LatLong = namedtuple('LatLong', 'lat long')
-Xiamen_tuple = ('Xiemen', 'China', '40,54', LatLong(24.26,118.03))
-Xiamen = City._make(Xiamen_tuple)
-
-print(Xiamen)
-# City(name='Xiemen', country='China', polulation='40,54', coordinates=(24.26, 118.03))
-
-# 将具名元组转为OrderDict
-Xiamen_dict = Xiamen._asdict()
-print(Xiamen_dict)
-# OrderedDict([('name', 'Xiemen'), ('country', 'China'), ('polulation', '40,54'), ('coordinates', LatLong(lat=24.26, long=118.03))])
-```
-
-总结一下,元组是一种很强大的可以当作记录来用的数据类型,这才是他存在的价值和意义所在。而为人所熟知的,它的第二个角色才是充当一个不可变的列表。
-
---------------
-
-
+# 1.6 有了列表,为什么 Python 还有元组?
+
+
+
+---
+
+Python中有一个基础的数据结构,叫做元组(tuple),但是一般挺少有人会去用它的,因为在开发过程中,列表(list)基本已经能够满足我们的需求。
+
+即使是这样,你也千万不要就此认为元组是多余的。不然在面试中也不会经常被人问,元组和列表有啥区别?为什么需要元组?
+
+以下两点,第一点是大家所熟知的,而第二点可能只有老司机才会知道,只有学习了第二点,才算真正理解了元组存在的价值和意义。
+
+## 1. 不可变列表
+
+这是`元组`区别于`列表`最显著的特征。
+
+- list:可变的序列
+- tuple:不可变的序列
+
+那什么是不可变的序列呢?
+
+那就是在元组对象生成后,诸如列表的`插入元素`、`删除元素`、`添加元素`、`清空元素`、`修改元素`等功能,在元组中通通没有,你是无法对其进行修改的。
+
+由于元组是不可变的,所以其方法也是很有限的。这里罗列一下。
+
+```
+# s1和s2都是元组
+s1=(1,2,3)
+s2=(4,5,6)
+
+# 拼接生成新元组
+s1+s2
+s1.__add__(s2)
+
+# 是否包含
+2 in s1
+s1.__contains__(2)
+
+# 统计元素包含的次数
+s1.count(2)
+
+# 获取元素
+s1[0]
+s1.__getitem__(0)
+
+# 找到2第一次出现的索引
+s1.index(2)
+
+# 获取长度
+len(s1)
+
+# 重复拼接
+s1*n
+```
+
+
+## 2. 具名元组
+
+这个特性,我个人认为,才是元组存在的意义所在。
+
+只讲 具名元组,可能不太好理解。如果称之为 `带字段名的记录`,你可能就清楚了。
+
+这里举个例子,但是实现带字段名,需要一个库(collections)的支持,你需要导入它。
+```python
+from collections import namedtuple
+# 生成一个City类
+City = namedtuple("City", "name country polulation coordinates")
+# 实例化
+tokyo = City("Tokyo", 'JP', '36.93', ('35.68','139,69'))
+
+print(tokyo)
+# City(name='Tokyo', country='JP', polulation='36.93', coordinates=('35.68', '139,69'))
+
+print(tokyo.name)
+# Tokyo
+```
+
+看着有点像字典,是不是,但是他不是字典(获取数值的方法也与字典不同),字典是可变。元组在创建后,就无法再对其进行修改。这在某个程度上说明元组适合存放那些无需修改的数据。比如上面的,地名,国家,经纬度。
+
+除了上面的用法之处,这里还要介绍一些元组自己专有的属性。
+```python
+# 打印字段名
+print(City._fields)
+('name', 'country', 'polulation', 'coordinates')
+
+# 生成新实例
+LatLong = namedtuple('LatLong', 'lat long')
+Xiamen_tuple = ('Xiemen', 'China', '40,54', LatLong(24.26,118.03))
+Xiamen = City._make(Xiamen_tuple)
+
+print(Xiamen)
+# City(name='Xiemen', country='China', polulation='40,54', coordinates=(24.26, 118.03))
+
+# 将具名元组转为OrderDict
+Xiamen_dict = Xiamen._asdict()
+print(Xiamen_dict)
+# OrderedDict([('name', 'Xiemen'), ('country', 'China'), ('polulation', '40,54'), ('coordinates', LatLong(lat=24.26, long=118.03))])
+```
+
+总结一下,元组是一种很强大的可以当作记录来用的数据类型,这才是他存在的价值和意义所在。而为人所熟知的,它的第二个角色才是充当一个不可变的列表。(以上都是个人看法,如有不同见解,欢迎留言讨论)
+
+--------------
+
+
diff --git a/source/c01/c01_06.rst b/source/c01/c01_06.rst
old mode 100755
new mode 100644
index a48254e..2609574
--- a/source/c01/c01_06.rst
+++ b/source/c01/c01_06.rst
@@ -1,5 +1,7 @@
-1.6 深入理解元组存在的意义
-==========================
+1.6 有了列表,为什么 Python 还有元组?
+======================================
+
+|image0|
--------------
@@ -9,8 +11,8 @@ Python中有一个基础的数据结构,叫做元组(tuple),但是一般
以下两点,第一点是大家所熟知的,而第二点可能只有老司机才会知道,只有学习了第二点,才算真正理解了元组存在的价值和意义。
-2.6.1 不可变列表
-----------------
+1. 不可变列表
+-------------
这是\ ``元组``\ 区别于\ ``列表``\ 最显著的特征。
@@ -53,8 +55,8 @@ Python中有一个基础的数据结构,叫做元组(tuple),但是一般
# 重复拼接
s1*n
-2.6.2 具名元组
---------------
+2. 具名元组
+-----------
这个特性,我个人认为,才是元组存在的意义所在。
@@ -100,11 +102,12 @@ Python中有一个基础的数据结构,叫做元组(tuple),但是一般
print(Xiamen_dict)
# OrderedDict([('name', 'Xiemen'), ('country', 'China'), ('polulation', '40,54'), ('coordinates', LatLong(lat=24.26, long=118.03))])
-总结一下,元组是一种很强大的可以当作记录来用的数据类型,这才是他存在的价值和意义所在。而为人所熟知的,它的第二个角色才是充当一个不可变的列表。
+总结一下,元组是一种很强大的可以当作记录来用的数据类型,这才是他存在的价值和意义所在。而为人所熟知的,它的第二个角色才是充当一个不可变的列表。(以上都是个人看法,如有不同见解,欢迎留言讨论)
--------------
-.. figure:: http://image.python-online.cn/image-20200320125724880.png
- :alt: 关注公众号,获取最新干货!
+|image1|
+
+.. |image0| image:: http://image.iswbm.com/20200602135014.png
+.. |image1| image:: http://image.iswbm.com/20200607174235.png
- 关注公众号,获取最新干货!
diff --git a/source/c01/c01_07.md b/source/c01/c01_07.md
index 2c5e7f7..f7f78a1 100644
--- a/source/c01/c01_07.md
+++ b/source/c01/c01_07.md
@@ -1,294 +1,284 @@
-# 1.7 15个Pythonic的代码示例
-
----
-
-Python由于语言的简洁性,让我们以人类思考的方式来写代码,新手更容易上手,老鸟更爱不释手。
-
-要写出 Pythonic(优雅的、地道的、整洁的)代码,还要平时多观察那些大牛代码,Github 上有很多非常优秀的源代码值得阅读,比如:requests、flask、tornado,这里小明收集了一些常见的 Pythonic 写法,帮助你养成写优秀代码的习惯。
-
-## 01. 变量交换
-
-Bad
-```python
-tmp = a
-a = b
-b = tmp
-```
-Pythonic
-```python
-a,b = b,a
-```
-
-## 02. 列表推导
-
-Bad
-```python
-my_list = []
-for i in range(10):
- my_list.append(i*2)
-```
-Pythonic
-```python
-my_list = [i*2 for i in range(10)]
-```
-
-## 03. 单行表达式
-
-虽然列表推导式由于其简洁性及表达性,被广受推崇。
-
-但是有许多可以写成单行的表达式,并不是好的做法。
-
-Bad
-```python
-print 'one'; print 'two'
-
-if x == 1: print 'one'
-
-if and :
- # do something
-```
-
-Pythonic
-```python
-print 'one'
-print 'two'
-
-if x == 1:
- print 'one'
-
-cond1 =
-cond2 =
-if cond1 and cond2:
- # do something
-```
-
-## 04. 带索引遍历
-
-Bad
-```python
-for i in range(len(my_list)):
- print(i, "-->", my_list[i])
-```
-Pythonic
-```python
-for i,item in enumerate(my_list):
- print(i, "-->",item)
-```
-
-## 05. 序列解包
-
-Pythonic
-```python
-a, *rest = [1, 2, 3]
-# a = 1, rest = [2, 3]
-
-a, *middle, c = [1, 2, 3, 4]
-# a = 1, middle = [2, 3], c = 4
-```
-
-## 06. 字符串拼接
-
-Bad
-```python
-letters = ['s', 'p', 'a', 'm']
-s=""
-for let in letters:
- s += let
-```
-
-Pythonic
-```python
-letters = ['s', 'p', 'a', 'm']
-word = ''.join(letters)
-```
-
-## 07. 真假判断
-
-Bad
-```python
-if attr == True:
- print 'True!'
-
-if attr == None:
- print 'attr is None!'
-```
-
-Pythonic
-```python
-if attr:
- print 'attr is truthy!'
-
-if not attr:
- print 'attr is falsey!'
-
-if attr is None:
- print 'attr is None!'
-```
-
-
-## 08. 访问字典元素
-
-Bad
-```python
-d = {'hello': 'world'}
-if d.has_key('hello'):
- print d['hello'] # prints 'world'
-else:
- print 'default_value'
-```
-
-Pythonic
-```python
-d = {'hello': 'world'}
-
-print d.get('hello', 'default_value') # prints 'world'
-print d.get('thingy', 'default_value') # prints 'default_value'
-
-# Or:
-if 'hello' in d:
- print d['hello']
-```
-
-## 09. 操作列表
-
-Bad
-```python
-a = [3, 4, 5]
-b = []
-for i in a:
- if i > 4:
- b.append(i)
-```
-
-Pythonic
-```python
-a = [3, 4, 5]
-b = [i for i in a if i > 4]
-# Or:
-b = filter(lambda x: x > 4, a)
-```
-
-Bad
-```python
-a = [3, 4, 5]
-for i in range(len(a)):
- a[i] += 3
-```
-
-Pythonic
-```python
-a = [3, 4, 5]
-a = [i + 3 for i in a]
-# Or:
-a = map(lambda i: i + 3, a)
-```
-
-## 10. 文件读取
-Bad
-```python
-f = open('file.txt')
-a = f.read()
-print a
-f.close()
-```
-
-Pythonic
-```python
-with open('file.txt') as f:
- for line in f:
- print line
-```
-
-## 11. 代码续行
-Bad
-```python
-my_very_big_string = """For a long time I used to go to bed early. Sometimes, \
- when I had put out my candle, my eyes would close so quickly that I had not even \
- time to say “I’m going to sleep.”"""
-
-from some.deep.module.inside.a.module import a_nice_function, another_nice_function, \
- yet_another_nice_function
-```
-
-Pythonic
-```python
-my_very_big_string = (
- "For a long time I used to go to bed early. Sometimes, "
- "when I had put out my candle, my eyes would close so quickly "
- "that I had not even time to say “I’m going to sleep.”"
-)
-
-from some.deep.module.inside.a.module import (
- a_nice_function, another_nice_function, yet_another_nice_function)
-```
-
-## 12. 显式代码
-Bad
-```python
-def make_complex(*args):
- x, y = args
- return dict(**locals())
-```
-
-Pythonic
-```python
-def make_complex(x, y):
- return {'x': x, 'y': y}
-```
-
-## 13. 使用占位符
-
-Pythonic
-```python
-filename = 'foobar.txt'
-basename, _, ext = filename.rpartition('.')
-```
-
-## 14. 链式比较
-
-Bad
-```python
-if age > 18 and age < 60:
- print("young man")
-```
-
-Pythonic
-```python
-if 18 < age < 60:
- print("young man")
-```
-
-理解了链式比较操作,那么你应该知道为什么下面这行代码输出的结果是 False
-```
->>> False == False == True
-False
-```
-
-## 15. 三目运算
-
-这个保留意见。随使用习惯就好。
-
-Bad
-```python
-if a > 2:
- b = 2
-else:
- b = 1
-#b = 2
-
-```
-Pythonic
-```python
-a = 3
-
-b = 2 if a > 2 else 1
-#b = 2
-```
-
-## 参考文档
-
-- http://docs.python-guide.org/en/latest/writing/style/
-- https://foofish.net/idiomatic_part2.html
-
-----
-
+# 1.7 15个Pythonic的代码示例
+
+
+Python由于语言的简洁性,让我们以人类思考的方式来写代码,新手更容易上手,老鸟更爱不释手。
+
+要写出 Pythonic(优雅的、地道的、整洁的)代码,还要平时多观察那些大牛代码,这里明哥收集了一些比较常见的 Pythonic 写法,帮助你养成写优秀代码的习惯。
+
+## 01. 变量交换
+
+交换两个变量的值,正常都会想利用一个中间临时变量来过渡。
+```python
+tmp = a
+a = b
+b = tmp
+```
+能用一行代码解决的(并且不影响可读性的),决不用三行代码。
+```python
+a,b = b,a
+```
+
+## 02. 列表推导
+
+下面是一个非常简单的 for 循环。
+```python
+my_list = []
+for i in range(10):
+ my_list.append(i*2)
+```
+在一个 for 循环中,如果逻辑比较简单,不如试用一下列表的列表推导式,虽然只有一行代码,但也逻辑清晰。
+
+```python
+my_list = [i*2 for i in range(10)]
+```
+
+## 03. 单行表达式
+
+上面两个案例,都将多行代码用另一种方式写成了一行代码。
+
+这并不意味着,代码行数越少,就越 Pythonic 。
+
+比如下面这样写,就不推荐。
+```python
+print('hello'); print('world')
+
+if x == 1: print('hello,world')
+
+if and :
+ # do something
+```
+
+建议还是按照如下的写法来
+```python
+print('hello')
+print('world')
+
+if x == 1:
+ print('hello,world')
+
+cond1 =
+cond2 =
+if cond1 and cond2:
+ # do something
+```
+
+## 04. 带索引遍历
+
+使用 for 循环时,如何取得对应的索引,初学者习惯使用 range + len 函数
+```python
+for i in range(len(my_list)):
+ print(i, "-->", my_list[i])
+```
+更好的做法是利用 enumerate 这个内置函数
+```python
+for i,item in enumerate(my_list):
+ print(i, "-->",item)
+```
+
+## 05. 序列解包
+
+使用 `*` 可以对一个列表解包
+
+```python
+a, *rest = [1, 2, 3]
+# a = 1, rest = [2, 3]
+
+a, *middle, c = [1, 2, 3, 4]
+# a = 1, middle = [2, 3], c = 4
+```
+
+## 06. 字符串拼接
+
+如果一个列表(或者可迭代对象)中的所有元素都是字符串对象,想要将他们连接起来,通常做法是
+```python
+letters = ['s', 'p', 'a', 'm']
+s=""
+for let in letters:
+ s += let
+```
+
+更推荐的做法是使用 join 函数
+```python
+letters = ['s', 'p', 'a', 'm']
+word = ''.join(letters)
+```
+
+## 07. 真假判断
+
+判断一个变量是否为真(假),新手习惯直接使用 `==` 与 True、False、None 进行对比
+```python
+if attr == True:
+ print('True!')
+
+if attr == None:
+ print('attr is None!')
+```
+
+实际上,`""`、`[]`、`{}` 这些没有任何元素的容器都是假值,可直接使用 `if not xx` 来判断。
+```python
+if attr:
+ print('attr is truthy!')
+
+if not attr:
+ print('attr is falsey!')
+```
+
+
+## 08. 访问字典元素
+
+当直接使用 `[]` 来访问字典里的元素时,若key不存在,是会抛异常的,所以新会可能会先判断一下是否有这个 key,有再取之。
+```python
+d = {'hello': 'world'}
+if d.has_key('hello'):
+ print(d['hello']) # prints 'world'
+else:
+ print('default_value')
+```
+
+更推荐的做法是使用 `get` 来取,如果没有该 key 会默认返回 None(当然你也可以设置默认返回值)
+```python
+d = {'hello': 'world'}
+
+print(d.get('hello', 'default_value')) # prints 'world'
+print(d.get('thingy', 'default_value')) # prints 'default_value'
+```
+
+## 09. 操作列表
+
+下面这段代码,会根据条件过滤过列表中的元素
+```python
+a = [3, 4, 5]
+b = []
+for i in a:
+ if i > 4:
+ b.append(i)
+```
+实际上可以使用列表推导或者高阶函数 filter 来实现
+```python
+a = [3, 4, 5]
+b = [i for i in a if i > 4]
+# Or:
+b = filter(lambda x: x > 4, a)
+```
+
+除了 filter 之外,还有 map、reduce 这两个函数也很好用
+```python
+a = [3, 4, 5]
+b = map(lambda i: i + 3, a)
+# b: [6,7,8]
+```
+
+## 10. 文件读取
+
+文件读取是非常常用的操作,在使用完句柄后,是需要手动调用 close 函数来关闭句柄的
+```python
+fp = open('file.txt')
+print(fp.read())
+fp.close()
+```
+如果代码写得太长,即使你知道需要手动关闭句柄,却也会经常会漏掉。因此推荐养成习惯使用 `with open` 来读写文件,上下文管理器会自动执行关闭句柄的操作
+```python
+with open('file.txt') as fp:
+ for line in fp.readlines():
+ print(line)
+```
+
+## 11. 代码续行
+
+将一个长度较长的字符串放在一行中,是很影响代码可读性的(下面代码可向左滑动)
+
+```python
+long_string = 'For a long time I used to go to bed early. Sometimes, when I had put out my candle, my eyes would close so quickly that I had not even time to say “I’m going to sleep.”'
+```
+
+稍等注重代码可读性的人,会使用三个引号 `\`来续写
+
+```python
+long_string = 'For a long time I used to go to bed early. ' \
+ 'Sometimes, when I had put out my candle, ' \
+ 'my eyes would close so quickly that I had not even time to say “I’m going to sleep.”'
+```
+
+不过,对我来说,我更喜欢这样子写 使用括号包裹 `()`
+```python
+long_string = (
+ "For a long time I used to go to bed early. Sometimes, "
+ "when I had put out my candle, my eyes would close so quickly "
+ "that I had not even time to say “I’m going to sleep.”"
+)
+```
+
+导包的时候亦是如此
+```python
+from some.deep.module.inside.a.module import (
+ a_nice_function, another_nice_function, yet_another_nice_function)
+```
+
+## 12. 显式代码
+
+有时候出于需要,我们会使用一些特殊的魔法来使代码适应更多的场景不确定性。
+```python
+def make_complex(*args):
+ x, y = args
+ return dict(**locals())
+```
+但若非必要,请不要那么做。无端增加代码的不确定性,会让原先本就动态的语言写出更加动态的代码。
+```python
+def make_complex(x, y):
+ return {'x': x, 'y': y}
+```
+
+## 13. 使用占位符
+
+对于暂不需要,却又不得不接收的的变量,请使用占位符
+```python
+filename = 'foobar.txt'
+basename, _, ext = filename.rpartition('.')
+```
+
+## 14. 链式比较
+
+对于下面这种写法
+```python
+score = 85
+if score > 80 and score < 90:
+ print("良好")
+```
+其实还有更好的写法
+```python
+score = 85
+if 80 < score < 90:
+ print("良好")
+```
+
+如果你理解了上面的链式比较操作,那么你应该知道为什么下面这行代码输出的结果是 False
+```
+>>> False == False == True
+False
+```
+
+## 15. 三目运算
+
+对于简单的判断并赋值
+```python
+age = 20
+if age > 18:
+ type = "adult"
+else:
+ type = "teenager"
+```
+其实是可以使用三目运算,一行搞定。
+```python
+age = 20
+b = "adult" if age > 18 else "teenager"
+```
+
+
+
+## 参考文档
+
+- http://docs.python-guide.org/en/latest/writing/style/
+- https://foofish.net/idiomatic_part2.html
+
+----
+
diff --git a/source/c01/c01_07.rst b/source/c01/c01_07.rst
old mode 100755
new mode 100644
index d9166e4..52a6391
--- a/source/c01/c01_07.rst
+++ b/source/c01/c01_07.rst
@@ -1,19 +1,17 @@
1.7 15个Pythonic的代码示例
==========================
---------------
-
+|image0|
Python由于语言的简洁性,让我们以人类思考的方式来写代码,新手更容易上手,老鸟更爱不释手。
要写出
-Pythonic(优雅的、地道的、整洁的)代码,还要平时多观察那些大牛代码,Github
-上有很多非常优秀的源代码值得阅读,比如:requests、flask、tornado,这里小明收集了一些常见的
+Pythonic(优雅的、地道的、整洁的)代码,还要平时多观察那些大牛代码,这里明哥收集了一些比较常见的
Pythonic 写法,帮助你养成写优秀代码的习惯。
01. 变量交换
------------
-Bad
+交换两个变量的值,正常都会想利用一个中间临时变量来过渡。
.. code:: python
@@ -21,7 +19,7 @@ Bad
a = b
b = tmp
-Pythonic
+能用一行代码解决的(并且不影响可读性的),决不用三行代码。
.. code:: python
@@ -30,7 +28,7 @@ Pythonic
02. 列表推导
------------
-Bad
+下面是一个非常简单的 for 循环。
.. code:: python
@@ -38,7 +36,8 @@ Bad
for i in range(10):
my_list.append(i*2)
-Pythonic
+在一个 for
+循环中,如果逻辑比较简单,不如试用一下列表的列表推导式,虽然只有一行代码,但也逻辑清晰。
.. code:: python
@@ -47,30 +46,30 @@ Pythonic
03. 单行表达式
--------------
-虽然列表推导式由于其简洁性及表达性,被广受推崇。
+上面两个案例,都将多行代码用另一种方式写成了一行代码。
-但是有许多可以写成单行的表达式,并不是好的做法。
+这并不意味着,代码行数越少,就越 Pythonic 。
-Bad
+比如下面这样写,就不推荐。
.. code:: python
- print 'one'; print 'two'
+ print('hello'); print('world')
- if x == 1: print 'one'
+ if x == 1: print('hello,world')
if and :
# do something
-Pythonic
+建议还是按照如下的写法来
.. code:: python
- print 'one'
- print 'two'
+ print('hello')
+ print('world')
if x == 1:
- print 'one'
+ print('hello,world')
cond1 =
cond2 =
@@ -80,14 +79,14 @@ Pythonic
04. 带索引遍历
--------------
-Bad
+使用 for 循环时,如何取得对应的索引,初学者习惯使用 range + len 函数
.. code:: python
for i in range(len(my_list)):
print(i, "-->", my_list[i])
-Pythonic
+更好的做法是利用 enumerate 这个内置函数
.. code:: python
@@ -97,7 +96,7 @@ Pythonic
05. 序列解包
------------
-Pythonic
+使用 ``*`` 可以对一个列表解包
.. code:: python
@@ -110,7 +109,7 @@ Pythonic
06. 字符串拼接
--------------
-Bad
+如果一个列表(或者可迭代对象)中的所有元素都是字符串对象,想要将他们连接起来,通常做法是
.. code:: python
@@ -119,7 +118,7 @@ Bad
for let in letters:
s += let
-Pythonic
+更推荐的做法是使用 join 函数
.. code:: python
@@ -129,59 +128,57 @@ Pythonic
07. 真假判断
------------
-Bad
+判断一个变量是否为真(假),新手习惯直接使用 ``==`` 与 True、False、None
+进行对比
.. code:: python
if attr == True:
- print 'True!'
+ print('True!')
if attr == None:
- print 'attr is None!'
+ print('attr is None!')
-Pythonic
+实际上,\ ``""``\ 、\ ``[]``\ 、\ ``{}``
+这些没有任何元素的容器都是假值,可直接使用 ``if not xx`` 来判断。
.. code:: python
if attr:
- print 'attr is truthy!'
+ print('attr is truthy!')
if not attr:
- print 'attr is falsey!'
-
- if attr is None:
- print 'attr is None!'
+ print('attr is falsey!')
08. 访问字典元素
----------------
-Bad
+当直接使用 ``[]``
+来访问字典里的元素时,若key不存在,是会抛异常的,所以新会可能会先判断一下是否有这个
+key,有再取之。
.. code:: python
d = {'hello': 'world'}
if d.has_key('hello'):
- print d['hello'] # prints 'world'
+ print(d['hello']) # prints 'world'
else:
- print 'default_value'
+ print('default_value')
-Pythonic
+更推荐的做法是使用 ``get`` 来取,如果没有该 key 会默认返回
+None(当然你也可以设置默认返回值)
.. code:: python
d = {'hello': 'world'}
- print d.get('hello', 'default_value') # prints 'world'
- print d.get('thingy', 'default_value') # prints 'default_value'
-
- # Or:
- if 'hello' in d:
- print d['hello']
+ print(d.get('hello', 'default_value')) # prints 'world'
+ print(d.get('thingy', 'default_value')) # prints 'default_value'
09. 操作列表
------------
-Bad
+下面这段代码,会根据条件过滤过列表中的元素
.. code:: python
@@ -191,7 +188,7 @@ Bad
if i > 4:
b.append(i)
-Pythonic
+实际上可以使用列表推导或者高阶函数 filter 来实现
.. code:: python
@@ -200,74 +197,73 @@ Pythonic
# Or:
b = filter(lambda x: x > 4, a)
-Bad
+除了 filter 之外,还有 map、reduce 这两个函数也很好用
.. code:: python
a = [3, 4, 5]
- for i in range(len(a)):
- a[i] += 3
-
-Pythonic
-
-.. code:: python
-
- a = [3, 4, 5]
- a = [i + 3 for i in a]
- # Or:
- a = map(lambda i: i + 3, a)
+ b = map(lambda i: i + 3, a)
+ # b: [6,7,8]
10. 文件读取
------------
-Bad
+文件读取是非常常用的操作,在使用完句柄后,是需要手动调用 close
+函数来关闭句柄的
.. code:: python
- f = open('file.txt')
- a = f.read()
- print a
- f.close()
+ fp = open('file.txt')
+ print(fp.read())
+ fp.close()
-Pythonic
+如果代码写得太长,即使你知道需要手动关闭句柄,却也会经常会漏掉。因此推荐养成习惯使用
+``with open`` 来读写文件,上下文管理器会自动执行关闭句柄的操作
.. code:: python
- with open('file.txt') as f:
- for line in f:
- print line
+ with open('file.txt') as fp:
+ for line in fp.readlines():
+ print(line)
11. 代码续行
------------
-Bad
+将一个长度较长的字符串放在一行中,是很影响代码可读性的(下面代码可向左滑动)
.. code:: python
- my_very_big_string = """For a long time I used to go to bed early. Sometimes, \
- when I had put out my candle, my eyes would close so quickly that I had not even \
- time to say “I’m going to sleep.”"""
+ long_string = 'For a long time I used to go to bed early. Sometimes, when I had put out my candle, my eyes would close so quickly that I had not even time to say “I’m going to sleep.”'
+
+稍等注重代码可读性的人,会使用三个引号 ``\``\ 来续写
- from some.deep.module.inside.a.module import a_nice_function, another_nice_function, \
- yet_another_nice_function
+.. code:: python
-Pythonic
+ long_string = 'For a long time I used to go to bed early. ' \
+ 'Sometimes, when I had put out my candle, ' \
+ 'my eyes would close so quickly that I had not even time to say “I’m going to sleep.”'
+
+不过,对我来说,我更喜欢这样子写 使用括号包裹 ``()``
.. code:: python
- my_very_big_string = (
+ long_string = (
"For a long time I used to go to bed early. Sometimes, "
"when I had put out my candle, my eyes would close so quickly "
"that I had not even time to say “I’m going to sleep.”"
)
+导包的时候亦是如此
+
+.. code:: python
+
from some.deep.module.inside.a.module import (
a_nice_function, another_nice_function, yet_another_nice_function)
12. 显式代码
------------
-Bad
+有时候出于需要,我们会使用一些特殊的魔法来使代码适应更多的场景不确定性。
.. code:: python
@@ -275,7 +271,7 @@ Bad
x, y = args
return dict(**locals())
-Pythonic
+但若非必要,请不要那么做。无端增加代码的不确定性,会让原先本就动态的语言写出更加动态的代码。
.. code:: python
@@ -285,7 +281,7 @@ Pythonic
13. 使用占位符
--------------
-Pythonic
+对于暂不需要,却又不得不接收的的变量,请使用占位符
.. code:: python
@@ -295,21 +291,24 @@ Pythonic
14. 链式比较
------------
-Bad
+对于下面这种写法
.. code:: python
- if age > 18 and age < 60:
- print("young man")
+ score = 85
+ if score > 80 and score < 90:
+ print("良好")
-Pythonic
+其实还有更好的写法
.. code:: python
- if 18 < age < 60:
- print("young man")
+ score = 85
+ if 80 < score < 90:
+ print("良好")
-理解了链式比较操作,那么你应该知道为什么下面这行代码输出的结果是 False
+如果你理解了上面的链式比较操作,那么你应该知道为什么下面这行代码输出的结果是
+False
::
@@ -319,26 +318,22 @@ Pythonic
15. 三目运算
------------
-这个保留意见。随使用习惯就好。
-
-Bad
+对于简单的判断并赋值
.. code:: python
- if a > 2:
- b = 2
+ age = 20
+ if age > 18:
+ type = "adult"
else:
- b = 1
- #b = 2
+ type = "teenager"
-Pythonic
+其实是可以使用三目运算,一行搞定。
.. code:: python
- a = 3
-
- b = 2 if a > 2 else 1
- #b = 2
+ age = 20
+ b = "adult" if age > 18 else "teenager"
参考文档
--------
@@ -348,7 +343,8 @@ Pythonic
--------------
-.. figure:: http://image.python-online.cn/image-20200320125724880.png
- :alt: 关注公众号,获取最新干货!
+|image1|
+
+.. |image0| image:: http://image.iswbm.com/20200602135014.png
+.. |image1| image:: http://image.iswbm.com/20200607174235.png
- 关注公众号,获取最新干货!
diff --git a/source/c01/c01_08.md b/source/c01/c01_08.md
index 619365f..1c1730f 100644
--- a/source/c01/c01_08.md
+++ b/source/c01/c01_08.md
@@ -1,8 +1,10 @@
# 1.8 新式类和经典类的区别?
+
+
---
-## 2.8.1 版本支持 / 写法差异
+## 1. 版本支持 / 写法差异
在Python 2.x 中
@@ -36,12 +38,12 @@ class Ming(object):
pass
```
-## 2.8.2 使用方法 / 独特属性
+## 2. 使用方法 / 独特属性
经典类无法使用super()
-
+
经典类的类型是 classobj
-
+
新式类的类型是 type,保持class与type的统一。
@@ -78,7 +80,7 @@ AttributeError: Kls01 instance has no attribute 'name'
-## 2.8.3 MRO 查找算法的演变
+## 3. MRO 查找算法的演变
**经典类中**
@@ -108,7 +110,7 @@ print inspect.getmro(D)
非常好理解,但是在菱形继承时,方法的调用会出现问题。
-
+
假设 d 是 D 的一个实例,那么执行 d.show()是调用 A.show() 呢 还是调用 C.show()呢?
@@ -120,7 +122,7 @@ print inspect.getmro(D)
Python 2.2 的新式类 MRO 计算方式和经典类 MRO 的计算方式非常相似:它仍然采用从左至右的深度优先遍历,但是如果遍历中出现重复的类,只保留最后一个。重新考虑上面「菱形继承」的例子:
-
+
同样地,我们也来验证一下。另说明,在新式类中,除用inspect外,可以直接通过__mro__属性获取类的 MRO。
@@ -150,7 +152,7 @@ print inspect.getmro(D)
再来看一个复杂一点的例子。
-
+
如果只依靠上面的算法,我们来一起算下,其继承关系是怎样的。
@@ -192,7 +194,7 @@ order (MRO) for bases X, Y
例如下面这张图。
-
+
计算过程,会采用一种 merge算法。它的基本思想如下:
@@ -250,4 +252,4 @@ A.__mro__
---
-
+
diff --git a/source/c01/c01_08.rst b/source/c01/c01_08.rst
old mode 100755
new mode 100644
index 4df2492..33aaf4c
--- a/source/c01/c01_08.rst
+++ b/source/c01/c01_08.rst
@@ -1,10 +1,12 @@
1.8 新式类和经典类的区别?
==========================
+|image0|
+
--------------
-2.8.1 版本支持 / 写法差异
--------------------------
+1. 版本支持 / 写法差异
+----------------------
在Python 2.x 中
@@ -43,10 +45,10 @@
class Ming(object):
pass
-2.8.2 使用方法 / 独特属性
--------------------------
+2. 使用方法 / 独特属性
+----------------------
-经典类无法使用super() |image0| 经典类的类型是 classobj |image1|
+经典类无法使用super() |image1| 经典类的类型是 classobj |image2|
新式类的类型是 type,保持class与type的统一。
@@ -83,8 +85,8 @@
kls01.name
AttributeError: Kls01 instance has no attribute 'name'
-2.8.3 MRO 查找算法的演变
-------------------------
+3. MRO 查找算法的演变
+---------------------
**经典类中**
@@ -114,7 +116,7 @@
非常好理解,但是在菱形继承时,方法的调用会出现问题。
-|image2|
+|image3|
假设 d 是 D 的一个实例,那么执行 d.show()是调用 A.show() 呢 还是调用
C.show()呢?
@@ -132,7 +134,7 @@ A.show(),这显然是不合理的。所以这才有了后来的一步一步优
Python 2.2 的新式类 MRO 计算方式和经典类 MRO
的计算方式非常相似:它仍然采用从左至右的深度优先遍历,但是如果遍历中出现重复的类,只保留最后一个。重新考虑上面「菱形继承」的例子:
-|image3|
+|image4|
同样地,我们也来验证一下。另说明,在新式类中,除用inspect外,可以直接通过__mro__属性获取类的
MRO。
@@ -163,7 +165,7 @@ MRO。
再来看一个复杂一点的例子。
-|image4|
+|image5|
如果只依靠上面的算法,我们来一起算下,其继承关系是怎样的。
@@ -209,7 +211,7 @@ C 搜索顺序中 X 和 Y 互换仍然不能解决问题,这时候它又会和
例如下面这张图。
-|image5|
+|image6|
计算过程,会采用一种 merge算法。它的基本思想如下:
@@ -269,15 +271,14 @@ C 搜索顺序中 X 和 Y 互换仍然不能解决问题,这时候它又会和
--------------
-.. figure:: http://image.python-online.cn/image-20200320125724880.png
- :alt: 关注公众号,获取最新干货!
-
- 关注公众号,获取最新干货!
+|image7|
-.. |image0| image:: https://ws1.sinaimg.cn/large/8f640247gy1fyi765tblqj20cy05cwfx.jpg
-.. |image1| image:: https://ws1.sinaimg.cn/large/8f640247gy1fyi76mgwcbj20b708cmyo.jpg
-.. |image2| image:: https://ws1.sinaimg.cn/large/8f640247gy1fyi77urc3lj206108n74e.jpg
-.. |image3| image:: https://ws1.sinaimg.cn/large/8f640247gy1fyi78drp24j20680bjaaa.jpg
-.. |image4| image:: https://ws1.sinaimg.cn/large/8f640247gy1fyi78odu23j20740bomxh.jpg
-.. |image5| image:: https://ws1.sinaimg.cn/large/8f640247gy1fyi78xuzibj20940ayq39.jpg
+.. |image0| image:: http://image.iswbm.com/20200602135014.png
+.. |image1| image:: http://image.iswbm.com/20201004123025.png
+.. |image2| image:: http://image.iswbm.com/20201004123036.png
+.. |image3| image:: http://image.iswbm.com/20201004123106.png
+.. |image4| image:: http://image.iswbm.com/20201004123056.png
+.. |image5| image:: http://image.iswbm.com/20201004123115.png
+.. |image6| image:: http://image.iswbm.com/20201004123126.png
+.. |image7| image:: http://image.iswbm.com/20200607174235.png
diff --git a/source/c01/c01_09.md b/source/c01/c01_09.md
index b9d825b..697f7e0 100644
--- a/source/c01/c01_09.md
+++ b/source/c01/c01_09.md
@@ -1,12 +1,14 @@
# 1.9 多继承与Mixin设计模式
+
+
---
类的单继承,是我们再熟悉不过的,写起来也毫不费力。而多继承呢,见得很多,写得很少。在很多的项目代码里,你还会见到一种很奇怪的类,他们有一个命名上的共同点,就是在类名的结尾,都喜欢用 Mixin。
-## 1.9.1 认识Mixin模式
+## 1. 认识Mixin模式
那我们今天就来讲讲这个 Mixin,对于这个Mixin,如何理解?它其实是一种设计模式,如果开发者之间没有产生这样一种设计模式的共识,那么设计模式将不复存在。
@@ -42,7 +44,7 @@ class Airplane(Vehicle, PlaneMixin):
-## 1.9.2 不使用Mixin的弊端
+## 2. 不使用Mixin的弊端
你肯定会问,不使用 Mixin 行吗?
@@ -66,5 +68,5 @@ class Airplane(Vehicle, PlaneMixin):
---
-
+
diff --git a/source/c01/c01_09.rst b/source/c01/c01_09.rst
old mode 100755
new mode 100644
index 32fe3bd..ddc0e8e
--- a/source/c01/c01_09.rst
+++ b/source/c01/c01_09.rst
@@ -1,13 +1,15 @@
1.9 多继承与Mixin设计模式
=========================
+|image0|
+
--------------
类的单继承,是我们再熟悉不过的,写起来也毫不费力。而多继承呢,见得很多,写得很少。在很多的项目代码里,你还会见到一种很奇怪的类,他们有一个命名上的共同点,就是在类名的结尾,都喜欢用
Mixin。
-1.9.1 认识Mixin模式
--------------------
+1. 认识Mixin模式
+----------------
那我们今天就来讲讲这个
Mixin,对于这个Mixin,如何理解?它其实是一种设计模式,如果开发者之间没有产生这样一种设计模式的共识,那么设计模式将不复存在。
@@ -46,8 +48,8 @@ Mixin 类,一般都要求开发者遵循规范,在类名末尾加上 Mixin
- 功能单一:若有多个功能,那就写多个Mixin类;
- 绝对独立:不能依赖于子类的实现;子类即便没有继承这个Mixin类,也照样可以工作,就是缺少了某个功能。
-1.9.2 不使用Mixin的弊端
------------------------
+2. 不使用Mixin的弊端
+--------------------
你肯定会问,不使用 Mixin 行吗?
@@ -73,7 +75,8 @@ C3 算法,如果你还不清楚,可以点击我的另一篇文章 ,了解
--------------
-.. figure:: http://image.python-online.cn/image-20200320125724880.png
- :alt: 关注公众号,获取最新干货!
+|image1|
+
+.. |image0| image:: http://image.iswbm.com/20200602135014.png
+.. |image1| image:: http://image.iswbm.com/20200607174235.png
- 关注公众号,获取最新干货!
diff --git a/source/c01/c01_10.md b/source/c01/c01_10.md
index 3c5fc47..1538e31 100644
--- a/source/c01/c01_10.md
+++ b/source/c01/c01_10.md
@@ -1,2191 +1,193 @@
-# 1.10 Python 黑魔法指南 50 例
+# 1.10 如何修改 CentOS 6.x 上默认Python
----
+
-> 作者:王炳明,微信公众号《Python编程时光》。版权归个人所有,欢迎分享,仅用于学习交流,但勿用作商业用途,违者必究。
+最近在工作中遇到一个问题,就是有一个功能希望在各种服务器上实现,而服务器上的系统版本可能都不一样,有的是 CentOS 6.x,有的是 CentOS 7.x 。
+需要说明的一点是,CentOS 6.x 上的 Python 版本是 2.6.x 的,而 CentOS 7.x 上的 Python 版本是 2.7.x 的,这意味着我要实现的功能要适配这两种版本的系统。
-## 01. 默默无闻的省略号很好用
+你可能会说,这有什么的,自己写的时候,注意一下就好了。
-在Python中,一切皆对象,省略号也不例外。
+事情其实没有那么容易,我要实现的功能是基于一个框架进行定制,需要修改不少的框架代码。这个框架在不同的 Linux 版本上,是有不同的版本的,而且差异巨大,曾经想过在 CentOS 6.x 和 CentOS 7.x 将这个框架安装成同一个版本,最后还是失败了,无法安装,原因就是高版本需要 Python2.7,而 CentOS 6.x 上只有 Python2.6。
-在 Python 3 中你可以直接写 `...` 来得到它
-```python
->>> ...
-Ellipsis
->>> type(...)
-
-```
-
-而在 Python 2 中没有`...` 这个语法,只能直接写Ellipsis来获取。
-```python
->>> Ellipsis
-Ellipsis
->>> type(Ellipsis)
-
->>>
-```
-
-它转为布尔值时为真
-```python
->>> bool(...)
-True
-```
-最后,这东西是一个单例。
-```python
->>> id(...)
-4362672336
->>> id(...)
-4362672336
-```
-
-那这东西有啥用呢?
-
-1. 它是 Numpy 的一个语法糖
-2. 在 Python 3 中可以使用 ... 代替 pass
-
-```shell
-$ cat demo.py
-def func01():
- ...
-
-def func02():
- pass
-
-func01()
-func02()
-
-print("ok")
-
-$ python3 demo.py
-ok
-```
-
-
-
-## 02. 使用 end 来结束代码块
-
-有不少编程语言,循环、判断代码块需要用 end 标明结束,这样一定程度上会使代码逻辑更加清晰一点。
-
-但是其实在 Python 这种严格缩进的语言里并没有必要这样做。
-
-如果你真的想用,也不是没有办法,具体你看下面这个例子。
-
-```python
-__builtins__.end = None
-
-
-def my_abs(x):
- if x > 0:
- return x
- else:
- return -x
- end
-end
-
-print(my_abs(10))
-print(my_abs(-10))
-```
-
-执行后,输出如下
-
-```shell
-[root@localhost ~]$ python demo.py
-10
-10
-```
-
-## 03. 可直接运行的 zip 包
-
-我们可以经常看到有 Python 包,居然可以以 zip 包进行发布,并且可以不用解压直接使用。
-
-这与大多数人的认识的 Python 包格式不一样,正常人认为 Python 包的格式要嘛 是 egg,要嘛是whl 格式。
-
-那么这个zip 是如何制作的呢,请看下面的示例。
-
-```shell
-[root@localhost ~]# ls -l demo
-total 8
--rw-r--r-- 1 root root 30 May 8 19:27 calc.py
--rw-r--r-- 1 root root 35 May 8 19:33 __main__.py
-[root@localhost ~]#
-[root@localhost ~]# cat demo/__main__.py
-import calc
-
-print(calc.add(2, 3))
-[root@localhost ~]#
-[root@localhost ~]# cat demo/calc.py
-def add(x, y):
- return x+y
-[root@localhost ~]#
-[root@localhost ~]# python -m zipfile -c demo.zip demo/*
-[root@localhost ~]#
-```
-
-制作完成后,我们可以执行用 python 去执行它
-
-```shell
-[root@localhost ~]# python demo.zip
-5
-[root@localhost ~]#
-```
-
-## 04. 反斜杠的倔强: 不写最后
-
-`\` 在 Python 中的用法主要有两种
-
-**1、在行尾时,用做续行符**
-
- ```python
-[root@localhost ~]$ cat demo.py
-print("hello "\
- "world")
-[root@localhost ~]$
-[root@localhost ~]$ python demo.py
-hello world
- ```
-
-
-
-**2、在字符串中,用做转义字符,可以将普通字符转化为有特殊含义的字符。**
-
-```python
->>> str1='\nhello' #换行
->>> print(str1)
-
-hello
->>> str2='\thello' #tab
->>> print(str2)
- hello
-```
-
-但是如果你用单`\`结尾是会报语法错误的
-
-```python
->>> str3="\"
- File "", line 1
- str3="\"
- ^
-SyntaxError: EOL while scanning string literal
-```
-
-就算你指定它是个 raw 字符串,也不行。
-
-```python
->>> str3=r"\"
- File "", line 1
- str3=r"\"
- ^
-SyntaxError: EOL while scanning string literal
-```
-
-## 05. 单行实现 for 死循环如何写?
-
-如果让你在不借助 while ,只使用 for 来写一个死循环?
-
-**你会写吗?**
-
-**如果你还说简单,你可以自己试一下。**
-
-...
-
-如果你尝试后,仍然写不出来,那我给出自己的做法。
-
-```python
-for i in iter(int, 1):pass
-```
-
-
-
-**是不是傻了?iter 还有这种用法?这为啥是个死循环?**
-
-关于这个问题,你如果看中文网站,可能找不到相关资料。
-
-还好你可以通过 IDE 看py源码里的注释内容,介绍了很详细的使用方法。
-
-原来iter有两种使用方法。
-
-- 通常我们的认知是第一种,将一个列表转化为一个迭代器。
-
-- 而第二种方法,他接收一个 callable对象,和一个sentinel 参数。第一个对象会一直运行,直到它返回 sentinel 值才结束。
-
-那`int` 呢?
-
-这又是一个知识点,int 是一个内建方法。通过看注释,可以看出它是有默认值0的。你可以在console 模式下输入 `int()` 看看是不是返回0。
-
-由于int() 永远返回0,永远返回不了1,所以这个 for 循环会没有终点。一直运行下去。
-
-> 作者:王炳明,微信公众号《Python编程时光》。版权归个人所有,欢迎分享,仅用于学习交流,但勿用作商业用途,违者必究。
-
-## 06. 懒人必备技能:使用 “_”
-
-对于 `_` ,大家对于他的印象都是用于 **占位符**,省得为一个不需要用到的变量,绞尽脑汁的想变量名。
-
-今天要介绍的是他的第二种用法,就是在 console 模式下的应用。
-
-示例如下:
-
-```python
->>> 3 + 4
-7
->>> _
-7
->>> name='公众号: Python编程时光'
->>> name
-'公众号: Python编程时光'
->>> _
-'公众号: Python编程时光'
-```
-
-它可以返回上一次的运行结果。
-
-但是,如果是print函数打印出来的就不行了。
-
-```python
->>> 3 + 4
-7
->>> _
-7
->>> print("公众号: Python编程时光")
-ming
->>> _
-7
-```
-
-我自己写了个例子,验证了下,用`__repr__`输出的内容可以被获取到的。
-首先,在我们的目录下,写一个文件 demo.py。内容如下
-
-```python
-# demo.py
-class mytest():
- def __str__(self):
- return "hello"
-
- def __repr__(self):
- return "world"
-```
-
-然后在这个目录下进入交互式环境。
-
-```python
->>> import demo
->>> mt=demo.mytest()
->>> mt
-world
->>> print(mt)
-hello
->>> _
-world
-```
-
-知道这两个魔法方法的人,一看就明白了,这里不再解释啦。
-
-## 07. 最快查看包搜索路径的方式
-
-当你使用 import 导入一个包或模块时,Python 会去一些目录下查找,而这些目录是有优先级顺序的,正常人会使用 sys.path 查看。
-
-```python
->>> import sys
->>> from pprint import pprint
->>> pprint(sys.path)
-['',
- '/usr/local/Python3.7/lib/python37.zip',
- '/usr/local/Python3.7/lib/python3.7',
- '/usr/local/Python3.7/lib/python3.7/lib-dynload',
- '/home/wangbm/.local/lib/python3.7/site-packages',
- '/usr/local/Python3.7/lib/python3.7/site-packages']
->>>
-```
-
-那有没有更快的方式呢?
-
-我这有一种连 console 模式都不用进入的方法,一行命令即可解决
-
-```shell
-[wangbm@localhost ~]$ python3 -m site
-sys.path = [
- '/home/wangbm',
- '/usr/local/Python3.7/lib/python37.zip',
- '/usr/local/Python3.7/lib/python3.7',
- '/usr/local/Python3.7/lib/python3.7/lib-dynload',
- '/home/wangbm/.local/lib/python3.7/site-packages',
- '/usr/local/Python3.7/lib/python3.7/site-packages',
-]
-USER_BASE: '/home/wangbm/.local' (exists)
-USER_SITE: '/home/wangbm/.local/lib/python3.7/site-packages' (exists)
-ENABLE_USER_SITE: True
-```
-
-从输出你可以发现,这个列的路径会比 sys.path 更全,它包含了用户环境的目录。
-
-## 08. and 和 or 的取值顺序
-
-and 和 or 是我们再熟悉不过的两个逻辑运算符,在 Python 也有它有妙用。
-
-- 当一个 **or 表达式**中所有值都为真,Python会选择第一个值
-
-- 当一个 **and 表达式** 所有值都为真,Python 会选择第二个值。
-
-示例如下:
-
-```python
->>>(2 or 3) * (5 and 7)
-14 # 2*7
-```
-
-## 09. 如何修改解释器提示符
-
-这个当做今天的一个小彩蛋吧。应该算是比较冷门的,估计知道的人很少了吧。
-
-正常情况下,我们在 终端下 执行Python 命令是这样的。
-```python
->>> for i in range(2):
-... print (i)
-...
-0
-1
-```
-
-你是否想过 `>>>` 和 `...` 这两个提示符也是可以修改的呢?
-```python
->>> import sys
->>> sys.ps1
-'>>> '
->>> sys.ps2
-'... '
->>>
->>> sys.ps2 = '---------------- '
->>> sys.ps1 = 'Python编程时光>>>'
-Python编程时光>>>for i in range(2):
----------------- print (i)
-----------------
-0
-1
-```
-
-## 10. 逗号也有它独特的用法
-
-逗号,虽然是个很不起眼的符号,但在 Python 中也有他的用武之地。
-
-**第一个用法**
-
-元组的转化
-
-```shell
-[root@localhost ~]# cat demo.py
-def func():
- return "ok",
-
-print(func())
-[root@localhost ~]# python3 demo.py
-('ok',)
-```
-
-**第二个用法**
-
-print 的取消换行
-
-```shell
-[root@localhost ~]# cat demo.py
-for i in range(3):
- print i
-[root@localhost ~]#
-[root@localhost ~]# python demo.py
-0
-1
-2
-[root@localhost ~]#
-[root@localhost ~]# vim demo.py
-[root@localhost ~]#
-[root@localhost ~]# cat demo.py
-for i in range(3):
- print i,
-[root@localhost ~]#
-[root@localhost ~]# python demo.py
-0 1 2
-[root@localhost ~]#
-```
-
-
-
-> 作者:王炳明,微信公众号《Python编程时光》。版权归个人所有,欢迎分享,仅用于学习交流,但勿用作商业用途,违者必究。
-
-## 11. 默认参数最好不为可变对象
-
-函数的参数分三种
-- 可变参数
-- 默认参数
-- 关键字参数
-
-当你在传递默认参数时,有新手很容易踩雷的一个坑。
-
-先来看一个示例
-```python
-def func(item, item_list=[]):
- item_list.append(item)
- print(item_list)
-
-func('iphone')
-func('xiaomi', item_list=['oppo','vivo'])
-func('huawei')
-```
-在这里,你可以暂停一下,思考一下会输出什么?
-
-思考过后,你的答案是否和下面的一致呢
-```
-['iphone']
-['oppo', 'vivo', 'xiaomi']
-['iphone', 'huawei']
-```
-
-如果是,那你可以跳过这部分内容,如果不是,请接着往下看,这里来分析一下。
-
-Python 中的 def 语句在每次执行的时候都初始化一个函数对象,这个函数对象就是我们要调用的函数,可以把它当成一个一般的对象,只不过这个对象拥有一个可执行的方法和部分属性。
-
-对于参数中提供了初始值的参数,由于 Python 中的函数参数传递的是对象,也可以认为是传地址,在第一次初始化 def 的时候,会先生成这个可变对象的内存地址,然后将这个默认参数 item_list 会与这个内存地址绑定。在后面的函数调用中,如果调用方指定了新的默认值,就会将原来的默认值覆盖。如果调用方没有指定新的默认值,那就会使用原来的默认值。
-
-
-
-## 12. 访问类中的私有方法
-
-大家都知道,类中可供直接调用的方法,只有公有方法(protected类型的方法也可以,但是不建议)。也就是说,类的私有方法是无法直接调用的。
-
-这里先看一下例子
-```python
-class Kls():
- def public(self):
- print('Hello public world!')
-
- def __private(self):
- print('Hello private world!')
-
- def call_private(self):
- self.__private()
-
-ins = Kls()
-
-# 调用公有方法,没问题
-ins.public()
-
-# 直接调用私有方法,不行
-ins.__private()
-
-# 但你可以通过内部公有方法,进行代理
-ins.call_private()
-```
-
-既然都是方法,那我们真的没有方法可以直接调用吗?
-
-当然有啦,只是建议你千万不要这样弄,这里只是普及,让你了解一下。
-```python
-# 调用私有方法,以下两种等价
-ins._Kls__private()
-ins.call_private()
-```
-
-## 13. 时有时无的切片异常
-
-这是个简单例子,alist 只有5 个元素,当你取第 6 个元素时,会抛出索引异常。这与我们的认知一致。
-```python
->>> alist = [0, 1, 2, 3, 4]
->>> alist[5]
-Traceback (most recent call last):
- File "", line 1, in
-IndexError: list index out of range
-```
-但是当你使用 alist[5:] 取一个区间时,即使 alist 并没有 第 6个元素,也不抛出异常,而是会返回一个新的列表。
-```python
->>> alist = [0, 1, 2, 3, 4]
->>> alist[5:]
-[]
->>> alist[100:]
-[]
-```
-
-## 14. 哪些情况下不需要续行符?
-
-在写代码时,为了代码的可读性,代码的排版是尤为重要的。
-
-为了实现高可读性的代码,我们常常使用到的就是续行符 `\`。
-```
->>> a = 'talk is cheap,'\
-... 'show me the code.'
->>>
->>> print(a)
-talk is cheap,show me the code.
-```
-
-那有哪些情况下,是不需要写续行符的呢?
-
-经过总结,在这些符号中间的代码换行可以省略掉续行符:`[]`,`()`,`{}`
-
-```
->>> my_list=[1,2,3,
-... 4,5,6]
-
->>> my_tuple=(1,2,3,
-... 4,5,6)
-
->>> my_dict={"name": "MING",
-... "gender": "male"}
-```
-另外还有,在多行文本注释中 `'''` ,续行符也是可以不写的。
-```
->>> text = '''talk is cheap,
-... show me the code'''
-```
-
-## 15. Python2下 也能使用 print(“”)
-
-可能会有不少人,觉得只有 Python 3 才可以使用 print(),而 Python 2 只能使用`print ""`。
-
-但是其实并不是这样的。
-
-在Python 2.6之前,只支持
-```python
-print "hello"
-```
-
-在Python 2.6和2.7中,可以支持如下三种
-```python
-print "hello"
-print("hello")
-print ("hello")
-```
-
-在Python3.x中,可以支持如下两种
-```python
-print("hello")
-print ("hello")
-```
-
-虽然 在 Python 2.6+ 可以和 Python3.x+ 一样,像函数一样去调用 print ,但是这仅用于两个 python 版本之间的代码兼容,并不是说在 python2.6+下使用 print() 后,就成了函数。
-
-> 作者:王炳明,微信公众号《Python编程时光》。版权归个人所有,欢迎分享,仅用于学习交流,但勿用作商业用途,违者必究。
-
-## 16. 迷一样的字符串
-
-示例一
-
-```python
-# Python2.7
->>> a = "Hello_Python"
->>> id(a)
-32045616
->>> id("Hello" + "_" + "Python")
-32045616
-
-# Python3.7
->>> a = "Hello_Python"
->>> id(a)
-38764272
->>> id("Hello" + "_" + "Python")
-32045616
-```
-
-示例二
-
-```python
->>> a = "MING"
->>> b = "MING"
->>> a is b
-True
-
-# Python2.7
->>> a, b = "MING!", "MING!"
->>> a is b
-True
-
-# Python3.7
->>> a, b = "MING!", "MING!"
->>> a is b
-False
-```
-
-示例三
-
-```python
-# Python2.7
->>> 'a' * 20 is 'aaaaaaaaaaaaaaaaaaaa'
-True
->>> 'a' * 21 is 'aaaaaaaaaaaaaaaaaaaaa'
-False
-
-# Python3.7
->>> 'a' * 20 is 'aaaaaaaaaaaaaaaaaaaa'
-True
->>> 'a' * 21 is 'aaaaaaaaaaaaaaaaaaaaa'
-True
-```
-
-
-
-## 17. return不一定都是函数的终点
-
-众所周知,try…finally… 的用法是:不管try里面是正常执行还是有报异常,最终都能保证finally能够执行。
-
-同时我们又知道,一个函数里只要遇到 return 函数就会立马结束。
-
-那问题就来了,以上这两种规则,如果同时存在,Python 解释器会如何选择?哪个优先级更高?
-
-写个示例验证一下,就明白啦
-
-```python
->>> def func():
-... try:
-... return 'try'
-... finally:
-... return 'finally'
-...
->>> func()
-'finally'
-```
-
-从输出中,我们可以发现:在try…finally…语句中,try中的 return 会被直接忽视(这里的 return 不是函数的终点),因为要保证 finally 能够执行。
-
-**如果 try 里的 return 真的是直接被忽视吗?**
-
-我们都知道如果一个函数没有 return,会隐式的返回 None,假设 try 里的 return 真的是直接被忽视,那当finally 下没有显式的 return 的时候,是不是会返回None呢?
-
-还是写个 示例来验证一下:
-
-```python
->>> def func():
-... try:
-... return 'try'
-... finally:
-... print('finally')
-...
->>>
->>> func()
-finally
-'try'
->>>
-```
-
-从结果来看,当 finally 下没有 reutrn ,其实 try 里的 return 仍然还是有效的。
-
-那结论就出来了,如果 finally 里有显式的 return,那么这个 return 会直接覆盖 try 里的 return,而如果 finally 里没有 显式的 return,那么 try 里的 return 仍然有效。
-
-## 18. 用户无感知的小整数池
-
-为避免整数频繁申请和销毁内存空间,Python 定义了一个小整数池 [-5, 256] 这些整数对象是提前建立好的,不会被垃圾回收。
-
-以上代码请在 终端Python环境下测试,如果你是在IDE中测试,由于 IDE 的影响,效果会有所不同。
-
-```python
->>> a = -6
->>> b = -6
->>> a is b
-False
-
->>> a = 256
->>> b = 256
->>> a is b
-True
-
->>> a = 257
->>> b = 257
->>> a is b
-False
-
->>> a = 257; b = 257
->>> a is b
-True
-```
-
-**问题又来了:最后一个示例,为啥是True?**
-
-因为当你在同一行里,同时给两个变量赋同一值时,解释器知道这个对象已经生成,那么它就会引用到同一个对象。如果分成两成的话,解释器并不知道这个对象已经存在了,就会重新申请内存存放这个对象。
-
-## 19. 神奇的 intern 机制
-
-字符串类型作为Python中最常用的数据类型之一,Python解释器为了提高字符串使用的效率和使用性能,做了很多优化.
-
-例如:Python解释器中使用了 intern(字符串驻留)的技术来提高字符串效率,什么是intern机制?就是同样的字符串对象仅仅会保存一份,放在一个字符串储蓄池中,是共用的,当然,肯定不能改变,这也决定了字符串必须是不可变对象。
-
-```
->>> s1="hello"
->>> s2="hello"
->>> s1 is s2
-True
-
-# 如果有空格,默认不启用intern机制
->>> s1="hell o"
->>> s2="hell o"
->>> s1 is s2
-False
-
-# 如果一个字符串长度超过20个字符,不启动intern机制
->>> s1 = "a" * 20
->>> s2 = "a" * 20
->>> s1 is s2
-True
-
->>> s1 = "a" * 21
->>> s2 = "a" * 21
->>> s1 is s2
-False
-
->>> s1 = "ab" * 10
->>> s2 = "ab" * 10
->>> s1 is s2
-True
-
->>> s1 = "ab" * 11
->>> s2 = "ab" * 11
->>> s1 is s2
-False
-```
-
-
-
-## 20. 反转字符串/列表最优雅的方式
-
-反转序列并不难,但是如何做到最优雅呢?
-
-先来看看,正常是如何反转的。
-
-最简单的方法是使用列表自带的reverse()方法。
-
-```python
->>> ml = [1,2,3,4,5]
->>> ml.reverse()
->>> ml
-[5, 4, 3, 2, 1]
-```
-
-但如果你要处理的是字符串,reverse就无能为力了。你可以尝试将其转化成list,再reverse,然后再转化成str。转来转去,也太麻烦了吧?需要这么多行代码(后面三行是不能合并成一行的),一点都Pythonic。
-```python
-mstr1 = 'abc'
-ml1 = list(mstr1)
-ml1.reverse()
-mstr2 = str(ml1)
-```
-对于字符串还有一种稍微复杂一点的,是自定义递归函数来实现。
-```python
-def my_reverse(str):
- if str == "":
- return str
- else:
- return my_reverse(str[1:]) + str[0]
-```
-
-在这里,介绍一种最优雅的反转方式,使用切片,不管你是字符串,还是列表,简直通杀。
-```python
->>> mstr = 'abc'
->>> ml = [1,2,3]
->>> mstr[::-1]
-'cba'
->>> ml[::-1]
-[3, 2, 1]
-```
-
-> 作者:王炳明,微信公众号《Python编程时光》。版权归个人所有,欢迎分享,仅用于学习交流,但勿用作商业用途,违者必究。
-
-## 21. 改变默认递归次数限制
-
-上面才提到递归,大家都知道使用递归是有风险的,递归深度过深容易导致堆栈的溢出。如果你这字符串太长啦,使用递归方式反转,就会出现问题。
-
-那到底,默认递归次数限制是多少呢?
-```python
->>> import sys
->>> sys.getrecursionlimit()
-1000
-```
-
-可以查,当然也可以自定义修改次数,退出即失效。
-```python
->>> sys.setrecursionlimit(2000)
->>> sys.getrecursionlimit()
-2000
-```
-
-## 22. 一行代码实现FTP服务器
-
-搭建FTP,或者是搭建网络文件系统,这些方法都能够实现Linux的目录共享。但是FTP和网络文件系统的功能都过于强大,因此它们都有一些不够方便的地方。比如你想快速共享Linux系统的某个目录给整个项目团队,还想在一分钟内做到,怎么办?很简单,使用Python中的SimpleHTTPServer。
-
-SimpleHTTPServer是Python 2自带的一个模块,是Python的Web服务器。它在Python 3已经合并到http.server模块中。具体例子如下,如不指定端口,则默认是8000端口。
-```python
-# python2
-python -m SimpleHTTPServer 8888
-
-# python3
-python3 -m http.server 8888
-```
-
-
-
-SimpleHTTPServer有一个特性,如果待共享的目录下有index.html,那么index.html文件会被视为默认主页;如果不存在index.html文件,那么就会显示整个目录列表。
-
-## 23. 让你晕头转向的 else 用法
-
-if else 用法可以说最基础的语法表达式之一,但是今天不是讲这个的,一定要讲点不一样的。
-
-if else 早已烂大街,但可能有很多人都不曾见过 for else 和 try else 的用法。为什么说它曾让我晕头转向,因为它不像 if else 那么直白,非黑即白,脑子经常要想一下才能才反应过来代码怎么走。反正我是这样的。
-
-先来说说,for else
-```python
-def check_item(source_list, target):
- for item in source_list:
- if item == target:
- print("Exists!")
- break
-
- else:
- print("Does not exist")
-
-```
-在往下看之前,你可以思考一下,什么情况下才会走 else。是循环被 break,还是没有break?
-
-给几个例子,你体会一下。
-```python
-check_item(["apple", "huawei", "oppo"], "oppo")
-# Exists!
-
-check_item(["apple", "huawei", "oppo"], "vivo")
-# Does not exist
-```
-可以看出,没有被 break 的程序才会正常走else流程。
-
-再来看看,try else 用法。
-```python
-def test_try_else(attr1 = None):
- try:
- if attr1:
- pass
- else:
- raise
- except:
- print("Exception occurred...")
- else:
- print("No Exception occurred...")
-```
-
-同样来几个例子。当不传参数时,就抛出异常。
-```python
-test_try_else()
-# Exception occurred...
-
-test_try_else("ming")
-# No Exception occurred...
-```
-
-可以看出,没有 try 里面的代码块没有抛出异常的,会正常走else。
-
-总结一下,for else 和 try else 相同,只要代码正常走下去不被 break,不抛出异常,就可以走else。
-
-
-## 24. 字符串里的缝隙是什么?
-
-在Python中求一个字符串里,某子字符(串)出现的次数。
-
-大家都懂得使用 count() 函数,比如下面几个常规例子:
-
-```python
->>> "aabb".count("a")
-2
->>> "aabb".count("b")
-2
->>> "aabb".count("ab")
-1
-```
-
-但是如果我想计算空字符串的个数呢?
-```python
->>> "aabb".count("")
-5
-```
-
-**奇怪了吧?**
-
-不是应该返回 0 吗?怎么会返回 5?
-
-实际上,在 Python 看来,两个字符之间都是一个空字符,通俗的说就是缝隙。
-
-因此 对于 `aabb` 这个字符串在 Python 来看应该是这样的
-
-
-
-理解了这个“**缝隙**” 的概念后,以下这些就好理解了。
-
-```python
->>> (" " * 10).count("")
-11
->>>
->>> "" in ""
-True
->>>
->>> "" in "M"
-True
-```
-
-
-
-## 25. 正负得正,负负得正
-
-从初中开始,我们就开始接触了`负数` ,并且都知道了`负负得正` 的思想。
-
-Python 作为一门高级语言,它的编写符合人类的思维逻辑,包括 `负负得正` 。
-
-```python
->>> 5-3
-2
->>> 5--3
-8
->>> 5+-3
-2
->>> 5++3
-8
->>> 5---3
-2
-```
-
-> 作者:王炳明,微信公众号《Python编程时光》。版权归个人所有,欢迎分享,仅用于学习交流,但勿用作商业用途,违者必究。
-
-## 26. 数值与字符串的比较
-
-在 Python2 中,数字可以与字符串直接比较。结果是数值永远比字符串小。
-```Python
->>> 100000000 < ""
-True
->>> 100000000 < "hello"
-True
-```
-
-但在 Python3 中,却不行。
-```python
->>> 100000000 < ""
-TypeError: '<' not supported between instances of 'int' and 'str'
-```
-
-## 27. 循环中的局部变量泄露
-
-在Python 2中 x 的值在一个循环执行之后被改变了。
-```python
-# Python2
->>> x = 1
->>> [x for x in range(5)]
-[0, 1, 2, 3, 4]
->>> x
-4
-```
-不过在Python3 中这个问题已经得到解决了。
-```python
-# Python3
->>> x = 1
->>> [x for x in range(5)]
-[0, 1, 2, 3, 4]
->>> x
-1
-```
-
-## 28. 字典居然是可以排序的?
-
-在 Python 3.6 之前字典不可排序的思想,似乎已经根深蒂固。
-
-```python
-# Python2.7.10
->>> mydict = {str(i):i for i in range(5)}
->>> mydict
-{'1': 1, '0': 0, '3': 3, '2': 2, '4': 4}
-```
-
-假如哪一天,有人跟你说字典也可以是有序的,不要惊讶,那确实是真的
-
-在 Python3.6 + 中字典已经是有序的,并且效率相较之前的还有所提升,具体信息你可以去查询相关资料。
-
-```python
-# Python3.6.7
->>> mydict = {str(i):i for i in range(5)}
->>> mydict
-{'0': 0, '1': 1, '2': 2, '3': 3, '4': 4}
-```
-
-
-
-## 29. 有趣但没啥用的 import 用法
-
-import 是 Python 导包的方式。
-
-你知道 Python 中内置了一些很有(wu)趣(liao)的包吗?
-
-**Hello World**
-
-```
->>> import __hello__
-Hello World!
-```
-
-**Python之禅**
-
-```
->>> import this
-
-The Zen of Python, by Tim Peters
-
-Beautiful is better than ugly.
-Explicit is better than implicit.
-Simple is better than complex.
-Complex is better than complicated.
-Flat is better than nested.
-Sparse is better than dense.
-Readability counts.
-Special cases aren't special enough to break the rules.
-Although practicality beats purity.
-Errors should never pass silently.
-Unless explicitly silenced.
-In the face of ambiguity, refuse the temptation to guess.
-There should be one-- and preferably only one --obvious way to do it.
-Although that way may not be obvious at first unless you're Dutch.
-Now is better than never.
-Although never is often better than *right* now.
-If the implementation is hard to explain, it's a bad idea.
-If the implementation is easy to explain, it may be a good idea.
-Namespaces are one honking great idea -- let's do more of those!
-```
-
-**反地心引力漫画**
-
-在 cmd 窗口中导入`antigravity`
-
-```
->>> import antigravity
-```
-
-就会自动打开一个网页。
-
-
-## 30. 局部/全局变量傻傻分不清
-
-在开始讲之前,你可以试着运行一下下面这小段代码。
-
-```python
-# demo.py
-a = 1
-
-def add():
- a += 1
-
-add()
-```
-
-看似没有毛病,但实则已经犯了一个很基础的问题,运行结果如下:
-
-```python
-$ python demo.py
-Traceback (most recent call last):
- File "demo.py", line 6, in
- add()
- File "demo.py", line 4, in add
- a += 1
-UnboundLocalError: local variable 'a' referenced before assignment
-```
-
-回顾一下,什么是局部变量?在非全局下定义声明的变量都是局部变量。
-
-当程序运行到 `a += 1` 时,Python 解释器就认为在函数内部要给 `a` 这个变量赋值,当然就把 `a` 当做局部变量了,但是做为局部变量的 a 还没有被还没被定义。
-
-因此报错是正常的。
-
-理解了上面的例子,给你留个思考题。为什么下面的代码不会报错呢?
-
-```python
-$ cat demo.py
-a = 1
-
-def output():
- print(a)
-
-output()
-
-$ python demo.py
-1
-```
-
-
-
-> 作者:王炳明,微信公众号《Python编程时光》。版权归个人所有,欢迎分享,仅用于学习交流,但勿用作商业用途,违者必究。
-
-## 31. 字母也玩起了障眼法
-
-以下我分别在 Python2.7 和 Python 3.7 的 console 模式下,运行了如下代码。
-
-**在Python 2.x 中**
-
-```
->>> valuе = 32
- File "", line 1
- valuе = 32
- ^
-SyntaxError: invalid syntax
-```
-
-**在Python 3.x 中**
-
-```
->>> valuе = 32
->>> value
-11
-```
-
-什么?没有截图你不信?
-
-
-
-
-
-如果你在自己的电脑上尝试一下,结果可能是这样的
-
-
-
-
-
-**怎么又好了呢?**
-
-如果你想复现的话,请复制我这边给出的代码:`valuе = 32`
-
-
-
-**这是为什么呢?**
-
-原因在于,我上面使用的 value 变量名里的 `е` 又不是我们熟悉的 `e`,它是 Cyrillic(西里尔)字母。
-
-```
->>> ord('е') # cyrillic 'e' (Ye)
-1077
->>> ord('e') # latin 'e', as used in English and typed using standard keyboard
-101
->>> 'е' == 'e'
-False
-```
-
-细思恐极,在这里可千万不要得罪同事们,万一离职的时候,对方把你项目里的 `e` 全局替换成 `e`,到时候你就哭去吧,肉眼根本看不出来嘛。
-
-## 32. 字符串的分割技巧
-
-当我们对字符串进行分割时,且分割符是 `\n`,有可能会出现这样一个窘境:
-
-```python
->>> str = "a\nb\n"
->>> print(str)
-a
-b
-
->>> str.split('\n')
-['a', 'b', '']
->>>
-```
-
-会在最后一行多出一个元素,为了应对这种情况,你可以会多加一步处理。
-
-但我想说的是,完成没有必要,对于这个场景,你可以使用 `splitlines`
-
-```python
->>> str.splitlines()
-['a', 'b']
-```
-
-
-
-## 33. 嵌套上下文管理的另类写法
-
-当我们要写一个嵌套的上下文管理器时,可能会这样写
-
-```python
-import contextlib
-
-@contextlib.contextmanager
-def test_context(name):
- print('enter, my name is {}'.format(name))
-
- yield
-
- print('exit, my name is {}'.format(name))
-
-with test_context('aaa'):
- with test_context('bbb'):
- print('========== in main ============')
-```
-
-输出结果如下
-
-```python
-enter, my name is aaa
-enter, my name is bbb
-========== in main ============
-exit, my name is bbb
-exit, my name is aaa
-```
-
-除此之外,你可知道,还有另一种嵌套写法
-
-```python
-with test_context('aaa'), test_context('bbb'):
- print('========== in main ============')
-```
-
-## 34. += 不等同于=+
-
-对列表 进行`+=` 操作相当于 extend,而使用 `=+` 操作是新增了一个列表。
-
-因此会有如下两者的差异。
-
-```python
-# =+
->>> a = [1, 2, 3, 4]
->>> b = a
->>> a = a + [5, 6, 7, 8]
->>> a
-[1, 2, 3, 4, 5, 6, 7, 8]
->>> b
-[1, 2, 3, 4]
-
-
-# +=
->>> a = [1, 2, 3, 4]
->>> b = a
->>> a += [5, 6, 7, 8]
->>> a
-[1, 2, 3, 4, 5, 6, 7, 8]
->>> b
-[1, 2, 3, 4, 5, 6, 7, 8]
-```
-
-## 35. 增量赋值的性能更好
-
-诸如 `+=` 和 `*=` 这些运算符,叫做 增量赋值运算符。
-
-这里使用用 += 举例,以下两种写法,在效果上是等价的。
-
-```
-# 第一种
-a = 1 ; a += 1
-
-# 第二种
-a = 1; a = a + 1
-```
-
-`+=` 其背后使用的魔法方法是 \__iadd__,如果没有实现这个方法则会退而求其次,使用 \__add__ 。
-
-这两种写法有什么区别呢?
-
-用列表举例 a += b,使用 \__add__ 的话就像是使用了a.extend(b),如果使用 \__add__ 的话,则是 a = a+b,前者是直接在原列表上进行扩展,而后者是先从原列表中取出值,在一个新的列表中进行扩展,然后再将新的列表对象返回给变量,显然后者的消耗要大些。
-
-所以在能使用增量赋值的时候尽量使用它。
-
-> 作者:王炳明,微信公众号《Python编程时光》。版权归个人所有,欢迎分享,仅用于学习交流,但勿用作商业用途,违者必究。
-
-## 36. x == +x 吗?
-
-在大多数情况下,这个等式是成立的。
-
-```
->>> n1 = 10086
->>> n2 = +n1
->>>
->>> n1 == n2
-True
-```
-
-什么情况下,这个等式会不成立呢?
-
-由于Counter的机制,`+` 用于两个 Counter 实例相加,而相加的结果如果元素的个数 `<=` 0,就会被丢弃。
-
-```
->>> from collections import Counter
->>> ct = Counter('abcdbcaa')
->>> ct
-Counter({'a': 3, 'b': 2, 'c': 2, 'd': 1})
->>> ct['c'] = 0
->>> ct['d'] = -2
->>>
->>> ct
-Counter({'a': 3, 'b': 2, 'c': 0, 'd': -2})
->>>
->>> +ct
-Counter({'a': 3, 'b': 2})
-```
-
-
-
-## 37. 如何将 print 内容输出到文件
-
-Python 3 中的 print 作为一个函数,由于可以接收更多的参数,所以功能变为更加强大。
-
-比如今天要说的使用 print 将你要打印的内容,输出到日志文件中(但是我并不推荐使用它)。
-
-```python
->>> with open('test.log', mode='w') as f:
-... print('hello, python', file=f, flush=True)
->>> exit()
-
-$ cat test.log
-hello, python
-```
-
-
-
-## 38. site-packages和 dist-packages
-
-如果你足够细心,你会在你的机器上,有些包是安装在 **site-packages** 下,而有些包安装在 **dist-packages** 下。
-
-**它们有什么区别呢?**
-
-一般情况下,你只见过 site-packages 这个目录,而你所安装的包也将安装在 这个目录下。
-
-而 dist-packages 其实是 debian 系的 Linux 系统(如 Ubuntu)才特有的目录,当你使用 apt 去安装的 Python 包会使用 dist-packages,而你使用 pip 或者 easy_install 安装的包还是照常安装在 site-packages 下。
-
-Debian 这么设计的原因,是为了减少不同来源的 Python 之间产生的冲突。
-
-如何查找 Python 安装目录
-
-```python
->>> from distutils.sysconfig import get_python_lib
->>> print(get_python_lib())
-/usr/lib/python2.7/site-packages
-```
-
-## 39. argument 和 parameter 的区别
-
-arguments 和 parameter 的翻译都是参数,在中文场景下,二者混用基本没有问题,毕竟都叫参数嘛。
-
-但若要严格再进行区分,它们实际上还有各自的叫法
-
-- parameter:形参(**formal parameter**),体现在函数内部,作用域是这个函数体。
-- argument :实参(**actual parameter**),调用函数实际传递的参数。
-
-举个例子,如下这段代码,`"error"` 为 argument,而 msg 为 `parameter`。
-
-```python
-def output_msg(msg):
- print(msg)
-
-output_msg("error")
-```
-
-## 40. 简洁而优雅的链式比较
-
-先给你看一个示例:
-
-```python
->>> False == False == True
-False
-```
-
-你知道这个表达式为什么会会返回 False 吗?
-
-它的运行原理与下面这个类似,是不是有点头绪了:
-
-```python
-if 80 < score <= 90:
- print("成绩良好")
-```
-
-如果你还是不明白,那我再给你整个第一个例子的等价写法。
-
-```python
->>> False == False and False == True
-False
-```
-
-这个用法叫做链式比较。
-
-
-
-> 作者:王炳明,微信公众号《Python编程时光》。版权归个人所有,欢迎分享,仅用于学习交流,但勿用作商业用途,违者必究。
-
-## 41. 连接多个列表最极客的方式
-
-```python
->>> a = [1,2]
->>> b = [3,4]
->>> c = [5,6]
->>>
->>> sum((a,b,c), [])
-[1, 2, 3, 4, 5, 6]
-```
-
-## 42. 另外 8 种连接列表的方式
-
-**1. 最直观的相加**
-
-使用 `+` 对多个列表进行相加,你应该懂,不多说了。
-
-```python
->>> list01 = [1,2,3]
->>> list02 = [4,5,6]
->>> list03 = [7,8,9]
->>>
->>> list01 + list02 + list03
-[1, 2, 3, 4, 5, 6, 7, 8, 9]
->>>
-```
-
-
-
-**2. 借助 itertools**
-
-itertools 在 Python 里有一个非常强大的内置模块,它专门用于操作可迭代对象。
-
-在前面的文章中也介绍过,使用 `itertools.chain()` 函数先可迭代对象(在这里指的是列表)串联起来,组成一个更大的可迭代对象。
-
-最后你再利用 list 将其转化为 列表。
-
-```python
->>> from itertools import chain
->>> list01 = [1,2,3]
->>> list02 = [4,5,6]
->>> list03 = [7,8,9]
->>>
->>> list(chain(list01, list02, list03))
-[1, 2, 3, 4, 5, 6, 7, 8, 9]
->>>
-```
-
-**3. 使用 * 解包**
-
-使用 `*` 可以解包列表,解包后再合并。
-
-示例如下:
-
-```python
->>> list01 = [1,2,3]
->>> list02 = [4,5,6]
->>>
->>> [*list01, *list02]
-[1, 2, 3, 4, 5, 6]
->>>
-```
-
-
-
-**4. 使用 extend**
-
-在字典中,使用 update 可实现原地更新,而在列表中,使用 extend 可实现列表的自我扩展。
-
-```python
->>> list01 = [1,2,3]
->>> list02 = [4,5,6]
->>>
->>> list01.extend(list02)
->>> list01
-[1, 2, 3, 4, 5, 6]
-```
-
-**5. 使用列表推导式**
-
-Python 里对于生成列表、集合、字典,有一套非常 Pythonnic 的写法。
-
-那就是列表解析式,集合解析式和字典解析式,通常是 Python 发烧友的最爱,那么今天的主题:列表合并,列表推导式还能否胜任呢?
-
-当然可以,具体示例代码如下:
-
-```python
->>> list01 = [1,2,3]
->>> list02 = [4,5,6]
->>> list03 = [7,8,9]
->>>
->>> [x for l in (list01, list02, list03) for x in l]
-[1, 2, 3, 4, 5, 6, 7, 8, 9]
->>>
-```
-
-
-
-**6. 使用 heapq**
-
-heapq 是 Python 的一个标准模块,它提供了堆排序算法的实现。
-
-该模块里有一个 merge 方法,可以用于合并多个列表,如下所示
-
-```python
->>> list01 = [1,2,3]
->>> list02 = [4,5,6]
->>> list03 = [7,8,9]
->>>
->>> from heapq import merge
->>>
->>> list(merge(list01, list02, list03))
-[1, 2, 3, 4, 5, 6, 7, 8, 9]
->>>
-```
-
-要注意的是,heapq.merge 除了合并多个列表外,它还会将合并后的最终的列表进行排序。
-
-```python
->>> list01 = [2,5,3]
->>> list02 = [1,4,6]
->>> list03 = [7,9,8]
->>>
->>> from heapq import merge
->>>
->>> list(merge(list01, list02, list03))
-[1, 2, 4, 5, 3, 6, 7, 9, 8]
->>>
-```
-
-它的效果等价于下面这行代码:
-
-```python
-sorted(itertools.chain(*iterables))
-```
-
-如果你希望得到一个始终有序的列表,那请第一时间想到 heapq.merge,因为它采用堆排序,效率非常高。但若你不希望得到一个排过序的列表,就不要使用它了。
-
-**7. 借助魔法方法**
-
-有一个魔法方法叫 `__add__`,当我们使用第一种方法 list01 + list02 的时候,内部实际上是作用在 `__add__` 这个魔法方法上的。
-
-所以以下两种方法其实是等价的
-
-```python
->>> list01 = [1,2,3]
->>> list02 = [4,5,6]
->>>
->>> list01 + list02
-[1, 2, 3, 4, 5, 6]
->>>
->>>
->>> list01.__add__(list02)
-[1, 2, 3, 4, 5, 6]
->>>
-```
-
-借用这个魔法特性,我们可以 reduce 这个方法来对多个列表进行合并,示例代码如下
-
-```python
->>> list01 = [1,2,3]
->>> list02 = [4,5,6]
->>> list03 = [7,8,9]
->>>
->>> from functools import reduce
->>> reduce(list.__add__, (list01, list02, list03))
-[1, 2, 3, 4, 5, 6, 7, 8, 9]
->>>
-```
-
-
-
-**8. 使用 yield from**
-
-在 yield from 后可接一个可迭代对象,用于迭代并返回其中的每一个元素。
-
-因此,我们可以像下面这样自定义一个合并列表的工具函数。
-
-```python
->>> list01 = [1,2,3]
->>> list02 = [4,5,6]
->>> list03 = [7,8,9]
->>>
->>> def merge(*lists):
-... for l in lists:
-... yield from l
-...
->>> list(merge(list01, list02, list03))
-[1, 2, 3, 4, 5, 6, 7, 8, 9]
->>>
-```
-
-
-
-## 43. 在程序退出前执行代码的技巧
-
-使用 atexit 这个内置模块,可以很方便的注册退出函数。
-
-不管你在哪个地方导致程序崩溃,都会执行那些你注册过的函数。
-
-示例如下
-
-
-
-如果`clean()`函数有参数,那么你可以不用装饰器,而是直接调用`atexit.register(clean_1, 参数1, 参数2, 参数3='xxx')`。
-
-可能你有其他方法可以处理这种需求,但肯定比上不使用 atexit 来得优雅,来得方便,并且它很容易扩展。
-
-但是使用 atexit 仍然有一些局限性,比如:
-
-- 如果程序是被你没有处理过的系统信号杀死的,那么注册的函数无法正常执行。
-- 如果发生了严重的 Python 内部错误,你注册的函数无法正常执行。
-- 如果你手动调用了`os._exit()`,你注册的函数无法正常执行。
-
-## 44. 合并字典的 8 种方法
-
-**1. 最简单的原地更新**
-
-字典对象内置了一个 update 方法,用于把另一个字典更新到自己身上。
-
-```python
->>> profile = {"name": "xiaoming", "age": 27}
->>> ext_info = {"gender": "male"}
->>>
->>> profile.update(ext_info)
->>> print(profile)
-{'name': 'xiaoming', 'age': 27, 'gender': 'male'}
-```
-
-如果想使用 update 这种最简单、最地道原生的方法,但又不想更新到自己身上,而是生成一个新的对象,那请使用深拷贝。
-
-```python
->>> profile = {"name": "xiaoming", "age": 27}
->>> ext_info = {"gender": "male"}
->>>
->>> from copy import deepcopy
->>>
->>> full_profile = deepcopy(profile)
->>> full_profile.update(ext_info)
->>>
->>> print(full_profile)
-{'name': 'xiaoming', 'age': 27, 'gender': 'male'}
->>> print(profile)
-{"name": "xiaoming", "age": 27}
-```
-
-
-
-**2. 先解包再合并字典**
-
-使用 `**` 可以解包字典,解包完后再使用 dict 或者 `{}` 就可以合并。
-
-```python
->>> profile = {"name": "xiaoming", "age": 27}
->>> ext_info = {"gender": "male"}
->>>
->>> full_profile01 = {**profile, **ext_info}
->>> print(full_profile01)
-{'name': 'xiaoming', 'age': 27, 'gender': 'male'}
->>>
->>> full_profile02 = dict(**profile, **ext_info)
->>> print(full_profile02)
-{'name': 'xiaoming', 'age': 27, 'gender': 'male'}
-```
-
-若你不知道 `dict(**profile, **ext_info)` 做了啥,你可以将它等价于
-
-```python
->>> dict((("name", "xiaoming"), ("age", 27), ("gender", "male")))
-{'name': 'xiaoming', 'age': 27, 'gender': 'male'}
-```
-
-
-
-**3. 借助 itertools**
-
-在 Python 里有一个非常强大的内置模块,它专门用于操作可迭代对象。
-
-正好我们字典也是可迭代对象,自然就可以想到,可以使用 `itertools.chain()` 函数先将多个字典(可迭代对象)串联起来,组成一个更大的可迭代对象,然后再使用 dict 转成字典。
-
-```python
->>> import itertools
->>>
->>> profile = {"name": "xiaoming", "age": 27}
->>> ext_info = {"gender": "male"}
->>>
->>>
->>> dict(itertools.chain(profile.items(), ext_info.items()))
-{'name': 'xiaoming', 'age': 27, 'gender': 'male'}
-```
-
-
-
-**4. 借助 ChainMap**
-
-如果可以引入一个辅助包,那我就再提一个, `ChainMap` 也可以达到和 `itertools` 同样的效果。
-
-```python
->>> from collections import ChainMap
->>>
->>> profile = {"name": "xiaoming", "age": 27}
->>> ext_info = {"gender": "male"}
->>>
->>> dict(ChainMap(profile, ext_info))
-{'name': 'xiaoming', 'age': 27, 'gender': 'male'}
-```
-
-使用 ChainMap 有一点需要注意,当字典间有重复的键时,只会取第一个值,排在后面的键值并不会更新掉前面的(使用 itertools 就不会有这个问题)。
-
-```python
->>> from collections import ChainMap
->>>
->>> profile = {"name": "xiaoming", "age": 27}
->>> ext_info={"age": 30}
->>> dict(ChainMap(profile, ext_info))
-{'name': 'xiaoming', 'age': 27}
-```
-
-
-
-**5. 使用dict.items() 合并**
-
-在 Python 3.9 之前,其实就已经有 `|` 操作符了,只不过它通常用于对集合(set)取并集。
-
-利用这一点,也可以将它用于字典的合并,只不过得绕个弯子,有点不好理解。
-
-你得先利用 `items` 方法将 dict 转成 dict_items,再对这两个 dict_items 取并集,最后利用 dict 函数,转成字典。
-
-```python
->>> profile = {"name": "xiaoming", "age": 27}
->>> ext_info = {"gender": "male"}
->>>
->>> full_profile = dict(profile.items() | ext_info.items())
->>> full_profile
-{'gender': 'male', 'age': 27, 'name': 'xiaoming'}
-```
-
-
-
-当然了,你如果嫌这样太麻烦,也可以简单点,直接使用 list 函数再合并(示例为 Python 3.x )
-
-```python
->>> profile = {"name": "xiaoming", "age": 27}
->>> ext_info = {"gender": "male"}
->>>
->>> dict(list(profile.items()) + list(ext_info.items()))
-{'name': 'xiaoming', 'age': 27, 'gender': 'male'}
-```
-
-若你在 Python 2.x 下,可以直接省去 list 函数。
-
-```python
->>> profile = {"name": "xiaoming", "age": 27}
->>> ext_info = {"gender": "male"}
->>>
->>> dict(profile.items() + ext_info.items())
-{'name': 'xiaoming', 'age': 27, 'gender': 'male'}
-```
+这个历史问题一直遗留到现在,由于这次的功能影响到的代码较多,如果要对两个版本的框架分别进行定制的话,需要花不少的时间,为了不维护两套版本,避免浪费多余的精力去做适配,我决定将 CentOS 6.x 上默认的 Python2.6 升级成 Python2.7。
+下面是整个升级过程,别看步骤简单,这些精简步骤的背后可是有不少的坑,被我踩过后,你可以直接使用了。
-**6. 最酷炫的字典解析式**
-Python 里对于生成列表、集合、字典,有一套非常 Pythonnic 的写法。
-那就是列表解析式,集合解析式和字典解析式,通常是 Python 发烧友的最爱,那么今天的主题:字典合并,字典解析式还能否胜任呢?
+1. 首先确认下你机器上的默认的 Python 版本
-当然可以,具体示例代码如下:
+```shell
+$ python -V
+Python 2.6.6
-```python
->>> profile = {"name": "xiaoming", "age": 27}
->>> ext_info = {"gender": "male"}
->>>
->>> {k:v for d in [profile, ext_info] for k,v in d.items()}
-{'name': 'xiaoming', 'age': 27, 'gender': 'male'}
+$ whereis python
+python: /usr/bin/python /usr/bin/python2.6 /usr/lib/python2.6 /usr/lib64/python2.6 /usr/local/bin/python /usr/include/python2.6 /usr/share/man/man1/python.1.gz
```
+
+2. 由于我们将使用编译安装的方式,所以要安装下 gcc,及一些工具包。
-**7. Python 3.9 新特性**
+注意一定要全部安装,不然后面会发现有不少 python 的工具用不了。
-在 2 月份发布的 Python 3.9.04a 版本中,新增了一个抓眼球的新操作符操作符: `|`, PEP584 将它称之为合并操作符(Union Operator),用它可以很直观地合并多个字典。
+比如不安装 zlib 会无法安装 setuptools,不装 openssl 和 openssl-devel,会无法使用 pip 工具等
-```python
->>> profile = {"name": "xiaoming", "age": 27}
->>> ext_info = {"gender": "male"}
->>>
->>> profile | ext_info
-{'name': 'xiaoming', 'age': 27, 'gender': 'male'}
->>>
->>> ext_info | profile
-{'gender': 'male', 'name': 'xiaoming', 'age': 27}
->>>
->>>
```
-
-除了 `|` 操作符之外,还有另外一个操作符 `|=`,类似于原地更新。
-
-```python
->>> ext_info |= profile
->>> ext_info
-{'gender': 'male', 'name': 'xiaoming', 'age': 27}
->>>
->>>
->>> profile |= ext_info
->>> profile
-{'name': 'xiaoming', 'age': 27, 'gender': 'male'}
+$ yum install gcc -y
+$ yum groupinstall "Development tools"
+$ yum install zlib-devel bzip2-devel openssl openssl-devel ncurses-devel sqlite-devel -y
```
+ 如果你这里未按照我的步骤来安装,你后面使用的时候出现了各种各样的问题,不要慌,只要再回来这里,把没安装的包装上,安装完成后,你需要进入第四步重新编译安装Python。
-看到这里,有没有涨姿势了,学了这么久的 Python ,没想到合并字典还有这么多的方法。本篇文章的主旨,并不在于让你全部掌握这 7 种合并字典的方法,实际在工作中,你只要选用一种最顺手的方式即可,但是在协同工作中,或者在阅读他人代码时,你不可避免地会碰到各式各样的写法,这时候你能下意识的知道这是在做合并字典的操作,那这篇文章就是有意义的。
-
-
-
-> 作者:王炳明,微信公众号《Python编程时光》。版权归个人所有,欢迎分享,仅用于学习交流,但勿用作商业用途,违者必究。
-
-## 45. 条件语句的七种写法
-**第一种:原代码**
+3. 下载最新的 Python2.7.x 安装包,解压并进入指定目录
-这是一段非常简单的通过年龄判断一个人是否成年的代码,由于代码行数过多,有些人就不太愿意这样写,因为这体现不出自己多年的 Python 功力。
-
-```python
-if age > 18:
- return "已成年"
-else:
- return "未成年"
```
-
-下面我列举了六种这段代码的变异写法,一个比一个还 6 ,单独拿出来比较好理解,放在工程代码里,没用过这些学法的人,一定会看得一脸懵逼,理解了之后,又不经意大呼:**卧槽,还可以这样写?**,而后就要开始骂街了:**这是给人看的代码?** (除了第一种之外)
-
-**第二种**
-
-语法:
-
-```python
- if else
+$ wget https://www.python.org/ftp/python/2.7.14/Python-2.7.14.tgz
+$ tar zxvf Python-2.7.14.tgz
+$ cd Python-2.7.14
```
-例子
-
-```python
->>> age1 = 20
->>> age2 = 17
->>>
->>>
->>> msg1 = "已成年" if age1 > 18 else "未成年"
->>> print msg1
-已成年
->>>
->>> msg2 = "已成年" if age2 > 18 else "未成年"
->>> print msg2
-未成年
->>>
-```
-**第三种**
-语法
+4. 配置,编译,安装
-```python
- and or
+```shell
+# --prefix 指定 python 安装的路径
+$ ./configure --prefix=/usr/local/python/python2.7
+$ make
+$ make install
```
-例子
+`./configure` 命令执行完毕之后创建一个文件creating Makefile,供下面的make命令使用 执行 `make install` 之后就会把程序安装到我们指定的目录中去。
-```python
->>> msg1 = age1 > 18 and "已成年" or "未成年"
->>> msg2 = "已成年" if age2 > 18 else "未成年"
->>>
->>> print(msg1)
-已成年
->>>
->>> print(msg2)
-未成年
-```
+Configure是一个可执行脚本,它有很多选项,在待安装的源码路径下使用命令./configure –help输出详细的选项列表。其中 `--prefix` 选项是配置安装的路径,如果不配置该选项,安装后可执行文件默认放在/usr /local/bin,库文件默认放在 `/usr/local/lib` ,配置文件默认放在 `/usr/local/etc` ,其它的资源文件放在 `/usr /local/share`。如果配置 `--prefix`,如:`./configure --prefix=/usr/local/test` 可以把所有资源文件放在/usr/local/test的路径中,不会杂乱。
-**第四种**
+用了 `--prefix` 选项的另一个好处是卸载软件或移植软件。当某个安装的软件不再需要时,只须简单的删除该安装目录,就可以把软件卸载得干干净净;移植软件只需拷贝整个目录到另外一个机器即可(相同的操作系统)。当然要卸载程序,也可以在原来的make目录下用一次 `make uninstall`,但前提是make文件指定过uninstall。
-语法
-
-```python
-(, )[condition]
-```
+
-例子
+5. 查看系统的 Python 版本
-```python
->>> msg1 = ("未成年", "已成年")[age1 > 18]
->>> print(msg1)
-已成年
->>>
->>>
->>> msg2 = ("未成年", "已成年")[age2 > 18]
->>> print(msg2)
-未成年
+```shell
+$ python -V
+Python 2.6.6
```
-**第五种**
+ 如果你查看还是 Python 2.6.6 版本,请继续看第六步。
-语法
-```python
-(lambda: , lambda:)[]()
-```
-例子
+6. 修改系统默认的 Python 版本
-```python
->>> msg1 = (lambda:"未成年", lambda:"已成年")[age1 > 18]()
->>> print(msg1)
-已成年
->>>
->>> msg2 = (lambda:"未成年", lambda:"已成年")[age2 > 18]()
->>> print(msg2)
-未成年
-```
+查看新安装的Python版本,当前系统的Python版本,并将系统指向的Python从2.6.x修改为2.7.x,再次查看当前系统的Python版本,已经变更为2.7.x
-**第六种**
+```shell
+# 这是我们刚安装的 Python
+$/usr/local/bin/python2.7 -V
+Python 2.7.14
-语法:
+# 这是系统默认 Python
+$ /usr/bin/python -V
+Python 2.6.6
-```python
-{True: , False: }[]
-```
+# 备份原来的 Python 文件
+$ mv /usr/bin/python /usr/bin/python.bak
-例子:
+# 建立软链接,将我们刚安装的 python2.7 做为系统默认版本
+ln -s /usr/local/bin/python2.7 /usr/bin/python
-```python
->>> msg1 = {True: "已成年", False: "未成年"}[age1 > 18]
->>> print(msg1)
-已成年
->>>
->>> msg2 = {True: "已成年", False: "未成年"}[age2 > 18]
->>> print(msg2)
-未成年
+# 再次查看 Python 版本,已经成功切换过来
+$ python -V
+Python 2.7.14
```
-**第七种**
-
-语法
+7. 重新指定 yum 的Python版本
-```python
-(() and (,) or (,))[0]
-```
+上面我们改了系统的默认 Python 版本,由于CentOS 6.x 的 yum 是基于Python2.6 的,为了不影响 yum 的使用,需单独将yum指向python2.6版本。
-例子
+编辑: vim /usr/bin/yum ,将` /usr/bin/python` 改成 `/usr/bin/python2.6`
```python
->>> msg1 = ((age1 > 18) and ("已成年",) or ("未成年",))[0]
->>> print(msg1)
-已成年
->>>
->>> msg2 = ((age2 > 18) and ("已成年",) or ("未成年",))[0]
->>> print(msg2)
-未成年
+#!/usr/bin/python2.6
```
-以上代码,都比较简单,仔细看都能看懂,我就不做解释了。
-看到这里,有没有涨姿势了,学了这么久的 Python ,这么多骚操作,还真是活久见。。这六种写法里,我最推荐使用的是第一种,自己也经常在用,简洁直白,代码行还少。而其他的写法虽然能写,但是不会用,也不希望在我余生里碰到会在公共代码里用这些写法的同事。
-> 作者:王炳明,微信公众号《Python编程时光》。版权归个人所有,欢迎分享,仅用于学习交流,但勿用作商业用途,违者必究。
+8. 安装 setuptools 及 pip
-## 46. /usr/bin/env python 有什么用?
-
-我们经常会在别人的脚本或者项目的入口文件里看到第一行是下面这样
+pip是python的安装工具,很多python的常用工具,都可以通过pip进行安装。要安装pip,首先要安装setuptools。从这个链接,你可以得到相关信息:https://pypi.python.org/pypi/setuptools
```shell
-#!/usr/bin/python
-```
-
-或者这样
-
-```sh e llsh el
-#!/usr/bin/env python
+# 下载 setuptools
+$ wget https://pypi.python.org/packages/ff/d4/209f4939c49e31f5524fa0027bf1c8ec3107abaf7c61fdaad704a648c281/setuptools-21.0.0.tar.gz#md5=81964fdb89534118707742e6d1a1ddb4
```
-这两者有什么区别呢?
-
-稍微接触过 linux 的人都知道 `/usr/bin/python` 就是我们执行 `python` 进入console 模式里的 `python`
-
-
-
-而当你在可执行文件头里使用 `#!` + `/usr/bin/python` ,意思就是说你得用哪个软件 (python)来执行这个文件。
-
-那么加和不加有什么区别呢?
-
-不加的话,你每次执行这个脚本时,都得这样: `python xx.py` ,
-
-
-
-有没有一种方式?可以省去每次都加 `python` 呢?
-
-当然有,你可以文件头里加上`#!/usr/bin/python` ,那么当这个文件有可执行权限 时,只直接写这个脚本文件,就像下面这样。
-
-
-
-明白了这个后,再来看看 `!/usr/bin/env python` 这个 又是什么意思 ?
-
-当我执行 `env python` 时,自动进入了 python console 的模式。
-
-
-
-这是为什么?和 直接执行 python 好像没什么区别呀
-
-当你执行 `env python` 时,它其实会去 `env | grep PATH` 里(也就是 /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin )这几个路径里去依次查找名为python的可执行文件。
-
-找到一个就直接执行,上面我们的 python 路径是在 `/usr/bin/python` 里,在 `PATH` 列表里倒数第二个目录下,所以当我在 `/usr/local/sbin` 下创建一个名字也为 python 的可执行文件时,就会执行 `/usr/bin/python` 了。
-
-具体演示过程,你可以看下面。
-
-
-
-那么对于这两者,我们应该使用哪个呢?
-
-个人感觉应该优先使用 `#!/usr/bin/env python`,因为不是所有的机器的 python 解释器都是 `/usr/bin/python` 。
-
-## 47. 让我爱不释手的用户环境
-
-当你在机器上并没有 root 权限时,如何安装 Python 的第三方包呢?
-
-可以使用 `pip install --user pkg` 将你的包安装在你的用户环境中,该用户环境与全局环境并不冲突,并且多用户之间相互隔离,互不影响。
+同样的,进行安装:
```shell
-# 在全局环境中未安装 requests
-[root@localhost ~]$ pip list | grep requests
-[root@localhost ~]$ su - wangbm
-
-# 由于用户环境继承自全局环境,这里也未安装
-[wangbm@localhost ~]$ pip list | grep requests
-[wangbm@localhost ~]$ pip install --user requests
-[wangbm@localhost ~]$ pip list | grep requests
-requests (2.22.0)
-[wangbm@localhost ~]$
-
-# 从 Location 属性可发现 requests 只安装在当前用户环境中
-[wangbm@localhost ~]$ pip show requests
----
-Metadata-Version: 2.1
-Name: requests
-Version: 2.22.0
-Summary: Python HTTP for Humans.
-Home-page: http://python-requests.org
-Author: Kenneth Reitz
-Author-email: me@kennethreitz.org
-Installer: pip
-License: Apache 2.0
-Location: /home/wangbm/.local/lib/python2.7/site-packages
-[wangbm@localhost ~]$ exit
-logout
-
-# 退出 wangbm 用户,在 root 用户环境中发现 requests 未安装
-[root@localhost ~]$ pip list | grep requests
-[root@localhost ~]$
-```
-
-
-
-## 48. 实现类似 defer 的延迟调用
-
-在 Golang 中有一种延迟调用的机制,关键字是 defer,例如下面的示例
-
-```go
-import "fmt"
-
-func myfunc() {
- fmt.Println("B")
-}
-
-func main() {
- defer myfunc()
- fmt.Println("A")
-}
+$ tar vxf setuptools-21.0.0.tar.gz
+$ cd setuptools-21.0.0
+$ python setup.py install
```
-输出如下,myfunc 的调用会在函数返回前一步完成,即使你将 myfunc 的调用写在函数的第一行,这就是延迟调用。
+安装完成后,下载pip。其信息在如下网站:https://pypi.python.org/pypi/pip
-```
-A
-B
-```
-
-那么在 Python 中否有这种机制呢?
-
-当然也有,只不过并没有 Golang 这种简便。
-
-在 Python 可以使用 **上下文管理器** 达到这种效果
-
-```python
-import contextlib
-
-def callback():
- print('B')
-
-with contextlib.ExitStack() as stack:
- stack.callback(callback)
- print('A')
+```shell
+# 下载 pip
+wget https://pypi.python.org/packages/41/27/9a8d24e1b55bd8c85e4d022da2922cb206f183e2d18fee4e320c9547e751/pip-8.1.1.tar.gz#md5=6b86f11841e89c8241d689956ba99ed7
```
-输出如下
+同样的,进行安装
-```
-A
-B
+```shell
+$ tar vxf pip-8.1.1.tar.gz
+$ cd pip-8.1.1
+$ python setup.py install
```
-## 49. 自带的缓存机制不用白不用
+安装完成后,执行 `pip list` 查看一下安装的包,若无异常,则一切顺利。或者你也可以试着安装一下第三方包 `pip install requests` 。
-缓存是一种将定量数据加以保存,以备迎合后续获取需求的处理方式,旨在加快数据获取的速度。
-数据的生成过程可能需要经过计算,规整,远程获取等操作,如果是同一份数据需要多次使用,每次都重新生成会大大浪费时间。所以,如果将计算或者远程请求等操作获得的数据缓存下来,会加快后续的数据获取需求。
-为了实现这个需求,Python 3.2 + 中给我们提供了一个机制,可以很方便的实现,而不需要你去写这样的逻辑代码。
-
-这个机制实现于 functool 模块中的 lru_cache 装饰器。
-
-```python
-@functools.lru_cache(maxsize=None, typed=False)
-```
+8. 转移cloudinit
-参数解读:
+上面说的项目,其实就是 cloudinit。接下来就要将 centos 7.2 上的cloudinit 的目录整体拷贝到 centos 6.5 的/usr/local/lib/python2.7/site-packages/ 目录下
-- maxsize:最多可以缓存多少个此函数的调用结果,如果为None,则无限制,设置为 2 的幂时,性能最佳
-- typed:若为 True,则不同参数类型的调用将分别缓存。
+
-举个例子
+然后安装一些 cloudinit 的依赖包。
-```python
-from functools import lru_cache
+```shell
+$ pip install six requests prettytable jsonpatch configobj
-@lru_cache(None)
-def add(x, y):
- print("calculating: %s + %s" % (x, y))
- return x + y
+# 默认还是安装在 python2.6 下
+$ yum install PyYAML -y
-print(add(1, 2))
-print(add(1, 2))
-print(add(2, 3))
+# 将这些文件拷贝到 python2.7 目录下
+# 如果你不知道 python2.7 的目录,使用 import sys;print sys.path 就可以打印
+$ cd /usr/lib64/python2.6/site-packages
+$ cp -r yaml/ /usr/local/lib/python2.7/site-packages/
+$ cp -p _yaml.so /usr/local/lib/python2.7/site-packages/
+$ cp -p PyYAML-3.10-py2.6.egg-info /usr/local/lib/python2.7/site-packages/
```
-输出如下,可以看到第二次调用并没有真正的执行函数体,而是直接返回缓存里的结果
+执行一下 cloudinit 的几个命令,没有问题,任务就完成了。
```shell
-calculating: 1 + 2
-3
-3
-calculating: 2 + 3
-5
+$ cloud-init init -l
+$ cloud-init init
```
-## 50. 重定向标准输出到日志
-
-假设你有一个脚本,会执行一些任务,比如说集群健康情况的检查。
-
-检查完成后,会把各服务的的健康状况以 JSON 字符串的形式打印到标准输出。
-
-如果代码有问题,导致异常处理不足,最终检查失败,是很有可能将一些错误异常栈输出到标准错误或标准输出上。
-
-由于最初约定的脚本返回方式是以 JSON 的格式输出,此时你的脚本却输出各种错误异常,异常调用方也无法解析。
-
-如何避免这种情况的发生呢?
-
-我们可以这样做,把你的标准错误输出到日志文件中。
-
-```python
-import contextlib
-
-log_file="/var/log/you.log"
-
-def you_task():
- pass
-
-@contextlib.contextmanager
-def close_stdout():
- raw_stdout = sys.stdout
- file = open(log_file, 'a+')
- sys.stdout = file
-
- yield
-
- sys.stdout = raw_stdout
- file.close()
-
-with close_stdout():
- you_task()
-```
+**参考文章**
----
+- https://www.cnblogs.com/stonehe/p/7944366.html
-
+
diff --git a/source/c01/c01_10.rst b/source/c01/c01_10.rst
old mode 100755
new mode 100644
index cf3d866..7b9f5a7
--- a/source/c01/c01_10.rst
+++ b/source/c01/c01_10.rst
@@ -1,2339 +1,207 @@
-1.10 Python 黑魔法指南 50 例
-============================
+1.10 如何修改 CentOS 6.x 上默认Python
+=====================================
---------------
-
- 作者:王炳明,微信公众号《Python编程时光》。版权归个人所有,欢迎分享,仅用于学习交流,但勿用作商业用途,违者必究。
-
-01. 默默无闻的省略号很好用
---------------------------
-
-在Python中,一切皆对象,省略号也不例外。
-
-在 Python 3 中你可以直接写 ``...`` 来得到它
-
-.. code:: python
-
- >>> ...
- Ellipsis
- >>> type(...)
-
-
-而在 Python 2 中没有\ ``...`` 这个语法,只能直接写Ellipsis来获取。
-
-.. code:: python
-
- >>> Ellipsis
- Ellipsis
- >>> type(Ellipsis)
-
- >>>
-
-它转为布尔值时为真
+|image0|
-.. code:: python
+最近在工作中遇到一个问题,就是有一个功能希望在各种服务器上实现,而服务器上的系统版本可能都不一样,有的是
+CentOS 6.x,有的是 CentOS 7.x 。
- >>> bool(...)
- True
+需要说明的一点是,CentOS 6.x 上的 Python 版本是 2.6.x 的,而 CentOS 7.x
+上的 Python 版本是 2.7.x
+的,这意味着我要实现的功能要适配这两种版本的系统。
-最后,这东西是一个单例。
+你可能会说,这有什么的,自己写的时候,注意一下就好了。
-.. code:: python
+事情其实没有那么容易,我要实现的功能是基于一个框架进行定制,需要修改不少的框架代码。这个框架在不同的
+Linux 版本上,是有不同的版本的,而且差异巨大,曾经想过在 CentOS 6.x 和
+CentOS 7.x
+将这个框架安装成同一个版本,最后还是失败了,无法安装,原因就是高版本需要
+Python2.7,而 CentOS 6.x 上只有 Python2.6。
- >>> id(...)
- 4362672336
- >>> id(...)
- 4362672336
+这个历史问题一直遗留到现在,由于这次的功能影响到的代码较多,如果要对两个版本的框架分别进行定制的话,需要花不少的时间,为了不维护两套版本,避免浪费多余的精力去做适配,我决定将
+CentOS 6.x 上默认的 Python2.6 升级成 Python2.7。
-那这东西有啥用呢?
+下面是整个升级过程,别看步骤简单,这些精简步骤的背后可是有不少的坑,被我踩过后,你可以直接使用了。
-1. 它是 Numpy 的一个语法糖
-2. 在 Python 3 中可以使用 … 代替 pass
+1. 首先确认下你机器上的默认的 Python 版本
.. code:: shell
- $ cat demo.py
- def func01():
- ...
-
- def func02():
- pass
+ $ python -V
+ Python 2.6.6
- func01()
- func02()
+ $ whereis python
+ python: /usr/bin/python /usr/bin/python2.6 /usr/lib/python2.6 /usr/lib64/python2.6 /usr/local/bin/python /usr/include/python2.6 /usr/share/man/man1/python.1.gz
- print("ok")
+2. 由于我们将使用编译安装的方式,所以要安装下 gcc,及一些工具包。
- $ python3 demo.py
- ok
+注意一定要全部安装,不然后面会发现有不少 python 的工具用不了。
-02. 使用 end 来结束代码块
--------------------------
+比如不安装 zlib 会无法安装 setuptools,不装 openssl 和
+openssl-devel,会无法使用 pip 工具等
-有不少编程语言,循环、判断代码块需要用 end
-标明结束,这样一定程度上会使代码逻辑更加清晰一点。
-
-但是其实在 Python 这种严格缩进的语言里并没有必要这样做。
-
-如果你真的想用,也不是没有办法,具体你看下面这个例子。
+::
-.. code:: python
+ $ yum install gcc -y
+ $ yum groupinstall "Development tools"
+ $ yum install zlib-devel bzip2-devel openssl openssl-devel ncurses-devel sqlite-devel -y
- __builtins__.end = None
+如果你这里未按照我的步骤来安装,你后面使用的时候出现了各种各样的问题,不要慌,只要再回来这里,把没安装的包装上,安装完成后,你需要进入第四步重新编译安装Python。
+3. 下载最新的 Python2.7.x 安装包,解压并进入指定目录
- def my_abs(x):
- if x > 0:
- return x
- else:
- return -x
- end
- end
+::
- print(my_abs(10))
- print(my_abs(-10))
+ $ wget https://www.python.org/ftp/python/2.7.14/Python-2.7.14.tgz
+ $ tar zxvf Python-2.7.14.tgz
+ $ cd Python-2.7.14
-执行后,输出如下
+4. 配置,编译,安装
.. code:: shell
- [root@localhost ~]$ python demo.py
- 10
- 10
-
-03. 可直接运行的 zip 包
------------------------
+ # --prefix 指定 python 安装的路径
+ $ ./configure --prefix=/usr/local/python/python2.7
+ $ make
+ $ make install
-我们可以经常看到有 Python 包,居然可以以 zip
-包进行发布,并且可以不用解压直接使用。
+``./configure`` 命令执行完毕之后创建一个文件creating
+Makefile,供下面的make命令使用 执行 ``make install``
+之后就会把程序安装到我们指定的目录中去。
-这与大多数人的认识的 Python 包格式不一样,正常人认为 Python 包的格式要嘛
-是 egg,要嘛是whl 格式。
+Configure是一个可执行脚本,它有很多选项,在待安装的源码路径下使用命令./configure
+–help输出详细的选项列表。其中 ``--prefix``
+选项是配置安装的路径,如果不配置该选项,安装后可执行文件默认放在/usr
+/local/bin,库文件默认放在 ``/usr/local/lib`` ,配置文件默认放在
+``/usr/local/etc`` ,其它的资源文件放在
+``/usr /local/share``\ 。如果配置
+``--prefix``\ ,如:\ ``./configure --prefix=/usr/local/test``
+可以把所有资源文件放在/usr/local/test的路径中,不会杂乱。
-那么这个zip 是如何制作的呢,请看下面的示例。
-
-.. code:: shell
+用了 ``--prefix``
+选项的另一个好处是卸载软件或移植软件。当某个安装的软件不再需要时,只须简单的删除该安装目录,就可以把软件卸载得干干净净;移植软件只需拷贝整个目录到另外一个机器即可(相同的操作系统)。当然要卸载程序,也可以在原来的make目录下用一次
+``make uninstall``\ ,但前提是make文件指定过uninstall。
- [root@localhost ~]# ls -l demo
- total 8
- -rw-r--r-- 1 root root 30 May 8 19:27 calc.py
- -rw-r--r-- 1 root root 35 May 8 19:33 __main__.py
- [root@localhost ~]#
- [root@localhost ~]# cat demo/__main__.py
- import calc
-
- print(calc.add(2, 3))
- [root@localhost ~]#
- [root@localhost ~]# cat demo/calc.py
- def add(x, y):
- return x+y
- [root@localhost ~]#
- [root@localhost ~]# python -m zipfile -c demo.zip demo/*
- [root@localhost ~]#
-
-制作完成后,我们可以执行用 python 去执行它
+5. 查看系统的 Python 版本
.. code:: shell
- [root@localhost ~]# python demo.zip
- 5
- [root@localhost ~]#
-
-04. 反斜杠的倔强: 不写最后
---------------------------
-
-``\`` 在 Python 中的用法主要有两种
-
-**1、在行尾时,用做续行符**
-
-.. code:: python
-
- [root@localhost ~]$ cat demo.py
- print("hello "\
- "world")
- [root@localhost ~]$
- [root@localhost ~]$ python demo.py
- hello world
-
-**2、在字符串中,用做转义字符,可以将普通字符转化为有特殊含义的字符。**
-
-.. code:: python
-
- >>> str1='\nhello' #换行
- >>> print(str1)
-
- hello
- >>> str2='\thello' #tab
- >>> print(str2)
- hello
-
-但是如果你用单\ ``\``\ 结尾是会报语法错误的
-
-.. code:: python
-
- >>> str3="\"
- File "", line 1
- str3="\"
- ^
- SyntaxError: EOL while scanning string literal
-
-就算你指定它是个 raw 字符串,也不行。
-
-.. code:: python
-
- >>> str3=r"\"
- File "", line 1
- str3=r"\"
- ^
- SyntaxError: EOL while scanning string literal
-
-05. 单行实现 for 死循环如何写?
--------------------------------
-
-如果让你在不借助 while ,只使用 for 来写一个死循环?
-
-**你会写吗?**
-
-**如果你还说简单,你可以自己试一下。**
-
-…
-
-如果你尝试后,仍然写不出来,那我给出自己的做法。
-
-.. code:: python
-
- for i in iter(int, 1):pass
-
-**是不是傻了?iter 还有这种用法?这为啥是个死循环?**
-
-关于这个问题,你如果看中文网站,可能找不到相关资料。
-
-还好你可以通过 IDE 看py源码里的注释内容,介绍了很详细的使用方法。
-
-原来iter有两种使用方法。
-
-- 通常我们的认知是第一种,将一个列表转化为一个迭代器。
-
-- 而第二种方法,他接收一个 callable对象,和一个sentinel
- 参数。第一个对象会一直运行,直到它返回 sentinel 值才结束。
-
-那\ ``int`` 呢?
-
-这又是一个知识点,int
-是一个内建方法。通过看注释,可以看出它是有默认值0的。你可以在console
-模式下输入 ``int()`` 看看是不是返回0。
-
-由于int() 永远返回0,永远返回不了1,所以这个 for
-循环会没有终点。一直运行下去。
-
- 作者:王炳明,微信公众号《Python编程时光》。版权归个人所有,欢迎分享,仅用于学习交流,但勿用作商业用途,违者必究。
-
-06. 懒人必备技能:使用 “_”
---------------------------
-
-对于 ``_`` ,大家对于他的印象都是用于
-**占位符**\ ,省得为一个不需要用到的变量,绞尽脑汁的想变量名。
+ $ python -V
+ Python 2.6.6
-今天要介绍的是他的第二种用法,就是在 console 模式下的应用。
+如果你查看还是 Python 2.6.6 版本,请继续看第六步。
-示例如下:
+6. 修改系统默认的 Python 版本
-.. code:: python
-
- >>> 3 + 4
- 7
- >>> _
- 7
- >>> name='公众号: Python编程时光'
- >>> name
- '公众号: Python编程时光'
- >>> _
- '公众号: Python编程时光'
-
-它可以返回上一次的运行结果。
-
-但是,如果是print函数打印出来的就不行了。
-
-.. code:: python
-
- >>> 3 + 4
- 7
- >>> _
- 7
- >>> print("公众号: Python编程时光")
- ming
- >>> _
- 7
-
-我自己写了个例子,验证了下,用\ ``__repr__``\ 输出的内容可以被获取到的。
-首先,在我们的目录下,写一个文件 demo.py。内容如下
-
-.. code:: python
-
- # demo.py
- class mytest():
- def __str__(self):
- return "hello"
-
- def __repr__(self):
- return "world"
-
-然后在这个目录下进入交互式环境。
-
-.. code:: python
-
- >>> import demo
- >>> mt=demo.mytest()
- >>> mt
- world
- >>> print(mt)
- hello
- >>> _
- world
-
-知道这两个魔法方法的人,一看就明白了,这里不再解释啦。
-
-07. 最快查看包搜索路径的方式
-----------------------------
-
-当你使用 import 导入一个包或模块时,Python
-会去一些目录下查找,而这些目录是有优先级顺序的,正常人会使用 sys.path
-查看。
-
-.. code:: python
-
- >>> import sys
- >>> from pprint import pprint
- >>> pprint(sys.path)
- ['',
- '/usr/local/Python3.7/lib/python37.zip',
- '/usr/local/Python3.7/lib/python3.7',
- '/usr/local/Python3.7/lib/python3.7/lib-dynload',
- '/home/wangbm/.local/lib/python3.7/site-packages',
- '/usr/local/Python3.7/lib/python3.7/site-packages']
- >>>
-
-那有没有更快的方式呢?
-
-我这有一种连 console 模式都不用进入的方法,一行命令即可解决
+查看新安装的Python版本,当前系统的Python版本,并将系统指向的Python从2.6.x修改为2.7.x,再次查看当前系统的Python版本,已经变更为2.7.x
.. code:: shell
- [wangbm@localhost ~]$ python3 -m site
- sys.path = [
- '/home/wangbm',
- '/usr/local/Python3.7/lib/python37.zip',
- '/usr/local/Python3.7/lib/python3.7',
- '/usr/local/Python3.7/lib/python3.7/lib-dynload',
- '/home/wangbm/.local/lib/python3.7/site-packages',
- '/usr/local/Python3.7/lib/python3.7/site-packages',
- ]
- USER_BASE: '/home/wangbm/.local' (exists)
- USER_SITE: '/home/wangbm/.local/lib/python3.7/site-packages' (exists)
- ENABLE_USER_SITE: True
-
-从输出你可以发现,这个列的路径会比 sys.path
-更全,它包含了用户环境的目录。
-
-08. and 和 or 的取值顺序
-------------------------
-
-and 和 or 是我们再熟悉不过的两个逻辑运算符,在 Python 也有它有妙用。
-
-- 当一个 **or 表达式**\ 中所有值都为真,Python会选择第一个值
-
-- 当一个 **and 表达式** 所有值都为真,Python 会选择第二个值。
-
-示例如下:
-
-.. code:: python
+ # 这是我们刚安装的 Python
+ $/usr/local/bin/python2.7 -V
+ Python 2.7.14
- >>>(2 or 3) * (5 and 7)
- 14 # 2*7
+ # 这是系统默认 Python
+ $ /usr/bin/python -V
+ Python 2.6.6
-09. 如何修改解释器提示符
-------------------------
+ # 备份原来的 Python 文件
+ $ mv /usr/bin/python /usr/bin/python.bak
-这个当做今天的一个小彩蛋吧。应该算是比较冷门的,估计知道的人很少了吧。
+ # 建立软链接,将我们刚安装的 python2.7 做为系统默认版本
+ ln -s /usr/local/bin/python2.7 /usr/bin/python
-正常情况下,我们在 终端下 执行Python 命令是这样的。
+ # 再次查看 Python 版本,已经成功切换过来
+ $ python -V
+ Python 2.7.14
-.. code:: python
+7. 重新指定 yum 的Python版本
- >>> for i in range(2):
- ... print (i)
- ...
- 0
- 1
+上面我们改了系统的默认 Python 版本,由于CentOS 6.x 的 yum
+是基于Python2.6 的,为了不影响 yum
+的使用,需单独将yum指向python2.6版本。
-你是否想过 ``>>>`` 和 ``...`` 这两个提示符也是可以修改的呢?
+编辑: vim /usr/bin/yum ,将\ ``/usr/bin/python`` 改成
+``/usr/bin/python2.6``
.. code:: python
- >>> import sys
- >>> sys.ps1
- '>>> '
- >>> sys.ps2
- '... '
- >>>
- >>> sys.ps2 = '---------------- '
- >>> sys.ps1 = 'Python编程时光>>>'
- Python编程时光>>>for i in range(2):
- ---------------- print (i)
- ----------------
- 0
- 1
-
-10. 逗号也有它独特的用法
-------------------------
+ #!/usr/bin/python2.6
-逗号,虽然是个很不起眼的符号,但在 Python 中也有他的用武之地。
+8. 安装 setuptools 及 pip
-**第一个用法**
-
-元组的转化
+pip是python的安装工具,很多python的常用工具,都可以通过pip进行安装。要安装pip,首先要安装setuptools。从这个链接,你可以得到相关信息:https://pypi.python.org/pypi/setuptools
.. code:: shell
- [root@localhost ~]# cat demo.py
- def func():
- return "ok",
-
- print(func())
- [root@localhost ~]# python3 demo.py
- ('ok',)
+ # 下载 setuptools
+ $ wget https://pypi.python.org/packages/ff/d4/209f4939c49e31f5524fa0027bf1c8ec3107abaf7c61fdaad704a648c281/setuptools-21.0.0.tar.gz#md5=81964fdb89534118707742e6d1a1ddb4
-**第二个用法**
-
-print 的取消换行
+同样的,进行安装:
.. code:: shell
- [root@localhost ~]# cat demo.py
- for i in range(3):
- print i
- [root@localhost ~]#
- [root@localhost ~]# python demo.py
- 0
- 1
- 2
- [root@localhost ~]#
- [root@localhost ~]# vim demo.py
- [root@localhost ~]#
- [root@localhost ~]# cat demo.py
- for i in range(3):
- print i,
- [root@localhost ~]#
- [root@localhost ~]# python demo.py
- 0 1 2
- [root@localhost ~]#
-
-..
-
- 作者:王炳明,微信公众号《Python编程时光》。版权归个人所有,欢迎分享,仅用于学习交流,但勿用作商业用途,违者必究。
-
-11. 默认参数最好不为可变对象
-----------------------------
-
-函数的参数分三种 - 可变参数 - 默认参数 - 关键字参数
-
-当你在传递默认参数时,有新手很容易踩雷的一个坑。
-
-先来看一个示例
-
-.. code:: python
-
- def func(item, item_list=[]):
- item_list.append(item)
- print(item_list)
-
- func('iphone')
- func('xiaomi', item_list=['oppo','vivo'])
- func('huawei')
-
-在这里,你可以暂停一下,思考一下会输出什么?
-
-思考过后,你的答案是否和下面的一致呢
-
-::
-
- ['iphone']
- ['oppo', 'vivo', 'xiaomi']
- ['iphone', 'huawei']
-
-如果是,那你可以跳过这部分内容,如果不是,请接着往下看,这里来分析一下。
-
-Python 中的 def
-语句在每次执行的时候都初始化一个函数对象,这个函数对象就是我们要调用的函数,可以把它当成一个一般的对象,只不过这个对象拥有一个可执行的方法和部分属性。
-
-对于参数中提供了初始值的参数,由于 Python
-中的函数参数传递的是对象,也可以认为是传地址,在第一次初始化 def
-的时候,会先生成这个可变对象的内存地址,然后将这个默认参数 item_list
-会与这个内存地址绑定。在后面的函数调用中,如果调用方指定了新的默认值,就会将原来的默认值覆盖。如果调用方没有指定新的默认值,那就会使用原来的默认值。
-
-|image0|
-
-12. 访问类中的私有方法
-----------------------
-
-大家都知道,类中可供直接调用的方法,只有公有方法(protected类型的方法也可以,但是不建议)。也就是说,类的私有方法是无法直接调用的。
-
-这里先看一下例子
-
-.. code:: python
-
- class Kls():
- def public(self):
- print('Hello public world!')
-
- def __private(self):
- print('Hello private world!')
-
- def call_private(self):
- self.__private()
-
- ins = Kls()
+ $ tar vxf setuptools-21.0.0.tar.gz
+ $ cd setuptools-21.0.0
+ $ python setup.py install
- # 调用公有方法,没问题
- ins.public()
+安装完成后,下载pip。其信息在如下网站:https://pypi.python.org/pypi/pip
- # 直接调用私有方法,不行
- ins.__private()
-
- # 但你可以通过内部公有方法,进行代理
- ins.call_private()
-
-既然都是方法,那我们真的没有方法可以直接调用吗?
-
-当然有啦,只是建议你千万不要这样弄,这里只是普及,让你了解一下。
-
-.. code:: python
-
- # 调用私有方法,以下两种等价
- ins._Kls__private()
- ins.call_private()
-
-13. 时有时无的切片异常
-----------------------
-
-这是个简单例子,alist 只有5 个元素,当你取第 6
-个元素时,会抛出索引异常。这与我们的认知一致。
-
-.. code:: python
-
- >>> alist = [0, 1, 2, 3, 4]
- >>> alist[5]
- Traceback (most recent call last):
- File "", line 1, in
- IndexError: list index out of range
-
-但是当你使用 alist[5:] 取一个区间时,即使 alist 并没有 第
-6个元素,也不抛出异常,而是会返回一个新的列表。
-
-.. code:: python
-
- >>> alist = [0, 1, 2, 3, 4]
- >>> alist[5:]
- []
- >>> alist[100:]
- []
-
-14. 哪些情况下不需要续行符?
-----------------------------
-
-在写代码时,为了代码的可读性,代码的排版是尤为重要的。
-
-为了实现高可读性的代码,我们常常使用到的就是续行符 ``\``\ 。
-
-::
-
- >>> a = 'talk is cheap,'\
- ... 'show me the code.'
- >>>
- >>> print(a)
- talk is cheap,show me the code.
-
-那有哪些情况下,是不需要写续行符的呢?
-
-经过总结,在这些符号中间的代码换行可以省略掉续行符:\ ``[]``,\ ``()``,\ ``{}``
-
-::
-
- >>> my_list=[1,2,3,
- ... 4,5,6]
-
- >>> my_tuple=(1,2,3,
- ... 4,5,6)
-
- >>> my_dict={"name": "MING",
- ... "gender": "male"}
-
-另外还有,在多行文本注释中 ``'''`` ,续行符也是可以不写的。
-
-::
-
- >>> text = '''talk is cheap,
- ... show me the code'''
-
-15. Python2下 也能使用 print(“”)
---------------------------------
-
-可能会有不少人,觉得只有 Python 3 才可以使用 print(),而 Python 2
-只能使用\ ``print ""``\ 。
-
-但是其实并不是这样的。
-
-在Python 2.6之前,只支持
-
-.. code:: python
-
- print "hello"
-
-在Python 2.6和2.7中,可以支持如下三种
-
-.. code:: python
-
- print "hello"
- print("hello")
- print ("hello")
-
-在Python3.x中,可以支持如下两种
-
-.. code:: python
-
- print("hello")
- print ("hello")
-
-虽然 在 Python 2.6+ 可以和 Python3.x+ 一样,像函数一样去调用 print
-,但是这仅用于两个 python 版本之间的代码兼容,并不是说在
-python2.6+下使用 print() 后,就成了函数。
-
- 作者:王炳明,微信公众号《Python编程时光》。版权归个人所有,欢迎分享,仅用于学习交流,但勿用作商业用途,违者必究。
-
-16. 迷一样的字符串
-------------------
-
-示例一
-
-.. code:: python
-
- # Python2.7
- >>> a = "Hello_Python"
- >>> id(a)
- 32045616
- >>> id("Hello" + "_" + "Python")
- 32045616
-
- # Python3.7
- >>> a = "Hello_Python"
- >>> id(a)
- 38764272
- >>> id("Hello" + "_" + "Python")
- 32045616
-
-示例二
-
-.. code:: python
-
- >>> a = "MING"
- >>> b = "MING"
- >>> a is b
- True
-
- # Python2.7
- >>> a, b = "MING!", "MING!"
- >>> a is b
- True
-
- # Python3.7
- >>> a, b = "MING!", "MING!"
- >>> a is b
- False
-
-示例三
-
-.. code:: python
-
- # Python2.7
- >>> 'a' * 20 is 'aaaaaaaaaaaaaaaaaaaa'
- True
- >>> 'a' * 21 is 'aaaaaaaaaaaaaaaaaaaaa'
- False
-
- # Python3.7
- >>> 'a' * 20 is 'aaaaaaaaaaaaaaaaaaaa'
- True
- >>> 'a' * 21 is 'aaaaaaaaaaaaaaaaaaaaa'
- True
-
-17. return不一定都是函数的终点
-------------------------------
-
-众所周知,try…finally…
-的用法是:不管try里面是正常执行还是有报异常,最终都能保证finally能够执行。
-
-同时我们又知道,一个函数里只要遇到 return 函数就会立马结束。
-
-那问题就来了,以上这两种规则,如果同时存在,Python
-解释器会如何选择?哪个优先级更高?
-
-写个示例验证一下,就明白啦
-
-.. code:: python
-
- >>> def func():
- ... try:
- ... return 'try'
- ... finally:
- ... return 'finally'
- ...
- >>> func()
- 'finally'
-
-从输出中,我们可以发现:在try…finally…语句中,try中的 return
-会被直接忽视(这里的 return 不是函数的终点),因为要保证 finally
-能够执行。
-
-**如果 try 里的 return 真的是直接被忽视吗?**
-
-我们都知道如果一个函数没有 return,会隐式的返回 None,假设 try 里的
-return 真的是直接被忽视,那当finally 下没有显式的 return
-的时候,是不是会返回None呢?
-
-还是写个 示例来验证一下:
-
-.. code:: python
-
- >>> def func():
- ... try:
- ... return 'try'
- ... finally:
- ... print('finally')
- ...
- >>>
- >>> func()
- finally
- 'try'
- >>>
-
-从结果来看,当 finally 下没有 reutrn ,其实 try 里的 return
-仍然还是有效的。
-
-那结论就出来了,如果 finally 里有显式的 return,那么这个 return
-会直接覆盖 try 里的 return,而如果 finally 里没有 显式的 return,那么
-try 里的 return 仍然有效。
-
-18. 用户无感知的小整数池
-------------------------
-
-为避免整数频繁申请和销毁内存空间,Python 定义了一个小整数池 [-5, 256]
-这些整数对象是提前建立好的,不会被垃圾回收。
-
-以上代码请在 终端Python环境下测试,如果你是在IDE中测试,由于 IDE
-的影响,效果会有所不同。
-
-.. code:: python
-
- >>> a = -6
- >>> b = -6
- >>> a is b
- False
-
- >>> a = 256
- >>> b = 256
- >>> a is b
- True
-
- >>> a = 257
- >>> b = 257
- >>> a is b
- False
-
- >>> a = 257; b = 257
- >>> a is b
- True
-
-**问题又来了:最后一个示例,为啥是True?**
-
-因为当你在同一行里,同时给两个变量赋同一值时,解释器知道这个对象已经生成,那么它就会引用到同一个对象。如果分成两成的话,解释器并不知道这个对象已经存在了,就会重新申请内存存放这个对象。
-
-19. 神奇的 intern 机制
-----------------------
-
-字符串类型作为Python中最常用的数据类型之一,Python解释器为了提高字符串使用的效率和使用性能,做了很多优化.
-
-例如:Python解释器中使用了
-intern(字符串驻留)的技术来提高字符串效率,什么是intern机制?就是同样的字符串对象仅仅会保存一份,放在一个字符串储蓄池中,是共用的,当然,肯定不能改变,这也决定了字符串必须是不可变对象。
-
-::
-
- >>> s1="hello"
- >>> s2="hello"
- >>> s1 is s2
- True
-
- # 如果有空格,默认不启用intern机制
- >>> s1="hell o"
- >>> s2="hell o"
- >>> s1 is s2
- False
-
- # 如果一个字符串长度超过20个字符,不启动intern机制
- >>> s1 = "a" * 20
- >>> s2 = "a" * 20
- >>> s1 is s2
- True
-
- >>> s1 = "a" * 21
- >>> s2 = "a" * 21
- >>> s1 is s2
- False
-
- >>> s1 = "ab" * 10
- >>> s2 = "ab" * 10
- >>> s1 is s2
- True
-
- >>> s1 = "ab" * 11
- >>> s2 = "ab" * 11
- >>> s1 is s2
- False
-
-20. 反转字符串/列表最优雅的方式
--------------------------------
-
-反转序列并不难,但是如何做到最优雅呢?
-
-先来看看,正常是如何反转的。
-
-最简单的方法是使用列表自带的reverse()方法。
-
-.. code:: python
-
- >>> ml = [1,2,3,4,5]
- >>> ml.reverse()
- >>> ml
- [5, 4, 3, 2, 1]
-
-但如果你要处理的是字符串,reverse就无能为力了。你可以尝试将其转化成list,再reverse,然后再转化成str。转来转去,也太麻烦了吧?需要这么多行代码(后面三行是不能合并成一行的),一点都Pythonic。
-
-.. code:: python
-
- mstr1 = 'abc'
- ml1 = list(mstr1)
- ml1.reverse()
- mstr2 = str(ml1)
-
-对于字符串还有一种稍微复杂一点的,是自定义递归函数来实现。
-
-.. code:: python
+.. code:: shell
- def my_reverse(str):
- if str == "":
- return str
- else:
- return my_reverse(str[1:]) + str[0]
+ # 下载 pip
+ wget https://pypi.python.org/packages/41/27/9a8d24e1b55bd8c85e4d022da2922cb206f183e2d18fee4e320c9547e751/pip-8.1.1.tar.gz#md5=6b86f11841e89c8241d689956ba99ed7
-在这里,介绍一种最优雅的反转方式,使用切片,不管你是字符串,还是列表,简直通杀。
+同样的,进行安装
-.. code:: python
+.. code:: shell
- >>> mstr = 'abc'
- >>> ml = [1,2,3]
- >>> mstr[::-1]
- 'cba'
- >>> ml[::-1]
- [3, 2, 1]
+ $ tar vxf pip-8.1.1.tar.gz
+ $ cd pip-8.1.1
+ $ python setup.py install
-..
+安装完成后,执行 ``pip list``
+查看一下安装的包,若无异常,则一切顺利。或者你也可以试着安装一下第三方包
+``pip install requests`` 。
- 作者:王炳明,微信公众号《Python编程时光》。版权归个人所有,欢迎分享,仅用于学习交流,但勿用作商业用途,违者必究。
+8. 转移cloudinit
-21. 改变默认递归次数限制
-------------------------
+上面说的项目,其实就是 cloudinit。接下来就要将 centos 7.2 上的cloudinit
+的目录整体拷贝到 centos 6.5 的/usr/local/lib/python2.7/site-packages/
+目录下
-上面才提到递归,大家都知道使用递归是有风险的,递归深度过深容易导致堆栈的溢出。如果你这字符串太长啦,使用递归方式反转,就会出现问题。
+|image1|
-那到底,默认递归次数限制是多少呢?
+然后安装一些 cloudinit 的依赖包。
-.. code:: python
+.. code:: shell
- >>> import sys
- >>> sys.getrecursionlimit()
- 1000
+ $ pip install six requests prettytable jsonpatch configobj
-可以查,当然也可以自定义修改次数,退出即失效。
+ # 默认还是安装在 python2.6 下
+ $ yum install PyYAML -y
-.. code:: python
+ # 将这些文件拷贝到 python2.7 目录下
+ # 如果你不知道 python2.7 的目录,使用 import sys;print sys.path 就可以打印
+ $ cd /usr/lib64/python2.6/site-packages
+ $ cp -r yaml/ /usr/local/lib/python2.7/site-packages/
+ $ cp -p _yaml.so /usr/local/lib/python2.7/site-packages/
+ $ cp -p PyYAML-3.10-py2.6.egg-info /usr/local/lib/python2.7/site-packages/
- >>> sys.setrecursionlimit(2000)
- >>> sys.getrecursionlimit()
- 2000
+执行一下 cloudinit 的几个命令,没有问题,任务就完成了。
-22. 一行代码实现FTP服务器
--------------------------
+.. code:: shell
-搭建FTP,或者是搭建网络文件系统,这些方法都能够实现Linux的目录共享。但是FTP和网络文件系统的功能都过于强大,因此它们都有一些不够方便的地方。比如你想快速共享Linux系统的某个目录给整个项目团队,还想在一分钟内做到,怎么办?很简单,使用Python中的SimpleHTTPServer。
+ $ cloud-init init -l
+ $ cloud-init init
-SimpleHTTPServer是Python
-2自带的一个模块,是Python的Web服务器。它在Python
-3已经合并到http.server模块中。具体例子如下,如不指定端口,则默认是8000端口。
+**参考文章**
-.. code:: python
-
- # python2
- python -m SimpleHTTPServer 8888
-
- # python3
- python3 -m http.server 8888
-
-|image1|
-
-SimpleHTTPServer有一个特性,如果待共享的目录下有index.html,那么index.html文件会被视为默认主页;如果不存在index.html文件,那么就会显示整个目录列表。
-
-23. 让你晕头转向的 else 用法
-----------------------------
-
-if else
-用法可以说最基础的语法表达式之一,但是今天不是讲这个的,一定要讲点不一样的。
-
-if else 早已烂大街,但可能有很多人都不曾见过 for else 和 try else
-的用法。为什么说它曾让我晕头转向,因为它不像 if else
-那么直白,非黑即白,脑子经常要想一下才能才反应过来代码怎么走。反正我是这样的。
-
-先来说说,for else
-
-.. code:: python
-
- def check_item(source_list, target):
- for item in source_list:
- if item == target:
- print("Exists!")
- break
-
- else:
- print("Does not exist")
-
-在往下看之前,你可以思考一下,什么情况下才会走 else。是循环被
-break,还是没有break?
-
-给几个例子,你体会一下。
-
-.. code:: python
-
- check_item(["apple", "huawei", "oppo"], "oppo")
- # Exists!
-
- check_item(["apple", "huawei", "oppo"], "vivo")
- # Does not exist
-
-可以看出,没有被 break 的程序才会正常走else流程。
-
-再来看看,try else 用法。
-
-.. code:: python
-
- def test_try_else(attr1 = None):
- try:
- if attr1:
- pass
- else:
- raise
- except:
- print("Exception occurred...")
- else:
- print("No Exception occurred...")
-
-同样来几个例子。当不传参数时,就抛出异常。
-
-.. code:: python
-
- test_try_else()
- # Exception occurred...
-
- test_try_else("ming")
- # No Exception occurred...
-
-可以看出,没有 try 里面的代码块没有抛出异常的,会正常走else。
-
-总结一下,for else 和 try else 相同,只要代码正常走下去不被
-break,不抛出异常,就可以走else。
-
-24. 字符串里的缝隙是什么?
---------------------------
-
-在Python中求一个字符串里,某子字符(串)出现的次数。
-
-大家都懂得使用 count() 函数,比如下面几个常规例子:
-
-.. code:: python
-
- >>> "aabb".count("a")
- 2
- >>> "aabb".count("b")
- 2
- >>> "aabb".count("ab")
- 1
-
-但是如果我想计算空字符串的个数呢?
-
-.. code:: python
-
- >>> "aabb".count("")
- 5
-
-**奇怪了吧?**
-
-不是应该返回 0 吗?怎么会返回 5?
-
-实际上,在 Python 看来,两个字符之间都是一个空字符,通俗的说就是缝隙。
-
-因此 对于 ``aabb`` 这个字符串在 Python 来看应该是这样的
+- https://www.cnblogs.com/stonehe/p/7944366.html
|image2|
-理解了这个“**缝隙**” 的概念后,以下这些就好理解了。
-
-.. code:: python
-
- >>> (" " * 10).count("")
- 11
- >>>
- >>> "" in ""
- True
- >>>
- >>> "" in "M"
- True
-
-25. 正负得正,负负得正
-----------------------
-
-从初中开始,我们就开始接触了\ ``负数`` ,并且都知道了\ ``负负得正``
-的思想。
-
-Python 作为一门高级语言,它的编写符合人类的思维逻辑,包括 ``负负得正``
-。
-
-.. code:: python
-
- >>> 5-3
- 2
- >>> 5--3
- 8
- >>> 5+-3
- 2
- >>> 5++3
- 8
- >>> 5---3
- 2
-
-..
-
- 作者:王炳明,微信公众号《Python编程时光》。版权归个人所有,欢迎分享,仅用于学习交流,但勿用作商业用途,违者必究。
-
-26. 数值与字符串的比较
-----------------------
-
-在 Python2 中,数字可以与字符串直接比较。结果是数值永远比字符串小。
-
-.. code:: python
-
- >>> 100000000 < ""
- True
- >>> 100000000 < "hello"
- True
-
-但在 Python3 中,却不行。
-
-.. code:: python
-
- >>> 100000000 < ""
- TypeError: '<' not supported between instances of 'int' and 'str'
-
-27. 循环中的局部变量泄露
-------------------------
-
-在Python 2中 x 的值在一个循环执行之后被改变了。
-
-.. code:: python
-
- # Python2
- >>> x = 1
- >>> [x for x in range(5)]
- [0, 1, 2, 3, 4]
- >>> x
- 4
-
-不过在Python3 中这个问题已经得到解决了。
-
-.. code:: python
-
- # Python3
- >>> x = 1
- >>> [x for x in range(5)]
- [0, 1, 2, 3, 4]
- >>> x
- 1
-
-28. 字典居然是可以排序的?
---------------------------
-
-在 Python 3.6 之前字典不可排序的思想,似乎已经根深蒂固。
-
-.. code:: python
-
- # Python2.7.10
- >>> mydict = {str(i):i for i in range(5)}
- >>> mydict
- {'1': 1, '0': 0, '3': 3, '2': 2, '4': 4}
-
-假如哪一天,有人跟你说字典也可以是有序的,不要惊讶,那确实是真的
-
-在 Python3.6 +
-中字典已经是有序的,并且效率相较之前的还有所提升,具体信息你可以去查询相关资料。
-
-.. code:: python
-
- # Python3.6.7
- >>> mydict = {str(i):i for i in range(5)}
- >>> mydict
- {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4}
-
-29. 有趣但没啥用的 import 用法
-------------------------------
-
-import 是 Python 导包的方式。
-
-你知道 Python 中内置了一些很有(wu)趣(liao)的包吗?
-
-**Hello World**
-
-::
-
- >>> import __hello__
- Hello World!
-
-**Python之禅**
-
-::
-
- >>> import this
-
- The Zen of Python, by Tim Peters
-
- Beautiful is better than ugly.
- Explicit is better than implicit.
- Simple is better than complex.
- Complex is better than complicated.
- Flat is better than nested.
- Sparse is better than dense.
- Readability counts.
- Special cases aren't special enough to break the rules.
- Although practicality beats purity.
- Errors should never pass silently.
- Unless explicitly silenced.
- In the face of ambiguity, refuse the temptation to guess.
- There should be one-- and preferably only one --obvious way to do it.
- Although that way may not be obvious at first unless you're Dutch.
- Now is better than never.
- Although never is often better than *right* now.
- If the implementation is hard to explain, it's a bad idea.
- If the implementation is easy to explain, it may be a good idea.
- Namespaces are one honking great idea -- let's do more of those!
-
-**反地心引力漫画**
-
-在 cmd 窗口中导入\ ``antigravity``
-
-::
-
- >>> import antigravity
-
-就会自动打开一个网页。 |image3|
-
-30. 局部/全局变量傻傻分不清
----------------------------
-
-在开始讲之前,你可以试着运行一下下面这小段代码。
-
-.. code:: python
-
- # demo.py
- a = 1
-
- def add():
- a += 1
-
- add()
-
-看似没有毛病,但实则已经犯了一个很基础的问题,运行结果如下:
-
-.. code:: python
-
- $ python demo.py
- Traceback (most recent call last):
- File "demo.py", line 6, in
- add()
- File "demo.py", line 4, in add
- a += 1
- UnboundLocalError: local variable 'a' referenced before assignment
-
-回顾一下,什么是局部变量?在非全局下定义声明的变量都是局部变量。
-
-当程序运行到 ``a += 1`` 时,Python 解释器就认为在函数内部要给 ``a``
-这个变量赋值,当然就把 ``a`` 当做局部变量了,但是做为局部变量的 a
-还没有被还没被定义。
-
-因此报错是正常的。
-
-理解了上面的例子,给你留个思考题。为什么下面的代码不会报错呢?
-
-.. code:: python
-
- $ cat demo.py
- a = 1
-
- def output():
- print(a)
-
- output()
-
- $ python demo.py
- 1
-
-..
-
- 作者:王炳明,微信公众号《Python编程时光》。版权归个人所有,欢迎分享,仅用于学习交流,但勿用作商业用途,违者必究。
-
-31. 字母也玩起了障眼法
-----------------------
-
-以下我分别在 Python2.7 和 Python 3.7 的 console 模式下,运行了如下代码。
-
-**在Python 2.x 中**
-
-::
-
- >>> valuе = 32
- File "", line 1
- valuе = 32
- ^
- SyntaxError: invalid syntax
-
-**在Python 3.x 中**
-
-::
-
- >>> valuе = 32
- >>> value
- 11
-
-什么?没有截图你不信?
-
-|image4|
-
-如果你在自己的电脑上尝试一下,结果可能是这样的
-
-|image5|
-
-**怎么又好了呢?**
-
-如果你想复现的话,请复制我这边给出的代码:\ ``valuе = 32``
-
-**这是为什么呢?**
-
-原因在于,我上面使用的 value 变量名里的 ``е`` 又不是我们熟悉的
-``e``\ ,它是 Cyrillic(西里尔)字母。
-
-::
-
- >>> ord('е') # cyrillic 'e' (Ye)
- 1077
- >>> ord('e') # latin 'e', as used in English and typed using standard keyboard
- 101
- >>> 'е' == 'e'
- False
-
-细思恐极,在这里可千万不要得罪同事们,万一离职的时候,对方把你项目里的
-``e`` 全局替换成 ``e``\ ,到时候你就哭去吧,肉眼根本看不出来嘛。
-
-32. 字符串的分割技巧
---------------------
-
-当我们对字符串进行分割时,且分割符是
-``\n``\ ,有可能会出现这样一个窘境:
-
-.. code:: python
-
- >>> str = "a\nb\n"
- >>> print(str)
- a
- b
-
- >>> str.split('\n')
- ['a', 'b', '']
- >>>
-
-会在最后一行多出一个元素,为了应对这种情况,你可以会多加一步处理。
-
-但我想说的是,完成没有必要,对于这个场景,你可以使用 ``splitlines``
-
-.. code:: python
-
- >>> str.splitlines()
- ['a', 'b']
-
-33. 嵌套上下文管理的另类写法
-----------------------------
-
-当我们要写一个嵌套的上下文管理器时,可能会这样写
-
-.. code:: python
-
- import contextlib
-
- @contextlib.contextmanager
- def test_context(name):
- print('enter, my name is {}'.format(name))
-
- yield
-
- print('exit, my name is {}'.format(name))
-
- with test_context('aaa'):
- with test_context('bbb'):
- print('========== in main ============')
-
-输出结果如下
-
-.. code:: python
-
- enter, my name is aaa
- enter, my name is bbb
- ========== in main ============
- exit, my name is bbb
- exit, my name is aaa
-
-除此之外,你可知道,还有另一种嵌套写法
-
-.. code:: python
-
- with test_context('aaa'), test_context('bbb'):
- print('========== in main ============')
-
-34. += 不等同于=+
------------------
-
-对列表 进行\ ``+=`` 操作相当于 extend,而使用 ``=+``
-操作是新增了一个列表。
-
-因此会有如下两者的差异。
-
-.. code:: python
-
- # =+
- >>> a = [1, 2, 3, 4]
- >>> b = a
- >>> a = a + [5, 6, 7, 8]
- >>> a
- [1, 2, 3, 4, 5, 6, 7, 8]
- >>> b
- [1, 2, 3, 4]
-
-
- # +=
- >>> a = [1, 2, 3, 4]
- >>> b = a
- >>> a += [5, 6, 7, 8]
- >>> a
- [1, 2, 3, 4, 5, 6, 7, 8]
- >>> b
- [1, 2, 3, 4, 5, 6, 7, 8]
-
-35. 增量赋值的性能更好
-----------------------
-
-诸如 ``+=`` 和 ``*=`` 这些运算符,叫做 增量赋值运算符。
-
-这里使用用 += 举例,以下两种写法,在效果上是等价的。
-
-::
-
- # 第一种
- a = 1 ; a += 1
-
- # 第二种
- a = 1; a = a + 1
-
-``+=`` 其背后使用的魔法方法是
-\__iadd__,如果没有实现这个方法则会退而求其次,使用 \__add_\_ 。
-
-这两种写法有什么区别呢?
-
-用列表举例 a += b,使用 \__add_\_ 的话就像是使用了a.extend(b),如果使用
-\__add_\_ 的话,则是 a =
-a+b,前者是直接在原列表上进行扩展,而后者是先从原列表中取出值,在一个新的列表中进行扩展,然后再将新的列表对象返回给变量,显然后者的消耗要大些。
-
-所以在能使用增量赋值的时候尽量使用它。
-
- 作者:王炳明,微信公众号《Python编程时光》。版权归个人所有,欢迎分享,仅用于学习交流,但勿用作商业用途,违者必究。
-
-36. x == +x 吗?
-----------------
-
-在大多数情况下,这个等式是成立的。
-
-::
-
- >>> n1 = 10086
- >>> n2 = +n1
- >>>
- >>> n1 == n2
- True
-
-什么情况下,这个等式会不成立呢?
-
-由于Counter的机制,\ ``+`` 用于两个 Counter
-实例相加,而相加的结果如果元素的个数 ``<=`` 0,就会被丢弃。
-
-::
-
- >>> from collections import Counter
- >>> ct = Counter('abcdbcaa')
- >>> ct
- Counter({'a': 3, 'b': 2, 'c': 2, 'd': 1})
- >>> ct['c'] = 0
- >>> ct['d'] = -2
- >>>
- >>> ct
- Counter({'a': 3, 'b': 2, 'c': 0, 'd': -2})
- >>>
- >>> +ct
- Counter({'a': 3, 'b': 2})
-
-37. 如何将 print 内容输出到文件
--------------------------------
-
-Python 3 中的 print
-作为一个函数,由于可以接收更多的参数,所以功能变为更加强大。
-
-比如今天要说的使用 print
-将你要打印的内容,输出到日志文件中(但是我并不推荐使用它)。
-
-.. code:: python
-
- >>> with open('test.log', mode='w') as f:
- ... print('hello, python', file=f, flush=True)
- >>> exit()
-
- $ cat test.log
- hello, python
-
-38. site-packages和 dist-packages
----------------------------------
-
-如果你足够细心,你会在你的机器上,有些包是安装在 **site-packages**
-下,而有些包安装在 **dist-packages** 下。
-
-**它们有什么区别呢?**
-
-一般情况下,你只见过 site-packages 这个目录,而你所安装的包也将安装在
-这个目录下。
-
-而 dist-packages 其实是 debian 系的 Linux 系统(如
-Ubuntu)才特有的目录,当你使用 apt 去安装的 Python 包会使用
-dist-packages,而你使用 pip 或者 easy_install 安装的包还是照常安装在
-site-packages 下。
-
-Debian 这么设计的原因,是为了减少不同来源的 Python 之间产生的冲突。
-
-如何查找 Python 安装目录
-
-.. code:: python
-
- >>> from distutils.sysconfig import get_python_lib
- >>> print(get_python_lib())
- /usr/lib/python2.7/site-packages
-
-39. argument 和 parameter 的区别
---------------------------------
-
-arguments 和 parameter
-的翻译都是参数,在中文场景下,二者混用基本没有问题,毕竟都叫参数嘛。
-
-但若要严格再进行区分,它们实际上还有各自的叫法
-
-- parameter:形参(\ **formal
- parameter**\ ),体现在函数内部,作用域是这个函数体。
-- argument :实参(\ **actual parameter**\ ),调用函数实际传递的参数。
-
-举个例子,如下这段代码,\ ``"error"`` 为 argument,而 msg 为
-``parameter``\ 。
-
-.. code:: python
-
- def output_msg(msg):
- print(msg)
-
- output_msg("error")
-
-40. 简洁而优雅的链式比较
-------------------------
-
-先给你看一个示例:
-
-.. code:: python
-
- >>> False == False == True
- False
-
-你知道这个表达式为什么会会返回 False 吗?
-
-它的运行原理与下面这个类似,是不是有点头绪了:
-
-.. code:: python
-
- if 80 < score <= 90:
- print("成绩良好")
-
-如果你还是不明白,那我再给你整个第一个例子的等价写法。
-
-.. code:: python
-
- >>> False == False and False == True
- False
-
-这个用法叫做链式比较。
-
- 作者:王炳明,微信公众号《Python编程时光》。版权归个人所有,欢迎分享,仅用于学习交流,但勿用作商业用途,违者必究。
-
-41. 连接多个列表最极客的方式
-----------------------------
-
-.. code:: python
-
- >>> a = [1,2]
- >>> b = [3,4]
- >>> c = [5,6]
- >>>
- >>> sum((a,b,c), [])
- [1, 2, 3, 4, 5, 6]
-
-42. 另外 8 种连接列表的方式
----------------------------
-
-**1. 最直观的相加**
-
-使用 ``+`` 对多个列表进行相加,你应该懂,不多说了。
-
-.. code:: python
-
- >>> list01 = [1,2,3]
- >>> list02 = [4,5,6]
- >>> list03 = [7,8,9]
- >>>
- >>> list01 + list02 + list03
- [1, 2, 3, 4, 5, 6, 7, 8, 9]
- >>>
-
-**2. 借助 itertools**
-
-itertools 在 Python
-里有一个非常强大的内置模块,它专门用于操作可迭代对象。
-
-在前面的文章中也介绍过,使用 ``itertools.chain()``
-函数先可迭代对象(在这里指的是列表)串联起来,组成一个更大的可迭代对象。
-
-最后你再利用 list 将其转化为 列表。
-
-.. code:: python
-
- >>> from itertools import chain
- >>> list01 = [1,2,3]
- >>> list02 = [4,5,6]
- >>> list03 = [7,8,9]
- >>>
- >>> list(chain(list01, list02, list03))
- [1, 2, 3, 4, 5, 6, 7, 8, 9]
- >>>
-
-**3. 使用 \* 解包**
-
-使用 ``*`` 可以解包列表,解包后再合并。
-
-示例如下:
-
-.. code:: python
-
- >>> list01 = [1,2,3]
- >>> list02 = [4,5,6]
- >>>
- >>> [*list01, *list02]
- [1, 2, 3, 4, 5, 6]
- >>>
-
-**4. 使用 extend**
-
-在字典中,使用 update 可实现原地更新,而在列表中,使用 extend
-可实现列表的自我扩展。
-
-.. code:: python
-
- >>> list01 = [1,2,3]
- >>> list02 = [4,5,6]
- >>>
- >>> list01.extend(list02)
- >>> list01
- [1, 2, 3, 4, 5, 6]
-
-**5. 使用列表推导式**
-
-Python 里对于生成列表、集合、字典,有一套非常 Pythonnic 的写法。
-
-那就是列表解析式,集合解析式和字典解析式,通常是 Python
-发烧友的最爱,那么今天的主题:列表合并,列表推导式还能否胜任呢?
-
-当然可以,具体示例代码如下:
-
-.. code:: python
-
- >>> list01 = [1,2,3]
- >>> list02 = [4,5,6]
- >>> list03 = [7,8,9]
- >>>
- >>> [x for l in (list01, list02, list03) for x in l]
- [1, 2, 3, 4, 5, 6, 7, 8, 9]
- >>>
-
-**6. 使用 heapq**
-
-heapq 是 Python 的一个标准模块,它提供了堆排序算法的实现。
-
-该模块里有一个 merge 方法,可以用于合并多个列表,如下所示
-
-.. code:: python
-
- >>> list01 = [1,2,3]
- >>> list02 = [4,5,6]
- >>> list03 = [7,8,9]
- >>>
- >>> from heapq import merge
- >>>
- >>> list(merge(list01, list02, list03))
- [1, 2, 3, 4, 5, 6, 7, 8, 9]
- >>>
-
-要注意的是,heapq.merge
-除了合并多个列表外,它还会将合并后的最终的列表进行排序。
-
-.. code:: python
-
- >>> list01 = [2,5,3]
- >>> list02 = [1,4,6]
- >>> list03 = [7,9,8]
- >>>
- >>> from heapq import merge
- >>>
- >>> list(merge(list01, list02, list03))
- [1, 2, 4, 5, 3, 6, 7, 9, 8]
- >>>
-
-它的效果等价于下面这行代码:
-
-.. code:: python
-
- sorted(itertools.chain(*iterables))
-
-如果你希望得到一个始终有序的列表,那请第一时间想到
-heapq.merge,因为它采用堆排序,效率非常高。但若你不希望得到一个排过序的列表,就不要使用它了。
-
-**7. 借助魔法方法**
-
-有一个魔法方法叫 ``__add__``\ ,当我们使用第一种方法 list01 + list02
-的时候,内部实际上是作用在 ``__add__`` 这个魔法方法上的。
-
-所以以下两种方法其实是等价的
-
-.. code:: python
-
- >>> list01 = [1,2,3]
- >>> list02 = [4,5,6]
- >>>
- >>> list01 + list02
- [1, 2, 3, 4, 5, 6]
- >>>
- >>>
- >>> list01.__add__(list02)
- [1, 2, 3, 4, 5, 6]
- >>>
-
-借用这个魔法特性,我们可以 reduce
-这个方法来对多个列表进行合并,示例代码如下
-
-.. code:: python
-
- >>> list01 = [1,2,3]
- >>> list02 = [4,5,6]
- >>> list03 = [7,8,9]
- >>>
- >>> from functools import reduce
- >>> reduce(list.__add__, (list01, list02, list03))
- [1, 2, 3, 4, 5, 6, 7, 8, 9]
- >>>
-
-**8. 使用 yield from**
-
-在 yield from 后可接一个可迭代对象,用于迭代并返回其中的每一个元素。
-
-因此,我们可以像下面这样自定义一个合并列表的工具函数。
-
-.. code:: python
-
- >>> list01 = [1,2,3]
- >>> list02 = [4,5,6]
- >>> list03 = [7,8,9]
- >>>
- >>> def merge(*lists):
- ... for l in lists:
- ... yield from l
- ...
- >>> list(merge(list01, list02, list03))
- [1, 2, 3, 4, 5, 6, 7, 8, 9]
- >>>
-
-43. 在程序退出前执行代码的技巧
-------------------------------
-
-使用 atexit 这个内置模块,可以很方便的注册退出函数。
-
-不管你在哪个地方导致程序崩溃,都会执行那些你注册过的函数。
-
-示例如下
-
-|image6|
-
-如果\ ``clean()``\ 函数有参数,那么你可以不用装饰器,而是直接调用\ ``atexit.register(clean_1, 参数1, 参数2, 参数3='xxx')``\ 。
-
-可能你有其他方法可以处理这种需求,但肯定比上不使用 atexit
-来得优雅,来得方便,并且它很容易扩展。
-
-但是使用 atexit 仍然有一些局限性,比如:
-
-- 如果程序是被你没有处理过的系统信号杀死的,那么注册的函数无法正常执行。
-- 如果发生了严重的 Python 内部错误,你注册的函数无法正常执行。
-- 如果你手动调用了\ ``os._exit()``\ ,你注册的函数无法正常执行。
-
-44. 合并字典的 8 种方法
------------------------
-
-**1. 最简单的原地更新**
-
-字典对象内置了一个 update 方法,用于把另一个字典更新到自己身上。
-
-.. code:: python
-
- >>> profile = {"name": "xiaoming", "age": 27}
- >>> ext_info = {"gender": "male"}
- >>>
- >>> profile.update(ext_info)
- >>> print(profile)
- {'name': 'xiaoming', 'age': 27, 'gender': 'male'}
-
-如果想使用 update
-这种最简单、最地道原生的方法,但又不想更新到自己身上,而是生成一个新的对象,那请使用深拷贝。
-
-.. code:: python
-
- >>> profile = {"name": "xiaoming", "age": 27}
- >>> ext_info = {"gender": "male"}
- >>>
- >>> from copy import deepcopy
- >>>
- >>> full_profile = deepcopy(profile)
- >>> full_profile.update(ext_info)
- >>>
- >>> print(full_profile)
- {'name': 'xiaoming', 'age': 27, 'gender': 'male'}
- >>> print(profile)
- {"name": "xiaoming", "age": 27}
-
-**2. 先解包再合并字典**
-
-使用 ``**`` 可以解包字典,解包完后再使用 dict 或者 ``{}`` 就可以合并。
-
-.. code:: python
-
- >>> profile = {"name": "xiaoming", "age": 27}
- >>> ext_info = {"gender": "male"}
- >>>
- >>> full_profile01 = {**profile, **ext_info}
- >>> print(full_profile01)
- {'name': 'xiaoming', 'age': 27, 'gender': 'male'}
- >>>
- >>> full_profile02 = dict(**profile, **ext_info)
- >>> print(full_profile02)
- {'name': 'xiaoming', 'age': 27, 'gender': 'male'}
-
-若你不知道 ``dict(**profile, **ext_info)`` 做了啥,你可以将它等价于
-
-.. code:: python
-
- >>> dict((("name", "xiaoming"), ("age", 27), ("gender", "male")))
- {'name': 'xiaoming', 'age': 27, 'gender': 'male'}
-
-**3. 借助 itertools**
-
-在 Python 里有一个非常强大的内置模块,它专门用于操作可迭代对象。
-
-正好我们字典也是可迭代对象,自然就可以想到,可以使用
-``itertools.chain()``
-函数先将多个字典(可迭代对象)串联起来,组成一个更大的可迭代对象,然后再使用
-dict 转成字典。
-
-.. code:: python
-
- >>> import itertools
- >>>
- >>> profile = {"name": "xiaoming", "age": 27}
- >>> ext_info = {"gender": "male"}
- >>>
- >>>
- >>> dict(itertools.chain(profile.items(), ext_info.items()))
- {'name': 'xiaoming', 'age': 27, 'gender': 'male'}
-
-**4. 借助 ChainMap**
-
-如果可以引入一个辅助包,那我就再提一个, ``ChainMap`` 也可以达到和
-``itertools`` 同样的效果。
-
-.. code:: python
-
- >>> from collections import ChainMap
- >>>
- >>> profile = {"name": "xiaoming", "age": 27}
- >>> ext_info = {"gender": "male"}
- >>>
- >>> dict(ChainMap(profile, ext_info))
- {'name': 'xiaoming', 'age': 27, 'gender': 'male'}
-
-使用 ChainMap
-有一点需要注意,当字典间有重复的键时,只会取第一个值,排在后面的键值并不会更新掉前面的(使用
-itertools 就不会有这个问题)。
-
-.. code:: python
-
- >>> from collections import ChainMap
- >>>
- >>> profile = {"name": "xiaoming", "age": 27}
- >>> ext_info={"age": 30}
- >>> dict(ChainMap(profile, ext_info))
- {'name': 'xiaoming', 'age': 27}
-
-**5. 使用dict.items() 合并**
-
-在 Python 3.9 之前,其实就已经有 ``|``
-操作符了,只不过它通常用于对集合(set)取并集。
-
-利用这一点,也可以将它用于字典的合并,只不过得绕个弯子,有点不好理解。
-
-你得先利用 ``items`` 方法将 dict 转成 dict_items,再对这两个 dict_items
-取并集,最后利用 dict 函数,转成字典。
-
-.. code:: python
-
- >>> profile = {"name": "xiaoming", "age": 27}
- >>> ext_info = {"gender": "male"}
- >>>
- >>> full_profile = dict(profile.items() | ext_info.items())
- >>> full_profile
- {'gender': 'male', 'age': 27, 'name': 'xiaoming'}
-
-当然了,你如果嫌这样太麻烦,也可以简单点,直接使用 list
-函数再合并(示例为 Python 3.x )
-
-.. code:: python
-
- >>> profile = {"name": "xiaoming", "age": 27}
- >>> ext_info = {"gender": "male"}
- >>>
- >>> dict(list(profile.items()) + list(ext_info.items()))
- {'name': 'xiaoming', 'age': 27, 'gender': 'male'}
-
-若你在 Python 2.x 下,可以直接省去 list 函数。
-
-.. code:: python
-
- >>> profile = {"name": "xiaoming", "age": 27}
- >>> ext_info = {"gender": "male"}
- >>>
- >>> dict(profile.items() + ext_info.items())
- {'name': 'xiaoming', 'age': 27, 'gender': 'male'}
-
-**6. 最酷炫的字典解析式**
-
-Python 里对于生成列表、集合、字典,有一套非常 Pythonnic 的写法。
-
-那就是列表解析式,集合解析式和字典解析式,通常是 Python
-发烧友的最爱,那么今天的主题:字典合并,字典解析式还能否胜任呢?
-
-当然可以,具体示例代码如下:
-
-.. code:: python
-
- >>> profile = {"name": "xiaoming", "age": 27}
- >>> ext_info = {"gender": "male"}
- >>>
- >>> {k:v for d in [profile, ext_info] for k,v in d.items()}
- {'name': 'xiaoming', 'age': 27, 'gender': 'male'}
-
-**7. Python 3.9 新特性**
-
-在 2 月份发布的 Python 3.9.04a
-版本中,新增了一个抓眼球的新操作符操作符: ``|``\ , PEP584
-将它称之为合并操作符(Union Operator),用它可以很直观地合并多个字典。
-
-.. code:: python
-
- >>> profile = {"name": "xiaoming", "age": 27}
- >>> ext_info = {"gender": "male"}
- >>>
- >>> profile | ext_info
- {'name': 'xiaoming', 'age': 27, 'gender': 'male'}
- >>>
- >>> ext_info | profile
- {'gender': 'male', 'name': 'xiaoming', 'age': 27}
- >>>
- >>>
-
-除了 ``|`` 操作符之外,还有另外一个操作符 ``|=``\ ,类似于原地更新。
-
-.. code:: python
-
- >>> ext_info |= profile
- >>> ext_info
- {'gender': 'male', 'name': 'xiaoming', 'age': 27}
- >>>
- >>>
- >>> profile |= ext_info
- >>> profile
- {'name': 'xiaoming', 'age': 27, 'gender': 'male'}
-
-看到这里,有没有涨姿势了,学了这么久的 Python
-,没想到合并字典还有这么多的方法。本篇文章的主旨,并不在于让你全部掌握这
-7
-种合并字典的方法,实际在工作中,你只要选用一种最顺手的方式即可,但是在协同工作中,或者在阅读他人代码时,你不可避免地会碰到各式各样的写法,这时候你能下意识的知道这是在做合并字典的操作,那这篇文章就是有意义的。
-
- 作者:王炳明,微信公众号《Python编程时光》。版权归个人所有,欢迎分享,仅用于学习交流,但勿用作商业用途,违者必究。
-
-45. 条件语句的七种写法
-----------------------
-
-**第一种:原代码**
-
-这是一段非常简单的通过年龄判断一个人是否成年的代码,由于代码行数过多,有些人就不太愿意这样写,因为这体现不出自己多年的
-Python 功力。
-
-.. code:: python
-
- if age > 18:
- return "已成年"
- else:
- return "未成年"
-
-下面我列举了六种这段代码的变异写法,一个比一个还 6
-,单独拿出来比较好理解,放在工程代码里,没用过这些学法的人,一定会看得一脸懵逼,理解了之后,又不经意大呼:\ **卧槽,还可以这样写?**\ ,而后就要开始骂街了:\ **这是给人看的代码?**
-(除了第一种之外)
-
-**第二种**
-
-语法:
-
-.. code:: python
-
- if else
-
-例子
-
-.. code:: python
-
- >>> age1 = 20
- >>> age2 = 17
- >>>
- >>>
- >>> msg1 = "已成年" if age1 > 18 else "未成年"
- >>> print msg1
- 已成年
- >>>
- >>> msg2 = "已成年" if age2 > 18 else "未成年"
- >>> print msg2
- 未成年
- >>>
-
-**第三种**
-
-语法
-
-.. code:: python
-
- and or
-
-例子
-
-.. code:: python
-
- >>> msg1 = age1 > 18 and "已成年" or "未成年"
- >>> msg2 = "已成年" if age2 > 18 else "未成年"
- >>>
- >>> print(msg1)
- 已成年
- >>>
- >>> print(msg2)
- 未成年
-
-**第四种**
-
-语法
-
-.. code:: python
-
- (, )[condition]
-
-例子
-
-.. code:: python
-
- >>> msg1 = ("未成年", "已成年")[age1 > 18]
- >>> print(msg1)
- 已成年
- >>>
- >>>
- >>> msg2 = ("未成年", "已成年")[age2 > 18]
- >>> print(msg2)
- 未成年
-
-**第五种**
-
-语法
-
-.. code:: python
-
- (lambda: , lambda:)[]()
-
-例子
-
-.. code:: python
-
- >>> msg1 = (lambda:"未成年", lambda:"已成年")[age1 > 18]()
- >>> print(msg1)
- 已成年
- >>>
- >>> msg2 = (lambda:"未成年", lambda:"已成年")[age2 > 18]()
- >>> print(msg2)
- 未成年
-
-**第六种**
-
-语法:
-
-.. code:: python
-
- {True: , False: }[]
-
-例子:
-
-.. code:: python
-
- >>> msg1 = {True: "已成年", False: "未成年"}[age1 > 18]
- >>> print(msg1)
- 已成年
- >>>
- >>> msg2 = {True: "已成年", False: "未成年"}[age2 > 18]
- >>> print(msg2)
- 未成年
-
-**第七种**
-
-语法
-
-.. code:: python
-
- (() and (,) or (,))[0]
-
-例子
-
-.. code:: python
-
- >>> msg1 = ((age1 > 18) and ("已成年",) or ("未成年",))[0]
- >>> print(msg1)
- 已成年
- >>>
- >>> msg2 = ((age2 > 18) and ("已成年",) or ("未成年",))[0]
- >>> print(msg2)
- 未成年
-
-以上代码,都比较简单,仔细看都能看懂,我就不做解释了。
-
-看到这里,有没有涨姿势了,学了这么久的 Python
-,这么多骚操作,还真是活久见。。这六种写法里,我最推荐使用的是第一种,自己也经常在用,简洁直白,代码行还少。而其他的写法虽然能写,但是不会用,也不希望在我余生里碰到会在公共代码里用这些写法的同事。
-
- 作者:王炳明,微信公众号《Python编程时光》。版权归个人所有,欢迎分享,仅用于学习交流,但勿用作商业用途,违者必究。
-
-46. /usr/bin/env python 有什么用?
-----------------------------------
-
-我们经常会在别人的脚本或者项目的入口文件里看到第一行是下面这样
-
-.. code:: shell
-
- #!/usr/bin/python
-
-或者这样
-
-``sh e llsh el #!/usr/bin/env python``
-
-这两者有什么区别呢?
-
-稍微接触过 linux 的人都知道 ``/usr/bin/python`` 就是我们执行 ``python``
-进入console 模式里的 ``python``
-
-|image7|
-
-而当你在可执行文件头里使用 ``#!`` + ``/usr/bin/python``
-,意思就是说你得用哪个软件 (python)来执行这个文件。
-
-那么加和不加有什么区别呢?
-
-不加的话,你每次执行这个脚本时,都得这样: ``python xx.py`` ,
-
-|image8|
-
-有没有一种方式?可以省去每次都加 ``python`` 呢?
-
-当然有,你可以文件头里加上\ ``#!/usr/bin/python``
-,那么当这个文件有可执行权限 时,只直接写这个脚本文件,就像下面这样。
-
-|image9|
-
-明白了这个后,再来看看 ``!/usr/bin/env python`` 这个 又是什么意思 ?
-
-当我执行 ``env python`` 时,自动进入了 python console 的模式。
-
-|image10|
-
-这是为什么?和 直接执行 python 好像没什么区别呀
-
-当你执行 ``env python`` 时,它其实会去 ``env | grep PATH`` 里(也就是
-/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin
-)这几个路径里去依次查找名为python的可执行文件。
-
-找到一个就直接执行,上面我们的 python 路径是在 ``/usr/bin/python``
-里,在 ``PATH`` 列表里倒数第二个目录下,所以当我在 ``/usr/local/sbin``
-下创建一个名字也为 python 的可执行文件时,就会执行 ``/usr/bin/python``
-了。
-
-具体演示过程,你可以看下面。
-
-|image11|
-
-那么对于这两者,我们应该使用哪个呢?
-
-个人感觉应该优先使用 ``#!/usr/bin/env python``\ ,因为不是所有的机器的
-python 解释器都是 ``/usr/bin/python`` 。
-
-47. 让我爱不释手的用户环境
---------------------------
-
-当你在机器上并没有 root 权限时,如何安装 Python 的第三方包呢?
-
-可以使用 ``pip install --user pkg``
-将你的包安装在你的用户环境中,该用户环境与全局环境并不冲突,并且多用户之间相互隔离,互不影响。
-
-.. code:: shell
-
- # 在全局环境中未安装 requests
- [root@localhost ~]$ pip list | grep requests
- [root@localhost ~]$ su - wangbm
-
- # 由于用户环境继承自全局环境,这里也未安装
- [wangbm@localhost ~]$ pip list | grep requests
- [wangbm@localhost ~]$ pip install --user requests
- [wangbm@localhost ~]$ pip list | grep requests
- requests (2.22.0)
- [wangbm@localhost ~]$
-
- # 从 Location 属性可发现 requests 只安装在当前用户环境中
- [wangbm@localhost ~]$ pip show requests
- ---
- Metadata-Version: 2.1
- Name: requests
- Version: 2.22.0
- Summary: Python HTTP for Humans.
- Home-page: http://python-requests.org
- Author: Kenneth Reitz
- Author-email: me@kennethreitz.org
- Installer: pip
- License: Apache 2.0
- Location: /home/wangbm/.local/lib/python2.7/site-packages
- [wangbm@localhost ~]$ exit
- logout
-
- # 退出 wangbm 用户,在 root 用户环境中发现 requests 未安装
- [root@localhost ~]$ pip list | grep requests
- [root@localhost ~]$
-
-48. 实现类似 defer 的延迟调用
------------------------------
-
-在 Golang 中有一种延迟调用的机制,关键字是 defer,例如下面的示例
-
-.. code:: go
-
- import "fmt"
-
- func myfunc() {
- fmt.Println("B")
- }
-
- func main() {
- defer myfunc()
- fmt.Println("A")
- }
-
-输出如下,myfunc 的调用会在函数返回前一步完成,即使你将 myfunc
-的调用写在函数的第一行,这就是延迟调用。
-
-::
-
- A
- B
-
-那么在 Python 中否有这种机制呢?
-
-当然也有,只不过并没有 Golang 这种简便。
-
-在 Python 可以使用 **上下文管理器** 达到这种效果
-
-.. code:: python
-
- import contextlib
-
- def callback():
- print('B')
-
- with contextlib.ExitStack() as stack:
- stack.callback(callback)
- print('A')
-
-输出如下
-
-::
-
- A
- B
-
-49. 自带的缓存机制不用白不用
-----------------------------
-
-缓存是一种将定量数据加以保存,以备迎合后续获取需求的处理方式,旨在加快数据获取的速度。
-
-数据的生成过程可能需要经过计算,规整,远程获取等操作,如果是同一份数据需要多次使用,每次都重新生成会大大浪费时间。所以,如果将计算或者远程请求等操作获得的数据缓存下来,会加快后续的数据获取需求。
-
-为了实现这个需求,Python 3.2 +
-中给我们提供了一个机制,可以很方便的实现,而不需要你去写这样的逻辑代码。
-
-这个机制实现于 functool 模块中的 lru_cache 装饰器。
-
-.. code:: python
-
- @functools.lru_cache(maxsize=None, typed=False)
-
-参数解读:
-
-- maxsize:最多可以缓存多少个此函数的调用结果,如果为None,则无限制,设置为
- 2 的幂时,性能最佳
-- typed:若为 True,则不同参数类型的调用将分别缓存。
-
-举个例子
-
-.. code:: python
-
- from functools import lru_cache
-
- @lru_cache(None)
- def add(x, y):
- print("calculating: %s + %s" % (x, y))
- return x + y
-
- print(add(1, 2))
- print(add(1, 2))
- print(add(2, 3))
-
-输出如下,可以看到第二次调用并没有真正的执行函数体,而是直接返回缓存里的结果
-
-.. code:: shell
-
- calculating: 1 + 2
- 3
- 3
- calculating: 2 + 3
- 5
-
-50. 重定向标准输出到日志
-------------------------
-
-假设你有一个脚本,会执行一些任务,比如说集群健康情况的检查。
-
-检查完成后,会把各服务的的健康状况以 JSON 字符串的形式打印到标准输出。
-
-如果代码有问题,导致异常处理不足,最终检查失败,是很有可能将一些错误异常栈输出到标准错误或标准输出上。
-
-由于最初约定的脚本返回方式是以 JSON
-的格式输出,此时你的脚本却输出各种错误异常,异常调用方也无法解析。
-
-如何避免这种情况的发生呢?
-
-我们可以这样做,把你的标准错误输出到日志文件中。
-
-.. code:: python
-
- import contextlib
-
- log_file="/var/log/you.log"
-
- def you_task():
- pass
-
- @contextlib.contextmanager
- def close_stdout():
- raw_stdout = sys.stdout
- file = open(log_file, 'a+')
- sys.stdout = file
-
- yield
-
- sys.stdout = raw_stdout
- file.close()
-
- with close_stdout():
- you_task()
-
---------------
-
-.. figure:: http://image.python-online.cn/image-20200320125724880.png
- :alt: 关注公众号,获取最新干货!
-
- 关注公众号,获取最新干货!
-
-.. |image0| image:: http://image.python-online.cn/20190511165650.png
-.. |image1| image:: http://image.python-online.cn/20190511165716.png
-.. |image2| image:: http://image.iswbm.com/20200509172331.png
-.. |image3| image:: http://image.python-online.cn/20190511165735.png
-.. |image4| image:: http://image.iswbm.com/20200509122954.png
-.. |image5| image:: http://image.iswbm.com/20200509123107.png
-.. |image6| image:: http://image.iswbm.com/20200510112133.png
-.. |image7| image:: http://image.python-online.cn/20200331184021.png
-.. |image8| image:: http://image.python-online.cn/20200331185034.png
-.. |image9| image:: http://image.python-online.cn/20200331184755.png
-.. |image10| image:: http://image.python-online.cn/20200331185741.png
-.. |image11| image:: http://image.python-online.cn/20200331190224.png
+.. |image0| image:: http://image.iswbm.com/20200602135014.png
+.. |image1| image:: http://image.iswbm.com/20190831160317.png
+.. |image2| image:: http://image.iswbm.com/20200607174235.png
diff --git a/source/c01/c01_11.md b/source/c01/c01_11.md
index 57d427d..9d06f86 100644
--- a/source/c01/c01_11.md
+++ b/source/c01/c01_11.md
@@ -1,8 +1,10 @@
# 1.11 正则表达式必知必会
+
+
---
-## 一、正则表达式先导
+## 1. 正则表达式先导
### 1.1 正则基础知识
@@ -70,7 +72,7 @@ re_match.group(2)
`{n,m}?` :重复n到m次,但尽可能少重复
`{n,}?`: 重复n次以上,但尽可能少重复
-## 二、Python中的正则
+## 2. Python中的正则
在Python中,自带了re模块,这是专门用来做正则表达式的匹配的。
@@ -174,7 +176,7 @@ match.span()
(0, 3)
```
-## 三、检验表达式
+## 3. 检验表达式
### 3.1 校验数字
```
1. 数字:^[0-9]*$
@@ -322,4 +324,4 @@ match.span()
---
-
+
diff --git a/source/c01/c01_11.rst b/source/c01/c01_11.rst
old mode 100755
new mode 100644
index ca3cb4c..7113dfd
--- a/source/c01/c01_11.rst
+++ b/source/c01/c01_11.rst
@@ -1,10 +1,12 @@
1.11 正则表达式必知必会
=======================
+|image0|
+
--------------
-一、正则表达式先导
-------------------
+1. 正则表达式先导
+-----------------
1.1 正则基础知识
~~~~~~~~~~~~~~~~
@@ -75,8 +77,8 @@
| ``{n,m}?`` :重复n到m次,但尽可能少重复
| ``{n,}?``\ : 重复n次以上,但尽可能少重复
-二、Python中的正则
-------------------
+2. Python中的正则
+-----------------
在Python中,自带了re模块,这是专门用来做正则表达式的匹配的。
@@ -188,8 +190,8 @@ start(),end(),end()
match.span()
(0, 3)
-三、检验表达式
---------------
+3. 检验表达式
+-------------
3.1 校验数字
~~~~~~~~~~~~
@@ -344,7 +346,8 @@ start(),end(),end()
--------------
-.. figure:: http://image.python-online.cn/image-20200320125724880.png
- :alt: 关注公众号,获取最新干货!
+|image1|
+
+.. |image0| image:: http://image.iswbm.com/20200602135014.png
+.. |image1| image:: http://image.iswbm.com/20200607174235.png
- 关注公众号,获取最新干货!
diff --git a/source/c01/c01_12.md b/source/c01/c01_12.md
index 0ed9f6f..9d0c0af 100644
--- a/source/c01/c01_12.md
+++ b/source/c01/c01_12.md
@@ -1,10 +1,12 @@
# 1.12 搞懂字符编码的前世今生
+
+
---
初学计算机的人,肯定对众多字符编码感到头疼。为什么会那么多字符串编码? 这些内容是在去年整理的,现在重新整理下,发布在博客,搞懂字符串编码,这一篇文章足矣
-## 1.12.1 前言必知
+## 1. 前言必知
初学计算机的人,肯定对众多字符编码感到头疼。为什么会那么多字符串编码?
@@ -13,7 +15,7 @@
bit,位,一个bit可以表示两个数字,0和1
byte,字节,一个byte由8个bit表示,一个byte表示的数字区间[0,255]
-## 1.12.2 ASCII编码
+## 2. ASCII编码
### 原始ASCII编码
@@ -29,7 +31,7 @@ byte,字节,一个byte由8个bit表示,一个byte表示的数字区间[0,2

-## 1.12.3 ANSI标准
+## 3. ANSI标准
>ANSI:美国国家标准学会(AMERICAN NATIONAL STANDARDS INSTITUTE)
随着计算机的全世界普及,世界各国都用的上计算机了。但是每个国家都有各自的语言,而传统的计算机只支持ASCII编码表的字符。这对于不以英语为母语的人来说,使用计算机是非常吃力的。为了解决这个问题,各个国家的人都出了一套收录自己文字的字符编码(当然包含了ASCII里所有字符)。
@@ -46,7 +48,7 @@ byte,字节,一个byte由8个bit表示,一个byte表示的数字区间[0,2
* 在日文Windows中,ANSI编码是`Shift_JIS`
* ...
-## 1.12.4 Unicode编码
+## 4. Unicode编码
上节讲到各国都有了自己的编码,已经可以正常使用电脑了。
但是随着国际交流的日益频繁和迫切需要,我们中国人也要和美国人进行信息交流,美国人还要和日本人进行信息交流。假设,我们把一篇中文论文发到网上,美国人在用自己的计算机查看这个网页的时候,由于本地计算机不支持GB2312,结果无法显示正确信息,或者乱码。你会说,那很简单啊,让美国人在电脑上装上GB2312编码不就OK。真的OK吗?世界上那么多国家,那么多语言,那么多ANSI编码,都装上是不是要疯了。好吧,假如你真的不厌其烦的装上了,再假设,有一个中国人,他不仅会说汉语,还会说日语。他发表了一篇既有汉语也有日文的文章,而美国人在看这篇文章的时候,是用GB2312来解码呢,还是用Shift_JIS来解码呢?无论用哪个解码都会出现乱码的情况。
@@ -59,7 +61,7 @@ Unicode是由统一码联盟(英语:The Unicode Consortium),一个统筹
Unicode至今仍在不断增修,每个新版本都加入更多新的字符。目前最新的版本为2017年6月20日公布的10.0.0,已经收入超过十万个字符(第十万个字符在2005年获采纳)。[Unicode-维基百科](https://zh.wikipedia.org/wiki/Unicode)
-## 1.12.5 UTF-8编码
+## 5. UTF-8编码
到目前为止,世界各国人民,都能愉快的无语言障碍的使用计算机了。
但是随着信息化时代的来临,人们越来越追求资源的传输速度和硬盘的存储效率。
@@ -83,7 +85,7 @@ UTF-8(UTF:Unicode TransferFormat,即把Unicode转做某种格式的意思)
当在txt输入输入'联通',保存再次打开就乱码,输入'你好联通'就不会出现这个情况。

-## 1.12.6 编码之于Python
+## 6. 编码之于Python
**Python2**
Python2默认是使用ASCII编码,这也是出现编码问题的罪魁祸首。但这也不怪Python,在Python诞生的时候,Unicode还没出现。
@@ -111,7 +113,7 @@ print '\xe4\xb8\xad\xe6\x96\x87'.decode('utf-8')
在Python3中,已经默认使用Unicode编码了。解决了很多编码的问题。
如果py文件中,含有中文还是得在文件头出加入 `# coding=utf-8`
-## 1.12.7 扩展阅读
+## 7. 扩展阅读
中文编码的发展
GB2312-> GBK -> GB18030
@@ -134,4 +136,4 @@ GB 18030 与 GB 2312-1980 和 GBK 兼容,共收录汉字70244个。
---
-
+
diff --git a/source/c01/c01_12.rst b/source/c01/c01_12.rst
old mode 100755
new mode 100644
index b9736b4..695d245
--- a/source/c01/c01_12.rst
+++ b/source/c01/c01_12.rst
@@ -1,13 +1,15 @@
1.12 搞懂字符编码的前世今生
===========================
+|image0|
+
--------------
初学计算机的人,肯定对众多字符编码感到头疼。为什么会那么多字符串编码?
这些内容是在去年整理的,现在重新整理下,发布在博客,搞懂字符串编码,这一篇文章足矣
-1.12.1 前言必知
----------------
+1. 前言必知
+-----------
初学计算机的人,肯定对众多字符编码感到头疼。为什么会那么多字符串编码?
@@ -16,8 +18,8 @@
bit,位,一个bit可以表示两个数字,0和1
byte,字节,一个byte由8个bit表示,一个byte表示的数字区间[0,255]
-1.12.2 ASCII编码
-----------------
+2. ASCII编码
+------------
原始ASCII编码
~~~~~~~~~~~~~
@@ -42,8 +44,8 @@ byte,字节,一个byte由8个bit表示,一个byte表示的数字区间[0,2
原始ASCII编码
-1.12.3 ANSI标准
----------------
+3. ANSI标准
+-----------
ANSI:美国国家标准学会(AMERICAN NATIONAL STANDARDS INSTITUTE)
@@ -61,8 +63,8 @@ byte,字节,一个byte由8个bit表示,一个byte表示的数字区间[0,2
- 在日文Windows中,ANSI编码是\ ``Shift_JIS``
- …
-1.12.4 Unicode编码
-------------------
+4. Unicode编码
+--------------
上节讲到各国都有了自己的编码,已经可以正常使用电脑了。
但是随着国际交流的日益频繁和迫切需要,我们中国人也要和美国人进行信息交流,美国人还要和日本人进行信息交流。假设,我们把一篇中文论文发到网上,美国人在用自己的计算机查看这个网页的时候,由于本地计算机不支持GB2312,结果无法显示正确信息,或者乱码。你会说,那很简单啊,让美国人在电脑上装上GB2312编码不就OK。真的OK吗?世界上那么多国家,那么多语言,那么多ANSI编码,都装上是不是要疯了。好吧,假如你真的不厌其烦的装上了,再假设,有一个中国人,他不仅会说汉语,还会说日语。他发表了一篇既有汉语也有日文的文章,而美国人在看这篇文章的时候,是用GB2312来解码呢,还是用Shift_JIS来解码呢?无论用哪个解码都会出现乱码的情况。
@@ -76,8 +78,8 @@ Consortium),一个统筹统一码(Unicode)发展的非营利机构,其
Unicode至今仍在不断增修,每个新版本都加入更多新的字符。目前最新的版本为2017年6月20日公布的10.0.0,已经收入超过十万个字符(第十万个字符在2005年获采纳)。\ `Unicode-维基百科 `__
-1.12.5 UTF-8编码
-----------------
+5. UTF-8编码
+------------
| 到目前为止,世界各国人民,都能愉快的无语言障碍的使用计算机了。
| 但是随着信息化时代的来临,人们越来越追求资源的传输速度和硬盘的存储效率。
@@ -95,14 +97,14 @@ TransferFormat,即把Unicode转做某种格式的意思)应运而生
**扩展问题**
- UTF-8有用一个字节表示,有用两个字节表示,读取数据,如何识别是几个字节表示一个字符?
- |image0|
+ |image1|
- ‘联通’显示乱码
当在txt输入输入’联通’,保存再次打开就乱码,输入’你好联通’就不会出现这个情况。
- |image1|
+ |image2|
-1.12.6 编码之于Python
----------------------
+6. 编码之于Python
+-----------------
**Python2**
Python2默认是使用ASCII编码,这也是出现编码问题的罪魁祸首。但这也不怪Python,在Python诞生的时候,Unicode还没出现。
@@ -130,8 +132,8 @@ Python2默认是使用ASCII编码,这也是出现编码问题的罪魁祸首
在Python3中,已经默认使用Unicode编码了。解决了很多编码的问题。
如果py文件中,含有中文还是得在文件头出加入 ``# coding=utf-8``
-1.12.7 扩展阅读
----------------
+7. 扩展阅读
+-----------
中文编码的发展 GB2312-> GBK -> GB18030
@@ -154,11 +156,10 @@ Python2默认是使用ASCII编码,这也是出现编码问题的罪魁祸首
--------------
-.. figure:: http://image.python-online.cn/image-20200320125724880.png
- :alt: 关注公众号,获取最新干货!
-
- 关注公众号,获取最新干货!
+|image3|
-.. |image0| image:: https://i.loli.net/2017/08/02/598168fe2b016.png
-.. |image1| image:: https://i.loli.net/2017/08/02/59816d652aeb9.png
+.. |image0| image:: http://image.iswbm.com/20200602135014.png
+.. |image1| image:: https://i.loli.net/2017/08/02/598168fe2b016.png
+.. |image2| image:: https://i.loli.net/2017/08/02/59816d652aeb9.png
+.. |image3| image:: http://image.iswbm.com/20200607174235.png
diff --git a/source/c01/c01_13.md b/source/c01/c01_13.md
index aab1c8b..8cb56c2 100644
--- a/source/c01/c01_13.md
+++ b/source/c01/c01_13.md
@@ -1,127 +1,353 @@
-# 1.13 Python几个高阶函数
+# 1.13 别再使用 pprint 打印字典了
----
+
-## 1.13.1 lambda 表达式
+## 1. 吐槽问题
-匿名函数(英语:anonymous function)是指一类无需定义标识符(函数名)的函数。通俗来说呢,就是它可以让我们的函数,可以不需要函数名。
+pprint 你应该很熟悉了吧?
-正常情况下,我们定义一个函数,使用的是 `def` 关键字,而当你学会使用匿名函数后,替代 `def` 的是 `lambda`。
+随便在搜索引擎上搜索如何打印漂亮的字典或者格式化字符串时,大部分人都会推荐你使用这货 。
-这边使用`def` 和 `lambda` 分别举个例子,你很快就能理解。
-```
-def mySum(x, y):
- return x+y
-mySum(2, 3)
-# 5
+比如这下面这个 json 字符串或者说字典(我随便在网上找的),如果不格式化美化一下,根本无法阅读。
-(lambda x, y: x+y)(2, 4)
-# 6
+```json
+[{"id":1580615,"name":"皮的嘛","packageName":"com.renren.mobile.android","iconUrl":"app/com.renren.mobile.android/icon.jpg","stars":2,"size":21803987,"downloadUrl":"app/com.renren.mobile.android/com.renren.mobile.android.apk","des":"2011-2017 你的铁头娃一直在这儿。中国最大的实名制SNS网络平台,嫩头青"},{"id":1540629,"name":"不存在的","packageName":"com.ct.client","iconUrl":"app/com.ct.client/icon.jpg","stars":2,"size":4794202,"downloadUrl":"app/com.ct.client/com.ct.client.apk","des":"斗鱼271934 走过路过不要错过,这里有最好的鸡儿"}]
```
-从上面的示例,我们可以看到匿名函数直接运行,省下了很多行的代码,有没有?
-接下来,我们的仔细看一下它的用法
+如果你不想看到一堆密密麻麻的字,那就使用大伙都极力推荐的 pprint 看下什么效果(以下在 Python 2 中演示,Python 3 中是不一样的效果)。
-带 if/else
-```
->>>( lambda x, y: x if x < y else y )( 1, 2 )
-1
-```
-嵌套函数
-```
->>>( lambda x: ( lambda y: ( lambda z: x + y + z )( 1 ) )( 2 ) )( 3 )
-6
-```
-递归函数
-```
->>> func = lambda n:1 if n == 0 else n * func(n-1)
->>> func(5)
-120
-```
-或者
-```
->>> f = lambda func, n: 1 if n == 0 else n * func( func, n - 1 )
->>> f(f,4)
-24
+```python
+>>> info=[{"id":1580615,"name":"皮的嘛","packageName":"com.renren.mobile.android","iconUrl":"app/com.renren.mobile.android/icon.jpg","stars":2,"size":21803987,"downloadUrl":"app/com.renren.mobile.android/com.renren.mobile.android.apk","des":"2011-2017 你的铁头娃一直在这儿。中国最大的实名制SNS网络平台,嫩头青"},{"id":1540629,"name":"不存在的","packageName":"com.ct.client","iconUrl":"app/com.ct.client/icon.jpg","stars":2,"size":4794202,"downloadUrl":"app/com.ct.client/com.ct.client.apk","des":"斗鱼271934 走过路过不要错过,这里有最好的鸡儿"}]
+>>>
+>>> from pprint import pprint
+>>> pprint(info)
+[{'des': '2011-2017 \xe4\xbd\xa0\xe7\x9a\x84\xe9\x93\x81\xe5\xa4\xb4\xe5\xa8\x83\xe4\xb8\x80\xe7\x9b\xb4\xe5\x9c\xa8\xe8\xbf\x99\xe5\x84\xbf\xe3\x80\x82\xe4\xb8\xad\xe5\x9b\xbd\xe6\x9c\x80\xe5\xa4\xa7\xe7\x9a\x84\xe5\xae\x9e\xe5\x90\x8d\xe5\x88\xb6SNS\xe7\xbd\x91\xe7\xbb\x9c\xe5\xb9\xb3\xe5\x8f\xb0\xef\xbc\x8c\xe5\xab\xa9\xe5\xa4\xb4\xe9\x9d\x92',
+ 'downloadUrl': 'app/com.renren.mobile.android/com.renren.mobile.android.apk',
+ 'iconUrl': 'app/com.renren.mobile.android/icon.jpg',
+ 'id': 1580615,
+ 'name': '\xe7\x9a\xae\xe7\x9a\x84\xe5\x98\x9b',
+ 'packageName': 'com.renren.mobile.android',
+ 'size': 21803987,
+ 'stars': 2},
+ {'des': '\xe6\x96\x97\xe9\xb1\xbc271934 \xe8\xb5\xb0\xe8\xbf\x87\xe8\xb7\xaf\xe8\xbf\x87\xe4\xb8\x8d\xe8\xa6\x81\xe9\x94\x99\xe8\xbf\x87\xef\xbc\x8c\xe8\xbf\x99\xe9\x87\x8c\xe6\x9c\x89\xe6\x9c\x80\xe5\xa5\xbd\xe7\x9a\x84\xe9\xb8\xa1\xe5\x84\xbf',
+ 'downloadUrl': 'app/com.ct.client/com.ct.client.apk',
+ 'iconUrl': 'app/com.ct.client/icon.jpg',
+ 'id': 1540629,
+ 'name': '\xe4\xb8\x8d\xe5\xad\x98\xe5\x9c\xa8\xe7\x9a\x84',
+ 'packageName': 'com.ct.client',
+ 'size': 4794202,
+ 'stars': 2}]
```
-从以上示例来看,lambda 表达式和常规的函数相比,写法比较怪异,可读性相对较差。除了可以直接运行之外,好像并没有其他较为突出的功能,为什么在今天我们要介绍它呢?
+好像有点效果,真的是 “神器”呀。
-首先我们要知道 lambda 是一个表达式,而不是一个语句。正因为这个特点,我们可以在一些特殊的场景中去使用它。具体是什么场景呢?接下来我们会介绍到几个非常好用的内置函数。
+但是你告诉我, **\xe4\xbd\xa0\xe7\x9a** 这些是什么玩意?本来想提高可读性的,现在变成完全不可读了。
-## 1.13.2 map 函数
+好在我懂点 Python 2 的编码,知道 Python 2 中默认(不带u)的字符串格式都是 str 类型,也是 bytes 类型,它是以 byte 存储的。
-map 函数,它接收两个参数,第一个参数是一个函数对象(当然也可以是一个lambda表达式),第二个参数是一个序列。
+行吧,好像是我错了,我改了下,使用 unicode 类型来定义中文字符串吧。
-它可以实现怎样的功能呢,我举个例子你就明白了。
-```
->>> map(lambda x: x*2, [1,2,3,4,5])
-[2, 4, 6, 8, 10]
+```python
+>>> info = [{"id":1580615,"name":u"皮的嘛","packageName":"com.renren.mobile.android","iconUrl":"app/com.renren.mobile.android/icon.jpg","stars":2,"size":21803987,"downloadUrl":"app/com.renren.mobile.android/com.renren.mobile.android.apk","des":u"2011-2017你的铁头娃一直在这儿。中国最大的实名制SNS网络平台,嫩头青"},{"id":1540629,"name":u"不存在的","packageName":"com.ct.client","iconUrl":"app/com.ct.client/icon.jpg","stars":2,"size":4794202,"downloadUrl":"app/com.ct.client/com.ct.client.apk","des":u"斗鱼271934走过路过不要错过,这里有最好的鸡儿"}]
+>>>
+>>> from pprint import pprint
+>>> pprint(info)
+[{'des': u'2011-2017\u4f60\u7684\u94c1\u5934\u5a03\u4e00\u76f4\u5728\u8fd9\u513f\u3002\u4e2d\u56fd\u6700\u5927\u7684\u5b9e\u540d\u5236SNS\u7f51\u7edc\u5e73\u53f0\uff0c\u5ae9\u5934\u9752',
+ 'downloadUrl': 'app/com.renren.mobile.android/com.renren.mobile.android.apk',
+ 'iconUrl': 'app/com.renren.mobile.android/icon.jpg',
+ 'id': 1580615,
+ 'name': u'\u76ae\u7684\u561b',
+ 'packageName': 'com.renren.mobile.android',
+ 'size': 21803987,
+ 'stars': 2},
+ {'des': u'\u6597\u9c7c271934\u8d70\u8fc7\u8def\u8fc7\u4e0d\u8981\u9519\u8fc7\uff0c\u8fd9\u91cc\u6709\u6700\u597d\u7684\u9e21\u513f',
+ 'downloadUrl': 'app/com.ct.client/com.ct.client.apk',
+ 'iconUrl': 'app/com.ct.client/icon.jpg',
+ 'id': 1540629,
+ 'name': u'\u4e0d\u5b58\u5728\u7684',
+ 'packageName': 'com.ct.client',
+ 'size': 4794202,
+ 'stars': 2}]
```
-可以很清楚地看到,它可以将后面序列中的每一个元素做为参数传入lambda中。
-当我们不使用 map 函数时,你也许会这样子写。
+确实是有好点了,但是看到下面这些,我崩溃了,我哪里知道这是什么鬼,难道是我太菜了吗?当我是计算机呀?
+
```
-mylist=[]
-for i in [1,2,3,4,5]:
- mylist.append(i*2)
+u'\u6597\u9c7c271934\u8d70\u8fc7\u8def\u8fc7\u4e0d\u8981\u9519\u8fc7\uff0c\u8fd9\u91cc\u6709\u6700\u597d\u7684\u9e21\u513f'
```
-## 1.13.3 filter 函数
+除此之外,我们知道 json 的严格要求必须使用 **双引号**,而我定义字典时,也使用了双引号了,为什么打印出来的为什么是 **单引号**?我也太难了吧,我连自己的代码都无法控制了吗?
-filter 函数,和 map 函数相似。同样也是接收两个参数,一个lambda 表达式,一个序列。它会遍历后面序列中每一个元素,并将其做为参数传入lambda表达式中,当表达式返回 True,则元素会被保留下来,当表达式返回 False ,则元素会被丢弃。
+到这里,我们知道了 pprint 带来的两个问题:
-下面这个例子,将过滤出一个列表中小于0的元素。
-```
->>>filter(lambda x: x < 0, range(-5, 5))
-[-5, -4, -3, -2, -1]
-```
+1. 没法在 Python 2 下正常打印中文
+2. 没法输出 JSON 标准格式的格式化内容(双引号)
-## 1.13.4 reduce 函数
+## 2. 解决问题
-reduce 函数,也是类似的。它的作用是先对序列中的第 1、2 个元素进行操作,得到的结果再与第三个数据用 lambda 函数运算,将其得到的结果再与第四个元素进行运算,以此类推下去直到后面没有元素了。
+### 打印中文
+
+如果你是在 Python 3 下使用,你会发现中文是可以正常显示的。
+
+```python
+# Python3.7
+>>> info = [{"id":1580615,"name":u"皮的嘛","packageName":"com.renren.mobile.android","iconUrl":"app/com.renren.mobile.android/icon.jpg","stars":2,"size":21803987,"downloadUrl":"app/com.renren.mobile.android/com.renren.mobile.android.apk","des":u"2011-2017你的铁头娃一直在这儿。中国最大的实名制SNS网络平台,嫩头青"},{"id":1540629,"name":u"不存在的","packageName":"com.ct.client","iconUrl":"app/com.ct.client/icon.jpg","stars":2,"size":4794202,"downloadUrl":"app/com.ct.client/com.ct.client.apk","des":u"斗鱼271934走过路过不要错过,这里有最好的鸡儿"}]
+>>>
+>>> from pprint import pprint
+>>> pprint(info)
+[{'des': '2011-2017你的铁头娃一直在这儿。中国最大的实名制SNS网络平台,嫩头青',
+ 'downloadUrl': 'app/com.renren.mobile.android/com.renren.mobile.android.apk',
+ 'iconUrl': 'app/com.renren.mobile.android/icon.jpg',
+ 'id': 1580615,
+ 'name': '皮的嘛',
+ 'packageName': 'com.renren.mobile.android',
+ 'size': 21803987,
+ 'stars': 2},
+ {'des': '斗鱼271934走过路过不要错过,这里有最好的鸡儿',
+ 'downloadUrl': 'app/com.ct.client/com.ct.client.apk',
+ 'iconUrl': 'app/com.ct.client/icon.jpg',
+ 'id': 1540629,
+ 'name': '不存在的',
+ 'packageName': 'com.ct.client',
+ 'size': 4794202,
+ 'stars': 2}]
+>>>
-
-这边举个例子你也就明白了。
-```
->>>reduce(lambda x,y: x+y, [1,2,3,4,5])
-15
-```
-它的运算过程分解一下是这样的。
```
-1+2=3
-3+3=6
-6+4+10
-10+5=15
+
+但是很多时候(在公司的一些服务器)你无法选择自己使用哪个版本的 Python,本来我可以选择不用的,因为有更好的替代方案(**这个后面会讲**)。
+
+但是我出于猎奇,正好前两天不是写过一篇关于 编码 的文章吗,我自认为自己对于 编码还是掌握比较熟练的,就想着来解决一下这个问题。
+
+索性就来看下 pprint 的源代码,还真被我找到了解决方法,如果你也想挑战一下,不防在这里停住,自己研究一下如何实现,我相信对你阅读源码会有帮助。
+
+**以下是我的解决方案,供你参考**:
+
+写一个自己的 printer 对象,继承自 PrettyPrinter (pprint 使用的printer)
+
+并且复写 format 方法,判断传进来的字符串对象是否 str 类型,如果不是 str 类型,而是 unicode 类型,就用 uft8 编码成 str 类型。
+
+```python
+# coding: utf-8
+from pprint import PrettyPrinter
+
+# 继承 PrettyPrinter,复写 format 方法
+class MyPrettyPrinter(PrettyPrinter):
+ def format(self, object, context, maxlevels, level):
+ if isinstance(object, unicode):
+ return (object.encode('utf8'), True, False)
+ return PrettyPrinter.format(self, object, context, maxlevels, level)
+
+info = [{"id":1580615,"name":u"皮的嘛","packageName":"com.renren.mobile.android","iconUrl":"app/com.renren.mobile.android/icon.jpg","stars":2,"size":21803987,"downloadUrl":"app/com.renren.mobile.android/com.renren.mobile.android.apk","des":u"2011-2017你的铁头娃一直在这儿。中国最大的实名制SNS网络平台,嫩头青"},{"id":1540629,"name":u"不存在的","packageName":"com.ct.client","iconUrl":"app/com.ct.client/icon.jpg","stars":2,"size":4794202,"downloadUrl":"app/com.ct.client/com.ct.client.apk","des":u"斗鱼271934走过路过不要错过,这里有最好的鸡儿"}]
+
+MyPrettyPrinter().pprint(info)
```
-## 1.13.5 注意点
+输出如下,已经解决了中文的显示问题:
-以上几个函数,熟练的掌握它们的写法,可以让我们的代码看起来更加的 Pythonic ,在某一程度上代码看起来更加的简洁。
+
-如果你是新手呢,你需要注意的是,以上示例是在 Python2.x 环境下演示的。而在 Python3.x 中,却有所不同,你可以自己尝试一下。
+### 打印双引号
-这里总结一下:
+解决了中文问题后,再来看看如何让 pprint 打印双引号。
-第一点,map 和 filter 函数返回的都不再是一个列表,而是一个迭代器对象。这里以map为例
+在实例化 PrettyPrinter 对象的时候,可以接收一个 stream 对象,它表示你要将内容输出到哪里,默认是使用 sys.stdout 这个 stream,也就是标准输出。
+
+现在我们要修改输出的内容,也就是将输出的单引号替换成双引号。
+
+那我们完全可以自己定义一个 stream 类型的对象,该对象不需要继承任何父类,只要你实现 write 方法就可以。
+
+有了思路,就可以开始写代码了,如下:
+
+```python
+# coding: utf-8
+from pprint import PrettyPrinter
+
+class MyPrettyPrinter(PrettyPrinter):
+ def format(self, object, context, maxlevels, level):
+ if isinstance(object, unicode):
+ return (object.encode('utf8'), True, False)
+ return PrettyPrinter.format(self, object, context, maxlevels, level)
+
+class MyStream():
+ def write(self, text):
+ print text.replace('\'', '"')
+
+info = [{"id":1580615,"name":u"皮的嘛","packageName":"com.renren.mobile.android","iconUrl":"app/com.renren.mobile.android/icon.jpg","stars":2,"size":21803987,"downloadUrl":"app/com.renren.mobile.android/com.renren.mobile.android.apk","des":u"2011-2017你的铁头娃一直在这儿。中国最大的实名制SNS网络平台,嫩头青"},{"id":1540629,"name":u"不存在的","packageName":"com.ct.client","iconUrl":"app/com.ct.client/icon.jpg","stars":2,"size":4794202,"downloadUrl":"app/com.ct.client/com.ct.client.apk","des":u"斗鱼271934走过路过不要错过,这里有最好的鸡儿"}]
+MyPrettyPrinter(stream=MyStream()).pprint(info)
```
->>> map_obj = map(lambda x: x*2, [1,2,3,4,5])
->>> from collections.abc import Iterator
->>> isinstance(map_obj, Iterator)
-True
->>> next(map_obj)
+
+尝试执行了下,我的天,怎么是这样子的。
+
+```json
+[
+{
+"des"
+:
+2011-2017你的铁头娃一直在这儿。中国最大的实名制SNS网络平台,嫩头青
+,
+ "downloadUrl":
+"app/com.renren.mobile.android/com.renren.mobile.android.apk"
+,
+ "iconUrl":
+"app/com.renren.mobile.android/icon.jpg"
+,
+ "id":
+1580615
+,
+ "name":
+皮的嘛
+,
+ "packageName":
+"com.renren.mobile.android"
+,
+ "size":
+21803987
+,
+ "stars":
+2
+}
+,
+
+{
+"des"
+:
+斗鱼271934走过路过不要错过,这里有最好的鸡儿
+,
+ "downloadUrl":
+"app/com.ct.client/com.ct.client.apk"
+,
+ "iconUrl":
+"app/com.ct.client/icon.jpg"
+,
+ "id":
+1540629
+,
+ "name":
+不存在的
+,
+ "packageName":
+"com.ct.client"
+,
+ "size":
+4794202
+,
+ "stars":
2
->>> list(map_obj)
-[4, 6, 8, 10]
+}
+]
```
-第二点,reduce 不可以直接调用,而是要先导入才能使用,
+经过一番研究,才知道是因为 print 函数默认会将打印的内容后面加个 **换行符**。
+
+那如何将使 print 函数打印的内容,不进行换行呢?
+
+方法很简单,但是我相信很多人都不知道,只要在 print 的内容后加一个 **逗号** 就行。
+
+就像下面这样。
+
+
+
+知道了问题所在,再修改下代码
+
+```python
+# coding: utf-8
+from pprint import PrettyPrinter
+
+class MyPrettyPrinter(PrettyPrinter):
+ def format(self, object, context, maxlevels, level):
+ if isinstance(object, unicode):
+ return (object.encode('utf8'), True, False)
+ return PrettyPrinter.format(self, object, context, maxlevels, level)
+
+class MyStream():
+ def write(self, text):
+ print text.replace('\'', '"'),
+
+info = [{"id":1580615,"name":u"皮的嘛","packageName":"com.renren.mobile.android","iconUrl":"app/com.renren.mobile.android/icon.jpg","stars":2,"size":21803987,"downloadUrl":"app/com.renren.mobile.android/com.renren.mobile.android.apk","des":u"2011-2017你的铁头娃一直在这儿。中国最大的实名制SNS网络平台,嫩头青"},{"id":1540629,"name":u"不存在的","packageName":"com.ct.client","iconUrl":"app/com.ct.client/icon.jpg","stars":2,"size":4794202,"downloadUrl":"app/com.ct.client/com.ct.client.apk","des":u"斗鱼271934走过路过不要错过,这里有最好的鸡儿"}]
+
+MyPrettyPrinter(stream=MyStream()).pprint(info)
```
-from functools import reduce
+
+终于成功了,太不容易了吧。
+
+
+
+## 3. 何必折腾
+
+通过上面的一番折腾,我终于实现了我 **梦寐以求** 的需求。
+
+代价就是我整整花费了两个小时,才得以实现,而对于小白来说,可能没有信心,也没有耐心去做这样的事情。
+
+**所以我想说的是,Python 2 下的 pprint ,真的不要再用了**。
+
+为什么我要用这么 说,因为明明有更好的替代品,人生苦短,既然用了 Python ,当然是怎么简单怎么来咯,何必为难自己呢,一行代码可以解决的事情,偏偏要去写两个类,那不是自讨苦吃吗?(我这是在骂自己吗?
+
+如果你愿意抛弃 pprint ,那我推荐你用 json.dumps ,我保证你再也不想用 pprint 了。
+
+### 打印中文
+
+其实无法打印中文,是 Python 2 引来的大坑,并不能全怪 pprint 。
+
+但是同样的问题,在 json.dumps 这里,却只要加个参数就好了,可比 pprint 简单得不要太多。
+
+具体的代码示例如下:
+
+```python
+>>> info = [{"id":1580615,"name":"皮的嘛","packageName":"com.renren.mobile.android","iconUrl":"app/com.renren.mobile.android/icon.jpg","stars":2,"size":21803987,"downloadUrl":"app/com.renren.mobile.android/com.renren.mobile.android.apk","des":"2011-2017你的铁头娃一直在这儿。中国最大的实名制SNS网络平台,嫩头青"},{"id":1540629,"name":"不存在的","packageName":"com.ct.client","iconUrl":"app/com.ct.client/icon.jpg","stars":2,"size":4794202,"downloadUrl":"app/com.ct.client/com.ct.client.apk","des":"斗鱼271934走过路过不要错过,这里有最好的鸡儿"}]
+>>>
+>>> import json
+>>>
+>>>
+>>> print json.dumps(info, indent=4, ensure_ascii=False)
+[
+ {
+ "downloadUrl": "app/com.renren.mobile.android/com.renren.mobile.android.apk",
+ "iconUrl": "app/com.renren.mobile.android/icon.jpg",
+ "name": "皮的嘛",
+ "stars": 2,
+ "packageName": "com.renren.mobile.android",
+ "des": "2011-2017你的铁头娃一直在这儿。中国最大的实名制SNS网络平台,嫩头青",
+ "id": 1580615,
+ "size": 21803987
+ },
+ {
+ "downloadUrl": "app/com.ct.client/com.ct.client.apk",
+ "iconUrl": "app/com.ct.client/icon.jpg",
+ "name": "不存在的",
+ "stars": 2,
+ "packageName": "com.ct.client",
+ "des": "斗鱼271934走过路过不要错过,这里有最好的鸡儿",
+ "id": 1540629,
+ "size": 4794202
+ }
+]
+>>>
```
+json.dumps 的关键参数有两个:
+
+- **indent=4**:以 4 个空格缩进单位
+- **ensure_ascii=False**:接收非 ASCII 编码的字符,这样才能使用中文
+
+与 pprint 相比 json.dumps 可以说完胜:
+
+1. 两个参数就能实现所有我的需求(打印中文与双引号)
+2. 就算在 Python 2 下,使用中文也不需要用 `u'中文'` 这种写法
+3. Python2 和 Python3 的写法完全一致,对于这一点不需要考虑兼容问题
+
+## 4. 总结一下
+
+本来很简单的一个观点,我为了证明 pprint 实现那两个需求有多么困难,花了很多的时间去研究了 pprint 的源码(各种处理其实还是挺复杂的),不过好在最后也能有所收获。
+
+本文的分享就到这里,阅读本文,我认为你可以获取到三个知识点
+
+1. 核心观点:Python2 下不要再使用 pprint
+2. 若真要使用,且有和一样的改造需求,可以参考我的实现
+3. Python 2 中的 print 语句后居然可以加 逗号
+
+以上。希望此文能对你有帮助。
----
-
+
\ No newline at end of file
diff --git a/source/c01/c01_13.rst b/source/c01/c01_13.rst
old mode 100755
new mode 100644
index 2060038..99db502
--- a/source/c01/c01_13.rst
+++ b/source/c01/c01_13.rst
@@ -1,166 +1,391 @@
-1.13 Python几个高阶函数
-=======================
+1.13 别再使用 pprint 打印字典了
+===============================
+
+|image0|
+
+1. 吐槽问题
+-----------
+
+pprint 你应该很熟悉了吧?
+
+随便在搜索引擎上搜索如何打印漂亮的字典或者格式化字符串时,大部分人都会推荐你使用这货
+。
+
+比如这下面这个 json
+字符串或者说字典(我随便在网上找的),如果不格式化美化一下,根本无法阅读。
+
+.. code:: json
+
+ [{"id":1580615,"name":"皮的嘛","packageName":"com.renren.mobile.android","iconUrl":"app/com.renren.mobile.android/icon.jpg","stars":2,"size":21803987,"downloadUrl":"app/com.renren.mobile.android/com.renren.mobile.android.apk","des":"2011-2017 你的铁头娃一直在这儿。中国最大的实名制SNS网络平台,嫩头青"},{"id":1540629,"name":"不存在的","packageName":"com.ct.client","iconUrl":"app/com.ct.client/icon.jpg","stars":2,"size":4794202,"downloadUrl":"app/com.ct.client/com.ct.client.apk","des":"斗鱼271934 走过路过不要错过,这里有最好的鸡儿"}]
+
+如果你不想看到一堆密密麻麻的字,那就使用大伙都极力推荐的 pprint
+看下什么效果(以下在 Python 2 中演示,Python 3 中是不一样的效果)。
+
+.. code:: python
+
+ >>> info=[{"id":1580615,"name":"皮的嘛","packageName":"com.renren.mobile.android","iconUrl":"app/com.renren.mobile.android/icon.jpg","stars":2,"size":21803987,"downloadUrl":"app/com.renren.mobile.android/com.renren.mobile.android.apk","des":"2011-2017 你的铁头娃一直在这儿。中国最大的实名制SNS网络平台,嫩头青"},{"id":1540629,"name":"不存在的","packageName":"com.ct.client","iconUrl":"app/com.ct.client/icon.jpg","stars":2,"size":4794202,"downloadUrl":"app/com.ct.client/com.ct.client.apk","des":"斗鱼271934 走过路过不要错过,这里有最好的鸡儿"}]
+ >>>
+ >>> from pprint import pprint
+ >>> pprint(info)
+ [{'des': '2011-2017 \xe4\xbd\xa0\xe7\x9a\x84\xe9\x93\x81\xe5\xa4\xb4\xe5\xa8\x83\xe4\xb8\x80\xe7\x9b\xb4\xe5\x9c\xa8\xe8\xbf\x99\xe5\x84\xbf\xe3\x80\x82\xe4\xb8\xad\xe5\x9b\xbd\xe6\x9c\x80\xe5\xa4\xa7\xe7\x9a\x84\xe5\xae\x9e\xe5\x90\x8d\xe5\x88\xb6SNS\xe7\xbd\x91\xe7\xbb\x9c\xe5\xb9\xb3\xe5\x8f\xb0\xef\xbc\x8c\xe5\xab\xa9\xe5\xa4\xb4\xe9\x9d\x92',
+ 'downloadUrl': 'app/com.renren.mobile.android/com.renren.mobile.android.apk',
+ 'iconUrl': 'app/com.renren.mobile.android/icon.jpg',
+ 'id': 1580615,
+ 'name': '\xe7\x9a\xae\xe7\x9a\x84\xe5\x98\x9b',
+ 'packageName': 'com.renren.mobile.android',
+ 'size': 21803987,
+ 'stars': 2},
+ {'des': '\xe6\x96\x97\xe9\xb1\xbc271934 \xe8\xb5\xb0\xe8\xbf\x87\xe8\xb7\xaf\xe8\xbf\x87\xe4\xb8\x8d\xe8\xa6\x81\xe9\x94\x99\xe8\xbf\x87\xef\xbc\x8c\xe8\xbf\x99\xe9\x87\x8c\xe6\x9c\x89\xe6\x9c\x80\xe5\xa5\xbd\xe7\x9a\x84\xe9\xb8\xa1\xe5\x84\xbf',
+ 'downloadUrl': 'app/com.ct.client/com.ct.client.apk',
+ 'iconUrl': 'app/com.ct.client/icon.jpg',
+ 'id': 1540629,
+ 'name': '\xe4\xb8\x8d\xe5\xad\x98\xe5\x9c\xa8\xe7\x9a\x84',
+ 'packageName': 'com.ct.client',
+ 'size': 4794202,
+ 'stars': 2}]
+
+好像有点效果,真的是 “神器”呀。
+
+但是你告诉我,
+**:raw-latex:`\xe`4:raw-latex:`\xbd`:raw-latex:`\xa`0:raw-latex:`\xe`7:raw-latex:`\x`9a**
+这些是什么玩意?本来想提高可读性的,现在变成完全不可读了。
+
+好在我懂点 Python 2 的编码,知道 Python 2
+中默认(不带u)的字符串格式都是 str 类型,也是 bytes 类型,它是以 byte
+存储的。
+
+行吧,好像是我错了,我改了下,使用 unicode 类型来定义中文字符串吧。
+
+.. code:: python
+
+ >>> info = [{"id":1580615,"name":u"皮的嘛","packageName":"com.renren.mobile.android","iconUrl":"app/com.renren.mobile.android/icon.jpg","stars":2,"size":21803987,"downloadUrl":"app/com.renren.mobile.android/com.renren.mobile.android.apk","des":u"2011-2017你的铁头娃一直在这儿。中国最大的实名制SNS网络平台,嫩头青"},{"id":1540629,"name":u"不存在的","packageName":"com.ct.client","iconUrl":"app/com.ct.client/icon.jpg","stars":2,"size":4794202,"downloadUrl":"app/com.ct.client/com.ct.client.apk","des":u"斗鱼271934走过路过不要错过,这里有最好的鸡儿"}]
+ >>>
+ >>> from pprint import pprint
+ >>> pprint(info)
+ [{'des': u'2011-2017\u4f60\u7684\u94c1\u5934\u5a03\u4e00\u76f4\u5728\u8fd9\u513f\u3002\u4e2d\u56fd\u6700\u5927\u7684\u5b9e\u540d\u5236SNS\u7f51\u7edc\u5e73\u53f0\uff0c\u5ae9\u5934\u9752',
+ 'downloadUrl': 'app/com.renren.mobile.android/com.renren.mobile.android.apk',
+ 'iconUrl': 'app/com.renren.mobile.android/icon.jpg',
+ 'id': 1580615,
+ 'name': u'\u76ae\u7684\u561b',
+ 'packageName': 'com.renren.mobile.android',
+ 'size': 21803987,
+ 'stars': 2},
+ {'des': u'\u6597\u9c7c271934\u8d70\u8fc7\u8def\u8fc7\u4e0d\u8981\u9519\u8fc7\uff0c\u8fd9\u91cc\u6709\u6700\u597d\u7684\u9e21\u513f',
+ 'downloadUrl': 'app/com.ct.client/com.ct.client.apk',
+ 'iconUrl': 'app/com.ct.client/icon.jpg',
+ 'id': 1540629,
+ 'name': u'\u4e0d\u5b58\u5728\u7684',
+ 'packageName': 'com.ct.client',
+ 'size': 4794202,
+ 'stars': 2}]
+
+确实是有好点了,但是看到下面这些,我崩溃了,我哪里知道这是什么鬼,难道是我太菜了吗?当我是计算机呀?
---------------
+::
-1.13.1 lambda 表达式
---------------------
+ u'\u6597\u9c7c271934\u8d70\u8fc7\u8def\u8fc7\u4e0d\u8981\u9519\u8fc7\uff0c\u8fd9\u91cc\u6709\u6700\u597d\u7684\u9e21\u513f'
-匿名函数(英语:anonymous
-function)是指一类无需定义标识符(函数名)的函数。通俗来说呢,就是它可以让我们的函数,可以不需要函数名。
+除此之外,我们知道 json 的严格要求必须使用
+**双引号**\ ,而我定义字典时,也使用了双引号了,为什么打印出来的为什么是
+**单引号**\ ?我也太难了吧,我连自己的代码都无法控制了吗?
-正常情况下,我们定义一个函数,使用的是 ``def``
-关键字,而当你学会使用匿名函数后,替代 ``def`` 的是 ``lambda``\ 。
+到这里,我们知道了 pprint 带来的两个问题:
-这边使用\ ``def`` 和 ``lambda`` 分别举个例子,你很快就能理解。
+1. 没法在 Python 2 下正常打印中文
+2. 没法输出 JSON 标准格式的格式化内容(双引号)
-::
+2. 解决问题
+-----------
- def mySum(x, y):
- return x+y
- mySum(2, 3)
- # 5
+打印中文
+~~~~~~~~
- (lambda x, y: x+y)(2, 4)
- # 6
+如果你是在 Python 3 下使用,你会发现中文是可以正常显示的。
-从上面的示例,我们可以看到匿名函数直接运行,省下了很多行的代码,有没有?
+.. code:: python
-接下来,我们的仔细看一下它的用法
+ # Python3.7
+ >>> info = [{"id":1580615,"name":u"皮的嘛","packageName":"com.renren.mobile.android","iconUrl":"app/com.renren.mobile.android/icon.jpg","stars":2,"size":21803987,"downloadUrl":"app/com.renren.mobile.android/com.renren.mobile.android.apk","des":u"2011-2017你的铁头娃一直在这儿。中国最大的实名制SNS网络平台,嫩头青"},{"id":1540629,"name":u"不存在的","packageName":"com.ct.client","iconUrl":"app/com.ct.client/icon.jpg","stars":2,"size":4794202,"downloadUrl":"app/com.ct.client/com.ct.client.apk","des":u"斗鱼271934走过路过不要错过,这里有最好的鸡儿"}]
+ >>>
+ >>> from pprint import pprint
+ >>> pprint(info)
+ [{'des': '2011-2017你的铁头娃一直在这儿。中国最大的实名制SNS网络平台,嫩头青',
+ 'downloadUrl': 'app/com.renren.mobile.android/com.renren.mobile.android.apk',
+ 'iconUrl': 'app/com.renren.mobile.android/icon.jpg',
+ 'id': 1580615,
+ 'name': '皮的嘛',
+ 'packageName': 'com.renren.mobile.android',
+ 'size': 21803987,
+ 'stars': 2},
+ {'des': '斗鱼271934走过路过不要错过,这里有最好的鸡儿',
+ 'downloadUrl': 'app/com.ct.client/com.ct.client.apk',
+ 'iconUrl': 'app/com.ct.client/icon.jpg',
+ 'id': 1540629,
+ 'name': '不存在的',
+ 'packageName': 'com.ct.client',
+ 'size': 4794202,
+ 'stars': 2}]
+ >>>
-带 if/else
+但是很多时候(在公司的一些服务器)你无法选择自己使用哪个版本的
+Python,本来我可以选择不用的,因为有更好的替代方案(\ **这个后面会讲**\ )。
-::
+但是我出于猎奇,正好前两天不是写过一篇关于 编码
+的文章吗,我自认为自己对于
+编码还是掌握比较熟练的,就想着来解决一下这个问题。
- >>>( lambda x, y: x if x < y else y )( 1, 2 )
- 1
+索性就来看下 pprint
+的源代码,还真被我找到了解决方法,如果你也想挑战一下,不防在这里停住,自己研究一下如何实现,我相信对你阅读源码会有帮助。
-嵌套函数
+**以下是我的解决方案,供你参考**\ :
-::
+写一个自己的 printer 对象,继承自 PrettyPrinter (pprint 使用的printer)
- >>>( lambda x: ( lambda y: ( lambda z: x + y + z )( 1 ) )( 2 ) )( 3 )
- 6
+并且复写 format 方法,判断传进来的字符串对象是否 str 类型,如果不是 str
+类型,而是 unicode 类型,就用 uft8 编码成 str 类型。
-递归函数
+.. code:: python
-::
+ # coding: utf-8
+ from pprint import PrettyPrinter
- >>> func = lambda n:1 if n == 0 else n * func(n-1)
- >>> func(5)
- 120
+ # 继承 PrettyPrinter,复写 format 方法
+ class MyPrettyPrinter(PrettyPrinter):
+ def format(self, object, context, maxlevels, level):
+ if isinstance(object, unicode):
+ return (object.encode('utf8'), True, False)
+ return PrettyPrinter.format(self, object, context, maxlevels, level)
-或者
+ info = [{"id":1580615,"name":u"皮的嘛","packageName":"com.renren.mobile.android","iconUrl":"app/com.renren.mobile.android/icon.jpg","stars":2,"size":21803987,"downloadUrl":"app/com.renren.mobile.android/com.renren.mobile.android.apk","des":u"2011-2017你的铁头娃一直在这儿。中国最大的实名制SNS网络平台,嫩头青"},{"id":1540629,"name":u"不存在的","packageName":"com.ct.client","iconUrl":"app/com.ct.client/icon.jpg","stars":2,"size":4794202,"downloadUrl":"app/com.ct.client/com.ct.client.apk","des":u"斗鱼271934走过路过不要错过,这里有最好的鸡儿"}]
-::
+ MyPrettyPrinter().pprint(info)
- >>> f = lambda func, n: 1 if n == 0 else n * func( func, n - 1 )
- >>> f(f,4)
- 24
+输出如下,已经解决了中文的显示问题:
-从以上示例来看,lambda
-表达式和常规的函数相比,写法比较怪异,可读性相对较差。除了可以直接运行之外,好像并没有其他较为突出的功能,为什么在今天我们要介绍它呢?
+|image1|
-首先我们要知道 lambda
-是一个表达式,而不是一个语句。正因为这个特点,我们可以在一些特殊的场景中去使用它。具体是什么场景呢?接下来我们会介绍到几个非常好用的内置函数。
+打印双引号
+~~~~~~~~~~
-1.13.2 map 函数
----------------
+解决了中文问题后,再来看看如何让 pprint 打印双引号。
-map
-函数,它接收两个参数,第一个参数是一个函数对象(当然也可以是一个lambda表达式),第二个参数是一个序列。
+在实例化 PrettyPrinter 对象的时候,可以接收一个 stream
+对象,它表示你要将内容输出到哪里,默认是使用 sys.stdout 这个
+stream,也就是标准输出。
-它可以实现怎样的功能呢,我举个例子你就明白了。
+现在我们要修改输出的内容,也就是将输出的单引号替换成双引号。
-::
+那我们完全可以自己定义一个 stream
+类型的对象,该对象不需要继承任何父类,只要你实现 write 方法就可以。
- >>> map(lambda x: x*2, [1,2,3,4,5])
- [2, 4, 6, 8, 10]
+有了思路,就可以开始写代码了,如下:
-可以很清楚地看到,它可以将后面序列中的每一个元素做为参数传入lambda中。
+.. code:: python
-当我们不使用 map 函数时,你也许会这样子写。
+ # coding: utf-8
+ from pprint import PrettyPrinter
-::
+ class MyPrettyPrinter(PrettyPrinter):
+ def format(self, object, context, maxlevels, level):
+ if isinstance(object, unicode):
+ return (object.encode('utf8'), True, False)
+ return PrettyPrinter.format(self, object, context, maxlevels, level)
+
+ class MyStream():
+ def write(self, text):
+ print text.replace('\'', '"')
+
+ info = [{"id":1580615,"name":u"皮的嘛","packageName":"com.renren.mobile.android","iconUrl":"app/com.renren.mobile.android/icon.jpg","stars":2,"size":21803987,"downloadUrl":"app/com.renren.mobile.android/com.renren.mobile.android.apk","des":u"2011-2017你的铁头娃一直在这儿。中国最大的实名制SNS网络平台,嫩头青"},{"id":1540629,"name":u"不存在的","packageName":"com.ct.client","iconUrl":"app/com.ct.client/icon.jpg","stars":2,"size":4794202,"downloadUrl":"app/com.ct.client/com.ct.client.apk","des":u"斗鱼271934走过路过不要错过,这里有最好的鸡儿"}]
+ MyPrettyPrinter(stream=MyStream()).pprint(info)
+
+尝试执行了下,我的天,怎么是这样子的。
+
+.. code:: json
+
+ [
+ {
+ "des"
+ :
+ 2011-2017你的铁头娃一直在这儿。中国最大的实名制SNS网络平台,嫩头青
+ ,
+ "downloadUrl":
+ "app/com.renren.mobile.android/com.renren.mobile.android.apk"
+ ,
+ "iconUrl":
+ "app/com.renren.mobile.android/icon.jpg"
+ ,
+ "id":
+ 1580615
+ ,
+ "name":
+ 皮的嘛
+ ,
+ "packageName":
+ "com.renren.mobile.android"
+ ,
+ "size":
+ 21803987
+ ,
+ "stars":
+ 2
+ }
+ ,
+
+ {
+ "des"
+ :
+ 斗鱼271934走过路过不要错过,这里有最好的鸡儿
+ ,
+ "downloadUrl":
+ "app/com.ct.client/com.ct.client.apk"
+ ,
+ "iconUrl":
+ "app/com.ct.client/icon.jpg"
+ ,
+ "id":
+ 1540629
+ ,
+ "name":
+ 不存在的
+ ,
+ "packageName":
+ "com.ct.client"
+ ,
+ "size":
+ 4794202
+ ,
+ "stars":
+ 2
+ }
+ ]
- mylist=[]
- for i in [1,2,3,4,5]:
- mylist.append(i*2)
+经过一番研究,才知道是因为 print 函数默认会将打印的内容后面加个
+**换行符**\ 。
-1.13.3 filter 函数
-------------------
+那如何将使 print 函数打印的内容,不进行换行呢?
-filter 函数,和 map 函数相似。同样也是接收两个参数,一个lambda
-表达式,一个序列。它会遍历后面序列中每一个元素,并将其做为参数传入lambda表达式中,当表达式返回
-True,则元素会被保留下来,当表达式返回 False ,则元素会被丢弃。
+方法很简单,但是我相信很多人都不知道,只要在 print 的内容后加一个
+**逗号** 就行。
-下面这个例子,将过滤出一个列表中小于0的元素。
+就像下面这样。
-::
+|image2|
- >>>filter(lambda x: x < 0, range(-5, 5))
- [-5, -4, -3, -2, -1]
+知道了问题所在,再修改下代码
-1.13.4 reduce 函数
-------------------
+.. code:: python
-reduce 函数,也是类似的。它的作用是先对序列中的第 1、2
-个元素进行操作,得到的结果再与第三个数据用 lambda
-函数运算,将其得到的结果再与第四个元素进行运算,以此类推下去直到后面没有元素了。
+ # coding: utf-8
+ from pprint import PrettyPrinter
-|reduce 逻辑演示| 这边举个例子你也就明白了。
+ class MyPrettyPrinter(PrettyPrinter):
+ def format(self, object, context, maxlevels, level):
+ if isinstance(object, unicode):
+ return (object.encode('utf8'), True, False)
+ return PrettyPrinter.format(self, object, context, maxlevels, level)
-::
+ class MyStream():
+ def write(self, text):
+ print text.replace('\'', '"'),
- >>>reduce(lambda x,y: x+y, [1,2,3,4,5])
- 15
+ info = [{"id":1580615,"name":u"皮的嘛","packageName":"com.renren.mobile.android","iconUrl":"app/com.renren.mobile.android/icon.jpg","stars":2,"size":21803987,"downloadUrl":"app/com.renren.mobile.android/com.renren.mobile.android.apk","des":u"2011-2017你的铁头娃一直在这儿。中国最大的实名制SNS网络平台,嫩头青"},{"id":1540629,"name":u"不存在的","packageName":"com.ct.client","iconUrl":"app/com.ct.client/icon.jpg","stars":2,"size":4794202,"downloadUrl":"app/com.ct.client/com.ct.client.apk","des":u"斗鱼271934走过路过不要错过,这里有最好的鸡儿"}]
-它的运算过程分解一下是这样的。
+ MyPrettyPrinter(stream=MyStream()).pprint(info)
-::
+终于成功了,太不容易了吧。
- 1+2=3
- 3+3=6
- 6+4+10
- 10+5=15
+|image3|
-1.13.5 注意点
--------------
+3. 何必折腾
+-----------
-以上几个函数,熟练的掌握它们的写法,可以让我们的代码看起来更加的
-Pythonic ,在某一程度上代码看起来更加的简洁。
+通过上面的一番折腾,我终于实现了我 **梦寐以求** 的需求。
-如果你是新手呢,你需要注意的是,以上示例是在 Python2.x
-环境下演示的。而在 Python3.x 中,却有所不同,你可以自己尝试一下。
+代价就是我整整花费了两个小时,才得以实现,而对于小白来说,可能没有信心,也没有耐心去做这样的事情。
-这里总结一下:
+**所以我想说的是,Python 2 下的 pprint ,真的不要再用了**\ 。
-第一点,map 和 filter
-函数返回的都不再是一个列表,而是一个迭代器对象。这里以map为例
+为什么我要用这么 说,因为明明有更好的替代品,人生苦短,既然用了 Python
+,当然是怎么简单怎么来咯,何必为难自己呢,一行代码可以解决的事情,偏偏要去写两个类,那不是自讨苦吃吗?(我这是在骂自己吗?
-::
+如果你愿意抛弃 pprint ,那我推荐你用 json.dumps ,我保证你再也不想用
+pprint 了。
- >>> map_obj = map(lambda x: x*2, [1,2,3,4,5])
- >>> from collections.abc import Iterator
- >>> isinstance(map_obj, Iterator)
- True
- >>> next(map_obj)
- 2
- >>> list(map_obj)
- [4, 6, 8, 10]
+.. _打印中文-1:
-第二点,reduce 不可以直接调用,而是要先导入才能使用,
+打印中文
+~~~~~~~~
-::
+其实无法打印中文,是 Python 2 引来的大坑,并不能全怪 pprint 。
+
+但是同样的问题,在 json.dumps 这里,却只要加个参数就好了,可比 pprint
+简单得不要太多。
+
+具体的代码示例如下:
+
+.. code:: python
+
+ >>> info = [{"id":1580615,"name":"皮的嘛","packageName":"com.renren.mobile.android","iconUrl":"app/com.renren.mobile.android/icon.jpg","stars":2,"size":21803987,"downloadUrl":"app/com.renren.mobile.android/com.renren.mobile.android.apk","des":"2011-2017你的铁头娃一直在这儿。中国最大的实名制SNS网络平台,嫩头青"},{"id":1540629,"name":"不存在的","packageName":"com.ct.client","iconUrl":"app/com.ct.client/icon.jpg","stars":2,"size":4794202,"downloadUrl":"app/com.ct.client/com.ct.client.apk","des":"斗鱼271934走过路过不要错过,这里有最好的鸡儿"}]
+ >>>
+ >>> import json
+ >>>
+ >>>
+ >>> print json.dumps(info, indent=4, ensure_ascii=False)
+ [
+ {
+ "downloadUrl": "app/com.renren.mobile.android/com.renren.mobile.android.apk",
+ "iconUrl": "app/com.renren.mobile.android/icon.jpg",
+ "name": "皮的嘛",
+ "stars": 2,
+ "packageName": "com.renren.mobile.android",
+ "des": "2011-2017你的铁头娃一直在这儿。中国最大的实名制SNS网络平台,嫩头青",
+ "id": 1580615,
+ "size": 21803987
+ },
+ {
+ "downloadUrl": "app/com.ct.client/com.ct.client.apk",
+ "iconUrl": "app/com.ct.client/icon.jpg",
+ "name": "不存在的",
+ "stars": 2,
+ "packageName": "com.ct.client",
+ "des": "斗鱼271934走过路过不要错过,这里有最好的鸡儿",
+ "id": 1540629,
+ "size": 4794202
+ }
+ ]
+ >>>
+
+json.dumps 的关键参数有两个:
+
+- **indent=4**\ :以 4 个空格缩进单位
+- **ensure_ascii=False**\ :接收非 ASCII 编码的字符,这样才能使用中文
+
+与 pprint 相比 json.dumps 可以说完胜:
+
+1. 两个参数就能实现所有我的需求(打印中文与双引号)
+2. 就算在 Python 2 下,使用中文也不需要用 ``u'中文'`` 这种写法
+3. Python2 和 Python3 的写法完全一致,对于这一点不需要考虑兼容问题
+
+4. 总结一下
+-----------
+
+本来很简单的一个观点,我为了证明 pprint
+实现那两个需求有多么困难,花了很多的时间去研究了 pprint
+的源码(各种处理其实还是挺复杂的),不过好在最后也能有所收获。
- from functools import reduce
+本文的分享就到这里,阅读本文,我认为你可以获取到三个知识点
---------------
+1. 核心观点:Python2 下不要再使用 pprint
+2. 若真要使用,且有和一样的改造需求,可以参考我的实现
+3. Python 2 中的 print 语句后居然可以加 逗号
-.. figure:: http://image.python-online.cn/image-20200320125724880.png
- :alt: 关注公众号,获取最新干货!
+以上。希望此文能对你有帮助。
- 关注公众号,获取最新干货!
+|image4|
-.. |reduce 逻辑演示| image:: https://ws1.sinaimg.cn/large/8f640247gy1fyx6i8q3anj208c04u3yu.jpg
+.. |image0| image:: http://image.iswbm.com/20200602135014.png
+.. |image1| image:: http://image.iswbm.com/20200507171451.png
+.. |image2| image:: http://image.iswbm.com/20200507174459.png
+.. |image3| image:: http://image.iswbm.com/20200507174802.png
+.. |image4| image:: http://image.iswbm.com/20200607174235.png
diff --git a/source/c01/c01_14.md b/source/c01/c01_14.md
index cf90646..d47444a 100644
--- a/source/c01/c01_14.md
+++ b/source/c01/c01_14.md
@@ -1,6 +1,6 @@
# 1.14 with 与 上下文管理器
-> 提示:前面的内容较为基础,重点知识在后半段。
+
`with` 这个关键字,对于每一学习Python的人,都不会陌生。
@@ -11,7 +11,7 @@ with open('test.txt') as f:
print f.readlines()
```
-## 1.14.1 what context manager?
+## 1. what context manager?
**基本语法**
@@ -28,7 +28,7 @@ with EXPR as VAR:
3. f 不是上下文管理器,应该是资源对象。
```
-## 1.14.2 how context manager?
+## 2. how context manager?
要自己实现这样一个上下文管理,要先知道上下文管理协议。
@@ -61,7 +61,7 @@ with Resource() as res:
从这个示例可以很明显的看出,在编写代码时,可以将资源的连接或者获取放在`__enter__`中,而将资源的关闭写在`__exit__` 中。
-## 1.14.3 why context manager?
+## 3. why context manager?
学习时多问自己几个为什么,养成对一些细节的思考,有助于加深对知识点的理解。
@@ -111,7 +111,7 @@ with Resource() as res:
当主逻辑代码没有报异常时,这三个参数将都为None。
-## 1.14.4 how contextlib?
+## 4. how contextlib?
在上面的例子中,我们只是为了构建一个上下文管理器,却写了一个类。如果只是要实现一个简单的功能,写一个类未免有点过于繁杂。这时候,我们就想,如果只写一个函数就可以实现上下文管理器就好了。
@@ -185,7 +185,7 @@ with open_func('/Users/MING/mytest.txt') as file_in:
代码是这样的
-
+
总结起来,使用上下文管理器有三个好处:
@@ -195,4 +195,4 @@ with open_func('/Users/MING/mytest.txt') as file_in:
------
-
+
diff --git a/source/c01/c01_14.rst b/source/c01/c01_14.rst
index 263231c..bb99084 100644
--- a/source/c01/c01_14.rst
+++ b/source/c01/c01_14.rst
@@ -1,7 +1,7 @@
1.14 with 与 上下文管理器
=========================
- 提示:前面的内容较为基础,重点知识在后半段。
+|image0|
``with`` 这个关键字,对于每一学习Python的人,都不会陌生。
@@ -13,8 +13,8 @@
with open('test.txt') as f:
print f.readlines()
-1.14.1 what context manager?
------------------------------
+1. what context manager?
+-------------------------
**基本语法**
@@ -31,8 +31,8 @@
2. 上下文管理器:open('test.txt')
3. f 不是上下文管理器,应该是资源对象。
-1.14.2 how context manager?
-----------------------------
+2. how context manager?
+------------------------
要自己实现这样一个上下文管理,要先知道上下文管理协议。
@@ -66,8 +66,8 @@
从这个示例可以很明显的看出,在编写代码时,可以将资源的连接或者获取放在\ ``__enter__``\ 中,而将资源的关闭写在\ ``__exit__``
中。
-1.14.3 why context manager?
-----------------------------
+3. why context manager?
+------------------------
学习时多问自己几个为什么,养成对一些细节的思考,有助于加深对知识点的理解。
@@ -122,8 +122,8 @@ Python解释器,这个异常我们已经捕获了,不需要再往外抛了
当主逻辑代码没有报异常时,这三个参数将都为None。
-1.14.4 how contextlib?
-----------------------
+4. how contextlib?
+------------------
在上面的例子中,我们只是为了构建一个上下文管理器,却写了一个类。如果只是要实现一个简单的功能,写一个类未免有点过于繁杂。这时候,我们就想,如果只写一个函数就可以实现上下文管理器就好了。
@@ -199,7 +199,7 @@ open)的上下文管理器。
代码是这样的
-|image0|
+|image1|
总结起来,使用上下文管理器有三个好处:
@@ -209,10 +209,9 @@ open)的上下文管理器。
--------------
-.. figure:: http://image.python-online.cn/image-20200320125724880.png
- :alt: 关注公众号,获取最新干货!
-
- 关注公众号,获取最新干货!
+|image2|
-.. |image0| image:: http://image.python-online.cn/20190310172800.png
+.. |image0| image:: http://image.iswbm.com/20200602135014.png
+.. |image1| image:: http://image.iswbm.com/20190310172800.png
+.. |image2| image:: http://image.iswbm.com/20200607174235.png
diff --git a/source/c01/c01_15.md b/source/c01/c01_15.md
index fa232e8..d6561cd 100644
--- a/source/c01/c01_15.md
+++ b/source/c01/c01_15.md
@@ -1,71 +1,49 @@
-# 1.15 提升Python性能的7个习惯
+# 1.21 Python 开发的几个小 Tips
----
+## 1. 重定向标准输出到文件
-> 转载自:https://zhuanlan.zhihu.com/p/38160586
-
-## 1.15.1 使用局部变量
-
-尽量使用局部变量代替全局变量:便于维护,提高性能并节省内存。
-
-使用局部变量替换模块名字空间中的变量,例如 ls = os.linesep。一方面可以提高程序性能,局部变量查找速度更快;另一方面可用简短标识符替代冗长的模块变量,提高可读性。
-
-## 1.15.2 减少函数调用次数
-
-对象类型判断时,采用isinstance()最优,采用对象类型身份(id())次之,采用对象值(type())比较最次。
-
-```
-#判断变量num是否为整数类型type(num) == type(0) #调用三次函数type(num) is type(0) #身份比较isinstance(num,(int)) #调用一次函数
-```
-
-不要在重复操作的内容作为参数放到循环条件中,避免重复运算。
-
-```
-#每次循环都需要重新执行len(a)while i < len(a): statement#len(a)仅执行一次m = len(a)while i < m: statement
```
+import contextlib
-如需使用模块X中的某个函数或对象Y,应直接使用from X import Y,而不是import X; X.Y。这样在使用Y时,可以减少一次查询(解释器不必首先查找到X模块,然后在X模块的字典中查找Y)。
+def unshelve_task():
+ pass
-## 1.15.3 采用映射替代条件查找
+@contextlib.contextmanager
+def close_stdout():
+ raw_stdout = sys.stdout
+ file = open(log_file, 'a+')
+ sys.stdout = file
-映射(比如dict等)的搜索速度远快于条件语句(如if等)。Python中也没有select-case语句。
+ yield
+ sys.stdout = raw_stdout
+ file.close()
+
+with close_stdout():
+ unshelve_task()
```
-#if查找if a == 1: b = 10elif a == 2: b = 20...#dict查找,性能更优d = {1:10,2:20,...}b = d[a]
-```
-## 1.15.4 直接迭代序列元素
+## 2. 将子网掩码转换为cidr
+
+如何使用netaddr库将ipv4子网掩码转换为cidr表示法?
+示例:255.255.255.0到/ 24
-对序列(str、list、tuple等),直接迭代序列元素,比迭代元素的索引速度要更快。
+使用netaddr:
```
-a = [1,2,3]#迭代元素for item in a: print(item)#迭代索引for i in range(len(a)): print(a[i])
+>>> from netaddr import IPAddress
+>>> IPAddress("255.255.255.0").netmask_bits()
+24
```
-## 1.15.5 采用生成器表达式替代列表解析
-
-列表解析(list comprehension),会产生整个列表,对大量数据的迭代会产生负面效应。
-
-而生成器表达式则不会,其不会真正创建列表,而是返回一个生成器,在需要时产生一个值(延迟计算),对内存更加友好。
+您也可以在不使用任何库的情况下执行此操作,只需在网络掩码的二进制表示中计算1位:
```
-#计算文件f的非空字符个数#生成器表达式l = sum([len(word) for line in f for word in line.split()])#列表解析l = sum(len(word) for line in f for word in line.split())
+>>> netmask = "255.255.255.0"
+>>> sum([bin(int(x)).count("1") for x in netmask.split(".")])
+24
```
-## 1.15.6 先编译后调用
-
-使用eval()、exec()函数执行代码时,最好调用代码对象(提前通过compile()函数编译成字节码),而不是直接调用str,可以避免多次执行重复编译过程,提高程序性能。
-
-正则表达式模式匹配也类似,也最好先将正则表达式模式编译成regex对象(通过re.complie()函数),然后再执行比较和匹配。
-
-## 1.15.7 模块编程习惯
-
-模块中的最高级别Python语句(没有缩进的代码)会在模块导入(import)时执行(不论其是否真的必要执行)。因此,应尽量将模块所有的功能代码放到函数中,包括主程序相关的功能代码也可放到main()函数中,主程序本身调用main()函数。
-
-可以在模块的main()函数中书写测试代码。在主程序中,检测name的值,如果为'main'(表示模块是被直接执行),则调用main()函数,进行测试;如果为模块名字(表示模块是被调用),则不进行测试。
-
-
-------
-
+
\ No newline at end of file
diff --git a/source/c01/c01_15.rst b/source/c01/c01_15.rst
index d4a43ca..97bab2d 100644
--- a/source/c01/c01_15.rst
+++ b/source/c01/c01_15.rst
@@ -1,84 +1,53 @@
-1.15 提升Python性能的7个习惯
-============================
+1.21 Python 开发的几个小 Tips
+=============================
---------------
-
- 转载自:https://zhuanlan.zhihu.com/p/38160586
-
-1.15.1 使用局部变量
--------------------
-
-尽量使用局部变量代替全局变量:便于维护,提高性能并节省内存。
-
-使用局部变量替换模块名字空间中的变量,例如 ls =
-os.linesep。一方面可以提高程序性能,局部变量查找速度更快;另一方面可用简短标识符替代冗长的模块变量,提高可读性。
-
-1.15.2 减少函数调用次数
+1. 重定向标准输出到文件
-----------------------
-对象类型判断时,采用isinstance()最优,采用对象类型身份(id())次之,采用对象值(type())比较最次。
-
::
- #判断变量num是否为整数类型type(num) == type(0) #调用三次函数type(num) is type(0) #身份比较isinstance(num,(int)) #调用一次函数
+ import contextlib
-不要在重复操作的内容作为参数放到循环条件中,避免重复运算。
+ def unshelve_task():
+ pass
-::
-
- #每次循环都需要重新执行len(a)while i < len(a): statement#len(a)仅执行一次m = len(a)while i < m: statement
-
-如需使用模块X中的某个函数或对象Y,应直接使用from X import
-Y,而不是import X;
-X.Y。这样在使用Y时,可以减少一次查询(解释器不必首先查找到X模块,然后在X模块的字典中查找Y)。
+ @contextlib.contextmanager
+ def close_stdout():
+ raw_stdout = sys.stdout
+ file = open(log_file, 'a+')
+ sys.stdout = file
-1.15.3 采用映射替代条件查找
----------------------------
+ yield
-映射(比如dict等)的搜索速度远快于条件语句(如if等)。Python中也没有select-case语句。
-
-::
+ sys.stdout = raw_stdout
+ file.close()
+
+ with close_stdout():
+ unshelve_task()
- #if查找if a == 1: b = 10elif a == 2: b = 20...#dict查找,性能更优d = {1:10,2:20,...}b = d[a]
-
-1.15.4 直接迭代序列元素
+2. 将子网掩码转换为cidr
-----------------------
-对序列(str、list、tuple等),直接迭代序列元素,比迭代元素的索引速度要更快。
-
-::
-
- a = [1,2,3]#迭代元素for item in a: print(item)#迭代索引for i in range(len(a)): print(a[i])
-
-1.15.5 采用生成器表达式替代列表解析
------------------------------------
+如何使用netaddr库将ipv4子网掩码转换为cidr表示法? 示例:255.255.255.0到/
+24
-列表解析(list
-comprehension),会产生整个列表,对大量数据的迭代会产生负面效应。
-
-而生成器表达式则不会,其不会真正创建列表,而是返回一个生成器,在需要时产生一个值(延迟计算),对内存更加友好。
+使用netaddr:
::
- #计算文件f的非空字符个数#生成器表达式l = sum([len(word) for line in f for word in line.split()])#列表解析l = sum(len(word) for line in f for word in line.split())
-
-1.15.6 先编译后调用
--------------------
-
-使用eval()、exec()函数执行代码时,最好调用代码对象(提前通过compile()函数编译成字节码),而不是直接调用str,可以避免多次执行重复编译过程,提高程序性能。
+ >>> from netaddr import IPAddress
+ >>> IPAddress("255.255.255.0").netmask_bits()
+ 24
-正则表达式模式匹配也类似,也最好先将正则表达式模式编译成regex对象(通过re.complie()函数),然后再执行比较和匹配。
+您也可以在不使用任何库的情况下执行此操作,只需在网络掩码的二进制表示中计算1位:
-1.15.7 模块编程习惯
--------------------
-
-模块中的最高级别Python语句(没有缩进的代码)会在模块导入(import)时执行(不论其是否真的必要执行)。因此,应尽量将模块所有的功能代码放到函数中,包括主程序相关的功能代码也可放到main()函数中,主程序本身调用main()函数。
+::
-可以在模块的main()函数中书写测试代码。在主程序中,检测name的值,如果为’main’(表示模块是被直接执行),则调用main()函数,进行测试;如果为模块名字(表示模块是被调用),则不进行测试。
+ >>> netmask = "255.255.255.0"
+ >>> sum([bin(int(x)).count("1") for x in netmask.split(".")])
+ 24
---------------
+|image0|
-.. figure:: http://image.python-online.cn/image-20200320125724880.png
- :alt: 关注公众号,获取最新干货!
+.. |image0| image:: http://image.iswbm.com/20200607174235.png
- 关注公众号,获取最新干货!
diff --git a/source/c01/c01_16.md b/source/c01/c01_16.md
index 576eedf..11847b4 100644
--- a/source/c01/c01_16.md
+++ b/source/c01/c01_16.md
@@ -1,5 +1,7 @@
# 1.16 泛型函数怎么写?
+
+
泛型,如果你尝过java,应该对他不陌生吧。但你可能不知道在 Python 中(3.4+ ),也可以实现 简单的泛型函数。
在Python中只能实现基于单个(第一个)参数的数据类型来选择具体的实现方式,官方名称 是 `single-dispatch`。你或许听不懂,说人话,就是可以实现第一个参数的数据类型不同,其调用的函数也就不同。
@@ -159,4 +161,4 @@ hello, world
------
-
+
diff --git a/source/c01/c01_16.rst b/source/c01/c01_16.rst
index ab6c40b..50c0dcd 100644
--- a/source/c01/c01_16.rst
+++ b/source/c01/c01_16.rst
@@ -1,6 +1,8 @@
1.16 泛型函数怎么写?
=====================
+|image0|
+
泛型,如果你尝过java,应该对他不陌生吧。但你可能不知道在 Python 中(3.4+
),也可以实现 简单的泛型函数。
@@ -166,7 +168,8 @@
--------------
-.. figure:: http://image.python-online.cn/image-20200320125724880.png
- :alt: 关注公众号,获取最新干货!
+|image1|
+
+.. |image0| image:: http://image.iswbm.com/20200602135014.png
+.. |image1| image:: http://image.iswbm.com/20200607174235.png
- 关注公众号,获取最新干货!
diff --git a/source/c01/c01_17.md b/source/c01/c01_17.md
index 84c494e..9e13038 100644
--- a/source/c01/c01_17.md
+++ b/source/c01/c01_17.md
@@ -1,528 +1,274 @@
-# 1.17 深入理解「描述符」
+# 1.17 详解 Python 中的编码问题
-学习 Python 这么久了,说起 Python 的优雅之处,能让我脱口而出的, Descriptor(描述符)特性可以排得上号。
+
-描述符 是Python 语言独有的特性,它不仅在应用层使用,在语言的基础设施中也有涉及。
+Python 中编码问题,一直是很多 Python 开发者的噩梦,尽管你是工作多年的 Python 开发者,也肯定会经常遇到令人神烦的编码问题,好不容易花了半天搞明白了。
-我可以大胆地猜测,你对于描述符的了解是始于诸如 Django ORM 和 SQLAlchemy 中的字段对象,是的,它们都是描述符。你的它的认识,可能也止步于此,如果你没有去深究,它为何要如此设计?也就加体会不到 Python 给我们带来的便利与优雅。
+一段时间后,又全都忘光光了,一脸懵逼的你又开始你找各种博客、帖子,从头搞清楚什么是编码?什么是 unicode?它和 ASCII 有什么区别?为什么 decode encode 老是报错?python2 里和 python3 的字符串类型怎么都不一样,怎么对应起来?如何检测编码格式?
-由于 描述符的内容较多,长篇大论,容易让你倦怠,所以我打算分几篇来讲。
+反反复复,这个过程真是太痛苦了。
-## 1.17.1 为什么要使用描述符?
+今天我把大家在 Python 上会遇到的一些编码问题都讲清楚了,以后你可以不用再 Google,收藏这篇文章就行。
-假想你正在给学校写一个成绩管理系统,并没有太多编码经验的你,可能会这样子写。
-```python
-class Student:
- def __init__(self, name, math, chinese, english):
- self.name = name
- self.math = math
- self.chinese = chinese
- self.english = english
-
- def __repr__(self):
- return "".format(
- self.name, self.math, self.chinese, self.english
- )
-```
-看起来一切都很合理
+## 1. Python 3 中 str 与 bytes
-```python
->>> std1 = Student('小明', 76, 87, 68)
->>> std1
-
-```
+在 Python3中,字符串有两种类型 ,str 和 bytes。
+
+今天就来说一说这二者的区别:
-但是程序并不像人那么智能,不会自动根据使用场景判断数据的合法性,如果老师在录入成绩的时候,不小心录入了将成绩录成了负数,或者超过100,程序是无法感知的。
+- `unicode string(str 类型)`:以 Unicode code points 形式存储,**人类认识的形式**
+- `byte string(bytes 类型)`:以 byte 形式存储,**机器认识的形式**
-聪明的你,马上在代码中加入了判断逻辑。
+在 Python 3 中你定义的所有字符串,都是 unicode string类型,使用 `type` 和 `isinstance` 可以判别
```python
-class Student:
- def __init__(self, name, math, chinese, english):
- self.name = name
- if 0 <= math <= 100:
- self.math = math
- else:
- raise ValueError("Valid value must be in [0, 100]")
-
- if 0 <= chinese <= 100:
- self.chinese = chinese
- else:
- raise ValueError("Valid value must be in [0, 100]")
-
- if 0 <= chinese <= 100:
- self.english = english
- else:
- raise ValueError("Valid value must be in [0, 100]")
-
-
- def __repr__(self):
- return "".format(
- self.name, self.math, self.chinese, self.english
- )
+# python3
+
+>>> str_obj = "你好"
+>>>
+>>> type(str_obj)
+
+>>>
+>>> isinstance("你好", str)
+True
+>>>
+>>> isinstance("你好", bytes)
+False
+>>>
```
-这下程序稍微有点人工智能了,能够自己明辨是非了。
+而 bytes 是一个二进制序列对象,你只要你在定义字符串时前面加一个 `b`,就表示你要定义一个 bytes 类型的字符串对象。
-
+```python
+# python3
+>>> byte_obj = b"Hello World!"
+>>> type(byte_obj)
+
+>>>
+>>> isinstance(byte_obj, str)
+False
+>>>
+>>> isinstance(byte_obj, bytes)
+True
+>>>
+```
-程序是智能了,但在`__init__`里有太多的判断逻辑,很影响代码的可读性。巧的是,你刚好学过 Property 特性,可以很好的应用在这里。于是你将代码修改成如下,代码的可读性瞬间提升了不少
+但是在定义中文字符串时,你就不能直接在前面加 `b` 了,而应该使用 `encode` 转一下。
```python
-class Student:
- def __init__(self, name, math, chinese, english):
- self.name = name
- self.math = math
- self.chinese = chinese
- self.english = english
-
- @property
- def math(self):
- return self._math
-
- @math.setter
- def math(self, value):
- if 0 <= value <= 100:
- self._math = value
- else:
- raise ValueError("Valid value must be in [0, 100]")
-
- @property
- def chinese(self):
- return self._chinese
-
- @chinese.setter
- def chinese(self, value):
- if 0 <= value <= 100:
- self._chinese = value
- else:
- raise ValueError("Valid value must be in [0, 100]")
-
- @property
- def english(self):
- return self._english
-
- @english.setter
- def english(self, value):
- if 0 <= value <= 100:
- self._english = value
- else:
- raise ValueError("Valid value must be in [0, 100]")
-
- def __repr__(self):
- return "".format(
- self.name, self.math, self.chinese, self.english
- )
+>>> byte_obj=b"你好"
+ File "", line 1
+SyntaxError: bytes can only contain ASCII literal characters.
+>>>
+>>> str_obj="你好"
+>>>
+>>> str_obj.encode("utf-8")
+b'\xe4\xbd\xa0\xe5\xa5\xbd'
+>>>
```
-程序还是一样的人工智能,非常好。
-
-
+## 2. Python 2 中 str 与 unicode
-你以为你写的代码,已经非常优秀,无懈可击了。
+而在 Python2 中,字符串的类型又与 Python3 不一样,需要仔细区分。
-没想到,人外有天,你的主管看了你的代码后,深深地叹了口气:类里的三个属性,math、chinese、english,都使用了 Property 对属性的合法性进行了有效控制。功能上,没有问题,但就是太啰嗦了,三个变量的合法性逻辑都是一样的,只要大于0,小于100 就可以,代码重复率太高了,这里三个成绩还好,但假设还有地理、生物、历史、化学等十几门的成绩呢,这代码简直没法忍。去了解一下 Python 的描述符吧。
+在 Python2 里,字符串也只有两种类型,unicode 和 str 。
-经过主管的指点,你知道了「描述符」这个东西。怀着一颗敬畏之心,你去搜索了下关于 描述符的用法。
+只有 unicode object 和 非unicode object(其实应该叫 str object) 的区别:
-其实也很简单,一个实现了 `描述符协议` 的类就是一个描述符。
+- `unicode string(unicode类型)`:以 Unicode code points 形式存储,**人类认识的形式**
+- `byte string(str 类型)`:以 byte 形式存储,**机器认识的形式**
-什么描述符协议:实现了 `__get__()`、`__set__()`、`__delete__()` 其中至少一个方法的类,就是一个描述符。
+当我们直接使用双引号或单引号包含字符的方式来定义字符串时,就是 str 字符串对象,比如这样
-- `__get__`: 用于访问属性。它返回属性的值,若属性不存在、不合法等都可以抛出对应的异常。
-- `__set__ `:将在属性分配操作中调用。不会返回任何内容。
-- `__delete__ `:控制删除操作。不会返回内容。
+```python
+# python2
-对描述符有了大概的了解后,你开始重写上面的方法。
+>>> str_obj="你好"
+>>>
+>>> type(str_obj)
+
+>>>
+>>> str_obj
+'\xe4\xbd\xa0\xe5\xa5\xbd'
+>>>
+>>> isinstance(str_obj, bytes)
+True
+>>> isinstance(str_obj, str)
+True
+>>> isinstance(str_obj, unicode)
+False
+>>>
+>>> str is bytes
+True
+```
-如前所述,Score 类是一个描述器,当从 Student 的实例访问 math、chinese、english这三个属性的时候,都会经过 Score 类里的三个特殊的方法。这里的 Score 避免了 使用Property 出现大量的代码无法复用的尴尬。
+而当我们在双引号或单引号前面加个 `u`,就表明我们定义的是 unicode 字符串对象,比如这样
```python
-class Score:
- def __init__(self, default=0):
- self._score = default
-
- def __set__(self, instance, value):
- if not isinstance(value, int):
- raise TypeError('Score must be integer')
- if not 0 <= value <= 100:
- raise ValueError('Valid value must be in [0, 100]')
-
- self._score = value
-
- def __get__(self, instance, owner):
- return self._score
-
- def __delete__(self):
- del self._score
-
-class Student:
- math = Score(0)
- chinese = Score(0)
- english = Score(0)
-
- def __init__(self, name, math, chinese, english):
- self.name = name
- self.math = math
- self.chinese = chinese
- self.english = english
-
-
- def __repr__(self):
- return "".format(
- self.name, self.math, self.chinese, self.english
- )
-```
+# python2
-实现的效果和前面的一样,可以对数据的合法性进行有效控制(字段类型、数值区间等)
-
-
+>>> unicode_obj = u"你好"
+>>>
+>>> unicode_obj
+u'\u4f60\u597d'
+>>>
+>>> type(unicode_obj)
+
+>>>
+>>> isinstance(unicode_obj, bytes)
+False
+>>> isinstance(unicode_obj, str)
+False
+>>>
+>>> isinstance(unicode_obj, unicode)
+True
+```
-以上,我举了下具体的实例,从最原始的编码风格到 Property ,最后引出描述符。由浅入深,一步一步带你感受到描述符的优雅之处。
-通过此文,你需要记住的只有一点,就是描述符给我们带来的编码上的便利,它在实现 `保护属性不受修改`、`属性类型检查` 的基本功能,同时有大大提高代码的复用率。
----
+## 3. 如何检测对象的编码
-## 1.17.2 描述符的访问规则
+所有的字符,在 unicode 字符集中都有对应的编码值(英文叫做:`code point`)
-描述符分两种:
+而把这些编码值按照一定的规则保存成二进制字节码,就是我们说的编码方式,常见的有:UTF-8,GB2312 等。
-- 数据描述符:实现了`__get__` 和 `__set__` 两种方法的描述符
-- 非数据描述符:只实现了`__get__` 一种方法的描述符
+也就是说,当我们要将内存中的字符串持久化到硬盘中的时候,都要指定编码方法,而反过来,读取的时候,也要指定正确的编码方法(这个过程叫解码),不然会出现乱码。
-你一定会问,他们有什么区别呢?网上的讲解,我看过几个,很多都把一个简单的东西讲得复杂了。
+那问题就来了,当我们知道了其对应的编码方法,我们就可以正常解码,但并不是所有时候我们都能知道应该用什么编码方式去解码?
-其实就一句话,**数据描述器和非数据描述器的区别在于:它们相对于实例的字典的优先级不同**。
+这时候就要介绍到一个 python 的库 -- `chardet` ,使用它之前 需要先安装
-如果实例字典中有与描述器同名的属性,如果描述器是数据描述器,优先使用数据描述器,如果是非数据描述器,优先使用字典中的属性。
+```
+python3 -m pip install chardet
+```
-这边还是以上节的成绩管理的例子来说明,方便你理解。
+chardet 有一个 detect 方法,可以 `预测`其其编码格式
```python
-# 数据描述符
-class DataDes:
- def __init__(self, default=0):
- self._score = default
-
- def __set__(self, instance, value):
- self._score = value
-
- def __get__(self, instance, owner):
- print("访问数据描述符里的 __get__")
- return self._score
-
-# 非数据描述符
-class NoDataDes:
- def __init__(self, default=0):
- self._score = default
-
- def __get__(self, instance, owner):
- print("访问非数据描述符里的 __get__")
- return self._score
-
-
-class Student:
- math = DataDes(0)
- chinese = NoDataDes(0)
-
- def __init__(self, name, math, chinese):
- self.name = name
- self.math = math
- self.chinese = chinese
-
- def __getattribute__(self, item):
- print("调用 __getattribute__")
- return super(Student, self).__getattribute__(item)
-
- def __repr__(self):
- return "".format(
- self.name, self.math, self.chinese)
+>>> import chardet
+>>> chardet.detect('微信公众号:Python编程时光'.encode('gbk'))
+{'encoding': 'GB2312', 'confidence': 0.99, 'language': 'Chinese'}
```
-需要注意的是,math 是数据描述符,而 chinese 是非数据描述符。从下面的验证中,可以看出,当实例属性和数据描述符同名时,会优先访问数据描述符(如下面的math),而当实例属性和非数据描述符同名时,会优先访问实例属性(`__getattribute__`)
+为什么说是预测呢,通过上面的输出来看,你会看到有一个 confidence 字段,其表示预测的可信度,或者说成功率。
+
+但是使用它时,若你的字符数较少,就有可能 “`误诊`”),比如只有 `中文` 两个字,就像下面这样,我们是 使用 gbk 编码的,使用 chardet 却识别成 KOI8-R 编码。
```python
->>> std = Student('xm', 88, 99)
+>>> str_obj = "中文"
+>>> byte_obj = bytes(a, encoding='gbk') # 先得到一个 gbk 编码的 bytes
+>>>
+>>> chardet.detect(byte_obj)
+{'encoding': 'KOI8-R', 'confidence': 0.682639754276994, 'language': 'Russian'}
>>>
->>> std.math
-调用 __getattribute__
-访问数据描述符里的 __get__
-88
->>> std.chinese
-调用 __getattribute__
-99
+>>> str_obj2 = str(byte_obj, encoding='KOI8-R')
+>>> str_obj2
+'жпнд'
```
-讲完了数据描述符和非数据描述符,我们还需要了解的对象属性的查找规律。
+所以为了编码诊断的准确,要尽量使用足够多的字符。
-当我们对一个实例属性进行访问时,Python 会按 `obj.__dict__` → `type(obj).__dict__` → `type(obj)的父类.__dict__` 顺序进行查找,如果查找到目标属性并发现是一个描述符,Python 会调用描述符协议来改变默认的控制行为。
+chardet 支持多国的语言,从官方文档中可以看到支持如下这些语言(https://chardet.readthedocs.io/en/latest/supported-encodings.html)
-## 1.17.3 基于描述符如何实现property
+
-经过上面的讲解,我们已经知道如何定义描述符,且明白了描述符是如何工作的。
-正常人所见过的描述符的用法就是上篇文章提到的那些,我想说的是那只是描述符协议最常见的应用之一,或许你还不知道,其实有很多 Python 的特性的底层实现机制都是基于 `描述符协议` 的,比如我们熟悉的`@property` 、`@classmethod` 、`@staticmethod` 和 `super` 等。
-先来说说 `property` 吧。
+## 4. 编码与解码的区别
-有了第一篇的基础,我们知道了 property 的基本用法。这里我直接切入主题,从第一篇的例子里精简了一下。
+编码和解码,其实就是 str 与 bytes 的相互转化的过程(Python 2 已经远去,这里以及后面都只用 Python 3 举例)
-```python
-class Student:
- def __init__(self, name):
- self.name = name
-
- @property
- def math(self):
- return self._math
-
- @math.setter
- def math(self, value):
- if 0 <= value <= 100:
- self._math = value
- else:
- raise ValueError("Valid value must be in [0, 100]")
-```
+- **编码**:encode 方法,把字符串对象转化为二进制字节序列
-不防再简单回顾一下它的用法,通过property装饰的函数,如例子中的 math 会变成 Student 实例的属性。而对 math 属性赋值会进入 使用 `math.setter` 装饰函数的逻辑代码块。
+- **解码**:decode 方法,把二进制字节序列转化为字符串对象
-为什么说 property 底层是基于描述符协议的呢?通过 PyCharm 点击进入 property 的源码,很可惜,只是一份类似文档一样的伪源码,并没有其具体的实现逻辑。
+
-不过,从这份伪源码的魔法函数结构组成,可以大体知道其实现逻辑。
-这里我自己通过模仿其函数结构,结合「描述符协议」来自己实现类 `property` 特性。
-代码如下:
+那么假如我们真知道了其编码格式,如何来转成 unicode 呢?
-```python
-class TestProperty(object):
-
- def __init__(self, fget=None, fset=None, fdel=None, doc=None):
- self.fget = fget
- self.fset = fset
- self.fdel = fdel
- self.__doc__ = doc
-
- def __get__(self, obj, objtype=None):
- print("in __get__")
- if obj is None:
- return self
- if self.fget is None:
- raise AttributeError
- return self.fget(obj)
-
- def __set__(self, obj, value):
- print("in __set__")
- if self.fset is None:
- raise AttributeError
- self.fset(obj, value)
-
- def __delete__(self, obj):
- print("in __delete__")
- if self.fdel is None:
- raise AttributeError
- self.fdel(obj)
-
-
- def getter(self, fget):
- print("in getter")
- return type(self)(fget, self.fset, self.fdel, self.__doc__)
-
- def setter(self, fset):
- print("in setter")
- return type(self)(self.fget, fset, self.fdel, self.__doc__)
-
- def deleter(self, fdel):
- print("in deleter")
- return type(self)(self.fget, self.fset, fdel, self.__doc__)
-```
+**有两种方法**
-然后 Student 类,我们也相应改成如下
+**第一种**是,直接使用 decode 方法
```python
-class Student:
- def __init__(self, name):
- self.name = name
-
- # 其实只有这里改变
- @TestProperty
- def math(self):
- return self._math
-
- @math.setter
- def math(self, value):
- if 0 <= value <= 100:
- self._math = value
- else:
- raise ValueError("Valid value must be in [0, 100]")
+>>> byte_obj.decode('gbk')
+'中文'
+>>>
```
-为了尽量让你少产生一点疑惑,我这里做两点说明:
-
-1. 使用`TestProperty`装饰后,`math` 不再是一个函数,而是`TestProperty` 类的一个实例。所以第二个math函数可以使用 `math.setter` 来装饰,本质是调用`TestProperty.setter` 来产生一个新的 `TestProperty` 实例赋值给第二个`math`。
-
-2. 第一个 `math` 和第二个 `math` 是两个不同 `TestProperty` 实例。但他们都属于同一个描述符类(TestProperty),当对 math 对于赋值时,就会进入 `TestProperty.__set__`,当对math 进行取值里,就会进入 `TestProperty.__get__`。仔细一看,其实最终访问的还是Student实例的 `_math` 属性。
-
-说了这么多,还是运行一下,更加直观一点。
+**第二种**是,使用 str 类来转
```python
-# 运行后,会直接打印这一行,这是在实例化 TestProperty 并赋值给第二个math
-in setter
->>>
->>> s1.math = 90
-in __set__
->>> s1.math
-in __get__
-90
+>>> str_obj = str(byte_obj, encoding='gbk')
+>>> str_obj
+'中文'
+>>>
```
-对于以上理解 `property` 的运行原理有困难的同学,请务必参照我上面写的两点说明。如有其他疑问,可以加微信与我进行探讨。
-## 1.17.4 基于描述符如何实现staticmethod
-说完了 `property` ,这里再来讲讲 `@classmethod` 和 `@staticmethod` 的实现原理。
+## 5. 如何设置文件编码
-我这里定义了一个类,用了两种方式来实现静态方法。
+在 Python 2 中,默认使用的是 ASCII 编码来读取的,因此,我们在使用 Python 2 的时候,如果你的 python 文件里有中文,运行是会报错的。
-```python
-class Test:
- @staticmethod
- def myfunc():
- print("hello")
-
-# 上下两种写法等价
-
-class Test:
- def myfunc():
- print("hello")
- # 重点:这就是描述符的体现
- myfunc = staticmethod(myfunc)
```
-
-这两种写法是等价的,就好像在 `property` 一样,其实以下两种写法也是等价的。
-
-```python
-@TestProperty
-def math(self):
- return self._math
-
-math = TestProperty(fget=math)
+SyntaxError: Non-ASCII character '\xe4' in file demo.py
```
-话题还是转回到 `staticmethod` 这边来吧。
+原因就是 ASCII 编码表太小,无法解释中文。
-由上面的注释,可以看出 `staticmethod` 其实就相当于一个描述符类,而`myfunc` 在此刻变成了一个描述符。关于 `staticmethod` 的实现,你可以参照下面这段我自己写的代码,加以理解。
+而在 Python 3 中,默认使用的是 uft-8 来读取,所以省了不少的事。
-
+对于这个问题,通常解决方法有两种:
-调用这个方法可以知道,每调用一次,它都会经过描述符类的 `__get__` 。
+**第一种方法**
-```python
->>> Test.myfunc()
-in staticmethod __get__
-hello
->>> Test().myfunc()
-in staticmethod __get__
-hello
-```
-
-## 1.17.4 基于描述符如何实现classmethod
+在 python2 中,可以使用在头部指定
-同样的 ` classmethod` 也是一样。
+可以这样写,虽然很好看
-```python
-class classmethod(object):
- def __init__(self, f):
- self.f = f
-
- def __get__(self, instance, owner=None):
- print("in classmethod __get__")
-
- def newfunc(*args):
- return self.f(owner, *args)
- return newfunc
-
-class Test:
- def myfunc(cls):
- print("hello")
-
- # 重点:这就是描述符的体现
- myfunc = classmethod(myfunc)
```
-
-验证结果如下
-
-```python
->>> Test.myfunc()
-in classmethod __get__
-hello
->>> Test().myfunc()
-in classmethod __get__
-hello
+# -*- coding: utf-8 -*-
```
-讲完了 `property`、`staticmethod`和`classmethod` 与 描述符的关系。我想你应该对描述符在 Python 中的应用有了更深的理解。对于 super 的实现原理,就交由你来自己完成。
-
-## 1.17.5 所有实例共享描述符
-
-若要合理使用描述符,利用描述符给我们带来的编码上的便利。有一个坑,需要注意,比如下面这个Student我们没有定义构造函数
+但这样写太麻烦了,我通常使用下面两种写法
-```python
-class Score:
- def __init__(self, default=0):
- self._value = default
-
- def __get__(self, instance, owner):
- return self._value
+```
+# coding:utf-8
+# coding=utf-8
+```
- def __set__(self, instance, value):
- if 0 <= value <= 100:
- self._value = value
- else:
- raise ValueError
-class Student:
- math = Score(0)
- chinese = Score(0)
- english = Score(0)
+**第二种方法**
- def __repr__(self):
- return "".format(self.math, self.chinese, self.english)
```
+import sys
-看一下会出现什么样的问题,std2 居然共享了std1 的属性值,因为它被当成了一个类变量了,而每个实例都没有自己的实例变量,自然访问的是同一个变量。这样是很难达到我们使用描述符的初衷。
-
-```python
->>> std1 = Student()
->>> std1
-
->>> std1.math = 85
->>> std1
-
->>> std2 = Student()
->>> std2 # std2 居然共享了std1 的属性值
-
->>> std2.math = 100
->>> std1 # std2 也会改变std1 的属性值
-
+reload(sys)
+sys.setdefaultencoding('utf-8')
```
-而正确的做法应该是,所有的实例数据只属于该实例本身(通过实例初始化传入),具体写法可参考上一节。
+这里在调用sys.setdefaultencoding(‘utf-8’) 设置默认的解码方式之前,执行了reload(sys),这是必须的,因为python在加载完sys之后,会删除 sys.setdefaultencoding 这个方法,我们需要重新载入sys,才能调用 sys.setdefaultencoding 这个方法。
-## 参考文档
+## 6. 参考文章
-- [Python描述器引导(翻译)](https://pyzh.readthedocs.io/en/latest/Descriptor-HOW-TO-Guide.html#python)
+- [阮一峰老师文章的常识性错误之 Unicode 与 UTF-8](https://foofish.net/unicode_utf-8.html)
+- [Strings, Bytes, and Unicode in Python 2 and 3](https://timothybramlett.com/Strings_Bytes_and_Unicode_in_Python_2_and_3.html)
+- [字符编码笔记:ASCII,Unicode 和 UTF-8](http://www.ruanyifeng.com/blog/2007/10/ascii_unicode_and_utf-8.html)
---
-
+
+
+
\ No newline at end of file
diff --git a/source/c01/c01_17.rst b/source/c01/c01_17.rst
index f8bf4ac..b8f6202 100644
--- a/source/c01/c01_17.rst
+++ b/source/c01/c01_17.rst
@@ -1,589 +1,295 @@
-1.17 深入理解「描述符」
-=======================
-
-学习 Python 这么久了,说起 Python 的优雅之处,能让我脱口而出的,
-Descriptor(描述符)特性可以排得上号。
-
-描述符 是Python
-语言独有的特性,它不仅在应用层使用,在语言的基础设施中也有涉及。
-
-我可以大胆地猜测,你对于描述符的了解是始于诸如 Django ORM 和 SQLAlchemy
-中的字段对象,是的,它们都是描述符。你的它的认识,可能也止步于此,如果你没有去深究,它为何要如此设计?也就加体会不到
-Python 给我们带来的便利与优雅。
-
-由于 描述符的内容较多,长篇大论,容易让你倦怠,所以我打算分几篇来讲。
-
-1.17.1 为什么要使用描述符?
----------------------------
-
-假想你正在给学校写一个成绩管理系统,并没有太多编码经验的你,可能会这样子写。
-
-.. code:: python
-
- class Student:
- def __init__(self, name, math, chinese, english):
- self.name = name
- self.math = math
- self.chinese = chinese
- self.english = english
-
- def __repr__(self):
- return "".format(
- self.name, self.math, self.chinese, self.english
- )
-
-看起来一切都很合理
-
-.. code:: python
-
- >>> std1 = Student('小明', 76, 87, 68)
- >>> std1
-
-
-但是程序并不像人那么智能,不会自动根据使用场景判断数据的合法性,如果老师在录入成绩的时候,不小心录入了将成绩录成了负数,或者超过100,程序是无法感知的。
-
-聪明的你,马上在代码中加入了判断逻辑。
-
-.. code:: python
-
- class Student:
- def __init__(self, name, math, chinese, english):
- self.name = name
- if 0 <= math <= 100:
- self.math = math
- else:
- raise ValueError("Valid value must be in [0, 100]")
-
- if 0 <= chinese <= 100:
- self.chinese = chinese
- else:
- raise ValueError("Valid value must be in [0, 100]")
-
- if 0 <= chinese <= 100:
- self.english = english
- else:
- raise ValueError("Valid value must be in [0, 100]")
-
-
- def __repr__(self):
- return "".format(
- self.name, self.math, self.chinese, self.english
- )
-
-这下程序稍微有点人工智能了,能够自己明辨是非了。
+1.17 详解 Python 中的编码问题
+=============================
|image0|
-程序是智能了,但在\ ``__init__``\ 里有太多的判断逻辑,很影响代码的可读性。巧的是,你刚好学过
-Property
-特性,可以很好的应用在这里。于是你将代码修改成如下,代码的可读性瞬间提升了不少
-
-.. code:: python
-
- class Student:
- def __init__(self, name, math, chinese, english):
- self.name = name
- self.math = math
- self.chinese = chinese
- self.english = english
-
- @property
- def math(self):
- return self._math
-
- @math.setter
- def math(self, value):
- if 0 <= value <= 100:
- self._math = value
- else:
- raise ValueError("Valid value must be in [0, 100]")
-
- @property
- def chinese(self):
- return self._chinese
-
- @chinese.setter
- def chinese(self, value):
- if 0 <= value <= 100:
- self._chinese = value
- else:
- raise ValueError("Valid value must be in [0, 100]")
-
- @property
- def english(self):
- return self._english
-
- @english.setter
- def english(self, value):
- if 0 <= value <= 100:
- self._english = value
- else:
- raise ValueError("Valid value must be in [0, 100]")
-
- def __repr__(self):
- return "".format(
- self.name, self.math, self.chinese, self.english
- )
-
-程序还是一样的人工智能,非常好。
-
-|image1|
+Python 中编码问题,一直是很多 Python 开发者的噩梦,尽管你是工作多年的
+Python
+开发者,也肯定会经常遇到令人神烦的编码问题,好不容易花了半天搞明白了。
-你以为你写的代码,已经非常优秀,无懈可击了。
+一段时间后,又全都忘光光了,一脸懵逼的你又开始你找各种博客、帖子,从头搞清楚什么是编码?什么是
+unicode?它和 ASCII 有什么区别?为什么 decode encode 老是报错?python2
+里和 python3 的字符串类型怎么都不一样,怎么对应起来?如何检测编码格式?
-没想到,人外有天,你的主管看了你的代码后,深深地叹了口气:类里的三个属性,math、chinese、english,都使用了
-Property
-对属性的合法性进行了有效控制。功能上,没有问题,但就是太啰嗦了,三个变量的合法性逻辑都是一样的,只要大于0,小于100
-就可以,代码重复率太高了,这里三个成绩还好,但假设还有地理、生物、历史、化学等十几门的成绩呢,这代码简直没法忍。去了解一下
-Python 的描述符吧。
+反反复复,这个过程真是太痛苦了。
-经过主管的指点,你知道了「描述符」这个东西。怀着一颗敬畏之心,你去搜索了下关于
-描述符的用法。
+今天我把大家在 Python 上会遇到的一些编码问题都讲清楚了,以后你可以不用再
+Google,收藏这篇文章就行。
-其实也很简单,一个实现了 ``描述符协议`` 的类就是一个描述符。
+1. Python 3 中 str 与 bytes
+---------------------------
-什么描述符协议:实现了
-``__get__()``\ 、\ ``__set__()``\ 、\ ``__delete__()``
-其中至少一个方法的类,就是一个描述符。
+在 Python3中,字符串有两种类型 ,str 和 bytes。
-- ``__get__``\ :
- 用于访问属性。它返回属性的值,若属性不存在、不合法等都可以抛出对应的异常。
-- ``__set__``\ :将在属性分配操作中调用。不会返回任何内容。
-- ``__delete__``\ :控制删除操作。不会返回内容。
+今天就来说一说这二者的区别:
-对描述符有了大概的了解后,你开始重写上面的方法。
+- ``unicode string(str 类型)``\ :以 Unicode code points
+ 形式存储,\ **人类认识的形式**
+- ``byte string(bytes 类型)``\ :以 byte
+ 形式存储,\ **机器认识的形式**
-如前所述,Score 类是一个描述器,当从 Student 的实例访问
-math、chinese、english这三个属性的时候,都会经过 Score
-类里的三个特殊的方法。这里的 Score 避免了 使用Property
-出现大量的代码无法复用的尴尬。
+在 Python 3 中你定义的所有字符串,都是 unicode string类型,使用 ``type``
+和 ``isinstance`` 可以判别
.. code:: python
- class Score:
- def __init__(self, default=0):
- self._score = default
-
- def __set__(self, instance, value):
- if not isinstance(value, int):
- raise TypeError('Score must be integer')
- if not 0 <= value <= 100:
- raise ValueError('Valid value must be in [0, 100]')
-
- self._score = value
-
- def __get__(self, instance, owner):
- return self._score
-
- def __delete__(self):
- del self._score
-
- class Student:
- math = Score(0)
- chinese = Score(0)
- english = Score(0)
-
- def __init__(self, name, math, chinese, english):
- self.name = name
- self.math = math
- self.chinese = chinese
- self.english = english
-
-
- def __repr__(self):
- return "".format(
- self.name, self.math, self.chinese, self.english
- )
-
-实现的效果和前面的一样,可以对数据的合法性进行有效控制(字段类型、数值区间等)
-
-|image2|
-
-以上,我举了下具体的实例,从最原始的编码风格到 Property
-,最后引出描述符。由浅入深,一步一步带你感受到描述符的优雅之处。
-
-通过此文,你需要记住的只有一点,就是描述符给我们带来的编码上的便利,它在实现
-``保护属性不受修改``\ 、\ ``属性类型检查``
-的基本功能,同时有大大提高代码的复用率。
+ # python3
---------------
-
-1.17.2 描述符的访问规则
------------------------
-
-描述符分两种:
-
-- 数据描述符:实现了\ ``__get__`` 和 ``__set__`` 两种方法的描述符
-- 非数据描述符:只实现了\ ``__get__`` 一种方法的描述符
-
-你一定会问,他们有什么区别呢?网上的讲解,我看过几个,很多都把一个简单的东西讲得复杂了。
-
-其实就一句话,\ **数据描述器和非数据描述器的区别在于:它们相对于实例的字典的优先级不同**\ 。
-
-如果实例字典中有与描述器同名的属性,如果描述器是数据描述器,优先使用数据描述器,如果是非数据描述器,优先使用字典中的属性。
+ >>> str_obj = "你好"
+ >>>
+ >>> type(str_obj)
+
+ >>>
+ >>> isinstance("你好", str)
+ True
+ >>>
+ >>> isinstance("你好", bytes)
+ False
+ >>>
-这边还是以上节的成绩管理的例子来说明,方便你理解。
+而 bytes 是一个二进制序列对象,你只要你在定义字符串时前面加一个
+``b``\ ,就表示你要定义一个 bytes 类型的字符串对象。
.. code:: python
- # 数据描述符
- class DataDes:
- def __init__(self, default=0):
- self._score = default
-
- def __set__(self, instance, value):
- self._score = value
-
- def __get__(self, instance, owner):
- print("访问数据描述符里的 __get__")
- return self._score
-
- # 非数据描述符
- class NoDataDes:
- def __init__(self, default=0):
- self._score = default
-
- def __get__(self, instance, owner):
- print("访问非数据描述符里的 __get__")
- return self._score
-
-
- class Student:
- math = DataDes(0)
- chinese = NoDataDes(0)
-
- def __init__(self, name, math, chinese):
- self.name = name
- self.math = math
- self.chinese = chinese
-
- def __getattribute__(self, item):
- print("调用 __getattribute__")
- return super(Student, self).__getattribute__(item)
-
- def __repr__(self):
- return "".format(
- self.name, self.math, self.chinese)
+ # python3
+ >>> byte_obj = b"Hello World!"
+ >>> type(byte_obj)
+
+ >>>
+ >>> isinstance(byte_obj, str)
+ False
+ >>>
+ >>> isinstance(byte_obj, bytes)
+ True
+ >>>
-需要注意的是,math 是数据描述符,而 chinese
-是非数据描述符。从下面的验证中,可以看出,当实例属性和数据描述符同名时,会优先访问数据描述符(如下面的math),而当实例属性和非数据描述符同名时,会优先访问实例属性(\ ``__getattribute__``\ )
+但是在定义中文字符串时,你就不能直接在前面加 ``b`` 了,而应该使用
+``encode`` 转一下。
.. code:: python
- >>> std = Student('xm', 88, 99)
+ >>> byte_obj=b"你好"
+ File "", line 1
+ SyntaxError: bytes can only contain ASCII literal characters.
+ >>>
+ >>> str_obj="你好"
+ >>>
+ >>> str_obj.encode("utf-8")
+ b'\xe4\xbd\xa0\xe5\xa5\xbd'
>>>
- >>> std.math
- 调用 __getattribute__
- 访问数据描述符里的 __get__
- 88
- >>> std.chinese
- 调用 __getattribute__
- 99
-
-讲完了数据描述符和非数据描述符,我们还需要了解的对象属性的查找规律。
-当我们对一个实例属性进行访问时,Python 会按 ``obj.__dict__`` →
-``type(obj).__dict__`` → ``type(obj)的父类.__dict__``
-顺序进行查找,如果查找到目标属性并发现是一个描述符,Python
-会调用描述符协议来改变默认的控制行为。
+2. Python 2 中 str 与 unicode
+-----------------------------
-1.17.3 基于描述符如何实现property
----------------------------------
+而在 Python2 中,字符串的类型又与 Python3 不一样,需要仔细区分。
-经过上面的讲解,我们已经知道如何定义描述符,且明白了描述符是如何工作的。
+在 Python2 里,字符串也只有两种类型,unicode 和 str 。
-正常人所见过的描述符的用法就是上篇文章提到的那些,我想说的是那只是描述符协议最常见的应用之一,或许你还不知道,其实有很多
-Python 的特性的底层实现机制都是基于 ``描述符协议``
-的,比如我们熟悉的\ ``@property`` 、\ ``@classmethod``
-、\ ``@staticmethod`` 和 ``super`` 等。
+只有 unicode object 和 非unicode object(其实应该叫 str object)
+的区别:
-先来说说 ``property`` 吧。
+- ``unicode string(unicode类型)``\ :以 Unicode code points
+ 形式存储,\ **人类认识的形式**
+- ``byte string(str 类型)``\ :以 byte 形式存储,\ **机器认识的形式**
-有了第一篇的基础,我们知道了 property
-的基本用法。这里我直接切入主题,从第一篇的例子里精简了一下。
+当我们直接使用双引号或单引号包含字符的方式来定义字符串时,就是 str
+字符串对象,比如这样
.. code:: python
- class Student:
- def __init__(self, name):
- self.name = name
-
- @property
- def math(self):
- return self._math
+ # python2
- @math.setter
- def math(self, value):
- if 0 <= value <= 100:
- self._math = value
- else:
- raise ValueError("Valid value must be in [0, 100]")
-
-不防再简单回顾一下它的用法,通过property装饰的函数,如例子中的 math
-会变成 Student 实例的属性。而对 math 属性赋值会进入 使用 ``math.setter``
-装饰函数的逻辑代码块。
-
-为什么说 property 底层是基于描述符协议的呢?通过 PyCharm 点击进入
-property
-的源码,很可惜,只是一份类似文档一样的伪源码,并没有其具体的实现逻辑。
-
-不过,从这份伪源码的魔法函数结构组成,可以大体知道其实现逻辑。
-
-这里我自己通过模仿其函数结构,结合「描述符协议」来自己实现类
-``property`` 特性。
+ >>> str_obj="你好"
+ >>>
+ >>> type(str_obj)
+
+ >>>
+ >>> str_obj
+ '\xe4\xbd\xa0\xe5\xa5\xbd'
+ >>>
+ >>> isinstance(str_obj, bytes)
+ True
+ >>> isinstance(str_obj, str)
+ True
+ >>> isinstance(str_obj, unicode)
+ False
+ >>>
+ >>> str is bytes
+ True
-代码如下:
+而当我们在双引号或单引号前面加个 ``u``\ ,就表明我们定义的是 unicode
+字符串对象,比如这样
.. code:: python
- class TestProperty(object):
-
- def __init__(self, fget=None, fset=None, fdel=None, doc=None):
- self.fget = fget
- self.fset = fset
- self.fdel = fdel
- self.__doc__ = doc
+ # python2
- def __get__(self, obj, objtype=None):
- print("in __get__")
- if obj is None:
- return self
- if self.fget is None:
- raise AttributeError
- return self.fget(obj)
-
- def __set__(self, obj, value):
- print("in __set__")
- if self.fset is None:
- raise AttributeError
- self.fset(obj, value)
-
- def __delete__(self, obj):
- print("in __delete__")
- if self.fdel is None:
- raise AttributeError
- self.fdel(obj)
+ >>> unicode_obj = u"你好"
+ >>>
+ >>> unicode_obj
+ u'\u4f60\u597d'
+ >>>
+ >>> type(unicode_obj)
+
+ >>>
+ >>> isinstance(unicode_obj, bytes)
+ False
+ >>> isinstance(unicode_obj, str)
+ False
+ >>>
+ >>> isinstance(unicode_obj, unicode)
+ True
+3. 如何检测对象的编码
+---------------------
- def getter(self, fget):
- print("in getter")
- return type(self)(fget, self.fset, self.fdel, self.__doc__)
+所有的字符,在 unicode
+字符集中都有对应的编码值(英文叫做:\ ``code point``\ )
- def setter(self, fset):
- print("in setter")
- return type(self)(self.fget, fset, self.fdel, self.__doc__)
+而把这些编码值按照一定的规则保存成二进制字节码,就是我们说的编码方式,常见的有:UTF-8,GB2312
+等。
- def deleter(self, fdel):
- print("in deleter")
- return type(self)(self.fget, self.fset, fdel, self.__doc__)
+也就是说,当我们要将内存中的字符串持久化到硬盘中的时候,都要指定编码方法,而反过来,读取的时候,也要指定正确的编码方法(这个过程叫解码),不然会出现乱码。
-然后 Student 类,我们也相应改成如下
+那问题就来了,当我们知道了其对应的编码方法,我们就可以正常解码,但并不是所有时候我们都能知道应该用什么编码方式去解码?
-.. code:: python
+这时候就要介绍到一个 python 的库 – ``chardet`` ,使用它之前 需要先安装
- class Student:
- def __init__(self, name):
- self.name = name
+::
- # 其实只有这里改变
- @TestProperty
- def math(self):
- return self._math
+ python3 -m pip install chardet
- @math.setter
- def math(self, value):
- if 0 <= value <= 100:
- self._math = value
- else:
- raise ValueError("Valid value must be in [0, 100]")
+chardet 有一个 detect 方法,可以 ``预测``\ 其其编码格式
-为了尽量让你少产生一点疑惑,我这里做两点说明:
+.. code:: python
-1. 使用\ ``TestProperty``\ 装饰后,\ ``math``
- 不再是一个函数,而是\ ``TestProperty``
- 类的一个实例。所以第二个math函数可以使用 ``math.setter``
- 来装饰,本质是调用\ ``TestProperty.setter`` 来产生一个新的
- ``TestProperty`` 实例赋值给第二个\ ``math``\ 。
+ >>> import chardet
+ >>> chardet.detect('微信公众号:Python编程时光'.encode('gbk'))
+ {'encoding': 'GB2312', 'confidence': 0.99, 'language': 'Chinese'}
-2. 第一个 ``math`` 和第二个 ``math`` 是两个不同 ``TestProperty``
- 实例。但他们都属于同一个描述符类(TestProperty),当对 math
- 对于赋值时,就会进入 ``TestProperty.__set__``\ ,当对math
- 进行取值里,就会进入
- ``TestProperty.__get__``\ 。仔细一看,其实最终访问的还是Student实例的
- ``_math`` 属性。
+为什么说是预测呢,通过上面的输出来看,你会看到有一个 confidence
+字段,其表示预测的可信度,或者说成功率。
-说了这么多,还是运行一下,更加直观一点。
+但是使用它时,若你的字符数较少,就有可能 “``误诊``”),比如只有 ``中文``
+两个字,就像下面这样,我们是 使用 gbk 编码的,使用 chardet 却识别成
+KOI8-R 编码。
.. code:: python
- # 运行后,会直接打印这一行,这是在实例化 TestProperty 并赋值给第二个math
- in setter
+ >>> str_obj = "中文"
+ >>> byte_obj = bytes(a, encoding='gbk') # 先得到一个 gbk 编码的 bytes
>>>
- >>> s1.math = 90
- in __set__
- >>> s1.math
- in __get__
- 90
-
-对于以上理解 ``property``
-的运行原理有困难的同学,请务必参照我上面写的两点说明。如有其他疑问,可以加微信与我进行探讨。
-
-1.17.4 基于描述符如何实现staticmethod
--------------------------------------
-
-说完了 ``property`` ,这里再来讲讲 ``@classmethod`` 和 ``@staticmethod``
-的实现原理。
-
-我这里定义了一个类,用了两种方式来实现静态方法。
+ >>> chardet.detect(byte_obj)
+ {'encoding': 'KOI8-R', 'confidence': 0.682639754276994, 'language': 'Russian'}
+ >>>
+ >>> str_obj2 = str(byte_obj, encoding='KOI8-R')
+ >>> str_obj2
+ 'жпнд'
-.. code:: python
+所以为了编码诊断的准确,要尽量使用足够多的字符。
- class Test:
- @staticmethod
- def myfunc():
- print("hello")
+chardet
+支持多国的语言,从官方文档中可以看到支持如下这些语言(https://chardet.readthedocs.io/en/latest/supported-encodings.html)
- # 上下两种写法等价
+|image1|
- class Test:
- def myfunc():
- print("hello")
- # 重点:这就是描述符的体现
- myfunc = staticmethod(myfunc)
+4. 编码与解码的区别
+-------------------
-这两种写法是等价的,就好像在 ``property``
-一样,其实以下两种写法也是等价的。
+编码和解码,其实就是 str 与 bytes 的相互转化的过程(Python 2
+已经远去,这里以及后面都只用 Python 3 举例)
-.. code:: python
+- **编码**\ :encode 方法,把字符串对象转化为二进制字节序列
- @TestProperty
- def math(self):
- return self._math
-
- math = TestProperty(fget=math)
+- **解码**\ :decode 方法,把二进制字节序列转化为字符串对象
-话题还是转回到 ``staticmethod`` 这边来吧。
+|image2|
-由上面的注释,可以看出 ``staticmethod``
-其实就相当于一个描述符类,而\ ``myfunc`` 在此刻变成了一个描述符。关于
-``staticmethod`` 的实现,你可以参照下面这段我自己写的代码,加以理解。
+那么假如我们真知道了其编码格式,如何来转成 unicode 呢?
-|image3|
+**有两种方法**
-调用这个方法可以知道,每调用一次,它都会经过描述符类的 ``__get__`` 。
+**第一种**\ 是,直接使用 decode 方法
.. code:: python
- >>> Test.myfunc()
- in staticmethod __get__
- hello
- >>> Test().myfunc()
- in staticmethod __get__
- hello
-
-1.17.4 基于描述符如何实现classmethod
-------------------------------------
+ >>> byte_obj.decode('gbk')
+ '中文'
+ >>>
-同样的 ``classmethod`` 也是一样。
+**第二种**\ 是,使用 str 类来转
.. code:: python
- class classmethod(object):
- def __init__(self, f):
- self.f = f
+ >>> str_obj = str(byte_obj, encoding='gbk')
+ >>> str_obj
+ '中文'
+ >>>
+
+5. 如何设置文件编码
+-------------------
- def __get__(self, instance, owner=None):
- print("in classmethod __get__")
-
- def newfunc(*args):
- return self.f(owner, *args)
- return newfunc
+在 Python 2 中,默认使用的是 ASCII 编码来读取的,因此,我们在使用 Python
+2 的时候,如果你的 python 文件里有中文,运行是会报错的。
- class Test:
- def myfunc(cls):
- print("hello")
-
- # 重点:这就是描述符的体现
- myfunc = classmethod(myfunc)
+::
-验证结果如下
+ SyntaxError: Non-ASCII character '\xe4' in file demo.py
-.. code:: python
+原因就是 ASCII 编码表太小,无法解释中文。
- >>> Test.myfunc()
- in classmethod __get__
- hello
- >>> Test().myfunc()
- in classmethod __get__
- hello
+而在 Python 3 中,默认使用的是 uft-8 来读取,所以省了不少的事。
-讲完了 ``property``\ 、\ ``staticmethod``\ 和\ ``classmethod`` 与
-描述符的关系。我想你应该对描述符在 Python 中的应用有了更深的理解。对于
-super 的实现原理,就交由你来自己完成。
+对于这个问题,通常解决方法有两种:
-1.17.5 所有实例共享描述符
--------------------------
+**第一种方法**
-若要合理使用描述符,利用描述符给我们带来的编码上的便利。有一个坑,需要注意,比如下面这个Student我们没有定义构造函数
+在 python2 中,可以使用在头部指定
-.. code:: python
+可以这样写,虽然很好看
- class Score:
- def __init__(self, default=0):
- self._value = default
+::
- def __get__(self, instance, owner):
- return self._value
+ # -*- coding: utf-8 -*-
- def __set__(self, instance, value):
- if 0 <= value <= 100:
- self._value = value
- else:
- raise ValueError
+但这样写太麻烦了,我通常使用下面两种写法
+::
- class Student:
- math = Score(0)
- chinese = Score(0)
- english = Score(0)
+ # coding:utf-8
+ # coding=utf-8
- def __repr__(self):
- return "".format(self.math, self.chinese, self.english)
+**第二种方法**
-看一下会出现什么样的问题,std2 居然共享了std1
-的属性值,因为它被当成了一个类变量了,而每个实例都没有自己的实例变量,自然访问的是同一个变量。这样是很难达到我们使用描述符的初衷。
+::
-.. code:: python
+ import sys
- >>> std1 = Student()
- >>> std1
-
- >>> std1.math = 85
- >>> std1
-
- >>> std2 = Student()
- >>> std2 # std2 居然共享了std1 的属性值
-
- >>> std2.math = 100
- >>> std1 # std2 也会改变std1 的属性值
-
+ reload(sys)
+ sys.setdefaultencoding('utf-8')
-而正确的做法应该是,所有的实例数据只属于该实例本身(通过实例初始化传入),具体写法可参考上一节。
+这里在调用sys.setdefaultencoding(‘utf-8’)
+设置默认的解码方式之前,执行了reload(sys),这是必须的,因为python在加载完sys之后,会删除
+sys.setdefaultencoding 这个方法,我们需要重新载入sys,才能调用
+sys.setdefaultencoding 这个方法。
-参考文档
---------
+6. 参考文章
+-----------
-- `Python描述器引导(翻译) `__
+- `阮一峰老师文章的常识性错误之 Unicode 与
+ UTF-8 `__
+- `Strings, Bytes, and Unicode in Python 2 and
+ 3 `__
+- `字符编码笔记:ASCII,Unicode 和
+ UTF-8 `__
--------------
-.. figure:: http://image.python-online.cn/image-20200320125724880.png
- :alt: 关注公众号,获取最新干货!
-
- 关注公众号,获取最新干货!
+|image3|
-.. |image0| image:: http://image.python-online.cn/20190425221322.png
-.. |image1| image:: http://image.python-online.cn/20190425221322.png
-.. |image2| image:: http://image.python-online.cn/20190425221233.png
-.. |image3| image:: http://image.python-online.cn/20190519001930.png
+.. |image0| image:: http://image.iswbm.com/20200602135014.png
+.. |image1| image:: http://image.iswbm.com/20200423185819.png
+.. |image2| image:: http://image.iswbm.com/20200423190331.png
+.. |image3| image:: http://image.iswbm.com/20200607174235.png
diff --git a/source/c01/c01_18.md b/source/c01/c01_18.md
index 1a63ec8..0a20d4b 100644
--- a/source/c01/c01_18.md
+++ b/source/c01/c01_18.md
@@ -1,6 +1,8 @@
# 1.18 MySQL 使用总结
-## 1.18.1 安装MySQL-python
+
+
+## 1. 安装MySQL-python
MySQL-python 这玩意实在是太难装了,为了以防后面再踩坑,这里还是记录一下吧。
@@ -48,29 +50,29 @@ brew install mysql@5.7
经过漫长的等待后,mysql 终于安装成功
-
+
这时候,再 执行 pip install MySQL-python,发现还是报错。
-
+
有经验的我,立马知道了 `mysql_config` 这个文件的路径可能没有在环境变量中。
-
+
然后,我又重新执行 `pip install MySQL-python` ,发现还是报错。
-
+
但是这个错误相对比较明显,明眼人一看就知道是权限不足。
那我就以 root 权限去安装好了。
-
+
终于安装成功,折腾了两个晚上(主要是网速慢)。
-## 1.18.2 Mac 启动MySQL服务
+## 2. Mac 启动MySQL服务
使用 brew 安装 mysql 成功后,又陷入了一个坑。。
@@ -98,7 +100,7 @@ cd /usr/local/Cellar/mysql@5.7/5.7.25/bin
选择密码强度,视情况而写,我这边选最强的,长度大于8,有数字,有大小写,有特殊字符。
-
+
接下来还会问你,是否删除其他匿名用户,是否删除 test 数据库,是否允许远程使用root登陆(安全起见我选不允许)。
@@ -110,7 +112,7 @@ mysql -uroot -p
-## 1.18.3 Win上忘记密码
+## 3. Win上忘记密码
```shell
@@ -138,7 +140,7 @@ mysql -uroot -p
-## 1.18.4 命令行使用技巧
+## 4. 命令行使用技巧
-
+
diff --git a/source/c01/c01_18.rst b/source/c01/c01_18.rst
index 0ba5e6b..add8a60 100644
--- a/source/c01/c01_18.rst
+++ b/source/c01/c01_18.rst
@@ -1,8 +1,10 @@
1.18 MySQL 使用总结
===================
-1.18.1 安装MySQL-python
------------------------
+|image0|
+
+1. 安装MySQL-python
+-------------------
MySQL-python
这玩意实在是太难装了,为了以防后面再踩坑,这里还是记录一下吧。
@@ -58,31 +60,31 @@ mysql,这时也请将其卸载再重新安装吧。
经过漫长的等待后,mysql 终于安装成功
-|image0|
+|image1|
这时候,再 执行 pip install MySQL-python,发现还是报错。
-|image1|
+|image2|
有经验的我,立马知道了 ``mysql_config``
这个文件的路径可能没有在环境变量中。
-|image2|
+|image3|
然后,我又重新执行 ``pip install MySQL-python`` ,发现还是报错。
-|image3|
+|image4|
但是这个错误相对比较明显,明眼人一看就知道是权限不足。
那我就以 root 权限去安装好了。
-|image4|
+|image5|
终于安装成功,折腾了两个晚上(主要是网速慢)。
-1.18.2 Mac 启动MySQL服务
-------------------------
+2. Mac 启动MySQL服务
+--------------------
使用 brew 安装 mysql 成功后,又陷入了一个坑。。
@@ -111,7 +113,7 @@ mysql,这时也请将其卸载再重新安装吧。
选择密码强度,视情况而写,我这边选最强的,长度大于8,有数字,有大小写,有特殊字符。
-|image5|
+|image6|
接下来还会问你,是否删除其他匿名用户,是否删除 test
数据库,是否允许远程使用root登陆(安全起见我选不允许)。
@@ -122,8 +124,8 @@ mysql,这时也请将其卸载再重新安装吧。
mysql -uroot -p
-1.18.3 Win上忘记密码
---------------------
+3. Win上忘记密码
+----------------
.. code:: shell
@@ -149,16 +151,17 @@ mysql,这时也请将其卸载再重新安装吧。
# 再重新登陆,用新的密码登陆,发现可以生效
mysql -uroot -p
-1.18.4 命令行使用技巧
----------------------
+4. 命令行使用技巧
+-----------------
-|image6|
+|image7|
-.. |image0| image:: http://image.python-online.cn/20190615001340.png
-.. |image1| image:: http://image.python-online.cn/20190615001414.png
-.. |image2| image:: http://image.python-online.cn/20190615001633.png
-.. |image3| image:: http://image.python-online.cn/20190615001706.png
-.. |image4| image:: http://image.python-online.cn/20190615001908.png
-.. |image5| image:: http://image.python-online.cn/20190615112422.png
-.. |image6| image:: http://image.python-online.cn/20190705225651.png
+.. |image0| image:: http://image.iswbm.com/20200602135014.png
+.. |image1| image:: http://image.iswbm.com/20190615001340.png
+.. |image2| image:: http://image.iswbm.com/20190615001414.png
+.. |image3| image:: http://image.iswbm.com/20190615001633.png
+.. |image4| image:: http://image.iswbm.com/20190615001706.png
+.. |image5| image:: http://image.iswbm.com/20190615001908.png
+.. |image6| image:: http://image.iswbm.com/20190615112422.png
+.. |image7| image:: http://image.iswbm.com/20190705225651.png
diff --git a/source/c01/c01_33.md b/source/c01/c01_19.md
similarity index 87%
rename from source/c01/c01_33.md
rename to source/c01/c01_19.md
index e6f4756..8d3745b 100644
--- a/source/c01/c01_33.md
+++ b/source/c01/c01_19.md
@@ -1,4 +1,6 @@
-# 1.33 如何调试已经运行中的程序
+# 1.19 如何调试已经运行中的程序
+
+
官方原始wiki:https://wiki.python.org/moin/DebuggingWithGdb
@@ -46,7 +48,7 @@ sudo yum install gdb python-debuginfo
-
+
diff --git a/source/c01/c01_33.rst b/source/c01/c01_19.rst
similarity index 84%
rename from source/c01/c01_33.rst
rename to source/c01/c01_19.rst
index bfe6e11..65fc111 100644
--- a/source/c01/c01_33.rst
+++ b/source/c01/c01_19.rst
@@ -1,6 +1,8 @@
-1.33 如何调试已经运行中的程序
+1.19 如何调试已经运行中的程序
=============================
+|image0|
+
官方原始wiki:https://wiki.python.org/moin/DebuggingWithGdb
在CentOS 下,安装包过程,官方给的不够详细。这里记录一下
@@ -44,7 +46,8 @@
sudo yum install gdb python-debuginfo
-.. figure:: http://image.python-online.cn/image-20200320125724880.png
- :alt: 关注公众号,获取最新干货!
+|image1|
+
+.. |image0| image:: http://image.iswbm.com/20200602135014.png
+.. |image1| image:: http://image.iswbm.com/20200607174235.png
- 关注公众号,获取最新干货!
diff --git a/source/c01/c01_20.md b/source/c01/c01_20.md
index f70e790..1e9fde1 100644
--- a/source/c01/c01_20.md
+++ b/source/c01/c01_20.md
@@ -1,100 +1,39 @@
-# 1.20 静态方法其实暗藏玄机
+# 1.20 在 CentOS 7.2 上安装 Python3.7
-这个标题「**静态方法其实暗藏玄机**」其实只是该文章的一个知识点。或许有些标题党,但没有关系,我相信有不少人对此并没有深入研究他们,不信我问你三个问题,你看能否答上来。
+
-1、Python2.x和3.x中,函数和方法的区分有什么不同?
+首先下载 python3.7的源码包,然后解压
-2、有了类/实例方法和普通函数,为什么还会有静态方法?
+```shell
+$ cd ~
+$ wget -c https://www.python.org/ftp/python/3.7.1/Python-3.7.1.tgz
+$ tar xf Python-3.7.1.tgz && cd Python-3.7.1
+```
-3、Python3.x 中,静态方法有几种写法?
+安装 一些依赖包
-带着这三个问题,你可以尝试在下文中寻找答案。
+```shell
+$ yum install gcc zlib-devel bzip2-devel openssl openssl-devel ncurses-devel sqlite-devel libffi-devel python3-devel -y
+```
----
+编译安装
-在 Python 2 中的函数和方法的区别,十分清晰,很好分辨。但在 Python3中,我却发现完全又是另一套准则。
+```shell
+$ ./configure
+$ make
+$ sudo make install
+```
-首先先来 Python2 的(以下在 Python2.7中测试通过)
+至此,你已经成功安装 了 Python3, pip3,setuptools
-
+requests.get("https://www.baidu.com")
-可以得出结论:
+```
+python3 -m pip install --user requests aiohttp cryptography pymysql prettytable sh Fabric paramiko apscheduler bashplotlib httpie PathPicker -i https://pypi.douban.com/simple
+```
-1、普通函数(未定位在类里),都是函数。
-2、静态方法(@staticmethod),都是函数。
-3、类方法(@classmethod),都是方法。
-4、实例方法(首参数为self且非静态、非类方法的),都是方法。
-
-你一定想说,类方法和实例方法,是方法没错呀,毕竟名字本身就有方法,普通函数是函数,我也理解呀。那静态方法,为什么不是方法而是函数呢?
-
-名字只是一个外在的表面称呼,你能说「赵铁男」就一定是汉子吗?
-
-我的理解是:方法是一种和对象(实例或者类)绑定后的函数。
-
-类方法的首参是`cls`,调用时,无需显示传入。实例方法首参是self,调用时,也无需显示传入。
-
-而静态方法,其实和普通函数没啥区别,唯一的区别就是,他定义的位置被放在了类里,做为类或者实例的一个函数。
-
-那你肯定又要问了,既然静态方法和普通函数都是一样的,为什么要刻意将它放在类里呢?
-
-我上面说了,放在类里定义,就可以让它成为类的一个工具函数,这就像你身上随身携带了一把刀,这把刀与你本人并没有什么直接关系,唯一的联系就是这把刀是你的,而你把它带在身上,无论你去到哪里,只要需要,你就可以直接拿出来用上。对比将函数放在类外面,缺点是什么呢?就是当你出门在外(在别的模块里)发现你要用刀的时候,还要特地跑一趟去商店买一把刀(import 这个函数)。
-
-另外,我觉得静态方法在业务和设计上的意义,会更多一些。
-
-一般静态方法是做为类或者实例的一个工具函数,比如对变量的做一个合法性的检验,对数据进行序列化反序列化操作等等。
-
-说完了 Python2 ,再来说说Python3.
-
-以前我觉得 Python2 对于方法和函数的界线更加清晰,但接触了 Python3,我反而觉得Python3里方法和函数的区分似乎更加合理。
-
-还是刚刚那段代码,我更改了解释器为Python3.6(以下在 Python3.6中测试通过)
-
-
-
-和Python2的唯一区别是,`People.jump` 在Python3 中变成了函数。
-
-这一下颠覆了你刚刚才建立起来的知识体系有木有?
-
-先别急,我再做个试验,也许你就知道了。
-
-**在 Python2中**
-
-执行People.jump('hello'),会报错说,jump的首参必须为People的实例对象,这可以理解,毕竟jump定义时,第一个参数为self。
-
-
-
-**在 Python3中**
-
-你可以发现,这里的jump的首参不再要求是 People 的一个实例,而可以是任意的对象,比如我使用字符串对象,也没有报错。
-
-
-
-也就是说,当你往jump中传入的首参为People的实例时,jump 就是方法,而当你传入的首参不是People的实例对象时,jump就是函数。
-
-你看,多么灵活呀。
-
-再总结一下,在Python3中:
-
-1、普通函数(未定位在类里),都是函数。
-
-2、静态方法(@staticmethod),都是函数。
-
-3、类方法(@classmethod),都是方法。
-
-4、方法和函数区分没有那么明确,而是更加灵活了,一个函数有可能是方法也有可能是函数。
-
-你肯定又要问了,那这是不是就意味着,Python3 中静态方法,可以不用再使用@staticmethod 装饰了呢,反正Python3都可以识别。
-
-这是个好问题,是的,可以不用指定,但是最好指定,如果你不指定,你调用这个方法只能通过People.jump,而不能通过 self.jump了,因为首参不是 self,而如果使用@staticmethod 就可以使用self.jump。
-
-所以说这是一个规范,就像类的私有方法,规范要求外部最好不要调用,但这不是强制要求,不是说外部就不能调用。
-
-写这篇文章的起源,是前两天有位读者在交流里问到了相关的问题,正好没什么主题可以写,就拿过来做为素材整理一下,也正好没有写过静态方法、类方法的内容,没想到简单的东西,也能写出这么多的内容出来。
-
-
-
-
+
\ No newline at end of file
diff --git a/source/c01/c01_20.rst b/source/c01/c01_20.rst
index 08c279b..843d931 100644
--- a/source/c01/c01_20.rst
+++ b/source/c01/c01_20.rst
@@ -1,117 +1,40 @@
-1.20 静态方法其实暗藏玄机
-=========================
-
-这个标题「\ **静态方法其实暗藏玄机**\ 」其实只是该文章的一个知识点。或许有些标题党,但没有关系,我相信有不少人对此并没有深入研究他们,不信我问你三个问题,你看能否答上来。
-
-1、Python2.x和3.x中,函数和方法的区分有什么不同?
-
-2、有了类/实例方法和普通函数,为什么还会有静态方法?
-
-3、Python3.x 中,静态方法有几种写法?
-
-带着这三个问题,你可以尝试在下文中寻找答案。
-
---------------
-
-在 Python 2 中的函数和方法的区别,十分清晰,很好分辨。但在
-Python3中,我却发现完全又是另一套准则。
-
-首先先来 Python2 的(以下在 Python2.7中测试通过)
+1.20 在 CentOS 7.2 上安装 Python3.7
+===================================
|image0|
-可以得出结论:
-
-1、普通函数(未定位在类里),都是函数。
+首先下载 python3.7的源码包,然后解压
-2、静态方法(@staticmethod),都是函数。
+.. code:: shell
-3、类方法(@classmethod),都是方法。
+ $ cd ~
+ $ wget -c https://www.python.org/ftp/python/3.7.1/Python-3.7.1.tgz
+ $ tar xf Python-3.7.1.tgz && cd Python-3.7.1
-4、实例方法(首参数为self且非静态、非类方法的),都是方法。
+安装 一些依赖包
-你一定想说,类方法和实例方法,是方法没错呀,毕竟名字本身就有方法,普通函数是函数,我也理解呀。那静态方法,为什么不是方法而是函数呢?
+.. code:: shell
-名字只是一个外在的表面称呼,你能说「赵铁男」就一定是汉子吗?
+ $ yum install gcc zlib-devel bzip2-devel openssl openssl-devel ncurses-devel sqlite-devel libffi-devel python3-devel -y
-我的理解是:方法是一种和对象(实例或者类)绑定后的函数。
+编译安装
-类方法的首参是\ ``cls``\ ,调用时,无需显示传入。实例方法首参是self,调用时,也无需显示传入。
+.. code:: shell
-而静态方法,其实和普通函数没啥区别,唯一的区别就是,他定义的位置被放在了类里,做为类或者实例的一个函数。
+ $ ./configure
+ $ make
+ $ sudo make install
-那你肯定又要问了,既然静态方法和普通函数都是一样的,为什么要刻意将它放在类里呢?
+至此,你已经成功安装 了 Python3, pip3,setuptools
-我上面说了,放在类里定义,就可以让它成为类的一个工具函数,这就像你身上随身携带了一把刀,这把刀与你本人并没有什么直接关系,唯一的联系就是这把刀是你的,而你把它带在身上,无论你去到哪里,只要需要,你就可以直接拿出来用上。对比将函数放在类外面,缺点是什么呢?就是当你出门在外(在别的模块里)发现你要用刀的时候,还要特地跑一趟去商店买一把刀(import
-这个函数)。
+requests.get(“https://www.baidu.com”)
-另外,我觉得静态方法在业务和设计上的意义,会更多一些。
+::
-一般静态方法是做为类或者实例的一个工具函数,比如对变量的做一个合法性的检验,对数据进行序列化反序列化操作等等。
-
-说完了 Python2 ,再来说说Python3.
-
-以前我觉得 Python2 对于方法和函数的界线更加清晰,但接触了
-Python3,我反而觉得Python3里方法和函数的区分似乎更加合理。
-
-还是刚刚那段代码,我更改了解释器为Python3.6(以下在
-Python3.6中测试通过)
+ python3 -m pip install --user requests aiohttp cryptography pymysql prettytable sh Fabric paramiko apscheduler bashplotlib httpie PathPicker -i https://pypi.douban.com/simple
|image1|
-和Python2的唯一区别是,\ ``People.jump`` 在Python3 中变成了函数。
-
-这一下颠覆了你刚刚才建立起来的知识体系有木有?
-
-先别急,我再做个试验,也许你就知道了。
-
-**在 Python2中**
-
-执行People.jump(‘hello’),会报错说,jump的首参必须为People的实例对象,这可以理解,毕竟jump定义时,第一个参数为self。
-
-|image2|
-
-**在 Python3中**
-
-你可以发现,这里的jump的首参不再要求是 People
-的一个实例,而可以是任意的对象,比如我使用字符串对象,也没有报错。
-
-|image3|
-
-也就是说,当你往jump中传入的首参为People的实例时,jump
-就是方法,而当你传入的首参不是People的实例对象时,jump就是函数。
-
-你看,多么灵活呀。
-
-再总结一下,在Python3中:
-
-1、普通函数(未定位在类里),都是函数。
-
-2、静态方法(@staticmethod),都是函数。
-
-3、类方法(@classmethod),都是方法。
-
-4、方法和函数区分没有那么明确,而是更加灵活了,一个函数有可能是方法也有可能是函数。
-
-你肯定又要问了,那这是不是就意味着,Python3
-中静态方法,可以不用再使用@staticmethod
-装饰了呢,反正Python3都可以识别。
-
-这是个好问题,是的,可以不用指定,但是最好指定,如果你不指定,你调用这个方法只能通过People.jump,而不能通过
-self.jump了,因为首参不是 self,而如果使用@staticmethod
-就可以使用self.jump。
-
-所以说这是一个规范,就像类的私有方法,规范要求外部最好不要调用,但这不是强制要求,不是说外部就不能调用。
-
-写这篇文章的起源,是前两天有位读者在交流里问到了相关的问题,正好没什么主题可以写,就拿过来做为素材整理一下,也正好没有写过静态方法、类方法的内容,没想到简单的东西,也能写出这么多的内容出来。
-
-.. figure:: http://image.python-online.cn/image-20200320125724880.png
- :alt: 关注公众号,获取最新干货!
-
- 关注公众号,获取最新干货!
-
-.. |image0| image:: http://image.python-online.cn/20190630111243.png
-.. |image1| image:: http://image.python-online.cn/20190630104956.png
-.. |image2| image:: http://image.python-online.cn/20190630105735.png
-.. |image3| image:: http://image.python-online.cn/20190630105600.png
+.. |image0| image:: http://image.iswbm.com/20200602135014.png
+.. |image1| image:: http://image.iswbm.com/20200607174235.png
diff --git a/source/c01/c01_21.md b/source/c01/c01_21.md
index 38d4e7c..cf1190c 100644
--- a/source/c01/c01_21.md
+++ b/source/c01/c01_21.md
@@ -1,86 +1,216 @@
-# 1.21 开发小技巧
+# 1.21 Python 炫技操作:连接列表的八种方法
-## 1. 解决网页鼠标限制
+
-```
-解决网页不能选中,在console中输入:document.onselectstart=true
-解决网页不能复制,在console中输入:document.oncopy=true
-解决网页不能右键,在console中输入:document.oncontextmenu=true
-```
+Python 语言里有许多(而且是越来越多)的高级特性,是 Python 发烧友们非常喜欢的。在这些人的眼里,能够写出那些一般开发者看不懂的高级特性,就是高手,就是大神。
-## 2. 在 linux 上看 json 文件
+但你要知道,在团队合作里,炫技是大忌。
+为什么这么说呢?我说下自己的看法:
+
+1. 越简洁的代码,越清晰的逻辑,就越不容易出错;
+2. 在团队合作中,你的代码不只有你在维护,降低别人的阅读/理解代码逻辑的成本是一个良好的品德
+3. 简单的代码,只会用到最基本的语法糖,复杂的高级特性,会有更多的依赖(如语言的版本)
+
+该篇是「**炫技系列**」的第三篇内容,在这个系列里,我将总结盘点一下,我所见过的那些炫技操作。在这里,如果你是 Python 发烧友,你可以学到一些写出超酷的代码书写技巧。同时,看了这些内容,对你在阅读别人的代码时,也许会有些帮助。
+
+## 1. 最直观的相加
+
+使用 `+` 对多个列表进行相加,你应该懂,不多说了。
+
+```python
+>>> list01 = [1,2,3]
+>>> list02 = [4,5,6]
+>>> list03 = [7,8,9]
+>>>
+>>> list01 + list02 + list03
+[1, 2, 3, 4, 5, 6, 7, 8, 9]
+>>>
```
-cat test.json | python -m json.tool
-```
-## 3. 在 Mac 上多开微信
+
+## 2. 借助 itertools
+
+itertools 在 Python 里有一个非常强大的内置模块,它专门用于操作可迭代对象。
+
+在前面的文章中也介绍过,使用 `itertools.chain()` 函数先可迭代对象(在这里指的是列表)串联起来,组成一个更大的可迭代对象。
+
+最后你再利用 list 将其转化为 列表。
+
+```python
+>>> from itertools import chain
+>>> list01 = [1,2,3]
+>>> list02 = [4,5,6]
+>>> list03 = [7,8,9]
+>>>
+>>> list(chain(list01, list02, list03))
+[1, 2, 3, 4, 5, 6, 7, 8, 9]
+>>>
```
-open -n /Applications/WeChat.app
+
+## 3. 使用 * 解包
+
+在 [Python 炫技操作(02):合并字典的七种方法](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247486080&idx=1&sn=f1c5c4fc5363a1d787b9ae9baba0d07b&chksm=e8866a62dff1e374ad1e5ae2e51bc6cbeb41f631899cde980555ded61be840f0fe6c76c44b7d&token=688334198&lang=zh_CN#rd) 提到了使用 `**` 可解包字典。
+
+与它相似的,使用 `*` 可以解包列表。 `*` 和 `**` 常用在函数定义时,设置可变参数。
+
+现在我将它单独拿出来用在多个列表的合并。
+
+示例如下:
+
+```python
+>>> list01 = [1,2,3]
+>>> list02 = [4,5,6]
+>>>
+>>> [*list01, *list02]
+[1, 2, 3, 4, 5, 6]
+>>>
```
-## 3. 微信测试是否被删除
+
+## 4. 使用 extend
+
+在字典中,使用 update 可实现原地更新,而在列表中,使用 extend 可实现列表的自我扩展。
+
+```python
+>>> list01 = [1,2,3]
+>>> list02 = [4,5,6]
+>>>
+>>> list01.extend(list02)
+>>> list01
+[1, 2, 3, 4, 5, 6]
```
-1. 转账(不是好友不能点)
-2. 拉群(不说话不会提示)
+
+## 5. 使用列表推导式
+
+Python 里对于生成列表、集合、字典,有一套非常 Pythonnic 的写法。
+
+那就是列表解析式,集合解析式和字典解析式,通常是 Python 发烧友的最爱,那么今天的主题:列表合并,列表推导式还能否胜任呢?
+
+当然可以,具体示例代码如下:
+
+```python
+>>> list01 = [1,2,3]
+>>> list02 = [4,5,6]
+>>> list03 = [7,8,9]
+>>>
+>>> [x for l in (list01, list02, list03) for x in l]
+[1, 2, 3, 4, 5, 6, 7, 8, 9]
+>>>
```
-## 4. Mac 启动台 app 大小调整
+
+## 6. 使用 heapq
+
+heapq 是 Python 的一个标准模块,它提供了堆排序算法的实现。
+
+该模块里有一个 merge 方法,可以用于合并多个列表,如下所示
+
+```python
+>>> list01 = [1,2,3]
+>>> list02 = [4,5,6]
+>>> list03 = [7,8,9]
+>>>
+>>> from heapq import merge
+>>>
+>>> list(merge(list01, list02, list03))
+[1, 2, 3, 4, 5, 6, 7, 8, 9]
+>>>
```
-1.终端运行命令:10代表一行显示10个图标,几个可以自定义
-defaults write com.apple.dock springboard-columns -int 10
-2.设置完需要重新启动一下 启动台
-killall Dock
+要注意的是,heapq.merge 除了合并多个列表外,它还会将合并后的最终的列表进行排序。
+
+```python
+>>> list01 = [2,5,3]
+>>> list02 = [1,4,6]
+>>> list03 = [7,9,8]
+>>>
+>>> from heapq import merge
+>>>
+>>> list(merge(list01, list02, list03))
+[1, 2, 4, 5, 3, 6, 7, 9, 8]
+>>>
```
-## 5. 重定向标准输出到文件
+它的效果等价于下面这行代码:
+```python
+sorted(itertools.chain(*iterables))
```
-import contextlib
-def unshelve_task():
- pass
+如果你希望得到一个始终有序的列表,那请第一时间想到 heapq.merge,因为它采用堆排序,效率非常高。但若你不希望得到一个排过序的列表,就不要使用它了。
-@contextlib.contextmanager
-def close_stdout():
- raw_stdout = sys.stdout
- file = open(log_file, 'a+')
- sys.stdout = file
+## 7. 借助魔法方法
- yield
+在之前的文章里,把魔法方法介绍得很全。
- sys.stdout = raw_stdout
- file.close()
-
-with close_stdout():
- unshelve_task()
-```
+[非常全的通俗易懂 Python 魔法方法指南(上)](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485551&idx=1&sn=4c0983f22269a113bcdf83690e5e2b20&chksm=e886688ddff1e19b9ad230128a67ee1a9ee1eced0720c14b5d48f68943be10b1b85b23d8ca2d#rd)
-## 6. 将子网掩码转换为cidr
+[非常全的通俗易懂 Python 魔法方法指南(下)](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485555&idx=1&sn=0a218b796e651b451a17112e22790d07&chksm=e8866891dff1e18771a9392da7f509732244ebc4d1a6e2427acd39ee8b59b146e3d4961a2a62#rd)
-如何使用netaddr库将ipv4子网掩码转换为cidr表示法?
-示例:255.255.255.0到/ 24
+其中有一个魔法方法是 `__add__`,实际 上当我们使用第一种方法 list01 + list02 的时候,内部实际上是作用在 `__add__` 这个魔法方法上的.
-使用netaddr:
+所以以下两种方法其实是等价的
```
->>> from netaddr import IPAddress
->>> IPAddress("255.255.255.0").netmask_bits()
-24
+>>> list01 = [1,2,3]
+>>> list02 = [4,5,6]
+>>>
+>>> list01 + list02
+[1, 2, 3, 4, 5, 6]
+>>>
+>>>
+>>> list01.__add__(list02)
+[1, 2, 3, 4, 5, 6]
+>>>
```
-您也可以在不使用任何库的情况下执行此操作,只需在网络掩码的二进制表示中计算1位:
+借用这个魔法特性,我们可以 reduce 这个方法来对多个列表进行合并,示例代码如下
+```python
+>>> list01 = [1,2,3]
+>>> list02 = [4,5,6]
+>>> list03 = [7,8,9]
+>>>
+>>> from functools import reduce
+>>> reduce(list.__add__, (list01, list02, list03))
+[1, 2, 3, 4, 5, 6, 7, 8, 9]
+>>>
```
->>> netmask = "255.255.255.0"
->>> sum([bin(int(x)).count("1") for x in netmask.split(".")])
-24
+
+
+
+## 8. 使用 yield from
+
+在很早的一篇文章里([并发编程08|深入理解yield from语法](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485063&idx=1&sn=0ff7a99058320ff90a6237e7e03367fb&scene=21#wechat_redirect)),我很详细的介绍了 yield from 意义及使用方法。
+
+在 yield from 后可接一个可迭代对象,用于迭代并返回其中的每一个元素。
+
+因此,我们可以像下面这样自定义一个合并列表的工具函数。
+
+```python
+>>> list01 = [1,2,3]
+>>> list02 = [4,5,6]
+>>> list03 = [7,8,9]
+>>>
+>>> def merge(*lists):
+... for l in lists:
+... yield from l
+...
+>>> list(merge(list01, list02, list03))
+[1, 2, 3, 4, 5, 6, 7, 8, 9]
+>>>
```
-
\ No newline at end of file
+---
+
+
+
+
+
+
diff --git a/source/c01/c01_21.rst b/source/c01/c01_21.rst
index aab9498..8302f52 100644
--- a/source/c01/c01_21.rst
+++ b/source/c01/c01_21.rst
@@ -1,95 +1,233 @@
-1.21 开发小技巧
-===============
+1.21 Python 炫技操作:连接列表的八种方法
+========================================
-1. 解决网页鼠标限制
--------------------
+|image0|
-::
+Python 语言里有许多(而且是越来越多)的高级特性,是 Python
+发烧友们非常喜欢的。在这些人的眼里,能够写出那些一般开发者看不懂的高级特性,就是高手,就是大神。
- 解决网页不能选中,在console中输入:document.onselectstart=true
- 解决网页不能复制,在console中输入:document.oncopy=true
- 解决网页不能右键,在console中输入:document.oncontextmenu=true
+但你要知道,在团队合作里,炫技是大忌。
-2. 在 linux 上看 json 文件
---------------------------
+为什么这么说呢?我说下自己的看法:
-::
+1. 越简洁的代码,越清晰的逻辑,就越不容易出错;
+2. 在团队合作中,你的代码不只有你在维护,降低别人的阅读/理解代码逻辑的成本是一个良好的品德
+3. 简单的代码,只会用到最基本的语法糖,复杂的高级特性,会有更多的依赖(如语言的版本)
- cat test.json | python -m json.tool
+该篇是「\ **炫技系列**\ 」的第三篇内容,在这个系列里,我将总结盘点一下,我所见过的那些炫技操作。在这里,如果你是
+Python
+发烧友,你可以学到一些写出超酷的代码书写技巧。同时,看了这些内容,对你在阅读别人的代码时,也许会有些帮助。
-3. 在 Mac 上多开微信
---------------------
+1. 最直观的相加
+---------------
-::
+使用 ``+`` 对多个列表进行相加,你应该懂,不多说了。
- open -n /Applications/WeChat.app
+.. code:: python
-3. 微信测试是否被删除
----------------------
+ >>> list01 = [1,2,3]
+ >>> list02 = [4,5,6]
+ >>> list03 = [7,8,9]
+ >>>
+ >>> list01 + list02 + list03
+ [1, 2, 3, 4, 5, 6, 7, 8, 9]
+ >>>
-::
+2. 借助 itertools
+-----------------
- 1. 转账(不是好友不能点)
- 2. 拉群(不说话不会提示)
+itertools 在 Python
+里有一个非常强大的内置模块,它专门用于操作可迭代对象。
-4. Mac 启动台 app 大小调整
---------------------------
+在前面的文章中也介绍过,使用 ``itertools.chain()``
+函数先可迭代对象(在这里指的是列表)串联起来,组成一个更大的可迭代对象。
-::
+最后你再利用 list 将其转化为 列表。
- 1.终端运行命令:10代表一行显示10个图标,几个可以自定义
- defaults write com.apple.dock springboard-columns -int 10
+.. code:: python
- 2.设置完需要重新启动一下 启动台
- killall Dock
+ >>> from itertools import chain
+ >>> list01 = [1,2,3]
+ >>> list02 = [4,5,6]
+ >>> list03 = [7,8,9]
+ >>>
+ >>> list(chain(list01, list02, list03))
+ [1, 2, 3, 4, 5, 6, 7, 8, 9]
+ >>>
-5. 重定向标准输出到文件
------------------------
+3. 使用 \* 解包
+---------------
-::
+在 `Python
+炫技操作(02):合并字典的七种方法 `__
+提到了使用 ``**`` 可解包字典。
- import contextlib
+与它相似的,使用 ``*`` 可以解包列表。 ``*`` 和 ``**``
+常用在函数定义时,设置可变参数。
- def unshelve_task():
- pass
+现在我将它单独拿出来用在多个列表的合并。
- @contextlib.contextmanager
- def close_stdout():
- raw_stdout = sys.stdout
- file = open(log_file, 'a+')
- sys.stdout = file
+示例如下:
- yield
+.. code:: python
- sys.stdout = raw_stdout
- file.close()
-
- with close_stdout():
- unshelve_task()
+ >>> list01 = [1,2,3]
+ >>> list02 = [4,5,6]
+ >>>
+ >>> [*list01, *list02]
+ [1, 2, 3, 4, 5, 6]
+ >>>
-6. 将子网掩码转换为cidr
------------------------
+4. 使用 extend
+--------------
-如何使用netaddr库将ipv4子网掩码转换为cidr表示法? 示例:255.255.255.0到/
-24
+在字典中,使用 update 可实现原地更新,而在列表中,使用 extend
+可实现列表的自我扩展。
-使用netaddr:
+.. code:: python
-::
+ >>> list01 = [1,2,3]
+ >>> list02 = [4,5,6]
+ >>>
+ >>> list01.extend(list02)
+ >>> list01
+ [1, 2, 3, 4, 5, 6]
+
+5. 使用列表推导式
+-----------------
+
+Python 里对于生成列表、集合、字典,有一套非常 Pythonnic 的写法。
+
+那就是列表解析式,集合解析式和字典解析式,通常是 Python
+发烧友的最爱,那么今天的主题:列表合并,列表推导式还能否胜任呢?
+
+当然可以,具体示例代码如下:
+
+.. code:: python
+
+ >>> list01 = [1,2,3]
+ >>> list02 = [4,5,6]
+ >>> list03 = [7,8,9]
+ >>>
+ >>> [x for l in (list01, list02, list03) for x in l]
+ [1, 2, 3, 4, 5, 6, 7, 8, 9]
+ >>>
+
+6. 使用 heapq
+-------------
+
+heapq 是 Python 的一个标准模块,它提供了堆排序算法的实现。
+
+该模块里有一个 merge 方法,可以用于合并多个列表,如下所示
+
+.. code:: python
+
+ >>> list01 = [1,2,3]
+ >>> list02 = [4,5,6]
+ >>> list03 = [7,8,9]
+ >>>
+ >>> from heapq import merge
+ >>>
+ >>> list(merge(list01, list02, list03))
+ [1, 2, 3, 4, 5, 6, 7, 8, 9]
+ >>>
+
+要注意的是,heapq.merge
+除了合并多个列表外,它还会将合并后的最终的列表进行排序。
+
+.. code:: python
- >>> from netaddr import IPAddress
- >>> IPAddress("255.255.255.0").netmask_bits()
- 24
+ >>> list01 = [2,5,3]
+ >>> list02 = [1,4,6]
+ >>> list03 = [7,9,8]
+ >>>
+ >>> from heapq import merge
+ >>>
+ >>> list(merge(list01, list02, list03))
+ [1, 2, 4, 5, 3, 6, 7, 9, 8]
+ >>>
-您也可以在不使用任何库的情况下执行此操作,只需在网络掩码的二进制表示中计算1位:
+它的效果等价于下面这行代码:
+
+.. code:: python
+
+ sorted(itertools.chain(*iterables))
+
+如果你希望得到一个始终有序的列表,那请第一时间想到
+heapq.merge,因为它采用堆排序,效率非常高。但若你不希望得到一个排过序的列表,就不要使用它了。
+
+7. 借助魔法方法
+---------------
+
+在之前的文章里,把魔法方法介绍得很全。
+
+`非常全的通俗易懂 Python
+魔法方法指南(上) `__
+
+`非常全的通俗易懂 Python
+魔法方法指南(下) `__
+
+其中有一个魔法方法是 ``__add__``\ ,实际 上当我们使用第一种方法 list01 +
+list02 的时候,内部实际上是作用在 ``__add__`` 这个魔法方法上的.
+
+所以以下两种方法其实是等价的
::
- >>> netmask = "255.255.255.0"
- >>> sum([bin(int(x)).count("1") for x in netmask.split(".")])
- 24
+ >>> list01 = [1,2,3]
+ >>> list02 = [4,5,6]
+ >>>
+ >>> list01 + list02
+ [1, 2, 3, 4, 5, 6]
+ >>>
+ >>>
+ >>> list01.__add__(list02)
+ [1, 2, 3, 4, 5, 6]
+ >>>
+
+借用这个魔法特性,我们可以 reduce
+这个方法来对多个列表进行合并,示例代码如下
+
+.. code:: python
+
+ >>> list01 = [1,2,3]
+ >>> list02 = [4,5,6]
+ >>> list03 = [7,8,9]
+ >>>
+ >>> from functools import reduce
+ >>> reduce(list.__add__, (list01, list02, list03))
+ [1, 2, 3, 4, 5, 6, 7, 8, 9]
+ >>>
+
+8. 使用 yield from
+------------------
+
+在很早的一篇文章里(\ `并发编程08|深入理解yield
+from语法 `__\ ),我很详细的介绍了
+yield from 意义及使用方法。
+
+在 yield from 后可接一个可迭代对象,用于迭代并返回其中的每一个元素。
+
+因此,我们可以像下面这样自定义一个合并列表的工具函数。
+
+.. code:: python
+
+ >>> list01 = [1,2,3]
+ >>> list02 = [4,5,6]
+ >>> list03 = [7,8,9]
+ >>>
+ >>> def merge(*lists):
+ ... for l in lists:
+ ... yield from l
+ ...
+ >>> list(merge(list01, list02, list03))
+ [1, 2, 3, 4, 5, 6, 7, 8, 9]
+ >>>
+
+--------------
+
+|image1|
-.. figure:: http://image.python-online.cn/image-20200320125724880.png
- :alt: 关注公众号,获取最新干货!
+.. |image0| image:: http://image.iswbm.com/20200602135014.png
+.. |image1| image:: http://image.iswbm.com/20200607174235.png
- 关注公众号,获取最新干货!
diff --git a/source/c01/c01_22.md b/source/c01/c01_22.md
index 448951a..39cd84d 100644
--- a/source/c01/c01_22.md
+++ b/source/c01/c01_22.md
@@ -1,191 +1,175 @@
-# 1.22 如何修改 CentOS 6.x 上默认Python
+# 1.22 Python 炫技操作:海象运算符的三种用法
-最近在工作中遇到一个问题,就是有一个功能希望在各种服务器上实现,而服务器上的系统版本可能都不一样,有的是 CentOS 6.x,有的是 CentOS 7.x 。
+
-需要说明的一点是,CentOS 6.x 上的 Python 版本是 2.6.x 的,而 CentOS 7.x 上的 Python 版本是 2.7.x 的,这意味着我要实现的功能要适配这两种版本的系统。
+Python 版本发展非常快,如今最新的版本已经是 Pyhton 3.9,即便如此,有很多人甚至还停留在 3.6 或者 3.7,连 3.8 还没用上。
-你可能会说,这有什么的,自己写的时候,注意一下就好了。
+很多 Python 3.8 的特性还没来得及了解,就已经成为旧知识了,比如今天要说的海象运算符。
-事情其实没有那么容易,我要实现的功能是基于一个框架进行定制,需要修改不少的框架代码。这个框架在不同的 Linux 版本上,是有不同的版本的,而且差异巨大,曾经想过在 CentOS 6.x 和 CentOS 7.x 将这个框架安装成同一个版本,最后还是失败了,无法安装,原因就是高版本需要 Python2.7,而 CentOS 6.x 上只有 Python2.6。
+海象运算符是在 PEP 572 被提出的,直到 3.8 版本合入发布。
-这个历史问题一直遗留到现在,由于这次的功能影响到的代码较多,如果要对两个版本的框架分别进行定制的话,需要花不少的时间,为了不维护两套版本,避免浪费多余的精力去做适配,我决定将 CentOS 6.x 上默认的 Python2.6 升级成 Python2.7。
+它的英文原名叫 `Assignment Expressions`,翻译过来也就是 `赋值表达式`,不过现在大家更普遍地称之为海象运算符,就是因为它长得真的太像海象了。
-下面是整个升级过程,别看步骤简单,这些精简步骤的背后可是有不少的坑,被我踩过后,你可以直接使用了。
+
+## 1. 第一个用法:if/else
+可能有朋友是第一次接触这个新特性,所以还是简单的介绍一下这个海象运算符有什么用?
+在 Golang 中的条件语句可以直接在 if 中运算变量的获取后直接对这个变量进行判断,可以让你少写一行代码
-1. 首先确认下你机器上的默认的 Python 版本
+```go
+import "fmt"
-```shell
-$ python -V
-Python 2.6.6
-
-$ whereis python
-python: /usr/bin/python /usr/bin/python2.6 /usr/lib/python2.6 /usr/lib64/python2.6 /usr/local/bin/python /usr/include/python2.6 /usr/share/man/man1/python.1.gz
+func main() {
+ if age := 20;age > 18 {
+ fmt.Println("已经成年了")
+ }
+}
```
-
-
-2. 由于我们将使用编译安装的方式,所以要安装下 gcc,及一些工具包。
+若在 Python 3.8 之前,Python 必须得这样子写
-注意一定要全部安装,不然后面会发现有不少 python 的工具用不了。
+```python
+age = 20
+if age > 18:
+ print("已经成年了")
+```
-比如不安装 zlib 会无法安装 setuptools,不装 openssl 和 openssl-devel,会无法使用 pip 工具等
+但有了海象运算符之后,你可以和 Golang 一样(如果你没学过 Golang,那这里要注意,Golang 中的 `:=` 叫短变量声明,意思是声明并初始化,它和 Python 中的 `:=` 不是一个概念)
-```
-$ yum install gcc -y
-$ yum groupinstall "Development tools"
-$ yum install zlib-devel bzip2-devel openssl openssl-devel ncurses-devel sqlite-devel -y
+```python
+if (age:= 20) > 18:
+ print("已经成年了")
```
- 如果你这里未按照我的步骤来安装,你后面使用的时候出现了各种各样的问题,不要慌,只要再回来这里,把没安装的包装上,安装完成后,你需要进入第四步重新编译安装Python。
+## 2. 第二个用法:while
-3. 下载最新的 Python2.7.x 安装包,解压并进入指定目录
+在不使用 海象运算符之前,使用 while 循环来读取文件的时候,你也许会这么写
+```python
+file = open("demo.txt", "r")
+while True:
+ line = file.readline()
+ if not line:
+ break
+ print(line.strip())
```
-$ wget https://www.python.org/ftp/python/2.7.14/Python-2.7.14.tgz
-$ tar zxvf Python-2.7.14.tgz
-$ cd Python-2.7.14
-```
-
+但有了海象运算符之后,你可以这样
-4. 配置,编译,安装
-
-```shell
-# --prefix 指定 python 安装的路径
-$ ./configure --prefix=/usr/local/python/python2.7
-$ make
-$ make install
+```python
+file = open("demo.txt", "r")
+while (line := file.readline()):
+ print(line.strip())
```
-`./configure` 命令执行完毕之后创建一个文件creating Makefile,供下面的make命令使用 执行 `make install` 之后就会把程序安装到我们指定的目录中去。
+使用它替换以往的无限 while 循环写法更为惊艳
-Configure是一个可执行脚本,它有很多选项,在待安装的源码路径下使用命令./configure –help输出详细的选项列表。其中 `--prefix` 选项是配置安装的路径,如果不配置该选项,安装后可执行文件默认放在/usr /local/bin,库文件默认放在 `/usr/local/lib` ,配置文件默认放在 `/usr/local/etc` ,其它的资源文件放在 `/usr /local/share`。如果配置 `--prefix`,如:`./configure --prefix=/usr/local/test` 可以把所有资源文件放在/usr/local/test的路径中,不会杂乱。
+比如,实现一个需要命令行交互输入密码并检验的代码,你也许会这样子写
-用了 `--prefix` 选项的另一个好处是卸载软件或移植软件。当某个安装的软件不再需要时,只须简单的删除该安装目录,就可以把软件卸载得干干净净;移植软件只需拷贝整个目录到另外一个机器即可(相同的操作系统)。当然要卸载程序,也可以在原来的make目录下用一次 `make uninstall`,但前提是make文件指定过uninstall。
-
-
+```python
+while True:
+ p = input("Enter the password: ")
+ if p == "youpassword":
+ break
+```
-5. 查看系统的 Python 版本
+有了海象运算符之后,这样子写更为舒服
-```shell
-$ python -V
-Python 2.6.6
+```python
+while (p := input("Enter the password: ")) != "youpassword":
+ continue
```
- 如果你查看还是 Python 2.6.6 版本,请继续看第六步。
+## 3. 第三个用法:推导式
-6. 修改系统默认的 Python 版本
+这个系列的文章,几乎每篇都能看到推导式的身影,这一篇依旧如此。
-查看新安装的Python版本,当前系统的Python版本,并将系统指向的Python从2.6.x修改为2.7.x,再次查看当前系统的Python版本,已经变更为2.7.x
+在编码过程中,我很喜欢使用推导式,在简单的应用场景下,它简洁且不失高效。
-```shell
-# 这是我们刚安装的 Python
-$/usr/local/bin/python2.7 -V
-Python 2.7.14
+如下这段代码中,我会使用列表推导式得出所有会员中过于肥胖的人的 bmi 指数
-# 这是系统默认 Python
-$ /usr/bin/python -V
-Python 2.6.6
+```python
+members = [
+ {"name": "小五", "age": 23, "height": 1.75, "weight": 72},
+ {"name": "小李", "age": 17, "height": 1.72, "weight": 63},
+ {"name": "小陈", "age": 20, "height": 1.78, "weight": 82},
+]
-# 备份原来的 Python 文件
-$ mv /usr/bin/python /usr/bin/python.bak
+count = 0
-# 建立软链接,将我们刚安装的 python2.7 做为系统默认版本
-ln -s /usr/local/bin/python2.7 /usr/bin/python
+def get_bmi(info):
+ global count
+ count += 1
-# 再次查看 Python 版本,已经成功切换过来
-$ python -V
-Python 2.7.14
-```
+ print(f"执行了 {count} 次")
-7. 重新指定 yum 的Python版本
+ height = info["height"]
+ weight = info["weight"]
-上面我们改了系统的默认 Python 版本,由于CentOS 6.x 的 yum 是基于Python2.6 的,为了不影响 yum 的使用,需单独将yum指向python2.6版本。
+ return weight / (height**2)
-编辑: vim /usr/bin/yum ,将` /usr/bin/python` 改成 `/usr/bin/python2.6`
+# 查出所有会员中过于肥胖的人的 bmi 指数
+fat_bmis = [get_bmi(m) for m in members if get_bmi(m) > 24]
-```python
-#!/usr/bin/python2.6
+print(fat_bmis)
```
+输出如下
-
-8. 安装 setuptools 及 pip
-
-pip是python的安装工具,很多python的常用工具,都可以通过pip进行安装。要安装pip,首先要安装setuptools。从这个链接,你可以得到相关信息:https://pypi.python.org/pypi/setuptools
-
-```shell
-# 下载 setuptools
-$ wget https://pypi.python.org/packages/ff/d4/209f4939c49e31f5524fa0027bf1c8ec3107abaf7c61fdaad704a648c281/setuptools-21.0.0.tar.gz#md5=81964fdb89534118707742e6d1a1ddb4
```
-
-同样的,进行安装:
-
-```shell
-$ tar vxf setuptools-21.0.0.tar.gz
-$ cd setuptools-21.0.0
-$ python setup.py install
+执行了 1 次
+执行了 2 次
+执行了 3 次
+执行了 4 次
+[25.88057063502083]
```
-安装完成后,下载pip。其信息在如下网站:https://pypi.python.org/pypi/pip
+可以看到,会员数只有 3 个,但是 get_bmi 函数却执行了 4 次,原因是在判断时执行了 3 次,而在构造新的列表时又重复执行了一遍。
-```shell
-# 下载 pip
-wget https://pypi.python.org/packages/41/27/9a8d24e1b55bd8c85e4d022da2922cb206f183e2d18fee4e320c9547e751/pip-8.1.1.tar.gz#md5=6b86f11841e89c8241d689956ba99ed7
-```
+如果所有会员都是过于肥胖的,那最终将执行 6 次,这种在大量的数据下是比较浪费性能的,因此对于这种结构,我通常会使用传统的for 循环 + if 判断。
-同样的,进行安装
+```python
+fat_bmis = []
-```shell
-$ tar vxf pip-8.1.1.tar.gz
-$ cd pip-8.1.1
-$ python setup.py install
+# 查出所有会员中过于肥胖的人的 bmi 指数
+for m in members:
+ bmi = get_bmi(m)
+ if bmi > 24:
+ fat_bmis.append(bmi)
```
-安装完成后,执行 `pip list` 查看一下安装的包,若无异常,则一切顺利。或者你也可以试着安装一下第三方包 `pip install requests` 。
-
+在有了海象运算符之后,你就可以不用在这种场景下做出妥协。
+```python
+# 查出所有会员中过于肥胖的人的 bmi 指数
+fat_bmis = [bmi for m in members if (bmi := get_bmi(m)) > 24]
+```
-8. 转移cloudinit
-
-上面说的项目,其实就是 cloudinit。接下来就要将 centos 7.2 上的cloudinit 的目录整体拷贝到 centos 6.5 的/usr/local/lib/python2.7/site-packages/ 目录下
-
-
+最终从输出结果可以看出,只执行了 3 次
-然后安装一些 cloudinit 的依赖包。
+```
+执行了 1 次
+执行了 2 次
+执行了 3 次
+[25.88057063502083]
+```
-```shell
-$ pip install six requests prettytable jsonpatch configobj
+这里仅介绍了列表推导式,但在字典推导式和集合推导式中同样适用。不再演示。
-# 默认还是安装在 python2.6 下
-$ yum install PyYAML -y
-# 将这些文件拷贝到 python2.7 目录下
-# 如果你不知道 python2.7 的目录,使用 import sys;print sys.path 就可以打印
-$ cd /usr/lib64/python2.6/site-packages
-$ cp -r yaml/ /usr/local/lib/python2.7/site-packages/
-$ cp -p _yaml.so /usr/local/lib/python2.7/site-packages/
-$ cp -p PyYAML-3.10-py2.6.egg-info /usr/local/lib/python2.7/site-packages/
-```
-执行一下 cloudinit 的几个命令,没有问题,任务就完成了。
+海象运算符,是一个新奇的特性,有不少人觉得这样这种特性会破坏代码的可读性。确实在一个新鲜事物刚出来时是会这样,但我相信经过时间的沉淀后,越来越多的人使用它并享受它带来的便利时,这种争议也会慢慢消失在历史的长河中。
-```shell
-$ cloud-init init -l
-$ cloud-init init
-```
+---
-**参考文章**
-- https://www.cnblogs.com/stonehe/p/7944366.html
-
+
\ No newline at end of file
diff --git a/source/c01/c01_22.rst b/source/c01/c01_22.rst
index 80b32d3..177b431 100644
--- a/source/c01/c01_22.rst
+++ b/source/c01/c01_22.rst
@@ -1,206 +1,183 @@
-1.22 如何修改 CentOS 6.x 上默认Python
-=====================================
+1.22 Python 炫技操作:海象运算符的三种用法
+==========================================
-最近在工作中遇到一个问题,就是有一个功能希望在各种服务器上实现,而服务器上的系统版本可能都不一样,有的是
-CentOS 6.x,有的是 CentOS 7.x 。
-
-需要说明的一点是,CentOS 6.x 上的 Python 版本是 2.6.x 的,而 CentOS 7.x
-上的 Python 版本是 2.7.x
-的,这意味着我要实现的功能要适配这两种版本的系统。
-
-你可能会说,这有什么的,自己写的时候,注意一下就好了。
-
-事情其实没有那么容易,我要实现的功能是基于一个框架进行定制,需要修改不少的框架代码。这个框架在不同的
-Linux 版本上,是有不同的版本的,而且差异巨大,曾经想过在 CentOS 6.x 和
-CentOS 7.x
-将这个框架安装成同一个版本,最后还是失败了,无法安装,原因就是高版本需要
-Python2.7,而 CentOS 6.x 上只有 Python2.6。
-
-这个历史问题一直遗留到现在,由于这次的功能影响到的代码较多,如果要对两个版本的框架分别进行定制的话,需要花不少的时间,为了不维护两套版本,避免浪费多余的精力去做适配,我决定将
-CentOS 6.x 上默认的 Python2.6 升级成 Python2.7。
+|image0|
-下面是整个升级过程,别看步骤简单,这些精简步骤的背后可是有不少的坑,被我踩过后,你可以直接使用了。
+Python 版本发展非常快,如今最新的版本已经是 Pyhton
+3.9,即便如此,有很多人甚至还停留在 3.6 或者 3.7,连 3.8 还没用上。
-1. 首先确认下你机器上的默认的 Python 版本
+很多 Python 3.8
+的特性还没来得及了解,就已经成为旧知识了,比如今天要说的海象运算符。
-.. code:: shell
+海象运算符是在 PEP 572 被提出的,直到 3.8 版本合入发布。
- $ python -V
- Python 2.6.6
+它的英文原名叫 ``Assignment Expressions``\ ,翻译过来也就是
+``赋值表达式``\ ,不过现在大家更普遍地称之为海象运算符,就是因为它长得真的太像海象了。
- $ whereis python
- python: /usr/bin/python /usr/bin/python2.6 /usr/lib/python2.6 /usr/lib64/python2.6 /usr/local/bin/python /usr/include/python2.6 /usr/share/man/man1/python.1.gz
+|image1|
-2. 由于我们将使用编译安装的方式,所以要安装下 gcc,及一些工具包。
+1. 第一个用法:if/else
+----------------------
-注意一定要全部安装,不然后面会发现有不少 python 的工具用不了。
+可能有朋友是第一次接触这个新特性,所以还是简单的介绍一下这个海象运算符有什么用?
-比如不安装 zlib 会无法安装 setuptools,不装 openssl 和
-openssl-devel,会无法使用 pip 工具等
+在 Golang 中的条件语句可以直接在 if
+中运算变量的获取后直接对这个变量进行判断,可以让你少写一行代码
-::
+.. code:: go
- $ yum install gcc -y
- $ yum groupinstall "Development tools"
- $ yum install zlib-devel bzip2-devel openssl openssl-devel ncurses-devel sqlite-devel -y
+ import "fmt"
-如果你这里未按照我的步骤来安装,你后面使用的时候出现了各种各样的问题,不要慌,只要再回来这里,把没安装的包装上,安装完成后,你需要进入第四步重新编译安装Python。
+ func main() {
+ if age := 20;age > 18 {
+ fmt.Println("已经成年了")
+ }
+ }
-3. 下载最新的 Python2.7.x 安装包,解压并进入指定目录
+若在 Python 3.8 之前,Python 必须得这样子写
-::
+.. code:: python
- $ wget https://www.python.org/ftp/python/2.7.14/Python-2.7.14.tgz
- $ tar zxvf Python-2.7.14.tgz
- $ cd Python-2.7.14
+ age = 20
+ if age > 18:
+ print("已经成年了")
-4. 配置,编译,安装
+但有了海象运算符之后,你可以和 Golang 一样(如果你没学过
+Golang,那这里要注意,Golang 中的 ``:=``
+叫短变量声明,意思是声明并初始化,它和 Python 中的 ``:=`` 不是一个概念)
-.. code:: shell
+.. code:: python
- # --prefix 指定 python 安装的路径
- $ ./configure --prefix=/usr/local/python/python2.7
- $ make
- $ make install
+ if (age:= 20) > 18:
+ print("已经成年了")
-``./configure`` 命令执行完毕之后创建一个文件creating
-Makefile,供下面的make命令使用 执行 ``make install``
-之后就会把程序安装到我们指定的目录中去。
+2. 第二个用法:while
+--------------------
-Configure是一个可执行脚本,它有很多选项,在待安装的源码路径下使用命令./configure
-–help输出详细的选项列表。其中 ``--prefix``
-选项是配置安装的路径,如果不配置该选项,安装后可执行文件默认放在/usr
-/local/bin,库文件默认放在 ``/usr/local/lib`` ,配置文件默认放在
-``/usr/local/etc`` ,其它的资源文件放在
-``/usr /local/share``\ 。如果配置
-``--prefix``\ ,如:\ ``./configure --prefix=/usr/local/test``
-可以把所有资源文件放在/usr/local/test的路径中,不会杂乱。
+在不使用 海象运算符之前,使用 while 循环来读取文件的时候,你也许会这么写
-用了 ``--prefix``
-选项的另一个好处是卸载软件或移植软件。当某个安装的软件不再需要时,只须简单的删除该安装目录,就可以把软件卸载得干干净净;移植软件只需拷贝整个目录到另外一个机器即可(相同的操作系统)。当然要卸载程序,也可以在原来的make目录下用一次
-``make uninstall``\ ,但前提是make文件指定过uninstall。
+.. code:: python
-5. 查看系统的 Python 版本
+ file = open("demo.txt", "r")
+ while True:
+ line = file.readline()
+ if not line:
+ break
+ print(line.strip())
-.. code:: shell
+但有了海象运算符之后,你可以这样
- $ python -V
- Python 2.6.6
+.. code:: python
-如果你查看还是 Python 2.6.6 版本,请继续看第六步。
+ file = open("demo.txt", "r")
+ while (line := file.readline()):
+ print(line.strip())
-6. 修改系统默认的 Python 版本
+使用它替换以往的无限 while 循环写法更为惊艳
-查看新安装的Python版本,当前系统的Python版本,并将系统指向的Python从2.6.x修改为2.7.x,再次查看当前系统的Python版本,已经变更为2.7.x
+比如,实现一个需要命令行交互输入密码并检验的代码,你也许会这样子写
-.. code:: shell
+.. code:: python
- # 这是我们刚安装的 Python
- $/usr/local/bin/python2.7 -V
- Python 2.7.14
+ while True:
+ p = input("Enter the password: ")
+ if p == "youpassword":
+ break
- # 这是系统默认 Python
- $ /usr/bin/python -V
- Python 2.6.6
+有了海象运算符之后,这样子写更为舒服
- # 备份原来的 Python 文件
- $ mv /usr/bin/python /usr/bin/python.bak
+.. code:: python
- # 建立软链接,将我们刚安装的 python2.7 做为系统默认版本
- ln -s /usr/local/bin/python2.7 /usr/bin/python
+ while (p := input("Enter the password: ")) != "youpassword":
+ continue
- # 再次查看 Python 版本,已经成功切换过来
- $ python -V
- Python 2.7.14
+3. 第三个用法:推导式
+---------------------
-7. 重新指定 yum 的Python版本
+这个系列的文章,几乎每篇都能看到推导式的身影,这一篇依旧如此。
-上面我们改了系统的默认 Python 版本,由于CentOS 6.x 的 yum
-是基于Python2.6 的,为了不影响 yum
-的使用,需单独将yum指向python2.6版本。
+在编码过程中,我很喜欢使用推导式,在简单的应用场景下,它简洁且不失高效。
-编辑: vim /usr/bin/yum ,将\ ``/usr/bin/python`` 改成
-``/usr/bin/python2.6``
+如下这段代码中,我会使用列表推导式得出所有会员中过于肥胖的人的 bmi 指数
.. code:: python
- #!/usr/bin/python2.6
-
-8. 安装 setuptools 及 pip
-
-pip是python的安装工具,很多python的常用工具,都可以通过pip进行安装。要安装pip,首先要安装setuptools。从这个链接,你可以得到相关信息:https://pypi.python.org/pypi/setuptools
-
-.. code:: shell
-
- # 下载 setuptools
- $ wget https://pypi.python.org/packages/ff/d4/209f4939c49e31f5524fa0027bf1c8ec3107abaf7c61fdaad704a648c281/setuptools-21.0.0.tar.gz#md5=81964fdb89534118707742e6d1a1ddb4
+ members = [
+ {"name": "小五", "age": 23, "height": 1.75, "weight": 72},
+ {"name": "小李", "age": 17, "height": 1.72, "weight": 63},
+ {"name": "小陈", "age": 20, "height": 1.78, "weight": 82},
+ ]
-同样的,进行安装:
+ count = 0
-.. code:: shell
+ def get_bmi(info):
+ global count
+ count += 1
- $ tar vxf setuptools-21.0.0.tar.gz
- $ cd setuptools-21.0.0
- $ python setup.py install
+ print(f"执行了 {count} 次")
-安装完成后,下载pip。其信息在如下网站:https://pypi.python.org/pypi/pip
+ height = info["height"]
+ weight = info["weight"]
-.. code:: shell
+ return weight / (height**2)
- # 下载 pip
- wget https://pypi.python.org/packages/41/27/9a8d24e1b55bd8c85e4d022da2922cb206f183e2d18fee4e320c9547e751/pip-8.1.1.tar.gz#md5=6b86f11841e89c8241d689956ba99ed7
+ # 查出所有会员中过于肥胖的人的 bmi 指数
+ fat_bmis = [get_bmi(m) for m in members if get_bmi(m) > 24]
-同样的,进行安装
+ print(fat_bmis)
-.. code:: shell
+输出如下
- $ tar vxf pip-8.1.1.tar.gz
- $ cd pip-8.1.1
- $ python setup.py install
+::
-安装完成后,执行 ``pip list``
-查看一下安装的包,若无异常,则一切顺利。或者你也可以试着安装一下第三方包
-``pip install requests`` 。
+ 执行了 1 次
+ 执行了 2 次
+ 执行了 3 次
+ 执行了 4 次
+ [25.88057063502083]
-8. 转移cloudinit
+可以看到,会员数只有 3 个,但是 get_bmi 函数却执行了 4
+次,原因是在判断时执行了 3 次,而在构造新的列表时又重复执行了一遍。
-上面说的项目,其实就是 cloudinit。接下来就要将 centos 7.2 上的cloudinit
-的目录整体拷贝到 centos 6.5 的/usr/local/lib/python2.7/site-packages/
-目录下
+如果所有会员都是过于肥胖的,那最终将执行 6
+次,这种在大量的数据下是比较浪费性能的,因此对于这种结构,我通常会使用传统的for
+循环 + if 判断。
-|image0|
+.. code:: python
-然后安装一些 cloudinit 的依赖包。
+ fat_bmis = []
-.. code:: shell
+ # 查出所有会员中过于肥胖的人的 bmi 指数
+ for m in members:
+ bmi = get_bmi(m)
+ if bmi > 24:
+ fat_bmis.append(bmi)
- $ pip install six requests prettytable jsonpatch configobj
+在有了海象运算符之后,你就可以不用在这种场景下做出妥协。
- # 默认还是安装在 python2.6 下
- $ yum install PyYAML -y
+.. code:: python
- # 将这些文件拷贝到 python2.7 目录下
- # 如果你不知道 python2.7 的目录,使用 import sys;print sys.path 就可以打印
- $ cd /usr/lib64/python2.6/site-packages
- $ cp -r yaml/ /usr/local/lib/python2.7/site-packages/
- $ cp -p _yaml.so /usr/local/lib/python2.7/site-packages/
- $ cp -p PyYAML-3.10-py2.6.egg-info /usr/local/lib/python2.7/site-packages/
+ # 查出所有会员中过于肥胖的人的 bmi 指数
+ fat_bmis = [bmi for m in members if (bmi := get_bmi(m)) > 24]
-执行一下 cloudinit 的几个命令,没有问题,任务就完成了。
+最终从输出结果可以看出,只执行了 3 次
-.. code:: shell
+::
- $ cloud-init init -l
- $ cloud-init init
+ 执行了 1 次
+ 执行了 2 次
+ 执行了 3 次
+ [25.88057063502083]
-**参考文章**
+这里仅介绍了列表推导式,但在字典推导式和集合推导式中同样适用。不再演示。
-- https://www.cnblogs.com/stonehe/p/7944366.html
+海象运算符,是一个新奇的特性,有不少人觉得这样这种特性会破坏代码的可读性。确实在一个新鲜事物刚出来时是会这样,但我相信经过时间的沉淀后,越来越多的人使用它并享受它带来的便利时,这种争议也会慢慢消失在历史的长河中。
-.. figure:: http://image.python-online.cn/image-20200320125724880.png
- :alt: 关注公众号,获取最新干货!
+--------------
- 关注公众号,获取最新干货!
+|image2|
-.. |image0| image:: http://image.python-online.cn/20190831160317.png
+.. |image0| image:: http://image.iswbm.com/20200602135014.png
+.. |image1| image:: http://image.iswbm.com/image-20200418122739417.png
+.. |image2| image:: http://image.iswbm.com/20200607174235.png
diff --git a/source/c01/c01_23.md b/source/c01/c01_23.md
index 82e6a09..7142677 100644
--- a/source/c01/c01_23.md
+++ b/source/c01/c01_23.md
@@ -1,126 +1,142 @@
-# 1.23 Pythonista 学习 Js
-
-1. JavaScript的设计者希望用`null`表示一个空的值,而`undefined`表示值未定义。事实证明,这并没有什么卵用,区分两者的意义不大。大多数情况下,我们都应该用`null`。`undefined`仅仅在判断函数参数是否传递的情况下有用。
-
-
-
-2. 在JavaScript中,使用等号`=`对变量进行赋值。可以把任意数据类型赋值给变量,同一个变量可以反复赋值,而且可以是不同类型的变量,但是要注意只能用`var`申明一次。
-
-
-
-3. var 申明的变量不是全局变量,如果不使用 var 申明变量,就会变成全局变量,多个js文件会共享这个变量,互相影响。这个语言设计弊端,在 ECMA 推出 strict 模式后得到解决,不使用 var 申明变量的会运行出错。
-
-
-
-4. 直接操作数组的长度,或者对超出数组长度的索引赋值,可以扩容和压缩数组的大小。
-
-
-
-5. 对象之间的比较和Python差异巨大,`=` 表示值的比较,`===` 表示对象类型的比较,主要分为下面三种情况:
-
- ```javascript
- // 1. 对于string,number等【基础类型】,==和===是有区别的。
- // 如果是不同类型,== 会将两边转化成同一类型再看值是否相等
- // 如果是相同类型,直接对值进行比较。
- alert('1'==1);//结果是true
- alert('1'===1);//结果是false
-
-
- // 2. 如果是 Array,Object等高级类型,==和===是没有区别的。
- // 直接比较指针地址。,跟 Python 里的 is 等同。
-
-
- // 3. 基础类型与高级类型,==和===是有区别的。
- // 对于==,将高级转化为基础类型,进行“值”比较。
- // 因为类型不同,===结果为false。
- var a = new String('1');//定义一个string的高级类型
- var b = '1';//定一个基础类型字符串
- alert(b==a);//为true
- alert(b===a);//为false
- ```
-
-6. 定义一个函数,其参数个数可以少于传入的参数个数,基于可以不定义参数,还可以传入参数。那传入的多余的参数如何获取呢?可以使用 arguments 这个变量取得所有的参数,但这个变量仅在参数内起作用。
-
- ```python
- function foo(a, b, ...rest) {
- console.log('a = ' + a);
- console.log('b = ' + b);
- console.log(rest);
- }
- ```
-
-7. JavaScript的函数定义有个特点,它会先扫描整个函数体的语句,把所有申明的变量“提升”到函数顶部,但是并不会提前为变量赋值。
-
- ```javascript
- 'use strict';
-
- function foo() {
- var x = 'Hello, ' + y;
- console.log(x);
- var y = 'Bob';
- }
-
- foo();
-
- // 对于 javascript 引擎,看到代码相当于
- function foo() {
- var y; // 提升变量y的申明,此时y为undefined
- var x = 'Hello, ' + y;
- console.log(x);
- y = 'Bob';
- }
- ```
-
-
-
-8. This有个巨坑,需要新手注意。
-
- 如下,当没有 `var that = this;` 这句时,函数内部的函数里的this指向的是 window 对象。
-
- ```javascript
- 'use strict';
-
- var xiaoming = {
- name: '小明',
- birth: 1990,
- age: function () {
- var that = this; // 在方法内部一开始就捕获this
- function getAgeFromBirth() {
- var y = new Date().getFullYear();
- return y - that.birth; // 用that而不是this
- }
- return getAgeFromBirth();
- }
- };
-
- xiaoming.age(); // 25
- ```
-
- 或者,可以用 apply 的方法来解决这个问题,apply 的第一个参数是人该函数要绑定的对象,第二个参数是一个列表,装的是要传递给这个函数据参数。
-
- ```javascript
- function getAge() {
- var y = new Date().getFullYear();
- return y - this.birth;
- }
-
- var xiaoming = {
- name: '小明',
- birth: 1990,
- age: getAge
- };
-
- xiaoming.age(); // 29
- getAge.apply(xiaoming, []); // 29, this指向xiaoming, 参数为空
- ```
-
- 假如,不想绑定给任何对象,第一个参数可以用 null。如
-
- ```javascript
- Math.max.apply(null, [3, 5, 4]); // 5
- Math.max.call(null, 3, 5, 4); // 5
- ```
-
-
-
-
\ No newline at end of file
+# 1.23 Python 炫技操作:模块重载的五种方法
+
+
+
+## 环境准备
+
+新建一个 foo 文件夹,其下包含一个 bar.py 文件
+
+```
+$ tree foo
+foo
+└── bar.py
+
+0 directories, 1 file
+```
+
+bar.py 的内容非常简单,只写了个 print 语句
+
+```
+print("successful to be imported")
+```
+
+只要 bar.py 被导入一次,就被执行一次 print
+
+## 禁止重复导入
+
+'由于有 sys.modules 的存在,当你导入一个已导入的模块时,实际上是没有效果的。'
+
+```
+>>> from foo import bar
+successful to be imported
+>>> from foo import bar
+>>>
+```
+
+## 重复导入方法一
+
+如果你使用的 python2(记得前面在 foo 文件夹下加一个 `__init__.py`),有一个 reload 的方法可以直接使用
+
+```
+>>> from foo import bar
+successful to be imported
+>>> from foo import bar
+>>>
+>>> reload(bar)
+successful to be imported
+
+```
+
+如果你使用的 python3 那方法就多了,详细请看下面
+
+## 重复导入方法二
+
+如果你使用 Python3.0 -> 3.3,那么可以使用 imp.reload 方法
+
+```
+>>> from foo import bar
+successful to be imported
+>>> from foo import bar
+>>>
+>>> import imp
+>>> imp.reload(bar)
+successful to be imported
+
+```
+
+但是这个方法在 Python 3.4+,就不推荐使用了
+
+```
+:1: DeprecationWarning: the imp module is deprecated in favour of importlib; see the module's documentation for alternative uses
+```
+
+## 重复导入方法三
+
+如果你使用的 Python 3.4+,请使用 importlib.reload 方法
+
+```
+>>> from foo import bar
+successful to be imported
+>>> from foo import bar
+>>>
+>>> import importlib
+>>> importlib.reload(bar)
+successful to be imported
+
+```
+
+## 重复导入方法四
+
+如果你对包的加载器有所了解(详细可以翻阅我以前写的文章:https://iswbm.com/84.html)
+
+还可以使用下面的方法
+
+```
+>>> from foo import bar
+successful to be imported
+>>> from foo import bar
+>>>
+>>> bar.__spec__.loader.load_module()
+successful to be imported
+
+```
+
+## 重复导入方法五
+
+既然影响我们重复导入的是 sys.modules,那我们只要将已导入的包从其中移除是不是就好了呢?
+
+```
+>>> import foo.bar
+successful to be imported
+>>>
+>>> import foo.bar
+>>>
+>>> import sys
+>>> sys.modules['foo.bar']
+
+>>> del sys.modules['foo.bar']
+>>>
+>>> import foo.bar
+successful to be imported
+```
+
+有没有发现在前面的例子里我使用的都是 `from foo import bar`,在这个例子里,却使用 `import foo.bar`,这是为什么呢?
+
+这是因为如果你使用 `from foo import bar` 这种方式,想使用移除 sys.modules 来重载模块这种方法是失效的。
+
+这应该算是一个小坑,不知道的人,会掉入坑中爬不出来。
+
+```
+>>> import foo.bar
+successful to be imported
+>>>
+>>> import foo.bar
+>>>
+>>> import sys
+>>> del sys.modules['foo.bar']
+>>> from foo import bar
+>>>
+```
+
+
+
diff --git a/source/c01/c01_23.rst b/source/c01/c01_23.rst
index 19f5b35..c2f08a9 100644
--- a/source/c01/c01_23.rst
+++ b/source/c01/c01_23.rst
@@ -1,124 +1,156 @@
-1.23 Pythonista 学习 Js
-=======================
+1.23 Python 炫技操作:模块重载的五种方法
+========================================
-1. JavaScript的设计者希望用\ ``null``\ 表示一个空的值,而\ ``undefined``\ 表示值未定义。事实证明,这并没有什么卵用,区分两者的意义不大。大多数情况下,我们都应该用\ ``null``\ 。\ ``undefined``\ 仅仅在判断函数参数是否传递的情况下有用。
+|image0|
-2. 在JavaScript中,使用等号\ ``=``\ 对变量进行赋值。可以把任意数据类型赋值给变量,同一个变量可以反复赋值,而且可以是不同类型的变量,但是要注意只能用\ ``var``\ 申明一次。
+环境准备
+--------
-3. var 申明的变量不是全局变量,如果不使用 var
- 申明变量,就会变成全局变量,多个js文件会共享这个变量,互相影响。这个语言设计弊端,在
- ECMA 推出 strict 模式后得到解决,不使用 var 申明变量的会运行出错。
+新建一个 foo 文件夹,其下包含一个 bar.py 文件
-4. 直接操作数组的长度,或者对超出数组长度的索引赋值,可以扩容和压缩数组的大小。
+::
-5. 对象之间的比较和Python差异巨大,\ ``=`` 表示值的比较,\ ``===``
- 表示对象类型的比较,主要分为下面三种情况:
+ $ tree foo
+ foo
+ └── bar.py
- .. code:: javascript
+ 0 directories, 1 file
- // 1. 对于string,number等【基础类型】,==和===是有区别的。
- // 如果是不同类型,== 会将两边转化成同一类型再看值是否相等
- // 如果是相同类型,直接对值进行比较。
- alert('1'==1);//结果是true
- alert('1'===1);//结果是false
+bar.py 的内容非常简单,只写了个 print 语句
+::
- // 2. 如果是 Array,Object等高级类型,==和===是没有区别的。
- // 直接比较指针地址。,跟 Python 里的 is 等同。
+ print("successful to be imported")
+只要 bar.py 被导入一次,就被执行一次 print
- // 3. 基础类型与高级类型,==和===是有区别的。
- // 对于==,将高级转化为基础类型,进行“值”比较。
- // 因为类型不同,===结果为false。
- var a = new String('1');//定义一个string的高级类型
- var b = '1';//定一个基础类型字符串
- alert(b==a);//为true
- alert(b===a);//为false
+禁止重复导入
+------------
-6. 定义一个函数,其参数个数可以少于传入的参数个数,基于可以不定义参数,还可以传入参数。那传入的多余的参数如何获取呢?可以使用
- arguments 这个变量取得所有的参数,但这个变量仅在参数内起作用。
+‘由于有 sys.modules
+的存在,当你导入一个已导入的模块时,实际上是没有效果的。’
- .. code:: python
+::
- function foo(a, b, ...rest) {
- console.log('a = ' + a);
- console.log('b = ' + b);
- console.log(rest);
- }
+ >>> from foo import bar
+ successful to be imported
+ >>> from foo import bar
+ >>>
-7. JavaScript的函数定义有个特点,它会先扫描整个函数体的语句,把所有申明的变量“提升”到函数顶部,但是并不会提前为变量赋值。
+重复导入方法一
+--------------
- .. code:: javascript
+如果你使用的 python2(记得前面在 foo 文件夹下加一个
+``__init__.py``\ ),有一个 reload 的方法可以直接使用
- 'use strict';
+::
- function foo() {
- var x = 'Hello, ' + y;
- console.log(x);
- var y = 'Bob';
- }
+ >>> from foo import bar
+ successful to be imported
+ >>> from foo import bar
+ >>>
+ >>> reload(bar)
+ successful to be imported
+
- foo();
+如果你使用的 python3 那方法就多了,详细请看下面
- // 对于 javascript 引擎,看到代码相当于
- function foo() {
- var y; // 提升变量y的申明,此时y为undefined
- var x = 'Hello, ' + y;
- console.log(x);
- y = 'Bob';
- }
+重复导入方法二
+--------------
-8. This有个巨坑,需要新手注意。
+如果你使用 Python3.0 -> 3.3,那么可以使用 imp.reload 方法
- 如下,当没有 ``var that = this;``
- 这句时,函数内部的函数里的this指向的是 window 对象。
+::
- .. code:: javascript
+ >>> from foo import bar
+ successful to be imported
+ >>> from foo import bar
+ >>>
+ >>> import imp
+ >>> imp.reload(bar)
+ successful to be imported
+
- 'use strict';
+但是这个方法在 Python 3.4+,就不推荐使用了
- var xiaoming = {
- name: '小明',
- birth: 1990,
- age: function () {
- var that = this; // 在方法内部一开始就捕获this
- function getAgeFromBirth() {
- var y = new Date().getFullYear();
- return y - that.birth; // 用that而不是this
- }
- return getAgeFromBirth();
- }
- };
+::
- xiaoming.age(); // 25
+ :1: DeprecationWarning: the imp module is deprecated in favour of importlib; see the module's documentation for alternative uses
- 或者,可以用 apply 的方法来解决这个问题,apply
- 的第一个参数是人该函数要绑定的对象,第二个参数是一个列表,装的是要传递给这个函数据参数。
+重复导入方法三
+--------------
- .. code:: javascript
+如果你使用的 Python 3.4+,请使用 importlib.reload 方法
- function getAge() {
- var y = new Date().getFullYear();
- return y - this.birth;
- }
+::
- var xiaoming = {
- name: '小明',
- birth: 1990,
- age: getAge
- };
+ >>> from foo import bar
+ successful to be imported
+ >>> from foo import bar
+ >>>
+ >>> import importlib
+ >>> importlib.reload(bar)
+ successful to be imported
+
- xiaoming.age(); // 29
- getAge.apply(xiaoming, []); // 29, this指向xiaoming, 参数为空
+重复导入方法四
+--------------
- 假如,不想绑定给任何对象,第一个参数可以用 null。如
+如果你对包的加载器有所了解(详细可以翻阅我以前写的文章:https://iswbm.com/84.html)
- .. code:: javascript
+还可以使用下面的方法
- Math.max.apply(null, [3, 5, 4]); // 5
- Math.max.call(null, 3, 5, 4); // 5
+::
-.. figure:: http://image.python-online.cn/image-20200320125724880.png
- :alt: 关注公众号,获取最新干货!
+ >>> from foo import bar
+ successful to be imported
+ >>> from foo import bar
+ >>>
+ >>> bar.__spec__.loader.load_module()
+ successful to be imported
+
+
+重复导入方法五
+--------------
+
+既然影响我们重复导入的是
+sys.modules,那我们只要将已导入的包从其中移除是不是就好了呢?
+
+::
+
+ >>> import foo.bar
+ successful to be imported
+ >>>
+ >>> import foo.bar
+ >>>
+ >>> import sys
+ >>> sys.modules['foo.bar']
+
+ >>> del sys.modules['foo.bar']
+ >>>
+ >>> import foo.bar
+ successful to be imported
+
+有没有发现在前面的例子里我使用的都是
+``from foo import bar``\ ,在这个例子里,却使用
+``import foo.bar``\ ,这是为什么呢?
+
+这是因为如果你使用 ``from foo import bar`` 这种方式,想使用移除
+sys.modules 来重载模块这种方法是失效的。
+
+这应该算是一个小坑,不知道的人,会掉入坑中爬不出来。
+
+::
+
+ >>> import foo.bar
+ successful to be imported
+ >>>
+ >>> import foo.bar
+ >>>
+ >>> import sys
+ >>> del sys.modules['foo.bar']
+ >>> from foo import bar
+ >>>
+
+.. |image0| image:: http://image.iswbm.com/20200602135014.png
- 关注公众号,获取最新干货!
diff --git a/source/c01/c01_24.md b/source/c01/c01_24.md
index dc08028..ab429b8 100644
--- a/source/c01/c01_24.md
+++ b/source/c01/c01_24.md
@@ -1,723 +1,148 @@
-# 1.24 深入探讨 Python 的 import 机制:实现远程导入模块
+# 1.24 Python 炫技操作:条件语句的七种写法
-所谓的模块导入( `import` ),是指在一个模块中使用另一个模块的代码的操作,它有利于代码的复用。
+
-在 Python 中使用 import 关键字来实现这个操作,但不是唯一的方法,还有 `importlib.import_module()` 和 `__import__()` 等。
-
-也许你看到这个标题,会说我怎么会发这么基础的文章?
-
-与此相反。恰恰我觉得这篇文章的内容可以算是 Python 的进阶技能,会深入地探讨并以真实案例讲解 Python import Hook 的知识点。
-
-当然为了使文章更系统、全面,前面会有小篇幅讲解基础知识点,但请你有耐心的往后读下去,因为后面才是本篇文章的精华所在,希望你不要错过。
-
-
-
-## 1. 导入系统的基础
-
-### 1.1 导入单元构成
-
-导入单元有多种,可以是模块、包及变量等。
-
-对于这些基础的概念,对于新手还是有必要介绍一下它们的区别。
-
-**模块**:类似 \*.py,\*.pyc, \*.pyd ,\*.so,\*.dll 这样的文件,是 Python 代码载体的最小单元。
-
-**包** 还可以细分为两种:
-
-- Regular packages:是一个带有 `__init__.py` 文件的文件夹,此文件夹下可包含其他子包,或者模块
-- Namespace packages
-
-关于 Namespace packages,有的人会比较陌生,我这里摘抄官方文档的一段说明来解释一下。
-
-Namespace packages 是由多个 部分 构成的,每个部分为父包增加一个子包。 各个部分可能处于文件系统的不同位置。 部分也可能处于 zip 文件中、网络上,或者 Python 在导入期间可以搜索的其他地方。 命名空间包并不一定会直接对应到文件系统中的对象;它们有可能是无实体表示的虚拟模块。
-
-命名空间包的 `__path__ ` 属性不使用普通的列表。 而是使用定制的可迭代类型,如果其父包的路径 (或者最高层级包的 sys.path) 发生改变,这种对象会在该包内的下一次导入尝试时自动执行新的对包部分的搜索。
-
-命名空间包没有 `parent/__init__.py` 文件。 实际上,在导入搜索期间可能找到多个 parent 目录,每个都由不同的部分所提供。 因此 parent/one 的物理位置不一定与 parent/two 相邻。 在这种情况下,Python 将为顶级的 parent 包创建一个命名空间包,无论是它本身还是它的某个子包被导入。
-
-
-
-### 1.2 相对/绝对导入
-
-当我们 import 导入模块或包时,Python 提供两种导入方式:
-
-- 相对导入(relative import ):from . import B 或 from ..A import B,其中.表示当前模块,..表示上层模块
-- 绝对导入(absolute import):import foo.bar 或者 from foo import bar
-
-你可以根据实际需要进行选择,但有必要说明的是,在早期的版本( Python2.6 之前),Python 默认使用的相对导入。而后来的版本中( Python2.6 之后),都以绝对导入为默认使用的导入方式。
-
-使用绝对路径和相对路径各有利弊:
-
-- 当你在开发维护自己的项目时,应当使用相对路径导入,这样可以避免硬编码带来的麻烦。
-- 而使用绝对路径,会让你模块导入结构更加清晰,而且也避免了重名的包冲突而导入错误。
-
-### 1.3 导入的标准写法
-
-在 PEP8 中对模块的导入提出了要求,遵守 PEP8规范能让你的代码更具有可读性,我这边也列一下:
-
-- import 语句应当分行书写
-
-```python
-# bad
-import os,sys
-
-# good
-import os
-import sys
-```
-
-- import语句应当使用absolute import
-
-```python
-# bad
-from ..bar import Bar
-
-# good
-from foo.bar import test
-```
-
-- import语句应当放在文件头部,置于模块说明及docstring之后,全局变量之前
-
-- import语句应该按照顺序排列,每组之间用一个空格分隔,按照内置模块,第三方模块,自己所写的模块调用顺序,同时每组内部按照字母表顺序排列
-
-```python
-# 内置模块
-import os
-import sys
-
-# 第三方模块
-import flask
-
-# 本地模块
-from foo import bar
-```
-
-### 1.4 几个有用的 sys 变量
-
-`sys.path` 可以列出 Python 模块查找的目录列表
-
-```python
->>> import sys
->>> from pprint import pprint
->>> pprint(sys.path)
-['',
- '/Library/Frameworks/Python.framework/Versions/3.6/lib/python36.zip',
- '/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6',
- '/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/lib-dynload',
- '/Users/MING/Library/Python/3.6/lib/python/site-packages',
- '/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages']
->>>
-```
-
-`sys.meta_path` 存放的是所有的查找器。
-
-```python
->>> import sys
->>> from pprint import pprint
->>> pprint(sys.meta_path)
-[,
- ,
- ]
-```
-
-
-
-`sys.path_importer_cache` 比 `sys.path` 会更大点, 因为它会为所有被加载代码的目录记录它们的查找器。 这包括包的子目录,这些通常在 `sys.path` 中是不存在的。
-
-```python
->>> import sys
->>> from pprint import pprint
->>> pprint(sys.path_importer_cache)
-{'/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6': FileFinder('/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6'),
- '/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/collections': FileFinder('/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/collections'),
- '/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/encodings': FileFinder('/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/encodings'),
- '/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/lib-dynload': FileFinder('/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/lib-dynload'),
- '/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages': FileFinder('/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages'),
- '/Library/Frameworks/Python.framework/Versions/3.6/lib/python36.zip': None,
- '/Users/MING': FileFinder('/Users/MING'),
- '/Users/MING/Library/Python/3.6/lib/python/site-packages': FileFinder('/Users/MING/Library/Python/3.6/lib/python/site-packages')}
-```
-
-
-
-## 2. \__import__ 的妙用
-
-import 关键字的使用,可以说是基础中的基础。
-
-但这不是模块唯一的方法,还有 `importlib.import_module()` 和 `__import__()` 等。
-
-和 import 不同的是,`__import__` 是一个函数,也正是因为这个原因,使得 `__import__` 的使用会更加灵活,常常用于框架中,对于插件的动态加载。
-
-实际上,当我们调用 import 导入模块时,其内部也是调用了 `__import__` ,请看如下两种导入方法,他们是等价的。
+## 原代码
+这是一段非常简单的通过年龄判断一个人是否成年的代码,由于代码行数过多,有些人就不太愿意这样写,因为这体现不出自己多年的 Python 功力。
```python
-# 使用 import
-import os
-
-# 使用 __import__
-os = __import__('os')
-```
-
-通过举一反三,下面两种方法同样也是等价的。
-
-```python
-# 使用 import .. as ..
-import pandas as pd
-
-# 使用 __import__
-pd = __import__('pandas')
+if age > 18:
+ return "已成年"
+else:
+ return "未成年"
```
-上面我说 `__import__` 常常用于插件的动态,事实上也只有它能做到(相对于 import 来说)。
+下面我列举了六种这段代码的变异写法,一个比一个还 6 ,单独拿出来比较好理解,放在工程代码里,没用过这些学法的人,一定会看得一脸懵逼,理解了之后,又不经意大呼:**卧槽,还可以这样写?**,而后就要开始骂街了:**这是给人看的代码?** (除了第一种之外)
-`插件`通常会位于某一特定的文件夹下,在使用过程中,可能你并不会用到全部的插件,也可能你会新增插件。
+## 第一种
-如果使用 import 关键字这种硬编码的方式,显然太不优雅了,当你要新增/修改插件的时候,都需要你修改代码。更合适的做法是,将这些插件以配置的方式,写在配置文件中,然后由代码去读取你的配置,动态导入你要使用的插件,即灵活又方便,也不容易出错。
-
-假如我的一个项目中,有 `plugin01` 、`plugin02`、`plugin03 ` 、`plugin04` 四个插件,这些插件下都会实现一个核心方法 `run()` 。但有时候我不想使用全部的插件,只想使用 `plugin02`、`plugin04 ` ,那我就在配置文件中写我要使用的两个插件。
-
-```shell
-# my.conf
-custom_plugins=['plugin02', 'plugin04']
-```
-
-那我如何使用动态加载,并运行他们呢?
+语法:
```python
-# main.py
-
-for plugin in conf.custom_plugins:
- __import__(plugin)
- sys.modules[plugin].run()
-```
-
-
-
-## 3. 理解模块的缓存
-
-在一个模块内部重复引用另一个相同模块,实际并不会导入两次,原因是在使用关键字 `import` 导入模块时,它会先检索 `sys.modules` 里是否已经载入这个模块了,如果已经载入,则不会再次导入,如果不存在,才会去检索导入这个模块。
-
-来实验一下,在 `my_mod02` 这个模块里,我 import 两次 `my_mod01` 这个模块,按逻辑每一次 import 会一次 `my_mod01` 里的代码(即打印 `in mod01`),但是验证结果是,只打印了一次。
-
-```shell
-$ cat my_mod01.py
-print('in mod01')
-
-$ cat my_mod02.py
-import my_mod01
-import my_mod01
-
-$ python my_mod02.py
-in mod01
+ if else
```
-该现象的解释是:因为有 `sys.modules` 的存在。
-
-`sys.modules` 是一个字典(key:模块名,value:模块对象),它存放着在当前 namespace 所有已经导入的模块对象。
+例子
```python
-# test_module.py
-
-import sys
-print(sys.modules.get('json', 'NotFound'))
-
-import json
-print(sys.modules.get('json', 'NotFound'))
-```
-
-运行结果如下,可见在 导入后 json 模块后,`sys.modules` 才有了 json 模块的对象。
-
-```shell
-$ python test_module.py
-NotFound
-
+>>> age1 = 20
+>>> age2 = 17
+>>>
+>>>
+>>> msg1 = "已成年" if age1 > 18 else "未成年"
+>>> print msg1
+已成年
+>>>
+>>> msg2 = "已成年" if age2 > 18 else "未成年"
+>>> print msg2
+未成年
+>>>
```
- 由于有缓存的存在,使得我们无法重新载入一个模块。
-
-但若你想反其道行之,可以借助 importlib 这个神奇的库来实现。事实也确实有此场景,比如在代码调试中,在发现代码有异常并修改后,我们通常要重启服务再次载入程序。这时候,若有了模块重载,就无比方便了,修改完代码后也无需服务的重启,就能继续调试。
+## 第二种
-还是以上面的例子来理解,`my_mod02.py` 改写成如下
+语法
```python
-# my_mod02.py
-
-import importlib
-import my_mod01
-importlib.reload(my_mod01)
-```
-
-使用 python3 来执行这个模块,与上面不同的是,这边执行了两次 `my_mod01.py`
-
-```shell
-$ python3 my_mod02.py
-in mod01
-in mod01
+ and or
```
-
-
-## 4. 查找器与加载器
-
-如果指定名称的模块在 `sys.modules` 找不到,则将发起调用 Python 的导入协议以查找和加载该模块。
-
-此协议由两个概念性模块构成,即 `查找器` 和 `加载器`。
-
-一个 Python 的模块的导入,其实可以再细分为两个过程:
-
-1. 由查找器实现的模块查找
-2. 由加载器实现的模块加载
-
-### 4.1 查找器是什么?
-
-查找器(finder),简单点说,查找器定义了一个模块查找机制,让程序知道该如何找到对应的模块。
-
-其实 Python 内置了多个默认查找器,其存在于 sys.meta_path 中。
-
-但这些查找器对应使用者来说,并不是那么重要,因此在 Python 3.3 之前, Python 解释将其隐藏了,我们称之为隐式查找器。
+例子
```python
-# Python 2.7
->>> import sys
->>> sys.meta_path
-[]
+>>> msg1 = age1 > 18 and "已成年" or "未成年"
+>>> msg2 = "已成年" if age2 > 18 else "未成年"
>>>
+>>> print(msg1)
+已成年
+>>>
+>>> print(msg2)
+未成年
```
-由于这点不利于开发者深入理解 import 机制,在 Python 3.3 后,所有的模块导入机制都会通过 sys.meta_path 暴露,不会在有任何隐式导入机制。
-
-```python
-# Python 3.6
->>> import sys
->>> from pprint import pprint
->>> pprint(sys.meta_path)
-[,
- ,
- ]
-```
-
-观察一下 Python 默认的这几种查找器 (finder),可以分为三种:
-
-- 一种知道如何导入内置模块
-- 一种知道如何导入冻结模块
-- 一种知道如何导入来自 [import path](https://docs.python.org/zh-cn/3/glossary.html#term-import-path) 的模块 (即 [path based finder](https://docs.python.org/zh-cn/3/glossary.html#term-path-based-finder))。
-
-那我们能不能自已定义一个查找器呢?当然可以,你只要
-
-- 定义一个实现了 find_module 方法的类(py2和py3均可),或者实现 find_loader 类方法(仅 py3 有效),如果找到模块需要返回一个 loader 对象或者 ModuleSpec 对象(后面会讲),没找到需要返回 None
-- 定义完后,要使用这个查找器,必须注册它,将其插入在 sys.meta_path 的首位,这样就能优先使用。
-
-```python
-import sys
-
-class MyFinder(object):
- @classmethod
- def find_module(cls, name, path, target=None):
- print("Importing", name, path, target)
- # 将在后面定义
- return MyLoader()
-
-# 由于 finder 是按顺序读取的,所以必须插入在首位
-sys.meta_path.insert(0, MyFinder)
-```
-
-查找器可以分为两种:
-
-```shell
-object
- +-- Finder (deprecated)
- +-- MetaPathFinder
- +-- PathEntryFinder
-```
-
-这里需要注意的是,在 3.4 版前,查找器会直接返回 加载器(Loader)对象,而在 3.4 版后,查找器则会返回模块规格说明(ModuleSpec),其中 包含加载器。
-
-而关于什么是 加载器 和 模块规格说明, 请继续往后看。
-
-### 4.2 加载器是什么?
-
-查找器只负责查找定位找模,而真正负责加载模块的,是加载器(loader)。
-
-一般的 loader 必须定义名为 ` load_module() ` 的方法。
-
-为什么这里说一般,因为 loader 还分多种:
-
-```shell
-object
- +-- Finder (deprecated)
- | +-- MetaPathFinder
- | +-- PathEntryFinder
- +-- Loader
- +-- ResourceLoader --------+
- +-- InspectLoader |
- +-- ExecutionLoader --+
- +-- FileLoader
- +-- SourceLoader
-```
-
-通过查看源码可知,不同的加载器的抽象方法各有不同。
-
-
-
-加载器通常由一个 finder 返回。详情参见 PEP 302,对于 abstract base class 可参见 importlib.abc.Loader。
-
-那如何自定义我们自己的加载器呢?
-
-你只要
-
-- 定义一个实现了 load_module 方法的类
-- 对与导入有关的属性([点击查看详情](https://docs.python.org/zh-cn/3/reference/import.html#import-related-module-attributes))进行校验
-- 创建模块对象并绑定所有与导入相关的属性变量到该模块上
-- 将此模块保存到 sys.modules 中(顺序很重要,避免递归导入)
-- 然后加载模块(这是核心)
-- 若加载出错,需要能够处理抛出异常( ImportError)
-- 若加载成功,则返回 module 对象
-
-若你想看具体的例子,可以接着往后看。
-
-### 4.3 模块规格说明
-
-导入机制在导入期间会使用有关每个模块的多种信息,特别是加载之前。 大多数信息都是所有模块通用的。 模块规格说明的目的是基于每个模块来封装这些导入相关信息。
-
-模块的规格说明会作为模块对象的 `__spec__` 属性对外公开。 有关模块规格的详细内容请参阅 [`ModuleSpec`](https://docs.python.org/zh-cn/3/library/importlib.html#importlib.machinery.ModuleSpec)。
-
-在 Python 3.4 后,查找器不再返回加载器,而是返回 ModuleSpec 对象,它储存着更多的信息
-
-- 模块名
-- 加载器
-- 模块绝对路径
-
-那如何查看一个模块的 ModuleSpec ?
-
-这边举个例子
-
-```shell
-$ cat my_mod02.py
-import my_mod01
-print(my_mod01.__spec__)
-
-$ python3 my_mod02.py
-in mod01
-ModuleSpec(name='my_mod01', loader=<_frozen_importlib_external.SourceFileLoader object at 0x000000000392DBE0>, origin='/home/MING/my_mod01.py')
-```
-
-从 ModuleSpec 中可以看到,加载器是包含在内的,那我们如果要重新加载一个模块,是不是又有了另一种思路了?
-
-来一起验证一下。
-
-现在有两个文件:
+## 第三种
-一个是 my_info.py
+语法
```python
-# my_info.py
-name='wangbm'
+(, )[condition]
```
- 另一个是:main.py
+例子
```python
-# main.py
-import my_info
-
-print(my_info.name)
-
-# 加一个断点
-import pdb;pdb.set_trace()
-
-# 再加载一次
-my_info.__spec__.loader.load_module()
-
-print(my_info.name)
-```
-
-在 `main.py` 处,我加了一个断点,目的是当运行到断点处时,我修改 my_info.py 里的 name 为 `ming` ,以便验证重载是否有效?
-
-```shell
-$ python3 main.py
-wangbm
-> /home/MING/main.py(9)()
--> my_info.__spec__.loader.load_module()
-(Pdb) c
-ming
+>>> msg1 = ("未成年", "已成年")[age1 > 18]
+>>> print(msg1)
+已成年
+>>>
+>>>
+>>> msg2 = ("未成年", "已成年")[age2 > 18]
+>>> print(msg2)
+未成年
```
-从结果来看,重载是有效的。
-
-
-
-### 4.4 导入器是什么?
-
-导入器(importer),也许你在其他文章里会见到它,但其实它并不是个新鲜的东西。
-
-它只是同时实现了查找器和加载器两种接口的对象,所以你可以说导入器(importer)是查找器(finder),也可以说它是加载器(loader)。
+## 第四种
-
-
-## 5. 远程导入模块
-
-由于 Python 默认的 查找器和加载器 仅支持本地的模块的导入,并不支持实现远程模块的导入。
-
-为了让你更好的理解 Python Import Hook 机制,我下面会通过实例演示,如何自己实现远程导入模块的导入器。
-
-### 5.1 动手实现导入器
-
-当导入一个包的时候,Python 解释器首先会从 sys.meta_path 中拿到查找器列表。
-
-默认顺序是:内建模块查找器 -> 冻结模块查找器 -> 第三方模块路径(本地的 sys.path)查找器
-
-若经过这三个查找器,仍然无法查找到所需的模块,则会抛出ImportError异常。
-
-
-
-因此要实现远程导入模块,有两种思路。
-
-- 一种是实现自己的元路径导入器;
-- 另一种是编写一个钩子,添加到sys.path_hooks里,识别特定的目录命名模式。
-
-
-
-我这里选择第一种方法来做为示例。
-
-实现导入器,我们需要分别查找器和加载器。
-
-**首先是查找器**
-
-由源码得知,路径查找器分为两种
-
-- MetaPathFinder
-- PathEntryFinder
-
-这里使用 MetaPathFinder 来进行查找器的编写。
-
-在 Python 3.4 版本之前,查找器必须实现 `find_module()` 方法,而 Python 3.4+ 版,则推荐使用 `find_spec()` 方法,但这并不意味着你不能使用 `find_module()`,但是在没有 `find_spec()` 方法时,导入协议还是会尝试 `find_module()` 方法。
-
-我先举例下使用 `find_module()` 该如何写。
+语法
```python
-from importlib import abc
-
-class UrlMetaFinder(abc.MetaPathFinder):
- def __init__(self, baseurl):
- self._baseurl = baseurl
-
- def find_module(self, fullname, path=None):
- if path is None:
- baseurl = self._baseurl
- else:
- # 不是原定义的url就直接返回不存在
- if not path.startswith(self._baseurl):
- return None
- baseurl = path
-
- try:
- loader = UrlMetaLoader(baseurl)
- loader.load_module(fullname)
- return loader
- except Exception:
- return None
+(lambda: , lambda:)[]()
```
-若使用 `find_spec()` ,要注意此方法的调用需要带有两到三个参数。
-
-第一个是被导入模块的完整限定名称,例如 `foo.bar.baz`。 第二个参数是供模块搜索使用的路径条目。 对于最高层级模块,第二个参数为 `None`,但对于子模块或子包,第二个参数为父包 `__path__` 属性的值。 如果相应的 `__path__` 属性无法访问,将引发 [`ModuleNotFoundError`](https://docs.python.org/zh-cn/3/library/exceptions.html#ModuleNotFoundError)。 第三个参数是一个将被作为稍后加载目标的现有模块对象。 导入系统仅会在重加载期间传入一个目标模块。
+例子
```python
-from importlib import abc
-from importlib.machinery import ModuleSpec
-
-class UrlMetaFinder(abc.MetaPathFinder):
- def __init__(self, baseurl):
- self._baseurl = baseurl
- def find_spec(self, fullname, path=None, target=None):
- if path is None:
- baseurl = self._baseurl
- else:
- # 不是原定义的url就直接返回不存在
- if not path.startswith(self._baseurl):
- return None
- baseurl = path
-
- try:
- loader = UrlMetaLoader(baseurl)
- return ModuleSpec(fullname, loader, is_package=loader.is_package(fullname))
- except Exception:
- return None
+>>> msg1 = (lambda:"未成年", lambda:"已成年")[age1 > 18]()
+>>> print(msg1)
+已成年
+>>>
+>>> msg2 = (lambda:"未成年", lambda:"已成年")[age2 > 18]()
+>>> print(msg2)
+未成年
```
+## 第五种
-
-**接下来是加载器**
-
-由源码得知,路径查找器分为三种
-
-- FileLoader
-- SourceLoader
-
-按理说,两种加载器都可以实现我们想要的功能,我这里选用 SourceLoader 来示范。
-
-在 SourceLoader 这个抽象类里,有几个很重要的方法,在你写实现加载器的时候需要注意
-
-- get_code:获取源代码,可以根据自己场景实现实现。
-- exec_module:执行源代码,并将变量赋值给 `module.__dict__`
-- get_data:抽象方法,必须实现,返回指定路径的字节码。
-- get_filename:抽象方法,必须实现,返回文件名
-
-在一些老的博客文章中,你会经常看到 加载器 要实现 `load_module()` ,而这个方法早已在 Python 3.4 的时候就被废弃了,当然为了兼容考虑,你若使用 `load_module()` 也是可以的。
+语法:
```python
-from importlib import abc
-
-class UrlMetaLoader(abc.SourceLoader):
- def __init__(self, baseurl):
- self.baseurl = baseurl
-
- def get_code(self, fullname):
- f = urllib2.urlopen(self.get_filename(fullname))
- return f.read()
-
- def load_module(self, fullname):
- code = self.get_code(fullname)
- mod = sys.modules.setdefault(fullname, imp.new_module(fullname))
- mod.__file__ = self.get_filename(fullname)
- mod.__loader__ = self
- mod.__package__ = fullname
- exec(code, mod.__dict__)
- return None
-
- def get_data(self):
- pass
-
- def execute_module(self, module):
- pass
-
- def get_filename(self, fullname):
- return self.baseurl + fullname + '.py'
+{True: , False: }[]
```
-当你使用这种旧模式实现自己的加载时,你需要注意两点,很重要:
-
-- execute_module 必须重载,而且不应该有任何逻辑,即使它并不是抽象方法。
-- load_module,需要你在查找器里手动执行,才能实现模块的加载。。
-
-做为替换,你应该使用 `execute_module()` 和 `create_module()` 。由于基类里已经实现了 `execute_module` 和 `create_module()`,并且满足我们的使用场景。我这边可以不用重复实现。和旧模式相比,这里也不需要在设查找器里手动执行 `execute_module()`。
+例子:
```python
-import urllib.request as urllib2
-
-class UrlMetaLoader(importlib.abc.SourceLoader):
- def __init__(self, baseurl):
- self.baseurl = baseurl
-
- def get_code(self, fullname):
- f = urllib2.urlopen(self.get_filename(fullname))
- return f.read()
-
- def get_data(self):
- pass
-
- def get_filename(self, fullname):
- return self.baseurl + fullname + '.py'
+>>> msg1 = {True: "已成年", False: "未成年"}[age1 > 18]
+>>> print(msg1)
+已成年
+>>>
+>>> msg2 = {True: "已成年", False: "未成年"}[age2 > 18]
+>>> print(msg2)
+未成年
```
-查找器和加载器都有了,别忘了往sys.meta_path 注册我们自定义的查找器(UrlMetaFinder)。
+## 第六种
-```python
-def install_meta(address):
- finder = UrlMetaFinder(address)
- sys.meta_path.append(finder)
-```
-
-所有的代码都解析完毕后,我们将其整理在一个模块(my_importer.py)中
+语法
```python
-# my_importer.py
-import sys
-import importlib
-import urllib.request as urllib2
-
-class UrlMetaFinder(importlib.abc.MetaPathFinder):
- def __init__(self, baseurl):
- self._baseurl = baseurl
-
-
- def find_module(self, fullname, path=None):
- if path is None:
- baseurl = self._baseurl
- else:
- # 不是原定义的url就直接返回不存在
- if not path.startswith(self._baseurl):
- return None
- baseurl = path
-
- try:
- loader = UrlMetaLoader(baseurl)
- return loader
- except Exception:
- return None
-
-class UrlMetaLoader(importlib.abc.SourceLoader):
- def __init__(self, baseurl):
- self.baseurl = baseurl
-
- def get_code(self, fullname):
- f = urllib2.urlopen(self.get_filename(fullname))
- return f.read()
-
- def get_data(self):
- pass
-
- def get_filename(self, fullname):
- return self.baseurl + fullname + '.py'
-
-def install_meta(address):
- finder = UrlMetaFinder(address)
- sys.meta_path.append(finder)
-```
-
-### 5.2 搭建远程服务端
-
-最开始我说了,要实现一个远程导入模块的方法。
-
-我还缺一个在远端的服务器,来存放我的模块,为了方便,我使用python自带的 `http.server` 模块用一条命令即可实现。
-
-```shell
-$ mkdir httpserver && cd httpserver
-$ cat>my_info.py) and (,) or (,))[0]
```
-一切准备好,我们就可以验证了。
+例子
```python
->>> from my_importer import install_meta
->>> install_meta('http://localhost:12800/') # 往 sys.meta_path 注册 finder
->>> import my_info # 打印ok,说明导入成功
-ok
->>> my_info.name # 验证可以取得到变量
-'wangbm'
+>>> msg1 = ((age1 > 18) and ("已成年",) or ("未成年",))[0]
+>>> print(msg1)
+已成年
+>>>
+>>> msg2 = ((age2 > 18) and ("已成年",) or ("未成年",))[0]
+>>> print(msg2)
+未成年
```
-至此,我实现了一个简易的可以导入远程服务器上的模块的导入器。
-
-
-
-## 参考文档
-
-- https://docs.python.org/zh-cn/3/reference/import.html
-- https://docs.python.org/zh-cn/3/library/importlib.html#module-importlib.abc
-- https://python3-cookbook.readthedocs.io/zh_CN/latest/c10/p11_load_modules_from_remote_machine_by_hooks.html
-
+以上代码,都比较简单,仔细看都能看懂,我就不做解释了。
+看到这里,有没有涨姿势了,学了这么久的 Python ,这么多骚操作,还真是活久见。。这六种写法里,我最推荐使用的是第一种,自己也经常在用,简洁直白,代码行还少。而其他的写法虽然能写,但是不会用,也不希望在我余生里碰到会在公共代码里用这些写法的同事。
-
+
\ No newline at end of file
diff --git a/source/c01/c01_24.rst b/source/c01/c01_24.rst
index eca008a..4fa41c6 100644
--- a/source/c01/c01_24.rst
+++ b/source/c01/c01_24.rst
@@ -1,802 +1,165 @@
-1.24 深入探讨 Python 的 import 机制:实现远程导入模块
-=====================================================
-
-所谓的模块导入( ``import``
-),是指在一个模块中使用另一个模块的代码的操作,它有利于代码的复用。
-
-在 Python 中使用 import 关键字来实现这个操作,但不是唯一的方法,还有
-``importlib.import_module()`` 和 ``__import__()`` 等。
-
-也许你看到这个标题,会说我怎么会发这么基础的文章?
-
-与此相反。恰恰我觉得这篇文章的内容可以算是 Python
-的进阶技能,会深入地探讨并以真实案例讲解 Python import Hook 的知识点。
-
-当然为了使文章更系统、全面,前面会有小篇幅讲解基础知识点,但请你有耐心的往后读下去,因为后面才是本篇文章的精华所在,希望你不要错过。
+1.24 Python 炫技操作:条件语句的七种写法
+========================================
|image0|
-1. 导入系统的基础
------------------
-
-1.1 导入单元构成
-~~~~~~~~~~~~~~~~
-
-导入单元有多种,可以是模块、包及变量等。
-
-对于这些基础的概念,对于新手还是有必要介绍一下它们的区别。
-
-**模块**\ :类似 \*.py,*.pyc, \*.pyd ,*.so,*.dll 这样的文件,是
-Python 代码载体的最小单元。
-
-**包** 还可以细分为两种:
-
-- Regular packages:是一个带有 ``__init__.py``
- 文件的文件夹,此文件夹下可包含其他子包,或者模块
-- Namespace packages
-
-关于 Namespace
-packages,有的人会比较陌生,我这里摘抄官方文档的一段说明来解释一下。
-
-Namespace packages 是由多个 部分 构成的,每个部分为父包增加一个子包。
-各个部分可能处于文件系统的不同位置。 部分也可能处于 zip
-文件中、网络上,或者 Python 在导入期间可以搜索的其他地方。
-命名空间包并不一定会直接对应到文件系统中的对象;它们有可能是无实体表示的虚拟模块。
-
-命名空间包的 ``__path__`` 属性不使用普通的列表。
-而是使用定制的可迭代类型,如果其父包的路径 (或者最高层级包的 sys.path)
-发生改变,这种对象会在该包内的下一次导入尝试时自动执行新的对包部分的搜索。
+原代码
+------
-命名空间包没有 ``parent/__init__.py`` 文件。
-实际上,在导入搜索期间可能找到多个 parent
-目录,每个都由不同的部分所提供。 因此 parent/one 的物理位置不一定与
-parent/two 相邻。 在这种情况下,Python 将为顶级的 parent
-包创建一个命名空间包,无论是它本身还是它的某个子包被导入。
-
-1.2 相对/绝对导入
-~~~~~~~~~~~~~~~~~
-
-当我们 import 导入模块或包时,Python 提供两种导入方式:
-
-- 相对导入(relative import ):from . import B 或 from ..A import
- B,其中.表示当前模块,..表示上层模块
-- 绝对导入(absolute import):import foo.bar 或者 from foo import bar
-
-你可以根据实际需要进行选择,但有必要说明的是,在早期的版本( Python2.6
-之前),Python 默认使用的相对导入。而后来的版本中( Python2.6
-之后),都以绝对导入为默认使用的导入方式。
-
-使用绝对路径和相对路径各有利弊:
-
-- 当你在开发维护自己的项目时,应当使用相对路径导入,这样可以避免硬编码带来的麻烦。
-- 而使用绝对路径,会让你模块导入结构更加清晰,而且也避免了重名的包冲突而导入错误。
-
-1.3 导入的标准写法
-~~~~~~~~~~~~~~~~~~
-
-在 PEP8 中对模块的导入提出了要求,遵守
-PEP8规范能让你的代码更具有可读性,我这边也列一下:
-
-- import 语句应当分行书写
+这是一段非常简单的通过年龄判断一个人是否成年的代码,由于代码行数过多,有些人就不太愿意这样写,因为这体现不出自己多年的
+Python 功力。
.. code:: python
- # bad
- import os,sys
-
- # good
- import os
- import sys
-
-- import语句应当使用absolute import
-
-.. code:: python
+ if age > 18:
+ return "已成年"
+ else:
+ return "未成年"
- # bad
- from ..bar import Bar
+下面我列举了六种这段代码的变异写法,一个比一个还 6
+,单独拿出来比较好理解,放在工程代码里,没用过这些学法的人,一定会看得一脸懵逼,理解了之后,又不经意大呼:\ **卧槽,还可以这样写?**\ ,而后就要开始骂街了:\ **这是给人看的代码?**
+(除了第一种之外)
- # good
- from foo.bar import test
+第一种
+------
-- import语句应当放在文件头部,置于模块说明及docstring之后,全局变量之前
-
-- import语句应该按照顺序排列,每组之间用一个空格分隔,按照内置模块,第三方模块,自己所写的模块调用顺序,同时每组内部按照字母表顺序排列
-
-.. code:: python
-
- # 内置模块
- import os
- import sys
-
- # 第三方模块
- import flask
-
- # 本地模块
- from foo import bar
-
-1.4 几个有用的 sys 变量
-~~~~~~~~~~~~~~~~~~~~~~~
-
-``sys.path`` 可以列出 Python 模块查找的目录列表
-
-.. code:: python
-
- >>> import sys
- >>> from pprint import pprint
- >>> pprint(sys.path)
- ['',
- '/Library/Frameworks/Python.framework/Versions/3.6/lib/python36.zip',
- '/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6',
- '/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/lib-dynload',
- '/Users/MING/Library/Python/3.6/lib/python/site-packages',
- '/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages']
- >>>
-
-``sys.meta_path`` 存放的是所有的查找器。
-
-.. code:: python
-
- >>> import sys
- >>> from pprint import pprint
- >>> pprint(sys.meta_path)
- [,
- ,
- ]
-
-``sys.path_importer_cache`` 比 ``sys.path`` 会更大点,
-因为它会为所有被加载代码的目录记录它们的查找器。
-这包括包的子目录,这些通常在 ``sys.path`` 中是不存在的。
+语法:
.. code:: python
- >>> import sys
- >>> from pprint import pprint
- >>> pprint(sys.path_importer_cache)
- {'/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6': FileFinder('/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6'),
- '/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/collections': FileFinder('/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/collections'),
- '/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/encodings': FileFinder('/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/encodings'),
- '/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/lib-dynload': FileFinder('/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/lib-dynload'),
- '/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages': FileFinder('/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages'),
- '/Library/Frameworks/Python.framework/Versions/3.6/lib/python36.zip': None,
- '/Users/MING': FileFinder('/Users/MING'),
- '/Users/MING/Library/Python/3.6/lib/python/site-packages': FileFinder('/Users/MING/Library/Python/3.6/lib/python/site-packages')}
-
-2. \__import_\_ 的妙用
-----------------------
-
-import 关键字的使用,可以说是基础中的基础。
-
-但这不是模块唯一的方法,还有 ``importlib.import_module()`` 和
-``__import__()`` 等。
-
-和 import 不同的是,\ ``__import__``
-是一个函数,也正是因为这个原因,使得 ``__import__``
-的使用会更加灵活,常常用于框架中,对于插件的动态加载。
+ if else
-实际上,当我们调用 import 导入模块时,其内部也是调用了 ``__import__``
-,请看如下两种导入方法,他们是等价的。
+例子
.. code:: python
- # 使用 import
- import os
-
- # 使用 __import__
- os = __import__('os')
-
-通过举一反三,下面两种方法同样也是等价的。
-
-.. code:: python
-
- # 使用 import .. as ..
- import pandas as pd
-
- # 使用 __import__
- pd = __import__('pandas')
-
-上面我说 ``__import__`` 常常用于插件的动态,事实上也只有它能做到(相对于
-import 来说)。
-
-``插件``\ 通常会位于某一特定的文件夹下,在使用过程中,可能你并不会用到全部的插件,也可能你会新增插件。
-
-如果使用 import
-关键字这种硬编码的方式,显然太不优雅了,当你要新增/修改插件的时候,都需要你修改代码。更合适的做法是,将这些插件以配置的方式,写在配置文件中,然后由代码去读取你的配置,动态导入你要使用的插件,即灵活又方便,也不容易出错。
-
-假如我的一个项目中,有 ``plugin01`` 、\ ``plugin02``\ 、\ ``plugin03``
-、\ ``plugin04`` 四个插件,这些插件下都会实现一个核心方法 ``run()``
-。但有时候我不想使用全部的插件,只想使用 ``plugin02``\ 、\ ``plugin04``
-,那我就在配置文件中写我要使用的两个插件。
-
-.. code:: shell
+ >>> age1 = 20
+ >>> age2 = 17
+ >>>
+ >>>
+ >>> msg1 = "已成年" if age1 > 18 else "未成年"
+ >>> print msg1
+ 已成年
+ >>>
+ >>> msg2 = "已成年" if age2 > 18 else "未成年"
+ >>> print msg2
+ 未成年
+ >>>
- # my.conf
- custom_plugins=['plugin02', 'plugin04']
+第二种
+------
-那我如何使用动态加载,并运行他们呢?
+语法
.. code:: python
- # main.py
-
- for plugin in conf.custom_plugins:
- __import__(plugin)
- sys.modules[plugin].run()
-
-3. 理解模块的缓存
------------------
-
-在一个模块内部重复引用另一个相同模块,实际并不会导入两次,原因是在使用关键字
-``import`` 导入模块时,它会先检索 ``sys.modules``
-里是否已经载入这个模块了,如果已经载入,则不会再次导入,如果不存在,才会去检索导入这个模块。
-
-来实验一下,在 ``my_mod02`` 这个模块里,我 import 两次 ``my_mod01``
-这个模块,按逻辑每一次 import 会一次 ``my_mod01`` 里的代码(即打印
-``in mod01``\ ),但是验证结果是,只打印了一次。
+ and or
-.. code:: shell
-
- $ cat my_mod01.py
- print('in mod01')
-
- $ cat my_mod02.py
- import my_mod01
- import my_mod01
-
- $ python my_mod02.py
- in mod01
-
-该现象的解释是:因为有 ``sys.modules`` 的存在。
-
-``sys.modules``
-是一个字典(key:模块名,value:模块对象),它存放着在当前 namespace
-所有已经导入的模块对象。
+例子
.. code:: python
- # test_module.py
-
- import sys
- print(sys.modules.get('json', 'NotFound'))
-
- import json
- print(sys.modules.get('json', 'NotFound'))
-
-运行结果如下,可见在 导入后 json 模块后,\ ``sys.modules`` 才有了 json
-模块的对象。
-
-.. code:: shell
-
- $ python test_module.py
- NotFound
-
-
-由于有缓存的存在,使得我们无法重新载入一个模块。
+ >>> msg1 = age1 > 18 and "已成年" or "未成年"
+ >>> msg2 = "已成年" if age2 > 18 else "未成年"
+ >>>
+ >>> print(msg1)
+ 已成年
+ >>>
+ >>> print(msg2)
+ 未成年
-但若你想反其道行之,可以借助 importlib
-这个神奇的库来实现。事实也确实有此场景,比如在代码调试中,在发现代码有异常并修改后,我们通常要重启服务再次载入程序。这时候,若有了模块重载,就无比方便了,修改完代码后也无需服务的重启,就能继续调试。
+第三种
+------
-还是以上面的例子来理解,\ ``my_mod02.py`` 改写成如下
+语法
.. code:: python
- # my_mod02.py
+ (, )[condition]
- import importlib
- import my_mod01
- importlib.reload(my_mod01)
-
-使用 python3 来执行这个模块,与上面不同的是,这边执行了两次
-``my_mod01.py``
-
-.. code:: shell
-
- $ python3 my_mod02.py
- in mod01
- in mod01
-
-4. 查找器与加载器
------------------
-
-如果指定名称的模块在 ``sys.modules`` 找不到,则将发起调用 Python
-的导入协议以查找和加载该模块。
-
-此协议由两个概念性模块构成,即 ``查找器`` 和 ``加载器``\ 。
-
-一个 Python 的模块的导入,其实可以再细分为两个过程:
-
-1. 由查找器实现的模块查找
-2. 由加载器实现的模块加载
-
-4.1 查找器是什么?
-~~~~~~~~~~~~~~~~~~
-
-查找器(finder),简单点说,查找器定义了一个模块查找机制,让程序知道该如何找到对应的模块。
-
-其实 Python 内置了多个默认查找器,其存在于 sys.meta_path 中。
-
-但这些查找器对应使用者来说,并不是那么重要,因此在 Python 3.3 之前,
-Python 解释将其隐藏了,我们称之为隐式查找器。
+例子
.. code:: python
- # Python 2.7
- >>> import sys
- >>> sys.meta_path
- []
+ >>> msg1 = ("未成年", "已成年")[age1 > 18]
+ >>> print(msg1)
+ 已成年
>>>
+ >>>
+ >>> msg2 = ("未成年", "已成年")[age2 > 18]
+ >>> print(msg2)
+ 未成年
-由于这点不利于开发者深入理解 import 机制,在 Python 3.3
-后,所有的模块导入机制都会通过 sys.meta_path
-暴露,不会在有任何隐式导入机制。
-
-.. code:: python
-
- # Python 3.6
- >>> import sys
- >>> from pprint import pprint
- >>> pprint(sys.meta_path)
- [,
- ,
- ]
-
-观察一下 Python 默认的这几种查找器 (finder),可以分为三种:
-
-- 一种知道如何导入内置模块
-- 一种知道如何导入冻结模块
-- 一种知道如何导入来自 `import
- path `__
- 的模块 (即 `path based
- finder `__)。
-
-那我们能不能自已定义一个查找器呢?当然可以,你只要
-
-- 定义一个实现了 find_module 方法的类(py2和py3均可),或者实现
- find_loader 类方法(仅 py3 有效),如果找到模块需要返回一个 loader
- 对象或者 ModuleSpec 对象(后面会讲),没找到需要返回 None
-- 定义完后,要使用这个查找器,必须注册它,将其插入在 sys.meta_path
- 的首位,这样就能优先使用。
-
-.. code:: python
-
- import sys
-
- class MyFinder(object):
- @classmethod
- def find_module(cls, name, path, target=None):
- print("Importing", name, path, target)
- # 将在后面定义
- return MyLoader()
-
- # 由于 finder 是按顺序读取的,所以必须插入在首位
- sys.meta_path.insert(0, MyFinder)
-
-查找器可以分为两种:
-
-.. code:: shell
-
- object
- +-- Finder (deprecated)
- +-- MetaPathFinder
- +-- PathEntryFinder
-
-这里需要注意的是,在 3.4 版前,查找器会直接返回
-加载器(Loader)对象,而在 3.4
-版后,查找器则会返回模块规格说明(ModuleSpec),其中 包含加载器。
-
-而关于什么是 加载器 和 模块规格说明, 请继续往后看。
-
-4.2 加载器是什么?
-~~~~~~~~~~~~~~~~~~
-
-查找器只负责查找定位找模,而真正负责加载模块的,是加载器(loader)。
-
-一般的 loader 必须定义名为 ``load_module()`` 的方法。
-
-为什么这里说一般,因为 loader 还分多种:
-
-.. code:: shell
-
- object
- +-- Finder (deprecated)
- | +-- MetaPathFinder
- | +-- PathEntryFinder
- +-- Loader
- +-- ResourceLoader --------+
- +-- InspectLoader |
- +-- ExecutionLoader --+
- +-- FileLoader
- +-- SourceLoader
-
-通过查看源码可知,不同的加载器的抽象方法各有不同。
-
-加载器通常由一个 finder 返回。详情参见 PEP 302,对于 abstract base class
-可参见 importlib.abc.Loader。
-
-那如何自定义我们自己的加载器呢?
-
-你只要
-
-- 定义一个实现了 load_module 方法的类
-- 对与导入有关的属性(\ `点击查看详情 `__\ )进行校验
-- 创建模块对象并绑定所有与导入相关的属性变量到该模块上
-- 将此模块保存到 sys.modules 中(顺序很重要,避免递归导入)
-- 然后加载模块(这是核心)
-- 若加载出错,需要能够处理抛出异常( ImportError)
-- 若加载成功,则返回 module 对象
-
-若你想看具体的例子,可以接着往后看。
-
-4.3 模块规格说明
-~~~~~~~~~~~~~~~~
-
-导入机制在导入期间会使用有关每个模块的多种信息,特别是加载之前。
-大多数信息都是所有模块通用的。
-模块规格说明的目的是基于每个模块来封装这些导入相关信息。
-
-模块的规格说明会作为模块对象的 ``__spec__`` 属性对外公开。
-有关模块规格的详细内容请参阅
-```ModuleSpec`` `__\ 。
-
-在 Python 3.4 后,查找器不再返回加载器,而是返回 ModuleSpec
-对象,它储存着更多的信息
-
-- 模块名
-- 加载器
-- 模块绝对路径
-
-那如何查看一个模块的 ModuleSpec ?
-
-这边举个例子
-
-.. code:: shell
-
- $ cat my_mod02.py
- import my_mod01
- print(my_mod01.__spec__)
-
- $ python3 my_mod02.py
- in mod01
- ModuleSpec(name='my_mod01', loader=<_frozen_importlib_external.SourceFileLoader object at 0x000000000392DBE0>, origin='/home/MING/my_mod01.py')
-
-从 ModuleSpec
-中可以看到,加载器是包含在内的,那我们如果要重新加载一个模块,是不是又有了另一种思路了?
-
-来一起验证一下。
-
-现在有两个文件:
-
-一个是 my_info.py
-
-.. code:: python
-
- # my_info.py
- name='wangbm'
+第四种
+------
-另一个是:main.py
+语法
.. code:: python
- # main.py
- import my_info
-
- print(my_info.name)
-
- # 加一个断点
- import pdb;pdb.set_trace()
-
- # 再加载一次
- my_info.__spec__.loader.load_module()
-
- print(my_info.name)
-
-在 ``main.py`` 处,我加了一个断点,目的是当运行到断点处时,我修改
-my_info.py 里的 name 为 ``ming`` ,以便验证重载是否有效?
-
-.. code:: shell
-
- $ python3 main.py
- wangbm
- > /home/MING/main.py(9)()
- -> my_info.__spec__.loader.load_module()
- (Pdb) c
- ming
-
-从结果来看,重载是有效的。
-
-4.4 导入器是什么?
-~~~~~~~~~~~~~~~~~~
-
-导入器(importer),也许你在其他文章里会见到它,但其实它并不是个新鲜的东西。
-
-它只是同时实现了查找器和加载器两种接口的对象,所以你可以说导入器(importer)是查找器(finder),也可以说它是加载器(loader)。
-
-5. 远程导入模块
----------------
-
-由于 Python 默认的 查找器和加载器
-仅支持本地的模块的导入,并不支持实现远程模块的导入。
-
-为了让你更好的理解 Python Import Hook
-机制,我下面会通过实例演示,如何自己实现远程导入模块的导入器。
-
-5.1 动手实现导入器
-~~~~~~~~~~~~~~~~~~
-
-当导入一个包的时候,Python 解释器首先会从 sys.meta_path
-中拿到查找器列表。
-
-默认顺序是:内建模块查找器 -> 冻结模块查找器 -> 第三方模块路径(本地的
-sys.path)查找器
-
-若经过这三个查找器,仍然无法查找到所需的模块,则会抛出ImportError异常。
-
-因此要实现远程导入模块,有两种思路。
+ (lambda: , lambda:)[]()
-- 一种是实现自己的元路径导入器;
-- 另一种是编写一个钩子,添加到sys.path_hooks里,识别特定的目录命名模式。
-
-我这里选择第一种方法来做为示例。
-
-实现导入器,我们需要分别查找器和加载器。
-
-**首先是查找器**
-
-由源码得知,路径查找器分为两种
-
-- MetaPathFinder
-- PathEntryFinder
-
-这里使用 MetaPathFinder 来进行查找器的编写。
-
-在 Python 3.4 版本之前,查找器必须实现 ``find_module()`` 方法,而 Python
-3.4+ 版,则推荐使用 ``find_spec()`` 方法,但这并不意味着你不能使用
-``find_module()``\ ,但是在没有 ``find_spec()``
-方法时,导入协议还是会尝试 ``find_module()`` 方法。
-
-我先举例下使用 ``find_module()`` 该如何写。
-
-.. code:: python
-
- from importlib import abc
-
- class UrlMetaFinder(abc.MetaPathFinder):
- def __init__(self, baseurl):
- self._baseurl = baseurl
-
- def find_module(self, fullname, path=None):
- if path is None:
- baseurl = self._baseurl
- else:
- # 不是原定义的url就直接返回不存在
- if not path.startswith(self._baseurl):
- return None
- baseurl = path
-
- try:
- loader = UrlMetaLoader(baseurl)
- loader.load_module(fullname)
- return loader
- except Exception:
- return None
-
-若使用 ``find_spec()`` ,要注意此方法的调用需要带有两到三个参数。
-
-第一个是被导入模块的完整限定名称,例如 ``foo.bar.baz``\ 。
-第二个参数是供模块搜索使用的路径条目。 对于最高层级模块,第二个参数为
-``None``\ ,但对于子模块或子包,第二个参数为父包 ``__path__`` 属性的值。
-如果相应的 ``__path__`` 属性无法访问,将引发
-```ModuleNotFoundError`` `__\ 。
-第三个参数是一个将被作为稍后加载目标的现有模块对象。
-导入系统仅会在重加载期间传入一个目标模块。
+例子
.. code:: python
- from importlib import abc
- from importlib.machinery import ModuleSpec
-
- class UrlMetaFinder(abc.MetaPathFinder):
- def __init__(self, baseurl):
- self._baseurl = baseurl
- def find_spec(self, fullname, path=None, target=None):
- if path is None:
- baseurl = self._baseurl
- else:
- # 不是原定义的url就直接返回不存在
- if not path.startswith(self._baseurl):
- return None
- baseurl = path
-
- try:
- loader = UrlMetaLoader(baseurl)
- return ModuleSpec(fullname, loader, is_package=loader.is_package(fullname))
- except Exception:
- return None
-
-**接下来是加载器**
-
-由源码得知,路径查找器分为三种
-
-- FileLoader
-- SourceLoader
-
-按理说,两种加载器都可以实现我们想要的功能,我这里选用 SourceLoader
-来示范。
-
-在 SourceLoader
-这个抽象类里,有几个很重要的方法,在你写实现加载器的时候需要注意
+ >>> msg1 = (lambda:"未成年", lambda:"已成年")[age1 > 18]()
+ >>> print(msg1)
+ 已成年
+ >>>
+ >>> msg2 = (lambda:"未成年", lambda:"已成年")[age2 > 18]()
+ >>> print(msg2)
+ 未成年
-- get_code:获取源代码,可以根据自己场景实现实现。
-- exec_module:执行源代码,并将变量赋值给 ``module.__dict__``
-- get_data:抽象方法,必须实现,返回指定路径的字节码。
-- get_filename:抽象方法,必须实现,返回文件名
+第五种
+------
-在一些老的博客文章中,你会经常看到 加载器 要实现 ``load_module()``
-,而这个方法早已在 Python 3.4
-的时候就被废弃了,当然为了兼容考虑,你若使用 ``load_module()``
-也是可以的。
+语法:
.. code:: python
- from importlib import abc
-
- class UrlMetaLoader(abc.SourceLoader):
- def __init__(self, baseurl):
- self.baseurl = baseurl
-
- def get_code(self, fullname):
- f = urllib2.urlopen(self.get_filename(fullname))
- return f.read()
-
- def load_module(self, fullname):
- code = self.get_code(fullname)
- mod = sys.modules.setdefault(fullname, imp.new_module(fullname))
- mod.__file__ = self.get_filename(fullname)
- mod.__loader__ = self
- mod.__package__ = fullname
- exec(code, mod.__dict__)
- return None
-
- def get_data(self):
- pass
-
- def execute_module(self, module):
- pass
-
- def get_filename(self, fullname):
- return self.baseurl + fullname + '.py'
-
-当你使用这种旧模式实现自己的加载时,你需要注意两点,很重要:
+ {True: , False: }[]
-- execute_module 必须重载,而且不应该有任何逻辑,即使它并不是抽象方法。
-- load_module,需要你在查找器里手动执行,才能实现模块的加载。。
-
-做为替换,你应该使用 ``execute_module()`` 和 ``create_module()``
-。由于基类里已经实现了 ``execute_module`` 和
-``create_module()``\ ,并且满足我们的使用场景。我这边可以不用重复实现。和旧模式相比,这里也不需要在设查找器里手动执行
-``execute_module()``\ 。
+例子:
.. code:: python
- import urllib.request as urllib2
-
- class UrlMetaLoader(importlib.abc.SourceLoader):
- def __init__(self, baseurl):
- self.baseurl = baseurl
-
- def get_code(self, fullname):
- f = urllib2.urlopen(self.get_filename(fullname))
- return f.read()
-
- def get_data(self):
- pass
-
- def get_filename(self, fullname):
- return self.baseurl + fullname + '.py'
-
-查找器和加载器都有了,别忘了往sys.meta_path
-注册我们自定义的查找器(UrlMetaFinder)。
-
-.. code:: python
+ >>> msg1 = {True: "已成年", False: "未成年"}[age1 > 18]
+ >>> print(msg1)
+ 已成年
+ >>>
+ >>> msg2 = {True: "已成年", False: "未成年"}[age2 > 18]
+ >>> print(msg2)
+ 未成年
- def install_meta(address):
- finder = UrlMetaFinder(address)
- sys.meta_path.append(finder)
+第六种
+------
-所有的代码都解析完毕后,我们将其整理在一个模块(my_importer.py)中
+语法
.. code:: python
- # my_importer.py
- import sys
- import importlib
- import urllib.request as urllib2
-
- class UrlMetaFinder(importlib.abc.MetaPathFinder):
- def __init__(self, baseurl):
- self._baseurl = baseurl
-
-
- def find_module(self, fullname, path=None):
- if path is None:
- baseurl = self._baseurl
- else:
- # 不是原定义的url就直接返回不存在
- if not path.startswith(self._baseurl):
- return None
- baseurl = path
-
- try:
- loader = UrlMetaLoader(baseurl)
- return loader
- except Exception:
- return None
+ (() and (,) or (,))[0]
- class UrlMetaLoader(importlib.abc.SourceLoader):
- def __init__(self, baseurl):
- self.baseurl = baseurl
-
- def get_code(self, fullname):
- f = urllib2.urlopen(self.get_filename(fullname))
- return f.read()
-
- def get_data(self):
- pass
-
- def get_filename(self, fullname):
- return self.baseurl + fullname + '.py'
-
- def install_meta(address):
- finder = UrlMetaFinder(address)
- sys.meta_path.append(finder)
-
-5.2 搭建远程服务端
-~~~~~~~~~~~~~~~~~~
-
-最开始我说了,要实现一个远程导入模块的方法。
-
-我还缺一个在远端的服务器,来存放我的模块,为了方便,我使用python自带的
-``http.server`` 模块用一条命令即可实现。
-
-.. code:: shell
-
- $ mkdir httpserver && cd httpserver
- $ cat>my_info.py>> from my_importer import install_meta
- >>> install_meta('http://localhost:12800/') # 往 sys.meta_path 注册 finder
- >>> import my_info # 打印ok,说明导入成功
- ok
- >>> my_info.name # 验证可以取得到变量
- 'wangbm'
-
-至此,我实现了一个简易的可以导入远程服务器上的模块的导入器。
-
-参考文档
---------
+ >>> msg1 = ((age1 > 18) and ("已成年",) or ("未成年",))[0]
+ >>> print(msg1)
+ 已成年
+ >>>
+ >>> msg2 = ((age2 > 18) and ("已成年",) or ("未成年",))[0]
+ >>> print(msg2)
+ 未成年
-- https://docs.python.org/zh-cn/3/reference/import.html
-- https://docs.python.org/zh-cn/3/library/importlib.html#module-importlib.abc
-- https://python3-cookbook.readthedocs.io/zh_CN/latest/c10/p11_load_modules_from_remote_machine_by_hooks.html
+以上代码,都比较简单,仔细看都能看懂,我就不做解释了。
-.. figure:: http://image.python-online.cn/image-20200320125724880.png
- :alt: 关注公众号,获取最新干货!
+看到这里,有没有涨姿势了,学了这么久的 Python
+,这么多骚操作,还真是活久见。。这六种写法里,我最推荐使用的是第一种,自己也经常在用,简洁直白,代码行还少。而其他的写法虽然能写,但是不会用,也不希望在我余生里碰到会在公共代码里用这些写法的同事。
- 关注公众号,获取最新干货!
+|image1|
-.. |image0| image:: http://image.python-online.cn/20191027192949.png
+.. |image0| image:: http://image.iswbm.com/20200602135014.png
+.. |image1| image:: http://image.iswbm.com/20200607174235.png
diff --git a/source/c01/c01_25.md b/source/c01/c01_25.md
index dddcda6..428535f 100644
--- a/source/c01/c01_25.md
+++ b/source/c01/c01_25.md
@@ -1,109 +1,290 @@
-# 1.25 50% 的人不知道的Python 包与模块的知识盲区
+# 1.25 Python炫技操作:花式导包的八种方法
-## 1. 使用 \__all__ 控制可被导入的变量
+
-使用 `from module import *` 默认情况下会导入 module 里的所有变量,若你只想从模块中导入其中几个变量,可以在 module 中使用 `__all__` 来控制想要被其他模块导入的变量。
-```python
-# profile.py
-name='wangbm'
-age=27
-gender='male'
-__all__=['name']
+## 1. 直接 import
+
+人尽皆知的方法,直接导入即可
+
+```python
+>>> import os
+>>> os.getcwd()
+'/home/wangbm'
```
-打开 python console 验证一下
+与此类似的还有,不再细讲
```python
->>> from profile import *
->>> print(name)
-wangbm
->>> print(age)
-Traceback (most recent call last):
- File "", line 1, in
-NameError: name 'age' is not defined
->>> print(gender)
-Traceback (most recent call last):
- File "", line 1, in
-NameError: name 'gender' is not defined
+import ...
+import ... as ...
+from ... import ...
+from ... import ... as ...
```
-`__all__` 仅对于使用`from module import *` 这种情况适用。
+一般情况下,使用 `import` 语句导入模块已经够用的。
-它经常在一个包的 `__init__.py` 中出现。
+但是在一些特殊场景中,可能还需要其他的导入方式。
+下面我会一一地给你介绍。
+## 2. 使用 \__import__
-## 2. 命名空间包的神奇之处
+`__import__` 函数可用于导入模块,import 语句也会调用函数。其定义为:
-命名空间包,一个陌生的名字。
+```
+__import__(name[, globals[, locals[, fromlist[, level]]]])
+```
-与我们熟悉的常规包不同的是,它没有 `__init__.py` 文件。
+参数介绍:
-更为特殊的是,它可以跨空间地将两个不相邻的子包,合并成一个虚拟机的包,我们将其称之为 `命名空间包`。
+- name (required): 被加载 module 的名称
+- globals (optional): 包含全局变量的字典,该选项很少使用,采用默认值 global()
+- locals (optional): 包含局部变量的字典,内部标准实现未用到该变量,采用默认值 - local()
+- fromlist (Optional): 被导入的 submodule 名称
+- level (Optional): 导入路径选项,Python 2 中默认为 -1,表示同时支持 absolute import 和 relative import。Python 3 中默认为 0,表示仅支持 absolute import。如果大于 0,则表示相对导入的父目录的级数,即 1 类似于 '.',2 类似于 '..'。
-例如,一个项目的部分代码布局如下
+使用示例如下:
+```python
+>>> os = __import__('os')
+>>> os.getcwd()
+'/home/wangbm'
```
-foo-package/
- spam/
- blah.py
-bar-package/
- spam/
- grok.py
+如果要实现 `import xx as yy` 的效果,只要修改左值即可
+
+如下示例,等价于 `import os as myos`:
+
+```python
+>>> myos = __import__('os')
+>>> myos.getcwd()
+'/home/wangbm'
```
-在这2个目录里,都有着共同的命名空间spam。在任何一个目录里都没有__init__.py文件。
-让我们看看,如果将foo-package和bar-package都加到python模块路径并尝试导入会发生什么?
+
+上面说过的 `__import__` 是一个内建函数,既然是内建函数的话,那么这个内建函数必将存在于 `__buildins__` 中,因此我们还可以这样导入 os 的模块:
```python
->>> import sys
->>> sys.path.extend(['foo-package', 'bar-package'])
->>> import spam.blah
->>> import spam.grok
->>>
+>>> __builtins__.__dict__['__import__']('os').getcwd()
+'/home/wangbm'
```
-当一个包为命名空间包时,他就不再和常规包一样具有 `__file_` 属性,取而代之的是 `__path__`
+## 3. 使用 importlib 模块
+
+importlib 是 Python 中的一个标准库,importlib 能提供的功能非常全面。
+
+它的简单示例:
+
+```python
+>>> import importlib
+>>> myos=importlib.import_module("os")
+>>> myos.getcwd()
+'/home/wangbm'
+```
+
+如果要实现 `import xx as yy`效果,可以这样
```python
->>> import sys
->>> sys.path.extend(['foo-package', 'bar-package'])
->>> import spam.blah
->>> import spam.grok
->>> spam.__path__
-_NamespacePath(['foo-package/spam', 'bar-package/spam'])
->>> spam.__file__
-Traceback (most recent call last):
- File "", line 1, in
-AttributeError: 'module' object has no attribute '__file__'
+>>> import importlib
+>>>
+>>> myos = importlib.import_module("os")
+>>> myos.getcwd()
+'/home/wangbm'
```
-## 3. 包重载就是一个坑
+## 4. 使用 imp 模块
-第一种方法
+`imp` 模块提供了一些 import 语句内部实现的接口。例如模块查找(find_module)、模块加载(load_module)等等(模块的导入过程会包含模块查找、加载、缓存等步骤)。可以用该模块来简单实现内建的 `__import__` 函数功能:
```python
->>> import spam
>>> import imp
->>> imp.reload(spam)
-
+>>> file, pathname, desc = imp.find_module('os')
+>>> myos = imp.load_module('sep', file, pathname, desc)
+>>> myos
+
+>>> myos.getcwd()
+'/home/wangbm'
+```
+
+从 python 3 开始,内建的 reload 函数被移到了 imp 模块中。而从 Python 3.4 开始,imp 模块被否决,不再建议使用,其包含的功能被移到了 importlib 模块下。即从 Python 3.4 开始,importlib 模块是之前 imp 模块和 importlib 模块的合集。
+
+
+
+## 5. 使用 execfile
+
+在 Python 2 中有一个 execfile 函数,利用它可以用来执行一个文件。
+
+语法如下:
+
+```
+execfile(filename[, globals[, locals]])
+```
+
+参数有这么几个:
+
+- filename:文件名。
+- globals:变量作用域,全局命名空间,如果被提供,则必须是一个字典对象。
+- locals:变量作用域,局部命名空间,如果被提供,可以是任何映射对象。
+
+```python
+>>> execfile("/usr/lib64/python2.7/os.py")
+>>>
+>>> getcwd()
+'/home/wangbm'
+```
+
+
+
+## 6. 使用 exec 执行
+
+`execfile` 只能在 Python2 中使用,Python 3.x 里已经删除了这个函数。
+
+但是原理值得借鉴,你可以使用 open ... read 读取文件内容,然后再用 exec 去执行模块。
+
+示例如下:
+
+```python
+>>> with open("/usr/lib64/python2.7/os.py", "r") as f:
+... exec(f.read())
+...
+>>> getcwd()
+'/home/wangbm'
+```
+
+
+
+## 7. import_from_github_com
+
+有一个包叫做 **import_from_github_com**,从名字上很容易得知,它是一个可以从 github 下载安装并导入的包。为了使用它,你需要做的就是按照如下命令使用pip 先安装它。
+
+```shell
+$ python3 -m pip install import_from_github_com
+```
+
+这个包使用了PEP 302中新的引入钩子,允许你可以从github上引入包。这个包实际做的就是安装这个包并将它添加到本地。你需要 Python 3.2 或者更高的版本,并且 git 和 pip 都已经安装才能使用这个包。
+
+pip 要保证是较新版本,如果不是请执行如下命令进行升级。
+
+```shell
+$ python3 -m pip install --upgrade pip
+```
+
+确保环境 ok 后,你就可以在 Python shell 中使用 import_from_github_com
+
+示例如下
+
+```python
+>>> from github_com.zzzeek import sqlalchemy
+Collecting git+https://github.com/zzzeek/sqlalchemy
+Cloning https://github.com/zzzeek/sqlalchemy to /tmp/pip-acfv7t06-build
+Installing collected packages: SQLAlchemy
+Running setup.py install for SQLAlchemy ... done
+Successfully installed SQLAlchemy-1.1.0b1.dev0
+>>> locals()
+{'__builtins__': , '__spec__': None,
+'__package__': None, '__doc__': None, '__name__': '__main__',
+'sqlalchemy': ,
+'__loader__': }
>>>
```
-由于这种重载方法,只对 `import module` 有效,而使用 `from module import arg` 导入的 arg 并不会刷新。
+看了 import_from_github_com的源码后,你会注意到它并没有使用importlib。实际上,它的原理就是使用 pip 来安装那些没有安装的包,然后使用Python的`__import__()`函数来引入新安装的模块。
+
+
+
+## 8. 远程导入模块
+
+我在这篇文章里([深入探讨 Python 的 import 机制:实现远程导入模块](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484838&idx=1&sn=1e6fbf5d7546902c6965c60383f7b639&chksm=e8866544dff1ec52e01b6c9a982dfa150b8e34ad472acca35201373dc51dadb5a8630870982a&scene=21#wechat_redirect)),深入剖析了导入模块的内部原理,并在最后手动实现了从远程服务器上读取模块内容,并在本地成功将模块导入的导入器。
+
+具体内容非常的多,你可以点击这个[链接](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484838&idx=1&sn=1e6fbf5d7546902c6965c60383f7b639&chksm=e8866544dff1ec52e01b6c9a982dfa150b8e34ad472acca35201373dc51dadb5a8630870982a&scene=21#wechat_redirect)进行深入学习。
+
+示例代码如下:
+
+```python
+# 新建一个 py 文件(my_importer.py),内容如下
+import sys
+import importlib
+import urllib.request as urllib2
+
+class UrlMetaFinder(importlib.abc.MetaPathFinder):
+ def __init__(self, baseurl):
+ self._baseurl = baseurl
+
+
+ def find_module(self, fullname, path=None):
+ if path is None:
+ baseurl = self._baseurl
+ else:
+ # 不是原定义的url就直接返回不存在
+ if not path.startswith(self._baseurl):
+ return None
+ baseurl = path
+
+ try:
+ loader = UrlMetaLoader(baseurl)
+ return loader
+ except Exception:
+ return None
+
+class UrlMetaLoader(importlib.abc.SourceLoader):
+ def __init__(self, baseurl):
+ self.baseurl = baseurl
+
+ def get_code(self, fullname):
+ f = urllib2.urlopen(self.get_filename(fullname))
+ return f.read()
+
+ def get_data(self):
+ pass
+
+ def get_filename(self, fullname):
+ return self.baseurl + fullname + '.py'
+
+def install_meta(address):
+ finder = UrlMetaFinder(address)
+ sys.meta_path.append(finder)
+```
+
+并且在远程服务器上开启 http 服务(为了方便,我仅在本地进行演示),并且手动编辑一个名为 my_info 的 python 文件,如果后面导入成功会打印 `ok`。
+
+```shell
+$ mkdir httpserver && cd httpserver
+$ cat>my_info.py>> from my_importer import install_meta
+>>> install_meta('http://localhost:12800/') # 往 sys.meta_path 注册 finder
+>>> import my_info # 打印ok,说明导入成功
+ok
+>>> my_info.name # 验证可以取得到变量
+'wangbm'
+```
-因此,在生产环境中可能需要避免重新加载模块。而在调试模式中,它会提供一定的便利,但你要知道这个重载的弊端,以免掉入坑里。
+好了,8 种方法都给大家介绍完毕,对于普通开发者来说,其实只要掌握 import 这种方法足够了,而对于那些想要自己开发框架的人来说,深入学习` __import__ `以及 importlib 是非常有必要的。
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/source/c01/c01_25.rst b/source/c01/c01_25.rst
index 846efa9..90d9581 100644
--- a/source/c01/c01_25.rst
+++ b/source/c01/c01_25.rst
@@ -1,111 +1,309 @@
-1.25 50% 的人不知道的Python 包与模块的知识盲区
-==============================================
+1.25 Python炫技操作:花式导包的八种方法
+=======================================
-1. 使用 \__all_\_ 控制可被导入的变量
-------------------------------------
+|image0|
-使用 ``from module import *`` 默认情况下会导入 module
-里的所有变量,若你只想从模块中导入其中几个变量,可以在 module 中使用
-``__all__`` 来控制想要被其他模块导入的变量。
+1. 直接 import
+--------------
+
+人尽皆知的方法,直接导入即可
.. code:: python
- # profile.py
- name='wangbm'
- age=27
- gender='male'
+ >>> import os
+ >>> os.getcwd()
+ '/home/wangbm'
+
+与此类似的还有,不再细讲
+
+.. code:: python
+
+ import ...
+ import ... as ...
+ from ... import ...
+ from ... import ... as ...
+
+一般情况下,使用 ``import`` 语句导入模块已经够用的。
+
+但是在一些特殊场景中,可能还需要其他的导入方式。
+
+下面我会一一地给你介绍。
+
+2. 使用 \__import_\_
+--------------------
+
+``__import__`` 函数可用于导入模块,import 语句也会调用函数。其定义为:
+
+::
+
+ __import__(name[, globals[, locals[, fromlist[, level]]]])
+
+参数介绍:
+
+- name (required): 被加载 module 的名称
+- globals (optional): 包含全局变量的字典,该选项很少使用,采用默认值
+ global()
+- locals (optional):
+ 包含局部变量的字典,内部标准实现未用到该变量,采用默认值 - local()
+- fromlist (Optional): 被导入的 submodule 名称
+- level (Optional): 导入路径选项,Python 2 中默认为 -1,表示同时支持
+ absolute import 和 relative import。Python 3 中默认为 0,表示仅支持
+ absolute import。如果大于 0,则表示相对导入的父目录的级数,即 1
+ 类似于 ‘.’,2 类似于 ‘..’。
+
+使用示例如下:
+
+.. code:: python
- __all__=['name']
+ >>> os = __import__('os')
+ >>> os.getcwd()
+ '/home/wangbm'
-打开 python console 验证一下
+如果要实现 ``import xx as yy`` 的效果,只要修改左值即可
+
+如下示例,等价于 ``import os as myos``\ :
+
+.. code:: python
+
+ >>> myos = __import__('os')
+ >>> myos.getcwd()
+ '/home/wangbm'
+
+上面说过的 ``__import__``
+是一个内建函数,既然是内建函数的话,那么这个内建函数必将存在于
+``__buildins__`` 中,因此我们还可以这样导入 os 的模块:
.. code:: python
- >>> from profile import *
- >>> print(name)
- wangbm
- >>> print(age)
- Traceback (most recent call last):
- File "", line 1, in
- NameError: name 'age' is not defined
- >>> print(gender)
- Traceback (most recent call last):
- File "", line 1, in
- NameError: name 'gender' is not defined
+ >>> __builtins__.__dict__['__import__']('os').getcwd()
+ '/home/wangbm'
-``__all__`` 仅对于使用\ ``from module import *`` 这种情况适用。
+3. 使用 importlib 模块
+----------------------
-它经常在一个包的 ``__init__.py`` 中出现。
+importlib 是 Python 中的一个标准库,importlib 能提供的功能非常全面。
-2. 命名空间包的神奇之处
------------------------
+它的简单示例:
-命名空间包,一个陌生的名字。
+.. code:: python
+
+ >>> import importlib
+ >>> myos=importlib.import_module("os")
+ >>> myos.getcwd()
+ '/home/wangbm'
+
+如果要实现 ``import xx as yy``\ 效果,可以这样
+
+.. code:: python
-与我们熟悉的常规包不同的是,它没有 ``__init__.py`` 文件。
+ >>> import importlib
+ >>>
+ >>> myos = importlib.import_module("os")
+ >>> myos.getcwd()
+ '/home/wangbm'
-更为特殊的是,它可以跨空间地将两个不相邻的子包,合并成一个虚拟机的包,我们将其称之为
-``命名空间包``\ 。
+4. 使用 imp 模块
+----------------
-例如,一个项目的部分代码布局如下
+``imp`` 模块提供了一些 import
+语句内部实现的接口。例如模块查找(find_module)、模块加载(load_module)等等(模块的导入过程会包含模块查找、加载、缓存等步骤)。可以用该模块来简单实现内建的
+``__import__`` 函数功能:
+
+.. code:: python
+
+ >>> import imp
+ >>> file, pathname, desc = imp.find_module('os')
+ >>> myos = imp.load_module('sep', file, pathname, desc)
+ >>> myos
+
+ >>> myos.getcwd()
+ '/home/wangbm'
+
+从 python 3 开始,内建的 reload 函数被移到了 imp 模块中。而从 Python 3.4
+开始,imp 模块被否决,不再建议使用,其包含的功能被移到了 importlib
+模块下。即从 Python 3.4 开始,importlib 模块是之前 imp 模块和 importlib
+模块的合集。
+
+5. 使用 execfile
+----------------
+
+在 Python 2 中有一个 execfile 函数,利用它可以用来执行一个文件。
+
+语法如下:
::
- foo-package/
- spam/
- blah.py
+ execfile(filename[, globals[, locals]])
- bar-package/
- spam/
- grok.py
+参数有这么几个:
-在这2个目录里,都有着共同的命名空间spam。在任何一个目录里都没有__init__.py文件。
+- filename:文件名。
+- globals:变量作用域,全局命名空间,如果被提供,则必须是一个字典对象。
+- locals:变量作用域,局部命名空间,如果被提供,可以是任何映射对象。
-让我们看看,如果将foo-package和bar-package都加到python模块路径并尝试导入会发生什么?
+.. code:: python
+
+ >>> execfile("/usr/lib64/python2.7/os.py")
+ >>>
+ >>> getcwd()
+ '/home/wangbm'
+
+6. 使用 exec 执行
+-----------------
+
+``execfile`` 只能在 Python2 中使用,Python 3.x 里已经删除了这个函数。
+
+但是原理值得借鉴,你可以使用 open … read 读取文件内容,然后再用 exec
+去执行模块。
+
+示例如下:
.. code:: python
- >>> import sys
- >>> sys.path.extend(['foo-package', 'bar-package'])
- >>> import spam.blah
- >>> import spam.grok
+ >>> with open("/usr/lib64/python2.7/os.py", "r") as f:
+ ... exec(f.read())
+ ...
+ >>> getcwd()
+ '/home/wangbm'
+
+7. import_from_github_com
+-------------------------
+
+有一个包叫做
+**import_from_github_com**\ ,从名字上很容易得知,它是一个可以从 github
+下载安装并导入的包。为了使用它,你需要做的就是按照如下命令使用pip
+先安装它。
+
+.. code:: shell
+
+ $ python3 -m pip install import_from_github_com
+
+这个包使用了PEP
+302中新的引入钩子,允许你可以从github上引入包。这个包实际做的就是安装这个包并将它添加到本地。你需要
+Python 3.2 或者更高的版本,并且 git 和 pip 都已经安装才能使用这个包。
+
+pip 要保证是较新版本,如果不是请执行如下命令进行升级。
+
+.. code:: shell
+
+ $ python3 -m pip install --upgrade pip
+
+确保环境 ok 后,你就可以在 Python shell 中使用 import_from_github_com
+
+示例如下
+
+.. code:: python
+
+ >>> from github_com.zzzeek import sqlalchemy
+ Collecting git+https://github.com/zzzeek/sqlalchemy
+ Cloning https://github.com/zzzeek/sqlalchemy to /tmp/pip-acfv7t06-build
+ Installing collected packages: SQLAlchemy
+ Running setup.py install for SQLAlchemy ... done
+ Successfully installed SQLAlchemy-1.1.0b1.dev0
+ >>> locals()
+ {'__builtins__': , '__spec__': None,
+ '__package__': None, '__doc__': None, '__name__': '__main__',
+ 'sqlalchemy': ,
+ '__loader__': }
>>>
-当一个包为命名空间包时,他就不再和常规包一样具有 ``__file_``
-属性,取而代之的是 ``__path__``
+看了
+import_from_github_com的源码后,你会注意到它并没有使用importlib。实际上,它的原理就是使用
+pip
+来安装那些没有安装的包,然后使用Python的\ ``__import__()``\ 函数来引入新安装的模块。
+
+8. 远程导入模块
+---------------
+
+我在这篇文章里(\ `深入探讨 Python 的 import
+机制:实现远程导入模块 `__\ ),深入剖析了导入模块的内部原理,并在最后手动实现了从远程服务器上读取模块内容,并在本地成功将模块导入的导入器。
+
+具体内容非常的多,你可以点击这个\ `链接 `__\ 进行深入学习。
+
+示例代码如下:
.. code:: python
- >>> import sys
- >>> sys.path.extend(['foo-package', 'bar-package'])
- >>> import spam.blah
- >>> import spam.grok
- >>> spam.__path__
- _NamespacePath(['foo-package/spam', 'bar-package/spam'])
- >>> spam.__file__
- Traceback (most recent call last):
- File "", line 1, in
- AttributeError: 'module' object has no attribute '__file__'
+ # 新建一个 py 文件(my_importer.py),内容如下
+ import sys
+ import importlib
+ import urllib.request as urllib2
+
+ class UrlMetaFinder(importlib.abc.MetaPathFinder):
+ def __init__(self, baseurl):
+ self._baseurl = baseurl
+
+
+ def find_module(self, fullname, path=None):
+ if path is None:
+ baseurl = self._baseurl
+ else:
+ # 不是原定义的url就直接返回不存在
+ if not path.startswith(self._baseurl):
+ return None
+ baseurl = path
+
+ try:
+ loader = UrlMetaLoader(baseurl)
+ return loader
+ except Exception:
+ return None
+
+ class UrlMetaLoader(importlib.abc.SourceLoader):
+ def __init__(self, baseurl):
+ self.baseurl = baseurl
+
+ def get_code(self, fullname):
+ f = urllib2.urlopen(self.get_filename(fullname))
+ return f.read()
-3. 包重载就是一个坑
--------------------
+ def get_data(self):
+ pass
-第一种方法
+ def get_filename(self, fullname):
+ return self.baseurl + fullname + '.py'
+
+ def install_meta(address):
+ finder = UrlMetaFinder(address)
+ sys.meta_path.append(finder)
+
+并且在远程服务器上开启 http
+服务(为了方便,我仅在本地进行演示),并且手动编辑一个名为 my_info 的
+python 文件,如果后面导入成功会打印 ``ok``\ 。
+
+.. code:: shell
+
+ $ mkdir httpserver && cd httpserver
+ $ cat>my_info.py>> import spam
- >>> import imp
- >>> imp.reload(spam)
-
- >>>
+ >>> from my_importer import install_meta
+ >>> install_meta('http://localhost:12800/') # 往 sys.meta_path 注册 finder
+ >>> import my_info # 打印ok,说明导入成功
+ ok
+ >>> my_info.name # 验证可以取得到变量
+ 'wangbm'
-由于这种重载方法,只对 ``import module`` 有效,而使用
-``from module import arg`` 导入的 arg 并不会刷新。
+好了,8 种方法都给大家介绍完毕,对于普通开发者来说,其实只要掌握 import
+这种方法足够了,而对于那些想要自己开发框架的人来说,深入学习\ ``__import__``\ 以及
+importlib 是非常有必要的。
-因此,在生产环境中可能需要避免重新加载模块。而在调试模式中,它会提供一定的便利,但你要知道这个重载的弊端,以免掉入坑里。
+|image1|
-.. figure:: http://image.python-online.cn/image-20200320125724880.png
- :alt: 关注公众号,获取最新干货!
+.. |image0| image:: http://image.iswbm.com/20200602135014.png
+.. |image1| image:: http://image.iswbm.com/20200607174235.png
- 关注公众号,获取最新干货!
diff --git a/source/c01/c01_26.md b/source/c01/c01_26.md
index c94c65c..d75731e 100644
--- a/source/c01/c01_26.md
+++ b/source/c01/c01_26.md
@@ -1,262 +1,220 @@
-# 1.26 C语言基础的学习
+# 1.26 Python 炫技操作:合并字典的七种方法
-## 1. 安装编译器
+
-C 语言编译器用于把源代码编译成最终的可执行程序。这里假设您已经对编程语言编译器有基本的了解了。
+Python 语言里有许多(而且是越来越多)的高级特性,是 Python 发烧友们非常喜欢的。在这些人的眼里,能够写出那些一般开发者看不懂的高级特性,就是高手,就是大神。
-最常用的免费可用的编译器是 GNU 的 C/C++ 编译器。
+但你要知道,在团队合作里,炫技是大忌。
-### 1.1 windows
+为什么这么说呢?我说下自己的看法:
-MinGw 是 Minimal GNU on Windows 的缩写,允许在 GNU/Linux 和 Windows 平台生成本地的 Windows 程序而不需要第三方运行时库。本文主要介绍 MinGw 的安装和使用。
+1. 越简洁的代码,越清晰的逻辑,就越不容易出错;
+2. 在团队合作中,你的代码不只有你在维护,降低别人的阅读/理解代码逻辑的成本是一个良好的品德
+3. 简单的代码,只会用到最基本的语法糖,复杂的高级特性,会有更多的依赖(如语言的版本)
-**安装**
+该篇是「**炫技系列**」的第二篇内容,在这个系列里,我将总结盘点一下,我所见过的那些炫技操作。在这里,如果你是 Python 发烧友,你可以学到一些写出超酷的代码书写技巧。同时,看了这些内容,对你在阅读别人的代码时,也许会有些帮助。
-1.下载 [min-gw](https://osdn.net/projects/mingw/downloads/68260/mingw-get-setup.exe/) 安装程序,下载 mingw-get-setup.exe (86.5 kB)
+## 1. 最简单的原地更新
-2.运行 mingw-get-setup.exe (86.5 kB) ,点击“运行”,continue等,注意记住安装的目录,如 **C:\MinGw**,下面修改环境变量时还会用到。
-3.修改环境变量: 选择计算机—属性---高级系统设置---环境变量,在系统变量中找到 Path 变量,在后面加入 min-gw的安装目录,如 **C:\MinGw\bin**
-4.在开始菜单中,点击“运行”,输入 **cmd**,打开命令行:输入 **mingw-get.exe**,如果弹出 MinGw installation manager 窗口,说明安装正常。此时,关闭 MinGw installation manager 窗口,否则接下来的步骤会报错
-5.在cmd中输入命令 **mingw-get install gcc**,等待一会,gcc 就安装成功了。
-
-如果想安装 g++,gdb,只要输入命令 **mingw-get install g++** 和 **mingw-get install gdb**
-
-**使用**
-
-在 cmd 的当前工作目录写 C 程序 hello.c:
+字典对象内置了一个 update 方法,用于把另一个字典更新到自己身上。
+```python
+>>> profile = {"name": "xiaoming", "age": 27}
+>>> ext_info = {"gender": "male"}
+>>>
+>>> profile.update(ext_info)
+>>> print(profile)
+{'name': 'xiaoming', 'age': 27, 'gender': 'male'}
```
-# include
-int main()
-{
- printf("%s\n","hello world");
- return 0;
-}
-```
-
-在 cmd 中输入命令
-```shell
-$ gcc hello.c
+如果想使用 update 这种最简单、最地道原生的方法,但又不想更新到自己身上,而是生成一个新的对象,那请使用深拷贝。
+
+```python
+>>> profile = {"name": "xiaoming", "age": 27}
+>>> ext_info = {"gender": "male"}
+>>>
+>>> from copy import deepcopy
+>>>
+>>> full_profile = deepcopy(profile)
+>>> full_profile.update(ext_info)
+>>>
+>>> print(full_profile)
+{'name': 'xiaoming', 'age': 27, 'gender': 'male'}
+>>> print(profile)
+{"name": "xiaoming", "age": 27}
```
-在当前目录下会生成 a.exe 的可执行文件,在 cmd 中输入 a.exe 就可以执行程序了。
-如果想调试程序,可以输入 gdb a.exe
-进入 gdb 的功能,使用 gdb 常用的命令就可以调试程序了。
+## 2. 先解包再合并字典
-### Mac OSX 及 Linux
+使用 `**` 可以解包字典,解包完后再使用 dict 或者 `{}` 就可以合并。
-从苹果的网站上下载 [Xcode 开发环境](https://developer.apple.com/xcode/),并按照安装说明进行安装。一旦安装上 Xcode,您就能使用 GNU 编译器。
-
-**使用**
+```python
+>>> profile = {"name": "xiaoming", "age": 27}
+>>> ext_info = {"gender": "male"}
+>>>
+>>> full_profile01 = {**profile, **ext_info}
+>>> print(full_profile01)
+{'name': 'xiaoming', 'age': 27, 'gender': 'male'}
+>>>
+>>> full_profile02 = dict(**profile, **ext_info)
+>>> print(full_profile02)
+{'name': 'xiaoming', 'age': 27, 'gender': 'male'}
+```
-打开终端,使用 vim 编辑文件 hello.c
+若你不知道 `dict(**profile, **ext_info)` 做了啥,你可以将它等价于
-```
-# include
-int main()
-{
- printf("%s\n","hello world");
- return 0;
-}
+```python
+>>> dict((("name", "xiaoming"), ("age", 27), ("gender", "male")))
+{'name': 'xiaoming', 'age': 27, 'gender': 'male'}
```
-使用 gcc 工具进行编译完后,就会在当前目录下生成一个名为 a.out 的可执行文件,手动运行它即可
-```shell
-$ gcc hello.c
-$ ./a.out
-```
+## 3. 借助 itertools
+在 Python 里有一个非常强大的内置模块,它专门用于操作可迭代对象。
-### 使用 VS code
-
-在 vscode 中引入标准库的头文件时,会出现波浪线,提示找不到头文件。
-
-解决方法是在当前项目下的 .vscode/c_cpp_properties.json 中的 includePath 列表中添加包含标准头文件的路径,这个路径哪里来呢?你可以使用 everything 搜索一下,哪个路径下有此 stdio.h 文件。
-
-比如我的是这样,注意路径不要使用 `\` 和 `\\` ,一定要使用 `\`
-
-```json
-{
- "configurations": [
- {
- "name": "Win32",
- "includePath": [
- "${workspaceFolder}/**",
- "E:/MinGW/lib/gcc/mingw32/8.2.0/include",
- "E:/MinGW/include",
- "E:/MinGW/lib/gcc/mingw32/8.2.0/include/c++/tr1",
- "E:/MinGW/lib/gcc/mingw32/8.2.0/include/ssp",
- "C:/Users/wangbm/AppData/Local/Programs/Common/Microsoft/Visual C++ for Python/9.0/VC/include"
- ],
- "defines": [
- "_DEBUG",
- "UNICODE",
- "_UNICODE"
- ],
- "intelliSenseMode": "msvc-x64"
- }
- ],
- "version": 4
-}
-```
+正好我们字典也是可迭代对象,自然就可以想到,可以使用 `itertools.chain()` 函数先将多个字典(可迭代对象)串联起来,组成一个更大的可迭代对象,然后再使用 dict 转成字典。
+```python
+>>> import itertools
+>>>
+>>> profile = {"name": "xiaoming", "age": 27}
+>>> ext_info = {"gender": "male"}
+>>>
+>>>
+>>> dict(itertools.chain(profile.items(), ext_info.items()))
+{'name': 'xiaoming', 'age': 27, 'gender': 'male'}
+```
-## 字符串学习
-### 格式式字符串
+## 4. 借助 ChainMap
-```shell
-%d 有符号10进制整数(%ld 长整型,%hd短整型 )
-%hu 无符号短整形(%u无符号整形,%lu无符号长整形)
-%i 有符号10进制整数 (%i 和%d 没有区别,%i 是老式写法,都是整型格式)
+如果可以引入一个辅助包,那我就再提一个, `ChainMap` 也可以达到和 `itertools` 同样的效果。
-%o 无符号8进制整数
-%u 无符号10进制整数
-%x 无符号的16进制数字,并以小写abcdef表示
-%X 无符号的16进制数字,并以大写ABCDEF表示
+```python
+>>> from collections import ChainMap
+>>>
+>>> profile = {"name": "xiaoming", "age": 27}
+>>> ext_info = {"gender": "male"}
+>>>
+>>> dict(ChainMap(profile, ext_info))
+{'name': 'xiaoming', 'age': 27, 'gender': 'male'}
+```
-%f 输入输出为浮点型 (%lf双精度浮点型)
-%E/e 用科学表示格式的浮点数
+使用 ChainMap 有一点需要注意,当字典间有重复的键时,只会取第一个值,排在后面的键值并不会更新掉前面的(使用 itertools 就不会有这个问题)。
-%c 输入输出为单个字符
-%s 输入输出为字符串
+```python
+>>> from collections import ChainMap
+>>>
+>>> profile = {"name": "xiaoming", "age": 27}
+>>> ext_info={"age": 30}
+>>> dict(ChainMap(profile, ext_info))
+{'name': 'xiaoming', 'age': 27}
```
-### 字符串声明定义
-字符串声明使用 `char`
-```c
-#include
+## 5. 使用dict.items() 合并
-// 定义 name ,不设置大小
-char name[] = "wangbm";
+在 Python 3.9 之前,其实就已经有 `|` 操作符了,只不过它通常用于对集合(set)取并集。
-// 定义 gender ,设置大小为7个字节
-char gender[7] = "female";
+利用这一点,也可以将它用于字典的合并,只不过得绕个弯子,有点不好理解。
-int main()
-{
- printf("name is %s\n", name);
- printf("size of name: %lu\n", sizeof(name));
- printf("len of name: %lu\n", strlen(name));
+你得先利用 `items` 方法将 dict 转成 dict_items,再对这两个 dict_items 取并集,最后利用 dict 函数,转成字典。
- printf("gender is %s\n", gender);
- printf("size of gender: %lu\n", sizeof(gender));
- printf("len of gender: %lu\n", strlen(gender));
- return 0;
-}
+```python
+>>> profile = {"name": "xiaoming", "age": 27}
+>>> ext_info = {"gender": "male"}
+>>>
+>>> full_profile = dict(profile.items() | ext_info.items())
+>>> full_profile
+{'gender': 'male', 'age': 27, 'name': 'xiaoming'}
```
-### 字符串操作
-- strcat(s1,s2) :string catenate,连接s2到s1末尾
-- strcpy(s1,s2) :string copy,复制字符串s2到s1
-- strlen(s1) :(string length),返回s1字符串的长度
-- strlwr(s1) :string lowercase,将s1的字符串的字母全部大写返回
-- strupr(s1) :string upercase,将s1的字符串的字母全部小写返回
-- strcmp(s1,s2) :string compare,如果 s1 和 s2 是相同的,则返回 0;如果 s1s2 则返回大于 0。
-### 字符的输入
+当然了,你如果嫌这样太麻烦,也可以简单点,直接使用 list 函数再合并(示例为 Python 3.x )
-使用 `printf` 和 `scanf` 函数
+```python
+>>> profile = {"name": "xiaoming", "age": 27}
+>>> ext_info = {"gender": "male"}
+>>>
+>>> dict(list(profile.items()) + list(ext_info.items()))
+{'name': 'xiaoming', 'age': 27, 'gender': 'male'}
+```
-```c
-# include
+若你在 Python 2.x 下,可以直接省去 list 函数。
-int main(int argc, char const *argv[])
-{
- char name[10];
- printf("Enter your name: ");
- scanf("%s", name);
- printf("Your name is: %s \n", name);
- return 0;
-}
+```python
+>>> profile = {"name": "xiaoming", "age": 27}
+>>> ext_info = {"gender": "male"}
+>>>
+>>> dict(profile.items() + ext_info.items())
+{'name': 'xiaoming', 'age': 27, 'gender': 'male'}
```
-使用 fgets 和 fputs 函数
-```c
-# include
-int main(int argc, char const *argv[])
-{
- char name[10];
- printf("Enter your name: ");
- fgets(name, 10, stdin);
- printf("Your name is: ");
- fputs(name, stdout);
- return 0;
-}
-```
+## 6. 最酷炫的字典解析式
-getchar() & putchar()
+Python 里对于生成列表、集合、字典,有一套非常 Pythonnic 的写法。
+那就是列表解析式,集合解析式和字典解析式,通常是 Python 发烧友的最爱,那么今天的主题:字典合并,字典解析式还能否胜任呢?
+当然可以,具体示例代码如下:
-### 指针相关的两个符号
+```python
+>>> profile = {"name": "xiaoming", "age": 27}
+>>> ext_info = {"gender": "male"}
+>>>
+>>> {k:v for d in [profile, ext_info] for k,v in d.items()}
+{'name': 'xiaoming', 'age': 27, 'gender': 'male'}
+```
-记住 `*` 有3个用途:
-1. 乘号(Multiply): 2*3 就是6
-2. 声明指针(Pointer Statement): int a =5; int* ptr=&a;就是声明变量a是5,把a的地址附到指针ptr上
-3. 解引用 (Dereference): *ptr 单独拿出来就是找出 ptr指针指向的值,按照第二点的说法就是5.
-`&`叫做取地址符号,一般指针只能接受一个内存地址而不能接受一个值
+## 7. Python 3.9 新特性
-```c
-// 如下是错误的,指针不能接受一个值
-int a =5; int* ptr=a;
+在 2 月份发布的 Python 3.9.04a 版本中,新增了一个抓眼球的新操作符操作符: `|`, PEP584 将它称之为合并操作符(Union Operator),用它可以很直观地合并多个字典。
-// 如下是正确的,a的地址给指针ptr
-int a =5; int* ptr=&a;
+```python
+>>> profile = {"name": "xiaoming", "age": 27}
+>>> ext_info = {"gender": "male"}
+>>>
+>>> profile | ext_info
+{'name': 'xiaoming', 'age': 27, 'gender': 'male'}
+>>>
+>>> ext_info | profile
+{'gender': 'male', 'name': 'xiaoming', 'age': 27}
+>>>
+>>>
```
+除了 `|` 操作符之外,还有另外一个操作符 `|=`,类似于原地更新。
+
+```python
+>>> ext_info |= profile
+>>> ext_info
+{'gender': 'male', 'name': 'xiaoming', 'age': 27}
+>>>
+>>>
+>>> profile |= ext_info
+>>> profile
+{'name': 'xiaoming', 'age': 27, 'gender': 'male'}
+```
-### 可变参数的获取
-
-以下写了一个函数 `get_sum` 来求得输入的所有参数的和(除了第一个参数外,第一个参数表示,对后面几个可变参数求和,在示例中是作为结束条件存在)。
-
-```c
-#include
-#include
-
-int get_sum(int n, ...)
-{
- va_list arglist; // 定义一个va_list类型的字符指针,用来指向当前参数,后面取参必须通过这个指针进行
- va_start(arglist, n); // 初始化这个指针,让其指向可变参数里的第一个参数,这里的参数应该填写 ... 的那个参数
- double sum;
-
- for (int i = 1; i <= n; i++)
- {
- sum += va_arg(arglist, int);
- if (i >= 3)
- {
- break;
- }
- }
-
- va_end(arglist); // 养成好习惯,将这个指针关闭
- return sum;
-}
-
-int main(int argc, char const *argv[])
-{
- /* code */
- printf("The sum is: %d", get_sum(3,1,1,1));
- return 0;
-}
-```
+
+看到这里,有没有涨姿势了,学了这么久的 Python ,没想到合并字典还有这么多的方法。本篇文章的主旨,并不在于让你全部掌握这 7 种合并字典的方法,实际在工作中,你只要选用一种最顺手的方式即可,但是在协同工作中,或者在阅读他人代码时,你不可避免地会碰到各式各样的写法,这时候你能下意识的知道这是在做合并字典的操作,那这篇文章就是有意义的。
+
+---
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/source/c01/c01_26.rst b/source/c01/c01_26.rst
index 4f32dcb..cb9ad4c 100644
--- a/source/c01/c01_26.rst
+++ b/source/c01/c01_26.rst
@@ -1,288 +1,231 @@
-1.26 C语言基础的学习
-====================
+1.26 Python 炫技操作:合并字典的七种方法
+========================================
-1. 安装编译器
--------------
+|image0|
-C
-语言编译器用于把源代码编译成最终的可执行程序。这里假设您已经对编程语言编译器有基本的了解了。
+Python 语言里有许多(而且是越来越多)的高级特性,是 Python
+发烧友们非常喜欢的。在这些人的眼里,能够写出那些一般开发者看不懂的高级特性,就是高手,就是大神。
-最常用的免费可用的编译器是 GNU 的 C/C++ 编译器。
+但你要知道,在团队合作里,炫技是大忌。
-1.1 windows
-~~~~~~~~~~~
+为什么这么说呢?我说下自己的看法:
-MinGw 是 Minimal GNU on Windows 的缩写,允许在 GNU/Linux 和 Windows
-平台生成本地的 Windows 程序而不需要第三方运行时库。本文主要介绍 MinGw
-的安装和使用。
+1. 越简洁的代码,越清晰的逻辑,就越不容易出错;
+2. 在团队合作中,你的代码不只有你在维护,降低别人的阅读/理解代码逻辑的成本是一个良好的品德
+3. 简单的代码,只会用到最基本的语法糖,复杂的高级特性,会有更多的依赖(如语言的版本)
-**安装**
+该篇是「\ **炫技系列**\ 」的第二篇内容,在这个系列里,我将总结盘点一下,我所见过的那些炫技操作。在这里,如果你是
+Python
+发烧友,你可以学到一些写出超酷的代码书写技巧。同时,看了这些内容,对你在阅读别人的代码时,也许会有些帮助。
-1.下载
-`min-gw `__
-安装程序,下载 mingw-get-setup.exe (86.5 kB)
+1. 最简单的原地更新
+-------------------
-2.运行 mingw-get-setup.exe (86.5 kB)
-,点击“运行”,continue等,注意记住安装的目录,如
-\**C::raw-latex:`\MinGw*`\*,下面修改环境变量时还会用到。 3.修改环境变量:
-选择计算机—属性—高级系统设置—环境变量,在系统变量中找到 Path
-变量,在后面加入 min-gw的安装目录,如
-\**C::raw-latex:`\MinGw`:raw-latex:`\bin*`\*
-4.在开始菜单中,点击“运行”,输入 **cmd**,打开命令行:输入
-**mingw-get.exe**,如果弹出 MinGw installation manager
-窗口,说明安装正常。此时,关闭 MinGw installation manager
-窗口,否则接下来的步骤会报错 5.在cmd中输入命令 **mingw-get install
-gcc**,等待一会,gcc 就安装成功了。
+字典对象内置了一个 update 方法,用于把另一个字典更新到自己身上。
-如果想安装 g++,gdb,只要输入命令 **mingw-get install g++** 和 **mingw-get
-install gdb**
+.. code:: python
-**使用**
+ >>> profile = {"name": "xiaoming", "age": 27}
+ >>> ext_info = {"gender": "male"}
+ >>>
+ >>> profile.update(ext_info)
+ >>> print(profile)
+ {'name': 'xiaoming', 'age': 27, 'gender': 'male'}
-在 cmd 的当前工作目录写 C 程序 hello.c:
+如果想使用 update
+这种最简单、最地道原生的方法,但又不想更新到自己身上,而是生成一个新的对象,那请使用深拷贝。
-::
+.. code:: python
- # include
- int main()
- {
- printf("%s\n","hello world");
- return 0;
- }
+ >>> profile = {"name": "xiaoming", "age": 27}
+ >>> ext_info = {"gender": "male"}
+ >>>
+ >>> from copy import deepcopy
+ >>>
+ >>> full_profile = deepcopy(profile)
+ >>> full_profile.update(ext_info)
+ >>>
+ >>> print(full_profile)
+ {'name': 'xiaoming', 'age': 27, 'gender': 'male'}
+ >>> print(profile)
+ {"name": "xiaoming", "age": 27}
-在 cmd 中输入命令
+2. 先解包再合并字典
+-------------------
-.. code:: shell
+使用 ``**`` 可以解包字典,解包完后再使用 dict 或者 ``{}`` 就可以合并。
- $ gcc hello.c
+.. code:: python
-在当前目录下会生成 a.exe 的可执行文件,在 cmd 中输入 a.exe
-就可以执行程序了。
+ >>> profile = {"name": "xiaoming", "age": 27}
+ >>> ext_info = {"gender": "male"}
+ >>>
+ >>> full_profile01 = {**profile, **ext_info}
+ >>> print(full_profile01)
+ {'name': 'xiaoming', 'age': 27, 'gender': 'male'}
+ >>>
+ >>> full_profile02 = dict(**profile, **ext_info)
+ >>> print(full_profile02)
+ {'name': 'xiaoming', 'age': 27, 'gender': 'male'}
-如果想调试程序,可以输入 gdb a.exe
+若你不知道 ``dict(**profile, **ext_info)`` 做了啥,你可以将它等价于
-进入 gdb 的功能,使用 gdb 常用的命令就可以调试程序了。
+.. code:: python
-Mac OSX 及 Linux
-~~~~~~~~~~~~~~~~
+ >>> dict((("name", "xiaoming"), ("age", 27), ("gender", "male")))
+ {'name': 'xiaoming', 'age': 27, 'gender': 'male'}
-从苹果的网站上下载 `Xcode
-开发环境 `__\ ,并按照安装说明进行安装。一旦安装上
-Xcode,您就能使用 GNU 编译器。
+3. 借助 itertools
+-----------------
-**使用**
+在 Python 里有一个非常强大的内置模块,它专门用于操作可迭代对象。
-打开终端,使用 vim 编辑文件 hello.c
+正好我们字典也是可迭代对象,自然就可以想到,可以使用
+``itertools.chain()``
+函数先将多个字典(可迭代对象)串联起来,组成一个更大的可迭代对象,然后再使用
+dict 转成字典。
-::
+.. code:: python
- # include
- int main()
- {
- printf("%s\n","hello world");
- return 0;
- }
+ >>> import itertools
+ >>>
+ >>> profile = {"name": "xiaoming", "age": 27}
+ >>> ext_info = {"gender": "male"}
+ >>>
+ >>>
+ >>> dict(itertools.chain(profile.items(), ext_info.items()))
+ {'name': 'xiaoming', 'age': 27, 'gender': 'male'}
-使用 gcc 工具进行编译完后,就会在当前目录下生成一个名为 a.out
-的可执行文件,手动运行它即可
+4. 借助 ChainMap
+----------------
-.. code:: shell
+如果可以引入一个辅助包,那我就再提一个, ``ChainMap`` 也可以达到和
+``itertools`` 同样的效果。
- $ gcc hello.c
- $ ./a.out
+.. code:: python
-使用 VS code
-~~~~~~~~~~~~
+ >>> from collections import ChainMap
+ >>>
+ >>> profile = {"name": "xiaoming", "age": 27}
+ >>> ext_info = {"gender": "male"}
+ >>>
+ >>> dict(ChainMap(profile, ext_info))
+ {'name': 'xiaoming', 'age': 27, 'gender': 'male'}
-在 vscode 中引入标准库的头文件时,会出现波浪线,提示找不到头文件。
+使用 ChainMap
+有一点需要注意,当字典间有重复的键时,只会取第一个值,排在后面的键值并不会更新掉前面的(使用
+itertools 就不会有这个问题)。
-解决方法是在当前项目下的 .vscode/c_cpp_properties.json 中的 includePath
-列表中添加包含标准头文件的路径,这个路径哪里来呢?你可以使用 everything
-搜索一下,哪个路径下有此 stdio.h 文件。
+.. code:: python
-比如我的是这样,注意路径不要使用 ``\`` 和 ``\\`` ,一定要使用 ``\``
+ >>> from collections import ChainMap
+ >>>
+ >>> profile = {"name": "xiaoming", "age": 27}
+ >>> ext_info={"age": 30}
+ >>> dict(ChainMap(profile, ext_info))
+ {'name': 'xiaoming', 'age': 27}
-.. code:: json
+5. 使用dict.items() 合并
+------------------------
- {
- "configurations": [
- {
- "name": "Win32",
- "includePath": [
- "${workspaceFolder}/**",
- "E:/MinGW/lib/gcc/mingw32/8.2.0/include",
- "E:/MinGW/include",
- "E:/MinGW/lib/gcc/mingw32/8.2.0/include/c++/tr1",
- "E:/MinGW/lib/gcc/mingw32/8.2.0/include/ssp",
- "C:/Users/wangbm/AppData/Local/Programs/Common/Microsoft/Visual C++ for Python/9.0/VC/include"
- ],
- "defines": [
- "_DEBUG",
- "UNICODE",
- "_UNICODE"
- ],
- "intelliSenseMode": "msvc-x64"
- }
- ],
- "version": 4
- }
+在 Python 3.9 之前,其实就已经有 ``|``
+操作符了,只不过它通常用于对集合(set)取并集。
-字符串学习
-----------
+利用这一点,也可以将它用于字典的合并,只不过得绕个弯子,有点不好理解。
-格式式字符串
-~~~~~~~~~~~~
+你得先利用 ``items`` 方法将 dict 转成 dict_items,再对这两个 dict_items
+取并集,最后利用 dict 函数,转成字典。
-.. code:: shell
+.. code:: python
- %d 有符号10进制整数(%ld 长整型,%hd短整型 )
- %hu 无符号短整形(%u无符号整形,%lu无符号长整形)
- %i 有符号10进制整数 (%i 和%d 没有区别,%i 是老式写法,都是整型格式)
+ >>> profile = {"name": "xiaoming", "age": 27}
+ >>> ext_info = {"gender": "male"}
+ >>>
+ >>> full_profile = dict(profile.items() | ext_info.items())
+ >>> full_profile
+ {'gender': 'male', 'age': 27, 'name': 'xiaoming'}
- %o 无符号8进制整数
- %u 无符号10进制整数
- %x 无符号的16进制数字,并以小写abcdef表示
- %X 无符号的16进制数字,并以大写ABCDEF表示
+当然了,你如果嫌这样太麻烦,也可以简单点,直接使用 list
+函数再合并(示例为 Python 3.x )
- %f 输入输出为浮点型 (%lf双精度浮点型)
- %E/e 用科学表示格式的浮点数
+.. code:: python
- %c 输入输出为单个字符
- %s 输入输出为字符串
+ >>> profile = {"name": "xiaoming", "age": 27}
+ >>> ext_info = {"gender": "male"}
+ >>>
+ >>> dict(list(profile.items()) + list(ext_info.items()))
+ {'name': 'xiaoming', 'age': 27, 'gender': 'male'}
-字符串声明定义
-~~~~~~~~~~~~~~
+若你在 Python 2.x 下,可以直接省去 list 函数。
-字符串声明使用 ``char``
+.. code:: python
-.. code:: c
+ >>> profile = {"name": "xiaoming", "age": 27}
+ >>> ext_info = {"gender": "male"}
+ >>>
+ >>> dict(profile.items() + ext_info.items())
+ {'name': 'xiaoming', 'age': 27, 'gender': 'male'}
- #include
+6. 最酷炫的字典解析式
+---------------------
- // 定义 name ,不设置大小
- char name[] = "wangbm";
+Python 里对于生成列表、集合、字典,有一套非常 Pythonnic 的写法。
- // 定义 gender ,设置大小为7个字节
- char gender[7] = "female";
+那就是列表解析式,集合解析式和字典解析式,通常是 Python
+发烧友的最爱,那么今天的主题:字典合并,字典解析式还能否胜任呢?
- int main()
- {
- printf("name is %s\n", name);
- printf("size of name: %lu\n", sizeof(name));
- printf("len of name: %lu\n", strlen(name));
+当然可以,具体示例代码如下:
- printf("gender is %s\n", gender);
- printf("size of gender: %lu\n", sizeof(gender));
- printf("len of gender: %lu\n", strlen(gender));
- return 0;
- }
+.. code:: python
-字符串操作
-~~~~~~~~~~
+ >>> profile = {"name": "xiaoming", "age": 27}
+ >>> ext_info = {"gender": "male"}
+ >>>
+ >>> {k:v for d in [profile, ext_info] for k,v in d.items()}
+ {'name': 'xiaoming', 'age': 27, 'gender': 'male'}
-- strcat(s1,s2) :string catenate,连接s2到s1末尾
-- strcpy(s1,s2) :string copy,复制字符串s2到s1
-- strlen(s1) :(string length),返回s1字符串的长度
-- strlwr(s1) :string lowercase,将s1的字符串的字母全部大写返回
-- strupr(s1) :string upercase,将s1的字符串的字母全部小写返回
-- strcmp(s1,s2) :string compare,如果 s1 和 s2 是相同的,则返回
- 0;如果 s1s2 则返回大于 0。
+7. Python 3.9 新特性
+--------------------
-字符的输入
-~~~~~~~~~~
+在 2 月份发布的 Python 3.9.04a
+版本中,新增了一个抓眼球的新操作符操作符: ``|``\ , PEP584
+将它称之为合并操作符(Union Operator),用它可以很直观地合并多个字典。
-使用 ``printf`` 和 ``scanf`` 函数
+.. code:: python
-.. code:: c
-
- # include
-
- int main(int argc, char const *argv[])
- {
- char name[10];
- printf("Enter your name: ");
- scanf("%s", name);
- printf("Your name is: %s \n", name);
- return 0;
- }
-
-使用 fgets 和 fputs 函数
-
-.. code:: c
-
- # include
+ >>> profile = {"name": "xiaoming", "age": 27}
+ >>> ext_info = {"gender": "male"}
+ >>>
+ >>> profile | ext_info
+ {'name': 'xiaoming', 'age': 27, 'gender': 'male'}
+ >>>
+ >>> ext_info | profile
+ {'gender': 'male', 'name': 'xiaoming', 'age': 27}
+ >>>
+ >>>
- int main(int argc, char const *argv[])
- {
- char name[10];
- printf("Enter your name: ");
- fgets(name, 10, stdin);
- printf("Your name is: ");
- fputs(name, stdout);
- return 0;
- }
+除了 ``|`` 操作符之外,还有另外一个操作符 ``|=``\ ,类似于原地更新。
-getchar() & putchar()
+.. code:: python
-指针相关的两个符号
-~~~~~~~~~~~~~~~~~~
+ >>> ext_info |= profile
+ >>> ext_info
+ {'gender': 'male', 'name': 'xiaoming', 'age': 27}
+ >>>
+ >>>
+ >>> profile |= ext_info
+ >>> profile
+ {'name': 'xiaoming', 'age': 27, 'gender': 'male'}
-记住 ``*`` 有3个用途:
+看到这里,有没有涨姿势了,学了这么久的 Python
+,没想到合并字典还有这么多的方法。本篇文章的主旨,并不在于让你全部掌握这
+7
+种合并字典的方法,实际在工作中,你只要选用一种最顺手的方式即可,但是在协同工作中,或者在阅读他人代码时,你不可避免地会碰到各式各样的写法,这时候你能下意识的知道这是在做合并字典的操作,那这篇文章就是有意义的。
-1. 乘号(Multiply): 2*3 就是6
-2. 声明指针(Pointer Statement): int a =5; int\*
- ptr=&a;就是声明变量a是5,把a的地址附到指针ptr上
-3. 解引用 (Dereference): \*ptr 单独拿出来就是找出
- ptr指针指向的值,按照第二点的说法就是5.
+--------------
-``&``\ 叫做取地址符号,一般指针只能接受一个内存地址而不能接受一个值
+|image1|
-.. code:: c
+.. |image0| image:: http://image.iswbm.com/20200602135014.png
+.. |image1| image:: http://image.iswbm.com/20200607174235.png
- // 如下是错误的,指针不能接受一个值
- int a =5; int* ptr=a;
-
- // 如下是正确的,a的地址给指针ptr
- int a =5; int* ptr=&a;
-
-可变参数的获取
-~~~~~~~~~~~~~~
-
-以下写了一个函数 ``get_sum``
-来求得输入的所有参数的和(除了第一个参数外,第一个参数表示,对后面几个可变参数求和,在示例中是作为结束条件存在)。
-
-.. code:: c
-
- #include
- #include
-
- int get_sum(int n, ...)
- {
- va_list arglist; // 定义一个va_list类型的字符指针,用来指向当前参数,后面取参必须通过这个指针进行
- va_start(arglist, n); // 初始化这个指针,让其指向可变参数里的第一个参数,这里的参数应该填写 ... 的那个参数
- double sum;
-
- for (int i = 1; i <= n; i++)
- {
- sum += va_arg(arglist, int);
- if (i >= 3)
- {
- break;
- }
- }
-
- va_end(arglist); // 养成好习惯,将这个指针关闭
- return sum;
- }
-
- int main(int argc, char const *argv[])
- {
- /* code */
- printf("The sum is: %d", get_sum(3,1,1,1));
- return 0;
- }
-
-.. figure:: http://image.python-online.cn/image-20200320125724880.png
- :alt: 关注公众号,获取最新干货!
-
- 关注公众号,获取最新干货!
diff --git a/source/c01/c01_27.md b/source/c01/c01_27.md
index f440996..e8b2220 100644
--- a/source/c01/c01_27.md
+++ b/source/c01/c01_27.md
@@ -1,643 +1,143 @@
-# 1.27 全面学习 Python 包:包的构建与分发
+# 1.27 Python 炫技操作:判断是否包含子串的七种方法
-> 首发于公众号:Python编程时光
+
+Python 语言里有许多(而且是越来越多)的高级特性,是 Python 发烧友们非常喜欢的。在这些人的眼里,能够写出那些一般开发者看不懂的高级特性,就是高手,就是大神。
+但你要知道,在团队合作里,炫技是大忌。
-## 1. 为什么需要对项目分发打包?
+为什么这么说呢?我说下自己的看法:
-平常我们习惯了使用 pip 来安装一些第三方模块,这个安装过程之所以简单,是因为模块开发者为我们默默地为我们做了所有繁杂的工作,而这个过程就是 `打包`。
+1. 越简洁的代码,越清晰的逻辑,就越不容易出错;
+2. 在团队合作中,你的代码不只有你在维护,降低别人的阅读/理解代码逻辑的成本是一个良好的品德
+3. 简单的代码,只会用到最基本的语法糖,复杂的高级特性,会有更多的依赖(如语言的版本)
-打包,就是将你的源代码进一步封装,并且将所有的项目部署工作都事先安排好,这样使用者拿到后即装即用,不用再操心如何部署的问题(如果你不想对照着一堆部署文档手工操作的话)。
+该篇是「**炫技系列**」的第三篇内容,在这个系列里,我将总结盘点一下,我所见过的那些炫技操作。在这里,如果你是 Python 发烧友,你可以学到一些写出超酷的代码书写技巧。同时,看了这些内容,对你在阅读别人的代码时,也许会有些帮助。
-不管你是在工作中,还是业余准备自己写一个可以上传到 PyPI 的项目,你都要学会如何打包你的项目。
+## 1. 使用 in 和 not in
-Python 发展了这么些年了,项目打包工具也已经很成熟了。他们都有哪些呢?
+`in` 和 `not in` 在 Python 中是很常用的关键字,我们将它们归类为 `成员运算符`。
-你可能听过 `disutils`、 `distutils` 、`distutils2`、`setuptools`等等,好像很熟悉,却又很陌生,他们都是什么关系呢?
-
-## 2. 包分发的始祖:distutils
-
-`distutils` 是 Python 的一个标准库,从命名上很容易看出它是一个分发(distribute)工具(utlis),它是 Python 官方开发的一个分发打包工具,所有后续的打包工具,全部都是基于它进行开发的。
-
-`distutils` 的精髓在于编写 setup.py,它是模块分发与安装的指导文件。
-
-那么如何编写 setup.py 呢?这里面的内容非常多,我会在后面进行详细的解析,请你耐心往下看。
-
-你有可能没写过 setup.py ,但你绝对使用过 setup.py 来做一些事情,比如下面这条命令,我们经常用它来进行模块的安装。
-
-```shell
-$ python setup.py install
-```
-
-这样的安装方法是通过源码安装,与之对应的是通过二进制软件包的安装,同样我也会在后面进行介绍。
-
-## 3. 分发工具升级:setuptools
-
-`setuptools` 是 distutils 增强版,不包括在标准库中。其扩展了很多功能,能够帮助开发者更好的创建和分发 Python 包。大部分 Python 用户都会使用更先进的 setuptools 模块。
-
- **distribute**,或许你在其他地方也见过它,这里也提一下。
-
-distribute 是 setuptools 有一个分支版本,分支的原因可能是有一部分开发者认为 setuptools 开发太慢了。但现在,distribute 又合并回了 setuptools 中。因此,我们可以认为它们是同一个东西。
-
-还有一个大包分发工具是 **distutils2**,其试图尝试充分利用distutils,detuptools 和 distribute 并成为 Python 标准库中的标准工具。但该计划并没有达到预期的目的,且已经是一个废弃的项目。
-
-因此,setuptools 是一个优秀的,可靠的 Python 包安装与分发工具。
-
-那么如何在一个干净的环境中安装 setuptools 呢?
-
-主要有两种方法:
-
-- 源码安装:在 https://pypi.org/project/setuptools/#files 中下载 zip 包 解压执行 `python setup.py install` 安装
-- 通过引导程序安装:下载引导程序,它可以用来下载或者更新最新版本的 setuptools
-
-```shell
-$ wget http://peak.telecommunity.com/dist/ez_setup.py
-
-# 安装
-$ python ez_setup.py
-
-# 更新,以下两种任选
-$ python ez_setup.py –U setuptools
-$ pip install -U setuptools
-```
-
-
-## 4. easy_install 使用指南
-
-当你安装完 setuptools 后,就拥有了一个叫做 `easy_install` 的第三方管理工具,这也是它区分于 distutils 的一大改进。
-
-这里简单介绍一下它的用法,虽然它已经用得非常少了。
-
-先是包的安装
-
-```shell
-# 通过包名,从PyPI寻找最新版本,自动下载、编译、安装
-$ easy_install pkg_name
-
-# 通过包名从指定下载页寻找链接来安装或升级包
-$ easy_install -f http://pythonpaste.org/package_index.html
-
-# 指定线上的包地址安装
-$ easy_install http://example.com/path/to/MyPackage-1.2.3.tgz
-
-# 从本地的 .egg 文件安装
-$ easy_install xxx.egg
-
-# 在安装时你可以添加额外的参数
-指定安装目录:--install-dir=DIR, -d DIR
-指定用户安装:--user
-```
-
-再者是包的升级
-
-```shell
-# 从 pypi 中搜索并升级包
-$ easy_install --upgrade pkg_name
-
-# 指定版本进行升级
-$ easy_install "SomePackage==2.0"
-```
-
-最后是包的删除
-
-```shell
-$ easy_install -m pkg_name
-```
-
-需要注意的是,这样的删除,仅是在 easy-install.pth 文件中删除,使其不能在 python 中使用 这个模块,但实际的包还在你的电脑中,若要删除彻底,需要你手动删除相关的 .egg 及 其他文件。
-
-
-
-默认情况下,easy_install 只会从 pypi 上下载相关软件包,由于这个源在国外,下载包的速度并不理想,使用过pip的朋友自然会想,easy_install 是否能指定源进行安装呢?
-
-答案是,可以的。
-
-编辑配置文件 `/root/.pydistutils.cfg`
-
-```ini
-[easy_install]
-index-url=http://mirrors.aliyun.com/pypi/simple/
-find-links=http://mirrors.aliyun.com/pypi/simple/
-```
-
-以上仅介绍了 easy_install 的一些常用的方法,想要了解更多,你可以点击官方文档:https://setuptools.readthedocs.io/en/latest/easy_install.html
-
-
-
-总结一句:setuptools 是官方提供的一个专业用于包分发的工具,若只从安装的角度来看,它的功能确实简单。它更大的意义是对包的分发很有用,定制化程序非常高,我们现在也还在用它进行版本包的发布。
-
-
-
-## 5. 源码包与二进制包什么区别?
-
-Python 包的分发可以分为两种:
-
-1. 以源码包的方式发布
-
-源码包安装的过程,是先解压,再编译,最后才安装,所以它是跨平台的,由于每次安装都要进行编译,相对二进包安装方式来说安装速度较慢。
-
-源码包的本质是一个压缩包,其常见的格式有:
-
-
-
-2. 以二进制包形式发布
-
-二进制包的安装过程省去了编译的过程,直接进行解压安装,所以安装速度较源码包来说更快。
-
-由于不同平台的编译出来的包无法通用,所以在发布时,需事先编译好多个平台的包。
-
-二进制包的常见格式有:
-
-
-
-## 6. eggs 与 wheels 有什么区别?
-
-Egg 格式是由 setuptools 在 2004 年引入,而 Wheel 格式是由 PEP427 在 2012 年定义。Wheel 的出现是为了替代 Egg,它的本质是一个zip包,其现在被认为是 Python 的二进制包的标准格式。
-
-以下是 Wheel 和 Egg 的主要区别:
-
-- Wheel 有一个官方的 PEP427 来定义,而 Egg 没有 PEP 定义
-- Wheel 是一种分发格式,即打包格式。而 Egg 既是一种分发格式,也是一种运行时安装的格式,并且是可以被直接 import
-- Wheel 文件不会包含 .pyc 文件
-- Wheel 使用和 PEP376 兼容的 .dist-info 目录,而 Egg 使用 .egg-info 目录
-- Wheel 有着更丰富的命名规则。
-- Wheel 是有版本的。每个 Wheel 文件都包含 wheel 规范的版本和打包的实现
-- Wheel 在内部被 sysconfig path type 管理,因此转向其他格式也更容易
-
-wheel 包可以通过 pip 来安装,只不过需要先安装 wheel 模块,然后再使用 pip 的命令。
-
-```shell
-$ pip install wheel
-$ pip wheel --wheel-dir=/local/wheels pkg
-```
-
-
-
-## 7. 超详细讲解 setup.py 的编写?
-
-打包分发最关键的一步是编写 `setup.py` 文件。
-
-以下是一个 setup.py 简单的使用示例
+使用这两个成员运算符,可以很让我们很直观清晰的判断一个对象是否在另一个对象中,示例如下:
```python
-from setuptools import setup, find_packages
-
-setup(
- name="mytest",
- version="1.0",
- author="wangbm",
- author_email="wongbingming@163.com",
- description="Learn to Pack Python Module -->公众号:Python编程时光",
-
- # 项目主页
- url="http://python-online.cn/",
-
- # 你要安装的包,通过 setuptools.find_packages 找到当前目录下有哪些包
- packages=find_packages()
-)
+>>> "llo" in "hello, python"
+True
+>>>
+>>> "lol" in "hello, python"
+False
```
-接下来,我将慢慢扩充这个setup函数,增加更多的参数,以便你能理解setup函数能做哪些事情。
-**程序分类信息**
-`classifiers` 参数说明包的分类信息。所有支持的分类列表见:https://pypi.org/pypi?%3Aaction=list_classifiers
+## 2. 使用 find 方法
-示例:
+使用 字符串 对象的 find 方法,如果有找到子串,就可以返回指定子串在字符串中的出现位置,如果没有找到,就返回 `-1`
```python
-from setuptools import setup, find_packages
-
-setup(
- classifiers = [
- # 发展时期,常见的如下
- # 3 - Alpha
- # 4 - Beta
- # 5 - Production/Stable
- 'Development Status :: 3 - Alpha',
-
- # 开发的目标用户
- 'Intended Audience :: Developers',
-
- # 属于什么类型
- 'Topic :: Software Development :: Build Tools',
-
- # 许可证信息
- 'License :: OSI Approved :: MIT License',
-
- # 目标 Python 版本
- 'Programming Language :: Python :: 2',
- 'Programming Language :: Python :: 2.7',
- 'Programming Language :: Python :: 3',
- 'Programming Language :: Python :: 3.3',
- 'Programming Language :: Python :: 3.4',
- 'Programming Language :: Python :: 3.5',
- ]
-)
+>>> "hello, python".find("llo") != -1
+True
+>>> "hello, python".find("lol") != -1
+False
+>>
```
-**关于文件的分发**
+## 3. 使用 index 方法
-```python
-from setuptools import setup, find_packages
-
-
-setup(
- name="mytest",
- version="1.0",
- author="wangbm",
- author_email="wongbingming@163.com",
- description="Learn to Pack Python Module",
- url="http://python-online.cn/",
- packages=find_packages(),
-
- # 安装过程中,需要安装的静态文件,如配置文件、service文件、图片等
- data_files=[
- ('', ['conf/*.conf']),
- ('/usr/lib/systemd/system/', ['bin/*.service']),
- ],
-
- # 希望被打包的文件
- package_data={
- '':['*.txt'],
- 'bandwidth_reporter':['*.txt']
- },
- # 不打包某些文件
- exclude_package_data={
- 'bandwidth_reporter':['*.txt']
- }
-)
-```
-
-除了以上的参数配置之外,还可以使用一个叫做 `MANIFEST.in` 的文件,来控制文件的分发。
-
-如下这是一个 `MANIFEST.in` 的样例:
-
-```
-include *.txt
-recursive-include examples *.txt *.py
-prune examples/sample?/build
-```
-
-这些配置,规定了如下几点
-
-- 所有根目录下的以 txt 为后缀名的文件,都会分发
-- 根目录下的 examples 目录 和 txt、py文件都会分发
-- 路径匹配上 examples/sample?/build 不会分发
-
-`MANIFEST.in` 需要放在和 setup.py 同级的顶级目录下,setuptools 会自动读取该文件。
-
-
-
-**关于依赖包下载安装**
+字符串对象有一个 index 方法,可以返回指定子串在该字符串中第一次出现的索引,如果没有找到会抛出异常,因此使用时需要注意捕获。
```python
-from setuptools import setup, find_packages
-
-
-setup(
- ...
-
- # 表明当前模块依赖哪些包,若环境中没有,则会从pypi中下载安装
- install_requires=['docutils>=0.3'],
-
- # setup.py 本身要依赖的包,这通常是为一些setuptools的插件准备的配置
- # 这里列出的包,不会自动安装。
- setup_requires=['pbr'],
-
- # 仅在测试时需要使用的依赖,在正常发布的代码中是没有用的。
- # 在执行python setup.py test时,可以自动安装这三个库,确保测试的正常运行。
- tests_require=[
- 'pytest>=3.3.1',
- 'pytest-cov>=2.5.1',
- ],
-
- # 用于安装setup_requires或tests_require里的软件包
- # 这些信息会写入egg的 metadata 信息中
- dependency_links=[
- "http://example2.com/p/foobar-1.0.tar.gz",
- ],
-
- # install_requires 在安装模块时会自动安装依赖包
- # 而 extras_require 不会,这里仅表示该模块会依赖这些包
- # 但是这些包通常不会使用到,只有当你深度使用模块时,才会用到,这里需要你手动安装
- extras_require={
- 'PDF': ["ReportLab>=1.2", "RXP"],
- 'reST': ["docutils>=0.3"],
- }
-)
+def is_in(full_str, sub_str):
+ try:
+ full_str.index(sub_str)
+ return True
+ except ValueError:
+ return False
+print(is_in("hello, python", "llo")) # True
+print(is_in("hello, python", "lol")) # False
```
-关于 `install_requires`, 有以下五种常用的表示方法:
-
-1. `'argparse'`,只包含包名。 这种形式只检查包的存在性,不检查版本。 方便,但不利于控制风险。
-2. `'setuptools==38.2.4'`,指定版本。 这种形式把风险降到了最低,确保了开发、测试与部署的版本一致,不会出现意外。 缺点是不利于更新,每次更新都需要改动代码。
-3. `'docutils >= 0.3'`,这是比较常用的形式。 当对某个库比较信任时,这种形式可以自动保持版本为最新。
-4. `'Django >= 1.11, != 1.11.1, <= 2'`,这是比较复杂的形式。 如这个例子,保证了Django的大版本在1.11和2之间,也即1.11.x;并且,排除了已知有问题的版本1.11.1(仅举例)。 对于一些大型、复杂的库,这种形式是最合适的。
-5. `'requests[security, socks] >= 2.18.4'`,这是包含了额外的可选依赖的形式。 正常安装requests会自动安装它的`install_requires`中指定的依赖,而不会安装`security`和`socks`这两组依赖。 这两组依赖是定义在它的`extras_require`中。 这种形式,用在深度使用某些库时。
-
-**关于安装环境的限制**
+## 4. 使用 count 方法
-有些库并不是在所以的 Python 版本中都适用的,若一个库安装在一个未兼容的 Python 环境中,理论上不应该在使用时才报错,而应该在安装过程就使其失败,提示禁止安装。
+利用和 index 这种曲线救国的思路,同样我们可以使用 count 的方法来判断。
-这样的功能,可以使用 `python_requires` 来实现。
+只要判断结果大于 0 就说明子串存在于字符串中。
```python
-setup(
- ...
- python_requires='>=2.7, <=3',
-)
-```
-
+def is_in(full_str, sub_str):
+ return full_str.count(sub_str) > 0
-
-**生成可执行文件的分发**
-
-```python
-from setuptools import setup, find_packages
-
-
-setup(
- name="mytest",
- version="1.0",
- author="wangbm",
- author_email="wongbingming@163.com",
- description="Learn to Pack Python Module",
- url="http://python-online.cn/",
- packages=find_packages(),
-
- # 用来支持自动生成脚本,安装后会自动生成 /usr/bin/foo 的可执行文件
- # 该文件入口指向 foo/main.py 的main 函数
- entry_points={
- 'console_scripts': [
- 'foo = foo.main:main'
- ]
- },
-
- # 将 bin/foo.sh 和 bar.py 脚本,生成到系统 PATH中
- # 执行 python setup.py install 后
- # 会生成 如 /usr/bin/foo.sh 和 如 /usr/bin/bar.py
- scripts=['bin/foo.sh', 'bar.py']
-)
+print(is_in("hello, python", "llo")) # True
+print(is_in("hello, python", "lol")) # False
```
-上面的 scripts 里有的脚本中有 `sh` 和 `py` 后缀,那么安装后,setuptools 会原封不动的移动到 /usr/bin 中,并添加可执行权限。
-若你想对这些文件再作一些更改,比如去掉多余的后缀,可以这样做
-
-```python
-from setuptools.command.install_scripts import install_scripts
-
-class InstallScripts(install_scripts):
-
- def run(self):
- setuptools.command.install_scripts.install_scripts.run(self)
-
- # Rename some script files
- for script in self.get_outputs():
- if basename.endswith(".py") or basename.endswith(".sh"):
- dest = script[:-3]
- else:
- continue
- print("moving %s to %s" % (script, dest))
- shutil.move(script, dest)
-
-setup(
- ...
- scripts=['bin/foo.sh', 'bar.py'],
-
- cmdclass={
- "install_scripts": InstallScripts
- }
-)
-```
+## 5. 通过魔法方法
+在第一种方法中,我们使用 in 和 not in 判断一个子串是否存在于另一个字符中,实际上当你使用 in 和 not in 时,Python 解释器会先去检查该对象是否有 `__contains__` 魔法方法。
-**ext_modules**
+若有就执行它,若没有,Python 就自动会迭代整个序列,只要找到了需要的一项就返回 True 。
-`ext_modules` 参数用于构建 C 和 C++ 扩展扩展包。其是 Extension 实例的列表,每一个 Extension 实例描述了一个独立的扩展模块,扩展模块可以设置扩展包名,头文件、源文件、链接库及其路径、宏定义和编辑参数等。如:
+示例如下;
```python
-setup(
- # other arguments here...
- ext_modules=[
- Extension('foo',
- glob(path.join(here, 'src', '*.c')),
- libraries = [ 'rt' ],
- include_dirs=[numpy.get_include()])
- ]
-)
+>>> "hello, python".__contains__("llo")
+True
+>>>
+>>> "hello, python".__contains__("lol")
+False
+>>>
```
-详细了解可参考:https://docs.python.org/3.6/distutils/setupscript.html#preprocessor-options
-
-
-
-setup.py 的参数非常多,能够不借助文档写好一个setup.py好像没那么简单。为了备忘,我整理了 setup 函数常用的一些参数:
-
-
-
-更多参数可见:https://setuptools.readthedocs.io/en/latest/setuptools.html
-
-## 8. 打包辅助神器PBR 是什么?
+这个用法与使用 in 和 not in 没有区别,但不排除有人会特意写成这样来增加代码的理解难度。
-`pbr` 是 setuptools 的辅助工具,最初是为 OpenStack 开发(https://launchpad.net/pbr),基于`d2to1`。
+## 6. 借助 operator
+operator模块是python中内置的操作符函数接口,它定义了一些算术和比较内置操作的函数。operator模块是用c实现的,所以执行速度比 python 代码快。
+在 operator 中有一个方法 `contains` 可以很方便地判断子串是否在字符串中。
-`pbr` 会读取和过滤setup.cfg中的数据,然后将解析后的数据提供给 `setup.py` 作为参数。包含如下功能:
-
-1. 从git中获取Version、AUTHORS and ChangeLog信息
-2. Sphinx Autodoc。pbr 会扫描project,找到所有模块,生成stub files
-3. Requirements。pbr会读取requirements.txt,生成setup函数需要的`install_requires/tests_require/dependency_links`
-
-这里需要注意,在 `requirements.txt` 文件的头部可以使用:`--index https://pypi.python.org/simple/`,这一行把一个抽象的依赖声明如 requests==1.2.0 转变为一个具体的依赖声明 requests 1.2.0 from pypi.python.org/simple/
-
-4. long_description。从README.rst, README.txt or README file中生成`long_description`参数
-
-
-
-使用pbr很简单:
-
-```
-from setuptools import setup
-
-setup(
- setup_requires=['pbr'],
- pbr=True,
-)
-
-```
-
-使用pbr时,setup.cfg中有一些配置。在[files]中,有三个key:
-`packages`:指定需要包含的包,行为类似于setuptools.find_packages
-`namespace_packages`:指定namespace packages
-`data_files`: 指定目的目录和源文件路径,一个示例:
-
-```
-[files]
-data_files =
- etc/pbr = etc/pbr/*
- etc/neutron =
- etc/api-paste.ini
- etc/dhcp-agent.ini
- etc/init.d = neutron.init
-
-```
-
-`[entry_points]` 段跟 setuptools 的方式相同。
-
-
-
-到此,我讲了三种编写使用 setup.py 的方法
-
-- 使用命令行参数指定,一个一个将参数传递进去(极不推荐)
-- 在 setup.py 中的setup函数中指定(推荐使用)
-- 使用 pbr ,在 setup.cfg 中指定(易于管理,更推荐)
-
-## 9. 如何使用 setup.py 构建包
-
-1、构建源码发布包。
-
-用于发布一个 Python 模块或项目,将源码打包成 tar.gz (用于 Linux 环境中)或者 zip 压缩包(用于 Windows 环境中)
-
-```shell
-$ python setup.py sdist
-```
-
-那这种包如何安装呢?
-
-答案是,使用下一节即将介绍的 `setuptools` 中提供的 `easy_install` 工具。
-
-```shell
-$ easy_install xxx.tar.gz
-```
-
-使用 sdist 将根据当前平台创建默认格式的存档。在类 Unix 平台上,将创建后缀后为 `.tar.gz` 的 gzip 压缩的tar文件分发包,而在Windows上为 ZIP 文件。
-
-当然,你也可以通过指定你要的发布包格式来打破这个默认行为
-
-```shell
-$ python setup.py sdist --formats=gztar,zip
-```
-
-你可以指定的格式有哪些呢?
-
-创建一个压缩的tarball和一个zip文件。可用格式为:
-
-
-
-对以上的格式,有几点需要注意一下:
-
-- 在版本3.5中才添加了对 `xztar` 格式的支持
-- zip 格式需要你事先已安装相应的模块:zip程序或zipfile模块(已成为Python的标准库)
-- ztar 格式正在弃用,请尽量不要使用
-
-另外,如果您希望归档文件的所有文件归root拥有,可以这样指定
-
-```
-python setup.py sdist --owner=root --group=root
-```
-
-
-
-2、构建二进制分发包。
-
-在windows中我们习惯了双击 exe 进行软件的安装,Python 模块的安装也同样支持 打包成 exe 这样的二进制软件包。
-
-```shell
-$ python setup.py bdist_wininst
-```
-
-而在 Linux 中,大家也习惯了使用 rpm 来安装包,对此你可以使用这条命令实现 rpm 包的构建
-
-```shell
-$ python setup.py bdist_rpm
-```
-
-若你喜欢使用 easy_install 或者 pip 来安装离线包。你可以将其打包成 egg 包
-
-```shell
-$ python setup.py bdist_egg
-```
-
-若你的项目,需要安装多个平台下,既有 Windows 也有 Linux,按照上面的方法,多种格式我们要执行多次命令,为了方便,你可以一步到位,执行如下这条命令,即可生成多个格式的进制包
-
-```shell
-$ python setup.py bdist
-```
-
-
-
-## 10. 如何使用 setup.py 安装包
-
-正常情况下,我们都是通过以上构建的源码包或者二进制包进行模块的安装。
-
-但在编写 setup.py 的过程中,可能不能一步到位,需要多次调试,这时候如何测试自己写的 setup.py 文件是可用的呢?
-
-这时候你可以使用这条命令,它会将你的模块安装至系统全局环境中
-
-```shell
-$ python setup.py install
-```
-
-如若你的项目还处于开发阶段,频繁的安装模块,也是一个麻烦事。
-
-这时候你可以使用这条命令安装,该方法不会真正的安装包,而是在系统环境中创建一个软链接指向包实际所在目录。这边在修改包之后不用再安装就能生效,便于调试。
-
-```shell
-$ python setup.py develop
+```python
+>>> import operator
+>>>
+>>> operator.contains("hello, python", "llo")
+True
+>>> operator.contains("hello, python", "lol")
+False
+>>>
```
-## 11. 如何发布包到 PyPi?
+## 7. 使用正则匹配
-通过上面的学习,你一定已经学会了如何打包自己的项目,若你觉得自己开发的模块非常不错,想要 share 给其他人使用,你可以将其上传到 PyPi (Python Package Index)上,它是 Python 官方维护的第三方包仓库,用于统一存储和管理开发者发布的 Python 包。
+说到查找功能,那正则绝对可以说是专业的工具,多复杂的查找规则,都能满足你。
+对于判断字符串是否存在于另一个字符串中的这个需求,使用正则简直就是大材小用。
+```python
+import re
-如果要发布自己的包,需要先到 pypi 上注册账号。然后创建 `~/.pypirc` 文件,此文件中配置 PyPI 访问地址和账号。如的.pypirc文件内容请根据自己的账号来修改。
-
-典型的 .pypirc 文件
-
-```ini
-[distutils]
-index-servers = pypi
-
-[pypi]
-username:xxx
-password:xxx
-```
-
-然后使用这条命令进行信息注册,完成后,你可以在 PyPi 上看到项目信息。
-
-```shell
-$ python setup.py register
-```
-
-注册完了后,你还要上传源码包,别人才使用下载安装
+def is_in(full_str, sub_str):
+ if re.findall(sub_str, full_str):
+ return True
+ else:
+ return False
-```shell
-$ python setup.py upload
+print(is_in("hello, python", "llo")) # True
+print(is_in("hello, python", "lol")) # False
```
-或者也可以使用 `twine` 工具注册上传,它是一个专门用于与 pypi 进行交互的工具,详情可以参考官网:https://www.ctolib.com/twine.html,这里不详细讲了。
-
-
-## 参考文章
-- http://blog.konghy.cn/2018/04/29/setup-dot-py/
-- https://note.qidong.name/2018/01/python-setup-requires/
+---
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/source/c01/c01_27.rst b/source/c01/c01_27.rst
index 3e69eb0..f65a018 100644
--- a/source/c01/c01_27.rst
+++ b/source/c01/c01_27.rst
@@ -1,695 +1,154 @@
-1.27 全面学习 Python 包:包的构建与分发
-=======================================
-
- 首发于公众号:Python编程时光
-
-1. 为什么需要对项目分发打包?
------------------------------
-
-平常我们习惯了使用 pip
-来安装一些第三方模块,这个安装过程之所以简单,是因为模块开发者为我们默默地为我们做了所有繁杂的工作,而这个过程就是
-``打包``\ 。
-
-打包,就是将你的源代码进一步封装,并且将所有的项目部署工作都事先安排好,这样使用者拿到后即装即用,不用再操心如何部署的问题(如果你不想对照着一堆部署文档手工操作的话)。
-
-不管你是在工作中,还是业余准备自己写一个可以上传到 PyPI
-的项目,你都要学会如何打包你的项目。
-
-Python 发展了这么些年了,项目打包工具也已经很成熟了。他们都有哪些呢?
-
-你可能听过 ``disutils``\ 、 ``distutils``
-、\ ``distutils2``\ 、\ ``setuptools``\ 等等,好像很熟悉,却又很陌生,他们都是什么关系呢?
-
-2. 包分发的始祖:distutils
---------------------------
-
-``distutils`` 是 Python
-的一个标准库,从命名上很容易看出它是一个分发(distribute)工具(utlis),它是
-Python
-官方开发的一个分发打包工具,所有后续的打包工具,全部都是基于它进行开发的。
-
-``distutils`` 的精髓在于编写 setup.py,它是模块分发与安装的指导文件。
-
-那么如何编写 setup.py
-呢?这里面的内容非常多,我会在后面进行详细的解析,请你耐心往下看。
-
-你有可能没写过 setup.py ,但你绝对使用过 setup.py
-来做一些事情,比如下面这条命令,我们经常用它来进行模块的安装。
-
-.. code:: shell
-
- $ python setup.py install
-
-这样的安装方法是通过源码安装,与之对应的是通过二进制软件包的安装,同样我也会在后面进行介绍。
-
-3. 分发工具升级:setuptools
----------------------------
-
-``setuptools`` 是 distutils
-增强版,不包括在标准库中。其扩展了很多功能,能够帮助开发者更好的创建和分发
-Python 包。大部分 Python 用户都会使用更先进的 setuptools 模块。
-
-**distribute**\ ,或许你在其他地方也见过它,这里也提一下。
-
-distribute 是 setuptools
-有一个分支版本,分支的原因可能是有一部分开发者认为 setuptools
-开发太慢了。但现在,distribute 又合并回了 setuptools
-中。因此,我们可以认为它们是同一个东西。
-
-还有一个大包分发工具是
-**distutils2**\ ,其试图尝试充分利用distutils,detuptools 和 distribute
-并成为 Python
-标准库中的标准工具。但该计划并没有达到预期的目的,且已经是一个废弃的项目。
-
-因此,setuptools 是一个优秀的,可靠的 Python 包安装与分发工具。
-
-那么如何在一个干净的环境中安装 setuptools 呢?
-
-主要有两种方法:
-
-- 源码安装:在 https://pypi.org/project/setuptools/#files 中下载 zip 包
- 解压执行 ``python setup.py install`` 安装
-- 通过引导程序安装:下载引导程序,它可以用来下载或者更新最新版本的
- setuptools
-
-.. code:: shell
-
- $ wget http://peak.telecommunity.com/dist/ez_setup.py
-
- # 安装
- $ python ez_setup.py
-
- # 更新,以下两种任选
- $ python ez_setup.py –U setuptools
- $ pip install -U setuptools
-
-4. easy_install 使用指南
-------------------------
-
-当你安装完 setuptools 后,就拥有了一个叫做 ``easy_install``
-的第三方管理工具,这也是它区分于 distutils 的一大改进。
-
-这里简单介绍一下它的用法,虽然它已经用得非常少了。
-
-先是包的安装
-
-.. code:: shell
-
- # 通过包名,从PyPI寻找最新版本,自动下载、编译、安装
- $ easy_install pkg_name
-
- # 通过包名从指定下载页寻找链接来安装或升级包
- $ easy_install -f http://pythonpaste.org/package_index.html
-
- # 指定线上的包地址安装
- $ easy_install http://example.com/path/to/MyPackage-1.2.3.tgz
-
- # 从本地的 .egg 文件安装
- $ easy_install xxx.egg
-
- # 在安装时你可以添加额外的参数
- 指定安装目录:--install-dir=DIR, -d DIR
- 指定用户安装:--user
-
-再者是包的升级
-
-.. code:: shell
-
- # 从 pypi 中搜索并升级包
- $ easy_install --upgrade pkg_name
-
- # 指定版本进行升级
- $ easy_install "SomePackage==2.0"
-
-最后是包的删除
-
-.. code:: shell
-
- $ easy_install -m pkg_name
-
-需要注意的是,这样的删除,仅是在 easy-install.pth 文件中删除,使其不能在
-python 中使用
-这个模块,但实际的包还在你的电脑中,若要删除彻底,需要你手动删除相关的
-.egg 及 其他文件。
-
-默认情况下,easy_install 只会从 pypi
-上下载相关软件包,由于这个源在国外,下载包的速度并不理想,使用过pip的朋友自然会想,easy_install
-是否能指定源进行安装呢?
-
-答案是,可以的。
-
-编辑配置文件 ``/root/.pydistutils.cfg``
-
-.. code:: ini
-
- [easy_install]
- index-url=http://mirrors.aliyun.com/pypi/simple/
- find-links=http://mirrors.aliyun.com/pypi/simple/
-
-以上仅介绍了 easy_install
-的一些常用的方法,想要了解更多,你可以点击官方文档:https://setuptools.readthedocs.io/en/latest/easy_install.html
-
-总结一句:setuptools
-是官方提供的一个专业用于包分发的工具,若只从安装的角度来看,它的功能确实简单。它更大的意义是对包的分发很有用,定制化程序非常高,我们现在也还在用它进行版本包的发布。
-
-5. 源码包与二进制包什么区别?
------------------------------
-
-Python 包的分发可以分为两种:
-
-1. 以源码包的方式发布
-
-源码包安装的过程,是先解压,再编译,最后才安装,所以它是跨平台的,由于每次安装都要进行编译,相对二进包安装方式来说安装速度较慢。
-
-源码包的本质是一个压缩包,其常见的格式有:
+1.27 Python 炫技操作:判断是否包含子串的七种方法
+================================================
|image0|
-2. 以二进制包形式发布
-
-二进制包的安装过程省去了编译的过程,直接进行解压安装,所以安装速度较源码包来说更快。
-
-由于不同平台的编译出来的包无法通用,所以在发布时,需事先编译好多个平台的包。
-
-二进制包的常见格式有:
-
-|image1|
-
-6. eggs 与 wheels 有什么区别?
-------------------------------
-
-Egg 格式是由 setuptools 在 2004 年引入,而 Wheel 格式是由 PEP427 在 2012
-年定义。Wheel 的出现是为了替代 Egg,它的本质是一个zip包,其现在被认为是
-Python 的二进制包的标准格式。
+Python 语言里有许多(而且是越来越多)的高级特性,是 Python
+发烧友们非常喜欢的。在这些人的眼里,能够写出那些一般开发者看不懂的高级特性,就是高手,就是大神。
-以下是 Wheel 和 Egg 的主要区别:
+但你要知道,在团队合作里,炫技是大忌。
-- Wheel 有一个官方的 PEP427 来定义,而 Egg 没有 PEP 定义
-- Wheel 是一种分发格式,即打包格式。而 Egg
- 既是一种分发格式,也是一种运行时安装的格式,并且是可以被直接 import
-- Wheel 文件不会包含 .pyc 文件
-- Wheel 使用和 PEP376 兼容的 .dist-info 目录,而 Egg 使用 .egg-info
- 目录
-- Wheel 有着更丰富的命名规则。
-- Wheel 是有版本的。每个 Wheel 文件都包含 wheel 规范的版本和打包的实现
-- Wheel 在内部被 sysconfig path type 管理,因此转向其他格式也更容易
+为什么这么说呢?我说下自己的看法:
-wheel 包可以通过 pip 来安装,只不过需要先安装 wheel 模块,然后再使用 pip
-的命令。
+1. 越简洁的代码,越清晰的逻辑,就越不容易出错;
+2. 在团队合作中,你的代码不只有你在维护,降低别人的阅读/理解代码逻辑的成本是一个良好的品德
+3. 简单的代码,只会用到最基本的语法糖,复杂的高级特性,会有更多的依赖(如语言的版本)
-.. code:: shell
-
- $ pip install wheel
- $ pip wheel --wheel-dir=/local/wheels pkg
+该篇是「\ **炫技系列**\ 」的第三篇内容,在这个系列里,我将总结盘点一下,我所见过的那些炫技操作。在这里,如果你是
+Python
+发烧友,你可以学到一些写出超酷的代码书写技巧。同时,看了这些内容,对你在阅读别人的代码时,也许会有些帮助。
-7. 超详细讲解 setup.py 的编写?
--------------------------------
+1. 使用 in 和 not in
+--------------------
-打包分发最关键的一步是编写 ``setup.py`` 文件。
+``in`` 和 ``not in`` 在 Python 中是很常用的关键字,我们将它们归类为
+``成员运算符``\ 。
-以下是一个 setup.py 简单的使用示例
+使用这两个成员运算符,可以很让我们很直观清晰的判断一个对象是否在另一个对象中,示例如下:
.. code:: python
- from setuptools import setup, find_packages
+ >>> "llo" in "hello, python"
+ True
+ >>>
+ >>> "lol" in "hello, python"
+ False
- setup(
- name="mytest",
- version="1.0",
- author="wangbm",
- author_email="wongbingming@163.com",
- description="Learn to Pack Python Module -->公众号:Python编程时光",
-
- # 项目主页
- url="http://python-online.cn/",
-
- # 你要安装的包,通过 setuptools.find_packages 找到当前目录下有哪些包
- packages=find_packages()
- )
+2. 使用 find 方法
+-----------------
-接下来,我将慢慢扩充这个setup函数,增加更多的参数,以便你能理解setup函数能做哪些事情。
-
-**程序分类信息**
-
-``classifiers``
-参数说明包的分类信息。所有支持的分类列表见:https://pypi.org/pypi?%3Aaction=list_classifiers
-
-示例:
+使用 字符串 对象的 find
+方法,如果有找到子串,就可以返回指定子串在字符串中的出现位置,如果没有找到,就返回
+``-1``
.. code:: python
- from setuptools import setup, find_packages
-
- setup(
- classifiers = [
- # 发展时期,常见的如下
- # 3 - Alpha
- # 4 - Beta
- # 5 - Production/Stable
- 'Development Status :: 3 - Alpha',
-
- # 开发的目标用户
- 'Intended Audience :: Developers',
-
- # 属于什么类型
- 'Topic :: Software Development :: Build Tools',
-
- # 许可证信息
- 'License :: OSI Approved :: MIT License',
-
- # 目标 Python 版本
- 'Programming Language :: Python :: 2',
- 'Programming Language :: Python :: 2.7',
- 'Programming Language :: Python :: 3',
- 'Programming Language :: Python :: 3.3',
- 'Programming Language :: Python :: 3.4',
- 'Programming Language :: Python :: 3.5',
- ]
- )
-
-**关于文件的分发**
+ >>> "hello, python".find("llo") != -1
+ True
+ >>> "hello, python".find("lol") != -1
+ False
+ >>
-.. code:: python
+3. 使用 index 方法
+------------------
- from setuptools import setup, find_packages
-
-
- setup(
- name="mytest",
- version="1.0",
- author="wangbm",
- author_email="wongbingming@163.com",
- description="Learn to Pack Python Module",
- url="http://python-online.cn/",
- packages=find_packages(),
-
- # 安装过程中,需要安装的静态文件,如配置文件、service文件、图片等
- data_files=[
- ('', ['conf/*.conf']),
- ('/usr/lib/systemd/system/', ['bin/*.service']),
- ],
-
- # 希望被打包的文件
- package_data={
- '':['*.txt'],
- 'bandwidth_reporter':['*.txt']
- },
- # 不打包某些文件
- exclude_package_data={
- 'bandwidth_reporter':['*.txt']
- }
- )
-
-除了以上的参数配置之外,还可以使用一个叫做 ``MANIFEST.in``
-的文件,来控制文件的分发。
-
-如下这是一个 ``MANIFEST.in`` 的样例:
-
-::
-
- include *.txt
- recursive-include examples *.txt *.py
- prune examples/sample?/build
-
-这些配置,规定了如下几点
-
-- 所有根目录下的以 txt 为后缀名的文件,都会分发
-- 根目录下的 examples 目录 和 txt、py文件都会分发
-- 路径匹配上 examples/sample?/build 不会分发
-
-``MANIFEST.in`` 需要放在和 setup.py 同级的顶级目录下,setuptools
-会自动读取该文件。
-
-**关于依赖包下载安装**
+字符串对象有一个 index
+方法,可以返回指定子串在该字符串中第一次出现的索引,如果没有找到会抛出异常,因此使用时需要注意捕获。
.. code:: python
- from setuptools import setup, find_packages
-
-
- setup(
- ...
-
- # 表明当前模块依赖哪些包,若环境中没有,则会从pypi中下载安装
- install_requires=['docutils>=0.3'],
-
- # setup.py 本身要依赖的包,这通常是为一些setuptools的插件准备的配置
- # 这里列出的包,不会自动安装。
- setup_requires=['pbr'],
-
- # 仅在测试时需要使用的依赖,在正常发布的代码中是没有用的。
- # 在执行python setup.py test时,可以自动安装这三个库,确保测试的正常运行。
- tests_require=[
- 'pytest>=3.3.1',
- 'pytest-cov>=2.5.1',
- ],
-
- # 用于安装setup_requires或tests_require里的软件包
- # 这些信息会写入egg的 metadata 信息中
- dependency_links=[
- "http://example2.com/p/foobar-1.0.tar.gz",
- ],
-
- # install_requires 在安装模块时会自动安装依赖包
- # 而 extras_require 不会,这里仅表示该模块会依赖这些包
- # 但是这些包通常不会使用到,只有当你深度使用模块时,才会用到,这里需要你手动安装
- extras_require={
- 'PDF': ["ReportLab>=1.2", "RXP"],
- 'reST': ["docutils>=0.3"],
- }
- )
-
-关于 ``install_requires``\ , 有以下五种常用的表示方法:
-
-1. ``'argparse'``\ ,只包含包名。 这种形式只检查包的存在性,不检查版本。
- 方便,但不利于控制风险。
-2. ``'setuptools==38.2.4'``\ ,指定版本。
- 这种形式把风险降到了最低,确保了开发、测试与部署的版本一致,不会出现意外。
- 缺点是不利于更新,每次更新都需要改动代码。
-3. ``'docutils >= 0.3'``\ ,这是比较常用的形式。
- 当对某个库比较信任时,这种形式可以自动保持版本为最新。
-4. ``'Django >= 1.11, != 1.11.1, <= 2'``\ ,这是比较复杂的形式。
- 如这个例子,保证了Django的大版本在1.11和2之间,也即1.11.x;并且,排除了已知有问题的版本1.11.1(仅举例)。
- 对于一些大型、复杂的库,这种形式是最合适的。
-5. ``'requests[security, socks] >= 2.18.4'``\ ,这是包含了额外的可选依赖的形式。
- 正常安装requests会自动安装它的\ ``install_requires``\ 中指定的依赖,而不会安装\ ``security``\ 和\ ``socks``\ 这两组依赖。
- 这两组依赖是定义在它的\ ``extras_require``\ 中。
- 这种形式,用在深度使用某些库时。
-
-**关于安装环境的限制**
-
-有些库并不是在所以的 Python 版本中都适用的,若一个库安装在一个未兼容的
-Python
-环境中,理论上不应该在使用时才报错,而应该在安装过程就使其失败,提示禁止安装。
-
-这样的功能,可以使用 ``python_requires`` 来实现。
-
-.. code:: python
+ def is_in(full_str, sub_str):
+ try:
+ full_str.index(sub_str)
+ return True
+ except ValueError:
+ return False
- setup(
- ...
- python_requires='>=2.7, <=3',
- )
+ print(is_in("hello, python", "llo")) # True
+ print(is_in("hello, python", "lol")) # False
-**生成可执行文件的分发**
+4. 使用 count 方法
+------------------
-.. code:: python
+利用和 index 这种曲线救国的思路,同样我们可以使用 count 的方法来判断。
- from setuptools import setup, find_packages
-
-
- setup(
- name="mytest",
- version="1.0",
- author="wangbm",
- author_email="wongbingming@163.com",
- description="Learn to Pack Python Module",
- url="http://python-online.cn/",
- packages=find_packages(),
-
- # 用来支持自动生成脚本,安装后会自动生成 /usr/bin/foo 的可执行文件
- # 该文件入口指向 foo/main.py 的main 函数
- entry_points={
- 'console_scripts': [
- 'foo = foo.main:main'
- ]
- },
-
- # 将 bin/foo.sh 和 bar.py 脚本,生成到系统 PATH中
- # 执行 python setup.py install 后
- # 会生成 如 /usr/bin/foo.sh 和 如 /usr/bin/bar.py
- scripts=['bin/foo.sh', 'bar.py']
- )
-
-上面的 scripts 里有的脚本中有 ``sh`` 和 ``py``
-后缀,那么安装后,setuptools 会原封不动的移动到 /usr/bin
-中,并添加可执行权限。
-
-若你想对这些文件再作一些更改,比如去掉多余的后缀,可以这样做
+只要判断结果大于 0 就说明子串存在于字符串中。
.. code:: python
- from setuptools.command.install_scripts import install_scripts
-
- class InstallScripts(install_scripts):
+ def is_in(full_str, sub_str):
+ return full_str.count(sub_str) > 0
- def run(self):
- setuptools.command.install_scripts.install_scripts.run(self)
+ print(is_in("hello, python", "llo")) # True
+ print(is_in("hello, python", "lol")) # False
- # Rename some script files
- for script in self.get_outputs():
- if basename.endswith(".py") or basename.endswith(".sh"):
- dest = script[:-3]
- else:
- continue
- print("moving %s to %s" % (script, dest))
- shutil.move(script, dest)
+5. 通过魔法方法
+---------------
- setup(
- ...
- scripts=['bin/foo.sh', 'bar.py'],
-
- cmdclass={
- "install_scripts": InstallScripts
- }
- )
+在第一种方法中,我们使用 in 和 not in
+判断一个子串是否存在于另一个字符中,实际上当你使用 in 和 not in
+时,Python 解释器会先去检查该对象是否有 ``__contains__`` 魔法方法。
-**ext_modules**
+若有就执行它,若没有,Python
+就自动会迭代整个序列,只要找到了需要的一项就返回 True 。
-``ext_modules`` 参数用于构建 C 和 C++ 扩展扩展包。其是 Extension
-实例的列表,每一个 Extension
-实例描述了一个独立的扩展模块,扩展模块可以设置扩展包名,头文件、源文件、链接库及其路径、宏定义和编辑参数等。如:
+示例如下;
.. code:: python
- setup(
- # other arguments here...
- ext_modules=[
- Extension('foo',
- glob(path.join(here, 'src', '*.c')),
- libraries = [ 'rt' ],
- include_dirs=[numpy.get_include()])
- ]
- )
-
-详细了解可参考:https://docs.python.org/3.6/distutils/setupscript.html#preprocessor-options
-
-setup.py
-的参数非常多,能够不借助文档写好一个setup.py好像没那么简单。为了备忘,我整理了
-setup 函数常用的一些参数:
-
-|image2|
-
-更多参数可见:https://setuptools.readthedocs.io/en/latest/setuptools.html
-
-8. 打包辅助神器PBR 是什么?
----------------------------
-
-``pbr`` 是 setuptools 的辅助工具,最初是为 OpenStack
-开发(https://launchpad.net/pbr),基于\ ``d2to1``\ 。
-
-``pbr`` 会读取和过滤setup.cfg中的数据,然后将解析后的数据提供给
-``setup.py`` 作为参数。包含如下功能:
-
-1. 从git中获取Version、AUTHORS and ChangeLog信息
-2. Sphinx Autodoc。pbr 会扫描project,找到所有模块,生成stub files
-3. Requirements。pbr会读取requirements.txt,生成setup函数需要的\ ``install_requires/tests_require/dependency_links``
-
-这里需要注意,在 ``requirements.txt``
-文件的头部可以使用:\ ``--index https://pypi.python.org/simple/``\ ,这一行把一个抽象的依赖声明如
-requests==1.2.0 转变为一个具体的依赖声明 requests 1.2.0 from
-pypi.python.org/simple/
-
-4. long_description。从README.rst, README.txt or README
- file中生成\ ``long_description``\ 参数
-
-使用pbr很简单:
-
-::
-
- from setuptools import setup
-
- setup(
- setup_requires=['pbr'],
- pbr=True,
- )
-
-使用pbr时,setup.cfg中有一些配置。在[files]中,有三个key:
-``packages``:指定需要包含的包,行为类似于setuptools.find_packages
-``namespace_packages``:指定namespace packages ``data_files``:
-指定目的目录和源文件路径,一个示例:
-
-::
-
- [files]
- data_files =
- etc/pbr = etc/pbr/*
- etc/neutron =
- etc/api-paste.ini
- etc/dhcp-agent.ini
- etc/init.d = neutron.init
-
-``[entry_points]`` 段跟 setuptools 的方式相同。
-
-到此,我讲了三种编写使用 setup.py 的方法
-
-- 使用命令行参数指定,一个一个将参数传递进去(极不推荐)
-- 在 setup.py 中的setup函数中指定(推荐使用)
-- 使用 pbr ,在 setup.cfg 中指定(易于管理,更推荐)
-
-9. 如何使用 setup.py 构建包
----------------------------
-
-1、构建源码发布包。
-
-用于发布一个 Python 模块或项目,将源码打包成 tar.gz (用于 Linux
-环境中)或者 zip 压缩包(用于 Windows 环境中)
-
-.. code:: shell
-
- $ python setup.py sdist
-
-那这种包如何安装呢?
-
-答案是,使用下一节即将介绍的 ``setuptools`` 中提供的 ``easy_install``
-工具。
-
-.. code:: shell
-
- $ easy_install xxx.tar.gz
-
-使用 sdist 将根据当前平台创建默认格式的存档。在类 Unix
-平台上,将创建后缀后为 ``.tar.gz`` 的 gzip
-压缩的tar文件分发包,而在Windows上为 ZIP 文件。
-
-当然,你也可以通过指定你要的发布包格式来打破这个默认行为
-
-.. code:: shell
+ >>> "hello, python".__contains__("llo")
+ True
+ >>>
+ >>> "hello, python".__contains__("lol")
+ False
+ >>>
- $ python setup.py sdist --formats=gztar,zip
+这个用法与使用 in 和 not in
+没有区别,但不排除有人会特意写成这样来增加代码的理解难度。
-你可以指定的格式有哪些呢?
+6. 借助 operator
+----------------
-创建一个压缩的tarball和一个zip文件。可用格式为:
+operator模块是python中内置的操作符函数接口,它定义了一些算术和比较内置操作的函数。operator模块是用c实现的,所以执行速度比
+python 代码快。
-|image3|
+在 operator 中有一个方法 ``contains``
+可以很方便地判断子串是否在字符串中。
-对以上的格式,有几点需要注意一下:
-
-- 在版本3.5中才添加了对 ``xztar`` 格式的支持
-- zip
- 格式需要你事先已安装相应的模块:zip程序或zipfile模块(已成为Python的标准库)
-- ztar 格式正在弃用,请尽量不要使用
-
-另外,如果您希望归档文件的所有文件归root拥有,可以这样指定
-
-::
-
- python setup.py sdist --owner=root --group=root
-
-2、构建二进制分发包。
-
-在windows中我们习惯了双击 exe 进行软件的安装,Python
-模块的安装也同样支持 打包成 exe 这样的二进制软件包。
-
-.. code:: shell
-
- $ python setup.py bdist_wininst
-
-而在 Linux 中,大家也习惯了使用 rpm 来安装包,对此你可以使用这条命令实现
-rpm 包的构建
-
-.. code:: shell
-
- $ python setup.py bdist_rpm
-
-若你喜欢使用 easy_install 或者 pip 来安装离线包。你可以将其打包成 egg 包
-
-.. code:: shell
-
- $ python setup.py bdist_egg
-
-若你的项目,需要安装多个平台下,既有 Windows 也有
-Linux,按照上面的方法,多种格式我们要执行多次命令,为了方便,你可以一步到位,执行如下这条命令,即可生成多个格式的进制包
-
-.. code:: shell
-
- $ python setup.py bdist
-
-10. 如何使用 setup.py 安装包
-----------------------------
-
-正常情况下,我们都是通过以上构建的源码包或者二进制包进行模块的安装。
-
-但在编写 setup.py
-的过程中,可能不能一步到位,需要多次调试,这时候如何测试自己写的
-setup.py 文件是可用的呢?
-
-这时候你可以使用这条命令,它会将你的模块安装至系统全局环境中
-
-.. code:: shell
-
- $ python setup.py install
-
-如若你的项目还处于开发阶段,频繁的安装模块,也是一个麻烦事。
-
-这时候你可以使用这条命令安装,该方法不会真正的安装包,而是在系统环境中创建一个软链接指向包实际所在目录。这边在修改包之后不用再安装就能生效,便于调试。
-
-.. code:: shell
-
- $ python setup.py develop
-
-11. 如何发布包到 PyPi?
------------------------
-
-通过上面的学习,你一定已经学会了如何打包自己的项目,若你觉得自己开发的模块非常不错,想要
-share 给其他人使用,你可以将其上传到 PyPi (Python Package
-Index)上,它是 Python
-官方维护的第三方包仓库,用于统一存储和管理开发者发布的 Python 包。
-
-如果要发布自己的包,需要先到 pypi 上注册账号。然后创建 ``~/.pypirc``
-文件,此文件中配置 PyPI
-访问地址和账号。如的.pypirc文件内容请根据自己的账号来修改。
-
-典型的 .pypirc 文件
-
-.. code:: ini
-
- [distutils]
- index-servers = pypi
-
- [pypi]
- username:xxx
- password:xxx
-
-然后使用这条命令进行信息注册,完成后,你可以在 PyPi 上看到项目信息。
+.. code:: python
-.. code:: shell
+ >>> import operator
+ >>>
+ >>> operator.contains("hello, python", "llo")
+ True
+ >>> operator.contains("hello, python", "lol")
+ False
+ >>>
- $ python setup.py register
+7. 使用正则匹配
+---------------
-注册完了后,你还要上传源码包,别人才使用下载安装
+说到查找功能,那正则绝对可以说是专业的工具,多复杂的查找规则,都能满足你。
-.. code:: shell
+对于判断字符串是否存在于另一个字符串中的这个需求,使用正则简直就是大材小用。
- $ python setup.py upload
+.. code:: python
-或者也可以使用 ``twine`` 工具注册上传,它是一个专门用于与 pypi
-进行交互的工具,详情可以参考官网:https://www.ctolib.com/twine.html,这里不详细讲了。
+ import re
-参考文章
---------
+ def is_in(full_str, sub_str):
+ if re.findall(sub_str, full_str):
+ return True
+ else:
+ return False
-- http://blog.konghy.cn/2018/04/29/setup-dot-py/
-- https://note.qidong.name/2018/01/python-setup-requires/
+ print(is_in("hello, python", "llo")) # True
+ print(is_in("hello, python", "lol")) # False
-.. figure:: http://image.python-online.cn/image-20200320125724880.png
- :alt: 关注公众号,获取最新干货!
+--------------
- 关注公众号,获取最新干货!
+|image1|
-.. |image0| image:: http://image.python-online.cn/20191218202833.png
-.. |image1| image:: http://image.python-online.cn/20191218203005.png
-.. |image2| image:: http://image.python-online.cn/20191218203255.png
-.. |image3| image:: http://image.python-online.cn/20191218203517.png
+.. |image0| image:: http://image.iswbm.com/20200602135014.png
+.. |image1| image:: http://image.iswbm.com/20200607174235.png
diff --git a/source/c01/c01_41.rst b/source/c01/c01_28.rst
similarity index 97%
rename from source/c01/c01_41.rst
rename to source/c01/c01_28.rst
index 03af012..82b79a2 100644
--- a/source/c01/c01_41.rst
+++ b/source/c01/c01_28.rst
@@ -1,6 +1,8 @@
1.41 Python 炫技操作:连接列表的八种方法
========================================
+|image0|
+
Python 语言里有许多(而且是越来越多)的高级特性,是 Python
发烧友们非常喜欢的。在这些人的眼里,能够写出那些一般开发者看不懂的高级特性,就是高手,就是大神。
@@ -224,7 +226,8 @@ yield from 意义及使用方法。
--------------
-.. figure:: http://image.python-online.cn/image-20200320125724880.png
- :alt: 关注公众号,获取最新干货!
+|image1|
+
+.. |image0| image:: http://image.iswbm.com/20200602135014.png
+.. |image1| image:: http://image.iswbm.com/20200607174235.png
- 关注公众号,获取最新干货!
diff --git a/source/c01/c01_29.md b/source/c01/c01_29.md
deleted file mode 100644
index 2657d2a..0000000
--- a/source/c01/c01_29.md
+++ /dev/null
@@ -1,11 +0,0 @@
-# 1.27 如何阅读 CPython源码?
-
-
-
-参考学习地址:https://realpython.com/cpython-source-code-guide/
-
-基于 Python 3.6 的源码分析:https://he11olx.com/
-
-
-
-
\ No newline at end of file
diff --git a/source/c01/c01_29.rst b/source/c01/c01_29.rst
index 9004d6b..9df2a34 100644
--- a/source/c01/c01_29.rst
+++ b/source/c01/c01_29.rst
@@ -1,11 +1,183 @@
-1.27 如何阅读 CPython源码?
-===========================
+1.42 Python 炫技操作:海象运算符的三种用法
+==========================================
-参考学习地址:https://realpython.com/cpython-source-code-guide/
+|image0|
-基于 Python 3.6 的源码分析:https://he11olx.com/
+Python 版本发展非常快,如今最新的版本已经是 Pyhton
+3.9,即便如此,有很多人甚至还停留在 3.6 或者 3.7,连 3.8 还没用上。
-.. figure:: http://image.python-online.cn/image-20200320125724880.png
- :alt: 关注公众号,获取最新干货!
+很多 Python 3.8
+的特性还没来得及了解,就已经成为旧知识了,比如今天要说的海象运算符。
+
+海象运算符是在 PEP 572 被提出的,直到 3.8 版本合入发布。
+
+它的英文原名叫 ``Assignment Expressions``\ ,翻译过来也就是
+``赋值表达式``\ ,不过现在大家更普遍地称之为海象运算符,就是因为它长得真的太像海象了。
+
+|image1|
+
+1. 第一个用法:if/else
+----------------------
+
+可能有朋友是第一次接触这个新特性,所以还是简单的介绍一下这个海象运算符有什么用?
+
+在 Golang 中的条件语句可以直接在 if
+中运算变量的获取后直接对这个变量进行判断,可以让你少写一行代码
+
+.. code:: go
+
+ import "fmt"
+
+ func main() {
+ if age := 20;age > 18 {
+ fmt.Println("已经成年了")
+ }
+ }
+
+若在 Python 3.8 之前,Python 必须得这样子写
+
+.. code:: python
+
+ age = 20
+ if age > 18:
+ print("已经成年了")
+
+但有了海象运算符之后,你可以和 Golang 一样(如果你没学过
+Golang,那这里要注意,Golang 中的 ``:=``
+叫短变量声明,意思是声明并初始化,它和 Python 中的 ``:=`` 不是一个概念)
+
+.. code:: python
+
+ if (age:= 20) > 18:
+ print("已经成年了")
+
+2. 第二个用法:while
+--------------------
+
+在不使用 海象运算符之前,使用 while 循环来读取文件的时候,你也许会这么写
+
+.. code:: python
+
+ file = open("demo.txt", "r")
+ while True:
+ line = file.readline()
+ if not line:
+ break
+ print(line.strip())
+
+但有了海象运算符之后,你可以这样
+
+.. code:: python
+
+ file = open("demo.txt", "r")
+ while (line := file.readline()):
+ print(line.strip())
+
+使用它替换以往的无限 while 循环写法更为惊艳
+
+比如,实现一个需要命令行交互输入密码并检验的代码,你也许会这样子写
+
+.. code:: python
+
+ while True:
+ p = input("Enter the password: ")
+ if p == "youpassword":
+ break
+
+有了海象运算符之后,这样子写更为舒服
+
+.. code:: python
+
+ while (p := input("Enter the password: ")) != "youpassword":
+ continue
+
+3. 第三个用法:推导式
+---------------------
+
+这个系列的文章,几乎每篇都能看到推导式的身影,这一篇依旧如此。
+
+在编码过程中,我很喜欢使用推导式,在简单的应用场景下,它简洁且不失高效。
+
+如下这段代码中,我会使用列表推导式得出所有会员中过于肥胖的人的 bmi 指数
+
+.. code:: python
+
+ members = [
+ {"name": "小五", "age": 23, "height": 1.75, "weight": 72},
+ {"name": "小李", "age": 17, "height": 1.72, "weight": 63},
+ {"name": "小陈", "age": 20, "height": 1.78, "weight": 82},
+ ]
+
+ count = 0
+
+ def get_bmi(info):
+ global count
+ count += 1
+
+ print(f"执行了 {count} 次")
+
+ height = info["height"]
+ weight = info["weight"]
+
+ return weight / (height**2)
+
+ # 查出所有会员中过于肥胖的人的 bmi 指数
+ fat_bmis = [get_bmi(m) for m in members if get_bmi(m) > 24]
+
+ print(fat_bmis)
+
+输出如下
+
+::
+
+ 执行了 1 次
+ 执行了 2 次
+ 执行了 3 次
+ 执行了 4 次
+ [25.88057063502083]
+
+可以看到,会员数只有 3 个,但是 get_bmi 函数却执行了 4
+次,原因是在判断时执行了 3 次,而在构造新的列表时又重复执行了一遍。
+
+如果所有会员都是过于肥胖的,那最终将执行 6
+次,这种在大量的数据下是比较浪费性能的,因此对于这种结构,我通常会使用传统的for
+循环 + if 判断。
+
+.. code:: python
+
+ fat_bmis = []
+
+ # 查出所有会员中过于肥胖的人的 bmi 指数
+ for m in members:
+ bmi = get_bmi(m)
+ if bmi > 24:
+ fat_bmis.append(bmi)
+
+在有了海象运算符之后,你就可以不用在这种场景下做出妥协。
+
+.. code:: python
+
+ # 查出所有会员中过于肥胖的人的 bmi 指数
+ fat_bmis = [bmi for m in members if (bmi := get_bmi(m)) > 24]
+
+最终从输出结果可以看出,只执行了 3 次
+
+::
+
+ 执行了 1 次
+ 执行了 2 次
+ 执行了 3 次
+ [25.88057063502083]
+
+这里仅介绍了列表推导式,但在字典推导式和集合推导式中同样适用。不再演示。
+
+海象运算符,是一个新奇的特性,有不少人觉得这样这种特性会破坏代码的可读性。确实在一个新鲜事物刚出来时是会这样,但我相信经过时间的沉淀后,越来越多的人使用它并享受它带来的便利时,这种争议也会慢慢消失在历史的长河中。
+
+--------------
+
+|image2|
+
+.. |image0| image:: http://image.iswbm.com/20200602135014.png
+.. |image1| image:: http://image.iswbm.com/image-20200418122739417.png
+.. |image2| image:: http://image.iswbm.com/20200607174235.png
- 关注公众号,获取最新干货!
diff --git a/source/c01/c01_30.md b/source/c01/c01_30.md
deleted file mode 100644
index f319646..0000000
--- a/source/c01/c01_30.md
+++ /dev/null
@@ -1,35 +0,0 @@
-# 1.30 盘点程序员学习编程的那些网站
-
-## 书栈网
-
-**网站链接**:https://www.bookstack.cn/rank?tab=popular
-
-
-
-## 魔法学院
-
- **网站链接**:http://www.nowamagic.net/academy/
-
-
-
-
-
-## Python 3 标准库实例教程
-
-**网站链接**:https://learnku.com/docs/pymotw
-
-
-
-
-
-## Django Web 框架
-
-网站链接:https://developer.mozilla.org/zh-CN/docs/learn/Server-side/Django
-
-该网站可以让你从0开始学习Web,包括前端(HTML,CSS,JS)、后端(Django)
-
-
-
-在服务端网页编程里,重点介绍了 Django
-
-
\ No newline at end of file
diff --git a/source/c01/c01_30.rst b/source/c01/c01_30.rst
deleted file mode 100644
index 0e34aaf..0000000
--- a/source/c01/c01_30.rst
+++ /dev/null
@@ -1,43 +0,0 @@
-1.30 盘点程序员学习编程的那些网站
-=================================
-
-书栈网
-------
-
-**网站链接**\ :https://www.bookstack.cn/rank?tab=popular
-
-|image0|
-
-魔法学院
---------
-
-**网站链接**\ :http://www.nowamagic.net/academy/
-
-|image1|
-
-Python 3 标准库实例教程
------------------------
-
-**网站链接**\ :https://learnku.com/docs/pymotw
-
-|image2|
-
-Django Web 框架
----------------
-
-网站链接:https://developer.mozilla.org/zh-CN/docs/learn/Server-side/Django
-
-该网站可以让你从0开始学习Web,包括前端(HTML,CSS,JS)、后端(Django)
-
-|image3|
-
-在服务端网页编程里,重点介绍了 Django
-
-|image4|
-
-.. |image0| image:: http://image.python-online.cn/20200104144109.png
-.. |image1| image:: http://image.python-online.cn/20200112210558.png
-.. |image2| image:: http://image.iswbm.com/20200508201333.png
-.. |image3| image:: http://image.iswbm.com/20200525080531.png
-.. |image4| image:: http://image.iswbm.com/20200525080715.png
-
diff --git a/source/c01/c01_31.md b/source/c01/c01_31.md
deleted file mode 100644
index 71656f0..0000000
--- a/source/c01/c01_31.md
+++ /dev/null
@@ -1,23 +0,0 @@
-# 1.31 学习 Pillow 笔记
-
-
-
-## 1. 安装 pillow
-
-```
-pip install pillow
-```
-
-
-
-## 2. 使用
-
-ARGB 是一种色彩模式,也就是RGB色彩模式附加上Alpha(透明度)通道,常见于32位位图的存储结构。
-
-RGB 色彩模式是工业界的一种颜色标准,是通过对红(R)、绿(G)、蓝(B)三个颜色通道的变化以及它们相互之间的叠加来得到各式各样的颜色的,RGB即是代表红、绿、蓝三个通道的颜色,这个标准几乎包括了人类视力所能感知的所有颜色,是目前运用最广的颜色系统之一。
-
-
-
-
-
-
\ No newline at end of file
diff --git a/source/c01/c01_31.rst b/source/c01/c01_31.rst
deleted file mode 100644
index 5b12101..0000000
--- a/source/c01/c01_31.rst
+++ /dev/null
@@ -1,23 +0,0 @@
-1.31 学习 Pillow 笔记
-=====================
-
-1. 安装 pillow
---------------
-
-::
-
- pip install pillow
-
-2. 使用
--------
-
-ARGB
-是一种色彩模式,也就是RGB色彩模式附加上Alpha(透明度)通道,常见于32位位图的存储结构。
-
-RGB
-色彩模式是工业界的一种颜色标准,是通过对红(R)、绿(G)、蓝(B)三个颜色通道的变化以及它们相互之间的叠加来得到各式各样的颜色的,RGB即是代表红、绿、蓝三个通道的颜色,这个标准几乎包括了人类视力所能感知的所有颜色,是目前运用最广的颜色系统之一。
-
-.. figure:: http://image.python-online.cn/image-20200320125724880.png
- :alt: 关注公众号,获取最新干货!
-
- 关注公众号,获取最新干货!
diff --git a/source/c01/c01_32.md b/source/c01/c01_32.md
deleted file mode 100644
index 77da3e4..0000000
--- a/source/c01/c01_32.md
+++ /dev/null
@@ -1,37 +0,0 @@
-# 1.32 在 CentOS 7.2 上安装 Python3.7
-
-首先下载 python3.7的源码包,然后解压
-
-```shell
-$ cd ~
-$ wget -c https://www.python.org/ftp/python/3.7.1/Python-3.7.1.tgz
-$ tar xf Python-3.7.1.tgz && cd Python-3.7.1
-```
-
-安装 一些依赖包
-
-```shell
-$ yum install gcc zlib-devel bzip2-devel openssl openssl-devel ncurses-devel sqlite-devel libffi-devel python3-devel -y
-```
-
-编译安装
-
-```shell
-$ ./configure
-$ make
-$ sudo make install
-```
-
-至此,你已经成功安装 了 Python3, pip3,setuptools
-
-requests.get("https://www.baidu.com")
-
-```
-python3 -m pip install --user requests aiohttp cryptography pymysql prettytable sh Fabric paramiko apscheduler bashplotlib httpie PathPicker -i https://pypi.douban.com/simple
-```
-
-
-
-
-
-
\ No newline at end of file
diff --git a/source/c01/c01_32.rst b/source/c01/c01_32.rst
deleted file mode 100644
index 4cebca3..0000000
--- a/source/c01/c01_32.rst
+++ /dev/null
@@ -1,37 +0,0 @@
-1.32 在 CentOS 7.2 上安装 Python3.7
-===================================
-
-首先下载 python3.7的源码包,然后解压
-
-.. code:: shell
-
- $ cd ~
- $ wget -c https://www.python.org/ftp/python/3.7.1/Python-3.7.1.tgz
- $ tar xf Python-3.7.1.tgz && cd Python-3.7.1
-
-安装 一些依赖包
-
-.. code:: shell
-
- $ yum install gcc zlib-devel bzip2-devel openssl openssl-devel ncurses-devel sqlite-devel libffi-devel python3-devel -y
-
-编译安装
-
-.. code:: shell
-
- $ ./configure
- $ make
- $ sudo make install
-
-至此,你已经成功安装 了 Python3, pip3,setuptools
-
-requests.get(“https://www.baidu.com”)
-
-::
-
- python3 -m pip install --user requests aiohttp cryptography pymysql prettytable sh Fabric paramiko apscheduler bashplotlib httpie PathPicker -i https://pypi.douban.com/simple
-
-.. figure:: http://image.python-online.cn/image-20200320125724880.png
- :alt: 关注公众号,获取最新干货!
-
- 关注公众号,获取最新干货!
diff --git a/source/c01/c01_35.md b/source/c01/c01_35.md
deleted file mode 100644
index 594e430..0000000
--- a/source/c01/c01_35.md
+++ /dev/null
@@ -1,382 +0,0 @@
-# 1.35 使用 Python 远程登陆服务器的利器
-
-在使用 Python 写一些脚本的时候,在某些情况下,我们需要频繁登陆远程服务去执行一次命令,并返回一些结果。
-
-在 shell 环境中,我们是这样子做的。
-
-```shell
-$ sshpass -p ${passwd} ssh -p ${port} -l ${user} -o StrictHostKeyChecking=no xx.xx.xx.xx "ls -l"
-```
-
-然后你会发现,你的输出有很多你并不需要,但是又不去不掉的一些信息(也许有方法,请留言交流),类似这样
-
-```shell
-host: xx.xx.xx.xx, port: xx
-Warning: Permanently added '[xx.xx.xx.xx]:xx' (RSA) to the list of known hosts.
-Login failure: [Errno 1] This server is not registered to rmp platform, please confirm whether cdn server.
-total 4
--rw-r--r-- 1 root root 239 Mar 30 2018 admin-openrc
-```
-
-对于直接使用 shell 命令,来执行命令的,可以直接使用管道,或者将标准输出重定向到文件的方法取得执行命令返回的结果
-
-## 1. 使用 subprocess
-
-若是使用 Python 来做这件事,通常我们会第一时间,想到使用 os.popen,os.system,commands,subprocess 等一些命令执行库来间接获取 。
-
-但是据我所知,这些库获取的 output 不仅只有标准输出,还包含标准错误(也就是上面那些多余的信息)
-
-所以每次都要对 output 进行的数据清洗,然后整理格式化,才能得到我们想要的数据。
-
-用 subprocess 举个例子,就像这样子
-
-```python
-import subprocess
-ssh_cmd = "sshpass -p ${passwd} ssh -p 22 -l root -o StrictHostKeyChecking=no xx.xx.xx.xx 'ls -l'"
-status, output = subprocess.getstatusoutput(ssh_cmd)
-
-# 数据清理,格式化的就不展示了
-
-```
-
-
-
-通过以上的文字 + 代码的展示 ,可以感觉到 ssh 登陆的几大痛点
-
-- **痛点一**:需要额外安装 sshpass(如果不免密的话)
-- **痛点二**:干扰信息太多,数据清理、格式化相当麻烦
-- **痛点三**:代码实现不够优雅(有点土),可读性太差
-- **痛点四**:ssh 连接不能复用,一次连接仅能执行一次
-- **痛点五**:代码无法全平台,仅能在 Linux 和 OSX 上使用
-
-
-
-为了解决这几个问题,我搜索了全网关于 Python ssh 的文章,没有看到有完整介绍这方面的技巧的。
-
-
-
-为此,我就翻阅了一个很火的 Github 项目: awesome-python-cn (https://github.com/BingmingWong/awesome-python-cn)。
-
-期望在这里,找到有一些关于 远程连接 的一些好用的库。
-
-还真的被我找到了两个
-
-- sh.ssh
-- Paramiko
-
-
-
-## 2. 使用 sh.ssh
-
-首先来介绍第一个,`sh.ssh`
-
-`sh` 是一个可以让你通过函数的调用来完成 Linxu/OSX 系统命令的一个库,非常好用,关于它有机会也写篇介绍。
-
-```shell
-$ python3 -m pip install sh
-```
-
-
-
-今天只介绍它其中的一个函数:`ssh`
-
-通常两台机器互访,为了方便,可设置免密登陆,这样就不需要输入密码。
-
-这段代码可以实现免密登陆,并执行我们的命令 `ls -l`
-
-```python
-from sh import ssh
-output=ssh("root@xx.xx.xx.xx", "-p 22", "ls -l")
-print(output)
-```
-
-但有可能 ,我们并不想设置互信免密,为了使这段代码更通用,我假定我们没有设置免密,只能使用密码进行登陆。
-
-问题就来了,要输入密码,必须得使用交互式的方法来输入呀,在 Python 中要如何实现呢?
-
-原来 ssh 方法接收一个 `_out` 参数,这个参数可以为一个字符串,表示文件路径,也可以是一个文件对象(或者类文件对象),还可以是一个回调函数,意思是当有标准输出时,就会调用将输出内容传给这个函数。
-
-这就好办了呀。
-
-我只要识别到有 `password:` 字样,就往标准输入写入我的密码就好了呀。
-
-
-
-完整代码如下:
-
-```python
-import sys
-from sh import ssh
-
-aggregated = ""
-def ssh_interact(char, stdin):
- global aggregated
- sys.stdout.write(char.encode())
- sys.stdout.flush()
- aggregated += char
- if aggregated.endswith("password: "):
- stdin.put("you_password\n")
-
-output=ssh("root@xx.xx.xx.xx", "-p 22", "ls -l",_tty_in=True, _out_bufsize=0, _out=ssh_interact)
-print(output)
-```
-
-这是官方文档(http://amoffat.github.io/sh/tutorials/interacting_with_processes.html?highlight=ssh)给的一些信息,写的一个demo。
-
-尝试运行后,发现程序会一直在运行中,永远不会返回,不会退出,回调函数也永远不会进入。
-
-通过调试查看源代码,仍然查不到问题所在,于是去 [Github](https://github.com/amoffat/sh/issues/393) 上搜了下,原来在 2017 年就已经存在这个问题了,到现在 2020 年了还没有修复,看来使用 `sh.ssh` 的人并不多,于是我又“追问”了下,期望能得到回复。
-
-
-
-以上这个问题,只有在需要输入密码才会出现,如果设置了机器互信是没有问题的。
-
-为了感受 `sh.ssh` 的使用效果,我设置了机器互信免密,然后使用如下这段代码。
-
-```python
-from sh import ssh
-
-my_server=ssh.bake("root@xx.xx.xx.xx", "-p 22")
-
-# 相当于执行登陆一次执行一次命令,执行完就退出登陆
-print(my_server.ls())
-
-# 可在 sleep 期间,手动登陆服务器,使用 top ,查看当前有多少终端在连接
-time.sleep(5)
-
-# 再次执行这条命令时,登陆终端数将 +1,执行完后,又将 -1
-print(my_server.ifconfig())
-```
-
-惊奇地发现使用 `bake` 这种方式,`my_server.ls()` 和 `my_server.ifconfig()` 这种看似是通过同一个ssh连接,执行两次命令,可实际上,你可以在远程机器上,执行 top 命令看到已连接的终端的变化,会先 `+1` 再 `-1`,说明两次命令的执行是通过两次连接实现的。
-
-如此看来,使用 `sh.ssh` 可以解决痛点一(如果上述问题能得到解决)、痛点二、痛点三。
-
-但是它仍然无法复用 ssh 连接,还是不太方便,不是我理想中的最佳方案。
-
-最重要的一点是, `sh` 这个模块,仅支持 Linxu/OSX ,在 Windows 你得使用它的兄弟库 - `pbs` ,然后我又去 pypi 看了一眼 [pbs](https://pypi.org/project/pbs/),已经 “年久失修”,没人维护了。
-
-
-
-至此,我离 “卒”,就差最后一根稻草了。
-
-
-
-## 3. 使用 paramiko
-
-带着最后一丝希望,我尝试使用了 `paramiko` 这个库,终于在 `paramiko` 这里,找回了本应属于 Python 的那种优雅。
-
-你可以通过如下命令去安装它
-
-```
-$ python3 -m pip install paramiko
-```
-
-
-
-然后接下来,就介绍几种常用的 ssh 登陆的方法
-
-### 方法1:基于用户名和密码的 sshclient 方式登录
-
-然后你可以参考如下这段代码,在 Linux/OSX 系统下进行远程连接
-
-```python
-import paramiko
-
-ssh = paramiko.SSHClient()
-# 允许连接不在know_hosts文件中的主机
-ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
-
-# 建立连接
-ssh.connect("xx.xx.xx.xx", username="root", port=22, password="you_password")
-
-# 使用这个连接执行命令
-ssh_stdin, ssh_stdout, ssh_stderr = ssh.exec_command("ls -l")
-
-# 获取输出
-print(ssh_stdout.read())
-
-# 关闭连接
-ssh.close()
-```
-
-
-
-### 方法2:基于用户名和密码的 transport 方式登录
-
-方法1 是传统的连接服务器、执行命令、关闭的一个操作,多个操作需要连接多次,无法复用连接[**痛点四**]。
-
-有时候需要登录上服务器执行多个操作,比如执行命令、上传/下载文件,方法1 则无法实现,那就可以使用 transport 的方法。
-
-```python
-import paramiko
-
-# 建立连接
-trans = paramiko.Transport(("xx.xx.xx.xx", 22))
-trans.connect(username="root", password="you_passwd")
-
-# 将sshclient的对象的transport指定为以上的trans
-ssh = paramiko.SSHClient()
-ssh._transport = trans
-
-# 剩下的就和上面一样了
-ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
-ssh_stdin, ssh_stdout, ssh_stderr = ssh.exec_command("ls -l")
-print(ssh_stdout.read())
-
-# 关闭连接
-trans.close()
-```
-
-
-
-### 方法3:基于公钥密钥的 SSHClient 方式登录
-
-```python
-import paramiko
-
-# 指定本地的RSA私钥文件
-# 如果建立密钥对时设置的有密码,password为设定的密码,如无不用指定password参数
-pkey = paramiko.RSAKey.from_private_key_file('/home/you_username/.ssh/id_rsa', password='12345')
-
-# 建立连接
-ssh = paramiko.SSHClient()
-ssh.connect(hostname='xx.xx.xx.xx',
- port=22,
- username='you_username',
- pkey=pkey)
-
-# 执行命令
-stdin, stdout, stderr = ssh.exec_command('ls -l')
-
-# 结果放到stdout中,如果有错误将放到stderr中
-print(stdout.read())
-
-# 关闭连接
-ssh.close()
-```
-
-
-
-### 方法4:基于密钥的 Transport 方式登录
-
-```python
-import paramiko
-
-# 指定本地的RSA私钥文件
-# 如果建立密钥对时设置的有密码,password为设定的密码,如无不用指定password参数
-pkey = paramiko.RSAKey.from_private_key_file('/home/you_username/.ssh/id_rsa', password='12345')
-
-# 建立连接
-trans = paramiko.Transport(('xx.xx.xx.xx', 22))
-trans.connect(username='you_username', pkey=pkey)
-
-# 将sshclient的对象的transport指定为以上的trans
-ssh = paramiko.SSHClient()
-ssh._transport = trans
-
-# 执行命令,和传统方法一样
-stdin, stdout, stderr = ssh.exec_command('df -hl')
-print(stdout.read().decode())
-
-# 关闭连接
-trans.close()
-```
-
-
-
-以上四种方法,可以帮助你实现远程登陆服务器执行命令,如果需要复用连接:一次连接执行多次命令,可以使用 **方法二** 和 **方法四**
-
-用完后,记得关闭连接。
-
-### 实现 sftp 文件传输
-
-同时,paramiko 做为 ssh 的完美解决方案,它非常专业,利用它还可以实现 sftp 文件传输。
-
-```python
-import paramiko
-
-# 实例化一个trans对象# 实例化一个transport对象
-trans = paramiko.Transport(('xx.xx.xx.xx', 22))
-
-# 建立连接
-trans.connect(username='you_username', password='you_passwd')
-
-# 实例化一个 sftp对象,指定连接的通道
-sftp = paramiko.SFTPClient.from_transport(trans)
-
-# 发送文件
-sftp.put(localpath='/tmp/11.txt', remotepath='/tmp/22.txt')
-
-# 下载文件
-sftp.get(remotepath='/tmp/22.txt', localpath='/tmp/33.txt')
-trans.close()
-```
-
-
-
-到这里,Paramiko 已经完胜了,但是仍然有一个痛点我们没有提及,就是多平台,说的就是 Windows,这里就有一件好事,一件坏事了,。
-
-好事就是:paramiko 支持 windows
-
-坏事就是:你需要做很多复杂的准备,你可 google 解决,但是我建议你直接放弃,坑太深了。
-
-
-
-### 注意事项
-
-使用 paramiko 的时候,有一点需要注意一下,这个也是我自己 "踩坑" 后才发现的,其实我觉得这个设计挺好的,如果你不需要等待它返回数据,可以直接实现异步效果,只不过对于不知道这个设计的人,确实是个容易掉坑的点
-
-就是在执行 `ssh.exec_command(cmd)` 时,这个命令并不是同步阻塞的。
-
-比如下面这段代码,执行时,你会发现 脚本立马就结束退出了,并不会等待 5 s 后,再 执行 ssh.close()
-
-```python
-import paramiko
-
-trans = paramiko.Transport(("172.20.42.1", 57891))
-trans.connect(username="root", password="youpassword")
-ssh = paramiko.SSHClient()
-ssh._transport = trans
-stdin, stdout, stderr = ssh.exec_command("sleep 5;echo ok")
-ssh.close()
-```
-
-
-
-但是如果改成这样,加上一行 stdout.read(), paramiko 就知道,你需要这个执行的结果,就会在 read() 进行阻塞。
-
-```python
-import paramiko
-
-trans = paramiko.Transport(("172.20.42.1", 57891))
-trans.connect(username="root", password="youpassword")
-ssh = paramiko.SSHClient()
-ssh._transport = trans
-stdin, stdout, stderr = ssh.exec_command("sleep 5;echo ok")
-
-# 加上一行 read()
-print(stdout.read())
-ssh.close()
-```
-
-
-
-## 4. 写在最后
-
-经过了一番对比,和一些实例的展示,可以看出 Paramiko 是一个专业、让人省心的 ssh 利器,个人认为 Paramiko 模块是运维人员必学模块之一,如果你恰好需要在 Python 代码中实现 ssh 到远程服务器去获取一些信息,那么我把 Paramiko 推荐给你。
-
-最后,希望这篇文章,能给你带来帮助。
-
-
-
-## 5. 参考链接
-
-- https://github.com/paramiko/paramiko
-- http://docs.paramiko.org
-- https://www.liujiangblog.com/blog/15/
-- http://docs.paramiko.org/en/stable/
-
-
-
-
\ No newline at end of file
diff --git a/source/c01/c01_35.rst b/source/c01/c01_35.rst
deleted file mode 100644
index 0ed5503..0000000
--- a/source/c01/c01_35.rst
+++ /dev/null
@@ -1,404 +0,0 @@
-1.35 使用 Python 远程登陆服务器的利器
-=====================================
-
-在使用 Python
-写一些脚本的时候,在某些情况下,我们需要频繁登陆远程服务去执行一次命令,并返回一些结果。
-
-在 shell 环境中,我们是这样子做的。
-
-.. code:: shell
-
- $ sshpass -p ${passwd} ssh -p ${port} -l ${user} -o StrictHostKeyChecking=no xx.xx.xx.xx "ls -l"
-
-然后你会发现,你的输出有很多你并不需要,但是又不去不掉的一些信息(也许有方法,请留言交流),类似这样
-
-.. code:: shell
-
- host: xx.xx.xx.xx, port: xx
- Warning: Permanently added '[xx.xx.xx.xx]:xx' (RSA) to the list of known hosts.
- Login failure: [Errno 1] This server is not registered to rmp platform, please confirm whether cdn server.
- total 4
- -rw-r--r-- 1 root root 239 Mar 30 2018 admin-openrc
-
-对于直接使用 shell
-命令,来执行命令的,可以直接使用管道,或者将标准输出重定向到文件的方法取得执行命令返回的结果
-
-1. 使用 subprocess
-------------------
-
-若是使用 Python 来做这件事,通常我们会第一时间,想到使用
-os.popen,os.system,commands,subprocess 等一些命令执行库来间接获取 。
-
-但是据我所知,这些库获取的 output
-不仅只有标准输出,还包含标准错误(也就是上面那些多余的信息)
-
-所以每次都要对 output
-进行的数据清洗,然后整理格式化,才能得到我们想要的数据。
-
-用 subprocess 举个例子,就像这样子
-
-.. code:: python
-
- import subprocess
- ssh_cmd = "sshpass -p ${passwd} ssh -p 22 -l root -o StrictHostKeyChecking=no xx.xx.xx.xx 'ls -l'"
- status, output = subprocess.getstatusoutput(ssh_cmd)
-
- # 数据清理,格式化的就不展示了
-
-
-通过以上的文字 + 代码的展示 ,可以感觉到 ssh 登陆的几大痛点
-
-- **痛点一**\ :需要额外安装 sshpass(如果不免密的话)
-- **痛点二**\ :干扰信息太多,数据清理、格式化相当麻烦
-- **痛点三**\ :代码实现不够优雅(有点土),可读性太差
-- **痛点四**\ :ssh 连接不能复用,一次连接仅能执行一次
-- **痛点五**\ :代码无法全平台,仅能在 Linux 和 OSX 上使用
-
-为了解决这几个问题,我搜索了全网关于 Python ssh
-的文章,没有看到有完整介绍这方面的技巧的。
-
-为此,我就翻阅了一个很火的 Github 项目: awesome-python-cn
-(https://github.com/BingmingWong/awesome-python-cn)。
-
-期望在这里,找到有一些关于 远程连接 的一些好用的库。
-
-还真的被我找到了两个
-
-- sh.ssh
-- Paramiko
-
-2. 使用 sh.ssh
---------------
-
-首先来介绍第一个,\ ``sh.ssh``
-
-``sh`` 是一个可以让你通过函数的调用来完成 Linxu/OSX
-系统命令的一个库,非常好用,关于它有机会也写篇介绍。
-
-.. code:: shell
-
- $ python3 -m pip install sh
-
-今天只介绍它其中的一个函数:\ ``ssh``
-
-通常两台机器互访,为了方便,可设置免密登陆,这样就不需要输入密码。
-
-这段代码可以实现免密登陆,并执行我们的命令 ``ls -l``
-
-.. code:: python
-
- from sh import ssh
- output=ssh("root@xx.xx.xx.xx", "-p 22", "ls -l")
- print(output)
-
-但有可能
-,我们并不想设置互信免密,为了使这段代码更通用,我假定我们没有设置免密,只能使用密码进行登陆。
-
-问题就来了,要输入密码,必须得使用交互式的方法来输入呀,在 Python
-中要如何实现呢?
-
-原来 ssh 方法接收一个 ``_out``
-参数,这个参数可以为一个字符串,表示文件路径,也可以是一个文件对象(或者类文件对象),还可以是一个回调函数,意思是当有标准输出时,就会调用将输出内容传给这个函数。
-
-这就好办了呀。
-
-我只要识别到有 ``password:`` 字样,就往标准输入写入我的密码就好了呀。
-
-完整代码如下:
-
-.. code:: python
-
- import sys
- from sh import ssh
-
- aggregated = ""
- def ssh_interact(char, stdin):
- global aggregated
- sys.stdout.write(char.encode())
- sys.stdout.flush()
- aggregated += char
- if aggregated.endswith("password: "):
- stdin.put("you_password\n")
-
- output=ssh("root@xx.xx.xx.xx", "-p 22", "ls -l",_tty_in=True, _out_bufsize=0, _out=ssh_interact)
- print(output)
-
-这是官方文档(http://amoffat.github.io/sh/tutorials/interacting_with_processes.html?highlight=ssh)给的一些信息,写的一个demo。
-
-尝试运行后,发现程序会一直在运行中,永远不会返回,不会退出,回调函数也永远不会进入。
-
-通过调试查看源代码,仍然查不到问题所在,于是去
-`Github `__ 上搜了下,原来在
-2017 年就已经存在这个问题了,到现在 2020 年了还没有修复,看来使用
-``sh.ssh`` 的人并不多,于是我又“追问”了下,期望能得到回复。
-
-|image0|
-
-以上这个问题,只有在需要输入密码才会出现,如果设置了机器互信是没有问题的。
-
-为了感受 ``sh.ssh``
-的使用效果,我设置了机器互信免密,然后使用如下这段代码。
-
-.. code:: python
-
- from sh import ssh
-
- my_server=ssh.bake("root@xx.xx.xx.xx", "-p 22")
-
- # 相当于执行登陆一次执行一次命令,执行完就退出登陆
- print(my_server.ls())
-
- # 可在 sleep 期间,手动登陆服务器,使用 top ,查看当前有多少终端在连接
- time.sleep(5)
-
- # 再次执行这条命令时,登陆终端数将 +1,执行完后,又将 -1
- print(my_server.ifconfig())
-
-惊奇地发现使用 ``bake`` 这种方式,\ ``my_server.ls()`` 和
-``my_server.ifconfig()``
-这种看似是通过同一个ssh连接,执行两次命令,可实际上,你可以在远程机器上,执行
-top 命令看到已连接的终端的变化,会先 ``+1`` 再
-``-1``\ ,说明两次命令的执行是通过两次连接实现的。
-
-如此看来,使用 ``sh.ssh``
-可以解决痛点一(如果上述问题能得到解决)、痛点二、痛点三。
-
-但是它仍然无法复用 ssh 连接,还是不太方便,不是我理想中的最佳方案。
-
-最重要的一点是, ``sh`` 这个模块,仅支持 Linxu/OSX ,在 Windows
-你得使用它的兄弟库 - ``pbs`` ,然后我又去 pypi 看了一眼
-`pbs `__\ ,已经 “年久失修”,没人维护了。
-
-|image1|
-
-至此,我离 “卒”,就差最后一根稻草了。
-
-3. 使用 paramiko
-----------------
-
-带着最后一丝希望,我尝试使用了 ``paramiko`` 这个库,终于在 ``paramiko``
-这里,找回了本应属于 Python 的那种优雅。
-
-你可以通过如下命令去安装它
-
-::
-
- $ python3 -m pip install paramiko
-
-然后接下来,就介绍几种常用的 ssh 登陆的方法
-
-方法1:基于用户名和密码的 sshclient 方式登录
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-然后你可以参考如下这段代码,在 Linux/OSX 系统下进行远程连接
-
-.. code:: python
-
- import paramiko
-
- ssh = paramiko.SSHClient()
- # 允许连接不在know_hosts文件中的主机
- ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
-
- # 建立连接
- ssh.connect("xx.xx.xx.xx", username="root", port=22, password="you_password")
-
- # 使用这个连接执行命令
- ssh_stdin, ssh_stdout, ssh_stderr = ssh.exec_command("ls -l")
-
- # 获取输出
- print(ssh_stdout.read())
-
- # 关闭连接
- ssh.close()
-
-方法2:基于用户名和密码的 transport 方式登录
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-方法1
-是传统的连接服务器、执行命令、关闭的一个操作,多个操作需要连接多次,无法复用连接[**痛点四**]。
-
-有时候需要登录上服务器执行多个操作,比如执行命令、上传/下载文件,方法1
-则无法实现,那就可以使用 transport 的方法。
-
-.. code:: python
-
- import paramiko
-
- # 建立连接
- trans = paramiko.Transport(("xx.xx.xx.xx", 22))
- trans.connect(username="root", password="you_passwd")
-
- # 将sshclient的对象的transport指定为以上的trans
- ssh = paramiko.SSHClient()
- ssh._transport = trans
-
- # 剩下的就和上面一样了
- ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
- ssh_stdin, ssh_stdout, ssh_stderr = ssh.exec_command("ls -l")
- print(ssh_stdout.read())
-
- # 关闭连接
- trans.close()
-
-方法3:基于公钥密钥的 SSHClient 方式登录
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-.. code:: python
-
- import paramiko
-
- # 指定本地的RSA私钥文件
- # 如果建立密钥对时设置的有密码,password为设定的密码,如无不用指定password参数
- pkey = paramiko.RSAKey.from_private_key_file('/home/you_username/.ssh/id_rsa', password='12345')
-
- # 建立连接
- ssh = paramiko.SSHClient()
- ssh.connect(hostname='xx.xx.xx.xx',
- port=22,
- username='you_username',
- pkey=pkey)
-
- # 执行命令
- stdin, stdout, stderr = ssh.exec_command('ls -l')
-
- # 结果放到stdout中,如果有错误将放到stderr中
- print(stdout.read())
-
- # 关闭连接
- ssh.close()
-
-方法4:基于密钥的 Transport 方式登录
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-.. code:: python
-
- import paramiko
-
- # 指定本地的RSA私钥文件
- # 如果建立密钥对时设置的有密码,password为设定的密码,如无不用指定password参数
- pkey = paramiko.RSAKey.from_private_key_file('/home/you_username/.ssh/id_rsa', password='12345')
-
- # 建立连接
- trans = paramiko.Transport(('xx.xx.xx.xx', 22))
- trans.connect(username='you_username', pkey=pkey)
-
- # 将sshclient的对象的transport指定为以上的trans
- ssh = paramiko.SSHClient()
- ssh._transport = trans
-
- # 执行命令,和传统方法一样
- stdin, stdout, stderr = ssh.exec_command('df -hl')
- print(stdout.read().decode())
-
- # 关闭连接
- trans.close()
-
-以上四种方法,可以帮助你实现远程登陆服务器执行命令,如果需要复用连接:一次连接执行多次命令,可以使用
-**方法二** 和 **方法四**
-
-用完后,记得关闭连接。
-
-实现 sftp 文件传输
-~~~~~~~~~~~~~~~~~~
-
-同时,paramiko 做为 ssh 的完美解决方案,它非常专业,利用它还可以实现
-sftp 文件传输。
-
-.. code:: python
-
- import paramiko
-
- # 实例化一个trans对象# 实例化一个transport对象
- trans = paramiko.Transport(('xx.xx.xx.xx', 22))
-
- # 建立连接
- trans.connect(username='you_username', password='you_passwd')
-
- # 实例化一个 sftp对象,指定连接的通道
- sftp = paramiko.SFTPClient.from_transport(trans)
-
- # 发送文件
- sftp.put(localpath='/tmp/11.txt', remotepath='/tmp/22.txt')
-
- # 下载文件
- sftp.get(remotepath='/tmp/22.txt', localpath='/tmp/33.txt')
- trans.close()
-
-到这里,Paramiko
-已经完胜了,但是仍然有一个痛点我们没有提及,就是多平台,说的就是
-Windows,这里就有一件好事,一件坏事了,。
-
-好事就是:paramiko 支持 windows
-
-坏事就是:你需要做很多复杂的准备,你可 google
-解决,但是我建议你直接放弃,坑太深了。
-
-|image2|
-
-注意事项
-~~~~~~~~
-
-使用 paramiko 的时候,有一点需要注意一下,这个也是我自己 “踩坑”
-后才发现的,其实我觉得这个设计挺好的,如果你不需要等待它返回数据,可以直接实现异步效果,只不过对于不知道这个设计的人,确实是个容易掉坑的点
-
-就是在执行 ``ssh.exec_command(cmd)`` 时,这个命令并不是同步阻塞的。
-
-比如下面这段代码,执行时,你会发现 脚本立马就结束退出了,并不会等待 5 s
-后,再 执行 ssh.close()
-
-.. code:: python
-
- import paramiko
-
- trans = paramiko.Transport(("172.20.42.1", 57891))
- trans.connect(username="root", password="youpassword")
- ssh = paramiko.SSHClient()
- ssh._transport = trans
- stdin, stdout, stderr = ssh.exec_command("sleep 5;echo ok")
- ssh.close()
-
-但是如果改成这样,加上一行 stdout.read(), paramiko
-就知道,你需要这个执行的结果,就会在 read() 进行阻塞。
-
-.. code:: python
-
- import paramiko
-
- trans = paramiko.Transport(("172.20.42.1", 57891))
- trans.connect(username="root", password="youpassword")
- ssh = paramiko.SSHClient()
- ssh._transport = trans
- stdin, stdout, stderr = ssh.exec_command("sleep 5;echo ok")
-
- # 加上一行 read()
- print(stdout.read())
- ssh.close()
-
-4. 写在最后
------------
-
-经过了一番对比,和一些实例的展示,可以看出 Paramiko
-是一个专业、让人省心的 ssh 利器,个人认为 Paramiko
-模块是运维人员必学模块之一,如果你恰好需要在 Python 代码中实现 ssh
-到远程服务器去获取一些信息,那么我把 Paramiko 推荐给你。
-
-最后,希望这篇文章,能给你带来帮助。
-
-5. 参考链接
------------
-
-- https://github.com/paramiko/paramiko
-- http://docs.paramiko.org
-- https://www.liujiangblog.com/blog/15/
-- http://docs.paramiko.org/en/stable/
-
-.. figure:: http://image.python-online.cn/image-20200320125724880.png
- :alt: 关注公众号,获取最新干货!
-
- 关注公众号,获取最新干货!
-
-.. |image0| image:: http://image.python-online.cn/20200228085749.png
-.. |image1| image:: http://image.python-online.cn/20200228093627.png
-.. |image2| image:: http://image.python-online.cn/20200228111654.png
-
diff --git a/source/c01/c01_36.md b/source/c01/c01_36.md
deleted file mode 100644
index 6b4f513..0000000
--- a/source/c01/c01_36.md
+++ /dev/null
@@ -1,251 +0,0 @@
-# 1.36 每日一库:pretty_errors 解决bug 洁癖
-
-当我们写的一个脚本或程序发生各种不可预知的异常时,如果我们没有进行捕获处理的时候,通常都会致使程序崩溃退出,并且会在终端打印出一堆 **密密麻麻** 的 traceback 堆栈信息来告诉我们,是哪个地方出了问题。
-
-就像这样子,天呐,密集恐惧症要犯了都
-
-
-
-上面这段 traceback
-
-- 只有黑白两个颜色,无法像代码高亮那样,对肉眼实现太不友好了
-- 无法直接显示报错的代码,排查问题慢人一步,效率太低
-
-那有没有一种办法,可以解决这些问题呢?
-
-当然有了,在 Python 中,没有什么问题是一个库解决不了的,如果有,那就等你去开发这个库。
-
-今天要介绍的这个库呢,叫做 `pretty-errors` ,从名字上就可以知道它的用途,是用来美化错误信息的。
-
-通过这条命令你可以安装它
-
-```shell
-$ python3 -m pip install pretty-errors
-```
-
-
-
-## 1. 环境要求
-
-由于使用了 `pretty-errors` 后,你的 traceback 信息输出,会有代码高亮那样的效果,因此当你在使用测试使用 `pretty-error` 时,请确保你使用的终端可以输出带有颜色的字体。
-
-在 windows 上你可以使用 Powershell,cmder 等
-
-在 Mac 上你可以使用自带的终端,或者安装一个更好用的 iTerm2
-
-## 2. 效果对比
-
-------
-
-随便写一个没有使用 pretty-errors ,并且报错了的程序,是这样子的。
-
-
-
-而使用了 pretty_errors 后,报错信息被美化成这样了。
-
-
-
-是不是感觉清楚了不少,那种密密麻麻带来的焦虑感是不是都消失了呢?
-
-当然这段代码少,你可能还没感受到,那就来看下 该项目在 Github上的一张效果对比图吧
-
-
-
-
-
-## 3. 配置全局可用
-
-可以看到使用了 pretty_errors 后,无非就是把过滤掉了一些干扰我们视线的无用信息,然后把有用的关键信息给我们高亮显示。
-
-既然既然这样,那 pretty_errors 应该也能支持我们如何自定义我们选用什么样的颜色,怎么排版吧?
-
-答案是显而易见的。
-
-pretty_errors 和其他库不太一样,在一定程度上(如果你使用全局配置的话),它并不是开箱即用的,你在使用它之前可能需要做一下配置。
-
-使用这一条命令,会让你进行配置,可以让你在该环境中运行其他脚本时的 traceback 输出都自动美化。
-
-```shell
-$ python3 -m pretty_errors
-```
-
-
-
-配置完成后,你再运行任何脚本,traceback 都会自动美化了。
-
-不仅是在我的 iTerm 终端下
-
-
-
-在 PyCharm 中也会
-
-
-
-唯一的缺点就是,原先在 PyCharm 中的 traceback 可以直接点击 `文件路径` 直接跳转到对应错误文件代码行,而你如果是在 VSCode 可以使用 下面自定义配置的方案解决这个问题(下面会讲到,参数是:`display_link`)。
-
-
-
-因此,有些情况下,你并不想设置 `pretty_errors` 全局可用。
-
-那怎么取消之前的配置呢?
-
-只需要再次输出 `python -m pretty_errors`,输出入 `C` 即可清除。
-
-
-
-
-
-## 4. 单文件中使用
-
-取消全局可用后,你可以根据自己需要,在你需要使用 `pretty-errors` 的脚本文件中导入` pretty_errors `,即可使用
-
-```python
-import pretty_errors
-```
-
-就像这样
-
-```python
-import pretty_errors
-
-def foo():
- 1/0
-
-if __name__ == "__main__":
- foo()
-```
-
-值得一提的是,使用这种方式,若是你的脚本中,出现语法错误,则输出的异常信息还是按照之前的方式展示,并不会被美化。
-
-因此,为了让美化更彻底,官方推荐你使用 `python -m pretty_errors`
-
-## 5. 自定义设置
-
-上面的例子里,我们使用的都是 `pretty_errors` 的默认美化格式,展示的信息并没有那么全。
-
-比如
-
-- 它并没有展示报错文件的绝对路径,这将使我们很难定位到是哪个文件里的代码出现错误。
-- 如果能把具体报错的代码,给我们展示在终端屏幕上,就不需要我们再到源码文件中排查原因了。
-
-如果使用了 `pretty_errors` 导致异常信息有丢失,那还不如不使用 `pretty_errors` 呢。
-
-不过,可以告诉你的是,`pretty_errors` 并没有你想象的那么简单。
-
-它足够开放,支持自定义配置,可以由你选择你需要展示哪些信息,怎么展示?
-
-这里举一个例子
-
-```python
-import pretty_errors
-
-# 【重点】进行配置
-pretty_errors.configure(
- separator_character = '*',
- filename_display = pretty_errors.FILENAME_EXTENDED,
- line_number_first = True,
- display_link = True,
- lines_before = 5,
- lines_after = 2,
- line_color = pretty_errors.RED + '> ' + pretty_errors.default_config.line_color,
- code_color = ' ' + pretty_errors.default_config.line_color,
-)
-
-# 原来的代码
-def foo():
- 1/0
-
-if __name__ == "__main__":
- foo()
-```
-
-在你像上面这样使用 `pretty_errrs.configure` 进行配置时,抛出的的异常信息就变成这样了。
-
-
-
-
-
-当然了,`pretty_errors.configure()` 还可以接收很多的参数,你可以根据你自己的需要进行配置。
-
-### 5.1 设置颜色
-
-- `header_color`:设置标题行的颜色。
-- `timestamp_color`:设置时间戳颜色
-- `default_color`:设置默认的颜色
-- `filename_color`:设置文件名颜色
-- `line_number_color`:设置行号颜色。
-- `function_color`:设置函数颜色。
-- `link_color`:设置链接的颜色。
-
-在设置颜色的时候,`pretty_errors` 提供了一些常用的 颜色常量供你直接调取。
-
-- `BLACK`:黑色
-- `GREY`:灰色
-- `RED`:红色
-- `GREEN`:绿色
-- `YELLOW`:黄色
-- `BLUE`:蓝色
-- `MAGENTA`:品红色
-- `CYAN`:蓝绿色
-- `WHITE`:白色
-
-而每一种颜色,都相应的匹配的 `BRIGHT_` 变体 和 `_BACKGROUND` 变体,
-
-其中,`_BACKGROUND` 用于设置背景色,举个例子如下。
-
-
-
-### 5.2 设置显示内容
-
-- `line_number_first`
- 启用后,将首先显示行号,而不是文件名。
-- `lines_before` : 显示发生异常处的前几行代码
-- `lines_after`: 显示发生异常处的后几行代码
-- `display_link`:启用后,将在错误位置下方写入链接,VScode将允许您单击该链接。
-- `separator_character`:用于创建标题行的字符。默认情况下使用连字符。如果设置为 `''` 或者 `None` ,标题将被禁用。
-- `display_timestamp`:启用时,时间戳将写入回溯头中。
-- `display_locals`
- 启用后,将显示在顶部堆栈框架代码中的局部变量及其值。
-
-- `display_trace_locals`
- 启用后,其他堆栈框架代码中出现的局部变量将与它们的值一起显示。
-
-### 5.3 设置怎么显示
-
-- `line_length`:设置每行的长度,默认为0,表示每行的输出将与控制台尺寸相匹配,如果你设置的长度将好与控制台宽度匹配,则可能需要禁用`full_line_newline`,以防止出现明显的双换行符。
-
-- `full_line_newline`:当输出的字符满行时,是否要插入换行符。
-
-- `timestamp_function`
- 调用该函数以生成时间戳。默认值为`time.perf_counter`。
-
-- `top_first`
- 启用后,堆栈跟踪将反转,首先显示堆栈顶部。
-
-- `display_arrow`
- 启用后,将针对语法错误显示一个箭头,指向有问题的令牌。
-
-- `truncate_code`
- 启用后,每行代码将被截断以适合行长。
-
-- `stack_depth`
- 要显示的堆栈跟踪的最大条目数。什么时候`0`将显示整个堆栈,这是默认值。
-
-- `exception_above`
- 启用后,异常将显示在堆栈跟踪上方。
-
-- `exception_below`:
- 启用后,异常显示在堆栈跟踪下方。
-
-- `reset_stdout`
- 启用后,重置转义序列将写入stdout和stderr;如果您的控制台留下错误的颜色,请启用此选项。
-
-- `filename_display`
-
- 设置文件名的展示方式,有三个选项: `pretty_errors.FILENAME_COMPACT` 、`pretty_errors.FILENAME_EXTENDED`,或者`pretty_errors.FILENAME_FULL`
-
-
-
-以上,就是我对 `pretty_errors` 的使用体验,总的来说,这个库功能非常强大,使用效果也特别酷炫,它就跟 PEP8 规范一样,没有它是可以,但是有了它会更好一样。对于某些想自定义错误输出场景的人,`pretty_errors` 会是一个不错的解决方案,明哥把它推荐给你。
-
-
\ No newline at end of file
diff --git a/source/c01/c01_36.rst b/source/c01/c01_36.rst
deleted file mode 100644
index f137980..0000000
--- a/source/c01/c01_36.rst
+++ /dev/null
@@ -1,285 +0,0 @@
-1.36 每日一库:pretty_errors 解决bug 洁癖
-=========================================
-
-当我们写的一个脚本或程序发生各种不可预知的异常时,如果我们没有进行捕获处理的时候,通常都会致使程序崩溃退出,并且会在终端打印出一堆
-**密密麻麻** 的 traceback 堆栈信息来告诉我们,是哪个地方出了问题。
-
-就像这样子,天呐,密集恐惧症要犯了都
-
-|image0|
-
-上面这段 traceback
-
-- 只有黑白两个颜色,无法像代码高亮那样,对肉眼实现太不友好了
-- 无法直接显示报错的代码,排查问题慢人一步,效率太低
-
-那有没有一种办法,可以解决这些问题呢?
-
-当然有了,在 Python
-中,没有什么问题是一个库解决不了的,如果有,那就等你去开发这个库。
-
-今天要介绍的这个库呢,叫做 ``pretty-errors``
-,从名字上就可以知道它的用途,是用来美化错误信息的。
-
-通过这条命令你可以安装它
-
-.. code:: shell
-
- $ python3 -m pip install pretty-errors
-
-1. 环境要求
------------
-
-由于使用了 ``pretty-errors`` 后,你的 traceback
-信息输出,会有代码高亮那样的效果,因此当你在使用测试使用
-``pretty-error`` 时,请确保你使用的终端可以输出带有颜色的字体。
-
-在 windows 上你可以使用 Powershell,cmder 等
-
-在 Mac 上你可以使用自带的终端,或者安装一个更好用的 iTerm2
-
-2. 效果对比
------------
-
---------------
-
-随便写一个没有使用 pretty-errors ,并且报错了的程序,是这样子的。
-
-|image1|
-
-而使用了 pretty_errors 后,报错信息被美化成这样了。
-
-|image2|
-
-是不是感觉清楚了不少,那种密密麻麻带来的焦虑感是不是都消失了呢?
-
-当然这段代码少,你可能还没感受到,那就来看下 该项目在
-Github上的一张效果对比图吧
-
-|image3|
-
-3. 配置全局可用
----------------
-
-可以看到使用了 pretty_errors
-后,无非就是把过滤掉了一些干扰我们视线的无用信息,然后把有用的关键信息给我们高亮显示。
-
-既然既然这样,那 pretty_errors
-应该也能支持我们如何自定义我们选用什么样的颜色,怎么排版吧?
-
-答案是显而易见的。
-
-pretty_errors
-和其他库不太一样,在一定程度上(如果你使用全局配置的话),它并不是开箱即用的,你在使用它之前可能需要做一下配置。
-
-使用这一条命令,会让你进行配置,可以让你在该环境中运行其他脚本时的
-traceback 输出都自动美化。
-
-.. code:: shell
-
- $ python3 -m pretty_errors
-
-|image4|
-
-配置完成后,你再运行任何脚本,traceback 都会自动美化了。
-
-不仅是在我的 iTerm 终端下
-
-|image5|
-
-在 PyCharm 中也会
-
-|image6|
-
-唯一的缺点就是,原先在 PyCharm 中的 traceback 可以直接点击 ``文件路径``
-直接跳转到对应错误文件代码行,而你如果是在 VSCode 可以使用
-下面自定义配置的方案解决这个问题(下面会讲到,参数是:\ ``display_link``\ )。
-
-|image7|
-
-因此,有些情况下,你并不想设置 ``pretty_errors`` 全局可用。
-
-那怎么取消之前的配置呢?
-
-只需要再次输出 ``python -m pretty_errors``\ ,输出入 ``C`` 即可清除。
-
-|image8|
-
-4. 单文件中使用
----------------
-
-取消全局可用后,你可以根据自己需要,在你需要使用 ``pretty-errors``
-的脚本文件中导入\ ``pretty_errors``\ ,即可使用
-
-.. code:: python
-
- import pretty_errors
-
-就像这样
-
-.. code:: python
-
- import pretty_errors
-
- def foo():
- 1/0
-
- if __name__ == "__main__":
- foo()
-
-值得一提的是,使用这种方式,若是你的脚本中,出现语法错误,则输出的异常信息还是按照之前的方式展示,并不会被美化。
-
-因此,为了让美化更彻底,官方推荐你使用 ``python -m pretty_errors``
-
-5. 自定义设置
--------------
-
-上面的例子里,我们使用的都是 ``pretty_errors``
-的默认美化格式,展示的信息并没有那么全。
-
-比如
-
-- 它并没有展示报错文件的绝对路径,这将使我们很难定位到是哪个文件里的代码出现错误。
-- 如果能把具体报错的代码,给我们展示在终端屏幕上,就不需要我们再到源码文件中排查原因了。
-
-如果使用了 ``pretty_errors`` 导致异常信息有丢失,那还不如不使用
-``pretty_errors`` 呢。
-
-不过,可以告诉你的是,\ ``pretty_errors`` 并没有你想象的那么简单。
-
-它足够开放,支持自定义配置,可以由你选择你需要展示哪些信息,怎么展示?
-
-这里举一个例子
-
-.. code:: python
-
- import pretty_errors
-
- # 【重点】进行配置
- pretty_errors.configure(
- separator_character = '*',
- filename_display = pretty_errors.FILENAME_EXTENDED,
- line_number_first = True,
- display_link = True,
- lines_before = 5,
- lines_after = 2,
- line_color = pretty_errors.RED + '> ' + pretty_errors.default_config.line_color,
- code_color = ' ' + pretty_errors.default_config.line_color,
- )
-
- # 原来的代码
- def foo():
- 1/0
-
- if __name__ == "__main__":
- foo()
-
-在你像上面这样使用 ``pretty_errrs.configure``
-进行配置时,抛出的的异常信息就变成这样了。
-
-|image9|
-
-当然了,\ ``pretty_errors.configure()``
-还可以接收很多的参数,你可以根据你自己的需要进行配置。
-
-5.1 设置颜色
-~~~~~~~~~~~~
-
-- ``header_color``\ :设置标题行的颜色。
-- ``timestamp_color``\ :设置时间戳颜色
-- ``default_color``\ :设置默认的颜色
-- ``filename_color``\ :设置文件名颜色
-- ``line_number_color``\ :设置行号颜色。
-- ``function_color``\ :设置函数颜色。
-- ``link_color``\ :设置链接的颜色。
-
-在设置颜色的时候,\ ``pretty_errors`` 提供了一些常用的
-颜色常量供你直接调取。
-
-- ``BLACK``\ :黑色
-- ``GREY``\ :灰色
-- ``RED``\ :红色
-- ``GREEN``\ :绿色
-- ``YELLOW``\ :黄色
-- ``BLUE``\ :蓝色
-- ``MAGENTA``\ :品红色
-- ``CYAN``\ :蓝绿色
-- ``WHITE``\ :白色
-
-而每一种颜色,都相应的匹配的 ``BRIGHT_`` 变体 和 ``_BACKGROUND`` 变体,
-
-其中,\ ``_BACKGROUND`` 用于设置背景色,举个例子如下。
-
-|image10|
-
-5.2 设置显示内容
-~~~~~~~~~~~~~~~~
-
-- ``line_number_first`` 启用后,将首先显示行号,而不是文件名。
-- ``lines_before`` : 显示发生异常处的前几行代码
-- ``lines_after``\ : 显示发生异常处的后几行代码
-- ``display_link``\ :启用后,将在错误位置下方写入链接,VScode将允许您单击该链接。
-- ``separator_character``\ :用于创建标题行的字符。默认情况下使用连字符。如果设置为
- ``''`` 或者 ``None`` ,标题将被禁用。
-- ``display_timestamp``\ :启用时,时间戳将写入回溯头中。
-- ``display_locals``
- 启用后,将显示在顶部堆栈框架代码中的局部变量及其值。
-
-- ``display_trace_locals``
- 启用后,其他堆栈框架代码中出现的局部变量将与它们的值一起显示。
-
-5.3 设置怎么显示
-~~~~~~~~~~~~~~~~
-
-- ``line_length``\ :设置每行的长度,默认为0,表示每行的输出将与控制台尺寸相匹配,如果你设置的长度将好与控制台宽度匹配,则可能需要禁用\ ``full_line_newline``\ ,以防止出现明显的双换行符。
-
-- ``full_line_newline``\ :当输出的字符满行时,是否要插入换行符。
-
-- ``timestamp_function``
- 调用该函数以生成时间戳。默认值为\ ``time.perf_counter``\ 。
-
-- ``top_first`` 启用后,堆栈跟踪将反转,首先显示堆栈顶部。
-
-- ``display_arrow``
- 启用后,将针对语法错误显示一个箭头,指向有问题的令牌。
-
-- ``truncate_code`` 启用后,每行代码将被截断以适合行长。
-
-- ``stack_depth``
- 要显示的堆栈跟踪的最大条目数。什么时候\ ``0``\ 将显示整个堆栈,这是默认值。
-
-- ``exception_above`` 启用后,异常将显示在堆栈跟踪上方。
-
-- ``exception_below``\ : 启用后,异常显示在堆栈跟踪下方。
-
-- ``reset_stdout``
- 启用后,重置转义序列将写入stdout和stderr;如果您的控制台留下错误的颜色,请启用此选项。
-
-- ``filename_display``
-
- 设置文件名的展示方式,有三个选项: ``pretty_errors.FILENAME_COMPACT``
- 、\ ``pretty_errors.FILENAME_EXTENDED``\ ,或者\ ``pretty_errors.FILENAME_FULL``
-
-以上,就是我对 ``pretty_errors``
-的使用体验,总的来说,这个库功能非常强大,使用效果也特别酷炫,它就跟
-PEP8
-规范一样,没有它是可以,但是有了它会更好一样。对于某些想自定义错误输出场景的人,\ ``pretty_errors``
-会是一个不错的解决方案,明哥把它推荐给你。
-
-.. figure:: http://image.python-online.cn/image-20200320125724880.png
- :alt: 关注公众号,获取最新干货!
-
- 关注公众号,获取最新干货!
-
-.. |image0| image:: http://image.python-online.cn/image-20200307210853246.png
-.. |image1| image:: http://image.python-online.cn/image-20200307212823345.png
-.. |image2| image:: http://image.python-online.cn/image-20200307213534278.png
-.. |image3| image:: https://warehouse-camo.cmh1.psfhosted.org/31399c5a034c3989b9e99b35249e8f2f0d40e102/68747470733a2f2f692e696d6775722e636f6d2f306a7045716f622e706e67
-.. |image4| image:: http://image.python-online.cn/image-20200307214742135.png
-.. |image5| image:: http://image.python-online.cn/image-20200307213534278.png
-.. |image6| image:: http://image.python-online.cn/image-20200307215530270.png
-.. |image7| image:: http://image.python-online.cn/image-20200307215834623.png
-.. |image8| image:: http://image.python-online.cn/image-20200307214600749.png
-.. |image9| image:: http://image.python-online.cn/image-20200308121949011.png
-.. |image10| image:: http://image.python-online.cn/image-20200308125431779.png
-
diff --git a/source/c01/c01_37.md b/source/c01/c01_37.md
deleted file mode 100644
index ebdc0c0..0000000
--- a/source/c01/c01_37.md
+++ /dev/null
@@ -1,159 +0,0 @@
-# 1.37 Python 炫技操作:条件语句的七种写法
-
-有的人说 Python 是一门 入门容易,但是精通难的语言,这一点我非常赞同。
-
-Python 语言里有许多(而且是越来越多)的高级特性,是 Python 发烧友们非常喜欢的。在这些人的眼里,能够写出那些一般开发者看不懂的高级特性,就是高手,就是大神。
-
-但你要知道,在团队合作里,炫技是大忌。
-
-为什么这么说呢?我说下自己的看法:
-1. 越简洁的代码,越清晰的逻辑,就越不容易出错;
-2. 在团队合作中,你的代码不只有你在维护,降低别人的阅读/理解代码逻辑的成本是一个良好的品德
-3. 简单的代码,只会用到最基本的语法糖,复杂的高级特性,会有更多的依赖(如语言的版本)
-
-在这个系列里,我将总结列举一下,我所见过的那些炫技操作,今天先来个热身,写一写很简单的条件判断语句里有哪些让人想骂街的炫技操作,在这里,如果你是 Python 发烧友,你可以学到一些写出超酷的代码书写技巧,但学习归学习,希望你区分场景使用。
-
-## 原代码
-
-这是一段非常简单的通过年龄判断一个人是否成年的代码,由于代码行数过多,有些人就不太愿意这样写,因为这体现不出自己多年的 Python 功力。
-```python
-if age > 18:
- return "已成年"
-else:
- return "未成年"
-```
-
-下面我列举了六种这段代码的变异写法,一个比一个还 6 ,单独拿出来比较好理解,放在工程代码里,没用过这些学法的人,一定会看得一脸懵逼,理解了之后,又不经意大呼:**卧槽,还可以这样写?**,而后就要开始骂街了:**这是给人看的代码?** (除了第一种之外)
-
-## 第一种
-
-语法:
-
-```python
- if else
-```
-
-例子
-
-```python
->>> age1 = 20
->>> age2 = 17
->>>
->>>
->>> msg1 = "已成年" if age1 > 18 else "未成年"
->>> print msg1
-已成年
->>>
->>> msg2 = "已成年" if age2 > 18 else "未成年"
->>> print msg2
-未成年
->>>
-```
-
-## 第二种
-
-语法
-
-```python
- and or
-```
-
-例子
-
-```python
->>> msg1 = age1 > 18 and "已成年" or "未成年"
->>> msg2 = "已成年" if age2 > 18 else "未成年"
->>>
->>> print(msg1)
-已成年
->>>
->>> print(msg2)
-未成年
-```
-
-## 第三种
-
-语法
-
-```python
-(, )[condition]
-```
-
-例子
-
-```python
->>> msg1 = ("未成年", "已成年")[age1 > 18]
->>> print(msg1)
-已成年
->>>
->>>
->>> msg2 = ("未成年", "已成年")[age2 > 18]
->>> print(msg2)
-未成年
-```
-
-## 第四种
-
-语法
-
-```python
-(lambda: , lambda:)[]()
-```
-
-例子
-
-```python
->>> msg1 = (lambda:"未成年", lambda:"已成年")[age1 > 18]()
->>> print(msg1)
-已成年
->>>
->>> msg2 = (lambda:"未成年", lambda:"已成年")[age2 > 18]()
->>> print(msg2)
-未成年
-```
-
-## 第五种
-
-语法:
-
-```python
-{True: , False: }[]
-```
-
-例子:
-
-```python
->>> msg1 = {True: "已成年", False: "未成年"}[age1 > 18]
->>> print(msg1)
-已成年
->>>
->>> msg2 = {True: "已成年", False: "未成年"}[age2 > 18]
->>> print(msg2)
-未成年
-```
-
-## 第六种
-
-语法
-
-```python
-(() and (,) or (,))[0]
-```
-
-例子
-
-```python
->>> msg1 = ((age1 > 18) and ("已成年",) or ("未成年",))[0]
->>> print(msg1)
-已成年
->>>
->>> msg2 = ((age2 > 18) and ("已成年",) or ("未成年",))[0]
->>> print(msg2)
-未成年
-```
-
-以上代码,都比较简单,仔细看都能看懂,我就不做解释了。
-
-看到这里,有没有涨姿势了,学了这么久的 Python ,这么多骚操作,还真是活久见。。这六种写法里,我最推荐使用的是第一种,自己也经常在用,简洁直白,代码行还少。而其他的写法虽然能写,但是不会用,也不希望在我余生里碰到会在公共代码里用这些写法的同事。
-
-
\ No newline at end of file
diff --git a/source/c01/c01_37.rst b/source/c01/c01_37.rst
deleted file mode 100644
index af11db0..0000000
--- a/source/c01/c01_37.rst
+++ /dev/null
@@ -1,179 +0,0 @@
-1.37 Python 炫技操作:条件语句的七种写法
-========================================
-
-有的人说 Python 是一门 入门容易,但是精通难的语言,这一点我非常赞同。
-
-Python 语言里有许多(而且是越来越多)的高级特性,是 Python
-发烧友们非常喜欢的。在这些人的眼里,能够写出那些一般开发者看不懂的高级特性,就是高手,就是大神。
-
-但你要知道,在团队合作里,炫技是大忌。
-
-为什么这么说呢?我说下自己的看法: 1.
-越简洁的代码,越清晰的逻辑,就越不容易出错; 2.
-在团队合作中,你的代码不只有你在维护,降低别人的阅读/理解代码逻辑的成本是一个良好的品德
-3.
-简单的代码,只会用到最基本的语法糖,复杂的高级特性,会有更多的依赖(如语言的版本)
-
-在这个系列里,我将总结列举一下,我所见过的那些炫技操作,今天先来个热身,写一写很简单的条件判断语句里有哪些让人想骂街的炫技操作,在这里,如果你是
-Python
-发烧友,你可以学到一些写出超酷的代码书写技巧,但学习归学习,希望你区分场景使用。
-
-原代码
-------
-
-这是一段非常简单的通过年龄判断一个人是否成年的代码,由于代码行数过多,有些人就不太愿意这样写,因为这体现不出自己多年的
-Python 功力。
-
-.. code:: python
-
- if age > 18:
- return "已成年"
- else:
- return "未成年"
-
-下面我列举了六种这段代码的变异写法,一个比一个还 6
-,单独拿出来比较好理解,放在工程代码里,没用过这些学法的人,一定会看得一脸懵逼,理解了之后,又不经意大呼:\ **卧槽,还可以这样写?**\ ,而后就要开始骂街了:\ **这是给人看的代码?**
-(除了第一种之外)
-
-第一种
-------
-
-语法:
-
-.. code:: python
-
- if else
-
-例子
-
-.. code:: python
-
- >>> age1 = 20
- >>> age2 = 17
- >>>
- >>>
- >>> msg1 = "已成年" if age1 > 18 else "未成年"
- >>> print msg1
- 已成年
- >>>
- >>> msg2 = "已成年" if age2 > 18 else "未成年"
- >>> print msg2
- 未成年
- >>>
-
-第二种
-------
-
-语法
-
-.. code:: python
-
- and or
-
-例子
-
-.. code:: python
-
- >>> msg1 = age1 > 18 and "已成年" or "未成年"
- >>> msg2 = "已成年" if age2 > 18 else "未成年"
- >>>
- >>> print(msg1)
- 已成年
- >>>
- >>> print(msg2)
- 未成年
-
-第三种
-------
-
-语法
-
-.. code:: python
-
- (, )[condition]
-
-例子
-
-.. code:: python
-
- >>> msg1 = ("未成年", "已成年")[age1 > 18]
- >>> print(msg1)
- 已成年
- >>>
- >>>
- >>> msg2 = ("未成年", "已成年")[age2 > 18]
- >>> print(msg2)
- 未成年
-
-第四种
-------
-
-语法
-
-.. code:: python
-
- (lambda: , lambda:)[