命名空间不能直接包含字段或方法之类的成员是什么意思_Python 学习笔记之类与实例...
Python 學習筆記之類與實例
一、定義
1.1、定義
類 (class) 封裝一組相關數據,使之成為一個整體,并使用一種方法持續展示和維護。
這有點像把零件組裝成整車提供給用戶,無須了解汽車的內部結構和工作原理,只要知道方向盤,剎車和油門這些外部接口就可以正常行駛。類存在兩種關系
繼承可以用來表達本車屬于某廠的哪個車族系列,除了繼承原車系的技術和優勢,還可基于某些環境進行改進。
組合可用來表述該車使用了哪些零部件,比如最新的發動機。
類與模塊的不同之處
這些特性模塊沒有或者不需要,同時,模塊粒度大,模塊可用來提供游戲場景級別的解決方案,而類則是該場景下的特定家族和演員。
1.2、創建
定義類,以此為個體為例。關鍵字 class 同樣是運行期指令,用于完成類型對象的創建。
class User:pass可在函數內定義,以限制其作用范圍。類型與實例
如果類在模塊中定義,那么其生命周期與模塊等同,如果被放在函數內,那么每次都是新建。即便名字和內容相同,也屬于不同類型。
def test():class X:passreturn X()?>>> a,b = test(),test()>>> a.__class__ is b.__class__Out[1]: False函數內定義的類型對象,在所有實例死亡后,會被垃圾回收。類型對象除了用來創建實例,也為所有實例定義了基本操作接口,其負責管理整個家族的可共享數據和行為目標。
實例只保存私有特征,其以內部引用從所屬類型或其它所屬祖先類查找所需的方法,用來驅動展現個體面貌。
名字空間
類型有自己的名字空間,存儲當前類型定義的字段和方法。這其中并不包括所繼承的祖先成員,其同樣以引用關聯祖先類型,無須復制到本地。
class A:a = 100 #類字段def __init__(self, x): #實例初始化方法self.x = x #實例字段def_get_x(self): #實例方法return self.xclass B(A): #繼承自Ab = "hello"def __init__(self, x, y):super().__init__(self,x) #調用父類的初始化方法self.y = ydef get_y(self):return self.y實例 instance o 會保存所有繼承層次的實例字段,因為這些都屬于其私有數據。
>>> o = B(1,2)>>> print(A.__dict__){'a': 100,'__init__': <function A.__init__ at 0x109d04f28>, '_get_x': <function A._get_x at 0x109d046a8>, ...}>>> print(B.__dict__){'b': 'hello', '__init__': <function B.__init__ at 0x109d272f0>, 'get_y': <function B.get_y at 0x109d27378>, ...}當通過實例或類訪問某個成員時,會從當前對象開始(instance o 開始查找),依次由近到遠向祖先類查找
(即 o --> class B --> class A 進行成員查找)。
如此做的好處就是祖先類的新增功能可以直接 【廣播】給所有后代。
在繼承層次的不同名字空間中允許有同名成員,并按順序優先命中。二、字段
依照所處空間不同,我們將字段分為類型字段和實例字段。
官方將成員統稱為 Attribute,我們可按例將數據當做字段。2.1、類型字段
【類型字段】在 class 語句塊內直接定義,而實例字段必須通過實例引用(self)賦值定義。
僅從執行方式來看,無論實例方法存在于哪級類型,其隱式參數 self 總指向當前調用實例。
class A:def test(self): #self 總是指向引用的當前實例,這與繼承層次無關print(self)self.x = 100class B(A):pass?if __name__=="__main__":o = B()print(hex(id(o)))print(o.test())0x109d2f358 <__main__.B object at 0x109d2f358> 實例參數 self 只是約定成俗的名字,這類似于其它語言中的 this,換成 this 同樣有效。2.2、字段賦值
可使用賦值語句為類型或實例添加的新字段。
class A: pass?if __name__=="__main__":A.x = 100 #新增類型字段print(A.x)print(vars(A))?'''100{x': 100, ...}'''可一旦實例重新賦值,就將會在其名字空間建立同名字段,并會遮蔽原字段。
a = A()a.b = 200print(a.b)print(vars(a))'''200{'b': 200}'''2.3、私有字段
將私有字段暴露給用戶是很危險的。因為無論是修改還是刪除都無法截獲,由此可能引發意外錯誤。因為語言沒有嚴格意義上的訪問權限設置,所以只好將它們隱藏起來。
如果成員名字以雙下劃線開頭,但沒有以雙下劃線結尾,那么編譯器會自動對其重命名。
class X:__table = "user" #類型變量def __init__(self,name):self.name = name #實例變量def get_name(self):return self.name同時雙下劃線開頭課結尾的,通常是系統方法,比如 __ init __ ,__ hash __ ,__ main __等。所謂重命名,就是編譯器附加了類型名稱前綴。雖然這種做法不能真正阻止用戶訪問,但基于名字的約定也算一種提示。這種方式讓繼承類也無法訪問。
重命名機制總是針對當前類型,繼承類型無法訪問重命名后的基類成員。
可將雙下劃線前綴改為單下劃線,這樣雖然不能自動重命名,不過提示作用依舊。
class A:__name = "user" #雙下劃線成員class B(A):def test(self):print(self.__name)?>>> B().test()'''AttributeError: 'B' object has no attribute '_B__name''''?class A:_name = "user" #單下劃線成員class B(A):def test(self):print(self._name)>>> B().test()'''user'''三、屬性
對私有字段會進行重命名保護,那公開字段如何處理呢?
問題是核心在于訪問攔截,必須由內部邏輯決定如何返回結果。而屬性(property)機制就是將讀、寫和刪除操作映射到指定的方法調用上,從而實現操作控制。
class C:def __init__(self, name):self.__name = name@propertydef name(self): #讀return self.__name?@name.setterdef name(self, value): #寫self.__name = value @name.deleterdef name(self): #刪除raise AttributeError("can't delete attribute")?c = C("user")print(c.name)# user c.name = "abc"print(c.name)# abcdel c.nameprint(c.name)#can't delete attribute這種 @ 語法被稱作裝飾器(decorator)。多個方法名必須相同,默認從讀方法尅是定義屬性,隨后以屬性名定義寫和刪除。
如果實現只讀,或禁止刪除,則只需去掉對應的方法即可。
四、方法
方法是一種特殊函數,其與特定對象綁定,用來獲取或修改對象狀態。
實際上,無論是對象構造,初始化,析構還是運算符,都以方法實現。根據綁定目標和調用方法的不同,方法可分為實例方法,類型方法,以及靜態方法。
名字以上下劃線開始和結束的方法,通常有特殊用途,其由解釋器和內部機制調用。實例方法
實例方法與實例對象綁定,在其參數列表中,將綁定對象作為第一參數,以便在方法中讀取或修改數據狀態。在以實例引用調用方法時,無須顯式傳入第一實參,而由解釋器自動完成。
官方建議參數名用 self,同樣以 cls 作為類型方法的第一參數名。 class W:def __init__(self, name):self.__name = namedef get(self): #以實例引用調用,自動傳入 self 參數return self.__namedef set(self, value):self.__name = value>>> w = W("python")>>> w.get() #忽略第一參數'''python'''類型方法
類型方法用來維護類型狀態,面向族群提供服務接口。除綁定的第一參數名稱不同外,還需添加專門的裝飾器,以便解釋器將其實例方法區分開來。
class D:__data = "D.data"@classmethod #定義為類型方法def get(cls): #解釋器自動將類型對象 D 作為 cls 參數傳入return cls.__data@classmethoddef set(cls, value):cls.__data = value>>> D.get() #同樣忽略 cls 參數'''D.data'''靜態方法
靜態方法,則更像是普通函數。其既不接收實例引用,也不參與類型處理,所以就沒有自動傳入第一參數。使用靜態方法,更多原因是將類型作為一個作用域,或者當前類型添加便捷接口。
class DES:def __init__(self, key):self.__key = keydef encrypt_bytes(self, value):return str(self.__key) + str(value)@staticmethoddef encrypt(key, s):return DES(key).encrypt_bytes(s.encode("utf-8"))>>> DES.encrypt("key", "value")'''keyb'value''''特殊方法
下面又解釋器自動調用,與對象生命周期相關的方法。
創建實例時,會先調用析構方法和初始化方法。
class E:def __new__(cls, *args): #與__init__接收相同的調用函數print("__new__", args)return super().__new__(cls)def __init__(self, *args): ##self 由 __new__創建并返回print("__init__", args)>>> E(1,2)'''__new__ (1, 2)__init__ (1, 2)'''如果 __ new __ 返回實例與 cls 類型不符,將導致 __ init __ 無法執行。
五、總結
學習到此,我總算把類的創建,屬性和方法等弄清楚了,我最想強調一點,希望讀者把 實例 self 參數弄明白,后續編碼過程中使用較多。
還要清楚實例方法和靜態方法的區別。
總結
以上是生活随笔為你收集整理的命名空间不能直接包含字段或方法之类的成员是什么意思_Python 学习笔记之类与实例...的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: jenkins pipeline api
- 下一篇: python填表_小Python填表得到