Python--高次関数、関数ネスト、名前空間および変数の役割ドメイン、閉パッケージ、装飾器

24330 ワード

1.高次関数(map/reduce/filter)
高次関数とは、関数のパラメータが関数であることを意味します.
この記事はいくつかのよく使われる高次関数をまとめます:map/reduce/filter
map関数、reduce関数、filter関数は、いずれもPythonの組み込み関数です.
map関数
map関数の役割は、1つの関数を1つのシーケンスの各要素に作用させることであり、1行のコードで完成することができ、私たちが普段好きなループを使う必要はありません.mapは演算を抽象化し,この関数が1つのシーケンスの各要素に対して同じ操作を行ったことを一目で見ることができる.map()関数は2つのパラメータを受信し、1つは関数であり、1つはIterableであり、mapは入力した関数をシーケンスの各要素に順次作用させ、結果を新しいIteratorとして返す.
最初のパラメータは、次のシーケンスの各要素が入力される関数数名のみを必要とすることに注意してください.mapの結果はIteratorであり,Iteratorは不活性シーケンスであるためlist()関数によりシーケンス全体を計算してlistを返す.
def mul(x):
    return x*x
    
r = list(map(mul, [1,2,3,4,5,6,7,8,9]))
#   :
r = list(map(lambda x:x*x, [1,2,3,4,5,6,7,8,9]))

reduce関数
reduceも1つの関数を1つのシーケンスに作用し、2つのパラメータを受信し、前の2つのパラメータの計算結果を最初のパラメータとしてシーケンスの次の要素と累積計算しなければならない.すなわち、
reduce(f, [x1, x2, x3, x4,x5]) = f(f(f(f(x1, x2), x3), x4),x5)

例1:
# list       
from functools import reduce
L=[1,2,5,8]
print(reduce(lambda x,y: x*10+y, L))#1258

