Swiftpack.co - Package - PerfectlySoft/Perfect-Python

Perfect - Python 简体中文

Get Involed with Perfect!

Star Perfect On Github Stack Overflow Follow Perfect on Twitter Join the Perfect Slack

Swift 4.0 Platforms OS X | Linux License Apache PerfectlySoft Twitter Slack Status

This project provides an expressway to import Python 2.7 module as a Server Side Swift Library.

This package builds with Swift Package Manager and is part of the Perfect project, but can also run independently.

Ensure you have installed and activated the latest Swift 4.0 tool chain.


A few updates were suggested by Chris Lattner. Thanks, @Chris.

Linux Build Note

Please make sure libpython2.7-dev was installed on Ubuntu 16.04:

$ sudo apt-get install libpython2.7-dev

MacOS Build Note

Please make sure Xcode 9.0 or later version was installed.

Quick Start

Add PerfectPython dependency to your Package.swift

.package(url: "https://github.com/PerfectlySoft/Perfect-Python.git", 
	from: "3.1.0")

// on target section:
	// name: "your project name",
	dependencies: ["PerfectPython"]),

Then import two different libraries into the swift source code:

import PythonAPI
import PerfectPython

Before any python api calls, make sure to initialize the library by calling Py_Initialize() function:


Import Python Modules

Use PyObj class to import python modules. In the following example, a python script /tmp/clstest.py has been imported into the current Swift context:

let pymod = try PyObj(path: "/tmp", import: "clstest")

Access Python Variables

Once imported modules, you can use PyObj.load() function to access a variable value, or using PyObj.save() to store a new value to the current python variable.

For example, if there is a variable called stringVar in a python script:

stringVar = 'Hello, world'

Then you can read its value in such a form:

if let str = pymod.load("stringVar")?.value as? String {
	// will print it out as "Hello, world!"

You can also directly overwrite the value of the same variable:

try pymod.save("stringVar", newValue: "Hola, 🇨🇳🇨🇦!")

NOTE Currently, Perfect-Python supports the following data types between Swift and Python:

Python Type|Swift Type|Remark ----------|---------|------- int|Int| float|Double| str|String| list|[Any]|Recursively dict|[String:Any]|Recursively

For example, you can convert a Swift String to PyObj by: let pystr = "Hello".python() or let pystr = try PyObj(value:"Hello").

To convert a PyObj to a Swift data type, e.g., a String, there are also two available approaches: let str = pystr.value as? String and let str = String(python: pystr).

Call A Python Function

Method PyObj.call() is available to execute function call with arguments. Consider the python code below:

def mymul(num1, num2):
	return num1 * num2

Perfect-Python can wrap this call by its name as a string and the arguments as an array:

if let res = pymod.call("mymul", args: [2,3])?.value as? Int {
	// the result will be 6

Python Object Classes

The same PyObj.load() function helps to access the python class type, however, a following method PyObj.construct() should be called for object instance initialization. This method also supports parameters as an array for python object class construction.

Assume that there is a typical python class called Person, which has two properties name and age, and an object method called intro():

class Person:
	def __init__(self, name, age):
		self.name = name
		self.age = age
	def intro(self):
		return 'Name: ' + self.name + ', Age: ' + str(self.age)

To initialize such a class object in Swift, the first two steps look like:

if let personClass = pymod.load("Person"),
    let person = personClass.construct(["rocky", 24]) {
    // person is now the object instance

Then you can access the properties and class methods as common variables and functions do:

if let name = person.load("name")?.value as? String,
    let age = person.load("age")?.value as? Int,
    let intro = person.call("intro", args: [])?.value as? String {
      print(name, age, intro)


Consider the following python code as you can execute a function as a parameter like x = caller('Hello', callback):

def callback(msg):
    return 'callback: ' + msg

def caller(info, func):
    return func(info)

The equivalent Swift code is nothing special but using the objective callback function as an argument before calling:

if let fun = pymod.load("callback"),
   let result = pymod.call("caller", args: ["Hello", fun]),
   let v = result.value as? String {
   	// it will be "callback: Hello"

Further Information

For more information on the Perfect project, please visit perfect.org.

Now WeChat Subscription is Available (Chinese)


Stars: 25
Help us keep the lights on



3.1.3 - Nov 10, 2017

3.1.2 - Nov 10, 2017

3.1.1 - Nov 9, 2017

3.1.0 - Nov 6, 2017

suggested by Chris Lattner, Nov 3rd, 2017. Also simplified Array conversion by applying this new protocol.

Thanks, @chris

3.0.2 - Nov 3, 2017

In reply Chris' questions of: L34: Fixing non-failable PyObj to String L183: Replacing open class to public as no subclass necessary L223: Will throw error with messages.

Also PyObj has a new property type for python type name.

Original Message from @Chris

why do you make the conversions from Python types to Swift types non-failable? This init I’d expect to be failable, for example:


I understand that you’re wrapping PyObject* with a class to get ARC behavior, but why make it “open”? What does subclassability mean for your PyObj type?


Why do you print errors when you throw, instead of including the details in the error that gets thrown?


Why include a fixed list of supported types, instead of using a protocol to make it extensible?

PerfectPython.swift#L260 (NOT FIXING YET)

What’s this defer doing?