數據類型
Python3 中常見的數據類型有:
- Number(數字)
- String(字符串)
- bool(布爾類型)
- List(列表)
- Tuple(元組)
- Set(集合)
- Dictionary(字典)
Python3 的六個標準數據類型中:
- 不可變數據(3 個):Number(數字)、String(字符串)、Tuple(元組);
- 可變數據(3 個):List(列表)、Dictionary(字典)、Set(集合)。
此外還有一些高級的數據類型,如: 字節數組類型(bytes)。
Number(數字)
Python3 支持 int、float、bool、complex(復數)。
在 Python 中,所有非零的數字和非空的字符串、列表、元組等數據類型都被視為 True,只有 0、空字符串、空列表、空元組等被視為 False。
因此,在進行布爾類型轉換時,需要注意數據類型的真假性。
List(列表)
List(列表) 是 Python 中使用最頻繁的數據類型。
列表可以完成大多數集合類的數據結構實現。列表中元素的類型可以不相同,它支持數字,字符串甚至可以包含列表(所謂嵌套)。
List 內置了有很多方法,例如 append()、pop() 等等。
def reverseWords(input):inputW = input.split(" ")inputW = inputW[-1::-1]output = ' '.join(inputW)return outputif __name__ == "__main__":input = 'I LIKE'rw = reverseWords(input)print(rw)
這個蠻有意思!
Tuple(元組)
元組(tuple)與列表類似,不同之處在于元組的元素不能修改。元組寫在小括號 () 里,元素之間用逗號隔開。
元組中的元素類型也可以不相同:
雖然tuple的元素不可改變,但它可以包含可變的對象,比如list列表。
如果你想創建只有一個元素的元組,需要注意在元素后面添加一個逗號,以區分它是一個元組而不是一個普通的值,這是因為在沒有逗號的情況下,Python會將括號解釋為數學運算中的括號,而不是元組的表示。
因此可以用來存儲一些不希望被修改的數據,例如身份證號、手機號等。
Set(集合)
Python 中的集合(Set)是一種無序、可變的數據類型,用于存儲唯一的元素。
集合中的元素不會重復,并且可以進行交集、并集、差集等常見的集合操作。
在 Python 中,集合使用大括號 {} 表示,元素之間用逗號 , 分隔。
另外,也可以使用 set() 函數創建集合。
注意:創建一個空集合必須用 set() 而不是 { },因為 { } 是用來創建一個空字典。
創建格式:
parame = {value01,value02,...}
或者
set(value)
Dictionary(字典)
字典(dictionary)是Python中另一個非常有用的內置數據類型。
列表是有序的對象集合,字典是無序的對象集合。兩者之間的區別在于:字典當中的元素是通過鍵來存取的,而不是通過偏移存取。
字典是一種映射類型,字典用 { } 標識,它是一個無序的 鍵(key) : 值(value) 的集合。
鍵(key)必須使用不可變類型。
在同一個字典中,鍵(key)必須是唯一的。
dict = {}
dict['one'] = "1 - 菜鳥教程"
dict[2] = "2 - 菜鳥工具"tinydict = {'name': 'runoob','code':1, 'site': 'www.runoob.com'}print (tinydict) # 輸出完整的字典
print (tinydict.keys()) # 輸出所有鍵
print (tinydict.values()) # 輸出所有值
構造函數 dict() 可以直接從鍵值對序列中構建字典如下:
>>> dict([('Runoob', 1), ('Google', 2), ('Taobao', 3)])
{'Runoob': 1, 'Google': 2, 'Taobao': 3}
>>> {x: x**2 for x in (2, 4, 6)}
{2: 4, 4: 16, 6: 36}
>>> dict(Runoob=1, Google=2, Taobao=3)
{'Runoob': 1, 'Google': 2, 'Taobao': 3}
另外,字典類型也有一些內置的函數,例如 clear()、keys()、values() 等。
bytes 類型
在 Python3 中,bytes 類型表示的是不可變的二進制序列(byte sequence)。
與字符串類型不同的是,bytes 類型中的元素是整數值(0 到 255 之間的整數),而不是 Unicode 字符。
bytes 類型通常用于處理二進制數據,比如圖像文件、音頻文件、視頻文件等等。在網絡編程中,也經常使用 bytes 類型來傳輸二進制數據。
創建 bytes 對象的方式有多種,最常見的方式是使用 b 前綴:
x = b"hello"
y = x[1:3] # 切片操作,得到 b"el"
z = x + b"world" # 拼接操作,得到 b"helloworld"
此外,也可以使用 bytes() 函數將其他類型的對象轉換為 bytes 類型。 bytes() 函數的第一個參數是要轉換的對象,第二個參數是編碼方式,如果省略第二個參數,則默認使用 UTF-8 編碼:
x = bytes("hello", encoding="utf-8")
需要注意的是,bytes 類型中的元素是整數值,因此在進行比較操作時需要使用相應的整數值。例如:
x = b"hello"
if x[0] == ord("h"):print("The first element is 'h'")
其中 ord() 函數用于將字符轉換為相應的整數值。
Python數據類型轉換
Python 數據類型轉換可以分為兩種:
- 隱式類型轉換 - 自動完成
- 顯式類型轉換 - 需要使用類型函數來轉換
Python 會將較小的數據類型轉換為較大的數據類型,以避免數據丟失。
判斷和循環
判斷
Python 中用 elif 代替了 else if,所以if語句的關鍵字為:if – elif – else。
使用縮進來劃分語句塊,相同縮進數的語句在一起組成一個語句塊。
在 Python 中沒有 switch…case 語句,但在 Python3.10 版本添加了 match…case,功能也類似。
具體的判斷符號還是以前那些。
在嵌套 if 語句中,可以把 if…elif…else 結構放在另外一個 if…elif…else 結構中。
但是最好別,執行效率太差了。
match…case 的條件判斷,不需要再使用一連串的 if-else 來判斷了。這玩意和switch case差不多。
循環
需要注意冒號和縮進。另外,在 Python 中沒有 do…while 循環。
我們可以通過設置條件表達式永遠不為 false 來實現無限循環
while var == 1 :
while 循環使用 else 語句
#!/usr/bin/python3count = 0
while count < 5:print (count, " 小于 5")count = count + 1
else:print (count, " 大于或等于 5")
類似 if 語句的語法,如果你的 while 循環體中只有一條語句,你可以將該語句與 while 寫在同一行中
整數范圍值可以配合 range() 函數使用
在 Python 中,for…else 語句用于在循環結束后執行一段代碼。
for 實例中使用了 break 語句,break 語句用于跳出當前循環體,不會執行 else 子句
如果你需要遍歷數字序列,可以使用內置 range() 函數。它會生成數列
你也可以使用 range() 指定區間的值:
也可以使 range() 以指定數字開始并指定不同的增量(甚至可以是負數,有時這也叫做’步長’):
您可以結合 range() 和 len() 函數以遍歷一個序列的索引,如下所示:
pass 不做任何事情,一般用做占位語句,如下實例
Python pass是空語句,是為了保持程序結構的完整性。
關鍵字end可以用于將結果輸出到同一行,或者在輸出的末尾添加不同的字符。
Python 推導式
Python 推導式是一種獨特的數據處理方式,可以從一個數據序列構建另一個新的數據序列的結構體。
Python 支持各種數據結構的推導式:
- 列表(list)推導式
- 字典(dict)推導式
- 集合(set)推導式
- 元組(tuple)推導式
列表推導式
[表達式 for 變量 in 列表]
[out_exp_res for out_exp in input_list]
或者
[表達式 for 變量 in 列表 if 條件]
[out_exp_res for out_exp in input_list if condition]
>>> multiples = [i for i in range(30) if i % 3 == 0]
>>> print(multiples)
[0, 3, 6, 9, 12, 15, 18, 21, 24, 27]
字典推導式
{ key_expr: value_expr for value in collection }或{ key_expr: value_expr for value in collection if condition }
集合推導式
{ expression for item in Sequence }
或
{ expression for item in Sequence if conditional }
元組推導式(生成器表達式)
(expression for item in Sequence )
或
(expression for item in Sequence if conditional )
元組推導式和列表推導式的用法也完全相同,只是元組推導式是用 () 圓括號將各部分括起來,而列表推導式用的是中括號 [],另外元組推導式返回的結果是一個生成器對象。
迭代器與生成器
迭代器
迭代是 Python 最強大的功能之一,是訪問集合元素的一種方式。
迭代器是一個可以記住遍歷的位置的對象。
迭代器對象從集合的第一個元素開始訪問,直到所有的元素被訪問完結束。迭代器只能往前不會后退。
迭代器有兩個基本的方法:iter() 和 next()。
字符串,列表或元組對象都可用于創建迭代器:
創建一個迭代器
把一個類作為一個迭代器使用需要在類中實現兩個方法 iter() 與 next() 。
如果你已經了解的面向對象編程,就知道類都有一個構造函數,Python 的構造函數為 init(), 它會在對象初始化的時候執行。
iter() 方法返回一個特殊的迭代器對象, 這個迭代器對象實現了 next() 方法并通過 StopIteration 異常標識迭代的完成。
next() 方法(Python 2 里是 next())會返回下一個迭代器對象。
創建一個返回數字的迭代器,初始值為 1,逐步遞增 1:
class MyNumbers:def __iter__(self):self.a = 1return selfdef __next__(self):x = self.aself.a += 1return xmyclass = MyNumbers()
myiter = iter(myclass)print(next(myiter))
print(next(myiter))
print(next(myiter))
print(next(myiter))
print(next(myiter))
這個其實換個名字也OK啊。
StopIteration 異常用于標識迭代的完成,防止出現無限循環的情況,在 next() 方法中我們可以設置在完成指定循環次數后觸發 StopIteration 異常來結束迭代。
class MyNumbers:def __iter__(self):self.a = 1return selfdef __next__(self):if self.a <= 20:x = self.aself.a += 1return xelse:raise StopIterationmyclass = MyNumbers()
myiter = iter(myclass)for x in myiter:print(x)
生成器
在 Python 中,使用了 yield 的函數被稱為生成器(generator)。
yield 是一個關鍵字,用于定義生成器函數,生成器函數是一種特殊的函數,可以在迭代過程中逐步產生值,而不是一次性返回所有結果。
跟普通函數不同的是,生成器是一個返回迭代器的函數,只能用于迭代操作,更簡單點理解生成器就是一個迭代器。
當在生成器函數中使用 yield 語句時,函數的執行將會暫停,并將 yield 后面的表達式作為當前迭代的值返回。
然后,每次調用生成器的 next() 方法或使用 for 循環進行迭代時,函數會從上次暫停的地方繼續執行,直到再次遇到 yield 語句。這樣,生成器函數可以逐步產生值,而不需要一次性計算并返回所有結果。
調用一個生成器函數,返回的是一個迭代器對象。
def countdown(n):while n > 0:yield nn -= 1# 創建生成器對象
generator = countdown(5)# 通過迭代生成器獲取值
print(next(generator)) # 輸出: 5
print(next(generator)) # 輸出: 4
print(next(generator)) # 輸出: 3# 使用 for 循環迭代生成器
for value in generator:print(value) # 輸出: 2 1
生成器函數的優勢是它們可以按需生成值,避免一次性生成大量數據并占用大量內存。此外,生成器還可以與其他迭代工具(如for循環)無縫配合使用,提供簡潔和高效的迭代方式。
常用函數
函數是組織好的,可重復使用的,用來實現單一,或相關聯功能的代碼段。
函數能提高應用的模塊性,和代碼的重復利用率。你已經知道Python提供了許多內建函數,比如print()。但你也可以自己創建函數,這被叫做用戶自定義函數。
主要是理解函數這玩意和別的語言的結構和規則上有什么區別就行。
- 函數代碼塊以 def 關鍵詞開頭,后接函數標識符名稱和圓括號 ()。
- 任何傳入參數和自變量必須放在圓括號中間,圓括號之間可以用于定義參數。
- 函數的第一行語句可以選擇性地使用文檔字符串—用于存放函數說明。
- 函數內容以冒號 : 起始,并且縮進。
- return [表達式] 結束函數,選擇性地返回一個值給調用方,不帶表達式的 return 相當于返回 None。
在 python 中,類型屬于對象,對象有不同類型的區分,變量是沒有類型的:
以上代碼中,[1,2,3] 是 List 類型,“Runoob” 是 String 類型,而變量 a 是沒有類型,她僅僅是一個對象的引用(一個指針),可以是指向 List 類型對象,也可以是指向 String 類型對象。
可更改(mutable)與不可更改(immutable)對象
在 python 中,strings, tuples, 和 numbers 是不可更改的對象,而 list,dict 等則是可以修改的對象。
-
不可變類型:變量賦值 a=5 后再賦值 a=10,這里實際是新生成一個 int 值對象 10,再讓 a 指向它,而 5 被丟棄,不是改變 a 的值,相當于新生成了 a。
-
可變類型:變量賦值 la=[1,2,3,4] 后再賦值 la[2]=5 則是將 list la 的第三個元素值更改,本身la沒有動,只是其內部的一部分值被修改了。
調用函數時可使用的正式參數類型:
- 必需參數
- 關鍵字參數
- 默認參數
- 不定長參數
必需參數須以正確的順序傳入函數。調用時的數量必須和聲明時的一樣。
關鍵字參數和函數調用關系緊密,函數調用使用關鍵字參數來確定傳入的參數值。
使用關鍵字參數允許函數調用時參數的順序與聲明時不一致,因為 Python 解釋器能夠用參數名匹配參數值。
調用函數時,如果沒有傳遞參數,則會使用默認參數。以下實例中如果沒有傳入 age 參數,則使用默認值:
你可能需要一個函數能處理比當初聲明時更多的參數。這些參數叫做不定長參數,和上述 2 種參數不同,聲明時不會命名。
def functionname([formal_args,] *var_args_tuple ):"函數_文檔字符串"function_suitereturn [expression]
#!/usr/bin/python3# 可寫函數說明
def printinfo( arg1, *vartuple ):"打印任何傳入的參數"print ("輸出: ")print (arg1)print (vartuple)# 調用printinfo 函數
printinfo( 70, 60, 50 )
還有一種就是參數帶兩個星號 **基本語法如下:
def functionname([formal_args,] **var_args_dict ):"函數_文檔字符串"function_suitereturn [expression]
加了兩個星號 ** 的參數會以字典的形式導入。
#!/usr/bin/python3# 可寫函數說明
def printinfo( arg1, **vardict ):"打印任何傳入的參數"print ("輸出: ")print (arg1)print (vardict)# 調用printinfo 函數
printinfo(1, a=2,b=3)
聲明函數時,參數中星號 * 可以單獨出現,例如:
def f(a,b,*,c):return a+b+c
如果單獨出現星號 *,則星號 * 后的參數必須用關鍵字傳入:
>>> def f(a,b,*,c):
... return a+b+c
...
>>> f(1,2,3) # 報錯
Traceback (most recent call last):File "<stdin>", line 1, in <module>
TypeError: f() takes 2 positional arguments but 3 were given
>>> f(1,2,c=3) # 正常
6
>>>
匿名函數
Python 使用 lambda 來創建匿名函數。
所謂匿名,意即不再使用 def 語句這樣標準的形式定義一個函數。
- lambda 只是一個表達式,函數體比 def 簡單很多。
- lambda 的主體是一個表達式,而不是一個代碼塊。僅僅能在 lambda 表達式中封裝有限的邏輯進去。
- lambda 函數擁有自己的命名空間,且不能訪問自己參數列表之外或全局命名空間里的參數。
- 雖然 lambda 函數看起來只能寫一行,卻不等同于 C 或 C++ 的內聯函數,內聯函數的目的是調用小函數時不占用棧內存從而減少函數調用的開銷,提高代碼的執行速度。
lambda [arg1 [,arg2,.....argn]]:expression
x = lambda a : a + 10
print(x(5))
#!/usr/bin/python3# 可寫函數說明
sum = lambda arg1, arg2: arg1 + arg2# 調用sum函數
print ("相加后的值為 : ", sum( 10, 20 ))
print ("相加后的值為 : ", sum( 20, 20 ))
我們可以將匿名函數封裝在一個函數內,這樣可以使用同樣的代碼來創建多個匿名函數。
以下實例將匿名函數封裝在 myfunc 函數中,通過傳入不同的參數來創建不同的匿名函數:
def myfunc(n):return lambda a : a * nmydoubler = myfunc(2)
mytripler = myfunc(3)print(mydoubler(11))
print(mytripler(11))
return [表達式] 語句用于退出函數,選擇性地向調用方返回一個表達式。不帶參數值的 return 語句返回 None。
強制位置參數
打印
數據輸入
數字函數
字符串函數
列表函數
字典函數
sorted排序
eval表達式
Python 裝飾器
裝飾器(decorators)是 Python 中的一種高級功能,它允許你動態地修改函數或類的行為。
裝飾器是一種函數,它接受一個函數作為參數,并返回一個新的函數或修改原來的函數。
裝飾器的語法使用 @decorator_name 來應用在函數或方法上。
暫時用不到,等需要了再學習!
數據結構
Python中列表是可變的,這是它區別于字符串和元組的最重要的特點,一句話概括即:列表可以修改,而字符串和元組不能。
列表
- 將列表當做堆棧使用
- 將列表當作隊列使用
- 列表推導式
- 嵌套列表
- del 語句
組在輸出時總是有括號的,以便于正確表達嵌套結構。在輸入時可能有或沒有括號, 不過括號通常是必須的(如果元組是更大的表達式的一部分)。
元組和序列
集合
集合是一個無序不重復元素的集。基本功能包括關系測試和消除重復元素。
可以用大括號({})創建集合。注意:如果要創建一個空集合,你必須用 set() 而不是 {} ;后者創建一個空的字典,下一節我們會介紹這個數據結構。
字典
序列是以連續的整數為索引,與此不同的是,字典以關鍵字為索引,關鍵字可以是任意不可變類型,通常用字符串或數值。
理解字典的最佳方式是把它看做無序的鍵=>值對集合。在同一個字典之內,關鍵字必須是互不相同。
一對大括號創建一個空的字典:{}。
遍歷技巧
在字典中遍歷時,關鍵字和對應的值可以使用 items() 方法同時解讀出來:
>>> knights = {'gallahad': 'the pure', 'robin': 'the brave'}
>>> for k, v in knights.items():
... print(k, v)
...
gallahad the pure
robin the brave
在序列中遍歷時,索引位置和對應值可以使用 enumerate() 函數同時得到:
>>> for i, v in enumerate(['tic', 'tac', 'toe']):
... print(i, v)
...
0 tic
1 tac
2 toe
同時遍歷兩個或更多的序列,可以使用 zip() 組合:
>>> questions = ['name', 'quest', 'favorite color']
>>> answers = ['lancelot', 'the holy grail', 'blue']
>>> for q, a in zip(questions, answers):
... print('What is your {0}? It is {1}.'.format(q, a))
...
What is your name? It is lancelot.
What is your quest? It is the holy grail.
What is your favorite color? It is blue.
要按順序遍歷一個序列,使用 sorted() 函數返回一個已排序的序列,并不修改原值:
>>> basket = ['apple', 'orange', 'apple', 'pear', 'orange', 'banana']
>>> for f in sorted(set(basket)):
... print(f)
...
apple
banana
orange
pear
模塊
模塊是一個包含所有你定義的函數和變量的文件,其后綴名是.py。模塊可以被別的程序引入,以使用該模塊中的函數等功能。這也是使用 python 標準庫的方法。
和golang好像
#!/usr/bin/python3
# 文件名: using_sys.pyimport sysprint('命令行參數如下:')
for i in sys.argv:print(i)print('\n\nPython 路徑為:', sys.path, '\n')
- 1、import sys 引入 python 標準庫中的 sys.py 模塊;這是引入某一模塊的方法。
- 2、sys.argv 是一個包含命令行參數的列表。
- 3、sys.path 包含了一個 Python 解釋器自動查找所需模塊的路徑的列表。
一個模塊只會被導入一次,不管你執行了多少次 import。這樣可以防止導入模塊被一遍又一遍地執行。
當我們使用 import 語句的時候,Python 解釋器是怎樣找到對應的文件的呢?
這就涉及到 Python 的搜索路徑,搜索路徑是由一系列目錄名組成的,Python 解釋器就依次從這些目錄中去尋找所引入的模塊。
這看起來很像環境變量,事實上,也可以通過定義環境變量的方式來確定搜索路徑。
搜索路徑是在 Python 編譯或安裝的時候確定的,安裝新的庫應該也會修改。搜索路徑被存儲在 sys 模塊中的 path 變量,做一個簡單的實驗,在交互式解釋器中,輸入以下代碼:
>>> import sys
>>> sys.path
['', '/usr/lib/python3.4', '/usr/lib/python3.4/plat-x86_64-linux-gnu', '/usr/lib/python3.4/lib-dynload', '/usr/local/lib/python3.4/dist-packages', '/usr/lib/python3/dist-packages']
>>>
sys.path 輸出是一個列表,其中第一項是空串 ‘’,代表當前目錄(若是從一個腳本中打印出來的話,可以更清楚地看出是哪個目錄),亦即我們執行python解釋器的目錄(對于腳本的話就是運行的腳本所在的目錄)。
因此若像我一樣在當前目錄下存在與要引入模塊同名的文件,就會把要引入的模塊屏蔽掉。
了解了搜索路徑的概念,就可以在腳本中修改sys.path來引入一些不在搜索路徑中的模塊。
- from … import 語句
- from … import * 語句
模塊除了方法定義,還可以包括可執行的代碼。這些代碼一般用來初始化這個模塊。這些代碼只有在第一次被導入時才會被執行。
每個模塊有各自獨立的符號表,在模塊內部為所有的函數當作全局符號表來使用。
所以,模塊的作者可以放心大膽的在模塊內部使用這些全局變量,而不用擔心把其他用戶的全局變量搞混。
從另一個方面,當你確實知道你在做什么的話,你也可以通過 modname.itemname 這樣的表示法來訪問模塊內的函數。
模塊是可以導入其他模塊的。在一個模塊(或者腳本,或者其他地方)的最前面使用 import 來導入一個模塊,當然這只是一個慣例,而不是強制的。被導入的模塊的名稱將被放入當前操作的模塊的符號表中。
還有一種導入的方法,可以使用 import 直接把模塊內(函數,變量的)名稱導入到當前操作模塊。
這種導入的方法不會把被導入的模塊的名稱放在當前的字符表中(所以在這個例子里面,fibo 這個名稱是沒有定義的)。
這還有一種方法,可以一次性的把模塊中的所有(函數,變量)名稱都導入到當前模塊的字符表:
>>> from fibo import fib, fib2
>>> fib(500)
1 1 2 3 5 8 13 21 34 55 89 144 233 377
這將把所有的名字都導入進來,但是那些由單一下劃線(_)開頭的名字不在此例。大多數情況, Python程序員不使用這種方法,因為引入的其它來源的命名,很可能覆蓋了已有的定義。
>>> from fibo import *
>>> fib(500)
1 1 2 3 5 8 13 21 34 55 89 144 233 377
__name__屬性
一個模塊被另一個程序第一次引入時,其主程序將運行。如果我們想在模塊被引入時,模塊中的某一程序塊不執行,我們可以用__name__屬性來使該程序塊僅在該模塊自身運行時執行。
#!/usr/bin/python3
# Filename: using_name.pyif __name__ == '__main__':print('程序自身在運行')
else:print('我來自另一模塊')
說明: 每個模塊都有一個__name__屬性,當其值是’main’時,表明該模塊自身在運行,否則是被引入。
說明:name 與 main 底下是雙下劃線, _ _ 是這樣去掉中間的那個空格。
dir() 函數
內置的函數 dir() 可以找到模塊內定義的所有名稱。以一個字符串列表的形式返回:
標準模塊
Python 本身帶著一些標準的模塊庫,在 Python 庫參考文檔中將會介紹到(就是后面的"庫參考文檔")。
有些模塊直接被構建在解析器里,這些雖然不是一些語言內置的功能,但是他卻能很高效的使用,甚至是系統級調用也沒問題。
這些組件會根據不同的操作系統進行不同形式的配置,比如 winreg 這個模塊就只會提供給 Windows 系統。
應該注意到這有一個特別的模塊 sys ,它內置在每一個 Python 解析器中。變量 sys.ps1 和 sys.ps2 定義了主提示符和副提示符所對應的字符串:
包
包是一種管理 Python 模塊命名空間的形式,采用"點模塊名稱"。
比如一個模塊的名稱是 A.B, 那么他表示一個包 A中的子模塊 B 。
就好像使用模塊的時候,你不用擔心不同模塊之間的全局變量相互影響一樣,采用點模塊名稱這種形式也不用擔心不同庫之間的模塊重名的情況。
這樣不同的作者都可以提供 NumPy 模塊,或者是 Python 圖形庫。
不妨假設你想設計一套統一處理聲音文件和數據的模塊(或者稱之為一個"包")。
現存很多種不同的音頻文件格式(基本上都是通過后綴名區分的,例如: .wav,:file:.aiff,:file:.au,),所以你需要有一組不斷增加的模塊,用來在不同的格式之間轉換。
并且針對這些音頻數據,還有很多不同的操作(比如混音,添加回聲,增加均衡器功能,創建人造立體聲效果),所以你還需要一組怎么也寫不完的模塊來處理這些操作。
這里給出了一種可能的包結構(在分層的文件系統中):
sound/ 頂層包__init__.py 初始化 sound 包formats/ 文件格式轉換子包__init__.pywavread.pywavwrite.pyaiffread.pyaiffwrite.pyauread.pyauwrite.py...effects/ 聲音效果子包__init__.pyecho.pysurround.pyreverse.py...filters/ filters 子包__init__.pyequalizer.pyvocoder.pykaraoke.py...
從一個包中導入*
如果我們使用 from sound.effects import * 會發生什么呢?
Python 會進入文件系統,找到這個包里面所有的子模塊,然后一個一個的把它們都導入進來。
但這個方法在 Windows 平臺上工作的就不是非常好,因為 Windows 是一個不區分大小寫的系統。
在 Windows 平臺上,我們無法確定一個叫做 ECHO.py 的文件導入為模塊是 echo 還是 Echo,或者是 ECHO。
為了解決這個問題,我們只需要提供一個精確包的索引。
導入語句遵循如下規則:如果包定義文件 init.py 存在一個叫做 all 的列表變量,那么在使用 from package import * 的時候就把這個列表中的所有名字作為包內容導入。
作為包的作者,可別忘了在更新包之后保證 all 也更新了啊。
以下實例在 file:sounds/effects/init.py 中包含如下代碼:
__all__ = ["echo", "surround", "reverse"]
這表示當你使用from sound.effects import *這種用法時,你只會導入包里面這三個子模塊。
如果 all 真的沒有定義,那么使用from sound.effects import *這種語法的時候,就不會導入包 sound.effects 里的任何子模塊。他只是把包sound.effects和它里面定義的所有內容導入進來(可能運行__init__.py里定義的初始化代碼)。
這會把 init.py 里面定義的所有名字導入進來。并且他不會破壞掉我們在這句話之前導入的所有明確指定的模塊。看下這部分代碼:
記住,使用 from Package import specific_submodule 這種方法永遠不會有錯。事實上,這也是推薦的方法。除非是你要導入的子模塊有可能和其他包的子模塊重名。
包還提供一個額外的屬性__path__。這是一個目錄列表,里面每一個包含的目錄都有為這個包服務的__init__.py,你得在其他__init__.py被執行前定義哦。可以修改這個變量,用來影響包含在包里面的模塊和子包。
這個功能并不常用,一般用來擴展包里面的模塊。
輸入和輸出
輸出格式美化
Python兩種輸出值的方式: 表達式語句和 print() 函數。
第三種方式是使用文件對象的 write() 方法,標準輸出文件可以用 sys.stdout 引用。
如果你希望輸出的形式更加多樣,可以使用 str.format() 函數來格式化輸出值。
如果你希望將輸出的值轉成字符串,可以使用 repr() 或 str() 函數來實現。
- str(): 函數返回一個用戶易讀的表達形式。
- repr(): 產生一個解釋器易讀的表達形式。
>>> s = 'Hello, Runoob'
>>> str(s)
'Hello, Runoob'
>>> repr(s)
"'Hello, Runoob'"
>>> str(1/7)
'0.14285714285714285'
>>> x = 10 * 3.25
>>> y = 200 * 200
>>> s = 'x 的值為: ' + repr(x) + ', y 的值為:' + repr(y) + '...'
>>> print(s)
x 的值為: 32.5, y 的值為:40000...
>>> # repr() 函數可以轉義字符串中的特殊字符
... hello = 'hello, runoob\n'
>>> hellos = repr(hello)
>>> print(hellos)
'hello, runoob\n'
>>> # repr() 的參數可以是 Python 的任何對象
... repr((x, y, ('Google', 'Runoob')))
"(32.5, 40000, ('Google', 'Runoob'))"
str.format() 的基本使用如下:
括號及其里面的字符 (稱作格式化字段) 將會被 format() 中的參數替換。
!a (使用 ascii()), !s (使用 str()) 和 !r (使用 repr()) 可以用于在格式化某個值之前對其進行轉化:
如果在 format() 中使用了關鍵字參數, 那么它們的值會指向使用該名字的參數。
位置及關鍵字參數可以任意的結合:
!a (使用 ascii()), !s (使用 str()) 和 !r (使用 repr()) 可以用于在格式化某個值之前對其進行轉化:
因為 str.format() 是比較新的函數, 大多數的 Python 代碼仍然使用 % 操作符。但是因為這種舊式的格式化最終會從該語言中移除, 應該更多的使用 str.format().
讀取鍵盤輸入
Python 提供了 input() 內置函數從標準輸入讀入一行文本,默認的標準輸入是鍵盤。
#!/usr/bin/python3str = input("請輸入:");
print ("你輸入的內容是: ", str)
讀和寫文件
open() 將會返回一個 file 對象,基本語法格式如下:
open(filename, mode)
- filename:包含了你要訪問的文件名稱的字符串值。
- mode:決定了打開文件的模式:只讀,寫入,追加等。所有可取值見如下的完全列表。這個參數是非強制的,默認文件訪問模式為只讀?。
文件對象的方法
f.read()
為了讀取一個文件的內容,調用 f.read(size), 這將讀取一定數目的數據, 然后作為字符串或字節對象返回。
size 是一個可選的數字類型的參數。 當 size 被忽略了或者為負, 那么該文件的所有內容都將被讀取并且返回。
f.readline()
f.readline() 會從文件中讀取單獨的一行。換行符為 ‘\n’。f.readline() 如果返回一個空字符串, 說明已經已經讀取到最后一行。
f.readlines()
f.readlines() 將返回該文件中包含的所有行。
如果設置可選參數 sizehint, 則讀取指定長度的字節, 并且將這些字節按行分割。
f.write()
f.write(string) 將 string 寫入到文件中, 然后返回寫入的字符數。
f.tell()
f.tell() 用于返回文件當前的讀/寫位置(即文件指針的位置)。文件指針表示從文件開頭開始的字節數偏移量。
f.tell() 返回一個整數,表示文件指針的當前位置。
f.seek()
如果要改變文件指針當前的位置, 可以使用 f.seek(offset, from_what) 函數。
f.seek(offset, whence) 用于移動文件指針到指定位置。
offset 表示相對于 whence 參數的偏移量,from_what 的值, 如果是 0 表示開頭, 如果是 1 表示當前位置, 2 表示文件的結尾,例如:
seek(x,0) : 從起始位置即文件首行首字符開始移動 x 個字符
seek(x,1) : 表示從當前位置往后移動x個字符
seek(-x,2):表示從文件的結尾往前移動x個字符
f.close()
在文本文件中 (那些打開文件的模式下沒有 b 的), 只會相對于文件起始位置進行定位。
當你處理完一個文件后, 調用 f.close() 來關閉文件并釋放系統的資源,如果嘗試再調用該文件,則會拋出異常。
當處理一個文件對象時, 使用 with 關鍵字是非常好的方式。在結束后, 它會幫你正確的關閉文件。 而且寫起來也比 try - finally 語句塊要簡短:
文件對象還有其他方法, 如 isatty() 和 trucate(), 但這些通常比較少用。
pickle 模塊
python的pickle模塊實現了基本的數據序列和反序列化。
通過pickle模塊的序列化操作我們能夠將程序中運行的對象信息保存到文件中去,永久存儲。
通過pickle模塊的反序列化操作,我們能夠從文件中創建上一次程序保存的對象。
OS 文件/目錄方法
os 模塊提供了非常豐富的方法用來處理文件和目錄。
錯誤和異常
這個部分我覺得還是蠻有意義的,因為需要檢測錯誤機制,
Python 有兩種錯誤很容易辨認:語法錯誤和異常。
Python assert(斷言)用于判斷一個表達式,在表達式條件為 false 的時候觸發異常。
異常處理
try/except
異常捕捉可以使用 try/except 語句。
try 語句按照如下方式工作;
-
首先,執行 try 子句(在關鍵字 try 和關鍵字 except 之間的語句)。
-
如果沒有異常發生,忽略 except 子句,try 子句執行后結束。
-
如果在執行 try 子句的過程中發生了異常,那么 try 子句余下的部分將被忽略。如果異常的類型和 except 之后的名稱相符,那么對應的 except 子句將被執行。
-
如果一個異常沒有與任何的 except 匹配,那么這個異常將會傳遞給上層的 try 中。
一個 try 語句可能包含多個except子句,分別來處理不同的特定的異常。最多只有一個分支會被執行。
一個except子句可以同時處理多個異常,這些異常將被放在一個括號里成為一個元組,例如:
except (RuntimeError, TypeError, NameError):pass
最后一個except子句可以忽略異常的名稱,它將被當作通配符使用。你可以使用這種方法打印一個錯誤信息,然后再次把異常拋出。
import systry:f = open('myfile.txt')s = f.readline()i = int(s.strip())
except OSError as err:print("OS error: {0}".format(err))
except ValueError:print("Could not convert data to an integer.")
except:print("Unexpected error:", sys.exc_info()[0])raise
try/except…else
try/except 語句還有一個可選的 else 子句,如果使用這個子句,那么必須放在所有的 except 子句之后。
else 子句將在 try 子句沒有發生任何異常的時候執行。
以下實例在 try 語句中判斷文件是否可以打開,如果打開文件時正常的沒有發生異常則執行 else 部分的語句,讀取文件內容:
try-finally 語句
try-finally 語句無論是否發生異常都將執行最后的代碼。
try:runoob()
except AssertionError as error:print(error)
else:try:with open('file.log') as file:read_data = file.read()except FileNotFoundError as fnf_error:print(fnf_error)
finally:print('這句話,無論異常是否發生都會執行。')
拋出異常
Python 使用 raise 語句拋出一個指定的異常。
用戶自定義異常
你可以通過創建一個新的異常類來擁有自己的異常。異常類繼承自 Exception 類,可以直接繼承,或者間接繼承,例如:
>>> class MyError(Exception):def __init__(self, value):self.value = valuedef __str__(self):return repr(self.value)>>> try:raise MyError(2*2)except MyError as e:print('My exception occurred, value:', e.value)My exception occurred, value: 4
>>> raise MyError('oops!')
Traceback (most recent call last):File "<stdin>", line 1, in ?
__main__.MyError: 'oops!'
在這個例子中,類 Exception 默認的 init() 被覆蓋。
當創建一個模塊有可能拋出多種不同的異常時,一種通常的做法是為這個包建立一個基礎異常類,然后基于這個基礎類為不同的錯誤情況創建不同的子類:
大多數的異常的名字都以"Error"結尾,就跟標準的異常命名一樣。
定義清理行為
以上例子不管 try 子句里面有沒有發生異常,finally 子句都會執行。
如果一個異常在 try 子句里(或者在 except 和 else 子句里)被拋出,而又沒有任何的 except 把它截住,那么這個異常會在 finally 子句執行后被拋出。
預定義的清理行為
一些對象定義了標準的清理行為,無論系統是否成功的使用了它,一旦不需要它了,那么這個標準的清理行為就會執行。
下面這個例子展示了嘗試打開一個文件,然后把內容打印到屏幕上:
for line in open("myfile.txt"):print(line, end="")
關鍵詞 with 語句就可以保證諸如文件之類的對象在使用完之后一定會正確的執行他的清理方法:
with open("myfile.txt") as f:for line in f:print(line, end="")
以上這段代碼執行完畢后,就算在處理過程中出問題了,文件 f 總是會關閉。