Category Archives: Python

Coconut – keywords I

proxy

Continuo con le keywords, da qui, copiando qui.

match

Coconut provides fully-featured, functional pattern-matching through its match statements.

Overview
match statements follow the basic syntax match <pattern> in <value>. The match statement will attempt to match the value against the pattern, and if successful, bind any variables in the pattern to whatever is in the same position in the value, and execute the code below the match statement. match statements also support, in their basic syntax, an if <cond> that will check the condition after executing the match before executing the code below, and an else statement afterwards that will only be executed if the match statement is not. What is allowed in the match statement’s pattern has no equivalent in Python, and thus the specifications below are provided to explain it.

Syntax Specification
Coconut match statement syntax is

match <pattern> in <value> [if <cond>]:
    <body>
[else:
    <body>]

where <value> is the item to match against, <cond> is an optional additional check, and <body> is simply code that is executed if the header above it succeeds. <pattern> follows its own, special syntax, defined roughly like so:

pattern ::= (
    "(" pattern ")"                 # parentheses
    | "None" | "True" | "False"     # constants
    | "=" NAME                      # check
    | NUMBER                        # numbers
    | STRING                        # strings
    | [pattern "as"] NAME           # capture
    | NAME "(" patterns ")"         # data types
    | "(" patterns ")"              # sequences can be in tuple form
    | "[" patterns "]"              #  or in list form
    | "(|" patterns "|)"            # lazy lists
    | "{" pattern_pairs "}"         # dictionaries
    | ["s"] "{" pattern_consts "}"  # sets
    | (                             # head-tail splits
        "(" patterns ")"
        | "[" patterns "]"
      ) "+" pattern
    | pattern "+" (                 # init-last splits
        "(" patterns ")"
        | "[" patterns "]"
      )
    | (                             # head-last splits
        "(" patterns ")"
        | "[" patterns "]"
      ) "+" pattern "+" (
        "(" patterns ")"            # this match must be the same
        | "[" patterns "]"          #  construct as the first match
      )
    | (                             # iterator splits
        "(" patterns ")"
        | "[" patterns "]"
        | "(|" patterns "|)"        # lazy lists
      ) "::" pattern
    | pattern "is" exprs            # type-checking
    | pattern "and" pattern         # match all
    | pattern "or" pattern          # match any
    )

😳 panico?

Semantic Specification
match statements will take their pattern and attempt to “match” against it, performing the checks and deconstructions on the arguments as specified by the pattern. The different constructs that can be specified in a pattern, and their function, are:

  • Constants, Numbers, and Strings: will only match to the same constant, number, or string in the same position in the arguments.
  • Variables: will match to anything, and will be bound to whatever they match to, with some exceptions:
    • If the same variable is used multiple times, a check will be performed that each use match to the same value.
    • If the variable name _ is used, nothing will be bound and everything will always match to it.
  • Explicit Bindings (<pattern> as <var>): will bind <var> to <pattern>.
  • Checks (=<var>): will check that whatever is in that position is equal to the previously defined variable <var>.
  • Type Checks (<var> is <types>): will check that whatever is in that position is of type(s) <types> before binding the <var>.
  • Data Types (<name>(<args>)): will check that whatever is in that position is of data type <name> and will match the attributes to <args>.
  • Lists ([<patterns>]), Tuples ((<patterns>)), or lazy lists ((|<patterns>|)): will only match a sequence (collections.abc.Sequence) of the same length, and will check the contents against <patterns>.
  • Dicts ({<pairs>}): will only match a mapping (collections.abc.Mapping) of the same length, and will check the contents against <pairs>.
  • Sets ({<constants>}): will only match a set (collections.abc.Set) of the same length and contents.
  • Head-Tail Splits (<list/tuple> + <var>): will match the beginning of the sequence against the <list/tuple>, then bind the rest to <var>, and make it the type of the construct used.
  • Init-Last Splits (<var> + <list/tuple>): exactly the same as head-tail splits, but on the end instead of the beginning of the sequence.
  • Head-Last Splits (<list/tuple> + <var> + <list/tuple>): the combination of a head-tail and an init-last split.
  • Iterator Splits (<list/tuple/lazy list> :: <var>, or <lazy list>): will match the beginning of an iterable (collections.abc.Iterable) against the <list/tuple/lazy list>, then bind the rest to <var> or check that the iterable is done.

When checking whether or not an object can be matched against in a particular fashion, Coconut makes use of Python’s abstract base classes. Therefore, to enable proper matching for a custom object, register it with the proper abstract base classes.

Examples:

c31

Showcases else statements, which work much like else statements in Python: the code under an else statement is only executed if the corresponding match fails.

c32

Showcases matching to data types. Values defined by the user with the data statement can be matched against and their contents accessed by specifically referencing arguments to the data type’s constructor.

c33

Showcases how the combination of data types and match statements can be used to powerful effect to replicate the usage of algebraic data types in other functional programming languages.

c34

Showcases head-tail splitting, one of the most common uses of pattern-matching, where a + <var> (or :: <var> for any iterable) at the end of a list or tuple literal can be used to match the rest of the sequence.
E naturalmente il gioco può continuare (l’idea della pipe, Douglas McIlroy et al. (tanti al. anche prima) è fantastica):

c35

With Pythoncan’t be done without a long series of checks for each match statement. See the compiled code for the Python syntax.
Un’osservazione non so quanto valida: il codice prodotto dalla compilazione di Coconut è sempree molto poco leggibile, OK se è finale, altrimenti richiede pazienza, assay😉

:mrgreen:

Coconut – operatori II

1892_5

Ci sono argomenti istituzionali come Octave (sì siamo in ritardo ma il caldo, le ferie, …) e ci sono argomenti che forse poi –chissà– come Coconut. Poi ce ne sono anche altri ancora, come SICP, che meriterebbero più attenzione. Avendo tempo. OK, oggi Coconut, continuo da qui a copiare qui.

Iterator Slicing
Coconut uses a $ sign right after an iterator before a slice to perform iterator slicing. Coconut’s iterator slicing works much the same as Python’s sequence slicing, and looks much the same as Coconut’s partial application, but with brackets instead of parentheses. It has the same precedence as subscription.
Iterator slicing works just like sequence slicing, including support for negative indices and slices, and support for slice objects in the same way as can be done with normal slicing. Iterator slicing makes no guarantee, however, that the original iterator passed to it be preserved (to preserve the iterator, use Coconut’s tee function).
Coconut’s iterator slicing is very similar to Python’s itertools.islice, but unlike itertools.islice, Coconut’s iterator slicing supports negative indices, and is optimized to play nicely with custom or built-in sequence types as well as Coconut’s map, zip, range, and count objects, only computing the elements of each that are actually necessary to extract the desired slice. This behavior can also be extended to custom objects if they define their __getitem__ method lazily and set __coconut_is_lazy__ to True.

c24

Nota: il manuale usa 10**100 ma mi da overflow😳

With Python can’t be done without a complicated iterator slicing function and inspection of custom objects. The necessary definitions in Python can be found in the Coconut header.

Alternative Unicode
Coconut supports Unicode alternatives to many different operator symbols. The Unicode alternatives are relatively straightforward, and chosen to reflect the look and/or meaning of the original symbol.

→ (\u2192)                  => "->"
↦ (\u21a6)                  => "|>"
*↦ (*\u21a6)                => "|*>"
↤ (\u21a4)                  => "<|" 
↤* (\u21a4*)                => "<*|" 
⋅ (\u22c5)                  => "*"
↑ (\u2191)                  => "**"
÷ (\xf7)                    => "/"
÷/ (\xf7/)                  => "//"
∘ (\u2218)                  => ".."
− (\u2212)                  => "-" (only subtraction)
⁻ (\u207b)                  => "-" (only negation)
¬ (\xac)                    => "~"
≠ (\u2260) or ¬= (\xac=)    => "!="
≤ (\u2264)                  => "<=" 
≥ (\u2265)                  => ">="
∧ (\u2227) or ∩ (\u2229)    => "&"
∨ (\u2228) or ∪ (\u222a)    => "|"
⊻ (\u22bb) or ⊕ (\u2295)    => "^"
« (\xab)                    => "<<" 
» (\xbb)                    => ">>"
… (\u2026)                  => "..."
× (\xd7)                    => "@" (only matrix multiplication)

c25

OK, ma forse –sono vecchio– perché cambiare. E poi non è comodissimo introdurre il codice dopo C-S-u. Non sono riuscito a usare quelli con il codice esadecimale, per esempio xf7.

Keywords

data
The syntax for data blocks is a cross between the syntax for functions and the syntax for classes. The first line looks like a function definition, but the rest of the body looks like a class, usually containing method definitions. This is because while data blocks actually end up as classes in Python, Coconut automatically creates a special, immutable constructor based on the given arguments.

Coconut data blocks create immutable classes derived from collections.namedtuple and made immutable with __slots__. Coconut data statement syntax looks like:

data <name>(<args>):
    <body>

<name> is the name of the new data type, <args> are the arguments to its constructor as well as the names of its attributes, and <body> contains the data type’s methods.Subclassing data types can be done easily by inheriting from them in a normal Python class, although to make the new subclass immutable, the line

__slots__ = ()

Rationale
A mainstay of functional programming that Coconut improves in Python is the use of values, or immutable data types. Immutable data can be very useful because it guarantees that once you have some data it won’t change, but in Python creating custom immutable data types is difficult. Coconut makes it very easy by providing data blocks.

Python Docs
Returns a new tuple subclass. The new subclass is used to create tuple-like objects that have fields accessible by attribute lookup as well as being indexable and iterable. Instances of the subclass also have a helpful docstring (with type names and field names) and a helpful __repr__() method which lists the tuple contents in a name=value format.

Any valid Python identifier may be used for a field name except for names starting with an underscore. Valid identifiers consist of letters, digits, and underscores but do not start with a digit or underscore and cannot be a keyword such as class, for, return, global, pass, or raise.

Named tuple instances do not have per-instance dictionaries, so they are lightweight and require no more memory than regular tuples.

c26

This will fail because data objects are immutable

c27

la versione Python

c28

Showcases the syntax, features, and immutable nature of data types.

c29

Nota: la riga dopo il decoratore viene scritta in modo bizzarro nella REPL, quella corretta è def size(Node(left, right)) = size(left) + size(right) ovviamente.

la versione Python

c30

Pausa, devo digerire il tutto; poi continuo sempre sulle keywords😀
:mrgreen:

pdb – debugger per Python

tapacolo

Doug Hellmann (che trovate qui e qui e forse anche da altre parti) mi dice che c’è pdb, debugger interattivo per Python, cosa che non sapevo.

E pensare che il modulo arriva preinstallato con Python (sia 2.7 che 3.x). Vero che:

This is currently undocumented but easily understood by reading the source.

Siccome Doug spiega tutto per bene potrei chiudere qui ma il post sarebbe troppo corto😳 e allora metto giù un paio di screenshots, quelli delle mie prove iniziali.
Finora mi sono limitato a mettere dei print dove verosimilmente dovrebbero esserci breakpoints: entrata delle finzioni che sto testando, valori che ritornano e così via.
Con i debuggers ho passato parecchio tempo, prima su Prime poi su Apollo e Unix. Finché ho scoperto la meraviglia dell’ambiente integrato (inizialmente con Turbo Pascal 3.0) e poi con i vari Basic di Microsoft.

Ma nel frattempo avevo visto che conveniva –di molto– scrivere in modo da minimizzare la necessità di ricorrere al debug; anche perché non sempre c’è (o non so che c’è). Che è poi quello che dice Daniel Lemire: I do not use a debugger.

Ma mai dire mai, un tool in più che può sempre servire.
A me piace partire al volo con l’equivalente di “Hello World!” che per il debug è qualcosa di simile (d0.py):

#!/usr/bin/python3

m = 7
n = 5
s = m + n
print("somma", m, n , s)
d = m - n
r = m / n
ri = m // n
print("- & /", d, r, ri)

d0

