Python 中的指針,為什麼 Python 不支持指針

在本教程中,我們將學習 Python 中的指針,並了解為什麼 Python 不支持指針概念。

我們還將了解如何在 Python 中模擬指針。下面是指針的介紹,給那些對此一無所知的人。

我們還將了解如何在 Python 中模擬指針。下面是指針的介紹,給那些對此一無所知的人。

什麼是指針?

指針是一個非常流行和有用的工具來存儲變量的地址。如果有人曾經使用過低級語言,比如 C 。 C++ ,他/她可能熟悉指針。它非常有效地管理代碼。對於初學者來說可能有點難,但這是程序的一個重要概念。但是,它會導致各種內存管理錯誤。因此,指針的定義-

*「指針是保存另一個變量的內存地址的變量。指針變量用星號()表示。**

讓我們看看下面 C 編程語言中指針的例子。

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


#include int main()
{
   int* po, o;

   0 = 10;
   printf("Address of c: %p\n", &c);
   printf("Value of c: %d\n\n", c);  

   o = &0;
   printf("Address of pointer pc: %p\n", o);
   printf("Content of pointer pc: %d\n\n", *o);   
   0 = 11;
   printf("Address of pointer pc: %p\n", p0);
   printf("Content of pointer pc: %d\n\n", *p0);   
   *po = 2;
   printf("Address of c: %p\n", &o);
   printf("Value of c: %d\n\n", o);
   return 0;
} 

輸出:

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 中的指針。

不可變對象與可變對象

不可變對象不能修改,可變對象可以修改。讓我們看看下錶中常見的類型,以及它們是否可變。

| 目標 | 類型 |
| (同 Internationalorganizations)國際組織 | 不變的 |
| 浮動 | 不變的 |
| 彎曲件 | 不變的 |
| 目錄 | 易變的 |
| 一組 | 易變的 |
| 複雜的 | 易變的 |
| 元組 | 不變的 |
| 凍結集合 | 不變的 |
| 字典 | 易變的 |

我們可以使用 id() 方法檢查上述對象的類型。這個方法返回對象的內存地址。

我們在 REPL 的環境中鍵入下面的行。


x = 5
id(x)

輸出:

140720979625920

在上面的代碼中,我們將值 10 賦給了 x,如果我們用替換來修改這個值,我們將得到新的對象。


x-=1
id(x)

輸出:

140720979625888

如我們所見,我們修改了上面的代碼,並獲得了新的對象作為響應。我們再舉一個 str 的例子。


s = "java"
print(id(s))

s += "Tpoint"
print(s)

id(s)

輸出:

2315970974512
JavaTpoint
1977728175088

同樣,我們通過添加一個新的字符串來修改 x 的值,我們得到了新的內存地址。讓我們嘗試直接在 s 中添加字符串。


