Python进阶-1

学习Python进阶知识点,全文来自于网络上的一本翻译的电子书:Python进阶教程

函数定义中的args 和 *kwargs

demo:

1
2
3
4
5
6
7
8
9
def demo(var1, *args, **kwargs):
print(var1)
for arg in args:
print(arg)
print(kwargs)
for key,value in kwargs.items():
print('key is:{}, value is:{}'.format(key, value))

demo('variable 1', 'var2', 'var3', name='qyy', age='23')

执行的输出为:

1
2
3
4
5
6
variable 1
var2
var3
{'name': 'qyy', 'age': '23'}
key is:name, value is:qyy
key is:age, value is:23

显而易见,针对未知数量的参数,在函数定义的参数前加一个星号,而针对未知数量的键值对,则加两个星号。
内部结构一个是list,一个是dict。

在什么时候使用它们?

针对需求,大多在写装饰器函数的时候为接收参数的时候或者在接触到猴子补丁的时候用到。

生成器

首先介绍几个概念:

  • 容器
  • 可迭代对象
  • 迭代器
  • 迭代
  • 生成器

首先是容器,容器简单来说,就是一种将多个元素组织在一起的数据结构。可以通过in查看元素是否在容器中,这些元素一般存放与内存中,当然也有例外。
简单来说,如下都是容器之一:

  • string
  • list
  • dict
  • set
  • tuple
    当然还有其他结构,我们可以从容器中获取每一个元素,这种能力不是容器支持的,而是因为这个容器是可迭代对象。

可迭代对象:实现了魔术方法iter,且此方法返回一个迭代器。

那么什么是迭代器?

任意对象实现了魔术方法next,就是一个迭代器(当然实质是一个对象)。此方法返回下一个可用的元素,如果没有元素了,抛出StopIteration异常。
迭代器并不是一定只能用一次,关键在于如何设计代码,可以通过分离可迭代对象和迭代器的定义,实现再次利用。

对 Python 迭代的深入研究这篇文章中,这位大佬描述的很好,通过其示例代码,实现了再次利用迭代器。

那么什么是迭代?
简单来说,例如从一个数据结构中取出一个元素的过程,可以称为迭代。
例如用for循环去获取某个list中的一系列值,这个过程就叫迭代。

如下为我自己实现迭代器和可迭代对象的实例代码:

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

class my_iterator(object):
def __next__(self):
v = random.randint(0,5)
if v < 1:
raise StopIteration()
else:
return v

class my_iterable(object):
def __iter__(self):
return my_iterator()

for v in my_iterable():
print(v)

某次输出为:

1
2
4
3

什么是生成器?
生成器也是一种迭代,不过生成器只能用一次,因为其数据不会存放在内存中,导致占用内存,例如你要遍历一个10000000个值的list,可能会占用大量内存进行保存这些值。
生成器不一样,这中结构在运行的时候生成值。

大多数时候生成器是以函数来实现的,这个函数最大的特点是不会返回一个值,而是使用关键字yield。执行到此,则next方法立即返回yield的值。当再次调用next方法,则从上次yield下面继续执行,直到
遇到下一个yield。在函数结尾的时候,抛出StopIteration异常。

例如如下代码:

1
2
3
4
5
6
7
def my_generator(n):
while True:
v = random.randint(0,n)
if v > 1:
yield v
else:
break

即可对上述迭代器和可迭代对象进行优化,就可以如下调用:

1
2
for i in my_generator(10):
print(i)

在以后的编码中,一定会用到生成器的知识。

补充

生成器的生命周期中,有四个状态:

  • GEN_CREATED # 等待开始执行
  • GEN_RUNNING # 解释器正在执行(只有在多线程应用中才能看到这个状态)
  • GEN_SUSPENDED # 在yield表达式处暂停
  • GEN_CLOSED # 执行结束
    通过
    1
    from inspect import getgeneratorstate

提供的getgeneratorstate方法,可以获取某个生成器的状态.

Set()

集合(set)是一个无序的不重复元素序列。

可以使用大括号 { } 或者 set() 函数创建集合,注意:创建一个空集合必须用 set() 而不是 { },因为 { } 是用来创建一个空字典。

集合支持的方法有:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
.add(value)

.remove(value) # 值不存在会报错

.discard(value) # 删除值,不存在也不会报错

.pop() # 随机,重点是随机删除一个值

.clear() # 清除

.copy() # 返回copy的集合

.difference(newSet) # 与newSet集合取差集并且作为一个集合返回

A.difference_update(B) # 二者之间的差集赋值给集合A

A.intersection(B) # 交集返回

A.intersection_update(B) # 交集赋值给A

set.symmetric_difference(set) # 返回不重复的集合内容

set.update(value) # 将一个可迭代对象更新进入set中,无返回值

... # 等等集合这种数学定义支持的操作,如下更是支持语义话的处理,而非使用函数

# Program to perform different set operations
# as we do in mathematics

# sets are define
A = {0, 2, 4, 6, 8};
B = {1, 2, 3, 4, 5};

# union
print("Union :", A | B)

# intersection
print("Intersection :", A & B)

# difference
print("Difference :", A - B)

# symmetric difference
print("Symmetric difference :", A ^ B)

# 输出
('Union :', set([0, 1, 2, 3, 4, 5, 6, 8]))
('Intersection :', set([2, 4]))
('Difference :', set([8, 0, 6]))
('Symmetric difference :', set([0, 1, 3, 5, 6, 8]))

三元运算符

在Python中,三元运算符基于条件表达式,看demo:

1
2
is_ok = True
state = "ok" if is_ok else "not"

另外一个写法,有人觉得晦涩,我却很喜欢。

1
2
3
4
fat = True
fitness = ("skinny", "fat")[fat]
print("Ali is", fitness)
# 输出: Ali is fat

因为在Python中,True等于1,而False等于0,这就相当于在元组中使用0和1来选取数据。

上面的例子没有被广泛使用,而且Python玩家一般不喜欢那样,因为没有Python味儿(Pythonic)。这样的用法很容易把真正的数据与True/False弄混。
另外一个不使用元组条件表达式的缘故是因为在元组中会把两个条件都执行,而 if-else 的条件表达式不会这样。

因此,如果逻辑中的条件异常,或者是重计算型(计算较久)的情况下,最好尽量避免使用元组条件表达式。