Python 中的参数和形参

2024 年 8 月 29 日 | 阅读 6 分钟

无论是什么编程语言,参数(Arguments)和形参(Parameters)这两个词都会给程序员带来很多困惑。有时,这两个词会被互换使用,但实际上,它们有着两个不同但又相似的含义。本教程将解释这两个词之间的区别,并深入探讨这些概念及其示例。

参数和形参都是传递给函数的变量/常量。区别在于:

  1. 参数(Arguments)是在函数调用中传递给函数的变量。
  2. 形参(Parameters)是在函数定义中使用的变量。
  3. 参数和形参的数量应始终相等,除非是可变长度参数列表。

示例

输出

Enter the value of the first number: 5
Enter the value of the second number: 2
Sum of two numbers:  7

从示例中可以学到的要点

  1. (num1, num2)在函数调用中,而(a, b)在函数定义中。
  2. (num1, num2)是参数,(a, b)是形参。

机制

请注意,在上面的示例中,num1 和 num2 是我们在调用函数时使用的函数调用中的值。当调用函数时,a 和 b 被 num1 和 num2 替换,对参数执行操作,然后返回结果。

函数是为了避免反复编写常用逻辑而编写的。为了编写通用逻辑,我们使用一些变量,这些变量称为**形参(parameters)**。它们属于函数定义。当我们在程序中需要使用该函数时,我们需要将程序中使用的变量(称为**参数(arguments)**)应用于函数逻辑。然后,我们使用参数调用函数。

参数的类型

根据我们传递参数给形参的方式,参数有两种类型:

  1. 位置参数(Positional arguments)
  2. 关键字参数(Keyword arguments)
  • 给定一些形参,如果相应的参数**按顺序**一个接一个地传递,则这些参数称为“位置参数”。
  • 如果参数在函数调用中通过**赋值**给其相应的形参传递,而不考虑传递顺序,则它们称为“关键字参数”。

示例

输出

Details of student:  Raghav
age:  12
grade:  6
Details of student:  Santhosh
age:  12
grade:  6

从示例中可以学到的要点

第一次函数调用

  • 该函数有三个形参:name、age 和 grade。因此,它接受三个参数。
  • 在第一次函数调用中:

参数**按位置**传递给形参,这意味着根据传递的顺序:

  • name 被替换为“Raghav”。
  • age 被替换为 12,并且
  • grade 被替换为 6。
  • 在第一次函数调用中,传递参数的顺序很重要。形参仅按给定顺序接收参数。
  • 第二次函数调用

在这里,第一个参数“Santhosh”是根据其位置传递给 name 的,接下来的两个参数是通过赋值传递给其相应形参的。正如你所观察到的,这里的顺序并不重要。

重要提示

  • **关键字参数必须始终跟随位置参数**。否则,Python 将引发语法错误。

如果我们这样写:details("Santhosh", age = 6, 12)

按值调用和按引用调用

这是关于参数和形参最重要的概念。根据传递给形参的参数类型,有两种调用函数的方法:按值调用(Call by value)和按引用调用(Call by reference)。

当参数的值被传递到函数中的形参时,值会被复制到形参中。这种方法称为“**按值调用(Call by value)**”。

在这种方法中,参数和形参是不同的,并且存储在不同的内存位置。

  • 在函数内部对形参所做的更改不会影响程序中的参数,反之亦然。
  • **Java** 函数/方法仅遵循按值调用。

当传递参数的地址而不是值到形参时,这种调用函数的方法称为“**按引用调用(Call by Reference)**”。

  • 参数(指针)和参数都引用相同的内存位置。
  • 对形参(指针)的更改将影响程序中参数的值。
  • **C 语言**默认遵循按值调用,但可以使用间接运算符和指针模拟按引用调用。

Python 遵循哪种方法?

Python 不使用按值调用或按引用调用。它遵循一种称为“**按赋值调用(Call by assignment)**”的方法。在 Python 中,每一个实体都是一个**对象**。对象分为可变对象和不可变对象。当我们在 Python 中给一个变量赋值时,其行为与 C 或 Java 等低级语言不同。

假设在语句中:

a = 20

a 是变量,20 是赋给它的值。在这里,20 被保存在一个内存位置,而 a 是我们为指向该内存位置的引用所取的名称。现在,如果我们写:

a = 21

该名称将不再指向内存位置 20,而是指向另一个内存位置 21。

在 C 等其他语言中,变量是存储值的内存位置。

示例

在 C 语言中:

输出

000000000062FE1C
000000000062FE1C

在 Python 中

输出

140714950863232
140714950863264
  • 正如你所观察到的:在 C 语言中,在重新赋值后,变量仍在同一内存位置,而在 Python 中,它指向不同的内存位置。(id -> Python 中的地址)。
  • 但这还不是全部。还有其他类型的对象。

现在 comes Python 中的可变对象和不可变对象概念。

Python 中的可变对象和不可变对象

  1. 可变对象(Mutable objects)是指在 Python 中创建后可以修改的对象/数据类型。例如:列表(Lists)、字典(Dictionaries)、集合(Sets)。
  2. 不可变对象(Immutable objects)则是在创建后无法修改的对象。例如:int、float、字符串(strings)、元组(tuples)。

示例

可变对象

输出

2253724439168
2253724439168

理解

列表是可变的,这意味着我们可以在创建后对其进行更改或修改。正如你所观察到的,当以名称 a 创建时,它保存在地址“2253724439168”中。使用 append(),我们通过追加另一个值来修改它。它仍然在同一个内存位置,这意味着同一个对象被修改了。

不可变对象

输出

140714950863232
140714950863968

理解

这是我们在教程前面讨论过的情况。整数对象是不可变的,这意味着我们无法在创建后修改它。你可能会想,我们在上面的代码中仍然添加了 23。请注意,创建的对象在添加后不是同一个对象。它们都位于不同的内存位置,这意味着它们是不同的对象。

那么,在调用函数时,参数是如何传递给形参的呢?

结合关于 Python 中赋值操作的所有知识:

  1. 如果参数是可变的,则传递方式类似于“按引用调用”。
  2. 如果参数是不可变的,则传递方式类似于“按值调用”。

示例

输出

Details of the student: 
name:  Harry Styles
age:  15
grade:  10
marks:  [25, 29, 2F1, 30, 26]
10
[25, 29, 21, 30, 26]

理解

该函数接受 4 个参数。请注意参数 **grade** 和 **marks**。grade 是一个整数值,这意味着它是不可变的。因此,一旦创建,我们就无法修改它。它遵循“按值调用”。正如我们在教程前面讨论过的,在遵循按引用调用时,“**对形参(指针)的更改不会影响程序中的参数值**”。因此,在函数定义中连接字符串后,grade 在程序中的原始值不会被修改。

对于 marks 来说,它是一个列表,是可变的。因此,它遵循“按引用调用”,这意味着“**对形参(指针)的更改将影响程序中的参数值**”。因此,在函数定义中追加列表后,更改会反映在原始程序中。