bretonics
bretonics

Reputation: 393

Correct way of setting Python class attributes

So I am trying to learn how to properly set Class attributes of a Python class, and wondering about using class functions to do so.

My beginners way has taught me to do:

class MyClass:
    """MyClass class instance"""
    def __init__(self, project, version, command):
        self.project = project
        self.__version = version
        self.__command = command


    """Get version used"""
    def getVersion(self):
        return self.__version

    """Get command executed"""
    def getCommand(self):
        return self.__command

    """Set version used"""
    def setVersion(self, version):
        self.__version = version

This is fine and simple, but this means that I already know the values of my attributes when I instantiate MyClass, since I am passing them to create my object, or if I don't pass them I would use my beginners way of setVersion(version) after creating MyClass object. In my search I read this, which lead me to read about properties, and made me realize that I should not use getter/setters in Python but rather utilize properties. Is this correct?

However, I am wondering if it's Pythonic (or just even correct) to set some of the MyClass instance attributes (project, version, command) using a function. I want to do this because I want this class to do the work of finding these values from specific files by only passing 1 argument. This came to mind, but thought it might be wrong:

class MyClass:
    """MyClass class instance"""
    def __init__(self, project):
        self.project = project
        self._version = self.version()
        self._command = self.command()

    """Get version used"""
    def version(self):
        ...
        use `project` attribute here to get `version` from file...
        ...
        return version

    """Get command executed"""
    def command(self):
        ...
        use `project` attribute here to get `command` from file...
        ...
        return command

Is it even a logical way of setting instance variables by calling a class function?

Should I utilize properties in some ways instead?

Should I make a function outside the MyClass that finds these values returns a MyClass instance instead?

Is there a more proper Pythonic way?

I basically want to create an object with all the extra information (version, command, etc..) by just instantiating it with 1 passed attribute.

Thank you in advance!

Upvotes: 7

Views: 4577

Answers (2)

nosklo
nosklo

Reputation: 223142

There are many ways to solve this, but this is how I usually do it:

class MyClass:
    """MyClass class instance"""
    def __init__(self, project, version, command):
        self.project = project
        self.version = version
        self.command = command

    @classmethod
    def from_file(cls, project):
        """ 
        Alternate constructor that takes a project 
        and extracts the other attributes from a file
        """
        #... some code to use `project` 
        #... and get `version` and `command` from file
        return cls(project, version, command)

# usage
m = MyClass.from_file('project01')

classmethod is a decorator that makes a method able to be called directly in the class, instead of the instance. The method then will automatically get the class as first parameter. cls is just a name that means class, like we use self for instances but we could use any name really. The main object constructor in __init__ takes full parameters, but the the new from_file class method works as an alternative constructor, by creating an instance of the class and returning it.

Note that this is not a definite answer, but just a suggestion of a pythonic way to solve it.

Upvotes: 7

ujhuyz0110
ujhuyz0110

Reputation: 383

  1. By using keyword variables in __init__, you get flexibility of passing only one variable while instantiation.
  2. using decorators such as, @property and @bar.setter enable you to access and modify the associated attributes. And I believe it is also a quite Pythonic way to do so.

The following would be my approach.

class MyClass:
    def __init__(self, project, version=None, command=None):
        self.project = project
        self.__version = version
        self.__command = command

    @property
    def version(self):
        return self.__version

    @version.setter
    def version(self, val):
        self.__version = val

    @property
    def command(self):
        return self.__command

    @command.setter
    def command(self, val):
        self.__command = val

Upvotes: 0

Related Questions