天天看點

Python進階知識點學習(三)

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

内部會做一些邏輯,其實是利用了生成器的特性。