lzykevin
5/3/2020 - 3:19 AM

Python Cookbook

# 2019-12-22
import json
from collections import OrderedDict
from collections import defaultdict
import heapq
from collections import deque
import os
# os.chdir('E:\\python\\学习笔记\\cookbook')
os.chdir('E:\\python\\')

# 第一章:数据结构和算法

# 1.1 解压序列赋值给多个变量
p = (4, 5)
x, y = p

s = 'Hello'
a, b, c, d, e = s

data = ['ACME', 50, 91.1, (2012, 12, 21)]
_, shares, price, _ = data  # 利用占位变量只解压部分需要的变量

# 1.2 解压可迭代对象赋值给多个变量
grades = [60, 62, 62, 68, 70]
first, *middle, last = grades  # 注意如果用*middle则无论结果包含零个,一个还是多个,middle均返回列表

record = ('ACNE', 50, 123, (12, 18, 2012))
name, *_, (*_, year) = record

# 1.3 保留最后N个元素


def search(lines, pattern, history=5):
    previous_lines = deque(maxlen=history)
    for line in lines:
        if pattern in line:
            yield line, previous_lines
        previous_lines.append(line)


if __name__ == "__main__":
    with open('somefile.txt') as f:
        for line, prevlines in search(f, 'python', 5):
            for pline in prevlines:
                print(pline, end='')
            print(line, end='')
            print('-' * 20)

q = deque(maxlen=3)
q.append(1)
q.append(2)
q.append(3)
print(q)  # deque([1, 2, 3], maxlen=3)
q.append(4)
print(q)  # deque([2, 3, 4], maxlen=3)

# 1.4 查找最大或最小的N个元素
nums = [1, 2, 37, 83, 2, 23, 43]
print(heapq.nlargest(3, nums))
print(heapq.nsmallest(3, nums))

portfolio = [
    {'name': 'IBM', 'shares': 100, 'price': 91.1},
    {'name': 'AAPL', 'shares': 50, 'price': 543.22},
    {'name': 'FB', 'shares': 200, 'price': 21.09},
    {'name': 'HPQ', 'shares': 35, 'price': 31.75},
    {'name': 'YHOO', 'shares': 45, 'price': 16.35},
    {'name': 'ACME', 'shares': 75, 'price': 115.65}
]
cheap = heapq.nlargest(3, portfolio, key=lambda s: s['price'])
expensive = heapq.nsmallest(3, portfolio, key=lambda s: s['price'])

# 1.5 实现一个优先级队列


class PriorityQueue:
    def __init__(self):
        self._queue = []
        self._index = 0

    def push(self, item, priority):
        heapq.heappush(self._queue, (-priority, self._index, item))
        self._index += 1

    def pop(self):
        return heapq.heappop(self._queue)[-1]


class Item:
    def __init__(self, name):
        self.name = name

    def __repr__(self):
        return 'Item({!r})'.format(self.name)


q = PriorityQueue()
q.push(Item('foo'), 1)
q.push(Item('bar'), 5)
q.push(Item('spam'), 4)
q.push(Item('grok'), 1)
q.pop()  # Item('bar')
q.pop()  # Item('spam')
q.pop()  # Item('grok')
q.pop()  # Item('foo')

# 1.6 字典中的键映射多个值
d = {
    'a': [1, 2, 3],
    'b': [4, 5]
}
d = defaultdict(list)
d['a'].append(1)
d['a'].append(2)
d['a'].append(4)

# 1.7 字典排序
d = OrderedDict()
d['foo'] = 1
d['bar'] = 2
d['spam'] = 3
d['grok'] = 4
for key in d:
    print(key, d[key])

json.dumps(d)

# 1.8 字典的运算
prices = {
    'ACME': 45.23,
    'AAPL': 523.65,
    'IBM': 213.55,
    'HPQ': 37.20,
    'FB': 12.22
}

list(zip(prices.values(), prices.keys()))
min_price = min(zip(prices.values(), prices.keys()))
max_price = max(zip(prices.values(), prices.keys()))
prices_sorted = sorted(zip(prices.values(), prices.keys()))

# 1.9 查找两字典的相同点
a = {
    'x': 1,
    'y': 2,
    'z': 3
}

b = {
    'w': 10,
    'x': 11,
    'y': 2
}

a.keys() & b.keys()  # {'x', 'y'}
a.keys() - b.keys()  # {'z'}
a.items() & b.items()  # ('y', 2)}

c = {key: a[key] for key in a.keys() - {'z', 'w'}}  # {'y': 2, 'x': 1}

# 补充知识:yield
def foo():
    print("starting...")
    while True:
        res = yield 4
        print("res:", res)


g = foo()
print(next(g))
print("*"*20)
print(next(g))

# 1.10 删除序列相同元素并保持顺序
def dedupe(items):
    seen = set()
    for item in items:
        if item not in seen:
            yield item
            seen.add(item)


a = [1, 5, 2, 1, 9, 1, 5, 10]
list(dedupe(a))

# 这种写法可以删除相同元素但是不能保持顺序(或者直接用set()同样能做到)


def dedupe(items):
    seen = set()
    for item in items:
        if item not in seen:
            # yield item
            seen.add(item)
    return seen


dedupe(a)

# 这种写法也可以删除元素并保持顺序


def dedupe(items):
    seen = []
    for item in items:
        if item not in seen:
            # yield item
            seen.append(item)
    return seen


dedupe(a)

# 1.11 命名切片(提高可读性和可维护性)
record = '....................100 .......513.25 ..........'
# cost = int(record[20:23]) * float(record[31:37])

SHARES = slice(20, 23)
PRICE = slice(31,37)
cost = int(record[SHARES]) * float(record[PRICE])

# 1.12 序列中出现次数最多的元素
words = [
    'look', 'into', 'my', 'eyes', 'look', 'into', 'my', 'eyes',
    'the', 'eyes', 'the', 'eyes', 'the', 'eyes', 'not', 'around', 'the',
    'eyes', "don't", 'look', 'around', 'the', 'eyes', 'look', 'into',
    'my', 'eyes', "you're", 'under'
]
from collections import Counter
words_counts = Counter(words)
top_three = words_counts.most_common(3)
words_counts['not'] # 1

# 1.13 通过某个关键字排序一个字典列表
rows = [
    {'fname': 'Brian', 'lname': 'Jones', 'uid': 1003},
    {'fname': 'David', 'lname': 'Beazley', 'uid': 1002},
    {'fname': 'John', 'lname': 'Cleese', 'uid': 1001},
    {'fname': 'Big', 'lname': 'Jones', 'uid': 1004}
]

from operator import itemgetter
rows_by_fname = sorted(rows, key=itemgetter('fname'))

# 1.14 排序不支持原生比较的对象

# 1.15 通过某个字段将记录分组
from operator import itemgetter
from itertools import groupby

rows = [
    {'address': '5412 N CLARK', 'date': '07/01/2012'},
    {'address': '5148 N CLARK', 'date': '07/04/2012'},
    {'address': '5800 E 58TH', 'date': '07/02/2012'},
    {'address': '2122 N CLARK', 'date': '07/03/2012'},
    {'address': '5645 N RAVENSWOOD', 'date': '07/02/2012'},
    {'address': '1060 W ADDISON', 'date': '07/02/2012'},
    {'address': '4801 N BROADWAY', 'date': '07/01/2012'},
    {'address': '1039 W GRANVILLE', 'date': '07/04/2012'},
]
rows.sort(key=itemgetter('date')) # 直接写date不行
for date, items in groupby(rows, key=itemgetter('date')):
    print(date)
    for i in items:
        print(' ', i)

# 1.16 过滤序列元素
# 列表推导,简单但是如果输入比较大时会比较占内存
mylist = [1,4,-5,10,-7,2,3,-1]
[n for n in mylist if n > 0]

# filter(function, iterable)
# 该函数接收两个参数,第一个为函数,第二个为序列,序列的每个元素作为参数传递给函数进行判,
# 然后返回 True 或 False,最后将返回 True 的元素放到新列表中。
values = ['1','2','-3','-','N/A','5']
def is_int(val):
    try:
        x = int(val)
        return True
    except ValueError:
        return False
ivals = list(filter(is_int, values))

# compress
from itertools import compress
addresses = [
    '5412 N CLARK',
    '5148 N CLARK',
    '5800 E 58TH',
    '2122 N CLARK',
    '5645 N RAVENSWOOD',
    '1060 W ADDISON',
    '4801 N BROADWAY',
    '1039 W GRANVILLE',
]
counts = [0, 3, 10, 4, 1, 7, 6, 1]
more5 = [n>5 for n in counts]
list(compress(addresses, more5))

# 1.17 从字典中提取子集
# 字典推导
prices = {
    'ACME': 45.23,
    'AAPL': 523.65,
    'IBM': 213.55,
    'HPQ': 37.20,
    'FB': 12.22
}
p1 = {key: value for key, value in prices.items() if value > 200}
tech_names = {'AAPL','IBM','HPQ','MSFT'}
p2 = {key: value for key, value in prices.items() if key in tech_names}

# 1.18 映射名称到序列元素

# 1.19 转换并同时计算数据
import os
os.getcwd()
files = os.listdir('学习笔记') # 不设置参数就是当前目录,参数为当前目录下的文件夹
if any(name.endswith('.py') for name in files):
    print('There be python!')
else:
    print('Sorry, no python.')

s = ('ACME',50,123.45)
print(','.join(str(x) for x in s)) # 用逗号连接字符串

portfolio = [
    {'name': 'GOOG', 'shares': 50},
    {'name': 'YHOO', 'shares': 75},
    {'name': 'AOL', 'shares': 20},
    {'name': 'SCOX', 'shares': 65}
]

min_shares = min(s['shares'] for s in portfolio) # 20
min_shares = min(portfolio, key=lambda s: s['shares']) # {'name': 'AOL', 'shares': 20}

# 1.20 合并多个字典或映射
from collections import ChainMap
a = {'x':1,'z':3}
b = {'y':2,'z':4}
c = ChainMap(a, b)
print(c['x'])
print(c['y'])
print(c['z']) # 返回3,重复键只返回第一次出现的映射值
len(c)
list(c.keys())
list(c.values())


# 第二章:字符串和文本

# 2.1 使用多个界定符分割字符串
import re
line = 'asdf fjdk; afed, fjek,asdf, foo'
re.split(r'[;,\s]\s*', line) # \s匹配空格或者tab键
re.split(r'(;|,|\s)\s*', line) # 使用捕获分组,则被匹配的文本将也出现在结果列表中
re.split(r'(?:;|,|\s)\s*', line) # 既需要用到小括号,又不想要使用捕获分组加?:

# 2.2 字符串开头或结尾匹配
filename = 'spam.txt'
filename.endswith('.txt') # True
filename.startswith('spam') # True
filename.endswith(('.py', 'txt'))  # True ,这种写法要传入元组tuple

# 2.3 用shell通配符匹配字符串

# 2.4 字符串匹配和搜索
text = 'yeah, but no, but yeah, but no, but yeah'
text == 'yeah' # False
text.find('no') # 返回第一个no出现的索引位置,注意是从0开始的

text1 = '12/23/2019'
text2 = 'Dec 23, 2019'
text = 'Today is 12/23/2019.PyCon starts 3/12/2020'
re.match(r'\d+/\d+/\d+', text1) # 只会从头开始匹配,返回字符串位置和内容
# 如果要用同一个模式做多次匹配,要先将该模式字符串预编译为模式对象
datepat = re.compile(r'\d+/\d+/\d+')
datepat.match(text1)
datepat.findall(text) # findall能找出全部符合的字符串,返回list
# 使用捕获分组(可以分别将每个组的内容提取出来)
datepat = re.compile(r'(\d+)/(\d+)/(\d+)')
m = datepat.match(text1)
m.group(0)  # '12/23/2019'
m.group(1)  # '12'
m.groups()  # ('12', '23', '2019')
month, day, year = m.groups()


# 2.5 字符串搜索和替换
text = 'yeah, but no, but yeah, but no, but yeah'
text.replace('yeah', 'yep')
text = 'Today is 12/23/2019.PyCon starts 3/12/2020'
re.sub(r'(\d+)/(\d+)/(\d+)',r'\3-\1-\2',text) # \3表示捕获组号
# \3表示捕获组号, subn加上一个替换了多少组,('Today is 2019-12-23.PyCon starts 2020-3-12', 2)
re.subn(r'(\d+)/(\d+)/(\d+)', r'\3-\1-\2', text)
print(text)

# 多个空格转为1个
whitespace = 'test      finish'
pattern_whitespace = re.compile(r'[ ]+')
re.sub(pattern_whitespace,' ', whitespace)

# 2.8 多行匹配模式
text1 = '/* this is a comment */'
text2 = '''/* this is a
... multiline comment */
... '''
comment = re.compile(r'/\*(.*?)\*/')
comment.findall(text1)
re.sub(comment, '',text1)

comment.findall(text2)  # [] 默认.不能匹配到换行符
# 方法1:添加?:\n
comment2 = re.compile(r'/\*((?:\n|.)*?)\*/') # ?:转为非捕获组
comment2.findall(text2)
# 方法2:re.DOTALL 让(.)匹配包括换行符在内的任意字符
comment3 = re.compile(r'/\*(.*?)\*/', re.DOTALL)
comment3.findall(text2)

# 2.9 将Unicode文本标准化
s1 = 'Spicy Jalape\u00f1o'

# 2.10 在正则式中使用Unicode
import re
num = re.compile('\d+')
num.match('123')
num.match('\u0661\u0662\u0663')

# 2.11 删除字符串中不需要的字符
s = ' hello world \n '
s.strip() # 左右空白都会删除(包括换行符)
s.lstrip() # 删除左侧
s.rstrip() # 删除右侧

t = '-----hello-world====='
t.strip('-=') # 删除左右两侧指定字符(中间的不会删除)

# 2.12 审查清理文本字符串
s = 'python\fis\tawesome\r\n'
# 方法1:translate(多次使用时translate更简洁,也可将replace写成函数效果相同)
remap = {
    ord('\t'): ' ',
    ord('\f'): ' ',
    ord('\r'): None,
    ord('\n'): None
}
a = s.translate(remap)
# 方法2:replace
b = s.replace('\t', ' ').replace('\f', ' ').replace('\r', '').replace('\n','') # 注意这里''不能用None代替

# 2.13 字符串对齐(用在print上很方便)
text = 'Hello World'
text.ljust(20)
text.rjust(20)
text.center(20)
text.center(30,'-')

format(text, '>20') # 20,居右
format(text, '<20') # 20,居左
format(text, '^20') # 20,居中
format(text, '-^20') # -补全,20,居中
text.format('>20')
# 补充知识:format
# https://www.cnblogs.com/lvcm/p/8859225.html
name,score = ('小明',95.5)
print('今天{}考了{:.2f}分'.format(name, score)) # format写法.2f前要加冒号

# 2.14 合并拼接字符串
parts = ['Is', 'Chicago', 'Not', 'Chicago?']
' '.join(parts)

a = 'Is Chicago'
b = 'Not Chicago?'
a + ' ' + b
print('{} {}'.format(a, b))
print(a + ' ' + b)
a = 'Hello' 'World'

# 2.15 字符串中插入变量
s = '{name} has {n} messages.'
s.format(name='Guido', n=37) # 这种写法需要把变量的值也写上

name = 'Guido'
n = 37
s2 = '{} has {} messages.'
s2.format(name, n)

name = 'Guido'
n = 37
s3 = '{name} has {n} messages.'
s3.format_map(vars()) # 使用format_map+vars 既可以在字符串中引用变量,又无需在函数里写变量值

# 2.16 以指定列宽格式化字符串
import textwrap
s = "Look into my eyes, look into my eyes, the eyes, the eyes, \
the eyes, not around the eyes, don't look around the eyes, \
look into my eyes, you're under."
print(textwrap.fill(s, width=40))

# 2.17 在字符串中处理html和xml

# 2.18 字符串令牌解析
import re
text = 'foo = 23 + 42 * 10'
tokens = [('NAME','foo'), ('EQ', '='), ('NUM', 23), ('PLUS', '+'),
          ('NUM', 42), ('TIMES', '*'), ('NUM', '10')]
NAME = r'(?P<NAME>[a-zA-Z][a-zA-Z_0-9]*)'
NUM = r'(?P<NUM>\d+)'
PLUS = r'(?P<PLUS>\+)'
TIMES = r'(?P<TIMES>\*)'
EQ = r'(?P<EQ>=)'
WS = r'(?P<WS>\s+)'

master_pat = re.compile('|'.join([NAME, NUM, PLUS, TIMES, EQ, WS]))
scanner = master_pat.scanner('foo = 42')
# 重复运行会一步步扫描
scanner.match()
_.lastgroup,_.group()

# 第三章:数字日期和时间
# 3.1 数字的四舍五入
round(1.23, 1)
round(2.5) # 2 当一个值刚好在两个边界中间是,返回离它最近的偶数
round(12.4, -1)
round(5, -1) # 0 不是10

format(1.23456, '.2f')
'value is {:.2f}'.format(1.23456)

# 3.2 执行精确的浮点数运算
a = 4.2
b = 2.1
a + b  # 6.300000000000001 != 6.3
from decimal import Decimal
a = Decimal('4.2')
b = Decimal('2.1')
a + b  # Decimal('6.3')
print(a+b) # 6.3

# 3.3 数字的格式化输出
x = 1234.56789
format(x, '.2f')
format(x, '>10.1f') # 居右,10格补全,保留一位小数
format(x, ',') # 千分位加逗号
format(x, ',.1f')

format(x, 'e')  # '1.234568e+03'
format(x, '.2E')  # '1.23E+03'
# 一般形式
'[<>^]?width[,]?(.digits)?' # ?代表可选

'The value is {:,.2f}'.format(x)

# 3.4 二八十六进制整数
x = 1234
bin(x)  # 二进制 '0b10011010010'
oct(x)  # 八进制 '0o2322'
hex(x)  # 十六进制 '0x4d2'
# 去掉前缀
format(x, 'b')  # 二进制 '10011010010'
format(x, 'o')  # 八进制 '2322'
format(x, 'x')  # 十六进制 '4d2'

int('4d2', 16) # 十六进制转十进制
int('10011010010', 2) # 二进制转十进制

# 3.5 字节到大整数的打包与解包

# 3.6 复数的数学运算
a = complex(2, 4) # 实部为2,虚部为4(python中用j而不是i表示虚部)
b = 3 - 5j

a.real  # 获取实部2.0
a.imag  # 获取虚部4.0
a.conjugate() # 共轭复数(2-4j)

abs(a)

# 3.7 无穷大与NaN
a = float('inf')
b = float('-inf')
c = float('nan')

import math
math.isinf(a) # True
math.isnan(c) # True

a+b # nan
d = float('nan')
c == d # False nan在作比较时总是返回False

# 3.8 分数运算
from fractions import Fraction
a = Fraction(5, 4) # 5/4
b = Fraction(7, 16)
print(a + b)
c = a * b # 35/64
c.numerator # 35 分子
c.denominator # 64 分母
float(c) # 转为浮点数
print(c.limit_denominator(8)) # 将分母限制最大为8,得到一个近似的分数
x = 3.75
Fraction(*x.as_integer_ratio()) # 小数转分数, x前要有*号

# 3.9 大型数组运算
x = [1,2,3,4]
y = [5,6,7,8]
x * 2  # [1, 2, 3, 4, 1, 2, 3, 4]
x + 10  # can only concatenate list (not "int") to list
x + y  # [1, 2, 3, 4, 5, 6, 7, 8]

import numpy as np
ax = np.array([1,2,3,4])
ay = np.array([5,6,7,8])
ax * 2  # array([2, 4, 6, 8])
ax + 10  # array([11, 12, 13, 14])
ax + ay  # array([ 6,  8, 10, 12])
ax * ay  # array([ 5, 12, 21, 32])

# 3.10 矩阵和线性代数运算
import numpy as np
m = np.matrix([[1,-2,3],[0,4,5],[7,8,-9]])
m.T # 转置矩阵
m.I # 逆矩阵
v = np.matrix([[2],[3],[4]])
m * v

x = np.linalg.solve(m, v)

# 3.11 随机选择
import random
values = [1,2,3,4,5,6]
random.choice(values) # 随机抽一个
random.sample(values, 2) # 随机抽两个
random.shuffle(values) # 随机打乱values
random.randint(0,10) # 随机生成整数(0和10也包括)
random.random() # 生成0-1范围内均匀分布的浮点数
random.seed(12345) # 设置随机数种子
random.random()

# 3.12 基本的日期与时间转换
from datetime import timedelta
a = timedelta(days=2, hours=6)
b = timedelta(hours=4.5)
c = a + b
c.days
c.seconds / 3600 # 10.5
c.total_seconds() / 3600  # 58.5

from datetime import datetime
a = datetime(2019, 9, 23)  # datetime.datetime(2019, 9, 23, 0, 0)
print(a)  # 2019-09-23 00:00:00
print(a + timedelta(days=10))

b = datetime(2012, 12, 21)
d = b - a
d.days

now = datetime.today()  # datetime.datetime(2019, 12, 28, 22, 23, 10, 728942)
print(now)  # 2019-12-28 22:23:10.728942
print(now + timedelta(minutes=10))

a = datetime(2012, 3, 1)
b = datetime(2012, 2, 28)
a - b  # datetime.timedelta(days=2)
(a - b).days # 2

c = datetime(2013, 3, 1)
d = datetime(2013, 2, 28)
(c - d).days # 1 (datetime函数会自动处理闰年)

a = datetime(2012, 9, 23)
from dateutil.relativedelta import relativedelta
a + relativedelta(months=1)

relativedelta(b, a) # relativedelta(months=-6, days=-24)

# 3.13 计算最后一个周五的日期

# 3.14 计算当前月份的日期范围

# 3.15 字符串转换为日期
from datetime import datetime
text = '2012-09-20'
y = datetime.strptime(text, '%Y-%m-%d')
z = datetime.now()
diff = z - y  # datetime.timedelta(days=2655, seconds=81660, microseconds=192)

nice_z = datetime.strftime(z, '%A %B %d, %Y')  # 'Saturday December 28, 2019'
# 区别:strftime是格式化输出,strptime是字符串转为时间类型
# 格式设置:https://blog.csdn.net/ljh0302/article/details/54882750

# 第五章:文件与IO
# 5.1 读写文本数据
# 读取文本
with open(r'e:\python\学习笔记\cookbook\somefile.txt', 'rt') as f:
    data = f.read()

with open(r'e:\python\学习笔记\cookbook\somefile.txt', 'rt') as f:
    for line in f:
        # pass

# 写入文本
with open('somefile.txt', 'wt') as f:
    f.write(text1)
    f.write(text2)

# 另一种写法(不用with需要手动关闭文件)
f = open(r'e:\python\学习笔记\cookbook\somefile.txt', 'rt')
data = f.read()
f.close() # 手动关闭文件

# 5.2 打印输出至文件中
with open(r'e:\python\学习笔记\cookbook\write.txt', 'wt') as f:
    print('Hello world!', file=f)

# 5.3 使用其他分隔符或行终止符打印

# 5.4 读取字节数据

# 5.5 文件不存在才能写入
with open(r'e:\python\学习笔记\cookbook\somefile.txt', 'xt') as f:
    f.write('Hello')  # FileExistsError: [Errno 17] File exists

# 5.11 文件路径名的操作
import os
path = r'E:\python\学习笔记\cookbook\somefile.txt'
os.path.basename(path)  # 'somefile.txt'
os.path.dirname(path) # 'E:\\python\\学习笔记\\cookbook'
os.path.join('tmp', 'data', os.path.basename(path)) # 'tmp\\data\\somefile.txt'

# 5.12 测试文件是否存在
import os
import time
os.path.exists(r'E:\python\学习笔记\cookbook') # True(结尾不能有斜杠)
os.path.getsize(r'E:\python\学习笔记\cookbook')  # 4096
os.path.getsize(r'E:\python\学习笔记\cookbook\somefile.txt')  # 7
time.ctime(os.path.getmtime(r'E:\python\学习笔记\cookbook\somefile.txt')) # 'Mon Dec 30 21:32:31 2019'

