自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

用復(fù)雜的方式學(xué)會(huì)數(shù)組(Python實(shí)現(xiàn)動(dòng)態(tài)數(shù)組)

開(kāi)發(fā) 前端
通過(guò)以上的介紹,我們知道了數(shù)組存在靜態(tài)和動(dòng)態(tài)類(lèi)型。而在本博客中,我們著重介紹了什么是動(dòng)態(tài)數(shù)組,并通過(guò)Python代碼進(jìn)行實(shí)現(xiàn)。希望你能從此以復(fù)雜的方式學(xué)會(huì)數(shù)組??偨Y(jié)發(fā)言,其實(shí)越是簡(jiǎn)單的操作,背后實(shí)現(xiàn)原理可能很復(fù)雜。

Part1聊聊Python序列類(lèi)型的本質(zhì)

在本博客中,我們來(lái)聊聊探討Python的各種“序列”類(lèi),內(nèi)置的三大常用數(shù)據(jù)結(jié)構(gòu)——列表類(lèi)(list)、元組類(lèi)(tuple)和字符串類(lèi)(str)的本質(zhì)。

不知道你發(fā)現(xiàn)沒(méi)有,這些類(lèi)都有一個(gè)很明顯的共性,都可以用來(lái)保存多個(gè)數(shù)據(jù)元素,最主要的功能是:每個(gè)類(lèi)都支持下標(biāo)(索引)訪問(wèn)該序列的元素,比如使用語(yǔ)法 Seq[i]?。其實(shí)上面每個(gè)類(lèi)都是使用 數(shù)組 這種簡(jiǎn)單的數(shù)據(jù)結(jié)構(gòu)表示。

但是熟悉Python的讀者可能知道這3種數(shù)據(jù)結(jié)構(gòu)又有一些不同:比如元組和字符串是不能修改的,列表可以修改。

1、計(jì)算機(jī)內(nèi)存中的數(shù)組結(jié)構(gòu)

計(jì)算機(jī)體系結(jié)構(gòu)中,我們知道計(jì)算機(jī)主存由位信息組成,這些位通常被歸類(lèi)成更大的單元,這些單元?jiǎng)t取決于精準(zhǔn)的系統(tǒng)架構(gòu)。一個(gè)典型的單元就是一個(gè)字節(jié),相當(dāng)于8位。

計(jì)算機(jī)系統(tǒng)擁有龐大數(shù)量的存儲(chǔ)字節(jié),那么如何才能找到我們的信息存在哪個(gè)字節(jié)呢?答案就是大家平時(shí)熟知的 存儲(chǔ)地址 。基于存儲(chǔ)地址,主存中的任何字節(jié)都能被有效的訪問(wèn)。實(shí)際上,每個(gè)存儲(chǔ)字節(jié)都和一個(gè)作為其地址的唯一二進(jìn)制數(shù)字相關(guān)聯(lián)。如下圖中,每個(gè)字節(jié)均被指定了存儲(chǔ)地址:

圖片

一般來(lái)說(shuō),編程語(yǔ)言記錄標(biāo)識(shí)符和其關(guān)聯(lián)值所存儲(chǔ)的地址之間的關(guān)系。比如,當(dāng)我們聲明標(biāo)識(shí)符 x 就有可能和存儲(chǔ)器中的某一值相關(guān)聯(lián),而標(biāo)識(shí)符 y就可能和其他的值相關(guān)聯(lián)。一組相關(guān)的變量能夠一個(gè)接一個(gè)地存儲(chǔ)在計(jì)算機(jī)存儲(chǔ)器的一塊連續(xù)區(qū)域內(nèi)。我們將這種方式稱為 數(shù)組。

我們來(lái)看Python中的例子,一個(gè)文本字符串 HELLO 是以一列有序字符的形式存儲(chǔ)的,假定該字符串的每個(gè)Unicode字符需要兩個(gè)字節(jié)的存儲(chǔ)空間。最下面的數(shù)字就是該字符串的索引值。

圖片

我們可以看到,數(shù)組可以存儲(chǔ)多個(gè)值而無(wú)需構(gòu)造具有特定索引的多個(gè)變量來(lái)指定其中的每個(gè)項(xiàng)目,并且?guī)缀踉谒芯幊陶Z(yǔ)言(例如C、Java、C#、C++)中使用,但是Python更具有優(yōu)勢(shì)。Python在構(gòu)建列表時(shí),熟悉的讀者可能知道,不需要預(yù)先定義數(shù)組或列表的大小,相反,在Python中,列表具有動(dòng)態(tài)性質(zhì),我們可以不斷的往列表中添加我們想要的數(shù)據(jù)元素。接下來(lái),讓我們看看Python列表的知識(shí)(已經(jīng)熟悉的讀者可以快速瀏覽或者跳過(guò))。

2、Python列表

Python列表的操作

  • 創(chuàng)建列表的語(yǔ)法格式:

[ele1, ele2, ele3, ele4, ...]

  • 創(chuàng)建元組的語(yǔ)法格式:

(ele1, ele2, ele3, ele4, ...)

元組比列表的內(nèi)存空間利用率更高,因?yàn)樵M是固定不變的,所以沒(méi)有必要?jiǎng)?chuàng)建擁有剩余空間的動(dòng)態(tài)數(shù)組。

我們先在Python的IDE中創(chuàng)建一個(gè)列表,然后大致了解一下列表部分內(nèi)置操作,我們先創(chuàng)建了一個(gè)名為test_list的列表,然后修改(插入或刪除)元素,反轉(zhuǎn)或清空列表,具體如下:

>>> test_list = []  # 創(chuàng)建名為test_list的空列表
>>> test_list.append("Hello")
>>> test_list.append("World")
>>> test_list
['Hello', 'World']
>>> test_list = ["Hello", "Array", 2019, "easy learning", "DataStructure"] # 重新給test_list賦值
>>> len(test_list) # 求列表的長(zhǎng)度
5
>>> test_list[2] = 1024 # 修改列表元素
>>> test_list
['Hello', 'Array', 1024, 'easy learning', 'DataStructure']
>>>
>>> test_list.insert(1, "I love") # 向列表中指定位置中插入一個(gè)元素
>>> test_list
['Hello', 'I love', 'Array', 1024, 'easy learning', 'DataStructure']
>>> test_list.append(2020) # 向列表末尾增加一個(gè)元素
>>> test_list
['Hello', 'I love', 'Array', 1024, 'easy learning', 'DataStructure', 2020]
>>>
>>> test_list.pop(1) # 刪除指定位置的元素
'I love'
>>> test_list.remove(2020) # 刪除指定元素
>>>
>>> test_list.index('Hello') # 查找某個(gè)元素的索引值
0
>>> test_list.index('hello') # 如果查找某個(gè)元素不在列表中,返回ValueError錯(cuò)誤
Traceback (most recent call last):
File "<pyshell#11>", line 1, in <module>
test_list.index('hello')
ValueError: 'hello' is not in list
>>>
>>> test_list.reverse() # 反轉(zhuǎn)整個(gè)列表
>>> test_list
['DataStructure', 'easy learning', 2019, 'Array', 'Hello']
>>> test_list.clear() # 清空列表
>>> test_list
[]

我們看上面的代碼,可以看到list的相關(guān)操作——增刪改查,已經(jīng)很強(qiáng)大了,還有一些內(nèi)置方法這里并沒(méi)有做展示,留給讀者自己去發(fā)現(xiàn)并體驗(yàn)。

Python列表的內(nèi)存分配背后的基礎(chǔ)知識(shí)

因此,讓我們通過(guò)編碼實(shí)踐以及內(nèi)存中保存的數(shù)組的實(shí)際大小與給定大小之間的關(guān)系來(lái)查看這種額外的空間演示。

前往Jupyter notebook進(jìn)行練習(xí)?;蛘呤褂米约哼x擇的任何編輯器或開(kāi)發(fā)環(huán)境。復(fù)制下面編寫(xiě)的代碼。

# 導(dǎo)入sys模塊能方便我們使用gestsizeof函數(shù)
import sys

# set n
n = 20
# set empty list
list = []
for i in range(n):
a = len(list)
# 調(diào)用getsizeof函數(shù)用于給出Python中存儲(chǔ)對(duì)象的真實(shí)字節(jié)數(shù)
b = sys.getsizeof(list)
print('Length:{0:3d}; Size of bytes:{1:4d}'.format(a, b))
# Increase length by one
list.append(n)

