天天看點

python面向對象的多态-類相關内置函數-類内置魔法函數-疊代器協定-上下文管理-04...多态常見的内置函數面向對象的内置魔法函數疊代器協定上下文管理

多态

一種事物具備不同的形态

例如:水 --> 固态、液态、氣态

多态:

# 多個不同對象可以相應同一個對象,産生不同的結果

首先強調,多态不是一種特殊的文法,而是一種狀态,特性(多個不同對象可以相應同一個方法,長身不同的結果)

好處:對于使用者而言,使用成本降低

​ 之前的USB接口下的滑鼠,鍵盤,就屬于多态

接口抽象類 鴨子類型都可以寫出具備多态的代碼(最簡單的就是鴨子類型)

'''
要管理 雞 鴨 鵝
    如何能夠最友善的管理,就是我說同一句話,他們都能了解
    他們擁有相同的方法
'''


class Chicken:
    @staticmethod
    def bark():
        print("咯咯咯咯")

    @staticmethod
    def spawn():
        print("下雞蛋...")


class Duck:
    @staticmethod
    def bark():
        print("嘎嘎嘎")

    @staticmethod
    def spawn():
        print("下鴨蛋...")


class E:
    @staticmethod
    def bark():
        print("鵝鵝鵝鵝")

    @staticmethod
    def spawn():
        print("下鵝蛋...")


j = Chicken()
y = Duck()
e = E()


def mange(obj):
    obj.spawn()


mange(j)
# 下雞蛋...
mange(y)
# 下鴨蛋...
mange(e)
# 下鵝蛋...                

python中常見的多态(不同的對象類型,擁有相同的方法,不同的結果)

# 不管什麼類型,他都與type這個方法  ---> python中多态的展現
# 多态在python中其實很常見,因為到處充斥着繼承與組合
a = 10
b = '10'
c = [10]

print(type(a))
print(type(b))
print(type(c))
# <class 'int'>
# <class 'str'>
# <class 'list'>                

常見的内置函數

  • isinstance

# isinstance()  # 判斷一個對象是不是某個類的執行個體
 # 參數1 要判斷的對象,參數2 要判斷的類型 
def add_num(a, b):
    # if type(a) == type(b):
    if isinstance(a, int) == isinstance(b, int):
        return a+b
    else:
        print("資料類型不符")


add_num("100", 10)                
  • issubclass

# issubclass() # 判斷一個類是不是另一個類的子類
#   參數一:子類,參數二:父類
class Animal:
    @staticmethod
    def eat():
        print("動物得吃東西...")


class Pig(Animal):
    @staticmethod
    def eat():
        print("豬吃東西...")


class Tree:
    @staticmethod
    def light():
        print("植物光合作用...")


def mange(obj):
    # if isinstance(obj, Animal):
    if issubclass(type(obj), Animal):
        obj.eat()
    else:
        print("不是動物...")


pig = Pig()
t = Tree
mange(pig)
# 豬吃東西...
mange(Tree)  # AttributeError: type object 'Tree' has no attribute 'eat'
# 不是動物...                

面向對象的内置魔法函數

  • __str__

'''
    __str__ 會在對象被轉為字元串時,轉換的結果就是這個函數的傳回值
    使用場景:我們可以利用該函數來自定義,對象是列印格式
'''
class Person:
    def __str__(self):  # 重寫object中的 __str__
        print("__str__ run")
        return 'abc'  # abc下面的報錯那裡就變成了 abc


p = Person()
# 所有的類都可以轉成字元串
print(p)  # 列印了 __str__ run,又報錯了
# __str__ run
# abc  # 寫return 之前TypeError: __str__ returned non-string (type NoneType)  --> __str__ 必須要有一個str類型的傳回值

str(p)  # 沒有寫print 在控制台也輸出了 __str__ run
# __str__ run                

将對象以指定格式輸出

# print列印對象時記憶體位址,沒什麼意義,此時就可以利用__str__來自定義對象列印
class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def __str__(self):  # 重寫object中的 __str__
        return f"這是要給Person對象,name:{self.name},age:{self.age}"


