>  기사  >  백엔드 개발  >  Python 고급: 고차 함수에 대한 자세한 설명

Python 고급: 고차 함수에 대한 자세한 설명

高洛峰
高洛峰원래의
2017-03-09 11:03:121546검색

이 기사에서는 Python 고급에 대해 설명합니다. 고차 함수에 대한 자세한 설명이 필요한 친구는

함수 프로그래밍

함수는 Python 내장에서 지원하는 일종의 캡슐화를 참조하세요. . 큰 코드 섹션을 함수로 분할하고 함수를 계층별로 호출함으로써 복잡한 작업을 간단한 작업으로 분해할 수 있습니다. 함수는 프로세스 지향 프로그래밍의 기본 단위입니다.

함수형 프로그래밍(추가 단어 "공식"에 유의하세요) - 함수형 프로그래밍은 프로세스 지향 프로그래밍에 속할 수도 있지만 그 아이디어는 수학적 계산에 더 가깝습니다.

먼저 컴퓨터와 컴퓨팅의 개념을 이해해야 합니다.

컴퓨터 수준에서는 CPU가 덧셈, 뺄셈, 곱셈, 나눗셈 등의 명령어 코드와 다양한 조건부 판단, 점프 명령어를 실행하므로 컴퓨터와 가장 가까운 언어는 어셈블리어이다.

계산이란 수학적 의미의 계산을 의미하며, 계산이 추상적일수록 컴퓨터 하드웨어와는 거리가 멀어집니다.

프로그래밍 언어에 해당합니다. 즉, 하위 수준 언어는 컴퓨터에 더 가깝고 추상화 수준이 낮으며 실행 효율성이 더 높습니다. 예를 들어 C 언어에 더 가깝습니다. Lisp 언어와 같이 추상화 수준이 높고 실행 효율성이 낮습니다.

함수형 프로그래밍은 추상화 수준이 높은 프로그래밍 패러다임입니다. 순수 함수형 프로그래밍 언어로 작성된 함수에는 변수가 없습니다. 따라서 어떤 함수에 대해서도 입력이 확실하면 출력이 됩니다. 확실합니다. 순수 함수는 부작용이 없다고 합니다. 변수 사용을 허용하는 프로그래밍 언어에서는 함수 내부의 변수 상태가 불확실하기 때문에 같은 입력이라도 다른 출력이 나올 수 있기 때문에 이런 종류의 함수에는 부작용이 있습니다.

함수형 프로그래밍의 특징 중 하나는 함수 자체를 매개변수로 다른 함수에 전달할 수 있고, 함수를 반환할 수도 있다는 것입니다!

Python은 함수형 프로그래밍을 부분적으로 지원합니다. Python은 변수 사용을 허용하므로 Python은 순수한 함수형 프로그래밍 언어가 아닙니다.

고차함수

고차함수를 영어로 Higher-order function이라고 합니다. 고차 함수란 무엇입니까? 실제 코드를 예로 들어 개념을 단계별로 심화시킵니다.

변수는 함수를 가리킬 수 있습니다.

Python의 내장 절대값 함수인 abs()를 예로 들어 이 함수를 호출하려면 다음 코드를 사용하세요.

> >>abs(-10)10

그런데 ABS만 쓴다면 어떨까요?

>>> abs

abs(-10)은 함수 호출이고, abs는 기능 그 자체입니다.

함수 호출 결과를 얻으려면 결과를 변수에 할당할 수 있습니다:

>>> x = abs(-10)>>> >

그런데 함수 자체가 변수에 대입된다면 어떨까요?

>>> f = abs

>>> f


결론: 함수 자체 is 또한 변수에 할당될 수 있습니다. 즉, 변수는 함수를 가리킬 수 있습니다.

변수가 함수를 가리키는 경우 변수를 통해 함수를 호출할 수 있나요? 다음 코드로 확인하세요.

>>> f = abs>>> f(-10)10


성공! 이는 변수 f가 이제 abs 함수 자체를 가리킨다는 것을 의미합니다. abs() 함수를 직접 호출하는 것은 변수 f()를 호출하는 것과 똑같습니다.

함수 이름도 변수입니다

그럼 함수 이름은 뭔가요? 함수 이름은 실제로 함수를 가리키는 변수입니다! 함수 abs()의 경우, 함수 이름 abs는 절대값을 계산할 수 있는 함수를 가리키는 변수로 간주할 수 있습니다!

abs가 다른 객체를 가리키면 어떻게 되나요?

>>> abs = 10

>>> abs(-10)
추적(가장 최근 호출 마지막):
파일 "" , 첫 번째 줄,
TypeError: 'int' 개체를 호출할 수 없습니다

abs를 10으로 지정한 후에는 abs(-10)을 통해 함수를 호출할 수 없습니다! abs 변수는 더 이상 절대값 함수를 가리키지 않고 정수 10을 가리키기 때문입니다!

물론 실제 코드를 이렇게 작성하면 안 됩니다. 함수 이름도 변수라는 점을 설명하기 위함입니다. abs 함수를 복원하려면 Python 대화형 환경을 다시 시작하세요.

참고: abs 함수는 실제로 import 내장 모듈에 정의되어 있으므로, 다른 모듈에 적용되도록 abs 변수의 포인터를 수정하려면 import 내장 내장.abs = 10을 사용하세요.

함수 전달

변수가 함수를 가리킬 수 있고 함수의 매개변수가 변수를 받을 수 있으므로 함수는 또 다른 함수를 매개변수로 받을 수 있는 것을 상위 함수라고 합니다. 주문 기능 .

가장 간단한 고차 함수:

def add(x, y, f): return f(x) + f(y)

when When add(-5, 6, abs)를 호출하고 매개변수 x, y 및 f는 각각 -5, 6 및 abs를 받습니다. 함수 정의에 따라 계산 프로세스를 다음과 같이 추론할 수 있습니다.

x = -5
y = 6
f = abs
f(x) + f(y) ==> abs(-5) + abs(6) ==> 11return 11

코드로 확인하세요:

>>> add(-5, 6, abs)11

고차 함수를 작성하면 함수의 매개변수는 다른 함수를 받을 수 있습니다.

요약

이러한 함수를 고차 함수라고 합니다. 이러한 고도로 추상적인 프로그래밍 패러다임을 말합니다.

map/reduce

Python에는 map() 및 Reduce() 함수가 내장되어 있습니다.

Google의 유명한 논문 "MapReduce: Simplified Data Processing on Large Clusters"를 읽어보셨다면 map/reduce의 개념을 대략적으로 이해하실 수 있습니다.

우선 지도를 살펴보겠습니다. map() 함수는 두 개의 매개변수를 받습니다. 하나는 함수이고 다른 하나는 Iterable입니다. map은 전달된 함수를 시퀀스의 각 요소에 차례로 적용하고 결과를 새 Iterator로 반환합니다.

예를 들어 f(x)=x2라는 함수가 있고 이 함수를 목록 [1, 2, 3, 4, 5, 6, 7, 8, 9] map()을 사용하여 다음과 같이 구현할 수 있습니다.

Python 고급: 고차 함수에 대한 자세한 설명

이제 Python 코드를 사용하여 구현합니다.

>> > def f(x): ... return x * x
...>>> r = map(f, [1, 2, 3, 4, 5, 6, 7, 8, 9])>>> ; list(r)
[1, 4, 9, 16, 25, 36, 49, 64, 81]

첫 번째 매개변수는 map()은 f, 즉 함수 객체 자체입니다. 결과 r은 Iterator이고 Iterator는 게으른 시퀀스이므로 list() 함수를 사용하여 전체 시퀀스를 계산하고 목록을 반환할 수 있습니다.

map() 함수가 필요하지 않다고 생각할 수도 있습니다. 루프를 작성하고 결과를 계산할 수 있습니다.

L = []for n in [1, 2, 3 , 4, 5, 6, 7, 8, 9]:
L.append(f(n))
print(L)

은 실제로 가능하지만 위의 루프 코드에서 "목록의 각 요소에 f(x)를 적용하여 결과적으로 새로운 목록을 생성한다"는 것을 한눈에 알 수 있나요?

따라서 map()은 실제로 연산 규칙을 ​​추상화하므로 간단한 f(x)=x2를 계산할 수 있을 뿐만 아니라 다음과 같은 복잡한 함수도 계산할 수 있습니다. 이 목록의 모든 숫자를 문자열로 변환합니다:

>>> list(map(str, [1, 2, 3, 4, 5, 6, 7, 8, 9])) '1', '2', '3', '4', '5', '6', '7', '8', '9']

코드 한 줄만 .

Reduce 사용법을 살펴보세요. Reduce는 시퀀스 [x1, x2, x3, ...]에 함수를 적용합니다. 이 함수는 두 개의 매개변수를 받아야 하며 시퀀스의 다음 요소로 결과의 누적 계산을 계속합니다. 🎜>reduce(f, [x1, x2, x3, x4]) = f(f(f(x1, x2), x3), x4)


예를 들어 시퀀스를 합산하면 다음을 달성하기 위해 Reduce를 사용할 수 있습니다:

>>> from functools import Reduce>>> def add(x, y):... return x + y

...> >> Reduce(add, [1, 3, 5, 7, 9])25



물론 합계 연산은 Python 내장 함수 sum()을 사용하여 직접 수행할 수 있습니다. Reduce 를 사용할 필요가 없습니다.

그러나 시퀀스 [1, 3, 5, 7, 9]를 정수 13579로 변환하려는 경우에는 축소가 유용할 수 있습니다.

>>> import Reduce>>> def fn(x, y):... return x * 10 + y

...>>> ]) 13579



이 예제 자체는 별로 유용하지 않습니다. 그러나 문자열 str도 시퀀스라고 생각하면 위의 예제를 약간 변경하고 map()을 사용하여 변환을 작성할 수 있습니다. 정수용 함수:

>>> from functools import Reduce>>> def fn(x, y):... return x * 10 + y

. .> ;>> def char2num(s):... return {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5 ': 5, '6': 6, '7': 7, '8': 8, '9': 9}[s]

...>>> , ' 13579'))13579


은 str2int 함수로 구성됩니다.

from functools import Reducedef str2int(s): def fn(x, y): return x * 10 + y def char2num(s): return {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9}[s] return 감소( fn, map(char2num, s))

은 람다 함수를 사용하여 더욱 단순화할 수도 있습니다.

