Python函数语法详解(与C++对比学习)

一、Python函数的形式

def function_name (参数, ...) -> return value_type:# 函数体return value# 看具体需求# 如果没有return语句,函数执行完毕后也会返回结果# 只是结果为None。return None可以简写为return

二、函数名

        这里可以与C++中进行类比,C++的函数名表示函数的地址,可以通过指针来指向函数的地址,再以指针调用函数:

#include <iostream>
void func(int a, int b)
{std::cout << a << " + " << b << " = " << a + b << std::endl;
}
int main()
{void (*f) (int, int) = func;f(1,2);return 0;
}

        Python中的函数也是一样的道理,函数名是指函数的本身,可以被变量名引用,变量名就可以充当新的函数名使用:

my_abs = abs;
print(my_abs(-10))
def add(a, b):return a + bmy_add = add;
print(my_add(1, 2))
print(my_add('abc', 'ABC'))

        所以函数名也是一个对象,我们可以使用函数名来充当函数的参数,从而实现类似于C语言在函数的参数中传递函数指针的操作,完成回调

三、 Python的返回值

【注】:每次返回都是返回的一个新的对象

        在Python3中,提供了可以标注函数返回值的类型的功能,但Python是动态类型语言对返回值的类型标注不会像C++这种静态类型语言那样进行严格的类型检查和限制,所以我们可以认为返回值只是给程序员自己看着方便,在语法不会检查。

返回值标注的作用:

  1. 增强代码的可读性:标注返回值类型可以让其他开发者在阅读代码时更清楚地理解函数的预                                   期输出。
  2. 作为文档:有助于为使用该函数的其他开发者提供明确的信息,减少误解和错误使用。
  3. 辅助静态类型检查工具:虽然 Python 本身不强制检查,但结合一些第三方的静态类型检查                                            工具(如 mypy),可以在一定程度上进行类型检查,提前发现潜在                                          的类型错误。

【C++】

        在C++中,函数的返回值类型是必须标注的,静态类型语言必须保证接收函数返回值的时候数据类型是匹配的

1. Python函数返回多个值

        返回的是一个元组对象,在语法上,返回一个tuple可以省略括号,而多个变量可以同时接收一个tuple,按位置赋给对应的值,所以,Python的函数返回多值其实就是返回一个tuple,但写起来更方便;

        而C++中返回多个值,可以采用输出型参数或者是返回一个类对象

def function_name (参数, ...):return a, b ,c

        如果函数的返回值我们只使用一部分,不关注其他的返回值,可以使用占位符 ‘ _ ’来进行占位

def func():return 1, 2, 3_, _, a = func()
print('a = %d' % a)

2. 函数名作为返回值

        以函数名作为返回值的函数,被调用时不立刻实现功能,返回一个内部函数名,在需要实现时,使用返回值(内部函数名)来完成功能的实现:

如:完成一个从1到num 的累加函数

def lazy_sum(num):def quick_sum():i = 1all_num = 0while i <= num:all_num += ii += 1return all_numreturn quick_sumtotal = lazy_sum(100)  # 返回一个求和函数,不会直接求和
t = total()            # 在调用该求和函数的时候才会求和
print(t)

        内部函数quick_sum可以引用外部函数lazy_sum的参数和局部变量,当lazy_sum返回函数quick_sum时,相关参数和变量都保存在返回的函数中。(此时的相关变量都是最后更新的变量值)

如何证明 内部函数 使用的变量是 外部函数提供的最新变量值?

def first_func():lister = []for i in range(1, 4):def second_func():lister.append(i)return listerreturn second_funcx = first_func()
y = first_func()
z = first_func()
print(x())
print(y())
print(z())

        在使用该函数的时候,我们在循环中实现了一个内部函数second_func,具体是想让其插入1到3的数,可是输出结果是只有3的,这与我们的预期不符。

