关于python带默认值的参数只能放在后面的问题

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了关于python带默认值的参数只能放在后面的问题相关的知识,希望对你有一定的参考价值。

python函数带默认值的参数应该放在后面,碰到如下问题:
为什么下面这个函数可以这么定义()
def fun(*x, y=4, z):
print(x)
print(y)
print(z)
而下能这个函数不能这么定义
def fun(x=4, y):
print(x)
print(y)

第一个那个*x是*args的意思,就是任意个参数,C++里的 va_arg,...符号这种,python函数里可以有*args和**kwargs。
*args之后可以随便放有默认值的和没有默认值的。
因为这之后的任何东西都得用”y=1“”z=1“这种来赋值。
比如说第一个函数,def fun(*x, y=4, z),我写fun(1,2,3,4,5),这五个数全跑到*x里了。我想给y和z赋值必须要写fun(1,2,3,4,5,y=6,z=7)。相当于z跟有默认值的那种也差不多了。所以规定可以把z放在*x后面的任何位置,包括y=4后面,它们的位置关系已经没有用了。
规定def fun(x=4, y) y必须放在x前,就是要保留这种位置关系,要不我写fun(1),你说这个1是给x的还是y的?就有了歧义。所以必须要有正确的位置关系。但是*x后面所有东西都是用参数名赋值的,位置关系不起作用了,所以可以任意顺序了。
要理解规定背后的原因。同理,**kwargs放在最后,也是这个原因。消除歧义性。
参考技术A 系统检查机制就是这样的,
假设允许你这样写,那么fun(2)中的2你要传给x还是y呢?解释器就没有办法判断,会有歧义,因为x是可以省略不传值,那么2有可能传给x,也有可能传给y。
第一种虽然可以通过语法检查(应该是*参数影响了原本的验证),也不建议这样使用,你自己试试传值,就知道了。追问

1

ES6—带默认值的函数参数及其作用域

在学习ES6函数一章时,发现了一个有意思的现象,原文描述如下:

技术分享图片

这段话主要state了3个事实:

①函数参数有默认值时,会在声明初始化阶段形成一个单独的作用域

②这个作用域在初始化结束后消失

③没默认值的情况下,没有①②的现象发生。

这就很有意思了,我们一般说函数作用域,一般就是和全局作用域、局部作用域相爱相杀,这下来了个“声明时作用域”,更热闹了,这3者之间到底什么关系呢?

让demo自己来说话吧。

例子1:

    var x = 10;
    function f( x,y=()=>{x=20;console.log(x);} ){//2
        console.log( x );//1
        y();
        var x = 30;
        console.log( x );//3
    }
    f();
    console.log( x );//4

大家觉得上述代码运行会输出什么呢?不卖关子了,直接看结果再来分析吧。

技术分享图片

结果从上到下分别对应代码里面的1、2、3、4输出语句。在主句分析以前,我们先回顾一下 “函数内变量提前声明” 的知识和上述的 “函数声明作用域”的概念,基于这些理论,我们可以得出如下作用域模型:

技术分享图片

在此基础上,我们分析每个结果的由来。

//1: 函数体内声明了与参数同名的变量x,此时根据“变量提前”原则,函数作用域内的x被提前到function头部声明,此时参数x也无默认值传递给它所以,函数内的x是undefined.

//2. 有默认值的函数作用域,在声明是,分别开辟了自己独有的存放x,y的作用域,这里的x与函数体内的x没任何关系,有值的情况下,只是用值初始化一下内部的那个x,执行y访问的当然是自己作用域内的x了,也就是被变成了20的那个x。

//3. 函数体内声明了与参数x同名的变量x,但这个x与参数x没有任何关系,只存在一个初始化赋值的操作。因此此处的x是函数体内作用域的那个存30的x变量

//4. 全局量就不说了,和上述两个作用域都没有关系,不会被改变。

 我们再看看下面的例子,有助于加深理解:有默认值的函数声明作用域和函数作用域有什么关系!

function f1( x=1,y=()=>{x=10;} ){
            y();
            console.log( x );
            var x = ‘abc‘;
        }
        f1();
        function f2( x=1,y=()=>{x=10;} ){
            y();
            console.log( x );//10
            //var x = ‘abc‘;
        }
        f2();

运行结果分别如下:

技术分享图片

分析如下:

f1中,因为参数有默认值,所以参数开辟了自己的作用域,函数体内声明了自己的x,也开辟了自己的作用域,这俩x没任何关系,只是参数用自己的值初始化了函数体内的x,所以它打印了1

f2中,函数体内未声明自己的同名变量,在变量解析的时候就顺着"作用域链"爬到了参数声明作用域内,此时访问的时候就是访问的参数声明作用域内的x。

综上,得到Final conclusion:

1. 有默认值的函数,的确是开辟了单独的“参数声明作用域A”,这个作用域与函数体内的作用域是互不相干的,独立的。

2. 函数体内的同名变量接受来自同名参数的默认赋值,并单独开辟函数体内的一个作用域B.

3. 在作用域B中找不到变量时,程序会攀援“scope-chain”找到作用域A中的变量,并加以操作。

所以,在没有let之前的作用域A和作用域B是互相独立又有“上溯”查找的关系,比较难以理解,虽然没人在传参后再声明一个同名的局部变量,但我们要理解js底层对这种行为的处理方式。

好在,ES6诞生了!let声明重名变量会报错,从根本上杜绝了这种现象的发生。

———学无止境,may stars guide your way

以上是关于关于python带默认值的参数只能放在后面的问题的主要内容,如果未能解决你的问题,请参考以下文章

TypeScript 函数

如何在Python中定义静态变量

sql操作带参数的时候用下面的方法,但是如果某个参数为 空值的话会报错,应该怎么改?

ES6—带默认值的函数参数及其作用域

C#怎么调用方法

python语法基础