Search code examples
pythonoopinputmodeling

Object-Oriented Way of Dealing with Read-Only User Input


I'm making a simple command-line game but not sure how to model read-only user inputs in Python.

Right now, I'm thinking of declaring an ABC called Input and subclassing it:

import abc


class Input(abc.ABC):
    @abc.abstractmethod
    def is_valid(self, *args, **kwargs):
        pass

    @abc.abstractmethod
    def pop(self):
        pass


class NameInput(Input):
    def __init__(self, name):
        self._name = name

    def is_valid(self, *args, **kwargs):
        pass

    def pop(self):
        return self._name

Another way I came up with is using the @property decorator:

import abc


class Input(abc.ABC):
    @abc.abstractmethod
    def is_valid(self, *args, **kwargs):
        pass

    @abc.abstractmethod
    def value(self, value, *args, **kwargs):
        pass


class NameInput(Input):
    def __init__(self, name):
        self._name = name

    def is_valid(self, *args, **kwargs):
        pass

    @property
    def value(self, value, *args, **kwargs):
        return self._name

Is there a better way of accomplishing my objective? If so, what is it?

For your information, my goal is not just building a program up and running but picking up good programming habits. Please feel free to overcomplicate and tell me the way you prefer (for the sake of scalability and maintainability) in a large-scale project.


Solution

  • The name .pop() is almost reserved for stacks. You shouldn't use that, as the behaviour you're doing here is different.

    The @property is pretty good, but you're misusing it. It should be:

    class NameInput(Input):
        def __init__(self, name):
            self._name = name
    
        def is_valid(self, *args, **kwargs):
            pass
    
        @property
        def value(self):
            return clean_data(self._name)
    
        @value.setter
        def value(self, value):
            self._name = value
    

    The second value function is if you want to have your value property settable.

    You can find more info there: How does the @property decorator work?

    The property allows you to do some in-depth cleanup (like removing HTML injections, ...) either on input or output. I'm calling clean_data() for the sake of the example, there.

    But maybe, depending on what your actual is, you may want to create your own descriptors, so that you can reuse them. That's what happens in ORMs, for instance. More info: https://docs.python.org/3.7/howto/descriptor.html

    Like, in the django ORM (eg. http://www.effectivedjango.com/orm.html ), all these models.CharField and models.XxxXxx are descriptors. You may want to read more documentation (link above) or checking these projects' source code. You might find inspiration :-).

    Good luck!