1. 程式人生 > >你所不知道的 Python 冷知識!(二)(建議收藏)

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

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

01. 互動式“_”操作符


對於 _ ,我想很多人都非常熟悉。

給變數取名好艱難,用 _; 懶得長長的變數名,用 _; 無用的垃圾變數,用 _

以上,我們都很熟悉了,今天要介紹的是他在互動式中使用。

>>> 3 + 4
7
>>> _
7
>>> name='ming'
>>> name
'ming'
>>> _
'ming'
複製程式碼

它可以返回上一次的執行結果。

但是,如果是print函式打印出來的就不行了。

>>> 3 + 4
7
>>> _
7
>>> print("ming")
ming
>>> _
7
複製程式碼

我自己寫了個例子,驗證了下,用__repr__輸出的內容可以被獲取到的。 首先,在我們的目錄下,寫一個檔案 ming.py。內容如下

# ming.py
class mytest():
    def __str__(self):
        return "hello"

    def __repr__(self):
        return "world"
複製程式碼

然後在這個目錄下進入互動式環境。

>>> import ming
>>> mt=ming.mytest()
>>> mt
world
>>> print(mt)
hello
>>> _
world
複製程式碼

知道這兩個魔法方法的人,一看就明白了。

02. 反轉字串/列表最優雅的方式


反轉序列並不難,但是如何做到最優雅呢?

先來看看,正常是如何反轉的。

最簡單的方法是使用列表自帶的reverse()方法。

>>> ml = [1,2,3,4,5]
>>> ml.reverse()
>>> 
ml [5, 4, 3, 2, 1] 複製程式碼

但如果你要處理的是字串,reverse就無能為力了。你可以嘗試將其轉化成list,再reverse,然後再轉化成str。轉來轉去,也太麻煩了吧?需要這麼多行程式碼(後面三行是不能合併成一行的),一點都Pythonic。

mstr1 = 'abc'
ml1 = list(mstr1)
ml1.reverse()
mstr2 = str(ml1)
複製程式碼

對於字串還有一種稍微複雜一點的,是自定義遞迴函式來實現。

def my_reverse(str):
    if str == "":
        return str
    else:
        return my_reverse(str[1:]) + str[0]
複製程式碼

在這裡,介紹一種最優雅的反轉方式,使用切片,不管你是字串,還是列表,簡直通殺。

>>> mstr = 'abc'
>>> ml = [1,2,3]
>>> mstr[::-1]
'cba'
>>> ml[::-1]
[3, 2, 1]
複製程式碼

03. 改變預設遞迴次數限制


上面才提到遞迴,大家都知道使用遞迴是有風險的,遞迴深度過深容易導致堆疊的溢位。如果你這字串太長啦,使用遞迴方式反轉,就會出現問題。

那到底,預設遞迴次數限制是多少呢?

>>> import sys
>>> sys.getrecursionlimit()
1000
複製程式碼

可以查,當然也可以自定義修改次數,退出即失效。

>>> sys.setrecursionlimit(2000)
>>> sys.getrecursionlimit()
2000
複製程式碼

04. 一行程式碼實現FTP伺服器


搭建FTP,或者是搭建網路檔案系統,這些方法都能夠實現Linux的目錄共享。但是FTP和網路檔案系統的功能都過於強大,因此它們都有一些不夠方便的地方。比如你想快速共享Linux系統的某個目錄給整個專案團隊,還想在一分鐘內做到,怎麼辦?很簡單,使用Python中的SimpleHTTPServer。

SimpleHTTPServer是Python 2自帶的一個模組,是Python的Web伺服器。它在Python 3已經合併到http.server模組中。具體例子如下,如不指定埠,則預設是8000埠。

# python2
python -m SimpleHTTPServer 8888

# python3
python3 -m http.server 8888
複製程式碼

SimpleHTTPServer有一個特性,如果待共享的目錄下有index.html,那麼index.html檔案會被視為預設主頁;如果不存在index.html檔案,那麼就會顯示整個目錄列表。

05. 讓你暈頭轉向的 else 用法


if else 用法可以說最基礎的語法表示式之一,但是今天不是講這個的,一定要講點不一樣的。

if else 早已爛大街,但可能有很多人都不曾見過 for else 和 try else 的用法。為什麼說它曾讓我暈頭轉向,因為它不像 if else 那麼直白,非黑即白,腦子經常要想一下才能才反應過來程式碼怎麼走。反正我是這樣的。

先來說說,for else

def check_item(source_list, target):
    for item in source_list:
        if item == target:
            print("Exists!")
            break

    else:
        print("Does not exist")

複製程式碼

在往下看之前,你可以思考一下,什麼情況下才會走 else。是迴圈被 break,還是沒有break?

給幾個例子,你體會一下。

check_item(["apple", "huawei", "oppo"], "oppo")
# Exists!

check_item(["apple", "huawei", "oppo"], "vivo")
# Does not exist
複製程式碼

可以看出,沒有被 break 的程式才會正常走else流程。

再來看看,try else 用法。

def test_try_else(attr1 = None):
    try:
        if attr1:
            pass
        else:
            raise
    except:
        print("Exception occurred...")
    else:
        print("No Exception occurred...")
複製程式碼

同樣來幾個例子。當不傳引數時,就丟擲異常。

test_try_else()
# Exception occurred...

test_try_else("ming")
# No Exception occurred...
複製程式碼

可以看出,沒有 try 裡面的程式碼塊沒有丟擲異常的,會正常走else。

總結一下,for else 和 try else 相同,只要程式碼正常走下去不被 break,不丟擲異常,就可以走else。

06. 空字串計數


求一個字串裡,某子字元(串)出現的次數。在Python中使用 count() 函式,就可以輕鬆實現。

比如下面幾個常規例子

>>> "aabb".count("a")
2
>>> "aabb".count("b")
2
>>> "aabb".count("ab")
1
複製程式碼

但是如果使用空字串呢,你可能想不到會是這樣的結果。

>>> "aabb".count("")
5
複製程式碼

具體原因,我不敢妄下結論。

由此我還衍生出另一個想法,實驗了下。不知道空字串,是一種什麼樣的存在,難道字母與字母之間 “縫隙” 也算嗎?

>>> "" in ""
True
>>> "" in "ab"
True
複製程式碼

有興趣的可以去看看CPython的原始碼實現。

07. 負負得正


從初中開始,我們就開始接觸了負數 這個概念。知道了負負得正,這和武俠世界裡的以毒功毒,有點神似。

Python 作為一門高階語言,它的編寫符合人類的思維邏輯,這其中也包括負負得正這個思想。

>>> 5-3
2
>>> 5--3
8
>>> 5+-3
2
>>> 5++3
8
>>> 5---3
2
複製程式碼

08. 數值與字串的比較


在 Python2 中,數字可以與字串直接比較。結果是數值永遠比字串小。

>>> 100000000 < ""
True
>>> 100000000 < "ming"
True
複製程式碼

但在 Python3 中,卻不行。

>>> 100000000 < ""
TypeError: '<' not supported between instances of 'int' and 'str'
複製程式碼

09. 迴圈中的區域性變數洩露


在Python 2中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
複製程式碼

10. 字典可排序


字典不可排序的思想,似乎已經根深蒂固。

# Python2.7.10
>>> mydict = {str(i):i for i in range(5)}
>>> mydict
{'1': 1, '0': 0, '3': 3, '2': 2, '4': 4}
複製程式碼

在 Python3 中字典已經是有序的。

# Python3.6.7
>>> mydict = {str(i):i for i in range(5)}
>>> mydict
{'0': 0, '1': 1, '2': 2, '3': 3, '4': 4}
複製程式碼

11. 鏈式比較


先給看一個示例

>>> False == False == True
False
複製程式碼

你知道這個表示式會返回 False 嗎?

我再給你舉個例子,你可能就懂了。

f 18 < age < 60:
    print("young man")
複製程式碼

如果還不明白,再給你整個等價寫法。

>>> False == False and False == True
False
複製程式碼

12. 奇怪的字母


直接看下列例子。

在Python 2.x 中

>>> value = 11
>>> valuе = 32
  File "<stdin>", line 1
    valuе = 32
        ^
SyntaxError: invalid syntax
複製程式碼

在Python 3.x 中

>>> value = 11
>>> valuе = 32
>>> value
11
複製程式碼

我相信你一開始看到這裡,一定是目瞪口呆。你可以在自己的電腦上嘗試一下,你會發現你不管在哪個版本的 Python 裡執行都沒有問題。

如果你想重現我這個場景,你可能複製我上面的程式碼貼上至自己的命令列中即可。

在這裡,也不賣關子了,上面程式碼中第二行的 е 和 第一行的 e 是不一樣的。

第二行的 e 是 Cyrillic(西里爾)字母,而不是我們熟悉的英文字母。

>>> ord('е') # cyrillic 'e' (Ye)
1077
>>> ord('e') # latin 'e', as used in English and typed using standard keyboard
101
>>> 'е' == 'e'
False
複製程式碼

細思恐極,平時可千萬不要得罪同事們,萬一辭職的時候,把你專案裡的 e 全域性替換成 e,到時候連錯都不知道錯哪了哈哈。

13. 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})
複製程式碼

14. 有趣的import


import 是 Python 導包的方式。

你知道 Python 中內建了一些很有(wu)趣(liao)的包嗎?

Hello World

>>> import __hello__
Hello World!
複製程式碼

Python之禪

>>> import this

The Zen of Python, by Tim Peters

Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren't special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one-- and preferably only one --obvious way to do it.
Although that way may not be obvious at first unless you're Dutch.
Now is better than never.
Although never is often better than *right* now.
If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea -- let's do more of those!
複製程式碼

反地心引力漫畫

在 cmd 視窗中匯入antigravity

>>> import antigravity
複製程式碼

就會自動開啟一個網頁。

15. 區域性/全域性變數分不清?


在開始講之前,你可以試著執行一下下面這小段程式碼。

a = 1

def func01():
    a += 1
    
func01()
複製程式碼

看似沒有毛病,但實則已經犯了一個很基礎的問題,這個報錯相當常見吧?

>>> func01()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 2, in func01
UnboundLocalError: local variable 'a' referenced before assignment
複製程式碼

回顧一下,什麼是區域性變數?在非全域性下定義宣告的變數都是區域性變數。

當程式執行到 a += 1 時,Python 直譯器就認為在函式內部要給 a 這個變數賦值,當然就把 a 當做區域性變量了,報錯是理所當然的。

參考文件