第五章Python関数はどのくらい知っていますか.


関数の役割:いくつかの複雑なコードをカプセル化して、関数は一般的に1つの機能で、使う時やっと呼び出して、重複利用率を高めてプログラムの構造を簡略化します.
5.1文法
def functionName(parms1, parms2, ...):
   code block
   return expression

関数はdefキーワードで始まり、スペースの後に関数名、カッコの中にパラメータがあり、パラメータを伝達するために使用され、関数コードセグメントの中で参照されます.
5.2関数の定義と呼び出し
#     
>>> def func():
...   print "Hello world!"
...   return "Hello world!" 
...
#     
>>> func()
Hello world!
'Hello world!'

関数を定義すると、実行されず、出力はありません.関数名を入力すると、関数に書かれたコードが二重カッコで実行されます.
ちなみにprintとreturnの違いは:
ちょっとおかしいですか.なぜprintはreturn出力と同じなのか、returnは引用符をつけて、明らかな違いはないようです.実は解釈器の下ですべての結果が出力されます.
returnの役割:関数を終了し、値を返します.式に従わないと、Noneが返されます.
では、彼らの違いを深く理解し、例を挙げてpyプログラムを書きます.
#!/usr/bin/env python
def func():
    print "1: Hello world!"
    return "2: Hello world!"
func()
# python test.py
1: Hello world!

分かったか?printは印刷オブジェクトの値であり、returnはオブジェクトを返す値である.つまりreturnのデフォルトはオブジェクト値を格納することです.中の値を知るにはprintで印刷できます.
#!/usr/bin/env python
def func():
    print "1: Hello world!"
    return "2: Hello world!"
print func()
# python test.py
1: Hello world!
2: Hello world!

なぜ関数にprintを使わないのか、印刷する必要がなく、他のコードに渡してこの関数の戻り値を処理する関数を定義することが多い.もちろんprintは関数コードをデバッグするときに役立ちます.
5.3関数パラメータ
5.3.1パラメータを受け入れる
    >>> def func(a, b):    
    ...   print a + b
    ...
    >>> func(1, 2)
    3
    >>> func(1, 2, 3)
    Traceback (most recent call last):
      File "", line 1, in 
    TypeError: func() takes exactly 2 arguments (3 given)

aおよびbは変数として理解され、内側コードブロックによって参照される.関数を呼び出すときは、括弧の中の式の数が関数パラメータの数に対応し、パラメータの位置をパラメータによって与えます.数が対応していない場合はTypeErrorエラーが投げ出されます.
もちろん、関数パラメータは配列でも構いません.
    >>> def func(a):    
    ...   print a
    ...
    >>> func([1,2,3])
    [1, 2, 3]
    >>> func({'a':1,'b':2})
    {'a': 1, 'b': 2}

パラメータ値を1つずつ対応させたくない場合は、次のように指定します.
    >>> def func(a,b):    
    ...   print a + b
    ...
    >>> func(b=2,a=1)
    3

5.3.2関数パラメータのデフォルト値
パラメータのデフォルト値は予め定義されており、関数を呼び出すときにこの値が入力されると、入力された値を実際の値とし、そうでない場合はデフォルト値とします.
    >>> def func(a, b=2):    
    ...   print a + b
    ...
    >>> func(1)
    3
    >>> func(1, 3)
    4

5.3.3任意数量のパラメータを受け入れる
上記の方法ではパラメータが複数固定されていますが、どのくらいのパラメータが分からない場合は以下の方法で使用できます.
単一のアスタリスクを使用:
    >>> def func(*a):         
    ...   print a
    ...
    >>> func(1,2,3)
    (1, 2, 3)

単一のアスタリスクはメタグループとして格納されます.
2つのアスタリスクを使用:
    >>> def func(**a):    
    ...   print a
    ...
    >>> func(a=1, b=2, c=3)
    {'a': 1, 'c': 3, 'b': 2}

2つのアスタリスクは1つの辞書として保存されます.これらはすべて配列の形で入力されていることがわかります.
資料を調べていると、このように書かれた関数パラメータ(*args,**kwargs)が見えるかもしれませんが、上とは名前が違うだけです.
    >>> def func(*args, **kwargs):    
    ...   print args
    ...   print kwargs
    ...
    >>> func(1,2,3,a=1,b=2,c=3)
    (1, 2, 3)
    {'a': 1, 'c': 3, 'b': 2}

通常のパラメータとともに使用:
    >>> def func(a, b, *c):    
    ...   print a + b
    ...   print c
    ...
    >>> func(1,2,3,5,6)
    3
    (3, 5, 6)
    >>> def func(a, b, **c):
    ...   print a + b
    ...   print c
    ...
    >>> func(1,2,a=1,b=2,c=3)
    Traceback (most recent call last):
      File "", line 1, in 
    TypeError: func() got multiple values for keyword argument 'a'
    >>> func(1,2,c=3,d=4,e=5)
    3
    {'c': 3, 'e': 5, 'd': 4}

異常を投げ出すのは,伝達された最初のパラメータ1と,3番目のパラメータa=1とが,いずれも伝達関数パラメータaと考えられるからである.この点に注意してください.
5.4作用域
役割ドメインは新鮮に聞こえますが、実は簡単です.変数やコードの使用可能範囲を制限し、この範囲でなければ使用できません.プログラムロジックの局所性を向上させ、名前の衝突を減らす.
スコープは、一般的にグローバル(global)->ローカル(local)->組み込み(build-in)です.
まず、グローバル変数とローカル変数を見てみましょう.
>>> a = 2
>>> def func():
...   b = 3
...
>>> a
2
>>> b
Traceback (most recent call last):
  File "", line 1, in 
NameError: name 'b' is not defined

a変数の役割ドメインは、グローバル変数、すなわち、コードの最初に定義された変数と呼ばれるコード全体で有効である.
b変数の役割ドメインは関数内部,すなわち局所変数であり,関数外では参照できない.
これにより,グローバル変数とローカル変数は名前が同じでも衝突しない.
関数内の変数もグローバルに参照できる場合はglobal宣言を使用します.
>>> def func():
...   global b
...   b = 3
...
>>> b
Traceback (most recent call last):
  File "", line 1, in 
NameError: name 'b' is not defined
>>> func()
>>> b
3

関数が参照されていない場合、中のコードブロックは説明されていません.
globalを使用して変数を宣言すると、外部は関数内部の変数を呼び出すことができます.
5.5ネスト関数
1)パラメータなし
>>> def func():
...   x = 2
...   def func2():
...     return x
...   return func2  #   func2  
...
>>> func()()
2
>>> func2()
Traceback (most recent call last):
  File "", line 1, in 
NameError: name 'func2' is not defined
>>> def func():   
...   x = 2         
...   global func2
...   def func2():
...     return x 
...   return func2
...
>>> func()()
2
>>> func2()
2

内層関数は、外層関数の役割ドメインにアクセスできます.埋め込み関数は外層関数でのみ呼び出されますが、globalを使用してグローバル役割ドメインを宣言することもできます.
内部関数を呼び出す別の方法:
2)バンドパラメータ
>>> def func(a):
...   def func2(b):
...     return a * b
...   return func2
...
>>> f = func(2)   #       。  ,        。
>>> f(5)
10
>>> func(2)(5)
10

内層関数は、外層関数の役割ドメインにアクセスできます.ただし、変数は再割り当てできません.たとえば、次のようになります.
>>> def func():
...   x = 2
...   def func2():
...      x = 3
...   func2()
...   return x
...
>>> func()
2
>>> def func():
...   x = 2
...   def func2():
...     x += 1
...   func2()
...   return x
...
>>> func()
Traceback (most recent call last):
  File "", line 1, in 
  File "", line 5, in func
  File "", line 4, in func2
UnboundLocalError: local variable 'x' referenced before assignment

5.6閉パッケージ
「公式」の解釈は、「閉パッケージ」とは、多くの変数とバインドされた環境を持つ式(通常は関数)を指し、これらの変数も式の一部である.
実際には、ネストされた関数は閉パッケージの1つです.
>>> def func(a):
...   def func2(b):
...     return a * b
...   return func2
...
>>> f = func(2)   #       。  ,        。
>>> f(5)
10

funcは1つの関数で、中にはまた1つの関数func 2がネストされていて、外部の関数から伝わってきたaパラメータで、この変数は関数func 2にバインドされます.func関数は、内層関数func 2を戻り値とし、func関数をf変数に格納する.外層関数が内層関数を呼び出すと、内層関数が実行され(func()())、閉パッケージが作成されます.
5.7高次関数
高次関数は、少なくとも2つのいずれかを満たす条件です.
1)1つ以上の関数を入力として受け入れることができる.
2)関数を出力します.
abs,map,reduceはいずれも高次関数であり,後述する.
実は、上記のネスト関数も高次関数です.
高次関数の例を示します.
>>> def f(x):
...   return x * x
...
>>> def f2(func, y):
...   return func(y)
...
>>> f2(f, 2)
4

ここでのf 2は、最初のパラメータが関数であり、最初の条件を満たすため、高次関数である.
ブログアドレス:http://lizhenliang.blog.51cto.com
QQ群:Shell/Python運維開発群323779636
5.8関数装飾器
デコレータ(decorator)自体は関数であり、別の関数やクラスをパッケージし、他の関数がコードを変更する必要がなく動的に機能を追加することができ、デコレータが返すのも関数オブジェクトです.
まず一例を挙げて、装飾器の効果を説明し、2つの関数を定義し、それぞれパラメータを伝達して積を計算します.
#!/usr/bin/python
# -*- coding: utf-8 -*-
def f1(a, b):
    print "f1 result: " + str(a * b)
def f2(a, b):
    print "f2 result: " + str(a * b)
f1(1, 2)
f2(2, 2)
# python test.py
f1 result: 2
f2 result: 4

予想通り、積が印刷されました.
この2つの関数に入力パラメータを印刷したい場合は、どうすればいいですか.
#!/usr/bin/python
# -*- coding: utf-8 -*-
def f1(a, b):
    print "f1 parameter: %d %d" %(a, b)
    print "f1 result: " + str(a * b)
def f2(a, b):
    print "f2 parameter: %d %d" %(a, b)
    print "f2 result: " + str(a * b)
f1(1, 2)
f2(2, 2)
# python test.py
f1 parameter: 1 2
f1 result: 2
f2 parameter: 2 2
f2 result: 4

思った通りに入力したパラメータを印刷して、もっと簡潔にする方法はありませんか?装飾器の後の効果を見てみましょう.
#!/usr/bin/python
# -*- coding: utf-8 -*-
def deco(func):
    def f(a, b):
        print "%s parameter: %d %d" %(func.__name__, a, b)
        return func(a, b)
    return f
@deco
def f1(a, b):
    print "f1 result: " + str(a * b)
@deco
def f2(a, b):
    print "f2 result: " + str(a * b)
f1(1, 2)
f2(2, 2)
# python test.py
f1 parameter: 1 2
f1 result: 2
f2 parameter: 2 2
f2 result: 4

装飾器でも上記の方法を実現し、装飾する関数に装飾器定義の機能を追加したことがわかりますが、この方法はもっと簡潔に見えますか?
では、装飾器の使い方を深く学び続けます.
5.8.1無パラメータ装飾器
      1:              
    #!/usr/bin/python
    # -*- coding: utf-8 -*-
    def deco(func):
        return func
    def f1():
        print "Hello world!"
    myfunc = deco(f1)
    myfunc()  
    # python test.py
    Hello world!
    
      2:     "@"     
    #!/usr/bin/python
    # -*- coding: utf-8 -*-
    def deco(func):
        return func
    @deco
    def f1():
        print "Hello world!"
    f1()
    # python test.py
    Hello world!