原因如下:

        在调用外部函数first_fun的时候,返回的是一个内部函数名second_func,但是不会被立刻执行,只有在我们显式地调用时,才会实现该函数,而外部函数执行结束,相关变量都被保存在内部函数中,此时的循环i已经自增到3了,但是内部函数根本没有实现,所以在我们显式地调用时,用到的i值为3,才会出现我们看到的现象

        所以当函数名作为返回值的时候,不要引用任何循环变量,或者后续会发生变化的变量  

        如果内部函数对外部函数的变量进行写操作(修改),则会报错,因为在修改的时候,Python解释器会将变量当成内部函数的局部变量,而变量名必须初始化,所以会报错,你没有初始化它,为了解决这一问题,需要在内部函数加一个nonlocal x的声明。加上这个声明后,Python解释器把x看作外层函数的局部变量,它已经被初始化了,可以正确计算。

二、Python函数的参数

1. 位置参数:

与C++一致,实参的位置与形参的位置相匹配

2. 缺省参数:

与C++一致,形参有缺省参数,可以不传参也可以传参

设置缺省参数的规则:

  • 参数在前,缺省参数在后
  • 当函数有多个参数时,在传参时把变化大的参数放在前面,变化小的参数放在后面(变化小的参数可以作为缺省参数)
  • 必须指向不可变对象

为什么默认参数在后?

        当默认参数在前时,我们传入一个实参,就会发生疑问,这个实参是给缺省参数的?还是给参数的?因为可以一方面通过位置参数来,另一方面可以通过缺省参数来,所以就会产生二义性,为了防止此类事件的发生,规定缺省参数要在参数的后面。

缺省参数的坑:为什么要指向不可变对象?

Python函数缺省参数的 “ 坑 ” (与C++对比学习)-CSDN博客

3. 可变参数:

        支持传入任意个参数,函数内部接收到的是一个元组tuple(在返回值和参数这里使用元组主要是保证传入或输出的对象的安全性,防止恶意篡改)

使用形式:

def func(*num):s = 0for i in num:s += ireturn sret = func(1, 2, 3, 4, 5, 6, 7, 8)
print(ret)

4. 显示命名参数

        按照形参名字传参,无视位置,一般搭配缺省参数使用

使用形式:

def func(id_num, name='dd', age=20):print('id:', id_num, '\nname:', name, '\nage:', age)func(1, age=18)

5. 关键字参数

        关键字参数允许我们传入任意个含有参数名的参数,这些关键字参数会在函数内部自动组装为一个dict。

使用形式:

def func(name, age, **kw):print('name:', name, '\nage:', age, '\nothers:', kw)func('dd', 20, city='上海', work='程序员')

简化版本:

        **dic表示把该字典中的所有key:value用关键字参数传入函数的**kw中,需要注意的是kw获得的dict是dic的一份拷贝,对kw的改动不会影响函数外的dic

def func(name, age, **kw):print('name:', name, '\nage:', age, '\nothers:', kw)dic = {'city': '上海', 'work': '程序员'}
func('yy', 18, **dic)

6. 命名关键字参数

        只可以传入命名关键字参数后的关键字参数

使用形式:

  • 特殊分隔符 * 后面的参数被视为命名关键字参数
  • 如果函数定义中有可变参数,后面跟着的命名关键字参数就不需要特殊分隔符*
  • 命名关键字参数必须传入参数名
def fun(name, age, *, city, work):print('name:', name, '\nage:', age, '\ncity:', city, '\nwork', work)fun('wyd', 20, city='上海', work='程序员')

7. 各种参数可以组合使用

参数定义的顺序必须是:必选参数、默认参数、可变参数、命名关键字参数和关键字参数

三、递归函数

        与C++一致,自己调用自己,但是要防止栈溢出,采用的方法是尾递归,保证每次函数调用只占用一个栈帧。

如计算n的阶乘:

采用递归的方式:

n值过大,则会导致栈空间溢出,因为下面这种是不断调用递归,会不断的压栈,从而导致栈空间不足

def fact(n):if n == 1:return nreturn n * fact(n-1)

采用尾递归的方式:

        每次只用一个栈帧,尾递归的好处就是最外层的函数调用完自身就会销毁函数栈帧,从而保证每次只使用一个栈帧,遗憾的是,大多数编程语言没有针对尾递归做优化,Python解释器也没有做优化,所以,即使把上面的fact(n)函数改成尾递归方式,也会导致栈溢出。

def fact_operator(n, s):if n == 1:return sreturn fact_operator(n - 1, n * s)def fact(n):return fact_operator(n, 1)

 四、 空函数

def func():pass

        C++的空函数只需要不写任何函数体

void func()
{
}

五、匿名函数lambda

        lambda表达式返回的是一个函数对象,所以一般我们要用变量名接收,再调用。

        表达式计算的结果就相当于普通函数的返回值

lambda 参数: 表达式
my_sum = lambda x, y: x + y
print(my_sum)
print(my_sum(3, 5))

如同下面的函数:

def add(x, y):return x + ymy_sum = add
print(my_sum(3, 5))

        C++中也有lambda表达式,相对于Python的更复杂,lambda表达式返回的是一个函数对象

#include <iostream>
int main()
{auto func = [](int a, int b){return a + b;};std::cout << func(1,2) << std::endl;;return 0;
}

六、偏函数

new_func_name = functools.partial(old_func_name, 参数=?,...)

    functools.partial 本质就是设置给参数一个缺省值,返回一个新的函数,从而便于我们调用,但在调用的时候仍然可以对新函数中设置过缺省值的参数进行传参

import functoolsint2 = functools.partial(int, base=2)  # 创建一个专门用于二进制序列转换为十进制的函数
print(int2('111'))
print(int2('1234', base=10))  # 仍然可以传参

        类似C++中对函数的封装,减少函数参数的个数,对程序员屏蔽复杂的参数传递,只展现简单的接口

七、函数对象的属性

  1. __name__:函数的名称。

  2. __doc__:函数的文档字符串(如果有)。

  3. __module__:函数所属的模块名。

  4. __defaults__:包含函数默认参数值的元组。

  5. __code__:与函数相关的代码对象,包含函数的字节码等信息。

  6. __globals__:函数所在模块的全局命名空间的字典。

  7. __closure__:如果函数是闭包,此属性包含闭包所使用的单元格对象。

def function(x):"""This is a sample function"""return x + 1print(function.__name__)  
print(function.__doc__)  

八、装饰器decorator

        装饰器可以在代码运行期间,既不改变函数本身的定义,又可以动态增添函数功能。装饰器本质就是一个返回函数名的高阶函数。

如,我们给添加一个调用函数时,自动输出日志的功能:

def log(func):def wrapper(*args):print('call %s()' % func.__name__)return func(*args)return wrapper@log  
def student(name, age, score):print("name = %s, age = %d, score = %.2f," % (name, age, score))student('dd', 20, 100)

装饰器中调用函数的流程

所以我们为什么要在wrapper函数中写可变参数列表,主要是为了迎合student的传参,否则会导致参数不匹配,调用失败。

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

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

强化学习总结(有具体代码实现)

文章目录 第一部分 强化学习基础第1章 强化学习概述1.1 强化学习概念1.2 强化学习的环境1.3 强化学习的目标1.4 强化学习的数据 第2章 多臂老虎机问题&#xff08;MAB问题&#xff09;2.1 问题描述2.1.1 问题定义2.1.2 形式化描述2.1.3 累积懊悔2.1.4 估计期望奖励 2.2 解决方法…

windows10 +VS2019环境下的PCL安装和配置