p = Person('jack', 10)
# 所有的類都可以轉成字元串
print(p)  # 列印了
# 這是要給Person對象,name:jack,age:10                
  • __del__

# del 析構函數   (__init__ 構造函數)
# 執行時機:手動删除對象時立馬執行,或是程式運作結束時也會自動執行(垃圾回收機制?)
# 使用場景:當你的對象再使用過程中打開了不屬于解釋器的資源,例如檔案,網絡端口
import time


class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def __del__(self):  # 重寫object中的 __str__
        print("del run...")
        return "del run"


p = Person("jack", 20)
# del p  # 删除對象觸發 __del__函數執行
# # del run...
time.sleep(2)
print("over")
# over
# del run...  # 程式結束後會把名稱空間清除掉,清除時觸發了 __del__,列印出 del run...                

結束使用自動關閉檔案資源案例

class FileTool:
    # 該類用于簡化檔案的讀寫操作

    def __init__(self, path):
        self.file = open(path, 'rt', encoding='utf-8')

    def read(self):
        return self.file.read()  # rt模式不推薦直接讀位元組(漢字、英文位元組不同),可以一行一行讀

    # 執行這個函數可以确定一個函數,這個對象肯定不用了,是以就可以放心的關心檔案了
    def __del__(self):
        self.file.close()


tool = FileTool("a.txt")
print(tool.read())  # 檔案屬于作業系統,不受垃圾回收機制管理
# aaaaaaaaaaaa

# 不知道什麼不使用該對象,那就寫在 __del__函數中,當其被删除時,指定關閉資源                
  • __call__

# call  調用對象時自動執行
# 執行時機:在調用對象時自動執行 ---> 對象()


class A:
    # 調用對象時自動執行
    def __call__(self, *args, **kwargs):
        print("__call__ run...")
        print(args)
        print(kwargs)


a = A()

a(1, 2, a=100, c=300)  # 對象加括号調用
# __call__ run...
# (1, 2)
# {'a': 100, 'c': 300}                
  • __slots__

python是動态語言,可以在運作期間動态修改對象的屬性,如何能存儲更多屬性呢?

需要開啟更大的記憶體區域,将原始的屬性指派過去

問題:如果開啟的容量太大(為了效率犧牲了空間),将造成記憶體的浪費

