python程序運行原理_談談 Python 程序的運行原理

因為我的個人網站 restran.net 已經啟用,博客園的內容已經不再更新。請訪問我的個人網站獲取這篇文章的最新內容,談談 Python 程序的運行原理

這篇文章準確說是『Python 源碼剖析』的讀書筆記,整理完之后才發現很長,那就將就看吧。

1. 簡單的例子

先從一個簡單的例子說起,包含了兩個文件 foo.py 和 demo.py

[foo.py]

def add(a, b):

return a + b

[demo.py]

import foo

a = [1, 'python']

a = 'a string'

def func():

a = 1

b = 257

print(a + b)

print(a)

if __name__ == '__main__':

func()

foo.add(1, 2)

執行這個程序

python demo.py

輸出結果

a string

258

同時,該文件目錄多出一個 foo.pyc 文件

2. 背后的魔法

看完程序的執行結果,接下來開始一行行解釋代碼。

2.1 模塊

Python 將 .py 文件視為一個 module,這些 module 中,有一個主 module,也就是程序運行的入口。在這個例子中,主 module 是 demo.py。

2.2 編譯

執行 python demo.py 后,將會啟動 Python 的解釋器,然后將 demo.py 編譯成一個字節碼對象 PyCodeObject。

有的人可能會很好奇,編譯的結果不應是 pyc 文件嗎,就像 Java 的 class 文件,那為什么是一個對象呢,這里稍微解釋一下。

在 Python 的世界中,一切都是對象,函數也是對象,類型也是對象,類也是對象(類屬于自定義的類型,在 Python 2.2 之前,int, dict 這些內置類型與類是存在不同的,在之后才統一起來,全部繼承自 object),甚至連編譯出來的字節碼也是對象,.pyc 文件是字節碼對象(PyCodeObject)在硬盤上的表現形式。

在運行期間,編譯結果也就是 PyCodeObject 對象,只會存在于內存中,而當這個模塊的 Python 代碼執行完后,就會將編譯結果保存到了 pyc 文件中,這樣下次就不用編譯,直接加載到內存中。pyc 文件只是 PyCodeObject 對象在硬盤上的表現形式。

這個 PyCodeObject 對象包含了 Python 源代碼中的字符串,常量值,以及通過語法解析后編譯生成的字節碼指令。PyCodeObject 對象還會存儲這些字節碼指令與原始代碼行號的對應關系,這樣當出現異常時,就能指明位于哪一行的代碼。

2.3 pyc 文件

一個 pyc 文件包含了三部分信息:Python 的 magic number、pyc 文件創建的時間信息,以及 PyCodeObject 對象。

magic number 是 Python 定義的一個整數值。一般來說,不同版本的 Python 實現都會定義不同的 magic number,這個值是用來保證 Python 兼容性的。比如要限制由低版本編譯的 pyc 文件不能讓高版本的 Python 程序來執行,只需要檢查 magic number 不同就可以了。由于不同版本的 Python 定義的字節碼指令可能會不同,如果不做檢查,執行的時候就可能出錯。

下面所示的代碼可以來創建 pyc 文件,使用方法

python generate_pyc.py module_name

例如

python generate_pyc.py demo

[generate_pyc.pyc]

import imp

import sys

def generate_pyc(name):

fp, pathname, description = imp.find_module(name)

try:

imp.load_module(name, fp, pathname, description)

finally:

if fp:

fp.close()

if __name__ == '__main__':

generate_pyc(sys.argv[1])

2.4 字節碼指令

為什么 pyc 文件也稱作字節碼文件?因為這些文件存儲的都是一些二進制的字節數據,而不是能讓人直觀查看的文本數據。

Python 標準庫提供了用來生成代碼對應字節碼的工具 dis。dis 提供一個名為 dis 的方法,這個方法接收一個 code 對象,然后會輸出 code 對象里的字節碼指令信息。

s = open('demo.py').read()

co = compile(s, 'demo.py', 'exec')

import dis

dis.dis(co)

執行上面這段代碼可以輸出 demo.py 編譯后的字節碼指令

1 0 LOAD_CONST 0 (-1)

3 LOAD_CONST 1 (None)

6 IMPORT_NAME 0 (foo)

9 STORE_NAME 0 (foo)

3 12 LOAD_CONST 2 (1)

15 LOAD_CONST 3 (u'python')

18 BUILD_LIST 2

21 STORE_NAME 1 (a)

