为什么需要函数

例子:建房子
第五章函数 - 图1

当代码越来越多的时候,肯定会有重复的内容,这个时候就需要“偷懒”

函数的作用

  • 合并重复的部分
  • 测试很方便,测试起来很简单
  • 修改很方便,从修改很多次改为修改一次
  • 打包后可以作为API进行发布
  • 函数的输入和输出会形成一种映射,函数就是这种映射关系。

    KV编程论:解释函数

    从函数的数学定义来讲,从集合和映射的观点出发,从一个集合映射到另外一个集合,这个映射关系就是函数
    以议论文的写作为例,函数就是从提出问题到得出结论的过程,其中的代码就是分析问题和解决问题的过程。

    函数的定义

    def function_name(s): statement(s) return o

其中def是关键词不可变,s代表参数可以有多个,用逗号分割,function_name是函数名,命名规则同标识符。statement(s)是代码语句,其中可以调用其他的函数
函数名和函数参数后面一定要加冒号。
函数一定要有返回值,使用return
函数示例代码

def choose_num(start,end): num = random.randint(start,end) return num;

函数的参数

默认参数

给形参赋予一个默认值,如果有带默认值的同时也有不带默认值的参数,应该把带默认值的参数放在没有默认值的函数的后面
默认参数是可以被覆盖的
示例代码:

def choose_num(start, end=10): num = random.randint(start, end) return num

默认参数要放在非默认参数后面
默认值只会赋值一次。
默认值就好比是一个函数的静态变量??
调用多次对默认参数的修改不会被delete,而是会保存下来。
如果函数默认参数为None,则不会被记录
示例代码1,会保存的:

def f(a, L=[]): L.append(a) return L print(f(1)) print(f(2)) print(f(3)) [1] [1, 2] [1, 2, 3]

示例代码2,用None加上if不会保存的:

def f(a, L=None): if L is None: L = [] L.append(a) return L print(f(1)) print(f(2)) print(f(3)) [1] [2] [3]

关键词参数

关键词参数是指不按位置传,而是使用参数的关键词进行赋值,这样的话就可以不按照参数的顺序位置传参
关键词参数可以给默认参数赋值,也可以给非默认参数赋值
但是非默认参数必须赋值,位置传参或者关键词传参都可以
示例代码

def f(a, L=None): if L is None: L = [] L.append(a) return L f(a=1,L=[3,4]) [3,4,1]

任意参数

形式:_args或者*_kw

  • *args是可以任意多个参数,而且这个参数必须使用位置传参,一个星号参数会被定义为一个元组tuple,里面包括所有的输入的参数
  • **kw也是可以任意多个参数,但是这个参数必须使用关键词传参,两个星号参数会被传递为字典dict
    ,所有输入的参数会被输入为一个字典dict,所以两个星号必须使用关键词传参,关键词是key,值是value,一组kv为一组
    一个星号示例代码:

    def car(*args): print(args) for k in args: print(k) car(“red”, 10, ‘de8ug’) (‘red’, 10, ‘de8ug’) red 10 de8ug

两个星号示例代码:

def car(**kw): for k,v in kw.items(): print(f’{k}::{v}’) car(color=’blue’, price=10) color::blue price::10

  • 如果希望这个函数可以传递任意类型的参数,既可以位置传参也可以关键词传参的话,要在定义时同时以加入args和**kw,其中位置传参会被args包括,关键词传参会
    args和**kw都有示例代码:

    def car(_args,*_kw): print(args) for k,v in kw.items(): print(f’{k}::{v}’) car(‘volvo’,100.05,color=’blue’, price=10) (‘volvo’, 100.05) color::blue price::10

  • 如果出现既有指定参数也有任意参数的情况,代表常规使用的是指定参数,但是可以任意添加参数
    指定参数和任意参数同时存在的示例代码:

    def car(color, price, _args, *_kw): print(color) print(price) print(args) for k,v in kw.items(): print(f’{k}::{v}’) car(‘blue’, 10, ‘a’, ‘b’, C=’c’, D=’d’) blue 10 (‘a’, ‘b’) C::c D::d

-如果想直接传一个字典给kw,在调用是字典也必须要加上
直接传字典给**kw示例代码:

kwargs= {‘color’:’red’, ‘price’:10} def car(kw):
for k,v in kw.items():
print(f’{k}::{v}’)
car(
kwargs) color::red price::10

  • 如果想直接传一个字典给带默认值的参数:
    1.要在调用时加上**
    2.字典的key要和默认参数的名字对应
    传字典给默认参数示例代码:

    kwargs = {‘color’:’red’, ‘price’:10} def car(color=’blue’, price=5): print(color,price) car(**kwargs) red 10

  • 如果在有默认值也有非默认值,但是有需要必须传递一个非默认值参数,可是使用,的形式来限定必须传递某个参数
    *但是这个必须传递的参数必须使用关键词传参

    示例代码:

    def car(color=’blue’, *, price): print(color,price) car(price=30) blue 30

lambda表达式

形式

Add = lambda x: x+1

  • 调用和函数是一样的:Add(19) 会输出20
  • 但是这种把lambda表达式赋给一个变量的方法是不被官方建议的,官方建议使用定义函数
  • lambda表达式在可以简化大量代码的时候是建议使用的,filter函数
    filter函数的作用是传入两个参数,第一个是一个函数或者None(一定是返回True和False的函数,用于对数据的筛选),第二个是可以迭代的数据对象(列表、元组和字典)
    示例代码:

    my_list = [1,2,3,4,5,6,7,8,9] list(filter(lambda x: x%2==1,my_list)) [1, 3, 5, 7, 9] # 返回奇数的 my_list = [1,2,3,4,5,6,7,8,9] list(filter(lambda x: x+1,my_list)) [1, 2, 3, 4, 5, 6, 7, 8, 9] # 返回+1后不是False(0)的

函数注释与文档说明(annotations,doc)

文档说明

  • 写在函数的定义行和函数体之间的代码注释
  • 使用””””””的
  • 示例代码:

    def add(x, y): “””Add x and y together””” return x+y

  • 得到一个函数的文档说明使用:
    func_name.doc

    函数注释

  • 位置为写在函数定义行的输入参数后或者整行最后表示这个函数返回的类型

  • 示例代码:

    def Add(x:int, y:’这个数随便’) ->int: “””Add x and y together””” return x + y

  • 得到一个函数的函数注释使用
    func_name.annatations

    占位符pass

    pass的本质是一个占位符,是用来在编程中表示这个函数/这个模块的代码还没写完用的

  • 示例代码:

    def Add(): pass 表示这个函数体结束了,一般都是当作占位符来用

变量的作用域与函数模块的关系

作用域的意思主要是变量能被识别的部分就是这个变量的作用域

作用域主要分三种:

  • 局部作用域,只在这个代码块中可以(用缩进来区分)
  • 封闭作用域,函数嵌套时会说
  • 全局作用域,在整个这个代码文件中都可以使用
  • 内置作用域,在python代码的任何位置都可以使用

    全局变量

  • 全局变量在这个代码文件中都可以识别,但是可能会被局部作用域的变量覆盖
    示例代码:

    x = 1 def Add(): x += 1 print(x) Add()

以上代码会报错local variable ‘x’ referenced before assignment,报错的原因是x+=1的本质是
x = x + 1
错误的原因是,python的解释器会把等于号左边的变量先予以声明,然后右边的x被认为是刚声明的变量,所以报错引用了未赋值的变量

  • 函数体内是可以识别全局变量的,但是全局变量会被同名的局部变量覆盖
    识别全局变量示例代码:

    x = 1 def add(): print (x) add() 1

  • list的作用域同int类型,也是全局范围识别,但是可以被局部变量覆盖,(为赋值就是用也会报错unboundLocalError)
    tips:list的+=运算符使用的前提是这个列表已经被赋值,否则会报错unBoundLocalError

  • 函数嵌套
    函数嵌套了一个函数,此时里面的函数的作用域就是封闭作用域,函数是python里的一级对象,一级对象就是:
  • 在运行时创建的
  • 能赋值给变量或数据结构
  • 能作为参数传给函数
  • 能作为函数的返回结果。
    函数嵌套的示例代码:

    def hello(): s = ‘de8ug’ def say(): print(s) return say h = hello() h() de8ug

嵌套的函数可以被返回,也可以在函数体内调用,也可以在函数体外调用,
直接打印s会报错name ‘s’ is not defined
但是函数内声明的变量s无法在函数体外被识别,但是可以被嵌套的函数所识别并输出,此时函数体内声明的变量s的作用域就是封闭作用域,意思是只能在函数体内被识别,如果出了函数体,虽然其本身仍然是存在在内存中的,但是不可以被识别(类似C语言中的private型变量,而函数在python中本身就是对象,所以函数内声明的嵌套函数和变量会存在也可以被使用,但是在函数体外是不被识别的)

  • 闭包函数
    函数闭包,或者闭包函数,本质是一种函数,可以在运行后,依然存在自由变量。可以对闭包内的数据进行隐藏,避免使用全局变量。
  • 在函数闭包内给闭包变量加上nonlocal关键词就可以使用闭包变量,不会报错unboundLocalError
    示例代码:

    def hello(): s = ‘hello’ def say(): nonlocal s s += ‘在干啥?’ print(s) return say h = hello() h() hello在干啥?