python語法入門

引言

上一節中,我們已經對python這門語言有了大概的瞭解,也安裝好了python解釋器和IDE。下面我們將認識python語法,我們學習python語言的主要目的是爲了控制計算機、讓計算機能夠像人一樣去工作,所以在python這門語言中,所有語法存在的意義都是爲了讓計算機具備人的某一項技能,這句話是我們理解後續所有python語法的根本。

第一節 變量(Variables)

Variables are used to store information to be referenced and manipulated in a computer program. They also provide a way of labeling data with a descriptive name, so our programs can be understood more clearly by the reader and ourselves. It is helpful to think of variables as containers that hold information. Their sole purpose is to label and store data in memory. This data can then be used throughout your program.

變量就是用來存儲一些信息,供程序以後調用或者操作修改。變量爲標記數據提供了一種描述性的名字,以便我們的程序可以被程序的閱讀者很清晰的理解。把變量作爲一個存儲信息的容器會更容易理解變量。它的主要是目的是筆記和存儲在內存中的數據,這個數據就可以在你的整個程序中使用。
在這裏插入圖片描述

# 示例
name = 'Jason' # 記下人的名字爲'Jason'
sex = '男'    # 記下人的性別爲男性
age = 18      # 記下人的年齡爲18歲
salary = 30000.1  # 記下人的薪資爲30000.1元

解釋器執行到變量定義的代碼時會申請內存空間存放變量值,然後將變量值的內存地址綁定給變量名,以變量的定義age=18爲例:
在這裏插入圖片描述
變量的規範使用:

# 命名規範
1. 變量名只能是 字母、數字或下劃線的任意組合
2. 變量名的第一個字符不能是數字
3. 區分大小寫
4. 關鍵字不能聲明爲變量名,常用關鍵字如下
['and', 'as', 'assert', 'break', 'class', 'continue', 
'def', 'del', 'elif', 'else', 'except', 'exec', 'finally', 
'for', 'from','global', 'if', 'import', 'in', 'is', 'lambda', 
'not', 'or', 'pass', 'print', 'raise', 'return', 'try', 'while', 'with', 'yield']

# 錯誤示範如下:
*a=123
$b=456
c$=789
2_name='lili'
123='lili'
and=123
年齡=18 # 強烈建議不要使用中文或拼音命名

# 正確示範如下
age_of_jason=31
page1='首頁'
_class='終極一班'

變量的命名風格:

# 風格一:駝峯體
AgeOfTony = 56 
NumberOfStudents = 80
# 風格二:純小寫下劃線(在python中,變量名的命名推薦使用該風格)
age_of_tony = 56 
number_of_students = 80

變量值三大特性及查看方法:

1id
#反應的是變量在內存中的唯一編號,內存地址不同id肯定不同
2type
#變量值的類型
3、value
#變量值
查看示例:
x='Info Tony:18'
>>> id(x),type(x),x
4376607152<class 'str'>,'Info Tony:18'

常量:指在程序運行過程中不會改變的量
在這裏插入圖片描述
例如圓周率3.141592653…
在Python中沒有一個專門的語法定義常量,約定俗成是用全部大寫的變量名錶示常量。如:PI=3.14159。所以單從語法層面去講,常量的使用與變量完全一致

第二節 基本數據類型(Basic data type)

我們學習變量是爲了讓計算機能夠像人一樣去記憶事物的某種狀態,而變量的值就是用來存儲事物狀態的,很明顯事物的狀態分成不同種類的(比如人的年齡,身高,職位,工資等等),所以變量值也應該有不同的類型。
在這裏插入圖片描述

數字(Number)

Python3 支持 int、float、bool、complex(複數)
在Python 3裏,只有一種整數類型 int,表示爲長整型,沒有 python2 中的 Long
像大多數語言一樣,數值類型的賦值和計算都是很直觀的。
內置的 type() 方法可以用來查詢變量所指的對象類型。

>>> a, b, c, d = 20, 5.5, True, 4+3j
>>> print(type(a), type(b), type(c), type(d))
<class 'int'> <class 'float'> <class 'bool'> <class 'complex'>

