首发于微信公众号:Python编程时光
'
每周三更新五个冷知识,欢迎前往订阅!html
...
这是省略号,在Python中,一切皆对象。它也不例外。python
在 Python 中,它叫作 Ellipsis 。git
在 Python 3 中你能够直接写…来获得这玩意。github
>>> ...
Ellipsis
>>> type(...)
<class 'ellipsis'>
复制代码
而在 2 中没有…这个语法,只能直接写Ellipsis来获取。编程
>>> Ellipsis
Ellipsis
>>> type(Ellipsis)
<type 'ellipsis'>
>>>
复制代码
它转为布尔值时为真bash
>>> bool(...)
True
复制代码
最后,这东西是一个单例。微信
>>> id(...)
4362672336
>>> id(...)
4362672336
复制代码
这东西有啥用呢?听说它是Numpy的语法糖,不玩 Numpy 的人,能够说是没啥用的。app
在网上只看到这个 用 ...
代替 pass ,稍微有点用,但又不是必须使用的。iphone
try:
1/0
except ZeroDivisionError:
...
复制代码
正常状况下,咱们在 终端下 执行Python 命令是这样的。函数
>>> for i in range(2):
... print (i)
...
0
1
复制代码
你是否想过 >>>
和 ...
这两个提示符也是能够修改的呢?
>>> import sys
>>> sys.ps1
'>>> '
>>> sys.ps2
'... '
>>>
>>> sys.ps2 = '---------------- '
>>> sys.ps1 = 'Python编程时光>>>'
Python编程时光>>>for i in range(2):
---------------- print (i)
----------------
0
1
复制代码
诸如 +=
和 *=
这些运算符,叫作 增量赋值运算符。
这里使用用 += 举例,如下两种写法,在效果上是等价的。
# 第一种
a = 1 ; a += 1
# 第二种
a = 1; a = a + 1
复制代码
+=
其背后使用的魔法方法是 __iadd__,若是没有实现这个方法则会退而求其次,使用 __add__ 。
这两种写法有什么区别呢?
用列表举例 a += b,使用 _add_ 的话就像是使用了a.extend(b),若是使用 _add_ 的话,则是 a = a+b,前者是直接在原列表上进行扩展,然后者是先从原列表中取出值,在一个新的列表中进行扩展,而后再将新的列表对象返回给变量,显而后者的消耗要大些。
因此在能使用增量赋值的时候尽可能使用它。
示例一
# Python2.7
>>> a = "Hello_Python"
>>> id(a)
32045616
>>> id("Hello" + "_" + "Python")
32045616
# Python3.7
>>> a = "Hello_Python"
>>> id(a)
38764272
>>> id("Hello" + "_" + "Python")
32045616
复制代码
示例二
>>> 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
复制代码
示例三
# Python2.7
>>> 'a' * 20 is 'aaaaaaaaaaaaaaaaaaaa'
True
>>> 'a' * 21 is 'aaaaaaaaaaaaaaaaaaaaa'
False
# Python3.7
>>> 'a' * 20 is 'aaaaaaaaaaaaaaaaaaaa'
True
>>> 'a' * 21 is 'aaaaaaaaaaaaaaaaaaaaa'
True
复制代码
and 和 or 是咱们再熟悉不过的两个逻辑运算符。而咱们一般只用它来作判断,不多用它来取值。
若是一个or表达式中全部值都为真,Python会选择第一个值,而and表达式则会选择第二个。
>>>(2 or 3) * (5 and 7)
14 # 2*7
复制代码
函数的参数分三种 - 可变参数 - 默认参数 - 关键字参数
这三者的具体区别,和使用方法在 廖雪峰的教程 里会详细的解释。这里就不搬运了。
今天要说的是,传递默认参数时,新手很容易踩雷的一个坑。
先来看一个示例
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 会与这个内存地址绑定。在后面的函数调用中,若是调用方指定了新的默认值,就会将原来的默认值覆盖。若是调用方没有指定新的默认值,那就会使用原来的默认值。
你们都知道,类中可供直接调用的方法,只有公有方法(protected类型的方法也能够,可是不建议)。也就是说,类的私有方法是没法直接调用的。
这里先看一下例子
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()
复制代码
既然都是方法,那咱们真的没有方法能够直接调用吗?
固然有啦,只是建议你千万不要这样弄,这里只是普及,让你了解一下。
# 调用私有方法,如下两种等价
ins._Kls__private()
ins.call_private()
复制代码
在正常状况下,咱们所编写的所见到的代码,好像都默许了类名首字母大写,而实例用小写的这一准则。但这并非强制性的,即便你反过来的也没有关系。
但有一些内置的类,首字母都是小写,而实例都是大写。
好比 bool 是类名,而 True,False 是其实例; 好比 ellipsis是类名,Ellipsis是实例; 还有 int,string,float,list,tuple,dict等一系列数据类型都是类名,它们都是小写。
这是个简单例子
my_list = [1, 2, 3, 4, 5]
print(my_list[5])
复制代码
执行一下,和咱们预期的同样,会抛出索引异常。
Traceback (most recent call last):
File "F:/Python Script/test.py", line 2, in <module>
print(my_list[5])
IndexError: list index out of range
复制代码
可是今天要说的确定不是这个,而是一个你可能会不知道的冷知识。
来看看,以下这种写法就不会报索引异常,执行my_list[5:],会返回一个新list:[]。
my_list = [1, 2, 3]
print(my_list[5:])
复制代码
在写代码时,为了代码的可读性,代码的排版是尤其重要的。
为了实现高可读性的代码,咱们经常使用到的就是续行符 \
。
>>> 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'''
复制代码
上面只举了一些简单的例子。
但你要学会触类旁通。同样的,在如下这些场景也一样适用
我相信应该有很多人,思惟定式,以为只有 Py3 才可使用 print(),而 Py2 只能使用print ’’。
其实并非这样的。
在Python 2.6以前,只支持
print "hello"
复制代码
在Python 2.6和2.7中,能够支持以下三种
print "hello"
print("hello")
print ("hello")
复制代码
在Python3.x中,能够支持以下两种
print("hello")
print ("hello")
复制代码
咱们都知道,try…finally… 语句的用法,无论try里面是正常执行仍是报异常,最终都能保证finally可以执行。
同时,咱们又知道,一个函数里只要遇到 return 函数就会立马结束。
基于以上这两点,咱们来看看这个例子,到底运行过程是怎么样的?
>>> def func():
... try:
... return 'try'
... finally:
... return 'finally'
...
>>> func()
'finally'
复制代码
惊奇的发现,在try
里的return竟然不起做用。
缘由是,在try…finally…语句中,try中的return会被直接忽视,由于要保证 finally 可以执行。
for 循环能够说是 基础得不能再基础的知识点了。
可是若是让你用 for 写一个死循环,你会写吗?(问题来自群友 陈**)
这是个开放性的问题,在往下看以前,建议你先尝试本身思考,你会如何解答。
好了,若是你尚未思路,那就来看一下 一个海外 MIT 群友的回答:
for i in iter(int, 1):pass
复制代码
是否是懵逼了。iter 还有这种用法?这为啥是个死循环?
这真的是个冷知识,关于这个知识点,你若是看中文网站,可能找不到相关资料。
还好你能够经过 IDE 看py源码里的注释内容,介绍了很详细的使用方法。
原来iter有两种使用方法,一般咱们的认知是第一种,将一个列表转化为一个迭代器。
而第二种方法,他接收一个 callable对象,和一个sentinel 参数。第一个对象会一直运行,直到它返回 sentinel 值才结束。
那int
呢,这又是一个知识点,int 是一个内建方法。经过看注释,能够看出它是有默认值0的。你能够在终端上输入int()
看看是否是返回0。
因为int() 永远返回0,永远返回不了1
这些问题和答案都源自于群友的智慧。若是你也想加入咱们的讨论中,请到公众号后台,添加我我的微信。
先看例子。
>>> 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
复制代码
为避免整数频繁申请和销毁内存空间,Python 定义了一个小整数池 [-5, 256] 这些整数对象是提早创建好的,不会被垃圾回收。
以上代码请在 终端Python环境下测试,若是你是在IDE中测试,并非这样的效果。
那最后一个示例,为啥又是True?
由于当你在同一行里,同时给两个变量赋同一值时,解释器知道这个对象已经生成,那么它就会引用到同一个对象。若是分红两成的话,解释器并不知道这个对象已经存在了,就会从新申请内存存放这个对象。
字符串类型做为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
复制代码