P019_02_python常用模块二

时间模块

  • 字符串:给人看的 2018-07-29
  • 时间戳时间:float时间:计算机看的
  • 结构化时间:元组,计算用的

常用方法

  1. time.sleep(secs) (线程)推迟指定的时间运行。单位为秒。
  2. time.time() 获取当前时间戳

strftime 格式化时间

1
2
3
print(time.strftime("%Y-%m-%d %H:%M:%S")) # year month day hour minute second

# 2018-07-29 12:01:20

时间格式化规则

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
%y 两位数的年份表示(00-99)
%Y 四位数的年份表示(000-9999)
%m 月份(01-12)
%d 月内中的一天(0-31)
%H 24小时制小时数(0-23)
%I 12小时制小时数(01-12)
%M 分钟数(00=59)
%S 秒(00-59)
%a 本地简化星期名称
%A 本地完整星期名称
%b 本地简化的月份名称
%B 本地完整的月份名称
%c 本地相应的日期表示和时间表示
%j 年内的一天(001-366)
%p 本地A.M.或P.M.的等价符
%U 一年中的星期数(00-53)星期天为星期的开始
%w 星期(0-6),星期天为星期的开始
%W 一年中的星期数(00-53)星期一为星期的开始
%x 本地相应的日期表示
%X 本地相应的时间表示
%Z 当前时区的名称
%% %号本身

结构化时间

时间元组:localtime将一个时间戳转换为当前时区的struct_time

1
2
time.localtime()
# time.struct_time(tm_year=2018, tm_mon=7, tm_mday=29, tm_hour=12, tm_min=5, tm_sec=42, tm_wday=6, tm_yday=210, tm_isdst=0)

不同时间格式的转换

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#时间戳-->结构化时间
#time.gmtime(时间戳) #UTC时间,与英国伦敦当地时间一致
#time.localtime(时间戳) #当地时间。例如我们现在在北京执行这个方法:与UTC时间相差8小时,UTC时间+8小时 = 北京时间
>>>time.gmtime(1500000000)
time.struct_time(tm_year=2017, tm_mon=7, tm_mday=14, tm_hour=2, tm_min=40, tm_sec=0, tm_wday=4, tm_yday=195, tm_isdst=0)
>>>time.localtime(1500000000)
time.struct_time(tm_year=2017, tm_mon=7, tm_mday=14, tm_hour=10, tm_min=40, tm_sec=0, tm_wday=4, tm_yday=195, tm_isdst=0)

#结构化时间-->时间戳 
#time.mktime(结构化时间)
>>>time_tuple = time.localtime(1500000000)
>>>time.mktime(time_tuple)
1500000000.0

#结构化时间-->字符串时间
#time.strftime("格式定义","结构化时间") 结构化时间参数若不传,则现实当前时间
>>>time.strftime("%Y-%m-%d %X")
'2017-07-24 14:55:36'
>>>time.strftime("%Y-%m-%d",time.localtime(1500000000))
'2017-07-14'

#字符串时间-->结构化时间
#time.strptime(时间字符串,字符串对应格式)
>>>time.strptime("2017-03-16","%Y-%m-%d")
time.struct_time(tm_year=2017, tm_mon=3, tm_mday=16, tm_hour=0, tm_min=0, tm_sec=0, tm_wday=3, tm_yday=75, tm_isdst=-1)
>>>time.strptime("07/24/2017","%m/%d/%Y")
time.struct_time(tm_year=2017, tm_mon=7, tm_mday=24, tm_hour=0, tm_min=0, tm_sec=0, tm_wday=0, tm_yday=205, tm_isdst=-1)

asctime

1
2
3
4
5
6
7
8
9
10
11
12
13
#结构化时间 --> %a %b %d %H:%M:%S %Y串
#time.asctime(结构化时间) 如果不传参数,直接返回当前时间的格式化串
>>>time.asctime(time.localtime(1500000000))
'Fri Jul 14 10:40:00 2017'
>>>time.asctime()
'Mon Jul 24 15:18:33 2017'

#时间戳 --> %a %d %d %H:%M:%S %Y串
#time.ctime(时间戳) 如果不传参数,直接返回当前时间的格式化串
>>>time.ctime()
'Mon Jul 24 15:19:07 2017'
>>>time.ctime(1500000000)
'Fri Jul 14 10:40:00 2017'

计算时间差

1
2
3
4
5
6
7
8
import time
true_time=time.mktime(time.strptime('2017-09-11 08:30:00','%Y-%m-%d %H:%M:%S'))
time_now=time.mktime(time.strptime('2017-09-12 11:00:00','%Y-%m-%d %H:%M:%S'))
dif_time=time_now-true_time
struct_time=time.gmtime(dif_time)
print('过去了%d年%d月%d天%d小时%d分钟%d秒'%(struct_time.tm_year-1970,struct_time.tm_mon-1,
struct_time.tm_mday-1,struct_time.tm_hour,
struct_time.tm_min,struct_time.tm_sec))

