一、Python 安装

一、Python 应用场景

1.Web应用开发
2.自动化运维
3.科学计算
4.桌面软件
5.服务器软件
6.游戏
7.人工智能
8.数据分析

二、Python3 开发环境搭建

1、Python2

​ 在大多数Linux系统上都已经有集成好了 Python2 开发环境,不需要安装就可以执行Python代码了

①.验证 Python2 环境是否搭建好:Ctrl + Alt + t 打开终端输入命令

$ python

②.退出 Python2 环境命令:exit() 或 Ctrl+d

2、Python3

1、在 /opt 目录下创建目录

$ rm -rf ./*
$ mkdir datas
#用于存放数据
$ mkdir software
#用于存放软件包
$ mkdir app
#用于存放程序

2、安装

1.首先安装依赖
$ yum -y install openssl-devel openssl-static zlib-devel lzma tk-devel xz-devel bzip2-devel ncurses-devel gdbm-devel readline-devel sqlite-devel gcc libffi-devel 

2.下载并安装软件包	官网:www.python.org
$ tar -zxf Python-3.7.6.tgz -C /opt/app/

3.进入Python目录 执行配置文件
$ cd /opt/app/Python-3.7.6/
$ ./configure 

4.编译安装
$ make && make install

5.生成python3的软连接
$ ln -s /opt/app/Python-3.7.6/python /usr/bin/python3
#方便命令的使用

6.验证是否安装成功
$ python3	
#能够进入python3的交互式命令行代表成功。

三、Python 执行过程解析

1、解析过程

Python语言写的程序不需要编译成二进制代码。 可以直接从源代码运行程序。 在计算机内部, Python解释器把源代码转换成称为字节码的中间形式,然后再把它翻译成计算机使用的机器语言并运行。

2、Python 解释器

① CPython:这个解释器是用C语言开发的,所以叫CPython。在命令行下运行python就是启动CPython解释器。CPython是使用最广的Python解释器

② IPython:IPython是基于CPython之上的一个交互式解释器,也就是说,IPython只是在交互方式上有所增强,但是执行Python代码的功能和CPython是完全一样的。好比很多国产浏览器虽然外观不同,但内核其实都是调用了IE。 CPython用>>>作为提示符, 而IPython用In [序号]:作为提示符。

③ PyPy:PyPy是另一个Python解释器,它的目标是执行速度。PyPy采用JIT技术, 对Python代码进行动态编译(注意不是解释),所以可以显著提高Python代码的执行速度。 绝大部分Python代码都可以在PyPy下运行,但是PyPy和CPython有一些是不同的,这就导致相同的Python代码在两种解释器下执行可能会有不同的结果。 如果你的代码要放到PyPy下执行,就需要了解PyPy和CPython的不同点。

④ Jython:Jython是运行在Java平台上的Python解释器, 可以直接把Python代码编译成Java字节码执行。

⑤ IronPython:IronPython和Jython类似, 只不过IronPython是运行在微软.Net平台上的Python解释器,可以直接把Python代码编译成.Net的字节码。

小结:Python的解释器很多,但使用最广泛的还是CPython。如果要和Java或.Net平台交互,最好的办法不是用Jython或IronPython,而是通过网络调用来交互,确保各程序之间的独立性。

四、Python 写程序

1、方式

第一种通过终端输入python或python3进入终端交互模式
第二种通过编辑器,如vim编写Python程序

2、配置 Python 解释器

在代码第一行写入执行时的 Python 解释器路径,编辑完后需要对此 Python 文件添加 ‘x’ 权限

#!/usr/bin/python      #指定 python 解释器的版本为Python2.X
# -*- coding:utf-8 -*-  #Python2.x 要指定编码,否则不支持中文,Python3不需要,默认就是utf-8编码
# coding=utf-8

二、IPython 安装

一、ipython 简介

​ IPython 是一个 python 的交互式 shell,比默认的pythonshell 好用得多,支持变量自动补全,自动缩进,支持bash shell 命令,内置了许多很有用的功能和函数。

二、IPython 安装

1.查看pip3的版本
$ pip3 -V
# pip 19.2.3 from /usr/local/lib/python3.7/site-packages/pip (python 3.7)

2.更新pip版本命令
$ pip3 install --upgrade pip 

3.安装ipython
$ pip3 install ipython
# 需要切换到root用户进行安装
# 如果报错,原因是网络不行

4.进入命令
$ ipython

5.退出
$ exit()  or  exit   or  Ctrl+d

三、Python 基础

一、注释

注释的分类:
1.单行注释:以#开头, #右边的所有东西当做说明,而不是真正要执行的程序,起辅助说明作用
# 注释单行内容
2.多行注释:
‘’‘三对单引号注释多行’’’
“”“三对双引号注释多行”""

二、变量以及数据类型

变量就是用来存东西的
程序就是用来处理数据的,而变量就是用来存储数据的
变量起名要有意义

数据类型

三、标识符和关键字

1、标识符的组成

​ 标识符由字母、下划线和数字组成,且数字不能开头,且不能是关键字

标识符是区分大小写的
Andy 和 andy 
Cat 和 cat
Person 和 person 
上面两个是不同的变量

2、标识符起名规则

驼峰命名法

① 小驼峰命名法:第一个单词以小写字母开始,第二个单词往后首字母大写。比如: myName、 firstName、 lastName
② 大驼峰命名法:每一个单词的首字母都采用大写字母。比如:FirstName、 LaseName

​ 在python语言中支持下划线连接多个单词。比如:first_name 、 last_name

3、关键字

​ Python一些具有特殊功能的标识符,这就是所谓的关键字
​ 关键字,是Python已经使用的了,所以不允许开发者自己定义和关键字相同的名字的标识符

可以通过以下命令进行查看当前系统中Python的关键字,在交互模式下:

import keyword
keyword.kwlist

四、输入函数

1、Python2

① raw_input()
特点:raw_input() 接收输入的内容不论是整数型、字符型还是浮点型,它都会当成字符串类型来处理
示例:

#!/usr/bin/python
#-*-coding:utf-8-*-
passwd = raw_input("请输入密码:")
print "您刚刚输入的密码是:%s" % passwd

② input()
input() 函数与 raw_input() 类似,但其接受的输入必须是表达式(数字类型)int 和 float 类型。
示例:

>>> a = input("输入内容:")
输入内容:2
>>> b = input("输入内容:")
输入内容:3
>>> c = a+b
>>> c
5

2、Python3

python3版本中没有raw_input()函数,只有input(),并且 python3中的input与python2中的raw_input()功能一样

① 若需要计算,需要强制转换为 int 类型
示例:

In [1]: a = int(input("输入内容:"))
输入内容:2

In [2]: b = int(input("输入内容:"))
输入内容:3

In [3]: c = a+b

In [4]: c
Out[4]: 5

五、输出函数

① print
示例:

In [5]: a=10

In [6]: print("我今年%d岁了"%a)
我今年10岁了

In [7]: a+=1

In [8]: print("我今年%d岁了"%a)
我今年11岁了

格式化输出

格式符号转换
%c字符
%s通过str() 字符串转换来格式化
%d有符号十进制整数
%f浮点实数

示例:

In [9]: age = 28

In [10]: name = "阿福"

In [11]: print("我的姓名是%s\n年龄是%d"%(name,age))
我的姓名是阿福
年龄是28
# 支持换行符 \n		制表符 \t

六、运算符

1、运算符

运算符描述实例
+两个对象相加 a + b 输出结果 7
-得到负数或是一个数减去另一个数 a - b 输出结果 3
*两个数相乘或是返回一个被重复若干次的字符串 a * b 输出结果 10
/x除以y a/ b 输出结果 2, python2中是2, python3中是2.5
//取整除取商, 5//2得2;返回商的整数部分 9//2 输出结果 4 。
%取余返回除法的余数 a % b 输出结果 1
**返回x的y次幂 a**b 为5的2次方, 输出结果 25

2、赋值符

运算符描述实例
=赋值运算符把=号右边的结果给左边的变量 num=1+2*3 结果num的值为7
+=加法赋值运算符c += a 等效于 c = c + a
-=减法赋值运算符c -= a 等效于 c = c - a
*=乘法赋值运算符c *= a 等效于 c = c * a
/=除法赋值运算符c /= a 等效于 c = c / a
%=取模赋值运算符c %= a 等效于 c = c % a
**=幂赋值运算符c **= a 等效于 c = c ** a
//=取整除赋值运算符c //= a 等效于 c = c // a

eval 函数:可以将字符串中的表达式提取出来进行运算
示例:

In [33]: a = input("请输入内容:")
请输入内容:5+5

In [34]: type(a)
Out[34]: str

In [35]: b = eval(a)

In [36]: b
Out[36]: 10

四、Python 语法

一、if

1、运算符

运算符描述示例
==检查两个操作数的值是否相等,如果是则条件变为真如a=3,b=3则(a == b)为Ture
!=检查两个操作数的值是否相等,如果值不相等,则条件变为真如a=1,b=3则(a != b)为Ture
<>检查两个操作数的值是否相等,如果值不相等,则条件变为真。
Python和在Pascal等特有方式,Java和c没有,在Python3中废弃了
如a=1,b=3则(a <> b)为Ture。这个类似于 != 运算符
>检查左操作数的值是否大于右操作数的值,如果是,则条件成立如a=7,b=3则(a>b)为Ture
<检查左操作数的值是否小于右操作数的值,如果是,则条件成立如a=7,b=3则(a<b)为Ture
>=检查左操作数的值是否大于右或等于操作数的值,如果是,则条件成立如a=3,b=3则(a>=b)为Ture
<=检查左操作数的值是否小于或等于右操作数的值,如果是,则条件成立如a=3,b=3则(a<=b)为Ture

2、比较运算符

运算符逻辑表达式描述实例
andx and y布尔"与"——如果x为False,x and y 返回False,否则它返回y的计算值
orx or y布尔"或"——如果x为Ture,它返回Ture,否则它返回y的计算值
notnot x布尔"非"——如果x为Ture,返回False,如果x为False,它返回Ture

3、随机数

import random
a = random.randint(0,5)
print(a)

二、while

Where循环的书写方式:

num = 1
while num <= 10:
	print(num)
	num += 1 

while循环注意事项:
i=i+1别忘记写,否则条件永远满足, 一直执行。
循环嵌套中,外层循环执行一次,内层循环执行一遍。

示例:打印直角三角形
i=1
while i<=5:
    j=1
    while j<=i:
        print("*",end="\t")		#这里的 end 表示不换行
        j+=1
    print()
    i+=1

结果:
*	
*	*	
*	*	*	
*	*	*	*	
*	*	*	*	*

三、for

1、for循环的格式

for 临时变量 in 列表或者字符串等:
循环满足条件时执行的代码

示例:
'''
试想如何打印以下效果:
当name = “abcdefg”
打印效果:
a
b
c
……
'''
name="abcdefg"
for i in name:
    print(i)

2、for-else循环的格式

for 临时变量 in 列表或者字符串等:
循环满足条件时执行的代码
else:
循环不满足条件时执行的代码

示例:试想如何打印以下效果:
'''
当name = “abcdefg”
打印效果:
a
b
c
d
……
'''
name = ''
for x in name:
	print(x)
else:		# 这里的 else 下的内容一定会被执行一次
	print("没有数据")		

3、for-循环中的break和continue

break:遇到它跳出整个循环, 如果是循环嵌套, break在内循环,退出的是内循环。
continue:遇到它跳出本次循环, 紧接着执行下一次的循环。
如果实在嵌套循环中,break 和 continue 支队最近的一层循环起作用。
注意:break 和 continue 只能用在循环中,除此之外不能单独使用。

4、if的各种真假判断

假:					真:
if "":					if 1if {}:					if 2if []:					if -1if none:				if a:
if 0:0 则为真

5、range()函数的使用

函数语法
range(start, stop, step)
参数说明:
•start: 计数从 start 开始。默认是从 0 开始。例如range(5)等价于range(0, 5) ;
•stop: 计数到 stop 结束,但不包括 stop。例如: range(0, 5) 是[0, 1, 2, 3, 4]没有5
•step:步长,默认为1。例如: range(0, 5) 等价于 range(0, 5, 1)

示例:计算1-100的数字和
sum=0
for i in range(1,101,1):
    sum = sum + i
print(sum)
结果:5050

总结

"""
if语句总结:
if往往用来对条件是否满足进行判断

1.基本方法
if 条件:
    满足时要做的事情
    
2.满足与否执行不同的事情
if 条件:
    满足时要做的事情
else:
    不满足时要做的事情
    
3.多个条件的判断
    if 条件:
        满足条件时要做的事情
    elif 条件2:
        满足条件2时要做的事情2
    elif 条件3:
        满足条件3时要做的事情3
    else:
        条件都不满足时要做的事情
        
4.if嵌套
if 条件1:
    满足条件1时要做的事情1
    if 条件2:
        满足条件2时要做的事情2
        
while循环一般通过数值是否满足来确定循环条件
for循环一般是对能保存多个数据的变量,进行遍历

if、while、for 等其它语句可以随意组合,这样往往可以完成复杂的功能

"""

五、字符串

一、字符串

1、表现形式

a = "100"
b = "hello world"
c = 'hello world'
d = '100'
e = '18.20520'
f = """我在北京宏福"""
g = '''
<!DOCTYPE html>
<html lang=”en“>
<head>
<title>办公系统</title>
</head>
<body class="login-body">
欢迎登录办公管理系统!
</body>
</html>
'''

len 函数返回对象的长度或者个数

In [13]: a='100'

In [14]: len (a)
Out[14]: 3

组成字符串的另外一种方式:字符串会拼接,数字会相加

In [15]: a = 'zhang'
In [16]: b = 'san'
In [17]: c = a + b
In [18]: c
Out[18]: 'zhangsan'
In [19]: f = '=====' + a + b + '====='
In [20]: f
Out[20]: '=====zhangsan====='
   
In [25]: d = 1
In [26]: e = 2
In [27]: g = d + e
In [28]: g
Out[28]: 3

2、字符串的输入输出

1、输入:input

In [29]: name = input("请输入你的名字:")
请输入你的名字:

In [30]: position = input("请输入你的职业:")
请输入你的职业:

2、输出:print

In [34]: print("姓名:%s\n职业:%s"%(name,position))
姓名:
职业:

3、format 的使用语法:格式化的方式展示数据,并可以通过多种方式展示数据。

①通过位置

格式一:
In [37]: print("{0},{1}".format("张三",20))
张三,20

格式二:
In [38]: print("{},{}".format("张三",20))
张三,20
# 大括号内的数字代表下标位置,大多数下标从0开始

②通过关键字参数

In [40]: print("{name},{age}".format(name = "张三",age = 20))
张三,20

③通过映射list

In [42]: alist = [ '曹操','中国',20]		#创建列表

In [43]: print('my name is {0[0]},from {0[1]},age is {0[2]}'.format(alist))		# {}内表示第几个列表,[]内表示列表内的第几个字符串
my name is 曹操,from 中国,age is 20

4、字符串输入
在 python3 中 input 获取的数据,都以字符串的方式进行保存,即使输入的是数字,那么也是以字符串方式保存

5、下标
下标索引 index 所谓"下标",就是编号。

In [46]: name = "txbing"

In [47]: name[0]
Out[47]: 't'

In [48]: name[-1]
Out[48]: 'g'
    
In [50]: name[len(name)-1]
Out[50]: 'g'

6、切片
切片是指对操作的对象截取其中一部分的操作。字符串、列表、元组都支持切片操作。
切片的语法: [起始:结束:步长]

In [55]: name = "txbing"

In [56]: print(name[0:3:1])
txb

In [58]: print(name[3:])		# 表示从下标3到结尾
ing

In [60]: print(name[1:-1])		# -1表示字符串倒数第一个字符
xbin

In [64]: print(name[::-1])		# 逆序
gnibxt

注意:选取的区间属于左闭右开型,即从"起始"位开始,到"结束"位的前一位结束(不包含结束位本身)
注意:如果不写步长默认是1。步长是控制方向的,正数从左往右取,负数是从右到左取

下标和切片小结

[:] 提取从开头(默认位置0)到结尾的整个字符串
[start:] 从start 提取到结尾
[:end] 从开头提取到end - 1
[start:end] 从start 提取到end - 1
[start:end:step] 从start 提取到end - 1,每step 个字符提取一个
[::-1]逆序 

3、字符串常见函数

必须要掌握的字符串函数
find()、 rfind ()、 index ()、 rindex ()、 replace ()、 split ()、 parttion ()、 rparttion ()、splitlines ()、 startswith ()、 endswith ()、 lower ()、 upper ()

find() 从左往右查找

In [68]: mystr = 'hello world yanzilu and yanziluPython'

In [69]: mystr.find('yanzilu')
Out[69]: 12		# 字符串开头的下标位置

rfind() 从右往左查找

In [68]: mystr = 'hello world yanzilu and yanziluPython'

In [70]: mystr.rfind('yanzilu')
Out[70]: 24
    
In [71]: mystr.rfind('zhangsan')
Out[71]: -1		# 返回值为 -1 表示搜索值不存在
    
In [73]: mystr.find('yanzilu',0,len(mystr))		# 表示从开始到结尾查找,在不知道结尾多长时,可用len函数
Out[73]: 12

index () 与 find 功能差不多,只是搜索内容不存在时报错

In [74]: mystr.index('yanzilu')
Out[74]: 12

In [75]: mystr.index('zhangsan')
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-75-6bc6d4c79c0e> in <module>
----> 1 mystr.index('zhangsan')

ValueError: substring not found

rindex () 从后往前搜索

In [76]: mystr.rindex('yanzilu')
Out[76]: 24

replace () 将字符串中的字符进行替换

In [77]: mystr.replace('yanzilu','zhangsan')		# 不指定替换几个时默认全部替换
Out[77]: 'hello world zhangsan and zhangsanPython'

In [78]: mystr.replace('yanzilu','zhangsan',1)		# 只替换一个
Out[78]: 'hello world zhangsan and yanziluPython'

split () 分割字符串,不包含切割者本身

In [81]: mystr.split()		#默认以空格进行分割,包含换行符\n 与 制表符\t
Out[81]: ['hello', 'world', 'yanzilu', 'and', 'yanziluPython']

In [82]: mystr.split('and')		# 以字符 and 进行分割,不包含 and
Out[82]: ['hello world yanzilu ', ' yanziluPython']

parttion () 分割字符串,包含切割者本身,默认将字符串切割为三个部分,前、切割者本身、后

In [83]: mystr.partition('and')
Out[83]: ('hello world yanzilu ', 'and', ' yanziluPython')

rparttion () 从后往前切割

In [85]: mystr.rpartition('yanzilu')
Out[85]: ('hello world yanzilu and ', 'yanzilu', 'Python')

splitlines () 按行分割

In [89]: mystr = 'hello\nworld\nyanzilu\nand\nyanziluPython'

In [90]: print(mystr)
hello
world
yanzilu
and
yanziluPytho

In [91]: mystr.splitlines()
Out[91]: ['hello', 'world', 'yanzilu', 'and', 'yanziluPython']

startswith () 检查字符串以什么内容开头,是,返回 Ture ,否, 返回 False

In [92]: mystr = 'hello world yanzilu and yanziluPython'

In [93]: mystr.startswith('hello')
Out[93]: True

In [94]: mystr.startswith('Hello')
Out[94]: False

endswith () 检查字符串以什么内容结尾,是,返回 Ture ,否, 返回 False

In [97]: mystr = 'hello world yanzilu and yanziluPython'

In [98]: mystr.endswith('Python')
Out[98]: True

In [99]: mystr.endswith('python')
Out[99]: False

lower () 将字符串中的所有字符替换为小写

In [104]: mystr.lower()
Out[104]: 'hello world yanzilu and yanzilupython'

upper () 将字符串中的所有字符替换为大写

In [100]: mystr.upper()
Out[100]: 'HELLO WORLD YANZILU AND YANZILUPYTHON'

center() 将字符串居中

In [105]: music = "那一夜我伤害了你"

In [106]: music.center(30)		# 左右各加15各空格
Out[106]: '           那一夜我伤害了你           '

lstrip() 删除左边的空格

In [112]: a = '           那一夜我伤害了你           '
  
In [114]: a.lstrip()
Out[114]: '那一夜我伤害了你           '

rstrip() 删除右边的空格

In [112]: a = '           那一夜我伤害了你           '
    
In [115]: a.rstrip()
Out[115]: '           那一夜我伤害了你'

strip() 删除所有空格

In [112]: a = '           那一夜我伤害了你           '
    
In [113]: a.strip()
Out[113]: '那一夜我伤害了你'

isspace() 如果字符串中只包含空格,是,返回 Ture ,否, 返回 False

In [116]: name = 'lisi'

In [117]: name.isspace()
Out[117]: False

In [120]: name = ' '

In [121]: name.isspace()
Out[121]: True

count() 统计字符有多少个

In [97]: mystr = 'hello world yanzilu and yanziluPython'

In [122]: mystr.count('hello')
Out[122]: 1

In [123]: mystr.count('o')
Out[123]: 3

capitalize() 将字符串的第一个字母大写,并且后面全部为小写

In [126]: mystr = 'hello world yanzilu and yanziluPython'

In [127]: mystr.capitalize()
Out[127]: 'Hello world yanzilu and yanzilupython'

title() 将每个单词的首字母大写

In [126]: mystr = 'hello world yanzilu and yanziluPython'

In [128]: mystr.title()
Out[128]: 'Hello World Yanzilu And Yanzilupython'

ljust() 将字符串移动到左边,右边填充空格

In [105]: music = "那一夜我伤害了你"

In [130]: music.ljust(30)
Out[130]: '那一夜我伤害了你                      '

rjust() 将字符串移动到右边,左边填充空格

In [105]: music = "那一夜我伤害了你"

In [131]: music.rjust(30)
Out[131]: '                      那一夜我伤害了你'

isalpha() 判断一个字符串是否为连续的字符,是,返回 Ture ,否, 返回 False。
字符只包括两种:utf-8 和 英文字母,不包含数字

In [105]: music = "那一夜我伤害了你"
In [133]: music.isalpha()
Out[133]: True

In [126]: mystr = 'hello world yanzilu and yanziluPython'
In [134]: mystr.isalpha()
Out[134]: False

isdigit() 判断是否只包含数字,是,返回 Ture ,否, 返回 False。

In [135]: A = "123"
In [136]: A.isdigit()
Out[136]: True

In [137]: A = "123abc"
In [138]: A.isdigit()
Out[138]: False

isalnum() 判断是否只包含字母数字,是,返回 Ture ,否, 返回 False。

In [137]: A = "123abc"
In [139]: A.isalnum()
Out[139]: True

In [140]: A = "123"
In [141]: A.isalnum()
Out[141]: True

六、列表及循环遍历

1、列表的格式

变量 names_list 的类型为列表
可以存字符串,数字,列表,元组,字典等

In [12]: names_list = ['刘备','曹操','孙权']

In [13]: print(names_list[0],names_list[1],names_list[2])
刘备 曹操 孙权
#使用for循环遍历列表
names_list = ['刘备','曹操','孙权',"貂蝉","吕布"]
for name in names_list:
    print(name)

#使用while循环
print("====使用while循环打印====")
i = 0
while i<len(names_list):		# len统计字符串长度
    print(names_list[i])
    i+=1

2、列表的增删改查

① 列表添加元素("增"append, extend, insert)
append可以向列表添加元素
extend将另一个集合中的元素逐一添加到列表中
insert在指定位置index前插入元素

#列表的增加
#第一种方式:使用append追加元素
names = ['刘备','曹操','孙权',"貂蝉","吕布"]
for name in names:
    print(name)

print("修改之后输出:")

temp = input("请输入要添加的姓名:")
names.append(temp)
for name in names:
    print(name)

#第二种方式:使用extend合并列表
anames = ["张三",'李四','王五']
names.extend(anames)
print(names)

#第三种,在指定位置插入 insert
print("使用insert插入输入之后:")
names.insert(0,"曹丕")
print(names)

② 删除元素("删"del, pop, remove)
del根据下标进行删除
pop删除最后一个元素
remove根据元素的值进行删除

#列表的删除
names = ['刘备','曹操','孙权',"貂蝉","吕布"]
#指定下标删除
del names[1]
print(names)

#pop删除最后一个元素
names.pop()
print(names)

#remove根据元素的值进行删除
names.remove("貂蝉")
print(names)

③ 通过下标修改元素(“改”)

#修改列表
names = ['刘备','曹操','孙权',"貂蝉","吕布"]
names[1]="我是谁"
print(names)

④ 查找元素("查"in, not in, index, count)
python中查找的常用方法为:
in(存在) ,如果存在那么结果为True,否则为False
not in(不存在),如果不存在那么结果为True,否则False
index和count
index和count与字符串中的用法相同

names = ['刘备','曹操','孙权',"貂蝉","吕布","刘备"]
print(names)

find_name = input("请输入要查找的姓名:")
if find_name in names:
    print("已经找到:%s"%find_name)
    print("%s的下标是:%s"%(find_name,names.index(find_name)))
    print("总共有%s个%s:"%(names.count(find_name),find_name))
else:
    print("没有找到:%s" % find_name)

#运行结果:
['刘备', '曹操', '孙权', '貂蝉', '吕布', '刘备']
请输入要查找的姓名:貂蝉
已经找到:貂蝉
貂蝉的下标是:3
总共有1个:貂蝉

⑤ 排序(sort, reverse)
sort 方法是将 list 按特定顺序重新排列,默认为由小到大,参数reverse=True可改为倒序, 由大到小。
reverse方法是将list逆置。

# sort 排序
a = [1,2,5,6,7,4,3,7,5,7,9]
a.sort()
print(a)
[1, 2, 3, 4, 5, 5, 6, 7, 7, 7, 9]

# reverse 倒叙
a.reverse()
print(a)
[9, 7, 7, 7, 6, 5, 5, 4, 3, 2, 1]

# 结合使用
a.sort(reverse=True)
print(a)
[9, 7, 7, 7, 6, 5, 5, 4, 3, 2, 1]

3、列表嵌套
类似while循环的嵌套,列表也是支持嵌套的
一个列表中的元素又是一个列表,那么这就是列表的嵌套

school_names = [['北京大学','清华大学'],['南开大学','天津大学'],['贵州大学','青海大学']]
#print(school_names[0][1])
#print(school_names[2][1])

for school in school_names:
    print(school)
    for x in school:
        print(x)

#运行结果:
['北京大学', '清华大学']
北京大学
清华大学
['南开大学', '天津大学']
南开大学
天津大学
['贵州大学', '青海大学']
贵州大学
青海大学

七、元组

元组定义:Python的元组与列表类似,不同之处在于元组的元素不能修改。元组使用小括号,列表使用方括号。元组和字符串都是不可变序列.

语法:定义元组语法 () 和 ,

In [1]: a = (1,)

In [2]: type(a)
Out[2]: tuple

访问元组:和列表一样

修改元组: Python中不允许修改元组的数据, 包括不能删除其中的元素。元组是不可变的,也就是说,元组中的元素在被赋值后不能改变。但是,如果元素本身是一个可变数据类型的列表,那么其嵌套项可以被改变

In [3]: a = (1,2,["b","c"])

In [4]: a[2].append("d")	# a[2]:元组 a 的下标为2的元素

In [5]: print(a)
(1, 2, ['b', 'c', 'd']

tuple函数
tuple函数的功能与list函数基本上一样的,以一个序列作为参数并把它转换为元组,如果参数是元组, 那么该参数就会被原样返回。

# 元组转列表
In [3]: a = (1,2,["b","c"])
In [6]: ab = list(a)
In [7]: type(ab)
Out[7]: list
In [8]: ab
Out[8]: [1, 2, ['b', 'c', 'd']]

# 列表转元组
In [9]: ab = tuple(a)
In [10]: ab
Out[10]: (1, 2, ['b', 'c', 'd'])
In [11]: type(ab)
Out[11]: tuple

多维列表/元祖访问 a [ ] [ ]

In [12]: a = (("刘备","刘禅"),("曹操","曹植"),("孙策","孙权"))

In [13]: a[0]
Out[13]: ('刘备', '刘禅')

In [14]: a[0][0]
Out[14]: '刘备'

元组的优点
与列表相比, 元组的优点:
① 通常将元组用于不同的数据类型,将列表用于相同(或相似)的数据类型。
② 由于元组不可变,所以遍历元组比列表要快(较小的性能提升)。
③ 元组可以用作字典的Key, 而列表不行。因为字典的Key 必须是不可变的,元组本身就是不可变的。
④ 如果数据不需要更改,将其作为元组来实现,可以确保“写保护”。

八、字典

字典定义:键值对
字典使用大括号,成对出现,以冒号分割

1、字典访问

访问不存在的键则会报错,解决方法:get()
使用get(‘key’)不存在不会报错,而且可以设置默认值,在我们不确定字典中是否存在某个键而又想获取其值时,可以使用get方法,还可以设置默认值.

In [15]: student= {'name':'宋江', 'id':100, 'sex':'男', 'address':'中国'}
In [16]: print(student['name'])
宋江
In [17]: print(student['id1'])
---------------------------------------------------------------------------
KeyError                                  Traceback (most recent call last)
<ipython-input-17-216e9575c8f7> in <module>
----> 1 print(student['id1'])

KeyError: 'id1'
In [18]: print(student.get("id1",18))
18

2、添加元素

如果在使用 变量名[‘键’] = 数据 时,这个“键”在字典中不存在,那么就会新增这个元素

#如果Key存在则是修改,不存在则是增加
student["age"] = 20
print(student)
student["age"] = 28
print(student)

#结果:
{'name': '宋江', 'id': 100, 'sex': '男', 'address': '中国', 'age': 20}
{'name': '宋江', 'id': 100, 'sex': '男', 'address': '中国', 'age': 28}

3、删除元素

对字典进行删除操作,有以下几种
1、del删除指定的元素
2、del删除整个字典
3、clear清空整个字典
4、pop删除字典指定元素并且得到值
5、popitem随机返回并删除字典中的一对键和值(项),因为字典中没有下标和顺序,所以是随机,其实删除的是最后一个键值对

student= {'name':'宋江', 'id':100, 'sex':'男', 'address':'中国'}
print("删除前:",student)
del student["id"]
print("删除后:",student)
#结果:
删除前: {'name': '宋江', 'id': 100, 'sex': '男', 'address': '中国'}
删除后: {'name': '宋江', 'sex': '男', 'address': '中国'}

#删除整个字典,无法再使用这个字典
del student
print(student)

#清空字典中的所有项
student.clear()
print("使用clear清空字典后:",student)
#结果:
使用clear清空字典后: {}

4、修改元素

字典的每个元素中的数据是可以修改的,只要通过key找到,即可修改

5、查找元素

#字典的查找
info = {'name':'宋江', 'id':100, 'sex':'男生', 'address':'中国梁山'}

name = info.get("name")
print("使用get(key)查找=",name)

if "name" in info:
    print("使用in根据key从字典中查找name是存在的。")
print(info.keys())
if "name" in info.keys():
    print("使用in根据key从字典中的info.keys()查找name是存在的。")
print(info.values())
if "宋江" in info.values():
    print("使用in根据 值 从字典中的info.values()查找宋江是存在的。")
    
#运行结果
使用get(key)查找= 宋江
使用in根据key从字典中查找name是存在的。
dict_keys(['name', 'id', 'sex', 'address'])
使用in根据key从字典中的info.keys()查找name是存在的。
dict_values(['宋江', 100, '男生', '中国梁山'])
使用in根据 值 从字典中的info.values()查找宋江是存在的。

6、字典的键值操作

len() 测量字典中,键值对的个数
keys 返回一个包含字典所有KEY的列表
values 返回一个包含字典所有value的列表
items 返回可遍历的(键,值)元组数组
has_key Python3中废弃了,这个方法是python 2.6以后支持的,但在python 3.0版本开始将使用in.
dict.has_key(key)如果key在字典中,返回True,否则返回False

1、字典遍历
通过for … in …:的语法结构,我们可以遍历字符串、列表、元组、字典等数据结构。
遍历字典的 key(键)
遍历字典的 value(值)
遍历字典的 items项(元素)
遍历字典的 key-value(键值对)
使用枚举遍历 enumerate()
遍历列表引入 enumerate

#字符串遍历
info = {'name':'宋江', 'id':100, 'sex':'男生', 'address':'中国梁山'}
for v in info.values():
    print(v)

宋江
100
男生
中国梁山

#键值遍历
print("========items=========")
for key,value in info.items():
    print(key,value)

========items=========
name 宋江
id 100
sex 男生
address 中国梁山

#列表遍历
names = ["宋江","卢俊义","吴用"]
for i in range(len(names)):
    print(i,names[i])

#如果使用枚举实现会更直接和优美
for index,value in enumerate(names,1):
    print(index,value)

1 宋江
2 卢俊义
3 吴用

#使用枚举遍历字典
infos = {'name':'宋江', 'id':100, 'sex':'男生', 'address':'中国梁山'}

for index,value in enumerate(infos.values()):	#这里不写 values() 时,不写默认遍历 key() 值
    print(index,value)
    
0 宋江
1 100
2 男生
3 中国梁山

九、集合

集合:set
集合与之前列表、 元组类似, 可以存储多个数据, 但是这些数据是不重复的。
集合对象还支持联合(union), 交集(intersection),差集(difference)等数学运算。

In [94]: x
Out[94]: {'a', 'b', 'c', 'd'}

In [95]: y=set(['h','e','l','l','o'])
In [96]: y
Out[96]: {'e', 'h', 'l', 'o'}

In [97]: z = set("spam")
In [98]: z
Out[98]: {'a', 'm', 'p', 's'}

In [99]: y&z  #交集
Out[99]: set()

In [100]: x&z  #交集
Out[100]: {'a'}

In [101]: x|y  #并集
Out[101]: {'a', 'b', 'c', 'd', 'e', 'h', 'l', 'o'}

In [102]: x|z #并集
Out[102]: {'a', 'b', 'c', 'd', 'm', 'p', 's'}

In [103]: x-y #差集
Out[103]: {'a', 'b', 'c', 'd'}

In [104]: x-z #并集
Out[104]: {'b', 'c', 'd'}

In [105]: x^z #对称差集,在x或z中,但不会同时出现在二者中
Out[105]: {'b', 'c', 'd', 'm', 'p', 's'}

集合(set)、列表(list)、元组(tuple)相互转换:

公共方法:

运算 符Python 表 达式结果描述支持的数据类型
+[1, 2] + [3, 4][1, 2, 3, 4]合并字符串、列表、元组 “a”+”b”→”ab” [1,2]+[3,4]→[1,2,3,4] (1,2)+(3,4)—》(1, 2, 3, 4)
*‘Hi!’ * 3[‘Hi!’, ‘Hi!’, ‘Hi!’]复制字符串、列表、元组
in3 in (1, 2, 3)True元素是否存 在字符串、列表、元组、 字典
not in4 not in (1, 2, 3)True元素是否不 存在字符串、列表、元组、 字典

Python内置函数:

序号方法描述
1cmp(item1, item2)比较两个值
2len(item)计算容器中元素个数
3max(item)返回容器中元素最大值
4min(item)返回容器中元素最小值
5del(item)删除变量

十、引用

➢ 用id()来判断两个变量是否为同一个变量的引用

在Python中值是靠引用来传递的
我们可以用id()来判断两个变量是否为同一个变量的引用。
我们可以把id理解为那块内存的地址表示。

➢ 可变类型和不可变类型
不可变类型:字符串、 数字、 元组
可变类型:列表, 字典
可以变类型可以修改值
不可变类型不能修改值

➢ 引用的应用-三种方式交换两个数
第一种方式:使用第三方变量
第二种方式:使用两个数和在减掉另外一个数
第三种方式: Python特有方式

十一、函数

一、定义函数

def 函数名():
	代码块

如:将九九乘法表定义为函数

def nn():
    a = 1
    while a <= 9:
        b = 1
        while b <= a:
            c = a * b
            print("%d*%d=%d" % (b, a, c), end=" ")
            b += 1
        print("")
        a += 1

二、调用函数

函数名()

#调用上方九九乘法表函数
print(nn())

练一练 使用函数打印自己的信息
要求:定义一个函数,能够输出自己的姓名和年龄,并且调用这个函数让它执行
提示:使用def定义函数,编写完函数之后,通过 函数名() 进行调用。函数命名要见明知意

#定义一个函数,打印个人信息
def print_info():
    print("张三")
    print("今年三十了")
print_info()
--------------------------------------------
张三
今年三十了

有传参返回值类型
def 函数名(参数1,参数2,……):

#带参函数
'''
需求:我们要让用户输入两个数,并让其相加,打印结果
'''
#x,y为形参
def add_sum(x,y):
    print("两数之和为:%s"%(x+y))

#传递两个实参
num1 = int(input("输入第一个数:"))
num2 = int(input("输入第二个数:"))
add_sum(num1,num2)
------------------------------------------------
输入第一个数:2
输入第二个数:4
两数之和为6
#带参函数
'''
需求:我们要让用户输入两个数,并让其相加,打印结果
'''
#x,y为形参
def add_sum(x,y):
    a = x+y
    #调用函数时的返回值
    return a
#传递两个实参
num1 = int(input("输入第一个数:"))
num2 = int(input("输入第二个数:"))
result = add_sum(num1,num2)
print("result = %s"%result)

传参函数的调用
函数名(参数1,参数2,……)

三、函数的类型

函数根据有没有参数/返回值,可以相互组合,一共有4种:

1.无参数,无返回值

def 函数名():
pass

2.无参数,有返回值

def 函数名():
return xxx

3.有参数,无返回值

def 函数名(x,y,…):
pass

4.有参数,有返回值:

def 函数名(x,y,…):
return xxx

给函数添加文档说明

def 函数名():
“文档说明”

或者使用三个双引号/单引号来编辑多行函数说明。

def add(a,b):
    "用来完成对两个数求和"
    return a+b

调用

print(help(函数名))

def add(a,b):
    "用来完成对两个数求和"
    return a+b
    
print(help(add))
---------------------------------------------
Help on function add in module __main__:

add(a, b)
    用来完成对两个数求和

None
#一个文件有多个函数,想要一次性打印出他们的文档说明信息
import 文件名
多个函数标签......
print(help(文件名))

四、函数的参数及返回值

➢函数的参数

思考一个问题,如下:
现在需要定义一个函数,这个函数能够完成2个数的加法运算,并且把结果打印出来,该怎样设计?下面的代码可以吗?有什么缺陷吗?

def add_num():
	a = 11
	b = 22
	c = a+b
	print(c)
add_num() 

为了让一个函数更通用,即想让它计算哪两个数的和,就让它计算哪两个数的和,在定义函数的时候可以让函数接收数据,就解决了这个问题,这就是函数的参数。

➢ 定义有参数类型的函数

def 函数名(参数1,参数2,……):
代码

➢ 传参函数的调用

函数名(参数1,参数2,……)
函数名(参数1=‘值1’,参数2=‘值2’,……) #这种传参方法可不按顺序

def test(a,b,c):
    return a+b-c

#调用时有几个形参就得传递几个形参
result = test(10,15,5)
print(result)
--------------------------------------------------
20

➢ 小总结

  • 定义时小括号中的参数,用来接收参数用的,称为 “形参”
  • 调用时小括号中的参数,用来传递给函数用的,称为 “实参”,定义了几个形参,就要传递几个实参。有缺省值除外

➢ 缺省值

调用函数时,缺省参数的值如果没有传入,则被认为是默认值。

注意:带有默认值的参数一定要位于参数列表的最后面。

def test(a,b,c=10):
    return a+b-c
result = test(10,15)
print(result)
result = test(10,15,25)
print(result)
#结果:调用函数时,缺省参数的值如果没有传入,则被认为是默认值。如果传入了,就等于传入的值。
15
0

➢ 不定长参数

有时可能需要一个函数能处理比当初声明时更多的参数。这些参数叫做不定长参数,声明时不会命名。

#*args
def sum_add(a,b,*args):
    print('='*50)
    print(a)
    print(b)
    print(args)
    result = a+b
    for num in args:
        result += num

    print('它们的和是:%s'%result)

sum_add(10,20)
sum_add(10,20,30)
sum_add(10,20,30,40)
-----------------------------------------------
#更多的参数(args)会变为元祖形式
==================================================
10
20
()
它们的和是:30
==================================================
10
20
(30,)
它们的和是:60
==================================================
10
20
(30, 40)
它们的和是:100
#**kwargs
def sum_add(a,b,*args,**kwargs):
    print('='*50)
    print(a)
    print(b)
    print(args)
    print(kwargs)
    result = a+b
    for num in args:
        result += num
        
sum_add(10,20,30,40,name='张三')
--------------------------------------------
#**kwargs会将结果保存为字典,但必须要以xx=xx形式传参
==================================================
10
20
(30, 40)
{'name': '张三'}

小结:

书写顺序

def 函数名(形参,缺省值,*args,**kwargs):

def a(a,b=1,*args,**kwargs):

➢拆包

#拆包
def test(a,b,c=33,*args,**kwargs):
    print('='*30)
    print(a)
    print(b)
    print(c)
    print(args)
    print(kwargs)

A = (40,50)
B = {'name':'小米','age':'6'}
test(10,20,30,*A,**B)		#将A、B拆包
----------------------------------------------
==============================
10
20
30
(40, 50)						#拆包的结果
{'name': '小米', 'age': '6'}	   #拆包的结果

➢ 返回值介绍

现实生活中的场景:

我给儿子10块钱,让他给我打酱油。这个例子中,10块钱是我给儿子的,就相当于调用函数时传递到参数,让儿子打酱油这个事情最终的目标是,让他把酱油给带回来,然后给到手上,此时酱油就是返回值。

开发中的场景:

定义了一个函数,完成了获取室内温度,想一想是不是应该把这个结果给调用者,只有调用者拥有了这个返回值,才能够根据当前的温度做适当的调整。

综上所述:

所谓“返回值”,就是程序中函数完成一件事情后,最后给调用者的结果。

➢ 带有参数的返回值

想要在函数中把结果返回给调用者,需要在函数中使用return

如下示例:

def add_2_num(a, b):
	c = a+b
	return c

或者

def add_2_num(a, b):
	return a+b

➢ 带有多个参数的返回值该怎么写?

#多个返回值
def add_num(a,b):
    c = a+b
    d = a-b
    return c,d
a,b = add_num(11,22)		#用两个值接受两个返回值
c = add
print(a)
print(b)
print(c)
--------------------------------
33
-11
(33, -11)			#用一个值接受多个返回值会保存为元祖

五、函数的嵌套及变量

函数的嵌套

一个函数里调用了另一个函数

def test3():
    print('test3----------1')

def test2():
    print('test2----------1')
    test3()
    print('test2----------2')

def test1():
    print('test1----------1')
    test2()
    print('test1----------2')

test1()
#结果
test1----------1
test2----------1
test3----------1
test2----------2
test1----------2

函数嵌套的应用

1.要求:写一个函数打印一条横线
打印自定义行数的横线

#打印一条直线的函数
def print_one_line():
    print('='*40)

def print_many_lines(num):
    i = 0
    #因为有打印多条直线的函数,只需多次调用即可
    while i < num:
        print_one_line()
        i += 1

print_many_lines(3)
#结果
========================================
========================================
========================================

2.求三个数的和以及三个数的平均值
思路:写一个个函数求三个数的和
再写一个函数求三个数的平均值

def sum_num(a,b,c):
    return a+b+c

def avg_num(a1,a2,a3):
    result = sum_num(a1,a2,a3)
    return result,result/3

result,result1 = avg_num(10,20,30)
print('两数之和:%s'%result)
print('两数平均值:%s'%result1)
-----------------------------------------------
两数之和:60
两数平均值:20.0

局部变量

局部变量,就是在函数内部定义的变量,临时保存值。

不同的函数,可以定义相同的名字的局部变量,但是各用个的不会产生影响。

def test1():
    a = 100
    print('test1中的a=%d'%a)

def test2():
    print('test2中的a=%d'%a)
test1()		#会正常输出
test2()		#会报错,因为test1函数内定义的a在test2无效	

全局变量

如果一个变量,既能在一个函数中使用,也能在其他的函数中使用,这样的变量就是全局变量

全局变量和局部变量名字相同

#全局变量

a = 100         #这是全局变量
def test1():
    a = 1		#这是局部变量
    print(a)

def test2():
    print(a)

test1()
test2()
----------------------------------------
1
100

global修改全局变量

global声明的变量可以作用于全局

global一般用于不可变类型。

一般建议全局变量名前面加个g,如:g_a

g_a = 100
def test1():
    global g_a    #声明a为全局变量
    g_a = 1
    print(g_a)

def test2():
    print(g_a)

test1()
test2()
-----------------------------------------------
1
1

可变数据类型全局变量

无需global声明即可修改。

info = [100,200]
name = {'name':'小美'}

def test1():
    info.append(300)
    name['age']=18
    print('修改之后输出info:',info)
    print('修改之后输出name:',name)

def test2():
    print('test2中调用info:',info)
    print('test2中调用name:',name)

test1()
test2()
------------------------------------------------
修改之后输出info: [100, 200, 300]
修改之后输出name: {'name': '小美', 'age': 18}
test2中调用info: [100, 200, 300]
test2中调用name: {'name': '小美', 'age': 18}

作用域

在函数内定义的值只在当前函数内部生效。

变量总结

  • 在函数外边定义的变量叫做全局变量。
  • 全局变量能够在所有的函数中进行访问。
  • 如果想在函数中修改全局变量,那么就需要使用global进行声明,否则出错。
  • 强龙不压地头蛇:如果全局变量的名字和局部变量的名字相同,那么优先使用局部变量的。
  • 全局变量要定义在函数调用前,否则会报错
  • 在函数中不使用global声明全局变量时不能修改全局变量的本质是不能修改全局变量的指向,即不能将全局变量指向新的数据。
  • 对于不可变类型的全局变量来说,因其指向的数据不能修改,所以不使用global时无法修改全局变量。
  • 对于可变类型的全局变量来说,因其指向的数据可以修改,所以不使用global时也可修改全局变量。

py文件中写代码顺序

#!/usr/bin/python
utf-8
import xxx
全局变量
函数的定义
定义类
---------下面都是调用-------
调用函数
创建类的实例对象
执行实例对象的函数

六、匿名及递归函数

➢ 递归函数

通过前面的学习知道一个函数可以调用其他函数。

如果一个函数在内部不调用其它的函数,而是自己本身的话,这个函数就是递归函数。

#使用递归函数实现计算阶乘
def calc2_num(num):
    #当前num=4,计算的是4*3!
    if num > 1:
        res = num*calc2_num(num-1)	#自己调用自己
    else:
        res = 1

    return res

print("递归函数计算4的阶乘=%d"%calc2_num(4))
------------------------------------------------
递归函数计算4的阶乘=24

➢ 匿名函数

函数定义

用lambda关键词能创建小型匿名函数。这种函数得名于省略了用def声明函数的标准步骤。

lambda函数的语法只包含一个语句,如下:

lambda [arg1 [,arg2,.....argn]]:expression
lambda 参数列表:函数体(表达式)

应用场合

1.作为内置函数的参数——表达式

def fun(a, b, opt):		#此处opt为表达式
    print("a =%d"%a)
    print("b =%d"%b)
    print("result=%d"%opt(a, b))
#下方匿名函数就是opt的表达式
fun(1, 2, lambda x,y:x+y)
------------------------------------------------
a =1
b =2
result=3

2 作为内置参数传递——排序

想一想,下面的数据如何指定按age或name排序?

#应用场合:
#作为内置函数的参数-表达式

def fun(a, b, opt):
    print("a =%d"%a)
    print("b =%d"%b)
    print("result=%d"%opt(a, b))

fun(1, 2, lambda x,y:x+y)

#作为内置参数传递——排序
#想一想,下面的数据如何指定按age或name排序?
stus = [{"name":"zhangsan", "age":18},
        {"name":"lisi", "age":19},
        {"name":"wangwu", "age":17}]
print("原来的列表=",stus)
#根据name排序
stus.sort(key=lambda x: x["name"])
print("排序后的列表=",stus)
stus.sort(key=lambda x: x["name"],reverse=True)
print("排序后的列表=",stus)
#对于字典的排序是基于ASK码排序的。
--------------------------------------------------
原来的列表= [{'name': 'zhangsan', 'age': 18}, {'name': 'lisi', 'age': 19}, {'name': 'wangwu', 'age': 17}]
排序后的列表= [{'name': 'lisi', 'age': 19}, {'name': 'wangwu', 'age': 17}, {'name': 'zhangsan', 'age': 18}]
排序后的列表= [{'name': 'zhangsan', 'age': 18}, {'name': 'wangwu', 'age': 17}, {'name': 'lisi', 'age': 19}]

十二、文件

一、文件操作介绍

文件作用:就是把一些数据存放起来,可以让程序下一次执行的时候直接使用,加载到内存中,而不必重新制作一份,省时省力。

二、文件的开关、读写

➢打开文件 open

在Python,使用open函数,可以打开一个已经存在的文件,或者创建一个新文件open(‘文件名’,‘访问模式’)

示例如下:

f = open('test.txt', 'w')
变量 = open('文件名''访问模式')
访问模式说明
r以只读方式打开文件。文件的指针将会放在文件的开头。这是默认模式。如果文件不存在会崩溃。Read
w打开一个文件只用于写入。如果该文件已存在则将其覆盖。如果该文件不存在,创建新文件。Write
a打开一个文件用于追加。如果该文件已存在,文件指针将会放在文件的结尾。也就是说,新的内容将会被写入到已有内容之后。如果该文件不存在,创建新文件进行写入
rb以二进制格式打开一个文件用于只读。文件指针将会放在文件的开头。这是默认模式。如果文件不存在会崩溃 read binary
wb以二进制格式打开一个文件只用于写入。如果该文件已存在则将其覆盖。如果该文件不存在,创建新文件
ab以二进制格式打开一个文件用于追加。如果该文件已存在,文件指针将会放在文件的结尾。也就是说,新的内容将会被写入到已有内容之后。如果该文件不存在,创建新文件进行写入。
r+打开一个文件用于读写。文件指针将会放在文件的开头。如果文件不存在会崩溃
w+打开一个文件用于读写。如果该文件已存在则将其覆盖。如果该文件不存在,创建新文件。
a+打开一个文件用于读写。如果该文件已存在,文件指针将会放在文件的结尾。文件打开时会是追加模式。如果该文件不存在,创建新文件用于读写
rb+以二进制格式打开一个文件用于读写。文件指针将会放在文件的开头。如果文件不存在会崩溃。
wb+以二进制格式打开一个文件用于读写。如果该文件已存在则将其覆盖。如果该文件不存在,创建新文件。
ab+以二进制格式打开一个文件用于追加。如果该文件已存在,文件指针将会放在文件的结尾。如果该文件不存在,创建新文件用于读写。

➢ 写数据 write

使用write()可以完成向文件写入数据。

In [1]: a = open('test.txt','w')
In [2]: a.write("今天好天气!\n老狼请吃鸡!")

➢ 读数据 read

使用read(num)可以从文件中读取数据,num表示要从文件中读取的数据的长度(单位是字节),如果没有传入num,那么就表示读取文件中所有的数据。

In [1]: a = open('test.txt','r')

In [2]: a.read()
Out[2]: '今天好天气!\n老狼请吃鸡!'

In [3]: a.read(1)
Out[3]: '今'

In [4]: a.read(2)
Out[4]: '天好'

In [5]: a.read(3)
Out[5]: '天气!'

➢关闭文件 close

编辑完文件后需要关闭文件,修改才会生效。

只要打开文件,就得关闭文件。

In [1]: a = open('test.txt','r')
In [2]: a.write("今天好天气!\n老狼请吃鸡!")
In [3]: a.close()

➢ 读数据 readlines

就像read没有参数时一样,readlines可以按照行的方式把整个文件中的内容进行一次性读取,并且返回的是一个列表,其中每一行的数据为一个元素.

In [21]: a = open('test.txt','r')

In [22]: a.readlines()
Out[22]: ['今天好天气!\n', '老狼请吃鸡!']

➢ 读数据 readline

每次读取一行的数据

In [18]: a = open('test.txt','r')

In [19]: a.readline()
Out[19]: '今天好天气!\n'

In [20]: a.readline()
Out[20]: '老狼请吃鸡!'

练一练 复制文件

需求描述:
输入文件的名字,然后程序自动完成对文件进行拷贝
例如,程序运行后提示“请输入你要拷贝的文件名:”,当你输入
“test.txt”的时候,会把“test.txt”文件的内容复制一份到文件名叫“test.txt [复制]”文件中。

#打开要复制的文件
old_file_name = input('请输入您要复制的文件名:')
old_file = open(old_file_name,'r')

#创建一个新的文件保存复制的内容
new_file_name = old_file_name+'[复制]'
new_file = open(new_file_name,'w')
new_file.write(old_file.read())

#关闭两个文件
old_file.close()
new_file.close()

➢ 文件读写定位 tell

获取当前读写的位置tell,在读写文件的过程中,如果想知道当前的位置,可以使用tell()来获取

In [1]: f = open('test1.txt','w')

In [2]: f.write("hello,how are you.\nI'm fine,and you?")
Out[2]: 36

In [3]: f.close()

In [10]: f = open('test1.txt','r')

In [11]: f.read(2)
Out[11]: 'he'

In [12]: f.tell()
Out[12]: 2

In [13]: f.read(5)
Out[13]: 'llo,h'

In [14]: f.tell()
Out[14]: 7

➢ 定位到某个位置 seek

使用seek()
seek(offset, from)有2个参数:
offset:偏移量
from:方向
0:表示文件开头
1:表示当前位置
2:表示文件末尾

python3中的seek:
定位到文件开头:seek(0,0),seek(正数,0);不支持seek(负数,0)
定位到当前位置:seek(0,1);不支持seek(负数,1),seek(正数,1)
定位到文件末尾:seek(0,2);不支持seek(负数,2),seek(正数,2)

python2中的seek:
定位到文件开头:seek(0,0),seek(正数,0);不支持seek(负数,0)
定位到当前位置:seek(正数,1);不支持seek(负数,1)
定位到文件末尾:seek(正数,2);不支持seek(负数,2)

In [10]: f = open('test1.txt','r')

In [11]: f.read(2)
Out[11]: 'he'

In [12]: f.tell()
Out[12]: 2

In [13]: f.read(5)			#只能依次读取
Out[13]: 'llo,h'

In [14]: f.tell()
Out[14]: 7					#读写位置只能依次向后排列

In [15]: f.seek(0,0)		#定位到开头
Out[15]: 0

In [16]: f.tell()			#读写位置变为0
Out[16]: 0

In [17]: f.read(5)			#又可以从头读取了
Out[17]: 'hello'

In [18]: f.read(5)
Out[18]: ',how '

三、使用os模块操作文件

使用os模块对文件的重命名、删除

➢ 文件重命名 rename

os模块中的rename()可以完成对文件的重命名操作

格式:rename(旧文件名, 新文件名)

import os
os.rename("程序猿自身修养.txt", "程序员自身修养.txt")

➢ 删除文件 remove

os模块中的remove()可以完成对文件的删除操作

格式:remove(“待删除的文件名”)

import os
os.remove("程序员自身修养.txt")

➢ 使用OS对文件夹的相关操作

创建文件夹
import os
os.mkdir("张三")
创建多层目录
import os
os.makedirs("张三/李四/王二")
获取当前目录
import os
os.getcwd()
改变默认目录

进入到某个目录,或者是切换目录

import os
os.chdir("./../")
获取目录列表

包含文件和目录,保存为列表形式

import os
print(os.listdir("./"))
删除文件夹

要求path必须是个空目录,否则抛出OSError错误

import os
os.rmdir("张三")

要想删除非空目录

import shutil
hutil.rmtree("非空文件夹")

练习:用程序创建一个a目录,进入a目录,再创建b目录;进入b目录后,创建一文件”日记.txt”,写入内容:”今天我在学习”,并且读取该文件,打印到终端上。

四、批量修改文件名

批量修改文件名

写个程序创建目录test,进入test目录,并创建文件,

创建文件如下:
人民的名义-1.avi
人民的名义-2.avi
人民的名义-3.avi
人民的名义-4.avi
人民的名义-5.avi
人民的名义-6.avi

#批量创建文件
import os

#创建目录
os.mkdir("./test")
#进入目录
os.chdir("./test")

#创建文件
i = 1
while i <= 6:
    open("人民的名义-%d.avi"%i,"w")
    i += 1
print("创建完毕!")
l = os.listdir("./")
for p in l:
    print(p)
-----------------------------------------------
创建完毕!
人民的名义-1.avi
人民的名义-2.avi
人民的名义-3.avi
人民的名义-4.avi
人民的名义-5.avi
人民的名义-6.avi
----------------------------------------------
$ cd test/
$ ls
人民的名义-1.avi  人民的名义-3.avi  人民的名义-5.avi
人民的名义-2.avi  人民的名义-4.avi  人民的名义-6.avi

批量修改文件

import os

#让用户输入修改哪个目录
folder = input("请输入你要批量修改文件的目录:")

os.chdir(folder)
#得到文件夹下所有文件名
file_names = os.listdir()

#遍历文件夹下的所有文件并重命名
for fileName in file_names:
    print(fileName)

    #新名字
    newname = "[宏福出品]"+fileName
    os.rename(fileName,newname)

print("重命名后的文件:\n")
for filename in os.listdir():
    print(filename)

Python换行符问题:

先知道结果:
在linux和mac系统上我们读写文本文件使用二进制方式或者文本方式都可以,因为在处理\n都是一样的;那么读写文本文件和二进制文件的时候,可以使用r和w模式或者rb和wb模式。
在window中的换行是\r\n,当我们使用程序以文本方式写入一个\n的时候,默认会帮我们加上\r,这样打开文件的时候才能得到换行的效果;所有在window上读写文本文件的时候,建议使用r或者w模式。在读写二进制文件的时候,建议用rb和wb模式。

不同操作系统换行符是不一样的
在处理文本数据的时候体现在不同操作系统处理\r\n是不一样的
不同操作系统换行符是不一样的

linux -->\n
unix–>\n
mac–>老版本(\r)–>\n
windows -->\r\n
对应换行符windows遇到\r\n才换行显示

在windows上分别使用wb和w写入内容:haha\nhaha

在linux系统上使用w和wb入的内容:haha\nhaha

在windows上都用rb(二进制)方式读取内容会发写入的内容有区别的

十三、对象与类

对象

一、面向过程和面向对象的区别
面向过程: 根据业务逻辑从上到下写代码
面向过程编程最易被初学者接受, 其往往用一长段代码来实现指定功能,开发过程的思路是将数据与函数按照执行的逻辑顺序组织在一起, 数据与函数分开考虑。

while Trueif cpu利用率 > 90%:
		#发送邮件提醒
		连接邮箱服务器
		发送邮件
		关闭连接
	if 硬盘使用空间 > 90%:
		#发送邮件提醒
		连接邮箱服务器
		发送邮件
		关闭连接
	if 内存占用 > 80%:
		#发送邮件提醒
		连接邮箱服务器
		发送邮件
		关闭连接

学过函数之后随着时间的推移,开始使用了函数式编程,增强代码的重用性和可读性,就变成了这样.

def 发送邮件(内容)
	#发送邮件提醒
	连接邮箱服务器
	发送邮件
	关闭连接
def main():
	while Trueif cpu利用率 > 90%:
			发送邮件('CPU报警')
		if 硬盘使用空间 > 90%:
			发送邮件('硬盘报警')
		if 内存占用 > 80%:
			发送邮件('内存报警')
#调用函数
main()

面向对象编程( Object Oriented Programming, OOP, 面向对象程序设计)
注: Java和C#来说只支持面向对象编程, 而Python比较灵活即支持面向对象编程也支持函数式编程。

面向过程和面向对象是解决问题的两种思路
面向对象编程

class MonitoringSystem(object):
"""监控系统工具"""
	def 发送邮件(self,内容)
		#发送邮件提醒
		连接邮箱服务器
		发送邮件
		关闭连接
	def 开始监控(self):
		while Trueif cpu利用率 > 90%:
				发送邮件('CPU报警')
			if 硬盘使用空间 > 90%:
				发送邮件('硬盘报警')
			if 内存占用 > 80%:
				发送邮件('内存报警')
ms = MonitoringSystem()
ms.开始监控()

需要了解的定义性文字

面向对象(object-oriented ;简称: OO) 至今还没有统一的概念 我这里把它定义为: 按人们认识客观世界的系统思维方式,采用基于对象(实体) 的概念建立模型,模拟客观世界分析、 设计、 实现软件的办法。

面向对象编程(Object Oriented Programming-OOP) 是一种解决软件复用的设计和编程方法。 这种方法把软件系统中相近相似的操作逻辑和操作应用数据、 状态,以类的型式描述出来,以对象实例的形式在软件系统中复用,以达到提高软件开发效率的作用。

类和对象

➢ 类的含义
具有相似内部状态和运动规律的实体的集合(或统称为抽象)。 具有相同属性和行为事物的统称。

➢ 对象的含义
某一个具体事物的存在 ,在现实世界中可以是看得见摸得着的。可以是直接使用的

➢ 类和对象之间的关系

➢ 类和对象之间的关系

➢ 类的构成
类(Class) 由3个部分构成
类的名称:类名
类的属性:一组数据
类的方法:允许对进行操作的方法 (行为)

➢ 类的抽象
拥有相同(或者类似)属性和行为的对象都可以抽像出一个类。

方法:一般名词都是类(名词提炼法)

定义类和对象

➢ 定义一个类

#经典类
class 类名:
	方法列表

#新式类
class 类名(object):
	方法列表

➢ 创建对象
当创建一个对象时, 就是用一个模子, 来制造一个实物 。
类是创建实例对象的模板, 可以创建很多个实例对象。

➢ 创建的格式为:对象名 = 类名()

➢ 调用对象的方法:对象.方法名()

理解 self 和 init 方法

➢ 从创建多个对象引入self
某个对象调用其方法时,Python解释器会把这个对象作为第一个参数传递给self,self通俗的话讲, 就是谁调用这个方法就是谁。

#理解self方法
#定义类
class Cat(object):
    #方法
    def eat(self):
        print("%s猫在吃鱼"%self.name)

    def drink(self):
        print("%s猫在喝水"%self.name)

    def print_info(self):
        print("%s的年龄是:%d"%(self.name,self.age))

tom = Cat()
tom.name = "汤姆"
tom.age = 40
tom.eat()
tom.print_info()

print("="*30)

lanmao = Cat()
lanmao.name = "蓝猫"
lanmao.age = 41
lanmao.drink()
lanmao.print_info()

➢ 类的 __ init__() 方法
1.当创建实例对象成功后, 有Python解释器来调用该方法, 这个方法不用我们手动调用;

➢ __ init__() 方法使用方式

class 类名:
#初始化函数,用来完成一些默认的设定
	def __init__(self):
	pass

➢ 使用 __ init__() 传递参数

class Cat(object):
    def __init__(self,name,age):
        self.name = name
        self.age = age

    def eat(self):
        print("猫在吃鱼!!!")

    def happy(self):
        print("%s觉得今天天气不错!"%self.name)

    def print_sex(self,sex):
        print("%s的性别是:%s" % (self.name, self.sex))

tom = Cat("汤姆",18)
print("%s的年龄是:%d"%(tom.name,tom.age))
tom.happy()
tom.sex = "男"
tom.print_sex(tom.sex)
lanmao = Cat("蓝猫",10)
print("%s的年龄是:%d"%(lanmao.name,lanmao.age))
lanmao.happy()
lanmao.sex="女"
lanmao.print_sex(lanmao.sex)

“魔法”方法

➢ "魔法"方法id()和__str__()
打印id()

class Cat(object):
    def __init__(self,name,age):
        self.name = name
        self.age = age

    def eat(self):
        print("猫在吃鱼!!!")

    def happy(self):
        print("%s觉得今天天气不错!"%self.name)

tom = Cat("汤姆",18)
print(id(tom))
lanmao = Cat("蓝猫",10)
print(id(lanmao))

定义 __ str__() 方法

当使用print输出对象的时候,只要自己定义了 __ str__(self) 方法,那么就会打印从在这个方法中return的数据.

class Cat(object):
    def __init__(self,name,age):
        self.name = name
        self.age = age
    def eat(self):
        print("猫在吃鱼!!!")

    def happy(self):
        print("%s觉得今天天气不错!"%self.name)

    def __str__(self):
        return "%s的年龄是:%d"%(self.name,self.age)

tom = Cat("汤姆",18)
print(tom)

lanmao = Cat("蓝猫",10)
print(lanmao)

➢ 类、对象、实体的相互关系和面向对象的解题思维方式

类属性、实例属性

➢ 类属性
在类的属性中,分为公有类属性和私有类属性
公有类属性:所有对象都可访问
私有类属性:只能在类内部访问,不能通过实例对象访问
➢ 实例属性(对象属性)
➢ 通过实例(对象)去修改类属性

class Person(object):
    #name = "Tom" #公有类属性
    __age1 = 18  #私有类属性

    address = '北京'

    def __init__(self):
        self.name = 'xiaoming'  #实例属性
        self.age = 20  #实例属性

    def test(self):
        self.__age1  = 20
        print("输出私有类属性:",self.__age1)

p = Person()
p.age = 12
p.address = '上海'
print(p.name)
print(p.age)
print(p.address)
p.test()
#print(p.age)   #错误,不能在类外通过实例对象访问私有类属性
#print(Person.age)   #错误,不能使用类对象访问实例属性
#print(Person.__age) #错误,不能再类外通过类对象访问私有类属性

➢ __ del__() 方法

创建对象后, Python解释器默认调用 __ init __ () 方法。 当删除一个对象时,Python解释器也会默认调用一个方法, 这个方法为 __ del __ () 方法。 在Python中, 对于开发者来说很少会直接销毁对象(如果需要, 应该使用del关键字销毁)。 Python的内存管理机制能够很好的胜任这份工作。 也就是说,不管是手动调用del还是由Python自动回收都会触发 __ del__ 方法执行。

面向对象四大特征

➢ 抽象

忽略一个主题中与当前目标无关的东西,专注的注意与当前目标有关的方面.(就是把现实世界中的某一类东西,提取出来,用程序代码表示,抽象出来的一般叫做类或者接口).抽象并不打算了解全部问题,而是选择其中的一部分,暂时不用部分细节.抽象包括两个方面,一个数据抽象,二是过程抽象.
数据抽象 -->表示世界中一类事物的特征,就是对象的属性.比如鸟有翅膀,羽毛等(类的属性)
过程抽象 -->表示世界中一类事物的行为,就是对象的行为.比如鸟会飞,会叫(类的方法)

➢ 封装

属性, 方法----> 封装类
有些时候我们不希望把对象的属性公开, 就可以把它设为私有, Python并没有像其他语言对成员的权限控制系统, 如private, 默认情况下, Python的所有属性都是公有的, 可以被访问到, 要设成私有, 则在前面加双下划线。

➢ 继承

继承可以使子类拥有父类的属性和方法, 并且可以重写这些方法, 加强代码的复用性Python中子类可以有多个父类, 但是不建议这样使用, 一般会产生重复调用的问题, Super().方法名, 可以调用父类的方法( 不用传参, 作用是调用父类的方法, 传的是子类实例的值)

➢ 多态

多态指的是一类事物有多种形态,(一个抽象类有多个子类, 因而多态的概念依赖于继承,Python里面不依赖继承)。指允许不同类的对象对同一消息做出响应。 即同一消息可以根据发送对象的不同而采用多种不同的行为方式。

面向对象三大特征

封装

面向对象的程序设计中,某个类把所需要的数据(也可以说是类的属性)和对数据的操作(也可以说是类的行为)全部都封装在类中,分别称为类的成员变量和方法(或成员函数)。这种把成员变量和成员函数封装在一起的编程特性称为封装。

公有成员变量和私有成员变量:
Python中用成员变量的名字来区分是公有成员变量或者是私有成员变量。Python中,以两个下划线 ‘ __ ’ 开头的变量都是私有成员变量,而其余的变量都属于公有成员变量。其中,私有的成员变量只能在类的内部访问,而共有的公有的成员变量可以在类的外部进行访问。

公有方法和私有方法:
类的方法是对类行为的封装。
类的方法也分为公有方法和私有方法。
类的私有方法只能通过对象名(在类内部也就是self)在类的内部进行访问。
而公有方法可以在类的外部通过对象名进行访问。
同样,公有的成员方法和私有的成员方法也是通过名字来区分的,双下划线 ‘ __ ’ 开头的方法是私有成员方法。

➢ 私有方法:只能在类的内部进行访问,对象无法访问。

#私有方法应用场景

class Test:
    #核心私有方法,用于发送短信
    def __send_msg(self):
        print("----正在发送短信---")

    def send_msg(self,new_money):
        if new_money > 100000:
            self.__send_msg()

        else:
            print("余额不足,请先充值!")

t = Test()
t.send_msg(1000000)

➢ 私有属性: 提高代码安全性,不允许别人随意修改。

#私有属性的应用

class Person(object):
    def __init__(self,name):
        self.__name = name

    def get_name(self):
        return self.__name

    def set_name(self,new_name):
        if len(new_name) >= 5:
            self.__name = new_name

        else:
            print("名字长度必须大于等于5.")

xiaoming = Person("xiaoming")
print(xiaoming.get_name())
#print(xiaoming.__name)
xiaoming.set_name("daming")

print(xiaoming.get_name())

"""
总结:
封装可以保证数据安全性,一些并不能让用户直接修改或访问的内容进行封装,并给用户提供访问或修改的接口,用户可以调用该接口进行访问或修改,必要时可以直接拒绝用户访问或修改。

练习:
定义一个类Person ,类中有私有方法和普通方法,私有属性和普通属性,并且都要调用私有方法和私有属性
"""

继承

➢ 继承的概念:
在程序中, 继承描述的是事物之间的所属关系, 例如猫和狗都属于动物,程序中便可以描述为猫和狗继承自动物;同理, 波斯猫和巴厘猫都继承自猫, 而沙皮狗和斑点狗都继承狗

#继承
class Animal:
    def eat(self):
        print("---吃----")

    def drink(self):
        print("----喝-----")

    def sleep(self):
        print("----睡觉-----")

    def run(self):
        print("----跑-----")

class Dog(Animal):
    def call(self):
        print("---汪汪叫.....")

class Cat(Animal):
    def call(self):
        print("喵喵叫.....")

class XiaoTq(Dog):
    def fly(self):
        print("---飞----")

xtq = XiaoTq()
xtq.fly()
xtq.call()
xtq.eat()
#注意点:私有属性和方法不会被继承
class Animal(object):
    def __init__(self,name = "动物",color="白色"):
        self.__name = name
        self.color= color

    def __test(self):
        print(self.__name)
        print(self.color)

    def test2(self):
        print(self.__name)
        print(self.color)

class Dog(Animal):
    def dog_test1(self):
        print(self.color)
        #print(self.__name)不能访问到父类的私有属性

    def dog_test2(self):
        self.test2()
        #self.__test() #不能访问父类的私有方法

A = Animal()
print(A.color)
A.test2()
print("---------------------------")

dog = Dog(name = "小花狗",color = "黄色")
dog.dog_test1()
dog.dog_test2()
dog.test2()

➢ 重写父类方法与调用父类方法:
所谓重写, 就是子类中, 有一个和父类相同名字的方法, 在子类中的方法会覆盖掉父类中同名的方法

#重写

class Cat(object):
    def sayHello(self):
        print("---hello----111")

    def test(self):
        print("111--test")

class Bosi(Cat):
    def sayHello(self):
        print("----hello---2222")
        #super().test()

    def test(self,name):
        print("2222--test%s"%name)

"""所谓重写,就是子类中,有一个和父类中相同名字的方法,在子类中的方法就会覆盖掉父类中同名方法"""

bosi = Bosi()
bosi.sayHello()
bosi.test("波斯猫")

➢ 使用super调用父类的方法:
可以直接调用父类方法,不需要通过 父类名.父类方法名 的方式

class Animal:
    def eat(self):
        print("---吃----")

    def drink(self):
        print("----喝-----")

    def sleep(self):
        print("----睡觉-----")

    def run(self):
        print("----跑-----")

    def call(self):
        print("--不知怎么叫----")

class Dog(Animal):
    def call(self):
        print("---汪汪叫.....")

class XiaoTq(Dog):
    def fly(self):
        print("---飞----")

    def call(self):
        print("..狂叫...")
        super().call()
        #Dog.call(self)

xtq = XiaoTq()
xtq.call()

➢ Python中的多继承概念:

class Base(object):
    def test(self):
        print("---Base---")

class A(Base):
    def test(self):
        print("A---test1----")

class B(Base):
    def test(self):
        print("B---test2---")

class C(A,B):
    def test(self):
        print("C---C---")

c = C()
c.test()
print(C.__mro__) #打印搜索方法的过程

多态

➢ 多态的定义:
所谓多态:定义时的类型和运行时的类型不一样, 此时就成为多态。多态指的是一类事物有多种形态,(一个抽象类有多个子类,因而多态的概念依赖于继承)。
当子类和父类都存在相同的print_self()方法时, 我们说, 子类的print_self() 覆盖了父类的 print_self(), 在代码运行的时候 , 总是会调用子类的print_self()。 这样, 我们就获得了继承的另一个好处: 多态。

#多态
class Dog(object):
    def print_self(self):
        print("大家好,我是xxx,请多多关照!")

class XiaoTq(Dog):
    def print_self(self):
        print("Hello,everybody,我是你们的老大,我是哮天犬!!")

#定义一个执行函数
def introduce(obj):
    obj.print_self()
    #定义时的类型并不知道调用哪个类的方法,当运行时才能确定调用哪个类的方法,这种情况我们就叫做多态。

dog = Dog()
introduce(dog)
xtq = XiaoTq()
introduce(xtq)

新式类和经典类的区别

新式类都从object继承,经典类不需要
Python 2.x中默认都是经典类, 只有显式继承了object
Python 3.x中默认都是新式类,经典类被移除, 不必显式的继承object

#coding=utf-8
class A:
	def __init__(self):
		print ('a',)
class B(A):
	def __init__(self):
		A().__init__()
		print ('b',)
d=B()
print(type(d))  
'''
经典类采用深度优先算法,新式类采用广度优先算法
'''
class A(object):
    def test(self):
        print("A----test")

class B(A):
    def test(self):
        print("B----test")

class C(B):
    def test(self):
        print("C----test")

print(C.__mro__)

十四、MySQL、pyMySQL

一、MySQL 安装

1.首先卸载自带的MySQL
CentOS7默认不能安装Mysql,只能安装MariaDB。

2.安装 wget 并下载

yum install wget
	wget -i -c http://dev.mysql.com/get/mysql57-community-release-el7-10.noarch.rpm

3.安装下载

yum -y install mysql57-community-release-el7-10.noarch.rpm

4.安装 mysql-community-server

yum -y install mysql-community-server

5.启动MySQL服务

systemctl start mysqld.service

6.查看启动状态

systemctl status mysqld.service

7.查找初始化密码

grep "password" /var/log/mysqld.log

8.进入MySQL命令行

mysql -uroot -p[mysqld.log中的密码]

9.修改密码

mysql> ALTER USER 'root'@'localhost' IDENTIFIED BY 'Root@123';

10.想要通过WinDows远程登录需要授权

mysql> grant all privileges on *.* to root@'%' identified by  'Root@123';
# grant all privileges(权限) on *.*(某数据库的某表) to root(用户)@'%(需要访问的ip地址)' identified by  'Root@123'(可同时更改密码);

11.刷新权限

mysql> flush privileges;

12.连接数据库管理工具 Navicat Premium

13.创建数据库、表

右击新建数据库 ———— 库内新建表

二、PyMySQL 模块 安装

1.PyMySQL模块安装

$ pip3 install PyMySQL

2.安装后测试

# 进入ipython
$ ipython
#导入pymysql模块,无报错表示安装成功
In [1]: import pymysql

3.Python 连接 MySQL

数据库连接,关键字说明
Connect :连接数据库
Cursor :创建游标对象
Execute :执行SQL语句
Close :关闭数据库连接

#打开数据库连接
In [3]: db = pymysql.connect(user="root",password="Root@123",host="localhost",database="testdb")
#使用cursor()方法创建一个游标对象cursor
In [4]: cursor = db.cursor()
#使用execute方法执行SQL,返回1代表有数据
In [5]: cursor.execute("SELECT VERSION()")
Out[5]: 1
#fetchone()获取单条数据
In [6]: data = cursor.fetchone()
In [7]: data
Out[7]: ('5.7.32',)
#关闭数据库连接
In [8]: db.close()

查看默认参数书写顺序:

$ cd /usr/local/lib/python3.7/site-packages/pymysql/
$ vim connections.py 
搜索: /__init__ 查看书写熟顺序
# 以 * 开始的需要加变量
# db = pymysql.connect(user="root",password="Root@123",host="localhost",database="testdb")
# 不以 * 开头时
# db = pymysql.connect("root","Root@123","localhost","testdb")

三、增、删、改、查

1.数据库内创建表

$ cd /opt/datas/
$ vim createTable.py
# 导入pymysql模块
import pymysql

# 打开数据库连接
db = pymysql.connect(user="root",password="Root@123",host="localhost",database="testdb")
# 使用cursor()方法创建一个游标对象cursor
cursor = db.cursor()
# 检测EMPLOYEE表是否存在,存在则删除
cursor.execute("DROP TABLE IF EXISTS EMPLOYEE")
# 使用预处理语句创建表
sql = """CREATE TABLE EMPLOYEE(
FIRST_NAME CHAR(20) NOT NULL,
LAST_NAME CHAR(20),
AGE INT,
SEX CHAR(2),
INCOME FLOAT)"""
# 若输入的是电话号码时,INT是不能插入11位数字的,此处可写为 BIGINT
# CHAR 型后面不写时默认是(1)
# 执行变量sql
cursor.execute(sql)
# 关闭连接
db.close()

2.增加 数据

$ cd /opt/datas/
$ vim insertTable.py
import pymysql

db = pymysql.connect(user="root",password="Root@123",host="localhost",database="testdb")
# 创建cursor()游标对象
cursor = db.cursor()

# SQL语句实现插入
sql = """INSERT INTO EMPLOYEE(FIRST_NAME,
LAST_NAME,
AGE,
SEX,
INCOME) VALUES('Mac','momo',20,'M',2000)"""
# 或者以下写法
# sql1 = """INSERT INTO EMPLOYEE(FIRST_NAME,
# LAST_NAME,
# AGE,
# SEX,
# INCOME) VALUES('%s','%s',%s,'%s',%s)"""%('Mac','momo',20,'M',2000)
try:
	# 执行SQL语句
	cursor.execute(sql)
	# cursor.execute(sql1)
	# 提交到数据库执行
	db.commit()
except:
	# 如果发生错误就回滚
	db.rollback()
# 关闭数据库连接
db.close()

3.查询 数据库数据

$ cd /opt/datas/
$ vim selectTable.py
import pymysql

db = pymysql.connect(user="root",password="Root@123",host="localhost",database="testdb")
# 创建cursor()游标对象
cursor = db.cursor()
# 条件:查询薪资大于1000的人
sql = "SELECT * FROM EMPLOYEE WHERE INCOME > %s"%(1000)

try:
	# 执行SQL语句
	cursor.execute(sql)
	# 获取所有记录列表
	# results = cursor.fetchone()	fetchone()是将查询到的结果作为一个结果集
	results = cursor.fetchall()
	# fetchall()是将查询到的结果作为全部的结果行
	for row in results:
		fname = row[0]
		lname = row[1]
		age = row[2]
		sex = row[3]
		income = row[4]
		# 打印结果
		print("fname=%s,lname=%s,age=%s,sex=%s,income=%s"%(fname,lname,age,sex,income))

except:
	print("Error:查询错误")

db.close()

4.修改 数据表内数据

$ cd /opt/datas/
$ vim updateTable.py
import pymysql

db = pymysql.connect(user="root",password="Root@123",host="localhost",database="testdb")
#创建cursor()游标对象
cursor = db.cursor()

#SQL语句实现将所有性别为M的人 年龄都加上一岁。
sql = "UPDATE EMPLOYEE SET AGE = AGE+1 WHERE SEX = 'M'"
#或者以下写法
try:
	#执行SQL语句
	cursor.execute(sql)
	#提交到数据库执行
	db.commit()
except:
	#如果发生错误就回滚
	db.rollback()
#关闭数据库连接
db.close()

5.删除 数据表内数据

import pymysql

db = pymysql.connect(user="root",password="Root@123",host="localhost",database="testdb")
#创建cursor()游标对象
cursor = db.cursor()

#SQL语句实现删除年龄大于22岁的人
sql = "DELETE FROM EMPLOYEE WHERE AGE > 22 "
try:
	#执行SQL语句
	cursor.execute(sql)
	#提交到数据库执行
	db.commit()
except:
	#如果发生错误就回滚
	db.rollback()
#关闭数据库连接
db.close()

十五、SQL扩展、视图

1.DQL(Data Query Language) 数据查询语言

◼ 查询数据库数据,如SELECT语句
◼ 简单的单表查询或多表的复杂查询和嵌套查询
◼ 数据库语言中最核心、最重要的语句
◼ 使用频率最高的语句

2.SELECT语法

语法
SELECT [ALL | DISTINCT]
{ * | table.* }
FROM table_name [ as table_ alias ]
[left|out|inner join table_name2]	# 联合查询
[ WHERE … ]		# 指定结果需满足的条件
[ GROUP BY …]	# 指定结果按照哪几个字段来分组
[HAVING …]	# 过滤分组的记录必须满足的次要条件
[ ORDER BY… ]	# 指定查询记录按一个或者多个条件排序
[ LIMIT { [ offset,] row_count | row_count ,offset }] ;		# 指定查询的记录从哪条至哪条
[] 括号代表可选的;
{} 括号代表必须的;
# MySQL语句中的注释符,也可以用 /*该处为注释*/

2.1指定查询字段

查询表结果时,可指定查询结果的数据列
查询表中所有的数据列结果,采用 “ * ” 符号;

select * from student;		# * 号此方法不推荐,效率低,一般使用字段名,效率高

可指定查询的结果数据列

# 如:只查询student表中的学号、姓名、电话
SELECT StudentNo, StudentName, Phone FROM student;
# 如:区分连接查询时两个表有同名的字段
SELECT student.StudentNo , result.StudentNo FROM student , result ;		#可指定该字段属于哪个表
SELECT s.StudentNo AS '学号',s.StudentName AS '姓名',s.Phone AS '手机号',r.studentNo from student AS s ,result AS r;
# AS 给数据列名取一个新别名,给表名取一个新别名
SELECT s.StudentNo  '学号',s.StudentName  '姓名',s.Phone  '手机号',r.studentNo from student s ,result r;
# AS 也可省略不写  

Linux 中运行 .sq文件

# 上传sql文件到 Linux,然后进入 mysql,在 mysql 中加载 .sql 文件
$ mysql > source ./testdb.sql

3.AS 子句作用

◼ 可给数据列名取一个新别名
◼ 可给表名取一个新别名
◼ 可把经计算或总结的结果用另外一个新值来代替

SELECT StudentNo AS “学号” FROM student;
SELECT a.StudentNo FROM student AS a;
SELECT Phone+1 AS Tel FROM student;		# 注意:AS 也可省略不写

