PEP 245 – 파이썬 인터페이스 구문
- Author:
- Michel Pelletier <michel at users.sourceforge.net>
- Status:
- Rejected
- Type:
- Standards Track
- Created:
- 11-Jan-2001
- Python-Version:
- 2.2
- Post-History:
- 21-Mar-2001
번역·라이선스 안내
이 비공식 한국어 번역은 원문 Copyright 절의 Public Domain 조건에 따라 제공합니다. 원저자와 공식 원문은 그대로 표시합니다. 수정되지 않은 기준 원문 · 공식 최신판
Note
여기 원래 링크되어 있었으며 이 PEP에 관한 추가 자료 링크를 담고 있던, 더 이상 사용할 수 없는 Zope 인터페이스 위키 페이지(https://www.zope.org/Wikis/Interfaces)는 Wayback Machine 아카이브에서 찾을 수 있습니다. 또한, 이 PEP가 논의되었던 Interface-Dev Zope 메일링 리스트는 폐쇄되었지만, 그 아카이브는 여전히 이용할 수 있습니다.
거부 공지
저는 이 PEP를 거부합니다. 이제 5년이 지났습니다. 언젠가는 파이썬에 인터페이스가 생길 것으로 예상하지만, 그것이 이 PEP의 구문과 비슷할 것이라고 기대하는 것은 순진한 생각일 것입니다. 또한, PEP 246은 완전히 다른 무언가를 위해 거부되고 있으며, 인터페이스는 적응(adaptation)이나 그것을 대체할 무언가에서 아무런 역할도 하지 않을 것입니다. GvR.
소개
이 PEP는 파이썬에서 인터페이스 객체를 생성하기 위한 제안된 구문을 설명합니다.
개요
파이썬에 정적 타입 시스템을 추가하는 것을 고려하는 것 외에도, Types-SIG는 파이썬을 위한 인터페이스 시스템을 고안하는 임무도 맡았습니다. 1998년 12월, Jim Fulton은 SIG의 논의를 바탕으로 한 프로토타입 인터페이스 시스템을 발표했습니다. 이 논의와 프로토타입에 관한 많은 쟁점과 배경 정보는 SIG 아카이브 [1]에서 찾을 수 있습니다.
2000년 말경, Digital Creations는 Zope [2]를 위한 더 나은 컴포넌트 모델 설계를 고민하기 시작했습니다. Zope의 미래 컴포넌트 모델은 인터페이스 객체에 크게 의존합니다. 이는 Jim의 “Scarecrow” 인터페이스 프로토타입의 추가 개발로 이어졌습니다. 버전 2.3부터 Zope는 표준 소프트웨어로 Interface 패키지를 포함합니다. Zope의 Interface 패키지는 이 PEP의 참조 구현으로 사용됩니다.
이 PEP에서 제안하는 문법은 PEP 232에서 설명하는 문법 개선에 의존하며, PEP 233이 기반으로 삼을 수 있는 기저 프레임워크를 설명합니다. 인터페이스 객체와 Proxy 객체에 관해 진행 중인 작업이 있으므로, 이 PEP의 선택적 부분에 대해서는 [3]을 참고하시기 바랍니다.
문제
인터페이스는 소프트웨어 개발 중 발생하는 여러 문제를 해결하기 때문에 중요합니다:
- 파이썬에는 흔히 “프로토콜”이라 불리는 암묵적 인터페이스가 많이 있습니다. 현재 이러한 프로토콜을 판별하는 것은 구현체 인트로스펙션에 기반하지만, 종종 이마저도 실패합니다. 예를 들어,
__getitem__을 정의하면 시퀀스와 매핑을 모두 암시합니다(전자는 순차적인 정수 키를 사용합니다). 개발자가 객체가 구현하려는 프로토콜을 명시적으로 나타낼 방법이 없습니다. - 개발자의 관점에서 볼 때, Python은 타입과 클래스의 구분으로 인해 제약을 받습니다. 타입이 요구되는 경우, 사용자는 ‘foo’가 문자열인지 판단하기 위해 ‘type(foo) == type(“”)’과 같은 코드를 사용합니다. 클래스의 인스턴스가 요구되는 경우, 사용자는 ‘foo’가 ‘MyString’ 클래스의 인스턴스인지 판단하기 위해 ‘isinstance(foo, MyString)’을 사용합니다. 어떤 객체가 특정한 유효한 방식으로 사용될 수 있는지 판단하기 위한 통합된 모델은 존재하지 않습니다.
- Python의 동적 타이핑은 매우 유연하고 강력하지만, 타입 검사를 제공하는 정적 타입 언어의 이점은 갖지 못합니다. 정적 타입 언어는 훨씬 더 높은 타입 안전성을 제공하지만, 객체를 공통 서브클래싱을 통해서만 일반화할 수 있고 캐스팅을 통해 구체적으로 사용해야 하기 때문에 (예를 들어 Java의 경우) 지나치게 장황해지는 경우가 많습니다.
또한 인터페이스가 해결하고자 하는 여러 문서화 문제들도 있습니다.
- 개발자들은 객체가 어떻게 동작하는지 파악하기 위해 여러분 시스템의 소스 코드를 들여다보는 데 많은 시간을 낭비합니다.
- 여러분의 시스템을 처음 접하는 개발자들은 객체가 어떻게 동작하는지 오해할 수 있으며, 이는 사용 오류를 유발하고 심지어 확산시킬 수도 있습니다.
- 인터페이스가 없다는 것은 사용법을 소스 코드로부터 추론해야 한다는 것을 의미하므로, 개발자들은 결국 “내부 전용”으로 의도된 메서드와 어트리뷰트를 사용하게 될 수도 있습니다.
- 코드를 살펴보는 일은 어려울 수 있으며, 고수가 작성한 코드를 제대로 이해하려는 초보 프로그래머에게는 매우 낙담스러운 일이 될 수 있습니다.
- 많은 사람이 (문서화되지 않은 소프트웨어와 같은) 모호함을 이해하려고 매우 애쓸 때 많은 시간이 낭비됩니다. 인터페이스를 미리 문서화하는 데 들이는 노력은 결국 이러한 시간의 상당 부분을 절약해 줄 것입니다.
인터페이스는 여러분의 객체에 대한 계약상 의무를 명시하는 방법과, 객체 사용법에 대한 문서화, 그리고 그 계약과 문서를 발견할 수 있는 내장 메커니즘을 제공함으로써 이러한 문제들을 해결하고자 합니다.
Python은 매우 유용한 자기 성찰(introspection) 기능을 갖추고 있습니다. Python이 객체에 대한 온갖 종류의 정보—타입, 문서화 문자열, 인스턴스 딕셔너리, 베이스 클래스, 언바운드 메서드 등—를 살펴볼 수 있는 능력을 제공하기 때문에, 이것이 대화형 인터프리터에서 개념을 탐구하는 일을 더 쉽게 만들어 준다는 것은 잘 알려져 있습니다.
이러한 기능들 중 많은 부분은 소프트웨어의 구현을 인트로스펙션하고, 사용하고, 변경하는 데 중점을 두고 있으며, 그중 하나(“독스트링”)는 문서화를 제공하는 데 중점을 두고 있습니다. 이 제안서는 객체의 인터페이스를 기술하는, 이러한 자연스러운 인트로스펙션 프레임워크에 대한 확장을 설명합니다.
인터페이스 구문 개요
대체로 인터페이스의 구문은 클래스의 구문과 매우 유사하지만, 미래의 필요성이나 논의 과정에서 제기되는 필요성에 따라 인터페이스 구문의 새로운 가능성이 정의될 수 있습니다.
구문에 대한 정식 BNF 설명은 이 PEP의 뒷부분에 나와 있으며, 예시를 위해 여기에서는 제안된 구문으로 생성한 두 가지 서로 다른 인터페이스의 예를 보입니다.:
interface CountFishInterface:
"Fish counting interface"
def oneFish():
"Increments the fish count by one"
def twoFish():
"Increments the fish count by two"
def getFishCount():
"Returns the fish count"
interface ColorFishInterface:
"Fish coloring interface"
def redFish():
"Sets the current fish color to red"
def blueFish():
"Sets the current fish color to blue"
def getFishColor():
"This returns the current fish color"
이 코드는 평가되면 CountFishInterface와 ColorFishInterface라는 두 개의 인터페이스를 생성합니다. 이 인터페이스들은 interface 문으로 정의됩니다.
인터페이스와 그 메서드에 대한 산문 형태의 문서는 독스트링에서 옵니다. 메서드 시그니처 정보는 def 문의 시그니처에서 옵니다. def 문에 본문이 없다는 점에 주목하십시오. 인터페이스는 어떤 서비스도 구현하지 않으며, 단지 서비스를 기술할 뿐입니다. 인터페이스와 인터페이스 메서드에는 독스트링이 필수이며, ‘pass’ 문은 제공될 수 없습니다. 인터페이스에서 pass 문에 해당하는 것은 빈 독스트링입니다.
다른 인터페이스를 “확장”하는 인터페이스를 만들 수도 있습니다. 여기에서는 CountFishInterface와 ColorFishInterface를 확장하는 새로운 유형의 Interface를 볼 수 있습니다.:
interface FishMarketInterface(CountFishInterface, ColorFishInterface):
"This is the documentation for the FishMarketInterface"
def getFishMonger():
"Returns the fish monger you can interact with"
def hireNewFishMonger(name):
"Hire a new fish monger"
def buySomeFish(quantity=1):
"Buy some fish at the market"
FishMarketInterface는 CountFishInterface와 ColorfishInterface를 확장합니다.
인터페이스 단언(Interface Assertion)
다음 단계는 인터페이스를 구현한다고 단언하는 구체적인 Python 클래스를 작성하여 클래스와 인터페이스를 결합하는 것입니다. 다음은 이를 수행할 수 있는 예제 FishMarket 컴포넌트입니다.:
class FishError(Error):
pass
class FishMarket implements FishMarketInterface:
number = 0
color = None
monger_name = 'Crusty Barnacles'
def __init__(self, number, color):
self.number = number
self.color = color
def oneFish(self):
self.number += 1
def twoFish(self):
self.number += 2
def redFish(self):
self.color = 'red'
def blueFish(self):
self.color = 'blue'
def getFishCount(self):
return self.number
def getFishColor(self):
return self.color
def getFishMonger(self):
return self.monger_name
def hireNewFishMonger(self, name):
self.monger_name = name
def buySomeFish(self, quantity=1):
if quantity > self.count:
raise FishError("There's not enough fish")
self.count -= quantity
return quantity
이 새로운 클래스 FishMarket은 FishMarketInterface를 구현하는 구체적인 클래스를 정의합니다. implements 문 뒤에 오는 객체를 “인터페이스 단언”이라고 부릅니다. 인터페이스 단언은 인터페이스 객체이거나, 인터페이스 단언들의 튜플일 수 있습니다.
이와 같이 class 문에 제공된 인터페이스 단언은 클래스의 __implements__ 클래스 속성에 저장됩니다. 위 예제를 해석하고 나면, ‘implements’ 내장 함수를 사용하여 다음과 같이 검사할 수 있는 클래스 문을 갖게 됩니다.:
>>> FishMarket
<class FishMarket at 8140f50>
>>> FishMarket.__implements__
(<Interface FishMarketInterface at 81006f0>,)
>>> f = FishMarket(6, 'red')
>>> implements(f, FishMarketInterface)
1
>>>
클래스는 하나 이상의 인터페이스를 구현할 수 있습니다. 예를 들어, 어떤 객체가 컨테이너 객체 안의 항목으로서 어떻게 동작하는지를 기술하는 ItemInterface라는 인터페이스가 있다고 가정하십시오. FishMarket 인스턴스가 FishMarketInterface뿐만 아니라 ItemInterface 인터페이스도 구현한다고 단언하고 싶다면, FishMarket 클래스에 인터페이스 객체들의 튜플을 담은 인터페이스 단언을 제공할 수 있습니다.:
class FishMarket implements FishMarketInterface, ItemInterface:
# ...
인터페이스 단언은 한 클래스가 어떤 인터페이스를 구현하며, 동시에 다른 클래스가 구현하는 모든 인터페이스도 구현한다고 단언하고자 할 때에도 사용할 수 있습니다.:
class MyFishMarket implements FishMarketInterface, ItemInterface:
# ...
class YourFishMarket implements FooInterface, MyFishMarket.__implements__:
# ...
이 새로운 클래스 YourFishMarket은 FooInterface를 구현할 뿐만 아니라, MyFishMarket 클래스가 구현하는 인터페이스들도 구현한다고 단언합니다.
인터페이스 단언에 대해 조금 더 자세히 살펴볼 가치가 있습니다. 인터페이스 단언은 인터페이스 객체이거나, 인터페이스 단언들의 튜플입니다. 예를 들면:
FooInterface
FooInterface, (BarInterface, BobInterface)
FooInterface, (BarInterface, (BobInterface, MyClass.__implements__))
모두 유효한 인터페이스 어서션(assertion)입니다. 두 인터페이스가 동일한 속성을 정의하는 경우, 어서션에서 정보가 우선시되는 순서는 위에서 아래로, 왼쪽에서 오른쪽입니다.
단순성을 위해 클래스와 인터페이스의 개념을 결합하여 간단한 인터페이스 강제를 제공하는 다른 인터페이스 제안들도 있습니다. 인터페이스 객체는 이러한 동작을 구현하는 지연된 클래스를 반환하는 deferred메서드를 가지고 있습니다:
>>> FM = FishMarketInterface.deferred()
>>> class MyFM(FM): pass
>>> f = MyFM()
>>> f.getFishMonger()
Traceback (innermost last):
File "<stdin>", line 1, in ?
Interface.Exceptions.BrokenImplementation:
An object has failed to implement interface FishMarketInterface
The getFishMonger attribute was not provided.
>>>
이는 해당 인터페이스를 구현하기 위해 무엇을 빠뜨렸는지 알려줌으로써 일종의 수동적인 인터페이스 강제를 제공합니다.
공식 인터페이스 구문
Python 구문은 Python 참조 매뉴얼 [4]에 설명된 변형 BNF 문법 표기법으로 정의됩니다. 이 절에서는 이 문법을 사용하여 제안된 인터페이스 구문을 설명합니다:
interfacedef: "interface" interfacename [extends] ":" suite
extends: "(" [expression_list] ")"
interfacename: identifier
인터페이스 정의는 실행 가능한 문장입니다. 이는 먼저 extends 목록이 있는 경우 이를 평가합니다. extends 목록의 각 항목은 인터페이스 객체로 평가되어야 합니다.
그런 다음 인터페이스의 스위트는 새로 생성된 로컬 네임스페이스와 원래의 전역 네임스페이스를 사용하여 새로운 실행 프레임(Python 참조 매뉴얼 4.1절 참조)에서 실행됩니다. 인터페이스의 스위트가 실행을 마치면 그 실행 프레임은 폐기되지만, 로컬 네임스페이스는 인터페이스 요소로 저장됩니다. 그런 다음 베이스 인터페이스에 대한 extends 목록과 저장된 인터페이스 요소를 사용하여 인터페이스 객체가 생성됩니다. 인터페이스 이름은 원래의 로컬 네임스페이스에서 이 인터페이스 객체에 바인딩됩니다.
이 PEP는 또한 Python의 ‘class’ 문에 대한 확장을 제안합니다:
classdef: "class" classname [inheritance] [implements] ":" suite
implements: "implements" implist
implist: expression-list
classname,
inheritance,
suite,
expression-list: see the Python Reference Manual
클래스의 스위트가 실행되기 전에, ‘inheritance’와 ‘implements’ 문이 있다면 평가됩니다. ‘inheritance’ 동작은 언어 참조서 7.6절에 정의된 대로 변경되지 않습니다.
‘implements’가 있다면, 상속 이후에 평가됩니다. 이는 반드시 인터페이스 명세로 평가되어야 하며, 인터페이스 명세는 인터페이스 하나이거나 인터페이스 명세들의 튜플입니다. 유효한 인터페이스 명세가 있으면, 그 단언은 튜플 형태로 클래스 객체의 ‘__implements__’ 속성에 할당됩니다.
이 PEP는 함수 정의나 할당의 문법에 어떠한 변경도 제안하지 않습니다.
클래스와 인터페이스
위의 예시 인터페이스들은 각 메서드에 대해 어떠한 동작도 기술하지 않으며, 단지 일반적인 FishMarket 객체가 실현할 인터페이스를 기술할 뿐입니다.
다른 인터페이스를 확장하는 인터페이스와 다른 클래스를 서브클래싱하는 클래스 사이에서 유사성을 발견할 수도 있습니다. 이는 유사한 개념입니다. 하지만 인터페이스는 인터페이스를 확장하고 클래스는 클래스를 서브클래싱한다는 점에 유의하는 것이 중요합니다. 클래스를 확장하거나 인터페이스를 서브클래싱할 수는 없습니다. 클래스와 인터페이스는 별개입니다.
클래스의 목적은 객체가 작동하는 방식의 구현을 공유하는 것입니다. 인터페이스의 목적은 객체가 어떻게 구현되었는지가 아니라, 객체를 어떻게 다뤄야 하는지를 문서화하는 것입니다. 서로 다른 구현을 가진 여러 클래스가 동일한 인터페이스를 실현하는 것도 가능합니다.
인터페이스의 기능 일부를 섞어 넣는 여러 클래스로 하나의 인터페이스를 구현하는 것도 가능하며, 반대로 하나의 클래스가 여러 인터페이스를 구현하는 것도 가능합니다. 이 때문에 인터페이스와 클래스를 혼동하거나 뒤섞어서는 안 됩니다.
인터페이스를 인식하는 내장 함수
인터페이스 객체를 고려할 때 파이썬의 내장 함수 목록에 유용하게 추가할 수 있는 것은 implements()일 것입니다. 이 내장 함수는 객체와 인터페이스라는 두 개의 인자를 받아, 객체가 해당 인터페이스를 구현하면 참 값을, 그렇지 않으면 거짓 값을 반환합니다. 예를 들면:
>>> interface FooInterface: pass
>>> class Foo implements FooInterface: pass
>>> f = Foo()
>>> implements(f, FooInterface)
1
현재 이 기능은 참조 구현에서 Interface 패키지 내의 함수들로 존재하며, 사용하려면 “import Interface”가 필요합니다. 이것이 내장 함수로 존재하는 것은 순전히 편의를 위한 것일 뿐 인터페이스를 사용하는 데 필수적이지는 않으며, 클래스에 대한 isinstance()와 유사합니다.
하위 호환성
제안된 인터페이스 모델은 파이썬에서 어떠한 하위 호환성 문제도 일으키지 않습니다. 그러나 제안된 문법은 그렇지 않습니다.
interface를 식별자로 사용하는 기존 코드는 모두 깨질 것입니다. interface를 새로운 키워드로 정의함으로써 발생할 수 있는 다른 종류의 하위 호환성 비호환도 있을 수 있습니다. 파이썬 문법에 대한 이 확장은 기존 문법을 하위 호환성을 해치는 방식으로 변경하지 않습니다.
새로운 from __future__ 파이썬 문법(PEP 236)과 새로운 경고 프레임워크(PEP 230)는 이 하위 호환성 문제를 해결하는 데 이상적입니다. 지금 인터페이스 문법을 사용하려면, 개발자는 다음 문장을 사용할 수 있습니다.:
from __future__ import interfaces
또한, interface 키워드를 식별자로 사용하는 모든 코드에는 파이썬이 경고를 발생시킵니다. 적절한 기간이 지나면 interface 구문이 표준이 되고, 위의 import 문은 아무 동작도 하지 않게 되며, interface라는 이름의 식별자는 예외를 발생시키게 됩니다. 이 기간은 24개월로 제안됩니다.
파이썬에 대한 제안된 변경 사항 요약
새로운 interface 키워드를 추가하고 implements로 클래스 구문을 확장합니다.
클래스 인터페이스를 확장하여 __implements__를 포함시킵니다.
‘implements(obj, interface)’ 내장 함수를 추가합니다.
위험 요소
이 PEP는 파이썬 언어에 새로운 키워드 하나, 즉 interface를 추가할 것을 제안합니다. 이는 코드를 깨뜨리게 됩니다.
미해결 문제
목표
구문
아키텍처
반대 의견
이 PEP는 아직 python-dev에서 논의되지 않았습니다.
참고 자료
Copyright
This document has been placed in the public domain.