Python 数据类型

变量

可以说python只有名字没有变量,不需要定义类型,你赋值啥他就啥类型(所以定义变量时就得赋值,有int、float、str、bool),甚至可以赋值其他类型,例如:

a=1
a='a'

那么a就从int变成char,这是可以的,还有就是比如:

>>> 5+3
8    #两个int,按正常计算
>>> '5+3'
'5+3'    #整个是str,所以不变
>>> '5'+'3'
'53'    #两个str,结果拼在一起

命名时可以包括字母、数字和下划线,但不能以数字开头,且大小写敏感
注:
python作为一门动态语言不需要指明变量类型,但这样也就少了编译时检查错误的环境(无法做类型检查),只有在运行时才能知道是否存在错误

变量实质

python当中的变量实际就只是一个指针,指向对应数据的地址,因此python的变量可以随意修改指向,举例:

a = 1
print(id(a), id(1))
a = 2
print(id(a), id(1), id(2))
# 会发现a只是将指针指向了2

# 1892936144 1892936144
# 1892936176 1892936144 1892936176

在python内部有个intern机制:将一定范围内的数或者短的字符串提前载入内存,建立全局唯一的对象,下次再使用时则直接指向他

e记法

python里计数可以用科学计数法,例如0.0002就可以是2e-4500000可以是5e5,但是e记法的数一定是浮点数,所以5e5 = 5.0e5 = 500000.0

bool类型

True就是1,False就是0(记住都是首字母大写),所以可以True * False(1*0=0),类似这样,但是不能True / False(0不能做分母),当然这些做法可以,但是不推荐
注:
所有值为0的:00+0j0.0,空字符串''、空列表[]、空元组()、空字典{}、空集合set()None都是False,其他都是True

强制转型

python支持,例如:a = int(1.666),结果a为1,其中float转int是将小数点的数后直接砍掉。还有其他的强转如下:

#类型转换
float()  #转成float
str()  #转成str,如果是几种类型混合,其会用连接符表示,比如:`a = str(ab1)`,就会变成`'ab+1'`
#进制转换
bin()  #转二进制
oct()  #转八进制
int()  #转十进制
hex()  #转十六进制

对上面强转举例:

>>> bin(21)
'0b10101'
>>> type(bin(21))

>>> oct(10)
'0o12'
>>> hex(10000)
'0x2710'

可以看出除了十进制的,其他几种进制强转都是前缀加数字部分,类型也都是字符串型,所以如果想要只取数字部分就可以通过replace()替换掉前缀
注:
强转时,不要有以其命名的变量,例如:

>>> str = 1
>>> str(2)
Traceback (most recent call last):
  File "", line 1, in 
    str(2)
TypeError: 'int' object is not callable

可以看出报错了,说str是int类型,无法被调用,说白了就是str已经被当成前面的变量1了,所以现在只是个数字而已(实际上现在获取的str是全局空间的,而不是内置空间的str)

python中负数的二进制展示问题:
对于十进制数据,通过bin转成二进制时,是直接将其转成正数的二进制加上一个负号;而对于十六进制通过bin转二进制时,才是返回真正的二进制表示,举例:

>>> bin(-100)
'-0b1100100'
>>> bin(-100 & 0xffffffff)
'0b11111111111111111111111110011100'

参考:https://www.runoob.com/w3cnote/python-negative-storage.html

"""长字符串"""

像特别长的字符串甚至有换行,好几行的那种,光用'或者"引起来是不行的,此时可以用头尾各3个"包起来实现,举例:

"""
这是
一个
多行
长字符串
"""

字符串(str)

基本使用

用单引号或双引号包起来的字符组合,但要注意不能单引号/双引号自己给自己嵌套使用,比如下面的用法是不正确的:

'在'单引号'里面嵌套单引号' 
"在"双引号"里面嵌套双引号"

所以如果要在字符串中表示单引号/双引号,可以混合着使用,或者使用转义字符,例如下面的方式:

'"双引号被单引号包着"'
"'单引号被双引号包着'"
'使用转义的单引号:\''

其可以几个字符用+连起来(得都是字符串类型),也可以*起来,举例:

>>> 'aaa'+ str(2)    #+起来时要求同种类型
'aaa2'
>>> 'aaa'*2    #*起来时不要求为同种类型
'aaaaaa'
开头加r

假如给变量赋值如下:str='c:\now',那么在print里输出时,\n会被转义,为了避免这种情况,此时我们可以用\\替换\,这是解决只有少数地方会被转义的情况。
但是如果是多路径比如:'c:\now\abc\ttt'时,每个地方都用\\来替换会很麻烦,所以此时就可以在字符串前面加个r,代表原始字符串,例如:str=r'c:\now\abc\ttt',则str就会自动变成c:\\now\\abc\\ttt,用print输出时就是正常的c:\now\abc\ttt

开头加u

一般在内容为中文时用,代表内容用utf-8编码的字符串

开头加b

代表字节类型,即byte类型,字节流通过decode(编码)来解码成字符串,反过来字符串通过encode(编码)来编码成字节类型,举例:

>>> type(b'abc')  //字节类型

>>> type(u'abc')  //字符串类型

>>> type(b'abc'.decode('utf-8'))  //字节流解码成字符串

>>> type(u'abc'.encode('utf-8'))  //字符串编码成字节流

分片

可以用分片方法截取字符串的一部分,例如str1='abcde',只取其中abc就可以:str1[:3],取'cbce'就可以:str1='c'+str1[1:3]+'e'

查询索引

字符串其实可以看作是一个特殊的数组,其每个字符按顺序排放,所以可以像查数组那样,例如上面的str1,如果:str1[1]就会输出'b',即第二个字符

通用方法

字符串能使用的方法特别多,像count()计数、index()索引(find方法也可以索引,使用跟index一样,但index找不到会报错,find会返回-1,还有像rfind方法,是从右边开始查,很多r开头的都是从右边开始的意思)啥的都能用,参照列表,还有一些常用方法如下:

把第一个字母改为大写

str1.capitalize()

把所有字母改为大写

str1.upper()

将字符串所有内容改为小写

str1.casefold()

将字符串居中

例如我要一个长度10的字符串,然后原来的字符串内容在里面就可以:str1.center(10),结果就是' abcde ';如果要居左就用ljust,举例:

>>> "abc".ljust(10) #居左
'abc       '
>>> "abc".rjust(10) #居右
'       abc'
指定编码

str1.encode(encoding='utf-8',error='strict')
表示指定为utf-8编码,error参数代表设置不同错误的处理方案,默认为strict,此时当编码不符是会报错,如果要设置宽松点的情况可以用ignore,此时会忽略这个错误

判断字符串是否以某子字符串结束

endswith(sub[,start[,end]])
例如判断上面str1:str1.endswith('e'),因为是以'e'结尾,所以返回True(判断'de'、'cde'啥的也是对的),如果判断是否以某字符串开头用startswith()

把'\t'变成空格
>>> str2='a\tbcd\tc'
>>> str2.expandtabs()
'a(7个空格)bcd(5个空格)c'

(因为如果方法里不填入数字,默认为8个空格,而tab又是从这个字符串头开始数加上空格共8位,所以a后面加7个空格,bcd加5个)

判断是否为只有字母的非空字符串

str1.isalpha()
结果返回True或False

判断是否只有字母或数字(可以都有)的非空字符串

str1.isalnum()

判断是否只有数字的非空字符串

str1.isnumertic(),还有isdigit()都是判断数字,但是numertic就算汉字数字比如四也是True,而digit是False,还有一个是isdecimal()
三者区别参考:
http://www.cnblogs.com/jebeljebel/p/4006433.html

判断字符串是否是标题,即字母都以大写开始,其余位均小写
>>> str1='Abc'
>>> str2='AbC'
>>> str1.istitle() 
True
>>> str2.istitle() #(要把字符串变标题形式就用title())
False
>>> 
以sub做分隔符插入到字符

str1.join(),举例:

>>> str1='abc'
>>> str1.join('123')
'1abc2abc3'
替换字符串

str1.replace(),举例:

>>> str1='abcabcabc'
>>> str1.replace('b','d',2)
'adcadcabc'

对于其参数,第一个是要替换的字符,第二个是替换成的,第三个是最多替换几个(第三个参数可选,默认全部替换)

分割字符串

str1.split(),举例:

>>> str1='i love   you'  #第二个中间有3个空格
>>> str1.split()  #按1到多个空格分割
['i', 'love', 'you']
>>> str1.split(' ')  #按单个空格分割
['i', 'love', '', '', 'you']
>>> str1.split('i')
['', ' love   you']

括号是选择按什么字符串分割,默认是空格(1到多个),比如括号里是'i',结果就是['',' love you'],'i'被切了,左边是空字符串
注:
split还有第二个参数,代表从左到右对前几个指定字符进行分割,举例:

>>> a = "x.y.z.k.q"
>>> a.split('.')
# 默认对所有.都分割
['x', 'y', 'z', 'k', 'q']
>>> a.split('.', 1)
# 只对从左到右第一个点分割
['x', 'y.z.k.q']
>>> a.split('.', 2)
# 只对从左到右第二个点分割
['x', 'y', 'z.k.q']

同理从右到左可以用rsplit方法实现

按行切割

split会对一到多个空格、缩进,以及换行等都进行切割,但splitlines只会根据行来切割,举例:

>>> a = """sda s
dsadas
gdfgd"""
>>> 
>>> a.split()
# 可以看到空格和行的都被切割了
['sda', 's', 'dsadas', 'gdfgd']
>>> a.splitlines()
# 可以看到仅按行切割
['sda s', 'dsadas', 'gdfgd']
删除字符串前面或后面的所有某一个字符串

str1.strip(),举例:

>>> str1='abacaa'
>>> str1.strip('a')
'bac'

只会删除前面和后面,中间不管,括号里没东西默认删空格,如果只要删除左边的某些字符就用:lstrip(),右边就用rstrip(),可以叠加使用,比如:

>>> str1='abacaa'
>>> str1.strip('a').rstrip('c')
'ba'
翻转字符串大小写

str1.swapcase(),举例:

>>> str1='aBc'
>>> str1.swapcase()
'AbC'
翻转字符串内容

常用的方法有分片、列表翻转后转字符串、循环,举例:

#分片
>>> a = 'abcde'
>>> a[::-1]
'edcba'
#列表翻转字符串
>>> b = list(reversed(a))
>>> "".join(b)
'edcba'
#for循环就不举例了,就列表翻转然后一一遍历相加
更多字符串方法参考

http://bbs.fishc.com/thread-38992-1-1.html

列表(list)

创建一个空列表:

empty=[]

不像数组必须都得存放同一种数据,列表里面可以放各种数据类型,例如:

user = [123 ,'abc' ,'用户1' ,[1,2,3,4]]

其中列表内每个对应下标为从0开始,但比较特殊的是比如上面的user[3]是一个列表:[1,2,3,4],所以如果要定位到4的下标就用user[3][3],即以二维数组的方式

添加元素
append()方法

例如对上面的user:user.append('新用户'),这个就会被加到列表的最后一个,因为在最后一位,所以可以通过user[-1]查看。要注意append只能加一个参数

extend()方法

想要加多个参数时可以用extend()方法,但是注意extend()也只能传一个参数,不过他可以传一个列表参数(其实是迭代器,但是可以先当成是一个列表),其原理就是把传过去的列表内容分别加到后面,从而传进多个参数,例如对上面的user: user.extend(['a','b']),结果就添加了'a'和'b'进去
注意要以列表形式传多个参数,如果直接传多个参数,比如extend('a','b')就会报错
注:
  append()也能传列表,但和extend()不同,extend是默认传入列表,所以会依次把列表里的参数传进去;而append传列表时就是单纯的传一个列表进去,比如前面通过两种方法传['a','b'],在extend里传入的是'a'和'b',在append里传入的是['a','b']

insert()方法

想要传到自己想要的位置时可以用insert()方法,例如想传字符串'c'到上面user列表的第二个位置:user.insert(1,'c'),其编号也是从0开始的
注:
当对列表使用+或者+=(两种方法实质是有区别的)时也可以添加元素,+操作算是新建一个新的列表并添加元素,而不是对原来列表添加元素,所以原来的列表并不会发生改变,而+=是在原来的列表里添加元素,所以效率上比+要高些,举例:

>>> a = [1,2,3]
>>> b = a + [4] #新建一个列表b存放a和[4]相加的列表
>>> b
[1, 2, 3, 4]
>>> a #a并没有发生改变
[1, 2, 3]
>>> a = [1,2,3]
>>> a += [4]
>>> a
[1, 2, 3, 4]

注2:
几种添加元素效率比对(一般情况下):+<insert<extend<append<+=

调换元素

拿上面user对调第一第二个元素举例:

temp=user[0]
user[0]=user[1]
user[1]=temp

当然python里还可以支持更精简的写法:

user[0], user[1] = user[1], user[0]

结果和前面是一样的,还有就是可以用列表分片方法来替换元素,参考下面的列表分片

删除元素
remove()方法

按变量名删除,例如:user.remove('abc'),如果有同名的变量就只删除第一个,如果想把某个同名元素全部删除,可以循环判断当存在某个元素时则删除该元素,举例:

while "abc" in user:
    li_safe_content.remove("abc")
del函数

按其下标位置删除,例如删除user的第一个变量:del user[1],del后面加变量名甚至可以删除整个变量,例如:del user就会把整个user列表删了

pop()方法

其会弹出一个元素(和删除有点区别,其在删除元素的基础上,还会将该元素作为返回值输出),例如:user.pop(1),就会删除列表的第2个元素(如果括号里没数字就默认弹出最后一个),并输出,我们可以把这个给赋值存储以备后面要用,例如:temp = user.pop(),此时删除最后一个值并赋值给temp

删除重复元素

可以使用numpy模块下的unique()方法,然后再用list()方法转成列表,也可以用set()方法转成集合后再转回来,举例:

>>> a = [1,2,3,1,2,1]
>>> list(set(a))
[1, 2, 3]
列表分片

可以截取列表的一部分,比如:user[1:3]说明从下标为1的截取到下标为3的(不包括3),还可以user[:3],说明从第1个到下标为3的(不包括3),user[:]说明截全部,user[:-2]说明截取去掉最后两个数据后的列表;其还有第三个可选参数,意思是变化量,举例:

>>> d = [1,3,4,5,8]
>>> d[:4:2]     #截取从第一个到第4个,每次跨两步(隔一个)取
[1, 4]
>>> d[::-1]     #截取整个列表,变化量为-1,即取倒过来的列表
[8, 5, 4, 3, 1]

切片还可以迭代(连续)切片,举例:

>>> d = [1,3,4,5,8]
>>> d[1:4][1:]      #先切出2到4个([3,4,5]),然后再切2到后面的所有
[4, 5]
>>> b = [[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]]
>>> b[1:][1] [3]      #先切出2到后面([[5, 6, 7, 8], [9, 10, 11, 12]]),然后取第2个([9, 10, 11, 12])的第4个
12 

上面说的通过列表替换元素方式如下:

>>> a = [1,2,3,4,5]
>>> a[1:3] = [6,6,6,6,6]  #把2、3位替换成5个6
>>> a
[1, 6, 6, 6, 6, 6, 4, 5]

注:
分片一般用来截取一个新的列表,例如:temp = user[1:3],此时temp就是一个新列表了,这里要注意下:temp1=user[:]temp2=user虽然看起来都是复制整个user,但实际上不一样,temp1应该更像是截取,所以user无论怎么变化,跟temp1都无关,而temp2更像是直接指向user,所以是和user同步的,user做了什么操作,temp2也会跟着改,这也是为什么需要列表分片来复制列表的原因之一

逻辑操作

两个列表比较大小,只比较第一个元素,如果第一个一样再比第二个...;两个列表相加就是后一个的变量加到前一个的后面;列表还可以和一个数相乘,就是把列表里的内容重复几次,例如:

>>> a=[1,2,3]
>>> a*2
[1,2,3,1,2,3]

注:
  在数据分析模块里有数组概念,其逻辑操作则和C当中数组一样,两个数组相加就把同一位置的值相加,相乘除就把所有值都乘除一定大小

判断是否在列表

in或者not in(成员关系操作符),例如a[]里面有1,所以:1 in a 就会是True,但只能判断第一层,假如a列表为a[2,3,[1,2,3]] ,那么判断结果就为False,假如这时还想判断是否存在1,可以用:1 in a[2],因为在第3个元素里所以用a[2]

计算参数个数

count()方法,括号里输入参数名,例如:

>>> a=[1,2,3,1,2]
>>> a.count(1)
2

既然count()可以计算列表里某个元素的数量,那么就可以通过将其和集合结合起来,获取列表每个数据的数量,举例:

li = [3, 9, 3, 2, 5, 6, 7, 3, 2, 3, 3, 3]
dict1 = {}
#用字典来存储结果
for i in set(li):
    dict1[i] = li.count(i)
print(dict1)

结果:
{2: 2, 3: 6, 5: 1, 6: 1, 7: 1, 9: 1}
索引参数位置

index()方法,但如果只输入要查找的变量名就只会告诉你第一个该参数的位置,例如对上面的a:a.index(2)输出就是1,所以可以在index里再加2个参数确定索引的开头和结尾,例如在第三个到第五个之间查2:a.index(2,2,4)

合并列表

在字符串中有split()来把字符串分割成列表,所以也有join()把列表合并成字符串,相当于split()的逆方法,比如:

>>> a = '-' #中间用a的值隔开
>>> b = a.join(['sad', '2', 'gds'])
>>> b
'sad-2-gds'
>>> b.split('-')
['sad', '2', 'gds']
翻转列表

reverse()方法,举例:

>>> a=[1,2,3]
>>> a.reverse()
[3,2,1]
列表排序

sort()方法,如果不输入参数则默认从小到大排序(列表内容必须为可对比的类型,例如列表里同时存在数字和字母就会报错),举例:

>>> a=[2,4,3]
>>> a.sort()
[2,3,4]

其实sort有3个参数,即sort(function,key,reverse=False),function是自己指定的算法,key代表和算法搭配的关键字,第三个代表是否倒序,默认为False代表不倒序,如果要倒序就改成True,例如对b=['s','ac','gdsa']进行按字符串长度的倒序排序则:b.sort(key=len,reverse=True)
注:
列表翻转和排序只会对当前列表进行操作,且没有返回值,所以如果想把列表翻转/排序后赋值给另一个列表要分两步进行,看下面代码:

>>> a = [2,3,7,5,1]
>>> b = a.sort()
>>> b
#可以看出b是指向空的,并没有把a排序后返回传给b
>>> a
[1, 2, 3, 5, 7]
#但是可以看出a已经排好序了
>>> a.reverse()
>>> b = a
>>> b
[7, 5, 3, 2, 1]
#可以看出如果要翻转/排序后赋值,分两步进行就能成功
打乱列表顺序

参考random模块的shuffle()方法,把列表作为参数传入即可,举例:

>>> import random
>>> a
[1, 2, 5, 3, 7]
>>> random.shuffle(a)
>>> a
[5, 3, 1, 7, 2]

元组(tuple)

和列表非常像,区别是其内容不可修改,所以像排序啥的操作基本都没有

特点
  • 可迭代
  • 不可变
  • 可拆包

注:
元组的不可变性是指存放的内容地址不可变,并不是内容不可变,举例:

content = [1,2,3]
a = (content,4)
print(a)
content.pop()
print(a)

# ([1, 2, 3], 4)
# ([1, 2], 4)

可以看到元组内部存放的列表内容是可以改变的,但列表的内容改变,列表的内存地址并没有一起改,所以是允许的

与list比较
  • 由于不可变,所以其会作为常量,在编译时就能够确定内容,因此性能更好
  • 由于不可变,因此可以进行哈希运算(要求元组内部的数据也是可哈希类型的,例如元组内部存放了列表,那么就不能进行哈希运算了),也就可以存放到集合或者字典的key当中
  • 线程安全
定义元组

定义方法是用小括号,例如:a=(1,2,3,4,5),创建一个空元组:a=(),但是元组的标志性符号是逗号不是小括号,例如定义:a=(1),用type函数会发现a是int型,而定义:a=1,2,3,会发现a是元组,所以说逗号才是元组的标志,像前面如果想创建只有一个元素的元组就得后面加个逗号,例如:a=(1,)(不用括号也可以)

查询元组

和列表查询一样,例如查第二个:a[1](就创建时候用小括号)
注:
  除了定义、删除的语法还有不可修改的规定和列表不同外,其他的使用方法和列表几乎相同,如分片:b=a[1:];还有像count()(计算出现次数)、index()方法(索引)、innot in和一些逻辑操作符都可以用,参照上面列表用法

更新元组

虽然说元组不能修改,但是我们可以更新他,比如在前面的a的第二个位置插入6,可以这样:a=a[:1]+(6,)+a[1:](这里括号和逗号都要在),这时候就更新元组了,这里不是改变,因为a被重新赋值了,已经不是原来的他了(用id(b)可以发现地址已变)

删除元组

一般删除元组里的内容就用分片截取就好了,跟上面的更新差不多,如果要删除整个元组就用del函数,例如:del a
注:
  上面知道元组的标志是逗号,所以(8)是代表整数8,(8,)代表元组8,,所以如果用8与上面两个分别相乘将会是不同的结果:

>>> (8)*8
64
>>> (8,)*8
(8,8,8,8,8,8,8,8)

注2:
  列表、元组可以同时给多个变量同时赋值,例如:

>>> x = [1,2,3,4,5]
>>> a,b,c,d,e =x
>>> a
1
>>> a,b,c,d,e
(1, 2, 3, 4, 5)
#可以看出此时a,b,c,d,e就变成对应的1,2,3,4,5了

前提是赋值变量数和列表/元组数值量相同

拆包
a = [1,2,3,4]
b, *c, d = a
print(b, c, d)

# 1 [2, 3] 4

集合(set)

如同字面意思,跟数学的集合一样,里面的数据都是唯一的(最主要意义,所以一般用来去除重复内容),而且是无序的

定义
  1. 直接用{}定义:举例:num1 = {1,2,3,3,4,2,1} ,因为值唯一,所以num1为{1, 2, 3, 4}
  2. set()函数:举例:set1 = set([1,2,3,3,2,1]),括号里面可以是列表、字符串、元组、字典等
    注:
    定义一个空的集合不能用:set1 = {},这样set1的类型是dict,所以应该用:set1 = set()
添加元素

add()方法,例如:num1.add(6),就会加进去了,但只能加一个数据,而且不能加list、dict等,可以加tuple

移除元素

remove()方法,例如:num1.remove(1),如果删除的内容不存在则会报错,如果希望删除的数据即使不存在也不会报错,那么可以用discard()方法
注:
  因为集合无序,所以无法索引,要读取里面数据可以用for一个个读取出来,也能用in来判断,或者把他变成一个列表等等

交集

使用intersection()方法,或者用&,举例:

>>> a = {1,2,3,5,7}
>>> b = {2,4,5,6}
>>> a.intersection(b)
{2, 5}
>>> a & b
{2, 5}
并集

使用union()方法,或者用|,举例:

>>> a = {1,2,3,5,7}
>>> b = {2,4,5,6}
>>> a.union(b)
{1, 2, 3, 4, 5, 6, 7}
>>> b.union(a)
{1, 2, 3, 4, 5, 6, 7}
>>> a | b
{1, 2, 3, 4, 5, 6, 7}
去不同

比如要找出两个集合的不同内容,可以将两个集合相减,举例:

>>> a = {1,2,3,4,5}
>>> b = {1,5,6}
>>> a-b
{2, 3, 4}
#可以看出结果就是将a中有的减去b中有的剩下的值

这里放上来一道网申的笔试题,感觉用集合解决太完美:

现在有一个数组,其值为从1到10000的连续增长的数字。出于某次偶然操作,导致这个数组中丢失了某三个元素,同时顺序被打乱,现在需要你用最快的方法找出丢失的这三个元素,并且将这三个元素根据从小到大重新拼接为一个新数字,计算其除以7的余数。 例:丢失的元素为336,10,8435,得到的新数字为103368435,除以七的余数为2。
输入描述:

输入数据为一行,包含9997个数字,空格隔开。
输出描述:
输出为一行,包含一个数字。

这道题最主要的问题就是用最快方法找出集合1到1w里面没有的3个数,如果用for循环递归一个个比对,显然花的时间不是一点两点,于是这里就可以用到集合相减找不同来实现,把两种方式的代码都放出来如下:

#用迭代的方式
li = input().split()
li.sort()
li_t = []
for i in range(1,10000):
#一个个用if去判断
    if str(i) not in li:
        li_t.append(i)

result = ''
for each in li_t:
    result += str(each)
print(int(result)%7)

#用集合的方式
li = set(input().split())
li2 = set(str(i) for i in range(1,10000))
#创建一个1到10000的集合然后和输入的集合相减,瞬间就得到那3个没有的了
li_result = list(li2 - li)
li_result.sort()
result = ''
for each in li_result:
    result += each
print(int(result)%7)

最终虽然两个都能实现,但是迭代花的时间确是集合的几十倍,所以可以看出集合在某些场景下应用的强大能力

更多集合操作参考

https://www.cnblogs.com/sunyang945/p/7859962.html

不可变集合(frozenset)

相当于集合和元组的集合,不可变。通过frozenset()函数定义,举例:set2 = frozenset([1,2,3,3,2,1])

更多参考

https://blog.csdn.net/weixin_37887248/article/details/81565468

字典(dict)

定义

(1)用{}定义,键值对引号隔开,例如:dic = {'a':'admin','b':'book','c':'cook'},则dic['b']就是'book'
注:
在python3.6以前的版本当中,字典都是无序的,如果希望使用有序字典,可以使用collections下的OrderedDict类;而在3.6及以后的版本当中,字典都是有序的了
(2)往dict()中插入元组/列表定义,举例:

>>> a = [(1,'one'),[2,'two']]
>>> dic1 = dict(a)
>>> dic1
{1: 'one', 2: 'two'}

注:
dict()能强转成字典类型,但是str不能直接转成dict,举例:

>>> dict("{'a':1,'b':2}")
Traceback (most recent call last):
  File "", line 1, in 
    dict("{'a':1,'b':2}")
ValueError: dictionary update sequence element #0 has length 1; 2 is required

所以这时候可以用eval()函数来转成dict,举例:

>>> a = eval("{'a':1,'b':2}")
>>> a
{'b': 2, 'a': 1}
>>> type(a)

(3)用dict()直接指定键值对定义,例如:dic1 = dict(x='abc',y='def')
注:
  第三种方式键是不加引号的,并且关键字会根据键来排序,例如:

>>> dic1 = dict(x='abc',y='def',z='ghi')
>>> dic1
{'z': 'ghi', 'x': 'abc', 'y': 'def'}

注2:
  像列表、字典等数据类型里面还能放像函数、类之类的数据,举例:

def abc(x, y):
    print(x+y)

class XYZ():
    def __init__(selt,x):
        print(x)
    a = 50

a = {'a':1, 'b':abc, 'c':abc(1000,1000), 'd':lambda x,y : print(x+y), 'e': XYZ, 'f': XYZ(10000)}
#b和c都是函数,但b没传参数,c传了参数,d是没传参的匿名函数,e是没传参的类,f是传参的类
#因为c和f都传了参数,所以调用时就已经执行了,结果会先输出200和1000,但是之后再调用就不会执行了(其已经变成函数的返回值,这里没有,所以是None)
a['b'](1, 1)  #调用abc函数,并传入参数1和2
print(a['c'])        #输出执行过参数为3和4的abc函数返回值,因为abc没返回,所以输出None
a['d'](5, 5)  #调用lambda函数
print(a['e'].a)  #调用类XYZ的a属性
a['e'](100)     #调用类,因为传入了参数,执行__init__函数

结果为:
2000  #定义a时第三个参数的输出
10000  #定义a时最后一个参数的输出
2
None
10
50
100
修改

直接给键赋值就好了,例如上面的dic1:

>>> dic1['x']='aaa'
{'z': 'ghi', 'x': 'aaa', 'y': 'def'}

如果是不存在的键就会新建这个键值对

有序字典

默认字典是根据hash之类的原理来排序的,如果希望字典的顺序可以像类似列表那样按顺序排的话,可以使用OrderedDict,具体参考下面链接:
http://c.biancheng.net/view/2438.html

内置函数
fromkeys(键[,值])

创建并返回一个统一值的新字典,其中可以设置键和值,举例:

>>> d1={}
>>> d1.fromkeys((1,2,3))    #创建一个字典,并定义3个键1、2、3
{1: None, 2: None, 3: None}
>>> d1.fromkeys((1,2,3),1)  #定义3个键并同时赋值为1
{1: 1, 2: 1, 3: 1}

他只能给所有键设置同一个值,所以不要以为可以通过传入列表、元组来依次赋值,比如:

>>> d1.fromkeys((1,2,3),(4,5,6))
{1: (4, 5, 6), 2: (4, 5, 6), 3: (4, 5, 6)}  #结果不是{1:4, 2:5, 3:6},而是值都为(4, 5, 6)

可以看出其还是给所有元素都设置了同一个值

keys()

返回字典的所有键名,例如:

>>> d1={1:2,2:'two'}
>>> d1.keys()
dict_keys([1, 2])
values()

返回字典的所有值,例如:

>>> d1={1:2,2:'two'}
>>> d1.values()
dict_values([2, 'two'])
items()

返回字典的所有键值对,例如:

>>> d1={1:2,2:'two'}
>>> d1.items()
dict_items([(1, 2), (2, 'two')])

注:
  keysvaluesitems常用于循环输出字典,例如:

for key in d1.keys()
    print("循环输出所有的键:",key)
get()

根据键获取值,一般常用d1[键]输出值,但如果键不存在,这种方法就会报错,所以用get就更宽松,比如对上面的d1:d1.get(1)结果就是2,但是d1.get(3)不输出也不会报错,如果用print打印结果会是none,我们还可以用get来判断输出,有就输出值,没有就输出我们想要的内容,比如:

>>> d1.get(3,'nothing')
nothing    #因为3不存在所以输出nothing,如果3在就输出3的值

注:
get可以判断键在不在字典,in和not in也可以,比如上面的:1 in d1结果就是True
注2:
因为键是唯一的,所以可以用get(键)dict[键]来获取值,反过来如果想通过值来获取键,可以通过get条件判断来进行遍历,举例:

>>> d
{2: 2, 3: 6, 5: 1, 6: 1, 7: 1, 9: 1}
>>> [i for i in d if d.get(i) == 1]
#链表推导式当i(键)=1(值)时,输出i
[5, 6, 7, 9]
>>> [i for i in d if d.get(i) == 6]
[3]

如果是要求最值的键,则可以用内置的min/max函数,举例:

>>> d
{2: 2, 3: 6, 5: 1, 6: 1, 7: 1, 9: 1}
>>> max(d, key=d.get)
3
clear()

清空字典,例如:d1.clear()结果d1就变成{}
注:
  d1={}会使d1变成空,但是其原来指向的字典没变成空,而clear才是真正意义上的清空,例如:

>>> d1={1:2,2:'two'}
>>> d2 = d1  #此时d2和d1指向同一个地址,可以用id(d2)查看
>>> d1 = {}
>>> d1  #d1变成空
{}
>>> d2  #d2还是指向d1原来的地方,而原来指向的地方没变成空,所以d2没变成空
{1: 2, 2: 'two'}
###这次使用clear
>>> d1={1:2,2:'two'}
>>> d2 = d1
>>> d1.clear()
>>> d1
{}
>>> d2  #这次d2和d1一样都清空了
{}
copy()

浅拷贝,和赋值不太一样,赋值就相当于确定了指向,所以用id()查看会发现赋值后原来的字典和赋值的字典地址是一样的,但copy的结果地址是不同的,所以如果原字典内容改变,赋值的字典会跟着改变,但copy的字典不变(copy有点像列表里的整个分段赋值)

pop()

括号里给定键弹出对应的值

popitem()

括号里没参数,随机弹出一个项

setdefault()

get有点像,能输出键对应的值,但不同的是如果键不存在他会新加进去,例如:d1.setdefault(1)结果是2,但d1.setdefault(3),结果d1会变成:{1: 2, 2: 'two', 3: None} ,新建同时还可以赋值,例如:d1.setdefault(5,"aaa"),d1就会变成:{1: 2, 2: 'two', 3: None, 5: 'aaa'}

update()

把一个字典的东西加到另一个字典里去,例如:

>>> d2={6:'bbb'}
>>> d1.update(d2)
{1: 2, 2: 'two', 3: None, 5: 'aaa', 6: 'bbb'}    #加入了d2的内容
for输出
  • 方法1:其输出是只输出键而不是值,所以如果想输出值就要输出字典的对应项,举例:
dict1 = {'a':'one','b':'two','c':'three'}
for each in dict1:
    print("%s -> %s" % (each,dict1[each]),end=' ')  #each只是键
结果为:a -> one b -> two c -> three 
  • 方法2:通过将字典放入迭代器,然后分别保存键、值,举例:
dict1 = {'a':'one','b':'two','c':'three'}
for key,value in dict1.items():    #分别赋值键值到key和value
    print(key,value,end=' ')
结果为:b two a one c three 
字典和json区别

字典和json样式十分相似,虽然前者是数据类型,后者是一种格式,但最明显的区别就是字典可以用单引号,而在json中得用双引号,所以如果想将一个字典以json格式传递时,记得将单引号都替换成双引号

Ellipsis

这个是python一个自带常量,和...是等价的(偶然输入...发现竟然没有报错,原来还有内置这样的常量),意思相当于什么都没有,或者在数组之类的地方起省略意义,一般可以和pass相互替换,举例:

>>> ...
Ellipsis
>>> print(...)
Ellipsis
>>> print(Ellipsis)
Ellipsis
字典效率、顺序
  • 字典的内存开销大,但是查询快,而自定义的对象、以及python提供的对象也都是用字典来包装的
  • 在python3.6之前,字典是无序的,因此其存储顺序和元素添加的顺序有关(基于哈希表内部的原理,哈希冲突的处理等),并且添加数据有可能改变已有数据的顺序(例如哈希表内部达到一定占用率时会进行扩容,将重新开辟空间依次存入,导致顺序改变),所以如果想要使用有序字典,可以使用collections模块下的OrderedDict;python3.6开始,字典都是有序的了

其他数据类型

队列(queue)

先入先出,且一个数据只能取一次,需要import queue,使用也很简单,先新建一个Queue对象,然后要往队列加东西就用put(),取东西就用get()就行了(用get如果队列为空则会卡住,所以也可以用get_nowait,当队列为空时会出异常),查看用qsize(),举例:

q = queue.Queue()   #新建一个队列
q.put("one")
q.put("two")
q.put([1,2,3]) 
q.qsize()       #结果为3
q.get()     #结果为one
q.get()     #结果为two
q.get()     #结果为[1,2,3]
q.get()     #程序卡住,如果是q.get_nowait()则会报出空异常

如果想要限制队列大小,可以在新建对象时里面加入maxsize参数,比如:

q = queue.Queue(maxsize=3)

此时一旦超过队列长度就会卡住,然后查看put和get会发现里面都有个block(默认为True)和timeout(默认为None)参数,前者代表是否卡住,后者代表卡住几秒,所以我们可以设置为False或者设置timeout时长来避免卡住,比如:

c.get(block=False)  #当队列为空时报错,不卡住
c.get(timeout=1)        #当队列为空时延时一秒,然后报错
#put方法同理
实例

(通过多线程和队列实现生产者消费者顺序购买产品问题)

import threading
import time
import queue

def Producer():
   count = 1
   while True:
      q.put("产品%d" % count)
      print("\033[42;m生产第%d个产品\033[0m" % count)
      time.sleep(0.3)
      count += 1
      print("\033[45;m目前还剩%d个产品\033[0m" % q.qsize())

def Consumer(name):
   num = 0
   while True:
      print("\033[43;m%s购买了%s\033[0m" % (name, q.get()))
      num += 1
      print("\033[45;m%s共买了%d个产品\033[0m" % (name, num))
      time.sleep(1)

q = queue.Queue()
producer = threading.Thread(target=Producer)
consumerA = threading.Thread(target=Consumer, args=("热心市民A",))
consumerB = threading.Thread(target=Consumer, args=("热心市民B",))
producer.start()
consumerA.start()
consumerB.start()
栈(stack)

和前面的队列十分相似,也是导入queue,但是新建的对象是LifoQueue,其他和队列一样,当然用列表的append()pop()也可以实现。

优先级队列

在queue中还有一个PriorityQueue是用来实现优先级队列的,其和上面的区别就是在put的时候传入的是个元组,里面同时设置优先值,值越小越优先,举例:

q = queue.PriorityQueue()
q.put((2, "two"))
q.put((5, "five"))
q.put((0, "zero"))
q.put((1, "one"))
q.get()         #(0, 'zero')
q.get()         #(1, 'one')
q.get()         #(2, 'two')
q.get()         #(5, 'five')
双向队列

前面的栈和队列都是单向的,一旦定义以后就只能从头或者尾其中一边操作,在list中虽然可以用pop来实现栈的功能,但是却无法实现队列的功能,所以这里可以用双向队列,需要使用到collectionsdeque,可以直接用from import导入deque,然后通过append()appendleft()pop()popleft()来实现栈和队列,举例:

from collections import deque
a = deque()     # deque([])
a.append(5)     # deque([5])
a.appendleft(4) # deque([4, 5])
a.appendleft(3) # deque([3, 4, 5])
a.pop()         #5
a.popleft()     #3
计数器

在前面的collections里有个Counter还是作为计数器的存在,其可以计算一个数据结构当中各元素出现的次数,并返回一个字典,需要导入Counter,举例:

from collections import Counter
c = Counter([1,2,3,32,1,2,1,2,2,3,4])
#计算该列表里各元素个数
print(c)
结果为:Counter({2: 4, 1: 3, 3: 2, 32: 1, 4: 1})

python内置类型

数值类型
int
float
bool
complex  复数类型
迭代类型
iterator  迭代器
generator  生成器
序列类型
list
bytes
bytearray  字节流,数据只能是0或1
memoryview  二进制序列
range
tuple
str
array  数组,相比于list,其所有数据必须为同一类型,因此内存空间也是连续的,效率更高
映射
dict
集合
set
frozenset  不可变集合

注:集合和映射在python中的实现差不多,因此效率都比较高

上下文管理器
with
其他
模块类型
类对象
类实例
函数类型
方法类型
代码类型
object对象
type类型
ellipsis类型
notimplemented类型

序列类

分类

容器序列:内部可以存放任意类型数据,如list、tuple、deque
扁平序列:只能存放相同类型的数据,如str、array、bytes、bytearray
可变序列:内容可以修改,如list、array、deque、bytearray
不可变序列:内容不能修改,如tuple、str、bytes

相关基类

collections.abc下提供了Sequence(不可变序列)/MutableSequence(可变序列)类

+/extend/+=区别
  • +:必须为相同类型,并且返回一个新的序列对象,源码(基于UserList稍作修改):
def __add__(self, other):
    return self.__class__(self + self.__class__(other))
  • extend:只要是序列类就行,也是在原先对象中改变,本质就是for循环,依次append,源码:
def extend(self, values):
    'S.extend(iterable) -- extend sequence by appending elements from the iterable'
    for v in values:
        self.append(v)
  • +=:只要是序列类就行,并且在原先的对象中进行改变,内部实际是调用extend方法,源码:
def __iadd__(self, values):
    self.extend(values)
    return self
切片
相关操作
>>> a = [1,2,3,4,5]
>>> a[2:] = [5,6,7]
# 替换第三个元素开始的后面所有元素
>>> a
[1, 2, 5, 6, 7]
>>> a[:0] = [0]
# 在头部插入元素
>>> a
[0, 1, 2, 5, 6, 7]
>>> a[2:] = [9, 8, 7]
>>> a
[0, 1, 9, 8, 7]
>>> a[::2] = [10, 20, 30]
# 隔一个改一个元素,右边迭代的长度必须和左边切片的长度相等
>>> a
[10, 1, 20, 8, 30]
>>> a[:1] = []
# 删除第一个元素
>>> a
[1, 20, 8, 30]
>>> del a[::2]
# 隔一个删一个元素
>>> a
[20, 30]
>>> del a[:1]
# 删除第一个元素
>>> a
[30]
>>> a[0:0] = [1,2,3]
# 往第0个位置插入3个元素
>>> a
[1, 2, 3, 30]
实现原理

在通过切片索引时,会自动将切片语法转成slice对象,该对象包括起始位置、结束位置和步长三个参数,举例:

a = [1,2,3,4,5]
print(a[0::2])
print(a[slice(0, None, 2)])

# [1, 3, 5]
# [1, 3, 5]

可以看出两个结果都是一样的,然后对象只需要实现__getitem__方法,在内部处理slice的处理逻辑即可,我们可以自己模拟实现一个自己的slice对象和相关的切片处理逻辑,举例:

class MySlice:
    def __init__(self, start=None, end=None, step=None):
        self.start = start
        self.end = end
        self.step = step
    def get_slice(self):
        return self.start, self.end, self.step

class Test:
    def __init__(self, data=[]):
        self.data = data

    def __setitem__(self, item, value):
        if isinstance(item, MySlice):
            start, end, step = self.parse_slice(item)
            value = iter(value)
            for i in range(start, end, step):
                self.data[i] = next(value)
        else:
            self.data[item] = value

    def __getitem__(self, item):
        cls = type(self)
        if isinstance(item, MySlice):
            start, end, step = self.parse_slice(item)
            return cls([self.data[i] for i in range(start, end, step)])
            # 重新实例化一个自身对象返回
        return cls(self.data[item])

    def parse_slice(self, s):
        """自定义切片设置默认值"""
        start, end, step = s.get_slice()
        if start == None:
            start = 0
        if end == None:
            end = len(self.data)
        if step == None:
            step = 1
        return start, end, step

    def __str__(self):
        return str(self.data)

li = Test([1,2,3,4,5])
sli = MySlice(0, None, 2)
print(li[sli])
li[sli] = [9, 8, 7]
print(li)

# [1, 3, 5]
# [9, 2, 8, 4, 7]

映射类

不可变映射类

MappingProxyType,继承于Mapping类,相当于一个只读的字典,源码如下:

MappingProxyType = type(type.__dict__)

我们可以从types模块下导入,使用举例:

from types import MappingProxyType

di = MappingProxyType({"a":1, "b":2})
print(di)
di["a"] = 2
# 不允许修改,这句会报错
可变映射类

dict,继承于MutableMapping类,而该类继承于Mapping类

UserDict

可以理解为用python实现的字典,由于python中的列表、字典都是用C写的,内部有些魔法方法之类的未必能生效,因此如果希望继承一个字典的话,那么可以继承collection模块下提供的UserDictcollection下还提供了UserList/UserStr与之同理),举例:

from collections import UserDict
class A(dict):
    def __setitem__(self, item, value):
        super().__setitem__(item, value*2)

class B(UserDict):
    def __setitem__(self, item, value):
        super().__setitem__(item, value*2)

a = A(x=1)
b = B(x=1)
print(a)
print(b)

# {'x': 1}
# {'x': 2}

可以看到继承自dict的类,实例化的对象的__setitem__方法没生效,但是继承自UserDict的却生效了

defaultdict

UserDict一样是dict的子类,初始化时可以指定当索引内容不存在时的默认值类型,本质是内部实现了__missing__的魔法方法,当key的值获取失败时,则会设置一个默认类型的值,举例:

def default():
    return {
        "name": "aaa",
        "age": 18
    }

a = defaultdict(default)
# 指定默认是一个固定内容的字典,传入的默认对象必须是可调用的,这里传入一个函数,也可以传入list、dict等数据类型
print(a["x"])
a["y"] = 2
print(a)

# {'name': 'aaa', 'age': 18}
# defaultdict(, {'x': {'name': 'aaa', 'age': 18}, 'y': 2})
可哈希对象
  • 对于字典的key、集合的元素都必须是可哈希的才能存入
  • 不可变对象都是可hash的,如:strfronzesettuple,如果希望自己定义的类可哈希,那么必须实现__hash__的魔法方法
    注:
    不可变对象如果是可哈希的,还必须满足子元素也都是可哈希的条件,举例:
print(hash((1,2,3)))
# 元组内部维护的数字是不可变的,因此可哈希
li = [1,2,3]
print(hash((1,li)))
# 元组内部有一个可变的列表,不可哈希,因此会报错

# 2528502973977326415
# TypeError: unhashable type: 'list'

你可能感兴趣的:(Python 数据类型)