Skip to content

介绍

一些有趣的编程练习题用来练习学过的知识

数字相关

猜数字游戏

python
from random import randint


def main():
    chances = 3    # 总共有多少次输入(猜测)的机会
    is_win = False # 是否赢得游戏
    print(f"猜数字游戏开始, 你有 {chances} 次机会!")

    min_num = 0
    max_num = 10
    print(f"请输入[{min_num} - {max_num}]范围内的一个数字!")

    anwser_num = randint(min_num, max_num)
    # print(f"答案是: {anwser_num}")

    while chances > 0:
        chances -= 1
        input_num = int(input("请输入一个数字: \n "))

        if input_num < min_num or input_num > max_num:
            print(f"输入的数字超出范围, 你还有{chances}次机会!!")
        elif input_num > anwser_num:
            print(f"你猜的数字大于答案, 你还有{chances}次机会!")
        elif input_num < anwser_num:
            print(f"你猜的数字小于答案, 你还有{chances}次机会!")
        else:
            is_win = True
            print("🎉 恭喜你猜对了, 你赢得了游戏")
            break

    if not is_win:
        print("😢 游戏结束, 你输了")


if __name__ == "__main__":
    main()

向上/向下/四舍五入取整

python
import math


def main():
    f1 = 1.1
    f2 = 1.8
    f3 = 1.5
    f4 = 1.4

    # 向上取整
    print(f"{f1} 向上取整: {math.ceil(f1)}")
    print(f"{f2} 向上取整: {math.ceil(f2)}")
    print(f"{f3} 向上取整: {math.ceil(f3)}")
    print(f"{f4} 向上取整: {math.ceil(f4)}")

    # 向下取整
    print(f"{f1} 向下取整: {math.floor(f1)}")
    print(f"{f2} 向下取整: {math.floor(f2)}")
    print(f"{f3} 向下取整: {math.floor(f3)}")
    print(f"{f4} 向下取整: {math.floor(f4)}")

    # 四舍五入取整: 注意是使用 builtins.round 而不是 math.round
    print(f"{f1} 四舍五入取整: {round(f1)}")
    print(f"{f2} 四舍五入取整: {round(f2)}")
    print(f"{f3} 四舍五入取整: {round(f3)}")
    print(f"{f4} 四舍五入取整: {round(f4)}")


if __name__ == "__main__":
    main()

最大/最小/绝对值/求和/求平均值

python
def main():
    nums = [1, 3, 5, -7, -9, 12, 18]

    print(f"{nums}中的最大值{max(nums)}")
    print(f"{nums}中的最小值{min(nums)}")
    print(f"{nums}中的所有数字的和{sum(nums)}")
    print(f"{nums}中的所有数字的平均值{sum(nums) / len(nums)}")
    print(f"一个数字的绝对值: -5 绝对值{abs(-5)}, 6 绝对值{abs(6)}")
    print(f"一个数字的n次方: 5 的 2 次方: {5**2}, 2 的 5 次方:{pow(2, 5)}")


if __name__ == "__main__":
    main()

日期时间处理

python
from datetime import datetime
import time

def main():
    # 1.获取当前日期+时间+毫秒
    now = datetime.now()
    print("now:", now) # 2020-01-12 12:01:33.053142

    # 2.获取当前的日期
    date = datetime.now().date()
    print("date:", date) # 2020-01-12

    # 3.获取格式化的日期+时间字符串
    now = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
    print("formated datetime:", now) # 2020-01-12 12:01:33

    # 4.获取当前时间戳
    timestamp = int(datetime.now().timestamp())
    print("timestamp:", timestamp) # 1600709953

    # 5.使用 time 模块获取时间戳(秒)
    timestamp = int(time.time())
    print("timestamp:", timestamp) # 1600709953.053142

    # 6.获取指定时间的时间戳
    time_str = "2024-03-20 12:00:00"
    time_tuple = time.strptime(time_str, "%Y-%m-%d %H:%M:%S")
    time_stamp = int(time.mktime(time_tuple))
    print("time_stamp:", time_stamp) # 1710907200


if __name__ == "__main__":
    main()

字符串处理

格式化

python
def main():
    # f 模板字符串
    user = "tom"
    message = f"hello {user}!"
    print(message)

    # format 方法
    formated_str = "{a} + {b} = {result}".format(a=1, b=2, result=1 + 2)
    print(formated_str)

if __name__ == "__main__":
    main()

搜索替换

