1. 程式人生 > 實用技巧 >warning: deprecated conversion from string constant to ‘char*’ [-Wwrite-strings]-給char* 形參 傳入 巨集定義字串

warning: deprecated conversion from string constant to ‘char*’ [-Wwrite-strings]-給char* 形參 傳入 巨集定義字串

現在已經是Python 3.8的最後一個alpha版本,接著就是本月底要釋出的的3.8.0 beta 1了。按規定,3.8已經不會再新增(修改)功能了,之前非常有爭議的PEP 572的實現已經算是很固定了,我們這篇文章就來先嚐個鮮。看看這個新的賦值表示式語法怎麼用,何時用。

海象運算子

PEP572的標題是「Assignment Expressions」,也就是「賦值表示式」,也叫做「命名錶達式」,不過它現在被廣泛的別名是「海象運算子」(The Walrus Operator)。因為:=很像海象「眼睛小,長著兩枚長長的牙」這個特點^_^。

語法和語義

我們不詳細介紹PEP的內容,直接說應用場景。我覺得它主要可以用在2個地方

賦值給中間變數

這個小標題想了很久,沒找到更合適能表達的。不過相信通過2個例子相信大家就能理解了。

首先是一個正則匹配的例子:

pattern = re.compile('s')
data = 'ss'
if pattern.match(data):
  print(pattern.match(data).group(0))

如果能匹配到條件,match 物件才會有group 方法。但是這樣寫雖然節省到一行程式碼卻讓執行變慢了,因為重複地執行了2次re.match(data)。正確的寫法是:

match = pattern.match(data)
if match:
  print(match.group(0))

程式碼也就只能寫成這樣了,但如果使用賦值表示式:

if (match := pattern.match(data)) is not None:
  print(match.group(0))

本來if這種控制結構語句只是求值表示式,看結果是不是符合條件。而在這裡,它做了3件事:

  • 對錶達式pattern.match(data)求值
  • 把值的結果賦值給match
  • 把match 作為if的條件,判斷它的值是不是None

我對它的理解是: 求值過程中也賦值了新的中間變數,這個(些)中間變數(如這裡的match)可以在程式碼塊中被繼續使用。

再看一個檔案讀取的例子:

while 1:
  line = fp.readline()
  if not line:
    break
  print(line)

現在可以直接寫成:

while (line := fp.readline()):
  print(line)

這可以說是一種程式碼風格的改進了。

簡化列表解析

列表解析效能好,而且非常 pythonic,但是它應用場景有限,我們看個例子:

results = []
for x in data:
  result = f(x)
  if result:
    results.append(result)

這是一個日常開發裡面比較常見的結構。現在是不能用列表解析的,不信的話下面的方案:

results = [
  f(x) for x in data
  if f(x)
]

這個是錯誤的,每次迴圈執行了2 次f函式。現在用賦值表示式可以寫成:

results = [
  y for x in data
  if (y := f(x))
]

可以用列表解析了!

再看一個PEP提的例子:

stuff = [[y := f(x), x/y] for x in range(5)]

其實又回到了賦值給中間變數這個點,每一項包含了y,以及要用y才能獲得結果的x/y。

上面說的就是海象運算子能實現的目的了~

Golang裡面的:=

:=並不是Python首創的,Golang裡面有一個短變數宣告(Short variable declarations)語法:

// ShortVarDecl = IdentifierList ":=" ExpressionList .
i, j := 0, 10
f := func() int { return 7 }

func f(n int) (res int, err error) {
  if _, err := f(n-1); err != nil {
    return
  }
  return
}

:=的作用是替代 var 定義,宣告時不需要指定型別。同時由於語言設計,和Python的賦值表示式一樣,如上面的例子,f(n-1)的第二個返回值err可以被後面的err != nil使用,用來判斷條件是否成立,我非常喜歡!

我對PEP 572的看法

在之前我曾經在知乎回答過「如何看待 PEP 572 ?」這個問題,當時我這麼說:

這個PEP 有明確的 Recommended use-cases, 在正確的地方使用,而不是濫用,當然不喜歡的可以不用,用舊的形式。我語言提供了更多特性和選擇的機會,但控制權是開發者手裡的,就像元類、描述符、dataclass 甚至裝飾器等等都是有適用場景的。

有人覺得它不符合Python之禪,其實我個人感覺挺好的呀。現在PEP 572的實現已經合併到Python3.8,試用下來非常贊。

Dustin Ingram在PyCON2019上做了一個《PEP 572: The Walrus Operator》的分享,最後他也說自己不喜歡這個語法,但是他接著說:

You might say well i don't like it, that's totally fine. you don't have to like it if you don't like it then don't write it

我覺得說的非常好,沒人強制你必須使用它~

好了,本文就給大家介紹到這裡,希望對大家有所幫助!