成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

為了 1% 情形,犧牲 99% 情形下的性能: 蝸牛般的 Python 深拷貝

開發 開發工具
Python的深拷貝很慢,原因在于深拷貝需要維護一個memo用于記錄已經拷貝的對象。而維護這個memo的原因是為了避免相互引用造成的死循環。

最近使用 Python 一個項目,發現 Python 的深拷貝 copy.deepcopy 實在是太慢了。

[[192491]]

相關背景

在 Python 中, 我們有兩種拷貝對象的方式:淺拷貝和深拷貝。淺拷貝和深拷貝都可以在 copy 模塊中找到, 其中 copy.copy() 進行淺拷貝操作, copy.deepcopy() 進行深拷貝操作。淺拷貝是在另一塊地址中創建一個新的對象,但是新對象內的子對象引用指向源對象的子對象。如果這時候我們修改源對象的子對象的屬性, 新對象中子對象的屬性也會改變。為了獲取一個和源對象沒有任何關系的全新對象,我們需要深拷貝。深拷貝是在另一塊地址中創建一個新的對象,同時對象內的子對象也是新開辟的,和源對象對比僅僅是值相同。

下面用一個例子說明淺拷貝和深拷貝的區別,下面的代碼將輸出 "b [2,2,3]" 和 "c [1,2,3]"。

  1. import copyclass A: 
  2.    def __init__(self): 
  3.     array = [1,2,3] 
  4. a = A() 
  5. b = copy.copy(a) 
  6. c = copy.deepcopy(a) 
  7. a.array[0] = 2 
  8. print "b",b.array 
  9. print "c",c.array 

b 是由 a 淺拷貝而來,c 是由 a 深拷貝而來。修改 a.array 之后, b.array 也隨之發生變化。其實 a.array 和 b.array 指向同一個對象。而 c.array 則保持原樣。

深拷貝比淺拷貝符合人類的直覺,代價就是深拷貝實在是太慢了。下面代碼中,case1 和 case2 是等價的。在我的機器上測試,case1 不到一秒,而 case2 則達到了 十秒。那么深拷貝為什么那么慢呢?

  1. import copyclass A: 
  2.    def __init__(self): 
  3.        array = [1,2,3] 
  4.  
  5. a = [A() for i in xrange(100000)] 
  6. a[10].array[0] = 100 
  7.  
  8. ### case1 
  9. b = [A() for i in xrange(100000)] 
  10. for i in xrange(100000): 
  11.     for j in xrange(3): 
  12.        b[i].array[j] = a[i].array[j] 
  13.  
  14. ### case2 
  15. c = copy.deepcopy(a) 

深拷貝分析

為了搞清楚為什么 Python 深拷貝這么慢,我閱讀了下 Python 2.7 版本的 copy.deepcopy 的實現: https://github.com/python/cpython/blob/2.7/Lib/copy.py。其大體結構為:

  1. def deepcopy(x, memo=None_nil=[]): 
  2.     if memo is None: 
  3.         memo = {} 
  4.     d = id(x) 
  5.     y = memo.get(d, _nil) 
  6.     if y is not _nil: 
  7.         return y 
  8.   
  9.     cls = type(x) 
  10.   
  11.     copier = _deepcopy_dispatch.get(cls) 
  12.     if copier: 
  13.         y = copier(x, memo) 
  14.     else: 
  15.         ... ## 其他特殊的拷貝方式 
  16.  
  17.     memo[d] = y 
  18.     return y 

其中 memo 保存著所有拷貝過的對象。為什么要設置 memo 呢?在某些特殊情況下,一個對象的相關對象可以指向它自己,比如雙向鏈表。如果不將拷貝過的對象存著,那程序將陷入死循環。另外一個值得注意的點是 _deepcopy_dispatch,這行代碼根據待拷貝對象的類型,選擇相應的拷貝函數。可選的拷貝函數有:用于拷貝基本數據類型的 _deepcopy_atomic, 用于拷貝列表的 _deepcopy_list,用于拷貝元組 _deepcopy_tuple,用于拷貝字典 的 _deepcopy_dict,用于拷貝自定義對象的 _deepcopy_inst 等等。其中比較重要的拷貝函數 __deepcopy_inst 代碼如下所示:如果對象有 _ _ deepcopy _ _ 函數,則使用該函數進行拷貝;如果沒有,那么先拷貝初始構造參數,然后構造一個對象,再拷貝對象狀態。

  1. def _deepcopy_inst(x, memo): 
  2.     if hasattr(x,'__deepcopy__'): 
  3.         return x.__deepcopy__(memo) 
  4.     if hasattr(x,'__getinitargs__'): 
  5.         args = x.__getinitargs__() 
  6.         args = deepcopy(args, memo) 
  7.         y = x.__class__(*args) 
  8.     else: 
  9.         y = _EmptyClass() 
  10.         y.__class__ = x.__class__ 
  11.  
  12.     memo[id(x)] = y     
  13.     if hasattr(x, '__getstate__'): 
  14.         state = x.__getstate__() 
  15.     else: 
  16.         state = x.__dict__ 
  17.     state = deepcopy(state, memo) 
  18.     if hasattr(y, '__setstate__'): 
  19.         y.__setstate__(state) 
  20.     else: 
  21.         y.__dict__.update(state) 
  22.  
  23.     return y 

