├── .gitignore ├── B-Tree └── b-tree │ ├── big_o_complexity.py │ ├── binary_search_tree.py │ ├── height_of_btree.py │ ├── red_black_tree.py │ ├── test_binary_search_tree.py │ ├── test_red_black_tree.py │ ├── test_two_three_tree.py │ ├── tree_rotation.py │ ├── two_three_tree.py │ ├── utils.py │ ├── visitor.py │ ├── visitor_anytree.py │ └── visitor_treelib.py ├── BeginnerSession ├── csv-viewer │ ├── 00_preparation │ │ ├── README.md │ │ └── columns.txt │ ├── 01_csv-viewer │ │ ├── t1.py │ │ ├── t2.py │ │ └── t3.py │ ├── 02_cli-tool │ │ ├── t4.py │ │ ├── t5.py │ │ ├── t6.py │ │ └── t7.py │ └── 03_store-database │ │ ├── t10.py │ │ ├── t8.py │ │ └── t9.py ├── slack-api │ ├── 00_preparation │ │ └── README.md │ └── 01_post-message │ │ ├── t1.py │ │ ├── t2.py │ │ └── t3.py └── yahoo-japan-api │ ├── 00_preparation │ └── README.md │ └── 01_auction │ └── t1.py ├── BizPy ├── openpyxl │ ├── 20200212 │ │ ├── copy_worksheet.py │ │ ├── create_worksheet.py │ │ ├── load_workbook.py │ │ ├── read_all_cells.py │ │ ├── read_single_cell.py │ │ ├── remove_worksheet.py │ │ ├── sample-data │ │ │ └── opendata_lg_list.xlsx │ │ ├── write_cell.py │ │ └── write_cell_with_style.py │ ├── 20200415 │ │ ├── README.md │ │ ├── cli_sample1.py │ │ ├── cli_sample2.py │ │ ├── data │ │ │ ├── opendata_lg_list.xlsx │ │ │ ├── sample.md │ │ │ ├── sample.txt │ │ │ └── sub │ │ │ │ ├── population.xlsx │ │ │ │ └── test.txt │ │ └── original_sample.py │ ├── 20200513 │ │ ├── README.md │ │ ├── bubble_charts.py │ │ ├── doughnut_charts.py │ │ ├── horizontal_chart.py │ │ ├── line_charts.py │ │ ├── monthly_sales.csv │ │ ├── pie_charts.py │ │ ├── population.csv │ │ ├── store_sales.csv │ │ ├── store_sales_share.csv │ │ ├── vertical_chart.py │ │ ├── vertical_chart_percent_stacked.py │ │ └── vertical_chart_stacked.py │ └── 20200610 │ │ ├── README.md │ │ ├── classic_cell.py │ │ ├── classic_formula.py │ │ ├── color_scale.py │ │ ├── data_bar.py │ │ ├── dump_rule.py │ │ └── icon_set.py ├── slack │ ├── 20211027 │ │ ├── README.md │ │ ├── authentication1.png │ │ ├── authorization1.png │ │ ├── bizpy_bot.py │ │ ├── slack-app-bot-overview1.png │ │ ├── slack-create-app1.png │ │ ├── slack-create-app2.png │ │ ├── slack-create-app3.png │ │ ├── slack-create-app4.png │ │ ├── slack-create-app5.png │ │ ├── slack-create-app6.png │ │ ├── slack-integration-workflow-shortcut1.png │ │ ├── slack-integration-workflow-step1.png │ │ ├── slack-integration-workflow1.png │ │ └── slack-integration-workflow2.png │ ├── 20211110 │ │ ├── README.md │ │ ├── bizpy_bot.py │ │ ├── slack-bot-app-home1.png │ │ ├── slack-bot-app-home2.png │ │ ├── slack-bot-app-home3.png │ │ ├── slack-bot-app-home4.png │ │ ├── slack-bot-app-home5.png │ │ └── slack-simplepoll-app-home1.png │ └── 20211201 │ │ ├── README.md │ │ ├── bizpy_bot.py │ │ ├── slack-bot-chat-message1.png │ │ ├── slack-bot-slash-command1.png │ │ ├── slack-bot-slash-command2.png │ │ ├── slack-bot-slash-command3.png │ │ └── slack-bot-slash-command4.png ├── testing │ └── 20201014 │ │ ├── README.md │ │ ├── contents.json │ │ ├── pytest_utils.py │ │ ├── test_utils.py │ │ └── utils.py └── webapi │ ├── 20200708 │ ├── README.md │ ├── answer_phone.py │ ├── answer_sms.py │ ├── make_call.py │ ├── send_sms.py │ ├── send_sms_with_media.py │ └── slack_send_message.py │ ├── 20200812 │ ├── README.md │ ├── hiragana_translation.py │ ├── janome_analyzer.py │ ├── janome_noun.py │ ├── janome_parse.py │ ├── keyword_extraction.py │ ├── morphological_analysis.py │ ├── named_entity_extraction.py │ ├── slot_value_extraction.py │ ├── textpair_doc.py │ └── time_normalization.py │ └── 20200909 │ ├── README.md │ ├── client.py │ ├── contents.json │ ├── get_wikipedia_contents.py │ ├── get_wikipedia_entities.py │ ├── search.json │ ├── search_wikipedia_contents.py │ └── utils.py ├── ExpertPythonProgramming ├── SecondEdition │ └── 01 │ │ └── future │ │ ├── foo.py │ │ ├── non_future.py │ │ ├── sub │ │ ├── __init__.py │ │ ├── absolute.py │ │ ├── foo.py │ │ └── relative.py │ │ └── use_future.py └── ThirdEdition │ └── 5 │ ├── import-path-hooks │ ├── main.py │ └── other.py │ └── meta-hooks │ ├── main.py │ ├── other.py │ └── sub.py ├── HighPerformancePython ├── 11 │ ├── non_slots_example.py │ └── slots_example.py ├── 01 │ └── vectorization.py ├── 02 │ └── julia1_py3.py ├── 03 │ ├── list_vs_tuple.py │ └── use_bisect.py ├── 04 │ ├── compare_namespace.py │ └── create_large_dict.py ├── 05 │ ├── test.txt │ └── yield_sample.py └── 08 │ ├── yield_from1.py │ └── yield_from2.py ├── LICENSE ├── LanguageProcessing └── 100-knocks-2015 │ ├── README.md │ └── chapter1 │ ├── 00.py │ ├── 01.py │ ├── 02.py │ ├── 03.py │ ├── 04.py │ ├── 05.py │ └── 06.py ├── MiniOsaka ├── abc_like_interface.py ├── demo_greet.py ├── demo_greetable1.py ├── demo_greetable2.py ├── demo_greetable_protocol.py ├── demo_typehint.py ├── forward_reference.py ├── forward_reference_error.py ├── generics.py ├── generics_class_subscript.py ├── metaclass.py ├── namedtuple_class.py ├── namedtuple_normal.py ├── namedtuple_typing.py ├── newtype_class.py ├── newtype_typing.py ├── nominal_subtyping.py ├── normaldict.py ├── return_generic_func.py ├── structural_subtyping.py ├── typeddict.py ├── typeddict_class.py └── typeddict_total.py ├── NFC_NFD_problem ├── NFC_sample1.txt ├── NFC_sample2.txt ├── NFD_sample1.txt ├── NFD_sample2.txt ├── read_file_and_normalize.py ├── read_file_and_print.py └── read_file_and_show_unicode_name.py ├── Python36ReleaseParty └── async │ ├── async_comprehension_example.py │ ├── async_comprehension_example_kai1.py │ ├── async_for_example_pep492.py │ ├── async_for_example_pep492_kai1.py │ ├── async_for_example_pep525.py │ ├── async_iter_ticker.py │ ├── producer_consumer_pattern_generator_based_coroutine.py │ ├── producer_consumer_pattern_native_coroutine.py │ ├── surprising_generator.py │ ├── surprising_generator_async.py │ ├── yield_from1.py │ └── yield_from2.py ├── PythonHackerGuideBook ├── 9 │ ├── README.md │ ├── adder.py │ ├── hyadder.hy │ ├── unparse_hy_ast.py │ └── use_ast_from_source.py └── 13 │ ├── oop1.py │ ├── oop2.py │ ├── overload1.py │ ├── overload2.py │ ├── overload3.py │ ├── overload4.py │ ├── singledispatch_sample1.py │ └── singledispatch_sample2.py ├── README.md ├── ScrapingWithBS4 ├── impress_book_reprint_list_all.py └── impress_book_reprint_list_dekiru.py ├── SearchString ├── boyer_moore_horspool.py ├── boyer_moore_sunday.py ├── brute_force_search.py ├── data │ ├── 28HYOGO.CSV │ ├── KEN_ALL.CSV │ └── readme.txt ├── simplified_boyer_moore.py ├── test_search.py └── utils.py ├── StartPython └── 75 │ ├── README.md │ ├── enum1.py │ ├── enum2.py │ ├── enum3.py │ ├── enum4.py │ ├── enum5.py │ ├── metaclass1.py │ └── test.py ├── Testing ├── 00_virtualenv │ └── README.md ├── 01_use_standard_library_only │ ├── README.md │ ├── doctest_sample.py │ └── unittest_tests │ │ ├── __init__.py │ │ ├── test_sample1.py │ │ ├── test_sample2.py │ │ └── test_sample3.py ├── 02_use_3rd_party │ ├── README.md │ ├── package-sample │ │ ├── LICENSE │ │ ├── MANIFEST.in │ │ ├── README.md │ │ ├── mypackage │ │ │ ├── __init__.py │ │ │ ├── main.py │ │ │ └── utils.py │ │ ├── setup.py │ │ ├── tests │ │ │ └── test_enum.py │ │ └── tox.ini │ └── pytest_sample │ │ ├── bad_coding_style.py │ │ └── tests │ │ ├── test_assert.py │ │ ├── test_fixture.py │ │ └── test_parameterize.py ├── 03_use_type_hint │ ├── README.md │ ├── error_sample1.py │ ├── type_hint_builtin_types.py │ ├── type_hint_classes.py │ ├── type_hint_complicated.py │ └── type_hint_functions.py └── README.md ├── docx └── read_footnote │ └── read_docx_footnotes.py └── profiler └── profiler.py /.gitignore: -------------------------------------------------------------------------------- 1 | .envrc 2 | *.swp 3 | 4 | # Byte-compiled / optimized / DLL files 5 | __pycache__/ 6 | *.py[cod] 7 | *$py.class 8 | 9 | # C extensions 10 | *.so 11 | 12 | # Distribution / packaging 13 | .Python 14 | env/ 15 | build/ 16 | develop-eggs/ 17 | dist/ 18 | downloads/ 19 | eggs/ 20 | .eggs/ 21 | lib/ 22 | lib64/ 23 | parts/ 24 | sdist/ 25 | var/ 26 | *.egg-info/ 27 | .installed.cfg 28 | *.egg 29 | 30 | # PyInstaller 31 | # Usually these files are written by a python script from a template 32 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 33 | *.manifest 34 | *.spec 35 | 36 | # Installer logs 37 | pip-log.txt 38 | pip-delete-this-directory.txt 39 | 40 | # Unit test / coverage reports 41 | htmlcov/ 42 | .tox/ 43 | .coverage 44 | .coverage.* 45 | .cache 46 | nosetests.xml 47 | coverage.xml 48 | *,cover 49 | .hypothesis/ 50 | 51 | # Translations 52 | *.mo 53 | *.pot 54 | 55 | # Django stuff: 56 | *.log 57 | local_settings.py 58 | 59 | # Flask stuff: 60 | instance/ 61 | .webassets-cache 62 | 63 | # Scrapy stuff: 64 | .scrapy 65 | 66 | # Sphinx documentation 67 | docs/_build/ 68 | 69 | # PyBuilder 70 | target/ 71 | 72 | # IPython Notebook 73 | .ipynb_checkpoints 74 | 75 | # pyenv 76 | .python-version 77 | 78 | # celery beat schedule file 79 | celerybeat-schedule 80 | 81 | # dotenv 82 | .env 83 | 84 | # virtualenv 85 | venv/ 86 | ENV/ 87 | 88 | # Spyder project settings 89 | .spyderproject 90 | 91 | # Rope project settings 92 | .ropeproject 93 | 94 | # osx 95 | .DS_Store 96 | -------------------------------------------------------------------------------- /B-Tree/b-tree/big_o_complexity.py: -------------------------------------------------------------------------------- 1 | """ 2 | Understand Logarithmic time 3 | 4 | https://en.wikipedia.org/wiki/Time_complexity#Logarithmic_time 5 | """ 6 | import argparse 7 | import logging 8 | import math 9 | 10 | import matplotlib.pyplot as plt 11 | 12 | from utils import draw_with_keyboard_interrupt 13 | 14 | logging.basicConfig( 15 | level=logging.INFO, 16 | format='%(asctime)s %(levelname)s: %(message)s', 17 | ) 18 | log = logging.getLogger(__file__) 19 | 20 | 21 | class Notation: 22 | 23 | def name(self, complexity): 24 | return '%s: %s' % (self.__class__.__name__, complexity) 25 | 26 | 27 | class Constant(Notation): 28 | 29 | def __init__(self, color='blue'): 30 | self.color = color 31 | 32 | def __str__(self): 33 | return self.name('O(1)') 34 | 35 | def show(self, numbers): 36 | y = [1] * len(numbers) 37 | plt.plot(numbers, y, label=self, color=self.color) 38 | 39 | 40 | class Linear(Notation): 41 | 42 | def __init__(self, color='green'): 43 | self.color = color 44 | 45 | def __str__(self): 46 | return self.name('O(n)') 47 | 48 | def show(self, numbers): 49 | plt.plot(numbers, numbers, label=self, color=self.color) 50 | 51 | 52 | class Quadratic(Notation): 53 | 54 | def __init__(self, color='orange'): 55 | self.color = color 56 | 57 | def __str__(self): 58 | return self.name('O(n^2)') 59 | 60 | def show(self, numbers): 61 | y = [pow(n, 2) for n in numbers] 62 | plt.plot(numbers, y, label=self, color=self.color) 63 | 64 | 65 | class Logarithmic(Notation): 66 | 67 | def __init__(self, base=2, color='red'): 68 | self.base = base 69 | self.color = color 70 | 71 | def __str__(self): 72 | return self.name('O(log n)') 73 | 74 | def compute(self, n): 75 | if n == 0: 76 | return 0 77 | return math.log(n, self.base) 78 | 79 | def show(self, numbers): 80 | x = numbers 81 | y = list(map(self.compute, numbers)) 82 | plt.plot(x, y, label=self, color=self.color) 83 | 84 | 85 | class Linearithmic(Logarithmic): 86 | 87 | def __init__(self, base=2, color='purple'): 88 | super().__init__(base, color) 89 | 90 | def __str__(self): 91 | return self.name('O(n log n)') 92 | 93 | def show(self, numbers): 94 | x = numbers 95 | y = [n * self.compute(n) for n in numbers] 96 | plt.plot(x, y, label=self, color=self.color) 97 | 98 | 99 | Notations = [ 100 | Constant, 101 | Linear, 102 | Quadratic, 103 | Logarithmic, 104 | Linearithmic, 105 | ] 106 | 107 | notation_names = [i.__name__ for i in Notations] 108 | 109 | 110 | def parse_argument(): 111 | parser = argparse.ArgumentParser() 112 | parser.set_defaults( 113 | excludes=[], 114 | max_num=8, 115 | verbose=False, 116 | ) 117 | 118 | parser.add_argument( 119 | '--max-num', dest='max_num', type=int, required=True, 120 | help='set number of scale', 121 | ) 122 | 123 | parser.add_argument( 124 | '--exclude', dest='excludes', nargs='*', choices=notation_names, 125 | help='exclude graphs', 126 | ) 127 | 128 | parser.add_argument( 129 | '-v', '--verbose', action='store_true', 130 | help='set verbose mode', 131 | ) 132 | 133 | args = parser.parse_args() 134 | if args.verbose: 135 | log.setLevel(logging.DEBUG) 136 | 137 | return args 138 | 139 | 140 | def main(): 141 | args = parse_argument() 142 | log.debug(args) 143 | 144 | numbers = list(range(0, args.max_num + 1)) 145 | notations = filter(lambda n: n.__name__ not in args.excludes, Notations) 146 | 147 | for notation in notations: 148 | notation().show(numbers) 149 | 150 | plt.title('Big-O Complexity') 151 | plt.xlabel('Growth of Input') 152 | plt.ylabel('Complexity') 153 | plt.legend(loc=2) 154 | 155 | draw_with_keyboard_interrupt(plt) 156 | 157 | 158 | if __name__ == '__main__': 159 | main() 160 | -------------------------------------------------------------------------------- /B-Tree/b-tree/height_of_btree.py: -------------------------------------------------------------------------------- 1 | """ 2 | Understand the best and worst case height of a B-tree 3 | 4 | * https://en.wikipedia.org/wiki/B-tree 5 | 6 | * https://cs.stackexchange.com/questions/59453/why-is-b-tree-search-olog-n 7 | * https://www.quora.com/How-do-I-derive-the-best-and-worst-case-height-of-a-B-Tree # noqa 8 | """ 9 | import argparse 10 | import logging 11 | import math 12 | 13 | import matplotlib.pyplot as plt 14 | 15 | logging.basicConfig( 16 | level=logging.INFO, 17 | format='%(asctime)s %(levelname)s: %(message)s', 18 | ) 19 | log = logging.getLogger(__file__) 20 | 21 | 22 | class BinaryTree: 23 | 24 | def _height(self, n): 25 | if n == 0: 26 | return 0 27 | return math.log(n, 2) 28 | 29 | def height(self, numbers): 30 | x = numbers 31 | y = list(map(self._height, numbers)) 32 | plt.plot(x, y, label='binary-tree', color='blue') 33 | 34 | 35 | class BTree: 36 | 37 | def __init__(self, m=3): 38 | self.m = m 39 | 40 | def label(self, complexity): 41 | return '%s: %s' % (self.__class__.__name__, complexity) 42 | 43 | def height_best(self, n): 44 | if n == 0: 45 | return 0 46 | return math.log(n, self.m) 47 | 48 | def height_worst(self, n): 49 | if n == 0: 50 | return 0 51 | 52 | return math.log(n, (self.m / 2.0)) 53 | 54 | def height(self, numbers): 55 | x = numbers 56 | 57 | best = list(map(self.height_best, numbers)) 58 | plt.plot(x, best, label='best', color='red') 59 | 60 | worst = list(map(self.height_worst, numbers)) 61 | plt.plot(x, worst, label='worst', color='orange') 62 | 63 | 64 | def parse_argument(): 65 | parser = argparse.ArgumentParser() 66 | parser.set_defaults( 67 | btree_order=3, 68 | max_num=8, 69 | verbose=False, 70 | ) 71 | 72 | parser.add_argument( 73 | '--btree-order', dest='btree_order', type=int, required=True, 74 | help='set order of b-tree', 75 | ) 76 | 77 | parser.add_argument( 78 | '--max-num', dest='max_num', type=int, required=True, 79 | help='set number of scale', 80 | ) 81 | 82 | parser.add_argument( 83 | '-v', '--verbose', action='store_true', 84 | help='set verbose mode', 85 | ) 86 | 87 | args = parser.parse_args() 88 | if args.verbose: 89 | log.setLevel(logging.DEBUG) 90 | 91 | return args 92 | 93 | 94 | def main(): 95 | args = parse_argument() 96 | log.debug(args) 97 | 98 | numbers = list(range(0, args.max_num + 1)) 99 | 100 | binary_tree = BinaryTree() 101 | binary_tree.height(numbers) 102 | 103 | btree = BTree(args.btree_order) 104 | btree.height(numbers) 105 | 106 | plt.title('B-Tree Height') 107 | plt.xlabel('Growth of Input') 108 | plt.ylabel('Height') 109 | plt.legend(loc=2) 110 | 111 | plt.show() 112 | 113 | 114 | if __name__ == '__main__': 115 | main() 116 | -------------------------------------------------------------------------------- /B-Tree/b-tree/utils.py: -------------------------------------------------------------------------------- 1 | import time 2 | 3 | 4 | def draw_with_keyboard_interrupt(plt): 5 | plt.draw() 6 | plt.pause(1) 7 | while True: 8 | try: 9 | time.sleep(1) 10 | except KeyboardInterrupt: 11 | plt.close() 12 | break 13 | -------------------------------------------------------------------------------- /B-Tree/b-tree/visitor.py: -------------------------------------------------------------------------------- 1 | from abc import ABC 2 | from abc import abstractmethod 3 | 4 | 5 | class NodeVisitor(ABC): 6 | 7 | @abstractmethod 8 | def visit_root(self, node, **kwargs): 9 | pass 10 | 11 | @abstractmethod 12 | def visit_node(self, node, **kwargs): 13 | pass 14 | 15 | @abstractmethod 16 | def visit_leaf(self, node, **kwargs): 17 | pass 18 | 19 | def visit(self, node, **kwargs): 20 | if node.is_leaf: 21 | return self.visit_leaf(node, **kwargs) 22 | elif node.is_root: 23 | return self.visit_root(node, **kwargs) 24 | else: # root or internal node 25 | return self.visit_node(node, **kwargs) 26 | return '' 27 | -------------------------------------------------------------------------------- /B-Tree/b-tree/visitor_anytree.py: -------------------------------------------------------------------------------- 1 | from anytree import Node, RenderTree 2 | from anytree.exporter import DotExporter 3 | 4 | from visitor import NodeVisitor 5 | 6 | 7 | class AnyTreeVisitor(NodeVisitor): 8 | 9 | def visit_root(self, node, parent=None): 10 | root = Node(repr(node)) 11 | for child in node.children: 12 | self.visit(child, parent=root) 13 | return root 14 | 15 | def visit_node(self, node, parent=None): 16 | internal_node = Node(repr(node), parent=parent) 17 | for child in node.children: 18 | self.visit(child, parent=internal_node) 19 | return internal_node 20 | 21 | def visit_leaf(self, node, parent=None): 22 | leaf = Node(repr(node), parent=parent) 23 | return leaf 24 | 25 | 26 | def print_node(node): 27 | node.update_parent() 28 | root = AnyTreeVisitor().visit(node) 29 | 30 | for pre, fill, node in RenderTree(root): 31 | print('%s%s' % (pre, node.name)) 32 | 33 | # export graphviz files 34 | exporter = DotExporter(root) 35 | exporter.to_dotfile('graphviz.dot') 36 | exporter.to_picture('graphviz.png') 37 | -------------------------------------------------------------------------------- /B-Tree/b-tree/visitor_treelib.py: -------------------------------------------------------------------------------- 1 | from treelib import Tree 2 | 3 | from visitor import NodeVisitor 4 | 5 | 6 | class TreeLibVisitor(NodeVisitor): 7 | 8 | def visit_root(self, node, tree=None): 9 | tree = Tree() 10 | root = repr(node) 11 | tree.create_node(root, root) 12 | for child in node.children: 13 | tree = self.visit(child, tree=tree) 14 | return tree 15 | 16 | def visit_node(self, node, tree=None): 17 | _node = repr(node) 18 | tree.create_node(_node, _node, parent=repr(node.parent)) 19 | for child in node.children: 20 | tree = self.visit(child, tree=tree) 21 | return tree 22 | 23 | def visit_leaf(self, node, tree=None): 24 | leaf = repr(node) 25 | tree.create_node(leaf, leaf, parent=repr(node.parent)) 26 | return tree 27 | 28 | 29 | def print_node(node): 30 | node.update_parent() 31 | print(TreeLibVisitor().visit(node)) 32 | -------------------------------------------------------------------------------- /BeginnerSession/csv-viewer/00_preparation/README.md: -------------------------------------------------------------------------------- 1 | 2 | # 郵便番号データダウンロード 3 | 4 | * http://www.post.japanpost.jp/zipcode/download.html 5 | 6 | $ wget http://www.post.japanpost.jp/zipcode/dl/kogaki/zip/13tokyo.zip 7 | $ wget http://www.post.japanpost.jp/zipcode/dl/kogaki/zip/14kanaga.zip 8 | $ wget http://www.post.japanpost.jp/zipcode/dl/roman/ken_all_rome.zip 9 | 10 | $ unzip 13tokyo.zip 11 | $ unzip 14kanaga.zip 12 | $ unzip ken_all_rome.zip 13 | 14 | # 郵便番号データの説明 15 | 16 | * http://www.post.japanpost.jp/zipcode/dl/readme.html 17 | 18 | -------------------------------------------------------------------------------- /BeginnerSession/csv-viewer/00_preparation/columns.txt: -------------------------------------------------------------------------------- 1 | 全国地方公共団体コード(JIS X0401、X0402)……… 半角数字 2 | (旧)郵便番号(5桁)……………………………………… 半角数字 3 | 郵便番号(7桁)……………………………………… 半角数字 4 | 都道府県名 ………… 半角カタカナ(コード順に掲載) (注1) 5 | 市区町村名 ………… 半角カタカナ(コード順に掲載) (注1) 6 | 町域名 ……………… 半角カタカナ(五十音順に掲載) (注1) 7 | 都道府県名 ………… 漢字(コード順に掲載) (注1,2) 8 | 市区町村名 ………… 漢字(コード順に掲載) (注1,2) 9 | 町域名 ……………… 漢字(五十音順に掲載) (注1,2) 10 | 一町域が二以上の郵便番号で表される場合の表示 (注3) (「1」は該当、「0」は該当せず) 11 | 小字毎に番地が起番されている町域の表示 (注4) (「1」は該当、「0」は該当せず) 12 | 丁目を有する町域の場合の表示 (「1」は該当、「0」は該当せず) 13 | 一つの郵便番号で二以上の町域を表す場合の表示 (注5) (「1」は該当、「0」は該当せず) 14 | 更新の表示(注6)(「0」は変更なし、「1」は変更あり、「2」廃止(廃止データのみ使用)) 15 | 変更理由 (「0」は変更なし、「1」市政・区政・町政・分区・政令指定都市施行、「2」住居表示の実施、「3」区画整理、「4」郵便区調整等、「5」訂正、「6」廃止(廃止データのみ使用)) 16 | -------------------------------------------------------------------------------- /BeginnerSession/csv-viewer/01_csv-viewer/t1.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | CSV ファイルを読み込んで1行ずつ表示する 4 | """ 5 | 6 | 7 | with open('../00_preparation/13TOKYO.CSV', encoding='cp932') as f: 8 | for line in f: 9 | print(line.strip()) 10 | -------------------------------------------------------------------------------- /BeginnerSession/csv-viewer/01_csv-viewer/t2.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | csv モジュールを使って csv データとして読み込んで表示する 4 | """ 5 | 6 | import csv 7 | 8 | 9 | with open('../00_preparation/13TOKYO.CSV', encoding='cp932') as f: 10 | for line_num, data in enumerate(csv.reader(f), 1): 11 | print('line number: {0}'.format(line_num)) 12 | print(data) 13 | control_code = input('\nq: quit, Enter: next\n: ') 14 | if control_code == 'q': 15 | break 16 | print() 17 | -------------------------------------------------------------------------------- /BeginnerSession/csv-viewer/01_csv-viewer/t3.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | CSV ファイルを読み込んで1行ずつカラム単位に縦に表示する 4 | """ 5 | 6 | import csv 7 | 8 | 9 | COLUMNS = [ 10 | '全国地方公共団体コード', 11 | '(旧)郵便番号', 12 | '郵便番号', 13 | '都道府県名(カナ)', 14 | '市区町村名(カナ)', 15 | '町域名(カナ)', 16 | '都道府県名(漢字)', 17 | '市区町村名(漢字)', 18 | '町域名(漢字)' 19 | '一町域が二以上の郵便番号', 20 | '小字毎に番地が起番されている町域', 21 | '丁目を有する町域', 22 | '一つの郵便番号で二以上の町域を表す', 23 | '更新', 24 | '変更理由', 25 | ] 26 | 27 | COLUMN_WIDTH = 40 28 | 29 | 30 | with open('../00_preparation/13TOKYO.CSV', encoding='cp932') as f: 31 | for line_num, data in enumerate(csv.reader(f), 1): 32 | print('line number: {0}'.format(line_num)) 33 | 34 | for i, column in enumerate(COLUMNS): 35 | length = COLUMN_WIDTH - len(column) 36 | print('{0:{length}}: {1}'.format(column, data[i], length=length)) 37 | 38 | control_code = input('\nq: quit, Enter: next\n: ') 39 | if control_code == 'q': 40 | break 41 | print() 42 | -------------------------------------------------------------------------------- /BeginnerSession/csv-viewer/02_cli-tool/t4.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | CLI ツールとして CSV ビューアを使えるようにする 4 | """ 5 | 6 | import csv 7 | 8 | 9 | COLUMNS = [ 10 | '全国地方公共団体コード', 11 | '(旧)郵便番号', 12 | '郵便番号', 13 | '都道府県名(カナ)', 14 | '市区町村名(カナ)', 15 | '町域名(カナ)', 16 | '都道府県名(漢字)', 17 | '市区町村名(漢字)', 18 | '町域名(漢字)' 19 | '一町域が二以上の郵便番号', 20 | '小字毎に番地が起番されている町域', 21 | '丁目を有する町域', 22 | '一つの郵便番号で二以上の町域を表す', 23 | '更新', 24 | '変更理由', 25 | ] 26 | 27 | COLUMN_WIDTH = 40 28 | 29 | 30 | def main(): 31 | with open('../00_preparation/13TOKYO.CSV', encoding='cp932') as f: 32 | for line_num, data in enumerate(csv.reader(f), 1): 33 | print('line number: {0}'.format(line_num)) 34 | 35 | for i, column in enumerate(COLUMNS): 36 | length = COLUMN_WIDTH - len(column) 37 | print('{0:{length}}: {1}'.format( 38 | column, data[i], length=length)) 39 | 40 | control_code = input('\nq: quit, Enter: next\n: ') 41 | if control_code == 'q': 42 | break 43 | print() 44 | 45 | 46 | if __name__ == '__main__': 47 | main() 48 | -------------------------------------------------------------------------------- /BeginnerSession/csv-viewer/02_cli-tool/t5.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | CLI ツールとしてコマンドライン引数を処理できるようにする 4 | """ 5 | 6 | import argparse 7 | import csv 8 | import logging 9 | 10 | 11 | COLUMNS = [ 12 | '全国地方公共団体コード', 13 | '(旧)郵便番号', 14 | '郵便番号', 15 | '都道府県名', 16 | '市区町村名', 17 | '町域名', 18 | '都道府県名', 19 | '市区町村名', 20 | '町域名', 21 | '一町域が二以上の郵便番号', 22 | '小字毎に番地が起番されている町域', 23 | '丁目を有する町域', 24 | '一つの郵便番号で二以上の町域を表す', 25 | '更新', 26 | '変更理由', 27 | ] 28 | 29 | COLUMN_WIDTH = 36 30 | 31 | log_format = '%(asctime)s %(levelname)s %(message)s' 32 | logging.basicConfig(format=log_format, level=logging.INFO) 33 | log = logging.getLogger(__name__) 34 | 35 | 36 | def parse_argument(): 37 | parser = argparse.ArgumentParser() 38 | parser.set_defaults( 39 | csv=None, 40 | mode='viewer', 41 | ) 42 | 43 | parser.add_argument( 44 | '-c', '--csv', required=True, 45 | help='set csv file', 46 | ) 47 | parser.add_argument( 48 | '-m', '--mode', choices=['viewer', 'batch'], 49 | help='set mode to handle csv data', 50 | ) 51 | parser.add_argument( 52 | '-v', '--verbose', action='store_true', 53 | help='set verbose mode' 54 | ) 55 | 56 | args = parser.parse_args() 57 | return args 58 | 59 | 60 | def show_csv_data(args): 61 | with open(args.csv, encoding='cp932') as f: 62 | for line_num, data in enumerate(csv.reader(f), 1): 63 | log.info('line number: {0}'.format(line_num)) 64 | 65 | for i, column in enumerate(COLUMNS): 66 | length = COLUMN_WIDTH - len(column) 67 | print('{0:{length}}: {1}'.format( 68 | column, data[i], length=length)) 69 | 70 | if args.mode == 'viewer': 71 | control_code = input('\nq: quit, Enter: next\n: ') 72 | log.debug('control code: {0}'.format(control_code)) 73 | if control_code == 'q': 74 | break 75 | print() 76 | 77 | 78 | def main(): 79 | args = parse_argument() 80 | if args.verbose: 81 | log.setLevel(logging.DEBUG) 82 | log.debug(args) 83 | 84 | show_csv_data(args) 85 | 86 | 87 | if __name__ == '__main__': 88 | main() 89 | -------------------------------------------------------------------------------- /BeginnerSession/csv-viewer/02_cli-tool/t6.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | CLI ツールとしてコマンドライン引数に応じた処理を実装する 4 | """ 5 | 6 | import argparse 7 | import csv 8 | import logging 9 | 10 | 11 | COLUMNS = [ 12 | '全国地方公共団体コード', 13 | '(旧)郵便番号', 14 | '郵便番号', 15 | '都道府県名(カナ)', 16 | '市区町村名(カナ)', 17 | '町域名(カナ)', 18 | '都道府県名(漢字)', 19 | '市区町村名(漢字)', 20 | '町域名(漢字)' 21 | '一町域が二以上の郵便番号', 22 | '小字毎に番地が起番されている町域', 23 | '丁目を有する町域', 24 | '一つの郵便番号で二以上の町域を表す', 25 | '更新', 26 | '変更理由', 27 | ] 28 | 29 | COLUMN_WIDTH = 40 30 | 31 | log_format = '%(asctime)s %(levelname)s %(message)s' 32 | logging.basicConfig(format=log_format, level=logging.INFO) 33 | log = logging.getLogger(__name__) 34 | 35 | 36 | def parse_argument(): 37 | parser = argparse.ArgumentParser() 38 | parser.set_defaults( 39 | csv=None, 40 | mode='viewer', 41 | ) 42 | 43 | parser.add_argument( 44 | '-c', '--csv', required=True, 45 | help='set csv file', 46 | ) 47 | parser.add_argument( 48 | '-m', '--mode', choices=['viewer', 'batch'], 49 | help='set mode to handle csv data', 50 | ) 51 | parser.add_argument( 52 | '-v', '--verbose', action='store_true', 53 | help='set verbose mode' 54 | ) 55 | 56 | args = parser.parse_args() 57 | return args 58 | 59 | 60 | def get_csv_data(reader): 61 | for line_num, data in enumerate(reader, 1): 62 | yield line_num, data 63 | 64 | 65 | def show_csv_data(reader): 66 | for line_num, data in enumerate(reader, 1): 67 | log.info('line number: {0}'.format(line_num)) 68 | 69 | for i, column in enumerate(COLUMNS): 70 | length = COLUMN_WIDTH - len(column) 71 | print('{0:{length}}: {1}'.format( 72 | column, data[i], length=length)) 73 | 74 | control_code = input('\nq: quit, Enter: next\n: ') 75 | log.debug('control code: {0}'.format(control_code)) 76 | if control_code == 'q': 77 | break 78 | print() 79 | 80 | 81 | def main(): 82 | args = parse_argument() 83 | if args.verbose: 84 | log.setLevel(logging.DEBUG) 85 | log.debug(args) 86 | 87 | with open(args.csv, encoding='cp932') as f: 88 | reader = csv.reader(f) 89 | if args.mode == 'viewer': 90 | show_csv_data(reader) 91 | elif args.mode == 'batch': 92 | for line_num, data in get_csv_data(reader): 93 | # something to do 94 | print(data) 95 | 96 | 97 | if __name__ == '__main__': 98 | main() 99 | -------------------------------------------------------------------------------- /BeginnerSession/csv-viewer/02_cli-tool/t7.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | 関数を小さく整理してテストを書きやすくする 4 | """ 5 | 6 | import argparse 7 | import csv 8 | import logging 9 | 10 | 11 | COLUMNS = [ 12 | '全国地方公共団体コード', 13 | '(旧)郵便番号', 14 | '郵便番号', 15 | '都道府県名(カナ)', 16 | '市区町村名(カナ)', 17 | '町域名(カナ)', 18 | '都道府県名(漢字)', 19 | '市区町村名(漢字)', 20 | '町域名(漢字)' 21 | '一町域が二以上の郵便番号', 22 | '小字毎に番地が起番されている町域', 23 | '丁目を有する町域', 24 | '一つの郵便番号で二以上の町域を表す', 25 | '更新', 26 | '変更理由', 27 | ] 28 | 29 | COLUMN_WIDTH = 40 30 | 31 | log_format = '%(asctime)s %(levelname)s %(message)s' 32 | logging.basicConfig(format=log_format, level=logging.INFO) 33 | log = logging.getLogger(__name__) 34 | 35 | 36 | def parse_argument(): 37 | parser = argparse.ArgumentParser() 38 | parser.set_defaults( 39 | csv=None, 40 | mode='viewer', 41 | ) 42 | 43 | parser.add_argument( 44 | '-c', '--csv', required=True, 45 | help='set csv file', 46 | ) 47 | parser.add_argument( 48 | '-m', '--mode', choices=['viewer', 'batch'], 49 | help='set mode to handle csv data', 50 | ) 51 | parser.add_argument( 52 | '-v', '--verbose', action='store_true', 53 | help='set verbose mode' 54 | ) 55 | 56 | args = parser.parse_args() 57 | return args 58 | 59 | 60 | def get_csv_data(reader): 61 | """ 62 | >>> reader = [['abc', 'def'], ['ghi', 'jkf']] 63 | >>> expected = [(1, reader[0]), (2, reader[1])] 64 | 65 | >>> actual = list(get_csv_data(reader)) 66 | >>> actual == expected 67 | True 68 | """ 69 | for line_num, data in enumerate(reader, 1): 70 | yield line_num, data 71 | 72 | 73 | def make_column_text(columns, column_width): 74 | """ 75 | >>> columns = ['テスト', 'カラムデータ'] 76 | >>> column_width = 10 77 | 78 | >>> expected = [(columns[0], 7), (columns[1], 4)] 79 | >>> actual = list(make_column_text(columns, column_width)) 80 | >>> actual == expected 81 | True 82 | """ 83 | for column in columns: 84 | length = column_width - len(column) 85 | yield column, length 86 | 87 | 88 | def show_csv_data(reader): 89 | for line_num, data in enumerate(reader, 1): 90 | log.info('line number: {0}'.format(line_num)) 91 | 92 | g = make_column_text(COLUMNS, COLUMN_WIDTH) 93 | for i, (column, length) in enumerate(g): 94 | print('{0:{length}}: {1}'.format( 95 | column, data[i], length=length)) 96 | 97 | control_code = input('\nq: quit, Enter: next\n: ') 98 | log.debug('control code: {0}'.format(control_code)) 99 | if control_code == 'q': 100 | break 101 | print() 102 | 103 | 104 | def main(): 105 | args = parse_argument() 106 | if args.verbose: 107 | log.setLevel(logging.DEBUG) 108 | log.debug(args) 109 | 110 | with open(args.csv, encoding='cp932') as f: 111 | reader = csv.reader(f) 112 | if args.mode == 'viewer': 113 | show_csv_data(reader) 114 | elif args.mode == 'batch': 115 | for line_num, data in get_csv_data(reader): 116 | # something to do 117 | print(data) 118 | 119 | 120 | if __name__ == '__main__': 121 | main() 122 | -------------------------------------------------------------------------------- /BeginnerSession/slack-api/00_preparation/README.md: -------------------------------------------------------------------------------- 1 | 2 | # slack API 3 | 4 | * https://api.slack.com/ 5 | 6 | ## chat.postMessage 7 | 8 | * https://api.slack.com/methods/chat.postMessage 9 | 10 | -------------------------------------------------------------------------------- /BeginnerSession/slack-api/01_post-message/t1.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import requests 4 | 5 | """ 6 | slack api を呼び出してメッセージを POST する 7 | 8 | * https://api.slack.com/methods/chat.postMessage 9 | """ 10 | 11 | url = 'https://slack.com/api/chat.postMessage?token=xoxp-2445376243-2445376247-81017233411-dac7613762&channel=C02D3B287&text=%E3%81%93%E3%82%93%E3%81%AB%E3%81%A1%E3%81%AF%E3%83%BC&pretty=1' 12 | r = requests.get(url) 13 | print(r.text) 14 | -------------------------------------------------------------------------------- /BeginnerSession/slack-api/01_post-message/t2.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import sys 3 | 4 | import requests 5 | 6 | """ 7 | URL の生成を構造化して任意のメッセージを投稿する 8 | 9 | * https://api.slack.com/methods/chat.postMessage 10 | """ 11 | 12 | URL = 'https://slack.com/api/chat.postMessage' 13 | TOKEN = 'xoxp-2445376243-2445376247-81017233411-dac7613762' 14 | CHANNEL = 'C02D3B287' 15 | 16 | 17 | if len(sys.argv) < 2: 18 | print('need text message') 19 | sys.exit(0) 20 | 21 | text = sys.argv[1] 22 | params = { 23 | 'token': TOKEN, 24 | 'channel': CHANNEL, 25 | 'text': text, 26 | 'as_user': True, 27 | 'pretty': 1, 28 | } 29 | 30 | r = requests.get(URL, params=params) 31 | print(r.text) 32 | -------------------------------------------------------------------------------- /BeginnerSession/slack-api/01_post-message/t3.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import sys 3 | 4 | import requests 5 | 6 | """ 7 | API の返り値を確認する 8 | 9 | * https://api.slack.com/methods/chat.postMessage 10 | """ 11 | 12 | URL = 'https://slack.com/api/chat.postMessage' 13 | TOKEN = 'xoxp-2445376243-2445376247-81017233411-dac7613762' 14 | CHANNEL = 'C02D3B287' 15 | 16 | 17 | if len(sys.argv) < 2: 18 | print('need text message') 19 | sys.exit(0) 20 | 21 | text = sys.argv[1] 22 | params = { 23 | 'token': TOKEN, 24 | 'channel': CHANNEL, 25 | 'text': text, 26 | 'as_user': True, 27 | 'pretty': 1, 28 | } 29 | 30 | r = requests.get(URL, params=params) 31 | 32 | data = r.json() 33 | if data['ok']: 34 | print('メッセージ "{0}" の登録に成功しました'.format(text)) 35 | else: 36 | print('メッセージ "{0}" の登録に失敗しました'.format(text)) 37 | print(r.text) 38 | -------------------------------------------------------------------------------- /BeginnerSession/yahoo-japan-api/00_preparation/README.md: -------------------------------------------------------------------------------- 1 | 2 | # Yahoo!デベロッパーネットワーク 3 | 4 | * http://developer.yahoo.co.jp/ 5 | 6 | ## オークション API 7 | 8 | * http://developer.yahoo.co.jp/webapi/auctions/ 9 | 10 | -------------------------------------------------------------------------------- /BeginnerSession/yahoo-japan-api/01_auction/t1.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import xml.etree.ElementTree as ET 3 | 4 | import requests 5 | 6 | """ 7 | yahoo auction api を呼び出して出品リストを取得する 8 | 9 | * http://developer.yahoo.co.jp/webapi/auctions/auction/v2/categoryleaf.html 10 | """ 11 | 12 | URL = 'http://auctions.yahooapis.jp/AuctionWebService/V2/categoryLeaf' 13 | APPID = 'your-app-id' 14 | # オークション > コンピュータ > パソコン > Mac > ノートブック、ノートパソコン > MacBook Air 15 | CATEGORY = 2084286828 16 | 17 | params = { 18 | 'appid': APPID, 19 | 'category': CATEGORY, 20 | } 21 | r = requests.get(URL, params=params) 22 | root = ET.fromstring(r.text) 23 | print(root) 24 | 25 | for result in root.findall('{urn:yahoo:jp:auc:categoryLeaf}Result'): 26 | for item in result.findall('{urn:yahoo:jp:auc:categoryLeaf}Item'): 27 | print('=' * 72) 28 | title = item.find('{urn:yahoo:jp:auc:categoryLeaf}Title') 29 | url = item.find('{urn:yahoo:jp:auc:categoryLeaf}ItemUrl') 30 | price = item.find('{urn:yahoo:jp:auc:categoryLeaf}CurrentPrice') 31 | print('title: {}'.format(title.text)) 32 | print('price: {}'.format(price.text)) 33 | print('url: {}'.format(url.text)) 34 | -------------------------------------------------------------------------------- /BizPy/openpyxl/20200212/copy_worksheet.py: -------------------------------------------------------------------------------- 1 | from openpyxl import load_workbook 2 | 3 | wb = load_workbook('various_worksheets.xlsx') 4 | for ws in wb.worksheets: 5 | print(f'{ws.title} をコピーします') 6 | wb.copy_worksheet(ws) 7 | wb.save('copied_worksheets.xlsx') 8 | -------------------------------------------------------------------------------- /BizPy/openpyxl/20200212/create_worksheet.py: -------------------------------------------------------------------------------- 1 | from openpyxl import Workbook 2 | 3 | wb = Workbook() 4 | ws0 = wb.active 5 | ws0.title = 'ワークシート0' #←「ワークシート0」はワークブックと一緒に作成される 6 | ws1 = wb.create_sheet('ワークシート1') #←「ワークシート1」を作成 7 | ws2 = wb.create_sheet('ワークシート2') #←「ワークシート2」を作成 8 | print(f'ワークシート: {wb.sheetnames}') #←すべてのワークシート名を表示 9 | 10 | wb.active = 2 #←「ワークシート2」を選択されている状態に変更 11 | wb.save('new_worksheets.xlsx') 12 | -------------------------------------------------------------------------------- /BizPy/openpyxl/20200212/load_workbook.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from openpyxl import load_workbook 3 | 4 | filename = sys.argv[1] 5 | wb = load_workbook(filename, read_only=True) 6 | print(f'{filename} のワークシート情報を読み込みます') 7 | 8 | for ws in wb.worksheets: 9 | print(f'ワークシート名: {ws.title}') 10 | print(f'- 列の値 最小:{ws.min_column}, 最大:{ws.max_column}') 11 | print(f'- 行の値 最小:{ws.min_row}, 最大:{ws.max_row}') 12 | -------------------------------------------------------------------------------- /BizPy/openpyxl/20200212/read_all_cells.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from openpyxl import load_workbook 3 | 4 | filename = sys.argv[1] 5 | wb = load_workbook(filename, read_only=True) 6 | print(f'{filename} のワークシート情報を読み込みます') 7 | 8 | ws0 = wb.worksheets[0] 9 | print(f'{ws0.title} のセルを1行ずつ表示します') 10 | for row in ws0: 11 | values = [str(column.value) for column in row] 12 | print(values) 13 | -------------------------------------------------------------------------------- /BizPy/openpyxl/20200212/read_single_cell.py: -------------------------------------------------------------------------------- 1 | from openpyxl import load_workbook 2 | 3 | filename = 'various_worksheets.xlsx' 4 | wb = load_workbook(filename, read_only=True) 5 | print(f'{filename} のワークシート情報を読み込みます') 6 | 7 | ws0 = wb.worksheets[0] 8 | print(f'{ws0.title} の特定のセルを指定して表示します') 9 | 10 | b1 = ws0['b1'] #←B1のセルを読み込む 11 | print(f'B1 ({b1.column}列, {b1.row}行): {b1.value}') 12 | 13 | c3 = ws0['c3'] #←C3のセルを読み込む 14 | print(f'C3 ({c3.column}列, {c3.row}行): {c3.value}') 15 | 16 | f6 = ws0['f6'] #←F6のセルを読み込む 17 | print(f'F6 ({f6.column}列, {f6.row}行): {f6.value}') 18 | -------------------------------------------------------------------------------- /BizPy/openpyxl/20200212/remove_worksheet.py: -------------------------------------------------------------------------------- 1 | from openpyxl import load_workbook 2 | 3 | wb = load_workbook('copied_worksheets.xlsx') 4 | for ws in wb.worksheets: 5 | print(f'ワークシート名: {ws.title}') 6 | if ws.title.endswith('Copy'): 7 | print(f'{ws.title} を削除します') 8 | wb.remove(ws) 9 | 10 | wb.save('copied_worksheets.xlsx') 11 | -------------------------------------------------------------------------------- /BizPy/openpyxl/20200212/sample-data/opendata_lg_list.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/t2y/python-study/52a132ea600d4696164e540d8a8f8f5fc58e097a/BizPy/openpyxl/20200212/sample-data/opendata_lg_list.xlsx -------------------------------------------------------------------------------- /BizPy/openpyxl/20200212/write_cell.py: -------------------------------------------------------------------------------- 1 | from datetime import datetime 2 | from openpyxl import Workbook 3 | 4 | wb = Workbook() 5 | ws = wb.active 6 | 7 | ws['A1'] = 'セル1' #←ワークシートのA1のセルに直接書き込む 8 | 9 | b1 = ws['B1'] #←ワークシートからB1のセルを取得する 10 | b1.value = 3.1 #←B1のセルへ値を書き込む 11 | 12 | c1 = ws['C1'] 13 | c1.value = datetime.now() #←現在日時を書き込む 14 | 15 | ws.title = 'セル書き込みワークシート' 16 | wb.save('write_cell.xlsx') 17 | -------------------------------------------------------------------------------- /BizPy/openpyxl/20200212/write_cell_with_style.py: -------------------------------------------------------------------------------- 1 | from openpyxl import Workbook 2 | from openpyxl.styles import PatternFill, Border, Side, Font 3 | 4 | wb = Workbook() 5 | ws = wb.active 6 | 7 | navy_colored_font = Font(name='MS P明朝', size=20, color='000080') 8 | 9 | a1 = ws['A1'] 10 | a1.value = 'フォントを変える' 11 | a1.font = navy_colored_font 12 | 13 | salmon_colored_fill = PatternFill('solid', fgColor='FA8072') 14 | 15 | a2 = ws['A2'] 16 | a2.value = '背景色を変える' 17 | a2.fill = salmon_colored_fill 18 | 19 | red_colored_thin = Side(style='thin', color='ff0000') 20 | blue_colored_double = Side(style='double', color='0000ff') 21 | border = Border( 22 | left=red_colored_thin, right=red_colored_thin, #←左右は赤の罫線を使う 23 | top=blue_colored_double, bottom=blue_colored_double, #←上下は青の二重罫線を使う 24 | ) 25 | 26 | b4 = ws['B4'] 27 | b4.value = '罫線で囲む' 28 | b4.border = border 29 | 30 | ws.title = 'セルとスタイル' 31 | wb.save('write_cell_with_style.xlsx') 32 | -------------------------------------------------------------------------------- /BizPy/openpyxl/20200415/README.md: -------------------------------------------------------------------------------- 1 | 2 | ## コードレビュー/リファクタリング 3 | 4 | * tkinter を使ってダイアログでファイルの選択と保存を行っている 5 | * 処理を自動化するなら GUI から CLI へ 6 | * コマンドライン引数を受け取る 7 | * あるディレクトリ配下のファイルをフィルターする 8 | * `main()` 関数を設ける 9 | * グローバル変数を使わないようにする 10 | * ジェネレーター (`yield`) を使って繰り返し処理を効率化する 11 | 12 | ## リファレンス 13 | 14 | ### Python 公式ドキュメント 15 | 16 | * [pathlib --- オブジェクト指向のファイルシステムパス](https://docs.python.org/ja/3/library/pathlib.html) 17 | * [sys --- システムパラメータと関数](https://docs.python.org/ja/3/library/sys.html) 18 | 19 | ### ジェネレーター 20 | 21 | * [Pythonオンライン学習サービス PyQ(パイキュー)ドキュメント: ジェネレーター](https://docs.pyq.jp/python/library/generator.html) 22 | 23 | ### サンプルコード 24 | 25 | * [できる 仕事がはかどるPython自動処理 全部入り。](https://book.impress.co.jp/books/1118101147) 26 | 27 | -------------------------------------------------------------------------------- /BizPy/openpyxl/20200415/cli_sample1.py: -------------------------------------------------------------------------------- 1 | import openpyxl 2 | import glob 3 | import pandas as pd 4 | import os 5 | import numpy as np 6 | 7 | import pathlib 8 | import sys 9 | #----------------------- 10 | # https://sabopy.com/py/pandas_1/ 11 | #datafile_list = glob.glob(r'*.xlsx') 12 | #print(datafile_list) 13 | 14 | # sortしている 15 | # datafile_list.sort() 16 | # print(datafile_list) 17 | #------------------------------------------------------------------- 18 | 19 | 20 | def get_xlsx_files(path): 21 | for po in path.iterdir(): 22 | if po.is_dir(): 23 | print(f'sub dir: {po}') 24 | yield from get_xlsx_files(po) 25 | elif po.match('*.xlsx'): 26 | print(f' - found!: {po}') 27 | yield po 28 | else: 29 | print(f' - what?: {po}') 30 | 31 | 32 | #エクセルの読み込みと書き込み、そして保存する関数 33 | #エクセルファイルを開く 34 | def openFile(dir_name): 35 | global sheet 36 | global wb 37 | #fileType = [("excel", "*.xlsx")] 38 | #iDir = "/Users/Desktop/" 39 | #filePath = tkinter.filedialog.askopenfilename(filetypes = fileType, initialdir = iDir) 40 | print(f'directory: {dir_name}') 41 | path = pathlib.Path(dir_name) 42 | for path in get_xlsx_files(path): 43 | print(f'{path}') 44 | wb = openpyxl.load_workbook(str(path)) 45 | sheet1 = wb.active 46 | print(sheet1) 47 | getData() 48 | #------------------------------------------------------------------- 49 | 50 | #ExcelファイルのDataを取得 51 | def getData (): 52 | global u 53 | global sheet2 54 | 55 | #データを入れるためのシートを作る 56 | wb.create_sheet(index=2, title="Copy Data") 57 | sheet2 = wb["Copy Data"] 58 | print(sheet2) 59 | #------------------------------------------------------------------- 60 | 61 | #入れ物の作成 62 | sheet1 = wb.active 63 | u = np.zeros((sheet1.max_column, sheet1.max_row)).reshape(sheet1.max_column, sheet1.max_row) 64 | # u = np.array(sheet) 65 | 66 | 67 | #------------------------------------------------------------------- 68 | 69 | #データを入れるためのシートを作る 70 | #Excelファイルを名前をつけて保存する 71 | def saveFile(): 72 | wb.create_sheet(index=3, title="Copy Data") 73 | sheet2 = wb["Copy Data"] 74 | #------------------------------------------------------------------- 75 | 76 | # シートにデータを貼り付ける 77 | sheet1 = wb.active 78 | for i in range(1, sheet1.max_column+1): 79 | for j in range(1, sheet1.max_row+1): 80 | sheet2.cell(row=j, column=i).value = u[i-1][j-1] 81 | 82 | # セルへ書き込む 83 | # シート変数[セル記号] = 書き込む値 84 | # シート変数.cell(row=行,column=列).value = 書き込む値 85 | for i in range(13,19): 86 | sheet2.cell(column=1,row=i-12).value=sheet1["C3"].value 87 | sheet2.cell(column=2,row=i-12).value=sheet1["E3"].value 88 | sheet2.cell(column=6,row=i-12).value='=C6*(C7+C8)' 89 | 90 | # 縦データを横向きに出力(15は横にとばす、100は縦にとばす) 91 | for j in range(1,43): 92 | if sheet1.cell(column=i, row=j).value is None: 93 | sheet1.cell(column=i, row=j).value=0 94 | sheet2.cell(column=15+j,row=i-12).value=sheet1.cell(column=i, row=j).value 95 | 96 | 97 | #新しいExcelファイルを名前をつけて保存する 98 | closePath = tkinter.filedialog.asksaveasfilename() 99 | wb.save(closePath+".xlsx") 100 | 101 | if len(sys.argv) < 2: 102 | print(f'引数にディレクトリを指定してください') 103 | sys.exit(0) 104 | 105 | openFile(sys.argv[1]) 106 | -------------------------------------------------------------------------------- /BizPy/openpyxl/20200415/cli_sample2.py: -------------------------------------------------------------------------------- 1 | import openpyxl 2 | import glob 3 | import pandas as pd 4 | import os 5 | import numpy as np 6 | 7 | import pathlib 8 | import sys 9 | #----------------------- 10 | # https://sabopy.com/py/pandas_1/ 11 | #datafile_list = glob.glob(r'*.xlsx') 12 | #print(datafile_list) 13 | 14 | # sortしている 15 | # datafile_list.sort() 16 | # print(datafile_list) 17 | #------------------------------------------------------------------- 18 | 19 | 20 | def get_xlsx_files(path): 21 | for po in path.iterdir(): 22 | if po.is_dir(): 23 | print(f'sub dir: {po}') 24 | yield from get_xlsx_files(po) 25 | elif po.match('*.xlsx'): 26 | print(f' - found!: {po}') 27 | yield po 28 | else: 29 | print(f' - what?: {po}') 30 | 31 | 32 | #エクセルの読み込みと書き込み、そして保存する関数 33 | #エクセルファイルを開く 34 | def open_files(dir_name): 35 | print(f'directory: {dir_name}') 36 | path = pathlib.Path(dir_name) 37 | for path in get_xlsx_files(path): 38 | print(f'{path}') 39 | wb = openpyxl.load_workbook(str(path)) 40 | yield path.name, wb 41 | #------------------------------------------------------------------- 42 | 43 | 44 | #------------------------------------------------------------------- 45 | 46 | #データを入れるためのシートを作る 47 | #Excelファイルを名前をつけて保存する 48 | def save_file(filename, wb, shape): 49 | wb.create_sheet(index=3, title="Copy Data") 50 | sheet3 = wb["Copy Data"] 51 | #------------------------------------------------------------------- 52 | 53 | # シートにデータを貼り付ける 54 | sheet1 = wb.active 55 | for i in range(1, sheet1.max_column+1): 56 | for j in range(1, sheet1.max_row+1): 57 | sheet3.cell(row=j, column=i).value = shape[i-1][j-1] 58 | 59 | # セルへ書き込む 60 | # シート変数[セル記号] = 書き込む値 61 | # シート変数.cell(row=行,column=列).value = 書き込む値 62 | for i in range(13,19): 63 | sheet3.cell(column=1,row=i-12).value=sheet1["C3"].value 64 | sheet3.cell(column=2,row=i-12).value=sheet1["E3"].value 65 | sheet3.cell(column=6,row=i-12).value='=C6*(C7+C8)' 66 | 67 | # 縦データを横向きに出力(15は横にとばす、100は縦にとばす) 68 | for j in range(1,43): 69 | if sheet1.cell(column=i, row=j).value is None: 70 | sheet1.cell(column=i, row=j).value=0 71 | sheet3.cell(column=15+j,row=i-12).value=sheet1.cell(column=i, row=j).value 72 | 73 | #新しいExcelファイルを名前をつけて保存する 74 | copy_filename = f'{filename.split(".")[0]}-copy.xlsx' 75 | wb.save(copy_filename) 76 | print(f'wrote into {copy_filename}') 77 | 78 | 79 | def main(): 80 | if len(sys.argv) < 2: 81 | print(f'引数にディレクトリを指定してください') 82 | sys.exit(0) 83 | 84 | for filename, wb in open_files(sys.argv[1]): 85 | wb.create_sheet(index=2, title="Copy Data") 86 | sheet1 = wb.active 87 | print(sheet1) 88 | sheet1_range = (sheet1.max_column, sheet1.max_row) 89 | shape = np.zeros(sheet1_range).reshape(sheet1.max_column, sheet1.max_row) 90 | sheet2 = wb["Copy Data"] 91 | print(sheet2) 92 | save_file(filename, wb, shape) 93 | 94 | 95 | if __name__ == '__main__': 96 | main() 97 | -------------------------------------------------------------------------------- /BizPy/openpyxl/20200415/data/opendata_lg_list.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/t2y/python-study/52a132ea600d4696164e540d8a8f8f5fc58e097a/BizPy/openpyxl/20200415/data/opendata_lg_list.xlsx -------------------------------------------------------------------------------- /BizPy/openpyxl/20200415/data/sample.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/t2y/python-study/52a132ea600d4696164e540d8a8f8f5fc58e097a/BizPy/openpyxl/20200415/data/sample.md -------------------------------------------------------------------------------- /BizPy/openpyxl/20200415/data/sample.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/t2y/python-study/52a132ea600d4696164e540d8a8f8f5fc58e097a/BizPy/openpyxl/20200415/data/sample.txt -------------------------------------------------------------------------------- /BizPy/openpyxl/20200415/data/sub/population.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/t2y/python-study/52a132ea600d4696164e540d8a8f8f5fc58e097a/BizPy/openpyxl/20200415/data/sub/population.xlsx -------------------------------------------------------------------------------- /BizPy/openpyxl/20200415/data/sub/test.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/t2y/python-study/52a132ea600d4696164e540d8a8f8f5fc58e097a/BizPy/openpyxl/20200415/data/sub/test.txt -------------------------------------------------------------------------------- /BizPy/openpyxl/20200415/original_sample.py: -------------------------------------------------------------------------------- 1 | import openpyxl 2 | import glob 3 | import pandas as pd 4 | import os 5 | import tkinter.filedialog 6 | import numpy as np 7 | import xlrd 8 | #----------------------- 9 | # https://sabopy.com/py/pandas_1/ 10 | #datafile_list = glob.glob(r'*.xlsx') 11 | #print(datafile_list) 12 | 13 | # sortしている 14 | # datafile_list.sort() 15 | # print(datafile_list) 16 | #------------------------------------------------------------------- 17 | 18 | #エクセルの読み込みと書き込み、そして保存する関数 19 | #エクセルファイルを開く 20 | def openFile(event): 21 | global sheet 22 | global wb 23 | fileType = [("excel", "*.xlsx")] 24 | iDir = "/Users/Desktop/" 25 | filePath = tkinter.filedialog.askopenfilename(filetypes = fileType, initialdir = iDir) 26 | wb = openpyxl.load_workbook(filePath) 27 | sheet1 = wb.active 28 | print(sheet1) 29 | getData() 30 | #------------------------------------------------------------------- 31 | 32 | #ExcelファイルのDataを取得 33 | def getData (): 34 | global u 35 | global sheet2 36 | 37 | #データを入れるためのシートを作る 38 | wb.create_sheet(index=2, title="Copy Data") 39 | sheet2 = wb["Copy Data"] 40 | print(sheet2) 41 | #------------------------------------------------------------------- 42 | 43 | #入れ物の作成 44 | sheet1 = wb.active 45 | u = np.zeros((sheet1.max_column, sheet1.max_row)).reshape(sheet1.max_column, sheet1.max_row) 46 | # u = np.array(sheet) 47 | 48 | 49 | #------------------------------------------------------------------- 50 | 51 | #データを入れるためのシートを作る 52 | #Excelファイルを名前をつけて保存する 53 | def saveFile(event): 54 | wb.create_sheet(index=3, title="Copy Data") 55 | sheet2 = wb["Copy Data"] 56 | #------------------------------------------------------------------- 57 | 58 | # シートにデータを貼り付ける 59 | sheet1 = wb.active 60 | for i in range(1, sheet1.max_column+1): 61 | for j in range(1, sheet1.max_row+1): 62 | sheet2.cell(row=j, column=i).value = u[i-1][j-1] 63 | 64 | # セルへ書き込む 65 | # シート変数[セル記号] = 書き込む値 66 | # シート変数.cell(row=行,column=列).value = 書き込む値 67 | for i in range(13,19): 68 | sheet2.cell(column=1,row=i-12).value=sheet1["C3"].value 69 | sheet2.cell(column=2,row=i-12).value=sheet1["E3"].value 70 | sheet2.cell(column=6,row=i-12).value='=C6*(C7+C8)' 71 | 72 | # 縦データを横向きに出力(15は横にとばす、100は縦にとばす) 73 | for j in range(1,43): 74 | if sheet1.cell(column=i, row=j).value is None: 75 | sheet1.cell(column=i, row=j).value=0 76 | sheet2.cell(column=15+j,row=i-12).value=sheet1.cell(column=i, row=j).value 77 | 78 | 79 | #新しいExcelファイルを名前をつけて保存する 80 | closePath = tkinter.filedialog.asksaveasfilename() 81 | wb.save(closePath+".xlsx") 82 | 83 | #------------------------------------------------------------------- 84 | #GUIの作成およびボタンの設置 85 | root = tkinter.Tk() 86 | root.title("Excel Test") 87 | root.geometry("100x100") 88 | #イベントを起こすためのボタンの作成 89 | openButton = tkinter.Button(text = "Open", width = 50) 90 | saveButton = tkinter.Button (text = "Save", width = 50) 91 | openButton.bind("", openFile) 92 | saveButton.bind("", saveFile) 93 | openButton.pack() 94 | saveButton.pack() 95 | #実行 96 | root.mainloop() 97 | 98 | #------------------------------------------------------------------- 99 | -------------------------------------------------------------------------------- /BizPy/openpyxl/20200513/README.md: -------------------------------------------------------------------------------- 1 | 2 | ## グラフを生成するサンプルコードを試してみる 3 | 4 | 基本的なグラフ作成の流れ 5 | 6 | * csv ファイルからデータを読み込む 7 | * ワークシートにデータを追加する 8 | * chart オブジェクトに属性を設定する 9 | * それぞれのグラフの特性にあわせて異なる 10 | 11 | やや複雑な応用例としてドーナツグラフのサンプルコードの紹介 12 | 13 | * オブジェクトのコピー 14 | * データの生成処理を関数にまとめる 15 | * 同じ処理を行う共通関数としてまとめる 16 | * 複数のグラフをワークシートに追加する 17 | 18 | openpyxl のドキュメントの紹介 19 | 20 | * 他のグラフのサンプルコードをみてみる 21 | 22 | ## リファレンス 23 | 24 | ### OpenPyXL のドキュメント 25 | 26 | * [Charts](https://openpyxl.readthedocs.io/en/stable/charts/introduction.html) 27 | 28 | ### Microsoft Office のヘルプサイト 29 | 30 | * [Excel のヘルプとラーニング](https://support.office.com/ja-jp/excel) 31 | * [Office で利用可能なグラフの種類](https://support.office.com/ja-jp/article/office-%E3%81%A7%E5%88%A9%E7%94%A8%E5%8F%AF%E8%83%BD%E3%81%AA%E3%82%B0%E3%83%A9%E3%83%95%E3%81%AE%E7%A8%AE%E9%A1%9E-a6187218-807e-4103-9e0a-27cdb19afb90) 32 | 33 | ### サンプルコード 34 | 35 | * [できる 仕事がはかどるPython自動処理 全部入り。](https://book.impress.co.jp/books/1118101147) 36 | 37 | -------------------------------------------------------------------------------- /BizPy/openpyxl/20200513/bubble_charts.py: -------------------------------------------------------------------------------- 1 | import pandas as pd 2 | from openpyxl import Workbook 3 | from openpyxl.chart import Series, Reference, BubbleChart 4 | 5 | wb = Workbook() 6 | ws = wb.active 7 | 8 | df = pd.read_csv('store_sales_share.csv') 9 | ws.append(df.columns.tolist()) 10 | for row in df.values: 11 | ws.append(list(row)) 12 | 13 | chart = BubbleChart() 14 | chart.style = 18 15 | 16 | i = 0 17 | for store in df['店名'].unique(): 18 | min_row = i + 2 19 | max_row = i + 4 20 | x = Reference(ws, min_col=3, max_col=3, min_row=min_row, max_row=max_row) 21 | y = Reference(ws, min_col=4, max_col=4, min_row=min_row, max_row=max_row) 22 | z = Reference(ws, min_col=5, max_col=5, min_row=min_row, max_row=max_row) 23 | series = Series(values=y, xvalues=x, zvalues=z, title=store) 24 | chart.series.append(series) 25 | i += 3 26 | 27 | ws.add_chart(chart, 'A13') 28 | wb.save('store_sales_share_bubble.xlsx') 29 | -------------------------------------------------------------------------------- /BizPy/openpyxl/20200513/doughnut_charts.py: -------------------------------------------------------------------------------- 1 | from copy import deepcopy 2 | from openpyxl import Workbook 3 | from openpyxl.chart import DoughnutChart, Reference, Series 4 | from openpyxl.chart.series import DataPoint 5 | 6 | sales_data = [ 7 | ['年間売上', 2017, 2018, 2019], 8 | ['書籍', 65, 55, 45], 9 | ['文具', 5, 10, 15], 10 | ['雑貨', 30, 35, 40], 11 | ] 12 | 13 | def create_data_points(): #←データ要素の作成と書式設定 14 | book = DataPoint(0) 15 | book.graphicalProperties.solidFill = '9370DB' #←MediumPurple 16 | 17 | stationery = DataPoint(1) 18 | stationery.graphicalProperties.solidFill = 'FFFFE0' #←LightYellow 19 | 20 | misc = DataPoint(2) 21 | misc.graphicalProperties.solidFill = 'D2691E' #←Chocolate 22 | misc.explosion = 10 #←「要素の切り出し」を10%に設定 23 | 24 | return [book, stationery, misc] 25 | 26 | def create_new_chart(base_chart, data, data_points, categories): 27 | #←作成済みのチャートをコピーしてデータのみ書き換える 28 | series = Series(data, title_from_data=True) 29 | series.data_points = data_points 30 | chart = deepcopy(base_chart) 31 | chart.title = None 32 | chart.series.append(series) 33 | chart.set_categories(categories) 34 | return chart 35 | 36 | def main(): 37 | wb = Workbook() 38 | ws = wb.active 39 | 40 | for row in sales_data: 41 | ws.append(row) 42 | 43 | data_points = create_data_points() #←データ系列を作成 44 | categories = Reference(ws, min_col=1, min_row=2, max_row=4) #←判例の範囲指定 45 | 46 | chart_2017 = DoughnutChart() #←ドーナツグラフ 47 | data = Reference(ws, min_col=2, min_row=1, max_row=4) #←2017年売上データの範囲指定 48 | chart_2017.add_data(data, titles_from_data=True) 49 | chart_2017.set_categories(categories) 50 | chart_2017.title = 'カテゴリ別年間売上' 51 | chart_2017.style = 26 52 | chart_2017.series[0].data_points = data_points #←データ系列を設定 53 | ws.add_chart(chart_2017, 'E1') #←2017年グラフをE列1行目に追加 54 | 55 | data = Reference(ws, min_col=3, min_row=1, max_row=4) #←2018年売上データの範囲指定 56 | chart_2018 = create_new_chart(chart_2017, data, data_points, categories) 57 | ws.add_chart(chart_2018, 'E17') #←2018年グラフをE列17行目に追加 58 | 59 | data = Reference(ws, min_col=4, min_row=1, max_row=4) #←2019年売上データの範囲指定 60 | chart_2019 = create_new_chart(chart_2018, data, data_points, categories) 61 | ws.add_chart(chart_2019, 'E33') #←2018年グラフをE列33行目に追加 62 | wb.save('sales_doughnut.xlsx') 63 | 64 | if __name__ == '__main__': 65 | main() 66 | -------------------------------------------------------------------------------- /BizPy/openpyxl/20200513/horizontal_chart.py: -------------------------------------------------------------------------------- 1 | import pandas as pd 2 | from openpyxl import Workbook 3 | from openpyxl.chart import BarChart, Reference 4 | 5 | wb = Workbook() 6 | ws = wb.active 7 | 8 | df = pd.read_csv('population.csv') 9 | ws.append(df.columns.tolist()) 10 | for row in df.values: 11 | ws.append(list(row)) 12 | 13 | row_length = 1 + len(df.values) 14 | values = Reference(ws, min_col=2, max_col=2, min_row=1, max_row=row_length) 15 | categories = Reference(ws, min_col=1, min_row=2, max_row=row_length) 16 | 17 | chart = BarChart() 18 | chart.type = 'bar' 19 | chart.style = 11 20 | chart.shape = 4 21 | chart.title = '都道府県別の人口' 22 | chart.x_axis.title = '都道府県' 23 | chart.y_axis.title = '人口' 24 | chart.add_data(values, titles_from_data=True) 25 | chart.set_categories(categories) 26 | 27 | ws.add_chart(chart, 'A9') 28 | wb.save('population_horizontal.xlsx') 29 | -------------------------------------------------------------------------------- /BizPy/openpyxl/20200513/line_charts.py: -------------------------------------------------------------------------------- 1 | import pandas as pd 2 | from copy import deepcopy 3 | from openpyxl import Workbook 4 | from openpyxl.chart import LineChart, Reference 5 | 6 | wb = Workbook() 7 | ws = wb.active 8 | 9 | df = pd.read_csv('monthly_sales.csv') 10 | ws.append(df.columns.tolist()) 11 | for row in df.values: 12 | ws.append(list(row)) 13 | 14 | row_length = 1 + len(df.values) 15 | data = Reference(ws, min_col=2, max_col=4, min_row=1, max_row=row_length) 16 | categories = Reference(ws, min_col=1, min_row=2, max_row=row_length) 17 | 18 | chart = LineChart() 19 | chart.grouping = 'stacked' 20 | chart.overlap = 100 21 | chart.title = '月別売り上げ' 22 | chart.y_axis.title = '売上' 23 | chart.add_data(data, titles_from_data=True) 24 | chart.set_categories(categories) 25 | 26 | percent_stacked = deepcopy(chart) 27 | percent_stacked.grouping = 'percentStacked' 28 | percent_stacked.title = '月別売り上げ (100%積み上げ)' 29 | 30 | ws.add_chart(chart, 'A9') 31 | ws.add_chart(percent_stacked, 'A25') 32 | wb.save('monthly_sales_line.xlsx') 33 | -------------------------------------------------------------------------------- /BizPy/openpyxl/20200513/monthly_sales.csv: -------------------------------------------------------------------------------- 1 | 月,書籍,文具,雑貨 2 | 4月,30,50,50 3 | 5月,35,50,40 4 | 6月,50,35,30 5 | 7月,55,30,20 6 | 8月,75,35,20 7 | 9月,65,40,25 8 | -------------------------------------------------------------------------------- /BizPy/openpyxl/20200513/pie_charts.py: -------------------------------------------------------------------------------- 1 | import pandas as pd 2 | from openpyxl import Workbook 3 | from openpyxl.chart import PieChart, Reference 4 | 5 | wb = Workbook() 6 | ws = wb.active 7 | 8 | df = pd.read_csv('population.csv') 9 | ws.append(df.columns.tolist()) 10 | for row in df.values: 11 | ws.append(list(row)) 12 | 13 | row_length = 1 + len(df.values) 14 | data = Reference(ws, min_col=2, max_col=2, min_row=2, max_row=row_length) 15 | categories = Reference(ws, min_col=1, max_col=1, min_row=2, max_row=row_length) 16 | 17 | pie = PieChart() 18 | pie.add_data(data, titles_from_data=True) 19 | pie.set_categories(categories) 20 | pie.title = '都道府県別の人口' 21 | 22 | ws.add_chart(pie, 'A9') 23 | wb.save('population_pie.xlsx') 24 | -------------------------------------------------------------------------------- /BizPy/openpyxl/20200513/population.csv: -------------------------------------------------------------------------------- 1 | 都道府県,人口(人),増減数(人) 2 | 東京都,13742906,227635 3 | 神奈川県,9161139,34925 4 | 大阪府,8831642,-7827 5 | 愛知県,7526911,43783 6 | 埼玉県,7307579,41045 7 | -------------------------------------------------------------------------------- /BizPy/openpyxl/20200513/store_sales.csv: -------------------------------------------------------------------------------- 1 | 支店,書籍,文具,雑貨 2 | 代々木上原店,200,100,150 3 | 表参道店,100,300,200 4 | 赤坂店,500,200,100 5 | 日比谷店,200,200,200 6 | 湯島店,80,100,300 7 | -------------------------------------------------------------------------------- /BizPy/openpyxl/20200513/store_sales_share.csv: -------------------------------------------------------------------------------- 1 | 店名,項目,販売数,売上,構成比 2 | 代々木上原店,書籍,12,200,44 3 | 代々木上原店,文具,19,100,22 4 | 代々木上原店,雑貨,8,150,33 5 | 表参道店,書籍,8,100,17 6 | 表参道店,文具,30,300,50 7 | 表参道店,雑貨,6,200,33 8 | 赤坂店,書籍,28,500,63 9 | 赤坂店,文具,15,200,25 10 | 赤坂店,雑貨,9,100,12 11 | -------------------------------------------------------------------------------- /BizPy/openpyxl/20200513/vertical_chart.py: -------------------------------------------------------------------------------- 1 | import pandas as pd 2 | from openpyxl import Workbook 3 | from openpyxl.chart import BarChart, Reference 4 | 5 | wb = Workbook() 6 | ws = wb.active 7 | 8 | df = pd.read_csv('population.csv') #←CSVファイルを読み込む 9 | ws.append(df.columns.tolist()) #←ワークシートにヘッダーを追加する 10 | for row in df.values: 11 | ws.append(list(row)) #←ワークシートに行データを追加する 12 | 13 | row_length = 1 + len(df.values) #←1行目はヘッダーなので行数に1を加算 14 | data = Reference(ws, min_col=2, max_col=2, min_row=1, max_row=row_length) 15 | categories = Reference(ws, min_col=1, max_col=1, min_row=2, max_row=row_length) 16 | 17 | chart = BarChart() #←棒グラフ 18 | chart.type = 'col' 19 | chart.style = 10 20 | chart.shape = 4 21 | chart.title = '都道府県別の人口' #←グラフのタイトル 22 | chart.x_axis.title = '都道府県' #←X軸ラベル 23 | chart.y_axis.title = '人口' #←Y軸ラベル 24 | chart.add_data(data, titles_from_data=True) 25 | chart.set_categories(categories) 26 | 27 | ws.add_chart(chart, 'A9') #←グラフをA列9行目に追加する 28 | wb.save('population_vertical.xlsx') 29 | -------------------------------------------------------------------------------- /BizPy/openpyxl/20200513/vertical_chart_percent_stacked.py: -------------------------------------------------------------------------------- 1 | import pandas as pd 2 | from openpyxl import Workbook 3 | from openpyxl.chart import BarChart, Reference 4 | 5 | wb = Workbook() 6 | ws = wb.active 7 | 8 | df = pd.read_csv('store_sales.csv') 9 | ws.append(df.columns.tolist()) 10 | for row in df.values: 11 | ws.append(list(row)) 12 | 13 | row_length = 1 + len(df.values) 14 | data = Reference(ws, min_col=2, max_col=4, min_row=1, max_row=row_length) 15 | categories = Reference(ws, min_col=1, min_row=2, max_row=row_length) 16 | 17 | chart = BarChart() 18 | chart.type = 'col' 19 | chart.shape = 4 20 | chart.grouping = 'percentStacked' #←100%積み上げ棒グラフを表すpercentStackedを指定 21 | chart.overlap = 100 22 | chart.title = '支店別売り上げ (100%積み上げ)' 23 | chart.x_axis.title = '支店' 24 | chart.y_axis.title = '売上' 25 | chart.add_data(data, titles_from_data=True) 26 | chart.set_categories(categories) 27 | 28 | ws.add_chart(chart, 'A9') 29 | wb.save('store_sales_vertical_percent_stacked.xlsx') 30 | -------------------------------------------------------------------------------- /BizPy/openpyxl/20200513/vertical_chart_stacked.py: -------------------------------------------------------------------------------- 1 | import pandas as pd 2 | from openpyxl import Workbook 3 | from openpyxl.chart import BarChart, Reference 4 | 5 | wb = Workbook() 6 | ws = wb.active 7 | 8 | df = pd.read_csv('store_sales.csv') 9 | ws.append(df.columns.tolist()) 10 | for row in df.values: 11 | ws.append(list(row)) 12 | 13 | row_length = 1 + len(df.values) 14 | data = Reference(ws, min_col=2, max_col=4, min_row=1, max_row=row_length) 15 | categories = Reference(ws, min_col=1, min_row=2, max_row=row_length) 16 | 17 | chart = BarChart() 18 | chart.type = 'col' 19 | chart.shape = 4 20 | chart.grouping = 'stacked' #←積み上げ棒グラフを表すstackedを指定 21 | chart.overlap = 100 #←積み上げ棒グラフのときは100固定 22 | chart.title = '支店別売り上げ (積み上げ)' 23 | chart.x_axis.title = '支店' 24 | chart.y_axis.title = '売上' 25 | chart.add_data(data, titles_from_data=True) 26 | chart.set_categories(categories) 27 | 28 | ws.add_chart(chart, 'A9') 29 | wb.save('store_sales_vertical_stacked.xlsx') 30 | -------------------------------------------------------------------------------- /BizPy/openpyxl/20200610/README.md: -------------------------------------------------------------------------------- 1 | 2 | ## 条件付き書式を扱う 3 | 4 | 基本的な条件付き書式設定の流れ 5 | 6 | * ランダム値のデータを生成 7 | * シートのセルにデータをセット 8 | * 条件付き書式を設定するための rule オブジェクトを作成する 9 | * rule オブジェクトを摘要するセルの範囲を指定する 10 | 11 | 実際の条件付き書式のサンプル 12 | 13 | * カラースケール 14 | * データバー 15 | * アイコンセット 16 | * 値や数式を使った条件付き書式 17 | 18 | Python でのデバッグの方法を少し 19 | 20 | * pdb/ipdb を使ってみよう 21 | 22 | ## リファレンス 23 | 24 | ### デバッガー 25 | 26 | 標準ライブラリに `pdb` があるのでインストールしなくても最初から使える。`pdb` よりも少し高機能なデバッガーに `ipdb` がある。 27 | 28 | * [pdb --- Python デバッガ](https://docs.python.org/ja/3/library/pdb.html) 29 | * [Pythonにおけるデバッガ: pdb, ipdb](https://qiita.com/Kobayashi2019/items/98e74110d74e4c60f617) 30 | 31 | ### OpenPyXL のドキュメント 32 | 33 | * [Conditional Formatting](https://openpyxl.readthedocs.io/en/stable/formatting.html) 34 | 35 | ### Microsoft Office のヘルプサイト 36 | 37 | * [Excel のヘルプとラーニング](https://support.office.com/ja-jp/excel) 38 | * [条件付き書式を使用して情報を強調表示する](https://support.office.com/ja-jp/article/%E6%9D%A1%E4%BB%B6%E4%BB%98%E3%81%8D%E6%9B%B8%E5%BC%8F%E3%82%92%E4%BD%BF%E7%94%A8%E3%81%97%E3%81%A6%E6%83%85%E5%A0%B1%E3%82%92%E5%BC%B7%E8%AA%BF%E8%A1%A8%E7%A4%BA%E3%81%99%E3%82%8B-fed60dfa-1d3f-4e13-9ecb-f1951ff89d7f) 39 | 40 | ### サンプルコード 41 | 42 | * [できる 仕事がはかどるPython自動処理 全部入り。](https://book.impress.co.jp/books/1118101147) 43 | 44 | -------------------------------------------------------------------------------- /BizPy/openpyxl/20200610/classic_cell.py: -------------------------------------------------------------------------------- 1 | import random 2 | from openpyxl import Workbook 3 | from openpyxl.styles import PatternFill 4 | from openpyxl.formatting.rule import CellIsRule 5 | 6 | wb = Workbook() 7 | ws = wb.active 8 | 9 | values = random.sample(range(100), k=10) 10 | for i, value in enumerate(values, 1): 11 | column_a = ws.cell(i, 1) 12 | column_a.value = value 13 | 14 | row_length = len(values) 15 | sky_blue_fill = PatternFill('solid', start_color='87CEEB', end_color='87CEEB') 16 | less_than_rule = CellIsRule( #←セルに対する設定 17 | operator='lessThan', 18 | formula=[50], 19 | stopIfTrue=True, 20 | fill=sky_blue_fill 21 | ) 22 | ws.conditional_formatting.add(f'A1:A{row_length}', less_than_rule) 23 | 24 | ws.title = 'クラシック(セル)' 25 | wb.save('classic_cell.xlsx') 26 | -------------------------------------------------------------------------------- /BizPy/openpyxl/20200610/classic_formula.py: -------------------------------------------------------------------------------- 1 | from openpyxl import Workbook 2 | from openpyxl.styles import PatternFill 3 | from openpyxl.formatting.rule import FormulaRule 4 | 5 | wb = Workbook() 6 | ws = wb.active 7 | ws.cell(2, 1).value = '空白ではない' 8 | ws.cell(4, 1).value = 5.3529 9 | 10 | orange_fill = PatternFill('solid', start_color='FFA500', end_color='FFA500') 11 | is_blank_rule = FormulaRule( #←Excelの数式を用いた設定 12 | formula=['ISBLANK(INDIRECT(ADDRESS(ROW(), COLUMN())))'], 13 | stopIfTrue=True, 14 | fill=orange_fill 15 | ) 16 | ws.conditional_formatting.add(f'A1:A5', is_blank_rule) 17 | 18 | ws.title = 'クラシック(数式)' 19 | wb.save('classic_formula.xlsx') 20 | -------------------------------------------------------------------------------- /BizPy/openpyxl/20200610/color_scale.py: -------------------------------------------------------------------------------- 1 | import random 2 | from openpyxl import Workbook 3 | from openpyxl.formatting.rule import ColorScaleRule 4 | 5 | wb = Workbook() 6 | ws = wb.active 7 | 8 | values = random.sample(range(100), k=20) #←100までの数字のうち、20個をランダムに取得 9 | for i, value in enumerate(values, 1): 10 | column_a = ws.cell(i, 1) 11 | column_a.value = value #←A列に値をセット 12 | column_b = ws.cell(i, 2) 13 | column_b.value = value #←B列に値をセット 14 | 15 | row_length = len(values) 16 | two_color_scale = ColorScaleRule( #←2色スケールの設定 17 | start_type='min', start_color='FFFFCC', 18 | end_type='max', end_color='FF8000' 19 | ) 20 | ws.conditional_formatting.add( 21 | f'A1:A{row_length}', two_color_scale 22 | ) #←A列に2色スケールを設定 23 | 24 | three_color_scale = ColorScaleRule( #←3色スケールの設定 25 | start_type='percentile', start_value=10, start_color='FFFFCC', 26 | mid_type='percentile', mid_value=50, mid_color='FF8000', 27 | end_type='percentile', end_value=90, end_color='FF0000' 28 | ) 29 | ws.conditional_formatting.add( 30 | f'B1:B{row_length}', three_color_scale 31 | ) #←B列に3色スケールを設定 32 | 33 | ws.title = 'カラースケール' 34 | wb.save('color_scale.xlsx') 35 | -------------------------------------------------------------------------------- /BizPy/openpyxl/20200610/data_bar.py: -------------------------------------------------------------------------------- 1 | import random 2 | from openpyxl import Workbook 3 | from openpyxl.formatting.rule import DataBarRule 4 | 5 | wb = Workbook() 6 | ws = wb.active 7 | 8 | values = random.sample(range(100), k=20) 9 | for i, value in enumerate(values, 1): 10 | column_a = ws.cell(i, 1) 11 | column_a.value = value 12 | 13 | row_length = len(values) 14 | rule = DataBarRule( #←データバーの設定 15 | start_type='percentile', start_value=10, 16 | end_type='percentile', end_value='90', 17 | color='0080FF', showValue='None', 18 | minLength=None, maxLength=None 19 | ) 20 | ws.conditional_formatting.add(f'A1:A{row_length}', rule) 21 | 22 | ws.title = 'データバー' 23 | wb.save('data_bar.xlsx') 24 | -------------------------------------------------------------------------------- /BizPy/openpyxl/20200610/dump_rule.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from openpyxl import load_workbook 3 | from pprint import pprint 4 | 5 | rule_method = { #←type属性とメソッド名の変換テーブル 6 | 'cellIs': 'CellIsRule', 7 | 'colorScale': 'ColorScaleRule', 8 | 'dataBar': 'DataBarRule', 9 | 'expression': 'FormulaRule', 10 | 'iconSet': 'IconSetRule', 11 | } 12 | 13 | if len(sys.argv) < 2: 14 | print(f'{__file__}の後に条件付き書式設定をもつExcelファイルを指定してください') 15 | sys.exit(0) 16 | 17 | filename = sys.argv[1] 18 | wb = load_workbook(filename) 19 | ws = wb.active 20 | for cond in ws.conditional_formatting: #←条件付き書式の設定を繰り返し処理 21 | for rule in cond.rules: 22 | rule_method_name = rule_method.get(rule.type) #←ルールメソッド名を変換 23 | if rule_method_name is None: 24 | continue 25 | 26 | print(f'#' * 32) 27 | print(f'セルの範囲: {cond.cells}') 28 | print(f'ルールメソッド: {rule_method_name}') 29 | print(f'パラメーター:') 30 | pprint(vars(rule)) #←ruleオブジェクトの属性を表示 31 | -------------------------------------------------------------------------------- /BizPy/openpyxl/20200610/icon_set.py: -------------------------------------------------------------------------------- 1 | import random 2 | from openpyxl import Workbook 3 | from openpyxl.formatting.rule import IconSetRule 4 | 5 | wb = Workbook() 6 | ws = wb.active 7 | 8 | values = random.sample(range(100), k=20) 9 | for i, value in enumerate(values, 1): 10 | column_a = ws.cell(i, 1) 11 | column_a.value = value 12 | 13 | row_length = len(values) 14 | rule = IconSetRule( #←アイコンセットの設定 15 | '5Arrows', 'percent', [0, 20, 40, 60, 80], 16 | showValue=None, percent=None, reverse=None 17 | ) 18 | ws.conditional_formatting.add(f'A1:A{row_length}', rule) 19 | 20 | ws.title = 'アイコンセット' 21 | wb.save('icon_set.xlsx') 22 | -------------------------------------------------------------------------------- /BizPy/slack/20211027/authentication1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/t2y/python-study/52a132ea600d4696164e540d8a8f8f5fc58e097a/BizPy/slack/20211027/authentication1.png -------------------------------------------------------------------------------- /BizPy/slack/20211027/authorization1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/t2y/python-study/52a132ea600d4696164e540d8a8f8f5fc58e097a/BizPy/slack/20211027/authorization1.png -------------------------------------------------------------------------------- /BizPy/slack/20211027/bizpy_bot.py: -------------------------------------------------------------------------------- 1 | import logging 2 | logging.basicConfig(level=logging.DEBUG) 3 | 4 | from slack_bolt import App 5 | 6 | # export SLACK_SIGNING_SECRET=*** 7 | # export SLACK_BOT_TOKEN=xoxb-*** 8 | app = App() 9 | 10 | @app.event("message") 11 | def handle_message_events(ack, body, logger): 12 | logger.info(f'message: {body}') 13 | 14 | @app.event('app_mention') 15 | def handle_app_mention(body, say, logger): 16 | logger.info('app_mention: {body}') 17 | say('はろーわーるど') 18 | 19 | if __name__ == '__main__': 20 | # POST http://localhost:3000/slack/events 21 | app.start(port=3000, path='/slack/events', http_server_logger_enabled=False) 22 | -------------------------------------------------------------------------------- /BizPy/slack/20211027/slack-app-bot-overview1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/t2y/python-study/52a132ea600d4696164e540d8a8f8f5fc58e097a/BizPy/slack/20211027/slack-app-bot-overview1.png -------------------------------------------------------------------------------- /BizPy/slack/20211027/slack-create-app1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/t2y/python-study/52a132ea600d4696164e540d8a8f8f5fc58e097a/BizPy/slack/20211027/slack-create-app1.png -------------------------------------------------------------------------------- /BizPy/slack/20211027/slack-create-app2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/t2y/python-study/52a132ea600d4696164e540d8a8f8f5fc58e097a/BizPy/slack/20211027/slack-create-app2.png -------------------------------------------------------------------------------- /BizPy/slack/20211027/slack-create-app3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/t2y/python-study/52a132ea600d4696164e540d8a8f8f5fc58e097a/BizPy/slack/20211027/slack-create-app3.png -------------------------------------------------------------------------------- /BizPy/slack/20211027/slack-create-app4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/t2y/python-study/52a132ea600d4696164e540d8a8f8f5fc58e097a/BizPy/slack/20211027/slack-create-app4.png -------------------------------------------------------------------------------- /BizPy/slack/20211027/slack-create-app5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/t2y/python-study/52a132ea600d4696164e540d8a8f8f5fc58e097a/BizPy/slack/20211027/slack-create-app5.png -------------------------------------------------------------------------------- /BizPy/slack/20211027/slack-create-app6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/t2y/python-study/52a132ea600d4696164e540d8a8f8f5fc58e097a/BizPy/slack/20211027/slack-create-app6.png -------------------------------------------------------------------------------- /BizPy/slack/20211027/slack-integration-workflow-shortcut1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/t2y/python-study/52a132ea600d4696164e540d8a8f8f5fc58e097a/BizPy/slack/20211027/slack-integration-workflow-shortcut1.png -------------------------------------------------------------------------------- /BizPy/slack/20211027/slack-integration-workflow-step1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/t2y/python-study/52a132ea600d4696164e540d8a8f8f5fc58e097a/BizPy/slack/20211027/slack-integration-workflow-step1.png -------------------------------------------------------------------------------- /BizPy/slack/20211027/slack-integration-workflow1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/t2y/python-study/52a132ea600d4696164e540d8a8f8f5fc58e097a/BizPy/slack/20211027/slack-integration-workflow1.png -------------------------------------------------------------------------------- /BizPy/slack/20211027/slack-integration-workflow2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/t2y/python-study/52a132ea600d4696164e540d8a8f8f5fc58e097a/BizPy/slack/20211027/slack-integration-workflow2.png -------------------------------------------------------------------------------- /BizPy/slack/20211110/README.md: -------------------------------------------------------------------------------- 1 | # Slack apps のホーム画面と Block Kit の紹介 2 | 3 | [前回の bot アプリ](https://github.com/t2y/python-study/tree/master/BizPy/slack/20211027) の続きから進めます。 4 | 5 | [新機能、アプリのホーム・ヴューを活用しよう🏡](https://api.slack.com/lang/ja-jp/app-home-with-modal) のチュートリアルをみながら実際にホーム画面の設定やモーダル画面の作成をやってみます。[Block Kit](https://api.slack.com/block-kit) は Slack apps の UI フレームワークを使って画面を作れます。 6 | 7 | 例えば、[Simple Poll](https://kazamori.slack.com/apps/A0HFW7MR6-simple-poll) というアンケートアプリのホーム画面は次になります。 8 | 9 | ![](slack-simplepoll-app-home1.png) 10 | 11 | ## ホーム画面を追加する 12 | 13 | ホーム画面を追加するには Slack App マネージメントで次のように設定します。 14 | 15 | 1. App Home で `Home Tab` を有効にして再インストールする 16 | 17 | この時点で Slack apps のアプリのタブに「ホーム」が追加されるのを確認できる。但し、画面の設定がないので何も表示されない。 18 | 19 | ![](slack-bot-app-home1.png) 20 | 21 | ## 必要なスコープの追加とイベントを購読する 22 | 23 | ホーム画面を作るためにスコープ (権限) 追加やイベントの購読をしていきます。 24 | 25 | 1. OAuth & Permissions で `OAuth スコープ` に `chat.write` を追加する 26 | 1. Event Subscriptions で 購読するイベントに `app_home_opened` を追加して変更を保存する 27 | 28 | slack client 上で bot アプリのホームタブを開くと、bot アプリに `app_home_opened` イベントが届くようになる。bot アプリがこのイベントを listen してなければ次のようなログが表示される。 29 | 30 | ```python 31 | [Suggestion] You can handle this type of event with the following listener function: 32 | 33 | @app.event("app_home_opened") 34 | def handle_app_home_opened_events(body, logger): 35 | logger.info(body) 36 | ``` 37 | 38 | 39 | 40 | ## ホーム画面でインタラクティブな操作を行う 41 | 42 | ホーム画面でボタンやモーダル画面を設置して、ユーザーがボタンをクリックしたり、フォームに入力した情報を受け取れるようにする。 43 | 44 | 1. Interactivity & Shortcuts で Interactivity を有効にする 45 | 46 | Request URL にインタラクティブな操作 (アクション) をしたときのリクエストを受け付ける URL を設定する。いまアクションを受け付ける bot アプリとイベントハンドラーの bot アプリは同じ bolt アプリケーションのソースコードにあるので Event Subscriptions で設定した Request URL と同じ URL を設定する。 47 | 48 | ``` 49 | https://somewhere.ngrok.io/slack/events 50 | ``` 51 | 52 | ![](slack-bot-app-home2.png) 53 | 54 | 後述する画面でインタラクティブな操作を行うと、アクションのイベントが届くようになる。bolt アプリケーションのソースコードがそのアクションのイベントハンドラーを実装していないと次のようなログが出力される。 55 | 56 | `@app.event("app_home_opened")` ではなく `@app.action("my_buton_click")` のようにデコレーターを実装する以外はイベントハンドラーとほとんど同じにみえる。 57 | 58 | ```python 59 | [Suggestion] You can handle this type of event with the following listener function: 60 | 61 | @app.action("my_buton_click") 62 | def handle_some_action(ack, body, logger): 63 | ack() 64 | logger.info(body) 65 | ``` 66 | 67 | ## Block Kit を使ってホーム画面を設定する 68 | 69 | [Block Kit](https://api.slack.com/block-kit) の設定は JSON で行える。Block Kit Builder を使うと、GUI でぽちぽちしながらブロックを組み合わせて画面とその JSON のペイロードを確認できる。この内容をみながらプロトタイプを作って、bolt アプリケーションに実装していくとよさそう。 70 | 71 | https://app.slack.com/block-kit-builder/TM8Q18RHT#%7B%22type%22:%22home%22,%22blocks%22:%5B%7B%22type%22:%22section%22,%22text%22:%7B%22type%22:%22mrkdwn%22,%22text%22:%22*Welcome!*%20%5CnThis%20is%20a%20home%20for%20Stickers%20app.%20You%20can%20add%20small%20notes%20here!%22%7D,%22accessory%22:%7B%22type%22:%22button%22,%22action_id%22:%22add_note%22,%22text%22:%7B%22type%22:%22plain_text%22,%22text%22:%22Add%20a%20Stickie%22%7D%7D%7D,%7B%22type%22:%22divider%22%7D%5D%7D 72 | 73 | 例えば、ホーム画面を設定するときは `client.views_publish()` に Block Kit の JSON ペイロードとなる設定を渡せばよい。Block Kit の設定内容はソースコードを参照。 74 | 75 | ```python 76 | @app.event('app_home_opened') 77 | def handle_app_home_opened_events(client, event, body, logger): 78 | logger.info(pformat(body)) 79 | client.views_publish(user_id=event['user'], view=_HOME_VIEW) 80 | ``` 81 | 82 | ![](slack-bot-app-home3.png) 83 | 84 | ボタンのアクションに対応するイベントハンドラーを実装する。ここではイベントを受け取ったことを `ack()` で返信してログ出力しているだけになる。`ack()` を返さないと Slack 側はイベントが届いたかどうかわからないのでリトライで同じイベントが届くようになるので何もしなくても `ack()` は返さないといけない。 85 | 86 | ```python 87 | @app.action('my_buton_click') 88 | def handle_click_button(ack, body, logger): 89 | ack() 90 | logger.info(pformat(body)) 91 | ``` 92 | 93 | ## モーダル画面を作ってみる 94 | 95 | ホーム画面とほとんど同じ要領でモーダル画面のアクション、そのモーダル画面で操作したときのビューに対応するイベントハンドラーを実装することでインタラクティブな操作を制御できる。ソースコードをみながら説明する。 96 | 97 | ホーム画面にモーダル画面を開くボタンを追加する。 98 | 99 | ![](slack-bot-app-home4.png) 100 | 101 | モーダル画面を開いたところ。 102 | 103 | ![](slack-bot-app-home5.png) 104 | 105 | ## デバッグ出力の整形 106 | 107 | [pprint](https://docs.python.org/ja/3/library/pprint.html) を使ってログをみやすく整形する。 108 | ディクショナリを整形したかったら pformat を使うというやり方もある。 109 | 110 | ## bolt サーバーの自動再起動 111 | 112 | bolt アプリケーションのソースコードを変更する度に手動でプロセスを再起動しないといけない。bolt-python の issues にも登録されていたが、現時点ではよい方法はないらしい。 113 | 114 | * https://github.com/slackapi/bolt-python/issues/390 115 | 116 | 試しに watchdog の watchmedo という CLI を使ってみたが、 socketserver モジュールでエラーになるので無理そう。 117 | 118 | ```bash 119 | $ watchmedo shell-command --pattern="*.py" --command='python "${watch_src_path}"' . 120 | ``` 121 | 122 | ``` 123 | self.server_bind() 124 | File "/usr/lib/python3.8/http/server.py", line 138, in server_bind 125 | socketserver.TCPServer.server_bind(self) 126 | File "/usr/lib/python3.8/socketserver.py", line 466, in server_bind 127 | self.socket.bind(self.server_address) 128 | OSError: [Errno 98] Address already in use 129 | ``` 130 | -------------------------------------------------------------------------------- /BizPy/slack/20211110/slack-bot-app-home1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/t2y/python-study/52a132ea600d4696164e540d8a8f8f5fc58e097a/BizPy/slack/20211110/slack-bot-app-home1.png -------------------------------------------------------------------------------- /BizPy/slack/20211110/slack-bot-app-home2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/t2y/python-study/52a132ea600d4696164e540d8a8f8f5fc58e097a/BizPy/slack/20211110/slack-bot-app-home2.png -------------------------------------------------------------------------------- /BizPy/slack/20211110/slack-bot-app-home3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/t2y/python-study/52a132ea600d4696164e540d8a8f8f5fc58e097a/BizPy/slack/20211110/slack-bot-app-home3.png -------------------------------------------------------------------------------- /BizPy/slack/20211110/slack-bot-app-home4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/t2y/python-study/52a132ea600d4696164e540d8a8f8f5fc58e097a/BizPy/slack/20211110/slack-bot-app-home4.png -------------------------------------------------------------------------------- /BizPy/slack/20211110/slack-bot-app-home5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/t2y/python-study/52a132ea600d4696164e540d8a8f8f5fc58e097a/BizPy/slack/20211110/slack-bot-app-home5.png -------------------------------------------------------------------------------- /BizPy/slack/20211110/slack-simplepoll-app-home1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/t2y/python-study/52a132ea600d4696164e540d8a8f8f5fc58e097a/BizPy/slack/20211110/slack-simplepoll-app-home1.png -------------------------------------------------------------------------------- /BizPy/slack/20211201/slack-bot-chat-message1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/t2y/python-study/52a132ea600d4696164e540d8a8f8f5fc58e097a/BizPy/slack/20211201/slack-bot-chat-message1.png -------------------------------------------------------------------------------- /BizPy/slack/20211201/slack-bot-slash-command1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/t2y/python-study/52a132ea600d4696164e540d8a8f8f5fc58e097a/BizPy/slack/20211201/slack-bot-slash-command1.png -------------------------------------------------------------------------------- /BizPy/slack/20211201/slack-bot-slash-command2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/t2y/python-study/52a132ea600d4696164e540d8a8f8f5fc58e097a/BizPy/slack/20211201/slack-bot-slash-command2.png -------------------------------------------------------------------------------- /BizPy/slack/20211201/slack-bot-slash-command3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/t2y/python-study/52a132ea600d4696164e540d8a8f8f5fc58e097a/BizPy/slack/20211201/slack-bot-slash-command3.png -------------------------------------------------------------------------------- /BizPy/slack/20211201/slack-bot-slash-command4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/t2y/python-study/52a132ea600d4696164e540d8a8f8f5fc58e097a/BizPy/slack/20211201/slack-bot-slash-command4.png -------------------------------------------------------------------------------- /BizPy/testing/20201014/pytest_utils.py: -------------------------------------------------------------------------------- 1 | import json 2 | from datetime import date 3 | 4 | import pytest 5 | 6 | from utils import get_entities 7 | from utils import get_last_day_of_month1 8 | 9 | 10 | def test_single_entry(): 11 | expected = ['エントリ'] 12 | actual = get_entities("括弧でくくられた[[エントリ]]を取り出す") 13 | assert expected == actual 14 | 15 | 16 | @pytest.fixture 17 | def contents(): 18 | with open('./contents.json') as f: 19 | data = json.load(f) 20 | for key in data['query']['pages']: 21 | revision = data['query']['pages'][key]['revisions'][0] 22 | contents = revision['*'] 23 | return contents 24 | 25 | 26 | def test_actual_contents(contents): 27 | actual = get_entities(contents) 28 | assert 223 == len(actual) 29 | assert 'File:Prog_one.png' == actual[0] 30 | assert 'Category:ソフトウェア開発工程' == actual[-1] 31 | 32 | 33 | @pytest.mark.parametrize('year, month, expected', [ 34 | (2019, 12, date(2019, 12, 31)), 35 | (2020, 1, date(2020, 1, 31)), 36 | (2020, 2, date(2020, 2, 29)), 37 | (2020, 3, date(2020, 3, 31)), 38 | (2020, 4, date(2020, 4, 30)), 39 | (2020, 5, date(2020, 5, 31)), 40 | (2020, 6, date(2020, 6, 30)), 41 | (2020, 7, date(2020, 7, 31)), 42 | (2020, 8, date(2020, 8, 31)), 43 | (2020, 9, date(2020, 9, 30)), 44 | (2020, 10, date(2020, 10, 31)), 45 | (2020, 11, date(2020, 11, 30)), 46 | (2020, 12, date(2020, 12, 31)), 47 | (2021, 1, date(2021, 1, 31)), 48 | ]) 49 | def test_last_day_of_month(year, month, expected): 50 | assert expected == get_last_day_of_month1(year, month) 51 | -------------------------------------------------------------------------------- /BizPy/testing/20201014/test_utils.py: -------------------------------------------------------------------------------- 1 | import json 2 | import unittest 3 | 4 | from utils import get_entities 5 | 6 | 7 | class TestGetEntities(unittest.TestCase): 8 | 9 | def test_empty_string(self): 10 | self.assertEqual([], get_entities("")) 11 | 12 | def test_single_entry(self): 13 | expected = ['エントリ'] 14 | actual = get_entities("括弧でくくられた[[エントリ]]を取り出す") 15 | self.assertEqual(expected, actual) 16 | 17 | def test_none_param(self): 18 | with self.assertRaises(TypeError): 19 | get_entities(None) 20 | 21 | def setUp(self): 22 | """ 23 | テストメソッドの開始前に実行したい前処理 24 | """ 25 | print('setUp') 26 | 27 | def tearDown(self): 28 | """ 29 | テストメソッドの終了後に実行したい後処理 30 | """ 31 | print('tearDown') 32 | 33 | @classmethod 34 | def setUpClass(cls): 35 | """ 36 | テストクラスの開始前に実行したい前処理 37 | """ 38 | print('setUpClass') 39 | with open('./contents.json') as f: 40 | data = json.load(f) 41 | cls.contents = cls.get_contents(data) 42 | 43 | @classmethod 44 | def tearDownClass(cls): 45 | """ 46 | テストクラスの終了後に実行したい後処理 47 | """ 48 | print('tearDownClass') 49 | 50 | @classmethod 51 | def get_contents(cls, data): 52 | for key in data['query']['pages']: 53 | revision = data['query']['pages'][key]['revisions'][0] 54 | contents = revision['*'] 55 | return contents 56 | 57 | def test_actual_contents(self): 58 | actual = get_entities(self.contents) 59 | self.assertEqual(223, len(actual)) 60 | self.assertEqual('File:Prog_one.png', actual[0]) 61 | self.assertEqual('Category:ソフトウェア開発工程', actual[-1]) 62 | 63 | 64 | if __name__ == '__main__': 65 | unittest.main() 66 | -------------------------------------------------------------------------------- /BizPy/testing/20201014/utils.py: -------------------------------------------------------------------------------- 1 | """ 2 | ここがモジュールレベルの docstring です。 3 | このモジュールはユーティリティ関数を提供します。 4 | """ 5 | 6 | import calendar 7 | import re 8 | from datetime import date, timedelta 9 | 10 | RE_ENTITIES = re.compile(r'\[\[(.*?)\]\]', re.MULTILINE) 11 | 12 | 13 | def get_entities(contents): 14 | """ 15 | ここが関数の docstring です。 16 | [[エントリ]] のように二重角括弧で囲まれた文字列を取り出します。 17 | 18 | >>> get_entities("") 19 | [] 20 | >>> get_entities("括弧でくくられた[[エントリ]]を取り出す") 21 | ['エントリ'] 22 | >>> get_entities("[[カッコ|括弧]]でくくられた[[エントリ]]を取り出す") 23 | ['カッコ', 'エントリ'] 24 | >>> get_entities("テストが[[失敗 ]]する例を書く") 25 | ['失敗'] 26 | """ 27 | entities = [] 28 | matches = RE_ENTITIES.findall(contents) 29 | if matches is not None: 30 | for entity in matches: 31 | if '|' in entity: 32 | entity = entity.split('|')[0] 33 | entities.append(entity) 34 | return entities 35 | 36 | 37 | 38 | def get_last_day_of_month1(year, month): 39 | """ 40 | >>> get_last_day_of_month1(2020, 2) 41 | datetime.date(2020, 2, 29) 42 | >>> get_last_day_of_month1(2019, 2) 43 | datetime.date(2019, 2, 28) 44 | >>> get_last_day_of_month1(2020, 4) 45 | datetime.date(2020, 4, 30) 46 | >>> get_last_day_of_month1(2020, 12) 47 | datetime.date(2020, 12, 31) 48 | """ 49 | current_month = date(year, month, 1) 50 | if month == 12: 51 | return current_month.replace(day=31) 52 | 53 | next_month = current_month.replace(month=current_month.month + 1) 54 | last_day = next_month - timedelta(days=1) 55 | return last_day 56 | 57 | 58 | def get_last_day_of_month2(year, month): 59 | """ 60 | >>> get_last_day_of_month2(2020, 2) 61 | datetime.date(2020, 2, 29) 62 | >>> get_last_day_of_month2(2019, 2) 63 | datetime.date(2019, 2, 28) 64 | >>> get_last_day_of_month2(2020, 4) 65 | datetime.date(2020, 4, 30) 66 | >>> get_last_day_of_month2(2020, 12) 67 | datetime.date(2020, 12, 31) 68 | """ 69 | month_range = calendar.monthrange(year, month) 70 | last_day = month_range[1] 71 | return date(year, month, last_day) 72 | 73 | 74 | def create_sequence(num): 75 | """ 76 | 引数で渡された値から連番のリストを返します。 77 | 78 | >>> create_sequence(3) 79 | [0, 1, 2] 80 | 81 | >>> create_sequence(3) # 単なる文字列のパターンマッチなのでこれはエラー 82 | [0,1,2] 83 | 84 | >>> create_sequence(100) # doctest: +ELLIPSIS 85 | [0, 1, 2, ..., 99] 86 | """ 87 | return list(range(num)) 88 | 89 | 90 | if __name__ == '__main__': 91 | import doctest 92 | doctest.testmod() 93 | -------------------------------------------------------------------------------- /BizPy/webapi/20200708/README.md: -------------------------------------------------------------------------------- 1 | 2 | # Web API とは 3 | 4 | > API (Application Programming Interface) はアプリケーションを利用するときに、外部とのインターフェースとなるものの総称です。 5 | > アプリケーションの提供元が利用者に対して「この機能はこのような仕様で使えます」と定めたものです。 6 | > Web APIは Webで利用する HTTP/HTTPS というプロトコルを使用し、ネットワークを経由して提供される API を指します。 7 | 8 | Web API は基本的には1つのリクエストに対して1つのレスポンスを返す仕組み。 9 | 10 | Web API を使う SDK (Software Development Kit) というのは、 11 | 複数の Web API を呼び出してなんらかの処理を実現したり、 12 | 開発者が Web API の仕様を知らなくても使えるように簡潔にしたり、 13 | Web API を呼び出すときに本質ではないいろいろな面倒な処理を隠蔽する意図があったりする。 14 | 15 | 一般論として SDK を使うとプログラムの中から Web API の呼び出しを簡単にしてくれるものと考えてよい。 16 | SDK も内部的には Web API を呼び出して機能を実現している。 17 | 18 | ## slack のチャンネルにメッセージを投稿する 19 | 20 | これは直接 Web API を呼び出す例として紹介する。 21 | 22 | サンプルコード 23 | 24 | * [slack_send_message.py](./slack_send_message.py) 25 | 26 | ```bash 27 | $ python slack_send_message.py てすと 28 | ``` 29 | 30 | [requests](https://requests.readthedocs.io/en/master/) というライブラリを使って Web API を呼び出している。 31 | requests を使う理由は、[Python の標準ライブラリを使う](https://docs.python.org/ja/3/library/urllib.request.html#examples) よりも簡単にコードが書けるから。 32 | 33 | > 参考 The Requests package is recommended for a higher-level HTTP client interface. 34 | > 35 | > [http.client のドキュメント](https://docs.python.org/ja/3/library/http.client.html) にもそう書いてある。 36 | 37 | ```bash 38 | $ pip install requests 39 | ``` 40 | 41 | 42 | # twilio API を試す 43 | 44 | これは SDK を使う例として紹介する。 45 | 46 | ```bash 47 | (twilio) $ pip install twilio 48 | ``` 49 | 50 | * [無料のTwilioトライアルアカウントの使用方法](https://jp.twilio.com/docs/usage/tutorials/how-to-use-your-free-trial-account) 51 | 52 | ## SMS を送る 53 | 54 | ```bash 55 | (twilio) $ python send_sms.py +81XXYYYYZZZZ メッセージ 56 | ``` 57 | 58 | ```bash 59 | (twilio) $ python answer_sms.py 60 | (twilio) $ ./ngrok http 5000 61 | ``` 62 | 63 | サンプルコード 64 | 65 | * [send_sms.py](./send_sms.py) 66 | * [send_sms_with_media.py](./send_sms_with_media.py) 67 | * [answer_sms.py](./answer_sms.py) 68 | 69 | 内容 70 | 71 | * twilio のライブラリ (sdk) のインストール 72 | * 環境変数の扱い 73 | * ドキュメントとソースコードの場所 74 | * デモ 75 | * 普通に sms を送る 76 | * 添付ファイル付きで sms を送る 77 | * sms の内容に返信する 78 | 79 | ### リファレンス 80 | 81 | * [Twilio SMS Python クイックスタート](https://jp.twilio.com/docs/sms/quickstart/python) 82 | * [メッセージの送信](https://jp.twilio.com/docs/sms/send-messages) 83 | * [TwiML™ for Programmable SMS](https://jp.twilio.com/docs/sms/twiml) 84 | 85 | ## 電話をかける 86 | 87 | サンプルコード 88 | 89 | * [make_call.py](make_call.py) 90 | * [answer_phone.py](answer_phone.py) 91 | 92 | ```bash 93 | (twilio) $ python call.py +818033431243 94 | # 電話がかかってくるのでなんか番号を押せと言っているのでどれでも番号を押すと音楽が再生される 95 | ``` 96 | 97 | 内容 98 | 99 | * 電話をかける 100 | * 電話に応答する 101 | 102 | alice は語学が堪能で language='ja-JP' をパラメーターに指定することで日本語も話せる。 103 | 104 | > 18 languages and 14 locales 105 | 106 | ### リファレンス 107 | 108 | * [Programmable Voice クイックスタート for Python](https://jp.twilio.com/docs/voice/quickstart/python) 109 | * [TwiML™ Voice: ](https://www.twilio.com/docs/voice/twiml/say) 110 | 111 | 112 | ## Slack 連携 113 | 114 | * slack の Outgoing WebHooks を使う 115 | * Autopilot というコンポーネントに設定する 116 | 117 | slack 側の Custom Integrations > Outgoing WebHooks に設定する url のフォーマット 118 | 119 | ``` 120 | https://channels.autopilot.twilio.com/v1///slack 121 | ``` 122 | 123 | 内容 124 | 125 | * ドキュメントをみながら設定を一緒にやってみる 126 | * twilio じゃなくても同種のサービスはあちこちにある 127 | 128 | ### リファレンス 129 | 130 | * [Build a Slackbot with Twilio Autopilot](https://www.twilio.com/blog/build-a-slackbot-with-twilio-autopilot) 131 | * [How to build a chatbot](https://jp.twilio.com/docs/autopilot/guides/how-to-build-a-chatbot) 132 | 133 | -------------------------------------------------------------------------------- /BizPy/webapi/20200708/answer_phone.py: -------------------------------------------------------------------------------- 1 | from flask import Flask 2 | from twilio.twiml.voice_response import VoiceResponse 3 | 4 | app = Flask(__name__) 5 | 6 | 7 | @app.route("/answer", methods=['GET', 'POST']) 8 | def answer_call(): 9 | """Respond to incoming phone calls with a brief message.""" 10 | # Start our TwiML response 11 | resp = VoiceResponse() 12 | 13 | # Read a message aloud to the caller 14 | resp.say("Thank you for calling! Have a great day.", voice='alice') 15 | 16 | return str(resp) 17 | 18 | 19 | if __name__ == "__main__": 20 | app.run(debug=True) 21 | -------------------------------------------------------------------------------- /BizPy/webapi/20200708/answer_sms.py: -------------------------------------------------------------------------------- 1 | from flask import Flask, request 2 | from twilio import twiml 3 | from twilio.twiml.messaging_response import Message, MessagingResponse 4 | 5 | app = Flask(__name__) 6 | 7 | 8 | @app.route("/sms", methods=['GET', 'POST']) 9 | def answer_sms(): 10 | number = request.form['From'] 11 | body = request.form['Body'] 12 | 13 | response = MessagingResponse() 14 | response.message(f'Hello {number}, you said: {body}') 15 | return str(response) 16 | 17 | 18 | if __name__ == "__main__": 19 | app.run(debug=True) 20 | -------------------------------------------------------------------------------- /BizPy/webapi/20200708/make_call.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | from pprint import pprint 4 | 5 | from twilio.rest import Client 6 | 7 | 8 | ACCOUNT_SID = os.environ.get('ACCOUNT_SID') 9 | AUTH_TOKEN = os.environ.get('AUTH_TOKEN') 10 | 11 | if ACCOUNT_SID is None or AUTH_TOKEN is None: 12 | print('configure AUTH_TOKEN as environment variable') 13 | 14 | 15 | def main(): 16 | from_number=os.environ.get('TRIAL_NUMBER') 17 | to_number = sys.argv[1] 18 | 19 | client = Client(ACCOUNT_SID, AUTH_TOKEN) 20 | call = client.calls.create( 21 | url='http://demo.twilio.com/docs/voice.xml', 22 | from_=from_number, 23 | to=to_number) 24 | pprint(vars(call)) 25 | 26 | 27 | if __name__ == '__main__': 28 | main() 29 | -------------------------------------------------------------------------------- /BizPy/webapi/20200708/send_sms.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | from pprint import pprint 4 | 5 | from twilio.rest import Client 6 | 7 | 8 | ACCOUNT_SID = os.environ.get('ACCOUNT_SID') 9 | AUTH_TOKEN = os.environ.get('AUTH_TOKEN') 10 | 11 | if ACCOUNT_SID is None or AUTH_TOKEN is None: 12 | print('configure AUTH_TOKEN as environment variable') 13 | 14 | 15 | def main(): 16 | from_number=os.environ.get('TRIAL_NUMBER') 17 | to_number = sys.argv[1] 18 | body = sys.argv[2] 19 | 20 | client = Client(ACCOUNT_SID, AUTH_TOKEN) 21 | message = client.messages.create( 22 | body=body, 23 | from_=from_number, 24 | to=to_number) 25 | pprint(vars(message)) 26 | 27 | 28 | if __name__ == '__main__': 29 | main() 30 | -------------------------------------------------------------------------------- /BizPy/webapi/20200708/send_sms_with_media.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import time 4 | from pprint import pprint 5 | 6 | from twilio.rest import Client 7 | 8 | 9 | ACCOUNT_SID = os.environ.get('ACCOUNT_SID') 10 | AUTH_TOKEN = os.environ.get('AUTH_TOKEN') 11 | 12 | if ACCOUNT_SID is None or AUTH_TOKEN is None: 13 | print('configure AUTH_TOKEN as environment variable') 14 | 15 | 16 | def main(): 17 | from_number=os.environ.get('TRIAL_NUMBER') 18 | to_number = sys.argv[1] 19 | body = sys.argv[2] 20 | media_url = [ 21 | 'https://pbs.twimg.com/profile_images/1243612485/bangasa320x240_400x400.jpg' 22 | ] 23 | 24 | client = Client(ACCOUNT_SID, AUTH_TOKEN) 25 | message = client.messages.create( 26 | body=body, 27 | from_=from_number, 28 | to=to_number, 29 | media_url=media_url) 30 | 31 | pprint(vars(message)) 32 | 33 | 34 | if __name__ == '__main__': 35 | main() 36 | -------------------------------------------------------------------------------- /BizPy/webapi/20200708/slack_send_message.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | 4 | import requests 5 | 6 | """ 7 | slack のチャンネルにメッセージを投稿する 8 | 9 | * https://api.slack.com/methods/chat.postMessage 10 | """ 11 | 12 | TOKEN = os.environ.get('SLACK_TOKEN') 13 | if TOKEN is None: 14 | print('configure SLACK_TOKEN as environment variable') 15 | 16 | URL = 'https://slack.com/api/chat.postMessage' 17 | CHANNEL = 'C014VCWT5KR' 18 | 19 | 20 | if len(sys.argv) < 2: 21 | print('need text message') 22 | sys.exit(0) 23 | 24 | 25 | def main(): 26 | text = sys.argv[1] 27 | data = { 28 | 'token': TOKEN, 29 | 'channel': CHANNEL, 30 | 'text': text, 31 | } 32 | 33 | response = requests.post(URL, data=data) 34 | 35 | response_data = response.json() 36 | if response_data['ok']: 37 | print('メッセージ "{0}" の登録に成功しました'.format(text)) 38 | else: 39 | print('メッセージ "{0}" の登録に失敗しました'.format(text)) 40 | print(response.text) 41 | 42 | 43 | if __name__ == '__main__': 44 | main() 45 | -------------------------------------------------------------------------------- /BizPy/webapi/20200812/hiragana_translation.py: -------------------------------------------------------------------------------- 1 | """ 2 | ひらがな化API 3 | https://labs.goo.ne.jp/api/jp/hiragana-translation/ 4 | """ 5 | import json 6 | import os 7 | import sys 8 | from enum import Enum 9 | 10 | import requests 11 | 12 | 13 | URL = 'https://labs.goo.ne.jp/api/hiragana' 14 | 15 | APP_ID = os.environ.get('APP_ID') 16 | if APP_ID is None: 17 | print('configure APP_ID as environment variable') 18 | 19 | 20 | class ContentType(Enum): 21 | JSON = 'application/json' 22 | URLENCODED = 'application/x-www-form-urlencoded' 23 | 24 | 25 | def get_headers(content_type): 26 | return { 27 | 'Content-type': content_type.value 28 | } 29 | 30 | 31 | def post_with_urlencoded(data): 32 | print('application/x-www-form-urlencoded 形式でリクエスト') 33 | headers = get_headers(ContentType.URLENCODED) 34 | r = requests.post(URL, headers=headers, data=data) 35 | print(f'リクエストヘッダー: {r.request.headers}') 36 | print(f'リクエストボディ: {r.request.body}') 37 | print(f'レスポンス: {r.json()}') 38 | print('=' * 72) 39 | 40 | 41 | def post_with_json(data): 42 | print('application/json 形式でリクエスト') 43 | headers = get_headers(ContentType.JSON) 44 | r = requests.post(URL, headers=headers, data=json.dumps(data)) 45 | print(f'リクエストヘッダー: {r.request.headers}') 46 | print(f'リクエストボディ: {r.request.body}') 47 | print(f'レスポンス: {r.json()}') 48 | print('=' * 72) 49 | 50 | 51 | def main(): 52 | sentence = sys.argv[1] 53 | output_type = sys.argv[2:] 54 | if output_type: 55 | output_type = output_type[0] 56 | else: 57 | output_type = 'hiragana' 58 | 59 | headers = get_headers(ContentType.JSON) 60 | data = { 61 | 'app_id': APP_ID, 62 | 'sentence': sentence, 63 | 'output_type': output_type, 64 | } 65 | 66 | # urlencoded 形式でリクエスト 67 | post_with_urlencoded(data) 68 | 69 | # json 形式でリクエスト 70 | post_with_json(data) 71 | 72 | 73 | if __name__ == '__main__': 74 | main() 75 | -------------------------------------------------------------------------------- /BizPy/webapi/20200812/janome_analyzer.py: -------------------------------------------------------------------------------- 1 | from janome.analyzer import Analyzer 2 | from janome.tokenfilter import POSKeepFilter, TokenCountFilter 3 | 4 | text = 'すもももももももものうち' 5 | token_filters = [POSKeepFilter('名詞'), TokenCountFilter()] 6 | a = Analyzer(token_filters=token_filters) 7 | 8 | for word, count in a.analyze(text): 9 | print(f'{word}: {count}') 10 | -------------------------------------------------------------------------------- /BizPy/webapi/20200812/janome_noun.py: -------------------------------------------------------------------------------- 1 | from janome.tokenizer import Tokenizer 2 | 3 | t = Tokenizer() 4 | text = 'すもももももももものうち' 5 | for token in t.tokenize(text): 6 | if token.part_of_speech.find('名詞') >= 0: 7 | print(token) 8 | -------------------------------------------------------------------------------- /BizPy/webapi/20200812/janome_parse.py: -------------------------------------------------------------------------------- 1 | from janome.tokenizer import Tokenizer 2 | 3 | t = Tokenizer() 4 | text = 'すもももももももものうち' 5 | for token in t.tokenize(text): 6 | print(token) 7 | -------------------------------------------------------------------------------- /BizPy/webapi/20200812/keyword_extraction.py: -------------------------------------------------------------------------------- 1 | """ 2 | キーワード抽出API 3 | https://labs.goo.ne.jp/api/jp/keyword-extraction/ 4 | """ 5 | import json 6 | import os 7 | import sys 8 | from enum import Enum 9 | 10 | import requests 11 | 12 | 13 | URL = 'https://labs.goo.ne.jp/api/keyword' 14 | 15 | 16 | APP_ID = os.environ.get('APP_ID') 17 | if APP_ID is None: 18 | print('configure APP_ID as environment variable') 19 | 20 | 21 | class ContentType(Enum): 22 | JSON = 'application/json' 23 | URLENCODED = 'application/x-www-form-urlencoded' 24 | 25 | 26 | def get_headers(content_type): 27 | return { 28 | 'Content-type': content_type.value 29 | } 30 | 31 | 32 | def post_with_urlencoded(data): 33 | print('application/x-www-form-urlencoded 形式でリクエスト') 34 | headers = get_headers(ContentType.URLENCODED) 35 | r = requests.post(URL, headers=headers, data=data) 36 | print(f'リクエストヘッダー: {r.request.headers}') 37 | print(f'リクエストボディ: {r.request.body}') 38 | print(f'レスポンス: {r.json()}') 39 | print('=' * 72) 40 | 41 | 42 | def main(): 43 | title = sys.argv[1] 44 | body = sys.argv[2] 45 | headers = get_headers(ContentType.JSON) 46 | data = { 47 | 'app_id': APP_ID, 48 | 'title': title, 49 | 'body': body, 50 | 'max_num': 3, 51 | 'focus': 'ORG', 52 | } 53 | 54 | # urlencoded 形式でリクエスト 55 | post_with_urlencoded(data) 56 | 57 | 58 | if __name__ == '__main__': 59 | main() 60 | -------------------------------------------------------------------------------- /BizPy/webapi/20200812/morphological_analysis.py: -------------------------------------------------------------------------------- 1 | """ 2 | 形態素解析API 3 | https://labs.goo.ne.jp/api/jp/morphological-analysis/ 4 | """ 5 | import json 6 | import os 7 | import sys 8 | from enum import Enum 9 | 10 | import requests 11 | 12 | 13 | URL = 'https://labs.goo.ne.jp/api/morph' 14 | 15 | APP_ID = os.environ.get('APP_ID') 16 | if APP_ID is None: 17 | print('configure APP_ID as environment variable') 18 | 19 | 20 | class ContentType(Enum): 21 | JSON = 'application/json' 22 | URLENCODED = 'application/x-www-form-urlencoded' 23 | 24 | 25 | def get_headers(content_type): 26 | return { 27 | 'Content-type': content_type.value 28 | } 29 | 30 | 31 | def post_with_urlencoded(data): 32 | print('application/x-www-form-urlencoded 形式でリクエスト') 33 | headers = get_headers(ContentType.URLENCODED) 34 | r = requests.post(URL, headers=headers, data=data) 35 | print(f'リクエストヘッダー: {r.request.headers}') 36 | print(f'リクエストボディ: {r.request.body}') 37 | print(f'レスポンス: {r.json()}') 38 | print('=' * 72) 39 | 40 | 41 | def main(): 42 | sentence = sys.argv[1] 43 | headers = get_headers(ContentType.JSON) 44 | data = { 45 | 'app_id': APP_ID, 46 | 'sentence': sentence, 47 | 'info_filter': 'form', 48 | 'pos_filter': None, 49 | } 50 | 51 | # urlencoded 形式でリクエスト 52 | post_with_urlencoded(data) 53 | 54 | 55 | if __name__ == '__main__': 56 | main() 57 | -------------------------------------------------------------------------------- /BizPy/webapi/20200812/named_entity_extraction.py: -------------------------------------------------------------------------------- 1 | """ 2 | 固有表現抽出API 3 | https://labs.goo.ne.jp/api/jp/named-entity-extraction/ 4 | """ 5 | import json 6 | import os 7 | import sys 8 | from enum import Enum 9 | 10 | import requests 11 | 12 | 13 | URL = 'https://labs.goo.ne.jp/api/entity' 14 | 15 | 16 | APP_ID = os.environ.get('APP_ID') 17 | if APP_ID is None: 18 | print('configure APP_ID as environment variable') 19 | 20 | 21 | class ContentType(Enum): 22 | JSON = 'application/json' 23 | URLENCODED = 'application/x-www-form-urlencoded' 24 | 25 | 26 | def get_headers(content_type): 27 | return { 28 | 'Content-type': content_type.value 29 | } 30 | 31 | 32 | def post_with_urlencoded(data): 33 | print('application/x-www-form-urlencoded 形式でリクエスト') 34 | headers = get_headers(ContentType.URLENCODED) 35 | r = requests.post(URL, headers=headers, data=data) 36 | print(f'リクエストヘッダー: {r.request.headers}') 37 | print(f'リクエストボディ: {r.request.body}') 38 | print(f'レスポンス: {r.json()}') 39 | print('=' * 72) 40 | 41 | 42 | def main(): 43 | sentence = sys.argv[1] 44 | headers = get_headers(ContentType.JSON) 45 | data = { 46 | 'app_id': APP_ID, 47 | 'sentence': sentence, 48 | 'class_filter': None, 49 | } 50 | 51 | # urlencoded 形式でリクエスト 52 | post_with_urlencoded(data) 53 | 54 | 55 | if __name__ == '__main__': 56 | main() 57 | -------------------------------------------------------------------------------- /BizPy/webapi/20200812/slot_value_extraction.py: -------------------------------------------------------------------------------- 1 | """ 2 | スロット値抽出API 3 | https://labs.goo.ne.jp/api/jp/slot-value-extraction/ 4 | """ 5 | import json 6 | import os 7 | import sys 8 | from enum import Enum 9 | 10 | import requests 11 | 12 | 13 | URL = 'https://labs.goo.ne.jp/api/slot' 14 | 15 | APP_ID = os.environ.get('APP_ID') 16 | if APP_ID is None: 17 | print('configure APP_ID as environment variable') 18 | 19 | 20 | class ContentType(Enum): 21 | JSON = 'application/json' 22 | URLENCODED = 'application/x-www-form-urlencoded' 23 | 24 | 25 | def get_headers(content_type): 26 | return { 27 | 'Content-type': content_type.value 28 | } 29 | 30 | 31 | def post_with_urlencoded(data): 32 | print('application/x-www-form-urlencoded 形式でリクエスト') 33 | headers = get_headers(ContentType.URLENCODED) 34 | r = requests.post(URL, headers=headers, data=data) 35 | print(f'リクエストヘッダー: {r.request.headers}') 36 | print(f'リクエストボディ: {r.request.body}') 37 | print(f'レスポンス: {r.json()}') 38 | print('=' * 72) 39 | 40 | 41 | def main(): 42 | sentence = sys.argv[1] 43 | headers = get_headers(ContentType.JSON) 44 | data = { 45 | 'app_id': APP_ID, 46 | 'sentence': sentence, 47 | 'slot_filter': None, 48 | } 49 | 50 | # urlencoded 形式でリクエスト 51 | post_with_urlencoded(data) 52 | 53 | 54 | if __name__ == '__main__': 55 | main() 56 | -------------------------------------------------------------------------------- /BizPy/webapi/20200812/textpair_doc.py: -------------------------------------------------------------------------------- 1 | """ 2 | テキストペア類似度API 3 | https://labs.goo.ne.jp/api/textpair_doc 4 | """ 5 | import json 6 | import os 7 | import sys 8 | from enum import Enum 9 | 10 | import requests 11 | 12 | 13 | URL = 'https://labs.goo.ne.jp/api/textpair' 14 | 15 | 16 | APP_ID = os.environ.get('APP_ID') 17 | if APP_ID is None: 18 | print('configure APP_ID as environment variable') 19 | 20 | 21 | class ContentType(Enum): 22 | JSON = 'application/json' 23 | URLENCODED = 'application/x-www-form-urlencoded' 24 | 25 | 26 | def get_headers(content_type): 27 | return { 28 | 'Content-type': content_type.value 29 | } 30 | 31 | 32 | def post_with_json(data): 33 | print('application/json 形式でリクエスト') 34 | headers = get_headers(ContentType.JSON) 35 | r = requests.post(URL, headers=headers, data=json.dumps(data)) 36 | print(f'リクエストヘッダー: {r.request.headers}') 37 | print(f'リクエストボディ: {r.request.body}') 38 | print(f'レスポンス: {r.json()}') 39 | print('=' * 72) 40 | 41 | 42 | def main(): 43 | text1 = sys.argv[1] 44 | text2 = sys.argv[2] 45 | headers = get_headers(ContentType.JSON) 46 | data = { 47 | 'app_id': APP_ID, 48 | 'text1': text1, 49 | 'text2': text2, 50 | } 51 | 52 | # urlencoded 形式でリクエスト 53 | post_with_json(data) 54 | 55 | 56 | if __name__ == '__main__': 57 | main() 58 | -------------------------------------------------------------------------------- /BizPy/webapi/20200812/time_normalization.py: -------------------------------------------------------------------------------- 1 | """ 2 | 時刻情報正規化API 3 | https://labs.goo.ne.jp/api/jp/time-normalization 4 | """ 5 | import json 6 | import os 7 | import sys 8 | from enum import Enum 9 | 10 | import requests 11 | 12 | 13 | URL = 'https://labs.goo.ne.jp/api/chrono' 14 | 15 | APP_ID = os.environ.get('APP_ID') 16 | if APP_ID is None: 17 | print('configure APP_ID as environment variable') 18 | 19 | 20 | class ContentType(Enum): 21 | JSON = 'application/json' 22 | URLENCODED = 'application/x-www-form-urlencoded' 23 | 24 | 25 | def get_headers(content_type): 26 | return { 27 | 'Content-type': content_type.value 28 | } 29 | 30 | 31 | def post_with_urlencoded(data): 32 | print('application/x-www-form-urlencoded 形式でリクエスト') 33 | headers = get_headers(ContentType.URLENCODED) 34 | r = requests.post(URL, headers=headers, data=data) 35 | print(f'リクエストヘッダー: {r.request.headers}') 36 | print(f'リクエストボディ: {r.request.body}') 37 | print(f'レスポンス: {r.json()}') 38 | print('=' * 72) 39 | 40 | 41 | def main(): 42 | sentence = sys.argv[1] 43 | headers = get_headers(ContentType.JSON) 44 | data = { 45 | 'app_id': APP_ID, 46 | 'sentence': sentence, 47 | 'doc_time': '2020-08-12T09:00:00', 48 | } 49 | 50 | # urlencoded 形式でリクエスト 51 | post_with_urlencoded(data) 52 | 53 | 54 | if __name__ == '__main__': 55 | main() 56 | -------------------------------------------------------------------------------- /BizPy/webapi/20200909/client.py: -------------------------------------------------------------------------------- 1 | """ 2 | HTTP クライアント 3 | """ 4 | from enum import Enum 5 | 6 | import requests 7 | 8 | 9 | class ContentType(Enum): 10 | JSON = 'application/json' 11 | URLENCODED = 'application/x-www-form-urlencoded' 12 | 13 | 14 | class HttpClient: 15 | 16 | def __init__(self, headers={}): 17 | self.headers = headers 18 | 19 | def _handle_response(self, response): 20 | if response.ok: 21 | return response.json() 22 | # show extra information to confirm error 23 | print(f'{response.status_code=}, {response.reason=}') 24 | 25 | def get(self, url, params): 26 | response = requests.get(url, headers=self.headers, params=params) 27 | return self._handle_response(response) 28 | 29 | def post(self, url, data): 30 | self.headers['Content-type'] = ContentType.URLENCODED.value 31 | response = requests.post(url, headers=self.headers, data=data) 32 | return self._handle_response(response) 33 | -------------------------------------------------------------------------------- /BizPy/webapi/20200909/get_wikipedia_contents.py: -------------------------------------------------------------------------------- 1 | """ 2 | MediaWiki API を使って Wikipedia の記事を取得する 3 | """ 4 | import json 5 | import sys 6 | from pprint import pprint 7 | 8 | from client import HttpClient 9 | 10 | URL = 'https://ja.wikipedia.org/w/api.php' 11 | 12 | 13 | def main(): 14 | title = sys.argv[1] 15 | client = HttpClient() 16 | params = { 17 | 'action': 'query', 18 | 'format': 'json', 19 | 'prop': 'revisions', 20 | 'rvprop': 'content', 21 | 'titles': title, 22 | } 23 | data = client.get(URL, params) 24 | if data is not None: 25 | pprint(data) 26 | json.dump(data, open('contents.json', 'w')) # ファイルとして保存 27 | 28 | 29 | if __name__ == '__main__': 30 | main() 31 | -------------------------------------------------------------------------------- /BizPy/webapi/20200909/get_wikipedia_entities.py: -------------------------------------------------------------------------------- 1 | """ 2 | MediaWiki API を使って Wikipedia の記事に含まれるタイトルを取得する 3 | """ 4 | import json 5 | import sys 6 | from pprint import pprint 7 | 8 | from client import HttpClient 9 | from utils import get_contents, get_entities 10 | 11 | URL = 'https://ja.wikipedia.org/w/api.php' 12 | 13 | 14 | def main(): 15 | title = sys.argv[1] 16 | client = HttpClient() 17 | params = { 18 | 'action': 'query', 19 | 'format': 'json', 20 | 'prop': 'revisions', 21 | 'rvprop': 'content', 22 | 'titles': title, 23 | } 24 | data = client.get(URL, params) 25 | if data is not None: 26 | contents = get_contents(data) 27 | entities = get_entities(contents) 28 | pprint(entities) 29 | print('=' * 72) 30 | 31 | print('\nカテゴリのみを出力') 32 | for category in filter(lambda x: x.startswith('Category:'), entities): 33 | print(category) 34 | 35 | 36 | if __name__ == '__main__': 37 | main() 38 | -------------------------------------------------------------------------------- /BizPy/webapi/20200909/search_wikipedia_contents.py: -------------------------------------------------------------------------------- 1 | """ 2 | MediaWiki API を使って Wikipedia の記事を取得する 3 | """ 4 | import json 5 | import sys 6 | from pprint import pprint 7 | 8 | from client import HttpClient 9 | 10 | URL = 'https://ja.wikipedia.org/w/api.php' 11 | 12 | 13 | def search(client, keyword): 14 | titles = [] 15 | params = { 16 | 'action': 'query', 17 | 'format': 'json', 18 | 'list': 'search', 19 | 'srlimit': 100, 20 | 'sroffset': 0, 21 | 'srsearch': keyword, 22 | } 23 | 24 | while True: 25 | data = client.get(URL, params) 26 | if data is None: 27 | break 28 | 29 | #json.dump(data, open('search.json', 'w')) # ファイルとして保存 30 | #pprint(data) 31 | for item in data['query']['search']: 32 | titles.append(item['title']) 33 | 34 | totalhits = data['query']['searchinfo']['totalhits'] 35 | print(f'{totalhits=}') 36 | data_continue = data.get('continue') 37 | if data_continue is None: 38 | break 39 | 40 | sroffset = data_continue['sroffset'] 41 | print(f'{sroffset=}') 42 | if totalhits <= sroffset or 500 == sroffset: 43 | break 44 | 45 | params['sroffset'] = sroffset 46 | 47 | return titles 48 | 49 | 50 | def main(): 51 | keyword = sys.argv[1] 52 | client = HttpClient() 53 | 54 | titles = search(client, keyword) 55 | pprint(titles) 56 | print(f'{len(titles)=}') 57 | 58 | 59 | if __name__ == '__main__': 60 | main() 61 | -------------------------------------------------------------------------------- /BizPy/webapi/20200909/utils.py: -------------------------------------------------------------------------------- 1 | import json 2 | import re 3 | from pprint import pprint 4 | 5 | RE_ENTITIES = re.compile(r'\[\[(.*?)\]\]', re.MULTILINE) 6 | 7 | 8 | def get_entities(contents): 9 | entities = [] 10 | matches = RE_ENTITIES.findall(contents) 11 | if matches is not None: 12 | for entity in matches: 13 | if '|' in entity: 14 | entity = entity.split('|')[0] 15 | entities.append(entity) 16 | return entities 17 | 18 | 19 | def get_contents(data): 20 | for key in data['query']['pages']: 21 | revision = data['query']['pages'][key]['revisions'][0] 22 | contents = revision['*'] 23 | return contents 24 | 25 | 26 | def test(path='./contents.json'): 27 | data = json.load(open(path)) 28 | contents = get_contents(data) 29 | entities = get_entities(contents) 30 | pprint(entities) 31 | 32 | 33 | if __name__ == '__main__': 34 | test() 35 | -------------------------------------------------------------------------------- /ExpertPythonProgramming/SecondEdition/01/future/foo.py: -------------------------------------------------------------------------------- 1 | print('I am toplevel foo') 2 | -------------------------------------------------------------------------------- /ExpertPythonProgramming/SecondEdition/01/future/non_future.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Python 2.x から 3.x へなるべく移行しやすくするための 4 | __future__ インポートの機能 5 | 6 | * http://docs.python.jp/2/library/__future__.html 7 | * http://methane.hatenablog.jp/entry/2014/01/18/Python_2/3_%E4%B8%A1%E5%AF%BE%E5%BF%9C%E3%81%AE%E3%81%9F%E3%82%81%E3%81%AB_%60unicode_literals%60_%E3%82%92%E4%BD%BF%E3%81%86%E3%81%B9%E3%81%8D%E3%81%8B 8 | """ 9 | 10 | 11 | def main(): 12 | # print statement 13 | print 'statement' 14 | print('statement') 15 | 16 | # division 17 | print('10 / 3 =', 10 / 3) 18 | 19 | # str/unicode string 20 | s = 'abc' 21 | u = u'あいうえお' 22 | print s, type(s) 23 | print u, type(u) 24 | 25 | 26 | if __name__ == '__main__': 27 | main() 28 | -------------------------------------------------------------------------------- /ExpertPythonProgramming/SecondEdition/01/future/sub/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/t2y/python-study/52a132ea600d4696164e540d8a8f8f5fc58e097a/ExpertPythonProgramming/SecondEdition/01/future/sub/__init__.py -------------------------------------------------------------------------------- /ExpertPythonProgramming/SecondEdition/01/future/sub/absolute.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Python 2.x から 3.x へなるべく移行しやすくするための 4 | __future__ インポートの機能 5 | 6 | * http://docs.python.jp/2/library/__future__.html 7 | 8 | $ python -c "import sub.absolute" 9 | """ 10 | from __future__ import absolute_import 11 | 12 | import foo 13 | 14 | -------------------------------------------------------------------------------- /ExpertPythonProgramming/SecondEdition/01/future/sub/foo.py: -------------------------------------------------------------------------------- 1 | print('I am module foo') 2 | -------------------------------------------------------------------------------- /ExpertPythonProgramming/SecondEdition/01/future/sub/relative.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | $ python -c "import sub.relative" 4 | """ 5 | import foo 6 | -------------------------------------------------------------------------------- /ExpertPythonProgramming/SecondEdition/01/future/use_future.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Python 2.x から 3.x へなるべく移行しやすくするための 4 | __future__ インポートの機能 5 | 6 | * http://docs.python.jp/2/library/__future__.html 7 | * http://methane.hatenablog.jp/entry/2014/01/18/Python_2/3_%E4%B8%A1%E5%AF%BE%E5%BF%9C%E3%81%AE%E3%81%9F%E3%82%81%E3%81%AB_%60unicode_literals%60_%E3%82%92%E4%BD%BF%E3%81%86%E3%81%B9%E3%81%8D%E3%81%8B 8 | """ 9 | from __future__ import division 10 | from __future__ import print_function 11 | from __future__ import unicode_literals 12 | 13 | 14 | def main(): 15 | # print function 16 | print('function') 17 | print(type(print)) 18 | 19 | # division 20 | print('10 / 3 =', 10 / 3) 21 | 22 | # str/unicode string 23 | s = 'abc' 24 | u = u'あいうえお' 25 | print(s, type(s)) 26 | print(u, type(u)) 27 | 28 | 29 | if __name__ == '__main__': 30 | main() 31 | -------------------------------------------------------------------------------- /ExpertPythonProgramming/ThirdEdition/5/import-path-hooks/main.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from importlib.abc import Loader 3 | from importlib.abc import PathEntryFinder 4 | from importlib.util import spec_from_loader 5 | 6 | 7 | class OtherModuleLoader(Loader): 8 | 9 | def __init__(self, filename): 10 | self.filename = filename 11 | 12 | def create_module(self, spec): 13 | print(f'{spec=}') 14 | return None 15 | 16 | def exec_module(self, module): 17 | print(f'{module=}') 18 | 19 | with open(self.filename) as f: 20 | data = f.read() 21 | 22 | _globals = vars(module) 23 | _globals['__file__'] = self.filename 24 | exec(data, _globals) 25 | 26 | 27 | class OtherPathEntryFinder(PathEntryFinder): 28 | 29 | def __init__(self, path_entry): 30 | print(f'{path_entry=}') 31 | self.path_entry = path_entry 32 | 33 | def find_spec(self, fullname, path, target=None): 34 | print(f'{fullname=}') 35 | print(f'{path=}') 36 | print(f'{target=}') 37 | 38 | path = path or self.path_entry 39 | if fullname == path: 40 | name = fullname 41 | other_module_loader = OtherModuleLoader('other.py') 42 | return spec_from_loader(name, loader=other_module_loader) 43 | 44 | 45 | def main(): 46 | print('start') 47 | 48 | print(f'{sys.path_hooks=}') 49 | sys.path_hooks.append(OtherPathEntryFinder) 50 | sys.path.insert(0, 'trigger') 51 | 52 | print('===== import trigger module') 53 | 54 | import trigger 55 | print(trigger.__file__) 56 | 57 | print('end') 58 | 59 | 60 | if __name__ == '__main__': 61 | main() 62 | -------------------------------------------------------------------------------- /ExpertPythonProgramming/ThirdEdition/5/import-path-hooks/other.py: -------------------------------------------------------------------------------- 1 | print('other') 2 | -------------------------------------------------------------------------------- /ExpertPythonProgramming/ThirdEdition/5/meta-hooks/main.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from importlib.abc import Loader, MetaPathFinder 3 | from importlib.util import spec_from_loader 4 | 5 | 6 | class OtherModuleLoader(Loader): 7 | 8 | def __init__(self, filename): 9 | self.filename = filename 10 | 11 | def create_module(self, spec): 12 | print(f'{spec=}') 13 | return None 14 | 15 | def exec_module(self, module): 16 | print(f'{module=}') 17 | 18 | with open(self.filename) as f: 19 | data = f.read() 20 | 21 | _globals = vars(module) 22 | _globals['__file__'] = self.filename 23 | exec(data, _globals) 24 | 25 | 26 | class MyOSModuleLoader(Loader): 27 | 28 | def create_module(self, spec): 29 | print(f'{spec=}') 30 | return None 31 | 32 | def exec_module(self, module): 33 | print(f'{module=}') 34 | import os 35 | 36 | 37 | class MyMetaPathFinder(MetaPathFinder): 38 | 39 | def find_spec(self, fullname, path, target=None): 40 | print(f'{fullname=}') 41 | print(f'{path=}') 42 | print(f'{target=}') 43 | if fullname == 'sub': 44 | name = 'other' 45 | other_module_loader = OtherModuleLoader('other.py') 46 | return spec_from_loader(name, loader=other_module_loader) 47 | elif fullname == 'os': 48 | return spec_from_loader('os', loader=MyOSModuleLoader()) 49 | 50 | return None 51 | 52 | 53 | def main(): 54 | print('start') 55 | 56 | print(f'{sys.meta_path=}') 57 | sys.meta_path.insert(0, MyMetaPathFinder()) 58 | 59 | print('===== import sub module') 60 | import sub 61 | print(sub.__file__) 62 | print() 63 | 64 | print('===== import os module') 65 | import os # already sys.modules has os module 66 | print(sys.modules['os']) 67 | 68 | del sys.modules['os'] 69 | import os 70 | print(sys.modules['os']) 71 | 72 | print('end') 73 | 74 | 75 | if __name__ == '__main__': 76 | main() 77 | -------------------------------------------------------------------------------- /ExpertPythonProgramming/ThirdEdition/5/meta-hooks/other.py: -------------------------------------------------------------------------------- 1 | print('ohter') 2 | -------------------------------------------------------------------------------- /ExpertPythonProgramming/ThirdEdition/5/meta-hooks/sub.py: -------------------------------------------------------------------------------- 1 | print('sub') 2 | -------------------------------------------------------------------------------- /HighPerformancePython/01/vectorization.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | """ 4 | ベクトル化 5 | 6 | * https://en.wikipedia.org/wiki/Automatic_vectorization 7 | * https://ja.wikipedia.org/wiki/ベクトル化 8 | 9 | 10 | そうだ! EuroPython 2011へ行こう 11 | #2 CPythonについてのハンズオン,講演 12 | 13 | * http://gihyo.jp/news/report/01/europython2011/0002 14 | 15 | 16 | Python List Comprehension Vs. Map 17 | 18 | * http://stackoverflow.com/questions/1247486/python-list-comprehension-vs-map 19 | """ 20 | 21 | import string 22 | 23 | 24 | def use_list_comprehension(letters): 25 | """ 26 | >>> import string 27 | >>> use_list_comprehension(string.ascii_letters) # doctest: +ELLIPSIS 28 | [97, 98, 99, ..., 88, 89, 90] 29 | """ 30 | return [ord(i) for i in letters] 31 | 32 | 33 | def use_map(letters): 34 | """ 35 | >>> import string 36 | >>> list(use_map(string.ascii_letters)) # doctest: +ELLIPSIS 37 | [97, 98, 99, ..., 88, 89, 90] 38 | """ 39 | return map(ord, letters) 40 | -------------------------------------------------------------------------------- /HighPerformancePython/02/julia1_py3.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Julia set generator with optional Pillow-based image drawing 4 | 5 | * https://github.com/mynameisfiber/high_performance_python 6 | """ 7 | 8 | 9 | import time 10 | from PIL import Image 11 | import array 12 | 13 | # area of complex space to investigate 14 | x1, x2, y1, y2 = -1.8, 1.8, -1.8, 1.8 15 | c_real, c_imag = -0.62772, -.42193 16 | 17 | 18 | def show_greyscale(output_raw, width, height, max_iterations): 19 | """Convert list to array, show using PIL""" 20 | # convert our output to PIL-compatible input 21 | # scale to [0...255] 22 | max_iterations = float(max(output_raw)) 23 | print(max_iterations) 24 | scale_factor = float(max_iterations) 25 | scaled = [int(o / scale_factor * 255) for o in output_raw] 26 | output = array.array('B', scaled) # array of unsigned ints 27 | # display with PIL 28 | im = Image.new("L", (width, width)) 29 | # EXPLAIN RAW L 0 -1 30 | im.frombytes(output.tostring(), "raw", "L", 0, -1) 31 | im.show() 32 | 33 | 34 | def show_false_greyscale(output_raw, width, height, max_iterations): 35 | """Convert list to array, show using PIL""" 36 | # convert our output to PIL-compatible input 37 | # sanity check our 1D array and desired 2D form 38 | assert width * height == len(output_raw) 39 | # rescale output_raw to be in the inclusive range [0..255] 40 | max_value = float(max(output_raw)) 41 | output_raw_limited = [int(float(o) / max_value * 255) for o in output_raw] 42 | # create a slightly fancy colour map that shows colour changes with 43 | # increased contrast (thanks to John Montgomery) 44 | output_rgb = ( 45 | (o + (256 * o) + (256 ** 2) * o) * 16 for o in output_raw_limited) # fancier 46 | # array of unsigned ints (size is platform specific) 47 | output_rgb = array.array('I', output_rgb) 48 | # display with PIL/pillow 49 | im = Image.new("RGB", (width, height)) 50 | # EXPLAIN RGBX L 0 -1 51 | im.frombytes(output_rgb.tostring(), "raw", "RGBX", 0, -1) 52 | im.show() 53 | 54 | 55 | def calculate_z_serial_purepython(maxiter, zs, cs): 56 | """Calculate output list using Julia update rule""" 57 | output = [0] * len(zs) 58 | for i in range(len(zs)): 59 | n = 0 60 | z = zs[i] 61 | c = cs[i] 62 | while abs(z) < 2 and n < maxiter: 63 | z = z * z + c 64 | n += 1 65 | output[i] = n 66 | return output 67 | 68 | 69 | def calc_pure_python(draw_output, desired_width, max_iterations): 70 | """Create a list of complex co-ordinates (zs) and complex parameters (cs), build Julia set and display""" 71 | x_step = (float(x2 - x1) / float(desired_width)) 72 | y_step = (float(y1 - y2) / float(desired_width)) 73 | x = [] 74 | y = [] 75 | ycoord = y2 76 | while ycoord > y1: 77 | y.append(ycoord) 78 | ycoord += y_step 79 | xcoord = x1 80 | while xcoord < x2: 81 | x.append(xcoord) 82 | xcoord += x_step 83 | # set width and height to the generated pixel counts, rather than the 84 | # pre-rounding desired width and height 85 | width = len(x) 86 | height = len(y) 87 | # build a list of co-ordinates and the initial condition for each cell. 88 | # Note that our initial condition is a constant and could easily be removed, 89 | # we use it to simulate a real-world scenario with several inputs to our 90 | # function 91 | zs = [] 92 | cs = [] 93 | for ycoord in y: 94 | for xcoord in x: 95 | zs.append(complex(xcoord, ycoord)) 96 | cs.append(complex(c_real, c_imag)) 97 | 98 | print("Length of x:", len(x)) 99 | print("Total elements:", len(zs)) 100 | start_time = time.time() 101 | output = calculate_z_serial_purepython(max_iterations, zs, cs) 102 | end_time = time.time() 103 | secs = end_time - start_time 104 | print(calculate_z_serial_purepython.__name__ + " took", secs, "seconds") 105 | 106 | # this sum is expected for 1000^2 grid with 300 iterations 107 | assert sum(output) == 33219980 108 | 109 | if draw_output: 110 | #show_false_greyscale(output, width, height, max_iterations) 111 | show_greyscale(output, width, height, max_iterations) 112 | 113 | 114 | if __name__ == "__main__": 115 | # Calculate the Julia set using a pure Python solution with 116 | # reasonable defaults for a laptop 117 | # set draw_output to True to use PIL to draw an image 118 | calc_pure_python(draw_output=True, desired_width=1000, max_iterations=300) 119 | -------------------------------------------------------------------------------- /HighPerformancePython/03/list_vs_tuple.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | リストとタプルの比較 4 | """ 5 | 6 | 7 | def create_list(n): 8 | """ 9 | >>> create_list(10) 10 | [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] 11 | """ 12 | return list(range(n)) 13 | 14 | 15 | def create_tuple(n): 16 | """ 17 | >>> create_tuple(10) 18 | (0, 1, 2, 3, 4, 5, 6, 7, 8, 9) 19 | """ 20 | return tuple(range(n)) 21 | 22 | 23 | def append_list(n): 24 | """ 25 | >>> append_list(10) 26 | [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] 27 | """ 28 | s = [] 29 | for i in range(n): 30 | s.append(i) 31 | return s 32 | 33 | 34 | def append_list_comprehension(n): 35 | """ 36 | >>> append_list_comprehension(10) 37 | [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] 38 | """ 39 | return [i for i in range(n)] 40 | 41 | 42 | def concatenate_sequence(s1, s2): 43 | """ 44 | >>> l1, l2 = list(range(10)), list(range(10, 20)) 45 | >>> concatenate_sequence(l1, l2) 46 | [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19] 47 | 48 | >>> t1, t2 = tuple(range(10)), tuple(range(10, 20)) 49 | >>> concatenate_sequence(t1, t2) 50 | (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19) 51 | """ 52 | return s1 + s2 53 | -------------------------------------------------------------------------------- /HighPerformancePython/03/use_bisect.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | bisect モジュールの紹介 4 | 5 | * http://docs.python.jp/3.5/library/bisect.html 6 | """ 7 | 8 | import bisect 9 | 10 | 11 | def index(seq, n): 12 | """ 13 | >>> l = [0, 1, 2, 3, 3, 3, 4, 5, 6, 7] 14 | >>> index(l, 3) 15 | (3, 6) 16 | """ 17 | return bisect.bisect_left(seq, n), bisect.bisect(seq, n) 18 | 19 | 20 | def grade(score, breakpoints=[60, 70, 80, 90], grades='FDCBA'): 21 | """ 22 | http://docs.python.jp/3.5/library/bisect.html#other-examplesa 23 | 24 | >>> [grade(score) for score in [33, 99, 77, 70, 89, 90, 100]] 25 | ['F', 'A', 'C', 'C', 'B', 'A', 'A'] 26 | """ 27 | i = bisect.bisect(breakpoints, score) 28 | return grades[i] 29 | -------------------------------------------------------------------------------- /HighPerformancePython/04/compare_namespace.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | 名前空間の検索 4 | """ 5 | 6 | import math 7 | from math import sin 8 | 9 | 10 | def test1(x): 11 | """ 12 | >>> test1(3) 13 | 0.1411200080598672 14 | 15 | >>> import dis 16 | >>> dis.dis(test1) 17 | 23 0 LOAD_GLOBAL 0 (math) 18 | 3 LOAD_ATTR 1 (sin) 19 | 6 LOAD_FAST 0 (x) 20 | 9 CALL_FUNCTION 1 (1 positional, 0 keyword pair) 21 | 12 RETURN_VALUE 22 | """ 23 | return math.sin(x) 24 | 25 | 26 | def test2(x): 27 | """ 28 | >>> test2(3) 29 | 0.1411200080598672 30 | 31 | >>> import dis 32 | >>> dis.dis(test2) 33 | 38 0 LOAD_GLOBAL 0 (sin) 34 | 3 LOAD_FAST 0 (x) 35 | 6 CALL_FUNCTION 1 (1 positional, 0 keyword pair) 36 | 9 RETURN_VALUE 37 | """ 38 | return sin(x) 39 | 40 | 41 | def test3(x, sin=math.sin): 42 | """ 43 | >>> test3(3) 44 | 0.1411200080598672 45 | 46 | >>> import dis 47 | >>> dis.dis(test3) 48 | 53 0 LOAD_FAST 1 (sin) 49 | 3 LOAD_FAST 0 (x) 50 | 6 CALL_FUNCTION 1 (1 positional, 0 keyword pair) 51 | 9 RETURN_VALUE 52 | """ 53 | return sin(x) 54 | 55 | 56 | def use_global_sin(n): 57 | for _ in range(n): 58 | sin(3) 59 | 60 | 61 | def use_local_sin(n): 62 | sin = math.sin 63 | for _ in range(n): 64 | sin(3) 65 | -------------------------------------------------------------------------------- /HighPerformancePython/04/create_large_dict.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | 巨大な辞書を作るときの tips 4 | 5 | * http://stackoverflow.com/questions/16256913/improving-performance-of-very-large-dictionary-in-python 6 | """ 7 | 8 | def create_dict(keys): 9 | """ 10 | >>> keys = {'red', 'green', 'blue', 'yellow', 'orange', 'pink', 'black'} 11 | >>> create_dict(keys) 12 | {'pink': None, 'red': None, 'black': None, 'green': None, 'yellow': None, 'orange': None, 'blue': None} 13 | """ 14 | d = dict.fromkeys(keys) # dict is pre-sized to 32 empty slots 15 | d.update(dict(d)) # This makes room for additional keys and makes the set collision-free. 16 | return d 17 | -------------------------------------------------------------------------------- /HighPerformancePython/05/test.txt: -------------------------------------------------------------------------------- 1 | abc 2 | def 3 | ghi 4 | jkl 5 | mno 6 | -------------------------------------------------------------------------------- /HighPerformancePython/05/yield_sample.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | ジェネレーターの利用例 4 | """ 5 | 6 | 7 | def tuggle(): 8 | """ 9 | >>> g = tuggle() 10 | >>> next(g) 11 | True 12 | >>> next(g) 13 | False 14 | >>> next(g) 15 | True 16 | """ 17 | r = False 18 | while True: 19 | r = not r 20 | yield r 21 | 22 | 23 | def file_read(file_name): 24 | with open(file_name) as f: 25 | for line in f: 26 | yield line.strip('\n') 27 | 28 | 29 | def modern_style_file_read_with_block(file_name, n): 30 | with open(file_name) as f: 31 | for block in iter(lambda: f.read(n), ''): 32 | yield block 33 | 34 | 35 | def old_style_file_read_with_block(file_name, n): 36 | with open(file_name) as f: 37 | while True: 38 | block = f.read(n) 39 | yield block 40 | if block == '': 41 | break 42 | -------------------------------------------------------------------------------- /HighPerformancePython/08/yield_from1.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | """ 4 | yield from 構文 5 | 6 | * https://www.python.org/dev/peps/pep-0380/ 7 | """ 8 | 9 | def g1(n): 10 | for i in range(n): 11 | yield i 12 | 13 | 14 | def g2_wrong(n): 15 | yield g1(n) 16 | 17 | 18 | def g2_delegate(n): 19 | """ 20 | 値を返すだけならこのコーディングも yield from も同じ 21 | """ 22 | for i in g1(n): 23 | yield i 24 | 25 | 26 | def g2_new_syntax(n): 27 | yield from g1(n) 28 | 29 | 30 | def main(): 31 | for i in g2_wrong(3): 32 | print(i) 33 | 34 | for i in g2_delegate(3): 35 | print(i) 36 | 37 | for i in g2_new_syntax(3): 38 | print(i) 39 | 40 | 41 | if __name__ == '__main__': 42 | main() 43 | -------------------------------------------------------------------------------- /HighPerformancePython/08/yield_from2.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | """ 4 | yield from 構文 5 | 6 | * https://www.python.org/dev/peps/pep-0380/ 7 | """ 8 | 9 | def g1(n): 10 | v = None 11 | for i in range(n): 12 | v = 0 if v is None else v 13 | v = yield i + v 14 | 15 | 16 | def g2_delegate(n): 17 | """ 18 | send() メソッドはこのジェネレーターに値を送るため、 19 | ここで値を受け渡すような実装にしないといけない => g2_delegate_kai を参照 20 | """ 21 | for i in g1(n): 22 | yield i 23 | 24 | 25 | def g2_delegate_kai(n): 26 | """ 27 | 途端にジェネレーターの委譲が難しくなった! 28 | """ 29 | g = g1(n) 30 | v = yield next(g) 31 | while True: 32 | v = yield g.send(v) 33 | 34 | 35 | def g2_new_syntax(n): 36 | yield from g1(n) 37 | 38 | 39 | def main(): 40 | gd = g2_delegate(3) 41 | print(next(gd)) 42 | print(gd.send(2)) 43 | print(gd.send(4)) 44 | 45 | print('-' * 72) 46 | 47 | gn = g2_new_syntax(3) 48 | print(next(gn)) 49 | print(gn.send(2)) 50 | print(gn.send(4)) 51 | 52 | print('-' * 72) 53 | 54 | gdk = g2_delegate_kai(3) 55 | print(next(gdk)) 56 | print(gdk.send(2)) 57 | print(gdk.send(4)) 58 | 59 | 60 | if __name__ == '__main__': 61 | main() 62 | -------------------------------------------------------------------------------- /HighPerformancePython/11/non_slots_example.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | __slots__ 4 | 5 | クラスの属性情報を保存している __dict__ を生成しないことでメモリを節約する 6 | 7 | * http://docs.python.jp/3/reference/datamodel.html#slots 8 | """ 9 | import memory_profiler 10 | 11 | 12 | class A(object): 13 | def __init__(self, x, y, z): 14 | self.x = x 15 | self.y = y 16 | self.z = z 17 | 18 | 19 | if __name__ == "__main__": 20 | instance_num = 10000 21 | initial = memory_profiler.memory_usage()[0] 22 | print "RAM at start {:0.1f}MiB".format(initial) 23 | 24 | data_non_slots = [A(1, 2, 3) for _ in range(instance_num)] 25 | after_create_non_slots = memory_profiler.memory_usage()[0] 26 | 27 | increase_mem = after_create_non_slots - initial 28 | print "RAM after creating instance without __slots__ {:0.1f}MiB, "\ 29 | "increased {:0.1f}MiB".format(after_create_non_slots, increase_mem) 30 | -------------------------------------------------------------------------------- /HighPerformancePython/11/slots_example.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | __slots__ 4 | 5 | クラスの属性情報を保存している __dict__ を生成しないことでメモリを節約する 6 | 7 | * http://docs.python.jp/3/reference/datamodel.html#slots 8 | """ 9 | import memory_profiler 10 | 11 | 12 | class S(object): 13 | __slots__ = ['x', 'y', 'z'] 14 | 15 | def __init__(self, x, y, z): 16 | self.x = x 17 | self.y = y 18 | self.z = z 19 | 20 | 21 | if __name__ == "__main__": 22 | instance_num = 10000 23 | initial = memory_profiler.memory_usage()[0] 24 | print "RAM at start {:0.1f}MiB".format(initial) 25 | 26 | data_with_slots = [S(1, 2, 3) for _ in range(instance_num)] 27 | after_create_with_slots = memory_profiler.memory_usage()[0] 28 | 29 | increase_mem = after_create_with_slots - initial 30 | print "RAM after creating instance with __slots__ {:0.1f}MiB, "\ 31 | "increased {:0.1f}MiB".format(after_create_with_slots, increase_mem) 32 | -------------------------------------------------------------------------------- /LanguageProcessing/100-knocks-2015/README.md: -------------------------------------------------------------------------------- 1 | ## 言語処理100本ノック 2015 2 | 3 | * http://www.cl.ecei.tohoku.ac.jp/nlp100/ 4 | -------------------------------------------------------------------------------- /LanguageProcessing/100-knocks-2015/chapter1/00.py: -------------------------------------------------------------------------------- 1 | """ 2 | 00. 文字列の逆順 3 | 文字列"stressed"の文字を逆に(末尾から先頭に向かって)並べた文字列を得よ. 4 | """ 5 | s = 'stressed' 6 | print(''.join(reversed(s))) 7 | -------------------------------------------------------------------------------- /LanguageProcessing/100-knocks-2015/chapter1/01.py: -------------------------------------------------------------------------------- 1 | """ 2 | 01. 「パタトクカシーー」 3 | 「パタトクカシーー」という文字列の1,3,5,7文字目を取り出して連結した文字列を得よ 4 | """ 5 | 6 | s = 'パタトクカシーー' 7 | # answer1 8 | print('%s%s%s%s' % (s[0], s[2], s[4], s[6])) 9 | print('{}{}{}{}'.format(s[0], s[2], s[4], s[6])) 10 | print(f'{s[0]}{s[2]}{s[4]}{s[6]}') 11 | 12 | # answer2 13 | print(''.join(s[i] for i in range(0, 7, 2))) 14 | 15 | # answer3 16 | from operator import itemgetter 17 | print(''.join(itemgetter(0, 2, 4, 6)(s))) 18 | 19 | # answer4 20 | print(''.join(c for i, c in enumerate(s) if i % 2 == 0)) 21 | 22 | # answer5 23 | print(s[::2]) 24 | -------------------------------------------------------------------------------- /LanguageProcessing/100-knocks-2015/chapter1/02.py: -------------------------------------------------------------------------------- 1 | """ 2 | 02. 「パトカー」+「タクシー」=「パタトクカシーー」 3 | 「パトカー」+「タクシー」の文字を先頭から交互に連結して文字列「パタトクカシーー」を得よ. 4 | """ 5 | 6 | s1 = 'パトカー' 7 | s2 = 'タクシー' 8 | print(''.join(i + j for i, j in zip(s1, s2))) 9 | -------------------------------------------------------------------------------- /LanguageProcessing/100-knocks-2015/chapter1/03.py: -------------------------------------------------------------------------------- 1 | """ 2 | 03. 円周率 3 | "Now I need a drink, alcoholic of course, after the heavy lectures involving quantum mechanics."という文を単語に分解し,各単語の(アルファベットの)文字数を先頭から出現順に並べたリストを作成せよ. 4 | """ 5 | s = 'Now I need a drink, alcoholic of course, after the heavy lectures involving quantum mechanics.' 6 | words = s.replace(',', '').replace('.', '').split() 7 | print(''.join(str(len(i)) for i in words)) 8 | -------------------------------------------------------------------------------- /LanguageProcessing/100-knocks-2015/chapter1/04.py: -------------------------------------------------------------------------------- 1 | """ 2 | 04. 元素記号 3 | "Hi He Lied Because Boron Could Not Oxidize Fluorine. New Nations Might Also Sign Peace Security Clause. Arthur King Can." 4 | という文を単語に分解し,1, 5, 6, 7, 8, 9, 15, 16, 19番目の単語は先頭の1文字,それ以外の単語は先頭に2文字を取り出し, 5 | 取り出した文字列から単語の位置(先頭から何番目の単語か)への連想配列(辞書型もしくはマップ型)を作成せよ. 6 | """ 7 | s = 'Hi He Lied Because Boron Could Not Oxidize Fluorine. New Nations Might Also Sign Peace Security Clause. Arthur King Can.' 8 | d = {} 9 | index = {0, 4, 5, 6, 7, 8, 14, 15, 18} 10 | words = s.replace('.', '').split() 11 | for i, word in enumerate(words): 12 | num = i + 1 13 | if i in index: 14 | d[word[0]] = num 15 | else: 16 | d[word[0:2]] = num 17 | print(d) 18 | 19 | from pprint import pprint 20 | pprint(d) 21 | -------------------------------------------------------------------------------- /LanguageProcessing/100-knocks-2015/chapter1/05.py: -------------------------------------------------------------------------------- 1 | """ 2 | 05. n-gram 3 | 与えられたシーケンス(文字列やリストなど)からn-gramを作る関数を作成せよ.この関数を用い,"I am an NLPer"という文から単語bi-gram,文字bi-gramを得よ. 4 | """ 5 | 6 | def char_bi_gram(s): 7 | length = len(s) 8 | if length < 2: 9 | yield s 10 | return 11 | 12 | for i, c in enumerate(s): 13 | yield c + s[i + 1] 14 | if i == length - 2: 15 | break 16 | 17 | def word_bi_gram(s): 18 | for word in s.split(): 19 | yield from char_bi_gram(word) 20 | 21 | s = 'I am an NLPer' 22 | print(f"'{s}'") 23 | print(list(char_bi_gram(s))) 24 | print(list(word_bi_gram(s))) 25 | -------------------------------------------------------------------------------- /LanguageProcessing/100-knocks-2015/chapter1/06.py: -------------------------------------------------------------------------------- 1 | """ 2 | 06. 集合 3 | "paraparaparadise"と"paragraph"に含まれる文字bi-gramの集合を, 4 | それぞれ,XとYとして求め,XとYの和集合,積集合,差集合を求めよ. 5 | さらに,'se'というbi-gramがXおよびYに含まれるかどうかを調べよ. 6 | """ 7 | 8 | def char_bi_gram(s): 9 | length = len(s) 10 | if length < 2: 11 | yield s 12 | return 13 | 14 | for i, c in enumerate(s): 15 | yield c + s[i + 1] 16 | if i == length - 2: 17 | break 18 | 19 | s1 = 'paraparaparadise' 20 | s1_bi_gram = set(char_bi_gram(s1)) 21 | print(f's1: {s1_bi_gram}') 22 | 23 | s2 = 'paragraph' 24 | s2_bi_gram = set(char_bi_gram(s2)) 25 | print(f's2: {s2_bi_gram}') 26 | 27 | def show_sets_operation(s1, s2): 28 | sum_of_sets = s1.copy() 29 | sum_of_sets.update(s2) 30 | print(f'sum: {sum_of_sets}') 31 | 32 | product_sets = s1.copy() 33 | product_sets.intersection_update(s2) 34 | print(f'product: {product_sets}') 35 | 36 | difference_sets = s1.copy() 37 | difference_sets.difference_update(s2) 38 | print(f'diff: {difference_sets}') 39 | 40 | show_sets_operation(s1_bi_gram, s2_bi_gram) 41 | -------------------------------------------------------------------------------- /MiniOsaka/abc_like_interface.py: -------------------------------------------------------------------------------- 1 | from abc import ABCMeta, abstractmethod 2 | 3 | class InterfaceGreet(metaclass=ABCMeta): 4 | """ 5 | Interface like in Python 6 | """ 7 | @property 8 | @abstractmethod 9 | def name(self): ... 10 | 11 | @abstractmethod 12 | def greet(self, other): ... 13 | 14 | class Person(InterfaceGreet): 15 | name = '' 16 | 17 | p = Person() # error: Cannot instantiate abstract class 18 | # 'Person' with abstract attribute 'greet' 19 | -------------------------------------------------------------------------------- /MiniOsaka/demo_greet.py: -------------------------------------------------------------------------------- 1 | 2 | 3 | def greet(x, y): 4 | x.greet(y.name) 5 | -------------------------------------------------------------------------------- /MiniOsaka/demo_greetable1.py: -------------------------------------------------------------------------------- 1 | from typing_extensions import Protocol 2 | 3 | class Greetable(Protocol): 4 | name: str 5 | 6 | def greet(self, other: str) -> None: ... 7 | 8 | class Person: 9 | def __init__(self, name: str) -> None: 10 | self.name = name 11 | 12 | def greet(self, other: str) -> None: 13 | print('こんにちはー {}'.format(other)) 14 | 15 | class Cat: 16 | def __init__(self, name: str) -> None: 17 | self.name = name 18 | 19 | def greet(self, other: str) -> None: 20 | print('にゃあー {}'.format(other)) 21 | 22 | class Anonymous: 23 | def greet(self, other: str) -> None: 24 | print('やあ {}'.format(other)) 25 | 26 | def greet_each_other(x: Greetable, y: Greetable) -> None: 27 | x.greet(y.name) 28 | y.greet(x.name) 29 | 30 | def main() -> None: 31 | morimoto = Person('もりもと') 32 | tama = Cat('たま') 33 | greet_each_other(morimoto, tama) 34 | 35 | anonymous = Anonymous() 36 | greet_each_other(morimoto, anonymous) 37 | # error: Argument 2 to "greet_each_other" has incompatible type "Anonymous"; expected "Greetable" 38 | # note: 'Anonymous' is missing following 'Greetable' protocol member: 39 | # note: name 40 | 41 | if __name__ == '__main__': 42 | main() 43 | -------------------------------------------------------------------------------- /MiniOsaka/demo_greetable2.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | from typing_extensions import Protocol 3 | 4 | class Greetable(Protocol): 5 | name: str 6 | 7 | def greet(self, other: str) -> None: ... 8 | 9 | class Person: 10 | def __init__(self, name: str) -> None: 11 | self.name = name 12 | 13 | def greet(self, other: str) -> None: 14 | print('こんにちはー {}'.format(other)) 15 | 16 | class Cat: 17 | def __init__(self, name: str) -> None: 18 | self.name = name 19 | 20 | def greet(self, other: str) -> None: 21 | print('にゃあー {}'.format(other)) 22 | 23 | class Anonymous: 24 | def greet(self, other: str) -> None: 25 | print('やあ {}'.format(other)) 26 | 27 | def greet_each_other(x: Greetable, y: Greetable) -> None: 28 | x.greet(y.name) 29 | y.greet(x.name) 30 | 31 | def greet(animals: List[Greetable]) -> None: 32 | for i, animal in enumerate(animals[:-1]): 33 | print('=' * 32) 34 | greet_each_other(animal, animals[i+1]) 35 | 36 | def main() -> None: 37 | animals: List[Greetable] = [Person('もりもと'), Cat('たま'), Anonymous()] 38 | # error: List item 2 has incompatible type "Anonymous"; expected "Greetable" 39 | # note: 'Anonymous' is missing following 'Greetable' protocol member: 40 | # note: name 41 | greet(animals) 42 | 43 | if __name__ == '__main__': 44 | main() 45 | -------------------------------------------------------------------------------- /MiniOsaka/demo_greetable_protocol.py: -------------------------------------------------------------------------------- 1 | from typing_extensions import Protocol 2 | 3 | class Greetable(Protocol): 4 | name: str 5 | 6 | def greet(self, other: str) -> None: ... 7 | 8 | class Person: 9 | def __init__(self, name: str) -> None: 10 | self.name = name 11 | 12 | p: Greetable = Person('もりもと') 13 | # error: Incompatible types in assignment (expression has type "Person", variable has type "Greetable") 14 | # note: 'Person' is missing following 'Greetable' protocol member: 15 | # note: greet 16 | -------------------------------------------------------------------------------- /MiniOsaka/demo_typehint.py: -------------------------------------------------------------------------------- 1 | import json 2 | import sys 3 | from pprint import pformat 4 | from typing import Any, Dict 5 | 6 | def pretty_format(data: Dict[str, Any]) -> str: 7 | return pformat(data, width=1) 8 | 9 | def main(raw_data: str) -> None: 10 | data = pretty_format(json.loads(raw_data)) 11 | data.append('mypy') # error: "str" has no attribute "append" 12 | 13 | if __name__ == '__main__': 14 | raw_data: str = sys.argv[1] 15 | main(raw_data) 16 | -------------------------------------------------------------------------------- /MiniOsaka/forward_reference.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | class C: 4 | field = 'c_field' 5 | def method(self) -> C.field:... # this is OK 6 | 7 | def method(self) -> field:... # this is OK 8 | 9 | def method(self) -> C.D:... # this is OK 10 | 11 | def method(self) -> D:... # this is OK 12 | 13 | class D: 14 | field2 = 'd_field' 15 | def method(self) -> C.D.field2:... # this is OK 16 | 17 | def method(self) -> D.field2:... # this is OK 18 | 19 | def method(self) -> field2:... # this is OK 20 | 21 | def method(self) -> field:... # this FAILS, class D doesn't 22 | # see C's attributes, This was 23 | # already true before this PEP. 24 | -------------------------------------------------------------------------------- /MiniOsaka/forward_reference_error.py: -------------------------------------------------------------------------------- 1 | class A: 2 | def f(self): 3 | return B() 4 | 5 | a = A() 6 | a.f() # NameError: name 'B' is not defined 7 | 8 | b = B() # NameError: name 'B' is not defined 9 | 10 | def f(): 11 | b = B() 12 | 13 | f() # NameError: name 'B' is not defined 14 | 15 | class B: 16 | pass 17 | -------------------------------------------------------------------------------- /MiniOsaka/generics.py: -------------------------------------------------------------------------------- 1 | from typing import TypeVar, Generic 2 | 3 | T = TypeVar('T') 4 | 5 | class Stack(Generic[T]): 6 | def __init__(self) -> None: 7 | # Create an empty list with items of type T 8 | self.items = [] # type: List[T] 9 | 10 | def push(self, item: T) -> None: 11 | self.items.append(item) 12 | 13 | stack = Stack[int]() 14 | stack.push(1) 15 | stack.push(2) 16 | print(stack.items) 17 | -------------------------------------------------------------------------------- /MiniOsaka/generics_class_subscript.py: -------------------------------------------------------------------------------- 1 | class MetaSubscript(type): 2 | 3 | def __new__(cls, name, bases, namespace, **kwds): 4 | print('__new__ called') 5 | return super().__new__(cls, name, bases, dict(namespace)) 6 | 7 | def __getitem__(cls, params): 8 | print('__getitem__ called') 9 | cls.params = params 10 | return cls.__class__(cls.__name__, cls.__bases__, cls.__dict__) 11 | 12 | class MyGeneric(metaclass=MetaSubscript): 13 | pass 14 | 15 | g = MyGeneric() 16 | kg = MyGeneric[str]() 17 | kprint(MyGeneric.params) # 18 | -------------------------------------------------------------------------------- /MiniOsaka/metaclass.py: -------------------------------------------------------------------------------- 1 | class Meta(type): 2 | def __add__(self, x: str) -> str: 3 | return 'a' + x 4 | 5 | class C(metaclass=Meta): 6 | ... 7 | 8 | print(C + 'x') # Okay 9 | print(C + 1) # error: Unsupported operand types for + ("Type[C]" and "int") 10 | -------------------------------------------------------------------------------- /MiniOsaka/namedtuple_class.py: -------------------------------------------------------------------------------- 1 | from typing import NamedTuple 2 | 3 | class Point(NamedTuple): 4 | x: int 5 | y: int 6 | 7 | p = Point(x=1, y='x') # Argument has incompatible type "str"; expected "int" 8 | -------------------------------------------------------------------------------- /MiniOsaka/namedtuple_normal.py: -------------------------------------------------------------------------------- 1 | from collections import namedtuple 2 | 3 | Point = namedtuple('Point', ['x', 'y']) 4 | p = Point(x=1, y=2) 5 | print(p.z) # Error: Point has no attribute 'z' 6 | -------------------------------------------------------------------------------- /MiniOsaka/namedtuple_typing.py: -------------------------------------------------------------------------------- 1 | from typing import NamedTuple 2 | 3 | Point = NamedTuple('Point', [('x', int), 4 | ('y', int)]) 5 | p = Point(x=1, y='x') # Argument has incompatible type "str"; expected "int" 6 | print(p) 7 | -------------------------------------------------------------------------------- /MiniOsaka/newtype_class.py: -------------------------------------------------------------------------------- 1 | class UserId(int): 2 | pass 3 | 4 | def name_by_id(user_id: UserId) -> str: 5 | ... 6 | -------------------------------------------------------------------------------- /MiniOsaka/newtype_typing.py: -------------------------------------------------------------------------------- 1 | from typing import NewType 2 | 3 | UserId = NewType('UserId', int) 4 | 5 | def name_by_id(user_id: UserId) -> str: 6 | ... 7 | 8 | UserId('user') # error: Argument 1 to "UserId" has incompatible type "str"; expected "int" 9 | 10 | name_by_id(42) # error: Argument 1 to "name_by_id" has incompatible type "int"; expected "UserId" 11 | name_by_id(UserId(42)) # OK 12 | 13 | num = UserId(5) + 1 # type: int 14 | -------------------------------------------------------------------------------- /MiniOsaka/nominal_subtyping.py: -------------------------------------------------------------------------------- 1 | from typing import Sized, Iterable, Iterator 2 | 3 | class Bucket(Sized, Iterable[int]): 4 | 5 | def __len__(self) -> int: ... 6 | 7 | def __iter__(self) -> Iterator[int]: ... 8 | 9 | def collect(items: Iterable[int]) -> int: ... 10 | 11 | result: int = collect(Bucket()) # Passes type check 12 | -------------------------------------------------------------------------------- /MiniOsaka/normaldict.py: -------------------------------------------------------------------------------- 1 | from typing import Dict, Any, Union 2 | #movie_any = {'name': 'Blade Runner', 'year': 1982} # type: Dict[str, Any] 3 | #movie_union = {'name': 'Blade Runner', 'year': 1982} # type: Dict[str, Union[str, int]] 4 | 5 | blade_runner: Dict[str, Any] = {'name': 'Blade Runner', 'year': 1982} 6 | toy_story: Dict[str, Union[str, int]] = {'name': 'Toy Story', 'year': 1995} 7 | -------------------------------------------------------------------------------- /MiniOsaka/return_generic_func.py: -------------------------------------------------------------------------------- 1 | from typing import TypeVar, Callable, Any, cast 2 | 3 | T = TypeVar('T', bound=Callable[..., Any]) 4 | 5 | def logged(description: str) -> Callable[[T], T]: 6 | def decorator(f: T) -> T: 7 | def wrapper(*args, **kwargs): 8 | print('entering:', description) 9 | value = f(*args, **kwargs) 10 | print('leaving:', description) 11 | return cast(T, wrapper) 12 | return decorator 13 | 14 | @logged('system initialization') 15 | def init() -> None: 16 | print('do something') 17 | 18 | init(1) # Too many arguments (signature is correctly preserved) 19 | -------------------------------------------------------------------------------- /MiniOsaka/structural_subtyping.py: -------------------------------------------------------------------------------- 1 | from typing import Iterator, Iterable 2 | 3 | class Bucket: 4 | 5 | def __len__(self) -> int: ... 6 | 7 | def __iter__(self) -> Iterator[int]: ... 8 | 9 | def collect(items: Iterable[int]) -> int: ... 10 | 11 | result: int = collect(Bucket()) # Passes type check 12 | -------------------------------------------------------------------------------- /MiniOsaka/typeddict.py: -------------------------------------------------------------------------------- 1 | from mypy_extensions import TypedDict 2 | 3 | Movie = TypedDict('Movie', {'name': str, 'year': int}) 4 | blade_runner: Movie = {'name': 'Blade Runner', 'year': 1982} 5 | toy_story = Movie(name='Toy Story', year=1995) 6 | -------------------------------------------------------------------------------- /MiniOsaka/typeddict_class.py: -------------------------------------------------------------------------------- 1 | from mypy_extensions import TypedDict 2 | 3 | class Movie(TypedDict): 4 | name: str 5 | year: int 6 | 7 | blade_runner: Movie = {'name': 'Blade Runner', 'year': 1982} 8 | 9 | class ExtraMovie(Movie): 10 | director: str 11 | 12 | the_rock = ExtraMovie(name='The Rock', year=1996, director='Michael Bay') 13 | 14 | print(the_rock) 15 | -------------------------------------------------------------------------------- /MiniOsaka/typeddict_total.py: -------------------------------------------------------------------------------- 1 | from mypy_extensions import TypedDict 2 | 3 | Movie = TypedDict('Movie', {'name': str, 'year': int}, total=False) 4 | the_rock = Movie(name='The Rock') 5 | 6 | the_rock 7 | the_rock['year'] 8 | -------------------------------------------------------------------------------- /NFC_NFD_problem/NFC_sample1.txt: -------------------------------------------------------------------------------- 1 | プログラミング 2 | -------------------------------------------------------------------------------- /NFC_NFD_problem/NFC_sample2.txt: -------------------------------------------------------------------------------- 1 | プログラミング 2 | パナマ 3 | オランダ 4 | グングニル 5 | プリンタ 6 | -------------------------------------------------------------------------------- /NFC_NFD_problem/NFD_sample1.txt: -------------------------------------------------------------------------------- 1 | プログラミング 2 | -------------------------------------------------------------------------------- /NFC_NFD_problem/NFD_sample2.txt: -------------------------------------------------------------------------------- 1 | プログラミング 2 | パナマ 3 | オランダ 4 | グングニル 5 | プリンタ 6 | -------------------------------------------------------------------------------- /NFC_NFD_problem/read_file_and_normalize.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import unicodedata 3 | 4 | def is_nfd(line): 5 | for char in line.strip(): 6 | if unicodedata.combining(char) != 0: 7 | return True 8 | return False 9 | 10 | def show_unicode_name(line): 11 | for char in line.strip(): 12 | name = unicodedata.name(char) 13 | space = ' ' 14 | if unicodedata.combining(char) != 0: 15 | space += ' ' 16 | print(f'{char}{space}: {name}') 17 | 18 | filename = sys.argv[1] 19 | with open(filename, encoding='utf-8') as f: 20 | for line in f: 21 | text = [char for char in line.strip()] 22 | print(f'文字単位: {text}, 長さ: {len(text)}') 23 | show_unicode_name(line) 24 | 25 | if is_nfd(line): 26 | print('NFD から NFC への変換') 27 | converted = unicodedata.normalize('NFC', line) 28 | show_unicode_name(converted) 29 | -------------------------------------------------------------------------------- /NFC_NFD_problem/read_file_and_print.py: -------------------------------------------------------------------------------- 1 | import sys 2 | 3 | filename = sys.argv[1] 4 | with open(filename, encoding='utf-8') as f: 5 | for line in f: 6 | print(line.strip()) 7 | -------------------------------------------------------------------------------- /NFC_NFD_problem/read_file_and_show_unicode_name.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import unicodedata 3 | 4 | def show_unicode_name(line): 5 | for char in line.strip(): 6 | name = unicodedata.name(char) 7 | space = ' ' 8 | if unicodedata.combining(char) != 0: 9 | space += ' ' 10 | print(f'{char}{space}: {name}') 11 | 12 | filename = sys.argv[1] 13 | with open(filename, encoding='utf-8') as f: 14 | for line in f: 15 | text = [char for char in line.strip()] 16 | print(f'文字単位: {text}, 長さ: {len(text)}') 17 | show_unicode_name(line) 18 | -------------------------------------------------------------------------------- /Python36ReleaseParty/async/async_comprehension_example.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | pep 530 -- asynchronous comprehensions 4 | 5 | https://www.python.org/dev/peps/pep-0530 6 | """ 7 | import asyncio 8 | 9 | 10 | async def aiter(num): 11 | for i in range(num): 12 | yield i 13 | 14 | 15 | async def apow(num): 16 | return pow(num, 2) 17 | 18 | 19 | async def run(num): 20 | data = [i async for i in aiter(num)] 21 | for i in data: 22 | print(i) 23 | print('-' * 12) 24 | power = [await apow(i) for i in data] 25 | for i in power: 26 | print(i) 27 | 28 | 29 | def main(): 30 | event_loop = asyncio.get_event_loop() 31 | try: 32 | event_loop.run_until_complete(run(10)) 33 | finally: 34 | event_loop.close() 35 | 36 | 37 | if __name__ == '__main__': 38 | main() 39 | -------------------------------------------------------------------------------- /Python36ReleaseParty/async/async_comprehension_example_kai1.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | pep 530 -- asynchronous comprehensions 4 | 5 | https://www.python.org/dev/peps/pep-0530 6 | """ 7 | import asyncio 8 | from random import choice 9 | 10 | 11 | async def do_something(): 12 | while True: 13 | await asyncio.sleep(0.2) 14 | print('do something ...') 15 | 16 | 17 | async def aiter(num): 18 | for i in range(num): 19 | await asyncio.sleep(0.1) 20 | print('%d yielding aiter ...' % i) 21 | yield i 22 | 23 | 24 | async def apow(num): 25 | sleep_second = choice(range(3)) 26 | await asyncio.sleep(sleep_second) 27 | print('%d applying apow ...' % num) 28 | return pow(num, 2) 29 | 30 | 31 | async def run(num): 32 | data = [await apow(i) async for i in aiter(num)] 33 | for i in data: 34 | print(i) 35 | 36 | 37 | def main(): 38 | event_loop = asyncio.get_event_loop() 39 | try: 40 | event_loop.create_task(do_something()) 41 | event_loop.run_until_complete(run(10)) 42 | finally: 43 | event_loop.close() 44 | 45 | 46 | if __name__ == '__main__': 47 | main() 48 | -------------------------------------------------------------------------------- /Python36ReleaseParty/async/async_for_example_pep492.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | pep492 aiter with coroutine before pep 525 4 | 5 | https://www.python.org/dev/peps/pep-0492 6 | https://www.python.org/dev/peps/pep-0525 7 | """ 8 | import asyncio 9 | 10 | 11 | async def yield_num(i): 12 | await asyncio.sleep(0.5) 13 | return i 14 | 15 | 16 | class AsyncIterable: 17 | def __init__(self, coroutines): 18 | self.done = [] 19 | self.not_done = coroutines 20 | 21 | def __aiter__(self): 22 | return self 23 | 24 | async def __anext__(self): 25 | data = await self.fetch_data() 26 | if data is not None: 27 | return data 28 | else: 29 | raise StopAsyncIteration 30 | 31 | async def fetch_data(self): 32 | if self.not_done: 33 | self.done, self.not_done = await asyncio.wait( 34 | self.not_done, return_when=asyncio.FIRST_COMPLETED) 35 | if not self.done: 36 | return None 37 | return self.done.pop().result() 38 | 39 | 40 | async def run(num): 41 | coroutines = [yield_num(i) for i in range(num)] 42 | async for i in AsyncIterable(coroutines): 43 | print(i) 44 | 45 | 46 | def main(): 47 | event_loop = asyncio.get_event_loop() 48 | try: 49 | event_loop.run_until_complete(run(10)) 50 | finally: 51 | event_loop.close() 52 | 53 | 54 | if __name__ == '__main__': 55 | main() 56 | -------------------------------------------------------------------------------- /Python36ReleaseParty/async/async_for_example_pep492_kai1.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | pep492 aiter using queue before pep 525 4 | 5 | https://www.python.org/dev/peps/pep-0492 6 | https://www.python.org/dev/peps/pep-0525 7 | """ 8 | import asyncio 9 | 10 | 11 | async def yield_num(num, queue): 12 | for i in range(num): 13 | queue.put_nowait(i) 14 | await asyncio.sleep(0.5) 15 | 16 | 17 | class AsyncIterable: 18 | def __init__(self, queue): 19 | self.queue = queue 20 | self.done = [] 21 | 22 | def __aiter__(self): 23 | return self 24 | 25 | async def __anext__(self): 26 | data = await self.fetch_data() 27 | if data is not None: 28 | return data 29 | else: 30 | raise StopAsyncIteration 31 | 32 | async def fetch_data(self): 33 | while not self.queue.empty(): 34 | self.done.append(self.queue.get_nowait()) 35 | if not self.done: 36 | return None 37 | return self.done.pop(0) 38 | 39 | 40 | async def consume_num(queue): 41 | async for i in AsyncIterable(queue): 42 | await asyncio.sleep(0.5) 43 | print(i) 44 | 45 | 46 | def main(): 47 | event_loop = asyncio.get_event_loop() 48 | queue = asyncio.Queue(loop=event_loop) 49 | try: 50 | event_loop.create_task(yield_num(10, queue)) 51 | event_loop.run_until_complete(consume_num(queue)) 52 | finally: 53 | event_loop.close() 54 | 55 | 56 | if __name__ == '__main__': 57 | main() 58 | -------------------------------------------------------------------------------- /Python36ReleaseParty/async/async_for_example_pep525.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | pep 525 -- asynchronous generators 4 | 5 | https://www.python.org/dev/peps/pep-0525 6 | """ 7 | import asyncio 8 | 9 | 10 | async def aiter(num): 11 | for i in range(num): 12 | await asyncio.sleep(0.5) 13 | yield i 14 | 15 | 16 | async def run(num): 17 | async for i in aiter(num): 18 | print(i) 19 | 20 | 21 | def main(): 22 | event_loop = asyncio.get_event_loop() 23 | try: 24 | event_loop.run_until_complete(run(10)) 25 | finally: 26 | event_loop.close() 27 | 28 | 29 | if __name__ == '__main__': 30 | main() 31 | -------------------------------------------------------------------------------- /Python36ReleaseParty/async/async_iter_ticker.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | PEP 525 -- Asynchronous Generators 4 | 5 | the difference of implementation for asynchronous generators 6 | 7 | https://www.python.org/dev/peps/pep-0525/#rationale-and-goals 8 | """ 9 | import asyncio 10 | 11 | 12 | class Ticker: 13 | """Yield numbers from 0 to `to` every `delay` seconds.""" 14 | 15 | def __init__(self, delay, to): 16 | self.delay = delay 17 | self.i = 0 18 | self.to = to 19 | 20 | def __aiter__(self): 21 | return self 22 | 23 | async def __anext__(self): 24 | i = self.i 25 | if i >= self.to: 26 | raise StopAsyncIteration 27 | self.i += 1 28 | if i: 29 | await asyncio.sleep(self.delay) 30 | return i 31 | 32 | 33 | async def ticker(delay, to): 34 | """Yield numbers from 0 to `to` every `delay` seconds.""" 35 | for i in range(to): 36 | yield i 37 | await asyncio.sleep(delay) 38 | 39 | 40 | async def run(delay, to): 41 | async for i in Ticker(delay, to): 42 | print(i) 43 | 44 | print('-' * 36) 45 | 46 | async for i in ticker(delay, to): 47 | print(i) 48 | 49 | 50 | def main(): 51 | event_loop = asyncio.get_event_loop() 52 | try: 53 | event_loop.run_until_complete(run(0.1, 3)) 54 | finally: 55 | event_loop.close() 56 | 57 | 58 | if __name__ == '__main__': 59 | main() 60 | -------------------------------------------------------------------------------- /Python36ReleaseParty/async/producer_consumer_pattern_generator_based_coroutine.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | implement Producer-Consumer pattern using asyncio 4 | 5 | * generator based coroutine version 6 | 7 | http://www.hyuki.com/dp/dpinfo.html#ProducerConsumer 8 | """ 9 | import asyncio 10 | from random import choice 11 | 12 | 13 | @asyncio.coroutine 14 | def heavy_job(): 15 | sleep_second = choice(range(3)) 16 | yield from asyncio.sleep(sleep_second) 17 | 18 | 19 | @asyncio.coroutine 20 | def put(fruit, table): 21 | counter = 0 22 | while True: 23 | if table.empty(): 24 | table.put_nowait(fruit) # might occur QueueFull if full 25 | print('%d: put %s' % (counter, fruit)) 26 | counter += 1 27 | yield from heavy_job() 28 | 29 | 30 | @asyncio.coroutine 31 | def get(consumer, table): 32 | counter = 0 33 | while True: 34 | if table.full(): 35 | fruit = table.get_nowait() # might occur QueueEmpty if empty 36 | print('%d: %s eats %s' % (counter, consumer, fruit)) 37 | counter += 1 38 | yield from heavy_job() 39 | 40 | 41 | def main(): 42 | event_loop = asyncio.get_event_loop() 43 | table = asyncio.Queue(maxsize=1, loop=event_loop) 44 | try: 45 | # producer 46 | event_loop.create_task(put('apple', table)) 47 | event_loop.create_task(put('banana', table)) 48 | event_loop.create_task(put('candy', table)) 49 | # consumer 50 | event_loop.create_task(get('bob', table)) 51 | event_loop.create_task(get('john', table)) 52 | event_loop.create_task(get('mary', table)) 53 | # start 54 | event_loop.run_forever() 55 | finally: 56 | event_loop.close() 57 | 58 | 59 | if __name__ == '__main__': 60 | main() 61 | -------------------------------------------------------------------------------- /Python36ReleaseParty/async/producer_consumer_pattern_native_coroutine.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | implement Producer-Consumer pattern using asyncio 4 | 5 | * native coroutine version 6 | 7 | http://www.hyuki.com/dp/dpinfo.html#ProducerConsumer 8 | """ 9 | import asyncio 10 | from random import choice 11 | 12 | 13 | async def heavy_job(): 14 | sleep_second = choice(range(3)) 15 | await asyncio.sleep(sleep_second) 16 | 17 | 18 | async def put(fruit, table): 19 | counter = 0 20 | while True: 21 | if table.empty(): 22 | table.put_nowait(fruit) # might occur QueueFull if full 23 | print('%d: put %s' % (counter, fruit)) 24 | counter += 1 25 | await heavy_job() 26 | 27 | 28 | async def get(consumer, table): 29 | counter = 0 30 | while True: 31 | if table.full(): 32 | fruit = table.get_nowait() # might occur QueueEmpty if empty 33 | print('%d: %s eats %s' % (counter, consumer, fruit)) 34 | counter += 1 35 | await heavy_job() 36 | 37 | 38 | def main(): 39 | event_loop = asyncio.get_event_loop() 40 | table = asyncio.Queue(maxsize=1, loop=event_loop) 41 | try: 42 | # producer 43 | event_loop.create_task(put('apple', table)) 44 | event_loop.create_task(put('banana', table)) 45 | event_loop.create_task(put('candy', table)) 46 | # consumer 47 | event_loop.create_task(get('bob', table)) 48 | event_loop.create_task(get('john', table)) 49 | event_loop.create_task(get('mary', table)) 50 | # start 51 | event_loop.run_forever() 52 | finally: 53 | event_loop.close() 54 | 55 | 56 | if __name__ == '__main__': 57 | main() 58 | -------------------------------------------------------------------------------- /Python36ReleaseParty/async/surprising_generator.py: -------------------------------------------------------------------------------- 1 | """ 2 | design mistake 3 | 4 | http://lucumr.pocoo.org/2016/10/30/i-dont-understand-asyncio/ 5 | """ 6 | 7 | def surprising_generator(n): 8 | if n in (0, 1): 9 | return [1] 10 | for i in range(n): 11 | yield i * 2 12 | 13 | 14 | print('list(surprising_generator(0): ', list(surprising_generator(0))) 15 | print('list(surprising_generator(1): ', list(surprising_generator(1))) 16 | print('list(surprising_generator(2): ', list(surprising_generator(2))) 17 | -------------------------------------------------------------------------------- /Python36ReleaseParty/async/surprising_generator_async.py: -------------------------------------------------------------------------------- 1 | """ 2 | design mistake 3 | 4 | http://lucumr.pocoo.org/2016/10/30/i-dont-understand-asyncio/ 5 | """ 6 | 7 | async def surprising_generator(n): 8 | if n in (0, 1): 9 | return [1] 10 | for i in range(n): 11 | yield i * 2 12 | 13 | 14 | print('list(surprising_generator(0): ', list(surprising_generator(0))) 15 | print('list(surprising_generator(1): ', list(surprising_generator(1))) 16 | print('list(surprising_generator(2): ', list(surprising_generator(2))) 17 | -------------------------------------------------------------------------------- /Python36ReleaseParty/async/yield_from1.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | """ 4 | yield from 構文 5 | 6 | * https://www.python.org/dev/peps/pep-0380/ 7 | """ 8 | 9 | def g1(n): 10 | for i in range(n): 11 | yield i 12 | 13 | 14 | def g2_wrong(n): 15 | yield g1(n) 16 | 17 | 18 | def g2_delegate(n): 19 | """ 20 | 値を返すだけならこのコーディングも yield from も同じ 21 | """ 22 | for i in g1(n): 23 | yield i 24 | 25 | 26 | def g2_new_syntax(n): 27 | yield from g1(n) 28 | 29 | 30 | def main(): 31 | for i in g2_wrong(3): 32 | print(i) 33 | 34 | for i in g2_delegate(3): 35 | print(i) 36 | 37 | for i in g2_new_syntax(3): 38 | print(i) 39 | 40 | 41 | if __name__ == '__main__': 42 | main() 43 | -------------------------------------------------------------------------------- /Python36ReleaseParty/async/yield_from2.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | """ 4 | yield from 構文 5 | 6 | * https://www.python.org/dev/peps/pep-0380/ 7 | """ 8 | 9 | def g1(n): 10 | v = None 11 | for i in range(n): 12 | v = 0 if v is None else v 13 | v = yield i + v 14 | 15 | 16 | def g2_delegate(n): 17 | """ 18 | send() メソッドはこのジェネレーターに値を送るため、 19 | ここで値を受け渡すような実装にしないといけない => g2_delegate_kai を参照 20 | """ 21 | for i in g1(n): 22 | yield i 23 | 24 | 25 | def g2_delegate_kai(n): 26 | """ 27 | 途端にジェネレーターの委譲が難しくなった! 28 | """ 29 | g = g1(n) 30 | v = yield next(g) 31 | while True: 32 | v = yield g.send(v) 33 | 34 | 35 | def g2_new_syntax(n): 36 | yield from g1(n) 37 | 38 | 39 | def main(): 40 | gd = g2_delegate(3) 41 | print(next(gd)) 42 | print(gd.send(2)) 43 | print(gd.send(4)) 44 | 45 | print('-' * 72) 46 | 47 | gn = g2_new_syntax(3) 48 | print(next(gn)) 49 | print(gn.send(2)) 50 | print(gn.send(4)) 51 | 52 | print('-' * 72) 53 | 54 | gdk = g2_delegate_kai(3) 55 | print(next(gdk)) 56 | print(gdk.send(2)) 57 | print(gdk.send(4)) 58 | 59 | 60 | if __name__ == '__main__': 61 | main() 62 | -------------------------------------------------------------------------------- /PythonHackerGuideBook/13/oop1.py: -------------------------------------------------------------------------------- 1 | 2 | class Instrument: 3 | def play(self, accessory): 4 | raise NotImplementedError('Cannot play these') 5 | 6 | 7 | class SnareDrum(Instrument): 8 | def play(self, accessory): 9 | if isinstance(accessory, Stick): 10 | return 'POC!' 11 | if isinstance(accessory, Brushes): 12 | return 'SHHHH!' 13 | raise NotImplementedError('Cannot play these') 14 | 15 | 16 | class Cymbal(Instrument): 17 | def play(self, accessory): 18 | if isinstance(accessory, Brushes): 19 | return 'FRCCCHHT!' 20 | raise NotImplementedError('Cannot play these') 21 | 22 | 23 | class Stick(Instrument): pass 24 | class Brushes(Instrument): pass 25 | 26 | 27 | def play(instrument, accessory): 28 | return instrument.play(accessory) 29 | 30 | 31 | print(play(SnareDrum(), Stick())) 32 | print(play(SnareDrum(), Brushes())) 33 | print(play(Cymbal(), Stick())) 34 | #print(play(Stick(), Stick())) 35 | -------------------------------------------------------------------------------- /PythonHackerGuideBook/13/oop2.py: -------------------------------------------------------------------------------- 1 | 2 | class Instrument: 3 | def play(self, accessory, other=None): 4 | raise NotImplementedError('Cannot play these') 5 | 6 | 7 | class SnareDrum(Instrument): 8 | def play(self, accessory, other=None): 9 | if isinstance(accessory, Stick): 10 | return 'POC!' 11 | if isinstance(accessory, Brushes): 12 | return 'SHHHH!' 13 | raise NotImplementedError('Cannot play these') 14 | 15 | 16 | class Cymbal(Instrument): 17 | def play(self, accessory, other): 18 | if isinstance(accessory, Brushes) and isinstance(other, Stick): 19 | return 'FRCCCHHT!' 20 | raise NotImplementedError('Cannot play these') 21 | 22 | 23 | class Stick(Instrument): pass 24 | class Brushes(Instrument): pass 25 | 26 | 27 | def play(instrument, accessory, other=None): 28 | return instrument.play(accessory, other) 29 | 30 | 31 | print(play(SnareDrum(), Stick())) 32 | print(play(SnareDrum(), Brushes())) 33 | print(play(Cymbal(), Brushes(), Stick())) 34 | -------------------------------------------------------------------------------- /PythonHackerGuideBook/13/overload1.py: -------------------------------------------------------------------------------- 1 | 2 | def add(x, y): 3 | print(f'{x=} + {y=} = {x + y}') 4 | 5 | 6 | def add(x, y, z): 7 | print(f'{x=} + {y=} + {z=} = {x + y + z}') 8 | 9 | 10 | add(1, 2, 3) 11 | add(1, 2) 12 | -------------------------------------------------------------------------------- /PythonHackerGuideBook/13/overload2.py: -------------------------------------------------------------------------------- 1 | 2 | def add(x, y, z=0): 3 | print(f'{x=} + {y=} + {z=} = {x + y + z}') 4 | 5 | 6 | add(1, 2, 3) 7 | add(1, 2) 8 | -------------------------------------------------------------------------------- /PythonHackerGuideBook/13/overload3.py: -------------------------------------------------------------------------------- 1 | 2 | def add(x, *args): 3 | total = x 4 | for i in args: 5 | total += i 6 | print(f'{x=} + {args=} = {total}') 7 | 8 | add(1, 2, 3) 9 | add(1, 2) 10 | add(1, 2, 3, 4, 5, 6, 7) 11 | add(1) 12 | -------------------------------------------------------------------------------- /PythonHackerGuideBook/13/overload4.py: -------------------------------------------------------------------------------- 1 | import inspect 2 | 3 | class Overload: 4 | 5 | def __init__(self): 6 | self.namespace = {} 7 | 8 | def __call__(self, func): 9 | spec = inspect.getfullargspec(func) 10 | self.namespace[len(spec.args)] = func 11 | def wrapped(*args): 12 | f = self.namespace.get(len(args)) 13 | if f is None: 14 | raise NotImplementedError(f'Not defined for {len(args)}') 15 | return f(*args) 16 | return wrapped 17 | 18 | 19 | overload = Overload() 20 | 21 | @overload 22 | def add(x, y): 23 | print(f'{x=} + {y=} = {x + y}') 24 | 25 | 26 | @overload 27 | def add(x, y, z): 28 | print(f'{x=} + {y=} + {z=} = {x + y + z}') 29 | 30 | 31 | @overload 32 | def add(a, b, c, d, e, f, g): 33 | print(f'{a + b + c + d + e + f + g}') 34 | 35 | 36 | add(1, 2, 3) 37 | add(1, 2) 38 | add(1, 2, 3, 4, 5, 6, 7) 39 | add(1, 2, 3, 4) 40 | -------------------------------------------------------------------------------- /PythonHackerGuideBook/13/singledispatch_sample1.py: -------------------------------------------------------------------------------- 1 | import functools 2 | 3 | class SnareDrum: pass 4 | class Cymbal: pass 5 | class Stick: pass 6 | class Brushes: pass 7 | 8 | 9 | @functools.singledispatch 10 | def play(instrument, accessory): 11 | raise NotImplementedError('Cannot play these') 12 | 13 | 14 | @play.register(SnareDrum) 15 | def _(instrument, accessory): 16 | if isinstance(accessory, Stick): 17 | return 'POC!' 18 | if isinstance(accessory, Brushes): 19 | return 'SHHHH!' 20 | raise NotImplementedError('Cannot play these') 21 | 22 | 23 | @play.register(Cymbal) 24 | def _(instrument, accessory): 25 | if isinstance(accessory, Brushes): 26 | return 'FRCCCHHT!' 27 | raise NotImplementedError('Cannot play these') 28 | 29 | 30 | print(play(SnareDrum(), Stick())) 31 | print(play(SnareDrum(), Brushes())) 32 | print(play(Cymbal(), Stick())) 33 | print(play(SnareDrum, Cymbal())) 34 | -------------------------------------------------------------------------------- /PythonHackerGuideBook/13/singledispatch_sample2.py: -------------------------------------------------------------------------------- 1 | import functools 2 | 3 | class SnareDrum: pass 4 | class Cymbal: pass 5 | class Stick: pass 6 | class Brushes: pass 7 | 8 | 9 | @functools.singledispatch) 10 | def play(instrument, accessory): 11 | raise NotImplementedError('Cannot play these') 12 | 13 | 14 | @play.register(SnareDrum) 15 | def _(instrument, accessory): 16 | if isinstance(accessory, Stick): 17 | return 'POC!' 18 | if isinstance(accessory, Brushes): 19 | return 'SHHHH!' 20 | raise NotImplementedError('Cannot play these') 21 | 22 | 23 | @play.register(Cymbal) 24 | def _(instrument, accessory, other): 25 | if isinstance(accessory, Brushes) and isinstance(other, Stick): 26 | return 'FRCCCHHT!' 27 | raise NotImplementedError('Cannot play these') 28 | 29 | 30 | print(play(SnareDrum(), Stick())) 31 | print(play(SnareDrum(), Brushes())) 32 | print(play(Cymbal(), Brushes(), Stick())) 33 | -------------------------------------------------------------------------------- /PythonHackerGuideBook/9/README.md: -------------------------------------------------------------------------------- 1 | 2 | ## Simple sample code 3 | 4 | ```python 5 | def add(x, y): 6 | z = x + y 7 | return z 8 | 9 | 10 | print(add(3, 5)) 11 | ``` 12 | 13 | ```bash 14 | (py39) $ python adder.py 15 | 8 16 | ``` 17 | 18 | ## How to use ast helpers 19 | 20 | ```bash 21 | (py39) $ python use_ast_from_source.py adder.py 22 | ``` 23 | 24 | ## Use hy 25 | 26 | > Hy is a Lisp dialect that's embedded in Python. Since Hy transforms its Lisp code into Python abstract syntax tree (AST) objects, you have the whole beautiful world of Python at your fingertips, in Lisp form. 27 | > 28 | > https://github.com/hylang/hy 29 | 30 | 31 | ``` 32 | (py39) $ pip install hy 33 | ``` 34 | 35 | ```lisp 36 | (defn add [x, y] 37 | (+ x, y)) 38 | 39 | (print (add 3, 5)) 40 | ``` 41 | 42 | ```bash 43 | (py39) $ hy hyadder.hy 44 | 8 45 | ``` 46 | 47 | ## Unparse ast from hy source 48 | 49 | ```bash 50 | (py39) $ python unparse_hy_ast.py 51 | 8 52 | ================================ 53 | def add(hyx_xXcommaX, y): 54 | return hyx_xXcommaX + y 55 | print(add(3, 5)) 56 | ``` 57 | -------------------------------------------------------------------------------- /PythonHackerGuideBook/9/adder.py: -------------------------------------------------------------------------------- 1 | 2 | def add(x, y): 3 | z = x + y 4 | return z 5 | 6 | print(add(3, 5)) 7 | -------------------------------------------------------------------------------- /PythonHackerGuideBook/9/hyadder.hy: -------------------------------------------------------------------------------- 1 | 2 | (defn add [x, y] 3 | (+ x, y)) 4 | 5 | (print (add 3, 5)) 6 | -------------------------------------------------------------------------------- /PythonHackerGuideBook/9/unparse_hy_ast.py: -------------------------------------------------------------------------------- 1 | import ast 2 | 3 | from hy.lex import hy_parse 4 | from hy.compiler import hy_compile 5 | 6 | 7 | hy_source = """ 8 | (defn add [x, y] 9 | (+ x, y)) 10 | 11 | (print (add 3, 5)) 12 | """ 13 | 14 | 15 | def main(): 16 | hy_tree = hy_parse(hy_source) 17 | py_ast = hy_compile(hy_tree, '__main__') 18 | code = compile(py_ast, '', 'exec') 19 | exec(code) 20 | print('=' * 32) 21 | print(ast.unparse(py_ast)) 22 | 23 | 24 | if __name__ == '__main__': 25 | main() 26 | -------------------------------------------------------------------------------- /PythonHackerGuideBook/9/use_ast_from_source.py: -------------------------------------------------------------------------------- 1 | import ast 2 | import sys 3 | 4 | 5 | def parse_source(filename): 6 | with open(filename) as py: 7 | source = py.read() 8 | return ast.parse(source) 9 | 10 | 11 | def run_code(filename): 12 | tree = parse_source(filename) 13 | print(tree) 14 | code = compile(tree, '', 'exec') 15 | print(code) 16 | exec(code) 17 | 18 | 19 | def walk_ast(filename): 20 | tree = parse_source(filename) 21 | for node in ast.walk(tree): 22 | print(node) 23 | 24 | 25 | def dump_ast(filename): 26 | tree = parse_source(filename) 27 | print(ast.dump(tree, indent=2)) 28 | 29 | 30 | class MultiplicationNodeTransformer(ast.NodeTransformer): 31 | 32 | def visit_BinOp(self, node): 33 | print(f'orig: {vars(node)}') 34 | print(f'left: {vars(node.left)}') 35 | print(f'right: {vars(node.right)}') 36 | bin_op = ast.BinOp( 37 | left=node.left, 38 | op=ast.Mult(), 39 | right=node.right 40 | ) 41 | ast.copy_location(bin_op, node) 42 | print(f'modified: {vars(bin_op)}') 43 | return bin_op 44 | #import ipdb; ipdb.set_trace() 45 | 46 | 47 | def transform_ast(filename): 48 | tree = parse_source(filename) 49 | print('before transform') 50 | code = compile(tree, '', 'exec') 51 | exec(code) 52 | 53 | print('=' * 32) 54 | transformer = MultiplicationNodeTransformer() 55 | tree = transformer.visit(tree) 56 | print('=' * 32) 57 | 58 | code = compile(tree, '', 'exec') 59 | print('after transform') 60 | exec(code) 61 | 62 | 63 | def main(): 64 | filename = sys.argv[1] 65 | run_code(filename) 66 | dump_ast(filename) 67 | walk_ast(filename) 68 | transform_ast(filename) 69 | 70 | 71 | if __name__ == '__main__': 72 | main() 73 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # python-study 2 | 3 | Public repository to study Python 4 | -------------------------------------------------------------------------------- /ScrapingWithBS4/impress_book_reprint_list_all.py: -------------------------------------------------------------------------------- 1 | import requests 2 | from bs4 import BeautifulSoup 3 | 4 | page_data = requests.get('https://book.impress.co.jp/staff_blog/').text 5 | page = BeautifulSoup(page_data, 'lxml') 6 | details = page.select(""" 7 | body > div.block-wrap > div.block-content > main > div > div:nth-child(3) > div.block-book-detail-box-body.module-usr-text > div.block-book-detail 8 | """) 9 | 10 | for detail in details: 11 | hrefs = detail.select('h2 > a') 12 | reprint_messages = detail.select('div.module-book-copy > b') 13 | hrefs_and_reprints = zip(hrefs, reprint_messages) 14 | for href, reprint in hrefs_and_reprints: 15 | print(f'{href["title"]}: {reprint.text}') 16 | -------------------------------------------------------------------------------- /ScrapingWithBS4/impress_book_reprint_list_dekiru.py: -------------------------------------------------------------------------------- 1 | import requests 2 | from bs4 import BeautifulSoup 3 | 4 | page_data = requests.get('https://book.impress.co.jp/staff_blog/').text 5 | page = BeautifulSoup(page_data, 'lxml') 6 | details = page.select(""" 7 | body > div.block-wrap > div.block-content > main > div > div:nth-child(3) > div.block-book-detail-box-body.module-usr-text > div.block-book-detail 8 | """) 9 | 10 | for detail in details: 11 | hrefs = detail.select('h2 > a') 12 | reprint_messages = detail.select('div.module-book-copy > b') 13 | hrefs_and_reprints = zip(hrefs, reprint_messages) 14 | for href, reprint in hrefs_and_reprints: 15 | if href["title"] == 'できる 仕事がはかどるPython自動処理 全部入り。': 16 | print(f'{href["title"]}: {reprint.text}') 17 | -------------------------------------------------------------------------------- /SearchString/boyer_moore_horspool.py: -------------------------------------------------------------------------------- 1 | """ 2 | Implement Boyer-Moore Horspool searching 3 | 4 | Reference: 5 | http://www.geocities.jp/m_hiroi/light/pyalgo11.html 6 | """ 7 | import logging 8 | import sys 9 | import statistics 10 | import timeit 11 | 12 | from utils import log 13 | from utils import parse_argument 14 | from utils import find_current_line_end, find_previous_line_end 15 | from utils import make_table 16 | 17 | 18 | def match_word(blob, word, offset, m): 19 | j = m - 1 20 | while j >= 0: 21 | if blob[offset + j] != word[j]: 22 | break 23 | j -= 1 24 | return j < 0 25 | 26 | 27 | def boyer_moore_horspool_search(blob, word, table): 28 | blob_view = memoryview(blob) 29 | word_view = memoryview(word) 30 | n, m = len(blob), len(word) 31 | end = n - m 32 | results = [] 33 | i = 0 34 | cnt = 0 35 | while i < end: 36 | if match_word(blob_view, word_view, i, m): 37 | prev_line_end = find_previous_line_end(blob_view, i) 38 | cur_line_end = find_current_line_end(blob_view, i + m, n) 39 | line = blob_view[prev_line_end:cur_line_end].tobytes() 40 | results.append(line) 41 | i = cur_line_end 42 | else: 43 | i += table[blob_view[i + m]] 44 | cnt += 1 45 | log.info('ループ回数: %d' % cnt) 46 | return results 47 | 48 | 49 | def main(): 50 | args = parse_argument() 51 | byte_word = args.word.encode('utf-8') 52 | log.info('検索語: %s' % args.word) 53 | log.info('検索語のバイト長: %d' % len(byte_word)) 54 | 55 | if args.measure: 56 | log.setLevel(logging.ERROR) 57 | 58 | with args.data() as blob: 59 | table = make_table(byte_word) 60 | results = boyer_moore_horspool_search(blob, byte_word, table) 61 | 62 | for result in results: 63 | log.debug(result.decode('utf-8').strip()) 64 | log.info('検索結果: %d 件' % len(results)) 65 | 66 | 67 | if __name__ == '__main__': 68 | args = parse_argument() 69 | if args.measure: 70 | setup = 'from __main__ import main' 71 | sec = timeit.repeat('main()', setup=setup, repeat=3, number=5) 72 | log.setLevel(logging.INFO) 73 | log.info('平均実行時間: %f sec' % statistics.mean(sec)) 74 | else: 75 | main() 76 | -------------------------------------------------------------------------------- /SearchString/boyer_moore_sunday.py: -------------------------------------------------------------------------------- 1 | """ 2 | Implement Boyer-Moore Sunday searching, aka Quick-Search 3 | 4 | Reference: 5 | http://www.geocities.jp/m_hiroi/light/pyalgo11.html 6 | """ 7 | import logging 8 | import sys 9 | import statistics 10 | import timeit 11 | 12 | from utils import log 13 | from utils import parse_argument 14 | from utils import find_current_line_end, find_previous_line_end 15 | 16 | 17 | def make_qs_table(word): 18 | size = len(word) 19 | table = [size + 1] * 256 20 | for i in range(size): 21 | table[word[i]] = size - i 22 | return table 23 | 24 | 25 | def match_word(blob, word, offset, m): 26 | j = 0 27 | while j < m: 28 | if blob[offset + j] != word[j]: 29 | break 30 | j += 1 31 | return j == m 32 | 33 | 34 | def boyer_moore_sunday_search(blob, word, table): 35 | blob_view = memoryview(blob) 36 | word_view = memoryview(word) 37 | n, m = len(blob), len(word) 38 | end = n - m 39 | results = [] 40 | i = 0 41 | cnt = 0 42 | while i < end: 43 | if match_word(blob_view, word_view, i, m): 44 | prev_line_end = find_previous_line_end(blob_view, i) 45 | cur_line_end = find_current_line_end(blob_view, i + m, n) 46 | line = blob_view[prev_line_end:cur_line_end].tobytes() 47 | results.append(line) 48 | i = cur_line_end 49 | else: 50 | i += table[blob_view[i + m]] 51 | cnt += 1 52 | log.info('ループ回数: %d' % cnt) 53 | return results 54 | 55 | 56 | def main(): 57 | args = parse_argument() 58 | byte_word = args.word.encode('utf-8') 59 | log.info('検索語: %s' % args.word) 60 | log.info('検索語のバイト長: %d' % len(byte_word)) 61 | 62 | if args.measure: 63 | log.setLevel(logging.ERROR) 64 | 65 | with args.data() as blob: 66 | table = make_qs_table(byte_word) 67 | results = boyer_moore_sunday_search(blob, byte_word, table) 68 | 69 | for result in results: 70 | log.debug(result.decode('utf-8').strip()) 71 | log.info('検索結果: %d 件' % len(results)) 72 | 73 | 74 | if __name__ == '__main__': 75 | args = parse_argument() 76 | if args.measure: 77 | setup = 'from __main__ import main' 78 | sec = timeit.repeat('main()', setup=setup, repeat=3, number=5) 79 | log.setLevel(logging.INFO) 80 | log.info('平均実行時間: %f sec' % statistics.mean(sec)) 81 | else: 82 | main() 83 | -------------------------------------------------------------------------------- /SearchString/brute_force_search.py: -------------------------------------------------------------------------------- 1 | """ 2 | Implement Brute-Force searching 3 | 4 | Reference: 5 | http://www.geocities.jp/m_hiroi/light/pyalgo11.html 6 | """ 7 | import logging 8 | import statistics 9 | import sys 10 | import timeit 11 | 12 | from utils import log 13 | from utils import parse_argument 14 | from utils import find_current_line_end, find_previous_line_end 15 | 16 | 17 | def match_word(blob, word, offset, m): 18 | i = 0 19 | while i < m: 20 | if blob[offset + i] != word[i]: 21 | break 22 | i += 1 23 | return i == m 24 | 25 | 26 | def brute_force_search(blob, word): 27 | blob_view = memoryview(blob) 28 | word_view = memoryview(word) 29 | n, m = len(blob), len(word) 30 | end = n - m 31 | results = [] 32 | i = 0 33 | cnt = 0 34 | while i <= end: 35 | if match_word(blob_view, word_view, i, m): 36 | prev_line_end = find_previous_line_end(blob_view, i) 37 | cur_line_end = find_current_line_end(blob_view, i, n) 38 | line = blob_view[prev_line_end:cur_line_end].tobytes() 39 | results.append(line) 40 | i = cur_line_end 41 | else: 42 | i += 1 43 | cnt += 1 44 | log.info('ループ回数: %d' % cnt) 45 | return results 46 | 47 | 48 | def main(): 49 | args = parse_argument() 50 | byte_word = args.word.encode('utf-8') 51 | log.info('検索語: %s' % args.word) 52 | log.info('検索語のバイト長: %d' % len(byte_word)) 53 | 54 | if args.measure: 55 | log.setLevel(logging.ERROR) 56 | 57 | with args.data() as blob: 58 | results = brute_force_search(blob, byte_word) 59 | 60 | for result in results: 61 | log.debug(result.decode('utf-8').strip()) 62 | log.info('検索結果: %d 件' % len(results)) 63 | 64 | 65 | if __name__ == '__main__': 66 | args = parse_argument() 67 | if args.measure: 68 | setup = 'from __main__ import main' 69 | sec = timeit.repeat('main()', setup=setup, repeat=3, number=5) 70 | log.setLevel(logging.INFO) 71 | log.info('平均実行時間: %f sec' % statistics.mean(sec)) 72 | else: 73 | main() 74 | -------------------------------------------------------------------------------- /SearchString/simplified_boyer_moore.py: -------------------------------------------------------------------------------- 1 | """ 2 | Implement simplified Boyer-Moore searching 3 | 4 | Reference: 5 | http://www.geocities.jp/m_hiroi/light/pyalgo11.html 6 | """ 7 | import logging 8 | import sys 9 | import statistics 10 | import timeit 11 | 12 | from utils import log 13 | from utils import parse_argument 14 | from utils import find_current_line_end, find_previous_line_end 15 | from utils import make_table 16 | 17 | 18 | def match_word(blob, word, offset, m): 19 | i = 0 20 | j = m - 1 21 | while i < m: 22 | byte = word[j - i] 23 | if blob[offset - i] != byte: 24 | break 25 | i += 1 26 | return i == m, byte 27 | 28 | 29 | def simplified_boyer_moore_search(blob, word, table): 30 | blob_view = memoryview(blob) 31 | word_view = memoryview(word) 32 | n, m = len(blob), len(word) 33 | end = n - m 34 | results = [] 35 | i = 0 36 | cnt = 0 37 | while i <= end: 38 | match, byte = match_word(blob_view, word_view, i, m) 39 | if match: 40 | prev_line_end = find_previous_line_end(blob_view, i) 41 | cur_line_end = find_current_line_end(blob_view, i, n) 42 | line = blob_view[prev_line_end:cur_line_end].tobytes() 43 | results.append(line) 44 | i = cur_line_end 45 | else: 46 | i += table[byte] 47 | cnt += 1 48 | log.info('ループ回数: %d' % cnt) 49 | return results 50 | 51 | 52 | def main(): 53 | args = parse_argument() 54 | byte_word = args.word.encode('utf-8') 55 | log.info('検索語: %s' % args.word) 56 | log.info('検索語のバイト長: %d' % len(byte_word)) 57 | 58 | if args.measure: 59 | log.setLevel(logging.ERROR) 60 | 61 | with args.data() as blob: 62 | table = make_table(byte_word) 63 | results = simplified_boyer_moore_search(blob, byte_word, table) 64 | 65 | for result in results: 66 | log.debug(result.decode('utf-8').strip()) 67 | log.info('検索結果: %d 件' % len(results)) 68 | 69 | 70 | if __name__ == '__main__': 71 | args = parse_argument() 72 | if args.measure: 73 | setup = 'from __main__ import main' 74 | sec = timeit.repeat('main()', setup=setup, repeat=3, number=5) 75 | log.setLevel(logging.INFO) 76 | log.info('平均実行時間: %f sec' % statistics.mean(sec)) 77 | else: 78 | main() 79 | -------------------------------------------------------------------------------- /SearchString/test_search.py: -------------------------------------------------------------------------------- 1 | import sys 2 | 3 | import pytest 4 | 5 | from boyer_moore_horspool import boyer_moore_horspool_search 6 | from brute_force_search import brute_force_search 7 | from simplified_boyer_moore import simplified_boyer_moore_search 8 | from boyer_moore_sunday import boyer_moore_sunday_search, make_qs_table 9 | from utils import read_hyogo, make_table 10 | 11 | expected = list(map(lambda s: s.encode('utf-8'), [ 12 | '28102,"657 ","6570051","ヒョウゴケン","コウベシナダク","ヤハタチョウ","兵庫県","神戸市灘区","八幡町",0,0,1,0,0,0\n', 13 | '28203,"673 ","6730871","ヒョウゴケン","アカシシ","オオクラハチマンチョウ","兵庫県","明石市","大蔵八幡町",0,0,0,0,0,0\n', 14 | '28210,"67512","6751204","ヒョウゴケン","カコガワシ","ヤハタチョウカミサイジョウ","兵庫県","加古川市","八幡町上西条",0,0,0,0,0,0\n', 15 | '28210,"67512","6751203","ヒョウゴケン","カコガワシ","ヤハタチョウシモムラ","兵庫県","加古川市","八幡町下村",0,0,0,0,0,0\n', 16 | '28210,"67512","6751201","ヒョウゴケン","カコガワシ","ヤハタチョウソウサ","兵庫県","加古川市","八幡町宗佐",0,0,0,0,0,0\n', 17 | '28210,"67512","6751205","ヒョウゴケン","カコガワシ","ヤハタチョウナカサイジョウ","兵庫県","加古川市","八幡町中西条",0,0,0,0,0,0\n', 18 | '28210,"67512","6751202","ヒョウゴケン","カコガワシ","ヤハタチョウノムラ","兵庫県","加古川市","八幡町野村",0,0,0,0,0,0\n', 19 | '28210,"67512","6751206","ヒョウゴケン","カコガワシ","ヤハタチョウフナマチ","兵庫県","加古川市","八幡町船町",0,0,0,0,0,0\n', 20 | ])) 21 | 22 | 23 | def test_brute_force_search(): 24 | byte_word = '八幡町'.encode('utf-8') 25 | with read_hyogo() as blob: 26 | actual = brute_force_search(blob, byte_word) 27 | assert expected == actual 28 | 29 | 30 | def _boyer_moore_search(search_func, table_func): 31 | byte_word = '八幡町'.encode('utf-8') 32 | with read_hyogo() as blob: 33 | actual = search_func(blob, byte_word, table_func(byte_word)) 34 | assert expected == actual 35 | 36 | 37 | def test_simplified_boyer_moore_search(): 38 | _boyer_moore_search(simplified_boyer_moore_search, make_table) 39 | 40 | 41 | def test_boyer_moore_horspool_search(): 42 | _boyer_moore_search(boyer_moore_horspool_search, make_table) 43 | 44 | 45 | def test_boyer_moore_sunday_search(): 46 | _boyer_moore_search(boyer_moore_sunday_search, make_qs_table) 47 | 48 | 49 | @pytest.mark.parametrize('word, num', [ 50 | ('八', 104), 51 | ('八幡', 11), 52 | ('チョウ', 3229), 53 | ('65606', 4), 54 | ('28224', 110), 55 | ('0', 5223), 56 | (',', 5223), 57 | ]) 58 | def test_search_results(word, num): 59 | byte_word = word.encode('utf-8') 60 | table = make_table(byte_word) 61 | qs_table = make_qs_table(byte_word) 62 | with read_hyogo() as blob: 63 | bfs = brute_force_search(blob, byte_word) 64 | sbm = simplified_boyer_moore_search(blob, byte_word, table) 65 | bmh = boyer_moore_horspool_search(blob, byte_word, table) 66 | bms = boyer_moore_sunday_search(blob, byte_word, qs_table) 67 | assert num == len(bfs) == len(sbm) == len(bmh) == len(bms) 68 | assert bfs == sbm == bmh == bms 69 | -------------------------------------------------------------------------------- /SearchString/utils.py: -------------------------------------------------------------------------------- 1 | import logging 2 | from contextlib import contextmanager 3 | 4 | LINE_SEPARATOR = ord('\n') 5 | 6 | PATH_HYOGO = 'data/28HYOGO.CSV' 7 | PATH_KEN_ALL = 'data/KEN_ALL.CSV' 8 | 9 | logging.basicConfig( 10 | level=logging.INFO, 11 | format='%(asctime)s %(levelname)s: %(message)s', 12 | ) 13 | log = logging.getLogger(__file__) 14 | 15 | 16 | @contextmanager 17 | def read_hyogo(): 18 | with open(PATH_HYOGO, mode='rb') as f: 19 | yield f.read() 20 | 21 | 22 | @contextmanager 23 | def read_ken_all(): 24 | with open(PATH_KEN_ALL, mode='rb') as f: 25 | yield f.read() 26 | 27 | 28 | def read_until_line_end(byte): 29 | i = 0 30 | while byte[i] != LINE_SEPARATOR: 31 | i += 1 32 | offset = i + 1 33 | return offset, byte[0:offset] 34 | 35 | 36 | def find_previous_line_end(blob, offset): 37 | i = offset 38 | while i >= 0 and blob[i] != LINE_SEPARATOR: 39 | i -= 1 40 | return i + 1 41 | 42 | 43 | def find_current_line_end(blob, offset, n): 44 | i = offset 45 | while i < n and blob[i] != LINE_SEPARATOR: 46 | i += 1 47 | return i + 1 48 | 49 | 50 | def make_table(word): 51 | size = len(word) 52 | table = [size] * 256 53 | for i in range(size): 54 | table[word[i]] = size - i 55 | return table 56 | 57 | 58 | def parse_argument(): 59 | def data_reader_type(typ): 60 | if typ == 'large': 61 | return read_ken_all 62 | return read_hyogo 63 | 64 | import argparse 65 | parser = argparse.ArgumentParser() 66 | parser.set_defaults( 67 | data=read_hyogo, 68 | measure=False, 69 | verbose=False, 70 | word=None, 71 | ) 72 | 73 | parser.add_argument( 74 | '--data', type=data_reader_type, help='set data type', 75 | ) 76 | parser.add_argument( 77 | '--measure', action='store_true', 78 | help='measure running time by timeit', 79 | ) 80 | parser.add_argument( 81 | '-v', '--verbose', action='store_true', 82 | help='set verbose mode', 83 | ) 84 | parser.add_argument( 85 | 'word', help='word to search' 86 | ) 87 | 88 | args = parser.parse_args() 89 | if args.verbose: 90 | log.setLevel(logging.DEBUG) 91 | 92 | return args 93 | -------------------------------------------------------------------------------- /StartPython/75/README.md: -------------------------------------------------------------------------------- 1 | 2 | ## [みんなのPython勉強会#75](https://startpython.connpass.com/event/228136/) 3 | 4 | スライド資料の [本と学びの段階](https://docs.google.com/presentation/d/11QQ7yGf_9lF3Uy5JjrmZIQOrWvInZ7PkPvwt3UEpeyM/) に出てくるサンプルコード 5 | -------------------------------------------------------------------------------- /StartPython/75/enum1.py: -------------------------------------------------------------------------------- 1 | from enum import Enum 2 | 3 | class Color(Enum): 4 | RED = 1 5 | GREEN = 2 6 | BLUE = 3 7 | -------------------------------------------------------------------------------- /StartPython/75/enum2.py: -------------------------------------------------------------------------------- 1 | from enum import Enum 2 | 3 | class Color(Enum): 4 | RED 5 | GREEN 6 | BLUE 7 | -------------------------------------------------------------------------------- /StartPython/75/enum3.py: -------------------------------------------------------------------------------- 1 | from enum import Enum, EnumMeta, _EnumDict 2 | 3 | class EnumDefaultDict(_EnumDict): 4 | def __missing__(self, key): 5 | self[key] = 1 6 | 7 | class ImplicitEnumMeta(EnumMeta): 8 | @classmethod 9 | def __prepare__(metacls, cls, bases): 10 | return EnumDefaultDict() 11 | 12 | class ImplicitEnum(Enum, metaclass=ImplicitEnumMeta): 13 | pass 14 | 15 | class Color(ImplicitEnum): 16 | RED 17 | GREEN 18 | BLUE 19 | -------------------------------------------------------------------------------- /StartPython/75/enum4.py: -------------------------------------------------------------------------------- 1 | from enum import Enum, auto 2 | 3 | class Color(Enum): 4 | RED = auto() 5 | GREEN = auto() 6 | BLUE = auto() 7 | -------------------------------------------------------------------------------- /StartPython/75/enum5.py: -------------------------------------------------------------------------------- 1 | from enum import Enum 2 | 3 | Color = Enum('Color', ['RED', 'GREEN', 'BLUE']) 4 | -------------------------------------------------------------------------------- /StartPython/75/metaclass1.py: -------------------------------------------------------------------------------- 1 | 2 | class MyMeta(type): 3 | 4 | def __new__(mcs, name, bases, namespace, **kwargs): 5 | print(f'{mcs}, called __new__') 6 | return super().__new__(mcs, name, bases, namespace) 7 | 8 | @classmethod 9 | def __prepare__(mcs, name, bases, **kwargs): 10 | print(f'{mcs}, called __prepare__') 11 | return {'🐙🐙🐙': 'たこ'} 12 | 13 | class MyClass(metaclass=MyMeta): 14 | pass 15 | 16 | def test(): 17 | print(vars(MyClass)) 18 | 19 | if __name__ == '__main__': 20 | test() 21 | -------------------------------------------------------------------------------- /StartPython/75/test.py: -------------------------------------------------------------------------------- 1 | from metaclass1 import MyClass 2 | -------------------------------------------------------------------------------- /Testing/00_virtualenv/README.md: -------------------------------------------------------------------------------- 1 | # Python の仮想環境の構築 2 | 3 | Python の仮想環境の構築には [virtualenv](https://pypi.org/project/virtualenv/) を使う。virtualenv は Python 3.3 以降では同等の機能が [venv](https://docs.python.org/ja/3/library/venv.html) として提供されている。新しい Python を使っているなら virtualenv をインストールせずにすぐに使える。 4 | 5 | ## 仮想環境の作成 6 | 7 | `venv` モジュールを使って仮想環境を作成する。 8 | 9 | ```bash 10 | $ python3.6 -m venv myenv 11 | $ tree -L 1 myenv 12 | myenv 13 | ├── bin 14 | ├── include 15 | ├── lib 16 | └── pyvenv.cfg 17 | 18 | 3 directories, 1 file 19 | ``` 20 | 21 | ## 仮想環境の有効化 22 | 23 | 仮想環境を有効にするときは `source` コマンドで作成した仮想環境内にある `activate` ファイルを読み込む。 24 | 25 | ```bash 26 | $ source myenv/bin/activate 27 | (myenv) $ 28 | (myenv) $ pip freeze # 何も出力されない 29 | ``` 30 | 31 | 自分でなにかパッケージをインストールしてみましょう。 32 | 33 | ```bash 34 | (myenv) $ pip install snorse 35 | (myenv) $ pip freeze 36 | click==3.3 37 | snorse==1.0.0 38 | (myenv) $ snorse snowman 39 | ☃ ☃ ☃ ⛄⛄⛄ ☃ ⛄⛄⛄ ⛄⛄⛄ ⛄⛄⛄ ☃ ⛄⛄⛄ ⛄⛄⛄ ⛄⛄⛄ ⛄⛄⛄ ☃ ⛄⛄⛄ ⛄⛄⛄ ☃ 40 | ``` 41 | 42 | ## 仮想環境の無効化 43 | 44 | 仮想環境を無効にするときは `deactivate` というシェル関数を実行する。 45 | 46 | ```bash 47 | (myenv) $ deactivate 48 | $ 49 | ``` 50 | 51 | ## 仮想環境の削除 52 | 53 | 作成した仮想環境のディレクトリを削除する。 54 | 55 | ```bash 56 | $ rm -rf myenv 57 | ``` 58 | -------------------------------------------------------------------------------- /Testing/01_use_standard_library_only/doctest_sample.py: -------------------------------------------------------------------------------- 1 | """ 2 | doctest のサンプルコード 3 | 4 | * http://docs.python.jp/3/library/doctest.html 5 | """ 6 | 7 | def add(x, y): 8 | """ 9 | 引数に渡したパラメーターを加算した結果を返す 10 | 11 | >>> add(1, 2) 12 | 3 13 | >>> add(-3, 3) 14 | 0 15 | """ 16 | return x + y 17 | 18 | 19 | def get_twenty_list(): 20 | """ 21 | 0-19 までの20個の値をもつリストを返す 22 | 23 | >>> len(get_twenty_list()) 24 | 20 25 | 26 | >>> get_twenty_list() # doctest: +ELLIPSIS 27 | [0, 1, ..., 18, 19] 28 | 29 | >>> get_twenty_list() # doctest: +ELLIPSIS, +NORMALIZE_WHITESPACE 30 | [0, 1, 31 | ..., 32 | 18, 19] 33 | """ 34 | return list(range(20)) 35 | 36 | 37 | if __name__ == '__main__': 38 | import doctest 39 | import sys 40 | 41 | flags = doctest.REPORT_NDIFF | doctest.FAIL_FAST 42 | if len(sys.argv) > 1: 43 | name = sys.argv[1] 44 | if name in globals(): 45 | obj = globals()[name] 46 | else: 47 | obj = __test__[name] 48 | doctest.run_docstring_examples(obj, globals(), name=name, 49 | optionflags=flags) 50 | else: 51 | fail, total = doctest.testmod(optionflags=flags) 52 | print("{} failures out of {} tests".format(fail, total)) 53 | -------------------------------------------------------------------------------- /Testing/01_use_standard_library_only/unittest_tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/t2y/python-study/52a132ea600d4696164e540d8a8f8f5fc58e097a/Testing/01_use_standard_library_only/unittest_tests/__init__.py -------------------------------------------------------------------------------- /Testing/01_use_standard_library_only/unittest_tests/test_sample1.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | 3 | """ 4 | unittest のサンプルコード 5 | 6 | * http://docs.python.jp/3/library/unittest.html 7 | """ 8 | 9 | class TestStringMethods(unittest.TestCase): 10 | 11 | def test_upper(self): 12 | self.assertEqual('foo'.upper(), 'FOO') 13 | 14 | def test_isupper(self): 15 | self.assertTrue('FOO'.isupper()) 16 | self.assertFalse('Foo'.isupper()) 17 | 18 | def test_split(self): 19 | s = 'hello world' 20 | self.assertEqual(s.split(), ['hello', 'world']) 21 | # check that s.split fails when the separator is not a string 22 | with self.assertRaises(TypeError): 23 | s.split(2) 24 | -------------------------------------------------------------------------------- /Testing/01_use_standard_library_only/unittest_tests/test_sample2.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | 3 | """ 4 | unittest のサンプルコード 5 | 6 | * http://docs.python.jp/3/library/unittest.html 7 | """ 8 | 9 | 10 | def is_prime(n): 11 | """ 12 | 素数判定関数 (低パフォーマンス) 13 | """ 14 | if n < 2: 15 | return False 16 | 17 | if n == 2: 18 | return True 19 | 20 | for i in range(2, n - 1): 21 | if n % i == 0: 22 | return False 23 | 24 | return True 25 | 26 | 27 | class PrimeTest(unittest.TestCase): 28 | 29 | def test_is_prime(self): 30 | """ 31 | Test that number is prime or not. 32 | """ 33 | data = [ 34 | (0, False), 35 | (1, False), 36 | (2, True), 37 | (3, True), 38 | (4, False), 39 | (5, True), 40 | (6, False), 41 | (7, True), 42 | (8, False), 43 | (9, False), 44 | ] 45 | 46 | for n, expect in data: 47 | with self.subTest(n=n, expect=expect): 48 | self.assertEqual(is_prime(n), expect) 49 | -------------------------------------------------------------------------------- /Testing/01_use_standard_library_only/unittest_tests/test_sample3.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | from datetime import datetime 3 | from unittest.mock import patch 4 | 5 | """ 6 | unittest.mock のサンプルコード 7 | 8 | * http://docs.python.jp/3/library/unittest.mock.html 9 | """ 10 | 11 | def greet(name): 12 | now = datetime.now() 13 | if now.hour < 12: 14 | return '%s, good morning' % name 15 | elif 12 <= now.hour < 16: 16 | return '%s, good afternoon' % name 17 | else: 18 | return '%s, good evening' % name 19 | 20 | 21 | class Greeting: 22 | 23 | def __init__(self, name): 24 | self.name = name 25 | 26 | def do_greet(self): 27 | return greet(self.name) 28 | 29 | 30 | class GreetingTestCase(unittest.TestCase): 31 | 32 | @patch('unittest_tests.test_sample3.greet') 33 | def test_greet(self, mock_greet): 34 | name = 'john' 35 | expected = '%s, good night' % name 36 | 37 | mock_greet.return_value = expected 38 | actual = greet(name) 39 | self.assertEqual(actual, expected) 40 | 41 | # assert greet function is called with this parameter 42 | mock_greet.assert_called_with(name) 43 | 44 | @patch.object(Greeting, 'do_greet') 45 | def test_obj_greet(self, mock_obj_greet): 46 | name = 'bob' 47 | expected = '%s, hello' % name 48 | 49 | mock_obj_greet.return_value = expected 50 | g = Greeting(name) 51 | actual = g.do_greet() 52 | self.assertEqual(actual, expected) 53 | 54 | # assert Greeting.greet method is called with these parameter 55 | mock_obj_greet.assert_called_with() 56 | -------------------------------------------------------------------------------- /Testing/02_use_3rd_party/package-sample/MANIFEST.in: -------------------------------------------------------------------------------- 1 | include LICENSE 2 | include MANIFEST.in 3 | include README.md 4 | recursive-include mypackage *.py 5 | recursive-include tests *.py 6 | -------------------------------------------------------------------------------- /Testing/02_use_3rd_party/package-sample/mypackage/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/t2y/python-study/52a132ea600d4696164e540d8a8f8f5fc58e097a/Testing/02_use_3rd_party/package-sample/mypackage/__init__.py -------------------------------------------------------------------------------- /Testing/02_use_3rd_party/package-sample/mypackage/main.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import division, print_function, absolute_import 3 | 4 | import sys 5 | from os.path import basename 6 | 7 | from enum import Enum 8 | 9 | 10 | __version__ = '0.1.0' 11 | 12 | 13 | class Color(Enum): 14 | red = 1 15 | green = 2 16 | blue = 3 17 | 18 | 19 | def get_color(value_or_attribute): 20 | if value_or_attribute in Color.__members__: 21 | return getattr(Color, value_or_attribute) 22 | 23 | return Color(int(value_or_attribute)) 24 | 25 | 26 | def main(): 27 | print('I am mypackage.main') 28 | if len(sys.argv) < 2: 29 | print('Usage: %s red' % basename(sys.argv[0])) 30 | return 31 | 32 | value = get_color(sys.argv[1]) 33 | print(value) 34 | 35 | 36 | if __name__ == '__main__': 37 | main() 38 | -------------------------------------------------------------------------------- /Testing/02_use_3rd_party/package-sample/mypackage/utils.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import division, print_function, absolute_import 3 | 4 | 5 | def cmd(): 6 | print('I am utils.cmd') 7 | -------------------------------------------------------------------------------- /Testing/02_use_3rd_party/package-sample/setup.py: -------------------------------------------------------------------------------- 1 | import re 2 | import sys 3 | 4 | from setuptools import find_packages, setup 5 | 6 | 7 | metadata_py = open('mypackage/main.py').read() 8 | metadata = dict(re.findall("__([a-z]+)__ = '([^']+)'", metadata_py)) 9 | 10 | setup( 11 | name='mypackage', 12 | version=metadata['version'], 13 | description='Sample pakcage to learn Python packaging', 14 | classifiers=[ 15 | 'License :: OSI Approved :: Apache Software License', 16 | 'Development Status :: 4 - Beta', 17 | 'Intended Audience :: Developers', 18 | 'Operating System :: OS Independent', 19 | 'Programming Language :: Python :: 3', 20 | 'Programming Language :: Python :: 3.6', 21 | 'Programming Language :: Python :: 3.7', 22 | 'Programming Language :: Python :: Implementation :: CPython', 23 | 'Topic :: Software Development :: Libraries', 24 | 'Environment :: Console', 25 | 'Topic :: Utilities', 26 | ], 27 | url='https://github.com/t2y/python-study/tree/master/Testing', 28 | license='Apache License 2.0', 29 | author='Tetsuya Morimoto', 30 | author_email='t2y@example.com', 31 | zip_safe=False, 32 | platforms=['unix', 'linux', 'osx', 'windows'], 33 | packages=find_packages(), 34 | include_package_data=True, 35 | install_requires=[ 36 | 'requests', 37 | ], 38 | tests_require=[ 39 | 'tox', 'pytest', 'pytest-pep8', 'pytest-flakes', 40 | ], 41 | entry_points = { 42 | 'console_scripts': [ 43 | 'mycmd=mypackage.main:main', 44 | 'yourcmd=mypackage.utils:cmd', 45 | ], 46 | }, 47 | ) 48 | -------------------------------------------------------------------------------- /Testing/02_use_3rd_party/package-sample/tests/test_enum.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import division, print_function, absolute_import 3 | 4 | import pytest 5 | 6 | from mypackage.main import Color, get_color 7 | 8 | 9 | @pytest.mark.parametrize(("argument", "expected"), [ 10 | # attributes 11 | ('red', Color.red), 12 | ('green', Color.green), 13 | ('blue', Color.blue), 14 | 15 | # values 16 | ('1', Color.red), 17 | ('2', Color.green), 18 | ('3', Color.blue), 19 | 20 | ], ids=[ 21 | 'red is specified as argument', 22 | 'blue is specified as argument', 23 | 'green is specified as argument', 24 | 25 | '1 is specified as argument', 26 | '2 is specified as argument', 27 | '3 is specified as argument', 28 | ]) 29 | def test_get_color_normal(argument, expected): 30 | actual = get_color(argument) 31 | assert actual is expected 32 | 33 | 34 | @pytest.mark.parametrize("argument", [ 35 | 'yellow', 36 | '5', 37 | ]) 38 | def test_get_color_exception(argument): 39 | with pytest.raises(ValueError): 40 | get_color(argument) 41 | -------------------------------------------------------------------------------- /Testing/02_use_3rd_party/package-sample/tox.ini: -------------------------------------------------------------------------------- 1 | [tox] 2 | envlist = py35, py36, py37 3 | 4 | [testenv] 5 | deps = 6 | pytest 7 | pytest-pep8 8 | pytest-flakes 9 | 10 | commands = py.test -v --pep8 --flakes mypackage tests 11 | -------------------------------------------------------------------------------- /Testing/02_use_3rd_party/pytest_sample/bad_coding_style.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import sys 3 | 4 | 5 | def f(x,y): 6 | z=x+y 7 | return z 8 | 9 | def g(): 10 | d = {'x':1, 'y':2} 11 | return d 12 | -------------------------------------------------------------------------------- /Testing/02_use_3rd_party/pytest_sample/tests/test_assert.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import pytest 3 | 4 | 5 | def func(x): 6 | return x + 1 7 | 8 | def test_answer(): 9 | assert func(3) == 5 10 | 11 | def test_raise(): 12 | with pytest.raises(ZeroDivisionError): 13 | 1 / 1 14 | -------------------------------------------------------------------------------- /Testing/02_use_3rd_party/pytest_sample/tests/test_fixture.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import pytest 3 | 4 | 5 | @pytest.fixture 6 | def my_args(): 7 | return [1, 2, 3] 8 | 9 | 10 | def test_fixture(my_args): 11 | assert len(my_args) == 3 12 | assert my_args[0] == 1 13 | 14 | 15 | def test_use_tmpdir(tmpdir): 16 | print(tmpdir) 17 | assert True 18 | -------------------------------------------------------------------------------- /Testing/02_use_3rd_party/pytest_sample/tests/test_parameterize.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import pytest 3 | 4 | 5 | @pytest.mark.parametrize(('x', 'y', 'expected'), [ 6 | (1, 2, 3), 7 | (3, 4, 7), 8 | (5, 6, 11), 9 | ]) 10 | def test_parameter(x, y, expected): 11 | assert x + y == expected 12 | -------------------------------------------------------------------------------- /Testing/03_use_type_hint/error_sample1.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from typing import Dict, List 3 | 4 | 5 | def f(x: float, y: float) -> Dict[str, int]: 6 | d = { 7 | 'x': x, 8 | 'y': y, 9 | } 10 | return d 11 | 12 | 13 | def g() -> List[int]: 14 | numbers = [] # type: List[str] 15 | for i in range(10): 16 | numbers.append(i) 17 | numbers.append('test') 18 | return numbers 19 | 20 | 21 | def main() -> None: 22 | print(f(0.1, 0.2)) 23 | print(g()) 24 | 25 | 26 | if __name__ == '__main__': 27 | main() 28 | -------------------------------------------------------------------------------- /Testing/03_use_type_hint/type_hint_builtin_types.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from typing import List, Set, Dict, Tuple, Text, Optional 3 | 4 | # For simple built-in types, just use the name of the type. 5 | i = 1 # type: int 6 | f = 1.0 # type: float 7 | b = True # type: bool 8 | s = "test" # type: str 9 | by = b"test" # type: bytes 10 | 11 | # For collections, the name of the type is capitalized, and the 12 | # name of the type inside the collection is in brackets. 13 | l = [1] # type: List[int] 14 | si = set([6, 7]) # type: Set[int] 15 | 16 | # For mappings, we need the types of both keys and values. 17 | d = dict(field=2.0) # type: Dict[str, float] 18 | 19 | # For tuples, we specify the types of all the elements. 20 | t = (3, "yes", 7.5) # type: Tuple[int, str, float] 21 | 22 | def func(n: int) -> Optional[str]: 23 | if n == 0: 24 | return None 25 | return 'not zero' 26 | 27 | # Use Optional for values that could be None. 28 | input_str = func(0) # type: Optional[str] 29 | if input_str is not None: 30 | print(input_str) 31 | -------------------------------------------------------------------------------- /Testing/03_use_type_hint/type_hint_classes.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | class MyClass: 4 | # The __init__ method doesn't return anything, so it gets return 5 | # type None just like any other method that doesn't return anything. 6 | def __init__(self) -> None: 7 | ... 8 | # For instance methods, omit `self`. 9 | def my_class_method(self, num: int, str1: str) -> str: 10 | return num * str1 11 | 12 | # User-defined classes are written with just their own names. 13 | x = MyClass() # type: MyClass 14 | -------------------------------------------------------------------------------- /Testing/03_use_type_hint/type_hint_complicated.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from typing import Union, Any, List, cast 3 | 4 | # To find out what type mypy infers for an expression anywhere in 5 | # your program, wrap it in reveal_type. Mypy will print an error 6 | # message with the type; remove it again before running the code. 7 | reveal_type(1) # -> error: Revealed type is 'builtins.int' 8 | 9 | # Use Union when something could be one of a few types. 10 | x1 = [3, 5, "test", "fun"] # type: List[Union[int, str]] 11 | 12 | # Use Any if you don't know the type of something or it's too 13 | # dynamic to write a type for. 14 | x2 = mystery_function() # type: Any 15 | 16 | # Use `ignore` to suppress type-checking on a given line, when your 17 | # code confuses mypy or runs into an outright bug in mypy. 18 | # Good practice is to comment every `ignore` with a bug link 19 | # (in mypy, typeshed, or your own code) or an explanation of the issue. 20 | x3 = confusing_function() # type: ignore # https://github.com/python/mypy/issues/1167 21 | 22 | # cast is a helper function for mypy that allows for guidance of how to convert types. 23 | # it does not cast at runtime 24 | a = [4] 25 | b = cast(List[int], a) # passes fine 26 | c = cast(List[str], a) # passes fine (no runtime check) 27 | reveal_type(c) # -> error: Revealed type is 'builtins.list[builtins.str]' 28 | print(c) # -> [4] the object is not cast 29 | 30 | # TODO: explain "Need type annotation for variable" when 31 | # initializing with None or an empty container 32 | -------------------------------------------------------------------------------- /Testing/03_use_type_hint/type_hint_functions.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from typing import Callable, Iterable, Union, Optional, List 3 | 4 | # This is how you annotate a function definition. 5 | def stringify(num: int) -> str: 6 | return str(num) 7 | 8 | # And here's how you specify multiple arguments. 9 | def plus(num1: int, num2: int) -> int: 10 | return num1 + num2 11 | 12 | # Add type annotations for kwargs as though they were positional args. 13 | def f1(num1: int, my_float: float = 3.5) -> float: 14 | return num1 + my_float 15 | 16 | # This is how you annotate a function value. 17 | x = f1 # type: Callable[[int, float], float] 18 | 19 | # A generator function that yields ints is secretly just a function that 20 | # returns an iterable (see below) of ints, so that's how we annotate it. 21 | def f2(n: int) -> Iterable[int]: 22 | i = 0 23 | while i < n: 24 | yield i 25 | i += 1 26 | 27 | # For a function with many arguments, you can of course split it over multiple lines 28 | def send_email(address: Union[str, List[str]], 29 | sender: str, 30 | cc: Optional[List[str]], 31 | bcc: Optional[List[str]], 32 | subject='', 33 | body: List[str] = None 34 | ) -> bool: 35 | return True 36 | -------------------------------------------------------------------------------- /Testing/README.md: -------------------------------------------------------------------------------- 1 | # Python でテストを書くためのチュートリアル 2 | 3 | ## このリポジトリの目的 4 | 5 | Python でテスト駆動開発 (TDD) をするための機能やツールなどを学ぶ。 6 | 7 | * 本リポジトリにあるコードは Python 3.6 以上で動作確認している 8 | 9 | 10 | ## 概要 11 | 12 | 大きく分けて3つに分類されます。 13 | 14 | これらの3つのうち、どれか1つを選択するといったものではなく、説明しやすいように分類しているだけです。例えば、標準ライブラリの doctest とサードパーティー製のツールを組み合わせて使うこともできます。そのプロジェクトの要件や状況に応じて適切な機能やテストツールの選択をすると良いです。 15 | 16 | 1. Python の標準ライブラリのみを使う 17 | 18 | * doctest 19 | * unittest (unittest.mock) 20 | 21 | 2. サードパーティー製のテストツールを使う 22 | 23 | * pytest 24 | * tox 25 | 26 | 3. 型ヒントと型チェッカーを使う 27 | 28 | * typing 29 | * mypy 30 | 31 | ## リポジトリを取得する 32 | 33 | このリポジトリを clone する。 34 | 35 | $ git clone git@github.com:t2y/python-study.git 36 | $ cd python-study/Testing 37 | $ ls 38 | 00_virtualenv 01_use_standard_library_only 02_use_3rd_party 03_use_type_hint README.md 39 | 40 | それぞれのテストのやり方については対応するディレクトリ配下にある README.md を参照してください。 41 | -------------------------------------------------------------------------------- /docx/read_footnote/read_docx_footnotes.py: -------------------------------------------------------------------------------- 1 | import io 2 | import sys 3 | import xml.etree.ElementTree as ET 4 | 5 | import docx 6 | from docx.opc.constants import RELATIONSHIP_TYPE as rt 7 | 8 | def read_xml_namespaces(xml): 9 | source = io.BytesIO(xml) 10 | ns_tree = ET.iterparse(source, events=['start-ns']) 11 | ns = dict([elem for event, elem in ns_tree]) 12 | return ns 13 | 14 | def read_footnote_xml(xml): 15 | ns = read_xml_namespaces(xml) 16 | ns_w = ns['w'] 17 | 18 | tree = ET.parse(io.BytesIO(xml)) 19 | root = tree.getroot() 20 | footnotes_elems = root.findall('.//w:footnote[@w:id]', namespaces=ns) 21 | 22 | footnotes = [] 23 | for fn in footnotes_elems: 24 | id_ = fn.attrib.get(f'{{{ns_w}}}id') 25 | if id_ is not None: 26 | if int(id_) > 0: 27 | texts = fn.findall('.//w:t', namespaces=ns) 28 | note = ''.join(i.text for i in texts).strip() 29 | footnotes.append((id_, note)) 30 | return footnotes 31 | 32 | 33 | doc = docx.Document(sys.argv[1]) 34 | 35 | for relation in doc.part.rels.values(): 36 | if relation.reltype == rt.FOOTNOTES: 37 | footnote_xml = relation.target_part.blob 38 | footnotes = read_footnote_xml(footnote_xml) 39 | 40 | for id_, note in footnotes: 41 | print(f'{id_}: {note}') 42 | -------------------------------------------------------------------------------- /profiler/profiler.py: -------------------------------------------------------------------------------- 1 | import inspect 2 | import sys 3 | 4 | 5 | def add(x, y): 6 | return x + y 7 | 8 | 9 | def print_trace(frame, event, arg): 10 | print('=' * 72) 11 | print('frame.f_code.co_name:', frame.f_code.co_name) 12 | print('event:', event) 13 | print('arg:', arg) 14 | print('inspect.getargvalues(arg):', inspect.getargvalues(frame)) 15 | 16 | 17 | def main(): 18 | add(3, 2) 19 | 20 | 21 | if __name__ == '__main__': 22 | sys.setprofile(print_trace) 23 | main() 24 | sys.setprofile(None) 25 | --------------------------------------------------------------------------------