勇闯前后端:后端(Python)——Week1
勇闯前后端:后端(Python)——Week1
前言
注意,这个博客记录并不会从头详细的讲解Python,因为大量的教程已经讲解的非常的通俗易懂了。除此之外,因为核心是后端,所以前面的内容知识点会大概的掠过。不过所有的细节笔者已经附上了详细的参考文档,可以去看看然后了解一下Python。
基本的最最常见类型
int:任意精度整数;float:IEEE-754 双精度浮点数(注意精度误差)。- 除法与取整:
/总是返回float,//做向下取整(floor division),%取模(余数)。例如7/3 == 2.333...,7//3 == 2,7%3 == 1。(Python documentation) - 位运算与幂运算:
<<,>>,&,|,^,~;幂使用**(如2**10)。 - 精度注意:浮点相等判断应使用容差(
abs(a-b) < eps)。
示例:
a = 7
b = 3
print(a / b, a // b, a % b) # 2.3333... 2 1
字符串(str)
- 字符串是不可变的(immutable),索引与切片返回新对象;常用操作:索引、切片、拼接、格式化(f-string、
format())。不可变特性影响性能:大量逐个拼接字符串应使用str.join()。(Python documentation)
示例:
s = "Hello, world"
print(s[0], s[-5:], f"{s}!") # H world Hello, world!
列表(list)
- 列表是可变序列:常用方法
append,extend,insert,pop,remove,sort,reverse等。注意:许多就地修改的方法(如list.sort())返回None(即会在原地修改)。这点在链式调用时容易出错。(Python documentation) - 列表切片操作
L[a:b]返回新列表(不是引用同一对象)。 - 性能:索引访问 O(1),在末尾添加 amortized O(1),在头部/中间插入或删除通常为 O(n)。
示例:
L = [1,2,3]
L.append(4) # L -> [1,2,3,4]
L2 = L[::-1] # 切片反转,返回新列表
元组(tuple)
- 元组是不可变序列,适合表示固定记录或作为 dict 的键(当元素都是 hashable 时)。用处:数据不被修改时首选,用来保证不变性与潜在的少量性能优势。
字典(dict)
- 字典是映射类型(键 -> 值),键必须是可哈希(hashable)的(例如不可用 list 作为键)。常用方法:
dict.get(key, default),setdefault,pop,items(),keys(),values()。查找、插入和删除在均摊情况下平均 O(1)。(Python documentation)
示例:
users = {"Alice":1000, "Bob":500}
print(users.get("Charlie", 0))
集合(set)
- 无序、不重复元素集合,常用集合运算:并(
|/union)、交(&/intersection)、差(-/difference)、对称差(^/symmetric_difference)等。适用于去重和成员测试(平均 O(1))。(Python documentation)
示例:
A = {1,2,3}
B = {2,3,4}
print(A | B, A & B, A - B)
Reference
- 内置类型(stdtypes — 数字、序列、映射等) — 官方中文版:文档页面。(Python documentation)
(推荐读:内置类型章节,包含 list/tuple/str/dict/set 的方法说明) - 内建函数(
list(),dict()等)参考页 — functions 列表(内建函数)。(Python documentation)
练习
- 交换变量 a, b(不使用临时变量)。
输入:a = 10; b = 20 - 给列表
L=[1,2,3,4,5],用切片反转并打印。 - 字典操作:
users = {"Alice":1000,"Bob":500}- 打印所有用户名
- 打印所有余额
- 判断 “Charlie” 是否在字典中,如果不在则添加
"Charlie": 0
- 集合运算:
A={1,2,3},B={2,3,4}打印并集、交集、差集A-B。
参考答案
"""
languages.python.chapter1.pr1.simple_practice
"""def simple_swap(a: int, b: int) -> tuple[int, int]:return b, aa = 30
b = 20
print(f"Before swap: a: {a} and b: {b}")
a, b = simple_swap(a, b)
print(f"After swap: a: {a} and b: {b}")ordered_l = [1, 2, 3]
reversed_l = ordered_l[::-1]
print(f"Ordered list: {ordered_l}, reversed: {reversed_l}")users: dict[str, int] = {"Alice": 1000, "Bob": 500}for user in users:print(f"User: {user}, deposits: {users[user]}")checked: str = "Charlie"
expected_deposits: int = 0
if checked not in users:users[checked] = expected_depositsfor user in users:print(f"User: {user}, deposits: {users[user]}")A = {1, 2, 3}
B = {2, 3, 4}print(f"And: {A.union(B)}, Or: {A.intersection(B)}, minus: {A.difference(B)}")
函数与作用域(要点 + 链接)
基本定义
- 使用
def name(params):定义函数,使用return返回值(无 return 等价于返回None)。示例:def fn(x): return x*2。(Python documentation)
默认参数陷阱
- 默认参数在函数定义时只评估一次(即定义时绑定),因此不要用可变对象(如
[]、{})作为默认参数。若需要可变默认,使用None并在函数体内创建新的容器。示例见下。(Python documentation)
正确写法示例:
def append_item(item, lst=None):if lst is None:lst = []lst.append(item)return lst
可变参数与关键字参数
*args:位置可变参数,接收一个 tuple;**kwargs:关键字可变参数,接收一个 dict。
def f(*args, **kwargs):print(args, kwargs)
作用域(scope)与命名空间
- 局部变量优先于全局变量;若要在函数内赋值修改全局变量,使用
global;若要修改外层非全局(闭包)变量,使用nonlocal(在嵌套函数里)。阅读教程关于作用域与命名空间的章节。(Python documentation)
示例:
x = 10
def change_x():global xx = 5
change_x()
print(x) # 5
Reference
- 官方教程:函数定义与作用域章节(Python 教程,函数与命名空间/作用域)。(Python documentation)
练习
- 编写
withdraw(balance, amount):若amount > balance返回None并打印"余额不足"(笔者习惯抛异常解决问题)。 - 编写
summary(*numbers)返回(avg, total)。 - 作用域测试:定义全局
x = 10,写change_x()内部尝试x = 5,调用后打印全局x并解释结果;然后修改函数使其能修改全局x。 - 编写带关键字-only 参数的
greet(name, *, msg="Hello"),打印"{msg}, {name}"。
参考答案
def deposit(balance: int, amount: int) -> int:return balance + amountdef withdraw(balance: int, amount: int) -> int:if balance - amount < 0:# Overflowprint("[Warning]: Balance not fits")raise OverflowErrorreturn balance - amountdef summary(*sums: int) -> tuple[float, int]:if len(sums) == 0:return 0, 0sum: int = 0average: float = 0for each in sums:sum += eachaverage = sum / len(sums)return average, sumaverage, sum = summary(1, 2, 3)
print(f"get average: {average}, sum: {sum}")def greetings(name: str, *, message: str) -> str:return f"Hello, {name}, {message}"print(greetings("Charlie", message="Nice To meet you!"))x:int = 10def change_x() -> None:# global x # Uncomment these to see the global changedx = 5 # If you open Pylance Strict Check, you shell find the var unusedchange_x()
print(x)
类与对象入门(要点 + 链接)
基本类定义
- 使用
class Name:定义类,__init__(self, ...)作为构造器,self表示实例本身。实例方法第一个参数通常是self。(Python documentation)
属性与私有化约定
- Python 中“私有”只是命名约定:前导单下划线
_attr表示内部使用,双下划线__attr会触发名称重整(name mangling),变为_ClassName__attr,用于避免子类冲突,但不是完全私有。
魔法方法
__str__用于用户可读的字符串表示(print(obj)),__repr__用于调试表达。实现它们可以大大方便调试。
继承与方法重写
- 通过
class Sub(Super):实现继承;可以用super()调用父类方法。
Reference
- 官方教程:类章节(Classes — Python 教程)— 建议逐节阅读(类定义、继承、私有变量、方法等)。(Python documentation)
练习
- 基本
User类:User(name, balance),实现deposit(amount);实现withdraw(amount),余额不足时抛出ValueError。 - 为
User添加__str__打印User(name=XXX, balance=YYY)。 - 每次存取款记录交易字符串到
transactions列表(例如"存入 100"、"取出 50"),并实现show_transactions()。 - 继承练习:
VIPUser继承User,新增discount_rate(例如 0.95),存款或取款时自动乘以折扣率(演示继承与方法扩展)。
答案
from enum import Enumclass Operations(Enum):DEPOSIT = 1,WITHDRAW = 2class User:"""User:Contains the abstractions of user,including user name, balance and so on"""def __init__(self, /, name: str, balance: float = 0) -> None:self.__name: str = nameself.__balance: float = balanceself._transactions: list[str] = []def _trace_type(self, amount: float, type: Operations) -> None:record: str = f"Op: {type.name}, Amount: {amount}"self._transactions.append(record)def __format_print(self) -> str:return f"{self.__name}: {self.__balance}"def __repr__(self) -> str:return self.__format_print()def __str__(self) -> str:return self.__format_print()def deposit(self, amount: float):if amount < 0:raise ValueError(f"Amount should be above 0, however get: {amount}")self.__balance += amountself._trace_type(amount, Operations.DEPOSIT)def withdraw(self, amount: float):if amount < 0:raise ValueError(f"Amount should be above 0, however get: {amount}")if self.__balance - amount < 0:raise OverflowError(f"Have no enough money")self.__balance -= amountself._trace_type(amount, Operations.WITHDRAW)def user_transaction(self) -> list[str]:return self._transactionsclass UserShownHelper:@staticmethoddef show_transactions(u: User) -> str:res: str = ""for each in u.user_transaction():res += each + '\n'return res
from User import User
from User import Operationsclass VIPUser(User):def __init__(self, name: str, balance: float = 0, discount_rate: float = 1.0):super().__init__(name=name, balance=balance)self.__rate = discount_ratedef withdraw(self, amount: float):amount *= self.__ratereturn super().withdraw(amount)def deposit(self, amount: float):amount *= self.__ratereturn super().deposit(amount)def _trace_type(self, amount: float, type: Operations) -> None:record: str = f"Op: {type.name}, Amount: {amount}, Rate: {self.__rate}"self._transactions.append(record)
from User import User, UserShownHelper
from VIPUser import VIPUser
user: User = User(name="Charlie")
print(f"User: {user}")
user.deposit(1000)
user.withdraw(100)
print(f"User: {user}")print(UserShownHelper.show_transactions(user))vip_user: VIPUser = VIPUser(name="Charlie")
print(f"User: {vip_user}")
vip_user.deposit(1000)
vip_user.withdraw(100)
print(f"User: {vip_user}")print(UserShownHelper.show_transactions(vip_user))
大作业:ATM机
必须实现:
- 登录(按用户名,若不存在提示并允许注册)
- 查询余额、存款、取款(异常处理)、转账(双边账户检查)
- 退出时保存(写回
users.json)
实现建议:把用户序列化为字典(to_dict() / from_dict()),Bank 管理 users: Dict[str, User] 并负责 load()/save()。参考 json、sqlite3(如需更强持久化)与 argparse(批处理/脚本参数)的官方文档。(Python documentation)
笔者的实现如下:
User.py
from enum import Enum
from typing import Anyclass Operations(Enum):DEPOSIT = 1,WITHDRAW = 2class User:"""User:Contains the abstractions of user,including user name, balance and so on"""def __init__(self, /, name: str, balance: float = 0) -> None:self.__name: str = nameself.__balance: float = balanceself._transactions: list[str] = []def _trace_type(self, amount: float, type: Operations) -> None:record: str = f"Op: {type.name}, Amount: {amount}"self._transactions.append(record)def __format_print(self) -> str:return f"{self.__name}: {self.__balance}"def __repr__(self) -> str:return self.__format_print()def __str__(self) -> str:return self.__format_print()def deposit(self, amount: float):if amount < 0:raise ValueError(f"Amount should be above 0, however get: {amount}")self.__balance += amountself._trace_type(amount, Operations.DEPOSIT)def withdraw(self, amount: float):if amount < 0:raise ValueError(f"Amount should be above 0, however get: {amount}")if self.__balance - amount < 0:raise OverflowError(f"Have no enough money")self.__balance -= amountself._trace_type(amount, Operations.WITHDRAW)def user_transaction(self) -> list[str]:return self._transactionsdef get_desp_dict(self) -> dict[str, Any]:return {"name": self.__name, "balance": self.__balance}class UserShownHelper:@staticmethoddef show_transactions(u: User) -> str:res: str = ""for each in u.user_transaction():res += each + '\n'return res
storage.py
from json import dump, load, JSONEncoder, JSONDecoder, JSONDecodeError
from pathlib import Path
from typing import Any
from User import Userclass UserEncoder(JSONEncoder):def default(self, o: Any) -> dict[str, Any] | Any:if isinstance(o, User):return o.get_desp_dict()else:return super().default(o)class UserDecoder(JSONDecoder):def user_hook(self, o: Any):if "name" in o and "balance" in o:return User(o["name"], o["balance"])return odef save_users(user_list: list[User], save_path: str):with open(save_path, "w", encoding="utf-8") as f:dump(user_list, f, cls=UserEncoder, indent=2)def load_users(save_path: str) -> list[User]:"""Load file from json"""p = Path(save_path)if not p.exists() or p.stat().st_size == 0:return []with open(p, "r", encoding="utf-8") as f:try:data = load(f, cls=UserDecoder)return [User(**d) for d in data]except JSONDecodeError as e:print(f"JSON 文件损坏: {e}")return []
main.py
from storage import save_users, load_users
from User import Userdef get_user_by_name(users: list[User], user_name: str) -> User | None:for user in users:if user.get_desp_dict()['name'] == user_name:return userreturn Noneif __name__ == "__main__":users: list[User] = []try:users = load_users("users.json")except FileNotFoundError:print("users.json may not found or illformed, ATM will load empty!")if len(users) == 0:print("User Lists empty")else:print("user.json load finished, see user lists")input_user_name = input("Submit the login name or r for registered: ")login_ok = Falseu: User | None = Nonewhile not login_ok:if input_user_name == "r":input_user_name = input("Name:> ")u = User(name=input_user_name)users.append(u)breakelse:u: User | None = get_user_by_name(users=users, user_name=input_user_name)if u:breakinput_user_name = input("user submit invalid! do you registered? <for>")assert u is not None # Let type ignore :)while True:cmd = input("操作 (d=存款, w=取款, q=退出, s=展示):")if cmd == "d":amt = float(input("金额:"))u.deposit(amt)print(f"State: {u}")elif cmd == "w":amt = float(input("金额:"))u.withdraw(amt)print(f"State: {u}")elif cmd == "s":print(f"State: {u}")elif cmd == "q":breaksave_users(users, "users.json")