Python基礎(chǔ)教程

Python基礎(chǔ)

介紹

輸入和輸出

  • 所有的通過(guò)input獲取的數(shù)據(jù),都是字符串類(lèi)型
  • print()

變量

  • 程序就是用來(lái)處理數(shù)據(jù)的,而變量就是用來(lái)存儲(chǔ)數(shù)據(jù)的
  • 變量在程序中就是用一個(gè)變量名表示了,變量名必須是大小寫(xiě)英文、數(shù)字和_的組合,且不能用數(shù)字開(kāi)頭
  • 可以使用type(變量的名字),來(lái)查看變量的類(lèi)型
  • 在Python中,等號(hào)=是賦值語(yǔ)句,可以把任意數(shù)據(jù)類(lèi)型賦值給變量,同一個(gè)變量可以反復(fù)賦值,而且可以是不同類(lèi)型的變量
    • 這種變量本身類(lèi)型不固定的語(yǔ)言稱(chēng)之為動(dòng)態(tài)語(yǔ)言,與之對(duì)應(yīng)的是靜態(tài)語(yǔ)言。靜態(tài)語(yǔ)言在定義變量時(shí)必須指定變量類(lèi)型,如果賦值的時(shí)候類(lèi)型不匹配,就會(huì)報(bào)錯(cuò)。

常量

Python中,通常用全部大寫(xiě)的變量名表示常量

  • 比如常用的數(shù)學(xué)常數(shù)π就是一個(gè)常量,但事實(shí)上PI仍然是一個(gè)變量,Python根本沒(méi)有任何機(jī)制保證PI不會(huì)被改變,所以,用全部大寫(xiě)的變量名表示常量只是一個(gè)習(xí)慣上的用法

數(shù)據(jù)類(lèi)型

可變對(duì)象與不可變對(duì)象

  • 可變對(duì)象
    • 列表list,字典dict,集合set
    • 特點(diǎn): 這個(gè)些數(shù)據(jù)類(lèi)型,是可以直接在原對(duì)象上進(jìn)行修改數(shù)據(jù),修改完成后,并不影響原對(duì)象地址
  • 不可變對(duì)象
    • 數(shù)字 int,字符串 str,浮點(diǎn)數(shù) float,布爾類(lèi)型 bool,元組 tuple
    • 特點(diǎn): 這些數(shù)據(jù)都是不可以直接修改的,如果在修改或賦值時(shí),都會(huì)開(kāi)辟一個(gè)新空間
    • 對(duì)于不變對(duì)象來(lái)說(shuō),調(diào)用對(duì)象自身的任意方法,也不會(huì)改變?cè)搶?duì)象自身的內(nèi)容。相反,這些方法會(huì)創(chuàng)建新的對(duì)象并返回,這樣,就保證了不可變對(duì)象本身永遠(yuǎn)是不可變的。

整數(shù)int

浮點(diǎn)數(shù)float

  • 浮點(diǎn)數(shù)也就是小數(shù),之所以稱(chēng)為浮點(diǎn)數(shù),是因?yàn)榘凑湛茖W(xué)記數(shù)法表示時(shí),一個(gè)浮點(diǎn)數(shù)的小數(shù)點(diǎn)位置是可變的
  • 整數(shù)和浮點(diǎn)數(shù)在計(jì)算機(jī)內(nèi)部存儲(chǔ)的方式是不同的,整數(shù)運(yùn)算永遠(yuǎn)是精確的(除法難道也是精確的?是的?。?,而浮點(diǎn)數(shù)運(yùn)算則可能會(huì)有四舍五入的誤差。

字符串string

  • 字符串是以單引號(hào)'或雙引號(hào)"括起來(lái)的任意文本

  • 如果字符串內(nèi)部既包含'又包含"怎么辦?可以用轉(zhuǎn)義字符\來(lái)標(biāo)識(shí)

    • 轉(zhuǎn)義字符\可以轉(zhuǎn)義很多字符,比如\n表示換行,\t表示制表符,字符\本身也要轉(zhuǎn)義,所以\\表示的字符就是\
    • Python還允許用r''表示''內(nèi)部的字符串默認(rèn)不轉(zhuǎn)義

字符串與編碼

Unicode把所有語(yǔ)言都統(tǒng)一到一套編碼里,這樣就不會(huì)再有亂碼問(wèn)題了。

ASCII編碼是1個(gè)字節(jié),而Unicode編碼通常是2個(gè)字節(jié)。

UTF-8編碼把一個(gè)Unicode字符根據(jù)不同的數(shù)字大小編碼成1-6個(gè)字節(jié),常用的英文字母被編碼成1個(gè)字節(jié),漢字通常是3個(gè)字節(jié),只有很生僻的字符才會(huì)被編碼成4-6個(gè)字節(jié)。如果你要傳輸?shù)奈谋景罅坑⑽淖址?,用UTF-8編碼就能節(jié)省空間

在計(jì)算機(jī)內(nèi)存中,統(tǒng)一使用Unicode編碼,當(dāng)需要保存到硬盤(pán)或者需要傳輸?shù)臅r(shí)候,就轉(zhuǎn)換為UTF-8編碼。

最新的Python 3版本中,字符串是以Unicode編碼的,也就是說(shuō),Python的字符串支持多語(yǔ)言

  • Python提供了ord()函數(shù)獲取字符的整數(shù)表示,
  • chr()函數(shù)把編碼轉(zhuǎn)換為對(duì)應(yīng)的字符
>>> ord('A')
65
>>> ord('中')
20013
  • Python對(duì)bytes類(lèi)型的數(shù)據(jù)用帶b前綴的單引號(hào)或雙引號(hào)表示:

    x = b'ABC'
    
  • 純英文的str可以用ASCII編碼為bytes,內(nèi)容是一樣的,含有中文的str可以用UTF-8編碼為bytes,用encode()方法

  • 讀到的數(shù)據(jù)就是bytes。要把bytes變?yōu)?code>str,就需要用decode()方法

列表,字典,集合

布爾值

  • 布爾值只有TrueFalse兩種值

空值None

空值是Python里一個(gè)特殊的值,用None表示。None不能理解為0,因?yàn)?code>0是有意義的,而None是一個(gè)特殊的空值。

數(shù)據(jù)類(lèi)型的轉(zhuǎn)換

函數(shù) 說(shuō)明
int(x [,base ]) 將x轉(zhuǎn)換為一個(gè)整數(shù)
float(x ) 將x轉(zhuǎn)換為一個(gè)浮點(diǎn)數(shù)
complex(real [,imag ]) 創(chuàng)建一個(gè)復(fù)數(shù),real為實(shí)部,imag為虛部
str(x ) 將對(duì)象 x 轉(zhuǎn)換為字符串
repr(x ) 將對(duì)象 x 轉(zhuǎn)換為表達(dá)式字符串
eval(str ) 用來(lái)計(jì)算在字符串中的有效Python表達(dá)式,并返回一個(gè)對(duì)象
tuple(s ) 將序列 s 轉(zhuǎn)換為一個(gè)元組
list(s ) 將序列 s 轉(zhuǎn)換為一個(gè)列表
chr(x ) 將一個(gè)整數(shù)轉(zhuǎn)換為一個(gè)Unicode字符
ord(x ) 將一個(gè)字符轉(zhuǎn)換為它的ASCII整數(shù)值
hex(x ) 將一個(gè)整數(shù)轉(zhuǎn)換為一個(gè)十六進(jìn)制字符串
oct(x ) 將一個(gè)整數(shù)轉(zhuǎn)換為一個(gè)八進(jìn)制字符串
bin(x ) 將一個(gè)整數(shù)轉(zhuǎn)換為一個(gè)二進(jìn)制字符串

運(yùn)算符

運(yùn)算符 描述 實(shí)例
+ 兩個(gè)對(duì)象相加 a + b 輸出結(jié)果 30
- 得到負(fù)數(shù)或是一個(gè)數(shù)減去另一個(gè)數(shù) a - b 輸出結(jié)果 -10
* 兩個(gè)數(shù)相乘或是返回一個(gè)被重復(fù)若干次的字符串 a * b 輸出結(jié)果 200
/ b / a 輸出結(jié)果 2
// 取整除 返回商的整數(shù)部分 9//2 輸出結(jié)果 4 , 9.0//2.0 輸出結(jié)果 4.0
% 取余 返回除法的余數(shù) b % a 輸出結(jié)果 0
** 指數(shù) a**b 為10的20次方, 輸出結(jié)果 100000000000000000000
  • 復(fù)合賦值運(yùn)算符
運(yùn)算符 描述 實(shí)例
+= 加法賦值運(yùn)算符 c += a 等效于 c = c + a
-= 減法賦值運(yùn)算符 c -= a 等效于 c = c - a
*= 乘法賦值運(yùn)算符 c *= a 等效于 c = c * a
/= 除法賦值運(yùn)算符 c /= a 等效于 c = c / a
%= 取模賦值運(yùn)算符 c %= a 等效于 c = c % a
**= 冪賦值運(yùn)算符 c **= a 等效于 c = c ** a
//= 取整除賦值運(yùn)算符 c //= a 等效于 c = c // a

字符串string

字符串輸出

格式化操作符

格式符號(hào) 轉(zhuǎn)換
%c 字符
%s 字符串
%d 有符號(hào)十進(jìn)制整數(shù)
%u 無(wú)符號(hào)十進(jìn)制整數(shù)
%o 八進(jìn)制整數(shù)
%x 十六進(jìn)制整數(shù)(小寫(xiě)字母0x)
%X 十六進(jìn)制整數(shù)(大寫(xiě)字母0X)
%f 浮點(diǎn)數(shù)
%e 科學(xué)計(jì)數(shù)法(小寫(xiě)'e')
%E 科學(xué)計(jì)數(shù)法(大寫(xiě)“E”)
%g %f和%e 的簡(jiǎn)寫(xiě)
%G %f和%E的簡(jiǎn)寫(xiě)
age = 18
name = "xiaohua"
print("我的姓名是%s, 年齡是%d" % (name, age))

