一,函数的非固定参数

1.1 默认参数

在定义形参的时候,提前给形参赋一个固定的值。

#代码演示:
def test(x,y=2):    #形参里有一个默认参数
    print (x)
    print (y)

test(3)    

#输出结果

3
2

默认参数特点:

调用函数的时候,默认参数非必须传递

默认参数的用途:

  • 安装一个软件的时候,都会有所谓默认安装和自定义安装之类的选项。那么默认安装是什么呢?就是软件预先已经设定好了安装的内容参数,你只要执行就会按照默认参数的方式进行安装。
  • 连接数据库的端口号,或者xshell链接Linux默认的22端口等等

1.2 参数组

元组式参数组

不知道同学们想到了没有,假如我们在定义实参的时候,想让实参的个数可以不固定。那么我们该如何定义形参呢?

#代码演示:

def test(*args) :   #以*开头定义实参
    print(args)

test(1,2,3,4,5)
test(*[1,2,3,4,5,5])    #特殊调用

#输出结果

[root@localhost chen]# python3 file.py 
(1, 2, 3, 4, 5) 
(1, 2, 3, 4, 5, 5)

综上,我们可知,当我们需要传递多个实参,而又无法确定实参的个数时,我们可以采用*号加上变量名的形式定义形参,这样形参就可以接收无限量的实参。并把他们放到一个元组里。

#代码演示:

def test(x,*Mr_chen) :
    print (x)
    print (Mr_chen)                                                               

test(1,2,3,4,5,6,7)

#输出结果

[root@localhost chen]# python3 file.py 
1       #位置参数按顺序对应
(2, 3, 4, 5, 6, 7)

结论:元组式参数组就是将多个位置参数转化成元组进行接收的过程。

键值式参数组

#代码演示

def test(**kwargs) :
    print (kwargs)

test(name="Mr.chen",age="33",sex="男")
test(**{"name":"alex","age":8}) #特殊调用 #直接以参数组方式传递

#输出结果

[root@localhost chen]# python3 file.py 
{'name': 'Mr.chen', 'sex': '男', 'age': '33'}
{'name': 'alex', 'age': 8}

思考
如果我们在函数体里要取所传递的字典的值,那么我们是不是也能按照字典取值的方式呢?

#代码演示:

def test(**kwargs) :
    print (kwargs)
    print (kwargs["name"])
    print (kwargs["age"])
    print (kwargs["sex"])

test(name="Mr.chen",age="33",sex="男")
test(**{"name":"alex","age":8,"sex":"男"})  

#输出结果

[root@localhost chen]# python3 file.py 
{'age': '33', 'name': 'Mr.chen', 'sex': '男'}
Mr.chen
33
男
{'age': 8, 'name': 'alex', 'sex': '男'}
alex
8
男

结论:键值式参数组就是将多个关键字参数转化成字典进行接收的过程。

参数组的用途:

在最早我们设计软件的时候,刚开始也许我们估计的形参个数刚好够用,但是到了软件后期,我们也许会发现形参的个数不够了。那么,为了不总是动不动就修改源代码,我们可以多定义一个参数组,来接收多余的实参。

1.3 练习:参数的混合应用示例

请同学们判断以下调用示例正确与否

#例1

def test(name,**kwargs) :
    print (name)
    print (kwargs)

test(name="Mr.chen") 

#例2

def test(name,**kwargs) :
    print (name)
    print (kwargs)

test("Mr.chen")

#例3

def test(name,**kwargs) :
    print (name)
    print (kwargs)

test("Mr.chen",18) 

#例4

def test(name,**kwargs) :
    print (name)
    print (kwargs)

test("Mr.chen",age=18,sex="man")        

#例5

def test(name,age=18,**kwargs) :
    print (name)
    print (age)                                                   
    print (kwargs)

test("Mr.chen")

#例6

def test(name,age=18,**kwargs) :
    print (name)
    print (age)
    print (kwargs)

test("Mr.chen",22,age=26) 

#例7

def test(name,age=18,**kwargs) :
    print (name)
    print (age)
    print (kwargs)

test("Mr.chen",22,sex="man")  

#例8

def test(name,age=18,**kwargs,sex="man") :
    print (name)
    print (age)
    print (sex)                                      
    print (kwargs)

test("Mr.chen",22,sex="man")

#例9

def test(name,age=18,**kwargs) :
    print (name)
    print (age)
    print (kwargs)

test("Mr.chen",sex="man",hobby="Tom",age=22) 

#例10

def test(name,age=18,*args,**kwargs) :
    print (name)
    print (age)
    print (args)
    print (kwargs)

test("Mr.chen",sex="man",hobby="Tom",age=22)

#例11

def test(name,age=18,*args,**kwargs) :
    print (name)
    print (age)
    print (args)
    print (kwargs)

test(name="Mr.chen",22,sex="man",44)

#例12

def test(name,age=18,*args,**kwargs) :
    print (name)
    print (age)
    print (args)
    print (kwargs)

test("Mr.chen",22,sex="man",44)  

#例13

def test(name,age=18,*args,**kwargs) :
    print (name)
    print (age)
    print (args)
    print (kwargs)

test("Mr.chen",22,44,sex="man")    

#例14

def test(age=18,name,*args,**kwargs) :
    print (name)
    print (age)
    print (args)
    print (kwargs)

test("Mr.chen")       

#例15

def test(name,age=18,**kwargs,*args) :  
    print (name)
    print (age)
    print (args)
    print (kwargs)

test("Mr.chen")

特别提示:

  • 在写形参的时候,必须按照位置参数,关键字参数,元组式参数组,键值式参数组的顺序。
  • 在写实参的时候位置参数必须在关键字参数的前边,并且已经被位置参数一一对应赋值的形参,不能再被关键字参数赋值。

1.4 Python解释器的解析顺序

同学们比较下面的两个例子,看看运行的结果有何不同?分析下不同的原因。

#例1

def test(name,age=18,*args,**kwargs) :                            
    print (name)
    print (age)
    print (args)
    print (kwargs)
    logger("TEST")

def logger(sorce) :
    print ("这是一个{}".format(sorce))

test("Mr.chen",22,"TOM",sex="man")



#例2

def test(name,age=18,*args,**kwargs) :
    print (name)
    print (age)
    print (args)
    print (kwargs)
    logger("TEST")

test("Mr.chen",22,"TOM",sex="man")

def logger(sorce) :
    print ("这是一个{}".format(sorce))

二,作用域,局部与全局变量

全局变量:在函数体外定义赋值的变量
局部变量:在函数体内定义赋值的变量

#代码演示

def change_name(name) :
    print (sex)         #打印全局变量 
    print ("before change",name)    #打印传递进来的全局变量
    name = "Mr.chen"    #修改传递进来的全局变量的值
    age = 23            #局部变量
    print ("after change",name) #修改后的全局变量仅局部生效

name = "yunjisuan"  #全局变量
sex = "man"         #全局变量
change_name(name)   
print (name)        #输出全局变量


#输出结果  

[root@localhost chen]# python3 file.py 
man
before change yunjisuan
after change Mr.chen
yunjisuan   #函数体修改后的全局变量未在全局生效
  • 从上边的代码示例来看,我们发现全局变量sex可以直接在函数体内被打印输出。然后函数体内的变量name的值则不能生效于函数体外。
  • 或许同学们感觉很懵逼,下面我们来慢慢分拆理解这个过程
#例1:

def change_name() :
    print (name)

name = "yunjisuan"
change_name()
print (name)   

#输出结果
[root@localhost chen]# python3 file.py 
yunjisuan
yunjisuan

结论1:在函数体外赋值的变量我们称为全局变量,变量的赋值生效于全局范围,因此,不论函数体内还是函数体外,我们都可以直接调用输出它的值。

#例2

def change_name() :
    age = 18                                                      
    print (name)
    print (age)

name = "yunjisuan"
change_name()
print (name)

#输出结果

[root@localhost chen]# python3 file.py 
yunjisuan
18
yunjisuan

结论2:函数体内赋值的变量我们可以在函数体内调用并输出它的值。
概念扩展:但是,我们不能在函数体外来调用输出它。演示如下:

#代码演示

def change_name() :
    age = 18
    print (name)
    print (age)

name = "yunjisuan"
change_name()
print (name)
print (age)     #输出函数体内定义的变量

#输出结果

NameError: name 'age' is not defined    #age未在全局定义

假如,我们要在全局应用一个在函数体内定义的变量,那么我们该如何做呢?

#例3

def change_name() :
    age = 18
    print (name)
    print (age)
    return age      #我们需要将函数体内的age的值返回                                             

name = "yunjisuan"
age = change_name() #赋值给全局的age变量
print (name)
print (age)

#输出结果

[root@localhost chen]# python3 file.py 
yunjisuan
18
yunjisuan
18

结论3:要在函数体外应用一个局部变量,那么我们需要通过return来将局部变量的值返回,并赋值给一个全局变量,然后输出它。

我们思考一下:

假如一个变量已经在全局被定义了,我们还能创建一个同样的局部变量吗?

#例4

def change_name() :
    name = "Mr.chen"
    print (name)

name = "yunjisuan"
change_name()                                                     
print (name)

#输出结果

[root@localhost chen]# python3 file.py 
Mr.chen     #函数体内的输出
yunjisuan   #函数体外的输出

结论4:当我们在函数体内赋值了一个和函数体外的全局变量名字完全一样的局部变量时。局部变量的生效范围只能发生在函数体内,而函数体外则是全局变量的生效范围。

我们继续思考一下:

假如一个变量已经在函数体内被定义了。那么我们还能按照全局规则调用它吗?

#例5

def change_name() :
    print (name)        #输出全局变量name
    name = "Mr.chen"
    print (name)

name = "yunjisuan"
change_name()
print (name)

#输出结果

UnboundLocalError: local variable 'name' referenced before assignment  #本地变量name没有被定义

结论5:一旦一个变量名在函数体内被定义,那么它就会被这个函数体认为是局部变量。你只要在函数体内输出它,都会被认为是局部变量,而不是全局变量。

我们还得思考一下:

假如我们需要将局部变量的值带回到全局生效,那么我们有没有什么办法呢?

方法一: global关键字

#代码演示:

def change_name() :
    global name     #将局部变量的作用域声明全局生效
    name = "Mr.chen"
    print (name)

name = "yunjisuan"                                                
change_name()
print (name)

#输出结果

[root@localhost chen]# python3 file.py 
Mr.chen
Mr.chen

方法二: return关键字

#代码演示

def change_name() :
    name = "Mr.chen"
    print (name)
    return name

name = "yunjisuan"
print (name)                                                      
name = change_name()
print (name)

#输出结果

[root@localhost chen]# python3 file.py 
yunjisuan
Mr.chen
Mr.chen

结论6:假如我们需要将局部变量的值带回给全局变量并让他全局能够生效。那么我们需要通过return关键字将局部变量的值返回给全局变量并赋值。

我们最后思考一下:

假如我们需要在函数体内调用全局变量的值,然而在这个函数体内我们已经定义过了和全局变量名字完全一样的局部变量,而且在函数体整个运行的过程中,这个变量的值也许会发生改变。那么通常我们又该如何做呢?

#例6

def change_name(age) :
    if age == 22 :
        age = 44
        return age
    return age


age = 22
age = change_name(age)
print (age)    

#输出结果

[root@localhost chen]# python3 file.py 
44

结论7:如果我们想要将全局被定义的变量经过函数体内的代码进行判断,并且将判断的结果返回给全局,那么我们需要将全局变量以实参的方式传递给函数体的形参进行接收,最后将判断的结果返回到全局并赋值。

特别提示:

  • 在我们写代码的时候,一定不要用global的方式将局部变量声明全局生效。这是因为,一旦global我们用多了,你就会忘记哪个局部变量被生效过了。一旦发生这样的事情,当我们代码写的很多了以后,你就会频繁发生变量冲突,这样非常麻烦。
  • 所以,我们在写代码的时候,一定要养成好的习惯。采用传参后return的方式将结果返回给全局变量从而全局生效。