一、封装特性

封装是面向对象编程的重要特性,在 Python 中主要通过以下方式实现:

  1. 属性私有化
    • 受保护属性:属性名以单下划线_开头,这类属性可在本类或子类中使用,不过这更多是一种编程约定。
    • 私有属性:属性名以双下划线__开头,它只能在当前类内部被访问。实际上,Python 会将其重命名为_类名__属性名的形式,虽然可以通过这种重命名后的名称在外部访问私有属性,但不建议这么做。
  2. 示例代码

    class Cat:
        def __init__(self, name, age):
            self.__name = name  # 私有属性
            self._age = age     # 受保护属性
    

二、property 属性

1. 基本概念

property 属性的作用是将属性的获取、修改和删除方法整合为一个属性,让属性操作能像普通字段一样简洁,同时隐藏内部的实现细节。

2. 两种实现方式

方式一:使用 property 类

class Cat:
    def __init__(self, age):
        self.__age = age

    def get_age(self):
        return self.__age

    def set_age(self, age):
        if 0 <= age <= 20:
            self.__age = age
        else:
            raise ValueError("年龄需在0-20之间")

    age = property(get_age, set_age)
方式二:使用装饰器

class Dog:
    def __init__(self, age):
        self.__age = age

    @property
    def age(self):
        return self.__age

    @age.setter
    def age(self, age):
        if 0 <= age <= 20:
            self.__age = age
        else:
            raise ValueError("年龄需在0-20之间")

三、静态方法和类方法

1. 静态方法

静态方法使用@staticmethod装饰器来标记,它与类和对象都没有直接关联,其第一个参数不需要是selfcls。静态方法通常用于封装与类相关的工具函数。

class Calc:
    @staticmethod
    def add(a, b):
        return a + b

2. 类方法

类方法使用@classmethod装饰器来标记,它的第一个参数通常是cls,代表类本身。类方法常被用于实现工厂模式或操作类属性。

class Calc:
    @classmethod
    def add(cls, a, b):
        return a + b

四、成员属性与类属性

1. 成员属性

成员属性在__init__方法中定义,它与对象相关,每个对象的成员属性都是独立的。

class Book:
    def __init__(self, title):
        self.title = title  # 成员属性

2. 类属性

类属性直接定义在类中,它被所有对象共享。当通过对象访问类属性时,如果对象没有同名的成员属性,就会访问类属性;而当通过对象对类属性进行赋值操作时,实际上会创建一个同名的成员属性。

class Book:
    count = 0  # 类属性

    def __init__(self):
        Book.count += 1

五、集合去重原理

1. set 集合去重机制

set 集合去重依赖于对象的__hash____eq__方法。如果两个对象的哈希值相同,并且通过__eq__方法比较结果也相同,那么 set 集合会将它们视为同一个对象,从而实现去重。

2. 示例代码

class Fish:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def __eq__(self, other):
        return self.name == other.name and self.age == other.age

    def __hash__(self):
        return hash((self.name, self.age))

六、注意事项

  1. 访问私有属性:在类外部可以通过_类名__属性名的方式访问私有属性,但这种方式破坏了类的封装性,不建议使用。
  2. 类方法与静态方法的调用:类方法和静态方法都可以通过类名直接调用,不过静态方法也能通过对象调用,只是不推荐这么做。
  3. 属性查找顺序:当通过对象访问属性时,会先查找成员属性,若没有则查找类属性;而对属性进行赋值操作时,会优先创建或修改成员属性。

一、圆形类(Circle)

需求:定义圆形类,私有化半径属性,实现周长、面积计算;创建对象时半径可缺省(默认 0)。

class Circle:
    def __init__(self, radius=0):
        self.radius = radius  # 调用setter方法

    # 计算面积
    def circle_s(self):
        return 3.14 * self.radius **2

    # 计算周长
    def circle_c(self):
        return 2 * 3.14 * self.radius

    # 半径属性(私有化访问)
    @property
    def radius(self):
        return self.__radius

    @radius.setter
    def radius(self, radius):
        if radius < 0:
            raise ValueError("半径不可能为负数!")
        self.__radius = radius

    @radius.deleter
    def radius(self):
        del self.__radius


# 测试代码
if __name__ == '__main__':
    try:
        c1 = Circle(-1)  # 触发异常
    except ValueError as e:
        print(e)  # 输出:半径不可能为负数!

    c2 = Circle(2)
    print(f"半径为2的圆面积:{c2.circle_s()}")  # 输出:12.56
    print(f"半径为2的圆周长:{c2.circle_c()}")  # 输出:12.56

    c3 = Circle()  # 缺省半径(默认0)
    print(f"默认半径的圆面积:{c3.circle_s()}")  # 输出:0.0

二、小说类(Novel)

需求:私有化作者、小说名、类型、价格属性;实现列表排序(按价格降序)和集合去重(按小说名 + 作者)。

class Novel:
    def __init__(self, author, name, genre, price):
        self.author = author
        self.name = name
        self.genre = genre
        self.price = price

    # 用于判断对象是否相等(去重依据)
    def __eq__(self, other):
        if not isinstance(other, Novel):
            return False
        return self.name == other.name and self.author == other.author

    # 用于集合去重(哈希值依据)
    def __hash__(self):
        return hash((self.name, self.author))

    # 打印对象时显示的信息
    def __repr__(self):
        return f"Novel(作者:{self.author}, 书名:{self.name}, 类型:{self.genre}, 价格:{self.price})"

    # 私有属性的property访问
    @property
    def author(self):
        return self.__author

    @author.setter
    def author(self, author):
        self.__author = author

    @property
    def name(self):
        return self.__name

    @name.setter
    def name(self, name):
        self.__name = name

    @property
    def genre(self):
        return self.__genre

    @genre.setter
    def genre(self, genre):
        self.__genre = genre

    @property
    def price(self):
        return self.__price

    @price.setter
    def price(self, price):
        self.__price = price


# 测试代码
if __name__ == '__main__':
    # 1. 创建5本小说并存储到列表
    novels = [
        Novel("金庸", "射雕英雄传", "武侠", 59.8),
        Novel("金庸", "神雕侠侣", "武侠", 69.8),
        Novel("古龙", "陆小凤传奇", "武侠", 49.9),
        Novel("金庸", "射雕英雄传", "武侠", 59.8),  # 与第一本重复(书名+作者相同)
        Novel("JK罗琳", "哈利波特", "魔幻", 89.0)
    ]

    # 按价格降序排序
    novels_sorted = sorted(novels, key=lambda x: x.price, reverse=True)
    print("按价格降序排序后的小说:")
    for novel in novels_sorted:
        print(novel)

    # 2. 存储到集合(自动去重)
    novels_set = set(novels)
    print("\n去重后的小说集合:")
    for novel in novels_set:
        print(novel)

三、工具类(QikuxUtils)

需求:实现数字计算、闰年判断、手机号 / 邮箱验证及脱敏等静态方法。

import re  # 用于正则表达式验证

class QikuxUtils:
    # a) 计算多个数字的和(至少1个数字)
    @staticmethod
    def sum(a, *args):
        total = a
        for num in args:
            total += num
        return total

    # b) 计算多个数字的最大值
    @staticmethod
    def max(a, *args):
        current_max = a
        for num in args:
            if num > current_max:
                current_max = num
        return current_max

    # c) 计算多个数字的最小值
    @staticmethod
    def min(a, *args):
        current_min = a
        for num in args:
            if num < current_min:
                current_min = num
        return current_min

    # d) 判断是否为闰年
    @staticmethod
    def is_leap_year(year):
        return (year % 4 == 0 and year % 100 != 0) or (year % 400 == 0)

    # e) 验证手机号(11位数字,以1开头,第二位为3/4/5/6/7/8/9)
    @staticmethod
    def is_tel(string):
        return len(string) == 11 and string.isdigit() and string[0] == '1' and string[1] in '3456789'

    # f) 验证邮箱(简化版:包含@和.,且格式合法)
    @staticmethod
    def is_email(string):
        # 正则表达式:用户名@域名.后缀(简化验证)
        pattern = r'^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$'
        return re.match(pattern, string) is not None

    # g) 手机号脱敏(中间4位用****代替)
    @staticmethod
    def tel_secure(string):
        if QikuxUtils.is_tel(string):
            return f"{string[:3]}****{string[7:]}"
        return string

    # h) 邮箱脱敏(账号保留第一个字符,其余用6个*代替)
    @staticmethod
    def email_secure(string):
        if QikuxUtils.is_email(string):
            username, domain = string.split('@')
            return f"{username[0]}******@{domain}"
        return string

    # i) 判断是否为偶数
    @staticmethod
    def is_even(num):
        return num % 2 == 0

    # j) 判断是否为奇数
    @staticmethod
    def is_odd(num):
        return num % 2 != 0


