標籤:

為什麼在python3里b=a=1是合理表達式,而print(a=1)卻不是。a=1為什麼沒有返回值?

lisp里每個表達式均有值,而python3里為什麼這種很平常的表達式卻沒有返回值呢?


首先,因為你在用C/C++的思維理解python。

沒有就是沒有,返回值的提法也不對,返回值是函數的結果。準確說應該是表達式值。

語言的語法是人定義的,賦值是assignment statement,不是expression,所以沒有值。

另外,在python裡面function_name(var=????)是有特殊的語義的,它指的是function_name的var參數的實參是????。

例如

def f(x, y):
return str(x) + str(y)

調用的時候我們可以

f(1, 2)

還可以寫全了

f(x=1, y=2)

還可以倒過來

f(y=2, x=1)

上面3個調用的結果全部是"12"

你用print(a=1)就意味著print有個形參是a。顯然就不對了。


In [1]: def foo():
...: a = b = 1
...:

In [2]: from dis import dis

In [3]: dis(foo)
2 0 LOAD_CONST 1 (1)
3 DUP_TOP
4 STORE_FAST 0 (a)
7 STORE_FAST 1 (b)
10 LOAD_CONST 0 (None)
13 RETURN_VALUE

a = b = 1等價於 a = 1, b = 1

不要用C/C++來理解…不然給你個

if (1&<=a&<=10)

怎麼辦


你要理解為b=a=1是一個語句,而不是表達式。


樓上的各位解釋的已經很清楚了,總結一下就是:

python中賦值就是賦值,他不是一個表達式,也就不會返回一個表達式的值。

這樣做的一個好處是讓語法更加清晰。

在C中存在一個「行內賦值」的問題,舉個栗子:你打算判斷x的值是否等於1000, 如果等於1000則返回真,如果不等則返回假,正確的代碼應該是:

#include&
void main(){
int x = 200;
if(x == 1000)
printf("true");
else
printf("false");
}

x的初始值為200,顯然不等於1000,那麼自然是執行else語句塊中的內容,返回假值:

但是萬萬沒想到,在編寫代碼的時候不小心將 x == 1000 錯寫成了x = 1000,成了下面這種模樣:

#include&
void main(){
int x = 200;
if(x = 1000)
printf("true");
else
printf("false");
}

雖然x的初始值仍然是200, 但是這時候判斷語句被錯寫成了賦值表達式,而由於C是支持「行內賦值」的,並不會產生任何語法錯誤,於是 x = 1000 被成功執行,且表達式自身會有一個返回值,這個返回值為真,因此if語句塊中的內容被執行了,返回true:

而在python中,切記賦值就是賦值,是語句,不會有任何的返回值,類似這種」行內賦值「是不被允許的。倘若你嘗試用類似的方法,在 if/while/for 等等這樣的複合語句中使用 a=b 這樣的賦值語句,python解釋器就會毫不留情的拋出一個語法錯誤,像這樣:

x = 200

if x = 1000:
print "true"
else:
print "false"

雖然類似的異常被拋出很不好看,但是它卻為程序調試提供了很大的便利,」行內賦值「這類的bug往往隱藏的很深,無形之中將原本語法層面的問題,轉變成了程序邏輯層面的問題。而python的這種設計,永遠不會出現這樣的問題。此外,python的函數支持一種特殊的語法,叫做」關鍵字參數「。簡單的說,就是在給函數傳遞實參的時候,可以顯示的指定參數的名字,同樣舉一個簡單的栗子,假設你定義了這樣一個函數:

def greeting(title, name):
print "Hello, %(title)s %(name)s" % {"title":title, "name":name}

你顯然可以按照參數的位置傳入實參,像C或C++一樣:

greeting("professor", "Li")

這樣title的值為"professor", name的值為」Li「,函數會輸出你想要的結果:

當然在python中,你還可以這樣寫:

greeting(title="professor", name="Li")

or

greeting(name="Li", title="professor")

此時,傳遞參數的順序已經不重要了,python解釋器會根據顯示的賦值,來確定參數名和參數值的對應關係,調用的結果和按照位置傳入參數的結果是一樣的:

因此當你在調用 print(a=1) 的時候,實際上,python解釋器理解為給print()函數的形參a傳值,而不是你理解的「先個a賦值,再用print函數輸出a=1這個表達式的值」。遺憾的是,print() 函數並沒有叫這個名字的形參,於是解釋器拋出一個 TypeError 異常,告訴你它不認識變數a

