Python两种函数及参数类型:实参和形参、位置参数和关键字参数 以及变量作用域和闭包closure


主要关于:Python两种函数及参数类型:实参和形参、位置参数和关键字参数 以及变量作用域和闭包closure

一、函数类型

1. 普通函数

def square(x):  # 关键字  函数名  输入参数
	s = x*x     # 函数主体
	return s    # 返回参数,可同时返回多个

2. lamada函数

func1 =  lambda x:x+1  # 函数名 = 关键字 输入参数 返回参数

二、参数类型

1. 两种分类

  • 实参和形参

    • 实参:调用参数时用的参数: f(2)

    • 形参:定义函数时,用的参数:

      def f(a): # a是形参
          return a
      f(2)  # 2是实参
      
  • 位置参数和关键字参数

2. 位置参数(传入时注重顺序

  • 定义

    • 调用时,不需要指定参数,按顺序传入即可
    • 调用时,也可以使用同名参数名传入
    • 也可以有默认值,调用时既可以用
  • 分类

    传参顺序:必须参数,默认参数、可变长位置参数

    • 必须参数:只有形参

      def f(x,y):pass
      
    • 默认参数:

      • 定义:有形参有默认值

      • 使用:4种方法

        def f(x,y=1,z=2):pass  # 必须参数x, 默认参数y,z
        f(1)         #1 可以不传默认参数 
        f(1,3,4)     #2 按顺序传入三个参数
        f(1,z=1,y=1) #3 默认参数的可以不按参数顺序传入
        f(*[1,3,4])  #4 一次传入多个,用*解包成多个元素
        

        在没有斜杠的时候,调用位置参数时,既可以用位置也关键字方法
        def f(pos1,pos2,/,pos_kw,*,kw1,kw2):

    • 可变长的位置参数

      • 定义:参数的个数不定

      • 使用:2种方法

        def f(x,*args): # 通常用*args表示不定长位置参数
            print(type(args),args)  # tuple
        f(1,2,3,4,5)  #1 直接传入
        f(1,*[1,3,4]) #2 一次传入多个,用*解包成多个元素
        

    打包(pack)和解包(unpack):

    • 函数定义时, * 可以将按位置将传递进来的参数 打包成元组 (tuple) 类型;
    • 函数调用时, *可以解包待传递到函数中的元组、 列表、集合、字符串等类型, 并按位置传递到函数
    • 函数调用时,**可以解包字典,并按照关键字传递到函数入口参数中(下面会用到)

3. 关键字参数(传入时提供关键字)

  • 定义

    • 调用时需要提供关键字的名字,才能传入参数

    • 关键字参数,可以不按照顺序输入

      关键字参数和位置参数混用时,需要位置参数在前

  • 分类

    • 类似位置参数中的必须参数

      • 定义:在调用时,需要用关键字=value才可以使用

      • 使用:

        • 直接传入:f(x=1,y=2)
        • 一次传入多个参数:用**解包,来传入f(**{‘x’:1,‘y’:2})
        def f1(x,y,z):pass
        f1(x=1,y=2,z=3)
        #f1(x=1,3,z=3)  # 关键字参数要在位置参数前,x=1之后,都是关键字参数,所以3需要带上关键字
        f1(**{
                  
                  'x':1,'y':2,'z':3}) # 用**解包字典,当成关键字传入函数
        
    • 可变长的关键字参数

      • 定义:参数个数不定
      • 使用:同上
      def f2(**kwargs):print(type(kwargs),kwargs) # dict
      def f2(x,**kwargs):print(type(kwargs),kwargs) # dict
      f2(x=1,y=2,z=3)
      f2(**{
              
              'x':1,'y':2,'z':3})
      
    • 命名的关键字参数

      • 定义:可以指定变量名的关键字参数,调用时需要加变量名

      • 使用:

        def f3(x,*,y=2,z):pass # 命名的关键字参数,*表示后面的参数都是关键字参数,
        def f4(x,*args,y,z):pass #如果关键字参数前有可变长位置参数,则不需要再加* 了
        f3(x=1,y=2,z=3)
        f3(**{
                  
                  'x':1,'y':2,'z':3})
        f4(1,2,3,4,y=3,z=4)
        # f4(1,2,3,4,3,4) # 若不指定y和z的取值,就会报错,因为y,z是关键参数
        

4. 位置参数、关键字参数的混用

  1. 位置参数中,必须参数在前,默认参数在中,可变长参数在后
  2. 关键字参数中,命名参数在前,不定长参数在后
  3. 位置和关键字混用时,位置参数在前,关键字参数在后
  4. f(*args,**kwargs)可以传入所以类型的参数

一个压箱底的例子

def f(x,y=2,*args,k1,k2=1,**kwargs):
    print("位置参数有:必须参数、默认参数、可变长参数:")
    print("\t必须参数是:x =",x)
    print("\t默认参数是:y = ",y)
    print("\t可变长参数是:args =",args)
    print("关键字参数有:命名关键字参数、可变长关键字参数:")
    print("\t命名关键字参数是:k1 = %d, k2 = %d"%(k1,k2))
    print("\t可变长关键字参数是:kwargs =",kwargs)
f(1,2,*(3,4,5),k1=6,k2=7,**{
    
    'a':1,'b':2,'c':3})

python 语言中实现C的三目运算符:

# bSum?x+y:x-y  # 这个return有点儿东西的啊
def f(x,y,bsum=True):
    return x+y if bSum else x-y
f(1,2,1)

三、变量作用域(LEGB原则)

1. LEGB原则:

  • 当一个函数体内需要引用一个变量的时候, 会按照如下顺序查找:
  1. Locals: 首先查找局部变量
  2. Enclosing function locals: 去函数体的外层去寻找局部变量(嵌套情况)
  3. Gloal:从全局变量中寻找
  4. Built-in:还是找不到, 只好去找内置库 .

2. global 和 non local

global :声明把该变量变成全局变量
nonlocal : 声明局部变量的作用域,向外层扩展

When we declare a variable inside a nested function as nonlocal, its scope is extended beyond this inner-function to the outer-function it is nested within.

def outer_f():
    a = 'outer_string'
    def inner_f():
        nonlocal a   # 扩展变量a的作用域到外层
        a = 'local_string'
        global b     # 扩展变量b的作用域到全局
        b = 'global_string'
    inner_f();print(a) # local_string
outer_f();print(b)     # global_string

函数内,调用全局变量可以,但是不能重赋值全局变量

a = 0
def addtwo():
    c =a+2   # 此时可以,可以认为是调用全局变量a
    a = a+2  # return a 此时会报错,因为此时a赋值就变成了局部变量
    return c
d = addtwo()

四、closure闭包:(算是一种轻类)

1. 闭包的定义

把数据和函数绑定在一起的技术叫闭包。函数和函数调用的外部变量合在一起称为一个closure。

通俗的说,一个函数内部没有定义该变量,但是其外层函数定义了,假如内层调用的话,则该变量就会绑定binding到内层函数上,这叫闭包。

2. 闭包的准则

  1. 必须是一个嵌套函数
  2. 被嵌套的函数,一定要调用包裹函数内的变量
  3. 包裹函数,必须返回被嵌套的函数(类似装饰器)
def out_f():        #1
    a = 'out_string'
    def inner_f():  #1
        print(a)    #2
    return inner_f  #3
f = out_f()  # 返回的是一个函数
f()

3. 闭包的特点

  1. 该数据不是通过参数传进去的
  2. 在闭包区域(enclosing)的变量和内层函数会被保存,即使变量跑出闭包区域或者外层函数被删除了,变量依然在。
    1. 所有的函数都有一个__closure__属性
    2. 如果函数是一个闭包,则其__closure__属性就会返回一个tuple of cell objects
    3. _closure_[0].cellcontents 可以查看闭包里存了哪些变量
out_f.__closure__[0].cellcontents  # 'out_string'
dir();del out_f;dir()  # 闭包函数out_f被删了,out_f中第2行到第4行的对象也会被保存
f.__closure__[0].cellcontents  # 'out_string'

4. 什么时候使用闭包closure

  1. 闭包可以避免使用全局变量,可以隐藏一些变量
  2. 可以包含一些属性和多个函数(通常是一个,返回一个函数元组),算是一种轻类,更简洁。
  3. 有时装饰器算是一种闭包(闭包第2个特征有时可能不满足)
def out_f():
    a = 'out_string'
    def inner_f():print(a)
    def inner_f1():print(a*2)
    return inner_f,inner_f1
f = out_f()
f[0]();f[1]() # 分别调用两个函数

猜你喜欢

转载自blog.csdn.net/weixin_43899514/article/details/109402360
今日推荐