解包赋值是一个方便代码编写的语法特性,在一个赋值语句中可将右边表达式的值自动解开赋值给左边,如:
首先,计算右边的表达式,以及左边的各左值元素中的需计算部分(如果有的话,比如a[i + 1], b = t),而这两者的计算顺序则根据语言定义
其次,根据左边的元素数量,将右边表达式转换为对应大小的数组,转换过程是直接迭代,换句话说,右边的表达式不一定需要是一个tuple/list/dict/set等,只要可迭代就行;转换完后,需要检查数量和数组大小的一致性,即上例中,假如t的元素不为2个,无论多余或少于,都会报异常
最后,将对应位置的左值和数组元素做赋值操作,需要注意的是,这个赋值不仅可以是简单赋值,也可以有解包操作,例如:
这个语法的好处在于可以让代码简单明了,尤其是在循环语句中:
有些语言变相实现了类似的语法,比如go语言函数的多值返回,不过它这个实现相当于C语言中多值返回的一种变形,C代码:
不过,python和larva的解包赋值按照上面说的实现,有一个小问题,比如这个例子(python代码):
具体到larva目前的转java代码实现,在LarUtil类中有一个函数unpack_seq,输入一个LarObj和大小n,它会对输入的LarObj进行迭代,转换成一个大小为n的LarObj[],如果这个过程中检查到大小不一致,则异常,具体的实现可以看git上的代码
a, b = t
简单看上去相当于:a = t[0]
b = t[1]
不过若t的位置是一个表达式,则下面这样做就必须用一个临时变量来防止重复求值,事实上这个代码和解包并不等价,解包赋值的具体步骤更复杂一点:首先,计算右边的表达式,以及左边的各左值元素中的需计算部分(如果有的话,比如a[i + 1], b = t),而这两者的计算顺序则根据语言定义
其次,根据左边的元素数量,将右边表达式转换为对应大小的数组,转换过程是直接迭代,换句话说,右边的表达式不一定需要是一个tuple/list/dict/set等,只要可迭代就行;转换完后,需要检查数量和数组大小的一致性,即上例中,假如t的元素不为2个,无论多余或少于,都会报异常
最后,将对应位置的左值和数组元素做赋值操作,需要注意的是,这个赋值不仅可以是简单赋值,也可以有解包操作,例如:
a, (b, c) = t
t中有两个元素,第一个赋给a,第二个对b,c再做一次解包这个语法的好处在于可以让代码简单明了,尤其是在循环语句中:
for a, b in l:
... //循环体
而不用写成:for t in l:
a = t[0]
b = t[1] //假设l的元素是一个二元组,假如是一个迭代器,则这里更麻烦
... //循环体
<span style="font-family: Arial, Helvetica, sans-serif;"></span>
在larva中也实现了python的这个语法,不过做了一点小小的限制,全局变量初始化不能使用,全局变量必须采用“变量名=表达式”这种方式初始化,另外,函数参数也不能写成解包的形式,比如:
func f(a, (b, c)):
这个形式在python中是可以的有些语言变相实现了类似的语法,比如go语言函数的多值返回,不过它这个实现相当于C语言中多值返回的一种变形,C代码:
void f(int in, int *out_1, int *out_2, int *out_3)
go也有指针,所以就是个语法糖的事情了不过,python和larva的解包赋值按照上面说的实现,有一个小问题,比如这个例子(python代码):
l = [1,2,3,4]
it = iter(l)
try:
a, b = it
except ValueError:
pass
for i in it:
print i
最后这个for循环会打印出4,原因也很简单,在执行解包赋值的时候,对it进行迭代,发现有超过2个元素(也就是next()执行了三次),异常被捕获忽略后,it的状态是l的第四个元素,可以继续迭代。这个似乎有些设计上的不合理,不过考虑到正常代码几乎不会碰到这种情况,所以也在可接受范围了具体到larva目前的转java代码实现,在LarUtil类中有一个函数unpack_seq,输入一个LarObj和大小n,它会对输入的LarObj进行迭代,转换成一个大小为n的LarObj[],如果这个过程中检查到大小不一致,则异常,具体的实现可以看git上的代码
这篇博客探讨了Laravel框架如何实现Python的解包赋值语法,并指出其存在的一些限制,例如不允许在全局变量初始化和函数参数中使用解包形式。

1395

被折叠的 条评论
为什么被折叠?



