init
This commit is contained in:
commit
38355d2442
9083 changed files with 1225834 additions and 0 deletions
|
|
@ -0,0 +1,281 @@
|
|||
.. _differences-from-python:
|
||||
|
||||
Differences from Python
|
||||
=======================
|
||||
|
||||
Mypyc aims to be sufficiently compatible with Python semantics so that
|
||||
migrating code to mypyc often doesn't require major code
|
||||
changes. There are various differences to enable performance gains
|
||||
that you need to be aware of, however.
|
||||
|
||||
This section documents notable differences from Python. We discuss
|
||||
many of them also elsewhere, but it's convenient to have them here in
|
||||
one place.
|
||||
|
||||
Running compiled modules
|
||||
------------------------
|
||||
|
||||
You can't use ``python3 <module>.py`` or ``python3 -m <module>``
|
||||
to run compiled modules. Use ``python3 -c "import <module>"`` instead,
|
||||
or write a wrapper script that imports your module.
|
||||
|
||||
As a side effect, you can't rely on checking the ``__name__`` attribute in compiled
|
||||
code, like this::
|
||||
|
||||
if __name__ == "__main__": # Can't be used in compiled code
|
||||
main()
|
||||
|
||||
Type errors prevent compilation
|
||||
-------------------------------
|
||||
|
||||
You can't compile code that generates mypy type check errors. You can
|
||||
sometimes ignore these with a ``# type: ignore`` comment, but this can
|
||||
result in bad code being generated, and it's considered dangerous.
|
||||
|
||||
.. note::
|
||||
|
||||
In the future, mypyc may reject ``# type: ignore`` comments that
|
||||
may be unsafe.
|
||||
|
||||
Runtime type checking
|
||||
---------------------
|
||||
|
||||
Non-erased types in annotations will be type checked at runtime. For example,
|
||||
consider this function::
|
||||
|
||||
def twice(x: int) -> int:
|
||||
return x * 2
|
||||
|
||||
If you try to call this function with a ``float`` or ``str`` argument,
|
||||
you'll get a type error on the call site, even if the call site is not
|
||||
being type checked::
|
||||
|
||||
twice(5) # OK
|
||||
twice(2.2) # TypeError
|
||||
twice("blah") # TypeError
|
||||
|
||||
Also, values with *inferred* types will be type checked. For example,
|
||||
consider a call to the stdlib function ``socket.gethostname()`` in
|
||||
compiled code. This function is not compiled (no stdlib modules are
|
||||
compiled with mypyc), but mypyc uses a *library stub file* to infer
|
||||
the return type as ``str``. Compiled code calling ``gethostname()``
|
||||
will fail with ``TypeError`` if ``gethostname()`` would return an
|
||||
incompatible value, such as ``None``::
|
||||
|
||||
import socket
|
||||
|
||||
# Fail if returned value is not a str
|
||||
name = socket.gethostname()
|
||||
|
||||
Note that ``gethostname()`` is defined like this in the stub file for
|
||||
``socket`` (in typeshed)::
|
||||
|
||||
def gethostname() -> str: ...
|
||||
|
||||
Thus mypyc verifies that library stub files and annotations in
|
||||
non-compiled code match runtime values. This adds an extra layer of
|
||||
type safety.
|
||||
|
||||
Casts such as ``cast(str, x)`` will also result in strict type
|
||||
checks. Consider this example::
|
||||
|
||||
from typing import cast
|
||||
...
|
||||
x = cast(str, y)
|
||||
|
||||
The last line is essentially equivalent to this Python code when compiled::
|
||||
|
||||
if not isinstance(y, str):
|
||||
raise TypeError(...)
|
||||
x = y
|
||||
|
||||
In interpreted mode ``cast`` does not perform a runtime type check.
|
||||
|
||||
Native classes
|
||||
--------------
|
||||
|
||||
Native classes behave differently from Python classes. See
|
||||
:ref:`native-classes` for the details.
|
||||
|
||||
Primitive types
|
||||
---------------
|
||||
|
||||
Some primitive types behave differently in compiled code to improve
|
||||
performance.
|
||||
|
||||
``int`` objects use an unboxed (non-heap-allocated) representation for small
|
||||
integer values. A side effect of this is that the exact runtime type of
|
||||
``int`` values is lost. For example, consider this simple function::
|
||||
|
||||
def first_int(x: List[int]) -> int:
|
||||
return x[0]
|
||||
|
||||
print(first_int([True])) # Output is 1, instead of True!
|
||||
|
||||
``bool`` is a subclass of ``int``, so the above code is
|
||||
valid. However, when the list value is converted to ``int``, ``True``
|
||||
is converted to the corresponding ``int`` value, which is ``1``.
|
||||
|
||||
Note that integers still have an arbitrary precision in compiled code,
|
||||
similar to normal Python integers.
|
||||
|
||||
Fixed-length tuples are unboxed, similar to integers. The exact type
|
||||
and identity of fixed-length tuples is not preserved, and you can't
|
||||
reliably use ``is`` checks to compare tuples that are used in compiled
|
||||
code.
|
||||
|
||||
.. _early-binding:
|
||||
|
||||
Early binding
|
||||
-------------
|
||||
|
||||
References to functions, types, most attributes, and methods in the
|
||||
same :ref:`compilation unit <compilation-units>` use *early binding*:
|
||||
the target of the reference is decided at compile time, whenever
|
||||
possible. This contrasts with normal Python behavior of *late
|
||||
binding*, where the target is found by a namespace lookup at
|
||||
runtime. Omitting these namespace lookups improves performance, but
|
||||
some Python idioms don't work without changes.
|
||||
|
||||
Note that non-final module-level variables still use late binding.
|
||||
You may want to avoid these in very performance-critical code.
|
||||
|
||||
Examples of early and late binding::
|
||||
|
||||
from typing import Final
|
||||
|
||||
import lib # "lib" is not compiled
|
||||
|
||||
x = 0
|
||||
y: Final = 1
|
||||
|
||||
def func() -> None:
|
||||
pass
|
||||
|
||||
class Cls:
|
||||
def __init__(self, attr: int) -> None:
|
||||
self.attr = attr
|
||||
|
||||
def method(self) -> None:
|
||||
pass
|
||||
|
||||
def example() -> None:
|
||||
# Early binding:
|
||||
var = y
|
||||
func()
|
||||
o = Cls()
|
||||
o.x
|
||||
o.method()
|
||||
|
||||
# Late binding:
|
||||
var = x # Module-level variable
|
||||
lib.func() # Accessing library that is not compiled
|
||||
|
||||
Monkey patching
|
||||
---------------
|
||||
|
||||
Since mypyc function and class definitions are immutable, you can't
|
||||
perform arbitrary monkey patching, such as replacing functions or
|
||||
methods with mocks in tests.
|
||||
|
||||
.. note::
|
||||
|
||||
Each compiled module has a Python namespace that is initialized to
|
||||
point to compiled functions and type objects. This namespace is a
|
||||
regular ``dict`` object, and it *can* be modified. However,
|
||||
compiled code generally doesn't use this namespace, so any changes
|
||||
will only be visible to non-compiled code.
|
||||
|
||||
Stack overflows
|
||||
---------------
|
||||
|
||||
Compiled code currently doesn't check for stack overflows. Your
|
||||
program may crash in an unrecoverable fashion if you have too many
|
||||
nested function calls, typically due to out-of-control recursion.
|
||||
|
||||
.. note::
|
||||
|
||||
This limitation will be fixed in the future.
|
||||
|
||||
Final values
|
||||
------------
|
||||
|
||||
Compiled code replaces a reference to an attribute declared ``Final`` with
|
||||
the value of the attribute computed at compile time. This is an example of
|
||||
:ref:`early binding <early-binding>`. Example::
|
||||
|
||||
MAX: Final = 100
|
||||
|
||||
def limit_to_max(x: int) -> int:
|
||||
if x > MAX:
|
||||
return MAX
|
||||
return x
|
||||
|
||||
The two references to ``MAX`` don't involve any module namespace lookups,
|
||||
and are equivalent to this code::
|
||||
|
||||
def limit_to_max(x: int) -> int:
|
||||
if x > 100:
|
||||
return 100
|
||||
return x
|
||||
|
||||
When run as interpreted, the first example will execute slower due to
|
||||
the extra namespace lookups. In interpreted code final attributes can
|
||||
also be modified.
|
||||
|
||||
Unsupported features
|
||||
--------------------
|
||||
|
||||
Some Python features are not supported by mypyc (yet). They can't be
|
||||
used in compiled code, or there are some limitations. You can
|
||||
partially work around some of these limitations by running your code
|
||||
in interpreted mode.
|
||||
|
||||
Operator overloading
|
||||
********************
|
||||
|
||||
Native classes can only use these dunder methods to override operators:
|
||||
|
||||
* ``__eq__``
|
||||
* ``__ne__``
|
||||
* ``__getitem__``
|
||||
* ``__setitem__``
|
||||
|
||||
.. note::
|
||||
|
||||
This limitation will be lifted in the future.
|
||||
|
||||
Generator expressions
|
||||
*********************
|
||||
|
||||
Generator expressions are not supported. To make it easier to compile
|
||||
existing code, they are implicitly replaced with list comprehensions.
|
||||
*This does not always produce the same behavior.*
|
||||
|
||||
To work around this limitation, you can usually use a generator
|
||||
function instead. You can sometimes replace the generator expression
|
||||
with an explicit list comprehension.
|
||||
|
||||
Descriptors
|
||||
***********
|
||||
|
||||
Native classes can't contain arbitrary descriptors. Properties, static
|
||||
methods and class methods are supported.
|
||||
|
||||
Stack introspection
|
||||
*******************
|
||||
|
||||
Frames of compiled functions can't be inspected using ``inspect``.
|
||||
|
||||
Profiling hooks and tracing
|
||||
***************************
|
||||
|
||||
Compiled functions don't trigger profiling and tracing hooks, such as
|
||||
when using the ``profile``, ``cProfile``, or ``trace`` modules.
|
||||
|
||||
Debuggers
|
||||
*********
|
||||
|
||||
You can't set breakpoints in compiled functions or step through
|
||||
compiled functions using ``pdb``. Often you can debug your code in
|
||||
interpreted mode instead.
|
||||
Loading…
Add table
Add a link
Reference in a new issue