本文共 129255 字,大约阅读时间需要 430 分钟。
1、+ 先拼接,再输出
2、,分次输出,用分隔符分开 3、sep设置分隔符 4、end关键字用于输出完成后追加的字符,默认 \n>>> a="hello" + "world!">>> a'helloworld!'>>> a="hello ">>> b=a+"world!">>> b'hello world!'
函数input():输入内容默认转为字符串
>>> input()12'12'
创建名为login.py的程序文件,提示用户输入用户名,用户输入用户名后,打印欢迎用户
[root@room9pc01 nsd2019]# vim login.py#!/usr/local/bin/python3username=input("localhost login:")print("welcome to",username)#!/usr/local/bin/python3username=input('localhost login:')print('-'*20)print("|welcome|",username)print('-'*20)
工厂函数 int float bool str list tuple dict
数字运算:+ - * / //(取整) %(取余)**()>>> 6+39>>> 3 + -21>>> 8-9-1>>> 2*918>>> 8/42.0>>> 9//33>>> 5%32>>> 2**664>>> 2+3*10/2**35.75>>> (2+3)*10/2**36.25比较运算>>> 1 > 5False>>> 6 >= 4True>>> 6< 10True>>> 5!=9True>>> 8== 8True
逻辑判断
>>> 1==1 and 2!=3True>>> 1<2 and 5>9False>>> 1<2 or 5>9True>>> 1 and 00>>> 0 or 11>>> 1< 4 or 1True>>> 1> 4 and 1False
int:有符号整数
bool:布尔值(True:1,False:0) float:浮点数 complex:复数>>> int(66.66)66>>> float(88)88.0>>> bool(3)True>>> bool(0)False>>> bool(-9)True>>> complex(1+5j)**2(-24+10j)
• python中字符串被定义为引号之间的字符集合
• python支持使用成对的单引号或双引号 • 无论单引号,还是双引号,表示的意义相同 • python还支持三引号(三个连续的单引号或者双引号),可以用来包含特殊字符 • python不区分字符和字符串• 使用索引运算符[ ]和切片运算符[ : ]可得到子字符串
• 第一个字符的索引是0,最后一个字符的索引是-1 • 子字符串包含切片中的起始下标,但不包含结束下标起始位置<=字符串获取范围<终止位置
要显示的字符串长度=终止位置 - 起始位置 py_str[起始位置:终止位置:步长]>>> a = "python 3.6.5">>> a'python 3.6.5'>>> a[6]' '>>> a[7]'3'>>> a[-5]'3'>>> a[2:4]'th'>>> a[2:]'thon 3.6.5'>>> a[:5]'pytho'>>> a="http://www.baidu.com/index.html">>> a[7:20]'www.baidu.com'>>> a[7:-11]'www.baidu.com'>>> a[20:31]'/index.html'>>> a[-11:-1]'/index.htm'>>> a[-11:] //负向默认索引,起始位置到结尾'/index.html'>>> a[:7]'http://' //正向默认索引,初始位置到终止位置>>> a[-24:-11]'www.baidu.com'>>> b="123456789">>> b[::2]'13579'>>> b[1::2]'2468'
列表:
• 可以将列表当成普通的“数组”,它能保存任意数量 任意类型的python对象 • 像字符串一样,列表也支持下标和切片操作 • 列表中的项目可以改变 1、子对象获取使用切片语法 py_list[index] py_list[起始位置:终止位置:步长] 2、添加对象到列表 py_list.append(新对象) py_list+=[新对象] 3、列表是可以改变的 py_list[index]=新对象 列表操作1、切片
>>> alist=[1, 2, 3, 4, 5, "python", "aa", "bb","cc"]>>> alist[5]'python'>>> alist[7:]['bb', 'cc']>>> alist[6:]['aa', 'bb', 'cc']>>> alist[5][-2:] //嵌套切片,先取出字符串python,再取出最后两个字符'on'
2、修改
>>> alist=["hello", "world"]>>> alist['hello', 'world']>>> alist[1]'world'>>> alist[1]="nsd1812">>> alist['hello', 'nsd1812']
3、追加
>>> tea=["犇犇"]>>> tea.append("丁丁")>>> tea['犇犇', '丁丁']>>> tea+["晶晶"]['犇犇', '丁丁', '晶晶']>>> tea += ["静静"]>>> tea['犇犇', '丁丁', '静静']
4、判断
>>> '静静' in teaTrue>>> '星星' in teaFalse>>> '凯凯' not in teaTrue>>> teacher = ['犇犇', '丁丁', '静静']>>> teacher['犇犇', '丁丁', '静静']>>> id(teacher)140041257956552>>> teacher += ["ooxx"]>>> teacher['犇犇', '丁丁', '静静', 'ooxx']>>> id(teacher)140041257956552 //id值不变
• 可以认为元组是“静态”的列表
• 元组一旦定义,不能改变元组使用()
元组里所有度操作和列表一样 元组是不可变对象 py_tuple[index]=新对象 ,报错>>> tea = ('小爱', '依依', '静静')>>> tea('小爱', '依依', '静静')>>> tea[1:2]('依依',)>>> tea += ("轻轻",)>>> tea('小爱', '依依', '静静', '轻轻')>>> id(tea)140041258298728>>> tea += ("ooxx",)>>> tea('小爱', '依依', '静静', '轻轻', 'ooxx')>>> id(tea)140041257940336 //id值发生变化,表明生成了新的元组
• 字典是由键-值(key-value)对构成的映射数据类型
• 通过键取值,不支持下标操作字典使用{}
字典是key:value 键值对格式,可变对象 字典不支持下标和切片语法 字典调用直接使用py_dict[‘key’]>>> dict= {"name":"bob", "age":23}>>> dict{'name': 'bob', 'age': 23}>>> "bob" in dictFalse>>> "name" in dictTrue>>> dict['name']'bob'
数据类型比较
• 按存储模型分类 – 标量类型:数值、字符串 – 容器类型:列表、元组、字典 • 按更新模型分类: – 可变类型:列表、字典 – 不可变类型:数字、字符串、元组 • 按访问模型分类 – 直接访问:数字 – 顺序访问:字符串、列表、元组 – 映射访问:字典>>> a='123456789'>>> a[::-1] //反向输出'987654321'>>> a[-1::-1]'987654321'
标准if条件语句的语法
if expression if_suite else else_suite • 如果表达式的值非0或者为布尔值True, 则代码组 if_suite被执行;否则就去执行else_suite • 代码组是一个python术语,它由一条或多条语句组 成,表示一个子代码块• 扩展if语句结构
if expression1: if_suiteelif expression2: elif_suiteelse: else_suite
练习01
#!/usr/local/bin/python3default_username='root'default_password='123456'username=input('localhost login:') #输入用户名if username==default_username: #判断用户名是否正确 password=input('password:') #用户名正确则输入密码 if password==default_password: #判断密码是否正确 print('welcome ',username,'!') #密码正确则输出欢迎 exit else: print('password error!') #密码错误则输出错误提示信息 exitelse: print('username error!') #用户名错误则输出错误提示信息 exit练习02#!/usr/local/bin/python3x = input('id:')print( 'yes' if int(x) > 55 else "no")练习03#!/usr/local/bin/python3num=input('grade:')x=int(num)if x > 90: print('优秀')elif x > 80: print('好')elif x > 70: print('良')elif x > 60: print('及格')else: print('你要努力了') exit练习04#!/usr/local/bin/python3#石头剪刀布游戏import randomalist = ['石头', '剪刀', '布']x = random.randint(0,2)computer = alist[x]print(''' 0:石头 1:剪刀 2:布''')y = input('guest number[0-2]')y = int(y)if y >= 0 and y <= 2: guest = alist[y] if guest == computer: print('平局!') elif guest== '石头' and computer == '剪刀': print('你赢了') elif guest== '石头' and computer == '布': print('你输了') elif guest== '剪刀' and computer == '石头': print('你输了') elif guest== '剪刀' and computer == '布': print('你赢了') elif guest== '布' and computer == '剪刀': print('你输了') elif guest== '布' and computer == '石头': print('你赢了')else: print('你犯规了...')#!/usr/local/bin/python3#石头剪刀布游戏import randomalist = ['石头', '剪刀', '布']x = random.randint(0,2)blist = [(0,1), (1,2), (2,0)]print(''' 0:石头 1:剪刀 2:布''')y = input('guest number[0-2]')y = int(y)if y >= 0 and y <= 2: res= '你赢了'if (y,x) in blist else '你输了' print(res)else: print('你犯规了...')
• 一组被重复执行的语句称之为循环体,能否继续重复,
决定循环的终止条件 • Python中的循环有while循环和for循环 • 循环次数未知的情况下,建议采用while循环 • 循环次数可以预知的情况下,建议采用for循环while结构
while循环语法结构 • 当需要语句不断的重复执行时,可以使用while循环 while expression: while_suite • 语句while_suite会被连续不断的循环执行,直到表达 式的值变成0或False练习01#!/usr/local/bin/python3#计算1—100以内所有整数的和i,count = 1, 0while i <= 100: count += i i += 1print(count)练习02#!/usr/local/bin/python3#计算1—100以内所有偶数的和sum = 0i = 0while i <= 100: i+= 1 if i % 2: continue sum += iprint('result is %d'%sum)练习03#!/usr/local/bin/python3#计算1--100以内所有奇数的和sum = 0i = 0while i <= 99: i+= 1 if not i % 2: continue sum += iprint('result is %d'%sum)练习03#!/usr/local/bin/python3default_username='root'default_password='123456'while True: //判断永远为真,直到用户输入正确为止 username=input('localhost login:') #输入用户名 password=input('password:') #输入密码 if username == default_username and password == default_password: #判断用户名和密码是否输入正确 print('welcome', username, '!') //正确则打印欢迎 break //结束循环 else: print('输入错误!') //否则提示输出错误练习04#!/usr/local/bin/python3import randoma = random.randint(0,100)num = 1while num <= 5: user = input('请猜一个数:') b = int(user) if a == b: print('猜对了!') break else: num += 1 print('猜错了,请重新猜!')print('正确答案是', a)练习05#!/usr/local/bin/python3#石头剪刀布游戏,三局两胜制import randomgamelist= ['石头', '剪刀', '布']winlist= [(0,2), (1,0), (2,1)]xcount= 0ycount= 0while True: print(''' 0:石头 1:剪刀 2:布 ''') x = random.randint(0,2) y = input('guest number[0-2]:') y = int(y) if y >= 0 and y <= 2: if x == y: print('平局', end= '\n\n') continue elif (x,y) in winlist: ycount += 1 else: xcount += 1 print('电脑:', gamelist[x],' ', '用户:', gamelist[y]) if xcount == 2: print('你输了') break elif ycount == 2: print('你赢了') break else: print('你犯规了...')
• python中的for接受可迭代对象(例如序列或迭代器)作为其参数,每次迭代其中一个元素
语法结构: for iter_var in iterable: suite_to_repeat • 与while循环一样,支持break、continue、else语句 • 一般情况下,循环次数未知采用while循环,循环次数已知,采用for循环range函数
• for循环常与range函数一起使用 • range函数提供循环条件 • range函数的完整语法为: range(start, end, step =1)练习01#!/usr/local/bin/python3#斐波那契数列就是某一个数,总是前两个数之和,比如0,1,1,2,3,5,8#生成指定长度的斐波拉契数列alist = [0, 1]n = input('num: ')n = int(n)-2for i in range(n): x = alist[-1]+ alist[-2] alist.append(x)print(alist)练习02#!/usr/local/bin/python3#打印九九乘法表for x in range(1, 10): for y in range(1, x+ 1): print(str(x)+'x'+str(y)+'=',x*y, end= '\t') else: print() //print()函数自带换行
它是一个非常有用、简单、而且灵活的工具,可以用来动态地创建列表
• 语法: [expr for iter_var in iterable] • 这个语句的核心是for循环,它迭代iterable对象的所有条目 • expr应用于序列的每个成员,最后的结果值是该表达式产生的列表>>> alist= [1, 2, 3, 9]>>> blist=[]>>> for i in alist:... blist.append(i**2)... >>> blist[1, 4, 9, 81]>>> [i**2 for i in alist][1, 4, 9, 81]>>> alist=[1, 2, 3, 7]>>> blist=[i**2 for i in alist]>>> blist[1, 4, 9, 49]>>> [5+i for i in range(5)][5, 6, 7, 8, 9]>>> [5+i for i in range(10) if i%2==0][5, 7, 9, 11, 13]>>> ['192.168.1.'+ str(i) for i in range(255)]['192.168.1.0', '192.168.1.1',..]**************>> TrueTrue>>> bool()False>>> bool(' ')True
• 作为打开文件之门的“钥匙”,内建函数open() 提供了初始化输入/输出(I/O)操作的通用接口
• 成功打开文件后时候会返回一个文件对象,否则引发一个错误 • 基本语法: file_object =open(file_name, access_mode=‘r’, buffering=-1)r 以读方式打开(文件不存在则报错)
w 以写方式打开(文件存在则清空,不存在则创建) a 以追加模式打开(必要时创建新文件) r+ 以读写模式打开(参见r) w+ 以读写模式打开(参见w) a+ 以读写模式打开(参见a) b 以二进制模式打开read内建函数
• read()方法用来直接读取字节到字符串中,最多读取给定数目个字节 • 如果没有给定size参数(默认值为-1)或者size值为负,文件将被读取直至末尾readline内建函数
• 读取打开文件的一行(读取下个行结束符之前的所有字节) • 然后整行,包括行结束符,作为字符串返回 • 它也有一个可选的size参数,默认为-1,代表读至行结束符 • 如果提供了该参数,那么在超过size个字节后会返回不完整的行 • readlines()方法读取所有(剩余的)行然后把它们作为一个字符串列表返回>>> data = fobj.read()>>> print(data)>>> data = fobj.readline()>>> print(data)>>> data = fobj.readlines()>>> print(data)
write方法
• write()内建方法功能与read()和readline()相反。它 把含有文本数据或二进制数据块的字符串写入到文件中去 • 写入文件时,不会自动添加行结束标志,需要程序员手工输入>>> fobj.write('Hello World!\n')13
函数基本概念
• 函数是对程序逻辑进行结构化或过程化的一种编程方法 • 将整块代码巧妙地隔离成易于管理的小块 • 把重复代码放到函数中而不是进行大量的拷贝,这样既能节省空间,也有助于保持一致性 • 通常函数都是用于实现某一种功能创建函数
• 函数是用def语句来创建的,语法如下:def function_name(arguments): "function_documentation_string" function_body_suite
• 标题行由def关键字,函数的名字,以及参数的集合
(如果有的话)组成 • def子句的剩余部分包括了一个虽然可选但是强烈推荐的文档字串,和必需的函数体 定义函数时,使用return返回结果>>> def func(i):... x = i**2... return x //调用函数需要使用return返回结果... >>> func(4)16>>> func(15)225>>> x = func(15)>>> x225>>> def test(i):... if i%2 == 0:... return True //返回真... else:... return False //返回假>>> if test(10):... print('ok')... ok>>> if test(13):... print('ok')... else:... print('no')... no
位置参数
• 与shell脚本类似,程序名以及参数都以位置参数的方式传递给python程序 • 使用sys模块的argv列表接收(sys.argv[0]为程序名本身,sys.argv[n]为第n个位置参数)[root@zzghost1 day02]# vim args.py#!/usr/bin/env python3import sysprint sys.argv[root@zzghost1 day02]# ./args.py hello world['./args.py', 'hello', 'world']
#!/usr/local/bin/python3#复制文件程序import sysdef copy(src_file,dest_file): #定义复制函数 src_fb = open(src_file, 'rb') #以只读二进制方式打开源文件 dest_fb = open(dest_file, 'wb') #以写入二进制方式打开目标文件 while True: data = src_fb.read(1048576) #读取源文件内容 if data: #判断文件是否为空 dest_fb.write(data) #不为空则写入读取的数据到目标文件 else: break #为空则退出while循环 src_fb.close() #关闭打开的源文件 dest_fb.close() #关闭打开的目标文件#maincopy(sys.argv[1],sys.argv[2]) #调用函数,传递参数[root@room9pc01 exercise]# ./copy_sys.py /root/ls /opt/ls //执行程序[root@room9pc01 exercise]# md5sum /root/ls /opt/ls //校验源文件与目标文件一致性918cb545b3458e1bf18b712b36af304f /root/ls918cb545b3458e1bf18b712b36af304f /opt/ls
#!/usr/local/bin/python3#打印九九乘法表def mtable(n): for i in range(1, n+1): print('%dx%d=%d' % (i,n,i*n), end='\t')for x in range(1, 10): mtable(x) print('')[root@room9pc01 exercise]# ./9x9_sys.py1x1=1 1x2=2 2x2=4 1x3=3 2x3=6 3x3=9 1x4=4 2x4=8 3x4=12 4x4=16 1x5=5 2x5=10 3x5=15 4x5=20 5x5=25 1x6=6 2x6=12 3x6=18 4x6=24 5x6=30 6x6=36 1x7=7 2x7=14 3x7=21 4x7=28 5x7=35 6x7=42 7x7=49 1x8=8 2x8=16 3x8=24 4x8=32 5x8=40 6x8=48 7x8=56 8x8=64 1x9=9 2x9=18 3x9=27 4x9=36 5x9=45 6x9=54 7x9=63 8x9=72 9x9=81
#!/usr/local/bin/python3#用户登录程序def login_check(username,password): #定义检查登录函数 default_username= 'root' default_password= '123456' if username == default_username and password == default_password: return True #用户名和密码正确则返回为真while True: username=input('localhost login:') #输入用户名 password=input('password:') #输入密码 if login_check(username,password): #调用检查登录函数 print('welcome', username, '!') #函数返回值为真则打印欢迎 break #结束循环 else: print('输入错误!') #否则提示输出错误,进入下一次输入
• 模块是从逻辑上组织python代码的形式
• 当代码量变得相当大的时候,最好把代码分成一些有组织的代码段,前提是保证它们的彼此交互 • 这些代码片段相互间有一定的联系,可能是一个包含数据成员和方法的类,也可能是一组相关但彼此独立的操作函数创建模块
• 模块物理层面上组织模块的方法是文件,每一个以.py作为结尾的python文件都是一个模块 • 模块名称切记不要与系统中已存在的模块重名 • 模块文件名字去掉后面的扩展名(.py)即为模块名导入模块(import)
• 使用import导入模块 • 模块属性通过“模块名.属性”的方法调用 • 如果仅需要模块中的某些属性,也可以单独导入>>> import sys>>> import os, string>>> string.digits'0123456789'>>> from random import randint>>> randint(1, 10)3
[root@room9pc01 exercise]# vim cat_sys.py //定义模块#!/usr/local/bin/python3#查看文件程序#import sysdef cat(file): f1 = open(file,'r') for eachLine in f1: print(eachLine,end='')#main#cat(sys.argv[1])>>> import cat_sys //导入模块>>> cat_sys.cat('login_def.py') //引用模块内属性#!/usr/local/bin/python3#用户登录程序def login_check(username,password): #定义检查登录函数 default_username= 'root' default_password= '123456' if username == default_username and password == default_password: return True #用户名和密码正确则返回为真while True: username=input('localhost login:') #输入用户名 password=input('password:') #输入密码 if login_check(username,password): #调用检查登录函数 print('welcome', username, '!') #函数返回值为真则打印欢迎 break #结束循环 else: print('输入错误!') #否则提示输出错误,进入下一次输入------------------------------------------------------------------------------------------->>> from cat_sys import cat //单独导入模块内属性>>> cat('login_def.py') //直接调用模块内属性#!/usr/local/bin/python3#用户登录程序def login_check(username,password): #定义检查登录函数 default_username= 'root' default_password= '123456' if username == default_username and password == default_password: return True #用户名和密码正确则返回为真while True: username=input('localhost login:') #输入用户名 password=input('password:') #输入密码 if login_check(username,password): #调用检查登录函数 print('welcome', username, '!') #函数返回值为真则打印欢迎 break #结束循环 else: print('输入错误!') #否则提示输出错误,进入下一次输入
默认参数
>>> def moren(num=30):... print('8'*num)... >>> moren()888888888888888888888888888888>>> moren(60)888888888888888888888888888888888888888888888888888888888888
文件迭代
• 如果需要逐行处理文件,可以结合for循环迭代文件 • 迭代文件的方法与处理其他序列类型的数据类似>>> fobj = open('star.py')>>> for eachLine in fobj:... print(eachLine, end= '')
>> f1= open('login_def.py', 'r')>>> for eachLine in f1:... print(eachLine, end='')... #!/usr/local/bin/python3#用户登录程序def login_check(username,password): #定义检查登录函数 default_username= 'root' default_password= '123456' if username == default_username and password == default_password: return True #用户名和密码正确则返回为真while True: username=input('localhost login:') #输入用户名 password=input('password:') #输入密码 if login_check(username,password): #调用检查登录函数 print('welcome', username, '!') #函数返回值为真则打印欢迎 break #结束循环 else: print('输入错误!') #否则提示输出错误,进入下一次输入
>>> for i in range(1, 20):... print('%2d' %i)... 1 2 3 4 5 6 7 8 910111213141516171819>>> for i in range(1, 20):... print('%2d' %i)... 1 2 3 4 5 6 7 8 910111213141516171819>>> for i in range(1, 20):... print('%02d' %i)... 01020304050607080910111213141516171819>>> for i in range(1, 20):... print('%03d' %i)... 001002003004005006007008009010011012013014015016017018019
https://docs.python.org/zh-cn/3/ -> 标准库参考
搜狗翻译 -> https://fanyi.sogou.com/ 查看帮助>>> import shutil>>> help(shutil)>>> help(shutil.copy)subprocess模块
常用方法
>>> import subprocess>>> subprocess.run('ls')# ls>>> subprocess.run(['ls', '/home'])>>> subprocess.run('ls /home') # 没有shell环境# FileNotFoundError>>> subprocess.run('ls /home', shell=True) # 在shell环境中运行ls /home-----------------------------------------------------------------------------------------------------------没有shell环境,就没有环境变量、命令扩展>>> rc = subprocess.run(['ls', '~'])ls: 无法访问~: 没有那个文件或目录CompletedProcess(args=['ls', '~'], returncode=2)>>> rc.returncode# 相当于是$?2>>> rc = subprocess.run('ls ~', shell=True)>>> rc.returncode
获取输出
可以通过subprocess.PIPE将命令的错误和输出保存到stderr和stdout中。这两个参数是bytes类型。>>> rc = subprocess.run('id root; id wangwu', shell=True,stderr=subprocess.PIPE, stdout=subprocess.PIPE)>>> rc.args # 执行的指令'id root; id wangwu'>>> rc.stderrb'id: wangwu: no such user\n'>>> rc.stdoutb'uid=0(root) gid=0(root) \xe7\xbb\x84=0(root)\n'>>> rc.stdout.decode()'uid=0(root) gid=0(root) 组=0(root)\n'
字符串分为bytes类型和str类型
>>> s1 = '达内'>>> type(s1)>>> b1 = s1.encode() # 编码成bytes类型,使用utf8编码>>> b1b'\xe8\xbe\xbe\xe5\x86\x85'>>> type(b1) >>> b1.decode() # 解码成str类型'达内'
语法
>>> x = y = 10>>> a, b = 10, 20>>> a10>>> b20>>> c, d = (10, 20)>>> c10>>> d20>>> e, f = [10, 20]>>> e10>>> f20>>> a, b = b, a # 将a、b的值互换
关键字
>>> import keyword>>> keyword.kwlist['False', 'None', 'True', 'and', 'as', 'assert', 'break','class', 'continue', 'def', 'del', 'elif', 'else','except', 'finally', 'for', 'from', 'global', 'if','import', 'in', 'is', 'lambda', 'nonlocal', 'not', 'or','pass', 'raise', 'return', 'try', 'while', 'with','yield']
内建
内建不是关键字,能够被覆盖,但是不推荐这么做 内置函数>>> len('abcd')4>>> len = 10>>> len10>>> len('abcd') # 现在len是10Traceback (most recent call last):File "", line 1, in TypeError: 'int' object is not callable>>> 10('abcd') # 数字是不能被调用的Traceback (most recent call last):File " ", line 1, in TypeError: 'int' object is not callable
python代码布局
!/usr/bin/python3'文档字符串,用于在帮助中显示'# 模块导入部分import randomimport time# 全局变量定义all_chs = '0123456789qwertyuiopasdfghjklzxcvbnm'# 类的定义class MyClass:pass# 函数定义def func(): pass# 程序主体代码if __name__ == '__main__': mc = MyClass() func()
以创建文件为例
文件名: /etc/hosts文件已存在,请重试文件名: /tmp/mytest.txt请输入内容,输入END结束> Hello World!> 2nd line.> 3rd line.> END# cat /tmp/mytest.txtHello World!2nd line.3rd line.
序列对象
>>> str(10)'10'>>> list('abcd')['a', 'b', 'c', 'd']>>> tuple('abcd')('a', 'b', 'c', 'd')>>> alist = [10, 20, 30, 40]>>> tuple(alist)(10, 20, 30, 40)
enumerate方法
>>> alist = ['tom', 'jerry', 'bob', 'alice']>>> enumerate(alist)>>> list(enumerate(alist))[(0, 'tom'), (1, 'jerry'), (2, 'bob'), (3, 'alice')]
reversed方法
>>> import random>>> alist = [random.randint(1, 100) for i in range(10)]>>> alist[1, 66, 61, 91, 37, 71, 92, 85, 31, 95]>>> reversed(alist)>>> list(reversed(alist))[95, 31, 85, 92, 71, 37, 91, 61, 66, 1]>>> for i in reversed(alist):... print(i)
sorted方法
>>> sorted(alist)[1, 31, 37, 61, 66, 71, 85, 91, 92, 95]>>> sorted(alist, reverse=True)[95, 92, 91, 85, 71, 66, 61, 37, 31, 1]
>>> ord('a')97>>> ord('A')65>>> ord('中')20013
>>> '%s is %s years old' % ('bob', 22)'bob is 22 years old'>>> 'I am %s' % 'tom' # 只有一个%s,%后面的数据不用写到元组'I am tom'>>> '%s is %d years old' % ('bob', 22) # 整数可以用%d'bob is 22 years old'>>> '%d is %d years old' % ('bob', 22) # 字符串不能用%dTraceback (most recent call last):File "", line 1, in TypeError: %d format: a number is required, not str>>> '%10s%8s' % ('name', 'age')' name age' # name占10列,age占8列>>> '%10s%8s' % ('tom', 22)' tom 22'>>> '%-10s%-8s' % ('name', 'age') # 负数表示左对齐'name age ' >>> '%-10s%-8s' % ('tom', 22)'tom 22 '
其他简单了解的格式化方法
>>> '97: %c' % 97'97: a'>>> '11: %#o' % 11'11: 0o13'>>> '11: %#x' % 11'11: 0xb'>>> '5 / 3 = %f' % (5 / 3)'5 / 3 = 1.666667'>>> '5 / 3 = %.2f' % (5 / 3)'5 / 3 = 1.67'>>> '%10d' % 5' 5'>>> '%010d' % 5'0000000005'
format方法
也用于实现字符串的格式化,与%s/%d类似>>> '{} is {} years old'.format('bob', 22)'bob is 22 years old'>>> '{} is {} years old'.format(22, 'bob')'22 is bob years old'>>> '{1} is {0} years old'.format(22, 'bob')'bob is 22 years old'# 第0个位置的数据是列表,根据第0个位置的下标取值>>> '{0[0]} is {0[1]} years old'.format(['bob', 22])'bob is 22 years old'
1、全局变量
在函数外面定义的变量,从它定义的开始位置,一直到程序结束,都可见可用。 一般来说,变量的值自始至终都不需要变化,可以设置为全局变量。 2、局部变量 在函数内部定义的变量,只能在本函数内部使用。生成随机字符串
>>> import string>>> import random>>> all_chs = string.ascii_letters + string.digits>>> result = [random.choice(all_chs) for i in range(8)]>>> result['V', '1', '1', 'R', '1', '7', 'm', '4']>>> ''.join(result)'V11R17m4'>>> '-'.join(result)'V-1-1-R-1-7-m-4'>>> '##'.join(result)'V##1##1##R##1##7##m##4'
原始字符串
作用:取消转义行为>>> win_path = 'c:\temp\newdir'>>> print(win_path) # \t成为tab,\n成为换行c: empewdir>>> wpath = r'c:\temp\newdir'>>> print(wpath)c:\temp\newdir>>> wpath'c:\\temp\\newdir'
字符串方法
>>> s1 = ' \tHello World! '>>> s1.strip() # 删除两端空白字符'Hello World! '>>> s1.lstrip()' \tHello World! '>>> s2 = 'hao123'>>> s2 = 'hao123'>>> s2.center(30)' hao123 '>>> s2.center(30, '*')'************hao123************'>>> s2.ljust(30, '#')'hao123########################'>>> s2.rjust(30, '#')'########################hao123'>>> s2.replace('h','H') #替换'Hao123'>>> s2.upper()'HAO123'>>> s2.lower()'hao123'>>> 'HAO123'.lower()'hao123'>>> s2.islower() #字母都是小写的吗True>>> s2.isdigit() #所有字符都是数字吗?False>>> s2.startswith('ab') #是以ab开头吗? False>>> s2.endswith('123') #是以123结尾吗True>>> s2.count('l') //统计l出现的个数0>>> s2.index('o') #第一个‘o’出现的下标2
容器、可变、顺序
>>> alist = [10, 80, 30, 60]>>> alist.append(50)>>> alist.extend([15,100]) //将序列对象扩展到alist中>>> alist[10, 80, 30, 60, 50, 15, 100]>>> alist.remove(20)Traceback (most recent call last): File "", line 1, in ValueError: list.remove(x): x not in list>>> alist.remove(30) #删除元素>>> alist.index(60) #取出60的下标2>>> alist.reverse() #反转>>> alist.insert(2, 60) //将60插入到下标为2的位置>>> alist[100, 15, 60, 50, 60, 80, 10]>>> alist.sort() #升序排列>>> alist.sort(reverse=True) #降序排列>>> alist.count(60)2>>> alist.pop() #默认弹出最后一项10>>> alist.pop(2) #弹出下标为2的项目60>>> alist.clear() #清空列表
容器、不可变、顺序
>>> atuple = (100, 80, 60, 60, 50, 15, 10)>>> atuple.count(30)0>>> atuple.index(50)4>>> a=(10)>>> a10>>> type(a)>>> a=(10,)>>> type(a) >>> a(10,)>>> atuple = (100, 80, 60, 60, 50, 15, 10)>>> atuple.count(30)0>>> atuple.index(50)4>>> a=(10)>>> a10>>> type(a) >>> a=(10,)>>> type(a) >>> a(10,)>>> len(a)1
方法的返回值
列表等对象,它们的方法就是一个函数。函数有可能有返回值,也可能没有,没有返回值,默认None>> alist = [100, 80, 60, 50, 15, 50]>>> a = alist.remove(50)>>> alist[100, 80, 60, 15, 50]>>> print(a)None>>> b = alist.pop() #pop有返回值>>> alist[100, 80, 60, 15]>>> b50
注意:变量没有 print() 语句,在交互解释器中可以显示它的值,但是作为程序文件运行,没有 print() 是不会打印值的
用列表模拟栈
0) 压栈(1) 出栈(2) 查询(3) 退出请选择(0/1/2/3): 2[](0) 压栈(1) 出栈(2) 查询(3) 退出请选择(0/1/2/3): 0数据: hello(0) 压栈(1) 出栈(2) 查询(3) 退出请选择(0/1/2/3): 2['hello'](0) 压栈(1) 出栈(2) 查询(3) 退出请选择(0/1/2/3): 1从列表中弹出了: hello(0) 压栈(1) 出栈(2) 查询(3) 退出请选择(0/1/2/3): 2[](0) 压栈(1) 出栈(2) 查询(3) 退出请选择(0/1/2/3): 1空列表(0) 压栈(1) 出栈(2) 查询(3) 退出请选择(0/1/2/3): 3Bye-bye
2.分析需要多个功能,将功能写成功能函数
def push_it():def pop_it():def view_it():def show_menu():if __name__ == '__main__':show_menu()
3.填写主程序代码
4.填写函数代码容器、可变、映射
创建字典
>>> adict = {'name': 'bob', 'age': 25}>>> dict(['ab',('name', 'alice'), ['age', '20']]) #分别通过字符串、元组和列表创建字典键值对{'a': 'b', 'name': 'alice', 'age': '20'}>>> {}.fromkeys(['bob', 'tom', 'jerry'], 'male'){'bob': 'male', 'tom': 'male', 'jerry': 'male'}
访问字典
>>> adict{'name': 'bob', 'age': 25}>>> 'bob' in adict #bob是字典的key吗?False>>> 'name' in adict #name是字典的key吗?True>>> for key in adict:... print('%s: %s' % (key, adict[key]))... name: bobage: 25>>> '%(name)s is %(age)s years old' % adict'bob is 25 years old'
更新字典
字典的key不能重复。在进行赋值的时候,如果key不存在,则向字典加入新值;如key已存在,则更新。>>> adict{'name': 'bob', 'age': 25}>>> adict['email']= 'bob@tedu.cn'>>> adict{'name': 'bob', 'age': 25, 'email': 'bob@tedu.cn'}>>> adict['age']= 23>>> adict{'name': 'bob', 'age': 23, 'email': 'bob@tedu.cn'}
del方法
del 可以删除各种对象,如删除变量,列表中的某一项、字典中的某一项>>> adict{'name': 'bob', 'age': 23, 'email': 'bob@tedu.cn'}>>> del adict['email']>>> adict{'name': 'bob', 'age': 23}
字典的key
1、字典的key必须是不可变类型:数字、字符串、元组 2、通过hash函数判断是否可变>>> hash(10)10>>> hash('abc')-8391699022895507984>>> hash((1,2))3713081631934410656>>> hash([1,2])Traceback (most recent call last): File "", line 1, in TypeError: unhashable type: 'list'
字典的方法
>>> adict{'name': 'bob', 'age': 23}>>> adict.get('name')'bob'>>> print(adict.get('qq'))None>>> adict.get('name', 'not found') #字典中有name,返回value:‘bob’'bob'>>> adict.get('qq', '123456') #字典中没有qq,返回123456'123456'>>> adict.update({'qq': '123456789', 'phone': '15856948712'})>>> adict{'name': 'bob', 'age': 23, 'qq': '123456789', 'phone': '15856948712'}>>> adict.pop('phone')'15856948712'>>> adict.keys()dict_keys(['name', 'age', 'qq'])>>> adict.values()dict_values(['bob', 23, '123456789'])>>> adict.items()dict_items([('name', 'bob'), ('age', 23), ('qq', '123456789')])
1、集合是数学上的概念,要求集合中的所有元素都是唯一的。
2、集合元素是唯一的、不可变的、无序的。集合就像是一个没有值的字典。>> aset = set('abc')>>> aset{'a', 'c', 'b'}>>> bset= set('bcd')>>> bset{'c', 'b', 'd'}>>> aset & bset #交集{'c', 'b'}>>> aset | bset #并集{'c', 'a', 'b', 'd'}>>> aset - bset #差补,只在aset中包含的元素{'a'}>>> bset - aset{'d'}
集合方法
>> aset.update(['d', 'e'])>>> aset{'c', 'b', 'a', 'd', 'e'}>>> aset.add('hello')>>> aset{'c', 'b', 'a', 'hello', 'd', 'e'}>>> aset.remove('hello')>>> aset{'c', 'b', 'a', 'd', 'e'}>>> aset.issuperset(bset) #aset是bset的超集吗?True>>> bset.issubset(aset) #bset是aset的子集吗?True>>> aset.union(bset) #aset | bset{'c', 'a', 'b', 'd', 'e'}>>> aset.intersection(bset) #aset & bset{'c', 'b', 'd'}>>> aset.difference(bset) #aset - bset{'a', 'e'}
比较两个文件,将/tmp/mima中存在的,但是在/tmp/passwd中不存在的取出来。
[root@room9pc01 day05]# cp /etc/passwd /tmp[root@room9pc01 day05]# cp /etc/passwd /tmp/mima[root@room9pc01 day05]# vim /tmp/mima //修改(复制,添加,删除)>>> with open('/tmp/passwd') as f1:... aset = set(f1)... >>> with open('/tmp/mima') as f2: ... bset = set(f2) #自动去除重复行...>>> cset = bset - aset>>> cset{'hello world\n', 'I am 56 years old\n', 'how are you\n'}>>> with open('/tmp/diff.txt', 'w') as fobj: #/tmp/diff.txt不存在则创建... fobj.writelines(cset)...[root@room9pc01 day05]# cat /tmp/diff.txt hello worldI am 56 years oldhow are you+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++[root@room9pc01 day05]# wc -l /tmp/mima //统计行数67 /tmp/mima[root@room9pc01 day05]# sort /tmp/mima | uniq | wc -l //排序后统计非重复行行数59++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++去除重复项>>> nums = [random.randint(1, 10) for i in range(20)]>>> nums[9, 8, 7, 9, 1, 8, 6, 4, 3, 1, 6, 6, 3, 6, 3, 10, 4, 9, 8, 10]方法一:>>> b=set(nums) #set自动去重>>> b{1, 3, 4, 6, 7, 8, 9, 10}>>> c = list(nums)>>> c[9, 8, 7, 9, 1, 8, 6, 4, 3, 1, 6, 6, 3, 6, 3, 10, 4, 9, 8, 10]>>> c = list(set(nums))>>> c[1, 3, 4, 6, 7, 8, 9, 10]方法二:>>> result = []>>> for i in nums:... if i not in result:... result.append(i)... >>> result[9, 8, 7, 1, 6, 4, 3, 10]
astr = 'hello'alist = ['tom', 'jerry']atuple = (10, 20, 30)adict = {'name': 'tom', 'age': 22}aset = set('abc')fname = '/etc/passwd'for ch in astr: print(ch)for name in alist: print(name)for i in atuple: print(i)for key in adict: print('%s : %s', (key, adict[key])for i in aset: print(i)with open(fname) as fobj: for line in fobj: print(line)
>>> import time>>> time.time() //自1970-1-1 00:00:00到某一时间点之间的秒数1557710846.0968487>>> time.ctime() //世界协调时间'Mon May 13 09:33:44 2019' //字符串时间,以英国格林威治所在经度为起始点,每隔15度角成为一个时区struct_time:九元组>>> time.localtime()time.struct_time(tm_year=2019, tm_mon=5, tm_mday=13, tm_hour=9, tm_min=37, tm_sec=8, tm_wday=0, tm_yday=133, tm_isdst=0)>>> t1 = time.localtime()>>> t1.tm_year2019>>> t1.tm_hour9
>>> import time>>> time.time() # 自1970-1-1 00:00:00到time.time()之间的秒数1557711710.3989246>>> time.sleep(3) # 睡眠>>> time.strftime('%Y-%m-%d %H:%M:%S') #接收时间元组,并返回以可读字符串表示的当地时间,格式由括号内format自定义'2019-05-13 09:51:36'>>> time.strftime('%a %A') # 周几 #%a表示本地简化星期名称,%A表示本地完整星期名称'Mon Monday'
给定字符串和时间样式,将字符串转换成struct_time`
>>> t1 = time.strptime('2019-05-13 09:51:36', '%Y-%m-%d %H:%M:%S') #根据指定的格式把一个时间字符串解析为时间元组>>> t1time.struct_time(tm_year=2019, tm_mon=5, tm_mday=13, tm_hour=9, tm_min=51, tm_sec=36, tm_wday=0, tm_yday=133, tm_isdst=-1)>>> t2 = time.localtime()>>> t2time.struct_time(tm_year=2019, tm_mon=5, tm_mday=13, tm_hour=9, tm_min=56, tm_sec=58, tm_wday=0, tm_yday=133, tm_isdst=0)>>> t2 > t1True>>> t2 < t1False
datetime时间模块
>>> from datetime import datetime>>> datetime.now() #返回的对象,各部分是年月日时分秒毫秒datetime.datetime(2019, 5, 13, 10, 47, 29, 235159)>>> t = datetime.now()>>> t.year2019>>> t.month5>>> t.day13>>> t.hour10>>> t.minute48>>> t.second32>>> t.microsecond570523>>> t.strftime('%Y%m%d %H:%M:%S')'20190513 10:48:32'>>> datetime.strptime('2019-05-13 12:00:00', '%Y-%m-%d %H:%M:%S')datetime.datetime(2019, 5, 13, 12, 0)>>> t1 = datetime(2019, 5, 13)>>> t1datetime.datetime(2019, 5, 13, 0, 0)
练习01:将一段时间内的日志提取出来
[root@room9pc01 day06]# vim mylog.log //编写日志文件2019-05-13 08:42:26 000000000002019-05-13 09:51:36 aaaaaa2019-05-13 09:52:36 bbbbbbbbb2019-05-13 09:58:36 cccccccccc2019-05-13 10:01:36 ddddddd2019-05-13 11:30:36 eeeeeeee2019-05-13 12:25:46 ffffffffffff2019-05-13 13:21:30 gggggggggg[root@room9pc01 day06]# vim mylog.py //编写切割日志程序import timet9 = time.strptime('2019-05-13 09:00:00', '%Y-%m-%d %H:%M:%S')t12 = time.strptime('2019-05-13 12:00:00', '%Y-%m-%d %H:%M:%S')with open('mylog.log') as fobj: for line in fobj: tstr = line[:19] #取出字符串前19个字符 #print(tstr) t = time.strptime(tstr, '%Y-%m-%d %H:%M:%S') if t9 < t < t12: print(line, end='') [root@room9pc01 day06]# python3 mylog.py 2019-05-13 09:51:36 aaaaaa2019-05-13 09:52:36 bbbbbbbbb2019-05-13 09:58:36 cccccccccc2019-05-13 10:01:36 ddddddd2019-05-13 11:30:36 eeeeeeee
timedelta时间模块
>>> from datetime import datetime,timedelta>>> t1 = datetime.now()>>> t2 = timedelta(days=100, hours=1)>>> t1 - t2 #100天零1个小时之前的时间datetime.datetime(2019, 2, 2, 10, 26, 6, 755687)>>> t1 + t2 #100天零1个小时之后的时间datetime.datetime(2019, 8, 21, 12, 26, 6, 755687)
异常处理
当程序不能正常工作时,程序出现错误,它将崩溃终止执行,这时程序默认向终端抛出异常。 把有可能发生异常的语句,放到try中执行。通过except捕获异常,异常不发生才需要执行的语句,放到else中。异常不管是否发生,都要执行的语句,放到finally中。[root@room9pc01 day06]# vim myerr.pytry: nums = int(input('number: ')) result = 100 / nums print(result)except ValueError: print('无效输入') exit(1)except KeyboardInterrupt: print('\n你按了“ctrl+c”') exit(2)except ZeroDivisionError: print('除数不能为0') exit(3)except EOFError: print('\n输入无效(ctrl+d)') exit(4)#print('Done')[root@room9pc01 day06]# python3 myerr.py number: 1100.0[root@room9pc01 day06]# python3 myerr.py number: 520.0[root@room9pc01 day06]# python3 myerr.py number: 输入无效(ctrl+d)[root@room9pc01 day06]# python3 myerr.py number: ^C你按了“ctrl+c”[root@room9pc01 day06]# python3 myerr.py number: 0除数不能为0[root@room9pc01 day06]# python3 myerr.py number: 无效输入
在编写程序时,并不总是需要写全部的语法,用的最多的组合是try-except和try- nally
[root@room9pc01 day06]# vim myerr2.py try: nums = int(input('number: ')) result = 100 / numsexcept (ValueError, EOFError): #输入ctrl+d或者Enter时异常处理 print('无效输入') #exit(1)except KeyboardInterrupt: print('\n你按了“ctrl+c”') #exit(2)except ZeroDivisionError: print('除数不能为0') #exit(3)else: print(result)finally: print('Done')[root@room9pc01 day06]# python3 myerr2.py number: 520.0Done[root@room9pc01 day06]# python3 myerr2.py number: 无效输入Done[root@room9pc01 day06]# python3 myerr2.py number: 无效输入Done[root@room9pc01 day06]# python3 myerr2.py number: ^C你按了“ctrl+c”Done[root@room9pc01 day06]# python3 myerr2.py number: 0除数不能为0Done
os模块
>>> import os>>> os.getcwd() #pwd'/root'>>> os.listdir() #ls>>> os.listdir('/home') #ls /home>> os.mkdir('/tmp/demo') #mkdir /temp/demo>>> os.makedirs('/tmp/aaa/bbb') #mkdir -p /temp/aaa/bbb>>> os.chdir('/root/桌面/python') #cd /root/桌面/python>>> os.symlink('/etc/hosts', 'zhuji') #ln -s>>> import shutil>>> shutil.copy('/etc/passwd', 'passwd')'passwd'>>> os.listdir()['passwd', 'zhuji', 'nsd2019', 'zzg_pypkgs', 'zzg_pypkgs.tar.gz']>>> os.stat('passwd') #stat passwdos.stat_result(st_mode=33188, st_ino=2638715, st_dev=2050, st_nlink=1, st_uid=0, st_gid=0, st_size=2929, st_atime=1557747976, st_mtime=1557747976, st_ctime=1557747976)>>> mima = os.stat('passwd')>>> mima.st_size2929>>> time.ctime(mima.st_atime) //查看最后访问时间(access time)'Mon May 13 19:46:16 2019'>>> time.ctime(mima.st_mtime) //查看最后修改时间(modify time)'Mon May 13 19:46:16 2019'>>> time.ctime(mima.st_ctime) //查看文件的权限、拥有者、所属的组、链接数发生改变时的时间(change time)'Mon May 13 19:46:16 2019'linux中文件权限的数值为八进制[root@room9pc01 python]# ll //查看passwd文件修改前权限总用量 776572drwxr-xr-x 8 root root 4096 5月 10 11:04 nsd2019-rw-r--r-- 1 root root 2929 5月 13 19:46 passwd //权限为644lrwxrwxrwx 1 root root 10 5月 13 15:37 zhuji -> /etc/hostsdrwxr-xr-x 16 ftp ftp 4096 1月 4 09:13 zzg_pypkgs-rw-r--r-- 1 root root 795192400 5月 7 08:56 zzg_pypkgs.tar.gz>>> os.chmod('passwd', 0o755) #修改passwd文件权限为755[root@room9pc01 python]# ll #查看passwd文件修改后权限总用量 776572drwxr-xr-x 8 root root 4096 5月 10 11:04 nsd2019-rwxr-xr-x 1 root root 2929 5月 13 19:46 passwd //权限变为755lrwxrwxrwx 1 root root 10 5月 13 15:37 zhuji -> /etc/hostsdrwxr-xr-x 16 ftp ftp 4096 1月 4 09:13 zzg_pypkgs-rw-r--r-- 1 root root 795192400 5月 7 08:56 zzg_pypkgs.tar.gz>>> 0o644 #权限644对应的八进制数420>>> os.chmod('passwd', 420) #修改passwd文件权限为644[root@room9pc01 python]# ll #查看passwd文件修改后权限总用量 776572drwxr-xr-x 8 root root 4096 5月 10 11:04 nsd2019-rw-r--r-- 1 root root 2929 5月 13 19:46 passwd #权限变为644lrwxrwxrwx 1 root root 10 5月 13 15:37 zhuji -> /etc/hostsdrwxr-xr-x 16 ftp ftp 4096 1月 4 09:13 zzg_pypkgs-rw-r--r-- 1 root root 795192400 5月 7 08:56 zzg_pypkgs.tar.gz>>> os.chown('passwd', 1009, 1009) #chown[root@room9pc01 python]# ll总用量 776572drwxr-xr-x 8 root root 4096 5月 10 11:04 nsd2019-rw-r--r-- 1 tom tom 2929 5月 13 19:46 passwd //所有者和所属组变为tomlrwxrwxrwx 1 root root 10 5月 13 15:37 zhuji -> /etc/hostsdrwxr-xr-x 16 ftp ftp 4096 1月 4 09:13 zzg_pypkgs-rw-r--r-- 1 root root 795192400 5月 7 08:56 zzg_pypkgs.tar.gz>>> os.getcwd()'/root/桌面/python'>>> os.listdir()['zhuji', 'nsd2019', 'zzg_pypkgs', 'zzg_pypkgs.tar.gz']>>> os.path.abspath('nsd2019')'/root/桌面/python/nsd2019'>>> fname = os.path.abspath('nsd2019')>>> fname'/root/桌面/python/nsd2019'>>> os.path.basename(fname)'nsd2019'>>> os.path.dirname(fname)'/root/桌面/python'>>> os.path.split(fname)('/root/桌面/python', 'nsd2019')>>> os.path.join('/root/桌面/python', 'nsd2019')'/root/桌面/python/nsd2019'>>> os.path.isdir('/etc/abc') #[ -d /etc/abc ]False>>> os.path.isfile('/root/桌面/python/nsd2019') #[ -f /root/桌面/python/nsd2019 ]False>>> os.path.islink('/root/桌面/python/zhuji') #是链接吗True>>> os.path.ismount('/') #是挂载点吗True>>> os.path.exists('/etc') #存在吗True
pickle模块
常规的文件,只能写入字符串,不能写其他数据类型>>> f = open('/tmp/data', 'w')>>> f.write('ni hao\n')7>>> f.write({'name': 'bob', 'age': 23, 'sex': 'male'})Traceback (most recent call last): File "", line 1, in TypeError: write() argument must be str, not dict>>> f.close()pickle模块可以把任意的数据类型写入到文件,还可以无损地取出来。>>> import pickle>>> shop_list = {'eggs': 3, 'apple': 6, 'banana': 5}>>> with open('/tmp/shop.data', 'wb') as fobj:... pickle.dump(shop_list, fobj)... >>> with open('/tmp/shop.data', 'rb') as fobj:... myadict = pickle.load(fobj)... >>> type(myadict) >>> myadict{'eggs': 3, 'apple': 6, 'banana': 5}>>> myadict['eggs']3
案例:记账程序
[root@room9pc01 day07]# vim account.pyimport osimport picklefrom time import strftimedef save(fname): try: amount = int(input('金额:')) comment = input('备注:') except (KeyboardInterrupt, EOFError, ValueError): print('\n无效输入,返回') return #提前结束函数并返回 data = strftime('%Y-%m-%d') with open(fname, 'rb') as fobj: records = pickle.load(fobj) balance = records[-1][-2] + amount record = [data, amount, 0, balance, comment] records.append(record) with open(fname, 'wb') as fobj: pickle.dump(records,fobj)def cost(fname): try: amount = int(input('金额:')) comment = input('备注:') except (KeyboardInterrupt, EOFError, ValueError): print('\n无效输入,返回') return #提前结束函数并返回 data = strftime('%Y-%m-%d') with open(fname, 'rb') as fobj: records = pickle.load(fobj) balance = records[-1][-2] - amount record = [data, 0, amount, balance, comment] records.append(record) with open(fname, 'wb') as fobj: pickle.dump(records, fobj)def query(fname): print('%-12s%-8s%-8s%-12s%-20s' % ('data', 'save', 'cost', 'balance', 'comment')) with open(fname, 'rb') as fobj: records = pickle.load(fobj) for record in records: print('%-12s%-8s%-8s%-12s%-20s' % tuple(record))def show_menu(): cmds = {'0': save, '1': cost, '2': query} prompt = '''(0)收入(1)支出(2)查询(3)退出请输入(0/1/2/3): ''' fname = 'account.data' if not os.path.exists(fname): init_data = [ ['2019-05-14', 0, 0, 10000, 'init'], ] with open(fname, 'wb') as fobj: pickle.dump(init_data, fobj) while True: try: choice = input(prompt).strip() except (KeyboardInterrupt, EOFError, KeyError): choice = '3' if choice not in ['0', '1', '2', '3']: print('无效输入,请重试') if choice == '3': print('\nBye-bye') break cmds[choice](fname)if __name__ == '__main__': show_menu()
函数参数
只有一个参数名,称作位置参数 参数形式时key=value,称作关键字参数>>> def get_age(name, age):... print('%s is %s years old' % (name, age))... >>> get_age('bob', 20)bob is 20 years old>>> get_age() #Error,参数太少Traceback (most recent call last): File "", line 1, in TypeError: get_age() missing 2 required positional arguments: 'name' and 'age'>>> get_age('bob', 20, 100) #Error,参数太多Traceback (most recent call last): File " ", line 1, in TypeError: get_age() takes 2 positional arguments but 3 were given>>> get_age(20, 'bob') #语法正确,但是语义错误20 is bob years old>>> get_age(age=20, name= 'bob') #okbob is 20 years old>>> get_age(age=20, 'bob') #Error,关键字参数必须在后 File " ", line 1SyntaxError: positional argument follows keyword argument>>> get_age(20, name= 'bob') #Error,name得到了多个值Traceback (most recent call last): File " ", line 1, in TypeError: get_age() got multiple values for argument 'name'>>> get_age('bob', age=20) #okbob is 20 years old
参数组
如果函数的参数个数是不固定的,可以使用参数组接收参数• python允许程序员执行一个没有显式定义参数的函数
• 相应的方法是通过一个把元组(非关键字参数)或字典(关键字参数)作为参数组传递给函数— 在参数名前加"*“表示参数是元组
— 在参数名前加”**"表示参数是字典>>> def func1(*args):... print(args)... >>> func1()()>>> func1('bob')('bob',)>>> func1('bob', 123, 'hello', 'aaa')('bob', 123, 'hello', 'aaa')>>> def func2(**args):... print(args)... >>> func2(){}>>> func2(name ='bob', age=20){'name': 'bob', 'age': 20}调用函数时,如果参数有“*”,表示把参数拆开>>> def add(x, y):... print(x+y)... >>> nums = [10, 20]>>> num_dict = {'x': 10, 'y': 20}>>> num_dict2 = {'a': 10, 'b': 20}>>> add(nums) #Error,参数nums赋值给了x,y没有得到值Traceback (most recent call last): File "", line 1, in TypeError: add() missing 1 required positional argument: 'y'>>> add(num_dict) #Error,参数nums赋值给了x,y没有得到值Traceback (most recent call last): File " ", line 1, in TypeError: add() missing 1 required positional argument: 'y'>>> add(*nums) # nums拆成了10, 2030>>> add(**num_dict) # 拆成了x=100, y=20030>>> add(**num_dict2) # 拆成了a=100, b=200,与函数定义的形参名不符Traceback (most recent call last): File " ", line 1, in TypeError: add() got an unexpected keyword argument 'a'
—如果函数的代码块非常简单,只有一行,可以使用匿名函数。—匿名函数使用lambda关键字定义。• python允许用lambda关键字创造匿名函数• 匿名是因为不需要以标准的def方式来声明• 一个完整的lambda“语句”代表了一个表达式,这个表达式的定义体必须和声明放在同一行lambda [arg1[, arg2, ... argN]]: expression>>> a = lambda x, y: x + y>>> print(a(3, 4))7
>>> def add(x, y):... return x + y... >>> add(10, 20)30>>> myadd = lambda x, y: x + y>>> myadd(10, 20)30[root@room9pc01 day07]# vim math_game3.pyfrom random import randint, choicedef exam(): cmds = {'+': lambda x, y: x + y, '-': lambda x, y: x-y} #使用匿名函数传值 nums = [randint(1, 100) for i in range(2)] nums.sort(reverse = True) #降序排列 op = choice('+-') result = cmds[op](*nums) prompt ='%s %s %s = ' % (nums[-2], op, nums[-1]) counter = 0 while counter < 3: try: answer = int(input(prompt)) except: #不指定异常默认捕获所有异常,但是不推荐 print() continue if answer == result: print('Very Good!') break print('Answer Wrong !') counter += 1 else: print('%s %s' % (prompt, result))if __name__ == '__main__': while True: exam() try: yn = input('Continue(y/n)? ').strip()[0] #删除用户输入的多余空格,并取出第一个字符 except IndexError: yn = 'y' except (KeyboardInterrupt, EOFError): yn = 'n' if yn in 'nN': print('\nBye-bye') break
filter和map函数
filter()函数 • filter(func, seq):调用一个布尔函数func来迭代遍历 每个序列中的元素;返回一个使func返回值为true的 元素的序列 • 如果布尔函数比较简单,直接使用lambda匿名函数 就显得非常方便了>>> data = filter(lambda x: x % 2, [num for num in range(10)])>>> print(list(d
map()函数
• map(func, seq):调用一个函数func来迭代遍历每个 序列中的元素;返回一个经过func处理过的元素序列 • 如果布尔函数比较简单,直接使用lambda匿名函数 就显得非常方便了>>> data = map(lambda x: x*2+1, [num for num in range(10)])>>> list(data)[1, 3, 5, 7, 9, 11, 13, 15, 17, 19]
[root@room9pc01 day07]# vim filter_func.pyffrom random import randintdef func1(x): return True if x > 50 else Falsedef func2(x): return x +2if __name__ == '__main__': nums = [randint(1,100) for i in range(10)] print(nums) result =filter(func1, nums) print(list(result)) result2 = filter(lambda x: True if x > 50 else False, nums) print(list(result2)) result3 = map(func2, nums) print(list(result3)) result4 = map(lambda x: x+2, nums) print(list(result4))[root@room9pc01 day07]# python3 filter_func.py[34, 3, 74, 31, 94, 81, 38, 13, 20, 57][74, 94, 81, 57][74, 94, 81, 57][36, 5, 76, 33, 96, 83, 40, 15, 22, 59][36, 5, 76, 33, 96, 83, 40, 15, 22, 59]
filter和map函数赋值时只能使用一次
>>> nums = filter(lambda x: x%2==0, [i for i in range(10)])>>> nums#filter函数对象>>> list(nums)[0, 2, 4, 6, 8]>>> list(nums)[]>>> nums = map(lambda x: x+2, [i for i in range(10)])>>> nums
全局变量
在函数外面定义的变量。从定义起始位置开始,一直到程序结束,任何地方均可见可用。局部变量
在函数内部定义的变量。只在函数内部使用,离开函数不能使用。函数的参数也可以看作是局部变量。>>> x = 10 #x为全局变量>>> def func1():... print(x)... >>> func1()10>>> def func2():... x = 'hello' #x为局部变量,局部和全局有相同的变量,局部遮盖住全局变量... print(x)... >>> func2()hello>>> x10>>> def func3():... global x # 声明函数内部使用的是全局变量x... x = 'ni hao'... print(x)... >>> func3()ni hao>>> x'ni hao'名称查找的时候,先查局部,再查全局,最后查内建。>>> def func4():... print(len('abc'))... >>> func4() #查到了内建len函数3>>> len = 10>>> func4() #在全局查到len的值是10,数字不能调用Traceback (most recent call last): File "", line 1, in File " ", line 2, in func4TypeError: 'int' object is not callable
partial偏函数
偏函数 • 偏函数的概念是将函数式编程的概念和默认参数以及 可变参数结合在一起 • 一个带有多个参数的函数,如果其中某些参数基本上 固定的,那么就可以通过偏函数为这些参数赋默认值>>> from operator import add>>> from functools import partial>>> add10 = partial(add, 10)>>> print(add10(25))35
>>> from functools import partial>> add(10, 20, 30, 40, 50)150>>> add(10, 20, 30, 40, 9)109>>> add(10, 20, 30, 40, 15)115>>> myadd = partial(add, 10, 20, 30, 40)>>> myadd(50)150>>> myadd(9)109>>> myadd(15)115
[root@room9pc01 day07]# vim mygui.py //编写gui程序import tkinterfrom functools import partialwindow = tkinter.Tk()lb = tkinter.Label(window, text = 'Hello World', font= 'Arial 20')#b1 = tkinter.Button(window, fg='white', bg='blue', text='Button 1')#b2 = tkinter.Button(window, fg='white', bg='blue', text='Button 2')#b3 = tkinter.Button(window, fg='white', bg='blue', text='quit', command=window.quit)MyButton = partial(tkinter.Button, window, fg='white', bg='blue')b1 = MyButton(text='Button 1')b2 = MyButton(text='Button 1')b3 = MyButton(text='quit', command=window.quit)lb.pack()b1.pack()b2.pack()b3.pack()window.mainloop()
• 如果函数包含了对其自身的调用,该函数就是递归的
• 在操作系统中,查看某一目录内所有文件、修改权限等都是递归的应用[root@room9pc01 day07]# vim fun_jiecheng.py //阶乘程序计算n!def func(n): if n == 1: return 1 return n*func(n-1)if __name__ == '__main__': print(func(5))[root@room9pc01 day07]# python3 fun_jiecheng.py //计算5的阶乘5!120快速排序:假定第一个数是中间值。把比这个数小的放到smaller列表,把比它大的数放到larger列表。最后把这三个部分拼接起来。smaller和larger采用相同的办法继续排序,直到列表的长度是0或1结束。[root@room9pc01 day07]# vim qsort.py //编写程序,在1—100之间随机取10个数,并按从小到大顺序排序from random import randintdef qsort(seq): if len(seq) < 2: #如果长度小于2,不用排序,直接返回 return seq middle = seq[0] #假定第一个值是中间值 smaller = [] #用于保存比中间值小的数据 larger = [] #用于保存比中间值大的数据 for i in seq[1:]: if i < middle: smaller.append(i) else: larger.append(i) return qsort(smaller) + [middle] +qsort(larger)if __name__ == '__main__': nums = [randint(1, 100) for i in range(10)] print(nums) print(qsort(nums))[root@room9pc01 day07]# python3 qsort.py //程序演示[44, 96, 7, 52, 76, 35, 10, 55, 68, 76][7, 10, 35, 44, 52, 55, 68, 76, 76, 96]
>>> def mygen(): #定义生成器函数... yield 'hello' #生成器函数通过yield返回值... yield 50... yield 'a+b'... a = 10 + 20... yield a... >>> mg = mygen()>>> mg>>> list(mg) #将生成器对象转换为列表['hello', 50, 'a+b', 30]>>> list(mg) #生成器对象只能使用一次[]>>> mga = mygen() #通过赋值值生成器函数,可以重复获取生成器对象>>> mga #表明为生成器对象,占用内存空间小>>> list(mga)['hello', 50, 'a+b', 30]>>> for i in mga: #上一条命令使用了一次生成器对象,无法遍历... print(i)... >>> mga = mygen()>>> for i in mga: #重新赋值生成器对象,for循环可以遍历... print(i)... hello50a+b30
生成器还可以使用生成器表达式
>>> from random import randint>>> nums = (randint(1, 100) for i in range(10)) #生成器表达式>>> numsat 0x7fd566a8feb8> #生成器对象>>> list(nums)[5, 17, 84, 16, 22, 4, 48, 22, 87, 67]>>> list(nums)[]>>> ips = ('192.168.8.%s' % i for i in range(1, 5))>>> ips at 0x7fd566a8ffc0> #生成器对象>>> for ip in ips:... print(ip)... 192.168.8.1192.168.8.2192.168.8.3192.168.8.4
python导入模块时,将会从以下两个位置搜索模块:
>>> import sys>>> sys.path['', '/usr/local/lib/python36.zip', '/usr/local/lib/python3.6', '/usr/local/lib/python3.6/lib-dynload', '/usr/local/lib/python3.6/site-packages']
常用的导入方法
>>> import os>>> from time import strftime
不常用的导入方法
>>> import random, datetime>>> import pickle as p
导入和加载
hashlib模块
计算哈希值的模块。hash哈希是单向加密的算法。
通过原始数据可以生成固定长度的乱码
原始数据相同,乱码也一定相同
原始数据有微小的不同,乱码也一定完全不同
不能通过乱码反推回原始数据
常用的算法有:md5、sha(均为单向加密算法)
经常用于:存储加密密码、文件完整性校验
>>> import hashlib>>> m = hashlib.md5(b'123456') #校验数据时要求是bash类型>>> m.hexdigest() #以十六进制(hex)数的形式返回md5值'e10adc3949ba59abbe56e057f20f883e'#如果需要计算的数据量非常大,可以分批量进行更新>>> m1 = hashlib.md5()>>> m1.update(b'12')>>> m.hexdigest()'e10adc3949ba59abbe56e057f20f883e'>>> m1.update(b'34')>>> m.hexdigest()'e10adc3949ba59abbe56e057f20f883e'>>> m1.update(b'56')>>> m.hexdigest()'e10adc3949ba59abbe56e057f20f883e'
[root@room9pc01 day08]# vim check_md5.pyimport hashlibimport sysdef check_md5(fname): m = hashlib.md5() with open(fname, 'rb') as fobj: while True: data =fobj.read(4096) if not data: break m.update(data) return m.hexdigest()if __name__ == '__main__': print(check_md5(sys.argv[1]))[root@room9pc01 day08]# python3 check_md5.py /etc/passwd710e1b6731bb6d457c6bce0f9835830a
tarfile模块
实现归档压缩功能,支持gzip、bzip2、lzma格式的压缩。>>> import tarfile>>> tar = tarfile.open('/tmp/demo/myfile.tar.gz', 'w:gz')>>> tar.add('/etc/security')>>> tar.add('/etc/hosts')>>> tar.close()>>> tar = tarfile.open('/tmp/demo/myfile.tar.gz', 'r')>>> tar.extractall('/tmp/demo') #不指定解压目标目录,默认解压当前目录>>> tar.close()[root@room9pc01 day08]# ls /tmp/demoetc myfile.tar.gz[root@room9pc01 day08]# cd /tmp/demo/etc/[root@room9pc01 etc]# lshosts security
os.walk方法
>>> os.walk('/tmp/demo/security')>>> list(os.walk('/tmp/demo/security'))[('/tmp/demo/security', ['console.apps', 'console.perms.d', 'limits.d', 'namespace.d'], ['access.conf', 'chroot.conf', 'console.handlers', 'console.perms', 'group.conf', 'limits.conf', 'namespace.conf', 'namespace.init', 'opasswd', 'pam_env.conf', 'sepermit.conf', 'time.conf', 'pwquality.conf']), ('/tmp/demo/security/console.apps', [], ['config-util', 'xserver', 'liveinst', 'setup']), ('/tmp/demo/security/console.perms.d', [], []), ('/tmp/demo/security/limits.d', [], ['20-nproc.conf']), ('/tmp/demo/security/namespace.d', [], [])]>>> a = list(os.walk('/tmp/demo/security'))>>> len(a)5>>> a[0]('/tmp/demo/security', ['console.apps', 'console.perms.d', 'limits.d', 'namespace.d'], ['access.conf', 'chroot.conf', 'console.handlers', 'console.perms', 'group.conf', 'limits.conf', 'namespace.conf', 'namespace.init', 'opasswd', 'pam_env.conf', 'sepermit.conf', 'time.conf', 'pwquality.conf'])>>> a[1]('/tmp/demo/security/console.apps', [], ['config-util', 'xserver', 'liveinst', 'setup'])
os.walk()方法返回的数据由多个元组构成。每个元组又有三项,这三项分别是:路径字符串、该路径下的目录列表、该路径下的文件列表。
>>> list(os.walk('/tmp/demo/security'))[ ('父目录', ['父目录下的子目录列表'], ['父目录下的文件列表']), ('子目录1', ['子目录1下的孙目录列表'], ['子目录下的文件列表']), ('子目录2', ['子目录2下的孙目录列表'], ['子目录下的文件列表']),]>>> for path, folders, files in os.walk('/tmp/demo/etc'):... for file in files:... os.path.join(path, file)... '/tmp/demo/etc/hosts''/tmp/demo/etc/security/namespace.conf''/tmp/demo/etc/security/sepermit.conf''/tmp/demo/etc/security/group.conf''/tmp/demo/etc/security/limits.conf''/tmp/demo/etc/security/time.conf''/tmp/demo/etc/security/access.conf''/tmp/demo/etc/security/pwquality.conf''/tmp/demo/etc/security/console.handlers''/tmp/demo/etc/security/opasswd''/tmp/demo/etc/security/chroot.conf''/tmp/demo/etc/security/console.perms''/tmp/demo/etc/security/pam_env.conf''/tmp/demo/etc/security/namespace.init''/tmp/demo/etc/security/console.apps/setup''/tmp/demo/etc/security/console.apps/xserver''/tmp/demo/etc/security/console.apps/config-util''/tmp/demo/etc/security/console.apps/liveinst''/tmp/demo/etc/security/limits.d/20-nproc.conf'
OOP:面向对象的编程
• 类(Class):用来描述具有相同的属性和方法的对象的集合。它定义了该集合中每个对象所共有的属性和方
法。对象是类的实例。 • 实例化:创建一个类的实例,类的具体对象。 • 方法:类中定义的函数。 • 对象:通过类定义的数据结构实例。对象包括两个数据成员(类变量和实例变量)和方法。创建类
• 使用 class 语句来创建一个新类,class 之后为类的 名称并以冒号结尾 • 类名建议使用驼峰形式class BearToy: pass
创建实例
• 类是蓝图,实例是根据蓝图创建出来的具体对象 如果使用以前所学知识,可以定义字典保存人物属性,创建函数定义人物的行为:tidy = BearToy()
>>> class GameCharacter:... def __init__(self, name, weapon):... self.name = name... self.weapon = weapon... def speak(self, word):... print('我是%s, %s' % (self.name, word))... >>> lvbu = GameCharacter('吕布', '方天画戟')>>> lvbu.name'吕布'>>> lvbu.weapon'方天画戟'>>> lvbu.speak('人在塔在')我是吕布, 人在塔在>>> guanyu = GameCharacter('关羽', '青龙偃月刀')>>> guanyu.name'关羽'>>> guanyu.weapon'青龙偃月刀'>>> guanyu.speak('呵呵')我是关羽, 呵呵##############################################################[root@room9pc01 day08]# vim game.pyclass GameCharacter: def __init__(self, name, weapon): self.name = name self.weapon = weapon def speak(self, word): print('我是%s, %s' % (self.name, word)) def walk(self): print('我有%s, 我能走' % self.weapon)if __name__ == '__main__': lvbu =GameCharacter('吕布','方天画戟') print(lvbu.name) print(lvbu.weapon) lvbu.speak('人在塔在') lvbu.walk() guanyu = GameCharacter('关羽', '青龙偃月刀') print(guanyu.name) print(guanyu.weapon) guanyu.speak('呵呵')[root@room9pc01 day08]# python3 game.py吕布方天画戟我是吕布, 人在塔在我有方天画戟, 我能走关羽青龙偃月刀我是关羽, 我的大刀已饥渴难耐了
什么是组合
• 类被定义后,目标就是要把它当成一个模块来使用,并把这些对象嵌入到你的代码中去 • 组合就是让不同的类混合并加入到其它类中来增加功能和代码重用性 • 可以在一个大点的类中创建其它类的实例,实现一些其它属性和方法来增强对原来的类对象[root@room9pc01 day08]# vim game2.pyclass Weapon: def __init__(self, wname, strength, type): self.name = wname self.strength = strength self.type = typeclass GameCharacter: def __init__(self, name, weapon): self.name = name self.weapon = weapon def speak(self, word): print('我是%s, %s' % (self.name, word))if __name__ == '__main__': ji =Weapon('方天画戟', 100, '物理攻击') lvbu = GameCharacter('吕布', ji) #ji是Weapon类的实例,作为GameCharacte类的组件,实现将Weapon(武器)类实例绑定到Gamecharacter(游戏角色)类实例 print(lvbu.weapon.name) print(lvbu.weapon.type) print(lvbu.weapon.strength) guanyu = GameCharacter('关羽','大刀') guanyu.speak('我的大刀已饥渴难耐了')[root@room9pc01 day08]# python3 game2.py 方天画戟物理攻击100我是关羽, 我的大刀已饥渴难耐了
继承
[root@room9pc01 day08]# vim game3.pyclass GameCharacter: def __init__(self, name, weapon): self.name = name self.weapon = weapon def speak(self, word): print('我是%s, %s' % (self.name, word))class Warrior(GameCharacter): # 括号中指定父类(基类) def attack(self): print('近身肉搏')class Mage(GameCharacter): def attack(self): print('远程攻击')if __name__ == '__main__': gl = Warrior('盖伦', '大刀') tm = Mage('提莫', '蘑菇') gl.speak('人在塔在') gl.attack() tm.speak('我去前面用脸探探路') tm.attack()[root@room9pc01 day08]# python3 game3.py我是盖伦, 人在塔在近身肉搏我是提莫, 我去前面用脸探探路##########################################################################[root@room9pc01 day08]# vim myclass.pyclass A: def foo(self): print('a foo')class B: def bar(self): print('b bar')class C(B, A): pass if __name__ == '__main__': c1 = C() c1.foo() c1.bar()[root@room9pc01 day08]# python3 myclass.pya foob bar###################################################################[root@room9pc01 day08]# vim myclass.pyclass A: def foo(self): print('a foo') def fn1(self): print('a fn')class B: def bar(self): print('b bar') def fn1(self): print('b fn')class C(B, A): pass def fn1(self): print('c fn')if __name__ == '__main__': c1 = C() c1.foo() c1.bar() c1.fn1()[root@room9pc01 day08]# python3 myclass.pya foob barc fn####################################################################[root@room9pc01 day08]# vim myclass.pyclass A: def foo(self): print('a foo') def fn1(self): print('a fn')class B: def bar(self): print('b bar') def fn1(self): print('b fn')class C(B, A): #自左向右(自下向上:B—>A)调用父类 pass # def fn1(self): # print('c fn')if __name__ == '__main__': c1 = C() c1.foo() c1.bar() c1.fn1()[root@room9pc01 day08]# python3 myclass.pya foob barb fn######################################################################[root@room9pc01 day08]# vim myclass.pyclass A: def foo(self): print('a foo') def fn1(self): print('a fn')class B: def bar(self): print('b bar') def fn1(self): print('b fn')class C(A, B): #自左向右(自上向下:A—>B)调用父类 pass # def fn1(self): # print('c fn')if __name__ == '__main__': c1 = C() c1.foo() c1.bar() c1.fn1()[root@room9pc01 day08]# python3 myclass.pya foob bara fn
正则表达式
#将ip地址对应mac地址用:隔开方式表示192.168.1.1 00G5C6123456192.168.1.2 00A5G2127854 192.168.1.1 00:G5:C6:12:34:56192.168.1.2 00:A5:G2:12:78:54 ~ ~ ~ :%s/\(..\)\(..\)\(..\)\(..\)\(..\)\(..\)$/\1:\2:\3:\4:\5:\6//t[a-z]m 匹配t(a-z任意单个字符)m/t[^0-9]m 匹配t(取反,不是0-9)m/t\Sm 匹配t(中间一个字符不是空白字符)m/t\sm 匹配t(中间一个字符是空白字符)m
re模块
>>> import re#match函数尝试用正则表达式模式从字符串的开头匹配,如果匹配成功,则返回一个匹配对象;否则返回None>>> re.match('f..', 'food') #匹配以"f.."开头的字符串<_sre.SRE_Match object; span=(0, 3), match='foo'>>>> re.match('f..', 'seafood')>>> print(re.match('f..', 'food'))<_sre.SRE_Match object; span=(0, 3), match='foo'>>>> print(re.match('f..', 'seafood'))None#search函数在字符串中查找正则表达式模式的第一次出现,如果匹配成功,则返回一个匹配对象;否则返回None>>> re.search('f..', 'food') #查找第一个匹配"f.."的字符串<_sre.SRE_Match object; span=(0, 3), match='foo'>>>> re.search('f..', 'seafood')<_sre.SRE_Match object; span=(3, 6), match='foo'>#group方法使用match或search匹配成功后,返回的匹配对象可以通过group方法获得匹配内容>>> m = re.search('f..', 'seafood')>>> m.group() # 返回匹配到的内容'foo'#findall函数在字符串中查找正则表达式模式的所有(非重复)出现,返回一个匹配对象的列表>>> re.findall('f..', 'seafood is food')['foo', 'foo']#finditer函数和findall()函数有相同的功能,但返回的不是列表而是迭代器;对于每个匹配,该迭代器返回一个匹配对象>>> for m in re.finditer('f..', 'seafood is food'):... print(m.group())... foofoo#split方法根据正则表达式中的分隔符把字符分割为一个列表,并返回成功匹配的列表>>> re.split('-|\.', 'hello-world.tar.gz')['hello', 'world', 'tar', 'gz']sub方法把字符串中所有匹配正则表达式的地方替换成新的字符串>>> re.sub('X', 'tom', 'Hi X.Nice to meet you X.' )'Hi tom.Nice to meet you tom.'compile函数• 对正则表达式模式进行编译,返回一个正则表达式对象• 不是必须要用这种方式,但是在大量匹配的情况下,可以提升效率>>> patt = re.compile('f..')>>> patt.search('seafood')>>> m = patt.search('seafood')>>> m<_sre.SRE_Match object; span=(3, 6), match='foo'>>>> m.group()'foo'<_sre.SRE_Match object; span=(3, 6), match='foo'>>>> patt.findall('seafood is food')['foo', 'foo']
Counter对象
统计对象次数并降序排列>>> from collections import Counter>>> c = Counter()>>> c.update('1.1.1.1') #对象是字符串时,统计字符个数>>> cCounter({'1': 4, '.': 3}) #“1”出现了4次,“.”出现了3次>>> c1 = Counter() >>> c1.update(['1.1.1.1']) #对象是列表时,将列表作为整体进行统计>>> c1.update(['1.1.1.2'])>>> c1.update(['1.1.1.2'])>>> c1.update(['1.1.1.2'])>>> c1.update(['1.1.1.3'])>>> c1.update(['1.1.1.3'])>>> c1.update(['1.1.1.3'])>>> c1.update(['1.1.1.3'])>>> c1.update(['1.1.1.3'])>>> c1Counter({'1.1.1.3': 5, '1.1.1.2': 3, '1.1.1.1': 1}) #统计各列表次数>>> c1.most_common(2) 取出(次数)排名前二的对象[('1.1.1.3', 5), ('1.1.1.2', 3)]
案例:分析apache访问日志
• 编写一个apche日志分析脚本#函数式编程[root@room9pc01 day09]# vim count_patt.pyimport refrom collections import Counterdef count_patt(fname, patt): cpatt = re.compile(patt) #先编译模式 c = Counter() #用于保存结果 #打开文件,从每一行中匹配,将匹配结果更新到c中 with open(fname) as fobj: for line in fobj: m =cpatt.search(line) if m: c.update([m.group()]) return cif __name__ == '__main__': fname = 'access_log' ip = '^(\d+\.){3}\d+' #匹配ip地址正则表达式(如: 192.168.1.25, 545135.445465.765.312354566) br = 'Firefox|MSIE|Chrome' #匹配浏览器类型正则表达式 print(count_patt(fname, ip)) print(count_patt(fname, br))[root@room9pc01 day09]# python3 count_patt.py Counter({'172.40.0.54': 391, '172.40.50.116': 244, '201.1.1.254': 173, '127.0.0.1': 121, '201.1.2.254': 119, '192.168.2.254': 110, '192.168.4.254': 103, '172.40.58.150': 10, '172.40.58.101': 10, '172.40.58.124': 6})Counter({'Firefox': 870, 'MSIE': 391, 'Chrome': 24})#面向对象编程[root@room9pc01 day09]# vim count_patt2.pymport refrom collections import Counterclass CountPatt: #定义类(常用于定义固定不变的属性) def __init__(self, fname): self.fname = fname def count_patt(self, patt): #定义类中的方法(函数) cpatt = re.compile(patt) #先编译模式 c = Counter() #用于保存结果 #打开文件,从每一行中匹配,将匹配结果更新到c中 with open(self.fname) as fobj: for line in fobj: m =cpatt.search(line) if m: c.update([m.group()]) return cif __name__ == '__main__': cp = CountPatt('access_log') #调用Countatt类创建实例cp ip = '^(\d+\.){3}\d+' #匹配ip地址正则表达式(如: 192.168.1.25, 545135.445465.765.312354566) br = 'Firefox|MSIE|Chrome' #匹配浏览器类型正则表达式 result1 = cp.count_patt(ip) result2 = cp.count_patt(br) print(result1) print(result2)[root@room9pc01 day09]# python3 count_patt2.py //程序运行结果一致Counter({'172.40.0.54': 391, '172.40.50.116': 244, '201.1.1.254': 173, '127.0.0.1': 121, '201.1.2.254': 119, '192.168.2.254': 110, '192.168.4.254': 103, '172.40.58.150': 10, '172.40.58.101': 10, '172.40.58.124': 6})Counter({'Firefox': 870, 'MSIE': 391, 'Chrome': 24})
安装pymysql模块
更改安装源#在线安装[root@room9pc01 day09]# mkdir ~/.pip/[root@room9pc01 day09]# vim ~/.pip/pip.conf[global]index-url= http://mirrors.163.com/pypi/simple/[install]trusted-host=mirrors.163.com[root@room9pc01 day09]# pip3 install pymysql#离线安装[root@room9pc01 zzg_pypkgs]# cd pymysql_pkgs/[root@room9pc01 pymysql_pkgs]# lsasn1crypto-0.24.0-py2.py3-none-any.whlcffi-1.11.5-cp36-cp36m-manylinux1_x86_64.whlcryptography-2.4.2-cp34-abi3-manylinux1_x86_64.whlidna-2.7-py2.py3-none-any.whlpycparser-2.19.tar.gzPyMySQL-0.9.2-py2.py3-none-any.whlsix-1.11.0-py2.py3-none-any.whl[root@room9pc01 pymysql_pkgs]# pip3 install *
[root@room9pc01 ~]# yum install -y mariadb-server[root@room9pc01 ~]# systemctl start mariadb[root@room9pc01 ~]# mysql -uroot -ptedu.cnMariaDB [(none)]> CREATE DATABASE nsd1812 DEFAULT CHARSET utf8;
小型企业员工数据库
1、为一个小型企业编写数据库,能够记录员工信息,记录发工资情况。 2、经过调查,需要这些字段:姓名、出生日期、联系方式、部门、工资日、基本工资、奖金、总工资。 3、关系型数据库,应该尽量减少数据冗余(重复的数据)。#关系型数据库字段需要满足数据库范式:
[root@room9pc01 day09]# vim py_mysql.pyimport pymysqlconn = pymysql.connect( #创建连接 host='127.0.0.1', port=3306, user='root', passwd='123456', db='nsd1812', charset='utf8')#游标#• 游标(cursor)就是游动的标识•# 通俗的说,一条sql取出对应n条结果资源的接口/句柄,就是游标,沿着游标可以一次取出一行cursor = conn.cursor()#创建表#create_dep = '''CREATE TABLE departments(#dep_id INT, dep_name VARCHAR(50),#PRIMARY KEY(dep_id)#)'''#create_emp = '''CREATE TABLE employees(#emp_id INT, emp_name VARCHAR(50), email VARCHAR(50), dep_id INT,#PRIMARY KEY(emp_id), FOREIGN KEY (dep_id) REFERENCES departments(dep_id)#)'''#create_sal = '''CREATE TABLE salary(#id INT, date DATE, emp_id INT, basic INT, awards INT,#PRIMARY KEY(id), FOREIGN KEY(emp_id) REFERENCES employees(emp_id)#)'''#cursor.execute(create_dep)#cursor.execute(create_emp)#cursor.execute(create_sal)####################################################插入语句#insert_dep = 'INSERT INTO departments VALUES (%s, %s)'#cursor.executemany(insert_dep, [(1, '人事部')])#deps = [(2, '财务部'), (3, '运维部'), (4, '开发部'),(5, '测试部'), (6, '市场部')]#cursor.executemany(insert_dep, deps)###################################################基础查询#select1 = 'SELECT * FROM departments'#cursor.execute(select1) #游标默认停留在第一行#print(cursor.fetchone()) #读取游标所在行记录的数据(游标自动下移一行)#print('*' * 20)#print(cursor.fetchmany(2)) #读取游标自当前行之后的2条记录(游标自动下移一行)#print('*' * 20)#print(cursor.fetchall()) #读取游标自当前行之后所有行###################################################移动游标#select1 = 'SELECT * FROM departments ORDER BY dep_id'#cursor.execute(select1)#cursor.scroll(2, mode='relative') # 以相对方式向下移动2行记录#print(cursor.fetchone())#print('*' * 20)#cursor.scroll(0, mode='absolute') # 以绝对方式移动到第1行记录#print(cursor.fetchone())##################################################修改#update1 = 'UPDATE departments set dep_name=%s WHERE dep_name=%s'#cursor.execute(update1, ('人力资源部', '人事部'))##################################################删除#delete1 = 'DELETE FROM departments WHERE dep_name=%s'#cursor.execute(delete1, ('市场部',))conn.commit() #对数据库做修改操作,必须要commit(提交)cursor.close()conn.close() #断开连接
sqlalchemy模块
可以操作各种数据库,如mysql、sql server、oracle等。它不需要书写sql语句,可以通过简单的python语法,实现对数据库的增删改查。[root@room8pc16 zzg_pypkgs]# cd sqlalchemy_pkgs/[root@room8pc16 sqlalchemy_pkgs]# pip3 install *
ORM:Object Relationship Mapping(对象关系映射)
创建数据库
MariaDB [nsd1812]> CREATE DATABASE tedu1812 DEFAULT CHARSET utf8;[root@room9pc01 day09]# vim dbconn.pyfrom sqlalchemy import create_engine, Column, Integer, String, ForeignKey, Datefrom sqlalchemy.ext.declarative import declarative_basefrom sqlalchemy.orm import sessionmakerengine = create_engine( #mysql+pymysql://用户名:密码@服务器/数据库?参数 'mysql+pymysql://root:123456@127.0.0.1/tedu1812?charset=utf8', encoding = 'utf8', #echo = True # 在屏幕上输出日志,生产环境中不要使用)#创建ORM的基类Base = declarative_base()Session = sessionmaker(bind=engine)class Department(Base): __tablename__ = 'departments' # 定义库中的表名 dep_id = Column(Integer, primary_key=True) dep_name =Column(String(50), unique=True, nullable=False) #名称唯一,不能为空class Employee(Base): __tablename__ = 'employees' # 定义库中的表名 emp_id = Column(Integer, primary_key=True) emp_name = Column(String(50), nullable=False) #不能为空 email = Column(String(50), unique=True, nullable=False) dep_id = Column(Integer, ForeignKey('departments.dep_id'))class Salary(Base): # 定义库中的表名 __tablename__ = 'salary' id = Column(Integer, primary_key=True) data = Column(Date, nullable=False) emp_id = Column(Integer, ForeignKey('employees.emp_id')) basic = Column(Integer) awards = Column(Integer)if __name__ == '__main__': # 如果库中没有相关的表则创建,有的话不会创建 Base.metadata.create_all(engine)
from dbconn import Session, Department, Employee, Salarysession = Session()############################################ hr = Department(dep_id=1, dep_name='人事部')# finance = Department(dep_id=2, dep_name='财务部')# ops = Department(dep_id=3, dep_name='运维部')# dev = Department(dep_id=4, dep_name='开发部')# qa = Department(dep_id=5, dep_name='测试部')# session.add_all([hr, finance, ops, dev, qa])############################################## wt = Employee(# emp_id=1,# emp_name='王涛',# email='wangtao@qq.com',# dep_id=3# )# zj = Employee(# emp_id=2,# emp_name='张钧',# email='zhangjun@163.com',# dep_id=3# )# sy = Employee(# emp_id=3,# emp_name='苏艳',# email='suyan@qq.com',# dep_id=1# )# wjy = Employee(# emp_id=4,# emp_name='吴计印',# email='wujiying@126.com',# dep_id=4# )# kzw = Employee(# emp_id=5,# emp_name='康志文',# email='kangzhiwen@qq.com',# dep_id=4# )# hzq = Employee(# emp_id=6,# emp_name='胡志强',# email='huzhiqiang@163.com',# dep_id=5# )# lh = Employee(# emp_id=7,# emp_name='李浩',# email='lihao@126.com',# dep_id=2# )# session.add_all([wt, zj, sy, wjy, kzw, hzq, lh])##########################################################qset1 = session.query(Department)#print(qset1) # qset1只是个sql语句,当取具体值的时候,才真正查数据库# qset1.all()取出全部的部门,因为查询的是类名,所以返回所有的实例组成的列表#print('*' * 30)#print(qset1.all())#print('*' * 30)#for dep in qset1: # 遍历实例列表中的每个实例# print('%s: %s' % (dep.dep_id, dep.dep_name))########################################################### qset2 = session.query(Department).order_by(Department.dep_id) #按部门id号排序查询# for dep in qset2: #遍历实例列表中的每个实例# print('%s: %s' % (dep.dep_id, dep.dep_name))########################################################### qset3 = session.query(Employee.emp_name, Employee.email) #查询员工对应邮箱# 查询的参数是字段,返回的结果是元组# for item in qset3:# print(item)# print('*' * 30)# for name, email in qset3:# print('%s: %s' % (name, email))########################################################## qset4 = session.query(Department).order_by(Department.dep_id)[1:4] #查询按部门id号排序后的第2、3、4个部门名称# for dep in qset4: #遍历实例列表中每个实例# print('%s: %s' % (dep.dep_id, dep.dep_name))######################################################### qset5 = session.query(Department).filter(Department.dep_id==2) #查询部门id为2的部门名称# print(qset5)# print(qset5.all()) #all()返回列表# dep = qset5.one() # 返回一个实例,如果返回值不是一个,将报错# print(dep.dep_id, dep.dep_name)############################################################ qset6 = session.query(Department).filter(Department.dep_id>1).filter(Department.dep_id<4) 查询部门id大于1小于4的部门名称# for dep in qset6:# print(dep.dep_id, dep.dep_name, sep=': ') #打印时,部门id和部门名称之间用:隔开####################################################### qset7 = session.query(Employee).filter(Employee.email.like('%@qq.com')) #模糊查询,查询使用qq邮箱的员工姓名# for emp in qset7:# print(emp.emp_name, emp.email)########################################################## qset8 = session.query(Department).filter(Department.dep_id.in_([3, 4])) #查询部门id在[3,4]范围内的部门名称# for dep in qset8:# print(dep.dep_id, dep.dep_name)######################################################## qset9 = session.query(Department).filter(Department.dep_name.isnot(None)) #查询部门名称不是非空的部门#for dep in qset9:# print(dep.dep_id, dep.dep_name)######################################################### query中先写的是Employee,join中要写Department# qset10 = session.query(Employee.emp_name, Department.dep_name).join(Department) #多表查询# for row in qset10:# print(row)######################################################### 修改数据,先找到实例,再给实例的属性重新赋值# qset11 = session.query(Department).filter(Department.dep_name=='人事部')# hr = qset11.one()# hr.dep_name='人力资源部'######################################################### 删除,只要找到实例,然后删除即可# qset12 = session.query(Employee).filter(Employee.emp_id==6)# emp = qset12.one()# session.delete(emp)##########################################################session.commit()session.close()
什么是进程
• 计算机程序只不过是磁盘中可执行的、二进制(或其 它类型)的数据 • 进程(有时被称为重量级进程)是程序的一次执行 • 每个进程都有自己的地址空间、内存以及其它记录其运行轨迹的辅助数据 • 操作系统管理在其上运行的所有进程,并为这些进程公平地分配时间 ######################################################### 什么是线程 • 线程(有时被称为轻量级进程)跟进程有些相似。不同的是,所有的线程运行在同一个进程中,共享相同的运行环境 • 一个进程中的各个线程之间共享同一片数据空间,所以线程之间可以比进程之间更方便地共享数据以及相互通讯 #########################################################什么是forking
进程的生命周期
• 父进程fork出子进程并挂起 • 子进程运行完毕后,释放大部分资源并通知父进程, 这个时候,子进程被称作僵尸进程 • 父进程获知子进程结束,子进程所有资源释放#执行shell的方式
os.fork()方法
os.fork()针对父子进程都有返回值。父进程的返回值是非0值(子进程的ID号),子进程返回0[root@room9pc01 day10]# python3 myfork.pyprint('start...')retval = os.fork() #创建子进程,后续代码将同时在父子进程中执行if retval: print('hello from 父进程')else: print('hello from 子进程')print('hello from both')[root@room9pc01 day10]# python3 myfork.py start...hello from 父进程hello from bothhello from 子进程hello from both
os.fork()编程思路
[root@room9pc01 day10]# vim myfork2.pyimport osfor i in range(3): revtal = os.fork() if not revtal: print('hello world!')[root@room9pc01 day10]# python3 myfork2.py hello world!hello world!hello world!hello world!hello world!hello world!hello world!#################################################[root@room9pc01 day10]# vim myfork2.pyimport osfor i in range(3): revtal = os.fork() if not revtal: print('hello world!') exit() #进程遇到exit将会完全结束[root@room9pc01 day10]# python3 myfork2.py hello world!hello world!hello world!
ping176.130.1.0网段内主机,显示ping的结果
[root@room9pc01 day10]# vim forkping.pyimport osimport subprocessdef ping(host): result = subprocess.run( 'ping -c2 %s &> /dev/null' % host, shell = True ) if result.returncode == 0: print('%s:up' % host) else: print('%s:down' % host)if __name__ == '__main__': ips = ('176.130.1.%s' % i for i in range(1, 255)) for ip in ips: retval = os.fork() if not retval: ping(ip) exit()
僵尸进程
• 僵尸进程没有任何可执行代码,也不能被调度
• 如果系统中存在过多的僵尸进程,将因为没有可用的进程号而导致系统不能产生新的进程 • 对于系统管理员来说,可以试图杀死其父进程或重启系统来消除僵尸进程当子进程已经结束,但是父进程还未结束,父进程又没有处理僵尸进程的代码,僵尸进程就会一直存在,直到父进程工作结束。如果父进程结束了,子进程会变成孤儿进程,它将会被systemd接管。如果systemd发现子进程是僵尸进程,就会处理。
[root@room9pc01 day10]# vim zb.pyimport osimport timeprint('starting...')revtal = os.fork()if revtal: print('in parent, sleeping') time.sleep(30) print('parent done')else: print('in child, sleeping') time.sleep(15) print('child done') exit()#打开两个终端,一个执行以上程序,另一个执行命令watch -n1 ps a(每隔一秒查看系统运行进程)
• 父进程通过os.wait()来得到子进程是否终止的信息
• 在子进程终止和父进程调用wait()之间的这段时间,子进程被称为zombie(僵尸)进程 • 如果子进程还没有终止,父进程先退出了,那么子进程会持续工作。系统自动将子进程的父进程设置为init进程,init将来负责清理僵尸进程 • python可以使用waitpid()来处理子进程 • waitpid()接受两个参数,第一个参数设置为-1,表示与wait()函数相同;第二参数如果设置为0表示挂起父进程,直到子程序退出,设置为1表示不挂起父进程 • waitpid()的返回值:如果子进程尚未结束则返回0,否则返回子进程的PID[root@room9pc01 day10]# vim zb2.pyimport osimport timerevtal = os.fork()if revtal: print('in parent') result = os.waitpid(-1, 0) #挂起父进程,直到子进程结束才会继续 print(result) time.sleep(10) print('parent done')else: print('in child') time.sleep(10) print('child done') exit()#以上程序共执行20秒,运行过程中不存在僵尸进程###########################################################[root@room9pc01 day10]# vim zb3.pyimport osimport timeretval = os.fork()if retval: print('in parent') result = os.waitpid(-1, 1) # 不挂起父进程 print(result) time.sleep(30) print('parent done')else: print('in child') time.sleep(10) print('child done') exit()#以上程序共执行30秒,运行至10秒时出现僵尸进程,再过20秒僵尸进程消失
多线程的动机
• 在多线程(MT)编程出现之前,电脑程序的运行由一个执行序列组成,执行序列按顺序在主机的中央处理器(CPU)中运行 • 无论是任务本身要求顺序执行还是整个程序是由多个子任务组成,程序都是按这种方式执行的 • 即使子任务相互独立,互相无关(即,一个子任务的结果不影响其它子任务的结果)时也是这样 • 如果并行运行这些相互独立的子任务可以大幅度地提升整个任务的效率多线程任务的工作特点
• 它们本质上就是异步的,需要有多个并发事务 • 各个事务的运行顺序可以是不确定的,随机的,不可预测的 • 这样的编程任务可以被分成多个执行流,每个流都有一个要完成的目标 • 根据应用的不同,这些子任务可能都要计算出一个中间结果,用于合并得到最后的结果多线程编程思路
多线程相关模块
• thread和threading模块允许程序员创建和管理线程 • thread模块提供了基本的线程和锁的支持,而threading提供了更高级别、功能更强的线程管理功能 • 推荐使用更高级别的threading模块传递函数给Thread类
• 多线程编程有多种方法,传递函数给threading模块的Thread类是介绍的第一种方法 • Thread对象使用start()方法开始线程的执行,使用join()方法挂起程序,直到线程结束传递可调用类给Thread类
• 传递可调用类给Thread类是介绍的第二种方法 • 相对于一个或几个函数来说,由于类对象里可以使用类的强大的功能,可以保存更多的信息,这种方法更为灵活############################################################
[root@room9pc01 day10]# vim mtping.pyimport threadingimport subprocessdef ping(host): result = subprocess.run( 'ping -c2 %s &> /dev/null' % host, shell = True ) if result.returncode == 0: print('%s:up' % host) else: print('%s:down' % host)if __name__ == '__main__': ips = ('176.130.1.%s' % i for i in range(1, 255)) for ip in ips: t = threading.Thread(target=ping, args=(ip,)) t.start() #运行target(*args)########################################################[root@room9pc01 day10]# vim mtping2.pyimport threadingimport subprocessclass Ping: def __init__(self, host): self.host = host def __call__(self): result = subprocess.run( 'ping -c2 %s &> /dev/null' % self.host, shell = True ) if result.returncode == 0: print('%s:up' % self.host) else: print('%s:down' % self.host)if __name__ == '__main__': ips = ('176.130.1.%s' % i for i in range(1, 255)) for ip in ips: t = threading.Thread(target=Ping(ip)) t.start() #运行target()
#####################################################################
urllib模块 urllib中包括了四个模块爬取网页
• 先需要导入用到的模块:urllib.request • 在导入了模块之后,我们需要使用 urllib.request.urlopen打开并爬取一个网页 • 读取内容常见的有3种方式: (1) read()读取文件的全部内容,与readlines()不同的是,read()会把读取到的内容赋给一个字符串变量。 (2) readlines()读取文件的全部内容,readlines()会把读取到的内容赋值给一个列表变量。 (3) readline()读取文件的一行内容。>>> from urllib import request>>> html = request.urlopen('https://upload-images.jianshu.io/upload_images/12347101-bc5e84e92e23c692.jpg')>>> data = html.read()>>> with open('/tmp/fork.jpg', 'wb') as fobj:... fobj.write(data)... 178500>>>[root@room9pc01 day10]# eog /tmp/fork.jpg //查看爬取网页图片链接地址后写入的图片#############################################################[root@room9pc01 day10]# vim download.pyrom urllib import requestimport sysdef download(url, fname): html = request.urlopen(url) with open(fname, 'wb') as fobj: while True: data = html.read(1024) if not data: break fobj.write(data)if __name__ == '__main__': url = sys.argv[1] fname = sys.argv[2] download(url, fname)[root@room9pc01 day10]# python3 download.py https://upload-images.jianshu.io/upload_images/12347101-bc5e84e92e23c692.jpg /tmp/picture.jpg[root@room9pc01 day10]# eog /tmp/picture.png
模拟客户端访问
当客户机访问服务器时,客户机发送的请求头中使用User-Agent说明自己用的客户端是什么。服务器将在它的访问日志中记录。可以修改请求头,模拟正常的客户端访问,而不是通过python代码的访问。编码
url只允许一部分字符,如果需要用到其他字符,需要对这些字符进行编码。 如果直接使用汉字,将会报错:>>> html = request.urlopen('https://www.baidu.com/s?ie=utf-8&f=3&rsv_bp=1&rsv_idx=2&tn=baiduhome_pg&wd=中国') //报错
需要进行以下转换:
>>> url = 'https://www.baidu.com/s?ie=utf-8&f=3&rsv_bp=1&rsv_idx=2&tn=baiduhome_pg&wd=' + request.quote('中国')>>> url'https://www.baidu.com/s?ie=utf-8&f=3&rsv_bp=1&rsv_idx=2&tn=baiduhome_pg&wd=%E4%B8%AD%E5%9B%BD'
[root@room9pc01 day10]# pip3 install wget>>> import wget>>> wget.download('https://upload-images.jianshu.io/upload_images/12347101-bc5e84e92e23c692.jpg', '/tmp/1234.jpg')100% [........................................................] 178500 / 178500'/tmp/1234.jpg'[root@room9pc01 day10]# eog /tmp/1234.jpg
基础使用介绍
• SSHClient – 创建用于连接ssh服务器的实例 • paramiko.AutoAddPolicy – 设置自动添加主机密钥 • ssh.connect – 连接ssh服务器 • ssh.exec_comand – 在ssh服务器上执行指定命令paramiko实例
• 编写用于实现ssh访问的脚本 – 创建SSHClient实例 – 设置添加主机密钥策略 – 连接ssh服务器 – 执行指定命令 – 在shell命令行中接受用于连接远程服务器的密码以及在远程主机上执行的命令#安装paramiko模块[root@room9pc01 paramiko_pkgs]# lsasn1crypto-0.24.0-py2.py3-none-any.whlbcrypt-3.1.4-cp36-cp36m-manylinux1_x86_64.whlcffi-1.11.5-cp36-cp36m-manylinux1_x86_64.whlcryptography-2.4.2-cp34-abi3-manylinux1_x86_64.whlidna-2.7-py2.py3-none-any.whlparamiko-2.4.2-py2.py3-none-any.whlpyasn1-0.4.4-py2.py3-none-any.whlpycparser-2.19.tar.gzPyNaCl-1.3.0-cp34-abi3-manylinux1_x86_64.whlsix-1.11.0-py2.py3-none-any.whl[root@room9pc01 paramiko_pkgs]# pip3 install *>>> import paramiko>>> ssh =paramiko.SSHClient()>>> ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) # 当远程主机发来密钥时,接受>>> ssh.connect('192.168.8.11', username='root', password='123456')>>> ssh.exec_command('mkdir /tmp/demo')>>> ssh.close()[root@room9pc01 ~]# ssh 192.168.8.11[root@nova01 ~]# ls /tmp/demo/
远程主机执行远程命令(id)
[root@room9pc01 day11]# vim rcmd.pyimport paramikodef rcmd(host, user='root', pwd=None, port=22, command=None): ssh = paramiko.SSHClient() # 实例化SSHClient ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) # 设置自动接受密钥 ssh.connect(host, username=user, password=pwd, port=port) # 连接远程服务器 # exec_command的返回值共三项,它们分别是输入、输出、错误,这三项都是类文件对象。那么它们都有read()方法。 stdin, stdout, stderr = ssh.exec_command(command) # 在远程服务器上执行命令 out = stdout.read() err = stderr.read() if out: # 如果有输出,则打印在屏幕上 print('[\033[32;1m%s\033[0m] OUT:\n%s' % (host, out.decode())) if err: print('[\033[31;1m%s\033[0m] ERROR:\n%s' % (host, err.decode())) ssh.close() # 关闭连接if __name__ == '__main__': rcmd('192.168.8.11', pwd='123456', command='id root; id john')[root@room9pc01 day11]# python3 rcmd.py [192.168.8.11] OUT:uid=0(root) gid=0(root) groups=0(root)[192.168.8.11] ERROR:id: john: no such user
批量远程主机修改密码(多线程):
[root@room9pc01 day11]# vim rcmd.pyimport paramikoimport threadingimport sysimport getpassimport osdef rcmd(host, user='root', pwd=None, port=22, command=None): ssh = paramiko.SSHClient() # 实例化SSHClient ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) # 设置自动接受密钥 ssh.connect(host, username=user, password=pwd, port=port) # 连接远程服务器 stdin, stdout, stderr = ssh.exec_command(command) # 在远程服务器上执行命令 out = stdout.read() err = stderr.read() if out: # 如果有输出,则打印在屏幕上 print('[\033[32;1m%s\033[0m] OUT:\n%s' % (host, out.decode())) if err: print('[\033[31;1m%s\033[0m] ERROR:\n%s' % (host, err.decode())) ssh.close() # 关闭连接if __name__ == '__main__': if len(sys.argv) != 3: print("Usage: %s ipfile 'command_to_execute'" % sys.argv[0]) exit(1) if not os.path.isfile(sys.argv[1]): print('No such file: %s' % sys.argv[1]) exit(2) ipfile = sys.argv[1] command = sys.argv[2] pwd = getpass.getpass() #导入getpass模块后输入密码不回显 with open(ipfile) as fobj: for line in fobj: ip = line.strip() # 把一行文本两端的空白字符移除 t = threading.Thread(target=rcmd, args=(ip,), kwargs={'pwd': pwd, 'command': command}) t.start() # target(*args, **kwargs)[root@room9pc01 day11]# python3 rcmd.py ipaddr.txt 'echo 123456 |passwd --stdin root'Password: #导入getpass模块后隐形输入密码[192.168.8.12] OUT:Changing password for user root.passwd: all authentication tokens updated successfully.[192.168.8.11] OUT:Changing password for user root.passwd: all authentication tokens updated successfully.[192.168.8.10] OUT:Changing password for user root.passwd: all authentication tokens updated successfully.
##############################################################
SMTP概述 • SMTP(Simple Mail Transfer Protocol)即简单邮件传输协议,使用TCP协议25端口 • 它是一组用于由源地址到目的地址传送邮件的规则,由它来控制信件的中转方式 • python的smtplib提供了一种很方便的途径发送电子邮件。它对smtp协议进行了简单的封装SMTP设置邮件
• 标准邮件需要三个头部信息 – From:发件人 – To:收件人 – Subject:主题sendmail方法
• Python SMTP 对象使用 sendmail 方法发送邮件 SMTP.sendmail(from_addr, to_addrs, msg[, mail_options, rcpt_options]) • sendmail方法三个必须的参数有: – 收件人 – 发件人 – 消息主体msg是一个字符串,表示邮件[root@room9pc01 day11]# vim myemail.py #通过本机发送邮件from email.mime.text import MIMETextfrom email.header import Headerfrom smtplib import SMTP#准备邮件message = MIMEText('Python email test\n', 'plain', 'utf8')message['From'] = Header('root', 'utf8')message['To'] = Header('bob', 'utf8')message['Subject'] = Header('py email', 'utf8')#发送邮件smtp = SMTP('127.0.0.1')smtp.sendmail('root', ['root', 'bob'], message.as_bytes())############################################################################[root@room9pc01 day11]# python3 myemail.py[root@room9pc01 day11]# mail #查看邮件>N 49 =?utf8?q?root?=@room Sat May 18 14:34 19/645 "py email"& Message 49:From root@room9pc01.tedu.cn Sat May 18 14:34:37 2019Return-Path:X-Original-To: rootDelivered-To: root@room9pc01.tedu.cnContent-Type: text/plain; charset="utf8"From: root@room9pc01.tedu.cnTo: bob@room9pc01.tedu.cnSubject: py emailDate: Sat, 18 May 2019 14:34:37 +0800 (CST)Status: RPython email test&
案例:通过互联网服务器发送邮件
[root@room9pc01 day11]# vim send_email.pyfrom email.mime.text import MIMETextfrom email.header import Headerfrom smtplib import SMTPimport getpassdef send_email(server, passwd, sender, recievres, subject, message): msg = MIMEText(message, 'plain', 'utf8') msg['From'] = Header(sender, 'utf8') msg['To'] = Header(recievres[0], 'utf8') msg['Subject'] = Header(subject, 'utf8') smtp = SMTP(server) # smtp.starttls() # 如果邮件服务器要求安全连接,打开此注释 smtp.login(sender, passwd) smtp.sendmail(sender, recievres, msg.as_bytes())if __name__ == '__main__': server = 'smtp.163.com' sender = 'liuxe1990@163.com' passwd = getpass.getpass() reciveres = ['liuxe1990@163.com'] subject = '163 mail test' message = 'Hello world!\n Nice to meet you!' send_email(server, passwd, sender, reciveres, subject, message)
################################################################
JSON概述 • JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式 • 易于人阅读和编写,同时也易于机器解析和生成 • 基于JavaScript Programming Language • JSON采用完全独立于语言的文本格式,但是也使用了类似于C语言家族的习惯(包括C, C++, C#, Java,JavaScript, Perl, Python等) • 这些特性使JSON成为理想的数据交换语言JSON结构
• JSON主要有两种结构 – “键/值”对的集合:python中主要对应成字典 – 值的有序列表:在大部分语言中,它被理解为数组dumps方法
• 对编码后的json对象进行decode解码,得到原始数据, 需要使用的json.loads()函数>>> import json>>> adict = {'name': 'bob', 'age': 20}>>> json.dumps(adict)'{"name": "bob", "age": 20}'>>> jdata = json.dumps(adict) # 在网络中发送该字典前,将其转换成json字符串>>> type(jdata)>>> jdata'{"name": "bob", "age": 20}'>>> json.loads(jdata) {'name': 'bob', 'age': 20}>>> mydict = json.loads(jdata) # 接收方收到数据后,将json字符串转换回字典>>> type(mydict) >>> mydict{'name': 'bob', 'age': 20}
案例:天气预报查询
城市代码搜索“中国天气网 城市代码”
如北京:http://www.weather.com.cn/data/sk/101010100.html
http://www.weather.com.cn/data/cityinfo/101010100.html
http://www.weather.com.cn/data/zs/101010100.html
>>> import urllib>>> from urllib import request>>> bj = request.urlopen('http://www.weather.com.cn/data/sk/101010100.html')>>> data = bj.read()>>> datab'{"weatherinfo":{"city":"\xe5\x8c\x97\xe4\xba\xac","cityid":"101010100","temp":"27.9","WD":"\xe5\x8d\x97\xe9\xa3\x8e","WS":"\xe5\xb0\x8f\xe4\xba\x8e3\xe7\xba\xa7","SD":"28%","AP":"1002hPa","njd":"\xe6\x9a\x82\xe6\x97\xa0\xe5\xae\x9e\xe5\x86\xb5","WSE":"<3","time":"17:55","sm":"2.1","isRadar":"1","Radar":"JC_RADAR_AZ9010_JB"}}'>>> import json>>> json.loads(data){'weatherinfo': {'city': '北京', 'cityid': '101010100', 'temp': '27.9', 'WD': '南风', 'WS': '小于3级', 'SD': '28%', 'AP': '1002hPa', 'njd': '暂无实况', 'WSE': '<3', 'time': '17:55', 'sm': '2.1', 'isRadar': '1', 'Radar': 'JC_RADAR_AZ9010_JB'}}
############################################################33
requests模块简介 它是一个网络模块,底层采用urllib3。它把很多http的功能定义成了函数,需要某个功能,只要调用函数即可。 • Requests是用Python语言编写的、优雅而简单的HTTP库 • Requests内部采用来urillib3 • Requests使用起来肯定会比urillib3更简单便捷 • Requests需要单独安装requests特性
• 支持keep-alive的连接池 • 支持通用的域名以及URL地址 • 支持使用cookie • 支持使用类似浏览器的SSL验证 • 文件上传、下载GET和POST
• 通过requests发送一个GET请求,需要在URL里请求 的参数可通过params传递r = requests.get(url="", params={}, headers={}, cookies={})
• 与GET不同的是,POST请求新增了一个可选参数data,需要通过POST请求传递的body里的数据可以通过data传递
r = requests.post(url="", data ={}, params={}, file={}, headers={}, cookies={})
安装request
[root@room9pc01 requests_pkgs]# lscertifi-2018.10.15-py2.py3-none-any.whl requests-2.20.1-py2.py3-none-any.whlchardet-3.0.4-py2.py3-none-any.whl urllib3-1.24.1-py2.py3-none-any.whlidna-2.7-py2.py3-none-any.whl[root@room9pc01 requests_pkgs]# pip3 install *
纯文本
>>> import requests>>> r = requests.get('http://www.sogou.com')>>> r.text
非文本,如图片
>>> r = requests.get('http://k.zol-img.com.cn/sjbbs/7692/a7691515_s.jpg')>>> with open('/tmp/fengguang.jpg', 'wb') as fobj:... fobj.write(r.content)... 118297[root@room9pc01 day11]# eog /tmp/fengguang.jpg
编码
>>> r = requests.get('http://www.weather.com.cn/data/sk/101010100.html')>>> r.json() # 乱码>> r.encoding = 'utf8'>>> r.encoding'utf8'>>> r.json(){'weatherinfo': {'city': '北京', 'cityid': '101010100', 'temp': '27.9', 'WD': '南风', 'WS': '小于3级', 'SD': '28%', 'AP': '1002hPa', 'njd': '暂无实况', 'WSE': '<3', 'time': '17:55', 'sm': '2.1', 'isRadar': '1', 'Radar': 'JC_RADAR_AZ9010_JB'}}
查快递
查询快递的接口(API应用程序编程接口):http://www.kuaidi100.com/query?type=%s&postid=%s
其中type是快递公司的名称,需要到相应的网站上查询,postid是单号
>>> url = 'http://www.kuaidi100.com/query'>>> params = {'type': 'zhongtong', 'postid': '75146444783846'}>>> r = requests.get(url, params= params)>>> r.json(){'message': 'ok', 'nu': '75146444783846', 'ischeck': '0', 'condition': '00', 'com': 'zhongtong', 'status': '200', 'state': '0', 'data': [{'time': '2019-05-18 01:37:00', 'ftime': '2019-05-18 01:37:00', 'context': '快件已发往 泉州转运中心', 'location': None}, {'time': '2019-05-18 01:08:52', 'ftime': '2019-05-18 01:08:52', 'context': '快件已到达 深圳转运中心', 'location': None}, {'time': '2019-05-18 00:48:04', 'ftime': '2019-05-18 00:48:04', 'context': '快件已发往 深圳转运中心', 'location': None}, {'time': '2019-05-17 19:29:53', 'ftime': '2019-05-17 19:29:53', 'context': '广东省深圳市华富公司取件人: 深圳市市场部6() 已收件', 'location': None}]}
Zabbix api概述
• Zabbix API允许你以编程方式检索和修改Zabbix的配置,并提供对历史数据的访问。它广泛用于: – 创建新的应用程序以使用Zabbix – 将Zabbix与第三方软件集成 – 自动执行常规任务JSON-RPC
• Zabbix API是基于Web的API,作为Web前端的一部分提供。它使用JSON-RPC 2.0协议,这意味着两件事: – 该API包含一组独立的方法 – 客户端和API之间的请求和响应使用JSON格式进行编码API结构
• Zabbix API包含许多方法,这些方法都名义上分组为单组的API • 每个方法执行一个特定任务。例如,方法host.create 隶属于 host 这个API ,用于创建新主机 • 历史上,API有时被称为“类” • 大多数API至少包含四种方法: get,、create、update 和 delete ,分别是检索、创建、更新和删除数据。但是某些API提供一套完全不同的一组方法。执行请求
• 设置前端后,,就可以使用远程HTTP请求来调用API。为此,需要向api_jsonrpc.php 位于前端目录中的文件发送HTTP POST请求。 • 请求的 Content-Type 头部必须设置为以下值之一: – application/json-rpc – application/json – application/jsonrequest官方文档页:https://www.zabbix.com/documentation/3.4/zh/manual
这里演示的环境路径是:/var/www/html/ 那么zabbix api地址是:http://192.168.2.55/api_jsonrpc.php通过zabbix提供的API接口,就可以使用python与其交互
import requestsimport jsonurl = 'http://192.168.2.55/api_jsonrpc.php'headers = {'Content-Type': 'application/json-rpc'}#################################### 获取zabbix api版本信息# data = {# "jsonrpc": "2.0", # zabbix固定值# "method": "apiinfo.version", # 官方手册页上查询到的# "params": [], # 参数# "id": 101 # 作业ID,随便指定一个值即可# }####################################工作流程# • 在访问大多数Zabbix中的任何数据之前,需要登录并获取身份验证令牌# • 取得令牌后,访问其他数据只要出示该令牌即可,不需要再进行身份验证# • 通过zabbix api提供的各种方法实现数据的检索、项目的创建等# 获取令牌,使用 user.login 方法登录并获取身份验证令牌#jsonrpc - API使用的JSON-RPC协议的版本; Zabbix API实现JSON-RPC版本2.0#• method - 调用的API方法#• params - 将被传递给API方法的参数#• id - 请求的任意标识符#• auth -用户认证令牌; 因为我们还没有一个,它的设置None#• 如果你正确提供了凭据,API返回的响应将包含用户身份验证令牌# data = {# "jsonrpc": "2.0",# "method": "user.login",# "params": {# "user": "admin",# "password": "123456"# },# "id": 1# }# 7f3dd136e378e30935a83ae0ceea8099###################################检索主机#• 有一个有效的用户身份验证令牌,可以用来访问Zabbix中的数据#• 使用 host.get 方法检索所有已配置主机的ID、主机名和接口。auth 属性设置为获得的身份验证令牌# 获取主机信息# data = {# "jsonrpc": "2.0",# "method": "host.get",# "params": {# "output": "extend",# "filter": {# # "host": [# # "Zabbix server",# # "Linux server"# # ]# }# },# "auth": "7f3dd136e378e30935a83ae0ceea8099",# "id": 1# }#################################### 删除主机# data = {# "jsonrpc": "2.0",# "method": "host.delete",# "params": [# "10255",# ],# "auth": "7f3dd136e378e30935a83ae0ceea8099a",# "id": 1# }#################################### 取出Linux Servers组号# data = {# "jsonrpc": "2.0",# "method": "hostgroup.get",# "params": {# "output": "extend",# "filter": {# "name": [# "Linux servers"# ]# }# },# "auth": "7f3dd136e378e30935a83ae0ceea8099",# "id": 1# }# groupid: 2#################################### 取出Template OS Linux模板的ID# data = {# "jsonrpc": "2.0",# "method": "template.get",# "params": {# "output": "extend",# "filter": {# "host": [# "Template OS Linux",# ]# }# },# "auth": "7f3dd136e378e30935a83ae0ceea8099",# "id": 1# }# 'templateid': '10001'#################################### 创建主机data = { "jsonrpc": "2.0", "method": "host.create", "params": { "host": "nsd1812web1", "interfaces": [ { "type": 1, "main": 1, "useip": 1, "ip": "192.168.4.2", "dns": "", "port": "10050" } ], "groups": [ { "groupid": "2" } ], "templates": [ { "templateid": "10001" } ], "inventory_mode": 0, "inventory": { "macaddress_a": "123456789012", "macaddress_b": "fsdalkjfsdaljfdsa" } }, "auth": "7f3dd136e378e30935a83ae0ceea8099", "id": 1}###################################r = requests.post(url, data=json.dumps(data), headers=headers)print(r.json())
############################################################################
Ansible简介 • Ansible是一个配置管理和配置工具,类似于Chef,Puppet或Salt • 这是一款很简单也很容易入门的部署工具,它使用SSH连接到服务器并运行配置好的任务 • 服务器上不用安装任何多余的软件,只需要开启ssh,所有工作都交给client端的ansible负责准备环境,启动三台虚拟机:
node4.tedu.cn | 192.168.4.4/24 |
---|---|
node5.tedu.cn | 192.168.4.5/24 |
node6.tedu.cn | 192.168.4.6/24 |
名称解析(在192.168.4.254主机上配置,并将配置后的/etc/hosts文件拷贝至以上3台主机对应的目录):
[root@room9pc01 ~]# for i in {4..6}> do> echo -e "192.168.4.$i\tnode$i.tedu.cn\tnode$i" >>/etc/hosts> done
收集主机密钥:
[root@room9pc01 ~]# ssh-keyscan 192.168.4.{4..6} node{4..6} node{4..6}.tedu.cn >> ~/.ssh/known_hosts
安装ansible
[root@room9pc01 ansible_pkg]# lsansible-2.7.2.tar.gzasn1crypto-0.24.0-py2.py3-none-any.whlbcrypt-3.1.4-cp36-cp36m-manylinux1_x86_64.whlcffi-1.11.5-cp36-cp36m-manylinux1_x86_64.whlcryptography-2.4.2-cp34-abi3-manylinux1_x86_64.whlidna-2.7-py2.py3-none-any.whlJinja2-2.10-py2.py3-none-any.whlMarkupSafe-1.1.0-cp36-cp36m-manylinux1_x86_64.whlparamiko-2.4.2-py2.py3-none-any.whlpyasn1-0.4.4-py2.py3-none-any.whlpycparser-2.19.tar.gzPyNaCl-1.3.0-cp34-abi3-manylinux1_x86_64.whlPyYAML-3.13.tar.gzsetuptools-40.6.2-py2.py3-none-any.whlsix-1.11.0-py2.py3-none-any.whl[root@room9pc01 ansible_pkg]# pip3 install *
配置ansible的基础应用环境
[root@room9pc01 ~]# mkdir myansible[root@room9pc01 ~]# cd myansible[root@room9pc01 myansible]# vim ansible.cfg[defaults]inventory = hostsremote_user = root[root@room9pc01 myansible]# vim hosts[dbservers]node4.tedu.cn[webservers]node5.tedu.cnnode6.tedu.cn[root@room9pc01 myansible]# ansible all -m ping -k
使用playbook
• Playbooks是Ansible的配置、部署、编排语言。 • 它们可以被描述为一个需要希望远程主机执行命令的方案,或者一组程序运行的命令集合 • Playbook由一到多个Play组成 • 每个play可以指定哪些主机执行哪些任务 • 执行任务一般通过调用模块来实现Yaml简介
• Playbooks的格式是YAML • 语法做到最小化,意在避免 playbooks 成为一种编程语言或是脚本 • 使用 YAML 是因为它像 XML 或 JSON 是一种利于人们读写的数据格式Yaml语法
• 每一个 YAML 文件都是从一个列表开始 • 列表中的每一项都是一个键值对, 通常它们被称为一个“哈希 或“字典” • 所有的 YAML 文件开始行都应该是 —。这是 YAML 格式的一部分,表明一个文件的开始 • 列表中的所有成员都开始于相同的缩进级别,并且使用一个 "- " 作为开头(一个横杠和一个空格) • 一个字典是由一个简单的 键: 值 的形式组成(冒号后面必须是一个空格)配置VIM
• Yaml的缩进不能使用tab键 • 建议缩进为两个空格 • 为了实现yml文件按tab键缩进两个空格,可以按以下方式对vim进行定制[root@room9pc01 myansible]# vim ~/.vimrcset aiset ts=4set etset encoding=utf8autocmd FileType yaml setlocal sw=2 ts=2 et ai
创建playbook,实现免密登陆
[root@room9pc01 myansible]# ansible-doc autorized_key[root@room9pc01 myansible]# ssh-keygen //生成密钥[root@room9pc01 myansible]# vim auth.yml---- name: user auth key hosts: all tasks: - name: upload root pub key authorized_key: user: root state: present key: "{ { lookup('file', '/root/.ssh/id_rsa.pub') }}"[root@room9pc01 myansible]# ansible-playbook --syntax-check auth.yml //检查yml脚本语法playbook: auth.yml[root@room9pc01 myansible]# ansible-playbook auth.yml -k
通过playbook配置yum
[root@room9pc01 myansible]# mkdir files[root@room9pc01 myansible]# vim files/server.repo[server]name= serverbaseurl=ftp://192.168.4.254/rhel7enabled=1gpgcheck=0[root@room9pc01 myansible]# vim yum.yml---- name: upload yum repo file hosts: all tasks: - name: upload server.repo copy: src: files/server.repo dest: /etc/yum.repos.d/server.repo[root@room9pc01 myansible]# ansible-playbook --syntax-check yum.yml[root@room9pc01 myansible]# ansible-playbook yum.yml[root@room9pc01 myansible]# ansible dbservers -m yum -a "list=mariadb-server" //检查web服务器是否安装了mariadb-server
案例:通过playbook在dbservers上配置mariadb,在webservers上配置apache
[root@room9pc01 myansible]# vim lamp.yml---- name: configure dbservers hosts: dbservers tasks: - name: install mariadb yum: name: mariadb-server state: present - name: start mariadb service: name: mariadb state: started enabled: yes- name: configure webservers hosts: webservers tasks: - name: install apache yum: name: [httpd, php, php-mysql] state: latest - name: start httpd service: name: httpd state: started enabled: yes[root@room9pc01 myansible]# ansible-playbook --syntax-check lamp.yml[root@room9pc01 myansible]# ansible-playbook lamp.yml
#####################################################################################
ansible编程基础ansible python api
官方手册页:https://docs.ansible.com/ ->-> 搜索python api。把python api example中的代码复制粘贴到adhoc.py
命名元组
• 命名元组与普通元组一样,有相同的表现特征,其添加的功能就是可以根据名称引用元组中的项 • collections 模块提供了namedtuple()函数,用于创建自定义的元组数据类型>>> from collections import namedtuple>>> Point = namedtuple('Point', ['x', 'y', 'z'])>>> p1 = Point(10, 20, 25)>>> len(p1)3>>> p1[0]10>>> p1[1:](20, 25)>>> p1.x10>>> p1.y20>>> p1.z25
Ansible常用属性
• from ansible.parsing.dataloader import DataLoader – 用来加载解析yaml文件或JSON内容,并且支持vault的解密 • from ansible.vars.manager import VariableManager – 管理变量的类,包括主机,组,扩展等变量 • from ansible.inventory.manager import InventoryManager – 用于创建主机清单,主机清单的源采用配置文件或是逗号分开主机名字符串 • from ansible.playbook.play import Play – 用于创建play对象,能够通过play_source提供的信息自动创建任务对象 • from ansible.executor.task_queue_manager import TaskQueueManager – 用于处理进程池中的多进程。队列管理器负责加载play策略插件,以便在选定的主机上执行任务 • import ansible.constants as C – 存储ansible一些预定义变量ad-hoc模式
使用TaskQueueManager • 创建TaskQueueManager实例,用于管理子进程、通过主机列表和任务建立对象 • TaskQueueManager需要主机清单参数、主机变量参数、连接选项等 • 不管是执行ad-hoc还是playbook都需要以下模块#!/usr/bin/env pythonimport shutilfrom collections import namedtuplefrom ansible.parsing.dataloader import DataLoaderfrom ansible.vars.manager import VariableManagerfrom ansible.inventory.manager import InventoryManagerfrom ansible.playbook.play import Playfrom ansible.executor.task_queue_manager import TaskQueueManagerimport ansible.constants as Cplay_source = dict(name = "Ansible Play",hosts = 'localhost',gather_facts = 'no',tasks = [dict(action=dict(module='shell', args='mkdir /tmp/mytestdir'), register='shell_out'),])play = Play().load(play_source, variable_manager=variable_manager, loader=loader)
设置参数
• 运行命令时有很多参数要指定,这些参往往很长,可 以先把它们提前定义出来Options = namedtuple('Options', ['connection', 'module_path', 'forks', 'become', 'become_method', 'become_user', 'check', 'diff'])options = Options(connection='local', module_path=[''], forks=10, become=None, become_method=None, become_user=None, check=False, diff=False)loader = DataLoader()passwords = dict(vault_pass='secret')inventory = InventoryManager(loader=loader, sources='localhost,')variable_manager = VariableManager(loader=loader, inventory=inventory)
######################################################################
[root@room9pc01 day03]# vim adhoc.pyimport shutilfrom collections import namedtuplefrom ansible.parsing.dataloader import DataLoaderfrom ansible.vars.manager import VariableManagerfrom ansible.inventory.manager import InventoryManagerfrom ansible.playbook.play import Playfrom ansible.executor.task_queue_manager import TaskQueueManagerimport ansible.constants as C# since API is constructed for CLI it expects certain options to always be set, named tuple 'fakes' the args parsing options object# 此处定义了执行ansible命令时的选项# connection定义连接方式,local表示本机,ssh表示ssh,smart表示自动选择# module_path指定自定义的模块位置# forks指定启动的进程数目# become:如果使用普通用户远程登陆服务器,那么切换成其他用户该怎么切换# check: 不真正执行操作,而是预言操作的结果# diff 显示修改小文件前后的变化Options = namedtuple('Options', ['connection', 'module_path', 'forks', 'become', 'become_method', 'become_user', 'check', 'diff'])options = Options(connection='ssh', module_path=['/to/mymodules'], forks=10, become=None, become_method=None, become_user=None, check=False, diff=False)# initialize needed objects# DataLoader用于查找并解析yaml、json、ini文件,把它们转换成python的数据类型loader = DataLoader() # Takes care of finding and reading yaml, json and ini files# 设置密码passwords = dict()# create inventory, use path to host config file as source or hosts in a comma separated string# 定义主机清单,可以将各主机用逗号隔开的字符串,也可以指定文件路径列表# inventory = InventoryManager(loader=loader, sources='localhost,')inventory = InventoryManager(loader=loader, sources=['myansible/hosts'])# variable manager takes care of merging all the different sources to give you a unifed view of variables available in each context# 变量管理器variable_manager = VariableManager(loader=loader, inventory=inventory)# create datastructure that represents our play, including tasks, this is basically what our YAML loader does internally.play_source = dict( name = "Ansible Play", # play的名字 hosts = 'webservers', # 指定在哪些主机上执行命令 gather_facts = 'no', tasks = [ dict(action=dict(module='shell', args='ls'), register='shell_out'), dict(action=dict(module='debug', args=dict(msg='{ {shell_out.stdout}}'))) ] )# Create play object, playbook objects use .load instead of init or new methods,# this will also automatically create the task objects from the info provided in play_source# 通过上面的配置,创建一个playplay = Play().load(play_source, variable_manager=variable_manager, loader=loader)# Run it - instantiate task queue manager, which takes care of forking and setting up all objects to iterate over host list and tasks# 创建任管理器,执行playtqm = Nonetry: tqm = TaskQueueManager( inventory=inventory, variable_manager=variable_manager, loader=loader, options=options, passwords=passwords, ) result = tqm.run(play) # most interesting data for a play is actually sent to the callback's methodsfinally: # we always need to cleanup child procs and the structres we use to communicate with them if tqm is not None: tqm.cleanup() # Remove ansible tmpdir shutil.rmtree(C.DEFAULT_LOCAL_TMP, True)########################################################################################[root@room9pc01 myansible]# vim adhoc2.py import shutilfrom collections import namedtuplefrom ansible.parsing.dataloader import DataLoaderfrom ansible.vars.manager import VariableManagerfrom ansible.inventory.manager import InventoryManagerfrom ansible.playbook.play import Playfrom ansible.executor.task_queue_manager import TaskQueueManagerimport ansible.constants as Cdef adhoc(sources=None, hosts=None, module=None, args=None): Options = namedtuple('Options', ['connection', 'module_path', 'forks', 'become', 'become_method', 'become_user', 'check', 'diff']) options = Options(connection='ssh', module_path=['/to/mymodules'], forks=10, become=None, become_method=None, become_user=None, check=False, diff=False) loader = DataLoader() passwords = dict() inventory = InventoryManager(loader=loader, sources=sources) variable_manager = VariableManager(loader=loader, inventory=inventory) play_source = dict( name="Ansible Play", # play的名字 hosts=hosts, # 指定在哪些主机上执行命令 gather_facts='no', tasks=[ dict(action=dict(module=module, args=args), register='shell_out'), ] ) play = Play().load(play_source, variable_manager=variable_manager, loader=loader) tqm = None try: tqm = TaskQueueManager( inventory=inventory, variable_manager=variable_manager, loader=loader, options=options, passwords=passwords, ) result = tqm.run(play) finally: if tqm is not None: tqm.cleanup() shutil.rmtree(C.DEFAULT_LOCAL_TMP, True)if __name__ == '__main__': sources = ['hosts'] hosts = 'webservers' module = 'shell' args = 'useradd alice' adhoc(sources, hosts, module, args)[root@room9pc01 myansible]# python3 adhoc2.py##############################################################################[root@room9pc01 myansible]# vim runpb.pyfrom collections import namedtuplefrom ansible.parsing.dataloader import DataLoaderfrom ansible.vars.manager import VariableManagerfrom ansible.inventory.manager import InventoryManagerfrom ansible.executor.playbook_executor import PlaybookExecutorOptions = namedtuple( 'Options', [ 'connection', 'remote_user', 'ask_sudo_pass', 'verbosity', 'ask_pass', 'module_path', 'forks', 'become', 'become_method', 'become_user', 'check', 'listhosts', 'listtasks', 'listtags', 'syntax', 'sudo_user', 'sudo', 'diff' ])options = Options( connection='smart', remote_user=None, ask_pass=None, sudo_user=None, forks=5, sudo=None, ask_sudo_pass=False, verbosity=5, module_path=None, become=None, become_method=None, become_user=None, check=False, diff=False, listhosts=None, listtasks=None, listtags=None, syntax=None)loader = DataLoader()passwords = dict()def runpb(pb_path, sources): inventory = InventoryManager(loader=loader, sources=sources) variable_manager = VariableManager(loader=loader, inventory=inventory) playbook = PlaybookExecutor( playbooks=pb_path, inventory=inventory, variable_manager=variable_manager, loader=loader, options=options, passwords=passwords ) result = playbook.run() return resultif __name__ == '__main__': runpb(pb_path=['lamp.yml'], sources=['hosts'])
#####################################################################################################
ansible模块开发官方模块
• Ansible官方已经提供了大量模块,在编写模块之前,可以查看是否已有现成模块 • 官方已发布模块 – http://docs.ansible.com/ansible/modules.html • 官方正在开发的模块 – https://github.com/ansible/ansible/labels/module模块执行流程
• 将模块文件读入内存,然后添加传递给模块的参数,最后将模块中所需要的类添加到内存,由zipfile压缩后,再由base64进行编码,写入到模板文件内 • 通过默认的连接方式(一般是ssh),ansible连接到远程主机,创建临时目录,并关闭连接 • 打开另外一个ssh连接,将模板文件以sftp方式传送到刚刚创建的临时目录中,写完后关闭连接 • 打开一个ssh连接将任务对象赋予可执行权限,执行成功后关闭连接 • 最后,ansible将再打开一个新连接来执行模块,并删除临时目录及其所有内容 • 模块的结果是从标准输出stdout中获取json格式的字符串。ansible将解析和处理此字符串[root@room9pc01 myansible]# mkdir library //自定义模块目录[root@room9pc01 myansible]# cd library/[root@room9pc01 library]# pwd/root/桌面/myansible/library[root@room9pc01 library]# export ANSIBLE_LIBRARY=/root/桌面/myansible/library //使用 ANSIBLE_LIBRARY环境变量指定自定义模块存放路径
编写模块,用于在远程主机上将一个文件拷贝到目标位置
[root@room8pc16 library]# vim mycopy.pyimport shutilfrom ansible.module_utils.basic import AnsibleModuledef main(): module = AnsibleModule( argument_spec=dict( yuan=dict(required=True, type='str'), mubiao=dict(required=True, type='str') ) ) shutil.copy(module.params['yuan'], module.params['mubiao']) module.exit_json(changed=True)if __name__ == '__main__': main()[root@room8pc16 myansible]# ansible dbservers -m mycopy -a "yuan=/etc/hosts mubiao=/tmp/zj.txt"[root@node4 ~]# ls /tmp/zj.txt /tmp/zj.txt
案例:
[root@room9pc01 myansible]# ansible dbservers -m download -a "url=http://192.168.4.5/people.jpg local=/home/"
执行时报错,提示没有wget模块。原因是ansible会将download模块拷贝到远程dbservers服务器,在dbservers上执行download,但是dbservers上没有安装wget模块。因此需要在dbservers上安装wget模块
[root@room9pc01 ~]# pip3 download wget --trusted-host mirrors.163.com //下载wget模块[root@room9pc01 ~]# scp wget-3.2.zip 192.168.4.4:/tmp[root@node4 ~]# ls /tmpzj.txtwget-3.2.zip[root@node4 ~]# cd /tmp[root@node4 tmp]# unzip wget-3.2.zip [root@node4 tmp]# cd wget-3.2/[root@node4 wget-3.2]# python setup.py install //dbservers上安装wget模块[root@node4 wget-3.2]# python>>> import wget>>>########################################################################[root@room9pc01 myansible]# cd library/[root@room9pc01 library]# vim download.pyimport wgetfrom ansible.module_utils.basic import AnsibleModuledef main(): module = AnsibleModule( argument_spec=dict( url=dict(required=True, type='str'), local=dict(required=True, type='str') ) ) wget.download(module.params['url'], module.params['local']) module.exit_json(changed=True)if __name__ == '__main__': main()[root@room9pc01 library]# cd ..[root@room9pc01 myansible]# ansible dbservers -m download -a "url=http://192.168.4.5/people.jpg local=/home/" //ansible执行自定义download模块node4.tedu.cn | CHANGED => { "changed": true}[root@node4 ~]# ls /home //图片下载到dbservers对应目录下lisi people.jpg
ansible-cmdb模块
用于将ansible收集下来的主机信息转换成html页面[root@room9pc01 ansible-cmdb_pkgs]# pip3 install *[root@room9pc01 ansible-cmdb_pkgs]# vim /usr/local/bin/ansible-cmdb 8 PY_BIN=$(which python3) # 第8行改为python3,默认是python2[root@room9pc01 myansible]# ansible all -m setup --tree /tmp/out # 通过setup模块收集远程主机信息并保存到/tmp/out/目录[root@room9pc01 myansible]# ansible-cmdb /tmp/out/ > /tmp/hosts.html # 利用ansible-cmdb分析/tmp/out/下的文件,生成html[root@room9pc01 myansible]# firefox /tmp/hosts.html
Git简介
• Git是一个开源的分布式版本控制系统,用于敏捷高效地处理任何或小或大的项目。 • Git 是 Linus Torvalds 为了帮助管理 Linux 内核开发而开发的一个开放源码的版本控制软件。 • Git 与常用的版本控制工具 CVS, Subversion 等不同,它采用了分布式版本库的方式,不必服务器端软件支持。git工作流程
工作区、暂存区和版本库
• 工作区:就是你在电脑里能看到的目录 • 暂存区:英文叫stage, 或index。一般存放在 “.git目录下” 下的index文件(.git/index)中,所以我们把暂存区有时也叫作索引(index) • 版本库:工作区有一个隐藏目录.git,这个不算工作区,而是Git的版本库 准备一台虚拟机(192.168.110),需要可以访问互联网(NAT)[root@localhost ~]# ifconfig virbr0 down[root@localhost ~]# brctl delbr virbr0[root@web33 ~]# ifdown eth0; ifup eth0[root@localhost ~]# ping www.baidu.comPING www.a.shifen.com (14.215.177.39) 56(84) bytes of data.64 bytes from 14.215.177.39 (14.215.177.39): icmp_seq=1 ttl=54 time=14.0 ms64 bytes from 14.215.177.39 (14.215.177.39): icmp_seq=2 ttl=54 time=13.1 ms64 bytes from 14.215.177.39 (14.215.177.39): icmp_seq=3 ttl=54 time=62.3 ms^C
git安装及配置
[root@localhost ~]# yum -y install git[root@localhost ~]# git config --global user.name 'liuxe1990'[root@localhost ~]# git config --global user.email 'liuxe1990@163.com'[root@localhost ~]# git config --global core.editor vim[root@localhost ~]# git config --listuser.name=liuxe1990user.email=liuxe1990@163.comcore.editor=vim[root@localhost ~]# cat ~/.gitconfig[user] name = liuxe1990 email = liuxe1990@163.com[core] editor = vim
初始化
[root@localhost ~]# git init myproject初始化空的 Git 版本库于 /root/myproject/.git/[root@localhost ~]# ls -A myproject/.git
[root@localhost ~]# mkdir devops[root@localhost ~]# cd devops/[root@localhost devops]# echo 'hello world!
' > index.html[root@localhost devops]# git init .初始化空的 Git 版本库于 /root/devops/.git/[root@localhost devops]# ls -A.git index.html
git应用
[root@localhost devops]# git status //查看状态# 位于分支 master## 初始提交## 未跟踪的文件:# (使用 "git add..." 以包含要提交的内容)## index.html提交为空,但是存在尚未跟踪的文件(使用 "git add" 建立跟踪)[root@localhost devops]# git status -s //简要信息?? index.html[root@localhost devops]# git add . //将目录下所有内容加入暂存区,开始跟踪[root@localhost devops]# git status# 位于分支 master## 初始提交## 要提交的变更:# (使用 "git rm --cached ..." 撤出暂存区)## 新文件: index.html#[root@localhost devops]# git status -sA index.html[root@localhost devops]# git rm --cached index.html //撤出暂存区rm 'index.html'[root@localhost devops]# git status# 位于分支 master## 初始提交## 未跟踪的文件:# (使用 "git add ..." 以包含要提交的内容)## index.html提交为空,但是存在尚未跟踪的文件(使用 "git add" 建立跟踪)[root@localhost devops]# git status -s?? index.html[root@localhost devops]# git add . //重新加入暂存区[root@localhost devops]# git status -sA index.html[root@localhost devops]# git commit //确认至版本库,需要写日志[master(根提交) b951f4a] myproject 1 file changed, 1 insertion(+) create mode 100644 index.html[root@localhost devops]# git status# 位于分支 master无文件要提交,干净的工作区[root@localhost devops]# git status -s# 接下来的常规则应用,就是修改代码、加入跟踪、确认至版本库[root@localhost devops]# echo ' nice to meet you
' >> index.html [root@localhost devops]# cp /etc/hosts .[root@localhost devops]# git status -s M index.html?? hosts[root@localhost devops]# git status# 位于分支 master# 尚未暂存以备提交的变更:# (使用 "git add..." 更新要提交的内容)# (使用 "git checkout -- ..." 丢弃工作区的改动)## 修改: index.html## 未跟踪的文件:# (使用 "git add ..." 以包含要提交的内容)## hosts修改尚未加入提交(使用 "git add" 和/或 "git commit -a")[root@localhost devops]# git add .[root@localhost devops]# git status -sA hostsM index.html[root@localhost devops]# git status # 位于分支 master# 要提交的变更:# (使用 "git reset HEAD ..." 撤出暂存区)## 新文件: hosts# 修改: index.html#[root@localhost devops]# git commit -m "modify index.html, add hosts"[master 666d37a] modify index.html, add hosts 2 files changed, 3 insertions(+) create mode 100644 hosts[root@localhost devops]# git status# 位于分支 master无文件要提交,干净的工作区
恢复误删除的文件
[root@localhost devops]# rm -rf *[root@localhost devops]# ls[root@localhost devops]# git status# 位于分支 master# 尚未暂存以备提交的变更:# (使用 "git add/rm..." 更新要提交的内容)# (使用 "git checkout -- ..." 丢弃工作区的改动)## 删除: hosts# 删除: index.html#修改尚未加入提交(使用 "git add" 和/或 "git commit -a")[root@localhost devops]# git checkout -- * //恢复删除的文件[root@localhost devops]# lshosts index.html
如果真想删除文件应该采用以下方式:
[root@localhost devops]# git rm hostsrm 'hosts'[root@localhost devops]# lsindex.html[root@localhost devops]# git status -sD hosts[root@localhost devops]# git status# 位于分支 master# 要提交的变更:# (使用 "git reset HEAD..." 撤出暂存区)## 删除: hosts#[root@localhost devops]# git reset HEAD hosts重置后撤出暂存区的变更:D hosts[root@localhost devops]# git status# 位于分支 master# 尚未暂存以备提交的变更:# (使用 "git add/rm ..." 更新要提交的内容)# (使用 "git checkout -- ..." 丢弃工作区的改动)## 删除: hosts#修改尚未加入提交(使用 "git add" 和/或 "git commit -a")[root@localhost devops]# git checkout -- hosts[root@localhost devops]# lshosts index.html
删除文件的完整过程
[root@localhost devops]# git rm hostsrm 'hosts'[root@localhost devops]# git status -sD hosts[root@localhost devops]# git commit -m "del hosts"[master 7b9c7ba] del hosts 1 file changed, 2 deletions(-) delete mode 100644 hosts
改名、移动
[root@localhost devops]# cp /etc/passwd .[root@localhost devops]# lsindex.html passwd[root@localhost devops]# git add .[root@localhost devops]# git commit -m "add passwd"[master 5e83b42] add passwd 1 file changed, 40 insertions(+) create mode 100644 passwd[root@localhost devops]# git mv passwd mima[root@localhost devops]# git status -sR passwd -> mima[root@localhost devops]# git commit -m "rename passwd -> mima"[master acc5fef] rename passwd -> mima 1 file changed, 0 insertions(+), 0 deletions(-) rename passwd => mima (100%) [root@localhost devops]# lsindex.html mima[root@localhost devops]# git status# 位于分支 master无文件要提交,干净的工作区
将HEAD指针指向以前的某个提交就可以切换到以前的某个状态
[root@localhost devops]# git log //查看所有提交commit acc5fef62c2cf2001c168a6d80efa49596db43d0Author: liuxe1990Date: Tue May 21 14:55:30 2019 +0800 rename passwd -> mimacommit 5e83b42744978a7400c2e5a59f8c1a03e7e4f974Author: liuxe1990 Date: Tue May 21 14:54:13 2019 +0800 add passwdcommit 7b9c7bacdf6c8a8a477d299e2fa93bc968954071Author: liuxe1990 Date: Tue May 21 14:46:00 2019 +0800 del hostscommit 666d37a0add3ca459ab642ddd434cc7fdbfb1115Author: liuxe1990 Date: Tue May 21 14:36:54 2019 +0800 modify index.html, add hosts[root@localhost devops]# git checkout 666d37a0add3ca459ab642ddd434cc7fdbfb1115 //切换至以前某一版本[root@localhost devops]# ls //查看切换至某版本后git仓库文件hosts index.html[root@localhost devops]# git checkout master //返回最新的提交之前的 HEAD 位置是 666d37a... modify index.html, add hosts切换到分支 'master'[root@localhost devops]# lsindex.html mima
查看分支
[root@localhost devops]# git branch* master[root@localhost devops]# lsindex.html mima
新建分支
[root@localhost devops]# git branch fn1[root@localhost devops]# git branch fn1* master //*表示所在分支
在fn1分支中编写程序
[root@localhost devops]# git checkout fn1切换到分支 'fn1'[root@localhost devops]# git branch* fn1 master
在fn1分支中编写程序
[root@localhost devops]# cp ~/anaconda-ks.cfg .[root@localhost devops]# git add .[root@localhost devops]# git commit -m "fn1 add anaconda"[fn1 f0cbaae] fn1 add anaconda 1 file changed, 65 insertions(+) create mode 100644 anaconda-ks.cfg[root@localhost devops]# ls //查看fn1分支文件anaconda-ks.cfg index.html mima
切换回master分支
[root@localhost devops]# git checkout master切换到分支 'master'[root@localhost devops]# ls //主干并不存在fn1分支编写的程序anaconda-ks.cfg index.html mima
合并fn1分支到主干
[root@localhost devops]# git merge fn1更新 acc5fef..f0cbaaeFast-forward anaconda-ks.cfg | 65 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 anaconda-ks.cfg[root@localhost devops]# ls //fn1分支编写的程序合并到主干anaconda-ks.cfg index.html mima
fn1分支使命已经达成,可以删除
[root@localhost devops]# git branch -d fn1已删除分支 fn1(曾为 f0cbaae)。[root@localhost devops]# git branch* master
#################################################################
gitlab服务器 为了实现多人共享代码,可以将git内容上传到gitlab服务器。 准备一台虚拟机(gitlab:192.168.122.124),要求可以访问外网,内存4G以上。安装docker[root@room9pc01 ~]# ls /var/ftp/docker/ //配置宿主机(192.168.122.1)为docker网络yum源docker-engine-1.12.1-1.el7.centos.x86_64.rpm docker_images.zip repodatadocker-engine-selinux-1.12.1-1.el7.centos.noarch.rpm docker_uname.txt[root@gitlab ~]# yum -y install docker-engine //安装docker[root@gitlab ~]# systemctl start docker //启动docker[root@gitlab ~]# systemctl enable docker[root@gitlab ~]# docker load < gitlab_zh.tar //导入gitlab镜像[root@gitlab ~]# vim /etc/ssh/sshd_config //修改docker宿主机的ssh端口为202217 Port 2022[root@gitlab ~]# systemctl restart sshd[root@room9pc01 ~]# ssh -p2022 192.168.122.124 //测试宿主机ssh连接#启动容器[root@gitlab ~]# docker run -d -h gitlab --name gitlab -p 443:443 -p 80:80 -p 22:22 --restart always -v /srv/gitlab/config:/etc/gitlab -v /srv/gitlab/logs:/var/log/gitlab -v /srv/gitlab/data:/var/opt/gitlab gitlab_zh:latest[root@gitlab ~]# docker ps //当状态是healthy才能正常工作
配置gitlab服务器
访问http://192.168.122.137,初始化密码必须是8位以上,复杂。登陆时的用户名是root。gitlab中重要的概念
1、新建组,名为devops。群组等级为公开。
2、为devops组中的成员创建用户zzg。新建用户的时候,不能创建密码。用户建立好之后,修改用户,可以为其加密码http://192.168.122.124/admin/users。 3、新建项目devops。新建的用户zzg是新项目的主程序员。可见等级为公开。项目创建完成后,点击左下角的“折叠边栏”=>“设置”=>“成员”=>邀请上一步创建的用户,角色是“主程序员”。新建的用户上传代码
上传代码有两种方式,一种是http的方式,这种方式,每次上传代码都需要填写用户名和密码。另一种是通过ssh实现免密登陆 ssh免密登陆需要将用户的公钥上传:[root@localhost devops]# ssh-keygen -t rsa -C "project01@tedu.cn" -b 4096 //生成密钥[root@localhost devops]# cat ~/.ssh/id_rsa.pub //查看生成的公钥
在用户的设置页面中,点击左侧的ssh密钥,把公钥内容粘贴进去。
[root@localhost devops]# git remote rename origin old-origin //如果出现以下报错,忽略error: 不能重命名配置小节 'remote.origin' 到 'remote.old-origin'[root@localhost devops]# git remote add origin git@192.168.122.124:devops/devops.git[root@localhost devops]# git push -u origin --all
Jenkins概述
• Jenkins是由java编写的一款开源软件 • 作为一款非常流行的CI(持续集成)工作,用于构建和测试各种项目 • Jenkins 的主要功能是监视重复工作的执行,例如软件工程的构建或在 cron下设置的 jobs持续集成
• 持续集成(CI)是当下最为流行的应用程序开发实践方式 • 程序员在代码仓库中集成了修复bug、新特性开发或是功能革新 • CI工具通过自动构建和自动测试来验证结果。这可以检测到当前程序代码的问题,迅速提供反馈Jenkins特点
• 简单、可扩展、用户界面友好 • 支持各种SCM(软件配置管理)工具,如SVN、GIT、CVS等 • 能够构建各种风格的项目 • 可以选择安装多种插件 • 跨平台,几乎可以支持所有的平台准备jenkins服务器(配置IP、主机名、yum、安装java),要求可以访问外网
[root@jenkins ~]# ifconfig virbr0 down[root@jenkins ~]# brctl delbr virbr0[root@jenkins ~]# ifdown eth0; ifup eth0[root@jenkins ~]# ping www.baidu.comPING www.a.shifen.com (14.215.177.39) 56(84) bytes of data.64 bytes from 14.215.177.39 (14.215.177.39): icmp_seq=1 ttl=54 time=14.0 ms64 bytes from 14.215.177.39 (14.215.177.39): icmp_seq=2 ttl=54 time=13.1 ms64 bytes from 14.215.177.39 (14.215.177.39): icmp_seq=3 ttl=54 time=62.3 ms^C
安装jenkins
[root@jenkins ~]# lsanaconda-ks.cfg jenkins-2.138.2-1.1.noarch.rpm initial-setup-ks.cfg[root@jenkins ~]# yum -y install jenkins-2.138.2-1.1.noarch.rpm[root@jenkins ~]# systemctl start jenkins[root@jenkins ~]# systemctl enable jenkins
打开http://192.168.122.73:8080。安装插件选择自定义=>无。不用创建管理员帐号,使用admin登陆即可。登陆后,将管理员的密码改掉。
[root@jenkins ~]# cat /var/lib/jenkins/secrets/initialAdminPassworde191d2f592f24baab2b58bd9956d1f62 #将该密码信息粘贴到初始化页面
在Jenkins上安装插件
配置方法详见:https://blog.csdn.net/you227/article/details/81076032
清华大学插件地址:https://mirrors.tuna.tsinghua.edu.cn/jenkins/updates/update-center.json
在可选插件中安装git parameter。
因为现在使用的是jenkins低版本,无法安装插件,插件需要更高的版本。所以下载新版本:http://mirrors.jenkins-ci.org/redhat/jenkins-2.177-1.1.noarch.rpm
[root@jenkins ~]# wget http://mirrors.jenkins-ci.org/redhat/jenkins-2.177-1.1.noarch.rpm[root@jenkins ~]# lsanaconda-ks.cfg jenkins-2.177-1.1.noarch.rpmjenkins-2.138.2-1.1.noarch.rpm[root@jenkins ~]# systemctl stop jenkins[root@jenkins ~]# yum -y update jenkins-2.177-1.1.noarch.rpm[root@jenkins ~]# systemctl start jenkins
项目流程
[root@localhost ~]# git init myweb初始化空的 Git 版本库于 /root/myweb/.git/[root@localhost ~]# cd myweb/[root@localhost myweb]# echo 'My Web Site
' > index.html[root@localhost myweb]# git commit -m "web 1.0"[root@localhost myweb]# git tag 1.0 //将当前状态标记为1.0版本[root@localhost myweb]# echo '2nd version
' >> index.html[root@localhost myweb]# git add .[root@localhost myweb]# git commit -m "web 2.0"[master f2e300a] web 2.0 1 file changed, 1 insertion(+)[root@localhost myweb]# git tag 2.0
[root@localhost myweb]# git remote rename origin old-origin[root@localhost myweb]# git remote add origin git@192.168.122.124:devops/myweb.git[root@localhost myweb]# git push -u origin --all[root@localhost myweb]# git push -u origin --tags
[root@jenkins ~]# ls /var/lib/jenkins/workspace/myweb[root@jenkins ~]# cat /var/lib/jenkins/workspace/myweb/index.htmlMy Web Site
完善jenkins
[root@jenkins ~]# yum -y install httpd[root@jenkins ~]# systemctl start httpd[root@jenkins ~]# systemctl enable httpd[root@jenkins ~]# mkdir -p /var/www/html/deploy/pkgs# /var/www/html/deploy/: 保存livever、lastver,即当前版本和前一个版本的版本号# /var/www/html/deploy/pkgs/: 保存软件压缩包和它的md5值[root@jenkins ~]# chown -R jenkins.jenkins /var/www/html/deploy/
增加构建步骤=> Excute shell:
deploy_dir=/var/www/html/deploypkgs_dir=/var/www/html/deploy/pkgscp -r myweb-$webver $pkgs_dir # 将下载的软件目录拷贝到web服务器目录cd $pkgs_dirrm -rf myweb-$webver/.git # 删除版本库文件tar czf myweb-$webver.tar.gz myweb-$webver # 打包压缩md5sum myweb-$webver.tar.gz | awk '{print $1}' > myweb-$webver.tar.gz.md5 # 计算并保存md5值rm -rf myweb-$webver # 删除程序目录cd $deploy_dir[ -f livever ] && cat livever > lastver # 将当前版本内容写到前一版本文件echo $webver > livever # 更新当前版本
[root@jenkins ~]# ls /var/www/html/deploy/livever pkgs[root@jenkins ~]# cat /var/www/html/deploy/livever 1.0[root@jenkins ~]# ls /var/www/html/deploy/pkgs/myweb-1.0.tar.gz myweb-1.0.tar.gz.md5
[root@jenkins ~]# ls /var/www/html/deploy/pkgs/myweb-1.0.tar.gz myweb-1.0.tar.gz.md5 myweb-2.0.tar.gz myweb-2.0.tar.gz.md5[root@jenkins ~]# cat /var/www/html/deploy/livever 2.0[root@jenkins ~]# cat /var/www/html/deploy/lastver 1.0
#################################################################
编写python工具,实现代码自动上线将物理主机作为应用服务器,对外提供服务:
/var/www/download/:用于存储下载的软件包
/var/www/deploy/:用于存储当前版本文件和解压后的软件包
/var/www/html/link:指向软件目录的链接
[root@jenkins ~]# mkdir /var/www/download[root@jenkins ~]# mkdir /var/www/deploy
编写自动化部署python程序
[root@jenkins ~]# vim bushu_web.pyimport requestsimport wgetimport osimport hashlibimport tarfiledef has_new_ver(ver_url, ver_fname): # 如果本地没有版本文件,则有新版本 if not os.path.isfile(ver_fname): return True # 取出本地版本 with open(ver_fname) as fobj: local_ver = fobj.read() # 如果本地版本和远程版本不一致,则有新版本 r = requests.get(ver_url) if local_ver != r.text: return True return Falsedef check_pkgs(md5_url, pkg_path): # 取出远程md5值 r = requests.get(md5_url) remote_md5 = r.text.strip() m = hashlib.md5() with open(pkg_path, 'rb') as fobj: while True: data = fobj.read(4096) if not data: break m.update(data) local_md5 = m.hexdigest() if local_md5 == remote_md5: return True return Falsedef deploy(pkg_path, deploy_dir, version): dest = '/var/www/html/link' src = '/var/www/deploy/myweb-%s' % version tar = tarfile.open(pkg_path, 'r') tar.extractall(path=deploy_dir) tar.close() # 如果目标链接已存在,先删除,否则无法创建 if os.path.exists(dest): os.remove(dest) # 创建链接 os.symlink(src, dest)if __name__ == '__main__': # 如果没有新版本则退出 ver_url = 'http://192.168.122.73/deploy/livever' ver_fname = '/var/www/deploy/livever' download_dir = '/var/www/download/' deploy_dir = '/var/www/deploy/' if not has_new_ver(ver_url, ver_fname): print('没有发现新版本') exit(1) # 有新版本,下载软件压缩包 r = requests.get(ver_url) version = r.text.strip() pkg_url = 'http://192.168.122.73/deploy/pkgs/myweb-%s.tar.gz' % version wget.download(pkg_url, download_dir) # 检查软件包是否损坏 pkg_path = pkg_url.split('/')[-1] pkg_path = os.path.join(download_dir, pkg_path) md5_url = pkg_url + '.md5' if not check_pkgs(md5_url, pkg_path): print('软件包已损坏') os.remove(pkg_path) exit(2) # 如果软件压缩包是完好的,则部署,并更新软件版本文件 deploy(pkg_path, deploy_dir, version) with open(ver_fname, 'w') as fobj: fobj.write(r.text)
#开发人员编写代码
[root@localhost myweb]# vim index.html[root@localhost myweb]# cat index.htmlMy Web Site
2nd version
4th version
[root@localhost myweb]# git add .[root@localhost myweb]# git commit -m "web4.0" //提交新版本[root@localhost myweb]# git tag 4.0[root@localhost myweb]# git push -u origin --all[root@localhost myweb]# git push -u origin --tags Total 0 (delta 0), reused 0 (delta 0)To git@192.168.122.124:devops/myweb.git * [new tag] 4.0 -> 4.0
#运维人员基于4.0版本构建项目
运行自动化部署脚本[root@jenkins ~]# python3 bushu_web.py 100% [..............................................................] 279 / 279
浏览器访问http://192.168.122.73/link/,页面更新至4.0版本
超文本
• Web 是一个超文本文件的集合 • 超文本文件是 Web 的基本组成单元,也称为网页或HTML文档、Web页等,通常是以.html或.htm为后缀的文件 • Web页之间通过超文本中的超级链接组织在一起什么是 HTML
• HTML(HyperText Markup Language):超文本标记语言,一种纯文本类型的语言 – 使用带有尖括号的“标记”将网页中的内容逐一标识出来 • 用来设计网页的标记语言 • 用该语言编写的文件,以 .html 或者 .htm 为后缀 • 由浏览器解释执行 • HTML 页面上,可以嵌套用脚本语言编写的程序段,如:VBScript,JavaScript元素,也叫标签、标记。分为封闭型的双标记和单标记。
大型网站建站
前端:html / css / js(javascript)
后端:python / php / javamy html
1 | 1 | |
1 | 1 | |
姓名 | 阶段 | 课程 |
张三 | 一 | Linux |
李四 | 二 | 运维 |
段落元素
段落元素
以上网页效果如下图
myhtml2
以上网页效果如下图:
CSS:层叠样式表、级联样式表
CSS分类:样式表的应用,注意的两个方面
样式表的特性
选择器
id 选择器
• id 选择器以一种独立于文档元素的方式来指定样式 • 它仅作用于 id 属性的值 • 语法为: – 选择器前面需要有一个 # 号 – 选择器本身则为文档中某个元素的 id 属性的值伪类选择器
• 伪类用于向某些选择器添加特殊的效果 • 使用冒号(:)作为结合符,结合符左边是其他选择器,右边是伪类 – 选择器 : 伪类选择器 • 链接伪类 – : link ,适用于尚未访问的链接 – : visited ,适用于访问过的链接 • 动态伪类,用于呈现用户操作 – :hover,适用于鼠标悬停在 HTML 元素时 – :active,适用于 HTML 元素被激活时 – :focus,适用于 HTML 元素获取焦点时#pycharm编辑mycss.htmlmy css 2号标题
hello world!
以上html文件页面效果如下:
#pycharm编辑mycss2.htmlmy css2 2号标题
这是一段测试文字这是一个div标签,采用内联样式
以上html文件页面效果如下图
#pycharm新建mycss.css样式文件* { margin: 0; /*外边距*/ padding: 0; /*内边距*/}body{ color: blue;}.c1 { text-align: center;}.c2, .c3 { text-align: right;}a:link { /*访问之前的样式*/ color: red;}a:hover{ /*鼠标悬停时的样式*/ font-size: 50px;}a:visited { color: gray;}#id1 { background: rebeccapurple;}p { color: forestgreen;}div{ font-size: 100px;}hr { color: red;}################################################################################pycharm编辑mycss3.htmlmy css3 booksetdocker第一段
第二段
第三段
第一行
第二行
以上html文件页面效果如下图: