함수에서 여러 값을 어떻게 반환합니까? [닫은]


1067

이를 지원하는 언어로 여러 값을 반환하는 표준 방법은 종종 튜플 링 입니다.

옵션 : 튜플 사용

이 사소한 예를 고려하십시오.

def f(x):
  y0 = x + 1
  y1 = x * 3
  y2 = y0 ** y3
  return (y0, y1, y2)

그러나 반환되는 값의 수가 증가함에 따라 이는 빠르게 문제가됩니다. 4 개 또는 5 개의 값을 반환하려면 어떻게합니까? 물론, 계속 튜플 링 할 수는 있지만 어느 값이 어디에 있는지 잊어 버릴 수 있습니다. 또한 원하는 곳 어디에서나 포장을 풀면 추악합니다.

옵션 : 사전 사용

다음 논리적 단계는 일종의 '레코드 표기법'을 도입하는 것 같습니다. 파이썬에서이를 수행하는 확실한 방법은을 사용하는 것입니다 dict.

다음을 고려하세요:

def g(x):
  y0 = x + 1
  y1 = x * 3
  y2 = y0 ** y3
  return {'y0': y0, 'y1': y1 ,'y2': y2}

명확하게 말하면, y0, y1 및 y2는 추상적 식별자로 의미됩니다. 실제로 지적한 바와 같이 의미있는 식별자를 사용합니다.

이제 반환 된 객체의 특정 멤버를 투영 할 수있는 메커니즘이 있습니다. 예를 들어

result['y0']

옵션 : 수업 사용

그러나 다른 옵션이 있습니다. 대신 특수 구조를 반환 할 수 있습니다. 파이썬의 맥락 에서이 프레임을 만들었지 만 다른 언어에도 적용된다고 확신합니다. 실제로 C에서 작업하는 경우 이것이 유일한 옵션 일 수 있습니다. 간다 :

class ReturnValue:
  def __init__(self, y0, y1, y2):
     self.y0 = y0
     self.y1 = y1
     self.y2 = y2

def g(x):
  y0 = x + 1
  y1 = x * 3
  y2 = y0 ** y3
  return ReturnValue(y0, y1, y2)

파이썬에서 앞의 두 아마도 배관의 측면에서 매우 유사하다 - 결국 { y0, y1, y2 }단지 내부의 항목 끝나게 __dict__ReturnValue.

작은 객체, __slots__속성에 대해 Python이 제공하는 추가 기능이 하나 있습니다 . 수업은 다음과 같이 표현 될 수 있습니다.

class ReturnValue(object):
  __slots__ = ["y0", "y1", "y2"]
  def __init__(self, y0, y1, y2):
     self.y0 = y0
     self.y1 = y1
     self.y2 = y2

로부터 파이썬 참조 설명서 :

__slots__선언 각 변수에 대한 값을 유지하도록 각각의 인스턴스 인스턴스 변수 보유 충분한 공간의 시퀀스 걸린다. __dict__각 인스턴스마다 공간이 생성되지 않아 공간이 절약 됩니다.

옵션 : 데이터 클래스 사용 (Python 3.7 이상)

Python 3.7의 새로운 데이터 클래스를 사용하여 자동으로 추가 된 특수 메소드, 입력 및 기타 유용한 도구가있는 클래스를 리턴하십시오.

@dataclass
class Returnvalue:
    y0: int
    y1: float
    y3: int

def total_cost(x):
    y0 = x + 1
    y1 = x * 3
    y2 = y0 ** y3
    return ReturnValue(y0, y1, y2)

옵션 : 목록 사용

내가 간과 한 또 다른 제안은 Bill the Lizard에서 온 것입니다.

def h(x):
  result = [x + 1]
  result.append(x * 3)
  result.append(y0 ** y3)
  return result

이것은 내가 가장 좋아하는 방법입니다. 나는 Haskell에 노출되어 오염되었다고 생각하지만 혼합 유형 목록의 아이디어는 항상 나에게 불편하다고 느꼈습니다. 이 특정 예제에서 목록은 혼합 유형이 아니지만 가능할 수 있습니다.

이 방법으로 사용 된 목록은 실제로 내가 알 수있는 한 튜플과 관련하여 아무것도 얻지 못합니다. 파이썬에서 목록과 튜플의 유일한 차이점은 목록은 변경 가능 하지만 튜플은 변경 불가능 하다는 것 입니다.

필자는 개인적으로 함수형 프로그래밍에서 관례를 따르는 경향이 있습니다. 동일한 유형의 여러 요소에 대해 목록을 사용하고 미리 결정된 유형의 고정 된 요소에 대해 튜플을 사용하십시오.

질문

긴 서문 후에 피할 수없는 질문이 온다. 가장 좋은 방법은 무엇입니까?


10
훌륭한 예제에서 variable을 사용 y3하지만 y3이 전역으로 선언되지 않으면 NameError: global name 'y3' is not defined아마도 3?
hetepeperfan

11
'opinion'키워드가 발생하기 때문에 많은 답변이있는 많은 훌륭한 질문이 닫힙니다. 당신은 SO 전체가 의견에 근거한다고 주장 할 수 있지만, 사실, 참고 문헌 및 특정 전문 지식에 의해 의견이 전달됩니다. 누군가가 "당신이 가장 좋다고 생각한다"고 묻는다고해서 실제 사실, 참고 문헌 및 특정 전문 지식으로부터 추상화 된 개인적인 의견을 요구한다는 의미는 아닙니다. 그들은 거의 확실하게 그러한 종류의 의견, 그 사람이 의견을 형성하는 데 사용한 사실, 언급 및 특정 전문 지식을 철저히 기반으로하고 문서화 한 종류의 의견을 요구하고 있습니다.
NeilG

@hetepeperfan은 3을 변경할 필요가 없으며 전역에서 y3을 정의하지 않으며 로컬 이름을 사용할 수도 있습니다 y3. 이는 동일한 작업을 수행합니다.
okie

답변:


637

이 목적으로 명명 된 튜플 이 2.6에 추가되었습니다. 유사한 내장 예제는 os.stat 를 참조하십시오 .

>>> import collections
>>> Point = collections.namedtuple('Point', ['x', 'y'])
>>> p = Point(1, y=2)
>>> p.x, p.y
1 2
>>> p[0], p[1]
1 2

최신 버전의 Python 3 (3.6 이상, 내 생각에)에서 새로운 typing라이브러리는 NamedTuple명명 된 튜플을 더 쉽고 강력하게 만들 수 있는 클래스를 얻었습니다 . 상속은 문서 typing.NamedTuple문자열, 기본값 및 형식 주석을 사용할 수있게합니다.

예 (문서에서) :

class Employee(NamedTuple):  # inherit from typing.NamedTuple
    name: str
    id: int = 3  # default value

employee = Employee('Guido')
assert employee.id == 3

68
이것은 OP가 고려하지 않은 유일한 정식 구조이고 긴 튜플 관리 문제를 해결하기 때문에 정답입니다. 허용 된 것으로 표시해야합니다.
airstrike

7
음, 디자인의 이론적 근거 namedtuple대량 결과 (DB 쿼리 결과와 같은 긴 튜플 목록)에 대한 메모리 풋 프린트가 작습니다 . 개별 항목의 경우 (문제의 함수가 자주 호출되지 않는 경우) 사전과 클래스도 좋습니다. 그러나 명명 된 튜플은이 경우에도 훌륭하고 좋은 솔루션입니다.
Lutz Prechelt

8
@wom : 이러지 마십시오. 파이썬은 namedtuple정의 를 단일화하기 위해 노력하지 않습니다 (각 호출은 새로운 호출을 만듭니다). namedtuple클래스를 생성하는 것은 CPU와 메모리 모두에서 상대적으로 비싸며 모든 클래스 정의에는 본질적으로 순환 참조가 포함됩니다 (따라서 CPython에서는 순환 GC 실행을 기다리고 있습니다) 그들을 풀어주기 위해). 또한 pickle클래스가 불가능하므로 multiprocessing대부분의 경우 인스턴스를 사용할 수 없습니다 . 3.6.4 x64에서 클래스를 만들 때마다 ~ 0.337ms가 소비되고 1KB 미만의 메모리를 차지하므로 인스턴스 절약 효과가 사라집니다.
ShadowRanger