# 5.13 获取文件夹中的文件列表
import os
name = os.listdir(r'E:\python\学习笔记\cookbook')
# ['download (1).wget', 'download.wget', 'somefile.txt', 'write.txt']


# 第七章:函数
# 7.1 可接受任意数量参数的函数
# * 接受任意数量的位置参数
def avg(first, *rest):
    return (first + sum(rest)) / (1 + len(rest))

avg(1, 2)
avg(1, 2, 3)

# ** 接受任意数量的关键字参数
import html
def make_element(name, value, **attrs):
    keyvals = [' %s="%s"' % item for item in attrs.items()]
    attr_str = ''.join(keyvals)
    element = '<{name}{attrs}>{value}</{name}>'.format(
        name=name,
        attrs=attr_str,
        value=html.escape(value))
    return element

make_element('item', 'Albatross', size='large', quantity=6)

# **只能在最后
def b(x, *args, y, **kwargs):
    pass

# 7.2 只接受关键字参数的函数
def recv(maxsize, *, block):
    'Receives a message'
    pass

recv(1024, True) # recv() takes 1 positional argument but 2 were given
recv(1024, block=True) # OK

def minimum(*values, clip=None):
    m = min(values)
    if clip is not None:
        m = clip if clip > m else m
    return m

minimum(1, 5, 2, -5, 10) # -5
minimum(1, 5, 2, -5, 10, clip=0) # 加上了关键字参数限制,最小值小于0时输出0

# 7.3 给函数参数增加元信息
def add(x:int, y:int) -> int:
    return x + y

help(add)
add.__annotations__  # {'x': int, 'y': int, 'return': int}

# 7.4 赶回多个值的函数
def myfun():
    return 1, 2, 3

a, b, c = myfun()

# 7.5 定义有默认参数的函数
def spam(a, b=42):
    print(a, b)

spam(1)
spam(1, 2)

def spam(a, b=None):
    if b is None:
        b = []

# 7.6 定义匿名或内联函数
add = lambda x,y:x + y
add(2, 3)

names = ['David Beazley', 'Brian Jones',
         'Raymond Hettinger', 'Ned Batchelder']
sorted(names, key=lambda name:name.split()[-1].lower())
# names[-1].split()[-1].lower()

# 7.7 匿名函数捕获变量值
x = 10
a = lambda y: x+y
x = 20
b = lambda y: x+y
a(10)
b(10)

# 第八章:类与对象
# 8.1 改变对象的字符串显示
class Pair():
    def __init__(self, x, y):
        self.x = x
        self.y = y
    
    def __repr__(self):
        return 'Pair({0.x!r}, {0.y!r})'.format(self)

    def __str__(self):
        return '({0.x!s}, {0.y!s})'.format(self)

p = Pair(3, 4)
print(p)

# 8.2 自定义字符串的格式化
_formats = {
    'ymd' : '{d.year}-{d.month}-{d.day}',
    'mdy' : '{d.month}/{d.day}/{d.year}',
    'dmy' : '{d.day}/{d.month}/{d.year}'
    }

class Date:
    def __init__(self, year, month, day):
        self.year = year
        self.month = month
        self.day = day

    def __format__(self, code):
        if code == '':
            code = 'ymd'
        fmt = _formats[code]
        return fmt.format(d=self)

d = Date(2012, 12, 21)
format(d)
format(d, 'mdy')
'The date is {:ymd}'.format(d)

# 8.7 调用父类方法
class A:
    def spam(self):
        print('A.spam')

class B(A):
    def spam(self):
        print('B.spam')
        super().spam()

# 常见用法1:在__init__方法中确保父类被正确的初始化了
class A:
    def __init__(self):
        self.x = 0

class B(A):
    def __init__(self):
        super().__init__()
        self.y = 1

# 常见用法2:出现在覆盖Python特殊方法的代码中
class Proxy:
    def __init__(self, obj):
        self.obj = obj
    
    def __getattr__(self, name):
        return getattr(self._obj, name)
    
    def __setattr__(self, name, value):
        if name.starwith('_'):
            super().__setattr__(name, value)
        else:
            setattr(self.obj, name, value)