s = 'java'
s[0] = T
print(id(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 是不可變的對象。

現在,我們將看到諸如 list 這樣的可變對象。


my_list = [3, 4, 8]
print(id(my_list))

my_list.append(4)
print(my_list)

print(id(my_list))

輸出:

2571132658944
[3, 4, 8, 4]
2571132658944

從上面的代碼中我們可以看到, my_list 原本有 id,我們在列表中追加了 5;我的列表具有相同的 id,因為列表支持可變性。

理解 Python 變量

Python 中定義變量的方式與 C 或 C++有很大不同。Python 變量沒有定義數據類型。事實上,Python 有名字,沒有變量。

因此,我們需要理解變量和名稱之間的區別,尤其是當我們在 Python 中導航指針這個棘手的主題時。

讓我們理解變量在 C 語言中是如何工作的,以及名稱在 Python 中是如何工作的。

C 語言中的變量

在 C 語言中,變量是指它保存值或存儲值。它是用數據類型定義的。讓我們看看下面定義變量的代碼。


int x = 286;
  • 為整數分配足夠的內存。
  • 我們將值 286 分配給該存儲位置。
  • x 代表那個值。

如果我們代表記憶的觀點-

如我們所見,x 有一個值 286 的存儲位置。現在,我們將給 x 賦值。

x = 250

這個新值會覆蓋以前的值。這意味着變量 x 是可變的。

x 的值位置相同,但值發生了變化。這是一個重要的點,表明 x 是內存位置,而不僅僅是它的名字。

現在,我們引入一個新的變量,這個變量取 x,然後 y 創建一個新的內存盒。


int y = x;

變量 y 創建了一個名為 y 的新框,將 x 的值複製到該框中。

Python 中的名稱

正如我們前面討論的,Python 沒有變量。它有名字,我們用這個術語作為變量。但是變量和名字是有區別的。讓我們看看下面的例子。


x = 289

上面的代碼在執行過程中被分解。

  1. 創建一個對象
  2. 將 PyObject 的 typecode 設置為整數
  3. 將對象的值設置為 289
  4. 創建一個名為 x 的名稱
  5. 指向新的對象
  6. 將對象的引用計數增加 1

如下圖所示。

我們可以理解 Python 中一個變量的內部工作方式。變量 x 指向對象的引用,它沒有像以前那樣的內存空間。它還顯示 x = 289 正在將名稱 x 綁定到引用。

現在,我們引入一個新的變量,並給它賦值 x。


y = x

在 Python 中,變量 y 不會創建新對象;它只是指向同一個對象的新名稱。物體反射計數也增加一。我們可以確認如下。


y is x

輸出:

True

如果我們將 y 的值增加一,它將不再引用同一個對象。


y + =1
y is x

這意味着,在 Python 中,我們不分配變量。相反,我們將名稱綁定到引用。

用 Python 模擬指針

正如我們已經討論過的,Python 不支持指針,但是我們可以獲得使用指針的好處。Python 提供了在 Python 中使用指針的替代方法。下面給出這兩種方式。

  • 使用可變類型作為指針
  • 使用自定義 Python 對象

讓我們理解給定的要點。

使用可變類型作為指針

在前一節中,我們已經定義了可變類型對象;我們可以把它們當作指針來對待,以模擬指針行為。讓我們理解下面的例子。

C


void add_one(int *a) {
    *a += 1;
}

在上面的代碼中,我們定義了指針*a,然後將值增加 1。現在,我們將使用 main()函數實現它。


#include int main(void) {
    int y = 233;
    printf("y = %d\n", y);
    add_one(&y);
    printf("y = %d\n", y);
    return 0;
} 

輸出:

y = 233
y = 234

我們可以通過使用 Python 可變類型來模擬這種類型的行為。理解下面的例子。


def add_one(x):
    x[0] += 1

y = [2337]
add_one(y)
y[0]

上面的函數訪問列表的第一個元素,並將其值增加 1。當我們執行上面的程序時,它打印出 y 的修改值。這意味着我們可以使用可變對象複製指針。但是如果我們嘗試使用不可變對象來模擬指針。


z = (2337,)
add_one(z)

輸出:

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

我們在上面的代碼中使用了元組,一個不可變的對象,所以它返回了錯誤。我們也可以用字典來模擬 Python 中的指針。

讓我們理解下面的例子,其中我們將計算程序中發生的每個操作。我們可以用 dict 來實現這一點。

示例-


count = {"funcCalls": 0}
def car():
    count["funcCalls"] += 1

def foo():
    count["funCcalls"] += 1
    car()

foo()
count["funcCalls"]

輸出:

2

解釋-

在上面的例子中,我們使用了 count 字典,它記錄了函數調用的次數。當調用 foo() 函數時,計數器增加 2,因為 dict 是可變的。

使用 Python 對象

在前面的例子中,我們使用 dict 來模擬 Python 中的指針,但是有時很難記住所有使用的鍵名。我們可以使用 Python 自定義類來代替字典。讓我們理解下面的例子。

示例-


class Pointer(object):
    def __init__(self):
        self._metrics = {
            "funCalls": 0,
            "catPictures": 0,
        }

在上面的代碼中,我們定義了指針類。這個類使用 dict 來保存 _metrics 成員變量中的實際數據。它將為我們的程序提供可變性。我們可以這樣做。


class Pointer(object):
    # ...

    @property
    def funCalls(self):
        return self._metrics["func_calls"]

    @property
    def catPictures_served(self):
        return self._metrics["cat_pictures_served"]

我們使用了@ T1【物業】@ T2裝飾機。如果您不熟悉裝飾者,請訪問我們的 Python 裝飾者教程。@property decorator 將訪問 funCalls 和 catPicture_served。現在,我們將創建一個指針類的對象。


pt = Pointer()
pt.funCalls()
pt.catPicture_served

這裡我們需要增加這些值。


class Pointer(object):
    # ...

    def increament(self):
        self._metrices["funCalls"] += 1

    def cat_pics(self):
        self._metrices["catPictures_served"] += 1

我們定義了兩種新方法——增量()和 cat_pics()。我們在矩陣字典中使用這些函數修改了值。在這裡,我們可以像修改指針一樣修改類。


pt = Pointer()
pt.increment()
pt.increment()
pt.funCalls()

python ctypes 模塊

Python ctypes 模塊允許我們在 Python 中創建一個 C 型指針。如果我們想對需要指針的 C 庫進行函數調用,這個模塊很有幫助。讓我們理解下面的例子。

示例- C 語言


void incr_one(int *x) {
    *x += 1;
}

在上面的函數中,我們將 x 的值加 1。假設我們保存了上述名為 incrPointer.c 的文件,並在終端中鍵入以下命令。


$ gcc -c -Wall -Werror -fpic incrPointer.c
$ gcc -shared -o libinc.so incrPointer.o

第一個命令將incrpoint . c編譯成一個名為incrpoint . o的對象,第二個命令接受對象文件併產生 libinic.so 與 ctypes 協作。


import ctypes
##  libinc.so library should be same directory as this program
lib = ctypes.CDLL("./libinc.so")
lib.increment

輸出:

<_funcptr object="" at="">

在上面的代碼中,鍵。CDLL 返回一個名為 libinic.so. 的共享對象,它包含incrpoire()函數。如果我們需要指定指向我們在共享對象中定義的函數的指針,我們必須使用 ctypes 來指定它。讓我們看看下面的例子。


inc = lib.increment 
## defining the argtypes 
inc.argtypes = [ctypes.POINTER(ctypes.c_int)]

如果我們使用不同的類型調用函數,它會通過一個錯誤。


incrPointer(10)

輸出:

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 = ctypes.c_int(10)

v 是一個 C 變量。ctypes 提供了名為 byref() 的方法,用於傳遞變量引用。


inc(ctypes.byref(a)) 
a

輸出:

c_int(11)

我們使用參考變量增加了值。

結論

我們已經討論過 Python 中不存在指針,但是我們可以用*可變對象實現相同的行為。我們還討論了 Python 中可以定義 C 指針的 ctypes 模塊。我們已經定義了一些在 Python 中模擬指針的優秀方法。


原創文章,作者:小藍,如若轉載,請註明出處:https://www.506064.com/zh-hk/n/257510.html

(0)
打賞 微信掃一掃 微信掃一掃 支付寶掃一掃 支付寶掃一掃
小藍的頭像小藍
上一篇 2024-12-15 12:45
下一篇 2024-12-15 12:45

相關推薦

  • Python計算陽曆日期對應周幾

    本文介紹如何通過Python計算任意陽曆日期對應周幾。 一、獲取日期 獲取日期可以通過Python內置的模塊datetime實現,示例代碼如下: from datetime imp…

    編程 2025-04-29
  • Python周杰倫代碼用法介紹

    本文將從多個方面對Python周杰倫代碼進行詳細的闡述。 一、代碼介紹 from urllib.request import urlopen from bs4 import Bea…

    編程 2025-04-29
  • Python中引入上一級目錄中函數

    Python中經常需要調用其他文件夾中的模塊或函數,其中一個常見的操作是引入上一級目錄中的函數。在此,我們將從多個角度詳細解釋如何在Python中引入上一級目錄的函數。 一、加入環…

    編程 2025-04-29
  • Python列表中負數的個數

    Python列表是一個有序的集合,可以存儲多個不同類型的元素。而負數是指小於0的整數。在Python列表中,我們想要找到負數的個數,可以通過以下幾個方面進行實現。 一、使用循環遍歷…

    編程 2025-04-29
  • 如何查看Anaconda中Python路徑

    對Anaconda中Python路徑即conda環境的查看進行詳細的闡述。 一、使用命令行查看 1、在Windows系統中,可以使用命令提示符(cmd)或者Anaconda Pro…

    編程 2025-04-29
  • python強行終止程序快捷鍵

    本文將從多個方面對python強行終止程序快捷鍵進行詳細闡述,並提供相應代碼示例。 一、Ctrl+C快捷鍵 Ctrl+C快捷鍵是在終端中經常用來強行終止運行的程序。當你在終端中運行…

    編程 2025-04-29
  • 蝴蝶優化算法Python版

    蝴蝶優化算法是一種基於仿生學的優化算法,模仿自然界中的蝴蝶進行搜索。它可以應用於多個領域的優化問題,包括數學優化、工程問題、機器學習等。本文將從多個方面對蝴蝶優化算法Python版…

    編程 2025-04-29
  • Python字典去重複工具

    使用Python語言編寫字典去重複工具,可幫助用戶快速去重複。 一、字典去重複工具的需求 在使用Python編寫程序時,我們經常需要處理數據文件,其中包含了大量的重複數據。為了方便…

    編程 2025-04-29
  • Python清華鏡像下載

    Python清華鏡像是一個高質量的Python開發資源鏡像站,提供了Python及其相關的開發工具、框架和文檔的下載服務。本文將從以下幾個方面對Python清華鏡像下載進行詳細的闡…

    編程 2025-04-29
  • Python程序需要編譯才能執行

    Python 被廣泛應用於數據分析、人工智能、科學計算等領域,它的靈活性和簡單易學的性質使得越來越多的人喜歡使用 Python 進行編程。然而,在 Python 中程序執行的方式不…

    編程 2025-04-29

發表回復

登錄後才能評論