Python进阶

本文介绍了Python中的面向对象编程,包括类和实例的概念,以及封装、继承和多态的特点。同时讲解了异常处理机制,如try...except...else...结构,并展示了如何自定义异常。还探讨了文件的读写操作,以及正则表达式的基础知识,如匹配规则和re模块的使用。
摘要由CSDN通过智能技术生成

Python进阶(Python基础可以参考我上篇文章)

1 面向对象

  • 面向对象编程OOP——Object Oriented Programming,是一种程序设计思想,OOP把对象作为程序的基本单元,一个对象包含了数据和操作数据的函数

  • 在Python中,所有数据类型都可以视为对象,当然也可以自定义对象。自定义的对象数据类型就是面向对象中的类(Class)的概念

  • 面向对象的抽象程度又比函数要高,因为一个Class既包含数据,又包含操作数据的方法

class Student(object):
    def __init__(self, name, score, boss):  # name,score,boss都是参数
        #有了__init__方法,在创建实例的时候,就不能传入空的参数了,必须传入与__init__方法匹配的参数
        self.name = name
        self.score = score
        self.boss = boss  # 初始化变量存入实参

    def print_score(self):  # 函数
        print(f"{self.name}的成绩是:{self.score}")


student1 = Student("Jack", 99, "cheng")  # 对象的初始化,创建实例
print(student1.name)
student1.print_score()  # 调用函数
Jack
Jack的成绩是:99

1.1 类的定义

Class是一种抽象概念,而实例(Instance)则是一个个具体的Student

  • 比如定义的Class_Student,是指学生这个概念
  • Jack是具体的Student
  • 面向对象的设计思想是抽象出Class,根据Class创建Instance

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

  • 封装:将一段代码放入一个函数中,使用这段代码时就调用这段函数
  • 继承:一个派生类(子类)继承基类(父类)的所有属性和方法。继承也允许把一个派生类的对象作为一个基类对象对待
  • 多态:父类的引用指向子类的对象
#封装
#如果要让内部属性不被外部访问,可以把属性的名称前加上两个下划线__,只有内部可以访问,外部不能访问
class Student(object):
    def __init__(self, name, score, boss):  
        self.__name = name
        self.__score = score
        self.__boss = boss
#加两个下划线可以让外部不能访问
#我们通常加一个下划线,虽然不能阻止外部访问,但以此来警示别人这个属性不允许外部访问,这是一个规矩
#继承和多态
class Animal(object):
    def run(self):
        print('Animal is running...')
#当需要编写Dog和Cat类时,就可以直接从Animal类继承
class Dog(Animal):
    pass
class Cat(Animal):
    pass
#对于Dog来说,Animal就是它的父类,对于Animal来说,Dog就是它的子类。Cat和Dog类似。
#继承最大的好处是子类获得了父类的全部功能。由于Animial实现了run()方法,因此,Dog和Cat作为它的子类,什么事也没干,就自动拥有了run()方法
dog = Dog()
dog.run()
cat = Cat()
cat.run()
#运行结果如下:
Animal is running...
Animal is running...
#无论是Dog还是Cat,它们run()的时候,显示的都是Animal is running...,不符合逻辑,因此,对Dog和Cat类改进如下:
class Dog(Animal):
    def run(self):
        print('Dog is running...')

class Cat(Animal):
    def run(self):
        print('Cat is running...')
#再次运行,结果如下:
Dog is running...
Cat is running...
#当子类和父类都存run()方法时,可以看做子类的run()覆盖了父类的run(),在代码运行的时候,总是会调用子类的run()。这样就获得了继承的另一个好处:多态

当然,也可以对子类增加一些方法,比如Dog类:

class Dog(Animal):
    def run(self):
        print('Dog is running...')
    def eat(self):
        print('Eating meat...')

一些专业术语:

  • 实例变量:在类的声明中,属性是用变量来表示的,这种变量就称为实例变量,实例变量就是一个用 self 修饰的变量,可以理解为实例的属性。例self.name
  • 实例化:创建一个类的实例,类的具体对象。即Student类中的具体实例student1
  • 对象:通过类定义的数据结构实例,包括两个数据成员(类变量和实例变量)和方法
  • 类与实例就是Student和student1的关系
#   有一对兔子,从出生后第3个月起每个月都生一对兔子,小兔子长到第三个月后每个月又生一对兔子,假如兔子都不死,问每个月的兔子总数为多少?
#   类的思想写兔子问题
class Rabbit():
    def __init__(self):
        self.age = 0    #   初始化变量  
        
    def grow_age(self):
        self.age += 1

if __name__ == '__main__':
    total = [Rabbit()]          #   ra1 = Rabbit()  ra2 = Rabbit()...
    month = 12
    #   每只兔子都有一个相同的规律,每过一个月,年龄就长一个月
    #   年龄>=三个月,生一只兔子,所以可以构造两个for循环
    for i in range(month):
        for ra in total:
            ra.grow_age()
            if ra.age >= 3:
                total.append(Rabbit())
    print(len(total))

    pass
