高级 Python:函数

照片由 <a href=“https://unsplash.com/@iam_os?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText”>iam_os</a> 在 <a href=“https://unsplash.com/photos/jst9zmbCD58?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText”>Unsplash</a> 上拍摄
伊利亚·拉扎列维奇·

一、说明

        读完标题后,你可能会问自己一些类似的事情,“Python 中的函数是一个高级概念?如何?所有课程都引入了功能作为语言的基本块。你既是对的,也是错的。

        大多数关于 Python 的课程都将函数作为基本概念和构建块进行介绍,因为没有它们,您将根本无法编写函数式代码。这与函数式编程范式完全不同,函数式编程范式是一个单独的概念,但我也将触及这个概念。

        在我们深入研究 Python 函数的高级复杂性之前,让我们简要回顾一些基本概念和您可能已经知道的事情。

二、简要基础知识

        所以你开始编写你的程序,在某个时候你最终会写出相同的代码序列。你开始重复自己和代码块。事实证明,这是引入功能的好时机和地点。至少,是这样。在 Python 中,将函数定义为:

def shout(name):
    print(f'Hey! My name is {name}.')

在软件工程领域,我们区分了功能定义的各个部分:

  • def- 用于定义函数的 Python 关键字。
  • shout- 函数名称。
  • shout(name)- 函数声明。
  • name- 函数参数。
  • print(...)是函数体的一部分,或者我们如何称呼它为函数定义。

        一个函数可以返回一个值,也可以根本没有返回值,就像我们之前定义的那个。当函数返回值时,它可以返回一个或多个:

def break_sentence(sentence):
    return sentence.split(' ')

        结果是一个元组,您可以解压缩或选择任何元组元素来继续。

        对于那些不知情的人来说,Python 中的函数是一等公民。这是什么意思?这意味着您可以像使用任何其他变量一样使用函数。您可以将它们作为参数传递给其他函数,从函数返回它们,甚至将它们存储在变量中。下面是其中一个示例:

def shout(name):
    return f'Hey! My name is {name}.'

# we will use break_sentence defined above

# assign function to another variable
another_breaker = break_sentence 

another_breaker(shout('John'))
# ['Hey!', 'My', 'name', 'is', 'John.']

# Woah! Yes, this is a valid way to define function
name_decorator = lambda x: '-'.join(list(name))

name_decorator('John')
# 'J-o-h-n'

        等等,这个 lambda 是什么?这是在 Python 中定义函数的另一种方式。这就是所谓的未命名或匿名函数。好吧,在这个例子中,我们将它分配给一个名为 name_decorator 的变量,但您可以将 lambda 表达式作为另一个函数的参数传递,而无需命名它。我将很快介绍这一点。

        剩下的就是给出一个示例,说明如何将函数作为参数传递或从另一个函数作为值返回。这是我们正在向先进概念迈进的部分,所以请耐心等待。

def dash_decorator(name):
    return '-'.join(list(name))

def no_decorator(name):
    return name

def shout(name, decorator=no_decorator):
    decorated_name = decorator(name)
    return f'Hey! My name is {decorated_name}'

shout('John')
# 'Hey! My name is John'

shout('John', decorator=dash_decorator)
# 'Hey! My name is J-o-h-n'

        这就是将lambda函数作为参数传递给另一个函数的样子。功能呢?好吧,看看下面的例子:

def shout(name, decorator=lambda x: x):
    decorated_name = decorator(name)
    return f'Hey! My name is {decorated_name}'

print(shout('John'))
# Hey! My name is John

