やり直しPython3(関数)

2016-04-22(Fri) by Makoto Yamahira

Python3の勉強(7) 関数

Python基礎勉強7日目です。今回は関数について。
決まった処理を何度も実行する場合にコードを再利用できると便利だ。
そのような時に関数が使える。

関数の定義

関数は次のような形で定義する。

   def 関数の名前(仮引数1, 仮引数2, 仮引数3, ...):
       処理
       return 返り値1, 返り値2, 返り値3, ...

関数の名前は変数名と同じ規則に従う(先頭は英字か_のみ使用可能で、それ以外の部分は英字・数字・_のいずれかを使用すること)。
呼び出し元から受け取る値は引数(ひきすう)として受け取る。
returnで呼び出し元に値を返すことができるが、Pythonでは何個でも返り値を返すことができる。
ちなみに引数、返り値はなくても良い。

関数の呼び出し

関数は次のような形で呼び出す。

    関数名(実引数1, 実引数2, 実引数3, ...)

引数がない場合もカッコは省略せずに書く。カッコがないと関数呼び出しではなく、関数オブジェクトそのものを指定することになる。

関数を使ってみる

それでは実例を見ていく。
まずは一番簡単な関数から。

    def do_nothing():
        pass

これは何もしな関数だ。
何もしないということを示すためにはpassが必要となる。
もちろん何らかの処理がある場合にはpassは必要ない。

    >>> def do_something():
    ...     print('done')

この関数を呼び出してみる。

    >>> do_something()
    done

カッコを忘れると実行されない。(関数オブジェクトそのものを示す)

    >>> do_something
    <function do_something at 0x1011b1268>

引数を使ってみる。

    >>> def say_something(your_name):
    ...    print('I like %s.' % your_name)
    ... 
    >>> say_something('makohira')
    I like makohira.

返り値を使って実行結果を返すように定義してみる。

    >>> def her_feelings(your_name):
    ...     return 'She likes %s.' % your_name

この関数を変数を使って利用してみる。

    >>> my_treasure = her_feelings('makohira')
    >>> print(my_treasure)
    She likes makohira.

ちなみにカッコを忘れると次のようになる(引数も忘れているが。。。)

    ... my_treasure = her_feelings

    >>> # 変数に関数が代入されている状態なので、この変数を使って関数呼び出しができる
    ... my_favorite = my_treasure('makohira')
    >>> print(my_favorite)
    She likes makohira.

引数を複数使った関数を定義してみる。

    >>> def some_feelings(your_name, her_name):
    ...     print('%s likes %s.' % (your_name, her_name))
    ... 

引数は指定した順番で代入される。

    >>> some_feelings('Bob', 'Catherine')
    Bob likes Catherine.

    >>> some_feelings('Catherine', 'Bob')
    Catherine likes Bob.

この混乱を避けるために仮引数の名前を指定することもできる。

    >>> some_feelings(her_name='Catherine', your_name='Bob')
    Bob likes Catherine.

仮引数にはデフォルト値を指定できる。

    >>> def some_feelings(your_name, her_name='Catherine'):
    ...     print('%s likes %s.' % (your_name, her_name))
    ... 
    >>> some_feelings('Bob')
    Bob likes Catherine.

引数のタプル化

*で始まる仮引数を作ると可変個の引数をタプルとして受け取ることができるようになる。
仮引数名は*で始まれば何でも良いが、慣習として*argが使われる。

    >>> def my_friends(*args):    # Pythonの*はポインタではないよ!
    ...     for friend in args:
    ...         print(friend)
    ... 
    >>> my_friends('Bob', 'Catherine', 'Hana', 'Osamu', 'Scott', 'Kenji', 'Hideaki')
    Bob
    Catherine
    Hana
    Osamu
    Scott
    Kenji
    Hideaki

引数の辞書化

**で始まる仮引数を作るとキーワード引数を辞書として受け取ることができるようになる。
仮引数名は慣習として**kwargsが使われる。

    >>> def my_favorites(**kwargs):
    ...     for key, value in kwargs.items():
    ...         print('%s: %s' % (key, value))
    ... 
    >>> my_favorites(food='ramen', snack='potechi', drink='beer')
    snack: potechi
    drink: beer
    food: ramen

*argsと**kwargsを併用する場合この順番で並べる必要がある。

    >>> def introduce(name, *args, **kwargs):
    ...     print("Hi, I'm %s." % name)
    ...     print('My favorite things are...')
    ...     for thing in args:
    ...         print('  ' + thing)
    ...     print('Additional Information')
    ...     for key, value in kwargs.items():
    ...         print('  %s: %s' % (key, value))
    ... 
    >>> introduce('makohira', 'banana', 'guitar', 'beer', age=39, message='Go for drinks!')
    Hi, I'm makohira.
    My favorite things are...
      banana
      guitar
      beer
    Additional Information
      age: 39
      message: Go for drinks!

関数内関数

関数内にも関数を定義できる。

    >>> def say_something():
    ...     def say_hello():
    ...         print('hello')
    ...     def say_hi():
    ...         print('hi')
    ...     for i in range(0, 10):
    ...         say_hello()
    ...         say_hi()
    ... 
    >>> say_something()
    hello
    hi
    hello
    hi
    hello
    hi
    hello
    hi
    hello
    hi
    hello
    hi
    hello
    hi
    hello
    hi
    hello
    hi
    hello
    hi

クロージャ

