网工速通Python-part3-基础数据类型:数字、字符串与列表
  vYgR16aEGw5K 2023年11月02日 41 0

前言

《从零开始NetDevOps》是本人8年多的NetDevOps实战总结的一本书(且称之为书,通过公众号连载的方式,集结成册,希望有天能以实体书的方式和大家相见)。

NetDevOps是指以网络工程师为主体,针对网络运维场景进行自动化开发的工作思路与模式,是2014年左右从国外刮起来的一股“网工学Python"的风潮,最近几年在国内逐渐兴起,最近两年在国内也有少量的几本书,从不同角度来阐述了NetDevOps和它的技能体系,但和本人对于NetDevOps的理解还是有所不同,因为国内的网络环境相对比较复杂,而一些知识又和国外的平台绑定比较深,导致NetDevOps实践中有很多难题困扰着大家,笔者也在各种平台和场合解答过很多问题。此次希望能通过自己的知识分享,给大家呈现出一个不同于其他人的实战为指导、普适性强、善于抠细节、知其然知其所以然风格、深入浅出的NetDevOps知识体系,给大家一个不同的视角,一个来自于实战中的视角。

本人在国内某大型金融机构的数据中心从事网络自动化开发8年之久,从最早的脚本开发、一个简单的web自动化工具,到目前迭代出了一个由众多微服务组成的网络自动化运维系平台,覆盖了国内外众多主流厂商的设备,日常运维各种“奇葩”需求,和众多网工交流过NetDevOps底层技术,和很多网络团队管理者聊过网络自动化的前景与NetDevOps思想,甚至有幸和一些总监、创始人们聊过网络自动化运维平台的建设。

思想的不断碰撞、知识体系的不断迭代,让我有很多想说又无法名状的想法与情愫,希望在这本书中,与读者朋友们娓娓道来。


补充一个昨天的内容,Python的33个保留字如下:

网工速通Python-part3-基础数据类型:数字、字符串与列表_字符串

2.4 基础数据类型

每个编程语言都有自己独特的基础数据类型,我们耳熟能详的比如整数、字符串、数组等等,其他所有的复杂对象都是从这些基础的数据结构衍生出来的。。

Python的基础数据类型包含以下几种:数字、字符串、列表、字典、元组、集合、布尔。

另外还有一个特殊值,空值None,类似于其他语言中的NULL,虽然它不是基础数据类型,但它很“基础”。它代表是一个空对象(它不是基础数据类型,但是是一个非常简单且需要掌握的数据类型),不指向任何实际的数据。

下面我们将展开讲解各个数据类型。

2.4.1 数字

Python中的数字分为整数(int)、浮点数(float)、复数(complex),复数在日常中基本不会涉及,我们跳过。

整数

Python中的整数与我们的书写习惯一致。正整数、负整数、零均按我们的日常使用习惯使用即可。

整数的上下限在64位系统中可以认为无上限。而且在日常网络运维也不会出现一个内存无法保存的整数。我们无需关注这些极端情况。

a = 10
b = 0
c = -20

浮点数

浮点数即我们日常所说的小数,浮点数在Python中与我们的书写也一致。

x = 51.2
y = -11.2

同时它还支持科学计数法,大家了解即可,日常开发中很少使用。

a = 1.23e-18  # 等同于1.23*10^-18

数字的计算

