學好Python,必須要深刻理解"對象"

搞IT的都常說“人生苦短,我學python”,生動形象的描述了python在當今IT界的地位。我們都知道python數據高級語言,屬於面向對象編程。那麼什麼是對象呢?怎麼操作對象呢?看起來很是模糊,沒關係,本片我們就深度解析對對象的理解。

學好Python,必須要深刻理解

喜歡的,關注喲!

點開這首音樂,我們開始吧。

一、面向對象編程和麵向過程編程區別

先看一段代碼,實現兩個字符串的拼接,代碼如下:

<code># c代碼如下
#include<stdio.h>
#include<string.h>

int main(){
\tchar st1[]="hello ";
\tchar *st2="world!";
\tstrcat(st1,st2);
\tprintf("%s\\n",st1);
\treturn 0;
}

#python代碼如下
st1 = "hello "
st2 = "world!"
print(st1 + st2)/<string.h>/<stdio.h>/<code>
學好Python,必須要深刻理解

簡短的三行

我們來分析下,c語言每個字符都需要去申請內存,有自動分配的,也有我們主動申請的-malloc,操作字符串拼接就是要去操作指針,還好c庫給我封裝好了strcat函數方便了我們去操作。但是python中我們直接用"+"就可以實現拼接,為什麼呢?我們來看下。

<code>st1 = "hello "
st2 = "world!"
print(st1 + st2)
print(type(st1))
print(st1.__add__("world!"))
"""
def __add__(self, *args, **kwargs): # real signature unknown
Return self+value.
pass
"""/<code>
學好Python,必須要深刻理解

str類內置的方法_add__

我們的對象“st1”是字符串類的實例化對象,它包含自增的方法。c語言運算後的地址不變,那麼我們的這步操作雖然實現了字符串拼接功能,但是還是"它本身"嗎?

面向過程--怎麼做?

1.把完成某一個需求的所有步驟從頭到尾逐步實現

2.根據開發需求,將某些功能獨立的代碼封裝成一個又一個的函數

3.最後完成的代碼,就是順序地調用不同的函數

面向對象--誰來做事情?

相比較函數,面向對象是更大的封裝,根據職責在一個對象重封裝多個方法

1.在完成某一個需求前,需要確定職責--要做的事情(方法)

2.根據職責確定不同的對象,在對象內部封裝不同的方法(多個)

3.最後完成的代碼,就是順序地讓不同的對象調用不同的方法

引子結束了,下面我們進入核心部分。

二、對象的理解

我們先看下官方的解釋:

  • 每個變量都有標識、類型和值,對象一旦創建,它的標識絕不會變
  • 標識理解為對象在內存中的地址,is運算符比較兩個對象的標識,id函數返回對象標識
  • 值可以改變的對象被稱為可變對象,值不可以改變的對象就被稱為不可變對象

我自己的理解如下:

  • 實實在在存在的東西,有自己的內存地址【數字、列表、字符串、元組、字典、集合、函數、模塊、類、等等】
  • 判斷變量是否引用同一個對象只能用is或者id()去判斷
  • ==用來比較兩個對象結果相等,因為對象的 __eq__ 方法就是這樣實現的
  • 對象的駐留機制【數字和字符串】

直接上代碼:

<code># id函數或者is判斷同一對象,==只能判斷值,不能判斷地址
a = [1, 2, 3]
b = [1, 2, 3]
if a == b:
print(True) # True
if a is not b:
print(True) # True
print(id(a), id(b)) # 27715984 27735624
b = a
print(id(a), id(b)) # 27715984 27715984
a[2] = [1, '2']
c = [1, 2, [1, '2']]
print(id(a), id(b), id(c)) # 27715984 27715984 27734424

# 字符串和數字的駐留性
a1 = (1, 2, 3)
b1 = (1, 2, 3)
print(id(a1), id(b1)) # 27735024 27735104

a2 = '500'
b2 = '500'
print(id(a2), id(b2)) # 27250400 27250400

a3 = [1, 3, '3']
b3 = [1, 3, '3']
print(id(a3), id(b3)) # 27735424 27736464

a4 = 1000
b4 = 1000
print(id(a4), id(b4)) # 27760032 27760032/<code>
學好Python,必須要深刻理解

對象的理解

通俗點講,針對元組和列表,同一包糖,放我這是我的,放你那就是你的,是兩個東西。針對數字和字符串,我的糖就是我的,那在你那也是我的,是一個東西。對於駐留機制,有興趣的可以自己測試一下,有數據限制的。

學好Python,必須要深刻理解

穿暖花開,陽光正好!

對象可變與不可變理解:

  • 1.不可變對象:數值、字符串、元組、集合(frozenset)
  • 2.可變對象:列表、字典、集合(set)
  • 3.可改變與不可變指的是否可以在對象原處改變
  • 4.不可變序列包含可變序列可對可變序列逐個元素更改達到“可變”的目的,不可整體改變可改變序列

