Python 中的指针 | 为什么 Python 不支持指针

2025年3月17日 | 阅读 10 分钟

在本教程中,我们将学习 Python 中的指针,并了解为什么 Python 不支持指针概念。

我们还将了解如何在 Python 中模拟指针。下面是对那些不了解指针的人的指针介绍。

我们还将了解如何在 Python 中模拟指针。下面是对那些不了解指针的人的指针介绍。

什么是指针?

指针是一个非常流行且有用的工具,用于存储变量的地址。如果有人曾经使用过像 CC++ 这样的低级语言,他/她可能对指针很熟悉。它非常有效地管理代码。对于初学者来说可能有点难,但它是程序中一个重要的概念。然而,它可能导致各种内存管理错误。因此,指针的定义是 -

“指针是保存另一个变量的内存地址的变量。指针变量用星号 (*) 表示。”

让我们看看 C 编程语言中指针的以下示例。

示例 - 如何在 C 中使用指针

输出

Address of o: 2686784
Value of o: 22

Address of pointer po: 2686784
Content of pointer po: 22

Address of pointer po: 2686784
Content of pointer po: 11

Address of o: 2686784
Value of o: 2

除了有用之外,指针在 Python 中不使用。在本主题中,我们将讨论 Python 的对象模型,并了解为什么 Python 中不存在指针。我们还将学习在 Python 中模拟指针的不同方法。首先,让我们讨论为什么 Python 不支持指针。

为什么 Python 不支持指针

不支持指针的确切原因尚不清楚。Python 中是否存在原生指针?Python 的主要概念是其简洁性,但指针违反了 Python 之禅。 指针主要鼓励隐式更改而不是显式更改。它们也很复杂,特别是对于初学者来说。

指针倾向于在代码中产生复杂性,而 Python 主要关注可用性而不是速度。因此,Python 不支持指针。然而,Python 确实提供了一些使用指针的好处。

在理解 Python 中的指针之前,我们需要了解以下几点的基本概念。

  • 不可变对象与可变对象
  • Python 变量/名称

Python 中的对象

在 Python 中,一切都是对象,甚至是类、函数、变量等。每个对象至少包含三个数据片段。

  • 引用计数
  • 类型

让我们一一讨论。

引用计数 - 它用于内存管理。要获取有关 Python 内存管理的更多信息,请阅读 Python 中的内存管理。

类型 - CPython 层用作类型,以确保运行时类型安全。最后,有一个值,它是与对象关联的实际值。

如果我们深入研究这个对象,我们会发现并非所有对象都相同。对象类型之间的重要区别是不可变和可变。首先,我们需要了解对象类型之间的区别,因为它解释了 Python 中的指针。

不可变对象与可变对象

不可变对象不能修改,而可变对象可以修改。让我们看看下面列出的常见类型及其可变性。

对象类型
int不可变
Float不可变
Bool不可变
列表可变
Set可变
复杂可变
元组不可变
Frozenset(冻结集合)不可变
字典(Dict)可变

我们可以使用 id() 方法检查上述对象的类型。此方法返回对象的内存地址。

我们正在 REPL 环境中输入以下代码。

输出

140720979625920

在上面的代码中,我们将值 10 赋值给 x。如果通过替换修改此值,我们将获得新对象。

输出

140720979625888

正如我们所看到的,我们修改了上面的代码并得到了作为响应的新对象。让我们再举一个 str 的例子。

输出

2315970974512
JavaTpoint
1977728175088

同样,我们通过添加新字符串来修改 x 的值,并获得新的内存地址。让我们尝试直接在 s 中添加字符串。

输出

Traceback (most recent call last):
  File "C:/Users/DEVANSH SHARMA/PycharmProjects/MyPythonProject/python1.py", line 34, in 
    s[0] = T
NameError: name 'T' is not defined

上面的代码返回错误,这意味着字符串不支持修改。所以 str 是不可变对象。

现在,我们将看到可变对象,例如列表。

输出

2571132658944
[3, 4, 8, 4]
2571132658944

正如我们在上面的代码中看到的,my_list 最初具有 ID,我们向列表中追加了 5;my_list 具有相同的 ID,因为列表支持可变性。

理解 Python 变量

Python 中定义变量的方式与 C 或 C++ 有很大不同。Python 变量不定义数据类型。事实上,Python 有名称,而不是变量。

所以我们需要理解变量和名称之间的区别,尤其是在我们探讨 Python 中棘手的指针主题时。

让我们了解变量在 C 中如何工作以及名称在 Python 中如何工作。

C 中的变量

在 C 语言中,变量是保存或存储值的。它使用数据类型定义。让我们看下面的代码,它定义了变量。

  • 为整数分配足够的内存。
  • 我们将值 286 赋给该内存位置。
  • x 代表该值。

如果我们表示内存视图 -

Pointer in Python

正如我们所看到的,x 有一个用于值 286 的内存位置。现在,我们将新值赋给 x。

x = 250

这个新值会覆盖之前的值。这意味着变量 x 是可变的。

x 的值位置相同,但值发生了变化。这是一个重要的点,表明 x 是内存位置,而不仅仅是它的名称。