4 24 LOAD_CONST 4 (u'a string')

27 STORE_NAME 1 (a)

6 30 LOAD_CONST 5 ()

33 MAKE_FUNCTION 0

36 STORE_NAME 2 (func)

11 39 LOAD_NAME 1 (a)

42 PRINT_ITEM

43 PRINT_NEWLINE

13 44 LOAD_NAME 3 (__name__)

47 LOAD_CONST 6 (u'__main__')

50 COMPARE_OP 2 (==)

53 POP_JUMP_IF_FALSE 82

14 56 LOAD_NAME 2 (func)

59 CALL_FUNCTION 0

62 POP_TOP

15 63 LOAD_NAME 0 (foo)

66 LOAD_ATTR 4 (add)

69 LOAD_CONST 2 (1)

72 LOAD_CONST 7 (2)

75 CALL_FUNCTION 2

78 POP_TOP

79 JUMP_FORWARD 0 (to 82)

>> 82 LOAD_CONST 1 (None)

85 RETURN_VALUE

2.5 Python 虛擬機

demo.py 被編譯后,接下來的工作就交由 Python 虛擬機來執行字節碼指令了。Python 虛擬機會從編譯得到的 PyCodeObject 對象中依次讀入每一條字節碼指令,并在當前的上下文環境中執行這條字節碼指令。我們的程序就是通過這樣循環往復的過程才得以執行。

2.6 import 指令

demo.py 的第一行代碼是 import foo。import 指令用來載入一個模塊,另外一個載入模塊的方法是 from xx import yy。用 from 語句的好處是,可以只復制需要的符號變量到當前的命名空間中(關于命名空間將在后面介紹)。

前文提到,當已經存在 pyc 文件時,就可以直接載入而省去編譯過程。但是代碼文件的內容會更新,如何保證更新后能重新編譯而不入舊的 pyc 文件呢。答案就在 pyc 文件中存儲的創建時間信息。當執行 import 指令的時候,如果已存在 pyc 文件,Python 會檢查創建時間是否晚于代碼文件的修改時間,這樣就能判斷是否需要重新編譯,還是直接載入了。如果不存在 pyc 文件,就會先將 py 文件編譯。

2.7 絕對引入和相對引入

前文已經介紹了 import foo 這行代碼。這里隱含了一個問題,就是 foo 是什么,如何找到 foo。這就屬于 Python 的模塊引入規則,這里不展開介紹,可以參考 pep-0328。

2.8 賦值語句

接下來,執行到 a = [1, 'python'],這是一條賦值語句,定義了一個變量 a,它對應的值是 [1, 'python']。這里要解釋一下,變量是什么呢?

變量是一個存儲位置和一個關聯的符號名字,這個存儲位置包含了一些已知或未知的量或者信息。

變量實際上是一個字符串的符號,用來關聯一個存儲在內存中的對象。在 Python 中,會使用 dict(就是 Python 的 dict 對象)來存儲變量符號(字符串)與一個對象的映射。

那么賦值語句實際上就是用來建立這種關聯,在這個例子中是將符號 a 與一個列表對象 [1, 'python'] 建立映射。

緊接著的代碼執行了 a = 'a string',這條指令則將符號 a 與另外一個字符串對象 a string 建立了映射。今后對變量 a 的操作,將反應到字符串對象 a string 上。

2.9 def 指令

我們的 Python 代碼繼續往下運行,這里執行到一條 def func(),從字節碼指令中也可以看出端倪 MAKE_FUNCTION。沒錯這條指令是用來創建函數的。Python 是動態語言,def 實際上是執行一條指令,用來創建函數(class 則是創建類的指令),而不僅僅是個語法關鍵字。函數并不是事先創建好的,而是執行到的時候才創建的。

def func() 將會創建一個名稱為 func 的函數對象。實際上是先創建一個函數對象,然后將 func 這個名稱符號綁定到這個函數上。

Python 中是無法實現 C 和 Java 中的重載的,因為重載要求函數名要相同,而參數的類型或數量不同,但是 Python 是通過變量符號(如這里的 func)來關聯一個函數,當我們用 def 語句再次創建一個同名的函數時,這個變量名就綁定到新的函數對象上了。

2.10 動態類型

