Python/整理中
複素数
[編集]Pythonには複素数型が組み込まれており、複素数を操作するための組み込み関数や演算子が提供されています。Pythonでは、j
またはJ
を使って虚数単位を表します[1]。
例えば、次のようにして複素数を定義できます:
# 実数部が3で虚数部が4の複素数 z = 3 + 4j print(f"{z=}, {z.real=}, {z.imag=}, {abs(z)=}, {type(z)=}")
- 実行結果
z=(3+4j), z.real=3.0, z.imag=4.0, abs(z)=5.0, type(z)=<class 'complex'>
また、複素数同士の演算もサポートされています:
print(f"{(3 + 4j) + (2 - 1j)=}") print(f"{(3 + 4j) * (2 - 1j)=}")
- 実行結果
(3 + 4j) + (2 - 1j)=(5+3j) (3 + 4j) * (2 - 1j)=(10+5j)
Pythonのcmath
モジュールには、複素数に対するさまざまな数学関数(三角関数、指数関数、対数関数など)が提供されています。これを使って複素数に対する操作や計算を行うことができます。
import cmath # 複素数の絶対値を取得 print(f"{abs(3 + 4j)=}") # 複素数の偏角を取得 print(f"{cmath.phase(3 + 4j)=}")
- 実行結果
abs(3 + 4j)=5.0 cmath.phase(3 + 4j)=0.9272952180016122
これらの関数や演算子を使うことで、Pythonで簡単に複素数を操作できます。
正規表現
[編集]Pythonの 正規表現(regular expression)は組込みではなくモジュールreをインポートし使用します。
正規表現とは、検索のような機能です。詳しくは上記リンク『正規表現』のリンク先を読んでください。
検索 search
[編集]pythonで文字列中の検索をしたい場合、search という関数を re.search() という形式で使います。
なお、Perl など他の言語では、似たような演算を match という場合もありますが、しかし python では、matchという関数が別の意味で使われているので(後述)、混同しないように気をつけてください。
- コード例
import re string = 'books' m = re.search('oo', string) if m: print("あった") else: print("なかった")
- 実行結果
あった
- 解説
Pythonでは、正規表現を使うには、まずreをインポートする必要があります。
文字列中の文字を検索するには re.serach 関数を使って
re.search("検索したい文字列", 検索対象の文字列または文字列変数)
のように記述します。
検索する文字列を「oo」から、たとえば「goo」に返ると、とうぜん「book」には「goo」という文字の並びは無いので、検索しなくなります。
- コード例
import re string = 'books' m = re.search('goo', string) if m: print("あった") else: print("なかった")
- 結果
なかった
下記のように、 re.search 関数中でひとまとめに検索しても可能です。
- コード例
import re string = 'books' m = re.search('oo', 'books') if m: print("あったよ") else: print("なかったよ")
- 結果
あったよ
先頭 match
[編集]正規表現の関数 re.match は、対象の文字が、文字列の先頭にないと検索にヒットしません。
下記の2種類のコードとその結果を見比べてください。
「book」の文字列中に「oo」があるにもかかわらず、先頭は「oo」からは始まらないので、 re.match の場合には「oo」ではヒットせず、「boo」にしないとヒットしないのです。
- コード例1
import re string = 'books' m = re.search('boo', string) if m: print("あった") else: print("なかった")
- 結果
あった
- コード例2
import re string = 'books' m = re.search('oo', string) if m: print("あったぜ") else: print("なかったぜ")
- 結果
なかったぜ
split関数
[編集]Pythonに限らず、PerlやC#などでもsplit関数というのがあり、1個の文字列を、区切り文字でくぎってリスト(配列)に代入し、リストのいくつもの個数の要素に代入していく機能が、モダンなプログラム言語にはあります。
このうち Python の split関数は、正規表現のreモジュールで扱われますので、python では関数 re.split を使うことになります。(他のプログラム言語では、正規表現とは無関係なのが一般的。)
import re string = 'きみとぼく' m = re.split('と', string) print ("1個目は" , m[0]) print ("2個目は" , m[1])
- 結果
1個目は きみ 2個目は ぼく
対話モード
[編集]対話モードでも、正規表現は使えます。
- 対話例
>>> import re >>> m = re.match(r'<title>(.*?)</title>', '<TITLE>Example Web Page</TITLE>', re.I) >>> m.group(0) '<title>Example Web Page</title>' >>> m.group(1) 'Example Web Page'
r''
やr""
のようなraw文字列はエスケープ文字を解釈しないので、\d
や\s
などのメタ文字を書くのに重宝します。matchメソッドは第一引数に正規表現パターン、第二引数にマッチ対象文字列、第三引数にre.I
やre.IGNORECASE
やre.M
やre.MULTILINE
やre.S
やre.DOTALL
などのフラグを指定します。Pythonに正規表現リテラルというものは存在しません。
matchメソッドはマッチオブジェクトを返します。マッチオブジェクトのgroupメソッドを使用して一致部分を抽出します。group(0)
はマッチ全体、group(1), group(2), ..., group(N)
は後方参照です。matchメソッドで対象文字列にマッチしなかった場合はマッチオブジェクトではなくNoneを返します。マッチに失敗する可能性がある場合は必ずif m == None:
あるいはif not m:
(None
はFalse
)で確認してください。
HTTPクライアント
[編集]Python 2ではurllibまたはurllib2、Python 3以降はurllib.requestのurlopenメソッドを使用してHTTPクライアントを作成することができます。
# -*- coding: utf-8 -*- try: from urllib.request import urlopen except: from urllib2 import urlopen print( urlopen('http://www.example.com/').read().decode() )
レスポンスはbytesで返ってくるので、decodeメソッドを使用してstrに変換しています。
JSON
[編集]JSONを扱うにはimport json
します。
>>> import json >>> json.dumps({'key': 'value'}) '{"key": "value"}' >>> json.loads(json.dumps({'key': 'value'})) {'key': 'value'} >>>
このコードから、オブジェクトのコピー(特にディープコピー)にJSONを利用できることに気がつくと思いますが、その用途にはモジュール copy の copy() や deepcopy() を使います。
インクリメントとデクリメント
[編集]PythonにはC/C++やJavaにあるインクリメント演算子とデクリメント演算子はないので、加算・減算と再代入で実現します。
>>> a=50 >>> a=a+1 >>> print(a) 51 >>> a=50 >>> a=a-1 >>> print(a) 49
累算代入文
[編集]Pythonにインクリメント演算子とデクリメント演算子はありませんが、累算代入文( augmented assignment statement )[2]の加算代入文・減算代入文はあります。
>>> a=50 >>> a+=1 >>> print(a) 51 >>> a=50 >>> a-=1 >>> print(a) 49
pass
[編集]passは「文法上必ず処理を書かなくてはならないが何も処理を書きたくない」というときに使います。
>>> def p(): ... pass ... >>> p()
ラムダ式
[編集]無名関数を定義するにはlambda
文を使用します。
>>> (lambda x: x * x)(2) 4
ヒアドキュメント
[編集]ヒアドキュメントは複数に渡る文字列リテラルで、文字列を""" """
あるいは''' '''
で囲みます。
docstring
[編集]モジュール・クラス・関数の冒頭の文字列リテラルは docstring と呼ばれドキュメントを記すために使われ、 オブジェクト.__doc__ で参照できます。 また、doctestではドキュメントの中の用例を使ってテストを行うときに使われます。
- docstringの例
"""モジュールのdocstring""" class MyClass: """クラスのdocstring""" def method(self): """メソッドのdocstring""" def function(): """関数のdocstring""" print(f'''\ {__doc__=} {MyClass.__doc__=} {MyClass.method.__doc__=} {function.__doc__=} ''')
- 実行結果
__doc__='モジュールのdocstring' MyClass.__doc__='クラスのdocstring' MyClass.method.__doc__='メソッドのdocstring' function.__doc__='関数のdocstring'
ジェネレーター
[編集]Pythonではジェネレーターを簡単に書くことができます。ジェネレーターを用いると、最初のフィボナッチ数を求めるプログラムは次のように書くことができます。
- ジェネレーターの例
def fibgen(n): a, b = 0, 1 for _ in range(n) : yield a a, b = a + b, a for i in fibgen(8) : print(i, end=' ') print() print(tuple(fibgen(8)))
- 実行結果
0 1 1 2 3 5 8 13 (0, 1, 1, 2, 3, 5, 8, 13)
- Pythonでは特に指定のない変数は自動的にローカル変数(レキシカルスコープ)になります。
a, b = 0, 1
というのは分割代入(Destructuring assignment)で、a = 0; b = 1
と同じ意味です。a, b = a + b, a
も同様ですが、代入は一気に行われるため、t = a; a = a + b; b = t
と同義です(aとbの値が入れ替わります)。- Pythonでは
while True:
のように制御構文の条件式をかっこで囲む必要がありません。定義済みキーワードとして、真を表すTrue、偽を表すFalse、そしてNoneが存在します。 - yield文はreturn文と似ていますが、サブルーチンの戻り値を処理の「途中で」返すことができるところが違います。
- nextメソッドを呼ぶと、「途中から」処理が継続されます(for文では暗黙に next メソッドが呼出されます)。このような関数をジェネレーターといいます。
内包表記とジェネレータ式
[編集]Pythonには、シーケンスの内包表記とジェネレータ式があります。
- 内包表記とジェネレータ式
for label, expr in {"加算":"1 + 1", "リスト内包表記":"[2 ** x for x in range(5)]", "集合内包表記":"{2 ** x for x in range(5)}", "辞書内包表記":"{x: 2 ** x for x in range(5)}", "ジェネレータ式":"(2 ** x for x in range(5))", "タプルコンストラクターにジェネレータ式を適用":"tuple(2 ** x for x in range(5))", }.items() : print(f'''{label}: {expr} ⇒ {eval(expr)} : {type(eval(expr))}''')
- 実行結果
加算: 1 + 1 ⇒ 2 : <class 'int'> リスト内包表記: [2 ** x for x in range(5)] ⇒ [1, 2, 4, 8, 16] : <class 'list'> 集合内包表記: {2 ** x for x in range(5)} ⇒ {1, 2, 4, 8, 16} : <class 'set'> 辞書内包表記: {x: 2 ** x for x in range(5)} ⇒ {0: 1, 1: 2, 2: 4, 3: 8, 4: 16} : <class 'dict'> ジェネレータ式: (2 ** x for x in range(5)) ⇒ <generator object <genexpr> at 0x14ac1a9e56d0> : <class 'generator'> タプルコンストラクターにジェネレータ式を適用: tuple(2 ** x for x in range(5)) ⇒ (1, 2, 4, 8, 16) : <class 'tuple'>
- Pythonの式を表す文字列(Ex. "1 + 1")を要素としたタプルをループ変数exprで回しています。
print(f'{label}:\n {expr}\n ⇒ {eval(expr)} : {type(eval(expr))}')
は、- を表示します。
ラベル: 式 ⇒ 式の評価結果 : 式の評価結果の型
(2 ** x for x in range(5))
は、タプル内包表記…ではなく、ジェネレータ式で未評価のシーケンス(generator)を返します(組込み関数のrange同様、この時点ではメモリーオブジェクトではありません)。- タプルのコンストラクターにジェネレータ式を渡すと、タプルが返ります(タプルはイミュータブルですがメモリーオブジェクトです)。
代入演算子
[編集]if文やwhile文の条件には式が要求されるので代入文 =
は使えませんでした。
しかし、条件式の中で代入を行うのがふさわしいケースもあります。
このため Python 3.8で、条件式中でもつかえる代入演算子(Assignment Expressions) :=
(俗称:セイウチ演算子;Walrus operator)が導入されました[3]。
- 代入演算子の使用例
a = "1234567" # 7文字 if (n := len(a)) > 5: print("文字が5文字より多いです" , n - 5, "文字オーバー") print(f"あなたは{n} 文字を入力しました")
- 実行結果
文字が5文字より多いです 2 文字オーバー あなたは7 文字を入力しました
代入演算子を使った条件式を含む文のブロックの外に出ても、そのまま代入した効果は残ります。
なお、f"あなたは{n} 文字を入力しました"
はテンプレート・リテラルです。
エスケープシーケンス
[編集]たとえばprint()関数で、「こんにちは」と表示させたい場合なら
print("こんにちは")
と書きました。 では、" を含んだ文字列を表示するにはどうしたら良いでしょう? やり方は、いくつかあります。
- " を含んだ文字列を表示する方法
print('"') print("\"") print("\42") print("\x22")
- クオート文字を ' に変えました。素朴ですがパワフルな方法です。
- しかし、この方法では " と ' は1つの文字列の中で共存できません。
- " の前に \ を前置すると文字列を閉じる " を打消すことができます。
- \ は、¥(円記号)の半角で表示されるかもしれませんが、文字コードと機能は同じです。
- \ に続けて " の文字コードを8進数で表記します。
- 10進数ではないので注意してください。
- \ に続けて x それに続けて文字コードを16進数で表記します。
2. から 4. の様に、 \ を前置して文字列中に所望に文字を書く方法をエスケープシーケンス( escape sequence )といい、エスケープシーケンスに前置する文字 ' をエスケープ文字( escape character )といいます。
エスケープシーケンスの一覧
[編集]エスケープシーケンスの一覧 エスケープシーケンス 意味 \<改行> バックスラッシュと改行を無視 \\ バックスラッシュ自身 ( \
)\' シングルクォーテーション ( '
)\" ダブルクオーテーション ( "
)\a ASCII ベル(アラート) (BEL) \b ASCII バックスペース (BS) \f ASCII フォームフィード (FF) \n ASCII ラインフィード (LF) \r ASCII キャリッジリターン (CR) \t ASCII 水平タブ (TAB) \v ASCII 垂直タブ (VT) \ooo 8進数キャラクタコードによる文字指定 ooo \xhh 16進数キャラクタコードによる文字指定 hh \uxxxx 16ビットの16進数値xxxxを持つUnicode文字 \Uxxxxxxxx 32ビットの16進数値xxxxxxxxを持つUnicode文字
print関数
[編集]# 「ようこそ」 と出力
print("ようこそ")
#から行末まではコメントです。文字列の出力はprint()
を使います。Python 2まではprint
は文でしたが、Python 3では組込み関数printとなったため、かっこが必須となります。
文字列は" "
で囲んでも' '
で囲んでも同じ意味であり、エスケープ文字の取り扱いに違いはありません。
備考
[編集]- インデントについて
Pythonのブロックはスペース4つのインデントによって表されます(オフサイドルールといいます)。
- pythonについて
pythonは、Googleなどの企業のみならず、MITの初年度のプログラミングの授業でも採用されています。英語圏ではRubyやPerlよりも普及しています。
Pythonは1990年にグイド・ヴァンロッサムによって作られました。誰が書いても同じソースコードになるように(違う目的のコードは違う見た目になるように)設計されており、常に読みやすいプログラムを書くことができます。教育用プログラミング言語としても秀逸です。
文字列の書式化
[編集]Pythonには、いくつかの異なる文字列の書式化の方法があります。
書式化付き文字列化
[編集]C言語のsprintf()
に相当する書式付き文字列化は、Pythonでは文字列の % 演算子を使います。
また、書式化文字列に % によるフィールドが複数ある場合は、下のようにタプルを使います。
>>> print("%d.%d.%d" % (2, 6, 4)) 2.6.4
文字列の format メソッド
[編集]文字列の format メソッドを使う方法[4]。
>>> print("{} {} {}".format(2, 6, 4)) 2.6.4
f文字列
[編集]PEP 498で新しく f文字列 が追加されました[5]。
>>> print(f"{4} {9} {8}") 4 9 8
- 文字列の書式化
print("%d %d %d" % (0!=0, 0==0, 999)) print("{} {} {}".format(0!=0, 0==0, 999)) print(f"{0!=0} {0==0} {999}") print(f"{abs(-123)} {2**10} {999}")
- 実行結果
0 1 999 False True 999 False True 999 123 1024 999
pip
[編集]pipはPythonのパッケージインストーラーです[6]。Python Package Index[7] などのインデックスからパッケージをインストールするのに使用します。
pipがインストールされているかは、(インターラクティブ・モードではなく)コマンドラインから確認します。
- FreeBSD
% uname -a FreeBSD localhost 14.0-STABLE FreeBSD 14.0-STABLE #0 adc61137f: Wed Mar 27 08:16:08 JST 2024 user1@localhost:/usr/obj/usr/src/amd64.amd64/sys/GRIFFON amd64 % pip -V pip 23.3.2 from /usr/local/lib/python3.11/site-packages/pip (python 3.11)
- Windows 11
PS C:\Users\user1> pip.exe -V pip 23.2.1 from C:\Program Files\WindowsApps\PythonSoftwareFoundation.Python.3.12_3.12.240.0_x64__qbz5n2kfra8p0\Lib\site-packages\pip (python 3.12)
- GNU/Linux
$ uname -a Linux asus-2 6.6.25-01713-g2f237acc8e50 #1 SMP PREEMPT_DYNAMIC Fri, 19 Apr 2024 14:53:12 +0000 x86_64 GNU/Linux $ pip -V pip 23.0.1 from /usr/lib/python3/dist-packages/pip (python 3.11)
YAML
[編集]YAMLは、構造化データやオブジェクトを文字列にシリアライズするためのデータ形式の一種です。 Pythonは、初期状態ではYAMLを利用できませんが、pyYaml [8]をインストールすることで、pythonでYAMLを処理できるようになります。
pipがインストール済みならば、
- コマンドライン
% sudo pip install pyyaml
で pyYaml をインストールできます。 YAMLとPythonは別個の出自なのですが、YAMLはデータ構造をインデントで表し[9]、Pythonはプログラム構造をインデントをあらわすので似通った外観になります。
YAMLファイルの読出し
[編集]- test.yaml
名前:
姓: 山田
名: 太郎
国籍: 日本
性別: 男
部活: 野球部
- test-yaml.py
import yaml with open('/workspace/test.yaml') as f: obj = yaml.safe_load(f) print(f"""\ {obj=} {obj["国籍"]=} {obj["名前"]["姓"]=} """)
- 実行結果
obj={'名前': {'姓': '山田', '名': '太郎'}, '国籍': '日本', '性別': '男', '部活': '野球部'} obj["国籍"]='日本' obj["名前"]["姓"]='山田'
- import で yaml をインポートする必要があります。
- pythonでYAMLのセミコロンのデータを読取った場合の辞書型のオブジェクトを返します
かつてYAMLの読取りには、yaml.load()が使われていましたが、セキュリティ上の懸念から deprecated となり、yaml.load() を使うと
Main.py:4: YAMLLoadWarning: calling yaml.load() without Loader=... is deprecated, as the default Loader is unsafe. Please read https://msg.pyyaml.org/load for full details.
と警告されます。yaml.safe_load() を使いましょう。
オブジェクトのYAMLへの変換
[編集]- コード例
import yaml obj = { "a": [2,3,5,7], "b": 3.14, "c": "test test", } s = yaml.dump(obj) print("*** Block style ***") print(s); print("*** Load ***") print(yaml.safe_load(s)) s = yaml.dump(obj, default_flow_style=True) print("*** Flow style ***") print(s);
- 実行結果
*** Block style *** a: - 2 - 3 - 5 - 7 b: 3.14 c: test test *** Load *** {'a': [2, 3, 5, 7], 'b': 3.14, 'c': 'test test'} *** Flow style *** {a: [2, 3, 5, 7], b: 3.14, c: test test}
YAMLファイルの書込み
[編集]- コード例
import yaml obj = { "a": [2,3,5,7], "b": 3.14, "c": "test test", } with open('/workspace/test.yaml', "w") as f: yaml.dump(obj, f) with open('/workspace/test.yaml') as f: for s in f: print(s, end='') print("-"*40) with open('/workspace/test.yaml') as f: print(yaml.safe_load(f))
- 実行結果
a: - 2 - 3 - 5 - 7 b: 3.14 c: test test ---------------------------------------- {'a': [2, 3, 5, 7], 'b': 3.14, 'c': 'test test'}
pickle と marshal
[編集]pickleモジュールは、Pythonのオブジェクト構造をシリアル化およびデシリアル化するためのバイナリプロトコルを実装しています[10]。 marshalモジュールも、Pythonのオブジェクト構造をシリアル化およびデシリアル化するためのバイナリプロトコルの実装ですが、将来に渡ってフォーマットを変えないことは保証されていないので、その用途にはpickleモジュールあるいはshelveモジュールを使ってください[11]。 marshalモジュールは、主に.pycファイルのPythonモジュールの "擬似コンパイル "コードの読み書きをサポートするために存在します。
- コード例
import pickle obj = [1,3,5,7] obj.append(obj) print(f'{obj=}') pkl = pickle.dumps(obj) print(f'{pkl=}') print(f'{pickle.loads(pkl)=}') import marshal msl = marshal.dumps(obj) print(f'{msl=}') print(f'{marshal.loads(msl)=}')
- 実行結果
obj=[1, 3, 5, 7, [...]] pkl=b'\x80\x04\x95\x0f\x00\x00\x00\x00\x00\x00\x00]\x94(K\x01K\x03K\x05K\x07h\x00e.' pickle.loads(pkl)=[1, 3, 5, 7, [...]] msl=b'\xdb\x05\x00\x00\x00\xe9\x01\x00\x00\x00\xe9\x03\x00\x00\x00\xe9\x05\x00\x00\x00\xe9\x07\x00\x00\x00r\x00\x00\x00\x00' marshal.loads(msl)=[1, 3, 5, 7, [...]]
shelve
[編集]shelveモジュールは、永続的な辞書の実装です[12]。
- コード例
import shelve filename = "/workspace/temp.shelve" with shelve.open(filename) as sh: sh['x'] = 1 sh['y'] = "abc" sh['z'] = [0, 1, 2] print(f'{sh["z"]=}') with shelve.open(filename) as sh: print(f'{ {k:v for k,v in sh.items()}=}')
- 実行結果
sh["z"]=[0, 1, 2] {k:v for k,v in sh.items()}={'z': [0, 1, 2], 'x': 1, 'y': 'abc'}
脚註
[編集]- ^ 虚数単位は数学では一般的に を用いますが、Pythonでは
j
を用います。これは電気工学・電子工学の慣習で電流(intensity of electricity)の i(または I)との混同を避けるためです。また変数j
と区別するため1j
のように必ず係数を付けます。 - ^ [https://docs.python.org/ja/3/reference/simple_stmts.html#augmented-assignment-statements 3.10.4 Documentation » Python 言語リファレンス » 7. 単純文 (simple statement)]
- ^ “PEP 572 -- Assignment Expressions” (2018年2月28日). 2021年11月12日閲覧。
- ^ PEP-3101
- ^ PEP-498
- ^ “pip documentation v21.3.1” (2021/12/20 accessdate=2021/12/20).テンプレート:Cite web/error
- ^ “The Python Package Index (PyPI) is a repository of software for the Python programming language.” (2021/12/20 accessdate=2021/12/20).テンプレート:Cite web/error
- ^ https://github.com/yaml/pyyaml
- ^ インデントでデータ構造を表すスタイルをブロックスタイルと呼びます。YAMLには他にフロースタイルと言う形式があり、これはYAML1.2からはJSONそのものです。YAML Ain’t Markup Language (YAML™) version 1.2
- ^ “3.10.0 Documentation » The Python Standard Library » Data Persistence » pickle — Python object serialization” (2021年12月2日). 2021年12月2日閲覧。
- ^ “3.10.0 Documentation » The Python Standard Library » Data Persistence » marshal — Internal Python object serialization” (2021年12月2日). 2021年12月2日閲覧。
- ^ “3.10.0 Documentation » The Python Standard Library » Data Persistence » shelve — Python object persistence” (2021年12月2日). 2021年12月2日閲覧。