函数

函数的基本使用

什么是函数

函数就相当于具备某一功能的工具
函数的使用必须遵循一个原则:先定义 后调用

为何要用函数

1、组织结构不清晰,可读性差
2、代码冗余
3、可维护性、扩展性差

如何用函数

    先定义
        三种定义方式
    后调用
        三种调用方式
    返回值
        三种返回值的形式

定义的语法

def 函数名(参数1,参数2,...):
    """文档描述"""
    函数体
    return 值

形式一:无参函数

def func():
    print('哈哈哈')

定义函数发生的事情:
1、申请内存空间保存函数体代码
2、将上述内存地址绑定函数名
3、定义函数不会执行函数体代码,但是会检测函数体语法
调用函数发生的事情:
1、通过函数名找到函数的内存地址
2、然后加口号就是在触发函数体代码的执行

示范1:

def bar(): # bar=函数的内存地址
    print('from bar')
def foo():
    # print(bar)
    bar()
    print('from foo')
foo()

示范2:

def foo():
    bar()
    print('from foo')
def bar():  # bar=函数的内存地址
    print('from bar')
foo()

示范3:

def foo():
    bar()
    print('from foo')
foo()
def bar():  # bar=函数的内存地址
    print('from bar')

形式二:有参函数

def func(x,y): # x=1  y=2
    print(x,y)
func(1,2)

形式三:空函数,函数体代码为pass

def func(x, y):
    pass
```
###三种定义方式各用在何处:
无参函数的应用场景
```
def interactive():
    name=input('username>>: ')
    age=input('age>>: ')
    gender=input('gender>>: ')
    msg='名字:{} 年龄:{} 性别'.format(name,age,gender)
        print(msg)
interactive()
```
有参函数的应用场景
```
def add(x,y): # 参数-》原材料
    # x=20
    # y=30
    res=x + y
    # print(res)
    return res # 返回值-》产品

add(10,2)
res=add(20,30)
print(res)
```
空函数的应用场景
```
def auth_user():
    """user authentication function"""
    pass

def download_file():
    """download file function"""
    pass

def upload_file():
    """upload file function"""
    pass

def ls():
    """list contents function"""
    pass

def cd():
    """change directory"""
    pass
```
##调用函数
语句的形式:只加括号调用函数
```
interactive()
```
表达式形式
```
def add(x,y): # 参数-》原材料
    res=x + y
    return res # 返回值-》产品
```
赋值表达式
```
res=add(1,2)
print(res)
```
数学表达式
```
res=add(1,2)*10
print(res)
```
函数调用可以当做参数
```
res=add(add(1,2),10)
print(res)
```
##函数返回值
return是函数结束的标志,即函数体代码一旦运行到return会立刻
终止函数的运行,并且会将return后的值当做本次运行的结果返回:
1、返回None:函数体内没有return
```
            return
            return None
```
2、返回一个值:return 值
```
def func():
    return 10
res=func()
print(res)
```
3、返回多个值:用逗号分隔开多个值,会被return返回成元组
```
def func():
    return 10, 'aa', [1, 2]
res = func()
print(res, type(res))
```
#形参与实参介绍
形参:在定义函数阶段定义的参数称之为形式参数,简称形参,相当于变量名
```
def func(x, y): 
    print(x, y)
```
实参:在调用函数阶段传入的值称之为实际参数,简称实参,相当于变量值
```
def func(x, y): 
    print(x, y)
func(1,2)
```
形参与实参的关系:
1、在调用阶段,实参(变量值)会绑定给形参(变量名)
2、这种绑定关系只能在函数体内使用
3、实参与形参的绑定关系在函数调用时生效,函数调用结束后解除绑定关系
####实参是传入的值,但值可以是以下形式
形式一:
```
func(1,2)
```
形式二:
```
a=1
b=2
func(a,b)
```
形式三:
```
func(int('1'),2)
func(func1(1,2,),func2(2,3),333)
```
##形参与实参的具体使用
###位置参数:按照从左到右的顺序依次定义的参数称之为位置参数
####位置形参:在函数定义阶段,按照从左到右的顺序直接定义的"变量名"
特点:必须被传值,多一个不行少一个也不行
```
def func(x,y):
    print(x,y)
