社区所有版块导航
Python
python开源   Django   Python   DjangoApp   pycharm  
DATA
docker   Elasticsearch  
aigc
aigc   chatgpt  
WEB开发
linux   MongoDB   Redis   DATABASE   NGINX   其他Web框架   web工具   zookeeper   tornado   NoSql   Bootstrap   js   peewee   Git   bottle   IE   MQ   Jquery  
机器学习
机器学习算法  
Python88.com
反馈   公告   社区推广  
产品
短视频  
印度
印度  
Py学习  »  Python

如何优雅的写好Pythonic代码?

Python爱好者社区 • 8 年前 • 943 次点击  

 


作者:曹金龙

个人公众号:Python那些事


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!'


另外,while以及try关键字都可以和else搭配使用.


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


这些Pythonic的代码是否让你耳目一新?你还能写出哪些Pythonic的代码?



关注公众号,“Python爱好者社区”,回复“爬虫”即可获取崔老师爬虫免费学习视频。



Python爱好者社区


为大家提供与Python相关的最新技术和资讯。

长按指纹 > 识别图中二维码 > 添加关注


今天看啥 - 高品质阅读平台
本文地址:http://www.jintiankansha.me/t/HNBkFL01V3
Python社区是高质量的Python/Django开发社区
本文地址:http://www.python88.com/topic/3268