💡

JavaScriptエンジニアがPythonを学ぶ(◯◯文とかエラー処理)

に公開
2

基本的な文や関数の書き方洗い出し
ifとかjs側とほぼ同じものはPythonの方だけ書いておく

if文

Python特有の{}を使わずに書く感じ
条件式のあとは : を忘れずに書くこと

※ifの中にifがある(条件分岐のネスト)場合1つ目のインデントと同じスペースの数にしないとエラーになるので注意する

ざっと使った感じ論理演算子(and, or, not)はVBAみたいな感じで記述する

animal = 'bird'

if animal == 'dog':
    print('犬')
elif animal == 'bird':
    print('鳥')
else:
    print('未定義の動物')

number = '1'
#jsの===と同じなのでこの場合はその他が出力される
if number == 1:
    print(1)
# 普通にnumberにすると型エラーになるので一旦文字型にしておく
# ちなみにnumberに3を入れると下記条件式に一致する
elif number > '2' and number < '5':
    print('2より大きく5より小さい')
elif number == 6 or number == 7:
    print('6または7')
else:
    print('その他')

falsy判定(jsの一般的なもの以外)

また下記の様な、何も入っていない場合もfalsyな値として扱ってくれるので、わざわざlengthで比較する必要がない

ary = []
tuple=()
dict={}
set=set()

if not ary and not tuple and not dict and not set:
    print('false')

None判定

普通に empty == None みたいな書き方もできるが、isを使うのが一般的
(VBAのIs Nothingの書き方みたいだね)

none = None

if none is None:
    print('Noneだよ') 

while文

基本的には他の言語と同じif文と同じく : はちゃんとつけてインデントも揃える

ary = [1,2,3,4,5,6,7,8,9,10]
count = 0
while count < len(ary):
    print(ary[count])
    count += 1

breakやcontinueもちゃんと使える

ary = [0,1,2,3,4,5,6,7,8,9,10]
count = 0
while count < len(ary):
    if ary[count] == 0:
        count += 1
        continue
    if ary[count] >= 6:
        break
    print(ary[count])
    count += 1

while else

break以外でループを抜けた場合に使う処理
下記の場合は出力結果に「not break」と「break!!!!」が同時に出力されることはない

count = 0
isBreak = False

while count < 5:
    if isBreak:
        # elseのprintは実行されない
        print('break!!!!')
        break
    count += 1
else:
    # breakされた場合は実行されない
    print('not break')

出力結果

# isBreakがFalseの場合
not break
# isBreakがTrueの場合
break!!!!

for文

基本的にはjs同様の書き方ができる

もちろんこちらもbreakやcontinueを使える
for-elseも使えるので動作の内容忘れたらwhile elseを見る

ary = [1,2,3,4,5,6,7,8,9,10]
for value in ary :
    print(value)

また、Pythonは文字列もループの引数として渡してあげられる

for value in 'test' :
    print(value)

出力結果

t
e
s
t

下記のような感じでrangeを使って書くとわざわざ配列を生成しなくて良くなるので便利

for i in range(10):
    print(i)

出力結果

0
1
2
3
4
5
6
7
8
9

辞書型を渡す場合

そのまま渡すとkeyのみの取得になるので

user  = {'name':'山田', 'age': 11}
for key in user :
    print(key)

出力結果

name
age

items関数を使う

user  = {'name':'山田', 'age': 11}
for key, value in user.items() :
    print(key,value)

出力結果

name 山田
age 11

items関数はタプル型にしてくれる関数なのでこのような動作になる

user  = {'name':'山田', 'age': 11}
print(user.items())

# dict_items([('name', '山田'), ('age', 11)])

enumerate関数

Pythonで配列のループを回すときにenumerate関数を使うと添字をスマートに取得できる

ary = ['dog','bird','cat']
for i, value in enumerate(ary) :
    print(i, value)

出力結果

0 dog
1 bird
2 cat

zip関数

同じ配列の数のループをまとめて行いたい場合はzip関数が使える
なにかの要素が多い場合は無視されるので注意する

animals = ['dog', 'bird', 'cat', 'rabbit']
prices = [100, 200, 300]
for animal, price in zip(animals,prices) :
    print(animal, price)

出力結果

dog 100
bird 200
cat 300