今天想做点云重建&#xff0c;千篇一律&#xff0c;PCL少不了。一路跑下来觉得PCL的安装和环境配置还挺麻烦的&#xff0c;比OpenCV真的麻烦很多&#xff0c;有点不想写详细安装和配置过程了&#xff0c;偷个懒&#xff0c;就转载一下大佬的文章吧&#xff0c;下面的博主们已经…

PostgreSQL 中如何处理数据的并发更新冲突解决?

文章目录 一、并发更新冲突的场景二、PostgreSQL 中的并发控制机制&#xff08;一&#xff09; 封锁机制&#xff08;二&#xff09; 事务隔离级别 三、并发更新冲突的解决方法&#xff08;一&#xff09; 重试机制&#xff08;二&#xff09; 使用乐观并发控制&#xff08;三&…

用这款免费爬虫神器,不用手动撸代码了!

很多人学习Python和我说是为了“爬虫”&#xff0c;爬虫的用处确实很丰富&#xff0c;如&#xff1a; 市场研究&#xff0c;了解竞争对手信息&#xff0c;爬虫收集舆论信息、产品动态。 价格分析&#xff0c;通过抓取不同平台商品价格&#xff0c;监测价格波动&#xff0c;…

PDManer使用教程及安装包

以下安装包版本比较低&#xff0c;用习惯了&#xff0c;需要高版本可以去官网下载 链接&#xff1a;https://pan.baidu.com/s/1Hj4zJ0UCcdk0YQTlteVCTQ?pwdv72v 提取码&#xff1a;v72v 使用教程 连接数据库 导入表信息 创建关系图 第一步 第二步 如果列显示不全 &#x…

【LLM大模型】机器学习导论(西瓜书)[推荐阅读]

哈喽啊大家&#xff0c;今天又来给大家推荐一本机器学习方面的书籍<机器学习西瓜书>。本书作为该领域的入门教材&#xff0c;在内容上尽可能涵盖机器学习基础知识的各方面。 为了使尽可能多的读者通过本书对机器学习有所了解&#xff0c;作者试图尽可能少地使用数学知识…

【内网渗透】MSF渗透阶段的常用指令笔记

目录 渗透阶段划分 msfvenom 常用参数 各平台生成payload命令 Meterpreter Meterpreter的常用命令 基本命令 常用命令 针对安卓手机的一些命令 针对Windows的一些命令 文件系统命令 生成木马反弹shell(以linux靶机为例) 木马生成 配置监控 攻击利用 辅助模块 怎…

QT TCP多线程网络通信

学习目标&#xff1a; TCP网络通信编程 学习前置环境 运行环境:qt creator 4.12 QT TCP网络通信编程-CSDN博客 Qt 线程 QThread类详解-CSDN博客 学习内容 使用多线程技术实现服务端计数器 核心代码 客户端 客户端&#xff1a;负责连接服务端&#xff0c;每次连接次数1。…

从零开始做题:MP3

题目 给出一个mp3文件 解题 右键->selection->save selection->另存为xxx.png即可 8750d5109208213f E:\逐鹿\MISC\tools\MP3Stego_1_1_19\MP3Stego>.\decode -X cipher.mp3 MP3StegoEncoder 1.1.19 See README file for copyright info Input file cipher.mp3…

秒懂设计模式--学习笔记(8)【结构型-组合模式】

目录 7、组合模式7.1 组合模式&#xff08;Composite&#xff09;7.2 叉树结构7.3 文件系统7.4 目录树展示7.5 自相似性的涌现7.6 组合模式的各角色定义7.7 组合 7、组合模式 7.1 组合模式&#xff08;Composite&#xff09; 是针对由多个节点对象&#xff08;部分&#xff0…

centos部署jar包

第一步&#xff1a; 将IDEA中的项目打包为jar,将这个jar文件放到centos服务器上的目录里&#xff0c;我在opt新建api目录&#xff0c;将jar文件放入&#xff0c;如下图&#xff1a; 第二步&#xff1a; 将需要读取的配置文件也放入此目录(其他目录也可以&#xff0c;和脚本中…