3
파이썬 3.7 은 새로운 namedtuple클래스 를 만드는 속도를 향상 시켰습니다 . CPU 비용은 약 4 배 감소 하지만 여전히 인스턴스 생성 비용보다 약 1000 배 더 높으며 각 클래스의 메모리 비용은 여전히 ​​높습니다 ( '1KB 미만'에 대한 마지막 의견에서는 잘못되었습니다) 클래스의 경우 _source자체적으로 일반적으로 1.5KB이며 _source3.7에서 제거되므로 클래스 생성 당 1KB 미만의 원래 클레임에 더 가깝습니다).
ShadowRanger

4
@SergeStroobandt-이것은 표준 라이브러리의 일부이며 내장 된 것이 아닙니다. Python> = 2.6을 사용하는 다른 시스템에 설치되지 않을 수도 있습니다. 아니면 여분의 코드 줄에 반대합니까?
저스틴

234

작은 프로젝트의 경우 튜플을 사용하는 것이 가장 쉽다는 것을 알았습니다. 그것이 관리하기가 너무 어려워지면 (이전이 아닌) 논리 구조로 그룹화하기 시작하지만 제안 된 사전과 ReturnValue객체의 사용이 잘못되었거나 너무 단순 하다고 생각합니다 .

키 사전을 반환 "y0", "y1", "y2", 등 튜플을 통해 어떤 이점을 제공하지 않습니다. 돌아 오는 ReturnValue특성을 가진 인스턴스를 .y0, .y1, .y2, 등 중 튜플을 통해 어떤 이점을 제공하지 않습니다. 어디든 가려면 이름을 지정해야합니다. 어쨌든 튜플을 사용하여 이름을 지정할 수 있습니다.

def get_image_data(filename):
    [snip]
    return size, (format, version, compression), (width,height)

size, type, dimensions = get_image_data(x)

IMHO, 튜플을 넘어서는 유일한 좋은 기술은 re.match()또는 에서 얻는 것처럼 적절한 방법과 속성을 사용하여 실제 객체를 반환하는 것 open(file)입니다.


6
질문- size, type, dimensions = getImageData(x)과 차이가 (size, type, dimensions) = getImageData(x)있습니까? 즉, 튜플 링 된 과제의 왼쪽을 감싸는 것이 어떤 차이가 있습니까?
Reb. Cabin

11
@ Reb.Cabin 차이는 없습니다. 튜플은 쉼표로 식별되며 괄호 사용은 사물을 그룹화하는 것입니다. 예를 들어 (1)int (1,)또는 1,튜플입니다.
phil

19
"y0, y1, y2 등의 키로 사전을 반환하면 튜플에 비해 이점이 없습니다": 사전은 기존 코드를 손상시키지 않고 반환 된 사전에 필드를 추가 할 수 있다는 장점이 있습니다.
ostrokach

"y0, y1, y2 등의 키로 사전을 반환하면 튜플에 비해 이점이 없습니다": 위치보다는 이름을 기준으로 데이터에 액세스 할 때 읽기 쉽고 오류가 적습니다.
Denis Dollfus

204

많은 답변은 사전이나 목록과 같은 일종의 컬렉션을 반환해야한다고 제안합니다. 추가 구문을 생략하고 쉼표로 구분하여 반환 값을 쓸 수 있습니다. 참고 : 이것은 기술적으로 튜플을 반환합니다.

def f():
    return True, False
x, y = f()
print(x)
print(y)

제공합니다 :

True
False

24
여전히 컬렉션을 반환하고 있습니다. 튜플입니다. 더 명확하게하려면 괄호를 선호합니다. 이것을 시도하십시오 : type(f())returns <class 'tuple'>.
Igor

20
@ 아이고 : tuple측면을 명시 적으로 만들 이유가 없습니다 ; 를 반환하는 것은 중요하지 않습니다 tuple. 여러 값을 반환하는 관용구입니다. 스왑 관용구 x, y = y, x, 다중 초기화 x, y = 0, 1등으로 parens를 생략하는 것과 같은 이유 ; 확실히, tuples를 후드 아래로 만들지 만, s가 전혀 중요하지 않기 때문에 명시 적으로 만들 이유 tuple가 없습니다. 파이썬 튜토리얼 은 s를 다루기 훨씬 전에 여러 할당을 소개합니다tuple .
ShadowRanger

@ShadowRanger 오른쪽에 쉼표로 구분 된 값 시퀀스는 =괄호가 있거나없는 파이썬의 튜플입니다. 실제로 여기에는 명시 적이거나 암시적인 것이 없습니다. a, b, c는 (a, b, c)만큼 많은 튜플입니다. 이러한 값을 반환 할 때 "단순히"튜플을 만드는 것도 없습니다. 왜냐하면 단순한 단순한 튜플이기 때문입니다. OP는 이미 튜플을 언급 했으므로 실제로 언급 한 것과이 답변이 보여주는 것과는 아무런 차이가 없습니다. 없음
Ken4scholars

2
이것은 말 그대로 질문에서 제안 된 첫 번째 옵션입니다
endolith

1
두 번 @endolith 사람은 ( "어떻게?"나는 여러 값을 반환하려면 어떻게합니까 "하고 질문을 하면 여러 값을 반환?")이 답변 응답한다. 질문 내용이 때때로 변경되었습니다. 그리고 그것은 의견에 근거한 질문입니다.
조셉 한센

74

사전에 투표합니다.

2-3 개 이상의 변수를 반환하는 함수를 만들면 사전에 접을 것입니다. 그렇지 않으면 나는 돌아 오는 것의 순서와 내용을 잊어 버리는 경향이있다.

또한 '특별한'구조를 도입하면 코드를 따르기가 더 어려워집니다. (다른 사람은 코드를 검색하여 코드를 찾아야합니다.)

유형 조회에 관심이있는 경우 설명 사전 키 (예 : 'x- 값 목록')를 사용하십시오.

def g(x):
  y0 = x + 1
  y1 = x * 3
  y2 = y0 ** y3
  return {'y0':y0, 'y1':y1 ,'y2':y2 }

5
수년간의 프로그래밍 후에, 나는 데이터와 기능의 구조가 필요한 어느쪽으로 향하는 경향이 있습니다. 기능 우선, 필요에 따라 언제든지 리팩토링 할 수 있습니다.
monkut

함수를 여러 번 호출하지 않고 사전 내에서 값을 얻는 방법은 무엇입니까? 예를 들어, 다른 함수에서 y1과 y3을 사용하려면?
Matt

3
결과를 별도의 변수에 할당하십시오. result = g(x); other_function(result)
monkut

1
@monkut 예. 이 방법을 사용하면 매번 결과의 특정 부분을 구체적으로 참조 할 필요없이 여러 함수에 결과를 전달할 수 있습니다.
Gnudiff

38

다른 옵션은 생성기를 사용하는 것입니다.

>>> def f(x):
        y0 = x + 1
        yield y0
        yield x * 3
        yield y0 ** 4


>>> a, b, c = f(5)
>>> a
6
>>> b
15
>>> c
1296

IMHO 튜플이 일반적으로 가장 좋지만 반환되는 값이 클래스의 캡슐화 후보 인 경우를 제외하고는 가장 좋습니다.


1
이것은 가장 깨끗한 솔루션으로 보이며 구문이 깨끗합니다. 이것에 대한 단점이 있습니까? 모든 반품을 사용하지 않으면 '미사용'수익률이 당신을 다치게 기다리고 있습니까?
Jiminion

24
이것은 "깨끗한"것일 수도 있지만 전혀 직관적 인 것처럼 보이지는 않습니다. 자동 터플 포장 풀기 작업을 수행 할 때마다이 패턴을 경험 한 적이없는 사람은 어떻게 yield됩니까?
coredumperror 2018 년

