python高级函数

python高级函数

1.函数嵌套

Python中以函数为作用域,在作用域中定义的相关数据只能被当前作用域或子作用域使用。

name = "达莱"
print(name)


def func():
    print(name)


func()

1.1 函数在作用域中

其实,函数也是定义在作用域中的数据,在执行函数时候,也同样遵循:优先在自己作用域中寻找,没有则向上一接作用域寻找,例如:

# 1. 在全局作用域定义了函数func
def func():
    print("你好")
    
# 2. 在全局作用域找到func函数并执行。
func()


# 3.在全局作用域定义了execute函数
def execute():
    print("开始")
    # 优先在当前函数作用域找func函数,没有则向上级作用域中寻找。
    func()
    print("结束")

# 4.在全局作用域执行execute函数
execute()    #  你好 开始 你好 结束
  • 易错点:作用域中的值在被调用时到底是什么?

    示例1:

    def func():
        print("你好")
        
    func()
    
    def execute():
        print("开始")
        func()
        print("结束")
        
    execute()
    
    def func():
        print(666)
        
    func() # 666
    
  • 情景2

    def func():
        print("你好")
        
    func()
    
    def execute():
        print("开始")
        func()
        print("结束")
    
    def func():
        print(666)
    
    func()
    execute()  # 你好  666 开始  666  结束
    

    1.2 函数定义的位置

    上述示例中的函数均定义在全局作用域,其实函数也可以定义在局部作用域,这样函数被局部作用域和其子作用于中调用(函数的嵌套)。

    def func():
        print("沙河高晓松")
        
    def handler():
        print("昌平吴彦祖")
        def inner():
            print("朝阳大妈")
    	inner()
        func()
        print("海淀网友")
    
    handler()  #昌平吴彦祖   沙河高晓松   海淀网友
    

    大多数情况下我们都会将函数定义在全局,不会嵌套着定义函数。不过,当我们定义一个函数去实现某功能,想要将内部功能拆分成N个函数,又担心这个N个函数放在全局会与其他函数名冲突时(尤其多人协同开发)可以选择使用函数的嵌套。

    def f1():
        pass
    
    def f2():
        pass
    
    def func():
    	f1()
        f2()
    
    def func():
        def f1():
            pass
    
        def f2():
            pass
    	f1()
        f2()
    
    # 示例代码
    """
    生成图片验证码的示例代码,需要提前安装pillow模块(Python中操作图片中一个第三方模块)
    	pip3 install pillow
    """
    import random
    from PIL import Image, ImageDraw, ImageFont
    
    
    def create_image_code(img_file_path, text=None, size=(120, 30), mode="RGB", bg_color=(255, 255, 255)):
        """ 生成一个图片验证码 """
        _letter_cases = "abcdefghjkmnpqrstuvwxy"  # 小写字母,去除可能干扰的i,l,o,z
        _upper_cases = _letter_cases.upper()  # 大写字母
        _numbers = ''.join(map(str, range(3, 10)))  # 数字
        chars = ''.join((_letter_cases, _upper_cases, _numbers))
    
        width, height = size  # 宽高
        # 创建图形
        img = Image.new(mode, size, bg_color)
        draw = ImageDraw.Draw(img)  # 创建画笔
    
        def get_chars():
            """生成给定长度的字符串,返回列表格式"""
            return random.sample(chars, 4)
    
        def create_lines():
            """绘制干扰线"""
            line_num = random.randint(*(1, 2))  # 干扰线条数
    
            for i in range(line_num):
                # 起始点
                begin = (random.randint(0, size[0]), random.randint(0, size[1]))
                # 结束点
                end = (random.randint(0, size[0]), random.randint(0, size[1]))
                draw.line([begin, end], fill=(0, 0, 0))
    
        def create_points():
            """绘制干扰点"""
            chance = min(100, max(0, int(2)))  # 大小限制在[0, 100]
    
            for w in range(width):
                for h in range(height):
                    tmp = random.randint(0, 100)
                    if tmp > 100 - chance:
                        draw.point((w, h), fill=(0, 0, 0))
    
        def create_code():
            """绘制验证码字符"""
            if text:
                code_string = text
            else:
                char_list = get_chars()
                code_string = ''.join(char_list)  # 每个字符前后以空格隔开
    
            # Win系统字体
            # font = ImageFont.truetype(r"C:\Windows\Fonts\SEGOEPR.TTF", size=24)
            # Mac系统字体
            # font = ImageFont.truetype("/System/Library/Fonts/SFNSRounded.ttf", size=24)
            # 项目字体文件
            font = ImageFont.truetype("MSYH.TTC", size=15)
            draw.text([0, 0], code_string, "red", font=font)
            return code_string
    
        create_lines()
        create_points()
        code = create_code()
    
        # 将图片写入到文件
        with open(img_file_path, mode='wb') as img_object:
            img.save(img_object)
        return code
    
    
    code = create_image_code("a2.png")
    print(code)
    

    1.3嵌套引发的作用域问题

    • 基于内存和执行过程分析作用域。
    name = "达莱"
    
    
    def run():
        name = "查苏娜"
    
        def inner():
            print(name)
    
        inner()
    
    
    run()
    
    name = "达莱"
    
    
    def func():
        name = "查苏娜"
    
        def inner():
            print(name)
    
        return inner
    
    
    v1 = func()
    v1()
    
    name = "达莱"
    
    
    def run():
        name = "查苏娜"
    
        def inner():
            print(name)
        return [inner, inner, inner]
    
    
    func_list = run()
    func_list[2]()
    func_list[1]()
    
    funcs = run()
    funcs[2]()
    funcs[1]()
    

    三句话搞定作用域:

    • 优先在自己的作用域找,自己没有就去上级作用域。

    • 在作用域中寻找值时,要确保此次此刻值是什么。

    • 分析函数的执行,并确定函数作用域链。(函数嵌套)

    2.闭包

    闭包,简而言之就是将数据封装在一个包(区域)中,使用时再去里面取。(本质上 闭包是基于函数嵌套搞出来一个中特殊嵌套)

    • 闭包应用场景1:封装数据防止污染全局。

      name = "达莱"
      
      def f1():
          print(name, age)
      
      def f2():
      	print(name, age)
      
      def f3():
      	print(name, age)
          
      def f4():
          pass
      
      def func(age):
          name = "达莱"
      
          def f1():
              print(name, age)
      
          def f2():
              print(name, age)
      
          def f3():
              print(name, age)
      
          f1()
          f2()
          f3()
      
      func(123)
      
      • 闭包应用场景2:封装数据封到一个包里,使用时在取。
      def task(arg):
          def inner():
              print(arg)
          return inner
      
      v1 = task(11)
      v2 = task(22)
      v3 = task(33)
      v1()
      v2()
      v3()
      
      def task(arg):
          def inner():
              print(arg)
          return inner
      
      inner_func_list = []
      for val in [11,22,33]:
          inner_func_list.append( task(val) )
          
      inner_func_list[0]() # 11
      inner_func_list[1]() # 22
      inner_func_list[2]() # 33
      
      # 基于多线程去下载视频
      import requests
      from concurrent.futures.thread import ThreadPoolExecutor
      
      
      def task(url):
          res = requests.get(
              url=url,
              headers={
                  "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36 FS"
              }
          )
          return res.content
      
      
      # 下载完成之后,Python的多线程内部会执行的函数。
      def outer(file_name):
          def done(arg):
              # 视频内容
              content = arg.result()
              with open(file_name, mode='wb') as file_object:
                  file_object.write(content)
          return done
      
      
      # 线程池10个人
      POOL = ThreadPoolExecutor(10)
      
      # 三个视频信息
      video_list = [
          ("东北F4模仿秀.mp4", "https://aweme.snssdk.com/aweme/v1/playwm/?video_id=v0300f570000bvbmace0gvch7lo53oog"),
          ("卡特扣篮.mp4", "https://aweme.snssdk.com/aweme/v1/playwm/?video_id=v0200f3e0000bv52fpn5t6p007e34q1g"),
          ("罗斯mvp.mp4", "https://aweme.snssdk.com/aweme/v1/playwm/?video_id=v0200f240000buuer5aa4tij4gv6ajqg")
      ]
      for item in video_list:
          # 去线程池取一个人,让这个人执行去执行task函数(函数内部定义下载逻辑)
          future = POOL.submit(task, url=item[1])
          # 当执行完成task函数(下载完成)之后自动执行某个函数。
          future.add_done_callback( outer(item[0]) )
          print(item)
      

      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-25JCvgKd-1627713352245)(assets/image-20201229185549102-16276510687793-16276510712414.png)]

