5

Looking to clean up some code by using a namedtuple to hold multiple variables for passing through a number of functions. Below is a simplified example (I actually have a few more additional arguments).

Before:

def my_function(session_cass, session_solr, session_mysql, some_var, another):
"""Blah blah.

Args:
    session_cass (Session): Cassandra session to execute queries with.
    session_solr (SolrConnection): Solr connection to execute requests with.
    session_mysql (connection): MySQL connection to execute queries with.
    some_var (str): Yada yada.
    another (int): Yada yada.
"""

After:

def my_function(sessions, some_var, another):
"""Blah blah.

Args:
    sessions (namedtuple): Holds all the database sessions.
    some_var (str): Yada yada.
    another (int): Yada yada.
"""

For docstrings, I've been following the Google style guide, with the addition of types (inspired by this post), which I really like because it makes it a lot easier to keep track of what types are coming in.

My question is, how would you go about documenting a namedtuple in this scenario? Obviously as it's currently set up, you have no information about the types within the namedtuple. Is there an accepted way to extend the docstring here, or document the namedtuple where it's defined (not shown)?

I know you could document a class in this manor, but I'm trying to stay away from using a class as I don't really have any purpose for it other than to hold the variables.

Community
  • 1
  • 1
latetojoin
  • 113
  • 1
  • 8
  • 1
    why is it a named tuple? why not just take an (arbitrary) dictionary of {ident:session} or even just a list of sessions ... are you actually validating that its a named tuple thats passed in (in my experience named tuples work much better as return values than method arguments) – Joran Beasley Aug 23 '16 at 23:34
  • No particular reason. I considered both and arbitrarily chose namedtuple - it seemed more "lightweight" and typically I use dictionaries when the values are all the same type (I'm aware you can mix types). Anyways, I think the question is the same for both - in a more general sense, when you pass either into a function and they contain mixed types, what's the best way to document that? Perhaps it's better to just use a class (documenting the types in the class doc), or just don't worry about it and backtrack to where the namedtuple/dictionary was initialized. – latetojoin Aug 24 '16 at 00:00

2 Answers2

6

The Python3 documentation for namedtuple shows that a namedtuple's docstrings can be customized by appending your own strings to the __doc__ fields. For your question, you could write:

Sessions = namedtuple('Sessions', ['cass', 'solr', 'mysql'])
Sessions.__doc__ += ': All database sessions.'
Sessions.cass.__doc__ += ': Cassandra session to execute queries with.'
Sessions.solr.__doc__ += ': Solr connection to execute requests with.'
Sessions.mysql.__doc__ += ': MySQL connection to execute requests with.'

Then executing help(Sessions) outputs:

Help on class Sessions in module MyModule:

class Sessions(builtins.tuple)
|  Sessions(cass, solr, mysql): All database sessions.
|  
|  Method resolution order:
|      Sessions

and then after few other rows of documentation comes:

|----------------------------------------------------------------------
|  Data descriptors defined here:
|  
|  cass
|      Alias for field number 0: Cassandra session to execute queries with.
|  
|  solr
|      Alias for field number 1: Solr connection to execute requests with.
|  
|  mysql
|      Alias for field number 2: MySQL connection to execute requests with.
|  
|  ----------------------------------------------------------------------

Admittedly the amount of automatically documentation text for Sessions can make it difficult to find specific documentation you added.

snow_abstraction
  • 408
  • 6
  • 13
  • I got this error when I tried this: `AttributeError: attribute '__doc__' of 'type' objects is not writable`. I'm in python 2.7. – Ben Caine Dec 21 '18 at 20:25
  • 2
    @BenCaine This is expected in 2.7 and a partial work-around is subclassing a namedtuple. See **Example from the documentation** section of the this answer https://stackoverflow.com/a/28568351/3527268 for a simple example. – snow_abstraction Jan 06 '19 at 20:44
1

I am not familiar with Google style guide, but how about this:

for a namedtuple or tuple or list or whatever that is interchangeable I would go for something like this

def my_function(sessions, some_var, another):
    """Blah blah.

    Args:
        sessions (sequence): A sequence of length n that 
                             holds all the database sessions.
                             In position 0 need bla bla
                             In position 1 need ble ble
                             ...
                             In position n-1 need blu blu
        some_var (str): Yada yada.
        another (int): Yada yada.
    """    

on the other hand if I use the attributes of the namedtuple, then maybe something like this

def my_function(sessions, some_var, another):
    """Blah blah.

    Args:
        sessions (object): A object that holds all the database sessions.
                           It need the following attributes 
                           bla_bla is ...
                           ble_ble is ...
                             ...
                           blu_blu is ...
        some_var (str): Yada yada.
        another (int): Yada yada.
    """    

for a dictionary, how about this

def my_function(sessions, some_var, another):
    """Blah blah.

    Args:
        sessions (map): A dictionary-like object that holds all the 
                        database sessions, it need the following keys
                        bla_bla is ...
                        ble_ble is ...
                           ...
                        blu_blu is ...
        some_var (str): Yada yada.
        another (int): Yada yada.
    """    

or

def my_function(sessions, some_var, another):
    """Blah blah.

    Args:
        sessions (customclass): Holds all the database sessions.
        some_var (str): Yada yada.
        another (int): Yada yada.
    """   

In each instance just ask for the minimum functionality that the function need to work correctly

Copperfield
  • 8,131
  • 3
  • 23
  • 29