學習筆記(抽象基類、上下文管理、多型的應用、節約記憶體的例項)

weixin_45389821發表於2020-12-19

學習筆記(抽象基類、上下文管理、多型的應用、節約記憶體的例項)

抽象基類

  • 什麼是抽象類?
    • 抽象類是一個特殊的類,它的特殊之處在於只能被繼承,不能被例項化.
  • 抽象類的作用
    • 從一堆類中抽取相同的內容,內容包括資料屬性和函式屬性。我的個人理解就是抽象基類就是一個框架模型。
  • 抽象基類的繼承
    • 繼承了抽象基類必須重寫指定的方法。
import abc

class F_file(metaclass=abc.ABCMeta):
    all_type = 'file'
    @abc.abstractmethod #定義抽象方法
    def read(self):
        pass

    @abc.abstractmethod
    def write(self):
        pass

class TXT(F_file):
    def read(self):
        print('讀txt檔案')

    def write(self):
        print('寫入到txt檔案中')

a=TXT()
print(issubclass(TXT,F_file)) #True
print(isinstance(a,F_file)) #True
- 使用register方法註冊。
import abc

class A(metaclass=abc.ABCMeta):
    all_title='file'

    @abc.abstractmethod
    def read(self):
        pass

    @abc.abstractmethod
    def write(self):
        pass

class B(object):
    pass

A.register(B)

print(issubclass(B,A)) #True

自定義一個類的案例

我們想自定義一種新型別的元組,對於傳入的可迭代物件,我們只保留其中int型別且值大於0的元素。
例:

data = [12,-5,['tu',1],5,'8',1.1]
print(tuple(data))
#(12, -5, ['tu', 1], 5, '8', 1.1)
#新型別元組:
#new_tuple(data)  =>(12,5)

思路:繼承元組並重寫方法

class new_tuple(tuple):
	def __new__(cls, iterable):
		f = (i for i in iterable if isinstance(i,int) and i > 0 )
		return  super().__new__(cls,f)

print(new_tuple(data))

上下文管理

  • 同時包含 __enter__() 和 __exit__() 方法的物件就是上下文管理器。
    
  •   __enter__(self):進入上下文管理器自動呼叫的方法。	
    
  •  __exit__(self, exc_type, exc_value, exc_traceback):退出上下文管理器自動呼叫的方法
    
class Get_info():
	def __init__(self,name):
		self.name = name

	def __enter__(self):
		print('獲取資源')
		return self.name  #將返回值作為as的物件
			
	def __exit__(self,exc_type,exc_val,exc_tb):
		print('錯誤型別:',exc_type)
		print('錯誤值:',exc_val)
		print('回溯錯誤:',exc_tb)
		return True #返回值為True時丟擲錯誤但不會中斷程式
		
with Get_info('lh') as f:
	print(f)

執行結果:

	獲取資源
	lh
	釋放資源
	錯誤型別: None
	錯誤值: None
	回溯錯誤: None

使用內建模組contextlib,@contextmanager讓我們通過編寫generator來簡化上下文管理。

import contextlib

@contextlib.contextmanager
def show_info(name):
    #__enter__方法
    file_name = open(name,'r',encoding='utf-8')
	
	#yield
    try:
        yield file_name

    except Exception as err:
        print(err)

    #__exit__()方法
    finally:
        file_name.close()


with show_info('test.txt') as f:
    print(f.readline())

抽象基類的應用

import abc
import math
# from functools import total_ordering

# @total_ordering
class Shape(metaclass=abc.ABCMeta):

    @abc.abstractmethod
    def area(self):
        pass

    def __lt__(self, other):
        return self.area() < other.area()

    def __eq__(self, other):
        return self.area() == other.area()

class Cricle(Shape):
    def __init__(self,x):
        self.x = x
    def area(self):
        return self.x ** 2 * math.pi

class Square(Shape):
    def __init__(self,l):
        self.l = l

    def area(self):
        return self.l ** 2

class Rectange(Shape):
    def __init__(self,x,y):
        self.x = x
        self.y = y

    def area(self):
        return self.x * self.y

cricle=Cricle(5)
square=Square(6)
rectange=Rectange(4,9)

print(cricle < rectange)
print(square == rectange)

多型的應用

實現一個統一的獲取面積的函式

import  math
class Cricle(object):
    def __init__(self,x):
        self.x = x

    def get_area(self):
        return self.x ** 2 * math.pi


class Rectange(object):
    def __init__(self,x,y):
        self.x = x
        self.y = y

    def get_area(self):
        return self.x * self.y

class Square(object):
    def __init__(self,x):
        self.x = x

    def get_area(self):
        return self.x ** 2

cricle=Cricle(5)
rectange=Rectange(7,6)
square=Square(7)

shape_list=[cricle,rectange,square]
for i in shape_list:
    print(i.get_area())

使用getattr方法實現

import  math
class Cricle(object):
    def __init__(self,x):
        self.x = x

    def get_area(self):
        return self.x ** 2 * math.pi


class Rectange(object):
    def __init__(self,x,y):
        self.x = x
        self.y = y

    def get_Area(self):
        return self.x * self.y

class Square(object):
    def __init__(self,x):
        self.x = x

    def area(self):
        return self.x ** 2

def get_all_area(object):
    method_name = ['get_area','get_Area','area']
    for name in method_name:
        goal = getattr(object,name,None)
        if goal:
           return goal()

cricle=Cricle(5)
rectange=Rectange(7,6)
square=Square(7)

shape_list=[cricle,rectange,square]
area_list = list(map(get_all_area,shape_list))
print(area_list)

節約記憶體的例項

在遊戲開發中,有一個玩家類Player,每有一個線上玩家,在伺服器內則有一個player的例項,當線上的人數很多時,將產生大量例項(百萬級)。
如何降低這些大量例項的記憶體開銷?

  •    由於Python中預設用一個字典來儲存一個物件的例項屬性,使得我們在執行時可以任意設定新屬性。如果建立成千上萬個這樣的小類,Python就會浪費掉很多記憶體,所以python中引入了 __slot__屬性。這個屬性的兩個作用:1、給類指定一個固定大小的空間存放屬性;2、無法給該類建立的例項新增新的屬性。
    
#首先建立兩個物件使用__dict__來檢視物件內的內容
class Player1(object):
	pass

class Player2(object):
	__slot__ = ()
	pass

print(Player1.__dict__)
print(Player2.__dict__)

執行結果:

	{'__module__': '__main__', '__dict__': <attribute '__dict__' of 'Player1' objects>, '__weakref__': <attribute '__weakref__' of 'Player1' objects>, '__doc__': None}
	{'__module__': '__main__', '__slots__': (), '__doc__': None}

顯而易見,使用__slot__()後,物件中的屬性都由__slot__ : ()取代了。這時你可能是會說,雖然屬性看著確實少了,但是沒有說服力,那確實,下面我們拿資料來證明。
這裡我需要呼叫tracemalloc模組來對記憶體使用進行跟蹤

import tracemalloic

class Player1(object):    
    def __init__(self,name,uid,attribute=1,skill=2):
        self.name = name
        self.uid = uid
        self.attribute = attribute
        self.skill = skill

class Player2(object):    
	__slots__ = ('name','uid','attribute','skill')
    def __init__(self,name,uid,attribute=1,skill=2):
        self.name = name
        self.uid = uid
        self.attribute = attribute
        self.skill = skill
       
#記憶體跟蹤
tracemalloc.start() #開始跟蹤記憶體分配
player1 = [Player1('haha',1,) for i in range(100000)]  #size=15.3 MiB
# player2=[Player2('haha',1) for i in range(100000)] #size=7055 KiB

snapshot = tracemalloc.take_snapshot() #快照,當前記憶體分配
top = snapshot.statistics('filename')  #快照物件的統計  監控檔案

for start in top:
    print(start)       

相關文章