Python 类与对象
目录
学习目标
- 理解面向对象编程(OOP)的基本概念
- 掌握类的定义和对象的创建
- 理解实例属性、类属性和方法的区别
- 掌握
__init__构造方法和self的含义
一、什么是面向对象编程
面向对象编程(Object-Oriented Programming,OOP)是一种编程范式,将数据和操作数据的方法组织在一起,形成"对象"。
核心概念:
- 类(Class):对象的蓝图或模板
- 对象(Object):类的实例
- 属性(Attribute):对象的数据
- 方法(Method):对象的行为
二、定义类
class Dog:
"""狗类"""
# 类属性(所有实例共享)
species = "Canis familiaris"
# 构造方法(初始化实例)
def __init__(self, name, age):
# 实例属性(每个实例独立)
self.name = name
self.age = age
# 实例方法
def bark(self):
return f"{self.name} says: 汪汪!"
def info(self):
return f"{self.name} 今年 {self.age} 岁"
# 创建对象(实例化)
dog1 = Dog("Buddy", 3)
dog2 = Dog("Luna", 2)
# 访问属性和方法
print(dog1.name) # Buddy
print(dog1.age) # 3
print(dog1.bark()) # Buddy says: 汪汪!
print(dog1.info()) # Buddy 今年 3 岁
print(dog2.name) # Luna
print(dog2.bark()) # Luna says: 汪汪!三、__init__ 构造方法
__init__ 是类的构造方法,在创建对象时自动调用,用于初始化实例属性。
class Person:
def __init__(self, name, age=18):
"""
构造方法
name: 姓名(必需)
age: 年龄(默认 18)
"""
self.name = name
self.age = age
self._id = None # 受保护属性(约定俗成)
def set_id(self, id_number):
self._id = id_number
def get_id(self):
return self._id
# 使用
p1 = Person("Alice", 25)
p2 = Person("Bob") # 使用默认年龄
print(p1.name, p1.age) # Alice 25
print(p2.name, p2.age) # Bob 18四、self 是什么
self 代表类的实例本身,它是实例方法的第一个参数(约定俗成的名称)。
class Counter:
def __init__(self):
self.count = 0
def increment(self):
self.count += 1
return self.count
def reset(self):
self.count = 0
c = Counter()
print(c.increment()) # 1
print(c.increment()) # 2
print(c.increment()) # 3
c.reset()
print(c.count) # 0等价调用方式:
# 以下两种调用等价
c.increment() # 隐式传递 self
Counter.increment(c) # 显式传递 self五、类属性 vs 实例属性
class Student:
# 类属性(所有实例共享)
school = "北京大学"
count = 0
def __init__(self, name):
# 实例属性(每个实例独立)
self.name = name
self.scores = []
Student.count += 1 # 修改类属性
def add_score(self, score):
self.scores.append(score)
# 创建学生
s1 = Student("Alice")
s2 = Student("Bob")
# 访问类属性
print(s1.school) # 北京大学
print(s2.school) # 北京大学
print(Student.school) # 北京大学
# 访问实例属性
print(s1.name) # Alice
print(s2.name) # Bob
# 类属性共享
print(Student.count) # 2(两个实例)
# 修改类属性
Student.school = "清华大学"
print(s1.school) # 清华大学
print(s2.school) # 清华大学
# 通过实例修改类属性(危险!会创建实例属性)
s1.school = "复旦大学" # 这里创建了一个实例属性,不影响类属性
print(s1.school) # 复旦大学(实例属性)
print(s2.school) # 清华大学(类属性)
print(Student.school) # 清华大学六、类方法、静态方法与实例方法
class MathUtils:
# 类属性
PI = 3.14159
def __init__(self, name):
self.name = name
# 实例方法(需要 self)
def greet(self):
return f"Hello, {self.name}"
# 类方法(需要 cls,可访问类属性)
@classmethod
def circle_area(cls, radius):
return cls.PI * radius ** 2
# 静态方法(不需要 self 或 cls,类似普通函数)
@staticmethod
def add(a, b):
return a + b
# 调用实例方法
m = MathUtils("Alice")
print(m.greet()) # Hello, Alice
# 调用类方法
print(MathUtils.circle_area(5)) # 78.53975
print(m.circle_area(5)) # 也可以
# 调用静态方法
print(MathUtils.add(3, 5)) # 8
print(m.add(3, 5)) # 也可以| 方法类型 | 装饰器 | 第一个参数 | 用途 |
|---|---|---|---|
| 实例方法 | 无 | self |
操作实例属性 |
| 类方法 | @classmethod |
cls |
操作类属性、替代构造方法 |
| 静态方法 | @staticmethod |
无 | 与类相关的工具函数 |
类方法作为替代构造方法
class Date:
def __init__(self, year, month, day):
self.year = year
self.month = month
self.day = day
@classmethod
def from_string(cls, date_str):
"""从字符串创建日期对象"""
year, month, day = map(int, date_str.split("-"))
return cls(year, month, day)
@classmethod
def today(cls):
"""创建今天的日期对象"""
import datetime
now = datetime.datetime.now()
return cls(now.year, now.month, now.day)
def __str__(self):
return f"{self.year}-{self.month:02d}-{self.day:02d}"
# 使用替代构造方法
d1 = Date(2024, 1, 15)
d2 = Date.from_string("2024-06-01")
d3 = Date.today()
print(d1) # 2024-01-15
print(d2) # 2024-06-01
print(d3) # 今天的日期七、访问控制
Python 没有严格的访问控制,使用命名约定:
class BankAccount:
def __init__(self, owner, balance):
self.owner = owner
self._balance = balance # 受保护属性(单下划线)
self.__password = "123456" # 私有属性(双下划线)
def deposit(self, amount):
if amount > 0:
self._balance += amount
return True
return False
def withdraw(self, amount):
if 0 < amount <= self._balance:
self._balance -= amount
return True
return False
def get_balance(self):
return self._balance
def _internal_method(self): # 受保护方法
pass
def __private_method(self): # 私有方法
pass
account = BankAccount("Alice", 1000)
print(account.owner) # Alice
print(account._balance) # 1000(可以访问,但不建议)
# print(account.__password) # AttributeError!
# 双下划线属性被改名(名称修饰)
print(account._BankAccount__password) # 123456(不要这样做)
account.deposit(500)
print(account.get_balance()) # 1500
account.withdraw(200)
print(account.get_balance()) # 1300八、综合示例
"""
图书管理系统
"""
class Book:
"""图书类"""
total_books = 0
def __init__(self, title, author, isbn):
self.title = title
self.author = author
self.isbn = isbn
self._is_borrowed = False
Book.total_books += 1
def borrow(self):
if not self._is_borrowed:
self._is_borrowed = True
return f"《{self.title}》借阅成功"
return f"《{self.title}》已被借出"
def return_book(self):
if self._is_borrowed:
self._is_borrowed = False
return f"《{self.title}》归还成功"
return f"《{self.title}》未被借出"
def get_status(self):
status = "已借出" if self._is_borrowed else "在馆"
return f"《{self.title}》- {status}"
def __str__(self):
return f"《{self.title}》by {self.author} (ISBN: {self.isbn})"
class Library:
"""图书馆类"""
def __init__(self, name):
self.name = name
self.books = {}
def add_book(self, book):
self.books[book.isbn] = book
return f"已添加 {book.title}"
def find_book(self, isbn):
return self.books.get(isbn)
def list_books(self):
print(f"\n=== {self.name} 藏书列表 ===")
for book in self.books.values():
print(f" {book} - {book.get_status()}")
# 使用
lib = Library("城市图书馆")
book1 = Book("Python编程", "张三", "978-1")
book2 = Book("数据结构", "李四", "978-2")
book3 = Book("算法导论", "王五", "978-3")
lib.add_book(book1)
lib.add_book(book2)
lib.add_book(book3)
lib.list_books()
print(f"\n{book1.borrow()}")
print(f"{book2.borrow()}")
lib.list_books()
print(f"\n{book1.return_book()}")
print(f"\n图书馆总藏书: {Book.total_books}")小结
- 类是对象的模板,使用
class关键字定义 __init__是构造方法,用于初始化实例属性self代表实例本身,是实例方法的第一个参数- 类属性被所有实例共享,实例属性每个实例独立
@classmethod定义类方法,@staticmethod定义静态方法- Python 使用单下划线
_表示受保护,双下划线__表示私有(名称修饰)
练习
- 定义一个
Rectangle类,包含长和宽属性,以及计算面积和周长的方法。 - 定义一个
Circle类,使用类属性存储 PI,提供计算面积和 circumference 的方法。 - 编写一个
BankAccount类,支持存款、取款、查询余额,余额不能为负数。 - 使用
@classmethod为Book类添加一个从字典创建实例的方法。 - 解释
self和cls的区别,并举例说明。