[파이썬] 람다 함수와 IPv4 문자열<=>long 변환 Develop Tip

람다 함수라는 것은 위키피디어에 따르면,

하고 정의 되어 있습니다.

일반 C 에서는 문법으로 정의 되어 있지 않지만,
C# 등에서는 정의할 수 있습니다. (익명함수라고 표현을 하는군요)

파이썬에서도 보다 쉽게 정의 되어 있구요.

다음의 예로 살펴 보도록 하겠습니다.

###############################################################################
def from_ipv4string(ipv4str):
"""
@brief IPv4 주소 문자열을 long 형식으로 변환
@param ipv4str [string] IPv4 문자열, 예) '1.2.3.4'
@return [long] 해당 IPv4 문자열의 long 표현 방식
"""
eles = ipv4str.split(".") 
a = 0L 
for ele in eles:
a <<= 8
b = long(ele)
a |= b
return a

위의 코드를 보면 일반 C와 같이 작성이 되었습니다.
먼저 '.' 으로 뜯어 목록을 만들고 그것을 long 형식으로 변환한 다음,
a 를 왼쪽으로 8비트 밀고 a에다 b를 비트 OR 연산 시키는 것입니다.

람다 함수를 사용하면, 이 7줄을 다음의 한줄로 줄일 수 있습니다.

###############################################################################
def from_ipv4string(ipv4str):
"""
@brief IPv4 주소 문자열을 long 형식으로 변환
@param ipv4str [string] IPv4 문자열, 예) '1.2.3.4'
@return [long] 해당 IPv4 문자열의 long 표현 방식
"""
return reduce(lambda a,b: a<<8 | b, map(long, ipv4str.split(".")))

우선 map함수를 보면,
map(fund, list) 로 두번째 인자인 list에 대하여 첫벗째 인자의 함수를 호출하고 그 결과 목록을 되돌려 줍니다.

즉, ipv4str이 '1.2.3.4' 라고 한다면,
map(long, ipv4str.split('.')) 결과는 [1L,2L,3L,4L] 의 목록이 됩니다.

이제 reduce 함수를 살펴보겠습니다.

reduce(func, list)
이 reduce는 두번 째 인자인 list에서 왼쪽에서부터 두 개의 내용을 꺼내어 func 함수를 호출하는 것입니다.

예를 들어,
reduce(lambda x, y: x+y, [1, 2, 3, 4, 5]) 

위의 함수는 
((((1+2)+3)+4)+5) 의 순서로 계산하게 됩니다.

이제 결론적으로 위의 함수를 풀어보면,

reduce(lambda a,b: a<<8 | b, map(long, ipv4str.split(".")))
==>
reduce(lambda a,b: a<<8 | b, map(long, ['1','2','3','4']))
==>
reduce(lambda a,b: a<<8 | b, [1L,2L,3L,4L])
==>
(((1L << 8 | 2L) << 8 | 3L) << 8 | 4L)

와 같이 계산됩니다.


유사한 방법으로 

###############################################################################
def to_ipv4string(ipv4long):
"""
@brief IPv4 long 형식 값을 '1.2.3.4' 와 같은 문자열 형식으로 변환
@param ipv4str [long] IPv4 long 값
@return [string] 해당 IPv4 문자열 표현 방식
"""
return ".".join(map(lambda n: str(ipv4long>>n & 0xFF), [24,16,8,0]))

와 같이 역 변환이 가능합니다.

앞서, 
map(func, list) 는 list의 항목에 대하여 func를 수행한다 하였습니다.

map(lambda n: str(ipv4long>>n & 0xFF), [24,16,8,0])
==>
[str(ipv4long >> 24 & 0xff), str(ipv4long >> 16 & 0xff), str(ipv4long >> 8 & 0xff), str(ipv4long >> 24 & 0xff)]
가 됩니다.

즉, ipv4long 이 0x01020304L 값이라면,
위의 결과 값은,
['1','2','3','4'] 가 나오게 되고,

'.'.join(['1','2','3','4']) 의 결과는
'1.2.3.4' 가 됩니다.

목록, map, 람다 함수와 reduce를 적절히 잘 사용함으로써 코드의 간결함과 더불어
속도나 모든 측면에서 코딩의 질을 높일 수 있을 것입니다.

(솔직히 저도 그동안 이를 잘 활용하지 못했는데, 앞으로 기회되는 데로 활용해 볼까 합니다)

어느분께는 도움이 되셨기를...

핑백

  • 지훈현서 : [Python] 목록의 필터링 적용 2013-04-16 10:02:49 #

    ... 이 줄 역시나 다음과 같은 한 줄로 치환 가능합니다. y = filter(lambda e: e is not None and len(e) &gt; 0, y) 지난번 람다함수관련 내용과,목록 관련 내용인 목록과 셑, 목록 소팅 및 목록 초기화 등과 더불어많이 사용하고픈 내용입니다. 어느분께는 도움이 되셨기를... ... more

덧글

  • fullc0de 2013/03/13 13:33 # 답글

    Pythonic 하게 바꾸셨네요^^;; /짝짝
  • 지훈현서아빠 2013/03/13 17:25 #

    칭찬 감사드립니다~ ^^
  • 여신같은 순록 2013/03/14 08:24 # 답글

    위에 사용된 예제같은 경우 reduce의 경우에는 어쩔 수 없지만, map과 lambda를 통한 anonymous function을 사용할 경우 stack을 이용하기때문에 간단한 list comprehension보다 더 느려지는 경우가 많습니다.

    reduce(lambda a,b: a<<8 | b, (long(i) for i in ipv4str.split(".")))

    ".".join(str(ipv4long>>n & 0xFF) for n in [24,16,8,0])

    그리고 reduce는 for-loop보다 대부분의 경우 더 느립니다. for-loop도 느리지만..
  • 지훈현서아빠 2013/03/14 10:00 #

    음... 위와 같이 몇 번 안도는 예제는 별 차이 없겠지만,
    만약 큰 루프라면 충분히 문제의 소지가 있겠군요...
    뭐 최적화도 결국은 파이썬 C, C++ wrapper Call로
    해결해야할 경우가 많겠지만요~~
    좋은 정보 감사드립니다... ^^
댓글 입력 영역

구글애드텍스트