3.装饰器

现在给你一个函数,在不修改函数源码的前提下,实现在函数执行前和执行后分别输入 “before” 和 “after”。

def func():
    print("before")
    print("我是func函数")
    value = (11,22,33,44)
    print("after")
    return value
    
result = func()
print(result)
  • 装饰器实现

    def outer(arg):
        def inner():
            print("before")
            res = arg()
            print("after")
            print()
            return res
    
        return inner
    
    
    @outer
    def func1():
        print("达莱")
        value = [11, 22, 33]
        return value
    
    
    @outer
    def func2():
        print("达莱")
        value = [11, 22, 33]
        return value
    
    
    @outer
    def func3():
        print("达莱")
        value = [11, 22, 33]
        return value
    
    
    func1()
    func2()
    func3()
    

    装饰器,在不修改原函数内容的前提下,通过@函数可以实现在函数前后自定义执行一些功能(批量操作会更有意义)。

优化

  • 优化以支持多个参数的情况。
def outer(org):
    def inner(*args, **kwargs):
        res = org(*args, **kwargs)
        return res

    return inner


@outer
def func1(*args, **kwargs):
    print("达莱")
    value = [11, 22, 33]
    return value


@outer
def func2(*args, **kwargs):
    print("达莱")
    value = [11, 22, 33]
    return value


