当前位置:首页>python>Python 零基础100天—Day22 方法与封装

Python 零基础100天—Day22 方法与封装

  • 2026-10-11 07:28:33
Python 零基础100天—Day22 方法与封装

🐍 方法与封装 — 让类更强大

🕐 预计用时:2-3 小时 | 🎯 目标:掌握实例方法/类方法/静态方法、私有属性、property


📖 今日目录

  1. 三种方法:实例方法
  2. 三种方法:类方法 @classmethod
  3. 三种方法:静态方法 @staticmethod
  4. 三种方法对比
  5. 私有属性与名称改写
  6. property 属性装饰器
  7. 封装的实际应用
  8. 实战练习
  9. 今日小结

1. 三种方法:实例方法

实例方法是最常用的方法——第一个参数是 self,能访问和修改实例属性。

class Dog:    def __init__(self, name, age):        self.name = name        self.age = age    # 实例方法:第一个参数是 self    def bark(self):        print(f"{self.name}:汪汪!")    def birthday(self):        self.age += 1        print(f"🎂 {self.name} 生日快乐!现在 {self.age} 岁")dog = Dog("旺财", 3)dog.bark()       # 旺财:汪汪!dog.birthday()   # 🎂 旺财 生日快乐!现在 4 岁

💡 实例方法的特点:1. 第一个参数是 self2. 能访问/修改实例属性3. 能访问/修改类属性4. 通过 对象.方法() 调用


2. 三种方法:类方法 @classmethod

类方法的第一个参数是 cls(类本身),不需要创建对象就能调用。

class Student:    school = "Python大学"    student_count = 0    def __init__(self, name, score):        self.name = name        self.score = score        Student.student_count += 1    # 类方法:第一个参数是 cls(类本身)    @classmethod    def get_school(cls):        return cls.school    @classmethod    def get_count(cls):        return cls.student_count    @classmethod    def from_string(cls, data_str):        """从字符串创建对象(工厂方法)"""        name, score = data_str.split(",")        return cls(name.strip(), int(score.strip()))# 不需要创建对象,直接通过类调用print(Student.get_school())   # Python大学print(Student.get_count())    # 0# 工厂方法:从不同格式创建对象s1 = Student("张三", 85)s2 = Student.from_string("李四, 92")  # 从字符串创建print(s2.name, s2.score)     # 李四 92print(Student.get_count())   # 2

🎯 类方法的典型用途

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):        from datetime import datetime        now = datetime.now()        return cls(now.year, now.month, now.day)    def __str__(self):        return f"{self.year:04d}-{self.month:02d}-{self.day:02d}"# 多种创建方式d1 = Date(2024, 1, 15)d2 = Date.from_string("2024-06-20")d3 = Date.today()print(d1)  # 2024-01-15print(d2)  # 2024-06-20print(d3)  # 2024-xx-xx(当天日期)

💡 类方法的核心价值:1. 工厂方法:从不同格式创建对象(from_string、from_dict、today)2. 不需要实例:直接通过类调用3. 能被子类继承:cls 自动指向调用它的类


3. 三种方法:静态方法 @staticmethod

静态方法就是普通函数,只是"住"在类里面——不需要 self,也不需要 cls。

class MathUtils:    pi = 3.14159    # 静态方法:不需要 self 或 cls    @staticmethod    def is_even(n):        return n % 2 == 0    @staticmethod    def factorial(n):        if n <= 1:            return 1        result = 1        for i in range(2, n + 1):            result *= i        return result    @staticmethod    def gcd(a, b):        """最大公约数"""        while b:            a, b = b, a % b        return a    @staticmethod    def lcm(a, b):        """最小公倍数"""        return a * b // MathUtils.gcd(a, b)# 不需要创建对象print(MathUtils.is_even(4))     # Trueprint(MathUtils.factorial(5))   # 120print(MathUtils.gcd(12, 8))    # 4print(MathUtils.lcm(12, 8))    # 24

💡 什么时候用静态方法?1. 方法不需要访问实例属性或类属性2. 方法和类有逻辑关系,但不需要 self3. 纯工具函数,放在类里只是为了组织代码


4. 三种方法对比

特性
实例方法
类方法
静态方法
装饰器
无
@classmethod@staticmethod
第一个参数
self
(实例)
cls
(类)
无特殊参数
访问实例属性
✅
❌
❌
访问类属性
✅
✅(通过 cls)
❌(只能通过类名)
调用方式
对象.方法()类名.方法()类名.方法()
典型用途
操作实例数据
工厂方法、类级操作
工具函数
class Demo:    class_attr = "我是类属性"    def __init__(self, value):        self.value = value    def instance_method(self):        """实例方法:能访问实例和类属性"""        return f"实例:{self.value}, 类:{self.class_attr}"    @classmethod    def class_method(cls):        """类方法:能访问类属性,不能访问实例属性"""        return f"类:{cls.class_attr}"    @staticmethod    def static_method(x, y):        """静态方法:不能访问任何属性"""        return x + yd = Demo(42)print(d.instance_method())      # 实例:42, 类:我是类属性print(Demo.class_method())      # 类:我是类属性print(Demo.static_method(3, 5)) # 8

5. 私有属性与名称改写

Python 用"双下划线前缀"实现私有属性——外部不能直接访问。

class BankAccount:    def __init__(self, owner, balance):        self.owner = owner        # 公开属性        self.__balance = balance  # 私有属性(双下划线前缀)        self.__password = None    # 私有属性    # 通过方法访问私有属性(受控访问)    def get_balance(self, password):        if password == self.__password:            return self.__balance        return "密码错误"    def set_password(self, old_pw, new_pw):        if self.__password is None or self.__password == old_pw:            self.__password = new_pw            print("✅ 密码已设置")        else:            print("❌ 原密码错误")    def deposit(self, amount):        if amount > 0:            self.__balance += amount            print(f"✅ 存入 ¥{amount},余额 ¥{self.__balance}")    def withdraw(self, amount, password):        if password != self.__password:            print("❌ 密码错误")            return        if amount > self.__balance:            print("❌ 余额不足")            return        self.__balance -= amount        print(f"✅ 取出 ¥{amount},余额 ¥{self.__balance}")acc = BankAccount("张三", 10000)acc.set_password(None, "123456")# print(acc.__balance)    # ❌ AttributeError: 私有属性,外部不能访问print(acc.get_balance("123456"))  # ✅ 10000(通过方法访问)acc.deposit(5000)acc.withdraw(3000, "123456")

🔍 名称改写(Name Mangling)

# Python 的"私有"不是真的私有,而是名称改写class Secret:    def __init__(self):        self.__secret = "密码123"s = Secret()# print(s.__secret)         # ❌ AttributeErrorprint(s._Secret__secret)    # ✅ "密码123"(名称被改写了)# 双下划线属性被改写为:_类名__属性名# 这是 Python 的设计哲学:"我们都是成年人",不强制私有
前缀
含义
外部访问
name
公开属性
✅ 直接访问
_name
受保护属性(约定)
⚠️ 可以访问但不建议
__name
私有属性(名称改写)
❌ 需要 _类名__name

💡 Python 的封装哲学:Python 不强制私有,而是"约定":_name = "我是受保护的,请不要直接用"__name = "我是私有的,别碰"真正的保护靠文档和团队约定,不是语言强制。


6. property 属性装饰器

@property 让方法像属性一样访问——既简洁,又能控制读写。

