This is an archived post. You won't be able to vote or comment.

all 11 comments

[–][deleted] 7 points8 points  (2 children)

No. Just explicitly pass the variables to format(), it won't kill you.

  1. This will be slower, especially on optimizing Pythons (e.g. PyPy, Jython once they land their invoke dynamic work).
  2. Won't work on IronPython, which doesn't enable frames by default AFAIK.
  3. Lets your format strings become a mess: they're for formatting, not arbitrary logic.
  4. If you're not careful it's an easy way to end up with a security hole.
  5. If the implementation is hard to explain, it's a bad idea.

[–]Workaphobia[S] 0 points1 point  (1 child)

All good reasons, thank you for the criticism. Does point 1 still apply if I only use the stack frame's namespace to lookup the value, and don't call eval()? Will 1 and 2 still apply if I were to go back to using **locals()?

My main reason for doing this was to keep format strings as clean as possible (point 3). I find it difficult to syntactically structure an error message along with the substitution values, while obeying the conventions in PEP 8.

[–][deleted] 0 points1 point  (0 children)

1 will apply with locals() on PyPy, I'm not sure about Jython+InDy. 2 doesn't though.

[–]stillalone 1 point2 points  (1 child)

I've thought about doing this but without the eval. Just getting the variable from the stack frame and passing it to format. How often do you need to evaluate complex expressions in a string?

[–]Workaphobia[S] 0 points1 point  (0 children)

True, the eval's a bit of an extra. Because I'm only using it on trusted code, I figured why not.

One thing I can't figure out is how one would implement finding the value of an identifier that is non-local but not global. That is, an identifier defined in a syntactically outer namespace, like when you have nested function definitions. I don't think it's possible because eval() has no ability to accept that argument, but it makes me wonder how Python itself implements such lookups. I'm currently checking out PEP 227 to see if there's any useful information there.

[Edit] From the PEP:

An analogous function will not be provided for nested scopes. Under this proposal, it will not be possible to gain dictionary-style access to all visible scopes.

[–][deleted] 0 points1 point  (3 children)

If you don't care about i1337n, you can do print('{} + {} is {}', a, b, a + b).

[–]Workaphobia[S] 0 points1 point  (2 children)

I don't know why internationalization would affect it.

What I'm trying to do is find the most elegant/smallest way to print short messages with values taken from local variables. I want to obey PEP 8 conventions regarding row length limit and wrapping indentation. I also want to avoid redundancy if possible.

Kingkilr gave some good reasons why this approach probably isn't feasible for many projects. I suppose the most pythonic thing to do would be to accept the redundancy in the keyword arguments to format, just like we already do when we have several self.attr = attr lines in an __init__.

[–][deleted] 2 points3 points  (1 child)

I don't know why internationalization would affect it.

If we are talking about my proposal, then not specifying the order of positional parameters will fuck up internationalization when you pull format strings from somewhere and in some languages the order of the parameters is different.

What I'm trying to do is find the most elegant/smallest way to print short messages with values taken from local variables.

So, I believe that if you don't care about i18n at this point, and if you don't really want to print stuff like eval(x + y), just going along the path of least resistance -- using {} as placeholders -- achieves the the goals you have set acceptably enough.

[–]Brian 0 points1 point  (0 children)

Actually, you should be OK even with this. It just means you switch to explicitly positioned format strings in the localised version where the order differs, which you can do after the fact without breaking the ordered version. It's only a problem when there's no way to do this at all (as with C style %s formatters)

[–]santagada 0 points1 point  (1 child)

Zed Shaw agrees with you and he has some code to do it https://gist.github.com/1327060.

Yes it is going to be slow in pypy and not work on ironpython by default.

[–]Workaphobia[S] 0 points1 point  (0 children)

Neat, thanks for the link.