객체 공간(object space)

소개

객체 공간(object space)은 PyPy에서 모든 객체를 생성하며, 그 객체에 대한 연산을 수행하는 방법을 알고 있습니다. 객체 공간(object space)을 고정된 API, 즉 Python 객체의 알려진 의미론에 대응하는 구현과 함께 하는 일련의 연산을 제공하는 라이브러리로 생각하면 도움이 될 수 있습니다.

예를 들어, add()는 연산이며, 객체 공간(object space)에는 (add()가 숫자에 대해 동작할 때는) 숫자 덧셈을, (add()가 시퀀스에 대해 동작할 때는) 연결(concatenation)을 수행하는 등의 구현들이 있습니다.

바이트코드 인터프리터에 연결될 수 있는 몇 가지 작동하는 객체 공간(object space)이 있습니다:

  • 표준 객체 공간(object space)은 파이썬의 다양한 내장 타입과 객체에 대한 완전한 구현입니다. 표준 객체 공간(object space)은 바이트코드 인터프리터와 함께 우리 파이썬 구현의 기반입니다. 내부적으로 이것은 정수, 문자열, 리스트, 타입 등 다양한 application-level 객체를 구현하는 interpreter-level 클래스의 집합입니다. CPython과 비교하자면, 표준 객체 공간(object space)은 C 구조체 PyIntObject, PyListObject 등에 상응하는 것을 제공합니다.
  • 다양한 객체 공간(object space) 프록시는 다른 객체 공간(object space)(예: 표준 객체 공간)을 감싸고, 지연 계산 객체(연산이 수행될 때만 계산되는 객체), 보안 검사 객체, 여러 기계에 분산되어 있는 분산 객체 등과 같은 새로운 기능을 추가합니다.

여기에 문서화된 다양한 객체 공간(object space)들은 pypy/objspace에서 찾을 수 있습니다.

대부분의 객체 공간(object space) 연산은 application-level 객체를 받아 반환하며, 인터프리터는 이 객체를 불투명한 “블랙박스”로 취급합니다. 극히 일부 연산만이 바이트코드 인터프리터가 application-level 객체의 값에 대해 어느 정도 알 수 있게 해줍니다.

객체 공간(object space) 인터페이스

이것은 모든 객체 공간(object space)이 구현하는 공개 API입니다:

관리 함수

getexecutioncontext()

Return the currently active execution context. (pypy/interpreter/executioncontext.py).

getbuiltinmodule(name)

Return a Module object for the built-in module given by name. (pypy/interpreter/module.py).

객체 공간(object space)의 객체에 대한 연산

이 함수들은 “래핑된”(즉, application-level) 객체를 받고 반환합니다.

다음 함수들은 언어 수준 구문에 직접 대응하는, 명확한 의미를 가진 연산을 구현합니다:

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)

Calls a function with the given positional (w_args) and keyword (w_kwds) arguments.

index(w_obj)

Implements index lookup (as introduced in CPython 2.5) using w_obj. Will return a wrapped integer or long, or raise a TypeError if the object doesn’t have an __index__() special method.

is_(w_x, w_y)

Implements w_x is w_y.

isinstance(w_obj, w_type)

Implements issubtype() with type(w_obj) and w_type as arguments.

편의 함수

다음 함수들은 매우 자주 사용되어서 단축 표현으로 도입할 가치가 있었습니다. 다만, 다른 여러 객체 공간(object space) 메서드를 사용하여 표현할 수 있으므로 반드시 필요한 것은 아닙니다.

eq_w(w_obj1, w_obj2)

Returns True when w_obj1 and w_obj2 are equal. Shortcut for space.is_true(space.eq(w_obj1, w_obj2)).

is_w(w_obj1, w_obj2)

Shortcut for space.is_true(space.is_(w_obj1, w_obj2)).

hash_w(w_obj)

Shortcut for space.int_w(space.hash(w_obj)).

len_w(w_obj)

Shortcut for space.int_w(space.len(w_obj)).

위 네 함수가 application-level 객체가 아니라 interpreter-level 객체를 반환한다는 점에 주의하십시오!

not_(w_obj)

Shortcut for space.newbool(not space.is_true(w_obj)).

finditem(w_obj, w_key)

Equivalent to getitem(w_obj, w_key) but returns an interpreter-level None instead of raising a KeyError if the key is not found.

call_function(w_callable, *args_w, **kw_w)

Collects the arguments in a wrapped tuple and dict and invokes space.call(w_callable, ...).

call_method(w_object, 'method', ...)

Uses space.getattr() to get the method object, and then space.call_function() to invoke it.

unpackiterable(w_iterable[, expected_length=-1])

Iterates over w_x (using space.iter() and space.next()) and collects the resulting wrapped objects in a list. If expected_length is given and the length does not match, raises an exception.

