![](/img/trans.png)
[英]def self.foo(arg): rather than def foo(self, arg): in Python
[英]Why is foo(*arg, x) not allowed in Python?
請看下面的例子
point = (1, 2)
size = (2, 3)
color = 'red'
class Rect(object):
def __init__(self, x, y, width, height, color):
pass
打電話是非常誘人的:
Rect(*point, *size, color)
可能的解決方法是:
Rect(point[0], point[1], size[0], size[1], color)
Rect(*(point + size), color=color)
Rect(*(point + size + (color,)))
但是為什么不允許使用Rect(*point, *size, color)
,是否存在任何語義模糊或者你能想到的一般缺點?
為什么在函數調用中不允許多個* arg擴展?
為什么* arg擴展后不允許位置參數?
我不打算說為什么多元組解包不是Python的一部分,但我會指出你的類不符合你的例子中的數據。
您有以下代碼:
point = (1, 2)
size = (2, 3)
color = 'red'
class Rect(object):
def __init__(self, x, y, width, height, color):
self.x = x
self.y = y
self.width = width
self.height = height
self.color = color
但是表達Rect對象的更好方法如下:
class Rect:
def __init__(self, point, size, color):
self.point = point
self.size = size
self.color = color
r = Rect(point, size, color)
通常,如果您的數據是元組,請讓構造函數使用元組。 如果您的數據在dict中,請讓您的構造函數采用dict。 如果您的數據是對象,請讓構造函數獲取對象等。
一般來說,您希望使用該語言的習語,而不是嘗試解決它們。
編輯看到這個問題有多受歡迎,我會給你一個裝飾器,讓你可以隨意調用構造函數。
class Pack(object):
def __init__(self, *template):
self.template = template
def __call__(self, f):
def pack(*args):
args = list(args)
for i, tup in enumerate(self.template):
if type(tup) != tuple:
continue
for j, typ in enumerate(tup):
if type(args[i+j]) != typ:
break
else:
args[i:i+j+1] = [tuple(args[i:i+j+1])]
f(*args)
return pack
class Rect:
@Pack(object, (int, int), (int, int), str)
def __init__(self, point, size, color):
self.point = point
self.size = size
self.color = color
現在,您可以按照自己喜歡的方式初始化對象。
r1 = Rect(point, size, color)
r2 = Rect((1,2), size, color)
r3 = Rect(1, 2, size, color)
r4 = Rect((1, 2), 2, 3, color)
r5 = Rect(1, 2, 2, 3, color)
雖然我不建議在實踐中使用它(它違反了你應該只有一種方法來實現它的原則),但它確實有助於證明通常有一種方法可以用Python做任何事情。
據我所知,這是一個設計選擇,但它背后似乎有一個邏輯。
編輯:函數調用中的*args
表示法被設計為可以傳入一個任意長度的變量元組,這些變量可以在調用之間改變。 在這種情況下,有一個像f(* a,* b,c)這樣的東西作為一個調用是沒有意義的,好像a
更改長度b的所有元素被分配給錯誤的變量,而c不在正確的地方。
保持語言簡單,強大和標准化是一件好事。 保持與處理參數的實際情況同步也是一件非常好的事情。
考慮語言如何解壓縮函數調用。 如果允許多個*arg
以任何順序(如Rect(*point, *size, color)
,請注意正確解壓縮的所有重要因素是點和大小總共有四個元素。 因此, point=()
, size=(1,2,2,3)
和color='red')
將允許Rect(*point, *size, color)
作為正確的調用。 基本上,它解析* point和* size時的語言將它視為一個組合的*arg
元組,因此Rect(*(point + size), color=color)
更忠實的表示。
永遠不需要在*args
形式中傳遞兩個參數元組,您始終可以將其表示為一個。 由於參數的賦值僅取決於此組合*arg
列表中的順序,因此將其定義為有意義。
如果你可以像f(* a,* b)那樣進行函數調用,那么這種語言幾乎可以讓你在參數列表中定義帶有多個* args的函數,而這些函數無法處理。 例如,
def f(*a, *b):
return (sum(a), 2*sum(b))
如何處理f(1,2,3,4)
?
我認為這就是為什么語法具體性,語言強制函數調用和定義具有以下特定形式; 像f(a,b,x=1,y=2,*args,**kwargs)
,它們依賴於順序。
那里的所有東西在函數定義和函數調用中都有特定的含義。 a
和b
是沒有默認值定義的參數,下一個x
和y
是用默認值定義的參數(可以跳過;所以在無默認參數之后)。 接下來, *args
被填充為一個元組,其中所有的args都填充了函數調用中不是關鍵字參數的其余參數。 這是在其他人之后,因為這可能會改變長度,並且您不希望某些內容可以改變調用之間的長度以影響變量的賦值。 最后** kwargs獲取所有未在其他地方定義的關鍵字參數。 通過這些具體定義,您永遠不需要具有多個*args
或**kwargs
。
*point
表示你傳遞了一整個項目序列 - 類似於列表中的所有元素,但不是列表。
在這種情況下,您無法限制傳入的元素數量。因此,解釋器無法知道序列的哪些元素是*points
一部分,哪些是*size
例如,如果您傳遞以下內容作為輸入: 2, 5, 3, 4, 17, 87, 4, 0
您能告訴我,這些數字中的哪一個用*points
表示,哪個用*size
? 這也是口譯員面臨的同樣問題
希望這可以幫助
Python充滿了這些微妙的故障。 例如,您可以這樣做:
first, second, last = (1, 2, 3)
你做不到:
first, *others = (1, 2, 3)
但是在Python 3中你現在可以。
您的建議可能會在PEP中被建議並且有一天會被整合或拒絕。
那么,在Python 2中,您可以說:
point = 1, 2
size = 2, 3
color = 'red'
class Rect(object):
def __init__(self, (x, y), (width, height), color):
pass
然后你可以說:
a_rect= Rect(point, size, color)
注意前兩個參數是len == 2的序列。
注意:此功能已從Python 3中刪除。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.