OK,這種「關鍵字」形式的傳參有什麼意義呢?它的確有一些便利之處。例如你定義了一個函數,它有一些默認參數,像下面這個栗子一樣:

def student_info(id, name, age, grade="G1", school="CUMT"):
info_str = """
id: %s;
name: %s;
age: %s;
grade: %s;
school: %s;
"""

print info_str % (id, name, age, grade, school)

if __name__ == "__main__":
student_info(1001, "Liu", 22)
student_info(1002, "Wang", 20, grade="G2")
student_info(1003, "Zhang", 21, school="PKU")

student_info() 這個函數有兩個默認參數,分別是grade(年級)和school(學校),默認值分別為"G1"和「CUMT」。

第一次調用,grade和school參數都使用了默認值;

第二次調用,grade我們指定了值為"G2",school參數仍然使用默認值;

第三次調用,請注意,我們跳過了grade參數,給school參數傳了新的值"PKU",而grade參數仍然使用默認的"G1";

輸出結果如下:

第一次和第二次調用很好理解,和C/C++是類似的,當然第二次調用中我們用到了grade="G2"這種python特有的參數傳遞的語法。第三次調用則展示了python「關鍵字參數」的「魔法」。在C/C++中,我們想要給最右側的school傳入新的值,則必須給它前面的grade先傳入值。在python中則不需要,還記得么」關鍵字參數「和位置是無關的,因此你可以跳過grade給school傳值,而grade依舊保留默認值,只要你使用關鍵字參數,顯示的寫出來,解釋器會說:Oh, 剩下的就交給我吧!


為什麼在python3里b=a=1是合理表達式

鏈式賦值只是一種語法結構 他並不是跟cpp一樣"把1賦給a 再把a的值賦給b"

而是把1賦給b和a

而print(a=1)卻不是

這句話的意思是把1賦給print的形參a 而不是像cpp那樣「把1賦給局部變數a 再把a的值傳進去」

a=1為什麼沒有返回值

a=1在cpp裡面是表達式(expression) 在py裡面叫語句(statement)

語句就像cpp的"return xxx;"一樣 不必要有值

另外表達式的值也不叫返回值


  1. Python的賦值語句是沒有返回值的,而且很多語言都沒有,這並不是編程語言界的規定或者慣例。
  2. 你的那個

    print(a=1)
    --------------------------------------------------------------------------
    TypeError Traceback (most recent call last)
    & in &()
    ----&> 1 print(a=1)
    TypeError: "a" is an invalid keyword argument for this function

    默認會把a識別成為一個形參,但是print這個函數沒有a這個參數,所以會報錯,如果你想要輸出的是a=1這個表達式的值,你至少應該使用

print((a=1))
File "&", line 1
print((a=1))
^
SyntaxError: invalid syntax

這種情況下會報語法錯誤。


賦值語句沒有返回值,所以不是表達式,這是因為賦值語句不需要返回值, Python 既不是 C 也不是 Lisp。

即使有返回值的 Lisp ,返回值一般也是 "(),這就告訴你不要想用返回值做什麼事,其實和 Python 的思想是一樣的。

區別是 Python 用一個語法錯誤來防止程序員干蠢事而已。

Why does Python assignment not return a value?

鏈式賦值的邏輯不是先求值右側的表達式再賦給左側,僅僅是一種語法而已。下面可以看到,賦值的語句的解析顯然不是遞歸的。

Simple statements

assignment_stmt ::= (target_list "=")+ (expression_list | yield_expression)


因為Python語言的設計者認為賦值最重要的作用是其副作用,不應該像表達式那樣有返回值.詳見:https://docs.python.org/3/faq/design.html?highlight=assignment#why-can-t-i-use-an-assignment-in-an-expression


。。。為了防止程序員出現if(x=1)這樣的錯誤而找不到bug所在,我們規定這樣寫是錯誤的


if( x = 1 ) { #do.... }

我覺得很難知道你是想先賦值再判斷x,還是想判斷 x 是否等於1,只是不小心漏掉了一個"等號"。

swift 更是從根本上解決了這一問題。我支持這一點。

A: x = 1
if(x){ #do... }

B: if( x == 1 ){ #do... }

代碼,應該像上面 A 或者 B 這樣寫才更清楚。。。


推薦閱讀:

新手該學SQL還是Python?
tornado cgi wsgi uwsgi之間的關係?
用C寫Python擴展時哪些地方容易導致內存泄露?
2017年,Web 後端出現了哪些新的思想和技術?
(做生物信息的)你們是怎麼知道Python裡面sys.argv和getopt這種函數的?

TAG:Python |