学python历程中
蛋蛋人物学python历程中
相比shell而言,python执行效率高。可以跨平台,可移植性好,标准统一,在不同平台执行的结果一致。而且python模块丰富,应用场景更丰富。python采用强制缩进的方式使得代码具有极佳的可读性。但是它是一门解释型语言,跟编译型语言相比。执行效率较慢。
python的安装。。。【rpm安装、源码安装】(略)。
pyenv安装(pyenv是一个python多版本管理工具,当服务器上存在不同版本的python项目时,使用pyenv可以做到多版本的隔离使用(类似虚拟化,每个项目使用不同版本互不影响。安装地址:https://github.com/pyenv/pyenv)
第一个python程序:
使用vi或vim直接写:
# vim 1.py #!/usr/bin/python # 声明类型,指明解释器命令路径
#-*- coding: utf-8 -*- # 指定字符格式为utf-8(可以打印中文),python3不用再指定了print "hellow world" # python2的写法,python3执行会报错
print("hello world") # python3的写法,python2也可以执行
print("哈哈") # python2指定了utf-8字符,那么这里就可以使用中文执行方法一:
# python 1.py执行方法二:# chmod 755 1.py# ./1.py # 需要有执行权限和声明类型才能这样执行
使用python命令(默认版本)交互写
# python3>>> print ("hello world")
hello world
>>> exit() --使用exit()或quit()或ctrl+d键来退出
print打印
Python程序由多个逻辑行构成,一个逻辑行不一定为一个物理行(人眼看到的行)
显示行连接:
在物理行后跟反斜杠, 代表此行连接下一行代码
隐式行连接: () [] {}
在括号里换行会自动行连接
字符串需要用引号引起来,单引双引三引都可以。
变量:在内存中开辟一块空间,临时保存数据。通俗的说变量名就是存储空间的名字,通过这个名字来访问和存储空间中的数据。
变量的特点
- 可以反复存储数据
- 可以反复取出数据
- 可以反复更改数据
变量的命名规则
- 变量名只能是字母、数字或下划线的任意组合
- 变量名的第一个字符不能是数字
- 变量名要有见名知义的效果
变量的创建:在python中,每一个变量在使用前都必须赋值,变量赋值后,变量就创建成功了
两个变量值的交换:
其它语言中可以借助于第三个变量来交换变量 a 和b 的值
python中可以直接交换,两个变量的值.
变量的数据类型是由值决定的.
Python基本数据类型分类
- 数字
- int 整型(1, 2, -1, -2)
- float 浮点型(34.678)
- bool 布尔型(True/False)
- complex 复数(4+3J, 不应用于常规编程,这种仅了解一下就好
- 字符串
- str 单引号,双引号和三引号内表示的内容为字符串 “hello world" “12345”
- 列表
- list 使用中括号表示 [1, 2, 3, 4]
- 元组
- tuple 使用小括号表示 (1, 2, 3, 4)
- 字典
- dict 使用大括号表示,存放key-value键值对 {“a”:1, “b”:2, “c”:3}
- 集合
- set 也使用大括号表示,但与字典有所不同 {1, 2, 3, 4}
类型的转换
转换函数 | 说明 |
---|---|
int(xxx) | 将xxx转换为整数 |
float(xxx) | 将xxx转换为浮点型 |
str(xxx) | 将xxx转换为字符串 |
list(xxx) | 将xxx转换为列表 |
tuple(xxx) | 将xxx转换为元组 |
dict(xxx) | 将xxx转换为字典 |
set(xxx) | 将xxx转换为集合 |
chr(xxx) | 把整数[0-255]转成对应的ASCII码 |
ord(xxx) | 把ASCII码转成对应的整数[0-255] |
输入输出
输入
用python3中可以使用input()函数等待用户的输入(python2中为raw_input()函数)
示例:
name = input("what is your name: ")
age = input("what is your age: ") # input输入的直接就为str类型,不需要再str()转换了print(name, "你" + age + "岁了")
name = input("请输入你的名字:")
age = int(input("请输入你的年龄:"))print(name+",你5年后为"+str(age+5)+"岁了")
小结: 用单引号,双引号,三引号, str()函数转换的和input()函数输入的都为字符串类型。
输出
普通输出
输出用print()
示例:
print("="*10) # 表示连续打印10个=符号
print("1-系统")
print("2-数据库")
print("3-quit")
print("="*10)
或者
print("="*10)
print('''1-系统 # 使用''' '''符号来换行
2-数据库
3-quit''')
print("="*10)
结果一样,如下:
格式化输出
很多语言里使用printf来进行格式化打印
python里不用printf,但也可以用 % 表示格式化操作符
操作符 | 说明 |
---|---|
%s | 字符串 |
%d | 整数 |
%f | 浮点数 |
%% | 输出 % |
示例:
name = "daniel"
age = str(20)# 字符串拼接的写法
print(name+",你有"+age+"岁了")# 两种格式化输出的写法
print("%s,你有%s岁了" % (name, age))
print("{},你有{}岁了".format(name, age))
name = input("请输入你的名字:")
age = int(input("请输入你的年龄:"))print(name+",你5年后为"+str(age+5)+"岁了")
print("%s,你5年后为%d岁了" % (name, age+5))
print("{},你5年后为{}岁了".format(name, age+5))
小结:
- %s或%d相当于是一个占位符,按顺序一一对应后面()里的变量(需要类型对应)
- {}也相当于是一个占位符,按顺序一一对应后面format()里的变量。这种写法的好处是不用对应类型
- 这三种写法为格式化输出, 简单来说就是为了解决
调用变量值和其它字符串结合场景
的方式。
运算符
算术运算符(常用)
算术运算符 | 描述 | 实例 |
---|---|---|
+ | 加法 | 1+2=3 |
- | 减法 | 5-1=4 |
* | 乘法 | 3*5=15 |
/ | 除法 | 10/2=5 |
// | 整除 | 10//3=3 不能整除的只保留整数部分 |
** | 求幂 | 2**3=8 |
% | 取余(取模) | 10%3=1 得到除法的余数 |
赋值运算符
赋值运算符 | 描述 | 实例 |
---|---|---|
= | 简单的赋值运算符,下面的全部为复合运算符 | c =a + b 将a + b的运算结果赋值给c |
+= | 加法赋值运算符 | a += b 等同于 a = a + b |
-= | 减法赋值运算符 | a -= b 等同于 a = a - b |
*= | 乘法赋值运算符 | a *= b 等同于 a = a * b |
/= | 除法赋值运算符 | a /= b 等同于 a = a / b |
//= | 整除赋值运算符 | a //= b 等同于 a = a // b |
**= | 求幂赋值运算符 | a ** = b 等同于 a = a ** b |
%= | 取余(取模)赋值运算符 | a %= b 等同于 a = a % b |
比较运算符(常用)
比较运算符 | 描述 | 实例 |
---|---|---|
== | 等于(注意与=赋值运算符区分开),类似shell里的-eq | print(1==1) 返回True |
!= | 不等于,类似shell里的-ne | print(2!=1) 返回True |
<> | 不等于(同 != ) | print(2<>1) 返回True |
> | 大于, 类似shell里的-gt | print(2>1) 返回True |
< | 小于, 类似shell里的-lt | print(2<1) 返回False |
>= | 大于等于 类似shell里的-ge | print(2>=1) 返回True |
<= | 小于等于 类似shell里的-le | print(2<=1) 返回False |
print(type(2<=1)) # 结果为bool类型,所以返回值要么为True,要么为False.
逻辑运算符
逻辑运算符 | 逻辑表达式 | 描述 |
---|---|---|
and | x and y | x与y都为True,则返回True;x与y任一个或两个都为False,则返回False |
or | x or y | x与y任一个条件为True,则返回True |
not | not x | x为True,返回False; x为False,返回True |
成员运算符
在后面讲解和使用序列(str,list,tuple) 时,还会用到以下的运算符。
成员运算符 | 描述 |
---|---|
in | x 在 y 序列中 , 如果 x 在 y 序列中返回 True; 反之,返回False |
not in | x 不在 y 序列中 , 如果 x 不在 y 序列中返回 True; 反之,返回False |
在SQL语句里也有in和not in运算符;如(没有学习mysql的话,后面会学习了就知道了)
mysql > select * from xxx where name not in ('tom','john');
身份运算符
身份运算符 | 描述 | 实例 |
---|---|---|
is | is 是判断两个标识符是不是引用自一个对象 | x is y, 类似 id(x) == id(y) , 如果是同一个对象则返回 True,否则返回 False |
is not | is not 是判断两个标识符是不是引用自不同对象 | x is not y ,类似 id(a) != id(b)。如果不是同一个对象则返回结果 True,否则返回 False。 |
is 与 == 区别:
is 用于判断两个变量引用对象是否为同一个(同一个内存空间), == 用于判断引用变量的值是否相等。
a = [1,2,3] # 后面会学到,这是列表
b = a[:] # 后面会学到,这是列表的切片
c = a
print(b is a) # False
print(b == a) # Trueprint(c is a) # True
print(c == a) # True
位运算符
大家还记得IP地址与子网掩码的二进制算法吗?
这里的python位运算符也是用于操作二进制的。
位运算符 | 说明 |
---|---|
& | 对应二进制位两个都为1,结果为1 |
| | 对应二进制位两个有一个1, 结果为1, 两个都为0才为0 |
^ | 对应二进制位两个不一样才为1,否则为0 |
>> | 去除二进制位最右边的位,正数上面补0, 负数上面补1 |
<< | 去除二进制位最左边的位,右边补0 |
~ | 二进制位,原为1的变成0, 原为0变成1 |
运算符的优先级
常用的运算符中: 算术 > 比较 > 逻辑 > 赋值
示例: 请问下面的结果是什么?
result = 3 - 4 >= 0 and 4 * (6 - 2) > 15
print(result)result = -1 >= 0 and 16 > 15 # 算术运算后
result = False and True # 比较运算后
result = False # 逻辑运算后
判断语句
生活中的判断
shell里的判断语句格式
shell单分支判断语句:
if 条件;then执行动作一
fi
shell双分支判断语句:
if 条件;then执行动作一
else执行动作二
fi
shell多分支判断语句:
if 条件一;then执行动作一
elif 条件二;then执行动作二
elif 条件三;then执行动作三
else执行动作四
fi
shell里的case语句
case "变量" in 值1 )执行动作一;;值2 )执行动作二;;值3 )执行动作三;;* )执行动作四
esac
python里的判断语句格式
python单分支判断语句:
if 条件: # 条件结束要加:号(不是;号)执行动作一 # 这里一定要缩进(tab键或四个空格),否则报错# 没有fi结束符了,就是看缩进
python双分支判断语句:
if 条件:执行动作一
else: # else后面也要加:执行动作二
python多分支判断语句:
if 条件一:执行动作一
elif 条件二: # elif 条件后面都要记得加:执行动作二
elif 条件三:执行动作三
else:执行动作四
shell里有个case语句, 也可以实现多分支判断。但是python里没有case语句.
if嵌套
if嵌套也就是if里还有if,你可以无限嵌套下去,但层次不宜过多(嵌套层次过多的话程序逻辑很难读,也说明你的程序思路不太好,应该有很好的流程思路来实现)
比如下面的格式:
if 条件一:if 条件二:执行动作一 # 条件一,二都为True,则执行动作一else:执行动作二 # 条件一True,条件二False,则执行动作二执行动作三 # 条件一True,条件二无所谓,则执行动作三
else:if 条件三:执行动作四 # 条件一False,条件三True,则执行动作四else:执行动作五 # 条件一False,条件三False,则执行动作五执行动作六 # 条件一False,条件二,三无所谓,则执行动作六
执行动作七 # 与if里的条件无关,执行动作七
示例:
name = input("输入你的名字:")
sex = input("输入你的性别:")
age = int(input("输入你的年龄:"))if sex == "男":if age >= 18:print("{},sir".format(name))else:print("{},boy".format(name))
elif sex == "女":if age >= 18:print("{},lady".format(name))else:print("{},girl".format(name))
else:print("性别有误")
如果输入的年龄不为纯数字,怎么判断**
shell里判断age变量的值是否为纯数字的做法
echo $age |grep -E ^[0-9]+$if [ $? -eq 0 ];thenecho "纯数字"
elseecho "不是纯数字"
fi
python中判断是否为纯数字
name = input("输入你的名字:")
sex = input("输入你的性别:")
age = input("输入你的年龄:")if not age.isdigit(): # 判断是否纯数字print("输入的年龄不是数字")exit() # 类似shell里的exit,退出整个程序
else:age = int(age)if sex == "男":if age >= 18:print("{},sir".format(name))else:print("{},boy".format(name))
elif sex == "女":if age >= 18:print("{},lady".format(name))else:print("{},girl".format(name))
else:print("性别有误")
循环语句
while循环
只要满足while指定的条件,就循环。
while 循环的基本格式
while 条件:条件满足时候:执行动作一条件满足时候:执行动作二......
注意: 没有像shell里的do…done来界定循环体,所以要看缩进。
示例: 打印1-10
i = 1
while i < 11:print(i, end=" ")i += 1
示例:打印1-100的奇数
i = 1
while i < 101:if i % 2 == 1:print(i, end=" ")i += 1
跳出循环语句
continue 跳出本次循环,直接执行下一次循环
break 退出循环,执行循环体外的代码
exit() 退出python程序,可以指定返回值
示例: 猜数字小游戏
import random # 导入随机数模块(后面会专门讲模块的使用,这里先拿来用用)num = random.randint(1, 100) # 取1-100的随机数(包括1和100)while True:gnum = int(input("你猜:"))if gnum > num:print("猜大了")elif gnum < num:print("猜小了")else:print("猜对了")breakprint("领奖")
for循环
for循环遍历一个对象(比如数据序列,字符串,列表,元组等),根据遍历的个数来确定循环次数。
for循环可以看作为定循环,while循环可以看作为不定循环。
如:
6点-18点,每个小时整点循环(定了次数,每天都会有6点-18点)
当有太阳,每个小时整点循环(不定次数,天气和季节都会影响是否有太阳)
循环嵌套
前面在讲if时解释过嵌套是什么,这里我们再来做一个总结: if,while,for都可以互相嵌套。
示例: 打印九九乘法表
for line in range(1, 10):for field in range(1, line+1):print("{}*{}={}".format(field, line, field*line), end="\t")print()```
for也可以结合else使用,如下面判断质数(只能被1和自己整除的数)的例子
num = int(input("输入一个大于2的整数"))for i in range(2, num):if num % i == 0:print("不是质数")break
else: # 这里的else是与for在同一列上,不与if在同一列。print("是质数")```
字符串-str
字符串的定义与输入
在python中,用引号引起来的都是字符串。还有input函数输入的, str()函数转换的等。
string1 = "hello"
string2 = 'hello'
string3 = """hello
python"""
string4 = '''hello
world'''
string5 = input("input anything: ")
string6 = str(18)print(isinstance(string3, str)) # isinstance函数可以用来判断数据是否为某一个数据类型,返回值为True或False
字符串的拼接与格式化输出
name = "daniel"str1 = "===" + name + "==="
str2 = "===%s===" % (name)
str3 = "==={}===".format(name)print(str1)
print(str2)
print(str3) # 三种方法结果一样
name = "张三"print("你好,", name) # 这种打印的结果与后三种稍有不同
print("你好," + name)
print("你好,%s" % (name))
print("你好,{}".format(name))
小结: 变量在引号里得不到值,只能得到变量名,要得到变量的**值**,就格式化输出(使用上面三种方法之一即可)
字符串的下标(重点)
字符串,列表,元组都属于序列(sequence),所以都会有下标。
什么是下标(index)?
示例: 将字符串遍历打印
str1 = "hello,python"for i in str1: # 直接用for循环遍历字符串print(i, end=" ")
示例: 将字符串遍历打印,并对应打印出下标
str1 = "hello,python"for i, j in enumerate(str1): # 枚举print(i, j)
示例: 将字符串遍历打印,并对应打印出顺序号(从1开始,不是像下标那样从0开始)
str1 = "abcde"for index, i in enumerate(str1):print("第{}个字符是{}".format(index+1, i))index = 0
for i in str1:print("第{}个字符是{}".format(index+1, i))index += 1
字符串的切片,倒序
问题: 打印字符串的第3-5个字节
shell里的方法:
# echo abcdefg | cut -c3-5
cde# echo abcdefg | awk '{print substr($0,3,3)}' # substr()是awk里的截取函数
cde# echo abcdefg | sed -r 's/(..)(...)(.*)/\2/' # sed的分域操作
cde
python里循环的方法(但不建议):
str1 = "abcdefg"for index, i in enumerate(str1):if 5 > index > 1:print(i, end="")
上面的方法仅作拓宽思路用,python里实现字符串的截取操作首先就应该想到**切片**的方法。
字符串,列表,元组都属于序列,所以都可以**切片(也就是我们shell里所说的截取**)。
a = "abcdefg"
print(a[0:3]) # 取第1个到第3个字符(注意,不包含第4个)
print(a[2:5]) # 取第3个到第5个字符(注意,不包含第6个)print(a[0:-1]) # 取第1个到倒数第2个(注意:不包含最后一个)
print(a[1:]) # 取第2个到最后一个
print(a[:]) # 全取
print(a[0:5:2]) # 取第1个到第5个,但步长为2(结果为ace)
print(a[::-1]) # 字符串的倒序(类似shell里的rev命令)
扩展比较难理解的切片方法(仅作了解):
str1 = "abcdefghijk"print(str1[2:0:-1])
print(str1[:3:-1])
小结:
下标: 就类似一个字符串里的字符编号,通过下标可以很轻松找到第N个字符。
循环遍历: 可以把字符串里所有字符都循环操作。
切片: 通过下标得到字符串内一定范围的字符。
格式: 字符串[起始下标:结束下标(但不包含): 步长]
, 里面都可以用负数. 如果步长为负数,表示从右到左来进行切片。
建议: 格式不用记那么详细,主要记忆一个思路。 跟shell里${}截取
和awk的substr()函数截取
思路是类似,但具体格式有差别。
字符串的常见操作
abc = "hello,nice to meet you"print(len(abc)) # 调用len()函数来算长度 (常用)
print(abc.__len__()) # 使用字符串的__len__()方法来算字符串的长度print(abc.capitalize()) # 整个字符串的首字母大写
print(abc.title()) # 每个单词的首字母大写
print(abc.upper()) # 全大写
print(abc.lower()) # 全小写
print("HAHAhehe".swapcase()) # 字符串里大小写互换print(abc.center(50,"*")) # 一共50个字符,字符串放中间,不够的两边补*
print(abc.ljust(50,"*")) # 一共50个字符,字符串放左边,不够的右边补*
print(abc.rjust(50,"*")) # 一共50个字符,字符串放右边,不够的左边补*print(" haha\n".strip()) # 删除字符串左边和右边的空格或换行 (常用,处理文件的换行符很有用)
print(" haha\n".lstrip()) # 删除字符串左边的空格或换行
print(" haha\n".rstrip()) # 删除字符串右边的空格或换行print(abc.endswith("you")) # 判断字符串是否以you结尾 类似于正则里的$ (常用)
print(abc.startswith("hello")) # 判断字符串是否以hello开始 类似于正则里的^ (常用)print(abc.count("e")) # 统计字符串里e出现了多少次 (常用)print(abc.find("nice")) # 找出nice在字符串的第1个下标,找不到会返回-1
print(abc.rfind("e")) # 找出最后一个e字符在字符串的下标,找不到会返回-1
print(abc.index("nice")) # 与find类似,区别是找不到会有异常(报错)
print(abc.rindex("e")) # 与rfind类似,区别是找不到会有异常(报错)print(abc.isalnum()) # 判断是否为数字字母混合(可以有大写字母,小写字母,数字任意混合)
print(abc.isalpha()) # 判断是否全为字母(分为纯大写,纯小写,大小写混合三种情况)
print(abc.isdigit()) # 判断是否为纯数字
print(abc.islower()) # 测试结果为:只要不包含大写字母就返回True
print(abc.isupper()) # 测试结果为:只要不包含小写字母就返回True
print(abc.isspace()) # 判断是否为全空格print(abc.upper().isupper()) # 先把abc字符串全转为大写,再判断是否为全大写字母,结果为True
小建议: 字符串的方法非常多,新手第一次容易晕,但请不要一个一个去记忆,在我们的基础课程里主要会用到后面几个标为常用的方法,可以先记住这几个就好。其它的要用的话再查。
示例: 使用input输入字符,判断输入是数字,纯大写字母,纯小写字母,大小写混合字母,还是其它
char = input("输入: ")if char.isdigit():print("输入的是数字")
elif char.isalpha():if char.isupper():print("输入的是纯大写字母") # 在isalpha()里面再判断isupper()则是判断是否为纯大写了elif char.islower():print("输入的是纯小写字母")else:print("输入是大小写混合字母")
else:print("输入的是其它")
更加复杂的分类示例:
char = input("请输入字符串:")if char.isalnum():if char.isdigit():print("纯数字")elif char.isalpha():if char.isupper():print("纯大写")elif char.islower():print("纯小写")else:print("大小写字母混合")else:print("大写字母与数字混合,小写字母与数字混合")
else:if len(char) == 0: # 或者改为 if char == "":print("空字符串")elif char.isspace():print("字符串为空格或多个空格")else:print("其它")
字符串的其它操作
数字,字符串,元组是不可变数据类型(改变值的话是在内存里开辟新的空间来存放新值,原内存地址里的值不变).下面的操作可以替换字符串的值,但原字符串没有改变。
列表,字典,集合是**可变数据类型**(在内存地址不变的基础上可以改变值)
aaa = "hello world,itcast"
bbb = aaa.replace('l','L',2) # 从左到右,把小写l替换成大写L,并且最多只替换2个
print(aaa) # 原值不变
print(bbb) # 改变的值赋值给了bbb变量,所以这里看到的是替换后的值
字符串的分列操作
print("root:x:0:0".split(":")) # 以:为分隔号,分隔成列表 (常用)
print("root:x\n:0:0".splitlines()) # 以\n为分隔号,分隔成列表
字符串的join操作
print(" ".join(['df', '-h'])) # 把列表里的元素以前面引号里的分隔符合成字符串
小结:
引号引起来的,input()函数输入的,str()转换的都为字符串类型
多个字符串可以做拼接和格式化输出
字符串属于**序列,属于序列的数据类型都有下标**,可以循环遍历,可以切片,可以拼接
字符串属于**不可变数据类型**,不可变数据类型没有增,删,改这种操作
十一、列表-list
列表是一种基本的**序列**数据结构(字符串和元组也属于序列)
列表是一种**可变数据类型**(再次强调数字,字符串,元组是不可变类型)
列表的创建
使用**中括号括起来,里面的数据称为元素**。可以放同类型数据,也可以放不同类型数据,但通常是同类型。
os_list = ["rhel", "centos", "suse", "ubuntu"]print(os_list)
列表的下标
和字符串一样,见字符串的下标章节
示例:
os_list = ["rhel", "centos", "suse", "ubuntu"]for i, j in enumerate(os_list):print(i, j)
列表的切片,倒序
和字符串一样,见字符串的切片,倒序章节
示例:
os_list = ["rhel", "centos", "suse", "ubuntu"]print(os_list[1:3])
print(os_list[1:-1])
print(os_list[::2]) # 打印第1个和第3个元素
print(os_list[::-1]) # 通过切片来倒序
示例: 验证列表为可变数据类型
os_list = ["rhel", "centos", "suse", "ubuntu"]print(id(os_list))
os_list.reverse() # 通过reverse操作来倒序,并且是直接改变原数据
print(os_list) # 列表的元素倒序了
print(id(os_list)) # 在倒序前后id()函数得到的值一样,说明内存地址不变,但值变了(可变数据类型)
列表的常见操作
列表的增删改查操作
列表是可变数据类型,可以进行增,删,改操作
os_list = ["rhel", "centos", "suse"]
# 增
os_list.append("ubuntu") # 在列表最后增加一个元素
print(os_list)
os_list.insert(2, "windowsxp") # 插入到列表,变为第三个
print(os_list)
# 改
os_list[2] = "windows10" # 修改第三个
print(os_list)
# 删
os_list.remove("windows10") # 以元素来删除
os_list.pop(2) # 以下标来删除,还可以del os_list[2]来删除print(os_list)
# 查
print(os_list[0]) # 通过下标就可以
# 其它操作
print(os_list.count("centos")) # 统计元素出现的次数
print(os_list.index("centos")) # 找出centos在os列表里的位置os_list.reverse() # 反转列表
print(os_list)
os_list.sort() # 排序列表,按ASCII编码来排序
print(os_list)
os_list.clear() # 清除列表所有元素,成为空列表,不是删除列表
print(os_list)
小建议: 重点记住列表的增,删,改,查这几种操作.
列表合并,拼接
list1 = ["haha", "hehe", "heihei"]
list2 = ["xixi", "hoho"]list1.extend(list2) # list1 += list2也可以,类似字符串拼接
print(list1)
练习: 下面是四个选修课报名的列表,请问张三报名了几门选修课?
math = ["张三", "田七", "李四", "马六"]
english = ["李四", "王五", "田七", "陈八"]
art = ["陈八", "张三", "田七", "赵九"]
music = ["李四", "田七", "马六", "赵九"]# 方法1
print(math.count("张三")+english.count("张三")+art.count("张三")+music.count("张三"))# 方法2
list1 = math + english + art + music
print(list1.count("张三"))
双列表
name_list = ["zhangsan", "lisi", "wangwu", "maliu"]
salary = [18000, 16000, 20000, 15000]for i in range(len(name_list)):print("{}的月收入为{}元".format(name_list[i].ljust(10," "),salary[i]))for index, name in enumerate(name_list):print("{}的月薪为{}元".format(name,salary[index]))
问题: 请查找lisi的工资
1, 先查找lisi的下标
2, 找下标对应在salary里的元素print(salary[name_list.index("lisi"))])
问题:找出工资最高的人叫啥?
1, 先找最高工资是多少
2, 再最高工资的下标
3, 找下标对应在name_list里的元素
print(name_list[salary.index(max(salary))])
列表嵌套
列表里可以嵌套列表,也可以嵌套其它数据类型。
emp = [["zhangsan", 18000], ["lisi", 16000], ["wangwu", 20000], ["maliu", 15000]]for i in range(len(emp)):print("{}的月收入为{}元".format(emp[i][0].ljust(10," "),emp[i][1]))for index, i in enumerate(emp):print("%s的月薪为%d元" % (i[0],i[1]))
问题:查找lisi的工资
for i in emp:if i[0] == "lisi":print(i[1])
问题:找出最高工资的人叫啥?
方法一:
emp = [["zhangsan", 18000], ["lisi", 16000], ["wangwu", 20000], ["maliu", 15000]]emp2 = []
for i in emp:emp2.append(i[1])print(emp[emp2.index((max(emp2)))][0])
方法二:
emp = [["zhangsan", 18000], ["lisi", 16000], ["wangwu", 20000], ["maliu", 15000]]max = 0
name_list = []
for i in emp: if i[1] > max: # 每次循环得到的工资与max比name_list.insert(0, i[0])max = i[1] # 把两个比较结果里大的值赋值给maxprint("{}的工资最高,为{}元。".format(name_list[0], max))
方法三:
emp = [["zhangsan", 18000], ["lisi", 16000], ["wangwu", 20000], ["maliu", 15000]]num = 0
index = 0
for i in emp:if i[1]>num:num=i[1]index=emp.index(i)print(emp[index])
小结:
列表的创建: 由**中括号**包含元素
列表属于**序列, 有下标**,可以循环遍历,可以切片,可以拼接
列表属于可变数据类型; 有增,删,改的操作
元组-tuple
元组就相当于是**只读的列表**;因为只读,所以没有append,remove,修改等操作方法.
它只有两个操作方法:count,index
元组,字符串,列表都属于**序列.所以元组也有下标**, 也可以切片,也可以使用for来遍历, 也可以两个元组拼接.
元组的创建与操作
列表使用中括号,元组使用小括号。
示例:
tuple1 = (1, 2, 3, 4, 5, 1, 7)print(type(tuple1))print(tuple1.index(3)) # 打印3这个元素在元组里的下标
print(tuple1.count(1)) # 统计1这个元素在元组里共出现几次
print(tuple1[2:5]) # 切片tuple1[5] = 6 # 修改元组会报错
**元组是只读的,不代表元组里任何数据不可变。如果元组里有列表,那么列表里是
emp2 = (["zhangsan", 18000], ["lisi", 16000], ["wangwu", 20000], ["maliu", 15000])emp2[0].append("haha") # 元组里面的列表可以修改print(emp2)可变的。
字典-dict
字典:是一种key:value(键值对)类型的数据,它是**无序的(没有像列表那样的索引,下标)**. (备:现在也有特殊方法实现的有序字典,有兴趣课后可以搜索了解一下)
它是通过key来找value,查找速度快;
如果key相等, 会**自动去重(去掉重复值), 也就是说dict中没有重复的key。但是值是可以相等的**。
字符串,列表,元组属于序列,所以有下标,可以切片. 字典和集合是无序的,没有下标,不能切片。
字典属于**可变数据类型**
字典的创建
dict1 = {'stu01': "zhangsan",'stu02': "lisi",'stu03': "wangwu",'stu04': "maliu",
}print(type(dict1))
print(len(dict1))
print(dict1)
字典的常见操作
字典是**可变数据类型**,所以可以做增删改操作
# 增
dict1["stu05"] = "tianqi" # 类似修改,如果key值不存在,则就增加
print(dict1)# 改
dict1["stu04"] = "马六" # 类似增加,如果key值存在,则就修改
print(dict1)
# 字典的增加与修改的写法是一样的,区别就在于key是否已经存在# 查
print(dict1["stu01"]) # 如果key值不存在,会返回keyerror错误
print(dict1.get("stu01")) # 这种取值方法如果key值不存在,会返回none,不会返回错误# 删
dict1.pop("stu05") # 删除这条;也可以del dict1["stu05"]来删除
dict1.popitem() # 删除显示的最后一条
dict1.clear() # 清空字典
print(dict1)
# del dict1 # 删除整个字典
字典的循环遍历
前面的学习中我们知道可以使用for来遍历字符串,列表,元组.字典虽然不属于序列,但也可以使用for来遍历
print(dict1.keys()) # 打印所有的keys
print(dict1.values()) # 打印所有的values
print(dict1.items()) # 字典的行转成元组
# 上面这三种可以使用for来循环遍历for key in dict1.keys():print(key)for value in dict1.values():print(value)for line in dict1.items():print(line)
东城区的说明(也就是打印出"景点")
2. 修改北京东城区的说明,改为"故宫在这"
3. 增加北京昌平区及其说明
4. 修改北京海淀区的说明,将"大学"改为"清华",“北大”,“北邮"三个学校的列表
5. 在大学列表里再加一个"北影”
6. 循环打印出北京的区名,并在前面显示序号(以1开始)
7. 循环打印出北京海淀区的大学,并在前面显示序号(以1开始)
小结:
字典是否属于序列? 不属于
字典能否切片? 不能切
“我要打印字典的第2个到第5个键值对”,这种说法是否正确? 不对
字典是属于可变数据类型还是不可变数据类型? 可变
“字典里面可以嵌套字典,也可以嵌套列表或元组等其它数据类型”,这种说法是否正确? 正确
思考: 以下几种数据你觉得用python的哪种数据类型比较合适?
- 游戏里的所有英雄名称
["亚瑟", "后羿"]
- 游戏里的单个英雄属性
攻击: 100
防御: 45
血量: 1000
蓝量: 500物理穿透:
真实伤害:
暴击:
致命一击:
- 大公司的部门及其部门的职责说明
管理部门: 管理
人事: 人事
教学: 负责课程设计与教学
......
- NBA球员的数据
姓名: xxx
身高: xxx
年龄: xxx
集合-set
集合和字典一样都是使用**大括号**。但集合没有value,相当于只有字典的key。
字符串,列表和元组属于序列,是有序的,但集合是无序的,所以不能通过下标来查询和修改元素。
再总结一下:
整数,字符串,元组是**不可变**数据类型(整数和字符串改变值的话是在内存里开辟新的空间来存放新值,原内存地址里的值不变)
列表,字典,集合是**可变**数据类型(在内存地址不变的基础上可以改变值)
当然还有一种不可变集合我们这里不做讨论
集合主要特点:
- 天生去重(去掉重复值)
- 可以增,删(准确来说,集合可以增加删除元素,但不能修改元素的值)
- 可以方便的求交集,并集,补集
集合的创建
示例:
set1 = {1, 2, 3, 4, 5, 1, 2}
set2 = {2, 3, 6, 8, 8}print(type(set1))
print(set1)
print(set2) # 打印的结果,没有重复值
集合的常见操作
set1 = {1, 4, 7, 5, 9, 6}
set2 = set([2, 4, 5, 9, 8])# 交集
print(set1.intersection(set2))
print(set1 & set2)print(set1.isdisjoint(set2)) # 判断两个集合是否有交集,类型为bool(有交集为False,没交集为True)
# 并集
print(set1.union(set2))
print(set1 | set2)
# 差集(补集)
print(set1.difference(set2)) # set1里有,set2里没有
print(set1-set2)
print(set2.difference(set1)) # set2里有,set1里没有
print(set2-set1)
# 对称差集
print(set1.symmetric_difference(set2)) # 我有你没有的 加上 你有我没有的
print(set1^set2)
# 子集
set3=set([4, 5])
print(set3.issubset(set1)) # 判断set3是否为set1的子集
print(set1.issuperset(set3)) # 判断set1是否包含set3
# 集合的增加操作
set1.add(88)
print(set1)
set1.update([168, 998]) # 添加多个
print(set1)# 集合的删除操作
set1.remove(88) # 删除一个不存在的元素会报错
print(set1)
set1.discard(666) # 删除一个不存在的元素不会报错,存在则删除
print(set1)
小建议: 集合在运维开发中应用不多,我们主要记一下集合的天生去重特性与求交并补集方便这主要的两点就好。
集合小结:
集合是只有key的字典(无序的,没有重复值,可以增加和删除), 可以方便地求交并补集。
数据类型小结
序列: 字符串, 列表,元组( 有下标,可切片)
非序列: 数字,字典,集合(没有下标,不能切片)
可变: 列表,字典,集合
不可变: 数字,字符串,元组
(): 定义元组 , 函数 len(),print(),type(),int(),str(); char.upper() list1.append()
[]: 定义列表 , 取下标和取key
{}: 定义字典,集合. 格式化输出的占位符
python数据类型总结:
1, 数字(int,float,bool,complex); 字符串, 列表,元组,字典,集合
2, 字符串,列表,元组属于序列,它们都有下标。字典,集合没有下标
3, 数字,字符串,元组是不可变类型; 列表,字典,集合是可变类型(不可变集合不讨论)
4, 可以做增,删操作的有列表,字典,集合。
5, 列表,字典可以修改里面的元素, 但集合不可以修改里面的元素。
python里括号使用总结:
小括号(): 用于定义元组; 方法调用; print打印(其实print()也是一个函数); 函数,如len(),id(),type().
中括号(): 用于定义列表; 字符串,列表,元组取下标; 字典取key
大括号(): 用于定义字典,集合; format格式化输出用于取代%s这种的占位符也是大括号
问题:
属于序列的数据类型有哪几种?序列类型有哪些共性?
字符串,列表,元组 有下标,能切片,能拼接,for遍历
属于可变数据类型的有哪几种? 可变数据类型有哪些共性?
列表,集合,字典 可以增,删,改(集合有点特殊)
天生去重的数据类型有哪几种?
集合,字典里的key
可以用for循环遍历打印的数据类型有几种?
字符串,列表,元组,字典, 集合(有多个元素的都可以循环遍历)
可以做四则运算的数据类型
数字
python文件IO操作
io(input and output): 磁盘读写
回顾下shell里的文件操作
shell里主要就是调用awk,sed命令来处理文件, 特别是sed命令可以使用sed -i
命令直接操作文件的增,删,改。
python文件操作的步骤
python文件的操作就三个步骤:
- 先**open打开**一个要操作的文件
- **操作**此文件(读,写,追加等)
- **close关闭**此文件
python文件的打开与关闭
f = open("文件路径","文件访问模式") # 打开文件简单格式,需要赋值给一个变量f.close() # 关闭的方法
with open("文件路径", '文件访问模式') as f: # 此方法打开文件不用后面再使用close关闭了
python文件访问模式
访问模式 | 说明 |
---|---|
r (read) | 只读模式,不能写(文件必须存在,不存在会报错) |
w (write) | 只写模式,不能读(文件存在则会被覆盖内容(要千万注意),文件不存在则创建) |
a (append) | 只追加模式,不能读 |
r+ | 读写模式 |
w+ | 写读模式 |
a+ | 追加读模式 |
rb | 二进制读模式 |
wb | 二进制写模式 |
ab | 二进制追加模式 |
rb+ | 二进制读写模式 |
wb+ | 二进制写读模式 |
ab+ | 二进制追加读模式 |
python主要的访问模式示例
只读模式®
**示例: **
先在linux操作系统上使用head -5 /etc/passwd > /tmp/1.txt
准备一个文件
f = open('/tmp/1.txt", encoding="utf-8") # 默认就是只读模式
# 如果不同平台,可能会字符集编码不一致,不一致的需要指定;一致的不用指定。data1 = f.read()
data2 = f.read() # 读第二遍f.close()print(data1)
print("="*50)
print(data2) # 发现读第二遍没有结果;类似从上往下读了一遍,第二次读从最后开始了,所以就没结果了
tell与seek的理解
f = open("/tmp/1.txt", "r")
print(f.tell()) # 结果为0 (告诉你光标在哪,刚打开文件,光标在0位置)
f.seek(5) # 移你的光标到整个文件的第6个字符那(因为0为第一个)
print(f.tell()) # 结果为5
f.seek(2) # 移你的光标到整个文件的第3个字符那,从0开始算,而不是从上面的位置开始算
print(f.tell()) # 结果为2f.close()
深入理解只读模式
示例:
f = open("/tmp/1.txt", mode="r")data1 = f.read() # 读了第一次后,光标在最后的位置
f.seek(0) # 通过seek(0)将光标又重置回开始的位置
data2 = f.read() # 再次读的话,就可以又重头读一遍了,data2变量的内容与data1的内容就一致了f.close()print(data1)
print("="*20)
print(data2)
**示例: **
f = open("/tmp/1.txt", "r")f.seek(5) # 光标移到第6个字符那里
data1 = f.read() # read是读整个文件在光标后面的所有字符(包括光标所在的那个字符),读完后,会把光标移到你读完的位置f.seek(5) # 光标重置到第6个字符那里
data2 = f.readline() # readline是读光标所在这一行的在光标后面的所有字符(包括光标所在的那个字符),读完后,会把光标移到你读完的位置f.seek(5) # 光标重置到第6个字符那里
data3 = f.readlines() # readlines和read类似,但把读的字符按行来区分做成了列表f.close()
print(data1)
print("="*30)
print(data2)
print("="*30)
print(data3)
示例: 打印文件的第3行
f = open("/tmp/1.txt", mode="r")data = f.readlines() # 把文件从第1行到最后1行全读取,并转成列表f.close()
print(data[2].strip()) # 通过列表的下标2取第3行,取出来的行是字符串,使用strip()去掉换行符
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gMPWegV4-1602116466482)(图片/python文件IO读模式操作小结图.png)]
文件读的循环方法
**示例: **
f = open("/tmp/1.txt", "r")#循环方法一:
for index, line in enumerate(f.readlines()):print(index, line.strip()) # 需要strip处理,否则会有换行# 循环方法二:这样效率较高,相当于是一行一行的读,而不是一次性全读(如果文件很大,那么一次性全读会速度很慢)
for index, line in enumerate(f):print(index, line.strip())f.close()
示例: 打印文件的第3行
f = open("/tmp/1.txt", mode="r")for index, line in enumerate(f): # 边读边循环if index == 2: # 通过enumerate产生的下标来取第3行print(line.strip()) # 每一行的内容为字符串类型,使用strip()处理换行符f.close()
示例: 通过/proc/meminfo得到可用内存的值
f = open("/proc/meminfo", "r")for line in f:if line.startswith("MemAvailable"):print(line.split()[1])f.close()
文件IO读操作小结:
seek()
tell()
read()
readlines()
readline()
只写模式(w)
**示例: **
f = open("/tmp/1.txt",'w') # 只写模式(不能读),文件不存在则创建新文件,如果文件存在,则会复盖原内容(千W要小心)
data = f.read() # 只写模式,读会报错f.close()
示例: 创建新文件,并写入内容
f = open("/tmp/2.txt", 'w') # 文件不存在,会帮你创建(类似shell里的 > 符号)f.write("hello\n") # 不加\n,默认不换行写
f.write("world\n")
f.truncate() # 截断,括号里没有数字,那么就是不删除
f.truncate(3) # 截断,数字为3,就是保留前3个字节
f.truncate(0) # 截断,数字为0,就是全删除f.close()
练习: 配置本地yum
f = open("/etc/yum.repos.d/local.repo", mode="w")f.write("[local]\n")
f.write("name=local\n")
f.write("baseurl=file:///mnt\n")
f.write("enabled=1\n")
f.write("gpgcheck=0\n")f.close()
f = open("/etc/yum.repos.d/local.repo", mode="w")f.write('''[local]
name=local
baseurl=file:///mnt
gpgcheck=0
enabled=1
''')f.close()
f = open("/etc/yum.repos.d/local.repo", mode="w")f.write("[local]\n""name=local\n""baseurl=file:///mnt\n""gpgcheck=0\n""enabled=1\n")f.close()
f = open("/etc/yum.repos.d/local.repo", mode="w")f.write("[local]\nname=local\nbaseurl=file:///mnt\ngpgcheck=0\nenabled=1\n")f.close()
练习: 把九九乘法表直接写到一个文件里
f = open("/tmp/3.txt", mode="w")for i in range(1, 10):for j in range(1, i+1):f.write("{}*{}={}\t".format(j, i, i*j))f.write("\n")f.close()
只追加模式(a)
示例:
f = open("/tmp/2.txt", 'a') # 类似shell里的>>符f.write("hello\n")
f.write("world\n")
f.truncate(0) # 追加模式也可以使用truncate截取前面的数据f.close()
小结:
r: 只读模式 文件必须要存在;
w: 只写模式 文件存在则会清空原文件内容再写,不存在则会创建文件再写
a: 只追加模式 与w模式的区别为a模式不清空原文件内容,在最后追加写, (文件不存在也会先创建文件)
比较r+,w+,a+三种模式
r+文件必须要存在,相当于在只读模式的基础上加了写权限;w+会清空原文件的数据,相当于在只写模式的基础上加了读权限;(可以在任意位置写,但要用seek定位光标,还要区分读光标和写光标)a+不会改变原文件的数据,相当于在只追加模式的基础上加了读权限;(写是在最后写,即使用seek把光标移到前面,仍然会写在最后)
r+,w+,a+混合读写深入理解(拓展)
**示例: **
f = open("/tmp/2.txt", "w+")f.write("11111\n")
f.write("22222\n")
f.write("33333\n")print(f.tell()) # 打印结果为18,表示光标在文件最后f.write("aaa") # 这样写aaa,相当于在文件最后追加了aaa三个字符f.seek(0) # 表示把光标移到0(也就是第1个字符)
f.write("bbb") # f.seek(0)之后再写bbb,就是把第一行前3个字符替换成了bbbf.seek(0) # 把光标再次移到0
f.readline() # 把光标移到第1行最后
f.seek(f.tell()) # f.seek到第1行最后(如果这里理解为两个光标的话,你可以看作是把写光标同步到读光标的位置)
f.write("ccc") # 这样写ccc,就会把第二行的前3个字符替换成cccf.close()
练习: 往一个新文件里写九九乘法表,并直接读出结果
f = open("/tmp/4.txt", "w+")for i in range(1, 10):for j in range(1, i+1):f.write("{}*{}={} ".format(i, j, i*j))f.write("\n")f.seek(0) # 这里需要seek(0),否则读不出来
data1 = f.read()f.close()
print(data1)
总结:
r+,w+,a+这三种模式因为涉及到读与写两个操作,你可以使用两个光标来理解,但这样的话读光标与写光标会容易混乱,所以我们总结下面几条:
f.seek(0)是将读写光标都移到第1个字符的位置
如果做了f.read()操作,甚至是做了f.readline()操作,写光标都会跑到最后
如果做了f.write()操作,也会影响读光标
**所以建议在做读写切换时使用类似f.seek(0)和类似f.seek(f.tell())这种来确认一下位置,再做切换操作
函数
使用函数的优点: 功能模块化,代码重用(编写的代码可以重复调用)
回顾下shell里的函数
# service sshd start
# /etc/init.d/sshd start
在centos6里,上面两条命令里的start都是最终调用/etc/init.d/sshd服务脚本里的start()函数
function start() { # 定义函数名start,前面可以加function也可以不加/usr/sbin/sshd
}stop() {kill -15 `cat /var/run/sshd/sshd.pid`
}reload() {kill -1 `cat /var/run/sshd/sshd.pid`
}restart() {stopstart # 函数里调函数就是函数的嵌套
}case "$1" instart )start # 调用start函数;;stop )stop;;restart )restart;;reload )reload;;* )echo "参数错误"
esac
python里的函数
python里函数分为内置函数与自定义函数
内置函数: 如int(), str(), len(), range(), id(), max(), print(),type()等,所有的内置函数参考
https://docs.python.org/3/library/functions.html
自定义函数的定义与调用
def funct1(): # 函数名(),括号里面可以写参数,也可以不写"""函数说明或注释""" # 说明或注释可以不写,大型程序为了程序可读性最好写print("进水") # 函数代码主体print("洗衣服") # 函数代码主体print("脱水") # 函数代码主体return 0 # 函数返回值,可有可无funct1() # 调用函数的方式(调用函数也就是执行函数内部的代码)
函数传参数
为什么要传参?
答: 把自动洗衣机比喻成生活中的函数,我们不同的使用者也要告诉洗衣机加多少水,放什么衣服,洗衣的模式,脱水多久等,这些都可以看作是给洗衣机传参。
同样,给空调调模式和温度,给电视调频道与音量都可以看作是传参。
def test(a, b, c): # 定义函数,传3个参数,分别为a,b,cprint("进水{}升".format(a)) # 调用a的值print("洗{}".format(b)) # 调用b的值 print("脱水{}分钟".format(c)) # 调用c的值test(3, "毛衣", 10) # 将值3赋值给a变量,值"毛衣"赋值给b变量,值10赋值给c变量
示例: 形参,实参,位置参数,关键字参数
def test(a, b): # a,b是形参(形式上的参数,就是占位置用的)print(a + b)test(1, 2) # 1,2在这里是实参(实际的参数),实参也可以传变量,个数要与形参个数对应,也会按顺序来传参(位置参数);
# test(1) # 执行的话会报错,位置参数要与形参一一对应,个数不能少
# test(1, 2, 3) # 执行的话会报错,位置参数要与形参一一对应,个数也不能多
test(b=4, a=3) # 这里是关键字调用,那么就与顺序无关了(关键字参数)# test(5, b=6) # 混用的话,就比较怪了,结果可以自行测试一下(结论:位置参数必须写在关键字参数的前面)
示例: 默认参数或者叫默认值参数再或者叫缺省参数
def connect_ssh(host, user, password, port=22): # port=22为默认参数pass # pass就类似一个占位符,保证函数完整,没有语法错误host = input("input host:")
user = input("input user:")
password = input("password")connect_ssh(host, user, password,33) # 不用再传port的值的,默认就是22;也可以传一个新的值替代22
示例:
def ssh(ip, port=22):print("ssh {} -p {}".format(ip, port))ssh("10.1.1.15", 2222)
def ssh_connect(host, user="root", port=22 ):print("ssh {} -l {} -p {}".format(host, user, port))for i in range(11, 16):if i == 15:ssh_connect("10.1.1.{}".format(i), "aaa", 2222)else:ssh_connect("10.1.1.{}".format(i))
一句话小结: 默认值参数就是不传值就用默认的,传了值就用传的值。
示例:可变长参数
def funct1(*args): # 参数名前面加*(变量名可以自定义)就可以定义为可变长参数for i in args:print(i, end=" ")print()funct1(1, 2, 3, 4, 5, 6, 7, 8)
定义一个函数,传多个整数,实现求和
def add_num(*args):sum = 0for i in args:if isinstance(i, int): # 判断是整数类型才求和,或者使用if type(i) == int:sum += iprint(sum)add_num(1, 4, 3, "a")
小结:
为什么要传参?
每次调用函数可能会有不同的需求,传参就相当于是和函数做交互,传不同的值给函数来实现不同的需求
形参,实参
位置参数: 实参与形参按顺序一一对应
关键字参数: 在实参里也要写上形参的名字,这样做可以改变顺序
默认值参数: 大部分情况下值不变, 极少数值不一样的情况下可以使用默认值参数。
默认值参数就是不传值就用默认的,传了值就用传的值
可变长参数: 参数个数不确定的情况就用可变长参数
示例: 多个关键字参数转字典(拓展)
def test(**kwargs): # **两个后面加变量(变量名可以自定义),这样后面传多个值(需要传关键字参数),并且结果为字典print(kwargs)test(name="zhangsan", age=18, gender="M")def people(name, *args, age=18, **kwargs):print(name)print(args)print(age)print(kwargs)people("zhangsan", "man", 25, salary=20000, department="IT")
people("zhangsan", "man", 180, age=25, salary=20000, department="IT")
people("zhangsan", "man", 180, 25, salary=20000, department="IT")
函数返回值
函数的功能要专一, 一个函数只完成一个功能。
理解函数返回值的作用: 把函数的执行结果返回给需要调用的地方。
函数return返回的是一个值,所以要赋值给一个变量,然后通过调用变量得到返回值。
函数返回值写在函数体的最后,因为函数返回值意味着函数的结束。
不用return指定函数返回值,则返回值默认为None
示例:
def test(a, b):c = a + breturn cprint("haha") # 返回值后面的代码不执行,也就是说函数执行到return就结束了test(1, 2) # 再回顾一下,这是函数的调用,执行函数体内的代码,但这样得不到函数返回值d = test(1, 2)
print(d) # 这样终于得到函数的返回值了print(test(1,2)) # 不赋值给变量,直接打印也是可以得到函数的返回值
比较下面两段(目前看不出来返回值的优势)
def test1(a, b):print(a + b)def test2(a ,b):c = a + breturn ctest1(1, 2)print(test2(2, 3))
实例说明返回值的应用
def add_num(*args):sum = 0for i in args:if type(i) == int:sum += i
# print(sum) # 函数体内打印的结果,只能在调用函数时执行一次,不能被其它地方调用return sum # 将print(sum)换成return sum就能将结果给其它地方调用了add_num_sum = add_num(1, 4, 3, "a") # 返回值赋值给一个变量,那么这个变量可以被其它地方调用了if add_num_sum > 5: # 这里使用了add_num_sum变量,其实也就是调用了add_num函数的返回值print("好大啊")
else:print("好小啊")
小结:
函数的结果要被其它地方调用,就不要在函数里用print打印,而是用return做成返回值, 再把这个返回值赋值给一个变量,让调用者使用这个变量就是在使用这个函数的结果。
嵌套函数
还记得前面的if,while,for嵌套吗?再总结一下: if,while,for,函数都可以嵌套,也可以互相嵌套。
请问: 下面的两段代码结果是否一样?
def aaa():print("aaa")
def bbb():print("bbb")aaa()
bbb()def bbb():print("bbb")aaa()
def aaa():print("aaa")
bbb()
请问: 下面的代码有没有问题
def bbb():print("bbb")aaa()
bbb()
def aaa():print("aaa")
小结:
- 函数要先定义, 才能调用
- 函数类似一个变量, 定义函数就是把函数体内的代码在内存开辟一个空间存放进去,然后可以通过函数名来调用
- 调用函数其实就是执行函数体内的代码
变量作用域: 全局变量,局部变量
示例:
name = "zhangsan" # 全局变量def change_name():name = "lisi" # 这个变量只能在函数内生效,也就是局部变量(可以说这个函数就是这个变量的作用域)gender = "male"change_name()
print(name) # 结果为zhangsan
print(gender) # 这句会报错,因为它是局部变量,在外面调用不了
示例:
name = "zhangsan" # 全局变量def change_name():global name,gender # 这句可以把name改为全局变量,但不建议这样用,如果多次调用函数,这样很容易混乱,容易与外部的变量冲突name = "lisi"gender = "male"print(name)change_name() # 这句结果为lisi,调用函数内部的print(name)得到的结果
print(name) # 这句结果为lisi
print(gender) # 可以调用gender变量了,能打印出结果
递归函数(拓展)
函数可以调用其它函数,也可以调用自己;如果一个函数自己调用自己,就是递归函数,但递归也有次数上限(保护机制),所以递归需要有一个结束条件
在计算机中,函数调用是通过栈(stack)这种数据结构实现的,每当进入一个函数调用,栈就会加一层栈帧,每当函数返回,栈就会减一层栈帧。由于栈的大小不是无限的,所以,递归调用的次数过多,会导致栈溢出
示例: 下面代码就可以看到最高可以递归近1000次**
def aaa():print("aaa")aaa()aaa()
示例:
def abc(n):print(n)if n//2 > 0:abc(n//2)abc(100)
模块
模块的定义
模块就是一个.py结尾的python代码文件(文件名为hello.py,则模块名为hello), 用于实现一个或多个功能(变量,函数,类等)
函数是多行代码的打包(封装), 模块可以看作是类与函数的进一步打包(封装)
模块的分类
模块分为
- 标准库(python自带的模块,可以直接调用)
- 开源模块(第三方模块,需要先pip安装,再调用)
- 自定义模块(自己定义的模块)
模块的存放路径
模块主要存放在/usr/local/lib/python3.6/
目录下,还有其它目录下。使用sys.path查看。
# python3.6
>>> import sys
>>> print(sys.path) # 模块路径列表,第一个值为空代码当前目录
['', '/usr/local/lib/python36.zip', '/usr/local/lib/python3.6', '/usr/local/lib/python3.6/lib-dynload', '/root/.local/lib/python3.6/site-packages', '/usr/local/lib/python3.6/site-packages']
# sys.path和linux上的$PATH很类似,如果两个目录里分别有同名模块,则按顺序来调用目录靠前的。
# sys.path的结果是一个列表,所以你可以使用sys.path.append()或sys.path.insert()增加新的模块目录。
示例: 手动在当前目录下(pycharm当前项目目录)写一个简单模块(名为hello.py)
注意: 模块名不要用纯数字, 如1.py这种
如果你命名为os.py这种就坑了,原因是什么?
(因为os是一个标准库模块,你自定义的模块会和它冲突)
def funct1():print("funct1")def funct2():return "funct2"
示例:与上面的hello.py同目录下,再写一个1.py来调用hello.py
import hello # 调用同目录的hello模块hello.funct1() # 可以调用hello模块里的funct1()函数
print(hello.funct2()) # 调用hello模块里的funct2()函数,因为结果为函数返回值,所以还需要print打印
模块的基本导入语法
import导入方法相当于是直接解释模块文件
import导入单模块
import hello
import导入多模块
import module1,module2,module3
import module1
import module2
import module3
from导入模块里所有的变量,函数
from hello import *
from导入模块文件里的部分函数
from hello import funct1,funct2
两种模块导入方式的区别
区别1: import导入模块里的所有函数, 而from可以选择只导入模块里的部分函数(为了优化,只导入自己要用的)
区别2: 调用import导入模块需要前面接模块名,而from语法不用
import hello
hello.funct1() # 前面要接模块名from hello import *
funct1() # 前面不用模块名了
前面不加模块名会产生一个问题: 如果当前代码文件里的函数名与调用模块的函数的名冲突怎么办呢 ?
为了区分本地funct1和导入的hello模块里的funct1,可以导入时做别名
from hello import funct1 as funct1_hello
示例: 利用别名来解决模块与本地函数冲突的问题
from hello import funct1 as funct1_hellodef funct1():print("local funct1")funct1_hello() # 用别名来调用hello模块里的funct1
funct1() # 本地的funct1
包
包(Package)是用来逻辑上组织多个模块,其实就是一个目录,目录里必须有一个__init__.py文件(导入一个包就是执行包下的__init__.py文件)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gpXLiH54-1602116950549)(图片/新建python包.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ldNCLqsK-1602116950553)(图片/新建python包内结构.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uPXkDZDV-1602116950554)(图片/包结构1.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0kgHR7Sn-1602116950556)(图片/包结构2.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vRSmtpXn-1602116950558)(图片/包结构3.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pFWowe8J-1602116950559)(图片/包结构4.png)]
标准库之os模块
在学此模块前,告诫大家不要去死记硬背,首先能够看懂,然后需要用的时候就查。
示例: 查看目录与切换目录等
import osprint(os.getcwd()) # 查看当前目录
os.chdir("/tmp") # 改变当前目录print(os.curdir) # 打印当前目录.
print(os.pardir) # 打印上级目录..
os.chdir(os.pardir) # 切换到上级目录
print(os.listdir("/")) # 列出目录里的文件,结果是相对路径,并且为list类型
示例: 查看文件状态
import osprint(os.stat("/etc/fstab")) # 得到文件的状态信息,结果为一个tuple类型
print(os.stat("/etc/fstab")[6]) # 得到状态信息(tuple)的第7个元素,也就是得到大小
print(os.stat("/etc/fstab")[-4]) # 得到状态信息(tuple)的倒数第4个元素,也就是得到大小
print(os.stat("/etc/fstab").st_size) # 用这个方法也可以得到文件的大小print(os.path.getsize(__file__)) # 得到文件的大小,__file__是特殊变量,代表程序文件自己
print(os.path.getsize("/etc/fstab")) # 也可以指定想得到大小的任意文件
示例: 文件路径相关操作
import osprint(os.path.abspath(__file__)) # 得到文件的绝对路径
print(os.path.dirname("/etc/fstab")) # 得到文件的绝对路径的目录名,不包括文件
print(os.path.basename("/etc/fstab")) # 得到文件的文件名,不包括目录
print(os.path.split("/etc/fstab")) # 把dirname和basename分开,结果为tuple类型
print(os.path.join("/etc","fstab")) # 把dirname和basename合并
示例: 判断相关操作
import osprint(os.path.isfile("/tmp/1.txt")) # 判断是否为文件,结果为bool类型
print(os.path.isabs("1.txt")) # 判断是否为绝对路径,结果为bool类型
print(os.path.exists("/tmp/11.txt")) # 判断是否存在,结果为bool类型
print(os.path.isdir("/tmp/")) # 判断是否为目录,结果为bool类型
print(os.path.islink("/etc/rc.local")) # 判断是否为链接文件,结果为bool类型
示例: 文件改名与删除,目录创建与删除等
import osos.rename("/tmp/1.txt","/tmp/11.txt") # 改名
os.remove("/tmp/11.txt") # 删除os.mkdir("/tmp/aaa") # 创建目录
os.rmdir("/tmp/aaa") # 删除目录
os.makedirs("/tmp/a/b/c/d") # 连续创建多级目录
os.removedirs("/tmp/a/b/c/d") # 从内到外一级一级的删除空目录,目录非空则不删除
**os.popen()和os.system()**可以直接调用linux里的命令,二者有一点小区别:
# 下面这两句执行操作都可以成功
os.popen("touch /tmp/222")
os.system("touch /tmp/333")print(os.popen("cat /etc/fstab").read()) # 通过read得到命令的内容,可直接打印出内容,也可以赋值给变量
print(os.system("cat /etc/fstab")) # 除了执行命令外,还会显示返回值(0,非0,类似shell里$?判断用的返回值)如果执行命令没有结果输出,两个都可以
所以如果是为了得到命令的结果输出,并想对结果赋值进行后续操作的话,就使用os.popen(cmd).read();
如果直接得到命令结果就可以了,那么直接使用os.system(cmd)就OK
问题: 感觉我就只要会os.popen()和os.system()就够了啊,因为我是搞linux运维的,命令熟悉啊。为啥还去记上面那些方法?
答: os.popen()与os.system()主要用于linux运维,在windows或MAC平台上就不能命令通用了。而os模块的其它方法都是跨平台通用的.
示例: 判断linux上的文件是否为block类型
import osfile_path = input("input a file path:")file_type = os.popen("ls -l {} |cut -c1".format(file_path)).read().strip()if file_type == "b":print("block file")
标准库之re模块
正则表达式简介
re是regex的缩写,也就是正则表达式
# grep ^daemon /etc/passwd
daemon:x:2:2:daemon:/sbin:/sbin/nologin# awk '$0~"^daemon" {print $0}' /etc/passwd
daemon:x:2:2:daemon:/sbin:/sbin/nologin# sed -n '/^daemon/p' /etc/passwd
daemon:x:2:2:daemon:/sbin:/sbin/nologin
表达式或符号 | 描述 |
---|---|
^ | 开头 |
$ | 结尾 |
[abc] | 代表一个字符(a,b,c任取其一) |
[^abc] | 代表一个字符(但不能为a,b,c其一) |
[0-9] | 代表一个字符(0-9任取其一) “[[:digit:]]” |
[a-z] | 代表一个字符(a-z任取其一) “[[:lower:]]” |
[A-Z] | 代表一个字符(A-Z任取其一) “[[:upper:]]” |
. | 一个任意字符 |
* | 0个或多个前字符 |
.* | 代表任意字符 |
+ | 1个或多个前字符 |
? | 代表0个或1个前字符 |
\d | 匹配数字0-9 |
\D | 匹配非数字 |
\w | 匹配[A-Za-z0-9] |
\W | 匹配非[A-Za-z0-9] |
\s | 匹配空格,制表符 |
\S | 匹配非空格,非制表符 |
{n} | 匹配n次前字符 |
{n,m} | 匹配n到m次前字符 |
re模块常用操作
模块+函数(方法) | 描述 |
---|---|
re.match() | 开头匹配,类似shell里的^符号 |
re.search() | 整行匹配,但只匹配第一个 |
re.findall() | 全匹配并把所有匹配的字符串做成列表 |
re.split() | 以匹配的字符串做分隔符,并将分隔的转为list类型 |
re.sub() | 匹配并替换 |
示例: re.match
import reprint(re.match("aaa", "sdfaaasd")) # 结果为none,表示匹配未成功
print(re.match("aaa", "aaasd")) # 有结果输出,表示匹配成功abc = re.match("aaa\d+", "aaa234324bbbbccc")
print(abc.group()) # 结果为aaa234324,表示打印出匹配那部分字符串
示例: re.search
import reprint(re.search("aaa", "sdfaaasdaaawwsdf")) # 有结果输出,表示匹配成功;re.search就是全匹配,而不是开头(但只返回一个匹配的结果);想开头匹配的话可以使用^aaa
print(re.search("aaa\d+", "aaa111222bbbbcccaaaa333444").group()) # 验证,确实只返回一个匹配的结果,并使用group方法将其匹配结果打印出来
示例: re.findall
import reprint(re.findall("aaa\d+", "aaa111222bbbbcccaaaa333444")) # 没有group()方法了,结果为['aaa111222', 'aaa333444']
print(re.findall("aaa\d+|ddd[0-9]+", "aaa111222bbbbddd333444")) # 结果为['aaa111222', 'ddd333444']
小结: re.search()与re.findall()
- 都不是开头匹配
- re.search()只匹配一行里第一个,而re.findall()会把一行内匹配的多个都匹配出来
- re.search()可以通过group()打印匹配的结果, re.findall()没有group()方法,直接把匹配的所有结果以列表的形式展示
运维的文本处理操作一般就只有四种:
1, 行匹配(查找行)
- 按行号来
- 按正则匹配
2, 截取
3, 统计
4, 替换
打印/etc/passwd
文件里的以daemon开头的行
import ref = open("/etc/passwd", mode="r")for line in f:
# if line.startswith("daemon"):
# if re.match("daemon", line):if re.search("daemon", line):print(line.strip())f.close()
打印/etc/passwd
文件里的有root的行
import ref = open("/etc/passwd", mode="r")for line in f:
# if re.search("root", line):if re.findall("root", line):print(line.strip())f.close()
统计/etc/passwd
文件里的root关键字一共出现多少次
import ref = open("/etc/passwd", mode="r")sum=0
for line in f:sum += len(re.findall("root", line))print(sum)f.close()
练习:使用input输入一个字符串,判断是否为强密码: 长度至少8位,包含大写字母,小写字母,数字和下划线这四类字符则为强密码
import redef check_password(password):if (len(password) >= 8 and re.search("[0-9]", password) andre.search("[a-z]", password) and re.search("[A-Z]", password)and re.search("_", password) ):print("强密码")else:print("弱密码")check_password("fdsfsfsdfsafSDFSA234_")
示例: re.split
import reprint(re.split(":", "root:x:0:0:root:/root:/bin/bash")) # 以:分隔后面字符串,并转为列表
练习:打印/etc/passwd文件的最后一列
import ref = open("/etc/passwd", "r")for line in f:abc = re.split(":",line)print(abc[len(abc)-1].strip()) # len()算长度,通过长度-1得到最后一列的下标
# print(line.strip().split(":")[-1]) # 用字符串的split方法切分,再通过下标-1得到结果
# print(re.split(":",line.strip())[-1]) f.close()
练习: 统计/etc/passwd
文件里的root关键字一共出现多少次(换成split的写法)
import ref = open("/etc/passwd", mode="r")sum=0
for line in f:if re.findall("root", line):line_list1 = re.split("root", line)sum = sum + len(line_list1) -1print(sum)f.close()
import redef count_file_words(keyword, filepath):f = open(filepath, mode="r")sum=0for line in f:if re.findall(keyword, line):line_list1 = re.split(keyword, line)sum = sum + len(line_list1) -1print(sum)f.close()count_file_words("root", "/etc/passwd")
示例: re.sub
import reprint(re.sub(":","-","root:x:0:0:root:/root:/bin/bash")) # 全替换:成-
print(re.sub(":","-","root:x:0:0:root:/root:/bin/bash",count=3)) # 只替换3次
练习:
对一个文件进行关键字屏蔽,如"笨蛋",打印出来后变成两个星号来替代,并做成函数(封装起来)
import reinput_str = input("输入字符串:")def badword_replace(input_str):bad_words = ["笨蛋", "草", "靠", "羊驼"]for word in bad_words:input_str = re.sub(word, "*"*len(word), input_str)print(input_str) badword_replace(input_str)
第三方模块之psutil
psutil是一个跨平台库,能够轻松实现获取系统运行的进程和系统利用率(包括CPU、内存、磁盘、网络等)信息。它主要应用于系统监控,分析和限制系统资源及进程的管理。
psutil模块安装
安装方法1:
因为是第三方模块,所以需要先使用pip命令安装后再能使用
# pip3.6 install psutil -i https://pypi.tuna.tsinghua.edu.cn/simple # 安装需要能上公网网络# pip3.6 list |grep psutil
psutil 5.4.8
这样安装后,直接# python
进入交互模式可以import psutil
,但pycharm却导入不了(重启pycharm也不行).,因为pycharm使用的是python虚拟环境,所以并没有修改过系统的解释器,按下面的解决方法修改一下即可。
解决方法:
pycharm里点File–>Settings–>再按如下图所示操作–> ok --> ok
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JcqkxMBG-1602116950561)(图片/pycharm里更改系统解释器版本0.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xDLNZlRd-1602116950561)(图片/pycharm里更改系统解释器版本.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PenxMpGL-1602116950562)(图片/1541320800203.png)]
安装方法2:
在pycharm图形界面直接操作安装, pycharm会调用pip命令为你安装
pycharm里点File–>Settings–>再按如下图所示操作
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-k886Pr72-1602116950563)(图片/pycharm安装模块.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3NClyUYY-1602116950564)(C:/Users/Administrator/AppData/Roaming/Typora/typora-user-images/1597217392225.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-d6GcHZ8i-1602116950565)(图片/pycharm安装模块2.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Zhvomvln-1602116950566)(图片/pycharm安装模块3.png)]
psutil模块常用操作
linux下top,vmstat,sar,free,mpstat等命令可以查,而python程序员可以不用关心linux的命令直接使用psutil模块就能得到相应的信息
import psutil
# cpu
print(psutil.cpu_times()) # 查看cpu状态,类型为tuple
print(psutil.cpu_count()) # 查看cpu核数,类型为int# memory(内存)
print(psutil.virtual_memory()) # 查看内存状态,类型为tuple
print(psutil.swap_memory()) # 查看swap状态,类型为tuple# partition(分区)
print(psutil.disk_partitions()) # 查看所有分区的信息,类型为list,内部为tuple
print(psutil.disk_usage("/")) # 查看/分区的信息,类型为tuple
print(psutil.disk_usage("/boot")) # 查看/boot分区的信息,类型为tuple# io(磁盘读写)
print(psutil.disk_io_counters()) # 查看所有的io信息(read,write等),类型为tuple
print(psutil.disk_io_counters(perdisk=True)) # 查看每一个分区的io信息,类型为dict,内部为tuple# network(网络)
print(psutil.net_io_counters()) # 查看所有网卡的总信息(发包,收包等),类型为tuple
print(psutil.net_io_counters(pernic=True)) # 查看每一个网卡的信息,类型为dict,内部为tuple# process(进程)
print(psutil.pids()) # 查看系统上所有进程pid,类型为list
print(psutil.pid_exists(1)) # 判断pid是否存在,类型为bool
print(psutil.Process(1)) # 查看进程的相关信息,类型为tuple# user(用户)
print(psutil.users()) # 查看当前登录用户相关信息,类型为list
示例:监控/分区的磁盘使用率,超过90%(阈值,也就是临界值)就发给微信好友
# pip3.6 install -i https://pypi.tuna.tsinghua.edu.cn/simple itchat # 先安装itchat,或者用pycharm图形安装(可以连接微信的一个模块)
import psutil,itchatitchat.auto_login(hotReload=True) # 第一次登陆会扫描二维码登陆(hotreload=True会缓存,不用每次都登录)
user_info = itchat.search_friends("Candy") # Candy为你的好友名,这是一个list类型,里面是dict
user_id = user_info[0]['UserName'] # 通过上面获取的信息得到Candy的好友id# 下面这句是算出磁盘使用率并赋值给root_disk_use_percent变量
root_disk_use_percent = psutil.disk_usage("/")[1]/psutil.disk_usage("/")[0]if root_disk_use_percent > 0.9: # 如果/分区没有使用超过90%,为了方便测试可以把0.9改小itchat.send("/ is overload", toUserName=user_id) # 发送信息给好友id
第三方模块之paramiko
paramiko模块支持以加密和认证的方式连接远程服务器。可以实现远程文件的上传,下载或通过**ssh**远程执行命令。
PS: 课程学习的配置自动化工具ansible底层就是使用paramiko模块。
paramiko模块安装
python使用paramiko模块来实现远程ssh操作(或者使用pycharm图形安装方式)
# pip3.6 install paramiko -i https://pypi.tuna.tsinghua.edu.cn/simple
远程上传下载文件
示例: paramiko模块实现文件的上传下载(要传密码的做法)
import paramiko # 导入import模块trans = paramiko.Transport("10.1.1.12",22) # 产生连接10.1.1.12的22的传输,赋值给transtrans.connect(username="root",password="123456") # 指定连接用户名与密码sftp = paramiko.SFTPClient.from_transport(trans) # 指定为sftp传输方式sftp.get("/etc/fstab","/tmp/fstab") # 下载 (注意不能只写/tmp,必须加文件名写成/tmp/fstab)
sftp.put("/etc/inittab","/tmp/inittab") # 上传trans.close()
示例:paramiko模块实现文件的上传下载(免密登录)
先提前做好免密登录
# ssh-keygen # 三次回车在本机产生空密码密钥对# ssh-copy-id -i 10.1.1.12 # 将公钥传给对方目标机器
import paramiko trans = paramiko.Transport("10.1.1.12",22) # 产生连接10.1.1.12的22的传输,赋值给transprivate_key = paramiko.RSAKey.from_private_key_file("/root/.ssh/id_rsa") # 指定本机私钥路径trans.connect(username="root",pkey=private_key) # 提前使用ssh-keygen做好免密登录sftp = paramiko.SFTPClient.from_transport(trans)sftp.get("/etc/fstab","/tmp/fstab2")
sftp.put("/etc/inittab","/tmp/inittab2") trans.close()
远程命令操作
指定密码连接与密钥连接两种方式二选一
import paramikossh = paramiko.SSHClient() # 创建一个客户端连接实例private_key = paramiko.RSAKey.from_private_key_file("/root/.ssh/id_rsa") # 指定本机私钥路径
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy) # 加了这一句,如果第一次ssh连接要你输入yes,也不用输入了# ssh.connect(hostname="10.1.1.12", port=22, username="root", password="123456") # 指定密码连接ssh.connect(hostname="10.1.1.12",port=22,username="root",pkey=private_key) # 指定密钥连接stdin,stdout,stderr = ssh.exec_command("touch /tmp/123") print(stdout.read().decode())
print(stderr.read().decode()) ssh.close()
示例: 将上例用函数封装
import paramikodef paramiko_ssh(hostname, command, port=22, username="root"):ssh = paramiko.SSHClient()private_key = paramiko.RSAKey.from_private_key_file("/root/.ssh/id_rsa")ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy)ssh.connect(hostname=hostname, port=port, username=username, pkey=private_key)stdin, stdout, stderr = ssh.exec_command(command)print(stdout.read().decode())print(stderr.read().decode())ssh.close()paramiko_ssh("10.1.1.12", "df -h")
异常处理
异常处理:
Python程序运行语法出错会有异常抛出
不处理异常会导致程序终止
异常种类 | 描述 |
---|---|
IndentationError | 缩进对齐代码块出现问题 |
NameError | 自定义标识符找不到 |
IndexError | 下标错误 |
KeyError | 键名出错 |
AssertionError | 断言异常 |
SyntaxError | 语法错误 |
AttributeError | 找不到属性 |
TypeError | 类型错误 |
KeyboardInterrupt | ctrl + c 被按下 |
ImportError | 导入模块出错 |
… |
示例: 异常处理的简单应用
abc = input("输入一个数字:")# if not abc.isdigit():
# print("你到底知不知道什么是数字?")
# exit()
# else:
# abc = int(abc)try:abc = int(abc)print(abc)
except ValueError:print("你到底知不知道什么是数字?")
try语句
- 首先,执行try子句(在关键字try和关键字except之间的语句)。
- 如果没有异常发生,忽略except子句,try子句执行后结束。
- 如果在执行try子句的过程中发生了异常,那么try子句余下的部分将被忽略。
- 如果异常的类型和 except 之后的名称相符,那么对应的except子句将被执行。最后执行 try 语句之后的代码。
- 如果一个异常没有与任何的except匹配,那么这个异常将会报错并终止程序。
示例:
list1 = [1,2,3]try:print(list1[3]) # 执行这一句,正常就直接执行,不用管后面的错误;如果不正常,出现了异常,则要继续往下看
except TypeError: # 捕捉异常,如果是TypeError错误,则执行print("error1");如果不是,继续往下看print("error1")
except IndexError as err: # 捕捉异常,如果是IndexError错误,则执行print("error2:",err)print("error2:",err)print("haha") # 异常都没有被捕捉到,就会报异常,并终止程序运行;如果捕捉到,会报你自定义的错误,并继续可以执行下面的代码
示例:
list1 = [1,2,3]try:print(list1[3]) # 这里两句,从上往下执行,只要发现错误就不继续往下try了,而是直接执行后面的except语句print(list1[0])
except TypeError as err:print("error1",err)
except IndexError as err:print("error2:",err)
示例:
list1 = [1,2,3]try:print(list1[3]) # 这是一个IndexError
except TypeError as err: # 这里没有捕捉对错误类型print("error1",err)
except SyntaxError as err: # 这里没有捕捉对错误类型print("error2:",err)
except Exception as err: # Exception代表所有错误异常print("error3",err)
示例:
list1 = [1,2,3]try:print(list1[3]) except TypeError as err:print("error1",err)
except SyntaxError as err:print("error2:",err)
except Exception as err:print("error3",err)
else: # 没有异常,会执行;有异常被捕捉到不会执行;有异常没被捕捉到也不会执行print("everything is ok,do it!")
finally: # 没有异常,有异常被捕捉到,有异常没有被捕捉到,finally里的代码都会执行print("no matter what,do it!") print("haha")
示例: 自定义异常程
class Daniel_define_exception(Exception): # 如果官方的异常类型你还是觉得不够用,还可以自定义异常类型。我这里就自定义了一个异常Daniel_define_exceptiondef __init__(self,error_msg):self.error_msg = error_msgclass Daniel_define_exception(Exception):def __init__(self, error_msg):self.error_msg = error_msgnum = int(input("input a num bigger than 10:"))if num < 11:try:raise Daniel_define_exception("bigger than 10,you idiot!!")except Daniel_define_exception as error:print(error)
标准库之sys模块
sys.argv[n] # sys.argv[0]等同于shell里的$0, sys.argv[1]等同于shell里的$1,以此类推
示例:
# vim 1.pyimport sys,oscommand = " ".join(sys.argv[1:]) # df -h取出来会变为['df', '-h'],所以需要join成字符串print(command)print(os.popen(command).read()) # 这一句加上,就可以直接得到df -h命令的结果
# os.system(command) # 或者使用os.system()直接输出结果# python3.6 1.py df -h # 这样可以把df -h命令取出来(在bash环境这样执行,不要使用pycharm直接执行)
标准库之random模块
import randomprint(random.random()) # 0-1之间的浮点数随机
print(random.uniform(1,3)) # 1-3间的浮点数随机print(random.randint(1,3)) # 1-3整数随机 (常用)
print(random.randrange(1,3)) # 1-2整数随机
print(random.randrange(1,9,2)) # 随机1,3,5,7这四个数,后面的2为步长 (常用) print(random.choice("hello,world")) # 字符串里随机一位,包含中间的逗号
print(random.sample("hello,world", 3)) # 从前面的字符串中随机取3位,并做成列表list = [1, 2, 3, 4, 5]
random.shuffle(list) # 把上面的列表洗牌,重新随机
print(list)
示例: 随机打印四位小写字母,做一个简单的验证码
import random
# 方法一:
code = ""
for i in range(4):code += random.choice("abcdefghijklmnopqrstuvwxyz")print(code)# 方法二:
code = random.sample("abcdefghijklmnopqrstuvwxyz", 4)code2=""
for i in code:code2+=i
print(code2)# 方法三:
code = ""
for i in range(4):for j in chr(random.randint(97,122)): # chr()在变量的数据类型转换的表格里有写,这里97-122使用chr()转换后对应的就是a-zcode += j
print(code)
示例: 验证码要求混合大写字母,小写字母,数字
import randomcode = ""
for i in range(4):a = random.randint(1,3)if a == 1:code += chr(random.randrange(65,91)) # 大写的A-Z随机elif a == 2:code += chr(random.randrange(97,123)) # 小写的a-z随机else:code += chr(random.randrange(48,58)) # 0-9随机print(code)
第三方模块之pymysql
# yum install mariadb*
# systemctl restart mariadb# pip3.6 install pymysql
示例:
import pymysqldb = pymysql.connect(host="localhost",user="root",password="",port=3306) # 指定数据的连接host,user,password,port,schemacursor = db.cursor() # 创建游标,就类似操作的光标cursor.execute("show databases;")print(cursor.fetchone()) # 显示结果的一行
print(cursor.fetchmany(2)) # 显示结果的N行(接着前面的显示2行)print(cursor.fetchall()) # 显示结果的所有行(接着前面的显示剩余的所有行)cursor.close()
db.close()
示例:
import pymysqldb = pymysql.connect(host="localhost",user="root",password="",port=3306,db="mysql") # 多指定了db="mysql",表示登录后会直接use mysqlcursor = db.cursor()
# cursor.execute("use mysql;") # 前面连接时指定了连接的库,这里不用再执行use mysql;
cursor.execute("show tables;")print(cursor.fetchall())cursor.close()
db.close()
示例: 操作数据库(建库,建表等)
import pymysqldb = pymysql.connect(host="localhost",user="root",password="",port=3306)cursor = db.cursor()cursor.execute("create database aaa;")
cursor.execute("use aaa;")
cursor.execute("create table emp(ename varchar(20),sex char(1),sal int)")
cursor.execute("desc emp")print(cursor.fetchall())cursor.close()
db.close()
示例: 远程数据库dba先建一个库,再授权一个普通用户给远程开发的连接
# 比如在10.1.1.12(测试服务器)上安装数据库,然后对10.1.1.11(开发人员)授权
# mysql
MariaDB [mysql]> create database aaadb;MariaDB [mysql]> grant all on aaadb.* to 'aaa'@'10.1.1.11' identified by '123';MariaDB [mysql]> flush privileges;
# 下面开发代码是在10.1.1.11(开发人员)上执行的
import pymysqldb = pymysql.connect(host="10.1.1.12",user="aaa",password="123",port=3306,db="aaadb")cursor = db.cursor()cursor.execute("create table hosts(ip varchar(15),password varchar(10),hostgroup tinyint)")
# 插入数据方法一
cursor.execute("insert into hosts(ip,password,hostgroup) values('10.1.1.22','123456',1)")# 插入数据方法二
insertsql = '''insert into hosts(ip,password,hostgroup)values('10.1.1.23','123456',1),('10.1.1.24','123456',1),('10.1.1.25','123',2),('10.1.1.26','1234',2),('10.1.1.27','12345',2);
'''
cursor.execute(insertsql)# 插入数据方法三
data = [('10.1.1.28','12345',2),('10.1.1.29','12345',3),('10.1.1.30','12345',3),('10.1.1.31','12345',3),('10.1.1.32','12345',3),('10.1.1.33','12345',3),('10.1.1.34','12345',3),
]
cursor.executemany("insert into hosts(ip,password,hostgroup) values(%s,%s,%s);",data)db.commit() # 这里做完DML需要commit提交,否则数据库没有实际插入数据cursor.execute("select * from hosts;")print(cursor.fetchall()) # 上面不提交,这里也可以看得到cursor.close()
db.close()
标准库之时间相关模块
python中有三种时间类型
时间类型 | 描述 |
---|---|
struct_time(时间元组) | 记录时间的年,月,日,时,分等 |
timestamp**时间戳**(epoch时间) | 记录离1970-01-01 00:00:00有多少秒 |
格式化的时间字符串 | 如2018-01-01 12:00:00(格式可以自定义) |
三种类型之间的转换图:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-B8mo6Rqc-1602117105590)(D:/1-深圳4期/python基础/python06/图片/python三种时间类型转换图.png)]
示例: 三种基本格式的打印
import timetime.sleep(1) # 延迟1秒print(time.localtime()) # 打印当前时间的年,月,日,时,分等等,本地时区(时间元组)
print(time.gmtime()) # 与localtime类似,但是为格林威治时间(时间元组)print(time.strftime("%Y-%m-%d %H:%M:%S")) # 打印当前时间(格式化字符串)
print(time.strftime("%F %T")) # 打印当前时间(格式化字符串)
print(time.asctime()) # 打印当前时间(常规字符串格式)print(time.time()) # 打印当前时间,离1970年1月1号0点的秒数(时间戳)
示例: 三种格式间的转换
import timeabc = time.localtime() # 当前时间(本地时区)的时间元组赋值给abc
print(time.mktime(abc)) # 时间元组转时间戳
print(time.strftime("%Y-%m-%d %H:%M:%S",abc)) # 时间元组转格式化字符串(自定义格式)
print(time.asctime(abc)) # 时间元组转格式化字符串(常规格式)print(time.strptime("2018-01-01 10:30:25","%Y-%m-%d %H:%M:%S")) # 格式化字符串转时间元组print(time.localtime(86400)) # 打印离1970年86400秒的时间,本地时区(时间戳转时间元组)
print(time.gmtime(86400)) # 打印离1970年86400秒的时间,格林威治时间(时间戳转时间元组)print(time.ctime(335235)) # 时间戳转格式化字符串
示例: datetime,calendar模块
import datetime,calendarprint(datetime.datetime.now())
print(datetime.datetime.now()+datetime.timedelta(+3)) # 三天后
# shell里也有类似的用法,如: date '+%F %T' -d "+3 days"
print(datetime.datetime.now()+datetime.timedelta(days=-3)) # 三天前
print(datetime.datetime.now()+datetime.timedelta(hours=5)) # 五小时后
print(datetime.datetime.now()+datetime.timedelta(minutes=-10)) # 十分钟前
print(datetime.datetime.now()+datetime.timedelta(weeks=1)) # 一星期后print(calendar.calendar(2018))
print(calendar.isleap(2016))
示例: 打印昨天的时间(格式为YYYY-mm-dd HH:MM:SS)
import datetime,time# 字符串来计算时间
print(str(datetime.datetime.now()+datetime.timedelta(days=-1)).split(".")[0])# 转成时间戳来计算时间
print(time.strftime("%F %T",time.localtime(time.time()-86400)))
练习: 打印一年后的当前时间
import time# 时间元组转列表再计算时间
aaa = list(time.localtime())
aaa[0] += 1print(time.strftime("%F %T", tuple(aaa)))
示例:写一个2019-01-01的倒计时
import timegoal_seconds=int(time.mktime(time.strptime("2019-01-01 00:00:00","%Y-%m-%d %H:%M:%S")))while True:s = int(goal_seconds-int(time.time()))if s == 0:breakelse:print("离2019年还有{}天{}时{}分{}秒".format(int(s/86400),int(s%86400/3600),int(s%3600/60),int(s%60)))time.sleep(1)print("2019年到了")
示例: 每隔1秒循环打印2018年的日期(从2018-01-01至2018-12-31)
import time,datetimestart_time = datetime.datetime.strptime("2018-01-01","%Y-%m-%d")
delta = datetime.timedelta(days=1)while True:print(str(start_time).split()[0])start_time = start_time+deltatime.sleep(1)
示例: 简单的定时程序
import timegoal_time = input("输入定时的时间(年-月-日 时:分:秒):")while True:now = time.strftime("%Y-%m-%d %H:%M:%S")print(now)time.sleep(1)if now == goal_time:print("时间到了!")break
面向对象编程
面向过程编程思想与面向对象编程思想
事例:一个宿舍的电脑菜鸟要配新电脑
第一种方式: (宿舍里每个人都大概要走下面几步)
1, 查找电脑相关资料和学习相关知识
2, 根据预算和所学知识定好了要配置的电脑各硬件
3, 带着钱去电脑城选购
4, 导购推荐你这样,那样,超出预算了
5, 咬牙买了,成交
第二种方式:
1, 大家找了一位靠谱的老师(电脑高手)
2, 给钱这位老师,老师根据不同人的预算配置合适的电脑
第一种方式强调的是过程,每个人都一步一步地参与了自己的买电脑的步骤。(面向过程的思想)
第二种方式强调的是电脑高手这个人, 步骤不用亲自一步一步做,由电脑高手来搞定。(面向对象的思想)
封装
函数 -》 类(面向对象) -》 模块
我为什么在上事例中要强调是一个宿舍的人,而不是一个人?因为如果用程序来实现的话,一个人一步一步的好写,很多人就难了(不是循环,因为每人的动作不是完全一样的)。
如:
两个人一天干以下几件事:
- 张三: 起床—吃饭—工作—吃饭—工作—吃饭—工作—回家—睡觉
- 李四: 起床—吃饭—学习—吃饭—学习—回家—玩耍—睡觉
再如: 使用函数来代码复用
def get_up(name):print("{}起床".format(name))def eat(name):print("{}吃饭".format(name))def go_to_work(name):print("{}工作中".format(name))def go_to_school(name):print("{}学习中".format(name))def go_to_play(name):print("{}玩耍中".format(name))def go_home(name):print("{}回家".format(name))def go_to_bed(name):print("{}睡觉".format(name))get_up("zhangsan")
eat("zhangsan")
go_to_work("zhangsan")
eat("zhangsan")
go_to_work("zhangsan")
eat("zhangsan")
go_to_work("zhangsan")
go_home("zhangsan")
go_to_bed("zhangsan")get_up("lisi")
eat("lisi")
go_to_school("lisi")
eat("lisi")
go_to_school("lisi")
go_home("lisi")
go_to_play("lisi")
go_to_bed("lisi")# 如果吃,上班,去玩的动作再多一些,人除了张三,李四,王五外也再多一些,这样写还是感觉代码不够精简。
面向对象三大特性:
- 封装
- 继承
- 多态
类与对象
**类与对象**是面向对象两个非常重要的概念。
类是总结事物特征的抽象概念,是创建对象的模板。对象是按照类来具体化的实物。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qoE58Ffm-1602117221938)(图片/类与对象1.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-f9NgmqIS-1602117221941)(图片/类与对象2.png)]
类的构成
类的名称: 类名
类的属性: 一组参数数据
类的方法: 操作的方式或行为
如: 为人设计一个类:
名称 : people
属性: name,sex,age,weight,height等
方法: eat,drink,walk,run等
为王者荣耀里的英雄设计一个类:
名称: hero
属性: HP,MP,attack,armor,speed等
方法: 普攻,暴击, Q技能, W技能,E技能,R技能等
为笔记本设计一个类:
名称: laptop
属性: cpu,mem,disk,屏幕大小,显卡等
方法: 开机,关机等
类的创建
# class People(object): 新式类 class People(): 经典类class People(object): # 类名python建议使用大驼峰命名(每一个单词的首字母都采用大写字母); pass
创建对象
class People(object):passp1 = People() # 创建第一个对象p1,这个过程也叫类的实例化
p2 = People() # 创建第二个对象p2,这个过程也叫类的实例化print(p1)
print(p2) # 得到的内存地址都不同,也就是说类和类的实例都是生成的内存对象(类和不同的实例占不同的内存地址)print(id(p1))
print(id(p2))
给对象加上属性
比较下面两段代码的传参方式:
class People(object):passp1 = People()
p2 = People()p1.name = "张三" # 给实例p1赋于属性name和值"张三"
p1.sex = "男" # 给实例p1赋于属性sex和值"男"p2.name = "李四" # 给实例p2赋于属性name和值"李四”
p2.sex = "女" # 给实例p2赋于属性sex和值"女"print(p1.name, p1.sex)
print(p2.name, p2.sex) # 可以打印出赋值的数据
class People(object):def __init__(self, name, sex): # 第一个参数一定是self,代表实例本身.其它要传的参数与函数传参一样(可以传位置参数,关键字参数,默认参数,不定长参数等);__init__为构造函数self.name = name # 此变量赋给了实例,也就是实例变量self.sex = sexp1 = People("张三", "男") # 实例化的时候直接传参数
p2 = People("李四", "女")print(p1.name, p1.sex)
print(p2.name, p2.sex) # 也可以打印出传入的值
给类加上方法
比较上面代码和下面这段代码
class People(object):def __init__(self, name, sex):self.name = nameself.sex = sexdef info(self): # 定义类的方法,就是一个封装的函数print(self.name, self.sex) # 此方法就是打印对象的name和sexp1 = People("张三", "男")
p2 = People("李四", "女")p1.info()
p2.info() # 对象调用类的方法
类变量与实例变量
类的属性可分为类变量与实例变量
class People(object):country = "中国" # 类变量(相当于在类里面是一个全局变量,作用范围为类)def __init__(self, name, sex):self.name = name # 实例变量self.sex = sexdef info(self):print(self.name, self.sex, self.country) # 类变量可以在类的内部被调用p1 = People("张三", "男")print(p1.country) # 外部可直接调用类变量的值
p1.info # 也可通过方法调用类变量的值
类变量与默认值参数的作用类似
class People(object):def __init__(self, name, sex, country="中国"): # 默认值参数self.name = name self.sex = sexself.country = countrydef info(self):return "{} {} {}".format(self.name, self.sex, self.country)p1 = People("张三", "男", "美国") # 不传国籍,默认为中国;传了美国则为美国print(p1.info())
__str__与__del___(了解)
class Hero(object):def __init__(self, name):self.name = namedef __str__(self): # print(对象)会输出__str__函数的返回值return "我叫{},我为自己代言".format(self.name)def __del__(self): # 对象调用完销毁时,会调用此函数print("......我{}还会回来的......".format(self.name))hero1 = Hero("亚瑟")
hero2 = Hero("后羿")print(hero1)
print(hero2) # del hero1
# del hero2 # 把这两句del注释分别打开,会有不同的效果print("="*30)
小结:
方法 | 描述 |
---|---|
def __init__(self) | 创建对象的时候自动调用此方法 |
def __str__(self) | print(对象)时调用此方法 |
def __del__(self) | 对象被销毁的时候自动调用该方法,做一些收尾工作,如关闭打开的文件,释放变量等 |
私有属性与私有方法
一般情况下,私有的属性、方法都是不对外公布的,往往用来做内部的事情,起到安全的作用。
python没有像其它语言那样有public,private等关键词来修饰,而是在变量前加__来实现私有。
示例:
class People(object):__country = "中国" # 前面加上__,那么就做成了私有属性,就不能被类的外部直接调用def __init__(self, name, sex):self.name = nameself.__sex = sex # 前面加上__,那么就做成了私有属性,就不能被类的外部直接调用def __info(self): # 前面加上__,那么就做成了私有方法,就不能被类的外部直接调用print(self.name, self.sex)p1 = People("张三", "男")
# print(p1.sex)
# print(p1.__sex)
# print(p1.country)
# print(p1.__country)
# p1.info()
# p1.__info() # 这六句单独打开注释验证,都会报错。不能调用私有属性和私有方法
示例: 如果类的外部需要调用到私有属性的值,可以对私有属性单独定义一个类的方法,让实例通过调用此方法来调用私有属性(私有方法同理)
class People(object):__country = "中国"def __init__(self, name, sex):self.name = nameself.__sex = sex def __info(self):print(self.name, self.__sex)def show_sex(self):print(self.__sex)def show_country(self):print(self.__country)def show_info(self):People.__info(self)p1 = People("张三", "男")p1.show_sex()
p1.show_country()
p1.show_info()
小结: 内部属性不希望被外部调用与修改,就可以做成私有的.
继承
继承介绍
什么是继承?
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rZ9oMNEU-1602117221943)(图片/继承.png)]
class 人吃喝 玩拉睡
class 老师上课备课
class 工程师上班加班
继承的作用: 减少代码的冗余,便于功能的升级(原有的功能进行完善)与扩展(原没有的功能进行添加)
示例:
class People(object):def __init__(self, name, age):self.name = nameself.age = agedef eat(self):print("{}正在吃".format(self.name))def drink(self):print("{}正在喝".format(self.name))class Man(People): # 表示Man类继承父类(基类,超类)Peoplepassclass Woman(People): # 表示Woman类继承父类(基类,超类)Peoplepassm1 = Man("张三", 16)
m1.eat() # 继承了父类,就可以调用父类的方法
m1.drink() # 继承了父类,就可以调用父类的方法w1 = Woman("李四", 18)
w1.eat() # 继承了父类,就可以调用父类的方法
w1.drink() # 继承了父类,就可以调用父类的方法
方法重写
示例: 在子类里重写父类的方法
class People(object):def __init__(self, name, age):self.name = nameself.age = agedef eat(self):print("{}正在吃".format(self.name))def drink(self):print("{}正在喝".format(self.name))class Man(People):def eat(self): # 在子类中重写父类的方法print("{}正在吃肉".format(self.name))class Woman(People):def eat(self): # 在子类中重写父类的方法print("{}正在吃素".format(self.name))m1 = Man("张三", 16)
m1.eat()w1 = Woman("李四", 18)
w1.eat()
示例: 子类中增加方法和方法中调用方法
class People(object):def __init__(self, name, age):self.name = nameself.age = agedef eat(self):print("{}正在吃".format(self.name))def drink(self):print("{}正在喝".format(self.name))class Man(People):def eat(self):print("{}正在吃肉".format(self.name))def work(self): # 在子类中增加一个父类中没有的方法print("{}正在辛苦工作".format(self.name))self.eat() # 在方法中调用方法self.drink() # 也可以调用父类的方法class Woman(People):def eat(self):print("{}正在吃素".format(self.name))m1 = Man("张三", 20)
m1.work()
子类重新构造属性
示例:在子类重新构造属性
class People(object):def __init__(self, name, age):self.name = nameself.age = agedef eat(self):print("{}正在吃".format(self.name))def drink(self):print("{}正在喝".format(self.name))class Man(People):def eat(self):print("{}正在吃肉".format(self.name))def work(self):print("{}正在辛苦工作".format(self.name))self.eat()self.drink()class Woman(People):
# 下面这段代码就是在父类原来的name,age两个属性的基础上加上了一个love_shopping属性def __init__(self, name, age, love_shopping): super(Woman, self).__init__(name, age) # 这一句写法比较难记,对应父类的name,age属性self.love_shopping = love_shoppingdef eat(self):print("{}正在吃素".format(self.name))def shopping(self):if self.age >= 18 and self.love_shopping == True:print("{},去逛街吧".format(self.name))else:print("{},不要去逛街,乖乖呆家里面!".format(self.name))w1 = Woman("李四", 18, False)
w1.shopping()
多层继承
示例: 多层继承例一
class Grandfather(object):def house(self): # 爷爷类的方法print("a big house!")class Father(Grandfather): # 爸爸类继承爷爷类def car(self):print("a cool car!")class child(Father): # 孩子类继承爸爸类passp1 = child() # 实例化一个孩子
p1.house() # 这个孩子对象可以调用爷爷的方法
多重继承
支持面向对象编程的开发语言中,支持多重继承的语言并不多,像java,php这些都只支持单继承。支持多重继承的主要就是python,c++。
什么是多重继承?
答: 多重继承,即子类有多个父类(可以多于两个),并且具有它们的特征。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wFLxtbNp-1602117221946)(图片/多继承.png)]
示例: 多重继承例一
class Father(object):def sing(self):print("can sing")class Mother(object):def dance(self):print("can dance")class child(Father, Mother): # 继承Father,Mother两个父类passp1 = child()
p1.sing() # 可以用Father的方法
p1.dance() # 也可以用Mother的方法
两个对象交互
单继承实现
class People(object):def __init__(self, name, sex):self.name = nameself.sex = sexdef fall_in_love(self, obj):if self.sex == "男":print("{}向{}求婚".format(self.name, obj.name))elif self.sex == "女":print("{}要给{}生猴子".format(self.name, obj.name))else:print("性别输入有误")class Man(People): pass
class Woman(People):passm1 = Man("张三", "男")
w1 = Woman("李四", "女")m1.fall_in_love(w1) # w1传参给fall_in_love里的obj
w1.fall_in_love(m1)
多层继承实现
class People(object):def __init__(self, name, sex):self.name = nameself.sex = sexclass Love(People):def fall_in_love(self, obj):if self.sex == "男":print("{}向{}求婚".format(self.name, obj.name))elif self.sex == "女":print("{}要给{}生猴子".format(self.name, obj.name))else:print("性别输入有误")class Man(Love):pass
class Woman(Love):passm1 = Man("张三", "男")
w1 = Woman("李四", "女")m1.fall_in_love(w1)
w1.fall_in_love(m1)
多重继承实现
class People(object):def __init__(self, name, sex):self.name = nameself.sex = sexclass Love(object):def fall_in_love(self,obj):if self.sex == "男": # 这里的sex变量在Love类里并没有定义print("{}向{}求婚".format(self.name, obj.name))elif self.sex == "女":print("{}要给{}生猴子".format(self.name, obj.name))else:print("性别输入有误")class Man(People, Love): # Love里没有sex变量,People里有sex变量,多重继承合到一起就OKpassclass Woman(People, Love):passm1 = Man("张三", "男")
w1 = Woman("李四", "女")m1.fall_in_love(w1)
w1.fall_in_love(m1)
多态
多态: 一类事物的有多种形态。如水蒸汽,水,冰。
回顾下我们前面讲过: Python是强类型的**动态**解释型语言,这里的动态其实就是多态。
python是变量本身是没有类型的,变量的类型是由赋的值所决定的。值是int,变量就是int; 值是str,变量类型就是str。这其实就是一种多态。
python崇尚鸭子类型(ducking type): 鸭子类型是动态类型的一种风格。“当看到一只鸟走起来像鸭子、游泳起来像鸭子、叫起来也像鸭子,那么这只鸟就可以被称为鸭子。” 在鸭子类型中,关注的不是对象的类型本身,而是它是如何使用的。
作用: 接口统一
示例:
class Animal(object):def jiao(self):passclass Dog(Animal):def jiao(self):print("wang wang...")class Cat(Animal):def jiao(self):print("miao miao...")d1 = Dog()
c1 = Cat()d1.jiao() # 实例接类的方法来调用,结果是狗叫
c1.jiao() # 实例接类的方法来调用,结果为猫叫
示例:
class Dog(object):def jiao(self):print("汪汪...")class Cat(object):def jiao(self):print("喵喵...")def jiao(obj):obj.jiao()d1 = Dog()
c1 = Cat()jiao(d1) # 调用方式统一
jiao(c1) # 调用方式统一
把类做成模块给别人调用
假设下面的代码就是在当前项目目录下的一个模块,名为sound.py
class Dog(object):def jiao(self):print("汪汪...")class Cat(object):def jiao(self):print("喵喵...")def jiao(obj):obj.jiao()d1 = Dog()
c1 = Cat()jiao(d1)
jiao(c1)
当前项目目录另一个文件要调用上面的模块
import soundd1 = sound.Dog() # 实例化一个对象叫d1sound.jiao(d1) # 把d1对象做为一个参数传给sound模块里的jiao函数
课后示例与练习
示例: 一个英雄与怪物互砍小游戏
import random# 定义英雄类
class Hero(object):def __init__(self, name):self.name = nameself.hp = 100 # 血量self.attack = random.randint(31, 100) # 随机产生攻击值self.defense = 30# 显示英雄信息def __str__(self):return "名字:%s 血量:%s 攻击:%d 防御:%d" % (self.name, self.hp, self.attack, self.defense)# 攻击函数def fight(self, monster):# 计算怪物掉血多少mhp = self.attack - monster.defense# 减少怪物血量monster.hp = monster.hp - mhp# 提示信息print("英雄[%s]对怪物[%s]造成了%d伤害!" % (self.name, monster.name, mhp))# 定义怪物类
class Monster(object):def __init__(self, name):self.name = nameself.hp = 100 # 血量self.attack = random.randint(31, 100) # 随机产生攻击值self.defense = 30# 显示怪物信息def __str__(self):return "名字:%s 血量:%s 攻击:%d 防御:%d" % (self.name, self.hp, self.attack, self.defense)# 攻击函数def fight(self, hero):# 计算怪物掉血多少mhp = self.attack - hero.defense# 减少怪物血量hero.hp = hero.hp - mhp# 提示信息print("怪物[%s]对英雄[%s]造成了%d伤害!" % (self.name, hero.name, mhp))# 创建对象
hero = Hero("一刀满级")
# 创建怪物
monster = Monster("打死我爆好装备")
# 回合数
my_round = 1
# 开始回合战斗
while True:input()print(hero)print(monster)print("-"*50)print("当前第%d回合:" % my_round)hero.fight(monster)if monster.hp <= 0:print("英雄[%s]击败了怪物[%s],顺利通关!" % (hero.name, monster.name))breakmonster.fight(hero)if hero.hp <= 0:print("怪物[%s]仰天大笑,哈哈哈,弱鸡!" % monster.name)breakmy_round += 1print("Game Over!")
参考: 下例把paramiko的远程执行命令,上传,下载功能简单地做成了面向对象编程的方法。有兴趣可以拓展
- 本机要准备一个文件,记录你所管理的所有机器的IP与端口,如下面这种
# cat /tmp/1.txt
10.1.1.11:22
10.1.1.12:3333
10.1.1.13:22
10.1.1.14:2222
10.1.1.15:22
- 本机要产生空密码密钥对,并将公钥都拷贝到所管理的所有机器上,实现本机连接远程机器全免密
# ssh-keygen # 三次回车在本机产生空密码密钥对
# ssh-copy-id 10.1.1.11
# ssh-copy-id 10.1.1.12
# ssh-copy-id 10.1.1.13
# ssh-copy-id 10.1.1.14
# ssh-copy-id 10.1.1.15
- 在本机项目目录下创建一个
hostcmd.py
文件,自定义做成模块(我这里模块名为hostcmd)
import paramiko
import osclass Host_cmd(object):def __init__(self, ip, port, input_cmd):self.ip = ipself.port = portself.input_cmd = input_cmddef exec_cmd(self):ssh = paramiko.SSHClient()ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy)private_key = paramiko.RSAKey.from_private_key_file("/root/.ssh/id_rsa")ssh.connect(hostname=self.ip, port=self.port, username="root", pkey=private_key)stdin, stdout, stderr = ssh.exec_command(self.input_cmd)print(stdout.read().decode())print(stderr.read().decode())ssh.close()class Host_get_put(object):def __init__(self, ip, port, local_file, remote_file):self.ip = ipself.port = portself.local_file = local_fileself.remote_file = remote_filedef get(self):trans = paramiko.Transport(self.ip, self.port)private_key = paramiko.RSAKey.from_private_key_file("/root/.ssh/id_rsa")trans.connect(username="root", pkey=private_key)sftp = paramiko.SFTPClient.from_transport(trans)dirname = os.path.dirname(self.local_file) # 因为从多台机器上下载文件到本地会冲突,所以需要创建目录来区分aaa = os.path.join(dirname, self.ip)os.chdir("/")bbb= os.path.relpath(self.local_file)absname = os.path.join(aaa, bbb)os.makedirs(os.path.dirname(absname))sftp.get(self.remote_file, absname)trans.close()def put(self):trans = paramiko.Transport(self.ip, self.port)private_key = paramiko.RSAKey.from_private_key_file("/root/.ssh/id_rsa")trans.connect(username="root", pkey=private_key)sftp = paramiko.SFTPClient.from_transport(trans)sftp.put(self.local_file, self.remote_file)trans.close()class Host_list(object):def __init__(self, filepath):self.filepath = filepathdef remote_exec(self):print("""请选择你的操作:1-远程执行命令2-远程文件下载3-远程文件上传""")choice = int(input("请选择:"))if choice == 1:input_cmd = input("请输入你要执行的命令: ")f = open(self.filepath, mode="r")for line in f:read_ip = line.strip().split(":")[0]read_port = line.strip().split(":")[1]print("{}上执行{}命令".format(read_ip, input_cmd))host = Host_cmd(read_ip, read_port, input_cmd)host.exec_cmd()f.close()elif choice == 2:remote_file = input("输入你要下载的远程文件路径:")local_file = input("输入你要下载到本地的文件路径:")f = open(self.filepath, mode="r")for line in f:read_ip = line.strip().split(":")[0]read_port = line.strip().split(":")[1]host = Host_get_put(read_ip, int(read_port), local_file, remote_file) # 端口要转成inthost.get()f.close()elif choice == 3:local_file = input("输入你要上传的本地文件路径:")remote_file = input("输入你要上传到远程的文件路径:")f = open(self.filepath, mode="r")for line in f:read_ip = line.strip().split(":")[0]read_port = line.strip().split(":")[1]host = Host_get_put(read_ip, int(read_port), local_file, remote_file) # 端口要转成inthost.put()f.close()else:print("选择有误")
- 准备另一个文件调用hostcmd模块
import hostcmdh1 = hostcmd.Host_list('/tmp/1.txt')h1.remote_exec()
学python历程中相关推荐
- python中数据读写_【循序渐进学Python——文件中数据的读写以及操作】
[循序渐进学Python--文件中数据的读写以及操作] [循序渐进学Python--文件中数据的读写以及操作] [循序渐进学Python--文件中数据的读写以及操作] 相信大家都想过自己有一天,可以用 ...
- 学python工作中_Python -- 我工作学习中的好帮手
最近在工作中,我需要在Solaris上写很多测试或者辅助工作的脚本.因为单位只推荐使用Perl或者Python,所以我不能选择我最喜欢的Ruby,当然我不会去选择Perl(不是说Perl不好,只是我不 ...
- 零基础自学python的app-零基础学Python需要用哪些软件?
零基础学Python需要用哪些软件?对于没什么基础的初学者来讲,可能连Python要用到的软件都不太清楚.下面小编就从最基础的软件讲起,告诉大家随着学习Python的深入,我们都需要使用哪些软件.感兴 ...
- 父与子的编程之旅:与小卡特一起学Python.pdf
下载地址:网盘下载 编辑推荐 编程是一项充满乐趣的挑战,想要上手也非常容易!这本<父与子的编程之旅:与小卡特一起学Python>中,Warren和Carter父子以亲切的笔调.通俗的语言, ...
- 爆火的《看漫画学Python》出PDF版了,文末自取
学习Python的小伙伴大部分应该都知道<看漫画学Python:有趣.有料.好玩.好用(全彩版)>这本书! 毕竟,如果在B站搜索"漫画 Python"等相关关键词,会看 ...
- 《看漫画学Python(全彩版)》
学习Python的小伙伴大部分应该都知道<看漫画学Python:有趣.有料.好玩.好用(全彩版)>这本书! 毕竟,如果在B站搜索"漫画 Python"等相关关键词,会看 ...
- 小白学Python必看的五本书,堪称入门神器!
Ø前言 Python是一种通用的解释型编程,主要用于Web开发.机器学习和复杂数据分析.Python对初学者来说是一种完美的语言,因为它易于学习和理解,随着这种语言的普及,Python程序员的机会也越 ...
- 《以前工作中的三大痛点,只因他们没学Python》
1,工作中碰到新业务,拿出作业指导书依次操作,做到一半发现作业指导书逻辑不通,业务进行不下去了.如果起草的人能事先模拟一遍解释器,那么能省去很大的麻烦 2,不懂业务的纯"管理者"喜 ...
- c字符串中包含双引号_零基础学Python:一文看懂数字和字符串
来源:大数据DT 本文约2000字,建议阅读6分钟 数据类型是构成编程语言语法的基础.[ 导读 ]数据类型是构成编程语言语法的基础.不同的编程语言有不同的数据类型,但都具有常用的几种数据类型.Pyth ...
最新文章
- CVD-ALD前驱体材料
- 阿里达摩院2020趋势第一弹:感知智能的“天花板”和认知智能的“野望”
- java拍照搜题软件下载_修改版|学生福利!!免费拍照搜题秒出答案,扫一扫作业出答案!...
- 构建Hybrid应用-构建ionic开发环境
- Android资源文件
- python爬虫如何从一个页面进入另一个页面-Python爬虫 (一):爬取一个简单的静态网页...
- jmeter---linux安装运行
- 为了帮视障人士“看见”,阿里工程师做了哪些努力?
- Matlab guide菜单+快捷菜单的使用
- 关于python中self
- eureka注册中心之服务提供者
- 安装apk报错INSTALL_FAILED_UPDATE_INCOMPATIBLE的解决方法
- python基础教程廖雪峰云-Python 基础教程
- Scala: 简介和安装
- git的SSH上传方式
- paip.Adblock屏蔽onlinedown华军软件园的4秒下载广告总结..
- 架设传奇时打开DBC数据库出错或读取DBC失败解决方法
- carrot2 Workbench org.apache.http.client.HttpResponseException: Not Found 以及其他类找不到错误的解决办法
- FlashBuilder 4.6序列号破解
- 图像处理:图像灰度化