函数篇

参考网址

https://blog.csdn.net/weixin_35688430/article/details/107752681

判断函数是否可调用

def add(x,y):
    return x + y

print(callable(add(1,2))) # False
print(callable(add)) # True

python函数传参,很灵活,参数可以是位置参数,也可以是关键字参数

def add(x,y,z):
    return x+y+z

print(add(x=1,y=2,z=3)) # 传入关键字参数
print(add(1,2,3)) # 传入位置参数

## 如果使用位置参数和关键字参数混合调用。那么位置参数必须要在关键字参数之前传入,参数不能重复
add(4,z=5,y=3) ##正确, #错误写法add(4,x=3,z=8)

可变位置参数('*')和可变关键字参数('**变量名')

  • 在形参前使用 * 表示该形参是可变位置参数,可以接受多个实参(它将收集来的实参组织到一个tuple中)

  • 可变位置参数必须要在位置参数后面,可变位置参数不能使用关键字传参

def sum(*args):
    total = 0
    for i in args:
        total += i
    return total

print(sum(1,2,3,4)) # 10
  • 可变关键词参数(**变量名)

    • 形参前使用 ** 符号,表示可以接收多个关键字参数(收集的实参名称和值组成一个字典)

    • 可变关键字参数,必须要在所有参数最后面(即,参数,可变参数,可变位置参数)

def show_msg(**kwargs):
    for k,v in kwargs.items():
        print(k,'---',v)
        
show_msg(name='JimGreen',age=20) # name --- JimGreen  age --- 20
  • keyword-only参数: 就是这个参数必须采用关键字传参

    • 语法 :在一个星号之后,或一个可变位置参数(args)之后

    • 理解: 假如此时不使用'关键字参数',那么该参数永远会被包裹到'*args'中

def fn(*args,x):
   print(x,args)
fn(3,5,x=7) # 7 (3, 5)

def fn(*,x):
   print(x)
fn(x=7) # 7
  • 小结,参数一般顺序是: 普通参数、缺省参数、可变位置参数、keyword-only(可带缺省值)、可变关键字参数

参数解构

  • 在数据类型前使用 * 或者**,把数据类型的解构解开,提取所有元素作为函数的实参

  • 非字典类型使用*解构成位置参数(单层解构)

  • 字典类型使用**解构成关键字参数(双层解构)

  • 注意事项: 提取出来的元素数目要和参数的要求匹配,也要和参数的类型匹配

def add(x,y):
   return x + y

print(add(*[1,2])) # 3

print(add(*{'x':1,'y':2})) # xy # 这里只解构了一层,即key层,所以把字符串'x'和'y'传入函数

print(add(**{'x':1,'y':2})) # 3

函数返回值

  • 如果没有一条return语句被执行到,隐式调用return None

  • 调用return None,可以简写为return

  • 函数不能同时返回多个值,返回多个值,实质是被打包成 tuple

def show_msg():
   return 1,2,3

print(show_msg()) # (1, 2, 3)

x,y,z = show_msg()
print(x,y,z) # 1 2 3

变量作用域注意事项

  • 以下demo运行OK,因为全局变量可以在函数内部被调用
x = 5 

def foo():
   print(x)

foo() # 5
  • 此时,若函数内部再次定义x,比如以下demo,就会报错
x = 5

def fn():
   x = x + 1 # 定义局部变量x,注意,此时的x并不是全局x
   print(x) # local variable 'x' referenced before assignment

print(fn())

'''
- 相当于在foo内部定义一个局部变量 x,那么foo内部所有x都是这个局部变量x了
- x = x +1 相当于使用了局部变量x,但是这个x还没有完成赋值,就被右边拿来做加1操作了
- 结果肯定出错
'''

## 正确写法,把x换成其他变量即可
x = 5

def fn():
   y = x + 1
   print(y)

print(fn()) # 6 None

### 使用 global关键字声明也可以解决这个问题
x = 5

def fn():
    global x # 声明全局变量
    x = x + 1
    print(x)
    
print(fn()) # 6
print(x) # 6


函数闭包

  • 在嵌套函数中,指的是内层函数引用到了外层函数的自由变量,就形成了闭包

  • 典型 demo

def outer():
   x = 1
   def inner():
      y = x + 1
      return y
   return inner

f = outer()
print(f()) # 2
  • nonlocal:将变量标记为不在本地作用域定义,而是在上级的某一级局部作用域中定义,但不能是全局作用域中定义

  • 理解: 就是引用父级变量

def outer():
   x = 1
   def inner():
      nonlocal x # 引用上面的x
      y = x + 1
      return y
   return inner

f = outer()
print(f()) # 2
  • 闭包的好处,可以捕获父函数的参数,比如使用异步的时候,前端js可以使用它来解决文件上传问题

常用函数

  • isinstance(obj, class_or_tuple)
print(isinstance(1,int)) # True
print(isinstance(1,str)) # False
  • issubclass(对象,类): 判断对象是否是类的'子类',返回布尔
class Foo():
   pass

class Bar(Foo):
   pass

class Test:
   pass

print(issubclass(Bar,Foo)) # True
print(issubclass(Test,Foo)) # False

-  enumerate(iterable, start=0)

   - iterable 必须是一个序列,一个迭代器或其他支持迭代的对象
   - 返回值是一个元组,该元组包含一个计数(从 start 开始,默认值为 0)以及遍历迭代获得的值
      - (0,value1),(1,value2)

   collect_data = []
   for x in enumerate([2,4,6,8]):
       collect_data.append(x)
   print(collect_data) # [(0, 2), (1, 4), (2, 6), (3, 8)]

- zip(*iterables)
   - 返回一个迭代器,用于聚合来自每个迭代器的元素
   - iterables 必须是一个序列,一个迭代器或其他支持迭代的对象

   list1 = [1,2,3,4]
   list2 = [5,6,7,8]
   data = list(zip(list1,list2))
   print(data) # [(1, 5), (2, 6), (3, 7), (4, 8)]

匿名函数

- 使用lambda关键字定义匿名函数,格式为: lambda [参数列表]: 表达式
- 参数列表不需要小括号。无参就不写参数
- 冒号用来分割参数列表和表达式部分
- 不需要使用rrturn。表达式的值,就是匿名函数的返回值。表达式中不能出现等号
- lambda表达式(匿名函数)只能写在一行上,也称为单行函数
- 匿名函数往往用在未高阶函数传参时,使用lambda表达式,往往能简化代码
  • 示例demo

## - 将函数对象加入列表中,通过索引访问,然后传参
# 如果这里不加索引[0],由于list对象不可调用,会报错
# 也就是说: [lambda...][0] == func 函数对象
[lambda x : x + 1][0](1) # 返回值 2

## - 其他示例
# 使用括号()写好匿名函数,然后传参
>>> (lambda x,y=3:x + y)(5)
8
>>> (lambda x,y=3:x + y)(5,6)
11