Of course, in cases where iterating directly is better than collecting the elements in a list first, you should use space.iter() and space.next() directly.

unpacktuple(w_tuple[, expected_length=None])

Equivalent to unpackiterable(), but only for tuples.

callable(w_obj)

Implements the built-in callable().

애플리케이션 레벨 객체 생성

wrap(x)

Deprecated! Eventually this method should disappear. Returns a wrapped object that is a reference to the interpreter-level object x. This can be used either on simple immutable objects (integers, strings, etc.) to create a new wrapped object, or on instances of W_Root to obtain an application-level-visible reference to them. For example, most classes of the bytecode interpreter subclass W_Root and can be directly exposed to application-level code in this way - functions, frames, code objects, etc.

newint(i)

Creates a wrapped object holding an integral value. newint creates an object of type W_IntObject.

newlong(l)

Creates a wrapped object holding an integral value. The main difference to newint is the type of the argument (which is rpython.rlib.rbigint.rbigint). On PyPy3 this method will return an int (PyPy2 it returns a long).

newbytes(t)

The given argument is a rpython bytestring. Creates a wrapped object of type bytes (both on PyPy2 and PyPy3).

newtext(t)

The given argument is a rpython bytestring. Creates a wrapped object of type str. On PyPy3 this will return a wrapped unicode object. The object will hold a utf-8-nosg decoded value of t. The “utf-8-nosg” codec used here is slightly different from the “utf-8” implemented in Python 2 or Python 3: it is defined as utf-8 without any special handling of surrogate characters. They are encoded using the same three-bytes sequence that encodes any char in the range from '\u0800' to '\uffff'.

PyPy2 will return a bytestring object. No encoding/decoding steps will be applied.

newbool(b)

Creates a wrapped bool object from an interpreter-level object.

newtuple([w_x, w_y, w_z, ...])

Creates a new wrapped tuple out of an interpreter-level list of wrapped objects.

newlist([..])

Creates a wrapped list from an interpreter-level list of wrapped objects.

newdict()

Returns a new empty dictionary.

newslice(w_start, w_end, w_step)

Creates a new slice object.

newunicode(ustr)

Creates a Unicode string from an rpython unicode string. This method may disappear soon and be replaced by :py:function:`newutf8`.

newutf8(bytestr)

Creates a Unicode string from an rpython byte string, decoded as “utf-8-nosg”. On PyPy3 it is the same as :py:function:`newtext`.

더 많은 공간(space) 연산은 pypy/interpreter/baseobjspace.pypypy/objspace/std/objspace.py에서 찾을 수 있습니다.

애플리케이션 레벨에서 인터프리터 레벨로의 변환

unwrap(w_x)

Returns the interpreter-level equivalent of w_x – use this ONLY for testing, because this method is not RPython and thus cannot be translated! In most circumstances you should use the functions described below instead.

is_true(w_x)

Returns an interpreter-level boolean (True or False) that gives the truth value of the wrapped object w_x.

This is a particularly important operation because it is necessary to implement, for example, if-statements in the language (or rather, to be pedantic, to implement the conditional-branching bytecodes into which if-statements are compiled).

int_w(w_x)

If w_x is an application-level integer or long which can be converted without overflow to an integer, return an interpreter-level integer. Otherwise raise TypeError or OverflowError.

bigint_w(w_x)

If w_x is an application-level integer or long, return an interpreter-level rbigint. Otherwise raise 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 str and returns a rpython byte string, or it takes an unicode and uses the systems default encoding to return a rpython byte string.

On PyPy3 it takes a str and it will return an utf-8 encoded rpython string.

str_w(w_x)

Deprecated. use text_w or bytes_w instead If w_x is an application-level string, return an interpreter-level string. Otherwise raise TypeError.

unicode_w(w_x)

Takes an application level unicode and return an interpreter-level unicode string. This method may disappear soon and be replaced by :py:function:`text_w`.

float_w(w_x)

If w_x is an application-level float, integer or long, return an interpreter-level float. Otherwise raise TypeError (or:py:exc:OverflowError in the case of very large longs).

getindex_w(w_obj[, w_exception=None])

Call index(w_obj). If the resulting integer or long object can be converted to an interpreter-level int, return that. If not, return a clamped result if w_exception is None, otherwise raise the exception at the application level.

(If w_obj can’t be converted to an index, index() will raise an application-level TypeError.)

interp_w(RequiredClass, w_x[, can_be_None=False])

If w_x is a wrapped instance of the given bytecode interpreter class, unwrap it and return it. If can_be_None is True, a wrapped None is also accepted and returns an interpreter-level None. Otherwise, raises an OperationError encapsulating a TypeError with a nice error message.

interpclass_w(w_x)

If w_x is a wrapped instance of an bytecode interpreter class – for example Function, Frame, Cell, etc. – return it unwrapped. Otherwise return None.

