专栏首页猪圈子如何优雅的写好Pythonic代码?

如何优雅的写好Pythonic代码?

Python 与其它语言(比如Java或者C++)相比有比较大的区别,其中最大的特点就是非常简洁。如果按照其它语言的思路来写Python代码,则会使得代码繁琐复杂,并且容易出现Bug。在Python语言中,有个词很火,Pythonic。有的同学可能不明白这个词的意义,小编的理解就是用Python的写法写代码,而非是其它语言的通用的写法,写出Python的特点,写出Python的风格

下面,就通过几个示例来看一下不同思维的Python代码的差异。

1、变量值交换

这个问题最常见,大家从最开始写Java及C++等语言代码都会遇到这个问题。通常是通过一个临时变量来实现的:

tmp = a
a = b
b = tmp

而Python中可以直接交换两个变量,即:

a, b = b, a

2、列表推导式

列表推导式是Java及C++等语言没有的特性,能够很简洁的实现for循环,可以应用于列表,集合或者字典。

例如我们要求20以内的整除3的数的平方的列表,可以用如下代码实现:

numbers = []
for x in xrange(20):
    if x % 3 == 0:
        numbers.append(x*x)

而通过列表推导式一行代码即可实现:

numbers = [x*x for x in range(20) if x % 3 == 0]

列表推导式也可以用于集合和字典,将[...]变为{...}即可。集合和字典的实现如下所示:

集合:

numbers = {x * x for x in range(0, 20) if x % 3 == 0}

字典:

numbers = {x: x * x for x in range(0, 20) if x % 3 == 0}

3、字符串拼接

这是一个老生常谈的问题,当我们需要将数个字符串拼接的时候,习惯性的使用 "+" 作为连接字符串的手段。

然而,由于像字符串这种不可变对象在内存中生成后无法修改,合并后的字符串会重新开辟出一块内存空间来存储。因此每合并一次就会单独开辟一块内存空间,这样会占用大量的内存空间,严重影响代码的效率。

words = ['I', ' ', 'love', ' ', 'Python', '.']

sentence = ''
for word in words:
    sentence += '' + word

解决这个问题的办法是使用字符串连接的join,Python写法如下:

words = ['I', ' ', 'love', ' ', 'Python', '.']

sentence = ''.join(words)

4、如何快速翻转字符串

Java或者C++等语言的写法是新建一个字符串,从最后开始访问原字符串:

a = 'I love Python.'

reverse_a = ''
for i in range(0, len(a)):
    reverse_a += a[len(a) - i - 1]

而Python则将字符串看作list,而列表可以通过切片操作来实现反转:

a = 'I love Python.'
reverse_a = a[::-1]

5、for/else语句

在C语言或Java语言中,我们寻找一个字符是否在一个list中,通常会设置一个布尔型变量表示是否找到:

cities = ['BeiJing', 'TianJin', 'JiNan', 'ShenZhen', 'WuHan']
tofind = 'Shanghai'

found = False
for city in cities:
    if tofind == city:
        print 'Found!'
        found = True
        break
if not found:
    print 'Not found!'

而Python中的通过for...else...会使得代码很简洁,注意else中的代码块仅仅是在for循环中没有执行break语句的时候执行:

cities = ['BeiJing', 'TianJin', 'JiNan', 'ShenZhen', 'WuHan']
tofind = 'Shanghai'

for city in cities:
    if tofind == city:
        print 'Found!'
        break
else: 
    # 执行else中的语句意味着没有执行break
    print 'Not found!'

6、迭代对象善用enumerate类

enumerate类接收两个参数,其中一个是可以迭代的对象,另外一个是开始的索引。比如,我们想要打印一个列表的索引及其内容,可以用如下代码实现:

cities = ['BeiJing', 'TianJin', 'JiNan', 'ShenZhen', 'WuHan']

index = 0
for city in cities:
    index = index + 1
    print index, ':', city

而通过使用enumerate则极大简化了代码,这里索引设置为从1开始(默认是从0开始):

