In the hurry of announcing the beta, I forgot to give some hints on the new
VarPyth.pas:
It wraps a Python object and lets you act on it (call methods or use
properties) like you would do in Python, and like you did with PythonAtom,
but there are differences:
the variant always maintains its link to a Python object, and is never
casted to a basic variant type (integer, string, array) when returning a
value, like it was with PythonAtom.
v1 := VarPythonCreate(1);
v2 := VarPythonCreate(2);
v1 + v2 will return a new variant that will hold the a Python object that is
the result of the addition, and the addition will be performed by Python
itself, using the PyNumber_Add API.
Previously, with PythonAtom, if you accessed a property or called a method,
the value returned by Python was automatically converted to a basic variant
type, or wrapped up into an PythonAtom variant otherwise.
The main advantage of this solution was to convert Python sequences (lists
or tuples) into array of variants, letting us access them using the index
operator (list[x]).
Now, it is not possible anymore, as the returned sequence will be returned
as a new custom variant, and our custom variant doesn't support indexing!!!
I don't know how to do it? Maybe adding the flag varArray to our VType and
setting array bounds that let us access any item, but I'm not sure...
Anyway, it could work with sequences, but not with dictionaries, as it
accepts any type as a key.
So, to work around this problem I added several special methods/properties:
foo.GetItem(index): same as foo[index]
foo.SetItem(index, value): same as foo[index] = value
foo.Length or foo.Length(): same as len(foo)
foo.GetSlice(index1, index2): same as foo[index1:index2]
foo.SetSlice(index1, index2, value): same as foo[index1:index2] = value
foo.Contains(value): same as value in foo
foo.DeleteItem(Index): same del foo[index]
Note that my special methods are not case sensitive.
Note also that you must use the parenthesis with functions or methods that
have no argument, to distinguish your call with a property access:
list.sort()
if you write "list.sort" only, you'll get the instance method object instead
of performing the sort action.
The advantage of the new solution is that you always work with the Python
objects, and you can do anything you want with them, like you would in
Python. The arithmetic operations, or comparisons, work the same, and you
can't add an integer to a string, for instance, as you can with variants.
v1 := VarPythonCreate(1);
v2 := v1 + 'hello'; --> Python exception
but v2 := 'hello' + v1; will work as the string is a variant that forces the
right operand to be casted to a string variant, letting the concatenation to
apply.
But v2 := VarPythonCreate('hello') + v1; --> Python exception
And v2 := v1 + 'hello'; --> Python exception as hello is converted to a
Python variant and then an addition is beformed between a number and a
string, and Python does not like it!
You can write also:
v1 := VarPythonCreate([1, 2, 3]);
v2 := v1 + VarPythonCreate([4, 5, 6]); --> you'll get: [1, 2, 3, 4, 5, 6]
There are several facility functions that let you test the different types
of Python objects, or that let you import other Python modules, or access
the None object.
Now, you can easily execute a Python script and then access the global vars
of the main module:
GetPythonEngine.ExecString('x = 2');
ShowMessage( MainModule.x ); // will display 2
you can access the sys module with the function SysModule, or you can import
any module with the Import function, that returns the imported module
object.
myModule := Import('myModule');
myModule.foo(bar);
You can create a Python variant with:
v := VarPythonCreate(myPythonObjectPointer);
v := VarPythonCreate(1);
v := VarPythonCreate('abc');
v := VarPythonCreate(3.14);
v := VarPythonCreate(True);
v := VarPythonCreate(VarArrayOf([1, 2, 3]));
v := VarPythonCreate([1, 2, 3]);
v := VarPythonCreate([1, 2, 3], stTuple);
v := None;
v := NewPythonList;
v := NewPythonList(3);
v := NewPythonDict;
you can access to the Python object pointer stored in the variant with
ptr := ExtractPythonObjectFrom(v);
you can test the variant with:
VarIsPython(v)
VarIsSequence(v)
VarIsMapping(v)
VarIsNumber(v)
...
you can check if 2 variants shares the same Python object with
VarIsSame(v1, v2)
you can check if an instance has a class that is or inherits from a class
with:
VarIsInstanceOf(AInstance, AClass)
or you can check the inheritence between 2 classes withL
VarIsSubclassOf(ADerived, AClass)
Note that these 2 functions requires Python 2 or later.
You can check None with:
VarIsNone(v)
or
v = None
or
VarIsSame(v, None)
You can instanciate a class like you would in Python:
v := MainModule.MyClass()
Note, that your class object must the property of a container and can't be
stored in a variant:
cl := MainModule.MyClass; // cl refers to MyClass
v := cl(); // won't work because Delphi refuses to compile it!
You can cast a Python variant to another type any time simply by assigning
it to a variable of the required type or by forcing the cast with:
if Boolean(list.Contains(1)) then ...
So, I hope these explanations will help you understand better the new
approach.
Feel free to give your comments...
Morgan