導航:首頁 > 編程語言 > 銷毀一本圖書的代碼python

銷毀一本圖書的代碼python

發布時間:2024-04-13 14:38:42

python線程怎麼銷毀

【Python】線程的創建、執行、互斥、同步、銷毀
還是《【Java】利用synchronized(this)完成線程的臨界區》(點擊打開鏈接)、《【Linux】線程互斥》(點擊打開鏈接)、《【C++】Windows線程的創建、執行、互斥、同步、銷毀》(點擊打開鏈接)中的設置多個線程對一個ticket進行自減操作,用來說明Python中多線程的運用,涉及的創建、執行、互斥、同步、銷毀問題。
運行結果如下,還是差不多,運行三次,每次的運行結果,每個線程最終的得票結果是不同的,但是4個線程最終「得票」的總和為 ticket 最初設置的值為100000,證明這4個線程成功實現了互斥。
雖然每次運行結果是不同,但是可以看得出每次運行結果大抵上是平均的。貌似Python對線程作系統資源的處理,比Java要好。
然而,Python總要實現多線程,代碼並不像想像中簡單,具體如下:
[python] view plain print?在CODE上查看代碼片派生到我的代碼片
# -*-coding:utf-8-*-
import threading;
mutex_lock = threading.RLock(); # 互斥鎖的聲明
ticket = 100000; # 總票數
# 用於統計各個線程的得票數
ticket_for_thread1 = 0;
ticket_for_thread2 = 0;
ticket_for_thread3 = 0;
ticket_for_thread4 = 0;
class myThread(threading.Thread): # 線程處理函數
def __init__(self, name):
threading.Thread.__init__(self); # 線程類必須的初始化
self.thread_name = name; # 將傳遞過來的name構造到類中的name
def run(self):
# 聲明在類中使用全局變數
global mutex_lock;
global ticket;
global ticket_for_thread1;
global ticket_for_thread2;
global ticket_for_thread3;
global ticket_for_thread4;
while 1:
mutex_lock.acquire(); # 臨界區開始,互斥的開始
# 僅能有一個線程↓↓↓↓↓↓↓↓↓↓↓↓
if ticket > 0:
ticket -= 1;
# 統計哪到線程拿到票
print "%s搶到了票!票還剩餘:%d。" % (self.thread_name, ticket);
if self.thread_name == "線程1":
ticket_for_thread1 += 1;
elif self.thread_name == "線程2":
ticket_for_thread2 += 1;
elif self.thread_name == "線程3":
ticket_for_thread3 += 1;
elif self.thread_name == "線程4":
ticket_for_thread4 += 1;
else:
break;
# 僅能有一個線程↑↑↑↑↑↑↑↑↑↑↑↑
mutex_lock.release(); # 臨界區結束,互斥的結束
mutex_lock.release(); # python在線程死亡的時候,不會清理已存在在線程函數的互斥鎖,必須程序猿自己主動清理
print "%s被銷毀了!" % (self.thread_name);
# 初始化線程
thread1 = myThread("線程1");
thread2 = myThread("線程2");
thread3 = myThread("線程3");
thread4 = myThread("線程4");
# 開啟線程
thread1.start();
thread2.start();
thread3.start();
thread4.start();
# 等到線程1、2、3、4結束才進行以下的代碼(同步)
thread1.join();
thread2.join();
thread3.join();
thread4.join();
print "票都搶光了,大家都散了吧!";
print "=========得票統計=========";
print "線程1:%d張" % (ticket_for_thread1);
print "線程2:%d張" % (ticket_for_thread2);
print "線程3:%d張" % (ticket_for_thread3);
print "線程4:%d張" % (ticket_for_thread4);
1、從上面的代碼可以看出,在Python2.7中要使用線程必須使用threading而不是古老的thread模塊。
如果你像網上部分遺留依舊的文章一樣,在Python2.7中使用thread來實現線程,至少在Eclipse的Pydev中會報錯:sys.excepthook is missing,lost sys.stderr如下圖所示:
所以必須使用現時Python建議使用的threading。
2、與其它編程語言類似,聲明一個互斥鎖,與一系列的得票數。之後,與Java同樣地,Python實現線程的函數,是要重寫一個類。而類中使用全局變數,則與同為腳本語言的php一樣《【php】global的使用與php的全局變數》(點擊打開鏈接),要用global才能使用這個全局變數,而不是C/C++可以直接使用。
3、需要注意的,Python需要在線程跑完class myThread(threading.Thread)這個類的def run(self)方法之前,必須自己手動清理互斥鎖,它不會像其它編程語言那樣,說線程跑完def run(self)方法,會自然而然地清理該線程被創建的互斥鎖。如果沒有最後一句手動清理互斥鎖,則會造成死鎖。
4、最後與其它編程語言一樣了,利用線程的join方法可以等待這個線程跑完def run(self)方法中的所有代碼,才執行之後的代碼,實現同步。否則主函數中的代碼,相當於與父線程。主函數開啟的線程,相當於其子線程,互不影響的。

