一,函数的定义

函数最只要的目的:封装一个功能。
函数对象由函数定义创建。函数对象上的唯一操作是调用它:FUNC(参数列表)。
函数对象实际上有两种风格:内置函数和用户定义函数。两者都支持相同的操作(调用函数),但实现不同,因此对象类型不同。

二,函数的优点是什么

1,减少代码重复率。
2,增强代码可阅读性。

三,函数的组成部分

  1. def my_len(): #关键字,函数名,括号,冒号
  2. list1 = [1, 2, 3, 4]
  3. count = 0
  4. for i in list1:
  5. count = count + 1
  6. print(count)
  7. my_len()

四,执行函数

  1. my_len() #函数名+括号

函数的参数

位置参数

案例1:
求变动的两数之和:
# 函数在传参时,会根据位置一一对应进行传递

  1. def sum(a,b): # a,b为形参
  2. print(a+b)
  3. sum(2,4) # 2,4为实参

>

  1. 6

形参默认值

这里形参定义了默认值,引用函数时没有进行传参,则用默认值

  1. def sum(a=2,b=2,c=3):
  2. print(a+b+c)
  3. return
  4. sum()

>

  1. 7

这里形参定义了默认值,引用函数时进行传参,则默认值没有作用

  1. def sum(a=2,b=2,c=3):
  2. print(a+b+c)
  3. return
  4. sum(4,5,6)

>

  1. 15

关键字参数

可以不按照形参定义的顺序进行传递,直接根据参数名进行参数传递

  1. def sum(a=2,b=2,c=3):
  2. print('a:',a)
  3. print('b:',b)
  4. print('c:',c)
  5. return
  6. sum(b=4,a=5,c=7)

>

  1. a: 5
  2. b: 4
  3. c: 7

位置参数和关键字参数可以混合使用,但是必须将位置参数写在前面

  1. def sum(a=2,b=2,c=3):
  2. print('a:',a)
  3. print('b:',b)
  4. print('c:',c)
  5. return
  6. sum(4,b=5,c=9)

>

  1. a: 4
  2. b: 5
  3. c: 9

参数类型

函数在调用时,解析器不会检查实参的类型
# 实参可以传递任意类型的对象

```python

!/usr/bin/python

-- coding: UTF-8 --

def func1(a): print(a) return

下面可以依次测试,都可以顺利执行

b = 123 b = True b = ‘python’ b = None b = [1,2,3] b = {‘name’: ‘feige’}

func1(b)

  1. <a name="IUHL5"></a>
  2. ### 函数的重新赋值
  3. # 在函数中对形参进行重新赋值,不会影响其他变量
  4. ```python
  5. #!/usr/bin/python
  6. # -*- coding: UTF-8 -*-
  7. # 定义函数
  8. def fn1(a):
  9. a = 10;
  10. print('a = ', a)
  11. c = 30
  12. fn1(c)
  13. print(c)

>

a = 10
30

将c修改为20

#!/usr/bin/python
# -*- coding: UTF-8 -*-
# 定义函数
def fn1(a):
    a = 10;
    print('a = ', a)
c = 20
fn1(c)
print(c)

>

a =  10
20

如果形参执行的是一个对象,当我们通过形参去修改对象时,会影响所有指向该对象的变量。

#!/usr/bin/python
# -*- coding: UTF-8 -*-
# 定义函数
def fn1(a):
    a[0] = 10;
    print('a = ', a)
c = [1,2,3]
fn1(c)
print(c)
a =  [10, 2, 3]
[10, 2, 3]

不定长参数(*)

问题:想求几个数的和,但是几个数字不确定
# 定义函数时,在形参前面加一个,这样这个形参将会获取到所有的实参,它会将所有的实参保存到一个元组中 ```python def fn1(a): print(‘a = ‘,a,type(a))

fn1(2,3,4)

