文章目录
第三章 组合数据类型
**
3.1 序列类型
3.1.1 元组
元组和字符串类似,是固定的,不能替换或者删改包含的任意项
(1, 2, 3) + (1, 2, 3) #(1, 2, 3, 1, 2, 3)(1, 2, 3, 4) * 2 #(1, 2, 3, 4, 1, 2, 3, 4)(1, 2, 3, 4)[:3] #(1, 2, 3)(1, 2, 3, 1, 1, 3).count(1) #3(1, 2, 3, 1, 1, 3).index(3) #2hair = 'black', 'brown', 'blonde', 'red'hair[2]hair[-3:]hair[:2], 'gray', hair[2:] #(('black', 'brown'), 'gray', ('blonde', 'red'))hair[:2] + ('gray',) + hair[2:] #('black', 'brown', 'gray', 'blonde', 'red')#请注意上面一元组的写法,别弄错了things = (1, -7.5, ('pea', (5, 'XyZ'), 'queue'))things[2][1][1][2] # 'Z'
3.1.2 命名的元组 (collections.nametuple())
collections 模块提供了 namedtuple() 函数,该函数用于创建自定义的元组数据类型。
import collectionsSale = collections.namedtuple('Sale', 'productid customerid data quantity price')sales = []sales.append(Sale(432, 932, '2008-9-14', 3, 7.99))sales.append(Sale(419, 874, '2008-9-15', 1, 18.49))total = 0for sale in sales: total += sale.quantity * sale.priceprint('Total ${0:.2f}'.format(total)) #Total $42.46
第二个例子:
Aircraft = collections.namedtuple('Aircraft', 'manufacturer model seating')Seating = collecttions.namedtuple('Seating', 'minimum maximum')aircraft = Aircraft('Airbus', 'A320-200', Seating(100, 220))aircraft.seating.maximum #220print('{0} {1}'.format(aircraft.manufacturer, aircraft.model))print('{0.manufacturer} {0.model}'.format(aircraft))#命名的元组还有几个私有方法,有一个namedtuple._asdict()的方法特别有用print('{manufacturer} {model}'.format(**aircraft._asdict()))#Airbus A320-200
3.1.3 列表 (查询有关函数点这)
列表方法:
L.append(x)
L.count(x) L.extend(m) | L += m L.index(x, start, end) L.insert(i, x) #在索引i的位置上插入xa = [1, 2, 3]a[1:1] = [4] # == a.insert(1, 4)a # [1, 4, 2, 3]
L.pop() #返回并移除list L最右边的数据项
L.pop(i) # 索引为i的 L.remove(x) #从list中移除最左边出现的数据项x,如果找不到产生ValueError#a[2:4] = [] | del a[2:4] euqal
L.reverse()
L.sort(…) #排序 接受可选的key和reverse参数first, *rest = [9, 2, -4, 8, 7]first, *mid, last = 'Charles Philip Arthur George Windsor.'.split()*directories, executable = '/usr/local/bin/gvim'.split('/')#(['', 'usr', 'local', 'bin'], 'gvim')
3.1.4 列表内涵
[expression for item in iterable if condition]
[y for y in range(1900, 1940) if (y % 4 == 0 and y % 100 != 0) or (y % 400 == 0)]#[1904, 1908, 1912, 1916, 1920, 1924, 1928, 1932, 1936][s + z + c for s in 'MF' for z in 'SMLX' for c in 'BGW' if not (s == 'F' and z == 'X')]
第二行代码,和一般的for循环似乎不同,条件的判断似乎必须在循环的最后关头才会进行判断,所以其实for循环的顺序发生颠倒也没有什么关系。
3.2 集合类型
只有可哈希运算的对象可以添加到集合中。
3.2.1 集合(查询有关函数点这)
集合是0个或多个对象引用的无序组合,且排他!
{ 1, 1, 2, 3, 4, 5} #{1, 2, 3, 4, 5}
空集合只能用set()来创建, ‘{}’ 用来创建空dict
语法 | 描述 |
---|---|
s.add(x) | 将数据项x添加到集合s中——如果s中尚未包含x |
s.clear() | 移除集合s中的所有数据项 |
s.copy() | 返回集合s的浅拷贝* |
s.difference(t) s-t | 返回一个新集合, 其中包含在s中但不在集合t中的所有数据项* |
s.difference_update(t) s -= t | 移除每一个在集合t但不在集合s中的项 |
s.discard(x) | 如果数据项x存在于集合s中,就移除该数据项,参见set.remove() |
s.intersection(t) s & t | 返回一个新集合,其中包含所有同时包含在集合t与s中的数据项* |
s.intersection(t) s &= t | 使得集合s包含自身与集合t交集 |
s.isdisjoint(t) | 如果集合s与t没有相同的项, 就返回True* |
s.issubset(t) s<=t | 如果集合s与集合t相同, 或者是t的子集,就返回True。 使用s<t可以测试s是否是t的真子集* |
s.issuperset(t) s >= t | 如果集合s与集合t相同,或者是t的超集,就返回True。使用s>t可以测试s是否是t的真子集* |
s.pop() | 返回并移除集合s中一个随即项,如果s为空集,就产生KeyError异常 |
s.remove(x) | 从集合s中移除数据项x,如果s中不包含x,就产生KeyError异常,参见set.discard() |
s.symmetric_difference(t) s^t | 返回一个新集合,其中包含s与t中的每个数据项,但不包含同时在这俩个集合中的数据项* |
s.symmetric_difference_update(t) s ^= t | 使得集合s只包含其自身与集合t的对称差 |
s.union(t) s | t | 返回一个新集合,其中包含集合s中的所有数据项,以及在t中而不在s中的数据项* |
s.update(t) s|= t | 将集合t中每个s中不包含的数据项添加到集合s中 |
* 这一方法及其操作符也可用于frozensets
3.2.2 集合内涵
{expression for item in iterable}
{expression for item in iterable if condition}html = {x for x in files if x.lower().endswith((’.htm’, ‘.html’))}
固定集合(forzeonset)
如果讲二元运算符应用于集合于固定集合,那么产生结果的数据类型与左边操作数的数据类型一致。
3.3 映射类型
只有可哈希运算的对象才能作为字典的键。
3.3.1 字典 (查询有关函数点这)
dict() | {}
d1 = dict({ 'id': 1948, 'name': 'Washer', 'size': 3})d2 = dict(id = 1948, name = 'Washer', size = 3)d3 = dict([('id', 1948), ('name', 'Washer'), ('size', 3)])d4 = dict(zip(('id', 'name', 'size'), (1948, 'Washer', 3)))d5 = { 'id': 1948, 'name': 'Washer', 'size': 3}#{'id': 1948, 'name': 'Washer', 'size': 3}del d1['id'] #删除'd1'd1.pop('id') #删除'd1'
del : del删除的不是数据,而是删除对象与数据之间的绑定,若数据没有被其他对象引用,则进入垃圾收集流程。
语法 | 描述 |
---|---|
d.clear() | 从dict d 中移除所有项 |
d.copy() | 返回dict d的浅拷贝 |
d.fromkeys(s, v) | 返回一个dict,该字典的键为序列s中的项,值为None或v |
d.get(k) | 返回键k相关联的值,如果k不在dict d中就返回None |
d.get(k, v) | 返回键k相关联的值,如果k不在dict d中就返回v |
d.items() | 返回dict d 中所有(key, value)对的视图 |
d.keys() | 返回dict d 中所有键的视图 |
d.pop(k) | 返回键k相关联的值,并移除键为k的项,如果k不包含在d中,就产生KeyError异常 |
d.popitem() | 返回并移除dict d 中任意一个(key, value)对,如果d为空就产生KeyError异常 |
d.setdefault(k, v) | 与dict.get()方法一样,不同之处在于,如果k没有包含在dict d中就插入一个键为k的新项,其值为None或v |
d.update(a) | 将a中每个尚未包含在dict d中的(key, value) 对添加到d,对同时包含在d与a中的每个键,使用a中对应的值替换d中对应的值——a可以是字典,也可以是(key,value)对的一个iterable,或关键字参数 |
d.values() | 返回dict d中所有值的视图 |
视图是一个只读的iterable对象
特别的是:如果视图引用的字典发生变化,那么视图将反映该变换;键视图与项视图支持一些类似于集合的操作。 & | - ^d1 = dict(a = 1, b = 2, c = 3)d2 = dict(x = 1, y = 2, z = 3)for item in d1.items(): print(item)d1['a'] = 7for item in d1.items(): print(item)#('a', 1)#('b', 2)#('c', 3)#('a', 7)#('b', 2)#('c', 3)d1 = dict(a = 1, b = 2, c = 3)d2 = dict(x = 1, y = 2, z = 3)v = d1.items()x = d2.items()for item in v | x: print(item)#('b', 2)#('y', 2)#('z', 3)#('c', 3)#('x', 1)#('a', 1)
d = { }.fromkeys('ABCD', 3) #{'A':3, 'B':3, 'C':3, 'D':4}s = set('ACX') # {'A', 'C', 'X'}matches = d.keys() & s # {'A', 'C'}
一个例子(没玩过)
import stringimport syswords = { }strip = string.whitespace + string.punctuation + string.digits + "\"'"for filename in sys.argv[1:]: #这个是指在命令行输入的东东 for line in open(filename): for word in line.lower().split(): word = word.strip(strip) if len(word) > 2: if words[word] = words.get(word, 0) + 1 for word in sorted(words): print("'{0}' occurs {1} times".format(word, words[word]))
文件的读与写
open(filename, encoding = ‘utf8’) #for reading text
open(filename, ‘w’, encoding = ‘utf8’) #for writing text惯用方法:
for line in open(filename, encoding = ‘utf8’): process(line)readlines()方法将整个文件读入字符串列表
字典内涵
{keyexpression:valueexpression for key, value in iterable}
{keyexpression:valueexpression for key, value in iterable if condtion}file_sizes = { name: os.path.getsize(name) for name in os.listdir(".")}file_sizes = { name: os.path.getsize(name) for name in os.listdir(".") if os.path.isfile(name)}
inverted_d = {v: k for k, v in d.items()}#用于反转字典,但是对值有要求
3.3.3 默认字典
解释不清楚
import collectionswords = collections.defaultdict(int)
3.3.4 有序字典
d = collections.OrderedDict([('z',-4), ('e', 19), ('k', 7)])
注意,如果括号里面传入的无序的dict,或关键字参数,那么顺序将是任意的(我在没看出来任意来)。
3.4 组合数据类型的迭代与复制
3.4.1 迭代子、迭代操作与函数 (查询有关迭代子的函数点这)
语法 | 描述 |
---|---|
s + t | 返回一个序列,该序列是序列s与t的连接 |
s * n | 返回一个序列,该序列是序列s的n个副本的连接 |
x in i | 如果项x出现在iterable i 中,就返回True,not in 进行的测试则相反 |
all(i) | 如果iterable i 中的每一项都被评估为True,就返回True |
any(i) | 如果iterable i中的任意项都被评估为True,就返回True |
enumerate(i, start) | 通常用于for … in 循环中,提供一个(index, item)元组序列,其中的索引起始值为0或start |
len(x) | 返回x的“长度”,如果x是组合数据类型,那么返回的是其中数据项数;如果x是一个字符串,那么返回的是其中包含的字符数 |
max(i,key) | 返回iterable i 中的最大的项,如果给定的是key函数,就返回key值最大的项 |
min(i, key) | … |
range(start, stop, step) | 返回一个整数迭代子。使用一个参数(stop)时,迭代子的取值范围从0到stop-1;使用参数(start,stop)时,迭代子取值范围从start到stop-1;3个参数全部使用时,迭代范围从start到stop-1,但每俩个值之间间隔step |
reversed(i) | 返回一个迭代子,该迭代子以反序从迭代子i中返回项#dict 不行 有序字典倒是可以 |
sorted(i, key, reverse) | 以排序后顺序从迭代子i返回项,key用于提供DSU(修饰、排序、反修饰)排序,如果reverse为True,则排序以反序进行 |
sum(i,start) | 返回iterable i 中项的和,加上start(默认为0),i 可以包含字符串 |
zip(i1, … ,iN | 返回元组的迭代子,使用迭代子i1到iN |
x = [-2, 9, 7, -4, 3]all(x), any(x), len(x), min(x), max(x), sum(x)#(True, True, 5, -4, 9, 13)
iter() and next()
iter() 将返回一个用于传递给函数的对象的迭代子,如果该对象无法进行迭代,则产生一个TypeError (还有另外一种用法,关于参数和哨点值,另外再说吧(P114)).
next()会依次返回每个相继的数据项,直到没有数据项时产生StopIteration异常。product = 1for i in [1, 2, 3, 4]: product *= iprint(product)
与下面的是一样的:
product = 1i = iter([1, 2, 3, 4])while True: try: product *= next(i) except StopIteration: breakprint(product)
实例:程序读入一个forename文件与一个surname文件,创建俩个列表,之后创建test-names1.txt并向其中写入100个随机的名称
import randomdef get_fornames_and_surnames(): forenames = [] surnames = [] for names, filename in ((fornames, 'data/forenames.txt'), (surnames, 'data/surnames.txt')): for name in open(filename, encoding='utf8'): names.append(name.rstrip()) #删除字符串末尾指定字符(默认为空格) return forenames, surnamesforenames, surnames = get_fornames_and_surnames()fh = open('test-names.txt', 'w', encoding='utf8')for i in range(100): line = "{0} {1}\n".format(random.choice(forenames), random.choice(surnames)) fh.write(line)fh.close()
zip()
zip()会返回一个迭代子,每个元素是一个元组。
for t in zip(range(4), range(0, 10, 2), range(1, 10, 2)): print(t)#(0, 0, 1)#(1, 2, 3)#(2, 4, 5)#(3, 6, 7)#注意,即便传入的迭代子长度不一致,依然可以使用,取小。for i in zip([1, 2], [1, 2, 3]): print(i)#(1, 1)# (2, 2)
sorted() and reversed()
list(range(6)) #[0, 1, 2, 3, 4, 5]list(reversed(range(6))) #[5, 4, 3, 2, 1, 0]
x = []for t in zip(range(-10, 0, 1), range(0, 10, 2), range(1, 10, 2)): x += tx#[-10, 0, 1, -9, 2, 3, -8, 4, 5, -7, 6, 7, -6, 8, 9]sorted(x)#[-10, -9, -8, -7, -6, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9]sorted(x, reverse = True)#[9, 8, 7, 6, 5, 4, 3, 2, 1, 0, -6, -7, -8, -9, -10]sorted(x, key = abs)#[0, 1, 2, 3, 4, 5, 6, -6, -7, 7, -8, 8, -9, 9, -10]
#key关键字传入函数或方法,相当于先将x中的元素“修饰”,再对“修饰”后的元素加以排列得到一个序,最后的结果是x以这个序进行排列。
x = list(zip((1, 3, 1, 3), ('pram', 'dorie', 'kayak', 'canoe')))print(x)print(sorted(x))def swap(t): return t[1], t[0]print(sorted(x, key=swap))#[(1, 'pram'), (3, 'dorie'), (1, 'kayak'), (3, 'canoe')]#[(1, 'kayak'), (1, 'pram'), (3, 'canoe'), (3, 'dorie')]#[(3, 'canoe'), (3, 'dorie'), (1, 'kayak'), (1, 'pram')]
注意:sorted()只适用于可以进行相互比较的组合类型
sorted([1, '1')] #TypeError
不过,对于上面这种情况,有一个处理方法: key = int/float,当然这也只是对字符串为“数字”的特殊情形。
3.4.2 组合类型的复制
浅拷贝
a = [1, '2', 3]b = aa[1] = 'two'a, b #([1, 'two', 3], [1, 'two', 3])a = [1, ['2', 5], 3]b = a[:]a[0] = 2b[1][0] = 'two'a, b #([2, ['two', 5], 3], [1, ['two', 5], 3])
普通的 a=b,只是使用了对象引用,a, b指向了同一个内存对象
.copy()
dict() list() set() a[:] 这些都会返回一个浅拷贝深拷贝
a = [1, ['2', 5], 3]b = copy.deepcopy(a)a[0] = 2b[1][0] = 'two'a, b #([2, ['2', 5], 3], [1, ['two', 5], 3])
实例
生成用户名
import collectionsimport stringimport sys#在python中全大写的变量一般为常量,这是个约定ID = 0FORENAME = 1MIDDLENAME = 2SURNAME = 3#命名元组 User = collections.namedtuple('User', 'username forename middlename surname id')def Main(): if len(sys.argv) == 1 or sys.argv[1] in ('-h', '-help'): print('useage: {0} file1 [file2 [... fileN]]'.format( sys.argv[0])) sys.exit() usernames = set() users = { } for filename in sys.argv[1:]: for line in open(filename, encoding='utf8'): line = line.rstrip() if line: user = process_line(line, usernames) users[(user.username.lower(), user.forename.lower(), user.id)] = user print_users(users)def process_line(line, usernames): fields = line.split(':') username = generate_username(fields, usernames) user = User(username, fields[FORENAME], fields[MIDDLENAME], fields[SURNAME], fields[ID]) return userdef generate_username(fields, usernames): username = (fields[FORENAME][0] + fields[MIDDLENAME][:1] + fields[SURNAME]).replace('-', '').replace("'", "") username = original_name = username[:8].lower() count = 1 while username in usernames: username = "{0}{1}".format(original_name, count) count += 1 usernames.add(username) return usernamedef print_users(users): namewidth = 32 usernamewidth = 9 print("{0:<{nw}} {1:^6} {2:{uw}}".format( 'Name', 'ID', 'Username', nw = namewidth, uw = usernamewidth)) print("{0:-<{nw}} {0:-<6} {0:-<{uw}}".format( '', nw=namewidth, uw=usernamewidth)) for key in sorted(users): user = users[key] inital = '' if user.middlename: inital = ' ' + user.middlename[0] name = "{0.surname}, {0.forename}{1}".format(user, inital) print("{0:.<{nw}} {1.id:4} {1.username:{uw}}".format( name, user, nw = namewidth, uw = usernamewidth))if __name__ == '__main__': Main()
输入是这样的:
输出是这样的:
小惊喜 m[:1]来代替m[0] 防止产生IndexError
s = ''s[0] #报错s[:1]#不会报错s[:k]都不会报错,只是s[:1]在s非空的情况下满足取首项的要求
处理统计信息
import collectionsimport stringimport sysimport mathStatistics = collections.namedtuple('Statistic', 'mean mode median std_dev')def main(): if len(sys.argv) == 1 or sys.argv[1] in { '-h', '-help'}: print('usage: {0} file1 [file2 [... fileN]'.format( sys.argv[0])) sys.exit() numbers = [] frequencies = collections.defaultdict(int) for filename in sys.argv[1:]: read_data(filename, numbers, frequencies) if numbers: statistics = calculate_statistics(numbers, frequencies) print_results(len(numbers), statistics) else: print('no numbers found')def read_data(filename, numbers, frequencies): for lino, line in enumerate(open(filename, encoding='utf8'), start = 1): for x in line.split(): try: number = float(x) numbers.append(number) frequencies[number] += 1 except ValueError as err: print('{filename}:{lino}:skipping {x}:{err}'.format( **locals()))def calculate_statistics(numbers, frequencies): mean = sum(numbers) / len(numbers) mode = calculate_mode(frequencies, 3) median = calculate_median(numbers) std_dev = calculate_std_dev(numbers, mean) return Statistics(mean, mode, median, std_dev)def calculate_mode(frequencies, maximum_modes): highest_frequency = max(frequencies.values()) mode = [number for number, frequency in frequencies.items() if frequency == highest_frequency] if not (1 <= len(mode) <= maximum_modes): mode = None else: mode.sort() return modedef calculate_median(numbers): numbers = sorted(numbers) middle = len(numbers) // 2 median = numbers[middle] if len(numbers) %2 == 0: median = (median + numbers[middle - 1]) / 2 return mediandef calculate_std_dev(numbers, mean): total = 0 for number in numbers: total += (number - mean) **2 variance = total / (len(numbers) - 1) return math.sqrt(variance)def print_results(count, statistics): real = '9.2f' if statistics.mode is None: modeline = '' elif len(statistics.mode) == 1: modeline = 'mode = {0:{fmt}}\n'.format( statistics.mode[0], fmt=real) else: modeline = ("mode = ["+ ",".join(["{0:.2f}".format(m) for m in statistics.mode]) + "]\n") print("""\ count = {0:6} mean = {mean:{fmt}} median = {median:{fmt}} {1}\ std.dev. = {std_dev:{fmt}}""".format( count, modeline, fmt=real, **statistics._asdict()))if __name__ == '__main__': main()
小惊喜 print 三引号
上面的例子中出现了三引号,三引号使得程序以我们所可理解的方式展示文本(大概就是不用加\n之类的)。
print("""aaaaaddddd""")
aaaaa
dddddprint("""aaaaa \ddddd""")
aaaaa ddddd
注意这个时候 \ 可以起到把空行转义掉,回到原来的形状。print("""aaaaa\n\ddddd""")
aaaaa
ddddd