함수가 여러 값을 반환하는 것이 비단뱀입니까?


81

파이썬에서는 함수가 여러 값을 반환하도록 할 수 있습니다. 다음은 인위적인 예입니다.

def divide(x, y):
    quotient = x/y
    remainder = x % y
    return quotient, remainder  

(q, r) = divide(22, 7)

이것은 매우 유용 해 보이지만 남용 될 수도있는 것처럼 보입니다 ( "Well..function X는 이미 필요한 것을 중간 값으로 계산합니다. X도 그 값을 반환하도록합시다").

언제 선을 그리고 다른 방법을 정의해야합니까?

답변:


107

물론입니다 (제공 한 예의 경우).

튜플은 파이썬에서 일류 시민입니다.

divmod()정확히 그렇게 하는 내장 함수 가 있습니다.

q, r = divmod(x, y) # ((x - x%y)/y, x%y) Invariant: div*y + mod == x

다른 예는 다음과 같습니다 zip, enumerate, dict.items.

for i, e in enumerate([1, 3, 3]):
    print "index=%d, element=%s" % (i, e)

# reverse keys and values in a dictionary
d = dict((v, k) for k, v in adict.items()) # or 
d = dict(zip(adict.values(), adict.keys()))

BTW, 괄호는 대부분 필요하지 않습니다. Python Library Reference 에서 인용 :

튜플은 여러 가지 방법으로 구성 할 수 있습니다.

  • 한 쌍의 괄호를 사용하여 빈 튜플 표시 : ()
  • 싱글 톤 튜플에 후행 쉼표 사용 : a 또는 (a,)
  • 쉼표로 항목 구분 : a, b, c 또는 (a, b, c)
  • 내장 tuple () 사용 : tuple () 또는 tuple (iterable)

기능은 단일 용도로 사용되어야합니다.

따라서 단일 객체를 반환해야합니다. 귀하의 경우이 객체는 튜플입니다. 튜플을 임시 복합 데이터 구조로 간주하십시오. 거의 모든 단일 함수가 여러 값을 반환하는 언어가 있습니다 (Lisp의 목록).

때로는 (x, y)대신 반환하는 것으로 충분합니다 Point(x, y).

명명 된 튜플

Python 2.6에 명명 된 튜플이 도입되면서 많은 경우 일반 튜플 대신 명명 된 튜플을 반환하는 것이 좋습니다.

>>> import collections
>>> Point = collections.namedtuple('Point', 'x y')
>>> x, y = Point(0, 1)
>>> p = Point(x, y)
>>> x, y, p
(0, 1, Point(x=0, y=1))
>>> p.x, p.y, p[0], p[1]
(0, 1, 0, 1)
>>> for i in p:
...   print(i)
...
0
1

1
이와 같은 튜플을 사용하는 기능은 매우 편리합니다. 나는 자바에서 그런 능력을 가지고있는 것을 정말 그리워한다.
MBCook

3
명명 된 튜플을 언급 해 주셔서 감사합니다. 나는 이와 같은 것을 찾고 있었다.
vobject

제한된 반환 값의 경우 dict ()가 더 간단하고 빠를 수 있습니다 (특히 함수가 REST API 및 JSON에 사용되는 경우). 내 시스템에서 def test_nt () : Point = namedtuple ( 'Point', [ 'x', 'y']) p = Point (10.5, 11.5) json.dumps (p._asdict ())는 819 μs / loop를 사용합니다. , def test_dict () : d = { 'x': 10.5, 'y': 11.5} json.dumps (d)는 15.9μs / loop를 사용합니다 .... 그래서> 50 배 더 빠름
comte

@comte : 예, Point(10.5, 11.5)6 회보다 느린입니다 {'x': 10.5, 'y':11.5}. 절대 시간은 635 ns +- 26 ns105 ns +- 4 ns . 둘 다 응용 프로그램에서 병목 현상이 발생하지 않을 것입니다. 프로파일 러가 달리 언급하지 않는 한 최적화하지 마십시오. 필요한 이유를 모르면 함수 수준에서 클래스를 만들지 마십시오. API가 dict사용보다 필요한 경우 dict성능과는 관련이 없습니다.
jfs

27

첫째, Python은 다음을 허용합니다 (괄호가 필요 없음).

q, r = divide(22, 7)

귀하의 질문과 관련하여 어느 쪽이든 엄격하고 빠른 규칙은 없습니다. 간단한 (일반적으로 인위적인) 예제의 경우 주어진 함수가 단일 목적을 갖고 결과적으로 단일 값을 얻는 것이 항상 가능해 보일 수 있습니다. 그러나 실제 응용 프로그램에 Python을 사용할 때 여러 값을 반환해야하는 많은 경우에 빠르게 실행되어 코드가 더 깔끔해집니다.

그래서, 나는 말이되는 것은 무엇이든하고, 인위적인 관습을 따르려고하지 마십시오. Python은 여러 반환 값을 지원하므로 적절할 때 사용하십시오.


4
쉼표는 튜플을 생성하므로 expression : q, r 튜플입니다.
jfs

Vinko, 예를 들어 표준 라이브러리의 tempfile.mkstemp ()는 파일 핸들과 절대 경로를 포함하는 튜플을 반환합니다. JFS, 네, 맞습니다.
Jason Etheridge

하지만 그것은 하나의 목적입니다 ... 당신은 하나의 목적과 여러 개의 반환 값을 가질 수 있습니다
Vinko Vrsalovic