@outer
def func3(*args, **kwargs):
    print("达莱")
    value = [11, 22, 33]
    return value


func1({"大大大": 123}, ["查苏娜"])
func2({"大大大": 123}, ["查苏娜"])
func3({"大大大": 123}, ["查苏娜"])

装饰器实现原理

  • 实现原理:基于@语法和函数闭包,将原函数封装在闭包中,然后将函数赋值为一个新的函数(内层函数),执行函数时再在内层函数中执行闭包中的原函数。

  • 实现效果:可以在不改变原函数内部代码 和 调用方式的前提下,实现在函数执行和执行扩展功能。

  • 适用场景:多个函数系统统一在 执行前后自定义一些功能。

  • 装饰器示例

def outer(origin):
    def inner(*args, **kwargs):
		# 执行前
        res = origin(*args, **kwargs)  # 调用原来的func函数
        # 执行后
        return res
    return inner


@outer
def func():
    pass

func()
  • 基于装饰器实现的第三方模块Flask(框架)快速写一个网站:

    pip3.9 install flask
    
    from flask import Flask
    
    app = Flask(__name__)
    
    
    def auth(func):
        def inner(*args, **kwargs):
            # 在此处,判断如果用户是否已经登录,已登录则继续往下,未登录则自动跳转到登录页面。
            return func(*args, **kwargs)
    
        return inner
    
    
    @auth
    def index():w
        return "首页"
    
    
    @auth
    def info():
        return "用户中心"
    
    
    @auth
    def order():
        return "订单中心"
    
    
    def login():
        return "登录页面"
    
    
    app.add_url_rule("/index/", view_func=index, endpoint='index')
    app.add_url_rule("/info/", view_func=info, endpoint='info')
    app.add_url_rule("/order/", view_func=order, endpoint='order')
    app.add_url_rule("/login/", view_func=login, endpoint='login')
    
    app.run()
    

    重要补充:functools

    你会发现装饰器实际上就是将原函数更改为其他的函数,然后再此函数中再去调用原函数。

    import functools
    
    
    def outer(org):
        @functools.wraps(org)
        def inner(*args, **kwargs):
            print("你好")
            res = org(*args, **kwargs)
            print("查苏娜")
            return res
    
        return inner
    
    
    @outer
    def func():
        """这是一个func函数"""
        print("达莱")
    
    
    func()
    print(func.__doc__)
    print(func.__name__)
    

    一般情况下大家不用functools也可以实现装饰器的基本功能,但后期在项目开发时,不加functools会出错(内部会读取__name__,且__name__重名的话就报错),所以就要规范起来自己的写法。

import functools


def auth(func):
    @functools.wraps(func)
    def inner(*args, **kwargs):
        """注释"""
        res = func(*args, **kwargs)  # 执行原函数
        return res

    return inner

总结

  1. 函数可以定义在全局、也可以定义另外一个函数中(函数的嵌套)

  2. 学会分析函数执行的步骤(内存中作用域的管理)

  3. 闭包,基于函数的嵌套,可以将数据封装到一个包中,以后再去调用。

  4. 装饰器

    • 实现原理:基于@语法和函数闭包,将原函数封装在闭包中,然后将函数赋值为一个新的函数(内层函数),执行函数时再在内层函数中执行闭包中的原函数。

    • 实现效果:可以在不改变原函数内部代码 和 调用方式的前提下,实现在函数执行和执行扩展功能。

    • 适用场景:多个函数系统统一在 执行前后自定义一些功能。

    • 装饰器示例

import functools


def auth(func):
    @functools.wraps(func)
    def inner(*args, **kwargs):
        """注释"""
        res = func(*args, **kwargs)  # 执行原函数
        return res

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值