Skip to content

类的定义与实例创建

在Python中,类(Class)就像是一个蓝图或者模板,用来描述具有相同属性和方法的对象集合。而对象(Object)则是根据这个蓝图创建出来的具体实例。打个比方,如果说“汽车”是一个类,那么你的那辆红色小轿车就是一个对象。

定义一个类非常简单,使用class关键字即可。让我们来看一个最基础的例子:

python
# 定义一个简单的类
class Car:
    pass  # pass表示什么都不做,是Python中的空语句

# 创建Car类的实例(对象)
my_car = Car()

# 检查my_car是否是Car类的实例
print(isinstance(my_car, Car))  # 输出: True

这里我们定义了一个名为Car的空类,然后通过Car()创建了一个实例my_carisinstance()函数用来检查一个对象是否是某个类的实例。

实例方法表格

功能名称实例调用方法具体功能、注意事项、必需参数/可选参数
创建类实例ClassName()调用类名加括号创建新实例,可以传入初始化参数
检查实例类型isinstance(obj, ClassName)检查对象obj是否为ClassName类的实例,返回布尔值

使用示例

下面是一个更实用的例子,展示如何定义一个有实际用途的类:

python
# 定义一个Person类
class Person:
    """这是一个表示人的简单类"""
    
    def __init__(self, name, age):
        # 初始化方法,创建实例时自动调用
        self.name = name  # 实例属性:姓名
        try:
            self.age = int(age)
        except ValueError:
            print("错误:年龄必须是可以转换为整数的值")
            self.age = 0
        except TypeError:
            print("错误:传入的参数类型不正确")
            self.age = 0
    
    def introduce(self):
        # 实例方法:自我介绍
        return f"大家好,我叫{self.name},今年{self.age}岁。"

# 创建Person类的实例
try:
    person1 = Person("小明", 25)
    person2 = Person("小红", "23")
    
    # 调用实例方法
    print(person1.introduce())  # 输出: 大家好,我叫小明,今年25岁。
    print(person2.introduce())  # 输出: 大家好,我叫小红,今年23岁。
    
except Exception as e:
    # 捕获可能的异常
    print(f"创建实例时发生错误: {e}")

注意事项

  • 类名通常使用大驼峰命名法(PascalCase),如MyClassPersonInfo
  • 创建类实例时,如果类定义了__init__方法且有必需参数,必须提供相应的参数
  • 每个实例都有自己独立的属性空间,修改一个实例的属性不会影响其他实例
  • 类定义后,可以通过help(ClassName)查看类的帮助文档

这节讲了如何定义类和创建实例,这是面向对象编程的基础。通过类我们可以将数据和操作数据的方法组织在一起,让代码更加结构化和易于维护。

实例属性与方法

在上一节中,我们已经看到了如何创建类和实例,现在让我们深入了解实例属性和方法。实例属性是属于特定对象的变量,而实例方法是可以对这些属性进行操作的函数。

每个实例都有自己独立的属性副本,这意味着如果你有两个Person对象,它们的nameage属性是完全独立的。实例方法则可以访问和修改这些属性。

python
# 定义一个BankAccount类来演示实例属性和方法
class BankAccount:
    """银行账户类"""
    
    def __init__(self, owner, balance):
        self.owner = owner
        try:
            self.balance = float(balance)
            if self.balance < 0:
                raise ValueError("初始余额不能为负数")
        except ValueError as e:
            print(f"余额设置错误:{e}")
            self.balance = 0.0
        except TypeError:
            print("错误:余额类型不正确")
            self.balance = 0.0

    def deposit(self, amount):
        """存款方法"""
        try:
            amount = float(amount)
            if amount <= 0:
                raise ValueError("存款金额必须大于零")
            self.balance += amount
        except ValueError as e:
            print(f"存款失败:{e}")
        except TypeError:
            print("存款失败:金额类型错误")

    def withdraw(self, amount):
        """取款方法"""
        try:
            amount = float(amount)
            if amount <= 0:
                raise ValueError("取款金额必须大于零")
            if amount > self.balance:
                raise ValueError("余额不足")
            self.balance -= amount
        except ValueError as e:
            print(f"取款失败:{e}")
        except TypeError:
            print("取款失败:金额类型错误")

    def show(self):
        """显示账户信息"""
        print(f"账户 {self.owner} 的余额为 {self.balance}")