繼續看函數 func 里面的代碼,這時又有一條賦值語句 a = 1。變量 a 現在已經變成了第三種類型,它現在是一個整數了。那么 Python 是怎么實現動態類型的呢?答案就藏在具體存儲的對象上。變量 a 僅僅只是一個符號(實際上是一個字符串對象),類型信息是存儲在對象上的。在 Python 中,對象機制的核心是類型信息和引用計數(引用計數屬于垃圾回收的部分)。

用 type(a),可以輸出 a 的類型,這里是 int

b = 257 跳過,我們直接來看看 print(a + b),print 是輸出函數,這里略過。這里想要探究的是 a + b。

因為 a 和 b 并不存儲類型信息,因此當執行 a + b 的時候就必須先檢查類型,比如 1 + 2 和 "1" + "2" 的結果是不一樣的。

看到這里,我們就可以想象一下執行一句簡單的 a + b,Python 虛擬機需要做多少繁瑣的事情了。首先需要分別檢查 a 和 b 所對應對象的類型,還要匹配類型是否一致(1 + "2" 將會出現異常),然后根據對象的類型調用正確的 + 函數(例如數值的 + 或字符串的 +),而 CPU 對于上面這條語句只需要執行 ADD 指令(還需要先將變量 MOV 到寄存器)。

2.11 命名空間 (namespace)

在介紹上面的這些代碼時,還漏掉了一個關鍵的信息就是命名空間。在 Python 中,類、函數、module 都對應著一個獨立的命名空間。而一個獨立的命名空間會對應一個 PyCodeObject 對象,所以上面的 demo.py 文件編譯后會生成兩個 PyCodeObject,只是在 demo.py 這個 module 層的 PyCodeObject 中通過一個變量符號 func 嵌套了一個函數的 PyCodeObject。

命名空間的意義,就是用來確定一個變量符號到底對應什么對象。命名空間可以一個套一個地形成一條命名空間鏈,Python 虛擬機在執行的過程中,會有很大一部分時間消耗在從這條命名空間鏈中確定一個符號所對應的對象是什么。

在 Python中,命名空間是由一個 dict 對象實現的,它維護了(name,obj)這樣的關聯關系。

說到這里,再補充一下 import foo 這行代碼會在 demo.py 這個模塊的命名空間中,創建一個新的變量名 foo,foo 將綁定到一個 PyCodeObject 對象,也就是 foo.py 的編譯結果。

2.11.1 dir 函數

Python 的內置函數 dir 可以用來查看一個命名空間下的所有名字符號。一個用處是查看一個命名空間的所有屬性和方法(這里的命名空間就是指類、函數、module)。

比如,查看當前的命名空間,可以使用 dir(),查看 sys 模塊,可以使用 dir(sys)。

2.11.2 LEGB 規則

Python 使用 LEGB 的順序來查找一個符號對應的對象

locals -> enclosing function -> globals -> builtins

locals,當前所在命名空間(如函數、模塊),函數的參數也屬于命名空間內的變量

enclosing,外部嵌套函數的命名空間(閉包中常見)

def fun1(a):

def fun2():

# a 位于外部嵌套函數的命名空間

print(a)

globals,全局變量,函數定義所在模塊的命名空間

a = 1

def fun():

# 需要通過 global 指令來聲明全局變量

global a

# 修改全局變量,而不是創建一個新的 local 變量

a = 2

builtins,內置模塊的命名空間。Python 在啟動的時候會自動為我們載入很多內置的函數、類,比如 dict,list,type,print,這些都位于 __builtins__ 模塊中,可以使用 dir(__builtins__) 來查看。這也是為什么我們在沒有 import 任何模塊的情況下,就能使用這么多豐富的函數和功能了。

介紹完命名空間,就能理解 print(a) 這行代碼輸出的結果為什么是 a string 了。

2.12 內置屬性 __name__

現在到了解釋 if __name__ == '__main__' 這行代碼的時候了。當 Python 程序啟動后,Python 會自動為每個模塊設置一個屬性 __name__ 通常使用的是模塊的名字,也就是文件名,但唯一的例外是主模塊,主模塊將會被設置為 __main__。利用這一特性,就可以做一些特別的事。比如當該模塊以主模塊來運行的時候,可以運行測試用例。而當被其他模塊 import 時,則只是乖乖的,提供函數和功能就好。

2.13 函數調用

最后兩行是函數調用,這里略去不講。

3. 回顧

講到最后,還有些內容需要再回顧和補充一下。

3.1 pyc 文件

