第10天 函数详解

2018-10-14 10:50:26来源:博客园 阅读 ()

新老客户大回馈,云服务器低至5折

一. 人生三问

1. 什么是函数
  函数就是用def关键字声明的为了实现一组功能的代码块。
2. 为什么要用函数
  在开发程序的过程中,我们可能会遇到一些相同的功能,为了避免写重复的代码,从而出现了函数。
3. 怎么使用函数
  函数分为定义和调用两个阶段,函数名()就是调用函数的过程
  # 函数的定义
  def fun1(x, y, z):
      '''注释'''
      code1
      code2 
      return x, y, z 

  # 函数调用
  fun1(1, 2, 3)

 

二. 函数的使用原则

# 函数在调用过程中必须要先定义才能进行使用

1. 函数的定义阶段
  只检测函数体中是否存在语法错误
2. 函数的调用阶段
  调用过程才会去真正的执行代码块,查看是否有逻辑错误

测试:

def fun1():
    print('from fun1')
    bar()

# 当执行fun1代码的时候发现要执行bar
# 但是bar函数还没有定义,所以报错
fun1()

def bar():
    print('from bar')
View Code

1. 函数的三种定义方式

1. 无参函数
2. 有参函数
3. 空函数

 

2. 函数的三种调用方式

1. 语句的形式 =====》 register()
2. 表达式调用========》 res = register() * 12
3. 作为参数传递调用    ===========》

 

3. return的特点

功能
1. 可以返回值
    1.1 函数的返回值没有类型的限制
    1.2 函数的返回值没有个数的限制
     <1>.返回的是一个值就是函数本身
     <2>.返回多个值就是元祖
     <3>.返回0个值或者干脆没有就返回None
2. 可以结束函数的执行 函数内可以有多个return,只要执行了任意一个return函数就结束了

 

三. 参数详解

参数的分类:

1. 形式参数(形参)
  在函数定义阶段,传递的参数就是形参
2. 实际参数(实参)
  在函数调用阶段,传递的参数就是实参
细分:
  1. 位置参数
  2. 关键字参数(默认形参)
  3. 可变长参数
# 这个是函数的定义阶段
# 里面的参数x, y, z是函数的形参
def fun(x, y, z):
    print('hhhh')

# 这个是函数的调用阶段
# 里面的参数1,2,3是实参
fun(1, 2, 3)

 

1. 位置参数

位置参数:

1. 位置实参
  在调用阶段,按照从左到右的顺序依次定义的参数叫做位置实参
  # 特点:与形参的值一一对应
2. 位置形参
  在定义阶段,按照从左到右的顺序依次定义的参数叫做位置形参
  # 特点:必须被传值,多一个不行,少一个不行

 

验证:

# 形参和实参的值必须一一对应
def fun1(x, y, z):
    print(x, y, z)
fun1(1)  # 会报错
fun1(1, 2, 3)
fun1(1, 2, 3, 4) # 会报错

 

2. 关键字参数

1. 关键字实参
    在函数调用阶段,按照key=value的形式定义的实参叫做关键字实参
   # 特点:
   # 1. 关键字实参必须放在位置实参的后面
   # 2. 不能给同一个形参赋多个值

2. 关键字形参(默认形参) 在函数定义阶段,按照key=value的形式定义的形参叫做关键字形参。我们一般称之位默认形参
# 特点:
   # 1. 在定义阶段已经有值,意味着在调用阶段可以不用传值
   # 2. 位置形参必须放在默认形参的前面
   # 3. 默认形参的值在定义阶段已经固定死了,之后的更改不会影响
   # 4. 默认形参的值通常应该是不可变类型

 

验证:关键字参数

# 关键字参数必须放在位置参数的后面
def fun1(x, y, z):
    print(x, y, z)

fun1(1, y=2, 3) # 报错
fun1(1, 2, z=3)
关键字参数必须放在位置参数的后面
# 不能给同一个形参传递多个值
def fun1(x, y, z):
    print(x, y, z)

