教你三招,快速减少Python代码量

259 阅读4分钟

第一招:生成式

第一类:列表生成式

 场景:将a列表中的所有元素放到另一个list里面

我们使用for循环会写成下面的代码   

a = range(1,10)
b = []
for i in a:
    b.append(i)
print(b)

但是,python这么优雅的语言,允许我们使用列表生成式进行数据拷贝,数据过滤(相当于filter),实现相同的功能,列表生成式只需要一行

b=[i for i in a]

咱们看下远行结果:结果相同,代码量还少了,不错吧~

另外列表生成式还支持筛选,比如我们1到9之间的所有偶数


比较简洁吧,这种适合条件筛选和python的内置函数filter(过滤器)差不多,后面咱们说fiilter

第二类:字典生成式,和列表生成式的写法类似,它是下面的形式:

s = { 'i'+str(i):i for i in c } 

# c是上面的偶数列表

还可以使用将value设置成set(),list()用于一些数据制作非常有用,比如我们将要搜集各个省有哪些学校,我们已经有了省和对应学校的数据,但是学校在处理过程中重复出现,我们需要重新建立一个像是:省份:set(学校1,学校2,,,),这样的结构,我们可以将原来:省份:list[学校1,学校2,,,]写成下面的字典生成式:

pro_sh= { province:set(raw_schools) for province,raw_schools in pro_shools.iteritems()}

完成对原始字典的数据去重工作,在此去重列表可以直接使用set(),对list进行set(lst)获取一个set()集合,但是set不能进行下标操作,因此如果想要用下标,再讲set转回到list即可

2.第二招:生成器

什么是生成器,Python的生成器就是一个参数为可迭代对象的迭代函数,对应yield关键字。为啥用生成器,因为生成器可以节省内存?减少不必要的列表展开消耗,比如我们遍历一个列表

for i in a:
    print i

可以这样很简单,当时它在迭代遍历的时候相当于

for i in [1,2,3,4,5,6]:
    print i

这样对于循环过程的迭代会带来不小的问题,我们尝试使用生成器:

def generator(iterable_obj):
    for i in iterable_obj:
        print("before")
        yield i
        print("after")
for i in generator(a):
    print i

有啥不一样?生成器一次返回一个迭代对象,for用这一个对象去运行本次循环开销小了啊!

对于dict对象的遍历可以写运用items方法

def dict_generator(iterable_obj):
    for k,v in iterable_obj.items():
        print("before")
        yield k,v
        print("after")

3.第三招:map-reduce函数:分治,合并

map函数主要用于对list这种对象进行每个元素都就像相同的处理;

reduce函数用于将list元素进行合并;

比如我解析100个相同格式的xml,将所有xml文件中的item节点合成一个所有的列表,并返回一个list

首先,我们看一下file1的xml的格式;

<DOCUMENT>

 <item>a</item>

 <item>b</item>

 <item>c</item>

,,,

</DOCUMENT>

我们将解析xml到xmldom的过程处理作成一个函数

def read_parse(xmlfile_name):
    sobj = None
    with open(xmlfile_name,"r") as f:
        sxml = f.read()
        sobj etree.XML(sxml,etree.XMLParser())
    return sobj

然后我们假设有100个xml文件,文件名为test_i.xml i={1,100}

我们接着进行处理,拿到所有的文件名

file_names = [ "test_{_i}".format(_i=i) for i in range(1,101)]

接着我们对所有文件解析,并构建xml

xml_doms = map(lambda x:read_parse(x),file_names)

上面代码相当于

xml_doms = []
for i in file_names:
    xml_doms.append(read_parse(i))

考虑xml有可能解析失败返回None,我们需要使用filter进行None的过滤

xml_doms = filter(lambda x:x!=None,xml_doms)

过滤完之后,我们需要从各个xml_dom中获取所有item子节点,有如下代码

xml_doms = map(lambda x:x.findall(".//item"),xml_doms)

上面的lambda x:x.findall会将xml下面的所有item节点找到并返回一个list,因此上面代码会生成一个二维list,第一维是文件标号,第二维是那个文件中所有item节点

[[],[],[],[],...]

接着我们使用reduce合并所有列表

xml_doms = reduce(xml_doms,lambda x,y:x+y)

之所以可以这么干是因为list支持+操作,比如

所以合并list,不只是有extend方法,还有+号方法

这样我们将100个xml中的所有item节点都拿到了


最后,我们回顾一下哪三招?

生成式:列表生成式,字段生成式

生成器:可中途返回的参数为可迭代对象的可迭代函数

map-reduce:相同处理,归一合并

你get到了吗?