4

Let's say I have a file "icon.ico" and an url "url.com".
The'll be used just once within the class - "icon.ico" will be set to some window and we'll do the request to url in one method.
I have three ways to define these variables.

1-st way - define as global constants

#in the top of the file
ICON = "icon.ico"
URL = "http://url.com"

#and then
def setIcon(self):
    self.setWindowIcon(QtGui.QIcon(ICON))

def getData(self):
    content = requests.get(URL).content

2-nd way - define as variables of the class

def __init__(self):
    self.url = "http://url.com"
    self.icon = "icon.ico"

3-rd way - define just within the method the'll be used

def setIcon(self):
    icon = "icon.ico"

def getData(self):
    url = "http://url.com"
GriMel
  • 2,272
  • 5
  • 22
  • 40
  • What are your thoughts on that? When would for example an .ini file make sense? – fafl Jan 11 '16 at 11:32
  • @fafl For now I use the first way and I'm just wondering if it's convenient or not) – GriMel Jan 11 '16 at 11:33
  • If those variables are relative to a specific class, i would declare them as static members, not instance members like you suggest in you 2-nd way. – C.LECLERC Jan 11 '16 at 11:40
  • What is your question? As such, this question is currently not specific enough. All three methods are feasible. Which one you choose will depend on what the bigger picture is - what else you will need to do with these variables, etc. – Praveen Jan 11 '16 at 11:52
  • PEP8 is not really an opinion but a style guide: https://www.python.org/dev/peps/pep-0008/#constants and defines a structure for constants (which is your #1) – wordsforthewise May 07 '18 at 22:03

3 Answers3

7

Rules of thumb

  • In general, you should avoid global variables because they live in memory since you import the module until the program finishes (1st case)
  • In general, you should avoid to fix values inside a function (2nd and 3rd cases) because it makes the function les reusable.

Instead of:

def __init__(self):
    self.url = "http://url.com"
    self.icon = "icon.ico"

or

def setIcon(self):
    icon = "icon.ico"

is preferible:

def __init__(self, url, icon):
    self.url = url
    self.icon = icon

or, if you think the values are going to be 90% the same:

def __init__(self, url="http://url.com", icon="icon.ico"):
    self.url = url
    self.icon = icon

Tips of when to use each case

1-st way - define as global constants

  • The constant has sense as a module scope constant. Remember that several classes and functions may be declared inside the same module. This means the constant is going to be used along the whole module and it does not owns to any specific class.
  • You need to find the constant quickly, usually to change its value. In this case maybe you don't really need a constant, but a variable.

2-nd way - define as variables of the class

  • If it is a variable of the class, it is not a constant. If you want to use either a constant or a variable of the class (what is at class level and not at instance level), you should use the 4-th way - as a class level constant.
  • If you want an instance level constant or variable, you should use the 2dn rule of thumb

3-rd way - define just within the method the'll be used

  • You should avoid this way in favor of the 2nd rule of thumb

4-th way - as a class level constant

  • Recomendded way only for both variables and constants that share all the instance of the same class, what means, in fact, class level or class scope
pposca
  • 523
  • 3
  • 10
4

Defining them as class variables is probably the most future-proof method, as you could later use dependency injection to change these variables, which is very useful for unit testing. For example:

class Server:
    def __init__(self, url, icon):
        self.url = url
        self.icon = icon


server = Server('url.com', 'file.ico')

# in your tests, you may want to use a different ico/url
test_server = Server('url.com', 'test_icon.ico')

A note on getters an setters:

Also note that getters and setters tend to be avoided in Python, and properties are used instead if validation is required, or a class with lots of dependent code is refactored. In precompiled languages such as Java and C, the getters/setters are used for encapsulation so that the implementation can later be changed, but in Python are avoided for performance and clarity. So to start with, variables can be accessed as normal, and if the implementation ever changes you can then use the @property and @variable.setter decorators so that getters and setters are used, even though you appear to be accessing the variable directly.

So originally you can access icon directly.

print(server.icon)

But lets say later you refactor your icon inside the class to be _icon_file and _icon_image and load the file every time it's set, but the rest of your app expects the icon variable. This is what getters and setters are usually for (alongside any checks/conversion on setting variables), so we can now add getters and setters for icon, even though the icon variable no longer exists:

class Server:
    def __init__(self, url, icon_filename):
        self.url = url
        self._icon_filename = icon_filename
        self._icon_image = self._load_icon(icon_filename)

    @property
    def icon(self):
        """
        Get the icon file name
        @returns str of the icon filename
        """
        return self._icon_filename

    @icon.setter
    def icon(self, icon_filename):
        """
        Load a new icon file as the icon
        @param icon_filename the relative path to the icon file
        """
        if icon_filename[-4:] != '.ico':
            raise Exception('Must be of .ico format')

        self._icon_filename = icon_filename
        self._icon_image = self._load_icon(icon_filename)

    def _load_icon(self, icon_filename):
        """
        Load a .ico file, and maybe do some other stuff
        @returns Image of the loaded icon
        @private
        """
        # implementation here...       


server = Server('url.com', 'file.ico')
print(server.icon)  # file.ico
server.icon = 'icon2.ico'  # sets and loads new icon
print(server.icon)  # icon2.ico
server.icon = 'icon3.jpg'  # throws an exception
Liam Gray
  • 1,089
  • 9
  • 16
2

You forgot the option of defining them as class-level constants:

class Foo(...)
    ICON = "xxx"
    URL = "YYY"
bruno desthuilliers
  • 75,974
  • 6
  • 88
  • 118