第三周笔记
一:集合
python的set和其他语言类似, 是一个无序不重复元素集, 基本功能包括关系测试和消除重复元素. 集合对象还支持union(联合), intersection(交), difference(差)和sysmmetric difference(对称差集)等数学运算.
sets 支持 x in set, len(set),和 for x in set。作为一个无序的集合,sets不记录元素位置或者插入点。因此,sets不支持 indexing, slicing, 或其它类序列(sequence-like)的操作。
下面来点简单的小例子说明把。
>>> x = set('spam')
>>> y = set(['h','a','m'])
>>> x, y
(set(['a', 'p', 's', 'm']), set(['a', 'h', 'm']))
再来些小应用。
>>> x & y # 交集
set(['a', 'm'])
>>> x | y # 并集
set(['a', 'p', 's', 'h', 'm'])
>>> x - y # 差集
set(['p', 's'])
记得以前个网友提问怎么去除海量列表里重复元素,用hash来解决也行,只不过感觉在性能上不是很高,用set解决还是很不错的,示例如下:
>>> a = [11,22,33,44,11,22]
>>> b = set(a)
>>> b
set([33, 11, 44, 22])
>>> c = [i for i in b]
>>> c
[33, 11, 44, 22]
很酷把,几行就可以搞定。
1.8 集合
集合用于包含一组无序的对象。要创建集合,可使用set()函数并像下面这样提供一系列的项:
s = set([3,5,9,10]) #创建一个数值集合
t = set("Hello") #创建一个唯一字符的集合
与列表和元组不同,集合是无序的,也无法通过数字进行索引。此外,集合中的元素不能重复。例如,如果检查前面代码中t集合的值,结果会是:
>>> t
set(['H', 'e', 'l', 'o'])
注意只出现了一个'l'。
集合支持一系列标准操作,包括并集、交集、差集和对称差集,例如:
a = t | s # t 和 s的并集
b = t & s # t 和 s的交集
c = t – s # 求差集(项在t中,但不在s中)
d = t ^ s # 对称差集(项在t或s中,但不会同时出现在二者中)
基本操作:
t.add('x') # 添加一项
s.update([10,37,42]) # 在s中添加多项
使用remove()可以删除一项:
t.remove('H')
len(s)
set 的长度
x in s
测试 x 是否是 s 的成员
x not in s
测试 x 是否不是 s 的成员
s.issubset(t)
s <= t
测试是否 s 中的每一个元素都在 t 中
s.issuperset(t)
s >= t
测试是否 t 中的每一个元素都在 s 中
s.union(t)
s | t
返回一个新的 set 包含 s 和 t 中的每一个元素
s.intersection(t)
s & t
返回一个新的 set 包含 s 和 t 中的公共元素
s.difference(t)
s - t
返回一个新的 set 包含 s 中有但是 t 中没有的元素
s.symmetric_difference(t)
s ^ t
返回一个新的 set 包含 s 和 t 中不重复的元素
s.copy()
返回 set “s”的一个浅复制
请注意:union(), intersection(), difference() 和 symmetric_difference() 的非运算符(non-operator,就是形如 s.union()这样的)版本将会接受任何 iterable 作为参数。相反,它们的运算符版本(operator based counterparts)要求参数必须是 sets。这样可以避免潜在的错误,如:为了更可读而使用 set('abc') & 'cbs' 来替代 set('abc').intersection('cbs')。从 2.3.1 版本中做的更改:以前所有参数都必须是 sets。
另外,Set 和 ImmutableSet 两者都支持 set 与 set 之间的比较。两个 sets 在也只有在这种情况下是相等的:每一个 set 中的元素都是另一个中的元素(二者互为subset)。一个 set 比另一个 set 小,只有在第一个 set 是第二个 set 的 subset 时(是一个 subset,但是并不相等)。一个 set 比另一个 set 打,只有在第一个 set 是第二个 set 的 superset 时(是一个 superset,但是并不相等)。
子 set 和相等比较并不产生完整的排序功能。例如:任意两个 sets 都不相等也不互为子 set,因此以下的运算都会返回 False:a<b, a==b, 或者a>b。因此,sets 不提供 __cmp__ 方法。
因为 sets 只定义了部分排序功能(subset 关系),list.sort() 方法的输出对于 sets 的列表没有定义。
运算符
运算结果
hash(s)
返回 s 的 hash 值
下面这个表列出了对于 Set 可用二对于 ImmutableSet 不可用的运算:
运算符(voperator)
等价于
运算结果
s.update(t)
s |= t
返回增加了 set “t”中元素后的 set “s”
s.intersection_update(t)
s &= t
返回只保留含有 set “t”中元素的 set “s”
s.difference_update(t)
s -= t
返回删除了 set “t”中含有的元素后的 set “s”
s.symmetric_difference_update(t)
s ^= t
返回含有 set “t”或者 set “s”中有而不是两者都有的元素的 set “s”
s.add(x)
向 set “s”中增加元素 x
s.remove(x)
从 set “s”中删除元素 x, 如果不存在则引发 KeyError
s.discard(x)
如果在 set “s”中存在元素 x, 则删除
s.pop()
删除并且返回 set “s”中的一个不确定的元素, 如果为空则引发 KeyError
s.clear()
删除 set “s”中的所有元素
请注意:非运算符版本的 update(), intersection_update(), difference_update()和symmetric_difference_update()将会接受任意 iterable 作为参数。从 2.3.1 版本做的更改:以前所有参数都必须是 sets。
还请注意:这个模块还包含一个 union_update() 方法,它是 update() 方法的一个别名。包含这个方法是为了向后兼容。程序员们应该多使用 update() 方法,因为这个方法也被内置的 set() 和 frozenset() 类型支持。
二:文件操作
对文件进行操作的流程:
第一,打开文件,得到文件句柄并复制给一个变量。
第二,打开句柄对文件进行操作。
第三,关闭文件。
(1)r模式
在只读模式下写入内容会报错。
1 f = open('file1','r') 2 f_read = f.read() #read是逐字符地读取,read可以指定参数,设定需要读取多少字符,无论一个英文字母还是一个汉字都是一个字符。 3 print(f_read) 4 f.close()
1 f = open('file1','r') 2 f_read = f.readline() #readline只能读取第一行代码,原理是读取到第一个换行符就停止。 3 print(f_read) 4 f.close()
1 f = open('file1','r') 2 f_read = f.readlines() #readlines会把内容以列表的形式输出。 3 print(f_read) 4 f.close()
1 f = open('file1','r') 2 for line in f.readlines() #使用for循环可以把内容按字符串输出。 3 print(line) #输出一行内容输出一个空行,一行内容一行空格... 因为文件中每行内容后面都有一个换行符,而且print()语句本身就可以换行,如果不想输出空行,就需要使用下面的语句:print(line.strip()) 4 f.close()
(2)w模式
在进行操作前,文件中所有内容会被清空。比如在file1中写入'hello world',程序执行后file1中就只剩下一句'hello world'
1 f = open('file1','w',encoding='utf8') #由于Python3的默认编码方式是Unicode,所以在写入文件的时候需要调用utf8,以utf8的方式保存,这时pycharm(默认编码方式是utf8)才能正确读取,当读取文件时,文件是utf8格式,pycharm也是utf8,就不需要调用了。 2 f_w = f.write('hello world') 3 print(f_w) #有意思的是,这里并不打印'hello world',只打印写入多少字符 4 f.close()
(3)a模式
与w模式不同的是,a模式不会把原来内容清空,而是光标移到内容最后位置,继续写入新内容。比如在最后追加'hello world'
f = open('file1','a') f_a = f.write('hello world') print(f_a) #还是会打印写入的字符数 f.close()
在r模式时,我们说过用for循环和readlines()输出文件内容,这种输出内容的原理是:打开文件,把全部内容读入内存,然后再打印输入,当文件很大时,这种读取方式就不靠谱了,甚至会使机器崩溃。我们需要及时关闭文件,如下:
1 f = open('file','r') 2 data=f.readlines() #注意及时关闭文件 3 f.close() 4 5 num = 0 6 for i in data: 7 num += 1 8 if num == 5: 9 i = ''.join([i.strip(),'hello world']) #不要使用“+”进行拼接 10 print(i.strip()) 11 f.close()
对于大数据文件,要使用下面的方法:
1 num = 0 2 f.close() #不要过早关闭文件,否则程序不能识别操作句柄f. 3 f = open('file','r') 4 for i in f: #for内部把f变为一个迭代器,用一行取一行。 5 num += 1 6 if num == 5: 7 i = ''.join([i.strip(),'hello world']) 8 print(i.strip()) 9 f.close()
3.tell和seek
tell:查询文件中光标位置
seek:光标定位
1 f = open('file','r') 2 print(f.tell()) #光标默认在起始位置 3 f.seek(10) #把光标定位到第10个字符之后 4 print(f.tell()) #输出10 5 f.close() 6 ---------------------- 7 f = open('file','w') 8 print(f.tell()) #先清空内容,光标回到0位置 9 f.seek(10) 10 print(f.tell()) 11 f.close() 12 ---------------------- 13 f = open('file','a') 14 print(f.tell()) #光标默认在最后位置 15 f.write('你好 世界') 16 print(f.tell()) #光标向后9个字符,仍在最后位置 17 f.close()
4.flush 同步将数据从缓存转移到磁盘
示例,实现进度条功能
1 import sys,time #导入sys和time模块 2 for i in range(40): 3 sys.stdout.write('*') 4 sys.stdout.flush() #flush的作用相当于照相,拍一张冲洗一张 5 time.sleep(0.2) 6 下面代码也能够实现相同的功能 7 import time 8 for i in range(40): 9 print('*',end='',flush=True) #print中的flush参数 10 time.sleep(0.2)
5.truncate 截断
不能是r模式下执行,
w模式下,已经清空所有数据,使用truncate
没有任何意义,
a模式下,截断指定位置后的内容。
1 f = open('file','a') 2 f.truncate(6) #只显示6个字节的内容(6个英文字符或三个汉字),后面的内容被清空。
6.光标位置总结
一个汉字两个字节,涉及光标位置的方法有4个:read
、tell
、seek
、truncate
。
1 #--------------------------光标总结head----------------------------------- 2 f = open('file','r') 3 print(f.read(6)) #6个字符 4 print(f.tell()) #位置12字节,一个汉字两个字节,按字符计数 5 f.close() 6 7 f = open('file','r') 8 f.seek(6) #6个字节 9 print(f.tell()) 10 f.close() 11 12 f = open('file','a') 13 print(f.tell()) #光标默认在最后位置 14 f.write('你好 世界') 15 print(f.tell()) #光标向后9个字节,一个汉字两个字节,仍在最后位置 182-->191 16 f.close() 17 18 f = open('file','a',encoding='utf-8') 19 print(f.truncate(6)) #由于需要光标定位位置,所以也是字节。只显示6个字节的内容(6个英文字母或三个汉字,一个汉字两个字节),后面的内容被清空。 20 f.close() 21 #-----------------------------光标总结end---------------------------------
7.另外3种模式:r+、w+、a+
r+:读写模式,光标默认在起始位置,当需要写入的时候,光标自动移到最后
w+:写读模式,先清空原内容,再写入,也能够读取
a+:追加读模式,光标默认在最后位置,直接写入,也能够读取。
1 f = open('file','a') 2 print(f.tell()) #末尾207位置 3 f.close() 4 5 f = open('file','r+') 6 print(f.tell()) #0位置 7 print(f.readline()) #读取第一行 8 f.write('羊小羚') #光标移到末尾207位置并写入 9 print(f.tell()) #213位置 10 f.seek(0) #光标移到0位置 11 print(f.readline()) #读取第一行 12 f.close()
8.修改文件内容
思路:由于数据存储机制的关系,我们只能把文件1中的内容读取出来,经过修改后,放到文件2中。
1 f2 = open('file2','w',encoding='utf8') #写入的时候必须加utf8 2 f1 = open('file','r') 3 num = 0 4 for line in f1: #迭代器 5 num += 1 6 if num == 5: 7 line = ''.join([line.strip(),'羊小羚\n']) #里面就是对字符串进行操作了 8 f2.write(line) 9 f1.close() 10 f2.close()
9.with语句
可以同时对多个文件同时操作,当with
代码块执行完毕时,会自动关闭文件释放内存资源,不用特意加f.close()
,我们通过下面的示例体会with
的用法和好处。
用with
语句重写8中的代码
1 num = 0 2 with open('file','r') as f1,open('file2','w',encoding='utf8') as f2: 3 for line in f1: 4 num += 1 5 if num == 5: 6 line = ''.join([line.strip(),'羊小羚']) 7 f2.write(line)
三、函数
函数能提高应用的模块性,和代码的重复利用率。Python提供了许多内建函数,比如print()等。也可以创建用户自定义函数。
函数定义
函数定义的简单规则:
函数代码块以def关键词开头,后接函数标识符名称和圆括号(),任何传入参数和自变量必须放在圆括号中间
函数内容以冒号起始,并且缩进
若有返回值,Return[expression] 结束函数;不带return 表达式相当于返回None
函数通常使用三个单引号 '''...''' 来注释说明函数;函数体内容不可为空,可用 pass 来表示空语句;以下几个为简单的函数示例:
1 ''' some basic functions ''' 2 def func1(): # 函数无传入参数 3 print("func1") # 无return值 4 func1() # 函数调用 5 6 def func2(): 7 return("func2") # return 字符串 "func2" 8 print(func2()) 9 10 def func3(a,b): # 需传两个参数 11 print("a+b = %d" %(a+b)) # print表达式,无return 12 func3(3,4) 13 14 def func4(a,b): # 需传两个参数 15 return (a+b) # return a+b 的值 16 print(func4(4,3))
函数调用
定义一个函数只给了函数一个名称,指定了函数里包含的参数,和代码块结构。
这个函数的基本结构完成以后,你可以通过另一个函数调用执行,也可以直接从Python提示符执行。
上面的例子中 func1() 就是无参数函数的调用; func3(3,4) 为有参数函数的调用
深入函数定义
默认参数值
最常用的一种形式是为一个或过个参数指定默认值。调用时,可不传入有默认值的参数。参考下例:
1 ''' advanced: 简单询问框 ''' 2 def ask_ok(hint, retries=4, complaint='Yes or no, please!'): # 仅有hint是必须要传入的,retries 和 complaint 均有默认值 3 while True: 4 u = input(hint) 5 if u in ('y','ye','yes'): # in 的用法;若用户回答在('y','ye','yes') return True 6 return True 7 if u in ('n','no','nop','nope'): # 若用户回答在('n','no','nop','nope') return False 8 return False 9 retries = retries -1 # 若用户输入不在之前所列,可重试,重试次数-1 10 if retries <= 0 : # 若超出重试次数,raise自定义Error 11 raise IOError('refusenik user') 12 print(complaint) # 若用户输入不在之前所列,提示 complaint 信息 13 14 result1 = ask_ok("Yes or No?") # 只给必要的参数值hint,可尝试输入'y' 'no' 等;输入其他的如 'x' 超过4次 15 print(result1) # 查看return的值 16 17 # result2 = ask_ok("Yes or No?",2) # 给出retries=2,尝试输入其他的如 'x' 超过2次 18 19 # result3 = ask_ok("Yes or No?",'Y or N?') # 不可只省略第二个参数,若尝试输入其他的如 'x',会报错 20 21 # result4 = ask_ok("Yes or No?",3,'Y or N?') # 给出所有的参数,可尝试输入'y' 'no' 等;输入其他的如 'x' 超过3次 22 # print(result4)
注意:默认值是在函数定义作用域被解析的,如下所示
1 '''默认值是在函数定义作用域被解析的''' 2 i = 5 3 def print_i(var=i): 4 print(var) 5 i = 6 6 print_i() # 输出为5
重要警告:默认值只被赋值一次。这使得当默认值是可变对象时会有所不同,如列表、字典或大多数类的实例。如下例,函数在后续调用过程中会累积之前传给它的参数。
1 ''' 默认值只被赋值一次。这使得当默认值是可变对象时会有所不同,如列表、字典或大多数类的实例。 2 函数在后续调用过程中会累积之前传给它的参数。 3 ''' 4 def append_L(a,L=[]): # 必须传参a,L不必须,为list,默认为空 5 L.append(a) 6 return L 7 print(append_L(1)) # 给出参数 a=1, 此时 L 已变为 [1] 8 print(append_L(2)) # 输出 [1,2] 9 print(append_L(3)) # 输出 [1,2,3] 10 11 ''' L缺省时,做改变L,而不是累积值,可像下方这样定义函数 ''' 12 def change_L(a,L=None): 13 if L is None: 14 L = [] 15 L.append(a) 16 return L 17 print(change_L(1)) # 给出参数 a=1, L为None 18 print(change_L(2)) # 给出参数 a=2, L为None 输出 [2] 19 print(change_L(3,[0])) # 给出参数 a=1, L=[0] 输出 [0,3]
关键字参数
上面的例子中,调用函数给出的参数都是根据定义的顺序来的。函数还可以根据 关键字函数 的形式来调用,参见下面的示例:
1 def add(a,b): 2 return (a+b) 3 print(add(b=9,a=2)) # 关键字参数定义 b=9 , a=2 与传参顺序无关 4 # print(add(b=9,2)) # 会报错
可变参数列表
可以让函数调用可变个数的参数(不常用),这些参数被包装进一个元组。在这些可变个数的参数之前,可以有零到多个普通的参数。
可变参数的表示为在其参数名前加*,如*args;参见下面的示例:
1 def join_bysep(*strs,sep): # strs 可为多个参数 2 return sep.join(strs) # 字符串连接函数 sep.join(str) 3 print(join_bysep("red","blue","green",sep=" ")) 4 print(join_bysep("red","blue",sep=",")) 5 print(join_bysep("red",sep=",")) 6 print(join_bysep(sep=",")) # 无strs传参,为一空的字符串
python3 函数的形参、实参、位置参数、默认参数、关键字参数以及函数的递归
#_*_ coding:utf-8 _*_
def func(x,y=2):
print('我是形参%s'%x)
print('我是默认参y--->%s'%y)
func(1)
#1是实参,x为形参,y为默认参数
#若实参数目不固定
def func1(*args):
print(args)
func1([1,2.3,4,5])
#([1, 2.3, 4, 5],)
func1(*[1,2,3,4,5])
#(1, 2, 3, 4, 5)
#实参为字典 **kwargs把N个关键字参数转化为字典输出
def func2(**kwargs):
print(kwargs)
print(kwargs['name'])
print(kwargs['age'])
print(kwargs['salary'])
func2(name='Andy',age=20,salary=10000,job='doctor')
def func3(name,age=20,**kwargs):
print(name)
print(age)
print(kwargs)
func3('Andy')
#Andy
#{}
#默认参数赋值方式:1、位置参数赋值 2、关键字赋值
func3('Andy',salary=10000,age=21)
func3('Andy',21,salary=10000)
#*args接受N个位置参数,转化成元组;
#**kwargs接收N个关键字参数,转为字典
#位置参数必须放在关键字参数前面
# def func4(name,age=20,*args,**kwargs):
# print(name)
# print(age)
# print(args)
# print(kwargs)
# logger('test')
# func4('Andy',21,32,salary=10000,job='doctor')
# def logger(source):
# print('from %s'%source)
#局部变量(只在函数体内作用的变量)
def changename(name):
print('before change',name)
name='John'
age=23
print('after change',name)
name='Andy'
changename(name)
print(name)
# before change Andy
# after change John
# Andy
#全局变量(整个程序都可以用的变量,定义在函数体外)若修改全局变量,需在函数体内用global 重新定义变量 ***函数体内不要修改
# 全局变量,且函数体内不要定义全局变量
def func5():
global name
name='andy'
print(name)
func5()
print(name)
#全局变量(整个程序都可以用的变量,定义在函数体外)若修改全局变量,需在函数体内用global 重新定义变量 ***函数体内不要修改
# 全局变量,且函数体内不要定义全局变量
#列表、字典、集合、类作为全局变量时,函数体内可以修改
#字符串、数字作为全局变量时,函数体内无法修改
#函数的递归
def calc(n):
print(n)
if int(n/2) >0:
return calc(int(n/2))
print(n)
calc(10)