当前位置:   article > 正文

Python自动化运维---学习笔记_python 运维课程

python 运维课程

Python自动化运维

python简介

入门容易精通难,python适合作为第二编程语言(对于运维:Shell,对于开发:Java,对于全栈:nodejs(javascript))

python官网:https://www.python.org/

Python is a programming language that lets you work quicklyand integrate systems more effectively.

python是编程语言界中最神奇的一种编程语言

通常情况下使用python可以用几乎一半不到的代码量实现同等功能,相较于c++,java

python的强大不仅仅是自身,同时python更重要的是丰富的第三方库

python最早是谷歌开始使用的,用于服务器运维

python也是linux操作系统中必备的重要组件(包管理器)

在你使用linux系统的时候,就已经享受了python带来的便捷

绝大多数linux操作系统都默认自带python的环境

python的版本问题

python分为两大分支:

  • python 2.x
  • python 3.x

两个不兼容,目前python2停止维护,主流版本为python3

部分python老项目停留在python2的版本

很多linux操作系统会同时包含python2和python3环境

我们很多时候使用python更多的去考虑使用虚拟环境(于系统自带的python进行隔离) -> 容器技术(docker),类似于沙盒技术

一般情况下python的虚拟环境我们使用两种方式解决

  • python3自带virtualenv工具可以快速生成一个独立的python环境用于开发或运行python程序(只能使用已经安装的python版本)
  • 基于conda工具完成多个独立的python环境用于开发和运行程序(可以管理多个任意python版本)

本次课程使用conda(miniconda) 作为python的部署工具

安装配置python

采用miniconda版本

https://docs.conda.io/en/latest/miniconda.html

linux版本(centos8)

下载miniconda的安装脚本

[root@www ~]# curl -O https://mirrors.tuna.tsinghua.edu.cn/anaconda/miniconda/Miniconda3-py38_4.8.3-Linux-x86_64.sh

或者

[root@www ~]# wget https://mirrors.tuna.tsinghua.edu.cn/anaconda/miniconda/Miniconda3-py38_4.8.3-Linux-x86_64.sh

补充知识: 如何将windows的文件推送到windows

win10 默认自带openssh客户端,可以使用scp命令推送文件到linux中

scp windows文件路径 root@192.168.199.129:linux系统文件路径

windows到linux的免密ssh登录:(点击打开)

在linux中运行https://mirrors.tuna.tsinghua.edu.cn/anaconda/miniconda/Miniconda3-py38_4.8.3-Linux-x86_64.sh

vi ./.bashrc 在末尾添加deactivate 或者 echo conda deactivate >> .bashrc

重启终端环境 source .bashrc

windows版本

下载安装包

https://mirrors.tuna.tsinghua.edu.cn/anaconda/miniconda/Miniconda3-py38_4.8.3-Windows-x86_64.exe

双击运行程序

conda中常用的几个指令

对于国内用户可以通过以下网址

conda info -e			#查看所有的虚拟python环境
conda deactivate		#关闭被激活的虚拟python环境
conda activate 环境名    #激活虚拟python环境(可用于环境之间的切换)

conda create -n 环境名 python=版本号	#创建一个指定版本号的python虚拟环境

conda remove -n 环境名  --all  #完整删除某个环境

conda install 软件名称	#使用conda安装某个软件或者python包(需要先激活目标虚拟环境),会分析当前环境,把软件本身和依赖一起安装

conda remove 软件名称	#卸载某个软件或者python的包(需要先激活目标虚拟环境)

conda search 软件名称	#查找某个软件或者python的包(需要先激活目标虚拟环境)

conda list	#显示已经安装的软件或者python包
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

清华大学镜像站anconda下载地址

https://mirrors.tuna.tsinghua.edu.cn/help/anaconda/

配置VS Code开发环境

VS Code是一个高级文本编辑器,相较于传统的文本编辑器,可以配置开发环境,用于多种编程语言开发,同时比传统IDE轻量

VS Code是基于浏览器开发的一个工具,配置文件采用jason的形式

VS Code支持远程SSH开发(Windows上使用VS Code通过SSH的方式远程连接到Linux环境中进行开发操作)

另一个常用于python开发的IDE–Pycharm

https://code.visualstudio.com/

运行安装程序

安装扩展包:

Anaconda Extension Pack(python开发套件和yaml辅助套件) ;

远程开发包:Remote Development(wsl:在windos中使用linux,ssh:ssh传输协议, Containers:容器技术)

中文环境包

利用VS Code实现远程开发

点击左侧的远程资源管理器,选择ssh模式,输入ssh登录方式,将ssh登录config文件保存在用户主目录

Host 192.168.218.147                  # 远程设备名称(给使用看的,可随意起名字)
  HostName 192.168.218.147            # ssh远程的目标地址
  User root                           # ssh远程登录的用户
  • 1
  • 2
  • 3

然后通过VS Code远程链接上目标设备,在里面创建一个专门用于放置自己编写代码的文件夹

编写你人生中的第一行Python代码

任何编程语言的第一个程序无外乎Hello, World!

我们怎么来编写运行呢?

通过VS Code创建hello.py文件

在扩展中将Python的扩展安装到远程设备中

在打开python源代码文件的时候会自动的加载python扩展插件

我们需要手动选择一个Python环境用于VS Code快速执行代码文件

然后就可以愉快的编写代码了

print("Hello, World!")
  • 1

记得保存一下

我们可以通过这些方式来运行我们编写的Python代码

  • 使用终端,调用python解释器来执行目标代码文件
  • 使用VS Code快速执行
(py38) [root@server code]# python hello.py 
Hello, World!
(py38) [root@server code]# 
  • 1
  • 2
  • 3

代码风格问题

Python对于代码有个较为严格的格式要求

  • 默认每一行顶头
  • 每一行都是一句代码,不将多个代码合并到一行中(通过分号)
  • 同缩进的代码处在同一级别(Python中缩进可以是空格、也可以用tab,但是不能混用,默认情况4个空格作为一层缩进)
  • 代码留空最后一行

更多有关代码风格的问题我们会在后面具体接触到一些特定语法的时候单独讲

变量、输入、输出

变量

对于变量,首当其冲就是需要解决一个变量位置的问题(变量是程序运行过程中的数据、在内存中)

我们对于变量通常采用的是标识符命名法

标识符规范(Python3):

  • 下划线、数字、英文、汉字、片假、拉丁等
  • 开头不能是数字

对于Python中变量,不同于C、C++、Java,Python中的变量更多的是一个快递盒子

静态类型与动态类型

静态类型:会提前设计好不同大小的快递盒子,并且每种盒子可以装什么东西是提前规定好的

动态类型:根据东西定义盒子大小、不限定盒子装什么

静态类型:牺牲用户,提高快递店盒子的使用(代码编写规范要求多,但是往往带来性能层面的提升)

动态类型:方便用户,快递店要定制盒子(规范要求变少了,往往会牺牲较多的性能)

正是因为这个原因,所以如果没有东西,那么没有办法定制盒子(Python中的变量需要先赋值、然后才能被使用)

输出

在Python中,输出语句为print,会将括号中的值输出到终端上(如果说是一个变量,那么会输出变量的值)

print看做是x光检查器

print是Python自带的一个内置函数(方法)

print语句支持多个变量或者数据在括号中,使用英文逗号分隔,那么print将会一次性的打印出这些的值,用空格作为他们直接的分隔

