Python進階 - 對象,名字以及綁定

来源:http://www.cnblogs.com/crazyrunning/archive/2017/05/26/6909733.html
-Advertisement-
Play Games

Python進階 對象,名字以及綁定 1、一切皆對象 哲學: Python中一切皆對象 1.1 數據模型 對象,值以及類型 對象是 對數據的抽象。 程式中所有的數據都是對象或對象之間的關係表示的。(在某種意義上,為順應馮·諾依曼“存儲式電腦”的模型, 中的代碼也是對象。) 中每一個對象都有一個身份 ...


Python進階 - 對象,名字以及綁定

1、一切皆對象

Python哲學:

Python中一切皆對象

1.1 數據模型-對象,值以及類型

對象是Python對數據的抽象。Python程式中所有的數據都是對象或對象之間的關係表示的。(在某種意義上,為順應馮·諾依曼“存儲式電腦”的模型,Python中的代碼也是對象。)

Python中每一個對象都有一個身份標識,一個值以及一個類型。對象創建後,其身份標識絕對不會改變;可以把身份標識當做對象在記憶體中的地址。is操作符比較兩個對象的身份標識;id()函數返回表示對象身份標識的整數。

CPython實現細節:CPython解釋器的實現中,id(x)函數返回存儲x的記憶體地址

對象的類型決定了對象支持的操作(例如,對象有長度麽?),同時也決定了該類型對象可能的值。type()函數返回對象的類型(這個類型本身也是一個對象)。與其身份標識一樣,對象的類型也是不可改變的[1]

一些對象的值可以改變。可改變值的對象也稱作可變的(mutable);一旦創建,值恆定的對象也叫做 不可變的(immutable)。(當不可變容器對象中包含對可變對象的引用時,可變對象值改變時,這個不可變容器對象值也被改變了;然而,不可變容器對象仍被認為是不可變的,因為對象包含的值集合確實是不可改變的。因此,不可變性不是嚴格等同於擁有不可變的值,它很微妙。) (譯註:首先不可變容器對象的值是一個集合,集合中包含了對其他對象的引用;那麼這些引用可以看做地址,即使地址指向的內容改變了,集合中的地址本身是沒有改變的。所以不可變容器對象還是不可變對象。) 對象的可變性取決於其類型;例如,數字,字元串和元組是不可變的,但字典和列表是可變的。

對象從不顯式銷毀;當對象不可達時會被垃圾回收(譯註:對象沒有引用了)。一種解釋器實現允許垃圾回收延時或者直接忽略——這取決於垃圾回收是如何實現的,只要沒有可達對象被回收。

CPython實現細節: CPython解釋器實現使用引用計數模式延時探測迴圈鏈接垃圾,這種方式可回收大多數不可達對象,但並不能保證迴圈引用的垃圾被回收。查看gc模塊的文檔瞭解控制迴圈垃圾回收的更多信息。其他解釋器實現與CPython不同,CPython實現將來也許會改變。因此不能依賴垃圾回收器來回收不可達對象(因此應該總是顯式關閉文件對象。)。

需要註意,使用工具的調試跟蹤功能可能會導致應該被回收的對象一直存活,使用try...except語句捕獲異常也可以保持對象的存活。

一些對象引用瞭如文件或者視窗的外部資源。不言而喻持有資源的對象被垃圾回收後,資源也會被釋放,但因為沒有機制保證垃圾回收一定會發生,這些資源持有對象也提供了顯式釋放外部資源的方式,通常使用close()方法。強烈推薦在程式中顯式釋放資源。try...finally語句和with語句為釋放資源提供了便利。

一些對象包含對其他對象的引用,這些對象被稱作 容器。元組,列表和字典都是容器。引用的容器值的一部分。大多數情況下,談論容器的值時,我們暗指容器包含的對象值集合,而不是對象的身份標識集合;然而,談論容器的可變性時,我們暗指容器包含的對象的身份標識。因此,如果不可變對象(如元組)包含對可變對象的引用,可變對象改變時,其值也改變了。

類型影響對象的絕大多數行為。在某些情況下甚至對象的身份標識的重要性也受到影響:對於不可變類型,計算新值的操作實際上可能會返回已存在的,值和類型一樣的對象的引用,然而對於可變對象來說這是不可能的。例如,語句a = 1; b = 1執行之後,ab可能會也可能不會引用具有相同值得同一個對象,這取決於解釋器實現。但是語句c = []; d = []執行之後,可以保證cd會指向不同的,唯一的新創建的空列表。(註意 c = d = []分配相同的對象給cd)

