首页 > 其他分享 >7.函数

7.函数

时间:2022-11-02 18:01:16浏览次数:47  
标签:函数 a1 a2 func print def

程序就是一大堆代码,有些功能可能在程序中反复使用,我们就可以将这些功能代码整合成一个函数,起一个名字,以后使用就调用一下,避免相同代码的重复书写

函数的作用:

  • 提高代码重用性
  • 拆分代码,易于维护
  • 实现函数式编程

1.函数的定义

 

 

 函数主要由五部组成:

  • 定义函数的关键字def
  • 函数名字:调用者通过函数名调用
  • 函数参数:将需要处理的数据传递给函数
  • 函数体:函数的执行部分
  • 函数返回值:将函数的处理结果返回给调用者

当然函数参数和返回值是非必须的

2.函数名

函数名和变量名本质上是一样的,都是指向一块内存空间,区别在于变量指向的是数据,,函数名执行代码块

既然函数名字类似变量,那么函数名就可以有多种用法

  • 函数名做容器的元素
    # 定义一个函数
    def fun():
        print('我最帅')
    
    # 将函数名放在容器中
    fun_list = [fun, fun, fun]
    print(fun_list[0])  # <function fun at 0x0000028192C2D1F0>
  • 函数名重新赋值
    # 定义一个函数
    def fun():
        print('我最帅')
    
    # 将函数名重新赋值为一个变量
    fun = 123
    print(fun)  # 123
  • 函数名做参数
    # 定义一个函数
    def fun():
        print('我最帅')
    
    def handler(func):
        func()
    
    # 将函数作为参数传入另一个函数
    handler(fun)  # 我最帅

     

  • 函数名做返回值
    def handler():
        def fun():
            print('我最帅')
        return fun
    
    handler()()  # 我最帅

     

从上面的例子我们可以了解到,函数名就是一个名字,跟变量名好像没什么区别,其实真没什么区别

3.函数参数

有时候函数执行需要外部的一些数据,这些数据就通过参数进行传递

函数传递参数本质上是变量的内存地址的传递,这样做的特点有:

  • 节省内存
  • 对于可变类型数据,在函数体作了修改,就可以修改数据本身
def func(name, hobby):
    print(id(hobby))  # 2230389637952
    print(f"大家好,我叫{name}")
    print('我的兴趣爱好有如下几个:', end=' ')
    for ele in hobby:
        print(ele, end=' ')

    print('\n对了,我还喜欢篮球')
    hobby.append('篮球')

name = 'kunmzhao'
hob = ['篮球', '足球']
print(id(hob))  # 2230389637952

func(name, hob)
print(hob)  # ['篮球', '足球', '篮球']

通过以上案例我们可以发现 hob和hobby的内存地址是一样的,同时在func中队hobby添加元素,hob也会添加了元素

 

3.1 形式参数

def func(a1, a2):
    pass

a1和a2都是形式参数,给形式参数赋值有两种方式

  • 位置传参
    def func(a1, a2):
        pass
    
    # 1会赋值给a1, 2会赋值给a2
    func(1, 2)

     

  • 关键字传参
    def func(a1, a2):
        pass
    
    func(a2=2,a1=1)

    关键字传递参数可以更改参数的传参顺序,当函数有很多参数的时候,我们在调用的时候就可以选用该方法,不用在乎参数位置

3.2 默认参数

函数参数可以赋予默认值,在调用函数的时候如果不传递参数则使用默认值,在一定程度上可以更加人性化,但是也有一定的坑

def func(name, age=18):
    print(name, age)

func('kunmzhao') # kunmzhao 18

注意点:

  • 默认参数必须在形式参数后面,否则报错
  • 当默认参数是可变容器,需要警惕
    def func(a1, a2=[1, 2]):
        a2.append(a1)
        return a2
    
    v1 = func(10)
    print(v1)  #[1, 2, 10]
    
    v2 = func(20)
    print(v2)  # [1, 2, 10, 20]
    
    v3 = func(30, [11, 22])
    print(v3)  # [11, 22, 30]
    
    v4 = func(40)
    print(v4)  # [1, 2, 10, 20, 40]
    
    print(v1, v2, v3, v4)# [1, 2, 10, 20, 40] [1, 2, 10, 20, 40] [1, 22, 30] [1, 2, 10, 20, 40]

    分析:

    对于函数中的默认参数,在创建函数之初,就会开辟内存空间存储默认值,如果默认参数是可变类型,当函数修改了默认参数,那么后续函数中的默认值也会发生改变,在上述案例中,v1,v2, v4都没有传递默认参数,他们都指向同一个内存空间,所以数值都是一样的,而v3
    传递了默认参数,则不会在使用默认值了

3.3 动态参数

动态参数可以使得函数不限制参数的个数,有*args和**kwargs两种方式

  • *args
    可以将多余的实参保存成一个元组,传递给函数
    def func(name, *args):
        print(name, args)
    
    func('kunmzhao', 'victor', 18, 'shanghai')  # kunmzhao ('victor', 18, 'shanghai')
    *也可以打散一个元组或者列表,变成多个参数
    print(*(1, 2,))  # 1 2
  • **kwargs
    可以将多余的关键字传参保存成一个字典传递给函数
    def func(name, **kwargs):
        print(name, kwargs)
    
    
    func('kunmzhao', age=18, gender='man')  # kunmzhao {'age': 18, 'gender': 'man'}

    **可以打散一个字典,当成多个关键字传参

    def func(name, age):
        print(name, age)  # kunmzhao 20
    
    var = {'name':'kunmzhao', 'age':20}
    
    func(**var)

     

  • *args, **kwargs
    既可以传接收多个位置参数,也可以接收多个关键字参数,这种方式在很多矿建的源码中大量使用,可以极大的扩展函数功能

 

注意:在定义函数的时候,参数位置为:形式参数,默认参数,*args, **kwargs,否则程序报错

4.函数返回值

函数的返回值可以传递给调用者,返回执行结果,通过关键字return返回

def my_sum(num1, num2):
    return num1+num2


print(my_sum(100, 88))  # 188

当然,如果函数不需要有返回值,则可以返回None,以下方式都可以返回None

  • 没有使用return关键字
  • return
  • return None

有的时候我们可能返回多个数据值,我们就可以使用return返回多个值即可(使用逗号分割),python就会将多个返回值封装成一个元组返回给调用者

def fun():
    return 'kunmzhao', 'age', 18

print(fun())  # ('kunmzhao', 'age', 18)

 

5.函数中的作用域

函数的作用域即变量的生命周期,变量在自己的作用域可以被调用,否则不能被使用,作用域分为全局和局部

5.1局部作用域

在python的一个函数中,定义了的变量都可以在函数中使用,这点和C语言是不同的

def fun():
    a = 100
    if a > 100:
        b = 20
    else:
        b = 30
    print(b)  #

fun()  # 30

 

5.2全局作用域

一般声明在一个文件的头部位置的变量都是改文件的全局变量,可以被文件中的所有函数调用,常用大写来定义变量

COUNT = 100

def fun():
    print(COUNT)  # 100

fun()

但是局部变量只能对全局变量进行访问,不能做修改(内存地址的更改),如果想做修改,就可以在局部变量使用关键字global声明该全局变量

COUNT = 100

def fun():
    global  COUNT
    print(COUNT)  # 100
    COUNT -=10

fun()
print(COUNT)  # 90

 

6.函数的调用

函数的调用是非常简单的,使用函数名字,并传入必要的参数即可,这也是我们创建函数的意义所在

7.函数的嵌套

我们在一个函数中,声明一个或者多个函数,就称之为函数嵌套,函数嵌套也是闭包函数的基础

def run():
    name = "kunmzhao"

    def inner():
        print(name)
    inner()

嵌套函数会引发一个变量作用域的问题,我们只需要记住,变量优先在自己作用域中寻找,没有就向上级寻找,但绝对不会向下级寻找

案例:

name = "kunmzhao"


def run():
    name = "victor"

    def inner():
        print(name)
    return inner()


run()  # victor

 

8.闭包函数

闭包函数就是一个函数的嵌套,并且内部函数使用外部变量, 从而延长了变量的生命周期

def run():
    name = "victor"

    def inner():
        print(name)
    return inner

run()() # victor

为什么说延长了变量的生命周期呢?

一个变量只能在自己的作用域内存活,超过作用域就会被内存回收,在上述案例中,run()函数执行的时候,会声明name变量,然后返回一个函数,正常情况下,name会随着函数的执行结束而被销毁,但由于嵌套函数中使用了name变量,而延长生命周期直到函数inner执行完毕

9.装饰器

装饰器本质上就是闭包函数的语法糖,什么是语法糖呢?语法糖就是为了简化代码书写方式而实现的

装饰器可以在不改变原有函数的计算出上对函数扩展功能,是非常实用的,在很多优秀的框架中广泛使用

下面我们通过案例介绍装饰器,请在原函数的的基础上添加打印数据的功能

# 原函数
def sum(a1, a2):
    return a1+a2

版本一:直接修改原函数

def sum(a1, a2):
    print(a1, a2)
    return a1+a2

问题:如果函数有多个,难道我们要修改每一个函数吗?,同时,在实际开发中封装好的函数一般是不会轻易改动的

版本二:使用闭包函数

def sum(a1, a2):
    return a1+a2

def handler(func):
    def inner(a1, a2):
        print(a1, a2)
        res = func(a1, a2)
    return inner

handler(sum)(1, 2)  # 1, 2

通过闭包函数我们可以解决版本一的问题,但是这会导致我们在所有之前调用sum函数的地方都要修改为handler,也是很不方便的

版本三:使用闭包函数+函数重新赋值

def sum(a1, a2):
    return a1+a2


def handler(func):
    def inner(a1, a2):
        print(a1, a2)
        res = func(a1, a2)
    return inner

sum = handler(sum)
sum(1, 2)  # 1, 2

将sum函数重新赋值,指向一个新的函数,这样对于调用者是无感的,不过我们需要在每个原函数下面要对函数名重新赋值,于是便可以使用装饰器,其实装饰器本质上就是版本三

版本四:装饰器

def handler(func):
    def inner(a1, a2):
        print(a1, a2)
        res = func(a1, a2)
    return inner

@handler def sum(a1, a2): return a1+a2 sum(1, 2) # 1,2

但是我们会发现一个问题,此时的sum的__name__变成了inner,这不是我们希望的,我们就可以使用functools功能

def handler(func):
    # @functools.wraps(func)
    def inner(a1, a2):
        print(a1, a2)
        res = func(a1, a2)
    return inner

@handler
def sum(a1, a2):
    return a1+a2

sum(1, 2)  # 1,2
print(sum.__name__)  # inner
import functools
def handler(func):
    @functools.wraps(func)
    def inner(a1, a2):
        print(a1, a2)
        res = func(a1, a2)
    return inner

@handler
def sum(a1, a2):
    return a1+a2

sum(1, 2)  # 1,2
print(sum.__name__)  # sum

在面试中经常需要我们手写一个装饰器,以下提供一个完善的写法

import functools
def wrapper(func):
    @functools.wraps(func)
    def inner(*args, **kwargs):
        res = func(*args, **kwargs)
        return res
    return inner

 

从上面我们可以发现,装饰器基于闭包函数,闭包函数基于函数的嵌套

 

10.匿名函数

匿名函数就是一个没有函数名字的函数,使用lambda关键字实现,用于定义一些简单的函数

10.1 定义

res = lambda x, y:x+y

print(res(1, 2))  # 3

匿名函数的参数可以是多个,和有名函数的参数一样,但是函数体只能有一行,返回值就是:冒号之后的运算结果

匿名函数在后续一些高阶函数中使用非常广泛,如map, filter,sort, reduce,后续介绍

标签:函数,a1,a2,func,print,def
From: https://www.cnblogs.com/victor1234/p/16851852.html

相关文章

  • Python的str函数及其对象
    str()一般是将数值转成字符串。repr()是将一个对象转成字符串显示,注意只是显示用,有些对象转成字符串没有直接的意思。如list,dict使用str()是无效的,但使用repr可以,这是为了......
  • C语言strtok()函数:字符串分割_F_hawk189_新浪博客
    头文件:#include函数定义:char*strtok(char*s,constchar*delim);函数说明:strtok()用来将字符串分割成一个个片段。参数s指向欲分割的字符串,参数delim则为分割字符串......
  • Python基础之函数,面向对象
    目录1函数1.1定义一个函数1.2函数调用1.3参数1.3.1必需参数1.3.2关键字参数1.3.3默认参数1.3.4不定长参数1.3.4.1不定长*:元组1.3.4.2不定长**:字典1.3.5匿名......
  • hive的trunc函数详解
    一、日期TRUNC函数为指定元素而截去的日期值。其具体的语法格式:TRUNC(date[,fmt])其中:date一个日期值fmt日期格式,该日期将由指定的元素格式所截去。忽略它则由最......
  • Java 程序实现私有构造函数
    转:Java程序实现私有构造函数  ......
  • shell编程之函数以及函数中的递归
    一、什么是函数使用函数可以避免代码重复使用函数可以将大的工程分割为若干小的功能模块,代码的可读性更强类似于Java的方法    二、获取函数的返回值return表......
  • 1-2 变量与递归函数
    变量作用域(全局变量和局部变量)变量变量起作用的范围称为变量的作用域,不同作用域内同名变量之间互不影响。变量分为:全局变量、局部变量。全局变量1.在函数和类定义之外......
  • MySQL_分组函数
    功能用作统计使用,又称为聚合函数或统计函数或组函数分类Sum求和Avg平均值Max最大值Min最小值Count计算个数特点1sum、avg一般用于处理数值型2以上分组函......
  • Pinia在请求中无法获取函数的报错
    //在*.ts/js文件中使用pinia报错解决方法(未删减完全)//文件路径srcstore-index.ts-user.store.ts-user.store.tsrouter-index.ts//使用......
  • MySQL_单行函数
    常见函数:字符函数:LengthConcatSubstrInstrTrimUpperLowerLpadRpadReplace数学函数RoundCeilFloorTruncateMod日期函数NowCurdateCurtimeYearMonth......