Library-private parameter in Python

Question:

Context

I’m developing an open-source library (Google Calendar Simple API). Say I have an object (e.x. Attendee) that has a field that should only be set by the library but accessed by the user-developer (e.x. Attendee.response_status (whether attendee accepted the invitation or not)).

Question

What would be the best practice in Python to implement/enforce such behavior? Is there a neat way to differentiate whether the object was created within the same library or from outside?
The field isn’t private, but shouldn’t be set by developers, only within the library itself.

class Attendee:
    def __init__(self, email, response_status):
        self.email = email # can be set by developer
        self.response_status = response_status # should only be set by the library

The one simple solution I’m thinking about is:

class Attendee:
    def __init__(self, email, response_status=None, *, ignore_response_status_parameter=False):
        if not ignore_response_status_parameter and response_status is not None:
            raise ValueError('response_status can only be set automatically')
        
        self.email = email  # can be set by developer
        self.response_status = response_status  # should only be set by the library

(Is it a SO question? Should it be asked somewhere else?)

Asked By: Yevhen Kuzmovych

||

Answers:

This pattern would make the property read-only (in as much as that’s possible in Python) for the consumer of the object:

class Attendee:
    def __init__(self, email, response_status):
        self.email = email
        self.__response_status = response_status

    @property
    def response_status(self):
        return self.__response_status

Beyond that, you can’t prevent the consumer to simply import your class and instantiate it themselves and set response_status to whatever they like. That’s uselessly defensive programming. You just need to ensure that your code works correctly and your functions return the correct data; you can’t defend against someone picking your code—to which they have full access—apart and reusing it in other ways.

Answered By: deceze

This can be done using descriptors (the hard way) or using properties which are a nice descriptor encapsulation.

Here you could change your class to:

class Attendee:
    def __init__(self, email):
        self.email = email # can be set by developer
        self._response_status = 0 # should only be set by the library
    @property
    def response_status(self):
        return self._response_status
    def set_good_status(self):    # example of internal change
        self._response_status = 1

You can then test that response_status is a read-only attribute that can only be internally changed through the private _response_status member.

>>> a = Attendee('foo')
>>> a.response_status
0
>>> a.response_status = 1
Traceback (most recent call last):
  File "<pyshell#29>", line 1, in <module>
    a.response_status = 1
AttributeError: can t set attribute
>>> a.set_good_status()
>>> a.response_status
1
Answered By: Serge Ballesta
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.