Python內存管理指南
- 2019 年 11 月 27 日
- 筆記
原標題 | Memory Management in Python
作 者 | Jun Wu
翻 譯 | 天字一號
審 校 | 唐里、Pita
對於軟件開發人員而言,了解內存管理很重要。隨着Python在軟件開發中得到廣泛使用,編寫高效的Python代碼通常意味着需要編寫內存高效使用的代碼。隨着大數據的使用越來越廣泛,內存管理的重要性不容忽視。無效的內存管理會導致應用程序和服務器端組件運行緩慢。內存泄漏通常會導致花費大量時間進行測試和調試,它還會嚴重破壞數據處理並引起並發處理問題。
即使大多數Python的內存管理都是由Python內存管理器完成的,但了解最佳編碼實踐以及Python的內存管理器的工作方式仍可以使代碼更高效和可維護。
對於軟件開發人員而言,內存管理最重要的部分是內存分配。了解在計算機的物理或虛擬內存中分配空白空間的過程至關重要。有兩種類型的內存分配。
靜態內存分配 – 程序在編譯時分配了內存。例如在C / C ++中,您只能聲明具有固定大小的靜態數組。在編譯時分配內存。堆棧用於實現靜態分配。在這種情況下,不能重用內存。
static int a=10;
動態內存分配 – 在運行時為程序分配了內存。例如,在C / C ++中,您可以使用一元運算符new聲明數組。內存在運行時分配。堆用於實現動態分配。在這種情況下,不需要時可以釋放和重用內存。
int *p; p=new int;
關於Python的好處是Python中的所有東西都是對象。這意味着動態內存分配是Python內存管理的基礎。當不再需要對象時,Python內存管理器將自動從它們中回收內存。
Python是使用C編程語言實現的高級編程語言。Python內存管理器管理Python的內存分配。有一個私有heap,其中包含所有Python對象和數據結構。Python內存管理器按需管理Python堆。Python內存管理器具有特定於對象的分配器,可為int,string等特定對象分別分配內存。在此之下,原始內存分配器與操作系統的內存管理器進行交互,以確保私有堆上有空間。
Python內存管理器管理稱為「塊」的內存塊。相同大小的塊的集合構成了「池」。池是在Arenas上創建的,在堆= 64池上分配了256kB的內存塊。如果對象被銷毀,則內存管理器將用相同大小的新對象填充此空間。
方法和變量在堆棧存儲器中創建。每當創建方法和變量時,都會創建一個堆棧框架。只要返回方法,這些框架就會自動銷毀。
在堆內存中創建對象和實例變量。一旦返回變量和函數,將對垃圾對象進行垃圾回收。
請務必注意,Python內存管理器不一定會將內存釋放回操作系統,而是將內存返回給python解釋器。Python有一個小的對象分配器,用於分配內存以供進一步使用。在長時間運行的進程中,您可能有未使用內存的增量保留。
使用聯接將項目添加到列表是高效Python代碼的最佳做法
無需將line1,line2分別添加到mymsg,而是使用list和join。
不要這樣:
mymsg=』line1n』 mymsg+=』line2n』
最好這樣:
mymsg=[『line1』,』line2'] 『n』.join(mymsg)
避免對字符串使用+運算
如果可以避免,請不要使用+運算符進行串聯。由於字符串是不可變的,因此每次將元素添加到字符串時,Python都會創建一個新的字符串和一個新的地址。這意味着每次更改字符串時都需要分配新的內存。
不要這樣做:
msg=』hello』+mymsg+』world』
更好的選擇:
msg=』hello %s world』 % mymsg
使用Generators
生成器允許您創建一個函數,一次返回一個項目,而不是一次返回所有項目。這意味着,如果您有大型數據集,則不必等待整個數據集都可以訪問。
def __iter__(self): return self._generator() def _generator(self): for itm in self.items(): yield itm
將評估置於循環之外
如果要遍曆數據,則可以使用正則表達式的緩存版本。
match_regex=re.compile(「foo|bar」) for i in big_it: m = match_regex.search(i) ….
將函數分配給局部變量
Python訪問局部變量要比全局變量有效得多。將函數分配給局部變量,然後使用它們。
myLocalFunc=myObj.func for i in range(n): myLocalFunc(i)
使用內置函數和庫
儘可能使用內置函數和庫。內置函數通常使用最佳內存使用方法來實現。
不要這樣做:
mylist=map(str.lower, oldlist)
與循環相比,使用關鍵字參數創建數據集的更好選擇:
mycounter = Counter (a = 1, b = 2, c = 3, d = 5, e = 6, f = 7, g = 8) for i in mycounter.elements()
通過使用itertools擺脫不必要的循環
itertools(https://docs.python.org/3/library/itertools.html)為您節省了大量的循環時間。它還擺脫了代碼的複雜性。
不要這樣做:
mylist=[] for shape in [True, False]: for weight in (1, 5): firstlist=firstlist+function(shape, weight)
最好這樣:
from itertools import product, chain list(chain.from_iterable(function(shape, weight) for weight, shape in product([True, False], range(1, 5))))
覆蓋_new_並利用元類來進行安全和內存管理——作者@maxwell flitton
重寫_new_並利用元類進行安全和內存管理,方法是@maxwell flitton 在執行Singleton和Flyweight模式時,重寫__new__並利用元類對內存管理也非常有用和安全。例如,這是一個讀取Yaml文件的dict對象的示例。因為它的元類一經定義便是單例設計模式,因此可以將其導入系統中的任何位置並再次定義,並且解釋器將僅指向初始對象。它減少了內存佔用並確保了安全性。不管團隊中的其他開發人員多麼初級,它們都不會導致重複的對象,從而防止它們更改系統某一部分中的命令,並防止另一部分中引用另一條命令。
class Singleton(type): _instances = {} def __call__(cls, *args, **kwargs): if cls not in cls._instances: cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs) return cls._instances[cls] class ConfigDict(dict, metaclass=Singleton): def __init__(self): super().__init__(self.read_config_file()) @staticmethod def read_config_file(): 「」」 Reads config file based on path passed when running app. :return: (dict) loaded data from yml file 「」」 config_file_path = sys.argv[-1] if not config_file_path.endswith(「.yml」): raise ConfigDictError(message=」yml file not passed into flask app but {} instead」.format(config_file_path)) return yaml.load(open(str(config_file_path)), Loader=yaml.FullLoader)
如何檢查Python代碼的性能
您可以使用配置文件模塊(例如cProfile和Profile:https://docs.python.org/3/library/profile.html)進行性能檢查。
python -m cProfile [-o output_file][-s sort_order](-m module | myscript.py)
閱讀有關Python內存管理的更多信息,可查看以下資源:
- 流利的Python:清晰,簡潔,有效的編程(http://geni.us/xA2PP)
- Python Cookbook:精通Python 3(http://geni.us/yAoO1S)
- 真正的Python:Python中的內存管理(https://realpython.com/python-memory-management/)
- Python.org內存管理(https://docs.python.org/3/c-api/memory.html)
- Atem Golubin:Python中的內存管理(https://rushter.com/blog/python-memory-managment/)


