[Jan-2015] As part of a recent project, I extended an arguments type-checking function+method decorator that appears in Chapter 39 of Learning Python, 5th Edition, to also perform return type testing, and use 3.X function attributes instead of decorator arguments. That makes it 3.X only, but it's simple to translate this code to use 2.X-compatible decorator arguments instead, as shown in the book.
Fetch the decorator module here:
See the decorator module's self-test code for usage examples, and the book for the logic underlying the decorator itself (its origins go back to this post from 2008/2009).
As another use case—and an example of bytes and bit-level processing—I used this decorator to verify that type annotations coded in simple file obfuscation functions were being respected. Here's a partial excerpt from this (not yet published) client:
""" [Scheme 2 of 4] >>> data = do_encode2(b'spam', ord('x')) 120 b'spam' bytearray(b'\x00\xeb\x00\xe8\x00\xd9\x00\xe5') >>> data = do_decode2(bytes(data), ord('x')) # need bytes() for decorator only! 120 bytearray(b'\x00\xeb\x00\xe8\x00\xd9\x00\xe5') bytearray(b'spam') """ from debugtypes import debugtypes @debugtypes def do_encode2(data: bytes, adder: int) -> bytearray: trace(adder, data[:4]) newdata = bytearray() for byte in data: # bytes => int (or via bytes[i]) word = byte + adder # add scrambler byte1 = (word & 0xFF00) >> 8 # upper byte byte2 = (word & 0X00FF) # lower byte newdata.extend([byte1, byte2]) # 2 bytes for 1, to binary file trace(newdata[:8]) # int => bytes (or bytes([int])) return newdata @debugtypes def do_decode2(data: bytes, adder: int) -> bytearray: trace(adder, data[:8]) newdata = bytearray() ix = 0 while ix < len(data): byte1, byte2 = data[ix], data[ix+1] # bytes => int word = (byte1 << 8) + byte2 # 1 word to 2 bytes word -= adder # remove scrambler newdata.append(word) # retain low byte ix += 2 trace(newdata[:4]) return newdata ... from encoder import * encode, decode = do_encode2, do_decode2 # choose your weapon data = open(filename, 'rb').read() # load from original name data = encode(data, adder) # scramble byte data newname = filename + encext # write to new enc name with open(newname, 'wb') as newfile: # guarantee closes newfile.write(data)
This decorator might be useful during development to ensure coding-time expectations, but has some major downsides:
Update Apr-2015: A similar model is being proposed as standard type declarations for Python 3.5; it threatens to escalate the same caveats to best practice. For an arguably better use case for decorators, see the Private/Public attributes class decorator example from LP5E.