第四篇 函数

一.函数的定义

# 函数的定义

def 函数名(形参):
    '''
    文档字符串
    函数功能:
    函数参数:
    函数返回值
    '''
    function_body
    return [expression]

# 函数的使用
函数名(实参)
# 1. 函数的定义
def sayHello(name):
    print("你好,{},很高兴再次见到你".format(name))

    return "{}来北京看我了".format(name)


#函数的调用
print(sayHello("dali"))

# 输出
你好,dali,很高兴再次见到你
dali来北京看我了

 

# 2. 函数可以没有参数
def Hello():
    a= 3+4
    return a

print(Hello())

#输出
7

 

# 3. 定义一个变量,给变量的赋值是个函数名,就相当于把这个变量变成一个函数了

def sayHello2(name):
    print("你好,{},很高兴再次见到你".format(name))
    return 1
message = sayHello2
print(message("xiaochengzi"))

# 输出
你好,xiaochengzi,很高兴再次见到你
1

二.函数的参数

1. 必备参数 或者 位置参数

位置参数的特点:必须要给它传值,位置在最前面,定义了几个位置参数,调用函数的时候就必须传几个参数,否在会报错

# x,y 是位置参数,位置参数就是必须参数,函数调用时不能缺少
def add(x,y):
    return x+y

print(add(2,3))   #5
# 错误示例
def add(x,y):
    return x+y

print(add(2))  

# TypeError: add() missing 1 required positional argument: 'y'

2. 默认参数

如果不想给某个参数传值,但是参数个数还不减少,那可以在函数声明时设置默认参数,就是给某个参数一个默认值,

如果不传值,函数调用时,默认就用的是设置的默认参数,如果传值了,函数调用时就用的是传的值

特点:默认参数的位置,一定要放到所有的位置参数的后面

# # j 定义时就给了个默认参数,如果j不传值,就默认取0, 默认参数一定要放在所有参数的最后面
def add1(i,j=0):
    return i+j

print(add1(3))   # 3

3. 不定长参数

如果函数声明时,还不能确定到底会有多少个参数,可以定义不定长参数

特点:1) 不定长参数用*args表示, 2) *args的类型是元组 3)它的位置必须在默认参数后面

def add2(x,y=0,*args):
    print(args)
    return x + y

print(add2(2))
# 输出
()    # 此时x=2,y =0, *args没有传参,是个空元组
2

print(add2(4,5))
#输出
()   # 此时x=4,y =5, *args没有传参,是个空元组
9

print(add2(1,3,5,7,9))
# 输出
(5, 7, 9)  # 此时x=1,y =3, *args是个元组,其值是(5,7,9)
4
def add2(x,y=0,*args):
    print(args)
    return x + y + sum(args)  # 使用参数,要去掉*号


print(add2(1,3,5,7,9))

#输出
(5, 7, 9)
25

4. 关键字参数

如果想传的参数是字典,就可以用关键字参数

特点:关键字参数 **kargs ,其类型是字典,必须最最后面,以 a = 1的形式传参

def add4(x,y,*args,**kwargs):
    print(kwargs)
    return x + y + sum(args) + sum(kwargs.values())

print(add4(1,3,3,4,5,a=1,b=5,c=5,d=-5))

# 输出
{'a': 1, 'b': 5, 'c': 5, 'd': -5} # 此时,x=1,y=3, args=(3,4,5),kwargs = {'a': 1, 'b': 5, 'c': 5, 'd': -5}

22

5. 形参与实参

 

 

三.函数的作用域

函数作用域的寻找顺序:Local -> enclosing->global -> build-in

1. L(Local)局部作用域

变量是在函数内定义的

a = 3  # 全局作用域

def scope():
    a = 5   # 局部作用域
    print(a)

scope()

# 输出
5

 

2. E(Enclosing)闭包函数外的函数中

见后面闭包的介绍

 

3.G (Global)全局作用域

变量是在函数外定义的,是整个文件的

a = 3

def scope():
    print(a)

scope()
#输出
3

4. B(Built-in) 内建作用域

Python 自带的,预留的一些变量,函数名等

Python自带的不太适合举例说明

作用域的查找顺序是先局部,后全局,最后再内建,如果都没有该变量,就会报错

def scope():
    print(a)

scope()

# NameError: name 'a' is not defined

5. 作用域有两个函数:Locals() 和 globals()

a = 3

def scope():
    a = 5