random模块

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
import random
#随机小数
random.random()
# 大于0且小于1之间的小数 0.7664338663654585
random.uniform(1,3)
#大于1小于3的小数 1.6270147180533838

#随机整数
random.randint(1,5) # 大于等于1且小于等于5之间的整数
random.randrange(1,10,2) # 大于等于1且小于10之间的奇数


#随机选择一个返回
random.choice([1,'23',[4,5]])
# #1或者23或者[4,5]

#随机选择多个返回,返回的个数为函数的第二个参数
random.sample([1,'23',[4,5]],2)
# #列表元素任意2个组合 [[4, 5], '23']


#打乱列表顺序
item=[1,3,5,7,9]
random.shuffle(item)
# 打乱次序
item # [5, 1, 3, 7, 9]

random.shuffle(item)
# item [5, 9, 7, 1, 3]

生成随机验证码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import random

def v_code():

code = ''
for i in range(5):

num=random.randint(0,9)
alf=chr(random.randint(65,90))
add=random.choice([num,alf])
code="".join([code,str(add)])

return code

print(v_code())

P019_01_python常用模块一

常用模块

  • collections(不太重要)
  • 时间模块
  • random(随机相关)
  • os模块(和操作系统打交道的)
  • sys模块(和python解释器打交道的)
  • 序列化模块(非常重要的)
  • re模块(前面已经用了)

collections模块

  1. namedtuple: 生成可以使用名字来访问元素内容的tuple
  2. deque: 双端队列,可以快速的从另外一侧追加和推出对象
  3. Counter: 计数器,主要用来计数
  4. OrderedDict: 有序字典
  5. defaultdict: 带有默认值的字典

namedtuple

我们知道tuple可以表示不变集合,例如,一个点的二维坐标就可以表示成:

1
p = (1, 2)

但是,看到(1, 2),很难看出这个tuple是用来表示一个坐标的。

这时,namedtuple就派上了用场:

1
2
3
4
5
from collections import namedtuple
Point = namedtuple('Point', ['x', 'y'])
p = Point(1, 2)
p.x # 1
p.y # 2

万能公式: namedtuple(‘名称’, [属性list]):

类似的,如果要用坐标和半径表示一个圆,也可以用namedtuple定义:

1
Circle = namedtuple('Circle', ['x', 'y', 'r'])

扑克牌功能

1
2
3
4
from collections import namedtuple
# namedtuple('扑克牌', ['花色', '数字'])
Card = namedtuple('Card', ['color', 'num'])
c = Card('红桃','2')

queue 队列 FIFO

first in first out

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import queue

q = queue.Queue()
q.put(10)
q.put(1)
q.put(12)

print(q.get()) # 10
print(q.get()) # 1
print(q.get()) # 12
print(q.get()) # 阻塞 队列里只有3个元素 再次进行取值就会阻塞在这里
print(q.get()) # 阻塞 队列里只有3个元素 再次进行取值就会阻塞在这里

print(q.qsize()) # 查看队列里元素个数 取一个少一个

例如电影票200张但是500个人买,前200人顺利买了票,后面的只能停在哪里等待票

deque 双端队列

使用list存储数据时,按索引访问元素很快,但是插入和删除元素就很慢了,因为list是线性存储,数据量大的时候,插入和删除效率很低。

deque是为了高效实现插入和删除操作的双向列表,适合用于队列和栈:

1
2
3
4
5
6
7
8
9
from collections import deque

dq = deque([1,2])
dq.append('a') # 从后面放数据 [1,2,'a']
dq.appendleft('b') # 从前面放数据 ['b',1,2,'a']
dq.insert(2,3) # 根据索引放数据 ['b','1','3','2','a']
print(dq.pop()) # 从后面取数据 a
print(dq.pop()) # 从后面取数据 2
print(dq.popleft()) # 从前面取数据 b

deque除了实现list的append()和pop()外,还支持appendleft()和popleft(),这样就可以非常高效地往头部添加或删除元素。

OrderedDict 有序字典

使用dict时,Key是无序的。在对dict做迭代时,我们无法确定Key的顺序。

如果要保持Key的顺序,可以用OrderedDict:

1
2
3
4
5
6
7
8
from collections import OrderedDict

d = dict([('a', 1), ('b', 2), ('c', 3)])
# dict的Key是无序的 {'a': 1, 'c': 3, 'b': 2}