func(1,2,3)
func(1,)
```
####位置实参:在函数调用阶段, 按照从左到有的顺序依次传入的值
特点:按照顺序与形参一一对应
```
def func(x,y):
    print(x,y)
func(1,2)
```
##关键字参数
###关键字实参:在函数调用阶段,按照key=value的形式传入的值
特点:指名道姓给某个形参传值,可以完全不参照顺序
```
def func(x,y):
    print(x,y)
func(y=2,x=1)
func(1,2)
```
混合使用,强调
1、位置实参必须放在关键字实参前
```
def func(x,y):
    print(x,y)
func(1,y=2)
```
2、不能能为同一个形参重复传值
```
func(1,y=2,x=3)
```
###默认参数
####默认形参:在定义函数阶段,就已经被赋值的形参,称之为默认参数
特点:在定义阶段就已经被赋值,意味着在调用阶段可以不用为其赋值
```
def func(x,y=3):
    print(x,y)
 func(x=1)
func(x=1,y=44444)
def register(name,age,gender='男'):
    print(name,age,gender)
register('三炮',18)
register('二炮',19)
register('大炮',19)
register('没炮',19,'女')
```
####位置形参与默认形参混用,强调:
1、位置形参必须在默认形参的左边
```
def func(y=2,x):
    pass
