函数

---恢复内容开始---

一、为什么要有函数,什么是函数?

1、若是没有函数的话,容易出现代码的组织机构不清晰,可读性差,

2、遇到重复的功能只能重复编写实现代码,代码冗余。

3、功能需要扩展时,需要找出所有实现该功能的地方修改,无法统一管理,且维护难度极大

二、什么是函数呢?

简单的说函数就是具备某一功能的工具

#要想用函数就必须要先定义, 在使用

三:函数的分类

函数有内置函数与自定义函数

内置函数即python为我们定义好了的函数。无需我们在重新定义,可以直接拿来用。如len(),sum(),max()

自定义函数即我们事先根据自己的需求,定制好我们自己的函数来实现某种功能,在以后遇到需要应用的时候,可以直接拿来用。

四:定义函数

1、函数语法:

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

'''''注释'''''

函数体

return 返回值

其中函数名要能反应其意义

def auth(user:str,password:str)->int:
    ''''''
    # auth function
    # :param user: 用户名
    # :param password: 密码
    # :return: 认证结果
    # '''                  ====>>为注释体
    if user=='egon'and password=='123':    #代码块函数体
        return 1
user=input('用户名》》:').strip()
pwd=input('密码》》:').strip()
res=auth(user,pwd)
print(res)

2、函数使用的原则:先定义,在调用

函数即‘变量’,变量加引号,变量必须先定义后引用。未定义而直接引用函数,就相当于在引用一个不存在的变量名。

def foo():
    print('from foo')
    bar()
foo()     #会报错, 因为函数体里面的bar()没有被定义。

def bar():
    print('from bar')
def foo():
    print('from foo')
    bar()
foo()   #则不会报错,foo()已被定义('from foo') bar()已被定义(’from bar')。 所以结果  为 from foo, from bar

结论:函数的使用,必须遵循原则:先定义,后调用

#我们在使用函数时,一定要明确的区分 定义阶段和调用阶段

定义阶段

定义阶段:

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

调用阶段
    
foo()

3、函数在定义阶段都干了哪些事?

只检测语法,不执行代码

所以语法错误的话在函数定义阶段就会检测出来,而代码的逻辑错误只有在执行时才会知道

4、定义函数的三种形式

4.1)无参数:应用场景仅仅只是执行一些操作,比如与用户交互, 打印

4.2)有参数:需要根据外部传进来的参数,才能执行相应的逻辑,比如统计长度,求最大值,最小值

4.3)空函数:设计代码结构

def tell_tag(tag,n): #有参数
    print(tag*n)

def tell_msg():#无参数
    print('hello world')

#调用阶段
tell_tag('*',12)
tell_msg()
tell_tag('*',12)


#打印结果
************
hello world
************

结论:1.定义时无参,意味着调用时也无需传入参数

          2、定义时有参,意味着调用时则必须传入参数

五调用函数

5.1)调用函数:

函数的调用;函数名加括号

1:先找到名字

2 根据名字调用代码

5.2)函数的返回值

无return----》给出None

return  1个值--》返回1个值

return 逗号分隔多个值,返回多个值,会返回一个元组给调用者  
什么时候该有返回值?
调用函数,经过一系列操作,最后要拿到一个明确的结果,则必须要有返回值
通常有参数函数需要有返回值,输入参数,经过计算,得到一个最终的结果

什么时候不需要有返回值?
调用函数,仅仅只是执行一系列操作,最后不需要得到什么结果, 则不需要有返回值
通常无参函数不需要有返回值

注意点:
1、return返回值得值,没有类型限制
2、return是函数结束的标志,函数内可以写多个return,但执行一次,函数就立刻结束,
并把return后的值作为本次调用的返回值

5.3函数的参数

函数的参数分为形参与实参

形参(即形式参数):指的是在定义函数时,括号内定义的参数,形参其实就是变了名。

实参(即实际就是实际参数),指的是在调用函数时,括号内传入的值,实参起始就是变量的值。

def func(x,y):#x=10,y,=11,x,y 是形参
    print(x)
    print(y)

func(10,11)  #10,11 是实参

注意:

实参值(变量的值)与形参(变量名)的绑定关系,只在函数调用时菜户生效绑定,在函数调用结束后就立刻接触绑定。

