python优雅编程_Python 有哪些优雅的代码实现让自己的代码更pythonic?

写给C类程序员。

从顺序容器中取多个元素赋值给变量

比如我们从数据库查询数据或者逐行一个CSV文件,获取到一条记录,每个字段被存在了一个list中(python的list)。对标C++可能是数组或vector。我们要取第一个、第二个元、第三个字段赋值给三个变量。

C程序员用python可能会这么写:

name = record[0]

age = record[1]

sex = record[2]

pythonic的写法是:

name, age, sex = record[:3]

当然C++11以后的std::tie也能做到类似的事。但比python灵活性还差点,谁让python是脚本语言呢。

交互两个变量的值

交换变量a和b的值,C程序员的习惯,当然是三段式啦。

temp = a

a = b

b = tmp

pythonic的写法是:

b, a = a, b

变量多次比较相等

一个条件,需要在一个变量等于三个值的情况下触发,应该怎么写呢?C程序员的习惯:

if var == 100 or var == 202 or var == 999:

...

pythonic的写法是用in:

if var in (100, 202, 999):

...

当然如果是比较一个变量不等于多个值的时候,可以用not in

循环从dict取数据,key可能不存在怎么办?

比如我们有一组数据,list里面套着dict。求dict里面某个key的value之和。但是dict里面这个key不是都存在(不存在的时候视作0)。

C程序员的习惯:

sum = 0

for data in data_list:

if data.has_key('score'): # 或者 if 'score' in data:

sum += data['score']

这样写当然没错,但是我们可以更简洁一点:

sum = 0

for data in data_list:

sum += data.get('score', 0)

没有三目运算符怎么办?

python没有?: 这个运算符。所以想实现一些比较简短的条件表达式(比如max = (a>b)?a:b ),不能像C那样简洁。C程序员感到不适,但也很耐心的写下:

if a > b:

max = a

else:

max = b

其实python也可以压缩到一行:

max = a if a > b else b

这个你可能觉得还不算pythonic,那么你还可以这样写:

max = (a > b and [a] or [b])[0]

如果,a,b都是非负数。可以这样写:

# 只适用不存在负数的情况

max = (a > b and a or b)

上面这个用法不适合存在负数的情况。因为当a是0的时候会视作False,即使a > b也会输出b。所以可以用转换为list的那种写法。

一行 if 可以做很多事。同样一行for也能做很多。

给list每个元素乘方

C语言的循环,C++的for_each?不用。

li = [1, 2, 3, 4, 5]

li = [i*i for i in li]

当然我们还有map函数:

m = map(lambda x:x*x, li)

li = list(m)

因为map函数返回的类型不是list了。所以有时候可能要转换一下。当然除了map,也少不了reduce。其他的还有filter等。这些函数用起来就和STL的algorithm很像了,C++程序员可能不会觉得pythonic。

一行代码提取出dict中的key存储到list

conf_dict = {'host':'127.0.0.1', 'port':3600, 'user':'root', 'pswd':123456}

key_list = [key for key in conf_dict]

当然提取value组成list也可以啦:

value_list = [conf_dict[key] for key in conf_dict]

仅仅是举例子如何用一行for。其实提取字典key和value有keys()、values()、items()。

但是如果你不是想单纯地提取key和value,这个一行for是有价值的。比如,提取key组成list,并对key统一做一个操作:

#key转大写

key_list = [key.upper() for key in conf_dict]

提取value组成list,然后对value统一做一个操作:

#批量减法

student_dict = {'xiaowang': 1992, 'xiaoming': 1993, 'xiaozhang': 1995}

aget_list = [2020-student_dict[key] for key in student_dict]

一行for构造dict

alpha_list = ['aaa', 'bbb', 'ccc', 'ddd']

alpha_dict = {alpha: alpha for alpha in alpha_list}

# 'aaa':'aaa'

alpha_dict = {alpha: alpha.upper() for alpha in alpha_list}

# 'aaa':'AAA'

alpha_dict = {alpha: alpha*2 for alpha in alpha_list}

# 'aaa':'aaaaaa'

有一定局限性,因为value和key要有某种关联。

活学活用的话,可以配合函数使用,生成一个函数名到函数的映射。来实现一个简单反射。

# 假设有一个list,里面存放的是函数

def test_a():

pass

def test_b():

pass

...

fun_list = [test_a, test_b, test_c, test_d]

fun_dict = {fun.__name__: fun for fun in fun_list}

# 'test_a':test_a

# 调用

fun_dict['test_a']()

当然Python语法天生支持反射,只是语法稍显繁琐,没有特别需求的话,自己像这样简单弄一个就行啦。

没有重载怎么办?

相信我。你可能并不需要重载。

def test(*argv, **kwargs):

print('========')

if len(argv) != 0:

print(argv)

print(*argv)

if len(kwargs) != 0:

print(kwargs)

test(1, 2, 4)

test({'name': 'guodong', 'age': 26, 'sex': 1})

test(name='guodong', age=26, sex=1)

d = {'name': 'guodong', 'age': 26, 'sex': 1}

test(**d)

test(2020, 3, 24, name='guodong', age=26, sex=1)

输出:

========

(1, 2, 4)

1 2 4

========

({'name': 'guodong', 'age': 26, 'sex': 1},)

{'name': 'guodong', 'age': 26, 'sex': 1}

========

{'name': 'guodong', 'age': 26, 'sex': 1}

========

{'name': 'guodong', 'age': 26, 'sex': 1}

========

(2020, 3, 24)

2020 3 24

{'name': 'guodong', 'age': 26, 'sex': 1}

不管什么类型都能传递。list也好,dict也罢,或者直接打散传入参数也好,参数个数可变也行。太灵活了。

打开文件

OK,这个老生常谈了。

普通写法:

f = open('file.txt')

lines = f.reads()

...

f.close()

pythonic:

with open('file.txt') as f:

lines = f.reads()

...

手撸AOP?NO

Java程序员可能对AOP模式比较熟悉。python中官方支持的装饰器语法,是实现AOP的利器。简单很多。这里不展开AOP了,介绍一下装饰器,其实思想就是GOF23的装饰器模式,只不过python官方给出了语法糖!好吧,这糖我吃了。

无侵入性地打印函数名和函数参数:

import functools

def log(func):

@functools.wraps(func)

def wrapper(*args, **kwargs):

print('===========')

print(func.__name__)

print('args:', *args)

print('kwargs:', *kwargs)

return func(*args, **kwargs)

return wrapper

@log

def test(*argv, **kwargs):

pass

像上面介绍*argv和**kwargs的那个例子,来执行一遍,这次我把test的函数体清空了。因为之前它里面也只是为了示例,里面没有逻辑只是打印参数而以,现在我们不需要了。

调用:

test(1, 2, 4)

test({'name': 'guodong', 'age': 26, 'sex': 1})

test(name='guodong', age=26, sex=1)

d = {'name': 'guodong', 'age': 26, 'sex': 1}

test(**d)

test(2020, 3, 24, name='guodong', age=26, sex=1)

可以输出:

===========

test

args: 1 2 4

kwargs:

===========

test

args: {'name': 'guodong', 'age': 26, 'sex': 1}

kwargs:

===========

test

args:

kwargs: name age sex

===========

test

args:

kwargs: name age sex

===========

test

args: 2020 3 24

kwargs: name age sex

为什么python要单独提一个pythonic,像C、C++、Java都没有Cic、C++ic、Javaic这种说法呢……个人感觉啊,那就是从C类编程语言(C、C++、Java、C#)入门开始学习编程的人太多了。把C类语言中的一些习惯,当成了编程语言的共性。然后Python又是一门很容易上手的语言,把知识进行替换,概念做一下迁移,很容易学会使用Python。但是这个代码风格始终逃不出C的园囿。多年以前我也会用Python写点脚本、写一些批处理任务。没觉得自己有什么问题,直到后来换了工作。用Python跑MR的时候,同事看了我的代码,说了一句:你这个代码啊,从实现来说,写的没错。但是呢,不够pythonic!

一句惊醒梦中人。自以为Python简单,自己会写,但对于Python之博大,其实并不自知,像我这样。这篇回答主要写给C类程序员,写给你,也写给我。好了。pythonic的语法技巧有很多,先介绍到这,没介绍的后面可能会补,比如yield,for else等。但是有些其实很难界定说是不是pythonic。只能挑出一些和C类语言迥异的写法,姑且认为因为和C类或其他语言写作习惯不一样,故而称之为pythonic吧……。

python的语法有爽点,也有痛点,语言风格不值得升华到什么哲学高度。比如没有switch,没有++,我真不会洗……一门语言而言,学一学,玩一玩,凑合着混口饭吃。捕蛇者说​zhuanlan.zhihu.comv2-09e9c86a6fbb3242d8c68877bfd90e03_ipico.jpg

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值