生死怎能看淡, 不服就是要干
一. 亲身经历
python 是我第一个拿出10分认真的态度去学习的语言.
但是可能需要12分的认真。因为周围毕竟还有一些十分认真的人在尝试着用她解决问题。之前零零散散的坑笔记也懒得往过搬了。
python2: 对 range 的想法
- 于 2016年12月15日16:26:12
一个小时前CTO大人(以下简称杰哥)过来找我说一个很奇怪的问题:同一个脚本检索两台数据库,一台国内一台国外,国外的没出问题,国内的时不时报错。 (讲道理我的第一反应是…觉得国外的服务器香一些。。。)
登到监控服务器 vi+ pdb 一顿调试,问题出在了神奇的地方:
|
|
第一次运行 for i in range
就报出memeryError,很自然的想到是range.
因为python2 的range 返回的还是一个完整的数组, 而推荐使用的xrange
返回的是一个生成器,占用内存空间更小.
一开始我是拒绝的,因为之前一直都是懒的多写那个x
,因为本屁民觉得在2016年的今天,一个多占了几mb内存的range并不可能发生内存溢出错误
像我们这种土豪公司,开发机都是4核8线程 64g内存,怎么可能运行个脚本会发生内存错误?
它真的发生了:
这个故事发生在一个十分尴尬的环境,杰哥把脚本跑在了他自己的开车服务器(aws大流量1g内存1核), 整个脚本的流程是开两个线程,分别从国内集群和国外集群的log_db上查询 » 增量追加到内部处理log。
一个本地日志文件大概是300mb-1g不等的样子。
没错,日志很大,,但又恰好都没有到造成内存溢出的程度,所以就愉快的继续运行了,到了range
这里,localfilesize刚好是一个千万级别的整形数字(假设为1kw),即使是按c的计算方法,不考虑堆栈大小,在内存重要占用空间为:
|
|
同事脑补了触发memoryERROR 两种可能:1. 这个大数组挤爆了栈空间 | 2. 它成了挤爆内存的最后一根稻草
- 于 2016年12月15日20:54:08
上面的脑补不是很靠谱,刚才又做了一堆测试,发现了更多的问题。
测试过程
- 偷了台实验室的计算服务器 :D
|
|
|
|
foo1(x)
结果:
x 的值 | 运行时最高占用 |
---|---|
10 | ‘memoryError’ |
9 | 31138_mb |
8 | 3111_mb |
7 | 77mb |
跟之前口算的理想大小40mb 没差, 可以接受
然后给foo1 做一些修改:
|
|
foo2(x)
结果:
x 的值 | 运行时最高占用 |
---|---|
10 | ‘memoryError’ |
9 | 31138_mb |
8 | 3111_mb |
7 | 235mb |
**比较两次 foo(7)
可怕的事情发生了, range 生成同一内容的两种调用方法会有两种内存占用。 **
至于国外集群不出问题的原因, 国外服务器一天生产的log 比较少,所以文件也比较小。so …
end.
为什么我的super报错?
- 于 2016年12月16日15:06:31
- 问题代码:
|
|
上述代码会报TypeError: super() argument 1 must be type, not classobj
错误
解决办法
class A:
旧的类型写法已经被弃用, 改用class A(object)
万事大吉- 刚才我在想,为什么我不会范这个错误? 因为我已经懒到了根本不会写class… 这就是我的BOP 编程思想: Bed Oriented Programming
二. 搬运wtfpyhon项目 + 自评
项目地址: wtfpy 该项目使用DO WHAT THE FUCK YOU WANT 开源协议。所以应该在不获取授权的情况下搬运应该是没什么事的。
python 2的wft尽量少写。
字符编码
|
|
坑比指数 ⭐️
虽然看起来很魔幻,但是现实生活中能打出两个e的几率其实感觉相当低。必经我特么连cyrillic 码是啥都不知道。。。
缩进 | py-version < 3
|
|
上面的代码会输出 10而不会输出想象的 100
坑比指数⭐️
这码在idea里甚至会报错。。。就算vim党一般在配置里也会显式区分tab与spcace的区别(1tab=4space or 2space)
原因:在python2 的解释器中,1tab = 8space,所以return语句进入了for循环。python3已修复。
python的hash特性
坑比指数⭐️⭐️
eg:
|
|
magic:
|
|
“javascript"终究会被python无情碾压 ;D
python的 list 使用 下标的hash作为实际索引,同时使用hash的还有equal。
如果你希望 5.0 == 5
这个判断句在python中是成立的,就需要接受 hash(5.0) == hash(5)。
同时也应该能接受 javascript被python碾压的结果。
生成器 for x in y if z
坑比指数⭐️⭐🌟
eg:
|
|
Output:
|
|
在generator语法中, in 后面的对象是在声明时被确定的,而 if 后面的语句则会在生成器被执行时才被确定。所以point(list(g))
等价于:
|
|
dict迭代中编辑
坑比指数:⭐️🌟
就在看wftpython 前一天,还收到一个想学python的前端同事抱怨:
|
|
哈哈哈哈这事就这么过去吧。(这个锅python是不能背的)
wtfpy中的eg:
|
|
output:
|
|
tips:永远不曾要尝试在iter dict时尝试添加和修改key.
List的迭代中编辑
坑比指数⭐️🌟
eg:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
list_1 = [1, 2, 3, 4] list_2 = [1, 2, 3, 4] list_3 = [1, 2, 3, 4] list_4 = [1, 2, 3, 4] for idx, item in enumerate(list_1): del item for idx, item in enumerate(list_2): list_2.remove(item) for idx, item in enumerate(list_3[:]): list_3.remove(item) for idx, item in enumerate(list_4): list_4.pop(idx)
output:
1 2 3 4 5 6 7 8
>>> list_1 [1, 2, 3, 4] >>> list_2 [2, 4] >>> list_3 [] >>> list_4 [2, 4]
逐个分析:
list_1 的 enumrate的迭代中, del item 只是删除了 list中对象的副本。所以并不会变。
list_2和list_4在遍历的第一步删除数字1
后list的内容变成了[2, 3, 4]
.这些剩余的元素下标会顺延,比如 2的下标会变成0, 3的变成1,所以,当第二次遍历时,index 为 1的元素3将会被删除。
字符串联结
|
|
|
|
耗时天壤之别。但是坑比等级:⭐️
大三找实习的时候就有hr叔叔问过我你喜欢用什么做字符串联结。本萌新立马说 用➕。然后那个面试没过。加号的慢是因为 python中的str为不可变对象。加法作为一个双目运算符,每次做一次字符串联结都要创建一次新str对象。当对多个string进行联结时,比如 ‘’aa‘’ +‘‘bb’’+“cc”, 会首先生成中间变量 ”aabb“,在由“aabb”+“cc” 得到随后的 “aabbcc”
python内置的字符串填充工具
.format
和%
在做长字符串处理时效率很高, 但是在处理短字符串时表现欠佳。使用join是最好的选择,一次join只会创建一次对象,多目运算神清气爽。
再给join创建 字符串数组的时候 不要使用append,list的动态扩容内存开销极大。
字符串滞留
|
|
坑比等级:⭐️
同样是没什么卵用的字符串机制。在进行多次重复三字符串联结时,使用 += 要比单纯的两个加号快很多。因为在使用+=时 等号右面的s2+s3
对象并不会销毁。
else的扭曲之处
|
|
|
|
for-else会在for循环内无break发生后触发else.
同理 try-except-else 无脑三连在try中语句正确执行后才会触发else.
坑壁指数:⭐️
是是非非——is
|
|
坑比指数:⭐️⭐️⭐️⭐️
is
与 ==
的区别在于, 前者比较的是操作数是否为同一对象,即检查操作数id, 后者考量的是对象的数值是否相等,比如说使用 hash。
这里的现象涉及到了python的实现,当python启动时,整型数字中 -5 到 256的所有数字都会以对象形式预分配到内存中。所以 当 a,b为256 时, 他们都是一个256
对象的引用, a is b
返回True。
而当创建一个 大于256的数字时,则会生成一个新的integer对象。那么第二段case返回False也就理所应当了。
最后的True则涉及到另一个python的实现。当在一行初始化赋值多个变量时,同一值只会被创建一次。a = 257; b = 257
, c, d= 257, 257
这里 a is b
,c is d
都会被判True。
循环内的闭包
|
|
|
|
坑比指数:⭐️⭐️✨
定义在循环中的闭包 如果使用外部循环变量,闭包将会绑定该变量而不是变量值。所以在循环结束后调用闭包时,闭包内使用的循环变量是其遍历结束时的值。
|
|
这种局部函数的写法更加妥当。
- ##被调戏的自加##
input:
|
|
Output:
|
|
嗯,似乎是正常的。下面是核爆现场。
Input:
|
|
Output:
|
|
坑比指数 ⭐️⭐️⭐️✨
a = a + [5, 6, 7, 8]
会生成一个新list赋值给a, 此时的b就成了旧的a对象的唯一引用。
a += [5, 6, 7, 8]
会产生不同的结果是因为这里list的自加实现 使用了extend 直接在原list上添加新对象。 所以 这里a和b此时还是指向的同一个list。
不可变对象被玩弄###
同样是利用了上一条 list 的 自加实现。
|
|
坑比指数:⭐️⭐️⭐️⭐️
这个是连环坑。首先这个自加会被执行并赋值,同时还会报tuple不可变的错误。
我觉得这就是个大bug。。。
消失的外部变量###
|
|
|
|
坑比指数:⭐️
虽然很奇葩。但是如果按照规范coding是不会犯这种错误的。
这个现象涉及到expect as 语法的实现逻辑,当离开except 上下文时,会有一个隐式的del 语句将e删除.
一个正常的except语句:
|
|
会被解析成酱:
|
|
(挑着翻译看心情。插眼 2017.09.05, 第二次更新 2017.09.18)