Python 只會對那些以后可能繼續被使用和載入的模塊才會生成 pyc 文件,Python 認為使用了 import 指令的模塊,屬于這種類型,因此會生成 pyc 文件。而對于只是臨時用一次的模塊,并不會生成 pyc 文件,Python 將主模塊當成了這種類型的文件。這就解釋了為什么 python demo.py 執行完后,只會生成一個 foo.pyc 文件。

如果要問 pyc 文件什么時候生成,答案就是在執行了 import 指令之后,from xx import yy 同樣屬于 import 指令。

3.2 小整數對象池

在 demo.py 這里例子中,所用的整數特意用了一個 257,這是為了介紹小整數對象池的。整數在程序中的使用非常廣泛,Python 為了優化速度,使用了小整數對象池,避免為整數頻繁申請和銷毀內存空間。

Python 對小整數的定義是 [-5, 257),這些整數對象是提前建立好的,不會被垃圾回收。在一個 Python 的程序中,所有位于這個范圍內的整數使用的都是同一個對象,從下面這個例子就可以看出。

>>> a = 1

>>> id(a)

40059744

>>> b = 1

>>> id(b)

40059744

>>> c = 257

>>> id(c)

41069072

>>> d = 257

>>> id(257)

41069096

id 函數可以用來查看一個對象的唯一標志,可以認為是內存地址

對于大整數,Python 使用的是一個大整數對象池。這句話的意思是:

每當創建一個大整數的時候,都會新建一個對象,但是這個對象不再使用的時候,并不會銷毀,后面再建立的對象會復用之前已經不再使用的對象的內存空間。(這里的不再使用指的是引用計數為0,可以被銷毀)

3.3 字符串對象緩沖池

如果仔細思考一下,一定會猜到字符串也采用了這種類似的技術,我們來看一下

>>> a = 'a'

>>> b = 'a'

>>> id(a)

14660456

>>> id(b)

14660456

沒錯,Python 的設計者為一個字節的字符對應的字符串對象 (PyStringObject) 也設計了這樣一個對象池。同時還有一個 intern 機制,可以將內容相同的字符串變量轉換成指向同一個字符串對象。

intern 機制的關鍵,就是在系統中有一個(key,value)映射關系的集合,集合的名稱叫做 interned。在這個集合中,記錄著被 intern 機制處理過的 PyStringObject 對象。不過 Python 始終會為字符串創建 PyStringObject 對象,即便在interned 中已經有一個與之對應的 PyStringObject 對象了,而 intern 機制是在字符串被創建后才起作用。

>>> a = 'a string'

>>> b = 'a string'

>>> a is b

False

>>> a = intern('a string') # 手動調用 intern 方法

>>> b = intern('a string')

>>> a is b

True

關于 intern 函數 可以參考官方文檔,更多擴展閱讀:

值得說明的是,數值類型和字符串類型在 Python 中都是不可變的,這意味著你無法修改這個對象的值,每次對變量的修改,實際上是創建一個新的對象。得益于這樣的設計,才能使用對象緩沖池這種優化。

Python 的實現上大量采用了這種內存對象池的技術,不僅僅對于這些特定的對象,還有專門的內存池用于小對象,使用這種技術可以避免頻繁地申請和釋放內存空間,目的就是讓 Python 能稍微更快一點。更多內容可以參考這里。

如果想了解更快的 Python,可以看看 PyPy

3.4 import 指令

前文提到 import 指令是用來載入 module 的,如果需要,也會順道做編譯的事。但 import 指令,還會做一件重要的事情就是把 import 的那個 module 的代碼執行一遍,這件事情很重要。Python 是解釋執行的,連函數都是執行的時候才創建的。如果不把那個 module 的代碼執行一遍,那么 module 里面的函數都沒法創建,更別提去調用這些函數了。

執行代碼的另外一個重要作用,就是在這個 module 的命名空間中,創建模塊內定義的函數和各種對象的符號名稱(也就是變量名),并將其綁定到對象上,這樣其他 module 才能通過變量名來引用這些對象。

Python 虛擬機還會將已經 import 過的 module 緩存起來,放到一個全局 module 集合 sys.modules 中。這樣做有一個好處,即如果程序的在另一個地方再次 import 這個模塊,Python 虛擬機只需要將全局 module 集合中緩存的那個 module 對象返回即可。

你現在一定想到了 sys.modules 是一個 dict 對象,可以通過 type(sys.modules) 來驗證

3.5 多線程

demo.py 這個例子并沒有用到多線程,但還是有必要提一下。