다른 언어에 여러 반환 유형이있을 수 있다면 참조 및 'out'매개 변수에 얽매이지 않을 것입니다.
orip

여러 값을 반환하는 것은 파이썬에서 가장 큰 기능 중 하나입니다!
Martin Beckett

13

당신이 제공하는 예제는 실제로라는 파이썬 내장 함수 divmod입니다. 그래서 누군가는 어느 시점에서 핵심 기능을 포함하기에 충분히 비단뱀이라고 생각했습니다.

나에게 코드를 깔끔하게 만든다면 비단뱀 적이다. 다음 두 코드 블록을 비교하십시오.

seconds = 1234
minutes, seconds = divmod(seconds, 60)
hours, minutes = divmod(minutes, 60)

seconds = 1234
minutes = seconds / 60
seconds = seconds % 60
hours = minutes / 60
minutes = minutes % 60

3

예, 여러 값 (즉, 튜플)을 반환하는 것은 확실히 비단뱀입니다. 다른 사람들이 지적했듯이 Python 표준 라이브러리와 존경받는 Python 프로젝트에는 많은 예제가 있습니다. 두 가지 추가 설명 :

  1. 여러 값을 반환하는 것은 때때로 매우 유용합니다. 예를 들어 선택적으로 이벤트를 처리하고 (일부 값을 반환) 성공 또는 실패를 반환하는 메서드를 사용합니다. 이는 일련의 책임 패턴에서 발생할 수 있습니다. 다른 경우에는 주어진 예와 같이 밀접하게 연결된 여러 데이터 조각을 반환하려고합니다. 이 설정에서 여러 값을 반환하는 것은 여러 멤버 변수가있는 익명 클래스의 단일 인스턴스를 반환하는 것과 유사합니다.
  2. Python의 메서드 인수 처리에는 여러 값을 직접 반환하는 기능이 필요합니다. 예를 들어 C ++에서는 메서드 인수를 참조로 전달할 수 있으므로 형식적인 반환 값 외에 출력 값을 할당 할 수 있습니다. Python에서 인수는 "참조"로 전달됩니다 (그러나 C ++가 아닌 Java의 의미에서). 메서드 인수에 새 값을 할당하고 메서드 범위 외부에 반영되도록 할 수 없습니다. 예를 들면 :

    // C++
    void test(int& arg)
    {
        arg = 1;
    }
    
    int foo = 0;
    test(foo); // foo is now 1!
    

    비교 :

    # Python
    def test(arg):
        arg = 1
    
    foo = 0
    test(foo) # foo is still 0
    

"선택적으로 이벤트를 처리하고 성공 또는 실패를 반환하는 메서드"-이것이 예외입니다.
Nathan

예외는 단지 성공이나 실패가 아닌 "예외적 인"조건만을위한 것이라는 사실은 널리 받아 들여지고 있습니다. 일부 토론에 대한 Google "오류 코드 대 예외".
zweiterlinde 2010

1

확실히 비단뱀입니다. 어딘가에서 반환하는 유형의 모든 조합에 대해 구조체를 정의해야하는 C와 같은 언어에서 사용할 수있는 상용구 함수에서 여러 값을 반환 할 수 있다는 사실.

그러나 단일 함수에서 10 개의 값과 같은 미친 값을 반환하는 지점에 도달하면 그 지점에서 다루기가 어려워 지므로 클래스에 번들링하는 것을 진지하게 고려해야합니다.



1

OT : RSRE의 Algol68에는 흥미로운 "/ : ="연산자가 있습니다. 예.

INT quotient:=355, remainder;
remainder := (quotient /:= 113);

몫은 3이고 나머지는 16입니다.

참고 : 일반적으로 "(x / : = y)"의 값은 몫 "x"가 참조로 할당되기 때문에 버려지지만 RSRE의 경우 반환 된 값은 나머지입니다.

cf 정수 산술-Algol68


0

다음과 같은 간단한 함수에 대해 튜플을 사용하여 여러 값을 반환하는 것이 좋습니다. divmod . 코드를 읽을 수있게 만드는 것은 Pythonic입니다.

반환 값이 헷갈 리기 시작하면 함수가 너무 많은 일을하고 있는지 확인하고 그럴 경우 분할합니다. 큰 튜플이 개체처럼 사용되는 경우 개체로 만듭니다. 또한 Python 2.6에서 표준 라이브러리의 일부가 될 named tuples 사용을 고려하십시오 .


0

저는 Python을 처음 접했지만 튜플 기술은 저에게 매우 비단뱀처럼 보입니다. 그러나 가독성을 높일 수있는 또 다른 아이디어가 있습니다. 사전을 사용하면 위치가 아닌 이름으로 다른 값에 액세스 할 수 있습니다. 예를 들면 :

def divide(x, y):
    return {'quotient': x/y, 'remainder':x%y }

answer = divide(22, 7)
print answer['quotient']
print answer['remainder']

2
이러지마 결과를 한 줄로 지정할 수 없습니다. 매우 투박합니다. (결과를 저장하고 싶지 않다면,이 경우 메서드에 넣는 것이 좋습니다.) dict의 의도가 반환 값을 자체 문서화하는 것이라면 a) fn에 합리적으로 설명하는 이름 (예 : " divmod ")와 b) 나머지 설명을 독 스트링에 넣습니다. 해당 독 스트링에 두 줄 이상이 필요한 경우 해당 함수가 주제에 따라 오버로드되고 있으며 잘못된 생각 일 수 있습니다.
smci
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.