print(shout('John', decorator=dash_decorator))
# Hey! My name is J-o-h-n

        现在,默认的装饰函数是 lambda并按原样返回参数的值(幂等)。在这里,它是匿名的,因为它没有附加名称。

        请注意, print 也是一个函数,我们在其中传递一个函数作为参数。本质上,我们是链接函数。这可以引导我们走向函数式编程范式,这是你可以在 Python 中选择的路径。我将尝试专门针对这个主题写另一篇博文,因为它对我来说非常有趣。现在,我们将保持过程式编程范例;也就是说,我们将继续我们迄今为止所做的事情。

        如前所述,函数可以分配给变量,作为参数传递给另一个函数,并从该函数返回。我已经向您展示了前两种情况的一些简单示例,但是从函数返回函数怎么样?起初我想让它非常简单,但话又说回来,这是一个高级 Python!

三、中级或高级零件

        这绝不是 Python 中函数和函数高级概念的指南。有很多很棒的材料,我将在这篇文章的最后留下。但是,我想谈谈我发现非常有趣的几个有趣的方面。

        Python 中的函数是对象。我们怎样才能弄清楚这一点? Python 中的每个对象都是一个类的实例,该类最终继承自一个称为 type 的特定类。其细节很复杂,但为了能够了解这与函数有什么关系,这里有一个示例:

type(shout)
# function

type(type(shout))
# type

        当您在 Python 中定义一个类时,它会自动继承该类。继承哪个类?objectobject

type(object)
# type

        我应该告诉你 Python 中的类也是对象吗?事实上,这对初学者来说是令人难以置信的。但正如吴恩达所说,这并不那么重要;别担心。

        好的,所以函数是对象。当然,函数应该有一些神奇的方法,对吧?

shout.__class__
# function

shout.__name__
# shout

shout.__call__
# <method-wrapper '__call__' of function object at 0x10d8b69e0>
# Oh snap!

         魔术方法 __call__ 是为可调用的对象定义的。所以我们的shout对象(函数)是可调用的。我们可以带或不带参数调用它。但这很有趣。我们之前所做的是定义一个喊叫函数,并获取一个可以使用 __call__ 魔术方法调用的对象,该方法是一个函数。你看过电影《盗梦空间》吗?

        所以,我们的函数实际上并不是一个函数,而是一个对象。对象是类的实例并包含方法和属性,对吗?这是您应该从 OOP 中了解的内容。我们怎样才能知道对象的属性是什么?有一个名为 vars 的 Python 函数,它返回对象属性及其值的字典。让我们看看下一个示例中会发生什么:

 

vars(shout)
# {}

shout.name = 'Jimmy'

vars(shout)
# {'name': 'Jimmy'}

        这很有趣。并不是说你可以马上弄清楚它的用例。即使你能找到它,我也强烈建议你不要使用这种黑魔法。这并不容易遵循,尽管它是一个有趣的弯曲。我之所以向你展示这一点,是因为我们想证明函数确实是对象。请记住,Python 中的所有内容都是一个对象。这就是我们引入 Python 的方式。

        现在,期待已久的功能又回来了。这个概念也非常有趣,因为它为您提供了很多实用性。只要有一点点句法糖,你就会变得非常有表现力。让我们开始吧。

        首先,一个函数的定义可以包含另一个函数的定义。甚至不止一个。这里有一个很好的例子:

def shout(name):
    def _upper_case(s):
        return s.upper()

    return _upper_case(name)

        如果你认为这只是一个错综复杂的版本name.upper(),你是对的。但是等等,我们正在到达那里。        

        因此,鉴于前面的示例是功能齐全的 Python 代码,您可以尝试在函数内定义的多个函数。这个巧妙的技巧有什么价值?好吧,您可能会遇到这样的情况:您的函数很大,并且包含重复的代码块。这样,定义子函数将增加可读性。在实践中,巨大的函数是代码味道的标志,强烈建议将它们分成几个较小的函数。因此,遵循这个建议,您将很少需要在彼此内部定义多个函数。需要注意的一件事是 _upper_case 函数是隐藏的,并且在最终定义并可供调用的shout函数的范围内无法访问。这样,您就无法轻松测试它,这是这种方法的另一个问题。

        然而,在一种特定情况下,在另一个函数中定义一个函数是一种可行的方法。这是当你实现函数的装饰器时。这与我们在前面的示例之一中用来修饰名称字符串的函数无关。

