객체 공간(object space)¶
Contents
소개¶
객체 공간(object space)은 PyPy의 모든 객체를 생성하며, 그 객체들에 대한 연산을 수행하는 방법을 알고 있습니다. 객체 공간(object space)은 고정된 API를 제공하는 라이브러리라고 생각하면 이해하는 데 도움이 될 수 있습니다. 이 API는 연산의 집합과, 이미 알려진 Python 객체의 의미론에 부합하는 그 구현들로 이루어집니다.
예를 들어, add()는 연산이며, 객체 공간(object space)에는 이 연산의 구현들이 있습니다. 이 구현들은 숫자 덧셈(add()가 숫자에 대해 동작할 때), 연결(add()가 시퀀스에 대해 동작할 때) 등을 수행합니다.
바이트코드 인터프리터에 연결할 수 있는, 동작하는 객체 공간(object space)이 몇 가지 있습니다:
- 표준 객체 공간(object space)은 Python의 다양한 내장 타입과 객체를 완전하게 구현한 것입니다. 표준 객체 공간(object space)은 바이트코드 인터프리터와 함께 저희 Python 구현의 기반을 이룹니다. 내부적으로 표준 객체 공간(object space)은 다양한 application-level 객체(정수, 문자열, 리스트, 타입 등)를 구현하는 interpreter-level 클래스의 집합입니다. CPython과 비교하자면, 표준 객체 공간(object space)은 C 구조체인
PyIntObject,PyListObject등에 해당하는 것을 제공합니다. - 여러 가지 Object Space proxies는 다른 객체 공간(object space)(예: 표준 객체 공간)을 감싸서 새로운 기능을 추가합니다. 지연 계산 객체(연산이 수행될 때에만 계산되는 객체), 보안 검사 객체, 여러 머신에 분산되어 존재하는 분산 객체 등이 그러한 예입니다.
여기에서 설명하는 여러 객체 공간(object space)은 pypy/objspace에서 찾을 수 있습니다.
대부분의 객체 공간(object space) 연산은 애플리케이션 레벨 객체를 받고 반환한다는 점에 유의하십시오. 인터프리터는 이러한 객체를 불투명한 “블랙박스”로 취급합니다. 바이트코드 인터프리터가 애플리케이션 수준 객체의 값에 대해 어느 정도 알 수 있게 해 주는 연산은 극히 일부뿐입니다.
객체 공간(object space) 인터페이스¶
다음은 모든 객체 공간(object space)이 구현하는 공개 API입니다:
관리 함수¶
-
getexecutioncontext()¶ 현재 활성화된 실행 컨텍스트를 반환합니다. (pypy/interpreter/executioncontext.py).
-
getbuiltinmodule(name)¶ name으로 지정한 내장 모듈에 대한Module객체를 반환합니다. (pypy/interpreter/module.py).
객체 공간(object space)의 객체에 대한 연산¶
이 함수들은 “래핑된”(즉, 애플리케이션 수준) 객체를 인자로 받고, 반환값도 그런 객체입니다.
다음 함수들은 언어 수준의 구문에 직접 대응하는, 의미론이 단순한 연산을 구현합니다.
id, type, issubtype, iter, next, repr, str, len, hash,
getattr, setattr, delattr, getitem, setitem, delitem,
pos, neg, abs, invert, add, sub, mul, truediv, floordiv, div, mod, divmod, pow, lshift, rshift, and_, or_, xor,
nonzero, hex, oct, int, float, long, ord,
lt, le, eq, ne, gt, ge, cmp, coerce, contains,
inplace_add, inplace_sub, inplace_mul, inplace_truediv, inplace_floordiv, inplace_div, inplace_mod, inplace_pow, inplace_lshift, inplace_rshift, inplace_and, inplace_or, inplace_xor,
get, set, delete, userdel
-
call(w_callable, w_args, w_kwds)¶ 주어진 위치 인자(
w_args)와 키워드 인자(w_kwds)로 함수를 호출합니다.
-
index(w_obj)¶ w_obj를 사용하여 인덱스 조회(as introduced in CPython 2.5)를 구현합니다. 래핑된 정수 또는 long을 반환하며, 객체에__index__()특수 메서드가 없으면TypeError를 발생시킵니다.
-
is_(w_x, w_y)¶ w_x is w_y를 구현합니다.
-
isinstance(w_obj, w_type)¶ type(w_obj)와w_type를 인자로 하여issubtype()을 구현합니다.
편의 함수¶
다음 함수들은 매우 자주 사용되므로 단축 함수로 도입할 가치가 있었습니다. 다만 다른 여러 객체 공간(object space) 메서드를 사용하여 표현할 수 있으므로 반드시 필요한 것은 아닙니다.
-
eq_w(w_obj1, w_obj2)¶ w_obj1과w_obj2가 같으면True를 반환합니다.space.is_true(space.eq(w_obj1, w_obj2))의 축약형입니다.
-
is_w(w_obj1, w_obj2)¶ space.is_true(space.is_(w_obj1, w_obj2))의 축약형입니다.
-
hash_w(w_obj)¶ space.int_w(space.hash(w_obj))의 단축 표현입니다.
-
len_w(w_obj)¶ space.int_w(space.len(w_obj))의 단축 표현입니다.
위의 네 함수는 application-level 객체가 아니라 interpreter-level 객체를 반환한다는 점에 주의하십시오!
-
not_(w_obj)¶ space.newbool(not space.is_true(w_obj))의 단축 표현입니다.
-
finditem(w_obj, w_key)¶ getitem(w_obj, w_key)와 동일하지만, 키를 찾지 못하면 KeyError를 발생시키는 대신 인터프리터 수준의 None을 반환합니다.
-
call_function(w_callable, *args_w, **kw_w)¶ 인자들을 래핑된 튜플과 딕셔너리로 모은 다음
space.call(w_callable, ...)을 호출합니다.
-
call_method(w_object, 'method', ...)¶ space.getattr()를 사용해 메서드 객체를 가져온 다음,space.call_function()으로 이를 호출합니다.
-
unpackiterable(w_iterable[, expected_length=-1])¶ w_x를 (space.iter()와space.next()를 사용하여) 순회하며, 그 결과로 얻은 래핑된 객체들을 리스트에 모읍니다.expected_length가 주어졌는데 길이가 일치하지 않으면 예외를 발생시킵니다.물론 요소를 먼저 리스트에 모으는 것보다 직접 순회하는 편이 더 나은 경우에는
space.iter()와space.next()를 직접 사용해야 합니다.
-
unpacktuple(w_tuple[, expected_length=None])¶ unpackiterable()과 동일하지만, 튜플에만 해당합니다.
-
callable(w_obj)¶ 내장 함수
callable()을 구현합니다.
애플리케이션 수준 객체 생성¶
-
wrap(x)¶ 더 이상 사용되지 않습니다! 이 메서드는 결국 사라져야 합니다. 인터프리터 수준 객체
x에 대한 참조인 래핑된 객체를 반환합니다. 이 메서드는 단순한 불변 객체(정수, 문자열 등)에 사용하여 새로운 래핑된 객체를 만들 수도 있고,W_Root의 인스턴스에 사용하여 애플리케이션 수준에서 볼 수 있는 참조를 얻을 수도 있습니다. 예를 들어 바이트코드 인터프리터의 클래스 대부분은W_Root의 서브클래스이며, 함수, 프레임, 코드 객체 등은 이 방법으로 애플리케이션 수준 코드에 직접 노출될 수 있습니다.
-
newint(i)¶ 정숫값을 담은 래핑된 객체를 생성합니다.
newint는W_IntObject타입의 객체를 생성합니다.
-
newlong(l)¶ 정수 값을 담는 래핑된 객체를 생성합니다.
newint와의 주요 차이점은 인자의 타입(rpython.rlib.rbigint.rbigint)입니다. PyPy3에서 이 메서드는int를 반환합니다(PyPy2에서는long을 반환합니다).
-
newbytes(t)¶ 주어진 인자는 RPython 바이트 문자열입니다.
bytes타입의 래핑된 객체를 생성합니다(PyPy2와 PyPy3 모두 해당).
-
newtext(t)¶ 주어진 인자는 rpython 바이트 문자열입니다.
str타입의 래핑된 객체를 생성합니다. PyPy3에서는 래핑된 유니코드 객체를 반환합니다. 이 객체는 t를 utf-8-nosg로 디코딩한 값을 담습니다. 여기서 사용하는 “utf-8-nosg” 코덱은 Python 2나 Python 3에 구현된 “utf-8”과 약간 다릅니다. 이 코덱은 서로게이트 문자를 특별히 처리하지 않는 utf-8로 정의됩니다. 서로게이트 문자는'\u0800'부터'\uffff'까지의 범위에 있는 모든 문자를 인코딩할 때와 같은 3바이트 시퀀스로 인코딩됩니다.PyPy2는 바이트 문자열 객체를 반환합니다. 인코딩/디코딩 단계는 전혀 적용되지 않습니다.
-
newtuple([w_x, w_y, w_z, ...])¶ 래핑된 객체들로 이루어진 인터프리터 수준 리스트로부터 래핑된 새 튜플을 생성합니다.
-
newlist([..])¶ 인터프리터 수준의 래핑된 객체 리스트로부터 래핑된
list를 생성합니다.
-
newdict()¶ 새로운 빈 딕셔너리를 반환합니다.
-
newslice(w_start, w_end, w_step)¶ 새 슬라이스 객체를 생성합니다.
-
newunicode(ustr)¶ rpython 유니코드 문자열로부터 Unicode 문자열을 생성합니다. 이 메서드는 곧 사라지고 :py:function:`newutf8` 함수로 대체될 수 있습니다.
-
newutf8(bytestr)¶ RPython 바이트 문자열을 “utf-8-nosg”로 디코딩하여 유니코드 문자열을 만듭니다. PyPy3에서는 :py:function:`newtext` 함수와 같습니다.
그 밖의 여러 공간 연산은 pypy/interpreter/baseobjspace.py와 pypy/objspace/std/objspace.py에서 찾아볼 수 있습니다.
애플리케이션 수준에서 인터프리터 수준으로의 변환¶
-
unwrap(w_x)¶ w_x의 인터프리터 수준 등가물을 반환합니다. 이 메서드는 RPython이 아니므로 변환될 수 없으니, 테스트 목적으로만 사용하십시오! 대부분의 경우에는 대신 아래에 설명된 함수들을 사용해야 합니다.
-
is_true(w_x)¶ 래핑된 객체
w_x의 진릿값을 나타내는 인터프리터 수준의 불리언(True또는False)을 반환합니다.이것은 특히 중요한 연산입니다. 예를 들어 언어의 if 문을 구현하는 데(좀 더 엄밀히 말하면, if 문이 컴파일되어 만들어지는 조건 분기 바이트코드를 구현하는 데) 반드시 필요하기 때문입니다.
-
int_w(w_x)¶ w_x가 오버플로 없이 정수로 변환될 수 있는 애플리케이션 수준의 integer 또는 long이면, 인터프리터 수준의 정수를 반환합니다. 그렇지 않으면TypeError또는OverflowError를 발생시킵니다.
-
bigint_w(w_x)¶ w_x가 애플리케이션 수준 정수 또는 long이면 인터프리터 수준rbigint를 반환합니다. 그렇지 않으면TypeError를 발생시킵니다.
-
ObjSpace.bytes_w(w_x)¶ Takes an application level
bytes(on PyPy2 this equals str) and returns a rpython byte string.
-
ObjSpace.text_w(w_x)¶ PyPy2 takes either a
strand returns a rpython byte string, or it takes anunicodeand uses the systems default encoding to return a rpython byte string.On PyPy3 it takes a
strand it will return an utf-8 encoded rpython string.
-
str_w(w_x)¶ 폐기 예정입니다. 대신 text_w 또는 bytes_w를 사용하십시오
w_x가 애플리케이션 수준 문자열이면 인터프리터 수준 문자열을 반환합니다. 그렇지 않으면TypeError를 발생시킵니다.
-
unicode_w(w_x)¶ 애플리케이션 수준의
unicode를 받아 인터프리터 수준의 유니코드 문자열을 반환합니다. 이 메서드는 곧 사라지고 :py:function:`text_w` 함수로 대체될 수 있습니다.
-
float_w(w_x)¶ w_x가 애플리케이션 수준의 float, 정수 또는 long이면 인터프리터 수준의 float를 반환합니다. 그렇지 않으면TypeError를 발생시킵니다(매우 큰 long의 경우에는OverflowError를 발생시킵니다).
-
getindex_w(w_obj[, w_exception=None])¶ index(w_obj)를 호출합니다. 그 결과로 얻은 정수 또는 long 객체를 인터프리터 수준의int로 변환할 수 있으면 그 값을 반환합니다. 변환할 수 없으면w_exception이 None일 때는 범위에 맞게 잘라낸(clamped) 결과를 반환하고, 그렇지 않으면 애플리케이션 수준에서 예외를 발생시킵니다.(
w_obj를 인덱스로 변환할 수 없으면index()는 애플리케이션 수준TypeError를 발생시킵니다.)
-
interp_w(RequiredClass, w_x[, can_be_None=False])¶ w_x가 주어진 바이트코드 인터프리터 클래스의 래핑된 인스턴스이면, 이를 언래핑하여 반환합니다.can_be_None이True이면 래핑된None도 허용되며, 이 경우 인터프리터 수준의None을 반환합니다. 그렇지 않으면, 알기 쉬운 오류 메시지와 함께TypeError를 감싼OperationError를 발생시킵니다.
-
interpclass_w(w_x)¶ w_x가 바이트코드 인터프리터 클래스(예를 들어Function,Frame,Cell등)의 래핑된 인스턴스이면, 래핑을 해제하여 반환합니다. 그렇지 않으면None을 반환합니다.
데이터 멤버¶
-
space.builtin¶ 내장 객체를 포함하는
Module입니다.
-
space.sys¶ sysModule입니다.
-
space.w_None¶ ObjSpace의
None인스턴스입니다.
-
space.w_True¶ ObjSpace의
True인스턴스입니다.
-
space.w_False¶ ObjSpace의
False인스턴스입니다.
-
space.w_Ellipsis¶ ObjSpace의
Ellipsis인스턴스입니다.
-
space.w_NotImplemented¶ ObjSpace의
NotImplemented인스턴스입니다.
-
space.w_int¶ -
space.w_float space.w_long space.w_tuple space.w_str space.w_unicode space.w_type space.w_instance space.w_slice Python에서 가장 흔히 쓰이는 기본 타입 객체들입니다.
-
space.w_[XYZ]Error Python의 내장 예외 클래스(
KeyError,IndexError등).
-
ObjSpace.MethodTable¶ 인터페이스의 일반적인 부분에 대한
(method_name, symbol, number_of_arguments, list_of_special_names)를 담은 튜플의 목록입니다.튜플은 인터프리터 수준이라는 점에 유의하십시오.
-
ObjSpace.BuiltinModuleTable¶ 내장 모듈 이름 목록입니다.
-
ObjSpace.ConstantTable¶ 객체 공간(object space)이 정의해야 하는 상수의 이름 목록입니다.
-
ObjSpace.ExceptionTable¶ 예외 클래스 이름의 목록입니다.
-
ObjSpace.IrregularOpTable¶ 불규칙한 API를 가진(래핑된 객체가 아닌 객체를 인자로 받거나 반환하는) 메서드의 이름 목록.
표준 객체 공간(object space)¶
소개¶
표준 객체 공간(object space)(pypy/objspace/std/)은 CPython의 객체 라이브러리(배포판의 Objects/ 하위 디렉터리)에 직접 대응합니다. 이는 일반적인 Python 타입들을 더 저수준의 언어로 구현한 것입니다.
표준 객체 공간(object space)은 추상 부모 클래스인 W_Object와 함께 W_IntObject, W_ListObject 등의 서브클래스를 정의합니다. 래핑된 객체(바이트코드 인터프리터의 메인 루프 입장에서는 “블랙박스”)는 이 클래스들 중 하나의 인스턴스입니다. 메인 루프가 두 래핑된 객체 w1과 w2 사이에서 (덧셈과 같은) 연산을 호출하면, 표준 객체 공간(object space)은 (CPython의 Object/abstract.c와 유사한) 내부 디스패치를 수행한 뒤 해당 연산을 수행할 수 있는 적절한 W_XYZObject 클래스의 메서드를 호출합니다.
연산 자체는 RPython이 허용하는 기본 요소로 수행되며, 그 결과는 래핑된 객체로 구성됩니다. 예를 들어, 다음의 정수 덧셈 구현을 Object/intobject.c에 있는 int_add() 함수와 비교해 보십시오:
def add__Int_Int(space, w_int1, w_int2):
x = w_int1.intval
y = w_int2.intval
try:
z = ovfcheck(x + y)
except OverflowError:
raise FailedToImplementArgs(space.w_OverflowError,
space.wrap("integer addition"))
return W_IntObject(space, z)
정수 객체 하나를 위해 이렇게까지 하는 것은 수고가 많아 보일 수 있습니다(일반 정수를 그대로 쓰지 않고 왜 W_IntObject 인스턴스로 감쌀까요?). 하지만 단순한 정수부터 더 복잡한 타입까지 모든 객체를 같은 방식으로 감싸면 코드를 단순하고 읽기 쉽게 유지할 수 있습니다.
(흥미롭게도, 위의 명백한 최적화는 실제로 PyPy에 구현되어 있지만 이 수준에서 하드코딩되어 있지는 않습니다 – 표준 인터프리터 최적화를 참조하십시오.)
객체 타입¶
pypy/objspace/std/ 패키지의 대부분은 Python 표준 내장 객체 타입의 라이브러리를 정의하고 구현합니다. 각 타입 xxx(int, float, list, tuple, str, type 등)는 일반적으로 xxxobject.py 모듈에 구현됩니다.
W_AbstractXxxObject 클래스가 있는 경우, 이 클래스는 추상 기반 클래스이며 주로 Python 수준의 타입 객체에 나타나는 내용을 정의합니다. 그리고 실제 구현은 서브클래스로 존재하며, 이 서브클래스는 W_XxxObject라고 불리거나, 여러 가지 구현이 있는 경우에는 이를 변형한 이름으로 불립니다. 예를 들어, pypy/objspace/std/bytesobject.py는 str 애플리케이션 수준 타입을 빌드하는 데 필요한 모든 것을 담고 있는 W_AbstractBytesObject를 정의하며, 그 서브클래스로 W_BytesObject(일반적인 문자열)와 W_Buffer(반복적인 덧셈에 맞게 조정된 특수 구현으로, pypy/objspace/std/bufferobject.py에 있습니다)가 있습니다. 리스트나 딕셔너리 같은 가변 데이터 타입의 경우, 실제 데이터에 대한 간접 참조와 전략(strategy)을 가진 단일 클래스 W_ListObject 또는 W_DictMultiObject가 있으며, 전략은 객체의 내용이 변함에 따라 바뀔 수 있습니다.
사용자 관점에서는 W_AbstractXxxObject 서브클래스가 여러 개 있더라도 이 사실이 드러나지 않습니다. 애플리케이션 수준에서 이들은 여전히 모두 정확히 같은 Python 타입의 인스턴스입니다. 예를 들어 PyPy는 인터프리터 수준의 W_BytesObject 인스턴스의 애플리케이션 수준 타입이 str임을 알고 있습니다. W_BytesObject에 pypy/objspace/std/bytesobject.py의 문자열 타입 명세를 다시 가리키는 typedef 클래스 속성이 있기 때문입니다. 다른 모든 문자열 구현도 pypy/objspace/std/bytesobject.py의 같은 typedef를 사용합니다.
동일한 Python 타입을 여러 방식으로 구현한 다른 예시는 표준 인터프리터 최적화를 참조하십시오.
객체 공간(object space) 프록시¶
저희는 프록시 객체 공간을 여러 개 구현했습니다. 프록시 객체 공간은 다른 객체 공간(object space)(보통 표준 객체 공간)을 감싸서 모든 객체에 몇 가지 기능을 추가합니다. 자세한 내용은 투명 프록시(지원 중단됨)를 참조하십시오.