View on TensorFlow.org | View source on GitHub |
Swift For TensorFlow supports Python interoperability.
You can import Python modules from Swift, call Python functions, and convert values between Swift and Python.
import PythonKit
print(Python.version)
3.6.9 (default, Oct 8 2020, 12:12:24) [GCC 8.4.0]
Setting the Python version
By default, when you import Python
, Swift searches system library paths for the newest version of Python installed.
To use a specific Python installation, set the PYTHON_LIBRARY
environment variable to the libpython
shared library provided by the installation. For example:
export PYTHON_LIBRARY="~/anaconda3/lib/libpython3.7m.so"
The exact filename will differ across Python environments and platforms.
Alternatively, you can set the PYTHON_VERSION
environment variable, which instructs Swift to search system library paths for a matching Python version. Note that PYTHON_LIBRARY
takes precedence over PYTHON_VERSION
.
In code, you can also call the PythonLibrary.useVersion
function, which is equivalent to setting PYTHON_VERSION
.
// PythonLibrary.useVersion(2)
// PythonLibrary.useVersion(3, 7)
Note: you should run PythonLibrary.useVersion
right after import Python
, before calling any Python code. It cannot be used to dynamically switch Python versions.
Set PYTHON_LOADER_LOGGING=1
to see debug output for Python library loading.
Basics
In Swift, PythonObject
represents an object from Python.
All Python APIs use and return PythonObject
instances.
Basic types in Swift (like numbers and arrays) are convertible to PythonObject
. In some cases (for literals and functions taking PythonConvertible
arguments), conversion happens implicitly. To explicitly cast a Swift value to PythonObject
, use the PythonObject
initializer.
PythonObject
defines many standard operations, including numeric operations, indexing, and iteration.
// Convert standard Swift types to Python.
let pythonInt: PythonObject = 1
let pythonFloat: PythonObject = 3.0
let pythonString: PythonObject = "Hello Python!"
let pythonRange: PythonObject = PythonObject(5..<10)
let pythonArray: PythonObject = [1, 2, 3, 4]
let pythonDict: PythonObject = ["foo": [0], "bar": [1, 2, 3]]
// Perform standard operations on Python objects.
print(pythonInt + pythonFloat)
print(pythonString[0..<6])
print(pythonRange)
print(pythonArray[2])
print(pythonDict["bar"])
4.0 Hello slice(5, 10, None) 3 [1, 2, 3]
// Convert Python objects back to Swift.
let int = Int(pythonInt)!
let float = Float(pythonFloat)!
let string = String(pythonString)!
let range = Range<Int>(pythonRange)!
let array: [Int] = Array(pythonArray)!
let dict: [String: [Int]] = Dictionary(pythonDict)!
// Perform standard operations.
// Outputs are the same as Python!
print(Float(int) + float)
print(string.prefix(6))
print(range)
print(array[2])
print(dict["bar"]!)
4.0 Hello 5..<10 3 [1, 2, 3]
PythonObject
defines conformances to many standard Swift protocols:
Equatable
Comparable
Hashable
SignedNumeric
Strideable
MutableCollection
- All of the
ExpressibleBy_Literal
protocols
Note that these conformances are not type-safe: crashes will occur if you attempt to use protocol functionality from an incompatible PythonObject
instance.
let one: PythonObject = 1
print(one == one)
print(one < one)
print(one + one)
let array: PythonObject = [1, 2, 3]
for (i, x) in array.enumerated() {
print(i, x)
}
True False 2 0 1 1 2 2 3
To convert tuples from Python to Swift, you must statically know the arity of the tuple.
Call one of the following instance methods:
PythonObject.tuple2
PythonObject.tuple3
PythonObject.tuple4
let pythonTuple = Python.tuple([1, 2, 3])
print(pythonTuple, Python.len(pythonTuple))
// Convert to Swift.
let tuple = pythonTuple.tuple3
print(tuple)
(1, 2, 3) 3 (1, 2, 3)
Python builtins
Access Python builtins via the global Python
interface.
// `Python.builtins` is a dictionary of all Python builtins.
_ = Python.builtins
// Try some Python builtins.
print(Python.type(1))
print(Python.len([1, 2, 3]))
print(Python.sum([1, 2, 3]))
<class 'int'> 3 6
Importing Python modules
Use Python.import
to import a Python module. It works like the import
keyword in Python
.
let np = Python.import("numpy")
print(np)
let zeros = np.ones([2, 3])
print(zeros)
<module 'numpy' from '/tmpfs/src/tf_docs_env/lib/python3.6/site-packages/numpy/__init__.py'> [[1. 1. 1.] [1. 1. 1.]]
Use the throwing function Python.attemptImport
to perform safe importing.
let maybeModule = try? Python.attemptImport("nonexistent_module")
print(maybeModule)
nil
Conversion with numpy.ndarray
The following Swift types can be converted to and from numpy.ndarray
:
Array<Element>
ShapedArray<Scalar>
Tensor<Scalar>
Conversion succeeds only if the dtype
of the numpy.ndarray
is compatible with the Element
or Scalar
generic parameter type.
For Array
, conversion from numpy
succeeds only if the numpy.ndarray
is 1-D.
import TensorFlow
let numpyArray = np.ones([4], dtype: np.float32)
print("Swift type:", type(of: numpyArray))
print("Python type:", Python.type(numpyArray))
print(numpyArray.shape)
Swift type: PythonObject Python type: <class 'numpy.ndarray'> (4,)
// Examples of converting `numpy.ndarray` to Swift types.
let array: [Float] = Array(numpy: numpyArray)!
let shapedArray = ShapedArray<Float>(numpy: numpyArray)!
let tensor = Tensor<Float>(numpy: numpyArray)!
// Examples of converting Swift types to `numpy.ndarray`.
print(array.makeNumpyArray())
print(shapedArray.makeNumpyArray())
print(tensor.makeNumpyArray())
// Examples with different dtypes.
let doubleArray: [Double] = Array(numpy: np.ones([3], dtype: np.float))!
let intTensor = Tensor<Int32>(numpy: np.ones([2, 3], dtype: np.int32))!
[1. 1. 1. 1.] [1. 1. 1. 1.] [1. 1. 1. 1.]
Displaying images
You can display images in-line using matplotlib
, just like in Python notebooks.
// This cell is here to display plots inside a Jupyter Notebook.
// Do not copy it into another environment.
%include "EnableIPythonDisplay.swift"
print(IPythonDisplay.shell.enable_matplotlib("inline"))
('inline', 'module://ipykernel.pylab.backend_inline')
let np = Python.import("numpy")
let plt = Python.import("matplotlib.pyplot")
let time = np.arange(0, 10, 0.01)
let amplitude = np.exp(-0.1 * time)
let position = amplitude * np.sin(3 * time)
plt.figure(figsize: [15, 10])
plt.plot(time, position)
plt.plot(time, amplitude)
plt.plot(time, -amplitude)
plt.xlabel("Time (s)")
plt.ylabel("Position (m)")
plt.title("Oscillations")
plt.show()
Use `print()` to show values.