四、Python 中的装饰器函数

        什么是装饰器函数?可以把它看作是包装函数的函数。这样做的目的是为现有函数引入其他功能。例如,假设您希望在每次调用函数时进行记录:

def my_function():
    return sum(range(10))

def my_logger(fun):
    print(f'{fun.__name__} is being called!')
    return fun

my_function()
# 45

my_logger(my_function)
# my_function is being called!
# <function my_function at 0x105afbeb0>

my_logger(my_function)()
# my_function is being called!
# 45

        注意我们如何装饰我们的功能;我们将其作为参数传递给装饰参数。但这还不够!请记住,装饰器返回函数,并且需要调用(调用)此函数。这是最后一次调用的作用。

        现在,在实践中,您真正想要的是装饰保留在原始函数的名称下。在我们的例子中,我们希望在解释器解析我们的代码之后,是修饰函数的名称。这样一来,我们就能让事情变得简单易懂,并且我们确保代码的任何部分都无法调用函数的未修饰版本。例:my_function

def my_function():
    return sum(range(10))

def my_logger(fun):
    print(f'{fun.__name__} is being called!')
    return fun

my_function = my_logger(my_function)

my_function(10)
# my_function is being called!
# 45

        您会承认,我们将函数名称重新分配给装饰名称的部分很麻烦。你必须牢记这一点。如果要记录许多函数调用,则会有很多重复代码。这就是句法糖的用武之地。定义修饰器函数后,可以使用它来修饰另一个函数,方法是在函数定义前面加上修饰器函数的名称。例:@

def my_logger(fun):
    print(f'{fun.__name__} is being called!')
    return fun

@my_logger
def my_function():
    return sum(range(10))

my_function()
# my_function is being called!
# 45

        这是 Python 的禅宗。看看代码的表现力和简单性。

        这里有一件重要的事情需要注意!尽管输出有意义,但这不是您所期望的!在加载 Python 代码时,解释器将调用该函数并有效地运行它!您将获得日志输出,但这不会是我们首先想要的。现在看代码:my_logger

def my_logger(fun):
    print(f'{fun.__name__} is being called!')
    return fun

@my_logger
def my_function():
    return sum(range(10))

my_function()
# my_function is being called!
# 45
my_function()
# 45

        为了能够在调用原始函数后运行装饰器代码,我们必须将其包装在另一个函数周围。这就是事情可能会变得混乱的地方。下面是一个示例:

def my_logger(fun):
    def _inner_decorator(*args, **kwargs):
        print(f'{fun.__name__} is being called!')
        return fun(*args, **kwargs)

    return _inner_decorator

@my_logger
def my_function(n):
    return sum(range(n))

print(my_function(5))
# my_function is being called!
# 10

        在此示例中,也有一些更新,因此让我们回顾一下它们:

  1. 我们希望能够将参数传递给 my_function。
  2. 我们希望能够装饰任何函数,而不仅仅是 my_function。因为我们不知道未来函数的参数的确切数量,所以我们必须尽可能保持通用性,这就是我们使用 *args 和 **kwargs 的原因。
  3. 最重要的是,我们定义了 _inner_decorator,每次在代码中调用 my_function 时都会调用它。它接受位置参数和关键字参数,并将它们作为参数传递给修饰函数。

        始终记住,装饰器函数必须返回一个函数,该函数接受相同的参数(数字及其各自的类型)并返回相同的输出(同样,数字及其各自的类型)。也就是说,如果你想让函数用户不感到困惑,代码阅读器不试图弄清楚到底发生了什么。

        例如,假设您有两个结果不同的函数,但也需要参数:

@my_logger
def my_function(n):
    return sum(range(n))

@my_logger
def my_unordinary_function(n, m):
    return sum(range(n)) + m

print(my_function(5))
# my_function is being called!
# 10

print(my_unordinary_function(5, 1))
# my_unordinary_function is being called!
# 11

        在我们的示例中,装饰器函数只接受它装饰的函数。但是,如果要传递其他参数并动态更改装饰器行为,该怎么办?假设您要调整记录器装饰器的详细程度。到目前为止,我们的装饰器函数已经接受了一个参数:它装饰的函数。但是,当装饰器函数有自己的参数时,这些参数将首先传递给它。然后,装饰器函数必须返回一个接受修饰函数的函数。从本质上讲,事情变得越来越复杂。还记得电影《盗梦空间》的参考资料吗?

        下面是一个示例:

from enum import IntEnum, auto
from datetime import datetime
from functools import wraps

class LogVerbosity(IntEnum):
    ZERO = auto()
    LOW = auto()
    MEDIUM = auto()
    HIGH = auto()

def my_logger(verbosity: LogVerbosity):

    def _inner_logger(fun):

        def _inner_decorator(*args, **kwargs):
            if verbosity >= LogVerbosity.LOW:
                print(f'LOG: Verbosity level: {verbosity}')
                print(f'LOG: {fun.__name__} is being called!')
            if verbosity >= LogVerbosity.MEDIUM:
                print(f'LOG: Date and time of call is {datetime.utcnow()}.')
            if verbosity == LogVerbosity.HIGH:
                print(f'LOG: Scope of the caller is {__name__}.')
                print(f'LOG: Arguments are {args}, {kwargs}')

            return fun(*args, **kwargs)

        return _inner_decorator

    return _inner_logger

@my_logger(verbosity=LogVerbosity.LOW)
def my_function(n):
    return sum(range(n))

@my_logger(verbosity=LogVerbosity.HIGH)
def my_unordinary_function(n, m):
    return sum(range(n)) + m

print(my_function(10))
# LOG: Verbosity level: LOW
# LOG: my_function is being called!
# 45

print(my_unordinary_function(5, 1))
# LOG: Verbosity level: HIGH
# LOG: my_unordinary_function is being called!
# LOG: Date and time of call is 2023-07-25 19:09:15.954603.
# LOG: Scope of the caller is __main__.
# LOG: Arguments are (5, 1), {}
# 11 

    

        我不会详细描述与装饰器无关的代码,但我鼓励您查找并学习。这里我们有一个装饰器,以不同的详细程度记录函数调用。如前所述,my_logger 装饰器现在接受动态更改其行为的参数。将参数传递给它后,它返回的结果函数应该接受一个要装饰的函数。这是 _inner_logger 函数。现在,您应该了解装饰器代码的其余部分在做什么。

五、结论

        我写这篇文章的第一个想法是写一些高级主题,比如 Python 中的装饰器。但是,正如您现在可能知道的那样,我也提到并使用了许多其他高级主题。在以后的文章中,我将在一定程度上解决其中的一些问题。尽管如此,我对你的建议是,也要从其他来源了解这里提到的事情。如果你正在使用任何编程语言进行开发,掌握这些函数是必须的,但掌握你选择的编程语言的所有方面可以让你在编写代码方面有很大的优势。

        我希望我已经为你介绍了一些新的东西,并且你现在对作为高级 Python 程序员编写函数充满信心。

六、引用

  • Python 装饰器入门
  • Python 内部函数:它们有什么用?
  • 枚举 HOWTO

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/115028.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

银行账单转换beancount

用了beancount来记账后&#xff0c;发现每月的账单手动记是一件极其麻烦的事情。 然后再github搜索一通后&#xff0c;有double-entry-generator&#xff08;https://github.com/deb-sig/double-entry-generator&#xff09;能转换支付宝/微信的账单&#xff0c;但是没有自己用…

Prometheus+Node_exporter+Grafana实现监控主机

PrometheusNode_exporterGrafana实现监控主机 如果没有安装相关的配置&#xff0c;首先要进行安装配置&#xff0c;环境是基于Linux&#xff0c;虚拟机的相关环境配置在文末给出&#xff0c;现在先讲解PrometheusNode_exporterGrafana的安装和使用。 一.Prometheus安装 虽然…

vue-浏览器安装Vue开发者工具

极简插件&#xff1a;下载->开发者模式->拖曳安装->插件详情允许访问文件 网址&#xff1a;https://chrome.zzzmh.cn/index 搜索Vue Devtools 下载下来的安装包先解压 然后点击chrome浏览器的右上角三个点的按钮在里面找到扩展程序这个选项&#xff0c;然后点进去管理…

我在Vscode学OpenCV 基本的加法运算

根据上一篇我们可知__图像的属性 链接&#xff1a;《我在Vscode学OpenCV 处理图像》 属性— API 形状 img.shape 图像大小 img.size 数据类型 img.dtype  shape&#xff1a;如果是彩色图像&#xff0c;则返回包含行数、列数、通道数的数组&#xff1b;如果是二值图像或者灰度…

Java高级互联网架构师之路:垃圾回收器的介绍

本文重点 从本文开始我们将开启垃圾回收器的介绍了,我们知道垃圾回收算法是逻辑改变,而垃圾回收器是具体的实现。我们前面介绍的垃圾回收器有7个,本文将在添加三个,但是这三个目前来看不是很常用,我们只了解一下,我们主要还是讲解这7个垃圾回收器。 十个垃圾回收器 目…

【漏洞库】XXL-JOB 默认accessToken权限绕过导致RCE

文章目录 漏洞描述漏洞编号漏洞评级影响版本漏洞复现- EXP 编写 漏洞挖掘修复建议 漏洞描述 XXL-JOB 是一款开源的分布式任务调度平台&#xff0c;用于实现大规模任务的调度和执行。 XXL-JOB 默认配置下&#xff0c;用于调度通讯的 accessToken 不是随机生成的&#xff0c;而…

如何记录每天的工作日程?电脑手机通用的日程管理软件

在工作时间有限&#xff0c;但工作任务愈加繁多的现在职场中&#xff0c;要求每一个职场人士做好高效日程管理。通过高效管理日程&#xff0c;我们可以更好地组织和安排任务&#xff0c;合理分配时间和优先级&#xff0c;这有助于我们更专注地进行工作&#xff0c;减少时间的浪…

手写操作系统篇:实现裸机应用程序

文章目录 前言操作系统执行环境创建裸机平台项目Rust的Core库移除标准库依赖Qemu 启动流程内存布局编译流程内核的初始指令调整内核的内存布局手动加载内核可执行文件使用RustSBI提供的服务添加bootloader模块添加Makefile运行停止总体架构 前言 我们既然是手写操作系统&#…

行业追踪,2023-11-03

自动复盘 2023-11-03 凡所有相&#xff0c;皆是虚妄。若见诸相非相&#xff0c;即见如来。 k 线图是最好的老师&#xff0c;每天持续发布板块的rps排名&#xff0c;追踪板块&#xff0c;板块来开仓&#xff0c;板块去清仓&#xff0c;丢弃自以为是的想法&#xff0c;板块去留让…

学习小结,学而时习之,坚持学习之,温顾学习之

学习python一个多月了&#xff0c;之前也有接触过&#xff0c;还花了不少钱报班&#xff0c;看了看入门的头两节课&#xff0c;就止步了。每一种编程语言的入门感觉都差不多&#xff0c;学到现在&#xff0c;我对python的基本数据类型还是没掌握好啊&#xff0c;每次列表字典怎…

js 根据word文档模板导出内容

一、创建word导出模板 1、本地创建一个test.docx 2、将最终需要的文档内容及样式编辑完成(图1) 3、将所需动态值的位置,替换为变量参数(图2) 注: 动态值书写 图1 图2 模板值的书写要求 二、项目中使用 1、安装依赖 npm install docxtemplater-image-module-free --save n…

