复制 title: python基础
date: 2023-07-03 23:16:34
author: akkk
tags:
- python
categories:
- python
- 基础
password:
f85e1b8e3f2477665d3bbd863be4adb2
基础数据类型
数字(number)
整数 (int,long):整数在3版本没有大小长度分别,内存决定整数最大长度 浮点数 (float):具有小数点的数,无穷小数会做精度处理 布尔 (bool):非空非0为真,0或空为假 复数 (complex):复数的标志为虚部以大写 "J" 或小写 "j" 结尾
复制 >>> a = 1 #int
>>> a = 1.5 #float
>>> a = True #bool
>>> a = 2+3j #complex
字符串(str)
表示方式:使用单引号 'abc' ; 双引号 "abc" ; 或者这样 '''abc''' , """abc""",单个字符也称作字符串
索引:
使用index选择访问位置,索引从左向右,从0开始,从右向左,从-1开始
index 也是我们经常称呼的下标,下标值不可大于等于字符串最大长度
切片:
str[start:stop:[step…]],切片可以获取字符串上一定区间中的值
从start
开始,取到stop
为止数据,步长为step
复制 >>> mystr = '' #创建空字符串
>>> mystr = "a" #单个字符同样为字符串
>>> mystr = 'abcdefg'
>>> mystr[0] #索引值为正时,顺序从左向右取对应位置数据
'a'
>>> mystr[-1] #索引值为负时,顺序从右边向左取对应位置数据
'g'
>>> mystr[:] #未给出开始,结束索引;选择整个列表范围内的值
'abcdefg'
>>> mystr[:2] #切片选择0-2范围内的值,不包含索引为2的值
'ab'
>>> mystr[-3:] #切片选择从-3到最后位置的值
'efg'
>>> mystr[0:4:2] #切片选择取索引0-4范围内的值(不包含索引4),并且步长为2
'ac'
>>> mystr[::-1] #切片选择整个索引范围内的值,步长为负值时,为逆序
'gfedcba'
>>> mystr[0] = 1 #当对字符串数据对象进行值修改,报错
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'str' object does not support item assignment
列表(list)
表示方式:使用中括号,逗号分隔每个数据元素:[ 'a', 'b' , 'c' ]
复制 >>> mylist = [] #创建空列表
>>> mylist = [1,2,3,4]
>>> mylist[0] #索引值为正时,顺序从左向右取对应位置数据
1
>>> mylist[-1] #索引值为负时,顺序从右边向左取对应位置数据
4
>>> mylist[:] #未给出开始,结束索引;选择整个列表范围内的值
[1,2,3,4]
>>> mylist[:2] #切片选择0-2范围内的值,不包含索引为2的值
[1,2]
>>> mylist[-3:] #切片选择从-3到最后位置的值
[2,3,4]
>>> mylist[0:4:2] #切片选择取索引0-4范围内的值(不包含索引4),并且步长为2
[1,3]
>>> mylist[::-1] #切片选择整个索引范围内的值,步长为负值时,为逆序
[4,3,2,1]
>>> mylist[0] = 'a' #修改列表第一个位置上的值为字母'a'
>>> mylist
['a',2,3,4]#列表为可变数据对象
元组(tuple)
元组和列表类似,不同之处在于元组内数据不可以被修改
表示方式:使用小括号,逗号分隔每个数据元素:( 'a', 'b', 'c' )
单纯的创建一个只含有一个元素的元组是会被解释器认为是一个实际数据对象,并不解释成元组
复制 >>> mytuple = (1,) #正确方式创建只含有一个数据的元组
>>> mytuple
(1,)#返回值为元组
>>> mytuple = (1) #错误方式创建只含有一个数据的元组
1#返回值为整型
>>> mytuple = (1, 2, 3, 4, 5)
>>> mytuple[0]
1
>>> mytuple[-1] #索引值为负时,顺序从右边向左取对应位置数据
4
>>> mytuple[:] #未给出开始,结束索引;选择整个列表范围内的值
(1, 2, 3, 4, 5)
>>> mytuple[:2] #切片选择0-2范围内的值,不包含索引为2的值
(1, 2)
>>> mytuple[-3:] #切片选择从-3到最后位置的值
(3, 4, 5)
>>> mytuple[0:4:2] #切片选择取索引0-4范围内的值(不包含索引4),并且步长为2
(1, 3)
>>> mytuple[::-1] #切片选择整个索引范围内的值,步长为负值时,为逆序
(5, 4, 3, 2, 1)
>>> mytuple[0] = 'a' #当对元组数据对象进行值修改,报错
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'tuple' object does not support item assignment
运算表达式
算数表达式
Python2.x : 除法结果只会向复杂数据类型靠拢,两个整数 相除,是否除净结果都为整数 。
Python3.x : 除法中无论有无复杂数据类型,结果都将精确到浮点数
复制 #python2.x
>>> 5 / 3 #2中两整数做除法,结果还是整数
1 #结果为整数
>>> 5 / 3.0
1.6666666666666667 #只有在参与运算的数据类型中含有浮点数,结果才会成为浮点数
#python3.x
>>> 5 / 3
1.6666666666666667 #3中两整数做除法,结果会精确到浮点数
>>> 5 / 3.0
1.6666666666666667
复制 >>> 5 // 3
1
>>> 5 % 3
2
>>> 5 ** 3
125
除此之外,我们还可以使用round函数来控制返回结果的精度
复制 >>> round(5 / 3) #可以获取到5/3的四舍五入结果
2
>>> round(5 / 3, 2) #控制返回结果的精度为2
1.67
逻辑表达式
复制 >>> a = 1
>>> not a #a的逻辑非
False
>>> b = 0
>>> a and b #a和b的逻辑与
0
>>> a or b #a和b的逻辑或
1
is逻辑语句判断的是否为同一个对象,首先需要值相同,其次数据地址相同
复制 >>> a1 = [1,2,3,4,5]
>>> a2 = [1,2,3,4,5]
>>> a1 is a2 #a1和a2值虽相同,但并不是同一个数据对象
False
>>> id(a1)
2298768048584
>>> id(a2) #两数据地址并不相同
2298768048392
关系表达式
复制 >>> a = 1
>>> b = 2
>>> a == b
False
>>> a != b
True
>>> a > b
False
>>> a < b
True
>>> a <= b
True
>>> a >= b
False
位运算
二进制
计算机中真正存储的二进制为补码;位运算都是补码在进行运算 正数的原码,反码,补码都是其本身
这里举一个小例子 :
结果解释:
首先位运算都是补码在进行运算
1的补码为 : 0000 0001
首先按位取反的结果 : 0000 0001 > 1111 1110
1111 1110 为补码,对其求原码
将该补码看作原码,重新求出的补码即可
1111 1110 原码
1000 0001 反码 (最高位为1,为负数求码过程,符号伪不变,其余各位取反)
1000 0010 补码 (反码加一)
此时我们得到的1000 0010 则是-2的原码,所以~a的结果为-2
位运算小例子
实现两个变量值的替换 如 a = 1 , b = 2 ; 替换后结果为 a = 2 , b = 1;不允许出现第三个变量
复制 #a: 0000 0001 1
#b: 0000 0010 2
>>> a = 1
>>> b = 2
>>> a = a | b #按位或
#a: 0000 0011 3
#b: 0000 0010 2
>>> b = a ^ b #按位异或
#a: 0000 0011 3
#b: 0000 0001 1
>>> a = a ^ b #按位异或
#a: 0000 0010 2
#b: 0000 0001 1
>>> a
2
>>> b
1
变量探究
变量不需先定义在使用,我们可以直接给变量名赋值,直接使用
复制 >>> a = 1 #这里我们可以直接给a变量赋值为1,并没有提前对a进行声明
复制 >>> a = 1
>>> a = 'abc' #对a重复复制并不会导致报错
复制 >>> a, b = 1, 2
>>> a
1
>>> b
2
变量名遵循C语言风格,变量名可以由字母、数字、下划线组成,数字不可以打头,大小写敏感
复制 >>> a1 = 1 #Success
>>> _a = 1 #Success
>>> 1a = 1 #Error
File "<stdin>", line 1
1a = 1
^
SyntaxError: invalid syntax
复制 >>> a = 1
>>> ++a #这里的++a并不会报错,是因为+号被解释成了正号,正的正一等于1
>>> a
1 #结果同样可以看出a并没有实现自增
>>> --a #与++a同理,此时为负的负一
>>> a++ #后++报错
File "<stdin>", line 1
a++
^
SyntaxError: invalid syntax
>>> a--
File "<stdin>", line 1
a --
^
SyntaxError: invalid syntax
引用计数
Python中,相同数据的赋值,会共享同一片空间地址,并非占用一个新的地址单元
为了记录当前使用这个地址的变量有多少,引出了引用计数 这个概念
我们可以使用del语句对一个数据的引用计数进行减1的操作
当引用计数最后为0时,这个数据占用的内存地址最终释放
我们可以使用sys模块下的getrefcount(value)函数进行变量value引用计数的查看
复制 >>> a = 1
>>> id(a) #可以使用id函数查看当前变量值所处内存单元
1951315008
>>> b = a
>>> id(b)
1951315008
>>> import sys
>>> sys.getrefcount(a)
127 #当前有127个变量使用1这个值
>>> sys.getrefcount(b) #可以看到两个变量的引用计数相同
127
>>> del b #删除掉b
>>> sys.getrefcount(a)
126 #1的引用计数减少为126
这样做的好处在于可以节约内存,防止产生多余的内存碎片
字典&集合
字典
字典以键值对形式存在:{ key: value }
其中key值会进行hash运算,生成hash表,所以字典的访问效率 要优于 普通序列数据类型(列表,元组)
key值由于需要通过hash,则一些可变数据类型不可以 做key值,而value的类型没有限制,可以是任意数据类型
由于进行了hash运算,那么在内存中字典其中的数据元素是无序 的,没有办法进行一些像索引 和切片 一样的操作 (但是在Python3.6后 ,字典数据类型已经被更改为有序数据类型 ;查阅地址 ;(from collections import OrderedDict ))
创建方式
大括号包裹键值对:mydict = {1:'a' , 2:'b' }
复制 >>> mydict = {1:'a',2:'b'}
>>> mydict
{1: 'a', 2: 'b'}
工厂方法创建:mydict = dict(([1,'a'],[2,'b']))
复制 >>> mydict = dict(([1,'a'],[2,'b']))
>>> mydict
{1: 'a', 2: 'b'}
字典内建方法:mydict = dict.fromkeys([1,2],'a')
复制 >>> mydict = dict.fromkeys([1,2],'a')
>>> mydict
{1: 'a', 2: 'a'}
#这样可以批量创建key值,但是缺点也暴露出来无法单独分配value值
访问字典
复制 >>> mydict = {1:'a',2:'b'}
>>> mydict.keys()
dict_keys([1, 2])
复制 >>> mydict = {1:'a',2:'b'}
>>> mydict.values()
dict_values(['a', 'b'])
通过key值获取value: dict[key]
,这个操作类似索引和切片,但实际为字典访问 value
值,注意不能混淆
注意 :在访问一个不存在的key值会引发KeyError的错误
复制 >>> mydict = {1:'a',2:'b'}
>>> mydict[1]
'a'
>>> mydict[2]
'b'
>>> mydict[3]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
KeyError: 3
通过get方法获取,获取不到返回none或者get方法所传递的参数默认值
复制 dict.get('key',default)
迭代 获取其中key值:for key in dict: 迭代访问到的数据为key值
复制 >>> for key in mydict:
... print(key)
... print(mydict[key])
...
1
a
2
b
判断某key
使用 in 还有 not in 判断是否在字典中有对应Key值,返回值为bool类型
复制 >>> mydict = {1:'a',2:'b'}
>>> 1 in mydict
True
>>> 3 in mydict
False
#由于value值是可以重复的,所以判断value是没有意义的
更新字典
通过对应key值访问到value值之后,重新赋值 给它则可进行更新
复制 >>> mydict = {1:'a',2:'b'}
>>> mydict[1] = 'c'
>>> mydict
{1: 'c', 2: 'b'}
删除
使用del语句删除对应key值所关联的value值,会把这个键值对删除掉
复制 >>> mydict = {1:'a',2:'b'}
>>> del mydict[1]
>>> mydict
{2: 'b'}
pop(obj) 函数删除字典中的某个值,并将被删除值返回
复制 >>> mydict = {1:'a',2:'b'}
>>> mydict.pop(1)
'a'
>>> mydict
{2: 'b'}
清空字典
dict.clear() 函数会将这个字典重新成为一个新的空字典
复制 >>> mydict = {1:'a',2:'b'}
>>> mydict.clear()
>>> mydict
{}
复制 >>> mydict = {1:'a',2:'b'}
>>> del mydict
>>> mydict
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'mydict' is not defined
#字典被删除之后连变量都不见了
集合
集合分为两种:可变集合 (set),不可变集合 (frozenset)
集合的内部结构和字典类似,但是不存储 value,其中的元素也会进行hash运算,可以称的上是一个没有value的字典
复制 >>> myset = {[1,2,3]}
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'list'
#可变数据对象不可hash,所以不能成为集合中数据元素
创建集合
复制 >>> myset = {1,2,3,4,5}
#直接通过大括号创建
>>> myset = set([1,2,3,1,2,3])
>>> myset
{1, 2, 3}
#通过工厂函数接收一个可迭代对象
复制 >>> myset = frozenset([1,2,3,1,2,3])
>>> myset
frozenset({1, 2, 3})
访问集合
复制 >>> myset = {1,2,3}
>>> for var in myset:
... print(var)
...
1
2
3
#集合虽然无序,但是可以通过for循环迭代访问
更新集合
set.add():更新可hash数据类型到集合中,并维持 数据类型
由于集合中的数据元素需要进行hash运算,可变数据类型 是不可以进行hash运算的,也就不能传递进来被更新
复制 >>> myset = {1,2}
>>> myset.add('abc') #更新字符串到集合中
>>> myset
{1, 2, 'abc'}
>>> myset.add((1,2,3)) #更新元组到集合中
>>> myset
{1, 2, 'abc', (1, 2, 3)}
>>> myset.add([1,2,3]) #更新列表到集合中报错
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'list'
set.update():如果传入的是一个序列 ,将被拆分成单个元素,并且去重
复制 >>> myset = {1,2}
>>> myset.update('abc') #更新字符串
>>> myset
{1, 2, 'b', 'c', 'a'} #字符串被拆开
>>> myset.update([1,2,3]) #更新列表
>>> myset
{1, 2, 3, 'b', 'c', 'a'} #这里列表支持更新是因为存储的数据已经不再是列表,而是列表中的每一个实际数据
>>> myset.update((3,4,5)) #更新元组
>>> myset
{1, 2, 3, 4, 'b', 5, 'c', 'a'} #元组中的每一个数据都被拿出更新到集合中,并且去重
复制 >>> myset = frozenset('abc')
>>> myset
frozenset({'a', 'b', 'c'})
>>> myset.add()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'frozenset' object has no attribute 'add'
#不可变集合不支持add更新
>>> myset.update()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'frozenset' object has no attribute 'update'
#不可变集合不支持update更新
删除集合中的元素
s.remove(obj):删除obj元素从集合s中
该函数在删除一个不存在的数据 时会引发KeyError
复制 >>> myset = {1,2,3}
>>> myset.remove(3)
>>> myset
{1, 2}
>>> myset.remove(5)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
KeyError: 5
s.discard(obj) :如果obj是集合s中的元素,从集合s中删除对象obj
该函数在删除一个不存在 的数据时不会报错
复制 >>> myset = {1,2}
>>> myset.discard(1)
>>> myset
{2}
>>> myset = {1,2}
>>> myset.discard(3)
>>> myset
{1, 2}
复制 >>> myset = {1,2,3}
>>> del myset
>>> myset
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'myset' is not defined
集合类型操作符
in 和 not in可以判断某个元素是否在集合中
复制 >>> myset = {1,2}
>>> 1 in myset
True
>>> 3 in myset
False
>>> 2 not in myset
False
子集和超集:
a < b:a是否是b的子集a.issubset(b)
a > b:a是否是b的超集a.issuperset(b)
复制 >>> a = {1,2}
>>> b = {1,2,3,4}
>>> a < b
True
>>> a.issubset(b)
True
集合的交、并、补操作
联合
在a集合中和b集合去重数据元素,剩下的组合到一起返回一个新的集合
复制 >>> a = {1,2,'a','b'}
>>> b = {1,3,'a','c'}
>>> a | b
{1, 2, 3, 'c', 'b', 'a'}
交集:在 a集合 和 b集合 中,共同含有的集合成员去掉重复数据,组成到一起返回一个新的集合
复制 >>> a = {1,2,'a','b'}
>>> b = {1,3,'a','c'}
>>> a & b
{'a', 1}
差补
在a集合中去掉所有与b集合中相同的元素,只在a中保留b中没有的集合成员
复制 >>> a = {1,2,'a','b'}
>>> b = {1,3,'a','c'}
>>> a - b
{2, 'b'}
对称差分
找出两个集合中,只属于集合a或集合b的元素;也就是去掉共有元素后,返回一个新的集合
函数:a.symmetric_differenc(b)
复制 >>> a = {1,2,'a','b'}
>>> b = {1,3,'a','c'}
>>> a ^ b
{2, 3, 'b', 'c'}
输入输出
输出
直接输出字符串和数值类型
复制 >>> print(1)
1
>>> print('hello world')
hello world
#无论什么类型,数值,布尔,列表,字典..这些变量都可以直接输出
格式化输出,类似于c语言的 print
复制 >>> s= 'hello'
>>> x = len(s)
>>> print( 'the length of %s is %d' % ( s, x ) )
The length of Hello is 5
转义输出类型
接收整数(会转换为对应ascii码字符)、 单字符)
复制 >>> print('%d %i' % (20,-10)) #d,i
20 -10
>>> print('%o'%-10) #o
-12
>>> print('%u' % 20) #u
20
>>> print('%x'%12) #x
c
>>> print('%e'%2.56) #e
2.560000e+00
>>> print('%f'%2.56) #f
2.560000
>>> print('%c'% 65) #C
A
>>> print('%s'% 'abc') #s
abc
输入
Python2: raw_input()
、 input()
input 会默认用户输入的是合法的Python表达式
raw_input 会把所有的输入当做字符串 进行处理
复制 #python2.x raw_input
>>> var = raw_input('pls type a num you want:')
pls type a num you want:20
>>> var
'20'
>>> var = raw_input('pls type a num you want:')
pls type a num you want:abc
>>> var
'abc'
复制 #python2.x input
>>> var = input('pls type a num you want:')
pls type a num you want:20
>>> var
20
>>> var = input('pls type a num you want:')
pls type a num you want:abc
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<string>", line 1, in <module>
NameError: name 'abc' is not defined
#2中的input函数会严格检查输入的类型,abc此时没有指明字符串类型,被当成了变量名,所以引发了 NameError
>>> var = input('pls type a num you want:')
pls type a num you want:'abc' #改变输入值的类型
>>> var
'abc'
复制 #python3.x
>>> var = input('pls type a num you want:')
pls type a num you want:20
>>> var
'20'
>>> var = input('pls type a num you want:')
pls type a num you want:abc
>>> var
'abc'
#input保存的均为字符串类型
条件分支及循环语句
if条件语句
复制 if 条件语句:
执行代码
elif 条件语句:
执行代码
else:
执行代码
#当条件成立则执行对应的代码,条件判断只会进入一个
复制 >>> a = 1
>>> b = 2
>>> if a == b:
... print('a == b')
... elif a > b:
... print('a > b')
... else:
... print('a < b')
...
a < b
#else可有可无,起到收尾工作,其他条件都会进入到else中
在 if 语句中,缩进表示上下关系
while循环
复制 >>> while 条件语句:
执行语句
#当条件语句为真时,将执行循环内的代码
复制 >>> a = 5
>>> while a > 0:
... print(a)
... a = a - 1
...
5
4
3
2
1
for循环
复制 >>> for var in secquence:
执行语句
#for语句常用来迭代访问一个可迭代对象,如字符串,列表,元祖这样的序列,或者是文件对象等
复制 >>> mystr = 'abc'
>>> for var in mystr:
... print(var)
...
a
b
c
循环else语句
在循环正常结束 的时候,我们可以使用else语句来进行收尾工作
语法格式:
复制 >>> mystr = 'abc'
>>> for var in mystr:
... print(var)
... else:
... print('all done')
...
a
b
c
all done
>>> mylist = [1,2,3,4,5]
>>> while mylist:
... print(mylist[0])
... mylist = mylist[1:]
... else:
... print('all done')
...
1
2
3
4
5
all done
干预循环
break :
如果是用在嵌套循环中,break语句将停止执行最深层次 的循环,并开始执行下一行代码
复制 >>> for var in mystr:
... if var == 'b':
... break
... else:
... print(var)
... else:
... print('all done')
...
a
#在结果中,由于我们干预了正常的循环,所以结果不含有else语句中的内容
>>> for var in mystr:
... if var == 'b':
... continue
... else:
... print(var)
... else:
... print('all done')
...
a
c
all done
#在结果中,continue语句只是跳过这次循环,并没有导致循环直接结束,所以else语句中的结果依旧出现
基本数据类型常用内建函数
字符串内建函数
在字符串类型中,还包含一些其他方便我们处理数据的内建函数, ::: warning 某些函数可能返回结果与原字符串不同,但并不是修改了本身字符串,只是返回了一个新的而已 :::
大小写转换函数
复制 >>> mystr = 'aBc,bDc'
>>> mystr.lower()
'abc,bdc'
复制 >>> mystr = 'aBc,bDc'
>>> mystr.upper()
'ABC,BDC'
string.swapcase():字母大写转换小写,小写转换成大写
复制 >>> mystr = 'aBc,bDc'
>>> mystr.swapcase()
'AbC,BdC'
string.title():将每个 单词首字母大写,将句中字符变为小写
复制 >>> mystr = 'aBc,bDc'
>>> mystr.title()
'Abc,Bdc'
string.capitalize():将第一个 字母大写,将句中字符变为小写
复制 >>> mystr = 'aBc,bDc'
>>> mystr.capitalize()
'Abc,bdc'
搜索函数
string.find(str,[start=0,stop=len(string)]):计算string中出现str的第一个字母的索引,如果没有出现,则返回-1
复制 >>> mystr = 'aabbcc'
>>> mystr.find('a')
0
>>> mystr.find('1')
-1
string.index(str ,[start=0,stop=len(string)]):计算string中出现str的第一个字母的索引,如果没有出现,引发异常
复制 >>> mystr = 'aabbcc'
>>> mystr.index('b')
2
>>> mystr.index('d')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: substring not found
string.count(str ,[start=0,stop=len(string)]):计算str在string中出现的次数
复制 >>> mystr = 'aabbcc'
>>> mystr.count('a')
2
>>> mystr.count('d')
0
string.endswith(chr,[start=0,stop=len(string)]):检查string是否以chr结尾,如果是,返回True,反之,返回False
复制 >>> mystr = 'aabbcc'
>>> mystr.endswith('cc')
True
>>> mystr.endswith('aa')
False
替换函数
string.replace(str1, str2,[num= string.count(str1)]):将str1替换为str2, num为替换次数,默认次数为str1出现的次数
复制 >>> mystr = 'aabbcc'
>>> mystr.replace('a','*')
'**bbcc'
>>> mystr = 'aabbcc'
>>> mystr.replace('a','*',1)
'*abbcc'
string.strip(chr):在string的开头和结尾删除chr,当chr为空时,默认删除空白符
复制 >>> mystr = ' aabbcc '
>>> mystr.strip()
'aabbcc'
>>> mystr = '**abc**'
>>> mystr.strip('*')
'abc'
string.rstrip(chr):删除string字符串末尾的空白符或给定字符
复制 >>> mystr = ' aabbcc '
>>> mystr.rstrip()
' aabbcc'
分割,组合函数
string.split(chr,num=string.count(str)):以chr为分割符将string字符串分割,返回分割后的结果保存在列表 中;如果指定num参数,则只分割前num次
复制 >>> mystr = 'a:b:c'
>>> mystr.split(':')
['a', 'b', 'c']
chr.join(str.[list,tuple]):以chr作为连接符,拼接字符串序列
复制 >>> ''.join(['a','b','c'])
'abc'
>>> '*'.join(['a','b','c'])
'a*b*c'
判断函数
string.isdigit():如果string只包含数字,则返回True,否则返回False
复制 >>> mystr = '123'
>>> mystr.isdigit()
True
string.islower():字符串中的字母全为小写则返回True,否则返回False
复制 >>> mystr = 'abc'
>>> mystr.islower()
True
string.isupper():字符串中的字母全为大写则返回True,否则返回False
复制 >>> mystr = 'ABC'
>>> mystr.isupper()
True
string.isspace():字符串中只包含空白字符,返回True,否则返回False
复制 >>> mystr = ' '
>>> mystr.isspace()
True
列表内建函数
list.append(obj):在列表尾部追加obj
复制 >>> mylist = [1,2,3]
>>> mylist.append('abc')
>>> mylist
[1, 2, 3, 'abc']
list.count():返回一个对象在列表中出现的次数
复制 >>> mylist = [1,2,3]
>>> mylist.count(1)
1
list.extend(seq):把序列seq中的内容分别提取并加入到列表中
复制 >>> mylist = [1,2,3]
>>> mylist.extend('abc')
>>> mylist
[1, 2, 3, 'a', 'b', 'c']
list.insert(index,obj):在索引index的位置插入obj,原位置的内容向后移动
复制 >>> mylist = [1,2,3]
>>> mylist.insert(1,'a')
>>> mylist
[1, 'a', 2, 3]
list.pop(index):删除并返回 index位置的数据对象,默认是最后一个对象
复制 >>> mylist = [1,2,3]
>>> mylist.pop()
3
>>> mylist
[1, 2]
复制 >>> mylist = [1,2,3]
>>> mylist.reverse()
>>> mylist
[3, 2, 1]
元组内建函数
tuple.index(obj,beg=0,end=len(string)):检查obj是否包含在tuple中,不存在会报错
复制 >>> mytuple = (1,2,3)
>>> mytuple.index('a')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: tuple.index(x): x not in tuple
>>> mytuple.index(1)
0
tuple.count(obj):返回obj在元组中出现的次数
复制 >>> mytuple = (1,2,3)
>>> mytuple.count(1)
1
系统内建函数
reversed(seq):接受一个序列作为参数,返回一个以逆序访问的迭代器
复制 >>> mytuple = (1,2,3)
>>> for var in reversed(mytuple):
... print(var)
...
3
2
1
sorted(iter,key=None,reverse=False):接受一个可迭代对象作为参数,返回一个有序的列表,可选参数是一个排序方式
复制 >>> mytuple = (3,5,7,2,9)
>>> sorted(mytuple)
[2, 3, 5, 7, 9]
>>> mylist = ['aa','bbb','c']
>>> def cmp(x):
... return len(x)
...
>>> sorted(mylist,key=cmp)
['c', 'aa', 'bbb']
复制 >>> mylist = [1,2,3,4,5]
>>> sum(mylist)
15
zip(it0,it1,..itN):返回一个zip数据类型,将其中序列相同位置的值组成一个元组,以短板结束
复制 >>> a = [1,2,3]
>>> b = ['a','b','c','d']
>>> for var in zip(a,b):
... print(var)
...
(1, 'a')
(2, 'b')
(3, 'c')
map(func,seq):map函数第一个参数可以是一个函数对象,第二个是一个序列,函数将作用于序列中的每个值,返回一个map数据类型
复制 >>> def func(x):
... return x*x
...
>>> mylist = [1,2,3]
>>> for var in map(func,mylist):
... print(var)
...
1
4
9
reduce(func,seq):该函数来自于functools标准库,把一个函数作用在一个序列[x1, x2, x3...]上,这个函数必须接收两个参数,他会把结果继续和序列的下一个元素做累积计算
复制 >>> from functools import reduce
>>> def myadd(x,y):
... return x+y
...
>>> mylist = [1,2,3,4,5]
>>> reduce(myadd,mylist)
15
range函数
在Python中range函数属于内建函数(不需要从三方模块导入),可以更方便的生产一个范围内的数据
复制 range(stop) -> range object
#python3.x
>>> var = range(10)
>>> var
range(0, 10)
在Python2 版本中,除了 range 函数,还有一个 xrange 函数
复制 #python2.x
>>> var = range(10)#生成实际列表数据
>>> var
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]#生成抽象数据对象
>>> var = xrange(10)
>>> var
xrange(10)
Python2 :range函数具体生成有关数据,xrange为生成器(只有在真正用到数据的时候,数据才会产生), 生成器中的数据可以使用 for 循环迭代访问出来
复制 #python3.x
>>> for var in range(4):
... print(var)
...
0
1
2
3
Python3 :range函数为生成器,产生range类型的数据,这样做的好处是为了节约内存, 删除 了xrange
range 函数还支持按照一定步长取值,这里以Python3 为例
复制 range(start, stop[, step]) -> range object
#python3.x
>>> for var in range(0,5,2):
... print(var)
...
0
2
4
推导式
推导式是一种可以更加方便生产某种数据的方式;比如生产一些具有一定要求的列表 ,元组 ,字典 等数据集
列表推导式
复制 [ 表达式 for value in 序列 if 过滤条件 ]
所有从这个for循环出来的数据;首先会经过if条件过滤;执行表达式计算;重新返回成一个新的列表
过滤条件按照需求也可以没有
复制 >>> mylist = [1,2,3,4,5]
>>> newlist = [ var + 1 for var in mylist ]
>>> newlist
[2, 3, 4, 5, 6]
>>> mylist = [1,2,3,4,5]
>>> newlist = [ var * var for var in mylist if var != 3]
>>> newlist
[1, 4, 16, 25]
字典推导式
字典推导式 :与列表推导式类似,只不过需要两个值存在来维护字典的键值对形式
复制 {key:value for key in 序列 if 过滤条件}
>>> mylist = ['a','b','c']
>>> new_dict = {mylist.index(var):var for var in mylist}
>>> new_dict
{0: 'a', 1: 'b', 2: 'c'}
集合推导式
集合推导式跟列表推导式非常相似,唯一语法区别在于用 { } 代替 [ ] :
复制 { 表达式 for value in 序列 if 过滤条件 }
>>> mylist
['a', 'b', 'c']
>>> myset = { var.upper() for var in mylist}
>>> myset
{'C', 'B', 'A'}
深浅拷贝
::: tip 在Python中,面对列表这种可变数据类型,在赋值的过程中,需要我们注意 :::
复制 >>> mylist = [1,2,3,4]
>>> mylist1 = mylist
>>> mylist[0] = 'a'
>>> mylist
['a',2,3,4]
>>> mylist1
['a',2,3,4]
普通的赋值操作结束后,两个列表修改其中一个,会影响到另一个,此时并没有真正意义上进行数据拷贝,只是变量的引用
浅拷贝
复制 >>> mylist = [1,2,3]
>>> mylist1 = mylist[:] #mylist1切片拷贝自mylist
>>> mylist[0] = 'a'
>>> mylist
['a', 2, 3]
>>> mylist1
[1, 2, 3]
#切片实现浅拷贝,修改其中一个,另一个不会随之更改
复制 >>> import copy
>>> mylist = [1,2,3]
>>> mylist1 = copy.copy(mylist)
>>> mylist[0] = 'a'
>>> mylist1
[1, 2, 3]
>>> mylist
['a', 2, 3]
#这里在进行浅拷贝之后,两个列表中的值修改不会互相影响
但是浅拷贝也存在一个问题,虽然可以将列表数据进行复制拷贝,但是只能是浅层对象的拷贝:
复制 >>> import copy
>>> mylist = [1,2,['a','b']]
>>> mylist1 = copy.copy(mylist)
>>> mylist[-1][0] = 1 #修改嵌套列表中的第一个数据元素
>>> mylist
[1, 2, [1, 'b']]
>>> mylist1
[1, 2, [1, 'b']]
#此时浅拷贝无法拷贝深层对象,修改结果互相影响
深拷贝
复制 >>> import copy
>>> mylist = [1,2,['a','b']]
>>> mylist1 = copy.deepcopy(mylist)
>>> mylist[-1][0] = 1
>>> mylist
[1, 2, [1, 'b']]
>>> mylist1
[1, 2, ['a', 'b']]
经过deepcopy
函数拷贝后,深层次对象也会被拷贝
文本文件操作
文件操作主要分两大部分:打开文件、读写文件
打开文件
复制 fp = open(path,mode='r')
该函数可以路径为Path的文件,默认以读 权限打开,并返回一个打开文件对象
打开权限
复制 '''
- r: 读
- w: 写
- a: 追加
'''
单纯使用这三种方式打开文件,只拥有一种打开权限 ,要么读,要么写
如果希望在打开文件的时候,读写权限兼备 ,那么可以在权限后带一个+ 号
复制 '''
- r+: 读写 不创建新文件,文件读写指针在开头
- w+: 读写 创建新文件,读写指针在开头;如果文件存在则会清空这个文件之前的内容
- a+: 读写 创建新文件,读写指针在末尾;不会清空该文件之前的内容
'''
::: tip
注意 :含有写权限 在打开一个不存在文件时,该文件将被创建 :::
关闭文件
该函数可以关闭文件对象,并且刷新缓冲区
读文件
读取文件放到字符串变量中,参数num
为指定读取的字符数,默认为全读
1.txt文件其中内容
读取文件内容
复制 >>> fp = open('1.txt','r+')
>>> mystr = fp.read(2)
>>> print(mystr)
aa
>>> fp.close()
读取一行到一个字符串变量中(读到换行符 (\r\n),包括行末的标识符 ,或者是文件结束的标识(EOF) )
复制 >>> fp = open('1.txt','r+')
>>> mystr = fp.readline()
>>> print(mystr)
aaa
>>> fp.close()
# 输出的多余空行为文件中一行结尾的换行符
读取整个文件到字符串列表。
复制 >>> fp = open('1.txt','r+')
>>> str_list = fp.readlines()
>>> print(str_list)
['aaa\n','bbb']
>>> fp.close()
注意 :每次调用 readlines(size) 函数,会返回大约200MB 的数据,而且所返回的必然都是完整 的行数据,大多数情况下,返回的数据的字节数会稍微比 size 指定的值大一点 (除最后一次调用readlines(size) 函数的时候)。通常情况下,解释器会自动将用户指定的 size的值调整成内部缓存 大小的整数倍
写文件
在文件中写入字符串
复制 fp.writelines(list_of_string)
把字符串列表 写入文件
复制 >>> fp = open('1.txt','w')
>>> fp.write('abc\n')
>>> fp.writelines(['bbb\n','ccc\n']) #写入字符串列表
>>> fp.close()
::: tip 注意 :写入文件字符串的方法不会自动 的加上换行符 ,所以需要大家在写入时,手动写入 换行符标志 ::: 此外,写入文件的内容我们并不能直接在磁盘文件看到,这是因为写入的内容 暂时被保存在了缓存中 ;我们可以通过使用 fp.close()或fp.flush()函数来进行缓冲区刷新 操作,使写入文件的内容直接保存在磁盘中
刷新缓冲区
复制 fp.close()
# 关闭文件可以刷新缓冲区
fp.flush()
# 手动刷新缓冲区
读写指针
每次读或写操作之后我们会发现,我们下次的都操作都是在这一次之后,这是因为我们在打开一个文件的同时,内存中会维护一个读写指针 用来标识 我们访问文件的位置 ,一个文件对象读写操作共享 同一根指针
复制 >>> fp = open('1.txt','r')
>>> fp.readline()
aaa\n
>>> fp.readline()
bbb\n
#这里第二次读操作继续在第一次操作之后
修改读写指针位置
复制 fp.seek(offset[,whence])
'''
offset: 偏移量
whence: 从何处偏移;0从文件开头,1从当前位置,2从文件末尾处
'''
:::tip 注意 :以a或a+ 的模式打开,每次进行写操作时,文件操作标记会自动返回到文件末尾,如果此时你想读文件开头部分内容,需要修改读写指针位置为fp.seek(0, 0) :::
CSV文件
CSV文件通常用于我们在电子表格软件和纯文本之间交互数据,CSV文件内容是一些用逗号分隔 的原始字符串。
CSV文件的操作在Python中有单独的模块来使用,模块名为csv
CSV文本操作
复制 import csv # 导包
fp = open('1.csv', 'a+') # 生成文件句柄
fp = csv.writer(fp) # 生成csv写对象
fp = csv.reader(fp) # 生成csv读对象
CSV写文件
为文件写入以逗号分割 的数据;逗号分割的数据:常见的有list ,tuple ,set
复制 writer = csv.writer(fp)
writer.writerow(('a','b','c','d'))
::: tip
注意 :在写csv
文件的时候,可能会出现数据空行;可以通过在打开文件时指定: newline ='' 或以二进制 打开 :::
复制 fp = open('1.csv','rb',newline='')
CSV读文件
复制 reader = csv.reader(fp) #生成对应文件的csv读对象
for var1,var2,var3… in reader:
print(var1,var2,var3…)
字典形式读写文件
除了以上读写csv文件的方式,csv模块还提供了 csv.DictReader() 和 csv.DictWriter() 用于将字典形式的数据写入csv文件,以及从csv文件读取出的数据保存在字典中
复制 import csv
#csv.DictWriter
with open('names.csv', 'w') as csvfile:
fieldnames = ['first_name', 'last_name']#指定标题列
writer = csv.DictWriter(csvfile, fieldnames=fieldnames)
writer.writeheader()
writer.writerow({'first_name': 'Baked', 'last_name': 'Beans'})
writer.writerow({'first_name': 'Lovely', 'last_name': 'Spam'})
writer.writerow({'first_name': 'Wonderful', 'last_name': 'Spam'})
#csv.DictReader
with open('names.csv') as csvfile:
reader = csv.DictReader(csvfile)
for row in reader:
print(row['first_name'], row['last_name'])#根据标题列定义的数据取行内容
表格文件操作
xlrd读取表格
对表格文件进行读取
复制 import xlrd
execl = xlrd.open_workbook('path')
复制 sheet = execl.sheet_by_name('sheet_name') # 通过名称
sheet = execl.sheet_by_index('sheet_index') # 通过名字
复制 sheet.row_values(num)
sheet.col_values(num)
复制 data = sheet.cell(rowx, colx)
# 单元格属性获取
data.value # 单元格的值
data.ctype # 单元格值类型
'''
0:empty、1:string、2:number、4:boolean
3:date、5:error
'''
复制 import xlrd
execl = xlrd.open_workbook('/Users/lienze/Desktop/1.xlsx')
execl_names = execl.sheet_names() # 查看sheet的名字
sheet = execl.sheet_by_index(0) # 选择第一个sheet
#-----------遍历整个列表读取数据----------
for row in range(sheet.nrows):
for col in range(sheet.ncols):
data = sheet.cell(row,col)
print(data.value,end='\t|')
print('\v')
xlwt写入表格
对表格文件进行写入
复制 import xlwt
workbook = xlwt.Workbook(encoding = 'utf-8') # 创建一个workbook 设置编码
复制 worksheet = workbook.add_sheet('My Worksheet') # 添加一个sheet
复制 worksheet.write(1,0, label = 'this is test') # 表格指定位置写入数据
复制 workbook.save('Excel_test.xls')
xlutils拷贝表格
如果需要操作已有表格,那么需要进行拷贝,安装一个新的模块
复制 from xlutils.copy import copy
import xlrd
import xlwt
old_excel = xlrd.open_workbook('fileName.xls') # 打开历史文件
new_excel = copy(old_excel) # 拷贝为新的表格文件
ws = new_excel.get_sheet(0) # 获取对应sheet
ws.write(row, col, label='修改内容') # 修改
new_excel.save()
压缩文件
tar
linux系统下的打包工具,只打包,不压缩,这是一种归档行为
复制 import tarfile
tar = tarfile.open(fname + ".tar.gz", "w:gz") # 打开tar.gz的压缩文件
'''
open(name=None, mode='r', fileobj=None, bufsize=10240, **kwargs) method of builtins.type instance
Open a tar archive for reading, writing or appending. Return
an appropriate TarFile class.
mode:
'r' or 'r:*' open for reading with transparent compression
'r:' open for reading exclusively uncompressed
'r:gz' open for reading with gzip compression
'r:bz2' open for reading with bzip2 compression
'r:xz' open for reading with lzma compression
'a' or 'a:' open for appending, creating the file if necessary
'w' or 'w:' open for writing without compression
'w:gz' open for writing with gzip compression
'w:bz2' open for writing with bzip2 compression
'w:xz' open for writing with lzma compression
'x' or 'x:' create a tarfile exclusively without compression, raise
an exception if the file is already created
'x:gz' create a gzip compressed tarfile, raise an exception
if the file is already created
'x:bz2' create a bzip2 compressed tarfile, raise an exception
if the file is already created
'x:xz' create an lzma compressed tarfile, raise an exception
if the file is already created
'r|*' open a stream of tar blocks with transparent compression
'r|' open an uncompressed stream of tar blocks for reading
'r|gz' open a gzip compressed stream of tar blocks
'r|bz2' open a bzip2 compressed stream of tar blocks
'r|xz' open an lzma compressed stream of tar blocks
'w|' open an uncompressed stream for writing
'w|gz' open a gzip compressed stream for writing
'w|bz2' open a bzip2 compressed stream for writing
'w|xz' open an lzma compressed stream for writing
'''
复制 tar.add(filepath)
Help on method add in module tarfile:
add(name, arcname=None, recursive=True, exclude=None, *, filter=None) method of tarfile.TarFile instance
Add the file `name' to the archive. `name' may be any type of file
(directory, fifo, symbolic link, etc.). If given, `arcname'
specifies an alternative name for the file in the archive.
Directories are added recursively by default. This can be avoided by
setting `recursive' to False. `exclude' is a function that should
return True for each filename to be excluded. `filter' is a function
that expects a TarInfo object argument and returns the changed
TarInfo object, if it returns None the TarInfo object will be
excluded from the archive.
复制 import tarfile
import os
BASE_DIR = '/Users/lienze/Desktop/'
tar = tarfile.open(os.path.join(BASE_DIR,'1.tar'),'w')
tar.add(os.path.join(BASE_DIR, '1.csv'),arcname='1.csv') # 提供文件到归档包里
tar.close()
复制 import tarfile
import os
BASE_DIR = '/Users/lienze/Desktop/'
tar = tarfile.open(os.path.join(BASE_DIR,'1.tar'),'r')
file_names = tar.getnames()
for file_name in file_names:
tar.extract(file_name, BASE_DIR)
tar.close()
gz
即gzip ,通常只能压缩一个文件。与tar 结合起来就可以实现先打包,再压缩。
复制 gz = gzip.open('1.gz','wb')
open(filename, mode='rb', compresslevel=9, encoding=None, errors=None, newline=None)
Open a gzip-compressed file in binary or text mode.
The filename argument can be an actual filename (a str or bytes object), or
an existing file object to read from or write to.
The mode argument can be "r", "rb", "w", "wb", "x", "xb", "a" or "ab" for
binary mode, or "rt", "wt", "xt" or "at" for text mode. The default mode is
"rb", and the default compresslevel is 9.
For binary mode, this function is equivalent to the GzipFile constructor:
GzipFile(filename, mode, compresslevel). In this case, the encoding, errors
and newline arguments must not be provided.
For text mode, a GzipFile object is created, and wrapped in an
io.TextIOWrapper instance with the specified encoding, error handling
behavior, and line ending(s).
复制 gz.writelines(fp)
fz.write(fp.read())
一个压缩为gz文件的小例子,记得后缀名要具有文件历史的后缀,这是因为gz的解压会直接去掉gz后缀
复制 import gzip
help(gzip.open)
gz = gzip.open(
'/Users/lienze/Desktop/1.xls.gz',
'wb',
)
with open('/Users/lienze/Desktop/1.xls','rb') as fp:
gz.write(fp.read())
gz.close()
解压gz文件,只需要打开gz压缩文件,从其中读取即可
复制 import gzip
gz = gzip.open(
'/Users/lienze/Desktop/1.xls.gz',
'rb',
)
with open('/Users/lienze/Desktop/1.xls','wb') as fp:
fp.write(gz.read())
gz.close()
zip
不同于gzip ,虽然使用相似的算法,但可以打包压缩多个文件,压缩率低于tar.gz 及rar
复制 import zipfile
z = zipfile.ZipFile(filename, 'w')
复制 z.write(filename, arcname=None, compress_type=None)
'''
filename: 待压缩文件
arcname: 压缩文件包里的文件名
'''
复制 import zipfile
import os
BASE_DIR = '/Users/lienze/Desktop'
z = zipfile.ZipFile('/Users/lienze/Desktop/1.zip', 'w')
for file in os.listdir(BASE_DIR):
z.write(os.path.join(BASE_DIR,file))
z.close()
复制 import zipfile
import os
BASE_DIR = '/Users/lienze/Desktop'
z = zipfile.ZipFile('/Users/lienze/Desktop/1.zip', 'r')
for file in z.namelist():
with open(os.path.join(BASE_DIR,file),'wb') as fp:
content = z.read(file)
fp.write(content)
rar
打包压缩文件,最初用于DOS ,基于window 操作系统压缩率比zip 高;但速度慢,随机访问的速度也慢
函数的构成
当我们在编程过程中,发现某些功能可能会一直在我们的整个程序中使用,那么这里就需要函数来实现对功能的包装
def语句
def 语句可以在运行时,创建一个新的函数对象并赋值给一个变量名
def 语句可以出现在一个Python脚本 任何地方
def 语句定义函数可以嵌套,甚至是在if 语句中
复制 >>> def func():
... print('hello world')
...
>>> func()
hello world
参数
参数是我们在定义一个函数时,可能需要在函数内部处理一些外界的变量,那么需要通过参数来进行数据的导入
复制 >>> def func(a,b):
... return a + b
...
>>> var = func(1,2)
>>> var
3
形参: 定义函数时后面括号里 所写的形式参数,如这里的a,b,也称形参
实参 : 在函数实际调用 时,如这里的1, 2传入的数据为实参 ,也称实际参数
必备参数
定义函数时的形参,在我们传递实参时,需要数量 和顺序 保持一致
复制 >>> def func(age,sex):
... print('bob age is %d, sex is %s ' % (age, sex))
...
>>> func(10,'male') #按照规矩来
bob age is 10, sex is male
>>> func('男性',20) #不按规矩来,第一个位置的值被填写到了print时的%d处,引发错误
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 2, in func
TypeError: %d format: a number is required, not str
>>> func(10) #少传了一个参数,也会引发异常
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: func() missing 1 required positional argument: 'sex'
命名参数
我们可以通过命名传参的方式,打乱传递参数的顺序
复制 >>> def func(age,sex):
... print('bob age is %d, sex is %s ' % (age, sex))
...
>>> func(sex='女人',age=18)
bob's age is 18, sex is 女人
缺省参数
某些情况下,可能我们的函数会经常处理 一些相同 的数据,那么可以通过在形参中定义缺省参数 的方式来实现
缺省参数一定是从右向左 的顺序来设置
因为实参默认的接收顺序为从左向右,如果缺省参数之后还有一个需要传递值的形参,那么可能因为缺省参数已经被覆盖了值,而导致后面位置的形参无法接收到实际值而报错
复制 >>> def func(today,wea='Sunny'):
... print('today is %s, weather is %s.' % (today,wea))
...
>>> func('礼拜一')
today is 礼拜一, weather is Sunny.
不定长参数
某些时候我们需要传入的实参 可能无法在形参 处确定,那么可以使用不定长传参的方式
*args:接收不定长参数为元组 形式
复制 >>> def func(*arg):
... print(type(arg))
... print(arg)
...
>>> func(1,2,3,4,5) #传入不定长实参
<class 'tuple'> # *arg接收不定长实参会被保存为元组类型
(1, 2, 3, 4, 5)
kwargs:接收不定长参数为 键值对**形式(字典)
复制 >>> def func(**arg):
... print(type(arg))
... print(arg)
...
>>> func(a=1,b=2,c=3)
<class 'dict'> # **arg 接收不定长键值对形式实参会被保存为字典类型
{'a': 1, 'b': 2, 'c': 3}
>>> func(1,2,3) # **arg 格式接收不是键值对形式的参数,将会爆出TypeError
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: func() takes 0 positional arguments but 3 were given
返回值
return
语句:
可以返回任意类型 的对象作为返回值(包括函数 ,对象实例 等)
同时也可以返回多个值 ,在返回多个值时,会处理为一个元组
返回的值可以通过函数调用之后使用等号(=)赋值 给一个变量
复制 >>> def func():
... return 1,2
...
>>> var = func()
>>> var
(1, 2)
函数作用域
函数作用域标志了变量的生命周期
本地&全局
复制 a = 1
def add(y,z):
print y+z+a
return y+z+a
全局变量名:a add
因为这个a是在这个文件内最外层注册的变量名,所以他是全局变量
全局变量能够在函数内部直接使用 ,而不需要特定的声明
复制 >>> a = 1
>>> def change():
... a = 2
... print(a)
...
>>> a
1
>>> change() #打印函数内部修改之后的a
2
>>> a #打印外部a
1
外部的a并没有被修改
这是因为,函数内部赋值 的变量除非声明为全局变量或非本地变量,否则均为本地变量
这里的a = 2,因为是在函数内部使用赋值的,所以解释器会把它当作一个函数内部的变量,他的作用域是这个函数 内部
如果想修改 一个全局变量,只需要在函数内部被修改变量前加global 语句
复制 >>> a = 1
>>> def change():
... global a
... a = 2
... print(a)
...
>>> a
1
>>> change() #打印函数内部修改之后结果
2
>>> a #打印外部的a值
2
传值&传引用
可变对象作为参数传递,可以在函数内部直接修改初始数据对象的值,是传引用
不可变对象传递时,无法直接修改初始数据对象的值,是传值
传递一个不可变对象 ,按值传递:
复制 >>> a = 1
>>> def change(a):
... a = 2
... print(a)
...
>>> change(a)
2
>>> a
1
传递一个可变对象 ,按引用传递:
复制 >>> a = [1,2,3]
>>> def change(a):
... a[0] = 'a'
... print(a)
...
>>> a
[1, 2, 3]
>>> change(a)
['a', 2, 3]
>>> a
['a', 2, 3]
#可变对象作为参数传递,传的是引用,内部修改影响全局
函数嵌套
内部函数整个函数体都在外部函数的作用域;如果在外部没有对这个函数的引用,那么除了在函数体内,任何地方都不能调用这个函数
复制 def func1():
print('1')
def func2():
print('2')
如果我们想使用函数内部定义的func2()
,可以采用前项声明 的方式
复制 def func2():
pass
def func1():
print('1')
global func #声明局部函数为全局函数对象
def func2():
print('2')
匿名函数
匿名函数(lambda表达式):
除了def语句之外,我们还可以使用lambda表达式创建函数
语法格式 :
复制 lambda x1,x2,x3... : exper
参数可以有多个,返回值为冒号后面表达式所返回的结果
复制 >>> f = lambda x,y : x+y
>>> f(1,2)
3
::: tip 缺省参数 也可以在lambda
中使用;注意缺省参数定义顺序 :::
复制 >>> f = lambda x,y=1 : x+y
>>> f(10)
11
>>> f = lambda x=1,y : x+y #缺省参数定义顺序一定是从右向左
File "<stdin>", line 1
SyntaxError: non-default argument follows default argument
跳转表
跳转表 (jump table):函数方法的列表 或字典 ,能够按照需要执行相应的动作
复制 L = [ lambda x:x*2 , lambda x:x*3 , lambda x:x*4] #含有三个lambda函数的列表
for f in L:
print(f(2))
异常
当我们的程序发生一些解释器无法继续处理下去的事情,我们的解释器无法执行无法编译,这时会抛出错误 (异常 ) 一般的异常是一些逻辑错误 ,语法错误 ,无法生成结果 等 抛出错误(异常 )之后,我们的程序将无法正常执行下去(抛出的错误会使我们的程序(一般是终止 )做错误的默认处理) 但是我们也可以自己去改写 出现错误之后的默认处理动作 ,也叫做捕获 异常;这么做的目的就是为了提高我们程序的健壮性,应对各种复杂的互联网计算机环境
一些常见的异常
尝试访问未声明变量
复制 >>> a
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'a' is not defined
除数为0
复制 >>> 1/0
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ZeroDivisionError: integer division or modulo by zero
语法错误
复制 >>> if
File "<stdin>", line 1
if
^
SyntaxError: invalid syntax
访问字典中不存在的key值
复制 >>> mydict = {1:'a',2:'b'}
>>> mydict[3]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
KeyError: 3
索引超出范围
复制 >>> mylist = [1,2,3,4,5]
>>> mylist[5]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
IndexError: list index out of range
访问未知的对象属性
复制 >>> mylist = [1,2,3,4,5]
>>> mylist[5]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
IndexError: list index out of range
缩进错误
复制 >>> a = 1
>>> if a == 1:
... print 'a==1'
File "<stdin>", line 2
print 'a==1'
^
IndentationError: expected an indented block
异常捕获
将可能发生错误的语句写到try 语句部分,使用except 语句捕获对应异常,如果不明确捕捉的异常,可使用Exception 将所有异常列为被捕捉对象
复制 try:
语句
except 异常:
捕获异常后的执行语句
复制 >>> def func():
... print(1 / 0)
...
>>> func()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 2, in func
ZeroDivisionError: division by zero
复制 >>> try:
... func()
... except ZeroDivisionError:
... print('除数为0')
...
除数为0
try...finally
复制 try:
语句
except Exception:
捕获异常后的执行语句
finally:
不管异常抛出,都将执行这里的语句
复制 >>> def func():
... print(1 / 0)
...
>>> try:
... func()
... except Exception:
... print('出错了')
... finally:
... print('finally')
...
出错了
try...else
我们一般在else 语句中执行关闭套接字,关闭文件句柄,线程,进程资源释放,做一些对象内存释放的工作 为什么不在finally 中呢?这是因为可能因为异常抛出,我们对应的资源句柄连创建都没有完成,也就没有必要对应的释放
复制 try:
语句
except Exception:
捕获异常后的执行语句
else:
这里语句只在没有异常触发时执行
复制 >>> try:
... 1 / 0
... except Exception:
... print('Error')
... else:
... print('没有出错')
...
Error
在异常捕获时,我们一般使用else 语句与finally 语句配合使用;finally 语句被用作最终结束工作,做一些提示或日志写入等,而else 常用在被捕获语句成功执行后的一些资源释放工作
手动抛出异常
复制 >>> raise TypeError('出错了')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 出错了
>>> raise TypeError
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError
>>> raise Exception('出错了')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
Exception: 出错了
手动抛出的异常必须是在当前环境下已注册 的;若未定义,则报错
复制 >>> raise MyError
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'MyError' is not defined
异常也是常用的程序间通信的方式,类似信号
异常也经常用在测试方面:在对某些驱动进行测试时,我们可以在必要情况下手动抛出异常
自定义异常
在Python中所有的异常都是从BaseException 这个根异常类派生
这个根异常类派生如下异常类:
我们考虑的所有内建异常都是继承自Exception 类,可以通过继承Exception 类,来实现自定义异常
复制 class Myerror(Exception):
pass
def checklist(mylist,index): #这个函数输出对应索引位置上的值
print (mylist[index])
try:
mylist = input('请输入一个序列:')
index = int(input('请输入访问位置:'))
if index > len(mylist): #如果传入的索引值超过序列最大索引位置
raise Myerror #raise抛出自定义错误
except Myerror: #捕获自定义错误
print ('the index is out of range!')
else:
checklist(mylist,index)
C:\Users\Administrator\Desktop>python 1.py
请输入一个序列:abc
请输入访问位置:4
the index is out of range!
断言
断言一般用来判断一些bool语句,在断言成功时不采取任何措施,否则触发AssertionError(断言错误)的异常:
复制 >>> assert 1 == 0
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AssertionError
>>> assert not False
>>> #没有抛出断言异常
上下文管理
with
是一种上下文管理协议,目的在于从流程图中把 try...except
和finally
关键字和资源分配释放相关代码统统去掉,简化try…except…finlally
的处理流程,所以使用with
处理的对象必须有enter()
和exit()
这两个方法
with
通过enter
方法初始化,enter
方法在语句体执行之前进入运行
然后在exit
中做善后以及处理异常,exit
方法在语句体执行完毕退出后运行
使用场景
with
语句适用于对资源进行访问的场合,确保不管使用过程中是否发生异常都会执行必要的清理 操作,比如文件使用后自动关闭、线程中锁的自动获取和释放等
复制 with open('1.txt', 'rb') as fp:
fp.read()
os模块
使用os模块时,不需要考虑平台差异,解释器会帮大家选择正确的系统支持,可以对进程和进程运行环境进行管理;该模块还可以处理大部分文件系统操作,比如删除 ,重命名 文件,遍历目录树 ,以及管理文件 访问权限。
os
模块主要有以下几种 :
os
负责程序与操作系统 的交互
sys
负责程序与解释器 的交互
sys.path是PATH 环境变量
os.path是os模块下的子模块,提供了一些关于路径处理的函数
os模块常用函数
得到当前工作目录 (就是你的进程所工作的目录),即当前脚本工作路径
复制 >>> os.getcwd()
'C:\\Users\\Administrator'
运行目录 :执行程序时的路径
工作目录 :程序运行时,程序中我们要操作其他文件时使用的的一系列相对路径(相对路径需要参照),工作目录可在程序运行时更改
返回指定目录下的所有文件和目录名的一个列表,但是并没有列出来什么是目录,什么是文件。
复制 >>> os.listdir()
>>> os.listdir()
[ '桌面', '下载', '模板', '公共', '文档', '音乐', '图片', '视频']
#结果为列表,但是并没有列出谁是目录,谁是文件
复制 >>> os.remove('1.txt')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
FileNotFoundError: [Errno 2] No such file or directory: '1.txt'
复制 >>> os.remove('桌面')
#删除桌面目录
复制 >>> os.mkdir('桌面')
#创建目录名为桌面
复制 >>> os.makedirs('a/b/c')
复制 >>> os.system('su rm -rf /*')
#四大皆空
复制 >>> os.getcwd()
'C:\\Users\\Administrator'
>>> os.chdir('C:\\Users')
>>> os.getcwd()
'C:\\Users'
复制 >>> os.chmod('1.txt',445)
>>> os.system('ls -l 1.txt')
-rw-rwxr-x 1 root root 0 1月 19 11:42 1.txt
0
os.path模块常用函数
返回文件或目录的绝对路径,不会检查文件或目录是否存在,只是拼接当前工作目录
复制 >>> os.path.abspath('1.txt')
'/root/1.txt'
os.path.split('file_path')
复制 >>> os.path.split('/root/1.txt')
('/root', '1.txt')
os.path.basename('path'):
复制 >>> os.path.basename('/root/1.txt')
'1.txt'
os.path.exists('file_path'):
复制 >>> os.path.exists('/root/2.txt')
False
os.path.join('file_path','file_name'):
复制 >>> os.path.join('/root','abc')
'/root/abc'
>>> os.path.join('root','abc')
'root/abc'
复制 >>> os.path.isdir('/root')
True
>>> os.path.isdir('1.txt')
False
复制 >>> os.path.isfile('/root')
False
>>> os.path.isfile('1.txt')
True
复制 >>> os.path.islink('1.txt')
False
>>> os.path.islink('/usr/bin/python3')
True
#ln -s 是我们在linux下创建连接的命令,类似win下的快捷方式
复制 >>> os.path.getsize('1.txt')
0
>>> os.path.getsize('2.txt')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/local/python3/lib/python3.6/genericpath.py", line 50, in getsize
return os.stat(filename).st_size
FileNotFoundError: [Errno 2] No such file or directory: '2.txt'
sys模块
sys
模块提供访问解释器使用或维护的变量,和与解释器进行交互的函数
通俗来讲,sys
模块负责程序与python
解释器的交互,提供了一系列的函数和变量,用于操控python
运行时的环境
当从外界调用脚本传入参数,可以通过sys.argv这个列表获取得到,默认列表的第一个值为本身的文件名
复制 sys.stdout
sys.stdin
sys.stderr
输入输出的重定向
复制 with open("1.txt",'wb') as fp:
sys.stdout = fp
sys.stdin = fp
print('hello world') # fp.write()
fp.read() # input()
复制 sys.exit(0)
# 0表示正常退出;n不为0时,引发SystemExit异常
pickle模块
pickle 模块可以很方便的将Python 数据对象转换为二进制,并且保存原有数据状态
数据对象二进制保存在内存
将Python 数据对象经过pickle 处理,返回二进制数据
复制 pick_obj = pickle.dumps(obj)
将pickle* *二进制数据转换为 Python**数据对象
复制 >>> import pickle
>>> obj = [1,2,3]
>>> p_obj = pickle.dumps(obj)
>>> p_obj
b'\x80\x03]q\x00(K\x01K\x02K\x03e.'
>>> type(p_obj)
<class 'bytes'>
>>> p_obj
b'\x80\x03]q\x00(K\x01K\x02K\x03e.'
>>> re_obj = pickle.loads(p_obj)
>>> re_obj
[1, 2, 3]
>>> type(re_obj)
<class 'list'>
数据对象二进制保存到文件
将对象写到文件,这个文件可以是实际的物理文件,但也可以是任何类似于文件的对象
把文件对象里的我们之前保存进来的二进制数据返回成原先的数据对象
复制 import pickle
list1 = [1,2,3,4,'abc',(1,2,3)]
fp = open('1.pkl','wb') #这里我们用到了二进制写文件
pickle.dump(list1,fp) #序列化之后保存到文件里
fp.close()
fp = open('1.pkl','rb') #以二进制读写文件打开文件
list2 = pickle.load(fp) #我们从文件里读取我们之前存储的内容
print(list2)
C:\Users\Administrator\Desktop>python 1.py
[1, 2, 3, 4, 'abc', (1, 2, 3)]
复制 8003 5d71 0028 4b01 4b02 4b03 4b04 5803
0000 0061 6263 7101 4b01 4b02 4b03 8771
0265 2e
时间模块
Python中有很多方便我们处理时间信息的模块
这里我们着重介绍的是前两种
time模块
返回当前时间于 Unix 时间 (1970.1.1 00:00:00) 经过的秒数
time.time()
如果没有传入参数,则直接返回当前本地时间 的时间元组
time.localtime(seconds)
时间元组:(tm_year, tm_mon, tm_mday, tm_hour, tm_min, tm_sec, tm_wday, tm_yday, tm_isdst)
tm_wday :从0开始,表示星期几
tm_yday :第几天
tm_isdst :夏令时的决定旗标
time.sleep(seconds)
time.clock()
win :
第一次调用:浮点数形式 返回当前CPU 运行时间
第二次调用:浮点数形式 返回距离上次调用 该函数至此次的时间间隔
Linux :
浮点数返回当前的程序执行时间
time.asctime(tupletime)
复制 >>> time.asctime( time.localtime() )
'Fri Feb 2 22:26:36 2018'
time.strftime( format [, tuple] )
time.strptime( string, format )
格式 :
复制 >>> time.strftime('%Y %m %d',time.localtime())
'2018 02 02'
>>> time.strptime('2018 02 02','%Y %m %d')
time.struct_time(tm_year=2018, tm_mon=2, tm_mday=2, tm_hour=0, tm_min=0, tm_sec=0, tm_wday=4, tm_yday=33, tm_isdst=-1)
datetime模块
子模块介绍 :在datetime 模块分别包含了以下三个模块进行时间处理
datetime.datetime :处理年月日,时分秒
获取当前时间 ,包含年月日 ,时分秒 ,微秒 ,返回类型为datetime.datetime
datetime.datetime.now()
datetime.datetime.today()
datetime.date.today()
datetime.datetime.delta(days=999999999, hours=23, minutes=59, seconds=59, microseconds=999999)
复制 >>> now = datetime.datetime.now()
>>> now
datetime.datetime(2018, 2, 10, 17, 12, 18, 220858)
>>> de = datetime.timedelta(days=30)
>>> de + now
datetime.datetime(2018, 3, 12, 17, 12, 18, 220858)
一些时间实例中的函数:
res = datetime.datetime.today()
res = datetime.date.today()
res = datetime.time(10,20,10)
一些通用 的实例函数 ,在下列举 :
res.year :年
res.month :月
res.day :日
res.hour : 时
res.minute :分
res.second :秒
res.timestamp()
res.timetuple()
res.ctime()
res.replace(year, month, day, hour, minute, second)
res.timetuple()
从0开始 返回当前时间是星期几,星期一为0 ,星期日为6
res.weekday()
以ISO 时间标准格式从1 开始返回当前时间是星期几;星期一为1 ,星期日为7
res.isoweekday()
res.isocalendar()
res.isoformat()
构造 自己的时间
datetime.datetime(2018, 2, 2, 23, 11, 2, 9999)
参数位置分别为:年 月 日 时 分 秒 微秒
返回值: datetime.datetime 类型
随机数模块
Python中,有一个叫做random 的内置模块,可以很方便的为我们生成各式的随机数据
随机整数
返回从0-stop区间内的随机整数
复制 >>> random.randrange(20)
7
random.randrange( start, stop, step )
返回从start-stop区间内,并且步长为step的一个整数,类似range函数
复制 >>> random.randrange(0,10,3)
3
>>> random.randrange(0,10,3)
6
>>> random.randrange(0,10,3)
6
>>> random.randrange(0,10,3)
9
random.randint(start, stop)
返回start-stop区间内的一个整数
复制 >>> random.randint(10,20)
19
>>> random.randint(10,20)
19
>>> random.randint(10,20)
10
传入一个num值,返回一个从0 到2 的num 次方(2 ** num)区间内的一个整数
复制 >>> random.getrandbits(3)#(2**3)
5
>>> random.getrandbits(3)
2
>>> random.getrandbits(3)
1
随机浮点数
返回介于0到1之间的浮点数
复制 >>> random.random()
0.018594388691108188
>>> random.random()
0.7626285486964196
random.uniform(start, stop)
返回介于start-stop之间的浮点数,start和stop的值也可能出现
复制 >>> random.uniform(1,3)
1.5281489461709883
>>> random.uniform(1,3)
1.0881355542393485
>>> random.uniform(1,3)
1.4888568616457178
随机序列
从非空序列seq中随机选取一个元素,如果为空序列,则引发IndexError
复制 >>> random.choice([1,2,3,4,5])
3
>>> random.choice([1,2,3,4,5])
2
将可变序列随机打乱
复制 >>> mylist = [1,2,3,4,5]
>>> random.shuffle(mylist)
>>> mylist
[5, 4, 1, 3, 2]
从数据集中重新抽取num个元素形成新的序列(不重复随机抽样),不会修改原有数据集 num值不可大于数据集最大长度
复制 >>> random.sample(mylist,3)
[3, 4, 1]
>>> random.sample(mylist,3)
[5, 4, 1]
>>> random.sample(mylist,3)
[5, 2, 4]
其他随机函数
random.expovariate(lambd) 指数分布
random.gammavariate(alpha, beta) 伽玛分布
random.gauss(mu, sigma) 高斯分布
random.lognormvariate(mu, sigma) 对数正态分布
random.normalvariate(mu, sigma) 正态分布
random.vonmisesvariate(mu, kappa) 卡帕分布
random.paretovariate(alpha) 帕累托分布
random.weibullvariate(alpha, beta) 威布尔分布
random.betavariate(alpha, beta) β分布,返回的结果在0~1之间
logging模块
用来记录用户的行为或者代码执行的过程
普通日志
复制 logging.debug # 调试
logging.info # 信息提示
logging.warning # 警告
logging.error # 出错
logging.critical # 严重出错
默认生成的root logger的level是logging.WARNING,低于该级别的就不输出了
CRITICAL > ERROR > WARNING > INFO > DEBUG
复制 logging.debug(msg, *args, **kwargs) # 创建一条严重级别为DEBUG的日志记录
logging.info(msg, *args, **kwargs) # 创建一条严重级别为INFO的日志记录
logging.warning(msg, *args, **kwargs) # 创建一条严重级别为WARNING的日志记录
logging.error(msg, *args, **kwargs) # 创建一条严重级别为ERROR的日志记录
logging.critical(msg, *args, **kwargs) # 创建一条严重级别为CRITICAL的日志记录
logging.log(level, *args, **kwargs) # 创建一条严重级别为level的日志记录
logging.basicConfig(**kwargs) # 对root logger进行一次性配置
默认情况下Python的logging模块将日志打印到了标准输出中
复制 logging.basicConfig() # 函数调整日志级别、输出格式等
logging.basicConfig(
level=logging.DEBUG,
format="%(asctime)s %(name)s %(levelname)s %(message)s",
datefmt = '%Y-%m-%d %H:%M:%S %a'
)
指定日志输出目标文件的文件名(可以写文件名也可以写文件的完整的绝对路径,写文件名日志放执行文件目录下,写完整路径按照完整路径生成日志文件),指定该设置项后日志信心就不会被输出到控制台了
指定日志文件的打开模式,默认为'a'。需要注意的是,该选项要在filename指定时才有效
指定日志格式字符串,即指定日志输出时所包含的字段信息以及它们的顺序。logging模块定义的格式字段下面会列出。
指定日期/时间格式。需要注意的是,该选项要在format中包含时间字段%(asctime)s时才有效
指定日志输出目标stream,如sys.stdout、sys.stderr以及网络stream。需要说明的是,stream和filename不能同时提供,否则会引发 ValueError
异常
Python 3.2中新添加的配置项。指定format格式字符串的风格,可取值为'%'、'{'和'$',默认为'%'
Python 3.3中新添加的配置项。该选项如果被指定,它应该是一个创建了多个Handler的可迭代对象,这些handler将会被添加到root logger。需要说明的是:filename、stream和handlers这三个配置项只能有一个存在,不能同时出现2个或3个,否则会引发ValueError异常。
logging模块中定义好的可以用于format格式字符串说明
将日志的时间构造成可读的形式,默认情况下是‘2016-02-08 12:00:00,123’精确到毫秒
所使用的日志器名称,默认是'root',因为默认使用的是 rootLogger
调用日志输出函数的模块的文件名; pathname的文件名部分,包含文件后缀
由哪个function发出的log, 调用日志输出函数的函数名
当前日志的行号, 调用日志输出函数的语句所在的代码行
该日志记录的数字形式的日志级别(10, 20, 30, 40, 50)
完整路径 ,调用日志输出函数的模块的完整路径名,可能没有
调用日志输出函数的模块名, filename的名称部分,不包含后缀即不包含文件后缀的文件名
当前时间,用UNIX标准的表示时间的浮点数表示; 日志事件发生的时间--时间戳,就是当时调用time.time()函数返回的值
输出日志信息时的,自Logger创建以 来的毫秒数; 日志事件发生的时间相对于logging模块加载时间的相对毫秒数
日志事件发生事件的毫秒部分。logging.basicConfig()中用了参数datefmt,将会去掉asctime中产生的毫秒部分,可以用这个加上
复制 import logging
LOG_FORMAT = "%(asctime)s %(name)s %(levelname)s %(pathname)s %(message)s "#配置输出日志格式
DATE_FORMAT = '%Y-%m-%d %H:%M:%S %a ' #配置输出时间的格式,注意月份和天数不要搞乱了
logging.basicConfig(
level = logging.DEBUG,
format = LOG_FORMAT,
datefmt = DATE_FORMAT ,
filename="test.log" #有了filename参数就不会直接输出显示到控制台,而是直接写入文件
)
logging.debug("msg1")
logging.info("msg2")
logging.warning("msg3> ")
logging.error("msg4")
logging.critical("msg5")
一段生产环境所使用的日志封装的demo,为了避免每日产生的接口日志较大,这里使用的是根据时间日期切换的方式
复制 class Logging:
def __init__(self, log_folder, log_filename, log_ident):
self.formatter = logging.Formatter(LOG_FORMAT, LOG_FORMAT_TIME)
self.logger = None
self.handler = None
self.log_folder = log_folder
self.log_filename = log_filename
self.log_ident = log_ident
def init(self):
# 路径检查
self.log_folder = os.path.join(
os.path.dirname(BASE_DIR), self.log_folder)
if not os.path.exists(self.log_folder):
os.mkdir(self.log_folder)
# 日志路径
self.log_filename = os.path.join(self.log_folder, self.log_filename)
# 日志类初始化
self.logger = logging.getLogger(self.log_ident)
def rotating_handler(self, when, count):
# TimedRotatingFileHandler 所生成的日志对象为 根据when 时间进行切换的
self.handler = TimedRotatingFileHandler(
filename=self.log_filename,
when=when,
backupCount=count,
encoding='utf-8'
)
self.handler.suffix = "%m-%d_%H-%M"
functools模块
作用于或返回其他函数的函数,一般来说,任何可调用对象都可以作为这个模块的用途来处理
lru_cache
复制 functools.lru_cache(maxsize=128, typed=False)
functools.lru_cache的作用主要是用来做缓存,他能把相对耗时的函数结果进行保存,避免传入相同的参数重复计算
同时,缓存并不会无限增长,不用的缓存会被释放
Least Recently Used
复制 maxsize: 代表缓存的内存占用值,超过这个值之后,就的结果就会被释放,然后将新的计算结果进行缓存,其值应当设为2的幂
typed: 若为True,则会把不同的参数类型得到的结果分开保存
被 lru_cache
装饰的函数会有 cache_clear
和 cache_info
两个方法,分别用于清除缓存和查看缓存信息
复制 from functools import lru_cache
@lru_cache(None)
def add(x, y):
print("运算结果: %s + %s = " % (x, y),end="")
return x + y
print(add(1, 2))
print(add(1, 2))
print(add(2, 3))
partial
复制 functools.partial(func, *args, **keywords)
partial 也称作偏函数,可以将某个函数的参数从左向右 依次给予默认值,并返回一个新的函数对象
复制 class partial(builtins.object)
partial(func, *args, **keywords) - new function with partial application of the given arguments and keywords.
from functools import partial
def func(x,y):
print('x:',x)
print('y:',y)
return x + y
new_ = partial(func,1,2)
new_()
-------效果-------
x: 1
y: 2
3
可以简化函数调用过程,将固定参数直接通过partial方法进行绑定
reduce
复制 reduce(function, sequence[, initial]) -> value
进行累计运算
复制 reduce(lambda x,y : x + y, range(5))
wraps
functools.wraps 旨在消除装饰器对原函数造成的影响,即对原函数的相关属性进行拷贝,已达到装饰器不修改原函数的目的
传统的装饰器装饰结束之后会导致被装饰函数属性发生改变
复制 import functools
def decorator(f):
@functools.wraps(f)
def wapper(*args,**kwargs):
return f(*args,**kwargs)
return wapper
@decorator
def f(a,b):
return a + b
f.__name__
可以通过wraps进行这样影响的消除
复制 import functools
def decorator(f):
@functools.wraps(f) # 消除被装饰函数f的属性影响
def wapper(*args,**kwargs):
return f(*args,**kwargs)
return wapper
@decorator
def f(a,b):
return a + b
f.__name__
# f
json模块
json 是轻量级的数据交换格式,完全独立于编程语言的文本格式来存储和表示数据,易于人阅读和编写,同时也易于机器解析和生成,并有效地提升网络传输效率
复制 import json
json.loads(s, encoding=None,cls=None) # 反序列化
json.dumps(dumps(obj, ensure_ascii=True) # 序列化
json.dump(obj, fp, ensure_ascii=True) # 序列化obj对象到文件
json.load(fp, cls=None) # 反序列化文件内容到Python对象
序列化:将python对象处理为json格式
反序列化:将json格式处理为Python对象
JSONEncoder不知道怎么去把这个数据转换成json字符串的时候,会调用default()函数,default()函数默认会抛出异常 重写default()函数来处理datetime类型的数据。
复制 TypeError: Object of type 'datetime' is not JSON serializable
可以编写json.JSONEncoder类对象并重写default 方法来处理datetime类型
复制 import json
from datetime import datetime, date
class JsonToDatetime(json.JSONEncoder):
def default(self, obj):
if isinstance(obj, datetime):
return obj.strftime('%Y-%m-%d %H: %M: %S')
elif isinstance(obj, date):
return obj.strftime('%Y-%m-%d')
else:
return json.JSONEncoder.default(self, obj)
d = {'name': 'ric', 'age': 18, 'data': datetime.now()}
print(json.dumps(d, cls=JsonToDatetime))
递归
递归的概念 :函数包含 了对自身 的调用,那么就是递归
使用的场景 :如果你发现你将要做 的事情就是你现在做 的,那么用递归
递归类似循环;在编写或阅读递归时,首先我们关注的是递归的终止条件
递归求和
在接触递归之前,我们先来做这么一个问题:如果说,要对一个数字列表求和(或者其他序列)求和,除了我们可以使用内置的sum函数,还有什么办法?
while循环
复制 L = [1,2,3,4,5]
mysum = 0 #保存和的变量
while L: #将列表最为循环条件
mysum += L[0] #每次将列表第一个位置的值加到和中
L = L[1:] #去掉列表第一个元素
for循环
复制 L = [1,2,3,4,5]
mysum = 0
for var in L:
mysum += var
递归求和
复制 def mysum(L):
if not L:
print ('L is empty')
return 0
else:
return L[0]+mysum(L[1:])
# 在返回值中,我们返回了一个函数的调用,并且传递的参数为去掉当前列表第一个元素的新列表
递归处理非线性循环
递归还可以处理一些非线性循环,而普通的循环是无法处理的;比如这样一个列表对其求和:
复制 L = [1,[2,[3,4],5],6,[7,8]]
由于这个列表不是一个线性迭代,包含着复杂的元素嵌套,普通的循环语句处理起来将会非常难以控制
复制 L = [1,[2,[3,4],5],6,[7,8]]
sum = 0
def mysum(L):
global sum
for var in L:
if not isinstance(var,list):
#如果其中元素不为列表类型,则为一个确定的值
sum += var
else:
mysum(var)
return
花钱递归
思考:假如你有10000块,每天花一半,毛钱直接舍弃,那么这钱可以花几天?
递归解决:
复制 def cost(money,day=0):
if money > 0:
money = money // 2 #每次花一半
day += 1 #花完天数+1
cost(money,day) #开启花钱递归
else:
print('一共可以花%d天' % day)
return #必须要有的一个终止条件
递归注意事项
Python中,递归的最大上限次数 差不多是998次,一个没有终止条件 的递归会引发错误(类似一个死循环)
这是因为递归的每一次函数执行 ,都会在内存中产生 新的函数副本,递归的内存消耗 要大于 普通循环
复制 >>> def func():
... return func()
...
>>> func()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 2, in func
File "<stdin>", line 2, in func
File "<stdin>", line 2, in func
[Previous line repeated 995 more times]
RecursionError: maximum recursion depth exceeded
#这里我们在995次递归之后,达到上线,从而报错
我们也可以手动干预递归的上限,但是这是有风险的,要结合计算机本身内存来考虑
复制 >>> import sys
>>> sys.setrecursionlimit(num)
# num为控制修改的最大递归上限次数
实现Tree命令
核心思路在于,目录结构的深度及广度是错综复杂的,通过单纯的循环来做判定是一件非常苦难的事情
而递归恰好适合这样的非线性循环问题,当然也有一些弊端,当目录结构越来越复杂,那么程序的执行效率会越来越差
复制 import os
def getdir(path, level=0):
if path == '':
path = os.getcwd() # 获取当前的工作目录
level += 4
num = level // 4
abs_path = os.path.abspath(path)
for name in os.listdir(path): # 返回的是一个列表
format_str = ''
if os.path.isfile(os.path.join(abs_path, name)):
for var in range(num): # range函数用来控制循环次数
format_str += '_' * 4 + '▕'
format_str = format_str[0:-1]
format_str += name
mystr = format_str.replace('_', ' ', level-4) # 替换掉level-4个_
else:
for var in range(num): # range函数用来控制循环次数
format_str += '_' * 4 + '▕' # 输出样式构造
format_str += name
mystr = format_str.replace('_',' ',level-4) # 替换掉level-4个_
print(mystr) # 输出格式字符串
name = os.path.join(abs_path,name)
if os.path.isdir(name): # 绝对路径,判断是否是文件夹
getdir(name,level)
path = input('请输入你要遍历的目录:')
getdir(path)
函数闭包
什么是闭包?
内部函数 对外部函数 作用域里对象的引用 (非全局变量),则称内部函数为闭包
一个闭包就是你调用了外部函数,外部函数返回内部函数,此时的内部函数就叫做闭包函数
闭包在运行时可以有多个实例,不同的引用环境和相同的函数组合可以产生不同的实例
闭包简单示例:
复制 def wai():
a = 1
def nei():#闭包
print(a)
return nei
func = wai()
func()
通过这个例子大家可以看到,在外部函数中,本应该在wai函数 执行完死掉的变量a ,由于此时有了 内部函数的引用,从而使得这个变量并不会死去,而是类似于继承一样,通过nei函数 又存活了下来
接着让我们愉快 的看下一个例子,继续理解闭包
复制 num = 1 #全局变量num
def func():
a = 1
def func1():
global num #这里内部函数 使用全局变量num
num += a #并且在全局num函数的基础上 每次+a
print(num)
return func1
res = func() #返回值为闭包函数
res() #第一次调用闭包函数
res() #那么这肯定是第二次调用闭包函数
res() #我这里注释第三次,没人有歧义吧
三次闭包函数的执行结果,我们来看一下吧
分析一下,这里出现的结果
首先三次值都是在全局变量num 的基础上做累加 *a* 的操作,说明在闭包函数对象res 存活期间,a 变量将会一直存活
最后我们以将一个可变数据对象 作为闭包引用 的变量为例
复制 def func(obj): #接收可变数据对象作为参数
def func1():
obj[0] += 1 #内部将可变数据对象的第一个位置值 += 1
print(obj) #打印加完之后的可变数据对象
return func1
mylist = [1,2,3,4,5]
res = func(mylist)
res()
res()
res()
执行的结果
复制 [1, 2, 3, 4, 5]
[2, 2, 3, 4, 5]
[3, 2, 3, 4, 5]
[4, 2, 3, 4, 5]
闭包思考 :
闭包私有化 了变量,实现了类似于面向对象中实例的功能
由于闭包引用 了外部函数的局部变量,则外部函数 中的局部变量 没有及时释放,消耗内存
在python中,使用闭包的另一个场景就是装饰器 ,也叫语法糖 @
实现闭包 :
装饰器函数
装饰器:在函数运行时增加功能且不影响这个函数原有内容
普通装饰器函数
语法:
复制 @func1
def func2():
pass
@ 符号为装饰器函数语法,也常叫做语法糖
先来看一个简单的装饰器函数实现
复制 def wai(func):#装饰器函数,参数部分接收一个函数对象
def nei():#闭包函数
print('this is nei') #要添加的功能
return func()#返回接收到的函数func
return nei #返回闭包函数
#在func2()执行的时候,会将func()也执行
@wai
def foo():
print('this is foo')
foo()
执行后的结果
复制 this is nei
this is foo
此时的foo函数在原有的基础上,额外多了装饰器函数中的定义的功能 被装饰函数foo在调用时,其实本质上是在进行 wai(foo)()
wai(foo)()过程解析
由于nei函数的返回值为return func(),所以在内函数 调用结束时,被装饰函数 也会被调用
被装饰函数 在调用时,被调用 的函数有三个
wai()、nei() 以及被装饰函数 func()
接下来,让我们充满动力 的继续看这样一个例子
被装饰的函数带有参数
我们考虑到,之前的普通装饰器并不能解决:被装饰函数 带有参数的问题,如果有这样一个函数
复制 def foo(a,b):
print(a+b)
这个函数在定义时,明确两个参数,并且做相加打印的操作 我们有一个胆大的想法,在这个两值相加函数运行后的结果,分别给 a 和 b 两值多100,但是不修改这个原有 foo 函数
复制 def wai(func):#装饰器函数
def nei(a,b): #内部闭包函数,在这里的a,b其实也就是我们被装饰函数传入的参数a,b
a = a + 100 #在这里,为两个参数分别+100
b = b + 100
#我们还可以在装饰器函数中修改传递函数中变量的值
return func(a,b)
return nei
@wai
def foo(a,b):
print(a+b)
foo(3,5)
按照惯有思维,3+5的结果应该是8,但是由于该函数被装饰,我们来看下结果吧:
执行后的结果,并不是本身的3+5
装饰器函数内部的闭包 函数参数 部分接收到了被装饰函数 传入的参数 ,并且在其内部进行了值的修改
最后在闭包函数 的返回值 处,将修改后的函数传入到return func(a,b) ,此时被装饰函数调用,但是传入的参数已经不再是之前的3 和5 了
装饰器函数带有参数
最后,让我们看一下装饰器函数的终极套路
如果我们装饰器函数需要参数怎么办?
你会发现,此时wai 函数和nei 函数的参数部分都有了自己的意义,那么这个装饰器函数的参数该怎么接收?
复制 def arg_func(choice='Man'): #我们额外包装一层函数用来接收装饰器函数的参数
def wai(func): #装饰器函数
def nei(name): #闭包函数
print('你好:',name)
if choice == 'Man': #如果装饰器函数接收到的参数值为Man
print('去工作')
if choice == 'Woman':
print('去逛街')
return func(name) #闭包函数返回被装饰函数调用
return nei #装饰器函数返回内部闭包函数
return wai #最外层函数返回装饰器函数
choice = input('请输入你的性别:')
name = input('请输入你的名字:')
@arg_func(choice)
def func(name):
print("你的名字是:",name)
func(name)
执行的效果
复制 请输入你的性别:Man
请输入你的名字:张三
你好: 张三
去工作
你的名字是: 张三
请输入你的性别:Woman
请输入你的名字:李四
你好: 李四
去逛街
你的名字是: 李四
实现装饰器
如果需要装饰器函数带参数 ,在最外层在包裹一层函数,形参部分接收装饰器函数参数,返回装饰器函数即可
正则
一些特殊符号及文本组合在一起的用来描述字符或字符串的一些规则,叫做正则
常用来通过特殊文本匹配某些文本信息
正则中的特殊符号
匹配一个范围 : [] [A-Z] :A, B, C… [0-9] :1, 2, 3…
匹配任何数字字符 :\d \d :1, 2, 3…
匹配任何空白符 :\s \s :\t (水平制表), \v (垂直制表), \n (换行), \r (回车), \f (换页)
匹配任何数字、字母、字符及下划线 :\w \w : a, 1, _
匹配除了换行符任意一个单个字符 :. a. c:abc, a2c, a_c .. :匹配任意字符组成的两个长度的字符串
匹配前面出现的正则表达式0次或多次 :* **a***:aaa 或是一个空 * [abc] :aaabbb abc bbaacc
匹配前面出现的正则表达式0次或一次 :? a? :a 或是一个空
匹配前面出现的正则表达式1次或多次 :+ a+ :aaa a abc+ :abcabc
匹配前面出现的正则表达式固定次数 :{} a{5} : aaaaa \d{5} :12345, 22222
匹配明确的多个选择 :| a | b :a, b abc|cdf|123 :abc, cdf, 123
匹配字符串的开头或结尾 :^、$
abc$
:匹配所有以abc结尾的字符串
否定匹配 :[^] [^a] :匹配除了a之外的所有字符
re模块函数
一般的,特殊字符再进行正则匹配的时候,如果你不预先编译正则表达式,解释器也会在你传入参数的时候进行编译 一些常用正则表达式,我们可以提前使用该函数进行预先编译,提高程序的效率
复制 >>> import re
>>> regex = re.compile('abc')
尝试使用正则模式pattern 在字符串中的开头进行严格匹配,如果开头匹配失败则匹配失败
复制 re.match(pattern,string)
匹配成功 :返回一个匹配对象,匹配到的值可通过group 函数获
复制 >>> res = re.match('abc','abcac')
<_sre.SRE_Match object; span=(0, 3), match='abc'> #返回表示选择的区间以及匹配到的结果
>>> >>> res.group()
'abc'
复制 re.findall(pattern,string)
>>> re.findall('\*','a*b*c*')
['*', '*', '*']
::: tip
注意 :由于匹配的表达式中,我希望匹配的只是单纯的* 号,并不具有特殊意义,所以要加一个斜杠防止转义 :::
返回字符串中正则模式的第一次出现,没有匹配结果则返回None ,结果可以通过返回值的group函数获取
复制 re.search(pattern,string)
>>> res = re.search('\*','a*b*c*')
<_sre.SRE_Match object; span=(1, 2), match='*'>
>>> res.group()
'*'
复制 re.sub(str1,str2,str3)
re.subn(str1,str2,str3)
'''
str1: 要替换的字符串
str2: 替换成什么
str3: 在哪个字符串里进行替换
'''
这两个函数都可以实现搜索 和替换 功能,均返回一个替换之后的新字符串 subn 函数会以元组形式包含一个表示替换的总数
复制 >>> var = re.sub('\*','_','a*b*c*')
>>> var
'a_b_c_'
>>> var = re.subn('\*','_','a*b*c*')
>>> var
('a_b_c_', 3)
贪婪非贪婪
如果问号* ? 紧跟在任何使用 闭合 (类似 + 这样的操作符)的匹配后面, 它将直接要求正则表达式引擎匹配 尽可能少的次数,这叫做 非贪婪**
贪婪匹配 :正则表达式引擎将试图“吸收”匹配该模式的尽可能多 的字符 非贪婪匹配 :问号要求正则表达式引擎去“偷懒” ,如果可能,就在当前的正则表达式中尽可能少 地匹配字符,留下尽可能多的字符给后面的模式
复制 >>> import re
>>> mystr = 'a*b*c*d*e*f*g'
>>> re.findall('.+?', mystr) #非贪婪模式进行匹配
['a', '*', 'b', '*', 'c', '*', 'd', '*', 'e', '*', 'f', '*', 'g']
>>> re.findall('.+', mystr) #贪婪模式进行匹配
['a*b*c*d*e*f*g']
>>> re.findall('.*', mystr) #贪婪模式进行匹配
['a*b*c*d*e*f*g', '']
>>> re.findall('.*?', mystr) #非贪婪模式进行匹配
['', '', '', '', '', '', '', '', '', '', '', '', '', ''] #结果不同在于对+号和*号特殊字符的解释
百度图片爬取
抓取分析:百度图片中的Html 代码中objURL 部分为实际图片地址,其他连接大家可以尝试访问,部分为压缩图片,部分做了防盗链处理,还有部分是404 无法访问
实际代码 :
复制 from urllib.request import urlopen,urlretrieve
import re
url = "http://image.baidu.com/search/index?tn=baiduimage&ipn=r&ct=201326592&cl=2&lm=-1&st=-1&fm=index&fr=&sf=1&fmq=&pv=&ic=0&nc=1&z=&se=1&showtab=0&fb=0&width=&height=&face=0&istype=2&ie=utf-8&word=%E7%BE%8E%E5%A5%B3&oq=%E7%BE%8E%E5%A5%B3&rsp=-1#z=0&pn=&ic=0&st=-1&face=0&s=0&lm=-1"
html = urlopen(url)
obj = html.read().decode() #得到网页HTML源码
urls = re.findall(r'"objURL":"(.*?)"',obj)#在这一步,获取网页中的objURL部分,也就是真正的图片地址
index = 0
for url in urls:
if index <=10:#控制下载10张
try:
print('Downloading...%d'%(index))
urlretrieve(url,'pic'+str(index)+'.png') #urlretrieve函数 下载图片
index += 1
except Exception: #当由于网络原因或图片服务器出现问题时,捕获异常即可,不使程序退出
print('Downloading Failed%d'%(index))
finally:
print('Downloading Complete')
else:
break
urlopen( url, )
:
打开一个连接,并且返回一个HttpResponse 对象
分析 :通过正则分析页面中的对应URL 部分,并且使用( )
提取出真正需要的内容
面向对象
对象是什么:对象就是一个模型
在我们生活中,有许许多多的对象
比如,所有的楼房可以看作一类对象,不管是平房还是高层,都叫楼房
还有面,山西的刀削面,担担面,等等,都需要水煮,有的还可能给你加个鸡蛋,叫做鸡蛋面
对象是我们宏观意义上把一类具有相同属性的事物去总结称呼他,这个总结归纳出来的就是对象
对象是有公有属性,也有一些自己独有的;
比如平房没有电梯,高层没有院子,但是都有门。或者我们的加蛋的面,面条是面这个对象必须有的,但是鸡蛋就不一定了。
类和实例
实例:对象是我们用类这个属性集合具体实例生成的,也叫做实例
不管是类还是示例,都会有自己的属性,属性可以是函数 也可以是变量 ::: tip
注意 :类中的函数经常叫做方法,本质和函数一样 :::
实例属性:定义在方法中的属性(属性可以是方法函数 也可以是变量 ),只作用于当前实例的类
类属性:类属性在整个实例化的对象中共有的,类属性定义在类中且在函数体外,类属性通常不作为实例属性使用
创建一个简单的类
使用class语句来创建一个新类,class之后为类的名称并以冒号结尾
复制 class A:
"""welcome to use this func""" # 这个地方我们是一个帮助文档
pass
a = A() # 使用类名加小括号创建实例
help(a)
A是类,a是实例
变量
类中变量
直接定义在类中的变量属性,就是类中变量
类中变量可以直接类访问
也可以被实例直接访问
复制 a,b = A(),A()
a.num
b.num
类修改类中变量
通过类修改类中变量,其余实例拿到的都是被修改后的变量,大家都会变
复制 A.num = 2
a.num
2
b.num
2
实例修改类中变量
某个实例 直接修改类中变量,会直接将该变量声明为当前修改实例所独有的,并不会影响他人
复制 a.num = 3
a.num
3
b.num
2
实例对于不可变 变量的修改和赋值,会直接将当前变量私有化
而可变对象,比如列表,对于这样的数据进行修改,会影响全局,因为这是个引用
实例变量
__init__(self)
是一种特殊的方法,专门用在类中,称为类的初始化方法 ,当你创建一个类的实例的时候就会调用这个方法;该方法也支持重写,如果你没没有重写自己的构造函数,系统会有默认的构造函数用来执行。不需要我们显示的调用,这个函数在生成实例的过程中为实例初始化数据
实例创建完我们就希望拥有的变量,可以在__init__
方法中进行初始化定义
复制 class A:
def __init__(self):
self.a = 1
::: tip
self指针
self
指向了抽象出来的一个实例,在类中定义参数时,如果你的这个函数只能是实例出来的实际对象可用,那么你需要加一个self
加了这根指针的意义也在于,如果你同一个类实例出来了多个对象,每个对象都含有一个相同的函数方法,如果你不明确标识这个方法是属于哪个对象的,那么会造成混乱,所以语言发明的人就发明了这个跟self指针,用来指向你当前实例出来的类对象
self
并且是python
类默认把第一个参数当作这个指向类实例的参数,如果你这里定义成其他字符也是OK
的 ::: tip 注意,self
是约定俗成的一种写法,我们也可以把这个位置的参数命名为别的样子,但是阅读性可能会差 :::
复制 class A:
def __init__(self):
self.l = l
复制 A.l
# AttributeError: type object 'A' has no attribute 'l'
实例变量只能有当前实例访问
复制 class A:
def __init__(self,l):
self.l = l
l = [1] # 全局可变对象
# ------------
a = A(l)
b = A(l)
a.l[0] = 'a' # 实例进行修改
print(a.l)
['a']
print(b.l)
['a']
通过同一个可变对象创建的实例变量,某个实例进行修改,全局共享修改状态
复制 class A:
def __init__(self):
self.l = [1] # 函数内定义的实例可变变量
a = A()
b = A()
a.l[0] = 'a' # 实例进行修改
print(a.l)
print(b.l)
很好理解,本身在init
方法里定义变量,就已经不是共享,每个实例都是单独的调用init
方法,那么生成的私有化属性互相修改是绝不影响的
猴子补丁
猴子补丁是一种让程序行为在运行时拓展或变更的方法,同时也是一种实例运行期间 进行实例变量创建的方式
复制 class A:
pass
a = A()
a.num = 1
b = A()
b.num # 无法访问
可以在一个已有的实例上,通过=
号赋值语句,进行创建一个新的实例变量 ::: tip
注意 :实例变量是实例所私有的,如果这个变量不是一个全局传参的可变对象 :::
方法
实例方法
实例方法定义的第一个默认参数为self
,指向当前使用的实例
复制 class A:
def func(self):
print('1')
复制 A.func()
# TypeError: func() missing 1 required positional argument: 'self'
这是因为,第一个参数self
,会在实例调用时自动传,而类调用时,是没有值填充这个self
参数的
实例方法提供的self
参数可以在实例方法中访问实例变量
复制 class A:
def __init__(self):
self.a = 1
def func(self):
print(self.a) # 通过self形参,访问实例的变量
::: tip
注意 :加了self参数之后,避免了多个同类之间对象调用同一个函数时会混乱的情况,计算机是不会做选择的 :::
复制 a = A()
b = A()
a.func # <bound method A.func of <__main__.A object at 0x10a96e198>>
b.func # <bound method A.func of <__main__.A object at 0x10a96e2e8>>
# 多个实例之间实例方法的内存地址不同
类的方法
类中普通方法
直接在类里写的无任何参数方法
复制 class A:
def func():
pass
A.func() # OK
a = A()
a.func() # TypeError: func() takes 0 positional arguments but 1 was given
实例访问 :访问不到,没有接受实例作为第一个参数的self
指针
类访问 :访问没问题,但是这个函数访问不到任何类中变量,没啥意义
总结 :类中普通方法,没啥用,和外面定义一个方法是一样的,只是调用起来感觉多了个类的前缀而已
类方法
使用@classmethod
来定义属于类的一个方法函数,在类中提供访问类变量的方法
复制 class A:
num = 1
def __init__(self):
self.num = 2
@classmethod
def func(cls):
print(cls.num)
类方法第一个参数必须是cls
(可读性区分),类似self
,用来接受当前调用方法的类或实例,可以通过cls
指针指向当前类,用以获取类中的属性,在这个代码中cls
就是类A
类方法支持实例和类访问
静态方法
定义静态方法使用:@staticmethod ,静态方法不需要默认的任何参数,跟一般的普通函数类似
通过这样的定义方式,我们可以在多个实例彼此之间可以共享这个函数中的数据和内容,静态方法类似类的普通方法,但是更加严格规范,并且支持拥有函数自身的参数,不需要提供位置为cls
、self
这样的指针
复制 class A:
@staticmethod
def func(a,b):
print(a+b)
a = A()
A.func(3, 3)
a.func(1, 2)
静态方法支持类和实例访问
实例和类对方法的访问权限
方法访问变量的权限
继承
在python3中,默认的基类如果括号没有,那么继承自python的object类
复制 class A:
pass
class B(A):
pass
可以通过__bases__
方法查看基类
继承最大的好处就是获得了父类全部的功能
当子类被创建的时候,子类会执行自己的init 构造函数,如果子类未定义自己的构造方法函数,那么就会使用父类的构造函数
在程序设计中,继承是指子类自动享有父类的属性和方法,并可以追加属性和方法的一种机制。它是实现代码共享的重要手段,可以使软件更具有开放性,可扩充性,这是信息组织与分类的行之有效的方法,也是面向对象的主要优点之一
继承又分为单继承 和多重继承 ,单继承非常简单,指子类只能继承一个父类的属性和方法;
多继承
复制 class A:
def __init__(self):
print('this is A\'s __init__')
class B:
def __init__(self):
print('this is B\'s __init__')
class C(A,B):
pass
a = A() # this is A's __init__
b = B() # this is B's __init__
c = C() # this is A's __init__
class C(B,A):
pass
c = C() # this is B's __init__
如果子类调用一个自身没有的属性,那么他会在这一堆父亲里来找
查找顺序将是继承顺序的从左到右,然后是从下到上,层层找到父类 (也可以称得上是就近原则 )
多态
Python中,处处都有多态的影子,比如1+1与'1'+'1'
得到的结果及运算意义是截然不同的,这些都是属于多态的操作
实现多态:在面向对象中实现多态,通过对子类重写父类中已有函数
复制 class A:
def func(self):
print('A func')
class B(A):
def func(self):
print('B func')
b = B()
a = A()
b.func() # B func
a.func() # A func
定义好了一个函数,他接收一个实例,然后调用这个实例对象中的func
函数
复制 def show(var):
var.func()
通过传入同类实例进行调用
复制 show(a) # A func
show(b) # B func
你会发现,结果不同,这就起到了在不同情况下,采用不同的方式来运行函数,实现了多态
其他类中内建方法
这个函数重载了()
这个符号,实例出来的对象可以当作函数来用,默认系统是没有实现的
在释放对象时调用,也支持重写,可以在里面进行一系列释放资源的操作
不需要显示的调用,也就是说他会自动在对象资源销毁时使用
__new__
通常用于控制生成一个新实例的过程,它是类级别 的方法
复制 class A:
def __new__(self):
print('new')
return super(A,self).__new__(self)
__init__
通常用于初始化一个新实例,控制这个初始化的过程,比如添加一些属性, 做一些额外的操作,发生在类实例被创建完以后,它是实例级别 的方法
复制 class A:
__slots__ = ('name','age')
# A的实例只可以拥有name和age两个变量
>>> a = A()
>>> a.name = 'abc'
>>> a.age = 15
>>> a.sex = 'man'
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'A' object has no attribute 'sex'
实例a
添加额外的sex
属性时候,就会报错了AttributeError
运算符重载
基本方法
该函数如果存在,将在实例创建期间被调用,返回该实例的一个字符串对象
当len
函数的参数为实例时,返回该实例的这个函数调用
对比方法
复制 __lt__(self, obj)、__le__(self, obj)
复制 __gt__(self, obj)、__ge__(self, obj)
复制 __eq__(self, obj)、__ne__(self, obj)
运算方法
序列类型索引切片
复制 __getitem__(self, index)
复制 __setitem__(self, index, value)
映射类型
复制 __setitem__(self, key, value)
demo
复制 class A:
def __str__(self):
return 'abc'
def __len__(self):
return 10
def __lt__(self,obj):
return '哈哈哈哈'
def __setitem__(self,index,value):
print("你好啊")
def __getitem__(self,index):
print(index)
return 10
a = A()
print(str(a))
print(len(a))
print(a < 10)
print(a[10:10:10])
print(a)
元类
元类就是用来创建这些类(对象)的,元类就是类的爹,通过自定义元类可以控制类对象的初始化过程
比如创建一个A类
复制 A = MetaClass()
object = MyClass()
函数type实际上是一个元类。type就是Python在背后用来创建所有类的元类。你可以通过检查class 属性来看到这一点
元类会自动将传给type的参数作为自己的参数传入
复制 def upper_attr(future_class_name, future_class_parents, future_class_attr):
'''返回一个类对象,将属性都转为大写形式'''
attrs = ((name, value) for name, value in future_class_attr.items() if not name.startswith('__'))
uppercase_attr = dict((name.upper(), value) for name, value in attrs)
return type(future_class_name, future_class_parents, uppercase_attr)#返回一个类
class A(object,metaclass=upper_attr):
# __metaclass__ = upper_attr # python2写法
a = 'abc'
a = A()
a.A # abc
猴子补丁
对象的属性在对象运行期间动态的替换,叫做猴子补丁( Monkey Patch )
复制 class A:
def __init__(self):
self.num = 1
a = A()
a.num = lambda x:x**2
很多代码用到 import json,后来发现ujson性能更高,如果觉得把每个文件的import json 改成 import ujson as json成本较高,或者说想测试一下用ujson替换json是否符合预期,只需要在入口加上
复制 import json
import ujson
def monkey_patch_json():
json.__name__ = 'ujson'
json.dumps = ujson.dumps
json.loads = ujson.loads
monkey_patch_json()
迭代器
迭代器与Python2.2版本后添加,他为类序列对象提供了一个可以使其进化为迭代器的接口 iter
迭代器对象内部会维持一个状态 ,用于记录当前迭代的状态,以方便下次迭代时提取正确的数据元素
可迭代对象内置__iter__
函数,该函数将对象处理为可迭代对象
任何实现__iter__
和__next__
的对象都可看作迭代器
__iter__
返回迭代器自身、__next__
返回迭代的下个值
迭代器没有返回的元素,抛出StopIteration
,迭代器类似于工厂模式,每次返回一个值
无限迭代器
复制 from itertools import count
count(start=...step...)
复制 from itertools import cycle
有限迭代器
复制 from itertools import islice
islice(iterable, stop)
islice(iterable, start, stop[, step])
复制 takewhile(predicate, iterable)
itertools.takewhile(lambda x:x!=3, [1,2,3,4,5])
自定义迭代器
要完善__iter__
于__next__
方法
复制 class FBNQ:
def __init__(self):
self.perv = 0
self.curr = 1
def __iter__(self):
return self
def __next__(self):
rt = self.curr
self.curr += self.prev
self.prev = value
return value
生成器
生成器是特殊的迭代器,生成器自动实现了迭代器协议(iter
、next
),不需要手动维护这两种方法
元组推导式
复制 ge = (var for var in range(10))
该表达式可以在处理大量数据保存在序列时,比较费内存
而当前方式可以延迟生产数据,节约内存,数据只有在需要的时候才生产
yield关键字
如果在函数中出现了yield 关键字,该函数将不在是普通函数,而是生成器函数
复制 def func():
num = 18600000000
while True:
if num < 18700000000:
yield num
num += 1
::: tip
注意 :如果在生成器函数内部使用return、那么将直接抛出StopIteration ::: 创建好的生成器支持如下一些操作
复制 gen = func()
gen.close() # 手动关闭生成器函数,会使后面的生成器使用直接返回StopIteration
gen.send() # 生成器函数可以接收一个外部传入的变量,并且可以像函数处理参数一样将这个变量在运行期间进行处理并返回,在使用send函数时,第一次首先需要传入None或next函数调用的形式将生成器第一个值生产
gen.throw(ValueError) # 用来像生成器函数传入一个异常,使生成器结束
def gen():
num = 0
while True:
var = yield num
#send函数传入的值在这里
num = var + num
g = gen()
#re = g.send(None)
re = next(g)
print(re) # 0
re = g.send(3)
print(re) # 3
re = g.send(3)
print(re) # 6