Python笔记
Python 笔记
ctrl + c 强制终端程序
alt + p 重复调出上个语句
BIF(Buid-in Functions )是Python的内置函数,为了方便程序员快捷编写脚本程序。
len(dir(builtins)):158 python 3.12.5内置函数有158个
打开windowsCMD命令行工具(win + R 输入CMD):
cd \ (回到根目录)
cd temp (打开temp文件夹)
python hello.py (运行hello.py文件)
notepad hello.py (用记事本打开python文件)
收集参数:*args和**kwargs的理解
打包(pack):*args是把多个位置参数打包成元组,** kwargs是把多个关键字参数打包成字典。
拆分(unpack):* args是把打包了的参数拆成单个的,依次赋值给函数的形参,** kwargs是把字典的键值拆成单个的,依次赋值给函数的形参。
#*args,*作用,有两个:打包参数和拆分参数
#打包参数,将函数的五个参数打包成一个元组输出
def foo(*args):
for i in args:
print(i)
print(type(args))
foo(1,2,3,4,5)
#拆分参数,将列表或是元组进行拆分
def bar(a, b, c, d=10):
print(a, b, c, d)
bar(*[1, 2, 3])
# **kwargs,**的作用,分两种:打包参数(pack)和拆分参数(unpack)
'''
打包(pack):*args是把多个位置参数打包成元组,**kwargs是把多个关键字参数打包成字典。
拆分(unpack):*args是把打包了的参数拆成单个的,依次赋值给函数的形参,**kwargs是把字典的键值拆成单个的,依次赋值给函数的形参。
'''
# **kwargs是把多个关键字参数打包成字典
def bar(**number):
print(number)
bar(a=1, b=2, c=3)
# **kwargs是把字典的键值拆成单个的,依次赋值给函数的形参。
def bar(a, b, c):
print(a,b,c)
bar(**{'a': 1, 'b': 2, 'c': 3})
变量
"""
Python中的变量都是指针
python中所有可复制的东西,即可以出现在赋值号‘=’左边的东西,都是指针
指针即代表内存单元的地址
讲指针称作“箭头”,更容易理解。所有变量都是箭头,指向内存某处
对变量进行赋值的本质,就是让该变量(箭头)指向某个地方
is运算符和==的区别
a is b 表示a和b是否指向同一个地方
a==b 为True 说明a和b指向的地方放的东西相同,但是a和b不一定指向相同的地方
a=b 会使得a和b指向同一个地方
int, float, str,如果a==b则a is b,tuple、list、dict、set不一定,is和==都需要关注
函数参数的传递
函数参数传递方式都是传值,即形参是实际参数的一个拷贝。函数参数也是指针。形参和实参指向同一个地方。
对形参赋值(让其指向别处)不会影响实参。
"""
def ways(x, y):
temp = x
x = y
y = temp
a = 1
b = 2
ways(a, b)
print(a, b)# >> a = 1, b = 2,因为变量a指向的地址未发生变化,是数字1
def ways(x, y):
temp = x[0]
x[0] = y[0]
y[0] = temp
a = [4, 5]
b = [6, 7]
ways(a, b)
print(a, b) #>> a和b指向的是列表,列表地址未发生变化,但是列表中的值,他的地址发生了变化
运算符
算术运算符
-
+、-、*、/ ...
-
divmod(x, y):返回(x//y,x%y)地板除值和余数
- 地板除返回比真正的商小的,最接近的数字
- 余数不能为负数
-
abs(x) :x的绝对值
-
复数,如:z = 1 + 2j,实部为1,虚部为2
-
complex(re,im) :返回一个复数,re是实部,im是虚部
- complex("2+3j"),字符串中间不能有空格
-
布尔类型(特殊的整数类型)
- 结果为False的所有情况:
- 定义为False的对象:None和False
- 值为0的数字类型:0,0.0,0j,Decimal(0),Fraction(0,1)
- 空的序列和集合:'',(),[],{},set(),range(0)
- 结果为False的所有情况:
比较运算符
- ==、>=...

赋值运算符
- =、+=、-= ...
逻辑运算符(短路运算)
- and
- or
- not
- not > and > or
1 == True
0 == False
非0相当于True但不等于True
空字符串、空表相当于False但不等于False
not 4 < 5 and 4 >6 #>> False
条件分支语句
依次计算逻辑表达式1、逻辑表达式2...只要遇到一个表达式i为真,则执行语句组i,且后面的表达式不再执行
if 逻辑表达式1:
语句组1
elif 逻辑表达式2:
语句组2
...
elif 逻辑表达式n:
语句组n
else:
语句组n + 1
#题目:同一行输入两个整数一个标点符号,输出结果
#方法一
s = input().split()
a, b, c = int(s[0]),int(s[1]),s[2]
if c in ["+", "-", "*", "/"]:
if c == "+":
print(a+b)
elif c == "-":
print(a-b)
elif c == "*":
print(a*b)
else:
if b == 0:
print("Divided by zero!")
else:
print(a/b)
else:
print("Invalid operator!")
#方法二
s = input().split()
if s[2] not in ["+", "-", "*", "/"]:
print("Invalid operator!")
elif s[2] == '/' and int(s[1]) == 0:
print("Divided by zero!")
else:
print(eval(s[0] + s[2] + s[1]))
切片
切片使用[start:stop:step]语法。当使用a[0:-1]时,-1表示的是列表的最后一个元素的前一个位置(即倒数第二个元素)。因此,这个切片操作包括从索引0到倒数第二个元素的所有元素,并不包含最后一个元素。
print("12345"[1:3]) #>> 23
a = 'abcdef'
print(a[2:-1])#>>cde
print(a[0:6])#>>abcdef
输出格式控制
字符重安中的格式控制符
%s >>表示此处要输出一个字符串
%d>>表示此处要输出一个整数
%f>>表示此处要输出一个小数
%.nf>>表示此处要输出一个小数,保留小数后面n位,四舍六入,五则可能入也可能舍。注意,有'.',
格式控制只能出现在字符串中
isinstance(数据,类型)函数,isinstance(x, y)函数查询数据x是否是类型y
a = '1233'
res = isinstance(a, str)
print(res) #True
成员运算符
- in
- not in
常见数据结构
字符串、列表、元组与集合、字典
字符串
\,转义符,\n:换行
字符串的下标
有n个字符的字符串,其中的每个字符都是长度为1的字符串:
从左到右依次的编号:0,1,2,3... n-1
从右到左依次的编号:-1,-2...-n
string = '123'
print(string[-3])# >>1
print(string[0]) # >>1
用in, not in判断子串
a = "Hello"
b = "Python"
print("el" in a)#>>True
print("th" not in b)#>>False
print("lot" in a)#>>False
eval(x):把字符串x看作是python表达式,求其值
a = 15
eval("a+1")#>>16
小数到整数的转换:int(x) x是小数,则去尾取整
int(3.9) #>>3
字符串函数
字符串的split函数详解
s.split(x)
用字符串x做分隔符分割字符串s,得到分隔后的列表,两个相邻分隔符之间会被分隔出一个空串
a = "12..34.5346...a"
print(a.split("."))#>>['12', '', '34', '5346', '', '', 'a']
字符串高级分隔,多个分隔串进行分割
import re
a = "Beautiful, is; better*than\nugly"
print(re.split(r';| |,|\*|\n', a)) # 添加r前缀改用原始字符串>>['Beautiful', '', 'is', '', 'better', 'than', 'ugly']
count 求子串出现次数
s = 'ThisAAbb AA'
s.count('AA')#>> 2
输出语句print
print(x,y,z......, end="") #end=""表示去除换行,默认print输出时会换行,","默认是一个空格
循环
for循环语句
for < variable > in < sequence >:
<statements 1>
else:
<statements 2>
for i in range(10):#[0,10)
print(i)
#连续输出26字母
for i in range(26):
print(chr(ord("a") + i), end="")#ord把字符转化为编码(ASCII),chr把编码(ASCII)转化为字符
#习题:
#斐波那契数列,已知数列的第一个和第二个数都是1,求斐波那契数列中的第k个数是多少
k = int(input())
if k == 1 or k == 2:
print(1)
else:
a1 = a2 = 1
for i in range(k - 2):
a1, a2 = a2, a1 + a2
print(a2)
#求正整数n,求不大于n的正整数的阶乘的和
#解法1
n = int(input())
s = 0
for i in range(1, n + 1):
f = 1
for j in range(1, i + 1):
f *= j
s += f
print(s)
#解法2
n = int(input())
s, f = 0, 1
for i in range(1, n + 1):
f *= i
s += f
print(s)
#输入正整数n(n>=2),求不大于n的全部质数
#解法1
n = int(input())
for i in range(2, n+1):
ok = True
for k in range(2, i):
if i % k == 0:
ok = False
break
if ok:
print(i)
#解法2
n = int(input())
print(2)
for i in range(3, n + 1, 2):
ok =True
for k in range(3, i, 2):
if i % k == 0:
ok = False
break
if k * k > i:
break
if ok:
print(i)
#计算两个整数之间2出现的次数
#方法一
s = input().split()
L, R = s[0], s[1]
total = 0
for i in range(int(L), int(R) + 1):
s = str(i)
for x in s:
if x == '2':
total += 1
print(total)
#方法二
s = input().split()
L, R = s[0], s[1]
total = 0
for i in range(int(L), int(R) + 1):
while i != 0:
m = i % 10
if m == 2:
total += 1
i //= 10
print(total)
while循环
while 循环语句 exp:
语句组1
else:
语句组2
容器
容器是Python中管理数据集合的核心机制,通过存储引用实现对元素的统一操作。可变性决定了容器能否修改其内部指针序列(如增删元素),而元素的可变性依赖其指向对象自身的特性。理解这一机制,可有效规避错误(如误改元组内列表内容),同时合理选择容器类型优化程序性能。
容器是能够包含其他对象的对象,通过引用(指针)管理元素的存取。Python中主要的容器类型包括:
- 列表(List):有序、可变
- 元组(Tuple):有序、不可变
- 字典(Dict):键值对、可变
- 集合(Set):无序、可变
核心特性:
- 存储的是元素的引用(指针),而非直接保存值的副本。
- 容器对象本身可能可变或不可变,但均通过引用操作元素。
序列
在 Python 中,序列(Sequence) 是 按顺序存储元素 的一种数据结构类型,其特点是可以通过 索引(index) 访问元素,并支持 切片(slice)、迭代 等操作。列表(List) 和 元组(Tuple) 是典型的序列类型,而 字典(Dict) 不是序列,它与序列有本质区别。以下是具体对比和分析
在 Python 中,序列(Sequence) 是 按顺序存储元素 的一种数据结构类型,其特点是可以通过 索引(index) 访问元素,并支持 切片(slice)、迭代 等操作。列表(List) 和 元组(Tuple) 是典型的序列类型,而 字典(Dict) 不是序列,它与序列有本质区别。以下是具体对比和分析:
1. 序列(Sequence):有序 + 可索引
常见序列类型:
- 列表(List):可变序列,如
[1, 2, 3] - 元组(Tuple):不可变序列,如
(1, 2, 3) - 字符串(String):不可变序列,如
"abc"(每个字符按顺序存储)
核心特点:
-
元素按顺序排列:元素的位置(索引)固定,例如
list[0]始终是第一个元素。 -
支持索引和切片:可以直接通过位置访问元素。
lst = [10, 20, 30, 40] print(lst[1]) # 20(索引从0开始) print(lst[1:3]) # [20, 30](切片) -
可迭代:可以用
for循环遍历元素。 -
同一性:判断是否是序列的标准是是否实现了
__getitem__和__len__方法。
2. 字典(Dict):键值对映射 → 非序列
特点:
-
通过键(key)访问值(value),不依赖顺序(Python 3.7+ 后字典保留插入顺序,但逻辑上仍视为无序)。
d = {"name": "Alice", "age": 25} print(d["name"]) # "Alice"(直接通过键访问) -
不支持索引和切片,无法通过
d[0]访问第一个元素(因为无顺序语义)。 -
键(key)唯一:键不可重复,值可以重复。
3. 列表、元组 vs 字典 → 本质区别
| 特性 | 列表、元组(序列) | 字典(非序列) |
|---|---|---|
| 存储方式 | 按顺序存储元素 | 基于键值对的哈希表存储 |
| 访问方式 | 索引(如 list[0]) |
键(如 dict["key"]) |
| 可变性 | 列表可变,元组不可变 | 字典可变 |
| 典型用途 | 有序数据(如时间序列) | 键值关联数据(如数据库记录) |
4. 序列与非序列的共性:可迭代性
虽然字典不是序列,但 所有的序列和非序列(如字典、集合)都可以迭代,因为它们实现了 __iter__ 方法。例如:
# 列表(序列)的迭代
for num in [1, 2, 3]:
print(num)
# 字典(非序列)的迭代 → 默认迭代键
d = {"a": 1, "b": 2}
for key in d:
print(key) # 输出 "a", "b"
# 迭代字典的值或键值对
for value in d.values():
print(value) # 1, 2
for key, value in d.items():
print(key, value) # "a 1", "b 2"
5. 如何选择数据结构?
- 需要 有序存储、允许重复元素 → 使用 列表(可修改) 或 元组(不可修改)。
- 需要 键值映射、快速查找 → 使用 字典。
- 需要 去重元素、集合运算 → 使用 集合(Set)(如
{1, 2, 3},也非序列)。
总结
- 序列(如列表、元组)是有序且支持索引的一维数据结构。
- 字典是键值对的映射结构,不依赖顺序(尽管Python 3.7+保留插入顺序),属于非序列。
- 字符串是一种特殊的不可变序列。
魔法方法
以下是关于 @property 的巩固提升总结,旨在加深对这一核心技术的理解:
一、@property核心知识巩固
1. 核心机制
- 目的:将方法伪装成属性,实现属性访问的细粒度控制。
- 原理:
class Circle: def __init__(self, radius): self._radius = radius # 私有变量 @property # getter(读) def radius(self): return self._radius @radius.setter # setter(写) def radius(self, value): if value <= 0: raise ValueError("半径必须为正") self._radius = value
2. 关键优点
| 优点 | 说明 |
|---|---|
| 数据一致性 | 防止直接修改属性引发的数据不一致(如半径变化未触发直径更新) |
| 数据验证 | 通过 setter 实现输入校验(如 value > 0) |
| 逻辑隔离 | 属性的读写逻辑(如计算、联动)被封装在方法中 |
| 接口统一 | 对外表现为属性操作,对内实现业务逻辑,可随意修改内部实现 |
| 兼容旧代码 | 可逐步改进现有类的属性访问方式而无需修改使用者代码 |
二、提升要点
1. 私有变量 + property = 最佳搭档
-
切忌暴露私有变量:
class BadExample: @property def radius(self): return self.radius # ❌ 陷入无限递归!(除非操作的是self._radius) -
正确实践:
class GoodExample: @property def radius(self): return self._radius # ✅ 操作私有变量,避免递归
2. 属性联动的标准模式
- 场景:修改半径时自动更新直径。
class Circle: def __init__(self, radius): self._radius = radius self._diameter = radius * 2 # 保存计算值,优化性能 @property def radius(self): return self._radius @radius.setter def radius(self, value): self._radius = value self._diameter = value * 2 # ✅ 同步更新 @property def diameter(self): return self._diameter # 直接返回预存值,而非每次都计算 # 1. 创建实例对象 → 必须初始化 circle = Circle(radius=10) # ✅ 此时调用 __init__ 方法设置 _radius = 10 # 2. 通过实例的“属性”访问和修改(本质是通过 @property 定义的方法) print(circle.radius) # ✅ 输出: 10(调用 getter) circle.radius = 5 # ✅ 调用 setter,此时会校验 value > 0 # ❌ 错误用法示例 print(Circle.radius) # ❌ 类对象没有实例属性 radius(会报 AttributeError) Circle.radius = 5 # ❌ 这样会覆盖类的 @property 定义,破坏原有逻辑!
3. 延迟计算(Lazy Evaluation)
- 场景:仅在第一次访问时计算属性(适用于计算成本较高的场景)。
class CircleLazy: def __init__(self, radius): self._radius = radius self._diameter = None # 初始不计算直径 @property def diameter(self): if self._diameter is None: print("计算直径...") self._diameter = self._radius * 2 return self._diameter
4. 只读属性设计
- 场景:某些属性允许读取但禁止写入。
class ReadOnlyExample: def __init__(self, name): self._name = name @property def name(self): # 不定义 setter ,name 即为只读属性 return self._name
三、常见错误及规避方法
错误1:属性的递归调用
class RecursionErrorExample:
@property
def data(self):
return self.data # ❌ 应访问 self._data
错误2:未初始化私有变量
class InitErrorExample:
@property
def value(self):
return self._value # ❌ __init__ 中未定义 self._value
错误3:过度耦合的 setter
class OverlyComplexSetter:
@data.setter
def data(self, value):
# 此处包含数据库访问、网络请求等复杂操作 → 应解耦到单独方法中!
self._data = value
四、最佳实践总结
| 原则 | 说明 |
|---|---|
| 始终使用私有变量 | self._x 代替 self.x,避免数据污染 |
| 用途明确的分工 | getter用于读,setter用于写/校验,保持简单逻辑 |
| 禁止在__init__中直接调用属性逻辑 | 初始化时直接操作私有变量,避免触发可能的副作用 |
| 优先维护预存数据 | 牺牲空间换时间(如提前计算 diameter) |
| 保持装饰器方法轻量 | 复杂逻辑移出到其他方法,setter仅负责基础操作 |
五、实战测试题
题目1
修复以下代码:
class Temperature:
def __init__(self, celsius):
self.celsius = celsius # 触发 setter
@property
def celsius(self):
return self.celsius # ❌ 错误所在
@celsius.setter
def celsius(self, value):
self._kelvin = value + 273.15 # 每次设置摄氏度自动更新开尔文温度
答案及解析
**修复代码**:class Temperature:
def __init__(self, celsius):
self._kelvin = celsius + 273.15 # ✅ 直接操作私有变量
@property
def celsius(self):
return self._kelvin - 273.15 # ✅ 用返回式计算
@celsius.setter
def celsius(self, value):
self._kelvin = value + 273.15
错误点:
@property的 getter 方法中return self.celsius导致递归调用__init__方法中通过self.celsius调用属性触发重复逻辑(初始必须直接操作私有变量)
通过理解上述内容,你可以更精准地使用 @property 掌控类属性的行为!
列表
列表是有序的集合
添加:append,extend,insert
L = []
L.append('1')#只能添加一个元素
print(L)#结果为:['1']
L.extend(['2','3','4'])#可添加多个元素,参数必须是一个可迭代对象,追加在原列表最后一个元素后面
print(L)#结果为:['1','2','3','4']
L.insert(0,0)#在下标为0的位置添加数字0
L.insert(len(L),6)#在下标为列表长度的位置添加数字6
删除:del,remove,pop,clear
L = ['1','2']
del L[0]#填入下标
print(L)#结果为:['2']
L.remove('2')#填入指定元素,若元素不存在会报错
print(L)#结果为空
L = ['1','2','3']
L.pop()
print(L)#结果为:['1','2'],默认弹出最后一个元素,并输出结果
L.pop(0)
print(L)#结果为:['2'],可以指定位置弹出元素
L = ['1','2','3']
L.clear()
print(L)#结果为空列表
修改:
L = [1,2,3,4]
L[3] = 5
print(L)#结果为:[1,2,3,5]
排序:sort/sorted,冒泡,选择
sort改版列表本身排序
L = [7,3,4,1,1,2,3]
L.sort()
print(L)#结果为:[1,1,2,3,3,4,7],默认为升序排,想降序排列在sort(reverse=True)
自定义比较函数key,key是一个函数,sort按对每个元素调用该函数的返回值进行从小到大排序
#用例1
def myKey(x):
return x % 10
a = [25, 7, 16, 33, 4, 1, 2]
a.sort(key = myKey)
print(a) #>>[1, 2, 33, 4, 25, 16, 7]
#用例2
res = sorted("This is a test string from Andrew".split(),key = str.lower)
print(res) #>>['a', 'Andrew', 'from', 'is', 'string', 'test', 'This']
#用例3,多级排序
def f(x):
return (-x[2], x[1], x[0])
students = [('John', 'A', 15), ('Mike', 'C', 19), ('Wang', 'B', 12),
('Mike', 'B', 12), ('Mike', 'C', 12), ('Mike', 'C', 18),
('Bom', 'D', 10)]
students.sort(key = f)#按年龄从大到小,再按成绩从高到低,再按姓名
print(students) #>>[('Mike', 'C', 19), ('Mike', 'C', 18), ('John', 'A', 15), ('Mike', 'B', 12), ('Wang', 'B', 12), ('Mike', 'C', 12), ('Bom', 'D', 10)]
#用例4,元组不能修改,因此无sort函数,可以用sorted得到新的排序后的列表
def f(x):
return (-x[2], x[1], x[0])
students = (('John', 'A', 15), ('Mike', 'C', 19), ('Wang', 'B', 12),
('Mike', 'B', 12), ('Mike', 'C', 12), ('Mike', 'C', 18),
('Bom', 'D', 10)) #students是元组
print(sorted(students, key = f)) #sorted的结果是列表
sorted不改变原列表排序
L = [7,3,4,1,1,2,3]
L1 = L.sorted()
print(L1) #>>[1,1,2,3,3,4,7]
print(L) #>>[7,3,4,1,1,2,3]
选择排序
#选择排序,平庸算法
def selection_sort(arr):
n = len(arr)
for i in range(n):
min_idx = i
# 从i+1到末尾找到最小元素
for j in range(i + 1, n):
if arr[j] < arr[min_idx]:
min_idx = j
# 如果min_idx不是当前的位置,则交换位置
if min_idx != i:
arr[i], arr[min_idx] = arr[min_idx], arr[i]
return arr
# 测试用例
arr = [5, 3, 8, 6, 2]
sorted_arr = selection_sort(arr)
print(sorted_arr) # 输出应该为[2,3,5,6,8]
冒泡排序
#冒泡排序
def bubble_sort(arr):
n = len(arr)
for i in range(n):
# 标记本轮是否发生交换(优化关键)
swapped = False
for j in range(0, n - i - 1): # 减少已排序区域的重复比较
if arr[j] > arr[j + 1]:
# 交换相邻元素
arr[j], arr[j + 1] = arr[j + 1], arr[j]
swapped = True
if not swapped: # 若未交换,说明已整体有序,直接退出
break
return arr
# 示例使用
arr = [64, 34, 25, 12, 22, 11, 90]
sorted_arr = bubble_sort(arr)
print("排序后的数组:", sorted_arr) # 输出:[11, 12, 22, 25, 34, 64, 90]
查找:
count,针对某个元素计数,count(sub[,start[,end]])
L = [7,3,4,1,1,2,3]
L.count(1)#结果为2
str1 = '上海自来水来自海上'
L.count('上', 0, 5)#结果为1
index,查看元素的索引值,index(x,start,end),start和end是可选参数,若为找到抛出异常
L = [7,3,4,1,1,2,3]
L.index(4)#结果为:2
L[L.index(4)] = 5#通过索引值替换相关元素,只改变首个
print(L)#结果为:[7,3,5,1,1,2,3]
find,find(x,start,end)若为找到返回值为-1
str2 = '床上女子叫子女上床'
str2.find('女子')
2
copy,浅拷贝(只是拷贝了外层的对象,如果包含嵌套对象,拷贝的只是其引用)
L = [7,3,4,1,1,2,3]
#方法一
L_copy1 = L.copy()
print(L_copy1)#结果为:[7,3,4,1,1,2,3]
#方法二
L_copy2 = L[:]
print(L_copy2)#结果为:[7,3,4,1,1,2,3]
x = [[1,2,3],[4,5,6]]
y = x.copy()
x[1][1] = 0#结果:[[1,2,3],[4,0,6]]
y#结果会被同时修改:[[1,2,3],[4,0,6]]
import copy
x = [[1,2,3],[4,5,6]]
y = copy.copy(x)#copy模块的copy函数
x[1][1] = 0#结果:[[1,2,3],[4,0,6]]
y#结果会被同时修改:[[1,2,3],[4,0,6]]
copy,深拷贝(deepcopy函数将原对象拷贝的同时,也将对象所引用的子对象也拷贝,深拷贝效率低)
import copy
x = [[1,2,3],[4,5,6]]
y = copy.deepcopy(x)#copy模块的copy函数
x[1][1] = 0#结果:[[1,2,3],[4,0,6]]
x#结果:[[1,2,3],[4,0,6]]
y#结果:[[1,2,3],[4,5,6]]
嵌套列表,在列表中嵌套新的列表
matrix = [[1,2,3],[4,5,6],[7,8,9]]
for i in matrix:
for each in i:
print(each, end=' ')
列表和元组的高级用法
- append(x) 添加元素x到尾部
- exten(x) 添加列表x中的元素到尾部,x必须是列表
- insert(i, x) 将元素x插入到下标为i处
- remove(x) 删除元素x
- reverse() 颠倒整个列表
- index(x) 查找元素x,找到则返回第一次出现的下标,找不到则引发异常
列表映射
-
map(function, sequence),可用于讲一个序列(列表、元组、集合...)映射到另一个序列
-
返回一个延时求值对象,可以转换成list,tuple,set...,延时求值对象意思是放着一个操作并未执行
def f(x):
print(x, end = "")
return x*x
a = map(f, [1, 2, 3]) #将列表[1, 2, 3]每个元素去调用f函数的返回值收集起来
print(list(a)) #>>123[1, 4, 9],将a转换为list,此时有输出
print(tuple(a)) #>>(),因为已经经过list操作了,再进行tuple操作时已经为空
a = list(map(lambda x: 2*x, [2, 3, 4]))#>>[4, 6, 8]
print(a)
map用于输入
x, y, z = map(int, input().split()) #>>1 23 45
print(x, y, z) #>> 1 23 45
列表推导式(列表生成式)
列表推导式比循环执行速度要快
结构:[expression(表达式) for target in iterable if condition ] #>>[表达式 for 元素 in 可迭代对象 if 条件]
matrix = [[1,2,3],
[4,5,6],
[7,8,9]]
code = [row[1] for row in matrix]#>>[2,5,8]
code = [matrix[i][i] for i in range(len(matrix))] #>>[1,5,9]
code = [matrix[i][len(matrix)-1-i] for i in range(len(matrix))] #>>[3,5,7]
注意:循环是通过迭代来逐个修改原列表中的元素,列表推导式是创建新列表然后再赋值为原先的变量
B = [[0] * 3] * 3 #创建了一个假二维列表,给其中一个元素赋值则会影响三个元素,此方式三个指针指向同一个值
#可行方式
#方法一:
A = [0] * 3
for i in range(3):
A[i] = [0] * 3
#方法二:
s = [[0] * 3 for i in range(3)]
结构:[expression for target in iterable if condition]
先执行for语句再执行if语句最后执行表达式
#筛选出F字幕开头的元素
words = ["Great","FishC","Briilliant","Excellent","Fantistic"]
fwords = [i for i in words if i[0] == 'F']
结构:[expression for target1 in iterable1
for target2 in iterable2
...
for targetN in iterableN]
[x + y for x in "fishc" for y in "FISHC"]
结果为又称笛卡尔乘积:['fF', 'fI', 'fS', 'fH', 'fC', 'iF', 'iI', 'iS', 'iH', 'iC', 'sF', 'sI', 'sS', 'sH', 'sC', 'hF', 'hI', 'hS', 'hH', 'hC', 'cF', 'cI', 'cS', 'cH', 'cC']
_ = []#无关紧要的变量可以用'_'代替
结构:[expression for target1 in iterable1 if conditon1
for target2 in iterable2 if conditon2
...
for targetN in iterableN if conditonN]
[[x,y] for x in range(10) if x % 2 == 0 for y in range(10) if y % 3 == 0]
结果为:
[[0, 0], [0, 3], [0, 6], [0, 9], [2, 0], [2, 3], [2, 6], [2, 9], [4, 0], [4, 3], [4, 6], [4, 9], [6, 0], [6, 3], [6, 6], [6, 9], [8, 0], [8, 3], [8, 6], [8, 9]]
程序设计原则:KISS(Keep It Simple & Stupid) 简洁胜于复杂
列表过滤
- filter(function, sequence),抽取序列中令function(x)为True的元素x
- 返回一个延时求值对象,可以转换成list,tuple,set...
def f(x):
return x % 2 == 0
lst = tuple(filter(f, [1, 2, 3, 4, 5])) #抽取出偶数
print(lst)#>> 2, 4
二维列表
- 二维列表a可以看作是矩阵,a[i][j]就是第i行第j列的元素
matrix = [[0 for i in range(3)] for i in range(4)] #行为4列为3的二维列表>>[[0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0]]
matrix = [[i*3 + j for j in range(3)] for i in range(2)]#>>[[0, 1, 2], [3, 4, 5]]
列表的拷贝
- 浅拷贝,只拷贝指针,不拷贝指针所指向的内容
a = [1, 2, 3, 4]
b = a[:] #b是a的拷贝,b和a不是同一个对象,只想不同东西,通过切片实现
a = [1, [2]]
b = a[:]
b.append(4)
print(b) #>>[1, [2], 4]
a[1].append(3)
print(a) #>>[1, [2, 3]]
print(b) #>>[1, [2, 3], 1] b也因为a的改动而发生了变化(浅拷贝会导致的问题)未能进行深拷贝
- 深拷贝,既拷贝指针,又拷贝指针所指向的内容
import copy
a = [1, [2]]
b = copy.deepcopy(a)
b.append(4)
print(b) #>>[1, [2], 4]
a[1].append(3)
print(a)#>> [1, [2, 3]]
print(b)#>>[1, [2], 4]
元组
元组元素的指针本质
元组的元素都是指针。元组元素不可被修改,是指不可改变元组元素的指向,但是元组元素指向的内容,是有可能被修改的。元组不能修改也是指,元组这个容器对象不能被修改。
注意:✅ 一切容器(列表、元组、字典)的元素存储都是指针!
但关键区别在于:
- 列表是可变容器,允许修改这些指针的指向(改变元素)或增减元素(修改指针序列)。
- 元组是不可变容器,不允许修改指针序列(固定的指针数组),但指针指向的内容(比如列表元素)是否可以改变,取决于目标对象是否可变。
总结
您的理解需修正为:
- 列表和元组的元素存储本质相同(都是指针)。
- 列表的不可变/可变性由容器本身决定:
- 列表允许修改指针序列(增删改元素)。
- 元组不允许修改指针序列,但元素指向的可变对象可以修改内容。
t = (1,'fishc',3.14) #打包,列表、字符串也有打包和解包的功能
#解包,字符串左边的数量必须和右边序列的元素数量一致,若数量不能精准把控,可采取 a,*b = t
x,y,z = t# 结果:x = 1, y = 'fishc', z = 3.14
a,*b = t# 结果为:x = 1, b = ['fishc', 3.14]
x,y = 10,20 #原理,先进行打包在进行解包: _ = 10,20 -> x,y = _
#元组的元素不能修改,但元素的内容有可能被修改。例如:如果元素是列表,就可以修改该列表。比如:
v = ("hello", [1, 2, 3], [3, 2, 1]) #[1, 2, 3]是列表
v[1] = "world" #>>TypeError: 'tuple' object does not support item assignment
v[1][0] = "world"
print(v) #>>('hello', ['world', 2, 3], [3, 2, 1])
单元素的元组
empty = () #空元组
singleton = 'hello',#注意末尾的逗号,如果没有则是字符串
print(len(empty))#>> 0
print(len(singleton))#>> 1
x = ('hello')
print(type(x)) #>><class 'str'>
x = ('hello',) #无逗号则x为字符串
print(x) #>> ("hello",)
print(type(x)) #>> <class 'tuple'>
a = (1, 2, 3)
x = a
x += (4,5)
print(a,x) #>>(1, 2, 3) (1, 2, 3, 4, 5) 元组不可修改,因此生成的x为新元组,不影响a。x指针方向发生改变。
元组推导式(生成式)
tuple(x * x for x in range(1, 4)) #>>(1, 4, 9)
二维元组
matrix = ((1, 2, 3), (4, 5, 6), (7, 8, 9))#>>((1, 2, 3), (4, 5, 6), (7, 8, 9))
matrix = tuple(tuple(0 for i in range(3)) for i in range(3))#>>((0, 0, 0), (0, 0, 0), (0, 0, 0))
元组列表互转
a = [1, 2, 3]
b = tuple(a) #>>(1, 2, 3)
c = list(b) #>>[1, 2, 3]
t = (1, 2, 3)
(a, b, c) = t #>> a = 1, b = 2, c = 3
s = [1, 2, 3]
[a, b, c] = s #>> a = 1, b = 2, c = 3
字符串
字符串函数
x = "I love FishC"
x.capitalize()#首字母大写
x.casefold()#转换为小写,适用的语言更广泛
x.title()#每个字母开头大写
x.swapcase()#大小写反转
x.upper()#全部转化为大写
x.lower()#转换为小写
x.center(15,"淦")#左右两侧加15字符宽带
x.ljust(15)#左对齐
x.rjust(15)#右对齐
x.zfill(15)#在左侧用0添加多余的宽度
#在字符串中查找子串,返回找到的位置(下标)。找不到的话,find返回-1,index引发异常)
x.find()
x.rfind()
x.index()
x.rindex()
s = "1234abc567abc12"
print(s.find("ab"))#>>4, "ab"第一次出现在下标4
print(s.rfind("ab"))#>>10
print(s.find("12", 4))#>>13 指定下标4处开始查找
try:
s.index("afb")#找不到“afb”因此出现异常
except Exception as e:
print(e)
#replace替换
b = s.replace("abc", "FGHI")
print(b)#>>1234FGHI567FGHI12
print(s)#>>1234abc567abc12
#isdigit() islower() isupper()判断字符串是否是数。是否全是小写等
print("123.4".isdigit(),end=" ") #>>False
print("123.4ABa".islower(),end=" ") #>>False
print("123.4ABC".isupper(),end="") #>>True
#startswith,endswith 判断字符串是否以某子串开头、结尾
print("abcd".startswith("ab"))# True
print("abcd".endswith("ab"))# False
#strip() 返回除去头尾空白字符(空格,'\r','\t','\n')
#lstrip() 返回除去头部(左侧)空白字符后的字符串
#rstrip() 返回除去尾部(右侧)空白字符后的字符串
print(" \t12 34 \n ".strip()) #>>12 34
print(" \t12 34 5".lstrip())#>>12 34 5
#去除头尾'b','a',' ','\n'
print("takeab \n".strip("ba \n")) #>>take
将字符串中的字母大小写互换。
#方法一
for i in string:
if i.isupper():
print(i.lower(), end="")
else:
print(i.upper(), end="")
#方法二
#ord把字符转化为编码(ASCII),chr把编码(ASCII)转化为字符
for i in string:
if 'a' <= i <= 'z':
print(chr(ord(i) - 32), end="")
elif 'A' <= i <= 'Z':
print(chr(ord(i) + 32), end="")
else:
print(i, end="")
字符串的编码和格式化
字符串的编码在内存中的编码是Unicode的,虽然写入文件时可能是gbk或者utf-8
print(ord("a")) #>> 97
print(ord("好")) #>> 22920
print(chr(22920)) #>> 妈
print(chr(97)) #>> a
字符串格式化:
format()方法
"""
字符串格式化{序号:宽度.密度 类型} 宽度可能是0,如:{2:0.4f} 序号为2,宽度为0密度为4类型为小数'f'
> : 右对齐
< : 左对齐
^ : 中对齐
如:{0:>10.4f} 表示第0项是小数,以宽至少是10字符,右对齐(宽度不足时空格补在左边),保留小数点后4位输出
"""
x = "Hello {0} {1:10},you get ${2:0.4f}".format("Mr.", "Jack", 3.2)
#>>Hello Mr. Jack ,you get $3.2000
x = "Hello {0} {1:>10},you get ${2:0.4f}".format("Mr.", "Jack", 3.2)
#>>Hello Mr. Jack,you get $3.2000
print(x)
格式化操作符:%
print('%d转换为八进制是: %o' %(123, 123))#>>123转换为八进制是: 173
使用字符串方法(或正则表达式)从 s = "abc123@def$456ghi789" 中提取所有数字,组成列表。
#方法一
s = "abc123@def$456ghi789"
numbers = re.findall(r'\d+', s) # 匹配所有连续数字
print(numbers)
#方法二
s = "abc123@def$456ghi789"
# 1. 将非数字字符替换为空格单词频率统计
给定一段文本 text = "apple banana apple cherry banana apple",统计每个单词出现次数,并输出按频率降序排列的列表。
示例输出:[('apple',3), ('banana',2), ('cherry',1)]
temp_str = ''.join([c if c.isdigit() else ' ' for c in s])
# 2. 按空格分割字符串,自动忽略空项
numbers = temp_str.split()
print(numbers)
#方法三
s = "abc123@def$456ghi789"
result = []
current = ''
for c in s:
if c.digit():
current += c # 拼接连续数字
else:
if current: # 遇到非数字时保存当前结果
result.append(current)
current = ''
# 处理末尾可能的残留数字
if current:
result.append(current)
print(result)
元组、列表和字符串互转
print(list("hello")) #>>['h', 'e', 'l', 'l', 'o']
print("".join(['a', '44', 'c'])) #>>a44c
print(tuple("hello")) #>>('h', 'e', 'l', 'l', 'o')
print("".join(('a', '44','c'))) #>>a44c
正则表达式
示例1
[1-9]\d* #正整数
-[1-9]\d* #负整数
-?[1-9]\d|0 #整数
[1-9]\d*|0 #非负整数
-?([1-9]\d*\.\d*[1-9]|0\.\d*[1-9]|0) #左右都没有多余0的小数
\w+([-+.]\w+)*@\w+([-.]\w)*\.\w+([-.]\w+)* #邮箱
示例2
import re
text = "物品:5kg大米,500g盐,2L水(注意L不匹配),300ml酱油"
res =re.findall(r'(\d+\s*(?:kg|g|ml))',text) #>>['5kg', '500g', '300ml']
print(res)
#提取字符串中的 日期 和 时间,日期格式为 YYYY-MM-DD,时间格式为 HH:MM(24小时制)。
text = "会议时间:2023-09-15 14:30,地点:305室。提醒:2023/09/16 09:00 无效"
res = re.findall(r'(\d{4}-\d{2}-\d{2})\s+(\d{2}:\d{2})',text)
print(res)
#re.sub(pattern, repl, text) 用正则将文本中的 连续重复汉字 替换为 单个,例如将 哈哈哈哈哈 替换为 哈
text = "这个好好笑哈哈哈!!天气晴晴晴晴朗。"
# 匹配连续重复的汉字(至少2次),替换为单个字处理 Unicode 字符(如中文)时,可用 [\u4e00-\u9fa5] 匹配单个汉字。
result = re.sub(r'([\u4e00-\u9fa5])\1+', r'\1', text)
print(result) # 这个好笑哈!天气晴朗。
text = '<a href="https://example.com" class="link">Example</a>'
pattern = r'<a href="(.*?)".*?>(.*?)</a>'
res = re.findall(pattern, text)
print(res)
id()此BIF函数获取唯一标志的整数值并返回
s = [1,2,3]
id(s)
is和is not 同一性运算符,in和not in 包含运算符,del 删除变量(也可利用切片)
del s #删除了s列表,斩草除根式
s = [1,2,3]
del s[:]#清空列表
max,min 格式max(列表,default=""),enumerate()枚举对象
seasons = ["Spring","Summer","Fall","Winter"]
enumerate(seasons)
list(enumerate(seasons))#结果为:[(0, 'Spring'), (1, 'Summer'), (2, 'Fall'), (3, 'Winter')]
zip()创建一个聚合多个可迭代对象的迭代器
x = [1, 2, 3]
y = [4, 5, 6]
zipped = zip(x, y)
list(zipped)#[(1, 4), (2, 5), (3, 6)]
z = "FishC"
zipped = zip(x, y, z)
list(zipped)#[(1, 4, 'F'), (2, 5, 'i'), (3, 6, 's')]不全面
import itertools
zipped = itertools.zip_longest(x, y, z)
list(zipped)#[(1, 4, 'F'), (2, 5, 'i'), (3, 6, 's'), (None, None, 'h'), (None, None, 'C')]
map()函数会根据提供的函数对指定的可迭代对象的每个元素进行运算,并将返回运算结果的迭代器。
mapped = map(ord,"Fishc")#ord:求出传入字符对应的 unicode编码
list(mapped)#结果:[70, 105, 115, 104, 99]
mapped = map(pow,[2, 3, 10],[5, 2, 3])#计算2**5,3**2,10**3
list(mapped)#结果:[32, 9, 1000]
filter()函数(过滤器)会根据提供的函数对指定的可迭代对象的每个元素进行运算,并将运算结果为真的元素,以迭代器的方式返回
list(filter(str.islower,"FishC"))#结果为:['i', 's', 'h']
迭代器和可迭代对象,一个迭代器肯定是一个可迭代对象,可迭代对象可以重复使用而迭代器则是一次性的
异常是可控的,错误是不可控的
x = [1, 2, 3, 4, 5]
y = iter(x)#迭代器
type(y)#<class 'list_iterator'>
元素调用方法用next()
next(y)#迭代器内元素调用完时会有报异常
...
#解决异常的方法
y = iter()
next(y,"掏空了!")
1
next(y,"掏空了!")
2
next(y,"掏空了!")
3
next(y,"掏空了!")
4
next(y,"掏空了!")
5
next(y,"掏空了!")
'掏空了!'
分组(...)
import re
x = re.search(r'[a-z]+(\d+)[a-z]+', "ab 123d hello553world66")
print(x.group(1)) # >> 553
m = "(((ab*)c)d)e"
r = re.match(m, "abcdefg")
print(r.group(0)) # group(0)==group() 符合整个正则表达式 >>abcde
print(r.group(1)) # 符合第一个分组,最外层(((ab*)c)d)>>abcd
print(r.group(2)) # 符合第二个分组,第二层((ab*)c)>>abc
print(r.group(3)) # 符合第三个分组,第三层(ab*)>>ab
print(r.groups()) # 返回元组 >>('abcd', 'abc', 'ab')
m = r"(((ab*)c)d)e\3" # r表示字符串里的‘\’不再转义
# 要求 ab*cde后面跟着3号分组在本次匹配中匹配上的子串
r = re.match(m, "abbbcdeabbbkfg")
print(r.group(3))
print(r.group())
# 在分组的右边可以通过分组的编号引用该分组所匹配的子串
pt = 'a(.)\\1*b' # 或 pt = r'a(.)\1*b'
print(re.search(pt, 'kacccccb').group()) # >>acccccb
print(re.search(pt, 'kaxxxxb').group()) # >>axxxxb
print(re.search(pt, 'kaxb').group()) # >>axb
x = re.search(pt, 'kaxyb') # >>None
if x != None:
print(x.group())
字典
字典是Python中唯一实现了映射关系的内置类型。键不可重复,值可重复。
- 字典的每个元素是由“键:值"两部分组成,可以根据“键”进行快速查找
- 格式:d =
- 字典元素的值是可复制的,因为也是指针
- 所有元素的键都不相同
- 键必须是不可变的数据类型,比如字符串、整数、小数、元组。列表、集合、字典等可变的数据类型,不可作为字典元素的键
创建字典的方式有六中
a = {"吕布":"口口布", "关羽":"关习习"}
b = dict(吕布="口口布", 关羽="关习习")
c = dict([("吕布","口口布"),("关羽","关习习")])
d = dict({"吕布":"口口布", "关羽":"关习习"})
e = dict({"吕布":"口口布"}, 关羽 = "关习习")
f = dict(zip(["吕布","关羽"],["口口布","关习习"]))
字典的构造
items = [('name', 'Gumby'), ('age', 42)]
d = dict(items)
print(d)#>>{'name': 'Gumby', 'age': 42}
d = dict(name='Gumby', age=42, height=1.76)
print(d)#>>{'name': 'Gumby', 'age': 42, 'height': 1.76}
字典的get方法
get(key, v):如果键key存在,则返回键为key的元素的值,否则返回v,并将此键值对添加到字典
scope = {'a': 3, 'b': 4}
print('b' in scope)#>> True
scope['k'] = scope.get('k', 0) + 1
print(scope['k'])#>>1
scope['k'] = scope.get('k', 0) + 1
print(scope['k'])#>>2
字典的函数
- clear() 清空字典
- keys() 取字典的键的序列
- items() 取字典的元素的序列,可用于遍历字典,在python3.5之前,顺序不确定。3.6及以后,顺序同元素加入字典的顺序一致
- values() 取字典的值序列
- pop() 删除键为x的元素,如果不存在,产生异常
- copy()浅拷贝
注意⚠️:上述“序列”,不是list,tuple或set
d = {'name': 'Gumby', 'age': 42, 'GPA': 3.5}
if 'age' in d.keys():
print(d['age'])#>>42
for x in d.items():
print(x, end=",")#遍历输出元组>>('name', 'Gumby'),('age', 42),('GPA', 3.5),
for i in d.items():
print(f"键为:{i[0]}", end=' ') #键>>键为:name,键为:age,键为:GPA
print(f"值为:{i[1]}", end=' ') #值>>值为:Gumby,值为:42,值为:3.5
x = {'username': 'admin', 1978: [1, 2, 3]}
y = x.copy()
y['username'] = 'mlh'
# 删除元素2
y[1978].remove(2)
print(y)# >>{'username': 'mlh', 1978: [1, 3]}
print(x)# >>{'username': 'admin', 1978: [1, 3]}
x.pop('username') #删除键为'username'的元素
print(x)#>>{1978: [1, 3]}
x.clear()
print(x)#>>{}
fromkeys快速初始化字典值的方法
d = dict.fromkeys("Fishc", 251)#结果:{'F': 251, 'i': 251, 's': 251, 'h': 251, 'c': 251}
#pop删除字典元素
d.pop('s')#弹出值,并删除该键值对
d.popitem()#弹出最后一个值,并删除该键值对
删除
#del
del d #删除字典,重新查找该字典时弹出异常
#clear 清空字典
d.clear()#结果:{}
修改
d = dict.fromkeys("FishC")
d.update({'i':105, 'h':104})#结果:{'F': None, 'i': 105, 's': None, 'h': 104, 'C': None}
d.update(F='70', C='60')#结果:{'F': '70', 'i': 105, 's': None, 'h': 104, 'C': '60'}
查
d.get('c',"这里没有c")#结果:'这里没有c'
d.setdefault('c', "code")#若字典中无该键,则添加新键值对进入字典
items() keys() values()键值对三者的视图对象,视图对象:即字典的动态视图,当字典改变时,三者会同时更新
keys = d.keys() #键
values = d.values()#值
items = d.items()#键值对
list(reversed(d.values()))#将字典按值按倒序排序
字典推导式(字典生成式)
d = {'F':70, 'i':105, 's':115, 'h':104, 'C':67}
b = {v:k for k,v in d.items()}#将键和值互换:{70: 'F', 105: 'i', 115: 's', 104: 'h', 67: 'C'}
d = {x:ord(x) for x in "FishC"}#结果:{'F': 70, 'i': 105, 's': 115, 'h': 104, 'C': 67}
嵌套字典
将学生信息的元组列表转换为嵌套字典:
原始数据:students = [("Alice", 90), ("Bob", 85), ("Charlie", 92)]
要求格式:{'Alice': {'score': 90}, 'Bob': {'score': 85}, ...}
students = [("Alice", 90), ("Bob", 85), ("Charlie", 92)]
result = {name: {'score': score} for name, score in students}
集合
- 元素类型可以不同
- 不会有重复元素
- 可以增删元素
- 整数、小数、复数、字符串、元组都可以作为集合的元素。但是列表、字典和集合等可变的数据类型不可作为集合的元素
- 集合作用是快速判断某个东西是否在一堆东西里面(用in)
集合的构造
a = set([]) #空集合
a = set("abc")
print(a)#字符串转集合>>{'a', 'c', 'b'}
a = set({1:2, 'ok': 3, (3, 4): 4})
print(a)#字典转集合,仅将键转入>>{'ok', 1, (3, 4)}
b = (3, 4)
c = (3, 4)
a = set((1, 2, "ok", 2, b, c))
print(a)#元组转集合去重>>{'ok', 1, 2, (3, 4)}
print(a[2])#>>报错,集合是没有顺序的,不能用下标访问
集合是无序的,因此无法以下标形式进行访问,可通过in or not in 来判断元素是否存在于集合中
{s for s in "FishC"}#>>{'i', 'F', 'C', 'h', 's'}
集合自动去重
set([1, 1, 2, 3, 5])#>>{1, 2, 3, 5}
isdisjoint()判断元素是否有共同元素
s = set("FishC")
s.isdisjoint(set("Python"))#>>False
s.isdisjoint(set("JAVA"))#>>True
是否是子集
s = set("FishC")
s.issubset("FishC.com.cn")#>>True
超集,可填写多个参数
s.issubset("FishC")#True
并集,可填写多个参数
s.union({1, 2, 3})#{'F', 'C', 'i', 'h', 1, 2, 3, 's'}
交集,可填写多个参数
s.intersection("Fish")#>>{'F', 'i', 'h', 's'}
差集,可填写多个参数
s.difference("Fish")#>>{'C'}
对称差集,只能有一个参数
s.symmetric_difference("Python")#{'i', 'y', 'F', 'C', 'P', 'o', 'n', 't', 's'}
不可变集合,frozenset()函数,将元素冻结(frozen)起来
set1 = frozenset({1, 2, 3, 4, 5})
set1.add(6)#报错
Traceback (most recent call last):
File "<pyshell#1>", line 1, in <module>
set1.add(6)
AttributeError: 'frozenset' object has no attribute 'add'
程序或算法的时间复杂度
- 一个程序或算法的时间效率,也称“时间复杂度”,有时简称“复杂度”
- 复杂度通常用大的字母O和小写字母n来表示,比如O(n),O(n²)等。n代表问题的规模
- 时间复杂度是用算法运行过程中,某种时间固定的操作需要被执行的次数和n的关系来度量的。在无序数列中查找某个数,复杂度是O(n)
- 计算复杂度的时候,只统计执行次数最多的(n足够大时)那种固定操作的次数。比如某个算法需要执行加法n²次,除法n次,那么就记其复杂度是O(n²)的。
- 如果复杂度是多个n的函数之和,则只关心随n的增长增长得最快的那个函数
- O(n³ + n²) => O(n³)
- O(2ⁿ + n³) => O(2ⁿ)
- O(n! + 3ⁿ) => O(n!)
- 常数复杂度:O(1)
- 对数复杂度:O(log(n))
- 线性复杂度:O(n)
- 多项式复杂度:O(n^k)
- 指数复杂度:O(aⁿ)
- 阶乘复杂度:O(n!)
- 在无序数列中潮朝某个数(顺序查找) O(n)
- 插入排序、选择排序等庸俗排序方法 O(n²)
- 快速排序 O(n*log(n)) #如python自带的一些内置函数sort,sorted
- 二分查找 O(log(n))
in用于列表和用于字典、集合的区别
a in b
若b是列表,字符串或元组,则该操作时间复杂度为O(n),即时间和b的元素个数成正比
若b是字典或集合,则该操作时间复杂度O(1),即时间基本就是常数,和b里元素个数无关
因此集合用于需要经常判断某个东西是不是在一堆东西里的情况,此种场合用列表替代集合,容易导致超时
文本文件读写
文本文件读写概述
- open函数打开文件,将返回值放入一个变量,例如f
- 用f.write函数写入文本
- 用f.readlines函数读取全部文件内容
- 用f.readline函数读取文件一行
- 用f.close()函数关闭文件
- 用f.read()读取全部文件内容。返回一个字符串,包含文件全部内容
文本文件读写
- 创建文件并写入内容
a = open("c:\\tmp\\t.txt", "w")
# "w"表示写入,用此种方式打开文件,若文件本来存在,就会被覆盖,前提tmp文件得存在不然会创建失败
a.write("good\n")
a.write("好啊\n")
a.close()
- 读取现有文件
# readlines()
f = open("c:\\tmp\\t.txt", "r")
lines = f.readlines() #每一行都带结尾的换行符"\n"
f.close()
for x in lines:
print(x, end="")
# readline()
try:
infile = open(r"c:\\tmp\\t.txt", "r")
while True:
data1 = infile.readline()# data1带结尾的换行符"\n"。空行也有一个字符,就是"\n"
if data1 == "":
break
data1 = data1.strip() #去掉两头空格,包括结尾的"\n"
print(data1)
infile.close()
except Exception as e:
print(e)
- 添加文件内容
f = open("c:\\tmp\\t.txt", "a") # "a"要打开文件添加内容。若文件不存在,就创建文件
f.write("新增行\n")
f.write("ok\n")
f.close()
文本文件的编码
- 常见编码有 gbk和utf-8两个。打开文件时如果编码不对,则不能正常读取文件
- ANSI对应 gbk
- 写入文本时,如果不指定编码,则用操作系统的缺省编码
- Windows: gbk可能,从win10开始是utf-8
- Linux,MacOs: utf-8
- 打开和读取文件建议都明确编码
f = open("c:\\tmp\\t.txt", "a", encoding="utf-8")
open文件名参数的相对路径形式和绝对路径形式
- 相对路径形式:文件名没有包含盘符
open("readme.txt","r") #文件在当前文件夹(当前路径)下
open("tmp/readme.txt","r") #"/"写成"\\"效果也一样,文件在当前文件夹下的tmp文件夹里面
open("tmp/test/readme.txt","r") #文件在当前文件夹下的tmp文件夹里面的test文件夹里面
open("../readme.txt","r") #文件在当前文件夹的上一层文件夹里面
open("../../readme.txt","r") #文件在当前文件夹的上两层文件夹里面
open("/tmp3/readme.txt","r") #文件在当前盘符的根文件夹下的temp3/test/里面
- 绝对路径形式:文件名包含盘符
open("d:/tmp/test/readme.txt", "r") #路径也叫文件夹,或者目录(path, folder, directory)
Python程序的“当前文件夹(当前路径,当前目录)”
- 程序运行时,会有一个“当前文件夹”,open打开文件时,如果文件名不是绝对路径,则都是相对于当前文件夹的
- 一般情况下,.py文件所在的文件夹,就是程序运行时的当前文件夹。在Pycharm里面运行程序,就是如此。
- 程序可以获取当前文件夹:
import os
print(os.getcwd()) #os.getcwd()获取当前文件夹
os.chdir("d:/pythonProject") #在cmd窗口运行python文件,如果有这个语句则会改变文件路径为"c:/tmp",防止cmd窗口默认文件夹有误
print(os.getcwd()) #c:\tmp
命令行参数
以命令行方式运行python程序:
- 每次运行Python程序,都要从pycharm里运行,显然不方便,因此有时需要以命令行方式(命令脚本方式)运行python程序,具体方法如下:
- 在命令行窗口(mac叫“终端”)敲:
- python xxx.py
- 在命令行窗口(mac叫“终端”)敲:
- 如何编写了一个程序hello.py,功能是合并两个文件希望在命令行敲
- python hello.py a1.txt a2.txt就能完成把a2.txt合并到a1.txt上面
#hello.py中的程序内容
import sys
for x in sys.argv:
print(x, end=" ")
#在cmd窗口执行>>python hello.py this is "hello world"
#>>test.py this is hello world
文件处理实例
程序1:统计文章中的单词词频
- 程序名:countfile.py
- 用命令行方式启动程序
python countfile.py #>>源文件 结果文件
例如:
python countfile.py a1.txt r1.txt
python countfile.py c:\tmp\a2.txt d:\tmp\r2.txt
对“源文件”进行单词词频(出现次数)分析,分析结果写入“结果文件”,单词按照字典序排列
import sys
import re
def countFile(filename, words):
try:
f = open(filename, "r", encoding="utf-8")
except Exception as e:
print(e)
return 0
txt = f.read() # 全部文件内容存入字符串txt
f.close()
splitChars = set([]) # 分隔串的集合
for c in txt:
if not(c >= 'a' and c <='z' or c >= 'A' and c <= 'Z'):
splitChars.add(c)
splitStr = "" #用于re.split()的正则表达式
#该表达式形式类似于: ",|:| |-"之类,两个竖线之间的字符串就是分隔符
for c in splitChars:
if c in {'.', '?', '!', '"',"'", '(', ')', '|', '*', '$', '\\', '[', ']', '^', '{', '}'}:
#上面这些字符比较特殊,加到splitChars里面的时候要在前面加“\”
splitStr += "\\" + c + "|" # python字符串中,\\就是\
else:
splitStr += c + "|"
splitStr += " " # '|'后面必须要有东西,空格多些一遍没关系
lst = re.split(splitStr, txt) # lst是分隔后的单词列表
for x in lst:
if x == "":
continue
lx = x.lower()
if lx in words:
words[lx] += 1
else:
words[lx] = 1
return 1
result = {} # 结果字典,格式为{'a': 3, 'about': 3...}
if countFile(sys.argv[1], result) == 0: # sys.argv[1] 源文件
exit()
lst = list(result.items())
lst.sort()
f = open(sys.argv[2], "w")
for x in lst:
f.write("%s\t%d\n" % (x[0], x[1]))
f.close()
函数
函数结构
def 函数名(参数1,参数2,...):
函数体
return 返回值
def 函数名():
函数体
return 返回值
return语句的功能是结束函数的执行,return语句作为函数的出口,可以在函数中多次出现。多个return语句的“返回值”可以不同。在哪个return语句结束函数的执行,函数的返回值就和那个return与距离的“返回值”相等。‘‘返回值’’为元组
#回文函数
#方法一
def is_palindrame(s):
filtered = [c for c in s.lower() if 'a' <= c <= 'z']
if filtered[::-1] == filtered:
return True
else:
return False
#方法二 isalnum判断字符串是否由数字或字母构成,返回值为True/False
def is_palindrame(s):
filtered = [c for c in s.lower() if c.isalnum()]
return filtered == filtered[::-1]
print(is_palindrame("A man, a plan, a canal: Panama"))
#判断是否是素数的函数
def isPrime(n):
if n <= 1 or n % 2 == 0 and n != 2:
return False
elif n == 2:
return True
else:
for i in range(3, n, 2):
if n % i == 0:
return False
if i * i > n:
break
return True
#在100
for i in range(100):
if(isPrime(i)):
print(i,end=" ")
#递归方法求斐波那契数列第n项的函数(斐波那契数列概念,1,1,2,3,5...),数值越大运行速度相交正常算法会慢很多,非常差劲的算法
def fib(n):
if n == 1 or n == 2:
return 1
else:
return fib(n - 1) + fib(n - 2)
print(fib(10))
#汉诺塔问题(Hanoi)三个柱子,A,B,C把A上有n个盘子从大到小叠放,把A上的盘子移动到C,可用B作为过度,过程中也要保持盘子从大到小叠放。
def hanoi(n, src, mind, dest):
if(n == 1):
print(src + "->" + dest)
return
hanoi(n - 1, src, dest, mind)
print(src + "->" + dest)
hanoi(n - 1, mind, src, dest)
n = int(input())
hanoi(n, "A", "B", "C")
#有n级台阶,每一步可以走一级或两级,问有多少种不同的走法
def ways(n):
if n == 1:
return 1
elif n == 2:
return 2
else:
return ways(n - 1) + ways(n - 2)
print(ways(10))
#n阶雪花曲线
def snow(n, size):#n是阶数目,size是长度,从当前起点出发,在当前方向画一个长度为size,阶为n的雪花曲线
if n == 0:
turtle.fd(size)
else:
for angle in [0, 60, -120, 60]:
turtle.left(angle)
snow(n-1, size/3)
turtle.setup(800, 800)
turtle.speed(1000)
turtle.penup()#提笔
turtle.goto(-200, 100)#将笔移动到-300, -50的位置
turtle.pendown()#放下笔
turtle.pensize(3)#笔粗
level = 5
snow(level, 400)#绘制长度为400,阶为3的雪花曲线
turtle.right(120)
snow(level, 400)
turtle.right(120)
snow(level, 400)
turtle.done()#保持绘画窗口
#将一个正整数n表示成一系列正整数之和,n = n1 + n2 +...+ nk,其中n1>=n2>=...nk>=1,k>=1
def ways(n, m):
if n == 0:
return 1
if m == 0:
return 0
w = ways(n, m-1)
if n >= m:
w += ways(n-m,m)
return w
a = int(input())
print(ways(a,a))
闭包
闭包,该概念是由内部函数而来,所以不能再外部函数以外的地方对内部函数进行调用
#会报错,在内部函数中,只能对外部函数的局部变量进行访问,不能进行修改
def funX():
x = 1
funY():
x = x + 1
return x
return funy
#解决办法,在闭包函数中加入nonlocal关键字,类似global
def funX():
x = 1
def funY():
nonlocal x
x = x + 1
return x
return funY
lambda,是一个匿名函数,是一个函数对象,结构是由lambda 变量 : 返回值,返回值可加入if语句判断
def funX(x):
funY(y):
return x + y
return funy
#上述代码可以使用lambda关键字来创建匿名函数
def funX(x):
return lambda y : x + y
#加入if语句的lambda
f = lambda x : x if x % 2 else None
#相当于
def fun1(x):
if x % 2:
return x
else:
return None
内置序列函数
循环
continue 跳过本次循环,回到循环开头位置
循环语句被break后,else语句不会正常执行。内层循环正常跑完,才会执行else。
分隔符 split
语法格式
string.split(str="", num=string.count(str))
str1 = "肖申克的救赎/1994年/9.6分/美国"
str1.split(sep='/')
['肖申克的救赎', '1994年', '9.6分', '美国']
参数说明:
1.str:分隔符,默认为所有的空字符,包括空格、换行、水平制表符、垂直制表符、换页、回车
print("34\t\t45\n7".split()) #>>["34", "45", "7"] #\t是制表符,\n是换行符
print("12 34")
lst = s.split() #>>["12", "34"]
2.num:分隔次数。默认为-1,即分隔所有
#同一行输入两个整数求和
s = input() #>>2 3
numbers = s.split() #>>['2', '3']
print(int(numbers[0]) + int(numbers[1])) #>>5
#str:分隔符,默认为所有的空字符,包括空格、换行、水平制表符、垂直制表符、换页、回车
print("34\t\t45\n7".split()[1][1])#>>5
拼接符 join
和split()方法相反,join(iterable)方法用于拼接字符串:
countries = ['中国', '美国', '俄罗斯', '日本', '韩国']
'-'.join(countries)
'中国-美国-俄罗斯-日本-韩国'
OS模块(Operating System),操作系统模块
import os
os.getcwd()#获取应用程序当前的工作目录
collections模块
collections 是 Python 标准库中一个非常实用的模块,提供了多种高性能、易用的数据结构,可以替代或补充内置的 list、dict、tuple 等类型。以下是系统化的学习路径和核心知识点整理,帮助你高效掌握它。
1. 核心数据结构及用途
1.1 namedtuple
- 用途:创建具有命名字段的元组,增强代码可读性。
- 示例:
from collections import namedtuple Point = namedtuple('Point', ['x', 'y']) p = Point(10, y=20) print(p.x, p.y) # 输出: 10 20 - 应用场景:
- 处理 CSV 或数据库查询结果(字段多时更清晰)。
- 替代简单的类(当不需要方法时)。
1.2 deque(双端队列)
- 用途:高效的在两端添加/删除元素(时间复杂度 O(1))。
- 示例:
from collections import deque dq = deque([1, 2, 3]) dq.appendleft(0) # 左端添加元素 → deque([0, 1, 2, 3]) dq.pop() # 右端删除元素 → 3 - 应用场景:
- 实现队列(FIFO)或栈(LIFO)。
- 滑动窗口算法(如
maxlen参数限制窗口大小)。
1.3 Counter
- 用途:统计可哈希对象的元素频率。
- 示例:
from collections import Counter words = ['apple', 'banana', 'apple', 'orange'] word_counts = Counter(words) print(word_counts.most_common(1)) # 输出: [('apple', 2)] - 核心方法:
update()合并统计结果。elements()生成所有元素的迭代器(按计数重复)。
1.4 defaultdict
- 用途:字典的键不存在时返回默认值(避免
KeyError)。 - 示例:
from collections import defaultdict dd = defaultdict(list) dd['fruits'].append('apple') # 自动初始化空列表 - 常用默认工厂:
int(计数)、list(分组)、set(去重)。
1.5 OrderedDict
- 用途:保持键的插入顺序(Python 3.7+ 后普通
dict也支持,但此类提供额外方法)。 - 独有方法:
move_to_end(key):将键移动到末尾(用于 LRU 缓存)。popitem(last=True):按顺序弹出元素。
1.6 ChainMap
- 用途:将多个字典合并为一个逻辑视图(非物理合并)。
- 示例:
from collections import ChainMap defaults = {'color': 'red', 'size': 'M'} user_settings = {'size': 'L'} combined = ChainMap(user_settings, defaults) print(combined['size']) # 输出 'L'(优先使用 user_settings)
2. 进阶技巧
2.1 性能优化
dequevslist:deque在头尾操作快,list在随机访问快。- 频繁
insert(0, x)或pop(0)时用deque。
2.2 特殊用法
Counter的数学运算:c1 = Counter(a=3, b=1) c2 = Counter(a=1, b=2) print(c1 + c2) # Counter({'a':4, 'b':3}) print(c1 & c2) # 交集 → {'a':1, 'b':1}
2.3 自定义 defaultdict 工厂
- 动态默认值:
def dynamic_default(): return datetime.now() dd = defaultdict(dynamic_default)
3. 常见问题及解决
3.1 何时用 namedtuple 而不是类?
- 当数据不可变且无需方法时,
namedtuple更轻量、更高效。
3.2 defaultdict 的默认值陷阱
- 避免使用可变对象作为默认值(如
defaultdict(list)是安全的,但defaultdict(lambda: [])更显式)。
3.3 Counter 合并时的注意事项
update()是加法操作,若需覆盖用dict.update()。
4. 实战练习
- 文本分析:用
Counter统计一篇文章的词频。 - LRU 缓存:用
OrderedDict实现一个简单的缓存(move_to_end和popitem)。 - 多层配置:用
ChainMap管理默认配置、环境变量和命令行参数的优先级。
5. 扩展学习
- 阅读官方文档:collections — Container datatypes
- 探索源码:理解各数据结构的底层实现(如
deque的双向链表)。
通过结合理论学习和实际编码练习,你会快速掌握这些工具的精髓!
类和对象
类和对象通俗理解教程 🌱
1. 类(Class):设计蓝图 ⚙️
- 定义:类是描述事物特征的模板(可以想象成一张设计图纸)。
- 特点:
- 不存储具体数据,只规定物体应该长什么样、能做什么。
- 例子:
汽车设计图、动物分类标准。
2. 对象(Object):具体实例 🚗
- 定义:对象是根据类的规则创建的实体(基于设计图纸造出的真实物体)。
- 特点:
- 拥有实际数据,每个对象独立保存自己的属性。
- 例子:
你家的小轿车、邻居养的橘猫。
现实类比法 🏭
| 概念 | 类比案例 | 具体解释 |
|---|---|---|
| 类 | 冰淇淋制作模具 | 模具规定冰淇淋的形状、纹理,但不含具体的冰淇淋 |
| 对象 | 做好的草莓冰淇淋 | 根据模具做出的冰淇淋,带有真实的草莓口味和颜色 |
代码实战演示 💻
定义类(设计图纸)
class Dog: # 类名(用大驼峰式命名)
# 构造函数:定义对象必须有哪些属性
def __init__(self, name, age):
# 通过self定义每个对象的属性
self.name = name # 狗狗的名字
self.age = age # 狗狗的年龄
# 方法:定义对象能做什么
def bark(self):
print(f"{self.name} 在叫:汪汪!")
创建对象(实体)
# 根据狗类创建两只狗
dog1 = Dog("旺财", 3) # 旺财是一只3岁的狗
dog2 = Dog("小白", 2) # 小白是一只2岁的狗
# 获取属性
print(dog1.name) # → 旺财
print(dog2.age) # → 2
# 调用方法
dog1.bark() # → "旺财 在叫:汪汪!"
dog2.bark() # → "小白 在叫:汪汪!"
关键概念对比表 🧩
| 内容 | 类(Class) | 对象(Object) |
|---|---|---|
| 本质 | 模板(图纸) | 根据模板创建的实体 |
| 是否占内存 | 否 | 是(每个对象独立存储属性数据) |
| 如何修改 | 修改类的定义影响所有对象 | 修改对象只影响自身 |
| 类比案例 | 身份证的设计规范 | 你和我的身份证(具体实物) |
快速问答测试 ✅
-
类里能不能存我家狗的名字?
→ 不能!类里只规定所有狗有姓名属性,具体名字存在每个对象里。 -
为什么每个方法第一个参数是
self?
→ 当调用dog1.bark(),Python会自动把dog1传给self,让方法知道是谁在叫。 -
对象之间会互相影响吗?
→ 正常不会!修改dog1.age = 5不会改变dog2.age。
实际场景应用 🛠️
案例:学生管理系统
class Student:
def __init__(self, name, score):
self.name = name
self.score = score
def print_info(self):
print(f"{self.name} 的成绩是 {self.score}分")
# 创建学生对象
student_a = Student("张三", 85)
student_b = Student("李四", 92)
# 方法调用
student_a.print_info() # → "张三 的成绩是85分"
student_b.print_info() # → "李四 的成绩是92分"
总结公式 🔑
类 = 图纸(定义属性和方法)
对象 = 实物(保存数据,执行动作)
Python 类中的方法类型总结
1. 实例方法(Instance Method)
- 定义方式:默认不加任何装饰器。
- 首个参数:
self(指向类的实例)。 - 功能:用于操作实例属性,访问或修改实例的状态。
- 调用方式:
- 必须通过类的实例调用。
- 示例:
obj.method()或Class.method(obj)(需要手动传入实例)。
- 典型场景:
class MyClass: def instance_method(self): # 实例方法 print("调用了实例方法,实例属性:", self.value)
2. 类方法(Class Method)
- 定义方式:使用
@classmethod装饰器。 - 首个参数:
cls(指向类本身)。 - 功能:用于操作类属性或构建工厂方法(不需要具体实例)。
- 调用方式:
- 可通过类名直接调用,或通过实例隐式传递类。
- 示例:
Class.class_method()或obj.class_method()。
- 典型场景:
class MyClass: class_attr = "类属性" @classmethod def class_method(cls): # 类方法 print("调用了类方法,类属性:", cls.class_attr) # 可以创建特定配置的实例(工厂模式) return cls() # 返回一个新实例 MyClass.class_method() # 直接通过类调用
3. 静态方法(Static Method)
- 定义方式:使用
@staticmethod装饰器。 - 参数要求:无需
self或cls参数。 - 功能:执行与类相关的工具逻辑,但与类或实例的状态无关(类似普通函数,逻辑上归类到类中)。
- 调用方式:
- 可通过类名或实例触发,不与类或实例绑定。
- 示例:
Class.static_method()或obj.static_method()。
- 典型场景:
class MathUtils: @staticmethod def add(a, b): # 静态方法 return a + b print(MathUtils.add(2, 3)) # 类调用 → 5 print(MathUtils().add(4, 5)) # 实例调用 → 9
关键区别与示意图
| 方法类型 | 装饰器 | 首个参数 | 能否访问类属性? | 能否访问实例属性? | 典型用途 |
|---|---|---|---|---|---|
| 实例方法 | 无 | self |
✅(通过 self.__class__) |
✅ | 操作实例状态 |
| 类方法 | @classmethod |
cls |
✅ | ❌(除非传入实例) | 管理类属性或工厂逻辑 |
| 静态方法 | @staticmethod |
无 | ❌ | ❌ | 与类相关的工具函数 |
常见陷阱与使用建议
-
不要弄混装饰器:
@classmethod必须保留cls参数,否则会导致难以调试的错误。@staticmethod若误加了self或cls参数,会引发异常。
-
何时用静态方法:
- 当方法逻辑只属于类的“命名空间”但无需访问类内部状态时使用静态方法,如数学计算、格式转换等。
反例(错误设计):
class Dog: @staticmethod def bark(): # 静态方法错误使用(需要访问实例状态)! print("Woof!") # 硬编码输出,失去了多态性 # 正确设计应为实例方法: class Dog: def bark(self): print(self.sound) # 根据实例属性动态发声 - 当方法逻辑只属于类的“命名空间”但无需访问类内部状态时使用静态方法,如数学计算、格式转换等。
-
类方法的工厂模式应用:
class Car: def __init__(self, model): self.model = model @classmethod def from_json(cls, json_data): # 根据JSON创建实例的工厂方法 model = json_data.get("model", "Unknown") return cls(model) # 返回一个新实例 car_data = {"model": "Tesla Model S"} car = Car.from_json(car_data) print(car.model) # → "Tesla Model S"
通过实际代码深入理解
示例:统计类的实例数量(类方法 + 类属性)
class Person:
count = 0 # 类属性,统计实例总数
def __init__(self, name):
self.name = name
Person.count += 1 # 每次创建实例时类属性自增
@classmethod
def get_total_count(cls): # 类方法
return cls.count
p1 = Person("Alice")
p2 = Person("Bob")
print(Person.get_total_count()) # → 2 (通过类调用类方法)
print(p1.get_total_count()) # → 2 (效果同上,但实例调用不符合逻辑)
总结
- 实例方法:处理实例状态的逻辑(如修改对象的属性)。
- 类方法:处理类级别的操作(如修改类属性或作为工厂方法)。
- 静态方法:与类相关但与状态无关的辅助逻辑。
根据任务需求选择适当方法类型,合理设计类结构可以让代码更清晰、更易维护。
1. 工厂方法的基本规则 ✅
必要条件:
- 必须返回实例对象 → 通过
类名()或cls()显式构造。 - 存在位置灵活 → 可以是类内的类方法,也可以是独立函数。
示例对比:
| 实现方式 | 代码示例 | 关键点 |
|---|---|---|
| 类方法工厂 | @classmethod + 返回 cls(...) |
动态决定构造类(支持继承) |
| 静态方法工厂 | @staticmethod + 返回 类名(...) |
需硬编码类名 |
| 独立函数工厂 | 外部函数直接返回目标类的实例 | 更灵活但脱离类结构 |
2. 具体验证示例 🔍
① 类方法工厂
class Animal:
def __init__(self, name):
self.name = name
# ✅ 类方法工厂返回 cls() 构造的对象
@classmethod
def create_dog(cls): # cls 即 Animal 或其子类
return cls("狗") # 调用当前类的 __init__
class Dog(Animal):
def bark(self):
print("汪汪!")
# 创建子类对象(若通过子类调用)
dog = Dog.create_dog() # 实际调用的是 Dog 类的 create_dog()
print(dog.name) # → "狗"
dog.bark() # → "汪汪!"
② 独立函数工厂
class Laptop:
def __init__(self, brand):
self.brand = brand
# ✅ 独立函数工厂返回类实例
def create_laptop(brand):
return Laptop(brand) # 直接调用类构造函数
# 使用工厂函数
macbook = create_laptop("Apple")
print(macbook.brand) # → "Apple"
3. 特殊场景探讨 ⚠️
问题:工厂方法是否可以不返回对象?
- 否! 严格来说,工厂方法的 唯一目的是创建并返回对象实例。若不返回对象,则不属于工厂方法。
错误示例:
class Logger:
@classmethod
def setup(cls):
# 仅初始化类属性但不返回对象 → 不是工厂方法!
cls.log_path = "/var/log"
Logger.setup() # 调用后类属性被修改,但未产生新对象
修正为工厂方法:
class Logger:
def __init__(self, path):
self.path = path
@classmethod
def create_default(cls):
default_path = "/var/log"
return cls(default_path) # ✅ 正确构造并返回实例
logger = Logger.create_default()
print(logger.path) # → "/var/log"
总结规则 📜
- 工厂必须生产产品 → 工厂方法必须返回实例对象。
- 工厂可以是任何形式 → 类方法、静态方法、独立函数均可。
- 构造方式统一 → 通过
类名(...)或cls(...)显式创建。
附录:工厂方法设计模式关键图示 🖼️
[工厂接口]
│
▼
(类方法/静态方法/函数)
│
▼
创建具体对象并返回 → [实例对象]
只有严格遵循这一流程,才属于工厂方法。你的理解完全正确 💯!
对象
对象 = 属性 + 方法
封装
通过类把属性和方法打包在一起,然后通过类来生成对象,类中的每一个方法,默认的第一个参数都是self
class D:
def d():
print(123)
d = D()
d.d()
Traceback (most recent call last):
File "<pyshell#42>", line 1, in <module>
d.d()
TypeError: D.d() takes 0 positional arguments but 1 was given
#分析原因,self:传递给方法的就是实力对象本身
class C:
def get_self(self):
print(self)
c = C()
c.get_self()
<__main__.C object at 0x000002223DC6E060>
c
<__main__.C object at 0x000002223DC6E060>
继承
class A:
x = 520
def hello(self):
print("你好,我是A")
class B:
x = 8800
y = 5200
def hello(self):
print("你好我是B")
class C(A, B):
pass
c = C()
c.x
520
c.hello() #访问顺序按从左到右
你好,我是A
多重继承和组合
class Turtle():
def say(self):
print("不积跬步无以至千里")
class Cat():
def say(self):
print("喵喵喵~")
class Dog():
def say(self):
print("呦吼,我是一只小狗")
class Garden:
t = Turtle()
c = Cat()
d = Dog()
def say(self):
self.t.say()
self.c.say()
self.d.say()
g = Garden()
g.say()
不积跬步无以至千里
喵喵喵~
呦吼,我是一只小狗
self实现的功能就是实例对象跟类的方法进行绑定
构造函数 init
class C:
def __init__(self, x, y):
self.x = x#左侧self.x 是绑定到实例化对象里面的x属性,右边是传进来的x参数
self.y = y
def add(self):
return self.x + self.y
def mul(self):
return self.x * self.y
重写
class D(C):
def __init__(self, x, y, z):
C.__init__(self, x, y)
self.z = z
def add(self):
return C.add(self) + self.z
def mul(self):
return C.mul(self) * self.z
d = D(2, 3, 4)
d.add()
9
钻石继承
class A:
def __init__(self):
print("哈喽,我是A~")
class B1(A):
def __init__(self):
A.__init__(self)
print("哈喽,我是B1~")
class B2(A):
def __init__(self):
A.__init__(self)
print("哈喽,我是B2~")
class C(B1, B2):
def __init__(self):
B1.__init__(self)
B2.__init__(self)
print("哈喽,我是C~")
c = C()#classA输出了两次
哈喽,我是A~
哈喽,我是B1~
哈喽,我是A~
哈喽,我是B2~
哈喽,我是C~
'''
改进,利用super(),super()函数能够在父类中搜索指定的方法并绑定self参数,以MRO顺序进行(Method Resolution Order)方法解析顺序。BIF:mro()方法或mro属性__mro__。
例如:
C.mro():
[<class '__main__.C'>, <class '__main__.B1'>, <class '__main__.B2'>, <class '__main__.A'>, <class 'object'>]
C.__mro__:
(<class '__main__.C'>, <class '__main__.B1'>, <class '__main__.B2'>, <class '__main__.A'>, <class 'object'>)
'''
class B1(A):
def __init__(self):
super().__init__()
print("哈喽,我是B1~")
class B2(A):
def __init__(self):
super().__init__()
print("哈喽,我是B2~")
class C(B1, B2):
def __init__(self):
super().__init__()
print("哈喽,我是C~")
c = C()
哈喽,我是A~
哈喽,我是B2~
哈喽,我是B1~
哈喽,我是C~
习题
取得一个变量的类型,视频中介绍可以使用type()和isinstance(),你更倾向于使用哪个?
共同点:两者都可以判断对象类型
不同点:对于一个 class 类的子类对象类型判断,type就不行了,而 isinstance 可以。
type()可以判断未知变量的类型,目前更倾向于用type()
Python3可以给变量命名中文名,知道是为什么吗?
因为3中源文件默认使用的是UTF-8编码,所以变量名也可以使用中文
*** Python的floor除法现在使用“//”实现,那3.0//2.0你目测会显示什么内容?***
结果是不大于结果的最大整数
请用最快速度说出答案:not 1 or 0and 1 or 3 and 4 or 5 and 6 or 7 and 8 and 9
not or and的优先级是不同的:not> and > or,所以按优先级加上括号是:(not 1) or (0 and 1) or (3 and 4)or (5 and 6) or (7 and 8 and 9) = 0 or 0 or 4 or 6 or 9 = 4
爱因斯坦曾出过这样一道有趣的数学题:有一个长阶梯,若每步上2阶,最后剩1阶;若每步上3阶,最后剩2阶;若每步上5阶,最后剩4阶;若每步上6阶,最后剩5阶;只有每步上7阶,最后刚好一阶也不剩。题目:请编程求解该阶梯至少有多少阶?
x = 7
i = 0
flag = 0
while i < 100:
if (x % 2 == 1) and (x % 3 == 2) and (x % 5 == 4) and (x % 6 == 5):
flag = 1
break
i = i + 1
x = 7*i
if flag == 1:
print("最小阶梯数是:",x)
else:
print('在这个范围内未找到符合条件的数!')
*** 猜猜(x < y and [x] or [y])[0]实现什么样的功能?***
这其实是 Python 的作者还没有为 Python 加入三元操作符之前,Python 社区的小伙伴们灵活的使用 and 和 or 搭配来实现三元操作符的功能
*** 你听说过成员资格运算符吗?***
in。用于检查一个值是否在序列中,在就返回True,否则返回False
name = 'allen'
'a' in name #结果为:True
*** 【学会提高代码的效率】你觉得以下代码效率方面怎样?有没有办法可以大幅度改进(任然使用while)***
i = 0
string = 'ILoveFishC.com'
while i < len(string):
print(i)
i += 1
#改进后,主要是不用每次循环都调用len函数。
i = 0
string = 'ILoveFishC.com'
length = len(string)
while i < length:
print(i)
i += 1
*** 有红、黄、蓝三种颜色的求,其中红球 3 个,黄球 3 个,绿球 6 个。先将这 12 个球混合放在一个盒子中,从中任意摸出 8 个球,编程计算摸出球的各种颜色搭配***
print('red\tyellow\tgreen')
for red in range(0,4):
for yellow in range(0,4):
for green in range(0,7):
if red + yellow + green == 8:
print(red, '\t', yellow, '\t', green)
*** 请用一句话描述什么是列表?再用一句话描述什么是元组?***
列表:一个大仓库,你可以随时往里边添加和删除任何东西。
元组:封闭的列表,一旦定义,就不可改变(不能添加、删除或修改)
*** 什么情况下你需要使用元组而不是列表?***
当你需要保存的数据不被轻易更改时,就用元祖
请将下图左边列表的内置方法与右边的注释连线,并圈出元组可以使用的方法
拼接操作符:+
重复操作符:*
逻辑操作符:and,or
成员操作符:in,not in
关系操作符: >,<,<=,>=,==
*** 创建一个元组,什么情况下逗号和小括号必须同时存在,缺一不可?***
>>> tuple1 = ('allen','dragon','小甲鱼','gdut','allegro')
>>> tuple2 = tuple1[:1] + ('love',) + tuple1[1:]
>>> tuple2
('allen', 'love', 'dragon', '小甲鱼', 'gdut', 'allegro')
*** 还记得如何定义一个跨越多行的字符串吗(请至少写出两种实现的方法)?***
>>> str1 = ('all'
'en')
>>> str1
'allen'
>>> str4 = 'all\
en'
>>> str4
'allen'
编写一个函数,判断传入的字符串参数是否为“回文联”
#正确写法
def huiwen(string):
'判断传入的字符串是否为回文联'
list1 = list(string)
list2 = list(reversed(string))
if list1 == list2:
return '是回文联'
else:
return '不是回文联'
print(huiwen('上海自来水来自海上'))
#错误写法
def huiwen(string):
'判断传入的字符串是否为回文联'
list1 = list(string)
list2 = list1.reverse()#返回值为None
if list1 == list2:
return '是回文联'
else:
return '不是回文联'
print(huiwen('上海自来水来自海上'))
在嵌套的函数中,如果希望在内部函数修改外部函数的局部变量,应该使用什么关键字?
#使用 nonlocal 关键字
>>> def outside():
number = 4
def inside():
nonlocal number
number *= number
return number
return inside()
>>> outside()
16
*** 你可以使用filter()和lambda()表达式快速求出100以内所有3的倍数吗?***
>>> list(filter(lambda x : x%3 == 0,range(100)))
[0, 3, 6, 9, 12, 15, 18, 21, 24, 27, 30, 33, 36, 39, 42, 45, 48, 51, 54, 57, 60, 63, 66, 69, 72, 75, 78, 81, 84, 87, 90, 93, 96, 99]
还记得zip吗?使用zip会将两个数以元组的形式绑定在一起
>>> list(zip([1,3,5,7,9],[2,4,6,8,10]))
[(1, 2), (3, 4), (5, 6), (7, 8), (9, 10)]
但是如果我希望打包的形式是灵活多变的列表而不是元组(希望是[[1,2],[3,4],[5,6],[7,8],[9,10]]这种形式),你能做到吗?(采用map和lambda表达式)
>>> list(map(lambda x,y : [x,y],range(1,10,2),range(2,10,2)))
[[1, 2], [3, 4], [5, 6], [7, 8]]
题目 4:筛选列表中的素数
要求:
给定列表 [2,3,4,5,6,7,8,9],生成仅包含素数的子列表。
示例输出:[2,3,5,7]
def rimeNumber(n):
if n < 2:
return False
for i in range(2, int(n**0.5) + 1):
if n % i == 0:
return False
return True
lst = [2,3,4,5,6,7,8,9]
result = [num for num in lst if rimeNumber(num)]
print(result)
单词频率统计
给定一段文本 text = "apple banana apple cherry banana apple",统计每个单词出现次数,并输出按频率降序排列的列表。
示例输出:[('apple',3), ('banana',2), ('cherry',1)]
#方法一O(n) Counter
from collections import Counter
text = "apple banana apple cherry banana apple"
words = text.split()
result = list(Counter(words).items()) # 直接统计并按出现顺序排序(Python 3.7+)
print(result)
#方法二O(n)原生字典
text = "apple banana apple cherry banana apple"
words = text.split()
count_dict = {}
for word in words:
count_dict[word] = count_dict.get(word, 0) + 1 # 累加统计
print(list(count_dict.items()))
#方法三O(n**2)
text = "apple banana apple cherry banana apple"
res = text.split('')
bij = list(set(res))
result = []
for num in bij:
count = 0
for j in res:
if j == num:
count += 1
result.append((num, count))
print(result)
罗马数字包含以下七种字符: I, V, X, L,C,D 和 M。
字符 数值
I 1
V 5
X 10
L 50
C 100
D 500
M 1000
例如, 罗马数字 2 写做 II ,即为两个并列的 1 。12 写做 XII ,即为 X + II 。 27 写做 XXVII, 即为 XX + V + II 。 通常情况下,罗马数字中小的数字在大的数字的右边。但也存在特例,例如 4 不写做 IIII,而是 IV。数字 1 在数字 5 的左边,所表示的数等于大数 5 减小数 1 得到的数值 4 。同样地,数字 9 表示为 IX。这个特殊的规则只适用于以下六种情况: I 可以放在 V (5) 和 X (10) 的左边,来表示 4 和 9。 X 可以放在 L (50) 和 C (100) 的左边,来表示 40 和 90。 C 可以放在 D (500) 和 M (1000) 的左边,来表示 400 和 900。 给定一个罗马数字,将其转换成整数。
def romanToInt(s: str) -> int:
roman_dict = {'I': 1, 'V': 5, 'X': 10, 'L': 50, 'C': 100, 'D': 500, 'M': 1000}
total = 0
prev_value = 0
for char in s[::-1]: # 逆序遍历字符串
current_value = roman_dict[char]
if current_value < prev_value:
total -= current_value
else:
total += current_value
prev_value = current_value
return total
实现简易计算器(支持优先级)
def calculate(expr):
try:
# 直接使用Python的eval函数处理优先级
return eval(expr)
except:
return "Invalid Expression"
print(calculate("5 + 3 * 2"))

浙公网安备 33010602011771号