```
2、默认参数的值是在函数定义阶段被赋值的,准确地说被赋予的是值的内存地址
示范1:
```
m=2
def func(x,y=m): # y=>2的内存地址
    print(x,y
m=3333333333333333333
func(1)
```
示范2:
```
m = [111111, ]

def func(x, y=m): # y=>[111111, ]的内存地址
    print(x, y)
m.append(3333333)
func(1)
```
3、虽然默认值可以被指定为任意数据类型,但是不推荐使用可变类型
函数最理想的状态:函数的调用只跟函数本身有关系,不外界代码的影响
```m = [111111, ]
def func(x, y=m):
    print(x, y)
m.append(3333333)
m.append(444444)
m.append(5555)
func(1)
func(2)
func(3)

def func(x,y,z,l=None):
    if l is None:
        l=[]
    l.append(x)
    l.append(y)
    l.append(z)
    print(l)
func(1,2,3)
func(4,5,6)
new_l=[111,222]
func(1,2,3,new_l)
```
###可变长度的参数(*与**的用法)
可变长度指的是在调用函数时,传入的值(实参)的个数不固定
而实参是用来为形参赋值的,所以对应着,针对溢出的实参必须有对应的形参来接收
####可变长度的位置参数
*形参名:用来接收溢出的位置实参,溢出的位置实参会被*保存成元组的格式然后赋值紧跟其后的形参名
*后跟的可以是任意名字,但是约定俗成应该是args
```
def func(x,y,*z): # z =(3,4,5,6)
    print(x,y,z)
func(1,2,3,4,5,6)
def my_sum(*args):
    res=0
    for item in args:
        res+=item
    return res
res=my_sum(1,2,3,4,)
print(res)
```
 *可以用在实参中,实参中带*,先*后的值打散成位置实参
```
def func(x,y,z):
    print(x,y,z)
 func(*[11,22,33]) # func(11,22,33)
 func(*[11,22]) # func(11,22)
l=[11,22,33]
func(*l)
```
形参与实参中都带*
```
def func(x,y,*args): # args=(3,4,5,6)
    print(x,y,args)
func(1,2,[3,4,5,6])
func(1,2,*[3,4,5,6]) # func(1,2,3,4,5,6)
func(*'hello') # func('h','e','l','l','o')
```
###可变长度的关键字参数
**形参名:用来接收溢出的关键字实参,**会将溢出的关键字实参保存成字典格式,然后赋值给紧跟其后的形参名
**后跟的可以是任意名字,但是约定俗成应该是kwargs
```
def func(x,y,**kwargs):
    print(x,y,kwargs)
func(1,y=2,a=1,b=2,c=3)
```
**可以用在实参中(**后跟的只能是字典),实参中带**,先**后的值打散成关键字实参
```
def func(x,y,z):
    print(x,y,z)

func(*{'x':1,'y':2,'z':3}) # func('x','y','z')
func(**{'x':1,'y':2,'z':3}) # func(x=1,y=2,z=3)
```
形参与实参中都带**
```
def func(x,y,**kwargs):
    print(x,y,kwargs)

func(y=222,x=111,a=333,b=444)
func(**{'y':222,'x':111,'a':333,'b':4444})
```
混用*与**:*args必须在**kwargs之前
```
def func(x,*args,**kwargs):
    print(args)
    print(kwargs)

func(1,2,3,4,5,6,7,8,x=1,y=2,z=3)
```
```
def index(x,y,z):
    print('index=>>> ',x,y,z)

def wrapper(*args,**kwargs): #args=(1,) kwargs={'z':3,'y':2}
    index(*args,**kwargs)
    # index(*(1,),**{'z':3,'y':2})
    # index(1,z=3,y=2)

wrapper(1,z=3,y=2) # 为wrapper传递的参数是给index用的
```
#空间域与作用域
##名称空间namespacs:存放名字的地方,是对栈区的划分
有了名称空间之后,就可以在栈区中存放相同的名字,详细的,名称空间
###分为三种
####内置名称空间
存放的名字:存放的python解释器内置的名字
```
>>> print

>>> input

```
存活周期:python解释器启动则产生,python解释器关闭则销毁
####全局名称空间
存放的名字:只要不是函数内定义、也不是内置的,剩下的都是全局名称空间的名字
存活周期:python文件执行则产生,python文件运行完毕后销毁
```
import os
x=10
if 13 > 3:
    y=20
    if 3 == 3:
        z=30
# func=函数的内存地址
def func():
    a=111
    b=222
class Foo:
    pass

```
####局部名称空间
存放的名字:在调用函数时,运行函数体代码过程中产生的函数内的名字
存活周期:在调用函数时存活,函数调用完毕后则销毁
```
def func(a,b):
    pass
func(10,1)
func(11,12)
func(13,14)
func(15,16)
```
####名称空间的加载顺序
内置名称空间>全局名称空间>局部名称空间
####销毁顺序
局部名称空间>全局名空间>内置名称空间
####名字的查找优先级:当前所在的位置向上一层一层查找
内置名称空间
全局名称空间
局部名称空间
如果当前在局部名称空间:
局部名称空间—>全局名称空间->内置名称空间
```
 input=333
def func():
    # input=444
    print(input)
func()
```
如果当前在全局名称空间
全局名称空间->内置名称空间
```
input=333
def func():
    input=444
func()
print(input)
```
示范1:
```
def func():
    print(x)
x=111
func()
```
示范2:名称空间的"嵌套"关系是以函数定义阶段为准,与调用位置无关
```
x=1
def func():
   print(x)
def foo():
    x=222
    func()
foo()
```
示范3:函数嵌套定义
``
input=111
def f1():
    def f2():
        # input=333
        print(input)
    input=222
    f2()
f1()
```
示范4:
```
x=111
def func():
    print(x) #
    x=222
func()
```
##作用域-》作用范围
全局作用域:内置名称空间、全局名称空间
1、全局存活
2、全局有效:被所有函数共享
```
x=111
def foo():
    print(x,id(x))
def bar():
    print(x,id(x))
foo()
bar()
print(x,id(x))
```
###局部作用域: 局部名称空间的名字
1、临时存活
2、局部有效:函数内有效
```
def foo(x):
    def f1():
        def f2():
            print(x)
```
```
LEGB
# builtin
# global
def f1():
    # enclosing
    def f2():
        # enclosing
        def f3():
            # local
            pass
```
##global与nonlocal
示范1:
```
x=111
def func():
    x=222
func()
print(x)
```
示范2:如果再局部想要修改全局的名字对应的值(不可变类型),需要用global
```
x=111
def func():
    global x # 声明x这个名字是全局的名字,不要再造新的名字了
    x=222

func()
print(x)
```
示范3:
```
l=[111,222]
def func():
    l.append(333)
func()
print(l)

nonlocal(了解): 修改函数外层函数包含的名字对应的值(不可变类型)
x=0
def f1():
    x=11
    def f2():
        nonlocal x
        x=22
    f2()
    print('f1内的x:',x)

f1()
```

你可能感兴趣的:(函数)