Language
Limitations
What the compiler refuses, or traps on, rather than answering differently from CPython.
On this page
Each of these is refused at compile time, or traps, rather than answering differently from CPython, apart from the few listed first.
Where the answer differs
These answer differently from CPython without an error. Each is known, and anything else is a bug:
intis 32 bits and wraps instead of growing (see Numbers)- A NaN inside a collection does not equal itself:
(x,) == (x,)is False forx = float("nan"), where CPython answers True (see Numbers) - A temporary lives to the end of its statement: in
print(Res().name)theResis finalized after the line is printed, where CPython finalizes it as soon as.nameis read. What a variable, a field, or a collection holds, a function's own locals, and the arguments of a call to a function, a method, or a constructor are let go of exactly where CPython lets go of them - An object in a reference cycle is finalized when the collector next runs (after enough has been allocated, or as the program ends), which is not the allocation count CPython's collector runs at. Both finalize what is left at exit; a module with no
__main__block has no exit, so nothing is finalized when its host drops it - A number that happens to equal an object's address keeps the object alive while it is held, since the collector reads memory without knowing what each word is (the text of a str or bytes excepted, which it does not read), so that object's
__del__can run later than CPython's - An exception
__del__raises is discarded, without theException ignored inreport CPython writes to standard error - A traceback is not kept: a
__main__block's uncaught exception writes only the last line of CPython's report,logger.exception()andexc_info=log that same line (ValueError: bad) in place of the traceback, and a logging error on standard error leaves its traceback out - A generator expression given to a builtin or a standard-library call (
sum(),sorted(),str.join(),Counter()) is built as a list first, so every element is computed before the call reads any, where CPython computes each as it is read. The answer is the same; what differs is the order of anything the elements print or change. One aforstatement walks (with oneforclause of its own), or that is the argument ofany()orall(), is pulled only as far as CPython pulls it. Anywhere it could be told from a list, a generator expression is refused (see Generators) - A task's exception that nothing retrieves is reported on standard error with the first and last lines of CPython's report (
Task exception was never retrieved,ValueError: bad), since itsfuture:line names the script's path and the traceback is not kept; the report is written as the task is reclaimed, as CPython's is: as soon as it fails when nothing holds it, when the coroutine holding it finishes, or at exit when a module variable holds it. An exception a cancelled task raises atasyncio.run()'s shutdown is reported the same way, asasyncio.run()finishes
Types
- A place with no type holds a single word read back as an int: an unannotated parameter or return value, and the elements of a bare
list,set, ordictparameter or return. Only ints, bools, None, and functions pass through one; a string, float, collection, or instance is refused with a hint to annotate, since it would lose its length, width, or type on the way. A bare annotation on a variable or field is fine: its elements take the type of the value assigned. An empty list, set, or dict with no annotation (seen = []) takes the types of the first value stored in it (anappend(),add(), ord[k] = v), and a value whose type the compiler cannot tell there is refused with a hint to annotate the collection.len()of an untyped value is refused for the same reason - None is the word 0, which an untyped value can also be, so
x is Noneon an untyped value is refused. AnOptional[int],Optional[bool], orOptional[float]is boxed, so None is told apart from 0:is None,==, truthiness,str(), anddict.get(key)on number values give CPython's answer, and arithmetic or an ordering on None raises itsTypeError. An optional number passed or returned where a plain one is declared (past anis Nonetest, typically) is its number, and None there traps, since CPython would pass the None on and the place cannot hold it. Arithmetic between two optional numbers is refused dict.get(key)without a default is refused unless the values are numbers or instances, since a missing key answers None, which a string, bytes, or collection value has no distinct word for; pass a default- Reading an attribute through a value whose type is not known, most often an unannotated parameter, is a compile error with a hint to annotate it. The rich comparison methods are the exception: an unannotated
otherin__eq__/__lt__/... is typed as the class. A field the class does not have is refused too - A variable, field, or collection that holds instances of more than one class is typed as their nearest common base for the whole function, so a method call on it dispatches on each instance's own class. Types are not tracked per program point, so reading a field that only one of the subclasses has through it is refused, and so is a method call on instances with no common base. Storing an instance where an annotation names an unrelated class (a
Rectpassed to aCircleparameter, or returned from-> Circle) is refused rather than dispatched as the annotated class - Annotations are taken as the type a place holds: an int, bool, float, str, or bytes stored where a different one of these is declared (an int passed to a
strorOptional[str]parameter, a str assigned to a field typedint, or a local bound to an int and later to a str) is refused, since the place would read it as the declared kind. CPython keeps the value whatever the annotation says. That includes an int or a bool where a float is declared, and a bool where an int is: the place would hold it as the declared kind and print5.0or1where CPython prints5orTrue, sox: float = 0is refused with a hint to write0.0, andf(n)for an intnwith one to writefloat(n). Where only the number is used (compared with a float, computed with, or passed to amathfunction or adatetimeclass), an int converts with CPython's answer a or banda and bwhose answer is used as a value (stored, returned, printed) answer one of the operands, so the two need a type in common, as a conditional expression's arms do:n or "none"(an int or a str) andn > 1 and n(a bool or an int) are refused, where CPython keeps whichever it answers. AnOptionalleft oforcounts as what it holds, sinceoranswers it only when it is not None, andc and x or yasxagainsty. In a condition,not, orbool(), only the operands' truth is used, so any operands work- A lambda's parameters, and a nested function's unannotated ones, are typed from the calls in their scope when those agree, a
sorted()key's from the elements it is given, and a lambda passed where a parameter is declaredCallable[[...], R]takes those parameter types and returns anR(a body returning something else is refused). A parameter nothing types (a lambda only passed on to an unannotated parameter, or called with an int in one place and a str in another) holds a word like any untyped place, so indexing it, measuring it, or calling a method on it is refused; annotate a nameddefinstead - A
@singledispatchcall needs a first argument whose type the compiler knows (a literal, an annotated parameter, an instance); dispatching on an untyped value is refused rather than sent to the base implementation
Collections
- A collection holds one element type, so a literal mixing types no one type holds (
[1, "a"],{"n": "bob", "age": 30},[1, True]) is refused; use a class or a tuple for a record. A tuple is typed per position, so(1, 2.5)and(1, "a")are fine, but walking one whose members differ (a loop,sorted,sum) is refused - A collection reads every slot at one width, so a value written into one has to be its element type. Otherwise the write is refused rather than stored wrong: an int into a
List[float](CPython keeps the int), a float into a collection of ints (it would truncate), a float into a collection with no element type, a value whose type is unknown into a collection of floats, andlist.extend()between lists of different widths.list.extend()takes a list, or a tuple whose members share a type; any other iterable (a range, a set, a string) is refused - Comparing two collections needs both to have the same, known element types:
(1, 2) == (1.0, 2)(True in CPython) and a comparison with an untyped list are refused rather than read at the wrong width. Comparing two empty literals ({} == {},[] == []), whose element types nothing gives, comparing collections whose elements are sets, and a list, dict, or set as a set member or dict key (unhashable in CPython) are refused; two sets compare directly (==, and<=/<as subset tests). A class that defines__eq__cannot be a set member or dict key, since calling a user__hash__is not supported; a named tuple can, as it hashes as its tuple - A method called with the wrong number of arguments is a compile error:
xs.append(3, 4)andxs.clear(9)are refused. The optional forms Python accepts compile, sopop(),pop(i),sort(), andsort(reverse=True)all work for k, v in x.items()(and.keys()/.values()) calls the method whenxis known to be an instance of a class defining it (a local, a parameter, a field, or a call's declared result). Where the instance comes from something whose class is not known at the loop, such as another method's result, the loop is refused with a hint to assign it to an annotated local first- A loop over a list, dict, or set sees what is done to it while it runs, as CPython's does: a list is walked to wherever its end is by then, and a dict or set that changes size raises
RuntimeError(dictionary changed size during iteration,Set changed size during iteration;OrderedDict mutated during iterationwhile anOrderedDicthas a key left, anddeque mutated during iterationfor adeque). Three cases differ. A dict whose keys change without its size changing (a key renamed withd[k.upper()] = d.pop(k)) raisesRuntimeError: dictionary keys changed during iterationat once, where CPython's answer depends on how its table is laid out: it may walk on and raise later, or finish. AnOrderedDictordequechanged without its size changing (a key moved withmove_to_end(), adequerotated) is walked on, where CPython raises. And a dict or set changed by a function the loop calls, rather than by the loop's own code, traps where CPython raises, unless the function the loop is in can raise for some other reason - A comprehension over a dict literal with string keys written in place (
[k for k in {"a": 1}]) is refused; assign the dict to a variable first - A
bytearrayholds its bytes as a list of ints. It has the methods listed on the Python subset, and not bytes' text methods (decode,hex,split,upper, and the rest, which bytes do not have here either);in,find,count,index,startswith, andendswithtake a byte (an int) for the first four and bytes for the last two; slice assignment (b[1:3] = ...),bytearray(s, encoding), andbytearray()of another iterable (a range, a tuple) are refused. It is unhashable, as in CPython, so it cannot be a set member or a dict key - A chained comparison (
0 <= x <= 100) needs its middle operands to be names or constants; one that is a call or an attribute is refused, since it would be read twice. Assign it to a variable first
Functions and classes
- Closures capture the variable, not a snapshot of it: a captured variable reassigned after the closure is made changes what the closure sees, and closures created in a loop share the loop variable. A captured variable keeps one type (a float or a string included), so rebinding it to a value of another type is refused
- A nested
defhas to sit at the top level of the enclosing function's body (not inside anif, loop,try, orwith), outside a generator, with constant defaults, and it cannot be called before itsdefruns. A function value that passes through an untyped place (an unannotated parameter) is called with int arguments for an int result, so one taking or returning a float, string, collection, or instance is refused there; annotate the parameter withCallable[[...], R] - Every implementation of an overridden method is reached through one indirect call, which names a single WebAssembly signature, so an override that changes the parameter or return types is a compile error naming both methods
- Decorators: a wrapper that uses
*argsother than to pass it on, a user decorator on a method or a class, and any decorator name the program does not define and the compiler does not implement are refused. So are calls to any other name the program does not define and the compiler does not implement (divmod()andhash()of a builtin value, for example) - There is no
NotImplemented, so the special method the operator's rules pick answers: an__add__cannot defer to the right operand's__radd__. A reflected method is called only when the left operand has no method of its own, and needs one of the two operands to be a constant or a variable, since it runs the right operand first. A method's default must be a constant to be left out of a call (CPython evaluates it once, when the method is defined) reversed()gives the list it walks, so only where it is walked (a loop, a comprehension,list(),sorted(),str.join()), and only over a variable or a field, which it reads twice; anywhere else, where CPython gives an iterator object, it is refused- A class's bases are classes named directly: a class of the program, a standard-library class, a builtin exception,
object, orabc.ABC. Subclassing a builtin value type (class Stack(list),dict,str,int) is refused, since an instance would have to be that value too; subclasscollections.UserList,UserDict, orUserString, or hold the value in a field. A base computed when the class statement runs (class C(make_base()):) and a subscripted one other thanGeneric[T],Protocol[T], or acollectionsuser class are refused - A
withstatement needs its context manager's class to be known at compile time (an instantiation, a call with an annotated class return type, or a variable of known class type).__exit__'sexc_typereceives the exception object as well asexc, andtbNone; either may only be compared with None or tested for truth, and any other use is refused - The default
reprof an instance whose class defines neither__repr__nor__str__is refused, since CPython's includes an address - The
if __name__ == "__main__":block runs after every module definition is made, so one that reaches a definition placed after it (aNameErrorin CPython) is refused; move the block to the end of the file. A name the block binds for the first time is its own (an import's included), not a module global another function can read, and rebinding a module function or class there is refused
Exceptions and formatting
- Types are static, so the name a handler binds has the class the handler names (the common base of several): a field only a subclass has is refused through it.
e.argsreads back when every argument is a str, and traps otherwise - A traceback is not kept: a
__main__block's uncaught exception writes only the last line of CPython's report (ValueError: boom), andUnicodeDecodeError's position counts from the start of the line where CPython counts from its read buffer str.translate()takes a table whose type is known, adict[int, int]or adict[int, str](map a character to""to delete it, since a table mixing ints and None has no one type), andstr.maketrans()takes two strs of equal length; its one-argument and three-argument forms are refused.str.encode()takes its encoding anderrorsas constants:utf-8,ascii, orlatin-1, and for the last twostrict,ignore, orreplace. An exception it raises carries CPython's message, but notUnicodeEncodeError'sstart,end, orreasonattributes.bytes.decode()is not supported- A format spec CPython rejects (
format(1.5, "d"),"{:,s}".format("a")) is a compile error carrying CPython'sValueErrormessage when the spec is a literal, and traps when it is built at run time.str.formatand%need a literal format string; a*width or precision in%, and a precision on%d/%x/%o, are refused
Generators
- A generator method that another class of its hierarchy also defines is refused, since which generator a call makes would depend on the instance, and so is iterating a generator whose class is not known where it is iterated (one returned through an unannotated function, say)
- A generator expression is accepted where something walks it once: an argument of a builtin that takes an iterable (
sum,min,max,sorted,list,tuple,set,dict,any,all,enumerate,zip,map,filter), of a standard-library function or class, or of a collection's or str's method (join,extend,update, and the set methods), what aforstatement walks, or what a comprehension walks. Kept in a variable, returned, stored, or passed to the program's own functions and classes, it is refused, since it is built as a list, which can be walked twice, measured, and printed where CPython's generator cannot, and reads the variables it uses when it is made rather than when it is walked;print(),len(),bool(),str(), andtype()of one are refused for the same reason. Write a list comprehension for a list, or a generator function for a lazy sequence - A comprehension can iterate a generator or iterator only in its first
forclause: an inner clause is evaluated twice (once to size the result, once to fill it), so an iterator there is refused rather than drained twice.len()of a generator is refused, as CPython raisesTypeError
Coroutines and JavaScript
asynciocoversrun,create_task,ensure_future,gather,sleep,wait_for,timeout,TaskGroup(itscreate_task()takes the coroutine alone), a task's methods,Lock,Event,Semaphore,BoundedSemaphore, and the three queues; anything else of the module is refused by name. A queue's item type is named where it is made (q: asyncio.Queue[int] = asyncio.Queue()), and an unannotatedasyncio.Queue()is refused. A timeout'swhen()andreschedule()are refused, since the loop's clock (loop.time()) is not a value here.gather()andwait_for()are awaited directly (await asyncio.gather(...)),gather()with arguments that are all fixed or one*xs, andreturn_exceptions=Trueonly where every awaitable resolves to None, since a result or an exception in one list needs a type that holds either (its elements are thenOptional[BaseException]).sleep()takes the delay alone. A coroutine's result has a type an annotation can name, a coroutine method is not overridden in its class's hierarchy, and neither usessuper().task.cancel()takes no message. Tasks left pending are cancelled atasyncio.run()'s end, and a task group's when one of them fails, in the order they were made, where CPython's order is a set's and can change from run to run. AnExceptionGroupthat escapes to the top is reported by its own line (ExceptionGroup: unhandled errors in a TaskGroup (1 sub-exception)), where CPython draws the tree of the exceptions it holds;ExceptionGroup'ssplit(),subgroup(), andderive()are not supported- Time is the host's monotonic clock, and the loop sleeps through WASI's
poll_oneoff(in the playground's shim and the browser gate, a busy wait), except in a program using the JavaScript bridge run through the loader'sstart()where the browser has JSPI, which suspends instead. Equal delays set one after another fall due in the order they were set, as they do in CPython, whose clock moves between the two - A JavaScript value is converted where it is used, and a typed place holds one kind, so one stored where another is declared raises
TypeError(a JS string where an int is declared, an integer-valued number where a float is), where Pyodide keeps it as it is.int()andfloat()of a JS string raiseTypeErrorwith a hint to convert throughstr(), where Pyodide parses it. Arithmetic on a JS value and passing a list, dict, or instance to JavaScript (passto_js(x)) are refused. An augmented assignment to a JS property reads it as the kind of the right-hand side (a str, an int, or a float), so+= 1on a property holding a non-integer number raisesTypeErrorwhere Pyodide answers a float, and its object is read twice, so it must be a name or a chain of attributes.to_js()and.to_py()go through JSON, soto_js()of None inside a list makesnullwhere Pyodide makesundefined, a set is refused, and.to_py()of a Set, a function, a cycle, or a Map with keys that are not strings raisesTypeError. A name imported fromjsthat the page lacks raisesAttributeErrorwhere it is used, notImportErrorat the import. A JS value's item is read and assigned by an int (an array's, a typed array's), not a key pyfetch()'sjson()is stored where a type is declared (d: dict[str, int] = await r.json()) or awaited for nothing, and it parses with JavaScript'sJSON.parserather than Python'sjson, which differ on a number past 2**53 and onNaN.pyfetch()'ssignalandfetcher, and a response'sbuffer(),bytes(),memoryview(),abort(), andunpack_archive(), are refused. A keyword's dict or list value is written out in the call (headers={...}) or passed throughto_js()- A typed array's buffer methods are
to_bytes(),assign(b)of bytes or a bytearray, andassign_to(b)into a bytearray.to_memoryview()answers a memoryview, which is not supported, so it is refused, and.to_py()of a typed array (a memoryview in Pyodide) raisesTypeError. Copy the bytes into a bytearray withassign_to(), or out as bytes withto_bytes() create_proxy()andcreate_once_callable()take a function defined withdefat module level, or a lambda, and none of their options (capture_this,roundtrip). Passing a Python function to JavaScript without one is refused, where Pyodide passes a proxy it destroys when the call returns. A proxy called with another number of arguments than its function takes throwsPythonError(TypeError: the function takes 1 positional argument but 2 were given), whose wording differs from CPython's. An exception a proxied function raises comes back to the Python that called into JavaScript asJsException(PythonError: ValueError: ...), where Pyodide raises the original exception again. One JavaScript called on its own (an event listener, an animation frame) throws thePythonErrorfor the browser to report, where Pyodide also writes the traceback to standard error- A promise awaited inside
asyncio.run()needs JavaScript Promise Integration and the loader'sstart(); without them, or inside a call JavaScript made into the module, it raisesRuntimeError, since the loop cannot hand control back to JavaScript there. An exported coroutine awaits promises everywhere
The standard library
random.seed()of a str or bytes is refused: CPython seeds those from their SHA-512 digest. Seed with an int or a floatmath's transcendental functions round as musl's libm does, which CPython on macOS or glibc can differ from in the last bit (see Numbers)os.getcwd()is"/"andos.getpid()is 42, as on CPython for WASI, which has neither;sys.pathis empty, since every import is linked at compile timedatetime'sdatetimeis not a subclass ofdatehere, so a datetime passed where adateis declared is refused (isinstance(dt, date)still answers True). The onlytzinfoistimezone(a fixed offset, all the module defines), the local time zone is UTC, anddate.strftimeand the others render in the C locale.strptimetakes a constant format, without%G,%V,%C, the%O/%Eforms, or flags and widths (%-d).replace()takes the fields by name. The classes are not hashable here, so they cannot be set members or dict keys.sorted()orders them, butlist.sort(),min(), andmax()of instances are refused (min(days, key=lambda d: d.toordinal())works)repatterns are compiled with the program, so a pattern is written as a literal, a module constant, or a pattern compiled from one; one built at run time is refused, as are the LOCALE flag (for a bytes pattern too, since it matches by the host's locale), and a pattern CPython prints a FutureWarning for ([[,--in a set). A pattern'sfindallandsplitare only on a pattern whose group count is known (not one held where a plainre.Patternis declared).re.subandre.splittakecount,maxsplit, andflagsby name, as CPython 3.13 prints a DeprecationWarning for them by position. A group reference inside a lookbehind is refused.re.escape()of bytes needs the bytes written out, a module constant, or a parameter annotatedbytes; bytes reaching it any other way are refused as bytes passed for a strloggingformats are compiled, so a format string is written as a literal or a module constant, and one naming a field CPython could not fill (%(pathname)s,%(filename)s,%(module)s,%(thread)d, or a name a record does not have) is refused rather than reported as a logging error at every record. A message formatted with arguments is a literal.warn()is refused, since CPython prints a DeprecationWarning for it (callwarning()), as is aStreamHandlerwriting anywhere butsys.stdoutorsys.stderrlogger.exception()andexc_info=Truework inside anexceptblock; in a function called from one, or outside any, they are refused, asexc_info=eis the way to pass the exception along.exc_infois True, False, None, or an exception, not a tuple.%(exc_info)s,%(exc_text)s, andstack_info=are refusedextra=is a dict written out in the call, of str, int, float, or bool values, and a key has one type across the program;record.x = ...adds a field where the parameter is annotatedlogging.LogRecordlogging.config.dictConfig()reads its dict at compile time, so it is written out in the call or a module constant nothing else reads;incremental=Trueand the.key are refused, and a factory()is a class of the program named directly.fileConfig()andlisten()are refusedlogging.handlers:TimedRotatingFileHandlerkeeps local time as UTC, asdatetimedoes, soutc=changes nothing and no daylight saving time is accounted for, itsatTimeis refused, and itssuffixmay use%Y,%m,%d,%H,%M,%S, and%%only.SocketHandler,DatagramHandler,SysLogHandler,HTTPHandler, andSMTPHandler(no sockets),QueueHandlerandQueueListener(noqueue), andNTEventLogHandlerare refused. A rotating handler'snamerandrotatorare not supportedlogging.shutdown()at exit runs when a__main__block ends, not for a program entered throughmain()alonejson.loads()parses into the type its value is declared to have, so it needs one (an annotated variable, parameter, or return); a JSON object becomes adict[str, T], an array a list or tuple of the declared element types. A document the type cannot hold raisesTypeError, where CPython would hand back the value it found: a string where an int is declared, and an integer where a float is (a float place would print1.0where CPython keeps the int1, so write1.0in the document).json.dumps()takes its options as constants. A hook (dumps'default=, an encoder'sdefault()method,loads' or a decoder'sobject_hook=) is a function whose parameter and return are annotated, since what it is called with and what it answers are written and parsed by their types:defaultis called for the values of its parameter's type (or a subclass), and one that does not take a value it would be called with, or that answers what it is called with again, is refused. A lambda's parameter has no type here, so a hook is adef. Every JSON object goes to anobject_hook, the outer ones too, and one whose result the declared type cannot hold raisesTypeError, as a value of the wrong type does; the document is checked to be JSON before the hook sees any of it. AJSONEncoder'sindentis a constant, itsdefault=argument is refused (overridedefault()instead), as areiterencode()and readingindentback, andencode()on an encoder whosedefault()a subclass overrides. AJSONDecoder'sobject_hookis a function of the module, the program's hooks share one signature, andparse_float,parse_int,parse_constant,object_pairs_hook,strict=False, andraw_decode()are refused; so iscls=naming a class that defines__init__itertools.tee()andzip_longest()take sequences, andgroupby()a sequence, not a generator;itertools' functions take a function of the module or a lambda that reads nothing of the scope it is written innamedtuple()is supported as a module-level assignment (a class made inside a function would be a new class on every call), withdefaults=but notrename=ormodule=; a field given values of two types in different calls has no one type and holds ints only. A named tuple's fields cannot be assigned, as in CPython;_asdict()needs every field to have one type- A
Counter,defaultdict,OrderedDict,deque, orChainMapneeds its element types to be known where it is made: an annotation naming it or the builtin it reads as (c: Counter[str] = Counter(),self.by: Dict[str, List[int]] = defaultdict(list)), the constructor's argument, or what the function does with the variable afterwards. An empty one used for nothing that types it holds ints. A bare annotation (c: Counter) is refused with a hint to write the types. ACountermade from a generator whose element is a method call (Counter(w.lower() for w in words)) is refused; build the list first - A
defaultdictwhose factory is a function or a lambda has no repr (CPython's shows the function's address), and a lambda factory cannot read the scope it is written in. Adeque'spopleft()andappendleft()move its other elements, so they cost its length, where CPython's take constant time UserDict,UserList, andUserStringsubclasses: overriding__iter__is refused. AUserListorUserStringsubclass that overrides__getitem__cannot be walked (for,list(),sum(), and the like), since CPython walks it through that method, and a comprehension over.items()or.values()of a mapping with an overridden__getitem__is refused, while aforloop reads each value as it goes. A method that makes an instance (a slice,+,|,copy()) is refused where the subclass's__init__does not take the value alone, and aUserDictsubclass'scopy()where it has attributes of its own. Comparing a mapping view with==is refused.UserStringanswers the string methods it hands to its data as astrdoes (partition(),rfind(),istitle(),encode(), and the like); of those CPython makes a newUserStringfrom,zfill(),removeprefix(),removesuffix(),expandtabs(), andtranslate()are refused on it, as are%formatting andhash()collections' classes are not subclasses ofdicthere, though aCounter,defaultdict, orOrderedDictpasses where adictis declared (as the dict it holds) and answersisinstance(c, dict). A|merge of adefaultdictor anOrderedDictis refused, and so is a store into one reached through another object's field (self.stats.counts[w] += 1), which works on the instance's own field or a local. Two specializations are two classes, sotype(Counter("a")) is type(Counter([1]))is False where CPython answers True
Memory, modules, and files
- Memory is reclaimed by a mark-and-sweep collector that runs between statements once enough has been allocated, so a program that builds and drops objects in a loop runs in the memory its live objects need. It does not move objects or give pages back to the host, and it reads memory conservatively: a number that looks like an address keeps what it points at. Nothing is counted, so there is no cost on a store, and a module that never allocates carries no allocator or collector at all
- A value an exported function returns to the host (a string, a list, an instance) is kept for the life of the instance, since nothing says when the host is done with it. A host that calls such a function without end holds everything it was ever handed
delof an attribute (del obj.x) is refused.del nameis supported where the name is not read again before it is assigned: CPython raisesNameErrorthere, which a compiled variable cannot, so that program is refused, as is deleting a module variable another function uses or a variable a nested function shares- Imported user modules share one flat namespace in the output module, so two modules defining the same function name is a compile error naming both files
- Every module named by a constant is linked into the program when it is compiled.
importlib.import_module()of a name computed at run time loads a separately compiled module through the host (see Modules loaded by name), and__import__()of one is refused, as are their other arguments (package,fromlist) andimportlib's other functions. An import in a function binds its name there, so a read before it, a read in another function, and an import inside anif, a loop, or a handler, where it may not run, are refused. A module of your own first imported in a function runs its top level when that import first runs, as in CPython, but a dynamic import of one used in place (importlib.import_module("m").f()in a function) is refused; assign the import to a name. A module whose top level raised is not run again by a later import, where CPython runs it again - A file nothing imports, which
compile_python_projectlinks all the same, runs its top level when the program starts, after the entry's; CPython would not run it f.read()without a size reads up to 64 KiB per call;open()modes must be string literals