注意:
reduce()functoolsからimport
Lambda関数は匿名関数であり,定義関数を省くためにlambda関数を用いて簡単に行うことができる.Lambda関数のパラメータは0つ以上あり、パラメータはカンマで区切られています.パラメータの後にはコロン、式が続き、計算結果が返されます.
例2:
#         
from functools import reduce
def str2int(s):
    def str2num(x):
        DIGITS={'0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9}
        return DIGITS[x]
    return reduce(lambda y,z:y*10+z, map(str2num,s))
print('9748')#9748

#          
from functools import reduce
def str2float(s):
    DIGITS = {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9}    
    def str2num(a):
        return DIGITS[a]
    return reduce(lambda x,y:x*10+y, map(str2num, s.split('.')[0])) +reduce(lambda x,y:x*10+y, map(str2num, s.split('.')[1]))*10**(-len(s.split('.')[1]))

 
ろ過関数
filter関数はmap関数と同様に、2つのパラメータを受信します.1つは関数で、2つ目はシーケンスです.filter関数は、入力された関数をシーケンスの各要素に順次作用させ、関数の戻り値がTrueかFalseかに基づいて要素の保持または削除を決定します.
シーケンス内のすべての偶数を見つけたい場合などです.
#            
def not_empty(s):
    return s and s.strip()

list(filter(not_empty, ['A', '', 'B', None, 'C', '  ']))
#   : ['A', 'B', 'C']
#            (         ,      )
def is_palindrome(n):
    s=str(n)
    return s[::1] == s[::-1]

#   :
output = filter(is_palindrome, range(1, 200))
print(list(output))
#  :[1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 22, 33, 44, 55, 66, 77, 88, 99, 101, 111, 121, 131, 141, 151, 161, 171, 181, 191]

 
2.関数ネスト
関数ネストは、関数ネスト定義と関数ネスト呼び出しに分けられます.
関数ネスト定義は、1つの関数定義の関数内で別の関数を定義します.
#        
def f():
    print("f")
    def f1():
        print("f1")
        def f2():
            print("f2")
        f2()
    f1()

f()
#  :f
#       f1
#       f2

ネスト定義された関数をreturnのパラメータとして使用することもできます(この使い方は以下の閉パッケージで詳しく説明します).次のコードは上記のコード出力結果と一致します.
def f():
    print("f")
    def f1():
        print("f1")
        def f2():
            print("f2")
        return f2
    return f1

t1 = f()#t1  f1
t2 = t1()#   f1(),t2  f2
t3 = t2()#   f2()

 
関数ネスト呼び出しとは、1つの関数体の中で別の関数を呼び出すことです.
例:func 2でfunc 1関数が呼び出された
#        
def
func1(x,y): return x*y def func2(a,b,c): r1 = func1(a,b) r2 = func1(a,c) r3 = func1(b,c) print(r1,r2,r3) func2(2,5,9) # :10 18 45

3.名称空間及び変数作用域
名前空間とは名前を格納する場所であり,Pythonにおける変数と値のバインド関係の場所である.内蔵ネームスペース、グローバルネームスペース、ローカルネームスペースの3つに分けられます.
内蔵ネームスペース:Pythonインタプリタ起動時に最初にロードされた
≪グローバル名空間|Global Namespace|oem_src≫:スクリプトを実行すると、スクリプトの内容に基づいてグローバル名空間がロードされます.
≪ローカル名空間|Local Namespace|emdw≫:スクリプトの実行中に関数を呼び出すと、ローカル名空間が一時的に生成されます.
内蔵ネームスペースとグローバルネームスペースに対応する役割ドメインはグローバルであり、ローカルネームスペースに対応する役割ドメインはローカルである.
したがって、スクリプトでは、メモリを消費し、実行速度を遅らせるために、グローバルで有効な変数をできるだけ少なく定義する必要があります.
 
変数の役割ドメインは定義時に固定され、関数内のパラメータ変数は、関数定義段階で固定され、関数の呼び出し位置とは関係ありません.
関数内で定義された変数は一般的に関数内部でのみ機能し、関数外部では使用できません.
a = 99
print(a)#99
def addn():
    a = 100#     a          
    print(a)
    a += 1
    print(a)
addn()#100 101
print(a)#99
a = 200
print(a)#200

 
4.クローズ
上記の関数ネスト定義の第2の部分の例は、実際には閉パッケージです.閉包して返される関数オブジェクトは、関数オブジェクトだけでなく、関数の外に役割ドメインが包まれています.これにより、関数がどこで呼び出されても、自分の外層ラップの役割ドメインが優先的に使用されます.
閉パケット内の内層の関数はnonlocalキーワードによって外層関数で定義された変数を使用することができる.
次の例では、nがnonlocalと指定されていない場合、プログラムがエラーを報告します.
#       
def counter():
    n=0
    def incr():
        nonlocal n
        x=n
        n+=1
        return x
    return incr

c=counter()
print(c())#1
print(c())#2
print(c())#3

閉パケットを使用すると計算を遅延させ、関数を先にパケットし、必要に応じて呼び出すことができます.
 
5.装飾器
Pythonプログラムを書くときにログ情報を出力する必要があることが多いが、関数内部のコードを変更することを望んでいない場合は、出力ログのような関数を動的に追加し、装飾器で実現することができる.
アクセサリーには、パラメータとパラメータを持たないアクセサリーの2種類があります.
#        
import functools
def log(func):
@functools.wraps(func)
def wrapper(*args, **kw): print('call %s():' % func.__name__) return func(*args, **kw) return wrapper
#       
#text        
import functools
def log1(text): def decorator(func):
@functools.wraps(func)
def wrapper(*args, **kw): print('%s %s():' % (text, func.__name__))#func.__name__ func return func(*args, **kw) return wrapper return decorator

アクセラレータの入力は関数で、戻り値も関数です.定義する関数の前にデザイナを置きます(@)を使用すると、定義した関数を呼び出すときにデザイナが1回実行されるようになります.
@log
def now():
    print('2019-08-10')
@log1('tt')
def now():
    print('2019-08-10')

では、上記パラメータを持たない装飾器について、now()関数を呼び出す際の出力は、now=log(now)を実行したことに相当する
>>>now()
call now():
2019-08-10

上記パラメータ付きアクセラレータでnow()関数を呼び出すときの出力は、now=log('tt')(now)を実行したことに相当する.
>>>now()
tt now():
2019-08-10

複数のアクセラレータは、関数定義の上に同時に配置できます.
@log1
@log2
@log3
def now():
    pass

#   :now = log1(log2(log3(now)))

最後に装飾器の例を置きます.
1つのデザイナで複数の関数に認証機能を加え、ログインに成功した後、タイムアウト時間内に他の操作(次の関数を実行)を行うには、繰り返しログインする必要がなく、タイムアウト時間を超えた場合は、再ログインする必要があります.
#              ,                   (       )      ,       ,       
import time,random

user = {'name':None, 'login_time':False, 'timeout':0.0000002}

def log(func):
    def wrapper(*args,**kwargs):
        if user['name']:
            timeout=time.time()-user['login_time']
            if timeout < user['timeout']:
                return func(*args,**kwargs)
            else:
                print("    ,     !")
        name=input('name: ').strip()
        password=input('pwd: ').strip()
        if name == 'tt' and password == '123':
            user['name']=name
            user['login_time']=time.time()
            res=func(*args,**kwargs)
            return res
    return wrapper
        
        
@log
def first():
    print("welcome to login!")
    
            
@log
def home():
    print("welcome to home!")
    
first()
home()

初回ログイン後にfirst関数のコードを実行し、home関数を実行するとタイムアウト時間を超えた場合、出力は以下のようになり、ユーザー名とパスワードを再入力する必要があります.
name: tt
pwd: 123
welcome to login!
    ,     !
name:

タイムアウト時間を超えない場合、home関数は直接実行でき、出力されます.
name: tt
pwd: 123
welcome to login!
welcome to home!