f-strings

f-strings 以字母 'f' 或 'F' 為前綴, 格式化字符串使用一對(duì)單引號(hào)、雙引號(hào)、三單引號(hào)、三雙引號(hào)

name = '峰哥'
age = 33
format_string1 = f'我的名字是 {name}, 我的年齡是 {age}'

format()

使用字符串的format()方法,它會(huì)用傳入的參數(shù)依次替換字符串內(nèi)的占位符{0}、{1}……

>>> 'Hello, {0}, 成績(jī)提升了 {1:.1f}%'.format('小明', 17.125)
'Hello, 小明, 成績(jī)提升了 17.1%'

常見(jiàn)操作

如有字符串mystr = 'hello world itcast and itcastcpp'

find & rfind

檢測(cè) str 是否包含在 mystr中,如果是返回開(kāi)始的索引值,否則返回-1

mystr.find(str, start=0, end=len(mystr))
mystr.find('it', 0, 10)

index & rindex

跟find()方法一樣,只不過(guò)如果str不在 mystr中會(huì)報(bào)一個(gè)異常.

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 指定,則替換不超過(guò) count 次.

mystr.replace(str1, str2, mystr.count(str1))

capitalize

把字符串的第一個(gè)字符大寫(xiě)

mystr.capitalize()

title

把字符串的每個(gè)單詞首字母大寫(xiě)

>>> a = "hello itcast"
>>> a.title()
'Hello Itcast'

lower

轉(zhuǎn)換 mystr 中所有大寫(xiě)字符為小寫(xiě)

mystr.lower()        

upper

轉(zhuǎn)換 mystr 中的小寫(xiě)字母為大寫(xiě)

mystr.upper()    

startswith

檢查字符串是否是以 "hello" 開(kāi)頭, 是則返回 True,否則返回 False

mystr.startswith("hello")

endswith

檢查字符串是否以"world"結(jié)束,如果是返回True,否則返回 False.

mystr.endswith("world")

ljust & rjust

返回一個(gè)原字符串左對(duì)齊,并使用空格填充至長(zhǎng)度 width 的新字符串

mystr.ljust(width) 

center

返回一個(gè)原字符串居中,并使用空格填充至長(zhǎng)度 width 的新字符串

mystr.center(width)

lstrip & rstrip & strip

刪除 mystr 左邊/右邊/兩邊的空白字符或指定字符

mystr.lstrip()
mystr.lstrip(“#”)

split

以 str 為分隔符切片 mystr,如果 maxsplit有指定值,則僅分隔 maxsplit 個(gè)子字符串

mystr.split(" ", 2)

splitlines

按照行分隔,返回一個(gè)包含各行作為元素的列表

mystr.splitlines()  

partition & rpartition

把mystr以str分割成三部分,str前,str和str后

mystr.partition(str)

join

alist 中每個(gè)元素用str連接,構(gòu)造出一個(gè)新的字符串

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ù)類(lèi)型是列表:list。list是一種有序的集合,可以隨時(shí)添加和刪除其中的元素。

相關(guān)操作

添加元素

  • append(),向列表末尾添加元素
  • extend(),可以將另一個(gè)列表中的元素逐一添加到列表中
  • insert(index, item),在指定位置index前插入元素object

修改元素

  • li[i] = x,把索引為i的元素修改為x

查找元素

所謂的查找,就是看看指定的元素是否存在

python中查找的常用方法為:

  • in(存在),如果存在那么結(jié)果為true,否則為false
  • not in(不存在),如果不存在那么結(jié)果為true,否則false

index查找(與字符串查找相同)

>>> a = ['a', 'b', 'c', 'a', 'b']
>>> a.index('a', 1, 3) # 注意是左閉右開(kāi)區(qū)間

刪除元素

  • del:根據(jù)下標(biāo)進(jìn)行刪除

    del li[i] # 刪除列表li中索引為i元素
    
  • pop():按索引刪除一個(gè)元素,刪除時(shí)會(huì)返回被刪除的元素

    li.pop() # 刪除最后一個(gè)元素
    li.pop(i) # 刪除列表li中索引為i元素
    
  • remove:根據(jù)元素的值進(jìn)行刪除,刪除第一個(gè)符合條件的值

    >>> str=[1,2,3,4,5,2,6]
    >>> str.remove(2)
    >>> str
    [1, 3, 4, 5, 2, 6]
    

個(gè)數(shù)len()

len()函數(shù)可以獲得list元素的個(gè)數(shù)

排序sort()

sort方法是將list按特定順序重新排列,默認(rèn)為由小到大,參數(shù)reverse=True可改為倒序,由大到小。

遍歷

  • 使用for循環(huán)

    • 通過(guò)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非常類(lèi)似,但是tuple一旦初始化就不能修改,元組使用小括號(hào),列表使用方括號(hào)。
  • 因?yàn)閠uple不可變,所以代碼更安全。如果可能,能用tuple代替list就盡量用tuple。
  • 元組只有一個(gè)元素t = (1,)

小結(jié)

  • 元組修改元素

    • 將元組轉(zhuǎn)換為列表并更改值

    • 通過(guò)現(xiàn)有字符串的片段在構(gòu)造一個(gè)新的字符串的方式來(lái)等同于更新元組操作

      tuple_1=(1,2,3,"ewang","demo")
      #通過(guò)索引更新
      tuple_1=tuple_1[0],tuple_1[2],tuple_1[4]
      print tuple_1
      #通過(guò)切片更新
      tuple_1=tuple_1[0:2]
      print tuple_1
      # 添加元素
      tuple_1 = tuple_1 + (4,)
      
  • 列表和元組區(qū)別

    • 列表是可變類(lèi)型,元組是不可變類(lèi)型,可變類(lèi)型值,值發(fā)生變化地址不變;不可變類(lèi)型指,值發(fā)生改變,地址也發(fā)生改變,值指向一個(gè)新的值

切片

切片是指對(duì)操作的對(duì)象截取其中一部分的操作。

字符串、列表、元組都支持切片操作。

語(yǔ)法

  • [起始:結(jié)束:步長(zhǎng)]
  • 注意:選取的區(qū)間從"起始"位開(kāi)始,到"結(jié)束"位的前一位結(jié)束(不包含結(jié)束位本身)(左閉右開(kāi)),步長(zhǎng)表示選取間隔。

使用

s = "abcdefghijk"
print(s[0:5:1])
print(s[0:5:2])
print(s[3:6])   # 默認(rèn)步長(zhǎng)可以不寫(xiě),默認(rèn)為1
print(s[:5])    # 開(kāi)始索引也可以不寫(xiě),默認(rèn)從頭開(kāi)始
print(s[5:])    # 結(jié)束也可以不寫(xiě),默認(rèn)到最后
print(s[:])     # 全默認(rèn),默認(rèn)截取整串
print(s)
print(s[10:20])     # 切片時(shí)不會(huì)出現(xiàn)下標(biāo)越界錯(cuò)誤


# 切片的下標(biāo)還可是以負(fù)數(shù)
# 負(fù)數(shù)是,是從右向左切片,起始下標(biāo)為 -1
print(s[-1:-5])
print(s[-1:-5:-1])

# 特殊需要記住的切片方式
# 使用切片實(shí)現(xiàn)字符串逆序
print(s[::-1])

字典dict

使用鍵-值(key-value)存儲(chǔ),具有極快的查找速度。

相關(guān)操作

查看元素

  • 通過(guò)鍵訪問(wèn)值

    di['key'] = value   # 若訪問(wèn)不存在的鍵,則會(huì)報(bào)錯(cuò)
    
  • 在我們不確定字典中是否存在某個(gè)鍵而又想獲取其值時(shí),可以使用get方法,還可以設(shè)置默認(rèn)值

    >>> age = info.get('age')
    >>> age #'age'鍵不存在,所以age為None
    >>> type(age)
    <type 'NoneType'>
    >>> age = info.get('age', 18) # 若info中不存在'age'這個(gè)鍵,就返回默認(rèn)值18
    >>> age
    18
    

修改元素

字典的每個(gè)元素中的數(shù)據(jù)是可以修改的,只要通過(guò)key找到,即可修改

di['a'] = x  # 把鍵為a的值修改為x

添加元素

在使用 變量名['鍵'] = 數(shù)據(jù) 時(shí),這個(gè)“鍵”在字典中,不存在,那么就會(huì)新增這個(gè)元素

di['newkey'] = newvalue

刪除元素

  • pop('key'):按鍵刪除一個(gè)元素,刪除時(shí)會(huì)返回被刪除元素的值

  • del di['key']:刪除指定鍵

  • del di:刪除整個(gè)字典對(duì)象

  • clear():清空整個(gè)字典內(nèi)容

    di.clear()
    >>>{}
    

個(gè)數(shù)len()

len(di)

keys()

返回一個(gè)包含字典所有KEY的列表

values()

返回一個(gè)包含字典所有value的列表

items()

返回一個(gè)包含所有(鍵,值)元祖的列表

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

遍歷項(xiàng)(元素)

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

小結(jié)

  • dict內(nèi)部存放的順序和key放入的順序是沒(méi)有關(guān)系的。

  • 和list比較,dict有以下幾個(gè)特點(diǎn):

    1. 查找和插入的速度極快,不會(huì)隨著key的增加而變慢;
    2. 需要占用大量的內(nèi)存,內(nèi)存浪費(fèi)多。

    而list相反:

    1. 查找和插入的時(shí)間隨著元素的增加而增加;
    2. 占用空間小,浪費(fèi)內(nèi)存很少。

    所以,dict是用空間來(lái)?yè)Q取時(shí)間的一種方法。

  • dict的key必須是不可變對(duì)象。

    • 這是因?yàn)閐ict根據(jù)key來(lái)計(jì)算value的存儲(chǔ)位置,如果每次計(jì)算相同的key得出的結(jié)果不同,那dict內(nèi)部就完全混亂了。這個(gè)通過(guò)key計(jì)算位置的算法稱(chēng)為哈希算法(Hash)。
    • 要保證hash的正確性,作為key的對(duì)象就不能變。字符串、整數(shù)等都是不可變的,因此,可以放心地作為key。而list是可變的,就不能作為key

