mro算法
類屬性和執行個體屬性的查找順序
- 何為類屬性:定義在類内部的的一些變量或者方法,都統稱為類屬性
- 何為執行個體屬性:定義在對象内部的的一些變量或者方法,都統稱為執行個體屬性
對象也就是執行個體的意思。
class A:
aa = 1
def __init__(self, x, y):
self.x = x
self.y = y
a = A(2, 3)
類也是對象,看上邊代碼,實際上有兩個空間,A 和 a 兩個不同的空間。
單繼承時,屬性查找方式,向上查找,首先查找對象裡,再查找類中
在多繼承時,會很複雜
python2.2之前,python裡的類叫經典類,經典類繼承方式如果不顯式繼承object,實際上是不會自動繼承object,Python3中,經典類已經不存在了,都叫做新式類。
經典類中,深度優先查找 。
Python2.3之後,廣度優先也沒有了,至今都采用C3算法
Python3多重繼承C3算法:
#新式類
class D:
pass
class E:
pass
class C(E):
pass
class B(D):
pass
class A(B, C):
pass
print(A.__mro__)
列印結果:
(<class '__main__.A'>, <class '__main__.B'>, <class '__main__.D'>, <class '__main__.C'>, <class '__main__.E'>, <class 'object'>)
可以看到繼承順序是 A - B - D - C - E - object
類方法靜态方法和執行個體方法
執行個體方法:執行個體方法很常見,通常我們在類裡定義的都是執行個體方法,
- 隻是針對于執行個體進行操作,執行個體方法中第一個參數:self,self代表的就是執行個體本身
靜待方法:
- 帶@staticmethod裝飾器的方法叫靜态方法,靜态方法不需要接受cls或self,和普通的函數用法一樣
類方法:
- 帶@classmethod裝飾器的方法叫做類方法,類方法第一個參數是cls,代表的是類本身,(這裡的cls可以修改為任意形式的代表)
def a(self):
pass
@staticmethod
def b():
pass
@classmethod
def c(cls):
pass
Python中的私有屬性
雙下劃綫開頭表示私有屬性,私有屬性的通路,隻能在類中的公共方法中通路,類外部防問不到,無法通過執行個體通路。
私有屬性不僅僅是變量 還可以是函數。
class User:
def __init__(self, birthday):
self.__birthday = birthday
user = User(2000)
print(user.birthday)
運作結果:
AttributeError: 'User' object has no attribute 'birthday'
以上代碼塊結果就是通路不到私有屬性。
但是,私有不是絕對的,隻是加了一個小技巧,Python中将私有屬性的通路變形成這種:
class User:
def __init__(self, birthday):
self.__birthday = birthday
user = User(2000)
print(user._User__birthday)
運作結果:
2000
可以看到,通過變量
_User__birthday
這個就可以通路到私有屬性。
Java中的反射機制也是無法做到絕對安全的,從語言層面講,沒有絕對的私有屬性,Python簡單一些 Java麻煩一些。
Python自省
Python對象自省
何為自省?
自省是通過一定的機制查詢到對象的内部結構
使用
__dict__
魔法函數,dict是用C語言寫的 性能高,做了很多優化,推薦使用。
也可以使用dir() ,dir()會列出類中所有屬性,推薦使用。
class Student():
def __init__(self, scool_name):
self.scool_name = scool_name
if __name__ == "__main__":
user = Student("zhao")
#通過__dict__查詢屬性
print(user.__dict__)
列印結果:
{'scool_name': 'zhao'}
class Student():
def __init__(self, name):
self.name = name
if __name__ == "__main__":
user = Student("zhao")
user.__dict__["school_addr"] = "北京市"
print(user.school_addr)
print(user.name)
a = [1, 2]
print(dir(a))
print(dir(user))
列印結果:
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'name', 'school_addr', 'scool_name']
['__add__', '__class__', '__contains__', '__delattr__', '__delitem__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__iadd__', '__imul__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__reversed__', '__rmul__', '__setattr__', '__setitem__', '__sizeof__', '__str__', '__subclasshook__', 'append', 'clear', 'copy', 'count', 'extend', 'index', 'insert', 'pop', 'remove', 'reverse', 'sort']
Python中super()函數
super()就是調用父類。
class A:
def __init__(self):
print('is A')
class B(A):
def __init__(self):
print('is B')
# 在某些情況下,我們希望在運作完以上代碼調用父類的init方法,一般在Python3中使用下面這種方法調用
super().__init__()
if __name__ == "__main__":
b = B()
列印結果:
is B
is A
super()就是調用父類,其實這樣講并不準确,super()函數調用其實是按照mro查找的順序調用的。
class A:
def __init__(self):
print("A")
class B(A):
def __init__(self):
print("B")
super().__init__()
class C(A):
def __init__(self):
print("C")
super().__init__()
class D(B, C):
def __init__(self):
print("D")
super().__init__()
if __name__ == "__main__":
print(D.__mro__)
d = D()
列印結果:
(<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>)
D
B
C
A
既然我們重寫子類的構造函數, 為什麼還要去調用super再次調用父類構造函數?
有些時候,為了使用父類中的一些已經寫好的方法,是以會有使用super的情況。
上下文管理器
首先介紹下這種用法:
try:
print("code started")
raise KeyError
return 1
except KeyError as e:
print ("key error")
return 2
else:
print("other error")
return 3
finally:
print ("finally")
return 4
result = exe_try()
print (result)
運作結果:
code started
key error
finally
4
在finally語句塊中的代碼,不管上邊代碼是否發生異常都會執行finally中的代碼,優先finally中的return,其次return上邊的。
上下文管理器,也就是with語句,實質上就是為了解放try finally這種寫法而誕生的。
上下文管理器是如何完成的呢?
python是基于協定進行程式設計的,上下文管理器就是一種協定:上下文管理器協定。
上下文管理器協定可以使用是因為實作了兩個魔法函數:
__enter__
和
__exit__
class Sample:
def __enter__(self):
print("enter")
# 擷取資源
return self
def __exit__(self, exc_type, exc_val, exc_tb):
# 釋放資源
print("exit")
def do_something(self):
print("doing something")
with Sample() as sample:
sample.do_something()
運作結果:
enter
doing something
exit
還有一種方法,可以簡便的實作上下文管理器的功能:
@contextlib.contextmanager
可以将一個函數變為上下文管理器
import contextlib
@contextlib.contextmanager
def file_open(file_name):
print ("file open")
yield {}
print ("file end")
with file_open("a.txt") as f_opened:
print("file processing")
運作結果:
file open
file processing
file end
@contextlib.contextmanager
内部會做一些邏輯,其實是利用了生成器的特性。