>>>
```python
a =  (2, 3, 4) <class 'tuple  #类型为元组

那么,思路:

#!/usr/bin/python
# -*- coding: UTF-8 -*-
# 定义函数
def fn1(*nums):
   # 定义1个变量,保存和的结果
   result = 0
   # 遍历出每个元素的值
   for i in nums:
       result += i
   print(result)
fn1(2,3,4) # 这里就可以传任意个参数

>

9

带星号的参数只能有一个
# 带星号的参数,可以与其他参数配合使用
# 第一个参数给a,第二个参数给b,剩余的全部给C

#!/usr/bin/python
# -*- coding: UTF-8 -*-
# 定义函数
def fn1(a,b,*c):
    print('a = ',a)
    print('b = ',b)
    print('c = ',c)
fn1(2,3,4,5,6)

>

a =  2
b =  3
c =  (4, 5, 6)

可变参数不是必须写在最后,但是注意,带*的参数后的所有参数,必须以关键字参数的形式传递

#!/usr/bin/python
# -*- coding: UTF-8 -*-
# 定义函数
def fn1(a,*b,c):
    print('a = ',a)
    print('b = ',b)
    print('c = ',c)
fn1(2,3,4,5,c=6)  # 这里由于*b在中间,c必须指定值,否则会报错

>

a =  2
b =  (3, 4, 5)
c =  6

所有位置参数都给a,则b和c都需要使用关键字参数

#!/usr/bin/python
# -*- coding: UTF-8 -*-
# 定义函数
def fn1(*a,b,c):
    print('a = ',a)
    print('b = ',b)
    print('c = ',c)
fn1(2,3,4,b=5,c=6)

>

a =  (2, 3, 4)
b =  5
c =  6

如果在形参的开头直接写一个*,则要求所有参数必须以关键字参数进行传递

#!/usr/bin/python
# -*- coding: UTF-8 -*-
# 定义函数
def fn1(*,a,b,c):
    print('a = ',a)
    print('b = ',b)
    print('c = ',c)
fn1(a=4,b=5,c=6)

>

a =  4
b =  5
c =  6

不定长参数(**)

*形参只能接受位置参数,不能接受关键字参数,如下:

#!/usr/bin/python
# -*- coding: UTF-8 -*-
# 定义函数
def fn1(*a):
    print('a = ',a)
fn1(a=4,b=5,c=6)

>

Traceback (most recent call last):
  File "py01.py", line 8, in <module>
    fn1(a=4,b=5,c=6)
TypeError: fn1() got an unexpected keyword argument 'a'

**形参可以接受其他的关键字参数,它会将这些参数保存到一个字典中,字典的key就是参数的名字,字典的value就是参数的值

```python

!/usr/bin/python

-- coding: UTF-8 --

定义函数

def fn1(**a): print(‘a = ‘,a,type(a))

fn1(b=5,c=6)

>>>
```python
a =  {'b': 5, 'c': 6} <class 'dict'>

**形参只能有一个,并且必须写在所有参数的最后

#!/usr/bin/python
# -*- coding: UTF-8 -*-
# 定义函数
def fn1(b,c,**a):
    print('a = ',a,type(a))
    print('b = ', b)
    print('c = ', c)
fn1(b=1,c=2,d=8,f=9)

>

a =  {'d': 8, 'f': 9} <class 'dict'>
b =  1
c =  2

参数的解包

```python

!/usr/bin/python

-- coding: UTF-8 --

定义函数

def fn1(a,b,c): print(‘a = ‘, a) print(‘b = ‘, b) print(‘c = ‘, c)

创建1个元组

t= (10,20,30)

fn1(t) #这样会报错,会将t认为是1个参数

更改:
```python
#!/usr/bin/python
# -*- coding: UTF-8 -*-
# 定义函数
def fn1(a,b,c):
    print('a = ', a)
    print('b = ', b)
    print('c = ', c)

# 创建1个元组
t= (10,20,30)

fn1(t[0],t[1],t[2])

>

a =  10
b =  20
c =  30

但是上面写法比较不便
传递实参时,也可以在序列类型的参数前加*号,这样它会自动将序列中的元素依次作为参数传递
这里要求序列中元素的个数必须和形参的个数保持一致 ```python

!/usr/bin/python

-- coding: UTF-8 --

定义函数

def fn1(a,b,c): print(‘a = ‘, a) print(‘b = ‘, b) print(‘c = ‘, c)

创建1个元组

t= (10,20,30)

fn1(t) #解包元组用

>>>
```python
a =  10
b =  20
c =  30
#!/usr/bin/python
# -*- coding: UTF-8 -*-
# 定义函数
def fn1(a,b,c):
    print('a = ', a)
    print('b = ', b)
    print('c = ', c)

# 创建1个字典
dict1 = {'a': 100, 'b':200, 'c':300}

fn1(**dict1) # 解包字典用**

>

a =  100
b =  200
c =  300

函数的返回值:

return 的作用停止函数体的运行。和break的功能一样。并且返回给这个函数一个值。
1> return 空或者没有return时,打印函数执行者时,为None ```python def func1(): print (888) return

def func2(): print(9)

print(func1()) print(func2())

>>>
```python
888
None
9
None

2,return 加一个值,返回给函数这个值,这个值是什么类型就返回什么类型
3,return 加多个值是,返回给函数一个由这些值组成的元组,如

def func2():
    name = 'python'
    return  name
def func3():
    name = 'python'
    money = 19990
    return name,money
print(func2())
print(func3())

>

python
('python', 19990)

函数多个返回值

# -*- coding: utf-8 -*-
# @Author: afei
def func1(a, b):
    x = a // b
    y = a % b
    return x,y  # 实际上返回一个元组
print(func1(12,7))

>

(1, 5)

文档字符串

DocStrings 文档字符串是一个重要工具,用于解释文档程序,帮助你的程序文档更加简单易懂。
我们可以在函数体的第一行使用一对三个单引号 ‘’’ 或者一对三个双引号 “”” 来定义文档字符串。
你可以使用 doc(注意双下划线)调用函数中的文档字符串属性。 ```python

!/usr/bin/python

-- coding: UTF-8 --

def printMax(x,y): ‘’’打印两个数中的最大值。