在提到多線程的時候,往往要關注線程如何同步,如何訪問共享資源。Python 是通過一個全局解釋器鎖 GIL(Global Interpreter Lock)來實現線程同步的。當 Python 程序只有單線程時,并不會啟用 GIL,而當用戶創建了一個 thread 時,表示要使用多線程,Python 解釋器就會自動激活 GIL,并創建所需要的上下文環境和數據結構。

Python 字節碼解釋器的工作原理是按照指令的順序一條一條地順序執行,Python 內部維護著一個數值,這個數值就是 Python 內部的時鐘,如果這個數值為 N,則意味著 Python 在執行了 N 條指令以后應該立即啟動線程調度機制,可以通過下面的代碼獲取這個數值。

import sys

sys.getcheckinterval() # 100

線程調度機制將會為線程分配 GIL,獲取到 GIL 的線程就能開始執行,而其他線程則必須等待。由于 GIL 的存在,Python 的多線程性能十分低下,無法發揮多核 CPU 的優勢,性能甚至不如單線程。因此如果你想用到多核 CPU,一個建議是使用多進程。

3.6 垃圾回收

在講到垃圾回收的時候,通常會使用引用計數的模型,這是一種最直觀,最簡單的垃圾收集技術。Python 同樣也使用了引用計數,但是引用計數存在這些缺點:

頻繁更新引用計數會降低運行效率

引用計數無法解決循環引用問題

Python 在引用計數機制的基礎上,使用了主流垃圾收集技術中的標記——清除和分代收集兩種技術。

關于垃圾回收,可以參考

4. 參考文獻

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/538810.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/538810.shtml
英文地址,請注明出處:http://en.pswp.cn/news/538810.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

3.JDK和JRE和JVM的區別

JDK --Java Development Kit --java 開發工具包 JRE --Java Runtime Environment --java運行時環境 JVM --Java Virtual Machine --java虛擬機 ------------- 更多的Java,Angular,Android,大數據,J2EE,Python…

緩存cache

由于Django是動態網站,所有每次請求均會去數據進行相應的操作,當程序訪問量大時,耗時必然會更加明顯,最簡單解決方式是使用:緩存,緩存將一個某個views的返回值保存至內存或者memcache中,5分鐘內…

微信小程序 等待幾秒、_微信小程序—setTimeout定時器的坑

背景實驗室需要將項目的app搬到微信的小程序上,終于知道為什么程序員是手藝人了,只要有需求,就要想方設法去填充這種需求,去年是小程序的元年了可以說,去年冬天一個叫跳一跳的小程序游戲出現在我的微信中,當…

linux中斷處理模式,Linux在保護模式下的中斷處理分析.pdf

Linux在保護模式下的中斷處理分析.pdfLinux 在保護模式下的中斷處理分析劉萬里 楊 斌(西南交通大學計算機與通信工程學院,成都 610031)E-mail:awan摘 要 該文以 80x86 保護模式下的中斷處理方法為基礎,針對 Linux 在實時嵌入式系統中的具體應…

python3.7是什么_Python 3.7 有什么新變化

