Python函数学习——初步认识

函数使用背景

假设老板让你写一个监控程序,24小时全年无休的监控你们公司网站服务器的系统状况,

当cpu\memory\disk等指标的使用量超过阀值时即发邮件报警,

你掏空了所有的知识量,写出了以下代码

从图中可以看出上述代码存在了两个问题

1.代码冗余

2.维护性差

我们如何解决这个问题?请看下图

从上图可以看出:使用函数可以带来以下好处

  1. 减少重复代码
  2. 使程序变的可扩展
  3. 使程序变得易维护

函数定义

函数一词来源于数学,但编程中的「函数」概念,与数学中的函数是有很大不同的,具体区别,我们后面会讲,编程中的函数在英文中也有很多不同的叫法。在BASIC中叫做subroutine(子过程或子程序),在Pascal中叫做procedure(过程)和function,在C中只有function,在Java里面叫做method。

定义: 函数是指将一组语句的集合通过一个名字(函数名)封装起来,要想执行这个函数,只需调用其函数名即可

 

代码表现形式:

 def  函数名(参数1,参数2):

    ''' 函数注释'''

    print('函数体')

    return 返回值

        定义: def关键字开头,空格之后接函数名和圆括号,最后还要加一个冒号。

          def是固定的,不能变。

  函数名:函数名是包含字母,数字,下划线的任意组合,但是不能以数字开头。虽然函数名可以随便取名,但是一般尽量定义成可以表示函数功能的。

      

#下面这段代码
a,b = 5,8
c = a**b
print(c)


#改成用函数写
def calc(x,y):
    res = x**y
    return res #返回函数执行结果

c = calc(a,b) #结果赋值给c变量
print(c)

 

函数参数

使用参数可以让你的函数更灵活,根据调用时传参的不同来决定函数内部的执行流程

 

1.实参和形参

形参: 是函数定义时候定义的参数
    只有在被调用时才分配内存单元,在调用结束时,即刻释放所分配的内存单元。因此,形参只在函数内部有效。

实参: 调用的时候传进来的参数
   可以是常量、变量、表达式、函数等,无论实参是何种类型的量,在进行函数调用时,它们都必须有确定的值,
以便把这些值传送给形参。因此应预先用赋值,输入等办法使参数获得确定值

 

 2.参数传递

可以传递多个参数,多个参数之间用逗号隔开。
调用函数时传参数有两种方式:
  1.按照位置传参数
  2.按照关键字传参数
正常情况下,给函数传参数要按顺序,不想按顺序就可以用关键参数,只需指定参数名即可(指定了参数名的参数就叫关键参数),
但记住一个要求就是,关键参数必须放在位置参数(以位置顺序确定对应关系的参数)之后
用法:1.位置参数必须在关键字参数的前面
   2.对于一个参数只能赋值一次

def stu_register(name, age, course,country):
    print("----注册学生信息------")
    print("姓名:", name)
    print("age:", age)
    print("国籍:", country)
    print("课程:", course)

stu_register("alex", 23, "python", "china")  # 按照位置传递参数

stu_register("alex", 23, course = "python", country = "china")  # 按照位置和关键字传递参数

# 以下两种使用方法不正确
stu_register("alex",  "python", 23, "china")  # 位置不对

stu_register("王山炮", 22, age=25,country='JP')  # age参数被定义两遍

 

 3.默认参数

用法:为什么要用默认参数?将变化比较小的参数设置成默认参数(比如中国网站上注册用户功能,像国籍这种信息,就可以设置个默认值参数country=CN)

定义:默认参数可以不传,不传的时候用的就是默认值,如果传,会覆盖默认值。

def stu_register(name, age, course, country = "CN"):
    print("----注册学生信息------")
    print("姓名:", name)
    print("age:", age)
    print("国籍:", country)
    print("课程:", course)

stu_register("alex", 23, "python")  # 使用默认参数

stu_register("Bill", 23, "python", "Japan")  # 覆盖默认参数

 默认参数使用注意点——可变数据类型 (字符串,数字等)

 默认参数使用注意点——不可变数据类型(列表,字典等)

 

 

 4.动态参数

按位置传值多余的参数都由args统一接收,保存成一个元组的形式

按关键字传值接受多个关键字参数,由kwargs接收,保存成一个字典的形式

元祖
def stu_register(name, age, *args):  # *args 会把多传入的参数变成一个元组形式
    print(name,age,args)

stu_register("Alex",22)
# 输出
# Alex 22 () #后面这个()就是args,只是因为没传值,所以为空

stu_register("Jack",32,"CN","Python")
# 输出
# Jack 32 ('CN', 'Python')
字典
def stu_register(name, age, *args, **kwargs): # *kwargs 会把多传入的参数变成一个dict形式
    print(name,age,args,kwargs)

stu_register("Alex",22)
#输出
#Alex 22 () {}#后面这个{}就是kwargs,只是因为没传值,所以为空

#传参数的时候:必须先按照位置传参数,再按照关键字传参数
stu_register("Jack",32,"CN","Python",sex="Male",province="ShanDong")
#输出
# Jack 32 ('CN', 'Python') {'province': 'ShanDong', 'sex': 'Male'}

def fun(a,b,**kwargs):
    print(a,b,kwargs)

# 按照关键字传参数
fun(a = 10,b = 20,cccc= 30,dddd = 50)
# 输出
# 10 20 {'cccc': 30, 'dddd': 50}



def f(a,b,*args,defult=6,**kwargs):
    #位置参数,*args, 默认参数,**kwargs
    # print(a,b,args,defult,kwargs)
    return a,b,args,defult,kwargs

# 传参数的时候:必须先按照位置传参数,再按照关键字传参数
print(f(1,2,7,8,ccc=10,der=5)) 
# 输出
# 1,2,(7,8),6,{'ccc':10,'der':5}
动态参数拆包与装包

1.*args 

def run(a,*args):
    #第一个参数传给了a
    print(a)  # 1
    # args是一个元组,里面是2和3两个参数
    print(args)  # (2,3)
    # *args是将这个元组中的元素依次取出来
    print("对args拆包")
    print(*args) # 2 3 *args 相当于 a,b = args
    print("将未拆包的数据传给run1")
    run1(args)  # ((2, 3),) 和 (2, 3)
    print("将拆包后的数据传给run1")
    run1(*args)  # (2, 3)和2 3

def run1(*args):
     print("输出元组")
     print(args)
     print("对元组进行拆包")
     print(*args)

run(1,2,3) #后面的2和3

输出结果

1
(2, 3)
对args拆包
2 3
将未拆包的数据传给run1
输出元组
((2, 3),)
对元组进行拆包
(2, 3)
将拆包后的数据传给run1
输出元组
(2, 3)
对元组进行拆包
2 3

"""
理解这段代码需要把握住下面几点:
1. 形参中的*args其实真正接收数据的args,它是一个元组,把传进来的数据放在了args这个元组中。
2. 函数体里的args依然是那个元组,但是*args的含义就是把元组中的数据进行拆包,也就是把元组中的数据拆成单个数据。
3. 对于args这个元组,如果不对其进行解包,就将其作为实参传给其它以*args作为形参的函数时,args这个元组会看看作一个整体,作为一个类型为元组的数据传入。

"""

 

2. **kwargs

def run(**kwargs):
    """
    传来的 key = value 类型的实参会映射成kwargs里面的键和值
    kwargs是一个字典,将关键字参数以键值对的形式
    """
    print(kwargs)  # {'a':1,'b':2}
    print("对kwargs拆包")
    #  此处可以把**kwargs理解成对字典进行了拆包,{"a":1,"b":2}的kwargs字典又
    # 被拆成了a=1,b=2传递给run1,但是**kwargs是不能像之前*args那样被打印出来看的
    run1(**kwargs)  # 1 2
    # print(**kwargs)


def run1(a, b):  # 此处的参数名一定要和字典的键的名称一致
    print(a, b)

run(a=1, b=2)
d = {'a':1,'b':2}
run(**d)  # 输出结果与执行run(a=1, b=2)一样

输出结果

{'a': 1, 'b': 2} #run(a=1,b=2)
对kwargs拆包
1 2
{'a': 1, 'b': 2} #run(**d)
对kwargs拆包
1 2

"""
def run(**kwargs):#传来的 key = value 类型的实参会映射成kwargs里面的键和值
# kwargs是一个字典,将未命名参数以键值对的形式
print(kwargs)
print("对kwargs拆包")
# 此处可以把**kwargs理解成对字典进行了拆包,{"a":1,"b":2}的kwargs字典又
# 被拆成了a=1,b=2传递给run1,但是**kwargs是不能像之前*args那样被打印出来看的
run1(**kwargs)
#print(**kwargs)
def run1(a,b): #此处的参数名一定要和字典的键的名称一致

对于kwargs这个字典,如果不对其进行解包,就将其作为实参传给其它以**kwarg作为形参的函数时,kwarg这个字典会看看作一个整体,作为一个类型为字典的数据传入
"""

总结:

1. *args作为形参时是用来接收多余的位置参数,而**kwargs是用来接收key=value这种类型的关键字参数,args是元组,kwargs是字典。
2. *和**在函数体中除了拆包之外,并没有什么卵用。
3. 装包的含义就是把位置参数和关键字参数分别放在元组或者字典中。

函数返回值

函数外部的代码要想获取函数的执行结果,就可以在函数里用return语句把结果返回

1. 首先返回值可以是任意的数据类型。

2..函数可以有返回值:如果有返回值,必须要用变量接收才有效果

   也可以没有返回值:

    1.当不写return的时候,函数的返回值为None

    2.当只写一个return的时候,函数的返回值为None

3. 函数在执行过程中只要遇到return语句,就会停止执行并返回结果,so 也可以理解为 return 语句代表着函数的结束

def stu_register(name,age,course):

    print(name, age, course) # Peiqi 29 安保

    return [name, age] # 返回列表形式

status = stu_register('Peiqi',29,'安保')
print(status) # ['Peiqi', 29]


def stu_register(name,age,course):

    print(name, age, course) # Peiqi 29 安保

status = stu_register('Peiqi',29,'安保')
print(status) # 无返回值时,返回None

def stu_register(name,age,course):

    print(name, age, course) # Peiqi 29 安保
    return
    print(name, age) # 此句不会执行

status = stu_register('Peiqi',29,'安保')
print(status) # return后面没有值,返回None

 

posted @ 2018-03-15 16:47  一只小小的寄居蟹  阅读(1480)  评论(0编辑  收藏  举报