Python 第二节
2025-03-25
一、面向对象编程
1、几个概念
面向对象、面向过程
类
对象
属性、方法
封装
继承
多态
编程中需要先有类才能有对象,然后有对象去对象
这几个看起来拥有相同的特征和行为→归为同一类→类→具有相同的属性和方法
2、创建一个类和实例化对象
通过关键字
class
进行创建的通过类名()实例化出一个对象
# 示例
class Example: # 类名大驼峰命名
def __init__(self):
self.test = "test"
example = Example()
print(example.test) # 输出test
3、调用/添加属性和方法
(1)调用属性
类内部调用
self.属性名
类外部调用
对象名.属性名
class Person:
def __init__(self, name):
self.name = name # 类内部添加属性
def test(self):
print(self.name) # 类内部调用属性
p = Person("Alice")
# 类外部调用属性
print(p.name) # 输出: Alice
p.test() # 输出: Alice
(2)添加属性
类内部添加
self.属性名 = 值
类外部添加
对象名.属性名 = 值
class DynamicPerson:
def __init__(self):
self.base_attr = "基础属性"
def add_attr(self, key, value):
self.__dict__[key] = value # 扩展:通过__dict__内部动态添加属性
self.name = "张三" # 类内部直接添加属性
dp = DynamicPerson()
dp.add_attr("age", 25)
print(dp.age) # 输出: 25
print(dp.name) # 输出:张三
# 类外部直接添加属性
dp.gender = "女"
print(dp.gender) # 输出: 女
(3)调用方法
类内部调用方法
self.方法名()
类外部调用方法
对象名.方法名()
class Calculator:
def add(self, a, b):
return a + b # 类内部定义方法
def test(self):
result = self.add(3, 5) # 类内部调用方法
print(result) # 输出: 8
clac = Calculator()
result = clac.add(3, 5) # 类外部调用方法
print(result) # 输出: 8
(4)添加方法
类内部添加
def 方法名():
类外部添加
对象名.方法名 = 方法名
import types # 导入types模块
class Calculator:
def add(self, a, b):
return a + b
def test(self): # 类内部添加方法
return "test"
def example(self):
return "example"
# 类外部直接添加方法
Calculator.example = example
def dynamic_method(self):
return "method"
calc = Calculator()
# 扩展:通过types.MethodType动态绑定方法
calc.dynamic_method = types.MethodType(dynamic_method, calc)
print(calc.dynamic_method()) # 输出 method
print(calc.test()) # 输出 test
print(calc.example()) # 输出 example
# 请注意:动态绑定只适用于当前实例
new_calc = Calculator()
print(new_calc.test()) # 输出 test
print(new_calc.example()) # 输出 example
print(new_calc.dynamic_method()) # 将报错AttributeError
4、魔法方法
__xxx__()
以两个下划线开头和结尾的系统自带的方法
常用方法
__init__
:类实例化对象的时候会自动调用
class Person:
def __init__(self, name, age):
self.name = name
self.age = age # 初始化实例属性
p = Person("张三", 25)
__str__
:重写打印自己的方法,必须return
一个字符串
class Book:
def __init__(self, title):
self.title = title
def __str__(self):
return f"《{self.title}》是一本好书" # 自定义打印输出
book = Book("Python编程")
print(book) # 输出: 《Python编程》是一本好书
__del__
:对象被删除/释放的时候会自动调用,使用del
关键字进行删除,此时如果再调用该方法,则会报错没有定义该对象
class FileHandler:
def __init__(self, filename):
self.file = open(filename, 'w')
def __del__(self):
self.file.close() # 自动关闭文件
handler = FileHandler("test.txt")
# 当handler对象被销毁时,文件自动关闭
__dict__
:用来显示对象所有属性和的对应的属性值
class Employee:
def __init__(self, name, salary):
self.name = name
self.salary = salary
emp = Employee("李四", 10000)
print(emp.__dict__) # 输出: {'name': '李四', 'salary': 10000}
__main__
:用来定义程序的主入口__name__
:Python内置的一个全局属性,用于主程序判断
def main():
print("程序主入口")
if __name__ == "__main__": # 判断是否直接运行该文件
main()
二、类的封装
1、私有化
属性私有化:变量名前加
__
(名称改写)class MyClass: def __init__(self): self.__private = 10 # 私有属性
方法私有化:同样加
__
,避免被子类覆盖def __private_method(self): ...
2、getter, setter
传统写法
class Person: def __init__(self, name): self.__name = name # 私有属性 # 方法名可以随意取,但通常为了方便理解取名为get_属性名,set_属性名 def get_name(self): # Getter return self.__name def set_name(self, name): # Setter(带验证) if len(name) < 2: raise ValueError("姓名≥2字符") self.__name = name
@property
装饰器class Rectangle: def __init__(self, w, h): self.__w = w self.__h = h @property # Getter # 通过@属性名.getter这个装饰器也可以获取属性值 # 但因为有@property本身具备Getter功能,可忽略 def area(self): return self.__w * self.__h @area.setter # Setter # 通过@属性名.setter这个装饰器给属性赋值 def area(self, val): if val <= 0: raise ValueError("面积>0") self.__w = val ** 0.5 self.__h = val ** 0.5
三、继承
1、单继承
语法
class 父类:
def method(self):
print("父类方法")
class 子类(父类): # 单继承
def method(self): # 方法覆盖
super().method() # 调用父类方法
print("子类方法")
(1)方法覆盖
子类重新实现父类方法
class User():
def __init__(self):
self.name = 'zhnagsan'
class Student(User):
def __init__(self):
self.name = 'lisi'
#此时子类覆盖了父类的方法,将输出lisi
(2)方法扩展
在父类方法基础上新增功能
class User():
def __init__(self):
self.name = 'zhnagsan'
class Student(User):
def test(self):
super().__init__() # 调用父类方法,输出zhangsan
print("Hello") # 扩展输出Hello
2、多继承
语法
class 父类A:
def method_a(self):
print("父类A方法")
class 父类B:
def method_b(self):
print("父类B方法")
class 子类(父类A, 父类B): # 多继承
pass # 不写任何代码,使用pass跳过
需要注意的点
如果两个父类的
__init__
中有同名的属性,那么子类调用的时候用的是第一个父类的属性
class User():
def __init__(self):
self.name = 'zhangsan'
class Person():
def __init__(self):
self.name = "wangwu"
class Student(Person, User):
pass
stu = Student()
print(stu.name) # 输出wangwu
在这个例子中,
Student
类继承了Person
和User
这两个父类。当创建Student
类的实例stu
时,Python 会调用Student
类的__init__
方法。由于Student
类没有定义自己的__init__
方法,Python 会按照 MRO 顺序去查找__init__
方法。这里的 MRO 顺序是[Student, Person, User, object]
,所以 Python 会先调用Person
类的__init__
方法,这样stu.name
的值就会被设为"wangwu"
,从而忽略User
类的__init__
方法。
如果两个父类的
__init__
中有有不同的属性,那么子类只能调用第一个父类的属性,调用第二个属性会报错
class User():
def __init__(self):
self.name = 'zhangsan'
class Person():
def __init__(self):
self.age = 18
class Student(User, Person):
pass
stu = Student()
print(stu.name) # 输出zhangsan
print(stu.age) # 报错AttributeError
在这个例子里,
Student
类同样继承了User
和Person
两个父类。创建Student
类的实例stu
时,Python 按照 MRO 顺序[Student, User, Person, object]
查找__init__
方法,首先会调用User
类的__init__
方法,从而stu
实例会有name
属性,其值为"zhangsan"
。但由于没有调用Person
类的__init__
方法,stu
实例就不会有age
属性,且User
类没有设置age
属性,所以当你尝试访问stu.age
时,就会抛出AttributeError
异常。解决方案:
方案1:显式调用父类的
__init__
方法class student(User, Person): def __init__(self): User.__init__(self) Person.__init__(self)
方案2:使用
super()
函数class student(User, Person): def __init__(self): super().__init__() # 通过Super()调用第一个父类→User类的__init__方法 Person.__init__(self) # 再显式调用Person类的__init__方法
这两个问题的原因都与Python的多继承机制有关,准确讲是与方法解析顺序(Method Resolution Order,MRO)和构造函数的调用方式有关,Python 采用 C3 线性化算法来确定 MRO,这意味着在查找属性时,Python 会按照特定的顺序去遍历父类。
四、课后复习
1、扩展:OpenCV人脸识别加密
人脸识别:使用级联分类器
openCV自带的分类器(可上git网站查看),可以识别人脸,识别眼睛,识别微笑,识别猫脸
步骤:
导入分类器:
CascadeClassifier()
检测
将背景图进行灰度化
进行检测,得到人脸数据faces
遍历faces,全出每一张人脸的矩形框,并将对应加密后的位置覆盖上去
import cv2
import numpy
# 加载分类器
face_cascade = cv2.CascadeClassifier("haarcascade_frontalface_default.xml")
# 读取原图
original_img = cv2.imread('images/face.png')
# 将图片进行灰度化
gray_img = cv2.cvtColor(original_img, cv2.COLOR_BGR2GRAY)
# 进行人脸检测,返回的是二维列表,记录了多张人脸的位置
faces = face_cascade.detectMultiScale(
gray_img, # 要检测的图片
scaleFactor=1.01, # 图片缩放比例
minNeighbors=18, # 控制人脸检测的误检率
minSize=(8, 8) # 人脸的最小尺寸
)
# 获取原图数据
r, c, channels = original_img.shape
# 获取key
key = numpy.random.randint(0, 256, size=(r, c, channels), dtype=numpy.uint8)
# 异或进行加密
encryption_img = cv2.bitwise_xor(original_img, key)
# 遍历列表,替换加密数据和画框
for x, y, w, h in faces:
cv2.rectangle(original_img, (x, y), (x + w, y + h), (0, 0, 255), 2)
original_img[y:y + h, x:x + w] = encryption_img[y:y + h, x:x + w]
# 显示最终图片
cv2.imshow('faces', original_img)
cv2.waitKey()
cv2.destroyAllWindows()
2、扩展:面向对象版学生管理系统
class Student:
def __init__(self, name, phone, email):
self.name = name
self.phone = phone
self.email = email
def __str__(self):
return f"姓名:{self.name}--电话:{self.phone}--邮箱:{self.email}"
class StudentManager:
def __init__(self):
self.students = []
def add_student(self, student):
self.students.append(student)
print(f"\n学员 {student.name} 信息添加成功!")
def remove_student(self, name):
self.students = [s for s in self.students if s.name != name]
print(f"\n学员 {name} 信息已删除!")
def update_student(self, name, new_info):
for s in self.students:
if s.name == name:
s.phone = new_info.get('phone', s.phone)
s.email = new_info.get('email', s.email)
print(f"\n学员 {name} 信息已更新!")
return
print(f"\n未找到学员 {name}!")
def sort_students(self):
self.students.sort(key=lambda x: x.name)
print("\n学员信息已按姓名排序!")
def search_students(self, keyword):
return [s for s in self.students if keyword.lower() in s.name.lower()]
def export_to_excel(self):
# 模拟导出功能(实际需调用Excel库)
print("\n学员信息已导出到Excel!")
for s in self.students:
print(s)
class StudentView:
def display_menu(self):
print("\n" + "*" * 40)
print("欢迎使用[学生管理系统]V1.0\n")
print("1. 新建学员信息")
print("2. 显示所有学员")
print("3. 模糊查询学员")
print("4. 删除学员")
print("5. 修改学员信息")
print("6. 按姓名排序")
print("7. 导出学员数据")
print("0. 退出系统")
print("\n" + "*" * 40)
def get_input(self):
while True:
try:
choice = int(input("\n请输入操作编号: "))
return choice
except ValueError:
print("\n输入错误,请输入数字0-7!")
def main_loop(self, manager):
while True:
self.display_menu()
choice = self.get_input()
if choice == 1:
name = input("请输入姓名: ").strip()
phone = input("请输入电话: ").strip()
email = input("请输入邮箱: ").strip()
student = Student(name, phone, email)
manager.add_student(student)
elif choice == 2:
if not manager.students:
print("当前没有学员信息")
else:
for s in manager.students:
print(s)
elif choice == 3:
keyword = input("请输入查询关键字: ").strip().lower()
results = manager.search_students(keyword)
if not results:
print(f"未找到包含 '{keyword}' 的学员")
else:
for s in results:
print(s)
elif choice == 4:
name = input("请输入要删除的学员姓名: ").strip()
manager.remove_student(name)
elif choice == 5:
name = input("请输入要修改的学员姓名: ").strip()
new_phone = input("请输入新电话(留空保留原值): ").strip()
new_email = input("请输入新邮箱(留空保留原值): ").strip()
new_info = {'phone': new_phone, 'email': new_email}
manager.update_student(name, new_info)
elif choice == 6:
manager.sort_students()
elif choice == 7:
manager.export_to_excel()
elif choice == 0:
print("\n感谢使用,再见!")
break
else:
print("\n无效操作,请重新输入!")
if __name__ == "__main__":
manager = StudentManager()
view = StudentView()
view.main_loop(manager)