42

I come from a PHP background and would like to know if there's a way to do this in Python.

In PHP you can kill 2 birds with one stone like this:

Instead of:

if(getData()){
    $data = getData();
    echo $data;
}

I can do this:

if($data = getData()){
    echo $data;
}

You check to see if getData() exists AND if it does, you assign it to a variable in one statement.

I wanted to know if there's a way to do this in Python? So instead of doing this:

if request.GET.get('q'):
    q = request.GET.get('q')
    print q

avoid writing request.GET.get('q') twice.

kenorb
  • 155,785
  • 88
  • 678
  • 743
givp
  • 2,494
  • 6
  • 31
  • 30
  • Do you have a Python tutorial bookmarked? Which one? – S.Lott Nov 02 '09 at 22:03
  • 1
    Just the official one: http://docs.python.org/tutorial/ – givp Nov 02 '09 at 22:07
  • 2
    Thankfully, Python doesn't have this C misfeature, and in general, in most languages which inherited it from C/C++, its use is frowned upon for readability and clarity reasons. – Pavel Minaev Nov 02 '09 at 22:17
  • 1
    Duplicate: http://stackoverflow.com/questions/542212/is-there-any-way-to-do-variable-assignments-directly-inside-a-whilehere-loop – S.Lott Nov 03 '09 at 00:08
  • 2
    @S.Lott It's not a duplicate as this other question asks about `while` which made all answers focused on iteration thus bypassing the issue of assignment inside flow control statement entirely. – Piotr Dobrogost May 12 '13 at 09:34
  • 1
    Not so "misfeature", for example clojure even has special [if-let](http://clojuredocs.org/clojure_core/clojure.core/if-let) macro. – kolen Feb 10 '14 at 10:48

10 Answers10

26

See my 8-year-old recipe here for just this task.

# In Python, you can't code "if x=foo():" -- assignment is a statement, thus
# you can't fit it into an expression, as needed for conditions of if and
# while statements, &c.  No problem, if you just structure your code around
# this.  But sometimes you're transliterating C, or Perl, or ..., and you'd
# like your transliteration to be structurally close to the original.
#
# No problem, again!  One tiny, simple utility class makes it easy...:

class DataHolder:
    def __init__(self, value=None): self.value = value
    def set(self, value): self.value = value; return value
    def get(self): return self.value
# optional but handy, if you use this a lot, either or both of:
setattr(__builtins__,'DataHolder',DataHolder)
setattr(__builtins__,'data',DataHolder())

# and now, assign-and-set to your heart's content: rather than Pythonic
while 1:
    line = file.readline()
    if not line: break
    process(line)
# or better in modern Python, but quite far from C-like idioms:
for line in file.xreadlines():
    process(line)
# you CAN have your C-like code-structure intact in transliteration:
while data.set(file.readline()):
    process(data.get())
Community
  • 1
  • 1
Alex Martelli
  • 854,459
  • 170
  • 1,222
  • 1,395
  • 31
    You have a pretty smart 8-year-old! ;) – unutbu Nov 03 '09 at 03:28
  • 3
    @unutbu, heh - that would be my (older) cat as my kids are a bit older... my younger daughter's just started her PhD in telecom engineering (advanced radio systems, mostly)...;-) – Alex Martelli Nov 03 '09 at 04:15
24

Probably not exactly what you were thinking, but...

q = request.GET.get('q')
if q:
    print q

this?

Amber
  • 507,862
  • 82
  • 626
  • 550
  • yeah, that's right. the pattern is to use a variable if and only if it exists (meaning it gives a valid value, i.e. anything but *False*). –  Nov 02 '09 at 22:06
  • Thanks, looks like this is the best way to do it. As @Adam says, it's not possible to do this in Python. Thanks. – givp Nov 02 '09 at 22:08
  • 9
    The limitation of this solution is if you want to use it in a series of if-elif-elif-elif etc. For example, see this other SO question: http://stackoverflow.com/questions/122277/how-do-you-translate-this-regular-expression-idiom-from-perl-into-python – Craig McQueen Nov 27 '09 at 00:23
  • @Amber, One hic-up with this is that when `q==0` or `q==0.`, the `if` condition fails. `If` converts its condition expression to a `bool` and `0` and `0.` both cast to `False`. – hobs Mar 19 '12 at 09:32
  • @hobs Sure. And you can always use a more explicit conditional if you expect that might be the case sometimes (e.g. `if q is not None:`). – Amber Mar 20 '12 at 03:14
10

A variation on Alex's answer:

class DataHolder:
    def __init__(self, value=None, attr_name='value'):
        self._attr_name = attr_name
        self.set(value)
    def __call__(self, value):
        return self.set(value)
    def set(self, value):
        setattr(self, self._attr_name, value)
        return value
    def get(self):
        return getattr(self, self._attr_name)
save_data = DataHolder()

Usage:

if save_data(get_input()):
    print save_data.value