python的数值支持加(+),减(-),乘(*),除(/),整除(//),取余(%),代码如下:

a = 6
b = 8
print(a + b)  # 输出结果14
print(a - b)  # 输出结果-2
print(a * b)  # 输出结果48
print(a / b)  # 输出结果0.75
print(a // b)  # 输出结果0
print(a % b)  # 输出结果6

2.4.2 字符串

字符串是非常重要的一个数据类型,它是以引号括起来的一段文本。引号可以是单引号、双引号、三引号(三个单引号或者三个双引号),但是一定要成对出现,引号中间的文本内容是字符串承载的数据。字符串是严格区分大小写的。只写一对引号,内容为空,我们称之为空字符串。在Python中没有char(单个字符)这种数据类型,字符串中的字符可以是任意个,包括零个、一个或者多个。

示例:

a = 'NetDevOps'
b = "NetDevOps"
c = '''this is a book about NetDevOps
这是一本关于NetDevOps的书
'''

示例中我们用单引号,双引号和三引号分别创建了三个字符串。假如我们的字符串中含有引号,这个时候该如何处理呢?

方法1:定义字符串的引号和字符串文本中的引号使用不同的引号。对于新手建议使用这种。

d = "It's a book about NetDevOps."
e = 'It is a book about "NetDevOps".'
f = '''It's a book abount "NetDevOps".'''  # 文本中既有单引号又有双引号,我们可以考虑用三引号。

方法2:使用转义符号反斜杠\,转移符号后接我们要使用的引号。

d = 'It\'s a book about NetDevOps.'
e = "It is a book about \"NetDevOps\"."

关于转义

我们想用字符串表示一个回车怎么处理呢?

Python的做法是使用\n代表回车,其中\ 就是转义符号,它后面接字母n代表回车换行,字母n的表达意义发生了转换,这就是转义。反斜杠被称为转义符号,\n被称为转义字符。

下表是一些常用的转义字符(笔者根据日常运维所需进行了取舍,针对初学者列举出了使用频率比较高的)

转义字符

说明

\n

换行,将光标位置移到下一行开头。

\r

将光标位置移到本行开头

\t

横向制表符

\'

单引号

\"

双引号

\\

斜杠符号,\本身是转义符号,我们想用它表示字符串本身时需要转义

在NetDevOps中, 我们写代码,我们定义一个设备名称的变量,就可以赋值成字符串类型的,设备的制造商可以是字符串,设备的所在房间可以是字符串,设备端口的配置可以字符串。待执行的一条命令也可以是字符串。

dev_name = 'as01'
dev_manufacture = 'HUAWEI'
dev_room = '0401'
# 我们可以适当对单词进行缩写,比如用intf代表interface。
# 但是尽量不要用int,int是一个用于将对象转换成整数的函数。
intf_config = '''interface Vlan20
 ip address 192.168.137.201 255.255.255.0
'''
cmd = "show version"

字符串的常用方法

Python的字符串提供了很多便利的方法(可以简单等同于函数,初学者不必纠结名称),可以方便我们处理字符串,比如进行查找、切割、转大小写等,根据笔者的使用经验和大家简单介绍一下常用的方法。

注:函数是一个单独定义的代码块,方法是对象中的一个执行特定功能的代码块。对于初学者可以简单把函数与方法划等号,也可以简单认为方法是在对象中的函数的称谓,无需过多纠结这两个名词。本书编写会尽量严谨,但是读者在阅读过程中可以把方法和函数划等号。

以下方法直接在我们的字符串变量后写点和方法的名字即可,方法中要传入对应的参数,形式如下。

字符串变量.方法名( 参数 )

format

format方法是一个字符串格式化的方法,字符串的格式化是指,按照一定模板,向模板内传值,生成一个符合模板格式要求的字符串。Python的字符串格式化方法非常多,此处我们重点介绍format方法。

先编写一个字符串的模板,对于其中希望填充值的地方用花括号{}括起来,然后对模板字符串调用format方法,依次传入要填充的字符串,数目一定要与花括号的数目对应。

server = 'host01'
ip_addr = '192.168.1.100'
intf_desc_tpl = 'connect to {}, ip:{}'
intf_desc.format(server,ip_addr)
print(intf_desc)  # 结果是"connect to host01, ip:192.168.1.100"

这种方法还有另外一个书写方式,在定义字符串模板的时候,希望填充的值用花括号括起来,同时花括号内填这个填充值的参数名称,然后对模板字符串调用format方法,为花括号内的参数进行赋值。此方法可读性非常好,但是写起来会比上面的方法多写一些内容,初学者可以在看到函数部分后再回来看看此部分示例。

server = 'host01'
ip_addr = '192.168.1.100'
intf_desc_tpl = 'connect to {SERVER}, ip:{SERVER_IP}'
intf_desc.format(SERVER=server, SERVER_IP=ip_addr)
print(intf_desc)  # 结果是"connect to host01, ip:192.168.1.100"
find

find方法用户发现字符串中是否包含子串,如果发现子串返回子串首字母出现的位置索引值(Python中的索引从零开始),如果未发现返回-1

我们可以通过比较find的结果与0比较,小于零代表未发现,大于等于零代表发现了子串。

intf_show = 'Eth1/1 is up'
up_index = intf_show.find('up')
print(up_index)

网工速通Python-part3-基础数据类型:数字、字符串与列表_字符串_02

最终输出结果是10。

如果我们改为find('down')则输出结果是-1。

我们在NetDevOps开发中可以用于判断回显是否包含关键字。

startswth

startswith方法用于判断是否以给定的字符串开始的,返回是真(True)或假(False)。

intf_show = 'Ethernet1/1 is up'
is_interface_line = intf_show.startswith('Ethernet')
print(is_interface_line)  # 输出结果是Ture
endswith

endswith方法用于判断是否以给定的字符串结束的,返回是真(True)或假(False)。

intf_show = 'Ethernet1/1 is up'
interface_up = intf_show.endswith('up')
print(interface_up)

find 、startswith、endswith主要用于在文本中发现是否有关键字,通过关键字我们可以判断一些状态,或者确定此行中是否有我们要提取的信息等等。

split

split方法用于切割字符串,返回的结果是列表(list,后续会展开讲)。

默认是用空白符来进行切割,空白符泛指没有显示却又占位置的符号,比如空格、制表符、换行。

intf_show = 'Ethernet1/1 is up'
result = intf_show.split()
print(result)  # 结果是['Ethernet1/1', 'is', 'up']

通过这种方式,我们就可以获取包含一些字段信息的列表,再通过列表的访问机制就可以提取出端口名称和端口状态了,这个我们在列表中讲解使用方法。

我们也可以用指定的字符去切割,比如我们使用is去切割。

intf_show = 'Ethernet1/1 is up'
result = intf_show.split('is')
print(result)  # 结果是['Ethernet1/1 ', ' up']

这样直接获取了端口和状态。

strip

strip方法用去去除字符串左右的指定字符串,不修改原来的字符串(因为字符串是不可修改的类型),返回一个新的字符串。

默认是去除左右的所有空白符。

我们也可以在方法内直接传入要去除的字符串,同时strip还有两个变种方法lstrip和rstrip,可以只去除左侧或者右侧的指定字符串,但这些NetDevOps脚本编写很少涉及,故不演示。

intf_show = '    Ethernet1/1 is up    '
result = intf_show.strip()
print(result)  # 结果是"Ethernet1/1 is up"
splitlines

splitlines方法用于将一大段文本按行切割,返回一个列表。行的结束符号Python会帮我们自动判断,对于初学者无需关注。

intf_config = '''interface Vlan20
 ip address 192.168.137.201 255.255.255.0
'''
configs = intf_config.splitlines()
# 结果是['interface Vlan20', ' ip address 192.168.137.201 255.255.255.0']
print(configs)
replace

replace方法用于将某字符串替换为我们指定的字符串。它有两个参数,第一个想要替换的字符串,第二个是要去替换之前那个字符串的字符串。一个是old,一个是new。

intf_name = 'Eth1/1'
full_intf_name = intf_name.replace('Eth', 'Ethernet')
print(full_intf_name)  # 结果是"Ethernet1/1"

由于字符串是不可变的数据类型,所以原有的变量intf_name指向的字符串不会被修改,我们需要将函数返回的值赋值给一个变量,可以是新定义一个变量,也可以用原有的变量。

intf_name = 'Eth1/1'
intf_name = intf_name.replace('Eth', 'Ethernet')
print(intf_name)  # 结果是"Ethernet1/1"

字符串的拼接

字符串通过加号可以实现字符串的拼接,生成一个新的字符串。

server = 'host01'
ip_addr = '192.168.1.100'
intf_desc = 'connect to ' + server + ', ip:' + ip_addr
print(intf_desc)  # 结果是"connect to host01, ip:192.168.1.100"

对于初学者而言,多用于按照一定格式拼接多个字符串。如果是这种情况,更推荐字符串格式化这种方式。

以上几个字符串的相关使用,结合判断、循环,我们就可以写出一些复杂的逻辑,对网络配置进行解析,提取出我们想要的信息。这个思路,我们在掌握了判断循环后再去编写代码实现。

2.4.3 列表

列表(list)有点类似于其他编程语言中的数组,它是一组有序的数据,每个成员都有一个索引值,索引值从零开始依次递增。

这组有序的数据类型可以是Python的基础数据类型,也可以是复杂的对象。Python的列表最大的不同在于成员的数据类型可以不一。

它的创建方式比比较简单,用中括号(方括号)创建,列表中的成员用逗号隔开。

intfs = ['Eth1/1', 'Eth1/2', 'Eth1/3', 'Eth1/4']
dev_info = ['192.168.1.1', 'as01', 'huawei', 'ce6800', 48, ['beijing', 'dc01']]

如上,第一个我们用于定义一组端口,都是字符串的成员。这是在日常NetDevOps开发中比较常见的一种形式,成员都是相同的类型,代表一类事物。

第二个列表我们稍微改了一下,成员既有字符串,又有数字,还有列表。这种是典型用多个维度的成员组成的列表,用于描述一个事物。deb_info这个变量中我们通过成员描述了它的IP地址、设备名称、厂商、系列、端口数记忆所属数据中心。

这是笔者总结的两种组织列表的场景:同一纬度的成员描述一系列事物,不同维度的成员描述一个事物

访问成员

列表是有序的,通过它对应的排序(从零开始),称之为索引更为准确,通过方括号,我们可以访问到这个成员。

intfs = ['Eth1/1', 'Eth1/2', 'Eth1/3', 'Eth1/4']
intf = intfs[0]  # 此处千万不要用int去命名端口变量,会与int函数冲突
print(intf)  # 此处输出'Eth1/1'

intf = intfs[2]
print(intf)  # 此处输出'Eth1/3'

Python的列表访问成员还有一个非常有意思的,异于其他语言的特性,负索引。

我们可以输入负数,代表从倒数第N个成员。负索引最后一个成员开始排序,最后一个的索引是-1。

网工速通Python-part3-基础数据类型:数字、字符串与列表_数据类型_03

intfs = ['Eth1/1', 'Eth1/2', 'Eth1/3', 'Eth1/4']
intf = intfs[-1]  # 此处千万不要用int去命名端口变量,会与int函数冲突
print(intf)  # 此处输出'Eth1/4'

无论是正索引还是负索引,我们访问都不能越界,如上图,我们不能访问索引为4或者-5的成员,因为它不存在,Python都会报错。

计算列表长度

我们想获取列表长度的时候可以直接调用一个Python的内置函数len,然后传入列表。

intfs = ['Eth1/1', 'Eth1/2', 'Eth1/3', 'Eth1/4']
intf_sum = len(intfs)
print(intf_sum)  # 此处输出4
print(intfs[intf_sum - 1])  # 此处输出'Eth1/4'

我们访问列表的最后一个成员的时候,可以使用长度减一的索引来访问,但是不建议,Python的风格(我们称之为pythonic),一般使用负索引。

追加成员

列表是一个可变的数据类型,在创建之后,我们还可以继续在列表内追加成员,使用列表的append方法即可,一次只能追加一个成员,可以一直追加。

intfs = ['Eth1/1', 'Eth1/2', 'Eth1/3', 'Eth1/4']
intfs.append('Eth1/4')
# 结果是['Eth1/1', 'Eth1/2', 'Eth1/3', 'Eth1/4', 'Eth1/4']
print(intfs)

合并列表

两个列表合并有两种方式:

  1. 使用加法,两个列表合并成一个新的列表,原有的两个列表没有任何变化
intfs_part1 = ['Eth1/1', 'Eth1/2', 'Eth1/3', 'Eth1/4']
intfs_part2 = ['Eth1/5', 'Eth1/6', 'Eth1/7', 'Eth1/8']
intfs = intfs_part1 + intfs_part2

print(intfs_part1)  # 结果是['Eth1/1', 'Eth1/2', 'Eth1/3', 'Eth1/4', 'Eth1/4']
print(intfs_part2)  # 结果是['Eth1/5', 'Eth1/6', 'Eth1/7', 'Eth1/8']
print(intfs)  # 结果是['Eth1/1', 'Eth1/2', 'Eth1/3', 'Eth1/4', 'Eth1/5', 'Eth1/6', 'Eth1/7', 'Eth1/8']
  1. 使用extend方法,将另外一个列表B批量追加到调用方法的列表A之后,只有调用方法的列表A发生变化,有了新的成员。
intfs_part1 = ['Eth1/1', 'Eth1/2', 'Eth1/3', 'Eth1/4']
intfs_part2 = ['Eth1/5', 'Eth1/6', 'Eth1/7', 'Eth1/8']
intfs_part1.extend(intfs_part2)

print(intfs_part1)  # 结果是['Eth1/1', 'Eth1/2', 'Eth1/3', 'Eth1/4', 'Eth1/5', 'Eth1/6', 'Eth1/7', 'Eth1/8']
print(intfs_part2)  # 结果是['Eth1/5', 'Eth1/6', 'Eth1/7', 'Eth1/8']

切片

切片如同这个词的字面意思,是指从一个已有的列表中切取一“片”,这一“片”也就是一个子列表,切片的方式很灵活,它的规则如下。

[start_index:stop_index:step]

start_index 是指起始索引值,可以不写,默认是列表头。

stop_index 是指结束索引值,可以不写,默认取到列表尾。

step 是指步长,是取成员索引的间隔。(可以为负数,达到反向切片效果,了解即可)

通过以上的方式,则会返回一个新的列表。

切片过程中如果指定了stop_index,则只能取到stop_index 前一个符合步长的索引值。类似于数学中的开区间,无法取值到stop_index。

intfs = ['Eth1/1', 'Eth1/2', 'Eth1/3', 'Eth1/4']
sub_intfs = intfs[:3]
# 只能取值到3前面的索引也就是到索引2,结果是['Eth1/1', 'Eth1/2', 'Eth1/3']
print(sub_intfs)

sub_intfs = intfs[1:3]
# start_index是可以取到的,但是stop_index是取不到的,结果是['Eth1/2', 'Eth1/3']
print(sub_intfs)


sub_intfs = intfs[::2]
# 旗帜索引可以不填写,默认从头取到尾,间隔为2,结果是['Eth1/1', 'Eth1/3']
print(sub_intfs)

以上就是今天的分享,明天我们讲解剩余的基础数据类型。



【版权声明】本文内容来自摩杜云社区用户原创、第三方投稿、转载,内容版权归原作者所有。本网站的目的在于传递更多信息,不拥有版权,亦不承担相应法律责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@moduyun.com

  1. 分享:
最后一次编辑于 2023年11月08日 0

暂无评论

推荐阅读
  zwkHF6kqOgW1   2023年11月02日   85   0   0 python
  rRtXVg6Hk601   2023年11月02日   58   0   0 python
  zwkHF6kqOgW1   2023年11月02日   49   0   0 python
vYgR16aEGw5K