Python——编程规范

Python 编程中经验型的一些规范
持续更新


使用li[:]代替copy.copy(li)

  • li[:]等价于copy.copy(li)
  • li[:]不等价于copy.deepcopy(li)
  • li[:]可以简化代码

从后开始访问列表

  • list作为stack用时访问栈顶元素list[-1]
    1
    2
    3
    4
    5
    6
    7
    8
    9
    list1 = [1, 5, 2, 3]
    print list1[-1]
    print list1[-2]
    print list1[1:-1]

    # output:
    3
    2
    [5, 2]

函数内函数

  • Python可以在函数内部定义函数,但是要注意不能与外部变量混淆
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    def sum_all(alist):

    def sum_two(a, b):
    return a+b

    result = 0
    for i in alist:
    result = sum_two(result, i)
    return result


    print sum_all([1, 2, 3, 4, 5])
    # output:
    15

不要随意调用特殊方法

(比如:__len__)

  • 特殊方法不应该被开发者调用,而应该被开发者定义新类时实现
  • 一般使用len(object)即可
  • 原因: 内置的方法(比如:len())可能会直接返回对象中的ob_size属性,而不用调用__len__()(这个函数往往会用迭代或者其他比较复杂的方法实现)

多使用列表推导

(list comprehension, 简写listcomps)和生成器表达式(generator expression, 简写genexps)

  • listcomps

    1
    [str(i) for i in range(1,10)]
  • genexps

    1
    (str(i) for i in range(1,10))

考虑使用reduce而不是循环语句

  • reduce
    1
    2
    3
    def add(x, y):
    return x + y
    reduce(add, [1,2,3,4,5])
1
2
from operator import mul
reduce(mul, range(1, 10))

多使用pprint

  • (Pretty Print)而不是print
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    import pprint
    l1 = (1, {2: 3}, "first", ("second", 5, 6, [7, 8, 9]), [1, 3, 5], "this is a pprint")
    pprint.pprint(l1)

    # Output:
    (1,
    {2: 3},
    'first',
    ('second', 5, 6, [7, 8, 9]),
    [1, 3, 5],
    'this is a pprint')

多使用三目运算符

  • Python不像Java和C++一样,有x?y:z这样的三目运算符号,但是可以有自己的特殊使用方法,等价于三目运算符且更容易理解

    1
    2
    3
    # same as x?y:z
    result = y if x is True else z
    return y if x is True else z
    • 注意: 在使用return语句中的三目运算时必须有else语句,否则编译不通过,因为返回值可能会缺失

匿名变量的使用

  • 初始化一个列表时
    1
    list1 = [_ for _ in list2 if _.val > 10]

Python异常处理

  • 异常处理的正确姿势, 注意如果不是必要的话不要使用Exception, 可以考虑列出来需要捕获的所有异常, 然后在函数内部判断异常类型

    1
    2
    3
    4
    5
    try:
    read some thing
    except IOError, ValueError, e:
    exception_type = type(e)
    print("%s" % e)
  • 当然, 我们一般为了方便也会直接使用下面的方法

    1
    2
    3
    4
    5
    try:
    read some thing
    except Exception, e:
    exception_type = type(e)
    print("%s" % e)

位运算


函数

  • 内部函数访问全局变量时使用global关键字声明(与外部函数一样)
  • 内部函数访问外部函数的变量使用nonlocal(仅限Python 3)关键字声明
  • 如果没有声明
    • 变量变为只读的
      • 可以写出a.append()这样的语句
      • 但不可以写出a = b这样的语句
  • 函数内部变量与函数外同名时:
    • 若写出赋值操作,则认为当前变量为局部变量
    • 否则认为是函数外的全局变量
    • 若先访问变量(一般访问,不是对变量赋值,此时视为全局变量),然后对变量赋值(此时视为局部变量),则产生矛盾,Python解释器报错

三元表达式的使用

  • 如果在三元表达式使用在加法中,需要加上括号,不然整体意思会变成错误的

  • 比如两个结点的加法操作,带有进位carry,我们可以简单的写一行:

    • 下面是错误示例:

      1
      val = l1.val if l1 else 0 + l2.val if l2 else 0 + carry
    • 上面的表达式可以理解为如果l1不为空,返回l1.val,否则返回0 + l2.val if l2 else 0 + carry

    • l1为空时又可以理解为如果l2不为空,返回0+l2.val否则返回0+carry

  • 正确的写法

    1
    val = (l1.val if l1 else 0) + (l2.val if l2 else 0) + carry

迭代dict的键时用dict.iterkeys()

  • 迭代键值时使用iterkeys()而不是keys()

  • iterkeys()返回一个迭代器而不是所有键值列表

  • keys()返回所有键值列表

    1
    2
    3
    4
    # 迭代效率高
    for _ in dict.iterkeys()
    # 迭代效率低
    for _ in dict.keys()
  • 迭代值时也同理

    1
    2
    3
    4
    # 迭代效率高
    for _ in dict.itervalues()
    # 迭代效率低
    for _ in dict.values()

zip实现二维列表的行列变换

只适用于Python3,因为涉及到*操作

1
2
3
4
5
6
a = [[1,2,3],
[4,5,6]]

print [e for e in zip(*a)]
# Output
[(1,4), (2,5), (3,6)]
  • Python3中才能使用*作为操作列表解压符
  • Python3中zip函数返回的是一个生成器而不是列表,所以需要迭代成列表

Python中逻辑运算符的巧用

不建议使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
print 0 or 1
print 10 or 1
print None or 1
print 0 and 1
print 1 and 10
print None and 10

# output:
1
10
1
0
10
None
  • 理解

    1
    2
    3
    4
    5
    6
    7
    print a or b 
    <==>
    print a if a else b

    print a and b
    <==>
    print b if a else a
  • 记忆:

    • a, b当做逻辑表达式,如果访问到b则返回b,否则返回a

reversed函数的使用

  • 接受参数为可迭代对象,返回一个反向访问迭代对象的迭代器

    1
    2
    3
    4
    5
    for i in reversed(range(n)):

    <==>

    for i in range(n-1, -1, -1):
  • reversed的使用似乎更优雅,也更容易理解

  • 容易遗忘的点,需要注意: reversed的参数必须是可迭代的对象,而不是两个数字


将简单的句子优雅的写到一行

  • 返回值

    1
    2
    3
    4
    if i < 0: return Flase
    <==>
    if i < 0:
    return False
  • 其他简单的执行语句直接合并

    1
    i = 10; j = 10
  • 判断语句之后使用

    1
    if bool: i = 10;
  • 判断语句后使用多条

    1
    if bool: i = 10; j = 20
  • while语句后使用

    1
    while(bool): print 10; print 20
  • 当句子较长时不建议使用

  • 一般为了美观,平时的项目也不建议使用

  • 刷题时为了让代码看起来简便,是可以使用的,但是这样会使得代码不易调试

  • 总之:慎用


用同一个值初始化两个变量

1
x = y = value
  • 在初始化链表头部时最常用
    1
    head, curr = ListNode(None)

使用collections.Counter

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import collections

counter = collections.Counter([1, 2, 1, 3, 4, "a", "a", "c"])

print counter
print counter["a"]
print counter[1]
print counter[-1]
print counter.get("a")
print counter.get(-1, 0)

# Output:
Counter({'a': 2, 1: 2, 2: 1, 3: 1, 4: 1, 'c': 1})
2
2
0
2
0

递归函数中定义函数会影响递归函数的运行效率吗?(待更新)