⑵ Python如何把循環次數可以讓玩家修改而不看到源代碼

1、打開Python輸入pipinstallPyInstaller。
2、進入需要打包的程序目錄,設置讓玩家修改而看不到循環次數的源代碼。
3、輸入pyinstaller和需要打包的程序名稱。
4、以上為Python如何把循環次數可以讓玩家修改而不看到源代碼的步驟。

⑶ 面試必備 - Python 垃圾回收機制

眾所周知,Python 是一門面向對象語言,在 Python 的世界一切皆對象。所以一切變數的本質都是對象的一個指針而已。

Python 運行過程中會不停的創建各種變數,而這些變數是需要存儲在內存中的,隨著程序的不斷運行,變數數量越來越多,所佔用的空間勢必越來越大,如果對變數所佔用的內存空間管理不當的話,那麼肯定會出現 out of memory。程序大概率會被異常終止。

因此,對於內存空間的有效合理管理變得尤為重要,那麼 Python 是怎麼解決這個問題的呢。其實很簡單,對不不可能再使用到的內存進行回收即可,像 C 語言中需要程序員手動釋放內存就是這個道理。但問題是如何確定哪些內存不再會被使用到呢?這就是我們今天要說的垃圾回收了。

目前垃圾回收比較通用的解決辦法有三種,引用計數,標記清除以及分代回收。

引用計數也是一種最直觀,最簡單的垃圾收集技術。在 Python 中,大多數對象的生命周期都是通過對象的引用計數來管理的。其原理非常簡單,我們為每個對象維護一個 ref 的欄位用來記錄對象被引用的次數,每當對象被創建或者被引用時將該對象的引用次數加一,當對象的引用被銷毀時該對象的引用次數減一,當對象的引用次數減到零時說明程序中已經沒有任何對象持有該對象的引用,換言之就是在以後的程序運行中不會再次使用到該對象了,那麼其所佔用的空間也就可以被釋放了了。

我們來看看下面的例子。

函數 print_memory_info 用來獲取程序佔用的內存空間大小,在 foo 函數中創建一個包含一百萬個整數的列表。從列印結果我們可以看出,創建完列表之後程序耗用的內存空間上升到了 55 MB。而當函數 foo 調用完畢之後內存消耗又恢復正常。

這是因為我們在函數 foo 中創建的 list 變數是局部變數,其作用域是當前函數內部,一旦函數執行完畢,局部變數的引用會被自動銷毀,即其引用次數會變為零,所佔用的內存空間也會被回收。

為了驗證我們的想法,我們對函數 foo 稍加改造。代碼如下:

稍加改造之後,即使 foo 函數調用結束其所消耗的內存也未被釋放。

主要是因為我們將函數 foo 內部產生的列表返回並在主程序中接收之後,這樣就會導致該列表的引用依然存在,該對象後續仍有可能被使用到,垃圾回收便不會回收該對象。

那麼,什麼時候對象的引用次數才會增加呢。下面四種情況都會導致對象引用次數加一。

同理,對象引用次數減一的情況也有四種。

引用計數看起來非常簡單,實現起來也不復雜,只需要維護一個欄位保存對象被引用的次數即可,那麼是不是就代表這種演算法沒有缺點了呢。實則不然,我們知道引用次數為零的對象所佔用的內存空間肯定是需要被回收的。那引用次數不為零的對象呢,是不是就一定不能回收呢?

我們來看看下面的例子,只是對函數 foo 進行了改造,其餘未做更改。

我們看到,在函數 foo 內部生成了兩個列表 list_a 和 list_b,然後將兩個列表分別添加到另外一個中。由結果可以看出,即使 foo 函數結束之後其所佔用的內存空間依然未被釋放。這是因為對於 list_a 和 list_b 來說雖然沒有被任何外部對象引用,但因為二者之間交叉引用,以至於每個對象的引用計數都不為零,這也就造成了其所佔用的空間永遠不會被回收的尷尬局面。這個缺點是致命的。

為了解決交叉引用的問題,Python 引入了標記清除演算法和分代回收演算法。

顯然,可以包含其他對象引用的容器對象都有可能產生交叉引用問題,而標記清除演算法就是為了解決交叉引用的問題的。

標記清除演算法是一種基於對象可達性分析的回收演算法,該演算法分為兩個步驟,分別是標記和清除。標記階段,將所有活動對象進行標記,清除階段將所有未進行標記的對象進行回收即可。那麼現在的為問題變為了 GC 是如何判定哪些是活動對象的?

事實上 GC 會從根結點出發,與根結點直接相連或者間接相連的對象我們將其標記為活動對象(該對象可達),之後進行回收階段,將未標記的對象(不可達對象)進行清除。前面所說的根結點可以是全局變數,也可以是調用棧。

