Remrinのpython攻略日記

python3に入門しました。python3についてあれこれとサンプルコードとか。

関数

関数についての注意事項など。
 

docstring

関数ブロックの最初に文字列リテラルを書くと、doctrrintとして扱われる。
ここに関数の説明などと記入できる。
 

引数

・引数は参照渡しになるので、リスト型などmutableな変数を渡すと、
 もとの変数も関数内の操作で変更されてしまう。

def appendzero(list2):
    list2.append(0)
    print("list2:", list2)  # list2: [1, 3, 5, 0]

list1 = [1, 3, 5]
appendzero(list1)

print("list1:", list1)      # list1: [1, 3, 5, 0]

 
・デフォルト引数
値が渡されなかったときに使う値(デフォルト値)をあらかじめ設定。
引数の個数が多いときなどに便利。

def test(a=0, b=0, c=10, d="test"):
    print(a, b, c, d)
    
test(1, 2, 3)        # 1 2 3 test
test(1)              # 1 0 10 test
test()               # 0 0 10 test
test(d="green", c=5) # 0 0 5 green  

・キーワード指定した引数の後ろに、キーワード指定しない引数を渡せない。
 
・デフォルト引数にリスト型などのmutableなものを指定するときは
 デフォルト値の評価が1度しか行われず、デフォルト値が共有されるので注意。

def f(a, li=[]):
    li.append(a)
    return li

print(f(1))       
print(f(2))      
print(f(3))      
print(f(4, [0])) 
print(f(5))      
print(f(6))      

# [1]
# [1, 2]
# [1, 2, 3]
# [0, 4]
# [1, 2, 3, 5]
# [1, 2, 3, 5, 6]

 
デフォルト値を共有したくないときは

def f(a, li=None):
    if li == None:
        li = []
    li.append(a)
    return li

print(f(1))       
print(f(2))      
print(f(3))      
print(f(4, [0])) 
print(f(5))      
print(f(6))

# [1]
# [2]
# [3]
# [0, 4]
# [5]
# [6]

 
・キーワード引数
 値を渡すときに、keyword=valueの形で指定することができる。
 デフォルト値を設定していない引数にも使用できる。

def f(a, b):
    print(a, b)
    
f(1, 2)      # 1 2
f(3, b=4)    # 3 4
f(a=5, b=6)  # 5 6
#f(7, c=8)   # TypeError: f() got an unexpected keyword argument 'c'
f(b=9, a=10) #10 9

 
・可変長引数
*argsとして受け取ると、個数に関係なく残りの引数をタプルとして受け取れる。

def test(a, b, *args):
    print(a, b, args)
    
test(1, 2)           # 1 2 ()
test(1, 2, 3)        # 1 2 (3,)
test(1, 2, 3, 4, 5)  # 1 2 (3, 4, 5)
# test(1) # TypeError: test() missing 1 required positional argument: 'b'

 **kwargsとすると、キーワード引数をすべて受け取れる 
 

返り値(戻り値)

 return文で値を返すことができ、返り値という。
 返り値の記述がない場合は、Noneを返す。

def test1(a):
    a = 1
    return

def test2(a):
    a = 1
    return a

a = 0
print(test1(a)) # None
print(test2(a)) # 1

 

変数

・変数名の探索順位
 同名の変数があるときなどの探索順。
 
 1 関数内の変数
 2 外側の関数の変数
 3 グローバル変数
 4 組み込み変数

a, b, c = 1, 1, 1
def f1():
    a, b = 2, 2
    print(a, b, c)
    
    def f2():
        a = 3
        print(a, b, c)
    f2()
    
f1() 
# 2 2 1
# 3 2 1

 
・関数内からは関数外の変数の読み取りはできるが、書き換えはできない。
 global宣言...グローバル変数を書き換えるとき。
 nonlocal宣言...一つ外側の関数の変数を書き換えるとき。
 
・関数内変数の名前割り当ては関数呼び出し時の最初に行われる。

x = 3

def f1():
    print(x)

def f2():
    print(x)
    x = 5

f1() # 3
f2() # UnboundLocalError: local variable 'x' referenced before assignmen

f2()では変数xを使うが、関数呼び出し時にxという名前を確保、ただし代入はx=5のとき。
  

代入

関数もオブジェクトの1つなので、変数に代入したりできる。

def total(n=0):
    return (sum(range(n)))

print(total(5)) # 10

t1 = total      # 関数をt1に代入。t1が関数になる。
t2 = total()    # 関数を実行し、戻り値をt2に代入。

print(t1)       # <function total at 0x...>
print(t2)       # 0
print(t1(5))    # 10

r5 = range(5)
print(max(r5))  # 4

 

関数アノテーション

引数のあとの:に続けて引数の説明を、->のあとに続けて戻り値の説明を付けることができ、関数アノテーションを呼ぶ。

def test(a:int, b:"引数bの説明") ->"戻り値の説明":
    "for test"
    return a*b

 
アノテーションの説明に書けるのは
 ・文字列
 ・式
 ・関数呼び出し
 ・関数オブジェクト(関数名)
 
関数アノテーションの内容は関数オブジェクトが持つ__annotations__という辞書に格納。
文字列の代わりに引数チェック用の関数オブジェクトを使用し、デコレータと組み合わせることで引数や戻り値のチェックが手軽にできるとのことですが、詳しい方法は理解できませんでした。