Following system colour scheme Selected dark colour scheme Selected light colour scheme

Python 개선 제안 한국어 번역

PEP 363 – 동적 속성 접근을 위한 구문

Author:
Ben North <ben at redfrontdoor.org>
Status:
Rejected
Type:
Standards Track
Created:
29-Jan-2007
Post-History:
12-Feb-2007

Table of Contents

번역·라이선스 안내

이 비공식 한국어 번역은 원문 Copyright 절의 Public Domain 조건에 따라 제공합니다. 원저자와 공식 원문은 그대로 표시합니다. 수정되지 않은 기준 원문 · 공식 최신판

Warning

This PEP has been rejected.

×

자세한 내용은 메일링 리스트 논의을 참조하십시오.

초록

현재 “getattr” 및 “setattr” 내장 함수를 사용하여 동적으로 속성에 접근할 수 있습니다. 이 PEP에서는 이러한 접근을 더 쉽게 만드는 새로운 구문을 제안하며, 예를 들어 코더가 다음과 같이 작성할 수 있도록 합니다.:

x.('foo_%d' % n) += 1

z = y.('foo_%d' % n).('bar_%s' % s)

다음 대신에:

attr_name = 'foo_%d' % n
setattr(x, attr_name, getattr(x, attr_name) + 1)

z = getattr(getattr(y, 'foo_%d' % n), 'bar_%s' % s)

근거

딕셔너리 접근과 인덱싱에는 모두 편리한 호출 구문이 있습니다. 즉, x.__getitem__(12) 대신 코더는 x[12]라고 작성할 수 있습니다. 또한 이를 통해 “x[12] += 1”과 같이 확장 할당에서 첨자 표기 요소를 사용할 수 있습니다. 현재 제안은 이러한 편리함을 동적 속성 접근에도 제공합니다.

현재 속성 접근은 다음 두 가지 방법으로 가능합니다.

  • 코드를 작성할 때 속성 이름을 알고 있는 경우에는 “.NAME” 트레일러를 사용할 수 있습니다. 예를 들면 다음과 같습니다.:
    x.foo = 42
    y.bar += 100
    
  • 속성 이름이 실행 시간에 동적으로 계산되는 경우에는 “getattr” 및 “setattr” 내장 함수를 사용해야 합니다.:
    x = getattr(y, 'foo_%d' % n)
    setattr(z, 'bar_%s' % s, 99)
    

    또한 “getattr” 내장 함수를 사용하면 객체에 지정된 이름의 속성이 없는 경우 반환할 기본값을 코더가 지정할 수 있습니다.:

    x = getattr(y, 'foo_%d' % n, 0)
    

이 PEP에서는 위의 초록에 예시로 제시한 “x.(expr)”이라는 동적 속성 접근을 위한 새로운 구문을 설명합니다.

(새로운 구문을 사용하면 “get”의 경우에도 다음과 같이 기본값을 제공할 수 있습니다.:

x = y.('foo_%d' % n, None)

이 동적 속성 접근의 2인자 형식은 (확장 또는 일반) 할당의 대상으로 사용할 수 없습니다. 아래의 “Discussion” 절에는 이 2인자 확장에 관한 의견이 구체적으로 포함되어 있습니다.)

마지막으로 새로운 구문은 다음과 같이 “del” 문과 함께 사용할 수 있습니다.:

del x.(attr_name)

기존 코드에 미치는 영향

제안된 새로운 구문은 현재 유효하지 않으므로, 이 제안으로 인해 기존의 올바르게 형성된 프로그램의 의미가 변경되지는 않습니다.

2.5 배포판의 모든 “*.py” 파일에서 “getattr”, “setattr” 또는 “delattr”의 사용 사례는 약 600개입니다. 이들은 다음과 같이 분류됩니다. (부분적으로 수작업으로 검사하여 산출한 수치이므로 어느 정도 오차가 있을 수 있습니다.):

c.300 uses of plain "getattr(x, attr_name)", which could be
      replaced with the new syntax;

