Day6-函数与模块的使用

函数的作用

将一段程序封装起来,加以调用。减少代码的重复量。

定义函数

Python中定义函数用关键字"def",函数名与变量名的命名规则一样。函数名后可以传参。函数执行完毕后可以通过return 关键字返回一个值。

函数的参数

函数是绝大多数编程语言中都支持的一个代码“构建块”。但Python有一些特别之处,比如Python对函数参数的处理。在Python中,函数的参数可以有默认值,也支持可变参数(不确定参数的个数)。所以Python不需要像其他语言一样支持“函数的重载”。

Java支持函数重载。当多个同名函数的形式参数的数据类型不同或数量不同时,就构成了函数的重载。[1][2]

public class Test{
    public void A(){                //这是一个无形式参数名称为A的函数。
        
    }
    public void A(int a){           //这个函数有一个数据类型为int的函数,函数数目不同,故构成重载。
        
    }
    public void A(String a){        //这个函数数据类型为String,形式参数的数据类型不同,故构成重载。
        
    }
    public void A(int a,int b){     //这个函数有两个形式参数,故构成重载。
        
    }
    public int A(int a,String b){    //这个函数的第二个形式参数的数据类型与上一个函数的数据类型不同,故构成重载。
        /*
        重载的函数的返回值可以不同,但是如果两个或多个函数只有返回值不同,不足以构成重载。
        */
    }
}
"""
from random import randint

def roll_dice(n=2):
    #摇色子
    total = 0
    for _ in range(n):
        total += randint(1, 6)
    return total

def add(a=0, b=0, c=0):
    #三个数相加
    return a + b +c


#如果没有指定参数那么使用默认值摇两颗色子
print(roll_dice())
#摇3颗色子
print(roll_dice(3))
print(add())
print(add(1))
print(add(c=50, a=100, b=200))
"""

上面两个函数的参数都设置了默认值,表示如果没有传参,函数就会使用默认值来执行。所以这跟其他语言的函数重载的效果是一致的。

 

上面的代码也可以优化,当不知道函数有多少个参数的时候,可以用可变参数

"""
# 在参数名前面的*表示args是一个可变参数
def add(*args):
    total = 0
    for val in args:
        total += val
    return total

#在调用add函数可以传入0个或多个参数
print(add())
print(add(1, 2, 3))
"""

 

 

变量的作用域:

def foo():
    b = 'hello'#b是局部变量,
    #Python中可以在函数内部定义函数
    def bar():
        c = True
        print(a)
        print(b)
        print(c)
    bar()

if __name__ == '__main__':
    a = 100#a是全局变量
    foo()

100
hello
True

上面的代码能够顺利的执行并且打印出100、hello和True,但我们注意到了,在bar函数的内部并没有定义ab两个变量,那么ab是从哪里来的。我们在上面代码的if分支中定义了一个变量a,这是一个全局变量(global variable),属于全局作用域,因为它没有定义在任何一个函数中。在上面的foo函数中我们定义了变量b,这是一个定义在函数中的局部变量(local variable),属于局部作用域,在foo函数的外部并不能访问到它;但对于foo函数内部的bar函数来说,变量b属于嵌套作用域,在bar函数中我们是可以访问到它的。bar函数中的变量c属于局部作用域,在bar函数之外是无法访问的。事实上,Python查找一个变量时会按照“局部作用域”、“嵌套作用域”、“全局作用域”和“内置作用域”的顺序进行搜索,前三者我们在上面的代码中已经看到了,所谓的“内置作用域”就是Python内置的那些标识符,我们之前用过的inputprintint等都属于内置作用域。

 

def foo():
    a = 200#局部变量
    print(a) #200
    

if __name__ == '__main__':
    a = 100#a是全局变量
    foo()
    print(a)#100

在调用foo函数后,我们发现a的值仍然是100,这是因为当我们在函数foo中写a = 200的时候,是重新定义了一个名字为a的局部变量,它跟全局作用域的a并不是同一个变量,因为局部作用域中有了自己的变量a,因此foo函数不再搜索全局作用域中的a。如果我们希望在foo函数中修改全局作用域中的a,代码如下所示。

 

def foo():
    global a# 声明a是全局变量
    a = 200
    print(a)#200

if __name__ == '__main__':
    a = 100
    foo()
    print(a) #200

我们可以使用global关键字来指示foo函数中的变量a来自于全局作用域,如果全局作用域中没有a,那么下面一行的代码就会定义变量a并将其置于全局作用域。同理,如果我们希望函数内部的函数能够修改嵌套作用域中的变量,可以使用nonlocal关键字来指示变量来自于嵌套作用域,请大家自行试验。

 

在实际开发中,我们应该尽量减少对全局变量的使用,因为全局变量的作用域和影响过于广泛,可能会发生意料之外的修改和使用,除此之外全局变量比局部变量拥有更长的生命周期,可能导致对象占用的内存长时间无法被垃圾回收。事实上,减少对全局变量的使用,也是降低代码之间耦合度的一个重要举措,同时也是对迪米特法则的践行。减少全局变量的使用就意味着我们应该尽量让变量的作用域在函数的内部,但是如果我们希望将一个局部变量的生命周期延长,使其在定义它的函数调用结束后依然可以使用它的值,这时候就需要使用闭包,这个我们在后续的内容中进行讲解。

说明: 很多人经常会将“闭包”和“匿名函数”混为一谈,但实际上它们并不是一回事,如果想了解这个概念,可以看看维基百科的解释或者知乎上对这个概念的讨论。

说了那么多,其实结论很简单,从现在开始我们可以将Python代码按照下面的格式进行书写,这一点点的改进其实就是在我们理解了函数和作用域的基础上跨出的巨大的一步。

 

def main():
    #add your code here
    pass

if __name__ =='__main__':
    main()

 

posted @ 2021-03-05 15:32  三千杨柳细如丝  阅读(58)  评论(0)    收藏  举报