Note: 以上翻譯自 《The Python Language References#Data model# Objects, values, types》 3.6.1版本。

1.2 對象小結

官方文檔已經對Python 對象做了詳細的描述,這裡總結一下。

對象的三個特性:

  • 身份標識
    唯一標識對象;不可變;CPython解釋器實現為對象的記憶體地址。
    操作:id(),內建函數id()函數返回標識對象的一個整數;is比較兩個對象的身份標識。
    示例:

    >>> id(1)
    1470514832
    >>> 1 is 1
    True
  • 類型
    決定對象支持的操作,可能的值;不可變。
    操作:type(),內建函數返回對象的類型
    示例:

    >>> type('a')
    <class 'str'>

  • 數據,可變/不可變
    操作:==操作符用於比較兩個對象的值是否相等,其他比較運算符比較對象間大小情況。
    示例:

    >>> 'python'
    'python'
    >>> 1 == 2
    False

可變與不可變:一般認為,值不可變的對象是不可變對象,值可變的對象是可變對象,但是要註意不可變集合對象包含可變對象引用成員的情況。

Python中的對象:

# -*- coding: utf-8 -*-
# filename: hello.py

'a test module'

__author__ = 'Richard Cheng'

import sys

class Person(object):
    ''' Person class'''

    def __init__(self, name, age):
        self.name = name
        self.age = age


def tset():
    print(sys.path)
    p = Person('Richard', 20)
    print(p.name, ':', p.age)

def main():
    tset()

if __name__ == '__main__':
    main()

這段Python代碼中有很多對象,包括hello這個模塊對象,創建的Person類對象,幾個函數如test, main函數對象,數字,字元串,甚至代碼本身也是對象。

2、名字即“變數”

幾乎所有語言中都有“變數”的說法,嚴格說來,Python中的變數不應該叫變數,稱為名字更加貼切。

以下翻譯自 Code Like a Pythonista: Idiomatic Python # Python has "names"

2.1 其他語言有變數

其他語言中,為變數分配值就像將值放到“盒子”里。
int a = 1;

把1放到盒子里

盒子a現在有了一個整數1

為同一個變數分配值替換掉盒子的內容:
a =2;

把2放到同一個盒子里

現在盒子a中放了整數2

將一個變數分配給另一個變數,拷貝變數的值,並把它放到新的盒子里:
int b = a;

b盒子里放了2

a盒子里也放了2

b是第二個盒子,裝有整數2的拷貝。盒子a有一份單獨的拷貝。

2.2 Python有名字

Python中,名字或者標識符就像將一個標簽捆綁到對象上一樣。
a = 1

將名字a捆綁到對象1上

這裡,整數對象1有一個叫做a的標簽。

如果重新給a分配值,只是簡單的將標簽移動到另一個對象:
a = 2

標簽a移動到另一個對象
1對象沒有標簽了

現在名字a貼到了整數對象2上面。原來的整數對象1不再擁有標簽a,或許它還存在,但是不能通過標簽a訪問它了(當對象沒有任何引用時,會被回收。)

如果將一個名字分配給另一名字,只是將另一個名字標簽捆綁到存在的對象上:
b = a

有兩個標簽的對象

名字b只是綁定到與a引用的相同對象上的第二個標簽而已。

雖然在Python中普遍使用“變數”(因為“變數”是普遍術語),真正的意思是名字或者標識符。Python中的變數是值得標簽,不是裝值得盒子。

2.3 指針?引用?名字?

C/C++中有指針,Java中有引用,Python中的名字在一定程度上等同於指針和引用。

2.1節中其他語言的例子,也只是針對於它們的基本類型而言的,若是指針或者引用,表現也跟Python的名字一樣。這也在一定程度上說明瞭Python將面向對象貫徹得更加徹底。

2.4 名字支持的操作

可以對一個變數做什麼?聲明變數,使用變數,修改變數的值。名字作為Python中的一個重要概念,可以對它做的操作有:

  • 定義;名字需要先定義才能使用,與變數需要先聲明一樣。
  • 綁定:名字的單獨存在沒有意義,必須將它綁定到一個對象上。
  • 重綁定:名字可以重新引用另一個對象,這個操作就是重綁定。
  • 引用:為什麼要定義名字,目的是使用它。

3、綁定的藝術

名字以及對象,它們之間必然會發生些什麼。

3.1 變數的聲明

其他如C/C++Java的高級語言,變數在使用前需要聲明,或者說定義。以下在Java中聲明變數:

public static void main(String[] args) {
        int i = 0; // 先聲明,後使用
        System.out.println(i); // 使用變數i
}

這樣,在可以訪問到變數i所在作用域的地方,既可以使用i了。還有其他聲明變數的方法麽?好像沒有了。

3.2 名字的定義

Python中有多種定義名字的途徑,如函數定義,函數名就是引用函數對象的名字;類定義,類名就是指向類對象的名字,模塊定義,模塊名就是引用模塊對象的名字;當然,最直觀的還是賦值語句。

賦值語句

官方對賦值語句做了這樣的說明(地址):

Assignment statements are used to (re)bind names to values and to modify attributes or items of mutable objects.

即:

賦值語句被用來將名字綁定或者重綁定給值,也用來修改可變對象的屬性或項

那麼,我們關心的,就是賦值語句將名字和值(對象)綁定起來了。

看一個簡單的賦值語句:

a = 9

Python在處理這條語句時:

  1. 首先在記憶體中創建一個對象,表示整數9:

整數9對象

  1. 然後創建名字a,並把它指向上述對象:

名字a引用對象

上述過程就是通過賦值語句的名字對象綁定了。名字首次和對象綁定後,這個名字就定義在當前命名空間了,以後,在能訪問到這個命名空間的作用域中可以引用該名字了。

3.3 引用不可變對象

定義完名字之後,就可以使用名字了,名字的使用稱為“引用名字”。當名字指向可變對象和不可變對象時,使用名字會有不同的表現。

a = 9       #1
a = a + 1   #2

語句1執行完後,名字a指向表示整數9的對象:

名字a引用對象

由於整數是不可變對象,所以在語句2處引用名字a,試圖將表示整數9的對象 + 1,但該對象的值是無法改變的。因此就將該對象表示的整數值91,以整數10新建一個整數對象:

新建表示10的整數對象

接下來,將名字a 重綁定 到新建對象上,並移除名字對原對象的引用:

重綁定

使用id()函數,可以看到名字a指向的對象地址確實發生了改變:

>>> a = 9
>>> id(a)
1470514960
>>> a = a + 1
>>> id(a)
1470514976
3.4 引用可變對象
3.4.1 示例1:改變可變對象的值

可變對象可以改變其值,並且不會造成地址的改變:

>>> list1 = [1]
>>> id(list1)
42695136
>>> list1.append(2)
>>> id(list1)
42695136
>>> list1
[1, 2]
>>> 

執行語句list1 = [1],創建一個list對象,並且其值集中添加1,將名字list1指向該對象:

初始集合

執行語句list1.append(2),由於list是可變對象,可以直接在其值集中添加2

添加值

值得改變並沒有造成list1引用的對象地址的改變。

3.4.2 示例2:可變對象迴圈引用

再來看一個比較“奇怪”的例子:

values = [1, 2, 3]
values[1] = values
print(values)

一眼望去,期待的結果應該是

[1, [1, 2, 3], 3]

但實際上結果是:

[1, [...], 3]

我們知道list中的元素可以是各種類型的,list類型是可以的:

迴圈引用

3.4.3 示例3:重綁定可變對象

觀察以下代碼段:

>>> list1 = [1]
>>> id(list1)
42695136
>>> list1 = [1, 2]
>>> id(list1)
42717432

兩次輸出的名字list1引用對象的地址不一樣,這是因為第二次語句list 1 = [1, 2] 對名字做了重綁定:

重綁定

3.5 共用對象

當兩個或兩個以上的名字引用同一個對象時,我們稱這些名字共用對象。共用的對象可變性不同時,表現會出現差異。

3.5.1 共用不可變對象

函數attempt_change_immutable將參數i的值修改為2

def attempt_change_immutable(i):
    i = 2

i = 1
print(i)
attempt_change_immutable(i)
print(i)

Output:

1
1

如果你對輸出不感到意外,說明不是新手了 ^_^。

  1. 首先,函數的參數i與全局名字i不是在同一命名空間中,所以它們之間不相互影響。
  2. 調用函數時,將兩個名字i都指向了同一個整數對象。
  3. 函數中修改i的值為2, 因為整數對象不可變,所以新建值為2的整數對象,並把函數中的名字i綁定到對象上。
  4. 全局名字i的綁定關係並沒有被改變。

兩個i指向同一個對象

改變函數i的綁定關係

值得註意的是,這部分內容與命名空間和作用域有關係,另外有文章介紹它們,可以參考。

3.5.2 共用可變對象

函數attempt_change_mutable為列表增加字元串。

def attempt_change_mutable(list_param):
    list_param.append('test')

list1 = [1]
print(list1)
attempt_change_mutable(list1)
print(list1)

output:

[1]
[1, 'test']

可以看到函數成功改變了列表list1的值。傳遞參數時,名字list_param引用了與名字list1相同的對象,這個對象是可變的,在函數中成功修改了對象的值。

首先,名字list_param與名字list1指向對象:

共用可變對象

然後,通過名字list_param修改了對象的值:

修改共用對象值

最後,這個修改對名字list1可見。

4、其他說明

待續。。。

參考

  1. The Python Language References#Data model# Objects, values, types
  2. Python的名字綁定
  3. Python一切皆對象
  4. Code Like a Pythonista: Idiomatic Python
  5. python基礎(5):深入理解 python 中的賦值、引用、拷貝、作用域

腳註

[1] 在特定的控制條件下,改變對象的類型是可能的。但不是一種明智的做法,如果處理不當的話,會發生一些奇怪的行為。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 為了推廣.Net Core,微軟為我們提供了一個開源Demo-eShopOnContainers,這是一個使用Net Core框架開發的,跨平臺(幾乎涵蓋了所有平臺,windows、mac、linux、android、ios)的,基於微服務架構的,運行在容器中的小型應用,其不僅展示了.Net Cor... ...
  • 使用IdentityServer3 作為授權伺服器,如果沒有設置證書,而且client又沒有設置AccessTokenType = AccessTokenType.Reference,則獲取token的時候伺服器會報錯,設置證書即可,或者設置AccessTokenType = AccessToken ...
  • 1.安裝 mysql connector net 6.9.9 https://dev.mysql.com/downloads/file/?id=463758 和mysql for visual studio 1.2.6 2.nuget安裝 Install-Package MySql.Data.Ent ...
  • 參考網站:http://blog.csdn.net/u013242177/article/details/50437358 首先要包含commdlg.h頭文件,這個是通用對話框的頭文件,包括文件對話框,顏色對話框,列印對話框等。 然後再聲明一個CHOOSECOLOR類型的變數,聲明一個COLORRE ...
  • 活躍性危險 一、死鎖 發生:每個人都不願意放棄自己的鎖,確想要別人的鎖,這就會導致死鎖 1.鎖順序死鎖:如果每個線程以固定的順序獲取鎖,那麼至少在程式中不會出現鎖順序導致的死鎖; 因為順序固定如:所有線程:A-B-C 則無問題,如果一個A-B B-A則會發生死鎖 例子1:簡單死鎖 例子2:轉賬死鎖 ...
  • 轉自:http://blog.csdn.net/yixianfeng41/article/details/52591585 一、BMP文件由文件頭、點陣圖信息頭、顏色信息和圖形數據四部分組成。 顏色表用於說明點陣圖中的顏色,它有若幹個表項,每一個表項是一個RGBQUAD類型的結構,定義一種顏色。RGBQ ...
  • java存儲: 1)寄存器:這是最快的存儲區,位於處理器的內部。但是寄存器的數量有限,所以寄存器根據需求進行分配。我們不能直接進行操作。 2)堆棧:位於通用RAM中,可以通過堆棧指針從處理器那裡獲取直接支持。堆棧指針往下移動,則分配新的記憶體。網上移動,則釋放記憶體。但是 在創建程式的時候必須知道存儲在 ...
  • w3c 中的定義:鏈接 <a>http://www.w3school.com.cn/jquery/event_change.asp<a> jQuery 事件 - change() 方法 定義和用法 當元素的值發生改變時,會發生 change 事件。 該事件僅適用於文本域(text field),以及 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...