2012-12-09 97 views
2

我是Python新手,試圖爲參數打印默認值。我的代碼如下;爲什麼Python函數不打印默認值?

# Functions 

def shoppingcart(item='computer', *price): 
    print item 
    for i in price: 
     print i 

shoppingcart(100,200,300) 

當我運行代碼,我得到的是價值100,200,300。我期待computer,100,200,300。我究竟做錯了什麼?

編輯

如何在Python 2.7實現這一目標?

編輯

我我的代碼更新到

def shoppingcart(item='computer', *price): 
    print item 
    for i in price: 
     print i 

shoppingcart(item='laptop', 100,200,300) 

但得到的錯誤

enter image description here

+0

Python 2不會讓你寫'shoppingcart(item ='laptop',100,200,300)'。無論你如何編寫函數都沒關係,因爲你不能以這種方式調用它。 – abarnert

+2

至於錯誤的含義:一個「關鍵字arg」就像'item ='laptop'',你指定名稱和值;一個「位置參數」或者「非關鍵字參數」類似於'100',你只需要指定一個值,當你調用一個函數時,必須在關鍵字args後面加上所有的位置參數,所以,你可以編寫'購物車(100,200,300,項目=「筆記本電腦」)' - 和小葉女貞的代碼,如果你做的工作。 – abarnert

回答

2

你想做什麼是不可能的。你永遠不能用Python寫:

shoppingcart(item='laptop', 100,200,300) 

在任一2.x或3.x中,你會得到一個錯誤:

SyntaxError: non-keyword arg after keyword arg 

首先,一點背景:

A「關鍵字「參數是類似於item='laptop',您可以在其中指定名稱和值。 「位置」或「非關鍵字」參數與100類似,您只需指定值即可。無論何時調用函數,都必須在所有位置參數後面加上關鍵字args。所以,你可以寫shoppingcart(100, 200, 300, item='laptop'),但是你不能寫shoppingcart(item='laptop', 100, 200, 300)

當你理解關鍵字參數的處理方式時,這會更有意義。讓我們以一個非常簡單的情況下,用一套固定的參數,沒有缺省值,使一切更簡單:

def shoppingcart(item, price): 
    print item, price 

無論我把這種與shoppingcart('laptop', 200)shoppingcart(price=200, item='laptop'),或任何其他可能性,你可以計算出如何參數在函數體中到達itemprice。除了shoppingcart(price=200, 'laptop')之外,Python不知道laptop應該是什麼 - 它不是第一個參數,它沒有給出明確的關鍵字item,所以它不能是item。但它也不能是price,因爲我已經知道了。如果你認真思考,每當我嘗試在位置參數之前傳遞關鍵字參數時,都會遇到同樣的問題。在更復雜的情況下,很難看出原因,但在最簡單的情況下甚至不起作用。

因此,Python將引發一個SyntaxError: non-keyword arg after keyword arg,甚至沒有看你的函數是如何定義的。所以沒有辦法改變你的函數定義來完成這個工作;這是不可能的。

但是,如果您將item移動到參數列表的末尾,那麼我可以撥打shoppingcart(100, 200, 300, item='computer'),一切都會好的,對不對?遺憾的是,不,因爲您使用*args來吸收100, 200, 300,並且您不能在*args之後放置任何明確的參數。事實證明,這個限制沒有原則性的原因,所以他們在Python 3中放棄了它 - 但是如果你仍然使用2.7,你必須忍受它。但至少有一個解決方法。

解決方法是使用**kwargs來吸收所有關鍵字參數並自己解析它們,這與使用*args吸收所有位置參數的方式相同。就像*args得到一個帶有位置參數的list一樣,**kwargs會得到一個dict,每個關鍵字參數的關鍵字映射到它的值。

所以:

>>> def shoppingcart(*args, **kwargs): 
... print args, kwargs 
>>> shoppingcart(100, 200, 300, item='laptop') 
[100, 200, 300], {'item': 'laptop'} 

如果你想item是強制性的,你只需訪問這樣的:

item = kwargs['item'] 

如果你希望它是可選的,默認值,你做這樣的:

item = kwargs.get('item', 'computer') 

但這裏有一個問題:你只是想接受一個可選item的論點,但現在你實際上接受任何關鍵字參數。如果試圖簡單地用功能,你會得到一個錯誤:

>>> def simplefunc(a, b, c): 
...  print a, b, c 
>>> simplefunc(1, 2, 3, 4) 
TypeError: simplefunc() takes exactly 3 arguments (4 given) 
>>> simplefunc(a=1, b=2, c=3, d=4, e=5) 
TypeError: simplefunc() got an unexpected keyword argument 'd' 

有與**kwargs來模擬這是一個非常簡單的方法。請記住,這只是一個正常的dict,所以如果在解析它們時取出預期的關鍵字,則應該沒有任何遺漏 - 如果存在,則調用方傳遞了意外的關鍵字參數。如果你不想這樣做,你可以提出一個錯誤。你可以這樣寫:

