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인자 형식의 동적 속성 접근은 (복합 또는 일반) 할당의 대상으로는 허용되지 않습니다. 아래의 “토론” 절에는 특히 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.

예를 들어, Lib/distutils/command/install.py에 있는 다음 줄은:

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

다음과 같이 다시 작성할 수 있습니다.:

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

그리고 Lib/distutils/dist.py에 있는 다음 줄은:

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

다음과 같이 다시 작성할 수 있습니다.:

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에 새로운 AST 타입인 “DynamicAttribute”를 추가하며 이에 따라 symtable.c, ast.c, compile.c를 변경하고, 세 가지 새로운 옵코드(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에서 제외하기로 했습니다.

이어서 논의는 한발 물러나, 이 특정 기능이 새로운 문법을 정당화할 만큼 충분한 이점을 제공하는지에 대한 문제로 돌아갔습니다. 코드 작성자가 새 문법에 익숙해져야 한다는 점 외에도 하위 호환성 문제도 있습니다. 새 문법을 사용하는 코드는 이전 버전의 Python에서 실행되지 않습니다.

새로운 문법 대신 새로운 “래퍼 클래스”가 제안되었으며, 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에서 다룰 가능성으로 남겨 두었습니다.

참고 문헌