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

Chr*_*ris 28 python function variadic-functions

请看下面的例子

point = (1, 2)
size = (2, 3)
color = 'red'

class Rect(object):
    def __init__(self, x, y, width, height, color):
        pass
Run Code Online (Sandbox Code Playgroud)

打电话是非常诱人的:

Rect(*point, *size, color)
Run Code Online (Sandbox Code Playgroud)

可能的解决方法是:

Rect(point[0], point[1], size[0], size[1], color)

Rect(*(point + size), color=color)

Rect(*(point + size + (color,)))
Run Code Online (Sandbox Code Playgroud)

但是为什么Rect(*point, *size, color)不允许,你能想到的是否存在任何语义模糊或普遍缺点?

编辑:具体问题

为什么在函数调用中不允许多个*arg扩展?

为什么*arg扩展后不允许位置参数?

iro*_*hon 11

我不打算说为什么多元组解包不是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
Run Code Online (Sandbox Code Playgroud)

但是表达Rect对象的更好方法如下:

class Rect:
    def __init__(self, point, size, color):
        self.point = point
        self.size = size
        self.color = color

r = Rect(point, size, color)
Run Code Online (Sandbox Code Playgroud)

通常,如果您的数据是元组,请让构造函数使用元组.如果您的数据在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
Run Code Online (Sandbox Code Playgroud)

现在,您可以按照自己喜欢的方式初始化对象.

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)
Run Code Online (Sandbox Code Playgroud)

虽然我不建议在实践中使用它(它违反了你应该只有一种方法来实现它的原则),但它确实有助于证明通常有一种方法可以用Python做任何事情.

  • @Chris**定义**具有自动元组解包的功能在PEP 3113中被删除.**仍然允许使用元组作为参数调用**函数(并鼓励) (3认同)

dr *_*bob 9

据我所知,这是一个设计选择,但它背后似乎有一个逻辑.

编辑:*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))
Run Code Online (Sandbox Code Playgroud)

怎么f(1,2,3,4)处理?

我认为这就是为什么语法具体性,语言强制函数调用和定义具有以下特定形式; 喜欢f(a,b,x=1,y=2,*args,**kwargs)哪个是依赖于订单的.

那里的所有东西在函数定义和函数调用中都有特定的含义. a并且b是没有默认值定义的参数,next xy是使用默认值定义的参数(可以跳过;所以在无默认参数之后).接下来,*args将填充为元组,其中所有args都填充了函数调用中不是关键字参数的其余参数.这是在其他人之后,因为这可能会改变长度,并且您不希望某些内容可以改变调用之间的长度以影响变量的赋值.最后**kwargs获取所有未在其他地方定义的关键字参数.有了这些具体的定义,你永远不需要有多个*args**kwargs.