變量:

對對象的引用,指向對象內存空間,刪除變量並非一定刪除對象,對象的引用數為0時才回收對象。

變量賦值:

  • 1.基本賦值
  • 2.元組賦值
  • 3.列表賦值
  • 4.字符串賦值
  • 5.序列解包
  • 6.多目標賦值
  • 7.增強賦值
  • 8.深拷貝、淺拷貝

按照方式分為【1-7】、按照類型分為【1-7、8】---【對象本身、拷貝對象】、賦值操作---引用賦值,是變量對---對象引用的動作。

代碼示例如下:

<code># 賦值方式
Var = 888 # 直接賦值,任意對象,包括拷貝對象
Var_1, Var_2 = 666, 888 # 元組賦值
Var_1 += 666 # 自我運算賦值【區別見數據類型中分析】
[Var_1, Var_2] = [666, 888] # 列表賦值
Var_1, Var_2 = 'py' # 字符串賦值
Var_1, *Var_2 = 'python' # 序列解析賦值
Var_1 = Var_2 = 'python' # 多目標賦值,多個引用/<code>

引用賦值:

變量【各個引用】指向同一對象的內存地址【值相等、地址相同】

針對不可變對象,不可在原處改變,所以修改值相當於創建了新對象,指向的地址也就改變了。

針對可變對象,可以在原處改變值,並沒有創建新對象,所以各個變量指向的內存地址不會改變。

代碼示例如下:

<code># 賦值:引用可改變對象
# 原地修改,地址不變,各個引用值都修改
Var_1 = ["py", "life"]
Var_2 = Var_1
print(id(Var_1), id(Var_2)) # 51769432 51769432
Var_2.append("!")
print(id(Var_1), id(Var_2)) # 51769432 51769432
print(Var_1, Var_2) # ['py', 'life', '!'] ['py', 'life', '!']

# 賦值:引用不可改變對象
# 修改引用的值創建並引用了新對象--地址及值改變,其它引用值和地址不變
Var_1 = "py-life"
Var_2 = Var_1
print(id(Var_1), id(Var_2)) # 51785568 51785568
Var_2 = "python"
print(id(Var_1), id(Var_2)) # 51785568 51746312
print(Var_1, Var_2) # py-life python/<code>

淺拷貝:

切片操作[:]、工廠函數list、copy函數copy.copy()

1.淺拷貝會產生新的對象,即有新的內存地址,但成員是引用原對象的成員,所以成員地址不會改變

2.修改成員的值相當於創建了新對象,成員的地址會改變,但整體地址不會改變

3.淺拷貝只拷貝單層,不會拷貝嵌套【列表成員包含列表,修改內層列表的值,都會改變】

代碼示例如下:

<code># 賦值:引用淺拷貝對象
# 創建了新對象,成員是引用的已有的對象成員,包括嵌套--單層拷貝
import copy
a = ["py", [1, 2]]
b = copy.copy(a)
print(id(a), id(b)) # 55569064 55569824
for x in range(len(a)):
print(id(a[x]), id(b[x])) # 25426272 25426272、55568824 55568824
b.append(4)
b[1].append(3)
print(id(a[1]), id(b[1])) # 55568824 55568824
print(a, b) # ['py', [1, 2, 3]] ['py', [1, 2, 3], 4]/<code>

深拷貝:

copy函數copy.deepcopy()

1.產生新的對象,即有新的內存地址,但成員是引用原對象的成員,所以成員地址不會改變【單層】

2.多層拷貝,嵌套的複雜數據對象地址改變【嵌套層】

2.操作拷貝來的對象不會影響原始數據對象

代碼示例如下:

<code># 賦值:引用深拷貝對象
# 創建了新對象,成員是引用的已有的對象成員,不包括嵌套--多層拷貝

import copy
a = ["py", [1, 2]]
b = copy.deepcopy(a)
print(id(a), id(b)) # 11857232 11856552
for x in range(len(a)):
print(id(a[x]), id(b[x])) # 10025312 10025312、11857272 11857192
b.append(4)
b[1].append(3)
print(a, b) # ['py', [1, 2]] ['py', [1, 2, 4], 4]/<code>

我們在工作中,經常會使用臨時變量去操作數據,要根據自己的需要合理的拷貝數據備份,否則會出現改動源數據的情況,這就很不友好了。

到這裡,基本就結束了,面向對象編程這是基礎,需要牢固掌握,希望大家都能學好python!如有出錯,懇請指正!喜歡的點贊、評論、轉發、關注,謝謝!

學好Python,必須要深刻理解

謝謝!


分享到:


相關文章: