199 lines
5.1 KiB
ReStructuredText
199 lines
5.1 KiB
ReStructuredText
|
.. _native-classes:
|
||
|
|
||
|
Native classes
|
||
|
==============
|
||
|
|
||
|
Classes in compiled modules are *native classes* by default (some
|
||
|
exceptions are discussed below). Native classes are compiled to C
|
||
|
extension classes, which have some important differences from normal
|
||
|
Python classes. Native classes are similar in many ways to built-in
|
||
|
types, such as ``int``, ``str``, and ``list``.
|
||
|
|
||
|
Immutable namespaces
|
||
|
--------------------
|
||
|
|
||
|
The type object namespace of native classes is mostly immutable (but
|
||
|
class variables can be assigned to)::
|
||
|
|
||
|
class Cls:
|
||
|
def method1(self) -> None:
|
||
|
print("method1")
|
||
|
|
||
|
def method2(self) -> None:
|
||
|
print("method2")
|
||
|
|
||
|
Cls.method1 = Cls.method2 # Error
|
||
|
Cls.new_method = Cls.method2 # Error
|
||
|
|
||
|
Only attributes defined within a class definition (or in a base class)
|
||
|
can be assigned to (similar to using ``__slots__``)::
|
||
|
|
||
|
class Cls:
|
||
|
x: int
|
||
|
|
||
|
def __init__(self, y: int) -> None:
|
||
|
self.x = 0
|
||
|
self.y = y
|
||
|
|
||
|
def method(self) -> None:
|
||
|
self.z = "x"
|
||
|
|
||
|
o = Cls(0)
|
||
|
print(o.x, o.y) # OK
|
||
|
o.z = "y" # OK
|
||
|
o.extra = 3 # Error: no attribute "extra"
|
||
|
|
||
|
.. _inheritance:
|
||
|
|
||
|
Inheritance
|
||
|
-----------
|
||
|
|
||
|
Only single inheritance is supported (except for :ref:`traits
|
||
|
<trait-types>`). Most non-native classes can't be used as base
|
||
|
classes.
|
||
|
|
||
|
These non-native classes can be used as base classes of native
|
||
|
classes:
|
||
|
|
||
|
* ``object``
|
||
|
* ``dict`` (and ``Dict[k, v]``)
|
||
|
* ``BaseException``
|
||
|
* ``Exception``
|
||
|
* ``ValueError``
|
||
|
* ``IndexError``
|
||
|
* ``LookupError``
|
||
|
* ``UserWarning``
|
||
|
|
||
|
By default, a non-native class can't inherit a native class, and you
|
||
|
can't inherit from a native class outside the compilation unit that
|
||
|
defines the class. You can enable these through
|
||
|
``mypy_extensions.mypyc_attr``::
|
||
|
|
||
|
from mypy_extensions import mypyc_attr
|
||
|
|
||
|
@mypyc_attr(allow_interpreted_subclasses=True)
|
||
|
class Cls:
|
||
|
...
|
||
|
|
||
|
Allowing interpreted subclasses has only minor impact on performance
|
||
|
of instances of the native class. Accessing methods and attributes of
|
||
|
a *non-native* subclass (or a subclass defined in another compilation
|
||
|
unit) will be slower, since it needs to use the normal Python
|
||
|
attribute access mechanism.
|
||
|
|
||
|
You need to install ``mypy-extensions`` to use ``@mypyc_attr``:
|
||
|
|
||
|
.. code-block:: text
|
||
|
|
||
|
pip install --upgrade mypy-extensions
|
||
|
|
||
|
Class variables
|
||
|
---------------
|
||
|
|
||
|
Class variables must be explicitly declared using ``attr: ClassVar``
|
||
|
or ``attr: ClassVar[<type>]``. You can't assign to a class variable
|
||
|
through an instance. Example::
|
||
|
|
||
|
from typing import ClassVar
|
||
|
|
||
|
class Cls:
|
||
|
cv: ClassVar = 0
|
||
|
|
||
|
Cls.cv = 2 # OK
|
||
|
o = Cls()
|
||
|
print(o.cv) # OK (2)
|
||
|
o.cv = 3 # Error!
|
||
|
|
||
|
Generic native classes
|
||
|
----------------------
|
||
|
|
||
|
Native classes can be generic. Type variables are *erased* at runtime,
|
||
|
and instances don't keep track of type variable values.
|
||
|
|
||
|
Compiled code thus can't check the values of type variables when
|
||
|
performing runtime type checks. These checks are delayed to when
|
||
|
reading a value with a type variable type::
|
||
|
|
||
|
from typing import TypeVar, Generic, cast
|
||
|
|
||
|
T = TypeVar('T')
|
||
|
|
||
|
class Box(Generic[T]):
|
||
|
def __init__(self, item: T) -> None:
|
||
|
self.item = item
|
||
|
|
||
|
x = Box(1) # Box[int]
|
||
|
y = cast(Box[str], x) # OK (type variable value not checked)
|
||
|
y.item # Runtime error: item is "int", but "str" expected
|
||
|
|
||
|
Metaclasses
|
||
|
-----------
|
||
|
|
||
|
Most metaclasses aren't supported with native classes, since their
|
||
|
behavior is too dynamic. You can use these metaclasses, however:
|
||
|
|
||
|
* ``abc.ABCMeta``
|
||
|
* ``typing.GenericMeta`` (used by ``typing.Generic``)
|
||
|
|
||
|
.. note::
|
||
|
|
||
|
If a class definition uses an unsupported metaclass, *mypyc
|
||
|
compiles the class into a regular Python class*.
|
||
|
|
||
|
Class decorators
|
||
|
----------------
|
||
|
|
||
|
Similar to metaclasses, most class decorators aren't supported with
|
||
|
native classes, as they are usually too dynamic. These class
|
||
|
decorators can be used with native classes, however:
|
||
|
|
||
|
* ``mypy_extensions.trait`` (for defining :ref:`trait types <trait-types>`)
|
||
|
* ``mypy_extensions.mypyc_attr`` (see :ref:`above <inheritance>`)
|
||
|
* ``dataclasses.dataclass``
|
||
|
|
||
|
Dataclasses have partial native support, and they aren't as efficient
|
||
|
as pure native classes.
|
||
|
|
||
|
.. note::
|
||
|
|
||
|
If a class definition uses an unsupported class decorator, *mypyc
|
||
|
compiles the class into a regular Python class*.
|
||
|
|
||
|
Deleting attributes
|
||
|
-------------------
|
||
|
|
||
|
By default, attributes defined in native classes can't be deleted. You
|
||
|
can explicitly allow certain attributes to be deleted by using
|
||
|
``__deletable__``::
|
||
|
|
||
|
class Cls:
|
||
|
x: int = 0
|
||
|
y: int = 0
|
||
|
other: int = 0
|
||
|
|
||
|
__deletable__ = ['x', 'y'] # 'x' and 'y' can be deleted
|
||
|
|
||
|
o = Cls()
|
||
|
del o.x # OK
|
||
|
del o.y # OK
|
||
|
del o.other # Error
|
||
|
|
||
|
You must initialize the ``__deletable__`` attribute in the class body,
|
||
|
using a list or a tuple expression with only string literal items that
|
||
|
refer to attributes. These are not valid::
|
||
|
|
||
|
a = ['x', 'y']
|
||
|
|
||
|
class Cls:
|
||
|
x: int
|
||
|
y: int
|
||
|
|
||
|
__deletable__ = a # Error: cannot use variable 'a'
|
||
|
|
||
|
__deletable__ = ('a',) # Error: not in a class body
|
||
|
|
||
|
Other properties
|
||
|
----------------
|
||
|
|
||
|
Instances of native classes don't usually have a ``__dict__`` attribute.
|