On this page:
8.1 Python Lists - pylist
pylist?
pylist
pylist-ref
pyfirst
pysecond
pylist-set!
list->pylist
vector->pylist
pylist-length
pylist->list
pylist->vector
pylist->pytuple
in-pylist
pylist-insert!
pylist-append-item!
pylist-reverse!
pylist-sort!
pylist-get-slice
8.2 Python Tuples - pytuple
pytuple?
pytuple
pytuple-ref
list->pytuple
vector->pytuple
pytuple-length
pytuple->list
pytuple->vector
pytuple->immutable-vector
pytuple->pylist
in-pytuple
pytuple-get-slice
8.3 Python Dictionaries - pydict
pydict?
hash->pydict
pydict->hash
pydict
pydict-ref
pydict-set!
pydict-remove!
pydict-clear!
pydict-contains?
pydict-copy
pydict-keys
pydict-values
pydict-count
pydict-merge!
in-pydict
8.4 Python Strings - pystring
pystring?
pystring
string->pystring
pystring->string
pystring-length
pystring-ref
subpystring
in-pystring
8.5 Python Generators
in-pygenerator

8 Datatypes

In Python all values are stored as objects. An object has an identity, a type and a value.

The identity of an object is given by the object’s address in memory. The is operator in Python corresponds to eq? in Racket.

Objects whose value can change are mutable. If the value of an object can’t change the object is called immutable. Note: In Racket an immutable vector can contain (mutable) boxes. Even though the vector is immutable the contents of the boxes can change. What doesn’t change is the identity of the boxes in the vector. In the same way an immutable Python container might contain mutable objects.

In Python numbers, strings and tuples are immutable. Python dictionaries and lists are mutable.

Python objects aren’t explicitly destroyed. When an object becomes unreachable, the memory of an object is reclaimed by the garbage collector. The current Python interpreter uses reference counting to keep track of an objects reachability.

When a Python object is returned from Python to Racket as a new reference, the Python system won’t deallocate the object until Racket says it’s okay to do so. Since we don’t want manually to keep track of the lifetime of Python objects, the low-level C-bindings register all new references will a will executor. When the Racket garbage collector detects a value is unreachable, the garbage collector will execute any wills associated with the value. The will executor we are using, simply decrements the reference count of the Python object.

If you stick to the high level functions in pyffi you don’t need to worry about reference counting. However it might be relevant if you need to use the low-level C-API.

8.1 Python Lists - pylist

Despite the name a Python list is not a singly linked list, but an array. Objects with the type "list" will be called pylist to tell them apart from standard Racket lists.

The operations pylist, list->pylist and vector->pylist can be used to construct pylists from Racket values.

In in for-loops, use in-pylist to iterate through the elements.

procedure

(pylist? v)  boolean?

  v : any/c
Returns #t if v is a pylist (an obj with type "list").

> (pylist 1 2 3)

(obj "list" : [1, 2, 3])

> (pylist? (pylist 1 2 3))

#t

> (pylist? (pylist))

#t

> (pylist? '(1 2 3))

#f

procedure

(pylist v ...)  pylist?

  v : any/c
Returns a newly allocated pylist containing the vs as its elements.

> (pylist 1 2 3 4)

(obj "list" : [1, 2, 3, 4])

> (pylist)

(obj "list" : [])

> (pylist (pylist 1 2 3 4) 5 (pylist 6 7))

(obj "list" : [[1, 2, 3, 4], 5, [6, 7]])

procedure

(pylist-ref xs i)  any/c

  xs : pylist?
  i : exact-nonnegative-integer?
Returns the element with index i in the pylist xs. The first element has index 0, and the last elements is one less than (pylist-length xs).

This function takes constant time.

> (pylist-ref (pylist "a" "b" "c" "d") 1)

"b"

procedure

(pyfirst xs)  any/c

  xs : pylist?
Returns the first element of the pylist xs.

> (pyfirst (pylist "a" "b" "c" "d"))

"a"

procedure

(pysecond xs)  any/c

  xs : pylist?
Returns the second element of the pylist xs.

> (pysecond (pylist "a" "b" "c" "d"))

"b"

procedure

(pylist-set! xs i v)  any/c

  xs : pylist?
  i : exact-nonnegative-integer?
  v : any/c
Replace the element with index i of the pylist xs with the value v.

This function takes constant time.

> (define xs (pylist 0 1 2 3 4))
> (pylist-set! xs 1 #t)
> xs

(obj "list" : [0, True, 2, 3, 4])

procedure

(list->pylist xs)  pylist?

  xs : list?
Returns a pylist with the same length and (possibly converted) elements as xs.
The elements are converted with racket->python.

> (list->pylist '(1 2 3 4))

(obj "list" : [1, 2, 3, 4])

> (list->pylist '(1 "foo" #(3 4)))

(obj "list" : [1, 'foo', (3, 4)])

> (list->pylist '(1 (2 3) #(4 (5 6))))

(obj "list" : [1, [2, 3], (4, [5, 6])])

procedure

(vector->pylist xs)  pylist?

  xs : vector?
Returns a pylist with the same length and (possibly converted) elements as xs.
The elements are converted with racket->python.

> (vector->pylist '#(1 2 3 4))

(obj "list" : [1, 2, 3, 4])

> (vector->pylist '#(1 "foo" #(3 4)))

(obj "list" : [1, 'foo', (3, 4)])

> (vector->pylist '#(1 (2 3) #(4 (5 6))))

(obj "list" : [1, [2, 3], (4, [5, 6])])

procedure

(pylist-length xs)  integer?

  xs : pylist?
Returns the length (size) of a pylist (i.e. the number of elements in the pylist).

> (pylist-length (pylist 1 2 3))

3

procedure

(pylist->list xs)  list?

  xs : pylist?
Returns a list with the same length and elements as xs.

This function takes time proportional to the size of xs.

> (pylist->list (pylist 1 2 3 #t #f "a"))

'(1 2 3 #t #f (obj "str" : 'a'))

procedure

(pylist->vector xs)  vector?

  xs : pylist?
Returns a vector with the same length and elements as xs.

This function takes time proportional to the size of xs.

> (pylist->vector (pylist 1 2 3 #t #f "a"))

'#(1 2 3 #t #f (obj "str" : 'a'))

procedure

(pylist->pytuple xs)  pytuple?

  xs : pylist?
Returns a pytuple with the same length and elements as xs.

This function takes time proportional to the size of xs.

> (pylist->pytuple (pylist 1 2 3 #t #f "a"))

(obj "tuple" : (1, 2, 3, True, False, 'a'))

procedure

(in-pylist xs)  stream?

  xs : pylist?
Returns a sequence (that is also a stream) that is equivalent to using xs directly as a sequence.

> (define xs (pylist 0 1 2 3))
> (for/list ([x (in-pylist xs)])
    x)

'(0 1 2 3)

procedure

(pylist-insert! xs i v)  void?

  xs : pylist?
  i : exact-nonnegative-integer?
  v : any/c
Inserts the value v in the pylist xs at index i.

Worst case this function takes time proportional to the size of xs.

> (define xs (pylist 0 1 2 3))
> (pylist-insert! xs 2 #t)
> xs

(obj "list" : [0, 1, True, 2, 3])

procedure

(pylist-append-item! xs v)  void?

  xs : pylist?
  v : any/c
Add the element v to the end of the pylist xs. The length of the pylist becomes 1 greater.

> (define xs (pylist 10 11 12 13))
> (pylist-length xs)

4

> (pylist-append-item! xs 14)
> xs

(obj "list" : [10, 11, 12, 13, 14])

> (pylist-length xs)

5

procedure

(pylist-reverse! xs)  void?

  xs : pylist?
Reverse the order in which the elements in the pylist xs occur.

> (define xs (pylist 1 2 3 4))
> (pylist-reverse! xs)
> xs

(obj "list" : [4, 3, 2, 1])

procedure

(pylist-sort! xs)  void?

  xs : pylist?
Rearrange the order in which the elements in the pylist xs occur. After calling pylist-sort! the elements will be in order with respect to the Python comparison operator <.

> (define xs (pylist 3 2 4 1))
> (pylist-sort! xs)
> xs

(obj "list" : [1, 2, 3, 4])

> (define ys (pylist 3 #t 2 4 #f #f 1))
> (pylist-sort! ys)
> ys

(obj "list" : [False, False, True, 1, 2, 3, 4])

procedure

(pylist-get-slice xs low-index high-index)  pylist?

  xs : pylist?
  low-index : exact-nonnegative-integer?
  high-index : exact-nonnegative-integer?
Returns a new pylist with the elements of xs from index low-index inclusive to index high-index exclusive.

In Python notation: list[low:high].

> (define xs (pylist 1 2 3 #t #f "a"))
> (pylist-get-slice xs 1 3)

(obj "list" : [2, 3])

8.2 Python Tuples - pytuple

Python tuples correspond to immutable Racket vectors.

Even though there is no datastructure in Racket called "tuple", Python tuples will have the name "putuple" to match the names of pylist and pydict.

The operations pytuple, list->pytuple and vector->pytuple can be used to construct pytuples from Racket values.

In in for-loops, use in-pytuple to iterate through the elements.

procedure

(pytuple? v)  boolean?

  v : any/c
Returns #t if v is a pytuple (an obj with type "tuple").

> (pytuple 1 2 3)

(obj "tuple" : (1, 2, 3))

> (pytuple? (pytuple 1 2 3))

#t

> (pytuple? (pytuple))

#t

> (pytuple? '(1 2 3))

#f

procedure

(pytuple v ...)  pytuple?

  v : any/c
Returns a newly allocated pytuple containing the vs as its elements.

> (pytuple 1 2 3 4)

(obj "tuple" : (1, 2, 3, 4))

> (pytuple)

(obj "tuple" : ())

> (pytuple (pytuple 1 2 3 4) 5 (pytuple 6 7))

(obj "tuple" : ((1, 2, 3, 4), 5, (6, 7)))

procedure

(pytuple-ref xs i)  any/c

  xs : pytuple?
  i : exact-nonnegative-integer?
Returns the element with index i in the pytuple xs. The first element has index 0, and the last elements is one less than (pytuple-length xs).

This function takes constant time.

> (pytuple-ref (pytuple "a" "b" "c" "d") 1)

"b"

procedure

(list->pytuple xs)  pytuple?

  xs : list?
Returns a pytuple with the same length and (possibly converted) elements as xs.
The elements are converted with racket->python.

> (list->pytuple '(1 2 3 4))

(obj "tuple" : (1, 2, 3, 4))

> (list->pytuple '(1 "foo" #(3 4)))

(obj "tuple" : (1, 'foo', (3, 4)))

> (list->pytuple '(1 (2 3) #(4 (5 6))))

(obj "tuple" : (1, [2, 3], (4, [5, 6])))

procedure

(vector->pytuple xs)  pytuple?

  xs : vector?
Returns a pytuple with the same length and (possibly converted) elements as xs.
The elements are converted with racket->python.

> (vector->pytuple '#(1 2 3 4))

(obj "tuple" : (1, 2, 3, 4))

> (vector->pytuple '#(1 "foo" #(3 4)))

(obj "tuple" : (1, 'foo', (3, 4)))

> (vector->pytuple '#(1 (2 3) #(4 (5 6))))

(obj "tuple" : (1, [2, 3], (4, [5, 6])))

procedure

(pytuple-length xs)  integer?

  xs : pytuple?
Returns the length (size) of a pytuple (i.e. the number of elements in the pytuple).

> (pytuple-length (pytuple 1 2 3))

3

procedure

(pytuple->list xs)  list?

  xs : pytuple?
Returns a list with the same length and elements as xs.

This function takes time proportional to the size of xs.

> (pytuple->list (pytuple 1 2 3 #t #f "a"))

'(1 2 3 #t #f (obj "str" : 'a'))

procedure

(pytuple->vector xs)  vector?

  xs : pytuple?
Returns a vector with the same length and elements as xs.

This function takes time proportional to the size of xs.

> (pytuple->vector (pytuple 1 2 3 #t #f "a"))

'#(1 2 3 #t #f (obj "str" : 'a'))

procedure

(pytuple->immutable-vector xs)  vector?

  xs : pytuple?
Returns an immutable vector with the same length and elements as xs.

This function takes time proportional to the size of xs.

> (define xs (pytuple->immutable-vector (pytuple 1 2 3 #t #f "a")))
> xs

'#(1 2 3 #t #f (obj "str" : 'a'))

> (immutable? xs)

#t

procedure

(pytuple->pylist xs)  pylist?

  xs : pytuple?
Returns a pylist with the same length and elements as xs.

This function takes time proportional to the size of xs.

> (pytuple->pylist (pytuple 1 2 3 #t #f "a"))

(obj "list" : [1, 2, 3, True, False, 'a'])

procedure

(in-pytuple xs)  stream?

  xs : pylist?
Returns a sequence (that is also a stream) that is equivalent to using xs directly as a sequence.

> (define xs (pytuple 0 1 2 3))
> (for/list ([x (in-pytuple xs)])
    x)

'(0 1 2 3)

procedure

(pytuple-get-slice xs low-index high-index)  pytuple?

  xs : pytuple?
  low-index : exact-nonnegative-integer?
  high-index : exact-nonnegative-integer?
Returns a new pytuple with the elements of xs from index low-index inclusive to index high-index exclusive.

In Python notation: list[low:high].

> (define xs (pytuple 1 2 3 #t #f "a"))
> (pytuple-get-slice xs 1 3)

(obj "tuple" : (2, 3))

8.3 Python Dictionaries - pydict

Dictionaries in Python are associative arrays indexed by keys. Given a key one can lookup a value. Think of dictionaries as sets of key/value pairs.

Any immutable value can be used as a key, so strings, numbers and tuples can always be used as keys.

The corresponding data structure in Racket is the hash table.

Use the operations pydict->hash and hash->pydict to convert back and forth between pydicts and hash tables.

procedure

(pydict? v)  boolean?

  v : any/c
Returns #t if v is a pydict (an obj with type "dict").

> (pydict? (hash->pydict (hash "a" 1  "b" 2)))

#t

procedure

(hash->pydict x [#:convert convert])  pydict?

  x : hash?
  convert : procedure? = rp
Returns a newly allocated pydict with the same key/value-pairs as the hash table x. The function convert is used to convert Racket values to Python ones. The default value for the keyword argument convert is rp.

> (hash->pydict (hash "a" 1  "b" 2))

(obj "dict" : {'b': 2, 'a': 1})

> (hash->pydict (hash 1 "x"  2 "y"))

(obj "dict" : {1: 'x', 2: 'y'})

> (hash->pydict (hash #(1 2) "tuple used as key"))

(obj "dict" : {(1, 2): 'tuple used as key'})

procedure

(pydict->hash x    
  [#:convert-key convert-key    
  #:convert-value convert-value])  hash?
  x : pydict?
  convert-key : procedure? = pr/key
  convert-value : procedure? = pr
Returns a newly allocated hash table with the same key/value-pairs as the pydict x.

The function convert-key is used to convert the keys from Python values to Racket ones.
The function convert-value is used to convert the values.

The default value for the key conversion is pr/key.
The default value for the value conversion is pr.

> (pydict->hash (hash->pydict (hash "a" 1  "b" 2)))

'#hash(("a" . 1) ("b" . 2))

> (pydict->hash (hash->pydict (hash 1 "x"  2 "y")))

'#hash((1 . (obj "str" : 'x')) (2 . (obj "str" : 'y')))

> (pydict->hash (hash->pydict (hash #(1 2) "tuple used as key")))

'#hash((#(1 2) . (obj "str" : 'tuple used as key')))

procedure

(pydict [#:convert convert] key val ... ...)  pydict?

  convert : procedure? = rp
  key : any/c
  val : any/c
Creates a pydict with each given key mapped to the following val; each key must have a val, so the total number of arguments to hash must be even.

The key to val mappings are added to the table in the order that they appear in the argument list, so later mappings can hide earlier mappings if the keys are equal.

The default value for the key and value conversion is rp.

> (pydict "a" 1  "b" 2)

(obj "dict" : {'a': 1, 'b': 2})

> (pydict 1 "x"  2 "y")

(obj "dict" : {1: 'x', 2: 'y'})

> (pydict #(1 2) "tuple used as key")

(obj "dict" : {(1, 2): 'tuple used as key'})

procedure

(pydict-ref d key [failure-result])  any/c

  d : pydict?
  key : any/c
  failure-result : failure-result/c
   = 
(lambda ()
  (raise (make-exn:fail:contract ....)))
Returns the value for key in hash. If no value is found for key, then failure-result determines the result:

If failure-result is a procedure, it is called (through a tail call) with no arguments to produce the result.

Otherwise, failure-result is returned as the result.

> (define d (pydict "a" 1  "b" 2))
> d

(obj "dict" : {'a': 1, 'b': 2})

> (pydict-ref d "a")

1

> (pydict-ref d "b")

2

> (pydict-ref d "c")

pydict-ref: no value found for key

  key: "c"

procedure

(pydict-set! d key val)  void?

  d : pydict?
  key : any/c
  val : any/c
Maps key to val in d, overwriting any existing mapping for key.

> (define d (pydict "a" 1))
> (pydict-set! d "a" 11)
> (pydict-set! d "b" 22)
> d

(obj "dict" : {'a': 11, 'b': 22})

procedure

(pydict-remove! d key)  void?

  d : pydict?
  key : any/c
Removes any existing mapping for key in the pydict d.

> (define d (pydict "a" 1 "b" 2))
> (pydict-remove! d "b")
> (pydict-remove! d "c")
> d

(obj "dict" : {'a': 1})

procedure

(pydict-clear! d)  void?

  d : pydict?
Remove all key/value pairs from the dictionary.

> (define d (pydict "a" 1  "b" 2))
> d

(obj "dict" : {'a': 1, 'b': 2})

> (pydict-clear! d)
> d

(obj "dict" : {})

procedure

(pydict-contains? d key)  void?

  d : pydict?
  key : any/c
Returns #t if the pydict x contains the key key.

> (define d (pydict "a" 1  "b" 2))
> (pydict-contains? d "a")

#t

> (pydict-contains? d "x")

#f

procedure

(pydict-copy d)  pydict?

  d : pydict?
Return a new dict with the same key/vaue pairings as the pydict d.

> (define d1 (pydict "a" 1  "b" 2))
> (define d2 (pydict-copy d1))
> (list d1 d2)

'((obj "dict" : {'a': 1, 'b': 2}) (obj "dict" : {'a': 1, 'b': 2}))

> (pydict-set! d1 "a" 11)
> (list d1 d2)

'((obj "dict" : {'a': 11, 'b': 2}) (obj "dict" : {'a': 1, 'b': 2}))

procedure

(pydict-keys d)  pylist?

  d : pydict?
Return a pylist with all keys in the pydict d.

> (define d (pydict "a" 1  "b" 2))
> (pydict-keys d)

(obj "list" : ['a', 'b'])

procedure

(pydict-values d)  pylist?

  d : pydict?
Return a pylist with all values in the pydict d.

> (define d (pydict "a" 1  "b" 2 "c" 1))
> (pydict-values d)

(obj "list" : [1, 2, 1])

procedure

(pydict-count d)  integer?

  d : pydict?
Returns the number of keys mapped by the pict hash.

> (define d (pydict "a" 1  "b" 2 "c" 1))
> (pydict-count d)

3

procedure

(pydict-merge! d1 d2 [override?])  void?

  d1 : pydict?
  d2 : pydict?
  override? : boolean? = #t
Computes the union of d1 with the d2 by mutable update, adding each element of d2 to d1 in turn.

If a key k is present in both pydicts and is mapped to values v1 and v2 in d1 and d2 respectively, then k is mapped to v2 if override? is true, and mapped to v1 otherwise.

> (define d1 (pydict "a" 1  "b" 2))
> (define d2 (pydict "b" 22 "c" 33))
> (pydict-merge! d1 d2)
> d1

(obj "dict" : {'a': 1, 'b': 22, 'c': 33})

> (define d1 (pydict "a" 1  "b" 2))
> (define d2 (pydict "b" 22 "c" 33))
> (pydict-merge! d1 d2 #f)
> d1

(obj "dict" : {'a': 1, 'b': 2, 'c': 33})

procedure

(in-pydict d)  stream?

  d : pydict?
Returns a sequence (that is also a stream) equivalent to d directly as a sequence.

> (define d (pydict "a" 1 "b" 2))
> (for/list ([(key value) (in-pydict d)])
    (list key value))

'(((obj "str" : 'a') 1) ((obj "str" : 'b') 2))

8.4 Python Strings - pystring

Strings in Python are represented as objects with type str. Python strings are immutable sequences of Unicode code points.

There is no character type in Python, so various Python libraries use strings of length 1 to represent characters.

procedure

(pystring? v)  boolean?

  v : any/c
Returns #t if v is a pystring (an obj with type "str").

> (string->pystring "foo")

(obj "str" : 'foo')

> (pystring? (string->pystring "foo"))

#t

procedure

(pystring char ...)  pystring?

  char : char?
Returns a new pystring whose length is the number of provided chars, and whose positions are initialized with the given chars.

> (pystring #\f #\o #\o)

(obj "str" : 'foo')

procedure

(string->pystring x)  pystring?

  x : string?
Return a newly allocated pystring with the same characters as the string x.

> (string->pystring "foo")

(obj "str" : 'foo')

procedure

(pystring->string x)  string?

  x : pystring?
Return a newly allocated string with the same characters as the pystring x.

> (pystring->string (pystring #\f #\o #\o))

"foo"

procedure

(pystring-length x)  integer?

  x : pystring?
Returns the length of x (i.e. the number of characters in string).

> (pystring-length (pystring #\f #\o #\o))

3

procedure

(pystring-ref x k)  char?

  x : pystring?
  k : exact-nonnegative-integer?
Returns the character at position k in the pystring x. The first position in the string corresponds to 0, so the position k must be less than the length of the string, otherwise the exception exn:fail:contract is raised.

> (define foo (pystring #\f #\o #\o))
> (pystring-ref foo 0)

#\f

> (pystring-ref foo 10)

pystring-ref: index 10 out of range for the string "foo"

procedure

(subpystring x start [end])  pystring?

  x : pystring?
  start : exact-nonnegative-integer?
  end : exact-nonnegative-integer? = (pystring-length x)
Returns a pystring that is (- end start) characters long, and that contains the same characters as x from start inclusive to end exclusive.

The first position in a string corresponds to 0, so the start and end arguments must be less than or equal to the length of x, and end must be greater than or equal to start, otherwise the exception exn:fail:contract is raised.

> (subpystring (string->pystring "foobarbaz") 3 6)

(obj "str" : 'bar')

procedure

(in-pystring x)  stream?

  x : pystring
Returns a sequence (that is also a stream) that is equivalent to using x directly as a sequence.

> (define x (string->pystring "foo"))
> (for/list ([c (in-pystring x)])
    c)

'(#\f #\o #\o)

8.5 Python Generators

Python generator correspond to Racket generators from racket/generator. Think of a generator as a function that can produce a series of values.

If the body of Python function contains a yield statement, calling the function returns a generator object. Use next repeatedly on the generator to generate the series of values.

The yield expr statement both "pauses" the computation of the generator and returns the result of evaluating the expression. The computation is resumed by next.

This example shows a generator that produces the natural numbers.

 (run*  @~a{def f():

                  x=0

                  while 1:

                    x=x+1

                  yield x}

(let ([g (main.f)])

  (list (next g) (next g) (next g))) )

This produces the list (list 1 2 3).

Usually the most convenient way of using such a generator is to use in-pygenerator.

procedure

(in-pygenerator pygen)  stream?

  pygen : pygenerator?
Returns a sequence (that is also a stream) that produces the elements from pygen.

 (let ([g (main.f)])

   (for ([_ 3]

         [x (in-pygenerator g)])

     x))

Generators are automatically wrapped in an generator-obj struct, which has obj as super type. The wrapping allows us to make the in-generator implicit in for loops.

 (let ([g (main.f)])

   (for ([_ 3] [x g])

     x))