在本教程中,我們將學習 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
上面的代碼在執行過程中被分解。
- 創建一個對象
- 將 PyObject 的 typecode 設置為整數
- 將對象的值設置為 289
- 創建一個名為 x 的名稱
- 指向新的對象
- 將對象的引用計數增加 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-tw/n/257510.html