Skip to content

Latest commit

 

History

History
391 lines (308 loc) · 7.54 KB

basic3.md

File metadata and controls

391 lines (308 loc) · 7.54 KB

Python的基础故事(三)

本篇文章为大家带来Python中的基础编程方法和技巧。

连等赋值

Python允许采用连等赋值来同时赋值多个变量

e = 1
a = b = c = d = e
print(a)
# 1
print(b)
# 1
print(c)
# 1
print(d)
# 1

交换变量

其他语言中,交换变量通常这样写(以C++为例):

#include <stdio.h>

int main()
{
    int a = 1;
    int b = 2;
    int temp = a;
    a = b;
    b = temp;
    printf("a: %d, b: %d\n", a, b);
    return 0;
}

编译运行结果是:

a: 2, b: 1

而在Python中,可以这样来交换变量:

a = 1
b = 2
a, b = b, a
print(a)
# 2
print(b)
# 1

三元运算符

在其他语言中,三元运算符通常是这样的结构:

// C
#include <stdio.h>

int main()
{
    int a = 1;
    int b = a >= 1?2:3;
	return 0;
}

运行结果是当a >= 1b = 2,否则b = 3,等价于:

// C
#include <stdio.h>

int main()
{
    int a = 1;
    int b = 0;
    if (a >= 1) 
    {
        b = 2;
    }
    else
    {
        b = 3;
    }
	return 0;
}

Python并不支持三元运算符,但是有语法糖支持三元操作,请看:

a = 1
b = 2 if a == 1 else 3
print(b)
# 2

if条件判断为True,则b等于前面的值,否则等于后面的值,这样便实现了三元操作。

索引与列表与遍历

enumerate

在系列的第一篇中(→传送门)提到可以利用for...in...直接遍历一个列表的值。但是,某些时候确实还需要获取到列表的索引,这时候该怎么做呢?利用enumerate

a = [x for x in 'hello']
print(a)
# ['h', 'e', 'l', 'l', 'o']
for ind, val in enumerate(a):
    print('{}: {}'.format(ind, val))
    
# 0: h
# 1: e
# 2: l
# 3: l
# 4: o

enumerate本质上是一个迭代器(迭代器是什么?→传送门),因而你可以用next来访问元素:

a_enum = enumerate(a)
a_enum.__next__()
# (0, 'h')
print(check_iterator(a_enum))
# True

zip

另外一些情况中,可能希望同时遍历多个列表,该怎么做呢?利用zip

a = [x for x in 'hello']
b = [x for x in range(5)]
c = [ord(x) for x in a]

for val in zip(a, b, c):
    print(val)

# ('h', 0, 104)
# ('e', 1, 101)
# ('l', 2, 108)
# ('l', 3, 108)
# ('o', 4, 111)

zip按顺序将几个可迭代对象的元素聚合到元组中,这样,在迭代时就可以一次性迭代多个列表:

for a_el, b_el, c_el in zip(a, b, c):
    print('{}, {}, {}'.format(
        a_el,
        b_el,
        c_el
    ))

# h, 0, 104
# e, 1, 101
# l, 2, 108
# l, 3, 108
# o, 4, 111

所以,当你希望实现将两个可迭代对象分别作为一个字典的键值来生成这个字典时,zip是非常好的选择:

a = [x for x in 'dict']
b = [x for x in range(4)]

c = dict(zip(a, b))
print(c)
# {'c': 2, 'd': 0, 't': 3, 'i': 1}

如果可迭代对象长度不一致怎么办?zip只保留到最短的一个对象的长度:

a = [x for x in range(2)]
b = [x for x in range(3)]
c = [x for x in range(5)]
for val in zip(a, b, c):
    print(val)
    
# (0, 0, 0)
# (1, 1, 1)

想要按最长的对象保留,需要使用标准库itertools中的zip_longest

from itertools import zip_longest
for val in zip_longest(a, b, c, fillvalue=None):
    print(val)
    
# (0, 0, 0)
# (1, 1, 1)
# (None, 2, 2)
# (None, None, 3)
# (None, None, 4)

