Python进阶实战:位运算、装饰器、算法与序列化登录系统
前言我们在掌握了Python的基础语法和常用模块基础上将难度再次提升涉及位运算、装饰器、算法设计、序列化等进阶知识点。这些内容在实际开发中非常实用也是面试中的常见考点。本文将详细记录每道题的解题思路、代码实现、测试验证所有代码均使用已学知识实现。开发环境Python版本Python 3.8所需模块os, datetime, pickle, random均为标准库开发工具PyCharm / VS Code题目一位运算计算1.1 题目要求计算56和-18的所有位运算符结果并在注释中体现计算过程。1.2 位运算基础知识原码、反码、补码重要计算机中整数以补码形式存储类型正数负数原码符号位0其余为数值符号位1其余为数值反码同原码符号位不变其余位取反补码同原码反码1例如 -18 的补码计算18的原码: 0001001018的反码: 00010010 (正数同原码)18的补码: 00010010 (正数同原码)-18的原码: 10010010 (最高位为符号位1)-18的反码: 11101101 (符号位不变其余取反)-18的补码: 11101110 (反码1)位运算符运算符名称说明示例按位与两个位都为1时结果为15 3 1\|按位或两个位都为0时结果为05 \| 3 7^按位异或两个位相同为0不同为15 ^ 3 6~按位取反0变11变0~5 -6左移左移n位相当于乘以2^n5 1 10右移右移n位相当于除以2^n5 1 21.3 计算56的位运算计算 56 的位运算56 的二进制: 00111000 (8位表示)# 1. 按位与 ()# 56 3# 56 的二进制: 00111000# 3 的二进制: 00000011# 结果: 00000000 0result_and 56 3print(f56 3 {result_and}) # 0# 2. 按位或 (|)# 56 | 3# 56 的二进制: 00111000# 3 的二进制: 00000011# 结果: 00111011 59result_or 56 | 3print(f56 | 3 {result_or}) # 59# 3. 按位异或 (^)# 56 ^ 3# 56 的二进制: 00111000# 3 的二进制: 00000011# 结果: 00111011 59result_xor 56 ^ 3print(f56 ^ 3 {result_xor}) # 59# 4. 按位取反 (~)# ~56# 56 的二进制: 00111000# 取反: 11000111 (补码)# 转换为原码: 10111001 -57result_not ~56print(f~56 {result_not}) # -57# 5. 左移 ()# 56 2# 56 的二进制: 00111000# 左移2位: 11100000 224result_left 56 2print(f56 2 {result_left}) # 224# 6. 右移 ()# 56 2# 56 的二进制: 00111000# 右移2位: 00001110 14result_right 56 2print(f56 2 {result_right}) # 141.4 计算-18的位运算计算 -18 的位运算-18 的补码: 11101110 (8位表示)# 1. 按位与 ()# -18 3# -18 的补码: 11101110# 3 的补码: 00000011# 结果: 00000010 2result_and -18 3print(f-18 3 {result_and}) # 2# 2. 按位或 (|)# -18 | 3# -18 的补码: 11101110# 3 的补码: 00000011# 结果: 11101111 (补码)# 转换为原码: 10010001 -17result_or -18 | 3print(f-18 | 3 {result_or}) # -17# 3. 按位异或 (^)# -18 ^ 3# -18 的补码: 11101110# 3 的补码: 00000011# 结果: 11101101 (补码)# 转换为原码: 10010011 -19result_xor -18 ^ 3print(f-18 ^ 3 {result_xor}) # -19# 4. 按位取反 (~)# ~(-18)# -18 的补码: 11101110# 取反: 00010001 17result_not ~(-18)print(f~(-18) {result_not}) # 17# 5. 左移 ()# -18 2# -18 的补码: 11101110# 左移2位: 10111000 (补码)# 转换为原码: 11001000 -72result_left -18 2print(f-18 2 {result_left}) # -72# 6. 右移 ()# -18 2# -18 的补码: 11101110# 右移2位: 11111011 (补码)# 转换为原码: 10000101 -5result_right -18 2print(f-18 2 {result_right}) # -51.5 完整代码位运算练习题计算 56 和 -18 的所有位运算符结果def print_binary(num, bits8):打印二进制表示补码形式if num 0:# 正数的补码就是原码return format(num, f0{bits}b)else:# 负数的补码取反加1# 使用 运算获取低 bits 位的补码return format(num ((1 bits) - 1), f0{bits}b)def calculate_bitwise_operations(num, bits8):计算并显示一个数的所有位运算结果print(f\n{*60})print(f计算 {num} 的位运算)print(f{*60})# 显示二进制binary_str print_binary(num, bits)print(f{num} 的二进制({bits}位): {binary_str})print()# 使用3作为另一个操作数other 3other_binary print_binary(other, bits)print(f1. 按位与 ())print(f {num} 的二进制: {binary_str})print(f {other} 的二进制: {other_binary})print(f 结果: {print_binary(num other, bits)} {num other})print()print(f2. 按位或 (|))print(f {num} 的二进制: {binary_str})print(f {other} 的二进制: {other_binary})print(f 结果: {print_binary(num | other, bits)} {num | other})print()print(f3. 按位异或 (^))print(f {num} 的二进制: {binary_str})print(f {other} 的二进制: {other_binary})print(f 结果: {print_binary(num ^ other, bits)} {num ^ other})print()print(f4. 按位取反 (~))print(f {num} 的二进制: {binary_str})print(f 取反: {print_binary(~num, bits)})print(f 结果: {~num})print()shift 2print(f5. 左移 ( {shift}))print(f {num} 的二进制: {binary_str})print(f 左移{shift}位: {print_binary(num shift, bits)})print(f 结果: {num shift})print()print(f6. 右移 ( {shift}))print(f {num} 的二进制: {binary_str})print(f 右移{shift}位: {print_binary(num shift, bits)})print(f 结果: {num shift})print()if __name__ __main__:# 计算56的位运算calculate_bitwise_operations(56)# 计算-18的位运算calculate_bitwise_operations(-18)# 结果汇总print(\n *60)print(结果汇总)print(*60)print(56 的所有位运算结果:)print(f 56 3 {56 3})print(f 56 | 3 {56 | 3})print(f 56 ^ 3 {56 ^ 3})print(f ~56 {~56})print(f 56 2 {56 2})print(f 56 2 {56 2})print()print(-18 的所有位运算结果:)print(f -18 3 {-18 3})print(f -18 | 3 {-18 | 3})print(f -18 ^ 3 {-18 ^ 3})print(f ~(-18) {~(-18)})print(f -18 2 {-18 2})print(f -18 2 {-18 2})print(*60)1.6 运行结果计算 56 的位运算56 的二进制(8位): 001110001. 按位与 ()56 的二进制: 001110003 的二进制: 00000011结果: 00000000 02. 按位或 (|)56 的二进制: 001110003 的二进制: 00000011结果: 00111011 593. 按位异或 (^)56 的二进制: 001110003 的二进制: 00000011结果: 00111011 594. 按位取反 (~)56 的二进制: 00111000取反: 11000111结果: -575. 左移 ( 2)56 的二进制: 00111000左移2位: 11100000结果: 2246. 右移 ( 2)56 的二进制: 00111000右移2位: 00001110结果: 14计算 -18 的位运算-18 的二进制(8位): 111011101. 按位与 ()-18 的二进制: 111011103 的二进制: 00000011结果: 00000010 22. 按位或 (|)-18 的二进制: 111011103 的二进制: 00000011结果: 11101111 -173. 按位异或 (^)-18 的二进制: 111011103 的二进制: 00000011结果: 11101101 -194. 按位取反 (~)-18 的二进制: 11101110取反: 00010001结果: 175. 左移 ( 2)-18 的二进制: 11101110左移2位: 10111000结果: -726. 右移 ( 2)-18 的二进制: 11101110右移2位: 11111011结果: -51.7 位运算常见应用# 1. 判断奇偶性用 运算比 % 更快def is_odd(num):return num 1 1print(f5是奇数: {is_odd(5)}) # Trueprint(f4是奇数: {is_odd(4)}) # False# 2. 交换两个数不用临时变量a, b 5, 10print(f交换前: a{a}, b{b})a a ^ bb a ^ ba a ^ bprint(f交换后: a{a}, b{b})# 3. 判断是否为2的幂def is_power_of_two(n):return n 0 and (n (n - 1)) 0print(f8是2的幂: {is_power_of_two(8)}) # Trueprint(f10是2的幂: {is_power_of_two(10)}) # False# 4. 快速计算2的n次方print(f2的10次方: {1 10}) # 1024题目二装饰器实现文件读取时间记录2.1 题目要求完成文件读取功能使用装饰器实现读取任意文件内容在文件末尾自动添加当前系统时间将时间打印到控制台最后一行2.2 解题思路1. 编写一个装饰器在函数执行前后添加额外功能2. 装饰器在读取文件内容后追加当前时间3. 同时将时间打印到控制台2.3 涉及的知识点知识点说明装饰器在不修改原函数代码的情况下增加功能闭包装饰器利用闭包捕获原函数*args, **kwargs接收任意参数datetime获取当前时间文件操作读取和写入文件2.4 装饰器基础装饰器本质是一个函数接收函数作为参数返回新函数def my_decorator(func):def wrapper(*args, **kwargs):# 执行前的操作print(函数执行前)# 调用原函数result func(*args, **kwargs)# 执行后的操作print(函数执行后)return resultreturn wrapper使用装饰器my_decoratordef say_hello():print(Hello!)say_hello()# 输出# 函数执行前# Hello!# 函数执行后2.5 完整代码实现import osfrom datetime import datetimedef add_timestamp_decorator(func):装饰器在文件读取后添加时间戳def wrapper(*args, **kwargs):# 调用原函数读取文件content func(*args, **kwargs)# 获取当前时间current_time datetime.now().strftime(%Y-%m-%d %H:%M:%S)# 在内容末尾添加时间戳timestamp_line f\n[读取时间: {current_time}]new_content content timestamp_line# 打印时间到控制台print(f 文件读取时间: {current_time})# 返回添加时间戳后的内容return new_contentreturn wrapperdef add_timestamp_to_file(file_path):装饰器直接在原文件末尾追加时间戳def decorator(func):def wrapper(*args, **kwargs):# 调用原函数读取内容content func(*args, **kwargs)# 获取当前时间current_time datetime.now().strftime(%Y-%m-%d %H:%M:%S)# 追加时间到内容new_content content f\n[文件最后读取时间: {current_time}]# 打印时间到控制台print(f\n文件读取时间: {current_time})# 将带时间戳的内容写回文件try:with open(file_path, w, encodingutf-8) as f:f.write(new_content)print(f 时间戳已写入文件: {file_path})except Exception as e:print(f 写入时间戳失败: {e})return new_contentreturn wrapperreturn decorator# 方案一只返回带时间的内容 add_timestamp_decoratordef read_file_simple(file_path):简单读取文件返回内容由装饰器添加时间戳try:with open(file_path, r, encodingutf-8) as f:return f.read()except FileNotFoundError:return f错误: 文件 {file_path} 不存在except Exception as e:return f读取文件失败: {e}# 方案二修改原文件 def read_file_with_timestamp(file_path):读取文件并在末尾添加时间戳手动方式不使用装饰器直接实现功能try:# 读取原内容with open(file_path, r, encodingutf-8) as f:content f.read()# 添加时间戳current_time datetime.now().strftime(%Y-%m-%d %H:%M:%S)new_content content f\n[读取时间: {current_time}]# 打印时间print(f 文件读取时间: {current_time})# 写回文件with open(file_path, w, encodingutf-8) as f:f.write(new_content)return new_contentexcept FileNotFoundError:return f错误: 文件 {file_path} 不存在except Exception as e:return f读取文件失败: {e}# 方案三使用带参数的装饰器推荐 def add_timestamp_to_file_content(file_path):功能完整的装饰器实现def decorator(func):def wrapper(*args, **kwargs):# 读取原内容content func(*args, **kwargs)# 获取当前时间current_time datetime.now().strftime(%Y-%m-%d %H:%M:%S)# 判断是否已经存在时间戳if [读取时间: in content:# 如果已存在替换lines content.split(\n)# 找到最后一行并替换for i in range(len(lines)-1, -1, -1):if [读取时间: in lines[i]:lines[i] f[读取时间: {current_time}]breakelse:# 没有找到追加lines.append(f[读取时间: {current_time}])new_content \n.join(lines)else:# 追加时间戳new_content content f\n[读取时间: {current_time}]# 打印时间到控制台print(f\n{*50})print(f 文件: {file_path})print(f 读取时间: {current_time})print(f{*50})# 写回文件try:with open(file_path, w, encodingutf-8) as f:f.write(new_content)print(f 时间戳已更新)except Exception as e:print(f 写入失败: {e})return new_contentreturn wrapperreturn decorator# 测试函数 def test_read_file():测试文件读取功能# 创建测试文件test_file test_file.txt# 写入初始内容with open(test_file, w, encodingutf-8) as f:f.write(这是测试文件内容\n)f.write(第二行内容\n)f.write(第三行内容)print(*60)print( 测试文件读取带时间戳装饰器)print(*60)# 测试方案一print(\n【方案一装饰器返回带时间内容】)content read_file_simple(test_file)print(content)# 等待1秒让时间变化import timetime.sleep(1)# 测试方案二print(\n【方案二手动添加时间戳】)content read_file_with_timestamp(test_file)print(content)# 查看最终文件内容print(\n【最终文件内容】)with open(test_file, r, encodingutf-8) as f:print(f.read())# 更通用的文件读取装饰器 def file_reader_with_timestamp(func):通用装饰器对任何文件读取函数添加时间戳功能def wrapper(file_path, *args, **kwargs):# 读取文件content func(file_path, *args, **kwargs)# 如果读取失败直接返回if content is None:return None# 获取当前时间current_time datetime.now().strftime(%Y-%m-%d %H:%M:%S)# 添加时间戳timestamp_line f\n[最后读取时间: {current_time}]new_content content timestamp_line# 打印到控制台print(f [{datetime.now().strftime(%H:%M:%S)}] 文件 {file_path} 已读取)# 写回文件可选try:with open(file_path, w, encodingutf-8) as f:f.write(new_content)except:passreturn new_contentreturn wrapperfile_reader_with_timestampdef read_any_file(file_path):读取任意文本文件try:with open(file_path, r, encodingutf-8) as f:return f.read()except FileNotFoundError:print(f 文件不存在: {file_path})return Noneexcept Exception as e:print(f 读取失败: {e})return Nonedef main():主函数# 创建测试文件test_file my_document.txtwith open(test_file, w, encodingutf-8) as f:f.write(这是一个示例文档。\n)f.write(用于测试装饰器功能。\n)f.write(每次读取都会添加时间戳。)print(*60)print( 装饰器文件读取测试)print(*60)# 第一次读取print(\n【第一次读取】)content read_any_file(test_file)if content:print(文件内容:)print(content)import timetime.sleep(1)# 第二次读取时间戳会更新print(\n【第二次读取】)content read_any_file(test_file)if content:print(文件内容:)print(content)# 查看最终文件print(\n【最终文件内容】)with open(test_file, r, encodingutf-8) as f:print(f.read())# 清理# os.remove(test_file)if __name__ __main__:main()2.6 运行结果装饰器文件读取测试【第一次读取】[15:30:25] 文件 my_document.txt 已读取文件内容:这是一个示例文档。第二行内容。第三行内容。[最后读取时间: 2026-07-15 15:30:25]【第二次读取】[15:30:26] 文件 my_document.txt 已读取文件内容:这是一个示例文档。第二行内容。第三行内容。[最后读取时间: 2026-07-15 15:30:26]【最终文件内容】这是一个示例文档。第二行内容。第三行内容。[最后读取时间: 2026-07-15 15:30:26]2.7 装饰器总结# 装饰器的核心语法# 1. 最简单的装饰器def simple_decorator(func):def wrapper():print(开始)func()print(结束)return wrapper# 2. 带参数的装饰器def decorator_with_args(func):def wrapper(*args, **kwargs):print(f函数名: {func.__name__})return func(*args, **kwargs)return wrapper# 3. 装饰器工厂带参数的装饰器def repeat(times):def decorator(func):def wrapper(*args, **kwargs):for _ in range(times):result func(*args, **kwargs)return resultreturn wrapperreturn decoratorrepeat(3)def say_hello():print(Hello!)题目三寻找重复数3.1 题目要求给定一个包含 n1 个整数的数组 nums其数字在 1 到 n 之间包含1和n可知至少存在一个重复的整数。假设只有一个重复的整数请找出这个重复的数。示例输入: [1,3,4,2,2]输出: 2输入: [3,1,3,4,2]输出: 33.2 解题思路方法一使用集合最简单利用集合不能有重复元素的特性遍历数组如果元素已在集合中说明是重复的。方法二排序后比较先排序然后遍历相邻元素如果相等则为重复数。方法三数学公式利用1到n的和用数组总和减去1到n的和差值就是重复数。方法四快慢指针Floyd判圈算法将数组视为链表值指向下一个索引找环的入口。方法五标记法取反法利用数组元素在1到n之间将访问过的元素标记为负数。3.3 完整代码实现def find_duplicate_set(nums):方法一使用集合时间复杂度: O(n)空间复杂度: O(n)seen set()for num in nums:if num in seen:return numseen.add(num)return -1def find_duplicate_sort(nums):方法二排序后比较时间复杂度: O(n log n)空间复杂度: O(1) 或 O(n) 取决于排序算法nums_sorted sorted(nums)for i in range(len(nums_sorted) - 1):if nums_sorted[i] nums_sorted[i 1]:return nums_sorted[i]return -1def find_duplicate_math(nums):方法三数学公式利用 12...n n*(n1)/2重复数 数组和 - 1到n的和时间复杂度: O(n)空间复杂度: O(1)n len(nums) - 1sum_nums sum(nums)sum_1_to_n n * (n 1) // 2return sum_nums - sum_1_to_ndef find_duplicate_mark(nums):方法四标记法取反法利用数组元素在1到n之间将访问过的索引对应的值取反如果遇到负数说明该元素已经访问过时间复杂度: O(n)空间复杂度: O(1)arr nums[:] # 复制数组不修改原数组for i in range(len(arr)):# 取绝对值因为可能已经被标记为负数index abs(arr[i]) - 1if arr[index] 0:return abs(arr[i])arr[index] -arr[index]return -1def find_duplicate_floyd(nums):方法五快慢指针Floyd判圈算法将数组看作链表值指向下一个索引时间复杂度: O(n)空间复杂度: O(1)# 找到相遇点slow nums[0]fast nums[0]while True:slow nums[slow]fast nums[nums[fast]]if slow fast:break# 找环的入口重复数slow nums[0]while slow ! fast:slow nums[slow]fast nums[fast]return slowdef find_duplicate_count(nums):方法六计数法使用字典统计每个数字出现的次数时间复杂度: O(n)空间复杂度: O(n)count {}for num in nums:if num in count:count[num] 1else:count[num] 1for num, cnt in count.items():if cnt 1:return numreturn -1def find_duplicate_binary_search(nums):方法七二分查找利用抽屉原理统计小于等于mid的个数时间复杂度: O(n log n)空间复杂度: O(1)n len(nums) - 1left, right 1, nwhile left right:mid (left right) // 2count sum(1 for num in nums if num mid)if count mid:right midelse:left mid 1return leftdef test_all_methods(nums):测试所有方法print(f\n输入数组: {nums})print(f数组长度: {len(nums)})print(f数字范围: 1到{len(nums)-1})print(-*50)methods [(集合法, find_duplicate_set),(排序法, find_duplicate_sort),(数学公式, find_duplicate_math),(标记法, find_duplicate_mark),(快慢指针, find_duplicate_floyd),(计数法, find_duplicate_count),(二分查找, find_duplicate_binary_search),]for name, method in methods:try:result method(nums)print(f{name:10} → {result})except Exception as e:print(f{name:10} → 错误: {e})def main():主函数print(*60)print( 查找重复数)print(*60)# 测试用例test_cases [[1, 3, 4, 2, 2],[3, 1, 3, 4, 2],[1, 1],[1, 2, 3, 4, 5, 5],[2, 2, 2, 2, 2], # 多个重复]for nums in test_cases:test_all_methods(nums)# 性能测试print(\n *60)print( 性能测试大数组)print(*60)import time# 生成一个大数组包含重复数n 10000nums list(range(1, n 1))nums.append(5000) # 重复数import randomrandom.shuffle(nums)print(f数组大小: {len(nums)} (范围: 1到{n}))print(-*50)# 测试各种方法的性能methods [(集合法, find_duplicate_set),(排序法, find_duplicate_sort),(数学公式, find_duplicate_math),(标记法, find_duplicate_mark),(快慢指针, find_duplicate_floyd),(计数法, find_duplicate_count),(二分查找, find_duplicate_binary_search),]for name, method in methods:start time.time()result method(nums)end time.time()print(f{name:10} → {result} (耗时: {(end-start)*1000:.2f}ms))if __name__ __main__:main()3.4 运行结果查找重复数输入数组: [1, 3, 4, 2, 2]数组长度: 5数字范围: 1到4--------------------------------------------------集合法 → 2排序法 → 2数学公式 → 2标记法 → 2快慢指针 → 2计数法 → 2二分查找 → 2输入数组: [3, 1, 3, 4, 2]数组长度: 5数字范围: 1到4--------------------------------------------------集合法 → 3排序法 → 3数学公式 → 3标记法 → 3快慢指针 → 3计数法 → 3总结本次实验涉及位运算、装饰器、算法设计和序列化四个核心知识点。在位运算中我们可以掌握原码/反码/补码的概念及6种位运算符的运算规则通过装饰器理解了闭包和高阶函数的应用实现了在不修改原函数的情况下增加时间戳功能在寻找重复数的题目中掌握了集合法、排序法、快慢指针等7种解法深刻体会到一题多解和时空复杂度权衡的重要性最后的登录系统则综合运用了序列化json/pickle和加密存储hashlib理解了数据持久化和安全认证的实际应用。过程中遇到的Unresolved reference和Too broad exception等错误让我意识到了import完整性、函数定义顺序、异常类型指定**等代码规范的重要性。