3
\$\begingroup\$

When a property is essentially a boolean value for whether or not an attribute exists or is set a certain way, is it more appropriate to create an object that does not list the option attribute in its __init__() call, or is it better to simply create the object with the 'falsiest' version of that attribute?

Consider the following class:

class RectLikeObj(object):
    def __init__(self, x, y, w, h, pinned=None):
        self.x = x
        self.y = y
        self.w = w
        self.h = h
        self.pinned = pinned or ()

    ###series of property getter/setters for attributes that are
    ###derived from the above
    @property
    def topleft(self):
        return (self.x, self.y)

    @topleft.setter
    def topleft(self, value):
        self.x, self.y = value

    ###and so on for all corners and center, etc

    @property
    def is_pinned(self):
        if not self.pinned:
            return False
        return True

    def pin_corner(self, corner, xy):
        self.pinned = (corner, xy)

Alternatively the object may not ever have the self.pinned attribute and the property is instead looking for the attribute.

class RectLikeObj(object):
    def __init__(self, x, y, h, w):
        self.x = x
        self.y = y
        self.w = w
        self.h = h

    ###same getters/setters as above

    @property
    def is_pinned(self):
        if getattr(self, 'pinned', False):
            return True
        return False

    def pin_corner(self, corner, xy):
        self.pinned = (corner, xy)

Now it could be done to use a try/except, only there's nothing exceptional about this behavior; it's quite binary, either the Rect object is pinned to a particular corner and xy coordinates or it isn't. So I don't know that using eafp is the most correct choice here.

Is it better to use a 'falsy' attribute such as self.pinned = () in the __init__() method, or is it better to leave it to be set later by the self.pin_corner() method and then use the property to check for its existence?

\$\endgroup\$
  • 1
    \$\begingroup\$ Hypothetical code is off-topic for Code Review. Could you either add all relevant details or strip the hedge words from your question (e.g. "Let's say", "Assume")? \$\endgroup\$ – 200_success Sep 10 '14 at 18:54
  • \$\begingroup\$ I see why you'd say that; in this situation there is no vacuum however. The context is entirely dependent on the method by which the object checks its own attributes, so the original code was complete for the purpose of the question. Nevertheless I have edited it \$\endgroup\$ – Stick Sep 10 '14 at 19:38
  • \$\begingroup\$ Faced with a similar dilemma of potentially non-existing attributes, I typically do the equivalent in your case of setting self.pinned = None in the constructor, but I have probably not thinked as hard as you seem to have about this... \$\endgroup\$ – Jaime Sep 10 '14 at 20:22
1
\$\begingroup\$

I vote the first method (with the change of having () as the default argument instead of None, because it's simpler).

If you don't absolutely need code introspection, don't use it. In my mind it's more cluttered and risks confusing editors such as PyCharm, as well as human beings, about the composition of a class. To take this idea a little further, there's no need for "pinned" to accept a tuple in the constructor. Give it the same contract as pin_corner; that is, accept both arguments and default them both to None. There's less validation required - when accepting a tuple you would need to check its length as well as the validity of its members.

\$\endgroup\$
  • \$\begingroup\$ good call on the pin_corner method \$\endgroup\$ – Stick Sep 11 '14 at 13:07

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.