運(yùn)行代碼,可以看到如下輸出:

圖片

現(xiàn)在,隨著我們?cè)黾恿斜淼拈L(zhǎng)度,字節(jié)也增加了。我們分析一下,Length:1

位置的元素填入列表時(shí),字節(jié)數(shù)從64跳到96,增加了32個(gè)字節(jié)。因?yàn)楸緦?shí)驗(yàn)是在64位機(jī)器上運(yùn)行的,這表明每個(gè)內(nèi)存地址是64位(即8個(gè)字節(jié))。增加的32個(gè)字節(jié)即為分配的用于存儲(chǔ)4個(gè)對(duì)象引用的數(shù)組大小。當(dāng)增加第2個(gè)、第3個(gè)或者第4個(gè)元素時(shí),內(nèi)存占用沒(méi)有任何改變。字節(jié)數(shù)96能夠提供4個(gè)對(duì)象的引用。

96\ =\ 64\ +\ 8\ \times \ 4

當(dāng)Length:10

時(shí),字節(jié)數(shù)從一開(kāi)始的64跳到192,能存下16個(gè)對(duì)象的引用,

192\ =\ 64\ +\ 8\ \times \ 16

一直到Length: 17

后又開(kāi)始跳轉(zhuǎn),所以理論上264個(gè)字節(jié)數(shù)應(yīng)該可以存下25個(gè)對(duì)象

264\ =\ 64\ +\ 8\ \times \ 25

但因?yàn)槲覀冊(cè)诖a中設(shè)置n=20,然后程序就終止了。

我們可以看到Python內(nèi)置的list類(lèi)足夠智能,知道當(dāng)需要額外的空間來(lái)分配數(shù)據(jù)時(shí),它會(huì)為它們提供額外的大小,那么這究竟是如何被實(shí)現(xiàn)的呢?

好吧,答案是動(dòng)態(tài)數(shù)組。說(shuō)到這里,不知道大家學(xué)Python列表的時(shí)候是不是這樣想的——列表很簡(jiǎn)單嘛,就是list()類(lèi)、用中括號(hào)[]括起來(lái),然后指導(dǎo)書(shū)籍或文檔上的各類(lèi)方法append、insert、pop...在各種IDE一頓操作過(guò)后,是的我覺(jué)得我學(xué)會(huì)了。

但其實(shí)背后的原理真的很不簡(jiǎn)單,比如我舉個(gè)例子:A[-1]這個(gè)操作怎么實(shí)現(xiàn)?列表切片功能怎么實(shí)現(xiàn)?如何自己寫(xiě)pop()默認(rèn)刪除列表最右邊的元素(popleft刪除最左邊簡(jiǎn)單)?...這些功能用起來(lái)爽,但真的自己實(shí)現(xiàn)太難了(我也還在學(xué)習(xí)中,大佬們請(qǐng)輕噴!)如果我們能學(xué)習(xí)并理解,肯定可以加強(qiáng)我們對(duì)數(shù)組這一結(jié)構(gòu)的理解。

3、動(dòng)態(tài)數(shù)組

什么是動(dòng)態(tài)數(shù)組

動(dòng)態(tài)數(shù)組是內(nèi)存的連續(xù)區(qū)域,其大小隨著插入新數(shù)據(jù)而動(dòng)態(tài)增長(zhǎng)。在靜態(tài)數(shù)組中,我們需要在分配時(shí)指定大小。在定義數(shù)組的時(shí)候,其實(shí)計(jì)算機(jī)已經(jīng)幫我們分配好了內(nèi)存來(lái)存儲(chǔ),實(shí)際上我們不能擴(kuò)展數(shù)組,因?yàn)樗拇笮∈枪潭ǖ?。比如:我們分配一個(gè)大小為10的數(shù)組,則不能插入超過(guò)10個(gè)項(xiàng)目。

但是動(dòng)態(tài)數(shù)組會(huì)在需要的時(shí)候自動(dòng)調(diào)整其大小。這一點(diǎn)有點(diǎn)像我們使用的Python列表,可以存儲(chǔ)任意數(shù)量的項(xiàng)目,而無(wú)需在分配時(shí)指定大小。

