Python文法速査:7.関数ベース

8326 ワード

ディレクトリに戻る
 
(1)関数の基本
●関数は第1クラスのオブジェクト
Pythonでは万物が対象であり,すべてのオブジェクトが第1クラス(first class)であり,関数も例外ではなく第1クラスである.オブジェクトである以上、独自の属性を持つ、変数に値を割り当てる、コンテナに格納できる、別の関数として使用できるパラメータや戻り値など、一般的なオブジェクトデータ処理として扱うことができます.defを使用して関数を定義する場合は、関数オブジェクトを生成することに相当します.
次の例では、3つの組み込み関数を1つのシーケンスに挿入し、反復器で取り出してそれぞれ呼び出します.
line = 'abc,1,3.14'
fun_list = [str, int, float]
para_list = line.split(',')
obj_list = [f(val) for f, val n zip(fun_list, para_list)]

# obj_list   :['abc', 1, 3.14]

 
●文書文字列
通常、関数defの最初の行は、関数の使用を記述する「文書文字列」として関数の__に保存されるdoc__ダイアログを呼び出す関数の説明ドキュメントは、組み込み関数help(関数名)でも表示できます.
 
●呼び出し可能タイプ
呼び出し可能タイプは、ユーザー定義関数、組み込み関数、インスタンスメソッド、クラス、呼び出し可能インタフェースを提供するインスタンスなど、関数呼び出し操作をサポートするオブジェクトを表します.組み込み関数callable()を使用して、オブジェクトが呼び出せるかどうかを確認できます.
クラスはすべて呼び出すことができます.クラスを呼び出すと、自動的にクラスにパラメータが渡されます.init__()メソッドを使用して、新しいインスタンスを作成します.
インスタンスオブジェクトは一般に呼び出せませんが、このインスタンスが実装されている場合は_call__()メソッドでは、このインスタンスを直接呼び出すことができます.例えば、xがインスタンスである場合、x(args)を実行することは、x._を呼び出すことに相当する.call__(args)メソッド.
 
●関数の属性
関数はオブジェクトとして、理論的に関数に任意の属性を追加することができます.関数には、次の表を参照して、内部のデフォルトのプロパティもあります.
組み込み関数には、次の属性があります.
ツールバーの
説明
__doc__
ドキュメント文字列.
__name__
関数の名前.
__self__
メソッドに関連するインスタンス.説明:len()のような組み込み関数について、_self__None(バインドされていないことを示す).s.append()のような組み込み方法は,_self__リストオブジェクトsです.
 
ユーザー定義関数には、次の属性があります.
ツールバーの
説明
__doc__
ドキュメント文字列.
__name__
関数の名前.
__dict__
関数のプロパティを含む辞書.
__code__
コンパイルされたコード.
__defaults__
デフォルトのパラメータを含むメタグループ.
__globals__
関数の適用時に対応するグローバルネーミングスペースの辞書.
__clousre__
クローズドパッケージ(ネストされた役割ドメインに関連するデータを含むメタグループ)
 
インスタンスメソッドには、次のプロパティがあります.
ツールバーの
説明
__doc__
ドキュメント文字列.
__name__
メソッド名.
__class__
メソッドのクラスを定義します.
__func__
実装メソッドの関数オブジェクト
__self__
メソッドに関連するインスタンス(バインドされていない場合はNone)
 
 
●バインドと非バインド方法
インスタンスを使用してメソッドを呼び出す場合、バインドと非バインドの2つの使用法があります.バインドメソッドはメンバー関数と対応するインスタンスをカプセル化し、バインドメソッドを呼び出すと、インスタンスは最初のパラメータselfとしてメソッドに自動的に渡されます.バインドメソッドではなくメンバー関数のみがカプセル化され、インスタンスはありません.ユーザーは、バインドメソッドを呼び出すときに、インスタンスを最初のパラメータとして明示的に渡す必要があります.詳しくは次の2つの例を参照してください.
バインドの使用法(bound method):
class Foo():
    def meth(self, a):
        print(a)

obj = Foo()     #       
m = obj.meth    #  meth     obj     
m(2)            #        ,Python    obj  self     meth()  

非バインド使用法(unbound method):
class Foo():
    def meth(self, a):
        print(a)

obj = Foo()     #       
um = Foo.meth   #    ,           um,        
um(obj, 2)      #           ,         obj         。

 
 
●匿名関数
Lambda文を使用して、短いコールバック関数を指定するための式形式の匿名関数を作成します.構文:
LambdaパラメータLambdaぱらめーた:式しき
Lambda匿名関数には、非式文も複数行文も表示できません.
次の例では、匿名関数を定義します.
a = lambda x,y: x*y
b = a(2,5)   # b    :10

次の例では、シーケンスのsort()メソッドにlambda匿名関数を入力します.
[('b',2),('a',1)].sort(key=lambda x:x[1])  #     [('a',1),('b',2)]

#   :                 ,      ('a',1)    :          ( :1),        。

 
 
 
(2)関数パラメータ
●位置パラメータ、キーワードパラメータ、戻り値
関数を呼び出すときは、入力パラメータの順序数が関数定義と一致する必要があります.そうしないと、TypeError異常が発生します.呼び出し時にパラメータ名を指定すると、関数定義のパラメータ順序に従う必要がなくなり、呼び出し時の可読性が大幅に向上します.このような指定パラメータ名が入力されるパラメータをキーワードパラメータと呼び、一般的にパラメータ名が指定されていないパラメータを位置パラメータと呼ぶ.キーワードパラメータはすべての位置パラメータの後ろにしか配置できません.
関数パラメータはすべて値で渡されますが、オブジェクト(すなわち単純な数字ではありません)が渡される場合、値で渡されるというのは関数パラメータがオブジェクトのアドレス値をコピーして渡すだけなので、関数では外のオブジェクトの内容を変えることができます.一般的には、このスタイルの使用は避けたほうがよい.また、通常、副作用の影響を防止するためにスレッドロックを使用する必要があるため、スレッドおよび同時プログラムでは、このような関数を使用する効率が低い.
return文、または単一のreturnキーワードを省略すると、Noneオブジェクトが返されます.
 
●パラメータのデフォルト値
関数定義時に、いくつかのパラメータにデフォルト値を指定することで、呼び出し時にこのパラメータを指定しなくてもいいです.デフォルトのパラメータが表示されると、このパラメータの後続のパラメータにはデフォルト値が必要になります.そうしないと、SyntaxError異常が発生します.
空のリストのような可変オブジェクトをデフォルト値として使用しないことをお勧めします.これにより、次の例では、予期せぬバグが発生する可能性があります.
def fun(x, seq=[]):
    seq.append(x)
    return seq
fun(1)    #   [1] 
fun(2)    #   [1,2]
fun(3)    #   [1,2,3]

上記の例の本意は、seqパラメータが入力されていない場合、新しいリストを作成し、xを新しいリストに入れることです.しかし、実際にバグが発生します.この場合、seq=Noneを使用して、関数に新しいリストを作成することをお勧めします.
 
●アスタリスク*
関数定義では、パラメータの前に単一の星を付けると、残りの位置パラメータを収集し、同じメタグループに入れることを意味します.これにより、関数呼び出し時にユーザは任意の複数のパラメータを提供することができる.次の例を示します.
def fun(x, *y):
    print(x)
    print(y)
    
fun('a', 1, 2, 'c')   #    :a   (1,2,'c')
fun(3)                #    :3   ()

 
単一星番号は、関数を呼び出すときに*を使用して、1つのメタグループを自動的にいくつかの指定された名前のキーワードパラメータに展開することもできます.
def myadd(x, y):
    return x+y
    
t = (1,2)
myadd(*t)   #    ,        (1,2)    x, y

 
●ツインスター**
関数定義では、パラメータの前に二重星を付けると、残りのキーワードパラメータを収集し、辞書に入れることを意味します.これにより、関数を呼び出すと、パラメータとして拡張可能な構成項目が大量に入力されます.
def fun(x, **z):
    print(x)
    print(z)

fun(x=1, y=2, z=3)    #    :1   {'y':2, 'z':3}    

 
ダブルスターは、関数を呼び出すときに**を使用して、1つの辞書を指定した名前のキーワードパラメータに自動的に分割することもできます.
def myadd(x, y):
    return x+y

