背景
python 语言的最大特点就是整洁,但我老是按照其他语言的思路来写代码,经常导致代码繁琐复杂。所以,要努力学习,努力写出具有 pythonic 风格的代码,才有勇气在别人面前说我会写 python,而不是只会写 hello world 的菜瓜。
代码片段
变量值交换
以前使用最多的方式就是通过中间变量来进行交换。如下:
1
2
3
4
5
6
7
8
| a = 10
b = 20
tmp = a
a = b
b = tmp
print("a = %d, b = %d" %(a, b))
|
运行结果:
而 python 中交换变量的方式。如下:
1
2
3
4
5
6
| a = 10
b = 20
a, b = b, a
print("a = %d, b = %d" %(a, b))
|
运行结果:
列表推导式
求 20 以内的整除 3 的数的平方的列表,不使用列表推导式的代码。如下:
1
2
3
4
5
6
| numbers = []
for x in range(20):
if x % 3 == 0:
numbers.append(x*x)
print(numbers)
|
运行结果:
1
| [0, 9, 36, 81, 144, 225, 324]
|
使用列表推导式的代码,可以应用于列表、集合或者字典。如下:
1
2
3
4
5
6
7
| numbers_list = [x*x for x in range(20) if x % 3 == 0]
numbers_set = {x*x for x in range(20) if x % 3 == 0}
numbers_dict = {x: x*x for x in range(20) if x % 3 == 0}
print(numbers_list)
print(numbers_set)
print(numbers_dict)
|
运行结果:
1
2
3
| [0, 9, 36, 81, 144, 225, 324]
{0, 225, 36, 324, 9, 144, 81}
{0: 0, 3: 9, 6: 36, 9: 81, 12: 144, 15: 225, 18: 324}
|
字符串拼接
以前字符串拼接会惯性地使用 +
作为连接符,由于像字符串这种不可变对象在内存中生成后无法修改,合并后的字符串会重新开辟出一块内存空间来存储。因此每合并一次就会单独开辟一块内存空间,这样会占用大量的内存空间,严重影响代码的效率。
1
2
3
4
5
6
7
| words = ['I', ' ', 'love', ' ', 'Python', '.']
sentence = ''
for word in words:
sentence += '' + word
print(sentence)
|
运行结果:
使用 join 来进行字符串连接的 python 写法如下:
1
2
3
4
5
| words = ['I', ' ', 'love', ' ', 'Python', '.']
sentence = ''.join(words)
print(sentence)
|
运行结果:
字符串翻转
Java或者C++等语言的写法是新建一个字符串,从最后开始访问原字符串:
1
2
3
4
5
6
7
| a = 'I love Python.'
reverse_a = ''
for i in range(0, len(a)):
reverse_a += a[len(a) - i - 1]
print(reverse_a)
|
运行结果:
python 则将字符串看作 list,而列表可以通过切片操作来实现反转:
1
2
3
4
5
| a = 'I love Python.'
reverse_a = a[::-1]
print(reverse_a)
|
运行结果:
for-else 语句
在 C 语言或 Java 语言中,寻找一个字符是否在一个 list 中,通常会设置一个布尔型变量表示是否找到:
1
2
3
4
5
6
7
8
9
10
11
| 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 语句的时候才会执行:
1
2
3
4
5
6
7
8
9
10
| cities = ['BeiJing', 'TianJin', 'JiNan', 'ShenZhen', 'WuHan']
tofind = 'Shanghai'
for city in cities:
if tofind == city:
print('Found!')
break
else:
# 执行else中的语句意味着没有执行break
print('Not found!')
|
运行结果:
enumerate 迭代对象
打印一个列表的索引及其内容,可以用如下代码实现:
1
2
3
4
5
6
| cities = ['BeiJing', 'TianJin', 'JiNan', 'ShenZhen', 'WuHan']
index = 0
for city in cities:
index = index + 1
print(index, ':', city)
|
运行结果:
1
2
3
4
5
| 1 : BeiJing
2 : TianJin
3 : JiNan
4 : ShenZhen
5 : WuHan
|
而通过使用 enumerate 则极大简化了代码,enumerate 类接收两个参数,其中一个是可以迭代的对象,另外一个是开始的索引。这里索引设置为从 1 开始(默认是从0开始):
1
2
3
| cities = ['BeiJing', 'TianJin', 'JiNan', 'ShenZhen', 'WuHan']
for index, city in enumerate(cities, 1):
print(index, ":", city)
|
运行结果:
1
2
3
4
5
| 1 : BeiJing
2 : TianJin
3 : JiNan
4 : ShenZhen
5 : WuHan
|
lambda 定义函数
不使用 lambda 则需要单独定义一个函数:
1
2
3
4
| def f(x):
return x * x
print(f(20))
|
运行结果:
lambda可以返回一个可以调用的函数对象,会使得代码更为简洁。
1
2
3
| f = lambda x: x * x
print(f(20))
|
运行结果:
上下文管理
在打开文件时,通常是通过捕获异常来进行实现的,并且在 finally 模块中对文件来进行关闭:
1
2
3
4
5
6
7
8
| try:
file = open('python.txt')
for line in file:
print(line)
except:
print("File error!")
finally:
file.close()
|
而通过上下文管理中的 with 语句可以让代码非常简洁:
1
2
3
| with open('python.txt') as file:
for line in file:
print(line)
|
装饰器
装饰器在 python 中应用特别广泛,其特点是可以在具体函数执行之前或者之后做相关的操作,比如:执行前打印执行函数的相关信息,对函数的参数进行校验;执行后记录函数调用的相关流水日志等。使用装饰器最大的好处是 使得函数功能单一化,仅仅处理业务逻辑,而不附带其它功能。
在函数调用前打印时间函数名相关的信息,不使用装饰器可以用如下代码实现:
1
2
3
4
5
6
7
| from time import ctime
def foo():
print('[%s] %s() is called' % (ctime(), foo.__name__))
print('Hello, Python')
print(foo())
|
运行结果:
1
2
3
| [Wed May 12 16:12:20 2021] foo() is called
Hello, Python
None
|
这样写的问题是业务逻辑中会夹杂参数检查,日志记录等信息,使得代码逻辑不够清晰。所以,这种场景需要使用装饰器:
1
2
3
4
5
6
7
8
9
10
11
12
13
| 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')
print(foo())
|
运行结果:
1
2
3
| [Wed May 12 16:11:20 2021] foo() is called
Hello, Python
None
|
生成器函数
以计算斐波那契数列为例,使用列表可以用如下代码来实现:
1
2
3
4
5
6
7
8
9
10
| 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
print(fib(10))
|
运行结果:
1
| [1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
|
生成器是需要的时候生成的,基本不占用内存空间。是通过 yield 关键字来实现的:
1
2
3
4
5
6
7
8
9
10
| def fib(max):
n, a, b = 0, 0, 1
while n < max:
yield b
a, b = b, a + b
n = n + 1
res = fib(10)
for i in res:
print(i)
|
运行结果:
1
2
3
4
5
6
7
8
9
10
11
12
|
def fib(max):...
1
1
2
3
5
8
13
21
34
55
|
链式比较
1
2
3
4
5
6
|
>>> x = 79
>>> 80 < x < 90
False
>>> 70 < x < 80
True
|
函数返回多个值
在 Java 语言中,当函数需要返回多个值时,通常的做法是生成一个 Response 对象,然后将要返回的值写入对象内部。而 python 不需要这样做,可以直接返回多个值:
1
2
3
4
| def f():
error_code = 0
error_desc = "成功"
return error_code, error_desc
|
使用的时候也会非常简单:
1
2
| code, desc = f()
print(code, desc)
|
运行结果:
* 运算符解包
*
运算符和 **
运算符完美的解决了将元组参数、字典参数进行 unpack,从而简化了函数定义的形式,如:
1
2
3
4
5
6
| def fun(*args, **kwargs):
for eacharg in args:
print('tuple arg:', eacharg)
print(kwargs)
fun('I', 'love', 'Python', a=1, b=2)
|
运行结果:
1
2
3
4
| tuple arg: I
tuple arg: love
tuple arg: Python
{'a': 1, 'b': 2}
|
找出列表中出现最多的数
1
2
3
| num = [1, 3, 3, 4, 5, 6, 3, 6, 6, 6]
print(max(set(num),key=num.count))
|
运行结果: