1

I'm developing a class in Python, in which one of its attributes is supposed to represent a neural network. I would like this attribute to actually be an instance of another class, let's call it NN, with several methods (like train() for training, predict() for prediction, etc.). Furthermore, I thought a reasonable way to initialize this attribute was by using my class' constructor, which would expect an instance of the NN class as one of its arguments.

My first thought on how to solve this was to implement an interface, and then type check whatever it is that the user supplies to my class constructor, in order to make sure it is from the expected NN class. However, it seems like neither interfaces (1) nor typechecking (2, 3, 4, 5) are good practices in Python.

Instead, we're supposed to use Duck typing, and simply try using the objects methods until we can't (i.e. defer type checking to runtime). However, in my case the NN class might be quite complicated, with several methods and attributes. If I do discard the interface idea, and simply expect the user of my class to provide an object with all the necessary methods and attributes, how will he/she know what is expected of this object? Should I write all this information in my class docstring, in my constructor docstring, or somewhere else? Or is there a better way to solve this scenario?

JLagana
  • 1,224
  • 4
  • 14
  • 33
  • I would say that Duck Typing is the way to go. That way you don't have to write any code to handle the case where the instance passed to your class does not have the expected methods. So, it will be worth documenting your expectations, and make those expectations simple: make your class just call methods on the instance passed in. – quamrana Nov 13 '17 at 10:04
  • My practice, not using duck typing, but with a base class, which has the train and predict method and raise NotImplementedError – Jimmy Guo Nov 13 '17 at 10:06
  • @quamrana: I believe we agree on this. However, the main problem is then: how does one document this? Specially when the expected class is substantially complex. – JLagana Nov 15 '17 at 08:34
  • @KemmyGuo: Interesting. So you went against the tide here? Or did you have some pythonic motivation for it? – JLagana Nov 15 '17 at 08:34
  • @JLagana: Maybe it will take a separate document to be able to describe all the expectations around all the methods that your class expects to call. – quamrana Nov 15 '17 at 08:42

2 Answers2

1

I would say the __init__ docstring is the place you want to put it in, since that's where the NN object will be provided.

Something like:

"""
(...)
:param NN: an object supporting run(), train() and validate() methods
(...)
"""

The advantage here is that you can supply anything as an argument - hell, you could write a NN argument that's not even a class but a function, or a modified instance of a builtin, or other crazy things like that - and as long as the user provides something that runs correctly, it will work as your neural network.

jkm
  • 704
  • 4
  • 7
  • Thanks for your time! This doesn't seem to be very scalable, though. What if the NN class has *several* methods? Would I include the description of what each one should do there? Together with the expected arguments to these functions, plus all the attributes that this class should have? What if the NN class itself has a method which accepts an object of another complex class? – JLagana Nov 15 '17 at 08:31
  • Would I then have a nested docstring in my class (i.e. the documentation for the NN subclass inside the documentation for the NN class, inside the documentation for the constructor of my base class)? I hope there's a better way... – JLagana Nov 15 '17 at 08:31
  • To be clear, you're only documenting what methods _that you care about_ it needs to have. If the NN has 500 methods, but run() and validate() are the only two that you're gonna call - then you only specify the object must support these two. It's essentially a 'soft' version of an interface - since Python is dynamically typed, you just call the methods you require, and if it doesn't you either throw an error and let the users know you need that name defined, or you handle it yourself and e.g. supply a default. – jkm Nov 15 '17 at 19:37
0

As of Python 3.5 you can use type hints. This documents the expected type to the user and enables optional off-line type checking.

René Pijl
  • 4,310
  • 1
  • 19
  • 25
  • Thanks for your time! Can you provide a minimal example of how to use type hints to achieve this? More specifically, how would I use type hints to specify an entire class, which should be the type of one of the arguments of the constructor of my class? – JLagana Nov 15 '17 at 08:32