20220225のPythonに関する記事は27件です。

データ分析 コロナと少子高齢化

#分析内容 新型コロナウイルスが少子高齢化にどのような影響を与えたかを分析する
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

VSCODEでstreamlitのパスが認識されないエラー

VSCODEで以下のように実行すると「could not be resolved(reportMissingImports)」とエラーが発生した。(streamlit、numpy、pandasすべてで) なんとか、解決できたため備忘録として記載しておく。 エラー内容「reportMissingImports」を調べてみると、以下のように表示されstreamlitを探せていないように感じた。 ターミナルでpythonと打ち、対話モードへ import streamlitと print(streamlit.__ file __)も入力。 そうすると、streamlitの場所(パス)が表示される。 C:\anaconda3\lib\site-packages\までをコピー VSCODE→ファイル→ユーザー設定→設定→設定の検索→extra pathと入力 Python › Analysis: Extra Pathsを見つけて、項目の追加を選択し 先ほどコピーしたC:\anaconda3\lib\site-packages\を貼り付ければok エラー内容を調べて理解し、自分が欲しい情報を探し出す力が必要だと感じた。 以下、参考にしたサイト 参考:Visual Studio Codeでライブラリやモジュールが could not be resolved になる時の対処法 URL:https://startlab.jp/learning-python/vscode-settings/
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

CloudFormationテンプレートをPythonで読み込む

CloudFormation(以下CFn)に使用するテンプレートファイルをプログラムで読み込んで色々するツールを作りたくなる事は往々にしてあると思います。 また、CFnには必須とも言える便利な組み込み関数が用意されています。 問題 通常、PythonでYAMLを読み込む場合はPyYAMLやruamel.yaml等を用いて解析しますが、 !RefなどのCFn独自の組み込み関数の短縮形を表す感嘆符("!")から始まる文字列は、タグ1としてYAMLで定義されているため解析時に以下のようなエラーが発生してしまいます。 yaml.constructor.ConstructorError: could not determine a constructor for the tag '!Ref' 解決策 aws-cliパッケージに、この問題を解決できる関数が実装されています。 aws-cli/yamlhelper.py at develop · aws/aws-cli ※AWS CLIのaws cloudformation validate-templateコマンド2にて使用されています。 CFnにて正しく読み取れる形のYAML文字列を渡すと、順序付き辞書型(collections.OrderedDict)に変換してくれます。 awscli.customizations.cloudformation.yamlhelper.py#yaml_parse def yaml_parse(yamlstr): """Parse a yaml string""" try: # PyYAML doesn't support json as well as it should, so if the input # is actually just json it is better to parse it with the standard # json parser. return json.loads(yamlstr, object_pairs_hook=OrderedDict) except ValueError: loader = SafeLoaderWrapper loader.add_constructor(yaml.resolver.BaseResolver.DEFAULT_MAPPING_TAG, _dict_constructor) loader.add_multi_constructor("!", intrinsics_multi_constructor) return yaml.load(yamlstr, loader) 変換時には短縮形のCFn組み込み関数が完全な関数名の構文に置き換えられます。 ※感嘆符!をFn ::に変換したりGetAttを標準的な配列へ変換したりなど ※コメントにあるようにRefやConditionは完全な関数名にFn ::が付かないので、感嘆符!のみ取り除かれます。 このような変換処理をローダーに読ませて、PyYAMLで改めてロードしています。 awscli.customizations.cloudformation.yamlhelper#intrinsics_multi_constructor # Some intrinsic functions doesn't support prefix "Fn::" prefix = "Fn::" if tag in ["Ref", "Condition"]: prefix = "" if tag == "GetAtt" and isinstance(node.value, six.string_types): # ShortHand notation for !GetAtt accepts Resource.Attribute format # while the standard notation is to use an array # [Resource, Attribute]. Convert shorthand to standard format value = node.value.split(".", 1) 補足 タグによってYAMLとして解析できなくて困るというのはCFnに限らずあるみたいです。 ※解決策は同じで、コンストラクタを追加する形のようです。 yaml.add_multi_constructor('!', lambda loader, suffix, node: None) 実装 環境 $ python -V Python 3.8.5 $ aws --version aws-cli/1.19.57 Python/3.8.5 Linux/4.14.209-160.335.amzn2.x86_64 botocore/1.20.57 pipenvで管理していますが、awscliのバージョンは1.19.2です。 pipfile.lock "awscli": { "hashes": [ "sha256:7ca82e21bba8e1c08fef5f8c2161e1a390ddc19da69214eca8db249328ebd204", "sha256:8b79284e7fc018708afe2ad18ace37abb6921352cd079c0be6d15eabeabe5169" ], "index": "pypi", "version": "==1.19.2" }, 例 cfn-yaml_parse.py import yaml from awscli.customizations.cloudformation.yamlhelper import yaml_parse if __name__ == '__main__': try: yaml_str = open('cfn-template.yaml').read() print(f'yaml_str:{yaml_str}') # YAML文字列を解析し、順序付き辞書型のオブジェクトを返却する yaml_dict = yaml_parse(yaml_str) print(f'yaml_dict:{yaml_dict}') except yaml.parser.ParserError as e: print(e) print('YAML形式として解析できない文字列です。(例:キーや:が無い)') except yaml.scanner.ScannerError as e: print(e) print('YAML形式として読み取れない値が含まれています。(例:CFnの組み込み関数の構文誤り)') cfn-template.yaml Resources: ExampleVpc: Type: AWS::EC2::VPC Properties: CidrBlock: "10.0.0.0/16" IPv6CidrBlock: Type: AWS::EC2::VPCCidrBlock Properties: AmazonProvidedIpv6CidrBlock: true VpcId: !Ref ExampleVpc ExampleSubnet: Type: AWS::EC2::Subnet DependsOn: IPv6CidrBlock Properties: AssignIpv6AddressOnCreation: true CidrBlock: !Select [ 0, !Cidr [ !GetAtt ExampleVpc.CidrBlock, 1, 8 ]] Ipv6CidrBlock: !Select [ 0, !Cidr [ !Select [ 0, !GetAtt ExampleVpc.Ipv6CidrBlocks], 1, 64 ]] VpcId: !Ref ExampleVpc yaml_dict出力結果(整形済み) OrderedDict([('Resources', OrderedDict([ ('ExampleVpc', OrderedDict([ ('Type', 'AWS::EC2::VPC'), ('Properties', OrderedDict([ ('CidrBlock', '10.0.0.0/16') ]) ) ]) ), ('IPv6CidrBlock', OrderedDict([ ('Type', 'AWS::EC2::VPCCidrBlock'), ('Properties', OrderedDict([ ('AmazonProvidedIpv6CidrBlock', True), ('VpcId', {'Ref': 'ExampleVpc'}) ]) ) ]) ), ('ExampleSubnet', OrderedDict([ ('Type', 'AWS::EC2::Subnet'), ('DependsOn', 'IPv6CidrBlock'), ('Properties', OrderedDict([ ('AssignIpv6AddressOnCreation', True), ('CidrBlock', { 'Fn::Select': [ 0, {'Fn::Cidr': [ {'Fn::GetAtt': [ 'ExampleVpc', 'CidrBlock' ] }, 1, 8 ]} ] }), ('Ipv6CidrBlock', { 'Fn::Select': [ 0, {'Fn::Cidr': [ {'Fn::Select': [ 0, {'Fn::GetAtt': [ 'ExampleVpc', 'Ipv6CidrBlocks' ] } ]}, 1, 64 ]} ] }), ('VpcId', { 'Ref': 'ExampleVpc' }) ]) ) ]) ) ]) )]) 参考 2.4. Tags - YAML Ain’t Markup Language (YAML™) version 1.2.2 ↩ validate-template — AWS CLI 1.22.62 Command Reference ↩
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Project Euler】Problem 90: 2つの立方体の数字

本記事はProjectEulerの「100番以下の問題の説明は記載可能」という規定に基づいて回答のヒントが書かれていますので、自分である程度考えてみてから読まれることをお勧めします。 問題 90.2つの立方体の数字 原文 Problem 90: Cube digit pairs 問題の要約:立方体の面に0-9の異なる数字を書いて2つ並べたときに以下のように平方数になる書き方を数えよ。ただし9と6は逆さに置いてどちらにも使える。 以下のステップで全探索で解きました。 0-9から6個の数字を選ぶ組み合わせを2つ作り、その直積を取る それに6か9が含まれていたら、6と9を加えて、その2つの立方体の2つから出来る2桁の数字の集合(2つの順序を入れ替えたものも含めて)を作る。 それに2桁の平方数が全部含まれていれば関数isSquareはTrueを返すので、それを数えて2で割る。 from itertools import chain from itertools import combinations as comb from itertools import product as prod sqn = set(["01","04","09","16","25","36","49","64","81"]) dgt = [str(i) for i in range(10)] def a69(lst): # add 6 and 9 if lst includes 6 or 9 return set(lst) | {"6","9"} if len(set(lst) & {"6","9"}) > 0 else set(lst) def isSquare(cub, d1, d2): return cub <= set(chain.from_iterable([((a+b),(b+a)) for a,b in prod(a69(d1), a69(d2))])) # divide 2 considering the duplication print(f"Answer : {[isSquare(sqn,d1,d2) for d1,d2 in prod(comb(dgt,6),comb(dgt,6))].count(True)//2}") 開発環境:Google Colab
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

TouchDesignerで人物認識(人数)してみた。(mac)

以下のページの通り、Mac Book Pro 2017で試した結果、 認識は一応できましたが、途中までしかうまくいかなかったので報告します。 (OpenCVのコードがなぜかエラーをはいて、四角形や文字を描いてくれなかったです。) 0. 上の記事通り、Pythonの仮想環境を作って、TouchDesignerにパスを通してください。 1. OP Excuteを作成し、以下を記載。 以下を、DATの、OP Excuteに記載します。 ssdlite_mobilenetv2.onnxのファイルパスのところだけ自分の環境に合わせて変えてください。 # me - this DAT. # changeOp - the operator that has changed # # Make sure the corresponding toggle is enabled in the OP Execute DAT. import cv2 import numpy as np import onnxruntime # 上のテストと一緒なので省略します(使うときは上からコピーして使ってください) coco_classes = { 1: 'person', 2: 'bicycle', 3: 'car', 4: 'motorcycle', 5: 'airplane', 6: 'bus', 7: 'train', 8: 'truck', 9: 'boat', 10: 'traffic light', 11: 'fire hydrant', 12: 'stop sign', 13: 'parking meter', 14: 'bench', 15: 'bird', 16: 'cat', 17: 'dog', 18: 'horse', 19: 'sheep', 20: 'cow', 21: 'elephant', 22: 'bear', 23: 'zebra', 24: 'giraffe', 25: 'backpack', 26: 'umbrella', 27: 'handbag', 28: 'tie', 29: 'suitcase', 30: 'frisbee', 31: 'skis', 32: 'snowboard', 33: 'sports ball', 34: 'kite', 35: 'baseball bat', 36: 'baseball glove', 37: 'skateboard', 38: 'surfboard', 39: 'tennis racket', 40: 'bottle', 41:'wine glass', 42: 'cup', 43: 'fork', 44: 'knife', 45: 'spoon', 46: 'bowl', 47: 'banana', 48: 'apple', 49: 'sandwich', 50: 'orange', 51: 'broccoli', 52: 'carrot', 53: 'hot dog', 54: 'pizza', 55: 'donut', 56: 'cake', 57: 'chair', 58: 'couch', 59: 'potted plant', 60: 'bed', 61: 'dining table', 62: 'toilet', 63: 'tv', 64: 'laptop', 65: 'mouse', 66: 'remote', 67: 'keyboard', 68: 'cell phone', 69: 'microwave', 70: 'oven', 71: 'toaster', 72: 'sink', 73: 'refrigerator', 74: 'book', 75: 'clock', 76: 'vase', 77: 'scissors', 78: 'teddy bear', 79: 'hair drier', 80: 'toothbrush' } # ここは自分のpc内のssdlite_mobilenetv2.onnxファイルへのパスを書いてください! session = onnxruntime.InferenceSession("~~~~~~/ssdlite_mobilenetv2.onnx") def onPreCook(changeOp): return def onPostCook(changeOp): # 画像の読み込み frame = changeOp.numpyArray(delayed=True) arr = frame[:, :, 0:3] arr = arr * 255 arr = arr.astype(np.uint8) arr = np.flipud(arr) width, height = arr.shape[0:2] image_data = np.expand_dims(arr, axis=0) # モデルの推論の準備 input_name = session.get_inputs()[0].name # 'image' output_name_boxes = session.get_outputs()[0].name # 'boxes' output_name_classes = session.get_outputs()[1].name # 'classes' output_name_scores = session.get_outputs()[2].name # 'scores' output_name_num = session.get_outputs()[3].name # 'number of detections' # 推論 outputs_index = session.run([output_name_num, output_name_boxes, output_name_scores, output_name_classes], {input_name: image_data}) # 結果を受け取る output_num = outputs_index[0] # 検出した物体数 output_boxes = outputs_index[1] # 検出した物体の場所を示すボックス output_scores = outputs_index[2] # 検出した物体の予測確率 output_classes = outputs_index[3] # 検出した物体のクラス番号 # 検出したものの中で人だけのリスト op('constant1').par.value0 = np.count_nonzero(outputs_index[3][0][0:int(outputs_index[0][0])] == 1) def onDestroy(): return def onFlagChange(changeOp, flag): return def onWireChange(changeOp): return def onNameChange(changeOp): return def onPathChange(changeOp): return def onUIChange(changeOp): return def onNumChildrenChange(changeOp): return def onChildRename(changeOp): return def onCurrentChildChange(changeOp): return def onExtensionChange(changeOp, extension): return 2. TOPのvideodeviceinを作成。OP ExcuteのMonitor OPsにvideodeviceinを記載。 3. CHOPのConstantを作成。 constantの名前を、「constant1」に。 これで、constant1の値は、カメラで認識された人の人物数になります。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Cost Explorer API で特定のコストカテゴリの料金を取得する

