我的Python學習筆記_Day17 正則表達式 自定義異常 內存管理機制 拷貝

自定義異常

要求這個類必須繼承Exception
__str__返回值就是錯誤信息描述

class MyOnlyError(Exception):
    def __str__(self):
        return '報錯信息'

內存管理基礎

  1. c語言

    內存分爲棧區間和堆區間

    棧區間的內存是系統自動申請自動釋放

    堆上的內存需要程序通過調用malloc函數取申請,通過調用free函數去釋放

  2. 高級語言(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)

作業

  1. 寫一個正則表達式判斷一個字符串是否是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不能開頭

    答案:
    在這裏插入圖片描述

  2. 寫一個正則表達式可以匹配任意有效數字

    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)
    

    錯了,忽略了正常浮點數

    答案:

    在這裏插入圖片描述

  3. 驗證輸入的內容只能是漢字

    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('輸入的內容必須是純漢字!')
    
  4. 驗證是否是有效標識符: 由字母、數字、_組成並且數字不能開頭

    import re
    
    str_re = r'[a-zA-Z_][a-zA-Z_\d]*'
    result = re.fullmatch(str_re, '_default')
    print(result)
    
  5. 能夠完全匹配字符串“(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中(-)的-符號是特殊字符,不能作爲普通字符,所以不能匹配字符串中的-

  6. 能夠完全匹配字符串“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沒有轉換小寫

  7. 能夠完全匹配字符串“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

  8. 能夠完全匹配字符串“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多了一個空格

  9. 能夠在字符串中匹配“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:有多解才貪婪!!!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章