cities = ['BeiJing', 'TianJin', 'JiNan', 'ShenZhen', 'WuHan']
for index, city in enumerate(cities, 1):
    print index, ":", city

7、通过lambda来定义函数

lambda可以返回一个可以调用的函数对象,会使得代码更为简洁。若不使用lambda则需要单独定义一个函数:

def f(x):
    return x * x

map(f, [1, 2, 3, 4, 5, 6, 7, 8, 9])

使用lambda后仅仅需要一行代码:

map(lambda x: x * x, [1, 2, 3, 4, 5, 6, 7, 8, 9])

这里注意,lambda生成的是一个可以像其他函数一样使用的函数对象,即

def f(x):
    return x * x

等价于

lambda x: x * x

8、应用上下文管理

在打开文件时,通常是通过捕获异常来进行实现的,并且在finally模块中对文件来进行关闭:

try:
    file = open('python.txt')
    for line in file:
        print line
except:
    print "File error!"
finally:
    file.close()

而通过上下文管理中的with语句可以让代码非常简洁:

with open('python.txt') as file:
    for line in file:
        print line

9、使用装饰器

装饰器在Python中应用特别广泛,其特点是可以在具体函数执行之前或者之后做相关的操作,比如:执行前打印执行函数的相关信息,对函数的参数进行校验;执行后记录函数调用的相关流水日志等。使用装饰器最大的好处是使得函数功能单一化,仅仅处理业务逻辑,而不附带其它功能

在函数调用前打印时间函数名相关的信息,不使用装饰器可以用如下代码实现:

from time import ctime

def foo():
    print('[%s]  %s() is called' % (ctime(), foo.__name__))
    print('Hello, Python')

这样写的问题是业务逻辑中会夹杂参数检查,日志记录等信息,使得代码逻辑不够清晰。所以,这种场景需要使用装饰器:

from time import ctime

def deco(func):
    def decorator(*args, **kwargs):
        print('[%s]  %s() is called' % (ctime(), func.__name__))
        return func(*args, **kwargs)
    return decorator

@deco
def foo():
    print('Hello, Python')

10、使用生成器

生成器与列表最大的区别就是,列表是一次性生成的,需要较大的内存空间;而生成器是需要的时候生成的,基本不占用内存空间。生成器分为生成器表达式和生成器函数。

先看一下列表:

l = [x for x in range(10)]

改为生成器只需要将[...]变为(...),即

g = (x for x in range(10))

至于生成器函数,是通过yield关键字来实现的,我们以计算斐波那契数列为例,使用列表可以用如下代码来实现:

def fib(max):
    n, a, b = 0, 0, 1
    fibonacci = []
    while n < max:
        fibonacci.append(b)
        a, b = b, a + b
        n = n + 1
    return fibonacci

而使用生成器则变为:

def fib(max):
    n, a, b = 0, 0, 1
    while n < max:
        yield b
        a, b = b, a + b
        n = n + 1

11、Counter的使用

通常的词频统计中,我们的思路是:

需要一个字典,key值存储单词,value存储对应的词频。当遇到一个单词,判断是否在这个字典中,如果是,则词频加1;如果否,则字典中新增这个单词,同时对应的词频设置为1。

对应的Python代码实现如下:

#统计单词出现的频次
def computeFrequencies(wordList):
    #词频字典
    wordfrequencies = {}

    for word in wordList:
        if word not in wordfrequencies:
            # 单词不在单词词频字典中, 词频设置为1
            wordfrequencies[word] = 1
        else:
            # 单词在单词词频字典中, 词频加1
            wordfrequencies[word] = wordfrequencies[word]  + 1
     return wordfrequencies

有没有更简单的方式呢?答案是肯定的,就是使用Counter。collection 中的 Counter 类就完成了这样的功能,它是字典类的一个子类。Python代码变得无比简洁:

# 统计单词出现的频次
def computeFrequencies(wordList):
    #词频字典
    wordfrequencies = Counter(wordList)
    return wordfrequencies

12、链式比较

在实际数字比较中,我们可能需要多次比较多次,比如我们判断学习成绩是否位于某个区间:

x = 79

