Mars6364
2022/02/20阅读:64主题:默认主题
Python从入门到放弃-冷知识分享
魔法冷知识
默默无闻的省略号
-
默默无闻的省略号 在Python中,一切皆对象,省略号也不例外。在python中你开业直接写 "..." 来得到它
>>> ...
Ellipsis
>>> print(type(...))
<class 'ellipsis'>注意:这个特性在Python2中是没有的
它在转换为布尔值的时候为真
>>> bool(...)
True最后,这个省略号对象还是一个单例(单例的相关知识点也是很重要的,面试中也会经常问到,后续我也会整理分享一下)
>>> id(...)
94705372553616
>>> id(...)
94705372553616
>>>
>>>那么问题来了:这东西花里花哨的 有什么用呢?
-
它是numpy的一个语法糖
-
在Python3中可以使用... 代替 pass
使用end来结束代码块
有很多编程语言在循环、判断的时候需要用end表面结束,这样一定程度上能够使得代码逻辑更为清晰,但是在Python这样对缩进严格要求的语言中并没有必要这样做。
如果你真的想用,也不是没有办法,具体参考下面的实例。
__builtins__.end = None
def index(x):
if x > 0:
print("大于0")
else:
print("小于0")
end
end
可以直接运行的zip包
我们可以经常看到python包以zip形式发布,并且可以不用解压就直接使用,我最第一次看到这种使用方式有点不理解,因为我认识中的python包要么就是egg格式,要么就是whel格式的。
那么这种zip包是如何制作的呢,请看下面的实例,

