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可以匹配一个数字
- ‘00\d’可以匹配’007’,但无法匹配’00A’
- ‘\d\d\d’可以匹配’010’
- \w可以匹配一个字母或数字
- ‘\w\w\d’可以匹配’py3’
- '.'可以匹配任意字符
- 'py.‘可以匹配’pyc’、‘pyo’、'py!'等等
- \s可以匹配任何空白字符,包括空格、制表符、换页符等等
- \D,\W,\S分别对应相反的匹配
- ‘’\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} 不是一个有效的身份证号码。")