参数的具体应用

1:位置参数:位置即顺序,位置参数指的是按照从左到右的顺序依次定义的参数。

  分为两种:位置形参, 位置实参

位置形参:在定义函数时,按照位置定义的形参,成为位置形参

注意:位置形参的特性:在调用函数时必须为其传值, 而且多一个不行,少一个也不行。def foo(x,y,z): #x,y,z,就是位置形参

    print(x,y,z)

foo(1,2)           ##(TypeError: foo() missing 1 required positional argument: 'z'  则会报错, 形参有3个, 给实参给值时 则少一个  z没有给值。
foo(1,2,3,4)      ###TypeError: foo() takes 3 positional arguments but 4 were given  报错,值4 是多给的一个。没有相对应的形参。

位置实参:在调用函数时按照位置定义的实参,成为位置实参。

注意:位置实参必须与形参一一对应

def foo(x,y,z):
    print(x,y,z)

foo(1,2,3)    #位置实参必须与位置形参一一对应,即x=1,y=2,z=3

2:关键字参数:在调用函数时,按照key=value 的形式定义的实参(注意是实参),称之为关键字参数

注意:

1、相当于指名道姓的为形参传值,意味着即便是不按照顺序定义, 仍然能为指定的参数传值。

 2、在调用函数时,位置实参与关键字实参可以混合使用。但是必须遵循以下三点:

(1)必须遵循形参的规则

(2)不能为同一个形参重复传值

(3)位置实参必须放到关键字实参的前面。

def foo(x,y,z)
    print(x,y,z)

foo(1,2,3)                   #为位置实参,x=1,y=2,z=3
foo(y=2,z=3,x=1)             #关键字参数,没有顺序对应, 也能为形参传值。
foo(1,z=3,y=2)               # 这个位置参数 1 必须放到z=3,y=2的前面, 否则会报错。位置实参必须放到关键字实参的前面
foo(1,x=1,y=2,z=3)          # 这里也会报错, 1已经赋值给了x,  后面的x=1则重复给x 赋值,是不允许的。不能为同一个形参重复传值
foo(x=2,y=3)                 # 报错,z没有赋值。(形参的规则)

3.默认参数:在定义阶段,已经为某个形参赋值,那么该形参就称为默认参数

注意:

3.1:定义阶段已经有值,意味着调用阶段可以不传值

def register(name,age,sex='male'):  #sex=male 为默认参数,在实参传值时 不传值,则默认为已经赋予的值.
    print(name,age,sex)

register(
'egon',18,) #egon 18 male 不输入默认male register('alex',73,'female') #alex 73 female 输入则不默认 register('wxx',84,) #wxx 84 male 不输入为默认male

3.2:位置形参必须在默认参数的前面

def func(y=1,x):    #会报错,y=1,不可以放在位置形参的的前面。应该为(x,y=1))
    print(x,y)

3.3:默认参数的值只在定义阶段赋值一次,也就是说默认参数的值在定义阶段就固定死了,不能更改

m=10
def  foo(x,y=m)
    print(x,y)

m='aaaaa'

foo(1)  #结果只能是 (1,10)而不会是(1,'aaaaa').因为在定义阶段只赋值一次 y=m=10,后面m=aaaaa 则不会再赋值给y.

3.4.记住:默认参数的值应该设置为不可变类型(字符串,元组,集合)

def register(name,hobby,l=[]):
    l.append(hobby)
    print(name,l)

register('alex','play')  #alex ['play']
register('wxx','read')   #wxx ['play', 'read']  我们实际想要得到的结果wxx [ 'read']
register('egon','music') #egon ['play', 'read', 'music']    我们实际想要得到的结果egon ['music']


为什么会出现这样的结果呢, 就是 因为列表是可变的, 在赋值给alex之后,l[]里已经有了['play'],运行到'wxx'时,则自然会在music的前面加上play.同理 egon.
可以按照如下更改:

def register(name,hobby,l=None):
if l is None:
l=[]
l.append(hobby)
print(name,l)

register('alex','play') #alex ['play']
register('wxx','music') #wxx ['music']
register('egon','read') #egon ['read']

这样就l赋值给了None,就不会出现这样的情况了

应用:

对于经常需要变化的值,需要将对应的形参定义成位置形参,

对于大多数情况下值都一样的 情况, 需要将对应的形参定义成默认形参。

4.可变长度参数

可变长度指的是参数的个数可以不固定,实参有按位置定义的实参和按关键字定义的实参,

所以可变长的实参指的就是按照这两种形式定义的实参个数可以不固定,但是实参终究是要传值给形参,所以形参必须要有两种对应的解决方案来分别处理以上两种形式可变长度的实参(简单的说就是因为实参有位置实参和关键字实参, 它的长度可以不固定, 但是又必须要传值给形参, 所以形参就要有对应解决这两种形式可变长度的实参的方案。)这两种方案为  一个 “ *  ”,     两个“ ** ”。

#形参里包含  “   *   ”

* 会将溢出(多出)的位置实参全部接收,然后保存成 元组的形式赋值给args

def foo(x,y,z,*args):
    print(x,y,z)
    print(args)

foo(1,2,3,4,5,6,7,8,)


#:
1 2 3      1赋值给x,2赋值给y,3赋值给z.
(4, 5, 6, 7, 8)   保存成元组形式 赋值给args

#形参里包含 **

 ** 会将溢出(多出)的关键字实参全部接收, 然后保存成字典的形式赋值给kwargs

def foo(x,y,z,**kwargs):
    print(x,y,z)
    print(kwargs)

foo(x=1,y=2,z=3,a=1,b=2,c=3)


#
1 2 3 赋值给了x,y,z 
{'a': 1, 'b': 2, 'c': 3}  这个则保存成字典形式赋值给kwargs

若实参里包含 *与**

一旦碰到实参里包含* 就把该实参的值打散。

def foo(x,y,z,*args):
    print(x,y,z)
    print(args)

foo(1,2,3,*[4,5,6,7,8])

#
1 2 3
(4, 5, 6, 7, 8)        先把实参里*[4,5,6,7,8]打散, 变成4,5,6,7,8,然后就是foo(1,2,3,4,5,6,7,8)的函数,所以结果就 1 2 3赋值给x y z,
而 4 5 6 7 8赋值给args

同理:一旦碰到实参里包含**  也是把该实参的值打散。

def foo(x,y,z,**kwargs):
    print(x,y,z)
    print(kwargs)

foo(1,2,3,**{'a':1,'b':2})

#
1 2 3
{'a': 1, 'b': 2} 也是先把**{“a”:1,"b":2}打散 "a":1,"b":2. 所以即为调用函数为foo(1,2,3, "a":1,"b":2)。 1 2 3赋值给x y z. "a":1,"b":2赋值给kwargs.

组合使用例:

def index(name,age,gender):
    print('welcome %s %s %s' %(name,age,gender))

def wrapper(*args,**kwargs):
    # print(args)
    # print(kwargs)
    index(*args,**kwargs)

wrapper(name='egon',age=18,gender='male')



#最终结果  welcome egon 18 male

调用wrapper (*args,**kwargs),则会执行index(*args,**kwargs),里面的形参是 一样的,则可以说是调用 index(*args,**kwargs)
而index(*args,**kwargs)的形参在第一行为 index(name,age,gender):
所以最终调用则可理解为wrapper(name,age,gender):给出的实参。
即 wrapper(name
='egon',age=18,gender='male')。 即最终结果最终结果 welcome egon 18 male

 5.4 函数的嵌套

5.4.1)函数的嵌套调用:在函数内又调用了其他函数

def max2(x,y):
    if x>y:
        return x
    else:
        return y

def max3(x,y,z):
    res1=max2(x,y)
    res2=max2(res1,z)
    return res2
print(max3(11,199,2))   #打印结果 199    先比较11和199的大小, 然后又在比较大小后得出的结果值与2比较, 就是函数里面套函数

 5.4.2)函数的嵌套的定义:在函数内又定义了其他函数

def func1():
    print('from func1')
    def func2(): #func2=内存地址
        print('from func2')

    print(func2) #<function func1.<locals>.func2 at 0x0000024907A098C8>
 

func1()
#
结果如下:
from func1
<function func1.<locals>.func2 at 0x0000026BDEB85048>
func1里套func2
def f1():
    print('f1')
    def f2():
        print('f2')
        def f3():
            print('f3')
        f3()
    f2()
