mirror of
https://github.com/pocketpy/pocketpy
synced 2025-10-21 03:50:16 +00:00
...
This commit is contained in:
parent
e72d6d0d4d
commit
196616a917
@ -7,7 +7,7 @@ order: 80
|
||||
### Direct access
|
||||
|
||||
Some python objects have an instance dict, a.k.a, `__dict__` in cpython.
|
||||
You can use `PyObject::attr()` to manipulate the instance dict of an object.
|
||||
You can use `obj->attr()` to manipulate the instance dict of an object.
|
||||
|
||||
```cpp
|
||||
// get the `builtin` module
|
||||
@ -39,7 +39,8 @@ bool ok = !is_tagged(obj) && obj->is_attr_valid(); // false
|
||||
|
||||
### General access
|
||||
|
||||
As you can see, direct access does not take care of derived attributes or methods. In most cases, what you need is `getattr` and `setattr`.
|
||||
As you can see, direct access does not take care of derived attributes or methods.
|
||||
In most cases, what you need is `getattr` and `setattr`.
|
||||
These two methods handle all possible cases.
|
||||
|
||||
#### `PyObject* VM::getattr(PyObject* obj, StrName name, bool throw_err=true)`
|
||||
|
@ -5,39 +5,104 @@ order: 90
|
||||
---
|
||||
|
||||
In pkpy, any python object is represented by a `PyObject*`.
|
||||
There are 3 macros for you to do convert.
|
||||
|
||||
+ `VAR(...)`,
|
||||
create a `PyObject*` from a C type
|
||||
+ `CAST(T, ...)`,
|
||||
cast a `PyObject*` to a C type
|
||||
+ `_CAST(T, ...)`,
|
||||
cast a `PyObject*` to a C type, without type check
|
||||
|
||||
### Create `PyObject*` from C type
|
||||
|
||||
A set of overloaded function `PyObject* py_var(VM* vm, ...)` were implemented to
|
||||
create a `PyObject*` from a supported C type.
|
||||
In order to make it less verbose, we usually use macro `VAR(...)`, which is just a wrapper of `py_var`.
|
||||
|
||||
For example, create a python `int` object from a C `i64` type:
|
||||
|
||||
```cpp
|
||||
PyObject* x = VAR(12); // cast a C int to PyObject*
|
||||
int y = CAST(int, x); // cast a PyObject* to C int
|
||||
|
||||
PyObject* i = VAR("abc");
|
||||
std::cout << CAST(Str, i); // abc
|
||||
i64 i = 2;
|
||||
PyObject* obj = VAR(i);
|
||||
```
|
||||
|
||||
### Types
|
||||
Each python type has a corresponding C type, for example, `int` in python is `i64` in C.
|
||||
python's `list` corresponds to `List`, `str` corresponds to `Str`, etc.
|
||||
For strings, we have defined
|
||||
a set of overloaded version including `const char*`, `std::string`, `std::string_view`, `Str`, etc.
|
||||
|
||||
| python type | C type | note |
|
||||
| ------------ | ---------------- | ---------------------- |
|
||||
| `int` | `i64` | 62 bits integer |
|
||||
| `float` | `f64` | 62 bits floating point |
|
||||
| `str` | `pkpy::Str` | |
|
||||
| `bool` | `bool` | |
|
||||
| `list` | `pkpy::List` | |
|
||||
| `tuple` | `pkpy::Tuple` | |
|
||||
| `function` | `pkpy::Function` | |
|
||||
| ... | ... | ... |
|
||||
```cpp
|
||||
PyObject* obj = VAR("abc"); // create a python str object
|
||||
```
|
||||
|
||||
### Type check
|
||||
A more complex example is to create a python `list`.
|
||||
In the following code, we create a `list` equals to `[0, 1, 2, 3]`.
|
||||
|
||||
```cpp
|
||||
List list;
|
||||
for (i64 i = 0; i < 4; i++) {
|
||||
list.push_back(VAR(i));
|
||||
}
|
||||
|
||||
obj = VAR(std::move(list)); // create a python list object
|
||||
```
|
||||
|
||||
Please note that `std::move` is used here to avoid unnecessary copy.
|
||||
Most types have both a rvalue and a lvalue version of `VAR` function.
|
||||
|
||||
### Access internal C type of `PyObject*`
|
||||
|
||||
A set of template function `T py_cast<T>(VM* vm, PyObject* obj)` were implemented
|
||||
for each supported C type. We usually use macro `CAST(T, ...)` to make it less verbose.
|
||||
|
||||
```cpp
|
||||
i64 i = 2;
|
||||
PyObject* obj = VAR(i);
|
||||
|
||||
// cast a PyObject* to C i64
|
||||
i64 j = CAST(i64, obj);
|
||||
```
|
||||
|
||||
The `CAST` function will check the type of `obj` before casting.
|
||||
If the type is not matched, a `TypeError` will be thrown.
|
||||
|
||||
However, this type check has a cost. If you are sure about the type of `obj`,
|
||||
you can use the underscore version `_CAST` to skip the type check.
|
||||
|
||||
```cpp
|
||||
// cast a PyObject* to C i64 (unsafe but faster)
|
||||
i64 j = _CAST(i64, obj);
|
||||
```
|
||||
|
||||
For complex objects like `list`, we can use reference cast to avoid unnecessary copy.
|
||||
|
||||
```cpp
|
||||
PyObject* obj = VAR(List());
|
||||
// reference cast (no copy)
|
||||
List& list = CAST(List&, obj);
|
||||
```
|
||||
|
||||
### Check type of `PyObject*`
|
||||
|
||||
Each `PyObject*` has a `Type` field to indicate its type.
|
||||
`Type` is just an integer which is the global index in `VM::_all_types`.
|
||||
|
||||
`VM` class has a set of predefined `Type` constants for quick access.
|
||||
They are prefixed by `tp_`. For example, `tp_object`(object),
|
||||
`tp_int`(int), `tp_str`(str), `tp_list`(list), etc.
|
||||
|
||||
Types are divided into **tagged type** and **non-tagged type**.
|
||||
+ `int` and `float` are tagged type.
|
||||
+ Other types are non-tagged type.
|
||||
|
||||
To determine whether a `PyObject*` is of a specific type,
|
||||
you can use the following functions:
|
||||
|
||||
+ `bool is_type(PyObject* obj, Type type)`
|
||||
+ `bool is_int(PyObject* obj)`
|
||||
+ `bool is_float(PyObject* obj)`
|
||||
+ `bool is_tagged(PyObject* obj)`
|
||||
+ `bool is_non_tagged_type(PyObject* obj, Type type)`
|
||||
+ `void VM::check_type(PyObject* obj, Type type)` throws `TypeError` on failure
|
||||
+ `void VM::check_non_tagged_type(PyObject* obj, Type type)` throws `TypeError` on failure
|
||||
|
||||
Simply put, `is_type` is the most general function and can check any types.
|
||||
Other variants are designed for specific types and are faster.
|
||||
|
||||
You can also use `check_` prefix functions assert the type of a `PyObject*`,
|
||||
which will throw `TypeError` on failure.
|
||||
|
||||
+ `void VM::check_type(PyObject* obj, Type type)`
|
||||
+ `void VM::check_non_tagged_type(PyObject* obj, Type type)`
|
Loading…
x
Reference in New Issue
Block a user