函数基础

# 1、为什么要用函数?
# 2、函数的定义与调用
# 3、函数的返回值
# 4、函数的参数
# 5、命名空间和作用域
# 6、函数嵌套及作用域链
# 7、函数名的本质
# 8、函数的闭包
导读目录

1、为什么要用函数?

  假如len方法突然不能直接用了,现在有个需求是【计算'hello world'的长度】:

s1 = "hello world"
length = 0
for i in s1:
    length = length+1

print(length)

  现在又新增了一个需求,要计算另外一个字符串的长度,"hello China"。于是,这个时候你的代码就变成了这样:

s1 = "hello world"
length = 0
for i in s1:
    length = length+1
print(length)

s2 = "hello China"
length = 0
for i in s2:
    length = length+1
print(length)

  这会造成:

  首先,之前只要执行len方法就可以直接拿到一个字符串的长度了,现在为了实现相同的功能我们把相同的代码写了好多遍 —— 代码冗余

  其次,之前我们只写两句话读起来也很简单,一看就知道这两句代码是在计算长度,但是刚刚的代码却不那么容易读懂 —— 可读性差

  于是乎,要是能像len一样将这段代码起个名字就好了,需要的时候直接“喊名字”就可以了。

2、函数的定义与调用

  把上面求字符串长度的代码封装起来:

#函数定义
def mylen():
    """计算s1的长度"""
    s1 = "hello world"
    length = 0
    for i in s1:
        length = length+1
    print(length)

#函数调用  
mylen()

  函数名:函数名只能包含字符串、下划线和数字,且不能以数字开头。虽然函数名可以随便起,但我们给函数起名字还是要尽量简短,并能表达函数功能。

3、函数的返回值

  将上面函数定义里的print改成return,在函数调用的时候看需求再print

#函数定义
def mylen():
    """计算s1的长度"""
    s1 = "hello world"
    length = 0
    for i in s1:
        length = length+1
    return length

#函数调用
str_len = mylen()
print('str_len : %s'%str_len)

  3.1 return关键字的作用

  划重点:一旦遇到return,就结束整个函数

  返回值有几种情况:分别是没有返回值、返回一个值、返回多个值。

    3.1.1 没有返回值

# return   <=>   return None   <=>   没有return

def ret_demo():
    print(111)
    return
    print(222)

ret = ret_demo()
print(ret)
------------------------------------------------------------------------------------
def ret_demo():
    print(111)
    return None
    print(222)

ret = ret_demo()
print(ret)
------------------------------------------------------------------------------------
def ret_demo():
    print(111)
    print(222)

ret = ret_demo()
print(ret)

    3.1.2 返回一个值

# 注意:return和返回值之间要有空格,可以返回任意数据类型的值
def ret_demo():
    return 111
    print(222)

ret = ret_demo()
print(ret)    # 111

    3.1.3 返回多个值

# 可以返回任意多个、任意数据类型的值
def ret_demo1():
    '''返回多个值'''
    return 1,2,3,4

def ret_demo2():
    '''返回多个任意类型的值'''
    return 1,['a','b'],3,4

ret1 = ret_demo1()
print(ret1)                # (1, 2, 3, 4)
ret2 = ret_demo2()  # (1, ['a', 'b'], 3, 4)
print(ret2)
def ret_demo2():
    return 1,['a','b'],3,4

#返回多个值,用一个变量接收
ret2 = ret_demo2()
print(ret2)       # (1, ['a', 'b'], 3, 4)

#返回几个值,就用几个变量接收
a,b,c,d = ret_demo2()
print(a,"->",b,"->",c,"->",d)   # 1 -> ['a', 'b'] -> 3 -> 4
#序列解压一
>>> a,b,c,d = (1,2,3,4)
>>> a
1
>>> b
2
>>> c
3
>>> d
4
#序列解压二
>>> a,_,_,d=(1,2,3,4)
>>> a
1
>>> d
4
>>> a,*_=(1,2,3,4)
>>> *_,d=(1,2,3,4)
>>> a
1
>>> d
4
#也适用于字符串、列表、字典、集合
>>> a,b = {'name':'eva','age':18} 
>>> a
'name'
>>> b
'age'
序列解压扩展