c.150 uses of the 3-argument form, i.e., with the default
      value; these could be replaced with the 2-argument form
      of the new syntax (the cases break down into c.125 cases
      where the attribute name is a literal string, and c.25
      where it's only known at run-time);

c.5   uses of the 2-argument form with a literal string
      attribute name, which I think could be replaced with the
      standard "x.attribute" syntax;

c.120 uses of setattr, of which 15 use getattr to find the
      new value; all could be replaced with the new syntax,
      the 15 where getattr is also involved would show a
      particular increase in clarity;

c.5   uses which would have to stay as "getattr" because they
      are calls of a variable named "getattr" whose default
      value is the builtin "getattr";

c.5   uses of the 2-argument form, inside a try/except block
      which catches AttributeError and uses a default value
      instead; these could use 2-argument form of the new
      syntax;

c.10  uses of "delattr", which could use the new syntax.

예를 들어 다음 행은:

setattr(self, attr, change_root(self.root, getattr(self, attr)))

Lib/distutils/command/install.py의 다음과 같은 코드로 다시 작성할 수 있습니다.:

self.(attr) = change_root(self.root, self.(attr))

그리고 다음 행은:

setattr(self, method_name, getattr(self.metadata, method_name))

Lib/distutils/dist.py의 다음과 같은 코드로 다시 작성할 수 있습니다.:

self.(method_name) = self.metadata.(method_name)

성능에 미치는 영향

초기 pystone 측정 결과는 결론을 내리기 어렵지만, 패치된 버전에서는 pystones 점수가 약 1% 정도 하락할 수 있음을 시사합니다. 한 가지 추측은 ceval.c의 더 긴 메인 루프가 캐시 동작에 악영향을 주기 때문이라는 것이지만, 이는 아직 확인되지 않았습니다.

반면 측정 결과는 동적 속성 접근에서 약 40–45%의 속도 향상이 있음을 시사합니다.

오류 사례

속성 이름으로는 문자열만 허용되므로, 예를 들어 다음과 같은 오류가 발생합니다:

>>> x.(99) = 8
   Traceback (most recent call last):
     File "<stdin>", line 1, in <module>
   TypeError: attribute name must be string, not 'int'

이는 기존의 PyObject_GetAttr 함수로 처리됩니다.

초안 구현

초안 구현은 Grammar/Grammar의 “trailer” 절에 새로운 대안을 추가하고, Python.asdl에 “DynamicAttribute”라는 새로운 AST 타입을 추가하며, symtable.c, ast.c, compile.c에 그에 따른 변경을 가하고, 세 개의 새로운 opcode(load/store/del)와 함께 opcode.h 및 ceval.c에 그에 따른 변경을 가합니다. 이 패치는 핵심 코드에 약 180줄이 추가되고, 테스트에 약 100줄이 추가되는 것으로 구성됩니다. sourceforge 패치 #1657573 [1]로 제공됩니다.

메일링 리스트 논의

이 PEP의 초안 형태 초기 게시물은 20070209에 python-ideas에 올라왔으며 [2], 반응은 대체로 긍정적이었습니다. 이후 이 PEP는 20070212에 python-dev에 게시되었고 [3], 흥미로운 논의가 이어졌습니다. 간단히 요약하면 다음과 같습니다.

초기에는 이 아이디어에 대해 합리적인(하지만 만장일치는 아닌) 지지가 있었으나, 정확한 문법 선택에 대해서는 더 엇갈린 반응이 있었습니다. 여러 사람들이 “.”이 너무 쉽게 간과될 수 있어, 그 결과 이 문법이 메소드/함수 호출과 혼동될 수 있다고 생각했습니다. 몇 가지 대안 문법이 제안되었으며:

obj.(foo)
obj.[foo]
obj.{foo}
obj{foo}
obj.*foo
obj->foo
obj<-foo
obj@[foo]
obj.[[foo]]

“obj.[foo]”가 선호되는 것으로 부상했습니다. 이 초기 논의에서 두 인자 형태는 만장일치로 반감을 샀으므로, PEP에서 제외되기로 했습니다.

이후 논의는 이 특정 기능이 새로운 구문을 정당화할 만큼 충분한 이점을 제공하는지에 대한 원점으로 돌아갔습니다. 코더들이 새 구문에 익숙해져야 한다는 점 외에도, 하위 호환성 문제 또한 있었습니다 — 새 구문을 사용하는 코드는 이전 버전의 파이썬에서 실행되지 않을 것이었습니다.

새 구문 대신, Martin von Löwis가 다음과 같은 명세 / 개념적 구현을 제안한 새로운 “래퍼 클래스”가 제안되었습니다:

class attrs:
   def __init__(self, obj):
     self.obj = obj
   def __getitem__(self, name):
     return getattr(self.obj, name)
   def __setitem__(self, name, value):
     return setattr(self.obj, name, value)
   def __delitem__(self, name):
     return delattr(self, name)
   def __contains__(self, name):
     return hasattr(self, name)

이는 원래 문제에 대한 더 깔끔하고 우아한 해결책으로 여겨졌습니다. (또 다른 제안은 객체의 속성에 딕셔너리 방식 접근을 제공하는 믹스인 클래스였습니다.)

논의 초반부터 일부에서 제기해 온 견해대로, 현재 PEP가 새 구문 도입의 입증 책임을 충족하지 못한다는 결정이 내려졌습니다. 래퍼 클래스 아이디어는 향후 PEP를 위한 가능성으로 열어두었습니다.

참고 자료