# 创建银行账户实例
try:
    # 创建两个不同的账户实例
    account1 = BankAccount("张三", 1000)
    account2 = BankAccount("李四", 500)
    
    # 操作第一个账户
    account1.deposit(200)   # 存款200元
    account1.withdraw(150)  # 取款150元
    account1.show()         # 显示余额
    
    # 操作第二个账户
    account2.deposit(300)   # 存款300元
    account2.withdraw(1000) # 尝试取款1000元(会失败)
    account2.show()         # 显示余额
    
except Exception as e:
    print(f"操作账户时发生错误: {e}")

实例方法表格

功能名称实例调用方法具体功能、注意事项、必需参数/可选参数
访问实例属性instance.attribute直接通过点号访问实例属性,如果属性不存在会抛出AttributeError
修改实例属性instance.attribute = value可以直接给实例属性赋新值,甚至可以动态添加新属性
调用实例方法instance.method()调用实例方法,方法内部通过self参数访问实例属性
查看所有属性instance.__dict__返回包含所有实例属性的字典

使用示例

python
# 演示实例属性的动态特性
class Student:
    def __init__(self, name):
        self.name = name

# 创建学生实例
student = Student("王五")

# 查看初始属性
print("初始属性:", student.__dict__)  # 输出: {'name': '王五'}

# 动态添加新属性
student.grade = "三年级"
student.score = 95

# 查看添加后的属性
print("添加属性后:", student.__dict__)  # 输出: {'name': '王五', 'grade': '三年级', 'score': 95}

# 删除属性
del student.score
print("删除score后:", student.__dict__)  # 输出: {'name': '王五', 'grade': '三年级'}

注意事项

  • 实例方法的第一个参数必须是self,它代表当前实例对象
  • 在实例方法内部,通过self.attribute访问实例属性
  • 可以在运行时动态为实例添加新属性,但这通常不推荐,最好在__init__中定义所有预期的属性
  • 使用hasattr(instance, 'attribute_name')可以检查实例是否具有某个属性
  • 实例属性只属于该实例,不会影响类或其他实例

这节详细介绍了实例属性和方法的概念和使用方式。实例属性存储对象的状态,实例方法定义对象的行为,两者结合让对象能够封装数据和操作。

构造方法 __init__

构造方法__init__是Python类中一个特殊的方法,当我们创建类的实例时,Python会自动调用这个方法来初始化新创建的对象。你可以把它想象成对象的"出生证明",在这里设置对象的初始状态。

__init__方法的名字很特别,前后都有双下划线,这种命名方式在Python中被称为"魔术方法"或"特殊方法"。虽然你可以不定义__init__方法,但大多数情况下你都会需要它来设置对象的初始属性。

python
# 定义一个Rectangle类来演示__init__的使用
class Rectangle:
    """矩形类"""
    
    def __init__(self, width=1, height=1):
        """
        构造方法,初始化矩形的宽度和高度
        参数:
        - width: 矩形宽度,默认为1
        - height: 矩形高度,默认为1
        """
        # 验证输入参数的有效性
        if width <= 0 or height <= 0:
            raise ValueError("宽度和高度必须大于0")
        
        self.width = width
        self.height = height
    
    def area(self):
        """计算矩形面积"""
        return self.width * self.height
    
    def perimeter(self):
        """计算矩形周长"""
        return 2 * (self.width + self.height)

# 使用Rectangle类
try:
    # 创建矩形实例,使用默认参数
    rect1 = Rectangle()
    print(f"默认矩形 - 面积: {rect1.area()}, 周长: {rect1.perimeter()}")
    
    # 创建自定义尺寸的矩形
    rect2 = Rectangle(5, 3)
    print(f"5x3矩形 - 面积: {rect2.area()}, 周长: {rect2.perimeter()}")
    
    # 尝试创建无效的矩形(会抛出异常)
    rect3 = Rectangle(-1, 5)
    
except ValueError as e:
    print(f"创建矩形失败: {e}")
except Exception as e:
    print(f"未知错误: {e}")

实例方法表格

功能名称实例调用方法具体功能、注意事项、必需参数/可选参数
定义构造方法def __init__(self, ...):必须包含self参数,可以有其他参数用于初始化
参数验证__init__中使用条件判断可以在构造方法中验证参数有效性,抛出异常处理无效输入
默认参数def __init__(self, param=default):可以为参数设置默认值,使参数变为可选
调用父类构造方法super().__init__()在继承中调用父类的构造方法

使用示例

python
# 演示更复杂的__init__方法,包含类型检查和多种初始化方式
class Point:
    """二维坐标点类"""
    
    def __init__(self, x=0, y=0):
        """
        支持多种初始化方式:
        - Point() -> (0, 0)
        - Point(3) -> (3, 0)  
        - Point(3, 4) -> (3, 4)
        - Point(x=3, y=4) -> (3, 4)
        """
        # 类型检查:确保x和y是数字
        if not isinstance(x, (int, float)) or not isinstance(y, (int, float)):
            raise TypeError("坐标必须是数字")
        
        self.x = x
        self.y = y
    
    def distance_from_origin(self):
        """计算到原点的距离"""
        import math
        return math.sqrt(self.x ** 2 + self.y ** 2)
    
    def __str__(self):
        """定义对象的字符串表示"""
        return f"Point({self.x}, {self.y})"

# 测试不同的初始化方式
try:
    p1 = Point()           # 默认(0, 0)
    p2 = Point(3)          # (3, 0)
    p3 = Point(3, 4)       # (3, 4)
    p4 = Point(x=5, y=12)  # (5, 12)
    
    print(p1, f"到原点距离: {p1.distance_from_origin():.2f}")
    print(p2, f"到原点距离: {p2.distance_from_origin():.2f}")
    print(p3, f"到原点距离: {p3.distance_from_origin():.2f}")
    print(p4, f"到原点距离: {p4.distance_from_origin():.2f}")
    
    # 尝试无效输入
    p5 = Point("hello", 5)
    
except TypeError as e:
    print(f"类型错误: {e}")
except Exception as e:
    print(f"其他错误: {e}")

注意事项

  • __init__方法没有返回值,或者说它的返回值总是None
  • 如果在__init__中抛出异常,对象创建会失败,不会返回部分初始化的对象
  • __init__不是真正的构造函数,真正的构造函数是__new__,但在绝大多数情况下我们只需要重写__init__
  • 可以在__init__中调用其他方法来进行复杂的初始化逻辑
  • 参数验证和类型检查是__init__中的常见做法,有助于提高代码的健壮性

这节重点讲解了构造方法__init__的作用和使用方式。它是对象初始化的关键,让我们能够在创建对象时设置其初始状态,并进行必要的参数验证。

类的基本封装概念

封装是面向对象编程的三大特性之一(另外两个是继承和多态),它的核心思想是将数据(属性)和操作数据的方法组合在一起,并隐藏对象的内部实现细节,只暴露必要的接口给外部使用。

在Python中,虽然没有严格的私有成员概念(不像Java或C++那样有private关键字),但我们有一些约定和机制来实现封装。最常用的是使用下划线前缀来表示"内部使用"的属性和方法。

python
# 定义一个Temperature类来演示封装概念
class Temperature:
    """温度类,支持摄氏度和华氏度的转换"""
    
    def __init__(self, celsius=0):
        """初始化温度,内部统一使用摄氏度存储"""
        self._celsius = celsius  # 使用单下划线表示"受保护"的属性
    
    @property
    def celsius(self):
        """摄氏度属性的getter方法"""
        return self._celsius
    
    @celsius.setter
    def celsius(self, value):
        """摄氏度属性的setter方法,包含验证逻辑"""
        if value < -273.15:
            raise ValueError("温度不能低于绝对零度(-273.15°C)")
        self._celsius = value
    
    @property
    def fahrenheit(self):
        """华氏度属性的getter方法"""
        return self._celsius * 9/5 + 32
    
    @fahrenheit.setter
    def fahrenheit(self, value):
        """华氏度属性的setter方法"""
        celsius_value = (value - 32) * 5/9
        if celsius_value < -273.15:
            raise ValueError("温度不能低于绝对零度(-459.67°F)")
        self._celsius = celsius_value
    
    def __str__(self):
        return f"{self._celsius:.2f}°C ({self.fahrenheit:.2f}°F)"

# 使用Temperature类
try:
    # 创建温度对象
    temp = Temperature(25)
    print(temp)  # 输出: 25.00°C (77.00°F)
    
    # 通过属性访问器修改温度
    temp.celsius = 30
    print(f"修改后: {temp}")
    
    # 通过华氏度设置温度
    temp.fahrenheit = 86
    print(f"通过华氏度设置: {temp}")
    
    # 尝试设置无效温度
    temp.celsius = -300
    
except ValueError as e:
    print(f"温度设置错误: {e}")
except Exception as e:
    print(f"其他错误: {e}")

实例方法表格

功能名称实例调用方法具体功能、注意事项、必需参数/可选参数
属性装饰器@property将方法转换为只读属性,提供受控的数据访问
属性setter@property_name.setter为属性提供设置方法,可以在设置时进行验证
受保护属性_attribute单下划线前缀表示该属性是内部使用的,不应直接访问
私有属性__attribute双下划线前缀会触发名称改写,提供更强的封装

使用示例

python
# 演示不同级别的封装
class BankAccountSecure:
    """更安全的银行账户类"""
    
    def __init__(self, account_number, initial_balance=0):
        self._account_number = account_number  # 受保护属性
        self.__balance = initial_balance       # 私有属性(名称改写)
        self.__pin = "1234"                    # 私有PIN码
    
    @property
    def account_number(self):
        """只读的账户号码"""
        return self._account_number
    
    @property
    def balance(self):
        """受控的余额访问"""
        return self.__balance
    
    def deposit(self, amount, pin):
        """存款方法,需要PIN码验证"""
        if pin != self.__pin:
            raise ValueError("PIN码错误")
        if amount <= 0:
            raise ValueError("存款金额必须大于0")
        self.__balance += amount
        return f"存款成功,当前余额: {self.__balance}"
    
    def withdraw(self, amount, pin):
        """取款方法,需要PIN码验证"""
        if pin != self.__pin:
            raise ValueError("PIN码错误")
        if amount <= 0:
            raise ValueError("取款金额必须大于0")
        if amount > self.__balance:
            raise ValueError("余额不足")
        self.__balance -= amount
        return f"取款成功,当前余额: {self.__balance}"

# 测试封装效果
try:
    account = BankAccountSecure("123456789", 1000)
    
    # 正常使用公共接口
    print(f"账户号码: {account.account_number}")
    print(f"当前余额: {account.balance}")
    
    # 尝试直接访问私有属性(会失败)
    # print(account.__balance)  # AttributeError
    
    # 通过正确方法操作
    print(account.deposit(500, "1234"))
    print(account.withdraw(200, "1234"))
    
    # 尝试错误PIN码
    account.withdraw(100, "0000")
    
except ValueError as e:
    print(f"操作失败: {e}")
except AttributeError as e:
    print(f"属性访问错误: {e}")
except Exception as e:
    print(f"未知错误: {e}")

注意事项

  • Python中的封装主要是约定性的,单下划线_表示"内部使用",双下划线__会触发名称改写
  • @property装饰器是实现封装的强大工具,可以让方法像属性一样被访问
  • 封装的目的是提供清晰的接口,隐藏复杂的实现细节,而不是完全阻止访问
  • 过度封装可能导致代码复杂化,应该在易用性和安全性之间找到平衡
  • 即使使用双下划线,技术上仍然可以通过_ClassName__attribute的方式访问私有属性,但这违背了封装原则

这节介绍了Python中封装的基本概念和实现方式。通过合理的封装,我们可以创建更安全、更易维护的类,同时为用户提供简洁清晰的接口。