Typesafety in python? Not very pythonic.. I know. But I write better code with strong type restrictions and without duck typing. I realized that while I was refactoring some old code I have written a year ago. The error messages are far away from the source of the error because I did not checked the types of variables.
I am too lazy for writing tons of “if not isinstance(var, type): raise TypeError(“Var is expected to be of type ‘type’”)” lines. Using the new annotations in Python 3 and some hacking with decorations for classes and functions I created some nice features that can improve code security:
Function argument type checks
Python 3 provides us with a way to annotate function arguments. I used this to create a decorator that checks the type of passed arguments against the type defined in the annotation. Just like this:
Well this is too simple in most cases. What about lists and dicts?
@typesafe
def myFunction(stringList: [str], complexDict: {str: [int]})
pass
myFunction(["string"], dict(a=[1, 2], b=[3, 4]) # success
myFunction(["string", 1], dict(a=[1, 2], b=[3, 4]) # fails
myFunction(["string"], dict(a=[1, 2], b=["3", 4]) # fails
Automatic Conversions
How about automatic conversion of arguments? This is a spontaneous idea. I have not yet implemented it.
@typesafe
def myFunction(someString: str, someInteger: int)
pass
myFunction(42, "42") # success. Same as: myFunction(str(42), int("42"))
Typesafe class properties
@typesafe
class TestClass:
someString = public(str)
integerList = public([int])
test = TestClass()
test.someString = "42" # success
test.someString = 42 # fail
test.integerList = [1, 2] # success
test.integerList = 42 # fail
Note: someString and integerList will be object variables and NOT class variables like usual. But they will have the same type checks as above on assignment.
Okay. You may have noticed the strange public keyword for typechecks. This is the reason:
Access restrictions
Just like in many other languages. Restricts the access to variables from the outside. Of course you still can access all variables from “inside” the object.
@typesafe
class TestClass:
privateVar = private(str)
readOnlyVar = readonly(str)
publicVar = public(str)
test = TestClass()
# writing
test.privateVar = "" # fail
test.readOnlyVar = "" # fail
test.publicVar = "" # success
#reading
test.privateVar# fail
test.readOnlyVar # success
test.publicVar # success
Defaults and not None
You can also define default values for your variables. Per default you cannot access a variable that has not been assigned. To override this set None as a default value.
@typesafe
class TestClass:
varWithDefault = public(str, "default")
varWithDefaultNone = public(str, None)
varWithoutDefault= public(str)
test = TestClass()
test.varWithDefault # success. "default"
test.varWithDefaultNone # success. None
test.varWithoutDefault # fails
Ideas and Suggestions?
Well.. this are my experiments with python. It works well for now and I will try to apply these techniques in my next project. Any suggestions and discussions are welcome. Tell me what you think, would this be useful for you?
Code will be published when I finished writing all the unit tests ;)