练习
折纸问题
一张厚度为N层的纸,每次对折都会使厚度变为原来的2倍,问第多少次对折后,其高度超过珠穆朗玛峰的高度(8848.86米)
题解
def fold_paper(thickness, height):
count = 0
while thickness < height:
thickness *= 2
count += 1
return count
print(fold_paper(0.0001, 8848.86))
打印时间
描述
请从00:00依次打印出一天的时间 示例:
- 23 : 52
- 23 : 53
- 23 : 54
题解
for 时钟 in range(24):
for 分钟 in range(60):
print(时钟, ':', 分钟)
九九乘法表
描述
要求使用循环代码打印一个九九乘法表出来.如下
1*1=1
1*2=2 2*2=4
1*3=3 2*3=6 3*3=9
1*4=4 2*4=8 3*4=12 4*4=16
1*5=5 2*5=10 3*5=15 4*5=20 5*5=25
1*6=6 2*6=12 3*6=18 4*6=24 5*6=30 6*6=36
1*7=7 2*7=14 3*7=21 4*7=28 5*7=35 6*7=42 7*7=49
1*8=8 2*8=16 3*8=24 4*8=32 5*8=40 6*8=48 7*8=56 8*8=64
1*9=9 2*9=18 3*9=27 4*9=36 5*9=45 6*9=54 7*9=63 8*9=72 9*9=81
题解
#方法一
for i in range(1,10):
print()
for j in range(1,i+1):
print('%d*%d=%d' % (j,i,i*j),end=' ')
#方法二
i=1
while i<10: #控制行,1到9
j=1
while j <= i: #控制每行显示的数量,1到9
print("%d*%d=%d"%(j,i,i*j),end=' ') #输出
j+=1 #每行显示的数量加1
print("\n") #每一行结束换行
i+=1 #行数加1
复利的力量
小明从2026年1月1日每日定投100元,年化收益率为10%,考虑到交易日(仅排除周末),问小明在第几年可以实现100万的目标?
题解
from datetime import datetime, timedelta
import calendar
# 计算参数
daily_investment = 100 # 每日定投金额(元)
annual_return_rate = 0.10 # 年化收益率 10%
target_amount = 1000000 # 目标金额 100万元
# 起始日期:2026年1月1日
start_date = datetime(2026, 1, 1)
current_date = start_date
def count_trading_days(year):
"""统计某一年内的交易日数(仅排除周末)"""
days = 0
date_cursor = datetime(year, 1, 1)
end_date = datetime(year + 1, 1, 1)
while date_cursor < end_date:
if date_cursor.weekday() < 5:
days += 1
date_cursor += timedelta(days=1)
return days
trading_days_in_year = count_trading_days(start_date.year)
daily_return_rate = (1 + annual_return_rate) ** (1 / trading_days_in_year) - 1
# 模拟每日定投
total_amount = 0 # 当前总金额
trading_day_count = 0 # 交易日计数
current_year = start_date.year
trading_days_this_year = 0 # 当前年度的交易日数
last_year_end_amount = 0 # 上一年结束时的金额
last_year_trading_days = 0 # 上一年的交易日数
while total_amount < target_amount:
# 检查是否是工作日(周一到周五,weekday()返回0-6,0是周一,6是周日)
weekday = current_date.weekday() # 0=周一, 1=周二, ..., 4=周五, 5=周六, 6=周日
# 检查是否跨年(在处理交易日之前)
if current_date.year > current_year:
year_num = current_year - start_date.year + 1
print(f"第 {year_num} 年({current_year} 年)结束时,总金额: {last_year_end_amount:,.2f} 元,交易日数: {last_year_trading_days} 天")
current_year = current_date.year
trading_days_this_year = 0
last_year_end_amount = total_amount
last_year_trading_days = 0
trading_days_in_year = count_trading_days(current_year)
daily_return_rate = (1 + annual_return_rate) ** (1 / trading_days_in_year) - 1
if weekday < 5: # 周一到周五(0-4)是交易日
trading_day_count += 1
trading_days_this_year += 1
last_year_trading_days = trading_days_this_year
# 先计算昨日收益(复利)
total_amount = total_amount * (1 + daily_return_rate)
# 然后投入今日的定投金额
total_amount += daily_investment
last_year_end_amount = total_amount
# 检查是否达到目标(在交易日达到目标)
if total_amount >= target_amount:
break
# 移动到下一天
current_date += timedelta(days=1)
# 打印最后一年(如 果还没打印)
if trading_days_this_year > 0:
year_num = current_year - start_date.year + 1
print(f"第 {year_num} 年({current_year} 年)结束时,总金额: {last_year_end_amount:,.2f} 元,交易日数: {trading_days_this_year} 天")
# 计算达到目标的实际日期
# 由于在交易日达到目标后立即break,current_date就是达到目标的日期
actual_date = current_date
year_num = actual_date.year - start_date.year + 1
print("-" * 60)
print("\n最终结果:")
print("=" * 60)
print(f"达到目标日期: {actual_date.strftime('%Y年%m月%d日')} ({calendar.day_name[actual_date.weekday()]})")
print(f"达到目标时间: 第 {year_num} 年({actual_date.year} 年)的第 {trading_days_this_year} 个交易日")
print(f"总金额: {total_amount:,.2f} 元")
print(f"累计交易日数: {trading_day_count} 天")
print(f"累计投资本金: {trading_day_count * daily_investment:,.2f} 元")
print(f"累计收益: {total_amount - trading_day_count * daily_investment:,.2f} 元")
print(f"收益率: {(total_amount - trading_day_count * daily_investment) / (trading_day_count * daily_investment) * 100:.2f}%")
print(f"实际经过天数: {(actual_date - start_date).days + 1} 天")
print("=" * 60)
字典排序
描述
将字典数组按字典的某个key排序
题解
# 方法1:
sorted(d.cop(),key = lambda i:i[k])
# 方法2:
heappush(h,(i[k],i)) for i in d
单例模式
单例模式目的:让类创建的对象,在系统中只有 唯一的一个实例
特点:每一次执行 类名() 返回的对象,内存地址是相同的
方法一:使用 __new__ 方法
基本实现
class MusicPlayer(object):
# 记录第一个被创建对象的引用
instance = None
def __new__(cls, *args, **kwargs):
# 1. 判断类属性是否为空对象
if cls.instance is None:
# 调用父类方法为第一个对象分配空间
cls.instance = super().__new__(cls)
# 2. 返回类属性保存的对象引用
return cls.instance
# 测试单例效果
player1 = MusicPlayer()
player2 = MusicPlayer()
print(f"player1: {player1}")
print(f"player2: {player2}")
print(f"是否为同一对象: {player1 is player2}") # True
优化:只初始化一次
上述实现中,虽然返回的是同一 个对象,但 __init__ 方法会被多次调用
class MusicPlayer(object):
# 记录第一个被创建对象的引用
instance = None
# 记录初始化执行状态
init_flag = False
def __new__(cls, *args, **kwargs):
if cls.instance is None:
cls.instance = super().__new__(cls)
return cls.instance
def __init__(self):
# 1. 判断是否执行过初始化动作
if MusicPlayer.init_flag:
return
# 2. 如果没有执行过就执行初始化动作
print("播放器初始化")
# 3. 修改初始化状态
MusicPlayer.init_flag = True
# 测试
player1 = MusicPlayer() # 输出: 播放器初始化
player2 = MusicPlayer() # 不会再次初始化
方法二:使用装 饰器
def singleton(cls):
"""单例装饰器"""
instances = {}
def get_instance(*args, **kwargs):
if cls not in instances:
instances[cls] = cls(*args, **kwargs)
return instances[cls]
return get_instance
@singleton
class MySingleton:
def __init__(self, param):
self.param = param
print(f"初始化参数: {param}")
# 使用示例
if __name__ == '__main__':
a = MySingleton(10) # 输出: 初始化参数: 10
b = MySingleton(20) # 不会再次初始化
print(f"a.param: {a.param}") # 10
print(f"b.param: {b.param}") # 10
print(f"是否为同一对象: {a is b}") # True
- 代码简洁优雅
- 可重用性强
- 不需要修改原类的内部结构
方法三:使用类方法
class Singleton(object):
def __init__(self, name):
self.name = name
@classmethod
def instance(cls, *args, **kwargs):
if not hasattr(cls, "_instance"):
cls._instance = cls(*args, **kwargs)
return cls._instance
# 使用示例
single_1 = Singleton.instance('第1次创建')
single_2 = Singleton.instance('第2次创建')
print(f"single_1.name: {single_1.name}") # 第1次创建
print(f"single_2.name: {single_2.name}") # 第1次创建
print(f"是否为同一对象: {single_1 is single_2}") # True
线程安全版本
上述实现在多线程环境下不安全,需要加锁保护
from threading import RLock
class Singleton(object):
_lock = RLock() # 可重入锁
def __init__(self, name):
self.name = name
@classmethod
def instance(cls, *args, **kwargs):
# 使用锁确保线程安全
with cls._lock:
if not hasattr(cls, "_instance"):
cls._instance = cls(*args, **kwargs)
return cls._instance
方法四:使用元类
class SingletonType(type):
def __call__(cls, *args, **kwargs):
# 创建 cls 的对象时调用
if not hasattr(cls, "_instance"):
# 创建 cls 的对象
cls._instance = super(SingletonType, cls).__call__(*args, **kwargs)
return cls._instance
class Singleton(metaclass=SingletonType):
def __init__(self, name):
self.name = name
# 使用示例
single_1 = Singleton('第1次创建')
single_2 = Singleton('第2次创建')
print(f"single_1.name: {single_1.name}") # 第1次创建
print(f"single_2.name: {single_2.name}") # 第1次创建
print(f"是否为同一对象: {single_1 is single_2}") # True
Singleton是元类SingletonType的实例Singleton('参数')实际上是调用元类的__call__方法- 使用
super()避免递归调用
方法五:使用模块
这是 Python 官方推荐的单例实现方式,简单且天然线程安全
class Singleton:
def __init__(self, name):
self.name = name
def do_something(self):
print(f"{self.name} 正在工作...")
# 创建单例实例
singleton = Singleton('模块单例')
# file1.py
from my_singleton import singleton
print(f"file1 中的 singleton: {singleton}")
# file2.py
from my_singleton import singleton
print(f"file2 中的 singleton: {singleton}")
# 测试文件
import file1
import file2
print(f"是否为同一对象: {file1.singleton is file2.singleton}") # True
进度条打印
描述
字符串具有更丰富的格式化方法,结合转义字符、暂停模块,可以实现进度条打印。
效果如下:
从0%到100%,每0.5秒打印一次,每次在同一行打印
[# ] 0%
[##########] 100%
题解
# 模拟进度条
import time
for i in range(1,10):
print(f"[{'#'*i:<10}]",f"{i*10}%",'\r',end="",flush=True)
time.sleep(0.5)
后缀表达式
描述
后缀表达式,又称逆波兰式,指的是不包含括号,运算符放在两个运算对象的后面,所有的计算按运算符出现的顺序,严格从左向右进行(不再考虑运算符的优先规则)。
例如:后缀表达式为“2 3 + 4 × 5 -”计算过程如下: (1)从左至右扫描,将 2 和 3 压入堆栈; (2)遇到 + 运算符,因此弹出 3 和 2( 3 为栈顶元素,2 为次顶元素,注意与前缀表达式做比较),计算出 3+2 的值,得 5,再将 5 入栈; (3)将 4 入栈; (4)接下来是 × 运算符,因此弹出 4 和 5,计算出 4 × 5 = 20,将 20 入栈; (5)将 5 入栈; (6)最后是-运算符,计算出 20-5 的值,即 15,由此得出最终结果。
示例
listx = [15, 7, 1, 1, "+", "-", "/", 3, "*", 2, 1, 1, "+", "+", "-"]
题解
# 方法1-python人思维
while len(listx) > 1:
print(listx)
for i in range(len(listx)):
if str(listx[i]) in '+-*/':
if listx[i] == '+':
new = listx[i-2] + listx[i-1]
if listx[i] == '-':
new = listx[i-2] - listx[i-1]
if listx[i] == '*':
new = listx[i-2] * listx[i-1]
if listx[i] == '/':
new = listx[i-2] / listx[i-1]
del listx[i]
del listx[i-1]
listx[i-2] = new
break
print(listx)
# 方法2-利用pop 和 append 仿c语言栈操作
listy = []
for i in listx:
if str(i) not in "+-*/":
listy.append(i) # 入栈
else:
if i == "+":
new = listy.pop() + listy.pop() # 出栈
if i == "-":
new = listy.pop() - listy.pop()
if i == "*":
new = listy.pop() * listy.pop()
if i == "/":
new = listy.pop() / listy.pop()
listy.append(new)
print(listy)
电影演员
描述
小明拿到了一个电影+演员的数据名单,他想设计一个程序,要 求: 1.输入演员名 2.如果演员出演了电影,则打印他+他出演的全部电影。程序结束 3.如果演员没有出演电影,则打印查无此人。程序继续
电影 = [
'妖猫传',['黄轩','染谷将太'],
'