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)

比较运算符

  • ==、>=...

赋值运算符

  • =、+=、-= ...

逻辑运算符(短路运算)

  • 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
  • 如何编写了一个程序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 标准库中一个非常实用的模块,提供了多种高性能、易用的数据结构,可以替代或补充内置的 listdicttuple 等类型。以下是系统化的学习路径和核心知识点整理,帮助你高效掌握它。


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 性能优化

  • deque vs list
    • 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. 实战练习

  1. 文本分析:用 Counter 统计一篇文章的词频。
  2. LRU 缓存:用 OrderedDict 实现一个简单的缓存(move_to_endpopitem)。
  3. 多层配置:用 ChainMap 管理默认配置、环境变量和命令行参数的优先级。

5. 扩展学习

通过结合理论学习和实际编码练习,你会快速掌握这些工具的精髓!

类和对象

类和对象通俗理解教程 🌱


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)
本质 模板(图纸) 根据模板创建的实体
是否占内存 是(每个对象独立存储属性数据)
如何修改 修改类的定义影响所有对象 修改对象只影响自身
类比案例 身份证的设计规范 你和我的身份证(具体实物)

快速问答测试 ✅

  1. 类里能不能存我家狗的名字?
    不能!类里只规定所有狗有姓名属性,具体名字存在每个对象里。

  2. 为什么每个方法第一个参数是self?
    → 当调用 dog1.bark(),Python会自动把 dog1 传给 self,让方法知道是谁在叫。

  3. 对象之间会互相影响吗?
    → 正常不会!修改 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 装饰器。
  • 参数要求:无需 selfcls 参数。
  • 功能:执行与类相关的工具逻辑,但与类或实例的状态无关(类似普通函数,逻辑上归类到类中)。
  • 调用方式
    • 可通过类名或实例触发,不与类或实例绑定。
    • 示例: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 与类相关的工具函数

常见陷阱与使用建议

  1. 不要弄混装饰器

    • @classmethod 必须保留 cls 参数,否则会导致难以调试的错误。
    • @staticmethod 若误加了 selfcls 参数,会引发异常。
  2. 何时用静态方法

    • 当方法逻辑只属于类的“命名空间”但无需访问类内部状态时使用静态方法,如数学计算、格式转换等。
      反例(错误设计)
    class Dog:
        @staticmethod
        def bark():  # 静态方法错误使用(需要访问实例状态)!
            print("Woof!")  # 硬编码输出,失去了多态性
     
    # 正确设计应为实例方法:
    class Dog:
        def bark(self):
            print(self.sound)  # 根据实例属性动态发声
    
  3. 类方法的工厂模式应用

    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"

总结规则 📜

  1. 工厂必须生产产品 → 工厂方法必须返回实例对象。
  2. 工厂可以是任何形式 → 类方法、静态方法、独立函数均可。
  3. 构造方式统一 → 通过 类名(...)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")) 
posted @ 2024-10-28 23:13  Clayborne  阅读(53)  评论(0)    收藏  举报