python 函数

1 为什么使用函数

在没有接触函数时,有时候需要将一个功能多次写,如果需要修改其中一个变量,则需要把所有实现该功能的代码一处一处改。不利于代码维护,代码量大了,组织结构也会很不清晰。
所以总结不使用函数的确定:

  • 代码冗余(重复代码多)
  • 不利于维护(修改麻烦)
  • 可读性差(组织结构不清晰)

函数的优点:

  • 解决代码重用问题
  • 统一维护
  • 程序的组织结构清晰,可读性强

# 2 函数的定义

函数种类:

  1. 内置函数:内置在python解释器的函数
  2. 自定义函数:
def funtion_name(args):
    """描述信息"""  #可通过function_name.__doc__查看
    函数主体
    return

自定义函数的种类

  • 空函数

    def abc():
        """描述信息"""
        pass
    

    空函数使用在布置程序大致框架时,不用具体实现函数的功能。

  • 有参函数

    def abc(args):
        """描述信息"""
        函数主体
        return xxx
    

    通常有参的函数,都是有返回值的。

  • 无参函数

    def abc():
        """描述信息"""
        函数主体
    

    通常有参的函数,都是有返回值的。


# 3 函数调用 语句形式:函数名() 表达式形式:res=10*abc(1) 函数调用作为另一个函数的参数:print(abc()) 注意:我们调用一个不存在的函数 ```python func() #NameError: name 'func' is not defined #a=b #NameError: name 'b' is not defined ``` 这里返回的是,“NameError”,函数名未定义。所以,在python中,我们可以理解为**定义函数,就是定义变量名**。
# 4 函数的返回值 python中函数的返回值可以是任意类型。 1. 没有return或return为空,返回的是None。 2. 可以返回多个函数值(本质是打包成一个元组) 3. 如果有多个return(语法上不会报错),但返回第一个遇到的return。

注意:

def my_min(x:int,y:int) ->int:
    pass

print(my_min.__annotations__)   #{'x': <class 'int'>, 'y': <class 'int'>, 'return': <class 'int'>}

该方法适合那些从c++,java转到Python的。


# 5 函数的参数 定义函数时的参数,就是形式参数。 调用函数时的参数,就是实际参数。 函数调用时,会把实参绑定到形参,结束调用时,解除绑定。
#形参、实参都是不可变
def foo1(a):
    a=3
    print(a)
x=1
foo1(x)  #3
print(x) #1

foo1里面重新给a赋值不会影响x,为什么?我们可以这样理解,实参是本身存的一个变量的值,而形参存的是一个变量名(没有值)。当发起函数调用时,形参会绑定实参的值,如果在函数内给形参赋值,形参会绑定新的值。(可以和变量与变量的赋值类比)所以不会改变实参的值。

#形参是可变类型
def foo2(a,L=[]):
    L.append(a)
    print(L)

foo2(1)   #[1]
foo2(2)   #[1, 2]
foo2(3)   #[1, 2, 3]

当foo2加载到内存,L指向的是[],当每一次调用函数时,都是在向同一个[]附加数据。

#改良版
def foo2(a,L=None):
    if L is None:
        L=[]
    L.append(a)
    print(L)
foo2(1)   #[1]
foo2(2)   #[2]
foo2(3)   #[3]
#实参是可变类型
def foo3 (L):
    L.append("abc")
    print("foo",L)

li=[1,2,3]
foo3(li)
print(li)   #[1, 2, 3, 'abc']

当把一个列表li当实参传到函数时,是将列表li的地址传给形参,所以形参所做的修改,都会影响实参。建议:不要使用可变对象作为实参。

a=1
def foo3(x=a):
    print(x)
a=3
foo3() #1

这里要区别于java等编译型语言。python作为一门解释型语言,是一边解释一边执行的。当它执行到foo3(),形参x=1被写入内存的。当执行到a=3时,并不会影响x的值。

6 函数的实参

def func(a,b,c)

实参传值的方式:

  1. 按位置传参:func(1,2,3)

  2. 按关键字传值:func(a=1,b=2,c=3)

  3. 混合传值:func(a=1,b=2,c=3)

    混合传值注意事项:

  • 按位置传值必须在按关键字传值之前
  • 一个形参只能接收一个传值,如:func(1,2,b=2)会报错,此时b接收了2个值

7 函数的形参

  1. 位置参数:必须传值
  2. 默认参数:可传可不传,且必须放位置参数后面
    func(a,b=2)
  3. 可变参数 *args 可以理解为一堆位置参数
    先按位置传值,剩下的被*接收,打包成一个元组,再传给args
    #从形参的角度,形参带*
    def func(a,b,*args)
    func(1,2,3,4,5,6)
    result:
    a=1
    b=2
    args=(3,4,5,6)
    
    #从实参的角度,实参带* 
    def foo1(x,y,z):
        print(x)
        print(y)
        print(z)
    foo1(*(1,2,3)) #如果去掉*,会把整个元组赋值给x,此时y和z没有值会报错
    

    当实参遇到*(1,2,3),需要把它拆成1,2,3这样来看

  1. 关键字传值 **kwargs 按关键字传值多余的被**接收,打包成一个字典,传给kwargs

    #从形参的角度,形参带**
    def foo2(**kwargs):
        print(kwargs)
    foo2(a="aa",b="bb")
    
    #从实参的角度,实参带**
    def foo3(x,y,z=1):
        print(x)
        print(y)
        print(z)
    foo3(**{"x":"a","y":"b"})
    
  2. 命名关键字 *,args 用于指定必须以某些关键字来传值

   def foo4(*,x,y=2):
   print(x)
   print(y)
   foo4(x=1)

命名关键字参数不能和可变参数混用

形参的顺序:

def func1(a,b=2,*args,**kwargs)

def func2(a,b=2,*,c=3,**kwargs)

8 nonlocal

nonlocal关键字,只在python3才有。
nonlocal 的作用就是将变量添加到上一层函数的局部命名空间中。

def layer_1():

    count=1

    def layer_2():

        count=2

        def layer_3():
            
            nonlocal count
            count=3
            print(count)
            print(locals())

        layer_3()
        print(count)
        print(locals())

    layer_2()
    print(count)
    print(locals())

layer_1()

结果:

3
{'count': 3}
3
{'layer_3': <function layer_1.<locals>.layer_2.<locals>.layer_3 at 0x000002B39BF7BB70>, 'count': 3}
1
{'layer_2': <function layer_1.<locals>.layer_2 at 0x000002B39BF7BAE8>, 'count': 1}

在第3层定义的nonlocal count,count=3影响到第2层count的结果,但第1层没有任何影响

原文地址:https://www.cnblogs.com/yangzhenwei123/p/6758893.html