溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

[Python] 類型與對象

發(fā)布時間:2020-07-23 07:49:08 來源:網(wǎng)絡 閱讀:650 作者:爾冬 欄目:編程語言

1. 術語

程序中所存儲的所有數(shù)據(jù)都是對象。每個對象都有一個身份、一個類型和一個值。對象的身份可以看作是指向它在內(nèi)存中所處位置的指針,變量名就是引用這個具體位置的名稱。
對象的類型也稱作類別,用于描述對象的內(nèi)部表示及它支持的方法與操作。創(chuàng)建特定類型的對象時,有時也將該對象稱為該類型的實例。實例被創(chuàng)建之后,它的身份和類型就不可改變。如果對象的值是可以修改的,稱為可變對象,反之稱為不變對象。如果某個對象包含對其他對象的引用,則將其稱為容器或集合。
大多數(shù)對象擁有大量特有的數(shù)據(jù)屬性和方法。屬性就是與對象相關的值。方法就是被調用時將在對象上執(zhí)行某些操作的函數(shù)。使用點"."運算符可以訪問屬性和方法。
?

2. 對象的身份與類型

內(nèi)置函數(shù)id()可返回一個對象的身份,返回值為整數(shù)。is運算符用于比較兩個對象的身份。內(nèi)置函數(shù)type()則返回一個對象的類型。例如:

def compare(a, b):
    if a is b:
        # 同一個對象
    if a == b:
        # 具有相同的值
    if type(a) is type(b):
        # 具有相同類型

對象的類型本身也是一個對象,稱為對象的類。所有類型對象都有一個指定的名稱,可用于執(zhí)行類型檢查,例如:

if type(s) is list:
    s.append(item)
if type(d) is dict:
    d.update(t)

檢查類型的更佳方式是用內(nèi)置函數(shù)isinstance(object, type),例如:

if isinstance(s, list):
    s.append(item)
if isinstance(d, dict):
    d.update(t)

因為isinstance()函數(shù)能夠實現(xiàn)繼承,因此是檢查所有Python對象類型的首選方式。
?

3. 引用計數(shù)與垃圾收集

所有對象都有引用計數(shù)。無論是給對象分配一個新名稱,還是將其放入一個容器,該對象的引用計數(shù)就會增加,例如:

a = 37 # 創(chuàng)建值為37的對象
b = a # 增加37的引用計數(shù)
c = []
c.append(b) #增加37的引用計數(shù)

這個例子創(chuàng)建了一個包含值37的對象,a只是引用這個新創(chuàng)建對象的一個名稱,將a賦值給b時,b就成了同一對象的新名稱,而且該對象的引用計數(shù)會增加。類似地,將b放到一個列表中時,該對象的引用計數(shù)將再次增加。
使用del語句或者引用超出作用域時(或者被重新賦值),對象的引用計數(shù)就會減少,例如:

del a # 減少37的引用計數(shù)
b = 42 #減少37的引用計數(shù)
c[0] = 2.0 #減少37的引用計數(shù)

使用sys.getrefcount()函數(shù)可以獲得對象的當前引用計數(shù),例如:

a = 37
import sys
print(sys.getrefcount(a))

多數(shù)情況下,引用計數(shù)比猜測的要大得多,對于不可變數(shù)據(jù)(如數(shù)字和字符串),解釋器會主動在程序的不同部分共享對象,以便節(jié)約內(nèi)存。
當一個對象的引用計數(shù)歸零時,它將被垃圾收集機制處理掉。在某些情況下,很多已不再使用的對象間可能存在循環(huán)依賴關系,例如:

a = {}
b = {}
a['b'] = b
b['a'] = a
del a
del b

在以上例子中,del語句將會減少a和b的引用計數(shù),并銷毀用于引用底層對象的名稱。然而因為每個對象都包含一個對其他對象的引用,所以引用計數(shù)不會歸零,對象也不會被銷毀,從而導致內(nèi)存泄露。為了解決這個問題,解釋器會定期執(zhí)行一個循環(huán)檢測器,搜索不可訪問對象的循環(huán)并刪除它們。
?

