python 類的成員有三種:字段、方法、屬性前端
字段包括:普通字段和靜態字段,他們在定義和使用中有所區別,而最本質的區別是內存中保存的位置不一樣,python
普通字段
屬於對象,只有對象建立以後,纔會有普通字段,並且只能經過對象來調用git
靜態字段
屬於類,解釋器在加載代碼的時候已經建立,對象和類均可以調用程序員
例子:web
class Province:
sql
country = '中國' #靜態字段
數據庫
def __init__(self,name):
設計模式
self.name = name #普通字段
併發
#調用字段:
app
obj = Province('河南') #建立對象
res1 = obj.name #對象調用普通字典
res2 = obj.country #對象調用靜態字段
print('對象調用普通字典:',res1)
print('對象調用靜態字段:',res2)
res3 = Province.country #類調用靜態字段
res4 = Province.name #類調用普通字段,會報錯
print('類調用靜態字段:',res3)
print('類調用普通字段:',res4) #報錯
輸出結果:
對象調用普通字典: 河南
對象調用靜態字段: 中國
類調用靜態字段: 中國
Traceback (most recent call last):
File "D:/study-file/git/gitlab/study/code/day08/成員.py", line 24, in <module>
res4 = Province.name # 類調用普通字段,會報錯
AttributeError: type object 'Province' has no attribute 'name'
由於對象沒有建立,因此在內存中並無name這個字段,因此,類直接調用會報錯
方法包括:普通方法、靜態方法和類方法,三種方法在內存中都歸屬於類,區別在於調用方式不一樣
普通方法
屬於類,由對象去調用執行,參數至少有一個self,執行普通方法時,自動將調用該方法的對象賦值給self;
靜態方法
屬於類,由類直接調用.當方法內部不須要對象中封裝的值時,能夠將方法寫成靜態,而且使用 @staticmethoe裝飾,而且參數中不帶self,參數無關緊要
類方法
靜態方法的特殊形式,至少有一個cls參數 由類執行 @classmethoe裝飾,執行類方法時,自動將調用該方法的類複製給cls
舉例:
class Province:
country = '中國' # 靜態字段
def __init__(self, name):
self.name = name # 普通字段
def show(self): #普通方法
print(self.country,self.name)
@staticmethod
def f1(arg): #靜態方法
print(arg)
@classmethod
def f2(cls): #類方法 cls爲類名
print(cls)
# 調用字段:
obj = Province('河南') # 建立對象
obj.show() #類調用普通方法執行
obj.f1('對象調用靜態方法執行')
Province.f1('類調用靜態方法執行')
Province.f2() #類調用類方法執行,返回類名
執行結果:
中國 河南
對象調用靜態方法執行
類調用靜態方法執行
<class '__main__.Province'>
屬性是普通方法的變種,使用 @property來裝飾,因此具備方法的表現形式,使用字段調用的方法來調用方法,因此也具備字段的訪問形式。由對象來調用
屬性的基本使用
從執行結果中能夠看出,常規類中方法的調用是obj.方法()的形式,可是此時調用屬性是obj.方法,不加括號,這種形式和靜態字段調用的形式同樣,因此說有靜態字段的調用方法;而在代碼中看,屬性的表現形式都是普通方法的形式,即函數,而後使用property來裝飾,因此說有普通方法的表現形式
class Province:
country = '中國' # 靜態字段
def __init__(self, name):
self.name = name # 普通字段
def show(self): #普通方法
print(self.country,self.name)
@staticmethod
def f1(arg): #靜態方法
print(arg)
@classmethod
def f2(cls): #類方法 cls爲類名
print(cls)
@property
def f3(self): # 屬性
print(self.name)
@f3.deleter
def f3(self):
print('del f3')
@f3.setter
def f3(self,arg):
print('set f3',arg)
#調用屬性
obj = Province('河南') # 建立對象
obj.f3 #調用屬性,自動執行@f3.getter裝飾的方法 此形態相似於靜態字段的調用
del obj.f3 #自動執行@f3.deleter裝飾的方法,相似於靜態字段的del
obj.f3 = '123' #自動執行@f3.setter裝飾的方法,相似靜態字段的set方法
執行結果:
河南
del f3
set f3 123
裝飾器:
即在一個方法上應用@property裝飾器,使方法變爲一個屬性
``` class Foo: @property def f1(self): pass @f1.deleter def f1(self): pass @f1.setter def f3(self): pass ``` * 靜態字段: 在類中定義値爲property對象的靜態字段 ``` class Province: country = '中國' # 靜態字段 def __init__(self, name): self.name = name # 普通字段 def show(self): #普通方法 print(self.country,self.name) @staticmethod def f1(arg): #靜態方法 print(arg) @classmethod def f2(cls): #類方法 cls爲類名 print(cls) def f4(self): print(1234) def f5(self,arg): print('執行set') def f6(self): print('執行del') foo = property(fget=f4, fset=f5, fdel=f6) # 屬性的靜態字段表達方式 #調用屬性 obj = Province('河南') # 建立對象 obj.foo #自動執行f4方法 del obj.foo #自動執行f6方法 obj.foo = '123' #自動執行f5方法 輸出結果: 1234 執行del 執行set ```
總結:
屬性存在乎義是:訪問屬性時能夠製造出和訪問字段徹底相同的假象,按字段的操做來執行對象類中定義的屬性中特定的方法,如執行obj.foo會自動執行f4方法,del obj.foo 會自動執行f6方法,此映射關係都使用foo = property(fget=f4, fset=f5, fdel=f6)定義好,屬性只是僞造了字段的操做方式而已,不會刪除對應的東西,只是根據字段的操做方式來執行對應的方法,而具體執行什麼方法,方法有什麼功能,這都是本身靈活定義
屬性由方法變種而來,若是Python中沒有屬性,方法徹底能夠代替其功能。
類的成員修飾符使用類的全部成員,包括以下:
公有:在任何地方都能訪問和調用
私有:只能在類內部進行調用
「`
class Foo:
contry = ‘china’ #公有靜態字段
__contry1 = ‘china’ #私有靜態字段
def __init__(self,name): self.name = name #公有普通字段 self.__name1 = name #私有普通字段 def __f1(self): #私有方法 print(self.name) def f2(self): #公有方法 print(self.__contry) self.__f1()
「`
特例
若是想要強制訪問私有字段,能夠經過 對象._類名__ 私有成員名訪問
如:obj._Foo__\f1, obj_Foo__contry1, 不建議強制訪問私有成員
定義:私有成員命名時,前兩個字符是下劃線。(特殊成員除外,例如:init、call、dict 等)
python的特殊成員是採用__方法名__ 表示含有特殊意義的成員
init 構造方法,該方法在對象建立時自動建立
class Foo:
def __init__(self,name):
self.name = name #公有普通字段
當對象在內存中被釋放時,自動觸發執行,此方法通常無須定義,由於Python是一門高級語言,程序員在使用時無需關心內存的分配和釋放,由於此工做都是交給Python解釋器來執行,因此,析構函數的調用是由解釋器在進行垃圾回收時自動觸發執行
doc 表示類的描述信息
class Foo: """ 描述類信息,牛逼的python """ def func(self): passprint(Foo.__doc__) #輸出:類的描述信息輸出結果: 描述類信息,牛逼的python
module 和 class
module 表示當前操做的對象在那個模塊
class 表示當前操做的對象的類是什麼
class Foo: def f1(self): pass
from test import Foo obj = Foo() print(obj.__class__) print(obj.__module__) 輸出: <class 'test.Foo'> test
call
對象後面加括號,觸發執行。注:構造方法的執行是由建立對象觸發的,即:對象 = 類名() ;而對於 call 方法的執行是由對象後加括號觸發的,即:對象() 或者 類()()
class Foo: def __init__(self): pass def __call__(self, *args, **kwargs): print('__call__') obj = Foo() # 執行 __init__ obj() # 執行 __call__
dict 類或對象中的全部成員
class Foo: def __init__(self): self.name = 123 def f1(self): passprint(Foo.__dict__) #打印類的全部成員obj = Foo()print(obj.__dict__) #打印對象中的全部成員輸出結果:{'__init__': <function Foo.__init__ at 0x01FA1348>, '__module__': '__main__', '__weakref__': <attribute '__weakref__' of 'Foo' objects>, '__dict__': <attribute '__dict__' of 'Foo' objects>, '__doc__': None, 'f1': <function Foo.f1 at 0x01FA11E0>}{'name': 123}
str 指定print對象的時候輸出的內容
class Foo: def __init__(self): self.name = 123 def f1(self): pass def __str__(self): return "打印對象輸出結果" obj = Foo()print(obj) #打印對象輸出結果:打印對象輸出結果
getitem、setitem、delitem
用於索引操做,如字典。以上分別表示獲取、設置、刪除數據
class Foo: def __init__(self): self.name = 123 def __getitem__(self, item): print('__getitem__',item) def __delitem__(self, key): print('__delitem',key) def __setitem__(self, key, value): print('__setitem__',key,value)obj = Foo()result = obj['k1'] # 自動觸發執行 __getitem__obj['k2'] = 'hahhahhhha' # 自動觸發執行 __setitem__del obj['k1'] ## 自動觸發執行 __delitem__輸出結果:__getitem__ k1__setitem__ k2 hahhahhhha__delitem k1
iter 用於迭代器,之因此列表、字典、元組能夠進行for循環,是由於類型內部定義了 iter
class Foo: def __init__(self,num): self.num = num def __iter__(self): i = 1 while True: if i <= self.num: yield i i += 1 else: break obj = Foo(4) for i in obj: print(i) 輸出結果: 1 2 3 4
以上是經常使用的特殊成員,還有不少不經常使用的,不在舉例
isinstance(obj, cls)
檢查是否obj是不是類 cls 的對象
class Foo(object): passobj = Foo()isinstance(obj, Foo)
issubclass(sub, super)
檢查sub類是不是 super 類的派生類
class Foo(object): passclass Bar(Foo): passissubclass(Bar, Foo)
執行父類的方法
默認狀況下當子類和父類的方法同樣時,優先執行子類的方法,以下:
class Foo: def f1(self): print('Foo.f1')class Bar(Foo): def f1(self): print('Bar.f1')obj = Bar()obj.f1()輸出結果:Bar.f1
若是想要強制執行父類的方法呢?可使用super(子類名,self).父類方法 格式以下:
class Foo: def f1(self): print('Foo.f1')class Bar(Foo): def f1(self): super(Bar,self).f1() #使用super 來強制執行父類的f1方法 print('Bar.f1')obj = Bar()obj.f1()輸出結果:Foo.f1 #執行父類f1的結果Bar.f1
應用1,擴展原來代碼的功能
需求:一個開源的web框架,在保證不改變源碼的狀況下,個性定製本身的環境,適應需求。這就用到了類的繼承,我新擴展的功能是在原來功能的基礎上進行擴展的,因此,我只須要將新功能類繼承源代碼的相關功能類,而後使用super強制執行父類的方法,實現基本功能,最後在新類中擴展基本功能便可。此區別於裝飾器,使用裝飾器須要在原來的類上應用裝飾器,那就改變了源碼
若是我要擴展該功能,須要在每次f1執行前打印一個start,執行結束以後,打印一個end,看下面代碼
from test import Foo #從源代碼中導入Foo類
前端調用的時候,我直接調用本身建立的類便可,這就實現了基本的擴展,也不改變源代碼
字典key 的排序是無序的,若是要實現一個有序字典,能夠根據類的繼承來本身寫一個有序字典類
實現思路:
1.繼承dict類,使新定義的類有dict的全部方法
2.定一個列表,用來存放字典中的key,輸出的時候循環這個列表,那麼這個字典就變成有序輸出
3.使用__setitem__特殊方法實現能夠自定義key value
4.使用__str__特殊方法實現print字典
代碼以下:
下面來測試
應用2 實現有序字典
obj = Mydict() #建立一個字典
obj['k1'] = 'v1' #字典key value賦值
obj['k2'] = 'v2'
print(obj) #打印字典
print(type(obj)) #打印類型
輸出:
{'k1':v1,'k2':v2}
<class '__main__.Mydict'>
class Mydict(dict):
def __init__(self):
self.li = []
super(Mydict,self).__init__()
def __setitem__(self, key, value): #獲取obj['k1'] = 'v1'形式的賦值
self.li.append(key) #將key存入列表
super(Mydict, self).__setitem__(key,value) #強制執行父類的__setitem__,實現字典功能
def __str__(self):
temp = []
for key in self.li: #循環列表中的key
value = self.get(key)
temp.append("'%s':%s" % (key,value)) #將key value 組成元組存入一個臨時列表
ret = "{" + ','.join(temp) + '}' #join 來替換key value中間的空格爲冒號:,並拼接成字典形式
return ret
obj = New()
obj.f1()
輸出效果:
===start====
源代碼
基本功能執行完畢
===end===
class New(Foo):
def f1(self):
print('===start====')
super(New,self).f1()
print('===end===')
#這是源代碼類,實現打印輸出
class Foo:
def f1(self):
print('源代碼')
print('基本功能執行完畢')
單例模式指的是是多個對象建立時,若是每次都須要建立一個實例,在經過該實例去執行指定的方法,這樣每次頻繁的建立實例,對內存的讀寫消耗很大,若是將他們共同的實例,經過一種判斷機制,若是實例不存在,則建立實例,而後調用某個方法;若是實例存在,則直接調用某個方法,那麼在內存中就僅僅保留了一份實例,這樣豈不更好
看下面實例,若是class Mysql 是一個數據庫鏈接池
``` class Mysql: def __init__(self): self.host = 127.0.0.1 self.port = 3306 self.dbname = test self.user = jeck self.passwd = 123123 def create(self): #執行create語句 pass def delete(self): #執行delete語句 pass ```
若是用戶須要操做數據庫,那麼須要進行下面操做
user1 = Mysql()
user1.create()
user2 = Mysql()
user2.delete()
….
發現,每來一個用戶,都須要建立一個地址池實例,而後執行某個方法,這樣在高併發的網站,直接就崩潰了
換種思路,若是,我只建立一個地址池對象,用戶請求來以後,先進行判斷,沒有實例的話,就建立,有的話就直接使用,豈不更高效。
「`
class Mysql:
instance = False
def init(self):
self.host = ‘127.0.0.1’
self.port = 3306
self.dbname = ‘test’
self.user = ‘jeck’
self.passwd = ‘123123’
def create(self): # 執行create語句 pass def delete(self): # 執行delete語句 pass @classmethod def get_instance(cls): if cls.instance: #判斷instence 是否有値,若是有的話,直接返回 return cls.instance else: obj = cls() #instence沒有値的話,建立對象,並將對象賦給instence cls.instance = obj return obj
obj1 = Mysql() #多例模式
obj2 = Mysql() #多例模式
obj3 = Mysql.get_instance() #單例模式
obj4 = Mysql.get_instance() #單例模式
#打印內存地址
print(‘多例模式:’,obj1)
print(‘多例模式:’,obj2)
print(‘單例模式:’,obj3)
print(‘單例模式:’,obj4)
輸出結果:
多例模式: <main.Mysql object at 0x013AAC70>
多例模式: <main.Mysql object at 0x013AACD0>
單例模式: <main.Mysql object at 0x013AAD30>
單例模式: <main.Mysql object at 0x013AAD30>
「`
發現使用單例模式後,第二次建立的對象和第一次建立的對象內存地址是同樣的,即便再有成千上萬後實例,其都是公用的一個鏈接池
總結:單利模式存在的目的是保證當前內存中僅存在單個實例,避免內存浪費!
同步更新地址:http://www.cnblogs.com/pycode