ActiveState Code

Recipe 202234: Assignment in expression


An evil equivalent for C expression: 'while x=f()'.

Python
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
import sys
def set(**kw):
    assert len(kw)==1
    
    a = sys._getframe(1)
    a.f_locals.update(kw)
    return kw.values()[0]

#
# sample
#

A=range(10)

while set(x=A.pop()):
    print x

Discussion

This recipe is an abuse of implementation details which should only be used by debugging tools. So I strongly advise you to not use it in production code.

But it's quite fun and can still be useful for bets.

Comments

  1. 1. At 5:22 p.m. on 29 jul 2003, Andrew Dalke said:

    an alternative using list comprehensions. Again, don't use this in real code.

    >>> a = [1, 2, 9, 0]
    >>> n = iter(a).next
    >>> while [x for x in [n()] if x]:
    ...     print "I have", x
    ...
    I have 1
    I have 2
    I have 9
    >>>
    
  2. 2. At 12:35 a.m. on 25 may 2005, Raymond Hettinger said:

    Very nice.

  3. 3. At 2:08 p.m. on 22 jun 2005, Frank P Mora said:

    Alternative, safer methods.

    Assignment statements and statements in general are anathema to
    purely functional language and Lisp programmers but are sometimes
    but rarely required. Assignment in an expression can also be achieved
    like this:
    
    >>> ## Directly with the expressly designed function:
    >>> import __main__
    >>> setattr(__main__,'aaa', 300)
    >>> aaa
    300
    >>> ## Or again with the update method and dictionaries
    >>> locals().update({'aaa2':200})
    >>> aaa2
    200
    >>> globals().update({'aaa4':500})
    >>> aaa4
    500
    >>> __main__.__dict__.update({'aaa5':600})
    >>> aaa5
    600
    >>> ## Or with the update method and a list of 2-tuples
    >>> t= ('bbb',6)
    >>> locals().update([t])
    >>> bbb
    6
    >>> ## Or many at once
    >>> k= "v1 v2 v3 v4 v5"
    >>> v= "red blu grn yel brn"
    >>> locals().update(zip( *[ l.split() for l in (k,v)] ))
    >>> fmt= "The 5 color codes are %s, %s, %s, %s and %s"
    >>> print fmt % (v1,v2,v3,v4,v5)
    The 5 color codes are red, blu, grn, yel and brn
    
    Additionally, Dr. David Mertz’s article at
    
    http://gnosis.cx/publish/programming/charming_python_19.html
    
    addresses “Expression Binding” and the Xoltar functional toolkit.
    He offers the following:
    
    >>> list_of_list = [[1,2,3],[4,5,6],[7,8,9]]
    >>> [car_x for x in list_of_list for car_x in (x[0],)]
    [1, 4, 7]
    
    >>> car_x
    7
    
  4. 4. At 12:31 p.m. on 4 jul 2005, Frank P Mora said:

    An alternative necessary function definition and example.

    Sébastien is offering the utility and conciseness of the valued C code
    assignment and *not just* expression assignment as in the preceding
    comment of mine. His utility is of much greater value. It requires a
    function definition by necessity. Here is an alternate function
    definition and yet another example of the usefulness of Sébastien’s
    idea in a compression that calculates factorials.
    
    def sv(vnm='', vval=None, obj=__main__):
        setattr(obj, vnm, vval)
        return vval
    
    >>> import __main__
    >>> fac =4
    >>> [ [ sv('j', j*i) for i in range(2,fac+1) ][-1] for j in (1,) ][0]
    24
    >>> fac=5
    >>> [ [ sv('j', j*i) for i in range(2,fac+1) ][-1] for j in (1,) ][0]
    120
    
    The advantage of the setattr function is the variable variable which
    allows many assignments is a single pass as in the preceding comment.
    It is also the standard method of expression assignment in Python.
    
  5. 5. At 11:09 a.m. on 8 jul 2005, Frank P Mora said:

    But then again.

    The list comprehension can set an initial value as in the latter
    part of the factorial comprehension in my last comment. After being
    set to 1, ‘j’ then becomes the accumulator in the calculation.  If a
    list comprehension can set and return values as in a function can they
    act as accumulators?
    
    It is completly natural for them to do so.
    
    >>> fac =6
    >>> [ [ [ j for j in (j*i,) ][0] for i in range(2,fac+1) ][-1] for j in (1,) ]
    [720]
    
    Wow! Andrew Dalke and David Mertz are my new hero’s. It
    seems like the awe inspiring list comprehension can do just about
    anything. Does Python need function definitions at all much less lambdas?
    
    Maybe just for readability.
    
  6. 6. At 8:46 a.m. on 15 jul 2005, Frank P Mora said:

    Better yet. Steven Bethard pointed out in recipe 436482 that

    [j for j in [1] for i in range(2,fac+1) for j in [j*i]]  [-1]
    
    is equivalent but easier to write, read and use.
    
  7. 7. At 5:17 a.m. on 8 aug 2005, Yair Chuchem said:

    won't work in future versions. In future versions of python - list comprehensions won't expose the inner variable.

  8. 8. At 12:08 a.m. on 24 oct 2005, Connelly Barnes said:

    Perlishly elegant.

  9. 9. At 12:23 p.m. on 16 feb 2006, Moe Aboulkheir said:

    set() is now a builtin. so naming this function "set" is probably not the best idea

  10. 10. At 7:21 p.m. on 17 mar 2006, Moe Aboulkheir said:

    Brittle solution. this won't work if you use set() anywhere but the toplevel.

    e.g:

    def myfunc():
      seq = range(10)
      while set(var=seq.pop()):
        print var
    myfunc()
    

    -> NameError: global name 'var' is not defined

    the behaviour is also different if the loop variable is already bound:

    def myfunc(var='x')
      seq = range(10)
      while set(var=seq.pop()):
        print var
    myfunc()
    

    -> equivalent to "while seq.pop(): print 'x'"

  11. 11. At 3:51 p.m. on 14 aug 2006, Aristotelis Mikropoulos said:

    Another solution. class DataHolder( object ) : """A simple data holder. """

    def __init__( Self , Value = None ) :
        """The constructor.
        """
        Self.Value = Value
    
    def Get( Self ) :
        """Returns the value.
        """
        return Self.Value
    
    def Set( Self , Value ) :
        """Sets the value, as well as
        returns it.
        """
        Self.Value = Value
        return Self.Value
    

Sign in to comment