你所不知道的 Python 冷知識!(建議收藏)

首發於微信公衆號:Python編程時光
'
每週三更新五個冷知識,歡迎前往訂閱!html

01. 省略號也是對象


... 這是省略號,在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:
    ...
複製代碼

02. 如何修改解釋器提示符


正常狀況下,咱們在 終端下 執行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                                   
複製代碼

03. 增量賦值的性能更好


諸如 +=*= 這些運算符,叫作 增量賦值運算符。

這裏使用用 += 舉例,如下兩種寫法,在效果上是等價的。

# 第一種
a = 1 ; a += 1

# 第二種
a = 1; a = a + 1
複製代碼

+= 其背後使用的魔法方法是 __iadd__,若是沒有實現這個方法則會退而求其次,使用 __add__ 。

這兩種寫法有什麼區別呢?

用列表舉例 a += b,使用 _add_ 的話就像是使用了a.extend(b),若是使用 _add_ 的話,則是 a = a+b,前者是直接在原列表上進行擴展,然後者是先從原列表中取出值,在一個新的列表中進行擴展,而後再將新的列表對象返回給變量,顯而後者的消耗要大些。

因此在能使用增量賦值的時候儘可能使用它。

04. 奇怪的字符串


示例一

# 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
複製代碼

05. and 和 or 的取值順序


and 和 or 是咱們再熟悉不過的兩個邏輯運算符。而咱們一般只用它來作判斷,不多用它來取值。

若是一個or表達式中全部值都爲真,Python會選擇第一個值,而and表達式則會選擇第二個。

>>>(2 or 3) * (5 and 7)
14  # 2*7
複製代碼

06. 默認參數最好不爲可變對象


函數的參數分三種 - 可變參數 - 默認參數 - 關鍵字參數

這三者的具體區別,和使用方法在 廖雪峯的教程 裏會詳細的解釋。這裏就不搬運了。

今天要說的是,傳遞默認參數時,新手很容易踩雷的一個坑。

先來看一個示例

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 會與這個內存地址綁定。在後面的函數調用中,若是調用方指定了新的默認值,就會將原來的默認值覆蓋。若是調用方沒有指定新的默認值,那就會使用原來的默認值。

我的理解的記憶方法,不表明官方

07. 訪問類中的私有方法


你們都知道,類中可供直接調用的方法,只有公有方法(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()
複製代碼

08. 類首字母不必定是大寫


在正常狀況下,咱們所編寫的所見到的代碼,好像都默許了類名首字母大寫,而實例用小寫的這一準則。但這並非強制性的,即便你反過來的也沒有關係。

但有一些內置的類,首字母都是小寫,而實例都是大寫。

好比 bool 是類名,而 True,False 是其實例; 好比 ellipsis是類名,Ellipsis是實例; 還有 int,string,float,list,tuple,dict等一系列數據類型都是類名,它們都是小寫。

09. 時有時無的切片異常


這是個簡單例子

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:])
複製代碼

10. 哪些狀況下不須要續行符


在寫代碼時,爲了代碼的可讀性,代碼的排版是尤其重要的。

爲了實現高可讀性的代碼,咱們經常使用到的就是續行符 \

>>> 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'''
複製代碼

上面只舉了一些簡單的例子。

但你要學會觸類旁通。同樣的,在如下這些場景也一樣適用

  • 類,和函數的定義。
  • 列表推導式,字典推導式,集合推導式,生成器表達式

11. Py2 也可使用 print()


我相信應該有很多人,思惟定式,以爲只有 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")
複製代碼

12. 兩次 return


咱們都知道,try…finally… 語句的用法,無論try裏面是正常執行仍是報異常,最終都能保證finally可以執行。

同時,咱們又知道,一個函數裏只要遇到 return 函數就會立馬結束。

基於以上這兩點,咱們來看看這個例子,到底運行過程是怎麼樣的?

>>> def func():
...     try:
...         return 'try'
...     finally:
...         return 'finally'
...
>>> func()
'finally'
複製代碼

驚奇的發現,在try 裏的return竟然不起做用。

緣由是,在try…finally…語句中,try中的return會被直接忽視,由於要保證 finally 可以執行。

13. for 死循環


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

因此這個 for 循環會沒有終點。一直運行下去。

這些問題和答案都源自於羣友的智慧。若是你也想加入咱們的討論中,請到公衆號後臺,添加我我的微信。

14. 小整數池


先看例子。

>>> 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?

由於當你在同一行裏,同時給兩個變量賦同一值時,解釋器知道這個對象已經生成,那麼它就會引用到同一個對象。若是分紅兩成的話,解釋器並不知道這個對象已經存在了,就會從新申請內存存放這個對象。

15. 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
複製代碼

參考文檔



相關文章
相關標籤/搜索