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]
在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搭配使用,具体可以参考小编之前写的文章 Python中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
具体原理可以参考小编之前写的 with语句那点事。
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')
如果想深入理解装饰器,可以阅读小编之前写的 深入理解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技能