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

Python 개선 제안 한국어 번역

PEP 3126 – 암시적 문자열 연결 제거

Author:
Jim J. Jewett <JimJJewett at gmail.com>, Raymond Hettinger <python at rcn.com>
Status:
Rejected
Type:
Standards Track
Created:
29-Apr-2007
Post-History:
29-Apr-2007, 30-Apr-2007, 07-May-2007

Table of Contents

번역·라이선스 안내

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

거부 공지

이 PEP는 거부되었습니다. 찬성 의견이 충분하지 않았고, 제거하려는 기능이 그다지 해롭지도 않으며, 더 어려워질 일부 사용 사례도 있습니다.

초록

Python은 C에서 많은 구문 분석 규칙을 물려받았습니다. 이는 대체로 유용했지만, Python에는 덜 유용한 개별 규칙도 있으며 제거해야 합니다.

이 PEP는 리터럴의 인접성만을 기반으로 하는 암시적 문자열 연결을 제거할 것을 제안합니다.

다음 대신:

"abc" "def" == "abcdef"

작성자는 이를 명시적으로 작성하고 문자열을 다음과 같이 추가해야 합니다.:

"abc" + "def" == "abcdef"

또는 다음과 같이 결합해야 합니다.:

"".join(["abc", "def"]) == "abcdef"

동기

Python 3000의 한 가지 목표는 불필요한 기능을 제거하여 언어를 단순화하는 것이어야 합니다. 암시적 문자열 연결은 기존 기법을 사용하는 방향으로 제거해야 합니다. 이렇게 하면 문법이 단순해지고 사용자가 머릿속으로 그리는 Python의 모습도 단순해집니다. 후자는 언어를 “머릿속에 담아 두는” 데 중요합니다. 현재 사용자의 대다수는 암시적 연결 자체를 알지도 못합니다. 이를 아는 사람 중 상당수는 전혀 사용하지 않거나 습관적으로 피합니다. 이를 알고 사용하기까지 하는 사람 중에서도, 암시적 연산자 우선순위와 정의가 컴파일될 때 계산되는 경우와 실행될 때 계산되는 경우를 어떤 상황에서 구분하는지 자신 있게 말할 수 있는 사람은 극소수입니다.

역사 또는 미래

Python의 많은 구문 분석 규칙은 의도적으로 C와 호환됩니다. 이는 유용한 기본값이지만, 특수 사례는 Python에서의 유용성을 근거로 정당화해야 합니다. 더 이상 Python 프로그래머가 C에도 익숙할 것이라고 가정해서는 안 되므로, 언어 간 호환성은 정당화의 근거가 아니라 동률을 깨는 기준으로 다루어야 합니다.

C에서는 (런타임) 함수 호출을 사용하지 않고 문자열을 결합하여 변수에 저장하는 유일한 방법이 암시적 연결입니다. Python에서는 + 또는 "".join과 같은 더 일반적인 Python 관용구를 사용하여 문자열을 결합하면서도 불변으로 인식되게 할 수 있습니다.

문제

암시적 문자열 연결은 겉보기보다 짧은 튜플과 리스트를 만들며, 이는 혼란스럽거나 심지어 조용히 발생하는 오류로 이어질 수 있습니다. 예를 들어 여러 매개변수를 받지만 그중 일부에 기본값을 제공하는 함수가 있다고 하겠습니다.:

def f(fmt, *args):
    print fmt % args

이는 유효한 호출처럼 보이지만 실제로는 그렇지 않습니다.:

>>> f("User %s got a message %s",
      "Bob"
      "Time for dinner")

Traceback (most recent call last):
  File "<pyshell#8>", line 2, in <module>
    "Bob"
  File "<pyshell#3>", line 2, in f
    print fmt % args
TypeError: not enough arguments for format string

이 함수 호출은 조용히 잘못된 동작을 수행할 수 있습니다.:

def g(arg1, arg2=None):
    ...

# silently transformed into the possibly very different
# g("arg1 on this linearg2 on this line", None)
g("arg1 on this line"
  "arg2 on this line")

Jason Orendorff의 말을 인용하면 [1]:

아. 방금 이곳에서는 이런 일이 자주 발생한다는 사실을 깨달았습니다. 제가 일하는 곳에서는 scons를 사용하며, 각 SConscript에는 긴 파일 이름 목록이 있습니다.:
sourceFiles = [
    'foo.c'
    'bar.c',
    #...many lines omitted...
    'q1000x.c']

쉼표를 빠뜨리는 것은 흔한 실수이며, 그러면 scons가 ‘foo.cbar.c’를 찾을 수 없다고 불평합니다. 귀하가 Python 프로그래머인 경우에도 이는 상당히 당혹스러운 동작이며, 이곳의 모든 사람이 Python 프로그래머인 것도 아닙니다.

해결책

Python에서 문자열은 객체이며 __add__ 연산자를 지원하므로 작성할 수 있습니다.:

"abc" + "def"

이것들은 리터럴이므로 이 덧셈은 컴파일러에 의해 여전히 최적화되어 제거될 수 있으며, CPython 컴파일러는 이미 그렇게 처리합니다. [2]

기존의 다른 대안으로는 여러 줄(삼중 따옴표) 문자열과 join 메서드가 있습니다.:

"""This string
   extends across
   multiple lines, but you may want to use something like
   Textwrap.dedent
   to clear out the leading spaces
   and/or reformat.
"""


>>> "".join(["empty", "string", "joiner"]) == "emptystringjoiner"
True

>>> " ".join(["space", "string", "joiner"]) == "space string joiner"
True

>>> "\n".join(["multiple", "lines"]) == "multiple\nlines" == (
"""multiple
lines""")
True

우려 사항

연산자 우선순위

Guido는 [2]이 변경 사항을 PEP에서 처리해야 한다고 밝혔습니다. %와 같은 다른 문자열 연산자에 몇 가지 경계 사례가 있었기 때문입니다. (str %가 유지된다고 가정하면 – PEP 3101 – 고급 문자열 서식 지정을 선호하여 제거될 수도 있습니다. [3])

해결책은 우선순위를 강제하기 위해 괄호를 사용하는 것이며 – 오늘날에도 사용할 수 있는 것과 동일한 해결책입니다.:

# Clearest, works today, continues to work, optimization is
# already possible.
("abc %s def" + "ghi") % var

# Already works today; precedence makes the optimization more
# difficult to recognize, but does not change the semantics.
"abc" + "def %s ghi" % var

다음과는 대조적으로:

# Already fails because modulus (%) is higher precedence than
# addition (+)
("abc %s def" + "ghi" % var)

# Works today only because adjacency is higher precedence than
# modulus.  This will no longer be available.
"abc %s" "def" % var

# So the 2-to-3 translator can automatically replace it with the
# (already valid):
("abc %s" + "def") % var

긴 명령

… 가독성이 있다고 생각하는 SQL 쿼리를 구성합니다 [4]:
rows = self.executesql("select cities.city, state, country"
                       "    from cities, venues, events, addresses"
                       "    where cities.city like %s"
                       "      and events.active = 1"
                       "      and venues.address = addresses.id"
                       "      and addresses.city = cities.id"
                       "      and events.venue = venues.id",
                       (city,))

또 다른 대안으로는 삼중 따옴표 문자열, +, .join이 있습니다.:

query="""select cities.city, state, country
             from cities, venues, events, addresses
             where cities.city like %s
               and events.active = 1"
               and venues.address = addresses.id
               and addresses.city = cities.id
               and events.venue = venues.id"""

query=( "select cities.city, state, country"
      + "    from cities, venues, events, addresses"
      + "    where cities.city like %s"
      + "      and events.active = 1"
      + "      and venues.address = addresses.id"
      + "      and addresses.city = cities.id"
      + "      and events.venue = venues.id"
      )

query="\n".join(["select cities.city, state, country",
                 "    from cities, venues, events, addresses",
                 "    where cities.city like %s",
                 "      and events.active = 1",
                 "      and venues.address = addresses.id",
                 "      and addresses.city = cities.id",
                 "      and events.venue = venues.id"])

# And yes, you *could* inline any of the above querystrings
# the same way the original was inlined.
rows = self.executesql(query, (city,))

정규 표현식

복잡한 정규 표현식은 때때로 여러 개의 암묵적으로 연결된 문자열로 표현되며, 각 정규식 구성 요소는 서로 다른 줄에 놓이고 그 뒤에 주석이 옵니다. 여기에 더하기 연산자를 삽입할 수 있지만, 정규식을 읽기는 더 어려워집니다. 한 가지 대안은 re.VERBOSE 옵션을 사용하는 것입니다. 또 다른 대안은 여러 += 줄을 사용하여 정규식을 구성하는 것입니다.:

# Existing idiom which relies on implicit concatenation
r = ('a{20}'  # Twenty A's
     'b{5}'   # Followed by Five B's
     )

# Mechanical replacement
r = ('a{20}'  +# Twenty A's
     'b{5}'   # Followed by Five B's
     )

# already works today
r = '''a{20}  # Twenty A's
       b{5}   # Followed by Five B's
    '''                 # Compiled with the re.VERBOSE flag

# already works today
r = 'a{20}'   # Twenty A's
r += 'b{5}'   # Followed by Five B's

국제화

일부 국제화 도구 – 특히 xgettext – 는 이미 암묵적 연결에 대해 특별 처리가 되어 있지만, Python의 명시적 연결에 대해서는 그렇지 않습니다. [5]

이러한 도구는 (이미 적법한) 항목을 추출하지 못합니다.:

_("some string" +
  " and more of it")

하지만 흔히 다음에 대해서는 특별 처리를 합니다.:

_("some string"
  " and more of it")

지나치게 긴 한 줄을 사용하는 것도 가능할 것입니다(xgettext는 메시지를 2048자로 제한하며 [7], 이는 Python이 강제하는 제한보다 짧습니다). 또는 삼중 따옴표 문자열을 사용할 수도 있지만, 이러한 해결책은 코드의 가독성을 어느 정도 희생합니다.:

# Lines over a certain length are unpleasant.
_("some string and more of it")

# Changing whitespace is not ideal.
_("""Some string
     and more of it""")
_("""Some string
and more of it""")
_("Some string \
and more of it")

이에 대한 단기적으로 좋은 해결책은 보이지 않습니다.

전환

제안된 새 구문은 현재 Python에서 이미 적법하므로 즉시 사용할 수 있습니다.

2에서 3으로 변환하는 변환기가 기계적으로 변경하도록 할 수 있습니다.:

"str1" "str2"
("line1"  #comment
 "line2")

다음으로:

("str1" + "str2")
("line1"   +#comments
 "line2")

사용자가 다른 관용구 중 하나를 사용하려는 경우 그렇게 할 수 있습니다. 이러한 관용구는 모두 이미 Python 2에서 유효하므로, 번역기를 수정하는 대신 원본 소스에 수정 사항을 적용할 수 있습니다.

미해결 문제

외부 텍스트 추출 도구, 또는 적어도 xgettext [6]를 특히 지원하는 더 나은 방법이 있습니까?

참고 자료