反斜杠的倔强:不写最后
\ 在python中主要用法两种
-
在行尾的时候,用作续行符号
>>> print("hello "\
... "world")
hello world
>>>
>>> -
在字符串中,用作转义字符,可以把普通字符转换为带有特殊含义的字符。
>>> str1='\nhello' # 换行
>>> print(str1)
hello
>>>
>>>
>>>
>>> str2='\thello' # 制表符
>>> print(str2)
hello
>>>
>>>但是如果使用 单 \ 结尾 是会报语法错误的
>>> str3="\"
File "<stdin>", line 1
str3="\" ^
SyntaxError: EOL while scanning string literal即使你指定它是一个raw字符串,依然会报错
>>> str3=r"\"
File "<stdin>", line 1
str3=r"\" ^
SyntaxError: EOL while scanning string literal
如何修改解释器提示符号
这个当做今天的一个小菜单吧。应该是比较冷门的。
正常情况下我们在终端下面执行python命令的时候是这样的
>>> for i in range(2):
... print (i)
...
0
1
那么,里面的箭头和省略号是否可以被修改呢?
>>> import sys
>>> sys.ps1
'>>> '
>>> sys.ps2
'... '
>>>
>>> sys.ps2 = '---------------- '
>>> sys.ps1 = '程序员云梦>>>'
程序员云梦>>>for i in range(2):
---------------- print (i)
----------------
0
1
简洁而优雅的链式比较
先看一个实例:
>>> False == False == False
True
>>>
为什么这个表达式会返回False呢?它的运行原理和下面这个类似,是不是明白了:
if 80 < score < 90:
print("成绩良好")
如果还是不明白,那你看下面下面一个例子的等价写法。
>>> False == False and False == True
False
这个用法叫做链式比较。
and 和 or 的短路效应
and和or是我们经常用到的两个逻辑运算符,在Python中它也有妙用。
-
当一个or表达式中所有值都为真的时候,Python会选择第一个值。 -
当一个and表达式所有值都为真的时候,Python会选择最后一个值。
实例如下:
>>>(2 or 3) * (5 and 6 and 7)
14 # 2*7
合并多个列表最极客的方式
>>> a = [1,2]
>>> b = [2,3]
>>> c = [4,5]
>>>
>>>
>>> sum((a,b,c),[])
[1, 2, 2, 3, 4, 5]
>>>
Python中的字典是有序的
在Python3.6之前字典都是不可排序的,是无序的。
但是 在Python3.6+后面的版本中,字典都是有序的,并且效率相比较之前的还有所提示,具体的相关信息开业去查询相关资料。
## Python3.6.7
>>> mydict = {str(i):i for i in range(5)}
>>> mydict
{'0': 0, '1': 1, '2': 2, '3': 3, '4': 4}
用户无感知的小整数池
Python定义了一个小整数池[-5,256],这些整数对象是提前建立好的,不会被垃圾回收。
以下代码是在Python终端环境下测试实现,如果在IDE中测试,测试结果可能会有差距。
>>> 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?
因为在最后一个实例赋值的时候,是同时给两个变量赋同一个值,解释器知道这个数据对象已经生成,那么他就会引用到同一个对象。如果分成两行的话,解释器并不知道这个对象已经存在,就会重新申请内存存放这个对象。
那么问题又来了:实例代码中可以验证到数据对象其实是存储是数据引用地址,那么Python为什么要这样设计呢?有什么优点呢?有什么缺点呢?
>>> a=8
>>> b=8
>>> id(a)
94786084233984
>>> id(b)
94786084233984
>>>
>>>
>>>
>>> c=256
>>> d=256
>>> id(c)
94786084241920
>>> id(d)
94786084241920
>>>
>>>
>>>
>>> e=257
>>> f=257
>>> id(e)
140620066601872
>>> id(f)
140620066051312
>>>
优点:小整数存在的覆盖了很多常用区间范围内的数字,此处可以联想到到Python的垃圾回收机制里面的引用计数原理,即使多个对象使用同一个数值,也只是改数据的引用计数+1,不会在使用的时候不需要去频繁的开辟新的内存,消耗内存空间。
缺点:如果使用过程中涉及到大量的计算,其实并不是对数据本身进行了计算,而且开辟了一块新的内存空间,创建了新的对象。
神奇的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
site-packages和dist-packages
如果你足够细心,你会发现在你的机器上,有些包是安装在site-packages下,有些是在dist-pacjages下面。
他们有什么区别呢?
一般情况下,我们可能在IDE中会看到site-packages。因为我们pip安装的包是安装在这个目录下面。
而disr-packges其实是debian系统的Linux系统(比如Ubuntu)才有的目录,当你使用apt去安装的Python包会使用dist-packges,而你使用pip或者easy_install安装的包还是照常在site-packages下。
之所以这样设计,是为了减少不同来源的Python之间产生冲突。
>>> from distutils.sysconfig import get_python_lib
>>> print(get_python_lib())
/usr/local/python3/lib/python3.7/site-packages
>>>
argument和parameter的区别
字面意思 两个单词的翻译都是参数,在中文使用场景目录下,二者混用基本没有问题,毕竟都是参数。
但是既然有两个相近的存在,那么就必然会有一点差距,类比MySQL的char和varchar数据类型。
那么这两个“参数”有什么区别呢?
-
parameter:形参(formal parameter),体现在函数内部,作用域是这个函数体。 -
argument:实参(actual parameter),调用函数实际传递的参数。 举个例子:
def output_msg(msg):
print(msg)
output_msg("error")
这个例子中,“error”是argument,而“msg”是parameter
dict()和{}生成空字典有什么区别?
在初始化一个字典的时候,有的人会写dict,有的人会写{},
最开始我以为二者是相同的,但实际上有很大区别。
在运行效率上,后者会比前者快三倍左右。
具体看下面的实例:
➜ ~ python -m timeit -n 1000000 -r 5 -v "dict()"
raw times: 0.133 0.115 0.108 0.105 0.1
1000000 loops, best of 5: 0.1 usec per loop
➜ ~
➜ ~
➜ ~
➜ ~ python -m timeit -n 1000000 -r 5 -v "{}"
raw times: 0.0374 0.03 0.0311 0.0299 0.0343
1000000 loops, best of 5: 0.0299 usec per loop
➜ ~
➜ ~
➜ ~
那为什么会这样呢?
可以使用dis模块对比一下。
当使用{}的时候。
➜ ~ cat demo.py
{}
➜ ~
➜ ~ python -m dis demo.py
1 0 BUILD_MAP 0
2 POP_TOP
4 LOAD_CONST 0(None)
6 RETURN_VALUE
当使用dict()的时候:
➜ ~ cat demo.py
dict()
➜ ~
➜ ~ python -m dis demo.py
1 0 LOAD_NAME 0(dict)
2 CALL_FUNCTION 0
4 POP_TOP
6 LOAD_CONST 0(None)
8 RETURN_VALUE
可以发现使用dict(),会多了一个调用函数的过程,而这个过程会有进出栈的操作,相对更耗时。
return不一定都是函数的终点
众所周知,try。。。finally。。。的用法:不管try的代码是正常执行还是报异常,finally里面的代码都会执行。
同时我们也知道,一个函数遇到return函数救护立马结束。
那么问题来了,如果这两种规则同时存在,Python解释器会如何选择呢?谁的优先级会更高呢?
看下面的实例:
>>> def func():
try:
return 'try'
finally:
return 'finally'
>>> func()
'finally'
从程序的输出结果可以看到,在上面的例子中,try中的return会被直接忽视(这里的return不是函数的终点),只是要保证finally能够执行。
那么 try中的return真的是被直接忽略吗?
我们都知道一个函数如果没有return,会隐式的返回None,假设try里面的return真的是被直接忽视,当finally没有显示的return会不会范湖None呢。
上代码!
>>> def func():
try:
return 'try'
finally:
print('finally')
>>> func()
'try'
从结果来看,当finally里面没有return的时候,其实try里面的return仍然还是有效的。
那么结论就有了,如果finally里面有显示的return,那么这个return会直接覆盖try里面的return;而如果finally里面没有显示的return,那么try里面的return还是有效的。
时有时无的切片异常
看下面的实例,一个列表只有五个元素,如果根据下标取第六个元素的时候,会抛出索引异常。这很正常,与我们预料的结果一致,
>>> alist = [0, 1, 2, 3, 4]
>>> alist[5]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
IndexError: list index out of range
但是 当使用区间取值的时候,即使区间范围超出列表长度,也不会抛出异常,而是会返回一个新的列表。
>>> alist = [0, 1, 2, 3, 4]
>>> alist[5:]
[]
>>> alist[100:]
[]
谜一样的字符串
-
实例1
## Python2.7
>>> a = "Hello_Python"
>>> id(a)
32045616
>>> id("Hello" + "_" + "Python")
32045616
## Python3.7
>>> a = "Hello_Python"
>>> id(a)
38764272
>>> id("Hello" + "_" + "Python")
32045616 -
实例2
>>> 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 -
实例3
## Python2.7
>>> 'a' * 20 is 'aaaaaaaaaaaaaaaaaaaa'
True
>>> 'a' * 21 is 'aaaaaaaaaaaaaaaaaaaaa'
False
## Python3.7
>>> 'a' * 20 is 'aaaaaaaaaaaaaaaaaaaa'
True
>>> 'a' * 21 is 'aaaaaaaaaaaaaaaaaaaaa'
True
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})
+= 不等同于 =+
对列表进行+=操作相当于执行extend,但是使用=+操作是新增了一个列表。
因此会有以下两者的差异:
## =+
>>> 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]
循环中的局部变量泄露
在python2版本中 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
break和continue和上下文管理器那个优先级更高
众所周知,在循环体中(无论是for还是while),continue会用来跳入下一个循环,而break用来跳出某个循环体。
同时我们知道,在上下文管理器中,被包裹的程序主题代码结束会运行上下文管理器中的一段代码(通常是资源的释放)
但是如果把上下文管理器放在一个循环体中,然后在这个上下文管理器中执行了break,是否会直接跳出循环呢,
换句话说 break和continue和上下文管理器那个优先级更高一些?
实验代码如下:
import time
import contextlib
@contextlib.contextmanager
def runtime(value):
time.sleep(1)
print("start: a = " + str(value))
yield
print("end: a = " + str(value))
a=0
while True:
a+=1
with runtime(a):
if a % 2 == 0:
break
从输出的结果来看,当a=2的时候执行了break,此时并不会直接跳出循环,仍然要运行上下文管理器里面释放资源的代码,此处使用打印代替。
start: a = 1
end: a = 1
start: a = 2
end: a = 2
另外还有几个类似的问题,
-
continue和break一样,如果先遇到上下文管理器会先进行资源的释放, -
上面只距离了while的循环体,而for循环也是同样的。
如果想awk一样分割字符串?
如果使用过shell的awk工具,你会发现使用awk分割字符串是非常方便的。特别少多个连续的空格会被当做一个处理。
➜ ~ cat demo.txt
hello world
➜ ~
➜ ~ awk '{print$1,$2}' demo.txt
hello world
但是 转换到python上面来,结果是这样的:
>>> msg='hello world'
>>> msg.split(' ')
['hello', '', '', '', 'world']
如上所示,多个空格会被分割多次。
那么如何实现awk一样的效果呢。
两种方法
-
第一种方法: 不加参数,这种只适用于将多个空格当初一个空格处理的场景,如果不是一空格为分隔符的应用场景,这种就不适合了。
>>> msg='hello world'
>>> msg.split()
['hello', 'world'] -
第二种方法 使用filter来辅助,这种使用与所有的分隔符,下面一“-”为分隔符来举例:
>>> msg='hello----world'
>>> msg.split('-')
['hello', '', '', '', 'world']
>>>
>>> filter(None, msg.split('-'))
['hello', 'world']是不是很神奇,filter印象中第一个参数接受的是函数,这里传None居然有奇效。 查看了源代码以后,发现原来这个函数会适配None的情况,当第一个参数是空的时候,返回第二个参数(可迭代对象)中非空的值,非常方便。
这里顺便回顾一下filter()函数的用法:
用法:filter函数用于过滤序列,过滤掉不符合条件的元素,返回复合条件的元素构成新列表。
filter语法如下:
filter(function,iterable)
# 其中function为函数,iterable为序列 或者说是可迭代对象
序列中的每个元素都会作为参数传递给函数进行判断,返回True或者False,最后想返回True的元素放到新列表中。
filter用法实例:
筛选出序列中为奇数的元素:
def is_odd(n):
return n%2 == 1
lst1 = filter(is_odd,[1,2,3,4,5,6,7,8,9,10])
# lst = [1,3,5,7,9]
如何让大数据变的更易于阅读?
当一个数据非常大的时候,可能过百万,也可能上亿,太多位的数字,可读性就会变得很差,给我们的阅读提高了障碍,比如 889323737 你能一下说出他是多少万呢 还是多少亿呢
是不是不能很快的辨识出来
这个时候,就可以使用_来辅助便是,如下写法就会清晰很多了
8233_9499_0384_3
而且 这种写法在代码中并不会报错。(Python2不支持)
>>> number=281_028_344
>>> number
281028344
作者介绍