Python开发中的小问题及解决方法

Python开发中的小问题及解决方法

Pycharm不以pytest方式运行

运行脚本的时候以pytest方式运行时,如果要换回普通模式

  • 点击运行脚本时的配置旁边的展开

python run

  • 点击Edit configurations
  • 删除原有的pytest运行配置
  • 新增Python运行配置
  • 填写运行配置Name
  • 配置python解释器路径
  • 配置要运行的脚本路径
  • 保存即可以普通模式运行

解决urllib.request的HTTP请求在开启代理的情况下,访问502

问题背景:在开启Clash Verge系统代理,且是规则模式,已对访问域名及ip(这里是内网搭建的服务)尝试添加规则,会遇到错误ConnectionResetError: [WinError 10054] 远程主机强迫关闭了一个现有的连接。;但是关闭系统代理后,就能正常执行,目标的域名和ip也能ping的通。

ping的痛验证的是ICMP协议的连通性,并不代表HTTP请求会走同样的路径,系统代理主要影响的是HTTP/HTTPS流量。这个问题是由于urllib.request默认从环境变量或Windows注册表中获取代理设置,这可能回导致请求被代理拦截或路由错误。

由于这里的服务属于内网服务,即使Clash规则设定该域名为直连,开启系统代理后,请求经过代理的服务器无法访问到内网的服务而导致问题的出现。

  • 方法一:

    • 运行脚本前为访问的域名或ip设置环境变量

      1
      2
      3
      set NO_PROXY=<域名>,<内网服务ip>

      例子:set NO_PROXY=030321.xyz,10.10.11.*
    • Pycharm中编辑环境配置,在环境变量中添加NO_PROXY,值同上

  • 方法二:

    • 代码中添加

      1
      2
      3
      4
      5
      6
      7
      8
      9
      import os
      os.environ["no_proxy"] = "<域名>,<内网服务ip>"

      # 或者

      import urllib.request
      opener = urllib.request.build_opener(urllib.request.ProxyHandler({})) # 传空字典关闭代理
      urllib.request.install_opener(opener)
      # 然后再调用 push_to_gateway,这时 urllib 会使用没有代理的新 opener。

双下划线导致名称重整问题

在 Python 中,类中以 __ 开头但不以 __ 结尾的方法或属性会被“名称重整”(name mangling),以防止子类覆盖或外部访问。如

1
2
3
class BotConfigLoader:
def __replace_variable(...):
...

会被编译为

1
_BotConfigLoader__replace_variable

而就会可能遇到(情况:只在一台pc上遇到,其他的没有遇到,但是清理__pycache__缓存后又可以正常调用)

1
2
3
BotConfigLoader.__replace_variable(...)
# 遇到 AttributeError,导致你无法在类内部调用 __replace_variable() 方法。
# 实际上找不到这个方法,因为它被重命名了
  • 解决方案

    • 将方法名改为 _replace_variable,表示“受保护”但不会触发名称重整

    • 使用类名重整后的名称调用;不推荐

      1
      BotConfigLoader._BotConfigLoader__replace_variable(input_str, params_dict)
  • 拓展

    • 使用单下划线 _method 表示“内部使用”,但不触发名称重整;双下划线 __method 仅用于防止子类覆盖或外部访问,适用于类属性而非工具方法;如果希望方法完全私有,考虑将其移入闭包或模块级私有函数

    • 彻底清理__pycache__缓存

      • 项目根目录执行,递归删除所有__pycache__文件夹

        1
        2
        3
        4
        5
        6
        7
        # Linux / macOS
        find . -type d -name "__pycache__" -exec rm -rf {} +

        # Windows PowerShell
        cd "D:\MyProject" # 路径有空格需加引号

        Get-ChildItem -Path . -Recurse -Directory -Filter "__pycache__" | Remove-Item -Recurse -Force
      • 防止以后生成,设置环境变量(建议只在调试或临时环境用,.pyc有助于提高加载速度)

        1
        2
        3
        4
        5
        # Linux / macOS
        export PYTHONDONTWRITEBYTECODE=1

        # Windows PowerShell
        setx PYTHONDONTWRITEBYTECODE 1

python -m http.server访问编码问题

情景:python -m http.server 启动的 HTTP 服务默认会以 ISO-8859-1(Latin-1)编码 来处理静态文件,而不是 UTF-8。导致在浏览器中访问 .js 文件时,中文字符被错误地解析为乱码。实际上使用vscode保存为UTF-8也没有正常加载。

解决方法:使用Node.jshttp-server

1
2
3
4
npm install -g http-server
http-server -c-1 -p 8000
# -c -1:设置缓存控制(Cache-Control)为 no-store,即禁用浏览器缓存。
# -p 8000:指定服务器监听的端口为 8000,默认是 8080。

命令行临时设置代理

把HTTP/HTTPS 请求走本机代理软件的本地端口,临时生效,关掉窗口就会失效

1
2
3
4
5
6
7
8
9
10
# 临时生效
set http_proxy=http://127.0.0.1:7897
set https_proxy=http://127.0.0.1:7897

# 长期生效
setx http_proxy http://127.0.0.1:7897
setx https_proxy http://127.0.0.1:7897

# 验证是否生效
curl -I https://www.google.com

在类体内使用 @property + @classmethod 的组合在3.11弃用/3.13移除

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class TestTriggerCls:
@property
@classmethod
def demo(cls):
return "hello"


print(TestTriggerCls.demo) # ?
obj = TestTriggerCls()
print(obj.demo) # ?

# 期望得到
print(TestTriggerCls.demo) # hello
print(TestTriggerCls().demo) # hello

property 期待的是一个“实例方法”,结果却收到了一个 classmethod 对象,classmethod 不是普通函数,它不能直接调用,导致属性机制没法正常工作。

在 Python 里,property 是一个 描述符类,用来把方法变成属性。
它的构造方法是:

1
property(fget=None, fset=None, fdel=None, doc=None)
  • fget:获取属性时调用的函数(getter)。
  • fset:给属性赋值时调用的函数(setter)。
  • fdel:删除属性时调用的函数(deleter)。
  • doc:属性的文档字符串。

uv启动jupyter并设置中文

1
2
uv pip install jupyterlab-language-pack-zh-CN
uv run --with jupyter jupyter lab

random.choices()随机出现重复元素

这是因为random.choices()的行为是有放回地随机抽取元素,当从列表 l = [1, 2] 中用 random.choices(l, k=2) 抽取两个元素时:

  • 每次抽取后,元素会“放回”列表中,下一次抽取仍然可能抽到同一个元素。
  • 所以 [1, 1][2, 2][1, 2][2, 1] 都是可能的结果。

避免重复可以使用``random.sample(),这是无放回抽取

Mock配置类

Mock(模拟)是一种测试技术,它用一个虚拟的对象(称为Mock 对象)来代替真实对象,能够被开发者控制。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 支持空字典和空可迭代对象的一个Mock配置类
class ConfigMock:
def __getitem__(self, item):
return {}
def get(self, key, default=None):
return default
def keys(self):
return []
def values(self):
return []
def items(self):
return []
def __iter__(self):
return iter([])
def __next__(self):
raise StopIteration

Python关键字

分类 关键字 作用简述
逻辑与布尔 True, False, None 布尔值和空值
条件与循环 if, elif, else, for, while, break, continue 条件控制与循环控制
函数与类 def, class, lambda, return, yield 定义函数/类,返回值,生成器
作用域 global, nonlocal 作用域声明
异常处理 try, except, finally, raise, assert 异常捕获、抛出、断言
导入与模块 import, from, as 模块导入与别名
上下文管理 with 上下文管理器(常配合 open
运算相关 and, or, not, is, in 逻辑与/或/非,身份运算,成员运算
异步与并发 async, await 协程与异步编程
其他语法 pass, del, match, case 占位、删除、结构化模式匹配(3.10+ 引入)

可以打印输出内置库中的对象获取使用的Python版本中包含的关键字

1
2
import keyword
print(keyword.kwlist)
  • globalnonlocal的区别:global 指向的是全局变量,而 nonlocal 指向的是外层函数的局部变量。global 可以在任何地方使用,而 nonlocal 只能在嵌套函数的内层使用。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    # 全局变量
    global_var = 1
    def func1():
    global global_var
    global_var += 1
    func1()
    print(global_var) # 输出2

    # 局部变量
    def func1():
    var1 = 'a'
    def func2():
    nonlocal var1
    var1 += 'b'
    func2()
    print(var1) # 输出ab
    func1()

    # 注意:上面的局部变量部分代码并不是闭包,闭包要满足三个条件,1、嵌套函数:存在一个内部函数嵌套在外部函数中2、引用外部作用域变量:内部函数引用了其外部作用域中的变量,并且这个引用在外部函数执行完毕后依然存在。3、外部函数返回了内部函数
    # 闭包函数
    def func1():
    var1 = 'a'
    def func2():
    nonlocal var1
    var1 += 'b'
    return var1
    return func2
    f = func1()
    print(f()) # 输出ab
    print(f()) # 输出abb

GitSVN 在提交大量小文件修改的性能差异

在使用SVN提交合并大量小文件配置修改时(1~2k)时发现很容易出现网络出错,且处理速度很慢。

特性 Git SVN
提交位置 本地先提交,再推送远程 直接提交到远程服务器
网络依赖 低(本地操作快) 高(每次提交都依赖网络)
并发提交处理 支持本地分支并行提交 容易冲突,需排队提交
文件存储方式 快照(压缩优化) 差异增量存储

这是由于SVN每次提交都需要连接远程仓库,每个文件都要逐个处理差异,过程就会很慢,文件数量巨大的时候,.svn元素据目录也会膨胀,影响性能;但是SVN在处理大文件或二进制文件时更稳定。最后是分批进行多次提交合并解决

而Git是本地提交,几乎不受文件数量影响,使用快照机制,跳过未变更文件,避免重复存储,在处理大量小文件时,提交速度更快。

Python各版本性能差异

来源文章

Python各版本性能差异

从3.7开始,持续使用Cpython进行大量底层改进,性能逐步提升,并且是首次超越2.7;在3.11引入了Adaptive Interpreter(PEP 659)和更多执行效率优化,是性能提升最大的一次;在3.13及以上,支持JIT 和 Free-threading。

  • Adaptive Interpreter(自适应解释器):观察你的程序,针对常用操作生成优化执行策略

    1
    2
    3
    4
    5
    6
    7
    8
    # 假设频繁访问对象属性
    class Person:
    def __init__(self, name):
    self.name = name

    p = Person("Alice")
    for _ in range(10_000_000):
    x = p.name # 热路径操作

    普通解释器:每次访问都要查找字典、检查类型 → 较慢

    Adaptive Interpreter:检测到 p.name 是热点 → 生成专用快速指令 → 直接取值 → 快很多

  • JIT(Just-In-Time 编译、即时编译器):在程序运行时把某些代码片段从字节码(interpreter 执行)编译成机器码(CPU 可直接执行)

    1
    2
    3
    4
    # 热循环
    total = 0
    for i in range(100_000_000):
    total += i

    普通解释器:每次循环都解释执行字节码 → 慢

    JIT 编译后:整个循环生成机器码 → CPU 可以直接执行 → 快很多

  • Free-threading:

    Python 的 GIL(Global Interpreter Lock,全局解释器锁)会限制同一时刻只有一个线程在执行 Python 字节码,导致多线程无法充分利用多核 CPU

    1
    2
    3
    4
    5
    6
    7
    8
    import threading

    def compute():
    sum(i*i for i in range(10**7))

    threads = [threading.Thread(target=compute) for _ in range(4)]
    [t.start() for t in threads]
    [t.join() for t in threads]

    普通 CPython(有 GIL):四个线程依然几乎串行执行

    Free-threading 版本:四个线程可并行执行 → 利用四核 CPU → 速度大幅提升

Git不追踪文件名的大小写变化的问题

Git 在某些操作系统(尤其是 macOS 和 Windows)上默认不追踪文件名的大小写变化,这是因为这些系统的文件系统是大小写不敏感的(case-insensitive)。这会导致在本地修改了文件名的大小写,但 Git 并不会认为这是一次变更,从而不会提交到远程仓库。

场景:hexo将python爬虫基础.md重命名成Python爬虫基础.md,提交到远程仓库成功,但是访问重命名后的路径是404,这是由于在 macOS 或 Windows 上,Git 可能会认为这两个文件是一样的,不会记录这个改名操作。结果就是:

  • 本地预览没问题
  • 部署到 Linux 环境(如 Cloudflare Pages)时,路径大小写敏感,导致找不到文件 → 404

解决方法:将文件内容复制到temp.md,删除原文件并提交和推送,重新修改回原来的文件再次提交即可,Git就会识别出并更新

可选解决方法(不推荐):全局配置,git config core.ignorecase false


Git的变更判断分类

类型 Git 判断方式 是否受文件系统影响
内容变更 通过 SHA-1 哈希对比内容快照
文件名变更 通过文件路径对比(包括大小写) 是(尤其大小写)

Git 是通过内容快照和哈希值来判断文件内容是否变更,但文件名的变更(尤其是大小写)属于元数据变更,Git 的处理方式会受到操作系统文件系统的影响。

  • 为什么大小写变化可能会被忽略

    大小写不敏感的文件系统(如 macOS 默认的 HFS+,Windows 的 NTFS)中:

    • file.mdFile.md 被认为是同一个文件
    • Git 的默认配置 core.ignorecase = true,会忽略大小写变化
    • 所以改了文件名大小写,Git 不会认为这是一次变更 → 不会记录 → 不会提交 → 不会部署