两个值必须都是在整形数。'''
x=int(x)
y=int(y)
if x>y:
    print(x,'最大')
else:
    print(y,'最大')

printMax(3,5)

>>>
```python
5 最大
打印两个数中的最大值。

    两个值必须都是在整形数。

help函数
查看某个函数的使用说明书

#!/usr/bin/python
# -*- coding: UTF-8 -*-
# 定义函数
def fn4(*a):
    return a
result1 = fn4(1,2,3)
#print(result1)
help(print)

> ```python Help on built-in function print in module builtins:

print(…) print(value, …, sep=’ ‘, end=’\n’, file=sys.stdout, flush=False)

Prints the values to a stream, or to sys.stdout by default.
Optional keyword arguments:
file:  a file-like object (stream); defaults to the current sys.stdout.
sep:   string inserted between values, default a space.
end:   string appended after the last value, default a newline.
flush: whether to forcibly flush the stream.
<a name="c2wky"></a>
### 函数作用域
也叫名称空间<br />全局名称空间:创建的存储“变量名与值的关系”的空间叫做**全局名称空间**<br />局部名称空间:在函数的运行中开辟的临时的空间叫做**局部名称空间**<br />内置名称空间:内置名称空间中存放了python解释器为我们提供的名字:input,print,str,list,tuple...它们都是我们熟悉的,拿过来就可以用的方法。

---

python中的作用域分4种情况:

- L:local,局部作用域,即函数中定义的变量;
- E:enclosing,嵌套的父级函数的局部作用域,即包含此函数的上级函数的局部作用域,但不是全局的(闭包常见);
- G:globa,全局变量,就是模块级别定义的变量;
- B:built-in,系统固定模块里面的变量,比如int, bytearray等。

加载变量的优先级顺序依次是:py 内置作用域>当前模块中的全局(文件从上而下读取)>外层作用域>局部作用域<br />搜索变量的优先级顺序依次是:作用域局部>外层作用域>当前模块中的全局>python内置作用域,也就是LEGB。<br />当然,local 和 enclosing 是相对的,enclosing 变量相对上层来说也是 local 。
```python
# -*- coding: utf-8 -*-
# @Atthor: fei
def fn1():
    a = 18
    print('a = ', a)

def fn2():
    print('a = ', a)
fn1()
fn2()  # 会报错,因为fn2识别不到a

>

D:\Python-study>python py01.py
a =  18
Traceback (most recent call last):
  File "py01.py", line 10, in <module>
    fn2()
  File "py01.py", line 8, in fn2
    print('a = ', a)
NameError: name 'a' is not defined

```python

-- coding: utf-8 --

@Atthor: fei

x=6 def f2(): print(x) x=5 f2()

变量是先声明,再引用的

错误的原因在于 print(x),解释器会在局部作用域找,会找到x=5(函数已经加载到内存),但x使用在声明前了,所以报错:

local variable ‘x’ referenced before assignment.如何证明找到了x=5呢?简单:注释掉x=5,x=6

报错为:name ‘x’ is not defined

同理

x=6 def f2(): x+=1 #local variable ‘x’ referenced before assignment. x 使用之前已经被声明了

x+=1:x = x + 1;x 已经被声明了,x=6,这里等于 6 = 6 + 1,发生报错

<a name="Y93c3"></a>
### 变量的修改
<a name="lx8QE"></a>
#### 关键字global:
```python
# -*- coding: utf-8 -*-
# @Atthor: fei
x=6
def f2():
    global x
    print(x)
    x+=1 
f2()
print(x)

>

6
7

但是Global只能修改全部变量,下面就会报错: ```python

-- coding: utf-8 --

@Atthor: fei

def f2(): x=6 def f3(): global x print(x) x+=1 f2() print(x)

>>>
```python
Traceback (most recent call last):
  File "py01.py", line 11, in <module>
    print(x)
NameError: name 'x' is not defined

关键字nonlocal:

global关键字声明的变量必须在全局作用域上,不能嵌套作用域上,当要修改嵌套作用域(enclosing作用域,外层非全局作用域)中的变量怎么办呢,这时就需要nonlocal关键字了

# -*- coding: utf-8 -*-
# @Atthor: fei
x = 100
def f2():
    x=6
    def f3():
        nonlocal x
        x = 9
        print(x)
    f3()
f2()
print(x)

>

9    # 
100  # 这里能证明nonlocal修改的只是上一层的

函数内部调用函数

```python

-- coding: utf-8 --

@Atthor: fei

def fn1(): print(‘hello Python’)

def fn2(): print(‘hello Python’) fn1() print(‘hello Linux’)

fn2()

>>>
```python
hello Python
hello Python
hello Linux

练习题:
问题1:定义函数,求n到m之和

# -*- coding: utf-8 -*-
# @Author: afei
def add(n,m):
    sum = 0
    for i in range(n,m+1):
        sum += i
    return sum

resutl1 = add(1,9)
print(resutl1)

问题2:求一个n的阶乘

# -*- coding: utf-8 -*-
# @Author: afei
def fac(n):
    x = 1
    for i in range(1,n+1):
        x *= i
    return x
print(fac(4))

问题3: 计算m阶乘的和
m=4 ==> 1! + 2! + 3! + 4!

# -*- coding: utf-8 -*-
# @Author: afei
def fac(n):
    x = 1
    for i in range(1,n+1):
        x *= i
    return x
# print(fac(4))
def fac2(m):
    x = 0
    for i in range(1,m+1):
        x += fac(i)
    return x
print(fac2(4))

递归函数

在函数内部,可以调用其他函数。如果一个函数在内部调用自身本身,这个函数就是递归函数。

递归函数特性:

必须有一个明确的结束条件;
每次进入更深一层递归时,问题规模相比上次递归都应有所减少
相邻两次重复之间有紧密的联系,前一次要为后一次做准备(通常前一次的输出就作为后一次的输入)。
递归效率不高,递归层次过多会导致栈溢出(在计算机中,函数调用是通过栈(stack)这种数据结构实现的,每当进入一个函数调用,栈就会加一层栈帧,每当函数返回,栈就会减一层栈帧。由于栈的大小不是无限的,所以,递归调用的次数过多,会导致栈溢出)

问题1:求1到n的和

# -*- coding: utf-8 -*-
# @Author: afei
num = 0
def outer():
    global num
    num += 1
    print(num)
    if num < 5:
        outer()

outer()

>

1
2
3
4
5

问题2:求n的阶乘 ```python

-- coding: utf-8 --

@Author: afei

def fun1(n): if n == 0: return 1 return n * fun1(n-1) print(fun1(3))

>>>
```python
6