Python 笔记 (二)

news2025/4/1 13:08:07

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 # stop
		print("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 = 0
	while index < num:
		result = a
		a, b = b, a + b
		yield result # yield
		index += 1

f = 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):
	pass

test1(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 = weather

    def __str__(self):
        return self.date + ' ' + self.name

    def info(self):                 # 实例方法
        self.name='xiaoming'
    
    @property
    def weather(self):              # 属性方法
	    print("getting")
        return self._weather

    @weather.setter
    def 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 True
        else:
            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=42


let 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 + num2
		print("now value", num)
		# num1 += 10 # 直接修改外部变量(环境)实际只是内部重新声明了一个num1和外部无关
		# nonlocal num1 # 必须在所有使用之前声明!! 不能在中途声明
		num1 += 10
	return closure

func = 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 result
    return wrapper
 
def 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 result
	return inner

def logging(flag): # 正确装饰器
	def decorator(fn): # 外部函数
		def inner(num1, num2):
			if flag == "+":
				print("addition")
			elif flag == "-":
				print("subtraction")
			result = fn(num1, num2)
			return result
		return inner
	return 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 = comment
		self.__fn = func

	def __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 = level

	def __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_age

p = 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 = 0

	def get_age(self):
		return self.__age

	def set_age(self, age):
		if age >= 150:
			print("???")
		else:
			self.__age = age

	age = 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 defined

try:
	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 sys

for 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 timesum

PRIMIES = [112272535095293] * 100

def is_prime(n):
	if n < 2:
		return False
	if n == 2:
		return True
	if n % 2 == 0:
		return False
	sqrt_n = int(math.floor(math.sqrt(n)))
	for i in range(3, sqrt_n + 1, 2):
		if n % i == 0:
			return False
	return 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.coloradmin.cn/o/2324800.html

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

相关文章

d2025329

目录 一、修复表中名字 二、患某种疾病的患者 三、最长连续子序列 四、二叉树的层序遍历 一、修复表中名字 1667. 修复表中的名字 - 力扣&#xff08;LeetCode&#xff09; concat(A,B)&#xff0c;将字符串A和B拼接left(str,len)&#xff0c;从字符串左边开始截取len个字…

cordova android12+升级一些配置注意事项

1.以android13为例 Cordova Android 13.0.0 cordova platform remove android cordova platform add android13.0.0Cordova Android 13.0.0 这里建议将android-studio升级到最新 build时若是需要到gradled安装失败 建议多试几次 或者直接用网页下载 找到 Android Studio 的 G…

批量处理word里面表格的空白行

1&#xff0c;随便打开一个word文档。 2&#xff0c;按下Alt F11 VBA编辑器,在左侧的「工程资源管理器」窗口中找到Normal 项目,右键选择插入->模块。 弹出一下弹窗 3&#xff0c;输入一下代码 代码&#xff1a; Sub RemoveEmptyTableRows()Dim tbl As TableDim row As R…

K8S学习之基础五十七:部署代码扫描工具sonarqube

部署代码扫描工具sonarqube 拉取postgres、sonarqube镜像&#xff0c;在harbor上创建postgres、sonarqube项目&#xff0c;将镜像上传至harbordocker pull postgres docker pull sonarqube docker tat postgres:latest 172.16.80.140/postgres/postgres:latest docker tat sona…

音频知识 参数分析

通道布局 参考 通过pcm音频数据计算分贝 理解FFT和信号加窗原理及意义 dts音效大师教程

小型水库大坝安全及水雨情监测技术方案

一、小型水库监测系统构成 小型水库雨水情测报和大坝安全监测系统由水库监测站点、通信网络和监测平台等组成&#xff0c;系统总体架构如图所示。 水库监测站点设施包括&#xff1a;雨量计、水位计、视频监视设备、渗压计、量水堰计、变形监测仪器、数据采集仪、遥测终端、水准…

scala简介和基础语法

Scala简介 Scala 是一门多范式&#xff08;multi-paradigm&#xff09;的编程语言&#xff0c;设计初衷是要集成面向对象编程和函数式编程的各种特性。 Scala 运行在 Java 虚拟机上&#xff0c;并兼容现有的 Java 程序。Scala 源代码被编译成 Java 字节码&#xff0c;所以它可…

‘无法定位程序输入点kernel32.dll’详细的修复方法,一键快速修复kernel32.dll

在 Windows 系统运行过程中&#xff0c;若程序提示“无法定位程序输入点 kernel32.dll”&#xff0c;往往意味着程序调用了 kernel32.dll 中不存在或已变更的函数接口。作为系统的核心动态链接库&#xff0c;kernel32.dll 承担着内存管理、进程控制、文件操作等底层功能&#x…

电源系统的热设计与热管理--以反激式充电器为例

前言 反激电源常用于各种电子设备中&#xff0c;比如充电器、适配器等&#xff0c;它们通过变压器进行能量转换。高温环境可能对电子元件造成影响&#xff0c;特别是像MOSFET、二极管、变压器这样的关键部件&#xff0c;导致效率变低&#xff0c;甚至可能导致功能失效。还有安…

笔记本电脑更换主板后出现2203:System configuration is invalid,以及2201、2202系统错误的解决

笔记本电脑更换主板后启动出现2203:System configuration is invalid,以及2201、2202系统错误的解决 自用的一台ThinkpadT490笔记本电脑 ,由于主板故障,不得不更换主板,通过某宝购置主板后进行了更换。 具体拆卸笔记本可搜索网络视频教程。 注意: 在更换主板时,注意先拍…

项目-苍穹外卖(十七) Apache POI+导出数据

一、介绍 二、入门案例 package com.sky.test;import org.apache.poi.xssf.usermodel.XSSFRow; import org.apache.poi.xssf.usermodel.XSSFSheet; import org.apache.poi.xssf.usermodel.XSSFWorkbook;import java.io.File; import java.io.FileNotFoundException; import jav…

蓝桥杯单片机刷题——E2PROM记录开机次数

设计要求 使用E2PROM完成数据记录功能&#xff0c;单片机复位次数记录到E2PROM的地址0中。每复位一次数值加1&#xff0c;按下按键S4&#xff0c;串口发送复位次数。串口发送格式如下&#xff1a; Number&#xff1a;1 备注&#xff1a; 单片机IRC振荡器频率设置为12MHz。 …

基于盛科CTC7132交换机核心模块

简介 基于盛科CTC7132 SOC方案构建&#xff0c;通过板对板高速连接器引出32路10G SerDes接口、1路PCIex1、2路管理SGMII接口、3路Uart接口&#xff08;1路调试串口2路功能串口&#xff09;、4路I2C接口(2路SOC部分2路PPU部分)、5路SMI接口&#xff08;1路管理口4路业务口&…

How to install OpenJ9 JDK 17 on Ubuntu 24.04

概述 OpenJ9 是一款由 IBM 开发并开源的 Java 虚拟机&#xff08;JVM&#xff09;&#xff0c;现由 ​Eclipse 基金会管理&#xff08;名为 ​Eclipse OpenJ9&#xff09;。它旨在提供高性能、低内存消耗和快速启动时间&#xff0c;特别适用于云原生和容器化环境。 关键特性 …

【即插即用涨点模块-卷积】SPDConv空间深度卷积,助力小目标与低分辨有效涨点【附源码+注释】

《------往期经典推荐------》 一、AI应用软件开发实战专栏【链接】 项目名称项目名称1.【人脸识别与管理系统开发】2.【车牌识别与自动收费管理系统开发】3.【手势识别系统开发】4.【人脸面部活体检测系统开发】5.【图片风格快速迁移软件开发】6.【人脸表表情识别系统】7.【…

全流程剖析需求开发:打造极致贴合用户的产品

全流程剖析需求开发&#xff1a;打造极致贴合用户的产品 一、需求获取&#xff08;一&#xff09;与用户沟通&#xff08;二&#xff09;观察用户工作&#xff08;三&#xff09;收集现有文档 二、需求分析&#xff08;一&#xff09;提炼关键需求&#xff08;二&#xff09;建…

《Python Web网站部署应知应会》No4:基于Flask的调用AI大模型的高性能博客网站的设计思路和实战(上)

基于Flask的调用AI大模型的高性能博客网站的设计思路和实战&#xff08;上&#xff09; 摘要 本文详细探讨了一个基于Flask框架的高性能博客系统的设计与实现&#xff0c;该系统集成了本地AI大模型生成内容的功能。我们重点关注如何在高并发、高负载状态下保持系统的高性能和…

STM32_HAL开发环境搭建【Keil(MDK-ARM)、STM32F1xx_DFP、 ST-Link、STM32CubeMX】

安装Keil(MDK-ARM)【集成开发环境IDE】 我们会在Keil(MDK-ARM)上去编写代码、编译代码、烧写代码、调试代码。 Keil(MDK-ARM)的安装方法&#xff1a; 教学视频的第02分03秒开始看。 安装过程中请修改一下下面两个路径&#xff0c;避免占用C盘空间。 Core就是Keil(MDK-ARM)的…

多线程—JUC(java.util.concurrent)

上篇文章&#xff1a; 多线程—synchronized原理https://blog.csdn.net/sniper_fandc/article/details/146713129?fromshareblogdetail&sharetypeblogdetail&sharerId146713129&sharereferPC&sharesourcesniper_fandc&sharefromfrom_link 目录 1 Calla…

从零开始跑通3DGS教程:(三)坐标系与尺度编辑(CloudCompare)

写在前面 本文内容 本文所属《从零开始跑通3DGS教程》系列文章&#xff1b; sfm重建的点云已经丢掉了尺度信息&#xff0c;并且坐标系跟图像数据有关(SFM初始化选择的图像)&#xff0c;所以如果想恢复物理真实尺度&#xff0c;以及在想要的视角下渲染&#xff0c;那么需要对尺度…