可以在里面添加end参数来控制print结尾的字符

a = 100
b = "Python"

print(a, end="")
print(b)

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
输入

在Python中,输入语句为input,会接受从终端中输入的值(换行符结束)

input也是Python自带的一个内置函数(方法),返回值为字符串类型

input也有括号,括号可以填入数据或者变量来充当print

print("请输入你的姓名:")
name = input()
print("你好", name)
input("按回车键退出:")

  • 1
  • 2
  • 3
  • 4
  • 5

注意事项:输入的内容统统会被当做字符串来处理

基本数据类型

整数、浮点数、字符串、布尔

整数

Python中的整数和数学当中对于整数的定义是一致的,Python中整数可以为任意大小

浮点数

Python中的浮点数就是小数,Python中浮点数的精度是可以无限的

字符串

在Python中没有单独的字符类型,对于从终端接收的数据统统视为字符串

布尔

在Python中,布尔只有两种值:True、False

100     # 整数
100.0   # 浮点数
"100"   # 字符串
'100'   # 字符串
True    # 布尔
  • 1
  • 2
  • 3
  • 4
  • 5

运算符与表达式

Python对于运算符的支持很强大,提供了大量的运算符

符号作用
+数字类型求和运算,用于两个字符串直接的合并
-数学类型求差
*数字类型求积,用于字符串重复自我整数倍合并
/得到浮点数版本的商
**乘方运算
//地板除(得到整数商)
%求余数
=赋值
>大于(结果为布尔值)
<小于(结果为布尔值)
>=大于等于(结果为布尔值)
<=小于等于(结果为布尔值)
==等于(结果为布尔值)
!=不等于(结果为布尔值)
and与(布尔运算)
or或(布尔运算)
not非(布尔运算)
is判断是否是同一对象
in判断是否在内(判断字符串中是否有特定的字符)

分支语句

在Python中,分支语句使用下面的几个重要关键词

  • if
  • else
  • elif

分支语句结构

if bool表达式 :
    执行的语句
执行语句
  • 1
  • 2
  • 3
if bool表达式 :
    执行的语句
else:
    执行的语句
执行语句
  • 1
  • 2
  • 3
  • 4
  • 5
if bool表达式 :
    执行的语句
elif bool表达式:  # 先否定上面的条件,然后判断自己的这个bool表达式
    执行的语句
执行语句
  • 1
  • 2
  • 3
  • 4
  • 5
if bool表达式 :
    执行的语句
elif bool表达式:  # 先否定上面的条件,然后判断自己的这个bool表达式
    执行的语句
else:
    执行的语句
执行语句
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

问卷调查系统 v0.1

三个变量、分别存放用户输入的姓名、性别、职业

提示用户输入自己的姓名、性别、职业

如果用户输入了空的内容,那么对应的姓名、性别、职业应该赋值为——“用户未输入”

最后输出一句话,包含用户的姓名、性别、职业

难点:判断用户输入的为空

a = input()
if a:
    print("True")   #  当输入不为空时
else:
    print("False")  # 当输入为空的时候

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

循环结构

在python中,有关循环的关键词为for,while

while–真正的循环

结构

while bool 表达式
	重复执行的语句
循环之外的语句
  • 1
  • 2
  • 3

在循环中,有两个关键词用于控制循环:

  • break:强制终止循环,终止正在进行的循环结构
  • continue:跳过下面的语句,执行下一次循环

在Python中,可以用else来判断循环体是否被中断了

while bool表达式:    
	重复执行的语句 
else:    
	循环条件不满足的时候(循环正常结束) 
循环之外的语句
  • 1
  • 2
  • 3
  • 4
  • 5
for——通过迭代(遍历)的方式模拟循环

结构

for 变量 in 可迭代对象:    
	对可迭代对象中的每一个元素进行操作 
else:    
	全部对象都遍历完毕之后的时候 
循环之外的语句
  • 1
  • 2
  • 3
  • 4
  • 5

range方法:用于快速生成可迭代的纯数字结构
range可接受1、2、3个参数,均为大于0的整数

range(10)  # 0、1、2、3、4、5、6、7、8、9 
range(2, 5)  # 2、3、4 
range(1, 10, 2)  # 1、3、5、7、9
  • 1
  • 2
  • 3

range从Python3开始以生成器的身份出现(不会一下子把所有的数全部存放到内存中,而是在迭代或 者遍历过程中算出来)

小练习:水仙花数
如果有一个数,它的每一位的值得立方和等于其本身,那么就是水仙花数
例子 :
153 = 1 ^ 3 + 3 ^ 3 + 5 ^ 5 请通过Python求出1到1000范围内所有的水仙花数

总结一个求数字中每一位的通用公式

