jeudi 28 novembre 2019

Python static immutable properties

What's the correct way to implement static immutable properties in Python?

Minimal example:

A program module 'Family' has a class Parent, defined below:

class Parent():
    def __init__(self, type):
        self.type = type

The parent can be of either of the two string types: 'mother' or 'father'. An external user of this class should be able to set the type when constructing, and later query parent.type to get either of these two values. Furthermore, other parts of the Family module utilise the Parent class and rely on either of these two values being returned. Therefore, the requirements of the type are as follows:

  • be available both externally to users and internally to the Family module
  • be always either 'mother' or 'father', therefore be based on immutable variables

A naive approach could encourage setting type by passing a string:

parent = Parent('mother')

But this allows for accidental misspellings (eg. Parent('omther')). To prevent this, I utilise the following approach:

class Parent():
    TYPE_MOTHER = 'mother'
    TYPE_FATHER = 'father'
    TYPES = [TYPE_MOTHER, TYPE_FATHER]

    def __init__(self, type):
        assert type in self.TYPES, ('Invalid type "%s"' % type)
        self.type = type

parent = Parent(Parent.TYPE_MOTHER)

However, nothing would stop the user from changing these static variables as they like, eg:

parent = Parent('mother')
Parent.TYPE_MOTHER = 'burrito'
print(parent.type == Parent.TYPE_MOTHER)
#> False

To solve this, I considered using @staticmethod and @property annotations:

class Parent():
    @property
    @staticmethod
    def TYPE_MOTHER():
        return 'mother'

    @property
    @staticmethod
    def TYPE_FATHER():
        return 'father'

This wouldn't prevent the user from passing a string to constructor (eg. Parent('mother')) but at least it would prevent them from screwing up the Family module by changing what the parent types can be.


Problems I have with this method:

  • It's ugly, 4 lines of code instead of 1
  • It feels hacky (possibly due to my experience with language-supported private static variables in other languages)
  • My IDE's linter don't like it due to no 'self' argument (even if it's provided)

Questions for you:

  • Is this method Pythonic?
  • Is there a nicer way to do it?
  • Can you suggest a pattern that will achieve such immutability, whilst also enforcing user to use only the variables that I want them to use, preventing them from passing primitive string to the constructor?

Aucun commentaire:

Enregistrer un commentaire