1

I would like to know if it can be a good idea to set certain attributes of an object only when there's a get request and the attribute was not set already. If so, is this an appropriate approach (EAFP, few lines, @property)? If not, are there best practices?

I'm setting up a GUI tests environment with lackey and unittest in python. The visual recognition by ly.Pattern() i.e. the initialization of GUI elements takes some time so I want to do this only once and only when it's necessary.

import lackey as ly
img_path = "my_img.png"

One way

class MyClass:
    @property
    def foo(self):
        try:            
            return self._foo
        except AttributeError:
            self._foo = ly.Pattern(img_path)
        return self._foo

Another Way

class MyClass:
    @property
    def foo(self):
        try:            
            return self._foo
        except AttributeError:
            self._foo = ly.Pattern(img_path)
        return self._foo

    @foo.setter
    def foo(self, value):
        self._foo = ly.Pattern(value)
Agent49
  • 133
  • 1
  • 11
  • 2
    [This is relevant](https://stackoverflow.com/questions/903130/hasattr-vs-try-except-block-to-deal-with-non-existent-attributes) i think – bracco23 May 15 '19 at 14:24
  • For my part I don't see any problem in this kind of code, except if this attr is not setup in the constructor, which would makes the code less readable... – olinox14 May 15 '19 at 14:26
  • I also found this before but I did not find it in with @property and furthermore it's a quiet old thread. – Agent49 May 15 '19 at 14:27
  • 1
    @Agent49 Yes, quite old but also solved. It has a point on `hasattr` vs `try\except` but also provide a great solution with `getattr`. – bracco23 May 15 '19 at 14:29

2 Answers2

1

This is what I would do, to keep everything clean:

class MyClass:
    def __init__(self):
        self._foo = None

    @property
    def foo(self):
        if self._foo is None:
            self._foo = ly.Pattern(img_path)
        return self._foo
bracco23
  • 2,181
  • 10
  • 28
  • I tried out a few things: `self._foo = getattr(self, '_foo', ly.Pattern(img_path))` is the shortest. @bracco23 your suggestion for me is the cleanest in terms of providing a kind of an interface inside `__init__()`. Thanks a lot! – Agent49 May 15 '19 at 14:58
  • 1
    I would take a deeper look at `getattr` in your case, since I guess default `Pattern` object might be created even if it is not needed and that might be a problem. Glad I could help. – bracco23 May 15 '19 at 14:59
0

To add maybe the shortest way of doing this:

class MyClass:

@property
def foo(self):
    self._foo = getattr(self, '_foo', ly.Pattern(img_path))
    return self._foo
Agent49
  • 133
  • 1
  • 11