數學運算

>>> a = 1
>>> b = 3
>>> c = a + b
>>> c
4

比較大小

>>> x = 10
>>> y = 11
>>> x > y
False

字符串(String)

用來記錄人的名字,家庭住址,性別等描述性質的狀態

name = 'Tony'
address = '上海市浦東新區'
sex = '男'

用單引號、雙引號、多引號,都可以定義字符串,本質上是沒有區別的,但是

#1、需要考慮引號嵌套的配對問題
msg = "My name is Tony , I'm 18 years old!" #內層有單引號,外層就需要用雙引號
#2、多引號可以寫多行字符串
msg = '''
        天下只有兩種人。比如五串脆骨到手,一種人挑最好的先吃,另一種人把最好的留到最後吃。
        照例第一種人應該樂觀,因爲他每吃一串都是吃剩的脆骨裏最好的;第二種人應該悲觀,因爲他每吃一串都是吃剩的脆骨裏最壞的。
        不過事實卻適得其反,緣故是第二種人還有希望,第一種人只有回憶。
      '''

在這裏插入圖片描述
數字可以進行加減乘除等運算,字符串呢?也可以,但只能進行"相加"和"相乘"運算。

>>> name = 'tony'
>>> age = '18'
>>> name + age #相加其實就是簡單的字符串拼接
'tony18'
>>> name * 5 #相乘就相當於將字符串相加了5次
'tonytonytonytonytony'

列表(List)

如果我們需要用一個變量記錄多個學生的姓名,用數字類型是無法實現,字符串類型確實可以記錄下來,比如stu_names=‘張三 李四 王五’,但存的目的是爲了取,此時若想取出第二個學生的姓名實現起來相當麻煩,而列表類型就是專門用來記錄多個同種屬性的值(比如同一個班級多個學生的姓名、同一個人的多個愛好等),並且存取都十分方便.

List(列表) 是 Python 中使用最頻繁的數據類型。它可以完成大多數集合類的數據結構實現。列表中元素的類型可以不相同,它支持數字,字符串甚至可以包含列表(所謂嵌套)
列表是寫在方括號 [ ] 之間、用逗號分隔開的元素列表
和字符串一樣,列表同樣可以被索引和截取,列表被截取後返回一個包含所需元素的新列表。

#stu_names=['張三','李四','王五']   #定義列表
# 1、列表類型是用索引來對應值,索引代表的是數據的位置,從0開始計數
>>> stu_names=['張三','李四','王五']
>>> stu_names[0] 
'張三'
>>> stu_names[1]
'李四'
>>> stu_names[2]
'王五'
# 2、列表可以嵌套,嵌套取值如下
>>> students_info=[['tony',18,['jack',]],['jason',18,['play','sleep']]]
>>> students_info[0][2][0] #取出第一個學生的第一個愛好
'jack'

字典(Dictionary)

如果我們需要用一個變量記錄多個值,但多個值是不同屬性的,比如人的姓名、年齡、身高,用列表可以存,但列表是用索引對應值的,而索引不能明確地表示值的含義,這就用到字典類型,字典類型是用key:value形式來存儲數據,其中key可以對value有描述性的功能

person_info={'name':'tony','age':18,'height':185.3}  # 定義了一個存儲用戶信息的字典
# 1、字典類型是用key來對應值,key可以對值有描述性的功能,通常爲字符串類型
>>> person_info={'name':'tony','age':18,'height':185.3}
>>> person_info['name']
'tony'
>>> person_info['age']
18
>>> person_info['height']
185.3
# 2、字典可以嵌套,嵌套取值如下
>>> students=[
... {'name':'tony','age':38,'hobbies':['play','sleep']},
... {'name':'jack','age':18,'hobbies':['read','sleep']},
... {'name':'rose','age':58,'hobbies':['music','read','sleep']},
... ]
>>> students[1]['hobbies'][1] #取第二個學生的第二個愛好
'sleep'

