Python基礎
介紹
輸入和輸出
- 所有的通過input獲取的數(shù)據(jù),都是字符串類型
- print()
變量
- 程序就是用來處理數(shù)據(jù)的,而變量就是用來存儲數(shù)據(jù)的
- 變量在程序中就是用一個變量名表示了,變量名必須是大小寫英文、數(shù)字和
_的組合,且不能用數(shù)字開頭 - 可以使用type(變量的名字),來查看變量的類型
- 在Python中,等號
=是賦值語句,可以把任意數(shù)據(jù)類型賦值給變量,同一個變量可以反復賦值,而且可以是不同類型的變量- 這種變量本身類型不固定的語言稱之為動態(tài)語言,與之對應的是靜態(tài)語言。靜態(tài)語言在定義變量時必須指定變量類型,如果賦值的時候類型不匹配,就會報錯。
常量
Python中,通常用全部大寫的變量名表示常量
- 比如常用的數(shù)學常數(shù)π就是一個常量,但事實上
PI仍然是一個變量,Python根本沒有任何機制保證PI不會被改變,所以,用全部大寫的變量名表示常量只是一個習慣上的用法
數(shù)據(jù)類型
可變對象與不可變對象
- 可變對象
- 列表list,字典dict,集合set
- 特點: 這個些數(shù)據(jù)類型,是可以直接在原對象上進行修改數(shù)據(jù),修改完成后,并不影響原對象地址
- 不可變對象
- 數(shù)字 int,字符串 str,浮點數(shù) float,布爾類型 bool,元組 tuple
- 特點: 這些數(shù)據(jù)都是不可以直接修改的,如果在修改或賦值時,都會開辟一個新空間
- 對于不變對象來說,調(diào)用對象自身的任意方法,也不會改變該對象自身的內(nèi)容。相反,這些方法會創(chuàng)建新的對象并返回,這樣,就保證了不可變對象本身永遠是不可變的。
整數(shù)int
浮點數(shù)float
- 浮點數(shù)也就是小數(shù),之所以稱為浮點數(shù),是因為按照科學記數(shù)法表示時,一個浮點數(shù)的小數(shù)點位置是可變的
- 整數(shù)和浮點數(shù)在計算機內(nèi)部存儲的方式是不同的,整數(shù)運算永遠是精確的(除法難道也是精確的?是的!),而浮點數(shù)運算則可能會有四舍五入的誤差。
字符串string
字符串是以單引號
'或雙引號"括起來的任意文本-
如果字符串內(nèi)部既包含
'又包含"怎么辦?可以用轉義字符\來標識- 轉義字符
\可以轉義很多字符,比如\n表示換行,\t表示制表符,字符\本身也要轉義,所以\\表示的字符就是\ - Python還允許用
r''表示''內(nèi)部的字符串默認不轉義
- 轉義字符
字符串與編碼
Unicode把所有語言都統(tǒng)一到一套編碼里,這樣就不會再有亂碼問題了。
ASCII編碼是1個字節(jié),而Unicode編碼通常是2個字節(jié)。
UTF-8編碼把一個Unicode字符根據(jù)不同的數(shù)字大小編碼成1-6個字節(jié),常用的英文字母被編碼成1個字節(jié),漢字通常是3個字節(jié),只有很生僻的字符才會被編碼成4-6個字節(jié)。如果你要傳輸?shù)奈谋景罅坑⑽淖址?,用UTF-8編碼就能節(jié)省空間
在計算機內(nèi)存中,統(tǒng)一使用Unicode編碼,當需要保存到硬盤或者需要傳輸?shù)臅r候,就轉換為UTF-8編碼。
最新的Python 3版本中,字符串是以Unicode編碼的,也就是說,Python的字符串支持多語言
- Python提供了
ord()函數(shù)獲取字符的整數(shù)表示, -
chr()函數(shù)把編碼轉換為對應的字符
>>> ord('A')
65
>>> ord('中')
20013
-
Python對
bytes類型的數(shù)據(jù)用帶b前綴的單引號或雙引號表示:x = b'ABC' 純英文的
str可以用ASCII編碼為bytes,內(nèi)容是一樣的,含有中文的str可以用UTF-8編碼為bytes,用encode()方法讀到的數(shù)據(jù)就是
bytes。要把bytes變?yōu)?code>str,就需要用decode()方法
列表,字典,集合
布爾值
- 布爾值只有
True、False兩種值
空值None
空值是Python里一個特殊的值,用None表示。None不能理解為0,因為0是有意義的,而None是一個特殊的空值。
數(shù)據(jù)類型的轉換
| 函數(shù) | 說明 |
|---|---|
| int(x [,base ]) | 將x轉換為一個整數(shù) |
| float(x ) | 將x轉換為一個浮點數(shù) |
| complex(real [,imag ]) | 創(chuàng)建一個復數(shù),real為實部,imag為虛部 |
| str(x ) | 將對象 x 轉換為字符串 |
| repr(x ) | 將對象 x 轉換為表達式字符串 |
| eval(str ) | 用來計算在字符串中的有效Python表達式,并返回一個對象 |
| tuple(s ) | 將序列 s 轉換為一個元組 |
| list(s ) | 將序列 s 轉換為一個列表 |
| chr(x ) | 將一個整數(shù)轉換為一個Unicode字符 |
| ord(x ) | 將一個字符轉換為它的ASCII整數(shù)值 |
| hex(x ) | 將一個整數(shù)轉換為一個十六進制字符串 |
| oct(x ) | 將一個整數(shù)轉換為一個八進制字符串 |
| bin(x ) | 將一個整數(shù)轉換為一個二進制字符串 |
運算符
| 運算符 | 描述 | 實例 |
|---|---|---|
| + | 加 | 兩個對象相加 a + b 輸出結果 30 |
| - | 減 | 得到負數(shù)或是一個數(shù)減去另一個數(shù) a - b 輸出結果 -10 |
| * | 乘 | 兩個數(shù)相乘或是返回一個被重復若干次的字符串 a * b 輸出結果 200 |
| / | 除 | b / a 輸出結果 2 |
| // | 取整除 | 返回商的整數(shù)部分 9//2 輸出結果 4 , 9.0//2.0 輸出結果 4.0 |
| % | 取余 | 返回除法的余數(shù) b % a 輸出結果 0 |
| ** | 指數(shù) | a**b 為10的20次方, 輸出結果 100000000000000000000 |
- 復合賦值運算符
| 運算符 | 描述 | 實例 |
|---|---|---|
| += | 加法賦值運算符 | c += a 等效于 c = c + a |
| -= | 減法賦值運算符 | c -= a 等效于 c = c - a |
| *= | 乘法賦值運算符 | c *= a 等效于 c = c * a |
| /= | 除法賦值運算符 | c /= a 等效于 c = c / a |
| %= | 取模賦值運算符 | c %= a 等效于 c = c % a |
| **= | 冪賦值運算符 | c **= a 等效于 c = c ** a |
| //= | 取整除賦值運算符 | c //= a 等效于 c = c // a |
字符串string
字符串輸出
格式化操作符
| 格式符號 | 轉換 |
|---|---|
| %c | 字符 |
| %s | 字符串 |
| %d | 有符號十進制整數(shù) |
| %u | 無符號十進制整數(shù) |
| %o | 八進制整數(shù) |
| %x | 十六進制整數(shù)(小寫字母0x) |
| %X | 十六進制整數(shù)(大寫字母0X) |
| %f | 浮點數(shù) |
| %e | 科學計數(shù)法(小寫'e') |
| %E | 科學計數(shù)法(大寫“E”) |
| %g | %f和%e 的簡寫 |
| %G | %f和%E的簡寫 |
age = 18
name = "xiaohua"
print("我的姓名是%s, 年齡是%d" % (name, age))
f-strings
f-strings 以字母 'f' 或 'F' 為前綴, 格式化字符串使用一對單引號、雙引號、三單引號、三雙引號
name = '峰哥'
age = 33
format_string1 = f'我的名字是 {name}, 我的年齡是 {age}'
format()
使用字符串的format()方法,它會用傳入的參數(shù)依次替換字符串內(nèi)的占位符{0}、{1}……
>>> 'Hello, {0}, 成績提升了 {1:.1f}%'.format('小明', 17.125)
'Hello, 小明, 成績提升了 17.1%'
常見操作
如有字符串mystr = 'hello world itcast and itcastcpp'
find & rfind
檢測 str 是否包含在 mystr中,如果是返回開始的索引值,否則返回-1
mystr.find(str, start=0, end=len(mystr))
mystr.find('it', 0, 10)
index & rindex
跟find()方法一樣,只不過如果str不在 mystr中會報一個異常.
mystr.index(str, start=0, end=len(mystr))
count
返回 str在start和end之間 在 mystr里面出現(xiàn)的次數(shù)
mystr.count(str, start=0, end=len(mystr))
replace
把 mystr 中的 str1 替換成 str2,如果 count 指定,則替換不超過 count 次.
mystr.replace(str1, str2, mystr.count(str1))
capitalize
把字符串的第一個字符大寫
mystr.capitalize()
title
把字符串的每個單詞首字母大寫
>>> a = "hello itcast"
>>> a.title()
'Hello Itcast'
lower
轉換 mystr 中所有大寫字符為小寫
mystr.lower()
upper
轉換 mystr 中的小寫字母為大寫
mystr.upper()
startswith
檢查字符串是否是以 "hello" 開頭, 是則返回 True,否則返回 False
mystr.startswith("hello")
endswith
檢查字符串是否以"world"結束,如果是返回True,否則返回 False.
mystr.endswith("world")
ljust & rjust
返回一個原字符串左對齊,并使用空格填充至長度 width 的新字符串
mystr.ljust(width)
center
返回一個原字符串居中,并使用空格填充至長度 width 的新字符串
mystr.center(width)
lstrip & rstrip & strip
刪除 mystr 左邊/右邊/兩邊的空白字符或指定字符
mystr.lstrip()
mystr.lstrip(“#”)
split
以 str 為分隔符切片 mystr,如果 maxsplit有指定值,則僅分隔 maxsplit 個子字符串
mystr.split(" ", 2)
splitlines
按照行分隔,返回一個包含各行作為元素的列表
mystr.splitlines()
partition & rpartition
把mystr以str分割成三部分,str前,str和str后
mystr.partition(str)
join
alist 中每個元素用str連接,構造出一個新的字符串
str = "_"
alist = ['1', '2', '3']
str.join(alist)
>>> ‘1_2_3’
isalpha & isdigit & isalnum & isspace
如果 mystr 所有字符都是字母/數(shù)字/字母或數(shù)字/空格 則返回 True,否則返回 False
mystr.isalpha()
列表list
Python內(nèi)置的一種數(shù)據(jù)類型是列表:list。list是一種有序的集合,可以隨時添加和刪除其中的元素。
相關操作
添加元素
- append(),向列表末尾添加元素
- extend(),可以將另一個列表中的元素逐一添加到列表中
- insert(index, item),在指定位置index前插入元素object
修改元素
- li[i] = x,把索引為i的元素修改為x
查找元素
所謂的查找,就是看看指定的元素是否存在
python中查找的常用方法為:
- in(存在),如果存在那么結果為true,否則為false
- not in(不存在),如果不存在那么結果為true,否則false
index查找(與字符串查找相同)
>>> a = ['a', 'b', 'c', 'a', 'b']
>>> a.index('a', 1, 3) # 注意是左閉右開區(qū)間
刪除元素
-
del:根據(jù)下標進行刪除
del li[i] # 刪除列表li中索引為i元素 -
pop():按索引刪除一個元素,刪除時會返回被刪除的元素
li.pop() # 刪除最后一個元素 li.pop(i) # 刪除列表li中索引為i元素 -
remove:根據(jù)元素的值進行刪除,刪除第一個符合條件的值
>>> str=[1,2,3,4,5,2,6] >>> str.remove(2) >>> str [1, 3, 4, 5, 2, 6]
個數(shù)len()
用len()函數(shù)可以獲得list元素的個數(shù)
排序sort()
sort方法是將list按特定順序重新排列,默認為由小到大,參數(shù)reverse=True可改為倒序,由大到小。
遍歷
-
使用for循環(huán)
- 通過for ... in ... 我們可以遍歷字符串、列表、元組、字典等
namesList = ['xiaoWang','xiaoZhang','xiaoHua'] for name in namesList: print(name) -
使用while循環(huán)
namesList = ['xiaoWang','xiaoZhang','xiaoHua'] i = 0 while i < len(namesList): print(namesList[i]) i+ = 1
元組tuple
- 另一種有序列表叫元組:tuple。tuple和list非常類似,但是tuple一旦初始化就不能修改,元組使用小括號,列表使用方括號。
- 因為tuple不可變,所以代碼更安全。如果可能,能用tuple代替list就盡量用tuple。
- 元組只有一個元素
t = (1,)
小結
-
元組修改元素
將元組轉換為列表并更改值
-
通過現(xiàn)有字符串的片段在構造一個新的字符串的方式來等同于更新元組操作
tuple_1=(1,2,3,"ewang","demo") #通過索引更新 tuple_1=tuple_1[0],tuple_1[2],tuple_1[4] print tuple_1 #通過切片更新 tuple_1=tuple_1[0:2] print tuple_1 # 添加元素 tuple_1 = tuple_1 + (4,)
-
列表和元組區(qū)別
- 列表是可變類型,元組是不可變類型,可變類型值,值發(fā)生變化地址不變;不可變類型指,值發(fā)生改變,地址也發(fā)生改變,值指向一個新的值
切片
切片是指對操作的對象截取其中一部分的操作。
字符串、列表、元組都支持切片操作。
語法
- [起始:結束:步長]
- 注意:選取的區(qū)間從"起始"位開始,到"結束"位的前一位結束(不包含結束位本身)(左閉右開),步長表示選取間隔。
使用
s = "abcdefghijk"
print(s[0:5:1])
print(s[0:5:2])
print(s[3:6]) # 默認步長可以不寫,默認為1
print(s[:5]) # 開始索引也可以不寫,默認從頭開始
print(s[5:]) # 結束也可以不寫,默認到最后
print(s[:]) # 全默認,默認截取整串
print(s)
print(s[10:20]) # 切片時不會出現(xiàn)下標越界錯誤
# 切片的下標還可是以負數(shù)
# 負數(shù)是,是從右向左切片,起始下標為 -1
print(s[-1:-5])
print(s[-1:-5:-1])
# 特殊需要記住的切片方式
# 使用切片實現(xiàn)字符串逆序
print(s[::-1])
字典dict
使用鍵-值(key-value)存儲,具有極快的查找速度。
相關操作
查看元素
-
通過鍵訪問值
di['key'] = value # 若訪問不存在的鍵,則會報錯 -
在我們不確定字典中是否存在某個鍵而又想獲取其值時,可以使用get方法,還可以設置默認值
>>> age = info.get('age') >>> age #'age'鍵不存在,所以age為None >>> type(age) <type 'NoneType'> >>> age = info.get('age', 18) # 若info中不存在'age'這個鍵,就返回默認值18 >>> age 18
修改元素
字典的每個元素中的數(shù)據(jù)是可以修改的,只要通過key找到,即可修改
di['a'] = x # 把鍵為a的值修改為x
添加元素
在使用 變量名['鍵'] = 數(shù)據(jù) 時,這個“鍵”在字典中,不存在,那么就會新增這個元素
di['newkey'] = newvalue
刪除元素
pop('key'):按鍵刪除一個元素,刪除時會返回被刪除元素的值
del di['key']:刪除指定鍵
del di:刪除整個字典對象
-
clear():清空整個字典內(nèi)容
di.clear() >>>{}
個數(shù)len()
len(di)
keys()
返回一個包含字典所有KEY的列表
values()
返回一個包含字典所有value的列表
items()
返回一個包含所有(鍵,值)元祖的列表
di = {'Michael': 95, 'Bob': 75, 'Tracy': 85}
di.keys()
>>> ['Michael', 'Bob', 'Tracy']
di.values()
>>> [95, 75, 85]
di.items()
>>> [('Michael', 95), ('Bob', 75), ('Tracy', 85)]
遍歷
遍歷鍵key
di = {'Michael': 95, 'Bob': 75}
for key in di.keys():
print(key)
>>>
Michael
Bob
遍歷值value
di = {'Michael': 95, 'Bob': 75}
for value in di.values():
print(value)
>>>
95
75
遍歷項(元素)
di = {'Michael': 95, 'Bob': 75}
for item in di.items():
print(item)
>>>
('Michael', 95)
('Bob', 75)
遍歷字典的key-value
di = {'Michael': 95, 'Bob': 75}
for key,value in di.items():
print('%s, %s'% (key,value))
>>>
Michael, 95
Bob, 75
小結
dict內(nèi)部存放的順序和key放入的順序是沒有關系的。
-
和list比較,dict有以下幾個特點:
- 查找和插入的速度極快,不會隨著key的增加而變慢;
- 需要占用大量的內(nèi)存,內(nèi)存浪費多。
而list相反:
- 查找和插入的時間隨著元素的增加而增加;
- 占用空間小,浪費內(nèi)存很少。
所以,dict是用空間來換取時間的一種方法。
-
dict的key必須是不可變對象。
- 這是因為dict根據(jù)key來計算value的存儲位置,如果每次計算相同的key得出的結果不同,那dict內(nèi)部就完全混亂了。這個通過key計算位置的算法稱為哈希算法(Hash)。
- 要保證hash的正確性,作為key的對象就不能變。字符串、整數(shù)等都是不可變的,因此,可以放心地作為key。而list是可變的,就不能作為key
集合set
set和dict類似,也是一組key的集合,但不存儲value。由于key不能重復,所以,在set中,沒有重復的key。
相關操作
添加元素
通過add(key)方法可以添加元素到set中,可以重復添加,但不會有效果
s.add(key)
刪除元素
通過remove(key)方法可以刪除元素
s.remove(key)
clear()清空元素
交集(&)
交集指的是兩個不同的集合中相同的集合打印出來
>>> a = set('abc')
>>> b = set('cdef')
>>> a & b
set(['c'])
并集(|)
將兩個集合中所有元素合并到一起
>>> a = set('abc')
>>> b = set('cdef')
>>> a | b
set(['a', 'c', 'b', 'e', 'd', 'f'])
差集(-)
差集指的是兩個沒有集合中不同的元素,前面的集合為準
>>> a = set('abc')
>>> b = set('cdef')
>>> a - b
set(['a', 'b'])
對稱差集(^)
集合A與集合B中所有不屬于A∩B的元素的集合
>>> a = set('abc')
>>> b = set('cdef')
>>> a ^ b
set(['a', 'b', 'd' 'e', 'd', 'f'])
[圖片上傳失敗...(image-90cfb9-1620306245275)]
判斷語句
循環(huán)語句
break和continue
- break的作用:立刻結束break**所在的循環(huán)
- continue的作用:用來結束本次循環(huán),緊接著執(zhí)行下一次的循環(huán)
- break/continue只能用在循環(huán)中,除此以外不能單獨使用
- break/continue在嵌套循環(huán)中,只對最近的一層循環(huán)起作用
高級特性
列表生成式
>>> [x * x for x in range(1, 11)]
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
# for循環(huán)后面還可以加上if判斷,這樣我們就可以篩選出僅偶數(shù)的平方:
>>> [x * x for x in range(1, 11) if x % 2 == 0]
[4, 16, 36, 64, 100]
# 還可以使用兩層循環(huán),可以生成全排列
>>> [m + n for m in 'ABC' for n in 'XYZ']
['AX', 'AY', 'AZ', 'BX', 'BY', 'BZ', 'CX', 'CY', 'CZ']
# 把一個list中所有的字符串變成小寫
>>> L = ['Hello', 'World', 'IBM', 'Apple']
>>> [s.lower() for s in L]
['hello', 'world', 'ibm', 'apple']
迭代與迭代器
迭代是訪問集合元素的一種方式,如果給定一個list或tuple,我們可以通過for循環(huán)來遍歷這個list或tuple,這種遍歷我們稱為迭代(Iteration)。
可以直接作用于
for循環(huán)的對象統(tǒng)稱為可迭代對象:Iterable。可以被
next()函數(shù)調(diào)用并不斷返回下一個值的對象稱為迭代器:Iterator。它們表示一個惰性計算的序列生成器都是
Iterator對象,但list、dict、str雖然是可迭代對象Iterable,卻不是迭代器Iterator。把
list、dict、str等Iterable變成Iterator可以使用iter()函數(shù)-
可以直接作用于
for循環(huán)的數(shù)據(jù)類型有以下幾種:一類是集合數(shù)據(jù)類型,如
list、tuple、dict、set、str等;一類是
generator,包括生成器和帶yield的generator function。這些可以直接作用于
for循環(huán)的對象統(tǒng)稱為可迭代對象:Iterable。 可以使用
isinstance()判斷一個對象是否是Iterable對象
生成器
根據(jù)程序員制定的規(guī)則循環(huán)生成數(shù)據(jù),當條件不成立時則生成數(shù)據(jù)結束。數(shù)據(jù)不是一次性全部生成出來,而是使用一個,再生成一個,可以節(jié)約大量的內(nèi)存。
創(chuàng)建方式
-
生成器推導式
-
只要把一個列表生成式的
[]改成(),就創(chuàng)建了一個generator>>> g = (x * x for x in range(10)) >>> g <generator object <genexpr> at 0x1022ef630> >>> next(g) 0 >>> next(g) 1 …… >>> next(g) 81 >>> next(g) Traceback (most recent call last): File "<stdin>", line 1, in <module> StopIteration generator保存的是算法,每次調(diào)用
next(g),就計算出g的下一個元素的值,直到計算到最后一個元素,沒有更多的元素時,拋出StopIteration的錯誤。也可使用
for循環(huán),因為generator也是可迭代對象;我們創(chuàng)建了一個generator后,基本上永遠不會調(diào)用next(),而是通過for循環(huán)來迭代它,并且不需要關心StopIteration的錯誤。
-
-
yield 關鍵字
-
只要在def函數(shù)里面看到有 yield 關鍵字那么就是生成器
def get_value(n): for i in range(n): print('生成第一個值') # yield 關鍵字的作用是將這個函數(shù)變成一個生成器對象 # 執(zhí)行時,解釋器遇到 yield 后會中斷代碼的執(zhí)行,并返回yield后的數(shù)據(jù), # 下一次再執(zhí)行時,會恢復前面yield中斷的狀態(tài),繼續(xù)執(zhí)行 yield i print('第一個生成完成') g = get_value(4) value = next(g) print(value) value = next(g) print(value) 代碼執(zhí)行到 yield 會暫停,然后把結果返回出去,下次啟動生成器會在暫停的位置繼續(xù)往下執(zhí)行
yield 就是保存當前程序執(zhí)行狀態(tài)
-
應用
-
斐波那契數(shù)列
def fibonacci(max): n, a, b = 0, 0, 1 while n < max: yield b a, b = b, a + b n = n + 1 return 'done' fib = fibonacci(5) # 遍歷生成的數(shù)據(jù) for value in fib: print(value)
迭代器和生成器的區(qū)別
(1)生成器:
生成器本質(zhì)上就是一個函數(shù),它記住了上一次返回時在函數(shù)體中的位置。
對生成器函數(shù)的第二次(或第n次)調(diào)用,跳轉到函數(shù)上一次掛起的位置。
而且記錄了程序執(zhí)行的上下文。
生成器不僅“記住”了它的數(shù)據(jù)狀態(tài),生成還記住了程序執(zhí)行的位置。(2)迭代器
迭代器是一種支持next()操作的對象。它包含了一組元素,當執(zhí)行next()操作時,返回其中一個元素。
當所有元素都被返回后,再執(zhí)行next()報異常—StopIteration
生成器一定是可迭代的,也一定是迭代器對象(3)區(qū)別:
①生成器是生成元素的,迭代器是訪問集合元素的一中方式
②迭代輸出生成器的內(nèi)容
③迭代器是一種支持next()操作的對象
④迭代器(iterator):其中iterator對象表示的是一個數(shù)據(jù)流,可以把它看做一個有序序列,但我們不能提前知道序列的長度,只有通過nex()函數(shù)實現(xiàn)需要計算的下一個數(shù)據(jù)。可以看做生成器的一個子集。
函數(shù)
如果在開發(fā)程序時,需要某塊代碼多次,但是為了提高編寫的效率以及代碼的重用,所以把具有獨立功能的代碼塊組織為一個小模塊,這就是函數(shù)
基礎
局部變量與全局變量
-
局部變量
- 局部變量,就是在函數(shù)內(nèi)部定義的變量
- 其作用范圍是這個函數(shù)內(nèi)部,即只能在這個函數(shù)中使用,在函數(shù)的外部是不能使用的
- 因為其作用范圍只是在自己的函數(shù)內(nèi)部,所以不同的函數(shù)可以定義相同名字的局部變量
- 局部變量的作用,為了臨時保存數(shù)據(jù)需要在函數(shù)中定義變量來進行存儲
- 當函數(shù)調(diào)用時,局部變量被創(chuàng)建,當函數(shù)調(diào)用完成后這個變量就不能夠使用了
-
全局變量
在函數(shù)外邊定義的變量叫做
全局變量全局變量能夠在所有的函數(shù)中進行訪問
當函數(shù)內(nèi)出現(xiàn)局部變量和全局變量相同名字時,函數(shù)內(nèi)部中的
變量名 = 數(shù)據(jù)此時理解為定義了一個局部變量,而不是修改全局變量的值-
如果在函數(shù)中出現(xiàn)
global 全局變量的名字那么這個函數(shù)中即使出現(xiàn)和全局變量名相同的變量名 = 數(shù)據(jù)也理解為對全局變量進行修改,而不是定義局部變量# 可以使用一次global對多個全局變量進行聲明 global a, b # 還可以用多次global聲明都是可以的 # global a # global b
定義函數(shù)
定義一個函數(shù)要使用def語句,依次寫出函數(shù)名、括號、括號中的參數(shù)和冒號:,然后,在縮進塊中編寫函數(shù)體,函數(shù)的返回值用return語句返回。
函數(shù)名也是變量
調(diào)用函數(shù)
通過 函數(shù)名() 即可完成調(diào)用
- 每次調(diào)用函數(shù)時,函數(shù)都會從頭開始執(zhí)行,當這個函數(shù)中的代碼執(zhí)行完畢后,意味著調(diào)用結束了
- 當然了如果函數(shù)中執(zhí)行到了return也會結束函數(shù)
- 函數(shù)名其實就是指向一個函數(shù)對象的引用,完全可以把函數(shù)名賦給一個變量,相當于給這個函數(shù)起了一個“別名”
參數(shù)
默認參數(shù)(缺省參數(shù))
- 在定義函數(shù)時,函數(shù)中的形式參數(shù),被賦值,這個值就是默認值
- 當在函數(shù)調(diào)用時,如果給定了值,那么就使用給定值,如果沒有給定值,那就使用默認值
- ++注意:默認值參數(shù)只能出現(xiàn)在參數(shù)列表的最右側**++
位置參數(shù)(實參)
- 使用位置參數(shù)時,因為類型的原因,那么實參的順序要和形參的順序完全一致;
- 當沒有默認值的情況下,參數(shù)的個數(shù)和也要一致
關鍵詞參數(shù)(形參)
- 在定義形式參時,每個參數(shù)都可以理解成一個key;
使用這個key,可以明確的為當前這個參數(shù)進行賦值;使用關鍵字參數(shù),可以忽略參數(shù)的順序問題
不定長位置參數(shù)
- *args 在參數(shù)中定義了該形參后,那可以通過 *args 接收多個不確定個數(shù)的位置參數(shù)
- ++加了星號(*)的變量args會存放所有未命名的變量參數(shù),*args為元組++
不定長關鍵字參數(shù)
- **kwargs 在參數(shù)中定義了該形參后,那可以通過 **kwargs 接收多個不確定個數(shù)的關鍵字參數(shù)
- 加**的變量kwargs會存放命名參數(shù),即形如key=value的參數(shù), **kwargs為字典.
小結
定義時小括號中的參數(shù),用來接收參數(shù)用的,稱為 “形參”
調(diào)用時小括號中的參數(shù),用來傳遞給函數(shù)用的,稱為 “實參”
當全局變量和局部變量同名時,在函數(shù)內(nèi)使用變量,優(yōu)先使用局部變量;局部變量優(yōu)先級高于全局變量
-
參數(shù)定義的順序必須是:必選參數(shù)、默認參數(shù)、不定長位置參數(shù)和不定長關鍵字參數(shù)。
def f1(a, b, c=0, *args, **kwargs): print('a =', a, 'b =', b, 'c =', c, 'args =', args, 'kw =', kw) 對于任意函數(shù),都可以通過類似
func(*args, **kw)的形式調(diào)用它,無論它的參數(shù)是如何定義的。
高階函數(shù)
既然變量可以指向函數(shù),函數(shù)的參數(shù)能接收變量,那么一個函數(shù)就可以接收另一個函數(shù)作為參數(shù),這種函數(shù)就稱之為高階函數(shù)。
map()函數(shù)
map()函數(shù)接收兩個參數(shù),一個是函數(shù),一個是Iterable,map將傳入的函數(shù)依次作用到序列的每個元素,并把結果作為新的Iterator返回。
>>> def f(x):
... return x * x
...
>>> r = map(f, [1, 2, 3, 4, 5, 6, 7, 8, 9])
>>> list(r)
[1, 4, 9, 16, 25, 36, 49, 64, 81]
把這個list所有數(shù)字轉為字符串:
>>> list(map(str, [1, 2, 3, 4, 5, 6, 7, 8, 9]))
['1', '2', '3', '4', '5', '6', '7', '8', '9']
reduce()函數(shù)
reduce把一個函數(shù)作用在一個序列[x1, x2, x3, ...]上,這個函數(shù)必須接收兩個參數(shù)
reduce把結果繼續(xù)和序列的下一個元素做累積計算:
import functools
my_list = [1, 2, 3, 4, 5]
def f(x1, x2):
return x1 + x2
result = functools.reduce(f, my_list)
print(result)
>>>15
當然求和運算可以直接用Python內(nèi)建函數(shù)sum(),沒必要動用reduce。
如果要把序列[1, 3, 5, 7, 9]變換成整數(shù)13579:
>>> from functools import reduce
>>> def fn(x, y):
... return x * 10 + y
...
>>> reduce(fn, [1, 3, 5, 7, 9])
13579
filter()函數(shù)
和map()類似,filter()也接收一個函數(shù)和一個序列,不同的是,filter()把傳入的函數(shù)依次作用于每個元素,然后根據(jù)返回值是True還是False決定保留還是丟棄該元素。
例如,在一個list中,刪掉偶數(shù),只保留奇數(shù),可以這么寫:
def is_odd(n):
return n % 2 == 1
list(filter(is_odd, [1, 2, 4, 5, 6, 9, 10, 15]))
# 結果: [1, 5, 9, 15]
注意到filter()函數(shù)返回的是一個Iterator,也就是一個惰性序列,所以要強迫filter()完成計算結果,需要用list()函數(shù)獲得所有結果并返回list。
sorted()函數(shù)
Python內(nèi)置的sorted()函數(shù)就可以對list進行排序
此外,sorted()函數(shù)也是一個高階函數(shù),它還可以接收一個key函數(shù)來實現(xiàn)自定義的排序,例如按絕對值大小排序:
>>> sorted([36, 5, -12, 9, -21], key=abs)
[5, 9, -12, -21, 36]
給sorted傳入key函數(shù),即可實現(xiàn)忽略大小寫的排序:
>>> sorted(['bob', 'about', 'Zoo', 'Credit'], key=str.lower)
['about', 'bob', 'Credit', 'Zoo']
要進行反向排序,不必改動key函數(shù),可以傳入第三個參數(shù)reverse=True
匿名函數(shù)lambda
定義的函數(shù)沒有名字,這樣的函數(shù)叫做匿名函數(shù).
介紹
格式:lambda [形參1], [形參2], ... : [單行表達式] 或 [函數(shù)調(diào)用]
-
lambda定義和普通函數(shù)的區(qū)別:
- lambda 沒有函數(shù)名,不必擔心函數(shù)名沖突
- lambda 參數(shù)列表外沒有括號
- lambda 函數(shù)體中,只能實現(xiàn)簡單的表達式計算或函數(shù)調(diào)用
- lambda 函數(shù)體中,不能使用Return,if,while,for-in 這些都不行,不用寫
return,返回值就是該表達式的結果。 - lambda 函數(shù)體中,可以使用if 實現(xiàn)的三目運算符.
應用
-
定義簡單的單行函數(shù)
my_function = lambda a, b: a + b -
作為函數(shù)的參數(shù)進行傳遞
解決目標: 1、提高函數(shù)的通用性 2、減少代碼量
-
例子
#現(xiàn)有字典 d={‘a(chǎn)’:24,‘g’:52,‘i’:12,‘k’:33}請按字典中的 value 值進行排序? sorted(d.items(),key = lambda x:x[1]) #一句話解決階乘函數(shù) reduce(lambda x,y: x*y, range(1,n+1))
閉包
在函數(shù)嵌套的前提下,內(nèi)部函數(shù)使用了外部函數(shù)的變量,并且外部函數(shù)返回了內(nèi)部函數(shù),我們把這個使用外部函數(shù)變量的內(nèi)部函數(shù)稱為閉包。
構成條件
- 在函數(shù)嵌套(函數(shù)里面再定義函數(shù))的前提下
- 內(nèi)部函數(shù)使用了外部函數(shù)的變量(還包括外部函數(shù)的參數(shù))
- 外部函數(shù)返回了內(nèi)部函數(shù)
示例
# 定義一個外部函數(shù)
def func_out(num1):
# 定義一個內(nèi)部函數(shù)
def func_inner(num2):
# 內(nèi)部函數(shù)使用了外部函數(shù)的變量(num1)
result = num1 + num2
print("結果是:", result)
# 外部函數(shù)返回了內(nèi)部函數(shù),這里返回的內(nèi)部函數(shù)就是閉包
return func_inner
# 創(chuàng)建閉包實例
f = func_out(1)
# 執(zhí)行閉包
f(2)
f(3)
>>>
結果是: 3
結果是: 4
小結
閉包可以保存外部函數(shù)內(nèi)的變量,不會隨著外部函數(shù)調(diào)用完而銷毀。
閉包不僅可以保存外部函數(shù)的變量還可以提高代碼的可重用行。
返回閉包時牢記一點:返回函數(shù)不要引用任何循環(huán)變量,或者后續(xù)會發(fā)生變化的變量。
-
在內(nèi)部函數(shù)中修改外部變量使用nonlocal 關鍵字
nonlocal num1 # 告訴解釋器,此處使用的是 外部變量a # 修改外部變量num1 num1 = 10
裝飾器
給已有函數(shù)增加額外功能的函數(shù),它本質(zhì)上就是一個閉包函數(shù)。
特點
- 不修改已有函數(shù)的源代碼
- 不修改已有函數(shù)的調(diào)用方式
- 給已有函數(shù)增加額外的功能
語法格式
# 裝飾器
def decorator(fn): # fn:被裝飾的目標函數(shù).
def inner():
'''執(zhí)行函數(shù)之前'''
fn() # 執(zhí)行被裝飾的目標函數(shù)
'''執(zhí)行函數(shù)之后'''
return inner
例子
# 添加一個登錄驗證的功能
def check(fn):
print("裝飾器函數(shù)執(zhí)行了")
def inner():
print("請先登錄....")
fn()
return inner
# 使用語法糖方式來裝飾函數(shù)
@check
def comment():
print("發(fā)表評論")
comment()
>>>
請先登錄....
發(fā)表評論
裝飾帶有參數(shù)和返回值的函數(shù)
# 添加輸出日志的功能
def logging(fn):
def inner(num1, num2):
print("--正在努力計算--")
result = fn(num1, num2)
return result
return inner
# 使用裝飾器裝飾函數(shù)
@logging
def sum_num(a, b):
result = a + b
return result
result = sum_num(1, 2)
print(result)
>>>
--正在努力計算--
3
通用裝飾器
# 通用裝飾器
def logging(fn):
def inner(*args, **kwargs):
print("--正在努力計算--")
result = fn(*args, **kwargs)
return result
return inner
多個裝飾器
- 裝飾過程是: 離函數(shù)最近的裝飾器先裝飾,然后外面的裝飾器再進行裝飾,由內(nèi)到外的裝飾過程
def make_div(func):
"""對被裝飾的函數(shù)的返回值 div標簽"""
def inner():
return "<div>" + func() + "</div>"
return inner
def make_p(func):
"""對被裝飾的函數(shù)的返回值 p標簽"""
def inner():
return "<p>" + func() + "</p>"
return inner
# 裝飾過程: 1 content = make_p(content) 2 content = make_div(content)
# content = make_div(make_p(content))
@make_div
@make_p
def content():
return "人生苦短"
result = content()
print(result)
>>>
<div><p>人生苦短</p></div>
帶有參數(shù)的裝飾器
使用裝飾器裝飾函數(shù)的時候可以傳入指定參數(shù),語法格式: @裝飾器(參數(shù),...)
-
寫法:
在裝飾器外面再包裹上一個函數(shù),讓最外面的函數(shù)接收參數(shù),返回的是裝飾器,因為@符號后面必須是裝飾器實例。
# 添加輸出日志的功能
def logging(flag):
def decorator(fn):
def inner(num1, num2):
if flag == "+":
print("--正在努力加法計算--")
elif flag == "-":
print("--正在努力減法計算--")
result = fn(num1, num2)
return result
return inner
# 返回裝飾器
return decorator
# 使用裝飾器裝飾函數(shù)
@logging("+")
def add(a, b):
result = a + b
return result
result = add(1, 2)
print(result)
面向?qū)ο?/h1>
面向?qū)ο缶幊獭狾bject Oriented Programming,簡稱OOP,是一種程序設計思想。OOP把對象作為程序的基本單元,一個對象包含了數(shù)據(jù)和操作數(shù)據(jù)的函數(shù)。
三大特征有:封裝性、繼承性、多態(tài)性。
類和對象
面向?qū)ο缶幊痰?個非常重要的概念:類和對象
類是抽象的模板,而實例則是一個一個具體的對象,各個實例擁有的數(shù)據(jù)都互相獨立,互不影響;
-
類的構成
- 類的名稱:類名(命名規(guī)則按照"大駝峰命名法")
- 類的屬性:一組數(shù)據(jù)
- 類的方法:允許對進行操作的方法 (行為)
方法就是與實例綁定的函數(shù),和普通函數(shù)不同,方法可以直接訪問實例的數(shù)據(jù);
-
對象
- 對象名 = 類名(參數(shù)列表....)
- 對象調(diào)用方法的格式:對象名.方法名(參數(shù)列表)
- 注意:方法中參數(shù)列表中的第一個參數(shù)self,不需要手動傳遞,這個參數(shù)是由解釋器在執(zhí)行程序時,自動傳遞的默認會將當前調(diào)用方法的對象引用傳遞進去
在方法內(nèi)通過self獲取對象屬性
class Hero(object):
"""定義了一個英雄類,可以移動和攻擊"""
def move(self):
"""實例方法"""
print("正在前往事發(fā)地點...")
def attack(self):
"""實例方法"""
print("發(fā)出了一招強力的普通攻擊...")
def info(self):
"""在類的實例方法中,通過self獲取該對象的屬性"""
print("英雄 %s 的生命值 :%d" % (self.name, self.hp))
print("英雄 %s 的攻擊力 :%d" % (self.name, self.atk))
print("英雄 %s 的護甲值 :%d" % (self.name, self.armor))
# 實例化了一個英雄對象 泰達米爾
taidamier = Hero()
# 給對象添加屬性,以及對應的屬性值
taidamier.name = "泰達米爾" # 姓名
taidamier.hp = 2600 # 生命值
taidamier.atk = 450 # 攻擊力
taidamier.armor = 200 # 護甲值
# 通過.成員選擇運算符,獲取對象的實例方法
taidamier.info() # 只需要調(diào)用實例方法info(),即可獲取英雄的屬性
taidamier.move()
taidamier.attack()
_init_()方法
兩個下劃線開始,兩個下劃線結束的方法,就是魔法方法,__init__()就是一個魔法方法,通常用來做屬性初始化 或 賦值 操作。
-
__init__()方法,在創(chuàng)建一個對象時默認被調(diào)用,不需要手動調(diào)用 -
__init__(self)中的self參數(shù),不需要開發(fā)者傳遞,python解釋器會自動把當前的對象引用傳遞過去。如果在創(chuàng)建對象時傳遞了2個實參,那么__init__(self)中出了self作為第一個形參外還需要2個形參,例如__init__(self,x,y)
__str__()方法
用來顯示信息,該方法需要 return 一個數(shù)據(jù),并且只有self一個參數(shù),當在類的外部 print(對象) 則打印這個數(shù)據(jù)
def __str__(self):
return "英雄 <%s> 數(shù)據(jù): 生命值 %d" % (self.name, self.hp)
- 當使用print輸出對象的時候,默認打印對象的內(nèi)存地址。如果類定義了
__str__(self)方法,那么就會打印從在這個方法中return的數(shù)據(jù) -
__str__方法通常返回一個字符串,作為這個對象的描述信息
__del__()方法
當刪除對象時,python解釋器也會默認調(diào)用
- 當有變量保存了一個對象的引用時,此對象的引用計數(shù)就會加1;當使用del() 刪除變量指向的對象時,則會減少對象的引用計數(shù)。
- 如果對象的引用計數(shù)不為1,那么會讓這個對象的引用計數(shù)減1,當對象的引用計數(shù)為0的時候,則對象才會被真正刪除(內(nèi)存被回收)。
小結
- 在類內(nèi)部獲取 屬性 和 實例方法,通過self獲?。?/li>
- 在類外部獲取 屬性 和 實例方法,通過對象名獲取。
- 如果一個類有多個對象,每個對象的屬性是各自保存的,都有各自獨立的地址;
- 但是實例方法是所有對象共享的,只占用一份內(nèi)存空間。類會通過self來判斷是哪個對象調(diào)用了實例方法。
封裝
意義
- 將屬性和方法放到一起做為一個整體,然后通過實例化對象來處理;
- 隱藏內(nèi)部實現(xiàn)細節(jié),只需要和對象及其屬性和方法交互就可以了;
- 對類的屬性和方法增加 訪問權限控制。
私有屬性和私有方法
在屬性名和方法名 前面 加上兩個下劃線 __
- 類的私有屬性 和 私有方法,都不能通過對象直接訪問,但是可以在本類內(nèi)部訪問;
- 類的私有屬性 和 私有方法,都不會被子類繼承,子類也無法訪問;
- 私有屬性 和 私有方法 往往用來處理類的內(nèi)部事情,不通過對象處理,起到安全作用
修改私有屬性
定義一個可以調(diào)用的公有方法,在這個公有方法內(nèi)訪問修改。
通常會定義get_xxx()方法和set_xxx()方法來獲取和修改私有屬性值。
-
set/get方法對私有屬性操作時的好處:
- 提供精確的訪問控制權限
- 隱藏實現(xiàn)細節(jié),讓代碼更安全
- 可以提供更加安全的數(shù)據(jù)有效性控制
class Master(object): def __init__(self): # 私有屬性,可以在類內(nèi)部通過self調(diào)用,但不能通過對象訪問 self.__money = 10000 # 返回私有屬性的值 def get_money(self): return self.__money # 接收參數(shù),修改私有屬性的值 def set_money(self, num): self.__money = num
繼承
- 在程序中,繼承描述的是多個類之間的所屬關系。
- 如果一個類A里面的屬性和方法可以復用,則可以通過繼承的方式,傳遞到類B里。
- 那么類A就是基類,也叫做父類;類B就是派生類,也叫做子類。
單繼承
- 繼承的格式
class 子類名(父類名):
pass
當發(fā)生繼承后,子類會繼承父類中的屬性和方法,可以直接 使用
在子類中不能直接使用父類中的私有方法;通過繼承得到的父類的公有方法,間接 執(zhí)行父類的私有方法
因為子類提供了 init 方法后,那么在使用子類實例對象時,就會調(diào)用 子類自己 init 方法;如果想父類中的屬性可以得到,需要執(zhí)行父類中的init方法
格式:父類名.__init__(self,父類中需要屬性參數(shù)列表)
多層繼承
子類繼承父類,父類繼承爺爺類,這就是多層繼承
多繼承
子類繼承多個父類
如果子類和父類的方法名和屬性名相同,則默認使用子類的
注意:如果多個父類中有同名的 屬性和方法,則默認使用第一個父類的屬性和方法(根據(jù)類的魔法屬性mro的順序來查找)
多繼承的初始化
在多繼承時,如果繼承的多個類同時繼承同一個父類,那么這時會出現(xiàn)初始化問題,這個共同父類會被初始化多次.
-
super()執(zhí)行過程:
在 self 這個對象的所屬類中,通過 mro 找到方法解析順序
在順序中,找當前類名的下一個類來初始化或查找方法類名.__mro__得到了一個元組,元組中的元素是當前類在繼承關系上的一個順序;這個順序不是我們確定的,是由在確定某個類的繼承關系關系后,由解釋器來確定這個順序
-
多繼承調(diào)用指定父類中方法
父類名.方法() super().方法() # 方法2. super() 帶參數(shù)版本,只支持新式類 super(Prentice, self).__init__() # 執(zhí)行父類的 __init__方法 # super(Prentice, self).make_cake() # self.make_cake() # 方法3. super()的簡化版,只支持新式類 super().__init__() # 執(zhí)行父類的 __init__方法 super().make_cake() # 執(zhí)行父類的 實例方法 self.make_cake() # 執(zhí)行本類的實例方法
Mixin
在設計類的繼承關系時,通常,主線都是單一繼承下來的,但是,如果需要“混入”額外的功能,通過多重繼承就可以實現(xiàn)。這種設計通常稱之為MixIn。
MixIn的目的就是給一個類增加多個功能,這樣,在設計類的時候,我們優(yōu)先考慮通過多重繼承來組合多個MixIn的功能,而不是設計多層次的復雜的繼承關系。
比如,編寫一個多進程模式的TCP服務,定義如下:
class MyTCPServer(TCPServer, ForkingMixIn):
pass
多態(tài)
在需要使用父類對象的地方,也可以使用子類對象, 這種情況就叫多態(tài).
比如, 在函數(shù)中,我需要調(diào)用 某一個父類對象的方法, 那么我們也可以在這個地方調(diào)用子類對象的方法.
類屬性
類屬性就是
類對象所擁有的屬性,它被所有類對象的實例對象所共有,類對象和實例對象均可訪問,在內(nèi)存中只存在一個副本類屬性可以使用實例對象來引用,但是不能修改
一般情況下:類屬性 都只使用 類對象 來調(diào)用class People(object): name = 'Tom' # 公有的類屬性 __age = 12 # 私有的類屬性 p = People() print(p.name) # 正確 print(People.name) # 正確 print(p.__age) # 錯誤,不能在類外通過實例對象訪問私有的類屬性 print(People.__age) # 錯誤,不能在類外通過類對象訪問私有的類屬性
實例屬性(對象屬性)
由于Python是動態(tài)語言,根據(jù)類創(chuàng)建的實例可以任意綁定屬性。
給實例綁定屬性的方法是通過實例變量,或者通過self變量
class Student(object):
def __init__(self, name):
self.name = name
s = Student('Bob')
s.score = 90
- 以 對象名.xxx 的形式調(diào)用的都是實例的屬性或?qū)嵗姆椒?
- 實例屬性和實例方法只能由實例對象調(diào)用
類方法
是類對象所擁有的方法,需要用修飾器@classmethod來標識其為類方法,對于類方法,第一個參數(shù)必須是類對象,一般以cls作為第一個參數(shù)
定義格式:
@classmethod
def 方法名(cls,...):
pass
調(diào)用格式:
類對象.類方法名
注意:在類方法中,不能使用self,但是可以使用 cls,該參數(shù)用來表示 當前類對象,這個參數(shù)也是自動傳遞的
@classmethod 是一個裝飾 器,用來修飾一個方法成為類方法,當在執(zhí)行該 類方法時,解釋 會自動 將類對象傳遞到參數(shù) cls中
-
類方法還有一個用途就是可以對類屬性進行修改
class People(object): country = 'china' #類方法,用classmethod來進行修飾 @classmethod def get_country(cls): return cls.country @classmethod def set_country(cls,country): cls.country = country
靜態(tài)方法
通過修飾器@staticmethod來進行修飾,靜態(tài)方法不需要多定義參數(shù),可以通過對象和類來訪問
格式:
@staticmethod
def 方法名(參數(shù)列表....):
pass
調(diào)用方式:
同類方法
類對象.靜態(tài)方法名()
- 靜態(tài)方法中不需要額外定義參數(shù),因此在靜態(tài)方法中引用類屬性的話,必須通過類實例對象來引用
使用@property
property屬性就是負責把一個方法當做屬性進行使用,這樣做可以簡化代碼使用。
定義property屬性有兩種方式
- 裝飾器方式
- 類屬性方式
裝飾器方式
class Person(object):
def __init__(self):
self.__age = 0
# 裝飾器方式的property, 把age方法當做屬性使用, 表示當獲取屬性時會執(zhí)行下面修飾的方法
@property
def age(self):
return self.__age
# 把age方法當做屬性使用, 表示當設置屬性時會執(zhí)行下面修飾的方法
@age.setter
def age(self, new_age):
if new_age >= 150:
print("成精了")
else:
self.__age = new_age
# 創(chuàng)建person
p = Person()
print(p.age)
p.age = 100
print(p.age)
p.age = 1000
>>>
0
100
成精了
@property的實現(xiàn)比較復雜,我們先考察如何使用。把一個getter方法變成屬性,只需要加上@property就可以了,此時,@property本身又創(chuàng)建了另一個裝飾器@age.setter,負責把一個setter方法變成屬性賦值
- @property 表示把方法當做屬性使用, 表示當獲取屬性時會執(zhí)行下面修飾的方法
- @方法名.setter 表示把方法當做屬性使用,表示當設置屬性時會執(zhí)行下面修飾的方法
- 裝飾器方式的property屬性修飾的方法名一定要一樣。
類屬性方式
class Person(object):
def __init__(self):
self.__age = 0
def get_age(self):
"""當獲取age屬性的時候會執(zhí)行該方法"""
return self.__age
def set_age(self, new_age):
"""當設置age屬性的時候會執(zhí)行該方法"""
if new_age >= 150:
print("成精了")
else:
self.__age = new_age
# 類屬性方式的property屬性
age = property(get_age, set_age)
# 創(chuàng)建person
p = Person()
print(p.age)
p.age = 100
print(p.age)
p.age = 1000
>>>
0
100
成精了
property的參數(shù)說明:
- 第一個參數(shù)是獲取屬性時要執(zhí)行的方法
- 第二個參數(shù)是設置屬性時要執(zhí)行的方法
小結
# 數(shù)據(jù) (屬性)
# 區(qū)別: 類的每個對象(實例) 對于這個數(shù)據(jù)是獨有的還是共享的,是不同的還是相同的
# 1. 對象屬性 (實例屬性)
# 類的每個對象 這個名字的屬性數(shù)據(jù) 是獨有的 ,每個對象不同
# 2. 類屬性
# 類的每個對象 這個名字的屬性數(shù)據(jù) 是共享,每個對象都相同
# 函數(shù)(方法)
# 區(qū)別:方法中能夠直接使用的屬性數(shù)據(jù)不同
# 1. 對象方法(實例方法) 可以直接讀寫對象屬性,可以直接讀類屬性
# def obj_func(self):
# self.
# 2. 類方法
# @classmethod 可以直接讀寫類屬性
# def class_func(cls, ..):
# cls.
# 3. 靜態(tài)方法
# @staticmethod 雖然可以通過類名操作類屬性,但是我們可以認為 不是直接操作屬性
# def static_func():
# 類名.
# 選擇:如果定義一個函數(shù),這個函數(shù)中需要使用對象屬性,定義對象方法
# 如果定義一個函數(shù),這個函數(shù)中僅需要使用類屬性,定義類方法
# 如果定義一個函數(shù),這個函數(shù)不需要使用類的任何屬性,從邏輯的角度考慮 應該是類中的一個處理方法,此時定義靜態(tài)方法即可
總結
面向?qū)ο笈c面向過程
面向過程的程序設計把計算機程序視為一系列的命令集合,即一組函數(shù)的順序執(zhí)行。為了簡化程序設計,面向過程把函數(shù)繼續(xù)切分為子函數(shù),即把大塊函數(shù)通過切割成小塊函數(shù)來降低系統(tǒng)的復雜度。
而面向?qū)ο蟮某绦蛟O計把計算機程序視為一組對象的集合,而每個對象都可以接收其他對象發(fā)過來的消息,并處理這些消息,計算機程序的執(zhí)行就是一系列消息在各個對象之間傳遞。
- 對于面向過程的思想: 需要實現(xiàn)一個功能的時候,看重的是開發(fā)的步驟和過程,每一個步驟都需要自己親力親為,需要自己編寫代碼(自己來做)。面向過程語言是一種基于功能分析的、以算法為中心的程序設計方法;
- 對于面向?qū)ο?/strong>的思想:把一切都看成對象,而對象一般都由屬性+方法組成;當需要實現(xiàn)一個功能的時候,看重的并不是過程和步驟,而是關心誰幫我做這件事(偷懶,找人幫我做)。而面向?qū)ο笫且环N基于結構分析的、以數(shù)據(jù)為中心的程序設計思想。三大特征有:封裝性、繼承性、多態(tài)性。
- 面向過程(手洗):脫衣服、找一個盆、加水、加洗衣粉、浸泡30分鐘、搓洗、擰衣服、倒掉水、再加水、漂洗、擰衣服、倒掉水、晾衣服。
- 面向?qū)ο螅C洗):脫衣服、放入洗衣機、按下開關、拿出衣服晾曬。
高級
I/O編程
文件讀寫
打開文件
- 以文本方式打開方式的模式
r -> read text; file = open('a.txt','r')
以文本方式打開文件讀,文件存在,打開成功,文件不存在,打開失敗
w -> write text; file = open('a.txt','w')
以文本方式打開文件寫,不管文件是否存在,都會新創(chuàng)建一個新文件
a -> append text ;file = open('a.txt','w')
以文件方式打開文件追加,文件不存在,創(chuàng)建文件,文件存在,那么打開文件然后將光標移動到文件的最后
- 以二進制形式打開文件的模式
rb 以二進制形式打開文件讀取
wb 以二進制形式打開文件寫入
ab 以二進制形式打開文件追加
-
with語句
- Python提供了 with 語句的這種寫法,既簡單又安全,并且 with 語句執(zhí)行完成以后自動調(diào)用關閉文件操作,即使出現(xiàn)異常也會自動調(diào)用關閉文件操作**。
# 1、以寫的方式打開文件 with open("1.txt", "w") as f: # 2、讀取文件內(nèi)容 f.write("hello world")
讀數(shù)據(jù)
- 默認讀取全部文件內(nèi)容;只適用于文件比較小的情況
file = open('a.txt','rt')
content = file.read()
file.close()
- 讀取多行文件的方式
while True:
# 讀取
content = file.read(4096) #從文件中讀取的數(shù)據(jù)的長度(單位是字節(jié))
# 如果在文件讀取時,讀取的結果為空串,說明文件讀取完畢
# 根據(jù)這個條件 可以設置讀取文件的結束條件
if content == '':
break
print(content,end='')
# 關閉文件
file.close()
- readline()以行讀取
- readlines()以行的方式 讀取整 個文件,并且返回的是一個列表,其中每一行的數(shù)據(jù)為一個元素
寫數(shù)據(jù)
-
write()可以完成向文件寫入數(shù)據(jù)
f = open('test.txt', 'w') f.write('hello world, i am here!') f.close() 如果文件不存在那么創(chuàng)建
操作文件和目錄
導入 os 模塊
-
os.rename(需要修改的文件名, 新的文件名)
import os os.rename("畢業(yè)論文.txt", "畢業(yè)論文-最終版.txt") os.remove(待刪除的文件名)
os.mkdir("張三")創(chuàng)建文件夾;如果當前目錄 存在,會報錯
os.getcwd()獲取當前目錄
os.chdir("../指定路徑")改變當前目錄 到指定 的路徑 上去
-
os.listdir("./")獲取目錄下的文件名稱,存在列表中
file_list = os.listdir('.') print(file_list) for file in file_list: print(file) os.rmdir('路徑') 刪除一個空文件夾,當目錄文件夾不為空,不能刪除
模塊
模塊是一組Python代碼的集合,可以使用其他模塊,也可以被其他模塊使用。
使用模塊
import
在Python中用關鍵字import來引入某個模塊,比如要引用模塊math,就可以在文件最開始的地方用import math來引入
在調(diào)用math模塊中的函數(shù)時,必須這樣引用:
模塊名.函數(shù)名
from…import
Python的from語句讓你從模塊中導入一個指定的部分到當前命名空間中,此時可以用下面方法實現(xiàn):
from 模塊名 import 函數(shù)名1,函數(shù)名2....
- 通過這種方式引入的時候,調(diào)用函數(shù)時只能給出函數(shù)名,不能給出模塊名,但是當兩個模塊中含有相同名稱函數(shù)的時候,后面一次引入會覆蓋前一次引入
- 如果想一次性引入math中所有的東西,還可以通過from math import *來實現(xiàn)
- 用as給引入起別名
- 在模塊中的__all__變量就是為了限制或者指定能被導入到別的模塊的函數(shù),如果指定了那么只能是指定的那些可以被導入,沒有指定默認就是全部可以導入,當然私有屬性應該除外。
制作模塊
編寫一個hello的模塊:
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
' a test module '
__author__ = 'Michael Liao'
import sys
def test():
args = sys.argv
if len(args)==1:
print('Hello, world!')
elif len(args)==2:
print('Hello, %s!' % args[1])
else:
print('Too many arguments!')
if __name__=='__main__':
test()
第1行和第2行是標準注釋,第1行注釋可以讓這個hello.py文件直接在Unix/Linux/Mac上運行,第2行注釋表示.py文件本身使用標準UTF-8編碼;
第4行是一個字符串,表示模塊的文檔注釋,任何模塊代碼的第一個字符串都被視為模塊的文檔注釋;
第6行使用__author__變量把作者寫進去,這樣當你公開源代碼后別人就可以瞻仰你的大名;
以上就是Python模塊的標準文件模板,當然也可以全部刪掉不寫,但是,按標準辦事肯定沒錯。
定位模塊
當你導入一個模塊,Python解析器對模塊位置的搜索順序是:
- 當前目錄
- 如果不在當前目錄,Python則搜索在shell變量PYTHONPATH下的每個目錄。
- 如果都找不到,Python會察看默認路徑。UNIX下,默認路徑一般為/usr/local/lib/python/
- 模塊搜索路徑存儲在system模塊的sys.path變量中。變量里包含當前目錄,PYTHONPATH和由安裝過程決定的默認目錄。
安裝第三方模塊
在Python中,安裝第三方模塊,是通過包管理工具pip完成的。
第三方庫都會在Python官方的pypi.python.org網(wǎng)站注冊,要安裝一個第三方庫,必須先知道該庫的名稱,可以在官網(wǎng)或者pypi上搜索
異常
當Python檢測到一個錯誤時,解釋器就無法繼續(xù)執(zhí)行了,反而出現(xiàn)了一些錯誤的提示,這就是所謂的"異常"
格式
try:
可能會出現(xiàn)異常問題的代碼
except Exception as e:
當出現(xiàn)異常時,解決異常的代碼
else:
當沒有出現(xiàn)異常時,正常執(zhí)行的代碼
finally:
無論是否出現(xiàn)異常,都會執(zhí)行這里的代碼
自定義異常
格式:
class 異常名Error(Exception):
def __init__(self,msg=''):
self.__msg = msg
def __str__(self):
return self.__msg
class CustomError(Exception):
pass
調(diào)試與測試
斷言assert
斷言就是判斷一個函數(shù)或?qū)ο蟮囊粋€方法所產(chǎn)生的結果是否符合你期望的那個結果。 python中assert斷言是聲明布爾值為真的判定,如果表達式為假會發(fā)生異常。單元測試中,一般使用assert來斷言結果。
def foo(s):
n = int(s)
assert n != 0, 'n is zero!' # 表達式為假,逗號后為自定義異常;也可不定義
return 10 / n
def main():
foo('0')
如果斷言失敗,assert語句本身就會拋出AssertionError:
程序中如果到處充斥著assert,和print()相比也好不到哪去。不過,啟動Python解釋器時可以用-O(是字母O)參數(shù)來關閉assert,關閉后,你可以把所有的assert語句當成pass來看。:
$ python -O err.py
常用的斷言方法:
常用的斷言方法:
assertEqual 如果兩個值相等,則pass
assertNotEqual 如果兩個值不相等,則pass
assertTrue 判斷bool值為True,則pass
assertFalse 判斷bool值為False,則pass
assertIsNone 不存在,則pass
assertIsNotNone 存在,則pass
logging
logging模塊是Python內(nèi)置的標準模塊,主要用于輸出運行日志,可以設置輸出日志的等級、日志保存路徑、日志文件回滾等;相比print,具備如下優(yōu)點:
- 可以通過設置不同的日志等級,默認是warning級別,在release版本中只輸出重要信息,而不必顯示大量的調(diào)試信息;
- print將所有信息都輸出到標準輸出中,嚴重影響開發(fā)者從標準輸出中查看其它數(shù)據(jù);logging則可以由開發(fā)者決定將信息輸出到什么地方,以及怎么輸出;
import logging
logging.basicConfig(level=logging.INFO)
s = '0'
n = int(s)
logging.info('n = %d' % n)
print(10 / n)
-
記錄信息的級別
- debug : 打印全部的日志,詳細的信息,通常只出現(xiàn)在診斷問題上
- info : 打印info,warning,error,critical級別的日志,確認一切按預期運行
- warning : 打印warning,error,critical級別的日志,一個跡象表明,一些意想不到的事情發(fā)生了,或表明一些問題在不久的將來(例如。磁盤空間低”),這個軟件還能按預期工作
- error : 打印error,critical級別的日志,更嚴重的問題,軟件沒能執(zhí)行一些功能
- critical : 打印critical級別,一個嚴重的錯誤,這表明程序本身可能無法繼續(xù)運行
-
logging.basicConfig函數(shù)各參數(shù):
filename:指定日志文件名;
filemode:和file函數(shù)意義相同,指定日志文件的打開模式,'w'或者'a';
format:指定輸出的格式和內(nèi)容,format可以輸出很多有用的信息,
參數(shù):作用 %(levelno)s:打印日志級別的數(shù)值 %(levelname)s:打印日志級別的名稱 %(pathname)s:打印當前執(zhí)行程序的路徑,其實就是sys.argv[0] %(filename)s:打印當前執(zhí)行程序名 %(funcName)s:打印日志的當前函數(shù) %(lineno)d:打印日志的當前行號 %(asctime)s:打印日志的時間 %(thread)d:打印線程ID %(threadName)s:打印線程名稱 %(process)d:打印進程ID %(message)s:打印日志信息
-
輸出日志
import logging # 引入logging模塊 import os.path import time # 第一步,創(chuàng)建一個logger logger = logging.getLogger() logger.setLevel(logging.INFO) # Log等級總開關 # 第二步,創(chuàng)建一個handler,用于寫入日志文件 rq = time.strftime('%Y%m%d%H%M', time.localtime(time.time())) log_path = os.path.dirname(os.getcwd()) + '/Logs/' log_name = log_path + rq + '.log' logfile = log_name fh = logging.FileHandler(logfile, mode='w') fh.setLevel(logging.DEBUG) # 輸出到file的log等級的開關 # 第三步,定義handler的輸出格式 formatter = logging.Formatter("%(asctime)s - %(filename)s[line:%(lineno)d] - %(levelname)s: %(message)s") fh.setFormatter(formatter) # 第四步,將logger添加到handler里面 logger.addHandler(fh) # 日志 logger.debug('this is a logger debug message') logger.info('this is a logger info message') logger.warning('this is a logger warning message') logger.error('this is a logger error message') logger.critical('this is a logger critical message')
單元測試
單元測試是用來對一個模塊、一個函數(shù)或者一個類來進行正確性檢驗的測試工作。
import unittest
class TestClass(unittest.TestCase):
#該方法會首先執(zhí)行,相當于做測試前的準備工作
def setUp(self):
pass
#該方法會在測試代碼執(zhí)行完后執(zhí)行,相當于做測試后的掃尾工作
def tearDown(self):
pass
#測試代碼
def test_app_exists(self):
pass
-
運行單元測試
-
一旦編寫好單元測試,我們就可以運行單元測試。最簡單的運行方式是在
mydict_test.py的最后加上兩行代碼:if __name__ == '__main__': unittest.main()這樣就可以把
mydict_test.py當做正常的python腳本運行:$ python mydict_test.py -
另一種方法是在命令行通過參數(shù)
-m unittest直接運行單元測試,這是推薦的做法:$ python -m unittest mydict_test ..... ---------------------------------------------------------------------- Ran 5 tests in 0.000s OK
-