# 测试代码
if __name__ == "__main__":
    # 测试数字计算
    print("sum(1,2,3) =", QikuxUtils.sum(1,2,3))  # 6
    print("max(5,1,9) =", QikuxUtils.max(5,1,9))  # 9

    # 测试闰年
    print("2024是否闰年?", QikuxUtils.is_leap_year(2024))  # True

    # 测试手机号
    tel = "13812345678"
    print(f"手机号{tel}是否合法?", QikuxUtils.is_tel(tel))  # True
    print("手机号脱敏:", QikuxUtils.tel_secure(tel))  # 138****5678

    # 测试邮箱
    email = "test@example.com"
    print(f"邮箱{email}是否合法?", QikuxUtils.is_email(email))  # True
    print("邮箱脱敏:", QikuxUtils.email_secure(email))  # t******@example.com

四、列表工具类(ListUtils)

需求:实现列表过滤、映射、查找等静态方法。

from typing import List, Any, Callable

class ListUtils:
    # 过滤满足条件的元素(返回新列表)
    @staticmethod
    def filter(array: List[Any], predicate: Callable[[Any, int], bool]) -> List[Any]:
        return [x for i, x in enumerate(array) if predicate(i, x)]

    # 按规则映射元素(返回新列表)
    @staticmethod
    def map(array: List[Any], functional: Callable[[Any, int], Any]) -> List[Any]:
        return [functional(i, x) for i, x in enumerate(array)]

    # 查找第一个满足条件的元素(无则返回None)
    @staticmethod
    def find(array: List[Any], predicate: Callable[[Any, int], bool]) -> Any:
        for i, x in enumerate(array):
            if predicate(i, x):
                return x
        return None

    # 查找第一个满足条件的元素索引(无则返回-1)
    @staticmethod
    def index(array: List[Any], predicate: Callable[[Any, int], bool]) -> int:
        for i, x in enumerate(array):
            if predicate(i, x):
                return i
        return -1

    # 列表扁平化(嵌套列表展开)
    @staticmethod
    def flat(array: List[Any]) -> List[Any]:
        result = []
        for item in array:
            if isinstance(item, list):
                result.extend(ListUtils.flat(item))  # 递归处理嵌套列表
            else:
                result.append(item)
        return result


# 测试代码
if __name__ == "__main__":
    ls = [1, 2, [3, 4, [5]], 6]
    
    # 测试flat
    print("扁平化列表:", ListUtils.flat(ls))  # [1,2,3,4,5,6]

    # 测试filter(保留偶数)
    print("过滤偶数:", ListUtils.filter([1,2,3,4], lambda i,x: x%2==0))  # [2,4]

    # 测试map(元素乘以2)
    print("元素乘2:", ListUtils.map([1,2,3], lambda i,x: x*2))  # [2,4,6]

五、猫类(Cat)

需求:私有化名字和年龄;实现对象比较(年龄)、集合去重(名字 + 年龄)。

class Cat:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    # 判断对象是否相等(名字+年龄相同)
    def __eq__(self, other):
        if not isinstance(other, Cat):
            return False
        return self.name == other.name and self.age == other.age

    # 集合去重的哈希值(名字+年龄)
    def __hash__(self):
        return hash((self.name, self.age))

    # 比较年龄大小
    def __gt__(self, other):  # 大于
        return self.age > other.age

    def __lt__(self, other):  # 小于
        return self.age < other.age

    # 打印对象信息
    def __repr__(self):
        return f"Cat(名字:{self.name}, 年龄:{self.age})"

    # 私有属性的property访问
    @property
    def name(self):
        return self.__name

    @name.setter
    def name(self, name):
        self.__name = name

    @property
    def age(self):
        return self.__age

    @age.setter
    def age(self, age):
        if age < 0:
            raise ValueError("年龄不能为负数")
        self.__age = age


# 测试代码
if __name__ == "__main__":
    cat1 = Cat("Tom", 3)
    cat2 = Cat("Tom", 3)  # 与cat1相同(名字+年龄)
    cat3 = Cat("Jerry", 2)

    # 测试对象相等
    print("cat1 == cat2?", cat1 == cat2)  # True
    print("cat1 > cat3?", cat1 > cat3)    # True(3>2)

    # 测试集合去重
    cats = {cat1, cat2, cat3}
    print("集合中的猫(去重后):", cats)  # 仅保留cat1和cat3

