前端的 Python 入门指南(四):参数传递方式对比 - 值与引用传递 vs 可变不可变数据的优劣分析+副作用分析

news/2024/12/11 13:39:11/

前端的 Python 入门指南》系列文章:

  • (一):常用语法和关键字对比
  • (二):函数的定义、参数、作用域对比
  • (三):数据类型对比 - 彻底的一切皆对象实现和包装对象异同
  • (四):参数传递方式对比 - 值与引用传递 vs 可变不可变数据
  • (五):面向对象特性之继承实现的方式对比 - 基于原型链和基于类各有什么优缺点

在 JavaScript 和 Python 中,函数参数传递机制和数据的可变性是两个核心概念。它们对程序的行为和副作用产生了深远的影响。对于前端开发者来说,理解 Python 和 JavaScript 在 值传递与引用传递可变与不可变数据 上的异同点,将有助于更高效地编写代码,避免潜在的副作用问题。

本文将探讨两种语言的参数传递机制,分析它们的优劣,以及如何利用这些机制编写高质量代码。

1. 值传递 vs 引用传递

什么是值传递和引用传递?

  • 值传递:传递参数时,函数内部接收到的是参数的副本,对副本的修改不会影响原始数据。
  • 引用传递:传递参数时,函数内部接收到的是原始对象的引用,对引用的修改会直接影响原始数据。

JavaScript 的参数传递机制

JavaScript 使用 按值传递按共享引用传递

  1. 原始类型(Primitive Types):按值传递。
    javascript">let x = 10;
    function modify(val) {val = 20;  // 修改的是 val 的副本
    }
    modify(x);
    console.log(x);  // 10,原始值不受影响
    
  2. 引用类型(Objects, Arrays):按共享引用传递。
    javascript">let obj = { key: "value" };
    function modify(o) {o.key = "newValue";  // 修改原对象内容
    }
    modify(obj);
    console.log(obj.key);  // "newValue",原对象被修改
    

Python 的参数传递机制

Python 使用 “传对象引用”(Pass by Object Reference)

  • 传递的是对象的引用,但行为取决于对象的可变性
    • 不可变对象(如 int, str, tuple):重新赋值不会影响原始数据。
    • 可变对象(如 list, dict, set):修改内容会影响原始数据。
不可变对象
python">x = 10
def modify(val):val = 20  # 重新赋值,不影响原始对象
modify(x)
print(x)  # 10
可变对象
python">obj = {"key": "value"}
def modify(o):o["key"] = "newValue"  # 修改对象内容
modify(obj)
print(obj["key"])  # "newValue",原对象被修改

2. 可变与不可变数据

Python 的参数行为与对象的可变性密切相关,而 JavaScript 对原始类型和引用类型的处理也与数据结构的可变性息息相关。

可变对象的优劣

优点:
  1. 高效操作:无需复制整个对象即可修改内容。
  2. 灵活性高:可以直接在函数内部操作共享的数据。
缺点:
  1. 副作用问题:函数可能无意中改变原始数据,导致难以预测的行为。
  2. 不适合并发:多线程操作可变数据容易引发竞争条件(Race Condition)。

不可变对象的优劣

优点:
  1. 无副作用:函数无法改变原始数据,减少了不可预测的行为。
  2. 线程安全:在多线程或异步环境中,不可变数据更安全。
  3. 简化调试:不可变数据的状态更容易追踪。
缺点:
  1. 性能开销:每次修改数据都需要创建新的副本,尤其对大数据结构可能不够高效。
  2. 操作复杂:需要使用替代方法来模拟“修改”不可变对象。

3. 副作用对比:如何避免问题?

JavaScript 中的副作用与避免

常见副作用:
  1. 函数修改传入的对象,影响外部代码逻辑。
  2. 使用全局变量或对象的共享状态,导致难以调试的问题。
避免方法:
  1. 使用 Object.assign 或解构赋值创建副本:

    javascript">let obj = { key: "value" };
    function modify(o) {let newObj = { ...o, key: "newValue" };  // 创建副本return newObj;
    }
    console.log(modify(obj));  // 新对象
    console.log(obj);  // 原对象未受影响
    
  2. 在函数中使用纯函数(无副作用):

    javascript">function add(a, b) {return a + b;  // 不修改任何外部数据
    }
    

Python 中的副作用与避免

常见副作用:
  1. 函数内部修改了可变对象(如 listdict),影响调用者的数据。
  2. 使用全局变量导致意外结果。