# 将所有的学生成绩加 10
SELECT studentResult+10 AS newResult FROM result

4.DISTINCT关键字的使用

◼ 作用:
去掉SELECT查询返回的记录结果中重复(所有返回列的值都相同)的记录,只返回一条
◼ 语法:

SELECT DISTINCT 字段名1, 字段名2... FROM 表名

◼ 注意:
ALL 关键字是默认的,返回所有的记录,与之相反

# 去重
SELECT DISTINCT subjectNo FROM result;

课堂练习
◼ 需求说明:查询课程表( subject)的所有记录
◼ 要求:
返回字段名称使用别称
返回课程名(SujectName)
总课时(SubjectHour)
返回10天上完课程的均课时( ClassHour/10)

SELECT s.SubjectName '课程名称',s.ClassHour '总课时',s.ClassHour/10 '均课时' FROM `subject` AS s;

5.where条件语句

◼ 用于检索数据表中符合条件的记录
◼ 搜索条件可由一个或多个逻辑表达式组成,结果一般为真或假
◼ 搜索条件的组成
逻辑操作符
比较操作符

逻辑操作符

操作符名称语法描述
AND或&&a AND b 或 a && b逻辑与,同时为真结 果才为真
OR或||a OR b 或 a||b逻辑或,只要一个为 真,则结果为真
NOT或!NOT a 或 !a逻辑非,若操作数为 假,结果则为真