4. 引用與復制

在程序進行像a = b這樣的賦值時,就會創(chuàng)建一個對b的引用。對于像數(shù)字和字符串這樣的不可變對象,這種賦值實際上創(chuàng)建了b的一個副本。然而,對于可變對象(如列表和字典)引用行為會完全不同,例如:

a = [1, 2, 3, 4]
b = a
print(b is a) # True
b[2] = -100
print(a[2]) #-100

因為a和b引用的同一個對象,修改其中任意一個變量都會影響到另一個。所以必須創(chuàng)建對象的副本而不是新的引用。對于像列表和字典這樣的容器對象,可以使用兩種復制操作: 淺復制和深復制。淺復制將創(chuàng)建一個新對象,但它包含的是對原始對象中包含的項的引用,例如:

a = [1, 2, [3, 4]]
b = list(a)
print(b is a) #False
b.append(100)
print(b) # [1, 2, [3, 4], 100]
print(a) # [1, 2, [3, 4]]
b[2][0] = -100
print(b) # [1, 2, [-100, 4], 100]
print(a) # [1, 2, [-100, 4]]

深復制將創(chuàng)建一個新對象,并且遞歸地復制它包含的所有對象??梢允褂脴藴蕩熘械腸opy.deepcopy()函數(shù)完成該工作,例如:

import copy
a = [1, 2, [3, 4]]
b = copy.deepcopy(a)
b[2][0] = -100
print(b) # [1, 2, [-100, 4]]
print(a) # [1, 2, [3, 4]]

?

5. 表示數(shù)據(jù)的內(nèi)置類型

大約有12種數(shù)據(jù)類型可用于表示程序中用到的大多數(shù)數(shù)據(jù)。如下表所示:

類型分類 類型名稱 描述
None Type(None) null對象None
數(shù)字 int 整數(shù)
數(shù)字 float 浮點數(shù)
數(shù)字 complex 復數(shù)
數(shù)字 bool 布爾值
序列 str 字符串
序列 list 列表
序列 tuple 元組
序列 range 創(chuàng)建的整數(shù)范圍
映射 range 創(chuàng)建的整數(shù)范圍
集合 set 可變集合
集合 frozenset 不可變集合

None類型表示一個沒有值的對象,在程序中表示為None。如果一個函數(shù)沒顯示返回值,則返回該對象。None常用于可選參數(shù)的默認值,以便讓函數(shù)檢測調用者是否為該參數(shù)實際傳遞了值。
Python使用4種數(shù)字類型:布爾型、整數(shù)、浮點數(shù)以及復數(shù)。除了布爾值,所有數(shù)字對象都是有符號的。所有數(shù)字類型都不可變。數(shù)字類型擁有大量的屬性和方法,可以簡化涉及混合算術的運算。為了與有理數(shù)兼容,整數(shù)使用了屬性x.numerator和x.denominator。為了兼容復數(shù),整數(shù)或浮點數(shù)y擁有屬性y.real和y.imag,以及方法y.conjugate()。使用y.as_interger_ratio()可將浮點數(shù)y轉換為分數(shù)形式的一對整數(shù)。方法y.is_interger()用于測試浮點數(shù)y是否表示整數(shù)值。通過方法y.hex()和y.fromhex()可用低級二進制形式使用浮點數(shù)。
序列表示索引為非負整數(shù)的有序對象集合,包括字符串、列表和元組。所有序列支持的方法如下表:

項目 描述
s[i] 返回一個序列的元素i
s[i:j] 返回一個切片
s[i:j:stride] 返回一個擴展切片
lens(s) s中的元素數(shù)
min(s) s中的最小值
max(s) s中的最大值
sum(s [, initial]) s中各項的和
all(s) 檢查s中的所有項是否為True
any(s) 檢查s中的任意項是否為True

適用于可變序列的方法如下表:

項目 描述
s[i] = v 項目賦值
s[i:j] = t 切片賦值
s[i:j:stride] = t 擴展切片賦值
del s[i] 項目刪除
del s[i:j] 切片刪除
del s[i:j:stride] 擴展切片刪除