モチベーション AWS Cost Explorer は 日次や月次レベルで AWS のコストと使用量の可視化、把握、管理を行うことができるサービスです。コンソールから利用できますが、API が用意されているため、プログラムから定期的に目的のコストを状況を抽出して通知するなどといった自動化が可能です。 CloudWatch の Billing メトリクスでも各アカウントの概算費用は取得できますが、アカウント ID の情報しか持っていません。そのためシステム単位等の特定のアカウント郡のコストだけを抽出することは CloudWatch メトリクスの機能だけでは実現できません。 AWS Billing and Cost Management にはコストカテゴリという機能があり、特定のルールでコストのグループを作成できます。Cost Explorer ではコストカテゴリでフィルターした料金を抽出できるため、CloudWatch メトリクスより簡単かつ、柔軟にコストの分析が可能です。 コストカテゴリの作成 AWS Billing Management コンソールからコストカテゴリを作成できます。例えば以下のコストカテゴリおよびルールはアカウント名が SampleSystem- で始まるアカウントをグループ化しています。現状、AWS Organizations の OU 単位等でのグループ化はサポートされていないため、ここではアカウント名をベースにカテゴリを作成します。 コストカテゴリを作成すると、指定したルールによって分類されたコストを確認することができます。以下の例では全体 AWS コストのうち、51% がコストカテゴリでグループ化したアカウントのコストであることがわかります。(金額等はマスキングしています。) コストカテゴリ作成後、Cost Explorer でフィルター条件として指定できるようになります。 Cost Explorer API 経由で取得する 例えば AWS Lambda から boto3 を使用して月の合計料金を取得する場合、最低限の処理は以下のようになります。 lambda_function.py import boto3 def lambda_handler(event, context): ce = boto3.client('ce') response = ce.get_cost_and_usage( TimePeriod={ 'Start': '2022-02-01', 'End' : '2022-02-28', }, Granularity='MONTHLY', Metrics= [ 'NetUnblendedCost' ], Filter={ 'CostCategories': { 'Key': 'Sample System Accounts', 'Values': [ 'Sample System Accounts', ] } } ) print(response['ResultsByTime'][0]['Total']) 結果例 {'NetUnblendedCost': {'Amount': '42969.6600696831', 'Unit': 'USD'}} GroupBy を使用するとコストをグループ化して取得することが可能です。Cost Explorer コンソールではグループ化条件は 1 つまでしか適用できませんが、Cost Explorer API では最大 2 つのグループ化条件を指定することができます。以下の例ではコストカテゴリでアカウントを Filter し、GroupBy で Linked Account ごとにサービス別のコストを取得しています。 lambda_function.py import boto3 def lambda_handler(event, context): ce = boto3.client('ce') response = ce.get_cost_and_usage( TimePeriod={ 'Start': '2022-02-01', 'End' : '2022-02-28', }, Granularity='MONTHLY', Metrics= [ 'NetUnblendedCost' ], Filter={ 'CostCategories': { 'Key': 'Sample System Accounts', 'Values': [ 'Sample System Accounts', ] } }, GroupBy=[ { 'Type': 'DIMENSION', 'Key': 'LINKED_ACCOUNT' }, { 'Type': 'DIMENSION', 'Key': 'SERVICE' } ] ) print(response['ResultsByTime'][0]['Groups']) 結果例 (見やすくするため改行をいれています) [ {'Keys': ['123456789012', 'AWS Key Management Service'], 'Metrics': {'NetUnblendedCost': {'Amount': '0.1175595208', 'Unit': 'USD'}}}, {'Keys': ['123456789012', 'AWS Secrets Manager'], 'Metrics': {'NetUnblendedCost': {'Amount': '0.095238096', 'Unit': 'USD'}}}, {'Keys': ['123456789012', 'AWS Security Hub'], 'Metrics': {'NetUnblendedCost': {'Amount': '1.904', 'Unit': 'USD'}}}, {'Keys': ['123456789012', 'EC2 - Other'], 'Metrics': {'NetUnblendedCost': {'Amount': '0.4245898221', 'Unit': 'USD'}}}, {'Keys': ['123456789012', 'Amazon Elastic Compute Cloud - Compute'], 'Metrics': {'NetUnblendedCost': {'Amount': '0.0002534851', 'Unit': 'USD'}}}, {'Keys': ['111111111111', 'Amazon Elastic Container Service for Kubernetes'], 'Metrics': {'NetUnblendedCost': {'Amount': '25.302975656', 'Unit': 'USD'}}}, {'Keys': ['111111111111', 'Amazon Elastic Load Balancing'], 'Metrics': {'NetUnblendedCost': {'Amount': '3.888', 'Unit': 'USD'}}}, {'Keys': ['111111111111', 'Amazon Kinesis'], 'Metrics': {'NetUnblendedCost': {'Amount': '1.56', 'Unit': 'USD'}}}, {'Keys': ['111111111111', 'Amazon Simple Storage Service'], 'Metrics': {'NetUnblendedCost': {'Amount': '0.0000000128', 'Unit': 'USD'}}}, {'Keys': ['222222222222', 'Amazon Virtual Private Cloud'], 'Metrics': {'NetUnblendedCost': {'Amount': '5.6000999356', 'Unit': 'USD'}}}, ] あとはこれらの結果を加工して Slack 通知するなど、煮るなり焼くなり好きにできますね。 注意点 Cost Explorer API は 1 リクエストあたり、$0.01 の料金が発生します。呼び出し回数が増えると料金も高額になってきますのでご注意ください。 Cost Explorer API を使用してアプリケーションを作成する場合は、キャッシュレイヤーを含むように設計することが推奨されています。 Best practices for optimizing your Cost Explorer API costs If you're creating an application using the Cost Explorer API, we recommend architecting the application so that it has a caching layer. 以上です。 参考になれば幸いです。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

書籍「Pythonで儲かるAIをつくる」最新正誤訂正

はじめに 書籍「Pythonで儲かるAIをつくる」の著者です。 著者による書籍紹介ページ 書籍サポートページ Amazonリンク 書籍のNotebookをGoogle Colabで動かしてみたところ、動かないコードがあったり、出版時と異なるグラフが表示されることがあったりと、いろいろありました。 調べた結果(一部コード修正で対応した結果)を以下の形でまとめましたので、連携いたします。 正誤訂正 第1版第1-3刷 章 ページ 内容        補足 最終更新日 4章 p.77 コード4-1-6の図 (出版時) (現在) Google Colab上のライブラリバージョンアップで表示結果が変わりました。(グラフの表示順が項目順とそろって見やすくなっています) 2022-02-25 4章 p.96 コード4-2-4 最終行 (出版時)df6 = df5.replace({'デッキ': {np.nan: 'N'}}) (修正後) df5['デッキ'] = df5['デッキ'].astype(object); df6 = df5.fillna({'デッキ': 'N'}) Google Colab上のライブラリバージョンアップで動かなくなったことへの対応(replace関数が使えなくなったのでp.96脚注で解説しているのとほぼ同じ方法を用いています) 2022-02-25 4章 p.151 コード4-4-7 上から3行目以降 (誤) print(y_proba[:10,:]) (正) print(y_proba[10:20,:]) 2022-02-25 4章 p.152 図4-4-4 (誤) (正) 2022-02-25 5章 p.194 表5-1 上から4行目 (誤)Prospect (正)Prophet 2022-02-25 5章 p.213 下から2行目 (誤)y=0 (正)y=1 2021-06-13 5章 p.213 下から1行目 (誤)y_proba0 (正)y_proba1 2021-06-13 5章 p.214 上から2行目、3行目 (誤)y_proba0 (正)y_proba1 2021-06-13 5章 p.232 下から2行目 (出版時)df.hist(bins=50) (現在)df.hist(bins=20, column=columns[1:]) Google Colab上のライブラリバージョンアップで日付列も表示されるようになったことへの対応でコードを修正 2022-02-25 5章 p.232 コード5-2-6の図 (出版時) (現在) Google Colab上のライブラリバージョンアップで表示結果が変わりました。(グラフの表示順が項目順とそろって見やすくなっています) 2022-02-25 正誤訂正 第1版第1-2刷 章 ページ 内容        補足 最終更新日 2章 p.21 下から6行目(誤)教師なし学習と教師なし学習(正)教師あり学習と教師なし学習 2020-12-21 6章 p.329 上から2行目(誤)bank-autoai.ipynb(正)c31_bank_autoai_data.ipynb 2020-12-21 6章 p.329 上から3行目(誤)bank-autoai.csv(正)bank-train-jp-autoai.csv 2020-12-21 6章 p.329 図6-1を以下に差し替え 2020-12-21 6章 p.329 図6-2を以下に差し替え 2020-12-21
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

事業企画1年目のpython学習5日目

本記事について とある事業会社で事業企画をやっています。 元々は、新規営業1年→エンプラ営業3年やっていました。 事業企画への異動に伴って、ルーチンワークの自動化やスクレイピングに興味を持ちpython学習をスタートしました。 目的達成までは記事を執筆予定です。どうぞよろしくお願いいたします。 開発環境 jupyterlab 使っている本 すっきりわかるpython入門 https://sukkiri.jp/books/sukkiri_python 勉強計画 週5日間ミニマム1h勉強する。 最初の1weekは1冊終わらせることをgoalに。 start@ 2022/2/21 学習時間 1.0h 学び 今日は繰り返しを学びたいと思います〜 pythonで部品を組み上げる 関数 今までの繰り返しなどを用いれば色々とプログラムを作れますが、見通しの悪いプログラム/修正難易度がかなり高いコードになっていきます。 そこで重要なのは、関数を自作することです。 関数の定義と関数の呼び出し 定義 def 関数名():    処理 ※pythonで定義されている関数(例えば、print)と重複することは許されているため、間違えて定義されている関数を定義するのは避けよう。 呼び出し 関数名() def hello(): #関数を作成 print('hello my name is kamon') hello() #自作したhello関数を呼び出し #hello my name is kamon ローカル変数と独立性 関数の中で定義された変数は、その関数の中でしか使えない。 ローカル変数の独立性という。 ##引数と戻り値 引数 関数は独立した一つの世界だが、ローカル変数を関数の外から参照したい時もある。 その際に使うのが引数。 def hello(name): #呼び出した時に渡されるデータを受け取る変数 print('hello my name is{}'.format(name)) hello('kamon') #呼び出しと同時にkamonを渡す hello('asakura') #hello my name is kamon #hello my name is asakura 複数の引数を渡す 関数の定義 def 関数名(引数1,引数2,・・・):        処理 引数を利用する関数の呼び出し 関数名(引数1,引数2,・・・) コレクションを引き渡すこともできる。 def profile(name,age,hobby): #関数を定義、複数の引数を受け取る関数。 print('my name is{}'.format(name)) print('my age is{}'.format(age)) print('my hobby is{}'.format(hobby)) profile('kamon','27','coffee') #profileを入力 #my name iskamon #my age is27 #my hobby iscoffee 引数と戻り値を利用する関数の定義 def 関数名(引数1,引数2,・・・):       処理      return 戻り値 引数は複数の利用が可能だが、戻り値は1つのみ def plus(x,y): answer = x+y return answer ) answer = plus(100,50) print('足算の答えは{}'.format(answer)) #足算の答えは150 関数の連携 めっちゃ使うよ! def input_scores(name): print('{}さんのスコアを入力してください'.format(name)) network = int(input('networkの点数は? >>')) database = int(input('databaseの点数は? >>')) security = int(input('securityの点数は? >>')) scores = [network,database,security] return scores def calc_average(scores): avg = sum(scores) / len(scores) return avg def output_result(name,avg): print('{}さんの平均点は{}です。'.format(name,avg)) kamon_scores = input_scores('kamon') asakura_scores = input_scores('asakura') kamon_avg = calc_average(kamon_scores) asakura_avg = calc_average(asakura_scores) output_result('kamon',kamon_avg) output_result('asakura',asakura_avg) #kamonさんのスコアを入力してください #networkの点数は? >> 60 #databaseの点数は? >> 70 #securityの点数は? >> 70 #asakuraさんのスコアを入力してください #networkの点数は? >> 40 #databaseの点数は? >> 50 #securityの点数は? >> 60 #kamonさんの平均点は66.66666666666667です。 #asakuraさんの平均点は50.0です。 関数の応用テクニック ###暗黙のタプルによる複数の戻り値 タプルは、()を省略できる暗黙のルールがある。 そのため、()内を1つの戻り値として返しているが複数のように見える。 def plus_and_minus(a,b): return (a+b, a-b) (next,prev) = plus_and_minus(1978,1) デフォルト引数 def 関数名(仮引数名 = デフォルト値,・・・): 処理        return 戻り値 def eat(breakfast,lunch,dinner='curry'): #第一引数はだめ、最後から設定していくルール。 print('breakfast is {}'.format(breakfast)) print('lunch is {}'.format(lunch)) print('dinner is {}'.format(dinner)) eat('panda','egg') #最後の引数はcurryと定義されているため省略可能 #breakfast is panda #lunch is egg #dinner is curry eat('panda','egg','curry and rice') #なお、引数設定すれば下記の通りくる #breakfast is panda #lunch is egg #dinner is curry and rice 引数にキーワードを指定した関数呼び出し 関数名(仮引数名 1=実引数1,仮引数名2 =実引数 2,・・・) 可変長変数 def 関数名 (仮引数名1,仮引数名2,・・・,*仮引数名 n): ※呼び出し時にn個以上の実引数を指定できる ※第n引数意向に指定した実引数は、1つのタプルとして受け取る ※第n実引数の指定が省略された場合は、関数は空のタプルを受け取る ※可変長引数は、末尾の仮引数にしか指定することができない。 def eat (breakfast,lunch,dinner = 'curry',*desserts): print('朝は{}を食べました'.format(breakfast)) print('昼は{}を食べました'.format(lunch)) print('夜は{}を食べました'.format(dinner)) for d in desserts: print('おやつに{}を食べました'.format(d)) ) eat('トースト','パスタ','カレー','アイス','チョコ','カレー') #朝はトーストを食べました #昼はパスタを食べました #夜はカレーを食べました #おやつにアイスを食べました #おやつにチョコを食べました #おやつにカレーを食べました ということでした。 グローバル変数 name = 'kamon' def hello(): print('hello' + name ) #関数内にnameは定義されていないが、外に参照しに行っている。 hello() #hellokamon name = 'kamon' def change_name(): global name #この変数は、グローバルの変数であることを宣言している。 name = 'asakura' def hello(): print('hello ' + name) グローバル変数を濫用することは、可読性や混乱を招く観点でリスクが非常に高い。 今日ミスったところ 本日は特にありませんでした。 練習問題の難易度が上がって行っています。 定期的に振り返りたいと思います。 感想  金曜日ですね。 土日は休みなので、練習問題の振り返りと6・7章の学習を進めたいと思います。運が良ければ8章までやって一周本を終わらせるかもしれません。 ところで初級編を終えた後に、実践編としておすすめの本はあるのでしょうか? どなたかお勧めがあればご教示いただけると嬉しいです。 それでは今週もお疲れ様でした。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

