From 2c14dde37c9551e6615a0f926558a2177091c464 Mon Sep 17 00:00:00 2001 From: liyongbin Date: 2022年7月15日 18:00:48 +0800 Subject: [PATCH 01/32] =?UTF-8?q?=E6=A0=BC=E5=BC=8F=E5=8C=96=E4=BB=A3?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- 04/dicset.py | 115 +++++++++++++++++++++++++-------------------------- 1 file changed, 57 insertions(+), 58 deletions(-) diff --git a/04/dicset.py b/04/dicset.py index 1bc3c366..68c59b93 100644 --- a/04/dicset.py +++ b/04/dicset.py @@ -4,133 +4,132 @@ # 根据商品ID求价格,用列表 def find_product_price(products, product_id): - for id, price in products: - if id == product_id: - return price - return None - + for id, price in products: + if id == product_id: + return price + return None + + products = [ - (1, 100), - (2, 400), - (3, 50), - (4, 400) + (1, 100), + (2, 400), + (3, 50), + (4, 400) ] # 更改需求,找出有多少种不同的价格 def find_unique_price(products): - unique_price_list = [] - for _, price in products: - if price not in unique_price_list: - unique_price_list.append(price) - return len(unique_price_list) - - + unique_price_list = [] + for _, price in products: + if price not in unique_price_list: + unique_price_list.append(price) + return len(unique_price_list) + + # 字典版 def find_unique_price_set(products): - unique_price_set = set() - for _, price in products: - unique_price_set.add(price) - return len(unique_price_set) + unique_price_set = set() + for _, price in products: + unique_price_set.add(price) + return len(unique_price_set) if __name__ == "__main__": # 初始化字典和集合 - d1 = {"name":"jason", "age":20, "gender":"male"} - d2 = dict({"name":"jason", "age":20, "gender":"male"}) + d1 = {"name": "jason", "age": 20, "gender": "male"} + d2 = dict({"name": "jason", "age": 20, "gender": "male"}) d3 = dict([('name', 'jason'), ('age', 20), ('gender', 'male')]) d4 = dict(name='jason', age=20, gender='male') print(d1 == d2 == d3 == d4) - + s1 = {1, 2, 3} s2 = set([1, 2, 3]) print(s1 == s2) - + # 混合类型 s = {1, "hello", 5.0} print(s) - + # 元素访问 - d = {"name":"zym", "age":20} + d = {"name": "zym", "age": 20} print(d["name"]) print(d.get("age")) print(d.get("locate", "null")) - + s = {1, 2, 3} # print(s[1]) #本行出错 - + # 判断元素是否在字典/集合内 s = {1, 2, 3} print(1 in s) print(10 in s) - d = {"name":"zym", "age":20} + d = {"name": "zym", "age": 20} print("name" in d) print("location" in d) - + # 增删查改函数 - d = {"name":"zym", "age":20} - d["gender"] = "male" #增加元素 + d = {"name": "zym", "age": 20} + d["gender"] = "male" # 增加元素 d["dob"] = "1999-02-01" print(d) - d["dob"] = "1998-01-01" #更新键值 + d["dob"] = "1998-01-01" # 更新键值 print(d) - d.pop("dob") #删除键值 + d.pop("dob") # 删除键值 print(d) - + s = {1, 2, 3} - s.add(4) #增加元素 + s.add(4) # 增加元素 print(s) - s.remove(4) #删除元素 + s.remove(4) # 删除元素 print(s) - + # 字典排序 - d = {'b':1, 'a':2, 'c':10} + d = {'b': 1, 'a': 2, 'c': 10} # 根据字典键的升序排序 - d_sorted_by_key = sorted(d.items(), key = lambda x:x[0]) + d_sorted_by_key = sorted(d.items(), key=lambda x: x[0]) print(d_sorted_by_key) # 根据字典值的升序排序 - d_sorted_by_value = sorted(d.items(), key = lambda x:x[1]) + d_sorted_by_value = sorted(d.items(), key=lambda x: x[1]) print(d_sorted_by_value) - + # 对集合排序 - s = [3, 4, 2, 1] + s = {3, 4, 2, 1} s_sorted = sorted(s) print(s_sorted) - + # 根据商品ID找商品价格 print("id为2的商品价格为{}".format(find_product_price(products, 2))) - + # 用字典来存储 products_set = { - 1:100, - 2:400, - 3:50, - 4:400} + 1: 100, + 2: 400, + 3: 50, + 4: 400} print("id为3的商品价格为{}".format(products_set[3])) - + # 看商品里有多少种不同的价格? # 列表版, O(N2) - print("不同价格的数目为{}".format( find_unique_price(products))) + print("不同价格的数目为{}".format(find_unique_price(products))) # 字典版,O(N) - print("不同价格的数目为{}".format( find_unique_price_set(products))) - + print("不同价格的数目为{}".format(find_unique_price_set(products))) + # 计算效率 import time - + id = [x for x in range(0, 10000)] price = [x for x in range(20000, 30000)] products = list(zip(id, price)) - + # 计算列表版本的时间 start_using_list = time.perf_counter() find_unique_price(products) end_using_list = time.perf_counter() print("使用列表耗时:{}".format(end_using_list - start_using_list)) - + # 计算字典版本的时间 start_using_set = time.perf_counter() find_unique_price_set(products) end_using_set = time.perf_counter() print("使用列表耗时:{}".format(end_using_set - start_using_set)) - - \ No newline at end of file From 6cebbe5d3a3ef0e0ac5cf948b4da25c74a9d13f6 Mon Sep 17 00:00:00 2001 From: liyongbin Date: 2022年7月18日 14:11:58 +0800 Subject: [PATCH 02/32] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E4=BB=A3=E7=A0=81?= =?UTF-8?q?=E6=AF=94=E8=BE=83=E5=AD=97=E7=AC=A6=E4=B8=B2=E6=8B=BC=E6=8E=A5?= =?UTF-8?q?&join=E5=87=BD=E6=95=B0=E8=80=97=E6=97=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- 05/string.py | 108 +++++++++++++++++++++++++++------------------------ 1 file changed, 57 insertions(+), 51 deletions(-) diff --git a/05/string.py b/05/string.py index 7f35653d..b8154515 100644 --- a/05/string.py +++ b/05/string.py @@ -1,54 +1,60 @@ -#-*- coding:utf8-*- +# -*- coding:utf8-*- # 基础篇:05深入浅出字符串 - +import time if __name__ == "__main__": - name = 'aaa' - city = "bbb" - text = """ccc""" - print(name, city, text) - - # 转义符 - s = "a\nb\tc" - print(s) - print(len(s)) - - # 索引切片遍历 - name = "jason" - print(name[0]) - print(name[1:3]) - for char in name: - print(char) - - # 改变字符串 - s = "hello" - s = 'H' + s[1:] - print(s) - s = s.replace('H', 'h') - print(s) - - # 字符串拼接,时间复杂度O(N) - s = '' - for n in range(0, 100000): - s += str(n) - print(s) - - # join函数 时间复杂度O(N) - l = [] - for n in range(0, 100000): - l.append(str(n)) - l = ' '.join(l) - print(l) - - # split分割数据 - path = "hive://ads/training_table" - namespace = path.split('//')[1].split('/')[0] - table = path.split('//')[1].split('/')[1] - print(namespace, table) - - # strip函数 - s = " my name is jason " - print(s.strip()) - - # 字符串格式化函数 - print("我的名字叫{},年龄{}".format("zym", str(35))) \ No newline at end of file + name = 'aaa' + city = "bbb" + text = """ccc""" + print(name, city, text) + + # 转义符 + s = "a\nb\tc" + print(s) + print(len(s)) + + # 索引切片遍历 + name = "jason" + print(name[0]) + print(name[1:3]) + for char in name: + print(char) + + # 改变字符串 + s = "hello" + s = 'H' + s[1:] + print(s) + s = s.replace('H', 'h') + print(s) + + # 字符串拼接,时间复杂度O(N) + start_using_list = time.perf_counter() + s = '' + for n in range(0, 1000): + s += str(n) + # print(s) + end_using_list = time.perf_counter() + print("使用字符串拼接耗时:{}".format(end_using_list - start_using_list)) + + # join函数 时间复杂度O(N) + start_using_list = time.perf_counter() + l = [] + for n in range(0, 1000): + l.append(str(n)) + l = ' '.join(l) + # print(l) + end_using_list = time.perf_counter() + print("使用join函数耗时:{}".format(end_using_list - start_using_list)) + + # split分割数据 + path = "hive://ads/training_table" + namespace = path.split('//')[1].split('/')[0] + table = path.split('//')[1].split('/')[1] + print(namespace, table) + + # strip函数 + s = " my name is jason " + print(s.strip()) + + # 字符串格式化函数 + print("我的名字叫{},年龄{}".format("zym", str(35))) \ No newline at end of file From d55155a766db0c1e7500bbd84612132acd62dced Mon Sep 17 00:00:00 2001 From: liyongbin Date: 2022年7月18日 20:01:35 +0800 Subject: [PATCH 03/32] =?UTF-8?q?=E4=BF=AE=E6=94=B9readline=E7=89=88?= =?UTF-8?q?=E6=9C=AC=E7=9A=84parse=E4=BB=A3=E7=A0=81=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- 06/inout.py | 199 ++++++++++++++++++++++++++-------------------------- 1 file changed, 99 insertions(+), 100 deletions(-) diff --git a/06/inout.py b/06/inout.py index 4e029bba..e245a9e3 100644 --- a/06/inout.py +++ b/06/inout.py @@ -1,6 +1,5 @@ -#coding:utf-8 -#第6课:输入与输出 - +# coding:utf-8 +# 第6课:输入与输出 import re import json @@ -8,105 +7,105 @@ # 处理文本 def parse(text): - # 去除标点符号和换行 - text = re.sub(r'[^\w ]', ' ', text) - # 转为小写 - text = text.lower() - # 单词列表 - word_list = text.split(' ') - # 去除空白单词 - word_list = filter(None, word_list) - # 生成单词和词频的字典 - word_cnt = {} - for word in word_list: - if word not in word_cnt: - word_cnt[word] = 0 - word_cnt[word] += 1 - - # 按照词频排序 - sorted_word_cnt = sorted(word_cnt.items(), key = lambda kv: kv[1], reverse = True) - return sorted_word_cnt - - + # 去除标点符号和换行 + text = re.sub(r'[^\w ]', ' ', text) + # 转为小写 + text = text.lower() + # 单词列表 + word_list = text.split(' ') + # 去除空白单词 + word_list = filter(None, word_list) + # 生成单词和词频的字典 + word_cnt = {} + for word in word_list: + if word not in word_cnt: + word_cnt[word] = 0 + word_cnt[word] += 1 + + # 按照词频排序 + sorted_word_cnt = sorted(word_cnt.items(), key=lambda kv: kv[1], reverse=True) + return sorted_word_cnt + + # readline版本的parse,练习1 # 处理文本 def parse_readline(infile): - # 生成单词和词频的字典 - word_cnt = {} - while True: - text = infile.readline() - if not text: - break - print(text) - # 去除标点符号和换行 - text = re.sub(r'[^\w ]', ' ', text) - # 转为小写 - text = text.lower() - # 单词列表 - word_list = text.split(' ') - # 去除空白单词 - word_list = filter(None, word_list) - - for word in word_list: - if word not in word_cnt: - word_cnt[word] = 0 - word_cnt[word] += 1 - - # 按照词频排序 - sorted_word_cnt = sorted(word_cnt.items(), key = lambda kv: kv[1], reverse = True) - return sorted_word_cnt - + # 生成单词和词频的字典 + word_cnt = {} + while True: + text = infile.readline() + if not text: + break + print(text) + # 去除标点符号和换行 + text = re.sub(r'[^\w ]', ' ', text) + # 转为小写 + text = text.lower() + # 单词列表 + word_list = text.split(' ') + # 去除空白单词 + word_list = filter(None, word_list) + + for word in word_list: + if word not in word_cnt: + word_cnt[word] = 0 + word_cnt[word] += 1 + + # 按照词频排序 + sorted_word_cnt = sorted(word_cnt.items(), key=lambda kv: kv[1], reverse=True) + return sorted_word_cnt + if __name__ == "__main__": - """ - # 输入 - name = input("姓名:") - gender = input("男的?(y/n)") - - welcome_str = "欢迎来到矩阵空间{prefix}{name}." - welcome_dic = { - "prefix":"Mr." if gender == 'y' else "Mrs.", - "name":name - } - print(welcome_str.format(**welcome_dic)) - - # 输入类型转换 - a = input("输入a:") - b = input("输入b:") - print("a + b ={}".format(a+b)) - print("a的类型为{},b的类型为{}".format(type(a), type(b))) - print("a + b ={}".format(int(a) + int(b))) - """ - # 文件输入输出 - with open("in.txt", "r") as fin: - text = fin.read() - - word_and_freq = parse(text) - - with open("out.txt", "w") as fout: - for word, freq in word_and_freq: - fout.write('{} {}\n'.format(word, freq)) - - # 使用JSON - params = { - "symbol" : "123456", - "type" : "limit", - "price" : 123.4, - "amount" : 23 - } - params_str = json.dumps(params) - print("序列化以后") - print("类型{},值{}".format(type(params_str), params_str)) - - original_params = json.loads(params_str) - print("在去序列化之后") - print("类型{},值{}".format(type(original_params), original_params)) - - # 思考题1 - with open("in.txt", "r") as fin: - word_and_freq = parse_readline(fin) - - with open("out_readline.txt", "w") as fout: - for word, freq in word_and_freq: - fout.write('{} {}\n'.format(word, freq)) - \ No newline at end of file + """ + # 输入 + name = input("姓名:") + gender = input("男的?(y/n)") + + welcome_str = "欢迎来到矩阵空间{prefix}{name}." + welcome_dic = { + "prefix": "Mr." if gender == 'y' else "Mrs.", + "name": name + } + print(welcome_str.format(**welcome_dic)) + + # 输入类型转换 + a = input("输入a:") + b = input("输入b:") + print("a + b ={}".format(a + b)) + print("a的类型为{},b的类型为{}".format(type(a), type(b))) + print("a + b ={}".format(int(a) + int(b))) + """ + + # 文件输入输出 + with open("in.txt", "r") as fin: + text = fin.read() + + word_and_freq = parse(text) + + with open("out.txt", "w") as fout: + for word, freq in word_and_freq: + fout.write('{} {}\n'.format(word, freq)) + + # 使用JSON + params = { + "symbol": "123456", + "type": "limit", + "price": 123.4, + "amount": 23 + } + params_str = json.dumps(params) + print("序列化以后") + print("类型{},值{}".format(type(params_str), params_str)) + + original_params = json.loads(params_str) + print("在去序列化之后") + print("类型{},值{}".format(type(original_params), original_params)) + + # 思考题1 + with open("in.txt", "r") as fin: + word_and_freq = parse_readline(fin) + + with open("out_readline.txt", "w") as fout: + for word, freq in word_and_freq: + fout.write('{} {}\n'.format(word, freq)) From 35ee768c8bd51723619a24c7437c75250d7c59cc Mon Sep 17 00:00:00 2001 From: liyongbin Date: 2022年7月18日 21:45:10 +0800 Subject: [PATCH 04/32] fix bug --- 07/ifloop.py | 234 ++++++++++++++++++++++++++------------------------- 1 file changed, 118 insertions(+), 116 deletions(-) diff --git a/07/ifloop.py b/07/ifloop.py index 4d06c4e8..0e2ce429 100644 --- a/07/ifloop.py +++ b/07/ifloop.py @@ -3,121 +3,123 @@ if __name__ == "__main__": - # 条件语句 - x = -3 - if x < 0: - y = -x - else: - y = x - print(y) - - # elif语句 - id = 2 - if id == 0: - print("red") - elif id == 1: - print("yellow") - else: - print("green") - - # 循环 - l = [1, 2, 3, 4] - for item in l: - print(item) - - # 字典循环 - d = { - "name":"jason", - "dob":"2000-01-01", - "gender":"male" - } - for k in d: - print(k) - - for v in d.values(): - print(v) - - for k, v in d.items(): - print("keys:{}, values:{}".format(k, v)) - - # 用索引来循环 - l = [1,2,3,4,5,6,7] - for index in range(0, len(l)): - if index < 5: - print(l[index]) - - # 用索引和元素来循环 - l = [1,2,3,4,5,6,7] - for index, item in enumerate(l): - if index < 5: - print(item) - - # break和continue - name_price = {"一":100, - "二":10, - "三":10000} - name_color = {"一":"红", - "二":"蓝", - "三":"红"} - # 不用continue - for name, price in name_price.items(): - if price < 1000: - if name in name_color: - for color in name_color[name]: - if color != "红": - print("name:{}, color:{}".format(name, color)) - else: - print("name:{}, color:{}".format(name, None)) - # 用continue - for name, price in name_price.items(): - if price>= 1000: - continue - if name not in name_color: - print("name:{}, color:{}".format(name, None)) - continue - for color in name_color[name]: - if color == "red": - continue - print("name:{}, color:{}".format(name, color)) - - # while循环 - l = [1,2,3,4] - index = 0 - while index < len(l): - print(l[index]) - index += 1 - - # 测试for和while的效率 - import time - start_for = time.perf_counter() - for i in range(0, 1000000): - pass - end_for = time.perf_counter() - print("for循环{}秒".format(end_for-start_for)) - start_while = time.perf_counter() - i = 0 - while i < 1000000: - i += 1 - end_while = time.perf_counter() - print("while循环{}秒".format(end_while-start_while)) - - # 思考题 - attributes = ['name', 'dob', 'gender'] - values = [ - ['jason', '2000-01-01', 'male'], - ['mike', '1999-01-01', 'male'], - ['nancy', '2001-02-01', 'female'] - ] - # 多行循环语句 - result = [] - for index in range(0, len(values)): - temp = {} - for j in range(3): - temp[attributes[j]]=values[index][j] - result.append(temp) - print(result) - # 一行条件循环语句 抄同学的 - result = [dict(zip(attributes,v)) for v in values] - print(result) + # 条件语句 + x = -3 + if x < 0: + y = -x + else: + y = x + print(y) + + # elif语句 + id = 2 + if id == 0: + print("red") + elif id == 1: + print("yellow") + else: + print("green") + + # 循环 + l = [1, 2, 3, 4] + for item in l: + print(item) + + # 字典循环 + d = { + "name": "jason", + "dob": "2000-01-01", + "gender": "male" + } + for k in d: + print(k) + + for v in d.values(): + print(v) + + for k, v in d.items(): + print("keys:{}, values:{}".format(k, v)) + + # 用索引来循环 + l = [1, 2, 3, 4, 5, 6, 7] + for index in range(0, len(l)): + if index < 5: + print(l[index]) + + # 用索引和元素来循环 + l = [1, 2, 3, 4, 5, 6, 7] + for index, item in enumerate(l): + if index < 5: + print(item) + + # break和continue + name_price = {"一": 100, + "二": 10, + "三": 10000} + name_color = {"一": ["红"], + "二": ["蓝"], + "三": ["红"]} + # 不用continue + for name, price in name_price.items(): + if price < 1000: + if name in name_color: + for color in name_color[name]: + if color != "红": + print("name:{}, color:{}".format(name, color)) + else: + print("name:{}, color:{}".format(name, None)) + # 用continue + for name, price in name_price.items(): + if price>= 1000: + continue + if name not in name_color: + print("name:{}, color:{}".format(name, None)) + continue + for color in name_color[name]: + if color == "红": + continue + print("name:{}, color:{}".format(name, color)) + + # while循环 + l = [1, 2, 3, 4] + index = 0 + while index < len(l): + print(l[index]) + index += 1 + + # 测试for和while的效率 + import time + + start_for = time.perf_counter() + for i in range(0, 1000000): + pass + end_for = time.perf_counter() + print("for循环{}秒".format(end_for - start_for)) + start_while = time.perf_counter() + i = 0 + while i < 1000000: + i += 1 + end_while = time.perf_counter() + print("while循环{}秒".format(end_while - start_while)) + print("同等循环次数,for循环是while循环执行效率的{}倍".format((end_while - start_while) / (end_for - start_for))) + + # 思考题 + attributes = ['name', 'dob', 'gender'] + values = [ + ['jason', '2000-01-01', 'male'], + ['mike', '1999-01-01', 'male'], + ['nancy', '2001-02-01', 'female'] + ] + # 多行循环语句 + result = [] + for index in range(0, len(values)): + temp = {} + for j in range(0, len(attributes)): + temp[attributes[j]] = values[index][j] + result.append(temp) + print(result) + # 一行条件循环语句 抄同学的 + result = [dict(zip(attributes, v)) for v in values] + print(result) \ No newline at end of file From c44a0ebbf9aa4e465f1e5763afd131d58cb51582 Mon Sep 17 00:00:00 2001 From: liyongbin Date: 2022年7月18日 22:50:03 +0800 Subject: [PATCH 05/32] =?UTF-8?q?=E6=A0=BC=E5=BC=8F=E5=8C=96=E4=BB=A3?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- 08/except.py | 49 ++++++++++++++++++++++++------------------------- 1 file changed, 24 insertions(+), 25 deletions(-) diff --git a/08/except.py b/08/except.py index 4486c99f..2c57e4c8 100644 --- a/08/except.py +++ b/08/except.py @@ -4,31 +4,30 @@ # 自定义异常类 class MyInputError(Exception): - def __init__(self, value): - self.value = value - - def __str__(self): - return("{} is invalid input".format(repr(self.value))) + def __init__(self, value): + self.value = value + + def __str__(self): + return "{} is invalid input".format(repr(self.value)) if __name__ == "__main__": - # try except语句 - try: - s = input("输入数字,以,分隔:") - num1 = int(s.split(",")[0].strip()) - num2 = int(s.split(",")[1].strip()) - - except ValueError as err: - print("值错误:{}".format(err)) - except Exception as err: - print("其它异常:{}".format(err)) - - print("继续") - - # 自定义异常 - try: - raise MyInputError(1) - except MyInputError as err: - print("error:{}".format(err)) - print("继续2") - \ No newline at end of file + # try except语句 + try: + s = input("输入数字,以,分隔:") + num1 = int(s.split(",")[0].strip()) + num2 = int(s.split(",")[1].strip()) + + except ValueError as err: + print("值错误:{}".format(err)) + except Exception as err: + print("其它异常:{}".format(err)) + + print("继续") + + # 自定义异常 + try: + raise MyInputError(1) + except MyInputError as err: + print("error:{}".format(err)) + print("继续2") From d7683bb132b1130efbc406d5e70ac5d1e0049ef2 Mon Sep 17 00:00:00 2001 From: BurgessLee <772096931@qq.com> Date: 2022年7月21日 00:05:27 +0800 Subject: [PATCH 06/32] =?UTF-8?q?=E6=A0=BC=E5=BC=8F=E5=8C=96=E4=BB=A3?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- 09/fun.py | 236 ++++++++++++++++++++++++++++++------------------------ 1 file changed, 130 insertions(+), 106 deletions(-) diff --git a/09/fun.py b/09/fun.py index 5ccfad6f..ae75ad96 100644 --- a/09/fun.py +++ b/09/fun.py @@ -3,114 +3,138 @@ # 调用另一个函数 def func(message): - my_func(message) + my_func(message) -def my_func(message): - print("收到一个消息:{}".format(message)) +def my_func(message): + print("收到一个消息:{}".format(message)) if __name__ == "__main__": - my_func("hello world!") - - # 函数嵌套 - def my_sum(a, b): - return a+b - - result = my_sum(3, 5) - print(result) - - def find_largest_element(l): - if not isinstance(l, list): - print("输入数据不是列表") - return - if len(l) == 0: - print("列表为空") - return - largest_element = l[0] - for item in l: - if item> largest_element: - largest_element = item - print("列表中最大元素为:{}".format(largest_element)) - - find_largest_element([3, -5, 6, 8, 2, 1]) - - func("你好,python") - - # 参数的多态性 - print(my_sum([1, 2], [3, 4])) - print("hell", " world") - try: - my_sum(5, "7") - except Exception as err: - print("发生错误!{}".format(err)) - - # 函数嵌套提高效率 - def factorial(input): - # 输入检查,只运行一次 - if not isinstance(input, int): - raise Exception("必须输入整数") - if input < 0: - raise Exception("输入必须大于等于0") - - # 实际计算 - def inner_factorial(input): - if input <= 1: - return 1 - return input*inner_factorial(input-1) - - return(inner_factorial(input)) - - try: - print(factorial(12)) - except Exception as err: - print(err) - - # 函数中改变外部变量 - value = 2 - overvalue = 3 - def changeValue(): - global value - value += 1 - overvalue = 6 - print(value, overvalue) - changeValue() - print(value) - - # 嵌套函数内部修改 - # 加nonlocal - print("加nonlocal") - def outer(): - x = 3 - def inner(): - nonlocal x - x = 5 - print("内部", x) - print("外部", x) - inner() - print("外部", x) - outer() - # 不加 - print("不加") - def outer2(): - x = 3 - def inner2(): - x = 5 - print("内部", x) - print("外部", x) - inner2() - print("外部", x) - outer2() - - # 闭包,计算n次幂 - def nth_power(exp): - def exponent_of(base): - return base**exp - return exponent_of - - square = nth_power(2) - cube = nth_power(3) - - print(square(2)) - print(cube(2)) - \ No newline at end of file + my_func("hello world!") + + + # 函数嵌套 + def my_sum(a, b): + return a + b + + + result = my_sum(3, 5) + print(result) + + + def find_largest_element(l): + if not isinstance(l, list): + print("输入数据不是列表") + return + if len(l) == 0: + print("列表为空") + return + largest_element = l[0] + for item in l: + if item> largest_element: + largest_element = item + print("列表中最大元素为:{}".format(largest_element)) + + + find_largest_element([3, -5, 6, 8, 2, 1]) + + func("你好,python") + + # 参数的多态性 + print(my_sum([1, 2], [3, 4])) + print("hell", " world") + try: + my_sum(5, "7") + except Exception as err: + print("发生错误!{}".format(err)) + + + # 函数嵌套提高效率 + def factorial(input): + # 输入检查,只运行一次 + if not isinstance(input, int): + raise Exception("必须输入整数") + if input < 0: + raise Exception("输入必须大于等于0") + + # 实际计算 + def inner_factorial(input): + if input <= 1: + return 1 + return input * inner_factorial(input - 1) + + return (inner_factorial(input)) + + + try: + print(factorial(12)) + except Exception as err: + print(err) + + # 函数中改变外部变量 + value = 2 + overvalue = 3 + + + def changeValue(): + global value + value += 1 + overvalue = 6 + print(value, overvalue) + + + changeValue() + print(value) + + # 嵌套函数内部修改 + # 加nonlocal + print("加nonlocal") + + + def outer(): + x = 3 + + def inner(): + nonlocal x + x = 5 + print("内部", x) + + print("外部", x) + inner() + print("外部", x) + + + outer() + # 不加 + print("不加") + + + def outer2(): + x = 3 + + def inner2(): + x = 5 + print("内部", x) + + print("外部", x) + inner2() + print("外部", x) + + + outer2() + + + # 闭包,计算n次幂 + def nth_power(exp): + def exponent_of(base): + return base ** exp + + return exponent_of + + + square = nth_power(2) + cube = nth_power(3) + + print(square(2)) + print(cube(2)) From fdb14adcb77386be40a204a1ca46673e9a745279 Mon Sep 17 00:00:00 2001 From: BurgessLee <772096931@qq.com> Date: 2022年7月21日 00:05:46 +0800 Subject: [PATCH 07/32] =?UTF-8?q?=E6=9B=B4=E6=96=B0.gitignore=E6=96=87?= =?UTF-8?q?=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 1 + 1 file changed, 1 insertion(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..757fee31 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/.idea \ No newline at end of file From be431da1af69bfe44baa6df0e263d0205f22e225 Mon Sep 17 00:00:00 2001 From: liyongbin Date: 2022年7月21日 15:02:03 +0800 Subject: [PATCH 08/32] =?UTF-8?q?=E6=A0=BC=E5=BC=8F=E5=8C=96=E4=BB=A3?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- 10/button.py | 4 +- 10/nmfun.py | 103 +++++++++++++++++++++++++++------------------------ 2 files changed, 56 insertions(+), 51 deletions(-) diff --git a/10/button.py b/10/button.py index 08aeaca0..5294e45f 100644 --- a/10/button.py +++ b/10/button.py @@ -4,8 +4,8 @@ from tkinter import Button, mainloop button = Button( - text = "This is a button", - command = lambda : print("being pressed") + text="This is a button", + command=lambda: print("being pressed") ) button.pack() mainloop() \ No newline at end of file diff --git a/10/nmfun.py b/10/nmfun.py index b8cc2141..1ac64797 100644 --- a/10/nmfun.py +++ b/10/nmfun.py @@ -3,52 +3,57 @@ if __name__ == "__main__": - # lambda表达式 - square = lambda x:x**2 - print(square(3)) - - # 列表内部使用 - l = [(lambda x:x**2)(x) for x in range(10)] - print(l) - - # 用作函数参数 - l = [(1, 20), (3, 0), (9, 10), (2, -1)] - l.sort(key = lambda x:x[1]) - print(l) - - # 让程序简洁 - squares = map(lambda x:x**2, [1,2,3,4,5]) - print(list(squares)) - - # 函数式编程,将列表元素加倍 - def mutiply_2_pure(l): - new_list = [] - for item in l: - new_list.append(item*2) - return new_list - - print(mutiply_2_pure([1,2,3,4])) - - # map函数 - l = [1,3,5,6,8] - new_list = list(map(lambda x:x**2, l)) - print(new_list) - - # filter函数,返回列表中所有偶数 - l = [1,2,3,4,5,6,7,8,9] - new_list = filter(lambda x:x%2 == 0, l) - print(list(new_list)) - - # reduce函数 计算阶乘 - from functools import reduce - product = reduce(lambda x, y:x*y, l) - print(product) - - # 思考题 - # 1 将字典按值从大到小排序 - import operator - d = {"mike":10, "lucy":2, "ben":30} - print(d.items()) - sort_d = sorted(d.items(), key=operator.itemgetter(1), reverse=True) - print(sort_d) - + # lambda表达式 + square = lambda x: x ** 2 + print(square(3)) + + # 列表内部使用 + l = [(lambda x: x ** 2)(x) for x in range(10)] + print(l) + + # 用作函数参数 + l = [(1, 20), (3, 0), (9, 10), (2, -1)] + l.sort(key=lambda x: x[1]) + print(l) + + # 让程序简洁 + squares = map(lambda x: x ** 2, [1, 2, 3, 4, 5]) + print(list(squares)) + + + # 函数式编程,将列表元素加倍 + def mutiply_2_pure(l): + new_list = [] + for item in l: + new_list.append(item * 2) + return new_list + + + print(mutiply_2_pure([1, 2, 3, 4])) + + # map函数 + l = [1, 3, 5, 6, 8] + new_list = list(map(lambda x: x ** 2, l)) + print(new_list) + + # filter函数,返回列表中所有偶数 + l = [1, 2, 3, 4, 5, 6, 7, 8, 9] + new_list = filter(lambda x: x % 2 == 0, l) + print(list(new_list)) + + # reduce函数 计算阶乘 + from functools import reduce + + product = reduce(lambda x, y: x * y, l) + print(product) + + # 思考题 + # 1 将字典按值从大到小排序 + import operator + + d = {"mike": 10, "lucy": 2, "ben": 30} + print(d.items()) + sort_d = sorted(d.items(), key=operator.itemgetter(1), reverse=True) + print(sort_d) + + print(sorted(d.items(), key=lambda x: x[1], reverse=True)) From 36303c1e1e7b4022a98ad65697fbeb56dbd11d61 Mon Sep 17 00:00:00 2001 From: liyongbin Date: 2022年7月21日 16:46:44 +0800 Subject: [PATCH 09/32] =?UTF-8?q?=E6=A0=BC=E5=BC=8F=E5=8C=96=E4=BB=A3?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- 11/class.py | 265 +++++++++++++++++++++++++++------------------------- 1 file changed, 136 insertions(+), 129 deletions(-) diff --git a/11/class.py b/11/class.py index 922d1518..4c7c2cac 100644 --- a/11/class.py +++ b/11/class.py @@ -4,150 +4,157 @@ # 类 class Document(): - def __init__(self, title, author, context): - print("调用初始函数!") - self.title = title - self.author = author - self.__context = context #私有属性 - - def get_context_length(self): - return len(self.__context) - - def intercept_context(self, length): - self.__context = self.__context[:length] - - + def __init__(self, title, author, context): + print("调用初始函数!") + self.title = title + self.author = author + self.__context = context # 私有属性 + + def get_context_length(self): + return len(self.__context) + + def intercept_context(self, length): + self.__context = self.__context[:length] + + # 类2 class Document2(): - WELCOME_STR = "欢迎,本书的内容为{}." - - def __init__(self, title, author, context): - print("调用初始函数!") - self.title = title - self.author = author - self.__context = context #私有属性 - - # 类函数 - @classmethod - def create_empty_book(cls, title, author): - return cls(title=title, author=author, context="nothing") - - # 成员函数 - def get_context_length(self): - return len(self.__context) - - # 静态函数 - @staticmethod - def get_welcome(context): - return Document2.WELCOME_STR.format(context) - + WELCOME_STR = "欢迎,本书的内容为{}." + + def __init__(self, title, author, context): + print("调用初始函数!") + self.title = title + self.author = author + self.__context = context # 私有属性 + + # 类函数 + @classmethod + def create_empty_book(cls, title, author): + return cls(title=title, author=author, context="nothing") + + # 成员函数 + def get_context_length(self): + return len(self.__context) + + # 静态函数 + @staticmethod + def get_welcome(context): + return Document2.WELCOME_STR.format(context) + + # 类的继承 class Entity(): - def __init__(self, object_type): - print("父类构造函数") - self.object_type = object_type - - def get_contex_length(self): - raise Exception("没有定义get_context_length") - - def print_title(self): - print(self.title) - - + def __init__(self, object_type): + print("父类构造函数") + self.object_type = object_type + + def get_contex_length(self): + raise Exception("没有定义get_context_length") + + def print_title(self): + print(self.title) + + class Document3(Entity): - def __init__(self, title, author, context): - Entity.__init__(self, "document") - print("Document3调用初始函数!") - self.title = title - self.author = author - self.__context = context - - def get_context_length(self): - return len(self.__context) - + def __init__(self, title, author, context): + Entity.__init__(self, "document") + print("Document3调用初始函数!") + self.title = title + self.author = author + self.__context = context + + def get_context_length(self): + return len(self.__context) + + class Video(Entity): - def __init__(self, title, author, video_length): - Entity.__init__(self, "video") - print("video调用初始函数!") - self.title = title - self.author = author - self.__video_length = video_length - - def get_context_length(self): - return self.__video_length - + def __init__(self, title, author, video_length): + Entity.__init__(self, "video") + print("video调用初始函数!") + self.title = title + self.author = author + self.__video_length = video_length + + def get_context_length(self): + return self.__video_length + # 抽象函数和抽象类 from abc import ABCMeta, abstractmethod -class Entity2(metaclass = ABCMeta): - @abstractmethod - def get_title(self): - pass - - @abstractmethod - def set_title(self, title): - pass - + + +class Entity2(metaclass=ABCMeta): + @abstractmethod + def get_title(self): + pass + + @abstractmethod + def set_title(self, title): + pass + class Document4(Entity2): - def get_title(self): - return self.title - - def set_title(self, title): - self.title = title - - + def get_title(self): + return self.title + + def set_title(self, title): + self.title = title + + # 思考题 class A(): - def __init__(self): - print("A") - + def __init__(self): + print("A") + + class B(A): - def __init__(self): - A.__init__(self) - print("B") - + def __init__(self): + A.__init__(self) + print("B") + + class C(A): - def __init__(self): - A.__init__(self) - print("C") - + def __init__(self): + A.__init__(self) + print("C") + + class D(B, C): - def __init__(self): - B.__init__(self) - C.__init__(self) - print("D") + def __init__(self): + B.__init__(self) + C.__init__(self) + print("D") + if __name__ == "__main__": - harry_potter_book = Document("hp", "J.K.Rowling", "aabbccgfdghhddee") - - print(harry_potter_book.title) - print(harry_potter_book.author) - print(harry_potter_book.get_context_length()) - harry_potter_book.intercept_context(10) - print(harry_potter_book.get_context_length()) - # print(harry_potter_book.__context) - - empty_book = Document2.create_empty_book("aaaaa", "bbbbb") - print(empty_book.get_context_length()) - print(empty_book.get_welcome("indeed nothing")) - - # 类继承 - hp_book = Document3("a", "aa", "aaa") - hp_movie = Video("b", "bb", 30) - - print(hp_book.object_type) - print(hp_movie.object_type) - - print(hp_book.get_context_length()) - print(hp_movie.get_context_length()) - - # 抽象类 - document = Document4() - document.set_title("hp") - print(document.get_title()) - - # entity = Entity2() - # 思考题 - d = D() - \ No newline at end of file + harry_potter_book = Document("hp", "J.K.Rowling", "aabbccgfdghhddee") + + print(harry_potter_book.title) + print(harry_potter_book.author) + print(harry_potter_book.get_context_length()) + harry_potter_book.intercept_context(10) + print(harry_potter_book.get_context_length()) + # print(harry_potter_book.__context) + + empty_book = Document2.create_empty_book("aaaaa", "bbbbb") + print(empty_book.get_context_length()) + print(empty_book.get_welcome("indeed nothing")) + + # 类继承 + hp_book = Document3("a", "aa", "aaa") + hp_movie = Video("b", "bb", 30) + + print(hp_book.object_type) + print(hp_movie.object_type) + + print(hp_book.get_context_length()) + print(hp_movie.get_context_length()) + + # 抽象类 + document = Document4() + document.set_title("hp") + print(document.get_title()) + + # entity = Entity2() + # 思考题 + d = D() From 4061c83892a508aa8337a70d551f846715943ab7 Mon Sep 17 00:00:00 2001 From: liyongbin Date: 2022年7月25日 11:00:04 +0800 Subject: [PATCH 10/32] =?UTF-8?q?=E6=A0=BC=E5=BC=8F=E5=8C=96=E4=BB=A3?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- 12/5.txt | 2 + 12/search.py | 338 ++++++++++++++++++++++++++------------------------- 2 files changed, 172 insertions(+), 168 deletions(-) create mode 100644 12/5.txt diff --git a/12/5.txt b/12/5.txt new file mode 100644 index 00000000..497f82cd --- /dev/null +++ b/12/5.txt @@ -0,0 +1,2 @@ +# 5.txt +And when this happens, and when we allow freedom ring, when we let it ring from every village and every hamlet, from every state and every city, we will be able to speed up that day when all of God's children, black men and white men, Jews and Gentiles, Protestants and Catholics, will be able to join hands and sing in the words of the old Negro spiritual: "Free at last! Free at last! Thank God Almighty, we are free at last!" \ No newline at end of file diff --git a/12/search.py b/12/search.py index d0fde773..7564abe4 100644 --- a/12/search.py +++ b/12/search.py @@ -4,194 +4,196 @@ # 搜索引擎基类 class SearchEngineBase(object): - def __init__(self): - print("父类") - - def add_corpus(self, file_path): - with open(file_path, "r") as fin: - text = fin.read() - self.process_corpus(file_path, text) - - def process_corpus(self, id, text): - raise Exception("process_corpus未定义") - - def search(self, query): - raise Exception("search未定义") - - + def __init__(self): + print("父类") + + def add_corpus(self, file_path): + with open(file_path, "r") as fin: + text = fin.read() + self.process_corpus(file_path, text) + + def process_corpus(self, id, text): + raise Exception("process_corpus未定义") + + def search(self, query): + raise Exception("search未定义") + + def main(search_engine): - for file_path in ["1.txt", "2.txt", "3.txt", "4.txt"]: - search_engine.add_corpus(file_path) - - while True: - query = input("输入检索词,输q结束:") - if query == "q": - break - results = search_engine.search(query) - print("found {} result(s):".format(len(results))) - - for result in results: - print(result) - - + for file_path in ["1.txt", "2.txt", "3.txt", "4.txt", "5.txt"]: + search_engine.add_corpus(file_path) + + while True: + query = input("输入检索词,输q结束:") + if query == "q": + break + results = search_engine.search(query) + print("found {} result(s):".format(len(results))) + + for result in results: + print(result) + + # 简单的搜索引擎 class SimpleEngine(SearchEngineBase): - def __init__(self): - super(SimpleEngine, self).__init__() - print("子类") - self.__id_to_texts = {} - - def process_corpus(self, id, text): - self.__id_to_texts[id] = text - - def search(self, query): - results = [] - for id, text in self.__id_to_texts.items(): - if query in text: - results.append(id) - return results + def __init__(self): + super(SimpleEngine, self).__init__() + print("子类") + self.__id_to_texts = {} + + def process_corpus(self, id, text): + self.__id_to_texts[id] = text + + def search(self, query): + results = [] + for id, text in self.__id_to_texts.items(): + if query in text: + results.append(id) + return results # 分词的搜索引擎 import re + class BOWEngine(SearchEngineBase): - def __init__(self): - super(BOWEngine, self).__init__() - self.__id_to_word = {} - - def process_corpus(self, id, text): - self.__id_to_word[id] = self.parse_text_to_word(text) - - def search(self, query): - query_words = self.parse_text_to_word(query) - results = [] - for id, words in self.__id_to_word.items(): - if self.query_match(query_words, words): - results.append(id) - return results - - @staticmethod - def parse_text_to_word(text): - # 使用正则表达式去除标点和换行符 - text = re.sub(r'[^\w ]', ' ', text) - # 转为小写 - text = text.lower() - # 生成所有单词的列表 - word_list = text.split(' ') - # 去除空白单词 - word_list = filter(None, word_list) - # 返回单词的set - return set(word_list) - - @staticmethod - def query_match(query_words, words): - for query_word in query_words: - if query_word not in words: - return False - return True + def __init__(self): + super(BOWEngine, self).__init__() + self.__id_to_word = {} + + def process_corpus(self, id, text): + self.__id_to_word[id] = self.parse_text_to_word(text) + + def search(self, query): + query_words = self.parse_text_to_word(query) + results = [] + for id, words in self.__id_to_word.items(): + if self.query_match(query_words, words): + results.append(id) + return results + + @staticmethod + def parse_text_to_word(text): + # 使用正则表达式去除标点和换行符 + text = re.sub(r'[^\w ]', ' ', text) + # 转为小写 + text = text.lower() + # 生成所有单词的列表 + word_list = text.split(' ') + # 去除空白单词 + word_list = filter(None, word_list) + # 返回单词的set + return set(word_list) + + @staticmethod + def query_match(query_words, words): + for query_word in query_words: + if query_word not in words: # 所有的搜索关键词都要出现在同一篇文章中 + return False + return True + # 减少查询的量 class BOWInvertedIndexEngine(SearchEngineBase): - def __init__(self): - super(BOWInvertedIndexEngine, self).__init__() - self.inverted_index = {} - - def process_corpus(self, id, text): - words = self.parse_text_to_word(text) - for word in words: - if word not in self.inverted_index: - self.inverted_index[word] = [] - self.inverted_index[word].append(id) - - def search(self, query): - query_words = list(self.parse_text_to_word(query)) - query_words_index = list() - for query_word in query_words: - query_words_index.append(0) - - # 如果某一单词倒序索引,立即返回 - for query_word in query_words: - if query_word not in self.inverted_index: - return [] - - result = [] - while True: - # 首先获得当前状态下所有倒序索引的index - current_ids = [] - for idx, query_word in enumerate(query_words): - current_index = query_words_index[idx] - current_inverted_list = self.inverted_index[query_word] - # 已经遍历到某个倒序索引的末尾,结束 - if current_index>= len(current_inverted_list): - return result - current_ids.append(current_inverted_list[current_index]) - - # 然后,如果 current_ids 的所有元素都一样,那么表明这个单词在这个元素对应的文档中都出现了 - if all(x == current_ids[0] for x in current_ids): - result.append(current_ids[0]) - query_words_index = [x+1 for x in query_words_index] - continue - - # 如果不是,把最小元素加1 - min_val = min(current_ids) - min_val_pos = current_ids.index(min_val) - query_words_index[min_val_pos] += 1 - - @staticmethod - def parse_text_to_word(text): - # 使用正则表达式去除标点和换行符 - text = re.sub(r'[^\w ]', ' ', text) - # 转为小写 - text = text.lower() - # 生成所有单词的列表 - word_list = text.split(' ') - # 去除空白单词 - word_list = filter(None, word_list) - # 返回单词的set - return set(word_list) + def __init__(self): + super(BOWInvertedIndexEngine, self).__init__() + self.inverted_index = {} + + def process_corpus(self, id, text): + words = self.parse_text_to_word(text) + for word in words: + if word not in self.inverted_index: + self.inverted_index[word] = [] + self.inverted_index[word].append(id) + + def search(self, query): + query_words = list(self.parse_text_to_word(query)) + query_words_index = list() + for query_word in query_words: + query_words_index.append(0) + + # 如果某一单词倒序索引,立即返回 + for query_word in query_words: + if query_word not in self.inverted_index: + return [] + + result = [] + while True: + # 首先获得当前状态下所有倒序索引的index + current_ids = [] + for idx, query_word in enumerate(query_words): + current_index = query_words_index[idx] + current_inverted_list = self.inverted_index[query_word] + # 已经遍历到某个倒序索引的末尾,结束 + if current_index>= len(current_inverted_list): + return result + current_ids.append(current_inverted_list[current_index]) + + # 然后,如果 current_ids 的所有元素都一样,那么表明这个单词在这个元素对应的文档中都出现了 + if all(x == current_ids[0] for x in current_ids): + result.append(current_ids[0]) + query_words_index = [x + 1 for x in query_words_index] + continue + + # 如果不是,把最小元素加1 + min_val = min(current_ids) + min_val_pos = current_ids.index(min_val) + query_words_index[min_val_pos] += 1 + + @staticmethod + def parse_text_to_word(text): + # 使用正则表达式去除标点和换行符 + text = re.sub(r'[^\w ]', ' ', text) + # 转为小写 + text = text.lower() + # 生成所有单词的列表 + word_list = text.split(' ') + # 去除空白单词 + word_list = filter(None, word_list) + # 返回单词的set + return set(word_list) # 缓存和多重继承 import pylru + class LRUCache(object): - def __init__(self, size = 2): - self.cache = pylru.lrucache(size) - - def has(self, key): - return key in self.cache - - def get(self, key): - return self.cache[key] - - def set(self, key, value): - self.cache[key] = value - + def __init__(self, size=2): + self.cache = pylru.lrucache(size) + + def has(self, key): + return key in self.cache + + def get(self, key): + return self.cache[key] + + def set(self, key, value): + self.cache[key] = value + class BOWInvertedIndexEngineWithCache(BOWInvertedIndexEngine, LRUCache): - def __init__(self): - super(BOWInvertedIndexEngineWithCache, self).__init__() - LRUCache.__init__(self) - - def search(self, query): - if self.has(query): - print("缓存命中!") - return self.get(query) - - result = super(BOWInvertedIndexEngineWithCache, self).search(query) - self.set(query, result) - - return result + def __init__(self): + super(BOWInvertedIndexEngineWithCache, self).__init__() + LRUCache.__init__(self) + + def search(self, query): + if self.has(query): + print("缓存命中!") + return self.get(query) + + result = super(BOWInvertedIndexEngineWithCache, self).search(query) + self.set(query, result) + + return result if __name__ == "__main__": - # search_engine = SimpleEngine() - # main(search_engine) - # search_engine = BOWEngine() - # main(search_engine) - # search_engine = BOWInvertedIndexEngine() - # main(search_engine) - search_engine = BOWInvertedIndexEngineWithCache() - main(search_engine) - \ No newline at end of file + # search_engine = SimpleEngine() + # main(search_engine) + # search_engine = BOWEngine() + # main(search_engine) + # search_engine = BOWInvertedIndexEngine() + # main(search_engine) + search_engine = BOWInvertedIndexEngineWithCache() + main(search_engine) From c74c97b1a1885e2f363185d5baf7af27ac989fdd Mon Sep 17 00:00:00 2001 From: liyongbin Date: 2022年7月26日 10:31:28 +0800 Subject: [PATCH 11/32] =?UTF-8?q?=E6=A0=BC=E5=BC=8F=E5=8C=96=E4=BB=A3?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- 13/main.py | 15 +++++++-------- 13/src/sub_main.py | 17 ++++++++--------- 13/test1/proto/mat.py | 8 ++++---- 13/test1/src/main.py | 6 ++++-- 13/test1/utils/mat_mul.py | 18 +++++++++--------- 13/utils/class_utils.py | 13 ++++++------- 13/utils/utils.py | 2 +- 7 files changed, 39 insertions(+), 40 deletions(-) diff --git a/13/main.py b/13/main.py index 5036c9d5..e618045a 100644 --- a/13/main.py +++ b/13/main.py @@ -9,11 +9,10 @@ if __name__ == "__main__": - print(get_sum(1, 2)) - - encoder = Encoder() - decoder = Decoder() - - print(encoder.encode("abcde")) - print(decoder.decode("edcba")) - \ No newline at end of file + print(get_sum(1, 2)) + + encoder = Encoder() + decoder = Decoder() + + print(encoder.encode("abcde")) + print(decoder.decode("edcba")) diff --git a/13/src/sub_main.py b/13/src/sub_main.py index 70644257..a6b3ba66 100644 --- a/13/src/sub_main.py +++ b/13/src/sub_main.py @@ -2,18 +2,17 @@ # 第13课 Python模块化 import sys + sys.path.append("..") from utils.class_utils import * from utils.utils import * - if __name__ == "__main__": - print(get_sum(1, 2)) - - encoder = Encoder() - decoder = Decoder() - - print(encoder.encode("abcde")) - print(decoder.decode("edcba")) - \ No newline at end of file + print(get_sum(1, 2)) + + encoder = Encoder() + decoder = Decoder() + + print(encoder.encode("abcde")) + print(decoder.decode("edcba")) diff --git a/13/test1/proto/mat.py b/13/test1/proto/mat.py index cbf3786d..5000c8ef 100644 --- a/13/test1/proto/mat.py +++ b/13/test1/proto/mat.py @@ -3,7 +3,7 @@ # /proto/mat.py class Matrix(object): - def __init__(self, data): - self.data = data - self.n = len(data) - self.m = len(data[0]) + def __init__(self, data): + self.data = data + self.n = len(data) + self.m = len(data[0]) diff --git a/13/test1/src/main.py b/13/test1/src/main.py index 6baac0d9..4fa6390f 100644 --- a/13/test1/src/main.py +++ b/13/test1/src/main.py @@ -2,11 +2,13 @@ # 第13课 Python模块化 # src/main.py import sys + +print(sys.path) sys.path.append("..") from proto.mat import Matrix from utils.mat_mul import mat_mul -a = Matrix([[1,2], [3,4]]) -b = Matrix([[5,6], [7,8]]) +a = Matrix([[1, 2], [3, 4]]) +b = Matrix([[5, 6], [7, 8]]) print(mat_mul(a, b).data) \ No newline at end of file diff --git a/13/test1/utils/mat_mul.py b/13/test1/utils/mat_mul.py index 2b48a7e5..31173d17 100644 --- a/13/test1/utils/mat_mul.py +++ b/13/test1/utils/mat_mul.py @@ -5,12 +5,12 @@ from proto.mat import Matrix def mat_mul(matrix_1: Matrix, matrix_2: Matrix): - assert matrix_1.m == matrix_2.n - n, m, s = matrix_1.n, matrix_1.m, matrix_2.m - result = [[0 for _ in range(n)] for _ in range(s)] - for i in range(n): - for j in range(s): - for k in range(m): - result[i][k] += matrix_1.data[i][j] * matrix_2.data[j][k] - - return Matrix(result) \ No newline at end of file + assert matrix_1.m == matrix_2.n + n, m, s = matrix_1.n, matrix_1.m, matrix_2.m + result = [[0 for _ in range(n)] for _ in range(s)] + for i in range(n): + for j in range(s): + for k in range(m): + result[i][k] += matrix_1.data[i][j] * matrix_2.data[j][k] + + return Matrix(result) diff --git a/13/utils/class_utils.py b/13/utils/class_utils.py index 4bc94506..cd77556a 100644 --- a/13/utils/class_utils.py +++ b/13/utils/class_utils.py @@ -2,11 +2,10 @@ # 第13课 Python模块化 class Encoder(object): - def encode(self, s): - return s[::-1] - - + def encode(self, s): + return s[::-1] + + class Decoder(object): - def decode(self, s): - return ' '.join(reversed(list(s))) - \ No newline at end of file + def decode(self, s): + return ' '.join(reversed(list(s))) diff --git a/13/utils/utils.py b/13/utils/utils.py index 65d1db43..75395718 100644 --- a/13/utils/utils.py +++ b/13/utils/utils.py @@ -2,4 +2,4 @@ # 第13课 Python模块化 def get_sum(a, b): - return a+b + return a + b From 8df77346150556784aad0ee5880e595dadf1f413 Mon Sep 17 00:00:00 2001 From: liyongbin Date: 2022年7月26日 11:22:03 +0800 Subject: [PATCH 12/32] =?UTF-8?q?=E6=A0=BC=E5=BC=8F=E5=8C=96=E4=BB=A3?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- 15/obcopy.py | 173 +++++++++++++++++++++++++++++---------------------- 1 file changed, 98 insertions(+), 75 deletions(-) diff --git a/15/obcopy.py b/15/obcopy.py index 6be03b4e..20326108 100644 --- a/15/obcopy.py +++ b/15/obcopy.py @@ -3,79 +3,102 @@ import copy - if __name__ == "__main__": - a = 2 - b = 2 - print(a == b) - print(a is b) - print("id(a) = {}".format(id(a))) - print("id(b) = {}".format(id(b))) - # 以上只对-5至256的值有效 - a = 10000000 - b = 10000000 - print(a == b) - print(a is b) - print("id(a) = {}".format(id(a))) - print("id(b) = {}".format(id(b))) - - # 对于不可变变量 - t1 = (1, 2, [3, 4]) - t2 = (1, 2, [3, 4]) - print(t1 == t2) - print(id(t1), id(t2)) - t1[-1].append(5) - print(t1 == t2) - print(id(t1), id(t2)) - - # 浅拷贝 - l1 = [1, 2, 3] - l2 = list(l1) - print(l1 == l2) - print(l1 is l2) - s1 = set([1, 2, 3]) - s2 = set(s1) - print(s1, s2) - print(s1 == s2) - print(s1 is s2) - # 通过切片操作 - l1 = [1, 2, 3] - l2 = l1[:] - print(l1 == l2) - print(l1 is l2) - # 使用copy函数 - l2 = copy.copy(l1) - print(l1 == l2) - print(l1 is l2) - # 元组的不同,返回一个指向元组的引用 - t1 = (1,2,3) - t2 = tuple(t1) - print(t1 == t2) - print(t1 is t2) - - # 浅拷贝的副作用 - l1 = [[1, 2], (30, 40)] - l2 = list(l1) - l1.append(100) - l1[0].append(3) - print(l1) - print(l2) - l1[1] += (50, 60) - print(l1) - print(l2) - - # 深拷贝 - l1 = [[1, 2], (30, 40)] - l2 = copy.deepcopy(l1) - l1.append(100) - l1[0].append(3) - print(l1, l2) - # 陷入无限循环的深拷贝 - x = [1] - x.append(x) - print(x) - y = copy.deepcopy(x) - print(y) - # 思考题 - # print(x == y) #报错 - print(x is y) \ No newline at end of file + a = 2 + b = 2 + print(a == b) + print(a is b) + print("id(a) = {}".format(id(a))) + print("id(b) = {}".format(id(b))) + + print("\n################################\n") + + # 以上只对-5至256的值有效 + a = 10000000 + b = 10000000 + print(a == b) + print(a is b) + print("id(a) = {}".format(id(a))) + print("id(b) = {}".format(id(b))) + + print("\n################################\n") + + # 对于不可变变量 + t1 = (1, 2, [3, 4]) + t2 = (1, 2, [3, 4]) + print(t1 == t2) + print(id(t1), id(t2)) + t1[-1].append(5) + print(t1, t2) + print(t1 == t2) + print(id(t1), id(t2)) + + print("\n################################\n") + + # 浅拷贝 + l1 = [1, 2, 3] + l2 = list(l1) + print(l1 == l2) + print(l1 is l2) + s1 = set([1, 2, 3]) + s2 = set(s1) + print(s1, s2) + print(s1 == s2) + print(s1 is s2) + + print("\n################################\n") + + # 通过切片操作 + l1 = [1, 2, 3] + l2 = l1[:] + print(l1 == l2) + print(l1 is l2) + + print("\n################################\n") + + # 使用copy函数 + l2 = copy.copy(l1) + print(l1 == l2) + print(l1 is l2) + + print("\n################################\n") + + # 元组的不同,返回一个指向元组的引用 + t1 = (1, 2, 3) + t2 = tuple(t1) + print(t1 == t2) + print(t1 is t2) + + print("\n################################\n") + + # 浅拷贝的副作用 + l1 = [[1, 2], (30, 40)] + l2 = list(l1) + l1.append(100) + l1[0].append(3) + print(l1) + print(l2) + l1[1] += (50, 60) + print(l1) + print(l2) + + print("\n################################\n") + + # 深拷贝 + l1 = [[1, 2], (30, 40)] + l2 = copy.deepcopy(l1) + l1.append(100) + l1[0].append(3) + print(l1, l2) + # 陷入无限循环的深拷贝 + x = [1] + x.append(x) + print(x) + y = copy.deepcopy(x) + print(y) + + print("\n################################\n") + + # 思考题 + # print(x == y) #报错 + print(x is y) From 19c863b11a2fdd336f77bb99b68a5477f44bb021 Mon Sep 17 00:00:00 2001 From: liyongbin Date: 2022年7月26日 15:37:00 +0800 Subject: [PATCH 13/32] =?UTF-8?q?=E6=A0=BC=E5=BC=8F=E5=8C=96=E4=BB=A3?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- 16/canshu.py | 155 +++++++++++++++++++++++++++++++-------------------- 1 file changed, 95 insertions(+), 60 deletions(-) diff --git a/16/canshu.py b/16/canshu.py index d6d645c4..8919ed5b 100644 --- a/16/canshu.py +++ b/16/canshu.py @@ -3,63 +3,98 @@ if __name__ == "__main__": - # 变量及赋值 - a = 1 - b = a - a = a + 1 - print(a, b) - # 列表赋值 - l1 = [1,2,3] - l2 = l1 - l1.append(4) - print(l1) - print(l2) - - # 函数参数传递 - def my_func1(b): - b = 2 - - a = 1 - my_func1(a) - print(a) - - def my_func2(b): - b = 2 - return b - - a = my_func2(a) - print(a) - # 传入可变对象 - def my_func3(l2): - l2.append(4) - l1 = [1,2,3] - my_func3(l1) - print(l1) - # 参数原值不变 - def my_func4(l2): - l2 = l2 + [4] - l1 = [1,2,3] - my_func4(l1) - print(l1) - # 要改变参数原值的做法 - def my_func5(l2): - l2 = l2 + [4] - return l2 - l1 = [1,2,3] - l1 = my_func5(l1) - print(l1) - - # 思考题1 - l1 = [1,2,3,4] - l2 = [1,2,3,4] - l3 = l2 - print(id(l1), id(l2), id(l3)) - # 思考题2 - def func(d): - d["a"] = 10 - d["b"] = 20 - - d = {"a":1, "b":2} - func(d) - print(d) - \ No newline at end of file + # 变量及赋值 + a = 1 + b = a + a = a + 1 + print(a, b) + + print("\n################################\n") + + # 列表赋值 + l1 = [1, 2, 3] + l2 = l1 + l1.append(4) + print(l1) + print(l2) + + print("\n################################\n") + + + # 函数参数传递 + def my_func1(b): + b = 2 + + + a = 1 + my_func1(a) + print(a) + + print("\n################################\n") + + + def my_func2(b): + b = 2 + return b + + + a = my_func2(a) + print(a) + + print("\n################################\n") + + + # 传入可变对象 + def my_func3(l2): + l2.append(4) + + + l1 = [1, 2, 3] + my_func3(l1) + print(l1) + + print("\n################################\n") + + + # 参数原值不变 + def my_func4(l2): + l2 = l2 + [4] + + + l1 = [1, 2, 3] + my_func4(l1) + print(l1) + + print("\n################################\n") + + + # 要改变参数原值的做法 + def my_func5(l2): + l2 = l2 + [4] + return l2 + + + l1 = [1, 2, 3] + l1 = my_func5(l1) + print(l1) + + print("\n################################\n") + + # 思考题1 + l1 = [1, 2, 3, 4] + l2 = [1, 2, 3, 4] + l3 = l2 + print(id(l1), id(l2), id(l3)) + + print("\n################################\n") + + + # 思考题2 + def func(d): + d["a"] = 10 + d["b"] = 20 + + + d = {"a": 1, "b": 2} + func(d) + print(d) From 3210c4b34477a45eae9607cf3757793c054a5672 Mon Sep 17 00:00:00 2001 From: liyongbin Date: 2022年7月27日 15:57:15 +0800 Subject: [PATCH 14/32] =?UTF-8?q?=E6=A0=BC=E5=BC=8F=E5=8C=96=E4=BB=A3?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- 17/zsq.py | 322 +++++++++++++++++++++++++++++++----------------------- 1 file changed, 187 insertions(+), 135 deletions(-) diff --git a/17/zsq.py b/17/zsq.py index fe380bf0..639e1c8f 100644 --- a/17/zsq.py +++ b/17/zsq.py @@ -5,139 +5,191 @@ import functools import time - if __name__ == "__main__": - # 函数作为变量 - def func(message): - print("收到一个消息:{}".format(message)) - - send_message = func - send_message("hello world") - - # 函数作为参数 - def root_call(fun, message): - print(fun(message)) - - root_call(func, "函数参数") - - # 函数嵌套 - def fund(message): - def get_message(message): - print("收到一个消息:{}".format(message)) - return get_message(message) - - fund("函数嵌套") - - # 闭包 - def func_closure(): - def get_message(message): - print("收到一个消息:{}".format(message)) - return get_message - - send_message = func_closure() - send_message("返回函数对象(闭包)") - - # 简单装饰器例子 - def my_decorator(func): - def wrapper(): - print("装饰器") - func() - return wrapper - - def greet(): - print("你好") - - greet = my_decorator(greet) - greet() - - # 原函数还是原函数吗? - print(greet.__name__) - print(help(greet)) - - # 使用functools.wrap - def my_decorator2(func): - @functools.wraps(func) - def wrapper(*args, **kwargs): - print("functools的装饰器") - func(*args, **kwargs) - return wrapper - - @my_decorator2 - def greet2(message): - print(message) - - greet2("functools") - print(greet2.__name__) - - # 类装饰器 - class Count(): - def __init__(self, func): - self.func = func - self.num_calls = 0 - - def __call__(self, *args, **kwargs): - self.num_calls += 1 - print("num of call is: {}".format(self.num_calls)) - return self.func(*args, **kwargs) - - @Count - def example(): - print("类装饰器") - - example() - example() - - # 装饰器嵌套 - def my_decorator_a(func): - @functools.wraps(func) - def wrapper(*args, **kwargs): - print("functools的装饰器a") - func(*args, **kwargs) - return wrapper - - def my_decorator_b(func): - @functools.wraps(func) - def wrapper(*args, **kwargs): - print("functools的装饰器b") - func(*args, **kwargs) - return wrapper - - @my_decorator_a - @my_decorator_b - def greet3(message): - print(message) - - greet3("functools") - print(greet3.__name__) - - # 应用举例 给函数加上计时功能 - def log_execution_time(func): - @functools.wraps(func) - def wrapper(*args, **kwargs): - start = time.perf_counter() - res = func(*args, **kwargs) - end = time.perf_counter() - print("函数{}运行耗时{}秒".format(func.__name__, end-start)) - return res - return wrapper - - @log_execution_time - def add(n): - s = 0 - for i in range(n): - s += i - return s - - res = add(10000) - print(res) - - @log_execution_time - def multiply(n): - s = 1 - for i in range(n): - s = s*(i+1) - return s - - res = multiply(10000) - print(res) - \ No newline at end of file + # 函数作为变量 + def func(message): + print("收到一个消息:{}".format(message)) + + + send_message = func + send_message("hello world") + + print("\n################################\n") + + + # 函数作为参数 + def root_call(fun, message): + print(fun(message)) + + + root_call(func, "函数参数") + + print("\n################################\n") + + + # 函数嵌套 + def fund(message): + def get_message(message): + print("收到一个消息:{}".format(message)) + + return get_message(message) + + + fund("函数嵌套") + + print("\n################################\n") + + + # 闭包 + def func_closure(): + def get_message(message): + print("收到一个消息:{}".format(message)) + + return get_message + + + send_message = func_closure() + send_message("返回函数对象(闭包)") + + print("\n################################\n") + + + # 简单装饰器例子 + def my_decorator(func): + def wrapper(): + print("装饰器") + func() + + return wrapper + + + def greet(): + print("你好") + + + greet = my_decorator(greet) + greet() + + print("\n################################\n") + + # 原函数还是原函数吗? + print(greet.__name__) + print(help(greet)) + + print("\n################################\n") + + + # 使用functools.wrap + def my_decorator2(func): + @functools.wraps(func) + def wrapper(*args, **kwargs): + print("functools的装饰器") + func(*args, **kwargs) + + return wrapper + + + @my_decorator2 + def greet2(message): + print(message) + + + greet2("functools") + print(greet2.__name__) + + print("\n################################\n") + + + # 类装饰器 + class Count(): + def __init__(self, func): + self.func = func + self.num_calls = 0 + + def __call__(self, *args, **kwargs): + self.num_calls += 1 + print("num of call is: {}".format(self.num_calls)) + return self.func(*args, **kwargs) + + + @Count + def example(): + print("类装饰器") + + + example() + example() + + print("\n################################\n") + + + # 装饰器嵌套 + def my_decorator_a(func): + @functools.wraps(func) + def wrapper(*args, **kwargs): + print("functools的装饰器a") + func(*args, **kwargs) + + return wrapper + + + def my_decorator_b(func): + @functools.wraps(func) + def wrapper(*args, **kwargs): + print("functools的装饰器b") + func(*args, **kwargs) + + return wrapper + + + @my_decorator_a + @my_decorator_b + def greet3(message): + print(message) + + + greet3("functools") + print(greet3.__name__) + + print("\n################################\n") + + + # 应用举例 给函数加上计时功能 + def log_execution_time(func): + @functools.wraps(func) + def wrapper(*args, **kwargs): + start = time.perf_counter() + res = func(*args, **kwargs) + end = time.perf_counter() + print("函数{}运行耗时{}秒".format(func.__name__, end - start)) + return res + + return wrapper + + + @log_execution_time + def add(n): + s = 0 + for i in range(n): + s += i + return s + + + res = add(10000) + print(res) + + print("\n################################\n") + + + @log_execution_time + def multiply(n): + s = 1 + for i in range(n): + s = s * (i + 1) + return s + + + res = multiply(10000) + print(res) + + print("\n################################\n") From 08fd13c8e9e8f47a60c07063f8f783233bc88f07 Mon Sep 17 00:00:00 2001 From: liyongbin Date: 2022年7月28日 14:26:59 +0800 Subject: [PATCH 15/32] =?UTF-8?q?=E6=A0=BC=E5=BC=8F=E5=8C=96=E4=BB=A3?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- 18/metaclass.py | 166 +++++++++++++++++++++++++++++------------------- 1 file changed, 101 insertions(+), 65 deletions(-) diff --git a/18/metaclass.py b/18/metaclass.py index db720284..c0ee88f0 100644 --- a/18/metaclass.py +++ b/18/metaclass.py @@ -6,70 +6,106 @@ class Monster(yaml.YAMLObject): - yaml_tag = "Monster" - def __init__(self, name, hp, ac, attacks): - self.name = name - self.hp = hp - self.ac = ac - self.attacks = attacks - - def __repr__(self): - return "{}(name = {}, hp = {}, ac = {}, attacks = {}".format(self.__class__.__name__, self.name, self.hp, self.ac, self.attacks) - + yaml_tag = "Monster" + + def __init__(self, name, hp, ac, attacks): + self.name = name + self.hp = hp + self.ac = ac + self.attacks = attacks + + def __repr__(self): + return "{}(name = {}, hp = {}, ac = {}, attacks = {}".format(self.__class__.__name__, self.name, self.hp, + self.ac, self.attacks) + if __name__ == "__main__": - Monster(name = "zym", hp = [2, 6], ac = 16, attacks = ["BITE", "HURT"]) - print(yaml.dump(Monster(name = "zym2", hp = [3, 6], ac = 18, attacks = ["BITE", "HURT"]))) - - # 所有用户自定义类,是type的实例 - class MyClass: - pass - - instance = MyClass() - print(type(instance)) - print(type(MyClass)) - # 用户自定义类,是type类的__call__运算符重载 - class MyClass2: - data = 1 - - instance = MyClass2() - print(MyClass2, instance, instance.data) - - MyClass = type("MyClass", (), {"data":1}) - instance = MyClass() - print(MyClass, instance, instance.data) - - # 网友的例子 - class MyMeta(type): - def __init__(self, name, bases, dic): - super().__init__(name, bases, dic) - print("===>MyMeta.__init__") - print(self.__name__) - print(dic) - print(self.yaml_tag) - - def __new__(cls, *args, **kwargs): - print("===>MyMeta.__new__") - print(cls.__name__) - return type.__new__(cls, *args, **kwargs) - - def __call__(cls, *args, **kwargs): - print("===>MyMeta.__call__") - obj = cls.__new__(cls) - cls.__init__(cls, *args, **kwargs) - return obj - - - class Foo(metaclass=MyMeta): - yaml_tag = "!Foo" - - def __init__(self, name): - print("Foo.__init__") - self.name = name - - def __new__(cls, *args, **kwargs): - print("Foo.__new__") - return object.__new__(cls) - - foo = Foo("foo") - \ No newline at end of file + Monster(name="zym", hp=[2, 6], ac=16, attacks=["BITE", "HURT"]) + print(yaml.dump(Monster(name="zym2", hp=[3, 6], ac=18, attacks=["BITE", "HURT"]))) + + print("\n################################\n") + + monster = yaml.load( + """ + ! + ac: 32 + attacks: + - BITE + - HURT + hp: + - 23 + - 86 + name: BurgessLee + """ + , Loader=yaml.FullLoader + ) + + print(yaml.dump(monster)) + + print("\n################################\n") + + + # 所有用户自定义类,是type的实例 + class MyClass: + pass + + + instance = MyClass() + print(type(instance)) + print(type(MyClass)) + + print("\n################################\n") + + + # 用户自定义类,是type类的__call__运算符重载 + class MyClass2: + data = 1 + + + instance = MyClass2() + print(MyClass2 == type(instance)) + print(MyClass2, instance, instance.data) + + print("\n################################\n") + + MyClass = type("MyClass", (), {"data": 1}) + instance = MyClass() + print(MyClass, instance, instance.data) + + print("\n################################\n") + + + # 网友的例子 + class MyMeta(type): + def __init__(self, name, bases, dic): + super().__init__(name, bases, dic) + print("===>MyMeta.__init__") + print(self.__name__) + print(dic) + print(self.yaml_tag) + + def __new__(cls, *args, **kwargs): + print("===>MyMeta.__new__") + print(cls.__name__) + return type.__new__(cls, *args, **kwargs) + + def __call__(cls, *args, **kwargs): + print("===>MyMeta.__call__") + obj = cls.__new__(cls) + cls.__init__(cls, *args, **kwargs) + return obj + + + class Foo(metaclass=MyMeta): + yaml_tag = "!Foo" + + def __init__(self, name): + print("Foo.__init__") + self.name = name + + def __new__(cls, *args, **kwargs): + print("Foo.__new__") + return object.__new__(cls) + + + foo = Foo("foo") From aa94ac4f0d89ad308019e586016bdd004ea34af4 Mon Sep 17 00:00:00 2001 From: liyongbin Date: Mon, 1 Aug 2022 16:05:29 +0800 Subject: [PATCH 16/32] =?UTF-8?q?=E6=A0=BC=E5=BC=8F=E5=8C=96=E4=BB=A3?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- 19/diedai.py | 269 +++++++++++++++++++++++++++------------------------ 1 file changed, 144 insertions(+), 125 deletions(-) diff --git a/19/diedai.py b/19/diedai.py index 8e93b0b4..b23e0909 100644 --- a/19/diedai.py +++ b/19/diedai.py @@ -7,129 +7,148 @@ import functools import time - if __name__ == "__main__": - # 判断一个对象是否可迭代 - def is_iterable(param): - try: - iter(param) - return True - except TypeError: - return False - - params = [ - 1234, - '1234', - [1, 2, 3, 4], - set([1, 2, 3, 4]), - {1:1, 2:2, 3:3, 4:4}, - (1, 2, 3, 4) - ] - for param in params: - print("{} is iterable? {}".format(param, is_iterable(param))) - - # 生成器 - def show_memory_info(hint): - pid = os.getpid() - p = psutil.Process(pid) - - info = p.memory_full_info() - memory = info.uss / 1024. /1024 - print("{} memory used: {}MB".format(hint, memory)) - - def log_execution_time(func): - @functools.wraps(func) - def wrapper(*args, **kwargs): - start = time.perf_counter() - res = func(*args, **kwargs) - end = time.perf_counter() - print("函数{}运行耗时{}秒".format(func.__name__, end-start)) - return res - return wrapper - - @log_execution_time - def test_iterator(): - show_memory_info("初始化迭代器") - list1 = [i for i in range(10000000)] - show_memory_info("初始化迭代器以后") - print(sum(list1)) - show_memory_info("调用sum以后") - - @log_execution_time - def test_generator(): - show_memory_info("初始化生成器") - list2 = (i for i in range(10000000)) - show_memory_info("初始化生成器以后") - print(sum(list2)) - show_memory_info("调用sum以后") - - test_iterator() - test_generator() - - # 使用生成器 - def generator(k): - i = 1 - while True: - yield i**k - i += 1 - - gen_1 = generator(1) - gen_3 = generator(3) - - def get_sum(n): - sum_1, sum_3 = 0, 0 - for i in range(n): - next_1 = next(gen_1) - next_3 = next(gen_3) - print("next_1={}, next_3={}".format(next_1, next_3)) - sum_1 += next_1 - sum_3 += next_3 - print(sum_1*sum_1, sum_3) - - get_sum(8) - - # 生成器的另一个例子,找指定元素在列表中的位置 - def index_generator(L, target): - for i, num in enumerate(L): - if num == target: - yield i - - print(list(index_generator([1, 6, 2, 4, 5, 2, 8, 6, 3, 2], 2))) - - # 给定两个有序序列,判断第一个是不是第二个的子序列 - def is_subsequence(a, b): - b = iter(b) - return all(i in b for i in a) - - print(is_subsequence([1,3,5], [1,2,3,4,5])) - print(is_subsequence([1,4,3], [1,2,3,4,5])) - - # 将上面的代码复杂化 - def is_subsequence2(a, b): - b = iter(b) - print(b) - - gen = (i for i in a) - print(gen) - - for i in gen: - print(i) - - gen = ((i in b) for i in a) - print(gen) - - for i in gen: - print(i) - - return all((i in b) for i in a) - - print(is_subsequence2([1,3,5], [1,2,3,4,5])) - print(is_subsequence2([1,4,3], [1,2,3,4,5])) - - # 思考题 有限元素生成器无限迭代 - gen = (i for i in range(5)) - for i in range(10): - print(next(gen)) - - - \ No newline at end of file + # 判断一个对象是否可迭代 + def is_iterable(param): + try: + iter(param) + return True + except TypeError: + return False + + + params = [ + 1234, + '1234', + [1, 2, 3, 4], + set([1, 2, 3, 4]), + {1: 1, 2: 2, 3: 3, 4: 4}, + (1, 2, 3, 4) + ] + for param in params: + print("{} is iterable? {}".format(param, is_iterable(param))) + + print("\n################################\n") + + + # 生成器 + def show_memory_info(hint): + pid = os.getpid() + p = psutil.Process(pid) + + info = p.memory_full_info() + memory = info.uss / 1024. / 1024 + print("{} memory used: {}MB".format(hint, memory)) + + + def log_execution_time(func): + @functools.wraps(func) + def wrapper(*args, **kwargs): + start = time.perf_counter() + res = func(*args, **kwargs) + end = time.perf_counter() + print("函数{}运行耗时{}秒".format(func.__name__, end - start)) + return res + + return wrapper + + + @log_execution_time + def test_iterator(): + show_memory_info("初始化迭代器") + list1 = [i for i in range(10000000)] + show_memory_info("初始化迭代器以后") + print(sum(list1)) + show_memory_info("调用sum以后") + + + @log_execution_time + def test_generator(): + show_memory_info("初始化生成器") + list2 = (i for i in range(10000000)) + show_memory_info("初始化生成器以后") + print(sum(list2)) + show_memory_info("调用sum以后") + + + test_iterator() + test_generator() + + print("\n################################\n") + + + # 使用生成器 + def generator(k): + i = 1 + while True: + yield i ** k + i += 1 + + + gen_1 = generator(1) + gen_3 = generator(3) + + + def get_sum(n): + sum_1, sum_3 = 0, 0 + for i in range(n): + next_1 = next(gen_1) + next_3 = next(gen_3) + print("next_1={}, next_3={}".format(next_1, next_3)) + sum_1 += next_1 + sum_3 += next_3 + print(sum_1 * sum_1, sum_3) + + + get_sum(8) + + print("\n################################\n") + + + # 生成器的另一个例子,找指定元素在列表中的位置 + def index_generator(L, target): + for i, num in enumerate(L): + if num == target: + yield i + + + print(list(index_generator([1, 6, 2, 4, 5, 2, 8, 6, 3, 2], 2))) + + + # 给定两个有序序列,判断第一个是不是第二个的子序列 + def is_subsequence(a, b): + b = iter(b) + return all(i in b for i in a) + + + print(is_subsequence([1, 3, 5], [1, 2, 3, 4, 5])) + print(is_subsequence([1, 4, 3], [1, 2, 3, 4, 5])) + + + # 将上面的代码复杂化 + def is_subsequence2(a, b): + b = iter(b) + print(b) + + gen = (i for i in a) + print(gen) + + for i in gen: + print(i) + + gen = ((i in b) for i in a) + print(gen) + + for i in gen: + print(i) + + return all((i in b) for i in a) + + + print(is_subsequence2([1, 3, 5], [1, 2, 3, 4, 5])) + print(is_subsequence2([1, 4, 3], [1, 2, 3, 4, 5])) + + # 思考题 有限元素生成器无限迭代 + gen = (i for i in range(5)) + for i in range(10): + print(next(gen)) From 3b50d3f220950958edb85f7903df4c3d16994d29 Mon Sep 17 00:00:00 2001 From: liyongbin Date: Tue, 2 Aug 2022 14:22:45 +0800 Subject: [PATCH 17/32] =?UTF-8?q?=E4=BF=AE=E6=94=B9&=E4=BC=98=E5=8C=96?= =?UTF-8?q?=E4=BB=A3=E7=A0=81=EF=BC=8C=E4=BD=BF=E5=85=B6=E6=AD=A3=E5=B8=B8?= =?UTF-8?q?=E8=BF=90=E8=A1=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- 20/web_crawl.py | 61 ++++---- 20/web_normal.py | 52 +++---- 20/xc.py | 353 +++++++++++++++++++++++++---------------------- 3 files changed, 242 insertions(+), 224 deletions(-) diff --git a/20/web_crawl.py b/20/web_crawl.py index 46ba946c..2b28fbac 100644 --- a/20/web_crawl.py +++ b/20/web_crawl.py @@ -9,39 +9,38 @@ async def fetch_content(url): - async with aiohttp.ClientSession(connector = aiohttp.TCPConnector(ssl=False)) as session: - async with session.get(url) as response: - return await response.text() + async with aiohttp.ClientSession(connector=aiohttp.TCPConnector(ssl=False)) as session: + async with session.get(url) as response: + return await response.text() async def main(): - url = "https://movie.douban.com/cinema/later/beijing/" - init_page = await fetch_content(url) - init_soup = BeautifulSoup(init_page, 'lxml') - - movie_names, urls_to_fetch, movie_dates = [], [], [] - - all_movies = init_soup.find("div", id = "showing-soon") - - for each_movie in all_movies.find_all("div", class_ = "item"): - all_a_tag = each_movie.find_all('a') - all_li_tag = each_movie.find_all("li") - - movie_names.append(all_a_tag[1].text) - urls_to_fetch.append(all_a_tag[1]["href"]) - movie_dates.append(all_li_tag[0].text) - - tasks = [fetch_content(url) for url in urls_to_fetch] - pages = await asyncio.gather(*tasks) - - for movie_name, movie_date, page in zip(movie_names, movie_dates, pages): - soup_item = BeautifulSoup(page, "lxml") - img_tag = soup_item.find("img") - print("{} {} {}".format(movie_name, movie_date, img_tag["src"])) - + url = "https://movie.douban.com/cinema/later/beijing/" + init_page = await fetch_content(url) + init_soup = BeautifulSoup(init_page, 'html.parser') + + movie_names, urls_to_fetch, movie_dates = [], [], [] + + all_movies = init_soup.find("div", id="showing-soon") + for each_movie in all_movies.find_all("div", class_="item"): + all_a_tag = each_movie.find_all('a') + all_li_tag = each_movie.find_all("li") + + movie_names.append(all_a_tag[1].text) + urls_to_fetch.append(all_a_tag[1]["href"]) + movie_dates.append(all_li_tag[0].text) + + tasks = [fetch_content(url) for url in urls_to_fetch] + pages = await asyncio.gather(*tasks) + + for movie_name, movie_date, page in zip(movie_names, movie_dates, pages): + soup_item = BeautifulSoup(page, "html.parser") + img_tag = soup_item.find("img") + print("电影:{}\n上映: {}\n海报: {}\n\n".format(movie_name, movie_date, img_tag['src'])) + if __name__ == "__main__": - start = time.perf_counter() - asyncio.run(main()) - end = time.perf_counter() - print("协程爬虫运行耗时{}秒".format(end-start)) \ No newline at end of file + start = time.perf_counter() + asyncio.run(main()) + end = time.perf_counter() + print("协程爬虫运行耗时{}秒".format(end - start)) diff --git a/20/web_normal.py b/20/web_normal.py index ef5c4346..40a111bd 100644 --- a/20/web_normal.py +++ b/20/web_normal.py @@ -8,30 +8,30 @@ def crawler(): - url = "https://movie.douban.com/cinema/later/beijing/" - init_page = requests.get(url).content - init_soup = BeautifulSoup(init_page, "lxml") - - all_movies = init_soup.find("div", id = "showing-soon") - for each_movie in all_movies.find_all("div", class_ = "item"): - all_a_tag = each_movie.find_all("a") - all_li_tag = each_movie.find_all("li") - - movie_name = all_a_tag[1].text - url_to_fetch = all_a_tag[1]['href'] - movie_date = all_li_tag[0].text - - response_item = requests.get(url_to_fetch).content - soup_item = BeautifulSoup(response_item, "lxml") - img_tag = soup_item.find("img") - - print("{} {} {}".format(movie_name, movie_date, img_tag)) - - -if __name__ == "__main__": - start = time.perf_counter() - crawler() - end = time.perf_counter() - print("常规爬虫运行耗时{}秒".format(end-start)) + url = "https://movie.douban.com/cinema/later/beijing/" + headers = { + 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36'} + init_page = requests.get(url, headers=headers).content + init_soup = BeautifulSoup(init_page, "html.parser") + + all_movies = init_soup.find("div", id="showing-soon") + for each_movie in all_movies.find_all("div", class_="item"): + all_a_tag = each_movie.find_all("a") + all_li_tag = each_movie.find_all("li") + + movie_name = all_a_tag[1].text + url_to_fetch = all_a_tag[1]['href'] + movie_date = all_li_tag[0].text + + response_item = requests.get(url_to_fetch, headers=headers).content + soup_item = BeautifulSoup(response_item, "html.parser") + img_tag = soup_item.find("img") - \ No newline at end of file + print("电影:{}\n上映: {}\n海报: {}\n\n".format(movie_name, movie_date, img_tag['src'])) + + +if __name__ == "__main__": + start = time.perf_counter() + crawler() + end = time.perf_counter() + print("常规爬虫运行耗时{}秒".format(end - start)) diff --git a/20/xc.py b/20/xc.py index 3d3d05d8..57a6b168 100644 --- a/20/xc.py +++ b/20/xc.py @@ -9,173 +9,192 @@ def log_execution_time(func): - @functools.wraps(func) - def wrapper(*args, **kwargs): - start = time.perf_counter() - res = func(*args, **kwargs) - end = time.perf_counter() - print("函数{}运行耗时{}秒".format(func.__name__, end-start)) - return res - return wrapper + @functools.wraps(func) + def wrapper(*args, **kwargs): + start = time.perf_counter() + res = func(*args, **kwargs) + end = time.perf_counter() + print("函数{}运行耗时{}秒".format(func.__name__, end - start)) + return res + + return wrapper if __name__ == "__main__": - # 爬虫的例子 - def crawl_page(url): - print("正在爬取{}".format(url)) - sleep_time = int(url.split('_')[-1]) - time.sleep(sleep_time) - print("OK {}".format(url)) - - @log_execution_time - def main(urls): - for url in urls: - crawl_page(url) - - main(["url_1", "url_2", "url_3", "url_4"]) - - # 并发版爬虫,效果与上面一致 - async def crawl_page2(url): - print("正在爬取{}".format(url)) - sleep_time = int(url.split('_')[-1]) - await asyncio.sleep(sleep_time) - print("OK {}".format(url)) - - async def main2(urls): - for url in urls: - await crawl_page2(url) - - start = time.perf_counter() - asyncio.run(main2(["url_1", "url_2", "url_3", "url_4"])) - end = time.perf_counter() - print("2运行耗时{}秒".format(end-start)) - - # 真正的并发版爬虫 - async def crawl_page3(url): - print("正在爬取{}".format(url)) - sleep_time = int(url.split('_')[-1]) - await asyncio.sleep(sleep_time) - print("OK {}".format(url)) - - async def main3(urls): - tasks = [asyncio.create_task(crawl_page3(url)) for url in urls] - for task in tasks: - await task - - start = time.perf_counter() - asyncio.run(main3(["url_1", "url_2", "url_3", "url_4"])) - end = time.perf_counter() - print("3运行耗时{}秒".format(end-start)) - - # task的另一种做法 - async def main4(urls): - tasks = [asyncio.create_task(crawl_page3(url)) for url in urls] - await asyncio.gather(*tasks) - - start = time.perf_counter() - asyncio.run(main4(["url_1", "url_2", "url_3", "url_4"])) - end = time.perf_counter() - print("4运行耗时{}秒".format(end-start)) - - # 协程运行底层 - async def worker_1(): - print("work1开始") - await asyncio.sleep(1) - print("work1结束") - - async def worker_2(): - print("work2开始") - await asyncio.sleep(2) - print("work2结束") - - async def main5(): - print("await之前") - await worker_1() - print("await worker_1之后") - await worker_2() - print("await worker_2之后") - - start = time.perf_counter() - asyncio.run(main5()) - end = time.perf_counter() - print("5运行耗时{}秒".format(end-start)) - - async def main6(): - task1 = asyncio.create_task(worker_1()) - task2 = asyncio.create_task(worker_2()) - print("await之前") - await task1 - print("await worker_1之后") - await task2 - print("await worker_2之后") - - - start = time.perf_counter() - asyncio.run(main6()) - end = time.perf_counter() - print("6运行耗时{}秒".format(end-start)) - - # 限定时间,超出就取消。协程出现错误 - async def worker1(): - await asyncio.sleep(1) - return 1 - - async def worker2(): - await asyncio.sleep(2) - return 2/0 - - async def worker3(): - await asyncio.sleep(3) - return 3 - - async def main7(): - task1 = asyncio.create_task(worker1()) - task2 = asyncio.create_task(worker2()) - task3 = asyncio.create_task(worker3()) - await asyncio.sleep(2) - task3.cancel() - - res = await asyncio.gather(task2, task2, task3, return_exceptions=True) - print(res) - - start = time.perf_counter() - asyncio.run(main7()) - end = time.perf_counter() - print("7运行耗时{}秒".format(end-start)) - - # 生产者消费者模型 - async def consumer(queue, id): - while True: - val = await queue.get() - print("{} get a val:{}".format(id, val)) - await asyncio.sleep(1) - - async def producer(queue, id): - for i in range(5): - val = random.randint(1, 10) - await queue.put(val) - print("{} set a val:{}".format(id, val)) - await asyncio.sleep(1) - - async def main8(): - queue = asyncio.Queue() - - consumer_1 = asyncio.create_task(consumer(queue, "consumer_1")) - consumer_2 = asyncio.create_task(consumer(queue, "consumer_2")) - producer_1 = asyncio.create_task(producer(queue, "producer_1")) - producer_2 = asyncio.create_task(producer(queue, "producer_2")) - - await asyncio.sleep(10) - - consumer_1.cancel() - consumer_2.cancel() - - await asyncio.gather(consumer_1, consumer_2, producer_1, producer_2, return_exceptions = True) - - start = time.perf_counter() - asyncio.run(main8()) - end = time.perf_counter() - print("8运行耗时{}秒".format(end-start)) - - - \ No newline at end of file + # 爬虫的例子 + def crawl_page(url): + print("正在爬取{}".format(url)) + sleep_time = int(url.split('_')[-1]) + time.sleep(sleep_time) + print("OK {}".format(url)) + + + @log_execution_time + def main(urls): + for url in urls: + crawl_page(url) + + + # main(["url_1", "url_2", "url_3", "url_4"]) + + # 并发版爬虫,效果与上面一致 + async def crawl_page2(url): + print("正在爬取{}".format(url)) + sleep_time = int(url.split('_')[-1]) + await asyncio.sleep(sleep_time) + print("OK {}".format(url)) + + + async def main2(urls): + for url in urls: + await crawl_page2(url) + + + # print(crawl_page2('')) + + # start = time.perf_counter() + # asyncio.run(main2(["url_1", "url_2", "url_3", "url_4"])) + # end = time.perf_counter() + # print("2运行耗时{}秒".format(end - start)) + + # 真正的并发版爬虫 + async def crawl_page3(url): + print("正在爬取{}".format(url)) + sleep_time = int(url.split('_')[-1]) + await asyncio.sleep(sleep_time) + print("OK {}".format(url)) + + + async def main3(urls): + tasks = [asyncio.create_task(crawl_page3(url)) for url in urls] + for task in tasks: + await task + + + # start = time.perf_counter() + # asyncio.run(main3(["url_1", "url_2", "url_3", "url_4"])) + # end = time.perf_counter() + # print("3运行耗时{}秒".format(end - start)) + + # task的另一种做法 + async def main4(urls): + tasks = [asyncio.create_task(crawl_page3(url)) for url in urls] + await asyncio.gather(*tasks) + + + # start = time.perf_counter() + # asyncio.run(main4(["url_1", "url_2", "url_3", "url_4"])) + # end = time.perf_counter() + # print("4运行耗时{}秒".format(end - start)) + + # 协程运行底层 + async def worker_1(): + print("work1开始") + await asyncio.sleep(10) + print("work1结束") + + + async def worker_2(): + print("work2开始") + await asyncio.sleep(20) + print("work2结束") + + + async def main5(): + print("await之前") + await worker_1() + print("await worker_1之后") + await worker_2() + print("await worker_2之后") + + + # start = time.perf_counter() + # asyncio.run(main5()) + # end = time.perf_counter() + # print("5运行耗时{}秒".format(end - start)) + + async def main6(): + task1 = asyncio.create_task(worker_1()) + task2 = asyncio.create_task(worker_2()) + print("await之前") + await task1 + print("await worker_1之后") + await task2 + print("await worker_2之后") + + + # start = time.perf_counter() + # asyncio.run(main6()) + # end = time.perf_counter() + # print("6运行耗时{}秒".format(end - start)) + + # 限定时间,超出就取消。协程出现错误 + async def worker1(): + await asyncio.sleep(10) + return 1 + + + async def worker2(): + await asyncio.sleep(20) + return 2 / 0 + + + async def worker3(): + await asyncio.sleep(30) + return 3 + + + async def main7(): + task1 = asyncio.create_task(worker1()) + task2 = asyncio.create_task(worker2()) + task3 = asyncio.create_task(worker3()) + await asyncio.sleep(20) + task3.cancel() + + res = await asyncio.gather(task1, task2, task3, return_exceptions=True) + print(res) + + + # start = time.perf_counter() + # asyncio.run(main7()) + # end = time.perf_counter() + # print("7运行耗时{}秒".format(end - start)) + + # 生产者消费者模型 + async def consumer(queue, id): + while True: + val = await queue.get() + print("{} get a val:{} --- {}".format(id, val, time.perf_counter())) + await asyncio.sleep(10) + + + async def producer(queue, id): + for i in range(5): + val = random.randint(1, 10) + await queue.put(val) + print("{} set a val:{} --- {}".format(id, val, time.perf_counter())) + await asyncio.sleep(10) + + + async def main8(): + queue = asyncio.Queue() + + consumer_1 = asyncio.create_task(consumer(queue, "consumer_1")) + consumer_2 = asyncio.create_task(consumer(queue, "consumer_2")) + producer_1 = asyncio.create_task(producer(queue, "producer_1")) + producer_2 = asyncio.create_task(producer(queue, "producer_2")) + + await asyncio.sleep(60) + + consumer_1.cancel() + consumer_2.cancel() + + await asyncio.gather(consumer_1, consumer_2, producer_1, producer_2, return_exceptions=True) + + + start = time.perf_counter() + asyncio.run(main8()) + end = time.perf_counter() + print("8运行耗时{}秒".format(end - start)) + + exit() From fc392b0bc8a80d65e2b6eaad5d177ef82b0c9ed9 Mon Sep 17 00:00:00 2001 From: liyongbin Date: Tue, 2 Aug 2022 16:49:14 +0800 Subject: [PATCH 18/32] =?UTF-8?q?=E6=A0=BC=E5=BC=8F=E5=8C=96=E4=BB=A3?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- 21/bf.py | 138 ++++++++++++++++++++++++++++--------------------------- 1 file changed, 70 insertions(+), 68 deletions(-) diff --git a/21/bf.py b/21/bf.py index b2da7147..c0cfe28a 100644 --- a/21/bf.py +++ b/21/bf.py @@ -10,85 +10,87 @@ # 单线程版下载 def download_one(url): - resp = requests.get(url) - print("read {} from {}".format(len(resp.content), url)) - + resp = requests.get(url) + print("read {} from {}".format(len(resp.content), url)) + def download_all(sites): - for site in sites: - download_one(site) - + for site in sites: + download_one(site) + # 多线程版下载 def download_all_futures(sites): - with concurrent.futures.ThreadPoolExecutor(max_workers = 5) as executor: - executor.map(download_one, sites) - + with concurrent.futures.ThreadPoolExecutor(max_workers=5) as executor: + executor.map(download_one, sites) + # 并行版 def download_all_futures_bx(sites): - with concurrent.futures.ThreadPoolExecutor() as executor: - executor.map(download_one, sites) - + with concurrent.futures.ProcessPoolExecutor() as executor: + executor.map(download_one, sites) + # 另一种写法的并行版本 def download_all_futures_bx2(sites): - with concurrent.futures.ThreadPoolExecutor() as executor: - to_do = [] - for site in sites: - future = executor.submit(download_one, site) - to_do.append(future) - for future in concurrent.futures.as_completed(to_do): - future.result() + with concurrent.futures.ThreadPoolExecutor(max_workers=5) as executor: + to_do = [] + for site in sites: + future = executor.submit(download_one, site) + to_do.append(future) + + for future in concurrent.futures.as_completed(to_do): + future.result() if __name__ == "__main__": - sites = [ - 'http://www.dapenti.com/blog/readforwx.asp?name=xilei&id=143655', - 'http://www.dapenti.com/blog/readforwx.asp?name=xilei&id=143656', - 'http://www.dapenti.com/blog/readforwx.asp?name=xilei&id=143657', - 'http://www.dapenti.com/blog/readforwx.asp?name=xilei&id=143658', - 'http://www.dapenti.com/blog/readforwx.asp?name=xilei&id=143659', - 'http://www.dapenti.com/blog/readforwx.asp?name=xilei&id=143660', - 'http://www.dapenti.com/blog/readforwx.asp?name=xilei&id=143661', - 'http://www.dapenti.com/blog/readforwx.asp?name=xilei&id=143662' - ] - try: - start_time = time.perf_counter() - download_all(sites) - end_time = time.perf_counter() - print("单线程版下载了{}个网站,耗时{}".format(len(sites), end_time-start_time)) - - start_time = time.perf_counter() - download_all_futures(sites) - end_time = time.perf_counter() - print("多线程版下载了{}个网站,耗时{}".format(len(sites), end_time-start_time)) - - start_time = time.perf_counter() - download_all_futures_bx(sites) - end_time = time.perf_counter() - print("并行版下载了{}个网站,耗时{}".format(len(sites), end_time-start_time)) - - start_time = time.perf_counter() - download_all_futures_bx2(sites) - end_time = time.perf_counter() - print("另一个并行版下载了{}个网站,耗时{}".format(len(sites), end_time-start_time)) - # 处理requests异常 - except ConnectionError as err: - print(err) - except HTTPError as err: - print(err) - except Timeout as err: - print(err) - # 处理futures异常 - except TooManyRedirects as err: - print(err) - except CancelledError as err: - print(err) - except TimeoutError as err: - print(err) - except BrokenExecutor as err: - print(err) - except: - print("发生错误") - \ No newline at end of file + sites = [ + 'http://www.dapenti.com/blog/readforwx.asp?name=xilei&id=143655', + 'http://www.dapenti.com/blog/readforwx.asp?name=xilei&id=143656', + 'http://www.dapenti.com/blog/readforwx.asp?name=xilei&id=143657', + 'http://www.dapenti.com/blog/readforwx.asp?name=xilei&id=143658', + 'http://www.dapenti.com/blog/readforwx.asp?name=xilei&id=143659', + 'http://www.dapenti.com/blog/readforwx.asp?name=xilei&id=143660', + 'http://www.dapenti.com/blog/readforwx.asp?name=xilei&id=143661', + 'http://www.dapenti.com/blog/readforwx.asp?name=xilei&id=143662' + ] + try: + # start_time = time.perf_counter() + # download_all(sites) + # end_time = time.perf_counter() + # print("单线程版下载了{}个网站,耗时{}".format(len(sites), end_time - start_time)) + + # start_time = time.perf_counter() + # download_all_futures(sites) + # end_time = time.perf_counter() + # print("多线程版下载了{}个网站,耗时{}".format(len(sites), end_time - start_time)) + + # start_time = time.perf_counter() + # download_all_futures_bx(sites) + # end_time = time.perf_counter() + # print("并行版下载了{}个网站,耗时{}".format(len(sites), end_time - start_time)) + + start_time = time.perf_counter() + download_all_futures_bx2(sites) + end_time = time.perf_counter() + print("另一个并行版下载了{}个网站,耗时{}".format(len(sites), end_time - start_time)) + # 处理requests异常 + except ConnectionError as err: + print(err) + except requests.HTTPError as err: + print(err) + except requests.Timeout as err: + print(err) + # 处理futures异常 + except concurrent.futures.TooManyRedirects as err: + print(err) + except concurrent.futures.CancelledError as err: + print(err) + except concurrent.futures.TimeoutError as err: + print(err) + except concurrent.futures.BrokenExecutor as err: + print(err) + except: + print("发生错误") + + exit() \ No newline at end of file From b03a2570f7aea7c9ca0b8cf2bb542ab9e23939da Mon Sep 17 00:00:00 2001 From: liyongbin Date: Tue, 2 Aug 2022 18:16:57 +0800 Subject: [PATCH 19/32] =?UTF-8?q?=E6=A0=BC=E5=BC=8F=E5=8C=96=E4=BB=A3?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- 22/bf_as.py | 156 ++++++++++++++++++++++++++++------------------------ 1 file changed, 83 insertions(+), 73 deletions(-) diff --git a/22/bf_as.py b/22/bf_as.py index c1ea8572..7803ca38 100644 --- a/22/bf_as.py +++ b/22/bf_as.py @@ -6,98 +6,108 @@ import aiohttp import time import concurrent.futures -import multiprocessing +import pathos.multiprocessing # 异步网页下载 async def download_one(url): - async with aiohttp.ClientSession() as session: - async with session.get(url) as resp: - print("read {} from {}".format(resp.content_length, url)) - - + async with aiohttp.ClientSession() as session: + async with session.get(url) as resp: + print("read {} from {}".format(resp.content_length, url)) + + async def download_all(sites): - tasks = [asyncio.create_task(download_one(site)) for site in sites] - await asyncio.gather(*tasks) - - + tasks = [asyncio.create_task(download_one(site)) for site in sites] + await asyncio.gather(*tasks) + + # 思考题 求列表中元素的整数平方和 常规版本 def cpu_bound(number): - print("number={}, result={}".format(number, sum(i*i for i in range(number)))) - + print("number={}, result={}".format(number, sum(i * i for i in range(number)))) + + def calculate_sums(numbers): - for number in numbers: - cpu_bound(number) - + for number in numbers: + cpu_bound(number) + + def calcuter_normal(numbers): - start_time = time.perf_counter() - calculate_sums(numbers) - end_time = time.perf_counter() - print("普通版本,耗时{}秒".format(end_time-start_time)) - + start_time = time.perf_counter() + calculate_sums(numbers) + end_time = time.perf_counter() + print("普通版本,耗时{}秒".format(end_time - start_time)) + + # 思考题 并行版本 def calculate_sums_future(numbers): - with concurrent.futures.ThreadPoolExecutor() as executor: - executor.map(cpu_bound, numbers) - + with concurrent.futures.ThreadPoolExecutor() as executor: + executor.map(cpu_bound, numbers) + + def calcuter_future(numbers): - start_time = time.perf_counter() - calculate_sums_future(numbers) - end_time = time.perf_counter() - print("多进程版本,耗时{}秒".format(end_time-start_time)) - + start_time = time.perf_counter() + calculate_sums_future(numbers) + end_time = time.perf_counter() + print("多进程版本,耗时{}秒".format(end_time - start_time)) + + # 思考题 动态规划版本 -squ = {} # 用来储存中间结果 +squ = {} # 用来储存中间结果 + + def cpu_dp(number): - result = 0 - for i in range(number): - if i not in squ.keys(): - squ[i] = i*i - result += squ[i] - print("number={}, result={}".format(number, result)) - + result = 0 + for i in range(number): + if i not in squ.keys(): + squ[i] = i * i + result += squ[i] + print("number={}, result={}".format(number, result)) + + def calculate_sums_dp(numbers): - for number in numbers: - cpu_dp(number) - + for number in numbers: + cpu_dp(number) + + def calcuter_dp(numbers): - start_time = time.perf_counter() - calculate_sums_dp(numbers) - end_time = time.perf_counter() - print("动态规划版本,耗时{}秒".format(end_time-start_time)) - + start_time = time.perf_counter() + calculate_sums_dp(numbers) + end_time = time.perf_counter() + print("动态规划版本,耗时{}秒".format(end_time - start_time)) + + # 思考题的multiprocessing版本 def calculate_sums_multiprocessing(numbers): - with multiprocessing.Pool() as pool: - pool.map(cpu_bound, numbers) - + with pathos.multiprocessing.Pool() as pool: + pool.map(cpu_bound, numbers) + + def calcuter_multiprocessing(numbers): - start_time = time.perf_counter() - calculate_sums_multiprocessing(numbers) - end_time = time.perf_counter() - print("multiprocessing版本,耗时{}秒".format(end_time-start_time)) + start_time = time.perf_counter() + calculate_sums_multiprocessing(numbers) + end_time = time.perf_counter() + print("multiprocessing版本,耗时{}秒".format(end_time - start_time)) if __name__ == "__main__": - sites = [ - 'http://www.dapenti.com/blog/readforwx.asp?name=xilei&id=143655', - 'http://www.dapenti.com/blog/readforwx.asp?name=xilei&id=143656', - 'http://www.dapenti.com/blog/readforwx.asp?name=xilei&id=143657', - 'http://www.dapenti.com/blog/readforwx.asp?name=xilei&id=143658', - 'http://www.dapenti.com/blog/readforwx.asp?name=xilei&id=143659', - 'http://www.dapenti.com/blog/readforwx.asp?name=xilei&id=143660', - 'http://www.dapenti.com/blog/readforwx.asp?name=xilei&id=143661', - 'http://www.dapenti.com/blog/readforwx.asp?name=xilei&id=143662' - ] - start_time = time.perf_counter() - asyncio.run(download_all(sites)) - end_time = time.perf_counter() - print("异步版下载了{}个网站,耗时{}".format(len(sites), end_time-start_time)) - - # 思考题 - numbers = [1000000+x for x in range(10)] - calcuter_normal(numbers) - calcuter_future(numbers) - calcuter_dp(numbers) - calcuter_multiprocessing(numbers) - \ No newline at end of file + sites = [ + 'http://www.dapenti.com/blog/readforwx.asp?name=xilei&id=143655', + 'http://www.dapenti.com/blog/readforwx.asp?name=xilei&id=143656', + 'http://www.dapenti.com/blog/readforwx.asp?name=xilei&id=143657', + 'http://www.dapenti.com/blog/readforwx.asp?name=xilei&id=143658', + 'http://www.dapenti.com/blog/readforwx.asp?name=xilei&id=143659', + 'http://www.dapenti.com/blog/readforwx.asp?name=xilei&id=143660', + 'http://www.dapenti.com/blog/readforwx.asp?name=xilei&id=143661', + 'http://www.dapenti.com/blog/readforwx.asp?name=xilei&id=143662' + ] + # start_time = time.perf_counter() + # asyncio.run(download_all(sites)) + # end_time = time.perf_counter() + # print("异步版下载了{}个网站,耗时{}".format(len(sites), end_time - start_time)) + + # 思考题 + numbers = [1000000 + x for x in range(10)] + calcuter_normal(numbers) + calcuter_future(numbers) + calcuter_dp(numbers) + calcuter_multiprocessing(numbers) From 9ad96797b3438be3db41c2e2a61f59bf034d4958 Mon Sep 17 00:00:00 2001 From: liyongbin Date: Wed, 3 Aug 2022 11:05:26 +0800 Subject: [PATCH 20/32] =?UTF-8?q?=E6=A0=BC=E5=BC=8F=E5=8C=96=E4=BB=A3?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- 23/gil.py | 98 +++++++++++++++++++++++++++++++------------------------ 1 file changed, 55 insertions(+), 43 deletions(-) diff --git a/23/gil.py b/23/gil.py index 46867b6c..69c2fa28 100644 --- a/23/gil.py +++ b/23/gil.py @@ -10,49 +10,61 @@ # 单线程版 def CountDown(n): - while n> 0: - n -= 1 - + while n> 0: + n -= 1 if __name__ == "__main__": - n = 3000000 - start_time = time.perf_counter() - CountDown(n) - end_time = time.perf_counter() - print("n = {},单线程版耗时{}".format(n, end_time-start_time)) - # 多线程版 - start_time = time.perf_counter() - t1 = Thread(target = CountDown, args = [n//2]) - t2 = Thread(target = CountDown, args = [n//2]) - t1.start() - t2.start() - t1.join() - t2.join() - end_time = time.perf_counter() - print("n = {},多线程版耗时{}".format(n, end_time-start_time)) - - # 对象引用计数 - for k in range(100): - a = [] - b = a - print(sys.getrefcount(a)) - - # 线程安全 - n = 0 - def foo(): - global n - n += 1 - - threads = [] - for i in range(100): - t = threading.Thread(target = foo) - threads.append(t) - - for t in threads: - t.start() - - for t in threads: - t.join() - - print(n) \ No newline at end of file + n = 100000000 + + # start_time = time.perf_counter() + # CountDown(n) + # end_time = time.perf_counter() + # print("n = {},单线程版耗时{}".format(n, end_time - start_time)) + + # 多线程版 + # start_time = time.perf_counter() + # t1 = Thread(target=CountDown, args=[n // 4]) + # t2 = Thread(target=CountDown, args=[n // 4]) + # t3 = Thread(target=CountDown, args=[n // 4]) + # t4 = Thread(target=CountDown, args=[n // 4]) + # t1.start() + # t2.start() + # t3.start() + # t4.start() + # t1.join() + # t2.join() + # t3.join() + # t4.join() + # end_time = time.perf_counter() + # print("n = {},多线程版耗时{}".format(n, end_time - start_time)) + + # 对象引用计数 + # a = [] + # b = a + # print(sys.getrefcount(a)) + + # 线程安全 + n = 0 + + lock = threading.Lock() + + + def foo(): + global n + with lock: + n += 1 + + + threads = [] + for i in range(100): + t = threading.Thread(target=foo) + threads.append(t) + + for t in threads: + t.start() + + for t in threads: + t.join() + + print(n) From 131efeb2283e826ce0b0979d93dda664350e0747 Mon Sep 17 00:00:00 2001 From: liyongbin Date: Wed, 3 Aug 2022 16:15:54 +0800 Subject: [PATCH 21/32] =?UTF-8?q?=E6=A0=BC=E5=BC=8F=E5=8C=96=E4=BB=A3?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- 24/garbage.py | 144 ++++++++++++++++++++++++++------------------------ 1 file changed, 75 insertions(+), 69 deletions(-) diff --git a/24/garbage.py b/24/garbage.py index 1e81eba2..215f415c 100644 --- a/24/garbage.py +++ b/24/garbage.py @@ -9,84 +9,90 @@ import objgraph +# 显示当前 python 程序占用的内存大小 def show_memory_info(hint): - pid = os.getpid() - p = psutil.Process(pid) - - info = p.memory_full_info() - memory = info.uss / 1024. / 1024 - print("{}内存使用了{}MB".format(hint, memory)) - - + pid = os.getpid() + p = psutil.Process(pid) + + info = p.memory_full_info() + memory = info.uss / 1024. / 1024 + print("{},内存使用了{}MB".format(hint, memory)) + + def func(): - show_memory_info("局部变量初始化") - a = [i for i in range(10000000)] - show_memory_info("局部变量创建后") - return a - + show_memory_info("局部变量初始化") + a = [i for i in range(10000000)] + show_memory_info("局部变量创建后") + return a + def func2(): - show_memory_info("全局变量初始化") - global a - a = [i for i in range(10000000)] - show_memory_info("全局变量创建后") - - + show_memory_info("全局变量初始化") + global a + a = [i for i in range(10000000)] + show_memory_info("全局变量创建后") + + # python内部引用计数 def jishu(): - a = [] - print(sys.getrefcount(a)) - - def func(a): - print(sys.getrefcount(a)) - - func(a) - print(sys.getrefcount(a)) + a = [] + # 两次引用,一次来自 a,一次来自 getrefcount + print(sys.getrefcount(a)) + + def func(a): + # 四次引用,a,python 的函数调用栈,函数参数,和 getrefcount + print(sys.getrefcount(a)) + + func(a) + # 两次引用,一次来自 a,一次来自 getrefcount,函数 func 调用已经不存在 + print(sys.getrefcount(a)) # 循环引用 def func3(): - show_memory_info("循环引用初始化") - a = [i for i in range(10000000)] - b = [i for i in range(10000000)] - show_memory_info("ab创建完成") - a.append(b) - b.append(a) - + show_memory_info("循环引用初始化") + a = [i for i in range(10000000)] + b = [i for i in range(10000000)] + show_memory_info("ab创建完成") + a.append(b) + b.append(a) + if __name__ == "__main__": - func() - show_memory_info("局部变量完成后") - func2() - show_memory_info("全局变量完成后") - l = func() - show_memory_info("列表变量完成后") - jishu() - - print("手动回收垃圾") - show_memory_info("初始化前") - a = [i for i in range(10000000)] - show_memory_info("初始化后") - - del a - gc.collect() - - show_memory_info("完成") - # print(a) - - # 循环引用 - func3() - show_memory_info("循环引用完成") - gc.collect() - show_memory_info("手动垃圾回收完成") - - # objgraph - a = [1,2,3] - b = [4,5,6] - - a.append(b) - b.append(a) - - objgraph.show_refs([a], filename = "objref.png") - objgraph.show_backrefs([a], filename = "backref.png" ) - \ No newline at end of file + # func() + # show_memory_info("局部变量完成后") + + # func2() + # show_memory_info("全局变量完成后") + + # l = func() + # show_memory_info("列表变量完成后") + + # jishu() + + # print("手动回收垃圾>>>>") + # show_memory_info("初始化前") + # a = [i for i in range(10000000)] + # show_memory_info("初始化后") + # + # del a + # gc.collect() + # + # show_memory_info("完成") + # print(a) + + # 循环引用 + # func3() + # show_memory_info("循环引用完成") + # gc.collect() + # show_memory_info("手动垃圾回收完成") + + # objgraph + a = [1, 2, 3] + b = [4, 5, 6] + + a.append(b) + b.append(a) + + objgraph.show_refs([a], filename="objref.png") + objgraph.show_backrefs([a], filename="backref.png") From e72458cbfaa8d8d73c42cb66d2985fe9accfaafd Mon Sep 17 00:00:00 2001 From: liyongbin Date: Thu, 4 Aug 2022 10:19:25 +0800 Subject: [PATCH 22/32] =?UTF-8?q?=E6=A0=BC=E5=BC=8F=E5=8C=96=E4=BB=A3?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- 28/assert.py | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/28/assert.py b/28/assert.py index 1dc08c9d..419b45de 100644 --- a/28/assert.py +++ b/28/assert.py @@ -3,18 +3,24 @@ def apply_discount(price, discount): - updated_price = price * (1 - discount) - assert 0 <= updated_price <= price, "价格必须大于等于0小于原价" - return updated_price - - + updated_price = price * (1 - discount) + assert 0 <= updated_price <= price, "价格必须大于等于0小于原价" + return updated_price + + def calculate_average_price(total_sales, num_sales): - assert num_sales> 0, "销售数量必须大于0" - return total_sales / num_sales - + assert num_sales> 0, "销售数量必须大于0" + return total_sales / num_sales + if __name__ == "__main__": - print(apply_discount(100, 0.2)) - # print(apply_discount(100, 2)) - print(calculate_average_price(100, 2)) - print(calculate_average_price(100, -2)) \ No newline at end of file + if __debug__: + print("__debug__") + else: + print("__release__") + + print(apply_discount(100, 0.2)) + # print(apply_discount(100, 2)) + + print(calculate_average_price(100, 2)) + # print(calculate_average_price(100, -2)) From e16ae85dd0c85a4476ae2d53a652b0397a3640de Mon Sep 17 00:00:00 2001 From: liyongbin Date: Thu, 4 Aug 2022 11:18:55 +0800 Subject: [PATCH 23/32] =?UTF-8?q?=E8=A1=A5=E5=85=A8=E7=A4=BA=E4=BE=8B?= =?UTF-8?q?=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- 29/with.py | 87 +++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 63 insertions(+), 24 deletions(-) diff --git a/29/with.py b/29/with.py index 30e9aa0f..1b9206b8 100644 --- a/29/with.py +++ b/29/with.py @@ -1,32 +1,71 @@ # coding:utf-8 # 第29课 资源上下文,with +from contextlib import contextmanager + # 基于类的上下文管理器 class FileManager: - def __init__(self, name, mode): - print("__init__") - self.name = name - self.mode = mode - - def __enter__(self): - print("enter") - self.file = open(self.name, self.mode) - return self.file - - def __exit__(self, exc_type, exc_val, exc_tb): - print("exit") - if self.file: - self.file.close() - + def __init__(self, name, mode): + print("__init__") + self.name = name + self.mode = mode + + def __enter__(self): + print("__enter__") + self.file = open(self.name, self.mode) + return self.file + + def __exit__(self, exc_type, exc_val, exc_tb): + print("__exit__") + if self.file: + self.file.close() + + +class Foo: + def __init__(self): + print('__init__ called') + + def __enter__(self): + print('__enter__ called') + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + print('__exit__ called') + if exc_type: + print(f'exc_type: {exc_type}') + print(f'exc_value: {exc_val}') + print(f'exc_traceback: {exc_tb}') + print('exception handled') + return True + if __name__ == "__main__": - for i in range(10000): - with open("test.txt", "w") as f: - f.write("hello") - - with FileManager("text.txt", "w") as f: - print("准备写文件") - f.write("hello 2") - - \ No newline at end of file + # for x in range(10000000): + # f = open("test.txt", "w") + # f.write('hello') + + # for i in range(10000): + # with open("test.txt", "w") as f: + # f.write("hello") + + # with FileManager("text.txt", "w") as f: + # print("准备写文件") + # f.write("hello 2") + + # with Foo() as obj: + # raise Exception('exception raised').with_traceback(None) + + @contextmanager + def file_manager(name, mode): + try: + f = open(name, mode) + yield f + finally: + f.close() + + + with file_manager('test.txt', 'w') as f: + f.write('hello python') + + exit() From a17da9c1061fd111aeb98bfb580f316a3bc9cf6b Mon Sep 17 00:00:00 2001 From: liyongbin Date: Thu, 4 Aug 2022 16:14:10 +0800 Subject: [PATCH 24/32] =?UTF-8?q?=E6=A0=BC=E5=BC=8F=E5=8C=96=E4=BB=A3?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- 30/test.py | 93 +++++++++++++++++++++++++++--------------------------- 1 file changed, 47 insertions(+), 46 deletions(-) diff --git a/30/test.py b/30/test.py index 7229376d..e5a80faf 100644 --- a/30/test.py +++ b/30/test.py @@ -8,56 +8,57 @@ # 将要测试的排序函数 def sort(arr): - l = len(arr) - for i in range(0, l): - for j in range(i + 1, l): - if arr[i]>= arr[j]: - tmp = arr[i] - arr[i] = arr[j] - arr[j] = tmp - - -# 单元测试 + l = len(arr) + for i in range(0, l): + for j in range(i + 1, l): + if arr[i]>= arr[j]: + tmp = arr[i] + arr[i] = arr[j] + arr[j] = tmp + + +# 单元测试,编写子类继承 unittest.TestCase class TestSort(unittest.TestCase): - # 以test开头的函数会被测试 - def test_sort(self): - arr = [3, 4, 1, 5, 6] - sort(arr) - self.assertEqual(arr, [1, 3, 4, 5, 6]) - - + # 以test开头的函数会被测试 + def test_sort(self): + arr = [3, 4, 1, 5, 6] + sort(arr) + # assert 结果跟我们期待的一样 + self.assertEqual(arr, [1, 3, 4, 5, 6]) + + # mock class A(unittest.TestCase): - def m1(self): - val = self.m2() - self.m3(val) - - def m2(self): - pass - - def m3(self, val): - pass - - def test_m1(self): - a = A() - a.m2 = MagicMock(return_value = "custom_val") - a.m3 = MagicMock() - a.m1() - self.assertTrue(a.m2.called) - a.m3.assert_called_with("custom_val") - + def m1(self): + val = self.m2() + self.m3(val) + + def m2(self): + pass + + def m3(self, val): + pass + + def test_m1(self): + a = A() + a.m2 = MagicMock(return_value="custom_val") + a.m3 = MagicMock() + a.m1() + self.assertTrue(a.m2.called) # 验证 m2 被 call 过 + a.m3.assert_called_with("custom_val") # 验证 m3 被指定参数 call 过 + def side_effect(arg): - if arg < 0: - return 1 - else: - return 2 - + if arg < 0: + return 1 + else: + return 2 + if __name__ == "__main__": - unittest.main() - mock = MagicMock() - mock.side_effect = side_effect - print(mock(1)) - print(mock(-2)) - \ No newline at end of file + # unittest.main() + + mock = MagicMock() + mock.side_effect = side_effect + print(mock(1)) + print(mock(-2)) From 3e3d24b4cfb08694ff525a70d6cdd706bfb1dbd5 Mon Sep 17 00:00:00 2001 From: liyongbin Date: Thu, 4 Aug 2022 18:00:45 +0800 Subject: [PATCH 25/32] =?UTF-8?q?=E5=AE=8C=E5=96=84=E7=A4=BA=E4=BE=8B?= =?UTF-8?q?=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- 31/debug.py | 63 ++++++++++++++++++++++++++++------------------------- 1 file changed, 33 insertions(+), 30 deletions(-) diff --git a/31/debug.py b/31/debug.py index 229c9067..cf1c02ae 100644 --- a/31/debug.py +++ b/31/debug.py @@ -7,44 +7,47 @@ def func(): - print("进入func()") - + print("进入func()") + def memoize(f): - memo = {} - def helper(x): - if x not in memo: - memo[x] = f(x) - return memo[x] - return helper + memo = {} + + def helper(x): + if x not in memo: + memo[x] = f(x) + return memo[x] + + return helper @memoize def fib(n): - if n == 0: - return 0 - elif n == 1: - return 1 - else: - return fib(n - 1) + fib(n - 2) - + if n == 0: + return 0 + elif n == 1: + return 1 + else: + return fib(n - 1) + fib(n - 2) + def fib_seq(n): - res = [] - if n> 0: - res.extend(fib_seq(n - 1)) - res.append(fib(n)) - return res + res = [] + if n> 0: + res.extend(fib_seq(n - 1)) + res.append(fib(n)) + return res if __name__ == "__main__": - a = 1 - b = 2 - # pdb.set_trace() - func() - c = 3 - print(a + b + c) - - # res = fib_seq(30) - # print(res) - cProfile.run("fib_seq(30)") \ No newline at end of file + # a = 1 + # b = 2 + # pdb.set_trace() + # func() + # c = 3 + # print(a + b + c) + + # res = fib_seq(30) + # print(res) + + cProfile.runctx("fib_seq(30)", None, locals()) From 28b5bfb9d0f3152968b9ac720de84162c8eb1998 Mon Sep 17 00:00:00 2001 From: liyongbin Date: Fri, 5 Aug 2022 16:39:40 +0800 Subject: [PATCH 26/32] =?UTF-8?q?=E5=AE=8C=E5=96=84=E7=A4=BA=E4=BE=8B?= =?UTF-8?q?=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- 33/closeprice.png | Bin 60657 -> 69743 bytes 33/invest.py | 79 +++++++++++++++++++++++++--------------------- 2 files changed, 43 insertions(+), 36 deletions(-) diff --git a/33/closeprice.png b/33/closeprice.png index 25a7f2e9584bc92fb474085681950223f4ef2336..9098eae569c5383326c761fcf5b18e9a0cbe7158 100644 GIT binary patch literal 69743 zcmeFZWmwc*+dc}2AbkS@Iuf^1illU_2!f=7bSX%UbPbK5AT6MDNJxj|AU#qdB{lRg zICRa>(tC~iJo|n3hrPe=IM4GkOiSY-6*+{QfPjEXMft%K0)mS$ z0s^AOOC;c5o{o-P1Aj=mDeAjvJ6gGUK680Np#IFw>7}FFOWWuFdc1IPwRLn5<`?c^ z!~3s|o12rXlz@Q!e_z1w=wdCvBgUo(K7`Ck*}#>6faV$gH(`N%zAXVE0fEYcdpfYR z^%+lJqrIl{EqLuquc?YE?q{#>j{jqG>++*X7uzp05saG~jXz93!q-rykFuIhHk&@B zCZMR%DAUOymlXo?+?Oezzmk&?R_0@_(o9SqonBAR$+4YCu)pOk%SOXGJ{0TAY*e`` zm$`E``Ev_q3|52x2@JQ$Tw(e1OhBMUNht95S?4B^>EFi!E#k(%k6KxmU|_}gpO5!x zaYTO}@4mT#zWC=ckX*5q;?E-iLwF5ドル-$$Oy|L24Mk12jz;Qx5V|K|l+Qo|Am(pKoJ z9Uoxz{QR0;icP6usefDVu1Y(`nTm?ac60hmSQ{LhO%2}wv4fOjQLkP0#qnd)%lBxGe} zz507+nLP?}A5`4j{_|Rb|6kv0t;h8w7wQ+vr7uT5u=ds?Fz=MK_9p$a^G?hKdA}!9 z4X08PS4I+NJ$CUY(R-8YPZ+%0@wN*yiP}iym}x0=4LKkouwwli=?bn))ra=QK#ilX zPSX?Ir1={|KR%bK$r(6->6M#p!F);n_d5hOB`y8eX%yWLHwJ9YIb>X=i3x-w|DMSE zraAKm0TaGjq$EVPiL(tA+6T?P<{|s!1xdzyd7gikyte~8ci0)s@pu8f72z~+cbmfw zx$+{2>F(b1pg+O*y)3luw#t~9?>glM8Nqv|zhPd>S+^fLLaJf%uSz%vw-@0@$lvfD z7(T3zi5q?v&+#T_>niFRL6YQuwo=mZe1gzHyVOX-=LYg^dlLGhR@2}7aMxh%8rdhm z&CgTwuF~@!T+gY1#dL)K^JU0-l3?3R@zFZVs)_mPYYbFsIHCg6|9-AJ)x{yXgL>vT zj(m25!B%2{Kpv&Px8}RynAQ=oBX7nfHp*`slr4k8<#;*i=aemq-aa|tcs!y za4Ze&&cO{^0c-5|?;5X>DGterbHPl&*Ax6bx0{Yx%15uuXxF9U=^LNnS1t74Rp0L> z@Eko+8udy$LXm2`=lHT7rq51ifj6&9|L&g~N^qHulA8!clisX_mGyeHzTZC2Pv1xY zHu3v}Zn}07mrvP>x0$H8a1}#txPgs7|L?{x#m-^-p;ZkV>!Y=>O`-)89z6t@T(+fdWM4IwB#hW~M#0z*v) z?5LtV?Eb*UX6A|s;k$>tjCvdiNk95l(KW*e3J{m@7AZnf1)#E{j!AaQ*h*7>zwX;m zd8#C0P0a%r@;}Z?r!)_ec(@f}qP9h}aPvw(8?K(Yn+$aY7`KS&_u&N=joOgG&g*Ne zNe+nb${}oY;w-CFG#dCeq%b=+yk)SocT9KP|I{j4H9@g9{V{~^X)Ioe#tieP^0A}c zbDu{E%7TykQ>_FRy&!%_H}{WLS!7q?R4mK0x?v(yZ(w2WKMwJ#qGs`xVdbacIbgHRb zZDov1DLc+Z6Za^tC!6GuSZaNAc%v`YV6A)QjK z?GbEy*K36Whpk_~^2SSJl3AMkRfdpd&(gR1T9$`frnM?hA;rOCmr@KYsqi}}y&*pB z6s#e;r!zV>R@~HN!er)KVd}Rxnl{ymacSMoE<@-zmg zb!K^hi)me6`)a3lo|U3#;oP*=wdOX#*U=&%Es~NlIWs8CtKxmK=g@%}QZT9?k-f~@ z-5dojl(8ir9Y%=qqZBPpH})Z$pH8Bg|CbEkZIx1!2HiO)eI{e4S10}f$lBS{P0trq z6P5fiN;&nouM`aAZ>k+;q-H#(?CbZCM(%y0V@Xr%;}r@_rm(Dj*#qMy@S^(d@3;51 zVgig0W&>Kj^f~(PTr&YCIGP-5&Rfdx(J0m}9ZC^}j*O0qiq6V_ExfzV8lycJSi}_3 z!4T0=0}psGfR)P)HQPW2a@O@(rfDZ)$oejqTqs(9J+-?&u5^X!e8S267pBfpZ?m+RagZ6d0h#qex@3&5u zo^mlptg(p8W_hA^$Exe!_?_k$Op$zj+_aO|Vt3ebVqkVOqZ*g1kbs)mkPQnIw<(l8 z?72;xWWA7T6GIEnI>XkGHl4auk00)Y16T(((b@(T`9!uvXjs^7ドル#GL5qJ= zwDhKgyPI25VyQuuQgCqaWVLPlm%hWL%uJ64Q7F>p^k^?GSL~JY{ z%95NU7{bh~c1#0SofO&IZvtf#(4z7_x!2U_sCDgt%0~12t=D0*!UQ3f!iRdLLpJns zSjS$#0QTukI_;JhL7w=K`;Uff88tP@qsmMx((+=b)}G69fp&_mMxUNnB$YL8KT3B; z8W?_Y`Q&x@RL|s-@PLdq)^|zT6;)PMCCoSd!O|Z!F=618ia^|pijFSI&wn^*BeEpc z>1SC#7jkW6eB9;?ZjK1r@@oT&yc^ORm^j?<*gnyi*~=mu&rhsb(e)a&y^tk|cj(2z zrQe_3e5aq-A%%dZ1-NGJ7ドルGr^ylS=>7HM7*jQ{bs13N*60 zSpS0tVYr2t@4{`fL15{_*$i19EAK?`g<5k#28050{}lb&3d)te6zvx3yd*tggad7{ z(jGZX%D)psNYgzcZa|Wu6xipg`%z(c?ptVPC?bvb+ZQ}#8uSLQklGRGuo0nneml`- z0~XmvBVTg3vy_@BzxzXBverH|B_(CvO=`hSicuH9l4rY&2r^ITh@H<ODnG}bFkd*3Ie4SoIkg};YU~8abc6+O%aUAfe(s)fd zN&8fN-aJw^-yhR>Od`oH%f@@jVex^0Q=Mumnnjt$$#Xy2M(|xdDhVx%UG`8JYAg3h zuAz(*p$oss8e=URP0a~qKC7>JnD4(MaD*Gk7*ZK4A3ZObHQ4os+;g#ry1le_n(*TQCll%(LD6y*9QpEwhuKOnaUI2cF)` zS)p!SES}35KAS?=oz>!UWS88&P`bW%mu?&D#OjI91kaIf=#t zPTRQbHz3HOi|XE8MPFPNSH?bA>fWpVH7!{luyEIo)}di0;lQ5&?bJc+$Z!Z;z2wy^ z!gSYu$rNGh=)+kMn^Uu~ZF5e`z$Fvf=67VpKz_eU4H03<35%=#cnrc;9%m{ z27iuEi*;A<5p=+rul_!g&gsqdy(pu%ua8|s3bbqxa4netxcg?p8s*o>?5kC$TV^9w z5vgT=V#`34V=(5)r7-IxVx{pbqx!Rf=V@EAB-T;navwR&(E#2?=_x!@>z`QGj8XLS z*?>xx$AJC=Df@DtaESsYAmbl>xOK)nOG?1JnFNA8{Vn)_d#Yjt;|j_L%Li)!n|e4P zf9{L0FRS}WZsOd*fI)GCHGI4rNsaQ3(Qb-;mt9c@ele>NWIxis%^2C$MyhG_kuP1r z+yCIYJ2e*(s{d8s^R4z9)>W&rc8<7ji(;mg+pwx275-55iud1giua(q$&tn<9#`y% zC9VFR@>2}g4pjL`@j$5ドル!y3xDL~6C^sKd;2KJ5WXU>gf4-oQ^9ドルIefvOyL(MJBXJ0 z3Bm^X!W9B_v-D}=STVoDy8(H0($a)rPvvtan7{2PuaxOM{N!{uC%>R`;T=cRd=i?a z_Vu^J_4O@l6OY-RblbTy4$)&5Ph2h1%WWk&f|HXA#(ig+nBYSs*>6ni>8&Jo{Kekp zYU4pWEz!IPEtiYYW{EOY87gb0eUl^PPQxkXG`0GKzS=ZV!OUw)C@9ドル!t{wbJxib5D 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ドルttwzg(wcqh)xd_il@<8k=^mbos9p~70sirwed#=@dqon%fuhpk) zF>b`X(AMVLpsj4WhS|^!6>sdV9aqy#F@(`e#bD4C5WFu>_Cw;y)j+asPVUw1t5o@( zFG6yJs4mjYsaX}oLxT=l@7_IdkomY1v+{1rz`sf;%Z3LX)rF~?c*KO9!&zzI5(LpT z`?!8f#bA!plOgSzCLjH-K4`C(U&D*on&dZa(7Wu`>v znBG;0SrT86T^@8uM%OTTndkOX{yEiWzd4Azire*1=o?SZ-c9l~rNoIq9??9nC!xo` z@m3^M*L4{#MG%L8Nr4lO<59q*q*%r4q8ltlur90~ha!pl!q6_l3*1u!8&ngah` z`bR7{-(%2P{ENhnH@~*a1Xi~F#@YkJFW0J-?jI=_j9F{oJg7K{P{zY=UwD<4nxo9pkpvydutuyrhvwdz4bwmv=u7o*t4n(e?pu4d6rpfha!j*y3tfat z-vH*(iX}?Ws`8YOM)y%Zs}G@@vnj(B{tz*}y3nwy$xoMf>q=fWVTUjmO!dpR6!#!F z9T7_2WQzpUVl@S$dZ1BAG@))#AJjKic%^KpNJ-Eu^0ztQfEG-w(O8*e8VouK8VtTG zc7(svzslKo%06+g_5rCtXP3TZ-!lP|*0t(sy)Swq%30ドル|LcI4KkJI%J0q(6a*x^cG&D;xk0#w$<amO8Aak>72Fz7{^gs%YS%@vwK}-$GLvU_=>d3{+p340`kiou+2fiyY539+@2Uf3QtV z93?K7AGiS}ZoCqzT(V2w2+A>jm7)XI=7<Qo-{ zio4Dnc_yNJ+#NQz*CWQe>{Y| zqMPn!J7%j|OPD#TIDkXeO;X~@H!pd0FGd4l*c~od&)hD?NhI%JrV&+=oHU~q@8KtS zU-3dXQ}k5^s!yrtQW7drH5`OBplRylS6Hd+D)YBfUkY>Yzo&Rf z4ni7SYAmrd46{||zyHfawx$XGzCm=uWB-@?lIwBr0LIzYy1b>8k3dZ{qwlx1h%KJo zRGFLo5ドル->VBNT>q!<*c*h=rtc)trkny|^ex)9b=i zQ8_LChkf1$Dm$Ey2Lrc+SXImDJaD=fa)Lqn26fHkAVxq6=jS#15nrnxeLuHPtMp5w z)|=f50`=agK41ドル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ドル-a3p1Iq6VKAV!K-o&gT=aLKh4z_v%>)qLdw_FI91Q(QG3^0 zvF%p|6H&q$%9#G!VedS#qt(ZT{LDb6(a=Bj*^7bY!t|MU){uY`;Z)V9iMr1GTqkUZi>vX1wbaBT$dLqm~^|bHkc-%hu(nkLv*6wZl zGCG~7#=)8g-ADV*Lj=|vtaYa4ePvSepLct=p5KTs7}TFGLBH#iM@ZXEaF@F>6FRy~ zp(%+Qry7Qfc|8t55qtzNHxS`Ju7i%ub^EEv`?_FpCXCyDw~ol`>ry{4t0YWs2xsS3#v#6ijM4oy4qY zbzUXGY@kIM^>-vTDr0iKww2gi(lk?4$dm_=?yidc3Xo^=y(}EvY^H6h_B5Z|C%nn@ zr%Jo&qomlD*2qAm4FqFOoC@Z>+#t_BDl=L7&{#P3;!sfRF4^Q3DwOs|bbiLY zl;rRb`4-<+n>9!ewgDsl{FywzP^FqsR#Tn7D8>suNW_}C3#1~64E4kl2bu03F56L8 zW3=EfmUvmAf%D(V=&N5(u0Y>I%1tUFF z)?+mI&iHefBC<%ky?9{ix7J>-Ay?}g z#8u-MYkzTYew{FGnt4MEl(5d8IIaL|E;&T%uOsZymYMsi_<8mls2>CZ40+jw{c zfQ{yCWNv?EQC-}}Y@*fW9vaZ3FtCpzu08#ic@$t2oGJtB>thN1*JnT0?K9h~O#|CqLvKQ4?g z+TCqQ%ilkmc9(0N3;!cZc+q1ドルbBT8S#8htHm8kBzC60ドルNEbQ?GHiCH=K=Ghlw|#fE zJM3$$x*W^p3lNq!tLkcWRi}KV+?}gryrhJpOn;gN2x5bbBzzf;lCRB~{>v;%Lm&gR zFkXICgxL2*H7Cbnuwj}XS4gwz2tnM;s_D>W$;;DFl>1K*w7(u}-8`9h*2%du|CM}aP^7OTHM;*W1`m*3L z4hU0HQc1oqlb2J_qSTjn4Y+tfC!qQ$?TP%;7zU0J!+x%UflDxG{2mp1mW^(oQt?2k zcR4LCTHv3)F+_udQOwy2TmXO%!S!p0eqN4_%&#GizcN?RQ7I7}3s1fW!mV#^xmz(v zG+ZcP{9E1_N?g^yOb(;idzbLYmIp#Nrxeu_D>gW$yYx6hfc6Oo7YqQ>7E!7ip(bKR zXaDwFRoQyDgu~+l;j5RAX2ieBLR*i&W+=qvPTop!d-$mI1$k{2*Xit(#IU)<6_wp= z4|EjVkXJa^g~yIWS1`! zC5( zeB060ドルo`AzdsxO4m@NRvQ91LKJMa|<`c?-!$hlmro9f;cm5jpnfioso?7ya1>!HDz zWB=`|SEy1+#Urr{1y;Q;~t40V zJP_W~1Oo=DnnE&D%=?Yr;rgBAnWUug>43LnC_9T%K}oO25gMi=&-d3M`E3VBRnm{i zVEGyDlkz)D4yAnlk{{tbw`H!BToUSPP$-2H0J@O<} z(Gk)4aLu9aUVesm(dydTC<;{;a<(q}-h5;?dvblol3)fd;?c&ehz+vdzoDiTzf0K=a{t8iwF4Ej0x3EFe12vyqUs({XR8{eC!z zUIt^JJv;+ys+@_Mi+l71HsdJSl430EaooJ~WS`dFgvn`KZjLqgS0o5-ORtJR_ClS& z0Db^kWs7>`WE50QkY?#piP@y|$Mhz4AmnCsCGz6!)bUP@31kAgs^afCF>*zCIjg4J zVJ=>v=Oj@=!C!aRKVVfS3MeZL;L|PU38@m@{2pZb98L7Hx<;u}oje+{ub9-zc8ul`ho1ivvnn7j%30n+Z6_`tp!?kCfJ* z`6fLxM39+kET`L*y0bPp^%?Ch-JVTyPv81=u%vIPUP;o}p@}=|F%d{*^D!>Gg|_zC zR{_$DY;{0HZNZ9u{+qc4N`+b)rR-G|51e6x^BRR3&X6YMDn)+OC;2a0##UVdTBR_h za=d!nY;p;X@-Cj7nyPu$Mw0HgKM|L!>AxMnxCgZNNb%+^s#36ドルN79~~Bb6_Dc3Usd z*z7F!9rc))EVGu^c=QQ)Paqr)wVHtg2S5*<=+j|(^swn9#_rk_^)-QkC&abLr1UAQ^owRW*yMec!eD7k zGJ0kvF$Lu*4Z5q&o&Luxdv@gq%Ycd{B@IrAFY)O9)G6BRb+>#1aZOkn23oj9@{;4z zLTrWgO&zC}CvcV5-}vMh^)G-#Q#|=sw9dJ-2Q;sEhfLr#$OfZ%%5lhYvvN$dv<3?c z%c2JqSu%)5e-yKyv~+LvK6p$UdAK9yl$+&UL*-otJVHr8Sg`vy05}R+-WY8|j ztgxXX^|MIt)9b>yEsNO}?mxd;sl1Vf?nqYV^q-8gg{RzW8Ek2=Owin*3bQ@$eSK<6obys~rejl3tz z4!+Q{B!H;rA9|bR|W#ks=zhA4VT`^otT(NX54&qM;^Pr&Rf?9 zOr!Mh;pp&ij*Uo)>ottrof)S#D!Z32Mb3qZ@F9%(Eb`Io`wUdW2wAY4*)yAT)OLDT zwKMka2;iHe@f)qXUf8xw_lO!lWV&US6YSnxmTa&E2 zq;hi1cIC3n0N%3A3F-`Ef5Cn+NH zf+rBE`Pt8)W1l%zGNMR__y-v37Rfau@lE`An#S$gV7<8m=yphg24z}m8l|mg{)%st zpzl7c2iBRz9PJiR()!nn;>WlQ0*~P^2%_uG^qu z-8r}2ドルWix(`88d*lpMQT=m16gbYG1C(l92*Q}<8niwa1vmw72f?z1!^$zwn#>R(ZP z$$S0Eu2a~Xq5fz1Z8s28)+)MKhffZ7s>;OkEE5IH%7ptR6#Dx5>etE|b`doGT2gwtpxIu$QDFgzke%?XunhItF1ULNgi4MF(H4emA4Y|YlYI`ta!s?5= z=ZgQo3|ikoX$zC_tz?1Oa*bO03*9^OfZl&Ph;|!y?_Kxa+1dBjoV+#Fv1um4s7aCL z48tqXkeaMhWwZ9UMQlCLWiIvyD2KTI<{w;lr2i=2(m+q5gy|o=1*-h2{nGl9)4R9W+MrFM!Pv|r^=qw&Y@-Xt30k|WuU$~-OZODwTI8->6v;d zW*+uh&mSDSgP|t%b9qW&(eEkFMs-Ndsj0Wk^+C{pb$cCfNQ82YN%=IKQ2lMqCD$Ya8qPf21(5El zCB89?nT+1pNgui2X?jz#S=YJvKZ(=;u{-y7D_T1my z({l}sLp-*&&exEbBL*MkXFySoXDb$OJP~HbK^Ul%U`LpTwJLG^M|1SHQ!hv^cj$^J z4>|Af5<1>Vx}|H?sv79|>ki{99ドル{fumNM_gl4JTmW~kT{ayXJe5Mjic;^%NWbIm2w zq@k`aAB>k)S1kvMZy8&hyaeKl-{d%8YIi%?r#L<_i`pqywr2=*!zkwp`rn0f%ll8` z@JBA!qLX*>FVEy4&q_JKq>1La2ln>S5}YmmVC^-KU~w`*0mY*HFL2K-*xYdSVt;zE z54Op(_t>D?W-w_AZ&G~Vrf_h@j|PR`F1xa`1~&PzKAEKv>@tOcYOsz(sm%-CLkYVe z$ptGNeyMw#j}Ut+!l<(*07zutqzl7-f;m>Ye15jF5(>vRHzpXkklonf#9aRv2ycGh zSl!zT-_r-%8mivDLH^vTn?OMQ{I}|vr2-V~-hIFH6x1FxlvfC`?c6YFNaN&C^0KeP z_e(Ips{>xA?AQ5TmOhnX6<2uhcxg}ysp+1hz^0yeu;ox?t;pz3dNROf}S0TfKjZb@Xw0j`diNL)D6nDx0Ga5H3tpHw4A%_Zwa+*5b&!%aKy^ znVzSuj5)m|nSOKj933_Z%6oCP^wnpj)ecUxGk<_?gb zaG6sz#9iOcwk(jLt>T+=S|o~19uVyuambXnMB>ui9~6U8CcRi8sdk#E+`QK;`^l#b z1n()p^fU0F_cdC{PeopXK{pq3L99nu>kWp_R{WJ7mJgfKyqcNv+scm1WQ-1cH>tef zx7sDt2Ylf%}_fZysM~{emBZH0(5%r2tP3BvvIWQr4X*86KKlY`xd7&o zz!(mGY3ドルfSOy#V@zL|{8I4*Z0s_^3E_Qeh*@$$Xikn@90jN8cxmsc7<9?o5p21shv z!ioMf;PO2MsQ9X60A*vV=eS?A? z-Q^Wm>E6s!w2l63-4eqLyij{+e)TGct%t%Y`e}2=z#S+~SVTHSU^7cq!>Epe%e9wV#Ih}0dS1_R%n#8s; z)dAxY&kVsanJcfhJ?6Gt0jD!~*yXmI$uIGwYvH^r!1>*2IjN(aajZnJZcOeOAIu zQ`ab8yAP<6_o3-pm4~i+ub@kv7f>{7me`4h{X?AAypI}CsWQqR4D2mL>C}wHkq6{D z^sH3BeZ#OQ*wgAEH3-tv)IR+y`s`PP7{wtK#I2o$-n29EfeKJjkwb!N`jG3{11rI9lGRyTq_&9kybk_N0p(mO4GSEbM zADQ)QHmtujaQB{^%~zK!dhWYfVUZC6$p_JRm5r=t;d8R6gf+{cSfQVN8H*1s0J^bX z6V~e?A(JLC=qvw&B{AS?@_sJ($mb6`gYxG#0QKJRKq67N=U-obG{o894XcIx{wY}R&i(2KxX(b5IHM5OF%r2?!tOLaxG@G! z-fi_KV^uby{{D%MP@Y6EJ{!6ドル#8unfyAdeFXqdVw*JD4^w%FBl_U_PGh7}CeIK*?v ze*QANzVzU$`MHfeoMO&z`p>(I9;i==LWPD_J7g#-tkoT@&CUqSyzb6mQ`j(R>qZNhLF9=CVL%Q z6h$axZ`q`>!m%PVWS%0&CJv4*`};g~eZIfn?RWeA^}XG`fAr7Q)j99;e!t$Y*K<4` z_vhCm9(LSSa%UM!B82xIs%tNAKeCwKPk}Cntc-1OI8!~XJm;YZ z-AGS@@I6I)hh{KlbX7^jC)xW~32#)`7rN%{B-2-PGEq@) zrB;+sQha%u;bnBt68vT8d&lE4SN&H|ds$H!Jpzjgf61ドルZO}Io$Zv%6hHUxF_Ux z0k6~RcvkUS$h;nk>-i9{S8)BdiE-9rr$>QUb)d2A>%MTDPjQwJglx6qTdQ5y~pU!8G`c2npVul1ZwD-z`BmjU`J>+t+!bin3O zWq%pDlDen^DfVb#C158hvh;k?)ID7hI^i-39rBYu)Gas;MC>Jq^uenWWm?v-{P=i$ zlC5AbE&hy}z46kM)+t+Y(H2ZfG8x*&dv~e5s4ドルl1?cMF4?s+HG5JPj^!V`y&?Kv!v zothYtirw7^4?Kv6?Y^WTA-JYLvmr^pYHVCKN6~ckrmXXN-S(FnHmg9i)6&!SX~#fq zU%|Rzp(pkBfkR0(L12-n+B!8UoL_xW9A5J-&1CSH=k1ThQQ4+rTNA&5v!bJ)KE4yk z4@K!)mL*V`Hn-*C4r#d`V9i?y(VBu5r}+ue?Nd(1YoKB^hVHm{HRDNHp+H|w1#p^d58Sq?eR$}v_k zcK_4M1SQH| zFq{uH++95{YT})0tn|AYmZ5j%o0IF$cO`!a)rm19Uj#6JJy)&XjykPWt|^<$ooyHm;fjag0{&4sxaABTpHvdCD}O+ueM*!4~>26Bav^%97oPJpOT;ca(omU%Wkq)(CbsfNM#+=o47~$ z_Su8utB}nIo$|S*qobESM$b9MzRj!1cm7aWr1kTP!;4V)WE(Xg?6m%293ドルy|7wR>j zVnymEYkS1%0F7o1b8%#4?$P6++D^lMmtwb;GkNYK92YK6n@8J?X^AHl99(9Jnos*S zKDvr(wzW4qD6$m<=no;9gabq<;pt(i(a=!sicR9+~9 z;2YH6-QxWjMcVW;{Na2yS(4g9dLP6<<3&my)w*fbg9ksifm8-ret$qvkv*ifnc$&4 zpt)z>qq3aXb9o~7gGrrsJ>OE-@SCjRgiL^XPRDLpN1twFe_216)7+W1yiN7ax`&9x z*5_QNkLAS+tjZ!_ut)Tv=bCjb*rV(qJ^a($#F^>E*|%}s(-BAD*) z`s8#RL0!~@%_zUT{MwSvgTk)_Xd?lsdw|~QuV41R2X%K5MkTdHB4>U_8I4gZFd_W) zYlr4e#}adbmwRFEBRkGq=efIS;M=-+NAH*UN2jbl%y4SV+PBmg^&`KG_gTt-o|4RI zP?z<2tu&7h20`om`^5h3ua4}tod5uzy*|i>sp|VAiUab!bLZMA5OJj8E7il50(gI= z>}pzn2|#^69~wL@rR#PK@z!lnbcjw*ui?+p;sXauGxk1&B$As|{<) z3PcWH&o1uFpyd}j?ur{=@15=${#+PZwkoq%;Xk2|Q4QrV#d}A?i0>TLlWnoUp5F;Z z_&&8Y8bOYigb!;a>+*)ToBCIIOUBsD^WSuo{5fdO^RT4WX@6;beaB_ROQ)TA|Et1i z>8&9_#{|apz}1}do3*S5-&q7qJnp%^i!2ドルaDQSu{!O4?}8PmNMjlXh@QbNF%J#=gxiMKPTAAV7 z3D8q%b}*s%`s?)@nh{Fvb8oG?R~j8RYCDkkN2CWH*M>c1$-t3mmFjRE7TT2F}FD3vLMcw+>Sjf`oG;EkS*BVQ^zu7~#} zO7tD_U-#@6!wO|Dg1x;3z-#hH5GnrEhtKBUwwXaCZdVrz@Cmz5SMB=7fW1TV>LF=- zNw?n{BKWmgrB8XYoj$b%AalEdy(JGERLdG<9&`!t6j>WK=8H}_?)aR2oIJRwVb=DT zyH@4+4l(a>ea4&!dd5;e^&0pUVy!|FgY`ml)z)()E!n<~4~bn@us?bi6ドルp_c$q=&m zq0n?h+`-+~Bs$EnQ(+slB_5fe&z?odx-1hwb+&G%S1~EEH#I+auQS@W0=4^lKT2Vu zUB9`vZ}lw8(&~aT(X&dB9g1eo9Zqcb>u1z`D%H z4TBN7-~o9`zF$r7#}2bUaY^$q-xJE7bCIRlhQEdK526(4{b0p9J~+CCNB8-!+e?{DK|8%9n?_IM!cbPm zSqzYmR_u%K?evX)DiFv5S2w&2?|x|~fkd`A@tsL7$k5r90{{l#bYK5n=8v3PPh3r& zM39vCJRDW?pd#U{(sxnI%mAd1ZN>J+Qrv{Dw+gVFz9 zBd++HEvlsDEe{QlrK0^;Ear3iW_2nA&atsE*)$)9Gwd!^I-e*od+jKz+*|kMrA4Z4 zLJvFo+~}|m&E5^B2>PFEE*(Sy#a-d*W`)!ljhb4;IoVu)(08wx{|fPyle?+Va>US| z3wYRj-*@PB#db99Lf`hgmt9HXYZq-uv9c!z#6Zs5*_Senk9Q6c^KPz3HZ)EJpgz$) z$#3c=G@YFsHuS@1>3gX_kMkNwgxSBd3ドルHv0ei2qpu<{2iwm0m;rorb0ild>V+PS); zpULRR84;BF;}Dej)AzNXJKLEO4gH;V_On@YDOEtmSS4^FIv z4Pm2{`$OM!7hm`4ioX}Bi&QN34-umWdWB~)${64u_lLFs7(j18*xvm2uE)`&bVr0e z2S69-GCAzjNRWiNUo?6QTLWT*H+Bc1X;n!3`*TX}_P|Kw*M6Ik7}nuonspU;0%K%| z`y61hu@-}~mf?7vY-=+?2rEqbKkhkYGR0X5S^j6kL<0c&of6nopnofxi$--=fOamVtuW%m}9xLa`W`b2--ESOPv-fu7wDQD#i_i1!i8ドルzb(O+O{CB28r@!^4Bszyeqs?Hk&sQ9V@~qPyE=I_L95 zn+!+wmY$D-ywIf8Yf+sy68B8EX2n=X)TIeE@nZfk5#wl7$br0ArXxC=HZ}3Sq-kmg zFcBTCieN~+Mx%PG9T=wfO382VcMo)sUwRgSLmc77Ju0(3v+!#w3QXmvL1lV?nUp>L zb#1oJ`RZf#sdQiKW+Tg_Wmo!>TJ4lPhD%1u00)UYI)=fKhZ=R-#g!#+qNytfZJJSXuv5e2=!3DI{1 zoWOQoEvYfOlUHa9@w%=zsq<5i|7rezdz+~e&djrj+yp#?;9ドルn$zc>o3;mi`z_VvZU z?J&X@->M+$Doac+ezV0i_)fq|LB7g+y?q80LBU6VfdXm~p0BGk*`+Q}bZDWlEYP?W zXA zst2jZ@KO{+5cz zZ}+Z=wir#Ervxn+kh1?uo0H-z6-KW}5SaE5VG#tT77@eNJohqr&Q^>&F3@51amv51 z#&{s&nqZX8g)oJ)K|@D`{X*P|Iv7snsE{hSq*n0SXwif*^a;t(CJk2=CY>+Ee-bDk zy3AaVuMzo(!ElRIo3^UavN^R#gcbSXkd0E|d`!#)x&NmK&L(du*d{NL{prLV9ZjLQ+XUe*gE=;6-4Lu+}iJCaHVEB1%>Jl2fB+4@6atFTbq3+i&Fzm}$x! z@eCS^ue!%ekJ_rIR#q7iwltI|&t^Z=b|BkREcujw8AMR6QUrM1sz*M5*Nz>&GrQE& z3iWlBFQlG#sY}7kW@n-H)k#K>TNzi$G8jKvpDx3>=MvdRe1-_onZ|A(qf=3Z*v8Yg z*p--`hl%5_5#xqs(x9X^zAN=C?kNM_EBUq7GTi5DzSV3SYe@w+5}Z{I_yVn(m+lk3 za#o?s(~BEO9iMTxaok1ドルDo?&>bMknUMal?*aFf9R@T<@&QTv}uPgai>QY!oFNeJySH57lN?_=Mvdr}d#f8SiE zt75tI5ElL`@*p*|SLBf_%f#8l^};GZ&a9C~2x_8$qlxf-?el8|iUR&9fCPXyKR-hqk1_%pW$B5j``39&=~j<*b+myo9ドルyxk;wifih{x%6kp=t}`>p;=B5K5B;2&W3;t7)c zUDm{arjkCeui5v3Om8Bg83j^en+HO7K%f^%fdH#WwIm3rdshXea{oS(Yw&!+bln0ko4J&vi|b%)bMSrq@%DCTe6|ueh=21ifq>V&rCRqQs6XaeOBR;D*G3r* z8${4y)m0~480ドルhimS{CJlx{!L=(Dytmy zV{sh=(UH2UPa}y{y&w>%mD3ZvIu+H{ZZ=@9h8?YAEq2xDf6Gn7TCP}$>Y9IV^XyYl zVC_)aRN0<7wp7z`)7zy#j?tp062hS&Hor3uWr1?xN_dE&V!P1 zVsCMeR15P9YqfRy8_y(cJsNz^AZ%33+C!}C5AU)od$JWg9%8wfU?1Y$N%N1Q5Y?@C zoY5H9xbykD+jjEML?su@`~w2N#K^oJyICgqf(rR37upn=V> z6xj4Y2`d#*nVzysAtw5Uji#Q=#Up9~GtGtj`P8_dFF1c?ASvW^AvkWoZok93z=aWU zvJVlHo=PA8HIfWuH5BPb5c_5_r@zac{=mf*4h#{-GqkT=ei%e@D65VQa#a`jJrPyj z^nr|{rR&~7ドル;jVR@zrbsqXcRQ#H7ws!Xg)Efj}pJm@+6(p+>gWD00kx2VzxemysWm zA9Dlqy~*WXmj=|{B@XqRuHb6)csGV=aFbfye0ihA2K@g1JbHCup7Q)H*qKO|w)CKo5DI#6IX0vroS53nTrp06-7vA+-B11{ zFwF{q+P_n{bQ6ドルHhCoIM=-UCRm6GVQb#J|$CKJUG+EM`J;Ay>jkG1=&K21QT7Ve*a zkD=@t|49EOb$|>F0>J3E#K<={t9_kg|m&i-r)u`hm#ximx?i(4cwijx%vu8d7^ppf z<->)Di!2q@4{7Of^2whcXE@Ef$a7xTo6ZA7fCy zDxxi^2NNshuYys(rmBShpARTugFBXUfi}hz_1&2j9=thZ5<*#HVv3*d16ドル$~G5@=orh|$V@9j6LE(-bI|4DPD-(#S=t)X4}j>8V8XI_Z5qPe@f24KfEZjCjs)Xw-fzq6ZM`TR4uNq)?Hm)J^0Mfn5D#i z{A~1U%&nX_bx9l`RguLN>Wn+1rN^~P`KgtNjYmEWrs;tPOTGt71qE}5Bmj$-gd0x7 z0<2gb2x{3aihvxk4wy0rpsdvrhogdqy2ドルqg+uw+l?j(1wqn5c*%@0~awrcds5w<0a zYbshO^OGjwtD{ut*GHrmx?f1NsLB)KUQ_h-O`NuEvqu00lH4MIpI&F7!T?tE)g(5N zeRZQVY26s8&^b%h1HHMgu3jW+shC<#a z8*1y4?i%Y%(^=;zn6ztKeGYUiK78_lF2(#b^x=xj`f^X}=H?jp4HpN(Yqugd)5a(;LjfaCWO#q0-sknhQ{A;BG=uB^TJQvfwQ&0j% z)oM5u@gLCWYo$Bt1=`fN)`M?4qf48P<^`62{rzl@idjr|owa4k6fpx|p~efun1xj5 z(O3>ukJ%_OmDp&Bg$vRfZM1)&v!Fg!ifuezb;i(U{(j0|zapeO*efg9YTvt7(Wc%KlJYbHkCsSu2t;@hOY?5 z5pq60KH?fXTiaJd$%uce0*`%JL2m^=OR&ZOL_#E_q*Zo9obIVx{Te|VHP*dMWoAvL zv2=jee%KmT>A2!m-v<~|_f)kob4{q)V7O zZE3jH9)hNd9Ll>I8rWY$(C=*h>E(cWC39G^$T2Ug^mKVvHDl)-xQ{;^nXdd|eGNsj zvNt@8!CsW}<+f+^pxy(izs%g0uf*mdxl9wumop9aw>myRHTC%%gESVWR|mrIZBSjW zZm$_rneBJ$i3kdspVZj&XC$LJ@Z`=xPOp0xdpTI=%uVPUDrH23u}Zft+`MbEmw#L& zTm|3+&bo2&ufx7tO+lm22E90>5KWuA7TYn4d@ulGd%e1}$YrYs#hw!q&HdoyKaF$dY%mm2_z`v9bTYjqg@TH~T;e1A#K)cKCi{xW{M zDu9ドルsx3;65? zjC9$Ted;2UreOU_AM6sAtN&=k=Z-!X%sB0MxkzCYuspfo^)yWsVw@FA1Dy{PX-LWTsEI`Ao_E^S*xh1ドルvlc}RI&F=$JjB@r17r=#Yl2Q) zzs$Nug$+vi^IV>a!ud!q5aK(ZuZ8_}2gLeXHIsq$IFAWvEc^P@0XVLr@~j@z>9VK$ z{i5RHwobHQl6Y(8(n4(b;n6ex+Buqr-!qYQrzeLyBjte)aO>|@MjLstM$o~~SYtqa zBWyDEJqdgVI4!r-+9!EE6VErb0&sLy1s)7#E?18`4e^vCb8w2n&BNomm5)97rx!`^ z1g+p$hRkkaqiDoXlE<~q^9c)_47ts-agg7_>oeILzjihF^UL?uNniRl)z{nI1U_1e zrkz{ox6jVNU$Pcs57Uq&rD3gS-s3!xVfk@ipu8u0_h37CZ|B`*>4sd?5z<}ol`i&@ z)y0?Gin!4k%@;nJ8BD+50XqkT`9K(dYZBPQSMzI7Rba%uU%CLyH!86nZRZrhS!2;b z#b=7trwW$`0b`BKR&u{BK)W~h{SUr($mD}~x$mdf|G60Qx)FCupuKmhz{Wy7$_x=A z_Biart~~Vixc?c@3u?L#BLUczA*+#^4rrgY1B^r}H3gtZ z?wjmm7IzLAc2#4eGbg!~mu;0~57(AsGJ;c5aP*k;tn-o$i3cyF#n0lo^Dcn;>vBBx2vn%%PaaS|Y zR?7ChuW91bB0pG0vHiI$Tq@rje*;4P7EO1@MB5n*4?_p2_A0%BT_ zX%tvX>OW9;*G(3lq}+b_k2qaa9&6hRcEx|7>i{Cr#?5LAkB$OejI)N?)7rbuMBg+aJoScDwk! z@iPHG)5ドルrYXbPdj#Nz1FVx zsHM(E>P#+aai=2aE;zA9v2D%H@;21(e`4{HVD+j(`Sl7bvD+!9_8-;jkU?gFTLOOz z$=aOmA$qZ6vD3X!#6i|n=z@2fW?Bns?kQNA{HFaD+D%f`p&ke6GBF)|=glX{f@m_+ zTuzSytAWbGbL$Jg{@L*vOe6*l;yxmc%EbS(ar&qC%20fhemDLuDRp_ts8mH0W zJ7sQ@7s#9NN4k^RycwXh0D}IL8?1oCkg0s2HB+0d*=)4f2l`(kWGA-%qWFE_%Ny7Y z&3w^+Xz<+egh?!_k0filkqxgzWzq{)*(`BE7sWlZ*>NbrE&`E`i& zUE}kVM93sxRT}^?G1M4>AT^lG6c=_uxbWY1ULb#i6ViX}5t2f1*a3ivRkrCq=s8dK zjMuKAv*o;+>mS-Q`4~*P*b$&FtD>BpU#!uJ|Ga?j_+ff*pn`eskEqxkGZG~i=HfpsaOCNkDa>lXXCONhg0VP>3yI|Wf8Hb*T15Bi%l;0uRYfM@J8ukydO`zP{K8G@gYS=lEvj{% zpQ^r>BK(P#RFFcrE(e!SFB$qQK=knr%ct*eoLMt%`NqTe9|A zUt8WOFlj1rzC)Nh{WUczwd!mRbUH2-xVZVcK)io<^)u+oc8$bpb@qe7oheo!h4w&i ze$kG>S|xAc)=0GUjCu7AhPV)rhzkKvXzRuzQ^5ドルzRj633@moH#nzca#&$`{5^M)Wf+lpRF`bk9yIMVBY z^DwrITJnO*3JW3Bl{u`^!-VqP!MEoNWuGT3v`ENWVT6=y)6Z+y0?vC}^KOvm2jd{q z1?6%Nm;uMcQsuP{5KGImM)`9l5^Np&!+s{QG%;4WlXYb>r!#hm8C6eR7>2sFr+8!C zXrWW}UsYFAtLxvZ%0*p;fD&l4f})q=?b=C~%uiVot6PDm-=&f8C6aqaF~35vGAUj@ zmso`TC^?G!p2LHR6P@ymw($K1WUjlzjzmZwSe(gVC#F0*_nTSADF@mZe~N&(HcfN< zwc(c3l#K2`ch>HqGQXM6oJ-|J{nSx6S`I;WR)?vD!f9ye68^g?;1Sh@hk_OM>PSfm{|p z6!F2o-{6B~DCBIn7;s`X`xI|z$@h5IsQ=280;BRV<@hlcw0;_{mj+cxyimqv<}cdx zLkF$^K^=IKmW*b_JI@YsE$cWcmb1Xuy9xF!3tbUlZy7Rp3K<7&s07syka1$dqz)w% z8(PH}Z7wpfHLi;tZC26`eexVQL48vc z#rZk+c4Vm|OjrohGAj{almruew`fUy?s&OG)Q=GwInf)O%jvrtB*+&vjEB@wDSHDm z&`X)m&4D;6=q(F{86MsyZmgB*Xhu1A!F59JOLSdc(66SQo*Z1{f`s2u=K*xA)dZxk8IprH6;`iaKW7t~1qz(~xS}PeiB1 z9W|?Up$N^QUv^&t#R1+NAp0)~U>uZ!xP{!M2(nh5UX$)tA+VF3{WFaqj6Fr zU<1pj`>Pt>h%q9p3*g7f+)LWi&t`zw(`!cN4C2W!5muZ5+${)75EI3&(rTW)OYMKj z_yo$Lllt&B$;rTRwE1yC^lHmy&<45r!kposucrkedqm|x3*?*at-3+ea$wf-t&yg{ zA{dZI63^IZm=2dt2KrX{m_gUH5pYHBWm0@uG7n*1qn**73%Z~_yRl=;ldB!iu8eZoKC)T>0%GmKQQfn92X_84gX9|s~qC0>hLuMSVk-VxA- zf@QYZ_B%yZDbUK2%npaY@Jaf5g}#L!B`v`EEpFV8gCqY5Ob~eu5|My|dp#h925e$j z`mqG89nSU^<~?yp1g2r>2|vC-ca-a^``584VbHla?&{9<@u5m0z-zwzfvat1h7xw} za{w<3fip9ab~nv>{Fc!wrRnkNi!JCOnWlTIDfW#cHsP{}RGtx_=`)R>Uz4q*c1RUv z|4_BeDbS-H;1&(oVdAlg)H4VM_SX3n%e_S~9|MaAH&`=yZ^UqM^k~6Q`cPmIU~<=( zB1=BB=YikF^ruu7;CwaaGKs`_pIXqS;%0Cx%qqpE*n_OY^kwKvtt(S}sOVp&K}ztA zH&cfPUe#}cq7MSZIV*rqKs_Q059g5bD3snO@fZo=3|PL`v+1?8U^LZ!CQ_PM62a<~ zn$TMu$Br9G1fu0_?4IKQ156+xy!vnz26IFj(gIHG#|IdivYgFFI<(gfypat@o)z2u z81{jb2QipMAhvm$_mRi?SjTB~i?5^vEj(AyjkHT9k@-83G_#qsLnsSBDpA85xlK*@R7XY&VuRX1=KkzWIFFoJA(|qWQdJi`+)n{K@ z%C|Qn>=_V$#^x+X0Ya*1hEcE29JOqAsPZ*P#Q`Z#a+)rhb1LNAaD>}tTUu7ドルvyc-$ zTWb4?=1F`@;RF2cXT8>{4~U=j)BuGOv8dG#4Z7f9-1{0qA`AJ%S#Ilxm1^L!hj$XV z4yM$Pmxlp7r(9Gr&}h_#zG{JRGEv`E>2$MrJD zJ$Lf{0mE4EZAfV56kyAGl&l@tHzP$M%yU6zUzT}$f%)RV6EH|vwgZrc0KL=oWsu_Z zaG3wgj8@tBL-l`{CL*XGuYIk%n48kly{OEqEy!|dO2O)Pm&G`UNP=t~l(Ax3ceuRZ zedhLVpUjp^#=)l0dLT0t8Ek^|72p(drdP8I=Phtc3ドル|_vuH(jhqQk$ba^m^Dq$KR; zp^b=u0TmyAu+LKLz?$qy;MFg^r@p?ns-d>a2C;Cg4z{-eUFy0oxFb^N*4~o3p|;3e ziZ1{XITpsC@NUl#RJS-SF9tZtZAsnFpmChNRqYtzGk%fQ|Wk90egEeABNk>(Y(GQwr-vU;~9qn!67LvFqqjR=q$cfGN zTAs^en#j@-+}*W4O25_tIAy|@xKu{oj{vWb2)C7;)#qWt1qgD2LV!Vgxz?%%L7K8f z_!s_g6NXGdJ&?n5C`)=d<-+i@#`o#Wy0nx`qnw2Jk zFj=rDh;OREEi%Mo=0Q$`gT5u@d`Hq0W&AV|7Jbt8C>zLuCde5jvyLrg^ON07|;M)knrMNSF{CYI;d)#{X!LctVfnWd$r6+rF@;A zSH>wYw{gFQXWm`v0oJn!jTt1=OXrIj%GhPGNuM&2>=9cy_D%DuWx4q**Oij=YVjV8 zdN$I4x_fQ);(bMVJ9@yfR385h6LE^ey`M)g z#n-|E3g*ay(M0?(ISHnz;I=>g1ドル_3BS7{5)ZhU`yGvF|Q>z5JMM0C2GS`9{iiHQ@F z)2X))Nga4BhC!J9tPI|b;1AFaIo~F!AeGVlkiBDjb(gKF_Ch54@p^U{6U80eSPO8- z7}PNG52$SNEaXex5L;t5;}Q(sJTFvm#9945u79@kEP{)Mdw#Y`ZJqyGPR_q7YUfn} zGVcon)?c$V(z#&YxqfgnxhXfu-?eGY->Doo;HeG=%8s_#Q$qxO2D=j%IpvI?CGT#s z!-Yqj5ドルYuT(F@OQ=aD20TTguez>2521A)ugN7Bb1Cof(<@tw&+rrxz-dbphh-1t6q zW2w2>c9eW#qTwV~?WbYA|LN9=Ue6k_Xz+{VPNMQRAhXu&4)*@$HY)2ドルpnjB#he_X( zW@w3d51G@19L!U7_R0S>PQgKl(_F_iJ$%8wv@ad*aj0DnyQuFJKvy-yaU#(v3ubPc zdBFHKj0-43@@5ff>Q0EKIf2L{(Rp>b=V@OwTN4fse>~NK>Yy-HY+agu`r%iXL;8#0 zJ>0uLCi>X)H2>894r;Q&KQCZk zp`OKaX+RZyaeP6xvor43i{WxVxrd-(MatC*FZ&T#Rd$Y{PtJXr)q;=Hy% zZ=^3m)tAeBPg4_n*yVe?@#WZJEeJ|wE^ahyu*Jr{ZFJRvSI!JV00b9t)9Dl1?G?R# zRH<)&joso{c>MS$zyI9TaK+Qxc@(U&Z#>L`{W*ndomie0?ul0wiu%o9r_9Yi(?+yg zj4L-swrWbi`S?pY?MBU~GTocZaa(x7GwUjpg_q=g2+(WU=6yYscl%d?(^>{z(9-A> zlT6Pb@G1Y9@U>stVV5}MG;}R8)pIb1b89oG$ys$Q;B*~i zZsY0IJ~Non_+o@tfwMOFie_K8D-4F+{EGY{K{W~^eyO8Y<%mhxsvmwjorxah`mrkr z(6vN^KfL$W>ZS=-?~svRpSqEi-$ddhZzhaNX?+LIV_>hBK~bWJRXPy1qaSf5{<#sx zgMr7}P60>!*n#r>WECa03VYtGuT_v`O1_DP7r0zf_wk| z!L+U7EKKkxf!*^weO6>y5}R5!RM_qaCEF9Lb`Tv0sS|ti^nHelhe9 z2*?Kdjs`$cxYt1i`R(UOs^RczR^F+1DX_<$rkyd&hr{#ezp;4g_ovb;6co8dueet? z`06aRYYYad&%XxyaQ`#fY^#6jb4m}o5fwgoo(|V;4WEqa5j}-~Ojth7NWo5-Ld<*(n zqaY@YyoWv6IAHuK%+hz|gU&ad$J`D&m#WyQGcv$Z8n~A%>i1AX!0m%f;p2Y(}wC2BITSQ0``6tMl*KY@2Mvy2= zery$LNu_Y=_}KYzV#nJ#Azm^{E%qq;vojlLKtWt8f|zsr-ir-oGtJt3rb0(hCH7XG zFLNmvwdlG$Iq8m!UQyB+W4Ul9>46B;e@zZM?m?YsN^R{5mHf2Qa%qBpWC;_*o2K90 zl@7l)m7_}?{kuY|TP;|g_SVb(we(Qcr4ek3lB zI5>To^aoKoIpNzRJ79cwZL3z+P}gij+Wtu%`TI@-CRdMw^U3J*xAj z49|WgI@K!-bhSJ4u9DlJN6s^msc(kkfcmy^;Gw%+X8-mbX~FX^L%p&YlW;Sw7PcGM zSC^O{?74jNr^3l51=)~Vgxzhjr;~RBro9E0Ok@oU7QOdU6DPO7=?1Ln_45GpsVsKF z7$ejgpD}%;jcmW-046VGNgz2wBF^J`u^(o+fhYH)f7hXpo8O~Hok?x1l!{yYN%T=L zXnhi#*B&R;!%!Mw;n$K*kMpy;QPSvtor7ドルdXYM#vatQs>2>Qqx@=xBcO(4uaM;hR3 z9T1gQ9dQ2>y^M|`^+0X;Rc$)#VZ{1ZZc_hdl5|sB&%e&Zx#zdU8`p7FQw2`HUx4`V z`foN$-pTHf*kDSpMkJQEn-F`)SkE{&C%+%1v(&w) zG>9>Q8K19`2CGOeQHs70a}RxTgkKIgjuMeLyvR7(Pt1E9^r8CS4PoTz#ubrCW~JPW zhk>e5wT_BzH}V>eYq@6dbfR5^;!{?L%E2(!W>1@R|{CI;6O=x?w&rr2!fNvnmA~ zQlY7RjV=lvtzGmy-Pf==k@63hgwXormiH}%p51$U5}9(FH;$cy{LvtUOreCP25BfcDq3BD0P#cIYEJk0}DG)Qp5{( zee_z?JWTq1kv5zXqN^W6Lw^L+X*g*-ZFEmX+DRkY&z-c~BLARNH1@O$fU`oAbDko; z^IPJOY0RbnGd6;&(iswkij57lxyA8 zcdB$`;SUO8_NBC|HZXU(XsP?MB;p3yw5?YsT};OtjK-YY6JJg^UTc1neF2^>TV*Z$ zqpA^~_D{r`$))8{p|ADF-2JX?b2)UrtUyzU$=3B6%eLzKf7j2w{Rp?A1QH>eDGujO zA74Q2lBYNf$%sX-Q^O>+i666i#xIJ9l#BW9H`j|_ol2jWV%?&gTZ6Hl>$iI%QF#^q zZW52da8e1m<~{*~+&py3xeq7moz3!ypr!|ko|o2nm^~>a1l-L<)}hy)`y z+nk!_)dMtlg-5t#(6&?Phf#5`w|b}^TY*W7WtMQkfY!jP8@1JNdgz@lD8O{5!iO+U z4k97G)CUDHR(Wht0>WP%!K?8pFs6__V5v+orr|!LU8(2Nat%un@OIgiTOkvrc^S%~ zTkb`5{;1)gi-QZZGGE~a{5O%d*%#fMX3@{S3mGsx+e}bqxG8u(jjA4GTDJ1ドル?zY|+ zP5gaw$VX1rgbb2%2HQr%N!#`sZXSd+R|%@rruL1h!Z|o*)*~pB{!KP9TiwaHA>a3` zYzb9xoe3BMGC|xR@=(zmFs~W^`e2@;s8te%y=-{TmzxQ;nkd`V#Nn8-z6hAir4t)K zC@2g9VWX~>mmELISy2hn&zg4???)}X8$(`CZ3oylXgj*Q&g*85Bhix5{uA$=d1 z)i}H*-0ZZ$;d`8fAiS=J^U&A+%IUe^U@~^MS(Q|PvsFBDF(op3#7D74HTDEj@GYmz z+DUGdkWdpI*p_SYoofQctFtN6 zZSc^+YWpe3CLm1LNnH8O{6TH~Zq5w_i=}c+;rd?VzB?D_`dn462X~vVVoH~xT4UAc zA}GXB@*!7JAETb)Fk$hSPjuvELzHVmuSx%E?~jdCn17uY#4ysF6ezGmg!42iMTcI; z;gQ^3=c?UU_57w{SEz?ZE($(4G8(L0+1Bnu4oaE+X%S5-l~>NBz?4aHjQY zr>5xC$s{imfah8^`+>}5?f0;~X!MO~VbiYEuE%a@(4a^macub~C@T%JeeQ`KOt%{Cbb_R`Jp*RH zkInu29V=V;07e1-;z~@@S~q`r&g0yA4pR~>GJCL*UNql(^=8+B+xJDU&WjPM%a8|;&=2GF!IZoz?XvDI_3{0 z@aB8#1TC+D=t2iv?XVL87NfQ048HT5Uwh$mu0MKSmJ{D%Z zh2`Q<(eyvrfhe@c58;hynf${g@@trfed}{gn_qr8gsc6bw103g6dyu(i@hh@p$lya z7aMTQ+F?z6^|W$<`*!<^5$o}{z2uasvuu&sgr6axvj9q8)r`hf)xgkz8xlv<0a8h6p1c!^s%s}sc~|vx zWgA^@?>P+Ti5?o)E#zqLOlL~J@d_}?AciVM(izh#d|@D;IhJgxA(qQ7&klbb&m7~s zR@G_PBSMP2`=AYda(`7y)y& z(rYu7!zr!44ドルDyYZU+yT659ドル+5Wd*?u!R@50%g6?ZpwqWji0`T(>H+pIGQQO&Y_CX z<jw!u5RyluAlW#I<2;o?u zEDr=1$=6FT^hx))EoO@OVaPNvU1vh5&92+9be?&dJ2twS$lhDAZS0KM#2ivLzI&~yBkOBvG_aTIb2A+^ujDs@ zUdGw+G$sG+yyf$qCwFRDyjZQ0(TtReU=M$FQ&f<3l51ドルv3>I_o4SPk#!yViak{UsU zMx?#JkfaX7Ttx-mTMK@!xC}t)+bHwqqfhMkSUNs^Td3xsUy1(%P`q=XP>H-g18t<} zut#HPVP0i`1I|l2Do3*0sVhL8RwnI zj}JRot7&%}I;B8w%0=YiR5_oa&H+M>)$@0B_`k)c(LZbCR&W5##>E|up$LxX3VPWQ z^Vxu^MBv>A_eeRxvh7S+ZIOYNP?0xom}(CnY!;7Ti*%ic@^CjX@vS6YrXs?tP^_PZ z_YcFg$?=V?#45{4ドルa<*yhqmh#w#}a$$dmuny~_0}|TVgjP|F)I1Pw&8}PYGVvTwm?xW^YpPS!4%@;`>>J*6_98$WwWLbfKTb@QQXIYl zL9P8P7dkM1KO^P>TeX#cjq2J9{?5UJ zD9`AuW4AFIdrPg~@QbPog_4@70lI4*t?2G?%?TSHS-J6GN&*EA0GNdSJ!Vcsp$uu@Q?n<%`e-weu{)zmpo)rx|5lp=lys$_fa^+?!#o86#2&soo2xt`;=mna1mm@ zGNRd}_`U(iO8*$nG!giSUnO8?+4(*68<0@a@omxjb#**^e50n#dlh(g9mtb1^8uu; zleRez1Ozn1lGNaExUpjn;8?{QIe*#P+k5@;e*HlxB3V!HwvJfnF`Y&e54Jqoj zsfYW=Ex+b0ts%4fFbyQ0m^8uufB3?N7Sib1_rc^Yvpxz@ye_$}=EHV%^V_ z5IGfwUj5L~#?hB*Ji7tEIngq;7LiOeYf8VWDonwd9$HKI8IXNXuxd^oHplS%dwRiK zckQC8ns<2g*~e0t3to`0+sq4vwviv`ta9jqgf#jtzuq`f3=jrfol+w4*5dhwocpkl zzc_y-#eIT3$|RO$;~N`b^7y(`rw9^sZY_E?`EY6t0s^x3f@+c7N&6DpiE>LkdjSv{ z?f_?qwov+ly1I$U?4LiCtW)4|jr%D(u3pmk;Sh72&c#bDCs?Nr0(w$$L2-RoJqpNl z*SUV; zPPEm7p;alg%FT%?79W6?vWVx?>g;dJM~IZCZkT7sWX0zvkzBSD7WP&1;qisg>jZ&; zg7-&Sre<9kzlibk;d*fd6y>st*R#Vv4}ZDlA^o9=JUAmGJ+GJBG-qy|j1rK!>#wCp zUM>H%y|Ng>5q!HmnX!H?c@4W5yItzue*hhT-U*u? z%&KW?n+H5MGPTqtm7-l|;AlWz_BkB=UQ-5GOtQz*E(joUE()8KfeztSlYuO-vi#XZ zzlhz)eOc^kCZ5i<<9f#lnwemc3`fv1iiuyay$kqpmnebo9n-g7`@;)6wuL8K z_#PIRu3en%uzgJP^~kTly3?Yg>_NvQx;-KN!B0stbx!e@0a=74U8SzI66ドルbL(6-fseL+Qf@4cxtTQpeRW`8EUibY^m z{R}ptsZR2DW`dMfFq{0+UloqO8iVFf*|sh3biB6ax3Wu7H&bdgJJJu<6_kfoy&s9b zJc#ENT{*gq-C~})du5#)9EOUBj?!QgfryC2`7lePSL`ISv_@tkahT2%BXPVqK?{E4 z;3dMrTCiRC^$FNC=uD#va&uqW+S>m11cVVKC8hk_+|ud?yu6`!DV-^?<)yo9nwkpy zjQXpQUqH^9YWO{+1Gs&Zvi6gYEHtlOL**XbWuJGjqek6HCvGXyP509=$f|Aa33+5tbT$Bkzw+|^fe31+f*ovf0M2r{bSFwf7uH~N7iidWN+p!HSMsXiGfEu_u!LBlX+f;>#lGgQWc z+%MbSqgAI0yY&yQsT8h?SHHjnNqJaYLD6U-iT%1-Zx}Q*7!=6mg-euJ-T()BMj(2z z=cNYeiMMlU=lVPlL0B^zJkXTf6obg8-!IObbDl}y>7qjU&S?!#E;+XfR?3}tr?nq| zuGD8tK40%c(?+9ynfS+POOi*?W7=+S=Mm$t(M(ueG!?!5{6Dy1KePJM;`U zI9QmlcB_fcWMQ0ドル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ドルxo)wcq@a=f?n;bux7nssh5sfw2#2fxeua<7;8dcg6gn)_- z{z}<-d?c5^ zFH)l`d)m>g#W43l=U4Y zgU(R1Ycqs2g#a9X%Wvc(0w{XzSUK6(Pb0ra3Bp^1b*) zXYZ7R)hRKVL#Sx>)1q`pVL(7u@TG6lV6~rQq)R6W9FQxZ}Rij4-Q}`DE#nOa$ljIplm$? zu)yB>#y$EF^J+1aIJk{97d9+ldZ8ClJPSmZFZwqQYO1{MUZH!m92%AWG(X<&`ay*zw*lrd7)2b?z_ki zd;j6MD1v%z6FEB|p!06)=qz!#u}z5X`W5g-h*r%sux_R1aqaaA{oCK?--tzv6nD^; z^`=hJjn*Iz_m+7!P##ivLg>}L&AC`EZqWP`YLTv;ozc$}mzT?0%F+1ZwVRn;3SdEa zJ|5aH?O~I-9-Bnm3i9wDnyzcDlviA@f~JqcwjfEm5@!~3u~-9B1t&oDDpmtBXz2yZ zF{NzDR1GOdUdmoIJE>bdpQt|xsDZcsEsg-A7IYJT2NyW(8klOZMipsLiK#|EZzY;~ zbw!^B-3gaISZ)vI=)8$P(QR<1b870xw58fa##r^ehr6eye zCef~FrS{dIu;lzTPGnkcmlG+`tb?}NWNnk&pgd8ePETZ4Y-ji90KuH6hLZ{NkQc#; zlcQ@9u%BnyfC}oPJ){`n=rc~ko1JK0-V9Pl0!{jz9s;fzzcfjuk~xq1Vj0^EuC;%8 zoCh7c%l7YH?`+RCxn)18ドル#q+xBJB;hpcT(n?Dsw=vo-~vTH%T`@~c46r;6Ldn81fV zaem42w*qJo)IM867L{D?v=!Ck5+eSQ8?p<=4uovtid%cxjasx<`t^008ドルuli;ftur z8Nft}M`#i#`0e&lC=3B{mN4F<14qm|&#p1hg|#&_t25{}3*6%q@1jd>;M~~vQ&mX& zv!+&sMDh|Fg-d;WflwB@xi5`mfdz+cQ31~mYVRFqdYV-1-Riu4zsS{finr!hI1JjS@|8h`b7 zw^srttsQ&Q;0ywzoP}2ye9)Hs;Su;a*9yz5&0MnC2A~^?0}ZJ+Z>8}w1!*iMgvC{y zQIiPN?(jKK1r-tNzvaUZ9NOR0UVGk)^%Y)rguwq|A%*e3H%<6mz`lrsse0v=0eg+` zU=hN=s+${#7W`{SLb1E==7tk@ShF&ypF!JxLJl-u__AJ9&xSGh%}uS((a0WFv%2I~*?OW%2;Eh6&i3d+wEsXOO~xpBeA)JWv9KOAv3c{{)TYC2Pz4dP&SZPfe&d zEvjyi5C#vWXkCSyixU`Ec?IGn4v3>;858{-w=su$Z2pZx6pC_wXtLQqr5A_H7kdHt zP^^vT>vi4C2rR?>{nZ$Z(%81=lEucxA~)T=gEKIB>%R*|%-<%q<3n^%r$jk_ue ztv-8={=2o8&8o1|Lk5Hncl^h<#5|k3?bcrbt9s>p8$O;IFD)_gvNLc!+Kl=#*K&)l zAocNenPcU%L6;=$DVDmE1WDtR`&Y~%C=iS)&rK9Hp=xxaT^FM{4}a4B;r`!`#(-tp z^`Mgs^~yw~;dQFS+()lBFaAL6>>|qU`p*uuBuOm0=(#9wI*!{68aJdhdi^Z+bS*MD z&waFT7T5J=R9Hn*>-`5bz1u0sS!mc;o<~c!kya%s0oczs)7?8w=mj{e3!@cmwr!nm ztG{b=Z5_;;LIenavy#smMRZL~BwjV0{1tLC<<_~asp&n(5pzf13>L%viIz`e27j-4 z_?>LtOF7AmxA2uXM2mXSHp22A$^hx4^NBIE+W(YBjd2}`jwN18P+C!;K5Uvt0+5RAL2`ruqx3)Ed#U^NRX1|=9(HEAG{+=QA;Zh7_CcC?f z-_m09N@VMG)Ewk|Ryq%7Om+;d)rLiL)YMNddRSuwf@iWpHieIZqq7RsQ@>$~0j-%J zi+eVKP=&dv{zR`~w|7`On)!Q{9S{v!r3cRH+*6EmBrp%rz!_Evj`RnG8rAi6ieR>{ znp11V0Nd9D8jn2tt9eMTQ*oYd7s{@Sf3+-$!g8gNlL1&U=n#tRVIk1Y@&65S)=05; zAVPbU+0)GIbI&7cTdvq{MAE$>eMRi`L9Ma2Mbdxq#GQP%5=PA4qc=ZfTIP3Wb>(0A z6x2*@{c4>q>`}NsJ;20x9ae0^@=^AMnu*JuX0zs`4XSf#U6IN|kA~>v7BWx}`<8ci z41Fc_g&NOVdp-g`&lD(O;z+mYmM=gvIt4ドルrU!%kZu^AYRDEuNZEkAj;$ zQY)SG#QUNdCn?M+xb+U;#~x7e|CMCuOBB`Yqd;zot;y5BD7{yPKOO@zPRJD|cS&;Q za%~jqr<`&pu-g=wsskcl4*sc;dpwmrzfui|y<)wtg!v?`2uhfn(km~}uppz9{k&`& zF@e35oLD^K+@Glj z>0>WTth~*c$WIaFf+m-27Dln9IZ@tAQKHou6*xqLU8Oev^MlAGc zf5BP;HMO3pJ5vw}grV=Qzlfou{=2Zb_G_(l<6p3>U}P^w#NIX4E>ZfRJ03V^UYxf0 z+9SEgHme$|iDq*{g0@JLJ`uoj0I-C@!z*DPH$=7wAARt})QHdERdvjJdhmQQWspE^ zz10t8qQP+Z6isOJwqAR9@z2R>wYA|8;iRPFlK=bv& zsrO8>B&71L*yRMzGPB8CwX>JG>)5_c{1Z1l4p1)fH{|gc&kc z9w8~|wbJ@Z2oL29jQV?OLy>@pk zkVZO*fP9$&c@@VAMYt=6apgTk7t!9kt@Lhq#7akm-K(@-yy5ドルd9xbfPeg_F`^zS6YFQIa@bG-*341ylst<+bb2c(!d_xr)z>)v zPK_V!`~T-nKsTrLr3+Fc=lq{`ihF-jAlAn&DUcSG`U0dgOQr6V_U>G2+<35+zy zGn?}}_|%OZ*l&Xw*nYvoB?XA->rkeq|=Q5;L~p&&{s^6d(@AWYKM4i+W}EI`-tMIzW(m( zYMAYO+gHkh(=V*cc$i*z>ZiYqPox14Wud}foB+RZEC@uV?bilen5k}HvPuun){9C& zub|MhsoLjdBVNQkom((=isAOSZ)MYC()B5>Pjna2PXZRB^_K6ドルP1pKlel@#}eD?-L z_sJ31QQn;X_v1$seFO@s+6Ji9!R^H9IaW-58{-d+l!&Sk7UY;Dh z!(p$BGY^}c(9hhUQyYbchN~QH|6mjQcm(P&gnBOtg6A<{6)d7{t`6 zlI{2PF+qnGcQ&DWsaf&CbCY?zYzSQkAvuxN?vG|*X--kYw_d9Kt2}jK=5fX6UigG- z??W)|^5nyWl&9U7K2Y9{emy!~ObiDrAj-Uo(9+Y<=qqgjjcm3`vvcqvz_8hk2u1kw zr2d{$AbSxPK!VHLG0suaJ!rG#%HCceoH=k}Ci%LY-<%zg`iS*z~wRgY6HC&XRAa zF#Hee(zH2P1=1#y%f_Qy_Rc8o_{mddX|*k-53i~ z2b-JI=rwfH7~4SKI-Pxaz4(mUdbKNs_u~rFhVwFCq_gr>gNhr!%hgY= zuOscB6!h*CJB|eei8Yij{QxZHuvi1c>-7U+vC2=s+SGwU8STXm>&4-0>6wm2a!T$D z-#I$jpKKO$=A#i(VcZ3Z=8P=F5vezooKxhAUgv1=hIuCFFSd}tRIJ2-R0hy*0*vOX z;GP7xL(kXdqzhT|JJSHkwMAh#zYaP#RENcsDxhh92)9To(FpZQzg#zXa=Cr`&|wUH z_KR*i)P0b*BEuZpA%i<+)g7ojg{f{tuam%5g)lnx-n<56ijpoq+6v7{juv(tux!jy zR7+&gsyaf@>x3k%Bo>|DRfAE@YU`_lrGo8#E z%twK*bS9@|;+q>D#NvQQ!aH2dOvjdYB_`zVU8@i7e#ZZNJkO~62Y-^^`#oL#P~g_O zI$MJH{7zUpO}-7o3GVI$*u@BJAdV(w5C_DBz?TmZmLLeTe&P#vLuX#=h5K%!BKEp) zm)~x%ozF0*!iD*lT5o@N2|mQM&682Xc$ue0mbYGmJq${pZybkdD+0xN7ドルxfxuAk4$ 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ドルo;tt$gqv7ドルvsvde0-&`sfmj3i9j*36bp-s) z+yxV4yFK+AfcYP~WeOaNRzToOF$_Wz58#SuORp?}f{b)oQC=n~V0o58h5SSB$pr;* zUWlj9e#a-h2u?6M^Y7aV=0tQ%*}ii;5tC%pEOepD8N`1HK7uS=fvt8jvCi9sC!# z+DCy@op+gXCiySo*}tEmUEIYcKiG`lzy1wZmoFFjdjNXjKtObdlJqJn!T~h@!p%x) zSiiCFGA$atQrjWvjmGmNSz-v04XwPbHn5ov(V`%Yj`kEm!k~3(Uh|-J zw#v$U=rfQj%O<82dw%8fpu4_sm`~kyaoyco>G+LufsYR~numwypR1Ji%a=FG9Q5@Q zz#j`8ezh9!JMWjLRs;K=m-hArR1m4GzP`TiQ{QW7iW(Y>DJdyw{QT4ZegR@t#T}XD zGBt3-MOMb=;WDZuwy2Gv94nITW3cr2lchnL(HG7Hu?z93bwU|PKLIz-aaO-ZGr(h0 z&6)u4eo#db8KA1}yFHuMUDkLQ;(7TLyGJ4)xyKv@CyhQBytoQLVr(0B0HxFN+C5gB z`^9zd`#6xge2+f^_C-{9V>*IJp#C#FQf}D|%!jVZUYsm;&c@bGJGDc)`ul4xzCxs{ zk9i5nqG>_L7!b}DLuJR-Y?nOi&H5O?r7UIbR##Q3Q;9DL%E`Nb_4PY-Vlw&NY!Q5W*N!}h1Ju!##)6)pXi>!yyiADgc}kEp+g*Px;tdM>xTA7UpDA0GBN_AK zGz=>~Y3Vy%0*JsFUTyRc%|?DJfQJ=G)u*;pa*g|3fH4^ramPviX9onL_}z7JO1}Sx z5h46XstJWMf(=zF zMOqOIcc2E3garrB0NUkhWdz8uV_c{gNle(Z!Py+N~+i&Ey{VZAI|vBA*Ez6Nyu{Zz!NJ2DwL_y37!p=($-o$F3f zXk!Hmu-jO|Jr0wefvPF~bO4q+>wp9ドル_WKJhCx041wLcOTJ_MdF`YR3-^GFh#&kT zrE|id=WbuAW}R9-`GR5w14KN%Luw8(7Qh@aWLBi;(%!6tf|08?)Iq?3%SMX|%)cYi zGBw3vub%*dW&sGQF;&W=*B@Q7K2HLJhF8F(zg9X8eY*)Ak{{cO;VPPTQ*m#o9j%wS zO*ETIG|AabOs=CNS`DvzID%Ir2cJ7cN(f`puYQ-n>iLgG_GsLqtG8EMN$C^VbR(0K zJW#M&(Rec}{H;-f+6%!G)_99+pWr_kn2E7N@fVg=&-Zjyfri&?-k5+?zY9;FW8~Z@ zSo;5*>$sqJvz}_$TrtddKWP%>S+U)4Y=Q&EVS|TgQ`lBF-kAT801&NW6WQu}iP0 zoulK}ZCxXw|4!XEeC0Z;(4K!;qw&(sg4Ml=^OPkvpd2d*atv^;0)>RF1~m+}erH}3 zmYLU^gB?>SHHuzbpQ7S)@Ls5G9+cE*Y|`l6lJi+}hi%02rQ4zb&osKyx}idDb@k4? zXj)9tWWBE-5b?r~2MupUR@B)xQX?AA-Be1x#9uPNV-B?J|I#-_f}`^az0r@OBR4+4 zF*~01DyedK_ADsXbD0tU<_4u^0p%4h@cnqkwx?a)#;rvhbph}wah{zg9pe2b#wh%( z30SICfdkq;Nx*kERLTSM!?A9OPW$!i)$bPJgCJr-TP}#2Nnk0ドルF&F0HdIp>~oWQ!BLIs z2~tl7ドルY5(n{Zn0hD6rv%?%EN5TSMKn5)>|R&!ab!Cvg%fmyK#{4hB|WU<6b^iq4wj zAR_ICD{`hD@L62J#q5gg1vJb2< zhR#|#*is^Rp(V;3{n;qqO}oaXw>XhMUcBWm{#R(Jyei085X}s~$P`ph^oHV~h;p=ZvGr{qJGA%7J=qYF`XMmn?q&b7# zHJ!T$I{3WQ?Kdc; z-*D@l%u`hBO&7J#Gyz+vpsc7%|no1Y{D92wd=D9Pr zdQnAMX(E91BFG2mU2f1r((UQhMuWXJ;H728ph=Nfbk2_!rckVrulTQa()R0%F+bZ2 zv`qOJR%(nClKoOi@hg=H8*oO!zZ^dECkNEA9Y43RAJcBxvVci)VK)BVyrz?e?g?n= zt9=JZxHt@xUVR1>)K<^gtnzf9bs3#gci*~5y;g{x;t*r~j+sflcm`@8{1s}+m z5~y$}e~|K!4n)^XIh21I6*;*o!T2>2OiyWJvE4P6+0>1`O*jn_LuKtEgwmUAn9;zv zk$I?I^B5Eso)=9xPS`s)Q&JQsgsEu-A0En~n1Tv411zY(;+&Vq)58RF*k#F73^2~5_;r=c6ドルmF5n}YkpWy6du{^ zyUn%QU)3s;_TcPK>LC80ドルA3CV=1ドルSKK8^CMBd?-h@1%2eTenxDM}`I&cNd;<0bjtb zQf@!$Xrxj_^LGYJ)<+_imcpa=6ie*hs~!jp|0dkb9q%tq1joyjc$kqcxm7j{{(grq z^C}-8d7`sq-23sb@V7O_mjC)Fj_a)kEr6yWEkc1>wJh&-!yC?T8$maf{T3hPRa9(P zRA}K=p6%ezCNE`bRAWQ81I}S<_vgl!wcq;mt=8^zjni@gyn{ox`ime(tzcxvhfcra zi=em0T!!B=k@&7J`J#LG zl@Dy%LtU3RVMV_NM1V{m1w_6qxBW^k0hEU#X`b@ZzZvw7zb4Mlu#O4wK`-mfP^nGZ zx1d9yN`|eX=7Y$=v6PHK>6B;dqYI(lslxL2qp|?j*x(n@6i(>gS2anH_$O%u(VVha ztKW-XszVy#JO2_!LbC(}(6_A5SRmd9vHA=4S3zWQ5A+Q4?Sa(kkz5P!m3k2ドルotjm% zk&(BxM`k{X?xCu^NiVas=j60l=@`CFcI5BuEmM8>~N z4J>N~_s=bYHs{z*5!!lNKkrh?viupn)!MS(y8)&4u+a#9iLOa2*T->(hvdw>Bh#$; zf?uc{TLE!Vt|{FuZO)*hbOxQbzT6yqpL3t=zmQK3RJZ)O%IOrD=Q-@tL#`TP+-NI2 zn2DGsy~m!oE$o&5Jxd~*V$Gv_7W;h$@Kpgs!A7tGJwpi`h$>euMu)}ArPj(8z z3ZLHZa^jKa9Y(0$BLcgtpG z-KvETYLnddUGTkJO`BcC9Doba@MdcS14GmpsT& zKY@`+iy=|!Yfhjp=p;PX)Q+G<&a4ntd~vosfw3e7}%_jOLJXrxvR=jA>lB zO)-;4HI)YN%*I$`&0$wuVDuC6PH<#^s5q1z0gqujagj63a*jj`iba)r1w!k3cxn9i zA2i!-71)n9PU`p^4-31V2JRTd-$o=S$_vTNtIdNF%`woQ5$KaeyzbfWB}3!j4xnTR zcvh7HJo9{=WF*M8mL}@MvW#?4$aFmj)?2tuY1RC$LP>q)Pb*ACy1=k-_+cp?@#or# zR3T8wz+9=gTF##6Ir>tG7=prCUNyedt7+=C1g#5*Y<07ggo2!t)l+pa2ny87j4>u6 z_+366xA!}Yc^;$kCh1BhQBnQ3t(siRK)6y95){}1v`tLq`VDf6RGXP~1_uV{3avYO zO6z~0b~G)gymegx777CbEcWwv;6b*q@{MN_@-79cSdxJ0m8ek!!0 zhndpp+XHBmkVFc;NQ1+w0tw4mKru}*F5Ylz6Tb~RB992oPm9bNj`+IcgvYC}@Pp!w zKUdc^Y7f%VfkZ~2&-)cTih2I8!{^uYWG_T7F7_5dO*rgY7eX_6F?xpXd*!U|RK8Q2 zvEqY!9zw4(r0shBYd|&I&(y12`;g<4lt;nyk#tp+j+ld={r07o&w@z$f{=se!tii` z`rFpm9pdmN6Q7KWYPXVNL)VXS=YQeaB9&`M)g1ffTyMVbMl8pTpyv)YH)uSoaDZ#R zhaw4;668(%y;_tSx~++?-PM!;MKJAnni=!yA#?tZae$L_!tEdoc=6)tDzKg0!9dv7 zK+#IyX1SUT9`hVnATxK{3E>W};L2m?73P?ZeR7*}RQBNwhTcMs2fZ#=QWrmj$_2@{ zS0SWt7eHHR+llO>0gA8@Bz5szvUQtJ8xEbSKti)hhz2Tl1O1ドルX-J=L1-;Pyo5fIG+ z4EKDcK5pPKDJ(4^^f`2>&kSR^zVTOMq0#rxYv_;#Y$sgLsB2`KJgEJDmG|Z0P`2;i zLyJU8C0mx_p|X|`5+yWw5G~fRM8aSwyGBGLTTP2CWQnXJ`_4pEjC~)*_K>6Yd-l(KJ^SsagG}4Zf{UIFc~&~T z+#DWTmkNuy6;z2Q7c(LBxO&eb%=YoKLG10I^4^*7z{8PCOcCrD@aWla$%$qfd5-UQ z6-4#}ld9Ly=vxKvYTk@Ng( zc0QoH=Fr$qRFbc^t9h60Iz>`&qK!}G2Emz5(3KoAZa;e8VjAx%YGnynaiAhjZlDR$ zQ=WDjUpx%inQ3qu;JbyieE{5?y!aoRJ04P4|9LV9aDRQIS6O0ljI>bb!H~dcJiXdi zLuRIO`eMFE$yN-E^yh!*eA-|7DSo<^4$gw-ir8`|ug+4uyl=y9qi51b<}6b1{(s7u zZ5#uBL`bf5?1=CZ6$=uJk=2*l7; z#K&QdvK(U5Z(f>OE}o00v8Fut^(2|}B7g`5WvbUAtD8vwKH~n| zH~rYal1s$Eq5T)Q{+?S_#uh-JdC;{a#qzTilP?A)-WgC|D~Y;O5OUxJxK34xr_V_N z$NjL;3HQu{hj-s3Mn)utkGR_`4gjbI6FQuq2w+JY?#?osa6jYjD}qBwjqlSBU2Zo6 zl#FodvLWWur!M`u^N}To1EumE9rf=ry6JgulmHmvd6MgHwB}VE2Yz}4VF^%qvG{oO z5H}ug9ドルBF%TMo%QSQ4ドルeZs@bop3N4L1}=j$kxP= zOrPCDPm?xo)seuW%198Vzc=UhJw5Jq;`?O{`GUA>C8<$=0hkmZ*rpwfR=98##2?{kL5Ks)T$HJC>z>L}K2_UqY?PPS1_i@+KsT|6P+r}(NNqnWH%IVlN^QE~MB$Lk~d;ru|ZJIUe zM)obR;1YB#ah%(~Cqjt(i4Nf(@b?7b)bhs0$(bG>@4{O4waMmYa@-{T0qiZxKyPun z0jjo2VlmIjhFUyqOVtzohob7qx&GV=nq?IV_dRwGn7q6bb_Wp59~41=?5RH#)^skG zmX#&-_w#Xuvm6?Y8zI~oKpUdoaZWAUGDU3ドル%}R<)a)c=_|8z+z1*qj+ekxrqgbbzi z7(L}2Rew^ZE`s@sJUnUOG}}Yk`pP_lltS5do?EX<0a)ZqOJn}AOEERGIVoD)b& z7gE{k;or!^j8fZUP(N+|ko(yTbTL|@T->xEmJ9yLju=unA~;gNBn-;u=g;2bZK#~^s6nGH3pJo1}KW%=$iiJ$CXbVn!bZi(}DDoA1yl@u0_9bMe+0! zq+Xy26AYSitWx(ZmzJ&3E$piAC%PP7^8|(Sn0F^!sXk!rg(P)~JNCk7;1ywqV5yrl zc{pkycm|Y3Q$Oqbky#E!Ubz7Iy%5W)?3Vk(hruCS23;febJTE}_i%cBOfYN=2;TI4 z(%=s9Cw!PNDC+^BZ?vb@?4Xjr*g}Gu^`6i=IF%6LOK1S z*`3Z`tg7PQZEaSZi;yB}WhHXLj<(p(w3&tvk8d*4llcvlfta&kad6%w`vwci8_&jz zmZzJewwRS)D;rPx^4Y*Ik^txrhkP}HTF~(dJ49%=tujMB7T0Rr$)EOd94f7vkHfAy zxNPoCMME#Vt^+3sdAY)#4wcDcPFxR!=o_1}Pu57?LfOZvW318~3;0sCeN{aer&R2c zKE51>*Un%v-Vu2DCgU6D1U=qGM?E$ZyUcRfVnF{WW2nx)4Hv=MOCPw{G=L)TM%p#^ zDn`xU+JH7NX>0!H@W`XCrC5fV+S1^;1P1sLQ8qy%Yt z?B$A2jh)hht`at{XXy;fTG4d4AB;riw`bV*TmHxxrsQRAqzrj_S1PT0_cxAN~vs(ff@ z{MpuZEtKEtDF%D}7p|m{$ff14Z@g41DZBNMxLkOKV&B%n962yuci(}Ilk$MgTjQT0 zljv_I5H_g8ESWLXQr&YBx@wBzIgSFnwN4yv>Gwe&}CMb>{ugtm10A6LLbZ*QD5ドル+Me#yR>J z;TCv#?=HG>9vwHLUFyL={%CPc6&w=J6rN;h=QHST5)I><%k2l)tx}zq&~goxk!oqs zh=yF+DLTZ0rzV>pj2+0BhSu2`(|`Wnzmhdi_`I(-eBhfs>vlLHanDl5J$xM~kUWxg ze<~au^$?u$|7b6ugs~3o=)_z^6wzehy`(rta>_fd53tl?*T!!nXnBy*AFmTs&n>

dy(ho#f_&Ux!W>or88F?IzuM^)SzyIub zWW&?@I-AZv0hh{>lfE5vRb?^%=EOP3qbMUW^~ikg=yB-BjpM4>E)Y|V6z3r z_&do)XJ8ovM@v4&hcbXgDx zztTO)Lx-vYh0>eaw>|o;Ib=c5Ecn~BxDW>%$%cHMd@e>Sl_V}6T|ktYrkuT=2=IQHRakV{;ipNYImoS=j0W0#)`eZD0(WPqCOGQA$+@=SWl z9VD3H=DmCo;ErkK$oDK++y=+o`W`onf@7u3{ep0&Hgm{nj1ドルE=^9%WHmU2aX6>(+B z?AZzqyF9TL+$YwQ@2rynS@m*zp7r!h@s)m0;lU+Fi%Xc+3-ayPnOCCy_x#81D8KY);~T|FfLq-wB|u>hq+@N4_H2CpNFB!h1>0 zzN}MiK5mHxc(vvRFhr7Ym*>5=CquCSOAjg{lWLqNCxIhJNv6zdV6$RK7H?EW>GpsT zpDU*i$vq^{i*>AF?3pmevA64ru~=!mH^lNV{l2)eGN7uihfv}sMwuW*H?p83%7=tb zr7L%oM&F|&vJc&CX>0jzMcoFkaesBPr(t`dUc$m(=(~q(i^rtXB(ZnOZ+=bXHR?8O zho(ciE25?yKVRya-1w^c4iM&j>#?6&fl#Ma&?)KTgviq1&$G=%hEBTrK1KZj&nn&r zH6G}LB~&4Rz~m(z@^b$|(^w@G0XBSj?&D{g`S|jstNK%GoSpWT*bZxPN=cRgNszEe zZQ#Jz?-=xWX6YehJ*g>TL4LP!zlC}~)Ah$$@u%t{;^IvzH-?gK0t_>G$L*0)Xs2Y_ z`9~20``pcQx*dUiwSIigPv_zvq|qzgKTn{T_9|A1M~DNo4Tm5F6?DbN(SyvSf6UrC zdcAoqXX&}yYkwowhjJqgxpm@+K!qBjCeJ-51KKqe5ckGUO!Ai7dR)vM2o?y4e4-TC>(@KFcJlp9n@9|d@c@v@UOR&1L%U=)Hi3GRFT}ufh%DB# zzDG9Vo<~c#4`>i#UPG>RF|Z8U=zQVbv8 zBB3>16IlXYvF(n-VnzI^ITxt(&p@r(ma4(^85|AYjTX5VzeI|dKb>S29qzu7;<60f z#ugiNP4ych2GgSf=|_|FZa>2NhKn9qjgJ97~< z51aF8$j&OoUvnBxrGFuEmBIw&3wh|M1L7gWsRHbNZ>Q^Tfd-tvTIksW!uiWBFxjX^ zB51(pEp>ZP+Xy=z7$=c9MVAedz%GP)y(T%NsYo?6TU0JySQV7j+^1_=9lM{+186@P zdkxKNvJKRP;`LDM>GD}ze;0M*FiTR2mrKJcl29TgU24yJINeiPGF*4@zRFzwz30VA zm!m5PHviHllqf32n|EIN!Ery&2`D&O)UrHu8Se{zaOW!TE4TY(Bcw2lH*Ei$@lG91 zJyW6b8UtYN#7h1tc9-%bn9M!}v(5BxUZztbv?GPk9L66qX7SGN5%2+--PjUCJ+j$N z?(s|YTQ*uDw*iQe9;11O{0yM+>CN8gK^GV2=pkF1!@d)Xv$Q_IjJgo>?@9wPhDo@% z64ドル}(?|Y`$?P3d}bxDexxYP2f0jGy58u=P$cBLMgN%TgoScAiQw~GmjptSZE+Th%2 zId=wm4KiEA!)R`v;f-L??=5mWk1eX*9@o-c8!Q*Tv{E4HvD7%=`N6&Vg=iR$M7|>7 z{_+8U7|FoFtIz3DjX$Z^4PojJ7@zZTmu}t7TDbM`+CWFL)I@-!N1px7FWg%{>HCZt zae%~9AYnOJaiG>v)MvoMqpjuqxn~gJCgGN>u@B5$_1{jnr8lpw&$Nu6BR>|(iQ*0b zbyy9BN4`egN5^`jSwBon1MWm))X1&ndNdR{GyD12KGsgqG0g%w-gPM>t`-9|!v88ドル zIgJx2KYEFQ3`7t>H5tj82%z24E^kC^E%Y5lKMo*1l)s=`~Ih1sll6IFv@_9jW z(kCc#I%M^)Ao^HK9zh}ppMO_d`Zo>l#lm;Xpk# zW-YSwXL%6GpN_P?I@F|I4eoz|kG9bbp*=^IpL;TQ> zJ;NEum!2S9JATWo1h6B~+Ea0cfig0e^DYVwNqq(62^@1!qmg24r`l7!P zPP1N#x^h7i5vUfkO?}HBEcSa*s5kii^ufKQE2Y1`ASyITq4kr_6iuf60x`+&M;%Lo z*K(xt*Z!Gati!Rb+vCcBH`+$+xrZ+EA4jb}1jb&O`|$V=DpD92$huA^hGOKa4H9|9 zFAuz}$x@pzC9HG%*i?y8rxoZTRCwY&s-4!gn~#(gYPqy#%14~&aNvY0=$+HKj*d48 zA!jBZ^h#-TXDZ&<1jp)eweo1rfsysh#2%xiv9b|7&lj>UJlI~{ur^rY0SN|HYQ@b0 zg@xBf6!%*MM<5x$&7osr056qg_x&+^o+grhhkxhzcxixm;21-nk*&z-wr}ou8fdd% zsO*M5!ZGwzdkxhc&wYO%Vz3ts{{97M%Y7n`I&_*DVzyTMqX4%16d%Q(a=Bu|OS;M~ zCn*lMIZ3CVlXj1=*kmC0fURYS{QT}D}9!muwyp7Q*X`0dLAU4+lU^@;>ILmqY4_RK));swtS#yTU;saa} zzh>s8805W`+dvD7GLl`V@u8_!9$BDKylcD5px)&K;n6h)0hURSdgMoAUHoZpN(@X- z?!{%w<-~fn!ea1it*^0$x!-rck(eb=ik6alb2uve01b`vl%j$l8hzv8r1kxga@h?n z5c$dE#MH=X%;un%Zh|NTpu7e{kXwoX>4N0C6Cj1@`Mx#oHR<3pn8k4!u&?tj$+}jj z2-si!f{T?@2g+EaE+GRL)AU8;v6p@z9|U?Zi$kjKkllM-D=T{t_By~ZfYQ&@UjrhH z26tvAA8OonvmbKVu8#LyVn*V1)GHfdRwh(c5Xf<ZX8NY3YfbA*abr8y&yjIkdM6 z+DVGH5AG{vN1(t=x9Cpyze!=uR8VFr5iz}L<%n256($=qut^;v5;u%du1lzlj92u| zmM1DE+pqfC36G9I%#MhDtoMy9EN;A$TO#L#{QdpPZsX!@Y&tjjh!xw#4e~37!5oq| zK~jHbwN~M@C^0>~4M2LCTA%?oJU^D6)6kM{OF5lyg~hf42Nt8!IQ(#JLIefVXfk4BAb_ z3k<4h>0vWSR1VT!LZH6e(%YMk;IBdGT{El>0EdWiz%->`G9@{tWn;F2I6qvAZH`yA zwqp{paok$!oE%S%?Kq}+Ij+;lLw#dqwi7VjI1IJBJLQvFSZYFo60U6NJ-6qosr+J- zQt`-pU`9Akb;ZG7PAs-+c7oGN!V&}ZHA zw3b5Jn9fPI0^CD6emS>#YK-^C!Hu=#eXMo{d1bOgj)jp~JuheKWACIVS9lUeLy~bw z-i@+%{TP2L_yc~qTXthA$IJ+xfK-N2nLeWXT^}Lw7aFweV}I7wEhQ^#p^evm>fA9l zCiZX5xF8a`jy3ELZxFbj%w1KFy@E;8A9ドルwBr z{G>tgbUpl90H(TzT;$g-iDDZo7iIkw;b|Ki?4&B|-XSa>*K=SBF$xAL`V7+cDa!b*xZ1Oi zZxtl{CH`eXnpJPojBeH*fV#UC2EAPoxaYTTp3dKT8W`HNV{k1Lp|GQPw~owR(?0XL zY&#R|zgc6)Jyu9pv)+oonKmrcuW|7|LykeQ)75zbv z$HP(7oe$G@@+IyJ+HZe2Pmj6Cg!E>YuZh;Sut?h8Ua7!>DFFaU3AMw`VABwv(|84( zoqo>>xb!;s|6W+gP%x_vQC}9NQToc&p)^UqKk_dv`BFF*s4@0Ny zG8;f`!7bqy27{?Sj(hOn6U@U5TZ9Snf|8M*o+~P`Bh|>zF91FrdU|>(Zy6na4`m{kxHM{Py9^UzaBr*C{9JMh@(wMwou83UOt4 zfn+wQOZ9UhY?yz3{lZqiVD8DcjiP$Y%Hx-FV&5G<;3e@dgqulye*qy@5f%+ob8~y8 zjJu-=L)6cuLfSzK2Ot_IE7-3Mc91wlF899U*Ab}rinNa!=yTw(vbJu4@yNC7lSQ|L zaLFM*Aan}CV)Zf2v34Ef_T_2HQrm6K@!PTMZxQ+%st!qy0q(aM zJO~`WvFKQmp6!zz^o8R`#E~P2UBVG?%o9F`uQV+*DiCKqS4#8&Lu{C-c*bd|T?f0d zJiR_DU6DeLlAeTnZ#Ull&5C)twWPEYO3V0od%!W1-|d;xA--ocv(%oB7q%_7FP7*) z5P~cqqJY1d?Sf>>D6hmgSW-#>V=KG0*qYI5F+zF}hDq}0cE3T#bg~Hn4*&dlGx(&8 z_Rd=DPD&K6TxLiL@U+I??FuHJF^Jys@F;mNtQYSwTh>{8nU7Bi(e}?Q8s!kt%~AyI z^x~=PxOD#K@)3DGb2d3H{s41^U_MKX`W-P$|i+JO^FL9M2MJ zN7V?_ZY{pEZ6uFEAT}9@7wOLSZ=MBVHvtbFq}!+)98spe!TvO7RYbpwV@0axa1j%> z$*ClPvdUw+y6KtiAY?Yc{wL_jt%6b(qgfw-u4!=iKpmr4W5V0XhLhP_$Y}%$Rka6c z+6*W^ZxWvgC-bLd<|+9{(o%wielvd^?!fmbgwh}gp@lk!z`&td6#%b8nszy>9=b&r zVngK9!nAvliZYuR2;~<`>PD467{6jU;JVVDmzn~$WgNG|CI8vj4f#=oz?b_`Y&D2h zWtwyhb!3w^RALB<{01}il2*4iyb8gy8##+qd!~_vzlpqkbdt+%m{cm>`v2jgdIMlR#@JXbe^K6Sp>2gruc)K+m_kq*V;|U=BIw^yR7UgwoPd^I0a)0VzbCo)1SA4>{0W zA39It_--PUp-Q|Bu7!k^cF7o)GVoeE`@^mPTOWS@2Q}FDdtIH{593RfCja>_--c;l zRGZO`oqNZ_`u9w{*ect<1n&cxlnw68ドルfi6vbg#%t(|q=zxxpiu9iic(2k^f?a6{jo zg8Jw4fAowL3BAA}h!i({)@xTV+89d&Lkhv}lD&*CV`l%2#X@X5o zKvz;mhI-w5k=qUqUmiVr$-yDMdf|{R5bu$kX-w{U(-iIdnh& zNzhi_yKB{fY-&K6Z12g#IxRM!|LNBJyB61)NiV2ドルyS${$W+xqod&4)DGkiESFQ87W1!# zaK+-3jEwfL&l%oDM5H|3+2{u!r%=aV(JrZ3Sxr5of(mN^o@;f+6X4U-Nx@ZQOikG! zG!t7}+YYJ!#wY6>yTVa%UWM)XP$#hS7zfXecT%=Ji9C=St^d&Uj4zojr}X&M?qzZz zlgYBso)geHs!K03hkE<@?mjr!;o8tn*ya~ow4g@v^f^ayyaf6?-c6vuy?|my0?!_* zE^O^i#W^{xY?9V*fx+g&??Cj51JdY&8yAc`rU9_Sn2?#-@V&nNVvOq$JvIsqLf8pG zchl~%`VxIWjfT+#4lf(!)2XPzW{47ドル-A90%(7+u zDsk7pdF3I>@4uiSJDzAKwz9FM;^tN~4?S#?B|=m7MTRx8+lzwG@-XC37x>v{%QrIf z$Q3(VxGIYrc8EVXOQY%blUhEh1&cjht7HC!aA#}&3NyUVwm+0Ukq2G#M)QhQv__lR z00bzK2N@Xnj1+d847vRK`_=6LuWEq6u-yl;n|_W(@Cgxp^XP{KlswDk(;51C=K?(T zGX}4(yQ8(Wv|jUDks~F6yUQFJVAYyr)m!M$VRpYg^=_oRM?RPZrQ0G=2n;q@#yA{U z;lA+!D9u*zS~0KS18VoSv?}bZX+or=iI|5VDJf(`6VerH!A@%sw%yUvVxBMj4m2z< z=HcUJ-M4RFq%_wZK+2*!K{gC4X&f!#ZP{2Yl+zv7ドルYhASI3 z=*t3L@Os?K(Eo&3jcFve_E^Bo@5z1;XpP1Ck0sOWZ+Jb$g=!(kDfzc`ceBqY84{r1 zxo!)i{L?Jr7dJgCGD~J;MGOmX=wt1hM5y*@mQCS?>{ zbvohoTAC@DbuJ2myK9Chb9ZKfM^2MV`2trN%z^dUooyT#mtmZ{A9QTVc5GeQbkNe* z=Wr;h8oUKJ9LkXR0rzx+0?U3=u2t*X*RSIm8Z@>B=oFgZ5uS!SdbNCXiS093`2fGF zq}R_)&-LyR@ejxr=U4wP=`zEx=t6f1Ek7BonH+&ZZ4C@-SQ~y&xlDBDHnxya^jxjt zf%=yGvaW4c)>VWbELt*^%|FW}zPSB6tYWNLm8TuNZ;~P;9$NPmI~4T7-(cls3H1}0-;faKL*cNRAs3AM;EsrDyeLK^Q7XiNl8CB3JkP9XgMDpEbBC~knTmmv}B^?Ls9mtZMY!IH;q!G)lPhPUGT!%L9+dhW; zg%eoQ5^(PihG7en0Fx{eVco3C;@)2i?C=6yI3Z?alWtJZ;5t+ED~Op-g-yE8@%9Bz ze%?V1`K3FLTZ^fY?VsROkU%%Sn^9a`x8^yCVrmAxFs`oss zd+a=(t8w$)jh-FhmmK1odwQ&GAA^M-do8y|`aHbioI*O87n3xitqq^dfBUie+KZ+L zvX`Ld1XWHXtNCJ9%QOsTfF)Ym+V`I&-sGT6#uENCKHEW>tQNYGApybG^(x49U>atDVwHMTl|HkDzw5J0ws? zaM+x$<%9~22bhsi*r<;u4qsxqn5pbm?d;$*o_t1f_#zb6=8gef4vuz(6sxdfdw%{w zK}je$JzY2FQ6SsNZ%h%Gf`sTU8bhQMKUV9#*k8;uLl z%6a!i%VAZ8_2ye!FBHFaF;x=U(k1O-892^m7VLAr(#0VRZ?n?a-* z2Bf6Fd%*X-_jCXM{quV+4?c%E=gdBPueJ7Cuk~8%81_s>j_4ZIH7qPFB8a@S8Wz?S zC>9nD{3;qd7804Gws&k`Rrn;K%o>`CIUr&{Ru_+*Ojr`?t;-@i^C3WHmIIwQ+lJ_le%|3j?huAn?6fCygaQRTDH#pvl5&Lr-!B*gaV7iZA2z_B2z)gU;{*$K-h{2h3MA;vr=4baQT^k2; zn)cQfIJz;AhUGKejAgtc72|J2gt_1*jS<_x6=nqif10arr9`y;&hc<6q#vdq^+#%{ z3!52=5i;Yw%X4@4Zp7Zc!Hane*1heUm*a9FErKY(i6=N0u6RY#?vtMkE*Tu0!XhQX z1~Yy_@e|kR??`n2jy{#^v3lXUqu*Y|JfvaZ3m#^5fp~Hk(|Yqqc{J(r2I$`#)|>>} zs4Xua_VXeHtVkNjNJSG~m_=An`L(_7#`_c@O(?3N-TTjzlTHPqj$HcQ(|Lb8t152ドル+*GgOe_u7w%jU@h#eK|*#FY|AG+iD7C z?)}GTEPBBib^ZS#Z4pFs+S6DZH;f9g#Odq&F>1@GwJp z)eU0*YCH*}aiLI+uguUc3Um3jM!H#f#>|`|62;h2+QQYivB&zd1ZBeiX`7gp+4ドルfJ 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ドルRI4;WGA1DZXn~cf4(YU zqf45c>){bbAqZ!mux4U1`jYd!@C!a0Lu&_Sk8HeC-tsOMY=c=(N7e*9Do0>oZ=m0ドル zAH5cO-D33MJq$w}I*4fNNdh>w_hoK&^8%YaQ@0Q1 zhbjMV__KayU%%mC-2NmQ!^8tB9T`ai0PXO`yAj^HRQs!=xEmfYn*Eir7~;#}Uta)T zbEU1^hKUy?c|vB`PWS%x@J}y1yzPEvo1UtxaOSzaX&gVsX@y;WY512}P;DM1b;*?p zb4)ek3C@L+A^b=NcVve8g!9%Qk|nrc;Q!>zA#Vw1)0Q#&`7cg* za-lEDh;qVNJ}}Vxi8gA}zeM~!jJ*-^A4gy;9rd78hrsE-!o}a|Zp-BMzTGIkQ741hG+VT+5nIIQwWDb#^cX3u~FvNZRj^Gb0Rv{p0!c9yV6=>yx&X4+Wut zwOw^9NEsF?zo#>B^EXRe7|<7rs?o86w|na-x1?1%6SN-xO|BY@ISIrP`EnJOwc7q8C6^p6ojB$Og%3 zM?Ar?kMN3_YD=x0lOyNQY0qd8g5m=s;5AyPOz9P4xo@tOErN+4X*vz*!4CW;CnFVA zIbq*`UPAzKQk#xXN}Mt3nNX%^x^W5R^|`SfdX&2Q#z7r|V%WImgF0p&jAM`I!f(F9y@?Pv)BGRiD=> ztn2M%33{w`r~BqNkrR2FxfUd!GFW)0#%5xl8*=-z8S@kubrB6_XL4NDJw63Ej}ZWf zk(rs{DYPNrH=k(=8C*LZ$rGZ|0<_59?y(oe#fcr`zlzu2lmbs9daihekux|wv%%t3 z<(ojd39jte#rke6wfamf?)c69pg#uj!8!`ay{bdfz#r1d<5@q;smsken`xaga z{kkm)xXCeoVufIOukH4=H@jQyBz9ll-co=-5c$uuc2}~Z*hp7TIx&RXv|eMR`m0yDW?&O!faAm%eg5cix;9aCT_q7XXSSZ>9eMdV2&ic1ドルZ$eO7e@ z9-LOEAW?`WGQW4)9M#JVonoW9h~ZD>TtUx6v=D|j9~m3717(PpLNwRjNP2ISckwyR zKHfAK$WYzr{|qd@2Z4%4hhfA)w_%~LZ z4b%Hs()+Fajf{;0ES*TPeqiIl{mc|NkTZs}pDY8O6*!*hf}DHx%!0aV^I7%m$r!ta zbI2Fv1y3{_Q18z!yd9a$cuuieFAQ1?t%S9huG=g*%*-hNm@iFYq> zI`Q%9={ePmc2ayM$fQC2w%YYSf^k*Ro!{}y6g?6vH zf#0=(45_1ofOAhGdU1nTjc3n3fnJijb^PF`Bg4a&l>s(x?kLetp)R2%>AAE0T1{>n&rYH=_c(H-bB*%H z=K$@?uzvbZR3a%E2D7N`I*)M%dJWAIWXBNVVB=T2EXeL?KYt!`hePL6?VP`UET38D z4N4ドルDooR4i;urse#C_9lqMR;1^LPntOSOA9d7`|)KIM0uu2Wg3Wc(o9 z=IysvWIW>9FO~;NXUFIkW6x=qP#{M}NWg(-Kpy=GlAr;UUH3|y{1o+T9Y-*R!IJJ< z;WWns(f|o;r}T%+P(j*)_2u|oZnL};;q=p6cZIil6TMe1#CDr&B z74j!@pwGwVdn}XppZU_2qgz42JoNG+^{qRa=;_gjg=p)!pWuP;-#DDA?+2 zAO;HI@4wDz6bn|p$u?T-Vm2vJN8E%V`z~8UMnRi_x~yzSn)liRz>KTUh%Y1Cf@$ff zg`7WuvW>^!POj3&%qWSoKW>Rvu~zUwyllsFDpNcV;uqkS!ca8Z&Lx(rY%6G{z3W*^ zpLnK76ドルf89@5O8BcvExwQ8+Tf{vOU&!A2}(}`$)5x`lb zO37gJ)H0lMD`BLXjWld`xuPMbs)jQ~T-}J`GGa?i9|FW@ev+|rn*rNtE=iAO=HBqQ zHRtWfl>jZ;;r&5x2XSvl`(~}t(o6>Kq5BW0JVt97Jz8n6$!W|2GL3w%6|ao@66GGz zdHRDaj?y(Kk6ドル_z#2$n}`BcykvVXxror>i<{ytb9jw@ii1ドルm&2*w9gk{)(p2v9{i( zh5jMOp-(sZDUI_S;;KJ0o%7o^bAc2;Btj9CWwp`qJ_P~$y%P|0x?ltj$sxQ4aCjs zZe5kjK($o^Ly8fwQR(3_?D*+91dXjIr)hj}*BWyX-Kglm57fuq#k8+{a&lP)FfP_4 zs1{8r1_u=!TRX@x;p{IGXpb3#V|Y(S@dxVzNB;zVoLOMsj1Y<&)xrlcRh7!D6OZf;hJ$qt(Ed2Xgz8@M^2+fK%|FK75wF_J0F&5 z@@)qlB+jL)IkB6Lk>pgVi3kFAz+JY6U8>?Pb{R+u`fpo!@w2v@`8#fmxpQgoeHf{g zkG)ng2_9PUxO~~03pt}X`pGXoLd>Iylga&B$bG60^N48d2UdP!3oU|0T732o3e-Ug zUQp8T)4ULNa@0jJT9_;RNnTp7simP7>VMEL$O5gL!kK+#%Z}k6ドルt`ZVQ~JKn6E|hj0}!16Q#Y=IN;zOV@0Z z+3Y-j6Ec_CZg95?(ted<9$cwd`;4wtsdm?nq89jbd0mz_lsd!@cv_@7jg?c<}%jjjab0z@*x z^Q=mS_fB)!T&WmGRyi^cjYbqvtI*|A(ZW$IPzM3n??+i;aF!w)&_*TOqU$Gr%ue0t zYfQdZqUU;#O8f^*TqAb3eXM5PTG=(PD}5!dNi#P4o!pH9Y7&FedEo-KvV(#`1b5yA z8g6h;Ro@ng2*+Pn-f{#RqEaFo_(Z_NUwdC0iU$j zM?Opv*gmOjw?x3ドル-m)-5|yi{Bd_64d)AqWgVvNQH=T zUKxlDZe#D#*L=zxNBGa13ALFae8WY*qe`qHpJFvWut|K zh&-ZDyWNe@J(tTfVh-saHu$(e*Hx$r_N`InxPZV$*$F#3@&@VxqAzj z-$I^mYjzNlx7pCnujlhg?*Uh+zuUTbW#gZdabeiXJVrnimjbE`PE;N}n*PP0E5a>s zON6JyA;{C%B8zc#&Bcq#IEeu=>6WcU9VLxhUe-%fik=oS0)vgxmyRT+8J9$PXoIS| zwW`eSG`ne2bN`9EQ&h=8Nr?rRe?DtZkt$t-?A)JTom0~@nTFFZ3`e6W+sPpL1*PBg zje>Adr^Q(|!=1Sve%E;dk=ffb@}>EPGE%!c*2v@U&3{~p2nwk{8%Z>1DC;p9iX8Oj z^merhT`-Co!Ss=_CbpLv`z`)M^g{*ls*u_6G>*i5yLJj!qt=|=h z`q<2;=tm5inwyxjrm|*@j#tpwpwhs;tk{fu8fdub%s$}>kft~t^8qAA6_xpE#!9D~ zb|fu^zFRu%%niB-SGy#ao)0iP!1o2ドルX3Mc(12N9MPDGD|hmG4%Jw&?wa|8-!78Ju9 zmcyUxH6z&!#ki<`h8+$?1ong*{zljw#d>*vDho_mue^3GyP6UuOJ89W6(c-iEjb^8RSj_%@E6MAN@bgA{X`sSaHAz6DUV_a9xK($y+!p)HJQ zcjRp!Co{Y|6=***e_79(p+$_HcUpDYg4&SZ-tU;DsC8;C>eM1p?l7YR{W5#4N(ph_ z8DBoCUul9uB6}wj4`gDqJDkvuqI9bzL{3F>g!JF&I*{MIPmr^Ie&&@+*JKf^CFT|( z`zyC#f_{)1rnq3V5Z6?`_9{mBWev>QQ+Oj;tl)_a-7=&_Hm{uwhD6!AR!D11ZH&Q< zOP>R1ドルgh42ToUam815c+&aKO6o7%Eex`&I2jY4<%}t{1o!vh4 zl&AzC``+s!p0rMt0pACB3R#EG_Ct&M7mC-e#6`3v0o0}s$NO@RbXa91azOw84;^)<@nrq?qb6l9< zQ10-Otfu*Tn`8;IIxzmx7a1u=NK4cnA@%!p1A&I+!n+MW`I(`dgKg{ALv{~Z^s*ef z*5c!qMz0&!>v%LO*RRqMMUL3rHupB0=@l=T_B6{gk}A+9ednTZ(FurH$!`$PE{Gy6Pv z5cDf41Ap4%shx&s070G3PppAqqGSjkMs4oik@VBhGheptnGV`M3Lg$q#qpA{Mi0rf zT%tZLyL-h}67NG^11}4xVgMKhzKxH`#5%q=bUoZu0H|P6_YTo3?z`T<0|mkKlaE<;i2~;ws9qcbd7v3x;5_^u3@1ofr8jhp(gpj z7eG%`g6+3`UNTswMYFPP2X>FnmCIa%jfyn*IpW1#au_oHr&PrcoXU?`3oI%CFTnaN zNBQ6l8Pfg!b*d8HW}(6lCTKCNcOHS=FZYSPzID+uEo<}_4{s7`i!m`4(78i$h#sds z0hB&Hw#qE4bn7#TAE{2JE>`B~eGkKFhJMA$siP^he(Sga&;{5)142W3@GqEpa z#q7@kO0(k-=Y7~BCE@I}Raea4rcYKheHZ#AT|IdF*c1;QQi@}=K$F*Ay57$o&3J0z zS^)Y9YXNIMX{1*MnnngU;x`|Fw@K_!uWSLP%$F+z#Ow>Ezv*Y&3SP!koAJ&mX!YZH z-|GPThdohG2sss`uENv!yQWsGMhD{ zPP~d3SJT*KI~)u~$?s#@^kvVtLzQE*h2yUM6yOw%?~z?ziJiw?BnhehgGyb(w~JGS zqUrLac;#epJFFz8*&)du;okV|{0loDV4GSX2+(g75BP{;nxS7PZ5;)PNwoP?uC}d9QjWa17W&&jv+8c7c?t_%H((wpW32R za#Wd5A&l4_Ken!xKX?c#Mo!2dnDW#@UM2%WiVlJ&a|RC$x`L=c1&_L)T_a>{WZnRT z|JCyE-(3zVUC8o>LWl>pQZyhAEnQ4Ae-HFjltK;8WX#2p)`| zcLxAYOV_Wc^HCUsytkFh{|HrD3ewNS*bFq^22Pov5Z* zaNFYN^--H(Cdm0-BC61Km%V*qc=uLQOIz}Ab7sbJP$x(g20&aGEtUxwAyN&%9 zSv|3)M=hHDT&JS(fEgW5DxL00lnzQ5h$w+(-GJY#KfrqtU2_v9BgaMYOCBp9y(qq| zugx<10+*e5bwm=a5?xbp+?=sc7ic_lk`u(orl}vxeb3b9e*9ddxr*=1@jyh_^l7d| zSQTwSB>t>+RR4OVlN;z53Q7Bj6a!sX0lxFzSw3O*dS7jPAMIs9p8?iWTAd$WS2!%f;ADOG)t18Zr=i8xSHAdZ_TSq(!9y=)RmWyy8uxZ$t({LtM=R*+ zYZyoPWb4ドルSq-AD4ja}-$TPn)MH7Zy}KZjcHZ*fb)&#iZ#)6D9fTh`Ab(())&lq=NV z^)HspYV1g z9Y0w+Ds*3|u$^=beP!ASMA>)WMUlwIg1j?InDcOknq?)SAXwFhv;F1Zu4?&ePvsui z+tvn8b3v<(p-qk3zkup7jitspn{m7|l^!vrmlr}p7gi9{hir}kslx?5#tfw#ukuc+ z3}2Pno2r(p0_?2b2?OZ0^{Z-BbLYect%6k`#Cjy~ThHdS^GOJd__2-jf%Ky*JetfR zr*3y)s$ZgHFY0^$z)`3K*?~^@RE8)QUEy*9dI;d4;SI=@)U+wUyUcQrSvz}#`+G0* zz4>yB)7vf)@EzViXJ+)EQ|3E5S(#76Rkm}z>Y9K^mg&x}-~py6BUsSoSEqoUXdL0} zCMzG#EEyZsd*9ドル~mBy2{&*1d{|KD$R+}EoZnkpiP_|1mD`?0ドル`a;>ZRFuaz=-Y`Zs zJ-eJsJQ^;h37TKE@=iOOI_w-PMtRBLfLLUN03waQfK@%XQXPZaFxy#?ox~Qr?L@r% z_;Ay4#>>T}R-m=WX3dDeNfjnxmgK*^?T=|`@L{LoYlu^s1B{Lyh_!%+S~;_mEz013 zp``#nslAV}@7{tJ3ofhYR_NyTslxvKS95auzhe*RTsFoheBz$syk^`88M&>{<9pp{ zLfI=|V30zuoAnL8f}*kYs}RHetBkK0%~iX?;-FuBR3SAMsGZ}56m-n(o*Fpk`k?{E zSp0abFOJZt*AKtVP<;67o03-vjdqfo;_;a!(3ドルfk`q3hf^n~ll!q%}th7t(wew-1+ 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ドルPB=TfK00W}m`7A`jI<;$?ndoewhbc-w_z7yg@!kaesoggllxha}ua1vf25#&@h# zWUExJnO{sm#wx&k+O!rTFcg>=eap2DPL8!znL`GifC)O~(D*sy4dt>jH%e-n#M-3o znF-nHV@EoHv$>}_E7?YpwD8yO>FVFNqk4FA=9a&PMkw?C;KnCehI;AC~rG`ejK|nxK1f(UT8M;J3x}-s*W2F1t^ZefbM?c_oW}may zUhA&y(!x^OW91YLW9uz|oxgs(J;pfR4+`zJKRct<#n>DJRYL&*_{twyy_lut{-Xn8 zbyU88;p36Sd4BH;o0I!w%b*hZj1)dUf;I1JkM|m;vzNkrfKr(O8oStNhG8X{*b91Q z8?q~}PhN-mq1l8Lq4Kl={CdBX`v&@XI@MwXH)s=o7m1ApTo%UY;P#TQBH0vn-t>Hd7CWZ6maTJsDAHjJ$SVLp+#IGN( zQ-Gt8zXs=RT$Q+S!;%N3|v2n`3yx(Nx z`T%;kRhL>QtlemQmT$$K!?zNg3J;DX(Qdc&g{I+EGzkHI?}8M>{q!+;$}C>7Tcu~v zqCnC)xhdY{=F8cK#?t<`ompckwpm?;l2g~quk87b0rjsd7|x!h^h(6y-+gickuz5n z=Le^D%Uy_XI7?Guu&2Om(~#nz*37S65e?G$$dyGD)?b;91G(6H!BtLhWm3cf{&W2N zu~wS$T6!U}lbi%1e&ZowO!b*h125ドルGX)KJk zhdHf(_{c$x#V7+tN~Ocgp<4h3b7w6>rFbqroG<2$#_v&hxyv_|7o?Jq>yI(Pl{oDWQlwb%VX`fRFqB7+<=jk1djfvjxd?!qx|s3> zEIBuLssB5U+qjC)FR3|U zWF=aRYQ~y>Kdsy{wNse>oq12{{J>6NzVO`B5=G_DSNR+x~(uk-&pmB1HJ0A)I`49an9?ChG{ta#>w8FhRffusQ<+*_&s_nl2i$ z9&Voc${da?O;Oj?Q+RGG*w79|+4TJk{Hj*JcNLlGtPSSfJIvUjSuiktYnBm0kd}emYUO( zZKwC?73iHjSaI>JX=WdsC}tm`9F}Y6Rql=3VXySSP-%{Gz6o6U4^doS>}^_8R(93> zl%iL{VqA{^ooLCI_eA3nGiNEQ`IbVkvrK4v?)p1~C@Z(VQ5IDQI_Vb(mJmKSWr9sT zPNhfh5VNt^Q*6jl+F`W0mrs#%ro^I5xK;IZdEaPQQlIC-Ss$@J9e4T9xNg}rRcLHu zf(7T4pmv=%kN)oP)pisli09a@18fO!7*ov@pg9-eA2O6A1%Qcy&AUxP2AlD=pM;3m zkj0%NY-aOY`X{t_(s}}4R+iHFp>bYH$j=P%!uORXMWHnjXglFz?8v<~>u&e$q?(-` z+hplj&ezOC)OGB*#bsmA7v4PChbqf}Z3}>j1VM}783(u6e;)v@BCd;hb{SvniOgjpIE$dcs#Ecwb0lt@HH;4=>FmC0Q*5T&_Mh;7Jis<_-qxq=nb}skfb#q+_l5e;0wnoykwik z50`x8ub$NzNfwfSb~V5b=}4gbD#N&*XzKVv=`oiTlhu=po#i)cT|(*XPS{JYCz<`t zP7nFt4AcFnq#$W@Fx!n$?K4->jdkZIgc!Kyf#&jI%iehP$=90RSb%?!3AwMh?_ccf zzhUG0Adc)ZgE3W(43QghJ46XSKk(PxeDJ+;dydZh+`uX+3rMRzey$ zr-27)^!H}{B0Du&`lr#~fEBW`aj3F#q==}8F~fwEo7~&pdi~Q+r3;yH{**veN0j=o z0;O`yL7}NX0Pm&4fT6PXGWJp^7CvZaA3)l~w_B3aiTK^Y!P!@5%h!4KCV!=kO%@S9ugBUuEK^hy$oTofHxmqa;c&Jnw`b7n&1p9muU+Gk$X4r2PN6ドルcdy zKBT}ThDvlsb94P19b(p|Kb%Ni*sXWY-5rNJ{yD`>aoXNBQ*;tND3?=Np+G&V=CAs* zqdV*@GFd#XO)k>A?KO9Bx~<%hVEFVXqJXqiprzA~0hfZphj8q}7HCQ1EgjYhSX1RaGq;~o|D2?koBq-#~x=jaQ z2hF2C6PQ31i9V#3BL}&0On_RlTiG5|tsavu3E{jJW^;m_LIVE%mVr06KFn-?+P5uE zdLqseP{XU{WBfLS;N@!pItBXHhen4+;jijizH+{(=^SXc|mW z{pCIShdW;nVW}oxcJ2%kp8Zrauv`0o;KS~#TeNY^pPdb`!<*wtud+j#myp1s0qz%4 zPBwxPuU8j0xJVfq%K{c*ow{WMohYDjXprbzqlusxJwVJ#=bH;0i$fFNmmMNa#nxZ9 z@@Cl0YeW5T`Dd8$eSfL?-}W8z;!a@3zjq4N z#g-$a>jM}ay^_kbZP0V9ZS;}j@kMo-Ui1B3-@#T-Q9rF$gdqkGAKZKU5(@`{07clp zQoBgp!#fiZXxWSOuh(Ixx!eOCm|k3|sd2ZCGh*yy#QUx}zmwnV3eIM8FC3ro-5hQf zU7x}+lF>mi#)Rl+QQ<=_hktzod#r18c@8<4l`8m58kw|mutsxu!`+mm8leqwpf#nv z4@_v1_4wAXEXnecIq#wLOI17)D8{$xSzoyj<34kpdh@55rlvlvjij=csn*zsgmb+# z_8FO}M5_ckj+Wll8Mg;A_C+rI(A$>KL^D}({7|AmcYV6?u-g`0)lg8ZK8ceOq|_@X zT&~!XL(Hs8C0txChnP#8K1+kWXMP;_{X_Z8m&LWjGqsEFcwV1Tml&cmkK_XAOqPwp z{{|EU=3aj#vd$#^K5+uOJ_~C!58!~TFi597fZ*(g5rwsHzlMNs7Bs1e6mUh92&;!-#{@2vsd;n@pEkmD<^vm z4Hvp3Kv&Ml;f3?L{k8?qXh?T@d~TECl$!7TXA-S%>xB^wYN|)_SFYpF!GRQ|%f3LZ z27Yeh5^P$aC&K|?9DpuLJc$OOA{f-4pS}G^`m^T z0TYH8?EULj!v)AwYN=s2J2ドル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ドル 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ドル?rB#rv8cFYWz;5kO?~^c#cPIphF=o*>7G3}0BA*C~5h)Hs#l+=BsuJ$muL1y>o0hUt zhaWAmMsw}9MG4)hdkkJ4R{JajWp)o z%IjoOfq4X}cpkK7j{ElkgI;$mv78X}Zb>FUJc)6ドル+T4a+5YTf^)#B2TP4$=7DtK`K zdHP{R3&~4bMK7#Dw^?q zisv1Rof4DSO9DXIx7+(|ZqmH*bwDQ?W9N{tf`Ac$_S(z&XqUqlZ~dD|uUUBhL){23 zrus#T{-vb98ZA22APv_co!e4Vc|mMq*>sw(u8au z3TP;bPiIY<_ik5fg@n}8?$@q@mxs2w@puo5m(23h2mzo@d&tg^fe`l$<2#_!VdX#b+E?9!nW5vuP5ドルP&y5MC&Z~i-1^f4!VF+ZQ&VosJS(K*TBy;!J!yve!UG_z0_mt%a zzn0#3j%qhAP58B%(%Y12#9`e72>akbtxrO(fY(cUab2`D2=15eJH-as$MY*`6ELJWc; z73H`hw96!+j>YczNp9m;TO$Jx%Iri9_SJZwK+;tFYMq)T<$?da59ua-uz~yt#mgx67c3cv+qbrrf3{1`7_hag8r#m zZ~-4}k6`LA0Zp?*t#lYs;44gU0x@!&H0ドルD+dY?+*+VPatA`Kku|CFj%yaB6P@>C1d zxyzxPYok-ty%m3-T?6Ex4fi&?x7}f#uWB>;T z$_8?zYW&w1c>p!OTkGa!WJLpwClXg`XmdqVc>4QjPb3Vez6;;D14um~;2nJT zU}oY&W+-0KJzlAX#n|~)H-ml_cSt3nr*uJdOB<)8#?c<3fnnreu~%!y`d0oeeyr5v zj~;G}(|!GSGVoDtc_$d?D1oSfi^uy*?_GC_hJkHxD6E5}YjUOdM_-MaRETAr} z?m@G2iLoJ1oc5mQ4+H}m4iXgewZ~V|>8&POfFK)s&?|utqzvJ?oT}X;Y3dM&9}B@Z z1*l39{xhC}@_c_zS;U?KeFcll`{>pt<*#?iaquyi&x?jpj$ce(*g!lsr}nxiuqa

s;)MP`l7`22g0Ntf~!#fm4g#Gsj3x5*~ViL_oHx>6biXWOr8f?Nn;oMdL$E z1~H=USmX#}*bN|3zV;(N%rMf&wULCB;iGol=__#rIx4MScJFF1LHA|>1(84blqY#A z?|`Q%u<+(47!soyliy^syncjhXK7%s0dwNo?PHxMc4QQ+J08kbIWxN|Sd7drPd50=b3a63ドルXrt; za~ltGAZ-Ib6yNa}egqTrP|hCQmiF&ejT3;#~d+oDVN1Z?v!`p*Ij|>hH9BIqSIfSZ88X zFPIV_IjB08L{F)ifcU-1UXFG8<;xzo@5$g<3rjz(svad6>e!e7zM4sCb5i#AJrPI> zzyeXftYESEzvu_+y~JJ$>i#8GYB*dg5k@2p9u`fq-m0Qx5$C(wmQ@@z$X_VbB0W0m zRQ5A^mB98bi`B#9>Yeu9@=z?c3>cU|bE~nm5EV?Fm%5tDLZvS$*4Dq7@J1_%sS1>B zoSx{Y)oEsMp!!n1@Z~mqd+kpNB7@3EHavA7K`?~GlA&xQ@7@u)~lmpAS zmM`y-C5#z-7*KG1hyXZ=l*J;1zte&6hHZ6SXrwdvbYv1(U{8ApR6iQC7TRK3B6@6J z*nR{6k@pnO#XH5D8$i@ry}*@j{56^RYT?PGfT~LT!xKa-g;%hb6$bo{88$~EiOK@I zot|}izc?VvxK6|%d=V;v4BSv%P?_FZ<7a$a0{rtrdnr^d()$nipmxk2ga2EQ@q*;s&o&9|9;dLnz;zi(bJNy;~pZ_Ec{G{PRSYR5G~ z=!>gSoLyxl$_X~B0$VYX!HpenItG_k>O97kQrq=?L>Qq0974#z1jR>|G1f@O%@2M> zI=J8p{)<62y}{u8i8a>P;>CWsmBD)HKN|2K0sEFB0B+OaM=bX>Mu6;6{tc)+>Xj z>e@7UK!TD=2MmdW84@UviKK4S<}?9=$}r$l=jh2p!vN6(cjZM)^xyEUI}RE(~7! zy&W~{v&`=Rnx!;#wT{*@HvBnzp5bU2VCM&LM}4l{kpaQ|rX$b;*>4syF+xL5Y!A_O zCz2jCPy$@vM_8sMy|yJLY1S8C!G?^NLlOE zhJPc10n_%9Wcx$!L1-^G9(u_aMqV{_ECqnd10Apehfk#>GxIDMTZte|*sB+nA4w4n znSYh*aAajF64#j!rw6dzC%U(GX*2sQ*CTaifp)lnSoJbZM~5gToN*`QrqW7T^$Qq& zjh1~o-97)tZ4J29t8P9WYAg!755083ドル`P%NmlqKy{44n8zymvW5d(ZgNC7CAwP?!F zcB{l!_Ex`fueD3|4g*#jIeRsIU2T_*MPsG-3;s0J-nQNEQPO}Y=Lm%+yd@?>;r|x% z2XD*7ドルbSfGA$pE5yf?$ypnoUXcH?oN9B-K!c_~+8g8N+mih^{9UR@pk2wdHuN}1N< z4~)NtvXnW?x{)AE#+KK>^?bfRf^)#gp3KL>5~`Cp=<}`4tig6hn$cdl10ドルfz=!-}{ z@*4{b^TtaPf6^;qi>Yi>V9LN@Pt*ID(UNMuA#a8QwfP_StOg+u;~i2Y+|3D8OYO^o zgojJjSl|{1rLAel?W#4iPcE4QJi6B2l5Dc&c3^t>KvF{2nNLSg`7)|MrN;%>Nn9rE zF%iFW_#w!IoG1>|yQ7UyjF;l`FGU~_>w+y`f7%wgvbtojA^GQ#1|HyWc~S=@sDp~0 z@@o(tem;7}(;9&h)V<+8fn}x_6fzq~ry1o2q*+u-sx8tr1rf9vpfze)kzevibkzrt zdN7{JbyXjz*_C__8M({suujnJV}U|r**OEZRXxg0E!zdrG4(weF`i(93R#*peV-q) zr~R%828dr=NaH(Q&B_E1HrO`Wpy_KQL&F$~r`(u&dNh8W&o0s@O;G2lHS zy8ZGh!Kg;y%lpLFb?K@hd;Np~pV5*MX|`oF2U!cfy{4NP;sp%q$!oGLlAZuMc>bE7 z&-xF6OM0RF_E8#*wQL`@bU~oo92syL^@FEEf#l{E(@gF%lsU)(JQ4VIxHA@f6W*oF zcFvo7j0)>Ydl-Uj0Hk#eSP#sE@b|UHc%PK#ARS`=BSwHTU;eWAhyEnMckMc8w)ic> zP@L`DHlHwH|NlpmF|dd}H_rF%$fRSK-glKYNep*BFp39R5b;|G{DzHW3&j5IAYy}u zCy^q%A~y#Ma`E+Zz$>Ff>47p7NcpE!NL_fzpLsw-@R1!2TWN!59KZ8Le#&~+f+(b8I@kxKD*zJaS|__|T@@8z0Dxz$ zwz8--9f{gAA2X~NW5W0M%CbAUe%L3WJN7yMzf>Qq=Us(p&MpKnE>E_*qbVwfi+QOV zds8v`%Pp&R(|iCA*U2MGl-oEHpY&rA2n#IA*%7o>81VP;I)Xsj{-5qGkr=>o3LJ*! zdo{9sP0|!YKo$*wT~5YE5E(j1B!CyKKgm?QfR$e zS0_GXw+rt*IN3ez16cu&kSoPz{FD_IQuU?tzkocM&%mKL*jSj|EJKR|$p@FOLq`i#1(!{3~-sphit~R&mkcuWYytBs2~4uUJiTQ5?bj zR#zIHr3k`g?`l4A_MHO%YT3>?QZ4d>s@+#gaZ9H!hn)uZjbC?@mbtlC709#mar^I< z@2LmAJcd@5>&SJV!IgKpC%=JXb}`)P-ew@Q4ru?RmxI~*}x&J(&;CL=!>HFZkQ zFD$ZFPNMhraxP-%-aOuJ8i`j-;y&luR*U!CPFZzLDrl(RIR9txc(U5c@klYsm7QaO zv2p~<8zn0;h;mc|yv^+(9ndi%hk&to|j_ck>p)V0OA%s5)2T!FPy4rlv1EqL;;(rd zJ%eL4)RI?ih_01>O0^mjQIBx%JiTe3;2DM^`IcDrOfN4A&y5m1Lm}i%ildMD?(Ado z>aF&hP9siFe#<5m++y91%a&kqtnbx32cdcnow=q3gvcv+)>c+26Ppvf7MJY4x_BcH zyWGBG+K8JDt}B1$Ae>qA;A{>eN{qrYnt=S5R;KJ?Jw-Xb4hZM5TJ${AmvozWrUb&F zPp?vpOv-#s=ku;jmh&bSx zOM6`TLF?}x+t;5ceR8iw#Mb9%XFpZ@87LXp*Q~a%Z$~DcU^pmS>DvxBH;pjVS!B#Ej`r2*&q8vx1-RL*J6ドルMLE z?Zt;UZkY#^5rd~QF9~*9MjXd~(#hW+78fY8$h8PcKUGR{RatqZ=cp?fioACDxmV9s zk@{16*}&lQvrUoh>-&Xrn;C|KqsHtz`#Qb>wu9_+K~DMZwS`jqlehbKNdyd=b;JGI z^IIC8$|=?f%A%{sX~VqY-llhJ#Y;=6mEd#e`m(_It)^b%uNn+N3=nb*a4qipwbEo- zY&kh6MW{sKBaRg`8eyzL@XbF zDl#Cr}En&gLNA%z&$=pk?$Lw%iTOjTJ(kY3WSY%4VQ;UT9(_3=vBNWxY;rEp1Uo09iN zo#CNr4@ILf*DF7(#x`-U0#FU=MjJoZwBe56gbaV+w=_Sl5RgJ7$%kn>)UDGsb>54` zHnxt_KhUy8#D=eqt?tn(J)Uk58HSIiW`zF@n%QUmZ?BY##LM0^uSo`WWKvr$$ei{k zoEfc__wPa zxhC^jYy-uAejb@VuHR@I{Q7oeglAIdE`I5Xm4iCGCW~oZIN-QGAhgXkZQ{x|g56~0 z1sc2q5*dT1l^$|`^V?GWh@QJ*?1Sd|;<<*>2XOtkp{TGy5~y4sCtL3p>u*+WuxsX+ z!L?;2(`c1*A1Qw)3rp&(A1=pXx&3fD{LL=`!i#y?U^E$U#(6@cnXT%s`H0F-j9%%l zL4l~V&K=9UKCAb;y^?M@-4Aro_V7L62{LQ@MzTC4{x;l zW#yCa#^x*!TGvXp()@60FnHbmw~i^C_4{8(RUDK0|M%KU;wF;q&L6saI6gVg=-e7i55pL zd4X&ZcC}T47GZ{p(G34y-46Su*DOjl@h>%?+OVQuV|3Q=9{eeGyAuMpuH@+O&No&` z2yp~23XjVJaDktU4XmtWVGQLEUewoILEqqQ(5f{uN?#TNF6C9yv`Tji8bN~ujZWYE zb=`VxBFOe-5}#SCSlGR@XmSq3MprURw~4JO2jdFi#U4 zUP8#nVq;p!D+pqb8@mhqq1Jx&PtgB-!L`}q?~#;bLZeI#USZK&>R8IW5C1#JL`Yl_(Nl;Z zTC^ZaBFL!GMU7r2h|Z`X;YN+od-QJf2}X zz4o`(`quuA(+#XRHHjTCqek;~V~+W3ドルUG&o4>v0lNGF|_RXz4+n}o(09yGHX~B;(0{B&sJ?Z`RLe^HwDDLepSC z=(T~p9ckn*oqSA?WVMle! z2Tt11b)cGTkp1|`S-SL%Rqv<3%pq{athzfuds96i3nbohc@mrMA6FxuwvPeeWhcdUy&c)U1d+UYKl5;>C3dKP7=V7l7_ae3&(t;~`(O!m#!cX4As(mX$`&04TgiCT*bdt|3JSa}!ngXy% z)~^fH^M&)P^+z+oZNNP3rF7<0dy-<8epwef=h@f`r0wwn{tgcfpt9mco|v*dy*vzz z%laUA3A2TL{=ofV0e@z2_;bc-AX!BtBI^2_@x=yo;fv3x;_*r*>a(kFN}ecM`yNEd z(cve699S94AX)GE0wb55~mT`X_1f#|U}DOZCm*r@@p zhN;s4nc49iRNsGJnRX=EjCBa1GL%uK<0fb`L_P5o3)o`x)IgERKco=gi*L*U&2u;H=L4JgNJOcNeD~j0 zTa_1q3H1vgyv7ce=HB<{r0kmwch>!>m0$hM6MC+lwZIrm2cK(p(-W72$#O%x+<8+h&piab54d*n=;r69st z3heWj3+hBRoR?7L!Sv9X7VYtn*8{~3`I%wo6JIvK%Ln=J#RC`Fd?sj0UjrK-r&gYg zG0-tbGZ~O_Ot@fgL;3@0LUY|e%LxwgyOf>2$zjAg`x5}6iQ*h?^$qJg_CC%<8ドルrqr zO8y?0OOpY9vyNS%Dg)Df!sEoSdD|mp`5LlKsShf+HA#`_h}k7j;*Ae7GX>&!`k9ye z1uJ#>0@#TfY~~X}R8xg~BK5I8^)J_H4pTmS8o! z(284_IQ?!D=xIKs4_&W4vJR%=ks>1_YwzwJIysn{G9-)WSsVZ=xoHNCek}LyJrAVh z975N)X+Kr4u*d-gNtq)fnoTV&n9O)ZYcd^zNGto~|L{ z8hiT}H@4J6cYZdf)og$D$Y`5Y^KEf4pgfoI6}crtj$%61%6XS1l3sYBQxX7)+XQUX zj9?jAPR&_{rpvvq=y0e%-s&|d=BC6g@Dj ztkOQq6UcThHxM!W09vA!bUV|&*H@hDjuLUj>;>9L1tldVp|j&1oZu;4>D$rAR#UaQ zIPqQ#qOMRaPu&6(^bReuv9XndqTpa^J{cPRx4j6j6X>MdMh)zEtL13#kRZ{(adU{j z!lg8=y7P)&>H3?lTElsbAC9|B7jhp=e^h#LxuyunK5BkcCbIcd`G%eKY5H>>Am0^0 zD3Ni79k^D@Xwfjj2h5Ja@VxGz=%s44q{9J}azS^O`l?oP^U6S53-s&s;M_Gwt@lHhn8gl1 zanv2#ftp)+ucPs)i0J4nP;f2@FSB1%s5#jm#|fa~Ggb!b`Qli?Xr3EVmgIcT#;#t=-D{*{EwJug_n94P303>CvO49SeFihsMO+iujh?W(KTVf(gS+0$=e?;quj=lb zwy}e=%GXneEtIh3z+TM&l9v}zmy3WlF3?DI8!I&nWwh8?=)&pYI?jBglsT7oO-@FD z5=Cu&a!Sf9oC-8hWClYDI?M`4t@aC@#UZazF2y*R;{t6kBFy{lee@U5e~6QR9;i)L}>cE893KtY0*uZL5F%dv7=46A#Fca;KEj$(Zwa)=* z{%-qFejN|*Mn3R!;46N5$)rau*v6{4zEHli_XiHxvsy$AeuJ(vfC-=Omc5a;)B{ry zL1rhwOC@IZo*ZRBTD=egiD2~j>(#*KdiQg+K>>@MzN>4Tx9ドルHmYwuBZvmsxDTnNx9 z-3ilc)?mqVEkIA1tU|6-44aEf1Ig#ZzEIC}oR0C&@1MZd4Ff>6*%8zULV>I?JpK6C z9iptIr4@*fm6a_OjRrmR$$p7wh?fxt-GR-3*JyxgE$sZIwuNCZo);iexf97q;_Wjv0tKYA{ND?63}Z7tJZF zIuN?YhN0do9j<^vtv`ms{a`_&)no2_m`kl`g8b5>pMS!ILJ6Vb4c6hQy6lt-9IgAq zysMlQq7{~kVS8>ob61_@=@Hv(!QReK@HakFE+$VGKzE&>0cVFjA+2xONA*<)w;ih6 z+@1b7|B+I!H-Lg|ceS*~a<(}bhf#9ui=r4{qwqij@yi45iacldbukale3mswmdh?< z-_+Ffy6!O#kK*=QZ}h+fU`EZhTuqpcOlQ!43IQrD1TEXny@BEh4%FqY~3-%Fg}0mtxkg-9W%| zsCuk%d~R#yV^4_Y$gYz1KU@GWwEk@&l~p`^&PQUu8m{NFpj8wEC2>;SwFfmD%RoH) zGh;>;O}sksRjt}^ZB0#c`RUB8th}P4`H$i%D&b&2Ytar28NFmyAc_ZND}!A9^5G>)_S zV#8-xwlLVYYfQM148W`RAY%gq0e&|~T;hdYVyF2opzC3T1L+3E0bs@5!alI-$otXU zXp)<2q&m;&dy%t_mn7SKJHvb@84`|KkL03UbKFn|DL6*f^ z%d)3>`ho3!Zz31&kStArYkLihZcAhL@gWw#9JzhvUy_3@^RlI5s; zQTMBfa~Eu;ummKTVTbe4dz+2dF#&9apN&!EPB>BMMX|F=J&!##tH~<0vsttf$b!r7 zfb(SG59H(0i8;QC3rx9}g6&5TVB3nNigD`!pf?{G7IqhcQJwW@+8D| zdaq+!%#N+CEw;z&G%X>4wkt_Q8;3j`!F~+Yo{&YdURm36n01E8Uh)W;nmA*y41W`wMw$meCAmO7X;G+8K z^;Aa7Ll6vKu7m0GtZV1gdBI-79lCPvGgA3d%?_k8L8^CzH3nYk4SW$m(uXro2d{oE zFfWSAztmDbDE{~;aXb!~vOoo@OpF2zQ!?W4Y|1Q1wr}(lT8~67Vyb&QQp`j}ZmJT$ zzraU?lH2_DMvd3Oz-L&ekROEBfy3Ocz;{ZT;ZK?36ZS~c(9m!fhb_XS=`9X!9ドルH9d z-8~e6O1pk_3*&(T~fgp$rN8q0{&ICa}gGQ@{n2*g5qdaU#%zR4u~W#v2Y>v^>w%fPeT-H>+0ドルpheuqrZ+YKZLvT&2RrkBX7tylBd}*#`QE`b=LBB zq;R0^7@w0Ibs3}x2O$Mq6ドル=`P6qn_?z zuS9-J@1Co8-VH1r4EJasu8c**2*$cKbCuz;_#5qsm$Yh- znu-XK^fg!Z=xXaOzSHC6wL^Fi=C7Ug)bBUeKxpUgm(;tdC!>X2{<@*;@dfd>Li#+S z<%>o47o2-~$%lLT(hZtV!)rc@XpaqRbTUDywl1Q$zDqj+D^;n(4jlGHMebX5Dt+Fs zB!DNvY}=I}R|6YghV7dgDZMcXCD1bpxR+UQ1$`&SJbP3BGSdqxN-2Jt@xSr$me>|( z+H^@=oDY4+KP_`xd3ySs&AMHUYy2dDq%HM415sElS{tW5>nEz2AbT{lh6TY+L!A`I zSu=Ygn|C_eB#b=MR*2Mwh#M4z)pfkD?WF`1mzt&hpj&giT|0_v^c*?M)#Pgrl6xqN zjDx)?{VsadYxlKX(X+1{Q!AC$bjAabI-)@6kGYwydl$t(=-|nf>d8!t`#&Q=ke=Ta zRhu&&I21A*k8iUhSs^^aN}U@_eAiwOXjUx=4*`feocVqK7qd$Xl6*5{9ドルa>UHAh`H zO?q%o9{a|5=Ue_T|6)#2i2}T@4(g=F@9Lpdig=Ns)^$>yvTuqQhtxxJSB>k(!;~P+ z+a(guZf~mp;LjZsW8_Zoru4K`muzffcG2e4;2{YO{oG-J3#_G`ve@2J*LxRBl*f{Bel2#`FAa;{i7#XRVwqCgX0%3VZsQ}sG?ww9 zmCMxwILoX6hrHU8*9(>j__3;V#d7!8ZjhItwt{WP#z|k&b1Na`2N0Q%D>WE=N(z!& zd8rMP#DHC~c?q@PQ=Xpv4r)&DWE5TwvJ>UJMSFI9;gi;-r*iTap5p`g83(v<=itum zU(ixvK9XR1#NP8X$$Fag-J@mP9S}0Yer|N%=Hu52v*C<3-b& zW68qW-K7?o`=F;QN_34gKMH`H{PCRKkqXMv(YO40{?0Ztwl(El*=AptA`Gh9>RznH zI{K?k%@q^ql~(UhCVqsO&(lNtDR`uJ8%o!;LHB%Se=V%4T%esinJq7(IWn%6SYW=? z+WKPFZ1)wUoFd>7aj94ICFo^i^rsQ^`s(GEl?sQ;!q{X=WEmLYH$+gEnQ$S4OACZQ z+p7wRP^M>vjFQzcAg_bhoNiUt!Gb`{vu_HBp2VUj;&39@vG->Od*=o@rzSG$+}*{i ziCLMbM5^fqnPc!bIME|un`CO_9eww9F80`jOwgN=>H_B4)*nfMoeRt)4F~TUBvHZA zXK4rvvW{VZ1*&&>BSl*uxNLZNWT8jwbveWgJT z3xLWc2F&56ドル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ドル~%ojw&&M^{u@@RT_T&{WYu&3*)^VnW}q?P|Vr&m~>u--`i%obT( z0vTIdw*KEvf`D!_I_8irr_n>-w=kQ`4YG*Wqb$KBXOAC#x&Lo|E@mGkB8>;mW?0?|3_w9)w==`Jps z1u6DPupOB1p$!3we+b-RFqNSPG_v1s{knku)%p$${(gXns*a|{3lIX7+g%nEn-8Qu zVvw?CP8dPwx$Yfi^R@`yytuo~beUq0;+b+>7r%#H_H-)R{m{ekFt^`~sKXcui9N-L z`cIn5qmw#!$!z1`WO;Ffp{KvXii6hPSp)-O+D$+X?9P6H86iF?Cx+t?7i=@v0uZX$ zM204pH-=UEII=+C8Y$TJ6a<99ote5lohq2x%%lsqkg1&=p%yfr(_uzb9gz+m5)k$d zI94ODr4wl_L_!?~Mn04&z91=GRiAI3ZN2%hM=XQvNmzXBZIFeP!aeezD zW0JccK(=?ur+#_d<21ue5d->_*_-k;{N~RpInBhVOFb!)TSdIw$qOP+&j=v8UTb1Av9bn)P)|lWU@F(wZKjIOSsM zyHBaMoQ&+0ドルBBm^q+_md+nKVbHE1|+Dgm+k^>Qv%FXFNbnGA4sy*b_qtqzr3wU^y= z4e!tIi9e}+U=N=Gz8C8W9jedj63UdwMwo&GDOLv|rgu@KC=V8KC!~WWO*5UpmX0;v z3+Q$E&DxsD%T!(T6M&M$dNq4ds~q|AQsf>WZVgxB5Bum+JZY*1NYROsHkvVUz_*3= z;eGP1E9h=cEw}Ai+JXtCFBc>Su>-jmU{Gi+NL0525>scV3+5|e9PlN)qde)fCIn8z zor_!zzy{vLIOPH`wotc|6#`KOV{X<_ztj{y)awiqiy=y#mh{(7<#n*~iwt`6d|jbz z2rm;sJSQ$WvhHw%@ZtCecXlhkRUwTF@~fWBmj|n#t!pTBsY29zLHmaNBr>YrtZ)tT z!FB@$oMhmIF@icX-AP^YxEU|d(+-k+SG#D!v+W;bTG&aw4W+lg6STdN!sUNQ!Bnxq z_5_vHwMinaa6Qm(T4*-9!zc!}m5|$!_2*S?b8&T=QzYoS`p+Z2@Dtp0?T#{*`r& zie((j@y;8i*V>|^`uQMJn%G%g+(_q?$iuA$V8$Rwq-rIrWD;kR4j|Cz48Y5g0e7q@ zZP{->Jngwe?lx~rsBCJucmZ9qLO6Lk@c=jhj{6^OMh^3@1cGR4q?e9Uyu*AanTJPJ zJ*3!}B;|bAr`CJ(3kvCgw4|r!A@47_D>ixUoZ{ z6A%Nh+{Q9S4-|gSem7~CSY`#8 z6sYJw`N03=dvI;q1%fHTw7(qA=2gZ~laEppCGSYfK+6taR_-m9tke>_IMe2RW(-yg zRgdvKI0Nu$$?j&>G($(-XTyS6HZh23)*i94ch7KFT;zx0S7D*sQW0@U_-@#b}8>jf<&q-b-;@!tadx?ug z^B-oxgQ|i^mE=cZUNeX1fjuVQrOkCL%l2=nv#_x0=nL|xObI&cB#ZgKBA>Tg0YxHX zaoQ;xqM(fQ)GdjRw}>72eE9TGMQw;mnB`WmV&6to_bZfFF#Z|(^o<7j>pkw~& zR$+ctt!J)XKpvlsoN{P%E{W;cK_pex@pWjKXqIZ7J0Od|+5n>+aaO7w=_pSfO z{{4e$R{65Cq9KUw&{vsO;pkUHd(BZsW0Cqfq5C)6tizMO8id?ZRDd27^52Px!AJN5 zP>6qwxd;s6@V)7^#7j5c)8YC{%+)0R6x;S}^ld+9VN9-V4mbRV8KBju?X@ESmX^nH zq3}LDLDr?-ZJz%DZ?gyhG;%*L+u`>=St03ドル`jdqgAXQc8V&q@Dx$*P~^xLOBlBKwv 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

# z2U7tDH4k(s|LN9ua@XaIXjno|ccSnB8jYUEJ?fHtB!OPLw|BHPZsD|mLOX~=H{Ubj zRA&az9o$MA4;;nr*mLzpA(R=eq{4Vce-SZ;jINo>1DEqt`pEUUPf8`9s` z7shzl>n|DsWNM$-xOQl7ANF{Of^O~BsKXBWdLmLEDi_z8$t(7R(>ip5nz=Th!Iv;x6BS);i4cplg7xrBG$uGCaM0{8H4{P~%${k?ZIhmepEkUE9y zfLex}!c+P8&R_@>qPE+00Q?7&sWx2y_$Os0s3+V811ドルVo$A^%8wH@3{s06yc+r1Lk z=j%=}btUg+zUjPcxRjxO-RlIf*yG~jI+Ml3pu>e-$lARE_ZedSRtgQBK4uKg7%zRo z%`+|D-X>5-8V>Z(OwcG-qNmzr=1O3+h2h40yns#jj~tbOiHU@8MbS#1ドル%SJ{zkMX; z@xbnaQMUk4de7dlzCKJ(Uq88ey^0M?#!i5kl9bY<=7imx4buk}b%+s}# z>g^tVv7kjf|Fp=`%P!OBzuSQeIJQ!s#Qrg|eXkp;HGt8L`N!zKF5dXehtK)%2})~V zJb3^4HbwHnzt;PL(fInKaCHf){18wEweKFe?AOn97TCV^ONjq|9P_J zzwh$pWWN*&n9GQE2HpMTpD&~w)l3v_qYwca@q}2m-w(0-X6~~G{zl-q>Sn*#pY?!< zR4c6x9ドル)Ij{kjzC+S|S_;3*-$J5}VD2dj%f#YSt$q?;R5ruXCT_QSX=&CRZ(C5T-Z z+$~j3clD#@y*xb}Z4`)Q{&b)3l}eIW1k3K*fi=3-n^Dri5xwtM=!1M8m!P3Sir8D1wUC>TGst5}~1k6UR^ zcnULz&m{Aor1H~-q~Y*OG<;crkh=!yfq_?#dgym0>m5T^YeV{pXklI*cHf4p2b z5ls^laYLQgcVm~G~wXLX=EGMHFsYLSm)BFb) z>g4GYH;#Ld2S2Yqxs~(V2ukc$ofOssTz z`|-C(dTPoxPRJEq_PlSya2w%)6+(j7Vz-v-;A4SssZ(RE(^YcPG@pY*_M==UeRMf< ze8rj!U@LEKQ~&Ar8-paYpOqw_;f!L~`AkNC^@edxa6ih;Q<#^vptujz6)_ia8@b5y z4}ZpCBW7P?s}=>dflFW-#;G<%k#&+1i}=u7xv-#1v*we`{bv^%@hj4~v>vPA5k6RD zSKAbN6Fe;Q+ZOt#?0hL_q~a@kAOX$O-%BJXyxv-mf0X()id|TS0!6AhTmIW-)El@= zW~XEzu4ljmAYg#>Yfg>&J4+AM{TNMsc8L>#%ShmQwK09lvX9I5dy2-i!pKzK!HhoA zT(H%lKL|duU*Jx8`l_g!b?xz z$t~}+-sPjx*;yefYiRh>6ドルQ?^_J*}5DVg9Td(Dl+gQ({IVP?yoda~c1mUo&lmZsSh zXB&-C``2jk|FlL1XEFCJX5oxkK~!Tcr8Ame+HfcVk5*D6QJ6`kd2rrKPPiD6^^N{#QnMZf z0Hmy|*D5%Aa4=BNe!+Nix`B$vR1Sx(R!2*-I$}Wiv6zhqMq5Q&J028lGwC0V4a3|l zaMyn)*Y6t*2YXMLY@lm0dcb}R=BKMYr;j!~Hr%R%0N}wWD=FF5AGsZ@cO?kg_xJXG zW;j`rc0D~@vGE;6RdzKG(=_`p&xcaA5-MS#dZ`Bok+iU_v zYHO;+aW{O@ta?*YX?KrtDft$=Rs@KfF63k-v0kdWE_VD5RJ%^#JRVR#-vN|^<951* zIU#l+xHUIn-_r%+ypI8HV{8MOxJ~ZIvkW~zlJzryfaia&A*Yh089d5%?K!?q`;?WJ z<6m`gbcu;twl9mhc=kx4j@u-e4lv{rj{9kr?rwl(7wu}nwf5n<)4k4k0mrmqta+vq z3Lthg2-I=|b#5Y5sbNzpLv~prDk?3AC?YXR2n7KFF#^ga z5~@`yM4+e$L_qdc*06`9|BExvwDV~`&WGW7_!3C&J?A~=U4C!w|JW4Vx-BY9N$E&* zZx9Mc%e8nS6E27^?zY4?O3hp{K~p9^Z3XqyV&6V3w<&9|hxbz0jtefihr&r{wejsa z-0yGA*XoiLs0&%LIBq1P99xw8h}OcmxH$$zAmm6esAnORYKRyH`E ziXs5GIw?ZPoi|T@hWIW(ku!0jlf_E^SxKxV5C0@au~>0)!%=HTgyT_eRm^Ie7!5Gz z!1k+_H4P2>gLs97rha~YL_Nb0Kukl}(S>?AEj>LwpEbu+%Hn%V66Vd?SXx@fY13jN zb&KxoNx9XZS^Qz(z(b(d!=R>CF{oYsQ68%qD4l8M9_2kY($UeGdYwkTK3`t>c3z&5 z)d)zjiSfIW+sWhE(L^Oc%R5FZ0X^-)L)YdT<%z26jqfv^t#e|8hgiri7gy5g-fzyz z$SB7n>eRh^ucO8lzibxo)-4`-B5pI6xHAOcwcdE@Jq=@^rrEwEij>X@H;FeEi=PH- zj);|Ah)5beuSHVF0s*hDf_+47#K11A=qoN}Et=zQ#0H-m?4Ni1fBnK6rkTtnbBV=O z4Th)ZU>ln-#|l#p&5}MY!`oSELkmJT<(fuys@ggd7zit2o5^ilqnr}ud7q*kwm_3@ zD-30ドルG&jt(x~VAvRW5yYdJJao>+31idqECs@`&C#m|K+?fa^f#AT>K1?rR(S^C5@0 zby^b-qwn!}6AKo0Tfmm%%S8G!3PCJ0xyM!HFm;OF^6;mtjuLk@&*Ptg#4a@hZJwJ) zr-Qy6nz@5H##7v!-XolyiN52~-22gE%)Q+tzZ#ZM&{nhgTZCS#0Ag&cCKvx>_-F3kJ-rN zC|2IkbNBAJ?ub2YX6d6@y}r@+)$YL2Tg~wxNvIi9P;Cdi^RnzIoSko3+xje$YHxql zB2xMKKs|llZ1fpc&S;h=Q_IoW%RwPV_xj_rNm1(x2djc(jgmh|J zC)67H$EO~?#VJrcJ{ipeDtZ>|RogEfhF`AFcDG~*aH4gt|Az)cV`F+xO-kVVvkSaI zR&Wf>7JeQmzxKZS-4D|b^YFH()#{8+5E(1ag-A=}%6EaUEW$k@-)5W+*64v3q;!|E~m5a(L zF|D;uo;u8d?rxH*s%k`4NT)$cFdUWzeqpO&H4FPC&mf;L`mI&=kvADNH#Q9vB;UDn zhXfU(!erw>k`gA*BIXd(6fErfg;ZHN1=0x&uI5JB%HX@Z8~~=0ND)bsRZ?+PQch0J z4-AH8RAEo*tq7RyP*)|#GC;;o&eo*md@H;_4s2JAupCK*7z-aCA20OyzGAuf)9ja= zj+PNQt~;fvMNAN=odMue&0{E}0K=$jeSf6ドルDSPWwa3`BygCCF3H*eo|2@I;VKk(3b zJUltn!Ztd-pCQ0Pb)eDMIDCBNnCbS=d;kZH7e9WX9-{#%gi1h}NBz0R#Vwz)zdN{% z#Y7-W^XRY3PSFFUvj(F`xBjBeaW!!gyaBqb6iBoXcZ}>ppI#ZSJEJ)qtdPjh80cGA z+JZyNspA8U!VX1wD+z{xbM@@FzfX;Ab*SbWtHh~2XD(lUEh+5T66 zL|C;Q6iGW}B-Qw_=8F=uJ-AHE)9?V5nMX5sA2I8u6fOruZywo&NS@(21#|eByd}p@seJq@*NXnoQYk@HDW)(4m8h zPtIa#TtO;es7RiCpLyBrwb^0rR()`(yR_UjSO-%B0sLDCBBTn|`a!Sa+4939h&iZZ?kPmu+hziI%^M;#sR){ibh!>sL_`3=?i@Z+3j(v&NN`DOCnu-xd9E(1&>qY2 z_4RGXg4*2cb5R0ppzU1n9Wm<&ph|s(*7ybdqtoa}?_9>~LB`miXMXi`!b~jw-x()P z*n)z?Lf*d0pLpC(PFCL(Fz|r?{%<$p%%gks?3n6qur2!ipt-kyjkfi7ner8w^s@wn z8sU7Z>1ドルF{=pR2u(7c*6~iCwfa(lFC1PibRD zH7>$qCvm9(zEtwg@2v}A*ryji(;AB!G(7y<6$fmh-)6#)i%8pvltd3bohr^ndkusz zH@l2!wwfeQD4bkvKhu{Ksk<&bi3a%*ku#uo*(u#e_an*t_2x0up+zcl#p>8#A#ruY z=6P@@GgFJ0&_jK-S(H`-q`0T3ACPBl1Hw#o>#IZ$tkXM7C&)t*X@i_-5VlGR@1>$GGiTOXRC?>M Date: Sat, 6 Aug 2022 18:16:22 +0800 Subject: [PATCH 27/32] =?UTF-8?q?=E5=AE=8C=E5=96=84=E7=A4=BA=E4=BE=8B?= =?UTF-8?q?=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Investment/do.py | 81 +++++++++++++++++++++++------------------------- 1 file changed, 39 insertions(+), 42 deletions(-) diff --git a/Investment/do.py b/Investment/do.py index 83ff4200..88a42fe9 100644 --- a/Investment/do.py +++ b/Investment/do.py @@ -10,46 +10,43 @@ import datetime import time - if __name__ == "__main__": - base_url = "https://api.sandbox.gemini.com" - endpoint = "/v1/order/new" - url = base_url + endpoint - - gemini_api_key = "account-PsnWZPOnOf0FatEh3pWj" - gemini_api_secret = "49wC5psfvDhMmtKaNzmmVg42yYYb" - - t = datetime.datetime.now() - payload_nonce = str(int(time.mktime(t.timetuple()) * 1000)) - - payload = { - "request" : "/v1/order/new", - "nonce" : payload_nonce, - "symbol" : "btcusd", - "amount" : "5", - "price" : "7428.00", - "side" : "buy", - "type" : "exchange limit", - "options" : ["maker-or-cancel"] - } - - encoded_payload = json.dumps(payload).encode() - b64 = base64.b64encode(encoded_payload) - signature = hmac.new(bytes(gemini_api_secret, "latin-1"), b64, hashlib.sha384).hexdigest() - print(signature) - - requests_header = { - "Content-Type" : "text/plain", - "Content-Length" : "0", - "X-GEMINI-APIKEY" : gemini_api_key, - "X-GEMINI-PAYLOAD" : b64, - "X-GEMINI-SIGNATURE" : signature, - "Cache-Control" : "no-cache" - } - - response = requests.post(url, - data = None, - headers = requests_header) - new_order = response.json() - print(new_order) - \ No newline at end of file + base_url = "https://api.sandbox.gemini.com" + endpoint = "/v1/order/new" + url = base_url + endpoint + + gemini_api_key = "account-IZvU5Qvf8IrILPFw33yI" + gemini_api_secret = "UjDoXzvkTTfuGV6eThN35GpuqBF" + + t = datetime.datetime.now() + payload_nonce = str(int(time.mktime(t.timetuple()) * 1000)) + + # 下单操作信息 + payload = { + "request": "/v1/order/new", + "nonce": payload_nonce, + "symbol": "btcusd", + "amount": "2", + "price": "23840.00", + "side": "buy", + "type": "exchange limit", + "options": ["maker-or-cancel"] + } + encoded_payload = json.dumps(payload).encode() + b64 = base64.b64encode(encoded_payload) + + signature = hmac.new(bytes(gemini_api_secret, "latin-1"), b64, hashlib.sha384).hexdigest() + print(signature) + + requests_header = { + "Content-Type": "text/plain", + "Content-Length": "0", + "X-GEMINI-APIKEY": gemini_api_key, + "X-GEMINI-PAYLOAD": b64, + "X-GEMINI-SIGNATURE": signature, + "Cache-Control": "no-cache" + } + + response = requests.post(url, data=None, headers=requests_header) + new_order = response.json() + print(new_order) From 21d74ddb054c5e25ed21d5d1ce80b7577d174804 Mon Sep 17 00:00:00 2001 From: liyongbin Date: Mon, 8 Aug 2022 14:50:12 +0800 Subject: [PATCH 28/32] =?UTF-8?q?=E5=AE=8C=E5=96=84=E7=A4=BA=E4=BE=8B?= =?UTF-8?q?=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Investment/datacatch.py | 94 +++++++++++++++++++++-------------------- 1 file changed, 49 insertions(+), 45 deletions(-) diff --git a/Investment/datacatch.py b/Investment/datacatch.py index d307232b..bbd795bb 100644 --- a/Investment/datacatch.py +++ b/Investment/datacatch.py @@ -4,68 +4,72 @@ import requests import timeit +import time import websocket -import threading +import _thread as thread import ssl import json # 获取报价 def get_orderbook(): - orderbook = requests.get("https://api.gemini.com/v1/book/btcusd").json() - - + orderbook = requests.get("https://api.gemini.com/v1/book/btcusd").json() + + # WebSocket测试 # 在接收到服务器发送的消息时调用 def on_message(ws, message): - print("接受到:" + message) - with open("BTCUSD.txt", "a+") as f: - print(message) - f.write(json.dumps(message) + "/n") - + print("接受到:" + message) + # with open("BTCUSD.txt", "a+") as f: + # print(message) + # f.write(json.dumps(message) + "/n") + -# 在和服务器建立完全连接时调用 +# 在和服务器建立完成连接时调用 def on_open(ws): - # 线程运行函数 - def gao(): - # 往服务器发送0-4,每次发送完休息0.1秒 - for i in range(5): - time.sleep(0.1) - msg = "{0}".format(i) - ws.send(msg) - print("发送了:" + msg) - # 休息1秒用于接受服务器回复的消息 - time.sleep(1) - - # 关闭websocket连接 - ws.close() - print("websocket关闭了") - - # 在另一个线程运行gao函数 - threading.start_new_thread(gao, ()) + # 线程运行函数 + def gao(): + # 往服务器发送0-4,每次发送完休息0.1秒 + for i in range(5): + time.sleep(0.1) + msg = "{0}".format(i) + ws.send(msg) + print("发送了:" + msg) + # 休息1秒用于接受服务器回复的消息 + time.sleep(1) -# 获取报价 + # 关闭websocket连接 + ws.close() + print("websocket关闭了") + + # 在另一个线程运行gao函数 + thread.start_new_thread(gao, ()) + + +# 全局计数器 count = 5 + def on_mes(ws, message): - global count - print(message) - count -= 1 - print("count={}".format(count)) - # 接受五次消息后关闭 - if count == 0: - ws.close() + global count + print(message) + count -= 1 + print("count={}".format(count)) + # 接收了 5 次消息之后关闭 websocket 连接 + if count == 0: + ws.close() if __name__ == "__main__": - ''' - n = 10 - latency = timeit.timeit("get_orderbook()", setup = "from __main__ import get_orderbook", number = n) - print("Latency is {} ms.".format(latency * 1000)) - ''' - # ws = websocket.WebSocketApp("ws://echo.websocket.org", on_message = on_message, on_open = on_open) - # ws.run_forever() - ws = websocket.WebSocketApp("wss://api.gemini.com/v1/marketdata/btcusd?top_of_book=true&offers=true", on_message = on_message) - ws.run_forever(sslopt={"cert_reqs" : ssl.CERT_NONE}) - \ No newline at end of file + # n = 10 + # # latency = timeit.timeit("get_orderbook()", setup="from __main__ import get_orderbook", number=n) * 1.0 / n + # latency = timeit.timeit(stmt=get_orderbook, number=n) * 1.0 / n + # print("Latency is {} ms.".format(latency * 1000)) + + # ws = websocket.WebSocketApp("wss://echo.websocket.events/", on_message=on_message, on_open=on_open) + # ws.run_forever() + + ws = websocket.WebSocketApp("wss://api.gemini.com/v1/marketdata/btcusd?top_of_book=true&offers=true", + on_message=on_mes) + ws.run_forever(sslopt={"cert_reqs": ssl.CERT_NONE}) From eb1a061340bef7f9091a479cc66c87da98df08f2 Mon Sep 17 00:00:00 2001 From: liyongbin Date: Mon, 8 Aug 2022 15:43:12 +0800 Subject: [PATCH 29/32] =?UTF-8?q?=E5=AE=8C=E5=96=84=E7=A4=BA=E4=BE=8B?= =?UTF-8?q?=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Investment/orderbook.py | 150 +++++++++++++++++++++------------------- 1 file changed, 77 insertions(+), 73 deletions(-) diff --git a/Investment/orderbook.py b/Investment/orderbook.py index 7d44c931..aec22611 100644 --- a/Investment/orderbook.py +++ b/Investment/orderbook.py @@ -10,82 +10,86 @@ class OrderBook(object): - - BIDS = "bid" - ASKS = "ask" - - def __init__(self, limit = 20): - self.limit = limit - - # (price, mount) - self.bids = {} - self.asks = {} - - self.bids_sorted = [] - self.asks_sorted = [] - - def insert(self, price, amount, direction): - if direction == self.BIDS: - if amount == 0: - if price in self.bids: - del self.bids[price] - else: - self.bids[price] = amount - elif direction == self.ASKS: - if amount == 0: - if price in self.asks: - del self.asks[price] - else: - self.asks[price] = amount - else: - print("错误:未知的方向{}".format(direction)) - - def sort_and_truncate(self): - # sort - self.bids_sorted = sorted([(price, amount) for price, amount in self.bids.items()], reverse = True) - self.asks_sorted = sorted([(price, amount) for price, amount in self.asks.items()]) - - # truncate - self.bids_sorted = self.bids_sorted[:self.limit] - self.asks_sorted = self.asks_sorted[:self.limit] - - # copy back - self.bids = dict(self.bids_sorted) - self.asks = dict(self.asks_sorted) - - def get_copy_of_bids_and_asks(self): - return copy.deepcopy(self.bids_sorted, copy.deepcopy(self.asks_sorted)) + BIDS = "bid" + ASKS = "ask" + + def __init__(self, limit=20): + self.limit = limit + + # (price, mount) + self.bids = {} # 买方挂单 + self.asks = {} # 卖方挂单 + + self.bids_sorted = [] + self.asks_sorted = [] + + def insert(self, price, amount, direction): + if direction == self.BIDS: + if amount == 0: + if price in self.bids: + del self.bids[price] + else: + self.bids[price] = amount + elif direction == self.ASKS: + if amount == 0: + if price in self.asks: + del self.asks[price] + else: + self.asks[price] = amount + else: + print("错误:未知的方向{}".format(direction)) + + def sort_and_truncate(self): + # sort + self.bids_sorted = sorted([(price, amount) for price, amount in self.bids.items()], reverse=True) + self.asks_sorted = sorted([(price, amount) for price, amount in self.asks.items()]) + + # truncate + self.bids_sorted = self.bids_sorted[:self.limit] + self.asks_sorted = self.asks_sorted[:self.limit] + + # copy back to bids and asks + self.bids = dict(self.bids_sorted) + self.asks = dict(self.asks_sorted) + + def get_copy_of_bids_and_asks(self): + return copy.deepcopy(self.bids_sorted), copy.deepcopy(self.asks_sorted) class Crawler: - def __init__(self, symbol, output_file): - self.orderbook = OrderBook(limit = 10) - self.output_file = output_file - - self.ws = websocket.WebSocketApp("wss://api.gemini.com/v1/marketdata/{}".format(symbol), on_message = lambda ws, message : self.on_message(message)) - self.ws.run_forever(sslopt = {"cert_reqs" : ssl.CERT_NONE}) - - def on_message(self, message): - data = json.loads(message) - print(data) - for event in data["events"]: - price, amount, direction = float(event["price"]), float(event["remaining"]), event["side"] - self.orderbook.insert(price, amount, direction) - - self.orderbook.sort_and_truncate() - - # 输出到文件 - with open(self.output_file, "a+") as f: - bids, asks = self.orderbook.get_copy_of_bids_and_asks() - output = { - "bids" : bids, - "asks" : asks, - "ts" : int(time.time() * 1000) - } - print(json.dump(output)) - f.write(json.dump(output) + "\n") + def __init__(self, symbol, output_file): + self.orderbook = OrderBook(limit=10) + self.output_file = output_file + + self.ws = websocket.WebSocketApp("wss://api.gemini.com/v1/marketdata/{}".format(symbol), + on_message=lambda ws, message: self.on_message(message)) + self.ws.run_forever(sslopt={"cert_reqs": ssl.CERT_NONE}) + + def on_message(self, message): + # 对收到的信息进行处理,然后送给 orderbook + data = json.loads(message) + print(data) + for event in data["events"]: + price, amount, direction = float(event["price"]), float(event["remaining"]), event["side"] + self.orderbook.insert(price, amount, direction) + + # 整理 orderbook,排序,只选取我们需要的前几个 + self.orderbook.sort_and_truncate() + + # 输出到文件 + with open(self.output_file, "a+") as f: + try: + bids, asks = self.orderbook.get_copy_of_bids_and_asks() + output = { + "bids": bids, + "asks": asks, + "ts": int(time.time() * 1000) + } + print(json.dumps(output)) + f.write(json.dumps(output) + "\n") + except Exception as err: + print("异常:{}".format(err)) if __name__ == "__main__": - crawler = Crawler(symbol = "BTCUSD", output_file = "BTCUSD.txt") - \ No newline at end of file + crawler = Crawler(symbol="BTCUSD", output_file="BTCUSD.txt") From af1a441390e27628975d3b9104decc2235496152 Mon Sep 17 00:00:00 2001 From: liyongbin Date: 2022年8月17日 18:28:50 +0800 Subject: [PATCH 30/32] =?UTF-8?q?=E5=AE=8C=E5=96=84=E7=A4=BA=E4=BE=8B?= =?UTF-8?q?=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Investment/backtest.py | 449 ++++++++++++++++++++++++----------------- Investment/ultil.py | 22 +- 2 files changed, 279 insertions(+), 192 deletions(-) diff --git a/Investment/backtest.py b/Investment/backtest.py index 66e99178..6686e84b 100644 --- a/Investment/backtest.py +++ b/Investment/backtest.py @@ -12,202 +12,283 @@ def assert_msg(condition, msg): - if not condition: - raise Exception(msg) - + if not condition: + raise Exception(msg) + def read_file(filename): - # 获得文件绝对路径 - filepath = path.join(path.dirname(__file__), filename) - - # 判断文件是否存在 - assert_msg(path.exists(filepath), "文件不存在") - - # 读取csv文件并返回 - return pd.read_csv(filepath, - index_col = 0, - parse_dates = True, - infer_datetime_format = True) - - + # 获得文件绝对路径 + filepath = path.join(path.dirname(__file__), filename) + + # 判断文件是否存在 + assert_msg(path.exists(filepath), "文件不存在") + + # 读取csv文件并返回 + return pd.read_csv(filepath, index_col=0, parse_dates=True, infer_datetime_format=True) + + # 策略类 -class Strategy(metaclass = abc.ABCMeta): - """抽象策略类,用于定义交易策略""" - def __init__(self, broker, data): - self._indicators = [] - self._broker = broker - self._data = data - self._tick = 0 - - def I(self, func : Callable, *args)->np.ndarray: - value = func(*args) - value = np.asarray(value) - assert_msg(value.shape[-1] == len(self._data.Close), "指示器长度必须和data长度相同") - - self._indicators.append(value) - return value - - @property - def tick(self): - return self._tick - - @abc.abstractmethod - def init(self): - pass - - @abc.abstractmethod - def next(self, tick): - """步进函数""" - pass - - def buy(self): - self._broker.buy() - - def sell(self): - self._broker.sell() - - @property - def data(self): - return self._data +class Strategy(metaclass=abc.ABCMeta): + """ + 抽象策略类,用于定义交易策略。 + + 定义自己的策略类,需要继承这个基类,并实现两个抽象方法: + Strategy.init + Strategy.next + """ + + def __init__(self, broker, data): + """ + 构造策略对象。 + + @params broker: ExchangeAPI 交易 API 接口,用于模拟交易 + @params data: list 行情数据 + """ + + self._indicators = [] + self._broker = broker # type: _Broker + self._data = data # type: _Data + self._tick = 0 + + def I(self, func: Callable, *args) -> np.ndarray: + """ + 计算买卖指标向量。买卖指标向量是一个数组,长度和历史数据对应; + 用于判定这个时间点上需要进行 " 买 " 还是 " 卖 "。 + + 例如计算滑动平均: + def init(): + self.sma = self.I(utils.SMA, self.data.Close, N) + """ + + value = func(*args) + value = np.asarray(value) + assert_msg(value.shape[-1] == len(self._data.Close), "指示器长度必须和data长度相同") + + self._indicators.append(value) + return value + + @property + def tick(self): + return self._tick + + @abc.abstractmethod + def init(self): + """ + 初始化策略。在策略回测 / 执行过程中调用一次,用于初始化策略内部状态。 + 这里也可以预计算策略的辅助参数。比如根据历史行情数据: + 计算买卖的指示器向量; + 训练模型 / 初始化模型参数 + """ + pass + + @abc.abstractmethod + def next(self, tick): + """ + 步进函数,执行第 tick 步的策略。 + tick 代表当前的 " 时间 "。比如 data[tick] 用于访问当前的市场价格。 + """ + pass + + def buy(self): + self._broker.buy() + + def sell(self): + self._broker.sell() + + @property + def data(self): + return self._data class SmaCross(Strategy): - fast = 10 - slow = 20 - - def init(self): - # 计算每个时刻的快线和慢线 - self.sma1 = self.I(SMA, self.data.Close, self.fast) - self.sma2 = self.I(SMA, self.data.Close, self.slow) - - def next(self, tick): - # 快线越过慢线,买入 - if crossover(self.sma1[:tick], self.sma2[:tick]): - self.buy() - # 慢线越过快线,卖出 - elif crossover(self.sma2[:tick], self.sma1[:tick]): - self.sell() - else: - pass - - + # 小窗口 SMA 的窗口大小,用于计算 SMA 快线 + fast = 30 + + # 大窗口 SMA 的窗口大小,用于计算 SMA 慢线 + slow = 90 + + def init(self): + # 计算历史上每个时刻的快线和慢线 + self.sma1 = self.I(SMA, self.data.Close, self.fast) + self.sma2 = self.I(SMA, self.data.Close, self.slow) + + def next(self, tick): + # 快线越过慢线,买入 + if crossover(self.sma1[:tick], self.sma2[:tick]): + self.buy() + + # 慢线越过快线,卖出 + elif crossover(self.sma2[:tick], self.sma1[:tick]): + self.sell() + + # 否则,这个时刻不执行任何操作 + else: + pass + + # 交易所类 class ExchangeAPI: - def __init__(self, data, cash, commission): - assert_msg(0 < cash, "初始现金数量需大于0,输入初始金额为{}".format(cash)) - assert_msg(0 <= commission <= 0.05, "合理手续费率不大于5%,输入的为{}".format(commission)) - self._initial_cash = cash - self._data = data - self._commission = commission - self._position = 0 - self._cash = cash - self._i = 0 - - @property - def cash(self): - return self._cash - - @property - def position(self): - return self._position - - @property - def initial_cash(self): - return self._initial_cash - - @property - def market_value(self): - return self._cash + self._position * self.current_price - - @property - def current_price(self): - return self._data.Close[self._i] - - def buy(self): - """用当前账户余额,全部按市价买入""" - self._position = float(self._cash / (self.current_price * (1 + self._commission))) - self._cash = 0.0 - - def sell(self): - """卖出当前账户所有持仓""" - self._cash += float(self._position * self.current_price * (1 - self._commission)) - self._position = 0.0 - - def next(self, tick): - self._i = tick + def __init__(self, data, cash, commission): + assert_msg(0 < cash, "初始现金数量需大于0,输入初始金额为{}".format(cash)) + assert_msg(0 <= commission <= 0.05, "合理手续费率不大于5%,输入的为{}".format(commission)) + self._initial_cash = cash + self._data = data + self._commission = commission + self._position = 0 + self._cash = cash + self._i = 0 + + @property + def cash(self): + """ + :return: 返回当前账户现金数量 + """ + return self._cash + + @property + def position(self): + """ + :return: 返回当前账户仓位 + """ + return self._position + + @property + def initial_cash(self): + """ + :return: 返回初始现金数量 + """ + return self._initial_cash + + @property + def market_value(self): + """ + :return: 返回当前市值 + """ + return self._cash + self._position * self.current_price + + @property + def current_price(self): + """ + :return: 返回当前市场价格 + """ + return self._data.Close[self._i] + + def buy(self): + """ + 用当前账户剩余资金,按照市场价格全部买入 + """ + self._position = float(self._cash * (1 - self._commission) / self.current_price) + self._cash = 0.0 + + def sell(self): + """ + 卖出当前账户剩余持仓 + """ + self._cash += float(self._position * self.current_price * (1 - self._commission)) + self._position = 0.0 + + def next(self, tick): + self._i = tick class Backtest: - """ - 回测类,用于读取历史行情数据,执行策略,模拟交易并估计收益。 - 调用run成员函数来执行。 - """ - def __init__(self, - data : pd.DataFrame, - strategy_type : type(Strategy), - broker_type : type(ExchangeAPI), - cash : float = 10000, - commission : float = .0): - assert_msg(issubclass(strategy_type, Strategy), "strategy_type不是一个Stragegy类型") - assert_msg(issubclass(broker_type, ExchangeAPI), "broker_type不是一个ExchangeAPI类型") - assert_msg(isinstance(commission, Number), "commission不是浮点数值类型") - - data = data.copy(False) - - # 如果没有volume列,填充Nan - if "Volume" not in data: - data["Volume"] = np.Nan - + """ + 回测类,用于读取历史行情数据,执行策略,模拟交易并估计收益。 + + 初始化的时候调用 Backtest.run 来执行回测。 + """ + + def __init__(self, + data: pd.DataFrame, + strategy_type: type(Strategy), + broker_type: type(ExchangeAPI), + cash: float = 10000, + commission: float = .0): + + """ + 构造回测对象。需要的参数包括:历史数据,策略对象,初始资金数量,手续费率等。 + 初始化过程包括检测输入类型,填充数据空值等。 + + 参数: + :param data: pd.DataFrame pandas Dataframe 格式的历史 OHLCV 数据 + :param strategy_type: type(Strategy) 策略类型 + :param broker_type: type(ExchangeAPI) 交易所 API 类型,负责执行买卖操作以及账户状态的维护 + :param cash: float 初始资金数量 + :param commission: float 每次交易手续费率。如 2% 的手续费此处为 0.02 + """ + + assert_msg(issubclass(strategy_type, Strategy), "strategy_type不是一个Strategy类型") + assert_msg(issubclass(broker_type, ExchangeAPI), "broker_type不是一个ExchangeAPI类型") + assert_msg(isinstance(commission, Number), "commission不是浮点数值类型") + + # False代表浅复制,没有内存拷贝 + data = data.copy(False) + + # 如果没有Volume列,填充NaN + if "Volume" not in data: + data["Volume"] = np.Nan + # 验证OHLC数据格式 - assert_msg(len(data.columns & {"Open", "High", "Low", "Close", "Volume"}) == 5, "输入data格式不正确,至少要包括五列") - + assert_msg(len(data.columns & {'Open', 'High', 'Low', 'Close', 'Volume'}) == 5, + ("输入的`data`格式不正确,至少需要包含这些列:" + "'Open', 'High', 'Low', 'Close'")) # 检查缺失值 - assert_msg(not data[["Open", "High", "Low", "Close", "Volume"]].max().isnull().any(), "部分数据包含缺失值") - - # 如果数据没有排序,就排序 - if not data.index.is_monotonic_increasing: - data = data.sort_index() - - # 利用数据,初始化交易所对象和策略对象 - self._data = data - self._broker = broker_type(data, cash, commission) - self._strategy = strategy_type(self._broker, self._data) - self._result = None - - def run(self) -> pd.Series: - """运行回测""" - strategy = self._strategy - broker = self._broker - # 策略初始化 - strategy.init() - - # 设定回测开始和结束位置 - start = 100 - end = len(self._data) - - # 回测主循环,更新市场状态,执行策略 - for i in range(start, end): - # 先把市场状态移动到第i时刻,然后执行策略 - broker.next(i) - strategy.next(i) - - # 执行完策略,计算并返回结果 - self._results = self._compute_result(broker) - return self._results - - def _compute_result(self, broker): - s = pd.Series() - s["初始市值"] = broker.initial_cash - s["结束市值"] = broker.market_value - s["收益"] = broker.market_value - broker.initial_cash - return s - + assert_msg(not data[['Open', 'High', 'Low', 'Close']].max().isnull().any(), + ('部分OHLC包含缺失值,请去掉那些行或者通过差值填充. ')) + + # 如果行情数据没有按照时间排序,重新排序一下 + if not data.index.is_monotonic_increasing: + data = data.sort_index() + + # 利用数据,初始化交易所对象和策略对象 + self._data = data # type: pd.DataFrame + self._broker = broker_type(data, cash, commission) + self._strategy = strategy_type(self._broker, self._data) + self._results = None + + def run(self) -> pd.Series: + + """ + 运行回测,迭代历史数据,执行模拟交易并返回回测结果。 + + Run the backtest. Returns `pd.Series` with results and statistics. + + Keyword arguments are interpreted as strategy parameters. + """ + + strategy = self._strategy + broker = self._broker + + # 策略初始化 + strategy.init() + + # 设定回测开始和结束位置 + start = 100 + end = len(self._data) + + # 回测主循环,更新市场状态,执行策略 + for i in range(start, end): + # 注意要先把市场状态移动到第 i 时刻,然后再执行策略。 + broker.next(i) + strategy.next(i) + + # 完成策略执行之后,计算结果并返回 + self._results = self._compute_result(broker) + return self._results + + def _compute_result(self, broker): + s = pd.Series() + s["初始市值"] = broker.initial_cash + s["结束市值"] = broker.market_value + s["收益"] = broker.market_value - broker.initial_cash + return s + if __name__ == "__main__": - BTCUSD = read_file("BTCUSD_GEMINI.csv") - assert_msg(BTCUSD.__len__()> 0, "读取失败") - print(BTCUSD.head()) - - ret = Backtest(BTCUSD, SmaCross, ExchangeAPI, 10000.0, 0.03).run() - print(ret) - \ No newline at end of file + # BTCUSD = read_file("BTCUSD_GEMINI.csv") + # assert_msg(BTCUSD.__len__()> 0, "读取失败") + # print(BTCUSD.head()) + + BTCUSD = read_file("BTCUSD_GEMINI.csv") + ret = Backtest(BTCUSD, SmaCross, ExchangeAPI, 10000.0, 0.003).run() + print(ret) diff --git a/Investment/ultil.py b/Investment/ultil.py index 1f25e6c9..e49dc861 100644 --- a/Investment/ultil.py +++ b/Investment/ultil.py @@ -6,11 +6,17 @@ def SMA(values, n): - """返回简单滑动平均""" - return pd.Series(values).rolling(n).mean() - - -def crossover(series1, series2): - """检查两个序列是否在结尾交叉""" - return series1[-2] < series2[-2] and series1[-1]> series1[-1] - \ No newline at end of file + """ + 返回简单滑动平均 + """ + return pd.Series(values).rolling(n).mean() + + +def crossover(series1, series2) -> bool: + """ + 检查两个序列是否在结尾交叉 + :param series1: 序列1 + :param series2: 序列2 + :return: 如果交叉返回True,反之False + """ + return series1[-2] < series2[-2] and series1[-1]> series2[-1] From e5b5b8dc3428aa29f675ec03f3381e2130e0c824 Mon Sep 17 00:00:00 2001 From: liyongbin Date: 2022年8月18日 19:53:19 +0800 Subject: [PATCH 31/32] =?UTF-8?q?=E5=AE=8C=E5=96=84=E7=A4=BA=E4=BE=8B?= =?UTF-8?q?=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Investment/info_post.py | 29 ++++++++++++++--------------- Investment/info_queue.py | 24 ++++++++++++------------ Investment/info_queue2.py | 24 ++++++++++++------------ 3 files changed, 38 insertions(+), 39 deletions(-) diff --git a/Investment/info_post.py b/Investment/info_post.py index 5a352766..b7e31015 100644 --- a/Investment/info_post.py +++ b/Investment/info_post.py @@ -7,19 +7,18 @@ def run(): - context = zmq.Context() - socket = context.socket(zmq.PUB) - socket.bind("tcp://*:6666") - socket.setsockopt_string(zmq.SUBSCRIBE, '') - - cnt = 1 - - while True: - time.sleep(1) - socket.send_string("server cnt {}".format(cnt)) - print("send {}".format(cnt)) - cnt += 1 - - + context = zmq.Context() + socket = context.socket(zmq.PUB) + socket.bind("tcp://*:6666") + + cnt = 1 + + while True: + time.sleep(1) + socket.send_string("server cnt {}".format(cnt)) + print("send {}".format(cnt)) + cnt += 1 + + if __name__ == "__main__": - run() + run() diff --git a/Investment/info_queue.py b/Investment/info_queue.py index 264ca0dc..2ab8c786 100644 --- a/Investment/info_queue.py +++ b/Investment/info_queue.py @@ -6,16 +6,16 @@ def run(): - context = zmq.Context() - socket = context.socket(zmq.SUB) - socket.connect("tcp://127.0.0.1:6666") - socket.setsockopt_string(zmq.SUBSCRIBE, '') - - print("client 1") - while True: - msg = socket.recv() - print("msg:%s" % msg) - - + context = zmq.Context() + socket = context.socket(zmq.SUB) + socket.connect("tcp://127.0.0.1:6666") + socket.setsockopt_string(zmq.SUBSCRIBE, '') + + print("client 1") + while True: + msg = socket.recv() + print("msg:%s" % msg) + + if __name__ == "__main__": - run() + run() diff --git a/Investment/info_queue2.py b/Investment/info_queue2.py index f2c08808..6b963f1c 100644 --- a/Investment/info_queue2.py +++ b/Investment/info_queue2.py @@ -6,16 +6,16 @@ def run(): - context = zmq.Context() - socket = context.socket(zmq.SUB) - socket.connect("tcp://127.0.0.1:6666") - socket.setsockopt_string(zmq.SUBSCRIBE, '') - - print("client 2") - while True: - msg = socket.recv() - print("msg:%s" % msg) - - + context = zmq.Context() + socket = context.socket(zmq.SUB) + socket.connect("tcp://127.0.0.1:6666") + socket.setsockopt_string(zmq.SUBSCRIBE, '') + + print("client 2") + while True: + msg = socket.recv() + print("msg:%s" % msg) + + if __name__ == "__main__": - run() + run() From 822f2e42777abe132f66ef230fba21cd91cf0f1d Mon Sep 17 00:00:00 2001 From: liyongbin Date: 2022年8月18日 20:52:23 +0800 Subject: [PATCH 32/32] =?UTF-8?q?=E5=AE=8C=E5=96=84=E7=A4=BA=E4=BE=8B?= =?UTF-8?q?=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../TradingMonitor/migrations/0001_initial.py | 23 ++++++++++++++++++ .../TradingMonitor/migrations/__init__.py | 0 .../TradingMonitor/TradingMonitor/models.py | 7 +++--- .../TradingMonitor/TradingMonitor/settings.py | 9 ++----- .../TradingMonitor/templates/positions.html | 23 ++++++++++++++++++ .../TradingMonitor/TradingMonitor/urls.py | 2 ++ .../TradingMonitor/TradingMonitor/views.py | 10 ++++---- Investment/TradingMonitor/db.sqlite3 | Bin 131072 -> 143360 bytes 8 files changed, 58 insertions(+), 16 deletions(-) create mode 100644 Investment/TradingMonitor/TradingMonitor/migrations/0001_initial.py create mode 100644 Investment/TradingMonitor/TradingMonitor/migrations/__init__.py create mode 100644 Investment/TradingMonitor/TradingMonitor/templates/positions.html diff --git a/Investment/TradingMonitor/TradingMonitor/migrations/0001_initial.py b/Investment/TradingMonitor/TradingMonitor/migrations/0001_initial.py new file mode 100644 index 00000000..a2ba2449 --- /dev/null +++ b/Investment/TradingMonitor/TradingMonitor/migrations/0001_initial.py @@ -0,0 +1,23 @@ +# Generated by Django 4.1 on 2022年08月18日 12:39 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ] + + operations = [ + migrations.CreateModel( + name='Position', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('asset', models.CharField(max_length=10)), + ('timestamp', models.DateTimeField()), + ('amount', models.DecimalField(decimal_places=3, max_digits=10)), + ], + ), + ] diff --git a/Investment/TradingMonitor/TradingMonitor/migrations/__init__.py b/Investment/TradingMonitor/TradingMonitor/migrations/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/Investment/TradingMonitor/TradingMonitor/models.py b/Investment/TradingMonitor/TradingMonitor/models.py index a2e7ecfd..5e8e1558 100644 --- a/Investment/TradingMonitor/TradingMonitor/models.py +++ b/Investment/TradingMonitor/TradingMonitor/models.py @@ -5,7 +5,6 @@ class Position(models.Model): - asset = models.CharField(max_length = 10) - timestamp = models.DataTimeField() - amount = models.DecimalField(max_digits = 10, decimal_places = 3) - \ No newline at end of file + asset = models.CharField(max_length=10) + timestamp = models.DateTimeField() + amount = models.DecimalField(max_digits=10, decimal_places=3) diff --git a/Investment/TradingMonitor/TradingMonitor/settings.py b/Investment/TradingMonitor/TradingMonitor/settings.py index bf60a986..354c2467 100644 --- a/Investment/TradingMonitor/TradingMonitor/settings.py +++ b/Investment/TradingMonitor/TradingMonitor/settings.py @@ -15,7 +15,6 @@ # Build paths inside the project like this: os.path.join(BASE_DIR, ...) BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) - # Quick-start development settings - unsuitable for production # See https://docs.djangoproject.com/en/2.2/howto/deployment/checklist/ @@ -27,7 +26,6 @@ ALLOWED_HOSTS = [] - # Application definition INSTALLED_APPS = [ @@ -37,6 +35,7 @@ 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', + 'TradingMonitor', ] MIDDLEWARE = [ @@ -54,7 +53,7 @@ TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', - 'DIRS': [], + 'DIRS': [os.path.join(BASE_DIR, 'TradingMonitor/templates')], 'APP_DIRS': True, 'OPTIONS': { 'context_processors': [ @@ -69,7 +68,6 @@ WSGI_APPLICATION = 'TradingMonitor.wsgi.application' - # Database # https://docs.djangoproject.com/en/2.2/ref/settings/#databases @@ -80,7 +78,6 @@ } } - # Password validation # https://docs.djangoproject.com/en/2.2/ref/settings/#auth-password-validators @@ -99,7 +96,6 @@ }, ] - # Internationalization # https://docs.djangoproject.com/en/2.2/topics/i18n/ @@ -113,7 +109,6 @@ USE_TZ = True - # Static files (CSS, JavaScript, Images) # https://docs.djangoproject.com/en/2.2/howto/static-files/ diff --git a/Investment/TradingMonitor/TradingMonitor/templates/positions.html b/Investment/TradingMonitor/TradingMonitor/templates/positions.html new file mode 100644 index 00000000..bb777bc3 --- /dev/null +++ b/Investment/TradingMonitor/TradingMonitor/templates/positions.html @@ -0,0 +1,23 @@ + + + +Positions for {{asset}} + + +

+

Positions for {{asset}}

+ + + + + + +{% for position in positions %} + + + + + +{% endfor %} +
TimeAmount
123{{position.timestamp}}{{position.asset}}
+

AltStyle によって変換されたページ (->オリジナル) /

\ No newline at end of file diff --git a/Investment/TradingMonitor/TradingMonitor/urls.py b/Investment/TradingMonitor/TradingMonitor/urls.py index 5d7984b3..7c487648 100644 --- a/Investment/TradingMonitor/TradingMonitor/urls.py +++ b/Investment/TradingMonitor/TradingMonitor/urls.py @@ -15,7 +15,9 @@ """ from django.contrib import admin from django.urls import path +from . import views urlpatterns = [ path('admin/', admin.site.urls), + path('positions/', views.render_positions) ] diff --git a/Investment/TradingMonitor/TradingMonitor/views.py b/Investment/TradingMonitor/TradingMonitor/views.py index 45ece67e..a8be9e66 100644 --- a/Investment/TradingMonitor/TradingMonitor/views.py +++ b/Investment/TradingMonitor/TradingMonitor/views.py @@ -2,11 +2,11 @@ from django.shortcuts import render -ftom .models import Position +from .models import Position def render_positions(request, asset): - positions = Position.objects.filter(asset = asset) - context = {"asset" : asset, "positions" : positions} - return render(request, "positions.html", context) - \ No newline at end of file + positions = Position.objects.filter(asset=asset) + + context = {"asset": asset, "positions": positions} + return render(request, "positions.html", context) diff --git a/Investment/TradingMonitor/db.sqlite3 b/Investment/TradingMonitor/db.sqlite3 index ca709993a17c83b7f45b0187a08d39132d78c04e..70310acb91cf290d7324034ac4b408c37a1e30e4 100644 GIT binary patch delta 2328 zcmd6oU2NOd6~}o+$&@Tn*S6w%vE@WooW!mphnM=KoGxK(81^;-_ww@af6u+=;GW;Taz%e-M}KRmcMQX@BWg3MO{2Dpubgn|#<0m3sjd|p zhWbOKYO@&o6{6>e))+VTH&M_1?#&Ap9oE0Za+UHLMIk!Q<;r|1uxj%TO8TNaQ7bIDA zD~Xmad;+xhA20BwydcY}%}SE^h`wV?m!wKT4ZY9M`D{nlP6TPj5AV)uTQmbY+zFK5 znk;}&?w*`5TDnyc0VNDdO+Cd!>yUPQ80_D{qYV(s&C~0B1Qap&CHxrv9=-!_!y8a+ zj!?_cI!I`-@!_Y>RBnVfa4Y$~Ld+q(*+l+>+#%mrBmM!oV9|o(fT#+5Z7)!3zZOgY zBFX3YGQVdX+O;OuI5GX$I@qTLM*+bllY7F0&BNdQ1n4#7YnXY9d`(^E6>{^MU84(K zUtm#%W@>IO&D5P$sgg-;csV3f<&0ncbun3ao#ek$u(af8;hmncuz&5alh*@5p?=xn zR}-u2!RO^@W@$b*7t6*kq*e=Ta@L)RR5w=Zi-q&iN`lX>*CXdkvNu@t&B{4fF%_NR z=P#h;&1%FMmVB9Lt}2w>1;p@6?&al}bJa03U!rBs$IJxebY-dL3DqmJY&MjRvC&Jc zFBCkxv=m>+7elDvnhs?pcG@{pt4oV~D#&DKXI)W0lEVDLf}blRey%Dep05ZMYC~SE zh?(THusoBU<51xngaz(v&*kyfs$qi~?h*9zl8129mqwsvn{-m@;dwh{0=m#ki#$`) zv^BpF2h}vf76x1JUbDCGQ+RX~4}t%dLQV7D=2(v&>%lO%V!os1|8?``)kzJBU44!< z43G|kfh7y8*<^~2%we9%_kz3iwsk74b5tq3^r1>YEti*la^ieiKI=loQFbmi;bAD( z`dPZ>^#&%yRMO=_g42~ME>6c{v6|#vjtH^2(#hy^`)lt}Xl@0ドルJzO#`h+Z#EF&@T= z&J>H22`T@C-s5*ulN7~JPDWX|{iaDRj`jo8*4^yA@;BIRzz0CblcxFLXGR-tg)0j2 z0n$SW3NQgz)NqI4w{$oFgtgNTb^5_hf3R8k%gZm?A$Sjiqvj6{A9Zb!HPel*1G**c zkBt8`oYn`7r|{o__gcr!fHy$veg^ze^Y))kXj)t6fE#GAIpqNnY!yUs3$#AE2qM7p z%)rP9xSB0z_Hu<)wsS-qhwCVX*K)hd;fK`J-xxjrIJ00HDF8 Ao&W#< delta 669 zcmX|8Ur1A76#ve>+r4-9-tBDOz>&2-8N;;M?q-6nNFS7_#KMQ@!P0W2lH1U>upp;K z4;%Q0i(IfEgC6QB!ZtX_`D3v3f{#^v7)1T@(~MEx4plqm6NyOcDJL?y=lUMo^siZ8kUfcFFH4y8k@XEj9 z|A|s78v$)WR)Eu%4prG)B5mR@M+`9(!^s(smOD1+1a zibm?oc*j1`GDUE| z_XvVPK$WZ1O?YLdo80 zmC~YUqY)Vyb}GEy*Z>n|?i36vl?nt1_BxXGIr*EoDju;t7Cs3j+#2QTV;9&l#$eCE z3nmQ#O?Cb<1d)v!wc~prrv2<;ou7h_eJgd}P7K$tJ}df|>=+v4x(^Kod= z)`fB{WJ5igfXlkk1wQj?3~pL2qqjV~x!wzVSbgjy^qS4bVH`j-pt#lg^d(rFtXuBT z`2zg?g9_Q~ScZoR-RTO!6}q?&=-JCQ+QK2VE~u(Owa%x8>mxPwVYOPVsi{@fKb>a3 A@Bjb+