f1()

 5.5 名称空间

名称空间是指存放名字与值绑定关系的地方

名称空间分为三类

1:内置名称空间:存放python解释器自带的名字,在解释器启动时就生效,解释器关闭则失效

2:全局名称空间:文件级别的名字(或者理解为不是内置名称空间也非局部名称空间之外的即为全局名称空间), 在执行文件的时候生效, 在文件结束或者在文件执行期间被删除则失效

x=1
def f1():
    def f2():
        print(x)
    f2()
f1()   #打印结果为  1.  这里的x=1即存为全局名称空间

3:局部名称空间:存放函数内定义的名字(函数的参数以及函数内的名字都存放于局部名称空间)

#在函数调用时临时生效, 函数结束则失效

# def func(x):
#     y=2  #y=2 则存放于局部名称空间
#
# func(1)        

加载顺序:内置名称空间==》全局名称空间==》局部名称空间

查找名字顺序:内部名称空间===》全局名称空间==》内置名称空间

len ='global'
def f1():
    len =1
    def f2():
        len =2
        print(len)
    f2()
f1()     

#打印结果为2    在执行中先找自己所在的域内有没有 ,若没有的话在向外一层扩展寻找, 这一层已经找到 ,所以外一层的len=1 len='global'就不会被用到


len ='global'
def f1():
    len =1
    def f2():
        print(len)
    f2()
f1()  
#打印结果为1,  在本域内没有找到又向外寻找一层,然后找到了len=1


len='global'
def f1():
    def f2():
        print(len)
    f2()
f1()

#打印结果  global   在全局名称空间找到



def f1():
    def f2():
        print(len)
    f2()
f1()

#

打印结果  <built-in function len>  这个 是在局域名称空间和全局名称空间都没有找到,  最后在内置名称空间找到了

5.6  作用域

全局作用域:包含的是内置名称空间与全局名称空间的名字。

特点:

         1:在任何位置都能够访问的到

          2:该范围内的名字会伴随程序整个生命周期。

局部作用域:包含的是局部名称空间的名字

特点:

      1:只能在函数内使用

      2: 调用函数时生效,调用结束失效。

 

---恢复内容结束---

一、为什么要有函数,什么是函数?

1、若是没有函数的话,容易出现代码的组织机构不清晰,可读性差,

2、遇到重复的功能只能重复编写实现代码,代码冗余。

3、功能需要扩展时,需要找出所有实现该功能的地方修改,无法统一管理,且维护难度极大

二、什么是函数呢?

简单的说函数就是具备某一功能的工具

#要想用函数就必须要先定义, 在使用

三:函数的分类

函数有内置函数与自定义函数

内置函数即python为我们定义好了的函数。无需我们在重新定义,可以直接拿来用。如len(),sum(),max()

自定义函数即我们事先根据自己的需求,定制好我们自己的函数来实现某种功能,在以后遇到需要应用的时候,可以直接拿来用。

四:定义函数

1、函数语法:

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

'''''注释'''''

函数体

return 返回值

其中函数名要能反应其意义

def auth(user:str,password:str)->int:
    ''''''
    # auth function
    # :param user: 用户名
    # :param password: 密码
    # :return: 认证结果
    # '''                  ====>>为注释体
    if user=='egon'and password=='123':    #代码块函数体
        return 1
user=input('用户名》》:').strip()
pwd=input('密码》》:').strip()
res=auth(user,pwd)
print(res)

2、函数使用的原则:先定义,在调用

函数即‘变量’,变量加引号,变量必须先定义后引用。未定义而直接引用函数,就相当于在引用一个不存在的变量名。

def foo():
    print('from foo')
    bar()
foo()     #会报错, 因为函数体里面的bar()没有被定义。

def bar():
    print('from bar')
def foo():
    print('from foo')
    bar()
foo()   #则不会报错,foo()已被定义('from foo') bar()已被定义(’from bar')。 所以结果  为 from foo, from bar

结论:函数的使用,必须遵循原则:先定义,后调用

#我们在使用函数时,一定要明确的区分 定义阶段和调用阶段

定义阶段

定义阶段:

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

调用阶段
    
foo()