比较操作符

操作符名称语法描述
IS NULLa IS NULL若操作符为NULL,则结果为 真
IS NOT NULLa IS NOT NULL若操作符不为NULL,则结果 为真
BETWEENa BETWEEN b AND c若a范围在b与c之间则结果 为真
LIKEa LIKE bSQL模式匹配,若a匹配b, 则结果为真
INa IN (a1,a2,a3,….)若a等于a1, a2…中的某一 个,则结果为真

注意
1、数值数据类型的记录之间才能进行算术运算;
2、相同数据类型的数据之间才能进行比较;

6.AND 查询

#查询大于80小于等于90的成绩	AND
SELECT * FROM result WHERE studentResult >=80 AND studentResult <= 90;

7.NULL 空值条件查询

◼ NULL代表“无值”
◼ 区别于零值0和空符串“”
◼ 只能出现在定义允许为 NULL 的字段
◼ 须使用 IS NULL 或 IS NOT NULL 比较操作符去比较

#查询电话不为空的学员信息 	NULL
SELECT * FROM student WHERE Phone IS NOT NULL;
#查询电话为空的学员信息	NULL
SELECT * FROM student WHERE Phone IS NULL;

8.BETWEEN AND 范围查询

◼ 根据一个范围值来检索
◼ 语法:

