Tuesday, November 25, 2014

ctypes short string shenannigans


>>>
>>>
>>>
42105668
>>>
42105668
>>>
'f'
>>>
'ab'
>>>
'f'
import ctypes
b_char = 'b'
ctypes.pythonapi.PyString_AsString(id(b_char))

ctypes.memset(_, ord('f'), 1)


b_char


'ab'


'ab'[1:]



Single character strings are interned in a special array in CPython.  This means mutation to the internals of a single character string will have global consequences.

(http://www.laurentluce.com/posts/python-string-objects-implementation/ has more information.)

Wednesday, November 19, 2014

assignment order

Variable assignment in a chained "=" goes left-to-right.

>>> a = [None]
>>> a[0][0] = a  # fails, as expected
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'NoneType' object has no attribute '__getitem__'
>>> a[0] = a[0][0] = a  # succeeds

The second form is equivalent to:
>>> a[0] = a
>>> a[0][0] = a

With a bit of code we can explicitly see that the assignments are happening in left-to-right order:

>>> class Squawker(object):
...    def __init__(self, name): self.name = name
...    def __setitem__(self, key, val): print self.name
...
>>> Squawker("a")[0] = Squawker("b")[0] = 1
a
b

Wednesday, November 12, 2014

what to expect except to accept

Any object can be the target of an except, not just subclasses of BaseException.

>>> try:
...    raise Exception("these are all fine!")
... except dict as not_exception:
...    pass
... except 42 as not_a_type:
...    pass
... except type as super_meta:
...    pass
...
Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
Exception: these are all fine!


except block types evaluated lazily

>>> try:
...    raise Exception()
... except Exception:
...    print "OK"
... except doesntexist:
...    pass
...
OK
>>>
>>> try:
...    raise Exception()
... except doesntexist:
...    pass
... except Exception:
...    print "OK"
...
Traceback (most recent call last):
  File "<stdin>", line 3, in <module>
NameError: name 'doesntexist' is not defined

Tuesday, November 11, 2014

dictzip


def dictzip(*dicts):
    return dict([(k, tuple([d[k] for d in dicts]))
                 for k in set(dicts[0]).intersection(*dicts[1:])])

zip() is a very handy function that takes a series of iterables and returns a list of tuples of the nth elements of each iterable:

>>> zip(['a', 'b', 'c'], [1, 2, 3])
[('a', 1), ('b', 2), ('c', 3)]

dictzip() takes an iterable of dictionaries and returns a dictionary of tuples of the elements of each dict on the same key:

>>> dictzip( {'a': 1}, {'a': 'cat'} )
{'a': (1, 'cat')}

This version is exclusive -- only keys that are in all dictionaries are output.  A simple variant would be to substitute None for missing keys.

def dictzip(*dicts):
    return dict([(k, tuple([d.get(k) for d in dicts]))
                 for k in set(dicts[0]).union(*dicts[1:])])

(Adapted from: http://stackoverflow.com/a/16458780)