od = OrderedDict([('a', 1), ('b', 2), ('c', 3)])
od
# OrderedDict的Key是有序的 OrderedDict([('a', 1), ('b', 2), ('c', 3)])

注意,OrderedDict的Key会按照插入的顺序排列,不是Key本身排序:

1
2
3
4
5
od = OrderedDict()
od['z'] = 1
od['y'] = 2
od['x'] = 3
od.keys() # 按照插入的Key的顺序返回 ['z', 'y', 'x']

defaultdict

有如下值集合 [11,22,33,44,55,66,77,88,99,90…],将所有大于 66 的值保存至字典的第一个key中,将小于 66 的值保存至第二个key的值中。

即: {‘k1’: 大于66 , ‘k2’: 小于66}

原生字典解决方法:很麻烦

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
values = [11, 22, 33,44,55,66,77,88,99,90]

my_dict = {}

for value in values:
if value>66:
if my_dict.has_key('k1'):
my_dict['k1'].append(value)
else:
my_dict['k1'] = [value]
else:
if my_dict.has_key('k2'):
my_dict['k2'].append(value)
else:
my_dict['k2'] = [value]

defaultdict字典解决方法

1
2
3
4
5
6
7
8
9
10
11
from collections import defaultdict

values = [11, 22, 33,44,55,66,77,88,99,90]

my_dict = defaultdict(list) # key的默认值为列表

for value in values:
if value>66:
my_dict['k1'].append(value)
else:
my_dict['k2'].append(value)

注意事项:

  • defaultdict()的参数必须是callable()的
  • 如果默认值为数字5就不能写defaultdict(5)
  • 你可以使用lambda表达式来实现

使用dict时,如果引用的Key不存在,就会抛出KeyError。如果希望key不存在时,返回一个默认值,就可以用defaultdict:

1
2
3
4
5
from collections import defaultdict
dd = defaultdict(lambda: 'N/A')
dd['key1'] = 'abc'
dd['key1'] # key1存在 'abc'
dd['key2'] # key2不存在,返回默认值 'N/A'

Counter(计数用的最没用的一个知识点)

Counter类的目的是用来跟踪值出现的次数。它是一个无序的容器类型,以字典的键值对形式存储,其中元素作为key,其计数作为value。计数值可以是任意的Interger(包括0和负数)。Counter类和其他语言的bags或multisets很相似。

1
2
3
c = Counter('abcdeabcdabcaba')
print c
输出:Counter({'a': 5, 'b': 4, 'c': 3, 'd': 2, 'e': 1})

其他详细内容

P018_01_python认识正则表达式

正则表达式

正则是通用的(几乎所有编程语言都适用的一个规则)

python里想使用正则只能引入re模块

1
import re

一个推荐的工具网站

正则工具网站

具体要记住的规则请参考我之前写的
[难以启齿的正则]文章

知识点总结的博客

Eva_J

匹配手机号的例子

1
2
3
4
5
6
import  re
phone_num = input('请输入手机号')
if re.match('^(13|14|15|18)[0-9]{9}$',phone_num):
print('合法')
else:
print('非法')

字符串中r的应用

原样匹配(取消「\」的转义)

1
正则 \\n 匹配 换行\n

贪婪匹配

1
2
3
4
5
<.*>  正则

<script>....</script>
# 匹配结果
<script>....</script>

惰性匹配

1
2
3
4
5
<.*?>  正则
<script>....</script>
# 匹配结果
<script>
</script>

几个常用的贪婪匹配

  • *?重复任意次,但尽可能少的重复
  • +?重复1次或更多次,但尽可能少的重复
  • ??重复0次或1次,但尽可能少的重复
  • {n,m}?重复n-m次,但尽可能少的重复
  • {n,}?重复n次以上,但尽可能少的重复

.*?的用法

1
2
3
4
5
6
7
. 任意字符
* 取0至无限长度
? 非贪婪模式


实例: .*?x
取前面任意的长度但是碰见x就结束了

re模块

相关重要方法:都是从一个字符串中匹配你要的规则

  • findall
  • search
  • match

findall 返回所有匹配规则的元素放入到列表里

1
2
3
4
5
6
7
import re
# 匹配所有a
res = re.findall('a','eva egon yuan')
print(res) # ['a','a']

res = re.findall('[a-z]+','eva egon yuan')
print(res) # ['eva','egon','yuan']

search

  • 从前往后找,匹配到一个就返回
  • 而且需要你调用group()才能返回结果
  • 但如果没有找到结果,那么返回的是None,调用group会爆错
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import re

res = re.search('a','eva egon yuan')
print(res.group()) # 'a'


res = re.search('j','eva egon yuan')
print(res) # None
print(res.group()) # None.group() 报错 报错

正确的写法

res = re.search('j','eva egon yuan')
if res:
print(res.group())

match 用法和search是一样的

  • match是从头开始匹配,如果正则从头开始匹配上,就返回一个变量
  • 匹配的内容调用group才能显示
  • 如果没匹配上,就返回None,调用group报错
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

res = re.match('e','eva egon yuan')
if res:
print(res.group())

结果:
'e'

res = re.match('a','eva egon yuan')
if res:
print(res.group())

结果: 什么也不打印


res = re.match('[a-z]','eva egon yuan')
if res:
print(res.group())

结果:
'eva'

split

1
2
3
4
5
6
res = re.split('[ab]','abcd')
# 先按a分割得到 ''和'bcd' 再按b分割 '','','cd'
print(res)
结果:

['','','cd']

sub

1
2
3
4
5
6
res = re.sub('\d', 'H', 'eva3egon4yuan4', 1)
#将数字替换成'H',参数1表示只替换1个
print(res) #evaHegon4yuan4

res = re.sub('\d', 'H', 'eva3egon4yuan4')
print(res) #evaHegonHyuanH

subn

返回元组(替换的结果,替换了多少次)

1
2
3
res = re.subn('\d', 'H', 'eva3egon4yuan4')
# 将数字替换成'H',返回元组(替换的结果,替换了多少次)
print(res) # ('evaHegonHyuanH', 3)

compile

  • 将正则表达式编译成为一个 正则表达式对象
  • 正则表达式对象调用正则方法
1
2
3
4
obj = re.compile('\d{3}')  #将正则表达式编译成为一个 正则表达式对象,规则要匹配的是3个数字
res = obj.search('abc123eeee') #正则表达式对象调用search,参数为待匹配的字符串
print(res.group())
#结果 : 123

finditer

  • 返回的是一个迭代器
  • 迭代元素要调用group才能查看

使用它就是为了节省内存

1
2
3
4
5
ret = re.finditer('\d', 'ds3sy4784a')   #finditer返回一个存放匹配结果的迭代器
print(ret) # <callable_iterator object at 0x10195f940>
print(next(ret).group()) #查看第一个结果
print(next(ret).group()) #查看第二个结果
print([i.group() for i in ret]) #查看剩余的左右结果

为什么search match要通过group才可以获取结果

分组的概念

1
2
3
4
5
res = re.search('^[1-9](\d{14})(\d{2}[0-9x]?$','110105199912122277')

print(res.group())
print(res.group(1))
print(res.group(2))

注意点「?」的第三种用法

  1. 在量词的地方表示匹配0次或一次
  2. 在量词后面表示惰性匹配的标志
  3. 放到分组的第一个代表取消分组优先

引入findall没有 group的概念所有它就优先匹配「()」组里的

1
2
3
4
5
6
7
import re

ret = re.findall('www.(baidu|oldboy).com', 'www.oldboy.com')
print(ret) # ['oldboy'] 这是因为findall会优先把匹配结果组里内容返回,如果想要匹配结果,取消权限即可

ret = re.findall('www.(?:baidu|oldboy).com', 'www.oldboy.com')
print(ret) # ['www.oldboy.com']

split的优先级查询

  • split也没有分组的机制,所以如果有分组就分组优先
1
2
3
4
5
6
7
8
9
ret=re.split("\d+","eva3egon4yuan")
print(ret) #结果 : ['eva', 'egon', 'yuan']

ret=re.split("(\d+)","eva3egon4yuan")
print(ret) #结果 : ['eva', '3', 'egon', '4', 'yuan']

#在匹配部分加上()之后所切出的结果是不同的,
#没有()的没有保留所匹配的项,但是有()的却能够保留了匹配的项,
#这个在某些需要保留匹配部分的使用过程是非常重要的。

其他规则

1
2
3
4
5
6
re.I(IGNORECASE)忽略大小写,括号内是完整的写法
re.M(MULTILINE)多行模式,改变^和$的行为
re.S(DOTALL)点可以匹配任意字符,包括换行符
re.L(LOCALE)做本地化识别的匹配,表示特殊字符集 \w, \W, \b, \B, \s, \S 依赖于当前环境,不推荐使用
re.U(UNICODE) 使用\w \W \s \S \d \D使用取决于unicode定义的字符属性。在python3中默认使用该flag
re.X(VERBOSE)冗长模式,该模式下pattern字符串可以是多行的,忽略空白字符,并可以添加注释

P017_02_python算法初识

算法

先看案例

1
2
3
4
5
6
99*13 =?   (100-1)*13
17*13 =? (13+4)*13
[1,5,2,66,22,33,21,110]
查找
排序
最短路径

二分查找算法

要点:

  • 必须处理有序的列表

用最快方式找到66的位置

1
a = [2,3,4,10,13,21,24,32,44,51,66,70,80,91,102,188]

以后更新

P017_01_python递归

递归函数

什么是递归:在函数中调用自身函数

1
2
3
4
5
6
7
8
9
10
11
12
# 死循环
while 1:
print(0)

# 如果用函数呢?
def aa():
print('从前有座山')
aa()

aa() #执行一部分后会报错
RecursionError: maximum recursion depth exceeded while calling a Python object
# 超过递归最大深度--因为一直的开辟空间函数没有结束

测试递归深度

1
2
3
4
5
6
7
8
9
10
n = 0
def xx():
global n
n+=1
print(n )
xx()

xx()

# 打印到998

递归的默认深度是997/998是python从内存角度出发做的限制

递归的深度是可以修改的,但是即使设置的过大依然要由硬件决定(我的hp能达到3222)

1
2
3
4
5
6
7
8
9
10
import sys
sys.setrecursionlimit(10000000)
n = 0
def xx():
global n
n+=1
print(n )
xx()

xx()

如果你说需要1000次递归才能实现

我建议不要用递归,因为要1000次才能实现的需求说明递归不合适

递归的缺点

  • 如果递归次数过多,就不适合用递归
  • 递归的缺点:占内存
  • 递归的优点:会让代码变简单

递归调用的定义

函数嵌套调用的一种特殊形式,函数在调用时,直接或间接调用了自身,就是递归调用

递归分为两个阶段:

  • 递推
  • 回溯

经典问题==>阶乘

1
2
3
4
5
6
7
8
9
def fn(n):
if (n == 1):
return 1;
else:
return fn(n-1) * n;


a = fn(5)
print(a) # 120

P016_03_python匿名函数

匿名函数

为实现很简单需求而设计的一句话函数

匿名函数使用规范

  • 匿名函数关键字是lambda
  • lambda写在最前面其后跟参数,然后以「:」为分割,最后实际的逻辑也就是最后的结果
  • 必须一行结束
  • 多个参数用「,」分开

万能公式 lambda 参数 : 返回值

1
2
3
4
xxx = lambda n:n*n

#两个参数
xxx = lambda m,n:m*n

求字段里值最大的

1
2
3
4
5
6
7
xx = {'k1':10,'k2':20,'k3':30}

'''
def fn(key):
return xx[key]
'''
max(xx,key=lambda k:xx[k])

容易拿分的面试题

1.问执行结果

1
2
3
4
5
6
7
8
d = lambda p:p*2
t = lambda p:p*3

x = 2
x = d(x) # 4
x = t(x) # 12
x = d(x) # 24
print(x)

2.有俩元组((‘a’),(‘b’)),((‘c’),(‘d’))如何转换为列表[{‘a’:’c’},{‘b’:’d’}]

提示zip拉链方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# step001 先用zip把格式转换
a = (('a'),('b'))
b = (('c'),('d'))

res = zip(a,b) # 此时是一个生成器

'''
for i in res:
print(i)

结果:
('a','c')
('b':'d')
'''

# step002 遍历这个生成器 将元组变为字典
# filter/map/sorted/max/min

# 分析出:明显是用map
def func(tup):
return {tup[0]:tup[1]}
res = map(func,res)

# step003 优化用lambda

res = map(lambda tup:{tup[0]:tup[1]},res)
print(list(res))

# step004 各种优化
res = map(lambda tup:{tup[0]:tup[1]},zip(a,b))

res = list(map(lambda tup:{tup[0]:tup[1]},zip(a,b)))

3.刁钻面试题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
def xx():
return (lambda x:i*x for i in range(4))

[m(2) for m in xx()]

结果
[0,2,4,6]

解析
先看xx函数
def xx():
return (lambda x:0*x,lambda x:1*x,lambda x:2*x,lambda x:3*x)
[m(2) for m in xx()]

m(2)实际就是个函数 并传递参数2带入。。。
[0,2,4,6]

推荐一个流程图的工具ProcessOn

P016_02_python_memoryview用途

注意:非重点知识点(几乎用不到)

memoryview

  • bytearray是可变(mutable)的字节序列,相对于Python2中的str,但str是不可变(immutable)的。
  • 在Python3中由于str默认是unicode编码,所以只有通过bytearray才能按字节访问。

memoryview为支持buffer protocol[1,2]的对象提供了按字节的内存访问接口,好处是不会有内存拷贝。

默认str和bytearray支持buffer procotol。

下面两种行为的对比:

简单点就是,str和bytearray的切片操作会产生新的切片str和bytearry并拷贝数据,使用memoryview之后不会。

1.不使用memoryview
1
2
3
4
5
6
7
8
9
10
>> a = 'aaaaaa'
>> b = a[:2] # 会产生新的字符串

>> a = bytearray('aaaaaa')
>> b = a[:2] # 会产生新的bytearray
>> b[:2] = 'bb' # 对b的改动不影响a
>> a
bytearray(b'aaaaaa')
>> b
bytearray(b'bb')
2.使用memoryview
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
>> a = 'aaaaaa'
>> ma = memoryview(a)
>> ma.readonly # 只读的memoryview
True
>> mb = ma[:2] # 不会产生新的字符串

>> a = bytearray('aaaaaa')
>> ma = memoryview(a)
>> ma.readonly # 可写的memoryview
False
>> mb = ma[:2] # 不会会产生新的bytearray
>> mb[:2] = 'bb' # 对mb的改动就是对ma的改动
>> mb.tobytes()
'bb'
>> ma.tobytes()
'bbaaaa'

我的使用场景是网络程序中socket接收和接收数据的解析:

  1. 使用memoryview之前的sock接收代码简化如下
1
2
3
4
5
6
7
8
9
10
def read(size):
ret = ''
remain = size
while True:
data = sock.recv(remain)
ret += data # 这里不断会有新的str对象产生
if len(data) == remain:
break
remain -= len(data)
return ret
  1. 使用meoryview之后,避免了不断的字符串拼接和新对象的产生
1
2
3
4
5
6
7
8
9
10
11
def read(size):
ret = memoryview(bytearray(size))
remain = size
while True:
data = sock.recv(remain)
length = len(data)
ret[size - remain: size - remain + length] = data
if len(data) == remain:
break
remain -= len(data)
return ret

返回memoryview还有一个优点,在使用struct进行unpack解析时可以直接接收memoryview对象,非常高效(避免大的str进行分段解析时大量的切片操作)。

例如:

1
2
3
mv = memoryview('\x00\x01\x02\x00\x00\xff...')
type, len = struct.unpack('!BI', mv[:5])
...

P016_01_python内置函数收尾

python中一个让你懵的面试题

数据类型有哪些?

  • int
  • bool

数据结构

  • dict
  • list
  • tuple
  • set
  • str

剩余内置函数

reversed() 保留原列表,返回一个反向的迭代器

1
2
3
4
5
6
7
l = [1,2,3,4,5]
l.reverse()
print(l) # 此时 l 还是那个列表只不过内容反序了

l = [1,2,3,4,5]
l2 = reversed(l) # 保留 l 不变 ,返回一个反序的迭代器
print(l2)

slice() 切片方法

1
2
3
4
l = (1,2,23,213,5612,342,43)
sli = slice(1,5,2)
print(l[sli])
print(l[1:5:2])

字符串相关内置方法

format()

1
2
3
4
5
6
7
8
print(format('test', '<20')) # 开辟20个字符空间 左对齐
print(format('test', '>40')) # 开辟40个字符空间 右对齐
print(format('test', '^40')) # 开辟40个字符空间 居中对齐

结果:
test
test
test

bytes() 把unicode转换成 「对应编码」 的bytes

1
2
3
4
5
6
7
8
9
10
# 我拿到的是gbk编码的,我想转成utf-8编码
print(bytes('你好',encoding='GBK')) # unicode转换成GBK的bytes
print(b'\xc4\xe3\xba\xc3'.decode(encoding='GBK'))
print(bytes('你好',encoding='utf-8')) # unicode转换成utf-8的bytes
print(b'\xe4\xbd\xa0\xe5\xa5\xbd'.decode(encoding='utf-8'))
结果:
b'\xc4\xe3\xba\xc3' gbk编码的byte编码
你好 decode按照gbk解码后的unicode字符
b'\xe4\xbd\xa0\xe5\xa5\xbd' utf-8编码的byte编码
你好 decode按照utf-8解码后的unicode字符

bytearry()

  • 网络编程 只能传二进制
  • 照片和视频也是以二进制存储
  • html网页爬取到的也是编码
1
2
3
4
5
6
7
8
9
b_array = bytearray('你好',encoding='utf-8') # unicode 你好转换为 utf-8编码的 byte数组
print(b_array)
print(len(b_array))
print(b_array[0])

结果:
bytearray(b'\xe4\xbd\xa0\xe5\xa5\xbd')
6
228

memoryview (非重点知识点)

  • 将数据转换为指定编码的byte之后进行一次memoryview就可以进行切片,同时:这个切片不创建新的,而且这个切片是字节类型的

详情请参考 https://segmentfault.com/q/1010000007137721

也可以看我的摘录上述链接的信息在 [P016_01_python内置函数收尾]

ord() 字符按照unicode转数字

1
2
3
print(ord('好')) # 22909
print(ord('a')) # 97
print(ord('1')) # 49

chr() 将数字转字符

1
chr(97) # a

ascii()

只要是ascii码中的内容,就打印出来,不是就转换成\u

repr() 原样输出

用于%r格式化输出(%r实际就是调用repr())

1
2
3
4
5
6
7
8
9
name = 'egg'
print('你好%r'%name)
print(repr('1'))
print(repr(1))

结果:
你好'egg'
'1'
1

数据集合相关方法

  1. 字典:
  • dict
  1. 集合:
  • set:无序排序且不重复,是可变的。
  • frozenset:是冻结的集合,他是不可变的,存在哈希值。可以作为字典的key

判断值的False/True

  • all
  • any
1
2
3
4
all(['a','',123]) # False 有一个值是False就是False

any(['a',123]) # True
any(['a','',123]) # True 有一个值是True就是True

拉链方法

  • zip
1
2
3
4
5
6
7
8
9
10
a = [1,2,3]
b = ['a','b','c']
# zip(a,b) # 返回的是 迭代器
for i in zip(a,b):
print(i)

结果:
(1,'a')
(2,'b')
(3,'c')

如果俩个列表长度不一致呢?

从头开始对齐,以短列表长度为准

1
2
3
4
5
6
7
8
9
a = [1,2,3,4]
b = ['a','b','c']
for i in zip(a,b):
print(i)

结果:
(1,'a')
(2,'b')
(3,'c')

多个呢?而且是元组呢?元组子元素继续是列表呢?

1
2
3
4
5
6
7
8
9
10
a = [1,2,3,4]
b = ['a','b','c']
c = ('*','**',[1,2])
for i in zip(a,b,c):
print(i)

结果:
(1,'a','*')
(2,'b','**')
(3,'c',[1,2])

如果是字典呢?

1
2
3
4
5
6
7
8
9
10
11
a = [1,2,3,4]
b = ['a','b','c']
c = ('*','**',[1,2])
d = {"k1":1,"k2":2}
for i in zip(a,b,c,d):
print(i)

结果:
(1,'a','*','k1')
(2,'b','**','k2')
-----------------------------由于字典的key是无序的 所以 k1 k2的顺序不是固定的

filter和map

filter(函数名,可迭代的数据类型)

根据返回值的布尔值来过滤

1
2
3
4
5
6
7
8
9
10
11
12
13
def is_odd(x):
return x%2 == 1

res = filter(is_odd,[1,4,6,7,9,12,17])
print(res) # 又是一个迭代器

for i in res:
print(i)
结果:
1
7
9
17

练习:求1-100开平方是100的数字

1
2
3
4
5
6
7
#要引入模块了
form math import sqrt
def func(n):
res = sqrt(n)
return res%1 == 0

res = filter(func,range(1,101))

map

1
2
3
4
5
6
7
8
res = map(abs,[1,-4,6,-8])
for i in res:
print(i)
结果:
1
4
6
8

总结:

  • filter执行之后的结果集合 <= 数据源
  • map 执行之后元素个数不变,值可能发生改变

sorted函数

可定制的排序

  • 生成了一个新列表
  • 不改变原列表
  • 占内存
1
2
3
4
5
6
a = [1,-4,6,5,-10]
a.sort(key=abs) # 在原列表基础上进行排序

b = [2,-4,6,-5]
print(sorted(b)) # 直接返回排序后的列表(已经不是生成器了,所以慎用),它排序后原列表还在
print(b)

花式用法

1
2
3
sorted(a,reversed=False)
sorted(a,reversed=True)
sorted(a,key=abs,reversed=False)

练习:列表按照每一个元素的长度排序

1
2
3
4
5
6
7
a = ['   ',[1,2],'hello world']
new_a = sorted(a,key=len)

你也可以这样
def xxx(n):
return len(n)
new_a = sorted(a,key=xxx)

P015_02_python内置函数二

字符串类型代码的执行

  • eval
  • exec
  • compile
1
2
3
4
5
eval('print(123)') # 123
exec('print(123)') # 123

eval('1+2+3') # 10
exec('1+2+3') # None

eval 和 exec区别

  • 都可以执行字符串代码
  • exec没有返回值

eval建议不要轻易使用,只能用在你明确知道要执行的代码

尤其eval(文件里的字符串/用户输入的) 非常危险

compile编译

1
2
3
4
code = '''for i in range(10):
print(i)
'''
exec(code) #

如果code的内容是500行代码 每次exec都要编译

compile就是一次编译多次执行

1
2
3
4
5
6
7
8
code1 = 'for i in range(10):print(i)'
compile1 = compile(code1,'','exec')
exec(compile1)


code1 = '1+2+3'
compile2 = compile(code2,'','eval')
print(eval(compile2)) # 6