print(globals())
# 输出
{'__name__': '__main__', '__doc__': '\n作用域生效机制\n局部  闭包函数外的函数中 全局 内建\n\n', '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x10511a470>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, '__file__': '/Users/qiaoersun/Documents/千万别删/测试/Codes/testfanStudyPractice/lesson02/作用域.py', '__cached__': None, 'a': 3, 'scope': <function scope at 0x10529bd08>}

暂时还没弄明白

四.闭包(_closure_)

闭包的特点:

1. 闭包函数必须有内嵌函数

2. 内嵌函数需要引用嵌套函数的变量

3. 闭包函数必须返回内嵌函数

闭包的作用:

1. Python闭包就是装饰器(或叫修饰器)

2. 在不改原有函数任何内容的情况下,给原有函数增加功能

def greeting(name):
    '''
    1.在greeting函数中内嵌了一个greet函数
    2.greeting函数的返回值是一个函数greet

    '''
    def greet():
        print(name)
    return greet   # 函数的返回值是greet函数

# 定义一个func变量,调用greeting函数,因为greeting函数的返回值是greet,而greet也是个函数,所以就相当于func是个函数
# 此时只是返回了greet,greet函数并没有被调用,所以也不会有任何返回值
func = greeting('老刘')


# 那么再调用func这个函数就等于是调用的下面的部分
'''
    def greet():
        print(name)
'''
func()
# 输出
老刘
# greeting()其实就是greet函数,那么也可以用下面的方式调用
greeting("小猫")()
# 输出
小猫

对闭包函数的公式总结

def greeting(name):
    # 1. greeting函数里内嵌,嵌套了一个greet函数 --> 满足了:闭包函数必须有内嵌函数
    a = 5
    def greet():
        # 2.在内嵌函数里引用了嵌套函数(greeting)的变量:a 和 name, 这个变量必须是内嵌函数外层已经定义的,不能是全局变量--->
        # 满足了:内嵌函数必须要引用嵌套函数的变量
        print(name)
        print(a)
    # 3. greeting函数里返回了一个内嵌函数greet--->满足了:函数必须返回内嵌函数
    return greet   # 注意此处不能返回greet()

 # 4. 由上面3点的同时满足,才使得greeting成为一个闭包函数
 
 # 5. 闭包函数的调用,在上面已经有了,此处略

在Python里闭包其实就是装饰器

五.装饰器

 装饰器的作用:就是为已经存在的对象添加额外的功能

 装饰器的应用场景:插入日志,性能测试,事务处理,缓存,权限校验等等

示例:计算一个函数执行用了多长时间

'''
闭包的作用:
1. 在Python闭包就是装饰器(或叫修饰器)
2. 在不改原有函数任何内容的情况下,给原有函数增加功能

利用闭包实现修饰器
'''

'''
1. 计算原有函数wait()的执行时间,但是不能改wait()函数的任何代码
2. add()函数,把add()执行结果写入一个文件,不能改变add()函数的任何值
'''


import  time
import random

'''
解决办法:用闭包的方式解决,那么就要把原来已经存在的功能,即函数作为参数传递给闭包,此处形参用func代替
闭包一定是写在原还是的前面的
'''
# 2.用闭包的方式不改变原代码的情况下,新增计算时间的功能
def decorator(func):
    def wrapper():
        startTime = time.time()
        result = func()
        endTime = time.time()
        print("测试结束,花费时间{}秒".format(endTime-startTime))
        return result   # 上面已经执行过了result,这里为什么还要再返回一下result?不返回会有什么影响?
    return wrapper

'''
wait函数是已经存在的一段代码,假设有一万行,突然有个需求,要在wait上加一个功能,计算wait函数执行需要花多长时间
如果直接在wait函数上改,可能会引起其他bug,第二你也懒得看wait的代码,一个好的解决办法就是使用闭包,用装饰器来解决
这个问题
'''

# 1. 已有的功能
def wait():
    w = random.randint(5,10)
    print(w)
    time.sleep(w)
    print("执行wait完毕,用了{}秒".format(w))

# 3. 调用闭包,获取执行结果
a = decorator(wait)  # 相当于a 就是 wrapper函数里
a()  # 再调用wrapper函数, 就能得到计算的结果

# 输出
9
执行wait完毕,用了9秒
测试结束,花费时间9.005208253860474秒

上面利用闭包写好了修饰器,但是闭包的使用不是像上面那样通过a = decorator(wait)()的方式使用的,而是通过在原有函数的上面加@闭包函数名的方式使用

import  time
import random


# 2.用闭包的方式不改变原代码的情况下,新增计算时间的功能
def decorator(func):
    def wrapper():
        startTime = time.time()
        result = func()
        endTime = time.time()
        print("测试结束,花费时间{}秒".format(endTime-startTime))
        return result   
    return wrapper


# 3.通过@闭包函数名的方式,实现装饰器
@decorator
# 1. 已有的功能
def wait():
    w = random.randint(5,10)
    print(w)
    time.sleep(w)
    print("执行wait完毕,用了{}秒".format(w))

# 4. 直接调用原函数就可以实现功能
wait()

# 输出
10
执行wait完毕,用了10秒
测试结束,花费时间10.002923727035522秒

 

# 示例2

'''
已知:现有add函数
需求:add函数执行结果写到一个文件里去
'''


def writeAddtoFile(func):  # 闭包的参数func,将来传的就是原有的功能函数add,这是永远不变的,套路,固定写法;外面的函数传函数名
    def wrapper(a,b):      # 内嵌函数的参数,接受的是原函数add的参数,这也是永远不变的,套路,固定写法;里面的函数传的才是参数
        result = func(a,b)
        with open("add.txt",'a',encoding='utf-8') as f:
            f.write(str(result)+"\n")   # 换行写入,要在写入的内容后面 加 +"\n"
            f.close()
    return wrapper

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


add(5,8)
add(3,5)
add(1,1)

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值