22
votes

Is there a way to format with the new format syntax a string from a function call? for example:

"my request url was {0.get_full_path()}".format(request)

so it calls the function get_full_path function inside the string and not as a parameter in the format function.

EDIT: Here is another example that will probably show my frustration better, this is what I would like:

"{0.full_name()} {0.full_last_name()} and my nick name is {0.full_nick_name()}".format(user)

this is what I want to avoid:

"{0} and {1} and my nick name is {2}".format(user.full_name(), user.full_last_name(), user.full_nick_name())
5
No, you can't. What difference will it make though doing "my request url was {0}".format(request.get_full_path())? - user2555451
what I want to avoid is something like this: "my request url was {0} and my post was {1}".format(request.get_full_path(), request.POST). I want to use the same object only once - Hassek
What's bad about using the same object twice? - Simeon Visser
In fact, by doing it like you want, you're still using the same object twice. - Paulo Bu
@nealmcb thanks for commenting, what I was looking got implemented in python 3.6 which is very exciting. - Hassek

5 Answers

14
votes

Python 3.6 adds literal string interpolation, which is written with an f prefix. See PEP 0498 -- Literal String Interpolation.

This allows one to write

>>> x = 'hello'
>>> s = f'{x}'
>>> print(s)
hello

It should be noted that these are not actual strings, but represent code that evaluates to a string each time. In the above example, s will be of type str, with value 'hello'. You can't pass an f-string around, since it will be evaluated to the result str before being used (unlike str.format, but like every other string literal modifier, such as r'hello', b'hello', '''hello'''). (PEP 501 -- General purpose string interpolation (currently deferred) suggests a string literal that will evaluate to an object which can take substitutions later.)

20
votes

Not sure if you can modify the object, but you could modify or wrap the object to make the functions properties. Then they would look like attributes, and you could do it as

class WrapperClass(originalRequest):
    @property
    def full_name(self):
        return super(WrapperClass, self).full_name()

"{0.full_name} {0.full_last_name} and my nick name is {0.full_nick_name}".format(user)

which IS legal.

8
votes

Python does not directly support variable interpolation. This means that it lacks certain functionality (namely, function calling in strings) which other languages support.

So, there isn't really anything to say here other than no, you can't do that. That's just not how Python's formatting syntax works.

The best you have is this:

"my request url was {0}".format(request.get_full_path())
1
votes

What about this very weird thing?

"my request url was %s and my post was %s"\
    % (lambda r: (r.get_full_path(), r.POST))(request)

Explanation:

  1. Classic way of formatting
  2. Lambda function which takes a request and returns a tuple with what you want
  3. Call the lambda inline as arguments for your string.

I still prefer the way you're doing it.

If you want readability you can do this:

path, post = request.get_full_path(), request.POST
"my request url was {} and my post was {}".format(path, post)
1
votes

So summary of methods would be

(base) [1]~ $ cat r.py
# user is dict:
user = {'full_name': 'dict joe'}
print('{0[full_name]}'.format(user))

# user is obj:
class user:
    @property
    def full_name(self):
        return 'attr joe'


print('{0.full_name}'.format(user()))


# Wrapper for arbitray values - as dict or by attr
class Getter:
    def __init__(self, src):
        self.src = src

    def __getitem__(self, k):
        return getattr(self.src, k, 'not found: %s' % k)

    __getattr__ = __getitem__


print('{0[foo]} - {0.full_name}'.format(Getter(user())))
(base) [1]~ $ python r.py
dict joe
attr joe
not found: foo - attr joe