Python Classes
This page describes how to define, instantiate and customize Python classes with Pydust.
Classes are defined by wrapping structs with the py.class
function.
Struct fields are used to store per-instance state, and public struct functions are exported as Python functions. See the section on functions for more details.
Instantiation
By default, Pydust classes can only be instantiated from Zig. While it is possible to create Pydust structs with Zig syntax, this will only create a Zig struct and not the corresponding Python object around it.
For example, the class above can be correctly constructed using the py.init
function:
Alternatively, a class can be allocated without instantiation. This can be useful when some fields of the class refer by pointer to other fields.
From Python
Declaring a __init__
function signifies to Pydust to make your class instantiable
from Python. This function may take zero arguments as a pure marker to allow instantiation.
Inheritance
Inheritance allows you to define a subclass of another Zig Pydust class.
Note
It is currently not possible to create a subclass of a Python class.
Subclasses are defined by including the parent class struct as a field of the subclass struct.
They can then be instantiated from Zig using py.init
, or from Python
if a __init__
function is defined.
Super
The py.super(Type, self)
function returns a proxy py.PyObject
that can be used to invoke methods on the super class. This behaves the same as the Python builtin super.
Properties
Properties behave the same as the Python @property
decorator. They allow you to define
getter and setter functions for an attribute of the class.
Pydust properties are again defined as structs with optional get
and set
methods. If you
do not define a set
method for example, then the property is read-only. And vice versa.
In this example we define an email
property that performs a naive validity check. It makes
use of Zig's @fieldParentPointer
builtin to get a handle on the class instance struct.
In the second example, the greeting
property takes *const Self
as a first parameter providing it direct
access to the outer struct. This is a convenience when implementing typically getter-only properties.
Instance Attributes
Attributes are similar to properties, except they do not allow for custom getters and setters. Due to how they are implemented, attributes wrap the type in a struct definition:
This means you must access the attribute in Zig using .value
.
Note
Attributes are currently read-only. Please file an issue if you have a use-case for writable attributes.
Class Attributes
Class attributes are not currently supported by Pydust.
Static Methods
Static methods are similar to class methods but do not have access to the class itself. You can define static methods by simply not taking a self
argument.
Zig Only Methods
Classes can define methods that are not exposed to python via py.zig
wrapper
Dunder Methods
Dunder methods, or "double underscore" methods, provide a mechanism for overriding builtin Python operators.
object
refers to either a pointer to a Pydust type, apy.PyObject
, or any other Pydust Python type, e.g.py.PyString
.CallArgs
refers to a Zig struct that is interpreted asargs
andkwargs
where fields are marked as keyword arguments if they have a default value.
Also note the shorthand signatures:
Type Methods
Method | Signature |
---|---|
__init__ |
fn() void |
__init__ |
fn(*Self) !void |
__init__ |
fn(*Self, CallArgs) !void |
__del__ |
fn(*Self) void |
__repr__ |
fn(*Self) !py.PyString |
__str__ |
fn(*Self) !py.PyString |
__call__ |
fn(*Self, CallArgs) !py.PyObject |
__iter__ |
fn(*Self) !object |
__next__ |
fn(*Self) !?object |
__getattr__ |
fn(*Self, object) !?object |
Sequence Methods
Method | Signature |
---|---|
__len__ |
fn(*Self) !usize |
The remaining sequence methods are yet to be implemented.
Mapping Methods
Method | Signature |
---|---|
__getitem__ |
binaryfunc |
The remaining mapping methods are yet to be implemented.
Rich Compare
Method | Signature |
---|---|
__lt__ |
fn(*Self, object) !bool |
__le__ |
fn(*Self, object) !bool |
__eq__ |
fn(*Self, object) !bool |
__ne__ |
fn(*Self, object) !bool |
__gt__ |
fn(*Self, object) !bool |
__ge__ |
fn(*Self, object) !bool |
Note
By default, __ne__
will delegate to the negation of __eq__
if it is defined.
Pydust also defines py.CompareOp
representing the CPython comparison operators allowing you
to implement the full comparison logic in a single __richcompare__
function.
Method | Signature |
---|---|
__hash__ |
fn(*Self) !usize |
__richcompare__ |
fn(*Self, other: object, CompareOp) !usize |
Tip
Whenever __eq__
is implemented, it is advisable to also implement __hash__
.
Number Methods
Method | Signature |
---|---|
__add__ |
binaryfunc |
__iadd__ |
binaryfunc |
__sub__ |
binaryfunc |
__isub__ |
binaryfunc |
__mul__ |
binaryfunc |
__imul__ |
binaryfunc |
__mod__ |
binaryfunc |
__imod__ |
binaryfunc |
__divmod__ |
binaryfunc |
__pow__ |
binaryfunc |
__ipow__ |
binaryfunc |
__lshift__ |
binaryfunc |
__ilshift__ |
binaryfunc |
__rshift__ |
binaryfunc |
__irshift__ |
binaryfunc |
__and__ |
binaryfunc |
__iand__ |
binaryfunc |
__or__ |
binaryfunc |
__ior__ |
binaryfunc |
__xor__ |
binaryfunc |
__ixor__ |
binaryfunc |
__truediv__ |
binaryfunc |
__itruediv__ |
binaryfunc |
__floordiv__ |
binaryfunc |
__ifloordiv__ |
binaryfunc |
__matmul__ |
binaryfunc |
__imatmul__ |
binaryfunc |
__neg__ |
unaryfunc |
__pos__ |
unaryfunc |
__abs__ |
unaryfunc |
__invert__ |
unaryfunc |
__int__ |
unaryfunc |
__float__ |
unaryfunc |
__index__ |
unaryfunc |
__bool__ |
inquiry |
Note
When implementing in place variants of the functions make sure to incref reference to self as your function is supposed to return a new reference, per CPython documentation
Dynamic dispatch example
All numerical methods example
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 |
|
Buffer Methods
Method | Signature |
---|---|
__buffer__ |
fn (*Self, *py.PyBuffer, flags: c_int) |
__release_buffer__ |
fn (*Self, *py.PyBuffer) |