文章目錄
自定義異常
要求這個類必須繼承Exception
__str__
返回值就是錯誤信息描述
class MyOnlyError(Exception):
def __str__(self):
return '報錯信息'
內存管理基礎
-
c語言
內存分爲棧區間和堆區間
棧區間的內存是系統自動申請自動釋放
堆上的內存需要程序通過調用malloc函數取申請,通過調用free函數去釋放
-
高級語言(java\C++\OC\Python)中的內存管理機制,都是針對堆上的內存的管理進行的自動化操作
Python的內存管理機制
C系列、Java等語言普通數據放在棧區間裏,對象(如果有)放在堆區間裏
而python中,一切皆對象,所以一切數據都是在堆區間中
相當於所有的變量都是指針
1)內存的申請
python中所有的數據都是存在堆區間中的,變量是保存在棧區間的,變量中保存的是保存在堆中的數據的地址,相當於所有的變量都是指針
重新給變量賦值,會先在堆區間中開闢新的內存區域,來保存新的數據,然後將新的數據的地址重新保存到變量
但是如果使用數字或者字符串給變量賦值,不會直接開闢新的內存,而是先檢查內存中有沒有這個數據,如果有,直接將原來的數據的地址給變量
2)內存的釋放
python中有一套機制叫垃圾回收機制
一個數據對應的內存空間是否釋放,就看這個數據的引用計數是否爲0;如果引用計數爲0,數據對應的內存就會被自動釋放
增加引用計數:增加數據的引用(讓更多的變量來保存數據的地址)
減少引用計數:刪除引用,或者讓引用去保存新的數據
特殊情況:循環引用問題(在python中不存在)
你引用我,我引用你
python的垃圾回收機制會自動處理循環引用問題
拷貝
1. 直接賦值
用一個變量直接給另外一個變量賦值的時候賦的地址,賦值後兩個變量保存的是同一個數據的地址
在棧區間裏的兩個變量都指向堆區間裏的同一個數據,改動其中一個,另一個也跟着動
a = [1, 2, 3]
b = a
a[0] = 999
print(b) # [999, 2, 3]
2. 淺拷貝和深拷貝
複製原數據,產生一個新的數據(值和原數據一樣,地址不同),然後將新的數據的地址返回
如果有子對象,子對象不會複製(淺拷貝相對深拷貝的特點)
import copy
a = [1, 2, 3]
b = copy.copy(a)
a[0] = 999
print(b) # [1, 2, 3]
上面是淺拷貝,ab改變本身數據的時候不會影響另一個
3. 深拷貝
複製原數據,產生一個新的數據(值和原數據一樣,地址不同),然後將新的數據的地址返回
如果有子對象,子對象也會複製(深拷貝相對淺拷貝的特點)
from copy import copy, deepcopy
class Dog:
def __init__(self, name, age=10):
self.name = name
self.age = age
class Person:
def __init__(self, name, age=10, dog=None):
self.name = name
self.age = age
self.dog = dog
p1 = Person('Tom', dog=Dog('dd'))
p2 = p1
p3 = copy(p1)
p4 = deepcopy(p1)
p2.dog.name = 'p2的狗'
p3.dog.name = 'p3的狗'
p4.dog.name = 'p4的狗'
print(p1.dog.name)
print(p2.dog.name)
print(p3.dog.name)
print(p4.dog.name)
上面說明直接賦值、淺拷貝數據裏的對象不會複製,即p1,p2,p3三個人的狗是同一條狗,而深拷貝數據裏的對象會複製,即p4的狗是自己的,長得跟p1的狗一樣而已
正則表達式
1. 什麼是正則表達式
用正則符號來描述字符串規則,讓字符串匹配更簡單的一種工具
正則本身的語法和編程語言無關,幾乎所有的編程語言都支持正則
python通過提供re模塊來支持正則表達式
2. 正則符號
匹配符號
一定要有一個字符與之匹配
1)普通字符
在正則表達式中沒有特殊功能或者特殊意義的字符都是普通字符
普通字符在正則表達式中就代表這個符號本身,匹配的時候只能和這個指定的字符進行匹配
import re
value = '13ab'
re = re.fullmatch(r'13ab', value)
if re:
print('匹配')
相當於:
if value == r'13ab':
print('匹配')
2)特殊字符
寫法 | 說明 |
---|---|
. | 任意字符 |
\w | ASCII碼錶中, 只能匹配,字母、數字、下劃線; ASCII碼錶以外的都可以匹配 存在意義不大 |
\d | 匹配任意一個數字字符,等價於 [0-9] |
\s | 匹配任意空白字符,等價於 [\t\n\r\f] |
\W, \D, \S | 匹配非小寫所匹配的字符 即小寫的能匹配的大寫的就不能匹配 |
[字符集] | 匹配字符集中的任意一個字符(見例子) 一箇中括號只能匹配一個字符 |
[^字符集] | 匹配非字符集中的任意一個字符 |
字符集例子
a. [普通字符集]
[abc12]:匹配a、b、c、1、2五個字符中的任意一個
b. [字符1-字符2]
匹配字符1的編碼值到字符2的編碼值之間的所有字符
要求字符1的編碼值必須小於字符2的
[1-9]:匹配123456789中的任意一個
[a-z]:匹配任意一個小寫字母
[a-zA-Z]:匹配任意一個字母
[\u4e00-\u9fa5]:匹配任意一箇中文字符
[1-9abc]:匹配123456789和a、b、c中的任意一個字符
[\dxy]:匹配數字和x、y中的任意一個字符
檢測符號
寫法 | 說明 |
---|---|
\b | 檢測是否是單詞結尾(見例子) 單詞結尾:所有可以區分出兩個不同單詞的符號都是單詞結尾,其中包括字符串開頭和字符串結尾 用法:檢測\b所在的位置是否是單詞結尾;不影響匹配時候的字符串長度 相當於檢測廣義上的分隔符 |
^ | 檢測^所在的位置是否是字符串開頭 |
$ | 檢測$所在的位置是否是字符串結尾 |
\b 的例子:
'ab\b':匹配一個長度是2的字符串,字符串爲'ab',並且要求b的後面是單詞邊界
\b在匹配中沒用,但是在search查找裏有用,可以找到單獨的字符串
匹配次數
字符 | 說明 |
---|---|
字符? | 字符匹配0次或1次 如:[a-z]? 小寫字母出現0次或1次 |
字符* | 字符匹配0次或多次 如:[a-z]* 小寫字母出現0次或多次 |
字符+ | 字符匹配1次或多次 如:[a-z]+ 小寫字母出現1次或多次 |
字符{} | 四種用法見下 |
字符{}:
1.字符{N} - 字符匹配N次
2.字符{M,N} - 字符匹配M到N次
3.字符{M,} - 字符匹配至少M次
4.字符{,N} - 字符匹配至多N次
練習:寫一個正則表達式判斷輸入的內容是否是整數
r'[+-]?[1-9]\d*'
貪婪和非貪婪
匹配次數不確定時,有貪婪和非貪婪兩種狀態
? * + {M,N} {M,} {,N} 默認是貪婪的
?? *? +? {M,N}? {M,}? {,N}? 加問號變成非貪婪
貪婪:在能匹配成功的前提下,儘可能多的匹配
import re
re_str = r'\d{3,5}'
print(re.search(re_str, 'abc123456789'))
# 結果:<re.Match object; span=(3, 8), match='12345'>
import re
re_str = r'a.+b'
print(re.search(re_str, 'jijalbkajsejfaibkdjfei'))
# 結果:<re.Match object; span=(3, 16), match='albkajsejfaib'>
非貪婪:在能匹配成功的前提下,儘可能少的匹配
import re
re_str = r'\d{3,5}?'
print(re.search(re_str, 'abc123456789'))
# 結果:<re.Match object; span=(3, 6), match='123'>
import re
re_str = r'a.+?b'
print(re.search(re_str, 'jijalbkajsejfaibkdjfei'))
# 結果:<re.Match object; span=(3, 6), match='alb'>
分支和分組
分支
正則1|正則2
先讓正則1去匹配,如果匹配失敗,再用正則2匹配;
只要兩個中有一個能夠匹配成功就成功
例子:匹配三個數字或者三個字母
r'\d{3}|[a-zA-Z]{3}'
例子:匹配一個字符串,abc前是三個數字或三個字母
r'\d{3}abc|[a-zA-Z]{3}abc'
分組
(正則表達式) - 看作一個整體
上一個例子中,分組可以這樣寫:
r'(\d{3}|[a-zA-Z]{3})abc'
(正則表達式)\M - 在\M的位置重複前面第M個分組匹配到的內容
練習:匹配字符串中某一個部分重複前面的
如:abc123abc xab456xab
不能 abc123cab xab789eee
import re
str_re = r'([a-z]{3})\d{3}\1'
result = re.fullmatch(str_re, 'xab123xab')
print(result)
作業
-
寫一個正則表達式判斷一個字符串是否是ip地址
規則:一個ip地址由4個數字組成,每個數字之間用.連接。每個數字的大小是0-255
255.189.10.37 正確
256.189.89.9 錯誤import re str_re = r'(((\d{1,2})|(1\d{2})|(2[0-5]{2})).){3}((\d{1,2})|(1\d{2})|(2[0-5]{2}))' result = re.fullmatch(str_re, '255.1.1.1') print(result) # 0~255 : (\d{1,2})|(1\d{2})|(2[0-5]{2})
這裏錯了,239不行了,應該把200-255分開成200-249,250-255
而且0不能開頭
答案:
-
寫一個正則表達式可以匹配任意有效數字
123 正確; 23.34 正確; -123 正確; +12.34 正確; 0.232 正確; -0.233 正確
0123 錯誤; 012.23 錯誤; 00.23 錯誤
import re str_re = r'[+-]?(([1-9]\d*)|(0\.\d+))' result = re.fullmatch(str_re, '-0.00000') print(result)
錯了,忽略了正常浮點數
答案:
-
驗證輸入的內容只能是漢字
import re str_re = r'[\u4e00-\u9fa5]+' while True: value = input('請輸入漢字, 輸入q退出: ') if value == 'q': exit() result = re.fullmatch(str_re, value) if result: print('輸入正確') else: raise ValueError('輸入的內容必須是純漢字!')
-
驗證是否是有效標識符: 由字母、數字、_組成並且數字不能開頭
import re str_re = r'[a-zA-Z_][a-zA-Z_\d]*' result = re.fullmatch(str_re, '_default') print(result)
-
能夠完全匹配字符串“(010)-62661617”和字符串“01062661617”的正則表達式包括( )
A. r“\(?\d{3}\)?-?\d{8}” B. r“[0-9()-]+” C. r“[0-9(-)]*\d*” D. r“[(]?\d*[)-]*\d*”
ABD
B是迷惑項,方括號中的圓括號不起作用,只是作爲普通字符
C中(-)的-符號是特殊字符,不能作爲普通字符,所以不能匹配字符串中的- -
能夠完全匹配字符串
“c:\\rapidminer\\lib\\plugs”
的正則表達式包括()A. r“c:\rapidminer\lib\plugs” B. r“c:\\rapidminer\\lib\\plugs” C. r“(?i)C:\\RapidMiner\\Lib\\Plugs” ?i:將後面的內容的大寫變成小寫 D. r“(?s)C:\\RapidMiner\\Lib\\Plugs” ?s:單行匹配
BC
A少了 \D沒有轉換小寫
-
能夠完全匹配字符串“back”和“back-end”的正則表達式包括()
A. r“\w{4}-\w{3}|\w{4}” B. r“\w{4}|\w{4}-\w{3}” C. r“\S+-\S+|\S+” D. r“\w*\b-\b\w*|\w*”
ABCD
-
能夠完全匹配字符串“go go”和“kitty kitty”,但不能完全匹配“go kitty”的正則表達式包括()
A. r“\b(\w+)\b\s+\1\b” B. r“\w{2,5}\s*\1” C. r“(\S+) \s+\1” D. r“(\S{2,5})\s{1,}\1”
AD
B沒有分組,所以不能用 \1
C多了一個空格
-
能夠在字符串中匹配“aab”,而不能匹配“aaab”和“aaaab”的正則表達式包括( )
A. r“a*?b” B. r“a{,2}b” C. r“aa??b” D. r“aaa??b”
BC
A的兩頭是a和b,所以只有一個解,在只有一個解的情況下貪不貪婪沒區別
D能匹配aaab,因爲有唯一解,不管貪不貪婪ps:有多解才貪婪!!!