Sorry. I am not trying to start any flame. My scripting experience is from Perl, and I am pretty new in Python.
I just want to check whether I can have the same degree of flexibility as in Python.
In Python :
page = form.getvalue("page")
str = 'This is string : ' + str(int(page) + 1)
In Perl :
$str = 'This is string : ' . ($page + 1);
Is there any way I can avoid int / str conversion?
No, since Python is strongly typed. If you keep page as an int you can do the following:
s = 'This is string : %d' % (page + 1,)
You could use:
mystr = "This string is: %s" % (int(page) + 1)
... the string conversion will be automatic when interpolating into the %s via the % (string formating operator).
You can't get around the need to convert from string to integer. Python will never conflate strings for other data types. In various contexts Python can return the string or "representation" of an object so there are some implicit data casts into string forms. (Under the hood these call .__str__() or .__repr__() object methods).
(While some folks don't like it I personally think the notion of overloading % for string interpolation is far more sensible than a function named sprintf() (if you have a language with operator overloading support anyway).
It looks like page is a str
page = form.getvalue("page")
S = 'This is string : %d'%(int(page)+1)
otherwise make page an int
page = int(form.getvalue("page"))
S = 'This is string : %d'%(page+1)
For the record (and to show that this is nothing to do with strong typing), you can also do crazy stuff like this:
>>> class strplus(int):
... def __radd__(self, other):
... return str(int(other).__add__(self))
...
>>> page = form.getvalue("page")
>>> page + strplus(1)
'3'
No. Python doesn't have the same level of polymorphism as perl. You can print anything, and mix and match floats and ints quite easily, and lots of things (0, '', "", () and []) all end up False, but no, it's not perl in terms of polymorphism.
Related
I am trying to format my validator message with the min/max values in the actual validator.
Here's my Flask Form:
class MyForm(FlaskForm):
example = IntegerField(label=('Integer 0-10'),
validators=[InputRequired(), NumberRange(min=0, max=10, message="must be between %(min)s and %(max)s!")])
Using message="must be between %(min)s and %(max)s!" gives me the expected output:
must be between 0 and 10!
Whereas using message=f"must be between {min} and {max}!" gives me the output:
must be between <built-in function min> and <built-in function max>!
How can I use f-string formatting for my validator message? Is this something related to f-string evaluating at run-time? I don't fully understand the concept behind it, I just know it's the preferred way to string format.
The f-string literal is evaluated immediately, before being passed to IntegerField.
>>> foo = 3
>>> print(f'{foo}')
3
The other string contains literal %(...) substrings which are
used later with the % operator.
>>> print("%(foo)s")
%(foo)s
>>> print("%(foo)s" % {'foo': 3})
3
"must be between %(min)s and %(max)s!" is a string literal that Flask will later perform a search-and-replace on, while f"must be between {min} and {max}!" is a simpler and more efficient way to say "must be between " + str(min) + " and " + str(max) + "!". That evaluates to the string you described.
You must declare such variables, like
min = 1
max = 2
print(f"must be between {min} and {max}!")
But please consider to use somewhat different variable names to not shadow builtin functions.
Ok, I see it now, you wanted to use that as a kind of string template.
Is there a graceful way to get names of named %s-like variables of string object?
Like this:
string = '%(a)s and %(b)s are friends.'
names = get_names(string) # ['a', 'b']
Known alternative ways:
Parse names using regular expression, e.g.:
import re
names = re.findall(r'%\((\w)\)[sdf]', string) # ['a', 'b']
Use .format()-compatible formating and Formatter().parse(string).
How to get the variable names from the string for the format() method
But what about a string with %s-like variables?
PS: python 2.7
In order to answer this question, you need to define "graceful". Several factors might be worth considering:
Is the code short, easy to remember, easy to write, and self explanatory?
Does it reuse the underlying logic (i.e. follow the DRY principle)?
Does it implement exactly the same parsing logic?
Unfortunately, the "%" formatting for strings is implemented in the C routine "PyString_Format" in stringobject.c. This routine does not provide an API or hooks that allow access to a parsed form of the format string. It simply builds up the result as it is parsing the format string. Thus any solution will need to duplicate the parsing logic from the C routine. This means DRY is not followed and exposes any solution to breaking if a change is made to the formatting specification.
The parsing algorithm in PyString_Format includes a fair bit of complexity, including handling nested parentheses in key names, so cannot be fully implemented using regular expression nor using string "split()". Short of copying the C code from PyString_Format and converting it to Python code, I do not see any remotely easy way of correctly extracting the names of the mapping keys under all circumstances.
So my conclusion is that there is no "graceful" way to obtain the names of the mapping keys for a Python 2.7 "%" format string.
The following code uses a regular expression to provide a partial solution that covers most common usage:
import re
class StringFormattingParser(object):
__matcher = re.compile(r'(?<!%)%\(([^)]+)\)[-# +0-9.hlL]*[diouxXeEfFgGcrs]')
#classmethod
def getKeyNames(klass, formatString):
return klass.__matcher.findall(formatString)
# Demonstration of use with some sample format strings
for value in [
'%(a)s and %(b)s are friends.',
'%%(nomatch)i',
'%%',
'Another %(matched)+4.5f%d%% example',
'(%(should_match(but does not))s',
]:
print StringFormattingParser.getKeyNames(value)
# Note the following prints out "really does match"!
print '%(should_match(but does not))s' % {'should_match(but does not)': 'really does match'}
P.S. DRY = Don't Repeat Yourself (https://en.wikipedia.org/wiki/Don%27t_repeat_yourself)
You could also do this:
[y[0] for y in [x.split(')') for x in s.split('%(')] if len(y)>1]
Don't know if this qualifies as graceful in your book, but here's a short function that parses out the names. No error checking, so it will fail for malformed format strings.
def get_names(s):
i = s.find('%')
while 0 <= i < len(s) - 3:
if s[i+1] == '(':
yield(s[i+2:s.find(')', i)])
i = s.find('%', i+2)
string = 'abd %(one) %%(two) 99 %%%(three)'
list(get_names(string) #=> ['one', 'three']
Also, you can reduce this %-task to Formater-solution.
>>> import re
>>> from string import Formatter
>>>
>>> string = '%(a)s and %(b)s are friends.'
>>>
>>> string = re.sub('((?<!%)%(\((\w)\)s))', '{\g<3>}', string)
>>>
>>> tuple(fn[1] for fn in Formatter().parse(string) if fn[1] is not None)
('a', 'b')
>>>
In this case you can use both variants of formating, I suppose.
The regular expression in it depends on what you want.
>>> re.sub('((?<!%)%(\((\w)\)s))', '{\g<3>}', '%(a)s and %(b)s are %(c)s friends.')
'{a} and {b} are {c} friends.'
>>> re.sub('((?<!%)%(\((\w)\)s))', '{\g<3>}', '%(a)s and %(b)s are %%(c)s friends.')
'{a} and {b} are %%(c)s friends.'
>>> re.sub('((?<!%)%(\((\w)\)s))', '{\g<3>}', '%(a)s and %(b)s are %%%(c)s friends.')
'{a} and {b} are %%%(c)s friends.'
I find that in lots of different projects I'm writing a lot of code where I need to evaluate a (moderately complex, possibly costly-to-evaluate) expression and then do something with it (e.g. use it for string formatting), but only if the expression is True/non-None.
For example in lots of places I end up doing something like the following:
result += '%s '%( <complexExpressionForGettingX> ) if <complexExpressionForGettingX> else ''
... which I guess is basically a special-case of the more general problem of wanting to return some function of an expression, but only if that expression is True, i.e.:
f( e() ) if e() else somedefault
but without re-typing the expression (or re-evaluating it, in case it's a costly function call).
Obviously the required logic can be achieved easily enough in various long-winded ways (e.g. by splitting the expression into multiple statements and assigning the expression to a temporary variable), but that's a bit grungy and since this seems like quite a generic problem, and since python is pretty cool (especially for functional stuff) I wondered if there's a nice, elegant, concise way to do it?
My current best options are either defining a short-lived lambda to take care of it (better than multiple statements, but a bit hard to read):
(lambda e: '%s ' % e if e else '')( <complexExpressionForGettingX> )
or writing my own utility function like:
def conditional(expr, formatStringIfTrue, default='')
... but since I'm doing this in lots of different code-bases I'd much rather use a built-in library function or some clever python syntax if such a thing exists
I like one-liners, definitely. But sometimes they are the wrong solution.
In professional software development, if the team size is > 2, you spent more time on understanding code someone else wrote than on writing new code. The one-liners presented here are definitely confusing, so just do two lines (even though you mentioned multiple statements in your post):
X = <complexExpressionForGettingX>
result += '%s '% X if X else ''
This is clear, concise, and everybody immediately understands what's going on here.
Python doesn't have expression scope (Is there a Python equivalent of the Haskell 'let'), presumably because the abuses and confusion of the syntax outweigh the advantages.
If you absolutely have to use an expression scope, the least worst option is to abuse a generator comprehension:
result += next('%s '%(e) if e else '' for e in (<complexExpressionForGettingX>,))
You could define a conditional formatting function once, and use it repeatedly:
def cond_format(expr, form, alt):
if expr:
return form % expr
else:
return alt
Usage:
result += cond_format(<costly_expression>, '%s ', '')
After hearing the responses (thanks guys!) I'm now convinced there's no way to achieve what I want in Python without defining a new function (or lambda function) since that's the only way to introduce a new scope.
For best clarity I decided this needed to be implemented as a reusable function (not lambda) so for the benefit of others, I thought I'd share the function I finally came up with - which is flexible enough to cope with multiple additional format string arguments (in addition to the main argument used to decide whether it's to do the formatting at all); it also comes with pythondoc to show correctness and illustrate usage (if you're not sure how the **kwargs thing works just ignore it, it's just an implementation detail and was the only way I could see to implement an optional defaultValue= kwarg following the variable list of format string arguments).
def condFormat(formatIfTrue, expr, *otherFormatArgs, **kwargs):
""" Helper for creating returning the result of string.format() on a
specified expression if the expressions's bool(expr) is True
(i.e. it's not None, an empty list or an empty string or the number zero),
or return a default string (typically '') if not.
For more complicated cases where the operation on expr is more complicated
than a format string, or where a different condition is required, use:
(lambda e=myexpr: '' if not e else '%s ' % e)
formatIfTrue -- a format string suitable for use with string.format(), e.g.
"{}, {}" or "{1}, {0:d}".
expr -- the expression to evaluate. May be of any type.
defaultValue -- set this keyword arg to override
>>> 'x' + condFormat(', {}.', 'foobar')
'x, foobar.'
>>> 'x' + condFormat(', {}.', [])
'x'
>>> condFormat('{}; {}', 123, 456, defaultValue=None)
'123; 456'
>>> condFormat('{0:,d}; {2:d}; {1:d}', 12345, 678, 9, defaultValue=None)
'12,345; 9; 678'
>>> condFormat('{}; {}; {}', 0, 678, 9, defaultValue=None) == None
True
"""
defaultValue = kwargs.pop('defaultValue','')
assert not kwargs, 'unexpected kwargs: %s'%kwargs
if not bool(expr): return defaultValue
if otherFormatArgs:
return formatIfTrue.format( *((expr,)+otherFormatArgs) )
else:
return formatIfTrue.format(expr)
Presumably, you want to do this repeatedly to build up a string. With a more global view, you might find that filter (or itertools.ifilter) does what you want to the collection of values.
You'll wind up with something like this:
' '.join(map(str, filter(None, <iterable of <complexExpressionForGettingX>>)))
Using None as the first argument for filter indicates to accept any true value. As a concrete example with a simple expression:
>>> ' '.join(map(str, filter(None, range(-3, 3))))
'-3 -2 -1 1 2'
Depending on how you're calculating the values, it may be that an equivalent list or generator comprehension would be more readable.
A quick no-brainer:
some_float = 1234.5678
print '%02d' % some_float # 1234
some_float = 1234.5678
print '{WHAT?}'.format(some_float) # I want 1234 here too
Note: {:.0f} is not an option, because it rounds (returns 1235 in this example).
format(..., int(some_float)) is exactly the thing I'm trying to avoid, please don't suggest that.
It's worth mentioning the built in behavior for how floats are rendered using the raw format strings. If you know in advance where your fractional part lies with respect to 0.5 you can leverage the format string you originally attempted but discovered it fell short from rounding side effects "{:0.0f}". Check out the following examples...
>>> "{:0.0f}".format(1.999)
'2'
>>> "{:0.0f}".format(1.53)
'2'
>>> "{:0.0f}".format(1.500)
'2'
>>> "{:0.0f}".format(1.33)
'1'
>>> "{:0.0f}".format(0.501)
'1'
>>> "{:0.0f}".format(0.5)
'0'
>>> "{:0.0f}".format(0.1)
'0'
>>> "{:0.0f}".format(0.001)
'0'
As you can see there's rounding behavior behind the scenes. In my case where I had a database converting ints to floats I knew I was dealing with a non fractional part in advance and only wanted to render in an html template the int portion of the float as a workaround. Of course if you don't know in advance the fractional part you would need to carry out a truncation operation of some sort first on the float.
It's possible to extend the standard string formatting language by extending the class string.Formatter:
class MyFormatter(Formatter):
def format_field(self, value, format_spec):
if format_spec == 't': # Truncate and render as int
return str(int(value))
return super(MyFormatter, self).format_field(value, format_spec)
MyFormatter().format("{0} {1:t}", "Hello", 4.567) # returns "Hello 4"
This works:
from math import trunc
some_float = 1234.5678
print '{:d}'.format(trunc(some_float))
=> 1234
Or just do this, for that matter:
print trunc(some_float)
=> 1234
I think it's an acceptable answer, it avoids the conversion to int. Notice that in this snippet: '%02d' % some_float an implicit conversion to int is happening, you can't avoid some sort of conversion for printing in the desired format.
This will work too:
some_float = 1234.5678
f = lambda x: str(x)[:str(x).find('.')]
print '{}'.format(f(some_float))
=> 1234
After doing a %timeit test it looks like this is a bit faster than the trunc method.
We have a subclass of str (call it MyStr), and I need to be able to control how str.join interacts with my subclass.
At minimum, a join of all MyStr's should produce another MyStr, and joining of MyStr and "plain" str should throw a TypeError.
Currently, this is what happens: (MyStr subclasses unicode)
>>> m = MyStr(':')
>>> m.join( [MyStr('A'), MyStr('B')] )
u'A:B'
>>> ':'.join( [MyStr('A'), 'B', u'C'] )
u'A:B:C'
Couldn't your class just override join:
class MyStr(unicode):
def join(self, strs):
# your code here
This will at least cover the case of MyStr(...).join(...)
After #bukzor's comment, I looked up how this works, and it looks like join is a C function that always returns a unicode object when called using a unicode seperator.
The code can be seen here. Take a look at the PyUnicode_Join function, especially this line:
res = _PyUnicode_New(res_alloc);
So, the result of PyUnicode_Join will always be an instance of PyUnicode.
The only error case I can see is if the input isn't unicode:
/* Convert item to Unicode. */
if (! PyUnicode_Check(item) && ! PyString_Check(item)) {
PyErr_Format(PyExc_TypeError,
"sequence item %zd: expected string or Unicode,"
" %.80s found",
i, Py_TYPE(item)->tp_name);
goto onError;
}
So I don't think it's possible to make this case fail (at least, not while your object extends from unicode):
':'.join( [MyStr('A'), 'B', u'C'] )
join() is a str method. If you want to end up with a MyStr object afterwards, you to use a MyStr ojbect to do the join.
If you want a TypeError you'll have to not inherit from str and provide all of str's methods yourself (at least the ones that you need). It's quite possible, though, that this will make them largely useless for normal string operations.