tags:

views:

125

answers:

3

I'd like to do something like:

def get_foo():   
  return self._foo or self._foo = Bar()

I am looking for the cleanest way to do it. Is it possible with or equals?

My attempts failed:

>>> foo = None
>>> foo or 'bar'
'bar'
>>> foo
>>> foo or foo = 'bar'
  File "<stdin>", line 1
SyntaxError: can't assign to operator
>>> foo or (foo = 'bar')
  File "<stdin>", line 1
    foo or (foo = 'bar')
                ^
SyntaxError: invalid syntax
+4  A: 

Python's assignment operator doesn't pass through the assigned value, primarily because in other languages it has proven to be a fruitful generator of accidental =/== bugs. Instead you have to be explicit:

def get_foo(self):
    if self._foo is None:
        self._foo= Bar()
    return self._foo

You can make an expression with side-effects if you really want to:

def set_foo(self, v):
    self._foo= v
    return v

return self._foo or self.set_foo(Bar())

but it's generally considered a Bad Thing.

bobince
+4  A: 

In Python, you cannot use assignments in expressions. So you have to do the assignment and the return statement on two different lines:

def get_foo(self):
    self._foo = self._foo or Bar()
    return self._foo

But in general, it's better to write it out:

def get_foo(self):
    if not self._foo:
        self._foo = Bar()
    return self._foo
Ivo
What's faster? In the first case, is python smart enough to do not the assignment operation if value is set?
WooYek
Yes. There's basically nothing in it.
katrielalex
@WooYek: If you worry about performance that much, then either this method should be eliminated altogether and go for static initialized members, or Python may not be the language you're looking for. In general, writing it the way you want it to (the short way) is bad because it's unreadable and leads to unmaintainable code.
Ivo
@Ivo: I'm just curious about performance and python's cleverness. I do not agree that using syntactic sugar leads to unreadable and unmaintainable code, if there is a way to make the code cleaner it should be used. Reading `||=` in ruby always get's me little bit confused for a moment, but I'm no ruby programmer and I expect that ruby programmers read it just fine.
WooYek
In the first case, Python will always do the assignment.
Ned Batchelder
A: 

I might be wrong, but this looks like a job for the ||= operator.

def get_foo():
  return self._foo |= Bar()

My Perl is rusty at best, but ||= seems to return an R-value, so the return should work.

Edit: Woopsie, this is Python. It doesn't have a ||=. Bummer.

Then I'd use the simulated ternary operator:

def get_foo()
  self._foo = self._foo is None and Bar() or self._foo
  return self._foo

Edit 2: Python has a real ternary operator. Use it instead of the above.

def get_foo()
  self._foo = Bar() if self._foo is None else self._foo
  return self._foo
egarcia
I thing there is no `||=` in python.
WooYek
There *is* a ternary operator in Python (2.6+ I believe): `self._foo = self._foo if self._foo else Bar()`.
Ivo
"cond and cond-is-true-value or cond-is-false-value" has always been error prone (if the cond-is-true-value is one of the Python values like 0, [], {}, etc. that Python considers to be False in its booleanness). Initialize self._foo to None, and use this proper ternary (beginning in Py2.5): `self._foo = Bar() if self._foo is None else self._foo`. But why assign self._foo to self._foo in the 99% case where self._foo is no longer None once it has been initialized? Just use `if self._foo is None: self._foo = Bar()` followed by `return self._foo`. No ternary magic, just easy-to-read code.
Paul McGuire
@Ivo I didn't know about the ternary operator. Thanks for correcting me.
egarcia
@Paul McGuire: The ´cond and x or y´ is error prone as only if ´cond´ doesn't return a boolean (I prefer real ternary operators if they exist). I prefer using a ternary operator simply because it takes less vertical space on my screen, and (to me) it is exactly as readable as the ´if´ alternative. I don't accept your argument that 99% `self._foo` "is already initialized"; the context is unknown; It could be constantly reset.
egarcia
@egarcia: "only if 'cond' doesn't return a boolean"?? Every Python value has an implicit boolean evaluation. The bugs occur in cases like `x = x is None and 0 or x` - surprise, you never get the 0 assignment because bool(0) is False.Far and away the most common use of this idiom is for lazy initialization of an expensive-to-evaluate attribute (such as a database field), where the attribute is initialized to None, and then on first access, it gets set to its real value. For the following 'n' accesses (might be 0, might be a million), the lazy-init value or an updated non-None value is used.
Paul McGuire
@egarcia: (ran out of room) - so to summarize 'cond and x or y' is bug-prone when bool(x) is False, in which case, you will get y, regardless of whether cond is True or False.
Paul McGuire
@egarcia: one other style point - when evaluating for a value's None-ness, the preferred idiom is `if x is None`, not `if x == None`. None is a singleton and can be safely compared with using the 'is' operator.
Paul McGuire
@Paul McGuire: I understand what you mean about bugginess now. You are right. I've also fixed the `None` and `is` thingie.
egarcia