fun1(1, 2, 3, x=1)# 会报错
不能给同一个形参传递多个值

 

验证:默认形参

# 调用阶段可以不用传值
def func1(x, y, z=2):
    print(x, y, z)
func1(1, 2)
调用阶段可以不用传值
# 位置形参必须在默认形参之前
def func1(x,z=2, y):# 会报错
    print(x, y, z)
func1(1, 2)
位置形参必须在默认形参之前
# 默认形参的值在定义阶段已经固定死了
m = 10
def func1(x, y, z = m):
    print(x, y, z)
m = 100
func1(1, 2)  # 答案依然是10
默认形参的值在定义阶段已经固定死了
# 默认形参通常应该是不可变类型
# 以下例子的耦合性非常高
m = []
def fun1(x, y, z = m):
    z.append(x)
    z.append(y)
    print(z)
fun1(1,2)
fun1(3,4)
fun1(5,6)
print(m)
# 结果:
# [1, 2]
# [1, 2, 3, 4]
# [1, 2, 3, 4, 5, 6]
# [1, 2, 3, 4, 5, 6]
默认形参通常应该是不可变类型

 

3. 可变长参数

1. 可变长实参
    在函数调用阶段,实参值的个数是不固定的。
2. 可变长形参
    在函数定义阶段,形参值的个数是不固定的。

 

可变长形参

* ====》会将溢出的位置实参存到一个元祖中,然后赋值给*后面的形参中
** =====》会将溢出的关键字实参以key和value的形式存成字典,然后赋值给**后面的形参中

 

可变长实参

*   ========》会将*后面的值打散成一个个的位置实参,然后与形参一一对应
** ========》会将**后面的值以key=value的形式打散成一个个的关键字参数,然后与形参一一对应

 

 

(1) 多出来的位置实参赋值给*args

def fun(x, y, *args): # args=(3, 4, 5,6)
    print(x, y, args)
fun(1, 2, 3, 4, 5, 6)# 多出来3,4,5,6四个位置实参

 

(2)任意数求和:

# 任意数求和
def sum2(*args): # args=(1,2)
    res = 0
    for i in args:
        res += i
    return res
res = sum2(1, 2,3)
print(res)

 

(3)形参和实参的对应关系

def fun(x,y,*z):# z = (1,2,3,4 )
    print(x, y, z)
fun(11,22,*[1, 2,3,4])  # fun(11, 22, 1,2,3,4)

 

(4)*args, **kwargs的应用

# *args,**kwargs可以被当作参数传递

def
index(name, age, sex): print(name, age, sex) def wrapper(*args, **kwargs): # args = ('egon') # kwargs = {'sex': 'male', 'agse': 18} index(*args, **kwargs)# index('egon', sex='male', agse=18) wrapper('egon',sex='male', age='18')

 

4. 命名关键字参数

关键字参数:
    在*和**之间的定义的参数称为命名关键字参数
关键字参数:
    命名关键字参数必须按照key=value的形式进行传值

 

# 在*号后面的形参在实参传递的过程中必须是关键字参数
def fun(x, *, y, z):
    print(x, y, z)

fun(1, y=2, z=3)

 

四. 函数对象

函数对象:
    函数是第一类对象:函数的内存地址可以像一个变量值一样去使用
变量值的使用方法:
1. 可以被引用
  a = 1
  b = a
2. 可以当作参数传递
3. 可以作为返回值
4. 可以当作容器内的元素

1. 函数被引用

def f1():
    print('from f1')

f = f1
f1 = f
f()
f1()

 

2. 可以被当作参数进行传递

def f1(func):
    func()

def f2():
    print('from f2')

f1(f2)

 

3. 可以作为返回值

def f1(func):
    return func

def f2():
    print('from f2')

f1(f2)()

 

4. 作为容器内的元素

作为功能字典使用:

def login():
    print('login.....')

def register():
    print('register.....')

func_dict = {'1': login, '2': register}

