| CARVIEW |
Class properties are a feature that people coming to Python from other object-oriented languages expect, and expect to be easy. Unfortunately, it’s not. In many cases, you don’t actually want class properties in Python - after all, you can have first class module-level functions as well, you might very well be happier with one of those.
I sometimes see people claim that you can’t do class properties at all in Python, and that’s not right either. It can be done, and it’s not too bad. Read on!
I’m going to assume here that you already know what class (sometimes called “static”) properties are in languages like Java, and that you’re somewhat familiar with Python metaclasses.
To make this feature work, we have to use a metaclass. In this example, we’ll suppose that we want to be able to access a list of all the instances of our class, as well as reference to the most recently created instance. It’s artificial, but it gives us a reason to have both read-only and read-write properties. We define a metaclass, which is again a class that extends type.
class Extent(type):
@property
def extent(self):
ext = getattr(self, '_extent', None)
if ext is None:
self._extent = []
ext = self._extent
return ext
@property
def last_instance(self):
return getattr(self, '_last_instance', None)
@last_instance.setter
def last_instance(self, value):
self._last_instance = value
Please note that if you want to do something like this for real, you may well need to protect access to these shared class properties with synchronization tools like RLock and friends to prevent different threads from overwriting each others’ work willy-nilly.
Next we create a class that uses that metaclass. The syntax is different in Python 2.7, so you may need to adjust if you’re working in an older version.
class Thing(object, metaclass=Extent):
def __init__(self):
self.__class__.extent.append(self)
self.__class__.last_instance = self
Another note for real code: these references (the extent and the last_instance) will keep your object from being garbage collected, so if you actually want to keep extents for your classes, you should do so using something like weakref.
Now we can try out our new class:
>>> t1 = Thing()
>>> t2 = Thing()
>>> Thing.extent
[<__main__.Thing object at 0x101c5d080>, <__main__.Thing object at 0x101c5d2b0>]
>>> Thing.last_instance
<__main__.Thing object at 0x101c5d2b0>
>>> Great, we have what we wanted! There are a couple of things to remember, though:
- Class properties are inherited!
- Class properties are not accessible via instances, only via classes.
Let’s see an example that demonstrates both. Suppose we add a new subclass of Thing called SuperThing:
>>> class SuperThing(Thing):
... @property
... def extent(self):
... return self.__class__.extent
...
>>> s = SuperThing()See how we created a normal extent property that just reads from the class property? So we can now do this:
>>> s.extent
[<__main__.Thing object at 0x101c5d080>, <__main__.Thing object at 0x101c5d2b0>, <__main__.SuperThing object at 0x101c5d2e8>]Whereas if we were to try that with one of the original Things, it wouldn’t work:
>>> t1.extent
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'Thing' object has no attribute 'extent'We can of course still access either one via classes:
>>> t1.__class__.extent
[<__main__.Thing object at 0x101c5d080>, <__main__.Thing object at 0x101c5d2b0>, <__main__.SuperThing object at 0x101c5d2e8>]
>>> s.__class__.extent
[<__main__.Thing object at 0x101c5d080>, <__main__.Thing object at 0x101c5d2b0>, <__main__.SuperThing object at 0x101c5d2e8>]
>>> Also note that the extent for each of these classes is the same, which shows that class properties are inherited.
Did you spot the bug in Thing? It only manifests when we have subclasses like SuperThing. We inherited the __init__ from Thing, which adds each new instance to the extent, and sets last_instance. In this case, self.__class__.extent was already initialized, on Thing, and so we added our SuperThing to the existing list. For last_instance, however, we assigned directly, rather than first reading and appending, as we did with the list property, and so SuperThing.last_instance will be our s, and Thing.last_instance will be our t2. Tread carefully, it’s easy to make a mistake with this kind of thing!
Hopefully this has been a (relatively) simple example of how to build your own class properties, with or without setters.
]]>
As I wrote in my previous post, I recently started working on a Mac. This is a collection of my notes on the problems and surprises I ran into. Maybe it will be useful for you, too.
Tools
In addition to many of the command line tools you may be familiar with from C++ development on Linux, Mac OS X has specialized tools for working with binaries.
otool
The otool utility displays information from binary headers. It works with Mach headers (which are the main thing, on Macs) but also works with other “universal” formats as well. There are many options, but the main one that’s been helpful to me so far has been otool -L <lib>, which tells you the dependencies of the archive, which are essential debugging information for dynamic linking problems. Another useful one is otool -D <lib>, which tells you the install names embedded in a given library.
install_name_tool
This tool allows you to change the install names for a binary. Simple, but important.
By now you must be wondering what install names are.
Install Names
Let us begin with man dyld. There is no dyld command. Nevertheless, it is the dynamic linker for Mac OS X. The man page tells us many interesting things, perhaps chief among which is how dynamic libraries are located at run-time.
When you link an application to a dynamic library, the path to that library is encoded in the application binary. Well, actually, when you build, the library is interrogated to find out where it is supposed to be installed, and that path gets encoded in the application binary. These paths where things are supposed to be installed are known as install names on Mac OS X. Every .dylib has (at least) one. You can use otool -D to find the install name of a .dylib file.
In most cases, the install name will have an absolute path, like this:
> $ otool -D /usr/lib/libiconv.dylib
/usr/lib/libiconv.dylib:
/usr/lib/libiconv.2.dylib
So if you link with /usr/lib/libiconv.dylib, your application will look for /usr/lib/libiconv.2.dylib when it launches. If the .dylib isn’t where it’s expected, the app crashes.
Relative paths
Sometimes a library will have a relative path as its install name. When this happens, dyld will search for a library with that name on several search paths. It’s similar to LD_LIBRARY_PATH in some ways. There are environment variables you can set to control where it searches. There are several, and you should consult man dyld if you want the gory details. One thing that is important to know is that one of these variables, DYLD_FALLBACK_LIBRARY_PATH, has the default value $(HOME)/lib:/usr/local/lib:/lib:/usr/lib. Occasionally you may see advice on the web that recommends symbolic linking a library into your $(HOME)/lib without any further explanation. DYLD_FALLBACK_LIBRARY_PATH is why.
@rpath
If you want to distribute an application that relies on libraries that are not necessarily in known standard places, it is a good idea to use @rpath as part of your install name. This directs dyld to look in a path that is relative to the application binary (or in the case of a library that is dynamically linked to another library, the path is relative to the calling library), rather than looking in the places it normally would. This would allow you to bundle a set of libraries along with your application, and have them be found regardless of where you install the application. There are several other options, such as @executable_path and @loader_path, but @rpath seems to be the right choice in most cases. Again, man dyld has all the details.
An Example: Dionysus
I wanted to build Dionysus, a library for working with persistent homology. I wanted to build the Python bindings.
Linker Errors
After I managed to get through the compile phase, I ran into linker problems, with unresolved symbol errors similar to the ones I mentioned in part 1. However, in this case, changing the compiler didn’t fix the problem. I was getting linking errors trying to link with Boost, which I had already reinstalled on my system from source, using gcc:
export HOMEBREW_CC=gcc-5
export HOMEBREW_CXX=g++-5
brew reinstall --build-from-source boost
brew reinstall --build-from-source boost --with-python3
That wasn’t enough though. I still got linker errors. Googling suggested maybe the problem was that Boost.Python (which Dionysus uses for its Python binding) was linked to a different version of Python than the one I was targeting. This turned out to be a red herring, however. As far as I can tell, Boost.Python doesn’t actually link to Python at all (though it has to be compiled with support for Python 3 if you want that, so there’s certainly a connection, just not a linking one).
The problem actually was that Dionysus wanted the C++ 11 version of Boost, and the default Homebrew version doesn’t have that support. For this, you need:
brew reinstall --build-from-source boost --with-c++11
brew reinstall --build-from-source boost-python --with-c++11
Build-time linking errors resolved, I thought I was home free.
Dynamic Linking Errors
At last, I had built the library. I fired up python2.7, imported the library. It worked! Then I created an instance of one of the classes, and got this:
TypeError: __init__() should return None, not 'NoneType'
After a lot of googling, it turned out that the most likely cause for this was that the Dionysus binding had wound up linked to the wrong Python library. I carefully checked the CMakeCache.txt file and eliminated any occurrences of the wrong Python library, or the wrong Boost library. Still no luck.
A look at otool -L output for the library showed something funny:
> $ otool -L lib_dionysus.dylib
lib_dionysus.dylib:
/Users/me/src/Dionysus/build/bindings/python/lib_dionysus.dylib (compatibility version 0.0.0, current version 0.0.0)
libpython2.7.dylib (compatibility version 2.7.0, current version 2.7.0)
/usr/local/opt/boost-python/lib/libboost_python-mt.dylib (compatibility version 0.0.0, current version 0.0.0)
/usr/local/opt/mpfr/lib/libmpfr.4.dylib (compatibility version 6.0.0, current version 6.3.0)
/usr/local/opt/gmp/lib/libgmp.10.dylib (compatibility version 14.0.0, current version 14.0.0)
Do you notice that all the entries are absolute paths except for the libpython2.7 one? A quick look at the Anaconda libpython2.7.dylib I linked against shows why:
> $ otool -D /Users/me/anaconda2/lib/libpython2.7.dylib
/Users/me/anaconda2/lib/libpython2.7.dylib:
libpython2.7.dylib
So Anaconda’s version of Python instructed the linker to find libpython2.7.dylib by going through the normal dyld process - checking DYLD_LIBRARY_PATH, then DYLD_FALLBACK_LIBRARY_PATH, and so on. Since /usr/lib is on DYLD_FALLBACK_LIBRARY_PATH by default, and the system libpython2.7.dylib is in that directory, Dionysus was getting linked with that one, not with the one that was already in memory in the current process. This led to the strange error I saw.
I was able to use install_name_tool to change the binary to point to the right libpython2.7:
> $ install_name_tool lib_dionysus.dylib -change libpython2.7.dylib /Users/me/anaconda2/lib/libpython2.7.dylib
After this, everything was fine.
Further reading
In addition to man dyld, these are useful:
- Linking and Install Names
- Runpath Dependent Libraries
- Using C++11 in Mac OS X with compiled Boost libraries
- Boost Python init should return None not NoneType
- Relative paths in otool output
I recently switched to a MacBook Pro. I have customers that use Linux and Mac, and I wanted to work in a similar environment. Also recently (a few months before the MacBook) I started working with C++ again after a long hiatus.
I had thought that the Mac, being a Unix, would be relatively close to Linux, and that things I was building for Linux would be much more likely to work there than on Windows. That might still be true, but it turns out that there are several things on Mac that are not obvious, and seriously complicate native code development compared with Linux. These are my notes on those differences and how to deal with them. Hopefully, it may be useful for other migrants to Mac as well.
Xcode
Apple includes something called Xcode. This is apparently a combination of a platform SDK, and an IDE with a user interface similar to iTunes. You have to have it, but you don’t have to use the IDE part. It must be installed from the App Store. Don’t fight it, just install it and move on.
Command line tools
You definitely want the Xcode command line tools. Run:
xcode-select --install
to install them. This will give you git as well.
Brew
There are actually two package managers for Mac OS X, MacPorts and Homebrew, and as a developer you’ll definitely need one of them. I use brew, because other people I know recommended it, and it’s been nice so far. You need it to install libraries and tools that don’t come with the Mac. Most notably gcc, cmake, and so on.
Clang and gcc
Apple ships the clang compiler with Mac OS X, so this is the considered the standard compiler for the platform. This means that some libraries (notably Qt) only support building with clang.
Some C/C++ projects assume (incorrectly) that everybody builds with gcc. For this reason (I guess), Apple did a really odd thing: they ship a gcc executable, which is actually clang in disguise:
> $ gcc
clang: error: no input files
This (I guess) works sometimes, since many flags work the same in both compilers. However, it is deeply confusing and causes problems as well. For example, gcc supports OpenMP, a powerful parallel computing tool, and crucial for the work I’m doing. Recent versions of clang support it as well, but Apple’s fork of clang that ships with Macs does not. So to use OpenMP, I have to have the real gcc. This will cause other problems down the road, we’ll get to them in a bit.
You’ll want to install gcc with brew:
brew install gcc gdb
Since clang is already masquerading as gcc, the Homebrew folks came up with a workaround - the gcc package installs executables called gcc-5 and g++-5 instead of gcc and g++. I added the following in my profile to encourage build systems to use these compilers instead of clang.
export HOMEBREW_CC=gcc-5
export HOMEBREW_CXX=g++-5
export CC=gcc-5
export CXX=g++-5
Note the Homebrew-specific ones. Homebrew generally installs binary, precompiled packages rather than compiling on your machine, but you can pass --build-from-source to it to make it recompile. If you do that, it will honor the HOMEBREW_CC and HOMEBREW_CXX environment variables and use those to do the build.
I also aliased cmake to ensure that cmake uses gcc-5 and g++-5 by default as well:
alias cmake=/usr/local/bin/cmake -DCMAKE_C_COMPILER=$CC -DCMAKE_CXX_COMPILER=$CXX
Compatibility
C++, unlike C, doesn’t specify a binary interface standard. This means that libraries that are compiled with different C++ compilers can have problems interoperating. So there’s that to consider when you use things that were compile with clang (such as anything you download using brew without recompiling) together with things you’ve been building with g++-5.
The most pressing example of this is related to the C++ standard library. There are, on Mac (and elsewhere too I suppose), at least two implementations: libstdc++, and libc++. By default, clang uses libc++ and gcc-5 uses libstdc++. In practice, this means that if you install a C++ based library with brew, you will be able to compile against it with g++-5, but when you get to the link stage, it will fail with lots of missing symbols. If this happens,
brew reinstall --build-from-source <package>
can often fix the problem. However, there are brew packages (e.g. pkg-config) that will fail to compile under g++-5, so there can be cases where this doesn’t work. One example: I was trying to build mxnet directly using the brew package for OpenCV support, and it failed with the aforementioned link errors. I tried to reinstall opencv with --build-from-source with brew, and it started recompiling all of opencv’s (many) dependencies, including pkg-config, which for some reason fails to compile. So in the end I had to pull opencv as well and build it manually, after which mxnet built fine too.
Next time
These were some important things to be aware of when starting to develop in C++ on Macs. In the next installment, we’ll talk about dynamic libraries, install names, and other such loveliness.
]]>The main resource is of course, the book, but these videos and other resources seem useful enough that I want a consolidated list of them for myself. So I thought maybe you might, too.
Group threads that provided these links:
Note that I’m likely to update this page in the future with other resources as I find them, and those links may or may not come from the Groups threads just mentioned.
Useful resources, in no particular order:
- Univalence as a new Principle of Logic - recommended by one writer as perhaps the first video to watch
- Wadler’s Propositions as Types paper - recommended by one writer as a good pairing with chapter 1 of the hott book.
- Robert Harper’s 15-819 Homotopy Type Theory(videos
- Awodey’s Category Theory book
- Awodey’s Category Theory lecture videos (URL is to first video, it’s a series)
- Bristol HoTT project
Related:
- Cubical models in Haskell
- Cubical type theory implementation
- Possible pre-hott intro material - course material, author mentions it may be hard to follow as a standalone resource
- Structural set theory
WordPress was nice for some time. There were lots of nice themes to choose from, installation was easy, things mostly worked. However, the need to keep updating it with security patches, and the need to keep sifting through comment spam, became tiresome.
I always wish I had time to work on something in Haskell, so I decided to put the need for a new blog platform together with that: I’ve switched to Hakyll, at least for now.
This means I generate the site statically, as HTML pages. This means my site requires very little compute power to run, and it’s even easier for me to administer than the WP site was. It also means I don’t have support for comments, which is sad but saves me lots of time sifting through spam. My contact information is on the contact page, please reach out directly if you have comments. Maybe I’ll add some third-party discussion site pointers at a future date. Let me know if you have opinions about that.
Creating the site was fairly simple and took about a day, and took the following steps:
- Download and install stack (not cabal). This is the build tool we’ll use to fetch and install hakyll and its dependencies. Stack is now the preferred build tool for Haskell. They have binaries available for lots of platforms.
- Follow the hakyll installation tutorial, substituting “stack” for “cabal”.
Now you have a (very) bare bones site. I then:
- Added Bootstrap. I basically copied in the HTML from the template and modified it as needed to make it work with Hakyll.
- Added RSS and Atom feeds using the snapshots tutorial
- Changed the landing page to show teasers rather than full articles or just links. The teaser tutorial was very helpful for that. Definitely read this after getting the RSS feeds working, it will make more sense.
- Other minor things like adding favicons
All in all, quite a bit simpler than I expected. The fact that it was so easy to use stack instead of cabal was a nice surprise along the way. The tutorials for Hakyll are really helpful, though there are some details missing, so be patient.
It turns out not to need as much actual Haskell programming time as I expected, but maybe when I need to make something fancier I’ll get to work on an actual Haskell project.
]]>
Foundation said he thought it sounded familiar. He checked his archival database on his laptop, and found a similar call from 20 years earlier, from a whale