在這裏插入圖片描述

元組(Tuple)

暫略

集合(Set)

暫略

不可變數據(3 個):Number(數字)、String(字符串)、Tuple(元組)
可變數據(3 個):List(列表)、Dictionary(字典)、Set(集合)

第三節 垃圾回收機制(Garbage collection mechanism)

解釋器在執行到定義變量的語法時,會申請內存空間來存放變量的值,而內存的容量是有限的,這就涉及到變量值所佔用內存空間的回收問題,當一個變量值沒有用了(簡稱垃圾)就應該將其佔用的內存給回收掉,那什麼樣的變量值是沒有用的呢?

​ 由於變量名是訪問到變量值的唯一方式,所以當一個變量值不再關聯任何變量名時,我們就無法再訪問到該變量值了,該變量值就是沒有用的,就應該被當成一個垃圾回收。毫無疑問,內存空間的申請與回收是非常耗費精力的事情,而且存在很大的危險性,稍有不慎就有可能引發內存溢出問題,好在Cpython解釋器提供了自動的垃圾回收機制(GC)來幫我們解決了這件事。
在這裏插入圖片描述
Python的GC模塊主要運用了“引用計數”(reference counting)來跟蹤和回收垃圾。在引用計數的基礎上,還可以通過“標記-清除”(mark and sweep)解決容器對象可能產生的循環引用的問題,並且通過“分代回收”(generation collection)以空間換取時間的方式來進一步提高垃圾回收的效率
在這裏插入圖片描述

引用計數原理

引用計數就是:變量值被變量名關聯的次數
如:age=18
變量值18被關聯了一個變量名age,稱之爲引用計數爲1
在這裏插入圖片描述
引用計數增加:
age=18 (此時,變量值18的引用計數爲1)
m=age (把age的內存地址給了m,此時,m,age都關聯了18,所以變量值18的引用計數爲2)
在這裏插入圖片描述
引用計數減少:
age=10(名字age先與值18解除關聯,再與3建立了關聯,變量值18的引用計數爲1)
del m(del的意思是解除變量名x與變量值18的關聯關係,此時,變量18的引用計數爲0)
在這裏插入圖片描述
值18的引用計數一旦變爲0,其佔用的內存地址就應該被解釋器的垃圾回收機制回收

循環引用問題

在這裏插入圖片描述
變量值被關聯次數的增加或減少,都會引發引用計數機制的執行(增加或減少值的引用計數),這存在明顯的效率問題。

如果說執行效率還僅僅是引用計數機制的一個軟肋的話,那麼很不幸,引用計數機制還存在着一個致命的弱點,即循環引用(也稱交叉引用)

# 如下我們定義了兩個列表,簡稱列表1與列表2,變量名l1指向列表1,變量名l2指向列表2
>>> l1=['xxx']  # 列表1被引用一次,列表1的引用計數變爲1   
>>> l2=['yyy']  # 列表2被引用一次,列表2的引用計數變爲1   
>>> l1.append(l2)             # 把列表2追加到l1中作爲第二個元素,列表2的引用計數變爲2
>>> l2.append(l1)             # 把列表1追加到l2中作爲第二個元素,列表1的引用計數變爲2

# l1與l2之間有相互引用
# l1 = ['xxx'的內存地址,列表2的內存地址]
# l2 = ['yyy'的內存地址,列表1的內存地址]
>>> l1
['xxx', ['yyy', [...]]]
>>> l2
['yyy', ['xxx', [...]]]
>>> l1[1][1][0]
'xxx'

循環引用會導致:值不再被任何名字關聯,但是值的引用計數並不會爲0,應該被回收但不能被回收,什麼意思呢?試想一下,請看如下操作

>>> del l1 # 列表1的引用計數減1,列表1的引用計數變爲1
>>> del l2 # 列表2的引用計數減1,列表2的引用計數變爲1

此時,只剩下列表1與列表2之間的相互引用,兩個列表的引用計數均不爲0,但兩個列表不再被任何其他對象關聯,沒有任何人可以再引用到它們,所以它倆佔用內存空間應該被回收,但由於相互引用的存在,每一個對象的引用計數都不爲0,因此這些對象所佔用的內存永遠不會被釋放,所以循環引用是致命的,這與手動進行內存管理所產生的內存泄露毫無區別。
所以Python引入了“標記-清除” 與“分代回收”來分別解決引用計數的循環引用與效率低的問題
在這裏插入圖片描述

標記-清除

容器對象(比如:list,set,dict,class,instance)都可以包含對其他對象的引用,所以都可能產生循環引用。而“標記-清除”計數就是爲了解決循環引用的問題。

在瞭解標記清除算法前,我們需要明確一點,關於變量的存儲,內存中有兩塊區域:堆區與棧區,在定義變量時,變量名與值內存地址的關聯關係存放於棧區,變量值存放於堆區,內存管理回收的則是堆區的內容,詳解如下圖,

定義了兩個變量x = 10、y = 20
在這裏插入圖片描述
當我們執行x=y時,內存中的棧區與堆區變化如下
在這裏插入圖片描述
標記/清除算法的做法是當應用程序可用的內存空間被耗盡的時,就會停止整個程序,然後進行兩項工作,第一項則是標記,第二項則是清除

#1、標記
標記的過程其實就是,遍歷所有的GC Roots對象(棧區中的所有內容或者線程都可以作爲GC Roots對象),然後將所有GC Roots的對象可以直接或間接訪問到的對象標記爲存活的對象,其餘的均爲非存活對象,應該被清除。
#2、清除
清除的過程將遍歷堆中所有的對象,將沒有標記的對象全部清除掉。

直接引用指的是從棧區出發直接引用到的內存地址,間接引用指的是從棧區出發引用到堆區後再進一步引用到的內存地址,以我們之前的兩個列表l1與l2爲例畫出如下圖像
在這裏插入圖片描述
當我們同時刪除l1與l2時,會清理到棧區中l1與l2的內容
在這裏插入圖片描述
這樣在啓用標記清除算法時,發現棧區內不再有l1與l2(只剩下堆區內二者的相互引用),於是列表1與列表2都沒有被標記爲存活,二者會被清理掉,這樣就解決了循環引用帶來的內存泄漏問題。

分代回收

基於引用計數的回收機制,每次回收內存,都需要把所有對象的引用計數都遍歷一遍,這是非常消耗時間的,於是引入了分代回收來提高回收效率,分代回收採用的是用“空間換時間”的策略

分代回收的核心思想是:在歷經多次掃描的情況下,都沒有被回收的變量,gc機制就會認爲,該變量是常用變量,gc對其掃描的頻率會降低,具體實現原理如下:

分代指的是根據存活時間來爲變量劃分不同等級(也就是不同的代)
新定義的變量,放到新生代這個等級中,假設每隔1分鐘掃描新生代一次,如果發現變量依然被引用,那麼該對象的權重(權重本質就是個整數)加一,當變量的權重大於某個設定得值(假設爲3),會將它移動到更高一級的青春代,青春代的gc掃描的頻率低於新生代(掃描時間間隔更長),假設5分鐘掃描青春代一次,這樣每次gc需要掃描的變量的總個數就變少了,節省了掃描的總時間,接下來,青春代中的對象,也會以同樣的方式被移動到老年代中。也就是等級(代)越高,被垃圾回收機制掃描的頻率越低

回收依然是使用引用計數作爲回收的依據
在這裏插入圖片描述
雖然分代回收可以起到提升效率的效果,但也存在一定的缺點:
例如一個變量剛剛從新生代移入青春代,該變量的綁定關係就解除了,該變量應該被回收,但青春代的掃描頻率低於新生代,所以該變量的回收就會被延遲。
在這裏插入圖片描述

第四節 交互 、運算符、註釋 (Interaction, operator, comment)

暫略

第五節 流程控制(Process control)

暫略

發佈了41 篇原創文章 · 獲贊 9 · 訪問量 18萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章