oreilly.comSafari Books Online.Conferences.


AddThis Social Bookmark Button

Introduction to PyObjC

by Bill Bumgarner

Editor's note -- Some programmers see the advantage of combining Python and Objective-C in the same environment, believing that a bridge between the two languages provides tremendous power and advantages to either language. For the Objective-C developer, access to Python provides a rapid application-development solution that's far more efficient than one requiring a compiler. For the Python developer, transparent access to Objective-C would allow the developer's scripts to leverage the full power and elegance of the Mac OS X environment. In this article, Bill Bumgarner shows you how to bring these worlds together.

In this article:

Introduction: Why Bother?

Objective-C is a dynamic and sophisticated object-oriented programming language (OOPL) created through a simple set of syntactic additions to ANSI C, combined with an active runtime library.

Python is also a dynamic and sophisticated OOPL, but is interpreted instead of compiled. Unlike those of Objective-C, Python programs are executed in an interpreter that the developer can interact with directly.

From an object-oriented perspective, both languages render an object model that is very similar. Both languages feature dynamic runtimes that provide live introspection and editing of live objects. Both languages can dynamically dispatch messages at runtime.

That is where the similarity ends.

Objective-C inherits all of the complexity associated with pointer arithmetic, macros, type coercion, manual memory management, and other powerful, highly error-prone features from C. Objective-C also requires an application to be compiled and linked before execution -- not only is this time consuming, but it also requires the application to be relaunched every time a new feature or bug fix is to be tested.

Objective-C, being an extension to pure C, has full access to all of the APIs made available on Mac OS X. This includes the Unix, Core, and Carbon APIs. Many of the Objective-C APIs are simply wrappers around existing system functionality that is available via lower-level, pure C APIs. As such, Objective-C provides an excellent means of wrapping, accessing, and controlling the lower-level layers of the Mac OS X operating system.

"Interpreted" implies slow execution, because the interpreter must parse, then interpret the language, whereas a compiled language is typically compiled and optimized prior to execution.

Python has a built-in optimizing compiler that will compile Python source down to optimized Python Virtual Machine instructions. The resulting compiled source is stored in parallel to the original source in the filesystem. Upon loading a Python source file, the interpreter automatically checks to see if an up-to-date, already-compiled version is available and will either use the compiled version directly or recompile the source file, if necessary.

Python is a fully interactively-interpreted language. As with a shell script, Python source is read and executed by an interpreter. The developer can interact directly with the interpreter, thus providing debugging and experimentation capabilities not available to compiled languages.

Python provides a wealth of APIs in the standard installation, including classes and modules that provide regular expressions, email/web/network clients and servers, web services, interaction with the underlying OS, XML services, and a myriad of other features. Many third-party modules are available, including advanced math libraries, image manipulation packages, and entire content management/publishing systems.

Clearly, a bridge between the two languages would provide tremendous power and advantages to either language.

For the Objective-C developer, access to Python would provide a rapid application development solution that is far more efficient than one requiring a compiler. The casual flexibility and rapid turnaround of Python are compelling prototyping capabilities. Python also offers many features that are not currently available in Objective-C at all, or are only available through the use of a complex functional API found in the Core frameworks.

For example, Python offers very fast object persistence and simple database access through the anydbm module. Creating an object store on disk that contains string keys and arbitrary object values is as simple as creating an anydbm object against a path, writing the key/value pairs into the anydbm object, and closing the anydbm object. The key/value pairs are "written" to the database file using the standard Python dictionary assignment syntax (anOpenDB['key'] = valueObject).

For the Python developer, transparent access to Objective-C would allow the developer's scripts to leverage the full power and elegance of the Mac OS X environment. An example of using the Objective-C APIs from Python to create a simple, yet powerful, tool can be found later in this article.

Bridging the Gap: the PyObjC Project

The charter of the PyObjC project is to provide a feature-complete bridge between the Python and Objective-C programming languages. With PyObjC, it is possible to transparently message Objective-C objects from Python and Python objects from Objective-C. The bridge also allows Objective-C classes to be subclassed within Python.

Related Reading

Objective-C Pocket Reference
By Andrew M. Duncan

Since Objective-C has its roots in C, there are a tremendous number of enumerated types, global variables (typically, identifiers for notifications or exceptions), and complex types found throughout the various frameworks. The PyObjC bridge automatically wraps these types in a fashion appropriate to the Python environment.

Installing the PyObjC Bridge

The PyObjC installer package is targeted to the OS X build of Python 2.2 that is included with Mac OS X 10.2. The installer package includes Project Builder templates for building Cocoa applications in Python, and a number of examples.

The PyObjC bridge has been tested with Python 2.2 and 2.3 in multiple forms, including the MacPython build, the Fink build, and directly from the Python source tree. In these cases, the PyObjC bridge must be built from the source. The source is available as a separate download or as a part of the disk image that includes the installer package.

For example, to build the PyObjC bridge from source to be installed into the Fink build of Python (assuming Fink is installed in the default location of /sw/):

% cd pyobjc
% /sw/bin/python install

Once installed, loading the bridge into the Python interpreter is simply a matter of importing the appropriate packages:

% python
Python 2.2 (#1, 07/14/02, 23:25:09) 
[GCC Apple cpp-precomp 6.14] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> from objc import *      
>>> from Foundation import *
>>> type(NSArray)
<type 'objc_class'>
>>> type(NSUTF8StringEncoding)
<type 'int'>

Differences in Syntax

For as much as Python and Objective-C are similar in the object model presented by the languages, their syntaxes are radically different. Objective-C derives its syntax from SmallTalk (Squeak is the most active modern derivative of SmallTalk).

In particular, Objective-C method invocation syntax is as follows:

[sharedWorkspace getInfoForFile: aFilePath application: &appPath type: &type];

That is, invoke the getInfoForFile:application:type: method of the sharedWorkspace object. It is important to note that the name of the method is exactly getInfoForFile:application:type: and not getInfoForFile: with the named arguments application: and type:. This is a common source of confusion; Objective-C does not have named arguments!

Python uses a method invocation syntax that is similar to Java or Perl. For example, the following Python code invokes the send_header method on the httpClient object, passing two arguments to the method:

httpClient.send_header("Content-type", ctype)

When invoking Objective-C methods from Python code using the PyObjC bridge, the developer uses Python-style method invocation syntax. Because Python does not support ":" characters in method names, each ":" is simply replaced by a "_" character when converting.

What follows is a session with the Python interpreter that demonstrates loading of the PyObjC bridge and use of the NSWorkspace class to determine the type of a particular document and the application that the system would use to open the document. Objective-C method invocations are emphasized.

% python
Python 2.2 (#1, 07/14/02, 23:25:09)
[GCC Apple cpp-precomp 6.14] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> from AppKit import *
>>> sharedWorkspace = NSWorkspace.sharedWorkspace()
>>> sharedWorkspace
<NSWorkspace: 0x109d50>
>>> import os.path
>>> pathToQuery = os.path.expanduser("~/Desktop/PyXML.pdf")
>>> sharedWorkspace.getInfoForFile_application_type_(pathToQuery)
(1, '/Applications/', 'pdf')

Note that the method getInfoForFile:application:type: normally takes three arguments. The last two arguments are pointers to pointers to instances of NSString that are set by the method to point to the application path and file type, respectively. Specifically:

NSString *appPath, *type;
[sharedWorkspace getInforForFile:aPath application:&appPath type:&type];

The PyObjC bridge does not directly support pointer types, as there is no real analogy to pointer types on the Python side of the bridge. Instead, the bridge automatically bridges methods of the style of getInfoForFile:application:type: by returning the pass-by-pointer-reference arguments in a Python tuple. The first element is the normal return value of the method (a boolean, in this case) and the rest of the elements are the values that would normally have been set in the pass-by-pointer-reference arguments.

When Bridging Methods Are Not Enough

Just about every OOPL provides classes that encapsulate Strings, Arrays and Hashtables. The names may be different -- Arrays are often called Lists or Vectors, and Hashtables frequently appear as Maps or Dictionaries.


The current implementation is naive; every string is always converted. The implementation will be vastly improved over time. In particular, Python strings can be encapsulated in a subclass of NSString that implements the appropriate primitive methods of the NSString class cluster. With the release of Python 2.3, it will be possible to do the same kind of encapsulation of Objective-C NSString instances in Python. This will eliminate the cost of conversion across the bridge.

Strings are used throughout most languages, both OOPL and non-object-oriented, as everything from hardwired identifiers, exception names, notification identifiers, I/O buffers, containers for composing output, to a myriad of other roles. As such, performance is often critically dependent on the implementation of the String class or Strings classes.

Both Python and Objective-C have classes that contain strings of characters. Because both languages so heavily depend on their own native implementation of the String classes, the PyObjc bridge automatically converts Python strings to Objective-C NSStrings and vice versa.

Arrays and Dictionaries

Many languages provide direct access and manipulation of List and Hashtable types through syntactic features of the language. For example, Python arrays and dictionaries are manipulated as follows:

% python
Python 2.2 (#1, 07/14/02, 23:25:09) 
[GCC Apple cpp-precomp 6.14] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> aDictionary = {}
>>> aDictionary['key'] = 'value' 
>>> aDictionary.keys()
>>> aList = ['apple', 'orange', 'banana']
>>> aList
['apple', 'orange', 'banana']
>>> aList[2]

Objective-C treats dictionarys and arrays as ordinary objects. That is, access and manipulation of their contents is entirely via the APIs of the NSDictionary and NSArray class clusters.

In either language, it is easy to make a container class of one behave just like the corresponding container class of the other.

For Objective-C, the developer simply creates a subclass of NSArray or NSDictionary and implements the appropriate primitive methods.

The process is similar in Python. In a class intended to behave like a dictionary or list, the developer implements a set of methods that provide the appropriate subset of dictionary-like or list-like behavior.

By leveraging the aforementioned features of each language, the PyObjC bridge is able to provide (relatively) transparent access to dictionary and list objects from either language on the either side of the bridge. For example, an NSDictionary can be used in Python like any other mapping type. Similarly, a Python list will arrive on the Objective-C side of the bridge as an instance of NSArray, and NSArray instances bridged into Python can be sliced using the normal Python slice operator.

Some examples (Obj-C method calls emphasized):

% python
Python 2.2 (#1, 07/14/02, 23:25:09)
[GCC Apple cpp-precomp 6.14] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> from Foundation import *
>>> standardUserDefaults = NSUserDefaults.standardUserDefaults()
>>> persistentDomains = standardUserDefaults.persistentDomainNames()
>>> type(persistentDomains)
<objective-c class NSCFArray at 0x1036a0>
>>> persistentDomains.objectAtIndex_(21)
>>> persistentDomains[21]
>>> aDomain = standardUserDefaults.persistentDomainForName_(persistentDomains[21])
>>> aDomain.keys()
    "NSToolbar Configuration DiskCopy",
    ... numerous keys deleted ...
>>> aDomain.objectForKey_("NSToolbar Configuration DiskCopy")
    "TB Display Mode" = 1;
    "TB Size Mode" = 1;
>>> aDomain["NSToolbar Configuration DiskCopy"]
    "TB Display Mode" = 1;
    "TB Size Mode" = 1;
>>> persistentDomains[20:25]
>>> mutableArray = NSMutableArray.arrayWithArray_(persistentDomains)
>>> mutableArray[20:30]
>>> del mutableArray[30:]
>>> del mutableArray[0:20]
>>> mutableArray
>>> mutableArray[0:5] = ['a','b','c']
>>> mutableArray


The PyObjC bridge also supports subclassing of Objective-C classes from within Python. Technically, subclassing of a Python class from Objective-C is also possible, but isn't practical in that it requires the creation of a stub class to link against -- a difficult task for which there are alternative implementation patterns that are far more effective.

Creating a subclass of an Objective-C class in Python works exactly like subclassing any other class in Python:

% python
Python 2.2 (#1, 07/14/02, 23:25:09) 
[GCC Apple cpp-precomp 6.14] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> from Foundation import *
>>> class MyClass(NSObject):
...   def doSomething_(self, aThing):
...     print "Did something to: %s" % aThing
...   def description(self):
...     superDescription = super(MyClass, self).description()
...     return superDescription + " (Pythonic subclass)"
>>> myInstance = MyClass.alloc().init()
>>> myInstance.doSomething_("that")
Did something to: that
>>> myInstance.performSelector_withObject_("doSomething:", "that")
Did something to: that
>>> myInstance.description()
'<MyClass: 0x843440> (Pythonic subclass)'

The syntax for invoking methods on super -- super(MyClass, self).method() -- is standard to Python, and the result of Python's support for multiple inheritance. It allows the developer to pick exactly which superclass to start the search for the method.

Note that when passing a method name as an argument to a method such as performSelector:withObject:, the PyObjC bridge uses the Objective-C naming conventions for the method name and the SEL is simply passed as regular string.

Bringing It All Together

The PyObjC bridge allows for the development of full featured applications that can leverage the power of Objective-C, Foundation, Cocoa, and any other framework's APIs.

What follows is a script that uses the NSWorkspace API to listen for volume mount notifications. When a volume is mounted, the script scans the top-level directory on the volume and automatically opens any files that look like they may be a 'ReadMe' document, release notes, or about the contents of the volume.

It uses the regular expression and directory traversal APIs found in Python to determine if a file is a match.

import sys
import os
import re

from AppKit import NSWorkspace, NSWorkspaceDidMountNotification
from Foundation import *

workspace = NSWorkspace.sharedWorkspace()
notificationCenter = workspace.notificationCenter()

readTheseFiles = re.compile('(.*reads*me.*|.*release.*note.*|^about.*)', re.I)

class NotificationHandler(NSObject):
    def handleMountNotification_(self, aNotification):
        path = aNotification.userInfo()['NSDevicePath']
        for aFile in os.listdir(path):
            if readTheseFiles.match(aFile):
                fullPath = os.path.join(path, aFile)
                success, app, None = workspace.getInfoForFile_application_type_(fullPath)
                if not success:
                    NSLog("Failed to find application to open file %s" % fullPath)
                workspace.openFile_withApplication_(fullPath, app)
notificationHandler =

NSLog("Listening for mount notifications....")
NSRunLoop.currentRunLoop().run() # never exits

The above script appears as the example within the PyObjC CVS repository (it did not ship with version 0.8).

Related Reading

Python in a Nutshell
By Alex Martelli

Memory Management

Anyone familiar with Objective-C's retain count-based memory management scheme is likely surprised that memory management has received so little attention in this article. That is because the PyObjC bridge attempts to make memory management as automatic as possible. Whenever an Objective-C object has been assigned to a variable on the Python side of the bridge, the bridge retains the object. As soon as that variable is no longer in scope, or is assigned another value (including "None"), the bridge releases the object.

If the retain count is increased by a method invocation; it is the developer's responsibility to ensure that the object is released by invoking release() or autorelease().

For example, if NSMutableData is instantiated by the expressions d = NSMutableData.alloc().init() or d =, the retain count will have been increased by 2. Once for the assignemnt to the variable d within Python, and once by the invocation of alloc() or new(). In this case, the developer must eventually invoke d.autorelease() or d.release(). The copy() and mutableCopy() methods also return retained instances.

It is not safe to assume that the object instantiated by invoking alloc() will be the same object returned by the initializer -- typically, init() -- of that object. The class clusters will often return a different instance from init(), depending on their internal implementation. This is an issue that Objective-C developers need to be careful of, as well.

>>> from Foundation import *
>>> a = NSArray.alloc()
>>> print a
<NSPlaceholderArray objective-c instance 0x1133a0>
>>> a.init()
<NSCFArray objective-c instance 0x254640>


Objective-C and Python are powerful OOPLs that offer similar features within the runtime and object model perpetuated by each language. Each has strengths and weaknesses that make them more appropriate for some tasks than for others.

Fortunately, the strengths and weaknesses of each language is such that melding the two together via the PyObjC bridge results in a combination that is powerful while remaining flexible.

With PyObjC, it is possible to build entire applications using Python as the language of implementation while fully leveraging the numerous Objective-C APIs provided by Mac OS X. An entire application can be prototyped very rapidly using Python, PyObjC, and Cocoa. If any part of the end result does not perform as required, it can easily be ported to Objective-C to gain the additional performance of a fully-compiled language.

The next article in this series will focus on the integration of Python and Cocoa through the PyObjC bridge. In that article, a full-featured web services application will be developed using Python as the language of implementation, Project Builder and Interface Builder as the IDE, Cocoa as the GUI API, and Python's XML-RPC library as the web services API.

Authors note: Chris Beauvois, Ronald Oussoren, Just van Rossum, and Ben Holt acted as technical and content editors. Thank you. Your input was and continues to be appreciated.

Bill Bumgarner is a founder and the CTO of CodeFab, Inc.

Return to the Mac DevCenter.