Working with Python Bridge objects
When using the PythonBridge objects are transferred from Python to GT as proxies. PBProxyObject
are GT native objects that refer to a Python object using an identifier. On the Python side there is a registry where those identifiers are mapped to the corresponding Python objects.
Numbers
Let's start with a list of numbers.
import random numbers = random.sample(range(100), 10)
What we get when we Inspect this Python expression is a GT Inspector with a double view: a Remote view and a Proxy view. They look at the same object in GT, a PBProxyObject
, referring to some Python list
. These two views show different perspectives.
The Remote view is the default and acts so that it looks as if this is normal object, even though it is remote. The underlying PBProxyObject
is hidden and we see inspector tabs related to the Python object. There is even a custom tab showing the list's items.
The Proxy view looks at the internals and shows the actual PBProxyObject
which is maintaining this illusion: it shows us the internal mechanism. Here we can see the identifier of the Python object in the registry as well as its Python class.
Clicking on a number will inspect an integer, still as a proxy. Now there is a Download local instance action (down arrow) which will convert this primitive type to a matching GT type.
Where things get a little bit confusing is that some composite primitive types like list
or dict
are also converted, but only when using the API, not using the Python snippet like above. They are automatically mapped to their GT equivalents (Array and Dictionary respectively).
PBApplication do: [ :application | application newCommandFactory << #numbers asP3GIdentifier; sendAndWait ]
PBApplication do: [ :application | application newCommandStringFactory resultExpression: '{"x":1,"y":2}'; sendAndWait ]
Still, this difference disappears when normal Python objects are involved. These are always tranferred as proxies.
Persons
Let's consider an ultra simple Person object and assign some instances to some variables.
class Person: def __init__(self, first, last): self.first = first self.last = last def __repr__(self): return f'Person({self.first} {self.last})' john = Person('John','Doe') jane = Person('Jane','Doe') persons = [ john, jane ]
You can see the list of Persons, look at one Person and look at the first and last attributes.
When going through the API, the list gets converted but not the elements.
PBApplication do: [ :application | application newCommandFactory << #persons asP3GIdentifier; sendAndWait ]
This is not a problem because we can send messages to the Python object. You can open the contextual playground at the bottom of the Inspector where a Python snippet's self
will refer to the Python object under inspection. Try self.first
.
jane
We can do the same programmatically using PBProxyObject>>#attributeAt:
and PBProxyObject>>#callMethod:
which will invoke Python side functionality over the bridge. You can experiment with this using the contextual playground of the proxy perspective instead of the remote perspective.
jane := PBApplication do: [ :application | application newCommandFactory << #jane asP3GIdentifier; sendAndWait ]
jane attributeAt: #first
jane callMethod: #__repr__
Mirrors
We can go one step further and add a Mirror class for our Python class. This is a subclass of PBProxyObject
that implements a class side pythonClass
method returning the name of the Python class that we want to mirror.
Create an empty PBPerson
subclass of of PBProxyObject
with a class side pythonClass
method returning Person
as a string or symbol. From now on, the proxy class on the GT side will change to our new mirror class.
On itself, this does not change anything, yet.
We can now add GT methods to our mirror class so that it acts more and more like a normal GT class even though it is effectively a proxy to a remote Python object.
Try adding first
and last
accessors that use PBProxyObject>>#attributeAt:
or a repr
method that uses PBProxyObject>>#callMethod:
.
You can now add more code to the mirror object, like complex, graphical Inspector views that are not possible in Python.