避免方法:
  1. 传递副本而非原始对象

    python">def modify(o):o = o.copy()  # 创建副本o["key"] = "newValue"return oobj = {"key": "value"}
    new_obj = modify(obj)
    print(new_obj)  # {"key": "newValue"}
    print(obj)      # {"key": "value"}
    
  2. 尽量使用不可变对象

    python">def modify_tuple(t):return t + (42,)  # 创建新元组tpl = (1, 2, 3)
    new_tpl = modify_tuple(tpl)
    print(new_tpl)  # (1, 2, 3, 42)
    print(tpl)      # (1, 2, 3)
    
  3. 封装全局变量访问
    使用函数参数或类来封装需要修改的状态,避免直接修改全局变量。


4. 两者的优劣对比

特性JavaScriptPython
参数传递机制原始类型按值传递,引用类型按共享引用传递所有对象按引用传递,但行为取决于对象的可变性
副作用控制倾向于直接修改引用类型,需显式创建副本避免副作用使用可变对象可能导致副作用,不可变对象天然防止副作用
操作效率修改引用类型时高效,但易出错可变对象修改高效,不可变对象需要拷贝,性能略低
适用场景适合需要快速迭代的场景,如动态 UI 更新适合数据安全性要求高或多线程操作的场景

5. 总结与实践建议

  1. 理解语言特性

    • JavaScript 和 Python 在参数传递时都需要注意共享引用对原始数据的影响。
    • Python 的行为与对象的可变性密切相关,而 JavaScript 需要明确区分原始类型和引用类型。
  2. 避免副作用

    • 在函数中修改共享对象可能会引发不可预期的问题,建议通过创建副本或使用不可变数据来减少副作用。
    • 在 Python 中,优先考虑使用不可变数据(如 tuple)。
  3. 性能优化

    • 对大数据结构,使用可变对象修改内容更高效。
    • 如果副本是必须的,尽量只复制必要的数据以减少性能开销。

通过理解两种语言在参数传递和副作用上的实现细节,可以帮助前端开发者在跨语言开发时更高效地编写代码,并避免潜在的错误!


http://www.ppmy.cn/news/1554228.html

相关文章

android 常用三方框架

说实话, 我是比较讨厌三方框架的, 比如一个eventbus 底层逻辑就是个观察者模式,当然他的场景涵盖的比较丰富, 单从 单一原则来说, 还是一个简单的观察者模式就能解决问题, 何必要添加那么多文件到我们的项目…

2021 年 3 月青少年软编等考 C 语言四级真题解析

目录 T1. 酒鬼思路分析T2. 重启系统思路分析T3. 鸣人的影分身思路分析T4. 宠物小精灵之收服思路分析T1. 酒鬼 Santo 刚刚与房东打赌赢得了一间在 New Clondike 的大客厅。今天,他来到这个大客厅欣赏他的奖品。房东摆出了一行瓶子在酒吧上。瓶子里都装有不同体积的酒。令 Sant…

Ubuntu中安装配置交叉编译工具并进行测试

01-下载获取交叉编译工具的源码 按照博文 https://blog.csdn.net/wenhao_ir/article/details/144325141的方法,把imx6ull的BSP下载好后,其中就有交叉编译工具。 当然,为了将来使用方便,我已经把它压缩并传到了百度网盘&#xff…

matlab画曲线属性

给曲线加注释 用displayname Linewidth 是线条的大小 plot(theta, 10.*log10(sigma_rain25_D180_wind0), DisplayName, Rain 25 dBZ,LineWidth, 1.5); 在指定点画一个实心点,用’.‘ 画圈圈用’o‘ markersize是画点的大小 color指定颜色 plot(x, y, .…

Error:Kotlin: Module was compiled with an incompatible version of Kotlin.

问题:Error:Kotlin: Module was compiled with an incompatible version of Kotlin. The binary version of its metadata is 1.6.0, expected version is 1.1.15. 解决方法: 如下图所示 更改好,重新打开项目,进行运行&#xff…

【Unity】PC VR双模式下,删除所有子物体某个组件

最近做了一个双模式的项目,写了一个删除某个物体下的所有子物体的功能,最开始比较麻烦,然后看了某个大佬的思路,觉得挺不错的,使用递归直接搞定。

constexpr、const和 #define 的比较

constexpr、const 和 #define 的比较 一、定义常量 constexpr 定义:constexpr用于定义在编译期可求值的常量表达式。示例:constexpr int x 5;这里,x的值在编译期就确定为5。 const 定义:const表示变量在运行期间不能被修改&…

3D 生成重建017-StyleGaussian用文本或图像对你的3DGS内容进行风格迁移

3D 生成重建017-StyleGaussian用文本或图像对你的3DGS内容进行风格迁移 文章目录 0 论文工作1 论文方法2 实验结果 0 论文工作 论文 “StyleGaussian: Instant 3D Style Transfer with Gaussian Splatting” 介绍了一种新颖的3D风格迁移方法 StyleGaussian,该方法通…