標記清除演算法主要用來處理一些容器對象,雖說該方法完全可以做到不誤殺不遺漏,但 GC 時必須掃描整個堆內存,即使只有少量的非可達對象需要回收也需要掃描全部對象。這是一種巨大的性能浪費。

由於標記清除演算法需要掃描整個堆的所有對象導致其性能有所損耗,而且當可以回收的對象越少時性能損耗越高。因此 Python 引入了分代回收演算法,將系統中存活時間不同的對象劃分到不同的內存區域,共三代,分別是 0 代,1 代 和 2 代。新生成的對象是 0 代,經過一次垃圾回收之後,還存活的對象將會升級到 1 代,以此類推,2 代中的對象是存活最久的對象。

那麼什麼時候觸發進行垃圾回收演算法呢。事實上隨著程序的運行會不斷的創建新的對象,同時也會因為引用計數為零而銷毀大部分對象,Python 會保持對這些對象的跟蹤,由於交叉引用的存在,以及程序中使用了長時間存活的對象,這就造成了新生成的對象的數量會大於被回收的對象數量,一旦二者之間的差值達到某個閾值就會啟動垃圾回收機制,使用標記清除演算法將死亡對象進行清除,同時將存活對象移動到 1 代。 以此類推,當二者的差值再次達到閾值時又觸發垃圾回收機制,將存活對象移動到 2 代。

這樣通過對不同代的閾值做不同的設置,就可以做到在不同代使用不同的時間間隔進行垃圾回收,以追求性能最大。

事實上,所有的程序都有一個相識的現象,那就是大部分的對象生存周期都是相當短的,只有少量對象生命周期比較長,甚至會常駐內存,從程序開始運行持續到程序結束。而通過分代回收演算法,做到了針對不同的區域採取不同的回收頻率,節約了大量的計算從而提高 Python 的性能。

除了上面所說的差值達到一定閾值會觸發垃圾回收之外,我們還可以顯示的調用 gc.collect() 來觸發垃圾回收,最後當程序退出時也會進行垃圾回收。

本文介紹了 Python 的垃圾回收機制,垃圾回收是 Python 自帶的功能,並不需要程序員去手動管理內存。

其中引用計數法是最簡單直接的,但是需要維護一個欄位且針對交叉引用無能為力。

標記清除演算法主要是為了解決引用計數的交叉引用問題,該演算法的缺點就是需要掃描整個堆的所有對象,有點浪費性能。

而分代回收演算法的引入則完美解決了標記清除演算法需要掃描整個堆對象的性能浪費問題。該演算法也是建立在標記清除基礎之上的。

最後我們可以通過 gc.collect() 手動觸發 GC 的操作。

題外話,如果你看過 JVM 的垃圾回收演算法之後會發現 Python 的垃圾回收演算法與其是如出一轍的,事實再次證明,程序語言設計時是會相互參考的。

⑷ python 爬蟲(學了3天寫出的代碼)

import requests import parsel import threading,os import queue

class Thread(threading.Thread): def init (self,queue,path): threading.Thread. init (self) self.queue = queue self.path = path

def download_novel(url, path): res = get_response(url) selctor = parsel.Selector(res) title = selctor.css('.bookname > h1::text').get() print(title) content = ' '.join(selctor.css('#content::text').getall()) # 使用join方法改變內容; with open( path + title + ".txt","w",encoding='utf-8') as f: f.write(content) print(title,'保存成功!') f.close()

def get_response(url): # 獲得網站源碼; response = requests.get(url) response.encoding = 'utf-8' return response.text

if name == ' main ': # 函數入口 url = str(input('請輸入你要下載小說的url:')) response = get_response(url) sel = parsel.Selector(response) novelname = sel.css('#info > h1::text').get() urllist = sel.css('.box_con p dl dd a::attr(href)').getall() queue = queue.Queue() path = './{}/'.format(novelname)

閱讀全文

與銷毀一本圖書的代碼python相關的資料

熱點內容
unityandroid游戲開發教程 瀏覽:93
android去掉虛擬按鍵 瀏覽:872
內地激情戲多的電影 瀏覽:42
更新最快的電視劇電影網 瀏覽:263
劍三宏設置命令 瀏覽:245
3C語言編譯器 瀏覽:170
我的世界基岩版怎麼加入tis伺服器 瀏覽:390
php論壇模板 瀏覽:908
找個免費看電影的網站 瀏覽:372
程序員怎麼接手別人遺留的代碼 瀏覽:752
瞬變pdf 瀏覽:307
php開發倉庫管理系統 瀏覽:688
12米小孩自己看電影 瀏覽:676
喪屍電影全部 瀏覽:660
go編譯器選擇 瀏覽:448
天正門窗總表命令 瀏覽:257
pdf閱讀器編輯 瀏覽:514
sp古風訓誡細致 瀏覽:857
android廣播啟動伺服器 瀏覽:902
廣東程序員賣椅子 瀏覽:259