Python导入包顺序

  1. 标准库导入
  2. 相关第三方库导入
  3. 本地应用/库的导入,按照字母顺序

配置pip源

Linux系统

执行命令即可

pip config set global.index-url https://mirrors.aliyun.com/pypi/simple/

Windows系统

  1. C:\Users\Administrator下新建pip文件夹

  2. 在文件夹里新建pip.ini文件,并写入如下内容

[global]
index-url = https://pypi.tuna.tsinghua.edu.cn/simple/

[install]
trusted-host = pypi.tuna.tsinghua.edu.cn

*args与*kwargs 的区别

*args 用于传递可变数量的非关键字参数,以元组的形式传递。

*kwargs 用于传递可变数量的关键字参数,以字典的形式传递。

def example_function(*args, **kwargs):
print("Non-keyword arguments:")
for arg in args:
print(arg)
print("\nKeyword arguments:")
for key, value in kwargs.items():
print(f"{key}: {value}")

# 调用函数
example_function("apple", "banana", "cherry", fruit1="orange", fruit2="kiwi")

# The result of the example code is:
# Non-keyword arguments:
# apple
# banana
# cherry

# Keyword arguments:
# fruit1: orange
# fruit2: kiwi

装饰器

装饰器是Python的一个特性,可以让我们在不修改原函数的情况下,动态增加函数的功能。

其本质上装饰器是一个函数,它接收一个函数的输入,返回一个新函数的输出。

装饰器的使用

def my_wrapper(func):
"""
Dao层的捕获错误装饰器
:param func:
:return:
"""

def wrapper(*args, **kwargs):
try:
res = func(*args, **kwargs)
print("this is wrapper", res)
return res + 1
except Exception as err:
print("exceptions_wrapper_dao", err)
raise err

return wrapper


@my_wrapper
def test():
a = 1 + 2
return a


if __name__ == '__main__':
r = test()
print(r)

# 执行结果
# this is wrapper 3
# 4

通过打断点可以知道:执行test函数时,会跳转到wrapper函数里,执行func函数时,会执行test函数里的内容,最后return的结果作为执行test函数的结果。

装饰器的参数

也就是在原有的基础上套上一层函数

def my_wrapper(n):
"""
Dao层的捕获错误装饰器
"""

def decorator(func):
def wrapper(*args, **kwargs):
try:
res = func(*args, **kwargs)
print("this is wrapper", res)
print("this is n ->>", n)
return res + 1
except Exception as err:
print("exceptions_wrapper_dao", err)
raise err

return wrapper

return decorator


@my_wrapper(123)
def test():
a = 1 + 2
return a


if __name__ == '__main__':
r = test()
print(r)

# 执行结果
# this is wrapper 3
# this is n ->> 123
# 4

logging日志重复输出

logging封装代码

import logging
import time
from io import StringIO
import os

from config import LOG_FILE_DIR


class MyLogger:
def __init__(self, name=None):
if name is None:
name = __name__
# 日志文件路径
log_path = os.path.join(LOG_FILE_DIR, time.strftime('%Y-%m-%d %H%M%S', time.localtime(time.time())) + ".log")
if not os.path.exists(LOG_FILE_DIR):
os.makedirs(LOG_FILE_DIR)

# 创建一个logger
self.logger = logging.getLogger(name)
self.logger.setLevel(logging.DEBUG)

# 创建一个StringIO对象
self.log_string = StringIO()
# 创建一个全局的StringIO对象
self.log_string_global = StringIO()

# 创建一个handler,用于写入日志文件
file_handler = logging.FileHandler(log_path)
file_handler.setLevel(logging.DEBUG)

# 创建一个handler,用于将日志输出到控制台
console_handler = logging.StreamHandler()
console_handler.setLevel(logging.DEBUG)

# 创建一个handler,用于写入日志字符串
string_handler = logging.StreamHandler(self.log_string)
string_handler.setLevel(logging.DEBUG)

# 创建一个handler,用于记录所有的字符串
string_global_handler = logging.StreamHandler(self.log_string_global)
string_global_handler.setLevel(logging.DEBUG)

# 定义handler的输出格式
formatter = logging.Formatter(
fmt="%(asctime)s %(name)s %(filename)s %(funcName)s %(lineno)d %(message)s",
datefmt="%Y-%m-%d %X"
)

# 为每个handler添加输出格式
file_handler.setFormatter(formatter)
console_handler.setFormatter(formatter)
string_handler.setFormatter(formatter)
string_global_handler.setFormatter(formatter)

# 给logger添加handler
self.logger.handlers.clear() # 清除之前的handler # 重点
self.logger.addHandler(file_handler)
self.logger.addHandler(console_handler)
self.logger.addHandler(string_handler)
self.logger.addHandler(string_global_handler)

def print_log(self, msg, level='info'):
# 记录日志
if level == 'debug':
self.logger.debug(msg)
elif level == 'info':
self.logger.info(msg)
elif level == 'warning':
self.logger.warning(msg)
elif level == 'error':
self.logger.error(msg)
elif level == 'critical':
self.logger.critical(msg)

# 获取并清除日志字符串
log_contents = self.log_string.getvalue()
self.log_string.truncate(0)
self.log_string.seek(0)

return log_contents

def get_global_log(self):
"""
获取全局日志信息
:return:
"""
return self.log_string_global.getvalue()

def clear_global_log(self):
"""
清除全局日志信息
:return:
"""
self.log_string_global.truncate(0)
self.log_string_global.seek(0)

不同的name对应不同的logger。同样的logger,它们会共享handler,从而导致日志重复输出。

解决方法:

  1. 提供不同的name值
  2. 在添加处理器时,清除处理器

线程锁

涉及多线程使用同一类时,可以为该类添加线程锁,当前线程在用着时,其他线程就不能使用

场景:启动flask时,会使用日志输出flask启动信息,而启动发送邮件线程也会使用日志输出启动信息,因为log的特殊性,输出当前信息就得清空log,所以两个线程同时使用的时候,后者线程可能刚输出日志之后就会被前者线程清空log。

解决方法:为日志类添加线程锁

class MyLoggerInfo(MyLogger):
text_browser = None # 日志信息框
lock = threading.Lock() # 线程锁 # 重点

def __init__(self):
super().__init__()

@staticmethod
def set_text_browser(text_browser):
"""
设置日志信息框
:param text_browser:
:return:
"""
MyLoggerInfo.text_browser = text_browser

def print_log(self, msg, level='info', is_print_to_text=True):
"""
输出日志
:param msg: 日志信息
:param level: 日志等级
:param is_print_to_text: 是否输出到qt信息框中
:return:
"""
with self.lock:
text_log = super().print_log(msg, level)
if is_print_to_text:
if MyLoggerInfo.text_browser is not None:
MyLoggerInfo.text_browser.append(text_log

解决错误码 -1073741819 (0xC0000005)

执行到某行代码时,程序自动停止,出现错误Process finished with exit code -1073741819 (0xC0000005)

出现原因:与金山词霸、有道词典等软件的划词功能有关

可能是因为这些软件和程序使用相同的系统资源

可视化打包Python程序

执行安装命令,pip install auto-py-to-exe

执行打包程序打开命令,auto-py-to-exe

解决问题 AssertionError

AssertionError: View function mapping is overwriting an existing endpoint function: message.w

问题原因:flask路由使用了相同的endpoint函数

解决方法:

方法一

重命名装饰器的名字

def flask_controller_info(req):
"""
装饰器
:param req: 请求该接口的参数
:return:
"""

def f(func):
def w(*args, **kwargs):
res = func(*args, **kwargs) # 执行函数
return res
w.__name__ = func.__name__
return w
return f

方法二

可以设置@app.route的endpoint名,以避免出现重名的endpoint函数

@app.route("/path1", endpoint='func1')
@exception_handler
def func1():
pass

@app.route("/path2", endpoint='func2')
@exception_handler
def func2():
pass

pipwin

pipwin是一个Python包管理器,它会自动安装来自http://www.lfd.uci.edu/~gohlke/pythonlibs/的包,非常方便。如果遇到pip找不到,或者说pip下载启动wheel编译后却失败的情况,不妨尝试一下这个工具。

pipwin的主要作用是解决pip在Windows系统中的一些兼容性问题,比如安装某些Python包时需要编译C++代码或者需要一些特定的编译器等。

注:http://www.lfd.uci.edu/~gohlke/pythonlibs/ 是一个提供Python库下载的网站,它提供了许多科学开源扩展包的32位和64位Windows二进制文件,这些包是官方CPython发行版所需的。

生成requirements文件

整个环境下的安装包都保存到requirements.txt中

弃用	pip freeze > requirements.txt
pip list --format=freeze > requirements.txt

只生成单个项目中的使用到的安装包

pip install pipreqs
pipreqs .

执行安装requirements.txt

pip install -r requirements.txt

filter方法

接收两个参数,函数、列表

函数计算得到为True的元素返回到新的列表中

def is_even(num):
return num % 2 == 0

numbers = [1, 2, 3, 4, 5, 6]
even_numbers = list(filter(is_even, numbers))
print(even_numbers) # 输出: [2, 4, 6]

request

在使用request的get方法时,params参数只支持一层的dict自动转换成url编码,因此如果有多层的dict需要将第二层以上的转为字符串

# 错误,参数不会自动转为url编码
parmas = {
"a": {
"a-1": 1,
"a-2": 2
},
"b": {
"b-1": 1
}
}

# 正确
parmas = {
"a": json.dumps({
"a-1": 1,
"a-2": 2
}),
"b": json.dumps({
"b-1": 1
})
}

数学计算

保留n位小数

def reserve_significant_digits(num, n=6):
"""
保留n位有效数字
@return:
"""
significant = f'%.{n}g'
return significant % num

matplotlib库添加字体

python
import matplotlib
matplotlib.matplotlib_fname()

输出matplotlib的字体库位置,/usr/local/lib/python3.11/site-packages/matplotlib/mpl-data/matplotlibrc

tff格式的字体复制到/usr/local/lib/python3.11/site-packages/matplotlib/mpl-data/fonts/ttf

mv xxx.ttf /usr/local/lib/python3.11/site-packages/matplotlib/mpl-data/fonts/ttf/xxx.ttf

清空缓存

python
import matplotlib
matplotlib.get_cachedir()

rm -rf 地址

查看字体是否添加

python
from matplotlib import font_manager
for font_name in sorted(font_manager.get_font_names()):
print(font_name)

alembic ORM数据库使用

库安装

pip install alembic

配置连接池

main_engine = None  # 引擎
main_session = None # 会话
MainBase = None


def connect_main_mysql():
"""
连接数据库
"""
global main_engine, main_session, MainBase
print("------------------")
print(Config.DB_URI)
print("---------------------")
main_engine = create_engine(Config.DB_URI)
main_session = Session(bind=main_engine)
MainBase = declarative_base()


connect_main_mysql()

初始化配置

alembic init alembic

执行该命令会在项目根目录下新建一个alembic.ini文件和alembic文件夹

修改配置

打开alembic.ini文件,配置连接池

sqlalchemy.url = sqlite:///module.sqlite

打开alembic/env.py文件,配置迁移的模型

...
from apps.models.SYS_Model import * # 添加需要迁移的model类
from db_exts import MainBase # 添加连接数据库时的基础类
target_metadata = MainBase.metadata # 配置类的参数
...

生成迁移文件

alembic revision --autogenerate -m "Initial migration"

执行迁移

alembic upgrade head

回滚命令

alembic downgrade -1