Python Revisited Day 03 (组合数据类型)

@
**

第三章 组合数据类型

**

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) #2
hair = '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 collections
Sale = 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 = 0
for sale in sales:
    total += sale.quantity * sale.price
print('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 #220
print('{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的位置上插入x

a = [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'] = 7
for 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 string
import sys
words = {}
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 collections
words = 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 = 1
for i in [1, 2, 3, 4]:
    product *= i
print(product)

与下面的是一样的:

product = 1
i = iter([1, 2, 3, 4])
while True:
    try:
        product *= next(i)
    except StopIteration:
        break
print(product)

实例:程序读入一个forename文件与一个surname文件,创建俩个列表,之后创建test-names1.txt并向其中写入100个随机的名称

import random
def 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, surnames

forenames, 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 += t
x
#[-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 = a
a[1] = 'two'
a, b #([1, 'two', 3], [1, 'two', 3])

a = [1, ['2', 5], 3]
b = a[:]
a[0] = 2
b[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] = 2
b[1][0] = 'two'
a, b #([2, ['2', 5], 3], [1, ['two', 5], 3])

实例

生成用户名


import collections
import string
import sys

#在python中全大写的变量一般为常量,这是个约定
ID = 0
FORENAME = 1
MIDDLENAME = 2
SURNAME = 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 user

def 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 username

def 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 collections
import string
import sys
import math

Statistics = 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 mode

def 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 median

def 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("""
aaaaa
ddddd
""")

aaaaa
ddddd

print("""
aaaaa \
ddddd
""")

aaaaa ddddd
注意这个时候   可以起到把空行转义掉,回到原来的形状。

print("""
aaaaa\n\
ddddd
""")

aaaaa
ddddd

猜你喜欢

转载自www.cnblogs.com/MTandHJ/p/10528154.html