Inline assignments

Steven D'Aprano steve at REMOVETHIScyber.com.au
Sun Mar 5 17:25:12 CET 2006


On Sun, 05 Mar 2006 15:09:28 +0100, Fredrik Tolf wrote:

> Hi list!
> 
> I'm relatively new to Python, and one thing I can't seem to get over is
> the lack of in-expression assignments, as present in many other
> languages. I'd really like to know how Python regulars solve the
> problems posed by that.

It is true that there is a whole class of bugs which Python code can't
duplicate because of the lack of in-expression assignments. Somehow
Python programmers manage to muddle along.


> For example, I recently wanted to do this:
> 
> if callable(f = getattr(self, "cmd_" + name)):
> 	# Do something with f
> elif callable(f = getattr(self, "cmdv_" + name)):
> 	# Do something else with f
> 
> However, since I can't do that in Python, I ended up using an extra
> local variable instead, like this:
> 
> f = getattr(self, "cmd_" + name)
> f2 = getattr(self, "cmdv_" + name)
> if callable(f):
> 	# Do something with f
> elif callable(f2):
> 	# Do something with f2

Here is one way:

f = getattr(self, "cmd_" + name)
if callable(f):
    # Do something with f
else:
    f = getattr(self, "cmdv_" + name)
    if callable(f):
        # do something with f


Here is another:

def handle_func(*fs):
    """Do something with the first callable argument."""
    for f in fs:
        if callable(f):
            # do something with f
            break

L = [getattr(self, s + name) for s in ("cmd_", "cmdv_")]
handle_func(L)


Here is a third:

L = map(lambda s, name=name: s+name, ("cmd_", "cmdv_"))
L = map(lambda s, me=self: getattr(me, s), L)
L = filter(callable, L)
# do something with L[0]


Or if you are really nuts, you can convert that last one to a one-liner:

# do something with:-
filter(callable, map(lambda s, me=self: getattr(me, s), map(lambda s,
name=name: s+name, ("cmd_", "cmdv_"))))[0]


Or possibly think about using a different algorithm, maybe something like
this:

class Dispatcher:
    def f1(self): pass
    def f2(self): pass

    def __init__(self):
        self.fmap = {"cmd_name": self.f1, "cmdv_name": self.f2}

    def handle_func(self, s="name"):
        try:
            f = self.fmap["cmd_"+s]
        except KeyError:
            f = self.fmap["cmdv_"+s]
        f()  # or do something with f

Remember that functions and methods are first class objects, you may not
need to pass strings around, then convert the string to a function. Just
pass the function directly.


 
> Another common problem for me are while loops. I would often like to do
> this:
> while (pos = somestring.find("/")) != -1:
> 	part = somestring[:pos]
> 	somestring = somestring[pos + 1:]
> 	# Handle part


If you are splitting pathnames, you may be able to use os.path instead of
reinventing the wheel.

 
> However, that, too, is impossible, and I end up doing something like
> this:
> while True:
> 	pos = somestring.find("/")
> 	if pos == -1: break
> 	# ...
> 
> Which is quite ugly. This might have been a bad example, since
> somestring.split() could be used instead, but it's still valid for other
> situations.

Such as?

Here is another way:

pos = somestring.find("/")
while pos != -1:
    part = somestring[:pos]
    somestring = somestring[pos + 1:]
    # handle part

And another:

while True:
    try:
        pos = somestring.index("/")
    except ValueError:
        break
    part = somestring[:pos]
    somestring = somestring[pos + 1:]
    # handle part


And a third:

start = 0
while True:
    try:
        pos = somestring.index("/", start)
    except ValueError:
        break
    # handle somestring[start:pos]
    start = pos + 1


If you are coming from a Java background, you may consider exceptions to
be a lot of work. In Python, setting up a try...except block is very
lightweight, so long as exceptions are rare. Hitting the except clause is
more work, so you should avoid that idiom if you expect the try to raise
an exception most of the time.



-- 
Steven.




More information about the Python-list mailing list