Python関数と装飾器

13243 ワード

関数の定義と呼び出し
定義:defキーワードの先頭に、スペースの後に関数名とカッコ()が接続され、最後に英語のコロン「:」があります.
関数名:Pythonでは関数が変数であるため、関数名も変数の命名制約に従います.数値アルファベットの下線で構成され、数値で始まることができず、関数を記述する機能を持つ必要があります.
かっこ:必ずつけなければならないので、なぜかっこがあるのか聞かないでください.とにかくかっこをつければいいです. :各関数は、機能とパラメータについて説明する必要があります.関数の下の最初の行に書く必要があります.コードの読み取り可能性を向上させます. :関数名()関数名に括弧を付けます!実行しない
関数を
>>> def bar():
...   print("hello function!
") ... >>> bar # , >>> bar() # hello function!

私たちは今、sum()という組み込み関数の機能を実現するために、1つの関数に2つの数の加算と操作を完了させたいと思っています.
def mysum(a,b):
    sum = a+b
result = mysum(1,2)
print(result)

結果:None
どうすれば彼をsum()のようにすることができますか?今の関数の機能はすでに完璧に実現して、しかし誰もこの関数がすでに任務を完成したことを知りません!どのように自分を証明して、関数は実行の結果を返してみんなに見せます!
関数の最後にreturnキーを付ける
def mysum(a,b):
    sum = a+b
    return sum

result = mysum(1,2)
print(result)

結果:3
この戻り値を詳細に説明する
1、returnキーワードがない
このような場合はPythonで許可され、デフォルトのPythonはNoneに戻ります
2、returnはありますが、後には値がありません
def bar():
    print(“I’m bar!”)
    return
print(bar)

結果:None
3、returnがあれば戻り値があり、もちろん上記の例mysumが例であり、明らかに許可されており、この戻り値は変数名で参照することができる.
4、returnあり複数の戻り値あり
def mysum(a,b):
    sum = a+b
    return a,b,sum

result = mysum(1,2)
print(result)

結果:(1,2,3)
戻り値が複数の値の場合、Pythonはいくつかのオブジェクトを1つのメタグループにカプセル化します.同時に、複数の変顔でそれぞれ対応する戻り値を取ることもできます.また、1つの変数でこのメタグループに取ることもできます. Pythonでは、カンマ","で分割された連続するいくつかの値が、1つのメタグループとみなされる
>>> 1,2,3
(1, 2, 3)

シーケンス解凍
>>> a,_,_,_,b = [i for i in range(5)]
>>> a
0
>>> b
4
>>> a,*_,b,c = 'unpack string'
>>> a
'u'
>>> b
'n'
>>> c
'g'
>>> _
['n', 'p', 'a', 'c', 'k', ' ', 's', 't', 'r', 'i']
>>> type(_)

>>> a,_,c = {1:'a',2:'b',3:'c'}
>>> a
1
>>> c
3
>>> *_,end = (1,2,3,4,5,6)
>>> end
6

returnの役割:
関数が呼び出されると、Pythonインタプリタは、returnキーが実行されると関数を終了し、結果を返します.その後のコードは実行されません.
def mysay():
    print("hello first!")
    print('        '.center(30, "*"))
    return
    print("hello second!")
mysay()

結果:
hello first! *********** ***********
このように、returnは何も書かないのと直接returnを書かないのとでは違いがあり、returnは関数がどこで終わるかを示すことができ、returnがなければ途中で関数を間違えていなければ、完全な関数体のコードを実行することができます.
関数パラメータ
2つの数字の和のとき,関数名の後の括弧に2つのパラメータa,bがあるのを見た.この2つの数を関数のパラメータと呼び,関数呼び出し時に特定のパラメータが伝達されることを関数実行時の実パラメータと呼ぶ.
パラメータは複数ありますが、定義と呼び出しの際にはカンマで分割する必要があります.
関数のパラメータは、位置パラメータ、キーワードパラメータ、デフォルトパラメータ、動的パラメータに分けられます.
注意: 。 Python : 。実参について言えば
位置パラメータ
この方法では,実パラメータを厳密に形パラメータの位置に従って伝達しなければならない.そうしないと,結果は我々が望んでいる様子ではなく,パラメータが多ければ少ないほど報告が間違ってしまう.
def func(arg1,arg2,arg3)
    pass
func('name','age','salary')     #    :arg1 = 'name'  arg2 = ’age'  arg3 = ’salary'

キーワード伝達パラメータ
この方法では、位置を気にする必要はありません.デフォルト値があります.実パラメータを入力しなくても、エラーは報告されません.
def func(arg1,arg2,arg3)
    pass
func(arg3=’name',arg2 = ’age', arg1 = ’salary')     #    :arg1 = 'salary’  arg2 = ’age'  arg3 = ’name’

混用する
2つの方法が混在する場合、 , , , 。に従う必要があります.
def func(arg1,arg2,arg3)
    pass
func('name',arg3 = ’age',arg2 = ’salary')     #    :arg1 = 'name'  arg2 = ’salary’  arg3 = ’age’

形参について言えば :位置パラメータは、どのような方法でパラメータを伝達しても、単一の対応する値に入力されなければならない. :パラメータのキーワードパラメータ、すなわちパラメータのデフォルト値.多くの場合、変更する必要のない値をデフォルト値に設定します.
def style(id, color="blue"):
    """          ,            
            color       
    """
    return id, color
 
print(style(3,'red'))
print(style(3))
:可能な限り可変タイプを避けることをデフォルト値(コンテナタイプを避ける)と呼びます.これらはオブジェクトの参照の集合であるため、パラメータが関数に入力されると、すべての操作はコンテナ自体を修正するのではなく、オブジェクトを修正します.変化の累積効果をもたらした.
def init_data(name,lst =[]):
    lst.append(name)
    return lst
 
print(init_data('monkey'))
 
print(init_data('JIAJIA'))
 
['monkey']
['monkey', 'JIAJIA']
#              ,          

パラメータと実パラメータは共有オブジェクトであり、lstは可変オブジェクトの参照であり、関数が呼び出されるたびにデフォルトで同じ可変オブジェクトであり、同じアドレスであり、変更するたびに同じ可変オブジェクトである.
ダイナミックパラメータ
def bar(id,age=18,*args,**kwargs):
    print(id,age,args,kwargs)
 
bar(1608,'    1','    2','     3',sex='famale')

# 1608
#     1
# ('    2', '     3')
# {'sex': 'famale'}

位置パラメータは位置パラメータに従って渡さなければなりません.余分な位置パラメータはメタグループにカプセル化されてargsに保存され、余分なキーワードパラメータは辞書にカプセル化されてkwargsにカプセル化されます.
関数のネスト
関数のネストされた呼び出しは、関数で別の関数を呼び出すことです.
def func():
    print('func')
 
def bar():
    print('The bar')
    func()
 
bar()     #  bar   func()

定義#テイギ#
def func():
    funcname = 'func'
    print(funcname)
    def bar():
        funcname = 'bar'
        print(funcname)
        def foo():
            funcname = 'foo'
            print(funcname)
        foo()
    bar()
func()

nonlocalは、ネストされた関数でnonloaclキーワードを使用して、本層関数ではなく上位関数変数として変数を宣言します.
nonlocalの使用規則:
1、彼はある関数の埋め込み関数で使用しなければならない.
2、彼が宣言した変数の前に、現在の関数に同名の変数は存在できません.
注:グローバル変数にバインドできません.ローカル変数のみです.
def outfunc():
    name = 'outfunc'
    def infunc():
        nonlocal name
        name = 'infunc'
        print(name)
    infunc()
    print(name)
outfunc()

さようチェーンドメイン
name = 'error! '
def outfunc():
    print('Outfunc name ="{}"'.format(name))
 
def func0():
    name = 'monkey'
    def func1():
        print('In func1 name ="{}"'.format(name))
        def func2():
            outfunc()
            print('In func2 name ="{}"'.format(name))
        func2()
    func1()
    outfunc()
func0()

# In func1 name ="monkey"
# Outfunc name ="error! "
# In func2 name ="monkey"
# Outfunc name ="error! "

関数は実行前に関数体の変数の定義をチェックし、このレイヤで見つからない場合は、その外側のレイヤを探し、見つからない場合は外側のレイヤに行き、グローバルが見つかるまで、見つからない場合はエラーを報告し、見つかったら使用します.
高次関数 :関数のパラメータまたは戻り値が関数の関数であり、高次関数(戻り値が関数自体である場合、関数の再帰と呼ばれる)と呼ばれる.
def bar():
    print("  This is bar!")
 
def foo(func):
    print("This is foo:")
    func()
 
foo(bar)

barを関数fooのパラメータとして入力し、fooでbar、すなわちfooのfuncを実行することができる.
def foo():
    print("This is foo:")
    def foo_inner():
        print("This is foo_inner")
    return foo_inner
ret = foo()
ret()


foo_innerは戻り値として出力され,関数外で実行される.
関数クローズ
ネストされた関数で、内部関数が外部関数を呼び出す変数
def outer():
    name = 'outer'
    def inner():
        print(name)
    print(inner.__closure__)
outer()

# (,)

クローズドパッケージかどうかをclosureメソッドで判断
cellで始まると、これが閉パケットで、Noneが閉パケットではないことを返します.
0 x 100 e 51538はinnerのアドレスです
strは外層変数を使用するタイプで、後ろにメモリアドレスがあります.
閉じた正しい姿勢
def outer():
    name = 'outer'
    def inner():
        print(name)
    return inner
func = outer()
func()

このようにして、nameは長く保存されます.funcはouter内部関数のアドレスを受け入れ、内部関数はouter下の変数nameを使用するので、この変数はメモリに長く保持されます.
1つの内部関数で外部役割ドメイン(グローバル役割ドメインではない)の変数を参照しますが、グローバル役割ドメインではない場合、この内部関数は閉パッケージです.
実際、閉パッケージの用途/利点は2つあります. ( )
デコレーション ? ?
デザイナは、関数コードを変更せず、関数呼び出し方式を変更しない前提で、関数に新しい機能を追加する必要があります.
装飾器の本質は関数です
Pythonでは関数と変数は本質的に同じであり、変数は関数オブジェクトを指すことができる.関数は変数です!
初級バージョン装飾器
装飾された関数を定義します.
def func():
    print("      ")

バージョン1
decratorを定義したいのですがv 1関数は装飾された関数を受け入れ,新しい機能を加えてこの関数のアドレスを返す.次にfunc変数をdecrator_に指定します.v 1関数、パラメータはfuncで大成功したようです.
def decrator_v1(foo):
    print('     ')
    foo()
    return foo
func = decrator_v1(func)
func()

#      
#       
#       

おや~どうして予想した结果と违うの??最初から最後まで撫でてきた!スロットfunc関数は両側に実行され、2回目は鳥の新しい機能がありません!
1 func=decrator_を実行するとv 1(func)のときにdecratoe_が呼び出されましたv 1このとき、私たちが望む結果が実行されます.
2しかし、戻りfooが変数funcによって受け入れられると、funcを実行し、fooを実行します.このときのfooは、元の定義時のfunc関数です.したがって,2回の実行が現れ,2回目はfuncのみが実行された.
3私たちは新しい機能とfunc自体をカプセル化したいと思っています.一緒に実行するには、このような薄っぺらなことはありません.明らかに、自然に関数の閉パッケージを考えて、関数にカプセル化しましょう.では、カプセル化しましょう.
バージョン2
def add_way(func):               #1
    def wrapper():                          #3
        print('     ')                          #6
        func()                                          #7
    return wrapper                              #4
 
func = add_way(func)                 #2
func()  

#      
#       

パーフェクト~ついにミッションを果たした~!
コードを説明します.
バージョンに対して定義されたwrapper関数は、バージョン1で呼び出されるとすぐにadd_を実行することを解決するために使用されます.way()の問題です.
現在は基本的に機能上のニーズを満たしており,呼び出し方式を変更したり,元の関数のコードを変更したりしていない.Pythonは文法糖「@」を提供してfunc=add_を完成させてくれましたway(func)この件!
構文糖バージョン
def add_way(func):
    def wrapper():
        print('     ')
        func()
    return wrapper
 
@add_way
def func():
    print("     :     ")
 
func()

今まで、デコレーションはほぼ成型していました~!
私たちが何をしたかを振り返ってみましょう.
1、高次関数を利用して被装飾関数をパラメータとして装飾器オブジェクトに出入りし、その後(関数のネスト)関数で新しい方法(機能)をカプセル化した後、カプセル化後のオブジェクト2を装飾器オブジェクト(関数)の戻り値とし、その後、被装飾関数名を装飾器オブジェクトの参照とし、これにより,アクセラレータが関数を変更しないコードが方法を変更しない呼び出し方式の新規機能を実現する.
もうちょっと磨きましょう.
装飾関数のプロパティが変化し、一部のプロパティで動作するモジュールが動作しない可能性があります.
print('         ',func.__doc__)
print('        ',func.__name__)
print('          ',func.__hash__)
             ~     __name__                ~ 

被装飾関数はどのように伝参しますか?
究極のバージョンの装飾
研磨後の装飾器:
1、from functions import wrapsはwraps装飾器で私たちのカスタム装飾器に伝わる関数、すなわち受信した関数を装飾します.
2、wrapperはfunc(被装飾関数の)のすべてのパラメータを受け入れる
3、wrapper関数はfunc(被装飾関数)の戻り値を返します.
#!/usr/bin/env python3
#_*_ coding: utf-8 _*_
__author__ = "monkey"
 
 
from functools import wraps
#         
#          wraps     
"""Decorator factory to apply update_wrapper() to a wrapper function
 
   Returns a decorator that invokes update_wrapper() with the decorated
   function as the wrapper argument and the arguments to wraps() as the
   remaining arguments. Default arguments are as for update_wrapper().
   This is a convenience function to simplify applying partial() to
   update_wrapper()."""
 
 
def func( *args,**kwargs):
    '''
    :param args:         
    :param kwargs:           
    :return:          
    '''
    print('  func')
    return args,kwargs
 
print('         ',func.__doc__)
print('        ',func.__name__)
print('          ',func.__hash__)
 
 
def decrator_end(func):               #1
    '''
    :param func:          ,           
    :return:              
    '''
    @wraps(func)        #     wrap              func     
    def wrapper(*args,**kwargs):                          #3
        '''
        wrapper      wrapper   func       
        :param args:          
        :param kwargs:       
        :return:           
        '''
        print('     ')                          #6
        ret = func(*args,**kwargs)                                          #7
        return ret
 
    return wrapper                              #4
 
func = decrator_end(func)                 #2
x = func('hook','monkey',name='monkey')
print(x)
 
print('         ',func.__doc__)
print('        ',func.__name__)
print('          ',func.__hash__)

結果:
         
 
    :param args:         
    :param kwargs:           
    :return:          
     
         func
           
     
  func
(('hook', 'monkey'), {'name': 'monkey'})
         
 
    :param args:         
    :param kwargs:           
    :return:          
     
         func
           

注意装飾後の属性変化を観察すると,完全に被装飾関数そのもののように見えるが,_hash__方法私たちはそれが元のものではないことを知っています.hash__物事はいつもそんなに完璧ではありません.需要に達すればいいです.これでいいのですが、本当に修正します_hash__ クラスの__を自分で関数に書き換えることができます.hash__方法それでも彼は元の関数ではなく、メモリが異なり、どのように変更しても彼らが2つのオブジェクトであることを変えることができないような事実にすぎないが、使用にとって、ユーザーや呼び出し者には透明で、パッケージされた後、彼らが1つのオブジェクトであるかどうかという問題には誰も注目しない.だから完璧を厳しく求める必要はありません!