3、函数在定义阶段都干了哪些事?

只检测语法,不执行代码

所以语法错误的话在函数定义阶段就会检测出来,而代码的逻辑错误只有在执行时才会知道

4、定义函数的三种形式

4.1)无参数:应用场景仅仅只是执行一些操作,比如与用户交互, 打印

4.2)有参数:需要根据外部传进来的参数,才能执行相应的逻辑,比如统计长度,求最大值,最小值

4.3)空函数:设计代码结构

def tell_tag(tag,n): #有参数
    print(tag*n)

def tell_msg():#无参数
    print('hello world')

#调用阶段
tell_tag('*',12)
tell_msg()
tell_tag('*',12)


#打印结果
************
hello world
************

结论:1.定义时无参,意味着调用时也无需传入参数

          2、定义时有参,意味着调用时则必须传入参数

五调用函数

5.1)调用函数:

函数的调用;函数名加括号

1:先找到名字

2 根据名字调用代码

5.2)函数的返回值

无return----》给出None

return  1个值--》返回1个值

return 逗号分隔多个值,返回多个值,会返回一个元组给调用者  
什么时候该有返回值?
调用函数,经过一系列操作,最后要拿到一个明确的结果,则必须要有返回值
通常有参数函数需要有返回值,输入参数,经过计算,得到一个最终的结果

什么时候不需要有返回值?
调用函数,仅仅只是执行一系列操作,最后不需要得到什么结果, 则不需要有返回值
通常无参函数不需要有返回值

注意点:
1、return返回值得值,没有类型限制
2、return是函数结束的标志,函数内可以写多个return,但执行一次,函数就立刻结束,
并把return后的值作为本次调用的返回值

5.3函数的参数

函数的参数分为形参与实参

形参(即形式参数):指的是在定义函数时,括号内定义的参数,形参其实就是变了名。

实参(即实际就是实际参数),指的是在调用函数时,括号内传入的值,实参起始就是变量的值。

def func(x,y):#x=10,y,=11,x,y 是形参
    print(x)
    print(y)

func(10,11)  #10,11 是实参

注意:

实参值(变量的值)与形参(变量名)的绑定关系,只在函数调用时菜户生效绑定,在函数调用结束后就立刻接触绑定。

参数的具体应用

1:位置参数:位置即顺序,位置参数指的是按照从左到右的顺序依次定义的参数。

  分为两种:位置形参, 位置实参

位置形参:在定义函数时,按照位置定义的形参,成为位置形参

注意:位置形参的特性:在调用函数时必须为其传值, 而且多一个不行,少一个也不行。def foo(x,y,z): #x,y,z,就是位置形参

    print(x,y,z)

foo(1,2)           ##(TypeError: foo() missing 1 required positional argument: 'z'  则会报错, 形参有3个, 给实参给值时 则少一个  z没有给值。
foo(1,2,3,4)      ###TypeError: foo() takes 3 positional arguments but 4 were given  报错,值4 是多给的一个。没有相对应的形参。

位置实参:在调用函数时按照位置定义的实参,成为位置实参。

注意:位置实参必须与形参一一对应

def foo(x,y,z):
    print(x,y,z)

foo(1,2,3)    #位置实参必须与位置形参一一对应,即x=1,y=2,z=3

2:关键字参数:在调用函数时,按照key=value 的形式定义的实参(注意是实参),称之为关键字参数

注意:

1、相当于指名道姓的为形参传值,意味着即便是不按照顺序定义, 仍然能为指定的参数传值。

 2、在调用函数时,位置实参与关键字实参可以混合使用。但是必须遵循以下三点:

(1)必须遵循形参的规则

(2)不能为同一个形参重复传值

(3)位置实参必须放到关键字实参的前面。

def foo(x,y,z)
    print(x,y,z)

foo(1,2,3)                   #为位置实参,x=1,y=2,z=3
foo(y=2,z=3,x=1)             #关键字参数,没有顺序对应, 也能为形参传值。
foo(1,z=3,y=2)               # 这个位置参数 1 必须放到z=3,y=2的前面, 否则会报错。位置实参必须放到关键字实参的前面
foo(1,x=1,y=2,z=3)          # 这里也会报错, 1已经赋值给了x,  后面的x=1则重复给x 赋值,是不允许的。不能为同一个形参重复传值
foo(x=2,y=3)                 # 报错,z没有赋值。(形参的规则)

