파이썬 사전 안에 함수를 저장하는 이유는 무엇입니까?


68

저는 파이썬 초보자이며 방금 사전과 함수와 관련된 기술을 배웠습니다. 구문은 쉽고 사소한 것처럼 보이지만 파이썬 감각은 따끔 거립니다. 무언가가 이것이 깊고 매우 비열한 개념이며 나에게 그 중요성을 파악하고 있지 않습니다. 누군가이 기술에 이름을 붙이고 그것이 어떻게 / 유용한지를 설명 할 수 있습니까?


이 기술은 파이썬 사전과 사용할 기능이있을 때입니다. dict에 추가 요소를 삽입합니다.이 요소의 값은 함수 이름입니다. 함수를 호출 할 준비가되면 이름 별 함수가 아니라 dict 요소를 참조하여 간접적으로 호출을 발행합니다 .

내가 일하고있는 예는 Learn Python the Hard Way, 2nd Ed의 것입니다. (이것은 Udemy.com 을 통해 가입 할 때 사용 가능한 버전입니다 . 슬프게도 라이브 무료 HTML 버전 은 현재 Ed 3이며 더 이상이 예제를 포함하지 않습니다).

말을 바꾸려면 :

# make a dictionary of US states and major cities
cities = {'San Diego':'CA', 'New York':'NY', 'Detroit':'MI'}

# define a function to use on such a dictionary
def find_city (map, city):
    # does something, returns some value
    if city in map:
        return map[city]
    else:
        return "Not found"

# then add a final dict element that refers to the function
cities['_found'] = find_city

다음 식은 동일합니다. 함수를 직접 호출하거나 값이 함수 인 dict 요소를 참조하여 함수를 호출 할 수 있습니다.

>>> find_city (cities, 'New York')
NY

>>> cities['_found'](cities, 'New York')
NY

누군가 이것이 이것이 어떤 언어 기능인지, 그리고 "실제"프로그래밍에서 어디에서 작동하는지 설명 할 수 있습니까? 이 장난감 연습은 나에게 구문을 가르쳐주기에 충분했지만 저를 끝까지 데려 가지 않았습니다.


13
이 게시물이 주제를 벗어난 이유는 무엇입니까? 훌륭한 알고리즘 및 데이터 구조 개념 질문입니다!
Martijn Pieters

나는 다른 언어로 이런 것들을 보았습니다. 스위치 구문으로 볼 수는 있지만 O (1) 조회 시간을 사용하여 전달 가능한 객체로 멋지게 정리했습니다.
KChaloux

1
나는 자신의 dict 안에 함수를 포함시키는 것에 대해 중요하고 자기 참조가있는 직감을 가지고 있었다 ... @dietbuddha의 대답을 보아라.
mdeutschmtl

답변:


83

dict을 사용하면 키를 호출 가능으로 변환 할 수 있습니다. 예제와 같이 키를 하드 코딩 할 필요는 없습니다.

일반적으로 이것은 호출자 디스패치의 형태로, 변수 값을 사용하여 함수에 연결합니다. 네트워크 프로세스가 명령 코드를 보내면 디스패치 매핑을 통해 명령 코드를 실행 코드로 쉽게 변환 할 수 있습니다.

def do_ping(self, arg):
    return 'Pong, {0}!'.format(arg)

def do_ls(self, arg):
    return '\n'.join(os.listdir(arg))

dispatch = {
    'ping': do_ping,
    'ls': do_ls,
}

def process_network_command(command, arg):
    send(dispatch[command](arg))

우리가 지금 호출하는 함수는 전적으로 값이 무엇인지에 달려 있습니다 command. 키도 일치하지 않아도됩니다. 문자열 일 필요는 없으며 키로 사용할 수 있으며 특정 응용 프로그램에 맞는 것을 사용할 수 있습니다.

디스패치 방법을 사용하면 eval()허용 가능한 명령을 미리 정의한 것으로 제한하기 때문에 와 같은 다른 기술보다 안전 합니다. ls)"; DROP TABLE Students; --예를 들어 공격자가 디스패치 테이블을 지나서 주입 을 몰래 침입하지 않습니다 .


5
@Martjin-이 경우 '명령 패턴'의 구현이라고 할 수 없습니까? OP가 파악하려는 개념 인 것 같습니까?
PhD

3
@PhD : 예, 내가 만든 예제는 명령 패턴 구현입니다. dict디스패처 (명령 관리자, 호출자 등) 의 역할을합니다.
Martijn Pieters

@Martijn 감사합니다. 나는 "파견"아이디어를 얻는 것 같아요.
mdeutschmtl

28

@Martijn Pieters는 기술을 잘 설명했지만 귀하의 질문에서 무언가를 분명히하고 싶었습니다.

알아야 할 중요한 것은 "함수 이름"을 사전에 저장 하지 않는다는 것 입니다. 함수 자체에 대한 참조를 저장하고 있습니다. print함수에서를 사용하여 이것을 볼 수 있습니다 .

>>> def f():
...   print 1
... 
>>> print f
<function f at 0xb721c1b4>

f정의한 함수를 참조하는 변수 일뿐입니다. 사전을 사용하면 같은 것을 그룹화 할 수 있지만 함수를 다른 변수에 할당하는 것과 다르지 않습니다.

>>> a = f
>>> a
<function f at 0xb721c3ac>
>>> a()
1