144

2. 异常处理

  • try…except…else…
try:
	# 正常运行
except Exception as e:
	# 异常时执行的代码
else:
	# 正常时执行
finally
	# 无论对错都执行
#	else和finally可以不用写
  • Python 使用 raise 语句抛出一个指定的异常
class AgeNegativeError(Exception):		#   继承Exception类
    pass
    try:
        age = int(input("请输入你的年龄:"))
    except Exception as e:
        print(e)
        age = -1
    if age > 200:
        print("你已成仙!!")
    elif age >= 18:
        print("你已成年!!")
    elif age > 0:
        print("你未成年!!")
    else:
        raise AgeNegativeError("年龄错误")
        print("输入错误")
请输入你的年龄:-1
Traceback (most recent call last):
  File "E:\shujia\code\python\7month18\demo2TryExcept.py", line 40, in <module>
    raise AgeNegativeError("年龄错误")
__main__.AgeNegativeError: 年龄错误

3 文件读写

3.1 打开文件

fp = open("../data/data.student.txt", "w",encoding="utf-8")
fp.close()
#	用上面的方法打开文件,需要在最后关闭文件fp.close()
#	但每次都这么写实在太繁琐,所以Python引入了with语句来自动调用close()方法
with open("../data/data.student.txt", "w", encoding="utf-8") as fp:

3.2 读文件

#	read(size)方法,每次最多读取size个字节的内容
#	调用readline()可以每次读取一行内容,调用readlines()一次读取所有内容并按行返回list
#	通过for...in循环遍历
for line in f.readlines():
    print(line.strip())	#	把末尾的'\n'删掉

3.3 写文件

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

4 正则表达式

4.1 定义

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

4.2 常见字符

  • \d可以匹配一个数字
    1. ‘00\d’可以匹配’007’,但无法匹配’00A’
    2. ‘\d\d\d’可以匹配’010’
  • \w可以匹配一个字母或数字
    1. ‘\w\w\d’可以匹配’py3’
  • '.'可以匹配任意字符
    1. 'py.‘可以匹配’pyc’、‘pyo’、'py!'等等
  • \s可以匹配任何空白字符,包括空格、制表符、换页符等等
  • \D,\W,\S分别对应相反的匹配
    1. ‘’\D’匹配一个非数字字符
4.2.1 精确地匹配
4.2.1.1 匹配变长的字符

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

  • \d{3}表示匹配3个数字,例如’010’
  • \s可以匹配一个空格(也包括Tab等空白符),所以\s+表示至少有一个空格,例如匹配 ‘空格’,‘空格空格’,'空格空格空格’等;
  • \d{3,8}表示3-8个数字,例如’1234567’,‘123’,‘12345’

综上,该正则表达式可以匹配以任意个空格隔开的带三位区号的电话号码

  • 例:‘010-12345’ 由于’-‘是特殊字符,要用’\'转义,所以可用\d{3}-\d{3,8}进行匹配,需要转义的字符给我们添加了很大的不便,所以我们在**匹配前加’r’**就不再需要考虑转义
4.2.1.2 进阶

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

  • [0-9a-zA-Z_]可以匹配一个数字、字母或者下划线
  • [0-9a-zA-Z_]+可以匹配至少由一个数字、字母或者下划线组成的字符串
  • A|B可以匹配A或B,所以(P|p)ython可以匹配’Python’或者’python’
  • 表示行的开头,\d表示必须以数字开头
  • KaTeX parse error: Undefined control sequence: \d at position 8: 表示行的结束,\̲d̲表示必须以数字结束

4.3 re模块

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

此处就是上面2.1.1说到的**'r’方法**标记字符串,使得不再需要考虑转义的问题

s = r’ABC-001’ 对应 ‘ABC-001’

4.4 match方法

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

import re 使用前需要先导入re模块

import re

str_b = 'python'
#   <re.Match object; span=(0, 6), match='python'>
print(re.match('python', str_b))

# + 表示的是{1,∞}
str_d = '\t\r'
print(re.match('\s+', str_d))

# ? 表示的是{0,1}
str_e = '\ta\nc\nb'
print(re.match('^[\s\w]+$', str_e))
<re.Match object; span=(0, 6), match='python'>
<re.Match object; span=(0, 2), match='\t\r'>
<re.Match object; span=(0, 6), match='\ta\nc\nb'>

4.5 切分字符串

Python中的字符串自带split方法可以指定分隔符进行字符串的切分

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

import re
# split------字符串
str_a = 'a,,b:c.d'
# print(str_a.split(',')[2].split(':'))
print(re.split('[,:.]', str_a))
# split切割方法
print(re.split(r'\W+', 'a,b;; c  d'))
['a', '', 'b', 'c', 'd']
['a', 'b', 'c', 'd']