Il comando w (where) visualizza la linea corrente, per il codice si usa l (list), q per quit.

d1

Hey! mi sembra di essere tornato indietro di 35 anni, il comando s (step) e la sessione è com’era su Prime; vero che allora dovevi compilare con l’opzione -d e linkare forse con una libreria apposita (non ricordo, è passato un po’ di tempo).

Dentro all’interprete ha senso solo per i moduli, uso l’esempio di Doug (ddh.py):

#!/usr/bin/env python3
# encoding: utf-8
#
# Copyright (c) 2010 Doug Hellmann.  All rights reserved.
#

class MyObj:

    def __init__(self, num_loops):
        self.count = num_loops

    def go(self):
        for i in range(self.num_loops):
            print(i)
        return

d2

e così via.

OK, adesso a fare le cose come si deve dovrei fare l’elenco dei comandi, far vedere come settare un breakpoint, andarci, esaminare lo stack e roba simile. Una cosa tremendamente poco sexy che spero davvero di non dover fare mai. Molto diversa dall’IDE del Borland Pascal (e simili)😀

:mrgreen:

Coconut – 2 – intermezzo per qualche riflessione

Wiz

Non ho ancora deciso se Coconut diventerà uno dei miei linguaggi; devo approfondire.
Ma sospendo un attimo –da qui— per qualche considerazione –banale lo so!– su come comportarmi.

c13

Nella REPL di Coconut si possono dare i comandi sia con la sua propria sintassi che con quella di Python. Può essere comodo😀

c14

Con Python (versione 2.x che è comunque quella usata da Coconut) sono più limitato; notare p.es. la print senza importarla dal futuro. Per contro è molto più veloce.

c15-0

IPython (a proposito ho creato l’alias per risparmiare i diti):

c15-1

Mi accorgo che devo approfondire, chissà …

Finora ho trattato comandi semplici; con le funzioni ho

c16

notare che si devono inserire gli spazi richiesti da Python; anche la riga vuota per terminare la funzione è richiesta.

c17

Stessa cosa con IPython.

c18

Coconut fa le bizze con funzioni Python

c19

le vuole con la sua sintassi. Ed è più lento.

Basta, ho deciso che devo vedere bene IPyton; pausa😀

:mrgreen:

Coconut – 1 – prime prove con un case study

sg4

Continuo l’esame di Coconut, iniziato qui, per vedere se può essere utile per estendere Python, premettendo che è tutto da un punto di vista strettamente personale. Oggi qualche case study, sempre dal tutorial.

Fattoriale

In the first case study we will be defining a factorial function, that is, a function that computes n! where n is an integer >= 0.
To start off with, we’re going to have to decide what sort of an implementation of factorial we want. There are many different ways to tackle this problem, but for the sake of concision we’ll split them into four major categories: imperative, recursive, iterative, and addpattern.

Metodo imperativo

def factorial(n):
    """Compute n! where n is an integer >= 0."""
    if n `isinstance` int and n >= 0:
        acc = 1
        for x in range(1, n+1):
            acc *= x
        return acc
    else:
        raise TypeError("the argument to factorial must be an integer >= 0")

# Test cases:
-1 |> factorial |> print # TypeError
0.5 |> factorial |> print # TypeError
0 |> factorial |> print # 1
3 |> factorial |> print # 6

c6

OOPS! non come volevo, provo dentro IPython:

c7

No, continua a non funzionare. Intanto ho dovuto cambiare il nome al file, il carattere – viene interpretato. Ma resta l’errore sulla metaclasse. Solo per verifica provo a togliere dal codice .coco e casi che danno errore e ricompilare:

def factorial(n):
    """Compute n! where n is an integer >= 0."""
    if n `isinstance` int and n >= 0:
        acc = 1
        for x in range(1, n+1):
            acc *= x
        return acc
    else:
        raise TypeError("the argument to factorial must be an integer >= 0")

# Test cases:
### -1 |> factorial |> print # TypeError
### 0.5 |> factorial |> print # TypeError
0 |> factorial |> print # 1
3 |> factorial |> print # 6

c8