解決方案:在建立對象是告訴系統這個對象隻有哪些屬性,也就是固定了對象的屬性數量,這樣就可任意要多少開多少,減少空間浪費(使用

__slots__

python面向對象的多态-類相關内置函數-類内置魔法函數-疊代器協定-上下文管理-04...多态常見的内置函數面向對象的内置魔法函數疊代器協定上下文管理
import sys


class Person:
    __slots__ = ['name']  # 加了以後再添加屬性就不行了,限制屬性

    # def __init__(self, name, age):
    def __init__(self, name):
        self.name = name
        # self.age = age  # 未在__slots__中聲明,直接報錯 AttributeError: 'Person' object has no attribute 'age'


# p = Person("jck", 18)
p = Person("jck")

print(sys.getsizeof(p))  # 擷取對象占用記憶體大小
# 56 ---> 48  ---> __slots__ 指定有哪些屬性,進而節省了記憶體空間(沒指定__slots__之前56,指定之後48)

# print(p.__dict__)  # 報錯,可變字典也被省掉了(名稱空間連開都不開了),AttributeError: 'Person' object has no attribute '__dict__'                

該屬性是一個類屬性,用于優化對象記憶體

優化的原理:将原本不固定的屬性數量,變得固定了,這樣的解釋器就不會以這個對象建立名稱空間(是以

__dict__

也沒了),進而達到減少記憶體開銷的效果

另外當類中出現了

__slots__

時将導緻這個類的對象不再添加

__slots__

定義之外的屬性

  • __getattr__ __setattr__ __delattr__ 及點文法原理

__getattr__ 用 .通路屬性時,如果屬性不存在,執行
__setattr__ 用 .設定屬性時執行
__delattr__ 用del 對象.屬性 删除屬性時,執行

這幾個函數反映了 python解釋器是如何實作 . 文法的原理

__getattribute__ 該函數也是用來擷取屬性
在擷取屬性時如果存在__getattribute__則先執行該函數,如果沒有拿到屬性則繼續調用__getattr__函數,如果拿到了則直接傳回                
class A:
    def __getattr__(self, item):
        print("__getattr__")
        return self.__dict__.get(item)

    def __setattr__(self, key, value):
        super().__setattr__(key, value)  # 這個不寫将導緻指派不成功,得到None
        print('__setattr__')

    def __delattr__(self, item):
        print('__delattr__')
        print(item)
        self.__dict__.pop(item)


a = A()
a.name = 'jack'
# __setattr__
print(a.name)  # 這個屬性存在,就沒有調用 __getattr__
# jack


b = A()
b.__dict__["name"] = 'jackson'  # 通過操作__dict__ 也可以操作屬性(. 文法的背後就是操作 __dict__)
print(b.name)  # 這個屬性存在,就沒有調用 __getattr__
# jackson

del b.name  # 觸發 __delattr__
# __delattr__
# name

print(b.name)  # b沒有name這個屬性了,就觸發了 __getattr__
# __getattr__
# None  # b沒有name這個屬性了                
class B:
    def __setattr__(self, key, value):  # 利用了 .文法指派改值就會觸發這個函數
        self.__dict__[key] = value
        print(f"{key}:{value}")


b = B()
b.name = 'jerry'
# name:jerry
b.name = 'tom'
# name:tom
print(b.name)
# tom

b.__dict__['halo'] = 'hi'  # 直接通過操作 __dict__ 也可以完成屬性的增改
print(b.halo)
# hi                
  • []

    的實作原理(

    __getitem__ __setitem__ __delitem__

任何的符号,都會被解釋器解釋稱特殊含義,例如 . [] ()

__getitem__ 當你用中括号去擷取屬性時 執行
__setitem__ 當你用中括号去設定屬性時 執行
__detitem__ 當你用中括号去删除屬性時 執行                
'''
需求:
    讓一個對象支援 點文法來取值,也支援括号取值
'''


class MyDict(dict):
    def __getattr__(self, key):
        return self.get(key)
        # return self[key]  # KeyError: 'name'

    def __setattr__(self, key, value):
        self[key] = value

    def __delattr__(self, item):
        del self[item]


# 繼承 dict 可以直接用字典的一些方式
a = MyDict()
a['name'] = 'jack'
print(a['name'])
# jack

# 使用 .文法(通過實作__getattr__ 、__setattr__、__delattr__來實作)
a.name = 'sum'
print(a.name, a['name'])
# sum sum
print(a['name'])
# sum
a.name = 'jackson'
print(a.name)
# jackson
del a.name
print(a.name)
# None  # 用的是 .get 是以不會報錯                
  • > >= == != < <=

    等比較運算符的的實作原理(運算符重載)(

    __gt__ __ge__ __eq__ __ne__ __lt__ __le__

    )

當我們在使用某個符号時,python解釋器都會為這個符号定義一個含義,同時調用對應的處理函數,當我們需要自定義對象的比較規則時,就可以在子類中覆寫大于等于等的方法

案例

# 自定義對象的比較
# 對象直接無法直接比較大小


class Person:
    def __init__(self, name, height, age):
        self.name = name
        self.height = height
        self.age = age


p1 = Person('jason', 185, 18)
p2 = Person('tank', 179, 18)
# print(p1 > p2)  # TypeError: '>' not supported between instances of 'Person' and 'Person'


class Student:
    def __init__(self, name, height, age):
        self.name = name
        self.height = height
        self.age = age

    # 自定義比較規則
    def __gt__(self, other):
        print(self)
        print(other)
        print("__gt__")

        # 比身高
        # if self.height > other.height:
        #     return True
        return self.height > other.height
        # 沒有傳回值預設傳回 None 即 False

    def __eq__(self, other):
        print("eq------")
        return self.name == other.name


stu1 = Student("jack", 180, 28)
stu2 = Student("rose", 165, 27)

print(stu1 > stu2)  # 直接報錯,TypeError: '>' not supported between instances of 'Student' and 'Student'
# <__main__.Student object at 0x000001992C7C8F60>
# <__main__.Student object at 0x000001992C7C8F98>
# __gt__
# True
print(stu1 < stu2)  # 大于和小于隻要實作一個即可,符号如果不同解釋器會自動交換兩個對象的位置
# <__main__.Student object at 0x000001992C7C8F98>
# <__main__.Student object at 0x000001992C7C8F60>
# __gt__
# False
print(stu1)
# <__main__.Student object at 0x000001992C7C8F60>
print(stu2)
# <__main__.Student object at 0x000001992C7C8F98>                

原本自定義對象無法直接使用大于小于來進行比較,我們可以自定義運算符來實作,讓自定義對象也支援比較符

上述代碼中.other指的是另一個參與比較的對象

大于和小于隻要實作一個即可,符号如果不同解釋器會自動交換兩個對象的位置

疊代器協定

疊代器:是指具有

__iter__

__next__

的對象

我們可以為對象增加這兩個方法來讓對象變成疊代器

class MyIter:
    # num 傳入,用來指定疊代次數
    def __init__(self, num):
        self.num = num
        self.c = 0

    def __iter__(self):
        return self

    def __next__(self):
        self.c += 1
        if self.c <= self.num:
            return "hahha"
        raise StopIteration  # 抛出異常


for i in MyIter(3):
    print(i)
# hahha
# hahha
# hahha                

自定義range函數

class MyRange:
    def __init__(self, start, end, step=1):
        self.start = start - 1
        self.end = end
        self.step = step

    def __iter__(self):
        return self

    def __next__(self):
        self.start += self.step
        if self.start < self.end:
            return self.start
        raise StopIteration


for i in MyRange(1, 3):
    print(i)
# 1
# 2                

上下文管理

上下文:這個概念屬于語言學科,指的是一段話的意義,要參考目前的場景,即上下文

在python中,上下文可以了解為一個代碼區間,一個範圍,例如with open 打開的檔案僅在這個上下文中有效

上下文涉及到的兩個方法

  • __enter__

    :表示進入上下文(進入某個場景了)
  • __exit__

    :表示退出上下文(離開了某個場景了)

​ 案例

class MyOpen:

    def __enter__(self):
        print("enter....")

    def __exit__(self, exc_type, exc_val, exc_tb):  # exc --> exception
        print("exit.....")
        print(exc_type, exc_val, exc_tb)


with MyOpen() as m:
    print("start...")
    # 1 + '123'
# enter....
# exit.....
# None None None                

實作了上面的兩個方法就可以配合with語句用了,當執行with語句時,會先執行

__enter__

,當代碼執行完畢後執行

__exit__

,或者代碼遇到了異常會立即執行

__exit__

,并傳入錯誤資訊,包含錯誤的類型,錯誤的資訊,錯誤的追蹤資訊

class MyOpen:

    def __enter__(self):
        print("enter....")

    def __exit__(self, exc_type, exc_val, exc_tb):  # exc --> exception
        print("exit.....")
        print(exc_type, exc_val, exc_tb)
        return True  # return True 可以讓程式不報錯


with MyOpen() as m:
    print("start...")
    1 + '123'  # TypeError: unsupported operand type(s) for +: 'int' and 'str'
# enter....
# exit.....
# None None None  # 沒有報錯時列印這個
# <class 'TypeError'> unsupported operand type(s) for +: 'int' and 'str' <traceback object at 0x00000283F3EE0608>  # 有錯時列印這個,若__exit__ 傳回為True則控制台不報錯,否則控制台也會報錯                

注意點

__enter__ 函數應該傳回對象自己
__exit__ 函數可以有傳回值,是一個bool類型,用于表示異常是否被處理,僅在上下文中出現異常時有用
如果為True 則意味着,異常已經被處理了
    False 異常未被處理,程式将中斷報錯                

轉載于:https://www.cnblogs.com/suwanbin/p/11266025.html