SELECT 字段列1,字段2 ,…FROM 表名 WHERE 字段x BETWEEN 值1 AND 值2
◼ 等同于 >=<= 联合使用
#BETWEEN AND
# 查询result表中成绩在70-80之间的
SELECT * FROM result WHERE studentResult BETWEEN 70 AND 80;	
等同于:
SELECT * FROM result WHERE studentResult >= 70 AND studentResult <=80;
# 查询result表中时间在2020-12-5与2021-2-25之间的
SELECT * FROM result WHERE ExamDate BETWEEN '2020-12-5' AND '2021-2-25';	

9.使用 IN 进行范围查询

◼ 在WHERE子句中使用IN进行范围查询
◼ 语法:

SELECT 字段列1,字段2 ,…FROM 表名 WHERE 字段x IN (1,2,3)
◼ 查询的字段x的值,至少与括号中的一个值相同
◼ 多个值之间用英文逗号隔开
#查询课时为100或110或120的
SELECT * FROM `subject` WHERE ClassHour = 100 OR ClassHour =110 OR ClassHour = 120;	 #普通组处理方式
SELECT * FROM `subject` WHERE ClassHour IN (100,110,120);	#使用 IN 进行查询方式,更为简洁,效率更高

10.在 WHERE 子句中,使用 LIKE 关键字进行模糊查询

◼ 与 “ % ” 一起使用,表示匹配0或任意多个字符
◼ 与 “ _ ” 一起使用,表示匹配单个字符

# 查询包含“数学”的所有课程
SELECT   *  FROM subject WHERE SubjectName  LIKE  "%数学%";
#查询所有姓名为“李**”三个字的学生信息
SELECT  StudentNo,StudentName FROM student  WHERE StudentName LIKE "李__";

练习
◼ 需求说明:查询所有姓“李”的学生所有成绩

#使用内连接和模糊查询
SELECT studentName,studentResult FROM student s,result r WHERE s.StudentNo = r.studentNo AND s.StudentName LIKE '李%';

11.常用聚集函数

1.ORDER BY 排序

◼ ORDER BY排序查询
◼ 对SELECT语句查询得到的结果,按某些字段进行排序
◼ 与DESC或ASC搭配使用,默认为ASC

# 查询所有考试结果,并按成绩由高到低排列
# 默认为升序排序,DESC代表降序
SELECT studentResult '成绩排名' FROM result GROUP BY studentResult DESC;

2.LIMIT

LIMIT m,n 或 LIMIT m OFFSET n
限制SELECT返回结果的行数
m 制定第一个返回记录行的偏移量
n 制定返回记录行的最大数目
注意: m不指定则从第一条开始,偏移量为0
m可不写,则返回前n条记录
LIMIT 常用于分页显示

#LIMIT
SELECT * FROM result LIMIT 5,10; #返回第6-15条记录
SELECT * FROM result LIMIT 5;  #返回前5条记录

3.常用函数 COUNT( )、 SUM( )、 AVG( )、MAX( )、 MIN( )

