Monday, February 28, 2011

PHP-like string concatenation (almost)

class PHPstr(str):
   def __getattr__(self, attr):
      return PHPstr(self+attr)

>>> a = PHPstr("test")
>>> a.test
'testtest'

Almost, but not quite the concatenation operator of PHP strings.

Friday, January 21, 2011

custom string class

Here is how to make a subclass of string which has all the same operations available, but which returns instances of that subclass:

import inspect
import functools

class MyStr(str):
    def __radd__(self, other): 
        return MyStr(str.__add__(other, self))
    def __repr__(self): 
        return self.__class__.__name__ + str.__repr__(self)

def _mystr_wrap(f):
    @functools.wraps(f, ('__name__','__doc__'))
    def g(*a, **kw):
        res = f(*a, **kw)
        if res.__class__ == str:
            return MyStr(res)
        return res
    return g

for name, f in inspect.getmembers(str, callable):
    #skip some special cases
    if name not in ['__class__', '__new__', '__str__', '__init__', '__repr__']:
        setattr(MyStr, name, _mystr_wrap(f))

>>> MyStr("cat")
MyStr'cat'
>>> MyStr("cat")+"dog"
MyStr'catdog'
>>> MyStr("cat")[1:]
MyStr'at'


Note: if you find yourself actually doing this, it is probably a bad code smell :-)

Wednesday, January 5, 2011

making your class work with built-in math functions

Just add __float__ and/or __int__.  Then the built-in math functions, or anything that sanitizes its input via int() or float() will see your objects as a numeric type.


>>> import math

>>> class B(object):
...    def __float__(self): return 0.5
...
>>> math.sin(B())
0.47942553860420301

Tuesday, December 28, 2010

nested sequence unpacking

>>> a,(b,c) = 1,(2,3)
>>> a
1
>>> b
2
>>> c
3

the confusingly named setdefault

setdefault has the same behavior as the following function:

>>> def setdefault2(my_dict, key, def):
...    my_dict[key] = my_dict.get(key, def)
...    return my_dict[key]
...
>>> a = {}
>>> setdefault2(a, "1", "2")
'2'
>>> a.setdefault(1, 2)
2
>>> setdefault2(a, "1", "3")
'2'
>>> a.setdefault(1, 3)
2
>>> a
{'1': '2', 1: 2}


This function is useful in similar cases to a collections.defaultdict.  The difference being, with this function you can choose what you want the default to be each time you fetch a key from the dictionary.  With a defaultdict, the default value for a missing key must be set at construction time.

using finally to do something after return

>>> def foo():
...    try:
...       print "returning"
...       return
...    finally:
...       print "after return"
...
>>> foo()
returning
after return

sequence unpacking in argument list

>>> def foo(a, (b,c)): print a,b,c
...
>>> foo(1,(2,3))
1 2 3
>>> foo(1,())
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 1, in foo
ValueError: need more than 0 values to unpack

This feature has probably really confused you if you ever mistakenly put parenthesis around the parameters to a lambda.  In that case the lambda expression will take only one argument, and that argument will be a tuple.

>>> a = lambda (a,b): a+b
>>> a(1,2)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: <lambda>() takes exactly 1 argument (2 given)
>>> a((1,2))
3