3.默认参数:在定义阶段,已经为某个形参赋值,那么该形参就称为默认参数

注意:

3.1:定义阶段已经有值,意味着调用阶段可以不传值

def register(name,age,sex='male'):  #sex=male 为默认参数,在实参传值时 不传值,则默认为已经赋予的值.
    print(name,age,sex)

register(
'egon',18,) #egon 18 male 不输入默认male register('alex',73,'female') #alex 73 female 输入则不默认 register('wxx',84,) #wxx 84 male 不输入为默认male

3.2:位置形参必须在默认参数的前面

def func(y=1,x):    #会报错,y=1,不可以放在位置形参的的前面。应该为(x,y=1))
    print(x,y)

3.3:默认参数的值只在定义阶段赋值一次,也就是说默认参数的值在定义阶段就固定死了,不能更改

m=10
def  foo(x,y=m)
    print(x,y)

m='aaaaa'

foo(1)  #结果只能是 (1,10)而不会是(1,'aaaaa').因为在定义阶段只赋值一次 y=m=10,后面m=aaaaa 则不会再赋值给y.

3.4.记住:默认参数的值应该设置为不可变类型(字符串,元组,集合)

def register(name,hobby,l=[]):
    l.append(hobby)
    print(name,l)

register('alex','play')  #alex ['play']
register('wxx','read')   #wxx ['play', 'read']  我们实际想要得到的结果wxx [ 'read']
register('egon','music') #egon ['play', 'read', 'music']    我们实际想要得到的结果egon ['music']


为什么会出现这样的结果呢, 就是 因为列表是可变的, 在赋值给alex之后,l[]里已经有了['play'],运行到'wxx'时,则自然会在music的前面加上play.同理 egon.
可以按照如下更改:

def register(name,hobby,l=None):
if l is None:
l=[]
l.append(hobby)
print(name,l)

register('alex','play') #alex ['play']
register('wxx','music') #wxx ['music']
register('egon','read') #egon ['read']

这样就l赋值给了None,就不会出现这样的情况了

应用:

对于经常需要变化的值,需要将对应的形参定义成位置形参,

对于大多数情况下值都一样的 情况, 需要将对应的形参定义成默认形参。

4.可变长度参数

可变长度指的是参数的个数可以不固定,实参有按位置定义的实参和按关键字定义的实参,

所以可变长的实参指的就是按照这两种形式定义的实参个数可以不固定,但是实参终究是要传值给形参,所以形参必须要有两种对应的解决方案来分别处理以上两种形式可变长度的实参(简单的说就是因为实参有位置实参和关键字实参, 它的长度可以不固定, 但是又必须要传值给形参, 所以形参就要有对应解决这两种形式可变长度的实参的方案。)这两种方案为  一个 “ *  ”,     两个“ ** ”。

#形参里包含  “   *   ”

* 会将溢出(多出)的位置实参全部接收,然后保存成 元组的形式赋值给args

def foo(x,y,z,*args):
    print(x,y,z)
    print(args)

foo(1,2,3,4,5,6,7,8,)


#:
1 2 3      1赋值给x,2赋值给y,3赋值给z.
(4, 5, 6, 7, 8)   保存成元组形式 赋值给args

#形参里包含 **

 ** 会将溢出(多出)的关键字实参全部接收, 然后保存成字典的形式赋值给kwargs

def foo(x,y,z,**kwargs):
    print(x,y,z)
    print(kwargs)

foo(x=1,y=2,z=3,a=1,b=2,c=3)


#
1 2 3 赋值给了x,y,z 
{'a': 1, 'b': 2, 'c': 3}  这个则保存成字典形式赋值给kwargs

若实参里包含 *与**

一旦碰到实参里包含* 就把该实参的值打散。

def foo(x,y,z,*args):
    print(x,y,z)
    print(args)

foo(1,2,3,*[4,5,6,7,8])

#
1 2 3
(4, 5, 6, 7, 8)        先把实参里*[4,5,6,7,8]打散, 变成4,5,6,7,8,然后就是foo(1,2,3,4,5,6,7,8)的函数,所以结果就 1 2 3赋值给x y z,
而 4 5 6 7 8赋值给args

同理:一旦碰到实参里包含**  也是把该实参的值打散。

def foo(x,y,z,**kwargs):
    print(x,y,z)
    print(kwargs)

foo(1,2,3,**{'a':1,'b':2})

#
1 2 3
{'a': 1, 'b': 2} 也是先把**{“a”:1,"b":2}打散 "a":1,"b":2. 所以即为调用函数为foo(1,2,3, "a":1,"b":2)。 1 2 3赋值给x y z. "a":1,"b":2赋值给kwargs.

组合使用例:

def index(name,age,gender):
    print('welcome %s %s %s' %(name,age,gender))

def wrapper(*args,**kwargs):
    # print(args)
    # print(kwargs)
    index(*args,**kwargs)

wrapper(name='egon',age=18,gender='male')



#最终结果  welcome egon 18 male

调用wrapper (*args,**kwargs),则会执行index(*args,**kwargs),里面的形参是 一样的,则可以说是调用 index(*args,**kwargs)
而index(*args,**kwargs)的形参在第一行为 index(name,age,gender):
所以最终调用则可理解为wrapper(name,age,gender):给出的实参。
即 wrapper(name
='egon',age=18,gender='male')。 即最终结果最终结果 welcome egon 18 male

 5.4 函数的嵌套

5.4.1)函数的嵌套调用:在函数内又调用了其他函数

def max2(x,y):
    if x>y:
        return x
    else:
        return y

def max3(x,y,z):
    res1=max2(x,y)
    res2=max2(res1,z)
    return res2
print(max3(11,199,2))   #打印结果 199    先比较11和199的大小, 然后又在比较大小后得出的结果值与2比较, 就是函数里面套函数

 5.4.2)函数的嵌套的定义:在函数内又定义了其他函数

def func1():
    print('from func1')
    def func2(): #func2=内存地址
        print('from func2')

    print(func2) #<function func1.<locals>.func2 at 0x0000024907A098C8>
 

func1()
#
结果如下:
from func1
<function func1.<locals>.func2 at 0x0000026BDEB85048>
func1里套func2
def f1():
    print('f1')
    def f2():
        print('f2')
        def f3():
            print('f3')
        f3()
    f2()
f1()

 5.5 名称空间与作用域:

一、名称空间:存放名字与值绑定关系的地方

二:名称空间分为三类

1 内置名称空间:存放Python解释器自带的名字,在解释器启动时就生效,解释器关闭则失效

2、全局名称空间:文件级别的名字,(可以理解为 不是内置名称空间和局部名称空间 就是全局名称空间)。在执行文件的时候生效,在文件结束或者在文件执行期间被删除则失效

3、局部名称空间:存放函数内定义的名字(函数的参数以及函数内的名字都存放与局部名称空间),
在函数调用时临时生效,函数结束则失效

重点:
#加载顺序:内置名称空间-》全局名称空间-》局部名称空间
#查找名字:局部名称空间-》全局名称空间-》内置名称空间

len ='global'
def f1():
    len =1
    def f2():
        len =2
        print(len)  
    f2()
f1()
#打印结果为  2   在当前局部名称空间内就已找到len的值为2,所以后面的len=1 和len=global 都可以不用理会

len ='global'
def f1():
    len =1
    def f2():
        print(len)
    f2()
f1()  打印结果为  1   在当前局部名称空间内没有找到len 的值 向外层寻找时  找到了len=1.  而后面的len=global    不用理会


len='global'
def f1():
    def f2():
        print(len)
    f2()
f1()

#打印结果为len='global'   在全局作用域内找到了



def f1():
    def f2():
        print(len)
    f2()
f1()

#打印结果<built-in function len>    
在内置名称空间找到len  ,python解释器自带。
三:作用域
全局作用域:包含的是内置名称空间与全局名称空间的名字,特点
1 在任何位置都能够访问的到
2 该范围内的名字会伴随程序整个生命周期

局部作用域:包含的是局部名称空间的名字
特点:
1、只能在函数内使用
2、调用函数时生效,调用结束失效
 
原文地址:https://www.cnblogs.com/lx3822/p/8652783.html