pycharm没法帮你找到字符串里的变量声明

交互类命令要用 single

1
2
3
4
code3 = 'name = input("请输入账号")'
compile3 = compile(code3,'','single')
print(exec(compile3)) # 执行交互命令
print(name) # pycharm无法检测到name 因为它在待编译的模板里还没有执行

和数字相关

  • complex 复数
  • bool
  • int
  • float 当你小数位过多时就不准了

进制转换

  • bin() 二进制 0b开头
  • oct() 八进制 0o开头
  • hex() 十六进制 0x开头

数学运算

  • abs() 绝对值
  • divmod() div除法 mod取余 ==》 分页处理
  • round() 四舍五入
  • pow() 幂运算
  • sum()
  • min()
  • max()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
abs(-5) # 5
divmod(7,2) # (7整除2,7对2取余) =>(3,1)
divmod(9,5) # (9整除5,9对5取余) =>(1,4)
round(3.1415926,3) # 3.14

pow(2,3) # 2*2*2 ==》8
如果三个参数就是 对最后的参数取余
pow(2,3,3) # 8取余3 ==》2


sum(1,2,3) # 6
sum([1,2,3]) # 6
sum([1,2,3],10) # 10代表起始值 ==》 31

min([1,2,3,4]) # 1
min(1,2,3,4) # 1
min(1,2,3,-4) # -4
min(1,2,3,-4,key=abs) # 1

P015_01_python内置函数

什么是内置函数

就是不用你def就可以直接调用的 如

  • print()
  • input()
  • len()
  • type()
  • open()
  • tuple()
  • list()
  • int()
  • bool()
  • set()
  • dir()
  • id()
  • str()

作用域相关

  • locals()
  • globals()
1
2
global 变量
nonlocal 变量

迭代器/生成器相关

  • range
  • next
  • iter

next()

1
2
3
4
5
6
7
8
9
10
11
12
迭代器.__next__() 这个是迭代器的方法
next(迭代器) 这个是内置函数

它俩完成的事一样,而__next__()是真正完成功能的地方
------------------------------
# next()的实质就是
def next(迭代器):
迭代器.__next__()

# len()的实质就是
def len([]):
[].__len__()

iter()

1
2
迭代器 = iter(可迭代的)
迭代器 = 可迭代的.__iter__()

range()

1
2
3
print('__next__' in dir(range(10))) # False 可迭代的,但不是一个迭代器

print('__next__' in dir(iter(range(10))) # True 通过iter转换为迭代器

其他

dir

查看一个变量拥有的方法

1
2
3
dir([])
dir({})
dir(1)

callable

调用相关,我们知道函数调用要使用()才可以,但是我们怎么知道一个xxx()就可以调用呢?

1
2
3
4
print(callable(print)) # True

a = 1
print(callable(a)) # False

help

查看帮助文档(在一些政府,银行禁网的封闭开发情况下)

1
help(str) # 打印和 str相关的方法 以及用法

模块相关

  • import
1
2
3
4
5
import time # 表面是调用了一个关键字
time.time()
实际上是
time = __import__(time)
time.time()

文件相关

  • open
1
2
3
f = open('1.txt')
print(f.writeable()) # 代表能不能写入
print(f.readable()) # 代表能不能读取

内存相关

  • id() 获取变量的内存地址
  • hash() 和内存相关的东西
1
2
3
4
5
print(hash(12345))  # 有结果   
print(hash('fsdafsadfasdf')) # 有结果
print(hash((1,'aaa'))) # 有结果

print(hash([1,2,3])) # 报错 unhashable type:'list' 意思就是不可哈希

对于可哈希数据类型的hash值在一次程序执行过程中总是不变的

如出现两次 ‘hahaha’ 他俩哈希值相同

hash函数和字典的存储规则有关系

字典的查找速度特别快,只要有key可以瞬间找到值

1
2
3
4
5
6
7
8
9
原理:
step001
dict = {
key:value
}
第一次创建key的时候 会生成一个hash值 3214141244(标识一个内存地址)
然后把value放到这个地址里

最后查询 dict[key] 实际是 hash(key) ==> value

输入输出

  • input
  • print

input

1
res = input('请输入账号') # 输入成功后按回车才把值给 res

print 其实你不会

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
print() # 每次print会自动给你一个换行

不想要换行咋办

print('我们是祖国的花朵',end='')
print('我们是祖国的花朵',end='')
# 我们是祖国的花朵我们是祖国的花朵

print(1,2,3,4,5) # 1 2 3 4 5

有分隔符的输出

print(1,2,3,4,5,sep='|') # 1|2|3|4|5

将输出写入到文件
f = open('file','w')
print('aaa',file=f)
f.close()