#set document(title: "10.4 Python types", author: "Modular Inc. / XYZ Homework") #set page(width: 8.5in, height: auto, margin: 1in) #import "@preview/cetz:0.5.2" #set text(font: ("STIX Two Text", "Libertinus Serif", "New Computer Modern"), size: 10.5pt, lang: "en") #show math.equation: set text(font: ("STIX Two Math", "New Computer Modern Math")) #set par(justify: true, leading: 0.62em, spacing: 0.9em) #set enum(spacing: 1.1em) // room between list items so tall inline fractions don't collide #set list(spacing: 1.1em) #set table(stroke: 0.5pt + rgb("#c7ccd3")) #let BLUE = rgb("#183B6F") // brand navy — section bars + example/solution labels (white on navy 11.09:1) #let ORANGE = rgb("#A94509") // brand primary-700 — AA-safe deep orange for TEXT (5.93:1 on white; raw brand #F37021 is 2.94:1 and must never carry text) #let RED = rgb("#DC2626") // brand error-600 #let GREEN = rgb("#059669") // brand success-600 (decoration only; small green text uses green-text #007942) #show heading.where(level: 1): it => block(width: 100%, above: 0pt, below: 16pt, fill: gradient.linear(BLUE, rgb("#2C5AA0")), inset: (x: 14pt, y: 12pt), radius: 3pt, text(fill: white, weight: "bold", size: 19pt, it.body)) #show heading.where(level: 2): it => block(width: 100%, above: 18pt, below: 10pt, fill: BLUE, inset: (x: 10pt, y: 6pt), radius: 2pt, text(fill: white, weight: "bold", size: 12pt, it.body)) #show heading.where(level: 3): it => text(fill: ORANGE, weight: "bold", size: 12.5pt, it.body) #show heading.where(level: 4): it => text(fill: BLUE, weight: "bold", size: 10.5pt, it.body) #let examplebox(label, title, body) = block(width: 100%, breakable: true, fill: rgb("#EFF1F5"), stroke: 0.5pt + rgb("#CFDDF0"), radius: 4pt, inset: 10pt, above: 12pt, below: 12pt)[ #block(below: 6pt)[#box(fill: BLUE, inset: (x: 6pt, y: 2pt), radius: 2pt, text(fill: white, weight: "bold", size: 8.5pt, label)) #h(0.4em) #strong[#title]] #body] // rail = decorative left rule (raw brand token); labelcolor = AA-safe label text shade #let notebox(label, rail, labelcolor, tint, body) = block(width: 100%, breakable: true, fill: tint, stroke: (left: 3pt + rail), inset: (left: 10pt, rest: 8pt), radius: (right: 4pt), above: 11pt, below: 11pt)[ #text(fill: labelcolor, weight: "bold", size: 7.5pt, tracking: 0.5pt)[#upper(label)] #linebreak() #body] #let solutionbox(body) = block(above: 4pt, below: 8pt)[ #text(fill: BLUE, weight: "bold", size: 8.5pt)[Solution] #linebreak() #body] #let figph(msg) = block(width: 100%, height: 60pt, fill: rgb("#f6f7f9"), stroke: (paint: rgb("#c7ccd3"), dash: "dashed"), radius: 4pt, inset: 10pt)[ #align(center + horizon, text(fill: rgb("#889"), style: "italic", size: 9pt, msg))] // Standardize inlined figure sizes: measure the natural CeTZ canvas, then scale to a // consistent envelope (aspect-aware; see build_typst.py FIG_* constants). Unlike the // print preamble, dimensions are FLOORED: in an editor a user can trim a figure to a // degenerate 1-D shape (a bare line), and w/h or tw/w would then divide by zero. #let _STD_W = 3.5 #let _WIDE_W = 5.6 #let _MAX_H = 3.4 #let _ASPECT_WIDE = 2.2 #let _UPSCALE_MAX = 1.15 #let stdfig(body) = context { let m = measure(body) let w = calc.max(m.width / 1in, 0.01) let h = calc.max(m.height / 1in, 0.01) let tw = if w / h > _ASPECT_WIDE { _WIDE_W } else { _STD_W } let s = calc.min(tw / w, _MAX_H / h, _UPSCALE_MAX) align(center, box(scale(x: s * 100%, y: s * 100%, reflow: true, body))) } #show figure: set block(breakable: false) #set figure(gap: 8pt) #show figure.caption: set text(size: 8.5pt, fill: rgb("#555")) == 10.4#h(0.6em)Python types When calling Python methods, Mojo needs to convert back and forth between native Python objects and native Mojo objects. Most of these conversions happen automatically, but there are a number of cases that Mojo doesn't handle yet. In these cases you may need to do an explicit conversion, or call an extra method. === Mojo types in Python Mojo primitive types implicitly convert into Python objects. Today we support integers, floats, booleans, and strings. To demonstrate, the following example dynamically creates an in-memory Python module named py\_utils containing a type\_printer() function, which simply prints the type of a given value. Then you can see how different Mojo values convert into corresponding Python types. from std.python import Python def main() raises: py\_module = """ def type\_printer(value): print(type(value)) """ py\_utils = Python.evaluate(py\_module, file=True, name="py\_utils") py\_utils.type\_printer(4) py\_utils.type\_printer(3.14) py\_utils.type\_printer(True) py\_utils.type\_printer("Mojo") \ \ \ \ === Python types in Mojo You can also create and use Python objects from Mojo. ==== Mojo wrapper objects When you use Python objects in your Mojo code, Mojo adds the #link("https://mojolang.org/docs/std/python/python_object/PythonObject/")[PythonObject] wrapper around the Python object. This object exposes a number of common double underscore methods (dunder methods) like \_\_getitem\_\_() and \_\_getattr\_\_(), passing them through to the underlying Python object. Most of the time, you can treat the wrapped object just like you'd treat it in Python. You can use dot-notation to access attributes and call methods, and use the \[\] operator to access an item in a sequence. You can explicitly create a wrapped Python object by initializing a PythonObject with a Mojo integer, float, boolean, or string. Additionally, you can create several types of Python collections directly in Mojo using the #link("https://mojolang.org/docs/std/python/python/Python/#dict")[Python.dict()], #link("https://mojolang.org/docs/std/python/python/Python/#list")[Python.list()], and #link("https://mojolang.org/docs/std/python/python/Python/#tuple")[Python.tuple()] static methods. For example, to create a Python dictionary, use the #link("https://mojolang.org/docs/std/python/python/Python/#dict")[Python.dict()] method: from std.python import Python def main() raises: py\_dict = Python.dict() py\_dict\["item\_name"\] = "whizbang" py\_dict\["price"\] = 11.75 py\_dict\["inventory"\] = 100 print(py\_dict) {'item\_name': 'whizbang', 'price': 11.75, 'inventory': 100} With the #link("https://mojolang.org/docs/std/python/python/Python/#list")[Python.list()] method, you can create a Python list and optionally initialize it: from std.python import Python def main() raises: py\_list = Python.list("cat", 2, 3.14159, 4) n = py\_list\[2\] print("n =", n) py\_list.append(5) py\_list\[0\] = "aardvark" print(py\_list) n = 3.14159 \['aardvark', 2, 3.14159, 4, 5\] The #link("https://mojolang.org/docs/std/python/python/Python/#tuple")[Python.tuple()] method creates a Python tuple of values: from std.python import Python def main() raises: py\_tuple = Python.tuple("cat", 2, 3.1415, "cat") n = py\_tuple\[2\] print("n =", n) print("Number of cats:", py\_tuple.count("cat")) n = 3.1415 Number of cats: 2 If you want to construct a Python type that doesn't have a literal Mojo equivalent, you can also use the #link("https://mojolang.org/docs/std/python/python/Python/#evaluate")[Python.evaluate()] method. For example, to create a Python set: from std.python import Python def main() raises: var py\_set = Python.evaluate('{2, 3, 2, 7, 11, 3}') num\_items = len(py\_set) print(num\_items, "items in the set.") contained = 7 in py\_set print("Is 7 in the set:", contained) 4 items in the set. Is 7 in the set: True PythonObject implements the #link("https://mojolang.org/docs/std/format/Writable/")[Writable] trait. This allows you to print Python values using the built-in #link("https://mojolang.org/docs/std/io/io/print/")[print()] function, as shown in several of the previous examples. However, most other Mojo APIs don't accept PythonObject values directly. In these cases you'll need to explicitly convert a Python value into a native Mojo value. For example: from std.python import Python from std.python import PythonObject def main() raises: var py\_string = PythonObject("Hello, Mojo!") var py\_bool = PythonObject(True) var py\_int = PythonObject(123) var py\_float = PythonObject(3.14) var mojo\_string = String(py=py\_string) var mojo\_bool = Bool(py=py\_bool) var mojo\_int = Int(py=py\_int) var mojo\_float = Float64(py=py\_float) ==== Comparing Python types in Mojo You can use Python objects in Mojo comparison expressions, and the Mojo is operator also works to compare the identity of two Python objects. Python values like False and None evaluate as false in Mojo boolean expressions as well. If you need to know the type of the underlying Python object, you can use the #link("https://mojolang.org/docs/std/python/python/Python/#type")[Python.type()] method, which is equivalent to the Python type() builtin. You can test if a Python object is of a particular type by performing an identity comparison against the type as shown below: from std.python import Python def main() raises: var value1 = PythonObject(3.7) value2 = Python.evaluate("10/3") \# Compare values print("Is value1 greater than 3:", value1 \> 3) print("Is value1 greater than value2:", value1 \> value2) \# Compare identities value3 = value2 print("value1 is value2:", value1 is value2) print("value2 is value3:", value2 is value3) \# Compare types py\_float\_type = Python.evaluate("float") print("Python float type:", py\_float\_type) print("value1 type:", Python.type(value1)) print("Is value1 a Python float:", Python.type(value1) is py\_float\_type) Is value1 greater than 3: True Is value1 greater than value2: True value1 is value2: False value2 is value3: True Python float type: \ value1 type: \ Is value1 a Python float: True