现在,我们引入新的变量 y,它将 x 的值赋给它,然后 y 创建一个新的内存盒。

变量 y 创建一个名为 y 的新盒子,并将 x 的值复制到该盒子中。

Pointer in Python

Python 中的名称

正如我们之前讨论的,Python 没有变量。它有名称,我们将其用作变量。但是变量和名称之间存在差异。让我们看下面的例子。

上面的代码在执行期间被分解。

  1. 创建一个 PyObject
  2. 将 PyObject 的类型码设置为整数
  3. 将 PyObject 的值设置为 289
  4. 创建一个名为 x 的名称
  5. 将 x 指向新的 PyObject
  6. 将 PyObject 的引用计数增加 1

它将如下图所示。

Pointer in Python

我们可以理解 Python 中变量的内部工作原理。变量 x 指向对象的引用,它不像以前那样拥有内存空间。它还表明 x = 289 将名称 x 绑定到引用。

现在,我们引入新变量并将 x 赋值给它。

在 Python 中,变量 y 不会创建新对象;它只是指向同一对象的新名称。对象的 refcount 也增加了一。我们可以通过以下方式确认。

输出

True

如果我们将 y 的值增加一,它将不再引用相同的对象。

这意味着,在 Python 中,我们不赋值变量。相反,我们将名称绑定到引用。

在 Python 中模拟指针

正如我们所讨论的,Python 不支持指针,但我们可以利用指针的好处。Python 提供了在 Python 中使用指针的替代方法。这两种方法如下所述。

  • 使用可变类型作为指针
  • 使用自定义 Python 对象

让我们理解给定的要点。

使用可变类型作为指针

在上一节中,我们定义了可变类型对象;我们可以将它们视为指针来模拟指针行为。让我们理解以下示例。

C

在上面的代码中,我们定义了指针 *a,然后我们将值增加一。现在,我们将在 main() 函数中实现它。

输出

y = 233
y = 234

我们可以通过使用 Python 可变类型来模拟这种行为。理解以下示例。

上面的函数访问列表的第一个元素并将其值增加一。当我们执行上面的程序时,它会打印 y 的修改值。这意味着我们可以使用可变对象复制指针。但如果我们尝试使用不可变对象模拟指针。

输出

Traceback (most recent call last):
  File "", line 1, in 
  File "", line 2, in add_one
TypeError: 'tuple' object does not support item assignment

我们在上面的代码中使用了元组,这是一个不可变对象,所以它返回了错误。我们还可以使用字典来模拟 Python 中的指针。

让我们理解以下示例,在该示例中,我们将计算程序中发生的每个操作。我们可以使用 dict 来实现这一点。

示例 -

输出

2

解释 -

在上面的示例中,我们使用了 count 字典,它跟踪函数调用的次数。当 foo() 函数被调用时,计数器增加 2,因为 dict 是可变的。

使用 Python 对象

在前面的例子中,我们使用 dict 来模拟 Python 中的指针,但有时记住所有使用的键名会变得困难。我们可以使用 Python 自定义类代替字典。让我们理解下面的例子。

示例 -

在上面的代码中,我们定义了 Pointer 类。这个类使用 dict 在 _metrics 成员变量中保存实际数据。它将为我们的程序提供可变性。我们可以如下实现:

我们使用了 @property 装饰器。如果您不熟悉装饰器,请访问我们的 Python 装饰器教程。@property 装饰器将访问 funCalls 和 catPicture_served。现在,我们将创建一个 Pointer 类的对象。

这里我们需要增加这些值。

我们定义了两个新方法 - increment() 和 cat_pics()。我们使用这些函数修改了矩阵字典中的值。在这里,我们可以像修改指针一样修改类。

Python ctypes 模块

Python ctypes 模块允许我们在 Python 中创建 C 类型的指针。如果我们想调用需要指针的 C 库函数,这个模块会很有用。让我们理解以下示例。

示例 - C 语言

在上面的函数中,我们将 x 的值增加了 1。假设我们将上面的文件保存为 incrPointer.c,并在终端中输入以下命令。

第一个命令将 incrPointer.c 编译成一个名为 incrPointer.o 的对象。第二个命令接受对象文件并生成 libinic.so 以与 ctypes 协作。

输出

<_FuncPtr object at 0x7f46bf6e0750>

在上面的代码中,ctypes.CDLL 返回一个名为 libinic.so 的共享对象。它包含 incrPointer() 函数。如果我们需要指定函数指针,我们需要使用 ctypes 来指定。让我们看下面的例子。

如果我们使用不同的类型调用函数,它会抛出错误。

输出

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ctypes.ArgumentError: argument 1: <class 'TypeError'>: expected LP_c_int instance instead of int

这是因为 incrPointer 需要一个指针,而 ctypes 是一种在 Python 中传递指针的方法。

v 是一个 C 变量。ctypes 提供了一个名为 byref() 的方法,用于传递变量引用。

输出

c_int(11)

我们使用引用变量增加了值。

结论

我们讨论了指针在 Python 中不存在,但我们可以使用可变对象实现相同的行为。我们还讨论了 ctypes 模块,它可以在 Python 中定义 C 指针。我们定义了几种在 Python 中模拟指针的优秀方法。