d = {'x':1, 'y':2}
myadd(**d)    #    ,        d   :x=1, y=2

 
位置パラメータとキーワードパラメータを同時に収集するには、*パラメータの後ろに**パラメータを表示する必要があります.
def fun(*args, **kwargs):
    print(args)
    print(kwargs)
    
fun(1,2,3, x=4,y=5,z=6)
#    :(1,2,3)   {'x':4, 'y':5, 'z':6 }

 
 
 
(3)作用域
関数を実行するたびに、新しいローカルネーミングスペースが作成されます.このネーミングスペース(役割ドメインとも呼ばれる)は、内部に「非表示」の辞書があり、この関数パラメータの名前と関数内部で定義されたすべてのローカル変数が含まれています.
各関数には局所的な役割ドメインがあるほか、グローバルな役割ドメインもあります.組み込み関数locals()とglobals()を使用して、ローカルおよびグローバルの役割ドメイン辞書を表示できます.組み込み関数vars(obj)は、インスタンスの役割ドメイン辞書を返します.
x = 1
def fun():
    y = 2
    print(locals())
    print(globals())

fun()

# locals()    :{'y':2}
# globals()        x  ,             :
{ 'x': 1,
  'fun': 
  '__name__': '__main__',
  '__doc__':  None,
  '__package__': None,
  …… 
}

 
●関数内でのグローバル変数へのアクセス
Pythonインタプリタが変数を解析するときは、まずローカル役割ドメインを検索し、見つからないとグローバル役割ドメインを検索し、見つからないと内蔵ネーミングスペースを検索し、見つからないとNameError異常を引き起こす.
この方法では、グローバル変数にアクセスできますが、グローバル変数に値を割り当てることはできません.グローバル変数を割り当てるにはglobalキーワードを使用する必要があります.
関数でグローバル変数を変更するには、次の手順に従います.
x = 1
def fun():
    global x
    x = 2
fun()       #   fun() ,     x     2

関数のローカル変数名とグローバル変数名が重複している場合:
x = 1
def fun():
    x = 2                 #          x      
    globals()['x'] = 3    #   globals()    ,                

fun()       #   fun() ,     x     3

 
注意:Pythonでは、関数で上位呼び出し関数にアクセスするローカル役割ドメインへのアクセスはサポートされていません.次の例のコードでNameError異常が発生します.
def fun_inner():
    print(x)

def fun_outer():
    x = 2
    fun_inner()
    
fun_outer()     #       NameError  ,   fun_inner()   ,          fun_outer()         x

 
●ネストされたスコープ
Python 3は、ネストされた定義された関数のうち、外層呼び出し関数ではない外層定義関数を使用する局所的な役割ドメインの変数をサポートします.これを動的役割ドメイン(dynamic scoping)と呼び、nonlocalキーワードを使用する必要があります.次の例に示します.
def fun_outer():
    x = 4
    def fun_inner():
        nonlocal x      #           x
        x = 5
        print(x)
        
    fun_inner()
    print(x)
    
fun_outer()             #      fun_inner()  print  ,   5;        print  ,   x       5

 
 
 
(4)再帰
関数呼び出し自体を再帰(recursion)と呼ぶ.再帰的に最も典型的な使用シーンは,大きな問題を繰り返しの小さな問題に分解して解決することができ,この小さな問題の解決構造は非常に簡潔である.ほとんどの再帰は解決の代わりにループを使用することができますが、再帰書き込み関数を使用するとループよりも可読性が高く、優雅に見える場合があります.
nの乗算を再帰的に計算する:
def factorial(n):
    if n <= 1: 
        return 1
    else:
        return n * factorial(n-1)

 
シーケンスの再帰的な二分法で検索:
def search(seq, num, lower, upper):
    if lower == upper:
        return upper
    else:
        middle = (lower + upper) // 2
        if num > seq[middle]:
            return search(eq, num, middle+1, upper)
        else:
            return search(seq, num, lower, middle)
            
seq = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
seq.sort()
search(seq, num=2, lower=0, upper=9)    #              ,       num

 
 
 
ディレクトリに戻る