Python 类和对象详解
一、类和对象的基本概念
类(Class):是创建对象的蓝图或模板,它定义了一组属性和方法,这些属性和方法描述了对象的行为和状态。
对象(Object):是类的实例,具有类所定义的属性和方法。
python
# 定义类
class Dog:
pass
# 创建对象
my_dog = Dog()
二、构造方法 __init__
__init__ 是一个特殊的方法,用于初始化新创建的对象。在创建对象时自动调用。
python
class Person:
def __init__(self, name, age):
self.name = name # 实例属性
self.age = age # 实例属性
def greet(self):
return f"Hello, my name is {self.name} and I'm {self.age} years old."
# 创建对象时传入参数
person1 = Person("Alice", 30)
person2 = Person("Bob", 25)
print(person1.greet()) # Hello, my name is Alice and I'm 30 years old.
print(person2.greet()) # Hello, my name is Bob and I'm 25 years old.
三、实例属性和类属性
1. 实例属性
- 属于特定实例的属性
- 通过
self在__init__中定义 - 每个实例都有自己独立的副本
python
class Car:
def __init__(self, brand, model):
self.brand = brand # 实例属性
self.model = model # 实例属性
car1 = Car("Toyota", "Camry")
car2 = Car("Honda", "Civic")
print(car1.brand, car1.model) # Toyota Camry
print(car2.brand, car2.model) # Honda Civic
2. 类属性
- 属于类的属性,所有实例共享
- 在类内部但在方法外部定义
python
class Student:
# 类属性
school = "清华大学"
student_count = 0
def __init__(self, name):
self.name = name # 实例属性
Student.student_count += 1 # 修改类属性
@classmethod
def get_school_info(cls):
return f"学校:{cls.school},学生总数:{cls.student_count}"
# 访问类属性
print(Student.school) # 清华大学
# 创建对象
s1 = Student("张三")
s2 = Student("李四")
print(s1.school) # 清华大学
print(s2.school) # 清华大学
# 修改类属性会影响所有实例
Student.school = "北京大学"
print(s1.school) # 北京大学
print(s2.school) # 北京大学
print(Student.get_school_info()) # 学校:北京大学,学生总数:2
四、实例方法、类方法和静态方法
1. 实例方法
- 第一个参数是
self,表示实例本身 - 只能通过实例调用
- 可以访问和修改实例属性和类属性
python
class Calculator:
def __init__(self, value=0):
self.value = value
def add(self, x):
self.value += x
return self.value
def multiply(self, x):
self.value *= x
return self.value
calc = Calculator(10)
print(calc.add(5)) # 15
print(calc.multiply(2)) # 30
2. 类方法
- 使用
@classmethod装饰器 - 第一个参数是
cls,表示类本身 - 可以通过类或实例调用
- 主要用于操作类属性
python
class Date:
def __init__(self, year, month, day):
self.year = year
self.month = month
self.day = day
@classmethod
def from_string(cls, date_string):
"""从字符串创建对象"""
year, month, day = map(int, date_string.split('-'))
return cls(year, month, day) # 相当于调用 Date(year, month, day)
@classmethod
def get_current_year(cls):
import datetime
return datetime.datetime.now().year
# 使用类方法创建对象
date1 = Date.from_string("2023-12-25")
print(f"{date1.year}-{date1.month}-{date1.day}") # 2023-12-25
# 调用类方法
print(Date.get_current_year()) # 当前年份
3. 静态方法
- 使用
@staticmethod装饰器 - 没有特殊参数(没有
self或cls) - 可以通过类或实例调用
- 与类和实例无关的工具函数
python
class MathUtils:
@staticmethod
def add(a, b):
return a + b
@staticmethod
def factorial(n):
if n <= 1:
return 1
return n * MathUtils.factorial(n-1)
# 使用静态方法
print(MathUtils.add(5, 3)) # 8
print(MathUtils.factorial(5)) # 120
# 也可以通过实例调用
utils = MathUtils()
print(utils.add(10, 20)) # 30
五、访问控制
Python 没有严格的访问控制,但通过命名约定来实现:
- 公有成员 :正常命名,如
name - 保护成员 :以一个下划线开头,如
_protected_var(提示性的,外部仍可访问) - 私有成员 :以两个下划线开头,如
__private_var(会进行名称修饰,实际上变为_类名__private_var)
python
class BankAccount:
def __init__(self, account_holder, initial_balance):
self.account_holder = account_holder # 公有
self._account_number = "12345678" # 保护(约定)
self.__balance = initial_balance # 私有(实际会被重命名)
def deposit(self, amount):
if amount > 0:
self.__balance += amount
return True
return False
def get_balance(self):
return self.__balance # 通过公共方法访问私有属性
account = BankAccount("Alice", 1000)
print(account.account_holder) # Alice
print(account._account_number) # 12345678(可以访问,但不建议)
# print(account.__balance) # ❌ 报错:AttributeError
print(account._BankAccount__balance) # 1000(可以通过修饰后的名字访问,但不推荐)
print(account.get_balance()) # 1000(推荐的方式)
六、属性装饰器 @property
@property 装饰器可以将方法变成属性,从而可以定义 getter、setter 和 deleter。
python
class Circle:
def __init__(self, radius):
self._radius = radius # 使用 _ 表示保护属性
@property
def radius(self):
"""radius属性的getter方法"""
return self._radius
@radius.setter
def radius(self, value):
"""radius属性的setter方法"""
if value < 0:
raise ValueError("半径不能为负")
self._radius = value
@radius.deleter
def radius(self):
"""radius属性的deleter方法"""
print("删除半径")
del self._radius
@property
def area(self):
"""只读属性,计算面积"""
return 3.14159 * self._radius ** 2
@property
def diameter(self):
"""只读属性,计算直径"""
return 2 * self._radius
circle = Circle(5)
# 使用属性
print(circle.radius) # 5
print(circle.area) # 78.53975
print(circle.diameter) # 10
# 修改属性
circle.radius = 10
print(circle.area) # 314.159
# 尝试设置负值
try:
circle.radius = -5
except ValueError as e:
print(f"错误:{e}") # 错误:半径不能为负
# 删除属性
del circle.radius # 输出:删除半径
# circle.area = 100 # ❌ 报错:只读属性
七、继承
继承允许一个类(子类)继承另一个类(父类)的属性和方法。
python
# 父类(基类)
class Animal:
def __init__(self, name, age):
self.name = name
self.age = age
def speak(self):
return "动物叫声"
def info(self):
return f"{self.name},{self.age}岁"
# 子类(派生类)
class Dog(Animal):
def __init__(self, name, age, breed):
super().__init__(name, age) # 调用父类的构造方法
self.breed = breed
def speak(self): # 方法重写
return "汪汪!"
def info(self):
# 扩展父类的方法
base_info = super().info()
return f"{base_info},品种:{self.breed}"
class Cat(Animal):
def __init__(self, name, age, color):
super().__init__(name, age)
self.color = color
def speak(self):
return "喵喵!"
def info(self):
return f"{super().info()},颜色:{self.color}"
# 使用继承
dog = Dog("旺财", 3, "金毛")
cat = Cat("咪咪", 2, "白色")
print(dog.speak()) # 汪汪!
print(cat.speak()) # 喵喵!
print(dog.info()) # 旺财,3岁,品种:金毛
print(cat.info()) # 咪咪,2岁,颜色:白色
# 检查继承关系
print(isinstance(dog, Animal)) # True
print(isinstance(dog, Dog)) # True
print(isinstance(dog, Cat)) # False
print(issubclass(Dog, Animal)) # True
print(issubclass(Cat, Animal)) # True
八、多继承
Python 支持多继承,即一个类可以继承多个父类。
python
class Flyable:
def fly(self):
return "我能飞"
def take_off(self):
return "起飞"
class Swimmable:
def swim(self):
return "我能游泳"
def dive(self):
return "潜水"
class Animal:
def eat(self):
return "吃东西"
# 多继承
class Duck(Animal, Flyable, Swimmable):
def __init__(self, name):
self.name = name
def quack(self):
return "嘎嘎叫"
duck = Duck("唐老鸭")
# 调用来自不同父类的方法
print(duck.eat()) # 吃东西
print(duck.fly()) # 我能飞
print(duck.swim()) # 我能游泳
print(duck.quack()) # 嘎嘎叫
print(duck.take_off()) # 起飞
print(duck.dive()) # 潜水
# 查看方法解析顺序(MRO)
print(Duck.__mro__)
# (<class '__main__.Duck'>, <class '__main__.Animal'>,
# <class '__main__.Flyable'>, <class '__main__.Swimmable'>, <class 'object'>)
九、特殊方法(魔术方法)
特殊方法以双下划线开头和结尾,用于实现类的特殊行为。
常见魔术方法:
python
class Vector:
def __init__(self, x, y):
self.x = x
self.y = y
# 字符串表示
def __str__(self):
return f"Vector({self.x}, {self.y})"
def __repr__(self):
return f"Vector({self.x}, {self.y})"
# 算术运算
def __add__(self, other):
return Vector(self.x + other.x, self.y + other.y)
def __sub__(self, other):
return Vector(self.x - other.x, self.y - other.y)
def __mul__(self, scalar):
return Vector(self.x * scalar, self.y * scalar)
# 比较运算
def __eq__(self, other):
return self.x == other.x and self.y == other.y
def __lt__(self, other):
return (self.x**2 + self.y**2) < (other.x**2 + other.y**2)
# 容器行为
def __len__(self):
return 2
def __getitem__(self, index):
if index == 0:
return self.x
elif index == 1:
return self.y
else:
raise IndexError("索引超出范围")
# 调用对象
def __call__(self, scale=1):
return Vector(self.x * scale, self.y * scale)
# 使用魔术方法
v1 = Vector(2, 3)
v2 = Vector(4, 5)
print(v1) # Vector(2, 3)
print(v1 + v2) # Vector(6, 8)
print(v1 * 3) # Vector(6, 9)
print(v1 == Vector(2, 3)) # True
print(v1 < v2) # True
print(len(v1)) # 2
print(v1[0], v1[1]) # 2 3
# 调用对象
v3 = v1(2) # 相当于 v1.__call__(2)
print(v3) # Vector(4, 6)
更多魔术方法:
python
class Book:
def __init__(self, title, author, pages):
self.title = title
self.author = author
self.pages = pages
# 上下文管理器
def __enter__(self):
print(f"开始阅读《{self.title}》")
return self
def __exit__(self, exc_type, exc_val, exc_tb):
print(f"结束阅读《{self.title}》")
# 描述符
def __get__(self, instance, owner):
print(f"获取书籍信息:{self.title}")
return self
# 迭代器
def __iter__(self):
self._current_page = 1
return self
def __next__(self):
if self._current_page > self.pages:
raise StopIteration
page = self._current_page
self._current_page += 1
return f"第{page}页"
# 类型转换
def __int__(self):
return self.pages
def __float__(self):
return float(self.pages)
def __bool__(self):
return self.pages > 0
# 使用上下文管理器
with Book("Python编程", "John", 300) as book:
print(f"正在阅读 {book.title}")
# 迭代书籍
for page in Book("小说", "作者", 3):
print(page)
# 输出:
# 第1页
# 第2页
# 第3页
十、抽象基类
抽象基类(Abstract Base Class, ABC)用于定义接口,不能被实例化,子类必须实现抽象方法。
python
from abc import ABC, abstractmethod
class Shape(ABC):
@abstractmethod
def area(self):
"""计算面积"""
pass
@abstractmethod
def perimeter(self):
"""计算周长"""
pass
def info(self):
"""非抽象方法"""
return f"这是一个形状,面积:{self.area()},周长:{self.perimeter()}"
class Rectangle(Shape):
def __init__(self, width, height):
self.width = width
self.height = height
def area(self):
return self.width * self.height
def perimeter(self):
return 2 * (self.width + self.height)
class Circle(Shape):
def __init__(self, radius):
self.radius = radius
def area(self):
return 3.14159 * self.radius ** 2
def perimeter(self):
return 2 * 3.14159 * self.radius
# 不能实例化抽象类
# shape = Shape() # ❌ 报错:TypeError
# 实例化具体子类
rect = Rectangle(5, 3)
circle = Circle(4)
print(rect.area()) # 15
print(rect.perimeter()) # 16
print(rect.info()) # 这是一个形状,面积:15,周长:16
print(circle.area()) # 50.26544
print(circle.perimeter()) # 25.13272
十一、数据类(Python 3.7+)
数据类用于存储数据,自动生成特殊方法(如 __init__、__repr__)。
python
from dataclasses import dataclass, field
from typing import List
@dataclass
class Point:
x: float
y: float
color: str = "black" # 默认值
def distance_from_origin(self):
return (self.x ** 2 + self.y ** 2) ** 0.5
@dataclass(order=True) # 自动生成比较方法
class Person:
name: str
age: int
hobbies: List[str] = field(default_factory=list) # 默认工厂函数
# 自动生成 __init__、__repr__、__eq__ 等方法
p1 = Point(3, 4)
p2 = Point(3, 4, "red")
print(p1) # Point(x=3, y=4, color='black')
print(p2) # Point(x=3, y=4, color='red')
print(p1 == Point(3, 4)) # True
# 排序
person1 = Person("Alice", 30, ["reading", "swimming"])
person2 = Person("Bob", 25)
person3 = Person("Charlie", 25, ["gaming"])
people = [person1, person2, person3]
print(sorted(people)) # 按 name 和 age 排序
十二、枚举类
枚举用于定义一组命名的常量。
python
from enum import Enum, auto
class Color(Enum):
RED = 1
GREEN = 2
BLUE = 3
# 自动赋值
class Status(Enum):
PENDING = auto() # 1
RUNNING = auto() # 2
COMPLETED = auto() # 3
FAILED = auto() # 4
# 使用枚举
print(Color.RED) # Color.RED
print(Color.RED.value) # 1
print(Color.RED.name) # RED
# 遍历枚举
for color in Color:
print(color.name, color.value)
# 通过值获取枚举
print(Color(1)) # Color.RED
print(Color['RED']) # Color.RED
# 枚举比较
print(Color.RED is Color.RED) # True
print(Color.RED == Color.RED) # True
print(Color.RED == Color.GREEN) # False
十三、__slots__ 优化内存
使用 __slots__ 可以限制类的属性,减少内存占用。
python
class RegularClass:
def __init__(self, x, y):
self.x = x
self.y = y
class SlotsClass:
__slots__ = ['x', 'y'] # 只能有这些属性
def __init__(self, x, y):
self.x = x
self.y = y
# 创建对象
regular = RegularClass(1, 2)
slotted = SlotsClass(1, 2)
# 添加新属性
regular.z = 3 # 可以
# slotted.z = 3 # ❌ 报错:AttributeError
import sys
print(f"RegularClass 内存占用: {sys.getsizeof(regular)} 字节")
print(f"SlotsClass 内存占用: {sys.getsizeof(slotted)} 字节")
十四、单例模式
python
class Singleton:
_instance = None
def __new__(cls, *args, **kwargs):
if not cls._instance:
cls._instance = super().__new__(cls)
return cls._instance
def __init__(self, name):
if not hasattr(self, 'initialized'): # 防止重复初始化
self.name = name
self.initialized = True
s1 = Singleton("实例1")
s2 = Singleton("实例2")
print(s1 is s2) # True
print(s1.name) # 实例1
print(s2.name) # 实例1(实际上指向同一个对象)
十五、类装饰器
类也可以作为装饰器:
python
class DecoratorClass:
def __init__(self, func):
self.func = func
def __call__(self, *args, **kwargs):
print("装饰器逻辑 - 前")
result = self.func(*args, **kwargs)
print("装饰器逻辑 - 后")
return result
@DecoratorClass
def my_function():
print("原始函数")
my_function()
十六、最佳实践
-
遵循命名约定
- 类名使用驼峰式:
MyClass - 方法和变量使用蛇形命名:
my_method - 私有成员使用下划线前缀:
_private
- 类名使用驼峰式:
-
单一职责原则
- 一个类只做一件事
-
优先使用组合而不是继承
python# 使用组合 class Engine: def start(self): return "引擎启动" class Car: def __init__(self): self.engine = Engine() # 组合 def start(self): return self.engine.start() -
使用类型注解
pythonclass Point: def __init__(self, x: float, y: float) -> None: self.x = x self.y = y def move(self, dx: float, dy: float) -> 'Point': return Point(self.x + dx, self.y + dy)
总结
Python 的类和对象提供了强大的面向对象编程能力。通过类,我们可以封装数据和行为,通过继承和多态实现代码复用,通过特殊方法自定义类的行为。掌握这些概念对于编写可维护、可扩展的 Python 代码至关重要。