Thursday, December 4, 2014

better translate()

translate is a simple, efficient function for doing fast byte-by-byte replacement on a string in Python.

However, Python Does What is always on the lookout for dangerous alternatives to simple things.  Instead of a function call, isn't it better to mutate global interpreter state so that the same result is achieved as a side-effect of a completely different operation?  (The answer is yes.)

>>> import ctypes
>>> def maketrans(inp, outp):
...    inp = list(inp)
...    outp = list(outp)
...    for aa, bb in zip(inp, outp):  # there's a fun reason single letter variable names are a bad idea here
...       ctypes.memset(ctypes.pythonapi.PyString_AsString(id(aa)), ord(bb), 1)
...
>>> maketrans("jkl", "xyz")
>>> "".join("jkl")
'xyz'

This method also has application to single character variable names that bears further exploration.

yade (yet another dict extension)

>>> from collections import defaultdict
>>> class TypeDict(dict):
...    def __missing__(self, key):
...       self[key] = defaultdict(key)
...       return self[key]
...
>>>
>>> td = TypeDict()
>>> td[float]['test']
0.0

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)

Friday, May 2, 2014

NullAttributor

Sometimes, you have a deeply nested set of attributes which may not be present somewhere along the chain.  A try/except would be clunky.

>>> class Foo(object): pass
...
>>> f = Foo()
>>> f.a = f
>>> f.a.b = f
>>> f.a.b.c.d
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'Foo' object has no attribute 'c'

This is a simple class which allows an expression to be wrapped and unwrapped, with the result at the end being either the value obtained or None.

>>> class na(object):
...     '''
...     A None-Attributator object.  Allows for "safe navigation",
...     similar to the ?. attribute in C#, or undefined in JavaScript.
...     '''
...     __slots__ = ("base",)
...     def __init__(self, base):
...         self.base = base
...     def __getattr__(self, name):
...         return na(getattr(self.base, name, None))
...     def __getitem__(self, key):
...         try:
...             return na(self.base[key])
...         except (KeyError, TypeError, IndexError):
...             return na(None)
...     def __call__(self):
...         return self.base
...
>>>
>>> na(f).a.b.c.d.e()
>>> na(f).a.b['cat']()

Friday, January 10, 2014

loopdefaultdict

Simple idiom for a tree.

>>> import collections
>>>
>>> def loopdefaultdict():
...    return collections.defaultdict(loopdefaultdict)
...
>>> d = loopdefaultdict()
>>> d[1][2][3]
defaultdict(<function loopdefaultdict at 0x02B249B0>, {})
>>> d[1][2][3] = "cat"

Edit: wikipedia has an even better version
http://en.wikipedia.org/wiki/Autovivification#Python
Tree = lambda: defaultdict(Tree)