Sì ma così non ha senso; supponiamo che il parametro a factorial sia dato interattivamente c’è il crash. Insomma no😦

Metodo ricorsivo
Continuo ad avere errori; salto al prossimo case study: quick_sort.

Case study 2: quick_sort

Sortare una sequenza

def quick_sort([]):
    return []

@addpattern(quick_sort)
def quick_sort([head] + tail):
    """Sort the input sequence using the quick sort algorithm."""
    return (quick_sort([x for x in tail if x < head])         + [head]         + quick_sort([x for x in tail if x >= head]))

# Test cases:
[] |> quick_sort |> print 
[3] |> quick_sort |> print 
[0,1,2,3,4] |> quick_sort |> print 
[4,3,2,1,0] |> quick_sort |> print 
[3,0,4,2,1] |> quick_sort |> print

c9

OK, provo a compilare…

c10

OK, dentro IPython ho ***errori*** devo approfondire come si usa.

Sortare un iteratore

def quick_sort(l):
    """Sort the input iterator, using the quick sort algorithm, and
       without using any data until necessary."""
    match [head] :: tail in l:
        tail, tail_ = tee(tail)
        yield from (quick_sort((x for x in tail if x < head))             :: (head,)             :: quick_sort((x for x in tail_ if x >= head))
            )

# Test cases:
[] |> quick_sort |> list |> print 
[3] |> quick_sort |> list |> print 
[0,1,2,3,4] |> quick_sort |> list |> print 
[4,3,2,1,0] |> quick_sort |> list |> print 
[3,0,4,2,1] |> quick_sort |> list |> print

c11

e compilando:

c12

Pausa😉 perplesso😳 devo vedere cli altri casi prima di giungere a una conclusione:mrgreen:

Coconut – 0 – e se…

banner

E se Coconut riesce a estendere Python come dice…
Da provare, installo

c0

e provo subito, seguendo le dritte di Coconut Tutorial:

c1


OK
, c’è; avanti😀

Usare il compilatore

Of course, while being able to interpret Coconut code on-the-fly is a great thing, it wouldn’t be very useful without the ability to write and compile larger programs. To that end, it’s time to write our first Coconut program: “hello, world!” Coconut-style.

First, we’re going to need to create a file to put our code into. The recommended file extension for Coconut source files is .coco, so let’s create the new file hello_world.coco.

Prima sorpresa: non serve la shebang, non servono gli usuali soliti import e la sintassi è (come già visto nel test sopra) un po’ diversa, insomma ecco hello_world.coco:

"hello, world!" |> print

compilo:

c2

OK, produce hello_world.py decisamente più lungo di quel che mi aspettavo.
C’è la shebang, gli import, tante definizioni di funzioni e alla fine

# Compiled Coconut: ------------------------------------------------------------

(print)("hello, world!")

che si può lanciare:

c3

Usare IPython o Jupyter

uhmmm…
Non ho nessuno dei due installati; prima o poi Jupyter potrebbe tentarmi, per intanto inizio con IPython.

c4

e poi …

c5

OK, lento ma è un caso scemo; poi sono niubbassay😳

nome

If you don’t get the reference, the image above is from Monty Python and the Holy Grail, in which the Knights of the Round Table bang Coconuts together to mimic the sound of riding a horse. The name was chosen to reference the fact that Python is named after Monty Python as well.

evan

Chi è stato?

Evan Hubinger is an undergraduate student studying mathematics and computer science at Harvey Mudd College. Fa anche altre cose, tante, rockz😀

Pausa, ma devo continuare a indagare.

:mrgreen:

Dov’è? ovvero differenze con Windows

n2Ieri ho scoperto che un piccolo tool che avevo scritto nel 2006 e completamente dimenticato è ancora vivo e usato😀

w1
È una cosa semplicissima, rileva la lista dei dischi montati (quelli con una lettera seguita da due-punti) e restituisce quello che nella radice ha il file con il nome specificato. Siccome allora ero giovane l’avevo scritto in Delphi e dev’essere stata una cosa volante, l’ho completamente dimenticato. Fino a ieri.