他の関数によって動的に生成される関数のことをクロージャという。
変数の値等によって処理を動的に生成することができる。

    >>> def your_name_func(your_name):
    ...     def inner():
    ...         return 'Your name is %s.' % your_name
    ...     return inner
    ... 
    >>> my_func = your_name_func('makohira')
    >>> my_func()
    'Your name is makohira.'
    >>> her_func = your_name_func('Elizabeth')
    >>> her_func()
    'Your name is Elizabeth.'

無名関数

Pythonで無名関数を作るにはlambda式を使う。
GUIのコールバック関数定義などで役に立つみたいだ。

    >>> my_func = lambda your_name: 'Your name is %s.' % your_name
    >>> my_func('makohira')
    'Your name is makohira.'

ジェネレータ

ジェネレータはシーケンスを実行時に作成するオブジェクトだ。
値をreturnで返す代わりにyieldで返すとジェネレータオブジェクトを返すジェネレータ関数を作ることができる。

    >>> def alarm_clock(your_name, first=1, last=10, step=1):
    ...     number = first
    ...     while number < last:
    ...         yield (your_name + ' ') * number
    ...         number += step
    ... 
    >>> my_alarm = alarm_clock('makohira', 1, 5)
    >>> for noise in my_alarm:
    ...     print(noise)
    ... 
    makohira 
    makohira makohira 
    makohira makohira makohira 
    makohira makohira makohira makohira 

next()関数で1回ずつ呼び出すこともできる。

    >>> my_alarm2 = alarm_clock('makohira', 1, 5)
    >>> next(my_alarm2)
    'makohira '
    >>> next(my_alarm2)
    'makohira makohira '

デコレータ

デバッグ文等、ソースコードを書き変えずに関数に処理を追加したい場合にはデコレータが利用できる。
デコレータは入力として関数をひとつ受け取り、別の関数を返す関数である。
次のような形で定義すると良さそう。

    def デコレータの名前(func):     # funcは関数を受け取る仮引数
        def 内部関数の名前(*args, **kwargs):
            処理
        return 内部関数の名前

実際のコードを書いてみる。
@デコレータの名前を関数の前につけてやるとデコレータを持った関数を定義できる。
実例は省略するが複数のデコレータを持つことができ、その場合関数のすぐ上のデコレータから順に下から上の順番で実行される。

    >>> from datetime import *
    >>> def execute_log(func):
    ...     def internal_func(*args, **kwargs):
    ...         print(datetime.now().isoformat())
    ...         print('「%s」を実行します。' % func.__name__)
    ...         print('位置引数:', args)
    ...         print('キーワード引数:', **kwargs)
    ...         result = func(*args, **kwargs)
    ...         print('実行結果:', result)
    ...         return result
    ...     return internal_func
    ... 
    >>> @execute_log
    ... def add(a, b):
    ...     return a + b
    ... 
    >>> add(5, 6)
    2016-04-29T05:49:35.961435
    add」を実行します。
    位置引数: (5, 6)
    キーワード引数:
    実行結果: 11
    11

デコレータの引数に関数を渡すとデコレータの機能を持った関数が作れるので、この方法だと既存の関数(組み込み関数等)にもデコレータの機能を追加できる。

    >>> log_print = execute_log(print)
    >>> log_print("I'm makohira.", "How are you?")
    2016-04-29T05:55:12.615915
    「print」を実行します。
    位置引数: ("I'm makohira.", 'How are you?')
    キーワード引数:
    I'm makohira. How are you?
    実行結果: None

名前空間

Pythonでは複数のオブジェクトを利用してコードを書くが、オブジェクトは名前空間で管理されている。
これによって同じ名前の変数を色々な場所で使いまわしても安全になる。

    >>> favorite_color = 'red'
    >>> print(favorite_color)
    red
    >>> def func1():
    ...     favorite_color = 'green'
    ...     print(favorite_color)
    ... 
    >>> func1()
    green
    >>> print(favorite_color)
    red

関数内でglobal名前空間の変数にアクセスしたい場合はglobalキーワードが利用できる。

    >>> favorite_color = 'red'
    >>> def func2():
    ...     global favorite_color
    ...     favorite_color = 'green'
    ...     print(favorite_color)
    ... 
    >>> func2()
    green
    >>> print(favorite_color)
    green

globals()関数で現在のグローバル名前空間の内容、locals()関数で現在のローカル名前空間の内容を調べることができる。

    >>> def show_objects():
    ...     a = 1
    ...     b = 'a'
    ...     print('globals:', globals())
    ...     print('locals :', locals())
    ... 
    >>> show_objects()
    globals: {'__package__': None, '__name__': '__main__', 'show_objects': <function show_objects at 0x10180cbf8>, '__loader__': <class '_frozen_importlib.BuiltinImporter'>, '__builtins__': <module 'builtins' (built-in)>, '__doc__': None, '__spec__': None}
    locals : {'b': 'a', 'a': 1}

2つのアンダースコアに囲まれた変数名

「__name__」のような2つのアンダースコアに囲まれた変数名はPythonが使う変数なので自分用に作ってはならない。(作れてしまうけどね。)
ちなみに次のようなものが利用できる。

  • 「__name__」:関数名
  • 「__doc__」:docstring
  • 「__main__」:メインプログラムの名前
    >>> sample()
    この関数の名前:sample
    >>> print(sample.__doc__)
     関数名のすぐ下に書かれた文字列は
        docstringになる

docstringはhelpでも使われるので、自作関数を作るときには書くようにしよう。
キーボードの「q」キーでhelp画面を終了できる。

    >>> help(sample)

    Help on function sample in module __main__:

    sample()
        関数名のすぐ下に書かれた文字列は
        docstringになる
    (END)

勉強に使っている本はオライリーの「入門Python3」


Comments