首发于微信公众号: Python 编程时光
'
每周三更新五个冷知识, 欢迎前往订阅!
01. 交互式 "_" 操作符
对于 _ , 我想很多人都非常熟悉.
给变量取名好艰难, 用 _; 懒得长长的变量名, 用 _; 无用的垃圾变量, 用 _;
以上, 我们都很熟悉了, 今天要介绍的是他在交互式中使用.
- >>> 3 + 4
- 7
- >>> _
- 7
- >>> name='ming'
- >>> name
- 'ming'
- >>> _
- 'ming'
它可以返回上一次的运行结果.
但是, 如果是 print 函数打印出来的就不行了.
- >>> 3 + 4
- 7
- >>> _
- 7
- >>> print("ming")
- ming
- >>> _
- 7
我自己写了个例子, 验证了下, 用__repr__输出的内容可以被获取到的. 首先, 在我们的目录下, 写一个文件 http://ming.py . 内容如下
- # ming.py
- class mytest():
- def __str__(self):
- return "hello"
- def __repr__(self):
- return "world"
然后在这个目录下进入交互式环境.
- >>> import ming
- >>> mt=ming.mytest()
- >>> mt
- world
- >>> print(mt)
- hello
- >>> _
- world
知道这两个魔法方法的人, 一看就明白了.
02. 反转字符串 / 列表最优雅的方式
反转序列并不难, 但是如何做到最优雅呢?
先来看看, 正常是如何反转的.
最简单的方法是使用列表自带的 reverse()方法.
- >>> ml = [1,2,3,4,5]
- >>> ml.reverse()
- >>> ml
- [5, 4, 3, 2, 1]
但如果你要处理的是字符串, reverse 就无能为力了. 你可以尝试将其转化成 list, 再 reverse, 然后再转化成 str. 转来转去, 也太麻烦了吧? 需要这么多行代码(后面三行是不能合并成一行的), 一点都 Pythonic.
- mstr1 = 'abc'
- ml1 = list(mstr1)
- ml1.reverse()
- mstr2 = str(ml1)
对于字符串还有一种稍微复杂一点的, 是自定义递归函数来实现.
- def my_reverse(str):
- if str == "":
- return str
- else:
- return my_reverse(str[1:]) + str[0]
在这里, 介绍一种最优雅的反转方式, 使用切片, 不管你是字符串, 还是列表, 简直通杀.
- >>> mstr = 'abc'
- >>> ml = [1,2,3]
- >>> mstr[::-1]
- 'cba'
- >>> ml[::-1]
- [3, 2, 1]
03. 改变默认递归次数限制
上面才提到递归, 大家都知道使用递归是有风险的, 递归深度过深容易导致堆栈的溢出. 如果你这字符串太长啦, 使用递归方式反转, 就会出现问题.
那到底, 默认递归次数限制是多少呢?
- >>> import sys
- >>> sys.getrecursionlimit()
- 1000
可以查, 当然也可以自定义修改次数, 退出即失效.
- >>> sys.setrecursionlimit(2000)
- >>> sys.getrecursionlimit()
- 2000
04. 一行代码实现 FTP 服务器
搭建 FTP, 或者是搭建网络文件系统, 这些方法都能够实现 Linux 的目录共享. 但是 FTP 和网络文件系统的功能都过于强大, 因此它们都有一些不够方便的地方. 比如你想快速共享 Linux 系统的某个目录给整个项目团队, 还想在一分钟内做到, 怎么办? 很简单, 使用 Python 中的 SimpleHTTPServer.
SimpleHTTPServer 是 Python 2 自带的一个模块, 是 Python 的 web 服务器. 它在 Python 3 已经合并到 http.server 模块中. 具体例子如下, 如不指定端口, 则默认是 8000 端口.
- # python2
- python -m SimpleHTTPServer 8888
- # python3
- python3 -m http.server 8888
SimpleHTTPServer 有一个特性, 如果待共享的目录下有 index.html, 那么 index.HTML 文件会被视为默认主页; 如果不存在 index.HTML 文件, 那么就会显示整个目录列表.
05. 让你晕头转向的 else 用法
if else 用法可以说最基础的语法表达式之一, 但是今天不是讲这个的, 一定要讲点不一样的.
if else 早已烂大街, 但可能有很多人都不曾见过 for else 和 try else 的用法. 为什么说它曾让我晕头转向, 因为它不像 if else 那么直白, 非黑即白, 脑子经常要想一下才能才反应过来代码怎么走. 反正我是这样的.
先来说说, for else
- 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?
给几个例子, 你体会一下.
- check_item(["apple", "huawei", "oppo"], "oppo")
- # Exists!
- check_item(["apple", "huawei", "oppo"], "vivo")
- # Does not exist
可以看出, 没有被 break 的程序才会正常走 else 流程.
再来看看, try else 用法.
- def test_try_else(attr1 = None):
- try:
- if attr1:
- pass
- else:
- raise
- except:
- print("Exception occurred...")
- else:
- print("No Exception occurred...")
同样来几个例子. 当不传参数时, 就抛出异常.
- test_try_else()
- # Exception occurred...
- test_try_else("ming")
- # No Exception occurred...
可以看出, 没有 try 里面的代码块没有抛出异常的, 会正常走 else.
总结一下, for else 和 try else 相同, 只要代码正常走下去不被 break, 不抛出异常, 就可以走 else.
06. 空字符串计数
求一个字符串里, 某子字符 (串) 出现的次数. 在 Python 中使用 count() 函数, 就可以轻松实现.
比如下面几个常规例子
- >>> "aabb".count("a")
- 2
- >>> "aabb".count("b")
- 2
- >>> "aabb".count("ab")
- 1
但是如果使用空字符串呢, 你可能想不到会是这样的结果.
- >>> "aabb".count("")
- 5
具体原因, 我不敢妄下结论.
由此我还衍生出另一个想法, 实验了下. 不知道空字符串, 是一种什么样的存在, 难道字母与字母之间 "缝隙" 也算吗?
- >>> ""in""
- True
- >>> ""in"ab"
- True
有兴趣的可以去看看 CPython 的源码实现.
07. 负负得正
从初中开始, 我们就开始接触了负数 这个概念. 知道了负负得正, 这和武侠世界里的以毒功毒, 有点神似.
Python 作为一门高级语言, 它的编写符合人类的思维逻辑, 这其中也包括负负得正这个思想.
- >>> 5-3
- 2
- >>> 5--3
- 8
- >>> 5+-3
- 2
- >>> 5++3
- 8
- >>> 5---3
- 2
08. 数值与字符串的比较
在 Python2 中, 数字可以与字符串直接比较. 结果是数值永远比字符串小.
- >>> 100000000 <""
- True
- >>> 100000000 <"ming"
- True
但在 Python3 中, 却不行.
>>> 100000000 <""TypeError:'<'not supported between instances of'int'and'str'
09. 循环中的局部变量泄露
在 Python 2 中 x 的值在一个循环执行之后被改变了.
- # Python2
- >>> x = 1
- >>> [x for x in range(5)]
- [0, 1, 2, 3, 4]
- >>> x
- 4
不过在 Python3 中这个问题已经得到解决了.
- # Python3
- >>> x = 1
- >>> [x for x in range(5)]
- [0, 1, 2, 3, 4]
- >>> x
- 1
10. 字典可排序
字典不可排序的思想, 似乎已经根深蒂固.
- # Python2.7.10
- >>> mydict = {
- str(i):i for i in range(5)
- }
- >>> mydict
- {
- '1': 1, '0': 0, '3': 3, '2': 2, '4': 4
- }
在 Python3 中字典已经是有序的.
- # Python3.6.7
- >>> mydict = {
- str(i):i for i in range(5)
- }
- >>> mydict
- {
- '0': 0, '1': 1, '2': 2, '3': 3, '4': 4
- }
11. 链式比较
先给看一个示例
- >>> False == False == True
- False
你知道这个表达式会返回 False 吗?
我再给你举个例子, 你可能就懂了.
- f 18 <age < 60:
- print("young man")
如果还不明白, 再给你整个等价写法.
- >>> False == False and False == True
- False
12. 奇怪的字母
直接看下列例子.
在 Python 2.x 中
>>> value = 11
>>> valuе = 32
File "<stdin>", line 1
valuе = 32
- ^
- SyntaxError: invalid syntax
在 Python 3.x 中
>>> value = 11
>>> valuе = 32
>>> value
11
我相信你一开始看到这里, 一定是目瞪口呆. 你可以在自己的电脑上尝试一下, 你会发现你不管在哪个版本的 Python 里运行都没有问题.
如果你想重现我这个场景, 你可能复制我上面的代码粘贴至自己的命令行中即可.
在这里, 也不卖关子了, 上面代码中第二行的 е 和 第一行的 e 是不一样的.
第二行的 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, 到时候连错都不知道错哪了哈哈.
13. 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
- })
14. 有趣的 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
就会自动打开一个网页.
15. 局部 / 全局变量分不清?
在开始讲之前, 你可以试着运行一下下面这小段代码.
- a = 1
- def func01():
- a += 1
- func01()
看似没有毛病, 但实则已经犯了一个很基础的问题, 这个报错相当常见吧?
- >>> func01()
- Traceback (most recent call last):
- File "<stdin>", line 1, in <module>
- File "<stdin>", line 2, in func01
- UnboundLocalError: local variable 'a' referenced before assignment
回顾一下, 什么是局部变量? 在非全局下定义声明的变量都是局部变量.
当程序运行到 a += 1 时, Python 解释器就认为在函数内部要给 a 这个变量赋值, 当然就把 a 当做局部变量了, 报错是理所当然的.
参考文档
来源: https://juejin.im/post/5bf16f33e51d4512d23d08b4