Python3.10以降では要素数が異なる場合エラーに落とす機能だったり
itertools.zip_longest()で足りない要素を埋めることができたりする

参考

関数

Pythonの関数はちゃんと使用する前に関数の定義を行わないとエラーになるので注意する

def sum(x,y):
    return x + y

print(sum(1,2))

出力結果

3

型定義

関数の引数と返り値の型定義は下記のように指定できる
ただこれエディタ上で型エラー出ないのでお守りみたいな感じかもしれない

def sum(x: int, y: int) -> int:
    return x + y

print(sum(1,2))

キーワード引数とデフォルト引数

関数を呼び出すときに 引数名={value} の形で呼び出すと引数位置を指定できる
また、js同様デフォルト値の設定もできる

def user(name='no name', age = 0, memo='none'):
    print('name:', name)
    print('age:', age)
    print('memo:', memo)

user(memo='メモ',name='山田', age=12)
print('===================')
user()

出力結果

name: 山田
age: 12
memo: メモ
===================
name: no name
age: 0
memo: none

可変長引数(*args)

引数を *args のような形で記載すると、
jsのスプレッド構文のようなこともできる

これは引数の *numbers で引数のタプル化を行っている

def sum(*numbers):
    price = 0
    for number in numbers:
        price += number
    return price

print(sum(1,2,3,4,5))

出力結果

15

先にタプル型にして展開することもできる

def sum(*numbers):
    price = 0
    for number in numbers:
        price += number
    return price

numbers = (1,2,3,4,5)
print(sum(*numbers))

可変長引数のキーワード(**kwargs)

可変長引数をキーワード化するにはアスタリスクを2つ使用する
**◯◯ というように記載すると辞書化されるのでitemsでループを回すことができるようになる

def user(**kwargs):
    for k, v in kwargs.items():
        print(f'{k}:{v}')

user(lastName='山田', firstName='太郎', age=10)

出力結果

lastName:山田
firstName:太郎
age:10

*argsのときと同様先に辞書化してから渡すこともできる

def user(**kwargs):
    for k, v in kwargs.items():
        print(f'{k}:{v}')


profile = {'lastName':'山田', 'firstName':'太郎', 'age':10}
user(**profile)

可変長引数の併用(応用)

タプル引数とキーワード引数は併用できるので応用として残しておく

def zoo(zooName, *animals, **visitors):
    print(zooName)
    print(animals)
    print(visitors)

zoo('東武動物公園', 'ライオン', 'キリン','ゾウ', lastName='山田', firstName='太郎')

出力結果

東武動物公園
('ライオン', 'キリン', 'ゾウ')
{'lastName': '山田', 'firstName': '太郎'}

コメント

jsとかでは関数の引数のコメントとかは関数の上に記載するが
Pythonでは中に記載する
(コメントの書き方のお作法は諸説ありそうなのでとりあえずそれっぽく適当に)

js

/**
 * 和を計算する関数
 * @param {number} a 左辺
 * @param {number} b 右辺
 * @returns {number}
 */
function sum(a,b){
    return a+b;
}

Python

def sum(a,b):
    """
    和を計算する関数
    @param {number} a 左辺
    @param {number} b 右辺
    @returns {number} 和
    """
    return a+b

Pythonでこの書き方をすると
print(sum.__doc__)
だったり
help(sum)
とかで参照できる。
この書き方をすると、doc中にHTMLを記載したりするとWeb上で参照できて良いらしい

Decorator

そもそもDecoratorとは、既存のクラスとメソッドに拡張機能を持たせることらしい(デコレーション?)
既存の処理を呼び出したときに同時に実行したい処理を書くイメージ

TypeScriptでも現状実験段階の機能としてあるらしい(知らなかった・・・)

この機能が使えると嬉しいところは普段は必要のない処理だけど一時的につけたいログ出力の処理や、色々な箇所で使う共通の処理何かを関数そのものをいじらずにつけれるところ

記載方法はデコレータ用の関数を定義し、
使用する関数の上で @{デコレーター名} を指定することで実行される
複数のデコレーターを連結して実行することも可能

def decorator(func):
    def wrapper(*args, **kwargs):
        print("前処理")
        result = func(*args, **kwargs)
        print("後処理")
        return result
    return wrapper

@decorator
def say_hello():
    print("Hello!")

say_hello()
# 出力:
# 前処理
# Hello!
# 後処理

出力結果

前処理
Hello!
後処理

パラメータ付きのデコレーターを使えばもっと拡張性を持たせられる

def log(level):
    def decorator(func):
        def wrapper(*args, **kwargs):
            print(f"[{level}] {func.__name__}を実行中")
            return func(*args, **kwargs)
        return wrapper
    return decorator

@log("INFO")
def process():
    print("処理中...")

process()

出力結果

[INFO] processを実行中
処理中...

Lambda(ラムダ)

Lambda式とは

lambda 引数: 式 の形式で書ける、名前を持たない関数。
関数定義を簡潔に書くことができる。
1行で書けるシンプルな処理に向いている。

つまりjsで言う無名関数のこと

引数に関数を持たせる関数を作る場合lambdaを使うと簡潔に記述ができる

map, filter, sortedなど引数に関数を与えられる関数との相性が良い

記述方法は lambda {引数1}: {処理} のような書き方で可能

引数なしは lambda: {処理}
複数の引数がある場合は lambda {引数1},{引数2}: {処理}
のような感じで指定できる

# 条件でフィルター
nums = [1, 2, 3, 4]
even = list(filter(lambda x: x % 2 == 0, nums))

# 文字数並び替え
names = ["Alice", "Bob", "Charlie"]
sorted_by_len = sorted(names, key=lambda x: len(x))

print(even)
print(sorted_by_len)

出力結果

[2, 4]
['Bob', 'Alice', 'Charlie']

Generator

yield を使うことで 1つずつ値を返す関数。
特徴として呼び出されるたびに、前回の実行位置から再開される
ちなみに下記で3回目のnext(t)を呼び出すと StopIteration というエラーになる
のでこれを利用して例外処理などを仕込める

def test():
    yield 1
    yield 2


t = test()
print(next(t))
print(next(t))

実行結果

1
2

ジェネレーターの利点として配列のような変数を一気に定義してメモリに保持するのではなく実行されたときに値を生成するため、数百万件のデータを扱う場合などはメモリ効率が良さそう
また実行時に生成するので下記のようにすると無限に値を返すことができる

def counter():
    a  = 1
    while True:
        yield a
        a += 1

f = counter()

print(next(f))
print(next(f))
print(next(f))
print(next(f))

実行結果

1
2
3
4

また、中断される位置は yield の部分になるので yield の後で重い処理を書くと
2回目以降の実行でその処理が動く事になり重い処理の遅延の実行を遅らせる事ができたりする

Iterator

要素を1つずつ返せるオブジェクト
オブジェクト生成後の基本的な使い方はジェネレーターと同じ

下記の様にリストなどを iterにいれるとジェネレーターと同じような使い方ができる

nums = iter([1, 2, 3])  # list からイテレーターを作成
print(next(nums))  # 1
print(next(nums))  # 2
print(next(nums))  # 3

出力結果

1
2
3

包括表記

リスト、辞書、集合などを既存の配列などから何かしらの処理を加えて生成するときに簡潔に処理できるようにする手法

rangeなどを使うと既存の配列がなくても生成できる

# 1~10の値をリストに格納
nums = [x for x in range(1, 11)]
print(nums)  # [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

# 偶数のみ抽出(条件分岐)
evens = [x for x in range(10) if x % 2 == 0]
print(evens)  # [0, 2, 4, 6, 8]

# 文字列のリストを作成
names = ["Alice", "Bob", "Charlie"]
upper_names = [name.upper() for name in names]
print(upper_names)  # ["ALICE", "BOB", "CHARLIE"]

# 2つのリストを結合
nums1 = [1, 2, 3]
nums2 = [4, 5, 6]
combined = [x + y for x, y in zip(nums1, nums2)]
print(combined)  # [5, 7, 9]

# 辞書のキーと値を入れ替え
original = {'x': 1, 'y': 2, 'z': 3}
swapped = {value: key for key, value in original.items()}
print(swapped)  # {1: 'x', 2: 'y', 3: 'z'}

# 2つのリストから辞書を作成
keys = ['a', 'b', 'c']
values = [1, 2, 3]
dictionary = dict(zip(keys, values))
print(dictionary)  # {'a': 1, 'b': 2, 'c': 3}

タプルとジェネレーターの内包表記