4.6 分组

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

import re

str_f = '010-12345'
# group方法可以提取字串,将需要提取的字串在正则里面用()包围起来
# group(0)是与整个正则表达式相匹配的字符串 group(1)就是与第一个括号相匹配的字符串
print(re.match(r'^(\d+)-(\d+)$',str_f).group(2))
# groups() 返回的是一个元组 元组内包含所有匹配的字符串
print(re.match(r'^(\d+)-(\d+)$',str_f).groups()[0])
print('*'*20)

# 14:25:30
str_g = '14:25:30'
re_str = r'([0|1]\d|[2][0-3]):([0-5]\d):([0-5]\d)'
print(re.match(r'([0|1]\d|[2][0-3]):([0-5]\d):([0-5]\d)',str_g))
print(re.match(r'([0|1]\d|[2][0-3]):([0-5]\d):([0-5]\d)',str_g).group(1))
print(re.match(r'([0|1]\d|[2][0-3]):([0-5]\d):([0-5]\d)',str_g).group(2))
print(re.match(r'([0|1]\d|[2][0-3]):([0-5]\d):([0-5]\d)',str_g).group(3))
12345
010
********************
<re.Match object; span=(0, 8), match='14:25:30'>
14
25
30

4.7 贪婪匹配

import re

# 贪婪匹配
print(re.match(r'^(\d+?)(0*)$', '102300').groups())
print(re.match(r'^(\w+?)(a\w*)$', 'youaregood').groups())
('1023', '00')
('you', 'aregood')

4.8 编译

import re

str_f = '010-12345'
re_pattern = re.compile(r'^\d+-\d+$')
print(re_pattern.match(str_f))
str_f = '010-12345'

5.综合练习

1、请尝试写一个验证Email地址的正则表达式,可以匹配如下邮箱格式:google@gmail.com

windows@microsoft.com

import re

# google@gmail.com
# windows@microsoft.com
# 1079417476@qq.com
# user@163.com
def validate_email(email):
    pattern = r'[0-9a-zA-Z._%+-]+@(gmail|microsoft|qq|163).com'
    return re.match(pattern,email) is not None

str_email = ['google@gmail.com', 'windows@microsoft.com', '10792318@qq.com',
             'invalid-email', 'john.doe@example', 'vascd@3241.com',
             'Ccsummer617@163.com']
for i in str_email:
    if validate_email(i) is True:
        print(f'{i}是一个有效的邮箱地址!')
    else:
        print(f'{i}是一个无效的邮箱地址!')
google@gmail.com是一个有效的邮箱地址!
windows@microsoft.com是一个有效的邮箱地址!
10792318@qq.com是一个有效的邮箱地址!
invalid-email是一个无效的邮箱地址!
john.doe@example是一个无效的邮箱地址!
vascd@3241.com是一个无效的邮箱地址!
Ccsummer617@163.com是一个有效的邮箱地址!

2、身份证验证

from datetime import datetime
def validate_chinese_id_card(id_card):
    # 校验身份证号码长度
    if len(id_card) != 18:
        return False

    # 校验身份证号码格式
    if not id_card[:-1].isdigit() or not id_card[-1].isdigit() and id_card[-1].upper() != 'X':
        return False

    # 校验身份证号码地区代码
    province_code = id_card[:2]
    if province_code not in PROVINCE_CODES:
        return False

    # 校验身份证号码生日
    birth_date = id_card[6:14]
    try:
        int(birth_date)
        year, month, day = int(birth_date[:4]), int(birth_date[4:6]), int(birth_date[6:])
        datetime(year, month, day)
    except ValueError:
        return False

    # 校验身份证号码校验位
    if calculate_id_card_check_digit(id_card[:-1]) != id_card[-1].upper():
        return False
    return True

def calculate_id_card_check_digit(id_card_base):
    if len(id_card_base) != 17 or not id_card_base.isdigit():
        return None

    # 加权因子
    weight_factors = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2]
    # 校验码对应值
    check_values = '10X98765432'
    # 计算加权和
    weighted_sum = sum(int(digit) * factor for digit, factor in zip(id_card_base, weight_factors))
    # 取模得到校验位的索引
    check_index = weighted_sum % 11
    return check_values[check_index]

# 省份和地区代码对应表
PROVINCE_CODES = [
    '11', '12', '13', '14', '15', '21', '22', '23', '31', '32',
    '33', '34', '35', '36', '37', '41', '42', '43', '44', '45',
    '46', '50', '51', '52', '53', '54', '61', '62', '63', '64',
    '65', '71', '81', '82'
]
# 测试
id_card = input('请输入你的身份证号码:')
if validate_chinese_id_card(id_card):
    print(f"{id_card} 是一个有效的身份证号码。")
else:
    print(f"{id_card} 不是一个有效的身份证号码。")
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值