4+m6A+机器学习+分型,要素过多,没有思路的同学可借鉴

今天给同学们分享一篇生信文章“Diagnostic, clustering, and immune cell infiltration analysis of m6A regulators in patients with sepsis”&#xff0c;这篇文章发表在Sci Rep.期刊上&#xff0c;影响因子为4.6。 结果解读&#xff1a; 脓毒症中m6A调节因子的转录改变 …

[PyTorch][chapter 61][强化学习-免模型学习1]

前言&#xff1a; 在现实的学习任务中&#xff0c;环境 其中的转移概率P,奖赏函数R 是未知的&#xff0c;或者状态X也是未知的 称为免模型学习&#xff08;model-free learning&#xff09; 目录&#xff1a; 1: 蒙特卡洛强化学习 2&#xff1a;同策略-蒙特卡洛强化学习 3&am…

非关系型数据库Redis的安装【Linux】及常用命令

前言 Redis&#xff08;Remote Dictionary Server&#xff09;是一种开源的内存数据库管理系统&#xff0c;它以键值存储方式来存储数据&#xff0c;并且支持多种数据结构&#xff0c;如字符串、哈希、列表、集合、有序集合等。Redis最初由Salvatore Sanfilippo开发&#xff0c…

Java配置47-Spring Eureka 未授权访问漏洞修复

文章目录 1. 背景2. 方法2.1 Eureka Server 添加安全组件2.2 Eureka Server 添加参数2.3 重启 Eureka Server2.4 Eureka Server 升级版本2.5 Eureka Client 配置2.6 Eureka Server 添加代码2.7 其他问题 1. 背景 项目组使用的 Spring Boot 比较老&#xff0c;是 1.5.4.RELEASE…

React使用富文本CKEditor 5,上传图片并可设置大小

上传图片 基础使用&#xff08;标题、粗体、斜体、超链接、缩进段落、有序无序、上传图片&#xff09; 官网查看&#xff1a;https://ckeditor.com/docs/ckeditor5/latest/installation/integrations/react.html 安装依赖 npm install --save ckeditor/ckeditor5-react cked…

【window系统】win10家庭版没有hyper-V的选项 问题解决办法

目录 事件起因环境和工具操作过程参考内容结束语 事件起因 在给我的新电脑安装docker和对应的Linux环境时 在 控制面版-程序-启用该或关闭Windows功能 时 没有找到对应网上那种教程里的Hyper-V的功能的选项&#xff0c;查找了一下&#xff0c;说是win10家庭版没有这个功能 专…

速学数据结构 | 循环队列怎么写才最高效?只需要掌握这些技巧

&#x1f3ac; 鸽芷咕&#xff1a;个人主页 &#x1f525; 个人专栏:《Linux深造日志》《C干货基地》 ⛺️生活的理想&#xff0c;就是为了理想的生活! 文章目录 &#x1f4cb; 前言一、什么是循环队列&#xff1f;二、如何实现循环队列&#xff1f;2.1 循环队列的结构2.2 循环…

SpringBoot集成MyBatis-Plus

SpringBoot集成MyBatis-Plus 文章目录 SpringBoot集成MyBatis-Plusapplication.ymlpom.xmlpojomapperserviceserviceimplconfigutilsweb 懒得打一遍&#xff0c;直接copy&#xff1a; SpringBoot集成MyBatis-Plus application.yml # 端口 server:port: 8080 # 数据源 spring:…

期中成绩怎么发布?

作为一名老师&#xff0c;期中考试结束后&#xff0c;你可能正在为如何发布成绩而烦恼。传统的纸质方式不仅耗时而且容易出错&#xff0c;那么有没有一种方式可以让学生自助查询成绩呢&#xff1f;答案是肯定的。下面就为你介绍几种实用的方法&#xff0c;让成绩发布变得轻松又…