python学习笔记 (6)

python学习笔记 (6)

快速排序

在一个无序序列中,选取一个元素为基准元素,然后设置两个指针,一个low指针指向左侧第一个位置,一个high指针指向右侧最后一个位置。

# 快排
# 选取一个基准值 小的往左移 大的往右移
# [34,32,12,51,75,25,84,1,8]
# [34 32 12 51 25 1 8]  75  [84]
# [34 32 12 25 1 8] 51 []
# [1 8] 12 [34 32 25]
# [] 1 [8] 12 [25] 32 [34]
def quick_sort(num_list: list):
    if len(num_list) <= 1:
        return num_list
    left_list = []
    right_list = []
    mid = num_list[len(num_list) // 2]     #选取基准元素
    num_list.remove(mid)
    for i in num_list:
        if i > mid:
            right_list.append(i)
        else:
            left_list.append(i)
    return quick_sort(left_list) + [mid] + quick_sort(right_list) #过程中取出来的mid也是一个集合
if __name__ == '__main__':
 print(quick_sort([34, 32, 12, 51, 75, 25, 84, 8, 1]))
#[1, 8, 12, 25, 32, 34, 51, 75, 84]

快速排序应用之汉诺塔:

image.png

# 汉诺塔问题
# 将所有的块当成一个整体 n  我们需要做的是 块划分为 n-1 和 1 然后去移动

"""
move(3,A,B,C) -> move(2,A,C,B) -> move(1,A,B,C) -> A --> C
                                  move(1,A,C,B) -> A --> B
                                  move(1,C,A,B) -> C --> B
                 move(1,A,B,C)                  -> A --> C
                 move(2,B,A,C) -> move(1,B,C,A) -> B --> A
                                  move(1,B,A,C) -> B --> C
                                  move(1,A,B,C) -> A --> C
"""

def move(n, a, b, c):    #n是块数
    if n == 1:
        print(f"{a} -> {c}")
    else:
        move(n - 1, a, c, b)
        move(1, a, b, c)
        # print(f"{a} -> {c}")
        move(n - 1, b, a, c)

if __name__ == '__main__':
    move(3, "A", "B", "C")

面向对象

1.初识面向对象

面向对象编程OOP——Object Oriented Programming,是一种程序设计思想。

面向过程的程序设计把计算机程序视为一系列的命令集合,即一组函数的顺序执行,为了简化程序设计,面向过程把函数继续切分为子函数,即把大块函数通过切割成小块函数来降低系统的复杂度 而面向对象的程序设计把计算机程序视为一组对象的集合,而每个对象都可以接收其他对象发过来的消息,并处理这些消息,计算机程序的执行就是一系列消息在各个对象之间传递 在Python中,所有数据类型都可以视为对象,当然也可以自定义对象。自定义的对象数据类型就是面向对象中的类(Class)的概念。

面向对象的设计思想是从自然界中来的,因为在自然界中,类(Class)和实例(Instance)的概念是很自然的。

Class是一种抽象概念,而实例(Instance)则是一个个具体的Student 比如定义的Class——Student,是指学生这个概念, Bart Simpson和Lisa Simpson是两个具体的Student 。

所以,面向对象的设计思想是抽象出Class,根据Class创建Instance 面向对象的抽象程度又比函数要高,因为一个Class既包含数据,又包含操作数据的方法。

面向对象主要有三大特点:封装、继承和多态。

面向对象中的一些专业术语:
类(Class): 用来描述具有相同的属性和方法的对象的集合。它定义了该集合中每个对象所共有的属性和方法。对象是类的实例。

方法:类中定义的函数

类变量:类变量在整个实例化的对象中是公用的。类变量定义在类中且在函数体之外。类变量通常不作为实例变量使用。

数据成员:类变量或者实例变量用于处理类及其实例对象的相关的数据

方法重写:如果从父类继承的方法不能满足子类的需求,可以对其进行改写,这个过程叫方法的覆盖(override),也称为方法的重写

局部变量:定义在方法中的变量,只作用于当前实例的类

实例变量:在类的声明中,属性是用变量来表示的,这种变量就称为实例变量,实例变量就是一个用 self 修饰的变量。

继承:即一个派生类(derived class)继承基类(base class)的字段和方法。继承也允许把一个派生类的对象作为一个基类对象对待 。

实例化:创建一个类的实例,类的具体对象 。

对象:通过类定义的数据结构实例,包括两个数据成员(类变量和实例变量)和方法。

2.类

类的定义格式:

image.png

面向对象最重要的概念就是类(Class)和实例(Instance),必须牢记类是抽象的模板,比如Student类,而实例是根据类创建出来的一个个具体的“对象”,每个对象都拥有相同的方法,但各自的数据可能不同 。

由于类可以起到模板的作用,因此,可以在创建实例的时候,把一些必须绑定的属性强制填写进去。通过定义一个特殊的__init__方法,在创建实例的时候,就把name,score等属性绑上去:

class Student(object):
        def __init__(self, name, score):
        self.name = name
        self.score = score

有了__init__方法,在创建实例的时候,就不能传入空的参数了,必须传入与__init__方法匹配的参数 self不需要传,Python解释器自己会把实例变量传进去。

面向对象编程的一个重要特点就是数据封装。在之前的Student类中,每个实例就拥有各自的name和score这些数据,在类中可以通过函数来访问这些数据,比如打印一个学生的成绩:

既然Student实例本身就拥有这些数据,要访问这些数据,就不需要通过外部函数访问,可直接在Student类的内部定义访问数据的函数,这样就把“数据”给封装起来了。这些封装数据的函数是和Student类本身是关联起来的,称之为类的方法。要定义一个方法,除了第一个参数是self外,其他和普通函数一样。要调用时只需要在实例变量上直接调用,除了self不用传递,其他参数正常传入:这样一来,从外部看Student类,就只需要知道,创建实例需要给出name和score,而如何打印,都是在Student类的内部定义的,这些数据和逻辑被“封装”起来了,调用很容易,但却不用知道内部实现的细节

封装的另一个好处是可以给Student类增加新的方法,比如get_grade:同样的,get_grade方法可以直接在实例变量上调用,不需要知道内部实现细节。

在Class内部,可以有属性和方法,而外部代码可以通过直接调用实例变量的方法来操作数据,这样,就隐藏了内部的复杂逻辑 但是,从前面Student类的定义来看,外部代码还是可以自由地修改一个实例的name、score属性:

如果要让内部属性不被外部访问,可以把属性的名称前加上两个下划线__,只有内部可以访问,外部不能访问:

# 定义类的时候 开头大写
class Student(object):
    # 数据
    name = 'syh'
    score = 61
    # 私有变量 只有类里面可以调用
    __secret = '无糖可口可乐'
    # 定义私有变量 使用一个下滑线
    # 约定俗成 一个下划线的就是私有变量 你不要随便动他
    _secret = '无糖雷碧'

    def set_secret(self, secret):
        self.__secret = secret

    def get_secret(self):
        return self.__secret

    # 私有方法
    def __student_secret_method(self):
        print("学生的私有方法 嘿嘿嘿")

    # 用一个下划线开头用以表示私有方法
    def _student_secret_method(self):
        print("学生的私有方法 嘿嘿嘿")

    # 初始化方法  、  构造函数
    def __init__(self, name, score):
        self.name = name
        self.score = score

    # 操作数据的函数
    def print_score(self):
        self.__student_secret_method()
        print(f'I\'m {self.name} score is {self.score}')

    def get_grade(self):
        if self.score > 60:
            print("good")

    # def get_grade(self,score):
    #     print(score)
    #     if self.score > 60:
    #         print("good")


def print_sco(name, score):
    print(f"{name},{score}")


if __name__ == '__main__':
    syh = Student("syh", 61)
    syh.print_score()
    syh.score = 90
    syh.print_score()
    print(Student)
    # 强行加一个属性  其他语言不要这么干  自己写python也不要这么干
    syh.age = 18
    print(syh.age)
    # 定义的外部方法
    print_sco(syh.name, syh.score)
    # AttributeError: 'Student' object has no attribute '__secret'
    # print(syh.__secret)
    # 强行访问类中的私有变量
    print(syh._Student__secret)
    print(syh._secret)
    syh.set_secret("香草可口可乐")
    print(syh._Student__secret)
    print(syh.get_grade(60))

3.继承和多态

在OOP程序设计中,当定义一个class的时候,可以从某个现有的class继承,新的class称为子类(Subclass),而被继承的class称为基类、父类或超类(Base class、Super class)。

继承有什么好处?最大的好处是子类获得了父类的全部功能。

# 继承
class Aniaml:

    def __init__(self, leg, ear, mouth):
        self.leg = leg
        self.ear = ear
        self.mouth = mouth
        pass

    def run(self):
        print("Animal is run")

    def ear_method(self):
        print(f"{self.ear} is good")
        
class Canidae:                   #动物之下的犬科
    def __init__(self,nose):
        self.nose = nose
    def nose_good(self):
        print("Canidae nose is good")
        
class Dog(Aniaml, Canidae):        #犬科中的dog类

    def __init__(self, leg, ear, mouth, nose):
        # 传入父类的构造方法
        super().__init__(leg, ear, mouth)
        # super().__init__(nose)
        # self.leg = leg
        # self.ear = ear
        # self.mouth = mouth
        self.nose = nose
        pass

    def run(self):
        print("dog is run and bark")
if __name__ == '__main__':
    spike = Dog(4, 2, 1, 1)
    spike.run()
    spike.ear_method()
    spike.nose_good()

当子类和父类都存run()方法时,可以看做子类的run()覆盖了父类的run(),在代码运行的时候,总是会调用子类的run()。这样就获得了继承的另一个好处:多态。

# 多态 : 父类的引用指向子类的对象

class Animal():
    def run(self):
        print("Animal is running")

class Dog(Animal):
    def run(self):
        print("dog is running and barking")
    pass

class Cat(Animal):
    def run(self):
        print("cat is running and arresting")

class Mouse(Animal):
    def run(self):
        print("mouse is running and digging")
    pass

def animal_run_twice(animal:Animal):
    animal.run()
    animal.run()

if __name__ == '__main__':
    spike = Dog()
    tom = Cat()
    jerry = Mouse()
    jerry.run()
    tom.run()
    spike.run()

    tyke = Animal()

    animal_run_twice(tyke)
    animal_run_twice(spike)
1.鸭子类型

鸭子类型是对Python中数据类型本质上是由属性和行为来定义的一种解读。

Python是一种动态语言,不像Java和C++这种强类型语言,Python里实际上没有严格的类型检查。

只要某个对象具有鸭子的方法,可以像鸭子那样走路和嘎嘎叫,那么它就可以被其它函数当做鸭子一样调用。

class Duck():
    def __init__(self, name):
        self.name = name
    def swim(self):
        print(f"a duck {self.name} is swimming")

    def call(self):
        print(f"a duck {self.name} is 桀桀桀")

class Goose():       #鹅也会像鸭子一样
    def __init__(self,name):
        self.name = name

    def swim(self):
        print(f"a goose {self.name} is swimming")

    def call(self):
        print(f"a goose {self.name} is 鹅鹅鹅")

def duck_show(duck:Duck):
    duck.swim()
    duck.call()

if __name__ == '__main__':
    bianzuilun = Duck("bianzuilun")
    mrping = Goose("Mr.ping")

    duck_show(bianzuilun)
    duck_show(mrping)
 #结果
a duck bianzuilun is swimming
a duck bianzuilun is 桀桀桀
a goose Mr.ping is swimming
a goose Mr.ping is 鹅鹅鹅   

由于Python是动态语言,没有严格类型检查,Goose这个类具有和Duck这个类相同的方法,Mr.ping这只鹅划起水来像只鸭子,叫起来也像一只鸭子,所以duck_show这个函数也可以对Mr.ping进行作用,故Python其数据类型属于鸭子类型.

2.猴子补丁

猴子补丁是对Python中模块和类可以在外部被动态修改这种特性的一个比喻。

在模块和类的外部对模块和类进行修改是一种非常耍赖的做法,会破坏代码的封装结构,这种事情大概只有淘气的猴子喜欢去做,因此形象地称之为猴子补丁。

class Dog():
    def __init__(self,name):
        self.name = name

    def bark(self):
        print(f"{self.name} is wangwangwang")

def eat(self): #在Dog类的外面定义了一个eat的方法
    print(f"dog is eatting")

if __name__ == '__main__':
    snoopy = Dog("snoopy")
    snoopy.bark()

    snoopy.leg = 4
    print(snoopy.leg)
    Dog.eat = eat
    snoopy.eat()
    #
    snoopy is wangwangwang
    4
    dog is eatting

异常处理

在程序运行过程中,总会遇到各种各样的错误。有的错误是程序编写有问题造成的,比如本来应该输出整数结果输出了字符串,这种错误通常称之为bug,bug是必须修复的。 有的错误是用户输入造成的,比如让用户输入email地址,结果得到一个空字符串,这种错误可以通过检查用户输入来做相应的处理 还有一类错误是完全无法在程序运行过程中预测的,比如写入文件的时候,磁盘满了,写不进去了,或者从网络抓取数据,网络突然断掉了。这类错误也称为异常,在程序中通常是必须处理的,否则,程序会因为各种问题终止并退出。 Python内置了一套异常处理机制,来帮助我们进行错误处理。

Python中的错误可以分为两种:语法错误和异常

语法错误(Syntax errors) :代码编译时的错误,不符合Python语言规则的代码会停止编译并返回错误信息。

异常(Exceptions) :相较于语法错误,异常比较难发现,因为它只在代码运行时才会发生, 如类型错误、数值错误、索引错误和属性错误等。

pyton异常类层级区别:
image.png

Python中的主要错误:

image.png

常见的语法错误:缺少起始符号或结尾符号(括号、引号等),缩进错误,关键词拼写错误。

六种典型的异常:

除零错误(ZeroDivisionError):除数为0。

名称错误(NameError):变量使用前未进行申明或者初始化。

类型错误(TypeError):某些函数或者方法只适用于特定的数据类型,如果 对数据类型的操作不当,就会产生类型错误。

数值错误(ValueError):在输入类型正确的情况下,具体输入值错误。

索引错误(IndexError):超出序列长度的索引操作。

属性错误(AttributeError):方法或者属性不适用该对象 。

1.处理异常

高级语言通常都内置了一套错误处理机制,Python也不例外 在Python中可通过try…except…else…finally…机制捕获异常并进行处理。

image.png
# 异常处理
try:
    # 放的是可能会出错需要 异常捕获的代码
    int("3.0")
except TypeError as e:
    # 上述代码出错执行以下代码
    print(e)
    print("代码出错1")
except ValueError as e:
    print(e)
    print("代码出错2")
else:
    # 上述try代码执行正常 执行如下代码
    print("dog eat")
finally:
    # 不管代码出不出错都会执行下述代码
    print("代码检测完毕")

在实际使用中,定义每一种错误太过于麻烦,因此我们常用:

except: 是 Python 中最简单的异常处理语句,它可以捕获并处理任何类型的异常。当程序执行过程中发生异常时,如果使用 except:,那么这个 except: 块会捕获该异常,而不管这个异常的类型是什么。

except Exception as e: 只会捕获 Exception 及其子类的异常。当程序执行过程中发生异常时,如果使用 except Exception as e:,那么这个 except Exception as e: 块只会捕获 Exception 及其子类的异常,其他类型的异常将不会被捕获。

2.自定义异常和抛出异常

在Python中可以通过创建一个新的异常类来拥有自己的异常。

自定义异常的原因 Python提供的内建异常不够用 可以预估某个错误的产生。

定义异常类 :异常类继承自 Exception 类,可以直接继承,或者间接继承。

抛出异常:Python 使用 raise 语句抛出一个指定的异常。

raise 需要指定了要被抛出的异常。它必须是一个异常的实例或者是异常的类(也就是 Exception 的子类)。

class WeightError(Exception):#定义一个体重异常类
    pass
syh_weight = 110
if syh_weight < 120:
    # 抛出异常
    raise WeightError(" 体重太轻了")

print("hhhhhh")#无异常输出
#
raise WeightError(" 体重太轻了")
__main__.WeightError:  体重太轻了

文件读写

读写文件是最常见的IO操作。Python内置了读写文件的函数,用法和C是兼容的。

1.读文件

要以读文件的模式打开一个文件对象,使用Python内置的open()函数,传入文件名和标示符:

open() 函数常用形式是接收两个参数:文件名(file)和模式(mode)

image.png

参数说明:

  • file: 必需,文件路径(相对或者绝对路径)
  • mode: 可选,文件打开模式,默认为r
  • encoding: 一般使用utf8编码
  • errors: 报错级别
  • newline: 区分换行符
  • buffering: 设置缓冲
  • closefd: 传入的file参数类型
image.png
#绝对路径
f=open("F:/No_30//test.py", 'r', encoding='utf-8')
#相对路径 . ..
f = open("./test.py", 'r', encoding='utf-8')
fp = open("../data/data.txt", "r", encoding='utf-8'

'r’标示符表示读,这样成功地打开了一个文件 如果文件不存在,open()函数就会抛出一个IOError的错误.

如果文件成功打开,调用read方法可以把内容读到内存,用一个str对象接收:

# read()  读取数据  不带参数  读取所有
# 读完之后在读  就没有东西了
# print(fp.read())
# read() 带参数 则读几个字符
# print(fp.read(1))

调用read()会一次性读取文件的全部内容,如果文件有10G,内存可能不够用,所以,要保险起见,可以反复调用read(size)方法,每次最多读取size个字节的内容

另外,调用readline()可以每次读取一行内容,调用readlines()一次读取所有内容并按行返回list。因此,要根据需要决定怎么调用 。

如果文件很小,read()一次性读取最方便;如果不能确定文件大小,反复调用read(size)比较保险;如果是配置文件,调用readlines()最方便,然后通过for…in循环遍历即可获取全部配置。

# readline() 读取一行
# readline(x) 读x个字符
# print(fp.readline(1))
# print(fp.readline(2))

# readable() 确定文件可不可读
# print(fp.readable())

# readlines() 每一行读进列表里
# readlines(x) 读取包含x个字符的所有行数
print(fp.readlines(7))
print(fp.readlines(1))


fp.close()   #文件使用完毕后调用close()方法可以关闭文件

例子:

# 3、创建一个学生成绩文件txt,里面放着学生的基本信息和成绩,使用Python读取其中文件,按照成绩排序输出姓名。
def ques3():
    fp = open("../data/data.txt", 'r', encoding='utf-8')
    stu_name_sco = fp.readlines()
    stu_dic = {i.strip().split(",")[0]: int(i.strip().split(",")[1]) for i in stu_name_sco}
    print(stu_dic)
    # sorted 排序方法
    print(sorted(stu_dic.items(), key=lambda x: x[1]))
    # print(sorted(stu_dic.keys()))    
fp.close()

2.写文件

写文件和读文件是一样的,唯一区别是调用open()函数时,传入标识符’w’或者’wb’表示写文本文件或写二进制文件:

strs = 'xxx is 18,pretty good'
fp = open("./data.txt", 'a', encoding='utf-8') #追加写
fp.write(strs + "\n")
fp.close()
# 读取一个图片 然后写出去
# 读二进制 写二进制没有encoding   重要
# fp_r = open(r"C:\Users\wallhaven-6dg7ll.png", 'rb')
# png_content = fp_r.read()
# print(png_content)
# fp_r.close()
# fp_w = open("./xxx.png", 'wb')
# fp_w.write(png_content)
# fp_w.close()

当写文件时,操作系统不会立刻把数据写入磁盘,而是放到内存缓存起来,空闲的时候再慢慢写入。

3.使用with语句

由于文件读写时都有可能产生IOError,一旦出错,后面的f.close()就不会调用。所以,为了保证无论是否出错都能正确地关闭文件,可以使用try … finally来实现。但每次都这么写实在太繁琐,所以Python引入了with语句来自动调用close()方法。

这和try … finally是一样的,但代码更佳简洁,并且不必调用f.close()方法。

# 整个代码内只需要打开或者写一次  建议使用with open
# 如果全代码好几个地方需要写 建议使用 open
with open("./data.txt", 'r', encoding='utf-8') as fp:
    print(fp.readlines())

正则表达式

正则表达式是一种用来匹配字符串的强有力的武器。它的设计思想是用一种描述性的语言来给字符串定义一个规则,凡是符合规则的字符串,就认为它“匹配”了,否则,该字符串就是不合法的。

Python中常见的元字符有:

image.png

要匹配变长的字符,在正则表达式中,用*表示任意个字符(包括0个),用+表示至少一个字符,用?表示0个或1个字符,用{n}表示n个字符,用{n,m}表示n~m个字符。

要做更精确地匹配,则还需编写更复杂的正则表达式:

在正则中-表示从什么到什么,[]表示范围。

[0-9a-zA-Z_]可以匹配一个数字、字母或者下划线

[0-9a-zA-Z_]+可以匹配至少由一个数字、字母或者下划线组成的字符串

[a-zA-Z\_][0-9a-zA-Z\_]*可以匹配由字母或下划线开头,后接任意个由一个数字、字母或者下划线组成的字符串,也就是Python合法的命名规则

[a-zA-Z\_][0-9a-zA-Z\_]{0, 19}更精确地限制了长度是1-20个字符(前面1个字符,后面最多19个字符)

A|B可以匹配A或B,所以(P|p)ython可以匹配’Python’或者’python’

表示行的开头,\d表示必须以数字开头

KaTeX parse error: Undefined control sequence: \d at position 8: 表示行的结束,\̲d̲表示必须以数字结束

Python提供re模块,包含所有正则表达式的功能。

由于Python的字符串本身也用\转义,所以要特别注意,推荐在Python中进行正则表达式匹配时,使用r前缀标记字符串.

re模块提供了一个match方法,可以判断正则表达式是否匹配.

match()方法判断是否匹配,如果匹配成功,返回一个Match对象,否则返回None。

import re
# 正则表达式
# 1、用一些字符去描述字符
# 2、如果直接给出字符就是精确匹配
a = 'wy'
print(re.match('wy', a))
# \d 匹配一个数字字符
print(re.match('\d\d\d', '012'))
# \w 匹配一个字母或者数字 和 _
print(re.match('\w\w\w', '01A'))
# . 可以匹配任意字符 除\n
print(re.match('...', 'w12'))
# \s 可以匹配任意空白字符 、 换页、 换行、 制表符
print(re.match('\s\s\s\s', ' \n\t\f'))

# 匹配个数符号  {num} 表示匹配这么多 少了返回空 多了返回num长
print(re.match('\d{11}', '0123413131231'))
print(re.match('\d{8,11}', '0123413131231'))

# 0551-1234567 010-1234567 匹配电话号码
print(re.match('\d{3,4}-\d{7}', '010-1234567'))#\用来转义
# * 匹配任意长度的字符
# + 匹配至少一个字符
# ? 匹配0或1个字符
# 匹配一个变量名
print(re.match('[a-zA-Z_]\w*', 'a_b'))
print(re.match('[Pp]ython', 'Python'))

s = 'ABC\\-001'
print(re.match(r"\w+\\-\d+", s))


s = "Hello 1234567 is a number. Regex String"
print(re.match("Hello (\d*).*", s))

match方法通常会结合if选择结构进行判断:

 #1、编写程序实现下述功能,提示用户输入用户名,要求用户名以字母开头,长度不少于3位,只能包含字母、数字、下划线,
# 如果用户输入符合要求,则提示注册成功,否则提示用户名不符合要求,请重新输入,一直循环直到用户名符合要求为止。

def ques1():
    while True:
        name = input("请输入姓名:")
        if re.match("[a-zA-Z]\w{2,}", name):
            print(name)
            break
if __name__ == '__main__':
 ques1()

1.切分字符串

re模块也提供了split方法,可以按照指定的正则表达式进行字符串的切分:

a = "a,b|c#d,,e"
print(a.replace("|", ",").replace("#", ",").split(","))#字符串的方法

print(re.split(r"[,|#]+", a))
print(re.split("[,;\s]+", "a,b;; c  d"))
#
['a', 'b', 'c', 'd', '', 'e']
['a', 'b', 'c', 'd', 'e']
['a', 'b', 'c', 'd']

2.分组

除了简单地判断是否匹配之外,正则表达式还有提取子串的强大功能。用()表示的就是要提取的分组(Group)。

如果正则表达式中定义了组,就可以在Match对象上用group()方法提取出子串来 注意:group(0)永远是与整个正则表达式相匹配的字符串,group(1)、group(2)…表示第1、2、……个子串。

# 分组 group
# groups 返回一个元组 记录所有括号获取的值
# group(num) 0 整个正则表达式相匹配的字符串  1 返回一个获取符合的字符串
# 340122200601014212
id_str = '340322200601014232'  #身份证号
print(re.match("(\d{6})(\d{8})(.*)", id_str).groups())
print(re.match("(\d{6})(\d{8})(.*)", id_str).group(0))
print(re.match("(\d{6})(\d{8})(.*)", id_str).group(1))
print(re.match("(\d{6})(\d{8})(.*)", id_str).group(2))
#('340322', '20060101', '4232')
#340322200601014232
#340322
#20060101

来看一个经典应用:

匹配时分秒:

# 匹配时分秒
# 24小时制
time_str = '09:09:10'
print(re.match("([0-1][0-9]|2[0-3]):([0-5]\d):([0-5]\d)", time_str).group(1))
print(re.match("([0-1][0-9]|2[0-3]):([0-5]\d):([0-5]\d)", time_str).group(2))
print(re.match("([0-1][0-9]|2[0-3]):([0-5]\d):([0-5]\d)", time_str).group(3))

但是有些时候,用正则表达式也无法做到完全验证,比如识别日期: ‘^(0[1-9]|1[0-2]|[0-9])-(0[1-9]|1[0-9]|2[0-9]|3[0-1]|[0-9])$’ 对于’2-30’,'4-31’这样的非法日期,该正则无法识别,或者说写出来非常困难,这时就需要程序配合识别了。

3.编译

当在Python中使用正则表达式时,re模块内部会干两件事情: 编译正则表达式,如果正则表达式的字符串本身不合法,会报错 用编译后的正则表达式去匹配字符串 如果一个正则表达式要重复使用几千次,出于效率的考虑,可以使用compile方法预编译该正则表达式,接下来重复使用时就不需要编译这个步骤了,直接匹配:

# 预编译 遇到很多相同样式的匹配的时候使用
num0_pattern = re.compile("(\d+?)(0*)$")
print(num0_pattern.match("10230000").groups())
print(num0_pattern.match("10230"))
print(num0_pattern.match("102"))
print(num0_pattern.match("102300000000"))
#
('1023', '0000')
<re.Match object; span=(0, 5), match='10230'>
<re.Match object; span=(0, 3), match='102'>
<re.Match object; span=(0, 12), match='102300000000'>

4.贪婪匹配

正则匹配默认是贪婪匹配,也就是匹配尽可能多的字符:

例如,匹配出数字后面的0:

# 贪婪匹配  匹配所有符合规则的字符串
print(re.match("(\d+)(0*)$", '10230000').groups())#以0结尾
#结果
('10230000', '')

由于\d+采用贪婪匹配,直接把后面的0全部匹配了,结果0*只能匹配空字符串。

所以我们必须改变这种模式:必须让\d+采用非贪婪匹配(也就是尽可能少匹配),才能把后面的0匹配出来,加个?就可以让\d+采用非贪婪匹配:

# ? 进入懒惰模式
print(re.match("(\d+?)(0*)$", '10230000').groups())
#结果
('1023', '0000')

5.一个例子

请编写一个正则表达式,用于验证密码格式是否符合要求: 只能是大小写字母或数字或英文标点符号,不能是空白字符 长度至少为10位,且必须至少包含一个大写字母、一个小写字母、一个符号:

def ques3():
    # 匹配英文标点符号
    print(re.match("[!-~]", "&*(#$%&"))
    # print(re.match("(?=.*[A-Z])(?=.*[a-z])(?=.*[!-~])[0-9A-Za-z!-~]", "&*(#$%&"))
    a = 'eqw231HJP^$#'
    # 检测是否存在大写字母
    if re.match(".*?[A-Z]+.*",a):
        if re.match(".*?[a-z]+.*",a):
            if re.match(".*?[!-~]+.*", a):
                if re.match("[0-9A-Za-z!-~]{10,}", a):
                    print("密码规则正确")
if __name__ == '__main__':
        ques3()

  • 40
    点赞
  • 48
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值