JSONとdict型の相互変換(Python)

元々Scrapboxに書いていた内容。 dict型変数の中身をファイルに書き出したい時などに重宝します。 JSONからdict型へ .jsonファイルから読み込んでdict(辞書)型変数に変換 import json dir = "読み込み先ファイルパス" encoding = "utf-8" # 読み込むファイルのエンコードによって適宜変える。 with open(dir, mode="rt", encoding="utf-8") as f: data = json.load(f) # JSONのファイル内容をdictに変換する。 JSON形式の文字列からdict型変数に変換 import json s = '{"name":"太郎", "value": 100}' # 読み込む文字列 data = json.loads(s) # sをdictに変換 dict型からJSONへ dict型変数からJSONに変換して保存 import json dir = "書き込み先ファイルパス" data = {"name":"太郎", "value": 100} # 任意のdict型変数 with open(dir, mode="wt", encoding="utf-8") as f: json.dump(data, f, ensure_ascii=False, indent=2) ensure_asciiの値をFalseにしないと、asciiにある文字以外はエンコードされてしまう。 indentの値は任意で。 2とすると半角スペース2個でインデントする。 dict型変数からJSON形式の文字列に変換 import json data = {"name":"太郎", "value": 100} # 任意のdict型変数 s = json.dumps(data) # JSON形式の文字列に変換 参考 【Python】JSONの読書き|dump、dumpsの違い | なしブロ 【Python入門】JSON形式データの扱い方 - Qiita
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

PythonでのAPI Key管理方法

PythonのAPI Key管理方法 複数のAPIを使用するとAPI Keyの管理が大変になる。また明示的にAPI Keyをファイルに記述することはセキュリティの問題でできない。今回はpythonによるAPI Key管理方法の一つであるdotenvやmodule化を紹介する。 1. dotenv .envファイルに環境変数を記述することで、複数の変数を一つのファイルで管理することができる。 lib/.env VALUE_NAME = value main.py from dotenv import load_dotenv dotenv_path = join(dirname(__file__), 'lib/.env') load_dotenv(dotenv_path) 2. from *** import ( var1, var2 ) 特定のpython code(line/key.py)に環境変数(var1, var2)を記述して、from lib.key import ( var1, var2 )で環境変数を一つのPython fileにまとめることができる。以下ではos.environで既に登録された環境変数(DEFINEDVAR1,DEFINEDVAR2)を読み込み割り当てている。 lib/key.py import os import sys VAR1 = os.environ.get('DEFINEDVAR1', None) VAR2 = os.environ.get('DEFINEDVAR2', None) if VAR1 is None: print('Specify VAR1 as environment variable.') sys.exit(1) if VAR2 is None: print('Specify VAR2 as environment variable.') sys.exit(1) python from lib.key import ( VAR1, VAR2 )
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Pyhon line-bot-sdkによるLINE Bot備忘録

LINE Botの始め方 LINE Botの始め方は以下の記事を参照ください。 RichMenuの追加 以下は、RichMenuを作成して特定の画像ファイル(png or jpeg)をメニュー画面にする。RichMenuの追加はBotサーバーではなく、LocalのPCから下記を実行することで追加される。RichMenuは作成して画像を正しくuploadされれば、deploy無しでもすぐに反映される。 from linebot.models import ( RichMenu, RichMenuArea, RichMenuBounds, RichMenuSize, MessageEvent, TextMessage, TextSendMessage, SourceUser, SourceGroup, SourceRoom, TemplateSendMessage, ConfirmTemplate, MessageTemplateAction, ButtonsTemplate, ImageCarouselTemplate, ImageCarouselColumn, URITemplateAction, PostbackTemplateAction, DatetimePickerTemplateAction, CarouselTemplate, CarouselColumn, PostbackEvent, StickerMessage, StickerSendMessage, LocationMessage, LocationSendMessage, ImageSendMessage, VideoSendMessage, ImageMessage, VideoMessage, AudioMessage, FileMessage, UnfollowEvent, FollowEvent, JoinEvent, LeaveEvent, BeaconEvent, CameraAction, CameraRollAction, URIAction ) from linebot.models.actions import PostbackAction YOUR_CHANNEL_ACCESS_TOKEN = アクセストークン YOUR_CHANNEL_SECRET = シークレットキー line_bot_api = LineBotApi(YOUR_CHANNEL_ACCESS_TOKEN) handler = WebhookHandler(YOUR_CHANNEL_SECRET) # json item/RichMenuのサイズや位置、押すとどのようなactionをするか決める。 rich_menu_to_create = RichMenu( size=RichMenuSize(width=2500, height=843), selected=False, name="Main menu", chat_bar_text="Tap here", areas=[RichMenuArea( bounds=RichMenuBounds(x=0, y=0, width=2500, height=843), action=URIAction(label='Main Menu', uri='https://line.me'))] ) # 上記のjson形式の定義を用いてRichMenuを作成する rich_menu_id = line_bot_api.create_rich_menu(rich_menu=rich_menu_to_create) # RichMenuで表示する画像をLineのサーバーにuplaodする path = 'sample.png' with open(path, 'rb') as f: line_bot_api.set_rich_menu_image(rich_menu_id, "image/png", f) # 上記のRichMenuをデフォルトに設定する line_bot_api.set_default_rich_menu(rich_menu_id) # 今までuploadしたRichMenuのリストを取る rich_menu_list = line_bot_api.get_rich_menu_list() # 作成済RichMenuを削除する #line_bot_api.delete_rich_menu(rich_menu_id) x,y座標の指定はLine Bot designerというアプリを使えば簡単に確認できる。 以下の箇所を修正することでsize=RichMenuSize(width=2500, height=843),で指定した範囲のどの箇所でどの機能を割り当てるか決めることができる。以下は指定範囲を6分割にしてURLを開く機能とメッセージを送る機能を追加した。 python areas=[RichMenuArea( bounds=RichMenuBounds(x=8, y=0, width=810, height=835), action=URIAction(label='label1', uri='https://line.me')), RichMenuArea( bounds=RichMenuBounds(x=848, y=0, width=810, height=835), action=MessageAction(label='label2',text='label2')), RichMenuArea( bounds=RichMenuBounds(x=1690, y=0, width=810, height=835), action=MessageAction(label='label3',text='label3')), RichMenuArea( bounds=RichMenuBounds(x=0, y=850, width=810, height=835), action=MessageAction(label='label4',text='label4')), RichMenuArea( bounds=RichMenuBounds(x=840, y=850, width=810, height=835), action=MessageAction(label='label5',text='label5')), RichMenuArea( bounds=RichMenuBounds(x=1690, y=850, width=810, height=835), action=URIAction(label='label6', uri='https://www.google.com/maps/search/%E7%9A%87%E5%B1%85/@35.6861593,139.7491146,16z/data=!3m1!4b1')) ] 特定のメッセージに応答して、メッセージと画像を返す方法 相手が送ってきたものがメッセージか画像か動画か絵文字なのかを判断するのは、@handler.add(MessageEvent, message=TextMessage)のTextMessageで判断する。この場合、送られてきたメッセージがテキストメッセージの場合は以下を実行する。 画像を返送するには、ImageSendMessageを使って、以下のように使う。 example (ImageSendMessage(original_content_url="https://hoge/hoge.png", preview_image_url="https://hoge/hoge.png")) python @handler.add(MessageEvent, message=TextMessage) def handle_message(event): send_message = event.message.text display_name = 'None' # 送ってきたユーザーのユーザーidや表示名を取得する。 if isinstance(event.source, SourceUser): profile = line_bot_api.get_profile(event.source.user_id) user_id = event.source.user_id display_name = profile.display_name else: print("user profile can't not use") if send_message == "画像を見せて" and isinstance(event.source, SourceUser): profile = line_bot_api.get_profile(event.source.user_id) tmpname = profile.display_name # テキストと画像を送る。 line_bot_api.reply_message( event.reply_token, ((TextSendMessage(text="Thank you %s! Here is seat plan on reception."%tmpname)), (ImageSendMessage(original_content_url="https://mybucket.s3.ap-northeast-1.amazonaws.com/table1.PNG", preview_image_url="https://mybucket.s3.ap-northeast-1.amazonaws.com/table1.PNG")))) else: line_bot_api.reply_message( event.reply_token, TextSendMessage(text='登録していないメッセージです。')) 送られてきた画像を保存する方法 送られてきた画像をバイナリデータとして、Heroku使用時はHerokuサーバーに一時保存して、Amazon S3へ送る。 python @handler.add(MessageEvent, message=ImageMessage) def handle_image_message(event): display_name = 'None' if isinstance(event.source, SourceUser): profile = line_bot_api.get_profile(event.source.user_id) user_id = event.source.user_id display_name = profile.display_name else: print("user profile can't not use") # 送られてきた画像の情報を取得 message_content = line_bot_api.get_message_content(event.message.id) # 送られてきた画像を1024byte分割でバイナリデータとして取得 img_data = line_bot_api.get_message_content(event.message.id).iter_content() # 送られてきた画像を保存する(Heroku使用時はHeroku サーバーの一時保存場所に保存される) src_img_path = "./image/sample.png" with open(src_img_path, "wb") as f: for chunk in message_content.iter_content(): f.write(chunk) # 一時保存した画像をAmazon S3のデポジトリに保存する client = boto3.client( 's3', aws_access_key_id=AWS_ACCESS_KEY_ID, aws_secret_access_key=AWS_SECRET_ACCESS_KEY, region_name=AWS_DEFAULT_REGION ) Bucket = 'mybucketname' Key = '%s_%s.png'%(display_name,event.message.id) try: client.upload_file(src_img_path, Bucket, Key) line_bot_api.reply_message(event.reply_token,TextSendMessage(text='Correctly uploaded!!!')) except: line_bot_api.reply_message(event.reply_token,TextSendMessage(text='Failure uploaded!!!')) LINE公式アカウントを登録時にメッセージを送る方法 FollowEventを使って、以下のように記述する。 python @handler.add(FollowEvent) def handle_follow(event): line_bot_api.reply_message( event.reply_token, TextSendMessage(text='Hello')) requirements.txtに追加したmoduleを記述し忘れないで 新しいpython moduleをHelokuで使う場合、requirements.txtに追加したmoduleを記述しないと動かないことがある。 まとめ 今回はLINE Botの諸々の機能の追加方法を紹介した。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

pythonでExcelのコピペ自動化

テンプレートとなるexcelファイルに、複数のexcelデータの情報をコピーし、出力ファイルを作るプログラムをpythonで作りました。 環境 python3,xlwings,numpy をインストールしてください。 excelファイルの操作にはxlwingsライブラリを使いました。openpyxlも試しましたが、テンプレートファイルにグラフが含まれていたときに出力ファイルのグラフが消え、破損ファイル扱いになる、ロードが異様に遅い、などの問題が発覚したため、xlwingsが優れているようです。 仕様 まず以下のようなインプットファイル(.xlsx)を用意します。 このファイルをpythonプログラムに与えると、templateファイルを元にして、コピペが実行されます。基本的な動作はこのファイルから想像できると思います。説明が必要な点を以下にのべます。 header headerの項目は、コピーしたデータ範囲の上か左のセルに入力される文字列です。headerが入力されるセルは、縦データの場合は先頭セルの上、横データの場合は先頭セルの左になります。headerを空欄にすると、データだけコピーされます。 input range, ouput range 基本的にはExcelの範囲指定と同じですが、「A5から下全部」のような範囲指定を'A5:A'のように指定することができます。 プログラム 実行 python xlcp.py input.xlsx オプション -f : アウトプットファイルと同じ名前のファイルが既にあった場合、そのファイルを上書きします。 -t : コピー元の範囲の行と列を入れ替えてからコピーします。 --headerPosition : ヘッダーの位置を変更します。 デフォルトは (top,left)です。 その他 関連するxlsxファイルが既に開いている場合、読み取りエラーがおこることがあります。関連するファイルは閉じておいたほうが無難です。 インプットファイルが複数シートをもつ場合、全てのシートの命令を実行します。 xlsxファイル以外にも、excelで開けるファイルすべてに対して同じように動作します。(xlwingsは実質的にExcelを操作しているため) コード ExcelクラスはExcelインスタンスを管理します。このようにする前は、Excelインスタンスがたくさん立ち上がって、本プログラム終了後もそれらが立ち上がったままになるという問題が生じました。プログラム動作中はExcelインスタンス一つだけが立ち上がり、プログラム終了時にそのインスタンスがたち下がる、という動作にしたいため、このクラスを作って、常に同じインスタンスを操作するようにしました。 Orderクラスにインプットファイルのシートの情報が入り、SubOrderクラスに、一つのコピーに必要な情報が入ります。 xlcp.py import argparse import xlwings import os import shutil import numpy as np from enum import Enum,auto class style(): BLACK = '\033[30m' RED = '\033[31m' GREEN = '\033[32m' YELLOW = '\033[33m' BLUE = '\033[34m' MAGENTA = '\033[35m' CYAN = '\033[36m' WHITE = '\033[37m' UNDERLINE = '\033[4m' RESET = '\033[0m' class Excel(): def __init__(self): self.app = xlwings.App(visible=False) self.books = [] for app in xlwings.apps: if app == self.app: continue for book in app.books: self.books.append(book) def __del__(self): self.app.quit() def isOpen(self,file): if file in [book.fullname for book in self.books]: return True return False def open(self,file): for book in self.books: if book.fullname == file: return book book = self.app.books.open(file) self.books.append(book) return book def close(self,book): if book not in self.books: book.close() class Position(Enum): TOP = auto() BOTTOM = auto() RIGHT = auto() LEFT = auto() def get(string): for item in Position: if item.name == string.upper(): return item return None @staticmethod def parse(string): s = string.strip().strip('{[()]}').split(',') s1 = s[0] s2 = s[1] if len(s) > 1 else None return list(map(Position.get,(s1,s2))) class Range: def __init__(self,string): self.startRow = 0 self.startColumn = 0 self.endRow = 0 self.endColumn = 0 self.range = None self.parse(string) def __str__(self): return '{{{0},{1},{2},{3}}}'.format(self.startRow,self.startColumn,self.endRow,self.endColumn) def set(self,sheet): tl = sheet[self.startRow,self.startColumn] if not self.endRow: bl = tl.end('down') else: bl = tl.offset(self.endRow - self.startRow,0) if not self.endColumn: br = bl.end('right') else: br = bl.offset(0,self.endColumn - self.startColumn) self.range = sheet.range(tl,br) def getHeaderCell(self,position): if self.range: (row,column) = self.range.shape tl = self.range[0] ofsr = 0 ofsc = 0 if position[0] == Position.TOP: ofsr = -1 elif position[0] == Position.LEFT: ofsc = -1 elif position[0] == Position.BOTTOM: ofsr = row elif position[0] == Position.RIGHT: ofsc = column if position[1]: if position[1] == Position.RIGHT: ofsc = column - 1 elif position[1] == Position.BOTTOM: ofsr = row - 1 return tl.offset(ofsr,ofsc) def parse(self,string): if type(string) is str: (start,end) = string.split(":") (self.startRow,self.startColumn) = Range.parseCell(start) (self.endRow,self.endColumn) = Range.parseCell(end) @staticmethod def parseAlpha(astr): num=0 exp=1 if not astr: num = None else: for a in astr: num = exp*num + ord(a.lower())-96 exp *= 26 if num: num = num - 1 return num @staticmethod def parseCell(cstr): (alpha,num) = ("","") for i in range(len(cstr)): if cstr[i].isalpha(): alpha += cstr[i] else: num = cstr[i:] break else: num = "" if num : num = int(num) - 1 else: num = None return (num,Range.parseAlpha(alpha)) class SubOrder: def __init__(self,order,args): ( self.header, self.filein, self.sheetin, self.rangein, self.fileout, self.sheetout, self.rangeout ) = args self.rangein = Range(self.rangein) self.rangeout = Range(self.rangeout) self.order = order def __str__(self): return ('header : {0}\n' 'filein : {1}\n' 'sheetin : {2}\n' 'rangein : {3}\n' 'fileout : {4}\n' 'sheetout : {5}\n' 'rangeout : {6}\n' ).format(self.header,self.filein,self.sheetin,str(self.rangein),self.fileout,self.sheetout,str(self.rangeout)) def isProper(self): return all([self.filein,self.sheetin,self.fileout,self.sheetout]) def read(self): filein = os.path.join(self.order.dirin,self.filein) if not os.path.exists(filein): print(style.CYAN + self.filein + style.RESET + ' does not exist in ' + style.CYAN + self.order.dirin + style.RESET) return False print('loading ' + style.CYAN + filein + style.RESET) bookin = order.excel.open(filein) if self.sheetin not in [sheet.name for sheet in bookin.sheets]: print(style.CYAN + self.sheetin + style.RESET + ' does not exist in ' + style.CYAN + filein + style.RESET) order.excel.close(bookin) return False sheetin = bookin.sheets[self.sheetin] self.rangein.set(sheetin) self.order.array = self.rangein.range.options(convert=np.array,ndim=2).value if self.order.transpose: self.order.array = self.order.array.transpose() print('data shape : ' + style.CYAN + str(self.order.array.shape) + style.RESET) order.excel.close(bookin) return True def write(self): fileout = os.path.join(self.order.dirout,self.fileout) if fileout in self.order.dict: bookout = self.order.dict[fileout] else: if not os.path.exists(fileout): shutil.copy(self.order.temp,fileout) elif not args.forceOverwrite: strin=input('file ' + style.CYAN + f'{fileout}' + style.RESET + ' already exists. Overwrite? (' + style.CYAN + 'y' + style.RESET + '/' + style.RED + 'n' + style.RESET + ')') if strin[0] != 'y': order.nolist.append(self.fileout) return bookout = order.excel.open(fileout) self.order.dict[fileout] = bookout if self.sheetout not in [sheet.name for sheet in bookout.sheets]: bookout.sheets.add(self.sheetout) print('created' + style.CYAN + self.sheetout + style.RESET + ' in ' + style.CYAN + self.fileout) sheetout = bookout.sheets[self.sheetout] self.rangeout.set(sheetout) if self.header: self.rangeout.getHeaderCell(self.order.headerPosition).value = self.header rangeout = self.rangeout.range rangeout.value = self.order.array[:rangeout.shape[0],:rangeout.shape[1]] class Order: def __init__(self,sheet,args,excel): self.temp = None self.dirin = None self.dirout = None self.list = [] self.nolist = [] self.dict = {} self.headerPosition = Position.parse(args.headerPosition) self.transpose = args.transpose self.load(sheet) self.excel = excel def __str__(self): string = ( 'temp : {0}\n' 'dirin : {1}\n' 'dirout : {2}\n\n' ).format(self.temp,self.dirin,self.dirout) for elem in self.list: string += str(elem) if elem is not self.list[-1]: string += '\n\n' return string def load(self,sheet): self.temp = sheet.range('B1').value self.dirin = sheet.range('B2').value self.dirout = sheet.range('B3').value self.list = [] self.dict = {} self.nolist = [] tl = sheet.range('A6') bl = tl.end('down') br = bl.offset(0,6) data = sheet.range(tl,br).value for row in data: subOrder = SubOrder(self,row) if subOrder.isProper(): self.list.append(subOrder) print(subOrder) def execAll(self): for suborder in self.list: if suborder.fileout in self.nolist: continue print(style.CYAN + suborder.filein + style.RESET + '->' + style.CYAN + suborder.fileout + style.RESET) if suborder.read(): suborder.write() for bookname,book in self.dict.items(): print('saving ' + style.CYAN + bookname + style.RESET) book.save() print('saved') self.excel.close(book) parser = argparse.ArgumentParser() parser.add_argument('orderFile',help='input order file') parser.add_argument('-t','--transpose',action='store_true',help='transpose data') parser.add_argument('-f','--forceOverwrite',action='store_true',help='force overwrite') parser.add_argument('--headerPosition',default='(top,left)',help='position of the header with respect to the data. default : (top,left). set this to (left,top) to put header to the side.') args = parser.parse_args() os.system('') try: excel = Excel() book = excel.open(args.orderFile) orderList = [] for sheet in book.sheets: orderList.append(Order(sheet,args,excel)) excel.close(book) for order in orderList: order.execAll() finally: pass
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Python ログの重複が出たときに確認すること