from functools import Reduceef char2num(s): return {'0': 0 , '1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, ' 9 ': 9}[s]def str2int(s): return Reduce(lambda x, y: x * 10 + y, map(char2num, s))

즉, Python을 가정하면 int() 함수를 제공하지 않으므로 문자열을 정수로 변환하는 함수를 직접 작성할 수 있으며 코드 몇 줄만 있으면 됩니다!

람다 함수의 사용법은 나중에 소개하겠습니다.

filter

Python에 내장된 filter() 함수는 시퀀스를 필터링하는 데 사용됩니다.

map()과 유사하게 filter()도 함수와 시퀀스를 받습니다. map()과 달리 filter()는 전달된 함수를 각 요소에 차례로 적용한 다음 반환 값이 True인지 False인지에 따라 요소를 유지할지 또는 삭제할지 결정합니다.

예를 들어 목록에서 짝수를 삭제하고 홀수만 유지하려면 다음과 같이 작성할 수 있습니다.

def is_odd(n): return n % 2 == 1

list(filter(is_odd, [1, 2, 4, 5, 6, 9, 10, 15]))# 결과: [1, 5, 9, 15]

빈 문자열을 삭제하려면 다음과 같이 작성할 수 있습니다.

def not_empty(s): return s and s.strip()

list(filter(not_empty, ['A', ' ' , 'B', None, 'C', ' ']))# 결과: ['A', 'B', 'C']

더 높은- order 함수 filter()가 사용되는 경우 핵심은 "필터" 기능을 올바르게 구현하는 것입니다.

filter() 함수는 지연 시퀀스인 Iterator를 반환하므로 filter()가 계산 결과를 강제로 완료하려면 list() 함수를 사용하여 모든 결과를 얻고 목록을 반환합니다.

sorted

정렬 알고리즘

정렬 역시 프로그램에서 자주 사용하는 알고리즘이다. 버블 정렬을 사용하든 퀵 정렬을 사용하든 정렬의 핵심은 두 요소의 크기를 비교하는 것입니다. 숫자라면 직접 비교할 수 있지만, 문자열이거나 두 개의 사전이라면 어떨까요? 수학적 크기를 직접 비교하는 것은 의미가 없으므로 비교 과정을 함수를 통해 추상화해야 합니다.

Python의 내장 sorted() 함수는 목록을 정렬할 수 있습니다:

>>> sorted([36, 5, -12, 9, -21])[- 21 , -12, 5, 9, 36]

또한 sorted() 함수는 다음과 같은 사용자 정의 정렬을 구현하는 핵심 함수도 받을 수 있습니다. 절대값 크기 정렬:

>>> sorted([36, 5, -12, 9, -21], key=abs)
[5, 9, -12, -21 , 36 ]

key로 지정된 함수는 목록의 각 요소에 작용하며 key 함수에서 반환된 결과에 따라 정렬됩니다. 원래 목록과 key=abs로 처리된 목록을 비교합니다.

list = [36, 5, -12, 9, -21]keys = [36, 5, 12, 9, 21]

그런 다음 sorted() 함수는 키별로 정렬하고 해당 관계에 따라 목록의 해당 요소를 반환합니다.

키 정렬 결과 => [5, 9, 12, 21, 36 ]
                                                                                                                                                                                              >
>>> Credit'])['Credit', 'Zoo', 'about', 'bob' ]

기본적으로 문자열 정렬은 ASCII 크기 비교를 기반으로 합니다. 따라서 'Z' < 'a'이므로 대문자 Z가 소문자 a 앞에 정렬됩니다. .

이제 대소문자를 무시하고 알파벳순으로 정렬하도록 제안합니다. 이 알고리즘을 구현하기 위해 키 함수를 사용하여 대소문자 정렬을 무시하도록 문자열을 매핑할 수 있는 한 기존 코드를 크게 변경할 필요가 없습니다. 대소문자를 무시하여 두 문자열을 비교한다는 것은 실제로 문자열을 먼저 대문자(또는 소문자)로 변경한 다음 비교하는 것을 의미합니다.

이런 방식으로 대소문자를 무시하는 정렬을 달성하기 위해 sorted에 주요 함수를 전달합니다.

>>> sorted(['bob', 'about', 'Zoo' , 'Credit'], key=str.lower)

['about', 'bob', 'Credit', 'Zoo']

역순으로 정렬하려면 키를 변경할 필요가 없습니다. 함수에서 세 번째 매개변수 reverse=True를 전달할 수 있습니다:


>>> sorted(['bob', 'about', 'Zoo', 'Credit'], key=str. lower , reverse=True)
['Zoo', 'Credit', 'bob', 'about']

위의 예에서 알 수 있듯이 고차의 추상화 능력은 기능은 매우 강력하며 핵심 코드는 매우 간단하게 유지될 수 있습니다.


요약

sorted()도 고차 함수입니다. sorted()를 사용한 정렬의 핵심은 매핑 기능을 구현하는 것입니다.

반환 함수

반환 값으로서의 함수

고차 함수는 함수를 매개변수로 허용하는 것 외에도 함수를 결과 값으로 반환할 수도 있습니다.

변수 매개변수의 합을 구현해 보겠습니다. 일반적으로 합계 함수는 다음과 같이 정의됩니다.

def calc_sum(*args):
ax = 0 for n in args:
ax = ax + n return ax

그런데 합을 바로 계산할 필요는 없고, 후속 코드에서 필요에 따라 계산한다면 어떻게 될까요? 합계 결과를 반환하는 대신 합계 함수를 반환할 수 있습니다.

deflazy_sum(*args): def sum():
ax = 0 for n in args:
ax = ax + n return ax return sum

lazy_sum()을 호출하면 반환되는 것은 합산 결과가 아니라 합산 함수입니다:

>>> (1, 3, 5, 7, 9)
>>> f
.sum at 0x101c6ed90>

결과 합계는 함수 f가 호출될 때 실제로 계산됩니다.

>>> f()25

이 예에서는 함수 sum에 이를 정의합니다. , 내부 함수 sum은 외부 함수lazy_sum의 매개 변수 및 지역 변수를 참조할 수 있습니다.lazy_sum이 함수 sum을 반환하면 해당 매개 변수 및 변수가 반환된 함수에 저장됩니다. 이를 "Closure"라고 합니다. 프로그램 구조가 훌륭합니다. 힘.

한 가지 더 주의하세요.lazy_sum()을 호출하면 동일한 매개변수가 전달되더라도 각 호출은 새로운 함수를 반환합니다.

>>> 게으른_합(1, 3, 5, 7, 9)>>> f2 = 게으른_합(1, 3, 5, 7, 9)>>> f1==f2False

f1()과 f2()의 호출 결과는 서로 영향을 미치지 않습니다.

클로저

반환된 함수는 정의 내에서 지역 변수 args를 참조합니다. 따라서 함수가 함수를 반환하면 내부 지역 변수도 새 함수에 의해 참조됩니다. 클로저는 사용하기 쉽지만 구현하기는 쉽지 않습니다.

주의해야 할 또 다른 문제는 반환된 함수가 즉시 실행되지 않고 f()가 호출될 때까지 실행되지 않는다는 것입니다. 예를 살펴보겠습니다.

def count():
fs = [] for i in range(1, 4): def f(): return i*i
fs.append( f) return fs

f1, f2, f3 = count()

위의 예에서는 반복할 때마다 새 함수가 생성되고 생성된 All 세 가지 함수가 반환되었습니다.

f1(), f2() 및 f3()을 호출한 결과는 1, 4, 9여야 한다고 생각할 수 있지만 실제 결과는 다음과 같습니다.

>>> ; f1() 9>>> f2()9>>> f3()9

모두 9입니다! 그 이유는 반환된 함수가 변수 i를 참조하지만 즉시 실행되지 않기 때문입니다. 세 함수가 모두 반환될 때 그들이 참조하는 변수 i는 3이 되므로 최종 결과는 9입니다.

클로저를 반환할 때 명심해야 할 한 가지는: 반환 함수는 루프 변수나 이후에 변경되는 변수를 참조해서는 안 된다는 것입니다.

루프 변수를 참조해야 한다면 어떻게 될까요? 이 방법은 다른 함수를 만들고 함수의 매개 변수를 사용하여 루프 변수의 현재 값을 바인딩하는 것입니다. 이후에 루프 변수가 어떻게 변경되더라도 함수 매개 변수에 바인딩된 값은 변경되지 않습니다.

def count(): def f(j):           def g():                                                                                 ’   ’ s ~ t ’s s ’ s ‐ ‐ ‐ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​    )가 즉시 실행되므로 i의 현재 값이 f()에 전달됩니다. return fs