choice = input('>>').strip()
if choice in func_dict:
    func_dict[choice]()
else:
    print('wrong!')

 

五. 函数嵌套

函数嵌套的两种类型
  1. 函数的嵌套调用
  2. 函数的嵌套定义

1. 函数的嵌套调用

# 函数的嵌套调用就是把一个复杂的功能拆分成
# 更小的功能,然后去调用它来解决问题
def max2(a, b):
    if a > b:
        return a
    else:
        return b


def max4(a,b,c,d):
    res = max(a, b)
    res = max(res,c)
    res = max(res, d)
    return res
print(max4(1, 2, 3, 4))

 

2. 函数的嵌套定义

# 从这个例子可以看出来,函数的嵌套声明可以把一类函数
# 打包到一块,然后通过传入一个参数执行不同的函数

from math import pi
def circle(banjing, choice):
    '''choice == 1计算周长,其他的计算面积'''  
    def zhouchang():
        return banjing * 2 * pi
    def mianji():
        return pi * banjing ** 2
    if choice == '1':
        res = zhouchang()
    else:
        res = mianji()
    return res

 

六. 名称空间

名称空间
    名称空间就是python的一个专门用来存储名字和值的内存地址映射关系的一个空间。

名称空间的分类
  1. 内置名称空间
  2. 全局名称空间
  3. 本地名称空间

内置名称空间:
  存放的名字:存放的是python解释器自带的名字
  产生:打开python解释器的时候会产生
  销毁:关闭python解释器的时候会销毁

全局名称空间: 
  存放的名字:存放的是顶格写入的p名字
  产生:在执行python文件的时候会产生
  销毁:在python文件执行完毕之后销毁

局部名称空间:
  存放的名字:存放的是函数内的名字
  产生:在执行函数代码体的时候会产生
  销毁:在函数执行完毕之后会销毁


名称空间的加载顺序: 内置名称空间----》全局名称空间-------》局部名称空间
名称空间的销毁顺序: 局部名称空间----》全局名称空间-------》内置名称空间

名称空间的查找顺序: 从当前名称空间一层一层往上查找

 

七. 作用域

作用域:
    作用域其实就是把名称空间分成了两大类
1. 全局作用域: 内置名称空间和全局名称空间的名称
  特点: 全局存活,全局有效
2. 局部作用域: 局部名称空间的名称
  特点: 临时存活,局部有效

 

作用域关系在函数定义阶段已经定义死了,与调用的位置无关:

# 例一:作用域关系在定义阶段已经定义死了
xxx = 2
def f1():
    print(xxx)
    xxx = 1
# 此时函数执行的时候是会报错的
# 原因:当定义函数f1的时候发现局部命名空间中是有xxx这个变量的
# 因此无论什么时候调用f1函数都会使用局部命名空间,因此在调用
# f1()函数的时候,执行print(xxx)中的xxx的时候,发现局部名称空间
# 目前还没有这个变量,因此会报错。
f1()
例一:作用域关系在定义阶段已经定义死了
例二:作用域关系在定义阶段已经定义死了

 

八. 闭包函数

闭包函数运用的知识:
    函数嵌套: 闭包函数就是在一个函数外面包上一层函数
    函数对象: 会返回内部函数的内存地址
    作用域关系:会传递值给内部函数进行使用


# 创建了一个闭包函数innter
def outter(): x = 1 def innter(): # 函数的嵌套 print('from inner', x)  # 函数的作用域关系 return innter # 函数的对象


什么是闭包函数呢?
  闭包函数本质上就是一个函数的嵌套。在嵌套的过程中会牵涉到函数的作用域关系。应用广泛的是装饰器。

 

标签:

版权申明:本站文章部分自网络,如有侵权,请联系:west999com@outlook.com
特别注意:本站所有转载文章言论不代表本站观点,本站所提供的摄影照片,插画,设计作品,如需使用,请与原作者联系,版权归原作者所有

上一篇:第11天内置函数详解

下一篇:python第一课