Python基础知识。
在实际项目开发中,我们通常会根据自己的需求去下载各种相应的框架库,如Scrapy、Beautiful Soup等,但是可能每个项目使用的框架库并不一样,或使用框架的版本不一样,这样需要我们根据需求不断的更新或卸载相应的库。在多个项目同时进行的过程中,对其他项目造成很多不必要的麻烦,管理也相当混乱。通过创建python虚拟环境,可以将不同项目需要的python开发环境封装在一个独立空间中,在这个空间中,安装项目需要的各种python包,这样将不同项目独立开来,便于开发管理。
conda create -n 虚拟环境名称 python=*(python版本)
例:conda create -n venv1 python=3.8
注:虚拟环境名称确定了,会建立个同名的文件夹,后续虚拟环境中安装的包都会在这个文件目录下
conda activate 虚拟环境名称
例:conda activate venv1
conda deactivate
例:conda deactivate
conda remove --name 虚拟环境名称 --all
conda remove --name venv1 --all
# 列出系统存在虚拟环境
conda info -e或者conda env list
# 查看当前环境下已安装的包
conda list
# 查看某个指定环境的已安装包
conda list -n venv1
# 查找某个package信息
conda search numpy
# 安装package
conda install -n venv1 numpy
# 如果不用-n指定环境名称,则被安装在当前激活环境
# 也可以通过-c指定通过某个channel安装
# 更新package
conda update -n venv1 numpy
# 删除package
conda remove -n venv1 numpy
~$ clear
terminals database is inaccessible
~$ export TERMINFO=/usr/share/terminfo
export
命令添加到~.bashrc
中。pip --no-cache-dir install xxx
Python镜像源
pip freeze > requirements.txt
# 这种方式配合virtualenv 才好使,否则把整个环境中的包都列出来了。
安装:
pip install pipreqs
用法:
在项目的根目录下使用 pipreqs ./
注:
如果是Windows系统,会报编码错误 (UnicodeDecodeError: 'gbk' codec can't decode byte 0xa8 in position 24: illegal multibyte sequence) ,使用时,指定编码格式:pipreqs ./ --encoding=utf8
生成requirements.txt 文件后,可以根据这个文件下载所有的依赖
用法:
pip install -r requriements.txt 即可
如果遇到connection问题,使用如下命令:
pipreqs ./ --pypi-server http://pypi.douban.com/simple/
这个工具的好处是可以通过对项目目录的扫描,自动发现使用了那些类库,自动生成依赖清单。
缺点是可能会有些偏差,需要检查并自己调整下。
PATH、PYTHONPATH、sys.path
PATH
作用:
定义python解释器路径
设置方法(当前用户永久生效):
vi ~/.bashrc
export PATH=$PATH:{python解释器所在目录},例如export PATH=$PATH:/home/zhangce/anaconda3/bin
source ~/.bashrc
PYTHONPATH
作用:
定义python模块的搜索路径
设置方法:
vi ~/.bashrc
export PYTHONPATH=$PYTHONPATH:{python模块所在目录},例如export PYTHONPATH=$PYTHONPATH:/home/zhangce/anaconda3/lib/python3.8/site-packages
source ~/.bashrc
sys.path
作用:
在python文件运行时,动态添加python模块搜索路径
设置方法:
import sys
sys.path.append("/home/zhangce/anaconda3/lib/python3.8/site-packages")
python文件的第一行代码一般为:
# !/usr/bin/env python3
或者
# !/usr/bin/python3
作用:
当在命令行,没有指定执行py文件的解释器,此行会指定执行代码的解释器
区别:
# !/usr/bin/python3
表示 python3 解释器所处的绝对路径就是/usr/bin/python3, 路径被写死了
# !/usr/bin/env/ python3
表示从 "PATH环境变量"中查找python3解释器的位置,路径没有被写死,而是在"环境变量"中寻找python3解释器的安装路径,再调用该路径下的解释器来执行脚本。
显然,采用后者的写法更灵活更具有通用性,推荐使用这种写法。
优先级:
python3 test.py的优先级最高,无论代码首行指定的是哪个解释器版本,都比以命令行输入的python解释器要弱。
【浅拷贝】
A = A1
# 说明:
# 改变A1的值,A的值也会发生变化
【copy浅拷贝】
A1 = A.copy()
#或者
import copy
A1 = copy.copy(A)
# 说明:
# 改变A1的值,A的值不会发生改变,除非A中包含子对象,比如:
#A = [0,1,2,[3,4,5],6,7,8]
#A copy拷贝给A1之后,若给A1添加一个元素9,则:
#A = [0,1,2,[3,4,5],6,7,8] 而A1 = [0,1,2,[3,4,5],6,7,8,9]
#若给A1[3]这个元素[3,4,5]添加一个元素9,则:
#A = [0,1,2,[3,4,5,9],6,7,8] A1 = [0,1,2,[3,4,5,9],6,7,8]
【深拷贝】
import copy
A1 = copy.deepcopy(A)
# 说明:
# 深拷贝则是无论A有无子对象,A1改变,A也不会发生改变。
Import "XXX" could not be resolved Pylance reportMissingImports
1、打开.vscode文件下的settings.json文件
2、添加如下代码块:
{
"python.analysis.extraPaths": ["/home/work/anaconda3/envs/zhangce-chatgpt/bin"]
}
其中:list中存储的是上述问题模块的路径
# 安装
pip install loguru
# 使用
from loguru import logger
from pathlib import Path
import time
# 获取当前工作路径的上一层路径
project_path = Path.cwd().parent
# 构建日志输出目录
log_path = Path(project_path, "logs")
# 构建时间变量
today = time.strftime("%Y_%m_%d")
# 构建日志工具
logger.add(f"{log_path}/ernie_bot_{today}.log", rotation='100MB', encoding="utf-8", retention=3, level="INFO")
# rotation='100MB':每100M创建一个新日志
# retention=3:同时保留三个日志文件
@staticmethod和@classmethod
相同点:
a. 都可以使用类对象和类对象实例访问
不同点:
a. @classmethod是一个函数修饰符,它表示接下来的是一个类方法,而对于平常我们见到的则叫做实例方法。
b. 类方法的第一个参数cls,而实例方法的第一个参数是self,表示该类的一个实例。
c. 普通对象方法至少需要一个self参数,代表类对象实例。
d. 类方法有类变量cls传入,从而可以用cls做一些相关的处理。
e. 对于类方法,可以通过类来调用,就像C.f(), 也可以通过类的一个实例来调用,就像C().f(),这里C(),写成这样之后它就是类的一个实例了。
f. 静态方法则没有cls参数,它基本上跟一个全局函数相同。
d. 有子类继承时,调用该类方法时,传入的类变量cls是子类,而非父类。
例:
class A(object):
@classmethod
def cm(cls):
print('类方法cm(cls)调用者:', cls.__name__)
@staticmethod
def sm():
print('静态方法sm()被调用')
class B(A):
pass
A.cm()
B.cm()
A.sm()
B.sm()
# 输出:
# 类方法cm(cls)调用者: A
# 类方法cm(cls)调用者: B
# 静态方法sm()被调用
# 静态方法sm()被调用
参考Python的super函数直观理解
super的作用就是执行父类的方法,虽然这句话不完全对,但是也差不多是那么个意思了。
1、super方法简介
比如以单继承为例:
class A:
def p(self):
print('A')
class B(A):
def p(self):
super().p()
B().p()
输出:A
可以看到B().p()其实就是执行的A.p
2、MRO简介
class A:
def p(self):
print('A')
class B():
def p(self):
print('B')
class C(A,B):
def p(self):
print('C')
class D(C):
def p(self):
print('D')
a = A()
b = B()
c = C()
d = D()
四个类的MRO (可以通过查看__mro__属性获得,例如A.__mro__)) 分别是:
A: (A, object)
B: (B, object)
C: (C, A, B, object)
D: (D, C, A, B, object)
什么意思呢,我们以A类为例,它的MRO顺序是他自己和object,很好理解,因为python里一切都是对象,所以你可以看到四个类的终点都是object。那C类的MRO也好理解,第一个顺序永远是自己,然后按照代码顺序依次是A,B,最后是object。
3、super用法
super本身其实就是一个类,super()其实就是这个类的实例化对象,它需要接收两个参数 super(class, obj),它返回的是obj对应类的MRO中class类的父类(或者说在MRO中按顺序的下一个类,下同):
class:就是类,这里你可以是A,B,C或者D
obj:就是一个具体的实例对象,即a,b,c,d。我们经常在类的__init__函数里看到super的身影,而且一般都是写成这个样子的super(className, self).__init__(),self其实就是某个实例化的对象。
4、举例
例1
super(C, d).p()
前面我们说过super的作用是 返回的是obj的MRO中class类的父类,在这里就表示返回的是d的MRO中C类的父类:
返回的是d对应类的MRO:(D, C, A, B, object)中C类的父类:A
那么super(C, d)就等价于A,那么super(C, d).p()会输出A
例2
super(A, c).p()
返回的是c的MRO中A类的父类:
返回的是c的MRO:(C, A, B, object)中A类的父类:B
所以最后的输出是B
例3
class A:
def p(self):
print('A')
class B(A):
def p(self):
super().p()
print('B')
class C(B):
def p(self):
print('C')
class D(C):
def p(self):
print('D')
d = D()
d.p()
这个很简单,最后只会输出D
那如果D类改成如下样子呢?
class D(C):
def p(self):
super().p()
print('D')
很简单,我们首先写出D的MRO为 (D,C,B,A,object),缺省状态下,super()就表示前一个父类,这里就是C类,那么super().p()就会调用C的p函数,但是C.p里没有调用super,所以就与A,B类无关了,那么最终的输出就是C,D
例4
class A:
def p(self):
print('A')
class B(A):
def p(self):
super().p()
print('B')
class C(B):
def p(self):
super().p()
print('C')
class D(C):
def p(self):
super().p()
print('D')
d = D()
d.p()
输出:A B C D
例5
多继承
如果一个类继承多个类,原理一样,我们只要知道了MRO就能知道执行顺序了,比如下面的例子:
class A:
def __init__(self):
print('A')
class B:
def __init__(self):
print('B')
class C(A,B):
def __init__(self):
super(C,self).__init__()
print('C')
class D(B,A):
def __init__(self):
super(B,self).__init__()
print('D')
print(C.__mro__)
print(D.__mro__)
print('initi C:')
c = C()
print('initi D:')
d = D()
输出结果为
(<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class 'object'>)
(<class '__main__.D'>, <class '__main__.B'>, <class '__main__.A'>, <class 'object'>)
initi C:
A
C
initi D:
A
D
结果解释,C 初始化后的打印结果应该很好理解,首先需要执行 super(C,self) 的_init_ 函数,根据C 的MRO可以知道super(C,self) 返回的是A ,所以可以看到最后的确是先打印出了A,然后是C
我们再看D 初始化的结果,注意执行的是super(B,self) ,根据D 的MRO我们可以知道返回的是A 所以打印的结果也是 A,然后打印出了D
总结
对于无论有多少个super(class, obj),我们首先需要知道obj的MRO是什么,然后找到class的前一个class是什么就好了。
super().__init__()
和xxxClass.__init__(self)
的关系:Python类中super()和__init__()的关系
一、匹配 \d
被匹配的文本 'abcdef\d1234'
方法一:(不使用r)
普通字符串中\d无特殊含义,所以仅需对\进行转义, 即:'abcdef\\d1234' (此步骤称为:字符转义)
正则表达式中\和\d均有特殊含义,所以需摒弃\和\d的特殊含义,即再进行一次转义(此步骤称为:正则转义)
str1 = 'abcdef\\d1234' # 因为\为转义符,所以需摒弃\的含义,因此此处为\\
print('原字符串',str1)
print('1. ',re.findall('\\\\d',str1))
控制台输出结果
# 原字符串 abcdef\d1234
# 1. ['\\d']
方法二:(使用r)
r'' 代表raw string 原始字符串,引号内括起来的内容所见即所得,所以不需要进行字符转义。即:r'abcdef\d1234'
在正则表达式中\d具有特殊含义,所以需对此进行转义,摒弃\d的特殊含义。即:r'abcdef\\d1234'
所以最终结果为:
str1 = 'abcdef\\d1234' # 因为\为转义符,所以需摒弃\的含义,因此此处为\\
print('原字符串',str1)
print('2. ',re.findall(r'\\d',str1))
控制台输出结果
# 原字符串 abcdef\d1234
# 2. ['\\d']
二、匹配\\d
被匹配的文本 'abcdef\\d1234'
方法一:(不使用r)
字符转义。即'abcdef\\\\d1234'
正则转义(对前三个\转义和\d转义)。即'abcdef\\\\\\\\d1234'
方法二:(使用r)
r所见即所得,无需转义。即r'abcdef\\d1234'
正则转义。即r'abcdef\\\\d1234'
str2 = 'abcdef\\\\d1234'
print('原字符串',str2)
print('3. ',re.findall('\\\\\\\\d',str2))
print('4. ',re.findall(r'\\\\d',str2))
控制台结果
# 原字符串 abcdef\\d1234
# 3. ['\\\\d']
# 4. ['\\\\d']
re.sub(pattern, r"\1\n\2", content)
"\1\n\2": 在第一个和第二个组之间用\n替换