通常タプル型を生成したい場合は () で生成できるが、
内包表記で生成する場合は () で生成しようとするとジェネレーターになる

タプルを内包表記で扱いたい場合は tuple() の形で生成する必要がある

# ジェネレーター
gen = (x * 2 for x in range(5))
print(next(gen))  # 0
print(next(gen))  # 2

# タプル
t = tuple([x * 2 for x in range(5)])
print(t)  # (0, 2, 4, 6, 8)

エラーハンドリング

例外が発生する箇所でtryをし、ハンドリングしたいエラーをexceptで記載する(基本的に個別で定義することなさそうだけどこれだけは別処理にしたいとかで使用するっぽい)
キャッチしたいエラー名を記載しない場合はすべてのケースでそこに流れていく

また、エラーの内容を出力したい場合は as {シンボル値} で記載するとエラー内容を変数に格納することができる

後続処理に続けたい場合はwhileの時のように else で記載できる
通常動作であろうがエラーだろうが処理したい場合は finally を使う
elseはelse→finallyの順で処理を入れたい場合に使用すると良さそう

jsはelseがないのでtryの中で全部処理書いちゃう気がするけどPythonはそんなことしないのかもしれない・・・

ary = [1,2,3]
# ary[index] のindexで3を指定するとIndexErrorが発生する
index = 2
# castIntで文字を指定するとTypeErrorが発生する
castInt = "1"

dictionary = {'a':1, 'b':2, 'c':3}
# keyで d を指定するとKeyErrorが発生する
key = 'a'

try:
    ary[index]
    int(castInt)
    dictionary[key]
# IndexErrorのみCatch
except IndexError as e::
    print('catch IndexError')
except TypeError:
    print('catch TypeError Error')
except:
    print('catch Other Error')
else:
    print('通常終了')
finally:
    print('何があろうと出力される')
print('終了')

ユーザー定義例外

正直js(開発はts)で独自で例外を作った記憶自体無いけどPythonは型周りがゆるゆるっぽい雰囲気を感じるのでこまめに例外エラーを定義しておくとなにか起こったときに原因がすぐに分かりそうなので学んでおく

まず、Pythonのデフォルトで定義されているExceptionを引き起こしたい場合は、 raise {Exceptionクラスかそのサブクラスのインスタンス} でエラーを起こせる

raise IndexError('エラーが発生しました')

実行結果

Exception: エラーが発生しました

個別に例外を実装したい場合は独自のclassを作成し、Exceptionを継承する必要がある
その後にエラーの判定をしたい関数か何かを定義し、raise で作成したclassを呼び出すと
独自実装したエラーを呼び出すことができる

# 独自例外を定義
#(Exception)でExceptionクラスを継承
class InvalidAgeError(Exception):
    # pass文は処理がなにもないときに書く
    pass

# 年齢チェック関数
def check_age(age):
    if age < 0:
        raise InvalidAgeError("年齢は0以上でなければなりません。")
    print(f"{age}歳は有効です。")

# 実行例
try:
    check_age(-1)
except InvalidAgeError as e:
    print(f"エラー: {e}")

input

コンソールから文字列の入力をしたい場合などに使う
入力された値は文字列型なので数値を扱いたい場合はキャストしてあげる必要がある

目的の値が入力されるまで入力待機を継続させたい場合はwhileを使って継続させる

# int型にキャストできるか判定
def isInt(value):
    try:
        int(value)
    except ValueError:
        return False
    else:
        return True


value = input('入力してね:')
if isInt(value):
    print(f'number value:{int(value)}')
else:
    print(f'other value:{value}')

これ系のリンク

JavaScriptエンジニアがPythonを学ぶ(型定義とか)
JavaScriptエンジニアがPythonを学ぶ(◯◯文とかエラー処理)(ここ)

Discussion

junerjuner

デフォルト値に配列などを渡すと参照渡しになり、関数を実行するたびに前の値が引き継がれてしまうので引数に値がない場合などは生成してあげるようにする

参照渡しというよりもインスタンスが共有されてしまい……?な気もします。(共有されるのは変数ではなくインスタンスな為

krs1krs1

コメントありがとうございます!
確かにそうですね・・・ご指摘の通り参照渡し云々の話ではなく、
関数のデフォルト引数が再評価されないことが原因だったので文言修正しましたmm

公式ドキュメント