首页 > 其他分享 >函数高级玩法

函数高级玩法

时间:2024-05-24 19:17:55浏览次数:18  
标签:outer 函数 作用域 玩法 高级 func print def

分享几个比较重要的知识点,web框架中大量的运用了,所以我给摘出来啦!

  • 函数的嵌套
  • 闭包
  • 装饰器

1.函数嵌套

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

name = "隔壁老王"
print(name )

def func():
    print(name )

func()

1.函数在作用域中

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

# 1. 在全局作用域定义了函数func
def func():
    print("你好")

# 2.在全局作用域找到func函数并执行。
func()

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

# 4.在全局作用域执行send函数
    send()

上两个实例,更容易理解

def func():
    print("你好")
    
func()

def send():
    print("开始")
    func()
    print("结束")
    
send()

def func():
    print(666)
    
func()

def func():
    print("你好")
    
func()           # 你好

def send():
    print("开始")
    func()
    print("结束")

def func():      # 会覆盖原来的func()函数,因为这个函数名和刚开始的函数名重名了
    print(666)

func()             # 666
send()
# 开始
# 666
# 结束
 

1.2函数定义的位置

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

def func():
    print("111")
    
def handler():
    print("222")
    def inner():
        print("333")
	inner()
    func()
    print("444")

handler()

只要理解数据定义时所存在的作用域,并根据从上到下代码执行过程进行分析,再怎么嵌套都可以搞定。

现在的你可能有疑问:为什么要这么嵌套定义?把函数都定义在全局不好吗?

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

def f1():
    pass

def f2():
    pass

def func():
	f1()
    f2()
def func():
    def f1():
        pass

    def f2():
        pass
	f1()
    f2()

1.3 嵌套引发的作用域问题

基于内存和执行过程分析作用域

name = "eric"

def run():
    name = "job"
    def inner():
        print(name)
	inner()
    
run()

**执行函数,内部会生成一个调用栈,理解成会创建一个作用域 **

name = "eric"

def run():
    name = "job"
    def inner():
        print(name)
	return inner
    
v1 = run()
v1()

v2 = run()
v2()

三句话搞定作用域:

  • 优先在自己的作用域找,自己没有就去上级作用域。
  • 在作用域中寻找值时,要确保此次此刻值是什么。
  • 分析函数的执行,并确定函数作用域链。(函数嵌套)

2.闭包

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

应用场景:

  • 1.封装到一个包中,供多个函数使用
  • 2.封装到一个个独立的包中,供自己调用

闭包应用场景1:封装数据防止污染全局。(有的函数不需要,只给需要的函数提供)

name = "eric"

def f1():
    print(name, age)

def f2():
	print(name, age)

def f3():
	print(name, age)
    
def f4():
    pass
def func(age):
    name = "eric"

    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()

不多说,直接上案例

""" 基于多线程去下载视频 """
from concurrent.futures.thread import ThreadPoolExecutor

import requests


def download_video(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


def outer(file_name):
    def write_file(response):  # 上面的res.content会自动传递给response
        content = response.result()  # 要拿取到里面的值必须要【.result】
        with open(file_name, mode='wb') as file_object: 
            file_object.write(content)

    return write_file


POOL = ThreadPoolExecutor(10)  # 创建线程池
	
video_dict = [
    ("东北F4模仿秀.mp4", "https://aweme.snssdk.com/aweme/v1/playwm/?video_id=v0300f570000bvbmace0gvch7lo53oog"),
    ("卡特扣篮.mp4", "https://aweme.snssdk.com/aweme/v1/playwm/?video_id=v0200f3e0000bv52fpn5t6p007e34q1g"),
]
for item in video_dict:
    future = POOL.submit(download_video, url=item[1]) #  执行download_video方法,并传递参数url
    # outer(item[0]) 这里涉及到闭包的知识点 
    future.add_done_callback(outer(item[0]))  # 上面执行完之后立刻执行括号中的方法【write_file函数】

POOL.shutdown()

3.装饰器

实现原理:基于@语法和闭包,将原函数封装到内部,并将函数赋值成新的函数【内层函数】,当你再调用该函数的时候,执行函数时在内层函数中执行闭包中封装的原函数

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

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

先提个小需求,在不改变源码之前,实现一个函数执行之前和执行之后分别输入"before"和"after"

对于刚刚入门的我,会这么写

def func():
    print("before")
    print("我是func函数")
    value = [11, 22, 33]
    print("before")
    return value

result = func()

对于我来说,我会这样做

def outer(origin):
    def inner():
        print("before")
        res = origin()
        print("after")
    return inner

def func():
    print("我是func函数")
    value = [11, 22, 33, 44]
    return value

res = outer(func)
res()

如果涉及多个地方要同时在操作前和操作后进行分别输出

"""
由于代码是从上往下执行的,那么装饰器那里 eg: func1 = outer(func1) 
此时,func1已经是inner函数,那么此时func1()是执行的inner函数,

"""
def outer(origin):
    def inner():
        print("before")
        res = origin()
        print("after")
        return res

    return inner


@outer  # func1 = outer(func1)
def func1():  
    print("我是func1函数")
    value = [11, 22, 33]
    return value


@outer  # func2 = outer(func2)
def func2():
    print("我是func2函数")
    value = [11, 22, 33]
    return value



@outer # func3 = outer(func3)
def func3():
    print("我是func3函数")
    value = [11, 22, 33]
    return value

func1()
func2()
func3()

下面,是我对装饰器进行了优化【函数带参数】

def outer(origin):
    def inner(*args,**kwargs):
        print("before")
        res = origin(*args,**kwargs)
        print("after")
        return res

    return inner


@outer  # func1 = outer(func1)
def func1(a1):
    print("我是func1函数")
    value = [11, 22, 33]
    return value


@outer  # func2 = outer(func2)
def func2(a2, a3):
    print("我是func2函数")
    value = [11, 22, 33]
    return value


@outer  # func3 = outer(func3)
def func3(a4):
    print("我是func3函数")
    value = [11, 22, 33]
    return value


func1(1)
func2(2, a3=4)
func3(5)

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

好了,装饰器示例


def outer(origin):
    def inner(*args, **kwargs):
        # 执行前
        res = origin(*args, **kwargs)
        # 执行后

    return inner

@outer
def func():
    pass

func()

但是,上面的装饰器只能够解决百分之99的问题,下面介绍一个模块functools
加与不加的区别在于原函数.__name原函数.__doc

#  先认识不加的

def outer(origin):
    def inner(*args, **kwargs):
        """我是小淘气"""
        # 执行前
        res = origin(*args, **kwargs)
        return res
        # 执行后

    return inner


@outer
def func():
    """我是图图"""
    value = [11, 22]
    return value


print(func.__name__)  # 目前func函数 -> inner函数 所以会打印inner函数的名称【inner】  
print(func.__doc__)   # 那么这么会打印inner函数的注释    【我是小淘气】
# 再认识加的
import functools

def outer(origin):
    @functools.wraps(origin)
    def inner(*args, **kwargs):
        """我是小淘气"""
        # 执行前
        res = origin(*args, **kwargs)
        return res
        # 执行后

    return inner


@outer
def func():
    """我是图图"""
    value = [11, 22]
    return value


print(func.__name__)  # 不管目前func是什么函数,我就打印原来函数的名称【func】  
print(func.__doc__)   # 那么这么会打印func函数的注释    【我是图图】

因为其实,一般情况下不用functools也可以实现装饰器的基本功能,但在项目开发时,不加functools会出错(内部会读取__name__,且__name__重名的话就报错)
闲话不多说,上代码

  

标签:outer,函数,作用域,玩法,高级,func,print,def
From: https://www.cnblogs.com/pythonav/p/18202043

相关文章

  • 状态价值函数、策略价值函数和动作价值函数
    策略价值函数求解策略价值函数Vπ(s)V^\pi(......
  • Java面试进阶指南:高级知识点问答精粹(二)
    Java面试问题及答案1.什么是Java内存模型(JMM)?它在并发编程中扮演什么角色?答案:Java内存模型(JMM)是一个抽象的模型,它定义了Java程序中各种变量(线程共享变量)的访问规则,以及在并发环境下这些变量如何被不同线程所看到。JMM规定了主内存和工作内存的概念,以及它们之间的交互规......
  • Java面试进阶指南:高级知识点问答精粹(一)
    Java面试问题及答案1.什么是Java中的集合框架?它包含哪些主要接口?答案:Java集合框架是一个设计用来存储和操作大量数据的统一的架构。它提供了一套标准的接口和类,使得我们可以以一种统一的方式来处理数据集合。集合框架主要包含以下接口:Collection:最基本的集合接口,它是......
  • Java高级面试精粹:问题与解答集锦(二)
    Java面试问题及答案1.什么是Java内存模型(JMM)?它的作用是什么?答案:Java内存模型(JMM)定义了Java虚拟机(JVM)在计算机内存中的工作方式,包括程序计数器、Java堆、方法区、栈和本地方法栈等。JMM的主要作用是为编写线程安全的程序提供规范,确保在多线程环境下,不同线程对共享变量的......
  • Java高级面试精粹:问题与解答集锦(一)
    Java面试问题及答案1.什么是Java中的多态,它是如何实现的?答案:多态是Java中的一个核心概念,它允许不同类的对象对同一消息做出响应,但具体的行为会根据对象的实际类型而有所不同。多态主要通过以下两种方式实现:重载(Overloading):当多个方法具有相同的名称,但参数列表不同时,......
  • 拷贝构造函数
    简介是一个特殊的构造函数,只有一个形参,该形参常用const修饰,是对该类型的引用。当定义一个新的对象并用一个同类型的对象对它进行初始化时,用显式拷贝构造函数。当该类型的对象传递给函数或从函数返回类型的对象时,用隐式拷贝构造函数。拷贝构造函数拷贝构造函数通常用于:1、通......
  • 【C语言】字符函数和字符串函数
    在C语言中,字符函数和字符串函数通常用于处理单个字符和字符串。这些函数定义在<ctype.h>(字符函数)和<string.h>(字符串函数)头文件中。以下是一些常用的字符函数和字符串函数及其用法:字符函数(ctype.h)字符函数主要用于测试字符的属性或转换字符。isalnum():检查字符是......
  • 窗口函数 | rows between …… and ……
    ROWSBETWEEN...AND... 是SQL窗口函数中的一个子句,用于定义窗口函数操作的行范围。窗口函数允许用户对一组相关的记录执行计算,这些记录被称为窗口。基本语法<窗口函数>OVER([PARTITIONBY<列名>]ORDERBY<列名>[ASC|DESC][ROWSBETWEEN......
  • js函数柯里化
    JavaScript函数柯里化详解 更新时间:2022年01月14日15:33:47 作者:天界程序员  这篇文章主要为大家介绍了JavaScript函数柯里化,具有一定的参考价值,感兴趣的小伙伴们可以参考一下,希望能够给你带来帮助 −目录一、简单了解apply和call二、什么是函数柯里化?三、写......
  • lua打印调用的函数文件及行数
    lua根据调用堆栈可以打印调谁调用了我 string.split=function(s,delim)localsplit={}localpattern="[^"..delim.."]+"string.gsub(s,pattern,function(v)table.insert(split,v)end)returnsplitendfunctiongetWhoCallsMe()......