I'd like to have a contract on a parameter of some function to enforce the parameter object must have specific property. I understand python isn't a strictly typed language, but having contracts and interfaces is very useful sometimes. Python now has type hints, which is great, so we can do this:
def myfunc(myparam: MyType) -> SomeType:
myparam.myprop # this should exist
But how can I say MyType must have a specific object property (myprop) without inserting assertions and throwing exceptions in run-time? I could define abstract classes with abc metaclasses, which can be used as interfaces.
from abc import ABC, abstractmethod
class MyInterface(ABC):
@property
@abstractmethod
def myprop(self) -> int: pass
now somewhere in the code I could define MyType as:
class MyType(MyInterface):
myprop = 8
It's working, but myprop is a class property and not an object property (attribute). Of course I could do this:
class MyType(MyInterface):
myprop = 0
def __init__(self):
self.myprop = 8
Fine, but I had to define an (unnecessary) class ("static") property and effectively hide it with an object property. Not very clean. Moreover now I have a default value for myprop which is not what I want. But if I do this:
class MyType(MyInterface):
myprop = None # wrong type here
def __init__(self):
self.myprop = 8
it's wrong because, myprop must be int and can not be None, which is correctly caught by the linter. There shall be an object property without a class property.
The goal would be that a static checker like mypy could catch implementation errors where a class doesn't obey the defined interface or contract which requires the parameter instance must have some property.
What is the pythonic (or not so pythonic) way to achieve this?
You don't need to create new interface or implement ABC
. You can use @dataclass
. I am using default code checker in pycharm (warnings are in comments).
from dataclasses import dataclass
@dataclass
class MyType:
my_prop: int
@dataclass
class SomeType:
my_prop_out: int
def my_func_ok(my_param: MyType) -> SomeType:
some_type = SomeType(my_prop_out=my_param.my_prop)
return some_type
def my_func_bad(my_param: MyType) -> SomeType:
return my_param # this is not returning SomeType
my_type = MyType() # this is expecting to set my_prop
my_type = MyType(my_prop="sss") # this is expecting to set my_prop with int not str
my_func_ok(my_param=100) # this is expecting MyType object
my_func_ok(my_param=MyType(my_prop=10)) # this is correct, no errors
I am adding picture of pycharm code checker warnings: