Quirrel4.41.0

Coming from Python

Most of Python carries over: dynamic types, closures, generators, exceptions, iteration over a container, and classes whose methods receive the instance. The syntax changes. Blocks use braces, indentation has no meaning, a statement ends at the end of the line, and every name is declared before it is read.

Two differences change what a program does without an error message: // is a comment, and "", [] and {} are true. Read the traps even if you skip the tables.

Values and bindings

PythonQuirrel
x = 1, rebindablelocal x = 1 - rebindable; let x = 1 - binds once
MAX = 10 by conventionconst MAX = 10 * 5, folded at compile time
None, True, Falsenull, true, false
int and float are separate typesthe same two, integer and float
7 // 27 / 2; two integers divide as integers
7 / 27 / 2.0; one float makes the answer a float
2 ** 3pow(2, 3) from math
round(2.5) gives 2round(2.5) gives 3; a half goes away from zero
x if c else yc ? x : y
f"{n} left"$"{n} left"
"a" + b$"a{b}", or concat; + joins too, and the analyzer flags it as w264
"""two lines"""@"two lines", a verbatim string with no escapes
str(x), int(s), float(s)x.tostring(), s.tointeger(), s.tofloat()
len(s)s.len()
type(x).__name__type(x), or typeof x
x is Nonex == null
a and b, a or b, not aa && b, a || b, !a, and they keep the operand value
1_000_0001_000_000
0b1010no binary literal; only decimal 123 or hex 0xF2
# comment// comment or /* ... */

Containers

PythonQuirrel
[1, 2, 3] list[1, 2, 3] array
{"a": 1} dict{ a = 1 }, a table, with = inside
a key that is not a name{ [123] = 1 }, any expression in the brackets
d["k"] = v on a new keyd.k <- v; plain = writes a slot that is already there
d["k"] on a missing keyit throws, as in Python; d?[k] gives null, and ?? fills in
d.get(k, 0)d?[k] ?? 0
del d[k]d.$rawdelete(k)
k in dk in d
v in itemsitems.contains(v); in on an array asks about an index
d.keys(), d.values(), d.items()d.keys(), d.values(), d.topairs()
items.append(v)items.append(v), and it takes several values
a + b for listsa.extend(b); + does not join two arrays
a[1:3], a[-2:]a.slice(1, 3), a.slice(-2)
s[0]s.slice(0, 1); s[0] is the character code
",".join(parts)",".join(parts)
s.split(","), s.strip()s.split(","), s.strip()
s.replace(a, b)s.replace(a, b), and it replaces every match
s.find(sub)s.indexof(sub), and null when the text is not there
sorted(a)let b = clone a then b.sort(); a.sort() sorts in place
[f(x) for x in a if p(x)]a.filter(@(v) p(v)).map(@(v) f(v))
{**a, **b}{ ...a, ...b }, or a.__merge(b); both return a new table
copy.copy(a)clone a, one level deep

Control flow

PythonQuirrel
if a: elif b: else:if (a) { } else if (b) { } else { }
for v in seq:foreach (v in seq)
for i, v in enumerate(seq):foreach (i, v in seq)
for k, v in d.items():foreach (k, v in d)
for i in range(n):for (local i = 0; i < n; i++); there is no range
while c:while (c) { }
break, continuethe same
matchif (a) ... else if (b) ...; switch is deprecated and off by default, see control flow
try / except E as e / finallytry { } catch (e) { }; no exception classes, and no finally
raise ValueError(m)throw m; any value can be thrown, and catch takes them all
assert c, massert(c, m)
with open(p) as f:no with; close the file yourself
yield vyield v
next(it)resume it
pass{ }

Functions

PythonQuirrel
def f(a, b = 1):function f(a, b = 1) { }
lambda x: x * 2@(x) x * 2
def f(*args):function f(...) { }; the extra values arrive in vargv
def f(**kw):not there; take a table
f(b = 1)not there: '=' inside 'function argument' is forbidden
f(*args)f.acall([this, a, b])
a nested def that reads an outer namethe same
global xthere are no implicit globals; global gives a const or enum to other files
a decoratornot there; wrap the function yourself

Classes

PythonQuirrel
class Squad:class Squad { }
class Squad(Unit):class Squad(Unit) { }, with one base class only
def __init__(self, n):constructor(n) { }
selfthis, and one method calls another as this.other()
super().__init__()base.constructor()
self.hp = 100 in __init__declare hp = 100 in the class body; an instance takes no new slots later
__str___tostring
__getitem__, __call___get, _call; see metamethods
@property_get and _set
isinstance(x, C)x instanceof C
a class attributestatic max = 3, one value for every instance

Modules

PythonQuirrel
import mathlet math = require("math"), or import "math" at compile time
from math import floorfrom "math" import floor
from math import *from "math" import *
import numpy as npimport "mod" as np
a module runs once and is cachedthe same, and modules says when

Traps

examples/pages/coming-from/python.nut
// // starts a comment, so a floor division quietly loses its right side
let half = 7 // 2
println(half)

// two integers divide as integers, and neither / nor % is floored
println(7 / 2)
println(-7 / 2)
println(-7 % 3)

// only null, false, 0 and 0.0 are false
println([] ? "[] is true" : "[] is false")
println("" ? "empty string is true" : "empty string is false")

// in asks about a key, and the keys of an array are its indices
println(2 in [10, 20, 30])
println(99 in [10, 20, 30])
println([10, 20, 30].contains(20))
Output:
7
3
-3
-1
[] is true
empty string is true
true
false
true

What is not there

See also