or if you prefer an alternative interface:

if save_data.set(get_input()):
    print save_data.get()

I would find this helpful to test a series of regular expressions in an if-elif-elif-elif etc construct, as in this SO question:

import re

input = u'test bar 123'
save_match = DataHolder(attr_name='match')
if save_match(re.search('foo (\d+)', input)):
    print "Foo"
    print save_match.match.group(1)
elif save_match(re.search('bar (\d+)', input)):
    print "Bar"
    print save_match.match.group(1)
elif save_match(re.search('baz (\d+)', input)):
    print "Baz"
    print save_match.match.group(1)
Community
  • 1
  • 1
Craig McQueen
  • 41,871
  • 30
  • 130
  • 181
  • 1
    I like your addition of `__call__()` to DataHolder, but I don't understand why it's useful to specify `attr_name`. If DataHolder were a singleton or if an instance of it could hold multiple values, each in different attributes, I'd understand, but it doesn't seem to have a strong purpose here. – Mr. Lance E Sloan Jun 09 '16 at 15:20
  • 1
    @LS: Yes, I could have just fixed it to be `value` or `x`. But [good variable names](http://c2.com/cgi/wiki?GoodVariableNames) improve readability. That's the essence of it. – Craig McQueen Jun 09 '16 at 23:17
2

PEP 572 introduces Assignment Expressions. From Python 3.8 and onwards you can write:

if q := request.GET.get('q'):
    print q

Here are some more examples from the Syntax and semantics part of the PEP:

# Handle a matched regex
if (match := pattern.search(data)) is not None:
    # Do something with match

# A loop that can't be trivially rewritten using 2-arg iter()
while chunk := file.read(8192):
   process(chunk)

# Reuse a value that's expensive to compute
[y := f(x), y**2, y**3]

# Share a subexpression between a comprehension filter clause and its output
filtered_data = [y for x in data if (y := f(x)) is not None]
timgeb
  • 76,762
  • 20
  • 123
  • 145
1
q = request.GET.get('q')
if q:
    print q
else:
    # q is None
    ...

There's no way of doing assignment and conditionals in one go...

Adam
  • 803
  • 1
  • 8
  • 11
  • Explicit is better than implicit. Readability counts. Special cases aren't special enough to break the rules. You are saving so much vertical space in your code by not writing all those lines with nothing but a right brace, so why not go for clarity in Python? Personally, I cut my teeth on C and have been writing Python code for 12 years now, and I never realised this feature was missing. You just don't need to do this. – Michael Dillon Nov 02 '09 at 23:34
  • @Michael: I agree in almost all cases. I am just missing it in the specific use case of testing multiple regular expressions, as described in this other SO question: http://stackoverflow.com/questions/122277/how-do-you-translate-this-regular-expression-idiom-from-perl-into-python – Craig McQueen Nov 27 '09 at 00:47
  • @MichaelDillon in your 12 years (24 years now, if you've kept at it) of writing python, I imagine you've had several occasions to write cascading regexp match statements, as in the other question Craig McQueen referenced. When you've got 10 of them, how do you write it? Nest-and-further-indent 10 times? – Don Hatch Feb 01 '21 at 04:43
0

a possible way to do it, without necessity to set the variable before, could be like:

if (lambda x: globals().update({'q':x}) or True if x else False)(request.GET.get('q')):
    print q

.. it's just for fun - this method should not be used, because it is ugly hack, difficult to understand at first sight, and it creates/overwrites a global variable (only if the condition is met, though)

mykhal
  • 19,175
  • 11
  • 72
  • 80
0

If get() throws an exception when it's not there, you could do

try:
   q = request.GET.get('q')
   print q
except :
   pass
Rizwan Kassim
  • 7,931
  • 3
  • 24
  • 34
0
config_hash = {}
tmp_dir = ([config_hash[x]  for x in ["tmp_dir"] if config_hash.has_key(x)] or ["tmp"])[0]
print tmp_dir
config_hash["tmp_dir"] = "cat"
tmp_dir = ([config_hash[x]  for x in ["tmp_dir"] if config_hash.has_key(x)] or ["tmp"])[0]
print tmp_dir
louis_xv
  • 1
  • 1
-1

Well, this would be one way

q = request.GET.get('q')
if q:
    print q

A briefer (but not superior, due to the call to print of nothing) way would be

print request.GET.get('q') or '',
Grumdrig
  • 16,588
  • 14
  • 58
  • 69
-1

Simply try:

print(request.GET.get('q', ''))

which basically prints nothing if the first argument is not present (see dict.get).


Alternative solution would be to use a conditional expression in Python:

<expression1> if <condition> else <expression2>

but you'll end up repeating variable twice, for example:

print(request.GET.get('q') if request.GET.get('q') else '')

For variable assignments in loops, check in here.

Community
  • 1
  • 1
kenorb
  • 155,785
  • 88
  • 678
  • 743