本文面相有 一定編程基礎 的朋友學習,所以略過了 環境安裝、IDE 搭建 等一系列簡單繁瑣的事情。
一、Python 簡介
Python 英文原意為 “蟒蛇”,直到 1989
年荷蘭人 Guido van Rossum (簡稱 Guido)發明了一種 面向對象 的 解釋型 編程語言,並將其命名為 Python,才賦予了它表示一門編程語言的含義。
說到 Python 的誕生,極具戲劇色彩,據 Guido 的自述記載,Python 語言是他在聖誕節期間為了打發時間開發出來的,之所以會選擇 Python 作為該編程語言的名字,是因為他是一個叫 Monty Python 戲劇團體的忠實粉絲。
解釋型 vs 編譯型
作為電子元器件的 計算機,實際上 只能 識別某些 特定的二進制指令 (特殊的 01
組合),但由於 人類閱讀 這些 指令 非常難以閱讀,加上使用這些指令編寫程序的 耗時 太過於 冗長,所以,人們在此基礎上再次制定了一套規范,采用特定的 人類可閱讀 的代碼編寫,待到要執行時再 翻譯 回特定二進制指令,這樣就幫助了人們更加輕松地理解和閱讀程序邏輯了。
這也就是所謂現在的 "高級編程語言" 了。
上述 翻譯 這個過程 (其實相當復雜,涉及語法分析、語義分析、性能優化等等..) 其實也是由一個特定程序來執行的,那 什么時候將源代碼翻譯成二進制指令呢?,不同的編程語言有不同的規定:
- 編譯型語言:必須 提前 將所有源代碼 一次性 轉換成二進制指令,生成一個可執行文件 (例如 Windows 下的
.exe
) 比如:C 語言、C++、Golang、匯編等。使用的轉換工具我們稱為 編譯器。 - 解釋型語言:允許程序 一邊執行一邊轉換,並且不會生成可執行程序,比如 Python、JavaScript、PHP 等。使用的轉換工具我們稱為 解釋器。
Java 和 C# 是一種比較奇葩的存在,它們是 半編譯半解釋型 的語言,源碼需要先轉換成一種中間文件 (字節碼文件),然后再把中間文件拿到 虛擬機 中執行。Java 引領了這種風潮,它的初衷是在跨平台的同時兼顧執行效率。
上圖 就展示了兩種不同類型語言的執行流程的不同,兩種方式對比起來總結如下:
- 編譯型語言一般不能跨平台:對於不同的 CPU 來說,它們的指令集是有差異的,這就導致了 可執行文件 (翻譯后的指令) 不能跨平台,另外不同的系統之間的命令也會存在差異,例如 Linux 中睡眠是
sleep()
參數是毫秒,而 Windows 中是Sleep()
(首字母大寫) 參數是秒,這就導致了 源代碼也不能跨平台; - 解釋型語言可跨平台:這一切都歸功於 解釋器,它本身就是一個可執行文件,官方只要針對不同的平台開發不同的解釋器,那么解釋器就能夠根據相同的語法解析出同樣功能的指令;
- 編譯型一般比解釋型效率高:由於解釋型是采取一邊執行一邊翻譯的做法,所以會慢上一些,再加上我們強大的 編譯器 會幫我們做許多代碼優化的工作。
關於 Python
Python 屬於典型的解釋型語言,所以運行 Python 程序需要解釋器的支持,只要你在不同的平台安裝了不同的解釋器,你的代碼就可以隨時運行,不用擔心任何兼容性問題,真正的 “一次編寫,到處運行”。
Python 幾乎支持所有常見的平台,比如 Linux、Windows、Mac OS、Android、FreeBSD、Solaris、PocketPC 等,你所寫的 Python 代碼無需修改就能在這些平台上正確運行。也就是說,Python 的 可移植性 是很強的。
面向對象 vs 面向過程
面向對象 和 面向過程 是我們使用計算機編程解決問題的兩種不同方式的方案。
面向過程 可以說是一種 基於事件 or 過程 來描述的編碼方式,譬如「把大象放進冰箱」就可以描述成那經典的三個步驟,「把牛放進冰箱」又是另一個相似的經典三步,只是這樣單獨的事件 or 過程多了之后,隨着項目復雜度的增加,項目會變得非常難以維護。
軟件危機最典型的例子莫過於 IBM 的
System/360
的操作系統開發。佛瑞德·布魯克斯(Frederick P. Brooks, Jr.)作為項目主管,率領2000
多個程序員夜以繼日的工作,共計花費了5000
人一年的工作量,寫出將近100
萬行的源碼,總共投入5
億美元,是美國的 “曼哈頓” 原子彈計划投入的1/4
。盡管投入如此巨大,但項目進度卻一再延遲,軟件質量也得不到保障。布魯克斯后來基於這個項目經驗而總結的《人月神話》一書,成了史上最暢銷的軟件工程書籍。
盡管 結構化的程序設計 (將一個大問題逐步划分成一個一個的小問題) 能夠幫助我們解決一部分問題,但 面向過程 仍然有一些不符合人類慣有的思考方式,譬如說:我今天想去存錢,我不會說「請拿走我的銀行卡和錢,然后在我卡上充值上相應的數目,最后把銀行卡還給我謝謝」,而我只會說「存錢,謝謝」,因為人大部分時間都是基於 對象 (或者可以說角色) 來思考的。
對於 面向過程 最好的總結可能是:「程序 = 算法 + 數據結構」,而對於 面向對象 來說則可以更改為:「程序 = 對象 + 交互」。
Why Python?
上面的 漫畫 很好地說明了 Python 快速構建工具的能力,這也是 Why Python
的一大理由。下面根據慣例列舉一些讓我們足以選擇 Python 的原因。
初學者友善 | 容易明白且功能強大
Python 的設計足夠簡單和易於使用,這樣使得初學者能夠從中不斷得獲取到樂趣以繼續 Python 之旅。
另外作為一種非常高級的語言,Python 讀起來像英語,這減輕了編碼初學者的許多語法學習壓力。Python 為您處理了很多復雜性,因此它非常適合初學者,因為它使初學者可以專注於學習編程概念,而不必擔心過多的細節。
Python 還一度被爆納入高考,收編到小學課本。
非常靈活
作為一種 動態類型 的語言,Python 確實非常靈活。這意味着沒有關於如何構建特征的硬性規則,並且使用不同的方法來解決問題將具有更大的靈活性 (盡管 Python 哲學鼓勵使用明顯的方法來解決問題)。此外,Python 也更寬容錯誤,因此您仍然可以編譯並運行程序,直到遇到問題為止。
越來越火爆
Python 在誕生之初,因為其功能不好,運轉功率低,不支持多核,根本沒有並發性可言,在計算功能不那么好的年代,一直沒有火爆起來,甚至很多人根本不知道有這門語言。
隨着時代的發展,物理硬件功能不斷提高,而軟件的復雜性也不斷增大,開發效率越來越被企業重視。因此就有了不一樣的聲音,在軟件開發的初始階段,性能並沒有開發效率重要,沒必然為了節省不到 1ms
的時間卻讓開發量增加好幾倍,這樣划不過來。也就是開發效率比機器效率更為重要,那么 Python 就逐漸得到越來越多開發者的親睞了。
在 12-14
年,雲計算升溫,大量創業公司和互聯網巨頭擠進雲計算領域,而最著名的雲核算開源渠道 OpenStack 就是基於 Python 開發的。
隨后幾年的備受關注的人工智能,機器學習首選開發語言也是 Python。
至此,Python 已經成為互聯網開發的焦點。在 「Top 10 的編程語言走勢圖」 可以看到,Python 已經躍居第三位,而且在 2017
年還成為了最受歡迎的語言。
工作機會 | 薪資待遇高
- 來自 gooroo.io 的薪資信息:
在天使榜上,Python 是需求第二高的技能,也是提供最高平均薪水的技能。
隨着大數據的興起,Python 開發人員需要作為數據科學家,尤其是因為 Python 可以輕松集成到 Web 應用程序中以執行需要機器學習的任務。
快速體驗 | No Hello World !
Hello World
似乎是學習編程繞不過去的東西,但使用 Python,我們來換點兒別的,Emmm.. 比如,一個 查詢天氣 的小程序 (效果如下圖):
源碼 & 解釋
http://wthrcdn.etouch.cn/weather_mini?city=xxx
這個網址可以返回任意城市昨日以及 5 天內的天氣預報,包括氣溫、指數、空氣質量、風力等,你可以用瀏覽器試着訪問一下,你會得到一個 weather_mini
的文件,里面就包含了我們想要的一些數據。
不過這里由於我們發起了網絡請求用到了第三方庫 requests
,所以在運行之前還需要使用 pip install requests
命令把該庫下載到 Python 的安裝目錄下。
# -*- coding: utf-8 -*-
import requests
while True:
city = input('請輸入城市,回車退出:\n')
if not city:
break
req = requests.get('http://wthrcdn.etouch.cn/weather_mini?city=%s' % city)
print(req.text)
運行你的代碼
你可以在 當前文件夾 下執行命令: python hello_python.py
,或是使用 python <源文件路徑>
運行也行,例如,如果我是 Windows 用戶並且將上述源文件保存在了 D
盤下面,那就可以執行 python D:\hello_python.py
,然后你就可以看到上面的效果了。當然如果使用 IDE 將更加方便。
Python vs Java
引入一張比較著名的圖吧,可以很明顯地感受到 Python 在寫法上要簡潔一些吧:
二、Python 基本語法簡介
Python 與其他語言最大的區別就是,Python 的代碼塊不使用大括號 {}
來控制類,函數以及其他邏輯判斷。Python 最具特色的就是用 縮進 來寫模塊。
2.0 注釋
# 第一個注釋
# 第二個注釋
'''
第三注釋
第四注釋
'''
"""
第五注釋
第六注釋
"""
print("Hello, Python!")
2.1 數據類型
Python 中的變量賦值不需要類型聲明。Python 有五個標准的數據類型:
- Numbers(數字):Python3 中有四種數字類型 (沒有 Python2 中的 Long),分別是
int
長整型、bool
布爾、float
浮點數、complex
復數 (1 + 2j); - String(字符串):Python 中字符串不能改變,並且沒有單獨的字符類型,一個字符就是長度為 1 的字符串;
- Tuple(元組):類似於 List,但不能二次賦值,相當於只讀列表。eg:
('test1', 'test2')
- List(列表):類似 Java 中的 Array 類型。eg:
[1, 2, ,3]
- Dictionary(字典):類似於 Java 的 Map 類型。eg:
{a: 1, b: 2}
set
集合也屬於數據結構,它是一個 無序 且 不重復 的元素序列。可以使用大括號{ }
或者set()
函數創建集合,注意:創建一個空集合必須用set()
而不是{ }
,因為{ }
是用來創建一個空字典。
str = 'Hello World!'
print str[2:5] # 輸出字符串中第三個至第五個之間的字符串
list = [ 'runoob', 786 , 2.23, 'john', 70.2 ]
print list[1:3] # 輸出第二個至第三個元素
tuple = ( 'runoob', 786 , 2.23, 'john', 70.2 )
print tuple[1:3] # 輸出第二個至第三個的元素
tinydict = {'name': 'john','code':6734, 'dept': 'sales'}
print tinydict['name'] # 輸出鍵為 2 的值
2.2 條件語句
# 當判斷條件為 1 個值時
flag = False
name = 'luren'
if name == 'python': # 判斷變量否為'python'
flag = True # 條件成立時設置標志為真
print 'welcome boss' # 並輸出歡迎信息
else:
print name # 條件不成立時輸出變量名稱
# 當判斷條件為多個值時
num = 5
if num == 3: # 判斷num的值
print 'boss'
elif num == 2:
print 'user'
else:
print 'roadman' # 條件均不成立時輸出
2.3 循環
while 循環
在 Python 中沒有 do..while
的循環
count = 0
while count < 5:
print (count, " 小於 5")
count = count + 1
else:
print (count, " 大於或等於 5")
for..in 循環
for..in
適用於 list/ dict/ set
數據類型,如果需要遍歷數字序列,我們也可以借助 range(min, max, step)
函數來生成數列。
sites = ["Baidu", "Google","Runoob","Taobao"]
for site in sites:
if site == "Runoob":
print("菜鳥教程!")
break
print("循環數據 " + site)
else:
print("沒有循環數據!")
print("完成循環!")
# 輸出 0/ 3/ 6/ 9
for i in range(0, 10, 3) :
print(i)
# 替換成 range(5) 則輸出 0/ 1/ 2/ 3/ 4
# 替換成 range(5,9) 則輸出 5/ 6/ 7/ 8
2.4 函數
函數基本定義和使用
# 計算面積函數
def area(width, height):
return width * height
def print_welcome(name):
print("Welcome", name)
print_welcome("Runoob")
w = 4
h = 5
print("width =", w, " height =", h, " area =", area(w, h))
參數傳遞
在 Python 中,類型屬於對象,變量是沒有類型的,例如 name = "wmyskxz"
,則 "wmyskxz"
是 String 類型,而變量 name
僅僅是一個對象的引用。
Python 中一切都是對象,嚴格意義我們 不能說值傳遞還是引用傳遞,我們應該說傳 不可變對象 (string、tuples、number 不可變) 和 傳可變對象 (list、dict 可變)。
############################################
# 傳遞不可變對象
def ChangeInt(a):
a = 10
b = 2
ChangeInt(b)
print b # 結果是 2
############################################
# 傳遞可變對象
def changeme( mylist ):
"修改傳入的列表"
mylist.append([1,2,3,4])
print ("函數內取值: ", mylist) # [10, 20, 30, [1, 2, 3, 4]]
return
# 調用changeme函數
mylist = [10,20,30]
changeme( mylist )
print ("函數外取值: ", mylist) # [10, 20, 30, [1, 2, 3, 4]]
# 函數內外值一致,因為都同屬於同一個引用
2.5 class 類
基本定義
class people:
#定義基本屬性
name = ''
age = 0
#定義私有屬性,私有屬性在類外部無法直接進行訪問
__weight = 0
#定義構造方法
def __init__(self,n,a,w):
self.name = n
self.age = a
self.__weight = w
def speak(self):
print("%s 說: 我 %d 歲。" %(self.name,self.age))
# 實例化類
p = people('wmyskxz', 24, 120)
p.speak() # wmyskxz 說: 我 10 歲。
繼承 & 多繼承 & 方法重寫
# 單繼承
class DerivedClassName(BaseClassName1):
<statement-1>
.
<statement-N>
# 多繼承
class DerivedClassName(Base1, Base2, Base3):
<statement-1>
.
.
.
<statement-N>
# 方法重寫演示
class Parent: # 定義父類
def myMethod(self):
print ('調用父類方法')
class Child(Parent): # 定義子類
def myMethod(self):
print ('調用子類方法')
c = Child() # 子類實例
c.myMethod() # 子類調用重寫方法
super(Child,c).myMethod() #用子類對象調用父類已被覆蓋的方法
# 先輸出 "調用子類方法" 再輸出 "調用父類方法"
2.6 module 模塊
一個 .py
文件就是一個模塊,想要使用其他 .py
文件中的方法就需要引入進來。
import [module]
# 導入整個random模塊,可以是內置/當前路徑
import random
# 使用 `random` 模塊下的 `randint` 方法
print(random.randint(0, 5))
from [module] import [name1, name2, ...]
# 從 `random` 模塊里導入其中一個方法 `randint`
from random import randint
# 不一樣的是,使用 `randint` 的就不需要先寫 `random` 了
print(randint(0, 5))
import [module] as [new_name]
# 但這個名字可能跟其他地方有沖突,因此改名成 `rd`
import random as rd
# 使用 `rd` 這個名稱取代原本的 `random`
print(rd.randint(0, 5))
from [module] import *
不推薦,容易造成名稱沖突,降低可讀性和可維護性。
# Import 所有 `random` module 底下的東西
from random import *
# 使用 `randint` 的時候也不需要先寫 `random`
print(randint(0, 5))
module 搜索路徑
當你導入一個模塊,Python 解析器對模塊位置的搜索順序是:
- 當前目錄
- 如果不在當前目錄,Python 則搜索在 shell 變量
PYTHONPATH
下的每個目錄。 - 如果都找不到,Python 會察看默認路徑。UNIX 下,默認路徑一般為
/usr/local/lib/python/
。
2.7 package 包
把兩個 module 放在一個新的目錄 sample_package
,再新增 _init__.py
(可以是空,但不能沒有),宣稱自己是一個 package :
sample_package
|-- __init__.py
|-- 1.py
|-- 2.py
# package_runoob 同級目錄下創建 test.py 來調用 package_runoob 包
# 導入包
from package_runoob.runoob1 import runoob1
from package_runoob.runoob2 import runoob2
runoob1()
runoob2()
單個py文件就是一個 module,而當多個
.py
文件 +__init__.py
文件時,就等於 package。
三、基本代碼規范
代碼規范 再怎么強調也不為過:
3.1 命名規范
模塊
- 模塊盡量使用 小寫命名,首字母保持小寫,盡量不要用下划線(除非多個單詞,且數量不多的情況)
# 正確的模塊名
import decoder
import html_parser
# 不推薦的模塊名
import Decoder
類名
- 類名使用 駝峰 (CamelCase)命名風格,首字母大寫,私有類可用一個下划線開頭
class Farm():
pass
class AnimalFarm(Farm):
pass
class _PrivateFarm(Farm):
pass
函數
- 函數名 一律小寫,如有多個單詞,用下划線隔開
def run():
pass
def run_with_env():
pass
- 私有函數在函數前加一個下划線
_
class Person():
def _private_func():
pass
變量名
- 變量名盡量 小寫, 如有多個單詞,用下划線隔開
if __name__ == '__main__':
count = 0
school_name = ''
- 常量使用以下划線分隔的 大寫 命名
MAX_CLIENT = 100
MAX_CONNECTION = 1000
CONNECTION_TIMEOUT = 600
3.2 基本編碼規范
縮進
- 統一使用 4 個空格進行縮進
行寬
每行代碼盡量不超過 80
個字符(在特殊情況下可以略微超過 80
,但最長不得超過 120
)
理由:
- 這在查看
side-by-side
的diff
時很有幫助 - 方便在控制台下查看代碼
- 太長可能是設計有缺陷
引號
簡單說,自然語言使用雙引號,機器標示使用單引號,因此 代碼里 多數應該使用 單引號
- 自然語言 使用雙引號
"..."
,例如錯誤信息;很多情況還是 unicode,使用u"你好世界" - 機器標識 使用單引號 '
...'
,例如 dict 里的 key - 正則表達式 使用原生的雙引號
r"..."
- 文檔字符串 (docstring) 使用三個雙引號
"""......"""
import 語句
- import 語句應該分行書寫
# 正確的寫法
import os
import sys
# 不推薦的寫法
import sys,os
# 正確的寫法
from subprocess import Popen, PIPE
- import 語句應該使用 absolute import
# 正確的寫法
from foo.bar import Bar
# 不推薦的寫法
from ..bar import Bar
- import 語句應該放在文件頭部,置於模塊說明及
DocString
之后,於全局變量之前; - import 語句應該按照順序排列,每組之間用一個空行分隔
import os
import sys
import msgpack
import zmq
import foo
- 導入其他模塊的類定義時,可以使用相對導入
from myclass import MyClass
- 如果發生命名沖突,則可使用命名空間
import bar
import foo.bar
bar.Bar()
foo.bar.Bar()
DocString
DocString 的規范中最其本的兩點:
- 所有的公共模塊、函數、類、方法,都應該寫 DocString 。私有方法不一定需要,但應該在
def
后提供一個塊注釋來說明。 - DocString 的結束"""應該獨占一行,除非此 DocString 只有一行。
"""Return a foobar
Optional plotz says to frobnicate the bizbaz first.
"""
"""Oneline docstring"""
3.3 注釋規范
建議
- 在代碼的 關鍵部分(或比較復雜的地方), 能寫注釋的要盡量寫注釋
- 比較重要的注釋段, 使用多個等號隔開, 可以更加醒目, 突出 重要性
app = create_app(name, options)
# =====================================
# 請勿在此處添加 get post等app路由行為 !!!
# =====================================
if __name__ == '__main__':
app.run()
文檔注釋(DocString)
- 文檔注釋以 """ 開頭和結尾, 首行不換行, 如有多行, 末行必需換行, 以下是Google的docstring風格示例
# -*- coding: utf-8 -*-
"""Example docstrings.
This module demonstrates documentation as specified by the `Google Python
Style Guide`_. Docstrings may extend over multiple lines. Sections are created
with a section header and a colon followed by a block of indented text.
Example:
Examples can be given using either the ``Example`` or ``Examples``
sections. Sections support any reStructuredText formatting, including
literal blocks::
$ python example_google.py
Section breaks are created by resuming unindented text. Section breaks
are also implicitly created anytime a new section starts.
"""
- 不要在文檔注釋復制函數定義原型, 而是具體描述其具體內容, 解釋具體參數和返回值等
# 不推薦的寫法(不要寫函數原型等廢話)
def function(a, b):
"""function(a, b) -> list"""
... ...
# 正確的寫法
def function(a, b):
"""計算並返回a到b范圍內數據的平均值"""
... ...
- 對函數參數、返回值等的說明采用
numpy
標准, 如下所示
def func(arg1, arg2):
"""在這里寫函數的一句話總結(如: 計算平均值).
這里是具體描述.
參數
----------
arg1 : int
arg1的具體描述
arg2 : int
arg2的具體描述
返回值
-------
int
返回值的具體描述
參看
--------
otherfunc : 其它關聯函數等...
示例
--------
示例使用doctest格式, 在`>>>`后的代碼可以被文檔測試工具作為測試用例自動運行
>>> a=[1,2,3]
>>> print [x + 3 for x in a]
[4, 5, 6]
"""
更多細致詳細的規范可以參考:
- Google 開源項目指南 - https://zh-google-styleguide.readthedocs.io/en/latest/google-python-styleguide/contents/
- 官方 PEP 8 代碼規范 - https://www.python.org/dev/peps/pep-0008/
推薦閱讀
- Redis(9)——史上最強【集群】入門實踐教程 - https://juejin.im/post/5e7429a16fb9a07ccc460fe7
- React入門學習 - https://juejin.im/post/5da5e9e8e51d4525292d2ed7
參考資料
- WhyStudyPython.md | TwoWater - https://github.com/TwoWater/Python/blob/master/Article/PythonBasis/python0/WhyStudyPython.md
- C 語言中文網 | Python 系列教程 - http://c.biancheng.net/python/
- Crossin的編程教室 - https://python666.cn/
- 計算機和編程語言的發展歷史 - https://blog.csdn.net/abc6368765/article/details/83990756
- 面向對象葵花寶典 - http://www.kancloud.cn:8080/yunhua_lee/oobaodian/110879
- RUNOOB | Python3 系列教程 - ttps://www.runoob.com/python3
- Python 基礎語法 | springleo'sblog - https://lq782655835.github.io/blogs/tools/python-grammar.html#_1-%E6%95%B0%E6%8D%AE%E7%B1%BB%E5%9E%8B
- 本文已收錄至我的 Github 程序員成長系列 【More Than Java】,學習,不止 Code,歡迎 star:https://github.com/wmyskxz/MoreThanJava
- 個人公眾號 :wmyskxz,個人獨立域名博客:wmyskxz.com,堅持原創輸出,下方掃碼關注,2020,與您共同成長!
非常感謝各位人才能 看到這里,如果覺得本篇文章寫得不錯,覺得 「我沒有三顆心臟」有點東西 的話,求點贊,求關注,求分享,求留言!
創作不易,各位的支持和認可,就是我創作的最大動力,我們下篇文章見!