python
def main():
    message = "python is best program language in the world!"

    ### 搜索 ###
    # 1.find 方法查询子字符串的开始位置
    start_idx1 = message.find("best")  # 找到返回开始索引位置10
    start_idx2 = message.find("hello")  # 找不到返回 -1
    print(start_idx1, start_idx2)

    # 2.index 方法查询子字符串的开始位置
    start_idx3 = message.index("best")  # 找到返回开始索引位置 10
    print(start_idx3)
    # index 找不到抛出异常: ValueError: substring not found
    # start_idx4 = message.index("hello")
    # print(start_idx4)

    # 3.字符串是否以某个字符串开始/结尾
    if message.startswith("python"):
        print("message starts with python")
    if message.endswith("world"):
        print("message ends with world")

    # 4.一个字符串是否包含某个字符串
    # 4.1 使用 find/index 方法
    if message.find("best") != -1:
        print("best is in message")

    try:
        message.index("bast")
        print("bast is in message")
    except ValueError:
        print("bast not is in message")

    # 4.2 使用 in/not in 运算符
    if "best" in message:
        print("best is in message")

    if "bast" in message:
        print("bast is in message")
    else:
        print("bast not in message")

    if "bast" not in message:
        print("bast not in message")

    ### 替换 ###
    # 1.直接替换
    result = message.replace("world", "universe")
    print(result)

    # 2.替换所有
    strs = "donwload.apple.com,update.apple.com,shop.apple.com"
    result = strs.replace("apple", "pear")
    print(result)  # donwload.pear.com,update.pear.com,shop.pear.com

    # 3.只替换一次
    result = strs.replace("apple", "pear", 1)
    print(result)  # donwload.pear.com,update.apple.com,shop.apple.com


if __name__ == "__main__":
    main()

拆分合并

python
def main():
    # split: 将字符串切割为列表
    strs = "donwload.apple.com,update.apple.com,shop.apple.com"
    urls = strs.split(",")
    print(urls)  # ['donwload.apple.com', 'update.apple.com', 'shop.apple.com']

    # join: 将列表合并为字符串
    str2 = "-".join(urls)
    print(str2)  # donwload.apple.com-update.apple.com-shop.apple.com

    # +运算符/模板字符串拼接两个字符串
    str1 = "abc"
    str2 = "def"
    str3 = str1 + str2
    str4 = f"{str1}{str2}"
    print(str3)  # abcdef
    print(str4)  # abcdef


if __name__ == "__main__":
    main()

字符串分类

python
def main():
    # 1.是否是全部大写
    print("abc".isupper())
    print("aBc".isupper())
    print("ABC".isupper())

    # 2.是否是全部小写
    print("abc".islower())
    print("aBc".islower())
    print("ABC".islower())

    # 3.是否是空白字符
    print("".isspace())  # 空字符串 False
    print(" ".isspace())  # 空格 True
    print("\t".isspace())  # 制表符 True
    print("\n".isspace())  # 换行符 True


if __name__ == "__main__":
    main()

大小写操作

python
def main():
    # 1.全部转大写
    print("abc abc".upper()) # ABC ABC

    # 2.全部转小写
    print("aBc aBc".lower()) # abc abc

    # 3.首字母大写
    print("abc abc".capitalize()) # Abc abc



if __name__ == "__main__":
    main()

填充去除操作

python
def main():
    # 去除两边空白字符(包含空格/制表符/换行)
    print(" abc\t ".strip() + "def")  # abcdef

    # 去除两边空格字符(不包括换行和tab)
    print(" abc\t ".strip(" ") + "def")  # abc\tdef

    # 去除指定的非空字符
    print("###abc@@@".strip("#@"))  # abc

    # 去除左边的指定字符
    print("###abc@@@".lstrip("#@"))  # abc@@@

    # 去除右边的指定字符
    print("###abc@@@".rstrip("#@"))  # ###abc


if __name__ == "__main__":
    main()

转写与编码格式

python
def main():
    # 字符串转二进制数据
    message = "python is a easy to learn, powerful programming language!"
    bytes_msg = message.encode("utf-8")
    print(bytes_msg)

    # 二进制数据转字符串
    bytes2 = b"python is a easy programming language!"
    print(bytes2.decode("utf-8"))

    # 字符串转unicode
    message = "你好,python"
    unicode_msg = message.encode("unicode_escape")
    print(unicode_msg)

    # unicode转字符串
    print(unicode_msg.decode("unicode_escape"))


if __name__ == "__main__":
    main()

列表

python
from types import FunctionType


def main():
    # 1.元组转列表
    lst1 = list((1, 2, 3, 4, 5))
    print(lst1)

    # 2.字符串转切割为列表
    lst2 = "hello world".split(" ")
    print(lst2)

    # 3.判断是否是列表
    if isinstance(lst2, list):
        print("lst2 is list")

    # 4.列表是否包含某个元素
    if "hello" in lst2:
        print("hello in lst2")
    if "hi" not in lst2:
        print("hi not in lst2")

    # 5.拼接两个数组
    merged = lst1 + lst2
    print(merged)

    # 6. 遍历数组
    for i in lst2:
        print(i)

    # 7. map 闭包获得新列表
    lst3 = list(map(lambda x: x * 2, lst1))
    print(lst3)

    # 8. filter 过滤获得新列表
    # lst4 = [num for num in lst1 if num % 2 == 0]
    lst4 = list(filter(lambda x: x % 2 == 0, lst1))
    print(lst4)

    # 9.查询是否有符合条件的
    def find(lst: list, handler: FunctionType):
        for item in lst:
            if handler(item):
                return item

    target1 = find([1, 2, 3, 4, 5], lambda x: x > 3)
    target2 = find([1, 2, 3, 4, 5], lambda x: x > 10)
    print(f"target1={target1}, target2={target2}")

    # 10.是否所有元素都符合条件
    def every(lst: list, handler: FunctionType):
        for item in lst:
            if not handler(item):
                return False
        return True

    every1 = every([4, 5, 6, 7, 8], lambda x: x > 3)
    every2 = every([4, 5, 6, 7, 8], lambda x: x > 5)
    print(f"every1={every1}, every2={every2}")

    # 11. 是否有一个元素符合条件
    def some(lst: list, handler: FunctionType):
        for item in lst:
            if handler(item):
                return True
        return False

    some1 = some([4, 5, 6, 7, 8], lambda x: x > 5)
    some2 = some([4, 5, 6, 7, 8], lambda x: x > 10)
    print(f"some1={some1}, some2={some2}")

    # pop/shift
    items = [1, 2, 3, 4, 5]
    pop_item = items.pop()
    shift_item = items.pop(0)
    print(f"items={items}, pop_item={pop_item}, shift_item={shift_item}")

    # unshift/push
    items = [1, 3, 5]
    items.append(7)
    print(f"items={items}")
    items.insert(0, 9)
    print(f"items={items}")

    # 排序
    origin = [9, 8, 7, 1, 3, 5]
    origin.sort()
    print(f"origin={origin}")

    # 根据集合的某一列排序
    items = [
        {
            "id": 1002,
            "name": "张三",
        },
        {
            "id": 1003,
            "name": "李四",
        },
        {
            "id": 1001,
            "name": "王五",
        },
    ]

    items.sort(key=lambda x: x["id"])
    print(items)


if __name__ == "__main__":
    main()

字典

python
def main():
    obj = {
        "id": 1001,
        "name": "张三",
    }

    # 1.是否有某个key
    if "id" in obj:
        print("有id属性")

    # 2.获取某个属性
    # 2.1 获取存在的键
    print(obj["id"])
    print(obj.get("id"))

    # 2.1 获取可能不存在的键, 使用默认值
    print(obj.get("name", "zhangsang"))
    print(obj.get("email", "zhangsang@example.com"))

    # 3.设置值
    # 3.1 使用 [] 语法
    obj["age"] = 18

    # 3.2 使用 update 方法
    obj.update(name="zhangsang")
    print(obj)

    # 4.获取所有key/value
    print(obj.keys())
    print(obj.values())

    # 5.删除值
    # 5.1 删除某个键
    del obj["age"]
    print(obj)

    # 5.2 删除所有键
    obj.clear()
    print(obj)



if __name__ == "__main__":
    main()

执行操作系统命令

python
import subprocess


def main():
    # 1.执行操作系统命令
    results = subprocess.run(["python", "--version"], capture_output=True, text=True)

    # 2.获取命令执行是否成功 returncode = 0 表示执行成功
    print(results.returncode)

    # 3.获取命令执行的输出结果
    print(results.stdout)

    # 4.执行封装的函数
    result2 = exec_os_cmd(["date", "+%Y-%m-%d"])
    print(result2)


# 4. 封装一个函数
def exec_os_cmd(cmd, **kwargs):
    """安全执行命令的简易封装"""
    kwargs.setdefault("capture_output", True)
    kwargs.setdefault("text", True)
    kwargs.setdefault("check", False)
    try:
        result = subprocess.run(cmd, **kwargs)
        return {
            "success": result.returncode == 0,
            "stdout": result.stdout.strip() if result.stdout else "",
            "stderr": result.stderr.strip() if result.stderr else "",
            "returncode": result.returncode,
        }
    except Exception as e:
        return {
            "success": False,
            "error": str(e),
        }


if __name__ == "__main__":
    main()

实现 Stack

使用列表来实现