데이터 멤버

space.builtin

The Module containing the builtins.

space.sys

The sys Module.

space.w_None

The ObjSpace’s instance of None.

space.w_True

The ObjSpace’s instance of True.

space.w_False

The ObjSpace’s instance of False.

space.w_Ellipsis

The ObjSpace’s instance of Ellipsis.

space.w_NotImplemented

The ObjSpace’s instance of 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’s most common basic type objects.

space.w_[XYZ]Error

Python’s built-in exception classes (KeyError, IndexError, etc).

ObjSpace.MethodTable

List of tuples containing (method_name, symbol, number_of_arguments, list_of_special_names) for the regular part of the interface.

NOTE that tuples are interpreter-level.

ObjSpace.BuiltinModuleTable

List of names of built-in modules.

ObjSpace.ConstantTable

List of names of the constants that the object space should define.

ObjSpace.ExceptionTable

List of names of exception classes.

ObjSpace.IrregularOpTable

List of names of methods that have an irregular API (take and/or return non-wrapped objects).

표준 객체 공간(object space)

소개

표준 객체 공간(object space)(pypy/objspace/std/)은 CPython의 객체 라이브러리(배포판의 Objects/ 하위 디렉터리)에 직접 대응합니다. 이는 공통 파이썬 타입들을 더 낮은 수준의 언어로 구현한 것입니다.

표준 객체 공간(object space)은 추상 부모 클래스인 W_Object뿐만 아니라 W_IntObject, W_ListObject 등과 같은 하위 클래스도 정의합니다. 래핑된 객체(바이트코드 인터프리터의 메인 루프에게는 “블랙박스”)는 이 클래스들 중 하나의 인스턴스입니다. 메인 루프가 두 래핑된 객체 w1w2 사이에서 (덧셈과 같은) 연산을 호출하면, 표준 객체 공간(object space)은 (CPython의Object/abstract.c와 유사한) 내부 디스패치를 수행하고, 해당 연산을 수행할 수 있는 적절한 W_XYZObject 클래스의 메서드를 호출합니다.

연산 자체는 RPython이 허용하는 기본 연산(primitive)으로 수행되며, 결과는 래핑된(wrapped) 객체로 구성됩니다. 예를 들어, 다음의 정수 덧셈 구현을 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/ 패키지 코드는 파이썬의 표준 내장 객체 타입 라이브러리를 정의하고 구현합니다. 각 타입 xxx (int, float, list, tuple, str, type 등)은 일반적으로 xxxobject.py 모듈에서 구현됩니다.

W_AbstractXxxObject클래스는 존재하는 경우 추상 베이스 클래스이며, 주로 Python 레벨 타입 객체에 나타나는 것을 정의합니다. 그런 다음 서브클래스로서 실제 구현체들이 있는데, 여러 다른 구현체가 있는 경우에는 W_XxxObject또는 그 변형으로 불립니다. 예를 들어, pypy/objspace/std/bytesobject.pystr앱 레벨 타입을 만드는 데 필요한 모든 것을 담고 있는 W_AbstractBytesObject를 정의합니다. 그리고 서브클래스로 W_BytesObject (일반적인 문자열)와 W_Buffer (반복적인 추가에 맞춰 조정된 특수 구현으로, pypy/objspace/std/bufferobject.py에 있음)가 있습니다. 리스트나 딕셔너리와 같은 가변 데이터 타입의 경우, 실제 데이터에 대한 간접 참조와 전략(strategy)을 가진 단일 클래스 W_ListObject또는 W_DictMultiObject가 있습니다. 이 전략은 객체의 내용이 변경됨에 따라 바뀔 수 있습니다.

사용자 관점에서 볼 때, W_AbstractXxxObject 하위 클래스가 여러 개 있더라도 이는 보이지 않습니다: 앱 레벨에서는 여전히 모두 정확히 동일한 Python 타입의 인스턴스입니다. PyPy는 (예를 들어) 인터프리터 레벨의 W_BytesObject 인스턴스의 애플리케이션 레벨 타입이 str이라는 것을 알고 있는데, 이는 W_BytesObjectpypy/objspace/std/bytesobject.py의 문자열 타입 명세를 가리키는 typedef 클래스 속성이 있기 때문입니다; 문자열의 다른 모든 구현체도 pypy/objspace/std/bytesobject.py의 동일한 typedef를 사용합니다.

동일한 Python 타입에 대한 여러 구현의 다른 예시는 표준 인터프리터 최적화를 참고하십시오.

객체 공간(object space) 프록시

우리는 다른 객체 공간(일반적으로 표준 객체 공간)을 감싸 모든 객체에 몇 가지 기능을 추가하는 여러 프록시 객체 공간(object space)들을 구현했습니다. 더 알아보려면 투명 프록시 (사용 중단됨)를 참고하십시오.