1
@CoreDumpError, 생성기는 그저… 생성기입니다. 이 사이에 외부 차이가없는 def f(x): …; yield b; yield a; yield r비교는 (g for g in [b, a, r]), 둘 다 쉽게리스트 나 튜플, 등의 뜻 지원 튜플 풀고로 변환합니다. 튜플 생성기 형태는 기능적 접근 방식을 따르지만, 함수 형태는 필수적이며 흐름 제어 및 변수 할당이 가능합니다.
sleblanc

30

튜플이 "자연 스럽다"고 느낄 때마다 튜플을 사용하는 것을 선호합니다. 좌표는 일반적인 예이며, 예를 들어 1 축만 스케일링 계산에서 별도의 객체가 자체적으로 존재할 수 있으며 순서가 중요합니다. 참고 : 그룹의 의미에 부정적인 영향을 미치지 않고 항목을 정렬하거나 섞을 수 있다면 튜플을 사용하지 않아야합니다.

그룹화 된 객체가 항상 같지 않은 경우에만 사전을 반환 값으로 사용합니다. 선택적인 이메일 헤더를 생각하십시오.

그룹화 된 객체가 그룹 내부에 고유 한 의미를 가지고 있거나 자체 메서드가있는 본격적인 객체가 필요한 나머지 경우에는 클래스를 사용합니다.


29

나는 선호한다:

def g(x):
  y0 = x + 1
  y1 = x * 3
  y2 = y0 ** y3
  return {'y0':y0, 'y1':y1 ,'y2':y2 }

다른 모든 것은 동일한 일을하기위한 추가 코드 일뿐입니다.


22
튜플은 압축을 풀기가 더 쉽습니다 : y0, y1, y2 = g () 당신이해야 할 dict : result = g () y0, y1, y2 = result.get ( 'y0'), result.get ( 'y1' ), result.get ( 'y2')는 조금 추악합니다. 각 솔루션에는 '플러스'와 '마이너스'가 있습니다.
Oli December

27
>>> def func():
...    return [1,2,3]
...
>>> a,b,c = func()
>>> a
1
>>> b
2
>>> c
3

@edouard 아니요,리스트가 아닌 튜플을 반환합니다.
Simon Hibbs

1
destructuring은 내 의견 목록을 반환하는 인수
semiomant

21

일반적으로 "특수 구조"는 실제로 고유 한 방법으로 객체의 현 상태입니다.

class Some3SpaceThing(object):
  def __init__(self,x):
    self.g(x)
  def g(self,x):
    self.y0 = x + 1
    self.y1 = x * 3
    self.y2 = y0 ** y3

r = Some3SpaceThing( x )
r.y0
r.y1
r.y2

가능한 경우 익명 구조의 이름을 찾고 싶습니다. 의미있는 이름은 일을 더 명확하게 만듭니다.


20

파이썬의 튜플, 받아쓰기 및 객체는 프로그래머가 작은 데이터 구조 ( "사물")에 대한 형식 성과 편의성 사이의 원활한 균형을 제공합니다. 나에게 물건을 표현하는 방법의 선택은 주로 구조를 어떻게 사용할 것인가에 달려 있습니다. C ++에서는 합법적으로 메소드를 ; 에 배치 할 수 있지만 struct데이터 전용 항목 및 class메소드가있는 오브젝트 에 사용하는 것이 일반적입니다 struct. 내 습관으로, 파이썬 비슷 dict하고 tuple대신에struct .

좌표 세트의 tuple경우 점 class또는 a 대신 a dict를 사용 tuple하고 사전 키로 사용할 수 있으므로dict 매우 희소 한 다차원 배열을 만듭니다).

내가 물건의 목록을 반복하려고한다면, 나는 tuple반복에 대한 포장 풀기를 선호합니다 .

for score,id,name in scoreAllTheThings():
    if score > goodScoreThreshold:
        print "%6.3f #%6d %s"%(score,id,name)