python
class Stack:
    __items = []

    def __init__(self):
        self.__items = []

    # 栈大小
    def size(self):
        return len(self.__items)

    # 是否为空
    def is_empty(self):
        return self.size() == 0

    # 查看最后一个但是不出栈
    def peek(self):
        if not self.is_empty():
            return self.__items[self.size() - 1]

    # 压栈
    def push(self, item):
        self.__items.append(item)

    # 出栈
    def pop(self):
        return self.__items.pop()

    # 清栈
    def clear(self):
        self.__items.clear()
python
import unittest

from stack import Stack

class TestStack(unittest.TestCase):
    def test_init(self):
        s = Stack()
        self.assertTrue(isinstance(s, Stack))
        self.assertEqual(s.size(), 0)
        self.assertTrue(s.is_empty())

    def test_push(self):
        s = Stack()
        s.push(1)
        self.assertEqual(s.size(), 1)
        self.assertFalse(s.is_empty())

    def test_peek(self):
        s = Stack()
        s.push(1)
        last = s.peek()
        self.assertEqual(last, 1)

        s.push(2)
        last = s.peek()
        self.assertEqual(last, 2)

    def test_pop(self):
        s = Stack()
        s.push(1)
        s.push(2)
        s.push(3)

        last = s.pop()
        self.assertEqual(last, 3)

        last = s.pop()
        self.assertEqual(last, 2)

        last = s.pop()
        self.assertEqual(last, 1)

        self.assertTrue(s.is_empty())

    def test_clear(self):
        s = Stack()
        s.push(1)
        s.push(2)
        s.push(3)
        s.clear()
        self.assertTrue(s.is_empty())

if __name__ == "__main__":
    unittest.main()

实现 Queue

python
class Queue:
    def __init__(self):
        self.__items = []

    # 队列是否为空
    def is_empty(self):
        return self.size() == 0

    # 队列的长度
    def size(self):
        return len(self.__items)

    # 入列
    def enqueue(self, value):
        self.__items.append(value)

    # 出列
    def dequeue(self):
        return self.__items.pop(0)

    # 清除队列
    def clear(self):
        self.__items.clear()
python
import unittest

from queue import Queue


class TestQueue(unittest.TestCase):
    def test_init(self):
        q = Queue()
        self.assertTrue(isinstance(q, Queue))

    def test_enqueue(self):
        q = Queue()
        self.assertEqual(q.size(), 0)
        q.enqueue(1)
        q.enqueue(2)
        q.enqueue(3)
        self.assertEqual(q.size(), 3)

    def test_dequeue(self):
        q = Queue()
        q.enqueue(1)
        q.enqueue(2)
        self.assertEqual(q.size(), 2)

        v = q.dequeue()
        self.assertEqual(q.size(), 1)
        self.assertEqual(v, 1)

        v = q.dequeue()
        self.assertEqual(q.size(), 0)
        self.assertEqual(v, 2)

    def test_clear(self):
        q = Queue()
        q.enqueue(1)
        q.enqueue(2)
        self.assertEqual(q.size(), 2)

        q.clear()
        self.assertEqual(q.size(), 0)

    def test_is_empty(self):
        q = Queue()
        self.assertTrue(q.is_empty())

        q.enqueue(1)
        self.assertFalse(q.is_empty())

        q.dequeue()
        self.assertTrue(q.is_empty())


if __name__ == "__main__":
    unittest.main()

实现链表

  • 单链表: LinkedLis
  • 双向链表: DoublyLinkedList
  • 环形链表: CirularLinkedList
python
from typing import Self


class LinkedListNode:
    def __init__(self, value):
        self.value = value
        self.next = None

    def set_next(self, node):
        self.next = node


def is_linkedlist_node(node):
    return isinstance(node, LinkedListNode)


class LinkedList:
    def __init__(self):
        self.__head = None
        self.__tail = None
        self.__size = 0  # 添加 size 便于操作

    # 头部添加节点
    def prepend(self, value) -> Self:
        new_node = LinkedListNode(value)
        if self.__head is None:
            self.__head = new_node
            self.__tail = new_node
        else:
            new_node.next = self.__head
            self.__head = new_node
        self.__size += 1
        return self

    # 尾部添加节点
    def append(self, value) -> Self:
        new_node = LinkedListNode(value)
        if self.__tail is None:
            self.__head = new_node
            self.__tail = new_node
        else:
            self.__tail.next = new_node
            self.__tail = new_node
        self.__size += 1
        return self

    # 指定位置插入节点
    def insert(self, raw_index, value) -> Self:
        # 处理负数索引
        if raw_index < 0:
            index = self.__size + raw_index
        else:
            index = raw_index

        # 边界检查
        if index < 0 or index > self.__size:
            raise IndexError(f"Index {raw_index} out of range")

        # 头部插入
        if index == 0:
            return self.prepend(value)

        # 尾部插入
        if index == self.__size:
            return self.append(value)

        # 中间插入
        new_node = LinkedListNode(value)
        current = self.__head
        for _ in range(index - 1):
            current = current.next
        new_node.next = current.next
        current.next = new_node
        self.__size += 1
        return self

    # 移除指定值的节点(删除第一个匹配的)
    def delete(self, value):
        if self.__head is None:
            return None

        # 头部匹配
        if self.__head.value == value:
            return self.delete_head()

        current = self.__head
        while current.next:
            if current.next.value == value:
                deleted_node = current.next
                current.next = current.next.next
                if current.next is None:  # 删除的是尾节点
                    self.__tail = current
                self.__size -= 1
                return deleted_node.value
            current = current.next

        return None

    # 删除头部节点
    def delete_head(self):
        if self.__head is None:
            return None

        deleted_value = self.__head.value
        self.__head = self.__head.next
        if self.__head is None:  # 链表为空了
            self.__tail = None
        self.__size -= 1
        return deleted_value

    # 删除尾部节点
    def delete_tail(self):
        if self.__tail is None:
            return None

        if self.__head == self.__tail:  # 只有一个节点
            return self.delete_head()

        current = self.__head
        while current.next != self.__tail:
            current = current.next

        deleted_value = self.__tail.value
        current.next = None
        self.__tail = current
        self.__size -= 1
        return deleted_value

    # 根据值查找节点
    def find_value(self, value):
        current = self.__head
        index = 0
        while current:
            if current.value == value:
                return index
            current = current.next
            index += 1
        return -1

    # 根据自定义函数查找节点
    def find(self, func):
        current = self.__head
        index = 0
        while current:
            if func(current.value):
                return {"index": index, "value": current.value}
            current = current.next
            index += 1
        return None

    # 将链表转为列表
    def to_list(self):
        result = []
        current = self.__head
        while current:
            result.append(current.value)
            current = current.next
        return result

    # 将链表转为字符串
    def __str__(self):
        values = self.to_list()
        return " -> ".join(str(v) for v in values) if values else "Empty"

    # 将 list 转为链表
    @staticmethod
    def from_list(items: list):
        ll = LinkedList()
        for item in items:
            ll.append(item)
        return ll

    # 辅助方法:获取长度
    def __len__(self):
        return self.__size

    # 辅助方法:判空
    def is_empty(self):
        return self.__size == 0
python
from typing import Self, Optional, Any, Callable, Dict, List

class DoublyListNode:
    def __init__(self, value):
        self.value = value
        self.next: Optional[DoublyListNode] = None
        self.prev: Optional[DoublyListNode] = None

def is_doublylinkedlist_node(node) -> bool:
    return isinstance(node, DoublyListNode)

class DoublyLinkedList:
    def __init__(self):
        self.__head: Optional[DoublyListNode] = None
        self.__tail: Optional[DoublyListNode] = None
        self.__size = 0

    # 头部添加
    def prepend(self, value) -> Self:
        new_node = DoublyListNode(value)
        if self.__head is None:
            self.__head = self.__tail = new_node
        else:
            new_node.next = self.__head
            self.__head.prev = new_node
            self.__head = new_node
        self.__size += 1
        return self

    # 尾部添加
    def append(self, value) -> Self:
        new_node = DoublyListNode(value)
        if self.__tail is None:
            self.__head = self.__tail = new_node
        else:
            new_node.prev = self.__tail
            self.__tail.next = new_node
            self.__tail = new_node
        self.__size += 1
        return self

    # 指定位置插入(支持负索引)
    def insert(self, raw_index: int, value) -> Self:
        if raw_index < 0:
            index = self.__size + raw_index
        else:
            index = raw_index

        if index < 0 or index > self.__size:
            raise IndexError(f"Index {raw_index} out of range for size {self.__size}")

        if index == 0:
            return self.prepend(value)
        if index == self.__size:
            return self.append(value)

        # 在中间插入:找到 index-1 位置的节点(prev_node)
        prev_node = self.__head
        for _ in range(index - 1):
            prev_node = prev_node.next

        new_node = DoublyListNode(value)
        next_node = prev_node.next

        new_node.prev = prev_node
        new_node.next = next_node
        prev_node.next = new_node
        if next_node is not None:
            next_node.prev = new_node

        self.__size += 1
        return self

    # 删除第一个匹配 value 的节点
    def delete(self, value) -> Optional[Any]:
        if self.__head is None:
            return None

        # 头部匹配
        if self.__head.value == value:
            return self.delete_head()

        current = self.__head
        while current.next:
            if current.next.value == value:
                target = current.next
                # 断开 target
                current.next = target.next
                if target.next:
                    target.next.prev = current
                else:
                    self.__tail = current  # target was tail
                self.__size -= 1
                return target.value
            current = current.next
        return None

    # 删除头节点
    def delete_head(self) -> Optional[Any]:
        if self.__head is None:
            return None
        deleted_value = self.__head.value
        if self.__head == self.__tail:  # only one node
            self.__head = self.__tail = None
        else:
            self.__head = self.__head.next
            self.__head.prev = None
        self.__size -= 1
        return deleted_value

    # 删除尾节点
    def delete_tail(self) -> Optional[Any]:
        if self.__tail is None:
            return None
        if self.__head == self.__tail:
            return self.delete_head()
        deleted_value = self.__tail.value
        self.__tail = self.__tail.prev
        self.__tail.next = None
        self.__size -= 1
        return deleted_value

    # 查找值首次出现的索引
    def find_value(self, value) -> int:
        current = self.__head
        index = 0
        while current:
            if current.value == value:
                return index
            current = current.next
            index += 1
        return -1

    # 根据函数查找(返回字典)
    def find(self, func: Callable[[Any], bool]) -> Optional[Dict[str, Any]]:
        current = self.__head
        index = 0
        while current:
            if func(current.value):
                return {"index": index, "value": current.value}
            current = current.next
            index += 1
        return None

    # 转为普通 list
    def to_list(self) -> List[Any]:
        result = []
        current = self.__head
        while current:
            result.append(current.value)
            current = current.next
        return result

    # 字符串表示(正向)
    def __str__(self) -> str:
        values = self.to_list()
        return " <-> ".join(str(v) for v in values) if values else "Empty"

    # 从 list 构建
    @staticmethod
    def from_list(items: List[Any]) -> Self:
        dll = DoublyLinkedList()
        for item in items:
            dll.append(item)
        return dll

    # 长度协议
    def __len__(self) -> int:
        return self.__size

    # 判空
    def is_empty(self) -> bool:
        return self.__size == 0

    # ✨ 新增:反向遍历(可用于调试或扩展)
    def to_list_reverse(self) -> List[Any]:
        result = []
        current = self.__tail
        while current:
            result.append(current.value)
            current = current.prev
        return result
