嘿,学习搭子!不知道你有没有遇到过这样的困惑:为什么Python里会有“类”这种东西?函数明明已经够用了,为什么还要搞出“对象”来?别急,我刚开始学的时候也是这么想的。
想象一下,你要开发一个图书管理系统。如果用函数式思维,你可能需要写一堆函数:add_book()、borrow_book()、return_book(),然后维护一堆全局变量来存储书籍信息。一旦功能变复杂,代码就会像一团乱麻,改一个地方可能牵动全身。
面向对象编程(OOP)就像给你一个“魔法工具箱”,让你可以把相关的数据和操作打包在一起。图书就是“对象”,它有属性(书名、作者、ISBN)和方法(借出、归还)。这样组织代码,不仅逻辑清晰,还能重复利用,就像搭积木一样简单!
预期成果:学完这一章,你不仅能看懂别人写的面向对象代码,还能自己设计一个简单的图书管理系统。更重要的是,你会开始用“对象”的视角看待编程问题,这可是Python进阶的必经之路!
2.1 类 vs 对象:什么是“蓝图”,什么是“实物”?
咱们先来打个比方。类(Class)就像汽车的设计图纸,它定义了汽车应该有什么(轮子、发动机、颜色)和能做什么(启动、加速、刹车)。而对象(Object)就是根据这张图纸制造出来的具体汽车。
在Python中,类是用来创建对象的模板。一个类定义了:
# 类定义:图书的“设计图纸”classBook:pass# 对象创建:根据图纸制造具体的书book1 = Book()book2 = Book()
# 错误:直接给类赋值属性(这是类的属性,不是对象的)Book.title = "Python入门"# 不推荐这样做# 正确:给对象赋值属性book1 = Book()book1.title = "Python入门"# ✅
解决方案: 记住类名首字母大写(约定),对象名小写。类定义的是“有什么能力”,对象才是“实际干活的”。
属性就是对象的数据,比如一本书的书名、作者、页数。方法就是对象能执行的操作,比如借出、归还。
classBook:# 方法:对象能做什么defdisplay_info(self):print(f"《{self.title}》 by {self.author}")# 使用book = Book()book.title = "流畅的Python"# 属性赋值book.author = "Luciano Ramalho"book.display_info() # 调用方法
classBook:defdisplay_info(): # 错误!缺少selfprint("显示信息")book = Book()book.display_info() # 报错:display_info() takes 0 positional arguments but 1 was given
解决方案: 所有实例方法的第一个参数必须是self(名字可以改,但大家都用self)。Python会自动把对象本身作为第一个参数传进去。
每次创建新对象时,__init__方法会自动调用。就像婴儿出生时要登记姓名、性别一样,我们可以在这里给对象设置初始属性。
classBook:def__init__(self, title, author, pages):self.title = title # 书名属性self.author = author # 作者属性self.pages = pages # 页数属性self.is_borrowed = False# 是否借出(默认False)# 创建对象时直接传入属性值book1 = Book("Python编程", "Eric Matthes", 560)book2 = Book("算法图解", "Aditya Bhargava", 240)print(book1.title) # 输出:Python编程print(book2.pages) # 输出:240
classBook:def__init__(self, title):self.title = titledefanother_method(self):self.author = "某作者"# 不推荐:属性分散在不同地方# 正确做法:在__init__中集中初始化所有重要属性classBook:def__init__(self, title, author=None):self.title = titleself.author = author # 可以设置默认值
解决方案: 尽量在__init__中初始化所有必要的属性。如果某个属性可能为None,可以设置默认值。
self代表对象自己。在方法内部,通过self.属性名来访问对象的属性,通过self.方法名()来调用对象的其他方法。
classBook:def__init__(self, title, author):self.title = titleself.author = authorself.read_count = 0defread(self):self.read_count += 1# 修改自己的属性print(f"《{self.title}》被阅读了{self.read_count}次")defis_popular(self):# 调用自己的属性做判断returnself.read_count > 100book = Book("Python学习笔记", "小明")book.read() # 输出:《Python学习笔记》被阅读了1次book.read() # 输出:《Python学习笔记》被阅读了2次print(book.is_popular()) # 输出:False
classBook:def__init__(mybook, title): # 可以!self只是约定名称 mybook.title = titlebook = Book("测试")print(book.title) # 输出:测试
解决方案: 虽然可以用其他名字,但强烈建议始终使用self。这是Python社区的约定,别人一看就懂。
当写下book = Book("标题", "作者")时,Python背后做了这些事:
- 2. 调用__new__:创建对象实例(通常不用我们管)
- 3. 调用__init__:传入参数,初始化对象属性
# 这个过程是自动的book = Book("Python入门", "张三")# 相当于手动步骤(简化理解)# 1. 创建空对象:book = object.__new__(Book)# 2. 初始化:Book.__init__(book, "Python入门", "张三")# 3. 得到:book.title = "Python入门", book.author = "张三"
classBook: language = "Python"# 类属性:所有书共享def__init__(self, title):self.title = title # 实例属性:每本书不同book1 = Book("书1")book2 = Book("书2")print(Book.language) # 输出:Python(通过类访问)print(book1.language) # 输出:Python(通过对象访问,但其实是类的)print(book2.language) # 输出:Python# 修改类属性会影响所有对象Book.language = "Java"print(book1.language) # 输出:Javaprint(book2.language) # 输出:Java# 修改对象的language属性会创建实例属性book1.language = "C++"print(book1.language) # 输出:C++(实例属性)print(book2.language) # 输出:Java(仍指向类属性)
解决方案: 类属性用于所有对象共享的数据(比如默认值),实例属性用于每个对象特有的数据。初始化实例属性用__init__,定义类属性直接在类内部。
现在,咱们用刚学的知识来构建一个简单的图书管理系统。别担心,我会一步步带你实现!
classBook:"""图书类:表示图书馆中的一本书"""def__init__(self, book_id, title, author, year):""" 初始化一本书 参数: book_id: 图书编号(唯一标识) title: 书名 author: 作者 year: 出版年份 """self.book_id = book_idself.title = titleself.author = authorself.year = yearself.is_borrowed = False# 是否被借出self.borrower = None# 借阅者(如果被借出)defborrow(self, borrower_name):"""借出这本书"""ifself.is_borrowed:print(f"❌ 《{self.title}》已被{self.borrower}借走")returnFalseelse:self.is_borrowed = Trueself.borrower = borrower_nameprint(f"✅ 《{self.title}》成功借给{borrower_name}")returnTruedefreturn_book(self):"""归还这本书"""ifnotself.is_borrowed:print(f"❌ 《{self.title}》未被借出,无需归还")returnFalseelse:print(f"✅ 《{self.title}》已从{self.borrower}处归还")self.is_borrowed = Falseself.borrower = NonereturnTruedefdisplay_info(self):"""显示图书信息""" status = "已借出"ifself.is_borrowed else"可借阅" borrower_info = f",借阅者:{self.borrower}"ifself.is_borrowed else""print(f"编号:{self.book_id} | 《{self.title}》 | 作者:{self.author} | "f"年份:{self.year} | 状态:{status}{borrower_info}")# 测试一下if __name__ == "__main__":# 创建几本书 book1 = Book("B001", "Python编程:从入门到实践", "Eric Matthes", 2016) book2 = Book("B002", "流畅的Python", "Luciano Ramalho", 2017) book3 = Book("B003", "Python核心编程", "Wesley Chun", 2016)# 显示信息print("=== 图书信息 ===") book1.display_info() book2.display_info() book3.display_info()# 借书测试print("\n=== 借书测试 ===") book1.borrow("张三") book1.borrow("李四") # 应该失败,因为已被借出 book1.display_info()# 还书测试print("\n=== 还书测试 ===") book1.return_book() book1.display_info()
classLibrary:"""图书馆类:管理多本图书"""def__init__(self, name):self.name = nameself.books = {} # 用字典存储,key是book_id,value是Book对象self.borrow_records = [] # 借阅记录defadd_book(self, book):"""添加一本书到图书馆"""if book.book_id inself.books:print(f"❌ 编号{book.book_id}的图书已存在")returnFalseself.books[book.book_id] = bookprint(f"✅ 成功添加《{book.title}》")returnTruedefremove_book(self, book_id):"""从图书馆移除一本书"""if book_id notinself.books:print(f"❌ 编号{book_id}的图书不存在")returnFalse book = self.books[book_id]if book.is_borrowed:print(f"❌ 《{book.title}》已被借出,不能移除")returnFalsedelself.books[book_id]print(f"✅ 成功移除《{book.title}》")returnTruedefborrow_book(self, book_id, borrower_name):"""借阅图书"""if book_id notinself.books:print(f"❌ 编号{book_id}的图书不存在")returnFalse book = self.books[book_id] success = book.borrow(borrower_name)if success:# 记录借阅信息 record = {"book_id": book_id,"book_title": book.title,"borrower": borrower_name,"borrow_time": "2024-12-11 10:00"# 简化,实际应用用datetime }self.borrow_records.append(record)return successdefreturn_book(self, book_id):"""归还图书"""if book_id notinself.books:print(f"❌ 编号{book_id}的图书不存在")returnFalsereturnself.books[book_id].return_book()defsearch_by_title(self, keyword):"""按书名关键词搜索""" results = []for book inself.books.values():if keyword.lower() in book.title.lower(): results.append(book)return resultsdefsearch_by_author(self, author):"""按作者搜索""" results = []for book inself.books.values():if author.lower() in book.author.lower(): results.append(book)return resultsdefdisplay_all_books(self):"""显示所有图书"""print(f"\n=== {self.name} 图书清单 ===")ifnotself.books:print("图书馆空空如也,快去添加些书吧!")returnfor book_id, book inself.books.items(): book.display_info()defdisplay_borrowed_books(self):"""显示所有已借出的图书""" borrowed = [book for book inself.books.values() if book.is_borrowed]print(f"\n=== 已借出图书(共{len(borrowed)}本) ===")for book in borrowed: book.display_info()# 完整系统测试if __name__ == "__main__":# 创建图书馆 my_library = Library("Python学习图书馆")# 添加图书 books_data = [ ("B001", "Python编程:从入门到实践", "Eric Matthes", 2016), ("B002", "流畅的Python", "Luciano Ramalho", 2017), ("B003", "Python核心编程", "Wesley Chun", 2016), ("B004", "Python数据分析", "Wes McKinney", 2017), ("B005", "Python机器学习", "Sebastian Raschka", 2019) ]for data in books_data: book = Book(*data) my_library.add_book(book)# 显示所有图书 my_library.display_all_books()# 借阅测试print("\n=== 模拟借阅过程 ===") my_library.borrow_book("B001", "张三") my_library.borrow_book("B002", "李四") my_library.borrow_book("B001", "王五") # 应该失败# 显示借出情况 my_library.display_borrowed_books()# 搜索测试print("\n=== 搜索测试 ===") python_books = my_library.search_by_title("Python")print(f"找到{len(python_books)}本Python相关书籍:")for book in python_books: book.display_info()# 归还测试print("\n=== 归还测试 ===") my_library.return_book("B001") my_library.display_borrowed_books()
我已经将完整代码保存为单独的文件,方便你运行和修改:
- •
library.py - 图书馆管理类的完整实现 - •
main.py - 演示如何使用这个图书管理系统
1. 在Python中,类和对象的关系是:A) 类是对象的实例B) 对象是类的实例 ✅C) 类和对象是同一个东西D) 对象是类的父类
2. 下面哪个是类的正确定义?A) class myClass:B) Class MyClass:C) class MyClass: ✅D) def MyClass():
3. __init__ 方法的作用是:A) 销毁对象B) 初始化对象属性 ✅C) 打印对象信息D) 比较两个对象
4. 实例方法的第一个参数通常命名为:A) thisB) self ✅C) meD) obj
5. 创建对象的过程叫做:A) 定义B) 调用C) 实例化 ✅D) 继承
classStudent: school = "第一中学"# Adef__init__(self, name):self.name = name # B ✅self.age = None# C ✅defstudy(self):self.hours = 2# D ✅
A) school(类属性)B) name(实例属性)✅C) age(实例属性)✅D) hours(实例属性,但只在study调用后才有)✅
7. 关于self,正确的是:A) self是Python关键字B) self必须作为第一个参数,且必须叫selfC) self代表类本身D) self代表对象实例本身 ✅
classDog:def__init__(self, name):self.name = namedefbark(self):print(f"{self.name}在叫!")dog1 = Dog("旺财")dog2 = Dog("来福")dog1.bark()
A) 旺财在叫! ✅B) 来福在叫!C) self在叫!D) 报错
9. 类属性和实例属性的区别是:A) 类属性属于类,所有对象共享;实例属性属于对象,每个对象独立 ✅B) 类属性必须大写,实例属性必须小写C) 类属性在方法外定义,实例属性在方法内定义D) 没有区别
10. 为什么要使用面向对象编程?A) 让代码更复杂B) 组织相关数据和操作,提高代码复用性和可维护性 ✅C) 因为大家都在用D) 为了显得专业
- • 在Book类中添加
category属性(如:"编程"、"文学"、"科学") - • 在Library类中添加
get_books_by_category(category)方法
- • 修改borrow方法,记录借出时间(可以使用
datetime.now())
- • 创建User类,包含属性:
user_id、name、borrowed_books
- • 实现
save_to_file()和load_from_file()方法
- • 创建
Student类、Course类和RegistrationSystem类
- • 创建
EBook类继承自Book类,增加file_size和format属性 - • 创建
AudioBook类继承自Book类,增加duration和narrator属性 - • 重写
display_info方法,让不同类型图书显示不同信息
咱们今天一起学习了面向对象编程的核心概念,让我用一张表帮你总结一下:
记住这些要点,你就能看懂大多数面向对象代码了。最重要的是,你现在有了“对象思维”——知道把相关的数据和操作打包在一起,让代码更有组织性。
嘿,搭子!如果你觉得今天的类与对象已经很有趣,那下周的内容会更精彩!咱们将深入面向对象编程的三大核心特性:
1. 继承: 就像“子承父业”,让新类可以继承已有类的属性和方法。比如EBook继承Book,自动拥有所有图书功能,还能添加自己的特色。
2. 多态: 同一个方法在不同类中有不同实现。比如display_info()方法,在Book、EBook、AudioBook中显示不同的信息格式。
3. 封装: 把数据和操作细节隐藏起来,只暴露必要的接口。就像汽车你只需要知道油门、刹车,不需要懂发动机原理。
准备好迎接挑战了吗?下周的实战项目是:重构图书管理系统,用继承和多态让系统更灵活、更强大!
我知道,面向对象的概念一开始可能有点抽象。别慌,我当初学的时候也花了好几天才转过弯来。重要的是多写代码、多思考:
- 1. 先模仿再创造:把示例代码运行一遍,然后试着修改属性、添加方法
- 2. 从生活中找例子:想想周围哪些事物可以抽象成类(比如手机、学生、课程)
- 3. 别怕犯错:把常见的错误都犯一遍,你就知道怎么避免了
如果你在练习中遇到问题,或者有新奇的想法,随时可以回来看看这篇文章。记住,编程不是死记硬背,而是解决问题的思维方式。