集合set

set和dict類(lèi)似,也是一組key的集合,但不存儲(chǔ)value。由于key不能重復(fù),所以,在set中,沒(méi)有重復(fù)的key。

相關(guān)操作

添加元素

通過(guò)add(key)方法可以添加元素到set中,可以重復(fù)添加,但不會(huì)有效果

s.add(key)

刪除元素

通過(guò)remove(key)方法可以刪除元素

s.remove(key)

clear()清空元素

交集(&)

交集指的是兩個(gè)不同的集合中相同的集合打印出來(lái)

>>> a = set('abc')
>>> b = set('cdef')
>>> a & b
set(['c'])

并集(|)

將兩個(gè)集合中所有元素合并到一起

>>> a = set('abc')
>>> b = set('cdef')
>>> a | b
set(['a', 'c', 'b', 'e', 'd', 'f'])

差集(-)

差集指的是兩個(gè)沒(méi)有集合中不同的元素,前面的集合為準(zhǔn)

>>> a = set('abc')
>>> b = set('cdef')
>>> a - b 
set(['a', 'b'])

對(duì)稱(chēng)差集(^)

集合A與集合B中所有不屬于A∩B的元素的集合

>>> a = set('abc')
>>> b = set('cdef')
>>> a ^ b 
set(['a', 'b', 'd' 'e', 'd', 'f'])

[圖片上傳失敗...(image-90cfb9-1620306245275)]

判斷語(yǔ)句

循環(huán)語(yǔ)句

break和continue

  1. break的作用:立刻結(jié)束break**所在的循環(huán)
  2. continue的作用:用來(lái)結(jié)束本次循環(huán),緊接著執(zhí)行下一次的循環(huán)
  3. break/continue只能用在循環(huán)中,除此以外不能單獨(dú)使用
  4. break/continue在嵌套循環(huán)中,只對(duì)最近的一層循環(huán)起作用

高級(jí)特性

列表生成式

>>> [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']

# 把一個(gè)list中所有的字符串變成小寫(xiě)
>>> L = ['Hello', 'World', 'IBM', 'Apple']
>>> [s.lower() for s in L]
['hello', 'world', 'ibm', 'apple']

迭代與迭代器

迭代是訪問(wèn)集合元素的一種方式,如果給定一個(gè)list或tuple,我們可以通過(guò)for循環(huán)來(lái)遍歷這個(gè)list或tuple,這種遍歷我們稱(chēng)為迭代(Iteration)。

  • 可以直接作用于for循環(huán)的對(duì)象統(tǒng)稱(chēng)為可迭代對(duì)象:Iterable。

  • 可以被next()函數(shù)調(diào)用并不斷返回下一個(gè)值的對(duì)象稱(chēng)為迭代器:Iterator。它們表示一個(gè)惰性計(jì)算的序列

  • 生成器都是Iterator對(duì)象,但list、dict、str雖然是可迭代對(duì)象Iterable,卻不是迭代器Iterator

  • list、dict、strIterable變成Iterator可以使用iter()函數(shù)

  • 可以直接作用于for循環(huán)的數(shù)據(jù)類(lèi)型有以下幾種:

    一類(lèi)是集合數(shù)據(jù)類(lèi)型,如list、tupledict、set、str等;

    一類(lèi)是generator,包括生成器和帶yield的generator function。

    這些可以直接作用于for循環(huán)的對(duì)象統(tǒng)稱(chēng)為可迭代對(duì)象:Iterable。

  • 可以使用isinstance()判斷一個(gè)對(duì)象是否是Iterable對(duì)象

生成器

根據(jù)程序員制定的規(guī)則循環(huán)生成數(shù)據(jù),當(dāng)條件不成立時(shí)則生成數(shù)據(jù)結(jié)束。數(shù)據(jù)不是一次性全部生成出來(lái),而是使用一個(gè),再生成一個(gè),可以節(jié)約大量的內(nèi)存。

創(chuàng)建方式

  1. 生成器推導(dǎo)式

    • 只要把一個(gè)列表生成式的[]改成(),就創(chuàng)建了一個(gè)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),就計(jì)算出g的下一個(gè)元素的值,直到計(jì)算到最后一個(gè)元素,沒(méi)有更多的元素時(shí),拋出StopIteration的錯(cuò)誤。

    • 也可使用for循環(huán),因?yàn)間enerator也是可迭代對(duì)象;我們創(chuàng)建了一個(gè)generator后,基本上永遠(yuǎn)不會(huì)調(diào)用next(),而是通過(guò)for循環(huán)來(lái)迭代它,并且不需要關(guān)心StopIteration的錯(cuò)誤。

  2. yield 關(guān)鍵字

    • 只要在def函數(shù)里面看到有 yield 關(guān)鍵字那么就是生成器

      def get_value(n):
          for i in range(n):
              print('生成第一個(gè)值')
              # yield 關(guān)鍵字的作用是將這個(gè)函數(shù)變成一個(gè)生成器對(duì)象
              # 執(zhí)行時(shí),解釋器遇到 yield 后會(huì)中斷代碼的執(zhí)行,并返回yield后的數(shù)據(jù),
              # 下一次再執(zhí)行時(shí),會(huì)恢復(fù)前面yield中斷的狀態(tài),繼續(xù)執(zhí)行
              yield i
              print('第一個(gè)生成完成')
      
      g = get_value(4)
      
      value = next(g)
      print(value)
      value = next(g)
      print(value)
      
    • 代碼執(zhí)行到 yield 會(huì)暫停,然后把結(jié)果返回出去,下次啟動(dòng)生成器會(huì)在暫停的位置繼續(xù)往下執(zhí)行

    • yield 就是保存當(dāng)前程序執(zhí)行狀態(tài)

應(yīng)用

  • 斐波那契數(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ì)上就是一個(gè)函數(shù),它記住了上一次返回時(shí)在函數(shù)體中的位置。
對(duì)生成器函數(shù)的第二次(或第n次)調(diào)用,跳轉(zhuǎn)到函數(shù)上一次掛起的位置。
而且記錄了程序執(zhí)行的上下文。
生成器不僅“記住”了它的數(shù)據(jù)狀態(tài),生成還記住了程序執(zhí)行的位置。

(2)迭代器

迭代器是一種支持next()操作的對(duì)象。它包含了一組元素,當(dāng)執(zhí)行next()操作時(shí),返回其中一個(gè)元素。
當(dāng)所有元素都被返回后,再執(zhí)行next()報(bào)異?!猄topIteration
生成器一定是可迭代的,也一定是迭代器對(duì)象

(3)區(qū)別:
①生成器是生成元素的,迭代器是訪問(wèn)集合元素的一中方式
②迭代輸出生成器的內(nèi)容
③迭代器是一種支持next()操作的對(duì)象
④迭代器(iterator):其中iterator對(duì)象表示的是一個(gè)數(shù)據(jù)流,可以把它看做一個(gè)有序序列,但我們不能提前知道序列的長(zhǎng)度,只有通過(guò)nex()函數(shù)實(shí)現(xiàn)需要計(jì)算的下一個(gè)數(shù)據(jù)??梢钥醋錾善鞯囊粋€(gè)子集。

函數(shù)

如果在開(kāi)發(fā)程序時(shí),需要某塊代碼多次,但是為了提高編寫(xiě)的效率以及代碼的重用,所以把具有獨(dú)立功能的代碼塊組織為一個(gè)小模塊,這就是函數(shù)

基礎(chǔ)

局部變量與全局變量

  • 局部變量

    • 局部變量,就是在函數(shù)內(nèi)部定義的變量
    • 其作用范圍是這個(gè)函數(shù)內(nèi)部,即只能在這個(gè)函數(shù)中使用,在函數(shù)的外部是不能使用的
    • 因?yàn)槠渥饔梅秶皇窃谧约旱暮瘮?shù)內(nèi)部,所以不同的函數(shù)可以定義相同名字的局部變量
    • 局部變量的作用,為了臨時(shí)保存數(shù)據(jù)需要在函數(shù)中定義變量來(lái)進(jìn)行存儲(chǔ)
    • 當(dāng)函數(shù)調(diào)用時(shí),局部變量被創(chuàng)建,當(dāng)函數(shù)調(diào)用完成后這個(gè)變量就不能夠使用了
  • 全局變量

    • 在函數(shù)外邊定義的變量叫做全局變量

    • 全局變量能夠在所有的函數(shù)中進(jìn)行訪問(wèn)

    • 當(dāng)函數(shù)內(nèi)出現(xiàn)局部變量和全局變量相同名字時(shí),函數(shù)內(nèi)部中的 變量名 = 數(shù)據(jù) 此時(shí)理解為定義了一個(gè)局部變量,而不是修改全局變量的值

    • 如果在函數(shù)中出現(xiàn)global 全局變量的名字 那么這個(gè)函數(shù)中即使出現(xiàn)和全局變量名相同的變量名 = 數(shù)據(jù) 也理解為對(duì)全局變量進(jìn)行修改,而不是定義局部變量

           # 可以使用一次global對(duì)多個(gè)全局變量進(jìn)行聲明
           global a, b
           # 還可以用多次global聲明都是可以的
           # global a
           # global b
      

定義函數(shù)

定義一個(gè)函數(shù)要使用def語(yǔ)句,依次寫(xiě)出函數(shù)名、括號(hào)、括號(hào)中的參數(shù)和冒號(hào):,然后,在縮進(jìn)塊中編寫(xiě)函數(shù)體,函數(shù)的返回值用return語(yǔ)句返回。

函數(shù)名也是變量

調(diào)用函數(shù)

通過(guò) 函數(shù)名() 即可完成調(diào)用

  • 每次調(diào)用函數(shù)時(shí),函數(shù)都會(huì)從頭開(kāi)始執(zhí)行,當(dāng)這個(gè)函數(shù)中的代碼執(zhí)行完畢后,意味著調(diào)用結(jié)束了
  • 當(dāng)然了如果函數(shù)中執(zhí)行到了return也會(huì)結(jié)束函數(shù)
  • 函數(shù)名其實(shí)就是指向一個(gè)函數(shù)對(duì)象的引用,完全可以把函數(shù)名賦給一個(gè)變量,相當(dāng)于給這個(gè)函數(shù)起了一個(gè)“別名”

參數(shù)

默認(rèn)參數(shù)(缺省參數(shù))

  • 在定義函數(shù)時(shí),函數(shù)中的形式參數(shù),被賦值,這個(gè)值就是默認(rèn)值
  • 當(dāng)在函數(shù)調(diào)用時(shí),如果給定了值,那么就使用給定值,如果沒(méi)有給定值,那就使用默認(rèn)值
  • ++注意:默認(rèn)值參數(shù)只能出現(xiàn)在參數(shù)列表的最右側(cè)**++

位置參數(shù)(實(shí)參)

  • 使用位置參數(shù)時(shí),因?yàn)轭?lèi)型的原因,那么實(shí)參的順序要和形參的順序完全一致;
  • 當(dāng)沒(méi)有默認(rèn)值的情況下,參數(shù)的個(gè)數(shù)和也要一致

關(guān)鍵詞參數(shù)(形參)

  • 在定義形式參時(shí),每個(gè)參數(shù)都可以理解成一個(gè)key;
    使用這個(gè)key,可以明確的為當(dāng)前這個(gè)參數(shù)進(jìn)行賦值;使用關(guān)鍵字參數(shù),可以忽略參數(shù)的順序問(wèn)題

不定長(zhǎng)位置參數(shù)

  • *args 在參數(shù)中定義了該形參后,那可以通過(guò) *args 接收多個(gè)不確定個(gè)數(shù)的位置參數(shù)
  • ++加了星號(hào)(*)的變量args會(huì)存放所有未命名的變量參數(shù),*args為元組++

不定長(zhǎng)關(guān)鍵字參數(shù)

  • **kwargs 在參數(shù)中定義了該形參后,那可以通過(guò) **kwargs 接收多個(gè)不確定個(gè)數(shù)的關(guān)鍵字參數(shù)
  • 加**的變量kwargs會(huì)存放命名參數(shù),即形如key=value的參數(shù), **kwargs為字典.

小結(jié)

  • 定義時(shí)小括號(hào)中的參數(shù),用來(lái)接收參數(shù)用的,稱(chēng)為 “形參”

  • 調(diào)用時(shí)小括號(hào)中的參數(shù),用來(lái)傳遞給函數(shù)用的,稱(chēng)為 “實(shí)參”

  • 當(dāng)全局變量和局部變量同名時(shí),在函數(shù)內(nèi)使用變量,優(yōu)先使用局部變量;局部變量?jī)?yōu)先級(jí)高于全局變量

  • 參數(shù)定義的順序必須是:必選參數(shù)、默認(rèn)參數(shù)、不定長(zhǎng)位置參數(shù)和不定長(zhǎng)關(guān)鍵字參數(shù)。

    def f1(a, b, c=0, *args, **kwargs):
        print('a =', a, 'b =', b, 'c =', c, 'args =', args, 'kw =', kw)
    
  • 對(duì)于任意函數(shù),都可以通過(guò)類(lèi)似func(*args, **kw)的形式調(diào)用它,無(wú)論它的參數(shù)是如何定義的。

高階函數(shù)

既然變量可以指向函數(shù),函數(shù)的參數(shù)能接收變量,那么一個(gè)函數(shù)就可以接收另一個(gè)函數(shù)作為參數(shù),這種函數(shù)就稱(chēng)之為高階函數(shù)。

map()函數(shù)

map()函數(shù)接收兩個(gè)參數(shù),一個(gè)是函數(shù),一個(gè)是Iterable,map將傳入的函數(shù)依次作用到序列的每個(gè)元素,并把結(jié)果作為新的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]

把這個(gè)list所有數(shù)字轉(zhuǎn)為字符串:

>>> list(map(str, [1, 2, 3, 4, 5, 6, 7, 8, 9]))
['1', '2', '3', '4', '5', '6', '7', '8', '9']

reduce()函數(shù)

reduce把一個(gè)函數(shù)作用在一個(gè)序列[x1, x2, x3, ...]上,這個(gè)函數(shù)必須接收兩個(gè)參數(shù)

reduce把結(jié)果繼續(xù)和序列的下一個(gè)元素做累積計(jì)算:

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

當(dāng)然求和運(yùn)算可以直接用Python內(nèi)建函數(shù)sum(),沒(méi)必要?jiǎng)佑?code>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()類(lèi)似,filter()也接收一個(gè)函數(shù)和一個(gè)序列,不同的是,filter()把傳入的函數(shù)依次作用于每個(gè)元素,然后根據(jù)返回值是True還是False決定保留還是丟棄該元素。

例如,在一個(gè)list中,刪掉偶數(shù),只保留奇數(shù),可以這么寫(xiě):

def is_odd(n):
    return n % 2 == 1

list(filter(is_odd, [1, 2, 4, 5, 6, 9, 10, 15]))
# 結(jié)果: [1, 5, 9, 15]

注意到filter()函數(shù)返回的是一個(gè)Iterator,也就是一個(gè)惰性序列,所以要強(qiáng)迫filter()完成計(jì)算結(jié)果,需要用list()函數(shù)獲得所有結(jié)果并返回list。

sorted()函數(shù)

Python內(nèi)置的sorted()函數(shù)就可以對(duì)list進(jìn)行排序

此外,sorted()函數(shù)也是一個(gè)高階函數(shù),它還可以接收一個(gè)key函數(shù)來(lái)實(shí)現(xiàn)自定義的排序,例如按絕對(duì)值大小排序:

>>> sorted([36, 5, -12, 9, -21], key=abs)
[5, 9, -12, -21, 36]

sorted傳入key函數(shù),即可實(shí)現(xiàn)忽略大小寫(xiě)的排序:

>>> sorted(['bob', 'about', 'Zoo', 'Credit'], key=str.lower)
['about', 'bob', 'Credit', 'Zoo']

要進(jìn)行反向排序,不必改動(dòng)key函數(shù),可以傳入第三個(gè)參數(shù)reverse=True

匿名函數(shù)lambda

定義的函數(shù)沒(méi)有名字,這樣的函數(shù)叫做匿名函數(shù).

介紹

  • 格式:lambda [形參1], [形參2], ... : [單行表達(dá)式] 或 [函數(shù)調(diào)用]

  • lambda定義和普通函數(shù)的區(qū)別:

    1. lambda 沒(méi)有函數(shù)名,不必?fù)?dān)心函數(shù)名沖突
    2. lambda 參數(shù)列表外沒(méi)有括號(hào)
    3. lambda 函數(shù)體中,只能實(shí)現(xiàn)簡(jiǎn)單的表達(dá)式計(jì)算或函數(shù)調(diào)用
    4. lambda 函數(shù)體中,不能使用Return,if,while,for-in 這些都不行,不用寫(xiě)return,返回值就是該表達(dá)式的結(jié)果。
    5. lambda 函數(shù)體中,可以使用if 實(shí)現(xiàn)的三目運(yùn)算符.

應(yīng)用

  • 定義簡(jiǎn)單的單行函數(shù)

    my_function = lambda a, b: a + b
    
  • 作為函數(shù)的參數(shù)進(jìn)行傳遞

    • 解決目標(biāo): 1、提高函數(shù)的通用性 2、減少代碼量

    • 例子

      #現(xiàn)有字典 d={‘a(chǎn)’:24,‘g’:52,‘i’:12,‘k’:33}請(qǐng)按字典中的 value 值進(jìn)行排序? 
      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ù),我們把這個(gè)使用外部函數(shù)變量的內(nèi)部函數(shù)稱(chēng)為閉包

構(gòu)成條件

  1. 在函數(shù)嵌套(函數(shù)里面再定義函數(shù))的前提下
  2. 內(nèi)部函數(shù)使用了外部函數(shù)的變量(還包括外部函數(shù)的參數(shù))
  3. 外部函數(shù)返回了內(nèi)部函數(shù)

示例

# 定義一個(gè)外部函數(shù)
def func_out(num1):
    # 定義一個(gè)內(nèi)部函數(shù)
    def func_inner(num2):
        # 內(nèi)部函數(shù)使用了外部函數(shù)的變量(num1)
        result = num1 + num2
        print("結(jié)果是:", result)
    # 外部函數(shù)返回了內(nèi)部函數(shù),這里返回的內(nèi)部函數(shù)就是閉包
    return func_inner

# 創(chuàng)建閉包實(shí)例    
f = func_out(1)
# 執(zhí)行閉包
f(2)
f(3)
>>>
結(jié)果是: 3
結(jié)果是: 4

小結(jié)

  • 閉包可以保存外部函數(shù)內(nèi)的變量,不會(huì)隨著外部函數(shù)調(diào)用完而銷(xiāo)毀。

  • 閉包不僅可以保存外部函數(shù)的變量還可以提高代碼的可重用行。

  • 返回閉包時(shí)牢記一點(diǎn):返回函數(shù)不要引用任何循環(huán)變量,或者后續(xù)會(huì)發(fā)生變化的變量。

  • 在內(nèi)部函數(shù)中修改外部變量使用nonlocal 關(guān)鍵字

    nonlocal num1  # 告訴解釋器,此處使用的是 外部變量a
    # 修改外部變量num1
    num1 = 10
    

裝飾器

給已有函數(shù)增加額外功能的函數(shù),它本質(zhì)上就是一個(gè)閉包函數(shù)

特點(diǎn)

  1. 不修改已有函數(shù)的源代碼
  2. 不修改已有函數(shù)的調(diào)用方式
  3. 給已有函數(shù)增加額外的功能

語(yǔ)法格式

# 裝飾器
def decorator(fn): # fn:被裝飾的目標(biāo)函數(shù).
    def inner():
        '''執(zhí)行函數(shù)之前'''
        fn() # 執(zhí)行被裝飾的目標(biāo)函數(shù)
        '''執(zhí)行函數(shù)之后'''
    return inner

例子

# 添加一個(gè)登錄驗(yàn)證的功能
def check(fn):
    print("裝飾器函數(shù)執(zhí)行了")
    def inner():
        print("請(qǐng)先登錄....")
        fn()
    return inner

# 使用語(yǔ)法糖方式來(lái)裝飾函數(shù)
@check
def comment():
    print("發(fā)表評(píng)論")


comment()

>>>
請(qǐng)先登錄....
發(fā)表評(píng)論

裝飾帶有參數(shù)和返回值的函數(shù)

# 添加輸出日志的功能
def logging(fn):
    def inner(num1, num2):
        print("--正在努力計(jì)算--")
        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)

>>>
--正在努力計(jì)算--
3

通用裝飾器

# 通用裝飾器
def logging(fn):
  def inner(*args, **kwargs):
      print("--正在努力計(jì)算--")
      result = fn(*args, **kwargs)
      return result

  return inner

多個(gè)裝飾器

  • 裝飾過(guò)程是: 離函數(shù)最近的裝飾器先裝飾,然后外面的裝飾器再進(jìn)行裝飾,由內(nèi)到外的裝飾過(guò)程
def make_div(func):
    """對(duì)被裝飾的函數(shù)的返回值 div標(biāo)簽"""
    def inner():
        return "<div>" + func() + "</div>"
    return inner

def make_p(func):
    """對(duì)被裝飾的函數(shù)的返回值 p標(biāo)簽"""
    def inner():
        return "<p>" + func() + "</p>"
    return inner


# 裝飾過(guò)程: 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ù),語(yǔ)法格式: @裝飾器(參數(shù),...)

  • 寫(xiě)法:

    在裝飾器外面再包裹上一個(gè)函數(shù),讓最外面的函數(shù)接收參數(shù),返回的是裝飾器,因?yàn)锧符號(hào)后面必須是裝飾器實(shí)例。

# 添加輸出日志的功能
def logging(flag):

    def decorator(fn):
        def inner(num1, num2):
            if flag == "+":
                print("--正在努力加法計(jì)算--")
            elif flag == "-":
                print("--正在努力減法計(jì)算--")
            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,簡(jiǎn)稱(chēng)OOP,是一種程序設(shè)計(jì)思想。OOP把對(duì)象作為程序的基本單元,一個(gè)對(duì)象包含了數(shù)據(jù)和操作數(shù)據(jù)的函數(shù)。

三大特征有:封裝性、繼承性、多態(tài)性。

類(lèi)和對(duì)象

面向?qū)ο缶幊痰?個(gè)非常重要的概念:類(lèi)和對(duì)象

類(lèi)是抽象的模板,而實(shí)例則是一個(gè)一個(gè)具體的對(duì)象,各個(gè)實(shí)例擁有的數(shù)據(jù)都互相獨(dú)立,互不影響;

  • 類(lèi)的構(gòu)成

    • 類(lèi)的名稱(chēng):類(lèi)名(命名規(guī)則按照"大駝峰命名法")
    • 類(lèi)的屬性:一組數(shù)據(jù)
    • 類(lèi)的方法:允許對(duì)進(jìn)行操作的方法 (行為)
  • 方法就是與實(shí)例綁定的函數(shù),和普通函數(shù)不同,方法可以直接訪問(wèn)實(shí)例的數(shù)據(jù);

  • 對(duì)象

    • 對(duì)象名 = 類(lèi)名(參數(shù)列表....)
    • 對(duì)象調(diào)用方法的格式:對(duì)象名.方法名(參數(shù)列表)
    • 注意:方法中參數(shù)列表中的第一個(gè)參數(shù)self,不需要手動(dòng)傳遞,這個(gè)參數(shù)是由解釋器在執(zhí)行程序時(shí),自動(dòng)傳遞的默認(rèn)會(huì)將當(dāng)前調(diào)用方法的對(duì)象引用傳遞進(jìn)去

在方法內(nèi)通過(guò)self獲取對(duì)象屬性

class Hero(object):
    """定義了一個(gè)英雄類(lèi),可以移動(dòng)和攻擊"""
    def move(self):
        """實(shí)例方法"""
        print("正在前往事發(fā)地點(diǎn)...")

    def attack(self):
        """實(shí)例方法"""
        print("發(fā)出了一招強(qiáng)力的普通攻擊...")

    def info(self):
        """在類(lèi)的實(shí)例方法中,通過(guò)self獲取該對(duì)象的屬性"""
        print("英雄 %s 的生命值 :%d" % (self.name, self.hp))
        print("英雄 %s 的攻擊力 :%d" % (self.name, self.atk))
        print("英雄 %s 的護(hù)甲值 :%d" % (self.name, self.armor))


# 實(shí)例化了一個(gè)英雄對(duì)象 泰達(dá)米爾
taidamier = Hero()

# 給對(duì)象添加屬性,以及對(duì)應(yīng)的屬性值
taidamier.name = "泰達(dá)米爾"  # 姓名
taidamier.hp = 2600  # 生命值
taidamier.atk = 450  # 攻擊力
taidamier.armor = 200  # 護(hù)甲值

# 通過(guò).成員選擇運(yùn)算符,獲取對(duì)象的實(shí)例方法
taidamier.info()  # 只需要調(diào)用實(shí)例方法info(),即可獲取英雄的屬性
taidamier.move()
taidamier.attack()

_init_()方法

兩個(gè)下劃線開(kāi)始,兩個(gè)下劃線結(jié)束的方法,就是魔法方法,__init__()就是一個(gè)魔法方法,通常用來(lái)做屬性初始化 或 賦值 操作。

  • __init__()方法,在創(chuàng)建一個(gè)對(duì)象時(shí)默認(rèn)被調(diào)用,不需要手動(dòng)調(diào)用
  • __init__(self)中的self參數(shù),不需要開(kāi)發(fā)者傳遞,python解釋器會(huì)自動(dòng)把當(dāng)前的對(duì)象引用傳遞過(guò)去。如果在創(chuàng)建對(duì)象時(shí)傳遞了2個(gè)實(shí)參,那么__init__(self)中出了self作為第一個(gè)形參外還需要2個(gè)形參,例如__init__(self,x,y)

__str__()方法

用來(lái)顯示信息,該方法需要 return 一個(gè)數(shù)據(jù),并且只有self一個(gè)參數(shù),當(dāng)在類(lèi)的外部 print(對(duì)象) 則打印這個(gè)數(shù)據(jù)

    def __str__(self):
        return "英雄 <%s> 數(shù)據(jù): 生命值 %d" % (self.name, self.hp)
  • 當(dāng)使用print輸出對(duì)象的時(shí)候,默認(rèn)打印對(duì)象的內(nèi)存地址。如果類(lèi)定義了__str__(self)方法,那么就會(huì)打印從在這個(gè)方法中 return 的數(shù)據(jù)
  • __str__方法通常返回一個(gè)字符串,作為這個(gè)對(duì)象的描述信息

__del__()方法

當(dāng)刪除對(duì)象時(shí),python解釋器也會(huì)默認(rèn)調(diào)用

  • 當(dāng)有變量保存了一個(gè)對(duì)象的引用時(shí),此對(duì)象的引用計(jì)數(shù)就會(huì)加1;當(dāng)使用del() 刪除變量指向的對(duì)象時(shí),則會(huì)減少對(duì)象的引用計(jì)數(shù)。
  • 如果對(duì)象的引用計(jì)數(shù)不為1,那么會(huì)讓這個(gè)對(duì)象的引用計(jì)數(shù)減1,當(dāng)對(duì)象的引用計(jì)數(shù)為0的時(shí)候,則對(duì)象才會(huì)被真正刪除(內(nèi)存被回收)。

小結(jié)

  1. 在類(lèi)內(nèi)部獲取 屬性 和 實(shí)例方法,通過(guò)self獲??;
  2. 在類(lèi)外部獲取 屬性 和 實(shí)例方法,通過(guò)對(duì)象名獲取。
  3. 如果一個(gè)類(lèi)有多個(gè)對(duì)象,每個(gè)對(duì)象的屬性是各自保存的,都有各自獨(dú)立的地址;
  4. 但是實(shí)例方法是所有對(duì)象共享的,只占用一份內(nèi)存空間。類(lèi)會(huì)通過(guò)self來(lái)判斷是哪個(gè)對(duì)象調(diào)用了實(shí)例方法。

封裝

意義

  1. 將屬性和方法放到一起做為一個(gè)整體,然后通過(guò)實(shí)例化對(duì)象來(lái)處理;
  2. 隱藏內(nèi)部實(shí)現(xiàn)細(xì)節(jié),只需要和對(duì)象及其屬性和方法交互就可以了;
  3. 對(duì)類(lèi)的屬性和方法增加 訪問(wèn)權(quán)限控制。

私有屬性和私有方法

在屬性名和方法名 前面 加上兩個(gè)下劃線 __

  1. 類(lèi)的私有屬性 和 私有方法,都不能通過(guò)對(duì)象直接訪問(wèn),但是可以在本類(lèi)內(nèi)部訪問(wèn);
  2. 類(lèi)的私有屬性 和 私有方法,都不會(huì)被子類(lèi)繼承,子類(lèi)也無(wú)法訪問(wèn);
  3. 私有屬性 和 私有方法 往往用來(lái)處理類(lèi)的內(nèi)部事情,不通過(guò)對(duì)象處理,起到安全作用

修改私有屬性

定義一個(gè)可以調(diào)用的公有方法,在這個(gè)公有方法內(nèi)訪問(wèn)修改。

  • 通常會(huì)定義get_xxx()方法和set_xxx()方法來(lái)獲取和修改私有屬性值。

  • set/get方法對(duì)私有屬性操作時(shí)的好處:

    • 提供精確的訪問(wèn)控制權(quán)限
    • 隱藏實(shí)現(xiàn)細(xì)節(jié),讓代碼更安全
    • 可以提供更加安全的數(shù)據(jù)有效性控制
    class Master(object):
        def __init__(self):
            # 私有屬性,可以在類(lèi)內(nèi)部通過(guò)self調(diào)用,但不能通過(guò)對(duì)象訪問(wèn)
            self.__money = 10000
            
        # 返回私有屬性的值
        def get_money(self):
            return self.__money
    
        # 接收參數(shù),修改私有屬性的值
        def set_money(self, num):
            self.__money = num
    

繼承

  • 在程序中,繼承描述的是多個(gè)類(lèi)之間的所屬關(guān)系。
  • 如果一個(gè)類(lèi)A里面的屬性和方法可以復(fù)用,則可以通過(guò)繼承的方式,傳遞到類(lèi)B里。
  • 那么類(lèi)A就是基類(lèi),也叫做父類(lèi);類(lèi)B就是派生類(lèi),也叫做子類(lèi)。

單繼承

  • 繼承的格式
    class 子類(lèi)名(父類(lèi)名):
        pass
  • 當(dāng)發(fā)生繼承后,子類(lèi)會(huì)繼承父類(lèi)中的屬性和方法,可以直接 使用

  • 在子類(lèi)中不能直接使用父類(lèi)中的私有方法;通過(guò)繼承得到的父類(lèi)的公有方法,間接 執(zhí)行父類(lèi)的私有方法

  • 因?yàn)樽宇?lèi)提供了 init 方法后,那么在使用子類(lèi)實(shí)例對(duì)象時(shí),就會(huì)調(diào)用 子類(lèi)自己 init 方法;如果想父類(lèi)中的屬性可以得到,需要執(zhí)行父類(lèi)中的init方法

格式:父類(lèi)名.__init__(self,父類(lèi)中需要屬性參數(shù)列表)

多層繼承

子類(lèi)繼承父類(lèi),父類(lèi)繼承爺爺類(lèi),這就是多層繼承

多繼承

子類(lèi)繼承多個(gè)父類(lèi)

  • 如果子類(lèi)和父類(lèi)的方法名和屬性名相同,則默認(rèn)使用子類(lèi)的

  • 注意:如果多個(gè)父類(lèi)中有同名的 屬性和方法,則默認(rèn)使用第一個(gè)父類(lèi)的屬性和方法(根據(jù)類(lèi)的魔法屬性mro的順序來(lái)查找)

多繼承的初始化

  • 在多繼承時(shí),如果繼承的多個(gè)類(lèi)同時(shí)繼承同一個(gè)父類(lèi),那么這時(shí)會(huì)出現(xiàn)初始化問(wèn)題,這個(gè)共同父類(lèi)會(huì)被初始化多次.

  • super()執(zhí)行過(guò)程:
    在 self 這個(gè)對(duì)象的所屬類(lèi)中,通過(guò) mro 找到方法解析順序
    在順序中,找當(dāng)前類(lèi)名的下一個(gè)類(lèi)來(lái)初始化或查找方法

    • 類(lèi)名.__mro__得到了一個(gè)元組,元組中的元素是當(dāng)前類(lèi)在繼承關(guān)系上的一個(gè)順序;

    • 這個(gè)順序不是我們確定的,是由在確定某個(gè)類(lèi)的繼承關(guān)系關(guān)系后,由解釋器來(lái)確定這個(gè)順序

    • 多繼承調(diào)用指定父類(lèi)中方法

              父類(lèi)名.方法()
              super().方法()
              
      # 方法2. super() 帶參數(shù)版本,只支持新式類(lèi)
       super(Prentice, self).__init__() # 執(zhí)行父類(lèi)的 __init__方法 
      # super(Prentice, self).make_cake()
      # self.make_cake()
      
      # 方法3. super()的簡(jiǎn)化版,只支持新式類(lèi)        super().__init__()  # 執(zhí)行父類(lèi)的 __init__方法 
      super().make_cake()  # 執(zhí)行父類(lèi)的 實(shí)例方法
      self.make_cake()  # 執(zhí)行本類(lèi)的實(shí)例方法
      

Mixin

在設(shè)計(jì)類(lèi)的繼承關(guān)系時(shí),通常,主線都是單一繼承下來(lái)的,但是,如果需要“混入”額外的功能,通過(guò)多重繼承就可以實(shí)現(xiàn)。這種設(shè)計(jì)通常稱(chēng)之為MixIn。

MixIn的目的就是給一個(gè)類(lèi)增加多個(gè)功能,這樣,在設(shè)計(jì)類(lèi)的時(shí)候,我們優(yōu)先考慮通過(guò)多重繼承來(lái)組合多個(gè)MixIn的功能,而不是設(shè)計(jì)多層次的復(fù)雜的繼承關(guān)系。

比如,編寫(xiě)一個(gè)多進(jìn)程模式的TCP服務(wù),定義如下:

class MyTCPServer(TCPServer, ForkingMixIn):
    pass

多態(tài)

在需要使用父類(lèi)對(duì)象的地方,也可以使用子類(lèi)對(duì)象, 這種情況就叫多態(tài).

比如, 在函數(shù)中,我需要調(diào)用 某一個(gè)父類(lèi)對(duì)象的方法, 那么我們也可以在這個(gè)地方調(diào)用子類(lèi)對(duì)象的方法.

類(lèi)屬性

  • 類(lèi)屬性就是類(lèi)對(duì)象所擁有的屬性,它被所有類(lèi)對(duì)象實(shí)例對(duì)象所共有,類(lèi)對(duì)象和實(shí)例對(duì)象均可訪問(wèn),在內(nèi)存中只存在一個(gè)副本

  • 類(lèi)屬性可以使用實(shí)例對(duì)象來(lái)引用,但是不能修改
    一般情況下:類(lèi)屬性 都只使用 類(lèi)對(duì)象 來(lái)調(diào)用

  • class People(object):
        name = 'Tom'  # 公有的類(lèi)屬性
        __age = 12  # 私有的類(lèi)屬性
    
    p = People()
    
    print(p.name)  # 正確
    print(People.name)  # 正確
    print(p.__age)  # 錯(cuò)誤,不能在類(lèi)外通過(guò)實(shí)例對(duì)象訪問(wèn)私有的類(lèi)屬性
    print(People.__age) # 錯(cuò)誤,不能在類(lèi)外通過(guò)類(lèi)對(duì)象訪問(wèn)私有的類(lèi)屬性
    

實(shí)例屬性(對(duì)象屬性)

由于Python是動(dòng)態(tài)語(yǔ)言,根據(jù)類(lèi)創(chuàng)建的實(shí)例可以任意綁定屬性。

給實(shí)例綁定屬性的方法是通過(guò)實(shí)例變量,或者通過(guò)self變量

class Student(object):
    def __init__(self, name):
        self.name = name

s = Student('Bob')
s.score = 90
  • 以 對(duì)象名.xxx 的形式調(diào)用的都是實(shí)例的屬性或?qū)嵗姆椒?
  • 實(shí)例屬性和實(shí)例方法只能由實(shí)例對(duì)象調(diào)用

類(lèi)方法

是類(lèi)對(duì)象所擁有的方法,需要用修飾器@classmethod來(lái)標(biāo)識(shí)其為類(lèi)方法,對(duì)于類(lèi)方法,第一個(gè)參數(shù)必須是類(lèi)對(duì)象,一般以cls作為第一個(gè)參數(shù)

定義格式:
@classmethod
def 方法名(cls,...):
    pass
    
調(diào)用格式:
類(lèi)對(duì)象.類(lèi)方法名
  • 注意:在類(lèi)方法中,不能使用self,但是可以使用 cls,該參數(shù)用來(lái)表示 當(dāng)前類(lèi)對(duì)象,這個(gè)參數(shù)也是自動(dòng)傳遞的

  • @classmethod 是一個(gè)裝飾 器,用來(lái)修飾一個(gè)方法成為類(lèi)方法,當(dāng)在執(zhí)行該 類(lèi)方法時(shí),解釋 會(huì)自動(dòng) 將類(lèi)對(duì)象傳遞到參數(shù) cls中

  • 類(lèi)方法還有一個(gè)用途就是可以對(duì)類(lèi)屬性進(jìn)行修改

    class People(object):
        country = 'china'
    
        #類(lèi)方法,用classmethod來(lái)進(jìn)行修飾
        @classmethod
        def get_country(cls):
            return cls.country
    
        @classmethod
        def set_country(cls,country):
            cls.country = country
    

靜態(tài)方法

通過(guò)修飾器@staticmethod來(lái)進(jìn)行修飾,靜態(tài)方法不需要多定義參數(shù),可以通過(guò)對(duì)象和類(lèi)來(lái)訪問(wèn)

格式:
@staticmethod
def 方法名(參數(shù)列表....):
    pass
    
調(diào)用方式:
    同類(lèi)方法
    類(lèi)對(duì)象.靜態(tài)方法名()
  • 靜態(tài)方法中不需要額外定義參數(shù),因此在靜態(tài)方法中引用類(lèi)屬性的話,必須通過(guò)類(lèi)實(shí)例對(duì)象來(lái)引用

使用@property

property屬性就是負(fù)責(zé)把一個(gè)方法當(dāng)做屬性進(jìn)行使用,這樣做可以簡(jiǎn)化代碼使用。

定義property屬性有兩種方式

  1. 裝飾器方式
  2. 類(lèi)屬性方式

裝飾器方式