결과를 다시 살펴보세요.

>>> f1, f2, f3 = count()> ;>> f1()1>>> f2()4>>> f3()9


단점은 코드가 더 길고 람다 함수가 사용 코드를 단축하세요.

요약

함수는 계산 결과 또는 함수를 반환할 수 있습니다.

함수를 반환할 때 함수가 실행되지 않는다는 점을 기억하고, 반환된 함수에서 변경될 수 있는 변수를 참조하지 마세요.

익명 함수

함수를 전달할 때 함수를 명시적으로 정의할 필요가 없는 경우에는 익명 함수를 직접 전달하는 것이 더 편리합니다.

Python에서는 익명 함수에 대한 지원이 제한되어 있습니다. 여전히 map() 함수를 예로 들면, f(x)=x2를 계산할 때 f(x) 함수를 정의하는 것 외에도 익명 함수를 직접 전달할 수도 있습니다.

> >> 목록(맵(람다 x: x * x, [1, 2, 3, 4, 5, 6, 7, 8, 9]))[1, 4, 9, 16, 25, 36, 49 , 64, 81]


비교에서 볼 수 있듯이 익명 함수 람다 x: x * x는 실제로 다음과 같습니다.

def f(x): return x * x


lambda 키워드는 익명 함수를 나타내고, 콜론 앞의 x는 함수 매개변수를 나타냅니다.

