Pregunta

So this works:

>>> x = 1
>>> y = 2
>>> "a={a}, b={b}, a+b={c}".format( a=x, b=y, c=x+y )

'a=1, b=2, a+b=3'

But this doesn't:

>>> "a={a}, b={b}, a+b={c}".format( a=x, b=y, c=a+b )

NameError: name 'a' is not defined

Is there any way to make the second one work? (Say for example that x and y are function calls and I don't want to recompute them during string formatting)

¿Fue útil?

Solución

The most pythonic (readable, in this case) solution for this is not to use a lambda function, but to cache a and b before the format() call:

a = function_x()
b = function_y()
"a={a}, b={b}, a+b={c}".format(a=a, b=b, c=a+b)

You'll be thankful when looking at the code 6 months from now.

Otros consejos

You can do it with lambda:

def x():
    return 1

def y():
    return 2

>>> "a={a},b={b}, a+b={c}".format(**(lambda a=x(),b=y():{'a':a,'b':b,'c':a+b})())
'a=1,b=2, a+b=3'

this lambda expression is equal to calling predefined function:

def twosumm(a, b):
    return {'a':a, 'b':b, 'c': a+b}

>>> "a={a},b={b}, a+b={c}".format(**twosumm(x(), y()))
'a=1,b=2, a+b=3'

Im also think that it is better to use simple and readable solution and just call x() and y() to get results before formatiing:

>>> a, b = x(), y()
>>> "a={a},b={b}, a+b={c}".format(a=a, b=b, c=a+b)
'a=1,b=2, a+b=3'
x = 1
y = 2

def f(x,y):
    return (x,y,x+y)

print "a={}, b={}, a+b={}".format( *f(x,y) )
# or
print "a={0[0]}, b={0[1]}, a+b={0[2]}".format( f(x,y) )

.

EDIT

I think your question is wrongly written and that induces blurry understanding of it, and then wrong answers.

x and y are not function calls. As they appear, they are just identifiers

If you evoke function calls, I think it is because, in fact, you wish to obtain the result of something like that:
"a={a}, b={b}, a+b={c}".format( a=f(), b=g(), c=f()+g() )
but without having to write c=f()+g() because it implies that f() and g() are each executed two times.

Firstly, it will forever be impossible in Python to write something like .format( a=x, b=y, c=a+b ) or .format( a=f(), b=g(), c=a+b ) where a and b in c=a+b will refer to the same objects as a and b in a=x and b=y.
Because any identifier at the left side of = is in the local namespace of format() while any identifier at the right side of = is in the namespace outside of the function format().
By the way, that's why the identifiers at the left are called parameters and the identifiers at the right are the identifiers of objects passed as arguments.

Secondly, if you want to avoid writing f() two times (one time as an alone argument and one time in the expression f()+g()), and the same for g(), that means you want to write each only one time, as alone argument.
So , if I understand you well, you essentially wish to write something like that:
"a={a}, b={b}, a+b={}".format( a=f(), b=g() )
With current method str.format , this expression with three replacement fields { } is evidently not correct.

No matter, let's redefine the method format ! And then it's possible to pass only two arguments to format().

def fx(): return 101
def fy(): return 45

class Pat(str):
    def __init__(self,s):
        self = s
    def format(self,x,y):
        return str.format(self,x,y,x+y)

p = Pat("a={}, b={}, a+b={}")
print 'p==',p
print p.format(fx(),fy())

result

p : a={}, b={}, a+b={}
a=101, b=45, a+b=146

We can even do more complex things:

from sys import exit

import re
def fx(): return 333
def fy(): return 6

class Pat(str):
    def __init__(self,s):
        for x in re.findall('(?<=\{)[^}]+(?=\})',s):
            if x not in ('A','M'):
                mess = " The replacement field {%s] isn't recognised" % x
                exit(mess)
        self.orig = s
        self.mod = re.sub('\{[^}]*\}','{}',s)
    def modif(self,R):
        it = iter(R)
        return tuple(sum(R) if x=='{A}'
                     else reduce(lambda a,b: a*b, R) if x=='{M}'
                     else next(it)
                     for x in re.findall('(\{[^}]*\})',self))
    def format(self,*args):
        return ''.join(self.mod.format(*self.modif(args)))

print Pat("a={}, b={}, a+b={A}").format(fx(),fy())
print '******************************************'
print Pat("a={}, b={}, c={}, a+b+c={A}").format(fx(),fy(),5000)
print '******************************************'
print Pat("a={}, b={}, a*b={M}").format(fx(),fy())
print '******************************************'
print Pat("a={}, b={}, axb={X}").format(fx(),fy())

result

a=333, b=6, a+b=339
******************************************
a=333, b=6, c=5000, a+b+c=5339
******************************************
a=333, b=6, a*b=1998
******************************************

Traceback (most recent call last):
  File "I:\potoh\ProvPy\Copie de nb.py", line 70, in <module>
    print Pat("a={}, b={}, axb={X}").format(fx(),fy())
  File "I:\potoh\ProvPy\Copie de nb.py", line 51, in __init__
    exit(mess)
SystemExit:  The replacement field {X] isn't recognised
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top