(num // 1 % 10)        # 个位 
(num // 10 % 10)       # 十位 
(num // 100 % 10)      # 百位 
(num // 1000 % 10)     # 千位
  • 1
  • 2
  • 3
  • 4

内置函数

https://docs.python.org/zh-cn/3/library/functions.html

Python的内置函数非常的丰富,包含了提供了诸多简单的方式处理问题,这里简单介绍一些内置函数

内置函数 作用
abs 求一个数的绝对值
bool 返回对象的布尔值

ord 将字符转为Unicode 码数字

chr 将Unicode 码数字转为字符

dir 列出包或者模块或者类当中所有的有效属性或者有效方法
id 获取一个对象的唯一标识(等效于内存地址)
len 返回长度(内部有多少元素)
max 求大
min 求小

sum 求和

round 设置精度(非四舍五入,采用银行家舍入,奇数进,偶数舍)

type 查看对象的类型

bin 将一个整数转变为一个前缀为“0b”的二进制字符串(二进制)oct 将一个整数转变为一个前缀为“0o”的二进制字符串(八进制)hex 将一个整数转变为一个前缀为“0x”的二进制字符串(十六进制)

字符串处理

字符串格式化

python中对于字符串常用format方法进行格式化操作

"".format()
  • 1

前面的字符串中需要占位符来限定格式化的操作

当format中有数字的时候,格式化可以在多加一些效果

"{<索引号>:<填充字符><对齐方式><宽度>}"

宽度: 设定字符的输出宽度(如果输出字符超过设定的宽度,会按照整个字符的宽度处理)
对齐方式:
    < 左对齐
    > 右对齐
    ^ 居中
填充字符: 填补空白区域,默认是空白
索引号: 对应formant
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
format中有数字时,格式化可以多加一些效果

"{<索引号>:<填充字符><对齐方式><宽度><,><精度数字><类型>}"
, : 是否显示千位分隔符
精度: 限定输出长度(小数部分或长度)
类型:
    b: 二进制
    c: unicode字符
    d: 十进制
    o: 八进制
    x: 十六进制
    X: 十六进制(大写模式)
        
    e: 科学计数法
    E:科学计数法大写
    f: 浮点数标准格式
    %: 按百分数形式输出
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

当然,字符串可以有很多额外的操作

r"" 	#转义字符
f""		#format偷懒方式
  • 1
  • 2
字符串常用方法

replace: 字符串替换

"".replace("被替换的字符串","替换的目标")
  • 1

split: 字符串分割

"".split(分割依据)  #默认非可见字符
  • 1

strip: 清除左右

"".strip(清除的字符) #默认非可见字符
  • 1

join: 合并字符串

'间隔符'.join(字符串)
  • 1

练习: 宝塔生成器

输入宝塔高度,作为宝塔高度

输出三角形塔:

1/3/5

居中,宽度40-60,

切片

切片不光可以用于字符串,列表、元祖也适用

Python中的索引规则

从左往右:0、1、2、3、4、。。。。
从右往左:-1、-2、-3、-4、。。。。

在Python中,切片使用[]来标识

"Python"[]
  • 1

切片的几种用法

"Python"[1]     # 等同于根据索引取值 
"Python"[1:]    # 从索引为1开始到结尾 
"Python"[:3]    # 从开头到索引为3截止(左闭右开) 
"Python"[:]     # 取全部

"Python"[-1]    # 从右往左取

"Python"[::2]   # 索引每隔2距离取
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

基本数据结构

在Python中有四大基本数据结构

列表

这个是Python中使用率高的一种结构,在结构上类似于其他编程语言的数组,但是是动态的(可自由 伸缩长度),列表中的元素可以是任何类型

关键词list

a = list()	#创建空列表
b = []		#

a = list(1, 2, 3)
b = [1, 2, 3]
  • 1
  • 2
  • 3
  • 4
  • 5

使用for操作列表

for i in 列表:
    操作
操作完毕
  • 1
  • 2
  • 3

在列表中常用的一些方法:

append: 在末尾添加元素

list().append(添加的内容)
  • 1

pop: 删除末尾元素,并返回元素值

list().pop()

a = [1,2,3,4,5]
	while len(a) > 0:
    print(a.pop())	#pop默认从后往前推,即栈结构,后入先出
print(a)

b = [1,2,3,4,5]
	while len(b) > 0:
    print(b.pop(0))	#pop(0)可以实现从前往后推,即队列,先入先出
print(b)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
字典

字典是一种key-value的形式,等同于其他语言中的map,hashmap

创建字典 关键词为dict

d = dict()	#创建一个空字典
d = {}		#同上

d = {
    "key1":任意对象,
    "key2":任意对象,
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

什么类型可以作为key:

在字典结构当中,一般用字符串或者整数作为key,一切不可变对象才可以作为key

在字典中,key是唯一存在的,value可以重复

字典中的一些方式

dict()["a"] = 1 #如果不存在key为a的,那么创建并赋值为1,存在就覆盖原值

del dict()['a'] #删除key为a的元素	del可以删除python中的任何对象
  • 1
  • 2
  • 3

字典常用的一个重要方法

dict().items()	#将字典转化为列表,列表中的每一个元素变为键值对的元组模式,在字典遍历很常用
  • 1
元组

元组是特殊的列表结构(只读)

关键词 tuple

创建元组

t = tuple()		#创建一个空元组
t = (1, )		#创建单元素元组
t = (1,2)		#创建多元素的元组,可不加括号
  • 1
  • 2
  • 3

元组的最大用途: 变量交换

a,b = b,a #a,b的值交换
  • 1
集合

性质于数学中的一致: 唯一,无序

集合没有value的字典,集合只能保存不可变类型

关键字set

s = set()	#创建空集合
s = {		#创建带数据的集合
    1,
    3,
    5,
    7,
}			
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

常用方法

set().add()		#添加元素
set().remove()	#删除元素 
  • 1
  • 2

模块的使用

  • 自己编写
  • 默认自带
  • 网上下载

在python中的模块调用使用import

import 模块
  • 1

模块搜索: 当前目录–> python的lib目录–> lib/site-package下

当引入自己编写的模块的时候,会生成一个 __ pycache __ 的文件夹,里面是被引入的模块的pyc版本 (被编译)

补充: python的包(多个模块在一个文件夹中)

https://docs.python.org/zh-cn/3/tutorial/modules.html#packages

在python项目中,会有_ init_.py的文件,用于初始化模块的一些功能(先搜索此文件),也是一个包的标识

常用内置库

了解这些主要的内置库

print(dir(模块名))

  • os模块

os.name #linux 系统的标识

os.uname

​ print(os.uname().sysname)

​ print(os.uname().nodename)

​ print(os.uname().machine)

os.pipe 管道

os.path #重要方法

​ os.path.isfile 判断是否为文件

​ os.path.islink 判断是否链接(软)

​ os.path.ismount 判断是否为挂载点

os.path.samefile(path1, path2)

如果两个路径都指向相同的文件或目录,则返回 True。这由设备号和 inode 号确定,在任一路径上调用 os.stat() 失败则抛出异常。

os.path.join(path, *paths)
合理地拼接一个或多个路径部分。返回值是 path 和 *paths 所有值的连接,每个非空部分后面都紧跟一个目录分隔符 (os.sep),除了最后一部分。这意味着如果最后一部分为空,则结果将以分隔符结尾。如果参数中某个部分是绝对路径,则绝对路径前的路径都将被丢弃,并从绝对路径部分开始连接。

在 Windows 上,遇到绝对路径部分(例如 r’\foo’)时,不会重置盘符。如果某部分路径包含盘符,则会丢弃所有先前的部分,并重置盘符。请注意,由于每个驱动器都有一个“当前目录”,所以 os.path.join(“c:”, “foo”) 表示驱动器 C: 上当前目录的相对路径 (c:foo),而不是 c:\foo。

os.path.split(path)
将路径 path 拆分为一对,即 (head, tail),其中,tail 是路径的最后一部分,而 head 里是除最后部分外的所有内容。tail 部分不会包含斜杠,如果 path 以斜杠结尾,则 tail 将为空。如果 path 中没有斜杠,head 将为空。如果 path 为空,则 head 和 tail 均为空。head 末尾的斜杠会被去掉,除非它是根目录(即它仅包含一个或多个斜杠)。在所有情况下,join(head, tail) 指向的位置都与 path 相同(但字符串可能不同)。另请参见函数 dirname() 和 basename()。


os.listdir() 列出当前工作目录

os.system(“shell命令”) #通过python操作linux

  • time 模块

    time.time() 时间戳

    time.localtime() 获取本地时间

    time.ctime() 可读日期(国外)

    time.strftime("%Y-%m-%d %H-%M-%S",time.localtime())


returncode
子进程的退出状态码. 通常来说, 一个为 0 的退出码表示进程运行正常.

一个负值 -N 表示子进程被信号 N 中断 (仅 POSIX).

stdout
从子进程捕获到的标准输出. 一个字节序列, 或一个字符串, 如果 run() 是设置了 encoding, errors 或者 text=True 来运行的. 如果未有捕获, 则为 None.

如果你通过 stderr=subprocess.STDOUT 运行, 标准输入和标准错误将被组合在一起, 并且 stderr` 将为 None.

stderr
捕获到的子进程的标准错误. 一个字节序列, 或者一个字符串, 如果 run() 是设置了参数 encoding, errors 或者 text=True 运行的. 如果未有捕获, 则为 None.


  • shutil 高级文件操作,可以替代很多体系工具

    shutil.copy2(src, dst, *, follow_symlinks=True)
    类似于 copy(),区别在于 copy2() 还会尝试保留文件的元数据。

    shutil.rmtree(’__ pycache __/’) #等效rm-rf

    shutil.rmtree(path, ignore_errors=False, οnerrοr=None)

    删除一个完整的目录树;path 必须指向一个目录(但不能是一个目录的符号链接)。

​ shutil.disk_usage(path)
返回给定路径的磁盘使用统计数据,形式为一个 named tuple,其中包含 total, used 和 free 属性,分别表示总计、已使用和未使用空间的字节数。 path 可以是一个文件或是一个目录。

例子:定制化处理数据

import shutil
disk_usage = shutil.disk_usage("/")
print("磁盘使用比:","{:E}".format(int(disk_usage.used)/int(disk_usage.total)))


  • 1
  • 2
  • 3
  • 4
  • 5


归档操作

https://docs.python.org/zh-cn/3/library/shutil.html

#!/bin/bash/env  python

import shutil

input("打包名")

input("格式")

input("打包位置")

shutil.make_archive("python","tar","?root/miniconda3/")
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

官方内置库:自我提升的平台https://docs.python.org/zh-cn/3/library/index.html

练习: 做一个文件分类器

分类指定目录下的多种文件(通过后缀名的方式),复制到归档用的文件夹中,按照后缀名区分文件, 在分类完毕之后归档(zip、tar)

可变类型与不可变类型

可变类型:

  • 字典,列表,集合

不可变类型:

  • 字符串,整数,浮点数,元组

区别:当变量指向同一个数据时,其中一个变量对数据修改,会不会导致其他变量中对应的值也发生变化

底层: C语言中指针概念

函数式编程

函数: 用于包装部分代码(需要多次使用)

在python中,一个函数的基本结构:

def 函数名(参数信息):
	代码语句
	return 返回值
  • 1
  • 2
  • 3

例子: 求两数之和

def add(x,y):
	return x+y
  • 1
  • 2
参数问题

参数和普通的变量一致,相当于参数=变量

在python中,可以使用特定的符号来限定参数的类型

def add(x: int, y: int) -> int: #开发工具会根据此内容去检查参数传递是否正确
     return x+y
  • 1
  • 2

在传入参数的时候,可以直接通过对参数赋值来传入数据(可以不按照函数定义的参数顺序从传入参 数)

当使用参数赋值法传入参数后,后面的参数也必须使用此方法

在函数定义的时候可以同时定义默认值:当没有对应参数的时候,使用默认值替代

def add(x: int, y=15) -> int:
    return x+y
  • 1
  • 2

python中函数没有重载

参数有两个特殊参数

  • **

通常情况下的用法

def fun1(*arg):		#只能接受普通参数形式,会变成一个元组形式
	print(arg)
def fun2(**karg):	#只能采用参数赋值形式,会变成一个字典形式
	print(karg)
  • 1
  • 2
  • 3
  • 4
返回值问题

当没有return时,默认返回None类型

在python中,return可以返回多个数据,通过逗号分隔,返回元组

文件IO

如何使用python操作文件

使用内置函数open

基本流程

f = open(文件名,打开方式)
进行文件操作
f.close()
  • 1
  • 2
  • 3
文件的打开方式
打开方式解释
r
w
a写(追加)
[r,w,a]b以二进制方式
[[r,w,a]b]+开启读,写
文件对象的操作

常用的几个操作

f.read() 	#读取文件中的所有数据

f.readline()	#读取一行数据(根据换行符)	文本类型使用

f.readlines()	#读取所有数据并且根据行分割成为列表	文本类型使用

f.write()		#向文件中写入数据

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

分段读取文件:

f = open("/etc/yum.conf", 'r')

while 1:
    t = f.read(10)
    if len(t)>0:
        print(t)
    else:
            break

f.close()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

二进制数据与文本类型数据的相互转换

t = b'[main]\ngpgcheck=1\ninstallonly_limit=3\nclean_requirements_on_remove=True\nbest=True\n'

t = t.decode("utf-8")

t = [main]\ngpgcheck=1\ninstallonly_limit=3\nclean_requirements_on_remove=True\nbest=True\n'
# 以bytes类型写入

s = s.encode("utf-8")	#编码
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
上下文管理器with

单独写f.close()容易忘记,怎么办

with open("/etc/yum.conf", 'r')as f:
    执行操作
(当离开with区域,会自动关闭文件)
  • 1
  • 2
  • 3

第三方库的安装

最常用的方式: pip

(py38)# pip
(py38)# python -m pip
  • 1
  • 2

pip的基本使用同yum,dnf,apt

pip install 包名
pip search 包名
pip list #显示安装的包
pip unistall 包名
pip update 包名
  • 1
  • 2
  • 3
  • 4
  • 5

利用pip生成环境配置文件(尤其是当使用venv方式生成虚拟环境的时候)

pip freeze > requirements.txt	#生成一个第三方库的环境列表

pip install -r requirements.txt	#根据环境列表安装所有的第三方库
  • 1
  • 2
  • 3

python -m venv my_python 生成一个python环境

python -m pip install --upgrade pip 更新pip环境

当使用conda环境时

conda list -e > requirements.txt 	#基于conda导出所有第三方库的环境列表

conda install --file requirements.txt	#在环境创建好之后基于有第三方库的环境列表安装环境
  • 1
  • 2
  • 3

pip源的网站:https://pypi.org/

awesome——python:Python常用第三方库的综合

https://github.com/jobbole/awesome-python-cn/

https://awesome-python.com/

网络请求http

基于requests库

Python默认自带了urlib库用于处理HTTP相关任务,但是urlib库不是很好用

所以我们使用requests库

pip install requests

conda install requests
  • 1
  • 2
  • 3

引入模块

import requests
  • 1

利用requests库访问页面

import requests

r = requests.get("https://www.python.org") 
print(r.text)
  • 1
  • 2
  • 3
  • 4

HTTP请求的方式: (RESTful)

GET: 用于获取资源,传递的参数会在url中体现(一般无需用户认证)

POST: 用于提交,上传数据,传递的数据一般在HTTP请求体中(安全性更高)

PUT: 用于创建新数据

DELETE: 用于删除数据

我们案例以 以https://www.sojson.com/api/semantic.html 提供的免费接口来模拟

URL:http://api.qingyunke.com/api.php?key=free&appid=0&msg=鹅鹅鹅

补充知识: (基于浏览器开发者面板F12)

cookie: :HTTP由于是无状态网络会话,cookie是由服务器发出要求保存着浏览器端的一些数据,用于保存会话状态(用户状态)

user-agent: 用于告诉服务器客户端的一些信息,(如浏览器的版本,类型,操作系统)

利用cookie和user-agent可以让我们的程序伪装成真实浏览器去完成相应的操作

然而在很多时候,requests操作有api提供的接口会很方便,但是有时候一些平台没有api接口,那么我 们就需要使用一些技术手段来实现

基于selenium框架

selenium是一个原本用java编写的一个用于自动化操作浏览器的工具,开始用于网站页面的自动化测试

selenium提供了python的接口,可以让我们用python很轻松的去操作浏览器访问web外部面板

实验将使用cockpit面板来测试

启动面板

systemctl enable --now cockpit.socket
  • 1

既然要操作浏览器,而且不同的浏览器,需要使用不同的浏览器驱动,本次实验我们使用的是chrome 浏览器

一般来说,我们使用windows来执行
需要安装selenium框架,需要下载对应的浏览器驱动

pip install selenium
conda install selenium
  • 1
  • 2

由于谷歌网站访问有难度,所以下载浏览器驱动可能会困难,所以从国内的镜像站下载驱动
https://npm.taobao.org/mirrors/chromedriver/

下载的时候注意与自己浏览器的版本要对应

将驱动和代码放在一个文件夹中

利用Python启动一个浏览器并且打开我们的web面板

# 这是一份样例代码(能跑,但是很多地方没好好的打磨) 
from selenium import webdriver 
import time 
# 让浏览器可以在后台运行(无需界面),这种方式往往会有一些小问题 
# from selenium.webdriver.chrome.options import Options

# ch_options = Options() 
# ch_options.add_argument("--headless")
# web = webdriver.Chrome(options=ch_options) 
web = webdriver.Chrome() 
web.get("http://192.168.218.147:9090/")

# 自动点击强行访问不安全链接 
time.sleep(1) 
web.save_screenshot("1.png") 
button = web.find_element_by_xpath("/html/body/div/div[2]/button[3]") button.click()

time.sleep(1) 
url_button = web.find_element_by_xpath("/html/body/div/div[3]/p[2]/a") url_button.click()

# 登录 
time.sleep(1) 
web.save_screenshot("2.png") 
user_name = web.find_element_by_xpath("/html/body/div[2]/div/div[2]/div/div[4]/input") 
user_name.send_keys("root")

time.sleep(1) 
pass_word = web.find_element_by_xpath("/html/body/div[2]/div/div[2]/div/div[5]/input") 
pass_word.send_keys("root")

time.sleep(1) 
login_button = web.find_element_by_xpath("/html/body/div[2]/div/div[2]/div/div[8]/button") 
login_button.click()

time.sleep(1) 
# 登录成功了 
web.maximize_window() # 最大化 
web.save_screenshot("2.png")

time.sleep(1) 
web.switch_to_frame("cockpit1:localhost/system") # 当页面中存在iframe框架的时候,需要 通过iframe的id或者name来切换到框架中 
count = 0 
while True:
 	time.sleep(0.5)    
	cpu_info = web.find_element_by_xpath("/html/body/div[1]/div/main/section[2]/div/article[2]/ div[2]/table/tbody/tr[1]/td/div/div[2]/span").text   	 print(cpu_info)   
    if count < 10:       
        count = count + 1   
    else:        
        break 
web.switch_to_default_content() # 从内部的iframe中回到整体页面中

web.close() # 关闭浏览器

  • 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
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53

案例: day3 3:00-4:00 页面获取监控面板

selenium可以直接操作浏览器,同时可以捕获浏览器上的各种数据,特别适用于没有api接口的网页

可以将部分重复操作通过自动化的方式处理掉(CMBD)

json

json是一种特殊的字符串,常用于Http级别上的数据交互,json是Javascript中的数据结构,但是被广泛的编程语言支持,在python中有专门的json内置库用于处理json数据,与字典关系密切

json.loads(json字符串)		#将json字符串变为字典

json.dump(字典)			#将字典编程json字符串
  • 1
  • 2
  • 3

正则表达式

正则表达式是所有编程语言解决处理字符串的第一选择

正则匹配对象
\d单个数字
\D单个非数字符
\s单个不可见字符
\S单个可见字符
\w单个字母,数字,下划线,汉字
\W单个符号(下划线除外)
.除换行外所有字符
单独的字母或数字对应的匹配
^从字符串首位开始
$到字符串尾部截止
*重复0或者多次
+重复1次或多次
?重复0或1
{n}重复n次
{n,}重复n次或多次
{n,m}重复n到m次
[]匹配字符集

https://tool.oschina.net/regex/ 正则匹配在线工具

在正则表达式中,贪婪模式和非贪婪模式

当单独使用* , + , ? , {} 默认情况下使用贪婪模式:尽可能匹配出最长的字符串

贪婪模式在大多数时不满足我们的需求

在上面的符号基础上再加上?实现懒惰模式:尽可能的匹配短字符串

在正则表达式中,小括号可以限定输出内容

在python中用于正则的是re模块

import re

    parren = re.compile(r'正则字符串')	#获取一个正则规则

	paeren.findall(待匹配的字符串)		#通过正则处理字符串,符合条件的结果为一个列表
  • 1
  • 2
  • 3
  • 4
  • 5

小练习:

172.69.35.5 - - [11/Aug/2020:16:42:01 +0800] “GET / HTTP/1.1” 200 730 “-” "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:79.0) Gecko/20100101 Firefox/79.0

统计出现的ip地址,请求类型,相应码

三个服务器的日志文件,

三个数据单独存放,

保存为网址.txt格式

内容预览:

172.69.35.5,GET/HTTP/1.1,200

.csv 文档格式的表格

异常处理

程序在执行的过程中,往往会发生一些意外情况

用if分支语句提前处理

让用户输入两个数字,前面被除数,后面是除数,(重点: 除数不能为零)

  • 第一种:正则

  • 第二种:试一试(try)

异常处理对于一个程序的健壮性有着非常重要的作用

异常处理的结构

try:
    可能会发生问题的代码
except 捕获什么异常:
    针对出现的问题怎么处理
else:
    代码try没有发生错误时指定的代码
finally:
    不管之前是否出现异常都一定会执行
其他代码
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
文件IO时需要异常处理(文件的权限,文件存在性)
try:    
    f = open("a.txt", 'r')    
    result = f.read()    
    print("文件读取完毕")    
    print("文件内容为:")    
    print(result) 
except FileNotFoundError as e: #错误码类型(py自带的功能,几乎全部错误码类型)   
    print("文件不存在")
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

socket通信

socket: 套接字

socket是绝大多数实现网络通信的基本

绝大多数编程语言,scoket == TCP/IP

在编程中,Http相关操作基于socket

socket让多台设备之间可以通过网络数据包的形式传输数据(分布式,集群,微服务)

编程思路:

C/S

服务端:

  • 创建套接字类型的对象
  • 绑定ip地址和端口
  • 死循环监听
  • 如果收到信息,处理信息,可能还需要返回数据
  • 关闭当前链接

客户端:

  • 创建套接字类型的对象
  • 连接服务器
  • 发送或接受数据
  • 关闭链接
tcp

案例:

服务端: Linux

客户端:Windows

server.py

import socket

server = socket.socket(socket.AF_INET,socket.SOCK_STREAM)

# scoket.AF_INET : IPv4		socket.SOCK_STREAM : 流式socket(TCP)
# scoket.AF_INET6 : IPv6	socket.SOCK_DDGRAM : 报文socket(UDP)

host = "192.168.199.129"
prot = 60000

server.listen(5)	#排队的链接最多可以为多少

client_socket,addr = server.accept()   # 返回一个元组,第一个为客户端的socket对象,后面为客户端的地址

recvmsg = client_socket.recv(1024)     # 因为是数据流的形式,如果不加一次性读取的数据, 那么就会一直等待结束
strmsg = recvmsg.decode("utf-8") 
print(strmsg)

server.close()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

cleint.py

import socket

client = socket.socket(socket.AF_INET,socket.SOCK_STREAM)

host = "192.168.199.129" 
port = 60000

client.connect((host,port))

client.send("这是从客户端发送过来的一条数据".encode("utf-8"))

client.close()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

修改一下,让客户端可以接受用户输入的命令,然后服务器执行,返回执行的结果:

server.py 写在服务端

import socket 
import subprocess

server = socket.socket(socket.AF_INET,socket.SOCK_STREAM)

host = "192.168.199.129" 
port = 40000

server.bind((host,port))

server.listen(5)

while True:    
    client_socket,addr = server.accept()
    
    recvmsg = client_socket.recv(1024)    
    strmsg = recvmsg.decode("utf-8")    
    result = subprocess.run(strmsg,shell=True,capture_output=True)           
    client_socket.send(result.stdout)     
    
    server.close()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

client.py 写在客户端

import socket

client = socket.socket(socket.AF_INET,socket.SOCK_STREAM)

host = "192.168.199.129" 
port = 40000

client.connect((host,port))

cmd = input("请输入你想要执行的命令:\n")

client.send(cmd.encode("utf-8"))

msg = client.recv(1024) 
print(msg.decode('utf-8'))

client.close()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

可以通过socket的方式批量与多设备之间交互(saltstack)

UDP

对于server而言

import socket 
import subprocess

server = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)

host = "192.168.199.129" 
port = 40000

server.bind((host,port))

while True:
    recvmsg,addr = server.recvfrom(1024)    
    strmsg = recvmsg.decode("utf-8")    
    result = subprocess.run(strmsg,shell=True,capture_output=True)            
    server.sendto(result.stdout, addr)     
    
    server.close()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

对于client

import socket

client = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)

host = "192.168.199.129" 
port = 40000

cmd = input("请输入你想要执行的命令:\n")

client.sendto(cmd.encode("utf-8"),(host,port))

msg = client.recvfrom(1024) 
print(msg.decode('utf-8'))

client.close()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

day4 10:30~11:30(上面的例子)

优缺点

优点:速度快(TCP/IP),支持自动发现

缺点: c/s结构, 代码编写有一定的难度


面向对象

代码封装为一个类,和函数类似,也是代码的复用,不过类比函数多了很多功能

术语

类:

把相似的东西共同的共同点总结在一起

属性: 变量

方法: 函数(行为)

实例:基于一个类的具体实现

定义一个类:

class Human:
    pass
  • 1
  • 2

实例化一个类

human1 = Human()	#造人
  • 1

给实例添加属性

human1.name = "小明"
  • 1

通过 __ init __ 方法实现在实例化的同时添加属性

class Human(object):
	def __init__(self,name):
        self.name = name
  • 1
  • 2
  • 3

补充: self是什么 —(self不是专有词)

self指被实例化的对象,实例

  • 类属性与实例属性

类属性由类和其实例共有,实例属性只有实例才有

class Human(object):    
    count = 0    # 类属性    
    class_name = "人类"    
    def __init__(self, name):        
        self.name = name   # 实例属性        
        Human.count = Human.count + 1    
    def go(self):        
        print(self.name,"跑起来")
        
human1 = Human("小明")
human2 = Human("小刚") 
human3 = Human("小红") 
print('造人数量:',Human.count) # 设计模式——单例模式
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
类 高级用法:
  • 继承:

Pyth支持多继承,继承的都可成为父类,但只有第一个为主要(覆盖后面继承的一些属性或者方法)

面向对象编程:

在于规范后面的代码,使用方便,增添功能方便
继承的层级太多的时候就很麻烦了

  • 私有属性和方法:

对于一些方法或者属性,不希望被调用

通过在方法或属性前面添加两个下划线实现私有封装(强制性): 只能通过类自身或者内部方法处理,子类也无法使用

单个下划线的属性或方法不进行直接访问

  • 魔术方法:

前面两个下划线,后面*两个下划线,*创建类时自带的

__ init__ :在类实例化时执行

__ new __ :实例化一个类

__ str__ : 优先影响print语句的输出,实际上是str函数对这个对象的操作的方法

__ repr__ : 次要影响print语句的输出,实际上是repr函数对这个对象的操作的方法

int() -> __ int __

float() -> __ float __


RPC(微服务的基本)

RPC基于socket,是早期实现远程操作的一种方式(高级方式)

RPC对网络层面的操作进行封装,同时通过接口数据的格式实现跨语言调用

RPC的实现:

  • TCP/IP + 二进制
  • HTTP(RESTful) + 文本数据(yaml、xml、json、自定义)

在Python中,自带了xmlrpc框架,基于xml和http实现的RPC通信手段

RPC也是C/S架构
对应两个库
xmlrpc.server

xmlrpc.client

最简单demo

serverrpc.py

from xmlrpc.server import SimpleXMLRPCServer
import subprocess
host = "192.168.199.129"
port = 9000

server = SimpleXMLRPCServer((host,port))

#定义可用方法


def get_uname():
    result = subprocess.run("uname-a",shell = True,capture_output=True)
    return result.stdout

server.register_function(get_uname)
server.serve_forever()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

clientrpc.py

import xmlrpc.client

server = xmlrpc.client.ServerProxy("http://192.168.199.129:9000")

result = server.get_uname()

print(result)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

数据库

原始日志分析: 人肉

高级一些日志分析: 电子表格

线代日志分析: 数据库

数据库是什么: 专门用于数据存储的,并且提供了高效的检索能力

数据库有专门的编程语言----SQL

数据库种类:

  • 关系型数据库(SQL)
  • 非关系数据库(NOSQL)

编程中常用的几种数据库:

  • sqlit:单文件级别的数据库,使用简单,性能较好,支持基本的sql,在移动平台使用率极高
  • mysql(mariadb): 目前互联网行业使用率最高的数据库,支持sql
  • mongoDB: 非关系数据库,采用键值对的方式存储数据,一般用于文件存储
  • Redis: 非关系数据库,内存数据库,常用于缓存,消息队列
(自学)在运维中,有一整套完整日志收集,存储,分析,展示的套件–ELK

Elasticsearch: 日志存储,检索

Logstash: 收集日志

Kibana: 展示

一个数据库内部会包含多个表、每个表内部会有多个字段,我们写在表中的数据成为一条记录,对照 excel

Python是如何操作数据库(sqlit3)的

import sqlite3 
db = sqlite3.connect("./python.db")

cur = db.cursor() # 创建一个游标(打开数据库交互界面)

sql = "" 
cur.execute(sql)

cur.close() 
db.close()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

那么sql的写法?

  • 创建表
CREATE TABLE log(        /* 创建一张表 */    
    ip char(50),         /* 字段和字段的数据类型  int char date float text */    
    function char(100),    
    status char(10) 
);

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 删除表
DROP TABLE log;    /*删除一张表*/
  • 1

不可以创建一个已经存在的表,或者删除不存在表

  • 插入数据:
INSERT INTO log (ip, function, status) value (v1,v2,v3) /* 标准写法 */

INSERT INTO log  values (v1,v2,v3)  /*写入的时候,传入的数据个数要和表的列数统一,且同序 */
  • 1
  • 2
  • 3

在插入语句执行之后需要额外执行一句

commit;
  • 1

让数据能够保存到数据库中

  • 查找数据:
SELECT 列 FROM 表 WHERE 条件;

sql = "SELECT ip,function,status FROM log WHERE status='200';"
result = cur.execute(sql).fetchall()
print('108.162.245.110请求数量为:',len(result))
  • 1
  • 2
  • 3
  • 4
  • 5

查询时可用的一些聚合函数

SELECT count(*) FROM log WHERE ip='108.162.245.110';
result = cur.execute(sql).fetchall()
print(result[0][0])
  • 1
  • 2
  • 3
  • 删除
DELETE FROM 表 WHERE 条件

sql = "DELETE FROM log WHERE ip='108.162.245.110';"
result = cur.execute(sql).fetchall()
print(result)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 修改数据
UPDATE 表 SET 字段 = 新的值 WHERE 条件

sql = "UPDATE log status = '200' WHERE status = '404';"
result = cur.execute(sql).fetchall()
print(result)
cur.execute("commit;")   /*提交确认
 */
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

时间: day4 16:00-16:40

为啥我们修改了数据之后,数据没有及时的变更呢?
关系型数据库的事务处理
当增、删、改的时候,需要执行commit

小挑战:
1、统计一下访问域名次数最多的IP地址是哪个(三个log文件的综合) 2、利用socket或者xmlrpc做一个监控(windows作为客户端,linux作为服务端,可交换),监控内存 占用情况(每5秒记录一次)

多进程与多线程

我们之前写的所有程序都是单进程单线程的,在面对大量任务的时候就会非常耗时间

进程与线程的区别

线程是程序的最小执行单元,一个进程至少有一个线程

线程之间共享进程的内存空间,进程与进程之间的数据是隔离的

一般来说,线程适合IO密集型,进程适合cpu密集型

在Pyth中*, GIL锁机制* (CPython专属)

在Python中虽然可以多线程,但是真实的执行依旧是单线程模式

多进程

内置multiprocessing库用于实现跨平台多进程的实现

from multiprocessing import Process
import os
import time


def proc(name):
    print("{}进程正在执行,pid为{}".format(name, os.getpid()))
    time.sleep(5)
    print("{}进程执行完毕,pid为{}".format(name, os.getpid()))


if __name__ == "__main":  # 一定要确保这个是主进程
    print("{}进程启动,pid为{}".format('主', os.getpid()))
    p1 = Process(target=proc, args=('proc1',))
    print("proc1进程创建完毕")
    p2 = Process(target=proc, args=('proc2',))
    print("proc2进程创建完毕")
    p1.start()
    p2.start()
    p1.join()
    p2.join()
    print("执行完毕")
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

利用列表来管理多个进程

from multiprocessing import Process
import os
import time


def proc(name):
    print("{}进程正在执行,pid为{}".format(name, os.getpid()))
    time.sleep(5)
    print("{}进程执行完毕,pid为{}".format(name, os.getpid()))


if __name__ == "__main":  # 一定要确保这个是主进程
    print("{}进程启动,pid为{}".format('主', os.getpid()))
    p = []
    # 批量创建进程对象
    for i in range(20):
        p.append(Process(target=proc, args=('proc'+str(i),)))
        print("proc{}进程创建完毕".format(i))
    for i in p:  # 批量启动
        i.start()
    for i in p:  # 等待全部完成
        i.join()

    p1 = Process(target=proc, args=('proc1',))
    print("proc1进程创建完毕")
    p2 = Process(target=proc, args=('proc2',))
    print("proc2进程创建完毕")
    p1.start()
    p2.start()
    p1.join()
    p2.join()
    print("执行完毕")

  • 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
  • 31
  • 32
  • 33

问题思考,如果直接开2000个进程呢?

并发执行的进程数量与cpu核心数一致

进程的创建与销毁是非常占用资源的

解决方案: 进程池

#进程池
from multiprocessing import Pool
import os
import time

def proc(name):
    print("{}进程正在执行,pid为{}".format(name,os.getpid()))
    time.sleep(10)
    print("{}进程执行完毕,pid为{}".format(name,os.getpid()))

if __name__ == "__main__":
    print("{}进程启动,pid为{}".format('主',os.getpid()))
    p = Pool()
    for i in range(20):
        p.apply_async(proc,("proc{}".format(i),))
    p.close()   #不让新的进程进来 
    p.join()    #等待池子里的进程都执行完毕
    print("执行完毕")
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

pool可以传入一个整数,用来控制池子的大小,默认值为cpu核心数

subprocess基于多进程技术

多线程
import threading #多线程库
import time

def thred():
    print('线程{}开始工作'.format(threading.current_thread().name))
    time.sleep(5)
    print('线程{}结束工作'.format(threading.current_thread().name))

if __name__ == "__main__":
    print("主线程{}开始工作".format(threading.current_thread().name))
    t1 = threading.Thread(target=thred)
    t2 = threading.Thread(target=thred)
    t1.start()
    t2.start()
    t1.join()
    t2.join()
    print("主线程{}结束工作".format(threading.current_thread().name))
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

多线程的安全性:

由于线程之间操作数据的顺序是不一致的,在操作共享数据的时候可能会出现问题

解决方法: 锁机制

锁保证了程序执行的顺序,防止多个线程访问公共数据导致数据混乱

但锁会降低多线程性能,极端情况将多线程编程单线程

#锁
import threading
import time

balance = 1000
lock = threading.Lock()

def change(n):
    global balance  #在函数中,声明调用外部的变量,让函数可用修改外部的变量
    balance = balance + n
    balance = balance - n

def run(n):
    for i in range(10000000):
        lock.acquire()  #获取锁
        try:
            change(n)
        except Exception as e:
            print(e)
        finally:
            lock.release()

if __name__ == "__main__":
    t1 = threading.Thread(target=run,args=(500,))
    t2 = threading.Thread(target=run,args=(800,))
    t3 = threading.Thread(target=run,args=(100,))

    t1.start()
    t2.start()
    t3.start()
    t1.join()
    t2.join()
    t3.join()
    print(balance)
  • 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
  • 31
  • 32
  • 33
  • 34
生产者与消费者模型(队列)

day5 11:00

两个线程,一个数据共享区模拟生产者和消费者模型

一个线程模拟生产者,一个模拟消费者

列表模拟数据共享区

import threading
import time
import random   #随机数库

MAX_LEN = 5

buff = []
lock = threading.Lock()

class Produce(threading.Thread):
    def run(self):
        global buff #全局调用
        while True:
            lock.acquire()  #上锁
            if len(buff) < MAX_LEN:
                num = random.randint(0,100)
                buff.append(num)
                print("生产产品.编号:",num)
            lock.release()
            time.sleep(random.randint(1,5))

class Consumer(threading.Thread):
    def run(self):
        global buff
        while True:
            lock.acquire()
            if buff:
                num = buff.pop(0)
                print("消费产品.编号",num)
            lock.release()
            time.sleep(random.randint(1,5))

if __name__ == "__main__":
    p = Produce()
    c = Consumer()

    #使用守护线程方式
    p.setDaemon(True)
    c.setDaemon(True)

    try:
        p.start()
        c.start()
        p.join()
        c.join()
    except KeyboardInterrupt:#按下Ctrl+C
        print("运行结束")
  • 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
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47

Python中提供了一个线程安全的类型: queue(队列)

import threading
import time
import random   #随机数库
from queue import Queue #队列
MAX_LEN = 5

buff = Queue(MAX_LEN)

class Produce(threading.Thread):
    def run(self):
        global buff #全局调用
        while True:
            num = random.randint(0,100)
            buff.put(num)
            print("生产产品.编号:",num)
            time.sleep(random.randint(1,5))

class Consumer(threading.Thread):
    def run(self):
        global buff
        while True:
            num = buff.get()
            print("消费产品.编号",num)
            time.sleep(random.randint(1,5))

if __name__ == "__main__":
    p = Produce()
    c = Consumer()

    #使用守护线程方式
    p.setDaemon(True)
    c.setDaemon(True)

    try:
        p.start()
        c.start()
        p.join()
        c.join()
    except KeyboardInterrupt:#按下Ctrl+C
        print("运行结束")
  • 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
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40

补充:

协程(微线程):在单线程上实现多线程的效果,切换可控,协程无需锁机制

同步异步(烧开水例子):

  • 同步版本:坐着等水开
  • 异步版本:水壶上加喇叭

具体: ngnix

psutil

Python中很不错的第三方库,方便监控的系统很多信息

由于是第三方库,所有需要额外的安装

pip install psutil

conda install psutil
  • 1
  • 2
  • 3

https://psutil.readthedocs.io/en/latest/

import psutil
while True:
    print(psutil.cpu_precent(5))	#获取cpu占用率
    
    
print(psutil.virtual_memory().total//1024//1024)#查看内存


  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

利用Python发送电子邮件

SMTP协议: 简单邮件传输协议

在Python中自带了smtplib用于处理SMTP协议相关的任务

email是有格式的

  • 主题
  • 正文
  • 附件

在Python中有emil库,用于邮件类型文件的排版

pip install PyEmail

import smtplib
from email.header import Header     #邮件主题
from email.mime.text import MIMEText    #文本正文

#构造一个邮件
massage = MIMEText("这是一封测试邮件",'plain','utf-8')
massage['Subject'] = Header("Python发邮件","utf-8")

my_mail = "1293517197@qq.com"
my_passwd = "fzyqoubngsvsfgbd"

#登录邮箱服务器,然后发送邮件

mail = smtplib.SMTP_SSL('smtp.qq.com')   #有ssl安全
mail.connect("smtp.qq.com",465)
mail.login(my_mail,my_passwd)
mail.sendmail("fanssi@qq.com",'1826386682@qq.com',massage.as_string())	#("发件人",'收件人')
print("邮件发送成功")
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

用yagmail库发送邮件

pip install yagmail


import yagmail

my_mail = "1293517197@qq.com"
my_passwd = "fzyqoubngsvsfgbd"

contents=[
    '在吗'
]
mail = yagmail.SMTP(user=my_mail,password=my_passwd,host='smtp.qq.com')
print("链接成功")
mail.send(to="1826386682@qq.com",subject="许亚琪",contents=contents)
print("发送成功")
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

Fabric

三种版本

Fabric2:现在新版本的产物,可以更好和代码结合(安装时候的默认)

Fabric3:早期产品的python3实现

http://www.fabfile.org/

这个库基于paramiko库(Ansible也基于这个库)

paramiko–python版本的openssh的实现

安装

conda install fabric
pip install fabric


from fabric import Connection

centos = Connection("192.168.199.129",user='root',connect_kwargs={"password":'0'})
result = centos.run("yum update -y",hide=True,)
print(result.command)
print(result.stdout)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

使用windows操作linux

from fabric import Connection

centos = Connection("192.168.199.129",user='root',connect_kwargs={"password":'1'})
centos.get('/root/Miniconda3-py38.sh')  #从服务器上下载文件
centos.put('python.db','/root/code/python.db')  #上传文件
with centos.cd("/root/code"):   #执行操作,操作结果返回到终端
    centos.run("ls -l")

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

run: 在目标端执行命令

cd: 配合with使用修改工作路径

put:上传文件

get: 下载文件

sudo: 使用管理员身份运行(最好配置sudo免密)

invoke:从早版本的Fabric中脱离出来的一个新项目,用于与shell交互

http://docs.pyinvoke.org/en/latest/api/context.html#invoke.context.Context.sudo

\

day5 15:30

Django web框架


几乎70%的运维平台由django组成

https://docs.djangoproject.com/zh-hans/3.1/ (文档质量较高)

web开发三层架构: MVC

M: 模型层,将数据库中的表抽象成面向对象编程中的类,ORM技术

V:视图层,基于模板引擎(jinja2),渲染显示页面

C: 控制层,用于处理请求

django开发的三层架构:MVT

M = M

V = C

T = V

安装Django

conda install django
pip install django
  • 1
  • 2

在安装之后有一条命令可用

django-admin

  • 1
  • 2

django-admin startproject pyweb #创建项目

修改settings.py language 为 zh-hans,时区改为上海

切换到项目目录下

执行python manage.py runserver启动服务器

python manage.py runserver 0.0.0.0:8000

ALLOWED_HOSTS = [] 内添加 ‘192.168.199.129’

重启服务器python manage.py runserver

实际开发功能模块都是在项目中创建app

使用python manage.py startapp 文件名 创建app

写一个web站点版本的helloword

pyweb/myapp/views.py

from django.shortcuts import render
from django.http import HttpResponse

# Create your views here.
def hello(request):
    return HttpResponse("你好,这是我们创建的web站点")
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

通过路由指定我们通过什么方式来访问这个方法

pyweb/urls.py

from django.contrib import admin
from django.urls import path
from myapp import views
urlpatterns = [
    path('admin/', admin.site.urls),
    path("index",views.hello),
]
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

一般在myapp里创建urls.py

/myapp/urls.py

from django.urls import path
from . import views #.表示自己

urlpatterns = [
    path('',views.hello),   #''表示所有
]
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

在此之前需要修改pyweb/urls.py的内容为

from django.contrib import admin
from django.urls import path
from django.urls import include

urlpatterns = [
    path('admin/', admin.site.urls),
    path("index/",include("myapp.urls")),
]
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

更好的url管理是在项目中将url管理分配到每一个app中单独管理

用ORM操作数据库

pyweb/myapp/models.py

from django.db import models

# Create your models here.


class Log(models, Model):
    ip = models.CharField("IP地址", max_length=30)
    function = models.TextField("访问情况", max_length=200)
    status_code = models.CharField("响应码", max_length=5)

    class Meta:
        verbose_name = "访问日志"
        verbose_name_plural = "访问日志"

#修改表
def __str__(self):
    return 'ip: {},访问方式:{},响应码:{}'.format(self.ip, self.function, self.status_code)

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

使用命令: 进行表数据迁移

利用Django自带的后台管理界面来操作这个数据表

pyweb/myapp/admin.py

from django.contrib import admin
from .models import Log

# Register your models here.
class LogAdmin(admin.ModelAdmin):
    list_display = ('ip','function','status_code',)
    list_display_links = ('ip','function','status_code',)


admin.site.Register(Log,LogAdmin)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

创建一个后台管理员账号

python manage.py createsuperuser
  • 1

在pyweb/settings.py中 INSTALLED_APPS 里添加’myapp’

python manage.py makemigrations #导入表

python manage.py migrate

开启服务器,访问admin页面

http://49.234.224.134:8000/admin/

添加数据

在my_app/views.py中添加

def add_info(request):
    obj = Log(ip='',function='',status_code='').save()
  
  • 1
  • 2
  • 3
自我提高的内容

Django: 模型层 的基本使用:定义查询; 能用视图层 对模型层进行增删改查

https://docs.djangoproject.com/zh-hans/3.1/

Django restful: https://www.django-rest-framework.org/

python 的基本库的使用: requests , os , sys , pathlib , shutil , haslib , logging , concurrent.futures(一个库实现多进程多线程)

自动化运维的前提是需要了解运维,

深入学习需要了解一些工具: elk ,ansible , zabbix

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Cpp五条/article/detail/293648
推荐阅读
相关标签
  

闽ICP备14008679号