idlelib 與 IDLE 多個對自動補全的修正。 (由 Louie Lu 在 bpo-15786 中貢獻。) Module Browser (在 File 菜單中,之前稱為 Class Browser) 現在會在最高層級函數和類之外顯示嵌套的函數和類。 (由 Guilherme Polo, Cheryl Sabell…

4.JVM簡述

JVM是一種規范。 就是一個虛擬的用于執行bytecodes字節碼的計算機 可以用軟件來實現,如IBM,SUN,BEA等按照這個規范實現,可以實現比SUN公司更好的JVM,我們自己也可以實現一個。 可以使用硬件來實現,如sun與intel公司研發java的芯…

python ssh shell交互_使用Paramiko在Python上用ssh實現交互式shell?

我想編寫一個程序(在Windows 7上的Python 3.x中),它通過ssh在遠程shell上執行多個命令.在查看paramikos的exec_command()函數之后,我意識到它不適合我的用例(因為在執行命令后通道被關閉),因為命令依賴于環境變量(由先前的命令設置)并且不能連接到一個exec_command()調用,因為它…

linux7如何進入緊急模式,CentOS7開機進入緊急模式EmergencyMode的解決辦法

iOS Runtime學習筆記Associated Objects: interface NSObject (AssociatedObject) property (nonatomic, strong) id associat ...Vim,極簡使用教程,讓你瞬間脫離鍵鼠切換的痛苦注:看大家對Vim仇恨極大,其實它只是一種文本操作方式,可以減少鍵鼠的切換,從…

用pycharm寫python_如何利用pyCharm編寫和運行python文件

在安裝python環境后,通常可以利用IDE pyCharm來編譯我們的python文件。創建一個python文件夾,用pyCharm打開文件夾,在文件夾中新建一個python文件demo.py 也許你知道用cmd中的python指令 python demo.py去運行這個文件,但是如何在…

5.JDK環境配置

下載 進入Oracle官網下載,點擊進入 安裝 一路下一步。記住安裝到哪里了。 配置環境變量 JAVA_HOME 剛才的java安裝目錄 PATH %JAVA_HOME%\bin PATH里配置多個用英文的分號; 分隔。 *classpath,jdk5.0以上可以不用配置了 測試 windows下&#xf…

GBK 編碼

GBK編碼范圍:8140-FEFE,漢字編碼范圍見第二節:碼位分配及順序。 GBK編碼,是對GB2312編碼的擴展,因此完全兼容GB2312-80標準。GBK編碼依然采用雙字節編碼方案,其編碼范圍:8140&#x…

less webpack 熱更新_webpack---less+熱更新 使用

最近嘗試用less寫界面,webpack進行打包,然后發現每次修改less時都需要重新執行webpack打包一下,于是就想到了webpack熱更新這個功能。一、使用lessless是一門css預處理語言,它是拓展了css,增加了變量,Mixin等等。使用l…

6.第一個程序Hello World

新建文件夾 在C盤新建個文件夾 mycode。注意不要用中文。 新建java文件 1、顯示隱藏文件名。 2、右鍵新建文本文件 3、重命名為 Welcome.java。(首字母必須大寫。如果不顯示隱藏文件名,會是Welcome.java.txt不是java文件) 4、編寫代碼 p…

pythonstdin_python 筆試輸入:sys.stdin.readline和input

①:輸入一行數據并輸 出兩種方法 # 輸入一行數據并輸出 import sys # 方法一: str1 input() print(input 輸入:,str1,len,len(str1)) print(循環遍歷輸入得到輸入的每個字符的ascii碼如下:) for i in str1: print(ord(i)) # 方法二&#xff…

c語言字符串二維數組的動態分配應,C語言中動態分配二維數組復習過程.doc

C語言中動態分配二維數組復習過程.docC語言中動態分配二維數組在C中動態分配內存的,對于單個變量,字符串,一維數組等,都是很容易的。C中動態分配二維數組的方法,很少有C語言書中描述,我查找了有的C語言書中…

7.Java常用開發工具

文本編輯器 任意選一款 UltraEdit EditPlus notepad 集成開發環境 IDE:Integrated Development Environment --JBulider (基本淘汰) --Eclipse 普遍使用。https://www.eclipse.org/downloads/ --NetBeans ------------- 更多的Java&a…

python京東商品_Python爬取京東的商品分類與鏈接

前言本文主要的知識點是使用Python的BeautifulSoup進行多層的遍歷。如圖所示。只是一個簡單的哈,不是爬取里面的隱藏的東西。示例代碼from bs4 import BeautifulSoup as bsimport requestsheaders {"host": "www.jd.com","User-Agent&quo…

python加入中小學課程_【python即將進入中學課堂,編程從小抓起,竟然在這幾點上應驗了】- 環球網校...

【摘要】我們都知道,不論是我們使用的app,還是各種各樣的游戲,小程序,都離不開編程軟件的貢獻,其中python占據了半壁江山,隨著大家對python的重視,python即將進入中學課堂,這究竟是好…

用動態規劃算法求解最少硬幣問題 c語言,動態規劃算法求解硬幣找零問題

該樓層疑似違規已被系統折疊 隱藏此樓查看此樓看著這代碼怎么這么熟悉。package 動態規劃找零;import java.util.Scanner;public class Main {public static void main(String[] args) {Scanner scan new Scanner(System.in);int change;change scan.nextInt();int[] coins …

git命令行使用

* git branch : 查看本地分支 * git branch -r : 查看遠程分支 * git branch -a : 查看全部分支 * git branch name : 本地新建分支 * git checkout name : 切換到本地 name 分支 * git pull origin name : 從遠程 name 拉取代碼 * git merge name : 合并name * git diff : 查看…