Ora per cose normali con Linux c’è whereis:

w2

che potrebbe anche essere configurato per casi speciali:

w3

Cose che con Windows sono (o erano?) possibili qui sono giustamente vietate, per elementari ragioni di sicurezza:

w4

Per il resto non dovrebbero esserci problemi

w5

e il nome dell’utente corrente può essere rilevato con uno di questi due modi (o altri ancora, forse)

w6

Programmatically (or scrptically), disks.py:

import os

mdir = "/media/" + os.getenv("USER")
print("mdir =", mdir)
disks = os.listdir(mdir)
print("disks =", disks)

w7

:mrgreen:

Python – la concorrenza resa davvero facile con i decoratori

CYd

Ieri ho pubblicato un post dicendo che non sono capace a raccontare certe cose e, inaspettatamente almeno per me, ha avuto un pakko di visite.
Allora mi viene la voglia di insistere, chissà se anche oggi…😉
Anche perché serendipicamente, dallo stesso ambiente del post di ieri, mi arriva una segnalazione davvero sexy invitante😀 Ovviamente su qualcosa di cui più volte ho detto cose diverse da quelle di oggi, cambio idea, mi aggiorno😀

Viene tutto da Decorated Concurrency – Python multiprocessing made really really easy sul blog di Peter Bengtsson, è un blog da seguire, imho.

Sì, finora dicevo di lasciar perdere i decoratori, poco intuitivi e difficilissimi da fare. Ma poi capita questo post che illustra un caso realistico reso facilissimo (davvero facilissimissimo)😀
Nel post trovate la fonte e la documentazione, tutto OK.
Non mi resta che provarlo.

Per Ubuntu occorre installare il package deco con

sudo pip3 install deco

Ma probabilmente pip3 non è installato e allora

sudo apt-get install python3-pip

OK, tutto pronto, la versione non concorrente dell’esempio di Peter (nc.py):

import time

def slow(index):
    time.sleep(5)

def run():
    for index in list('123'):
        slow(index)
run()

nc

sì, troppo semplice, non si vede il consumo di CPU.

Ecco la versione concorrente (cd.py):

import time
from deco import concurrent, synchronized

@concurrent
def slow(index):
    time.sleep(5)

@synchronized
def run():
    for index in list('123'):
        slow(index)

run()

cd

OK, visto? tutto come previsto.

Cosa succede se i processi sono tanti, più delle CPU presenti? (tcd.py):

import time
from deco import concurrent, synchronized

@concurrent
def slow(index):
    time.sleep(5)

@synchronized
def run():
    for index in list('123456789'):
        slow(index)

run()

tcd

ovvio; conviene visualizzare il thread attivo con una piccola modifica (tcdp.py):

import time
from deco import concurrent, synchronized

@concurrent
def slow(index):
    print(index)
    time.sleep(5)

@synchronized
def run():
    for index in list('123456789'):
        slow(index)

run()

tcdp

ecco le scritte compaiono a gruppi di 4 (per 4 CPU); si dall’immagine non si vede ma provate, dai😉

Quindi, conclusione personale, a volte servono anche i decoratori, non vale quanto detto in precedenza.

:mrgreen:

Il test di Jake

past

Cosa non si trova a seguire le persone giuste su Twitter; prendi Jake VanderPlas, un über-nerd che lascio si definisca da solo, paura di dimenticare qualcosa. Oggi twitta una cosa su Python (chi ha detto che è il nuovo Basic, semplice-semplice?). Il test è troppo bello per non riportarlo qui sul blog, senza spiegare il perché perché non ne sono capace voglio lasciare a voi il piacere di trovare la soluzione😉

Poi chi è vecchio –come me– si ricorderà di tanti casi in cui cambiando compilatore (o versione dello stesso) le cose cambiavano. If it’s working tuca nen! come da detto inglese: if it ain’t broke, don’t fix it.

OK, tornando a Jake ecco il cinguettio.

Da provare subito:

class A: 
    def b(self): 
        pass 

if A.b is A.b: 
    print("Python 3") 
else: 
    print("Python 2")

j0

