概念
類(Class) :用來描述具有相同屬性和方法的對象的集合。它定義了該集合中每個對象所共有的屬性和方法。其中的對象被稱作類的實例。
實例/對象:通過類定義的初始化方法,賦予具體的值,成為一個"有血有肉的實體"。
實例化:類--->對象 的過程或操作。
類變量:類變量是所有實例公有的變量。類變量定義在類中,但在方法體之外。
實例變量:定義在實例中的變量,只作用于當前實例。
實例方法:至少有一個參數(shù)并且以實例對象(self)作為其第一個參數(shù)的方法。
靜態(tài)方法:不需要實例化就可以由類執(zhí)行的方法。
類方法:類方法是將類本身作為對象進行操作的方法。
封裝:將內(nèi)部實現(xiàn)包裹起來,對外透明,提供api接口進行調(diào)用的機制。
繼承:即一個派生類(derived class)繼承父類(base class)的變量和方法。
多態(tài):根據(jù)對象類型的不同以不同的方式進行處理。
類的兩種作用
1.屬性引用(類名.屬性)
class Person: # 定義一個Person類
role = 'person' # 靜態(tài)屬性就是直接在類中定義的變量
def __init__(self,name):
self.name = name # 每一個角色都有自己的昵稱;
def walk(self): # 動態(tài)屬性就是定義在類中的方法
print("person is walking...")
print(Person.role) #查看人的role屬性
print(Person.walk) #引用人的走路方法,注意,這里不是在調(diào)用
2.實例化:類名加括號,自動觸發(fā)__init__函數(shù)的運行,為每個實例定制自己的特征
class 類名:
類屬性 = None
def __init__(self,參數(shù)1,參數(shù)2):
self.對象屬性1 = 參數(shù)1
self.對象屬性2 = 參數(shù)2
def 方法名(self):
pass
實例/對象名 = 類名(參數(shù)) # 對象就是實例,代表一個具體的東西
# 類名() : 類名+括號就是實例化一個類,相當于調(diào)用了__init__方法
# 括號里傳參數(shù),參數(shù)不需要傳self,其他與init中的形參一一對應
# 結(jié)果返回一個對象
實例/對象名.對象屬性 # 查看對象的屬性
實例/對象名.方法名() # 調(diào)用類中的方法
類的屬性和方法
類屬性/實例屬性(也叫類變量、實例變量)
1:實例屬性:
最好在__init__(self,...)中初始化
內(nèi)部調(diào)用時都需要加上self.
外部調(diào)用時用instancename.propertyname
2:類屬性:
在__init__()外初始化
在內(nèi)部用classname.類屬性名調(diào)用
外部既可以用classname.類屬性名又可以用instancename.類屬性名來調(diào)用
3:私有屬性:
1):單下劃線開頭:只是告訴別人這是私有屬性,外部依然可以訪問更改
2):雙下劃線_開頭:外部不可通過instancename.propertyname來訪問或者更改
實際將其轉(zhuǎn)化為了_classname__propertyname
實例方法/靜態(tài)方法/類方法
實例方法:類的實例方法由實例調(diào)用,至少包含一個self參數(shù),且為第一個參數(shù)。執(zhí)行實例方法時,會自動將調(diào)用該方法的實例賦值給self。self代表的是類的實例,而非類本身
靜態(tài)方法:靜態(tài)方法由類調(diào)用,無默認參數(shù)。將實例方法參數(shù)中的self去掉,然后在方法定義上方加上@staticmethod,就成為靜態(tài)方法。它屬于類,和實例無關(guān)。建議只使用類名.靜態(tài)方法的調(diào)用方式。(雖然也可以使用實例名.靜態(tài)方法的方式調(diào)用)
class Foo:
@staticmethod
def static_method():
pass
#調(diào)用方法
Foo.static_method()
類方法:類方法由類調(diào)用,采用@classmethod裝飾,至少傳入一個cls(代指類本身,類似self)參數(shù)。執(zhí)行類方法時,自動將調(diào)用該方法的類賦值給cls。建議只使用類名.類方法的調(diào)用方式。(雖然也可以使用實例名.類方法的方式調(diào)用)
class Foo:
@classmethod
def class_method(cls):
pass
Foo.class_method()
綜合例子:
class Foo:
def __init__(self, name):
self.name = name
def ord_func(self):
"""定義實例方法,至少有一個self參數(shù) """
print('實例方法')
@classmethod
def class_func(cls):
""" 定義類方法,至少有一個cls參數(shù) """
print('類方法')
@staticmethod
def static_func():
""" 定義靜態(tài)方法 ,無默認參數(shù)"""
print('靜態(tài)方法')
# 調(diào)用實例方法
f = Foo("Jack")
f.ord_func()
Foo.ord_func(f) # 請注意這種調(diào)用方式,雖然可行,但建議不要這么做!
# 調(diào)用類方法
Foo.class_func()
f.class_func() # 請注意這種調(diào)用方式,雖然可行,但建議不要這么做!
# 調(diào)用靜態(tài)方法
Foo.static_func()
f.static_func() # 請注意這種調(diào)用方式,雖然可行,但建議不要這么做!
封裝、繼承、多態(tài)
封裝
封裝是指將數(shù)據(jù)與具體操作的實現(xiàn)代碼放在某個對象內(nèi)部,使這些代碼的實現(xiàn)細節(jié)不被外界發(fā)現(xiàn),外界只能通過接口使用該對象,而不能通過任何形式修改對象內(nèi)部實現(xiàn),正是由于封裝機制,程序在使用某一對象時不需要關(guān)心該對象的數(shù)據(jù)結(jié)構(gòu)細節(jié)及實現(xiàn)操作的方法。使用封裝能隱藏對象實現(xiàn)細節(jié),使代碼更易維護,同時因為不能直接調(diào)用、修改對象內(nèi)部的私有信息,在一定程度上保證了系統(tǒng)安全性。類通過將函數(shù)和變量封裝在內(nèi)部,實現(xiàn)了比函數(shù)更高一級的封裝。
繼承
Python3的繼承機制
子類在調(diào)用某個方法或變量的時候,首先在自己內(nèi)部查找,如果沒有找到,則開始根據(jù)繼承機制在父類里查找。
根據(jù)父類定義中的順序,以深度優(yōu)先的方式逐一查找父類!
繼承參數(shù)的書寫有先后順序,寫在前面的被優(yōu)先繼承。
多態(tài)(鴨子模式)
多態(tài)指的是一類事物有多種形態(tài)
import abc
class Animal(metaclass=abc.ABCMeta): #同一類事物:動物
@abc.abstractmethod
def talk(self):
pass
class People(Animal): #動物的形態(tài)之一:人
def talk(self):
print('say hello')
class Dog(Animal): #動物的形態(tài)之二:狗
def talk(self):
print('say wangwang')
class Pig(Animal): #動物的形態(tài)之三:豬
def talk(self):
print('say aoao')
peo=People()
dog=Dog()
pig=Pig()
# peo、dog、pig都是動物,只要是動物肯定有talk方法
# 于是我們可以不用考慮它們?nèi)叩木唧w是什么類型,而直接使用
peo.talk()
dog.talk()
pig.talk()
#更進一步,我們可以定義一個統(tǒng)一的接口來使用
def func(obj):
obj.talk() # 調(diào)用的邏輯都一樣,執(zhí)行的結(jié)果卻不一樣
talk(peo)
talk(dog)
talk(pig)
特殊成員和魔法方法
__init__ : 構(gòu)造函數(shù),在生成對象時調(diào)用
__del__ : 析構(gòu)函數(shù),釋放對象時使用
__repr__ : 打印,轉(zhuǎn)換
__setitem__ : 按照索引賦值
__getitem__: 按照索引獲取值
__len__: 獲得長度
__cmp__: 比較運算
__call__: 調(diào)用
__add__: 加運算
__sub__: 減運算
__mul__: 乘運算
__div__: 除運算
__mod__: 求余運算
__pow__: 冪
使用:
1.__getitem__()、__setitem__()、__delitem__()
a = 標識符[]?。? 執(zhí)行__getitem__方法
標識符[] = a : 執(zhí)行__setitem__方法
del 標識符[] : 執(zhí)行__delitem__方法
class Foo:
def __getitem__(self, key):
print('__getitem__',key)
def __setitem__(self, key, value):
print('__setitem__',key,value)
def __delitem__(self, key):
print('__delitem__',key)
obj = Foo()
result = obj['k1'] # 自動觸發(fā)執(zhí)行 __getitem__
obj['k2'] = 'jack' # 自動觸發(fā)執(zhí)行 __setitem__
del obj['k1'] # 自動觸發(fā)執(zhí)行 __delitem__
2.__str__(),__repr__()改變對象的字符串顯示
3.__format__自定制格式化字符串
4.__del__()析構(gòu)方法,當對象在內(nèi)存中被釋放時,自動觸發(fā)執(zhí)行
5.__new__單例模式
6.__call__()對象后面加括號,觸發(fā)執(zhí)行
7.__len__() len()時執(zhí)行
8.__module__ 表示當前操作的對象在屬于哪個模塊
9.__class__ 表示當前操作的對象屬于哪個類
10.__dict__列出類或?qū)ο笾械乃谐蓡T!非常重要和有用的一個屬性
11.__iter__這是迭代器方法!列表、字典、元組之所以可以進行for循環(huán),內(nèi)部定義了 __iter__()這個方法
12.__slots__限制實例可以添加的變量
...
反射
反射(或自?。┲饕侵赋绦蚩梢栽L問、檢測、和修改它本身狀態(tài)或行為的一種能力,在python中,通過字符串的形式操作對象相關(guān)的屬性,python中的一切事物都是對象,即都可以使用反射。
反射4個內(nèi)置函數(shù)分別為:getattr、hasattr、setattr、delattr 獲取成員、檢查成員、設(shè)置成員、刪除成員
- hasattr:hasattr(object,name)判斷一個對象是否有name屬性或者name方法。有就返回True,沒有就返回False
- getattr:獲取對象的屬性或者方法,如果存在則打印出來。hasattr和getattr配套使用,需要注意的是,如果返回的是對象的方法,返回出來的是對象的內(nèi)存地址,如果需要運行這個方法,可以在后面添加一對()
- setattr:給對象的屬性賦值,若屬性不存在,先創(chuàng)建后賦值
- delattr:刪除該對象指定的一個屬性
- isinstance(obj,cls)檢查是否obj是否是類 cls 的對象
- issubclass(sub, super)檢查sub類是否是 super 類的派生類
Other
關(guān)于self
self:在實例化時自動將對象/實例本身傳給__init__的第一個參數(shù),你也可以給他起個別的名字。
實例(對象)只有一種作用:屬性引用
關(guān)于super
如果子類中實現(xiàn)了調(diào)用父類的方法:
在類內(nèi):super(子類,self).方法名() supper().__init__(參數(shù))
在類外:super(子類名,對象名).方法名()
零碎
getattr(obj,"name") = obj.name
Python內(nèi)置的@property裝飾器就是負責把一個方法變成屬性調(diào)用
super 是用來解決多重繼承問題的,直接用類名調(diào)用父類方法在使用單繼承的時候沒問題,
但是如果使用多繼承,會涉及到查找順序(MRO)、重復調(diào)用(鉆石繼承)等種種問題。