class Circle:    def __init__(self, radius):        self._radius = radius  # 受保护属性    @property    def radius(self):        """getter:读取时调用"""        return self._radius    @radius.setter    def radius(self, value):        """setter:赋值时调用"""        if value < 0:            raise ValueError("半径不能为负数")        self._radius = value    @property    def area(self):        """只读属性(没有 setter)"""        return 3.14159 * self._radius ** 2    @property    def circumference(self):        """只读属性"""        return 2 * 3.14159 * self._radiusc = Circle(5)print(c.radius)          # 5(像属性一样访问,实际调用 getter)print(c.area)            # 78.54(只读属性)print(c.circumference)   # 31.42c.radius = 10            # 像属性一样赋值,实际调用 setterprint(c.area)            # 314.16# c.radius = -5          # ❌ ValueError: 半径不能为负数# c.area = 100           # ❌ AttributeError: 只读属性

🎯 property 的实际应用

class User:    def __init__(self, first_name, last_name, birth_year):        self.first_name = first_name        self.last_name = last_name        self.birth_year = birth_year    @property    def full_name(self):        """全名(只读)"""        return f"{self.first_name}{self.last_name}"    @property    def age(self):        """年龄(只读,自动计算)"""        from datetime import datetime        return datetime.now().year - self.birth_year    @property    def email(self):        """邮箱(只读,自动生成)"""        return f"{self.first_name.lower()}.{self.last_name.lower()}@example.com"user = User("张", "三", 1995)print(user.full_name)  # 张三(像属性,不是方法调用)print(user.age)        # 自动计算print(user.email)      # 自动生成

💡 @property 的三大用途:1. 只读属性:只有 getter,没有 setter2. 计算属性:每次访问时自动计算3. 受控访问:读取/赋值时可以加验证逻辑


7. 封装的实际应用

🎯 完整的温度类(综合运用)

class Temperature:    """温度类:支持摄氏/华氏/开尔文互转"""    def __init__(self, celsius=0):        self._celsius = celsius    @property    def celsius(self):        return self._celsius    @celsius.setter    def celsius(self, value):        if value < -273.15:            raise ValueError("温度不能低于绝对零度 (-273.15°C)")        self._celsius = value    @property    def fahrenheit(self):        return self._celsius * 9 / 5 + 32    @fahrenheit.setter    def fahrenheit(self, value):        self.celsius = (value - 32) * 5 / 9    @property    def kelvin(self):        return self._celsius + 273.15    @kelvin.setter    def kelvin(self, value):        self.celsius = value - 273.15    def __str__(self):        return f"{self.celsius:.1f}°C = {self.fahrenheit:.1f}°F = {self.kelvin:.2f}K"    def __eq__(self, other):        return abs(self._celsius - other._celsius) < 0.001    def __lt__(self, other):        return self._celsius < other._celsius# 使用t1 = Temperature(100)print(t1)  # 100.0°C = 212.0°F = 373.15Kt2 = Temperature()t2.fahrenheit = 72print(t2)  # 22.2°C = 72.0°F = 295.37Kt3 = Temperature()t3.kelvin = 0print(t3)  # -273.1°C = -459.7°F = 0.00Kprint(t1 > t2)   # Trueprint(t1 == Temperature(100))  # True

8. 实战练习

🎯 练习 1:密码管理器

import hashlibimport timeclass PasswordManager:    """密码管理器:加密存储、验证、过期检测"""    def __init__(self, username):        self.username = username        self.__password_hash = None        self.__created_at = None        self.__last_changed = None        self.__attempt_count = 0        self.__locked = False    @staticmethod    def _hash(password):        """密码哈希(静态方法)"""        return hashlib.sha256(password.encode()).hexdigest()    @property    def is_set(self):        """是否已设置密码"""        return self.__password_hash is not None    @property    def is_locked(self):        """是否被锁定"""        return self.__locked    def set_password(self, new_password, confirm_password):        """设置密码"""        if self.__locked:            print("❌ 账号已锁定,无法设置密码")            return False        if new_password != confirm_password:            print("❌ 两次密码不一致")            return False        if len(new_password) < 6:            print("❌ 密码长度不能少于6位")            return False        self.__password_hash = self._hash(new_password)        self.__created_at = time.time()        self.__last_changed = time.time()        self.__attempt_count = 0        print(f"✅ {self.username} 密码已设置")        return True    def verify(self, password):        """验证密码"""        if self.__locked:            print("❌ 账号已锁定")            return False        if not self.is_set:            print("❌ 未设置密码")            return False        if self._hash(password) == self.__password_hash:            self.__attempt_count = 0            print(f"✅ {self.username} 验证成功")            return True        else:            self.__attempt_count += 1            remaining = 3 - self.__attempt_count            if remaining <= 0:                self.__locked = True                print(f"❌ 连续错误3次,账号已锁定!")            else:                print(f"❌ 密码错误,还剩 {remaining} 次机会")            return False    def change_password(self, old_password, new_password):        """修改密码"""        if self._hash(old_password) != self.__password_hash:            print("❌ 原密码错误")            return False        if len(new_password) < 6:            print("❌ 新密码长度不能少于6位")            return False        self.__password_hash = self._hash(new_password)        self.__last_changed = time.time()        print(f"✅ {self.username} 密码已修改")        return True    def reset_lock(self):        """重置锁定(管理员功能)"""        self.__locked = False        self.__attempt_count = 0        print(f"✅ {self.username} 锁定已解除")# 测试pm = PasswordManager("张三")pm.set_password("mypass123", "mypass123")pm.verify("mypass123")    # ✅pm.verify("wrong")        # ❌ 还剩2次pm.verify("wrong")        # ❌ 还剩1次pm.verify("wrong")        # ❌ 锁定!pm.verify("mypass123")    # ❌ 账号已锁定pm.reset_lock()           # ✅ 解除锁定

🎯 练习 2:电商商品类

class Product:    """电商商品"""    _id_counter = 0    def __init__(self, name, price, category):        Product._id_counter += 1        self._id = f"P{Product._id_counter:04d}"        self.name = name        self._price = price        self.category = category        self._stock = 0        self._sold = 0        self._reviews = []    @property    def price(self):        return self._price    @price.setter    def price(self, value):        if value < 0:            raise ValueError("价格不能为负")        self._price = value    @property    def stock(self):        return self._stock    @property    def rating(self):        if not self._reviews:            return 0        return round(sum(self._reviews) / len(self._reviews), 1)    @property    def revenue(self):        return self._price * self._sold    def restock(self, quantity):        self._stock += quantity        print(f"📦 {self.name} 补货 {quantity},库存: {self._stock}")    def sell(self, quantity=1):        if quantity > self._stock:            print(f"❌ 库存不足({self._stock})")            return False        self._stock -= quantity        self._sold += quantity        print(f"🛒 售出 {self.name} ×{quantity}")        return True    def add_review(self, score):        if 1 <= score <= 5:            self._reviews.append(score)            print(f"⭐ 新评分: {score},平均: {self.rating}")    def __str__(self):        return f"[{self._id}] {self.name} | ¥{self._price} | 库存:{self._stock} | 售出:{self._sold} | 评分:{self.rating}"# 测试p = Product("Python教程", 89.9, "图书")p.restock(100)p.sell(3)p.add_review(5)p.add_review(4)p.add_review(5)print(p)print(f"总收入: ¥{p.revenue:.2f}")

9. 今日小结

知识点
核心内容
实例方法
第一个参数 self,操作实例数据
类方法
@classmethod
,第一个参数 cls,工厂方法
静态方法
@staticmethod
,无特殊参数,工具函数
私有属性
__name
,名称改写为 _类名__name
受保护属性
_name
,约定俗成,不强制
@property
让方法像属性访问,支持 getter/setter
只读属性
只有 getter 没有 setter

🧠 记忆口诀:实例方法用 self,操作数据最常见。类方法用 cls,工厂方法创建用。静态方法啥不带,工具函数放里边。双下划线变私有,property 像属性。

🔮 预告: Day 23 继承与多态 — 单继承/多继承、super()、方法重写。代码复用的终极武器!

轻松时刻:

请在微信客户端打开

最新文章

随机文章