列表支持的方法如下表:

方法 描述
list(s) 將s轉換為一個列表
s.append(x) 將一個新元素x追加到s末尾
s.extend(x) 將一個新列表追加到s末尾
s.count(x) 計算s中x的出現(xiàn)次數(shù)
s.index(x [, start [, stop]]) 找到x首次出現(xiàn)的位置
s.insert(i, x) 在索引i處插入x
s.pop([i]) 返回元素i并從列表中移除它,省略i則返回列表中最后一個元素
s.remove(x) 搜索x并從s中移除它
s.reverse() 顛倒s中的所有元素的順序
s.sort([key [, reverse]]) 對s中的所有元素進行排序。key是一個鍵函數(shù)。reverse表明以倒序對列表進行排序

list(s)可將任意可迭代類型轉換為列表。如果s已經(jīng)是列表,則該函數(shù)構造的新列表是s的一個淺復制。
字符串支持的方法如下表:

方法 描述
s.captitalize() 首字符變大寫
s.center(width [, pad]) 在長度為width的字段內(nèi)將字符串居中。pad是填充字符
s.count(sub [, start [, end]]) 計算指定子字符串sub的出現(xiàn)次數(shù)
s.decode([encoding [, errors]]) 解碼一個字符串并返回一個Unicode字符串
s.encdoe([encoding [, errors]]) 返回字符串的編碼版本
s.endswith(suffix [, start [, end]]) 檢查字符串是否以suffix結尾
s.expandtabs([tabsize]) 使用空格替換制表符
s.find(sub [, start [, end]]) 找到指定子字符串sub首次出現(xiàn)的位置,否則返回-1
s.format(args, *kwargs) 格式化s
s.index(sub [, start [, end]]) 指到指定子字符串sub首次出現(xiàn)的位置,否則報錯
s.isalnum() 檢查所有字符是否都為字母或數(shù)字
s.isalpha() 檢查所有字符是否都為字母
s.isdigit() 檢查所有字符是否都為數(shù)字
s.islower() 檢查所有字符是否都為小寫
s.isspace() 檢查所有字符是否都為空白
s.istitle() 檢查字符串是否為標題字符串(每個單詞首字母大寫)
s.isupper() 檢查所有字符是否都為大寫
s.join(t) 使用s作為分隔符連接序列t中的字符串
s.ljust(width [, fill]) 在長度為width的字符串內(nèi)左對齊s
s.lower() 轉換為小寫形式
s.lstrip([chrs]) 刪掉chrs前面的空白或字符
s.partition(sep) 使用分隔符字符串sep劃分一個字符串。返回一個元組(head, sep, tail)
s.replace(old, new [, maxreplace]) 替換一個子字符串
s.rfind(sub [, start [, end]]) 找到一個子字符串最后一次出現(xiàn)的位置
s.rindex(sub [, start [, end]]) 找到一個子字符串最后一次出現(xiàn)的位置,否則報錯
s.rjust(width [, fill]) 在長度為width的字符串內(nèi)右對齊s
s.rpartition(sep) 使用分隔符sep劃分字符串,但是從字符串的結尾處開始搜索
s.rsplit([sep [, maxsplit]]) 使用sep作為分隔符對一個字符串從后往前進行劃分。maxsplit是最大劃分次數(shù)
s.rstrip([chrs]) 刪掉chrs尾部的空白或字符
s.split([sep [, maxsplit]]) 使用sep作為分隔符對一個字符串進行劃分。maxsplit是劃分的最大次數(shù)
s.splitlines([keepends]) 將字符串分為一個行列表。如果keepends為1,則保留各行最后的換行符
s.startswith(prefix [, start [, end]]) 檢查一個字符串是否以prefix開頭
s.strip([chrs]) 刪掉chrs開頭和結尾的空白或字符
s.swapcase() 將大寫轉換為小寫,或者相反
s.title() 將字符串轉換為標題格式
s.translate(table [, deletechars]) 使用一個字符轉換表table轉換字符串,刪除deletechars中的字符
s.upper() 將一個字符串轉換為大寫形式
s.zfill(width) 在字符串的左邊填充0,直至其寬度為width

很多字符串方法都接受可選的start和end參數(shù),其值為整數(shù),用于指定s中起始和結束位置的索引。大多數(shù)情況下,這些值可以為負值,表示索引是從字符串結尾處開始計算的。
映射類型表示一個任意對象的集合,而且可以通過另一個幾乎是任意鍵值的集合進行索引。和序列不同,映射對象是無序的,可以通過數(shù)字、字符串和其他對象進行索引。映射是可變的。
字典是唯一內(nèi)置的映射類型,任何不可變對象都可以用作字典鍵值,如字符串、數(shù)字、元組等。字典的方法如下表:

項目 描述
len(m) 返回m中的項目數(shù)
m[k] 返回m中鍵k的項
m[k] = x 將m[k]的值設為x
del m[k] 從m中刪除m[k]
k in m 如果k是m中的鍵,則返回True
m.clear() 刪除m中的所有項目
m.copy() 返回m的一個副本
m.fromkeys(s [, value]) 創(chuàng)建一個新字典并將序列s中的所有元素作為新字典的鍵,這些鍵的值均為value
m.get(k [, v]) 返回m[k],如果找不到m[k],則返回v
m.items() 返回由(key, value)對組成的一個序列
m.keys() 返回鍵值組成的一個序列
m.pop(k [, default]) 如果找到m[k],則返回m[k]并從m中刪除,否則返回default的值
m.popitem() 從m中刪除一個隨機的(key, value)對,并把它返回為一個元組
m.setdefault(k [, v]) 如果找到m[k],則返回m[k],不則返回v,并將m[k]的值設為v
m.update(b) 將b中的所有對象添加到m中
m.values() 返回m中所有值的一個序列

集合是唯一的無序集。與序列不同,集合不提供索引或切片操作。它們和字典也有所區(qū)別,即對象不存在相關的鍵值。放入集合的項目必須是不可變的。集合分為兩種類型,set是可變的集合,而frozenset是不可變的集合,這兩類集合都是用一對內(nèi)置函數(shù)創(chuàng)建的,例如:

s = set([1, 5, 10, 15])
f = frozenset(['a', 37, 'hello'])

所有集合支持的方法如下表:

項目 描述
len(s) 返回s中項目數(shù)
s.copy() 制作s的一份副本
s.difference(t) 求差集。返回所有要s中,但不在t中的項目
s.intersection(t) 求交集。返回所有同時在s和t中的項目
s.isdisjoint(t) 如果s和t沒有相同項,則返回True
s.issubset(t) 如果s是t的一個子集,則返回True
s.issuperset(t) 如果s是t的一個超集,則返回True
s.symmetric_difference(t) 求對稱差集。返回所有在s或t中,但又不同時在這兩個集合中的項
s.union(t) 求并集。返回所有在s或t中的項

可變集合還另外提供了一些方法,如下表:

項目 描述
s.add(item) 將item添加到s中。如果item已經(jīng)在s中,則無任何效果
s.clear() 刪除s中的所有項
s.difference_update(t) 從s中刪除同時也在t中的所有項
s.discard(item) 從s中刪除item,如果item不要s的成員,則無任何效果
s.intersection_update(t) 計算s與t的交集,并將結果放入s
s.pop() 返回一個任意的集合元素,并將其從s中刪除
s.remove(item) 從s中刪除item,如果item不是s的成員,引發(fā)異常
s.symmetric_difference_update(t) 計算s與t的對稱差集,并將結果放入s
s.update(t) 將t中的所有項添加到s中

所有的這些操作都可以直接修改集合s。
?

6. 表示程序結構的內(nèi)置類型

在Python中,函數(shù)、類和模塊都可以當做數(shù)據(jù)操作的對象,如下表:

類型分類 類型名稱 描述
可調用 types.BuiltinFunctionType 內(nèi)置函數(shù)或方法
可調用 type 內(nèi)置類型和類的類型
可調用 object 所有類型和類的祖先
可調用 types.FunctionType 用戶定義的函數(shù)
可調用 types.MethodType 類方法
模塊 types.ModuleType 模塊
object 所有類型和類的祖先
類型 type 內(nèi)置類型和類的類型

可調用類型表示支持函數(shù)調用操作的對象。具有這種屬性的對象有:用戶定義的函數(shù),方法、內(nèi)置函數(shù)與方法,可調用的類與實例。
用戶定義的函數(shù)是指用def語句或lambda運算符在模塊級別上創(chuàng)建的可調用對象,它具有以下屬性:

屬性 描述
f.__doc__ 文檔字符串
f.__name__ 函數(shù)名稱
f.__dict__ 包含函數(shù)屬性的字典
f.__code__ 字節(jié)編譯的代碼
f.__defaults__ 包含默認參數(shù)的元組
f.__globals__ 定義全局命名空間的字典
f.__closure__ 包含與嵌套作用域相關數(shù)據(jù)的元組

方法是在類定義中定義的函數(shù)。有3種常見的方法:實例方法、類方法和靜態(tài)方法。實例方法是操作指定類的實例的方法,實例作為第一個參數(shù)傳遞給方法,根據(jù)約定該參數(shù)一般稱為self。類方法把類本身當作一個對象進行操作,在第一個參數(shù)中將類對象傳遞給類。靜態(tài)方法就是打包在類中的函數(shù),它不能使用一個實例或類對象作為第一個參數(shù)。例如:

f = Foo()
meth = f.instance_method
meth(30)

在以上例子中,meth稱為綁定方法。綁定方法是可調用對象,它封裝了函數(shù)和一個相關實例。調用綁定方法時,實例就會作為第一個參數(shù)(self)傳遞給方法。方法查找也可以出現(xiàn)類本身上,例如:

umeth = Foo.instance_method
umeth(f, 30)

在以下例子中,umeth稱為非綁定方法。非綁定方法是封裝了方法函數(shù)的可調用對象,但需要傳遞一個正確類型的實例作為第一個參數(shù)。如果傳遞的對象類型錯誤,就會引發(fā)TypeError異常。
為方法對象定義的屬性如下表:

屬性 描述
m.__doc__ 文檔字符串
m.__name__ 方法名稱
m.__class__ 定義該方法的類
m.__func__ 實現(xiàn)方法的函數(shù)對象
m.__self__ 與方法相關的實例(如果是非綁定方法則為None)

類對象和實例也可以當作可調用對象進行操作。類對象使用class語句創(chuàng)建,并作為函數(shù)調用,以創(chuàng)建新實例。在這種情況下,將函數(shù)的參數(shù)傳遞給類的__init__()方法,以便初始化新創(chuàng)建的實例。如果實例定義了一個特殊方法__call__(),它就能夠模擬函數(shù)的行為。如果該方法是為某個實例x而定義,使用x(args)語句等同于調用方法x.__call__(args)。
定義類時,類定義通常會生成一個type類型的對象,一個類型對象t的常用屬性如下表:

屬性 描述
t.__doc__ 文檔字符串
t.__name__ 類名稱
t.__bases__ 基類的元組
t.__dict__ 保存類方法和變量的字典
t.__module__ 定義類的模塊名稱
t.__abstractmethods__ 抽象方法名稱的集合

創(chuàng)建一個對象實例時,實例的類型就是定義它的類,例如:

f = Foo()
print(type(f)) # <class '__main__.Foo'>

下表顯示實例擁有的特殊屬性:

屬性 描述
t.__class__ 實例所屬的類
t.__dict__ 保存實例數(shù)據(jù)的字典

模塊類型是一個容器,可保存使用import語句加載的對象。模塊定義了一個使用字典實現(xiàn)的命名空間,比如,m.x=y等價于m.__dic__["x"]=y。模塊的可用屬性如下:

屬性 描述
m.__dict__ 與模塊相關的字典
m.__doc__ 模塊文檔字符串
m.__name__ 模塊名稱
m.__file__ 用于加載模塊的文件
m.__path__ 完全限定包名,只在模塊對象引用包時定義
向AI問一下細節(jié)

免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權內(nèi)容。

AI