python 集合、函数和文件操作

时间:2023-01-21 07:13:49

1.set集合

set集合是一个无序、不可重复、可嵌套的序列,基本功能是进行成员关系测试和删除重复元素,可以使用大括号({})或者 set()函数创建集合,注意:创建一个空集合必须用 set() 而不是 { },因为 { } 是用来创建一个空字典

1.1 常用功能函数

  • 增加元素

    使用set.add()函数单个添加元素;使用set.update()函数来批量添加元素,参数为可迭代的对象,如列表,元组,字符串

    >>> test = {1,2,3,4,}      #创建一个集合
    >>> test.add("") #添加集合元素5
    >>> test.add("")
    >>> test
    {1, 2, 3, 4, '', ''}
    >>> test.add("") #重新添加集合元素5.发现,之前已有,印证了不重复性
    >>> test
    {1, 2, 3, 4, '', ''}
    >>> li = [7,8,9,0]
    >>> test.update(li) #将li列表的元素批量添加到集合中
    >>> test
    {0, 1, 2, 3, 4, 7, 8, 9, '', ''}
    >>> test.update("fuzj") #将fuzj字符串每个字符作为元素添加到集合中
    >>> test
    {0, 1, 2, 3, 4, 7, 8, 9, 'f', 'z', 'j', '', 'u', ''}
  • 删除元素

删除可以使用set.discard() 移除元素,不存在不报错 set.remove() 移除元素,不存在则报错 set.pop() 删除最后的元素,并打印,不能加参数

>>> test
{0, 1, 2, 3, 4, 7, 8, 9, 'f', 'z', 'j', '', 'u', ''}
>>> test.discard(0) #使用dicard删除0元素
>>> test
{1, 2, 3, 4, 7, 8, 9, 'f', 'z', 'j', '', 'u', ''}
>>> test.discard(100) #使用discard删除不存在的元素,不报错
>>> test
{1, 2, 3, 4, 7, 8, 9, 'f', 'z', 'j', '', 'u', ''}
>>> test.remove(1) #使用remove删除1元素
>>> test
{2, 3, 4, 7, 8, 9, 'f', 'z', 'j', '', 'u', ''}
>>> test.remove(100) #使用remove删除不存在的元素,报错
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
KeyError: 100
>>> test.pop() #使用pop随机删除元素
2
>>> test
{3, 4, 7, 8, 9, 'f', 'z', 'j', '', 'u', ''}
>>> test.pop()
3
  • 交集

交集为两个集合相同的元素,使用intersection函数或者&符号,将交集重新组合为一个集合

>>> test = {1,2,3,4,5,}
>>> test2 = {2,3,5,6,7,8,}
>>> test.intersection(test2) #取test和test2的交集
{2, 3, 5}
>>> test3 = test.intersection(test2)
>>> test3
{2, 3, 5}
>>> test & test2 #使用&符号来取交集
{2, 3, 5}
>>> test.intersection_update(test2) #加update将会把test更新,只留下交集部分
>>> test
{2, 3, 5}
  • 并集

并集为两个集合合并,去重后的结果,可以用union函数或|符号,将并集重新组合为一个集合

>>> test
{1, 2, 3, 4, 5}
>>> test2
{2, 3, 5, 6, 7, 8}
>>> test.union(test2)
{1, 2, 3, 4, 5, 6, 7, 8}
>>> test | test2
{1, 2, 3, 4, 5, 6, 7, 8}
  • 差集

差集为两个集合不相同的元素,或者B对于A中不存在的元素,或者A对于B中不存在的元素,取差集要确定好对比的前后顺序,使用difference函数和symmetrice_difference函数,也可以使用-和符号

>>> test
{1, 2, 3, 4, 5}
>>> test2
{2, 3, 5, 6, 7, 8}
>>> test.difference(test2) #取A集合中,B集合不存在的元素
{1, 4}
>>> test - test2 #使用-号 取A集合中,B集合不存在的元素
{1, 4}
>>> test2.difference(test) #取B集合中,A集合不存在的元素
{8, 6, 7}
>>> test2 - test #使用-号 取B集合中,A集合不存在的元素
{8, 6, 7}
>>> test.symmetric_difference(test2) #取A、B集合中互相不存在元素
{1, 4, 6, 7, 8}
>>> test ^ test2 #使用^号 取A、B集合中互相不存在元素
{1, 4, 6, 7, 8} 同样支持update将结果更新至原集合中,
test.symmetric_difference_update(test2)
>>> test
{1, 4, 6, 7, 8}
  • 其他函数

    • set.copy() 拷贝个集合,为浅copy
    • set.clear() 清空集合

1.2 练习示例

取old_cmdb和new_cmdb的差集,交集等,常用在cmdb更新中

```
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
#pyversion:python3.5
#owner:fuzj old_cmdb = {
"#1":8,
"#2":4,
"#4":2,
} new_cmdb = {
"#1": 4,
"#2": 4,
"#3": 2,
}
print("old_cmdb:" , old_cmdb)
print("new_cmdb:" , new_cmdb) s1 = set(old_cmdb.keys())
s2 = set(new_cmdb.keys()) #old_cmdb 要删除的
s_del = s1.difference(s2)
print("old_cmdb 要删除的: ",s_del) #old_cmdb 要更新的
s_upd1 = s1.intersection(s2)
s_upd2 = set()
for i in s_upd1:
if old_cmdb[i] != new_cmdb[i]:
s_upd2.add(i)
print("old_cmdb 要更新的:",s_upd2) #old_cmdb 要添加的
s_add = s2.difference(s1) print("old_cmdb 要添加的",s_add)
```

运行结果:

```
old_cmdb: {'#4': 2, '#2': 4, '#1': 8}
new_cmdb: {'#3': 2, '#2': 4, '#1': 4}
old_cmdb 要删除的: {'#4'}
old_cmdb 要更新的: {'#1'}
old_cmdb 要添加的 {'#3'} ```

2.函数

函数是组织好的,可重复使用的,用来实现单一,或相关联功能的代码段。函数能提高应用的模块性,和代码的重复利用率,分自定义函数和内置函数

2.1 函数的创建

  • 格式

def 函数名(参数):
代码块
return 返回值

  • 要素

    • 函数代码块以 def 关键词开头,后接函数标识符名称和圆括号 ()。
    • 任何传入参数和自变量必须放在圆括号中间,圆括号之间可以用于定义参数。
    • 函数的第一行语句可以选择性地使用文档字符串—用于存放函数说明。
    • 函数内容以冒号起始,并且缩进。
    • return [表达式] 结束函数,选择性地返回一个值给调用方。不带表达式的return相当于返回 None。函数中如果执行了return,此函数将终止执行return后面的语句
  • 示例

    ```
    >>> def hello(arg) :
    print(arg) >>> hello("Hello World!")
    Hello World!
    >>>
    ```

    2.2 函数的参数

  • 形式参数,实际参数

    形式参数为函数名后边括号里定义的内容,实际参数是函数被调用时传入的参数
    如上面例子,arg 为hello函数的形式参数,而下面hellow()被调用时Hello World!为实际参数

  • 默认参数

    调用函数时,如果没有传递参数,则会使用默认参数。以下实例中如果没有传入 age 参数,则使用默认值:

    ```
    
    #!/usr/bin/python3
    
    #printinfo函数说明
    def printinfo( name, age = 35 ):
    "打印任何传入的字符串"
    print ("名字: ", name);
    print ("年龄: ", age);
    return; #调用printinfo函数
    printinfo( age=50, name="runoob" );
    print ("------------------------")
    printinfo( name="runoob" ); 以上实例输出结果:
    名字: runoob
    年龄: 50
    ------------------------
    名字: runoob
    年龄: 35
    ```
  • 指定参数

关键字参数和函数调用关系紧密,函数调用使用关键字参数来确定传入的参数值。使用关键字参数允许函数调用时参数的顺序与声明时不一致,因为 Python 解释器能够用参数名匹配参数值,如上默认参数中,name参数和age参数即为指定参数

  • 动态参数

    *arg 默认传入的参数放到元组中, 动态参数使用中,如果实际参数前加*号,会将参数的元素传入到参数中
    **kwargs默认传入的参数放入到字典中.如果实际参数前加*号,会将字典的k/v 传入到参数中
    万能参数: 即 *args,**kwargs

    ```
    #!/usr/bin/env python
    # -*- coding: UTF-8 -*-
    #pyversion:python3.5
    #owner:fuzj def send1(*args):
    print(args,type(args)) def send2(**args):
    print(args,type(args)) def send3(*args,**kwargs):
    print(args,type(args))
    print(kwargs,type(kwargs)) li = [1,2,3,4,]
    send1(1,2,3,4)
    send1(li)
    send1(*li) #参数前加*号会把该参数进行for循环里面的值,然后传入函数 di = {"a":1,"b":2}
    send2(k=di)
    send2(**di) send3(1,2,3,4,"a"=1,"b"=2) ```

执行结果

```
#函数send1
(1, 2, 3, 4) <class 'tuple'> #传递不定长参数的结果
([1, 2, 3, 4],) <class 'tuple'> #传入一个列表参数的结果
(1, 2, 3, 4) <class 'tuple'> #传入一个带*的列表参数结果 #函数send2
{'k': {'b': 2, 'a': 1}} <class 'dict'> #传入一个字典结果
{'b': 2, 'a': 1} <class 'dict'> #传入一个带**的字典结果 # 函数send3
(1, 2, 3, 4) <class 'tuple'>
{'b': 2, 'a': 1} <class 'dict'>
```

2.3 变量作用域

Pyhton 中,程序的变量并不是在哪个位置都可以访问的,访问权限决定于这个变量是在哪里赋值的。
变量的作用域决定了在哪一部分程序可以访问哪个特定的变量名称,根据作用域的不同,可分为:

  • 全局变量: 所有的作用域都可读,但是作用域内不能重新赋值全局变量,但是可以使用global来重新定义全局变量.如果变量的值是列表\元组\字典 在作用域内可以进行修改,不可重新赋值

  • 局部变量: 局部变量只能在其被声明的函数内部访问,而全局变量可以在整个程序范围内访问

定义在函数内部的变量拥有一个局部作用域,定义在函数外的拥有全局作用域。
局部变量只能在其被声明的函数内部访问,而全局变量可以在整个程序范围内访问。调用函数时,所有在函数内声明的变量名称都将被加入到作用域中。如下实例:

```
#!/usr/bin/env python total = 0; # 这是一个全局变量 # 可写函数说明
def sum( arg1, arg2 ):
#返回2个参数的和."
total = arg1 + arg2; # total在这里是局部变量.
print ("函数内是局部变量 : ", total)
return total; #调用sum函数
sum( 10, 20 );
print ("函数外是全局变量 : ", total)
以上实例输出结果:
函数内是局部变量 : 30
函数外是全局变量 : 0 ```

2.4 匿名函数

python 使用 lambda 来创建匿名函数。
所谓匿名,意即不再使用 def 语句这样标准的形式定义一个函数。
lambda 只是一个表达式,函数体比 def 简单很多。
lambda的主体是一个表达式,而不是一个代码块。仅仅能在lambda表达式中封装有限的逻辑进去。
lambda 函数拥有自己的命名空间,且不能访问自有参数列表之外或全局命名空间里的参数。
虽然lambda函数看起来只能写一行,却不等同于C或C++的内联函数,后者的目的是调用小函数时不占用栈内存从而增加运行效率。
语法
lambda 函数的语法只包含一个语句,如下:
lambda [arg1 [,arg2,.....argn]]:expression
如下实例:

```
#!/usr/bin/python3 # 可写函数说明
sum = lambda arg1, arg2: arg1 + arg2; # 调用sum函数
print ("相加后的值为 : ", sum( 10, 20 ))
print ("相加后的值为 : ", sum( 20, 20 ))
以上实例输出结果:
相加后的值为 : 30
相加后的值为 : 40 ```

3.文件操作

3.1 open函数

python的内置函数open()可以打开文件,创建一个file对象,格式如下:

file object = open(file_name [, access_mode][, buffering][,encoding=utf-8])

各个参数的细节如下:

  • file_name:file_name变量是一个包含了你要访问的文件名称的字符串值。
  • access_mode:access_mode决定了打开文件的模式:只读,写入,追加等。所有可取值见如下的完全列表。这个参数是非强制的,默认文件访问模式为只读(r)。
  • buffering:如果buffering的值被设为0,就不会有寄存。如果buffering的值取1,访问文件时会寄存行。如果将buffering的值设为大于1的整数,表明了这就是的寄存区的缓冲大小。如果取负值,寄存区的缓冲大小则为系统默认
  • encoding = utf-8 表示使用utf-8字符集打开

3.2 文件打开模式

模式 描述
r 以只读方式打开文件。文件的指针将会放在文件的开头。这是默认模式。
rb 以二进制格式打开一个文件用于只读。文件指针将会放在文件的开头。这是默认模式。
r+ 打开一个文件用于读写。文件指针将会放在文件的开头
rb+ 以二进制格式打开一个文件用于读写。文件指针将会放在文件的开头。
w 打开一个文件只用于写入。如果该文件已存在则将其覆盖。如果该文件不存在,创建新文件。
wb 以二进制格式打开一个文件只用于写入。如果该文件已存在则将其覆盖。如果该文件不存在,创建新文件。
w+ 打开一个文件用于读写。如果该文件已存在则将其覆盖。如果该文件不存在,创建新文件。
wb+ 以二进制格式打开一个文件用于读写。如果该文件已存在则将其覆盖。如果该文件不存在,创建新文件。
a 打开一个文件用于追加。如果该文件已存在,文件指针将会放在文件的结尾。也就是说,新的内容将会被写入到已有内容之后。如果该文件不存在,创建新文件进行写入。
ab 以二进制格式打开一个文件用于追加。如果该文件已存在,文件指针将会放在文件的结尾。也就是说,新的内容将会被写入到已有内容之后。如果该文件不存在,创建新文件进行写入。
a+ 打开一个文件用于读写。如果该文件已存在,文件指针将会放在文件的结尾。文件打开时会是追加模式。如果该文件不存在,创建新文件用于读写。
ab+ 以二进制格式打开一个文件用于追加。如果该文件已存在,文件指针将会放在文件的结尾。如果该文件不存在,创建新文件用于读写。

PS:以b方式打开时,读取到的内容是字节类型,写入时也需要提供字节类型,否则使用读到的是字符串,写入时也按字符串

3.3 file对象的属性

  • 文件被打开之后,此文件对象的属性有如下几个:
属性 描述
file.closed 返回true如果文件已被关闭,否则返回false。
file.mode 返回被打开文件的访问模式。
file.name 返回文件的名称。
file.softspace 如果用print输出后,必须跟一个空格符,则返回false。否则返回true
file.encoding 返回打开文件时使用的字符集
  • 示例

    ```
    >>> fp = open("test",'r')
    >>> fp.name #显示打开的文件名
    'test'
    >>> fp.closed #判断文件是否被关闭
    False
    >>> fp.mode #输出文件打开的模式
    'r'
    >>> fp.softspace #python3不支持此属性
    Traceback (most recent call last):
    File "<stdin>", line 1, in <module>
    AttributeError: '_io.TextIOWrapper' object has no attribute 'softspace'
    ```

3.4 file对象的常用操作函数

  • seek() 调整当前的指针位置,以字节为单位进行调整.从指针位置开始后面会依次覆盖
  • tell() 获取当前指针位置
  • read() 默认读取一个打开的文件中所有字符串。需要重点注意的是,Python字符串可以是二进制数据,而不是仅仅是文字
  • write() 写数据
  • close() 关闭打开的文件
  • fileno 获取文件描述符
  • flush() 强制将缓冲区内容写到硬盘
  • readable() 是否可读
  • writeable() 是否可写
  • readline() 仅读取一行
  • truncate() 截断指针位置.后面内容清空
  • readlines() 读取文件内容,将每行作为列表的元素,最终组合成一个列表

PS:使用输入方法read() 或者 readlines() 从文件中读取行时,python并不会删除行结尾符
tell()方法告诉你文件内的当前位置;换句话说,下一次的读写会发生在文件开头这么多字节之后。
seek(offset [,whence])方法改变当前文件的位置。Offset变量表示要移动的字节数。whence变量指定开始移动字节的参考位置。
如果whence被设为0,这意味着将文件的开头作为移动字节的参考位置。如果设为1,则使用当前的位置作为参考位置。如果它被设为2,那么该文件的末尾将作为参考位置。注意:python3中已经不允许非二进制打开的文件,相对于文件末尾和当前位置的定位,指针只能从文件开头开始。python2没有限制

示例:
test 文件内容:
fuzengjie付增杰
付增杰abcdefghjkl
脚本:初衷是在test文件的第21个字符后面,指针偏移4位,插入123

 ```
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
#pyversion:python3.5
#owner:fuzj
fp = open("test","r+") #使用r+方式打开test
rs = fp.read(21) #读test第21个字符以内的所有字符,包括第21个以及中间换行。如果是字符
print("文件内容: ",rs) #打印读取的内容 ad = fp.tell() #打印当前的指针位置
print("当前指针位置: ",ad)
se = fp.seek(4,1)
fp.write("") #写入123
fp.close() ```

使用py3运行后会报错
io.UnsupportedOperation: can't do nonzero cur-relative seeks。
如果将fp.seek(4,1)改为fp.seek(4,0)从文件开头插入,则没问题。
如果使用py2执行,则不会出现上述问题。
可以先获取当前文件末尾的指针位置,然后再进行移动,
fp.seek(fp.tell(),0),这样就可以将指针定位到末尾
现在换一种方法,使用二进制打开文件,然后进行插入
代码:

```
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
#pyversion:python3.5
#owner:fuzj
fp = open("test","rb+") #使用二进制方式打开
rs = fp.read()
print("文件内容: ",str(rs,encoding='utf-8')) #由于对象是二进制,所以需要使用str()来转换成字符串 ad = fp.tell()
print("当前指针位置: ",ad)
se = fp.seek(-4,1) #在当前位置向左移动四个指针
print("改变后的指针位置:", fp.tell())
fp.write(bytes("",encoding='utf-8'))
```
运行结果:
```
文件内容: fuzengjie付增杰
付增杰abcdefg123l
当前指针位置: 39
改变后的指针位置: 35 test文件
fuzengjie付增杰
付增杰abcdefg123l #指针移动四位到h字符串,然后使用123开始往后替换,最后l字符串没得替换,保留了。 ```

注意:一个汉字如果使用utf-8编码集的话,会占3个字节,如果使用gbk编码集的话,会占用2个字节。

  • 文件打开的另一种方法:

使用with 打开文件,操作完之后,自动关闭

with open("test",'r') as f:    #打开一个文件
pass #同时打开两个文件
with open("test1",'r') as f1, open("test2",'r') as f2:
pass