六、日历类(Calendar)

需求:私有化年、月、日;实现日历显示、星期计算、闰年判断等功能。

class Calendar:
    def __init__(self, year, month, day):
        self.year = year
        self.month = month
        self.day = day

    # a) 显示当月日历
    def show_calendar(self):
        max_day = self.get_month_max_day()  # 当月最大天数
        first_week = self.__get_first_day_week()  # 当月1日是星期几(0=周日)

        print(f"\n{self.year}年{self.month}月日历")
        print("日  一  二  三  四  五  六")  # 表头

        # 打印第一行的空格(1日之前的空位)
        for _ in range(first_week):
            print("   ", end=" ")

        # 打印日期
        for day in range(1, max_day + 1):
            # 格式化日期(占3位,右对齐)
            print(f"{day:2d} ", end=" ")
            # 每7个日期换行(周日开始)
            if (first_week + day) % 7 == 0:
                print()
        print()

    # 辅助方法:获取当月1日的星期(用于日历显示)
    def __get_first_day_week(self):
        temp = Calendar(self.year, self.month, 1)
        return temp.get_week_num()

    # b) 蔡勒公式获取星期(返回(0, "周日"))
    def get_week(self):
        y, m, d = self.year, self.month, self.day
        # 蔡勒公式适配:1-2月视为上一年的13-14月
        if m < 3:
            m += 12
            y -= 1
        c = y // 100  # 世纪数
        y = y % 100   # 年份后两位
        # 蔡勒公式:w = (d + [13(m+1)/5] + y + [y/4] + [c/4] + 5c) % 7
        w = (d + (13 * (m + 1)) // 5 + y + y // 4 + c // 4 + 5 * c) % 7
        # 转换为0=周日,1=周一...6=周六
        w = (w + 1) % 7 if w != 0 else 0
        week_str = ["周日", "周一", "周二", "周三", "周四", "周五", "周六"][w]
        return (w, week_str)

    # c) 判断是否为闰年
    def is_leap(self):
        return (self.year % 4 == 0 and self.year % 100 != 0) or (self.year % 400 == 0)

    # d) 获取当月最大天数
    def get_month_max_day(self):
        # 大月(31天)、小月(30天)、二月(28/29天)
        if self.month in [1,3,5,7,8,10,12]:
            return 31
        elif self.month in [4,6,9,11]:
            return 30
        else:  # 二月
            return 29 if self.is_leap() else 28

    # e) 获取当前日期是当年第几天
    def get_day_in_year(self):
        days = 0
        # 累加前几个月的天数
        for m in range(1, self.month):
            temp = Calendar(self.year, m, 1)
            days += temp.get_month_max_day()
        # 加当月天数
        days += self.day
        return days

    # f) 获取星期数字(0=周日)
    def get_week_num(self):
        return self.get_week()[0]

    # g) 获取星期字符串(如"周日")
    def get_week_str(self):
        return self.get_week()[1]

    # 私有属性的property访问
    @property
    def year(self):
        return self.__year

    @year.setter
    def year(self, year):
        self.__year = year

    @property
    def month(self):
        return self.__month

    @month.setter
    def month(self, month):
        if not 1 <= month <= 12:
            raise ValueError("月份必须在1-12之间")
        self.__month = month

    @property
    def day(self):
        return self.__day

    @day.setter
    def day(self, day):
        if not 1 <= day <= self.get_month_max_day():
            raise ValueError(f"日期必须在1-{self.get_month_max_day()}之间")
        self.__day = day


# 测试代码
if __name__ == '__main__':
    cal = Calendar(2024, 2, 29)  # 2024是闰年,2月有29天
    print(f"测试日期:{cal.year}年{cal.month}月{cal.day}日")
    print("是否闰年:", cal.is_leap())  # True
    print("当月最大天数:", cal.get_month_max_day())  # 29
    print("是当年第几天:", cal.get_day_in_year())  # 31(1月)+29(2月)=60
    print("星期:", cal.get_week())  # 例如(4, "周五")
    cal.show_calendar()  # 显示2024年2月日历

以上代码均包含完整的类定义、方法实现及测试逻辑,可直接运行验证功能。每个类的核心逻辑已通过注释说明,便于理解设计思路。

 

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