2nd Python基礎(chǔ)語(yǔ)法的拓展補(bǔ)充

第二章 基礎(chǔ)語(yǔ)法的補(bǔ)充拓展
2.1 關(guān)于變量的機(jī)制
2.1.1 變量的創(chuàng)建和修改
當(dāng)Python
創(chuàng)建一個(gè)變量時(shí),我們假設(shè)是name
,賦值為zm
,變量的值(zm
)就會(huì)存儲(chǔ)在計(jì)算機(jī)的內(nèi)存中,而該變量的變量名(name
)就會(huì)指向變量數(shù)據(jù)(zm
)存放的內(nèi)存地址,我們?cè)诖a中可以通過(guò)id(name)
來(lái)查看變量name
的內(nèi)存地址。
現(xiàn)在我們修改name
的值,修改成zhuming
。此時(shí),zhuming
放到內(nèi)存的一個(gè)新的地方,然后name
指向的內(nèi)存地址改變成zhuming
的內(nèi)存地址。那么就意味著一開(kāi)始的zm
就已經(jīng)斷了聯(lián)系了,Python
解釋器有自動(dòng)垃圾回收機(jī)制,在一定時(shí)間內(nèi)就會(huì)刷新內(nèi)存,將沒(méi)有引用關(guān)系的變量值(zm
)清除掉,恢復(fù)成可用的存儲(chǔ)位置。
2.1.2 變量的指向關(guān)系
現(xiàn)在,創(chuàng)建變量name
并賦值為zm
,再創(chuàng)建一個(gè)新的變量name1
,賦值為變量name
,此時(shí)的對(duì)應(yīng)關(guān)系改怎么解釋。
name1
賦值為name
時(shí),name1
和name
就同時(shí)指向name
的內(nèi)存地址(name1
和name
都指向zm
的內(nèi)存地址,此時(shí)name1
和name
無(wú)任何關(guān)系);
我們這時(shí)候再修改name
的值為zhuming
,現(xiàn)在輸出name
和name1
,我們可以看到name
輸出為zhuming
,name1
輸出為zm
。
2.2 身份運(yùn)算和None
python
中有很多數(shù)據(jù)類型,查看一個(gè)數(shù)據(jù)類型的方法有type()
。
?name = 'zm'
?age = 1
?
?print(type(age)) # <class 'int'>
?print(type(name)) # <class 'str'>
2.2.1 身份運(yùn)算
需要用到is
和is not
運(yùn)算符,可以理解為是和不是。
?name = 'zm'
?print(type(name) is str) # True
?print(type(name) is int) # False
?print(type(name) is not int) # True
?print(type(name) is not str) # False
2.2.2 None
None
表示什么都沒(méi)有,為空。
在程序中的用處:很多時(shí)候創(chuàng)建出來(lái)的變量是給用戶輸入的數(shù)據(jù)來(lái)賦值的,但是我們?nèi)绻麅H僅創(chuàng)建變量,不賦值就會(huì)報(bào)錯(cuò)。我們就會(huì)用None
來(lái)賦值,表示現(xiàn)在值為空,后面會(huì)賦值的意思。
2.3 三元運(yùn)算
?a = 10
?b = 5
?
?# 一般代碼
?if a > 15:
? ? ?c = a
?else:
? ? ?c = b
?
?# 三元運(yùn)算 表示如果a > 15,d=a;否則d=b
?d = a if a > 15 else b
三元運(yùn)算的公式可以看為變量名 = 值1 if 條件A else 值2
2.4 細(xì)說(shuō)數(shù)據(jù)類型-列表
前面說(shuō)了列表的基本知識(shí)(1.3.4 列表),可以回顧一下。下面就做出一些補(bǔ)充。
2.4.1 列表的增
append(value)
:追加,需要一個(gè)參數(shù),將value
插入在末尾insert(index, value)
:插入,需要兩個(gè)參數(shù),將value
插入到index
的位置,原來(lái)此位置以及后面的元素都往后挪1
。extend(new_list)
:合并,需要一個(gè)列表參數(shù),將new_list
與調(diào)用此方法的
2.4.2 列表的刪
del
:Python自帶的刪除方法,不需調(diào)用。del
后面跟刪除的元素就好。eg:del names[1]
pop([index])
:刪除,不加參數(shù)就默認(rèn)刪除列表的最后一個(gè)元素;加參數(shù)就會(huì)刪除索引為index
的元素。如果傳進(jìn)的index
值越界就會(huì)報(bào)錯(cuò)。eg:names.pop() names.pop(3)
。clear()
:清空,不用參數(shù),直接將列表清空,變成空列表(可以print()
空列表看看,clear()
返回的不是None
)。eg:names.clear() ?# names為[] 即為空列表
。remove(value)
:從左到右刪除第一個(gè)匹配到的value
。刪掉不存在的值也會(huì)報(bào)錯(cuò)。
學(xué)過(guò)下面列表的查方法之一的index()
之后,我們可以搭配起來(lái)實(shí)現(xiàn),通過(guò)元素值來(lái)匹配刪除指定元素。
?names = ['zm', 'wzw', 'yz', 'zk']
?names.pop(names.index('wzw')) ?# 刪除列表中的wzw
2.4.3 列表的改
和前面一樣,還是以索引的方式來(lái)修改值。eg:names[0] = 'zm'
2.4.4 列表的查
index(value)
:從左到右查看第一個(gè)value
的下標(biāo),若列表中無(wú)value
的值,會(huì)報(bào)錯(cuò)的。eg:names.index('zm')
count(value)
:統(tǒng)計(jì)value
數(shù)量,返回的是int
型數(shù)據(jù)。
2.4.5 切片
切片的正反
?names = ['zm', 'wzw', 'yz', 'zk']
?print(names[1:2]) # 輸出['wzw'],顧頭不顧尾(包括1不包括2)
?print(names[2:]) # 輸出['yz', 'zk']
?print(names[:2]) # 輸出['zm', 'wzw']
?print(names[-2:]) # 輸出['yz', 'zk']
?print(names[:-3]) # 輸出['zm']
關(guān)于上面的負(fù)數(shù),是切片的逆序切法,但是切片還是從左到右(沒(méi)變),只是索引變成從右往左,所以還是顧頭不顧尾。這個(gè)東西......多試試就會(huì)懂了,上升至理論層次可能會(huì)比較抽象,難以理解。eg:第五行代碼切片包括-2
的元素,第六行代碼切片不包括-3
的元素。
步長(zhǎng)
切片完整的格式是[begin: end: step]
,這里的step
就是步長(zhǎng)。
關(guān)于步長(zhǎng),默認(rèn)是1
(eg:[begin: end]
),可以來(lái)試試修改步長(zhǎng)。eg:names[::2] # 列表全部元素隔一個(gè)輸出一個(gè)
切片也可以啥都不寫(xiě),比如names[::]
,name[:]
,他們都等價(jià)于names
,就是啥都沒(méi)切,原樣輸出了。
有趣的是,步長(zhǎng)可以為負(fù)值,這時(shí)候整個(gè)列表就會(huì)反轉(zhuǎn)輸出。
?names = ['zm', 'wzw', 'yz', 'zk']
?print(names[::]) # ['zm', 'wzw', 'yz', 'zk']
?print(names[::-1]) # ['zk', 'yz', 'wzw', 'zm']
那步長(zhǎng)為負(fù)時(shí),begin
和end
參數(shù)也是負(fù)值的話,那是不是也符合顧頭不顧尾呢?有興趣可以自己試試,這個(gè)點(diǎn)不是很重要。
2.4.6 列表的循環(huán)與排序
排序:
sort()
,根據(jù)字符的十進(jìn)制碼進(jìn)行排序。大體的優(yōu)先級(jí)可以模糊理解為:字符<英文<漢字,可能會(huì)有一些特殊的個(gè)例。循環(huán):通常我們使用循環(huán)來(lái)遍歷打印列表的元素(大多使用
for
循環(huán),while
循環(huán)看個(gè)人,習(xí)慣while
的話就可以使用while
)。
?names = [1, 2, 3, 4, 5]
?for i in names:
? ? ?print(i)
2.4.7 嵌套
嵌套適用于列表,也包括后面的元組、字典,除了字符串,其他的基本數(shù)據(jù)類型都支持嵌套語(yǔ)句。所謂嵌套,就是在一個(gè)數(shù)據(jù)類型里面放入一個(gè)數(shù)據(jù)類型,不限于單一的數(shù)據(jù)類型。
?names = ['zm', ['zm', 'wzw', 'yz'], {'411': 'zm', '409': 'yz'}]
?print(names)
2.4.8 enumerate()的使用
有時(shí)我們既需要取出列表的值,又需要列表的索引,我們就可以使用enumerate()
?# -*- encoding: utf-8 -*-
?lis = ['b', 'a']
?for i, k in enumerate(lis):
? ? ?print(i, k)
不過(guò),enumerate()
可以使用index()
來(lái)代替,但比較麻煩一點(diǎn)而已,盡量記住enumerate()
。
2.5 細(xì)說(shuō)數(shù)據(jù)類型-元組
取值和創(chuàng)建都與列表類似,只是把[]
改成了()
。與列表不同的是,元組創(chuàng)建后,只能查,不能增刪改,可以看做只讀列表。元組只有count()
和index()
兩種方法,方法的功能也與前面列表的一樣。
?names = ('zm', 'wzw')
?print(type(names)) # <class 'tuple'>
元組的循環(huán)遍歷、包含(if ... in tuple:
)等運(yùn)算都和列表一樣。
不過(guò),元組中的元素設(shè)置為可變的數(shù)據(jù)類型,就可以改變?cè)M中的元素。
?names = ('yz', ['zm', 'wzw'])
?print(names) # ('yz', ['zm', 'wzw'])
?names[1][0] = 'zhuming'
?print(names) # ('yz', ['zhuming', 'wzw'])
比如我們?cè)M中嵌套列表,我們可以修改列表里面的元素。元組只是存每個(gè)元素的內(nèi)存地址,而作為元組的元素的列表里面的元素存在內(nèi)存里另外的空間,所以是可變的。
2.6 細(xì)說(shuō)數(shù)據(jù)類型-字符串
2.6.1 切片
我們也可以對(duì)字符串使用切片的所有功能,或者說(shuō)我們可以把字符串當(dāng)做一個(gè)列表,每一個(gè)字符是列表的每個(gè)元素。
?name = 'zhuming'
?print(name[::-1])
?print(name[::2])
?print(name[::2])
但是,值得注意的是,字符串不可被修改,修改的話都會(huì)創(chuàng)建一個(gè)新值,我們可以通過(guò)查看修改前和修改后的內(nèi)存地址來(lái)觀察。
?name = 'zm'
?print(id(name))
?name = 'wzw'
?print(id(name)) ?# 與第一次輸出的地址不一樣
2.6.3 轉(zhuǎn)義字符
Python中有許多有代表特殊含義的字符,被稱為轉(zhuǎn)義字符。比如常見(jiàn)的有\n
、\t
等,分別代表了回車、縮進(jìn)等特殊含義。
如果不想讓轉(zhuǎn)義字符生效,就可以在前面加一個(gè)\
,或者在字符串前加一個(gè)r
,表示原生,不觸發(fā)轉(zhuǎn)義字符。
?print('zm\nwzw\tyz')
?# 輸出:
?# zm
?# wzw ? ?yz
?
?print('zm\\nwzw\\tyz') # 輸出:zm\nwzw\tyz
?print(r'zm\nwzw\tyz') # 輸出:zm\nwzw\tyz
2.6.4 字符串的常用操作
capitalize()
:大寫(xiě)字符串的首字母。eg:name = 'jack chen' name.capitalize()就是'Jack chen'
,注意而不是'Jack Chen'
casefold()
:大寫(xiě)全部變小寫(xiě),與lower()
函數(shù)的區(qū)別在于,casefold()
適用于大多數(shù)字符。eg:name = 'Jack Chen' name.casefold()就是'jack chen'
center(width, fillchar)
:定義寬度,然后字符中間顯示,兩側(cè)填充fillchar
的字符。?name = 'System Of Information'
?print(name.center(50, '#'))
?# 輸出:
?# ##############System Of Information###############count(char, start, end)
:計(jì)數(shù)字符串內(nèi)出現(xiàn)char
字符的次數(shù),后面兩個(gè)參數(shù)可填可不填,表示切片的開(kāi)始和結(jié)束,在切出來(lái)的一段字符串內(nèi)對(duì)char
進(jìn)行計(jì)數(shù)。?the_string = 'ssaadd sad'
?print(the_string.count('s')) # 3
?print(the_string.count('a', 0, 3)) # 1(顧頭不顧尾)endswith(str)
:判斷字符串是以什么結(jié)尾。?email = '2511343050@qq.com'
?if email.endswith('@qq.com'):
? ? ?print('郵箱格式正確')
?elif email.endswith('@163.com'):
? ? ?print('郵箱格式正確')
?else:
? ? ?print('郵箱格式錯(cuò)誤')
?# 輸出:
?# 郵箱格式正確find(char, start, end)
:這里的三個(gè)參數(shù)和count
的參數(shù)一樣的含義,找到了就返回char
的下標(biāo)。注意的是find
只能找從左到右第一個(gè)的char
。?name = 'qwq owo xwx'
?format()
:格式化輸出。?title = ' the system '
?choice_1 = ' [1] select '
?choice_0 = ' ?[0] exit ?'
?# %
?welcome = """
?----%s----
?----%s----
?----%s----
?----%s----
?""" % (title, choice_1, choice_0, title)
?print(welcome)
?
?# format 1
?welcome = """
?----{0}----
?----{1}----
?----{2}----
?----{0}----
?""".format(title, choice_1, choice_0)
?print(welcome)
?
?# format 2
?welcome = f"""
?----{title}----
?----{choice_1}----
?----{choice_0}----
?----{title}----
?"""
?print(welcome)index(char, start, end)
:和count以及find類似的功能,只是返回的是下標(biāo)isdigit()
:判斷字符串是否是一個(gè)整數(shù)(可以理解為int()
強(qiáng)制轉(zhuǎn)換為整型),返回True
或False
islower()
:判斷字符串是否全部是小寫(xiě)。isspace()
:判斷字符串是否有空格,多個(gè)空格也可以。isupper()
:判斷字符串是否全部是大寫(xiě)。join(iterable)
:字符串的拼接,必須全是字符串,不能有其他任何類型。?names = ['zm', 'wzw', 'yz']
?print(''.join(names)) # zmwzwyz
?print('|'.join(names)) # zm|wzw|yz
?print(', '.join(names)) # zm, wzw, yzljust(width, fillchar)
:與center()
類似,從左開(kāi)始數(shù)lower()
:大寫(xiě)變小寫(xiě),只支持ASCI Ⅱ編碼。strip(chars)
:將字符串兩邊的其他字符清除,不寫(xiě)具體chars
的值就默認(rèn)包括空格和轉(zhuǎn)義字符這些。該函數(shù)還衍生出另外兩個(gè)函數(shù),分別是lstrip()
和rstrip()
,lstrip()
功能是將左邊的其他字符清除,rstrip()
功能是將左邊的其他字符清除。replace(old, new, count=None)
:將原字符串中的old
片段換成new
,count
表示換多少個(gè)(順序?yàn)閺淖蟮接遥?,默認(rèn)是全部old
都換。?score = 'My score is 580!580!580!'
?print(score.replace("580", "630")) # 修改成My score is 630!630!630!
?print(score.replace("580", "630", 1)) # 修改成My score is 630!580!580!
?print(score.replace("580", "630", 2)) # 修改成My score is 630!630!580!split(sep, maxsplit=-1)
:字符串通過(guò)sep
分割成列表的元素,以列表的形式返回。如果什么參數(shù)都不寫(xiě),字符串直接變成只有一個(gè)元素的列表。maxsplit
設(shè)置的是從左到右分割幾次,默認(rèn)無(wú)數(shù)次。該函數(shù)還衍生出一個(gè)函數(shù),是rsplit()
,其功能就是把從左往右變成從右往左。?n = ['zm', 'yz', 'wzw']
?name = ",".join(n) # 'zm,yz,wzw'
?names = name.split(',') # ['zm', 'yz', 'wzw']startswith(prefix, start=None, end=None)
:和endswith()
一樣,一個(gè)判斷字符串以什么開(kāi)頭,一個(gè)以什么結(jié)尾。start
和end
和endswith()
的參數(shù)功能一模一樣。swapcase()
:大小寫(xiě)交換,小寫(xiě)變大寫(xiě),大寫(xiě)變小寫(xiě),不過(guò)特殊字符不會(huì)變。upper()
:字符串統(tǒng)一變成大寫(xiě)。zfill(width)
:規(guī)定長(zhǎng)度,不夠長(zhǎng)度用0
來(lái)補(bǔ),一般不常用,網(wǎng)絡(luò)編程時(shí)可能會(huì)用到。
總結(jié):
查:
find()
、index()
、count()
改:
replace()
、upper()
、lower()
、swapcase()
、casefold()
、strip()
、split()
格式化:
format()
、ljust()
、rjust()
、join()
判斷:
isdigit()
、startswith()
、endswith()
2.7 細(xì)說(shuō)數(shù)據(jù)類型-字典
2.7.1 字典的優(yōu)點(diǎn)及創(chuàng)建
方便嵌套操作海量數(shù)據(jù),而且字典是以鍵值對(duì)形式存儲(chǔ),可以通過(guò)查找鍵來(lái)獲得值,而字典的鍵可以是整型、字符串、元組等不可變數(shù)據(jù)類型(且每個(gè)鍵要唯一存在),而不是像列表那樣只有單一的下標(biāo)查詢。
字典的創(chuàng)建:
?# {key1: value1, key2: value2, ...}
?new_dict = {
? "name": 'zm',
? ? ?"password": '123',
?}
?print(new_dict)
?print(new_dict['name']) # 取鍵name對(duì)應(yīng)的值
字典沒(méi)有順序(無(wú)序),查詢速度不受字典大小的影響(HASH,索引結(jié)構(gòu)),非???。字典的鍵不可變、必須唯一,鍵對(duì)應(yīng)的值可以有多個(gè),可以被修改、可以不唯一。
幾種創(chuàng)建方法
?# 1
?person = {"name": "zm", "age": 20} # {'name': 'zm', 'age': 20}
?# 2
?person = dict(name='zm', age=20) # {'name': 'zm', 'age': 20}
?# 3
?{}.fromkeys([1, 2, 3, 4, 5, 6, 7, 8], 100) # {1: 100, 2: 100, 3: 100, 4: 100, 5: 100, 6: 100, 7: 100, 8: 100}
2.7.2 字典的增刪改查
字典的增
?new_dict = {
? "name": 'zm',
? ? ?"password": '123',
?}
?# 增 1 (如果job已經(jīng)存在就變成了修改job的值)
?new_dict['job'] = 'teacher'
?# 增 2 (如果address已經(jīng)存在,setdefault就只會(huì)返回原address的值,不進(jìn)行任何修改或添加操作)
?new_dict.setdefault('address', '地球')
?print(new_dict) ?# 多了job和address兩個(gè)鍵值對(duì)
字典的刪
?new_dict = {
??? "name": 'zm',
? ? "password": '123',
?}
?new_dict.pop('name') # 刪除鍵為name的鍵值對(duì)
?new_dict.popitem() # 隨機(jī)刪除一個(gè)鍵值對(duì)
?del new_dict['name'] # 刪除鍵為name的鍵值對(duì)
?new_dict.clear() # 清空字典
del
在刪除數(shù)據(jù)類型的所有方法里,盡量不要用......
字典的改
?dic = {1: 1, 2: 2, 3: 3}
?dic2 = {1: 2, 2: 4, 4: 8}
?# 修改3的鍵值對(duì)
?dic[3] = 5 # {1: 1, 2: 2, 3: 5}
?# 將dic2的鍵值對(duì)加到dic里面,如果鍵有重復(fù)的情況,dic2中的鍵值對(duì)會(huì)覆蓋dic的原鍵值對(duì)
?dic.update(dic2) # {1: 1, 2: 4, 4: 8, 3: 5}
字典的查
dic['key']
:返回字典key
對(duì)應(yīng)的值,不存在則會(huì)報(bào)錯(cuò);dic.get(key, default=None)
:如果有就返回key
對(duì)應(yīng)的value
,找不到就返回default
的值;'key' in dic
:存在返回True
,不存在返回False
;dic.keys()
:返回包括dic
的所有鍵的列表;dic.values()
:返回包括dic
的所有值的列表;dic.items()
:返回包括dic
所有鍵值對(duì)(元組的形式:(key, value)
)的列表。
循環(huán)
?for i in dic:
? ? ?print(i) # 打印的只是key
?# 推薦方法
?for i in dic:
? ? ?print(i, dic[i])
?# 以下效率比較低(用到keys(),items(),values()的都比較慢)
?for i in dic.items():
? ? ?print(i)
?for k, v in dic.items():
? ? ?print(k, v)
2.8 細(xì)說(shuō)數(shù)據(jù)類型-集合
2.8.1 集合的作用
和列表一樣,存一堆數(shù)據(jù),里面的元素不可變,不能存列表、字典等可變數(shù)據(jù)類型放在集合內(nèi),只能存儲(chǔ)元組、數(shù)字、字符串等不可變數(shù)據(jù);集合天生去重,在集合不存在重復(fù)元素;集合無(wú)序,不能通過(guò)索引來(lái)查找值。
集合最重要的兩個(gè)功能:去重和關(guān)系運(yùn)算(與、并、異或等)
2.8.2 集合的增刪改查
?# 去重
?names = ['zm', 'wzw', 'yz', 'zk', 'zm', 'zk']
?print(set(names)) # {'wzw', 'zm', 'zk', 'yz'}
?print(type(set(names))) # <class 'set'>
?
?# 增
?set_name = set(names)
?set_name.add('py') # {'zm', 'zk', 'yz', 'wzw', 'py'}
?
?# 刪
?set_name.discard('py') # 刪除py,{'zm', 'zk', 'yz', 'wzw'}
?set_name.discard('p2y') # 刪除不存在的值時(shí),就不進(jìn)行任何操作
?
?set_name.pop() # 隨機(jī)刪除并返回刪除后的集合
?
?set_name.remove('zm') # 刪除zm
?set_name.remove('zm2') # 刪除不存在的值時(shí),就會(huì)報(bào)錯(cuò)
?
?# 查
?# 只能in運(yùn)算
?if 'wzw' in set_name:
? ? ?print(True)
?
?# 改
?# 不能修改!
?# 切片也是不能用的
2.8.3 集合的關(guān)系運(yùn)算
?names_181 = ['zm', 'wzw', 'yz', 'zk', 'kyj']
?names_182 = ['zm', 'yz', 'py', 'zzw', 'ghd']
?
?print(names_181 & names_182) # 交集
?print(names_181 | names_182) # 并集
?print(names_181 - names_182) # 差集,only in names_181
?print(names_182 - names_181) # 差集,only in names_182
?print(names_181 ^ names_182) # 對(duì)稱差集,等于 兩個(gè)集合的并集 - 兩個(gè)集合的交集
集合的關(guān)系一般只有三個(gè):包含、相交、不相交。Python中分別用下面的方法判斷:
names_181.isdisjoint(names_182)
:判斷兩個(gè)集合是否相交,返回True or Falsenames_181.issubset(names_182)
:判斷name_181
是否被包含于name_182
中,返回True
orFalse
names_181.issuperset(names_182)
:判斷name_181
是否包含name_182
,返回True
orFalse
集合還有其他的方法,這些就不一一贅述了,用到就百度一下就好了。
2.9 關(guān)于二進(jìn)制和十六進(jìn)制
2.9.1 二進(jìn)制
理解數(shù)學(xué)的十進(jìn)制到二進(jìn)制的轉(zhuǎn)換就好了,二進(jìn)制逢2進(jìn)一。
計(jì)算機(jī)為什么用二進(jìn)制?是因?yàn)橛?jì)算機(jī)的晶體管只能有高電平和低電平兩個(gè)狀態(tài),先人把低電平和高電平用0和1表示,通過(guò)這種方法,用二進(jìn)制來(lái)抽象表示人類生活中的種種信息數(shù)據(jù)。
Python中將十進(jìn)制轉(zhuǎn)化成二進(jìn)制的方法是bin()
,十進(jìn)制的值只能從0-255
,更高的表示方法需要更深學(xué)習(xí)bin()
方法的具體表示,我們就可以不用看了,了解二進(jìn)制的轉(zhuǎn)化即可。
2.9.2 十六進(jìn)制
10進(jìn)制用0-9
表示所有數(shù)字,2進(jìn)制用0, 1
表示所有數(shù)字,而16進(jìn)制用0-9
以及A-F
,15個(gè)符號(hào)表示所有數(shù)字,逢16進(jìn)一。A-F
分別代表10-15
。
16進(jìn)制只是一種表示方法,計(jì)算機(jī)的底層還是使用2進(jìn)制的。計(jì)算機(jī)中16進(jìn)制多用來(lái)表示HTML/CSS
的顏色表的色號(hào)(有RGB
等,16進(jìn)制也是一種常用表示方法)、mac地址、字符編碼等都用16進(jìn)制來(lái)表示。
為了16進(jìn)制不與其他進(jìn)制不混淆,16進(jìn)制一般開(kāi)頭為0x
,后面的字符才是可以轉(zhuǎn)化的東西,比如hex(16)
輸出是0x10
,數(shù)學(xué)上的計(jì)算方法就是:0* 16*0+1* 16*1=16
,數(shù)學(xué)方法就不再多介紹了,可以自行百度了解,自己演算的時(shí)候很常用。
Python中將十進(jìn)制轉(zhuǎn)換成十六進(jìn)制的方法是hex()
,可以自己多試試。
2.10 字符編碼
2.10.1 文字的顯示原理
為了表示字符,我們必須實(shí)現(xiàn)zhuming -> 十進(jìn)制,使他們關(guān)聯(lián)起來(lái)。
將一個(gè)一個(gè)字符與十進(jìn)制數(shù)字對(duì)應(yīng)起來(lái),最常見(jiàn)的有ASCII
碼(基于拉丁字母和特殊字符的一套字符編碼表)。然而ASCII
碼一開(kāi)始只有127個(gè),后來(lái)又補(bǔ)充了一些(128后面的字符對(duì)應(yīng)關(guān)系稱作擴(kuò)展ASCII
碼),但是沒(méi)有中文。Python中可以通過(guò)ord()
來(lái)查看字符對(duì)應(yīng)的十進(jìn)制數(shù)。
但是每個(gè)字符轉(zhuǎn)化的二進(jìn)制長(zhǎng)度都不一樣,二進(jìn)制怎么斷句呢?因?yàn)锳SCI Ⅱ碼小于255,所以就規(guī)定每8位(bit
)代表一個(gè)字節(jié)(B
,又稱bytes
)。1024B=1KB
,1024KB=1MB
,1024MB=1GB
,1024GB=1TB
-> PB
EB
ZB
等等。
因?yàn)?code>ASCII表沒(méi)有中文,我們就自己研發(fā)了一張表,又因?yàn)闈h字一個(gè)bit
存不下,我們就用了兩個(gè)bit
來(lái)存儲(chǔ)。后來(lái)研發(fā)成功,名為GB2312
,總共可有存儲(chǔ)6w個(gè)漢字,當(dāng)時(shí)只有6763個(gè)漢字(日常生活中常用的)
隨著電腦在中國(guó)的普及,越來(lái)越多的字需要被計(jì)算機(jī)表示,后面升級(jí)成了GBK
編碼,表示了2w多個(gè)字符(包括漢字、少數(shù)民族語(yǔ)言、日語(yǔ)、韓語(yǔ)等),21世紀(jì)又進(jìn)行了升級(jí),表示了2w8k多個(gè)字符。
隨著發(fā)展,問(wèn)題又來(lái)了,人們生活中不光單純使用中文,英文使用的頻率和中文的頻率差不多,如果用GBK
來(lái)表示純英文的文檔比用ASCII
一倍的內(nèi)存。那么如何兼容呢?
因?yàn)?code>ASCII高位的字符(大于127
的特殊字符)比較少用,那么兩個(gè)連續(xù)的高位字符就更少出現(xiàn)了。我們就增加一個(gè)判斷,如果有兩個(gè)連續(xù)的高位字符,我們就使用GBK
編碼,其他情況就用ASCII
編碼,較好的解決了兼容性問(wèn)題。
2.10.2 編碼種類
每個(gè)國(guó)家可能都會(huì)有自己的編碼,比如美國(guó)是ASCII
,中國(guó)是GBK
,日本是shift_JIS
等等等等,每個(gè)的字符對(duì)應(yīng)關(guān)系都不一樣,那不同國(guó)家的軟件自己拿來(lái)就會(huì)產(chǎn)生亂碼。
最后,出現(xiàn)了Unicode
編碼,2-4字節(jié),俗稱萬(wàn)國(guó)碼,支持所有的語(yǔ)言(13w6k多個(gè)),可以與各種語(yǔ)言進(jìn)行轉(zhuǎn)換(將已經(jīng)出來(lái)的軟件不用推倒重做)
但是Unicode用了2-4個(gè)字節(jié),比ASCII的一個(gè)字節(jié)多了很多,在當(dāng)時(shí)的傳輸和硬件存儲(chǔ)水平下,很不適應(yīng)。
最后針對(duì)傳輸和存儲(chǔ),基于Unicode做出了一個(gè)新編碼,叫UTF-8(使用1、2、3、4字節(jié)表示所有字符,優(yōu)先使用少的字節(jié))。
下面兩個(gè)編碼因?yàn)闆](méi)有解決傳輸和存儲(chǔ)的問(wèn)題,所以不常使用,只做解。
UTF-16:使用2、4字節(jié)表示所有字符
UTF-32:使用4個(gè)字節(jié)表示所有字符
這里也簡(jiǎn)單提一下python2和python3的區(qū)別。
python2用的是ASCII
碼,python3用的是utf-8
,所以python2不能顯示中文,所以python2在程序的開(kāi)頭一般都要手動(dòng)聲明# -*- encoding: utf-8 -*-
(官網(wǎng)推薦寫(xiě)法),必須在代碼的頂頭(第一行)寫(xiě)。
我們python3呢建議也寫(xiě)上,防止python3偶爾因?yàn)榫幋a文件導(dǎo)致自己的項(xiàng)目代碼運(yùn)行報(bào)錯(cuò),最后花個(gè)好幾天解決就很傷了。
# -*- encoding: utf-8 -*-
2.11 字典密碼——HASH
2.11.1 HASH
HASH(又稱哈希)是把任意長(zhǎng)度的輸入(又叫做預(yù)映射)通過(guò)散列算法變換成固定長(zhǎng)度的輸出,該輸出值就是散列值。這種轉(zhuǎn)換是一種壓縮映射,這樣我們可以通過(guò)更小的值來(lái)表示很大的值所表示的東西。
HASH是一種算法,很復(fù)雜的運(yùn)算,它的輸入可以是字符創(chuàng)或者數(shù)字或者任何文件,經(jīng)過(guò)哈希算法處理后,變成一個(gè)固定長(zhǎng)度的輸出,該輸出就是哈希值。哈希算法有一個(gè)很大的特點(diǎn),就是不能從結(jié)果推算出輸出是什么,所以成為不可逆的算法。
哈希多應(yīng)用于密碼學(xué)以及區(qū)塊鏈等方面。
Python中有hash()
的方法來(lái)將字符串或數(shù)字變成哈希值,每次重啟Python之后,同樣的字符或數(shù)字hash()
的哈希值都不是一致的。注意,hash()
的參數(shù)只能hash
不可變的值。
HASH的特性
剛才也說(shuō)了,不可逆是HASH的一個(gè)重要特性。生活中也有許多不可逆的情況,比如煮熟的雞蛋變不回生雞蛋。
計(jì)算極快。20G高清電影和5K的文件
HASH的用途
密碼。常用的加密算法是
md5
。md5
就是基于HASH
做的,使你的密碼經(jīng)過(guò)基于HASH
的算法處理,使你的密碼與一個(gè)哈希值一一對(duì)應(yīng),然后存放進(jìn)數(shù)據(jù)庫(kù)中。如果想登陸,就只能知道原來(lái)的密碼是什么,然后輸入,進(jìn)行算法的處理,將生成的值與數(shù)據(jù)庫(kù)內(nèi)存儲(chǔ)的哈希值比較,如果一致才可以登陸成功。文件完整性校驗(yàn)。常用的也是
md5
算法。一個(gè)文件對(duì)應(yīng)一個(gè)哈希值,在傳輸過(guò)程中可能會(huì)遇到攔截等惡意攻擊,當(dāng)接收文件之后,再進(jìn)行md5處理,如果兩次得出的哈希值一致,那就說(shuō)明文件完整,沒(méi)有殘缺或植入了木馬等惡意程序。數(shù)字簽名。數(shù)字簽名是為了防止假情報(bào),效果類似于對(duì)暗號(hào),A說(shuō)天王蓋地虎,B回應(yīng)寶塔鎮(zhèn)河妖,那A和B就確定了是自己人,才可以進(jìn)行通信。數(shù)字簽名就是在互聯(lián)網(wǎng)上的暗號(hào)應(yīng)用。A說(shuō)的天王蓋地虎在互聯(lián)網(wǎng)上稱為私鑰,將發(fā)送的原文進(jìn)行
hash
,生成一段hash
值(就是摘要信息),然后把原文和摘要信息一同發(fā)給B。他回應(yīng)的寶塔鎮(zhèn)河妖被稱為公鑰,將你的摘要信息解密,得到hash
值。B再對(duì)原文件進(jìn)行hash
,也得到一個(gè)hash
值,將這個(gè)hash
與a發(fā)來(lái)的hash
值進(jìn)行比較,如果一致就說(shuō)明這個(gè)原文是A本人發(fā)送的,而不是別人發(fā)送的。區(qū)塊鏈。電子錢包,加密錢包等應(yīng)用。
還有很多其他的應(yīng)用就不一一介紹了。
2.11.2 應(yīng)用HASH的數(shù)據(jù)結(jié)構(gòu)
Python中有兩個(gè)數(shù)據(jù)類型是基于hash
來(lái)做的,一個(gè)是dict
,一個(gè)是set
。
字典快的原因
dict
有三個(gè)顯著特點(diǎn):
key
唯一key
不可變查詢速度極快,且不受
dict
大小的影響的特點(diǎn)。
第三個(gè)特性就是hash
帶來(lái)的。下面就進(jìn)行簡(jiǎn)單的原理解釋(真實(shí)的原理不是這樣,只是做類似的解說(shuō))。
dict
的每個(gè)key
都會(huì)經(jīng)過(guò)hash
生成一段固定長(zhǎng)度的hash
值,然后這些hash
值經(jīng)過(guò)大小排序放入一個(gè)列表A
中(hash
值都是一串?dāng)?shù)字),需要查詢哪個(gè)字典的值就通過(guò)二分法查找列表A
,所以只需要31次查詢就可以找20億的數(shù)據(jù),計(jì)算機(jī)查詢一次也僅僅只需要幾毫秒。
不過(guò),字典的原理還不單單只有這些,這些只是簡(jiǎn)單地類似原理,真實(shí)的字典原理比這還要復(fù)雜。
集合為何去重
集合每add
進(jìn)一個(gè)值,他都會(huì)經(jīng)過(guò)hash
運(yùn)算出一個(gè)hash
值,然后存放在一個(gè)列表A中,如果后面add
的數(shù)據(jù)的hash
值在列表A中,那么這個(gè)數(shù)據(jù)就不會(huì)再存進(jìn)集合里了。
2.11.3 判斷數(shù)據(jù)結(jié)構(gòu)是否可變
將這個(gè)數(shù)據(jù)進(jìn)行hash
,如果可以哈希就是不可變的數(shù)據(jù),反之則是可變的數(shù)據(jù)類型。
2.12 文件操作
2.12.1 基礎(chǔ)操作
日常文件操作的步驟:
找到文件,打開(kāi)
讀、修改
保存、關(guān)閉
Python中的文件操作:
open(filename)
:根據(jù)路徑和文件名打開(kāi)文件f.read()
/f.write()
:讀寫(xiě)操作f.close()
:文件關(guān)閉(默認(rèn)保存)
需要注意的是文件操作的模式只能以一種方式(后面再介紹復(fù)雜的多種方式混合的文件操作模式)。python有r
,w
,a
三種基本方法,分別代表只讀模式、寫(xiě)入模式、追加模式三種基本方式。(注意,如果w
的寫(xiě)入模式下會(huì)覆蓋原文件的舊數(shù)據(jù),想在原文件的舊數(shù)據(jù)后面寫(xiě)新數(shù)據(jù)的話就要用到a
的追加模式才可以)
因?yàn)槭俏募牟僮?,所以存在路徑、文件名等差異,每個(gè)人都不一樣。我們這里就統(tǒng)一規(guī)定一下。
以下文件操作的代碼都是對(duì)名為test.txt
,路徑是所在目錄是當(dāng)前python文件下的路徑的文件進(jìn)行的操作。
?# -*- encoding: utf-8 -*-
?# 文件的讀
?f = open(file='test.txt', mode='r')
?# 讀一行
?print(f.readline())
?# 讀剩下的數(shù)據(jù)
?print(f.read())
?f.close()
?# 不需要寫(xiě)close()的with語(yǔ)句
?with open(file='test.txt', mode='r') as f:
? # 讀一行
? ? ?print(f.readline())
? ? ?# 讀剩下的數(shù)據(jù)
? ? ?print(f.read())
?print('with代碼塊執(zhí)行完之后,with會(huì)關(guān)閉所有代碼塊中用到的功能進(jìn)程,其中就包括f.close()')
?
?# 文件的追加寫(xiě)入
?f = open(file='test.txt', mode='a')
?# 讀一行
?f.write('\nzk\t24\t18845516854')
?f.close()
?
?# 文件的覆蓋寫(xiě)入
?f = open(file='test.txt', mode='w')
?# 讀一行
?f.write('zk\t24\t18845516854')
?f.close()
循環(huán)輸出
?# -*- encoding: utf-8 -*-
?f = open(file='test.txt', mode='r')
?
?print('循環(huán)輸出:')
?for line in f:
? ? ?print(line.strip()) ? ? # strip是去掉每一行末尾的\n?# -*- encoding: utf-8 -*-
?f = open(file='test.txt', mode='r')
?print('輸出age大于20的信息')
?for line in f:
? ? ?line_info = line.strip().split(', ')
? ? ?if int(line_info[1]) >= 20:
? ? ? ? ?print(line.strip())
?
?f.close()?# -*- encoding: utf-8 -*-
?f = open(file='test.txt', mode='r')
?print("全部輸出:")
?f.readlines()
?
?f.close()
2.12.2 file類的常用功能
seek(num)
:光標(biāo)的定位,移動(dòng)到num
的位置,num
是以字節(jié)來(lái)算而不是字符!中文占三個(gè)字節(jié),所以seek()
定位的時(shí)候可能會(huì)定位到一個(gè)字符的幾個(gè)字節(jié)之間,導(dǎo)致可能會(huì)拋出編碼錯(cuò)誤(UnicodeDecodeError
)?# -*- encoding: utf-8 -*-
?with open('test.txt', 'r') as f:
? ? ?f.seek(2) # 從zm后面開(kāi)始輸出
? ? ?print(f.readlines())flush()
:強(qiáng)制刷入硬盤。因?yàn)閷?duì)硬盤的數(shù)據(jù)進(jìn)行頻繁操作很慢,所以文件操作有個(gè)緩存機(jī)制,write
的數(shù)據(jù)先寫(xiě)到緩存的內(nèi)存里,緩存滿了才會(huì)刷到硬盤里。比如我們一直write
方法寫(xiě)入,但是如果你不close()
它就不會(huì)立即刷到硬盤。如果寫(xiě)入的數(shù)據(jù)很重要,怕運(yùn)行過(guò)程中斷電等因素(計(jì)算機(jī)關(guān)閉時(shí)只有硬盤的數(shù)據(jù)不會(huì)被清空)影響到關(guān)鍵數(shù)據(jù)的寫(xiě)入,就可以用flush()
強(qiáng)制刷入硬盤中。readable()
:判斷文件是否可讀。seekable()
:判斷文件是否可以進(jìn)行seek()
,比如Linux
所有的東西都是文件,所以需要判斷這個(gè)函數(shù)判斷,不常用。tell()
:返回當(dāng)前光標(biāo)的位置。truncate()
:按指定長(zhǎng)度截?cái)辔募?。指定長(zhǎng)度就從文件頭開(kāi)始到指定長(zhǎng)度為止;不指定就從當(dāng)前位置截到文件末尾。長(zhǎng)度也是用字節(jié)來(lái)算,不是按字符來(lái)算!針對(duì)網(wǎng)站訪問(wèn)量大的日志的分析可能會(huì)用到。writable()
:判斷文件是否可寫(xiě)。writelines()
:傳入一個(gè)列表數(shù)據(jù),然后一個(gè)元素一行的寫(xiě)入文件。
2.12.3 混合模式下處理文件
文件模式肯定不止前面的基本三種,打開(kāi)文件還有w+
、r+
、a+
三種混合模式,分別是三種基本文件模式的組合,比如
w+
就是創(chuàng)建一個(gè)新文件,寫(xiě)一段內(nèi)容可以再把內(nèi)容讀出,可以理解為w
和r
的組合,不常用;r+
就是能讀能寫(xiě),可以理解為r
和a
的組合,文件寫(xiě)入是從末尾開(kāi)始寫(xiě);讀的話,是從開(kāi)頭開(kāi)始讀,不用重新seek()
,不用為較為常用;a+
就是文件一打開(kāi),文件寫(xiě)入是從末尾開(kāi)始寫(xiě),讀也是從文件末尾開(kāi)始讀。
這三種模式很少使用,r+
和a+
偶爾會(huì)用到。
混合模式修改文件
我們要修改文件的話就需要用到r+
的混合模式。比如我們要在test.txt
的第一行中插入一個(gè)'dd'
。
?# -*- encoding: utf-8 -*-
?with open('test.txt', 'r+', encoding='utf-8') as f:
? ? ?f.seek(2)
? ? ?f.write('dd')
插入過(guò)后打開(kāi)test.txt
文件,我們就可以看到,插入的'dd'
插入是插入成功了,但是dd
的第二個(gè)d
會(huì)把seek(2)
后面的一個(gè)字節(jié)(別忘了seek()
是以字節(jié)為單位的)覆蓋掉...... 這是我們不想看到的,下面我們就來(lái)闡述這個(gè)問(wèn)題的原因以及解決方法。
在文件中間修改數(shù)據(jù)的話,就會(huì)覆蓋后面的數(shù)據(jù),這個(gè)和數(shù)據(jù)在硬盤上的數(shù)據(jù)存儲(chǔ)方法有關(guān)系。但是像word
和vim
這些工具都可以修改文件,那他們到底是怎么實(shí)現(xiàn)的呢?
我們就可以先把硬盤的文件數(shù)據(jù)加載到內(nèi)存,再對(duì)內(nèi)存的文件數(shù)據(jù)進(jìn)行修改,然后再重新刷到硬盤里。如果你打開(kāi)很大的word
,你的電腦可能會(huì)花費(fèi)較多的時(shí)間加載,就是因?yàn)樵趯⑽募?shù)據(jù)讀入內(nèi)存。
2.12.4 其他模式
二進(jìn)制打開(kāi)文件的三種模式:rb
、wb
、ab
。
2.12.5 不占內(nèi)存的修改文件
如果像上面講的那樣,我們將數(shù)據(jù)從硬盤全部讀入到內(nèi)存,然后再對(duì)內(nèi)存的文件數(shù)據(jù)進(jìn)行修改的話,如果文件很大的話,那就會(huì)對(duì)內(nèi)存造成很大影響。
那么我們可以讀取一行數(shù)據(jù)到內(nèi)存然后對(duì)這行數(shù)據(jù)進(jìn)行修改的操作,然后刷入硬盤,周而復(fù)始,就完成了對(duì)整個(gè)文件數(shù)據(jù)的修改。這種方法被稱作邊讀邊寫(xiě)的方法。
將文件中的zm
都改為qq
。
?# -*- encoding: utf-8 -*-
?import os
?
?old_file = 'test.txt'
?new_file = 'test_new.txt'
?f = open(old_file, "r")
?f_new = open(new_file, "w")
?old_str = 'zm'
?new_str = "qq"
?
?for line in f:
? ? ?if "zm" in line:
? ? ? ? ?line = line.replace(old_str, new_str)
? ? ? ? ?f_new.write(line)
?
?f.close()
?f_new.close()
?
?os.replace(new_file, old_file)
2.13 關(guān)于腳本的說(shuō)明
Python原來(lái)就是一種腳本語(yǔ)言,腳本的數(shù)據(jù)獲取是通過(guò)sys
模塊的argv()
實(shí)現(xiàn)的。
前面沒(méi)講的是,在Python中導(dǎo)入庫(kù)或者模塊,需要用到import
語(yǔ)句,并在開(kāi)頭幾行寫(xiě),上面的import os
就是一個(gè)例子。
下面我們給出不占內(nèi)存修改文件的腳本寫(xiě)法,主要看5到7行的腳本獲取輸入數(shù)據(jù)。
?import os
?import sys
?
?# 腳本獲取傳入的數(shù)據(jù) 需要在前面寫(xiě),中間不能用其它語(yǔ)句
?old_str = sys.argv[1]
?new_str = sys.argv[2]
?file = sys.argv[3]
?
?new_file = f'new_{file}'
?
?f = open(file, 'r', encoding='utf-8')
?new_f = open(new_file, 'w', encoding='utf-8')
?
?for line in f.readlines():
? ? ?if old_str in line:
? ? ? ? ?new_line = line.replace(old_str, new_str)
? ? ?else:
? ? ? ? ?new_line = line
? ? ?new_f.write(new_line)
?
?f.close()
?new_f.close()
?os.replace(new_file, file)
末
有什么Python代碼或者其他問(wèn)題可以私信問(wèn)我或者在下面留言,Python課程設(shè)計(jì)我也偶爾可以有償幫做,祝大家變得更強(qiáng)[狗頭]
剩下的就是和上一篇文章末尾一樣要說(shuō)的,我就當(dāng)成套話了。
套話:因?yàn)樾∑普旧系奈谋靖袷綄?duì)演示代碼極其不友好,而且自己平時(shí)的筆記是通過(guò)Markdown語(yǔ)法來(lái)記錄的,在格式上和美觀程度上不是很好看,如果你看的不習(xí)慣,就去下載一個(gè)Typora(或者支持markdown語(yǔ)法的應(yīng)用),我這里給出md文件的迅雷網(wǎng)盤鏈接(百度網(wǎng)盤上傳不了),然后用Typora打開(kāi)文件看就好了。
鏈接:https://pan.xunlei.com/s/VMQbWK_-yU0afk7Eh4eRHm_hA1
提取碼:js9z