zip也是迭代器:

abc_zip = zip(a, b, c)
print(next(abc_zip))
# (0, 0, 0)
print(check_iterator(abc_zip))
# True

zip还有另一个作用,可以将一些组合按索引拆分成独立的部分:

l = [(1, 4), (2, 5), (3, 6)]
a, b = zip(*l)
print(a)
# (1, 2, 3)
print(b)
# (4, 5, 6)

(还记得星号表达式吗,→传送门)

现在看一个例子,利用zip模拟矩阵转置。我们利用嵌套列表来模拟矩阵:

from random import randint
mat = [
    [randint(x, y) for x in range(3)]
    for y in range(3)
]
print(mat)
# [[7, 5, 10], [9, 8, 7], [3, 7, 2]]

t_mat = zip(*mat)
print(list(t_mat))
# [(7, 9, 3), (5, 8, 7), (10, 7, 2)]

有人说列表变成了元组了,希望还使用列表,可以利用列表推导式(→传送门)做一点修改:

t_mat = [list(x) for x in zip(*mat)]
print(t_mat)
# [[7, 9, 3], [5, 8, 7], [10, 7, 2]]

帮助文档

在编(ban)程(zhuan)过程中,很多时候很难记住函数的参数都有哪些。除了查询文档外,你还可以利用help()函数直接查看函数的帮助信息:

>>> help(zip)
Help on class zip in module builtins:

class zip(object)
 |  zip(iter1 [,iter2 [...]]) --> zip object
 |
 |  Return a zip object whose .__next__() method returns a tuple where
 |  the i-th element comes from the i-th iterable argument. The .__next__()
 |  method continues until the shortest iterable in the argument sequence
 |  is exhausted and then it raises StopIteration.
...

这里仅贴出了前几行。

你也可以为你自己的函数或类增加这样的文档:

def add(a, b):
    '''
    Util: add
    Params:
    	@ a: object
    	@ b: same type with a
    Return:
    	% a plus b
    '''
    return a + b
help(add)


Help on function add in module __main__:

add(a, b)
    Util: add
    Params:
        @ a: object
        @ b: same type with a
    Return:
        % a plus b

生成斐波那契数列

斐波那契数列是指这样一个数列:

1, 1, 2, 3, 5, 8, 13, 21, 34, ...

其递归定义是这样的:

F(1) = 1
F(2) = 1
F(n) = F(n - 1) + F(n - 2)

它是无限长的数列。下面我们简单实现一个小程序,来获取一个斐波那契数列,并求其中小于n的元素中偶数(prime)的和

首先来判断一下是否是质数:

传统的思路,可能你会写出这样的代码来产生斐波那契数列,利用while循环,按照上述公式计算下一个值,存到列表里:

def fibonacci(n):
    fib = [1, 1]
    ind = 2
	while True:
        temp = fib[ind - 2] + fib[ind - 1]
        if temp > n:
            break
        fib.append(temp)
        ind += 1
    return fib

然后计算偶数和:

def even_sum(n):
    fib = fibonacci(n)
    s = 0 # sum
    for i in fib:
        if not (i % 2):
            s += i    
    return s

结合上篇文章的迭代器,加上本篇文章的一些内容,我们来完成一个更加Pythonic的版本:

class Fibonacci:
    def __init__(self):
        self.lcur = 1
        self.rcur = 1
    def __iter__(self):
        return self
    def __next__(self):
        # 这里是重点
    	self.lcur, self.rcur =\
        self.rcur, self.lcur + self.rcur
        return self.rcur

def even_sum(n):
    f = Fibonacci()
    s = 0
    for ele in f:
        if ele > n:
            break
        if not (ele % 2):
            s += ele
    return s

来看一下结果:

N = 10000000
# 列表法
print(even_sum(N))
# 82790070
# 迭代器法
print(even_sum(N))
# 82790070

迭代器能够节约大量的内存(从始至终只用了两个变量)。此外,这里利用了元素交换巧妙得完成了斐波那契相加的操作。