28

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())
informatik01
  • 16,038
  • 10
  • 74
  • 104
Hassek
  • 8,715
  • 6
  • 47
  • 59
  • 1
    No, you can't. What difference will it make though doing `"my request url was {0}".format(request.get_full_path())`? –  Nov 05 '13 at 18:31
  • Out of curiosity: why do you need something like that? – aga Nov 05 '13 at 18:31
  • 1
    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 Nov 05 '13 at 18:32
  • 3
    What's bad about using the same object twice? – Simeon Visser Nov 05 '13 at 18:32
  • 1
    In fact, by doing it like you want, you're still using the same object twice. – Paulo Bu Nov 05 '13 at 18:33
  • 1
    @SimeonVisser I just like more a syntax where I don't have to add the same object more than once in the format args – Hassek Nov 05 '13 at 18:41
  • @Hassek: It is just a *reference*. You can assign the same value to another, short name: `r = request`, and use `'...'.format(`r.get_full_path(), r.POST)`. It doesn't matter that much does it? String formatting gives you some handy shortcuts, including indexing and attribute access, but function calling is *not* part of the format. These are not full-blown Python expressions, nor should they be. – Martijn Pieters Nov 05 '13 at 18:52
  • 1
    @MartijnPieters any reason why they shouldn't be? my wild guess is that the format syntax takes the values from the object `__dict__` and that's why they can't call a function. but that doesn't makes it less of a pain to have to add the same object in the args (at least for me). – Hassek Nov 05 '13 at 19:02
  • 1
    @Hassek: The string formatting is parsing a dedicated format. It is not the regular python parser parsing the references, so these are *not* Python expressions. There is no namespace, you could not pass in arguments if you *could* call functions, for example. The attribute and item access is a convenience to simplify many common formatting requirements, but this is *not* meant (nor will it ever be) a full Python expression parser. – Martijn Pieters Nov 05 '13 at 19:19
  • @Hassek: Not being able to call anything has nothing to do with accessing the `__dict__` or anything else. It uses the standard methods to access attributes and items. – Martijn Pieters Nov 05 '13 at 19:20
  • @MartijnPieters I see, very interesting. Thanks for those comments :) – Hassek Nov 05 '13 at 19:27
  • Note that there are two answers that now give good approaches to do what you wanted, and which have more votes that the accepted answer. You might want to give them some official recognition. – nealmcb Mar 22 '17 at 21:30
  • 3
    @nealmcb thanks for commenting, what I was looking got implemented in python 3.6 which is very exciting. – Hassek Mar 22 '17 at 22:27

5 Answers5

20

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.

Community
  • 1
  • 1
Corley Brigman
  • 11,633
  • 5
  • 33
  • 40
  • 1
    It also works with dicts. `'{0[hello]}'.format({'hello':'world'})` results in `'world'`. – leewz Jul 01 '17 at 10:58
  • Yes, this dict retrieval works out-of-the-box without having this WrapperClass, FYI – EpicDavi Jul 28 '17 at 18:54
  • good to know (i thought that was added later), but the question was about calling functions, not really looking up dictionary items. literal string interpolation can do it though.. – Corley Brigman Jul 28 '17 at 21:02
17

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.)

leewz
  • 3,201
  • 1
  • 18
  • 38
8

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())
  • Just made an edit, is not about the request object really. I am almost certain you are right about this, I just would like another syntax option that I guess doesn't exists :( – Hassek Nov 05 '13 at 19:07
1

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)
Paulo Bu
  • 29,294
  • 6
  • 74
  • 73
  • 1
    Yea I think that's just not worth it heh. I am looking more for a syntax that is permited there, for example, to format a datetime you can do `{:%Y}` and it works. – Hassek Nov 05 '13 at 18:46
  • I'm sorry that's all I got :) I don't think Python goes so far. Especially, it's not famous for its string interpolation/formatting anyways :/ – Paulo Bu Nov 05 '13 at 18:49
  • I'll just leave the question open for a while, but my hopes are low on this one :( – Hassek Nov 05 '13 at 18:55
1

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
Red Pill
  • 511
  • 6
  • 15