python
class CircularListNode:
    def __init__(self, value):
        self.value = value
        self.next: Optional[CircularListNode] = None

def is_circularlinkedlist_node(node) -> bool:
    return isinstance(node, CircularListNode)

class CircularLinkedList:
    def __init__(self):
        self.__head: Optional[CircularListNode] = None
        self.__tail: Optional[CircularListNode] = None  # 用于 O(1) 尾插/删
        self.__size = 0

    # 头部添加(新节点成为新 head)
    def prepend(self, value) -> Self:
        new_node = CircularListNode(value)
        if self.__head is None:
            self.__head = self.__tail = new_node
            self.__head.next = self.__head  # 自循环
        else:
            # 插入到 head 前:即 tail → new → head
            new_node.next = self.__head
            self.__tail.next = new_node
            self.__head = new_node
        self.__size += 1
        return self

    # 尾部添加(新节点成为新 tail)
    def append(self, value) -> Self:
        new_node = CircularListNode(value)
        if self.__head is None:
            self.__head = self.__tail = new_node
            self.__head.next = self.__head
        else:
            new_node.next = self.__head
            self.__tail.next = new_node
            self.__tail = new_node
        self.__size += 1
        return self

    # 指定位置插入(支持负索引)
    def insert(self, raw_index: int, value) -> Self:
        if raw_index < 0:
            index = self.__size + raw_index
        else:
            index = raw_index

        if index < 0 or index > self.__size:
            raise IndexError(f"Index {raw_index} out of range for size {self.__size}")

        if index == 0:
            return self.prepend(value)
        if index == self.__size:
            return self.append(value)

        # 找到第 index-1 个节点(prev)
        prev = self.__head
        for _ in range(index - 1):
            prev = prev.next

        new_node = CircularListNode(value)
        new_node.next = prev.next
        prev.next = new_node

        # 若插入在 tail 后(即原 tail → head),则更新 tail
        if prev == self.__tail:
            self.__tail = new_node

        self.__size += 1
        return self

    # 删除第一个匹配 value 的节点
    def delete(self, value) -> Optional[Any]:
        if self.__head is None:
            return None

        # 头部匹配
        if self.__head.value == value:
            return self.delete_head()

        current = self.__head
        while current.next != self.__head:
            if current.next.value == value:
                target = current.next
                current.next = target.next
                if target == self.__tail:
                    self.__tail = current
                self.__size -= 1
                return target.value
            current = current.next

        return None

    # 删除头节点
    def delete_head(self) -> Optional[Any]:
        if self.__head is None:
            return None

        deleted_value = self.__head.value

        if self.__head == self.__tail:  # only one node
            self.__head = self.__tail = None
        else:
            self.__tail.next = self.__head.next
            self.__head = self.__head.next

        self.__size -= 1
        return deleted_value

    # 删除尾节点
    def delete_tail(self) -> Optional[Any]:
        if self.__tail is None:
            return None
        if self.__head == self.__tail:
            return self.delete_head()

        # 找到 tail 的前驱(即 tail.prev 逻辑)
        prev = self.__head
        while prev.next != self.__tail:
            prev = prev.next

        deleted_value = self.__tail.value
        prev.next = self.__head
        self.__tail = prev
        self.__size -= 1
        return deleted_value

    # 查找值首次出现的索引(最多遍历 size 次)
    def find_value(self, value) -> int:
        if self.__head is None:
            return -1
        current = self.__head
        index = 0
        # 安全计数避免死循环(理论上不会超 size)
        for _ in range(self.__size):
            if current.value == value:
                return index
            current = current.next
            index += 1
        return -1

    # 根据函数查找
    def find(self, func: Callable[[Any], bool]) -> Optional[Dict[str, Any]]:
        if self.__head is None:
            return None
        current = self.__head
        index = 0
        for _ in range(self.__size):
            if func(current.value):
                return {"index": index, "value": current.value}
            current = current.next
            index += 1
        return None

    # 转为普通 list(正向,从 head 开始)
    def to_list(self) -> List[Any]:
        if self.__head is None:
            return []
        result = []
        current = self.__head
        for _ in range(self.__size):
            result.append(current.value)
            current = current.next
        return result

    # 字符串表示(显示循环性,加 `(circular)` 提示)
    def __str__(self) -> str:
        values = self.to_list()
        if not values:
            return "Empty (circular)"
        return " -> ".join(str(v) for v in values) + " -> (back to head)"

    # 从 list 构建
    @staticmethod
    def from_list(items: List[Any]) -> Self:
        cll = CircularLinkedList()
        for item in items:
            cll.append(item)
        return cll

    # 长度协议
    def __len__(self) -> int:
        return self.__size

    # 判空
    def is_empty(self) -> bool:
        return self.__size == 0
python
import unittest

from linkedlist import LinkedList, LinkedListNode, is_linkedlist_node


class TestLinkedList(unittest.TestCase):
    def setUp(self):
        """每个测试前创建空链表"""
        self.ll = LinkedList()

    def test_prepend(self):
        """测试头部添加"""
        self.ll.prepend(1).prepend(2).prepend(3)
        self.assertEqual(self.ll.to_list(), [3, 2, 1])
        self.assertEqual(len(self.ll), 3)

    def test_append(self):
        """测试尾部添加"""
        self.ll.append(1).append(2).append(3)
        self.assertEqual(self.ll.to_list(), [1, 2, 3])
        self.assertEqual(len(self.ll), 3)

    def test_insert(self):
        """测试指定位置插入"""
        self.ll.append(1).append(3)
        self.ll.insert(1, 2)  # 1 -> 2 -> 3
        self.assertEqual(self.ll.to_list(), [1, 2, 3])

        # 头部插入
        self.ll.insert(0, 0)
        self.assertEqual(self.ll.to_list()[0], 0)

        # 尾部插入
        self.ll.insert(4, 4)
        self.assertEqual(self.ll.to_list(), [0, 1, 2, 3, 4])

    def test_insert_negative_index(self):
        """测试负数索引插入"""
        self.ll.append(1).append(2).append(3)
        self.ll.insert(-1, 99)  # 在倒数第1个位置插入(即索引2)
        self.assertEqual(self.ll.to_list(), [1, 2, 99, 3])

    def test_insert_out_of_range(self):
        """测试越界插入"""
        with self.assertRaises(IndexError):
            self.ll.insert(5, 1)

    def test_delete(self):
        """测试删除指定值"""
        self.ll.append(1).append(2).append(3).append(2)

        # 删除中间的2
        self.assertEqual(self.ll.delete(2), 2)
        self.assertEqual(self.ll.to_list(), [1, 3, 2])

        # 删除不存在的值
        self.assertIsNone(self.ll.delete(999))

    def test_delete_head(self):
        """测试删除头部"""
        self.ll.append(1).append(2).append(3)
        self.assertEqual(self.ll.delete_head(), 1)
        self.assertEqual(self.ll.to_list(), [2, 3])

        # 空链表
        empty = LinkedList()
        self.assertIsNone(empty.delete_head())

    def test_delete_tail(self):
        """测试删除尾部"""
        self.ll.append(1).append(2).append(3)
        self.assertEqual(self.ll.delete_tail(), 3)
        self.assertEqual(self.ll.to_list(), [1, 2])

        # 只剩一个节点
        single = LinkedList()
        single.append(1)
        self.assertEqual(single.delete_tail(), 1)
        self.assertTrue(single.is_empty())

    def test_find_value(self):
        """测试按值查找索引"""
        self.ll.append(10).append(20).append(30)
        self.assertEqual(self.ll.find_value(20), 1)
        self.assertEqual(self.ll.find_value(999), -1)

    def test_find_with_function(self):
        """测试按自定义函数查找"""
        self.ll.append(1).append(2).append(3).append(4)

        # 查找第一个偶数
        result = self.ll.find(lambda x: x % 2 == 0)
        self.assertEqual(result, {"index": 1, "value": 2})

        # 查找不存在的
        result = self.ll.find(lambda x: x > 10)
        self.assertIsNone(result)

    def test_to_list(self):
        """测试转为列表"""
        self.assertEqual(self.ll.to_list(), [])
        self.ll.append(1).append(2)
        self.assertEqual(self.ll.to_list(), [1, 2])

    def test_str(self):
        """测试字符串表示"""
        self.assertEqual(str(self.ll), "Empty")
        self.ll.append(1).append(2)
        self.assertEqual(str(self.ll), "1 -> 2")

    def test_from_list(self):
        """测试从列表创建链表"""
        ll = LinkedList.from_list([1, 2, 3, 4])
        self.assertEqual(ll.to_list(), [1, 2, 3, 4])

    def test_chained_operations(self):
        """测试链式调用"""
        result = self.ll.append(1).append(2).prepend(0).insert(2, 1.5)
        self.assertEqual(result.to_list(), [0, 1, 1.5, 2])

    def test_is_linkedlist_node(self):
        """测试节点类型检查"""
        node = LinkedListNode(1)
        self.assertTrue(is_linkedlist_node(node))
        self.assertFalse(is_linkedlist_node(123))
        self.assertFalse(is_linkedlist_node(None))