函数名称描述
COUNT( )返回满足SELECT条件的记录总和数, 如 SELECT COUNT(*)… # 不建议使用 *,效率低
SUM( )返回数字字段或表达式列作统计,返 回一列的总和
AVG()通常为数值字段或表达列作统计,返 回一列的平均值
MAX( )可以为数值字段、字符字段或表达式 列作统计,返回最大的值
MIN( )可以为数值字段、字符字段或表达式 列作统计,返回最小的值
#Count 返回某列的行数
SELECT COUNT(studentNo) FROM result;
SELECT COUNT(Phone) FROM student;
#SUM 返回某列值之和
SELECT SUM(ClassHour) FROM `subject`
SELECT AVG(ClassHour) FROM `subject`
SELECT MAX(ClassHour) FROM `subject`
SELECT MIN(ClassHour) FROM `subject`

SELECT COUNT(studentNo),SUM(studentResult), MAX(studentResult) ,MIN(studentResult),AVG(studentResult) FROM result;

12.GROUP BY 分组

◼ 使用GROUP BY关键字对查询结果分组
对所有的数据进行分组统计
分组的依据字段可以有多个,并依次分组
与 HAVING 结合使用,进行分组后的数据筛选

# GROUP BY 分组
SELECT GradeID,COUNT(*) FROM `subject` GROUP BY GradeID;
# HAVING
SELECT GradeID,COUNT(*) FROM `subject` GROUP BY GradeID HAVING COUNT(*) >= 2;

13.连接查询(多表查询)

◼ 如需要多张数据表的数据进行查询,则可通过连接运算符实现多个查询
分类包括:
◼ 外连接 ( out join )
左连接( LEFT JOIN)
右连接 ( RIGHT JOIN)
◼ 内连接 ( inner join )

外连接
◼ 左连接 ( LEFT JOIN ) :从左表( table_1)中返回所有的记录,即便在右( table_2)中没有匹配的行;
SELECT 字段1,字段2,… FROM table_1 LEFT [ OUTER ] JOIN table_2 ON table_1.字段x = table_2.字段y;
◼ 右连接 ( RIGHT JOIN ) :从右表( table_2)中返回所有的记录,即便在左( table_1)中没有匹配的行;
SELECT 字段1,字段2,… FROM table_1 RIGHT [ OUTER ] JOIN table_2 ON table_1.字段x = table_2.字段y;

#左连接
SELECT StudentName , studentResult FROM student LEFT JOIN result ON student.StudentNo = result.studentNo;
#右连接
SELECT StudentName , studentResult FROM student RIGHT JOIN result ON student.StudentNo = result.studentNo;
#右连接
SELECT StudentNo,StudentName,GradeName FROM grade RIGHT JOIN student ON grade.GradeID=student.GradID;

不同的 SQL JOIN 对比

操作符名称描述
JOIN( INNER JOIN)如果表中有至少一个匹配,则返回行
LEFT JOIN即使右表中没有匹配,也从左表中返 回所有的行
RIGHT JOIN即使左表中没有匹配,也从右表中返 回所有的行

14.子查询

◼ 在查询语句中的WHERE条件子句中,又嵌套了另外一个查询语句

注意: 嵌套查询可由多个子查询组成,求解的方式是由里及外;
子查询返回的结果一般都是集合,故而建议使用 IN 关键字;

#查询课程为《Python-1》且分数不小于80分的学生的学号和姓名
SELECT SubjectNo FROM `subject` WHERE SubjectName = 'Python-1'
SELECT StudentNo,StudentResult FROM result WHERE studentResult >= 80
SELECT StudentNo,StudentResult FROM result WHERE studentResult >= 80 AND SubjectNo IN (SELECT SubjectNo FROM `subject` WHERE SubjectName = 'Python-1') newResult

SELECT student.StudentNo,studentName,newResult.studentResult 
FROM student, 
	(SELECT StudentNo,studentResult 
		FROM result 
		WHERE studentResult >= 80 AND SubjectNo IN (SELECT `subject`.SubjectNo 
				FROM `subject` 
				WHERE SubjectName = 'Python-1') 
	) newResult 
WHERE student.StudentNo=newResult.studentNo

十六、视图

1.MySQL视图

​ **MySQL视图是一个虚拟表,是sql的查询结果,其内容由查询定义。**同真实的表一样,视图包含一系列带有名称的列和行数据。
​ 对其中所引用的基础表来说,MySQL视图的作用类似于筛选。定义视图的筛选可以来自当前或其它数据库的一个或多个表,或者其它视图。通过视图进行查询没有任何限制,通过它们进行数据修改时的限制也很少。
​ 视图是存储在数据库中的查询的sql 语句,它主要出于两种原因:安全原因,视图可以隐藏一些数据,如:社会保险基金表,可以用视图只显示姓名,地址,而不显示社会保险号和工资数等,另一原因是可使复杂的查询易于理解和使用。

案列测试:
测试表:user有id,name,age,sex字段
测试表:goods有id,name,price字段
测试表:ug有id,userid,goodsid字段

1.首先创建数据库mydb,在数据库mydb下运行sql文件,然后使用查询,将查询结果保存为视图

作用一

提高了重用性

就像一个函数,如果要频繁获取 user 的 name 和 goods 的 name。

SELECT a.`name` username,b.`name` goodsname 
FROM `user` as a ,goods as b ,ug AS c
WHERE a.id = c.userid and c.goodsid = b.id

#视图将常用的查询结果保存为视图,运行下面代码即可创建视图
create view other AS SELECT a.`name` username,b.`name` goodsname 
FROM `user` as a ,goods as b ,ug AS c
WHERE a.id = c.userid and c.goodsid = b.id
# other为视图名,自定义。
# 刷新视图,即可看到创建的视图

作用二

对数据库重构,却不影响程序的运行。

假如因为某种需求,需要将user拆成表usera和表userb,该两张表的结构如下:
测试表:usera有id,name,age字段
测试表:userb有id,name,sex字段

#创建表的方法:
CREATE TABLE usera(SELECT id,`name`,age FROM `user`);
CREATE TABLE userb(SELECT id,`name`,sex FROM `user`);

#创建一个视图,包含两张表中指定的字段
CREATE VIEW user1 AS SELECT a.name,a.age,b.sex 
FROM usera as a,userb as b WHERE a.name=b.name

作用三

提高了安全性能。可以对不同的用户,设定不同的视图。

例如:某用户只能获取user表的name和age数据,不能获取sex数据。如何创建?

CREATE VIEW other1 AS SELECT a.name,a.age FROM user as a;
SELECT * FROM other;		#此时查询最多只能获取name和age。并不能获取其它数据

作用四

让数据更加清晰。想要什么样的数据,就创建什么样的视图。

#显示视图创建情况
SHOW CREATE VIEW other;
show TABLES;
 
#删除视图
DROP VIEW other1;

#重命名视图
RENAME TABLE other to oo

十七、IPy 模块

​ IPy 模块可以方便的处理 IPv4 和 IPv6 地址,在 IP 地址规划中,涉及到计算大量的 IP 地址,包括网段、网络掩码、广播地址、子网数、IP 类型等

1.安装

$ cd 安装目录
# 下载 IPy 模块
$ wget https://pypi.python/packages/source/I/IPy/IPy-0.81.tar.gz --no-check-certificate
$ tar -zxvf Ipy-0.81.tar.gz
$ cd IPy-0.81
$ python setup.py install

2.应用

# 注意此处需要在安装目录内进入 ipython ,然后导入 IPy 模块,否则会导入失败
$ cd /opt/app/IPy-0.81/
$ ipython

# 导入IPy模块
In [1]: from IPy import IP
# IP网段的基本处理
In [2]: from IPy import IP
In [3]: ip1 = IP('192.168.1.0/24')
In [4]: print(ip1.len())
256
In [5]: for a in ip1:
   ...:     print(a)  #输出192.168.1.0/24 网段的所有IP清单

# IP地址的转化 
In [6]: ip2 = IP('192.168.1.1')

In [7]: ip2.reverseName()  #反向解析地址格式
Out[7]: '1.1.168.192.in-addr.arpa.'

In [8]: ip2.iptype()  #查看IP地址类型
Out[8]: 'PRIVATE'

In [9]: ip2.int()  #将格式转换为整形格式
Out[9]: 3232235777

In [10]: ip2.strHex()  #将格式转换为16进制格式
Out[10]: '0xc0a80101'

In [11]: ip2.strBin()  #将格式转换为2进制格式
Out[11]: '11000000101010000000000100000001'

In [12]: print(IP(0xc0a80101))  #将16进制转化为IP格式
192.168.1.1

IP网段的转化:
#输出192.168.1.0/24
In [13]: print(IP('192.168.1.0').make_net('255.255.255.0'))
192.168.1.0/24

In [14]: print(IP('192.168.1.0/255.255.255.0',make_net=True))
192.168.1.0/24

In [15]: print(IP('192.168.1.0-192.168.1.255',make_net=True))
192.168.1.0/24

In [18]: print(IP('192.168.1.0/24').strNormal(0))
192.168.1.0

In [19]: print(IP('192.168.1.0/24').strNormal(1))  #prefix 格式
192.168.1.0/24

In [20]: print(IP('192.168.1.0/24').strNormal(2))  # decimalnetmask 格式
192.168.1.0/255.255.255.0

In [21]: print(IP('192.168.1.0/24').strNormal(3))  # lastIP格式
192.168.1.0-192.168.1.255

多网络计算方法:
功能:比较两个网段是否存在包含、重叠等关系,
比如:192.168.1.0/24192.168.1.0/25 
      192.168.0.0/24192.168.1.0/24
IP('192.168.0.0/24').overlaps('192.168.1.0/24') #返回1表示重叠,返回0表示不重叠

In [38]: ip1 = IP('192.168.1.0/24')

In [39]: ip1.net()	#网络地址
Out[39]: IP('192.168.1.0')

In [40]: ip1.netmask()	#子网掩码
Out[40]: IP('255.255.255.0')

In [41]: ip1.broadcast()	#广播地址
Out[41]: IP('192.168.1.255')
练习:判断一个 ip 是网段还是网络地址
$ vim iptest.py

# 导入 IPy 模块
from IPy import IP
ip_input = input("请输入IP地址或网段地址:")
ip = IP(ip_input)
# 使用 len() 函数,通过长度来判断
if len(ip) > 1:# 这就是一个网段
    print("网络地址是:%s"%ip.net())
    print("子网掩码是:%s"%ip.netmask())
else:
    print("IP地址反向解析:%s"%ip.reverseName())
    print("十六进制地址:%s"%ip.strHex())

十八、psutil 模块

1.psutil 介绍

​ psutil 是一个跨平台库,能够轻松实现获取系统运行的进程和系统利用率,包括CPU、内存、磁盘、网络等信息,它主要应用于系统监控,分析和限制系统资源及进程的管理。它实现了同等命令行工具提供的功能,如:ps、top、lsof、netstat、ifconfig、who、free等

2.psutil 模块安装

Windows:pip3 install psutil

Linux:(使用root用户安装)

$ cd 合适目录
$ wget https://pypi.python/packages/source/p/psutil/psutil-3.2.1.tar.gz --no-check-certificate
$ tar zxvf psutil-3.2.1.tar.gz
$ cd psutil-3.2.1
$ python3 setup.py install

安装验证
# cd 出安装目录进行验证
$ ipython
# 进入ipython后,加载psutil模块运行,无报错即安装正常
In [1]: import psutil

In [2]: print(psutil.virtual_memory())
svmem(total=1019797504, available=499310592, percent=51.0, used=829345792, free=190451712, active=404471808, inactive=201838592, buffers=2277376, cached=306581504)

运行如若报错: command gcc ………… status 1

解决方法:
1、安装运行库
yum install gcc libffi-devel python-devel openssl-devel -y

2、使用python3 需要安装puthon3的devel环境
yum install python3-devel -y

3.如果报错为版本问题,可以使用以下命令进行版本升级。
pip3 install --ignore-installed psutil --user

3.语法使用

In [1]: import psutil	# 加载psutil模块

In [2]: psutil.virtual_memory()		#获取内存信息
Out[2]: svmem(total=1019797504, available=600526848, percent=41.1, used=674537472, free=345260032, active=340156416, inactive=191848448, buffers=2150400, cached=253116416)

In [3]: psutil.virtual_memory().total		#获取内存总大小
Out[3]: 1019797504

In [4]: psutil.virtual_memory().total / (1024*1024*1024)	#将内存以字节为单位转换为单位G
Out[4]: 0.9497604370117188

In [9]: psutil.cpu_times()		#获取CPU信息
Out[9]: scputimes(user=12.85, nice=0.0, system=21.49, idle=349.06, iowait=11.48, irq=0.0, softirq=0.42, steal=0.0, guest=0.0, guest_nice=0.0)

In [10]: psutil.cpu_count()	#获取CPU逻辑个数
Out[10]: 1

In [11]: psutil.cpu_count(logical=False) # 获取CPU物理个数
Out[11]: 1

In [13]: psutil.cpu_percent()	#获取当前CPU使用率
Out[13]: 3.2

In [14]: psutil.cpu_percent(5)	#获取5秒内CPU的使用均值
Out[14]: 0.2

In [15]: psutil.disk_partitions()	#获取磁盘分区信息
Out[15]: 
[sdiskpart(device='/dev/sda3', mountpoint='/', fstype='xfs', opts='rw,relatime,attr2,inode64,noquota'),
 sdiskpart(device='/dev/sr0', mountpoint='/media', fstype='iso9660', opts='ro,relatime'),
 sdiskpart(device='/dev/sda1', mountpoint='/boot', fstype='xfs', opts='rw,relatime,attr2,inode64,noquota')]

In [16]: psutil.disk_usage("/")	#获取"/"分区信息
Out[16]: sdiskusage(total=18791530496, used=5824638976, free=12966891520, percent=31.0)

In [17]: psutil.disk_io_counters()	#获取磁盘读写信息
Out[17]: sdiskio(read_count=8413, write_count=2917, read_bytes=252215296, write_bytes=44683776, read_time=71753, write_time=2221)

In [19]: psutil.users()	#获取登录用户信息
Out[19]: [suser(name='root', terminal='pts/0', host='192.168.1.1', started=1614417792.0)]

In [20]: psutil.boot_time()	#获取开机时间信息,此时间是时间戳格式
Out[20]: 1614417691.0

In [21]: import datetime	#加载时间模块

In [22]: datetime.datetime.fromtimestamp(psutil.boot_time())	#将时间转换为正常时间格式
Out[22]: datetime.datetime(2021, 2, 27, 17, 21, 31)

In [23]: datetime.datetime.fromtimestamp(psutil.boot_time()).strftime("%Y-%m-%d %H:%M:%S")	# 使用.strftime()再次将时间转换为普通格式
Out[23]: '2021-02-27 17:21:31'

In [25]: psutil.users()
Out[25]: [suser(name='root', terminal='pts/0', host='192.168.1.1', started=1614417792.0)]

In [27]: for a in psutil.users():	#使用for循环提取时间戳并转换为普通时间格式
    ...:     print(datetime.datetime.fromtimestamp(a[3]).strftime("%Y-%m-%d %H:%M:%S"))
    ...: 
2021-02-27 17:23:12

In [57]: psutil.pids() #获取所有进程的PID,以列表形式返回

In [58]: p = psutil.Process(30181) #实例化一个对象,参数为进程ID

In [60]: p.name()  #获取进程名称
Out[60]: 'ipython'

In [61]: p.exe()  #获取进程路径
Out[61]: '/usr/local/bin/python3.7'

In [62]: p.cwd()  #获取进入或启动该进程的路径
Out[62]: '/'			

In [64]: p.status()  #查看进程状态
Out[64]: 'running'

In [65]: p.create_time() #查看进程创建时间
Out[65]: 1614302819.11

In [66]: p.uids()  #UID信息
Out[66]: puids(real=0, effective=0, saved=0)

In [68]: p.gids()  #GID信息
Out[68]: pgids(real=0, effective=0, saved=0)

In [69]: p.cpu_times()  #CPU时间
Out[69]: pcputimes(user=17.52, system=1.86)

In [70]: p.memory_percent()  #内存利用率
Out[70]: 1.5658631757819876

In [71]: p.memory_info()  #内存rss和vms信息
Out[71]: pmem(rss=45858816, vms=351375360)

In [72]: p.io_counters()  #进程的IO信息
Out[72]: pio(read_count=3107, write_count=3171, read_bytes=0, write_bytes=1400832)

In [73]: p.connections()  #socket连接数
Out[73]: []

In [74]: p.num_threads()  #开启的线程数
Out[74]: 2	

4.psutil 脚本

# 示例

import psutil
import datetime
import time

# 当前系统时间
print("-"*50)
now_time = time.strftime("%Y-%m-%d %H:%M:%S",time.localtime(time.time()))
print("当前时间:%s"%now_time)

# 查看CPU物理、逻辑个数
print("-"*50)
print("CPU物理个数为:%s"%psutil.cpu_count(logical=False))
print("CPU逻辑个数为:%s"%psutil.cpu_count())

# CPU利用率
cpu = psutil.cpu_percent(1)
print("CPU1秒使用率:%s%%"%cpu)

# 查看内存信息,剩余内存.free 总内存.total
print("-"*50)
free = round(psutil.virtual_memory().free /(1024.0*1024.0*1024.0),2)
total = round(psutil.virtual_memory().total /(1024.0*1024.0*1024.0),2)
memory = round((psutil.virtual_memory().total - psutil.virtual_memory().free) / psutil.virtual_memory().total,2)
print("剩余内存为:%s G"%free)
print("总内存为:%s G"%total)
print("内存利用率为:%s %%"%(memory*100))

# 查看缓存信息
print("缓存使用空间为:%s M"%round(psutil.virtual_memory().cached /(1024.0*1024.0),2))

# 查看磁盘信息
print("-"*50)
for i in psutil.disk_partitions():
    print("磁盘分区信息为:",i)