class Person(object):

    def __init__(self):
        self.__age = 0

    # 裝飾器方式的property, 把a(bǔ)ge方法當(dāng)做屬性使用, 表示當(dāng)獲取屬性時(shí)會(huì)執(zhí)行下面修飾的方法
    @property
    def age(self):
        return self.__age

    # 把a(bǔ)ge方法當(dāng)做屬性使用, 表示當(dāng)設(shè)置屬性時(shí)會(huì)執(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的實(shí)現(xiàn)比較復(fù)雜,我們先考察如何使用。把一個(gè)getter方法變成屬性,只需要加上@property就可以了,此時(shí),@property本身又創(chuàng)建了另一個(gè)裝飾器@age.setter,負(fù)責(zé)把一個(gè)setter方法變成屬性賦值

  • @property 表示把方法當(dāng)做屬性使用, 表示當(dāng)獲取屬性時(shí)會(huì)執(zhí)行下面修飾的方法
  • @方法名.setter 表示把方法當(dāng)做屬性使用,表示當(dāng)設(shè)置屬性時(shí)會(huì)執(zhí)行下面修飾的方法
  • 裝飾器方式的property屬性修飾的方法名一定要一樣。

類(lèi)屬性方式

class Person(object):

    def __init__(self):
        self.__age = 0

    def get_age(self):
        """當(dāng)獲取age屬性的時(shí)候會(huì)執(zhí)行該方法"""
        return self.__age

    def set_age(self, new_age):
        """當(dāng)設(shè)置age屬性的時(shí)候會(huì)執(zhí)行該方法"""
        if new_age >= 150:
            print("成精了")
        else:
            self.__age = new_age

    # 類(lèi)屬性方式的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ù)說(shuō)明:

  • 第一個(gè)參數(shù)是獲取屬性時(shí)要執(zhí)行的方法
  • 第二個(gè)參數(shù)是設(shè)置屬性時(shí)要執(zhí)行的方法

小結(jié)

# 數(shù)據(jù) (屬性)
#    區(qū)別: 類(lèi)的每個(gè)對(duì)象(實(shí)例) 對(duì)于這個(gè)數(shù)據(jù)是獨(dú)有的還是共享的,是不同的還是相同的
#   1. 對(duì)象屬性 (實(shí)例屬性)
#       類(lèi)的每個(gè)對(duì)象 這個(gè)名字的屬性數(shù)據(jù) 是獨(dú)有的 ,每個(gè)對(duì)象不同
#   2. 類(lèi)屬性
#       類(lèi)的每個(gè)對(duì)象 這個(gè)名字的屬性數(shù)據(jù) 是共享,每個(gè)對(duì)象都相同

# 函數(shù)(方法)
#   區(qū)別:方法中能夠直接使用的屬性數(shù)據(jù)不同

#   1. 對(duì)象方法(實(shí)例方法)  可以直接讀寫(xiě)對(duì)象屬性,可以直接讀類(lèi)屬性
#       def obj_func(self):
#           self.

#   2. 類(lèi)方法
#      @classmethod  可以直接讀寫(xiě)類(lèi)屬性
#      def class_func(cls, ..):
#          cls.

#   3. 靜態(tài)方法
#      @staticmethod 雖然可以通過(guò)類(lèi)名操作類(lèi)屬性,但是我們可以認(rèn)為 不是直接操作屬性
#      def static_func():
#          類(lèi)名.

# 選擇:如果定義一個(gè)函數(shù),這個(gè)函數(shù)中需要使用對(duì)象屬性,定義對(duì)象方法
#      如果定義一個(gè)函數(shù),這個(gè)函數(shù)中僅需要使用類(lèi)屬性,定義類(lèi)方法
#      如果定義一個(gè)函數(shù),這個(gè)函數(shù)不需要使用類(lèi)的任何屬性,從邏輯的角度考慮 應(yīng)該是類(lèi)中的一個(gè)處理方法,此時(shí)定義靜態(tài)方法即可

總結(jié)

面向?qū)ο笈c面向過(guò)程

面向過(guò)程的程序設(shè)計(jì)把計(jì)算機(jī)程序視為一系列的命令集合,即一組函數(shù)的順序執(zhí)行。為了簡(jiǎn)化程序設(shè)計(jì),面向過(guò)程把函數(shù)繼續(xù)切分為子函數(shù),即把大塊函數(shù)通過(guò)切割成小塊函數(shù)來(lái)降低系統(tǒng)的復(fù)雜度。

而面向?qū)ο蟮某绦蛟O(shè)計(jì)把計(jì)算機(jī)程序視為一組對(duì)象的集合,而每個(gè)對(duì)象都可以接收其他對(duì)象發(fā)過(guò)來(lái)的消息,并處理這些消息,計(jì)算機(jī)程序的執(zhí)行就是一系列消息在各個(gè)對(duì)象之間傳遞。

  • 對(duì)于面向過(guò)程的思想: 需要實(shí)現(xiàn)一個(gè)功能的時(shí)候,看重的是開(kāi)發(fā)的步驟和過(guò)程,每一個(gè)步驟都需要自己親力親為,需要自己編寫(xiě)代碼(自己來(lái)做)。面向過(guò)程語(yǔ)言是一種基于功能分析的、以算法為中心的程序設(shè)計(jì)方法;
  • 對(duì)于面向?qū)ο?/strong>的思想:把一切都看成對(duì)象,而對(duì)象一般都由屬性+方法組成;當(dāng)需要實(shí)現(xiàn)一個(gè)功能的時(shí)候,看重的并不是過(guò)程和步驟,而是關(guān)心誰(shuí)幫我做這件事(偷懶,找人幫我做)。而面向?qū)ο笫且环N基于結(jié)構(gòu)分析的、以數(shù)據(jù)為中心的程序設(shè)計(jì)思想。三大特征有:封裝性、繼承性、多態(tài)性。
  • 面向過(guò)程(手洗):脫衣服、找一個(gè)盆、加水、加洗衣粉、浸泡30分鐘、搓洗、擰衣服、倒掉水、再加水、漂洗、擰衣服、倒掉水、晾衣服。
  • 面向?qū)ο螅C(jī)洗):脫衣服、放入洗衣機(jī)、按下開(kāi)關(guān)、拿出衣服晾曬。

高級(jí)

I/O編程

文件讀寫(xiě)

打開(kāi)文件

  • 以文本方式打開(kāi)方式的模式
r -> read text; file = open('a.txt','r') 
以文本方式打開(kāi)文件讀,文件存在,打開(kāi)成功,文件不存在,打開(kāi)失敗
w -> write text; file = open('a.txt','w') 
以文本方式打開(kāi)文件寫(xiě),不管文件是否存在,都會(huì)新創(chuàng)建一個(gè)新文件
a -> append text ;file = open('a.txt','w')
以文件方式打開(kāi)文件追加,文件不存在,創(chuàng)建文件,文件存在,那么打開(kāi)文件然后將光標(biāo)移動(dòng)到文件的最后
  • 以二進(jìn)制形式打開(kāi)文件的模式
  rb  以二進(jìn)制形式打開(kāi)文件讀取
    wb  以二進(jìn)制形式打開(kāi)文件寫(xiě)入
    ab  以二進(jìn)制形式打開(kāi)文件追加
  • with語(yǔ)句

    • Python提供了 with 語(yǔ)句的這種寫(xiě)法,既簡(jiǎn)單又安全,并且 with 語(yǔ)句執(zhí)行完成以后自動(dòng)調(diào)用關(guān)閉文件操作,即使出現(xiàn)異常也會(huì)自動(dòng)調(diào)用關(guān)閉文件操作**。
    # 1、以寫(xiě)的方式打開(kāi)文件
    with open("1.txt", "w") as f:
        # 2、讀取文件內(nèi)容
        f.write("hello world")
    

讀數(shù)據(jù)

  • 默認(rèn)讀取全部文件內(nèi)容;只適用于文件比較小的情況
file = open('a.txt','rt')
content = file.read()
file.close()
  • 讀取多行文件的方式
while True:
    # 讀取
    content = file.read(4096) #從文件中讀取的數(shù)據(jù)的長(zhǎng)度(單位是字節(jié))
    # 如果在文件讀取時(shí),讀取的結(jié)果為空串,說(shuō)明文件讀取完畢
    # 根據(jù)這個(gè)條件 可以設(shè)置讀取文件的結(jié)束條件
    if content == '':
        break
    print(content,end='')

# 關(guān)閉文件
file.close()
  • readline()以行讀取
  • readlines()以行的方式 讀取整 個(gè)文件,并且返回的是一個(gè)列表,其中每一行的數(shù)據(jù)為一個(gè)元素

寫(xiě)數(shù)據(jù)

  • write()可以完成向文件寫(xiě)入數(shù)據(jù)

    f = open('test.txt', 'w')
    f.write('hello world, i am here!')
    f.close()
    
  • 如果文件不存在那么創(chuàng)建

操作文件和目錄

  • 導(dǎo)入 os 模塊

  • os.rename(需要修改的文件名, 新的文件名)

    import os
    os.rename("畢業(yè)論文.txt", "畢業(yè)論文-最終版.txt")
    
  • os.remove(待刪除的文件名)

  • os.mkdir("張三")創(chuàng)建文件夾;如果當(dāng)前目錄 存在,會(huì)報(bào)錯(cuò)

  • os.getcwd()獲取當(dāng)前目錄

  • os.chdir("../指定路徑")改變當(dāng)前目錄 到指定 的路徑 上去

  • os.listdir("./")獲取目錄下的文件名稱(chēng),存在列表中

    file_list = os.listdir('.')
    print(file_list)
    for file in file_list:
        print(file)
    
  • os.rmdir('路徑') 刪除一個(gè)空文件夾,當(dāng)目錄文件夾不為空,不能刪除

模塊

模塊是一組Python代碼的集合,可以使用其他模塊,也可以被其他模塊使用。

使用模塊

import

在Python中用關(guān)鍵字import來(lái)引入某個(gè)模塊,比如要引用模塊math,就可以在文件最開(kāi)始的地方用import math來(lái)引入

在調(diào)用math模塊中的函數(shù)時(shí),必須這樣引用:

  模塊名.函數(shù)名

from…import

