Posted at

Python3 初心者 デコレータ、ジェネレータ、名前空間、他


これからはオライリージャパンの入門python3のコードを参考にやっていきます。ぜひ読んでみてください!



関数内関数

def outer(a, b):

def inner(c, d):
return c+d
return inner(a, b)

詳しく説明していこう。innerに入っているc、dがreturnに書いてあるa, bにつながっている。そしたらa,bはouterにもa, bが入っている。よって

outer(4, 7)

11

このようになる。違う例題をみてみましょう!

def knight(saying):

def inner(quote):
return "We are the knight:", quote
return inner(saying)


クロージャ

関数内関数はクロージャ(他の関数によって作成される関数でその関数の外で作られた変数などを覚えてたりすることができる)となる。

def knight2(saying):

def inner2():
return "we are the knight:", saying
return inner2

これはinner2関数を作った。

そしてknight2の戻り値はinner2となり、knight2の引数とinner関数が同期する。


ラムダ関数

ラムダ関数は一行であらわせる簡単に済む関数だと思ってください。これからわかりやすくするために普通の文法で作った関数とラムダ関数を比較してみます。

# 普通の文法から作った関数

def edit_story(words, func):
for word in words:
print(func(word))

# 要素
soccer_player=['pogba', 'messi', 'ronaldo', 'de yong']
# 関数
def capi(word):
return word.capitalize()
# 結果
edit_story(soccer_player, capi)
Pogba
Messi
Ronaldo
De yong
# ラムダ式での関数
edit_story(soccer_player, lambda word: word.capitalize)
Pogba
Messi
Ronaldo
De yong

という風に簡単に済むことができます。edit_story関数を使う時に普通は関数を定義した後に要素を入れて結果を出力するような感じでしたがこのように簡単にたった一行で定義することが可能です。


ジェネレータ

ジェネレータは莫大なシーケンスデータを処理する時によく使われるもので、例えば3,000ものデータを処理する時にいちいちreturnされたら処理にも時間がかかりますしなかなかたいへんですよね?そこでジェネレータが使われます。

def my_range(first=0, last=10, step=1):

number=first
while number<last:
yield number
number+=step

rangers=my_range(1, 4)
for ranger in rangers:
print(ranger)
1
2
3

このようにすることができます。ジェネレータは基本returnではなくyieldの後に出力したい値を持っていきます。


デコレータ

ソースコードを書き変えずに今ある関数に変更を加えたい時があるだろう。その時引数として何が渡されているかを確認したい時がある。その時にデバック文というものを使用する。デコレータは入力として関数を受け取り別の関数を返す関数だと考えてください!

これから示す関数は

関数名と引数を表示する。その引数を他の関数に渡して実行する。結果をだす。変更後の関数を返す。

def document_it(func):

def new_function(*args, **kwargs):
print('Function is:', func.__name__)
print('Argument is:', args)
print('Keyword is:', kwargs)
result=func(*args, **kwargs)
print('Result:', result)
return result
return new_function

document_itにどんな関数(func)を渡してもdocument_itが追加した文を含む新しい関数が返される。(ここではnew_function。ではこれらの基本的な使い方を示していこう。

def add_ints(a, b):

return a+b
add_ints(3, 5)
8
add_ints_change=document_it(add_ints)
add_ints_change(3, 5)
# new function()
Function is: add_ints
Argument is: (3, 5)
Keyword is: {}
Result: 8
8

この関数は辞書がなかったので辞書は空になっている。これを関数(add_ints)を定義した時にやりたいものである。そういう時に@document_itで可能になる。

@document_it

def add_ints(a, b)
return a+b

add_ints(3, 5)
Function is: add_ints
Argument is: (3, 5)
Keyword is: {}
Result: 8
8

関数に対するデコレータはたくさん持つことが可能である。

def square(func)

def new_function(*args, **kwargs):
return=func(*args, **kwargs)
return result*result
return new_function

デコレータは定義した時にちかいものが先に実行される。

# squareを優先した時

@document_it
@square
def add_ints(a, b):
return a+b

add_ints(3, 5)
Function is: add_ints
Argument is: (3, 5)
Keyword is: {}
Result: 64
64

# document_itを優先した時
@square
@document_it
def add_ints(a, b):
return a+b

add_ints(3, 5)
Function is: add_ints
Argument is: (3, 5)
Keyword is: {}
Result: 8
64

ぱっと見何が変わったのかと思うかもしれないがResultの値が変わっている。squareが先に定義されている時はresultの結果が64と先にバレてしまっているため全てのresultに64が入っている。しかし、document_itが先になった場合はresultが一度add_intsに入っている8が出てくる。


少し詳しくしてみます。squareを先にした時は64をだすことを最優先にしていますので、Resultの後の値も64であります。二つ目の方はdocument_itが先に来ていますので8が先に帰ってきます。しかし最後は64になっています。自分は最初の方はここは8じゃないかと思ってました笑笑。ここで自分が間違っているかもしれないですけど、自分なりに考えてみた結果、最後は「もうほんとにないのかな??あ、64が結果として出せる!」ということかなと思いました。一回resultの値が返ったので次は二つ目に定義した@squareの自乗した関数が返ってくるんだな。ということにしました!間違ってると思った方、コメ欄で教えてくださると、自分としても勉強になります!



名前空間とスコープ

pythonのプログラムはそれぞれ様々な名前空間を持っている。各関数にはそれぞれ専用の名前空間を定義される。たとえばnumberというものをメインのプログラムで定義し、関数内でnumberという名前の別の変数を定義したとすると、それらは別々の変数となってしまう。しかしそれをなくすものがある。プログラムのメインの部分にグローバル名前空間を定義することによって可能になる。それではグローバル名前空間がある時とない時を区別してみよう。

# グローバルにする前

soccer='Messi'
def global_love():
print('inside global_love:', soccer)

global_love()
inside global_love: Messi

# 関数内で変数を定義したとき
def global_love():
print('inside global_love:', soccer
soccer='Messi'
# エラーが出てくる
global_love()
Traceback (most recent call last):
.
.
.
# グローバル名前空間を使用せずsoccerの値を変更したい時
soccer='Messi'
def global_love():
soccer='Ronaldo'
print('inside global_love:', soccer)

global_love()
inside global_love: Ronaldo
soccer
'Messi'

# ではグローバル名前空間を使ってみよう
soccer='Messi'
def global_love():
global soccer
soccer='Ronaldo'
print('inside global_love:', soccer)

soccer
'Messi'
global_love()
inside global_love: Ronaldo
soccer
'Ronaldo'

このように関数を定義してしまった後は変数は変更することができる。いつもなぜ関数を定義した後も関数内で変更した値が関数を使った後も使うことができないのか。それはpythonプログラムはローカル名前空間を使っているからである。だから関数内に入っているもの(変数)は消えて無くなる。また今グローバル名前空間にある変数はどれか、ローカルも同じようにきになるだろう。それぞれlocal()global()でチェックすることが可能。例えば、最後グローバル空間を使用しなかった先ほどのものでは

グローバル名前空間ーー{'soccer': 'Messi'}

ローカル名前空間ーー{'soccer': 'Ronaldo'}

という感じになります!!

つぎは名前とエラー処理などをやっていく