方式1は,1つの関数をパラメータとして装飾器関数に渡す.
方式2文法糖を用いても同様の効果が得られた.
実は2つの方式の結果は同じで、方式1は装飾器を使うたびに先に変数を与えなければならなくて、方式2は装飾器を使う時直接文法糖"@"で引用して、もっと便利に見えて、実際のコードの中で普通はすべて文法糖を使います.
2)パラメータ付装飾器
    #!/usr/bin/python    
    # -*- coding: utf-8 -*-
    def deco(func):
        def f(a, b):
            print "function name: %s" % func.__name__   # __name__        ,           
            return func(a, b)   #       func           
        return f
    @deco
    def f1(a, b):
        print "Hello world!"
        print a + b
    f1(2, 2)
    # python test.py
    function name: f1
    Hello world!
    4

3)不定パラメータ
    #!/usr/bin/python    
    # -*- coding: utf-8 -*-
    def log(func):
        def deco(*args, **kwargs):
            print "function name: %s" % func.__name__
            return func(*args, **kwargs)
        return deco
    @log
    def f1(a, b):
        print "f1() run."
        print a + b
    f1(1,2)
    # python test.py
    function name: f1
    f1() run.
    3

4)装飾器プラスパラメータ
    #!/usr/bin/python    
    # -*- coding: utf-8 -*-
    #     ,  log    deco  ,        deco,     _deco  
    def log(arg):
        def deco(func):
            def _deco(*args, **kwargs):
                print "%s - function name: %s" % (arg, func.__name__)  
                return func(*args, **kwargs)
            return _deco
        return deco
    @log("info")
    def f1(a, b):
        print "f1() run."
        print a + b
    f1(1,2)
    # python test.py
    info - function name: f1
    f1() run.
    3

もう1つの例を挙げると、関数出力文字列に色が付きます.
    #!/usr/bin/python    
    # -*- coding: utf-8 -*-
    def fontColor(color):
        begin = "\033["
        end = "\033[0m"
        d = {
            'red':'31m',
            'green':'32m',
            'yellow':'33m',
            'blue':'34m'
        }
        def deco(func):
            print begin + d[color] + func() + end
        return deco
    @fontColor("red")
    def f():
        return "Hello world!"
    @fontColor("green")
    def f2():
        return "Hello world!"

装飾器処理方式は高次関数の条件を満たすことが分かるので,装飾器も高次関数である.
利点:関数を変更せずに装飾器に機能を追加し、コードの再利用性を高め、可読性を高める.
5.9匿名関数
匿名関数:関数名と文ブロックを定義する必要がない関数の形式を定義します.そのため、コードロジックは制限され、コード量を減らし、可読性を高めます.
Pythonでの匿名関数はlambdaです.
例:defキーワードとlambda関数定義関数の違いを説明します.
#     
>>> def func():
...   return "Hello world!"
...
>>> func()
>>> def func(a, b):
...   return a * b
...
>>> func(2, 2)
4

#     
>>> f = lambda:"Hello world!"
>>> f()
'Hello world!'
>>> f = lambda a, b: a * b   #          ,      
>>> f(2, 2)
4

Lambda関数の1行は1つの関数機能に書かれ、関数プロセスを定義することを省き、コードをより簡素化します.
5.10高次関数の組み込み
   5.10.1 map()
構文:map(function,sequence[,sequence,...])->list
シーケンス内の要素を関数処理で新しいリストに戻します.
例:
    >>> lst = [1,2,3,4,5]    
    >>> map(lambda x:str(x)+".txt", lst)
    ['1.txt', '2.txt', '3.txt', '4.txt', '5.txt']

   5.10.2 filter()
構文:filter(function or None,sequence)->list,tuple,or string
シーケンス内の要素を関数処理によって新しいリスト、メタグループ、または文字列に戻します.
たとえば、リスト内の奇数をフィルタします.
    >>> lst = [1,2,3,4,5]    
    >>> filter(lambda x:x%2==0, lst)
    [2, 4]

   5.10.3 reduce()
構文:reduce(function,sequence[,initial])->value
reduce()は二元演算関数であるため,二元操作関数のみを受け入れる.
たとえば、リストの合計を計算します.
    >>> lst = [1,2,3,4,5]    
    >>> reduce(lambda x,y:x+y, lst)
    15

最初の2つの要素を3に加算し、結果を3番目の要素に6に加算します.これがreduce()関数機能です.