简介
- 这里将从数据结构、字符串、迭代器、文件IO、类与对象、多线程、装饰器等问题入手
- 以写代码为主,讲述实用编程技巧,不多谈概念(需要有较好基础)
- 面试可用!
数据结构
问题:过滤列表中的负数
- 一般的解决方案是列表循环(代码过于简单不写了)
- 1.使用列表解析
import random
alist = [random.randint(-10,10) for _ in range(10)]
print(alist)
blist = [i for i in alist if i>=0]
print(blist)
- 2.使用
filter import random
alist = [random.randint(-10,10) for _ in range(10)]
print(alist)
gen = filter(lambda x:x>=0, alist)
blist = list(gen)
print(blist)
- 顺便搞一下字典的相关操作
adict = {'person%d'%i:random.randint(50,100) for i in range(1,11)}
print(adict)
bdict = {k:v for k,v in adict.items() if v>60}
print(bdict)
gen = filter(lambda item:item[1]>90, adict.items())
bdict = dict(gen)
print(bdict)
calc = lambda x,y:x**y
print(calc(2,5))
- 同理:集合也可以解析
问题:如何为元组的每个元素命名,提高可读性
- 元祖格式固定,对于确定字段的数据存储节省空间效率高
- 但是要通过index读取数据,比较烦
- 1.定义数值常量作为索引
NAME = 0
AGE = 1
HOBBY = 2
t1 = ('Roy', 18, 'basketball')
print(t1[NAME])
- 2.使用枚举类
from enum import IntEnum
class PersonEnum(IntEnum):
NAME = 0
AGE = 1
HOBBY = 2
print(t1[PersonEnum.NAME])
- 3.较好的方法,使用
collections.namedtuple 具名元组from collections import namedtuple
Student = namedtuple('Person',['name', 'age', 'hobby'])
t2 = Student('Roy', 18, 'basketball')
print(isinstance(t2, tuple))
print(t2.name)
问题:如何对字典排序
- 字典集合是无序的,就是说和存入顺序不一致
- 这里排序是根据字典的值进行的(并不是搞成存入顺序)
- 1.转换为元祖
print((3,2)>(1,3))
print((3,2)>(3,4))
adict = {k:random.randint(60,100) for k in 'abcdefg'}
print(adict)
alist = [(v,k) for k,v in adict.items()]
print(alist)
blist = sorted(alist)
print(blist)
z = zip([1,2,3], [4,5,6])
clist = list(z)
print(clist)
dlist = list(zip(adict.values(), adict.keys()))
print(dlist)
sorted(dlist, reverse=True)
- 直接使用
sorted 函数和lambda
alist = sorted(adict.items(), key=lambda item:item[1], reverse=True)
print(alist)
for i,(k,v) in enumerate(alist, 1):
adict[k] = (i, v)
print(adict)
问题:找出列表中出现次数最多的三个元素
- 将列表转换为字典,值为0,再遍历计数
alist = [random.randint(0,15) for _ in range(30)]
print(alist)
adict = dict.fromkeys(alist, 0)
print(adict)
for x in alist:
adict[x] += 1
print(adict)
blist = sorted(adict.items(), key=lambda item:item[1], reverse=True)[:3]
print(blist)
- 在很大的列表中排序是比较费时的!这里推荐使用堆
import heapq
clist = heapq.nlargest(3, ((v,k) for k,v in adict.items()))
print(clist)
- 使用
Counter 对象from collections import Counter
c = Counter(alist)
print(c.most_common(3))
问题:如何快速找到多个字典的公共键
- 使用列表生成器(解析)
from random import randint, sample
adict = {k:randint(1,3) for k in sample('abcdefg', randint(3,6))}
bdict = {k:randint(1,3) for k in sample('abcdefg', randint(3,6))}
cdict = {k:randint(1,3) for k in sample('abcdefg', randint(3,6))}
print(adict)
print(bdict)
print(cdict)
alist = [k for k in adict.keys() if k in bdict.keys() and k in cdict.keys()]
print(alist)
- 利用集合(set)的交集操作
s1 = adict.keys()
s2 = bdict.keys()
s3 = cdict.keys()
print(s1)
print(type(s1))
print(s1&s2)
from functools import reduce
sub = reduce(lambda a,b:a*b, range(1,11))
print(sub)
def square(x):
return x ** 2
map(square, [1,2,3,4,5])
s = reduce(lambda a,b:a&b, map(dict.keys, [adict,bdict,cdict]))
print(s)
问题:如何让字典保持有序
- 这里的排序不是根据之前的
value ,而是让字典元素按照添加顺序排序 - 这里使用
OrderedDict from collections import OrderedDict
from random import shuffle
keys = 'abcdefg'
od = OrderedDict()
for i, v in enumerate(keys, 1):
od[v] = i
print(od)
from itertools import islice
def queryByIndex(od, l,r=None):
l -= 1
if r is None:
r = l+1
key_list = list(islice(od, l, r))
return key_list
alist = queryByIndex(od, 3,5)
print(alist)
问题:如何实现用户的历史记录功能
- 使用容量为N的双端队列,python中提供了
deque from collections import deque
queue = deque([], 4)
queue.append(1)
queue.append(2)
queue.append(3)
queue.append(4)
queue.append(5)
queue.pop()
queue.popleft()
for i in queue:
print(i)
- 双端队列建立在内存中,程序执行结束历史记录就会消失,我们可以使用
pickle 落盘import pickle
pickle.dump(queue, open('q.pkl', 'wb'))
q2 = pickle.load(open('q.pkl', 'rb'))
print(queue)
print(q2)
字符串
问题:如何拆分含有多种分隔符的字符串
- 连续使用
str.split() 方法,借助map 方法str = 'ab,cd;efg|hig,klmn'
t = []
list(map(t.extend, [ss.split('|') for ss in str.split(',')]))
print(t)
alist = sum([ss.split('|') for ss in str.split(',')], [])
print(alist)
- 封装一个接口实现上面的功能
def my_split(str, splitList):
'''
指定分隔符分割字符串
:param str: 被分割
:param splitList: 分隔符列表
:return:
'''
res = [str]
for sp in splitList:
t = []
list(map(lambda ss:t.extend(ss.split(sp)), res))
res = t
return res
str = my_split(str, [',', ';', '|'])
print(str)
- 使用
re 的split 模块import re
str1 = re.split('[,;|]', str)
print(str1)
问题:如何判断字符串a是否以字符串b开头或结尾
- 使用
str.startswith() 和str.endswith() 方法,返回True/False - 多个匹配时参数用元组传入
- 外番:使用
os 包修改文件权限的故事~(chmod)
问题:如何调整字符串中文本的格式
问题:如何将多个小字符串拼接成大字符串
- 将列表中的所有字符串元素拼接,最常见的还是
+ 操作s1 = 'abcde'
s2 = '|fghj'
s3 = s1.__add__(s2)
print(s1)
print(s3)
- “累加”操作,也可以使用
reduce 方法from functools import reduce
s = ['acd','def','ghi']
str = reduce(str.__add__, s)
print(str)
- 上面的方法是有问题的!每次拼接一个子串后会得到新的子串,之前的子串对象不再使用,时间空间都浪费巨大;这里使用
join() 方法(推荐!)s = ['acd','def','ghi']
str = ';'.join(s)
print(str)
问题:如何将字符串对齐
- 使用字符串的函数
s = 'abcd'
s1 = s.ljust(6, '*')
print(s1)
s2 = s.rjust(6,'*')
print(s2)
s3 = s.center(6,'*')
print(s3)
- 使用
format 方法s4 = format(s, '+>6')
print(s4)
s5 = format(s, '+^6')
print(s5)
- 可能我们最常见的还是使用format输出
print("{1} {0} {1}".format("hello", "world"))
print("网站名:{name}, 地址 {url}".format(name="Roy", url="www.roykun.com"))
问题:如何去掉字符串中不需要的字符
- 使用字符串的方法
s = ' abcde '
s1 = s.lstrip()
print(s1)
s2 = s.rstrip()
print(s2)
s3 = s.strip()
print(s3)
str = '+\tabcdef\n*-'
s4 = str.strip('+\t\n-*')
print(s4)
- 但是不能处理中间的字符,可以使用
replace 方法str = ' abc def gh '
s1 = str.replace(' ', '')
print(s1)
import re
str1 = ' \tabcd \nfg * \t hij'
s2 = re.sub('[ \t\n*]+', '', str1)
print(s2)
translate 方法str = 'abcd'
s1 = str.translate({ord('a'):'A'})
print(s1)
str.translate(str.maketrans('abc', 'ABC'))
对象迭代
问题:如何实现可迭代对象和迭代器对象
- 先从for循环说起
from collections import Iterable, Iterator
alist = [1,2,3,4,5]
print(isinstance(alist, Iterable))
for i in alist:
print(i)
it = iter(alist)
print(next(it))
- for循环中
in 后面必须是可迭代对象,默认调用iter 方法得到迭代器,再使用next 方法每次获取元素,知道迭代完抛出StopIterator 异常 - 迭代器是一次性的,消费完了就要重新iter
- 自定义迭代器,迭代获取列表中多个城市天气的数据
from collections import Iterable, Iterator
import requests
class WeatherIterator(Iterator):
def __init__(self, citys):
self.citys = citys
self.index = 0
def __next__(self):
if self.index == len(self.citys):
raise StopIteration
city = self.citys[self.index]
self.index += 1
return self.get_weather(city)
def get_weather(self, city):
url = 'http://wthrcdn.etouch.cn/weather_mini?city=%s'%city
r = requests.get(url)
data = r.json()['data']['forecast'][0]
return city, data['high'], data['low']
class WeatherIterable(Iterable):
def __init__(self, citys):
self.citys = citys
def __iter__(self):
return WeatherIterator(self.citys)
def show(w):
for i in w:
print(i)
w = WeatherIterable(['北京', '上海', '天津'])
show(w)
show(w)
- 直接使用
iter 方法得到的迭代器对象就是一次性的!相当于一个泛型的返回迭代器对象的方法 - 需要注意这两种方式的不同!
问题:如何使用生成器函数实现可迭代对象
- 生成器是一种特殊的迭代器,生成器函数是使用
yield 关键字的函数 - 我们这里编写一个求素数的类
all() 函数用于判断给定的可迭代参数 iterable 中的所有元素是否都为 TRUE,如果是返回 True,否则返回 False - 编写测试函数
- 很好理解,
next 方法会继续运行yield后面的语句,效果上和自定义的__next__ 相同 - 相当于可迭代对象类和迭代器类一起定义了!
- 可迭代对象(iter/__iter__)与迭代器(next/__next__)的故事到此为止!
问题:如何实现反向迭代
- 如何反向输出一个列表?
alist = [1,2,3,4,5,6]
alist.reverse()
for i in alist:
print(i)
print(alist[::-1])
ralist = reversed(alist)
print(type(ralist))
for i in ralist:
print(i)
- 如何实现一个浮点数发生器
FloatRange ?class FloatRange(Iterable):
def __init__(self, a, b, step):
self.a = a
self.b = b
self.step = step
def __iter__(self):
t = self.a
while t<self.b:
yield t
t += self.step
def __reversed__(self):
t = self.b
while t>self.a:
yield t
t -= self.step
fr = FloatRange(3.0, 4.0, 0.2)
for x in fr:
print(x)
print('------------------')
for i in reversed(fr):
print(i)
- 这是一个很长见的问题,浮点数默认以二进制存储,本身是有误差的,需要使用
Decimal 转为十进制运算 from decimal import Decimal
class FloatRange(Iterable):
def __init__(self, a, b, step):
self.a = Decimal(str(a))
self.b = Decimal(str(b))
self.step = Decimal(str(step))
def __iter__(self):
t = self.a
while t<self.b:
yield float(t)
t += self.step
def __reversed__(self):
t = self.b
while t>self.a:
yield float(t)
t -= self.step
fr = FloatRange(3.0, 4.0, 0.2)
for x in fr:
print(x)
print('------------------')
for i in reversed(fr):
print(i)
- 注意:
Decimal 需要传入字符串类型,因为浮点数本身就是不精确的
问题:如何对可迭代对象做切片操作
- 一个500行的日志文件,open之后是可迭代对象,能否取到100-300行的数据?
- 切片的实质是实现了
__getitem__ 方法,其中调用了slice 方法
f = open('opera.txt', 'r', encoding='utf8')
flist = f.readlines()
print(flist)
from itertools import islice
f.seek(0)
fs = islice(f, 2, 3)
for i in fs:
print(i)
- 自定义切片方法
def my_islice(file, start, end, step=1):
tmp = 0
for i, f in enumerate(file):
if i >= end:
break
if i >= start:
if tmp==0:
tmp = step
yield f
tmp -= 1
fsm = my_islice(f, 2, 3)
print(list(islice(range(0,10), 2, 8, 2)))
print(list(my_islice(range(0,10), 2, 8, 2)))
- 从我们自定义的方法也可以看出,从中间开始切还是会迭代前面的元素的!
问题:如何在一个for语句中迭代多个对象
- 案例:语文数学英语三科成绩同时迭代统计每个学生的总分
- 可以使用
zip 打包import random
chinese = [random.randint(60, 100) for x in range(10)]
math = [random.randint(60, 100) for x in range(10)]
English = [random.randint(60, 100) for x in range(10)]
print(chinese, math, English)
alist = list(zip(chinese, math, English))
print(alist)
score = []
for c, m, e in alist:
score.append(c+m+e)
print(score)
blist = list(map(lambda c, m, e:c+m+e, chinese, math, English))
print(blist)
clist = list(map(lambda *args:args, chinese, math, English) )
print(clist)
- 上面是并行统计的,如果要串行,可以使用
chain 方法优雅的串起多个容器,返回迭代器!from itertools import chain
dlist = list(chain(*[[2,3], [4,5,6], [6,7,8]]))
print(dlist)
小结
- 问题还将继续,技巧繁多,只有领会到python编程的风格才能熟稔于心!
- 可迭代对象和迭代器的知识,以及生成器的使用是难点
|