python
uv 项目管理工具
安装
# On macOS and Linux.
curl -LsSf https://astral.sh/uv/install.sh | sh
# On Windows.
powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"
# With pip.
pip install uv使用
uv init <project dir> # 创建项目
uv venv
uv add pandas # 添加依赖
uv sync # 自动使用合适的 python 版本,安装对应依赖; 此操作还会将依赖同步到 .venv 中
uv run .\hello.py # 运行条件判断
age = 3
if age >= 18:
print('adult')
elif age >= 6:
print('teenager')
else:
print('kid')逻辑运算符
not > and > or
work_count = 20
envelope_count = 2
has_been_angry = False
if(work_count > 10 and envelope_count > 1 and not has_been_angry):
print('you are safe')
if not 'hello world' == str:
print('不是字符串')
else:
print('字符串')三元表达式
from sys import argv
args = argv[1:]
isOK = 'are you ok' in args # boolean
value = 'yes ok' if 'are you ok' in args else 'noOk' # 'yes ok' | 'noOk'match
score = 'B'
match score:
case x if x < 10:
print(f'< 10 years old: {x}')
case 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18:
print('score is A.')
case 'B':
print('score is B.')
case 'C':
print('score is C.')
case _: # _表示匹配到其他任何情况
print('score is ???.')# 我们假设用户输入了一个命令,用args = `['gcc', 'hello.c']`存储,下面的代码演示了如何用match匹配来解析这个列表:
args = ['gcc', 'hello.c', 'world.c']
# args = ['clean']
# args = ['gcc']
match args:
# 如果仅出现gcc,报错:
case ['gcc']:
print('gcc: missing source file(s).')
# 出现gcc,且至少指定了一个文件:
case ['gcc', file1, *files]:
print('gcc compile: ' + file1 + ', ' + ', '.join(files))
# 仅出现clean:
case ['clean']:
print('clean')
case _:
print('invalid command.')
# 第一个case `['gcc']` 表示列表仅有 `gcc` 一个字符串,没有指定文件名,报错;
# 第二个case `['gcc', file1, *files]` 表示列表第一个字符串是 `gcc`,第二个字符串绑定到变量 `file1` ,后面的任意个字符串绑定到 `*files` ,它实际上表示至少指定一个文件;
# 第三个 `case ['clean']` 表示列表仅有 `clean` 一个字符串;
# 最后一个 `case _` 表示其他所有情况。循环(遍历)
names = ['Michael', 'Bob', 'Tracy']
for name in names:
print(name)
# 生成 0,1,2,3,4
for item in list(range(5)):
print(item)
n = 0
while n < 10:
n = n + 1
if n % 2 == 0: # 如果n是偶数,执行continue语句
continue # continue语句会直接继续下一轮循环,后续的print()语句不会执行
print(n)
contact = {'name': 'rick', 'age': 19}
for key, value in contact.items():
print(key, value)
# 计算1到100的合
total = 0
for i in range(1, 101, 1): # range:1 到 (101-1),其中最后的1为 step,可选
total += i
print(total)错误捕捉
# 错误捕捉从上往下,只要捕捉到1个错误,那么往下的 except 不会再继续执行
try:
user_weight = float(input('请输入体重')) # 期望输入数字
user_height = float(input('请输入身高')) # 期望输入数字
user_BMI = user_weight / user_height ** 2 # user_height ** 2 身高的平方
except ValueError: # 产生值错误
print('非法输入,请输入数字')
except ZeroDivisionError: # 产生处零错误
print('非法输入,请输入大于 0 的数字')
except: # 其它错误
print('未知错误')
else: # 没有错误产生
print('计算成果:' + str(user_BMI))
finally: # 不管是否错误都会运行
print('程序执行完毕')测试用例
def my_adder(x,y):
return x + yimport unittest
from my_calculator import my_adder
class TestMyAdder(unittest.TestCase): # 继承 unittest.TestCase 的测试功能
def setUp(self): # 测试 class 的时候,所有的方法都可以基于一个对象,所以直接定义一个实例,接下来的方法都直接基于该实例测试,不需要每个函数中都创建一次
self.test_shopping = Shopping({'纸巾': 2, '帽子': 30})
def test_positive_with_positive(self): # 测试用例应该以 test_ 开头
self.assertEqual(my_adder(3, 5), 8)
def test_get_shopping_count(self):
self.assertTrue(self.test_shopping.get_item_count(), 2)
# assertEqual(A, B) ~ A == B assertNotEqual
# assertTrue(A) ~ A is True assertFalse
# assertIn(A, B) ~ A in B assertNotIn
#python -m unittest 自动查询所有继承了 unittest.TestCase 的子类并运行所有以 test_ 开头的方法
boolean
yes = True
no = False字符串
# 单个替换
'Hello, %s' % 'world'
# 多个替换
'Hello, %s-%s' % ('new', 'world')
# f-string {r}被变量r的值替换,{s:.2f}被变量s的值替换,并且:后面的.2f指定了格式化参数(即保留两位小数),因此,{s:.2f}的替换结果是19.62
r = 2.5
s = 3.14 * r ** 2
f'The area of a circle with radius {r} is {s:.2f}' # The area of a circle with radius 2.5 is 19.62
message = """
line {0}
line {1}
line {2}
""".format('a', 'b', 'c')
current_year = 2025
message = """
line {year}
""".format(year = current_year)
print(message)list
classmates = ['Michael', 'Bob', 'Tracy']
len(classmates) # 3
classmates[0] # 'Michael'
classmates[1] = 'new item b' # 修改
classmates.count('item b') # item b 出现的次数
classmates.pop() # 删除 list 末尾的元素; pop(i): 删除指定位置的元素, 其中 i 是索引位置
classmates.append('item c') # push
classmates.remove('item a')
sort_classmates = sorted(classmates)tuple 有序列表
tuple和list非常类似,但是tuple一旦初始化就不能修改
classmates = ('Michael', 'Bob', 'Tracy')
t = () # 空 tuple
t = (1,) # 避免歧义,单个 tuple 需要如此定义
def func():
return 'hello world', 1
str, num = func()dict
dict全称dictionary,在其他语言中也称为map,使用键-值(key-value)存储,具有极快的查找速度。
dict的实现原理和查字典是一样的,先在字典的索引表里(比如部首表)查这个字对应的页码,然后直接翻到该页,找到这个字。无论找哪个字,这种查找速度都非常快,不会随着字典大小的增加而变慢。
d = {'Michael': 95, 'Bob': 75, 'Tracy': 85}
d['Michael'] # 95
d.get('Thomas', -1) # 属性不存在则返回 -1 默认值
d.pop('Bob') # 删除属性
d.keys() # 所有键
d.values() # 所有值
d.items() # 所有键值队
d2 = {
('rick', 99): 'too rick'
}
d2[('rick', 99)] # too rick
('rick', 99) in d2 # True
del d2[('rick', 99)]set
set和dict类似,也是一组key的集合,但不存储value。由于key不能重复,所以,在set中,没有重复的key。
s = {1, 2, 3} # {1, 2, 3}
s = set([1, 2, 3]) # {1, 2, 3}
s.add(4)
s.remove(4)
s1 = {1, 2, 3}
s2 = {2, 3, 4}
# 交集
s1 & s2 # {2, 3}
# 并集
s1 | s2 # {1, 2, 3, 4}函数
默认参数必须指向不变对象,如 list 会一直指向计算好的引用,导致每次执行函数的时候,该默认参数都是同一个
定义
def my_abs(x):
if not isinstance(x, (int, float)):
raise TypeError('bad operand type')
if x >= 0:
return x
else:
return -x
print(my_abs(-99))- 空函数(pass 允许代码先运行起来)
def nop():
pass- 多返回结果
def getResult(active = False):
x = 1
y = 3 if not active else 2
return x, y
a, b = getResult()
r = getResult() # (1, 2); 实际返回的变量就是 tuple 类型- 可变参数
def calc(*numbers): # 参数 numbers 接收到的是一个 tuple
sum = 0
for n in numbers:
sum = sum + n * n
return sum
calc(*[1, 2, 3])
calc(*(1, 3, 5, 7))
calc(1, 2)- 关键字参数
可变参数允许你传入0个或任意个参数,这些可变参数在函数调用时自动组装为一个tuple。而关键字参数允许你传入0个或任意个含参数名的参数,这些关键字参数在函数内部自动组装为一个dict
kw 获得的 dict 是 extra 的一份拷贝,对kw的改动不会影响到函数外的 extra
def person(name, age, **kw):
print('name:', name, 'age:', age, 'other:', kw)
person('Michael', 30) # name: Michael age: 30 other: {}
person('Adam', 45, gender='M', job='Engineer') # name: Adam age: 45 other: {'gender': 'M', 'job': 'Engineer'}
person('Jack', 24, **{'city': 'Beijing', 'job': 'Engineer'}) # name: Jack age: 24 other: {'city': 'Beijing', 'job': 'Engineer'}- 命名关键字参数
和关键字参数 **kw 不同,命名关键字参数需要一个特殊分隔符 *,* 后面的参数被视为命名关键字参数。
def person(name, age, *, city, job):
print(name, age, city, job)
person('Jack', 24, city='Beijing', job='Engineer')
# 如果函数定义中已经有了一个可变参数,后面跟着的命名关键字参数就不再需要一个特殊分隔符 * 了
def person(name, age, *args, city, job):
print(name, age, args, city, job)
person("Alice", 30, city="Beijing", job="Engineer")
person("Alice", 30, "extra1", "extra2", city="Beijing", job="Engineer")匿名函数
def calculate_and_print(num, fn):
print(fn(num))
calculate_and_print(7, lambda num: num * 5)高级特性
- 切片
L = ['Michael', 'Sarah', 'Tracy', 'Bob', 'Jack']
L[0:3] # 取前 3 个元素,如果第一个索引是 0,可省略 L[:3]
# 倒数切片
L[-2:] # ['Bob', 'Jack']
# 前10个数,每两个取一个:
L[:10:2]
# 所有数,每5个取一个:
L[::5]
# 原样复制
L[:]- 迭代
判断是否可迭代
from collections.abc import Iterable
isinstance('abc', Iterable)实现类似Java那样的下标循环
for i, value in enumerate(['A', 'B', 'C'])
print(i, value)同时引用了两个变量
for x, y in [(1, 1), (2, 4), (3, 9)]:
print(x, y)- 列表生成式
# 生成[1x1, 2x2, 3x3, ..., 10x10]
[x * x for x in range(1, 11)] # [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
# 筛选出仅偶数的平方
[x * x for x in range(1, 11) if x % 2 == 0] # [4, 16, 36, 64, 100]
# 两层循环
[m + n for m in 'ABC' for n in 'XYZ'] # ['AX', 'AY', 'AZ', 'BX', 'BY', 'BZ', 'CX', 'CY', 'CZ']
# dict
d = {'x': 'A', 'y': 'B', 'z': 'C' }
for k, v in d.items():
print(k, '=', v)
# y = B
# x = A
# z = C
# 生成 list
[k + '=' + v for k, v in d.items()] # ['y=B', 'x=A', 'z=C']
# if else
[x if x % 2 == 0 else -x for x in range(1, 11)] # [-1, 2, -3, 4, -5, 6, -7, 8, -9, 10]函数式编程
- 高阶函数
# map
def f(x):
return x * x
list(map(f, [1, 2, 3, 4, 5, 6, 7, 8, 9])) # [1, 4, 9, 16, 25, 36, 49, 64, 81]
# reduce
from functools import reduce
def add(x, y):
return x + y
reduce(add, [1, 3, 5, 7, 9]) # 25
# filter
def is_odd(n):
return n % 2 == 1
list(filter(is_odd, [1, 2, 4, 5, 6, 9, 10, 15])) # [1, 5, 9, 15]
# sorted
sorted([36, 5, -12, 9, -21])
sorted([36, 5, -12, 9, -21], key=abs) # 绝对值
sorted(['bob', 'about', 'Zoo', 'Credit'], key=str.lower) # 忽略大小写
sorted(['bob', 'about', 'Zoo', 'Credit'], key=str.lower, reverse=True) # 反向排序- 闭包
# 引用 inc() 函数内部的x,所以需要在 fn() 函数内部加一个 nonlocal x 的声明
def inc():
x = 0
def fn():
# nonlocal x
x = x + 1
return x
return fn
print(inc()()) # 1- lambda 匿名函数
list(map(lambda x: x * x, [1, 2, 3, 4, 5, 6, 7, 8, 9]))
# lambda x: x * x 相当于
def f(x):
return x * x- 装饰器
@functools.wraps(func) 修正 __name__;
装饰器实际上是返回新的函数,所以 __name__ 也会被修改成新的函数名,某些依赖函数签名的代码执行就会出错;
import functools
def log(func):
@functools.wraps(func)
def wrapper(*args, **kw):
print('call %s():' % func.__name__)
return func(*args, **kw)
return wrapper
@log
def now():
print('2024-6-1')- 偏函数
简化固化参数
import functools
functools.partial(max, 10)
max2(5, 6, 7) # 相当于 max(10, 5, 6, 7)模块
第1行和第2行是标准注释,第1行注释可以让这个hello.py文件直接在Unix/Linux/Mac上运行,第2行注释表示.py文件本身使用标准UTF-8编码;
第4行是一个字符串,表示模块的文档注释,任何模块代码的第一个字符串都被视为模块的文档注释;
第6行使用 __author__ 变量把作者写进去,这样当你公开源代码后别人就可以瞻仰你的大名;
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
' a test module '
__author__ = 'Michael Liao'
import sys
# from sys import argv
# from sys from * // 也是可以直接使用函数名,但不推荐此用法,存在函数名重复可能
def test():
args = sys.argv
if len(args)==1:
print('Hello, world!')
elif len(args)==2:
print('Hello, %s!' % args[1])
else:
print('Too many arguments!')
if __name__=='__main__':
test()# 从 pypi.org 下载
pip install akshare面对对象编程
class Student(object):
def __init__(self, name, score, age):
self.name = name
self.score = score
self.__private_age = age # 私有变量,以 __开头,不允许外部访问(本质是 python 解释器会将该变量修改成别的变量名)
def print_score(self):
print('%s: %s' % (self.name, self.score))
bart = Student('Bart Simpson', 59, 88)
bart.print_score()- 继承和多态
class Animal(object):
def __init__(self, name, age):
self.name = name
self.age = age
def run(self):
print(f'Animal {self.name} is running...')
# 继承: Animal run
class Dog(Animal):
def __init__(self, name, age, sex):
super().__init__(name, age)
self.sex = sex
dog1 = Dog('Jojo', 18)
dog1.run() # Animal Jojo is running...
# 多态: 扩展 Animal run 方法
class Cat(Animal):
def run(self):
print('Cat is running...')- slots 限制自定义属性
class Student(object):
__slots__ = ('name', 'age') # 用tuple定义允许绑定的属性名称
s = Student()
s.name = 'rick'
s.age = 98
# 使用__slots__要注意,__slots__定义的属性仅对当前类实例起作用,对继承的子类是不起作用的
# 除非在子类中也定义__slots__,这样,子类实例允许定义的属性就是自身的__slots__加上父类的__slots__- @property 负责把一个方法变成属性
只定义 getter 方法,不定义 setter 方法就是一个只读属性
WARNING
属性的方法名不要和实例变量重名,会造成递归调用,导致栈溢出报错!
class Student(object):
@property
def score(self):
return self._score
@score.setter
def score(self, value):
if not isinstance(value, int):
raise ValueError('score must be an integer!')
if value < 0 or value > 100:
raise ValueError('score must between 0 ~ 100!')
self._score = value
s = Student()
s.score = 60
print(s.score) # 60- 多重继承
class Animal(object):
pass
# 哺乳类
class Mammal(Animal):
pass
class RunnableMixIn(object):
pass
# 既是哺乳类,也能奔跑
class Dog(Mammal, RunnableMixIn):
pass- 定制类
当调用 print(Student('Michael')) 的时候,按 str 函数返回内容 当调用 Student('Michael') 的时候,按 repr 函数返回内容
class Student(object):
def __init__(self, name):
self.name = name
def __str__(self):
return 'Student object (name=%s)' % self.name
__repr__ = __str____iter__ 遍历
__getitem__ 实现 list
__getattr__ 调用不存在的方法或属性
__call__ 调用实例本身 callable({ target }), 判断 target 是否可被调用
- Enum
from enum import Enum
Month = Enum('Month', ('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'))
# 遍历
for name, member in Month.__members__.items():
print(name, '=>', member, ',', member.value)
# value 属性则是自动赋给成员的 int 常量,默认从 1 开始计数
from enum import Enum, unique
@unique # 检查保证没有重复值
class Weekday(Enum):
Sun = 0 # Sun的value被设定为0
Mon = 1
Tue = 2
Wed = 3
Thu = 4
Fri = 5
Sat = 6错误处理
- try...except...finally
try:
print('try...')
r = 10 / int('a')
print('result:', r)
except ValueError as e:
print('ValueError:', e)
except ZeroDivisionError as e:
print('ZeroDivisionError:', e)
finally:
print('finally...')
print('END')文件
f = open('apps/note/views/python/index.md', 'r', encoding='utf-8') # r 只读、w 只写
print(f.read()) # 一次性读取全部的文件内容(内存占用大),二次调用的时候会返回空,因为是从上次读取的位置继续读取的,接受 int 参数,表示读取的字节数
print(f.readline()) # 读取一行(根据换行符决定)文件内容
# 连续读取
line = f.readline()
while line != ''
line = f.readline()
f.readlines() # 返回全部文件内容组成的列表
f.close() # 关闭文件,释放资源
# 代码执行完毕,自动释放资源
with open("a.txt") as f:
print(f.read())with open('apps/note/views/python/test_run.py', 'r+', encoding='utf-8') as f: # 如果文件不存在,不会报错,而是选择创建该文件; w 执行写入操作,会清空原先的文件内容、a 写入追加、r+ 支持读写、追加模式(前面追加)
f.write('hello \n')
f.write('world!')