Skip to content

python

uv 项目管理工具

安装
shell
# 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
使用
shell
uv init <project dir> # 创建项目
uv venv
uv add pandas # 添加依赖
uv sync # 自动使用合适的 python 版本,安装对应依赖; 此操作还会将依赖同步到 .venv 中
uv run .\hello.py # 运行

条件判断

python
age = 3
if age >= 18:
    print('adult')
elif age >= 6:
    print('teenager')
else:
    print('kid')

逻辑运算符

not > and > or

python
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('字符串')

三元表达式

python
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

python
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 ???.')
python
# 我们假设用户输入了一个命令,用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 _` 表示其他所有情况。

循环(遍历)

python
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)

错误捕捉

python
# 错误捕捉从上往下,只要捕捉到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('程序执行完毕')

测试用例

python
def my_adder(x,y):
    return x + y
python
import 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

python
yes = True
no = False

字符串

python
# 单个替换
'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

python
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一旦初始化就不能修改

python
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的实现原理和查字典是一样的,先在字典的索引表里(比如部首表)查这个字对应的页码,然后直接翻到该页,找到这个字。无论找哪个字,这种查找速度都非常快,不会随着字典大小的增加而变慢。

python
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。

python
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 会一直指向计算好的引用,导致每次执行函数的时候,该默认参数都是同一个

  • 定义

python
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 允许代码先运行起来)
python
def nop():
    pass
  • 多返回结果
python
def getResult(active = False):
    x = 1
    y = 3 if not active else 2
    return x, y
a, b = getResult()
r = getResult() # (1, 2); 实际返回的变量就是 tuple 类型
  • 可变参数
python
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

python
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 不同,命名关键字参数需要一个特殊分隔符 ** 后面的参数被视为命名关键字参数。

python
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")

匿名函数

python
def calculate_and_print(num, fn):
    print(fn(num))

calculate_and_print(7, lambda num: num * 5)

高级特性

  • 切片
python
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[:]
  • 迭代

判断是否可迭代

python
from collections.abc import Iterable
isinstance('abc', Iterable)

实现类似Java那样的下标循环

python
for i, value in enumerate(['A', 'B', 'C'])
    print(i, value)

同时引用了两个变量

python
for x, y in [(1, 1), (2, 4), (3, 9)]:
    print(x, y)
  • 列表生成式
python
# 生成[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]

函数式编程

  • 高阶函数
python
# 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) # 反向排序
  • 闭包
python
# 引用 inc() 函数内部的x,所以需要在 fn() 函数内部加一个 nonlocal x 的声明
def inc():
    x = 0
    def fn():
        # nonlocal x
        x = x + 1
        return x
    return fn
print(inc()()) # 1
  • lambda 匿名函数
python
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__ 也会被修改成新的函数名,某些依赖函数签名的代码执行就会出错;

python
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')
  • 偏函数

简化固化参数

python
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__ 变量把作者写进去,这样当你公开源代码后别人就可以瞻仰你的大名;

python
#!/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()
shell
# 从 pypi.org 下载
pip install akshare

面对对象编程

python
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()
  • 继承和多态
python
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 限制自定义属性
python
class Student(object):
    __slots__ = ('name', 'age') # 用tuple定义允许绑定的属性名称

s = Student()
s.name = 'rick'
s.age = 98

# 使用__slots__要注意,__slots__定义的属性仅对当前类实例起作用,对继承的子类是不起作用的
# 除非在子类中也定义__slots__,这样,子类实例允许定义的属性就是自身的__slots__加上父类的__slots__
  • @property 负责把一个方法变成属性

只定义 getter 方法,不定义 setter 方法就是一个只读属性

WARNING

属性的方法名不要和实例变量重名,会造成递归调用,导致栈溢出报错!

python
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
  • 多重继承
python
class Animal(object):
    pass
# 哺乳类
class Mammal(Animal):
    pass

class RunnableMixIn(object):
    pass

# 既是哺乳类,也能奔跑
class Dog(Mammal, RunnableMixIn):
    pass
  • 定制类

当调用 print(Student('Michael')) 的时候,按 str 函数返回内容 当调用 Student('Michael') 的时候,按 repr 函数返回内容

python
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
python
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
python
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')

文件

python
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())
python
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!')