Skip to content

dankeyy/swap.py

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

44 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation

swap.py

cpp-std::swap-like swap function for cpython

🔴 Caveats 🔴

This implementation heavily relies on c-api; will not work for other implementations of Python.

Tested as working for versions 2.5 upto 3.11.
But - if for some reason you spread your swap call over multiple lines,
you would need to either use a version newer than 3.7 (old parser issue) or one-line it.

What

from swap import swap

def f():
    a = 'foo'
    b = 'bar'
    
    swap(a, b)
    
    print(f'{a = }\n{b = }')
    
f() 
$ python test.py
a = 'bar'
b = 'foo'
An example without a function call wouldn't show as much because globals are easier to carve through

You know you could just do a, b = b, a right?

Right

Why

Why not

How

Swapping the two variables entry at the frame's locals.

But I thought locals is only a copy and you can't really change a function's locals at runtime

When there's a will, there's a way. That way is c-api.

So after we swap the entries of (say) a and b in the frame's f_locals (this is what you would've got through locals() if you were at the actual frame), we write those to the frame's fastlocals (what is behind your friendly neighborhood locals) so that the change will persist after exiting the current frame.
I will make it clear that all this of course is not possible with merely f_locals/ locals() which really are just a dict representation, given by demand, to the frame's fastlocals array. That's why we need c-api's fasttolocals to make it stick.
Overall that's really all there is to it. Pretty simple when you think about it.

Also, I feel obliged to say it at this point; this is using an undocumented function of the c-api created mainly (only?) for debuggers.
But as the shitposter I am, I'm going to abuse it.
So maybe don't do this at home, do as I say, not as I do, yada yada.

Wouldn't you need to have the arguments' original names to access them at locals' dictionary

Yea and that part can be a little tricky. Though a simple implementation would've just taken the call line string from the outer frame and regex out the variables' names.
But-
That breaks in case the call is spreaded over multiple lines.
Generally I don't think it's a big deal for a function like swap (only two variable-arguments, hard to believe you'd need more than one line for it).
But it just so happens in the last couple of days I wrote an implementation to carve out function call arguments representation (agnostic of newlines, see here), so I used that.
It's a bit lengthier than the alternative, but should generally work or raise ValueError for bad arguments/ bad number of arguments.

About

actual swap function for python

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages