2

I have three variables that are closely tied together and I do not want to pass separately every time I call a function. What is the proper way to bundle them.

Context: The purpose of the variables is to keep track of some properties of a document while I am reading it word by word.

My current approach is to bundle them in a class:

class MarkdownIsOpen(object):
    def __init__(self):
        self.ChapterOpen = False
        self.SectionOpen = False
        self.ArticleOpen = False

But this seems a bit wrong to me, as I do not intend to add any methods or other functionalities.

A namedtuple would be perfect if it were mutable.

What would be the proper (most pythonic) way to bundle the three variables?

Ottotos
  • 633
  • 5
  • 14

4 Answers4

3

You can use dataclasses.

@dataclass
class MarkdownIsOpen:
    ChapterOpen: bool = False
    SectionOpen: bool = False
    ArticleOpen: bool = False
3

Use a dataclass:

@dataclass
class MarkdownIsOpen:
    ChapterOpen: bool = False
    SectionOpen: bool = False
    ArticleOpen: bool = False

Or:

MarkdownIsOpen = make_dataclass('MarkdownIsOpen', ['ChapterOpen', 'SectionOpen', 'ArticleOpen'])

Note that this requires Python 3.7.

If you're using Python <= 3.6, then an ordinary class will do as well. Classes are not expensive, and they provide a hint to the user that your function does not expect any old dict-like, but a special container with the following attributes.

Compare this to, for example, C's struct or Scala's case class, which serve largely the same purpose.

Also, you can even override __slots__ and/or __getitem__ to allow dict-like access, and prevent the addition of new attributes:

class MarkdownIsOpen:

    __slots__ = ('ChapterOpen', 'SectionOpen', 'ArticleOpen')

    def __init__(self):
        self.ChapterOpen = False
        self.SectionOpen = False
        self.ArticleOpen = False

    def __getattr__(self, key):
        return getattr(self, key)

    def __setattr__(self, key, value):
        setattr(self, key, value)

Example:

m = MarkdownIsOpen()
m['ChapterOpen'] = True
print(m['SectionOpen'])
m['Nonexistent'] = False

Output:

False
AttributeError: 'MarkdownIsOpen' object has no attribute 'Nonexistent'
gmds
  • 19,325
  • 4
  • 32
  • 58
  • I'm not sure to understand the advantage of this over a simple namedtuple, even the [PEP](https://www.python.org/dev/peps/pep-0557/#rationale) doesn't convince me (unless if you need to have static type verification, which doesn't seem to be the case here). I would still suggest to use namedtuple for this. – cglacet Apr 10 '19 at 09:47
  • 1
    If you like to use dataclasses approach then may be better to write: `MarkdownIsOpen = make_dataclass('MarkdownIsOpen', ['ChapterOpen', 'SectionOpen', 'ArticleOpen']`). – intellimath Apr 10 '19 at 10:51
  • @cglacet OP mentioned that mutability is necessary, which rules out `namedtuple`. I am actually quite a big fan of `namedtuple`, but one problem I have with it is allowing numerical indexed access. – gmds Apr 10 '19 at 12:13
2

May take a look at this question: Existence of mutable named tuple in Python?

With two nice answers: recordclass and namedlist of mutable alternatives to named tuples

Sparky05
  • 4,692
  • 1
  • 10
  • 27
  • Thank you for the suggestions. I will go for the dataclass though because that is from the standard library. – Ottotos Apr 10 '19 at 09:38
  • [`namedtuple`](https://docs.python.org/3/library/collections.html#collections.namedtuple) also are in the standard library – cglacet Apr 10 '19 at 09:44
0

You could use a simple named tuple or a simple dictionary for that purpose, if you really never need to define any methods on the class.

BluesSolo
  • 608
  • 9
  • 28