... 개체 버전이 더 어수선 해지기 때문에 :

for entry in scoreAllTheThings():
    if entry.score > goodScoreThreshold:
        print "%6.3f #%6d %s"%(entry.score,entry.id,entry.name)

... 혼자서만 dict.

for entry in scoreAllTheThings():
    if entry['score'] > goodScoreThreshold:
        print "%6.3f #%6d %s"%(entry['score'],entry['id'],entry['name'])

사물이 널리 사용되고 코드의 여러 곳에서 비슷한 사소한 작업을 수행하는 경우 적절한 방법으로 클래스 객체로 만드는 것이 좋습니다.

마지막으로 비 Python 시스템 구성 요소와 데이터를 교환 dict하려는 경우 JSON 직렬화에 가장 적합하기 때문에 가장 자주 데이터를 보관합니다 .


19

명명 된 컨테이너 클래스에 대한 S.Lott의 제안에 +1.

Python 2.6 이상에서 명명 된 튜플 은 이러한 컨테이너 클래스를 쉽게 만들 수있는 유용한 방법을 제공하며 결과는 "가벼우 며 일반 튜플보다 더 많은 메모리가 필요하지 않습니다".


4

파이썬과 같은 언어에서는 일반적으로 새 클래스를 만드는 것보다 오버 헤드가 적기 때문에 사전을 사용합니다.

그러나 동일한 변수 집합을 지속적으로 반환한다는 사실을 알게되면 아마도 새로운 요소가 포함될 것입니다.


4

dict를 사용하여 함수에서 값을 전달하고 반환합니다.

에 정의 된 변수 양식을 사용하여 양식 .

form = {
    'level': 0,
    'points': 0,
    'game': {
        'name': ''
    }
}


def test(form):
    form['game']['name'] = 'My game!'
    form['level'] = 2

    return form

>>> print(test(form))
{u'game': {u'name': u'My game!'}, u'points': 0, u'level': 2}

이것은 나와 처리 장치에 가장 효율적인 방법입니다.

하나의 포인터 만 전달하고 하나의 포인터 만 반환해야합니다.

코드를 변경할 때마다 함수 (수천 개의) 인수를 변경할 필요가 없습니다.


둑은 변경 가능합니다. dict을 함수에 전달하고 해당 함수가 dict를 편집하면 변경 사항이 해당 함수의 범위를 벗어나 반영됩니다. 함수가 끝에 dict를 리턴하게하면 함수에 부작용이 없음을 암시 할 수 있으므로 값을 리턴해서는 안되므로 값 test을 직접 수정해야합니다. 이것을 값과 비교 dict.update하지 않는와 비교하십시오 .
sleblanc

@sleblanc "함수가 함수를 반환하면 함수에 부작용이 없음을 의미 할 수 있습니다." 그것은 당신이 말했듯이 dict가 변경 가능하기 때문에 암시하지 않습니다. 그러나 반환 form해도 가독성이나 성능이 저하되지 않습니다. 형식을 다시 지정해야하는 경우 form[form]을 form반환하면 어디에서나 양식 변경 내용을 추적하지 않으므로 마지막 형식 을 반환 해야합니다 .
Elis Byberi

3

"최고"는 부분적으로 주관적인 결정입니다. 불변이 허용되는 일반적인 경우 작은 리턴 세트에 튜플을 사용하십시오. 튜플은 가변성이 요구되지 않는 경우 항상 목록보다 선호됩니다.

보다 복잡한 반환 값 또는 형식이 중요한 경우 (즉, 고가의 코드) 명명 된 튜플이 더 좋습니다. 가장 복잡한 경우에는 일반적으로 객체가 가장 좋습니다. 그러나 실제로 중요한 상황입니다. 함수가 끝날 때 자연스럽게 가지고 있기 때문에 객체를 반환하는 것이 합리적이라면 (예 : 팩토리 패턴) 객체를 반환하십시오.

현명한 사람이 말했듯이 :

조기 최적화는 프로그래밍에서 모든 악 (또는 적어도 대부분)의 근원입니다.


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