- 常量:固定 的值,不能发生改变
- 变量:里面盛放的值,随时可以发生改变
- 声明一个变量,相当于在内存上开了一块空间
# 变量的赋值
# 定义一个变量的名称为 num
num = 100
二、变量的类型
# 在Python中,不需要单独去声明变量的类型
price = 68.00 # 价格
count = 2 # 数量
money = 136.00 # 总价
name = "alex" # 客户名称
tel = "18222837227" # 电话
sale = True # 是否有货
hdie = False # 是否允许匿名评价
print(type(price)) # 查看 price 的类型
print(type(sale)) # 查看 sale 的类型
money = money - 5 #并没有创建新的变量,而是使用原来的变量
----------------------------------------------
<class 'float'>
<class 'bool'>
三、变量的命名规则
-
标识符由字母、下划线和数字组成,且数字不能开头
- 中文也可用作变量名,在Python 3.x版本中,语言的编码格式是 UTF - 8 ,支持中文
-
python中的标识符是区分大小写的
-
驼峰命名法
- 小驼峰式命名法(lower camel case): 第一个单词以小写字母开始;第二个单词的首字母大写,例如:myName、aDog
- 大驼峰式命名法(upper camel case): 每一个单字的首字母都采用大写字母,例如:FirstName、LastName
- 还有一种命名法是用下划线“_”来连接所有的单词,比如send_buf,
student_name = 'James' #下划线命名法 studentName = 'Curry' #小驼峰命名法 UserLoginFlag = 'xxxxxx' #大驼峰命名法
-
关键字
- python一些具有特殊功能的标识符,这就是所谓的关键字
- 关键字,是python已经使用的了,所以不允许开发者自己定义和关键字相同的名字的标识符
Anna = 18 anna = 16 # 这是两个变量 print(id(Anna),id(anna)) #查看变量的ID # 关键字就是系统已经定义好的变量名,开发者不能再使用了 import keyword print(keyword.kwlist) # 打印当前系统的关键字 ----------------------------------------------------------- ['False', 'None', 'True', 'and', 'as', 'assert', 'async', 'await', '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']
password = input("请输入密码:")
print('您刚刚输入的密码是:%d' % password)
- input()的小括号中放入的是,提示信息,用来在获取数据之前给用户的一个简单提示
- input()在从键盘获取了数据以后,会存放到等号右边的变量中
- input()会把用户输入的任何值都作为字符串来对待
函数 | 说明 |
---|---|
int(x) | 将x转换为一个整数 |
float(x ) | 将x转换为一个浮点数 |
complex(real [,imag ]) | 创建一个复数,real为实部,imag为虚部 |
str(x ) | 将对象 x 转换为字符串 |
repr(x ) | 将对象 x 转换为表达式字符串 |
eval(str ) | 用来计算在字符串中的有效Python表达式,并返回一个对象 |
tuple(s ) | 将序列 s 转换为一个元组 |
list(s ) | 将序列 s 转换为一个列表 |
chr(x ) | 将一个整数转换为一个Unicode字符 |
ord(x ) | 将一个字符转换为它的ASCII整数值 |
hex(x ) | 将一个整数转换为一个十六进制字符串 |
oct(x ) | 将一个整数转换为一个八进制字符串 |
bin(x ) | 将一个整数转换为一个二进制字符串 |
"""
数据类型转换
字符串类型想要转换数字类型,需要输入阿拉伯数字
int不能转换带.的字符串,float可以
float可以被int转换
"""
passwd = input("请输入您的yhk密码:")
#passwd 是一个字符串类型的变量,如果他的值,看起来像数字,就可以转换成数字类型(整数,浮点型)
#将passwd转换成int
passwd_int = int(passwd) #不能接受带.的数据
print(passwd_int,type(passwd_int))
#将passwd转换成float
passwd_float = float(passwd)
print(passwd_float,type(passwd_float))
3、变量输入演练—超市买苹果
-
需求
收银员输入 苹果的价格,单位:元/斤
收银员输入 用户购买苹果的重量,单位:斤
计算并且 输出 付款金额
-
实验代码
#收银员输入 苹果的价格,单位:元/斤 price = input("请输入苹果的价格:") #收银员输入 用户购买苹果的重量,单位:斤 weight = input("请输入苹果的重量:") #计算并且 输出 付款金额 #在计算之前,需要进行数据类型转换 money = float(price) * float(weight) print("苹果的总价格是",money,"元")
name = "James"
age = "37"
gender = "boy"
"""
print(value,...,sep=' ',end='\n') #print函数的完整形式
在值之间插入字符串,默认是一个 space
sep: string inserted between values, default a space.
在值的最后追加一个字符串,默认是一个\n
end: string appended after the last value, default a newline.
"""
print(name,age,gender,sep='-',end='\n')
2、常见的转义符
转义字符 | 说明 |
---|---|
\n | 换行符,将光标位置移到下一行开头。 |
\r | 回车符,将光标位置移到本行开头。 |
\t | 水平制表符,也即 Tab 键,一般相当于四个空格。 |
\a | 蜂鸣器响铃。注意不是喇叭发声,现在的计算机很多都不带蜂鸣器了,所以响铃不一定有效。 |
\b | 退格(Backspace),将光标位置移到前一列。 |
\ | 反斜线 |
\’ | 单引号 |
\" | 双引号 |
\ | 在字符串行尾的续行符,即一行未完,转到下一行继续写。 |
person = "杨宇波"
address = " 天津市西青区天津市大学软件学院"
phone = "2150511032"
# 可以使用 “+” 来进行字符串的拼接
print("收件人是:" + person + ",地址为:" + address + "手机号:" + phone)
print("收件人是 %s,地址是 %s,电话号为: %s" % (person, address, phone))
# 使用占位符
price = 2.00
weight = 15
# %d代表整型变量,%.02f表示浮点型变量(保留两位小鼠),%s代表字符串
print("西瓜的价格是 %.02f 元/斤,重量是 %d 斤" % (price, weight))
print("西瓜的价格是 {} 元/斤,重量是 {} 斤".format(price, weight))
print("西瓜的价格是 {0} 元/斤,重量是 {1} 斤".format(price, weight))
print(f"西瓜的价格是 {price} 元/斤,重量是 {weight} 斤") #Python3.7以后支持
4、拳皇实验
- 实验代码
"""
游戏:拳皇97
输入游戏玩家姓名
输入密码
输入充值信息
"""
#游戏头
print("*" * 50)
print(" " * 22,"拳皇97","" * 22)
print("*" * 50)
#输入用户名
username = input("请输入用户名:")
#输入密码
password = input("请输入密码:")
#提示充值
balance = 10
print("恭喜登录成功!当前余额为{},请出入充值金额".format(balance))
#接受充值
coin = input("请输入您要充值的金额")
balance = balance + int(coin)
#输出 x 充值成功!当前余额位x,请开始游戏
print("{}充值成功! 当前余额为{},请开始游戏!".format(username,balance))
- 输出结果
**************************************************
拳皇97
**************************************************
请输入用户名:123
请输入密码:123
恭喜登录成功!当前余额为10,请出入充值金额
请输入您要充值的金额10
123充值成功! 当前余额为20,请开始游戏
5、LOL实验
- 实验代码
"""
游戏:英雄联盟
输入角色
输入拥有的装备
输入想购买的装备
输入付款金额
输出:xxxx 拥有xxxx,花了xxxx元钱
"""
banner = " / / // ) ) / /\n / / // / / / /\n / / // / / / / \n / / // / / / /\n / /____/ / ((___/ / / /____/ /"
print(f"欢迎来到:\n {banner}")
print("-" * 50)
# 输入角色
role = input("请输入英雄名称:")
# 输入拥有的装备
equipment = input("请输入拥有的装备:")
print(f"{role}带着{equipment}出门了!")
# 输入想购买的装备
wanna_equ = input("请输入想要购买的装备名:")
# 输入付款金额
pay = input("请输入付款金额:")
# 输出:xxxx 拥有xxxx,花了xxxx元钱
print("{}购买了{},花费了{}金币!".format(role, wanna_equ,pay))
- 输出结果
欢迎来到:
/ / // ) ) / /
/ / // / / / /
/ / // / / / /
/ / // / / / /
/ /____/ / ((___/ / / /____/ /
--------------------------------------------------
请输入英雄名称:疾风剑豪
请输入拥有的装备:无尽之刃
疾风剑豪带着无尽之刃出门了!
请输入想要购买的装备名:鬼索的狂暴之刃
请输入付款金额:1000
疾风剑豪购买了鬼索的狂暴之刃,花费了1000金币!
六、扩展赋值运算符
赋值运算符 | 说 明 | 举 例 | 展开形式 |
---|---|---|---|
= | 最基本的赋值运算 | x = y | x = y |
+= | 加赋值 | x += y | x = x + y |
-= | 减赋值 | x -= y | x = x - y |
*= | 乘赋值 | x *= y | x = x * y |
/= | 除赋值 | x /= y | x = x / y |
%= | 取余数赋值 | x %= y | x = x % y |
**= | 幂赋值 | x **= y | x = x ** y |
//= | 取整数赋值 | x //= y | x = x // y |
运算符 | 描述 | 实例 |
---|---|---|
+ | 加 - 两个对象相加 | a + b 输出结果 30 |
- | 减 - 得到负数或是一个数减去另一个数 | a - b 输出结果 -10 |
* | 乘 - 两个数相乘或是返回一个被重复若干次的字符串 | a * b 输出结果 200 |
/ | 除 - x除以y | b / a 输出结果 2 |
% | 取模 - 返回除法的余数 | b % a 输出结果 0 |
** | 幂 - 返回x的y次幂 | a**b 为10的20次方, 输出结果 100000000000000000000 |
// | 取整除 - 返回商的整数部分(向下取整) | >>> 9//2 4 >>> -9//2 -5 |
运算符 | 描述 | 实例 |
---|---|---|
== | 等于 - 比较对象是否相等 | (a == b) 返回 False。 |
!= | 不等于 - 比较两个对象是否不相等 | (a != b) 返回 true. |
<> | 不等于 - 比较两个对象是否不相等。python3 已废弃。 | (a <> b) 返回 true。这个运算符类似 != 。 |
> | 大于 - 返回x是否大于y | (a > b) 返回 False。 |
< | 小于 - 返回x是否小于y。 | (a < b) 返回 true。 |
>= | 大于等于 - 返回x是否大于等于y。 | (a >= b) 返回 False。 |
<= | 小于等于 - 返回x是否小于等于y。 | (a <= b) 返回 true。 |
运算符 | 逻辑表达式 | 描述 | 实例 |
---|---|---|---|
and | x and y | 布尔"与" - 如果 x 为 False,x and y 返回 False,否则它返回 y 的计算值。 | (a and b) 返回 20。 |
or | x or y | 布尔"或" - 如果 x 是非 0,它返回 x 的值,否则它返回 y 的计算值。 | (a or b) 返回 10。 |
not | not x | 布尔"非" - 如果 x 为 True,返回 False 。如果 x 为 False,它返回 True。 | not(a and b) 返回 False |
- 与 / 并且
- 两个条件同时满足,返回 True
- 只要有一个条件不满足,就返回 False
条件1 | 条件2 | 结果 |
---|---|---|
成立 | 成立 | 成立 |
成立 | 不成立 | 不成立 |
不成立 | 成立 | 不成立 |
不成立 | 不成立 | 不成立 |
- 或 / 或者
- 两个条件只要有一个满足,返回 True
- 两个条件都不满足,返回 False
条件1 | 条件2 | 结果 |
---|---|---|
成立 | 成立 | 成立 |
成立 | 不成立 | 成立 |
不成立 | 成立 | 成立 |
不成立 | 不成立 | 不成立 |
- 非 / 不是
条件 | 结果 |
---|---|
成立 | 不成立 |
不成立 | 成立 |
结果 if 表达式 else 结果
如果是真,返回前面的结果,如果是假,则返回后面的结果
判断语句与循环语句 一、 if判断语句基本格式介绍 if 要判断的条件:
条件成立时,要做的事情
二、 if-else的使用格式
if 条件:
满足条件时要做的事情1
满足条件时要做的事情2
满足条件时要做的事情3
...(省略)...
else:
不满足条件时要做的事情1
不满足条件时要做的事情2
不满足条件时要做的事情3
...(省略)...
三、if…elif…else…语句格式
1、elif的功能
if xxx1:
事情1
elif xxx2:
事情2
elif xxx3:
事情3
说明:
- 当xxx1满足时,执行事情1,然后整个if结束
- 当xxx1不满足时,那么判断xxx2,如果xxx2满足,则执行事情2,然后整个if结束
- 当xxx1不满足时,xxx2也不满足,如果xxx3满足,则执行事情3,然后整个if结束
score = 77
if score>=90 and score<=100:
print('本次考试,等级为A')
elif score>=80 and score<90:
print('本次考试,等级为B')
elif score>=70 and score<80:
print('本次考试,等级为C')
elif score>=60 and score<70:
print('本次考试,等级为D')
elif score>=0 and score<60:
print('本次考试,等级为E')
2、注意点
- 可以和else一起使用
if 性别为男性:
输出男性的体重
...
elif 性别为女性:
输出女性的体重
...
else:
第三种性别的体重
...
- 当 “性别为男性” 满足时,执行 “输出男性的体重”的相关代码
- 当 “性别为男性” 不满足时,如果 “性别为女性”满足,则执行 “输出女性的体重”的相关代码
- 当 “性别为男性” 不满足,“性别为女性”也不满足,那么久默认执行else后面的代码,即 “第三种性别的体重”相关代码
for 临时变量 in 列表或者字符串等可迭代对象:
循环满足条件时执行的代码
2、for循环练习
for i in range(1,6):
if i == 3 :
print("这个馒头有毒!!!")
print("没有吃饱/(ㄒoㄒ)/~~")
break
else:
print(f"正在吃第{i}个馒头")
else: #当for循环结束后,会执行 else 语句,如果被break打断,就不会再执行!
print("吃饱了~~~~")
五、while循环
1、while循环
while 条件:
条件满足时,做的事情1
条件满足时,做的事情2
条件满足时,做的事情3
...(省略)...
2、while练习
i = 1
while i <= 5:
print(f"这是第{i}遍Hello World!")
i += 1
3、while嵌套的格式
while 条件1:
条件1满足时,做的事情1
条件1满足时,做的事情2
条件1满足时,做的事情3
...(省略)...
while 条件2:
条件2满足时,做的事情1
条件2满足时,做的事情2
条件2满足时,做的事情3
...(省略)...
4、while练习
i = 1
while i <= 9:
j = 1
while j <= i:
print(f"{j}*{i}={i*j}",end='\t')
j += 1
print()
i+=1
字符串、列表、元组、字典、集合
一、列表
1、列表的格式
- 列表中的元素可以是不同类型的
name_list = ["James", "Curry", "Young", "Kuzma"]
2、打印列表
name_list = ["James", "Curry", "Young", "Kuzma"]
print(name_list[0])
print(name_list[1])
print(name_list[2])
James
Curry
Young
3、列表的循环遍历
#列表的遍历,就是将这个列表中的元素,按照顺序进行某些 *** 作
name_list_1 = [1, 4, 5, 6, 87, 9, 2, 3, 4, 6, 7, 8]
name_list_1.sort(reverse=True)
for item in name_list_1 :
print(item)
87
9
8
7
6
6
5
4
4
3
2
1
4、列表的相关 *** 作
①、添加元素
-
append
-
通过append可以向列表添加元素
-
name_list = ["张三", "李四", "王五"] # 增加元素 # 1、.append(),会在列表的最后增加一个元素 name_list.append("Curry") print(name_list)
-
- extend
-
通过extend可以将另一个集合中的元素逐一添加到列表中
-
# extend 将两个列表拼接到一起 name_list = ["张三", "李四", "王五"] name_list1 = ["Thompson", "Poole", "Monk", "Reaves"] name_list.extend(name_list1) print(name_list)
-
- insert
-
insert(index, object) 在指定位置index前插入元素object
-
# .insert() ,在列表范围内,根据索引值 ,插入一个值,后续的元素,往后排列 name_list = ["张三", "李四", "王五"] name_list.insert(3, "Young") print(name_list) # 超出范围会拍到最后 name_list.insert(66, "Kuzma") print(name_list)
-
- in, not in
-
in(存在),如果存在那么结果为true,否则为false
-
not in(不存在),如果不存在那么结果为true,否则false
-
# in成员运算符 name = "James" result = 'J' in name print(result) # True result = 'Jm' in name print(result) # False
-
-
del
-
根据下标进行删除
-
name_list = ["James", "Curry", "Young", "Kuzma"] # del 关键字 会将列表或者元素从内存中直接删除 del name_list[0] print(name_list) # 直接删除列表 del name_list print(name_list)
-
-
pop
-
删除最后一个元素
-
name_list = ["James", "Curry", "Young", "Kuzma"] # pop删除列表的最后一个元素 name_list.pop() print(name_list) # pop()可以指定索引删除对应值 name_list.pop(0) print(name_list)
-
-
remove
-
根据元素的值进行删除
-
name_list = ["James", "Curry", "Young", "Kuzma"] # remove 删除指定元素 print(name_list) name_list.remove("Young") print(name_list)
-
-
sort
-
升序排列
-
# .sort() 升序排列 num_list = [1, 4, 5, 6, 87, 9, 2, 3, 4, 6, 7, 8] # 将整个列表中的元素进行升序排列 num_list.sort() print(num_list) # 将整个列表中的元素进行降序排列 num_list.sort(reverse=True) print(num_list)
-
-
reverse
-
将列表的元素倒着输出,不会排序
-
# .reverse()方法,将列表的元素倒着输出,不会排序 name_list_1 = [1, 4, 5, 6, 87, 9, 2, 3, 4, 6, 7, 8] name_list_1.reverse() print(name_list_1)
-
-
count
-
统计某个元素出现的次数
-
# .count()函数 name_list = ["James", "Curry", "James", "Young", "Kuzma", "James"] print(name_list.count("James")) # 这时候remove删除,只会删除第一个元素 name_list.remove("James") print(name_list)
-
# 字符串可以看作是一列字符的有序的集合,但是在 Python ,没有字符(char)的概念,只有string的概念
name = "abcdefg"
"""
"""
# 下列三个变量的ID相同
letter = 'a'
letter_01 = "a"
letter_02 = '''a'''
print(id(letter), id(letter_01), id(letter_02))
print(letter is letter_01 is letter_02)
s1 = input("请输入:")
s2 = input("请出入:")
print(s1 == s2)
print(s1 is s2) # 如果输入的字符串长度是0或者1,结果为True,否则为False
2、切片
切片是指对 *** 作的对象截取其中一部分的 *** 作。字符串、列表、元组都支持切片 *** 作。
-
切片的语法
- [起始:结束:步长]
-
""" 索引分为正索引和负索引 正索引:从0开始的第几个 负索引:倒数第几个 语法: str[start:end:方向(±)/步长] 方向:切片的时候,字符串的输出方向 + :正着输出 - :反着输出 默认情况下是正向输出 步长:出去切片值的时候,取值的间隔 """ # 切片1:根据索引取值 filename = "picture.png" print(filename[5]) # 输出:r # 切片2:截取部分字符串 print(filename[0:7]) # 取从0到7之前的元素,包前不包后 print("索引5对应的元素为:{}".format(filename[7])) # 省略开头 print(filename[7:]) # 从7开始一直取到最后 # 省略结尾 print(filename[:7]) # 从开头一直取值到6 # 取负数值 print(filename[8:-1]) # 从第8个元素开始,取值到倒数第1个之前(不包含倒数第1个) print(filename[:-2]) # 从开头取值,一直取值到倒数第2个之前 print(filename[-2:]) # 从倒数第2个值取值,取到结尾 # [::] 将这个字符串取值,从前取到最后 print(filename[::]) #方向/步长,步长默认为1 print(filename[::-1]) #反着输出 print(filename[::-2]) #带有步长的切片 print(filename[-1:-5:-1]) #切片默认从左到右,-1代表从右向左取值
"""
1、和大小写相关的
"""
message = "Who do not do the next, first I do !"
# .capitalize()
print(message.capitalize())
# .title() #将每个单词的首字母大写
print(message.title())
# .upper() #将单词全部转换为大写
str_upper = message.upper()
print(str_upper)
# .lower() #将单词全部转换为小写
print(message.lower())
# .istitle() 返回的结果是bool类型的 Ture False 判断字符串是否符合 title的格式(每个单词开头都大写)
# 是 返回 True ,否 返回 False
str2 = "Who Do Not Do The Next, First I Do !"
print(str2.istitle())
print(message.istitle())
3.2、find
检测 str 是否包含在 mystr中,如果是返回开始的索引值,否则返回-1
mystr = 'hello world itcast and itcastcpp
mystr.find(str, start=0, end=len(mystr))
3.3、index
跟find()方法一样,只不过如果str不在 mystr中会报一个异常.
mystr = 'hello world itcast and itcastcpp
mystr.index(str, start=0, end=len(mystr))
3.4、count
返回 str在start和end之间 在 mystr里面出现的次数
mystr = 'hello world itcast and itcastcpp
mystr.count(str, start=0, end=len(mystr))
3.5、replace
把 mystr 中的 str1 替换成 str2,如果 count 指定,则替换不超过 count 次.
mystr = 'hello world itcast and itcastcpp
mystr.replace(str1, str2, count=mystr.count(str1))
3.6、split
以 str 为分隔符切片 mystr,如果 maxsplit有指定值,则仅分隔 maxsplit+1 个子字符串
mystr = 'hello world itcast and itcastcpp
mystr.split(str=" ", 2)
3.7、strip
能去除字符串前后的空格
"""
strip lstrip rstrip
strip 能去除字符串前后的空格
"""
str1= " Lebron James "
str2 = str1.strip()
print(str1)
print(str2)
#去除左边的空格
str3 = str1.lstrip()
print(str3)
#去除右边的空格
str4=str1.rstrip()
print(str4)
三、元组
1、元组的介绍
Python的元组与列表类似,不同之处在于元组的元素不能修改。元组使用小括号,列表使用方括号。
# 定义一个元组
tuple_1 = () # 没有元素的元组, 是一个空元组
print(type(tuple_1))
tuple_1_1 = tuple() # 定义空元组的方法
print(type(tuple_1_1))
tuple_2 = ("a", 1) # 假如只有一个元素需要加,才能构成元组
print(type(tuple_2))
print(tuple_2.index("a")) # 元组可以通过值取索引
print(tuple_2[0]) # 可以通过索引取值 元组是一个有序的数据的集合
a = 1
b = 2
a, b = b, a
print(a, b)
2、拆包与装包
t1 = (2, 3, 6, 8, 0, 5)
# 可以看到表达式的左边没用括号,表达式的右边,是带括号的,t1不可以直接对左边的表达式赋值
# 需要先将t1的括号去掉(拆包),然后在给左边的变量进行赋值
a, *b, c = t1 # *b代表可以接受非固定的数的元素
print(b) # [3, 6, 8, 0]
print(*b) # 3 6 8 0
# *b不属于任何类型,如果要使用*b,则对*b进行装包 b= [3 6 8 0](装包)
t1 = (9,)
a, *b = t1
print(a, b) # 9 []
x, y, *z = "hello"
print(x, y, z) # h e ['l', 'l', 'o'],z并不等于"llo",而是一个列表
print(*z) # l l o
# 列表的拆包和装包
x, y, *z = ['aa', 6, 'hello', 'good', 'happy', 'Lucky']
print(x,y,z)
print(*z)
四、字典
1、字典的格式
tom = {"name":"tom", #"name":"tom"叫做一个键值对
"age":18,
"height":1.75,
"weight":75}
- 字典和列表一样,也能够存储多个数据
- 列表中找某个元素时,是根据下标进行的
- 字典中找某个元素时,是根据’名字’(就是冒号:前面的那个值,例如上面代码中的’name’、age’、‘height’)
- 字典的每个元素由2部分组成,键:值。
info = {'name':'杨宇波', 'id':100, 'sex':'f', 'address':'天津市大学软件学院'}
print(info['name'])
print(info['address'])
杨宇波
天津市大学软件学院
3、字典的常见 *** 作
3.1、添加元素
info = {'name': '杨宇波', 'id': 100, 'sex': 'f', 'address': '天津市大学软件学院'}
info['phone'] = "123123123"
print(info)
{'name': '杨宇波', 'id': 100, 'sex': 'f', 'address': '天津市大学软件学院', 'phone': '123123123'}
3.2、删除元素
"""
删除 del pop()
pop(pop(key[,defalut]) )
"""
dict_2 = {"张三": 100, "李四": 40, "王五": 100, "小赵": 110}
# del dict_2["王五"]
# print(dict_2)
print(dict_2.pop("王五", "没有这个键值对")) # pop("要删除的键","如果字典中没有王五这个键,则提示的信息")
print(dict_2)
print(dict_2.popitem()) # 随机删除,一般删除最后一一个
3.3、items
-
字典的解封装,将字典拆包,生成 一个dict_items 的一个集合,dict_items的每个元素都是一个元组,元组包含了键值对
-
""" items() #字典的解封装,将字典拆包,生成 一个dict_items 的一个集合, dict_items的每个元素都是一个元组,元组包含了键值对 """ result = dict_1.items() # 数据类型是dict_items print(result, type(result)) for key, value in dict_1.items(): if value > 90: print(key) # values()和key()是字典的属性,而不是字典解封装的属性 print(dict_1.keys()) print(dict_1.values())
-
将两个字典合并成1个
-
""" update() 将两个字典合并成1个 """ dict1 = {0: "tom", 1: "jack", 2: "lucy"} # 字典中不能出现相同的键,如果在合并字典的时候。有相同的键,则保留后面字典键对应的值 dict2 = {0: "lily", "4": "ruby"} dict1.update(dict2) # 保留dict2 的键 print(dict1)
"""
fromkeys(seq,[]) #[]里可以设定每个键的默认值
#seq是一个序列,dict.fromkeys() 这个函数 会根据这个序列,来形成一个新字典,
会以序列中的元素作为字典的键,可以不赋值,如果没用赋值,则每个键对应的值为None
"""
list = ["alex", "box", "alice", "tom"]
# 创建一个新字典,使用list1的元素作为字典的键
new_dict = dict.fromkeys(list) # 以列表的每个元素作为字典的键,没用给字典的键赋值,为None(空)
print(new_dict)
{'alex': None, 'box': None, 'alice': None, 'tom': None}
五、集合
- 集合(set)是一个无序的不重复元素序列。
-
可以使用大括号 { } 或者 set() 函数创建集合,注意:创建一个空集合必须用 set() 而不是 { },因为 { } 是用来创建一个空字典。
创建格式:
parame = {value01,value02,...} 或者 set(value)
定义空集合
my_set = set()
#定义空字典
my_dict = dict()
my_dict1 = {}
#定义空元组
my_tuple = tuple ()
my_tuple1 = ()
#定义空列表
my_list = list()
my_list1 = []
3、集合的基本 *** 作
-
添加元素.add()
s1.add("James") s1.add("Curry") s1.add("Young") print(s1) # 集合的元素是无序的,所以每次输出的结果不同
-
.update()追加
tuple = ("Howard", "Westbrook") #追加元组 s1.update(tuple) print(s1) tuple2 = {"Anthony", "George"} #追加集合 s1.update(tuple2) print(s1) tuple3 = ["Durant", "Paul"] #追加列表 s1.update(tuple3) print(s1) tuple4 = {"key": "value", } # 如果添加的是字典,则添加的元素是字典的键 s1.update(tuple4) print(s1) str1 = "Yang" s1.update(str1) # 会将字符串拆分成单个字符,分别添加到集合中 print(s1)
-
.remove()删除
s1.remove("Y") # s1.remove("bo") #删除不存在的元素会报错 print(s1)
-
.pop()随机删除
s1.pop() print(s1)
-
discard()删除元素
s1.discard("bo") # 删除不存在的元素也不会报错 s1.discard("key") print(s1)
-
clear()清空集合
s1.clear() print(s1) # 输出:set()
-
其他 *** 作
set1 = set() set2 = {2, 3, 4, 5, 6} set3 = {2, 3, 4, 5, 6} print(6 in set1) print(6 in set2) print(set2 == set3) # 值相同 print(set2 is set3) # 但是内存不相同 print(id(set2)) print(id(set3))
False True True False 2199033381832 2199033383400
-
差集
""" #差集,在前一个在前一个集合存在,在后一个集合不存在.difference() """ print(set1 - set2) # 输出set1比set2多那些元素 # 等价于.difference() print(set1.difference(set2)) print(set2 - set1) # 输出set2比set1多那些元素 print(set2.difference(set1))
-
交集
""" #交集 :取两个集合中相同的元素,组成一个新的集合intersection() """ set3 = set2 & set1 print("set2和set1的交集为:{}".format(set3)) print(set1.intersection(set2))
-
并集
""" 并集 | .union() """ set4 = set1 | set2 print("set1和set2的并集为:{}".format(set4)) print(set1.union(set2))
-
对称差集
""" 对称差集 用两个集合的并集减去他们的交集 ^ """ set5 = set1 ^ set2 print(set5)
方法 | 功能 |
---|---|
add() | 为集合添加元素 |
clear() | 移除集合中的所有元素 |
copy() | 拷贝一个集合 |
difference() | 返回多个集合的差集 |
difference_update() | 移除集合中的元素,该元素在指定的集合也存在。 |
discard() | 删除集合中指定的元素 |
intersection() | 返回集合的交集 |
intersection_update() | 返回集合的交集。 |
isdisjoint() | 判断两个集合是否包含相同的元素,如果没有返回 True,否则返回 False。 |
issubset() | 判断指定集合是否为该方法参数集合的子集。 |
issuperset() | 判断该方法的参数集合是否为指定集合的子集 |
pop() | 随机移除元素 |
remove() | 移除指定元素 |
symmetric_difference() | 返回两个集合中不重复的元素集合。 |
symmetric_difference_update() | 移除当前集合中在另外一个指定集合相同的元素,并将另外一个指定集合中不同的元素插入到当前集合中。 |
union() | 返回两个集合的并集 |
update() | 给集合添加元素 |
- 不可变类型
- 可变类型
"""
可变和不可变类型
字典的键 必须是数字、字符串、元组
数字、字符串、元组是不可变类型,当变量的ID不发生改变的时候,他的值是不能变的
列表,字典都是可变的, 当id不发生改变的时候,他的值是可以改变的
字典的键 必须使用不同类型的变量
集合(set)的元素:必须使用不同类型的变量
"""
# 数字类型
x = 1
y = x
print(y)
y = 2
print(x)
print("--------------------")
#列表类型
a = [1, 2, 3, 4, 5, 6]
b = a
print(b)
b.append(777)
print(b)
print(a)
六、常用方法
1、join()用法
- Python join() 方法用于将序列中的元素以指定的字符连接生成一个新的字符串
#!/usr/bin/python
# -*- coding: UTF-8 -*-
str = "-";
seq = ("a", "b", "c"); # 字符串序列
print str.join( seq );
函数
一、函数的定义
所谓函数,就是把 具有独立功能的代码块 组织为一个小模块,在需要的时候 调用
函数的调用包括两个步骤:
1.定义函数一一封装 独立的功能
2.调用函数一一享受 封装 的成果
函数的作用,在开发程序时,使用函数可以提高编写的效率以及代码的 重用
二、函数的调用-
本地调用
def multiple_table(): # 使用def(define的缩写)关键字来定义一个函数 """ 定义一个乘法表函数 :return: """ for i in range(1, 10): for j in range(1, i + 1): print(f"{j}*{i}={i * j}", end="\t") print(" ") # 在本地调用 multiple_table()
-
外部调用
# 导入yang_01_函数这个文件,使用这个文件中的功能 import Day05.yang_01_函数 Day05.yang_01_函数.multiple_table() # 在yang_01_文件中,调用他的函数 import Day05.yang_04_函数的参数 Day05.yang_04_函数的参数.sum_2_num(1,50)
def add2num(a, b):
c = a+b
print c
2、调用带有参数的函数
以调用上面的add2num(a, b)函数为例:
def add2num(a, b): # a,b 形参 ,形式上的参数,没有固定的值,用来接受实参的值
c = a+b
print c
add2num(11, 22) # 调用带有参数的函数时,需要在小括号中,传递数据
四、函数的嵌套使用
def test1():
print("*" * 50)
print("我是test1")
print("*" * 50)
def test2():
print("-" * 50)
print("我是test2")
test1() # 调用 test1函数
print("-" * 50)
test1()
test2()
五、局部变量与全局变量
1、介绍
num = 100 # 全局变量 顶格写 定义的变量就是全局变量
def func():
num1 = 1000 # 定义在函数内部的变量就是局部变量
print("使用局部变量 num1--->{}".format(num1))
print("使用全局变量 num--->{}".format(num))
# 暂时无法在全局去调用函数内部的变量
func()
def func1():
print("使用全局变量 num--->{}".format(num))
func1()
2、global关键字
num = 100
print("定义完num变量ID为:", id(num))
def func(): # 在func中,修改全局变量的应用,可以使用global关键字进行声明
global num # 使用global关键字,之后,在函数内部,就可以修改全局变量的引用
num = 888 # num被重新赋值只在函数内部生效
print("局部变量内:", num)
print("在函数运行过程中num的ID:", id(num))
func()
print("程序执行结束后:", num)
print("程序执行结束后 num 的ID:", id(num))
六、缺省参数
- 调用函数时,缺省参数的值如果没有传入,则取默认值。
# 如果一个函数,存在多个缺省参数的写法:
def print_info(name, genfer=True, title="班长"): # 在函数中,定义缺省参数,一定要放到所以参数的最后
"""
:param name: 姓名
:param title: 职务
:param genfer: 性别 True 默认为男,False 为女
"""
gender_text = "男生" # 定义新的变量
gender_1 = "他"
if not genfer:
gender_text = "女生"
gender_1 = "她"
print(f"{name}的性别是{gender_text},{gender_1}的职务是{title}")
print_info("小明")
print_info("小美", False, "副班长")
七、多值参数
- 有时可能需要一个函数能处理比当初声明时更多的参数, 这些参数叫做不定长参数,声明时不会命名。
"""
函数参数的数量不固定
参数名前增加 一个 * 可以接收 元组
参数名前增加 两个 * 可以接收 字典
"""
# 定义一个函数,这个函数处理接受的第一个数字,后面的内容存储为元组,字典
def func(num, num1, *args, **kwargs):
print(f"我接受到的第一个值为:{num}")
print(f"我接受到的第二个值为:{num1}")
print(f"后续接收的是:{args}") # 装包
print(f"后续接受的内容是:{kwargs}") # 拆包
func(1, 2, 3, 4, 5, 6, 7, 8, name="杨宇波", age=22, gender=True)
八、递归函数
- 函数调用自身的 编程技巧 称为 递归
- 特点:l一个函数 内部 调用自己
"""
1.定义一个函数 sum_numbers
2.能够接收一个 num 的整形参数
3.计算 1+2+…+n的结果
分析:我们传入的 num 是 n 实际计算的是 n!
"""
def sum_numbers(num):
# 出口是1
if num == 1:
return 1
result = sum_numbers(num - 1)
return num + result
sum1 = sum_numbers(10)
print(sum1)
九、内部函数
1、函数的嵌套
- 在一个函数当中,调用其他函数
- 在一个函数当中,调用自身
- 在一个函数当中,定义一个函数(内部函数)
- 可以访问外部函数的变量
- 内部函数可以修改外部函数的可变类型的变量 比如:list1
- 函数内部修改全局变量,要加global 变量名,内部函数要修改外部函数的不可变类型的变量的时候要加nonlocal在函数内部,要想修改全局变量的值,使用 global
- locals() 查看本地变量有哪些,以字典的形式输出
- globals() 查看全局变量有哪些,以字典的形式输出()
# 闭包基于函数内部的形式
# 要想单纯的在 全局 调用内部函数:
def outside():
num_1 = 100
def inside():
print("我是内部函数,调用了外部函数的变量", num_1)
return inside # 不带括号
# 我想把 print(num_1) 表现出来,怎么办? 也就是在全局下调用inside()
# print(outside())
var = outside() # var = inside var()=inside()
# inside函数的地址是.inside at 0x00000274B3931948>
var()
十一、装饰器
-
也叫 语法糖@ (糖衣语法)
-
在闭包的基础上做的升级,遵循如下规则:
- 函数A作为参数出现,函数B接收函数A作为参数
- 要有闭包的特点
- 不影响原有函数的功能,还能添加新的功能
"""
# 装饰器 也叫 语法糖@ (糖衣语法)
在闭包的基础上做的升级,遵循如下规则:
1.函数A作为参数出现,函数B接收函数A作为参数
2.要有闭包的特点
3.不影响原有函数的功能,还能添加新的功能
"""
# 符合闭包的特点
def func_b(arg_1):
print("我是函数B!,是一个外部函数!")
def inner_func():
print("我是函数B的内部函数!")
return arg_1()
return inner_func
@func_b # 用func_b装饰func_a,其实是将未装饰的函数作为参数传递给闭包的外部,现在再执行 func_b
def func_a():
print("我是函数A!~~")
func_a()
"""
等价于
def func_a():
print("我是函数A!~~")
var = func_b(func_a)
var()
"""
十二 、匿名参数
Python 使用 lambda 来创建匿名函数。
所谓匿名,意即不再使用 def 语句这样标准的形式定义一个函数。
- lambda 只是一个表达式,函数体比 def 简单很多。
- lambda 的主体是一个表达式,而不是一个代码块。仅仅能在 lambda 表达式中封装有限的逻辑进去。
- lambda 函数拥有自己的命名空间,且不能访问自己参数列表之外或全局命名空间里的参数。
- 虽然 lambda 函数看起来只能写一行,却不等同于 C 或 C++ 的内联函数,后者的目的是调用小函数时不占用栈内存从而增加运行效率。
"""
不再使用def来定义函数,而是使用lambda
格式 : lambda 参数1,参数2,……; 运算
"""
# 正常定义函数
def func_name():
pass
# 匿名函数--->不需要考虑函数的命名
def add(a, b):
result = a + b
return result
var1 = add(1, 2)
print(var1)
# 相当于定义了add()函数
var2 = lambda a, b: a + b
# print(var2) # 返回的是var2的地址
var3 = lambda a, b: print(a + b) # a和b是参数。print(a+b)是返回值 var3 来接受返回值
var3(1, 5) # 通过var3来传递参数
十三、推导式
Python 推导式是一种独特的数据处理方式,可以从一个数据序列构建另一个新的数据序列的结构体。
Python 支持各种数据结构的推导式:
- 列表(list)推导式
- 字典(dict)推导式
- 集合(set)推导式
- 元组(tuple)推导式
-
格式
-
""" 通过旧的列表利用推导式生成新的列表 格式: [ 表达式 for 变量 in 旧列表] 或者 [ 表达式 for 变量 in 旧列表 if 条件] """ names = ["James", "Curry", "Young", "Kuzma", "y", "b"] # 用name来生成一个新列表,它的条件是 先遍历naems列表,拿取字符串长度大于3的元素 new_names = [name for name in names if len(name) > 3] print(new_names) # name for name in names for循环遍历names列表 new_names = [] """ for name in names: if len(name) > 3: new_names.append(name) """
-
-
格式
-
""" 类似于列表推导式,在列表推导式的基础上增加了一个去重复的功能 """ lis1 = [1, 2, 3, 4, 62, 2, 1, 3] new_lis1 = {x for x in lis1} print(new_lis1) # 添加条件,最小于5的元素进行+1 *** 作 new_lis2 = {i + 1 for i in new_lis1 if i < 5} print(new_lis2)
-
-
格式
-
""" # 案例: 需求:对字典的key 和 value 进行交换 """ # 定义的一个字典 dict1 = {'a': 'A', 'b': 'B', 'c': 'C'} dict2 = {value: key for key, value in dict1.items()} print(dict2) # dict1.items() 字典解封装 print(dict1.items()) # 数据类型是: dict_items # dict_items([('a', 'A'), ('b', 'B'), ('c', 'C')]) for i in dict1: print(i)
-
# 将 1-20的数字 进行 *3的 *** 作
new_list = [i * 3 for i in range(5)]
print(new_list) # [0, 3, 6, 9, 12]
"""
当我们再 调用 new_list的时候,实际上 ,再内存中,会先创建该列表,
如果打印或者遍历列表, 再从这个内存中取 取值,进行相对于的 *** 作
如果一个列表 有 100万个元素,只需要 对前10个元素进行 *** 作, 是否也需要给 100万个元素的列表 开空间?
"""
# 需求 只取 new_list 的前 3 个值
var = new_list.__iter__() # 迭代器
print(var) #
print(var.__next__()) # 0
print(var.__next__()) # 3
print(var.__next__()) # 6
print(next(var)) # var.__next__() 等价于 next(var)
print(next(var))
"""
如果 使用 迭代器的次数 ,大于了 集合元素的个数, 系统报错: StopIteration
"""
面向对象
一、面和对象简介
- 类(Class): 用来描述具有相同的属性和方法的对象的集合。它定义了该集合中每个对象所共有的属性和方法。对象是类的实例。
- **对象:**通过类定义的数据结构实例。对象包括两个数据成员(类变量和实例变量)和方法。
- **方法:**类中定义的函数。
-
类是模板,对象 是根据 类 这个模板创建出来的,应该 先有类,再有对象
-
类 只有一个,而 对象 可以有很多个
-
不同的对象 之间 属性 可能会各不相同
-
类 中定义了什么 属性和方法,对象 中就有什么属性和方法,不可能多,也不可能少
class Leiming: # 类名 大驼峰
# 创建对象的时候,程序会自动调用 new 和 init
# def __init__(self): # 程序自动执行,暂时先不要修改
# pass # 给对象开辟空间
def __init__(self,shuxing): # 给对象 定义属性
self.shuxing = shuxing
def method(self): # 方法 和 函数很像,多了一个 self 参数
pass
# 根据类创建对象
obj1 = Leiming("属性1")
obj1.method() # 对象可以调用方法
print(obj1.shuxing) # 对象可以访问属性
class MyClass:
"""一个简单的类实例"""
i = 12345
def f(self):
return 'hello world'
# 实例化类
x = MyClass()
# 访问类的属性和方法
print("MyClass 类的属性 i 为:", x.i)
print("MyClass 类的方法 f 输出为:", x.f())
四、构造方法
1、概念
Python 中的 init()方法,是一个特殊的类实例方法,称为构造方法、构造函数或构造器,英文为 Constructor。
仅包含 self 参数的 init(self) 构造方法,又称为类的默认构造方法。
构造方法最大的用处就是在创建对象时执行初始化,当创建一个对象时,系统会为这个对象的实例进行默认的初始化。
在 Python 类中,如果不手动为类添加任何构造方法,Python 会自动为类添加一个仅包含 self 参数的构造方法。
2、实例class Complex:
def __init__(self, realpart, imagpart):
self.r = realpart
self.i = imagpart
x = Complex(3.0, -4.5)
print(x.r, x.i) # 输出结果:3.0 -4.5
五、继承
- 在程序中,继承描述的是多个类之间的所属关系。
- 如果一个类A里面的属性和方法可以复用,则可以通过继承的方式,传递到类B里。
- 那么类A就是基类,也叫做父类;类B就是派生类,也叫做子类。
# 父类
class A(object):
def __init__(self):
self.num = 10
def print_num(self):
print(self.num + 10)
# 子类
class B(A):
pass
b = B()
print(b.num)
b.print_num()
1、单继承
- 子类在继承的时候,在定义类时,小括号()中为父类的名字
- 父类的属性、方法,会被继承给子类
# 定义一个Master类
class Master(object):
def __init__(self):
# 属性
self.kongfu = "古法煎饼果子配方"
# 实例方法
def make_cake(self):
print("按照 <%s> 制作了一份煎饼果子..." % self.kongfu)
# 定义Prentice类,继承了 Master,则Prentice是子类,Master是父类。
class Prentice(Master):
# 子类可以继承父类所有的属性和方法,哪怕子类没有自己的属性和方法,也可以使用父类的属性和方法。
pass
# laoli = Master()
# print(laoli.kongfu)
# laoli.make_cake()
damao = Prentice() # 创建子类实例对象
print(damao.kongfu) # 子类对象可以直接使用父类的属性
damao.make_cake() # 子类对象可以直接使用父类的方法
- 虽然子类没有定义
__init__
方法初始化属性,也没有定义实例方法,但是父类有。所以只要创建子类的对象,就默认执行了那个继承过来的__init__
方法
- 多继承可以继承多个父类,也继承了所有父类的属性和方法
- 注意:如果多个父类中有同名的 属性和方法,则默认使用第一个父类的属性和方法(根据类的魔法属性mro的顺序来查找)
- 多个父类中,不重名的属性和方法,不会有任何影响。
# 父类1
class Base1:
def demo1(self):
print("我是Base1的demo1方法")
# 父类2
class Base2:
def demo2(self):
print("我是Base2的demo2方法")
# 子类继承父类
class Derived(Base1, Base2): # 重名的话,谁写在()前,先调用谁
def test(self):
self.demo1()
self.demo2()
obj = Derived()
obj.demo1()
obj.demo2()
print(Derived.__mro__) #查看Derived类的调用方法或者访问属性的顺序
"""
(, , , )
object 如果按照查找顺序路都找不到,最后回在object类中进行查找,如果最后object也没有该方法或者属性,就会报错
object是所以类的父类
"""
3、方法的重写
class Animal:
def __init__(self):
self.color = "grey"
def eat(self):
print("站起来吃")
def drink(self):
print("drink")
def run(self):
print("run")
def sleep(self):
print("sleep")
class Dog(Animal):
def eat(self): # 虽然父类有eat方法,但是有所不同,所以需要重写
"""
重写父类方法:
1、完全覆盖父类方法,只保留父类方法的名字
2、在父类方法的基础上,对该方法进行拓展,会保留父类方法的功能
"""
super().eat() # super().的方式,保留父类方法的功能,对父类方法进行拓展
print("狗狗用盆吃")
def bark(self):
print("汪汪汪~")
wangcai = Dog()
wangcai.eat()
wangcai.bark()
六、私有属性和私有方法
私有权限:在属性名和方法名 前面 加上两个下划线 __
七、类的属性
- 类的私有属性 和 私有方法,都不能通过对象直接访问,但是可以在本类内部访问;
- 类的私有属性 和 私有方法,都不会被子类继承,子类也无法访问;
- 私有属性 和 私有方法 往往用来处理类的内部事情,不通过对象处理,起到安全作用。
- 如果需要在类外修改
类属性
,必须通过类对象
去引用然后进行修改。如果通过实例对象去引用,会产生一个同名的实例属性
,这种方式修改的是实例属性
,不会影响到类属性
,并且之后如果通过实例对象去引用该名称的属性,实例属性会强制屏蔽掉类属性,即引用的是实例属性
,除非删除了该实例属性
。
"""
定义一个 工具类
每件工具都有自己的 name 实例属性
需求 -- 知道使用这个类,创建了多少个工具对象 类属性
"""
class Tools(object):
count = 0 # 类属性,用来描述 这个类 创建了多少个 实例
def __init__(self, name): # 初始化方法,是用来给对象(实例赋予属性值的)
self.name = name # 实例属性
# 通过这个类创建了 多少个 对象 ,做一个计数 count
# 思路: 每创建一个对象,就会调用一次 __init__(self) 方法 ,每调用一次 __init__ ,就对 count做一个 +1 的 *** 作
Tools.count += 1
tool1 = Tools("手电筒") # 创建实例
tool2 = Tools("扳手")
tool3 = Tools("菜刀")
print(Tools.count) # 输出 通过类创建的对象数
print(tool1.count) # 注意: count属性,是 Tools 这个类的,但是 可以被实例访问
print(id(tool1.count), id(Tools.count)) # 1759695208752 1759695208752
tool1.count = 100 # 该定义方式,只是给 tool1 增加了一个属性 count,增加了一个 tool1.count的内存空间
# 而不会改变 类属性的值
print(id(tool1.count), id(Tools.count)) # 1759695211856 1759695208752
print(Tools.count) # --->3
八、类的方法
- 是类对象所拥有的方法,需要用修饰器
@classmethod
来标识其为类方法,对于类方法,第一个参数必须是类对象,一般以cls
作为第一个参数(当然可以用其他名称的变量作为其第一个参数,但是大部分人都习惯以’cls’作为第一个参数的名字,就最好用’cls’了),能够通过实例对象和类对象去访问。
# 类属性 是用来描述 类对象 的属性
# 类方法 描述 类的 方法
"""
在 类方法 内部可以直接访问 类属性 或者调用其他的 类方法
"""
"""
需求:
定义一个 工具类
每件工具都有自己的 name
需求 – 在 类 封装一个 show_tool_count 的类方法,输出使用当前这个类,创建的对象个数
"""
class Tools(object):
count = 0
def __init__(self, name):
self.name = name
Tools.count += 1
# 创建类方法
@classmethod
def show_tool_count(cls):
print(f"目前创建了 {cls.count}个 工具实例了!") # 类方法内部,访问了类属性,也可以调用其他类方法
# print(self.name) # 类方法内部,不能访问 实例属性,也不能调用实例方法
tool1 = Tools("James")
tool2 = Tools("Curry")
Tools.show_tool_count()
九、静态方法
- 需要通过修饰器
@staticmethod
来进行修饰,静态方法不需要多定义参数,可以通过对象和类来访问。
class Dog:
def __init__(self):
self.name = "旺财"
@staticmethod
def run():
"""
定义了一个 静态方法 静态方法不需要访问实例属性 也不需要 调用实例方法
静态方法不需要访问类属性 也不需要 调用类方法
"""
print(f"James")
James = Dog() # 根据类创建实例
James.run()
十、单例设计模式
1、设计模式
- 设计模式 是 前人工作的总结和提炼,通常,被人们广泛流传的设计都是针对 某一特定问题 的成熟的方案
- 使用 设计模式 是为了可重用代码、让代码更容易被他人理解、保证代码可靠性
- 目的 — 让 类 创建的对象,在系统中 只有 唯一的一个实例
- 每一次执行 类名( ) 返回的对象,内存地址是相同的
class MusicPlayer(object):
instance = None # instance设置为初始值 None,如果 创建过对象,
# 也就是 调用过 new方法了, instance 的值就不是 None了
init_flag = False # 定义一个初始值,用于标记 __init__方法,没有被再次执行
def __new__(cls, *args, **kwargs): # 重写__new__方法
if cls.instance is None: # 如果 之前没有执行过 new方法,则 cls.instance is None
cls.instance = super().__new__(cls) # 将 内存id 赋值给 cls.instance
return cls.instance # 重写 new方法后,将 内存 id 返回
def __init__(self):
if MusicPlayer.init_flag == True:
return
print("播放器初始化")
MusicPlayer.init_flag = True
player1 = MusicPlayer()
player2 = MusicPlayer()
print(id(player1)) # 多个实例 2918561262816引用 是通过 __new__返回的
print(id(player2))
模块和包
- 模块就好比是工具包,要想使用这个工具包中的工具(就好比函数),就需要导入这个模块
在Python中用关键字import
来引入某个模块,比如要引用模块math,就可以在文件最开始的地方用import math来引入。
import module1,mudule2...
- 如果想一次性引入math中所有的东西,还可以通过from math import *来实现
as
import Day09.yang_测试模块_01 as Dog_module # 用于当前文件中,简写
import Day09.yang_测试模块_02 as Cat_module
# 调用模块1的工具
print(Dog_module)
Dog_module.say_hello()
dog = Dog_module.Dog()
dog.james()
cat = Cat_module.Cat()
print(cat)
二、from…import
Python的from语句让你从模块中导入一个指定的部分到当前命名空间中
语法如下:
from modname import name1[, name2[, ... nameN]]
# import Day09.yang_测试模块_01 as Dog_module # 用于当前文件中,简写
# import Day09.yang_测试模块_02 as Cat_module
from Day09.yang_测试模块_01 import Dog # 仅导入yang_测试模块_01中的Dog类
# 如果多个模块之间,存在同名的工具,后导入的,会覆盖掉先导入的。为了防止这种现象,可以使用as方法
from Day09.yang_测试模块_01 import say_hello as Dog_say_hello
from Day09.yang_测试模块_02 import say_hello
# 调用模块1的工具
dog = Dog() # 此时,不需要带模块名
print(dog)
Dog_say_hello()
say_hello()
三、from … import *
把一个模块的所有内容全都导入到当前的命名空间也是可行的,只需使用如下声明:
from modname import *
注意
- 这提供了一个简单的方法来导入一个模块中的所有项目。然而这种声明不该被过多地使用。
-
包 是一个 包含多个模块 的 特殊目录
-
目录下有一个 特殊的文件 init . py
-
包名的 命名方式 和变量名一致,小写字母 + _
使用import包名,可以一次性的导入包中的所有模块
3、总结- 包将有联系的模块组织在一起,即放到同一个文件夹下,并且在这个文件夹创建一个名字为
__init__.py
文件,那么这个文件夹就称之为包
- 有效避免模块名称冲突问题,让应用组织结构更加清晰
try:
num = int(input("请输入一个数字:"))
result = 8 / num
print(f"您输入的数字为{result}")
except ValueError: # 开发者预测到的错误类型1
print("您输入的不是数字!")
except ZeroDivisionError:
print("被除数不能为0")
except Exception as e: # Exception类,包含着系统中定义的各种错误
# e 可以认为是Exception类的一个实例(可以自己定义)
# python 2.x except Exception , e
print(f"发生了以下错误:{e}")
2、else、finall
咱们应该对else
并不陌生,在if中,它的作用是当条件不满足时执行的实行;同样在try…except…中也是如此,即如果没有捕获到异常,那么就执行else中的事情
try…finally…语句用来表达这样的情况:
在程序中,如果一个段代码必须要执行,即无论异常是否产生都要执行,那么此时就需要使用finally。 比如文件关闭,释放锁,把数据库连接返还给连接池等
try:
num = int(input("请输入一个数字:"))
result = 8 / num
print(f"结果为:{result}")
except ValueError: # 开发者预测到的错误类型1
print("您输入的不是数字!")
except ZeroDivisionError:
print("被除数不能为0")
except Exception as e: # Exception类,包含着系统中定义的各种错误
# e 可以认为是Exception类的一个实例(可以自己定义)
# python 2.x except Exception , e
print(f"发生了以下错误:{e}")
else:
print("不抛出异常会执行的代码!")
finally:
print("无论是否抛异常,都会执行的代码!")
文件 *** 作
一、open()方法
1、基础介绍
Python open() 方法用于打开一个文件,并返回文件对象,在对文件进行处理过程都需要使用到这个函数,如果该文件无法被打开,会抛出 OSError。
**注意:**使用 open() 方法一定要保证关闭文件对象,即调用 close() 方法。
open() 函数常用形式是接收两个参数:文件名(file)和模式(mode)。
open(file, mode='r')
完整的语法格式为:
open(file, mode='r', buffering=-1, encoding=None, errors=None, newline=None, closefd=True, opener=None)
2、参数说明
- file: 必需,文件路径(相对或者绝对路径)。
- mode: 可选,文件打开模式
- buffering: 设置缓冲
- encoding: 一般使用utf8
- errors: 报错级别
- newline: 区分换行符
- closefd: 传入的file参数类型
- opener: 设置自定义开启器,开启器的返回值必须是一个打开的文件描述符。
模式 | 描述 |
---|---|
t | 文本模式 (默认)。 |
x | 写模式,新建一个文件,如果该文件已存在则会报错。 |
b | 二进制模式。 |
+ | 打开一个文件进行更新(可读可写)。 |
U | 通用换行模式(Python 3 不支持)。 |
r | 以只读方式打开文件。文件的指针将会放在文件的开头。这是默认模式。 |
rb | 以二进制格式打开一个文件用于只读。文件指针将会放在文件的开头。这是默认模式。一般用于非文本文件如图片等。 |
r+ | 打开一个文件用于读写。文件指针将会放在文件的开头。 |
rb+ | 以二进制格式打开一个文件用于读写。文件指针将会放在文件的开头。一般用于非文本文件如图片等。 |
w | 打开一个文件只用于写入。如果该文件已存在则打开文件,并从开头开始编辑,即原有内容会被删除。如果该文件不存在,创建新文件。 |
wb | 以二进制格式打开一个文件只用于写入。如果该文件已存在则打开文件,并从开头开始编辑,即原有内容会被删除。如果该文件不存在,创建新文件。一般用于非文本文件如图片等。 |
w+ | 打开一个文件用于读写。如果该文件已存在则打开文件,并从开头开始编辑,即原有内容会被删除。如果该文件不存在,创建新文件。 |
wb+ | 以二进制格式打开一个文件用于读写。如果该文件已存在则打开文件,并从开头开始编辑,即原有内容会被删除。如果该文件不存在,创建新文件。一般用于非文本文件如图片等。 |
a | 打开一个文件用于追加。如果该文件已存在,文件指针将会放在文件的结尾。也就是说,新的内容将会被写入到已有内容之后。如果该文件不存在,创建新文件进行写入。 |
ab | 以二进制格式打开一个文件用于追加。如果该文件已存在,文件指针将会放在文件的结尾。也就是说,新的内容将会被写入到已有内容之后。如果该文件不存在,创建新文件进行写入。 |
a+ | 打开一个文件用于读写。如果该文件已存在,文件指针将会放在文件的结尾。文件打开时会是追加模式。如果该文件不存在,创建新文件用于读写。 |
ab+ | 以二进制格式打开一个文件用于追加。如果该文件已存在,文件指针将会放在文件的结尾。如果该文件不存在,创建新文件用于读写。 |
- 默认为文本模式,如果要以二进制模式打开,加上 b 。
"""
r 只读
w 只写 不能读
a 追加 不能读
# r+ 读写,写 *** 作,不覆盖源文本
# w+ 读写,最开始 *** 作的时候会将文件覆盖,然后才能进行读和写的 *** 作
如果在 w+ 的模式下,进行read() *** 作,则返回字符串的长度
# a+ 读写,指针会放到文件末尾,执行write() *** 作,会在文本末尾继续写,读取不到内容,
"""
recv = open("readme", "r", encoding='utf-8') # 返回一个对象,也叫一个句柄
'''
已只读的形式打开readme文件,文本编码格式是UTF-8
'''
# 对句柄进行 *** 作
print(recv.read())
text = recv.read()
print(text)
# 完成 *** 作之后,要关闭文件句柄,从内存中将句柄删除
recv.close() # 容易忘记执行关闭的 *** 作
5、复制文件
with open("readme", encoding='utf-8') as recv:
text=recv.read()
with open('readme_副本',"w",encoding='utf-8') as recv_01:
recv_01.write(text)
6、.readlines()方法
with open("readme", encoding='utf-8') as recv:
# .readlines()方法,读取全部内容,在读取的过程中,将每一个行作为一个元素,存储到列表中
content = recv.readlines()
with open("readme_副本02", "w", encoding='utf-8') as recv2:
for item in content:
recv2.write(item)
正则表达式
一、正则匹配的方法
- match 匹配 : 要从字符串开头去匹配,如果不匹配,返回None(只返回一个)
- search匹配 : 查找字符串,如果有符合条件的字符串,返回结果(只返回一个)
- findall匹配 : 匹配整个字符串,一直找到字符串结尾,返回结果,不返回字符串的位置(可以返回多个)
[] 表示一个字符位
\w : 数字和字母
\s : 空格 space
\b : 表示边界 boundary
\d : 数字 digital
+ 表示匹配一次或者多次,如果是多次的话,则全部匹配出来
? 匹配0次或1次
* 匹配0次或多次
{x} 用于验证将前面的模式匹配m次
{x,} 用于匹配x次或者多次,如果是多次的话全部匹配出来
{x,y} 匹配从x次到y次 >=m,<=n
$ 用于结尾
^ 用于匹配字符串的开头,match自动使用这种方法
三、正则的分组
import re
# 匹配数字 0 到 100
# 5 50 100
# 第一位不能是0 第二位0-9 只有100才会有第三位,如果取值为0也符合
num = "100"
result = re.match("[1-9]?[0-9]?$ |100$", num) # 只能匹配0-99
print(result)
# 验证邮箱
# @前面字符 5-20位
email = "1532889973@qq.com"
result1 = re.match("\w{5,20}@(qq|163|126)\.(com|net|cn)$", email)
print(result1)
# 电话号码 带区号,分别提取区号和电话号码----> 分组
# 021-20703917
phone = '021-20703917'
result2 = re.match("(\d{3,4})-(\d{7,8}$)", phone)
print(result2)
print(result2.group(1))
print(result2.group(2))
print(result2.groups()) # 每个组都提取出来,生成一个元组
# 提取标签里面的内容
html = 'ab@c'
# 表示group1
result3 = re.match(r'<([\w]+)>(.+)<(/)>', html)
print(result3)
print(result3.group(1))
# 多个标签
html1 = "hello"
result4 = re.match(r'<(\w+)><(\w+)>(.+)>>', html1)
print(result4.groups())
四、sub和split
# sub 替换, 将符合规则的字符串 替换成 指定字符串
import re
msg = "java:99,python:0,C:59"
result = re.sub(r"\d+", "90", msg)
print(result)
# split 切割 如果遇到符合规则的字符串,进行切割,将结果保存到列表
msg1 = "java:99,python:0,C:59"
result = re.split(r"[:,]", msg1)
print(result)
五、贪婪和非贪婪
# 贪婪和非贪婪
import re
msg = 'abc123124312abc'
result = re.match(r'abc\d??',msg) # "+" 用于将前面的模式匹配1次或多次(贪婪模式)
result1 = re.match(r'abc\d{4,8}',msg)
result2 = re.match(r'abc\d{4,8}?',msg)
#+个? 代表非贪婪模式
print(result)
print(result1)
print(result2)
Socket 模块
一、简介
-
Socket模块的主要目的是帮助在网络上的两个程序之间建立信息通道。
-
在Python中提供了两个基本的Socket模块:
- 服务端Socket
- 客户端Socket
-
当创建了一个 服务端Socket 之后,这个Socket就会在本机的一个端口上 等待连接
-
客户端Socket 会 访问 这个端口,当两者完成连接之后,就可以进行交互了。
-
在使用Socket进行编程的时候,需要:首先 实例化一个Socket类 ,
-
这个实例化需要三个参数:
- 第一个参数是地址族
- 第二个参数是流(TCP/UDP)
- 第三个参数是使用的协议
socket(family,type[,protocal])
三、family
-
要使用的地址族
-
常用的协议族有AF_INET、AF_INET6、AF_LOCAL(或称AF_UNIX、UNIX域Socket)、AF_ROUTE等。
默认值为socket.AF_INET,通常使用这个默认值即可。
-
- 用来指明Socket类型
- SOCK_STREAM,这是TCP类型,保证数据顺序及可靠性;(默认)
- TCP流控机制:平滑增长
- SOCK_DGRAM,用于UDP类型
- SOCK_RAW,这是原始类型,允许对底层协议如IP或ICMP进行直接访问,基本不会用到
- SOCK_STREAM,这是TCP类型,保证数据顺序及可靠性;(默认)
s = socket.socket()
# 等价于
socket.socket(socket.AF_INET,socket.SOCK_STREAM)
# 初始化UDP类型的Socket
s = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
六、服务端函数
1、bind()
-
这个函数由服务端Socket调用,会将之前创建Socket与指定的IP地址和端口进行绑定。如果之前使用了AF_INET初始化Socket,那么这里可以使用元组(host,port)的形式表示地址。
-
s.bind(('127.0.0.1',2345))
-
这个函数用于在使用TCP的服务端 开启 监听模式
-
可以使用一个参数来指定可以挂起的最大连接数量。
这个参数的值最小为1,一般设置为5。例如,要在服务端开启一个监听,可以使用如下语句。 s.listen(5)
- 这个函数用于在使用TCP的 服务端 接收连接,一般是阻塞态。
- 接受TCP连接并返回(conn,address)
- 其中,conn是新的套接字对象,可以用来接收和发送数据;address是连接客户端的地址。
# s = socket.socket()
import socket # 导入模块
s1 = socket.socket() # 实例化
s1.bind(("127.0.0.1", 2345)) # 绑定开启的服务器的IP地址和端口号。注意: bind()方法的参数是元组的形式
s1.listen(5) # 开启侦听可以连接端口号的最大数
"""
开启侦听之后,不断地等待用户连接,并向客户端发送数据
"""
while 1:
conn, address = s1.accept() # 等待客户端的连接
print("a new connect from", address) # 检测到有人连接打印"a new connect from"
"""
方法一:
conn.sendall(b"Hello Yang")
b 是将"Hello Yang"转换为字节
data = conn.recv(65535) #表示服务器能接受的最大数据是65535个字符
"""
conn.sendall(b"Hello Yang") # 给客户端发送"Hello Yang"
conn.close() # 数据发送完毕,关闭socket
七、客户端函数
1、connect()
-
这个函数用于在使用TCP的 客户端去连接服务端 时使用,使用的参数是一个元组,形式为(hostname,port)。
-
例如,在客户端程序初始化了一个Socket之后,就可以使用这个函数去连接到服务端。要连接本机的2345端口,可以使用如下语句。 s.connect(("127.0.0.1",2345))
用于在使用 TCP 时发送数据,完整的形式为 send(string[,flag]),
利用这个函数可以将string代表的数据发送到已经连接的Socket,返回值是发送字节的数量。
但是可能未将指定的内容全部发送。
2、sendall()与send()相类似,也是用于在使用TCP时发送数据,完整的形式为 sendall(string[,flag])
与send()的区别是完整发送TCP数据。将string中的数据发送到连接的套接字,但在返回之前会尝试发送所有数据。成功返回None,失败则抛出异常。
例如,使用这个函数发送一段字符到Socket,可以使用如下语句。
s.sendall(bytes("Hello,My Friend!",encoding="utf-8"))
3、recv()
这个函数用于在使用TCP时接收数据,完整的形式为 recv(bufsize[,flag])
接收Socket的数据。数据以字符串形式返回,bufsize指定最多可以接收的数量,flag这个参数一般不会使用。
例如,通过这个函数接收一段长度为1024的字符Socket,可以使用如下语句。
obj.recv(1024)
4、sendto()
这个函数用于在使用UDP时发送数据,完整的形式为
sendto(string[,flag],address),
返回值是发送的字节数。address是形式为(ipaddr,port)的元组,指定远程地址。
5、recvfrom()UDP专用,接收数据,返回数据远端的IP地址和端口,但返回值是(data,address)。其中,data是包含接收数据的字符串,address是发送数据的套接字地址。
6、close()关闭socket。
7、实验import socket
s2 = socket.socket()
s2.connect(("127.0.0.1", 2345))
data = s2.recv(1024)
s2.close()
"""
方法二:
data.decode('utf-8') ----> 解码
"""
"""
Return the canonical string representation of the object.
返回对象的规范字符串表示形式
"""
print("Received", repr(data.decode('utf-8')))
Nmap模块
一、功能
-
主机发现功能。向目标计算机发送信息,然后根据目标的反应来确定它是否处于开机并联网的状态。
- 如果开启防火墙可能导致ping失败,此时可借助端口扫描
-
端口扫描。向目标计算机的指定端口发送信息,然后根据目标端口的反应来判断它是否开放。
-
服务及版本检测。向目标计算机的目标端口发送特制的信息,然后根据目标的反应来检测它运行服务的服务类型和版本。
-
*** 作系统检测。
-
python-nmap是一个可以帮助使用 Nmap功能的 Python 模块文件。
-
在python-nmap模块的帮助下,可以轻松地在自己的程序中使用Nmap扫描的结果,也可以编写程序自动化地完成扫描任务。
-
模块的作者的个人网站为http://xael.org/。
sudo apt-get install nmap
sudo pip install python-nmap
3、基本用法
python-nmap模块的核心就是
- PortScanner(同步):发送一次请求,收到回复后才会发送下一次请求
- PortScannerAsync(异步):一次性发出请求,等待回复
- PortScannerError
- PortScannerHostDict
- PortScannerYield等
实例化
nmap.PortScanner()
5、PortScannerAsync
实例化
nmap.PortScannerAsync()
三、Python-nmap中的函数
1、Scan()
这个函数的完整形式为
scan(self, hosts='127.0.0.1', ports=None, arguments='-sV',sudo=False)
用来对指定目标进行扫描,其中需要设置的三个参数包括hosts、ports和arguments。
参数hosts的值为字符串类型,表示要扫描的主机,形式可以是IP地址,例如“192.168.1.1”,也可以是一个域名,例如“www.nmap.org”。
参数ports的值也是字符串类型,表示要扫描的端口。如果要扫描的是单一端口,形式可以为“80”。如果要扫描的是多个端口,可以用逗号分隔开,形式 为“80,443,8080”。如果要扫描的是连续的端口范围,可以用横线,形式为“1-1000”。
参数arguments的值也是字符串类型,这个参数实际上就是Nmap扫描时所使用的参数,
如“-sP”“-PR”“-sS”“-sT”“-O”“-sV”等。
“-sP”表示对目标进行Ping主机在线扫描,
“-PR”表示对目标进行一个ARP的主机在线扫描,
“-sS”表示对目标进行一个TCP半开(SYN)类型的端口扫描,
“-sT”表示对目标进行一个TCP全开类型的端口扫描,
“-O”表示扫描目标的 *** 作系统类型,
“-sV”表示扫描目标上所安装网络服务软件的版本。
-
半开连接:只发送两次握手
-
全开链接:发送三次握手
-
实例
# 如果要对192.168.1.101的1~500端口进行一次TCP半开扫描,可以使用如图所示的命令 nm.scan("127.0.0.1","1-10000","-sS") import nmap nm = nmap.PortScanner() nm.scan("127.0.0.1","1-10000","-sS") result = nm.all_hosts() print(result) #扫描一个网段的主机(同步,时间较久) nm.scan("192.168.43.0/24") >>> import nmap >>> nm = nmap.PortScanner() >>> nm.scan("192.168.1.0/24") # all_hosts()函数:返回一个被扫描的所有主机列表
- 返回在当前扫描中使用的命令行
>>> import nmap
>>> nm = nmap.PortScanner()
>>> nm.scan("192.168.1.4","1-500","-sS")
{'nmap': {'command_line': 'nmap -oX - -p 1-500 -sS 192.168.1.4', 'scaninfo': {'tcp': {'method': 'syn', 'services': '1-500'}}, 'scanstats': {'timestr': 'Sun Apr 24 20:33:34 2022', 'elapsed': '0.14', 'uphosts': '1', 'downhosts': '0', 'totalhosts': '1'}}, 'scan': {'192.168.1.4': {'hostnames': [{'name': '', 'type': ''}], 'addresses': {'ipv4': '192.168.1.4'}, 'vendor': {}, 'status': {'state': 'up', 'reason': 'localhost-response'}}}}
>>> nm.all_hosts() # 返回一个被扫描的所有主机列表
['192.168.1.4']
>>> nm.command_line()
'nmap -oX - -p 1-500 -sS 192.168.1.4' #返回在当前扫描中使用的命令行
5、csv()函数
- 返回值是一个CSV(逗号分隔值文件格式)的输出,如图所示。显示一个比较工整的扫描结果
>>> nm.csv()
'host;hostname;hostname_type;protocol;port;name;state;product;extrainfo;reason;version;conf;cpe\r\n'
>>> print(nm.csv())
host;hostname;hostname_type;protocol;port;name;state;product;extrainfo;reason;version;conf;cpe
6、has_host(self, host)函数
- 检查是否有host的扫描结果,如果有则返回True,否则返回False
- 列出一个扫描信息的结构
>>> nm.scaninfo()
{'tcp': {'method': 'syn', 'services': '1-500'}}
nm["192.168.1.4"]["tcp"][67] #查看67端口是否开启
四、异步类的函数
PortScannerAsync类中最为重要的函数也是scan(),
用法与PortScanner类中的scan()基本一样,但是多了一个回调函数。
完整的scan()函数格式为:
scan(self, hosts='127.0.0.1',ports=None, arguments='-sV', callback=None, sudo=False)
这里面的callback是以(host, scan_data)为参数的函数
1、scan()
>>> import nmap
>>> nma = nmap.PortScannerAsync()
>>> nma.scan(hosts="192.168.1.0/24",arguments="-sP")
2、still_scanning()
- 如果扫描正在进行,则返回True,否则返回False
>>> nma.still_scanning()
True
3、stop()
- 停止当前扫描
>>> nma.stop()
>>> nma.still_scanning()
False
4、wait(self, timeout=None)
- 函数表示等待时间
#不管有没有扫到,2s扫描一个地址
>>> nma.wait(2)
五、nmap来编写扫描器
1、实验需求
- 编写一个简单的端口扫描器。扫描192.168.1.121 开放从1~1000的哪些端口,这里先使用命令行来完成这个程序。
import nmap
nm = nmap.PortScanner()
nm.scan('192.168.184.0/24', '1-1000')
# 获取该网段的主机IP地址 IP地址的列表
ip_list = nm.all_hosts() # 返回一个被扫描的所有主机列表
print(nm)
for host in ip_list:
print('-' * 88)
print(f"Host:{host}({nm[host].hostname()})") # 打印IP地址和host-name
print(f"Start:{nm[host].state()}") # 获取主机的状态A
# 从获取到的一台主机当中,获取主机的端口号
print('-' * 88)
for protocol in nm[host].all_protocols():
print(f'Protocol:{protocol}')
l_port = nm[host][protocol].keys()
for port in l_port: #遍历主机的端口列表
print(f"port:{port}\t"
f"start:{nm[host][protocol][port]['starte']}")
Scapy模块文件
一、简介
- Scapy是一个由Python语言编写的强大工具
- 也可以在自己的程序中使用这个模块来实现对网络数据包的发送、监听和解析
- 这个模块相比起Nmap来说,更为底层。可以更为直观地了解到网络中的各种扫描和攻击行为
- l如果想对网络中的各种问题进行深入研究,Scapy无疑是一个更好的选择,可以利用它来产生各种类型的数据包并发送出去,Scapy也只会把收到的数据包展示给你,而并不会告诉你这意味着什么,一切都将由你来判断。
- 首先使用几个实例来演示一下Scapy的用法,在Scapy中每一个协议就是一个类。只需要实例化一个协议类,就可以创建一个该协议的数据包
#如果要创建一个IP类型的数据包,就可以使用如下命令。
ip = IP()
- IP数据包最重要的属性就是源地址和目的地址,这两个属性可以使用src和dst来设置
#例如,要构造一个发往“192.168.1.107”的数据包,可以这么写。
ip = IP(dst="192.168.1.107")
>>> ip = IP(dst='192.168.1.100')
>>> ip
<IP dst=192.168.1.100 |>
- 这个目标dst值可以是一个IP地址,也可以是一个网段,例如192.168.1.0/24,这时产生的就不是一个数据包,而是256个数据包
>>> ip = IP(dst='192.168.1.0/24')
>>> ip
<IP dst=Net('192.168.1.0/24') |>
- 查看其中的每个数据包
[p for p in ip] # 列表推导式
>>> [p for p in ip]
[<IP dst=192.168.1.0 |>,
<IP dst=192.168.1.1 |>,
<IP dst=192.168.1.2 |>,
<IP dst=192.168.1.3 |>,
<IP dst=192.168.1.4 |>,
<IP dst=192.168.1.5 |>,
<IP dst=192.168.1.6 |>,
<IP dst=192.168.1.7 |>,
<IP dst=192.168.1.8 |>,
<IP dst=192.168.1.9 |>,
……………………………………………………………………
<IP dst=192.168.1.253 |>,
<IP dst=192.168.1.254 |>,
<IP dst=192.168.1.255 |>]
三、构造数据包
1、Scapy采用分层的形式来构造数据包
-
通常最下面的一个协议为Ether,然后是IP,再之后是TCP或者是UDP。
-
IP()函数无法用来构造ARP请求和应答数据包,可以使用Ether(),
-
这个函数可以设置发送方和接收方的MAC地址。
-
产生一个广播数据包
>>>Ether(dst="ff:ff:ff:ff:ff:ff")
-
Scapy中的分层通过符号“/”实现,一个数据包是由多层协议组合而成,这些协议之间就可以使用“/”分开,按照协议由底而上的顺序从左向右排列
-
TCP数据包
>>>Ether()/IP()/TCP()
-
HTTP数据包
>>>IP()/TCP()/"GET / HTTP/1.0\r\n\r\n" #没有表明二层的时候计算机会自动识别
-
将ttl的值设置为32
>>>IP(src="192.168.1.1",dst="192.168.1.101",ttl=32)
- 用ls()****函数来查看一个类所拥有的属性
- 使用**ls(Ether())**来查看Ether类的属性
- 使用**ls(IP())**来查看IP类的属性
>>> tcp_packet = IP(dst='192.168.184.10')/fuzz(TCP())
>>> tcp_packet
<IP frag=0 proto=tcp dst=192.168.184.10 |<TCP |>>
>>> ls(tcp_packet)
version : BitField (4 bits) = 4 (4) #IP部分
ihl : BitField (4 bits) = None (None)
tos : XByteField = 0 (0)
len : ShortField = None (None)
id : ShortField = 1 (1)
flags : FlagsField (3 bits) = <Flag 0 ()> (<Flag 0 ()>)
frag : BitField (13 bits) = 0 (0)
ttl : ByteField = 64 (64)
proto : ByteEnumField = 6 (0)
chksum : XShortField = None (None)
src : SourceIPField = '192.168.184.128' (None)
dst : DestIPField = '192.168.184.10' (None)
options : PacketListField = [] ([])
--
sport : ShortEnumField = <RandShort> (20) #TCP部分
dport : ShortEnumField = <RandShort> (80)
seq : IntField = <RandInt> (0)
ack : IntField = <RandInt> (0)
dataofs : BitField (4 bits) = None (None)
reserved : BitField (3 bits) = <RandNum> (0)
flags : FlagsField (9 bits) = <RandNum> (<Flag 2 (S)>)
window : ShortField = <RandShort> (8192)
chksum : XShortField = None (None)
urgptr : ShortField = <RandShort> (0)
options : TCPOptionsField = <RandTCPOptions> (b'')
四、Scapy中的函数
1、send()和sendp()
- 这两个函数的区别在于send()工作在第三层,而sendp()工作在第二层。
- **send()是用来发送IP数据包的,而sendp()**是用来发送Ether数据包的。
- 只发不收,只会将数据包发送出去,但是没有能力处理该数据包的回应包!!
# 例如,构造一个目的地址为“192.168.1.101”的ICMP数据包,并将其发送出去,可以使用如下语句。
send(IP(dst="192.168.184.10")/ICMP())
.
Sent 1 packets. #不会有数据的回显
#发送广播报文
>>>sendp(Ether(dst="ff:ff:ff:ff:ff:ff"))
2、fuzz()
- 如果希望发送一个内容是随机填充的数据包,而且又要保证这个数据包的正确性,那么可以是fuzz()函数。
# 可以使用如下命令来创建一个发往192.168.1.101的TCP数据包。
>>> IP(dst="192.168.1.101")/fuzz(TCP())
<IP frag=0 proto=tcp dst=192.168.1.101 |<TCP |>>
3、sr()、sr1()、srp()
-
在网络的各种应用中,需要做的不仅是将创建好的数据包发送出去,也需要接收这些数据包的应答数据包,这一点在网络扫描中尤为重要。
-
在Scapy中提供了三个用来 发送和接收数据包的函数,分别是sr()、sr1()和srp(),其中,sr()和sr1()主要用于第三层,例如IP和ARP等。而srp()用于第二层。
# 仍然向192.168.1.101发送一个ICMP数据包来比较一下sr()和send()的区别。
>>>sr(IP(dst="192.168.1.101")/ICMP()) # 会有数据回显
-
sr()
- sr()函数是Scapy的核心,他的返回值是两个列表
- 第一个列表是收到了应答的包和对应的应答
- 第二个列表是未收到应答的包
- 所以可以使用连个列表来保存sr()的返回值
.summary()
查看包中的具体内容
>>> icmp_packet = IP(dst='192.168.184.10')/ICMP() >>> icmp_packet <IP frag=0 proto=icmp dst=192.168.184.10 |<ICMP |>> >>> sr(icmp_packet) Begin emission: Finished sending 1 packets. .* Received 2 packets, got 1 answers, remaining 0 packets (<Results: TCP:0 UDP:0 ICMP:1 Other:0>, <Unanswered: TCP:0 UDP:0 ICMP:0 Other:0>) """ 当产生的数据包发送出去后,Scapy就会监听收到的数据包,并将其中对应的应答数据包筛选出来,显示在下面 Reveived 表示收到的数据包的个数 answers 表示对应的应答数据包 """ >>> ans,unans = sr(icmp_packet) Begin emission: Finished sending 1 packets. ..* Received 3 packets, got 1 answers, remaining 0 packets >>> ans.summary() #查看包中的具体内容 IP / ICMP 192.168.184.128 > 192.168.184.10 echo-request 0 ==> IP / ICMP 192.168.184.1 > 192.168.184.128 redirect network-redirect / IPerror / ICMPerror >>>
- sr()函数是Scapy的核心,他的返回值是两个列表
-
sr1()
- 和sr()的作用基本一样,但是只返回一个应答包
- 只需要一个列表就可以保存这个函数的返回值
>>> p = sr1(IP(dst = "192.168.184.10")/ICMP()) Begin emission: Finished sending 1 packets. .* Received 2 packets, got 1 answers, remaining 0 packets >>> p.summary() 'IP / ICMP 192.168.184.1 > 192.168.184.128 redirect network-redirect / IPerror / ICMPerror'
- 利用sr1()函数来测试目标的某个端口是否开放,采用半开扫描(SYN)的方法
>>> sr1(IP(dst="192.168.184.10")/TCP(dport=80,flags="S"))
-
这个函数可以在自己的程序中捕获经过本机网卡的数据包
>>> sniff() ^C<Sniffed: TCP:0 UDP:88 ICMP:25 Other:9>
-
filter参数
-
只捕获与192.168.1.102有关的数据包
>>> sniff(filter="host 192.168.1.102")
-
指定过滤ICMP类型的数据包
>>> sniff(filter="icmp")
-
同时满足多个条件可以使用
and
和or
等关系运算符>>> sniff(filter="host 192.168.1.102 and icmp")
-
-
iface参数
-
指定eth1作为监听网卡
>>> sniff(iface="eth1")
-
-
count参数
-
用来指定监听到的数据包的数量,达到指定的数量就会停止监听
-
只希望监听到三个数据包
>>> sniff(count=3)
-
-
综合案例
-
设计一个综合性的监听器,它会在网卡eth0上监听源地址或者目的地址为192.168.1.102的icmp数据包,当收到了三个这样的数据包之后,就会停止监听。
>>> sniff(filter="icmp and host 192.168.1.102",count=3,iface="eth0") """ 如果需要查看这三个数据包内容,可以使用“_”,在Scapy中这个符号表示是上一条语句执行的结果, 例如刚刚使用sniff捕获到的数据包,就可以用“_”表示。 >>> a =_ >>> a.nsummary() """
-
使用Scapy来实现一次ACK类型的端口扫描
-
**ACK****扫描:**扫描主机向目标主机发送ACK数据包。根据返回的RST数据包有两种方法可以得到端口的信息。
-
正常的时候,如果一个开放的端口会回应ACK数据包,而关闭的端口会回应RST数据包。
-
在网络中,一些网络安全设备会过滤掉一部分端口,这些端口不会响应来自外界的数据包,一切发往这些端口的数据包都如同石沉大海。注意这些端口的状态并非是开放或者关闭,而是被屏蔽。
#例如,对192.168.1.102 的 21、23、135、443、445 这5个端口是否被屏蔽进行扫描,注意是屏蔽不是关闭,采用ACK扫描模式,可以构造如下的命令方式。
>>> ans,unans = sr(IP(dst="192.168.1.102")/TCP(dport=[21,80,135,443,445],flags="A"))
"""
向目标发送了5个标志位置为“A”的TCP数据包。按照TCP三次握手的规则,如果目标端口没有被过滤,发出的数据包就会得到回应,否则没有回应。另外,根据Scapy的设计,ans列表中的数据包就是得到了回应的数据包,而unans中的则是没有得到回应的数据包,只需要分两次来读取这两个列表就可以得到端口的过滤结果
"""
>>>for s,r in ans:
... if s[TCP].dport == r[TCP].sport: # 如果发出去的报文和收到的报文端口号相同,则打印出来
... print (str(s[TCP].dport) + " is unfiltered“)
# 利用类似的方法来查看被过滤的端口
>>>for s in unans:
... print str(s[TCP].dport) + " is filtered"
"""
能够实现查看端口是否被屏蔽
"""
from scapy.all import IP,TCP,sr
ans,unans = sr(IP(dst="192.168.184.10")/TCP(dport=[21,23,80,135,443,445],flags="A"))
for s,r in ans: # s:send r:receive
if s[TCP].dport == r[TCP].sport:
print(f"{s[TCP].dport} 没有被过滤")
└─# python3 yang_02_端口扫描.py
Begin emission:
Finished sending 6 packets.
.******
Received 7 packets,got 6 answers,remaining 0 packets
21没有被过滤
23没有被过滤
80没有被过滤
135没有被过滤
443没有被过滤
445没有被过滤
情报收集
一、简介
这里的**“情报”**指的是目标网络、服务器、应用程序的所有信息。
渗透测试人员需要使用资源尽可能地获取要测试目标的相关信息。
二、分类- 信息收集获得信息的方法可以分成两种:被动扫描和主动扫描
-
被动扫描主要指的是在目标 无法察觉的情况下 进行的信息收集,例如,如果想了解一个远在天边的人,你会怎么做呢?显然可以选择在搜索引擎中去搜索这个名字。其实这就是一次对目标的被动扫描
-
主动扫描一般都是针对目标发送特制的数据包,然后根据目标的反应来获得一些信息。
扫描之后将会获得的信息包括:目标网络的结构,目标网络所使用设备的类型,目标主机上运行的 *** 作系统,目标主机上所开放的端口,目标主机上所提供的服务,目标主机上所运行的应用程序。
-
nmap + 参数
选择扫描目标的nmap语法如下所示。
(1)扫描指定IP主机:nmap 192.168.169.133
(2)扫描指定域名主机:nmap www.nmap.com。
(3)扫描指定范围主机:nmap 192.168.169.1-20。
(4)扫描一个子网主机:nmap 192.168.169.0/24。
2、对目标的端口进行扫描
扫描一个主机的特定端口:nmap -p 22 192.168. 169.1
扫描指定范围端口:nmap -p 1-80 192.168. 169.1
扫描100个最为常用的端口:nmap -F 192.168. 169.1
3、对目标端口状态进行扫描
使用TCP全开扫描:nmap -sT 192.168. 169.1 #正常三次握手
使用TCP半开扫描:nmap -sS 192.168. 169.1 # 只发送SYN,收到SA的时候回复RST
使用UDP扫描:nmap -sU -p 123,161,162 192.168. 169.1
4、对目标的 *** 作系统和允许服务进行扫描
扫描目标主机上运行的 *** 作系统:nmap -O 192.168.169.1
扫描目标主机上运行的服务类型:nmap -sV 192.168.169.1
身份认证
一、使用Python编写一个生成字典的程序
- 在这个程序中需要使用到一个新的模块:itertools,这是一个强大的内置模块。
>>>import itertools
第二步:指定生成字典的字符,这里使用所有的英文字符和数字(但是没有考虑大小写和特殊字符)
>>>words = "1234568790abcdefghijklmnopqrstuvwxyz"
**第三步:**使用itertools中提供的循环器来生成字典文件
>>>temp = itertools.permutations(words,2)
**第四步:**打开一个用于保存结果的记事本文件
>>>passwords = open("dic.txt","a")
**第五步:**使用一个循环将生成的密码写入到一个记事本文件中。
>>>for i in temp:
passwords.write("".join(i))
passwords.write("".join("\n"))
3、实例
- 普通情况
"""
创建一个字典文件
"""
# 导入itertools库
import itertools
# 根据字母池创建密码
words = "qwertyuiopasdfghjklzxcvbnm1234567890"
temp = itertools.permutations(words,4)
# 创建一个密码本字典
with open("dist.txt", 'a') as password_dict:
for i in temp:
password_dict.write("".join(i))
password_dict.write("".join('\n'))
- 熟知特定字符
import itertools
import sys
if len(sys.argv) != 3:
print("请按照如下格式输入:\n Python xxx.py 参数1 参数2")
sys.exit()
words = sys.argv[1]
lens = int(sys.argv[2])
temp = itertools.permutations(words, lens)
with open("Dist.txt", 'a',encoding='utf-8') as passwd_Dict:
for i in temp:
passwd_Dict.write("".join(i))
passwd_Dict.write("".join('\n'))
网络嗅探与欺骗
一、网络数据嗅探
1、sniff()函数
①、重要参数
- **count:**表示要捕获数据包的数量。默认值为0,表示不限制数量
- store:表示是否要保存捕获到的数据包,默认值为1
- prn:这个参数是一个函数,这个函数将会应用在每一个捕获到的数据包上,如果这个函数有返回值,将会显示出来。默认是空; 返回一个数据包信息
- iface:表示要使用的网卡或者网卡列表。
- Type用来规定使用名字或数字代表的类型,例如host、net和port等
- Dir用来规定流量的方向,例如src、dst和src and dst等
- Proto用来规定匹配的协议,例如ip、tcp和arp等
(1)只捕获与网络中某一个IP的主机进行交互的流量:“host 192.168.1.1”。
(2)只捕获与网络中某一个MAC地址的主机交互的流量:“ether host 00-1a-a0-52-e2-a0”。
(3)只捕获来自网络中某一个IP的主机的流量:“src host 192.168.1.1”。
(4)只捕获去往网络中某一个IP的主机的流量:“dst host 192.168.1.1”,host也可以省略。
(5)只捕获23端口的流量:“port 23”。
(6)捕获除了23端口以外的流量:“!23” 。
(7)只捕获目的端口为80的流量:“dst port 80”。
(8)只捕获ICMP流量:“icmp”。
(9)只捕获type为3,code为0的ICMP流量:“icmp[0] = 3 &&icmp[1] = 0”。 ([type,code])
⑥、实例>>> sniff(filter="dst 39.156.66.14 and tcp port 80",prn=lambda x:x[IP].src,
192.168.184.128
192.168.184.128
192.168.184.128
192.168.184.128
192.168.184.128
192.168.184.128
192.168.184.128
192.168.184.128
192.168.184.128
192.168.184.128
<Sniffed: TCP:10 UDP:0 ICMP:0 Other:0>
2、wrpcap()函数
-
通过sinff函数收集到的数据包,可以使用wrpcap()函数保存起来,通常使用的格式为pcap。
-
举例
>>> packet = sniff(count=5) >>>wrpcap("demo.pcap", packet) # 此时将会生成一个demo.pcap的文件
# 导入包
from scapy.all import *
import sys
# if 判断
if len(sys.argv) != 2:
print("Usage: catchPackets:\n eg: catchPackets 192.168.0.1 " )
sys.exit()
# 定义ip地址
ip = sys.argv[1]
# 定义callback 回调函数
def callback(packet):
print(packet.show())
packets = sniff(filter="host " + ip, prn=callback, count=5)
wrpcap("yang.pcap", packets)
二、ARP的原理与缺陷 1、概念此时开启一个终端
ping www.baidu.com
,另一个终端执行脚本,就会抓取到相应的流量,在桌面生成对应的数据包
ARP协议是“Address Resolution Protocol”(地址解析协议)的缩写。其作用是在以太网环境中,数据的传输所依懒的是MAC地址而非IP地址,而将已知IP地址转换为MAC地址的工作是由ARP协议来完成的。
2、缺陷- 过程中并没有任何的 认证机制
arpspoof [-i指定使用的网卡] [-t 要欺骗的目标主机] [-r] 要伪装成的主机
4、开启流量转发root@kali:~# echo 1 >> /proc/sys/net/ipv4/ip_forward
三、代理ARP 1、概念- 路由器收到ARP Request时,若发现查询的目的IP地址在不同子网,路由器会扮演代理的ARP的角色,代为回答,告诉查询者它所要做的MAC地址
- 代理ARP可以解决配置静态路由没有网关和不配置下一跳的问题
- 点到点模式不需要配置下一跳,自动发送广播报文
arp-proxy enable
四、无故/免费ARP
- 当一个VRRP 备份组完成选举之后, MASTER 接口会发出一个 无故ARP,用于通告 MASTER设备的MAC地址,用于流量的引导。
- 对于交换机来讲,交换机会将 虚拟mac地址对应的接口写入MAC地址表,用于流量的引导
- 对于普通用户来讲,成为Master的设备,通过发送无故ARP,告知用户,网关的MAC地址是多少。
目的:使客户机可以通过路由器所提供的DHCP服务获取到IP地址
DHCP:动态主机配置协议
主要为客户机提供TCP/IP参数:IP地址、子网掩码、网关、DNS服务器地址
2、配置[AR1]ip pool pool1 # 创建一个地址池
[AR1-ip-pool-pool1]network 192.168.1.0 mask 255.255.255.0 #设置范围
[AR1-ip-pool-pool1]gateway-list 192.168.1.254 # 配置网关
# 配置DNS服务器
[AR1-ip-pool-pool1]dns-list 8.8.8.8
[AR1-ip-pool-pool1]dns-list 114.114.114.114
[AR1]dhcp enable # 开启DHCP服务
[AR1-GigabitEthernet0/0/0]dhcp select global #调用一个全局模式下建立的地址池 (ip pool) 网关
[AR1-GigabitEthernet0/0/0]ip addr 192.168.1.254 24 # 配置IP地址
[AR1-ip-pool-pool1]excluded-ip-address 192.168.1.251 # 配置IP地址的时候自动排除192.168.1.251
[AR1]dis ip pool name pool1 all # 查看pool1地址池的详细信息
配置完成后,PC端即可自动获取IP地址
因为有些服务器需要固定的IP地址,所以每台设备获取DHCP的时候,会发送免费ARP,确定有没有人使用此IP,避免冲突情况
SSH服务
一、基础SSH配置
交换机、防火墙设备SSH服务的开启与测试
1、拓扑图 2、基础配置 ①、SW1# 配置管理Vlan并允许通过
[SW1]vlan batch 100
[SW1-GigabitEthernet0/0/1]port link-type trunk
[SW1-GigabitEthernet0/0/1]port trunk allow-pass vlan 100
[SW1-GigabitEthernet0/0/2]port link-type trunk
[SW1-GigabitEthernet0/0/2]port trunk allow-pass vlan 100
[SW1-GigabitEthernet0/0/24]port link-type trunk
[SW1-GigabitEthernet0/0/24]port trunk allow-pass vlan 100
[SW1-GigabitEthernet0/0/3]port link-type access
[SW1-GigabitEthernet0/0/3]port default vlan 100
[SW1]stp enable # 开启STP
[SW1]stp root primary # 设置SW1为根桥
# 配置IP地址
[SW1]int Vlanif 100
[SW1-Vlanif100]ip add 192.168.100.1 24
②、SW2
[SW2]vlan batch 100
[SW2-GigabitEthernet0/0/1]port link-type trunk
[SW2-GigabitEthernet0/0/1]port trunk allow-pass vlan 100
[SW2-GigabitEthernet0/0/2]port link-type trunk
[SW2-GigabitEthernet0/0/2]port trunk allow-pass vlan 100
[SW2-GigabitEthernet0/0/24]port link-type trunk
[SW2-GigabitEthernet0/0/24]port trunk allow-pass vlan 100
# 配置IP地址
[SW2-GigabitEthernet0/0/2]int vlan 100
[SW2-Vlanif100]ip addr 192.168.100.2 24
③、SW3
[SW3]vlan batch 100
[SW3-GigabitEthernet0/0/1]port link-type trunk
[SW3-GigabitEthernet0/0/1]port trunk allow-pass vlan 100
[SW3-GigabitEthernet0/0/2]port link-type trunk
[SW3-GigabitEthernet0/0/2]port trunk allow-pass vlan 100
# 配置IP地址
[SW3]int vlan 100
[SW3-Vlanif100]ip addr 192.168.100.3 24
4、SW4
[SW4]vlan batch 100
[SW4-GigabitEthernet0/0/1]port link-type trunk
[SW4-GigabitEthernet0/0/1]port trunk allow-pass vlan 100
[SW4-GigabitEthernet0/0/2]port link-type trunk
[SW4-GigabitEthernet0/0/2]port trunk allow-pass vlan 100
# 配置IP地址
[SW4]int vlan 100
[SW4-Vlanif100]ip addr 192.168.100.4 24
5、配置SSH
[SW1]dsa local-key-pair create # 创建本地密钥对
Please input the modulus [default=512]:2048
# 配置用户名和密码
[SW1]stelnet server enable # 开启SSH登录
[SW1]ssh user yang authentication-type password # 用户名为 yang,认证方式为密码认证
[SW1]ssh user yang service-type stelnet # 设置登录类型为ssh
#开放 Vty端口,能被远程登录
[SW1]user-interface vty 0 4 # 开启0-4的vty端口,允许5台设备登录
# 认证模式为aaa认证
"""
authentication 认证:谁能登录进来
authorizathon 授权:谁进来能干什么
according 审计:谁进来干了什么
"""
[SW1-ui-vty0-4]authentication-mode aaa
[SW1-ui-vty0-4]protocol inbound ssh #登录进来的协议是ssh
#配置aaa
[SW1-aaa]local-user yang password cipher 123123 # 设置密码
[SW1-aaa]local-user yang privilege level 15 # 设置权限等级
[SW1-aaa]local-user yang service-type ssh
6、测试连通性
C:\Users\Yang>ping 192.168.100.1
正在 Ping 192.168.100.1 具有 32 字节的数据:
来自 192.168.100.1 的回复: 字节=32 时间=35ms TTL=255
来自 192.168.100.1 的回复: 字节=32 时间=14ms TTL=255
来自 192.168.100.1 的回复: 字节=32 时间=11ms TTL=255
192.168.100.1 的 Ping 统计信息:
数据包: 已发送 = 3,已接收 = 3,丢失 = 0 (0% 丢失),
往返行程的估计时间(以毫秒为单位):
最短 = 11ms,最长 = 35ms,平均 = 20ms
7、在SW2做相同配置
[SW2]dsa local-key-pair create # 创建本地密钥对
Please input the modulus [default=512]:2048
# 配置用户名和密码
[SW2]stelnet server enable # 开启SSH登录
[SW2]ssh user yang authentication-type password # 用户名为 yang,认证方式为密码认证
[SW2]ssh user yang service-type stelnet # 设置登录类型为ssh
#开放 Vty端口,能被远程登录
[SW2]user-interface vty 0 4 # 开启0-4的vty端口,允许5台设备登录
# 认证模式为aaa认证
"""
authentication 认证:谁能登录进来
authorizathon 授权:谁进来能干什么
according 审计:谁进来干了什么
"""
[SW2-ui-vty0-4]authentication-mode aaa
[SW2-ui-vty0-4]protocol inbound ssh #登录进来的协议是ssh
#配置aaa
[SW2-aaa]local-user yang password cipher 123123 # 设置密码
[SW2-aaa]local-user yang privilege level 15 # 设置权限等级
[SW2-aaa]local-user yang service-type ssh
8、防火墙
防火墙
# 对G0/0/0管理接口 预配置
先修改 管理IP地址
开启 G0/0/0 的SSH 服务 (同SW1)
注意:配置密码的时候password cipher 123123
interface G0/0/0
ip addr 192.168.100.102
service-manage ssh permit #开启SSH 服务
service-manage ping permit # 开启此命令,其他设备才能ping通
service-manage https permit (在浏览器输入 https://IP 地址:8443)# 冒号是英文的
# SSH登录配置
ssh user yang
ssh user yang authentication-type password
ssh user yang service-type stelnet
stelnet server enable
# vty 端口的配置
user-interface vty 0 4
authentication-mode aaa
protocol inbound ssh
# 用户信息配置
aaa
manager-user yang
password cipher 123123
service-type ssh
level 15
二、Python通过SSH链接
import paramiko
import time
# 创建客户端对象
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
# 使用客户端导入参数 hostname="192.168.100.1", username="yang", password="123123"
ssh.connect(hostname="192.168.100.1", username="yang", password="123123")
# 发送命令,创建命令对象,调用shell
command = ssh.invoke_shell()
# 给设备发送配置命令,编码格式为UTF-8(将发送的命令,字符串形式转换为UTF-8格式的二进制,传递给网络设备)
command.send("sys \n".encode())
command.send("sys Hello \n".encode())
time.sleep(1) # 1s之后回显数据,发送完命令需要一个延时回显
output = command.recv(65535) # 接受服务器返回的数据
print(output.decode()) # 将收到的数据解码
ssh.close() # 程序结束关闭文件
三、Python读取设备配置文件
1、读取配置文件,将读取到的每一行发送给设备
import paramiko
import time
# 创建客户端对象
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
# 使用客户端导入参数 hostname="192.168.100.1", username="yang", password="123123"
ssh.connect(hostname="192.168.100.1", username="yang", password="123123")
# 发送命令,创建命令对象,调用shell
command = ssh.invoke_shell()
# 读取配置文件,将读取到的每一行发送给设备
with open("SW1.cfg", "r", encoding="utf-8") as f:
while True:
content = f.readline() # 讲读取到的文件,按行储存为一个列表
if not content:
break
# print(i)
command.send(content.encode())
# command.send("sys Hello \n".encode())
# print("please wait a monment ...")
time.sleep(5) # 1s之后回显数据,发送完命令需要一个延时回显
output = command.recv(65535) # 接受服务器返回的数据
print(output.decode()) # 将收到的数据解码
ssh.close() # 程序结束关闭文件
2、测试结果
四、配置多个文件
1、设备
dict_sw1 = {"ip_add": "192.168.100.9", "username": "yang", "password": "123123", "path": "D:\桌面\Dream\Day16\SW1.cfg"}
dict_sw2 = {"ip_add": "192.168.100.20", "username": "yang", "password": "123123", "path": "D:\桌面\Dream\Day16\SW2.cfg"}
database = [dict_sw1, dict_sw2]
def done():
print("""
,ggg, gg
dP""Y8a 88
Yb, `88 88
`" 88 88
88 88
88 88 ,gggg,gg ,ggg,,ggg, ,gggg,gg
88 ,88 dP" "Y8I ,8" "8P" "8, dP" "Y8I
Y8b,___,d888 i8' ,8I I8 8I 8I i8' ,8I
"Y88888P"88,,d8, ,d8b,,dP 8I Yb,,d8, ,d8I
,ad8888P"Y8888P"`Y88P' 8I `Y8P"Y8888P"888
d8P" 88 ,d8I'
,d8' 88 ,dP'8I
d8' 88 ,8" 8I
88 88 I8 8I
Y8,_ _,88 `8, ,8I
"Y888P" `Y8P"
""")
done()
2、脚本
import paramiko
import time
from Day16.devoce_info import *
# 多台设备需要配置数据库,里面存储着各个设备的信息
# 用列表创建数据库,信息使用字典
# range(len(database))遍历,使得item等于对应的值 ---> 0-2
for item in range(len(database)):
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect(hostname=database[item]["ip_add"], username=database[item]["username"],
password=database[item]["password"])
command = ssh.invoke_shell()
with open(database[item]["path"], "r", encoding="utf-8") as f:
while True:
content = f.readline() # 讲读取到的文件,按行储存为一个列表
if not content:
break
# print(i)
command.send(content.encode())
# command.send("sys Hello \n".encode())
print("please wait a monment ...")
time.sleep(5) # 1s之后回显数据,发送完命令需要一个延时回显
output = command.recv(65535) # 接受服务器返回的数据
print(output.decode()) # 将收到的数据解码
done()
ssh.close() # 程序结束关闭文件
五、备份设备配置文件
1、基础配置
display current-configuration # 查看所有配置
exclude #不包括什么配置
include #包括什么配置
display saved-configuration # 查看已保存的配置
[SW1]user-interface console 0 #进入console 0 口 Ensp默认console口登录
[SW1-ui-console0]screen-length 0 # 不分页全部输出
# 由于SSH登录默认
[SW1-ui-console0]screen-length 0
2、脚本
- 基础脚本
"""
display current-configuration 查看当前的配置
exclude #不包括什么配置
include #包括什么配置
display saved-configuration # 查看已保存的配置
"""
import paramiko
import time
# from datetime import time as time_01
from datetime import datetime # 获取当前时间
from Day17.devoce_info import *
# 创造一个SSH客户端
# 多个设备时需要循环
for item in range(len(database)):
ssh_client = paramiko.SSHClient()
ssh_client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh_client.connect(hostname=database[item]["ip_addr"],
username=database[item]["username"],
password=database[item]["password"])
command = ssh_client.invoke_shell()
# 给设备发送查看配置的命令
command.send("system-view\n"
"user-interface vty 0 4\n"
"screen-length 0\n".encode())
command.send("dis cu\n".encode())
time.sleep(3)
recv = command.recv(65535).decode()
# print(recv)
"""
recv 就是设备给我们返回的结果,但是如果直接将这个结果写入文件,文件呈现的效果是,一行结束会多一个回车。
所以处理方法为recv这个文件通过.splitlines(),将他的每一行进行切割,组成一个列表
从而遍历列表,即大文件的每一行,再将每一行写入文件
但是.splitlines()出来的列表没有换行,所以需要写入的时候添加\n
"""
resut = recv.splitlines() # 按行生成列表,没有\n
bk_time = str(datetime.now())
now_time = bk_time[:-7].replace(':', ":")
# 将回显的内容保存到本地
# 需求:文件名为保存文件的时间
with open(f"{database[item]['device_name']}-{now_time}.txt", "w") as file_backup:
for i in resut:
print(i)
file_backup.write(i + '\n')
ssh_client.close()
- datetime模块
from datetime import *
time1 = datetime.now()
# print(time1)
str_time = str(time1)
print(str_time[:-7].replace(":", ":"))
- 设备列表
SW1_info = {"ip_addr": "192.168.100.9",
"username": "yang",
"password": "123123",
"device_name": "SW1",
"device_type": "交换机"}
SW2_info = {"ip_addr": "192.168.100.20",
"username": "yang",
"password": "123123",
"device": "SW2",
"device_type": "交换机"}
database = [SW1_info, SW2_info]
3、测试
Ubuntu安装与配置
1、安装utools
sudo apt upgrade
sudo apt install open-vm-tools-desktop y-
sudo reboot
2、创建root
# 为root创建密码
sudo passwd root
# 切换到root
su - root
3、更换Ubuntu源
-
备份源
sudo cp /etc/apt/sources.list /etc/apt/sources.list.bak
-
更改源
#添加阿里源 deb http://mirrors.aliyun.com/ubuntu/ focal main restricted universe multiverse deb-src http://mirrors.aliyun.com/ubuntu/ focal main restricted universe multiverse deb http://mirrors.aliyun.com/ubuntu/ focal-security main restricted universe multiverse deb-src http://mirrors.aliyun.com/ubuntu/ focal-security main restricted universe multiverse deb http://mirrors.aliyun.com/ubuntu/ focal-updates main restricted universe multiverse deb-src http://mirrors.aliyun.com/ubuntu/ focal-updates main restricted universe multiverse deb http://mirrors.aliyun.com/ubuntu/ focal-proposed main restricted universe multiverse deb-src http://mirrors.aliyun.com/ubuntu/ focal-proposed main restricted universe multiverse deb http://mirrors.aliyun.com/ubuntu/ focal-backports main restricted universe multiverse deb-src http://mirrors.aliyun.com/ubuntu/ focal-backports main restricted universe multiverse #添加清华源 deb https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ focal main restricted universe multiverse # deb-src https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ focal main restricted universe multiverse deb https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ focal-updates main restricted universe multiverse # deb-src https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ focal-updates main restricted universe multiverse deb https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ focal-backports main restricted universe multiverse # deb-src https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ focal-backports main restricted universe multiverse deb https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ focal-security main restricted universe multiverse # deb-src https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ focal-security main restricted universe multiverse multiverse 3.更新
-
更新源
sudo apt-get update
sudo apt install python3 python3-pip -y
2、安装虚拟环境
apt install virtualenv virtualenvwrapper -y
3、定制虚拟环境
-
编辑~/.bashrc文件
tail -n3 ~/.bashrc export WORKON_HOME=$HOME/.virtualenvs # find / -name virtualenvwrapper.sh 可以查看文件路径 source /usr/share/virtualenvwrapper/virtualenvwrapper.sh
-
配置生效
source ~/.bashrc
# 创建命令
mkvirtualenv -p /usr/bin/python3 YikJiang
# 退出虚拟环境
deactivate
# 删除虚拟环境
rmvirtualenv YikJiang
# 进入虚拟环境
workon YikJiang
# 查看虚拟环境下的内容
ls .virtualenvs/
yikjiang@yikjiang:~$ ls .virtualenvs/
get_env_details postdeactivate postrmvirtualenv premkproject YikJiang
initialize postmkproject preactivate premkvirtualenv
postactivate postmkvirtualenv predeactivate prermvirtualenv
5、虚拟环境的模块源设置(PIP源)
# 安装软件
pip install name
# 查看通过pip安装的Python模块
pip list
# 删除模块
pip uninstall name # 不添加sudo
# 查看以安装的软件和版本号
pip freeze
# 更换pip源
pip install pip -U
pip config set global.index-ur1 https://pypi.tuna.tsinghua.deu.cn/simple
PyCharm集成
一、与Ubuntu联通
1、目标
- Window上的PyCharm使用Ubuntu上的虚拟环境里的解释器
-
需要查看Python解释器在哪里
ls .virtualenvs/YikJiang/bin/
-
完整的路径
/home/yangyubo/.virtualenvs/YikJiang/bin/
- PyCharm专业版与Ubuntu连通,实现文件传递
- PyCharm使用Ubuntu虚拟环境里面的python解释器
# 安装 SSH 软件
sudo apt-get install openssh-server
# 修改SSH配置(修改端口号,是否使用root用户登录等)
sudo vim /etc/ssh/sshd_config
# 能让root用户登录
PermitRootLogin yes
# 重启服务
sudo systemctl restart ssh
# 查看当前服务的状态
systemctl status ssh
# 关闭防火墙
sudo ufw disable
sudo systemctl stop ufw
# 测试SSH登录
3、配置SFTP
- 在PyCharm的菜单栏中,找到
tools-Deploymen-Configuration
,配置SFTP
- 配置SFTP参数
-
PyCharm远程连接失败、错误,报错:Can‘t connect…【解决方法与错误分析】
# 原因 未安装ssh # 解决方法 apt-get install openssh-server
-
创建目录
yikjiang@yikjiang:~$ mkdir yikjiang_py
-
创建关联目录
- 配置解释器位置
- 其中下面有本地文件和Ubuntu文件
-
PyCharm上载到Ubuntu
Ctrl+Shift+Alt+x
-
查看Ubuntu
-
psutil是一个跨平台库,主要获取两部分的信息
-
进程信息
-
系统利用率信息 - CPU、内存、磁盘、网络等
-
支持版本
- 2.x
- 3.x
-
最新版本:
psutil 5.9.0
yikjiang@yikjiang:~/yikjiang_py$ workon yikjiang
(yikjiang) yikjiang@yikjiang:~/yikjiang_py$ pip install psutil
二、常用命令
1、进程相关
ps、top、lsof、kill、nice、pidof、taskset
ps # 显示当前进程的状态,类似于Windows的进程管理器
ps -aux
a:显示其他用户启动的进程
u:启动这个进程的用户和他的启动时间
x:查看系统中属于自己的进程
lsof # lsof(list open files)是一个列出当前系统打开文件的工具
kill # Linux kill 命令用于删除执行中的程序或工作。
nice # nice命令用于改变进程的优先级。niceness值为负时,表示高优先级,能提前执行和获得更多的资源,对应低友善度;反之,则表示低优先级,高友善度。
pidof # pidof是一个命令行实用程序,可让您找到正在运行的程序的进程ID
。
taskset
# taskset命令用于设置进程(或 线程)的处理器亲和性(Processor Affinity)
#可以将进程(或 线程)绑定到特定的一个 或 多个CPU上去执行,而不允许将进程(或线程)调度到其他的CPU上
2、网络相关
netstat、ifconfig
netstat # Linux netstat 命令用于显示网络状态。
ifconfg # Linux ifconfig命令用于显示或设置网络设备。 ifconfig可设置网络设备的状态,或是显示目前的设置。
3、用户相关
who
who
# Linux who命令用于显示系统中有哪些使用者正在上面,显示的资料包含了使用者 ID、使用的终端机、从哪边连上来的、上线时间、呆滞时间、CPU 使用量、动作等等。
#使用权限:所有使用者都可使用。
3、磁盘相关
df
df # Linux df(英文全拼:disk free) 命令用于显示目前在 Linux 系统上的文件系统磁盘使用情况统计。
4、内存相关
free
free
# Linux free命令用于显示内存状态。
# free指令会显示内存的使用情况,包括实体内存,虚拟的交换文件内存,共享内存区段,以及系统核心使用的缓冲区等。
5、io相关
- iostat属于sysstat软件包。可以直接安装:
apt install sysstat -y
安装iostat
ionice、iostat、iotop
ionice # ionice 获取或设置程序的IO调度与优先级。
iostat # iostat是I/O statistics(输入/输出统计)的缩写 显示所有设备的负载情况
iotop # apt install iotop -y 安装iotop
iotop命令是一个用来监视磁盘I/O使用状况的top类工具。iotop具有与top相似的UI,其中包括PID、用户、I/O、进程等相关信息。Linux下的IO统计工具如iostat,nmon等大多数是只能统计到per设备的读写情况,如果你想知道每个进程是如何使用IO的就比较麻烦,使用iotop命令可以很方便的查看。
与iostat工具比较,iostat是系统级别的IO监控,而iotop是进程级别IO监控
6、运行时相关
uptime
uptime # 查看当前的允许状态 当前运行了几个账户
7、终端相关
tty
tty
# Linux tty命令用于显示终端机连接标准输入设备的文件名称。
# 在Linux *** 作系统中,所有外围设备都有其名称与代号,这些名称代号以特殊文件的类型存放于/dev目录下。
# 你可以执行tty(teletypewriter)指令查询目前使用的终端机的文件名称
8、将服务器调整为英文格式输出
yikjiang@yikjiang:~/桌面$ LANG=EN
yikjiang@yikjiang:~/桌面$ echo $LANG
EN
9、查看系统所支持的语言
yikjiang@yikjiang:~/桌面$ locale
LANG=zh_CN.UTF-8
LANGUAGE=zh_CN:zh
LC_CTYPE="zh_CN.UTF-8"
LC_NUMERIC="zh_CN.UTF-8"
LC_TIME="zh_CN.UTF-8"
LC_COLLATE="zh_CN.UTF-8"
LC_MONETARY="zh_CN.UTF-8"
LC_MESSAGES="zh_CN.UTF-8"
LC_PAPER="zh_CN.UTF-8"
LC_NAME="zh_CN.UTF-8"
LC_ADDRESS="zh_CN.UTF-8"
LC_TELEPHONE="zh_CN.UTF-8"
LC_MEASUREMENT="zh_CN.UTF-8"
LC_IDENTIFICATION="zh_CN.UTF-8"
LC_ALL=
三、命令实践
1、获取网卡的IP地址
ifconfig
iang@yikjiang:~/桌面$ ifconfig ens33 | grep netmask | awk '{print }'
192.168.184.133
ip addr
yikjiang@yikjiang:~/桌面$ ip addr | grep global | awk '{print }'
192.168.184.133/24
# cut -d '/' -f1 : 以 / 为分隔符,取第一个区
yikjiang@yikjiang:~/桌面$ ip addr | grep global | awk '{print }' | cut -d '/' -f1
192.168.184.133
cut
Linux cut命令用于显示每行从开头算起 num1 到 num2 的文字。
-b :以字节为单位进行分割。这些字节位置将忽略多字节字符边界,除非也指定了 -n 标志。
-c :以字符为单位进行分割。
-d :自定义分隔符,默认为制表符。
-f :与-d一起使用,指定显示哪个区域。
-n :取消分割多字节字符。仅和 -b 标志一起使用。如果字符的最后一个字节落在由 -b 标志的 List 参数指示的
范围之内,该字符将被写出;否则,该字符将被排除
2、获取所有网卡的IP地址
ifconfig
# 获取网卡名称
yikjiang@yikjiang:~/桌面$ ifconfig | grep flags | cut -d ':' -f1
ens33
lo
#获取所有IP地址
#仅限于 ifconfig命令,ip address 不能用
# cut -d '/' -f1 : 以 / 为分隔符,取第一个区域
yikjiang@yikjiang:~/桌面$ for net_name in $(ifconfig | grep flags | cut -d ':' -f1);do ifconfig $net_name | grep netm | awk '{print }';done
192.168.184.133
127.0.0.1
ip addr
yikjiang@yikjiang:~/桌面$ ip addr | grep scope | grep -v inet6 | awk '{print }' | cut -d '/' -f1
127.0.0.1
192.168.184.133
3、获取所有运行端口的信息
netstat
yikjiang@yikjiang:~/桌面$ sudo netstat -nupl #查看UDP类型的端口
[sudo] yikjiang 的密码:
激活Internet连接 (仅服务器)
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
udp 0 0 127.0.0.53:53 0.0.0.0:* 764/systemd-resolve
udp 0 0 0.0.0.0:5353 0.0.0.0:* 809/avahi-daemon: r
udp 0 0 0.0.0.0:39229 0.0.0.0:* 809/avahi-daemon: r
udp 0 0 0.0.0.0:631 0.0.0.0:* 880/cups-browsed
udp6 0 0 :::5353 :::* 809/avahi-daemon: r
udp6 0 0 :::58728 :::* 809/avahi-daemon: r
yikjiang@yikjiang:~/桌面$ sudo netstat -ntpl
激活Internet连接 (仅服务器)
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
tcp 0 0 127.0.0.53:53 0.0.0.0:* LISTEN 764/systemd-resolve
tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN 907/sshd: /usr/sbin
tcp 0 0 127.0.0.1:631 0.0.0.0:* LISTEN 812/cupsd
tcp6 0 0 :::22 :::* LISTEN 907/sshd: /usr/sbin
tcp6 0 0 ::1:631 :::* LISTEN 812/cupsd
四、模块安装
1、模块安装
- 进入普通用户的 虚拟环境 ,并查看已经安装的模块
yikjiang@yikjiang:~/桌面$ workon yikjiang
(yikjiang) yikjiang@yikjiang:~/桌面$ pip list
Package Version
------------- -------
pip 20.0.2
pkg-resources 0.0.0
psutil 5.9.0
setuptools 44.0.0
wheel 0.34.2
(yikjiang) yikjiang@yikjiang:~/桌面$ pip install psutil
2、模块的使用
- 在Python环境中
# 导入包
import psutil
# 使用包功能
psutil.virtual_memory()
(yikjiang) yikjiang@yikjiang:~/桌面$ python3
Python 3.8.10 (default, Mar 15 2022, 12:22:08)
[GCC 9.4.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import psutil
>>> psutil.virtual_memory()
svmem(total=2055090176, available=696741888, percent=66.1, used=1181618176, free=137699328, active=1053388800, inactive=419950592, buffers=69718016, cached=666054656, shared=2637824, slab=200876032)
- ipython
(yikjiang) yikjiang@yikjiang:~/桌面$ sudo apt install ipython3
(yikjiang) yikjiang@yikjiang:~/桌面$ ipython3
In [1]: import psutil
In [2]: psutil.virtual_memory()
Out[2]: svmem(total=2055090176, available=626012160, percent=69.5, used=1251840000, free=134275072, active=1024696320, inactive=449495040, buffers=50929664, cached=618045440, shared=2580480, slab=202473472)
五、PyCharm使用
1、测试
import psutil
info = psutil.virtual_memory()
print(info)
2、获取系统性能信息
CPU 信息
- Linux *** 作系统的CPU利用率有以下几个部分:
User Time
,执行用户进程的时间百分比;System Time
,执行内核进程和中断的时间百分比;Wait IO
,由于IO等待而使CPU处于idle(空闲)状态的时间百分比;Idle
,CPU处于idle状态的时间百分比
#基本信息
.cpu_times() # 我们可以为该方法添加 percpu=True 的属性,获取 逻辑cpu信息
#cpu核信息
.cpu_count() # 我们可以为该方法添加 percpu=True 的属性,获取 逻辑cpu信息
- 代码
import psutil
# 获取总体基本信息
cpu_info = psutil.cpu_times()
print(f"CPU的基本信息{cpu_info}")
# 获取部分信息
cpu_user = cpu_info.user
print(f'CPU用户进程的时间为:{cpu_user}')
cpu_system = cpu_info.system
print(f"CPU系统进程的时间为:{cpu_system}")
cpu_iowait = cpu_info.iowait
print(f"CPU IO等待时间占比为:{cpu_iowait}")
# 获取逻辑包含的部分信息
# 生成的是列表,并且显示多个逻辑CPU信息
# 在虚拟机设置里可以设置CPU的数量
cpu_info_percpu = psutil.cpu_times(percpu=True)
print(f"CPU基本信息:{cpu_info_percpu}")
- 输出
CPU的基本信息scputimes(user=35.73, nice=28.13, system=69.67, idle=12166.18, iowait=0.98, irq=0.0, softirq=0.88, steal=0.0, guest=0.0, guest_nice=0.0)
CPU用户进程的时间为:35.73
CPU系统进程的时间为:69.67
CPU IO等待时间占比为:0.98
CPU基本信息:[scputimes(user=16.29, nice=11.39, system=34.8, idle=6086.92, iowait=0.61, irq=0.0, softirq=0.47, steal=0.0, guest=0.0, guest_nice=0.0), scputimes(user=19.43, nice=16.74, system=34.87, idle=6079.25, iowait=0.36, irq=0.0, softirq=0.4, steal=0.0, guest=0.0, guest_nice=0.0)]
Process finished with exit code 0
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)