原创博客链接:python进阶17炫技巧
原则:可读性第一(效率固然重要,除非非常明显的效率差异,否则可读性优先)
学习炫技巧,更多为了读懂他人代码,自己开发过程中,相似代码量(可读性),建议使用通俗写法。反对为炫而炫。
有 Python 包,居然可以以 zip 包进行发布,并且可以不用解压直接使用。
这个zip 是如何制作的呢,请看下面的示例。
1 2 3 4 5 6 7 8 9 10 11 12 |
[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 ~]# cat demo/__main__.py import calc print(calc.add(2, 3)) [root@localhost ~]# cat demo/calc.py def add(x, y): return x+y [root@localhost ~]# python -m zipfile -c demo.zip demo/* |
制作完成后,我们可以执行用 python 去执行它
1 2 |
[root@localhost ~]# python demo.zip 5 |
大家对于他的印象都是用于 占位符,省得为一个不需要用到的变量,绞尽脑汁的想变量名。
今天要介绍的是他的第二种用法,就是在 console 模式下的应用。
示例如下:
1 2 3 4 5 6 7 8 9 |
>>> 3 + 4 7 >>> _ 7 >>> name='公众号: Python编程时光' >>> name '公众号: Python编程时光' >>> _ '公众号: Python编程时光' |
它可以返回上一次的运行结果。
但是,如果是print函数打印出来的就不行了。
1 2 3 4 5 6 7 8 |
>>> 3 + 4 7 >>> _ 7 >>> print("公众号: Python编程时光") ming >>> _ 7 |
1 2 3 4 5 6 7 8 9 10 11 12 |
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 |
当一个 or 表达式中所有值都为真,Python会选择第一个值
当一个 and 表达式 所有值都为真,Python 会选择第二个值。
示例如下:
1 2 |
>>>(2 or 3) * (5 and 7) 14 # 2*7 |
函数的参数分三种 - 可变参数 - 默认参数 - 关键字参数
当你在传递默认参数时,有新手很容易踩雷的一个坑。
先来看一个示例
1 2 3 4 5 6 7 |
def func(item, item_list=[]): item_list.append(item) print(item_list) func('iphone') func('xiaomi', item_list=['oppo','vivo']) func('huawei') |
在这里,你可以暂停一下,思考一下会输出什么?
思考过后,你的答案是否和下面的一致呢
1 2 3 |
['iphone'] ['oppo', 'vivo', 'xiaomi'] ['iphone', 'huawei'] |
如果是,那你可以跳过这部分内容,如果不是,请接着往下看,这里来分析一下。
Python 中的 def 语句在每次执行的时候都初始化一个函数对象,这个函数对象就是我们要调用的函数,可以把它当成一个一般的对象,只不过这个对象拥有一个可执行的方法和部分属性。
对于参数中提供了初始值的参数,由于 Python 中的函数参数传递的是对象,也可以认为是传地址,在第一次初始化 def 的时候,会先生成这个可变对象的内存地址,然后将这个默认参数 item_list 会与这个内存地址绑定。在后面的函数调用中,如果调用方指定了新的默认值,就会将原来的默认值覆盖。如果调用方没有指定新的默认值,那就会使用原来的默认值。
1 2 3 |
# 调用私有方法,以下两种等价 ins._Kls__private() ins.call_private() |
alist 只有5 个元素,当你取第 6 个元素时,会抛出索引异常。这与我们的认知一致。
1 2 3 4 5 |
>>> alist = [0, 1, 2, 3, 4] >>> alist[5] Traceback (most recent call last): File " |
但是当你使用 alist[5:] 取一个区间时,即使 alist 并没有 第 6个元素,也不抛出异常,而是会返回一个新的列表。
1 2 3 4 5 |
>>> alist = [0, 1, 2, 3, 4] >>> alist[5:] [] >>> alist[100:] [] |
那结论就出来了,如果 finally 里有显式的 return,那么这个 return 会直接覆盖 try 里的 return,而如果 finally 里没有 显式的 return,那么 try 里的 return 仍然有效。
Python 定义了一个小整数池 [-5, 256] 这些整数对象是提前建立好的,不会被垃圾回收。
python3 -m http.server 8888
for else 和 try else 相同,只要代码正常走下去不被 break,不抛出异常,就可以走else。(所以和去掉else,直接写后面没区别?)
优点在于,可以识别正常退出还是break退出(一般业务含义不同)
with test_context(‘aaa’), test_context(‘bbb’):
print(‘========== in main ============’)
1 2 3 4 5 |
>>> b = [3,4] >>> c = [5,6] >>> >>> sum((a,b,c), []) [1, 2, 3, 4, 5, 6] |
另外几种连接列表的方式
1 2 3 4 5 6 7 8 9 |
>>> list01 + list02 + list03 [1, 2, 3, 4, 5, 6, 7, 8, 9] >>> list(chain(list01, list02, list03)) >>> [*list01, *list02] >>> list01.extend(list02) >>> [x for l in (list01, list02, list03) for x in l] >>> from heapq import merge >>> list(merge(list01, list02, list03)) sorted(itertools.chain(*iterables)) |
使用 atexit 这个内置模块,可以很方便的注册退出函数。
如果clean()函数有参数,那么你可以不用装饰器,而是直接调用atexit.register(clean_1, 参数1, 参数2, 参数3=’xxx’)。
1 2 3 4 5 |
profile.update(ext_info) full_profile01 = {**profile, **ext_info} dict(itertools.chain(profile.items(), ext_info.items())) dict(ChainMap(profile, ext_info)) full_profile = dict(profile.items() | ext_info.items()) |
1 2 3 4 5 6 7 8 9 10 |
|
当你在机器上并没有 root 权限时,如何安装 Python 的第三方包呢?
可以使用 pip install –user pkg 将你的包安装在你的用户环境中,该用户环境与全局环境并不冲突,并且多用户之间相互隔离,互不影响。
样例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
import contextlib @contextlib.contextmanager def open_func(file_name): # __enter__方法 print('open file:', file_name, 'in __enter__') file_handler = open(file_name, 'r') try: yield file_handler except Exception as exc: # deal with exception print('the exception was thrown') finally: print('close file:', file_name, 'in __exit__') file_handler.close() return with open_func('/Users/MING/mytest.txt') as file_in: for line in file_in: 1/0 print(line) |
cat test.json | python -m json.tool
1 2 3 4 5 |
>>> sh.glob("/etc/*.conf") ['/etc/mke2fs.conf', '/etc/dnsmasq.conf', '/etc/asound.conf'] >>> r=sh.Command('/root/test.py') >>> r() hello,world |
1 2 3 4 5 |
1. 使用 in 和 not in 2. 使用 find 方法 3. 使用 index 方法 4. 使用 count 方法 5,借助 operator |
operator模块是python中内置的操作符函数接口,它定义了一些算术和比较内置操作的函数。operator模块是用c实现的,所以执行速度比 python 代码快。
在 operator 中有一个方法 contains 可以很方便地判断子串是否在字符串中。
1 2 3 |
>>> import operator >>> >>> operator.contains("hello, python", "llo") |
打印中文在 json.dumps 这里,却只要加个参数就好了
具体的代码示例如下:
1 2 |
>>> import json >>> print json.dumps(info, indent=4, ensure_ascii=False) |
JIT即时编译器,当虚拟机发现某个方法或代码块运行特别频繁时,就会把这些代码认定为热点代码,为了提高热点代码的运行效率,在运行时,虚拟机将会把这些代码编译成与本地平台的相关带代码,并进行各层次的优化。
正如上面所说的,默认情况下,Python的新式类和经典类的实例都有一个dict来存储实例的属性。这在一般情况下还不错,而且非常灵活,
乃至在程序中可以随意设置新的属性。但是,对一些在”编译”前就知道有几个固定属性的小class来说,这个dict就有点浪费内存了。
当需要创建大量实例的时候,这个问题变得尤为突出。一种解决方法是在新式类中定义一个slots属性。
slots声明中包含若干实例变量,并为每个实例预留恰好足够的空间来保存每个变量;这样Python就不会再使用dict,从而节省空间。
来个真的有点高(黑)级(暗)的技巧吧,开发一个 stateful service 的时候怎么排障?比如出现死锁了,或者某个线程意外退出。这个时候可以从
gc.get_objects()
掏出所有活着的对象,其中当然就包括活着的线程。
例如,打印出所有 Greenlet 的 stack:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
import os import gc import greenlet import traceback greenlets = [ o for o in gc.get_objects() if isinstance(o, greenlet.greenlet) if o] stack = '\n\n'.join( ''.join(traceback.format_stack(o.gr_frame)) for o in greenlets) open('/tmp/stack-%d.txt' % os.getpid(), 'w').write(stack) ``` 这个方法结合 gevent.backdoor 堪称排障利器。如果愿意冒一定的风险,甚至可以使用调试器对事先没有埋过点的进程做活体检测——挂上 GIL 再打印一份线程 stack。详见 GitHub - wooparadog/pstack: Tool to dump python thread and greenlet stacks. ## 调试利器,显示调用栈 有时候BUG隐藏的太深,需要对上下文都有清晰的展示来帮助判断。用pdb调试不方便,用print不直观。可以使用如下函数获取当前调用栈: ``` import sys def get_cur_info(): print sys._getframe().f_code.co_filename # 当前文件名 print sys._getframe(0).f_code.co_name # 当前函数名 print sys._getframe(1).f_code.co_name # 调用该函数的函数的名字,如果没有被调用,则返回module print sys._getframe().f_lineno # 当前行号 |
1 2 3 4 5 6 7 8 9 10 11 |
def testa(*v): print(list(v)) testa(1, 2, 3) testa(list('abc')) testa('a', 'b', list('cde')) [1, 2, 3] [['a', 'b', 'c']] ['a', 'b', ['c', 'd', 'e']] |
Python 黑魔法指南 50 例:python.iswbm.com/en/latest/c01/c01_10.html
Python 炫技操作:连接列表的八种方法:python.iswbm.com/en/latest/c01/c01_41.html
Python 炫技操作:判断是否包含子串的七种方法:python.iswbm.com/en/latest/c01/c01_40.html
Python 炫技操作:合并字典的七种方法:python.iswbm.com/en/latest/c01/c01_39.html
Python 炫技操作:条件语句的七种写法:python.iswbm.com/en/latest/c01/c01_37.html
每日一库:sh,最优雅的命令调用方式:python.iswbm.com/en/latest/c01/c01_34.html
Python 开发中有哪些高级技巧?:https://www.zhihu.com/question/23760468
python slots 详解(上篇):https://blog.csdn.net/sxingming/article/details/52892640