Thread类的start()方法和run()方法的区别

在Java多线程编程中&#xff0c;Thread类是一个非常重要的类&#xff0c;它提供了创建和管理线程的能力。对于初学者来说&#xff0c;理解Thread类的start()方法和run()方法之间的区别尤为重要。本文将深入探讨这两者之间的不同&#xff0c;帮助读者更好地掌握Java多线程编程的…

web端的vscode编辑器

下载code-server到本地 略 参考 https://blog.csdn.net/kfashfasf/article/details/137110668 运行code-server 到用户目录下设置 vim ~/.config/code-server/config.yaml . bind-addr: 0.0.0.0:8080 auth: password password: xxxxxx cert: false运行 [centosamazon22 ~…

中职网络安全wire0077数据包分析

从靶机服务器的FTP上下载wire0077.pcap&#xff0c;分析该文件&#xff0c;找出黑客入侵使用的协议&#xff0c;提交协议名称 SMTP 分析该文件&#xff0c;找出黑客入侵获取的zip压缩包&#xff0c;提交压缩包文件名 DESKTOP-M1JC4XX_2020_09_24_22_43_12.zip 分析该文件&…

使用Godot4组件制作竖版太空射击游戏_2D卷轴飞机射击-滚动背景(四)

文章目录 开发思路开发思路 使用Godot4组件制作竖版太空射击游戏_2D卷轴飞机射击&#xff08;一&#xff09; 使用Godot4组件制作竖版太空射击游戏_2D卷轴飞机射击-激光组件&#xff08;二&#xff09; 使用Godot4组件制作竖版太空射击游戏_2D卷轴飞机射击-飞船动画&#xff08…

pytorch-RNN实战-正弦曲线预测

目录 1. 正弦数据生成2. 构建网络3. 训练4. 预测5. 完整代码6. 结果展示 1. 正弦数据生成 曲线如下图&#xff1a; 代码如下图&#xff1a; 50个点构成一个正弦曲线随机生成一个0~3之间的一个值&#xff08;随机的原因是防止每次都从相同的点开始&#xff0c;50个点的正弦曲…

JavaSE 面向对象程序设计进阶 IO流 字节流详解 抛出异常

input output 像水流一样读取数据 存储和读取数据的解决方案 内存中数据不能永久化存储 程序停止运行 数据消失 File只能对文件本身进行操作 不能读写文件里存储的数据 读写数据必须要有IO流 可以把程序中的数据保存到文件当中 还可以把本地文件中的数据读取到数据当中 分…

白酒营销策划全攻略:从市场调研到执行落地的实战指南!

为白酒品牌做营销策划&#xff0c;那可得像给自家的孩子挑衣服一样&#xff0c;得量身定制&#xff0c;得考虑孩子的身材、喜好&#xff0c;还得看看衣服的款式和布料。 这里可以分享一点自己多年的实战干货给你&#xff0c;希望对你有所帮助。 首先&#xff0c;得做好“侦查…

【常见开源库的二次开发】一文学懂CJSON

简介&#xff1a; JSON&#xff08;JavaScript Object Notation&#xff09;是一种轻量级的数据交换格式。它基于JavaScript的一个子集&#xff0c;但是JSON是独立于语言的&#xff0c;这意味着尽管JSON是由JavaScript语法衍生出来的&#xff0c;它可以被任何编程语言读取和生成…

CentOS7系统上安装MySQL8.0(rpm-bundle.tar)详细过程

一、MySQL官网下载安装包 1.进入官网MySQL :: Download MySQL Community Server 2.查看自己的版本和架构 uname -mcat /etc/redhat-release 3.选择对应版本并下载 4.查看linux自带的mariadb数据库&#xff0c;有就卸载掉。 rpm -qa | grep mariadbrpm -e mariadb-libs…