簡體   English   中英

為什么Python中不允許使用foo(* arg,x)?

[英]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) ,它們依賴於順序。

那里的所有東西在函數定義和函數調用中都有特定的含義。 ab是沒有默認值定義的參數,下一個xy是用默認值定義的參數(可以跳過;所以在無默認參數之后)。 接下來, *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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM