2

I want to write a suite of methods that act as getters when passed zero arguments and as setters when passed a single argument. I have two two reasonable implementations, shown below. Is one better than the other, or is there perhaps an even better way?

class Thing(object):

    def __init__(self):
        self._size = 0

    def sizeA(self, *args):
        if not args:
            return self._size
        self._size = args[0]
        return self

    UNIQUE_ID = []

    def sizeB(self, arg=UNIQUE_ID):
        if arg is self.UNIQUE_ID:
            return self._size
        self._size = arg
        return self

A couple of comments:

sizeA() benchmarks faster than sizeB(), but doesn't give me argument count checking. sizeB() works, but it depends on UNIQUE_ID being invariant, and I wonder if there are circumstances (e.g. pickling) that would cause it to break.

P.S.: Though I'm currently developing in python-2.7, I'm open to python-3.x specific answers as appropriate.

  • 2
    Why not just use properties? stackoverflow.com/questions/17330160/… – Robert Harvey Apr 27 '16 at 23:45
  • @RobertHarvey: Good to see your comments, as always. I'm dabbling with fluent interfaces, e.g. Thing().size(30).color('red') etc..., and despite grumblings to the contrary I think fluency is a reasonable approach for my application. – fearless_fool Apr 28 '16 at 0:00
  • You don't do that by overloading methods (which Python doesn't support anyway), you do that by returning self. See stackoverflow.com/q/21785689/102937 – Robert Harvey Apr 28 '16 at 0:03
  • 1
    I'm not a Python expert, but wouldn't checking if len(args): be the most robust? – user949300 May 28 '16 at 14:08
  • @user949300: the *args argument can only ever be a tuple, and tuples are only evaluated as False in a boolean context if it has zero arguments, so there is no difference in robustness. – RemcoGerlich Jun 27 '16 at 14:12
1

A sentinel and a defaulted arg is a good way to do it. I would use an arbitrary object though, not a list (preference, and this way someone won't go about fiddling with it).

class A(object):
    _DEFAULT = object()

    def __init__(self):
        self._size = 0

    def sizeC(self, arg=_DEFAULT):
        if arg is not DEFAULT:
            self._size = arg
        return self._size

And pickle will handle it fine (it also handles it fine with a list, change _DEAFULT to be [] and change it to print id(y._DEFAULT)).

a = A()
a.sizeC(10)

x = pickle.dumps(a)
y = pickle.loads(x)

print y._DEFAULT
print y.sizeC()
print y.sizeC(20)
print y.sizeC()
print y._DEFAULT
print a._DEFAULT

gives

andy@batman:~$ p tmp.py 
<object object at 0x7ff952223090>
10
20
20
<object object at 0x7ff952223090>
<object object at 0x7ff952223090>

I can't think of a situation where this would cause issue, and is an accepted pattern as far as I'm aware (I don't have any citations to hand.)

Out of curiosity, what is the difference in the benchmark between the two options?

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.