所以實(shí)現(xiàn)一個(gè)動(dòng)態(tài)數(shù)組的實(shí)現(xiàn)的關(guān)鍵是——如何擴(kuò)展數(shù)組?當(dāng)列表list1的大小已滿時(shí),而此時(shí)有新的元素要添加進(jìn)列表,我們會(huì)執(zhí)行一下步驟來(lái)克服其大小限制的缺點(diǎn):

  1. 分配具有更大容量的新數(shù)組list2
  2. 設(shè)置list2[i] = list1[i] (i=0,1,2,...,n-1),其中n是該項(xiàng)目的當(dāng)前編號(hào)
  3. 設(shè)置list1 = list2,也就是說(shuō),list2正在作為新的數(shù)組來(lái)引用我們的新列表。
  4. 然后,只要將新的元素插入(添加)到我們的列表list1即可。

圖片

接下來(lái)要思考的問(wèn)題是,新數(shù)組應(yīng)該多大?通常我們得做法是:新數(shù)組的大小是已滿的舊數(shù)組的2倍。我們將在Python中編程實(shí)現(xiàn)動(dòng)態(tài)數(shù)組的概念,并創(chuàng)建一個(gè)簡(jiǎn)單的代碼,很多功能不及Python強(qiáng)大。

實(shí)現(xiàn)動(dòng)態(tài)數(shù)組的Python代碼

在Python中,我們利用ctypes的內(nèi)置庫(kù)來(lái)創(chuàng)建自己的動(dòng)態(tài)數(shù)組類(lèi),因?yàn)閏types模塊提供對(duì)原始數(shù)組的支持,為了更快的對(duì)數(shù)組進(jìn)行學(xué)習(xí),所以對(duì)ctypes的知識(shí)可以查看官方文檔進(jìn)行學(xué)習(xí)。關(guān)于Python的公有方法與私有方法,我們?cè)诜椒Q前使用雙下劃線**__**使其保持隱藏狀態(tài),代碼如下:

# -*- coding: utf-8 -*-
# @Time : 2019-11-01 17:10
# @Author : yuzhou_1su
# @ContactMe : https://blog.csdn.net/yuzhou_1shu
# @File : DynamicArray.py
# @Software : PyCharm

import ctypes


class DynamicArray:
"""A dynamic array class akin to a simplified Python list."""

def __init__(self):
"""Create an empty array."""
self.n = 0 # count actual elements
self.capacity = 1 # default array capacity
self.A = self._make_array(self.capacity) # low-level array

def is_empty(self):
""" Return True if array is empty"""
return self.n == 0

def __len__(self):
"""Return numbers of elements stored in the array."""
return self.n

def __getitem__(self, i):
"""Return element at index i."""
if not 0 <= i < self.n:
# Check it i index is in bounds of array
raise ValueError('invalid index')
return self.A[i]

def append(self, obj):
"""Add object to end of the array."""
if self.n == self.capacity:
# Double capacity if not enough room
self._resize(2 * self.capacity)
self.A[self.n] = obj # Set self.n index to obj
self.n += 1

def _resize(self, c):
"""Resize internal array to capacity c."""
B = self._make_array(c) # New bigger array
for k in range(self.n): # Reference all existing values
B[k] = self.A[k]
self.A = B # Call A the new bigger array
self.capacity = c # Reset the capacity

@staticmethod
def _make_array(c):
"""Return new array with capacity c."""
return (c * ctypes.py_object)()

def insert(self, k, value):
"""Insert value at position k."""
if self.n == self.capacity:
self._resize(2 * self.capacity)
for j in range(self.n, k, -1):
self.A[j] = self.A[j-1]
self.A[k] = value
self.n += 1

def pop(self, index=0):
"""Remove item at index (default first)."""
if index >= self.n or index < 0:
raise ValueError('invalid index')
for i in range(index, self.n-1):
self.A[i] = self.A[i+1]
self.A[self.n - 1] = None
self.n -= 1

def remove(self, value):
"""Remove the first occurrence of a value in the array."""
for k in range(self.n):
if self.A[k] == value:
for j in range(k, self.n - 1):
self.A[j] = self.A[j+1]
self.A[self.n - 1] = None
self.n -= 1
return
raise ValueError('value not found')

