高级 Python:函数
一、说明
读完标题后,你可能会问自己一些类似的事情,“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 namedef 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 Johnprint(shout('John', decorator=dash_decorator))
# Hey! My name is J-o-h-n
现在,默认的装饰函数是 lambda并按原样返回参数的值(幂等)。在这里,它是匿名的,因为它没有附加名称。
请注意, print 也是一个函数,我们在其中传递一个函数作为参数。本质上,我们是链接函数。这可以引导我们走向函数式编程范式,这是你可以在 Python 中选择的路径。我将尝试专门针对这个主题写另一篇博文,因为它对我来说非常有趣。现在,我们将保持过程式编程范例;也就是说,我们将继续我们迄今为止所做的事情。
如前所述,函数可以分配给变量,作为参数传递给另一个函数,并从该函数返回。我已经向您展示了前两种情况的一些简单示例,但是从函数返回函数怎么样?起初我想让它非常简单,但话又说回来,这是一个高级 Python!
三、中级或高级零件
这绝不是 Python 中函数和函数高级概念的指南。有很多很棒的材料,我将在这篇文章的最后留下。但是,我想谈谈我发现非常有趣的几个有趣的方面。
Python 中的函数是对象。我们怎样才能弄清楚这一点? Python 中的每个对象都是一个类的实例,该类最终继承自一个称为 type 的特定类。其细节很复杂,但为了能够了解这与函数有什么关系,这里有一个示例:
type(shout)
# functiontype(type(shout))
# type
当您在 Python 中定义一个类时,它会自动继承该类。继承哪个类?objectobject
type(object)
# type
我应该告诉你 Python 中的类也是对象吗?事实上,这对初学者来说是令人难以置信的。但正如吴恩达所说,这并不那么重要;别担心。
好的,所以函数是对象。当然,函数应该有一些神奇的方法,对吧?
shout.__class__
# functionshout.__name__
# shoutshout.__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 funmy_function()
# 45my_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 funmy_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
在此示例中,也有一些更新,因此让我们回顾一下它们:
- 我们希望能够将参数传递给 my_function。
- 我们希望能够装饰任何函数,而不仅仅是 my_function。因为我们不知道未来函数的参数的确切数量,所以我们必须尽可能保持通用性,这就是我们使用 *args 和 **kwargs 的原因。
- 最重要的是,我们定义了 _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)) + mprint(my_function(5))
# my_function is being called!
# 10print(my_unordinary_function(5, 1))
# my_unordinary_function is being called!
# 11
在我们的示例中,装饰器函数只接受它装饰的函数。但是,如果要传递其他参数并动态更改装饰器行为,该怎么办?假设您要调整记录器装饰器的详细程度。到目前为止,我们的装饰器函数已经接受了一个参数:它装饰的函数。但是,当装饰器函数有自己的参数时,这些参数将首先传递给它。然后,装饰器函数必须返回一个接受修饰函数的函数。从本质上讲,事情变得越来越复杂。还记得电影《盗梦空间》的参考资料吗?
下面是一个示例:
from enum import IntEnum, auto
from datetime import datetime
from functools import wrapsclass 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_decoratorreturn _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)) + mprint(my_function(10))
# LOG: Verbosity level: LOW
# LOG: my_function is being called!
# 45print(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
相关文章:
高级 Python:函数
伊利亚拉扎列维奇 一、说明 读完标题后,你可能会问自己一些类似的事情,“Python 中的函数是一个高级概念?如何?所有课程都引入了功能作为语言的基本块。你既是对的,也是错的。 大多数关于 Python 的课程都将函数作为基…...
【学习笔记】[PA2019] Osady i warownie 2
这题好抽象😱 EI 说这题可以转化为对偶图,但是我完全没看懂😅 考虑维护最向右和向下的两条路径,那么不能放的位置就是两条路径的交(感性理解一下) 考虑抽象的描述这条路径, r i r_i ri表示…...
Flask——接口路由技术
接口路由技术 一、Flask 简介1、环境安装:2、一个最小的应用3、两种运行方式 二、定义路由1、普通路由2、动态路由3、限定类型4、地址尾部的“/” 三、请求与响应-请求方法四、请求与响应-处理请求数据1、request的常用属性/方法2、get 请求参数3、json 请求4、表单…...
Dubbo篇---第一篇
系列文章目录 文章目录 系列文章目录一、说说一次 Dubbo 服务请求流程?二、说说 Dubbo 工作原理三、Dubbo 支持哪些协议?一、说说一次 Dubbo 服务请求流程? 基本工作流程: 上图中角色说明: 二、说说 Dubbo 工作原理 工作原理分 10 层: 第一层:service 层,接口层,…...
powermock-成员变量赋值
powermock成员变量设置 //被测试类 Service public class Demo {private String aaa ;public String method1(){return aaa;} }//测试类,测试类中使用了mockito、和powermock,用powermock设置成员变量相较于mockito简洁一些,一般mockito和po…...
Axios请求成功和失败时分别执行哪个函数?
在 Axios 中,请求成功和失败时分别执行的函数是 then 和 catch。 特点: then 函数用于处理请求成功的情况,它接受一个回调函数作为参数,在请求成功时会调用该回调函数。catch 函数用于处理请求失败的情况,它也接受一…...
【Linux】进程概念III --fork函数解析
Halo,这里是Ppeua。平时主要更新C语言,C,数据结构算法…感兴趣就关注我吧!你定不会失望。 本篇导航 0. 创建进程1. 认识fork函数2.使用Fork函数3.关于fork的为什么3.1 一个函数如何返回两次?fork究竟在干什么?3.2 为什么要给子…...
关闭 Android SplashScreen(闪屏)
SplashScreen在Android 12上是强制的,如果你什么都不做,你的App在Android 12上就会自动拥有SplashScreen界面 但是这个SplashScreen界面太局限了能改的地方太少了 其实也没什么他主要作用是为了在App启动初始化的时候避免让用户在一个空白界面等待过长时…...
react_16
主页 import {DownCircleOutlined,MenuFoldOutlined,VerticalAlignTopOutlined, } from "ant-design/icons"; import { Button, Layout, Menu } from "antd"; import { ItemType } from "antd/es/menu/hooks/useItems"; import { Link, Navigat…...
前端性能分析工具
前段时间在工作中,需要判断模块bundle size缩减对页面的哪些性能产生了影响, 因此需要了解前端的性能指标如何定义的,以及前端有哪些性能分析工具, 于是顺便整理了一篇笔记, 以供前端小白对性能这块知识点做一个入门级的了解. 页面渲染 在了解性能指标和分析工具之前,有必要先…...
根据Aurora发送时序,造Aurora 数据包,从而进行AXIS接口数据位宽转换仿真
首先Aurora采用AXIS接口 由于后续需要进行AXIS接口 不同时钟域的数据位宽转换(64bit和256bit之间的转换),因此分两次走。 第一种方法:采用AXIS数据位宽转换IP AXIS跨时钟域IP 第二种方法:逻辑完成 下面记录逻辑…...
java后端响应结果Result
目录 一、Result1-1、响应代码1-2、调用响应1-3、在前端vue页面使用方法 一、Result 1-1、响应代码 package com.aaa.common;import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor;Data AllArgsConstructor NoArgsConstructor public cla…...
react_11
MobX 介绍 需求,组件0 改变了数据,其它组件也想获得改变后的数据,如图所示 这种多个组件之间要共享状态数据,useState 就不够用了,useContext 也不好用了 能够和 react 配合使用的状态管理库有 MobX Redux 其中…...
AI:52-基于深度学习的垃圾分类
🚀 本文选自专栏:AI领域专栏 从基础到实践,深入了解算法、案例和最新趋势。无论你是初学者还是经验丰富的数据科学家,通过案例和项目实践,掌握核心概念和实用技能。每篇案例都包含代码实例,详细讲解供大家学习。 📌📌📌本专栏包含以下学习方向: 机器学习、深度学…...
[shell,hive] 在shell脚本中将hiveSQL分离出去
将Hive SQL语句写在单独的.hql文件中, 然后在shell脚本中调用这些文件来执行Hive查询。 这样可以将SQL语句与shell脚本分离,使代码更加清晰和易于维护。 基本用法 以下是一个示例,展示如何在shell脚本中使用.hql文件执行Hive查询…...
求两个(法)向量之间的rpy夹角
主要使用Eigen库实现: 1. 四元素到欧拉角的转换 #include <array> #include <Eigen/Geometry>template <typename T> inline Eigen::Matrix<typename std::remove_reference<T>::type::Scalar, 3, 1> eulerAnglesZYX(T q_in) {typedef typenam…...
[100天算法】-每个元音包含偶数次的最长子字符串(day 53)
题目描述 给你一个字符串 s ,请你返回满足以下条件的最长子字符串的长度:每个元音字母,即 a,e,i,o,u ,在子字符串中都恰好出现了偶数次。示例 1:输入:s &qu…...
从科幻走向现实,LLM Agent 做到哪一步了?
LLM 洪流滚滚,AI 浪潮席卷全球,在这不断冲击行业认知的一年中,Agent 以冉冉新星之态引起开发者侧目。OpenAI 科学家 Andrej Karpathy 曾言“OpenAI 在大模型领域快人一步,但在 Agent 领域,却是和大家处在同一起跑线上。…...
[笔记] 数据类型
整形 一字节(Byte,也就是平时KB、MB里面的B)就是八个二进制位(bit) 整形——int——4B无符号整形——unsigned int——4B短整形——short——2B长整型——long——4B双长整型——long long——8B 浮点型 参考博客:C 语言的浮点类型…...
QT学习之QT概述
1.1 什么是QT? Qt是一个跨平台的C图形用户界面应用程序框架。 QT特点: 跨平台,几乎支持所有的平台接口简单,容易上手,学习QT框架对学习其他框架有参考意义。一定程度上简化了内存回收机制开发效率高,能够…...
CMake基础:构建流程详解
目录 1.CMake构建过程的基本流程 2.CMake构建的具体步骤 2.1.创建构建目录 2.2.使用 CMake 生成构建文件 2.3.编译和构建 2.4.清理构建文件 2.5.重新配置和构建 3.跨平台构建示例 4.工具链与交叉编译 5.CMake构建后的项目结构解析 5.1.CMake构建后的目录结构 5.2.构…...
在Ubuntu中设置开机自动运行(sudo)指令的指南
在Ubuntu系统中,有时需要在系统启动时自动执行某些命令,特别是需要 sudo权限的指令。为了实现这一功能,可以使用多种方法,包括编写Systemd服务、配置 rc.local文件或使用 cron任务计划。本文将详细介绍这些方法,并提供…...
css的定位(position)详解:相对定位 绝对定位 固定定位
在 CSS 中,元素的定位通过 position 属性控制,共有 5 种定位模式:static(静态定位)、relative(相对定位)、absolute(绝对定位)、fixed(固定定位)和…...
高防服务器能够抵御哪些网络攻击呢?
高防服务器作为一种有着高度防御能力的服务器,可以帮助网站应对分布式拒绝服务攻击,有效识别和清理一些恶意的网络流量,为用户提供安全且稳定的网络环境,那么,高防服务器一般都可以抵御哪些网络攻击呢?下面…...
什么?连接服务器也能可视化显示界面?:基于X11 Forwarding + CentOS + MobaXterm实战指南
文章目录 什么是X11?环境准备实战步骤1️⃣ 服务器端配置(CentOS)2️⃣ 客户端配置(MobaXterm)3️⃣ 验证X11 Forwarding4️⃣ 运行自定义GUI程序(Python示例)5️⃣ 成功效果的水平?
刚刚刷完2遍 bugku 的 web 题,前来答题。 每个人对刷题理解是不同,有的人是看了writeup就等于刷了,有的人是收藏了writeup就等于刷了,有的人是跟着writeup做了一遍就等于刷了,还有的人是独立思考做了一遍就等于刷了。…...
学习STC51单片机32(芯片为STC89C52RCRC)OLED显示屏2
每日一言 今天的每一份坚持,都是在为未来积攒底气。 案例:OLED显示一个A 这边观察到一个点,怎么雪花了就是都是乱七八糟的占满了屏幕。。 解释 : 如果代码里信号切换太快(比如 SDA 刚变,SCL 立刻变&#…...
Maven 概述、安装、配置、仓库、私服详解
目录 1、Maven 概述 1.1 Maven 的定义 1.2 Maven 解决的问题 1.3 Maven 的核心特性与优势 2、Maven 安装 2.1 下载 Maven 2.2 安装配置 Maven 2.3 测试安装 2.4 修改 Maven 本地仓库的默认路径 3、Maven 配置 3.1 配置本地仓库 3.2 配置 JDK 3.3 IDEA 配置本地 Ma…...
如何在网页里填写 PDF 表格?
有时候,你可能希望用户能在你的网站上填写 PDF 表单。然而,这件事并不简单,因为 PDF 并不是一种原生的网页格式。虽然浏览器可以显示 PDF 文件,但原生并不支持编辑或填写它们。更糟的是,如果你想收集表单数据ÿ…...
uniapp 字符包含的相关方法
在uniapp中,如果你想检查一个字符串是否包含另一个子字符串,你可以使用JavaScript中的includes()方法或者indexOf()方法。这两种方法都可以达到目的,但它们在处理方式和返回值上有所不同。 使用includes()方法 includes()方法用于判断一个字…...