モジュールが増えてくるとロガーも必要に応じて追加しますよね。そのに落とし穴があって、ルートロガー以外にハンドラーを設定するとログの重複が発生するのです。 { 'root': { 'level': 'DEBUG', 'handlers': ['console', 'file'], 'propagate': True }, 'mod1': { 'level': 'DEBUG', 'handlers': ['console', 'file'], }, 'mod2': { 'level': 'DEBUG', 'handlers': ['console', 'file'], }, } こんな感じにやると、ログの重複が起こります。 ドキュメントの一番上にこのような注釈があります。 注釈 ハンドラを、あるロガー と その祖先のロガーに接続した場合、同一レコードが複数回発行される場合があります。一般的に、ハンドラを複数のロガーに接続する必要はありません。propagate 設定が True のままになっていれば、ロガーの階層において最上位にある適切なロガーにハンドラを接続するだけで、そのハンドラは全ての子孫ロガーが記録する全てのイベントを確認することができます。一般的なシナリオでは、ハンドラをルートロガーに対してのみ接続し、残りは propagate にすべて委ねます。 正しい書き方 { 'root': { 'level': 'DEBUG', 'handlers': ['console', 'file'], 'propagate': False # 推奨 }, 'mod1': { 'level': 'DEBUG', }, 'mod2': { 'level': 'DEBUG', }, } 結論 最上位のロガー(この例だとroot)に1つhandlersをセットすればよい。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Python pytestでcaplogがログをキャッチできないとき

結論 loggersのconfigでpropagateをTrueにする dictConfigの例 { 'loggers': { 'logger_name': { 'level': 'DEBUG', 'handlers': [], 'propagate': True # ここです } } } 参考 loggers - 対応する値は辞書で、そのそれぞれのキーがロガー名になり、それぞれの値が対応する Logger インスタンスをどのように環境設定するかを記述する辞書になります。 環境設定辞書は、以下のキーを検索されます: propagate (任意)。ロガーの伝播の設定です。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Pythonでディレクトリを(存在するかどうか確かめて存在しなかったら)作成する

1. はじめに ディレクトリの作成方法やチェック方法をよく忘れるので、自分の備忘録的に残しておきます。 僕と同じようにすぐ忘れちゃう系エンジニアや駆け出しエンジニアのお役に立てればと思い、しれっと初投稿。 ちなみに「あと10秒しか調べる時間がない…!」という危機に直面中の人は「まとめ」に飛んでください。 自分の動作環境は簡便ですがこんな感じです。 バージョン:Python 3.9.1 OS:Windows 10 2. ディレクトリ作成 ディレクトリを作成する関数を調べると以下の2つが出てきました。 import os os.mkdir([ディレクトリパス]) # 1つめ os.makedirs([ディレクトリパス]) # 2つめ ただし、1つ目のos.mkdirは、「...../存在しないディレクトリ/存在しないディレクトリ」のように深い階層で存在しないディレクトリ群を作ることができないようなので、基本的には2つ目のos.makedirsを使っていくのがよさそうです。 よって今回はos.makedirsでディレクトリの作成方法をメモします(公式ドキュメントのリンクはまとめに貼っておきます)。 というわけで、ディレクトリ作成のコードは以下。 import os dir = 'mydir' # 新たに作成するディレクトリ名やパス名 os.makedirs(dir) # 指定した名前(パス)でディレクトリを作成 以上です!いかがでしたか?お疲れさまでした! …とはいかず、これはそのディレクトリがまだ存在していない時限定で、既に存在するディレクトリを同様に作成しようとすると以下のようなエラーが発生してしまいました。 FileExistsError: [WinError 183] 既に存在するファイルを作成することはできません。: 'mydir' 3. ディレクトリの存在をチェックして作成(方法1) というわけで、「まずは作りたいディレクトリの存在をチェックして、存在しなかった場合には作成する」ための方法は以下。 よく紹介されている方法なので(後述の方法2より1行多いけど)メモ。 import os dir = "mydir" if not os.path.exists(dir): # ディレクトリが存在するか確認 os.makedirs(dir) # ディレクトリ作成 os.path.exists(dir) は、指定したディレクトリdirが存在すればTrue、なければFalseを返します。 ディレクトリが存在しなかったとき(Falseだったとき)だけ作成したいので、if notで判定しています。 重ねて作成しようとしたときに親切に何か出力したい時なんかがあれば使える感じかしら? ちなみにos.path.exists()は、ディレクトリだけでなくファイルが存在するかどうかのチェックにも使えるので覚えておこう(自分に言ってます)。 4. ディレクトリの存在をチェックして作成(方法2) makedirsのコードを見たところ、方法1のようにif文を使わなくても、以下1行でできてしまうようでした。 (ドキュメントを見るとバージョン3.2から追加実装されたそう) import os dir = "mydir" os.makedirs(dir, exist_ok=True) makedirsの引数に「exist_ok=True」を入れると、方法1のようにif文で2行書かなくても良いようです。 デフォルトだと(引数に書かないと)「exist_ok=False」になっているので、方法1のようにif文で分岐が必要になるわけです。 というわけで、比較的最近のバージョンを使っているのであればこれが一番楽そうですね。 5. makedirs() の exist_ok=Trueに関する余談 ここからはちょっと細かい話。 このmakedirsのコードを見ると、引数まで書くとmakedirs(name, mode=0o777, exist_ok=False)となっています。 公式ドキュメントを読んでみると、第2引数のmodeはディレクトリの権限モードを表すらしく、3.4.1より以前では動作が不安定な部分もあったようです。 以下、公式ドキュメントからの引用です。 Python 3.4.1 より前、 exist_ok が True でそのディレクトリが既存の場合でも、 makedirs() は mode が既存ディレクトリのモードと合わない場合にはエラーにしようとしていました。このモードチェックの振る舞いを安全に実装することが出来なかったため、 Python 3.4.1 でこのチェックは削除されました。 というわけで、恐らくpython 3.4.1以降での使用を推奨する感じ、ということでしょうか…? 6. まとめ ディレクトリを作成するコードを書くときは、 import os os.makedirs("ディレクトリパス", exist_ok=True) が一番簡単らしい。 ただし実装されたのはpython 3.2以降みたいだし、3.4.1より前ではちょっとエラーが出ちゃう場合もあるかも知れないから、バージョンには注意してね!(自分に言ってます) 公式ドキュメントはこちら。⇒ Python 3.9.1ドキュメント os.makedirs
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

「新・明解Pythonで学ぶアルゴリズムとデータ構造」で勉強日記#19

【出典】「新・明解Pythonで学ぶアルゴリズムとデータ構造」 前回の記事はこちら 5-4 8王妃問題 今回もハノイの塔と同様に、問題を小さく分割することで8王妃問題を学習します。 8王妃問題とは 互いに取りあえないように、8個の王妃(チェスのクイーン)をチェス盤に配置せよ。という問題です。 王妃の配置 8個の王妃を配置する組み合わせは全部で、64×63×62×61×60×59×58×57=178462987637760通りあります。そこで、 【方針1】各列には王妃を1個だけ配置する とすると、8の8乗=16777216通りになります。 これでも、すべての配置が8王妃問題の会ではありません。 そこで、 【方針2】各行には王妃を1個だけ配置する とします。 分枝操作 次々と枝分かれを行うことによって、すべての組み合わせを列挙するプログラムを作ります。 list5-7 #各列に1個の王妃を配置する組み合わせを再帰的に列挙 pos = [0] * 8 #各列の王妃の位置 def put() -> None: """盤面(各列の王妃の位置)を出力""" for i in range(8): print(f'{pos[i]:2}', end=' ') print() def set(i: int) -> None: """j列目に王妃を位置""" for j in range(8): pos[i] = j #王妃をj行に配置 if i == 7: #全列に配置終了 put() else: set(i + 1) #次の列に王妃を配置 set(0) #0列目に王妃を配置 次々と枝分かれを行っていくことによって、配置の組み合わせを列挙する手法を分枝操作といいます。 また、ハノイの塔や、8王妃問題のように問題を小問題に分割し、小問題の解を統合して全体の解を得ようとする手法を、分割統治法と呼びます。 限定操作と分枝限定法 先ほどは列挙しただけで、解を得ることはできません。そこで【方針2】の考えを組み入れます。 list5-8 # 各行・各列に1個の王妃を配置する組み合わせを再帰的に列挙 pos = [0] * 8 #各列の王妃の位置 flag = [False] * 8 #各行に王妃が配置済みか def put() -> None: """盤面(各列の王妃の位置)を出力""" for i in range(8): print(f'{pos[i]:2}', end=' ') print() def set(i: int) -> None: """j列目に王妃を位置""" for j in range(8): if not flag[j]: #j行には王妃は未配置 pos[i] = j #王妃をj行に配置 if i == 7: #全列に配置終了 put() else: flag[j] = True set(i + 1) #次の列に王妃を配置 flag[j] = False set(0) #0列目に王妃を配置 新たに flagというlist型の配列を導入しており、同一行に重複して王妃を配置しないようにするための目印です。 配置済みであればflag[j]をTrue、未配置であれば、Falseとします。 関数setでは、王妃が未配置の行(flag[j]がFalseである行)に対してのみ王妃を配置していきます。このように必要のない枝分かれを抑制して、不要な組み合わせの列挙を省く手法を限定操作と呼び、分枝操作と組み合わせて問題を解くのが、分枝限定法です。 8王妃問題を解くプログラム list5-8のプログラムは、王妃が行方向と列方向に重複しない組み合わせを列挙しました。ここで、斜め方向にも1つしか配置できないように限定操作の追加採用が必要になります。 list5-9 #8王妃問題 pos = [0] * 8 #各列の王妃の位置 flag_a = [False] * 8 #各行に王妃が配置済みか flag_b = [False] * 15 #右上がりの対角線に王妃が配置済みか flag_c = [False] * 15 #右下がりの対角線に王妃が配置済みか def put() -> None: """盤面(各列の王妃の位置)を出力""" for i in range(8): print(f'{pos[i]:2}', end=' ') print() def set(i: int) -> None: """j列目に王妃を位置""" for j in range(8): if ( not flag_a[j] #j行には王妃は未配置 and not flag_b[i + j] #右上がり対角線に王妃は未配置 and not flag_c[i - j + 7]): #右下がり対角線に王妃は未配置 pos[i] = j #王妃をj行に配置 if i == 7: #全列に配置終了 put() else: flag_a[j] = flag_b[i + j] = flag_c[i - j + 7] = True set(i + 1) #次の列に王妃を配置 flag_a[j] = flag_b[i + j] = flag_c[i - j + 7] = False set(0) #0列目に王妃を配置 数字だけだとイメージが付きづらいため盤面に合わせるような改良をしたのが次になります。 #8王妃問題(改) pos = [0] * 8 #各列の王妃の位置 flag_a = [False] * 8 #各行に王妃が配置済みか flag_b = [False] * 15 #右上がりの対角線に王妃が配置済みか flag_c = [False] * 15 #右下がりの対角線に王妃が配置済みか def put() -> None: """盤面を□と■で出力""" for j in range(8): for i in range(8): print('■' if pos[i] == j else '□', end=' ') print() print() def set(i: int) -> None: """j列目に王妃を位置""" for j in range(8): if ( not flag_a[j] #j行には王妃は未配置 and not flag_b[i + j] #右上がり対角線に王妃は未配置 and not flag_c[i - j + 7]): #右下がり対角線に王妃は未配置 pos[i] = j #王妃をj行に配置 if i == 7: #全列に配置終了 put() else: flag_a[j] = flag_b[i + j] = flag_c[i - j + 7] = True set(i + 1) #次の列に王妃を配置 flag_a[j] = flag_b[i + j] = flag_c[i - j + 7] = False set(0) #0列目に王妃を配置 一見すると頭が混乱するのですが、再帰呼び出しをした際どのように処理が行われるかしっかり考えれば何とか理解できそうです。 ただ、ゼロからこういったコードがスラスラ書けるようになるかといわれるとまだまだ先は長そうです…。 これで、第5章が終了です。 ありがとうございました。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Python実践]argparseに挑戦してみよう!《add_subparsers編》

はじめに 今回はPythonの「argparse」の基礎と「ArgumentParser」の使い方について解説していこうと思います! 第1弾はこちら! 第2弾はこちら! 「argparse」はコマンドライン引数を渡してくれるもので、Pythonエンジニアを目指している方は知っておいた方が良いものになります。 ぜひこの記事でマスターしていってください! この記事はPythonの公式ドキュメントを参考にしています。 Pythonに限らず、プログラミング言語の公式ドキュメントは非常に読みにくいので、この記事でわかりやすく解説していきます。 「argparse使ってみたい」 「コマンドライン引数を渡してみたい」 「argparseって何?」 「自作モジュール作ってみたい」 「他の人が書いたコードでargparseの部分がわからない」 このような人のお役に立てれば幸いです。 それでは早速本題に入っていきましょう! こちらにまとめてあります
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Python実践]argparseに挑戦してみよう!《add_argument編》

はじめに 今回はPythonの「argparse」の基礎と「add_argument」の使い方について解説していこうと思います! 第1弾はこちら! 第3弾はこちら! 「argparse」はコマンドライン引数を渡してくれるもので、Pythonエンジニアを目指している方は知っておいた方が良いものになります。 ぜひこの記事でマスターしていってください! この記事はPythonの公式ドキュメントを参考にしています。 Pythonに限らず、プログラミング言語の公式ドキュメントは非常に読みにくいので、この記事でわかりやすく解説していきます。 「argparse使ってみたい」 「コマンドライン引数を渡してみたい」 「argparseって何?」 「自作モジュール作ってみたい」 「他の人が書いたコードでargparseの部分がわからない」 このような人のお役に立てれば幸いです。 それでは早速本題に入っていきましょう! 記事はこちらにまとめてあります
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Python実践]argparseに挑戦!《ArgumentParser編》

はじめに 今回はPythonの「argparse」の基礎と「ArgumentParser」の使い方について解説していこうと思います! 第1弾はこちら! 第3弾はこちら! 「argparse」コマンドライン引数を渡してくれるもので、Pythonエンジニアを目指している方は知っておいた方が良いものになります。 ぜひこの記事でマスターしていってください! この記事はPythonの公式ドキュメントを参考にしています。 Pythonに限らず、プログラミング言語の公式ドキュメントは非常に読みにくいので、この記事でわかりやすく解説していきます。 「argparse使ってみたい」 「コマンドライン引数を渡してみたい」 「argparseって何?」 「自作モジュール作ってみたい」 このような人のお役に立てれば幸いです。 それでは早速本題に入っていきましょう! 入門Python 記事はこちらにまとめてあります。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

プログラミング言語じゃなくて、プログラミングの基礎を学びたい

教科書のプログラムを丸写ししていませんか? 「図形と数の並びで学ぶプログラミング基礎」発売しました!#プロラミング #Python #Scratch #ヘネ本 #漫画 #プログラミング初心者 #プログラミング独学 pic.twitter.com/OsTkxYCzT8— 図形と数の並びで学ぶプログラミング基礎 by 竹中要一&熊野ヘネ (@ROzWik1TwBBEgwl) February 25, 2022 漫画中の男の子みたいな初学者はいませんか? 「プログラムに何が書かれているか考えなさい」と言われても、そもそも、何をどうすればいいのかさっぱりわからない。 そんなプログラミング初学者のために本を書きました。 図形と数の並びで学ぶプログラミング基礎 技術評論社のサイト 【新刊】2022年2月28日発売『図形と数の並びで学ぶプログラミング基礎』本体2,280円+税、竹中要一/熊野ヘネ 著 『数学』が苦手な人でも大丈夫! https://t.co/qdWoVWHlPh pic.twitter.com/3wm68kR3nh— 技術評論社販売促進部 (@gihyo_hansoku) February 16, 2022 プログラミングの授業で学生さんを見ていると、教科書の写経をしているだけで、プログラムの中身に頓着しない方を多く見ます。 そのような人向けに、とてーも優しい部分からプログラムの基礎を説明しています。 プログラミングの基礎ってなんだろう プログラミングを学ぼうと思います。どうすればいいですか? このように質問すると、「今ならPython でしょ」とか、「C言語を学ぶべきだ」という回答が返ってくるかと思います。 でも、その回答はすこし外れています。 Python は比較的優しく学習できるし、C言語はとっても重要です。 でも、質問している人はC言語のint型とか、pythonで関数定義をする方法を知りたいのでしょうか。 もちろん、そういう人もいるかと思います。 しかし、技術者でない人、管理職の人、そしてプログラミング言語初学者の人が発する質問だとしたら、的が外れています。 プログラミング言語に寄らないプログラミングの知識というものがあるはずです。それを学んでもらいたいと思っています。 何が学べるの? 漫画に挙げた問題の(1)は簡単に解けると思います。 答は✚ですね。 では、どうして答は✚なのですか? この理由を説明できる事は、プログラムを考える事につながります。 では、問題(2)から(4)はどうでしょう? 問題(2)は暗黙知を学ぶ問題の一つです。  暗黙知とは、経験的に知っているけど、上手く言葉で説明できない知識です。 こんなの知っていて当たり前でしょ?と思っている知識も暗黙知です。 問題(3)(4)は模範的な回答例だけお伝えし、問題の意図は秘密にしておきます。 (3)お日様マーク (4)0 :数字のゼロ 模範的なと強調したのには理由があります。 (3)の答えは雪でもいいし、(4)の答はどんな数字でもよいからです。 なぜでしょうか? その理由が気になる方は本書を手に取ってもらえると嬉しいです。 他に学べること この本に含まれる学習項目を示します。 100点満点のプログラムを作成することは無理ゲー 暗黙知 数列と漸化式 自己再帰関数 プログラミング言語の教科書ではない、と上述しました。 しかし、プログラムの基礎知識とプログラム言語は無関係ではありません。 基礎知識と言語とのギャップを埋める助けになればいいなと思い、 Python と Scratch のコードも載せています。 大学で本書はどのように使われるの? 本書は、関西大学総合情報学部1年生500名の必修科目「データサイエンス基礎」の教科書です。 全15回90分授業の5回分に相当します。 ヘネ本って呼んでね
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[py2rb] 多重継承の6

はじめに 移植やってます。 ( from python 3.7 to ruby 2.7 ) 多重継承 (Python) 前回の記事のコメントにて、@Nabetani さんよりRuby3.1ではクラス変数のオーバーライドがエラーになることを教えていただきました。 また、クラス変数について興味深い記事を投稿されています。 失敗(Ruby) module Base def __init__ @name = 'Base' @base = 'Base' puts 'Base' end end module C4 include Base def __init__ @name = 'C4' puts 'C4' super end end module C2 include C4 def __init__ @name = 'C2' puts 'C2' super end end module C3 include Base def __init__ @name = 'C3' puts 'C3' super end end class C1 include C2, C3 attr_reader :name, :base def initialize __init__ end def __init__ @name = 'C1' puts 'C1' super end end c = C1.new p [c.name, c.base] p C1.ancestors # output C1 C2 C4 C3 Base ["Base", "Base"] [C1, C2, C4, C3, Base, Object, Kernel, BasicObject] 継承チェーンでBaseがC3の後から呼ばれています、よしよし。 クラス変数からインスタンス変数に替えましたが、@nameが'Base'で書き換えられています、ぐぬぬ。 成功(Ruby) module Base def __init__ @name = 'Base' @base = 'Base' puts 'Base' end end module C4 include Base def __init__ puts 'C4' super @name = 'C4' end end module C2 include C4 def __init__ puts 'C2' super @name = 'C2' end end module C3 include Base def __init__ puts 'C3' super @name = 'C3' end end class C1 include C2, C3 attr_reader :name, :base def initialize __init__ end def __init__ puts 'C1' super @name = 'C1' end end c = C1.new p [c.name, c.base] p C1.ancestors # output C1 C2 C4 C3 Base ["C1", "Base"] [C1, C2, C4, C3, Base, Object, Kernel, BasicObject] superの後でインスタンス変数を初期化することにより、正しいオーバーライドになるようです。 実験 module C4 include Base def __init__ puts 'C4' - super @name = 'C4' end end # output C1 C2 C4 ["C1", nil] [C1, C2, C4, C3, Base, Object, Kernel, BasicObject] 仮に、成功(Ruby)のコードで、module C4のsuperを削除した場合、継承チェーンのC4の次のC3のdef __init__が呼ばれないためputs 'C3'が実行されないことが分かります。 また、その結果Baseのdef __init__も実行されないため、@baseが初期化されていないこともわかります。 メモ Python の 多重継承の6 を学習した 百里を行く者は九十里を半ばとす
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Python リストやタプルを複数の範囲で指定したい!

はじめに テストでassertでexpectedとresultを比較するときに、resultに動的な値があるためテストしづらかったので、静的な値のみを取り出すために考えた (静,静,静,静,静,静,静,静,静) (静,静,静,静,動,静,静,静,静,静) import itertools import copy # 5番目以外の要素がほしい ls = [0,1,2,3,4,5,6,7,8,9,10] genr = itertools.chain(range(0,5),range(6,11)) # ジェネレータの中身 print(list(copy.deepcopy(genr))) # 取得結果 print([ls[i] for i in itr]) [0, 1, 2, 3, 4, 6, 7, 8, 9, 10] [0, 1, 2, 3, 4, 6, 7, 8, 9, 10] 感想 うわっ、静、連続して書いたら気持ち悪くなった 静って字、こんなに難しい字だった?
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Pyodideを使ってnwdiag(blockdiag)をクライアントサイドで動かす

Pyodide ブラウザ上でPythonを動かすという試みにはPyPy.jsやBrythonといったものが以前からある。Pyodideは後発ではあるもののpipの簡易実装であるmicropipが用意されていて、PyPI上のパッケージを(ものによっては)直接インストールできるところが画期的。 nwdiag nwdiag(やblockdiag)は、PlantUMLやGraphvizなどと同じく、テキストで定義して図を描くためのツール。この手のツールではMermaidの記法が最近GitHubでサポートされて話題になった。nwdiagはそんなテキストで定義して図を描くツールの中でも特にネットワーク構成図に特化したもの。 REPLで試す Pyodideのサイトにはデモ用のREPLが用意されており、WASMに対応したブラウザさえあれば手軽に試すことができる。今回はそれを使ってnwdiagを動かしてみる。 パッケージのインストール パッケージのインストールはpipで指定するのと同じ名前をmicropipに指定するだけ。 Welcome to the Pyodide terminal emulator ? Python 3.9.5 (default, Feb 22 2022 14:12:02) on WebAssembly VM Type "help", "copyright", "credits" or "license" for more information. >>> import micropip >>> await micropip.install('nwdiag') >>> あっけなく完了。 REPLから実行する準備 もともとnwdiagはCLI上から、 $ nwdiag -Tsvg simple.diag と実行するように設計されているため、REPL上で実行するためにはnwdiagコマンドが内部的に呼び出している処理を調べて真似る必要がある。 コマンド周りのソースを見てみると、command.pyのmain関数をCLIで渡す引数と同じものを配列で渡して呼び出せば良さそうだ。 command.pyをimportする。 >>> import nwdiag.command >>> とりあえず、何事もなくimportできた。 REPLから実行 早速、何かdiagファイルを用意して変換を試したいが、REPL環境に取り込むのが手間なので、とりあえずパッケージに含まれているテスト用のファイルで試すことにする。 Pyodideにはemscriptenによって用意された仮想的なファイルシステムが備わっており、そのファイルシステムの/lib/python3.9/site-packages/nwdiag/tests/diagrams以下に、GitHubのこの辺りのファイルが存在している。どれでもいいが、node_belongs_to_multiple_networks.diagで試してみる。 >>> nwdiag.command.main(["-Tsvg","/lib/python3.9/site-packages/nwdiag/tests/diagrams/node_belongs_to_multiple_networks.diag"]) ERROR: unknown format: SVG -1 >>> 動かない。。。 不具合箇所の特定 エラーメッセージを手がかりにnwdiag(と描画を担っているblockdiag)のソースを追ってみると、依存パッケージが足りていないようだ。 インストール済みのパッケージはmicropip.list()で確認できる。 >>> micropip.list() Name | Version | Source ------------- | ------- | ------- webcolors | 1.11.1 | pypi funcparserlib | 1.0.0a0 | pypi blockdiag | 3.0.0 | pypi nwdiag | 3.0.0 | pypi setuptools | 60.3.1 | pyodide pillow | 9.0.0 | pyodide distutils | 1.0 | pyodide micropip | 0.1 | pyodide pyparsing | 3.0.6 | pyodide packaging | 21.3 | pyodide >>> 足りているように見える。。。 さらにソースを読み進んで依存パッケージの有無をどのように判定しているのかまで追ってみる。 この辺りだ。 https://github.com/blockdiag/blockdiag/blob/master/src/blockdiag/imagedraw/__init__.py __init__.py def init_imagedrawers(debug=False): for drawer in pkg_resources.iter_entry_points('blockdiag_imagedrawers'): try: module = drawer.load() if hasattr(module, 'setup'): module.setup(module) except Exception as exc: if debug: warning('Failed to load %s: %r' % (drawer.module_name, exc)) pkg_resourcesが管理しているパッケージ情報がどうなっているのか確認してみる必要がありそうだ。 >>> import pkg_resources >>> pkg_resources.working_set.by_key {'tzdata': tzdata 2021.5 (/lib/python3.9), 'setuptools': setuptools 60.3.1 (/lib/python3.9/site-packages), 'packaging': packaging 21.3 (/lib/python3.9/site-packages), 'unknown': UNKNOWN 9.0.0 (/lib/python3.9/site-packages), 'pyparsing': pyparsing 3.0.6 (/lib/p ython3.9/site-packages), 'blockdiag': blockdiag 3.0.0 (/lib/python3.9/site-packages), 'nwdiag': nwdiag 3.0.0 (/lib/python3.9/site- packages), 'webcolors': webcolors 1.11.1 (/lib/python3.9/site-packages), 'funcparserlib': funcparserlib 1.0.0a0 (/lib/python3.9/si te-packages), 'micropip': micropip 0.1 (/lib/python3.9/site-packages)} >>> 'unknown': UNKNOWN 9.0.0って何だ? micropipの出力と比較してみると9.0.0というバージョン番号からみてpillowで間違いなさそうだ。(画像関係のパッケージだし) 'pillow': pillow 9.0.0のようになるべきところ、何かの不具合でunknownとなってしまっているのだろう。 不具合の回避 パッケージ情報を修正してみる。 >>> for dist in pkg_resources.distributions_from_metadata('/lib/python3.9/site-packages/Pillow-9.0.0.dist-info'): ... pkg_resources.working_set.by_key['pillow'] = dist ... >>> pkg_resources.working_set.by_key {'tzdata': tzdata 2021.5 (/lib/python3.9), 'setuptools': setuptools 60.3.1 (/lib/python3.9/site-packages), 'packaging': packaging 21.3 (/lib/python3.9/site-packages), 'unknown': UNKNOWN 9.0.0 (/lib/python3.9/site-packages), 'pyparsing': pyparsing 3.0.6 (/lib/p ython3.9/site-packages), 'blockdiag': blockdiag 3.0.0 (/lib/python3.9/site-packages), 'nwdiag': nwdiag 3.0.0 (/lib/python3.9/site- packages), 'webcolors': webcolors 1.11.1 (/lib/python3.9/site-packages), 'funcparserlib': funcparserlib 1.0.0a0 (/lib/python3.9/si te-packages), 'micropip': micropip 0.1 (/lib/python3.9/site-packages), 'pillow': Pillow 9.0.0 (/lib/python3.9/site-packages)} >>> 'unknown'というキーの存在はそのままだが、無事末尾に'pillow'が追加されている。 再チャレンジ 再度、node_belongs_to_multiple_networks.diagの変換を試みる。 >>> nwdiag.command.main(["-Tsvg","/lib/python3.9/site-packages/nwdiag/tests/diagrams/node_belongs_to_multiple_networks.diag"]) 0 >>> 無事に変換できたようだ。 変換元のdiagファイルと同じディレクトリ配下に拡張子svgのファイルが生成されているはずなので読み出してみる。 >>> svgfile = open("/lib/python3.9/site-packages/nwdiag/tests/diagrams/node_belongs_to_multiple_networks.svg","r") >>> svg = svgfile.read() >>> print(svg) <?xml version='1.0' encoding='UTF-8'?> <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN" "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd"> <svg viewBox="0 0 304 444" xmlns="http://www.w3.org/2000/svg" xmlns:inkspace="http://www.inkscape.org/namespaces/inkscape" xmlns:x link="http://www.w3.org/1999/xlink"> <defs id="defs_block"> <filter height="1.504" id="filter_blur" inkspace:collect="always" width="1.1575" x="-0.07875" y="-0.252"> <feGaussianBlur id="feGaussianBlur3780" inkspace:collect="always" stdDeviation="4.2" /> </filter> </defs> <title>blockdiag</title> <desc>{ network { A; } network { A; } network { A; } } </desc> <rect fill="rgb(0,0,0)" height="40" stroke="rgb(0,0,0)" style="filter:url(#filter_blur);opacity:0.7;fill-opacity:1" width="104" x="155" y="162" /> <path d="M 131 103 L 283 103 A2,4 0 0 1 283 111 L 131 111 A2,4 0 0 1 131 103" fill="rgb(0,0,0)" style="filter:url(#filter_blur)" /> <path d="M 131 247 L 283 247 A2,4 0 0 1 283 255 L 131 255 A2,4 0 0 1 131 247" fill="rgb(0,0,0)" style="filter:url(#filter_blur)" /> <path d="M 131 391 L 283 391 A2,4 0 0 1 283 399 L 131 399 A2,4 0 0 1 131 391" fill="rgb(0,0,0)" style="filter:url(#filter_blur)" /> <path d="M 128 100 L 280 100 A2,4 0 0 1 280 108 L 128 108 A2,4 0 0 1 128 100" fill="rgb(185,203,228)" stroke="rgb(0,0,0)" /> <path d="M 280 108 A2,4 0 0 1 280 100" fill="none" stroke="rgb(0,0,0)" /> <path d="M 128 100 L 280 100" fill="none" stroke="none" /> <path d="M 204 35 L 204 100" fill="none" stroke="rgb(0,0,0)" /> <path d="M 128 244 L 280 244 A2,4 0 0 1 280 252 L 128 252 A2,4 0 0 1 128 244" fill="rgb(185,203,228)" stroke="rgb(0,0,0)" /> <path d="M 280 252 A2,4 0 0 1 280 244" fill="none" stroke="rgb(0,0,0)" /> <path d="M 128 244 L 280 244" fill="none" stroke="none" /> <path d="M 128 388 L 280 388 A2,4 0 0 1 280 396 L 128 396 A2,4 0 0 1 128 388" fill="rgb(185,203,228)" stroke="rgb(0,0,0)" /> <path d="M 280 396 A2,4 0 0 1 280 388" fill="none" stroke="rgb(0,0,0)" /> <path d="M 128 388 L 280 388" fill="none" stroke="none" /> <path d="M 204 108 L 204 156" fill="none" stroke="rgb(0,0,0)" /> <path d="M 212 196 L 212 244" fill="none" stroke="rgb(0,0,0)" /> <path d="M 196 196 L 196 240" fill="none" stroke="rgb(0,0,0)" /> <path d="M 196.0 240.0 A8.0,8.0 0 0 1 196.0 256.0" fill="none" stroke="rgb(0,0,0)" /> <path d="M 196 256 L 196 388" fill="none" stroke="rgb(0,0,0)" /> <rect fill="rgb(255,255,255)" height="40" stroke="rgb(0,0,0)" width="104" x="152" y="156" /> <text fill="rgb(0,0,0)" font-family="sans-serif" font-size="11" font-style="normal" font-weight="normal" text-anchor="middle" te xtLength="6" x="204.0" y="182">A</text> </svg> >>>
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Python】DockerでOpenCVをインストールする時の設定

はじまり DockerでPython用のコンテナをビルドする時に OpenCVも一緒にインストールしたら躓いたので、備忘録。 最初に躓いたイメージ まず、このイメージでビルドししてopencvを利用しているプログラムを実行しました。 FROM ubuntu:latest ENV PYTHON_VERSION 3.7.1 ENV HOME /root ENV PYTHON_ROOT $HOME/local/python-$PYTHON_VERSION ENV PATH $PYTHON_ROOT/bin:$PATH ENV PYENV_ROOT $HOME/.pyenv RUN apt update RUN apt install -y python3 python3-pip RUN pip install --upgrade pip WORKDIR /usr/src/app COPY ./ /usr/src/app RUN pip install opencv-python そうしたら、こんなエラーが出現。 # python3 app.py ------------------------------------------------------------------------------------ Traceback (most recent call last): File "app.py", line 4, in <module> import cv2 File "/usr/local/lib/python3.8/dist-packages/cv2/__init__.py", line 8, in <module> from .cv2 import * ImportError: libGL.so.1: cannot open shared object file: No such file or directory ------------------------------------------------------------------------------------ 設定その1:モジュール追加その1、そして再度躓く どうやら、OpenCVの実行に必要なモジュールをaptで取ってくる必要があるらしい。 FROM ubuntu:latest ENV PYTHON_VERSION 3.7.1 ENV HOME /root ENV PYTHON_ROOT $HOME/local/python-$PYTHON_VERSION ENV PATH $PYTHON_ROOT/bin:$PATH ENV PYENV_ROOT $HOME/.pyenv RUN apt update # ↓追加↓ RUN apt-get install -y libgl1-mesa-dev RUN apt install -y python3 python3-pip RUN pip install --upgrade pip WORKDIR /usr/src/app COPY ./ /usr/src/app RUN pip install opencv-python しかぁし、またもや躓く。 ------------------------------------------------------------------------------------ Traceback (most recent call last): File "app.py", line 4, in <module> import cv2 File "/usr/local/lib/python3.8/dist-packages/cv2/__init__.py", line 8, in <module> from .cv2 import * ImportError: libgthread-2.0.so.0: cannot open shared object file: No such file or directory ------------------------------------------------------------------------------------ 設定その2:モジュール追加その2、そして再度躓く どうやら、OpenCVの実行に必要な別のモジュールをaptで取ってくる必要があるらしい。 FROM ubuntu:latest ENV PYTHON_VERSION 3.7.1 ENV HOME /root ENV PYTHON_ROOT $HOME/local/python-$PYTHON_VERSION ENV PATH $PYTHON_ROOT/bin:$PATH ENV PYENV_ROOT $HOME/.pyenv RUN apt update # - ↓削除↓ RUN apt-get install -y libgl1-mesa-dev # + ↓追加↓ RUN apt install -y libopencv-dev RUN apt install -y python3 python3-pip RUN pip install --upgrade pip WORKDIR /usr/src/app COPY ./ /usr/src/app RUN pip install opencv-python しかし、ビルドを始めて近くのコンビニに行って帰ってきたら、、、 ----------------------------------------------------------------------------------------------------------- => [3/8] RUN apt install -y libopencv-dev 833.9s => => # questions will narrow this down by presenting a list of cities, representing => => # the time zones in which they are located. => => # 1. Africa 4. Australia 7. Atlantic 10. Pacific 13. Etc => => # 2. America 5. Arctic 8. Europe 11. SystemV => => # 3. Antarctica 6. Asia 9. Indian 12. US => => # Geographic area: ----------------------------------------------------------------------------------------------------------- タイムゾーンの選択を愚直に待ち続けていたのでした・・・ 設定その3:タイムゾーンを設定する サーバのタイムゾーンを設定していないことで発生する模様。以下で追加して対処。 RUN ln -sf /usr/share/zoneinfo/Asia/Tokyo /etc/localtime そしたら、無事Pythonプログラムが走りました。 最終的にビルドできたイメージ FROM ubuntu:latest ENV PYTHON_VERSION 3.7.1 ENV HOME /root ENV PYTHON_ROOT $HOME/local/python-$PYTHON_VERSION ENV PATH $PYTHON_ROOT/bin:$PATH ENV PYENV_ROOT $HOME/.pyenv # タイムゾーン RUN ln -sf /usr/share/zoneinfo/Asia/Tokyo /etc/localtime # apt RUN apt update RUN apt install -y libopencv-dev # install python and pip RUN apt install -y python3 python3-pip RUN pip install --upgrade pip # set working directory and copy files WORKDIR /usr/src/app COPY ./ /usr/src/app # install opencv RUN pip install opencv-python おしまい タイムゾーン設定は忘れないようにしなければですね。いい勉強になりました。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Tkinterで画像ビューワを作る 第3回

Tkinterで画像ビューワを作る 続き 前々回、前回作成した画像ビューワを更に改良していきます 今回は 全画面表示 ファイルの削除 の二つをやっていきます 全画面表示 アプリケーションを全画面表示にします windowsではroot.attributesで'-fullscreen',Ubuntuでは'-zoomed'で更新できます Windows全画面 root.attributes('-fullscreen', False) Ubuntu全画面 root.attributes('-zoomed', True) windowsしか持っていないので、以後windowsのみ説明します attributes('-fullscreen')は二番目の引数なしで呼ぶと現在の状態が取得できるので 現在の状態を保存しておく用の変数が要らないです 全画面 if root.attributes('-fullscreen'): # 全画面表示のとき root.attributes('-fullscreen', False) # 全画面表示解除 else: root.attributes('-fullscreen', True) # 全画面表示にする 基本的にはこれで動くのですが、私の環境だと全画面を戻した後ウィンドウが最前面に固定されてしまいました。なので全画面表示を解除したときに最前面も解除することにします 普通は呼ぶ必要ないですが呼んでも特に害は無いのでそのまま載せておきます 最前面解除 root.attributes('-topmost', False) # 最前面解除 さて画面は最大化しましたが上の方に出ている読込ボタンとかが邪魔なので 最大化したときにこれを除去することにしましょう packで設置したフレームはpack_forget()で表示から消し 再度packすることで表示できます ただしpackは先にあるモノの後にどんどん積んで表示されるので 今表示されている描画用フレームより前に挿入する必要があります      fig.普通にpackするだけだと位置が変わってしまう 先にpackしてあるものより前に挿入するには以下のようにします frame_2の前にframe_1を挿入 frame_1.pack(fill=tkinter.X, before=frame_2) 後はこれをボタンなりショートカットキーで呼び出すだけです せっかくなので他のアプリに合わせてF11キーに割り当てましょう rootに新しくbindしても良いのですが前回左右キーを取得するためにをbind済みなので このkey_func関数内に継ぎ足していきましょう 全画面表示機能追加 root.bind("<KeyPress>", self.key_func) def key_func(self, event): # 前略 if event.keysym == "F11": if self.root.attributes('-fullscreen'): # 全画面の時 self.root.attributes('-fullscreen', False) # 全画面解除 self.root.attributes('-topmost', False) # 最上位解除 self.frame_c.pack(fill=tkinter.X, before=self.frame_b) # 上部フレームを再表示 else: self.root.attributes('-fullscreen', True) # 全画面 self.frame_c.pack_forget() # 上部フレームを除去 elif event.keysym == "Escape": if self.root.attributes('-fullscreen'): # 全画面の時 self.root.attributes('-fullscreen', False) # 全画面解除 self.root.attributes('-topmost', False) # 最上位解除 self.frame_c.pack(fill=tkinter.X, before=self.frame_b) # 上部フレームを再表示 ファイルの削除 ファイルの削除はos.remove()で出来ますがこれでそのまま削除すると間違ったときに取り返しがつかなくなるので、通常はゴミ箱に移動させます ゴミ箱に移動はsend2trashを使えば一発で出来ます ファイルをゴミ箱に入れる import send2trash send2trash.send2trash('kari.jpg') send2trashは標準モジュールではないのでpipなりcondaなりでインストールしてください ですが私の環境でpipのインストールがなんかうまくいかなかったのでwin32apiでゴミ箱に移動させてみました。当然windows専用になります ファイルをゴミ箱に入れる win32api import ctypes FO_DELETE = 0x0003 # 削除 FOF_ALLOWUNDO = 0x0040 # ごみ箱に入れる class SHFILEOPSTRUCT(ctypes.Structure): _fields_ = [("hwnd", ctypes.c_void_p), ("wFunc", ctypes.c_int32), ("pFrom", ctypes.c_wchar_p), ("pTo", ctypes.c_wchar_p), ("fFlags", ctypes.c_ushort), ("fAnyOperationsAborted", ctypes.c_bool), ("lpszProgressTitle", ctypes.c_wchar_p) ] def send_trash_exec(file_path): file_path_del = file_path + "\x00\x00" # ヌル文字二個追加 shell32 = ctypes.WinDLL("shell32") shell32.SHFileOperationW.restype = ctypes.c_int32 shell32.SHFileOperationW.argtypes = (ctypes.POINTER(SHFILEOPSTRUCT),) shfs = SHFILEOPSTRUCT() shfs.hwnd = None shfs.wFunc = FO_DELETE shfs.pFrom = file_path_del shfs.pTo = None shfs.fFlags = FOF_ALLOWUNDO shfs.lpszProgressTitle = None shell32.SHFileOperationW(shfs) こんなことするよりsend2trashモジュールをインストールした方が早くて確実です おすすめはしません。標準以外のモジュールを使いたくなって人はたまに居ますが、そもそもすでにpillowが無いと動かないですしね ではこれを前回までのコードに組み込んでいきます デフォルトコンストラクタがかなり長くなってきたのでインスタンス変数の初期化とウィンドウの初期設定部分を分けます。以下最終コード TkinterTestClass # -*- coding:utf-8 -*- import os import ctypes import tkinter import tkinter.filedialog from PIL import Image, ImageTk, ImageOps FO_DELETE = 0x0003 # SHFILEOPSTRUCT構造体用定数 削除 FOF_ALLOWUNDO = 0x0040 # SHFILEOPSTRUCT構造体用定数 ごみ箱に入れる PICTURE_DIR = os.getenv("HOMEDRIVE") + os.getenv("HOMEPATH") + "\\Pictures" # マイピクチャ SETTING_FILE = "setting.txt" # win32api SHFileOperationW用構造体の定義 class SHFILEOPSTRUCT(ctypes.Structure): _fields_ = [("hwnd", ctypes.c_void_p), ("wFunc", ctypes.c_int32), ("pFrom", ctypes.c_wchar_p), ("pTo", ctypes.c_wchar_p), ("fFlags", ctypes.c_ushort), ("fAnyOperationsAborted", ctypes.c_bool), ("lpszProgressTitle", ctypes.c_wchar_p) ] class TkinterTestClass: def __init__(self): self.pre_info = [] # setting.txtの中身 self.root = None # メインウィンドウ self.string_v = None # ラベルの文字 self.frame_c = None # フレーム コントロールパネル self.frame_b = None # フレーム 画像描画部 self.img = None self.img_pil = None self.picture_width = 0 self.picture_height = 0 self.file_array = [] # 画像ファイル配列 self.file_no = 0 # 表示しているファイル self.init() # 初期設定 def init(self): self.settingfile_read() # 前回保存データ読み込み geometry = "500x350" # 画面サイズ初期値 if len(self.pre_info) > 0: # 前回データの読み込みに成功したとき geometry = self.pre_info[0] self.root = tkinter.Tk() self.root.title("無題") self.root.geometry(geometry) self.frame_c = tkinter.Frame(self.root) # 制御エリア self.frame_b = tkinter.Frame(self.root) # 画像描画エリア # ファイル読み込みボタン button = tkinter.Button(self.frame_c, font=("メイリオ", "10", "bold"), text="読込", command=lambda: [self.fg()]) button.pack(side=tkinter.LEFT) # ラベル制御用StringVar self.string_v = tkinter.StringVar() self.string_v.set("") # ファイル名表示用ラベル label = tkinter.Label(self.frame_c, textvariable=self.string_v, bg="white") label.pack(side=tkinter.LEFT, expand=True, fill=tkinter.BOTH) self.frame_c.pack(fill=tkinter.X) self.frame_b.pack(expand=True, fill=tkinter.BOTH, padx=1, pady=1) # 画像表示用キャンバス作成 self.canvas = tkinter.Canvas(self.frame_b, bg="white") self.canvas_img = self.canvas.create_image(0, 0, anchor=tkinter.NW) self.canvas.pack(expand=True, fill=tkinter.BOTH) # キャンバス描画 if len(self.pre_info) > 1: # 前回データの読み込みに成功したとき file_path = self.pre_info[1] self.image_change(file_path) # 画像を変更 self.create_file_list(file_path) # ディレクトリのファイル一覧を取得 self.string_v.set(file_path) # ラベルにファイル名を表示 self.root.bind('<Configure>', self.resize_root) # rootの大きさや位置を変更したときのイベント self.root.bind("<KeyPress>", self.key_func) self.root.protocol("WM_DELETE_WINDOW", self.settingfile_write) self.root.mainloop() # フォルダ内の画像ファイルリストを作成 def create_file_list(self, file_path): self.file_array = [] # 画像ファイル配列 tmp_arr = os.path.split(file_path) # ファイルのパスをディレクトリとファイル名に分解 dir_name = tmp_arr[0] # ディレクトリ file_name = tmp_arr[1] # ファイル名 n = 0 for fname in os.listdir(dir_name): # ディレクトリ内のファイル一覧を取得 file_ext = os.path.splitext(fname)[1].lower() # 拡張子を小文字にして取得 if file_ext == ".jpg" or file_ext == ".png" or file_ext == ".tif" or file_ext == ".jpeg": self.file_array.append(os.path.join(dir_name, fname)) # 画像ファイルのとき、配列に格納 if file_name == fname: self.file_no = n # 開いたファイルの配列番号を残しておく n += 1 # 画像変更 def image_change(self, file_path): # キャンバスサイズ取得 self.check_canvas_size() try: # self.img = tkinter.PhotoImage(file=file_path)# ファイルを読み込んでPhotoImageオブジェクトを作成 self.img_pil = Image.open(file_path) # PhotoImageオブジェクト作成をpillowで行う if self.picture_width == 0 or self.picture_height == 0: self.img = ImageTk.PhotoImage(image=self.img_pil) else: img_pil2 = ImageOps.pad(self.img_pil, (self.picture_width, self.picture_height)) self.img = ImageTk.PhotoImage(image=img_pil2) self.canvas.itemconfig(self.canvas_img, image=self.img) # 画像を変更 self.string_v.set(file_path) # ファイル名をラベルに表示 except: self.img = None # キー押されたとき def key_func(self, event): mode = -1 if event.keysym == "Right": self.next_picture(1) elif event.keysym == "Left": self.next_picture(-1) elif event.keysym == "Escape": if self.root.attributes('-fullscreen'): # 全画面の時 self.root.attributes('-fullscreen', False) # 全画面解除 self.root.attributes('-topmost', False) # 最前面解除 self.frame_c.pack(fill=tkinter.X, before=self.frame_b) # コントロールパネルを復旧 self.frame_c.pack(fill=tkinter.X) elif event.keysym == "F11": if self.root.attributes('-fullscreen'): # 全画面の時 self.root.attributes('-fullscreen', False) # 全画面解除 self.root.attributes('-topmost', False) # 最前面解除 self.frame_c.pack(fill=tkinter.X, before=self.frame_b) # コントロールパネルを復旧 else: self.root.attributes('-fullscreen', True) # 全画面に切り替え self.frame_c.pack_forget() # コントロールパネルを除去 elif event.keysym == "Delete": self.send_trash() else: pass # 次のファイルを表示 # offset: 1:次のファイル # -1:前のファイル def next_picture(self, offset): cnt = len(self.file_array) if cnt == 0: # 画像ファイル配列が空の時何もしない return False file_no = self.file_no file_path = "" while file_path == "": self.file_no += offset if self.file_no == cnt: # 配列上限オーバー時は0に戻る self.file_no = 0 elif self.file_no == -1: # 配列上限オーバー時は0に戻る self.file_no = cnt - 1 file_path = self.file_array[self.file_no] # どのファイルを表示するか決定する if file_no == self.file_no: break if file_path == "": # 配列を一周して全部空だったとき self.file_array = [] self.file_no = 0 self.img_pil = None self.img = None self.canvas.itemconfig(self.canvas_img, image=self.img) return False self.image_change(file_path) # 画像の変更 # ゴミ箱へ送る def send_trash(self): file_path = self.file_array[self.file_no] # send2trash.send2trash(file_path) self.send_trash_exec(file_path) self.file_array[self.file_no] = "" self.next_picture(1) # ファイルをゴミ箱へ送る def send_trash_exec(self, file_path): file_path_del = file_path + "\x00\x00" # ヌル文字二個追加 shell32 = ctypes.WinDLL("shell32") shell32.SHFileOperationW.restype = ctypes.c_int32 shell32.SHFileOperationW.argtypes = (ctypes.POINTER(SHFILEOPSTRUCT),) shfs = SHFILEOPSTRUCT() shfs.hwnd = None shfs.wFunc = FO_DELETE shfs.pFrom = file_path_del shfs.pTo = None shfs.fFlags = FOF_ALLOWUNDO shfs.lpszProgressTitle = None shell32.SHFileOperationW(shfs) # ファイル読み込みダイアログ def fg(self): file_type = [("画像ファイル", "*.jpg;*.png;*.bmp")] file_path = tkinter.filedialog.askopenfilename(filetypes=file_type, initialdir=PICTURE_DIR) # ファイルが選択されていたら画像変更 if file_path != "": self.image_change(file_path) # 画像を変更 self.create_file_list(file_path) # ディレクトリのファイル一覧を取得 # メインウィンドウの大きさを変えたとき def resize_root(self, event): # イメージが無いときは何もしない if self.img_pil is None: return False # サイズを変更する必要があるかチェック if self.check_canvas_size(): # イメージをリサイズ img_pil2 = ImageOps.pad(self.img_pil, (self.picture_width, self.picture_height)) self.img = ImageTk.PhotoImage(image=img_pil2) self.canvas.itemconfig(self.canvas_img, image=self.img) # キャンバスサイズ取得 def check_canvas_size(self): canvas_width = 0 canvas_height = 0 try: canvas_width = self.canvas.winfo_width() canvas_height = self.canvas.winfo_height() except: return False # キャンバスサイズが取得できないときFalse # キャンバスサイズの縦横どちらかが2より小さいならならFalse if canvas_width < 2 or canvas_height < 2: return False # 前回のサイズから変わってなければFalse if canvas_width == self.picture_width and canvas_height == self.picture_height: return False # キャンバスサイズを記憶しておく self.picture_width = canvas_width self.picture_height = canvas_height return True # 設定ファイル読み込み def settingfile_read(self): try: with open(SETTING_FILE) as f: s = f.read() self.pre_info = s.splitlines() except FileNotFoundError: pass # 設定ファイル書き込み def settingfile_write(self): str_tmp = self.root.geometry() + "\n" # メインウィンドウの位置と大きさ if len(self.file_array) > 0: str_tmp += self.file_array[self.file_no] + "\n" with open(SETTING_FILE, mode="w") as f: f.write(str_tmp) self.root.destroy() # ウィンドウを閉じる これを忘れると閉じられなくなる if __name__ == '__main__': TkinterTestClass()
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

AWS Lambda Layersを使ったPythonライブラリーの追加

はじめに Lambda関数でサードパーティのライブラリーを使用したいときは、下記のいずれかの方法を使って、対象のライブラリーを追加する必要がある。 追加したいライブラリーを含んだパッケージデプロイ Lambda Layersを使って追加 今回は「Lambda Layersを使って追加」についてまとめていく。 Lambda Layersを使った追加方法 環境 Windows / Mac Dockerが使えること。 ここではDockerのインストール方法は省略するが、ローカルの場合はDocker Desktopを使うと簡単。 Linuxサーバー等にインストールする場合は下記記事記事を参照。 https://qiita.com/subretu/items/549bc720165004bca3c3 やってみて分かったこと Layerにアップロードするライブラリーは、AmazonLinux環境で作成しないと読み取ってくれないことが分かった。 WindowsやMacのローカル環境で、pipを使ってインストールしたライブラリーをLayerに追加しても正常に読み取ってくれなかった。 他のLinux環境では試していないので、もしかしたらそちらでも可能かもしれない(未検証) そこで、「docker-lambda」というLambda環境を構築できるDockerコンテナを利用してライブラリーを作成することでうまくいったので、次章ではその方法についてまとめていく。 docker-lambdaについては下記を参照。 https://github.com/lambci/docker-lambda https://hub.docker.com/r/lambci/lambda/ docker-lambdaを使ったライブラリーファイルの作成 requirements.txtに必要なライブラリーを記載する。 下記コマンドを実行。 初回だけイメージpullに時間がかかるが、2回目以降はしないので早くなる。 作成するフォルダ名は「python」にしないといけないみたい。 これについては諸説あるみたいだが、「python」とするとうまくいった。 Pythonのバージョンは例として3.8とした。 どのバージョンをサポートしているかは上記に記載したURLを参照。 docker run --rm -v "$(PWD):/var/task" lambci/lambda:build-python3.8 pip install -r ./requirements.txt -t python/lib/python3.8/site-packages/ カレントディレクトリに「python」フォルダができているのでzipファイルにする。 Layerを追加 Lambdaのページを開き、「レイヤー」で追加したいライブラリーを含んだレイヤーを作成する。 zipファイルが10Mを超える場合はS3経由のアップロードになるので注意すること。 レイヤーを作成後は、Lambda関数の「レイヤーを追加」で対象のレイヤーを追加する。 「ARNを指定」を選び、追加したレイヤーのARN貼り付ける。 正常にレイヤー追加されると、下記のように一覧に追加したレイヤーが表示される。 その後は通常通り、import XXXXXでライブラリーをimportして使用することができる。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

クラス変数の持ち主

これは何? クラス変数の持ち主が、 python と ruby で違うことに気がついたので、他の言語どうなってるんだろと思って調査したのでその記録。ジェネリクスとテンプレートを交えながら。 ruby の場合 ruby class B def set_v(x); @@v=x; end def v(); @@v; end end class C < B;end class D < B;end c = C.new.set_v("C#set_v") d = D.new.set_v("D#set_v") p [ C.new.v, D.new.v ] #=> ["D#set_v", "D#set_v"] クラス変数 @@v の持ち主は、 class B 。 C 経由で変更しても、 D から見た @@v の値が変わる。 Python3 の場合 python3 class B: v = "in-class-def" @classmethod def setV(cls,x): cls.v = x class C(B):... class D(B):... print(B().v, C().v, D().v) #=> in-class-def in-class-def in-class-def C().setV("C.setV") D().setV("D.setV") print(B().v, C().v, D().v) #=> in-class-def C.setV D.setV Python のクラス変数はcls.v のようにするので、v の持ち主は cls で指定される型。なので、クラス変数を参照しているメソッドが書かれているクラスとは関係がない。 最初の print 内にある C.v なんかは、C の基底クラスを見に行っている。 わりと珍しい作戦だと思う。 この作戦だと、深い継承ツリーで偶然同じ名前を使ってしまったために大惨事、みたいなことが起きると思う。 ※ 初出時間違ったことを書いてましたすいません。 Java の場合 Generics なしで まずは Generics なしで。 java import java.io.*; class B{ public static String s; } class C1 extends B {}; class C2 extends B {}; class Foo { public static void main (String[] args) throws java.lang.Exception { C1.s = "c1"; C2.s = "c2"; System.out.printf( "C1.s=%s C2.s=%s\n", C1.s, C2.s ); //=> C1.s=c2 C2.s=c2 } } ここは ruby と同じく、クラス変数の持ち主は、その変数が定義されたクラス。 Generics を使うと Generics を使って、型引数を変えてみると java import java.io.*; class B<T>{ public static String s; T dummy; } class C1 extends B<Integer> {}; class C2 extends B<Long> {}; class Foo { public static void main (String[] args) throws java.lang.Exception { C1.s = "c1"; C2.s = "c2"; System.out.printf( "C1.s=%s C2.s=%s\n", C1.s, C2.s ); //=> C1.s=c2 C2.s=c2 } } 異なる型引数でもクラス変数は同じだということがわかる。 なので、型引数の型を static 変数には使えない。なるほどこれが型消去かという感じ。 C++ の場合 テンプレートなし まずはテンプレートなしで。 c++17 #include <iostream> #include <string> struct B{ static inline std::string s; }; struct C1 : public B{}; struct C2 : public B{}; int main(){ C1{}.s = "c1"; C2{}.s = "c2"; std::cout << C1{}.s << " " << C2{}.s << std::endl; //=> c2 c2 return 0; } ruby, java と同様、static変数(クラス変数) の持ち主は、その変数が定義されたクラス。 テンプレートあり テンプレートクラスの場合。 c++17 #include <iostream> #include <string> template<int n> struct B{ static inline std::string s; }; struct C1 : public B<0>{}; struct C2 : public B<1>{}; int main(){ C1{}.s = "c1"; C2{}.s = "c2"; std::cout << C1{}.s << " " << C2{}.s << std::endl; //=> c1 c2 return 0; } テンプレート引数が異なるクラスは別クラスになる。なので、Java と違って static 変数の型にテンプレート引数が使える。 C# の場合 Generics なし C# using System; class B{ public static String s; } class C1 : B{}; class C2 : B{}; public class Test { public static void Main() { C1.s = "c1"; C2.s = "c2"; Console.WriteLine( "C1.s={0} C2.s={1}", C1.s, C2.s); //=> C1.s=c2 C2.s=c2 } } まあそうだよね。Java と同じ。 Generics あり Generics ありだと C# using System; class B<T>{ public static String s;} class C1 : B<int>{}; class C2 : B<long>{}; public class Test { public static void Main() { C1.s = "c1"; C2.s = "c2"; Console.WriteLine( "C1.s={0} C2.s={1}", C1.s, C2.s); //=> C1.s=c1 C2.s=c2 } } Java とは異なる。 まとめ ひとくちに「クラス変数」というけれど、言語によって意味が違うので要注意。 Python はたぶん異端で、明示されているレシーバがそのクラス変数の持ち主。基底クラス内の cls.class_var は、 cls が異なれば異なる変数。cls 指すクラスの別のスーパークラスで同名のクラス変数を使うと同じ変数となる。 ruby, Java, C++, C# は、クラス変数が定義されたクラスがクラス変数の持ち主。 Generics / template を使う場合。 型引数が違っても同じクラス、というのが Java の立場。 template 引数 / 型引数が違うクラスは別のクラス、というのが C++ と C# の立場。 他に試すべき言語あるかなぁ。 余談 今は亡き J# でジェネリクス使ったらどうなるんだろ。 そもそも J# でジェネリクス使えるかどうかも知らないけど。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む