def _print(self):
"""Print the array."""
for i in range(self.n):
print(self.A[i], end=' ')
print()

測(cè)試動(dòng)態(tài)數(shù)組Python代碼

上面我們已經(jīng)實(shí)現(xiàn)了一個(gè)動(dòng)態(tài)數(shù)組的類(lèi),相信都很激動(dòng),接下來(lái)讓我們來(lái)測(cè)試一下,看能不能成功呢?在同一個(gè)文件下,寫(xiě)的測(cè)試代碼如下:

def main():
# Instantiate
mylist = DynamicArray()

# Append new element
mylist.append(10)
mylist.append(9)
mylist.append(8)
# Insert new element in given position
mylist.insert(1, 1024)
mylist.insert(2, 2019)
# Check length
print('The array length is: ', mylist.__len__())
# Print the array
print('Print the array:')
mylist._print()
# Index
print('The element at index 1 is :', mylist[1])
# Remove element
print('Remove 2019 in array:')
mylist.remove(2019)
mylist._print()
# Pop element in given position
print('Pop pos 2 in array:')
# mylist.pop()
mylist.pop(2)
mylist._print()


if __name__ == '__main__':
main()

測(cè)試結(jié)果

激動(dòng)人心的時(shí)刻揭曉,測(cè)試結(jié)果如下。請(qǐng)結(jié)合測(cè)試代碼和數(shù)組的結(jié)構(gòu)進(jìn)行理解,如果由疏漏,歡迎大家指出。

The array length is:  5
Print the array:
10 1024 2019 9 8
The element at index 1 is : 1024
Remove 2019 in array:
10 1024 9 8
Pop pos 2 in array:
10 1024 8

Part2總結(jié)

通過(guò)以上的介紹,我們知道了數(shù)組存在靜態(tài)和動(dòng)態(tài)類(lèi)型。而在本博客中,我們著重介紹了什么是動(dòng)態(tài)數(shù)組,并通過(guò)Python代碼進(jìn)行實(shí)現(xiàn)。希望你能從此以復(fù)雜的方式學(xué)會(huì)數(shù)組。總結(jié)發(fā)言,其實(shí)越是簡(jiǎn)單的操作,背后實(shí)現(xiàn)原理可能很復(fù)雜。

責(zé)任編輯:武曉燕 來(lái)源: 宇宙之一粟
相關(guān)推薦

2010-02-03 10:11:17

C++動(dòng)態(tài)數(shù)組

2021-04-25 14:29:02

數(shù)據(jù)結(jié)構(gòu)動(dòng)態(tài)數(shù)組時(shí)間復(fù)雜度

2009-05-07 13:36:38

Java靜態(tài)數(shù)組動(dòng)態(tài)數(shù)組

2021-08-24 09:39:23

ReduceJS數(shù)組

2009-09-02 16:20:22

C#動(dòng)態(tài)創(chuàng)建數(shù)組

2009-09-02 16:14:21

C#動(dòng)態(tài)創(chuàng)建數(shù)組

2014-11-13 10:22:18

Multi-cloud亞馬遜Web服務(wù)云數(shù)據(jù)庫(kù)

2009-11-25 15:50:53

PHP刪除數(shù)組空白元素

2009-09-02 11:02:57

C#動(dòng)態(tài)數(shù)組

2009-09-17 18:07:22

C#動(dòng)態(tài)數(shù)組

2024-11-28 10:09:06

2009-10-19 11:26:08

Scala循環(huán)數(shù)組

2021-04-13 20:52:15

NumPy數(shù)組

2009-09-17 17:44:51

C#動(dòng)態(tài)數(shù)組

2009-09-17 17:40:36

C#動(dòng)態(tài)數(shù)組

2009-09-17 18:14:05

C#動(dòng)態(tài)數(shù)組

2009-09-02 11:18:10

C#動(dòng)態(tài)數(shù)組

2011-06-08 14:42:33

C++多維數(shù)組

2009-11-25 11:42:34

PHP判斷數(shù)組為空

2010-01-12 15:54:47

VB.NET動(dòng)態(tài)控件數(shù)
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)