4、函数的参数

#函数定义
def mylen(s1):
    """计算s1的长度"""
    length = 0
    for i in s1:
        length = length+1
    return length

#函数调用
str_len = mylen("hello world")
print('str_len : %s'%str_len)

  4.1 实参和形参

  我们调用函数时传递的这个“hello world”被称为实际参数,因为这个是实际的要交给函数的内容,简称实参。

  定义函数时的s1,只是一个变量的名字,被称为形式参数,因为在定义函数的时候它只是一个形式,表示这里有一个参数,简称形参

  4.2 传递多个参数

  参数可以传递多个,多个参数之间用逗号分割。

def mymax(x,y):
    the_max = x if x > y else y
    return the_max

ma = mymax(10,20)
print(ma)

  4.3 位置参数

    4.3.1 站在实参角度:(顺序:位置参数,默认参数,关键字参数a = '')

      对于一个形参只能赋值一次

# ①按位置传参
def mymax(x,y):
    #此时x=10,y=20
    the_max = x if x > y else y
    return the_max

ma = mymax(10,20)
print(ma)
# ② 按照关键字传参
def mymax(x,y):
    #此时x = 20,y = 10
    print(x,y)
    the_max = x if x > y else y
    return the_max

ma = mymax(y = 10,x = 20)
print(ma)
# ③ 位置、关键字形式混着用
def mymax(x,y):
    #此时x = 10,y = 20
    print(x,y)
    the_max = x if x > y else y
    return the_max

ma = mymax(10,y = 20)
print(ma)

    4.3.2 站在形参角度:(顺序:*args,默认参数,**kwargs)

def func(*args,default = 1,**kwargs):      #形参顺序: (*args,默认参数,**kwargs)
    print(args,default,kwargs)
func(1,2,3,default=2,a='aaaa',b='bbbbb')   #实参顺序: (位置参数,默认参数,关键字参数a = '')

运行结果:
(1, 2, 3) 2 {'a': 'aaaa', 'b': 'bbbbb'}   

    位置参数必须传值

def mymax(x,y):
    print(x,y)
    the_max = x if x > y else y
    return the_max

#调用mymax不传递参数
ma = mymax()
print(ma)

#结果
TypeError: mymax() missing 2 required positional arguments: 'x' and 'y'

  4.4 默认参数

# 默认参数的值是一个可变数据类型
# 每一次调用函数的时候,如果不传值,就公用这个数据类型的资源

def stu_info(name,sex = "male"):
    """打印学生信息函数,由于班中大部分学生都是男生,
        所以设置默认参数sex的默认值为'male'
    """
    print(name,sex)

 
stu_info('alex')               # alex male
stu_info('eva','female')   # eva female
# 默认参数的陷阱
def qqxing(k,d = {}):
    d[k] = 'v'
    print(d)

qqxing(1)    #{1: 'v'}
qqxing(2)    #{1: 'v', 2: 'v'}    #新执行完的都添加到默认的dict里边
qqxing(3)    #{1: 'v', 2: 'v', 3: 'v'}

  4.5 动态参数

# *args:按位置传值多余的参数都由args统一接收,保存成一个元组的形式
def mysum(*args):
    the_sum = 0
    for i in args:
        the_sum+=i
    return the_sum

the_sum = mysum(1,2,3,4)
print(the_sum)
# **kwargs
def stu_info(**kwargs):
    print(kwargs)
    print(kwargs['name'],kwargs['sex'])

stu_info(name = 'alex',sex = 'male')

  参数总结

5、命名空间和作用域

  5.1 命名空间

  【依赖倒置原则】:向上依赖
  【向下选择原则】:当我自己有的时候,我就不找我的上级要了;如果我自己没有,就一层一层往上级找(找级数最低的),如果内置的命名空间里也没有,则报错
   多个函数应该拥有多个独立的局部名字空间,不互相共享

命名空间
#
# 内置命名空间 -- python解释器 # python解释器一启动就可以使用的名字存储在内置命名空间中 # 内置的名字在其冻结时期的时候被加载进内存里 ## 全局命名空间 -- 我们写的代码,但不是函数中的代码 #是在程序从上到下被执行的过程中依次加载进内存的 #放置了我们设置的所有变量名和函数名 ## 局部命名空间 -- 函数 #就是函数内部调用的名字 #当调用函数的时候,才会产生这个名称空间,随着函数执行的结束,这个命名空间就又消失了 -------------------------------------------------------------------------------- ----##三种命名空间之间的加载与取值顺序: #加载顺序: #内置命名空间(程序运行前加载)->全局命名空间(程序运行中:从上到下加载)->局部命名空间(程序运行中:调用时才加载) # 取值顺序: # 在局部调用:局部命名空间->全局命名空间->内置命名空间         x = 1         def f(x):         x=3         print(x)         f(2) # 3         print(10) # 10 # 在全局调用:全局命名空间->内置命名空间         x = 10         def f(x):         print(x)         f(2) # 2         print(x) # 10

  5.2 作用域

  作用域就是作用范围,按照生效范围可以分为全局作用域和局部作用域。

  5.2.1 globals()  和 locals()方法

  【全局作用域】: -- globals():永远打印全局的名字

      包含内置名称空间、全局名称空间,在整个文件的任意位置都能被引用、全局有效。

  【局部作用域】:-- locals():取决于位置,在哪儿哪儿就是本地

      局部名称空间,只能在局部范围生效  。

a = 1
b = 2
def func():
    x = 'aaa'
    y = 'bbb'
    print(locals())
    print(globals())  #全局的:永远打印全局的名字
func()

运行结果:
{'y': 'bbb', 'x': 'aaa'}

{'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x00000219469AEF28>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, '__file__': 'F:/Python/Project/All_Stacks/01 基础/day10/02.函数的命名空间.py', '__cached__': None, 'a': 1, 'b': 2, 'func': <function func at 0x00000219468A3E18>}

  5.2.2 global关键字

a = 10
def func():
    global a     # 在函数(局部命名空间)中执行的是全局的变量a
    print('func中a的值:',a)
    a = 20       # 修改的是全局的a的值,生效
print('全局中a的值:',a)
func()
print('执行完func后全局中a的值:',a)

运行结果:
全局中a的值: 10
func中a的值: 10
执行完func后全局中a的值: 20

6、函数嵌套及作用域链 

  6.1 函数嵌套

    内部函数使用外部函数的变量

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

def max4(a,b,c,d):
    res1 = max2(a,b)
    res2 = max2(res1,c)
    res3 = max2(res2,d)
    return res3

ret = max4(23,-7,31,11)
print(ret)     # 31

--------------------------------------------------------------------------------

def f1():
    def f2():
        def f3():
            print("in f3") #
        print("in f2")    #
        f3()                 #
    print("in f1")        #
    f2()                     #
f1()

运行结果:
in f1
in f2
in f3
                                                    

  6.2 函数作用域链

def f1():
    a = 1
    def f2():
        def f3():
            print(a)
        f3()
    f2()
f1()      # 1

------------------------------------------------------------------------------------

def f1():
    a = 3
    def f2():
        a = 2
    f2()       # 并没有改变 f1函数中a的值
    print('a in f1 : ',a)
f1()    # 3

  6.3 nonlocal关键字

#定义:
  # 声明了一个上层的局部变量,作用域只到 上层第一个 有该变量(最低层),找上层中离当前函数最近一层的局部变量
     # 声明了nonlocal的内部函数变量修改,会影响到当前函数最近一层的局部变量
#规则:
    # 1.外部必须有这个变量
    # 2.在内部函数声明nonlocal变量之前不能再出现同名变量
    # 3.内部修改这个变量如果想在外部有这个变量的第一层函数中生效

def f1():
    a = 1
    def f2():
        nonlocal a
        a = 2
    f2()
    print('a in f1 : ',a)     # a in f1 :  2

f1()

  6.4 综合示例

a = 1
def outer():
    a = 1       #此处是局部变量
    def inner():
        a = 3
        b = 2
        print("inner中a的值:",a)
        print('inner')   #此处只是定义,并没有调用
        def inner2():
            # global a     #声明了一个全局变量,修改的是全局的a,不修改局部的
            nonlocal a     #声明了一个上层的局部变量,作用域只到 上层第一个 有该变量(最低层),找上层中离当前函数最近一层的局部变量
                           # 声明了nonlocal的内部函数变量修改,会影响到当前函数最近一层的局部变量
            a += 1       #不可变数据类型的修改
            print('inner2中的a:%d'%a)
            # print('inner2')
        inner2()
    inner()
    print('局部的a:%d'%a)
outer()
print('全局a:%d'%a)

运行结果:
inner中a的值: 3
inner
inner2中的a:4
局部的a:1
全局a:1

7、函数名的本质

   函数名本质上:就是函数的内存地址

def func():
    print(123)
func2 = func         ###  函数名可以赋值
func2()    # 123

  7.1 可以被引用

def func():
    print('in func')

f = func
print(f)       #<function func at 0x000001C6CF353E18>

  7.2 可以被当做容器类型的元素

def func():
    print(123)
func2 = func         # 函数名就是内存地址,可以赋值
l = [func,func2]     # 函数名可以作为容器类型(如list,dict等)的元素
print(l)
for i in l:     # i <=> 函数名
    i()         # 调用函数

运行结果:
[<function func at 0x0000025136343E18>, <function func at 0x0000025136343E18>]
123
123

  7.3 可以当做函数的参数和返回值

def func():
    print(123)
def wahaha(f):
    f()                 # 执行func()
    return f            # 函数名可以作为函数的返回值
qqxing = wahaha(func)   # 函数名可以作为函数的参数   <=> qqxing = f
qqxing()                # 等价于执行  func()

运行结果:
123
123

8、函数的闭包

   闭包发生在嵌套函数中:

# bar函数不需要传参数,就能获取到外面的变量,这是一个典型的闭包现象。

def foo():
    name = "Andy"  # 定义了一个foo函数内部的局部变量
    def bar():
        print(name)  # 在bar函数内部引用了其外部函数foo的局部变量name
    return bar

func = foo()    # 相当于执行 func = bar
func()          # 相当于执行 bar()     # Andy

  8.1 闭包的定义

  函数内部定义的函数,称为内部函数。这个内部函数只有使用了它外部函数的局部变量,即使外部函数返回了,这个内部函数还可以访问到外部函数的局部变量,这种现象就叫做闭包

  8.2 闭包的本质

  闭包:是由函数和与它相关的引用环境组合而成的实体。

  像下面的 func函数就不能称为闭包,因为它并没有使用包含它的外部函数的局部变量。

name = "Alex"  # 定义了一个全局变量
def foo():
    def bar():
        print(name)  # 在bar函数内部引用了全局变量name
    return bar

func = foo()
print(func.__closure__)      # None

  8.3 判断闭包函数的方法__closure__

#输出的__closure__有cell元素 :是闭包函数
def func():
    name = 'eva'
    def inner():
        print(name)
    print("inner.__closure__ : ",inner.__closure__)
    return inner
f = func()
f()    

#运行结果:
(<cell at 0x00000215E4550C48: str object at 0x00000215E46F84C8>,)
eva

————————————————————————————————————

#输出的__closure__为None :不是闭包函数
name = 'egon'
def func2():
    def inner():
        print(name)
    print("inner.__closure__ : ", inner.__closure__)
    return inner
f2 = func2()
f2()

#运行结果:
None
egon

   8.4 闭包嵌套

def wrapper():
    money = 1000
    def func():
        name = 'eva'
        def inner():
            print(name,money)   # eva 1000
        return inner
    return func
f = wrapper()
i = f()
i()
原文地址:https://www.cnblogs.com/timetellu/p/10681272.html