OK, come previsto. Notare che l’interprete Python guarda anche dove non deve, approfittando del cambiamento di print che nel 2.x è un’istruzione e nel 3.x una funzione faccio una piccola modifica, supposta innocente:

class A: 
    def b(self): 
        pass 

if A.b is A.b: 
    print("Python 3") 
else: 
    print "Python 2"

j1

Uh! ma mica finisce qui:

class A: 
    def b(self): 
        print("hi!")

if A.b is A.b: 
    print("Python 3")
    print(A.b) 
    A.b(True)
else: 
    print("Python 2")
    print(A.b) 
    A.b(True)

j2

Quasi panico!😳 Cose che capitano.

Io intanto torno con le operazioni di post-installazione dello Scoiattolo Ospitale di Xenial Xerus.

:mrgreen:

Rust e Python – 2

n3Continuo da qui l’esame della serie di post di TheK3nger sull’uso di Rust all’interno di codice Python e viceversa; oggi in particolare copio da How to use Rust in Python (Part 3).
Inoltre –per comodità– il Git è qui: THeK3nger/rust-python-integration-examples.

[W]e have seen how to pass not trivial data to Rust functions such as a Python list. It is still not enough, though. In many cases we need to pass complex data structure back and forth from a Rust library. We may need to pass quaternions [davvero qualcuno usa quella roba lì?], 3D points, trees, a list of “books”… In short: anything.

Lato Rust

As usual, the first thing to do is to implement the Rust library function. This time we need to design first a struct that will represent our complex object. We chose to represent 2D points.

#[repr(C)]
pub struct Point {
    x: f64,
    y: f64,
}

This is a classic rust struct declaration, but we need to use #[repr(C)] to explain to the compiler that Point is a C struct.

Now, we can simply implement the desired function. In this example we will implement a function that, given two Point  representing a segment, returns the midpoint of the segment.

#[no_mangle]
pub extern fn middle(p1: Point, p2: Point) -> Point {
    Point { x: (p1.x + p2.x)/2.0, y: (p1.y + p2.y)/2.0 }
}

Lato Python

Python side is slightly more complicated. Also in Python, we need first to declare the Point.

class Point(ctypes.Structure):
    _fields_ = [("x", ctypes.c_double), ("y", ctypes.c_double)]
    
    def __str__(self):
        return "Point ({},{})".format(self.x, self.y)

The class representing our C/Rust struct is different from the usual one:

  • The class must extend the ctypes.Structure class defined in ctypes.
  • The fields of the C struct must be defined using the builtin _fields_ attribute. This attribute must contain a list of tuples. Each tuple must contain the 1) name of the field and 2) the type of the field according the ctypes declaration. In our case Point has two fields, “x” and “y”, both doubles.

Now it is time to setup our Rust function. We specify the type declaration for our function.

lib.middle.argtypes = (Point, Point)
lib.middle.restype = Point

Then we can use this function as usual!

p1 = Point(1.0, 5.0)
p2 = Point(1.0, 10.0)

res_point = lib.middle(p1, p2)
print(res_point)

And we have done. It is not so difficult, isn’t it? Certo, semplicissimo, adesso provo, ecco il file rp_points.py:

import ctypes

lib = ctypes.CDLL("./target/release/librustypython.so")

class Point(ctypes.Structure):
    _fields_ = [("x", ctypes.c_double), ("y", ctypes.c_double)]
    
    def __str__(self):
        return "Point ({},{})".format(self.x, self.y)

# Then we specify as usual the type declaration of the Rust function.
lib.middle.argtypes = (Point, Point)
lib.middle.restype = Point

# And then we can easily use it as a native python function!
p1 = Point(1.0, 5.0)
p2 = Point(1.0, 10.0)

res_point = lib.middle(p1, p2)
print(res_point)

k6

OK😀 come già detto TheK3nger rockz!😀 Rust & Python too😀

Devo davvero imparare bene Rust, da usarsi per le operazioni impegnative.

:mrgreen:

Iscriviti

Ricevi al tuo indirizzo email tutti i nuovi post del sito.

Segui assieme ad altri 95 follower