def shoppingcart(*prices, **kwargs): 
    for key in kwargs: 
     if key != 'item': 
      raise TypeError("shoppingcart() got unexpected keyword argument(s) '%s' % kwargs.keys()[0]`) 
    item = kwargs.get('item', 'computer') 
    print item, prices 

但是,有一個方便的捷徑。 dict.pop方法的作用與dict.get類似,但除了將值返回給您之外,它還將其從字典中移除。當你刪除item(如果有的話),如果還有剩餘的東西,那是錯誤的。所以,你可以只是這樣做:

def shoppingcart(*args, **kwargs): 
    item = kwargs.pop('item', 'computer') 
    if kwargs: # Something else was in there besides item! 
     raise TypeError("shoppingcart() got unexpected keyword argument(s) '%s' % kwargs.keys()[0]`) 

除非你正在構建一個可重用的庫,你可以用的東西比這整個if/raise事情簡單,只是做assert not kwargs脫身;沒有人會在意你得到的是AssertionError而不是TypeError。這就是lqc的解決方案所做的。

回顧一下,如何編寫函數並不重要;它永遠不會被稱爲你想要的方式。但是,如果您願意將item移動到最後,您可以將它寫入並以合理的方式調用它(即使它不像Python 3版本那樣簡單或可讀)。

對於一些罕見的情況,還有其他方法可以去。例如,如果價格始終是數字,項目總是字符串,你可以改變你的代碼做這樣的事情:

def shoppingcart(*prices): 
    if prices and isinstance(prices[0], basestring): 
     item = prices.pop(0) 
    else: 
     item = 'computer' 
    print item 
    for price in prices: 
     print price 

現在,你可以這樣做:

>>> shoppingcart(100, 200, 300) 
computer 
100 
200 
300 
>>> shoppingcart('laptop', 100, 200, 300) # notice no `item=` here 
laptop 
100 
200 
300 

這基本上Python是用來實現slice(start=None, stop, step=None)的技巧,它在你的代碼中非常偶然。但是,這會讓你的實現變得更加脆弱,使得你的函數的行爲對讀者來說更加不明顯,並且通常不會感覺到Pythonic。所以,避免這種情況幾乎總是最好的,只要讓調用者使用真正的關鍵字參數(如果這是你想要的),並限制它們必須結束。

+0

謝謝。有趣。我從NetTuts收看的視頻將關鍵字參數放在非關鍵字或位置參數之前,除非我忽略了某些內容,否則它會起作用。當你說'讓調用者使用真正的關鍵字參數'你是什麼意思? – PeanutsMonkey

+0

通過「使調用者使用真正的關鍵字參數」,我的意思是以普通的Python方式來做事情,實際的關鍵字參數必須作爲參數傳遞,並且必須限制它們到最後,而不是提出一種沒有限制的僞造東西的方法。 – abarnert

+0

@PananutsMonkey:至於視頻:你能準確地發佈他們輸入的內容來調用函數嗎(並且,如果他們自己編寫函數,他們是如何定義它的)?我無法猜測視頻是否是錯誤的,或者你是在誤解它,或者是什麼,而不知道他們做了什麼。 – abarnert

6

簡單地說,如果沒有其他辦法的默認值僅用於。在這裏你有item=100,其他2個值進入*price

在Python3你可以寫:

def shoppingcart(*prices, item='computer'): 
    print(item) 
    print(prices) 

shoppingcart(100,200,300) 
shoppingcart(100,200,300, item='moon') 

在Python2你不能有默認參數後*args所以你必須找到另一種方式來調用您的函數。

+0

謝謝你。我該怎麼做同樣的在Python 2.7? – PeanutsMonkey

3

對於Python 2.x的,你必須使用**語法:

def shoppingcart(*prices, **kwargs): 
    item = kwargs.pop("item", "computer") 
    assert not kwargs, "This function only expects 'item' as keyword argument". 
    print(item) 
    print(prices) 

然後,你可以將它傳遞關鍵字參數:

shoppingcart(100,200,300, item='moon') 

(他們總是需要經過「位置'論據)

+0

謝謝。爲什麼我需要用' pop'也是你所說的「positional'參數的含義 – PeanutsMonkey

+0

@PeanutsMonkey:?您使用'pop'不僅'GET'的項目,但也如果存在從'dict'刪除這意味着以後。不管調用者是否傳遞了一個'item',字典都是空的,除非它們傳遞了一些意想不到的關鍵字參數,所以你可以測試它(這會更好地提出一個'TypeError(「shoppingcart()關鍵字參數「),而不是」assert「,但除非你正在編寫一個庫供其他人使用,否則就沒有問題。) – abarnert

+0

@lqc - 謝謝lqc。對不起,在這個新手,但你可以給我每個例子,因爲我不明白你的意思是什麼'get'和'dict'出現在圖片中? – PeanutsMonkey

相關問題