마찬가지로 함수를 인수로 전달할 수 있습니다.

>>> def c(func):
...   func()
... 
>>> c(f)
1

5
최고 수준의 기능을 언급하는 것은 확실히 :-) 도움이 될 것이다
플로리안 Margaine

7

파이썬 클래스는 실제로 사전의 구문 설탕입니다. 할 때 :

class Foo(object):
    def find_city(self, city):
        ...

전화 할 때

f = Foo()
f.find_city('bar')

실제로 다음과 같습니다.

getattr(f, 'find_city')('bar')

이름 확인 후 다음과 동일합니다.

f.__class__.__dict__['find_city'](f, 'bar')

유용한 기술 중 하나는 사용자 입력을 콜백에 매핑하는 것입니다. 예를 들면 다음과 같습니다.

def cb1(...): 
    ...
funcs = {
    'cb1': cb1,
    ...
}
while True:
    input = raw_input()
    funcs[input]()

이것은 대안으로 수업에서 쓸 수 있습니다 :

class Funcs(object):
    def cb1(self, a): 
        ...
funcs = Funcs()
while True:
    input = raw_input()
    getattr(funcs, input)()

콜백 구문이 더 좋은 것은 특정 응용 프로그램과 프로그래머의 취향에 달려 있습니다. 전자는 더 기능적인 스타일이고 후자는 더 객체 지향적입니다. 함수 사전의 항목을 동적으로 수정해야하는 경우 전자가 더 자연 스러울 수 있습니다 (아마도 사용자 입력에 따라). 동적으로 선택할 수있는 서로 다른 사전 설정 매핑이있는 경우 후자가 더 자연스럽게 느껴질 수 있습니다.


나는이 상호 교환 성이 나를 "파이 토닉 (pythonic)"이라고 생각하게했다고 생각합니다. 표면에서 보는 것이 훨씬 더 깊은 무언가를 제시하는 일반적인 방법이라는 사실입니다. 파이썬 연습 (그리고 파이썬 프로그래머?)은 이런 식으로 언어 기능에 대해 끔찍한 이야기를하는 것처럼 보이지만 아마도 파이썬에만 국한되지는 않습니다.
mdeutschmtl

또 다른 생각은, 두 개의 "용어"가 서로 인접 해있는 것, 즉 dict reference와 argument list처럼 보이는 것을 평가하려는 방식으로 파이썬에 특정한 무언가가 있는가? 다른 언어에서도 가능합니까? 그것은에서 대수의 도약의 프로그래밍 상당의 일종 5 * x으로 5x(간단한 비유를 용서).
mdeutschmtl

@mdeutschmtl : 파이썬에 고유하지는 않지만 일급 함수 또는 함수 객체가없는 언어에는 사전 액세스 직후 함수 호출이 가능한 상황이 없을 수 있습니다.
Lie Ryan

2
@mdeutschmtl "표면에 보이는 것은 훨씬 더 깊은 무언가를 제시하는 일반적인 방법이라는 사실입니다." -그것은 구문 설탕 이라고 불리우며 모든 곳에서 존재합니다
Izkata

6

당신이 언급 할 수있는 두 가지 기술이 있습니다. 두 언어 중 하나가 언어보다 더 광범위하다는 것은 Pythonic이 아닙니다.

1. 정보 숨기기 / 캡슐화 및 응집 기술 (보통 손을 잡고 함께 묶습니다).

데이터가있는 개체가 있고 데이터와 매우 밀접한 메서드 (동작)를 연결합니다. 기능을 변경해야하는 경우 기능을 확장하거나 발신자가 변경할 필요가없는 다른 변경을 수행하십시오 (추가 데이터를 전달할 필요가 없다고 가정).

2. 디스패치 테이블

함수가있는 항목이 하나뿐이므로 일반적인 경우는 아닙니다. 그러나 디스패치 테이블은 동적으로 조회하고 호출 할 수 있도록 키로 다른 동작을 구성하는 데 사용됩니다. 동적 방식으로 함수를 언급하지 않았기 때문에 이것에 대해 생각하는지 확실하지 않지만 여전히 효과적인 후기 바인딩 ( "간접"호출)을 얻지는 못합니다.

트레이드 오프

주목해야 할 것은 알려진 키 네임 스페이스를 사용하여 수행 한 작업입니다. 그러나 알 수없는 키 네임 스페이스를 사용하여 데이터와 함수간에 충돌이 발생할 위험이 있습니다.


dict (ionary)에 저장된 데이터와 함수가 관련되어 있으므로 응집력이 있기 때문에 캡슐화 데이터와 기능은 서로 다른 두 개의 dommain에서 가져 왔으므로 언뜻보기에 이질적인 개체가 모여있는 것처럼 보입니다.
ChuckCottrill

0

나는이 솔루션이 매우 일반적이며 특정 사례에 간단하고 쉽게 적용 할 수 있기 때문에 유용하다고 생각합니다.

def p(what):
    print 'playing', cmpsr

def l(what):
    print 'listening', cmpsr

actions = {'Play' : p, 'Listen' : l}

act = 'Listen'
cmpsr = 'Vivaldi'

actions[act].__call__(cmpsr)

각 요소가 함수 객체 인 목록을 정의하고 __call__내장 메소드를 사용할 수도 있습니다 . 영감과 협력에 대한 모든 크레딧.

"위대한 예술가는 단순화 자입니다", Henri Frederic Amiel

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.