Python 笔记 (二)

Python Note 2

    • 1. Python 慢的原因
    • 2. 三个元素
    • 3. 标准数据类型
    • 4. 字符串
    • 5. 比较大小: 富比较方法 rich comparison
    • 6. 数据容器 (支持*混装* )
      • 一、允许重复类 (list、tuple、str)
      • 二、不允许重复类 (set、dict)
        • 1、集合(set)
        • 2、字典(dict)
        • 3、特殊: 双端队列 deque
      • 三、数据容器的共性
      • 四、生成器 (推导式 + yield关键字)
    • 7. 方法
      • 一、4种方法参数 (「位置参数 `/`、`*` 关键字参数」、不定长参数、缺省参数)
      • 二、方法本身作为参数
      • 三、lambda 函数
      • 四、实例方法,静态方法,类方法
      • 五、函数参数 与 闭包 (Closure)
      • 六、装饰器 (Decorator, 切面作用, 本质是特殊的闭包)
      • 七、property 属性
    • 8. 文件
    • 9. 异常
    • 10. 模块
    • 11. 下划线
    • 12. Typing 模块
    • 13. Python 并发编程
      • 协程 (Coroutine: 协同程序)
      • 信号量 Semaphore

1. Python 慢的原因

  1. 动态类型语言, 边解释边执行
  2. GIL (Global Interpreter Lock, 全局解释器锁)
    1. 为了规避并发问题引入GIL
    2. 引用计数器

2. 三个元素

变量、标识符 identifiers (变量名 函数名 类名)、字面量 (被写在代码中固定的值, 常用: 整数 浮点数 字符串)

  • 不允许使用关键字作为标识符. 如 raise、elif、in、as、except、assert、from、nonlocal
  • 不允许使用自带函数名作为标识符, 会顶替掉函数功能. 如 sum、len

python 是动态类型强类型语言. 决定了变量名没有类型, 类型是运行决定的

  • 强弱: 类型隐式自动转换
  • 动静: 类型检查, 动:运行期or静:编译期
# 30个关键字
and exec not assert finally or break for pass class
from print continue global raise def if return del import
try elif in while else is with except lambda yield

3. 标准数据类型

  • Python 的标准数据类型有:

    • Boolean(布尔值)
    • Number(数字)
    • String(字符串)
    • List(列表)
    • Tuple(元组)
    • Set(集合)
    • Dictionary(字典)
  • Golang 不是面向对象的语言, 没有关键字 Class, 通过 struct、interface 类型实现 OOP 编程风格

    • boolean(布尔值)
    • numeric(数字)
    • string(字符串)
    • 数组(数组)
    • slice(切片:不定长数组)
    • map(字典)
    • struct(结构体)
    • pointer(指针)
    • function(函数)
    • interface(接口)
    • channel(通道)
  • 多变量赋值
    a = b = c = 1

  • 判断相等

    • a == b 值相等
    • a is b id 相等 id(a) == id(b)
    • isinstance(a, int)
    • issubclass(int, object)

4. 字符串

  1. 单引号方式、双引号方式、三引号方式:
    " ‘会显示单引号’ “、 ’ “会显示双引号” '、 “”“字符串3"””

  2. 字符串的输出:

    1. 加号拼接方式: “会返回” + “新的字符串”
    2. 百分号占位符方式: “%s %d %f” % (str(a), int(b), float©)
      1. 占位符精度控制 m.n (注意: 超过 m 会忽略、在 n 处会四舍五入)
    3. 快速格式化方式: f"{name} {variable}" # 不会理会类型, 全转为字符串, 不做精度控制
  3. 字符串输入:

    1. input(“提示信息!”) # 获得的永远都是字符串类型 str
  4. 字符串比较, 头到尾 ->, 其中一位大, 后面就无须比较

  5. True、False 产生: 字面量, 比较运算

  6. 函数没有 return 语句也会返回值: 字面量 None (类型为: <class ‘NoneType’>, 使用场景有: if 判断、变量定义)

5. 比较大小: 富比较方法 rich comparison

  • __gt__ (相反 __lt__)
  1. 当都定义时, 分别调用.
  2. 当另一个未定义时, 未定义的操作会调用已定义的方法取反.
  • __le____ge__
  1. 当都定义时, 分别调用.
  2. 当另一个未定义时, 未定义的操作会调用已定义的方法取反.
  • __eq____ne__
  1. 在自定义类中实现了 __eq____ne__ 这两个方法,则 ==!= 的两个对象比较分别调用了这两个方法进行比较.
  2. 当实现了 __eq__ 方法,未实现 __ne__ 方法时, 未定义的操作会调用 __eq__ 方法取反.
  3. 但实现了 __ne__ 方法,未实现 __eq__ 方法, 则 != 调用 __ne__ 方法,而 __eq__ 调用系统内置的 == 对应的方法
  • is vs ==
    1. is 表示的是对象标示符(object identity)
      相当于 id(a) == id(b), 是检查两个对象是否指向同一块内存空间
    2. == 表示的是相等(equality)
      相当于 a.__eq__(b), 是检查他们的值是否相等
    3. Python 里和 None 比较是 is None
      因为 None 是个单例类型,TrueFalse 也是.
      is== 更严格的检查

6. 数据容器 (支持混装 )

  1. python中的数据容器支持混装: str、int、bool
    1. map、list、set、都可以放入不同类型的变量
  2. 5类数据容器
    1. list [1,2] tuple (1,2) str "12" set {1,2} dict
    2. 字符串也可以作为容器, 支持下标索引 forin or str.split(" ") # 返回list

一、允许重复类 (list、tuple、str)

  1. 打磨: new_str = my_str.strip("12") # 表示前后的 两个字串 1 和 2 都会被去除
  2. 统计: my_str.count("it") # 因为允许重复, 所以可统计出现次数
  3. 不变类型: 字符串只可以存储字符、不可以修改
  4. 序列: 三个 (列表、元组、字符串) 都可被视为 序列
    1. 序列可以进行n切片 [start:end:step] step 默认为1 可以为负倒序执行 左闭右开
    2. 可以连续切片 a[:8][2:5][-1:] # [:8]就是0取到8 在从其中取2到5 最后取-1位 [4]
    3. 切片操作的时候, 超出下标不会报错, 相互矛盾的话不会报错, 返回为空
    4. 取反操作 [::-1] 可取代 reverse()
  5. 批量追加: list.extend(seq) 扩展原来的列表
    1. 在列表末尾一次性追加另一个序列中的值
    2. 无返回值,但会在已存在的列表中添加新的列表内容
  6. 元祖在只有一个元素时必须加逗号 (1,) 记住元祖是不可变对象, 定义是什么就是什么
    1. 只有这样才保证没有歧义
    2. python 在打印时也会加上
    3. 另外, 空的元祖不需要加逗号 ()

二、不允许重复类 (set、dict)

1、集合(set)
  1. 特点:
    1. 无序存储
    2. 不支持下标
    3. 不允许重复数据存在 (重要的去重功能)
    4. 不支持while循环
  2. 常用方法:
    1. set1.union(set2) 不变, 返回并集
    2. set1.difference(set2) 不变, 返回差集
    3. set1.difference_update(set2) set2不变, set1去掉set2中的元素
2、字典(dict)
  1. 特点:
    1. key不重复
    2. 不支持下标
    3. key 和 value 支持任意类型, 但 key 不可为字典类型和可变类型
  2. 使用:
    1. 存放: 字典[key] = value
    2. 取出: 字典.pop(key) (对应 value 删除)
    3. 清空: 字典.clear()
    4. 获取所有key: 字典.keys()
      1. 可用于 for 循环
      2. for f in futures 默认就等于 for f in futures.keys()
    5. 长度: len(字典)
    6. 是否包含: 字典.__contains__(key) or if xx in 字典:
  3. 特殊:
    1. 元祖的key可以是元祖 (包含不可变对象)
    2. 元组只有在其元素是可散列的(不可变类型)情况下,才可以作为字典的key值
    3. e.g. (“unhashable type: ‘list’”, [1, 2, 3], “unhashable type ‘dict’”, {})
3、特殊: 双端队列 deque
  1. 引入: from collections import deque
  2. 使用:
    • append pop extend [+ left]
    • index, insert(1, ?), count(“a”)
    • remove, reverse, rotate, copy

三、数据容器的共性

  1. 都支持 for 循环遍历
  2. 函数: len(容器)max(容器)min(容器)sorted(容器, [reverse=True])

四、生成器 (推导式 + yield关键字)

补充: range() 函数
1. 语法: range(start, stop[, step]) or range(stop) # default: start=0, step=1
2. 区分:
1. python3 range() 返回的是一个可迭代对象 (类型是对象) 打印的时候不会打印列表.
2. python2 range() 返回 列表类型(整数列表)

为什么要使用生成器: 生成器的数据是: 使用一个, 再生成一个, 可以节约大量内存

  • 生成器的使用记住: next(生成器对象) 方法

  • 推导式是一种数据处理方式, 类似于stream流. (从一个数据序列构建另一个新的数据序列)

  1. 列表推导式语法: [表达式 for 变量 in 列表 [if 条件]] 其中:
    1. 迭代 列表 将 变量 传入到 表达式 中.
    2. 表达式可以是有返回值的函数也可以是固定值
    3. if 条件语句, 可以过滤列表中不符合条件的值
  2. 字典推导式语法: { key_expr: value_expr for value in collection [if condition ]}
  3. 集合推导式语法: { expression for item in Sequence [if conditional]}
  4. 生成器推导式语法: (expression for item in Sequence if conditional )
    1. 生成推导式返回的结果是一个生成器对象, 不是元祖
      <generator object <genexpr> at 0x1074abac0>
    2. 使用 tuple 函数: tuple(a), 可以将生成器对象转换成元组
    3. 获取下一个值, 使用 next(genertor) 函数
    4. 遍历生成器中的每一个值. 使用 for i in genertor: 循环
  • yield 关键字
    1. 特征: 在 def 中具有 yield 关键字
    2. 定义:
      1. 函数要有 yield 关键字, 那函数就是生成器了(变成生成器了) ;
      2. yield 可以把一个对象返回出去;
    3. 特点: 保存计算状态 (执行状态); 当然还有节约内存资源!! 每次调用只生成一个数据
    4. 注意点:
      1. 赋值时不会运行函数, 调用时才会进入 for 循环, 并停在 yield 关键字处
      2. 代码执行到 yield 会暂停, 然后把结果返回出去, 下次启动生成器会在暂停的位置继续往下执行
      3. 如果生成器把数据生成完成, 再次获取生成器中的下一个数据会抛出 StopIteration 异常, 表示停止迭代异常
      4. while 循环内部没有处理异常操作, 需要手动添加处理异常操作
      5. for 循环内部自动处理了停止迭代异常, 使用起来更方便, 推荐使用
# yield 生成式
def generator(num):for i in range(num):print("start")yield i # stopprint("next generator")g = generator(100)
print(next(g))
print(next(g))for i in g:print(i)
# 简写
for i in generator(100):print(i)
  • 生成器应用: 斐波那契数列
def fb(num):a, b = 0, 1# 记录生成了几个数字index = 0while index < num:result = aa, b = b, a + byield result # yieldindex += 1f = fb(5)
for i in f:print(i)
# 简写
for i in fb(5):print(i)
  • 深拷贝和浅拷贝 import copy
    • 浅: copy.copy 函数, 浅拷贝最多只拷贝一层
    • 深: copy.deepcopy 函数, 深拷贝可拷贝多层, 直到遇到不可变对象
    • 两种拷贝, 成功都会开辟新的空间存储拷贝的对象, 都不会拷贝不可变对象 (数据安全)

7. 方法

一、4种方法参数 (「位置参数 /* 关键字参数」、不定长参数、缺省参数)

  1. 位置参数顺序必须一致, 和关键字参数混用: 位置参数必须在前面. 因为关键字参数不存在前后顺序
  2. 不定长参数:
    1. *位置传递: def user(*args): 所有参数都会被 args 变量收集, 根据位置顺序合并为一个元祖, args 是 元祖类型. 一般命名为 args
    2. **关键字传递: def user(**kwargs): 参数是 标识符=值 形式的情况下, 所有键值对都会组成字典类型 (注意 key 只能是标识符, 所以不能数字开头). 一般命名为 kwargs (keyword arguments)
  3. 缺省参数: 定义时, 写在后面 def twoSum(self, target: int = 6, /) -> list:
  4. 方法参数里有 /* :
    1. / 之前的参数, 调用时必须都用args方式的位置参数
    2. * 之后的参数, 调用时必须都用kwargs方式的关键字传递 (指定命名调用)
def test1(a, b, /, *, c, d):passtest1(1, 2, c=3, d=4)

二、方法本身作为参数

  1. 本质是计算逻辑的传递, 而非数据的传递
  2. 任何逻辑都可以自行定义并作为函数传入

三、lambda 函数

  1. lambda 关键字和 def 对应, 只不过没有名称和括号, 只能临时使用一次
  2. 语法: lambda 传入参数: 函数体(一行代码)
  3. 函数体只能写一行, 无法写多行代码
  4. 函数赋值给一个变量: dequeue = deque.popleft 调用: greet()

四、实例方法,静态方法,类方法

  1. 实例方法 (方法本身带 self 参数)
    既可以获取构造函数定义的变量,也可以获取类的属性值
    非本类中, 调用实例方法和实例变量, 需要先实例化类对象, 然后才能调用
    本类中, 调用实例方法通过 self.func_name() self 就是本类的实例引用

  2. 静态方法 (需要加 @staticmethod 装饰器)
    不能获取构造函数定义的变量,也不可以获取类的属性
    静态方法不能调用 类变量,类方法,实例变量 及 实例方法, 需要实例化对象, 然后通过对象去调取
    但是实例方法可以调用静态方法, 通过 self.static_func() 调用, 类方法也需要实例化后才能调用静态方法.

  3. 类方法 (需要加 @classmethod 装饰器)
    无需实例化, 不能获取构造函数定义的变量,可以获取类的属性
    在类中, 类方法调用类变量, 通过 cls.cls_param 的方式直接调用

  4. 属性方法 (需要加 @property 装饰器)
    类属性方法无传参且必须有返回值, 实例化对象调用的时候通过调取属性的方式进行调用

  5. 构造函数和析构函数

    1. 构造函数, 实例化自动调用该方法, 一般放些属性及初始化的内容
    2. 析构函数, 类实例被销毁的时自动执行
class Person(): #定义类date = '20201215'               # 类变量def __init__(self, weather = 0):             # self 本类, this对象self.name = 'Stephen'       # 实例变量self._weather = weatherdef __str__(self):return self.date + ' ' + self.namedef info(self):                 # 实例方法self.name='xiaoming'@propertydef weather(self):              # 属性方法print("getting")return self._weather@weather.setterdef weather(self, value):print("setting")if not isinstance(value, int):raise ValueError("weather must be an integer!")           if value < 0 or value > 100:raise ValueError('weather must between 0 ~ 100!')self._weather = value@staticmethod          # 静态方法:校验输入值类型和大小是否符合咱们的类型。def var_str(date_str):year, month, day = tuple(date_str.split("-"))if 0 < int(year) and 0 < int(month) <= 12 and 0 < int(day) < 32:return Trueelse:return False@classmethod                    # 类方法def class_method(cls, date_str):if cls.var_str(date_str):  # 这里是调用静态方法,注意要用 cls.静态方法,原理和实例方法中的self一样,自己可以试试,否则是不能读取的。year, month, day = tuple(date_str.split("-"))return cls(int(year), int(month), int(day))def __del__(self):              # 析构函数,类销毁自动执行print("类销毁的时候就执行")
  1. 拆包
def a(*arg):print(*args) # *args 相当于 print(a, b)def run(**kwargs):#传来的 key = value 类型的实参会映射成kwargs里面的键和值# kwargs是一个字典,将未命名参数以键值对的形式print(kwargs)print("对kwargs拆包")#  此处可以把**kwargs理解成对字典进行了拆包,{"a":1,"b":2}的kwargs字典又# 被拆成了a=1,b=2传递给run1,但是**kwargs是不能像之前*args那样被打印出来看的run1(**kwargs)#print(**kwargs)def run1(a,b): #此处的参数名一定要和字典的键的名称一致print(a,b)
    1. *args作为形参时是用来接收多余的未命名参数,而**kwargs是用来接收key=value这种类型的命名参数,args是元组,kwargs是字典。
    2. *和 ** 在函数体中除了拆包之外,并没有什么卵用。
    3. 装包的含义就是把未命名参数和命名参数分别放在元组或者字典中
  • 类似的有 js 中的解构
import X, { myA as myX, Something as XSomething } from './A'
// X: export default 42
// {}: export const A=42let obj = {a: 1, b: 2};
let {x, y} = obj;
//x undefined
//y undefined
let {a, b} = obj;
//a 1
//b 2
let {b, a} = obj;
//b 2
//a 1
let {a: x, b: y, c: z = {num: 1}} = obj; //a 报错 //b 报错 //x 1 //y 2 
// z 有默认值

五、函数参数 与 闭包 (Closure)

  • 函数参数 (函数可以像普通变量一样作为参数使用)

    • 函数名 存放的是: 函数所在空间的地址
    • 函数名 也可以像普变量一样赋值, 两者等价
    • 函数名() 执行的是函数名背后存放空间地址中的代码
  • 闭包 (Closure 暂时封锁, function closures, 内部函数)

    • 函数用完, 局部变量都会被销毁, 但有时我们需要保留…
      - 闭包就是能够读取其他函数内部变量的函数(函数嵌套)
      - javascript中,只有函数内部的子函数才能读取 局部变量 ,所以闭包可以理解成“定义在一个 函数内部 的函数
    • 闭包的作用: 保存函数内的变量, 不会随着函数调用完而销毁, 提高代码复用性.
      - 在本质上,闭包是将函数内部和函数外部连接起来的桥梁
    • 闭包的定义 (3个):
        1. 函数嵌套的前提下
        1. 内部函数使用了外部函数的变量 (自由变量)
        1. 且外部函数返回了内部函数 (函数被当成对象返回, 闭包则实际上是一个函数的实例,也就是说它是存在于内存里的某个结构体),
      • final: 我们把这个使用外部函数变量内部函数称为闭包
    • 两个关键元素: 闭包意味着同时包括 函数指针环境
    • 关键字 (必须在使用之前声明)
      • nonlocal 非局部变量关键字, 声明是 外部嵌套函数 的变量 (并且不是全局变量)
      • global 全局变量关键字, 如果局部 不声明不修改 全局变量, 也可以不用
    • 支持函数参数赋值的语言一般都支持闭包
def normalFunc(num1):def closure(num2):nonlocal num1 # 只有使用关键字nonlocal后, 修改才会生效.num = num1 + num2print("now value", num)# num1 += 10 # 直接修改外部变量(环境)实际只是内部重新声明了一个num1和外部无关# nonlocal num1 # 必须在所有使用之前声明!! 不能在中途声明num1 += 10return closurefunc = normalFunc(10) # actually: func = (closure + 环境)
func(1) # actually: func() = closure()
func(2)
  • 闭包的原理:
    • 闭包函数相对与普通函数会多出一个 __closure__ 的属性, 里面定义了一个元组用于存放所有的 cell 对象, 每个 cell 对象 (元祖)一一保存了这个闭包中所有的外部变量
printer = make_printer('Foo', 'Bar')  # 形成闭包printer.__closure__   # 返回cell元组 
# (<cell at 0x03A10930: str object at 0x039DA218>, <cell at 0x03A10910: str object at 0x039DA488>)printer.__closure__[0].cell_contents  # 第一个外部变量
# 'Foo'

六、装饰器 (Decorator, 切面作用, 本质是特殊的闭包)

早期函数添加额外功能是这样的: 在函数下方紧接着替换函数签名

def debug(func): # func: 被装饰的目标函数def wrapper():  # 装饰器的本质是闭包, 只不过外部函数入参是函数print "[DEBUG]: enter {}()".format(func.__name__)"""函数执行前"""result = func()"""函数执行后"""return resultreturn wrapperdef say_hello():print "hello!"say_hello = debug(say_hello)  # 使用装饰器装饰函数
  • 装饰器语法糖: @装饰器名称
@debug # 外部函数的名字 (注意一定是闭包), 等于 say_hello = debug(say_hello)
def say_hello():print "hello!"
  • 装饰带有参数的函数
    • 内部函数应该和 被装饰函数 参数一样多
    • 外部函数的 入参函数 也应该和 被装饰函数 一致
  • 装饰带有不定长参数的函数 (特例: 通用装饰器)
    • def inner(*args, **kwargs): fn(*args, **kwargs)
  • 多个装饰器使用: 越靠近函数名的越先包装(靠近的在内层)
  • 带参数的装饰器: 使用时指定参数 @getTime(参数, ...)
    • 装饰器的外部函数只能有一个参数
    • 实际是先执行装饰器函数, 然后再返回装饰器 (中间没有任何执行逻辑)
def decorator(fn, flag): # 错误def inner(num1, num2):if flag == "+":print("addition")elif flag == "-":print("subtraction")result = fn(num1, num2)return resultreturn innerdef logging(flag): # 正确装饰器def decorator(fn): # 外部函数def inner(num1, num2):if flag == "+":print("addition")elif flag == "-":print("subtraction")result = fn(num1, num2)return resultreturn innerreturn decorator # 返回装饰器@loggin("+")
def add(a, b): # 实际: 1. logging("+") 2. add = decorator(add)
  • 类装饰器
    • __call__ 方法
      一个类里面一旦实现了该方法, 那么这个类创建的对象就是一个可调用的对象, 可以像调用函数一样进行调用
class Check(object):def __call__(self, *args, **kwds):print("I'm call method")
c = Check()
c()
  • 类装饰器的定义
class CheckI:def __init__(self, func) -> None: # func = commentself.__fn = funcdef __call__(self, *args: Any, **kwds: Any) -> Any:print("登陆")self.__fn(*args, **kwds) # comment()@CheckI # comment = Check(comment)
def comment():print("发表评论")comment() # comment() 其实就是上面的 c()
"""
登陆
发表评论
"""
  • 带参数的类装饰器
class logging(object):def __init__(self, level='INFO'):self.level = leveldef __call__(self, func): # 接受函数def wrapper(*args, **kwargs):print "[{level}]: enter function {func}()".format(level=self.level,func=func.__name__)func(*args, **kwargs)return wrapper #返回函数@logging(level='INFO')
def say(something):print "say {}!".format(something)
  • 类装饰器总结
    • 想要让类实例对象能够像函数一样调用, 需要在类使用call方法, 把类实例变成可调用对象
    • 类装饰器 装饰 函数功能, 是在call方法里面进行添加的
  • 类装饰器最重要的就是 可继承

七、property 属性

定义property属性有两种方式: 装饰器方式、类属性方式
注意: 属性必须先在 __init__ 里面声明, 不然: "XX" object has no attribute "_XX__age"

  1. 装饰器方式 Decorator
    1. @property 表示把方法当作属性使用, 表示获取属性时会执行下面修饰的方法
    2. 方法名.setter 表示把方法当作属性使用, 表示设置属性时会执行下面修饰的方法
    3. 装饰器方式的 property 属性修饰的方法名一定要一样
class Person:def __init__(self) -> None:self.__age = 0# 装饰器方式@property # @property 表示把方法当作属性使用, 表示获取属性时会执行下面修饰的方法def age(self): # 装饰器方式的 property 属性修饰的方法名一定要一样return self.__age@age.setter # 方法名.setter 表示把方法当作属性使用, 表示设置属性时会执行下面修饰的方法def age(self, new_age):if new_age >= 150:print("???")else:self.__age = new_agep = Person()
# age = p.age()
print(p.age) # 自动调用 @property
p.age = 100 # 自动调用 @setter
  1. 类属性方式 Class
    1. 语法: property属性 = property(获取值方法, 设置值方法)
    2. 第一个参数时获取属性时要执行的方法
    3. 第二个参数时设置属性时要执行的方法
class ClassPerson:"""类属性方式"""def __init__(self) -> None:self.__age = 0def get_age(self):return self.__agedef set_age(self, age):if age >= 150:print("???")else:self.__age = ageage = property(get_age, set_age)

8. 文件

文件的4种操作: 打开、关闭、读、写 常用步骤: open -> read/write -> close
python 内置了文件操作函数

  1. open()
    1. 语法: f = open("python.txt", "r", encoding="UTF-8") # encoding 顺序不是第三位, 只能用关键字参数
      1. name: 文件名或路径名
      2. mode: 文件打开模式 (只读 r、写入 w(原有删除、会创建文件)、追加 a (会创建文件)、etc.)
      3. encoding: 文件编码格式 (推荐使用 UTF-8)
    2. open() 方法返回的是文件对象, 拥有属性和方法
  2. read()
    1. 语法: 文件对象.read(num) # 不填, 表示读取所有数据
    2. readlines() 方法: 一次性读取, 每行数据合成列表
    3. 注意文件指针的位置
    4. readline() 方法: 一次只读取一行
    5. for 遍历: for line in open("README.md", "r"): or for line in f: # for 遍历和 readline() 有关
  3. close() 关闭文件对象
    1. with open 语法: with open("READEME.md", "r") as f: 在 with open 块中对文件进行操作, 结束后自动close文件
  4. write()
    1. mode 必须有 w, f = open(“xx.txt”, “w”) # 在写入模式下文件不存在会自动创建 (反面: x 新建存在会报错)
    2. 语法1: f.write(“hello world”) # 文件写入
      语法2: f.flush() # 内容刷新. close() 内置了flush() // 作用: 多次写入, 一次输出

time.sleep(5) # 5s

9. 异常

  • 处理异常 (异常可以传递)
try:print(name)
except [NameError as e]:print("occur except")print(e) # name 'name' is not definedtry:print(1/0) # ZeroDivisionError: division by zero
except (NameError, ZeroDivisionError) as e:print("ZeroDivision or Name Error")# 完整语法
try:print(1/0)
except Exception as e:print("出现异常, 原因: ", e)
else: # 可选print("没有发生任何异常")
finally: # 可选f.close()
# 简洁语法
import sysfor line in sys.stdin:try:a = line.split()print(int(a[0]) + int(a[1]))except:continue
  • 抛出异常
if current is None:raise ValueError("是你下标过了头")
# or
assert current is not None, "你下标过头了" # 该异常是 AssertionError

10. 模块

  • 只能找到当前目录, 以及子目录的包, 找不到上层目录
  1. 导入语法: [from 模块名] import [模块|类|变量|函数|*] [as 别名]
  • case: from time import time as ti, sleep
  1. 使用语法: 模块名.功能名()
  • case: sleep(0.5)now = ti()
  1. 自定义模块: 每个python 文件都可以作为一个模块, 模块的名字就是 文件名
    1. 自定义模块名必须符合标识符命名规则

注意:

  1. 导入多个模块, 模块内有同名功能时, 调用到的是: 后导入的模块的功能
    1. 可以使用 as 区分同名模块
  2. 测试模块: 导入模块的单元测试要写: if __name__ == "__main__": 因为在导入时会自动调用该模块, 无论是导入其中的函数还是变量都会执行 (__name__ 变量执行本文件时为main, 模块调用时为文件名)

__name__ 变量. 当 Python 解释器读取一个源文件时,它首先定义了一些特殊的变量。在这种情况下,我们关心__name__变量。当你的模块是主程序时, 解释器会将硬编码的字符串分配给"__main__"变量__name__. 当您的模块被另一个模块导入时, 解释器将搜索您的foo.py文件 (以及搜索其他一些变体),并且在执行该模块之前,它会将"foo"导入语句中的名称分配给__name__变量.

__all__ 变量 (列表类型). 当该变量存在时, import * 只能导入这个列表中的元素.
注: __all__ 未限制 单独指定 的方式

11. 下划线

非官方

  • 前置单下划线:_var (仅在内部使用, 变量或方法, 只有约定含义)
  • 后置单下划线:var_ (绕过命名冲突, 规避关键字占用, 只有约定含义)
  • 常量: CONST_VAR (使用全大写, 只有约定含义)
    官方
  • 前置双下划线:__var (私有, 变量会被名称改写 (name mangling), __baz -> _Test__baz 防止变量在子类中被重写, 子类的子类 _ExtendedTest__baz)
  • 前后置双下划线:__var__ (一般 python 内置函数保留)
  • 单下划线:_
    1. 代表不关心, 循环中不需要访问正在运行的索引,可以使用 " _ " 来表示它只是一个临时值: for _ in range(32):.
    2. Python REPL中的一个特殊变量,表示由解释器最近一个表达式的结果. 获取命令行上一条语句执行的结果

12. Typing 模块

Python是一种动态类型语言,这意味着我们在编写代码的时候更为自由,运行时不需要指定变量类型, 但是同时 IDE 也无法像静态类型语言那样分析代码,及时给出相应提示,如字符串的 split() 方法提示.

作用: 帮助 IDE 为我们提供更智能的提示. 特性不会影响语言本身, 是一个规范约束,不是语法限制

  1. Union vs TypeVar
    U = Union[int, str]
    def union_func(arg1: U, arg2: U) -> U:
    union_func(1, “s”) # all right
    result: Any = union_func(1, 2) # correct, but return int? or str?

T = TypeVar("T", int, str)
def typevar_func(arg1: T, arg2: T) -> T:
typevar_func(1, “s”) # error
result: int = typevar_func(1, 2) # correct, and return always int

  1. Optional
    必须跟一个参数, 且只能是一个参数. 给个提示作用, 代表可以不传 (实际必须使用 None 占位)
    def foo_func(arg: Optional[int] = None):
    等效于: def foo_func(arg: int = None):
  • 类中:
class BasicStarship:captain: str = 'Picard'               # 实例变量,有默认值damage: int                           # 实例变量,没有默认值stats: ClassVar[Dict[str, int]] = {}  # 类变量,有默认值
  1. ClassVar
    是 typing 模块的一个特殊类. 向静态类型检查器指示, 不应在类实例上设置此变量

13. Python 并发编程

多线程和多进程两者的 API 几乎一致
![[多进程.png]]

  • 多进程适合 cpu 密集型计算

  • 多线程适合 io 密集型计算

  • 线程池不适合长时间占用

  • 可等待的对象分为三种类型:协程、任务 和 Future.

    • 当遇到可等待对象,进程会发生上下文切换(任务调度)
  • CPU 密集型使用多进程实例:

import math
from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutor
from util.Util import timesumPRIMIES = [112272535095293] * 100def is_prime(n):if n < 2:return Falseif n == 2:return Trueif n % 2 == 0:return Falsesqrt_n = int(math.floor(math.sqrt(n)))for i in range(3, sqrt_n + 1, 2):if n % i == 0:return Falsereturn True@timesum
def single_thread():for number in PRIMIES:# print(is_prime(number))is_prime(number)@timesum
def multi_thread():with ThreadPoolExecutor() as pool:pool.map(is_prime, PRIMIES) # 多线程甚至拖慢了速度@timesum
def multi_process():with ProcessPoolExecutor() as pool:pool.map(is_prime, PRIMIES) # 提升了 6 倍if __name__ == '__main__':single_thread()multi_thread()multi_process()""">>开始计时!
<<耗时 23.292997 s
>>开始计时!
<<耗时 23.495053 s
>>开始计时!
<<耗时 4.086435 s"""

协程 (Coroutine: 协同程序)

  • 在单线程内实现并发
    • 核心原理1: 用一个超级循环 (其实就是while true:) The one loop
    • 核心原理2: 配合 I/O 多路复用原理 (IO时 CPU 可以干其他事情)
  • 单线程异步爬虫, 很多时候是快于多线程的
    • 多线程不同线程调度的切换, 本身是耗费时间的
    • 没有上下文切换的开销, 相对来说更快

协程

import asyncio# 获取事件循环
loop = asyncio.get_event_loop()# 定义协程
async def myfunc(url): # async 说明函数是一个协程await get_url(url) # await 对应 IO, 执行到这里不进行阻塞, 超级循环直接进入下一个程序的执行# 创建task列表
tasks = [loop.create_task(myfunc(url))]# 执行爬虫事件列表
loop.run_until_complete(asyncio.wait(tasks)) # 执行直到完成/ 等待任务的完成# 补充
## 运行
asyncio.run(main())
asyncio.run(asyncio.gather(main(),main2()))
  • 协程异步注意事项:
  1. 要用在异步IO编程中, 依赖的库必须支持异步IO特性

  2. 到达await关键字,发生上下文切换

  3. 不使用 await 函数不会暂定现场,不会再回到该调用点

  4. 当一个协程通过 asyncio.create_task() 等函数被封装为一个 任务, 该协程会被自动调度执行

  5. 通常情况下 没有必要 在应用层级的代码中创建 Future 对象。这是一种特殊的 低层级 可等待对象

信号量 Semaphore

  • 异步IO中使用信号量控制爬虫并发度
    • Semaphore 信号量、旗语
      • 是一个同步对象, 用于保持在 0 至 指定最大值之间的一个计数值
      • 完成一个对该 semaphore 对象的等待 (wait) 时, 该计数值减一 (进入并发处理)
      • 完成一个对该 semaphore 对象的释放 (release) 时, 该计数值加一 (完成 释放)
      • 当计数值为 0, 则线程等待该 semaphore 对象不再能成功, 直至该 semaphore 对象变为 signaled 状态 (为0等待, signaled: 已发信号状态)
      • semaphore 对象的计数值大于 0, 为 signaled 状态, 计数值等于 0, 为 nonsignaled 状态,

信号量

爬虫引用中的 requests 不支持异步, 需要使用 aiohttp

netty

每个eventloop就是1个thread, 每个channel类似于1个协程.
进一步思考, 这跟Linux的epoll模型是否很类似

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/73856.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

kill子进程后再wait可以吗?

在父进程中先使用 kill 函数终止子进程&#xff0c;之后再使用 wait 函数是可行的&#xff0c;下面从原理、使用示例、注意事项几个方面详细说明。 原理 kill 函数&#xff1a;其作用是向指定进程发送信号。当向子进程发送 SIGTERM&#xff08;通常用于请求进程正常终止&…

ai-api-union项目,适配各AI厂商api

项目地址&#xff1a;alpbeta/ai-api-union 需求&#xff1a;实现兼容各大模型厂商api的流式对话和同步对话接口&#xff0c;本项目现兼容智谱、豆包、通义、通义版deepseek 设计 一个ChatController类对外暴露这两个接口&#xff0c;入参都为ChatRequest请求类&#xff0c;…

【QT】QT样式设计

QT样式设计 一、QT工程中添加资源文件1.资源文件&#xff1a;2. 添加步骤&#xff1a;3. 新增资源文件以及删除现有的资源文件4. 使用资源文件 二、QT中的qss语句(样式设计语句)1. 样式设计2.常见的qss语句示例代码&#xff1a; 一、QT工程中添加资源文件 1.资源文件&#xff…

Megatron-LM中的deepseek-v3实现

Megatron-LM&#xff1a;https://github.com/NVIDIA/Megatron-LM/tree/main 使用此仓库构建的著名的库也有很多&#xff0c;如: Colossal-AI, HuggingFace Accelerate, and NVIDIA NeMo Framework.Pai-Megatron-Patch工具是阿里人工智能平台PAI算法团队研发,ai-Megatron-Patch…

[mlr3] Bootstrap与交叉验证k-fold cross validation

五折交叉验证因其无放回分层抽样和重复验证机制&#xff0c;成为超参数调优的首选&#xff1b; 而Bootstrap因有放回抽样的重复性和验证集的不稳定性&#xff0c;主要服务于参数估计&#xff08;置信区间的计算&#xff09;而非调优。 实际应用中&#xff0c;可结合两者优势&am…

某大麦手机端-抢票

引言 仅供学习研究&#xff0c;欢迎交流 抢票难&#xff0c;难于上青天&#xff01;无论是演唱会、话剧还是体育赛事&#xff0c;大麦网的票总是秒光。作为一名技术爱好者&#xff0c;你是否想过用技术手段提高抢票成功率&#xff1f;本文将为你揭秘大麦手机端抢票的核心技术…

最常使用的现代C++新特性介绍

现代 C泛指的是从 C11 之后的 C标准. 从 C11 开始, C标准委员会实行班车制, 没三年发布一个新版本, 如果一个功能在新版本发布之前已经准备好, 则可以加入该版本中, 否则延后到下一个版本. 语言核心 自 C11 开始, 语言语法层面加了许多语法糖, 还有增加了一些新语法.使得 C语…

SQL Server:当在删除数据库时因为存在触发器而无法删除

当在删除数据库时因为存在触发器而无法删除&#xff0c;你可以通过禁用触发器来解决这个问题。下面为你介绍在 SQL Server 里禁用和启用触发器的方法。 禁用数据库中所有表的触发器 你可以使用系统视图 sys.triggers 来查询数据库里所有的触发器&#xff0c;然后生成禁用这些…

【Linux篇】进程入门指南:操作系统中的第一步

步入进程世界&#xff1a;初学者必懂的操作系统概念 一. 冯诺依曼体系结构1.1 背景与历史1.2 组成部分1.3 意义 二. 进程2.1 进程概念2.1.1 PCB&#xff08;进程控制块&#xff09; 2.2 查看进程2.2.1 使用系统文件查看2.2.2 使⽤top和ps这些⽤⼾级⼯具来获取2.2.3 通过系统调用…

销售易vs纷享销客:制造行业CRM选型深度解析

“以客户为中心”&#xff0c;顾名思义就是指让客户贯穿企业市场、研发、生产、销售、服务全流程&#xff0c;以客户需求为导向进行经营。CRM作为企业数字化建设基础设施&#xff0c;在企业高质量发展进程中扮演着重要角色。在众多CRM解决方案中&#xff0c;腾讯旗下CRM销售易凭…

【JavaScript】九、JS基础练习

文章目录 1、练习&#xff1a;对象数组的遍历2、练习&#xff1a;猜数字3、练习&#xff1a;生成随机颜色 1、练习&#xff1a;对象数组的遍历 需求&#xff1a;定义多个对象&#xff0c;存数组&#xff0c;遍历数据渲染生成表格 let students [{ name: 小明, age: 18, gend…

代码随想录day31 贪心part05

56.合并区间 以数组 intervals 表示若干个区间的集合&#xff0c;其中单个区间为 intervals[i] [starti, endi] 。请你合并所有重叠的区间&#xff0c;并返回 一个不重叠的区间数组&#xff0c;该数组需恰好覆盖输入中的所有区间 。 示例 1&#xff1a; 输入&#xff1a;in…

《C++11:通过thread类编写C++多线程程序》

关于多线程的概念与理解&#xff0c;可以先了解Linux下的底层线程。当对底层线程有了一定程度理解以后&#xff0c;再学习语言级别的多线程编程就轻而易举了。 【Linux】多线程 -&#xff1e; 从线程概念到线程控制 【Linux】多线程 -&#xff1e; 线程互斥与死锁 语言级别的…

c++位运算总结

在C中&#xff0c;位运算是对二进制位进行操作的运算&#xff0c;主要有以下几种&#xff1a; 1. 按位与&#xff08; & &#xff09;&#xff1a;两个操作数对应位都为1时&#xff0c;结果位才为1&#xff0c;否则为0。例如 3 & 5 &#xff0c; 3 二进制是 0000 0011…

1.1 计算机网络的概念

首先来看什么是计算机网络&#xff0c;关于计算机网络的定义并没有一个统一的标准&#xff0c;不同的教材有 不同的说法&#xff08;这是王道书对于计算机网络的定义&#xff09;&#xff0c;我们可以结合自己的生活经验去体会这个 定义。 可以用不同类型的设备去连接计算机网络…

用LLama factory时报类似Process 2504721 got signal: 1的解决方法

之前用nohup来远程跑LLama factory微调脚本&#xff0c;是没有问题的&#xff0c;但今天发现运行类似下面这个命令时&#xff0c; nohup llamafactory-cli train examples/train_qlora/qwen_lora.yaml 只要一关闭ssh session&#xff0c;就会终止训练&#xff0c;报类似&…

python常用内置时间函数+蓝桥杯时间真题

1.time 1.1 time.time() 时间戳指&#xff1a;1970年1月1日开始到现在所经过的秒数 import time print(time.time()) # 输出可得1970年1月1日开始到执行此代码所经过的秒数 1.2 time.localtime() 返回一个当前时间的时间对象&#xff0c;具体信息&#xff0c;并且可以单独…

一个用 C 语言打印出所有三位数水仙花数的程序

水仙花数&#xff08;Narcissistic number&#xff09;是指一个三位数&#xff0c;其各位数字的立方和等于该数本身。例如&#xff1a;153 是一个水仙花数&#xff0c;因为 (1^3 5^3 3^3 153)。 以下是一个用 C 语言打印出所有三位数水仙花数的程序&#xff1a; 代码实现 …

利用 VSCode 配置提升 vibe coding 开发效率

利用 VSCode 配置提升 vibe coding 开发效率 Vibe Coding&#xff08;氛围编程&#xff09;是一种基于AI的编程方法&#xff0c;其核心在于通过自然语言描述软件需求&#xff0c;再由大规模语言模型&#xff08;LLM&#xff09;自动生成代码&#xff0c;从而实现对传统手写编程…

练习题:110

目录 Python题目 题目 题目分析 需求理解 关键知识点 实现思路分析 代码实现 代码解释 函数定义&#xff1a; 计算值的总和&#xff1a; 测试函数&#xff1a; 运行思路 结束语 Python题目 题目 定义一个函数&#xff0c;接受一个字典作为参数&#xff0c;返回字…