>>> x < 80 and x > 70
True

而更Pythonic的写法变身链式比较,即:

x = 79

>>> 80 < x < 90
False

>>> 70 < x < 80
True

这种写法给人的感受也更为直观易懂。

13、函数返回多个值

在Java语言中,当函数需要返回多个值时,通常的做法是生成一个Response对象,然后将要返回的值写入对象内部。而Python不需要这样做,可以直接返回多个值:

def f():
    error_code = 0
    error_desc = "成功"
    return error_code, error_desc

使用的时候也会非常简单:

code, desc = f()
print code, desc

14、使用*运算符

*运算符和** 运算符完美的解决了将元组参数、字典参数进行 unpack,从而简化了函数定义的形式,如:

def fun(*args):
    for eacharg in args:
        print 'tuple arg:', eacharg

fun('I', 'love', 'Python')

运行的结果为:

tuple arg: I

tuple arg: love

tuple arg: Python

15、找出列表中出现最多的数

这是经常会遇到的一个问题。解决这个问题的其中一个思路是按照标题11提供的词频统计的方法,先统计词频,然后遍历字典,找出具有最大词频的数字。有没有更简洁的方式?

当然,Python代码如下:

num = [1, 3, 3, 4, 5, 6, 3, 6, 6, 6]

print  max(set(num),key=num.count)

这些Pythonic的代码是否让你耳目一新?你还能写出哪些Pythonic的代码?在留言区跟大家分享一下吧!

本文来自公众号:python那些事

文部分来源网络,如有侵权请第一时间联系删除。

本文分享自微信公众号 - 猪圈子(Tester-sc)

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2019-02-14

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Python 二十三大实践、编码建议和技巧

    2020年,你又立了什么新的 Flag?新一年,我为大家准备 23 个非常优秀的 Python 实践技巧。希望这些诀窍能在实际工作中帮助大家,并且学到一些有用的...

    测试小兵
  • Python的这几个技巧,简直屌爆了

    我已经使用Python编程有多年了,即使今天我仍然惊奇于这种语言所能让代码表现出的整洁和对DRY编程原则的适用。这些年来的经历让我学到了很多的小技巧和知识,大多...

    测试小兵
  • Python之requests入门

    最近在学习Python相关的框架花了点时间,早期在python+Selenium+Unittest+HTMLTestRunner(UI自动化这块花了些时间实践了...

    测试小兵
  • Python环境配置&Pycharm安装

    周围关注Python的小伙伴越来越多,自己也有经常被问到Python怎么学,但工欲善其事,必先利其器,学之前咱先得把环境搭建起来,不然也只能纸上谈兵了。 本文...

    Awesome_Tang
  • Python库大全(涵盖了Python应用的方方面面),建议收藏留用!

    学Python,想必大家都是从爬虫开始的吧。毕竟网上类似的资源很丰富,开源项目也非常多。

    程序员宝库
  • Python之路,Day1 - Python基础1

    python的创始人为吉多·范罗苏姆(Guido van Rossum)。1989年的圣诞节期间,吉多·范罗苏姆为了在阿姆斯特丹打发时间,决心开发一个新的脚本解...

    超蛋lhy
  • Python之路,Day1 - Python基础1

    python的创始人为吉多·范罗苏姆(Guido van Rossum)。1989年的圣诞节期间,吉多·范罗苏姆为了在阿姆斯特丹打发时间,决心开发一个新的脚本解...

    超蛋lhy
  • Python学习之认知(一)

    ​ 编程语⾔主要从以下几个⻆度为进行分类,编译型和解释型、静态语言和动态语⾔、强类型定义语言和弱类型定义语言。

    py3study
  • python 第一章 介绍-1.pyth

    高级语言,面向对象,可扩展,可移植性用于在不同的平台(因为Python是用C写的,又由于C的可移植性)

    py3study
  • 基础知识:编程语言介绍、Python介绍

     1.2 汇编语言:在机器语言的基础上,用英文标签取代二进制指令来编写程序,本质上也是直接控制硬件。

    py3study

扫码关注云+社区

领取腾讯云代金券