二十一、集合
集合set属于Python无序可变序列。集合是使用大括号括起来的各种数据,可以看作没有Value的字典。集合里面的元素不能重复。集合中只能包含数字、字符串、元组等不可变类型(或者说是可哈希)的数据,不能包含列表、字典、集合等可变类型数据
由于集合与字典一样,里面的值没有顺序,因此使用集合来去重是有代价的,代价就是原来列表的顺序也会被改变。
- 集合对象的创建与删除
可以使用set()函数将列表、元组、字符串、range对象等其他可迭代对象转换为集合
除了列表推导式、生成器推导式、字典推导式外,还有集合推导式
- 集合操作与运算
- 1、集合元素增加与删除
add()方法可以增加新元素,update()方法合并另外一个集合中的元素到当前集合中,并自动去重
集合对象的pop()方法随机删除并返回集合中的一个元素,如果集合为空则抛出异常;remove()方法删除集合中的元素,如果指定元素不存在则抛出异常;discard()方法从集合中删除一个特定元素,如果元素不存在则忽略该操作,所以总的来说discard()方法要安全一些;clear()方法清空集合
- 2、集合运算
- 1、集合元素增加与删除
- 不可变集合frozenset
与set不同的地方在于没有提供add()、remove()等可以修改集合对象的方法,但支持交集、并集等运算
- 集合应用案例
字典和集合都使用hash表来存储元素,元素查找速度非常快
import random
import time
x1 = list(range(10000))
x2 = tuple(range(10000))
x3 = set(range(10000))
x4 = dict(zip(range(10000), range(10000)))
r = random.randint(0, 9999)
for t in (x4, x3, x2, x1):
start = time.time()
for i in range(9999999):
r in t
print(type(t), 'time used:', time.time() - start)
集合可以简化很多代码量,e.g.
等价于
newSet = set(listRandom)
再来个例子,得到指定范围内一定数量的不重复数字
等价于
利用字典和集合模拟有向图结构
def getDegrees(orientedGraph, node):
outDegree = len(orientedGraph.get(node, []))
inDegree = sum(1 for v in orientedGraph.values() if node in v)
return (inDegree, outDegree)
graph = {'a' : set('bcdef'), 'b' : set('ce'), 'c' : set('d'), 'd' : set('e'), 'e' : set('f'), 'f' : set('cgh'), 'g' : set('fhi'), 'h' : set('fgi'), 'i' : set()}
print(getDegrees(graph, 'h'))
答案是(2,3)
- 序列解包多种形式和用法
序列解包就是让等号左右部分数目一样,把右边的内容同时赋值给左边
序列解包的逆运算:
二十二、程序控制结构
- 条件表达式
存在短路效应(可提高程序运行效率)
(1)在使用and连接的多个表达式中,只要有一个表达式不为真,那么后面的表达式就不会执行。(2)在使用or连接的多个表达式中,只要有一个表达式为真,那么后面的表达式就不会执行。
上述代码等价于
def Join(chList, sep = ','):
return sep.join(chList)
Join(chList)
- 选择结构:单分支结构、双分支结构、多分支结构、嵌套分支结构
x = input('Input two numbers:')
a, b = map(int, x.split()) #序列解包
if a > b:
a, b = b, a
print(a, b)
解决鸡兔同笼问题
C语言有三目运算符,例如a > b ? a : b
那么Python也有一个三元运算符,语法为value1 if condition else value2,如果condition为真,则执行value1,反之执行value2
对于多重条件选择判断,需要使用“if…elif…else…”。其中,“elif”可以有0个,也可以有多个,但是else只能有0个或者1个。
使用字典实现多重条件控制
嵌套结构要注意代码缩进,因为这决定不同代码的从属关系
构建跳转表实现多分支选择结构
- 循环结构
特别注意:在循环里面还有循环(循环嵌套)的时候,continue和break都只对自己所在的这一层循环有效,不会影响外面的循环。
循环代码优化技巧:尽量减少内层代码中的不必要计算,尽量引用局部变量,因为局部变量的查询和访问速度比全局变量略快
代码优化涉及的内容广泛:例如,如果经常需要测试一个序列是否包含一个元素就尽量使用字典或集合而不是用字典;把多个字符串连接成一个字符串时应尽量使用join()方法而不要使用运算符+;对列表进行元素插入或删除操作时应尽量从列表尾部进行…
对时间库的了结
拥有一款中国日历:
from datetime import date
daysOfMonth = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
def myCalendar(year, month):
start = date(year, month, 1).timetuple().tm_wday #获取year年month月1日是第几周
print('{0}年{1}月日历'.format(year, month).center(56))
print('日\t一\t二\t三\t四\t五\t六')
day = daysOfMonth[month - 1]
if month == 2:
if year % 400 == 0 or (year % 4 == 0 and year % 100 != 0):
day += 1
result = [' ' * 8 for i in range(start + 1)]
result += list(map(lambda d : str(d).ljust(8), range(1, day + 1)))
for i, day in enumerate(result):
if i != 0 and i % 7 == 0:
print()
print(day, end = '')
print()
def main(year, month = -1):
if type(year) != int or year < 1000 or year > 10000:
print('Year error')
return
if type(month) == int:
if month == -1: #如果没有指定月份,就打印全年的日历
for m in range(1, 13):
myCalendar(year, m)
elif month in range(1, 13):
myCalendar(year, month)
else:
print('Month error')
return
else:
print('Month error')
return
main(2020)
运行部分效果如下,快来试试吧