深拷貝需要維護一個 memo 用于記錄已經拷貝的對象,這是它比較慢的原因。在絕大多數情況下,程序里都不存在相互引用。但作為一個通用的模塊,Python 深拷貝必須為了這 1% 情形,犧牲 99% 情形下的性能。

一個場景

上面給出了深拷貝效率對比的結果,已經可以看出深拷貝很慢了,但沒有辦法直觀感受深拷貝拖累整個程序的運行速度。下面給一個實際項目的例子,最近在寫的一些游戲環境。玩家程序獲得游戲環境給出的信息,當前玩家程序選擇合適的動作,游戲環境根據該動作推進游戲邏輯;重復上述過程,直到分出勝負;整個過程如下所示。給玩家程序的信息必須從游戲環境深拷貝出來。如果給玩家的信息是從游戲環境淺拷貝出來的,那么玩家程序有可能通過信息獲取游戲秘密或者操縱游戲。

拷貝

我們已經知道默認的深拷貝很慢。為了改進深拷貝的效率,我們在信息類以及相關類都添加了 _ _ deepcopy _ _ 函數,下面是信息類示例。

  1. class FiveCardStudInfo(roomai.abstract.AbstractInfo): 
  2.     public_state  = None 
  3.     person_state  = None 
  4.  
  5.     def __deepcopy__(self, memodict={}): 
  6.         info = FiveCardStudInfo() 
  7.         info.public_state = self.public_state.__deepcopy__() 
  8.         info.public_state = self.person_state.__deepcopy__() 
  9.         return info 

簡單做了一個效率對比實驗:讓隨機玩法玩家打一千局梭哈,統計耗時。實驗結果如下所示。

原始深拷貝

使用原始深拷貝,程序運行時間為 143 秒,其中深拷貝時間 134 秒,深拷貝時間占整個程序時間的 94%。使用了改進深拷貝,程序運行時間是 23 秒,其中深拷貝時間 16 秒,占比為 69 %。雖然深拷貝依然占到了 69%,相比之間 94 % 已經下降很多。整個程序運行時間也從 134 秒減少到 23 秒了。

總結

Python 的深拷貝很慢,原因在于深拷貝需要維護一個 memo 用于記錄已經拷貝的對象。而維護這個 memo 的原因是為了避免相互引用造成的死循環。但作為一個通用的模塊,Python 深拷貝必須為了這 1% 情形,犧牲 99% 情形下的性能。我們可以通過自己實現 _ _ deepcopy _ _ 函數來改進其效率。

【本文為51CTO專欄作者“李立”的原創稿件,轉載請通過51CTO獲取聯系和授權】

戳這里,看該作者更多好文

責任編輯:趙寧寧 來源: 51CTO專欄
相關推薦

2010-07-20 11:18:12

SQL server阻

2010-07-20 11:26:08

SQL Server阻

2013-02-20 16:02:02

Android開發內存泄露

2009-07-02 10:52:30

JavaBean規范

2018-10-17 14:50:08

2020-04-24 10:44:45

Scala代碼開發

2024-04-17 09:01:08

Python深拷貝淺拷貝

2010-08-19 09:54:42

DB2死鎖

2021-07-16 12:33:24

Javascript深拷貝淺拷貝

2017-08-16 13:30:05

Java深拷貝淺拷貝

2012-02-07 17:07:46

虛擬化服務器虛擬化

2021-06-14 09:39:44

網絡威脅4G5G

2010-08-10 13:36:00

2009-05-19 17:28:44

深拷貝淺拷貝clone()

2020-10-12 08:35:22

JavaScript

2017-05-24 11:54:55

Javascript深拷貝

2018-07-06 11:03:08

手機全面屏劉海屏

2022-07-26 08:07:03

Python淺拷貝深拷貝

2024-02-05 22:56:16

C++拷貝開發

2019-11-01 16:00:04

網絡安全通信法律
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 99福利视频导航 | 国产精品美女久久久 | 干干干日日日 | 欧美综合一区 | 在线国产一区二区三区 | 亚洲精选久久 | 97人澡人人添人人爽欧美 | 操人网 | 高清黄色毛片 | 亚洲午夜精品一区二区三区他趣 | 少妇淫片aaaaa毛片叫床爽 | 久久国产精品-国产精品 | 黄色高清视频 | 国产精品色婷婷久久58 | 中文字幕二区三区 | 午夜免费福利影院 | 日韩高清一区 | 国产成人在线视频免费观看 | 免费黄色的网站 | 精品视频一二区 | 91精品国产综合久久久久久丝袜 | 午夜视频在线免费观看 | 隔壁老王国产在线精品 | 欧美aaaaaaaa| 国产精品毛片无码 | 成人免费在线视频 | 中文字幕 国产精品 | 亚洲狠狠| 久久免费观看视频 | 亚洲国产精品99久久久久久久久 | 国产精品高潮呻吟久久av野狼 | 99精品欧美一区二区三区 | 刘亦菲国产毛片bd | 一区二区三区视频在线观看 | 日韩伦理一区二区 | 蜜桃特黄a∨片免费观看 | 日韩一区二区三区在线视频 | 亚洲成人一区 | 高清视频一区 | 欧美自拍第一页 | 91九色视频 |