익명 함수에는 하나의 표현식만 가질 수 있다는 제한이 있습니다. 반환 값은 표현식의 결과입니다.

익명 함수를 사용하면 함수에 이름이 없으므로 함수 이름 충돌을 걱정할 필요가 없다는 장점이 있습니다. 또한 익명 함수는 함수 개체이기도 합니다. 익명 함수를 변수에 할당한 다음 해당 변수를 사용하여 함수를 호출할 수도 있습니다.

>>> * x
>>f
at 0x101c6ef28>
>>f(5)
25

마찬가지로 익명 함수를 반환 값으로 반환할 수 있습니다. 예를 들면 다음과 같습니다.

def build(x, y): return Lambda: x * x + y * y


요약

Python은 익명 함수에 대한 지원이 제한되어 있으며 일부 간단한 상황에서만 익명 함수를 사용할 수 있습니다.

부분 기능

Python의 functools 모듈은 많은 유용한 기능을 제공하며 그 중 하나가 부분 기능입니다. 여기서 부분 함수는 수학적 의미에서 부분 함수와 다르다는 점에 유의해야 합니다.

함수 매개변수를 소개할 때, 매개변수의 기본값을 설정함으로써 함수 호출의 난이도를 줄일 수 있다고 언급했습니다. 부분 함수도 이 작업을 수행할 수 있습니다. 예:

int() 함수는 문자열을 정수로 변환할 수 있습니다. 문자열만 전달되면 int() 함수는 기본적으로 10진수 변환을 사용합니다.

>> > int('12345')12345


그러나 int() 함수는 추가 기본 매개변수도 제공하며 기본값은 10입니다. 기본 매개변수를 전달하면 N-base 변환을 수행할 수 있습니다:

>>> int('12345', base=8)5349

>>> ' 12345', 16)74565

많은 수의 바이너리 문자열을 변환한다고 가정할 때, 매번 int(x, base=2)를 전달하는 것은 매우 번거로운 작업이므로 우리는 생각했습니다. int2( ) 함수를 정의할 수 있으므로 기본적으로 base=2가 전달됩니다.

def int2(x, base=2): return int(x, base)


이런 식으로 바이너리를 변환합니다. 매우 편리합니다.

>>> int2('1000000')64>>> int2('1010101')85


functools.partial은 부분 함수를 생성하기 위해 int2()를 직접 정의할 필요가 없습니다. 다음 코드를 직접 사용하여 새 함수 int2를 생성할 수 있습니다.

>>> ; import functools>>> int2 = functools .partial(int, base=2)>>> int2('1000000')64>>> int2('1010101')85 >

그래서 functools.partial에 대한 간략한 요약은 함수의 특정 매개변수를 수정(즉, 기본값 설정)하고 새 함수를 반환하는 것입니다. 이 새 함수를 호출하는 것이 더 쉬울 것입니다.

위의 새로운 int2 함수는 기본 매개변수를 기본값인 2로만 재설정하지만 함수가 호출될 때 다른 값도 전달할 수 있습니다.

>> ; > int2('1000000', base=10)1000000

마지막으로 부분 함수를 생성할 때 실제로 함수 객체, *args 및 **kw라는 세 가지 매개변수를 받을 수 있습니다. in :

int2 = functools.partial(int, base=2)

는 실제로 int() 함수의 키워드 매개변수 기반을 수정합니다. 즉,

int2('10010')

은 다음과 동일합니다:

kw = { 'base': 2 }int('10010', **kw)

전달된 경우:

max2 = functools.partial(max, 10)

은 실제로 *args의 일부로 왼쪽에 10을 자동으로 추가합니다. 즉, 다음과 같습니다.

max2(5, 6, 7)

은 다음과 같습니다.

args = (10, 5, 6, 7)

max(* args )


결과는 10입니다.

요약

함수에 매개변수가 너무 많아 단순화해야 하는 경우 functools.partial을 사용하여 새 함수를 만들 수 있습니다. 이 새 함수는 원본 매개변수 중 일부를 수정할 수 있습니다. 함수이므로 호출할 때 더 간단합니다.

위 내용은 Python 고급: 고차 함수에 대한 자세한 설명의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.