Pythonの参照渡しと(値渡し)
まずはこちらをご覧ください。
Pythonのサンプルコード書いてみました。
class Test: def __init__(self) -> None: self.list_ = [1,2,3,4] self.num = 100 def get_num(self): return self.num def get_list(self): return self.list_ test = Test() num = test.get_num() num = 200 # test.numは...?100 or 200? list_ = test.get_list() list_.append(700) # test.list_に700はいる or いない?
...num
と list_
、 test.num
と test.list_
をprintした時、何が出力されると思いますか?
ちなみに私は、
- num = 200、test.num = 100 - list_ = [1,2,3,4,700]、test.list_ = [1,2,3,4]
だと思ってました 👀
はい、これは、間違いですね ☝️
正解は...
# 出力部分の実装 print(f'test.num = {test.num}' + f'\tアドレス:{id(test.num)}') print(f'num = {num}' + f'\tアドレス:{id(num)}') print('==================================') print(f'test.list_ = {test.list_}' + f'\tアドレス:{id(test.list_)}') print(f'list_ = {list_}' + f'\tアドレス:{id(list_)}') # 結果 test.num = 100 アドレス:4332965200 num = 200 アドレス:4332968400 ================================== test.list_ = [1, 2, 3, 4, 700] アドレス:4334289664 list_ = [1, 2, 3, 4, 700] アドレス:4334289664
id
で各変数のアドレス(格納先)も出力させてみました。
こうすると、なんで num
と list_
で挙動が違うのかが、わかるかと思います。
リストの場合は、どうも参照渡しになっているのです 👀 !
(調べて分かったのですが、 num
も実は参照渡し...後述)
※ アドレスは実行環境によって変わります。
色々調べてみた結果...
Pythonには、値渡しが存在しない 👀 !?
- さっきは返り値
num
に代入していたので気づかなかったのですが、代入しないでアドレスを見ると...一緒でした 👀 タイトルの値渡しを( )にしている理由です
test = Test() num = test.get_num() # 結果 test.num = 100 アドレス:4395519312 num = 100 アドレス:4395519312
- 値が代入される時に、新しいオブジェクト(格納先)が作られて、そこに代入値が入れられるらしい
test.num = 100 アドレス:4332965200 num = 100 アドレス:4332965200 num = 200 の後↓ test.num = 100 アドレス:4332965200 num = 200 アドレス:4332968400 # アドレス違う
型によって挙動が変わるらしい
- イミュータブルな型 → 代入時に新しいオブジェクトが生成される(結果的に値渡しのような挙動になる)
- ミュータブルな型 → 参照渡しになるので、元の値も変わってしまう
- Pythonではどの型がイミュータブルなのかは一覧表↓
Pythonの組み込みデータ型の分類表(ミュータブル等) - ガンマソフト株式会社
Python 3.10ですが、公式ドキュメントにも、ちらっとイミュータブルについて説明が...
3. Data model - Python 3.10.4 documentation
オブジェクトによっては 値 を変更することが可能です。値を変更できるオブジェクトのことを mutable と呼びます。 生成後に値を変更できないオブジェクトのことを immutable と呼びます。 (mutable なオブジェクトへの参照を格納している immutableなコンテナオブジェクトの値は、 その格納しているオブジェクトの値が変化した時に変化しますが、 コンテナがどのオブジェクトを格納しているのかが変化しないのであれば immutable だと考えることができます。 したがって、immutable かどうかは値が変更可能かどうかと完全に一致するわけではありません) オブジェクトが mutable かどうかはその型によって決まります。 例えば、数値型、文字列型とタプル型のインスタンスは immutable で、dict や list は mutable です。
※ コンテナ = 任意の型のデータを複数格納できるデータ型(list, tuple, dict, set)
参考
こちらの記事を主に参考にしました!
参照についてもう少し詳しく ~PythonとJavaを例に~ - Qiita
感想
- Pythonには、値渡しが存在しないというのは知らなかったし、もはや、リストさえも値渡しだと思っていたので、色々間違って理解していたようです。
- これからリストを返り値で返すときは、参照するだけなら良いですが、変えてしまう可能性がある場合(あまり良くはない)は、新しいリストオブジェクトを作成して返すと事故らなそうですね。
react-to-printで詰まったところmemo
何者?
「Web画面をPDFで印刷したい、PDFで出力したい」をReactで実現してくれるライブラリ
https://www.npmjs.com/package/react-to-print
- 基本的に↑の例を見れば、サクッとできる。
- しかもDemoもあるので、参考になる
・・・・が、今回、関数コンポーネントでTypeScriptで書いている場合にサンプルコードだけでは、詰まった部分があったので、メモ
業務で書いたコードを加工してるので、あくまでイメージです。(動作確認してません
同じような境遇になった人の何かのヒントになれば良いなと思います。(あと、将来の自分用に残しておく、多分半年後何も覚えてない。)
(TypeScriptを含め、frontendは全体的に勉強中なので間違えているところもあるかもですが・・・優しい目で見てあげてください 🙇♀️ )
ComponentToPrint の部分
- クラスコンポーネント じゃないとダメそう(後ほど
import React, { Component, useRef } from "react"; class ComponentToPrint extends Component { public render(): JSX.Element { return ( <> <SampleComponent1 /> <- こんな感じで、複数のコンポーネントは並べられる <SampleComponent2 /> </> ); } }
ReactToPrint の部分
- ReactToPrint と ComponentToPrintの順番はどっちでも動きそうです
- ボタンを上に置くか、下に置くか見たいなところ
import React, { useRef } from 'react'; import ReactToPrint from 'react-to-print'; import Button from "@material-ui/core/Button"; import { ComponentToPrint } from './ComponentToPrint'; export const Exsample = () => { const componentRef = useRef(null); return ( <div> <- useRefに認識させるために div で囲う必要がある(と理解) <ComponentToPrint ref={componentRef} /> <- ref ってやつがクラスコンポーネントじゃないと使えないみたい <ReactToPrint trigger={() => ( <Box textAlign="right"> <- material-uiも使えますよ! <Button> 印刷 </Button> </Box> )} content={() => componentRef.current} documentTitle={ここに保存時のファイル名をデフォルトを設定できる} /> </div> ); };
使ってみて分かったこと
ComponentToPrint の部分はクラスコンポーネントにする
- 現状、クラスコンポーネントじゃないとうまく動作しないようです
- 次のメジャーアップデートで、関数コンポーネントにも対応させるとのコメントも見かけましたので、とりあえずクラスコンポーネントにしましょう
- FAQにも書いてありますね、抜粋
Can the ComponentToPrint be a functional component? Officially no, but there are workarounds using the useRef hook. See #96 and #181 for examples. We will officially support this once we release the next major version which will drop React 15 support.
ここら辺のissueにも書いてあるので、ざっと読んでみると良いかと思います。
https://github.com/gregnb/react-to-print/issues/363
頑張れば関数コンポーネントも使えるみたいなコメントもあります↓
https://github.com/gregnb/react-to-print/issues/181#issuecomment-814305408
こちらはHOC(higher-order component; HOC)というReactの応用テクニックらしいですね。
あるコンポーネントを受け取り、新規コンポーネントを返す関数 のことらしいです
https://ja.reactjs.org/docs/higher-order-components.htm
結局分からなかったこと
pageStyle について
- ReactToPrint に渡すと、PDFのページのmarginとかの設定を一括でできるらしいと使ったが、material-uiのデザインが吹っ飛ぶ(反映されなくなる)ので、結局使えなかった
- PDFの周囲に空白入れたかったが断念
改行ページ
- サンプルコードの通りに書いたがうまくいかない(使い方間違えている疑惑)
- https://www.npmjs.com/package/react-to-print の
Page Breaks
のところにサンプルコードもあるので、もう一回ちゃんと読めばできそうな気がする(気のせいかも)
pemとp.12は何が違うのか?
(久しぶりの更新になってしまった・・・) 業務をしていてふと、疑問に思ったのでサクッと調べてみた。
証明書の拡張子の種類
以下のサイトの表がわかりやすい。
どうやら、「エンコードの種類」もしくは「証明書 or 鍵なのか」で拡張子を分けるぽい
エンコードで分ける
- 拡張子の種類としては 「.der」と「.pem」が存在する
- どちらも「ASN.1」というデータ構造で定義している
「ASN.1」は深くは調べないが、データ構造の表現方法の一種なのだなと理解。
https://e-words.jp/w/ASN.1.html
.derについて
- DER(Distinguished Encoding Rules)形式
- 「ASN.1」で表現されたデータを「バイナリデータ化」したもの
- バイナリデータなので、そのままでは読めない(エディタとかでドラッグしても表示されないっぽい)
- 1つのファイルに「1つの証明書」しか含められない
.pemについて
- PEM(Privacy Enhanced Mail)形式
- 「ASN.1」で表現されたデータを「バイナリデータ化」して、さらに、「Base64」で変換したテキストファイル
[memo] Base64 = バイナリデータをテキスト(ascii)データに変換する
- .der形式 から pemファイルが生まれるというわけ🧚♀️
「----BEGIN 」で始まる
メール送信プロトコルであるSMTPでは、バイナリデータを送ることができないので、メールで証明書を送る手段として生まれたのが「PEM」らしい
OpenSSLのデフォルトフォーマットでLinuxでよく使われる(今いるチームのシステムでもpemファイル使ってるな〜
拡張子が「.cer」になることもあるらしいが、Linuxでは「.pem」が使われるとのこと
.pemファイルはエディタとかで開ける、メモ帳でも開ける、そのまま読める
- 1つのファイルに「複数の証明書」を含められる → 複数含める場合としては「中間証明書」をくっつける場合 とかがあるらしい
証明書か鍵かで分ける
- 拡張子の種類としては、「.crt もしくは .cer」、「.key」、「.pfx もしくは .p12」
.crt もしくは .cer
- 中身が証明書(公開鍵もしくは誰かの公開鍵の証明書)の時に使用される拡張子
- Windowsでは「.cer」を、LinuxやApahceでは「.crt」を使うことが多い
- バイナリデータ(DER形式)もしくはテキストデータ(PEM形式)のどちらかの形式になっている
.key
- 中身が秘密鍵の時に使用される拡張子
- バイナリデータ(DER形式)もしくはテキストデータ(PEM形式)のどちらかの形式になっている
.pfx もしくは .p12
- 「PKCS#12形式(Personal Information Exchange)」と呼ばれる(古い名前として「PFX」とも呼ばれる)
- 証明書と証明書に紐づく秘密鍵が1つのファイルにまとめられている形式
- つまり、公開鍵と秘密鍵が一緒に入ってるファイル ☝️
- このファイルにはパスワードがかけられているので、中身を確認するにはパスワードの入力が必須!
- バイナリ形式なので、そのまま読めない
- Windows OSで証明書を使うときは、このフォーマットのことが多いらしい
・・・・・・・ということで、ざっくり調査してみました。
当初のpemとp12の違いは何か理解。
わざわざシステムで使うときにp.12の形式の証明書をpemにしてたのは、中身を確認するためだったのとシステムのOSがLinuxだからなのかな〜という理解 👀
他に参考にしたサイトたち
https://milestone-of-se.nesuke.com/sv-basic/windows-basic/certificate-export-format/
http://tooljp.com/windows/chigai/html/Cipher/PEM-CRT-CER-CERT-KEY-DER-CSR.html
SQLAlchemyでまとめてupsert
きっかけ
SQLAlchmey(ver 1.3)において、まとめてupsert(bulk upsert) するのはどうすればいいのか学んだので、備忘録で書いとこうと思いました。
その前に・・・・
upsertについて
みなさん、upsertって知ってますか?
そんなの基本だと言われるかもしれませんが、なんとなくSQLしてきた人からすると、
selectとinsertとupdateとdeleteくらいしか、実は知らなかったりします。(私だけかな・・・
upsertとは?
upsertとは、対象のデータがDBになければ追加、あれば更新してくれる命令
update と insert を組み合わせて、upsertと呼ばれています
upsertを使うメリット
upsertは select してレコードがあったら、update命令にして、
レコードがなければ、insert命令にするって感じでアプリ側で操作することで実現することも可能
ただし、selectとupdaete もしくは insert の組み合わせでは、
DBに2回SQLの問い合わせが必ず発生する。
それなら1回の問い合わせで済むupsertを使った方が、DBの負担は少ない。
DBへの負荷はなるだけ、少なく済ませるのが、やはり暗黙の了解でしょうか・・・
参考:https://web-academia.org/482/
本題
公式ドキュメントはこちら
https://docs.sqlalchemy.org/en/14/dialects/postgresql.html#insert-on-conflict-upsert
単発、つまり1レコードについての書き方は以下のサイトがわかりやすかったのでこちらを参考にする
https://qiita.com/nsuhara/items/86570f789093222252b1#upsertの実装
上記のサイトをベースに
複数レコードについては、書き方はこんな感じになる(Python)
from sqlalchemy.dialects import postgresql class FeedbackNew(db.Model): __tablename__ = 't_feedback_new' id = db.Column(db.Integer, primary_key=True, autoincrement=True) title = db.Column(db.String(255), nullable=False) def bulk_upsert(record_lists) -> None: # stmt = ステートメント らしい stmt = postgresql.insert(FeedbackNew).values([ dict( id=x.id, title=x.title, ) for x in record_lists ]) do_stmt = stmt.on_conflict_do_update( index_elements=['id'], # set_ に渡せるのはdict型のみなので注意 # 更新対象を渡す set_=dict( title=stmt.excluded.title, ), ) # sessionの渡してるところとかは割愛します # とりあえず、どこかで session.executeするのを忘れずに・・・・ session.execute(do_stmt)
ちなみに・・・
- これは PostgreSQL ver.9.5以降 の機能らしい。
on_conflict_do_update
は、SQLAlchemyが提供するメソッドon_conflict_do_update
は、PostgreSQL固有のinsert()関数内のON CONFLICT をサポートしている- ステートメントは、1つのSQLの命令を指す、(select文, insert文とか)
https://style.potepan.com/articles/26487.html
細かいことは調べてないが、とりあえず、こんな書き方で一気にupsertすることができるらしい 🧚♂️
@singledispatchmethodとは
きっかけ
コードレビュー中に見かけたので、調査
2, 3個の個人ブログやらを経由してやっと理解したので、ここでまとめておきたい
あと前提の知識が必要なやつだったので、一気に理解できると嬉しいよねって気持ち
ちなみに @singledispatchmethod
を理解するにあたり、以下の順番で理解すると良さそう
- ジェネリック関数
singledispatch
singledispatchmethod
←今回学ぶやつ
前置き長いですが、順番に理解する方が意外と近道かもです 🐥
ジェネリック関数
- 「第一引数の型に応じて、異なる処理を行う関数」
ざっくりイメージとしては
def function(obj): if type(obj) == int: return print(f'int型が渡されました,obj={obj}') if type(obj) == str: return print(f'string型が渡されました,obj={obj}') # int型渡す function(1) # string型を渡す function('moji') ===========結果================== int型が渡されました,obj=1 string型が渡されました,obj=moji
こんな感じで引数の型によって function
関数の挙動が変わる
こういうやつをジェネリック関数 というらしい
もっと詳しくの方は、以下を参考にしてください。
https://retroid2016.com/2018/08/09/【pyhton】ジェネリック関数【備忘録】/
singledispatch
- Pythonから提供されているライブラリである「functools」の一部
公式ライブラリは以下ですが、まぁ・・・わからなかった
https://docs.python.org/ja/3/library/functools.html#functools.singledispatch
- ジェネリック関数を定義するためのデコレータ
- つまり、ジェネリック関数を作りたい!ってなったら、関数の先頭につけるデコレータ
- クラスの中にある関数(正しくはメソッド と言いますが)ではない、関数用のデコレータ
ジェネリック関数の説明で書いたソースコードを、singledispatch
で書き換えてみる
from functools import singledispatch @singledispatch def function(obj): <- 元となる関数名 return print(f'{type(obj)}型が渡されました, obj={obj}') # int型がきた時の関数の挙動を宣言 # ちなみに関数名が 「 _ 」なのは、何でもいいよって意図らしいです @function.register <- 元となる関数名.register で定義する def _(obj: int): return print(f'int型が渡されました,obj={obj}') # string型が来たときの関数の挙動を宣言 @function.register def _(obj: str): return print(f'string型が渡されました,obj={obj}') # int型渡す function(1) # string型を渡す function('moji') # list型を渡す function([1, 2, 3]) ===========結果================== int型が渡されました,obj=1 string型が渡されました,obj=moji <class 'list'>型が渡されました, obj=[1, 2, 3]
ちゃんと型によって挙動が変わった・・・!
すごい便利なデコレータですね 👀
では、やっとですが、本来知りたいやついきましょう。
ちなみに、このsingledispatch
がバッチリ理解できた人は、もう singledispatchmethod
も理解したに等しいですので、ご安心を。。。
singledispatchmethod
- 同じく、Pythonから提供されているライブラリである「functools」の一部
まぁ、同じく公式ドキュメント難しい
https://docs.python.org/ja/3/library/functools.html#functools.singledispatchmethod
singledispatch
は関数用でしたが、singledispatchmethod
はメソッド用- つまり、クラスの中とかで定義されたメソッドをジェネリック関数にしたいとなったら、こっちを使いましょう ということ
- そして使い方は、
singledispatch
と一緒です 🙌
singledispatch
で書いたソースコードをクラスの中のメソッドにして、singledispatchmethod
で書き直してみましょう
from functools import singledispatchmethod class Func: @singledispatchmethod def function(self, obj): return print(f'{type(obj)}型が渡されました, obj={obj}') # int型がきた時の関数の挙動を宣言 @function.register def return_int(self, obj: int): <- 今回はメソッド名をつけてみた return print(f'int型が渡されました,obj={obj}') @function.register def return_str(self, obj: str): return print(f'string型が渡されました,obj={obj}') # Funcクラスを作成 func = Func() # int型渡す func.function(1) <- 元の関数名で呼んであげるで良さそう # string型を渡す func.function('moji') # list型を渡す func.function([1, 2, 3]) ===========結果================== int型が渡されました,obj=1 string型が渡されました,obj=moji <class 'list'>型が渡されました, obj=[1, 2, 3]
ちなみに他のデコレータと一緒に使いたいってなった時は、以下のようにsingledispatchmethod
を一番外にして書いてあげると良いらしい
今回は staticmethod
を追加してあげる
=> については以下を見るとわかりやすいかも
https://www.lifewithpython.com/2014/02/python-difference-between-staticmethod-and-classmethod.html
from functools import singledispatchmethod class Func: @singledispatchmethod @staticmethod def function(obj): return print(f'{type(obj)}型が渡されました, obj={obj}') # int型がきた時の関数の挙動を宣言 @function.register def return_int(obj: int): return print(f'int型が渡されました,obj={obj}') @function.register def return_str(obj: str): return print(f'string型が渡されました,obj={obj}') # int型渡す Func.function(1) # string型を渡す Func.function('moji') # list型を渡す Func.function([1, 2, 3])
ちなみに、今回調べていて、公式ドキュメントを参考に書いてみたら、動かない現象を発見しましたので、一応メモ
以下のコードを動かすと
class Negator: @singledispatchmethod @classmethod def neg(cls, arg): raise NotImplementedError("Cannot negate a") @neg.register @classmethod def _(cls, arg: int): return -arg @neg.register @classmethod def _(cls, arg: bool): return not arg
以下のエラーが出ます
TypeError: Invalid first argument to `register()`: <classmethod object at 0x1043063d0>. Use either `@register(some_class)` or plain `@register` on an annotated function.
↑のコードは、 Python3.8.1 と3.8.3 でしか動作しないコードなのでご注意を・・・
どう修正すればいいのかまでは、調べてないですが・・・
1年前はまだ解決してなさそうですが、今はどうなんでしょう・・・ 🤔
まとめ
こちらのデコレータを使うと、何がいいかってif文が乱立するソースコードではなくなり、可読性があがりそうって感じでしょうかね 👐
今回の調査で参考にしたサイトたち
https://marusankakusikaku.jp/python/standard-library/functools/#singledispatch-ジェネリック関数の定義
PydanticのFieldクラスのdeprecated
きっかけ
- 将来消す可能性がある変数には、
Field(deprecated=True)
をつけておいてほしいと言われた - イメージとしてはこんな感じ
from pydantic import BaseModel, Field from typing import Optional class User(BaseModel): id :int name: str old_field: Optional[str] = Field(deprecated=True) <- これ new_field: str
deprecated
って何と思ったので調べてみた
結論
- 2021.6現在まだ、
pydantic
には実装されていないので、Field(deprecated=True)
をつけても無視される(つけたところで何も変わらない) pydantic
のField
クラスはこんな感じで無視されるようになっている
- こちらの方がissueで将来的に追加して欲しい機能としてチケットをあげている
Add deprecated
marker for fields in a model · Issue #2255 · samuelcolvin/pydantic
有効になるとどうなるのか?
- ↑のissueによると、
deprecated=True
をつけると、つけられたフィールドは将来的に削除される可能性があるので、使わないでね みたいなwarningメッセージみたいなものが出るようになるらしい
(原文) Pydantic Fields should have a boolean option deprecated (default False) which can be used to mark deprecated fields. Pydantic plugin for Pycharm can also use this field to see if the field should be used any longer, and show code hints if someone is trying to interact with deprecated fields.
副作用とは?
巷で話題の 「ワクチンの副作用」 のことではないので、そっちじゃないんですか〜ってなった方は、ここでお戻りください🙇♀️
概要
- ある機能があるデータを変化させる
- 1で変化したデータを使用している処理の結果が変わる
参考:https://gimo.jp/glossary/details/side_effect.html
どういう時に起こるか?
値渡しと参照渡しを例にあげてみた(C言語)
参照渡しでは、関数呼び元のデータを変化させてしまっており、その後の処理の結果を変えてしまっている(副作用がある)
参考:https://chomado.com/programming/call-by-value-or-by-reference/
#include <stdio.h> void function(int num) { num += 100; } void function2(int *pNum) { *pNum += 100; } int main() { int number = 1; /* 値渡し */ function(number); printf("値渡し後\n"); printf("%d\n", number); /* 参照渡し */ function2(&number); printf("参照渡し後\n"); printf("%d\n", number); } ===== 結果 ========= 値渡し後 1 参照渡し後 101 <- 本来であればnumber = 1であるはず
- この場合は、
function
関数の呼び元で宣言されているnumber
の値が、function
関数内で変化させられることはない - しかし参照渡しをしている
function2
関数内では、呼び元のnumber
が変えられてしまう事象が起きる
以下のサイトに上のコードをコピペして、実行すれば同じ結果が見れる
https://www.tutorialspoint.com/compile_c_online.php
副作用があることによるデメリットとは?
副作用を起こさないために。
- 変数のスコープを小さくする。
- 外部の変数や状態に依存しない実装を心がける
今、自分が実装しているコードは、自分のコード以外で何か気にしなければいけないのだろうかと一瞬立ち止まって考えてみるのも良いかもですねぇ。