1.安装与版本和IDE
1.1 python2.x和python3.x区别
python2在2020已经不再维护,目前主流开发使用python3. 二者语法上略有区别:输入输出、数据处理、异常和默认编码等,如:python3中字符串为Unicode字符串,使用UTF-8编码,而python2默认使用ASCII编码。python2和python3对应的包管理工具分别是pip和pip3. 另外,Python3引入了一些高级解释器特性,如协程、类型提示、异步编程等。
软件升级过程中,设计时不考虑向下相容(减少累赘),因此python3不完全兼容python2. 可通过Python3自带的工具2to3
对python文件进行转换,类似前端的ES6转ES5。
linux系统默认自带python2, python3一般需要手动安装, 可以参考: Python3安装教程。
说明:以下以python3为例进行介绍。
1.2 python的IDE
个人推荐 PyCharm和Spyder, 其中: Spyder是免费的,Pycharm的社区办是免费,而专业版收费。
2.工具包管理工具的使用和配置
python3使用pip3包管理工具处理依赖问题,类似java开发者用到的Maven.
2.1 配置三方包的镜像源和下载路径
镜像源配置:
镜像源配置可以通过命令(pip3 config set)或者配置文件方式进行修改,本文以配置文件方式进行说明。
windows系统中, 通过echo %APPDATA%
查看pip文件夹所在路径,修改%APPDATA%/pip/pip.ini
配置文件:
[global]
timeout = 6000
index-url = https://pypi.tuna.tsinghua.edu.cn/simple
trusted-host = pypi.tuna.tsinghua.edu.cn
修改完成后,可通过pip
命令查看是否修改成功:
D:\demo>pip3 config list
global.index-url='https://pypi.tuna.tsinghua.edu.cn/simple'
global.timeout='6000'
global.trusted-host='pypi.tuna.tsinghua.edu.cn'
Linux系统中, 通过修改~/.pip/pip.conf
配置文件:
[root@seong bin]# cat ~/.pip/pip.conf
[global]
index-url = http://mirrors.tencentyun.com/pypi/simple
trusted-host = mirrors.tencentyun.com
修改完成后,可通过pip
命令查看是否修改成功:
[root@seong bin]# ./pip3 config list
global.index-url='http://mirrors.tencentyun.com/pypi/simple'
global.trusted-host='mirrors.tencentyun.com'
下载路径配置:
配置文件中通过target
指定依赖包存放的地址.
[root@seong bin]# cat ~/.pip/pip.conf
[global]
index-url = http://mirrors.tencentyun.com/pypi/simple
trusted-host = mirrors.tencentyun.com
target = D:\xxx\Lib\site-packages
或者:
pip3 config set global.target D:\xxx\Lib\site-packages
2.2 IDE配置镜像源和安装路径
这里以pycharm进行介绍,使用pycharm管理项目时,可以选择Project venv或者Custom environment或者使用Conda环境。
Project venv(Virtual Environment)是一种基于Python的虚拟环境,可为每个项目创建独立的Python环境, 确保依赖包与其他项目相互隔离,避免版本冲突,较为常用。此时依赖包存放于项目根路径的.venv/Lib/site-packages目录下。
Custom environment支持手动指定Python解释器的路径。可以使用系统中已经安装的Python解释器或者虚拟环境。此时共用指定python环境的依赖包(而不需要单独维护一份)。
2.3 使用requirements.txt文件
requirements.txt
文件用于记录项目的依赖包以及版本信息, 用于项目的依赖管理。在一台机器上将某个项目的依赖导出为requirements.txt
,在另一台机器上可以根据requirements.txt
下载该项目的所有依赖包。
格式如下所示:
s
a>=1.0
b>=1.0, <1.1
c==2.0
可以用==
精确指定版本号,也可以使用 >=
或 <
或完全不指定,由python根据依赖关系确定。
生成requirements.txt
# pip freeze会把当前环境(或虚拟环境)下所依赖的所有包信息写入到requirements.txt文件中
# pip list会比pip freeze多几个包(pip , wheel , setuptools等)
pip freeze > requirements.txt
# 推荐使用pipreqs导出依赖包
# pipreqs仅导出程序所用到的包, 通过python3 -m pip install pipreqs 安装pipreqs
pipreqs ./ --encoding=utf8
使用requirements.txt导入依赖
#根据requirements.txt配置下载依赖项
pip install -r requirements.txt
2.4 pip3常用操作命令
#1.查看已安装的三方包列表
pip list
#2.安装包
pip install 包名
pip install 包名==版本号
pip install .whl安装包名 #通过.whl安装包安装
pip install -r requirements.txt #通过requirements.txt配置文件安装
#3.卸载包
pip uninstall 包名
#4.升级包
pip install -U 包名
#5.查看包信息
pip show 包名
其中,通过pip install
安装包时,可以通过 -i
指定使用的镜像源,如:
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple/ django
2.5 离线依赖包使用方式
#1.准备requirements.txt文件
pipreqs ./ --encoding=utf8
#2.生成离线依赖包
# -r 指定requirements.txt文件(生成的离线包中包含requirements.txt指定的依赖项)
# -d 指定离线包的保存路径
# 最终会生成很多.whl文件
pip download -r requirements.txt -d packages/
#3.指定从离线依赖包中安装依赖项
# -r 需要安装的依赖项
# --no-index 禁止从PyPI索引依赖包
# -find-links指定索引依赖包的路径
pip install -r ./requirements.txt --no-index --find-links=./packages
在步骤2中生成的.whl
文件,可以导入至无法联网的机器后,可以通过.whl
文件安装依赖包。
3.脚本运行原理
python类似java使用解释器+虚拟机的方式实现跨平台:python源码经过解释器和编译器生成 pyc 字节码文件,有PVM虚拟机读取pyc文件并执行。对于不同的平台有不同的PVM,将pyc文件的指令翻译为CPU指令执行。
4.包和模块的概念
4.1 包的概念
包可以将多个模块和子包组成在一起,每个包下需要有一个_ _init_ _.py文件,否则包会被当做普通目录对待。
包下的多个模块构成一个整体,可通过同一个包导入其他代码中。
4.2 模块的概念
每个python文件是一个模块,模块用于对逻辑进行封装,减少代码的耦合度。python中存在3种类型的模块:内置模块、三方模块、自定义模块;
[1] 内置模块:python自带的模块,如os提供操作系统的IO访问功能,math
数学运算, random
随机数,json
解析json文件,datetime
处理日期和时间相关;
[2] 三方模块:第三方开发的模块,可以通过pip安装后,再导入使用;如django
提供web能力;
[3] 自定义模块:自己开发的py文件。
4.3 导入模块
#导入模块,可通过as 进行别名
import 模块
import 模块 as m1
# 从模块中导入类、函数、变量等,可通过as进行别名
from 模块 import 函数
from 模块 import 函数1, 函数2
from 模块 import 函数1 as f1, 函数2 as f2
4.3 import机制
当你导入一个模块时,Python会执行该模块的代码,从而创建该模块的命名空间。这个命名空间包含了模块中定义的所有函数、类、变量等。 本质上,就是将导入的模块复制到当前文件中,但是会通过命名空间进行封装。import语句
导入指定的模块时会执行三个步骤:[1] 找到模块文件;[2] 编译成字节码;[3] 依次执行
说明:模块仅初始导入时才会执行上述步骤, 后续的导入会从内存中加载的模块对象。
4.4 案例介绍
1.同级目录下,可以直接导入
-- a.py
-- b.py
#在a.py中导入b模块
import b
2.模块a位于上一层级
-- a.py
-- pkg
-- __init__.py
-- b.py
#在a.py中导入b模块
import pkg.b #或者 from pkg import b
3.其他场景
sys.path是一个列表数据结构,包含了解释器搜索模块的路径。当导入一个模块时,解释器会按照sys.path
中的顺序逐个搜索模块的位置,都找不到会报错。 sys.path
包含当前目录、python标准库所在目录、第三方库所在目录。其他场景时(如模块a位于模块b的下一层级),sys.path
因不包含模块b的路径而找不到模块,因此需要手动将路径加入到sys.path
列表中。
case1:模块a位于下一层级
-- b.py
-- pkg
-- __init__.py
-- a.py
# 此时,可通过添加父路径完成
sys.path.append(str(Path(__file__).resolve().parents[1]))
# 或者
import os
sys.path.append(os.getcwd())
import b
此时,模块a导入模块b需要两个步骤:
[1] 将模块b所在路径添加到解析器的模块搜索路径sys.path
;
[2] 导入模块b.
case2:嵌套导入
--root
__init__.py
root.py
--pkg1
__init__.py
model1.py
--pkg2
__init__.py
model2.py
--pkg3
__init__.py
model3.py
root.py导入pkg1.model1模块,pkg1.model1导入pkg1.pkg2.model2模块,pkg1.pkg2.model2导入pkg1.pkg2.pkg3.model3模块。
代码如下:
#root.py
from pkg1.model1 import action1
print("root model name is " + __name__)
action1()
-------------------------------------------------------
#model1.py
import sys
from pathlib import Path
sys.path.append(str(Path(__file__).resolve().parents[0]))
from pkg2.model2 import action2
def action1():
print("model-1 name is " + __name__)
action2()
-------------------------------------------------------
#model2.py
import sys
from pathlib import Path
sys.path.append(str(Path(__file__).resolve().parents[0]))
from pkg3.model3 import action3
def action2():
print("model-2 name is " + __name__)
action3()
-------------------------------------------------------
#model3.py
def action3():
print("model-3 name is " + __name__)
执行结果如下:
root model name is __main__
model-1 name is pkg1.model1
model-2 name is pkg2.model2
model-3 name is pkg3.model3
上述使用相对路径时,在每个文件通过sys.path.append(str(Path(__file__).resolve().parents[0]))
将当前所在目录进入了sys.path
路径。
5.变量和基本类型
Python中定义变量不需要指定变量类型,函数和类的属性也不需要指定类型。python中变量只能使用字母数字和下划线组成,一般使用小写字母加下划线组成,常量使用大些字母和下划线组成。
5.1 基本变量类型
python中常用的变量类型包括:数值类型、布尔类型、字符串类型、列表类型、元组类型、集合和字典类型。 其中,布尔的字面量为True
和False
。
5.2 空对象
python使用None
表示空对象。
a = None
b = None
print(a is None)
print(b is not None)
print(a==b)
#代码运行结果如下:
True
False
True
5.3 查看对象类型
可以通过type(obj)
查看对象的类型,如下所示:
a = None
print("None is: %s" % type(a))
b = 1
print("1 is: %s" % type(b))
c = 'hello'
print("'hello' is: %s" % type(c))
d = []
print("[] is: %s" % type(d))
e = ()
print("() is: %s" % type(e))
#代码运行结果如下:
None is: <class 'NoneType'>
1 is: <class 'int'>
'hello' is: <class 'str'>
[] is: <class 'list'>
() is: <class 'tuple'>
6.字符串
python中字符串可以用单引号或者双引号包含,为不可变类型,常用操作如下:
str = "abc123"
#字符串长度
len(str)
#字符串切片
str[startIndex:endIndex] 从开始索引截取到结束索引,前开后闭
str[-3:] 最后3个字符
str[:3] 前3个字符
str[:] 整个字符串
#数组转字符串
arr = ["a", "b", "c"]
"_".join(arr) 得到 “a_b_c”字符串
#任何对象转字符串
str(obj)
#是否已特定字符串开头或者结尾
str.startswith('abc')
str.endswith('abc')
#删除前缀
str.removeprefix(“ss”)
#字符串删除左右空格(返回的是处理后的字符串,但是原始字符串不变)
str.strip() 删除左右空格
str.lstrip()删除左边空格
str.rstrip()删除右边空格
#f表达式
f"{变量名}{表达式}"
#字符串替换
"test%stest%stest%s" % ("1","2","3") 得到 "test1test2test3"
#也可以直接使用+对字符串进行拼接,需要注意整数等非字符需要使用str(int_temp)转字符串后才可以拼接,而使用%s替换不需要
7.集合类型
7.1 列表
列表中可以存多个不同类型的数据, 常用方法如下:
arr = [1, 2, "ss", True]
#根据下标获取数据
arr[0]
#遍历方式
for item in arr:
print(item)
#列表长度
len(arr)
#判断是否在数组中
print("ss" in arr)
print("ss" not in arr)
#在列表尾部添加一个元素
arr.append("ss")
#在指定的小标位置插入数据
arr.insert(0,"s1")
#remove删除数据
arr.remove("ss")
#clear情况列表, 此时列表等于[]
arr.clear();
#分片,前开后闭地截取
arr[startIndex,endIndex]
arr[0:3] 获取前3个,即arr[0],arr[1],arr[2]
arr[:]截取全部
arr[:3]前3个
arr[-3:]最后三个
7.2 元组
元组的使用方式与列表相同,区别在于元组创建后不可修改。
tuple_temo = (1, 2, "ss", True)
#根据下标获取数据
tuple_temo[0]
#遍历方式
for item in tuple_temo:
print(item)
#获取元组长度
len(tuple_temo)
#判断是否在元组中
print("ss" in tuple_temo)
print("ss" not in tuple_temo)
#分片,前开后闭地截取
tuple_temo[startIndex,endIndex]
tuple_temo[0:3] 获取前3个,tuple_temo[0],tuple_temo[1],tuple_temo[2]
tuple_temo[:]截取全部
tuple_temo[:3]前3个
tuple_temo[-3:]最后三个
7.3 字典
由多个键值对组成:键必须为不可变类型,如字符串、数值、元组等;值可以是数字、字符串、列表、字典。
#字典用{}表示
dict_temp={}
#根据键获取值
dict_temp.get("key") 或者 dict_temp['key']
#获取key列表
dict_temp.keys()
#获取value列表
dict_temp.values()
#获取(键, 值) 元组列表
dict_temp.items()
for key,value in dict_temp.items():
print(f"key is {key}, value is {value}.")
# 根据Key删除元素
del dict_temp['key']
#修改和添加
dict_temp['key1'] = "value1"
8.条件判断和循环逻辑
8.1 条件判断
python使用 ==
和 !=
表示是否相等和是否不等,使用 and 和 or 和 not表示与或非逻辑;python中只有if语句,没有switch-case
语句。
int score = 99;
if score >=80:
print("优秀")
elif score >= 70:
print("良")
elif score >= 60:
print("及格")
else if score >0:
print("不及格")
else:
pass
由于python使用缩进的方式替代了{},当处理逻辑为空时,可以使用pass语句占位。
8.2 循环逻辑
for循环
#循环10次
for i in range(0,10):
pass
#遍历列表
arr = [1, 2, 3, 's']
for item in arr:
print(arr)
while循环, 在python中没有do-while循环, while用法案例如下:
while 条件:
语句
#案例
num = 10
while num > 0:
num = num -1;
另外,继续下次循环语句continue
和中断循环语句break
的用法同java.
9.异常处理与自定义异常
9.1 try-except逻辑
try:
#语句1
except:
#语句2
当语句1执行过程中遇到异常时,会执行语句2.
注意,不建议这么使用,会将异常信息吞掉,需要指定异常对象,如下所示:
try:
#语句1
except 异常类型 as exception:
print(exception)
#语句2
当需要处理所有的异常类型时, 执行Exception(是所有异常类的父对象):
try:
#语句1
except Exception as exception:
print(exception)
#语句2
9.2 try-except-else逻辑
try:
#语句1
except Exception as exception:
print(exception)
#语句2
else:
#不发生异常执行的逻辑
9.3 try-except-else-finally逻辑
try:
#语句1
except Exception as exception:
print(exception)
#语句2
else:
#不发生异常执行的逻辑
finally:
#无论是否有异常都执行的语句
9.4 自定义异常
自定义异常需要继承Exception类:
class MyException(Exception):
def __init__(self, msg):
self.msg = msg
# 设置抛出异常的描述信息
def __str__(self):
return f'MyException occured, msg is {self.msg}'
使用raise
抛出异常(类似java的throw
),使用案例如下:
def main():
try:
raise MyException("消息异常")
except Exception as exception:
print(exception)
main()
运行结果如下:
MyException occured, msg is 消息异常
10.方法的定义和使用
10.1函数使用方式
将功能相似的函数一般放在一个模块里,在使用的地方通过导入的方式使用函数, 从而提高代码的可维护性。
函数的定义
命名规范:函数用小写字母加下划线命名。
def print_name():
print("ewen seong")
#带有参数的函数名
def print_name(first_name, last_name):
print(f"{first_name}{last_name}")
#函数可以有返回语句,用于返回一个或多个结果
def print_name(first_name, last_name):
name = f"{first_name}{last_name}"
print(name)
return name
#函数定义时,可以指定参数的默认值
def print_name(first_name, last_name="seong"):
print(f"{last_name}{first_name}")
函数的调用
#通过参数的顺序进行传参调用
print_name("ewen", "seong")
#通过指定形参名进行调用,不需要按照顺序
print_name(first_name="ewen", last_name="seong")
#等价于
print_name(last_name="seong",first_name="ewen")
10.2 main函数
python与java和C不同,没有固定的main
函数格式(按顺序解析执行)。可以手动指定main
方法,并作为程序的执行入口,使代码组织更加清晰、易于维护。
if __name__ == "__main__":
#main函数执行逻辑
此时,只有当前文件作为运行文件(被导入则不会执行),才会执行main
函数内部的逻辑。
11.类的定义和使用
11.1 类的定义
类名使用首字母大写的驼峰格式,同java;
class MyClass(object):
def __init__(self, id, name):
self.__id = id
self.name = name
def get_id(self):
return self.__id
def __set_id(self, id):
self.__id = id
[1] 使用class
关键字定义python类,所有类的基类为object对象,因此class MyClass(object)
等价于class MyClass
;
[2] 以双下划线__开头的方法和属性为私有属性,不能使用对象直接访问;否则,可以通过对象直接访问;
[3] 类中存在一个__init__
方法,当通过类构造对象时,会调用该方法,初始化对象;
[4] 类中定义的方法中包含一个self
对象,指代当前对象本身;方法被调用时,不需要用户传递。
11.2 类的使用
#直接通过类名以及__init__方法的入参进行构造类对象
myObj1 = MyClass(110, "name-110")
#通过对象调用公有方法
print(myObj1.get_id())
#通过对象获取公有属性
print(myObj1.name)
运行得到结果如下:
110
name-110
当强行访问私有方法或者私有属性时,会报错:
myObj1 = MyClass(110, "name-110")
#通过对象获取私有属性,报错
print(myObj1.__id)
Traceback (most recent call last):
File "E:\Data\2024\4\4.7-4.12\48\V5pythonDemo\V5pythonDemo\test\other_module.py", line 17, in <module>
print(myObj1.__id)
^^^^^^^^^^^
AttributeError: 'MyClass' object has no attribute '__id'
myObj1 = MyClass(110, "name-110")
#通过对象调用私有方法,报错
myObj1.__set_id(111)
Traceback (most recent call last):
File "E:\Data\2024\4\4.7-4.12\48\V5pythonDemo\V5pythonDemo\test\other_module.py", line 18, in <module>
myObj1.__set_id(111)
^^^^^^^^^^^^^^^
AttributeError: 'MyClass' object has no attribute '__set_id'
11.3 继承
子类继承父类时,拥有父类所有的属性和方法。注意:需要在子类的初始化函数中,调用父类的初始化方法。
class MySubClass(MyClass):
def __init__(self, id, name, age):
super().__init__(id, name)
self.age = age
def get_age(self):
return self.age
def print_msg(self):
print(f"id is {super().get_id()}, name is {self.name}, age is {self.age}")
mySubClass = MySubClass(110,"sy",100)
print(mySubClass.get_id())
print(mySubClass.name)
mySubClass.print_msg()
运行结果如下所示:
110
sy
id is 110, name is sy, age is 100
说明: 子类继承父类后,拥有了所有父类的属性和方法,但是只能访问公有的属性和方法;
12.日志框架
12.1 配置日志格式
log_config.py中定义日志格式、日志级别、日志文件路径等信息
import logging
def init_log(log_filename='app.log', level=logging.INFO):
# 设置日志格式
log_format = logging.Formatter(
'%(asctime)s - %(module)s - %(levelname)s - %(message)s',
datefmt='%Y-%m-%d %H:%M:%S'
)
# 配置根日志记录器
logger = logging.getLogger()
logger.setLevel(level)
# 文件处理器
file_handler = logging.FileHandler(log_filename)
file_handler.setLevel(level)
file_handler.setFormatter(log_format)
# 流处理器(控制台输出)
console_handler = logging.StreamHandler()
console_handler.setLevel(level)
console_handler.setFormatter(log_format)
# 为根日志记录器添加处理器
logger.addHandler(file_handler)
logger.addHandler(console_handler)
# 返回根日志记录器实例
return logger
12.2 在入口函数中引用setup_logging方法
from log_config import init_log
if __name__ == "__main__":
init_log()
12.3 在其他模块中直接使用logging日志对象
import logging
def print_log():
logging.info("日志信息")
打印结果如下:
2024-04-14 15:49:26 - other_module - INFO - 这是other_module.py中的日志信息
13.读写文件
python程序常需要从json和yaml、properties文件中读取数据,本章节仅读取json文件为例介绍python读取文件的方式。
从指定路径读取json文件, 返回字典类型
import json
import logging
def read_json_file(file_path):
try:
data = None
logging.debug("Begin to read json data from file %s", file_path)
with open(file_path, 'r') as file:
data = json.load(file)
except Exception as exception:
logging.info("Exception occurs during read json data, file is %s, exception is %s", file_path, exception)
finally:
return data
result = read_json_file("a.json")
logging.info("result is %s, type is %s.",result,type(result))
运行结果如下:
DEBUG - Begin to read json data from file a.json
INFO - result is {'name': 'sy', 'id': '110'}, type is <class 'dict'>.
说明: python使用open
函数与文件建立关联,可以选择’r’ 读操作,'w’写操作。案例中使用with open ... as ...
形式与java的try-with-resource
相似,执行完毕后会自动关闭文件。
14.执行shell命令
python中执行shell命令是python脚本的一个重要功能,该功能依赖于python提供的subprocess模块。
将python执行shell命令的逻辑封装在utils模块下:
#utils.py文件
import logging
import subprocess
def shell_execute(cmd, need_log=True):
p = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
if need_log:
logging.debug('pid(%s) cmd = %s' % (p.pid, cmd))
data = b''
for c in iter(lambda: p.stdout.read(1), b''):
if c != b'\n':
data = data + c
else:
try:
logging.debug('pid(%s) %s' % (p.pid, data.decode('utf-8')))
except:
pass
data = b''
p.wait()
if need_log:
logging.debug('pid(%s) returncode = %s' % (p.pid, p.returncode))
return p.returncode == 0
def shell_execute_with_output(cmd, need_log=True):
p = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
logging.debug('pid(%s) cmd = %s' % (p.pid, cmd))
output = []
data = b''
for c in iter(lambda: p.stdout.read(1), b''):
if c != b'\n':
data = data + c
else:
if need_log:
try:
logging.debug('pid(%s) %s' % (p.pid, data.decode('utf-8')))
except:
pass
output.append(data.decode('utf-8'))
data = b''
p.wait()
logging.debug('pid(%s) returncode = %s' % (p.pid, p.returncode))
return p.returncode == 0, output
上述shell_execute
方法仅执行命令,无返回值;shell_execute_with_output
返回一个长度为2的列表对象,第一个元素为命令是否执行成功(True表示命令执行成功),第二个命令表示执行命令得到的结果。
使用案例:
from utils import shell_execute, shell_execute_with_output
def action():
shell_execute("mkdir -p /test/test/test1")
shell_execute("mkdir -p /test/test/test2")
result = shell_execute_with_output("ls -al /test/test/")
print("cmd execute: %s" % result[0])
for each_item in result[1]:
print(each_item)
if __name__ == "__main__":
action()
执行结果如下:
[root@seong bin]# python application.py
cmd execute: True
总用量 0
drwxr-xr-x 4 root root 30 4月 9 09:20 .
drwxr-xr-x 3 root root 17 4月 9 09:20 ..
drwxr-xr-x 2 root root 6 4月 9 09:20 test1
drwxr-xr-x 2 root root 6 4月 9 09:20 test2