笔记啊笔记

这是一篇笔记

此部分主要用来记录Python的小知识。如果出现错误请评论指正!



一、Python杂记

1. 官方文档小记

(1) del obj 与 obj.__del__ ()的区别

del obj并不直接调用 obj.__del__()
------ 前者会将 obj 的引用计数减一,而后者仅会在 obj 的引用计数变为零时被调用。
官方文档 - 数据模型__del__

(2) "百年"难题:__repr__和__str__到底有什么区别

__repr__:

官方“正式”字符串,给开发人员用的。返回的必须是一个字符串对象!!而它的形式应该是:<...some useful description...>,但是如果没有定义__str__,它也可以用来当作"非正式"字符串使用。此方法通常被用于调试,因此确保其表示的内容包含丰富信息且无歧义是很重要的。

举个例子🌰:
在Test类下,并没有定义_str_,但是print()和str()时,结果却与repr相同。
其实纵观flask源码。里边的__repr__基本上也是这么写的。所以__repr__的属性更注重丰富的信息,而不是简单的输出。

In [21]: class Test:
    ...:     def __init__(self, name, description):
    ...:         self.name=name
    ...:         self.description=description
    ...:     def __repr__(self):
    ...:         return f"<{type(self).__name__} {self.name, self.description}>"
    ...:

In [22]: repr(Test('repr', 'this is repr magic method'))
Out[22]: "<Test ('repr', 'this is repr magic method')>"

In [23]: str(Test('repr', 'this is repr magic method'))
Out[23]: "<Test ('repr', 'this is repr magic method')>"

In [24]: print(Test('repr', 'this is repr magic method'))
<Test ('repr', 'this is repr magic method')>

__str__

通过 str(object) 以及内置函数 format() 和 print() 调用以生成一个对象的“非正式”或格式良好的字符串表示。返回值必须为一个 字符串 对象。
此方法与 object.__repr__() 的不同点在于 __str__() 并不预期返回一个有效的 Python 表达式:可以使用更方便或更准确的描述信息。
内置类型 object 所定义的默认实现会调用 object.__repr__()。

🌰稍加修改🌰
而__str__只需要输出一些简要信息就完事儿啦。

In [31]: class Test:
    ...:     def __init__(self, name, description):
    ...:         self.name=name
    ...:         self.description=description
    ...:     def __repr__(self):
    ...:         return f"<{type(self).__name__} {self.name, self.description}>"
    ...:     def __str__(self):
    ...:         return f"({self.name}, {self.description})"
    ...:

In [32]: repr(Test('repr', 'this is repr magic method'))
Out[32]: "<Test ('repr', 'this is repr magic method')>"

In [33]: str(Test('repr', 'this is repr magic method'))
Out[33]: '(repr, this is repr magic method)'

In [34]: print(Test('repr', 'this is repr magic method'))
(repr, this is repr magic method)

官方文档 - 数据模型__repr__(__str__就在下面)

2. 日常小记

(1) os、shutil对文件和目录的操作

#导入shutil模块和os模块
import shutil, os

#复制单个文件
shutil.copy("1.txt", "./new_dir")
#复制并重命名新文件
shutil.copy("1.txt", "./new_dir/2.txt")
#复制整个目录(备份)
shutil.copytree("old_dir", "../new_dir")

#删除文件
os.unlink("1.txt")
#删除<空>文件夹
try:
   os.rmdir("old_dir")
except Exception as ex:
   print("错误信息:"+str(ex))#提示:错误信息,目录不是空的
#删除文件夹及内容
shutil.rmtree("old_dir")

#移动文件
shutil.move("1.txt", "new_dir")
#移动文件夹
shutil.move("old_dir", "new_dir")

#重命名文件
shutil.move("2.txt", "./new_dir/new2.txt")
#重命名文件夹
shutil.move("old_dir", "./new_dir/party")

(2) 获取文件所属用户名/组名

# python2
from os import stat
from pwd import getpwuid
from grp import getgrgid

def find_owner(filename):
    print(getpwuid(stat(filename).st_uid).pw_name)
    print(getgrgid(stat(filename).st_gid).gr_name)

# python3
from pathlib import Path

path = Path("/path/to/your/file")
owner = path.owner()
group = path.group()
print(f"{path.name} is owned by {owner}:{group}")

(3) pip下载指定版本/平台的包和依赖

# --only-binary=:all: 只下载编译好的二进制文件, .whl
# --platform 平台 
注意: 指定平台(platform),必须制定下载类型(only-binary)
python -m pip download --only-binary=:all: --platform linux_x86_64 --python-version 38 -d D:\celery38  celery

(4) 文本读取

①python2与python3的with open

python3的with open自带encoding,可以设置utf-8等。
python2的with open没有encoding参数,解决方式:
1、使用with codecs.open(filepath, option, encoding)
2、二进制读取, "rb""wb""ab"模式,不需要编码

②三种易混的读写模式

r+读写模式
	打开文件之后光标位置位于0的位置
	根据光标位置读写
w+写读模式
 	会清空文件
	打开文件之后光标位置位于0的位置
	根据光标位置读写
a+追加模式
	不会清空
	打开文件之后自动将光标置于最后,而光标位置只会影响读操作
	每一次追加都会将光标置于最后

(5) Python2和 Python3的字符串

   首先,需要明白什么是编码,什么字符集,这俩特别容易混淆。举个例子:Unicode是字符集,每一个字符都对应着唯一的二进制码。而UTF-8是Unicode的编码规则,它是变长的,可以把一个Unicode字符编码为1到4个字节。
   Python2中的字符串分为 str unicode ,Python3的字符串分为 str bytes 。在2中,str与unicode是混用的,他们在英文状态下是一样的。其实这东西说着说着就乱了,我好像也没完全明白…还得慢慢理解,下面的引用可以参考一下。

ℹ 引用:

  1. Python 3 中str 和 bytes 的区别
  2. Python 中的 bytes 和 str

3. 异步(Async)

(1)、异步监控处理demo

  最近看了异步相关的知识,在日常应用一下。随手一记,如果有错误希望大佬果断指出!
demo的主要意思是:在linux环境下,利用 pyinotify 监控指定目录下的文件,并通过异步子任务进行处理。
流程

  • 启动1个异步子任务 handle_upload 来监控某个目录
  • 启动5个异步子任务 handle_upload 用来处理监控到的任务
# coding:utf-8
import datetime
import hashlib
import logging
import os
import random
import string

import pymysql
import asyncio
import shutil
import traceback

from logging import handlers
from pwd import getpwuid

from requests.packages import urllib3
from pyinotify import ProcessEvent, IN_CREATE, IN_DELETE, IN_MODIFY, IN_CLOSE_WRITE, WatchManager, AsyncioNotifier
from concurrent.futures import ThreadPoolExecutor



# 创建一个pymysql的上下文管理器
class DB(object):
    def __init__(self, host='127.0.0.1', user='test', passwd='test', port=3306, charset="utf8",
                       db="testdb"):
        self.conn = pymysql.connect(host=host, port=port, db=db, user=user, passwd=passwd, charset=charset)
        self.cur = self.conn.cursor(cursor=pymysql.cursors.DictCursor)

    def __enter__(self):
        return self.dbcur

    def __exit__(self, exc_type, exc_value, exc_trace):
        self.conn.commit()
        self.cur.close()
        self.conn.close()


# 处理文件
def do_something(filepath):
    try:
        print(os.stat(filepath))
        return True
    except Exception as e:
        return False


# 异步子任务, 子任务不会退出,一直等待队列有值后进行操作
async def handle_upload(queue):
    while True:
        try:
            # 阻塞,等待队列中的值(文件路径)
            file_path = await queue.get()
            _dir, _file_name = os.path.split(file_path)
            # 使用`loop`的`run_in_executor(executor, func, *args))`,可以将其他代码利用线程异步执行,不用重构,方便使用。
            # 注意:这个函数可以是导入进来的某一个函数,也可以是单独写的函数
            # 以下的 `func_1` 和 `args_1` 根据实际情况填写
            _res = await asyncio.get_running_loop().run_in_executor(_thread_executor, do_something, file_path)
            execute_flag = 'error'
            if _res:
                logging.warning("monitor: success! file: %s, message: %s" % (str(_file_name), str(_res)))
                execute_flag = 'error'
            if execute_flag == 'error':
                # 将文件路径存入集合,每周一重置。
                ERROR_FILE_SET.add(file_path)
        except Exception as e:
            traceback.print_exc()


async def input_queue(file_queue, task_queue):
    while True:
        if not FIRST_RUN:
            filepath = await file_queue.get()
            await task_queue.put(filepath)


class FileMonitor(ProcessEvent):
    def __init__(self, queue):
        super(FileMonitor, self).__init__()
        self.file_queue = queue

    # 重写这个函数(close_write),来执行操作代码
    # event为默认参数
    def process_IN_CLOSE_WRITE(self, event):
        # 文件夹路径判断,如果不是指定文件夹则退出(适用于不同路径文件的不同操作)
        if event.path == "/home/hellowrold/asyncDir/test/":
            # 获取文件路径
            pathname = event.pathname
            # 如果文件录入出现错误,将文件名称存入集合,每周一重置
            global ERROR_FILE_SET
            if datetime.date.today().weekday() == 0:
                ERROR_FILE_SET = set()

            _dir, _file = os.path.split(pathname)
            # 如果目标文件是:非文件或在错误集合中。直接返回,结束执行。
            if not os.path.isfile(pathname):
                logging.WARNING("monitor: %s is dir, return" % _file)
                return
            elif _file in ERROR_FILE_SET:
                logging.debug("monitor: %s in ERROR_FILE_SET, return" % _file)
                return
            else:
                # 非阻塞放入文件路径。
                self.file_queue.put_nowait(pathname)


async def fs_monitor(path, file_queue):
    # 创建监控
    wm = WatchManager()
    # 设置为 close_write(写完) 动作
    mask = IN_CLOSE_WRITE
    # 指定"路径"和"动作", 并开启目录递归(目录下的所有文件都会被监控)
    wm.add_watch(path, mask, rec=True)
    # 获取当前事件循环
    _run_loop = asyncio.get_event_loop()
    # 开启 pyinotify 的 异步任务,加入事件循环中, 传入协程队列参数。
    # 注意:此处使用 default_pro_fun,并不是callback。
    #      AsyncioNotifier加入事件循环后,每次抓取到指定动作就会执行一次`default_proc_fun`参数对应的函数
    notifer = AsyncioNotifier(wm, asyncio.get_event_loop(), default_proc_fun=FileMonitor(file_queue))


async def first_run_check_path(path, queue):
    logging.info("monitor: first run, Check whether files exist in the directory, path: %s" % path)
    global FIRST_RUN
    for _root, _dir, _files in os.walk(path):
        for _file in _files:
            filepath = os.path.join(_root, _file)
            if os.path.isfile(filepath):
                await queue.put(filepath)
    FIRST_RUN = False
    logging.info("monitor: Check done!")
    
    
async def async_run(watch_path):
    # task异步队列,长度为20
    task_queue = asyncio.Queue(20)
    # 文件异步队列,长度不限制
    file_queue = asyncio.Queue()
    # 创建5个 upload 异步任务
    tasks = [asyncio.create_task(handle_upload(queue)) for _ in range(5)]
    # 创建1个 文件监控 异步任务
    tasks.append(asyncio.create_task(fs_run(watch_path, file_queue)))
    # 创建1个 初次启动检查目录 异步任务
    tasks.append(asyncio.create_task(first_run_check_path(watch_path, task_queue)))
    # 创建1个 文件传送队列,将监控文件队列传入任务队列,防止任务队列因监控不释放导致阻塞 异步任务
    tasks.append(asyncio.create_task(input_queue(file_queue, task_queue)))
    # 并行运行所有异步任务
    await asyncio.gather(*tasks)


if __name__ == '__main__':
    log_path = "/var/log/asyncioTest/asyncio_test.log"
    # 设置日志打印格式
    log_fmt = '%(asctime)s - %(filename)s:line %(lineno)d - %(levelname)s - %(message)s'
    formatter = logging.Formatter(log_fmt)
    # 创建TimedRotatingFileHandler对象, 每周一新建一个log文件,12个log后回滚
    log_file_handler = handlers.TimedRotatingFileHandler(filename=log_path, when="W0", interval=1, backupCount=12)
    log_file_handler.setFormatter(formatter)
    logging.basicConfig(level=logging.INFO)
    log = logging.getLogger()
    log.addHandler(log_file_handler)
    logging.getLogger('requests').setLevel(logging.WARNING)
    logging.getLogger("paramiko").setLevel(logging.WARNING)
    # 禁止urllib3告警
    urllib3.disable_warnings()
    # 创建一个错误集合
    ERROR_FILE_SET = set()
    # 首次运行标识
    FIRST_RUN = True
    # 线程池,线程数量为10
    _thread_executor = ThreadPoolExecutor(10)
    watch_path = '/home/hellowrold/asyncDir/'
    # 启动所有异步任务
    asyncio.run(async_run(watch_path=watch_path))
    logging.info(f" ----- file_monitor run! ----- ")

二. Linux杂记

1. Linux常用命令

参考链接:Ubuntu常用命令手册

2. Shell脚本

(1)、批量创建FTP用户

#/bin/bash

userList=("user1" "user2" "user3" "user4" \
          "user5" "user6")
for user in ${userList[*]}; do
        have_user=`cat /etc/passwd | cut -f1 -d':' | grep -w "$user" -c`
        if [ $have_user -gt 0 ]
        then
                echo "$user 已存在, 用户数: $have_user 个"
                continue
        else
        		# 创建一个无法shell登录的用户,设置家目录为ftp公共路径
                useradd -s /sbin/nologin -d /home/[ftp_path] $user
                # 修改密码
                echo "$user:[password]" | chpasswd
                # 将用户加入到ftp公共组
                usermod -aG [ftp_group] $user
                # 开启chroot后,修改用户的ftp路径
                echo -e "local_root=/home/[ftp_path]\nallow_writeable_chroot=YES"  > /etc/vsftpd_config/vsftpd_user/$user
        fi
done

echo "执行结束"

3. 用户、用户组操作

(1)、用户操作

> 1. 查看用户信息
 id [username]
> 2. 查看所有用户
 cat /etc/passwd
> 3. 新增用户
 useradd [username]
> 4. 改变家目录
# 新增用户时改变家目录
 useradd -d [PATH] [username]
# 修改某个用户家目录(不转移原家目录内容数据)
 usermod -d [PATH] [username]
# 修改某个用户家目录,同时转移之前路径数据
 usermod -md [PATH] [username]
> 5. 设置用户密码
 passwd [username]
	# 非交互式修改密码()
	 echo "[username]:[password]" | chpasswd
> 7. 删除用户
 userdel [username]
> 8. 查看当前活跃用户
 w
> 9. 查看简明用户列表
 cat /etc/passwd|grep -v nologin|grep -v halt|grep -v shutdown|awk -F":" '{ print $1"|"$3"|"$4 }'|more

(2)、用户组操作

> 1. 查看用户组
 cat /etc/group
> 2. 新建工作组
 groupadd [groupname]
> 3. 将用户添加进工作组
 gpasswd -a [groupname] [username]
> 4. 将用户移除工作组
 gpasswd -d [groupname] [username]

4. 更改pip下载源

1. 在主目录下新建.pip文件夹
mkdir ~/.pip

2. 创建pip.conf文件
vim ~/.pip/pip.conf

3. 在文件中写入 清华源 或其他源即可
[global]
index-url = https://pypi.tuna.tsinghua.edu.cn/simple	

5. Service服务自定义参数启动

  先摆上大佬翻译的 systemd.service中文翻译手册
  因为平常会写一些脚本,用service服务来托管很爽,但是同时启动多个脚本的话又不想写多个service,所以记录一下操作流程。

正文:
  如果 Type=simple,那就只能写一个 ExecStart,但可以写多个ExecStartPre ExecStartPost,但这些都不适合长时间运行的命令,因为它们是 串行 执行的,并且只会在上一个执行完之后再执行下一个。
  如果 Type=oneshot,那就可以写多个 ExecStart,但这些任务仍是将 串行 运行的。

那么,如果想要并行运行多个单元,可以执行以下操作:

(1) 如果它们只在 1 个参数上不同

  可以使用模板单元,可以创建一个 /etc/systemd/system/foo@.service 注意:(这@ 很重要,用来传递参数)。

[Unit]
Description=script description %I 

[Service]
Type=simple
ExecStart=/script.py %i
Restart=on-failure

[Install]
WantedBy=multi-user.target

然后你执行下面的实例,parameter1和parameter2就会传入到service中的%i里并启动两个服务:

$ systemctl start foo@parameter1.service foo@parameter2.service

(2) 需要依赖的方式

可以将单个目标以链接的形式放入多个单元中:

#/etc/systemd/system/bar.target
[Unit]
Description=bar target
Requires=multi-user.target
After=multi-user.target
AllowIsolate=yes

然后只需将 .service 单元修改为WantedBy=bar.target:

#/etc/systemd/system/foo@.service
[Unit]
Description=script description %I

[Service]
Type=simple
ExecStart=/script.py %i
Restart=on-failure

[Install]
WantedBy=bar.target

然后你只需要并行启用你想要的 foo 服务,然后像这样启动 bar 目标:

$ systemctl daemon-reload
$ systemctl enable foo@param1.service
$ systemctl enable foo@param2.service
$ systemctl start bar.target

注意:这适用于任何类型的单元,而不仅仅是模板单元。

6. 挂载新盘

  一次老板给了新固态,然后替换了我的老机械硬盘。但是挂在的时候出问题mount: wrong fs type, bad option, bad superblock on xx…,原因是硬盘格式化的时候没有指定文件格式或这错误区块啥的…解决办法就是使用正确的文件格式重新格盘。
解决方式如下:

  1. 查看硬盘是否被正常识别: fdisk -l, 找到你的新硬盘
  2. 查看磁盘文件格式是否正确: lsblk -f, 并且能够看到硬盘uuid
  3. 使用ext4文件格式进行格式化(旧盘记得备份数据): mkfs -t ext4 /dev/xxx
  4. 如果没有其他输出报错,就可以挂载了: sudo mount /dev/xxx /xxx
  5. 设置重启自动挂载:
    (1) 找到挂载盘uuid(更准确一些): ls -l /dev/disk/by-uuid
    (2) 备份fstab文件: sudo cp /etc/fstab /etc/fstab.bak
    (3) 将挂载盘写入fstab文件: 写入:UUID=xxx 挂载目录 ext4 defaults 0 0

三、MySQL杂记

1. MySQL修改utfmb4编码

  某然一次工作中碰到了MySQL版本升级导致插入数据报错的问题。原来因为旧的MySQL中,utf8是阉割版,只支持3字节,像很多外国语或者特殊字符就插入不进去,本次记一下从utf8改为utf8mb4的方式。我的系统是ubuntu20.04。

[mysqld]
# Character Settings
init_connect='SET NAMES utf8mb4'

# 连接建立时执行设置的语句,对super权限用户无效
character-set-server = utf8mb4
# 设置服务端校验规则
collation-server = utf8mb4_general_ci
skip-character-set-client-handshake

[mysql]
default-character-set   = utf8mb4

[client]
default-character-set    =  utf8mb4

ℹ 拓展阅读:

  1. 【我是局长】: 全面了解mysql中utf8和utf8mb4的区别
  2. 【腾讯云开发者】MySQL中的COLLATE是什么?

2. MySQL数据迁移

  因为磁盘太慢了,导致写入数据瓶颈。老板大气换了块固态,想着把数据腾移过去,但是做了半天动作就是报错没权限!(errno: 13 - Permission denied),记录一下捣鼓的过程。

  1. 改配置文件,将数据先复制过去,别一下移动了在整坏了
  2. 修改目的文件的权限、用户和用户组,权限700,用户和用户组都是mysql
  3. 这时候要是你去重启服务,它可能个汇给你报目录没权限,如果报了请看下边。
  4. SELINUX,它是一个Linux的安全模块,关闭它。

    /etc/selinux/config文件中设置SELINUX=disabled

  5. apparmor,它可以限制资源访问,其中无权限就是他的问题。

    在 /etc/apparmor.d/usr.sbin.mysqld 这个文件中,有这两行,规定了mysql使用的数据文件路径权限:
    /var/lib/mysql/ r,
    /var/lib/mysql/** rwk,
    你要做的是在这里写上你的目的路径
    /data/mysql/ r,
    /data/mysql/** rwk,
    重启: sudo service apparmor restart
    你就发现权限也有了,重启也没问题了。

ℹ 引用:MySQL [Warning] Can’t create test file xxx lower-test(转)


四、其他

1. markdown

(1) 可能用得到的语法

下划线&#95;
空格&emsp;
换行<br/>
加粗<b></b>
居中<center></center>
图片居中最后加上,#pic_center

(2) 小表情、小图片网站

EmojiAll中文官方网站  📡 https://www.emojiall.com/zh-hans/ 📡
Full Emoji List  😂https://www.unicode.org/emoji/charts/full-emoji-list.html😂
unicode Emoji  🚀 https://apps.timwhitlock.info/emoji/tables/unicode# 🚀
Web FX   :🍁https://www.webfx.com/tools/emoji-cheat-sheet/🍁

2. win11相关

(1) win11右下角控制台点击无响应

  有一次因为C盘红了, 清理之后发现右下角控制台用不了了, wifi音量什么的都点不开, 解决方式为:

  • 右键开始菜单,管理员运行Poweshell或者新终端
  • 输入命令 : Get-AppxPackage | % { Add-AppxPackage -DisableDevelopmentMode -Register "$($_.InstallLocation)\AppxManifest.xml" -verbose } 执行期间可能有些应用因为占用不能更新,但并不影响。等待命令跑完。
  • 跑完后再次执行 命令 : sfc /SCANNOW , 就ok
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值