Python的from語(yǔ)句讓你從模塊中導(dǎo)入一個(gè)指定的部分到當(dāng)前命名空間中,此時(shí)可以用下面方法實(shí)現(xiàn):

from 模塊名 import 函數(shù)名1,函數(shù)名2....
  • 通過(guò)這種方式引入的時(shí)候,調(diào)用函數(shù)時(shí)只能給出函數(shù)名,不能給出模塊名,但是當(dāng)兩個(gè)模塊中含有相同名稱(chēng)函數(shù)的時(shí)候,后面一次引入會(huì)覆蓋前一次引入
  • 如果想一次性引入math中所有的東西,還可以通過(guò)from math import *來(lái)實(shí)現(xiàn)
  • 用as給引入起別名
  • 在模塊中的__all__變量就是為了限制或者指定能被導(dǎo)入到別的模塊的函數(shù),如果指定了那么只能是指定的那些可以被導(dǎo)入,沒(méi)有指定默認(rèn)就是全部可以導(dǎo)入,當(dāng)然私有屬性應(yīng)該除外。

制作模塊

編寫(xiě)一個(gè)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行是標(biāo)準(zhǔn)注釋?zhuān)?行注釋可以讓這個(gè)hello.py文件直接在Unix/Linux/Mac上運(yùn)行,第2行注釋表示.py文件本身使用標(biāo)準(zhǔn)UTF-8編碼;

第4行是一個(gè)字符串,表示模塊的文檔注釋?zhuān)魏文K代碼的第一個(gè)字符串都被視為模塊的文檔注釋?zhuān)?/p>

第6行使用__author__變量把作者寫(xiě)進(jìn)去,這樣當(dāng)你公開(kāi)源代碼后別人就可以瞻仰你的大名;

以上就是Python模塊的標(biāo)準(zhǔn)文件模板,當(dāng)然也可以全部刪掉不寫(xiě),但是,按標(biāo)準(zhǔn)辦事肯定沒(méi)錯(cuò)。

定位模塊

當(dāng)你導(dǎo)入一個(gè)模塊,Python解析器對(duì)模塊位置的搜索順序是:

  1. 當(dāng)前目錄
  2. 如果不在當(dāng)前目錄,Python則搜索在shell變量PYTHONPATH下的每個(gè)目錄。
  3. 如果都找不到,Python會(huì)察看默認(rèn)路徑。UNIX下,默認(rèn)路徑一般為/usr/local/lib/python/
  4. 模塊搜索路徑存儲(chǔ)在system模塊的sys.path變量中。變量里包含當(dāng)前目錄,PYTHONPATH和由安裝過(guò)程決定的默認(rèn)目錄。

安裝第三方模塊

在Python中,安裝第三方模塊,是通過(guò)包管理工具pip完成的。

第三方庫(kù)都會(huì)在Python官方的pypi.python.org網(wǎng)站注冊(cè),要安裝一個(gè)第三方庫(kù),必須先知道該庫(kù)的名稱(chēng),可以在官網(wǎng)或者pypi上搜索

異常

當(dāng)Python檢測(cè)到一個(gè)錯(cuò)誤時(shí),解釋器就無(wú)法繼續(xù)執(zhí)行了,反而出現(xiàn)了一些錯(cuò)誤的提示,這就是所謂的"異常"

格式

  try:
        可能會(huì)出現(xiàn)異常問(wèn)題的代碼
    except Exception as e:
        當(dāng)出現(xiàn)異常時(shí),解決異常的代碼
    else:
        當(dāng)沒(méi)有出現(xiàn)異常時(shí),正常執(zhí)行的代碼
    finally:
        無(wú)論是否出現(xiàn)異常,都會(huì)執(zhí)行這里的代碼

自定義異常

    格式:
    class 異常名Error(Exception):
        def __init__(self,msg=''):
            self.__msg = msg

        def __str__(self):
            return self.__msg


    class CustomError(Exception):
        pass

調(diào)試與測(cè)試

斷言assert

斷言就是判斷一個(gè)函數(shù)或?qū)ο蟮囊粋€(gè)方法所產(chǎn)生的結(jié)果是否符合你期望的那個(gè)結(jié)果。 python中assert斷言是聲明布爾值為真的判定,如果表達(dá)式為假會(huì)發(fā)生異常。單元測(cè)試中,一般使用assert來(lái)斷言結(jié)果。

def foo(s):
    n = int(s)
    assert n != 0, 'n is zero!' # 表達(dá)式為假,逗號(hào)后為自定義異常;也可不定義
    return 10 / n

def main():
    foo('0')

如果斷言失敗,assert語(yǔ)句本身就會(huì)拋出AssertionError

程序中如果到處充斥著assert,和print()相比也好不到哪去。不過(guò),啟動(dòng)Python解釋器時(shí)可以用-O(是字母O)參數(shù)來(lái)關(guān)閉assert,關(guān)閉后,你可以把所有的assert語(yǔ)句當(dāng)成pass來(lái)看。

$ python -O err.py

常用的斷言方法:

常用的斷言方法:

assertEqual     如果兩個(gè)值相等,則pass
assertNotEqual  如果兩個(gè)值不相等,則pass
assertTrue      判斷bool值為T(mén)rue,則pass
assertFalse     判斷bool值為False,則pass
assertIsNone    不存在,則pass
assertIsNotNone 存在,則pass

logging

logging模塊是Python內(nèi)置的標(biāo)準(zhǔn)模塊,主要用于輸出運(yùn)行日志,可以設(shè)置輸出日志的等級(jí)、日志保存路徑、日志文件回滾等;相比print,具備如下優(yōu)點(diǎn):

  1. 可以通過(guò)設(shè)置不同的日志等級(jí),默認(rèn)是warning級(jí)別,在release版本中只輸出重要信息,而不必顯示大量的調(diào)試信息;
  2. print將所有信息都輸出到標(biāo)準(zhǔn)輸出中,嚴(yán)重影響開(kāi)發(fā)者從標(biāo)準(zhǔn)輸出中查看其它數(shù)據(jù);logging則可以由開(kāi)發(fā)者決定將信息輸出到什么地方,以及怎么輸出;
import logging
logging.basicConfig(level=logging.INFO)

s = '0'
n = int(s)
logging.info('n = %d' % n)
print(10 / n)
  • 記錄信息的級(jí)別

    • debug : 打印全部的日志,詳細(xì)的信息,通常只出現(xiàn)在診斷問(wèn)題上
    • info : 打印info,warning,error,critical級(jí)別的日志,確認(rèn)一切按預(yù)期運(yùn)行
    • warning : 打印warning,error,critical級(jí)別的日志,一個(gè)跡象表明,一些意想不到的事情發(fā)生了,或表明一些問(wèn)題在不久的將來(lái)(例如。磁盤(pán)空間低”),這個(gè)軟件還能按預(yù)期工作
    • error : 打印error,critical級(jí)別的日志,更嚴(yán)重的問(wèn)題,軟件沒(méi)能執(zhí)行一些功能
    • critical : 打印critical級(jí)別,一個(gè)嚴(yán)重的錯(cuò)誤,這表明程序本身可能無(wú)法繼續(xù)運(yùn)行
  • logging.basicConfig函數(shù)各參數(shù):

    filename:指定日志文件名;

    filemode:和file函數(shù)意義相同,指定日志文件的打開(kāi)模式,'w'或者'a';

    format:指定輸出的格式和內(nèi)容,format可以輸出很多有用的信息,

    • 參數(shù):作用
      
      %(levelno)s:打印日志級(jí)別的數(shù)值
      %(levelname)s:打印日志級(jí)別的名稱(chēng)
      %(pathname)s:打印當(dāng)前執(zhí)行程序的路徑,其實(shí)就是sys.argv[0]
      %(filename)s:打印當(dāng)前執(zhí)行程序名
      %(funcName)s:打印日志的當(dāng)前函數(shù)
      %(lineno)d:打印日志的當(dāng)前行號(hào)
      %(asctime)s:打印日志的時(shí)間
      %(thread)d:打印線程ID
      %(threadName)s:打印線程名稱(chēng)
      %(process)d:打印進(jìn)程ID
      %(message)s:打印日志信息
      
  • 輸出日志

    import logging  # 引入logging模塊
    import os.path
    import time
    # 第一步,創(chuàng)建一個(gè)logger
    logger = logging.getLogger()
    logger.setLevel(logging.INFO)  # Log等級(jí)總開(kāi)關(guān)
    # 第二步,創(chuàng)建一個(gè)handler,用于寫(xiě)入日志文件
    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等級(jí)的開(kāi)關(guān)
    # 第三步,定義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')
    

單元測(cè)試

單元測(cè)試是用來(lái)對(duì)一個(gè)模塊、一個(gè)函數(shù)或者一個(gè)類(lèi)來(lái)進(jìn)行正確性檢驗(yàn)的測(cè)試工作。

import unittest
class TestClass(unittest.TestCase):

    #該方法會(huì)首先執(zhí)行,相當(dāng)于做測(cè)試前的準(zhǔn)備工作
    def setUp(self):
        pass

    #該方法會(huì)在測(cè)試代碼執(zhí)行完后執(zhí)行,相當(dāng)于做測(cè)試后的掃尾工作
    def tearDown(self):
        pass

    #測(cè)試代碼
    def test_app_exists(self):
        pass
  • 運(yùn)行單元測(cè)試

    • 一旦編寫(xiě)好單元測(cè)試,我們就可以運(yùn)行單元測(cè)試。最簡(jiǎn)單的運(yùn)行方式是在mydict_test.py的最后加上兩行代碼:

      if __name__ == '__main__':
          unittest.main()
      

      這樣就可以把mydict_test.py當(dāng)做正常的python腳本運(yùn)行:

      $ python mydict_test.py
      
    • 另一種方法是在命令行通過(guò)參數(shù)-m unittest直接運(yùn)行單元測(cè)試,這是推薦的做法:

      $ python -m unittest mydict_test
      .....
      ----------------------------------------------------------------------
      Ran 5 tests in 0.000s
      
      OK
      
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容