近年、Pythonのコーディングスキルの需要は高まっています。Pythonのプログラミングスキルを伸ばすために、コードを改善 する際に使えるクールなPythonトリックを30個厳選しました。今後30日間、毎日1つずつ学び、コードを一流に保つためにPythonのベストプラクティス もぜひご確認ください。
Pythonにまだ自信がない場合は、Python Programming スキルトラックで腕を磨いてください。
シーケンス&データ構造のトリック
#1 スライス
a = "Hello World!"
print ( a[ : : - 1 ] )
"""
!dlroW olleH
""" 提供: Learn-by-Exampleエディター、Tabキーを押して入力してください。EscapeキーとTabキーまたはShift+Tabキーで移動できます。
スライスは、インデックスに基づきシーケンスの一部にアクセスできるPythonの機能です。インデックスとは、シーケンス内の要素の位置を指します。シーケンス型がミュータブル(可変)なら、スライスでデータを抽出・変更できます。
注 : イミュータブル(不変)なシーケンスにもスライスは使えますが、スライスを変更しようとするとTypeErrorが発生します。
スライスの書式は sequence[start:stop:step] です。start 、stop 、step の値を省略すると既定値が使われます。既定値は次のとおりです。
"start" は既定で 0
"stop" は既定でシーケンスの長さ
"step" は指定しなければ既定で 1
sequence[start:stop] とした場合、返される要素は開始インデックスから stop - 1 まで(stop の位置は含まれません)。
負のインデックスも渡せます。これはシーケンスを反転する際などに使えます。例えば、4要素のリストでは、0番目のインデックスは -4 とも等しく、最後のインデックスは -1 です。上のコード例では、この知識を step パラメータに適用しています。結果として、文字列はシーケンスの末尾から0番目まで逆順に出力されました。
#2 インプレース交換/同時代入
a = 10
b = 5
print ( f"First: { a, b} " )
"""
First: (10, 5)
"""
a, b = b, a + 2
print ( f"Second: { a, b} " )
"""
Second: (5, 12)
""" 提供: Learn-by-Exampleエディター、Tabキーを押して入力してください。EscapeキーとTabキーまたはShift+Tabキーで移動できます。
最初にbの値が7になると思ったなら、インプレース交換のワナにはまっています。
Pythonでは、自動アンパックにより、1回の代入でイテラブルを複数の変数に展開できます。例えば次のようにします。
a, b, c = [ 1 , 2 , 3 ]
print ( a)
print ( b)
print ( c)
"""
1
2
3
""" 提供: Learn-by-Exampleエディター、Tabキーを押して入力してください。EscapeキーとTabキーまたはShift+Tabキーで移動できます。
* を使って複数の値を1つの変数にまとめる(パッキング)こともできます。以下はパッキングの例です。
a, * b = 1 , 2 , 3
print ( a, b)
"""
1 [2, 3]
""" 提供: Learn-by-Exampleエディター、Tabキーを押して入力してください。EscapeキーとTabキーまたはShift+Tabキーで移動できます。
自動パッキングとアンパッキングを組み合わせると、同時代入というテクニックが使えます。これにより、一連の値を一連の変数に同時に代入できます。
#3 リスト vs. タプル
import sys
a = [ 1 , 2 , 3 , 4 , 5 ]
b = ( 1 , 2 , 3 , 4 , 5 )
print ( f"List size: { sys. getsizeof( a) } bytes" )
print ( f"Tuple size: { sys. getsizeof( b) } bytes" )
"""
List size: 52 bytes
Tuple size: 40 bytes
""" 提供: Learn-by-Exampleエディター、Tabキーを押して入力してください。EscapeキーとTabキーまたはShift+Tabキーで移動できます。
多くのPythonプログラマはリストには親しみがありますが、タプル はそうでもありません。どちらもイテラブルで、インデックスアクセスができ、異種データ型を格納できます。ただし、タプルのほうが望ましい場面もあります。
まず、リストはミュータブル(可変)で、自由に変更できます。
a = [ 1 , 2 , 3 , 4 , 5 ]
a[ 2 ] = 8
print ( a)
"""
[1,2,8,4,5]
""" 提供: Learn-by-Exampleエディター、Tabキーを押して入力してください。EscapeキーとTabキーまたはShift+Tabキーで移動できます。
一方でタプルはイミュータブル(不変)で、変更しようとすると TypeError が発生します。
このため、タプルのほうがメモリ効率に優れます。Pythonは必要なメモリブロックをちょうどよく確保できますが、リストでは将来の拡張に備えて余分なメモリを確保する必要があります。これは動的メモリアロケーションと呼ばれます。
要点 : データを変更したくない場面では、メモリの観点からリストよりもタプルを選ぶべきです。タプルはリストより高速でもあります。
本チュートリアルでさらに詳しくPythonのデータ構造 を学んでください。
#4 ジェネレータ
a = [ x * 2 for x in range ( 10 ) ]
b = ( x * 2 for x in range ( 10 ) )
print ( a)
print ( b)
"""
[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
<generator object <genexpr> at 0x7f61f8808b50>
""" 提供: Learn-by-Exampleエディター、Tabキーを押して入力してください。EscapeキーとTabキーまたはShift+Tabキーで移動できます。
リスト内包表記は、他のイテラブルからリストを作るPython的な方法で、for ループよりはるかに高速です。ただし、角かっこ [] を誤って丸かっこ () にするとどうなるでしょうか? ジェネレータオブジェクトになります。
Pythonでは、リスト内包表記のロジックを丸かっこで囲むと、いわゆるジェネレータオブジェクトが作られます。ジェネレータは特別なイテラブルで、リストと異なり要素を保持しません。その代わり、各要素を順に生成する手順と現在の反復状態を保持します。
各要素は、遅延評価 と呼ばれる手法により、要求されたときにだけ生成されます。このジェネレータの利点は、全体のシーケンスを一度に構築しないため、メモリ使用量が少ないことです。
#5 エイリアシング
a = [ 1 , 2 , 3 , 4 , 5 ]
b = a
b[ 4 ] = 7
print ( id ( a) )
print ( id ( b) )
print ( a)
"""
15136008
15136008
[1, 2, 3, 4, 7]
""" 提供: Learn-by-Exampleエディター、Tabキーを押して入力してください。EscapeキーとTabキーまたはShift+Tabキーで移動できます。
Pythonはオブジェクト指向言語で、すべてがオブジェクトです。したがって、オブジェクトを識別子に代入することは、そのオブジェクトへの参照を作ることです。
ある識別子を別の識別子に代入すると、同じオブジェクトを参照する識別子が2つできます。これをエイリアシングといいます。片方での変更はもう一方にも影響します。望ましい場合もありますが、多くの場合は不意打ちになります。
回避策の1つは、ミュータブルなオブジェクトでエイリアシングを避けることです。または、参照ではなくクローン(複製)を作ることもできます。
最も簡単なクローンの作り方はスライスを使う方法です。
b = a[:] 提供:
これで、b の識別子に新しいリストオブジェクトの参照が作られます。
ほかにも、代入時に list(a) を呼ぶ、copy() メソッドを使うなどの方法があります。
#6 「not」演算子
a = [ ]
print ( not a)
"""
True
""" 提供: Learn-by-Exampleエディター、Tabキーを押して入力してください。EscapeキーとTabキーまたはShift+Tabキーで移動できます。
データ構造が空かどうかを確認する最も簡単な方法は、not 演算子を使うことです。Pythonの組み込みの not は論理演算子で、式が真でなければ True を返し、そうでなければ False を返します。つまり、真偽値やオブジェクトの真理値を反転させます。
if 文で次のように使われるのもよく見かけます。
if not a:
# do something... 提供:
a が True のときは not 演算子は False を返し、その逆も同様です。
最初は少し混乱するので、実際に試してみてください。
文字列&出力のトリック
#7 f文字列(F-strings)
first_name = "John"
age = 19
print ( f"Hi, I'm { first_name} and I'm { age} years old!" )
"""
Hi, I'm John and I'm 19 years old!
""" 提供: Learn-by-Exampleエディター、Tabキーを押して入力してください。EscapeキーとTabキーまたはShift+Tabキーで移動できます。
ときどき文字列オブジェクトを整形する必要がありますが、Python 3.6ではこの作業を簡単にするf文字列が導入されました。新しい方法の良さを理解するには、従来の整形方法を知っておくと役立ちます。
従来の整形方法は次のとおりです。
first_name = "John"
age = 19
print ( "Hi, I'm {} and I'm {} years old!" . format ( first_name, age) )
"""
Hi, I'm John and I'm 19 years old!
""" 提供: Learn-by-Exampleエディター、Tabキーを押して入力してください。EscapeキーとTabキーまたはShift+Tabキーで移動できます。
新しい整形方法は、本質的に高速で読みやすく、簡潔で、間違いも起きにくくなります。
f文字列のもう1つの用途は、変数名とその値を一緒に出力することです。これはPython 3.8で導入されました。
x = 10
y = 20
print ( f" { x = } , { y = } " )
"""
x = 10, y = 20
""" 提供: Learn-by-Exampleエディター、Tabキーを押して入力してください。EscapeキーとTabキーまたはShift+Tabキーで移動できます。
詳しくはPythonのF文字列フォーマット のチュートリアルをご覧ください。
#8 print() 関数の「end」パラメータ
languages = [ "english" , "french" , "spanish" , "german" , "twi" ]
print ( ' ' . join( languages) )
"""
english french spanish german twi
""" 提供: Learn-by-Exampleエディター、Tabキーを押して入力してください。EscapeキーとTabキーまたはShift+Tabキーで移動できます。
print 文をオプション引数を指定せずに使うことはよくあります。そのため、出力をある程度制御できることを知らないPythonistaも少なくありません。
変更できるオプション引数の1つが end です。end は、print 呼び出しの末尾に何を表示するかを指定します。
end の既定は "\n" で、新しい行を開始することを意味します。上のコードではこれをスペースに変えています。そのため、リストの要素が同じ行に並んで出力されました。
#9 タプルに追加
a = ( 1 , 2 , [ 1 , 2 , 3 ] )
a[ 2 ] . append( 4 )
print ( a)
"""
(1, 2, [1, 2, 3, 4])
""" 提供: Learn-by-Exampleエディター、Tabキーを押して入力してください。EscapeキーとTabキーまたはShift+Tabキーで移動できます。
タプルがイミュータブルであることはすでに知っています(トリック#3 参照)。タプルの状態を変更しようとすると TypeError になります。ただし、タプルを「変更できないオブジェクトへのバインディングを持つ名前の並び」と捉えると、見え方が変わるかもしれません。
このタプルの最初の2要素は整数で、イミュータブルです。最後の要素はリストで、Pythonではミュータブルなオブジェクトです。
リストを「変更できないバインディングを持つ並びの一つの名前」と見なすなら、そのリスト自体はタプルの内側からでも変更できることがわかります。
実務でこれを推奨するか? おそらくしませんが、知っておくと便利な小ネタです。
#10 辞書のマージ
a = { "a" : 1 , "b" : 2 }
b = { "c" : 3 , "d" : 4 }
a_and_b = a | b
print ( a_and_b)
"""
{"a": 1, "b": 2, "c": 3, "d": 4}
""" 提供: Learn-by-Exampleエディター、Tabキーを押して入力してください。EscapeキーとTabキーまたはShift+Tabキーで移動できます。
Python 3.9以降では、| (ビット単位OR)で辞書をマージできます。これ以上言うことはあまりありませんが、ずっと読みやすい書き方です!
コードスタイル&構文のトリック
#11 三項演算子/条件式
condition = True
name = "John" if condition else "Doe"
print ( name)
"""
John
""" 提供: Learn-by-Exampleエディター、Tabキーを押して入力してください。EscapeキーとTabキーまたはShift+Tabキーで移動できます。
上のコードで使っているのは三項演算子(条件式)と呼ばれるものです。条件が True か False かに応じて評価を行うために使います。
同じことを次のようにも書けます。
condition = True
if condition:
name = "John"
else :
name = "Doe"
print ( name)
"""
John
""" 提供: Learn-by-Exampleエディター、Tabキーを押して入力してください。EscapeキーとTabキーまたはShift+Tabキーで移動できます。
どちらも同じ結果ですが、三項演算子を使うと、より短く明快に書けます。Pythonistaが「よりPythonic」と呼ぶ書き方です。
#12 リストの重複削除
a = [ 1 , 1 , 2 , 3 , 4 , 5 , 5 , 5 , 6 , 7 , 2 , 2 ]
print ( list ( set ( a) ) )
"""
[1, 2, 3, 4, 5, 6, 7]
""" 提供: Learn-by-Exampleエディター、Tabキーを押して入力してください。EscapeキーとTabキーまたはShift+Tabキーで移動できます。
リストの重複を最も簡単に取り除くには、いったん集合(set)に変換し、必要なら再びリストに戻します。
ミュータビリティの観点では、set と list は似ています。どちらも要素の追加・削除が可能です。しかし中身は大きく異なります。
リストは順序付き、0始まりのインデックス、ミュータブル。セットは順序なし、インデックスなし。セットの要素はイミュータブル型でなければならず(セット自体はミュータブルでも)、インデックスでの取得や要素変更を試みるとエラーになります。
もう1つの大きな違いは、セットは重複を含めないことです。これにより、リストから重複を取り除けます。
#13 単独のアンダースコア
>> > print( _)
Traceback ( most recent call last) :
File "<stdin>" , line 1 , in < module>
>> > 1 + 2
3
>> > print( _)
3 提供:
アンダースコア(_)はPythonで有効な識別子なので、オブジェクトの参照名として使えます。同時に、最後に評価された結果を保持するという役割も持ちます。
ドキュメントには「対話型インタプリタは直前の評価結果を _ という変数で利用可能にする(print などの組み込み関数と同様に builtins モジュールに保存される)」とあります。
最初の行では、事前に _ に何も代入していないためエラーになりました。しかし 1 + 2 を計算すると、対話型インタプリタが結果を識別子 _ に保存してくれます。
#14 無視したい値にアンダースコアを使う
for _ in range ( 100 ) :
print ( "The index doesn't matter" )
"""
The index doesn't matter
The index doesn't matter
...
""" 提供: Learn-by-Exampleエディター、Tabキーを押して入力してください。EscapeキーとTabキーまたはShift+Tabキーで移動できます。
トリック#13では、対話型インタプリタが直前の評価結果をアンダースコア(_)に格納することを見ましたが、用途はそれだけではありません。
プログラムの後で使わない、気にかけないオブジェクトを表すためにも使えます。これは重要です。アンダースコアの代わりに識別子名を使ってしまうと、リンティング時に F841 エラー(代入されたローカル変数が未使用)が発生します。これは好ましくない実践です。
#15 末尾のアンダースコア
list_ = [ 0 , 1 , 2 , 3 , 4 ]
global_ = "Hi there" 提供: Learn-by-Exampleエディター、Tabキーを押して入力してください。EscapeキーとTabキーまたはShift+Tabキーで移動できます。
ここまでのアンダースコア(_)関連トリックに続き、もう1つの用途はPythonのキーワードとの衝突回避です。
PEP 8 では、末尾のアンダースコアは「Pythonのキーワードとの衝突を避けるために慣習として使うべき」と述べています。また「略語や不自然な綴りより、末尾にアンダースコアを1つ付けるほうがよい。つまり list_ は lst より望ましい」ともあります。
#16 先頭のアンダースコア
class Example :
def __init__ ( self) :
self. _internal = 2
self. external = 20 提供: Learn-by-Exampleエディター、Tabキーを押して入力してください。EscapeキーとTabキーまたはShift+Tabキーで移動できます。
経験豊富なPythonプログラマは、識別子やメソッド名の前にアンダースコアを付けることがよくあります。理由があります。
識別子やメソッドに先頭のアンダースコアが付いている場合、「この変数やメソッドは内部利用に限る」という隠れた意味があります。これはPEP 8で定義された注意喚起であり、Pythonによって強制されるわけではありません。したがって、先頭アンダースコアは弱い指標です。
Javaのように、Pythonには厳格なprivate/publicの区別はありません。つまり、コミュニティの合意によって意味が生まれているだけで、プログラムの振る舞いに影響はありません。
#17 アンダースコアの視覚的区切り
アンダースコアに関する最後の小ネタです。ここまで3つの使い方を見てきましたが、詳しくはPythonにおけるアンダースコア(_)の役割 のチュートリアルをご覧ください。
number = 1_500_000
print ( number)
"""
15000000
""" 提供: Learn-by-Exampleエディター、Tabキーを押して入力してください。EscapeキーとTabキーまたはShift+Tabキーで移動できます。
アンダースコアは、整数、浮動小数、複素数リテラルの桁区切りとしての視覚的セパレータにも使えます(Python 3.6で導入)。
長いリテラルや意味的に区切りが明確な値の可読性を高める目的です。詳細は PEP 515 を参照してください。
コードスタイル&構文のトリック
#18 __name__ == "__main__"
if __name__ == "__main__" :
print ( "Read on to understand what is going on when you do this." )
"""
print("Read on to understand what is going on when you do this.")
""" 提供: Learn-by-Exampleエディター、Tabキーを押して入力してください。EscapeキーとTabキーまたはShift+Tabキーで移動できます。
この構文を多くのPythonプログラムで見たことがあるはずです。Pythonは "__main__" という特別な名前を使い、実行中のPythonファイルがメインプログラムであれば、それを識別子 __name__ に設定します。
もしこのモジュール(Pythonファイル)を別のモジュールにインポートしてそのファイルを実行すると、コード中の式は偽になります。別モジュールからインポートされた場合、__name__ はそのモジュール名(ファイル名)に設定されるためです。
#19 「setdefault」メソッド
import pprint
text = "It's the first of April. It's still cold in the UK. But I'm going to the museum so it should be a wonderful day"
counts = { }
for word in text. split( ) :
counts. setdefault( word, 0 )
counts[ word] += 1
pprint. pprint( counts)
"""
{'April.': 1,
'But': 1,
"I'm": 1,
"It's": 2,
'UK.': 1,
'a': 1,
'be': 1,
'cold': 1,
'day': 1,
'first': 1,
'going': 1,
'in': 1,
'it': 1,
'museum': 1,
'of': 1,
'should': 1,
'so': 1,
'still': 1,
'the': 3,
'to': 1,
'wonderful': 1}
""" 提供: Learn-by-Exampleエディター、Tabキーを押して入力してください。EscapeキーとTabキーまたはShift+Tabキーで移動できます。
辞書のさまざまなキーに値を設定したいことがあります。例えば、コーパス内の単語の出現回数を追跡する場合です。一般的なやり方は次のとおりです。
辞書にキーが存在するか確認する
存在すれば値を1増やす
なければ追加し、値を1に設定する
コードにすると次のようになります。
counts = { }
for word in text. split( ) :
if word in counts:
counts[ word] += 1
else :
counts[ word] = 1 提供: Learn-by-Exampleエディター、Tabキーを押して入力してください。EscapeキーとTabキーまたはShift+Tabキーで移動できます。
これをより簡潔にするには、辞書オブジェクトの setdefault() メソッドを使います。
第1引数は確認したいキー、第2引数はそのキーが存在しない場合に設定する値です。キーがすでに存在する場合は、その値を返すだけで変更しません。
プログラム構造のトリック
#20 正規表現でのマッチング
import re
number = re. compile ( r"(0)?(\+44)?\d{10}" )
num_1 = number. search( "My number is +447999999999" )
num_2 = number. search( "My number is 07999999999" )
print ( num_1. group( ) )
print ( num_2. group( ) )
"""
'+447999999999'
'07999999999'
""" 提供: Learn-by-Exampleエディター、Tabキーを押して入力してください。EscapeキーとTabキーまたはShift+Tabキーで移動できます。
正規表現 を使うと、検索したいテキストのパターンを指定できます。多くの人はCTRL + F(Windows)で検索できることは知っていますが、正確な文字列がわからないときはどうするのでしょうか。答えは「パターンで検索する」です。
例えば、英国の電話番号は類似のパターンに従います。先頭に0があり、その後に10桁の数字が続く、または0の代わりに +44 が付き、その後に10桁の数字が続く(後者は国際形式を意味します)。
正規表現は大幅な時間節約になります。画像中のパターンを正規表現ではなくルールで捉えようとすると、10行以上のコードになるかもしれません。
コードを書かない場合でも、正規表現の仕組みを学ぶことは重要です。多くのテキストエディタやワープロには、正規表現を使った検索・置換機能があります。
#21 正規表現のパイプ
import re
heros = re. compile ( r"Super(man|woman|human)" )
h1 = heros. search( "This will find Superman" )
h2 = heros. search( "This will find Superwoman" )
h3 = heros. search( "This will find Superhuman" )
print ( h1. group( ) )
print ( h2. group( ) )
print ( h3. group( ) )
"""
Superman
Superwoman
Superhuman
""" 提供: Learn-by-Exampleエディター、Tabキーを押して入力してください。EscapeキーとTabキーまたはShift+Tabキーで移動できます。
正規表現にはパイプ(| )という特殊文字があり、複数の表現のいずれかをマッチさせられ、どこでも使えます。似たパターンが複数あるときにとても便利です。
例えば「Superman」「Superwoman」「Superhuman」は同じ接頭辞を持っています。そこでパイプを使い、繰り返される部分を保ち、異なる部分だけを変えられます。今回も時間の節約になります。
注意点 : 同じテキスト内に複数の候補がある場合、最初にマッチしたものが返されます。つまり「An example text containing Superwoman, Superman, Superhuman」といった文では Superwoman が返ります。
#22 print() 関数の「sep」パラメータ
day = "04"
month = "10"
year = "2022"
print ( day, month, year)
print ( day, month, year, sep = "" )
print ( day, month, year, sep = "." )
"""
04 10 2022
04/10/2022
04.10.2022
""" 提供: Learn-by-Exampleエディター、Tabキーを押して入力してください。EscapeキーとTabキーまたはShift+Tabキーで移動できます。
print() 関数の機能を十分に把握していないPythonプログラマは意外と多いものです。「Hello World」が最初のプログラムだったなら、print() は最初に学んだ組み込み関数の1つでしょう。画面に整形したメッセージを表示するために使いますが、print() にはもっと多くの機能があります。
上のコードでは、異なる方法で整形メッセージを表示しています。sep は print() のオプション引数で、複数のオブジェクトを渡したときに、それらをどのように区切るかを指定できます。
既定ではスペース区切りですが、例では sep を "" にしたり、"." にしたりして動作を変えています。
#23 ラムダ関数
def square ( num: int ) - > int :
return num ** 2
print ( f"Function call: { square( 4 ) } " )
"""
Function call: 16
"""
square_lambda = lambda x: x** 2
print ( f"Lambda function: { square_lambda( 4 ) } " )
"""
Lambda functional: 16
""" 提供: Learn-by-Exampleエディター、Tabキーを押して入力してください。EscapeキーとTabキーまたはShift+Tabキーで移動できます。
ラムダ関数 は、Pythonでできる中級〜上級のテクニックに通じます。このコースで中級Python を学びましょう。見た目は難しそうですが、とてもシンプルです。
例では引数を1つだけ使いましたが、複数使うこともできます。
square = lambda a, b: a ** b
print ( f"Lambda function: { square( 4 , 2 ) } " )
"""
16
""" 提供: Learn-by-Exampleエディター、Tabキーを押して入力してください。EscapeキーとTabキーまたはShift+Tabキーで移動できます。
本質的には、lambda キーワードにより、小さく制限された無名関数を1行で作れます。def キーワードで宣言した通常の関数と同様に振る舞いますが、名前がない点が異なります。
#24 「swapcase」メソッド
string = "SoMe RaNDoM sTriNg"
print ( string. swapcase( ) )
"""
sOmE rAndOm StRInG
""" 提供: Learn-by-Exampleエディター、Tabキーを押して入力してください。EscapeキーとTabキーまたはShift+Tabキーで移動できます。
swapcase() は、文字列中の大文字と小文字を一括で入れ替えるメソッドです。多くの用途があるわけではありませんが、知っておくと便利です。
#25 「isalnum」メソッド
password = "ABCabc123"
print ( password. isalnum( ) )
"""
True
""" 提供: Learn-by-Exampleエディター、Tabキーを押して入力してください。EscapeキーとTabキーまたはShift+Tabキーで移動できます。
ユーザーにパスワードを入力させるプログラムで、文字と数字の組み合わせである必要があるとします。これも、文字列インスタンスの isalnum() を呼ぶだけで1行で実現できます。
このメソッドは、すべての文字がアルファベット(A-Za-z)と数字(0-9)かを確認します。空白や記号(!#%$&? など)が含まれると False を返します。
#26 例外処理
def get_ration ( x: int , y: int ) - > int :
try :
ratio = x/ y
except ZeroDivisionError:
y = y + 1
ratio = x/ y
return ratio
print ( get_ration( x= 400 , y= 0 ) )
"""
400.0
""" 提供: Learn-by-Exampleエディター、Tabキーを押して入力してください。EscapeキーとTabキーまたはShift+Tabキーで移動できます。
Pythonのプログラムはエラーに遭遇すると終了します。
しかし、たとえばエンドユーザーがコードと対話する場面では、この挙動は望ましくありません。そこでプログラムが途中終了したらどうなるでしょう?
例外的な事態への対処にはいくつか考え方がありますが、多くのPythonプログラマは「許可より謝罪を」の思想を受け入れています。つまり、あらゆる例外ケースに備えるより、エラーが発生したときにそれを処理できる周辺文脈(例外処理)を用意するという発想です。すべての例外に備えようとして時間を浪費する必要はない、という考え方です。
ただし、これは問題発生後に対処する仕組みがある場合に限って成り立ちます。
#27 リストの差分を見つける
list_1 = [ 1 , 3 , 5 , 7 , 8 ]
list_2 = [ 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 ]
solution_1 = list ( set ( list_2) - set ( list_1) )
solution_2 = list ( set ( list_1) ^ set ( list_2) )
solution_3 = list ( set ( list_1) . symmetric_difference( set ( list_2) ) )
print ( f"Solution 1: { solution_1} " )
print ( f"Solution 2: { solution_2} " )
print ( f"Solution 3: { solution_3} " )
"""
Solution 1: [9, 2, 4, 6]
Solution 2: [2, 4, 6, 9]
Solution 3: [2, 4, 6, 9]
""" 提供: Learn-by-Exampleエディター、Tabキーを押して入力してください。EscapeキーとTabキーまたはShift+Tabキーで移動できます。
Pythonで2つのリストの差分を比較する方法を3つ紹介しました。
注 : list_1 が list_2 の部分集合であることが確実でない限り、解法1は他の2つと同じにはなりません。
#28 Args と kwargs
def some_function ( * args, ** kwargs) :
print ( f"Args: { args} " )
print ( f"Kwargs: { kwargs} " )
some_function( 1 , 2 , 3 , a= 4 , b= 5 , c= 6 )
"""
Args: (1, 2, 3)
Kwargs: {'a': 4, 'b': 5, 'c': 6}
""" 提供: Learn-by-Exampleエディター、Tabキーを押して入力してください。EscapeキーとTabキーまたはShift+Tabキーで移動できます。
関数が受け取る変数の数が不明なとき、関数の引数として *args と **kwargs を使います。
*args はキーワードなし(引数名を伴わない)可変個の引数を渡すためのものです。対して **kwargs は任意個のキーワード引数を渡せます。
実のところ、*args と **kwargs という語自体に魔法はありません。魔法はアスタリスク(*)にあります。つまり、*の後ろはどんな語でも構いませんが、args と kwargs を使うのが一般的で、Python開発者の間で定着しています。
#29 エリプシス(Ellipsis)
print ( . . . )
"""
Ellipsis
"""
def some_function ( ) :
. . .
def another_function ( ) :
pass 提供: Learn-by-Exampleエディター、Tabキーを押して入力してください。EscapeキーとTabキーまたはShift+Tabキーで移動できます。
Ellipsis はPythonのオブジェクトで、3つのドット(...)またはオブジェクト名(Ellipsis)で呼び出せます。
最もよく知られた用途は、NumPyにおける多次元配列のアクセスやスライスです。例えば次のように使います。
import numpy as np
arr = np. array( [ [ 2 , 3 ] , [ 1 , 2 ] , [ 9 , 8 ] ] )
print ( arr[ . . . , 0 ] )
"""
[2 1 9]
"""
print ( arr[ . . . ] )
"""
[[2 3]
[1 2]
[9 8]]
""" 提供: Learn-by-Exampleエディター、Tabキーを押して入力してください。EscapeキーとTabキーまたはShift+Tabキーで移動できます。
もう1つの用途は、未実装の関数におけるプレースホルダです。
つまり、Ellipsis、... 、または pass のいずれを使っても有効ということです。
#30 リスト内包表記
even_numbers = [ x for x in range ( 10 ) if x % 2 == 0 and x != 0 ]
print ( even_numbers)
"""
[2, 4, 6, 8]
""" 提供: Learn-by-Exampleエディター、Tabキーを押して入力してください。EscapeキーとTabキーまたはShift+Tabキーで移動できます。
最後のトリックはリスト内包表記 です。別のシーケンスからリストを生成するエレガントな方法で、上のコードのように高度なロジックやフィルタリングも行えます。
同じことを達成する別の方法もあります。例えば、次のようにラムダ関数を使うこともできます。
even_numbers = list ( filter ( lambda x: x % 2 == 0 and x != 0 , range ( 10 ) ) )
print ( even_numbers)
"""
[0, 2, 4, 6, 8]
""" 提供: Learn-by-Exampleエディター、Tabキーを押して入力してください。EscapeキーとTabキーまたはShift+Tabキーで移動できます。
しかし、多くのPythonistaは、この解法はリスト内包表記よりも可読性が低いと考えるでしょう。
近年、Pythonのコーディングスキルの需要は高まっています。Pythonのプログラミングスキルを伸ばすために、コードを改善する際に使えるクールなPythonトリックを30個厳選しました。今後30日間、毎日1つずつ学び、コードを一流に保つためにPythonのベストプラクティスもぜひご確認ください。
Pythonにまだ自信がない場合は、Python Programmingスキルトラックで腕を磨いてください。
シーケンス&データ構造のトリック
#1 スライス
スライスは、インデックスに基づきシーケンスの一部にアクセスできるPythonの機能です。インデックスとは、シーケンス内の要素の位置を指します。シーケンス型がミュータブル(可変)なら、スライスでデータを抽出・変更できます。
注: イミュータブル(不変)なシーケンスにもスライスは使えますが、スライスを変更しようとするとTypeErrorが発生します。
スライスの書式は sequence[start:stop:step] です。start、stop、step の値を省略すると既定値が使われます。既定値は次のとおりです。
sequence[start:stop] とした場合、返される要素は開始インデックスから stop - 1 まで(stop の位置は含まれません)。
負のインデックスも渡せます。これはシーケンスを反転する際などに使えます。例えば、4要素のリストでは、0番目のインデックスは -4 とも等しく、最後のインデックスは -1 です。上のコード例では、この知識を step パラメータに適用しています。結果として、文字列はシーケンスの末尾から0番目まで逆順に出力されました。
#2 インプレース交換/同時代入
最初にbの値が7になると思ったなら、インプレース交換のワナにはまっています。
Pythonでは、自動アンパックにより、1回の代入でイテラブルを複数の変数に展開できます。例えば次のようにします。
* を使って複数の値を1つの変数にまとめる(パッキング)こともできます。以下はパッキングの例です。
自動パッキングとアンパッキングを組み合わせると、同時代入というテクニックが使えます。これにより、一連の値を一連の変数に同時に代入できます。
#3 リスト vs. タプル
多くのPythonプログラマはリストには親しみがありますが、タプルはそうでもありません。どちらもイテラブルで、インデックスアクセスができ、異種データ型を格納できます。ただし、タプルのほうが望ましい場面もあります。
まず、リストはミュータブル(可変)で、自由に変更できます。
一方でタプルはイミュータブル(不変)で、変更しようとすると TypeError が発生します。
このため、タプルのほうがメモリ効率に優れます。Pythonは必要なメモリブロックをちょうどよく確保できますが、リストでは将来の拡張に備えて余分なメモリを確保する必要があります。これは動的メモリアロケーションと呼ばれます。
要点: データを変更したくない場面では、メモリの観点からリストよりもタプルを選ぶべきです。タプルはリストより高速でもあります。
本チュートリアルでさらに詳しくPythonのデータ構造を学んでください。
#4 ジェネレータ
リスト内包表記は、他のイテラブルからリストを作るPython的な方法で、for ループよりはるかに高速です。ただし、角かっこ [] を誤って丸かっこ () にするとどうなるでしょうか? ジェネレータオブジェクトになります。
Pythonでは、リスト内包表記のロジックを丸かっこで囲むと、いわゆるジェネレータオブジェクトが作られます。ジェネレータは特別なイテラブルで、リストと異なり要素を保持しません。その代わり、各要素を順に生成する手順と現在の反復状態を保持します。
各要素は、遅延評価と呼ばれる手法により、要求されたときにだけ生成されます。このジェネレータの利点は、全体のシーケンスを一度に構築しないため、メモリ使用量が少ないことです。
#5 エイリアシング
Pythonはオブジェクト指向言語で、すべてがオブジェクトです。したがって、オブジェクトを識別子に代入することは、そのオブジェクトへの参照を作ることです。
ある識別子を別の識別子に代入すると、同じオブジェクトを参照する識別子が2つできます。これをエイリアシングといいます。片方での変更はもう一方にも影響します。望ましい場合もありますが、多くの場合は不意打ちになります。
回避策の1つは、ミュータブルなオブジェクトでエイリアシングを避けることです。または、参照ではなくクローン(複製)を作ることもできます。
最も簡単なクローンの作り方はスライスを使う方法です。
これで、b の識別子に新しいリストオブジェクトの参照が作られます。
ほかにも、代入時に list(a) を呼ぶ、copy() メソッドを使うなどの方法があります。
#6 「not」演算子
データ構造が空かどうかを確認する最も簡単な方法は、not 演算子を使うことです。Pythonの組み込みの not は論理演算子で、式が真でなければ True を返し、そうでなければ False を返します。つまり、真偽値やオブジェクトの真理値を反転させます。
if 文で次のように使われるのもよく見かけます。
a が True のときは not 演算子は False を返し、その逆も同様です。
最初は少し混乱するので、実際に試してみてください。
文字列&出力のトリック
#7 f文字列(F-strings)
ときどき文字列オブジェクトを整形する必要がありますが、Python 3.6ではこの作業を簡単にするf文字列が導入されました。新しい方法の良さを理解するには、従来の整形方法を知っておくと役立ちます。
従来の整形方法は次のとおりです。
新しい整形方法は、本質的に高速で読みやすく、簡潔で、間違いも起きにくくなります。
f文字列のもう1つの用途は、変数名とその値を一緒に出力することです。これはPython 3.8で導入されました。
詳しくはPythonのF文字列フォーマットのチュートリアルをご覧ください。
#8 print() 関数の「end」パラメータ
print 文をオプション引数を指定せずに使うことはよくあります。そのため、出力をある程度制御できることを知らないPythonistaも少なくありません。
変更できるオプション引数の1つが end です。end は、print 呼び出しの末尾に何を表示するかを指定します。
end の既定は "\n" で、新しい行を開始することを意味します。上のコードではこれをスペースに変えています。そのため、リストの要素が同じ行に並んで出力されました。
#9 タプルに追加
タプルがイミュータブルであることはすでに知っています(トリック#3 参照)。タプルの状態を変更しようとすると TypeError になります。ただし、タプルを「変更できないオブジェクトへのバインディングを持つ名前の並び」と捉えると、見え方が変わるかもしれません。
このタプルの最初の2要素は整数で、イミュータブルです。最後の要素はリストで、Pythonではミュータブルなオブジェクトです。
リストを「変更できないバインディングを持つ並びの一つの名前」と見なすなら、そのリスト自体はタプルの内側からでも変更できることがわかります。
実務でこれを推奨するか? おそらくしませんが、知っておくと便利な小ネタです。
#10 辞書のマージ
Python 3.9以降では、|(ビット単位OR)で辞書をマージできます。これ以上言うことはあまりありませんが、ずっと読みやすい書き方です!
コードスタイル&構文のトリック
#11 三項演算子/条件式
上のコードで使っているのは三項演算子(条件式)と呼ばれるものです。条件が True か False かに応じて評価を行うために使います。
同じことを次のようにも書けます。
どちらも同じ結果ですが、三項演算子を使うと、より短く明快に書けます。Pythonistaが「よりPythonic」と呼ぶ書き方です。
#12 リストの重複削除
リストの重複を最も簡単に取り除くには、いったん集合(set)に変換し、必要なら再びリストに戻します。
ミュータビリティの観点では、set と list は似ています。どちらも要素の追加・削除が可能です。しかし中身は大きく異なります。
リストは順序付き、0始まりのインデックス、ミュータブル。セットは順序なし、インデックスなし。セットの要素はイミュータブル型でなければならず(セット自体はミュータブルでも)、インデックスでの取得や要素変更を試みるとエラーになります。
もう1つの大きな違いは、セットは重複を含めないことです。これにより、リストから重複を取り除けます。
#13 単独のアンダースコア
アンダースコア(_)はPythonで有効な識別子なので、オブジェクトの参照名として使えます。同時に、最後に評価された結果を保持するという役割も持ちます。
ドキュメントには「対話型インタプリタは直前の評価結果を _ という変数で利用可能にする(print などの組み込み関数と同様に builtins モジュールに保存される)」とあります。
最初の行では、事前に _ に何も代入していないためエラーになりました。しかし 1 + 2 を計算すると、対話型インタプリタが結果を識別子 _ に保存してくれます。
#14 無視したい値にアンダースコアを使う
トリック#13では、対話型インタプリタが直前の評価結果をアンダースコア(_)に格納することを見ましたが、用途はそれだけではありません。
プログラムの後で使わない、気にかけないオブジェクトを表すためにも使えます。これは重要です。アンダースコアの代わりに識別子名を使ってしまうと、リンティング時に F841 エラー(代入されたローカル変数が未使用)が発生します。これは好ましくない実践です。
#15 末尾のアンダースコア
ここまでのアンダースコア(_)関連トリックに続き、もう1つの用途はPythonのキーワードとの衝突回避です。
PEP 8では、末尾のアンダースコアは「Pythonのキーワードとの衝突を避けるために慣習として使うべき」と述べています。また「略語や不自然な綴りより、末尾にアンダースコアを1つ付けるほうがよい。つまり list_ は lst より望ましい」ともあります。
#16 先頭のアンダースコア
経験豊富なPythonプログラマは、識別子やメソッド名の前にアンダースコアを付けることがよくあります。理由があります。
識別子やメソッドに先頭のアンダースコアが付いている場合、「この変数やメソッドは内部利用に限る」という隠れた意味があります。これはPEP 8で定義された注意喚起であり、Pythonによって強制されるわけではありません。したがって、先頭アンダースコアは弱い指標です。
Javaのように、Pythonには厳格なprivate/publicの区別はありません。つまり、コミュニティの合意によって意味が生まれているだけで、プログラムの振る舞いに影響はありません。
#17 アンダースコアの視覚的区切り
アンダースコアに関する最後の小ネタです。ここまで3つの使い方を見てきましたが、詳しくはPythonにおけるアンダースコア(_)の役割のチュートリアルをご覧ください。
アンダースコアは、整数、浮動小数、複素数リテラルの桁区切りとしての視覚的セパレータにも使えます(Python 3.6で導入)。
長いリテラルや意味的に区切りが明確な値の可読性を高める目的です。詳細は PEP 515 を参照してください。
コードスタイル&構文のトリック
#18 __name__ == "__main__"
この構文を多くのPythonプログラムで見たことがあるはずです。Pythonは "__main__" という特別な名前を使い、実行中のPythonファイルがメインプログラムであれば、それを識別子 __name__ に設定します。
もしこのモジュール(Pythonファイル)を別のモジュールにインポートしてそのファイルを実行すると、コード中の式は偽になります。別モジュールからインポートされた場合、__name__ はそのモジュール名(ファイル名)に設定されるためです。
#19 「setdefault」メソッド
辞書のさまざまなキーに値を設定したいことがあります。例えば、コーパス内の単語の出現回数を追跡する場合です。一般的なやり方は次のとおりです。
コードにすると次のようになります。
これをより簡潔にするには、辞書オブジェクトの setdefault() メソッドを使います。
第1引数は確認したいキー、第2引数はそのキーが存在しない場合に設定する値です。キーがすでに存在する場合は、その値を返すだけで変更しません。
プログラム構造のトリック
#20 正規表現でのマッチング
正規表現を使うと、検索したいテキストのパターンを指定できます。多くの人はCTRL + F(Windows)で検索できることは知っていますが、正確な文字列がわからないときはどうするのでしょうか。答えは「パターンで検索する」です。
例えば、英国の電話番号は類似のパターンに従います。先頭に0があり、その後に10桁の数字が続く、または0の代わりに +44 が付き、その後に10桁の数字が続く(後者は国際形式を意味します)。
正規表現は大幅な時間節約になります。画像中のパターンを正規表現ではなくルールで捉えようとすると、10行以上のコードになるかもしれません。
コードを書かない場合でも、正規表現の仕組みを学ぶことは重要です。多くのテキストエディタやワープロには、正規表現を使った検索・置換機能があります。
#21 正規表現のパイプ
正規表現にはパイプ(|)という特殊文字があり、複数の表現のいずれかをマッチさせられ、どこでも使えます。似たパターンが複数あるときにとても便利です。
例えば「Superman」「Superwoman」「Superhuman」は同じ接頭辞を持っています。そこでパイプを使い、繰り返される部分を保ち、異なる部分だけを変えられます。今回も時間の節約になります。
注意点: 同じテキスト内に複数の候補がある場合、最初にマッチしたものが返されます。つまり「An example text containing Superwoman, Superman, Superhuman」といった文では Superwoman が返ります。
#22 print() 関数の「sep」パラメータ
print() 関数の機能を十分に把握していないPythonプログラマは意外と多いものです。「Hello World」が最初のプログラムだったなら、print() は最初に学んだ組み込み関数の1つでしょう。画面に整形したメッセージを表示するために使いますが、print() にはもっと多くの機能があります。
上のコードでは、異なる方法で整形メッセージを表示しています。sep は print() のオプション引数で、複数のオブジェクトを渡したときに、それらをどのように区切るかを指定できます。
既定ではスペース区切りですが、例では sep を "" にしたり、"." にしたりして動作を変えています。
#23 ラムダ関数
ラムダ関数は、Pythonでできる中級〜上級のテクニックに通じます。このコースで中級Pythonを学びましょう。見た目は難しそうですが、とてもシンプルです。
例では引数を1つだけ使いましたが、複数使うこともできます。
本質的には、lambda キーワードにより、小さく制限された無名関数を1行で作れます。def キーワードで宣言した通常の関数と同様に振る舞いますが、名前がない点が異なります。
#24 「swapcase」メソッド
swapcase() は、文字列中の大文字と小文字を一括で入れ替えるメソッドです。多くの用途があるわけではありませんが、知っておくと便利です。
#25 「isalnum」メソッド
ユーザーにパスワードを入力させるプログラムで、文字と数字の組み合わせである必要があるとします。これも、文字列インスタンスの isalnum() を呼ぶだけで1行で実現できます。
このメソッドは、すべての文字がアルファベット(A-Za-z)と数字(0-9)かを確認します。空白や記号(!#%$&? など)が含まれると False を返します。
#26 例外処理
Pythonのプログラムはエラーに遭遇すると終了します。
しかし、たとえばエンドユーザーがコードと対話する場面では、この挙動は望ましくありません。そこでプログラムが途中終了したらどうなるでしょう?
例外的な事態への対処にはいくつか考え方がありますが、多くのPythonプログラマは「許可より謝罪を」の思想を受け入れています。つまり、あらゆる例外ケースに備えるより、エラーが発生したときにそれを処理できる周辺文脈(例外処理)を用意するという発想です。すべての例外に備えようとして時間を浪費する必要はない、という考え方です。
ただし、これは問題発生後に対処する仕組みがある場合に限って成り立ちます。
#27 リストの差分を見つける
Pythonで2つのリストの差分を比較する方法を3つ紹介しました。
注: list_1 が list_2 の部分集合であることが確実でない限り、解法1は他の2つと同じにはなりません。
#28 Args と kwargs
関数が受け取る変数の数が不明なとき、関数の引数として *args と **kwargs を使います。
*args はキーワードなし(引数名を伴わない)可変個の引数を渡すためのものです。対して **kwargs は任意個のキーワード引数を渡せます。
実のところ、*args と **kwargs という語自体に魔法はありません。魔法はアスタリスク(*)にあります。つまり、*の後ろはどんな語でも構いませんが、args と kwargs を使うのが一般的で、Python開発者の間で定着しています。
#29 エリプシス(Ellipsis)
Ellipsis はPythonのオブジェクトで、3つのドット(...)またはオブジェクト名(Ellipsis)で呼び出せます。
最もよく知られた用途は、NumPyにおける多次元配列のアクセスやスライスです。例えば次のように使います。
もう1つの用途は、未実装の関数におけるプレースホルダです。
つまり、Ellipsis、...、または pass のいずれを使っても有効ということです。
#30 リスト内包表記
最後のトリックはリスト内包表記です。別のシーケンスからリストを生成するエレガントな方法で、上のコードのように高度なロジックやフィルタリングも行えます。
同じことを達成する別の方法もあります。例えば、次のようにラムダ関数を使うこともできます。
しかし、多くのPythonistaは、この解法はリスト内包表記よりも可読性が低いと考えるでしょう。