全局變量補充
python自己添加了些全局變量
print(vars()) """
結果: {'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x00795650>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, '__file__': 'C:/Users/Sullivan/PycharmProjects/q1/day12/global.py', '__cached__': None} """
這些全局變量的作用
#__doc__ 包含py文件的注釋,文件的注釋在——文件內容的最上面用三引號引起來 print(__doc__) #__file__ 包含當前文件的路徑 print(__file__) #__package__ 對於單讀文件的package是沒有意義的,代表當前的py文件在哪幾個文件夾下 print(__package__)#當前文件沒有,會返回None from day2 import homework print(homework.__package__) #文件夾套文件夾時用點"."區分 #結果:day2 #__cached__ 緩存,對於py文件是可以設置一個緩存的,python2里沒有,了解即可 print(__cached__)#當前文件也有,會返回None from day2 import homework print(homework.__cached__) #結果:C:\Users\Sullivan\PycharmProjects\q1\day2\__pycache__\homework.cpython-36.pyc #__name__ 如果當前執行的是當前文件,它的__name__就是__main__,其它的不管是導入還是其它的操作__name__就是文件名 print(__name__) #結果__main__ from day2 import homework print(homework.__name__) #結果:day2.homework #__builtins__ 里邊放的就是內置函數 #__loader__和__spec__ 都是p3中新加的

# 因為程序的入口文件就是用戶當前執行的文件,所以以后寫主程序文件的時候 if __name__ == "__main__": execute() #防止其他文件導入該文件時,執行主函數

#利用sys,os模塊和__file__變量,把路徑添加到system path中 import os,sys p1 = os.path.dirname(__file__) #獲取當前路徑 p2 = "bin" my_dir = os.path.join(p1,p2) #字符串拼接,拼接出新路徑 sys.path.append(my_dir) #添加到system path里面
Python參數傳遞
Python參數傳遞 有引用傳遞&值傳遞兩種
值傳遞:方法調用時,實際參數把它的值傳遞給對應的形式參數,方法執行中形式參數值的改變不影響實際參數的值。
a1 = 520 a2 = a1 print(a1,a2) 結果:520 520 a2 = a1 + 1 print(a1,a2) 結果:520 521
引用傳遞:也稱地址傳遞,在方法調用時,實際上是把參數的引用(傳的是地址,而不是參數的值)傳遞給方法中對應的形式參數,在方法執行中,對形式參數的操作實際上就是對實際參數的操作,方法執行中形式參數值的改變將會影響實際參數的值。
a1 = [1,2] a2 = a1 a2.append(3) print(a1) 結果:[1, 2, 3] print(a2) 結果:[1, 2, 3]
在Python中,數字、字符或者元組等不可變對象類型都屬於值傳遞,而字典dict或者列表list等可變對象類型屬於引用傳遞。
如果要想修改新賦值后原對象不變,則需要用到python的copy模塊,即對象拷貝。對象拷貝又包含淺拷貝和深拷貝。下面用例子來說明
例一
import copy l1 = [[1, 2], 3] l2 = copy.copy(l1) l2.append(4) #因為淺拷貝之拷貝第一層,所以對第一層操作不會影響l1 l2[0].append(5) #因為淺拷貝只拷貝的第一層,所以對第二層的操作還是值傳遞,所以l1和l2中都有列表的第一個元素都會添加一個5 print(l1) print(l2) 結果: [[1, 2, 5], 3] [[1, 2, 5], 3, 4]
例二
l1 = [[1, 2], 3] l3 = copy.deepcopy(l1) l3[0].append(6) print(l1) print(l3) #深拷貝全部拷貝,所以只是l3的值變了,l1並沒有改變 # 結果: [[1, 2], 3] [[1, 2, 6], 3]
從上例可以看出,copy.copy屬於淺拷貝,拷貝的是第一層list,而copy.deepcopy屬於深拷貝,對list所有子元素都進行深拷貝。
字符串格式化
Python的字符串格式化有兩種方式: 百分號方式、format方式
百分號的方式相對來說比較老,而format方式則是比較先進的方式,企圖替換古老的方式,目前兩者並存。
1、百分號方式
%[(name)][flags][width].[precision]typecode
- (name) 可選,用於選擇指定的key
- flags 可選,可供選擇的值有:
- + 右對齊;正數前加正好,負數前加負號;只有這個會在輸出后給整數加上正號(+)
- - 左對齊;正數前無符號,負數前加負號;
- 空格 右對齊;正數前加空格,負數前加負號;
- 0 右對齊;正數前無符號,負數前加負號;用0填充空白處
- width 可選,占有寬度
- .precision 可選,小數點后保留的位數
- typecode 必選
- s,獲取傳入對象的__str__方法的返回值,並將其格式化到指定位置
- r,獲取傳入對象的__repr__方法的返回值,並將其格式化到指定位置
- c,整數:將數字轉換成其unicode對應的值,10進制范圍為 0 <= i <= 1114111(py27則只支持0-255);字符:將字符添加到指定位置
- o,將整數轉換成 八 進制表示,並將其格式化到指定位置
- x,將整數轉換成十六進制表示,並將其格式化到指定位置
- d,將整數、浮點數轉換成 十 進制表示,並將其格式化到指定位置
- e,將整數、浮點數轉換成科學計數法,並將其格式化到指定位置(小寫e)
- E,將整數、浮點數轉換成科學計數法,並將其格式化到指定位置(大寫E)
- f, 將整數、浮點數轉換成浮點數表示,並將其格式化到指定位置(默認保留小數點后6位)
- F,同上
- g,自動調整將整數、浮點數轉換成 浮點型或科學計數法表示(超過6位數用科學計數法),並將其格式化到指定位置(如果是科學計數則是e;)
- G,自動調整將整數、浮點數轉換成 浮點型或科學計數法表示(超過6位數用科學計數法),並將其格式化到指定位置(如果是科學計數則是E;)
- %,當字符串中存在格式化標志時,需要用 %%表示一個百分號
注:Python中百分號格式化是不存在自動將整數轉換成二進制表示的方式
功能示例:
# %[(name)][flags][width].[precision]typecode s = ["i am %s,age %d" % ('ciri',18)] # (name) s = "i am %(n1)s,age %(n2)d" % {"n1":"ciri","n2":16} s = "i am %(n1)s,age %(n1)s" % {"n1":"ciri"} #也可以兩個共用一個值 #flags和width一般放到一起用,一般用不到 #字符 s = "i am %(n1)+10s ellie" % {"n1":"ciri"} s = "i am %(n1)-10s ellie" % {"n1":"ciri"} s = "i am %(n1) 10s ellie" % {"n1":"ciri"} s = "i am %(n1)010s ellie" % {"n1":"ciri"} #數字 s = "age %(n2)+10d ellie" % {"n2":16} s = "age %(n2)+10d ellie" % {"n2":-16} s = "age %(n2)-10d ellie" % {"n2":16} s = "age %(n2)-10d ellie" % {"n2":-16} s = "age %(n2) 10d ellie" % {"n2":16} s = "age %(n2) 10d ellie" % {"n2":-16} s = "age %(n2)010d ellie" % {"n2":16} s = "age %(n2)010d ellie" % {"n2":-16} #precision——默認輸出6位小數 s = "age %f ellie" % 1.2 s = "age %.2f ellie" % 1.2 #指定小數保留幾位
常用格式化:
tpl = "i am %s" % "alex" tpl = "i am %s age %d" % ("alex", 18) tpl = "i am %(name)s age %(age)d" % {"name": "alex", "age": 18} tpl = "percent %.2f" % 99.97623 tpl = "i am %(pp).2f" % {"pp": 123.425556, } tpl = "i am %.2f %%" % {"pp": 123.425556, }
2、Format方式
[[fill]align][sign][#][0][width][,][.precision][type]
都是中括號[],所以參數都可以省略
- fill 【可選】空白處填充的字符
- align 【可選】對齊方式(需配合width使用)
- <,內容左對齊
- >,內容右對齊(默認)
- =,內容右對齊,將符號放置在填充字符的左側,且只對數字類型有效。 即使:符號+填充物+數字
- ^,內容居中
- sign 【可選】有無符號數字
- +,正號加正,負號加負;
- -,正號不變,負號加負;
- 空格 ,正號空格,負號加負;
- # 【可選】對於二進制、八進制、十六進制,如果加上#,會顯示 0b/0o/0x,否則不顯示
- , 【可選】為數字添加分隔符,如:1,000,000
- width 【可選】格式化位所占寬度
- .precision 【可選】小數位保留精度
- type 【可選】格式化類型
- 傳入” 字符串類型 “的參數
- s,格式化字符串類型數據
- 空白,未指定類型,則默認是None,同s
- 傳入“ 整數類型 ”的參數
- b,將10進制整數自動轉換成2進制表示然后格式化
- c,將10進制整數自動轉換為其對應的unicode字符
- d,十進制整數
- o,將10進制整數自動轉換成8進制表示然后格式化;
- x,將10進制整數自動轉換成16進制表示然后格式化(小寫x)
- X,將10進制整數自動轉換成16進制表示然后格式化(大寫X)
- 傳入“ 浮點型或小數類型 ”的參數
- e, 轉換為科學計數法(小寫e)表示,然后格式化;
- E, 轉換為科學計數法(大寫E)表示,然后格式化;
- f , 轉換為浮點型(默認小數點后保留6位)表示,然后格式化;
- F, 轉換為浮點型(默認小數點后保留6位)表示,然后格式化;
- g, 自動在e和f中切換
- G, 自動在E和F中切換
- %,顯示百分比(默認顯示小數點后6位)
- 傳入” 字符串類型 “的參數
常用格式化:
# [[fill]align][sign][#][0][width][,][.precision][type] tpl = "i am {}, age {}".format("seven", 18) tpl = "i am {}, age {}".format(*["seven", 18,'ciri']) #傳入列表,列表的元素可以比前面的內容所 #可以傳索引 tpl = "i am {0}, age {1}".format("seven", 18) tpl = "i am {0}, age {1}".format(*["seven", 18]) #通過name獲取 tpl = "i am {name}, age {age}".format(name="seven", age=18) tpl = "i am {name}, age {age}".format(**{"name": "seven", "age": 18})#傳字典要穿加上** #第一個0表示拿第幾個元素 tpl = "i am {0[0]}, age {0[1]}".format([1, 2, 3], [11, 22, 33]) #分別接收字符串、數字和浮點數 tpl = "i am {:s}, age {:d}, money {:f}".format("seven", 18, 88888.1) tpl = "i am {:s}, age {:d}".format(*["seven", 18]) tpl = "i am {name:s}, age {age:d}".format(name="seven", age=18) tpl = "i am {name:s}, age {age:d}".format(**{"name": "seven", "age": 18}) tpl = "numbers: {:b},{:o},{:d},{:x},{:X}, {:%}".format(15, 15, 15, 15, 15, 15.87623, 2) tpl = "numbers: {0:b},{0:o},{0:d},{0:x},{0:X}, {0:%}".format(15) tpl = "numbers: {num:b},{num:o},{num:d},{num:x},{num:X}, {num:%}".format(num=15)
生成器——generator
列表生成器
# 先到 for x in range(10) 取元素,再把取出來的元素按照 前面的內容(x,x+1,x*2) 進行操作,然后把結果依次放到列表中
a = [x for x in range(10)] print(a) a = [x+1 for x in range(10)] print(a) a = [x*2 for x in range(10)] print(a) 結果: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] [0, 2, 4, 6, 8, 10, 12, 14, 16, 18] [1, 2, 3, 4, 5, 6, 7, 8, 9, 10
賦值一種方法
t = ('ciri',8) a,b = t #值和變量的個數要相同,否則會報錯 print(a) print(b) 結果: ciri 8
如何創建生成器?
方法一:小括號的方式
#把列表生成式的中括號,改成小括號,就變成了一個生成器 s = (x for x in range(10)) print(s) # <generator object <genexpr> at 0x03726360> #輸出的是一個生成器對象,所有的值壓根就沒有被創建出來(重點)
之前的列表生成式生成了10個值,就相當於10盤菜,已經做出來了,想什么時候成就什么時候吃,想什么時候調動就什么時候調用,想吃第幾道就吃第幾道。
生成器就相當於,廚師腦子里的10道菜,想吃的時候才做出來,你不吃就不會做出來,只能按順序吃(0,1...8,9),不能隔着吃,也不能倒着吃
方法二:yield方式
def func(): print('ok') yield 1 print('ok2') yield 2 print(func) # <function func at 0x03163540> 可以看出func還是一個函數 func() #不會執行函數,此時func()是一個生成器對象 print(func()) # <generator object func at 0x034764B0> 可以看出 func加上括號() 是一個生成器對象
yield的執行過程
def func(): print('ok') yield 1 print('ok2') yield 2 next(func()) next(func()) 結果: ok ok2 for i in func(): print(i) 結果: ok 1 ok2 2
如何使用生成器中的對象?
s = (x for x in range(10)) print(s) # 方法一: print(s.__next__())#內部特殊的方法,不建議用 # 方法二: #python3中新加的方法 print(next(s)) #以后就用這種方法 # 方法三: #python2中可以直接調用next方法 print(s.next) # 方法四: for i in s: print(i) #for就是內部調用next方法
斐波那契數列
def fib(max): n,b,a = 0,0,1 while n<max: print(b) #最開始的值 yield b b, a = a, b + a #會先把b+a計算出來 n = n + 1 g = fib(8) next(g) next(g) next(g) next(g)

b, a = a, b + a為什么這個可以,把a的值賦值給b,b+a的值賦值給a b = a a = b + a 這個不行 #因為b, a = a, b + a會先計算出來b+a的值
send方法
send和next的區別就是,send可以給yield前面的變量傳值
def bar(): print('ok') count = yield 1 print(count) #第二次進來,就會直接把send的值,直接傳給yield前的參數count yield 2 b = bar() #b.send('ciri') 會報錯,因為第一次進來,執行到到yield就凍結了,出去了,所以第一次不知道給誰賦值,所以就不能用send傳參數 ret1 = b.send(None) #用send第一次進入時,只能這么寫,等價於next(b) print(ret1) #結果 1 ret2 = b.send('ciri') print(ret2) #結果:2
實例:用yield實現偽並發
#做和吃是同時發生的 import time def consumer(name): print("%s 准備吃包子啦!" % name) while True: baozi = yield print("包子[%s]來了,被[%s]吃了!" %(baozi,name)) def producer(name): c = consumer('A') c2 = consumer('B') next(c) next(c2) print("老子開始准備做包子了") for i in range(10): time.sleep(1) print("做了2個包子") c.send(i) c2.send(i) producer("ciri")
迭代器——iterator
生成器都是迭代器,迭代器不一定是生成器
如何創建一個迭代器?
#iter方法就做一件事情,返回了一個迭代器對象 li = [1,2,3,4] d = iter(li) print(d) # <list_iterator object at 0x034A4770> iterator迭代器,iterable可迭代對象
什么是迭代器?
滿足兩個條件:1.有iter方法 2.有next方法
for循環后面加的是什么?
可迭代對象
什么是可迭代對象?
現象上——能進行 for循環的,都是可迭代對象
本質上——內部有iter方法的,就是可迭代對象
執行迭代器
li = [1,2,3,4] d = iter(li) #iter方法就做了一件事情,返回了一個迭代器對象 print(next(d)) print(next(d))
補充:for循環 執行時的內部操作三件事
- 調用可迭代對象的iter方法,返回一個迭代器對象
- 調用迭代器的next方法
- 處理stopiteration異常
實例:操作文件
f = open('abc.txt','r') for i in f.readlines(): #假如文件很大的話,會全部都放到內存中去 pass for i in f(): #這里的f就是一個可迭代對象,節省內存空間 pass
練習:使用文件讀取,找出文件中最長的行
max(len(x.strip()) for x in open('/hello/abc','r'))
Python的字符串格式化有兩種方式: 百分號方式、format方式
百分號的方式相對來說比較老,而format方式則是比較先進的方式,企圖替換古老的方式,目前兩者並存。