disk_total = round(psutil.disk_usage("/").total / (1024.0*1024.0*1024.0),2)
disk_used = round(psutil.disk_usage("/").used / (1024.0*1024.0*1024.0),2)
disk_free = round(psutil.disk_usage("/").free / (1024.0*1024.0*1024.0),2)
disk_use_ratio = round(psutil.disk_usage("/").used / psutil.disk_usage("/").total,2)
print("磁盘'/'分区总量为:%s G"%disk_total)
print("磁盘'/'分区使用量为:%s G"%disk_used)
print("磁盘'/'分区空闲量为:%s G"%disk_free)
# 查看磁盘读写
print("磁盘读取次数为:%s"%psutil.disk_io_counters().read_count)
print("磁盘写入次数为:%s"%psutil.disk_io_counters().write_count)
disk_read = round(psutil.disk_io_counters().read_bytes / (1024.0*1024.0),2)
print("磁盘读取大小为:%s M"%disk_read)
disk_write = round(psutil.disk_io_counters().write_bytes / (1024.0*1024.0),2)
print("磁盘写入大小为:%s M"%disk_write)

# 查看网络输入输出
print("-"*50)
net_sent = round(psutil.net_io_counters().bytes_sent / (1024.0*1024.0),2)
print("网络发送量为:%s M"%net_sent)
net_recv = round(psutil.net_io_counters().bytes_recv / (1024.0*1024.0),2)
print("网络接收量为:%s M"%net_recv)
net_packets_sent = psutil.net_io_counters().packets_sent
print("网络发包量为:%s个"%net_packets_sent)
net_packets_recv = psutil.net_io_counters().packets_recv
print("网络收包量为:%s个"%net_packets_recv)

# 查看登录用户信息
print("-"*50)
start_boot = datetime.datetime.fromtimestamp(psutil.boot_time()).strftime("%Y-%m-%d %H:%M:%S")
print("开机时间为:%s"%start_boot)
for j in psutil.users():
    print("登录用户信息:",j[0])
    print("登陆时间为:%s"%(datetime.datetime.fromtimestamp(j[3]).strftime("%Y-%m-%d %H:%M:%S")))

# 开启的线程数
print("-"*50)
print("当前进程名称:%s"%psutil.Process().name())
print("当前进程路径:%s"%psutil.Process().exe())
print("当前进程进入或启动路径:%s"%psutil.Process().cwd())
print("当前进程状态:%s"%psutil.Process().status())
process_create_time = datetime.datetime.fromtimestamp(psutil.Process().create_time()).strftime("%Y-%m-%d %H:%M:%S")
threads = psutil.Process().num_threads()
process_memory_percent = round(psutil.Process().memory_percent(),2)
print("当前进程占用内存利用率:%s"%process_memory_percent)
print("当前进程创建时间为:%s"%process_create_time)
print("开启的线程数为:%s"%threads)

print("-"*50)

5.测试系统硬件的小脚本(完整版).py

#coding:utf-8

import psutil
import datetime
import time

# 当前时间
now_time = time.strftime('%Y-%m-%d-%H:%M:%S', time.localtime(time.time()))
print(now_time)

# 查看cpu物理个数的信息
print(u"物理CPU个数: %s" % psutil.cpu_count(logical=False))

#CPU的使用率
cpu = (str(psutil.cpu_percent(1))) + '%'
print(u"cup使用率: %s" % cpu)

#查看内存信息,剩余内存.free  总共.total
#round()函数方法为返回浮点数x的四舍五入值。

free = str(round(psutil.virtual_memory().free / (1024.0 * 1024.0 * 1024.0), 2))
total = str(round(psutil.virtual_memory().total / (1024.0 * 1024.0 * 1024.0), 2))
memory = int(psutil.virtual_memory().total - psutil.virtual_memory().free) / float(psutil.virtual_memory().total)
print(u"物理内存: %s G" % total)
print(u"剩余物理内存: %s G" % free)
print(u"物理内存使用率: %s %%" % int(memory * 100))
# 系统启动时间
print(u"系统启动时间: %s" % datetime.datetime.fromtimestamp(psutil.boot_time()).strftime("%Y-%m-%d %H:%M:%S"))

# 系统用户
users_count = len(psutil.users())

users_list = ",".join([u.name for u in psutil.users()])
print(u"当前有%s个用户,分别是 %s" % (users_count, users_list))

#网卡,可以得到网卡属性,连接数,当前流量等信息
net = psutil.net_io_counters()
bytes_sent = '{0:.2f} Mb'.format(net.bytes_recv / 1024 / 1024)
bytes_rcvd = '{0:.2f} Mb'.format(net.bytes_sent / 1024 / 1024)
print(u"网卡接收流量 %s 网卡发送流量 %s" % (bytes_rcvd, bytes_sent))

io = psutil.disk_partitions()

print('-----------------------------磁盘信息---------------------------------------')

print("系统磁盘信息:" + str(io))

for i in io:
    o = psutil.disk_usage(i.mountpoint)
	
    print("总容量:" + str(int(o.total / (1024.0 * 1024.0 * 1024.0))) + "G")
    print("已用容量:" + str(int(o.used / (1024.0 * 1024.0 * 1024.0))) + "G")
    print("可用容量:" + str(int(o.free / (1024.0 * 1024.0 * 1024.0))) + "G")

print('-----------------------------进程信息-------------------------------------')
# 查看系统全部进程
for pnum in psutil.pids():
    p = psutil.Process(pnum)
    print(u"进程名 %-40s  内存利用率 %.2f%% \t 进程状态 %-10s 创建时间 %-10s " \
% (p.name(), p.memory_percent(), p.status(), datetime.datetime.fromtimestamp(p.create_time()).strftime("%Y-%m-%d %H:%M:%S")))

十九、模块

1.模块介绍

​ 在计算机程序的开发过程中,随着程序代码越写越多,在一个文件里代码就会越来越长,越来越不容易维护。
​ 为了编写可维护的代码,我们把很多函数分组,分别放到不同的文件里,这样, 每个文件包含的代码就相对较少,很多编程语言都采用这种组织代码的方式。在Python中,一个 .py 文件就称之为一个模块(module)。

优点:最大的好处是大大提高了代码的可维护性。其次,编写代码不必从零开始。当一个模块编写完毕,就可以被其他地方引用。我们在编写程序的时候,也经常引用其他模块,包括 Python 内置的模块和来自第三方的模块。

查看模块位置:
模块名.__ file __

[root@localhost ~]# ipython
In [1]: import os

In [2]: os.__file__
Out[2]: '/usr/local/lib/python3.7/os.py'

In [3]: import psutil

In [4]: psutil.__file__
Out[4]: '/usr/local/lib/python3.7/site-packages/psutil-3.2.1-py3.7-linux-x86_64.egg/psutil/__init__.py'

模块的意义:
模块就好比是工具包,要想使用这个工具包中的工具(就好比函数),就需要导入这个模块

模块的引用:模块名.函数名

[root@localhost ~]# cd /opt/datas/
[root@localhost datas]# vim sendmsg.py
def test1():
        print("--sendmsg--test1--")

def test2():
        print("--sendmsg--test2--")
[root@localhost datas]# vim main.py
import sendmsg

sendmsg.test1()
sendmsg.test2()
[root@localhost datas]# python3 main.py 
--sendmsg--test1--
--sendmsg--test2--
# 注意:sendmsg.py 与 main.py需要在一个目录内,否则调不到

引入方式的改变:
有时候我们只需要用到模块中的摸个函数,只需要引入该函数即可,此时可以用下面方法实现:
from 模块名 import 函数名1,函数名2...

导入某个模块的函数:
语法如下:
from modname import name1[,name2[, ... nameN]]

[root@localhost datas]# vim main.py
from sendmsg import test1,test2
test1()
test2()
[root@localhost datas]# python3 main.py 
--sendmsg--test1--
--sendmsg--test2--

'''
模块导入方式
第一种:
	import modname	#导入整个模块,包括了模块中的所有函数
第二种:
	from modname import 函数1,函数名2...		#导入某个模块中的某些函数
第三种:
	from modname import *		#导入某个模块的所有函数,不推荐使用	
'''

使用 as 起别名:

In [1]: import time as tt

In [2]: tt.sleep(3)		#睡眠三秒后进入下一步

模块搜索:
导入 sys 模块

In [5]: import sys

In [8]: sys.path
Out[8]: 
['/usr/local/bin',
 '/usr/local/lib/python37.zip',
 '/usr/local/lib/python3.7',
 '/usr/local/lib/python3.7/lib-dynload',
 '',		#代表当前工作目录
 '/usr/local/lib/python3.7/site-packages',
 '/usr/local/lib/python3.7/site-packages/psutil-3.2.1-py3.7-linux-x86_64.egg',
 '/usr/local/lib/python3.7/site-packages/IPython/extensions',
 '/root/.ipython']		#只有在ipython目录下才会加载这个目录

__ all __ :
如果一个文件中有 __ all __ 变量,那么也就意味着这个变量中的元素,会被 from xxx import * 导入
当有些功能你不想让别人用的时候就可以使用这种方式

[root@localhost datas]# vim test.py

# 全局变量
num = 100
def test1():
        print("test模块中的test1函数。。。")

def test2():
        print("test模块中的test2函数。。。")

class Person(object):
        age = 18
        
        def __init__(self,name):
                self.name = name

        def __str__(self):
                msg = "%s的年龄是:%d岁"%(self.name,Person.age)
                return msg

# 测试方法,在其他模块引入时不会影响其他模块的运行(此部分不会被其他模块引入)
if __name__ == '__main__':
        test1()
        test2()
        p = Person("张三")
        print(p)
        
[root@localhost datas]# python3 test.py 
test模块中的test1函数。。。
test模块中的test2函数。。。
张三的年龄是:18[root@localhost datas]# vim main2.py

from test import *

print(num)
test1()
test2()
p = Person("李四")
print(p)

[root@localhost datas]# python3 main2.py
100
test模块中的test1函数。。。
test模块中的test2函数。。。
李四的年龄是:18## 加入 __all__ 之后,就不会加载 test2() 了
__all__ = ['num','test1','Person']

# 全局变量
num = 100
def test1():
        print("test模块中的test1函数。。。")

def test2():
        print("test模块中的test2函数。。。")

class Person(object):
        age = 18
        def __init__(self,name):
                self.name = name

        def __str__(self):
                msg = "%s的年龄是:%d岁"%(self.name,Person.age)
                return msg

if __name__ == '__main__':
        test1()
        test2()
        p = Person("张三")
        print(p)
[root@localhost datas]# python3 main2.py
100
test模块中的test1函数。。。
Traceback (most recent call last):
  File "main2.py", line 5, in <module>
    test2()
NameError: name 'test2' is not defined
# 这里会报错,因为只会运行 __all__ 里面的函数,加上 test2() 即可正常运行。或注释掉调运 test2() 即可。

2.模块制作

定义自己的模块
在Python中,每个Python文件都可以作为一个模块,模块的名字就是文件的名字。比如有这样一个test.py,在test.py中定义了函数add。

[root@localhost datas]# vim test.py
def add(a,b):
        return a+b

def add2(a,b,opt):
        #a和b是两个数,opt是匿名函数
        result = opt(a,b)
        return result
#使用 __ name __ 测试模块,除了测试不会被其他模块调用
if __name__ == '__main__':
        a = add(100,200)
        print("a+b=%s"%a)

        result = add2(100,200,lambda x,y:x*y)
        print("使用匿名函数计算:100*200=%s"%result)
# 测试模块是否有错误
[root@localhost datas]# python3 test.py 
a+b=300
使用匿名函数计算:100*200=20000

调用自己的模块

[root@localhost datas]# vim main.py
import test
a = test.add(100,200)
print("a+b=%s"%a)

result = test.add2(100,200,lambda x,y:x*y)
print("使用匿名函数计算:100*200=%s"%result)
#或
#from test import *
#a = add(100,200)
#print("a+b=%s"%a)

#result = add2(100,200,lambda x,y:x*y)
#print("使用匿名函数计算:100*200=%s"%result)
[root@localhost datas]# python3 main.py 
a+b=300
使用匿名函数计算:100*200=20000

3.模块中的包

含义:
包将有联系的模块组织在一起,及放到同一个文件夹下,并且在这个文件夹创建一个名字为 __ init __ .py 文件,那么这个文件就称之为包。

__ all __ 在包中的作用:
在 __ init __ .py 文件中,定义一个 __ all __ 变量,他控制着 from 包名 import * 时导入的模块
将有联系的组织模块放在同一个文件夹下,并在这个文件夹下创建一个 __ init __ .py 的文件,这个文件夹就称之为包,并且包可以有效避免模块名冲突的问题

可以在 __ init __ .py 文件中编写内容,__ init __ .py 文件中的内容会默认先加载

[root@localhost datas]# mkdir day-13
[root@localhost datas]# cd day-13/
[root@localhost day-13]# touch __init__.py
[root@localhost day-13]# vim sendmsg.py
#发送短信
def sendmsg():
        print("正在发送短信。。。")
[root@localhost day-13]# vim recvmsg.py
#接收短信
def recvmsg():
        print("正在接受短信。。。")
[root@localhost day-13]# ls
__init__.py  recvmsg.py  sendmsg.py
[root@localhost day-13]# cd ..

# 没有写 __init__.py 文件时的导入方式
[root@localhost datas]# ipython
In [1]: import day_13.sendmsg

In [2]: day_13.sendmsg.sendmsg()
正在发送短信。。。

In [4]: import day_13.recvmsg

In [5]: day_13.recvmsg.recvmsg()
正在接受短信。。。

# 写了 __init__.py 文件时的导入方式
[root@localhost day_13]# vim __init__.py
__all__ = ['sendmsg','recvmsg']
[root@localhost day_13]# cd ..
[root@localhost datas]# ipython
In [3]: from day_13 import *

In [4]: recvmsg.recvmsg()
正在接受短信。。。

In [5]: sendmsg.sendmsg()
正在发送短信。。。

# __init__.py 文件还可以写一些初始化操作

4.嵌套的包

1. 目录结构

假定我们的包的例子有如下的目录结构:

Phone/
    __init__.py
    common_util.py
    Voicedta/
        __init__.py
        Pots.py
        Isdn.py
    Fax/
        __init__.py
        G3.py
    Mobile/
        __init__.py
        Analog.py
        igital.py
    Pager/
        __init__.py
        Numeric.py

2. 导入子包和使用模块

Phone 是最顶层的包,Voicedta 等是它的子包。 我们可以这样导入子包:

import Phone.Mobile.Analog 
Phone.Mobile.Analog.dial()

3. 你也可使用 from xxx import xxx 实现不同需求的导入

第一种方法是只导入顶层的子包,然后使用属性/点操作符向下引用子包树:

from Phone import Mobile
Mobile.Analog.dial('555-1212')

此外,我们可以还引用更多的子包:

from Phone.Mobile import Analog
Analog.dial('555-1212')

事实上,你可以一直沿子包的树状结构导入:

from Phone.Mobile.Analog import dial
dial('555-1212')

在我们上边的目录结构中,我们可以发现很多的 init.py 文件。这些是初始化模块,from-import 语句导入子包时需要用到它。 如果没有用到,他们可以是空文件。

4. 包同样支持 from xxx import *

包同样支持 from-import all 语句:

from package.module import *

然而,这样的语句会导入哪些文件取决于操作系统的文件系统。所以我们在__init__.py 中加入 all 变量。该变量包含执行这样的语句时应该导入的模块的名字。它由一个模块名字符串列表组成。

5.模块的安装及使用

打包模块

例如:模块目录结构如下

编辑 setup.py 文件,填写我们需要的信息以完成打包工作

生成发布压缩包

python setup.py sdist

# 下面的部分操作是建立上面目录结构环境的操作
[root@localhost ~]# cd /opt/datas/
[root@localhost datas]# cd day_13/
[root@localhost day_13]# ls
__init__.py  __pycache__  recvmsg.py  sendmsg.py
[root@localhost day_13]# mkdir msg
[root@localhost day_13]# mv __init__.py msg/
[root@localhost day_13]# mv recvmsg.py msg/
[root@localhost day_13]# mv sendmsg.py msg/
[root@localhost day_13]# cd msg/
[root@localhost msg]# ls
__init__.py  recvmsg.py  sendmsg.py
[root@localhost msg]# mkdir money/
[root@localhost msg]# cd money/
[root@localhost money]# touch __init__.py
[root@localhost money]# touch msgmoney.py
[root@localhost money]# ls
__init__.py  msgmoney.py
[root@localhost money]# cd ..
[root@localhost msg]# cd ..
[root@localhost day_13]# yum -y install tree
[root@localhost day_13]# tree msg/
msg/
├── __init__.py
├── money
│   ├── __init__.py
│   └── msgmoney.py
├── recvmsg.py
└── sendmsg.py
[root@localhost day_13]# touch setup.py
[root@localhost day_13]# vim setup.py
from distutils.core import setup
setup(name="hongfu_msg",version = "1.0",description = "宏福信息系统",author = "张三", py_modules = ['msg.sendmsg','msg.recvmsg','msg.money.msgmoney'])
[root@localhost day_13]# python3 setup.py build
[root@localhost day_13]# tree build/
build/
└── lib
    └── msg
        ├── __init__.py
        ├── money
        │   ├── __init__.py
        │   └── msgmoney.py
        ├── recvmsg.py
        └── sendmsg.py
# 使用 sdist 命令进行打包
[root@localhost day_13]# python3 setup.py sdist
[root@localhost day_13]# ls
build  dist  MANIFEST  msg  __pycache__  setup.py
# 已经生成可以发布的压缩包了
[root@localhost day_13]# ls dist/
hongfu_msg-1.0.tar.gz

模块安装使用

​ 1.解压 tar -xf xxx.tar.gz
​ 2.进入文件夹
​ 3.执行安装命令 sudo python setup.py install
​ 指定安装位置:python setup.py install --prefix = 路径

[root@localhost dist]# tar -xf hongfu_msg-1.0.tar.gz 
[root@localhost dist]# ls
hongfu_msg-1.0  hongfu_msg-1.0.tar.gz
[root@localhost dist]# cd hongfu_msg-1.0/
[root@localhost hongfu_msg-1.0]# python3 setup.py install
...
Writing /usr/local/lib/python3.7/site-packages/hongfu_msg-1.0-py3.7.egg-info
[root@localhost hongfu_msg-1.0]# cd /usr/local/lib/python3.7/site-packages/
[root@localhost site-packages]# ls
...
hongfu_msg-1.0-py3.7.egg-info
...

使用安装好的模块
在程序中,使用 from import 即可完成对安装的模块使用
from 模块名 import 模块名或者*

# 下面进入根目录进行安装测试
[root@localhost site-packages]# cd /
[root@localhost /]# ipython
In [2]: from msg import recvmsg,sendmsg

In [3]: recvmsg.recvmsg()
正在接受短信。。。

In [4]: import sys

In [5]: sys.path
Out[5]: 
['/usr/local/bin',
 '/usr/local/lib/python37.zip',
 '/usr/local/lib/python3.7',
 '/usr/local/lib/python3.7/lib-dynload',
 '',
 '/usr/local/lib/python3.7/site-packages',
 '/usr/local/lib/python3.7/site-packages/psutil-3.2.1-py3.7-linux-x86_64.egg',
 '/usr/local/lib/python3.7/site-packages/IPython/extensions',
 '/root/.ipython']

步骤总结:
1.编辑 setup.py 文件,在其中写入指定信息
2.运行命令生成可发布的压缩包
3.解压安装并使用

6.模块知识扩展

常用模块简介

​ Python有一套很有用的标准库(standard library)。标准库会随着Python解释器,一起安装在你的电脑中的。 它是Python的一个组成部分。这些标准库是Python为你准备好的利器,可以让编程事半功倍。

常用标准库

标准库说明
builtins内建函数默认加载
os操作系统接口
sysPython自身的运行环境
functools常用的工具
json编码和解码 JSON 对象
logging记录日志,调试
multiprocessing多进程
threading多线程
copy拷贝
time时间
datetime日期和时间
calendar日历
hashlib加密算法
random生成随机数
re字符串正则匹配
socket标准的 BSD Sockets API
shutil文件和目录管理
glob基于文件通配符搜索

更多标准库:https://docs.python/zh-cn/3.7/library/index.html

7.ansible

什么是ansible?

​ ansible是一种集成IT系统的配置管理、应用部署、执行特定任务的开源平台.它是基于python语言,由Paramiko和 PyYAML两个关键模块构建。集合了众多运维工具(puppet、cfengine、chef、func、fabric)的优点,实现了批量系统配置、批量程序部署、批量运行命令等功能。ansible是基于模块工作的,本身没有批量部署的能力。真正具有批量部署的是ansible所运行的模块,ansible只是提供一种框架。

ansible的优势

  • 部署简单,只需要在主控端部署Ansible环境,被控端无需做任何操作;
  • 默认使用SSH(Secure SHell)协议对设备进行管理;主从集中化管理;
  • 配置简单、功能强大、扩展性强;
  • 支持API及自定义模块,可通过Python轻松扩展;
  • 通过Playbooks来定制强大的配置、状态管理;
  • 对云计算平台、大数据都有很好的支持;
  • 提供一个功能强大、操作性强的Web管理界面和 REST API接口——AWX平台;
  • 幂等性:一种操作重复多次结果相同。

ansible 的安装和测试

1.epel源配置

$ wget -O /etc/yum.repos.d/epel.repo http://mirrors.aliyun/repo/epel-7.repo
$ yum -y install epel-release

2.安装ansible

$ yum -y install ansible

4.ansible配置

以三台机器为例,一台客户端,剩下两台服务器端
三台机器都得经过ssh配置,互相之间能够免密登录,即创建公钥,并将公钥发送给其他两台机器,三台机器均如此。一定要给自己也发送公钥。

5.在ansible的配置文件中添加主机信息,即可与目标主机进行通信。配置文件位置/etc/ansible/hosts,其中,[web][test]为主机组,可以批量控制主机组里面的所有主机,一个主机可以添加到多个组。

$ vim /etc/ansible/hosts
#末尾添加
[web]
192.168.221.111
192.168.221.182
[test]
192.168.221.182
192.168.221.183
[serverall]
192.168.221.111
192.168.221.182
192.168.221.183

#格式:
[机组名]
机组成员1
......
机组成员N

6.测试

# 查看用户组所有成员
# 格式:ansible 机组名 --list
$ ansible test --list
  hosts (2):
    192.168.221.182
    192.168.221.183
$ ansible web --list
  hosts (2):
    192.168.221.111
    192.168.221.182

#测试相互之间能否ping通,必须都是success。
$ ansible all -m ping

192.168.221.183 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": false, 
    "ping": "pong"
}
192.168.221.182 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": false, 
    "ping": "pong"
}
192.168.221.111 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": false, 
    "ping": "pong"
}

ansible 的模块使用

固定格式:ansible 机组名 -m 模块名 -a “需要内容”

1.远程命令模块
  • command :默认的模块,可以运行远程权限范围所有的shell命令

    $ ansible web -m command -a "free -m"
    
    192.168.221.182 | CHANGED | rc=0 >>
                 total       used       free     shared    buffers     cached
    Mem:           980        555        424          0         41        372
    -/+ buffers/cache:        141        838
    Swap:         2047          0       2047
    192.168.221.111 | CHANGED | rc=0 >>
                  total        used        free      shared  buff/cache   available
    Mem:           1819         367         685           9         766        1232
    Swap:          2047           0        2047
    
  • script:在远处主机上执行主控制端储存的shell脚本文件,相当于scp+shell组合

    #首先在主控端编辑一个shell脚本
    $ vim /root/hello.sh
    
    #!/bin/bash
    echo "12 34"
    
    #让web机组的机器远程执行脚本
    $ ansible web -m script -a "/root/hello.sh"
    192.168.221.182 | CHANGED => {
        "changed": true, 
        "rc": 0, 
        "stderr": "Shared connection to 192.168.221.182 closed.\r\n", 
        "stderr_lines": [
            "Shared connection to 192.168.221.182 closed."
        ], 
        "stdout": "12 34\r\n", 
        "stdout_lines": [
            "12 34"
        ]
    }
    192.168.221.111 | CHANGED => {
        "changed": true, 
        "rc": 0, 
        "stderr": "Shared connection to 192.168.221.111 closed.\r\n", 
        "stderr_lines": [
            "Shared connection to 192.168.221.111 closed."
        ], 
        "stdout": "12 34\r\n", 
        "stdout_lines": [
            "12 34"
        ]
    }
    
  • shell:执行远程主机的shell 脚本文件

    # 在非主控端的机器上有一个shell文件
    # 这个文件必须有执行权限!
    $ cat /root/day.sh 
    #!/bin/bash
    echo "今天是星期二。"
    
    #在主控端执行
    $ ansible web -m shell -a "/root/day.sh"
    
    192.168.221.182 | CHANGED | rc=0 >>
    今天是星期二。
    192.168.221.111 | FAILED | rc=127 >>
    /bin/sh: /root/day.sh: 没有那个文件或目录non-zero return code
    #因为机组中只有其中一台有那个文件,所以另一台执行失败。
    
2.copy模块

实现主控制端向目标拷贝文件.类似于scp

#src:拷贝的文件	dest:拷贝的目的地/重命名	owner:文件所有者	group:所属组	mode:权限	
#这里是将/etc/fstab文件拷贝到web组所有机器的根目录并改名为tmpl,所有者所属组都是root,权限744
$ ansible web -m copy -a "src=/etc/fstab dest=/tmpl owner=root group=root mode=744"

192.168.221.182 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": true, 
    "checksum": "2f4d7044171f0883db877d30c31c2a5520c13e70", 
    "dest": "/tmpl", 
    "gid": 0, 
    "group": "root", 
    "md5sum": "c18021506f50e8d3ed5f278429a9a3c5", 
    "mode": "0744", 
    "owner": "root", 
    "size": 465, 
    "src": "/root/.ansible/tmp/ansible-tmp-1614685678.63-8817-61711050372025/source", 
    "state": "file", 
    "uid": 0
}
192.168.221.111 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": true, 
    "checksum": "2f4d7044171f0883db877d30c31c2a5520c13e70", 
    "dest": "/tmpl", 
    "gid": 0, 
    "group": "root", 
    "md5sum": "c18021506f50e8d3ed5f278429a9a3c5", 
    "mode": "0744", 
    "owner": "root", 
    "size": 465, 
    "src": "/root/.ansible/tmp/ansible-tmp-1614685685.68-8815-222396985623330/source", 
    "state": "file", 
    "uid": 0
}
3.stat模块

获取远程文件状态信息,如 atime,md5,uid等

#path:获取哪个文件状态信息
$ ansible web -m stat -a "path=/etc/hosts"
4.get_url模块

实现远程主机下载指定的URL到本地,支持sha256sum校验和

#url:下载哪个页面	dest:下载位置以及命名 mode:权限 force:如果中断是否继续
$ ansible web -m get_url -a "url=https://www.baidu/ dest=/tmp/index.html mode=440 force=yes"
5.yum模块

Linux平台软件包管理模块:安装、卸载

$ ansible web -m yum -a "name=curl state=latest"
6.cron模块

远程主机的计划任务配置

#每周246的每分钟执行job声明的任务,任务名为name声明的。
$ ansible web -m cron -a 'minute=* weekday=2,4,6 job="/usr/bin/wall FBI WARNING" name=warningcron'

取消

$ ansible all -m cron -a "name=warningcron state=absent"

禁用

$ ansible all -m cron -a 'disabled=true job="/usr/bin/wall FBI WARNING" name=warningcron'

启用

ansible all -m cron -a 'disabled=false job="/usr/bin/wall FBl WARNING" name=warningcron
7.mount模块

远程主机挂载

$ ansible web -m mount -a “name=/mnt/data dest=/dev/sda1 fstpe=ext4 opts=ro state=present"
8.service模块

远程主机系统服务管理

$ ansible web -m service -a "name=httpd state=restarted"

ansible官网

https://docs.ansible/ansible/latest/user_guide/intro.html

8.自定义监控网站

python实现自动监控网站并发送邮件告警
通过定时执行python脚本,可以实现定期批量访问网站,如果发现网站打不开,第一时间发邮件到管理员邮箱进行预警

这里用的版本是python3.7
需要安装的插件:smtplib:发邮件需要用到
安装方法:pip3 install PyEmail
测试:进入python3
import smtplib 看是否能导入
pycurl:访问网站时会需要用到

1.模块安装

1.下载 :wget https://curl.haxx.se/download/curl-7.72.0.tar.gz
2.解压 :tar -zxf curl-7.72.0.tar.gz -C /opt/modules/
3.进入 :cd /opt/modules/curl-7.72.0/
4.编译 :./configure
5.安装 :make && make install
6.安装pycurl : pip3 install pycurl

2.实现的具体思路

​ python程序从txt里面批量读取到网站的信息,通过Curl.py模拟浏览器去访问网站,并且把访问的结果写入到以自己的网站名称-日期.txt格式的文件中记录;有几种情况:
1、如果发现打不开了,直接发邮件提示网站已经打不开
2、发现可以打开,读取文件中上一次访问的情况(读取txt文件最后一行)
​ 1)如果发现上一次是打不开的,发邮件提醒网站已经恢复了
​ 2)如果发现上一次是打得开的(200的返回码),只是记录网站访问的日志就可以了

3.实现自动监控并发送

主要用到的有三个模块:
Email.py 邮件类,主要用来发邮件的时候调用。
Curl.py 主要用来执行模拟浏览器访问网站并返回结果的文件
site_moniter.py 这个文件为主程序,主要执行调用上面的函数,读取txt文件中的网站清单,如果网站打不开就发邮件出来告警。

Email.py

#-*- coding:utf-8 -*-
import sys
import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
class Email_send(object):
    def __init__(self,msgTo,data2,Subject):
        self.msgTo=msgTo
        self.data2=data2
        self.Subject=Subject
    def sendEmail(self):
       # (p_w_upload,html) = content
        msg = MIMEMultipart()
        msg['Subject'] = self.Subject
        msg['From'] = '1512267052@163' #改成自己的邮箱
        msg['To'] = self.msgTo
        html_att = MIMEText(self.data2, 'html', 'utf-8')
        #att = MIMEText(p_w_upload, 'plain', 'utf-8')
        msg.attach(html_att)
        #msg.attach(att)
        try:
            smtp = smtplib.SMTP()
            smtp.connect('smtp.163', 25)
            smtp.login(msg['From'], 'JCWKMUACLL') #改成自己的邮箱密码
            smtp.sendmail(msg['From'], msg['To'].split(','), msg.as_string())
            return('邮件发送成功')
        except Exception as e:
            print('--------------sss------',e)
    def curl(self):
        import pycurl
        c=pycurl.Curl()
        #url="www.luoan"
        #indexfile=open(os.path.dirname(os.path.realpath(__file__))+"/content.txt","wb")
        c.setopt(c.URL,url)
        c.setopt(c.VERBOSE,1)
        c.setopt(c.ENCODING,"gzip")
        #模拟火狐浏览器
        c.setopt(c.USERAGENT,"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:86.0) Gecko/20100101 Firefox/86.0")
        return c

Curl.py

#-*- coding:utf-8 -*-
import sys
import pycurl
class Curl(object):
    def __init__(self,url):
        self.url=url
    def Curl_site(self):
        c=pycurl.Curl()
        url="http://59.110.161.57:550/"		#要访问的网站
        #indexfile=open(os.path.dirname(os.path.realpath(__file__))+"/content.txt","wb")
        c.setopt(c.URL,self.url)
        c.setopt(c.VERBOSE,1)
        c.setopt(c.ENCODING,"gzip")
        #ģ���������
        c.setopt(c.USERAGENT,"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:86.0) Gecko/20100101 Firefox/86.0")
        return c

site_moniter.py

#-*- coding:utf-8 -*-
import pycurl
import os
import sys
import linecache
import io  #解决encoding错误问题
import time  #引入事件类,用来获取系统当前时间
#from ceshi import Student
from Email import Email_send
from Curl import Curl

#bart = Student('aaron',59)
#bart.print_score()

def script(urls,type):
    msgTo = '1512267052@163'		#自己的邮箱
    now_time=time.strftime("%Y-%m-%d %H:%M:%S",time.localtime(time.time()))
    j=1
#       data2=[{'aa':'aa'}]
    for url_split in urls:
        #print(url_split)
        url_1=url_split.split('---')
        url=url_1[0]
        recovery_title = "监控通知----%s url:%s" % (url_1[0], url) + "在" + time.strftime("%Y-%m-%d %H:%M:%S",time.localtime(time.time())) + "已经恢复"
        down_title = "监控通知----%s url:%s" % (url_1[0], url) + "在" + time.strftime("%Y-%m-%d %H:%M:%S",time.localtime(time.time())) + "无法打开"
       
        #引用爬去网站的类,调用结果
        url_result = Curl(url)
        c = url_result.Curl_site()
        try:
            c.perform()
            code = str(c.getinfo(c.HTTP_CODE))
            print(code+'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa')
        except Exception as e:
            print('--------错误信息:--------',e)
            #indexfile.close()
            #c.close()
        code = str(c.getinfo(c.HTTP_CODE))
        
        filename = '%s-%s.txt' % (url_1[0], time.strftime("%Y-%m-%d", time.localtime(time.time())))
        #判断如果在网站无法打开的情况下
        if code == '0' or code=='400' or code=='500' or code=='404':
            resolveTime = 0
            Connection_Time = 0
            Transfer_Total_Time = 0
            Total_Time = 0
            
            data3 = '网站:%s无法打开%s' % (url_1[0], url)
            # indexfile.close()
            # c.close()
            #判断网站如果挂了就发邮件
            stat3 = Email_send(msgTo, data3, down_title)
            resole=stat3.sendEmail()
            #print(resole)
            #print(data3 + '邮件已经发送')
        else:
           
            print('网站可以正常打开')
            #f = open(filename, 'a',encoding='utf-8')
            file_exit=os.path.exists(filename)
            #print(file_exit)
            #判断这个日志文件存不存在
            if(file_exit):
                #读取文件最后一行,为了读取出来最后一次的状态值
                file = io.open(filename, 'r',encoding='utf-8')
                linecount = len(file.readlines())
                data = linecache.getline(filename, linecount)
                file.close
                if data == '':
                    print('这是'+data+'为空的数据')
                else:
                    print('其他信息%s'%(data))
                    explode = data.split('----')
                    #判断如果读取出来的值,最后一次是异常的情况就告警
                    if explode[3]=='0\n' or explode[3]=='400\n' or explode[3]=='500' or explode[3]=='404':
                        data3 = '网站:%s在%s已经恢复%s' % (url_1[0], now_time,url)
                        stat3 = Email_send(msgTo, data3, recovery_title)
                        resole = stat3.sendEmail()
                        print(resole)
                        print(data3 + '邮件已经发送')
                    else:
                        print('最后一次记录为其他值:%s'%(explode[3])+'-----')
            else:
				
                print('文件不存在')
        data2 = '\n' + url_1[0] + '----' + url + '-----' + time.strftime("%H:%M:%S", time.localtime(time.time())) + '-------' + code
        print('data2数据写入成功:' + data2)
        file = io.open(filename, 'a', encoding='utf-8')
        file.write(data2)
        file.close
#  bart = Student(data2,59)
#  bart.print_score()

if __name__ == "__main__":
    type = "监控通知-测试" + time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(time.time()))
    data1=['公司门户---http://59.110.161.57:550/','公司平台---http://59.110.161.57:550/n']
    #script(data1,type)


  #中心层面的网站清单
    file=io.open('/opt/datas/zhongxin.txt')
    data2=['公司门户---http://59.110.161.57:550/','公司平台---http://59.110.161.57:550/n'] #修改网站
    while 1:
        line2 =file.readline()
        print(line2)
        if not line2:
            break
        data2.append(line2[0:-1])
    data2=['http://59.110.161.57:550/','http://59.110.161.57:550/','www.qq']	#修改网站
    print(data2)
    title="监控通知-中心"+ time.strftime("%Y-%m-%d %H:%M:%S",time.localtime(time.time()))
    script(data2,title)

更多推荐

python 基础