6 ms·
Making C and Python Talk to Each Other
- nottorp 1y agoOr this? https://docs.python.org/3/extending/embedding.html https://docs.python.org/3/extending/embedding.html
- muragekibicho 1y agoLots of articles focus on Cython and optimizing Python using C code. This article is about embedding Python scripts inside a C codebase
- SandmanDP 1y agoI’ve been curious, what are the motivations for most projects to use Lua for enabling scripting in C over this? Is the concern around including an entire Python interpreter in a project and Lua is lighter?
- 90s_dev 1y agoNetwork effect.
- bandoti 1y agoLua is much lighter but the key is that it’s probably one of the easiest things to integrate (just copy the sources/includes and add them to build it’ll work)—like a “header only” kind of vibe. But, you can strip down a minimal Python build and statically compile it without too much difficulty. I tend to prefer Tcl because it has what I feel the perfect amount of functionality by default with a relatively small size. Tcl also has the better C APIs of the bunch if you’re working more in C. Lua is very “pushy” and “poppy” due to its stack-based approach, but that can be fun too if you enjoy programming RPN calculators haha :)
- crote 1y agoLua is absolutely trivial to isolate. As the embedder, you have complete control over what the interpreter and VM are doing. Don't want your Lua scripts to have file access? Don't hook up those functions and you're done. Want to prevent against endless loops? Tell the VM to stop after 10.000 instructions. Want to limit the amount of memory a script can use? Absolutely trivial. This makes Lua very attractive for things like game development. You can run untrusted addon code without any worry that it'll be able to mess up the game - or the rest of the system. Doing the same with Python is a lot harder. Python is designed first and foremost to run on its own. If you embed Python you are essentially running it besides your own code, with a bunch of hooks in both directions. Running hostile Python code? Probably not a good idea.
- OskarS 1y agoAnother thing to mention is that until very recently (Python 3.12, I think?) every interpreter in the address space shared a lot of global state, including most importantly the GIL. For my area (audio plugins) that made Python a non-starter for embedding, while Lua works great. I agree though: biggest reason is probably the C API. Lua's is so simple to embed and to integrate with your code-base compared to Python. The language is also optimized for "quick compiling", and it's also very lightweight. These days, however, one might argue that you gain so much from embedding either Python or JavaScript, it might be worth the extra pain on the C/C++ side.
- spacechild1 1y agoPeople already mentioned that Lua is very lightweight and easy to integrate. It's also significantly faster than Python. (I'm not even talking about LuaJIT.) Another big reason: the Lua interpreter does not have any global variables (and therefore also no GIL) so you can have multiple interpreters that are completely independent from each other.
- Derbasti 1y agoI've done both. Let me tell you, embedding Lua into a C program is magnitudes easier than embedding Python. The main reason is the endless fragility of Python's garbage collector. To be clear, the API is trying to be as helpful as possible, but it's still a whole bunch of complexity that's easy to mess up. Incidentally, this discussion is left out in the linked article, which makes it less than useless. In my experience with many a third party C/Py interface, data leaks are incredibly common in such code. Lua of course also has a garbage collector, but it essentially only knows two types of values: POD, and tables, with only the latter needing much consideration. The interaction model is based on a stack-based virtual machine, which is more complex than Python's full-function abstraction, but conveniently hides most of the garbage collector complexity. So long as you're just reshuffling things on the stack (i.e. most of the time), you don't need to worry about the garbage collector at all.
- jebarker 1y agoLots of people argue that AI R&D is currently done in Python because of the benefits of the rich library ecosystem. This makes me realize that's actually a poor reason for everything to be in Python since the actually useful libraries for things like visualization could easily be called from lower level languages if they're off the hot path.
- giancarlostoro 1y agoI think its more than just because of the available libraries. I think that industry has just predominantly preferred Python. Python is a really rich modern language, it might be quirky, but so is every single language you can name. Nothing is quite as quirky as JavaScript though, maybe VB6 but that's mostly dead, though slightly lingering. Mind you I've programmed in all the mentioned languages. ;)
- whattheheckheck 1y agoIt's the ease of distribution of packages and big functionality being a pip install away
- kstrauser 1y agoThat's the killer feature. Whatever it is you want to do, there's almost certainly a package for it. The joke is that Python's the second best language for everything. It's not the best for web backends, but it's pretty great. It's not the best for data analysis, but it's pretty great. It's not the best at security tooling, but it's pretty great. And it probably is the best language for doing all three of those things in one project.
- scj 1y agoWouldn't it be nice if popular libraries could export to .so files so the best language for a task could use the bits & pieces it needed without a programmer needing to know python (and possibly C)? Were I to write a scripting language, trivial export to .so files would be a primary design goal.
- a_t48 1y agoUseful, I’m going to be doing something similar w/C++ soon.
- deleted 1y ago[deleted]
- kvemkon 1y agoOnce I needed to implement a simple python plugin engine in a C/C++ software, I've been successfully using the official guide [1]. [1] https://docs.python.org/3/extending/embedding.html https://docs.python.org/3/extending/embedding.html
- eth_hack77 1y agoThanks a lot for the article. Here's a QQ: did you measure the time of some basic operations python vs C? (e.g. if I do a loop of 10 billion iterations, just dividing numbers in C and do the same in python, and then import these operations into one another as libraries, does anything change?) I'm a beginner engineer so please don't judge me if my question is not making perfect sense.
- bdbenton5255 1y agoC is many magnitudes faster than Python and you can measure this using nested conditionals. Python is built for a higher level of abstraction and this comes at the cost of speed. It is what makes it very natural and human-like to write in.
- xandrius 1y agoSyntax has nothing to do with the speed of the language: python could be "natural" and "human-like" while being much faster and also "unnatural" and "inhuman" while being slower.
- throwaway314155 1y agoLanguage abstractions that are not "zero-cost" inevitably lead to worse performance. Python has many such abstractions designed to improve developer experience. I think that's all the person you're responding to meant.
- bdbenton5255 1y agoYes, thank you.
- vlovich123 1y agoIt’s not mainly the syntax though although it has a marginal effect. It’s partially the lack of typing information (which is syntax) but mostly that it runs interpreted. Pypy is significantly faster because it applies JIT to generate machine code directly to represent the Python code and it’s significantly faster in most cases. Another huge cost is in multi-threaded scenarios where Python has the GIL (even in single threaded there’s a cost) which is an architectural and not syntactic decision. For example, Python has a drastically simpler syntax in some ways than C++ (ignoring the borrow annotations). In many ways it can look like Python code. Yet its performance is the same as c++ because it’s AOT compiled and has an explicit type system to support that. TLDR: most of python’s slowness is not syntactic but architectural design decisions about how to run the code which is why alternate Python implementations (IronPython, PyPy) typically run faster.
- rossant 1y agoMy visualization library [1] is written in C and exposes a visualization API in C. It is packaged as a Python wheel using auto-generated ctypes bindings, which includes the shared library (so, dylib, or dll) and a few dependencies. This setup works very well, with no need to compile against each Python version. I only need to build it for the supported platforms, which is handled automatically by GitHub Actions. The library is designed to minimize the number of C calls, making the ctypes overhead negligible in practice. [1] https://datoviz.org/ https://datoviz.org/
- Grimblewald 1y agoThis looks awesome, thanks for sharing - do you have any info on how it compares to matplotlib in terms of plotting speed? (even just rough estimates)
- fadesibert 1y agoA quick inspection of the article suggests there's a difference of intent. <snip> Datoviz is a relatively low-level visualization library. It focuses on rendering visual primitives like points, lines, images, and meshes — efficiently and interactively. Unlike libraries such as Matplotlib, Datoviz does not provide high-level plotting functions like plt.plot(), plt.scatter(), or plt.imshow(). Its goal is not to replace plotting libraries, but to serve as a powerful rendering backend for scientific graphics. </snip>
- rossant 1y agoYes, although there isn't much to do to go from Datoviz to simple scientific plots like scatter plots or polylines with axes. It's just a few lines of code. I should probably clarify the documentation.
- rossant 1y agoGood idea, I should do benchmarks on simple plots. It's orders of magnitude faster. Above 60 FPS on hundreds of thousands to even millions of points depending on the type of plot, on common hardware.
- brcmthrowaway 1y agoHow does this compare to pybind11?
- dgrunwald 1y agoThe article is using the Python C API directly. pybind11 is a C++ wrapper that makes the Python API more friendly to use from C++ (e.g. smart pointers instead of manual reference counting)
- nubinetwork 1y agoIsn't this the whole point to cffi and cython?
- softwaredoug 1y agoDefinitely though Cython is a layer of abstraction that might feel like Python has all kinds of weirdness you might as well write in a better understood language like C.
- pjmlp 1y agoAs someone that routinely kind of talks C down, yet knows it well enough since around 1990, I will acknowledge that for many in the current generation entering the workforce that is definetly not a given, especially the recent trend to treat C as if it was a scripting language, tryingt to avoid learning about the whole compiler toolchain with stuff like header only libraries.
- HexDecOctBin 1y ago> avoid learning about the whole compiler toolchain with stuff like header only libraries. Compromises needed to compile code on PDP-11, etc. are not some sacred immutable facts about how toolchain ought to work. One could learn everything about the crufty compiler toolchains and decide that it's all just a layering of over-abstracted nonsense over obsolete nonsense.
- pjmlp 1y agoCompiler toolchains are hardly compromises needed to compile code on a PDP-11. I do agree that UNIX compilation model is not the best example around, though. Nonetheless, most languages on UNIX like OSes will for the most part follow the same model, as they need to fit into the ecosystem, as otherwise there is always an impedance mismatch that will prevent their adoption in some cases. That is how you get modern languages like any wanabee C or C++ replacement using the same compiler toolchains and linkers, instead of compiler based build and linker systems, as languages not born into the UNIX culture. Thus pretending systems programming languages are like scripting languages will hardly help.
- dexzod 1y agoThe title of the article is misleading. Making C and python talk to each other implies, calling python from C and calling C from python. The article only covers the former.
- hughw 1y agoI realize I'm talking about C++ not C, but coincidentally just today I ported our 7 year old library's Swig/Python interface to nanobind. What a fragile c9k Swig has been all these years (don't touch it!) and the nanobind transformation is so refreshing and clean, and lots of type information suddenly available to Python programs. One day of effort and our tests all pass, and now nanobind seems able to allow us to improve the ergonomics (from the Python pov) of our lib.
- rrdharan 1y agoWhat is c9k short for?
- spacechild1 1y agoI guess it's "clusterfuck" :)
- DonHopkins 1y agoA c9k problem is much worse than a y2k problem (yuck).
- hugs 1y agoThis is one of the "killer apps" for Nim. Nim makes makes it easy to wrap C and easy to talk to Python (via Nimpy).
- vkoskiv 1y agoI did a lot of this for my raytracer, c-ray [1]. Originally it was just a self-contained C program, but I got tired of writing buggy and limited asset import/export code, so eventually I put together a minimal public C API [2] that I then wrapped with CPython bindings [3] and some additional python code [4] to expose a more 'pythonic' API. It's all still a WIP, but it has already allowed me to write a Blender plugin [5], so now I can play around with my renderer directly in Blender, and test with more complex scenes others have made. Fun project, and it's really cool to see my little renderer in the interactive viewport in Blender, but I have also learned that I don't particularly enjoy working with non-trivial amounts of Python code. [1] https://github.com/vkoskiv/c-ray https://github.com/vkoskiv/c-ray [2] https://github.com/vkoskiv/c-ray/blob/51a742b2ee4d0b570975cd99205708bbae58097c/include/c-ray/c-ray.h https://github.com/vkoskiv/c-ray/blob/51a742b2ee4d0b570975cd... [3] https://github.com/vkoskiv/c-ray/tree/51a742b2ee4d0b570975cd99205708bbae58097c/bindings/python https://github.com/vkoskiv/c-ray/tree/51a742b2ee4d0b570975cd... [4] https://github.com/vkoskiv/c-ray/tree/51a742b2ee4d0b570975cd99205708bbae58097c/bindings/python/lib https://github.com/vkoskiv/c-ray/tree/51a742b2ee4d0b570975cd... [5] https://github.com/vkoskiv/c-ray/tree/51a742b2ee4d0b570975cd99205708bbae58097c/integrations/blender https://github.com/vkoskiv/c-ray/tree/51a742b2ee4d0b570975cd...
- UncleEntity 1y agoJust a little nitpick but using Py_BuildValue is much better to generate PyObjects from C if you are doing more than one of them instead of populating a list and converting it to a tuple to pass to the python function. My general rule (when doing the opposite of passing C values to python as part of an extension) is to use the dedicated function, like PyLong_FromLong, when there is a single return and Py_BuildValue (with its format string for automagic conversion) when the function returns a tuple. Oh, and if you are checking your return values from python (you are, right?) using Py_XDECREF isn't all that good of an idea since it will mask some flawed logic. I pretty much only use it when a PyObject pointer could validly be NULL (like with an optional function argument) and I'm cleaning up right before throwing an exception. Tracking python reference counts is a whole blog post in itself since some functions steal references and others don't and if you get it wrong you can easily crash the interpreter and/or create memory leaks.