if __name__ == "__main__":
    # 运行测试
    unittest.main(verbosity=2)

实现一个备份脚本

  • 配置需要备份的目录路径/保存备份的目记录
  • 配置备份文件名的前缀/后缀
  • 配置文件的个数(只保留n个)
python
import os
import pathlib
import tarfile
from datetime import datetime

# 配置
source_path = pathlib.Path("./python-demo")
target_path = pathlib.Path("./backup-path")
max_backup_file_count = 7
bakfname_prefix = "backup"
bakfname_suffix = ".tar.gz"
bakfname_seperator = "__"


# 备份压缩包
def backup_archive():
    try:
        output_archive = target_path.joinpath(generate_bakfname())
        with tarfile.open(output_archive, "w:gz") as tar:
            with os.scandir(source_path) as entries:
                for item in entries:
                    # ingore hidden files
                    if item.name.startswith("."):
                        continue

                    # add item to tar
                    tar.add(item.path, arcname=item.name)
                print(f"[archive completed]: {output_archive}")
    except Exception as e:
        print(f"打包过程中发生错误: {e}")


# 生成备份文件名
def generate_bakfname() -> str:
    now = datetime.now()
    date = now.date()
    timestamp = int(now.timestamp())
    return bakfname_seperator.join(
        [
            bakfname_prefix,
            date,
            str(timestamp),
            bakfname_suffix,
        ]
    )


# 解析备份文件名
def parse_bakfname_with_order(bakfname: str):
    parts = bakfname.split(bakfname_seperator)
    if len(parts) >= 2 and parts[2]:
        return {
            "order": int(parts[2]),
            "filename": bakfname,
        }


# 检查已有的备份文件数量
def count_target_files() -> int:
    backup_file_count = 0
    with os.scandir(target_path) as entries:
        for entry in entries:
            if entry.name.endswith(bakfname_suffix):
                backup_file_count += 1
    return backup_file_count


# 清理已有的备份文件(最多保存七个备份)
def clear_target_files() -> None:
    bakfname_with_order_items = []
    with os.scandir(target_path) as entries:
        for entry in entries:
            result = parse_bakfname_with_order(entry.name)
            if result:
                bakfname_with_order_items.append(result)

    # sort by order
    bakfname_with_order_items.sort(key=lambda x: x["order"])
    delete_item = bakfname_with_order_items[0]
    delete_item_path = target_path.joinpath(delete_item["filename"])
    delete_item_path.unlink(missing_ok=True)


# 初始化路径
def init_paths():
    if not source_path.exists():
        raise FileNotFoundError("[init]source_path is not exist")

    if not target_path.exists():
        os.makedirs(target_path)


# main
def main():
    init_paths()
    if count_target_files() >= max_backup_file_count:
        clear_target_files()
    backup_archive()


if __name__ == "__main__":
    main()

Released under the MIT License.