标签归档:formatting

如何在Python中打破这条漫长的路线?

问题:如何在Python中打破这条漫长的路线?

您将如何格式化这样的长行?我希望宽度不超过80个字符:

logger.info("Skipping {0} because its thumbnail was already in our system as {1}.".format(line[indexes['url']], video.title))

这是我最好的选择吗?

url = "Skipping {0} because its thumbnail was already in our system as {1}."
logger.info(url.format(line[indexes['url']], video.title))

How would you go about formatting a long line such as this? I’d like to get it to no more than 80 characters wide:

logger.info("Skipping {0} because its thumbnail was already in our system as {1}.".format(line[indexes['url']], video.title))

Is this my best option?

url = "Skipping {0} because its thumbnail was already in our system as {1}."
logger.info(url.format(line[indexes['url']], video.title))

回答 0

那是一个开始。在使用长字符串的代码之外定义较长的字符串并不是一个坏习惯。这是分离数据和行为的一种方式。您的第一个选择是通过使字符串文字彼此相邻来隐式地将它们连接在一起:

("This is the first line of my text, "
"which will be joined to a second.")

或使用行尾连续符,这会更脆弱一些,因为这可行:

"This is the first line of my text, " \
"which will be joined to a second."

但这不是:

"This is the first line of my text, " \ 
"which will be joined to a second."

看到不同?没有?好吧,当它是您的代码时,您也不会。

隐式连接的不利之处在于,它仅适用于字符串文字,而不适用于从变量中提取的字符串,因此在进行重构时,事情可能会变得更加复杂。另外,您只能对整个组合字符串进行插值格式化。

另外,您可以使用串联运算符(+)显式加入:

("This is the first line of my text, " + 
"which will be joined to a second.")

正如zen python所说的那样,显式比隐式更好,但这会创建三个字符串而不是一个字符串,并且使用两倍的内存:您已经编写了两个字符串,再加上一个字符串,即两个字符串连接在一起,所以您必须知道何时忽略禅宗。好处是您可以将格式分别应用于每一行中的任何子字符串,也可以应用于位于括号外的全部子字符串。

最后,您可以使用三引号引起来的字符串:

"""This is the first line of my text
which will be joined to a second."""

这通常是我的最爱,尽管它的行为略有不同,因为换行符和后续行中的任何前导空格都会显示在您的最终字符串中。您可以使用转义的反斜杠消除换行符。

"""This is the first line of my text \
which will be joined to a second."""

这具有与上述相同技术相同的问题,因为正确的代码与不正确的代码的区别仅在于不可见的空格。

哪一个“最佳”取决于您的特定情况,但答案不只是简单的美学,而是微妙的不同行为之一。

That’s a start. It’s not a bad practice to define your longer strings outside of the code that uses them. It’s a way to separate data and behavior. Your first option is to join string literals together implicitly by making them adjacent to one another:

("This is the first line of my text, "
"which will be joined to a second.")

Or with line ending continuations, which is a little more fragile, as this works:

"This is the first line of my text, " \
"which will be joined to a second."

But this doesn’t:

"This is the first line of my text, " \ 
"which will be joined to a second."

See the difference? No? Well you won’t when it’s your code either.

The downside to implicit joining is that it only works with string literals, not with strings taken from variables, so things can get a little more hairy when you refactor. Also, you can only interpolate formatting on the combined string as a whole.

Alternatively, you can join explicitly using the concatenation operator (+):

("This is the first line of my text, " + 
"which will be joined to a second.")

Explicit is better than implicit, as the zen of python says, but this creates three strings instead of one, and uses twice as much memory: there are the two you have written, plus one which is the two of them joined together, so you have to know when to ignore the zen. The upside is you can apply formatting to any of the substrings separately on each line, or to the whole lot from outside the parentheses.

Finally, you can use triple-quoted strings:

"""This is the first line of my text
which will be joined to a second."""

This is often my favorite, though its behavior is slightly different as the newline and any leading whitespace on subsequent lines will show up in your final string. You can eliminate the newline with an escaping backslash.

"""This is the first line of my text \
which will be joined to a second."""

This has the same problem as the same technique above, in that correct code only differs from incorrect code by invisible whitespace.

Which one is “best” depends on your particular situation, but the answer is not simply aesthetic, but one of subtly different behaviors.


回答 1

连续字符串文字由编译器连接,带括号的表达式被视为单行代码:

logger.info("Skipping {0} because it's thumbnail was "
  "already in our system as {1}.".format(line[indexes['url']],
  video.title))

Consecutive string literals are joined by the compiler, and parenthesized expressions are considered to be a single line of code:

logger.info("Skipping {0} because it's thumbnail was "
  "already in our system as {1}.".format(line[indexes['url']],
  video.title))

回答 2

我个人不喜欢挂空块,所以我将其格式化为:

logger.info(
    'Skipping {0} because its thumbnail was already in our system as {1}.'
    .format(line[indexes['url']], video.title)
)

通常,我不会太费力地使代码完全适合80列行。可以将线长降低到合理的水平,但是硬80的限制已成过去。

Personally I dislike hanging open blocks, so I’d format it as:

logger.info(
    'Skipping {0} because its thumbnail was already in our system as {1}.'
    .format(line[indexes['url']], video.title)
)

In general I wouldn’t bother struggle too hard to make code fit exactly within a 80-column line. It’s worth keeping line length down to reasonable levels, but the hard 80 limit is a thing of the past.


回答 3

您可以使用textwrap模块将其分成多行

import textwrap
str="ABCDEFGHIJKLIMNO"
print("\n".join(textwrap.wrap(str,8)))

ABCDEFGH
IJKLIMNO

文档中

文本换行。wrap(text [,width [,…]])
将单个段落包装在文本(字符串)中,因此每一行最多为宽度字符。返回输出行列表,不带最终换行符。

可选的关键字参数与的实例属性相对应TextWrapper,如下所述。宽度默认为70

有关TextWrapper.wrap()wrap()行为的更多详细信息,请参见方法。

You can use textwrap module to break it in multiple lines

import textwrap
str="ABCDEFGHIJKLIMNO"
print("\n".join(textwrap.wrap(str,8)))

ABCDEFGH
IJKLIMNO

From the documentation:

textwrap.wrap(text[, width[, …]])
Wraps the single paragraph in text (a string) so every line is at most width characters long. Returns a list of output lines, without final newlines.

Optional keyword arguments correspond to the instance attributes of TextWrapper, documented below. width defaults to 70.

See the TextWrapper.wrap() method for additional details on how wrap() behaves.


回答 4

对于也尝试调用.format()长字符串并且在不中断后续.format(调用的情况下无法使用某些最受欢迎的字符串包装技术的任何人,您可以使用str.format("", 1, 2)代替"".format(1, 2)。这使您可以使用任何喜欢的技术来断开字符串。例如:

logger.info("Skipping {0} because its thumbnail was already in our system as {1}.".format(line[indexes['url']], video.title))

logger.info(str.format(("Skipping {0} because its thumbnail was already"
+ "in our system as {1}"), line[indexes['url']], video.title))

否则,唯一的可能性就是使用行尾延续,我个人并不喜欢。

For anyone who is also trying to call .format() on a long string, and is unable to use some of the most popular string wrapping techniques without breaking the subsequent .format( call, you can do str.format("", 1, 2) instead of "".format(1, 2). This lets you break the string with whatever technique you like. For example:

logger.info("Skipping {0} because its thumbnail was already in our system as {1}.".format(line[indexes['url']], video.title))

can be

logger.info(str.format(("Skipping {0} because its thumbnail was already"
+ "in our system as {1}"), line[indexes['url']], video.title))

Otherwise, the only possibility is using line ending continuations, which I personally am not a fan of.


熊猫:设置编号。最大行数

问题:熊猫:设置编号。最大行数

我在查看以下内容时遇到问题DataFrame

n = 100
foo = DataFrame(index=range(n))
foo['floats'] = np.random.randn(n)
foo

问题是它不会在ipython笔记本中默认情况下不打印所有行,但是我必须切片才能查看结果行。甚至以下选项也不会更改输出:

pd.set_option('display.max_rows', 500)

有谁知道如何显示整个数组?

I have a problem viewing the following DataFrame:

n = 100
foo = DataFrame(index=range(n))
foo['floats'] = np.random.randn(n)
foo

The problem is that it does not print all rows per default in ipython notebook, but I have to slice to view the resulting rows. Even the following option does not change the output:

pd.set_option('display.max_rows', 500)

Does anyone know how to display the whole array?


回答 0

设置display.max_rows

pd.set_option('display.max_rows', 500)

对于较早版本的熊猫(<= 0.11.0),您需要同时更改display.heightdisplay.max_rows

pd.set_option('display.height', 500)
pd.set_option('display.max_rows', 500)

另请参阅pd.describe_option('display')

您只能一次临时设置一个选项,如下所示:

from IPython.display import display
with pd.option_context('display.max_rows', 100, 'display.max_columns', 10):
    display(df) #need display to show the dataframe when using with in jupyter
    #some pandas stuff

您还可以将选项重置为默认值,如下所示:

pd.reset_option('display.max_rows')

然后将它们全部重置:

pd.reset_option('all')

Set display.max_rows:

pd.set_option('display.max_rows', 500)

For older versions of pandas (<=0.11.0) you need to change both display.height and display.max_rows.

pd.set_option('display.height', 500)
pd.set_option('display.max_rows', 500)

See also pd.describe_option('display').

You can set an option only temporarily for this one time like this:

from IPython.display import display
with pd.option_context('display.max_rows', 100, 'display.max_columns', 10):
    display(df) #need display to show the dataframe when using with in jupyter
    #some pandas stuff

You can also reset an option back to its default value like this:

pd.reset_option('display.max_rows')

And reset all of them back:

pd.reset_option('all')


回答 1

就个人而言,我喜欢直接使用赋值语句设置选项,因为iPython使得通过制表符补全很容易找到。我很难记住确切的选项名称是什么,因此此方法对我有用。

例如,我要记住的是,它始于 pd.options

pd.options.<TAB>

大多数选项在 display

pd.options.display.<TAB>

从这里,我通常输出如下所示的当前值:

pd.options.display.max_rows
60

然后,将其设置为我想要的样子:

pd.options.display.max_rows = 100

另外,您应该注意用于选项的上下文管理器,它可以在代码块内临时设置选项。将选项名称作为字符串传递,后跟所需的值。您可以在同一行中传递任意数量的选项:

with pd.option_context('display.max_rows', 100, 'display.max_columns', 10):
    some pandas stuff

您还可以将选项重置为默认值,如下所示:

pd.reset_option('display.max_rows')

然后将它们全部重置:

pd.reset_option('all')

通过设置选项仍然非常好pd.set_option。我只是发现直接使用属性更容易,并且对get_option和的需求也更少set_option

Personally, I like setting the options directly with an assignment statement as it is easy to find via tab completion thanks to iPython. I find it hard to remember what the exact option names are, so this method works for me.

For instance, all I have to remember is that it begins with pd.options

pd.options.<TAB>

Most of the options are available under display

pd.options.display.<TAB>

From here, I usually output what the current value is like this:

pd.options.display.max_rows
60

I then set it to what I want it to be:

pd.options.display.max_rows = 100

Also, you should be aware of the context manager for options, which temporarily sets the options inside of a block of code. Pass in the option name as a string followed by the value you want it to be. You may pass in any number of options in the same line:

with pd.option_context('display.max_rows', 100, 'display.max_columns', 10):
    some pandas stuff

You can also reset an option back to its default value like this:

pd.reset_option('display.max_rows')

And reset all of them back:

pd.reset_option('all')

It is still perfectly good to set options via pd.set_option. I just find using the attributes directly is easier and there is less need for get_option and set_option.


回答 2

此注释此答案中已经指出了一点,但是我将尝试对该问题给出更直接的答案:

from IPython.display import display
import numpy as np
import pandas as pd

n = 100
foo = pd.DataFrame(index=range(n))
foo['floats'] = np.random.randn(n)

with pd.option_context("display.max_rows", foo.shape[0]):
    display(foo)

从pandas 0.13.1(pandas 0.13.1发行说明)开始,pandas.option_context可用。根据

[it]允许您执行带有一组选项的代码块,当您退出with块时,这些选项会还原为先前的设置。

It was already pointed in this comment and in this answer, but I’ll try to give a more direct answer to the question:

from IPython.display import display
import numpy as np
import pandas as pd

n = 100
foo = pd.DataFrame(index=range(n))
foo['floats'] = np.random.randn(n)

with pd.option_context("display.max_rows", foo.shape[0]):
    display(foo)

pandas.option_context is available since pandas 0.13.1 (pandas 0.13.1 release notes). According to this,

[it] allow[s] you to execute a codeblock with a set of options that revert to prior settings when you exit the with block.


回答 3

正如@hanleyhansen在评论中指出的那样,从0.18.1版本开始,该display.height选项已被弃用,并说“使用display.max_rows代替”。因此,您只需要像这样配置它:

pd.set_option('display.max_rows', 500)

请参阅发行说明-pandas 0.18.1文档

现在已弃用的display.height,display.width仅是一个格式选项,无法控制摘要的触发,类似于<0.11.0。

As @hanleyhansen noted in a comment, as of version 0.18.1, the display.height option is deprecated, and says “use display.max_rows instead”. So you just have to configure it like this:

pd.set_option('display.max_rows', 500)

See the Release Notes — pandas 0.18.1 documentation:

Deprecated display.height, display.width is now only a formatting option does not control triggering of summary, similar to < 0.11.0.


回答 4

pd.set_option('display.max_rows', 500)
df

不工作的Jupyter!
而是使用:

pd.set_option('display.max_rows', 500)
df.head(500)
pd.set_option('display.max_rows', 500)
df

Does not work in Jupyter!
Instead use:

pd.set_option('display.max_rows', 500)
df.head(500)

回答 5

正如这个答案类似的问题,不存在需要破解的设置。编写起来要简单得多:

print(foo.to_string())

As in this answer to a similar question, there is no need to hack settings. It is much simpler to write:

print(foo.to_string())

如何将YAML格式的数据写入文件?

问题:如何将YAML格式的数据写入文件?

我需要使用Python将以下数据写入yaml文件:

{A:a, B:{C:c, D:d, E:e}} 

即字典中的字典。我该如何实现?

I need to write the below data to yaml file using Python:

{A:a, B:{C:c, D:d, E:e}} 

i.e., dictionary in a dictionary. How can I achieve this?


回答 0

import yaml

data = dict(
    A = 'a',
    B = dict(
        C = 'c',
        D = 'd',
        E = 'e',
    )
)

with open('data.yml', 'w') as outfile:
    yaml.dump(data, outfile, default_flow_style=False)

default_flow_style=False参数对于产生所需的格式(流样式)是必需的,否则对于嵌套集合,它将产生块样式:

A: a
B: {C: c, D: d, E: e}
import yaml

data = dict(
    A = 'a',
    B = dict(
        C = 'c',
        D = 'd',
        E = 'e',
    )
)

with open('data.yml', 'w') as outfile:
    yaml.dump(data, outfile, default_flow_style=False)

The default_flow_style=False parameter is necessary to produce the format you want (flow style), otherwise for nested collections it produces block style:

A: a
B: {C: c, D: d, E: e}

回答 1

链接到PyYAML文档,其中显示了default_flow_style参数的差异。要将其以块模式写入文件(通常更具可读性):

d = {'A':'a', 'B':{'C':'c', 'D':'d', 'E':'e'}}
with open('result.yml', 'w') as yaml_file:
    yaml.dump(d, yaml_file, default_flow_style=False)

生成:

A: a
B:
  C: c
  D: d
  E: e

Link to the PyYAML documentation showing the difference for the default_flow_style parameter. To write it to a file in block mode (often more readable):

d = {'A':'a', 'B':{'C':'c', 'D':'d', 'E':'e'}}
with open('result.yml', 'w') as yaml_file:
    yaml.dump(d, yaml_file, default_flow_style=False)

produces:

A: a
B:
  C: c
  D: d
  E: e

将Python字符串格式化与列表一起使用

问题:将Python字符串格式化与列表一起使用

s在Python 2.6.5中构造了一个字符串,该字符串将具有不同数量的%s令牌,这些令牌与list中的条目数匹配x。我需要写出格式化的字符串。以下内容不起作用,但表示我要执行的操作。在此示例中,有三个%s标记,并且列表具有三个条目。

s = '%s BLAH %s FOO %s BAR'
x = ['1', '2', '3']
print s % (x)

我希望输出字符串为:

1 BLAH 2 FOO 3 BAR

I construct a string s in Python 2.6.5 which will have a varying number of %s tokens, which match the number of entries in list x. I need to write out a formatted string. The following doesn’t work, but indicates what I’m trying to do. In this example, there are three %s tokens and the list has three entries.

s = '%s BLAH %s FOO %s BAR'
x = ['1', '2', '3']
print s % (x)

I’d like the output string to be:

1 BLAH 2 FOO 3 BAR


回答 0

print s % tuple(x)

代替

print s % (x)
print s % tuple(x)

instead of

print s % (x)

回答 1

您应该看一下python 的format方法。然后,您可以像这样定义格式字符串:

>>> s = '{0} BLAH BLAH {1} BLAH {2} BLAH BLIH BLEH'
>>> x = ['1', '2', '3']
>>> print s.format(*x)
'1 BLAH BLAH 2 BLAH 3 BLAH BLIH BLEH'

You should take a look to the format method of python. You could then define your formatting string like this :

>>> s = '{0} BLAH BLAH {1} BLAH {2} BLAH BLIH BLEH'
>>> x = ['1', '2', '3']
>>> print s.format(*x)
'1 BLAH BLAH 2 BLAH 3 BLAH BLIH BLEH'

回答 2

在此资源页面之后,如果x的长度变化,我们可以使用:

', '.join(['%.2f']*len(x))

为列表中的每个元素创建一个占位符x。这是示例:

x = [1/3.0, 1/6.0, 0.678]
s = ("elements in the list are ["+', '.join(['%.2f']*len(x))+"]") % tuple(x)
print s
>>> elements in the list are [0.33, 0.17, 0.68]

Following this resource page, if the length of x is varying, we can use:

', '.join(['%.2f']*len(x))

to create a place holder for each element from the list x. Here is the example:

x = [1/3.0, 1/6.0, 0.678]
s = ("elements in the list are ["+', '.join(['%.2f']*len(x))+"]") % tuple(x)
print s
>>> elements in the list are [0.33, 0.17, 0.68]

回答 3

这是一行代码。一个临时的答案,使用带有print()的format来迭代列表。

怎么样(Python 3.x):

sample_list = ['cat', 'dog', 'bunny', 'pig']
print("Your list of animals are: {}, {}, {} and {}".format(*sample_list))

在此处阅读有关使用format()的文档。

Here is a one liner. A little improvised answer using format with print() to iterate a list.

How about this (python 3.x):

sample_list = ['cat', 'dog', 'bunny', 'pig']
print("Your list of animals are: {}, {}, {} and {}".format(*sample_list))

Read the docs here on using format().


回答 4

由于我刚刚学到了这个很酷的东西(从格式字符串中索引到列表中),所以我添加了这个老问题。

s = '{x[0]} BLAH {x[1]} FOO {x[2]} BAR'
x = ['1', '2', '3']
print (s.format (x=x))

输出:

1 BLAH 2 FOO 3 BAR

但是,我仍然没有弄清楚如何进行切片(在格式字符串'"{x[2:4]}".format...中),并且很想弄清楚是否有人有想法,但是我怀疑您根本无法做到这一点。

Since I just learned about this cool thing(indexing into lists from within a format string) I’m adding to this old question.

s = '{x[0]} BLAH {x[1]} FOO {x[2]} BAR'
x = ['1', '2', '3']
print (s.format (x=x))

Output:

1 BLAH 2 FOO 3 BAR

However, I still haven’t figured out how to do slicing(inside of the format string '"{x[2:4]}".format...,) and would love to figure it out if anyone has an idea, however I suspect that you simply cannot do that.


回答 5

这是一个有趣的问题!处理可变长度列表的另一种方法是构建一个充分利用该.format方法和列表拆包的功能。在下面的示例中,我不使用任何特殊的格式,但是可以轻松地对其进行更改以满足您的需求。

list_1 = [1,2,3,4,5,6]
list_2 = [1,2,3,4,5,6,7,8]

# Create a function that can apply formatting to lists of any length:
def ListToFormattedString(alist):
    # Create a format spec for each item in the input `alist`.
    # E.g., each item will be right-adjusted, field width=3.
    format_list = ['{:>3}' for item in alist] 

    # Now join the format specs into a single string:
    # E.g., '{:>3}, {:>3}, {:>3}' if the input list has 3 items.
    s = ','.join(format_list)

    # Now unpack the input list `alist` into the format string. Done!
    return s.format(*alist)

# Example output:
>>>ListToFormattedString(list_1)
'  1,  2,  3,  4,  5,  6'
>>>ListToFormattedString(list_2)
'  1,  2,  3,  4,  5,  6,  7,  8'

This was a fun question! Another way to handle this for variable length lists is to build a function that takes full advantage of the .format method and list unpacking. In the following example I don’t use any fancy formatting, but that can easily be changed to suit your needs.

list_1 = [1,2,3,4,5,6]
list_2 = [1,2,3,4,5,6,7,8]

# Create a function that can apply formatting to lists of any length:
def ListToFormattedString(alist):
    # Create a format spec for each item in the input `alist`.
    # E.g., each item will be right-adjusted, field width=3.
    format_list = ['{:>3}' for item in alist] 

    # Now join the format specs into a single string:
    # E.g., '{:>3}, {:>3}, {:>3}' if the input list has 3 items.
    s = ','.join(format_list)

    # Now unpack the input list `alist` into the format string. Done!
    return s.format(*alist)

# Example output:
>>>ListToFormattedString(list_1)
'  1,  2,  3,  4,  5,  6'
>>>ListToFormattedString(list_2)
'  1,  2,  3,  4,  5,  6,  7,  8'

回答 6

与@neobot的答案相同,但更加现代和简洁。

>>> l = range(5)
>>> " & ".join(["{}"]*len(l)).format(*l)
'0 & 1 & 2 & 3 & 4'

The same as @neobot’s answer but a little more modern and succinct.

>>> l = range(5)
>>> " & ".join(["{}"]*len(l)).format(*l)
'0 & 1 & 2 & 3 & 4'

回答 7

x = ['1', '2', '3']
s = f"{x[0]} BLAH {x[1]} FOO {x[2]} BAR"
print(s)

输出是

1 BLAH 2 FOO 3 BAR
x = ['1', '2', '3']
s = f"{x[0]} BLAH {x[1]} FOO {x[2]} BAR"
print(s)

The output is

1 BLAH 2 FOO 3 BAR

为什么带有f字符串的f'{{{74}}}’与f'{{74}}’相同?

问题:为什么带有f字符串的f'{{{74}}}’与f'{{74}}’相同?

f字符串可从Python 3.6中获得,对于格式化字符串非常有用:

>>> n='you'
>>> f'hello {n}, how are you?'
'hello you, how are you?'

Python 3的f字符串中阅读有关它们的更多信息:改进的字符串格式语法(指南)。我发现了一个有趣的模式:

请注意,使用三重花括号将导致字符串中只有一个大括号:

>>> f"{{{74}}}"
'{74}'

但是,如果使用的括号多于三个,则可以显示更多的括号:

>>> f"{{{{74}}}}"
'{{74}}'

确实是这样:

>>> f'{74}'
'74'

>>> f'{{74}}'
'{74}'

现在,如果我们从两个传递{到三个,结果将是相同的:

>>> f'{{{74}}}'
'{74}'           # same as f'{{74}}' !

因此,我们最多需要4个!({{{{)获得两个大括号作为输出:

>>> f'{{{{74}}}}'
'{{74}}'

为什么是这样?从那时起,用两个花括号使Python需要一个额外的花括号会发生什么?

f-Strings are available from Python 3.6 and are very useful for formatting strings:

>>> n='you'
>>> f'hello {n}, how are you?'
'hello you, how are you?'

Reading more about them in Python 3’s f-Strings: An Improved String Formatting Syntax (Guide). I found an interesting pattern:

Note that using triple braces will result in there being only single braces in your string:

>>> f"{{{74}}}"
'{74}'

However, you can get more braces to show if you use more than triple braces:

>>> f"{{{{74}}}}"
'{{74}}'

And this is exactly the case:

>>> f'{74}'
'74'

>>> f'{{74}}'
'{74}'

Now if we pass from two { to three, the result is the same:

>>> f'{{{74}}}'
'{74}'           # same as f'{{74}}' !

So we need up to 4! ({{{{) to get two braces as an output:

>>> f'{{{{74}}}}'
'{{74}}'

Why is this? What happens with two braces to have Python require an extra one from that moment on?


回答 0

双括号逃脱牙套,所以没有发生插值:{{{}}}。并74保持不变的字符串,'74'

与三括号相同,外部的双括号被转义。另一方面,内部括号导致值的常规字符串插值74

也就是说,该字符串f'{{{74}}}'等效于f'{{ {74} }}',但没有空格(或等效于'{' + f'{74}' + '}')。

在用变量替换数字常量时,您可以看到区别:

In [1]: x = 74

In [2]: f'{{x}}'
Out[2]: '{x}'

In [3]: f'{{{x}}}'
Out[3]: '{74}'

Double braces escape the braces, so that no interpolation happens: {{{, and }}}. And 74 remains an unchanged string, '74'.

With triple braces, the outer double braces are escaped, same as above. The inner braces, on the other hand, lead to regular string interpolation of the value 74.

That is, the string f'{{{74}}}' is equivalent to f'{{ {74} }}', but without spaces (or, equivalently, to '{' + f'{74}' + '}').

You can see the difference when replacing the numeric constant by a variable:

In [1]: x = 74

In [2]: f'{{x}}'
Out[2]: '{x}'

In [3]: f'{{{x}}}'
Out[3]: '{74}'

格式使用标准json模块浮动

问题:格式使用标准json模块浮动

我正在使用python 2.6中的标准json模块来序列化float列表。但是,我得到这样的结果:

>>> import json
>>> json.dumps([23.67, 23.97, 23.87])
'[23.670000000000002, 23.969999999999999, 23.870000000000001]'

我希望浮点数仅使用两位十进制数字进行格式化。输出应如下所示:

>>> json.dumps([23.67, 23.97, 23.87])
'[23.67, 23.97, 23.87]'

我尝试定义自己的JSON Encoder类:

class MyEncoder(json.JSONEncoder):
    def encode(self, obj):
        if isinstance(obj, float):
            return format(obj, '.2f')
        return json.JSONEncoder.encode(self, obj)

这适用于唯一的float对象:

>>> json.dumps(23.67, cls=MyEncoder)
'23.67'

但是对于嵌套对象失败:

>>> json.dumps([23.67, 23.97, 23.87])
'[23.670000000000002, 23.969999999999999, 23.870000000000001]'

我不想有外部依赖性,所以我更喜欢使用标准的json模块。

我该如何实现?

I am using the standard json module in python 2.6 to serialize a list of floats. However, I’m getting results like this:

>>> import json
>>> json.dumps([23.67, 23.97, 23.87])
'[23.670000000000002, 23.969999999999999, 23.870000000000001]'

I want the floats to be formated with only two decimal digits. The output should look like this:

>>> json.dumps([23.67, 23.97, 23.87])
'[23.67, 23.97, 23.87]'

I have tried defining my own JSON Encoder class:

class MyEncoder(json.JSONEncoder):
    def encode(self, obj):
        if isinstance(obj, float):
            return format(obj, '.2f')
        return json.JSONEncoder.encode(self, obj)

This works for a sole float object:

>>> json.dumps(23.67, cls=MyEncoder)
'23.67'

But fails for nested objects:

>>> json.dumps([23.67, 23.97, 23.87])
'[23.670000000000002, 23.969999999999999, 23.870000000000001]'

I don’t want to have external dependencies, so I prefer to stick with the standard json module.

How can I achieve this?


回答 0

注:这并没有任何最新版本的Python的工作。

不幸的是,我相信您必须通过Monkey补丁来做到这一点(我认为这表明标准库json软件包中存在设计缺陷)。例如,此代码:

import json
from json import encoder
encoder.FLOAT_REPR = lambda o: format(o, '.2f')
    
print(json.dumps(23.67))
print(json.dumps([23.67, 23.97, 23.87]))

发出:

23.67
[23.67, 23.97, 23.87]

如您所愿。显然,应该有一种覆盖的结构化方法,FLOAT_REPR以便您可以控制浮点数的每个表示形式;但不幸的是,这不是json包装的设计方式:-(。

Note: This does not work in any recent version of Python.

Unfortunately, I believe you have to do this by monkey-patching (which, to my opinion, indicates a design defect in the standard library json package). E.g., this code:

import json
from json import encoder
encoder.FLOAT_REPR = lambda o: format(o, '.2f')
    
print(json.dumps(23.67))
print(json.dumps([23.67, 23.97, 23.87]))

emits:

23.67
[23.67, 23.97, 23.87]

as you desire. Obviously, there should be an architected way to override FLOAT_REPR so that EVERY representation of a float is under your control if you wish it to be; but unfortunately that’s not how the json package was designed:-(.


回答 1

import simplejson
    
class PrettyFloat(float):
    def __repr__(self):
        return '%.15g' % self
    
def pretty_floats(obj):
    if isinstance(obj, float):
        return PrettyFloat(obj)
    elif isinstance(obj, dict):
        return dict((k, pretty_floats(v)) for k, v in obj.items())
    elif isinstance(obj, (list, tuple)):
        return list(map(pretty_floats, obj))
    return obj
    
print(simplejson.dumps(pretty_floats([23.67, 23.97, 23.87])))

发出

[23.67, 23.97, 23.87]

无需进行Monkey修补。

import simplejson
    
class PrettyFloat(float):
    def __repr__(self):
        return '%.15g' % self
    
def pretty_floats(obj):
    if isinstance(obj, float):
        return PrettyFloat(obj)
    elif isinstance(obj, dict):
        return dict((k, pretty_floats(v)) for k, v in obj.items())
    elif isinstance(obj, (list, tuple)):
        return list(map(pretty_floats, obj))
    return obj
    
print(simplejson.dumps(pretty_floats([23.67, 23.97, 23.87])))

emits

[23.67, 23.97, 23.87]

No monkeypatching necessary.


回答 2

如果您使用的是Python 2.7,一个简单的解决方案是将浮点数显式舍入到所需的精度。

>>> sys.version
'2.7.1 (r271:86832, Nov 27 2010, 18:30:46) [MSC v.1500 32 bit (Intel)]'
>>> json.dumps(1.0/3.0)
'0.3333333333333333'
>>> json.dumps(round(1.0/3.0, 2))
'0.33'

之所以有效,是因为Python 2.7使浮点舍入更加一致。不幸的是,这在Python 2.6中不起作用:

>>> sys.version
'2.6.6 (r266:84292, Dec 27 2010, 00:02:40) \n[GCC 4.4.5]'
>>> json.dumps(round(1.0/3.0, 2))
'0.33000000000000002'

上面提到的解决方案是2.6的解决方法,但没有一个是完全足够的。如果您的Python运行时使用JSON模块的C版本,则Monkey修补json.encoder.FLOAT_REPR不起作用。Tom Wuttke的答案中的PrettyFloat类起作用,但是仅当%g编码对于您的应用程序全局起作用时。%.15g有点魔术,它可以工作,因为浮点精度是17个有效数字,%g不打印尾随零。

我花了一些时间尝试制作一个PrettyFloat,它允许为每个数字自定义精度。即,像这样的语法

>>> json.dumps(PrettyFloat(1.0 / 3.0, 4))
'0.3333'

要做到这一点并不容易。从float继承很尴尬。从Object继承并使用带有自己的default()方法的JSONEncoder子类应该可以工作,除了json模块似乎假定所有自定义类型都应序列化为字符串。即:您最终在输出中使用Javascript字符串“ 0.33”,而不是数字0.33。也许还有一种方法可以使这项工作完成,但是比看起来要难。

If you’re using Python 2.7, a simple solution is to simply round your floats explicitly to the desired precision.

>>> sys.version
'2.7.1 (r271:86832, Nov 27 2010, 18:30:46) [MSC v.1500 32 bit (Intel)]'
>>> json.dumps(1.0/3.0)
'0.3333333333333333'
>>> json.dumps(round(1.0/3.0, 2))
'0.33'

This works because Python 2.7 made float rounding more consistent. Unfortunately this does not work in Python 2.6:

>>> sys.version
'2.6.6 (r266:84292, Dec 27 2010, 00:02:40) \n[GCC 4.4.5]'
>>> json.dumps(round(1.0/3.0, 2))
'0.33000000000000002'

The solutions mentioned above are workarounds for 2.6, but none are entirely adequate. Monkey patching json.encoder.FLOAT_REPR does not work if your Python runtime uses a C version of the JSON module. The PrettyFloat class in Tom Wuttke’s answer works, but only if %g encoding works globally for your application. The %.15g is a bit magic, it works because float precision is 17 significant digits and %g does not print trailing zeroes.

I spent some time trying to make a PrettyFloat that allowed customization of precision for each number. Ie, a syntax like

>>> json.dumps(PrettyFloat(1.0 / 3.0, 4))
'0.3333'

It’s not easy to get this right. Inheriting from float is awkward. Inheriting from Object and using a JSONEncoder subclass with its own default() method should work, except the json module seems to assume all custom types should be serialized as strings. Ie: you end up with the Javascript string “0.33” in the output, not the number 0.33. There may be a way yet to make this work, but it’s harder than it looks.


回答 3

真不幸,dumps这使您无法做任何漂浮的事情。但是loads确实如此。因此,如果您不介意额外的CPU负载,则可以将其扔到编码器/解码器/编码器中,并得到正确的结果:

>>> json.dumps(json.loads(json.dumps([.333333333333, .432432]), parse_float=lambda x: round(float(x), 3)))
'[0.333, 0.432]'

Really unfortunate that dumps doesn’t allow you to do anything to floats. However loads does. So if you don’t mind the extra CPU load, you could throw it through the encoder/decoder/encoder and get the right result:

>>> json.dumps(json.loads(json.dumps([.333333333333, .432432]), parse_float=lambda x: round(float(x), 3)))
'[0.333, 0.432]'

回答 4

这是在Python 3中对我有用的解决方案,不需要Monkey补丁:

import json

def round_floats(o):
    if isinstance(o, float): return round(o, 2)
    if isinstance(o, dict): return {k: round_floats(v) for k, v in o.items()}
    if isinstance(o, (list, tuple)): return [round_floats(x) for x in o]
    return o


json.dumps(round_floats([23.63437, 23.93437, 23.842347]))

输出为:

[23.63, 23.93, 23.84]

它复制数据,但具有四舍五入的浮点数。

Here’s a solution that worked for me in Python 3 and does not require monkey patching:

import json

def round_floats(o):
    if isinstance(o, float): return round(o, 2)
    if isinstance(o, dict): return {k: round_floats(v) for k, v in o.items()}
    if isinstance(o, (list, tuple)): return [round_floats(x) for x in o]
    return o


json.dumps(round_floats([23.63437, 23.93437, 23.842347]))

Output is:

[23.63, 23.93, 23.84]

It copies the data but with rounded floats.


回答 5

如果您坚持使用Python 2.5或更早版本:如果安装了C加速,则Monkey-patch技巧似乎不适用于原始的simplejson模块:

$ python
Python 2.5.4 (r254:67916, Jan 20 2009, 11:06:13) 
[GCC 4.2.1 (SUSE Linux)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import simplejson
>>> simplejson.__version__
'2.0.9'
>>> simplejson._speedups
<module 'simplejson._speedups' from '/home/carlos/.python-eggs/simplejson-2.0.9-py2.5-linux-i686.egg-tmp/simplejson/_speedups.so'>
>>> simplejson.encoder.FLOAT_REPR = lambda f: ("%.2f" % f)
>>> simplejson.dumps([23.67, 23.97, 23.87])
'[23.670000000000002, 23.969999999999999, 23.870000000000001]'
>>> simplejson.encoder.c_make_encoder = None
>>> simplejson.dumps([23.67, 23.97, 23.87])
'[23.67, 23.97, 23.87]'
>>> 

If you’re stuck with Python 2.5 or earlier versions: The monkey-patch trick does not seem to work with the original simplejson module if the C speedups are installed:

$ python
Python 2.5.4 (r254:67916, Jan 20 2009, 11:06:13) 
[GCC 4.2.1 (SUSE Linux)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import simplejson
>>> simplejson.__version__
'2.0.9'
>>> simplejson._speedups
<module 'simplejson._speedups' from '/home/carlos/.python-eggs/simplejson-2.0.9-py2.5-linux-i686.egg-tmp/simplejson/_speedups.so'>
>>> simplejson.encoder.FLOAT_REPR = lambda f: ("%.2f" % f)
>>> simplejson.dumps([23.67, 23.97, 23.87])
'[23.670000000000002, 23.969999999999999, 23.870000000000001]'
>>> simplejson.encoder.c_make_encoder = None
>>> simplejson.dumps([23.67, 23.97, 23.87])
'[23.67, 23.97, 23.87]'
>>> 

回答 6

您可以做您需要做的事情,但是没有记录:

>>> import json
>>> json.encoder.FLOAT_REPR = lambda f: ("%.2f" % f)
>>> json.dumps([23.67, 23.97, 23.87])
'[23.67, 23.97, 23.87]'

You can do what you need to do, but it isn’t documented:

>>> import json
>>> json.encoder.FLOAT_REPR = lambda f: ("%.2f" % f)
>>> json.dumps([23.67, 23.97, 23.87])
'[23.67, 23.97, 23.87]'

回答 7

Alex Martelli的解决方案将适用于单线程应用程序,但不适用于需要控制每个线程的小数位数的多线程应用程序。这是一种应在多线程应用程序中使用的解决方案:

import threading
from json import encoder

def FLOAT_REPR(f):
    """
    Serialize a float to a string, with a given number of digits
    """
    decimal_places = getattr(encoder.thread_local, 'decimal_places', 0)
    format_str = '%%.%df' % decimal_places
    return format_str % f

encoder.thread_local = threading.local()
encoder.FLOAT_REPR = FLOAT_REPR     

#As an example, call like this:
import json

encoder.thread_local.decimal_places = 1
json.dumps([1.56, 1.54]) #Should result in '[1.6, 1.5]'

您仅可以将encoder.thread_local.decimal_places设置为所需的小数位数,而该线程中对json.dumps()的下一次调用将使用该小数位数

Alex Martelli’s solution will work for single threaded apps, but may not work for multi-threaded apps that need to control the number of decimal places per thread. Here is a solution that should work in multi threaded apps:

import threading
from json import encoder

def FLOAT_REPR(f):
    """
    Serialize a float to a string, with a given number of digits
    """
    decimal_places = getattr(encoder.thread_local, 'decimal_places', 0)
    format_str = '%%.%df' % decimal_places
    return format_str % f

encoder.thread_local = threading.local()
encoder.FLOAT_REPR = FLOAT_REPR     

#As an example, call like this:
import json

encoder.thread_local.decimal_places = 1
json.dumps([1.56, 1.54]) #Should result in '[1.6, 1.5]'

You can merely set encoder.thread_local.decimal_places to the number of decimal places you want, and the next call to json.dumps() in that thread will use that number of decimal places


回答 8

如果您需要在python 2.7中执行此操作而不覆盖全局json.encoder.FLOAT_REPR,这是一种方法。

import json
import math

class MyEncoder(json.JSONEncoder):
    "JSON encoder that renders floats to two decimal places"

    FLOAT_FRMT = '{0:.2f}'

    def floatstr(self, obj):
        return self.FLOAT_FRMT.format(obj)

    def _iterencode(self, obj, markers=None):
        # stl JSON lame override #1
        new_obj = obj
        if isinstance(obj, float):
            if not math.isnan(obj) and not math.isinf(obj):
                new_obj = self.floatstr(obj)
        return super(MyEncoder, self)._iterencode(new_obj, markers=markers)

    def _iterencode_dict(self, dct, markers=None):
        # stl JSON lame override #2
        new_dct = {}
        for key, value in dct.iteritems():
            if isinstance(key, float):
                if not math.isnan(key) and not math.isinf(key):
                    key = self.floatstr(key)
            new_dct[key] = value
        return super(MyEncoder, self)._iterencode_dict(new_dct, markers=markers)

然后,在python 2.7中:

>>> from tmp import MyEncoder
>>> enc = MyEncoder()
>>> enc.encode([23.67, 23.98, 23.87])
'[23.67, 23.98, 23.87]'

在python 2.6中,它无法正常工作,正如Matthew Schinckel指出的那样:

>>> import MyEncoder
>>> enc = MyEncoder()  
>>> enc.encode([23.67, 23.97, 23.87])
'["23.67", "23.97", "23.87"]'

If you need to do this in python 2.7 without overriding the global json.encoder.FLOAT_REPR, here’s one way.

import json
import math

class MyEncoder(json.JSONEncoder):
    "JSON encoder that renders floats to two decimal places"

    FLOAT_FRMT = '{0:.2f}'

    def floatstr(self, obj):
        return self.FLOAT_FRMT.format(obj)

    def _iterencode(self, obj, markers=None):
        # stl JSON lame override #1
        new_obj = obj
        if isinstance(obj, float):
            if not math.isnan(obj) and not math.isinf(obj):
                new_obj = self.floatstr(obj)
        return super(MyEncoder, self)._iterencode(new_obj, markers=markers)

    def _iterencode_dict(self, dct, markers=None):
        # stl JSON lame override #2
        new_dct = {}
        for key, value in dct.iteritems():
            if isinstance(key, float):
                if not math.isnan(key) and not math.isinf(key):
                    key = self.floatstr(key)
            new_dct[key] = value
        return super(MyEncoder, self)._iterencode_dict(new_dct, markers=markers)

Then, in python 2.7:

>>> from tmp import MyEncoder
>>> enc = MyEncoder()
>>> enc.encode([23.67, 23.98, 23.87])
'[23.67, 23.98, 23.87]'

In python 2.6, it doesn’t quite work as Matthew Schinckel points out below:

>>> import MyEncoder
>>> enc = MyEncoder()  
>>> enc.encode([23.67, 23.97, 23.87])
'["23.67", "23.97", "23.87"]'

回答 9

优点:

  • 适用于任何JSON编码器,甚至python的repr。
  • 短(ish),似乎起作用。

缺点:

  • 丑陋的regexp hack,未经测试。
  • 二次复杂度。

    def fix_floats(json, decimals=2, quote='"'):
        pattern = r'^((?:(?:"(?:\\.|[^\\"])*?")|[^"])*?)(-?\d+\.\d{'+str(decimals)+'}\d+)'
        pattern = re.sub('"', quote, pattern) 
        fmt = "%%.%df" % decimals
        n = 1
        while n:
            json, n = re.subn(pattern, lambda m: m.group(1)+(fmt % float(m.group(2)).rstrip('0')), json)
        return json

Pros:

  • Works with any JSON encoder, or even python’s repr.
  • Short(ish), seems to work.

Cons:

  • Ugly regexp hack, barely tested.
  • Quadratic complexity.

    def fix_floats(json, decimals=2, quote='"'):
        pattern = r'^((?:(?:"(?:\\.|[^\\"])*?")|[^"])*?)(-?\d+\.\d{'+str(decimals)+'}\d+)'
        pattern = re.sub('"', quote, pattern) 
        fmt = "%%.%df" % decimals
        n = 1
        while n:
            json, n = re.subn(pattern, lambda m: m.group(1)+(fmt % float(m.group(2)).rstrip('0')), json)
        return json
    

回答 10

导入标准json模块时,只需更改默认编码器FLOAT_REPR。确实不需要导入或创建Encoder实例。

import json
json.encoder.FLOAT_REPR = lambda o: format(o, '.2f')

json.dumps([23.67, 23.97, 23.87]) #returns  '[23.67, 23.97, 23.87]'

有时,将python可以用str猜出的最佳表示形式作为json输出也非常有用。这将确保重要数字不会被忽略。

import json
json.dumps([23.67, 23.9779, 23.87489])
# output is'[23.670000000000002, 23.977900000000002, 23.874890000000001]'

json.encoder.FLOAT_REPR = str
json.dumps([23.67, 23.9779, 23.87489])
# output is '[23.67, 23.9779, 23.87489]'

When importing the standard json module, it is enough to change the default encoder FLOAT_REPR. There isn’t really the need to import or create Encoder instances.

import json
json.encoder.FLOAT_REPR = lambda o: format(o, '.2f')

json.dumps([23.67, 23.97, 23.87]) #returns  '[23.67, 23.97, 23.87]'

Sometimes is also very useful to output as json the best representation python can guess with str. This will make sure signifficant digits are not ignored.

import json
json.dumps([23.67, 23.9779, 23.87489])
# output is'[23.670000000000002, 23.977900000000002, 23.874890000000001]'

json.encoder.FLOAT_REPR = str
json.dumps([23.67, 23.9779, 23.87489])
# output is '[23.67, 23.9779, 23.87489]'

回答 11

我同意@Nelson的观点,从float继承是很尴尬的,但是也许只涉及__repr__函数的解决方案是可以原谅的。我最终使用该decimal软件包在需要时重新格式化浮点数。好处是,这在所有repr()被调用的上下文中都有效,例如在简单地将列表打印到stdout时也是如此。同样,创建数据后,精度可以在运行时配置。缺点当然是您的数据需要转换为特殊的float类(不幸的是,您似乎无法获得Monkey补丁float.__repr__)。为此,我提供了一个简短的转换功能。

代码:

import decimal
C = decimal.getcontext()

class decimal_formatted_float(float):
   def __repr__(self):
       s = str(C.create_decimal_from_float(self))
       if '.' in s: s = s.rstrip('0')
       return s

def convert_to_dff(elem):
    try:
        return elem.__class__(map(convert_to_dff, elem))
    except:
        if isinstance(elem, float):
            return decimal_formatted_float(elem)
        else:
            return elem

用法示例:

>>> import json
>>> li = [(1.2345,),(7.890123,4.567,890,890.)]
>>>
>>> decimal.getcontext().prec = 15
>>> dff_li = convert_to_dff(li)
>>> dff_li
[(1.2345,), (7.890123, 4.567, 890, 890)]
>>> json.dumps(dff_li)
'[[1.2345], [7.890123, 4.567, 890, 890]]'
>>>
>>> decimal.getcontext().prec = 3
>>> dff_li = convert_to_dff(li)
>>> dff_li
[(1.23,), (7.89, 4.57, 890, 890)]
>>> json.dumps(dff_li)
'[[1.23], [7.89, 4.57, 890, 890]]'

I agree with @Nelson that inheriting from float is awkward, but perhaps a solution that only touches the __repr__ function might be forgiveable. I ended up using the decimal package for this to reformat floats when needed. The upside is that this works in all contexts where repr() is being called, so also when simply printing lists to stdout for example. Also, the precision is runtime configurable, after the data has been created. Downside is of course that your data needs to be converted to this special float class (as unfortunately you cannot seem to monkey patch float.__repr__). For that I provide a brief conversion function.

The code:

import decimal
C = decimal.getcontext()

class decimal_formatted_float(float):
   def __repr__(self):
       s = str(C.create_decimal_from_float(self))
       if '.' in s: s = s.rstrip('0')
       return s

def convert_to_dff(elem):
    try:
        return elem.__class__(map(convert_to_dff, elem))
    except:
        if isinstance(elem, float):
            return decimal_formatted_float(elem)
        else:
            return elem

Usage example:

>>> import json
>>> li = [(1.2345,),(7.890123,4.567,890,890.)]
>>>
>>> decimal.getcontext().prec = 15
>>> dff_li = convert_to_dff(li)
>>> dff_li
[(1.2345,), (7.890123, 4.567, 890, 890)]
>>> json.dumps(dff_li)
'[[1.2345], [7.890123, 4.567, 890, 890]]'
>>>
>>> decimal.getcontext().prec = 3
>>> dff_li = convert_to_dff(li)
>>> dff_li
[(1.23,), (7.89, 4.57, 890, 890)]
>>> json.dumps(dff_li)
'[[1.23], [7.89, 4.57, 890, 890]]'

回答 12

使用numpy

如果您实际上有很长的浮动,则可以使用numpy将其正确向上/向下取整:

import json 

import numpy as np

data = np.array([23.671234, 23.97432, 23.870123])

json.dumps(np.around(data, decimals=2).tolist())

'[23.67, 23.97, 23.87]'

Using numpy

If you actually have really long floats you can round them up/down correctly with numpy:

import json 

import numpy as np

data = np.array([23.671234, 23.97432, 23.870123])

json.dumps(np.around(data, decimals=2).tolist())

'[23.67, 23.97, 23.87]'


回答 13

我刚刚发布了fjson(一个小的Python库)来解决此问题。与安装

pip install fjson

并使用like json,并添加float_format参数:

import math
import fjson


data = {"a": 1, "b": math.pi}
print(fjson.dumps(data, float_format=".6e", indent=2))
{
  "a": 1,
  "b": 3.141593e+00
}

I just released fjson, a small Python library to fix this issue. Install with

pip install fjson

and use just like json, with the addition of the float_format parameter:

import math
import fjson


data = {"a": 1, "b": math.pi}
print(fjson.dumps(data, float_format=".6e", indent=2))
{
  "a": 1,
  "b": 3.141593e+00
}

Python中的货币格式

问题:Python中的货币格式

我想使用Python将类似188518982.18的数字格式化为188,518,982.18英镑。

我怎样才能做到这一点?

I am looking to format a number like 188518982.18 to £188,518,982.18 using Python.

How can I do this?


回答 0

请参阅语言环境模块。

这会进行货币(和日期)格式化。

>>> import locale
>>> locale.setlocale( locale.LC_ALL, '' )
'English_United States.1252'
>>> locale.currency( 188518982.18 )
'$188518982.18'
>>> locale.currency( 188518982.18, grouping=True )
'$188,518,982.18'

See the locale module.

This does currency (and date) formatting.

>>> import locale
>>> locale.setlocale( locale.LC_ALL, '' )
'English_United States.1252'
>>> locale.currency( 188518982.18 )
'$188518982.18'
>>> locale.currency( 188518982.18, grouping=True )
'$188,518,982.18'

回答 1

2.7的新功能

>>> '{:20,.2f}'.format(18446744073709551616.0)
'18,446,744,073,709,551,616.00'

http://docs.python.org/dev/whatsnew/2.7.html#pep-0378

New in 2.7

>>> '{:20,.2f}'.format(18446744073709551616.0)
'18,446,744,073,709,551,616.00'

http://docs.python.org/dev/whatsnew/2.7.html#pep-0378


回答 2

不太确定为什么没有在网上(或在此主题中)提及更多,但是Babel Edgewall家伙包(和Django实用程序)对于货币格式(以及许多其他i18n任务)来说很棒。很好,因为它不需要像核心Python语言环境模块那样全局地做所有事情。

OP给出的示例仅是:

>>> import babel.numbers
>>> import decimal
>>> babel.numbers.format_currency( decimal.Decimal( "188518982.18" ), "GBP" )
£188,518,982.18

Not quite sure why it’s not mentioned more online (or on this thread), but the Babel package (and Django utilities) from the Edgewall guys is awesome for currency formatting (and lots of other i18n tasks). It’s nice because it doesn’t suffer from the need to do everything globally like the core Python locale module.

The example the OP gave would simply be:

>>> import babel.numbers
>>> import decimal
>>> babel.numbers.format_currency( decimal.Decimal( "188518982.18" ), "GBP" )
£188,518,982.18

回答 3

这是一个古老的帖子,但是我只是实现了以下解决方案:

  • 不需要外部模块
  • 不需要创建新功能
  • 可以在线完成
  • 处理多个变量
  • 处理负的美元金额

码:

num1 = 4153.53
num2 = -23159.398598

print 'This: ${:0,.0f} and this: ${:0,.2f}'.format(num1, num2).replace('$-','-$')

输出:

This: $4,154 and this: -$23,159.40

对于原始海报,显然,只需切换$£

This is an ancient post, but I just implemented the following solution which:

  • Doesn’t require external modules
  • Doesn’t require creating a new function
  • Can be done in-line
  • Handles multiple variables
  • Handles negative dollar amounts

Code:

num1 = 4153.53
num2 = -23159.398598

print 'This: ${:0,.0f} and this: ${:0,.2f}'.format(num1, num2).replace('$-','-$')

Output:

This: $4,154 and this: -$23,159.40

And for the original poster, obviously, just switch $ for £


回答 4

我的语言环境设置似乎不完整,所以我也没有看到这样的答案,而是发现:

http://docs.python.org/library/decimal.html#recipes

与操作系统无关

只想在这里分享。

My locale settings seemed incomplete, so I had too look beyond this SO answer and found:

http://docs.python.org/library/decimal.html#recipes

OS-independent

Just wanted to share here.


回答 5

如果您使用的是OSX并且尚未设置区域设置模块,则此第一个答案将不起作用,您将收到以下错误:

Traceback (most recent call last):File "<stdin>", line 1, in <module> File "/System/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/locale.py", line 221, in currency
raise ValueError("Currency formatting is not possible using "ValueError: Currency formatting is not possible using the 'C' locale.

为了解决这个问题,您必须使用以下方法:

locale.setlocale(locale.LC_ALL, 'en_US')

If you are using OSX and have yet to set your locale module setting this first answer will not work you will receive the following error:

Traceback (most recent call last):File "<stdin>", line 1, in <module> File "/System/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/locale.py", line 221, in currency
raise ValueError("Currency formatting is not possible using "ValueError: Currency formatting is not possible using the 'C' locale.

To remedy this you will have to do use the following:

locale.setlocale(locale.LC_ALL, 'en_US')

回答 6

"{:0,.2f}".format(float(your_numeric_value))在Python 3中可以完成工作;它给出类似以下几行之一的内容:

10,938.29
10,899.00
10,898.99
2,328.99

"{:0,.2f}".format(float(your_numeric_value)) in Python 3 does the job; it gives out something like one of the following lines:

10,938.29
10,899.00
10,898.99
2,328.99

回答 7

如果我是你,我将使用BABEL:http : //babel.pocoo.org/en/latest/index.html

from babel.numbers import format_decimal


format_decimal(188518982.18, locale='en_US')

If I were you, I would use BABEL: http://babel.pocoo.org/en/latest/index.html

from babel.numbers import format_decimal


format_decimal(188518982.18, locale='en_US')

回答 8

哦,那是一个有趣的野兽。

我花了很多时间来解决这个问题,因地区而异的三个主要问题是:-货币符号和方向-千位分隔符-小数点

我已经编写了自己的相当广泛的实现,它是kiwi python框架的一部分,请在此处查看LGPL:ed源:

http://svn.async.com.br/cgi-bin/viewvc.cgi/kiwi/trunk/kiwi/currency.py?view=markup

该代码是Linux / Glibc特有的,但对于Windows或其他Unix来说应该不太难。

安装完成后,您可以执行以下操作:

>>> from kiwi.datatypes import currency
>>> v = currency('10.5').format()

然后会给你:

'$10.50'

要么

'10,50 kr'

取决于当前选择的语言环境。

这篇文章相对于其他文章的要点是它将与旧版本的python一起使用。locale.currency在python 2.5中引入。

Oh, that’s an interesting beast.

I’ve spent considerable time of getting that right, there are three main issues that differs from locale to locale: – currency symbol and direction – thousand separator – decimal point

I’ve written my own rather extensive implementation of this which is part of the kiwi python framework, check out the LGPL:ed source here:

http://svn.async.com.br/cgi-bin/viewvc.cgi/kiwi/trunk/kiwi/currency.py?view=markup

The code is slightly Linux/Glibc specific, but shouldn’t be too difficult to adopt to windows or other unixes.

Once you have that installed you can do the following:

>>> from kiwi.datatypes import currency
>>> v = currency('10.5').format()

Which will then give you:

'$10.50'

or

'10,50 kr'

Depending on the currently selected locale.

The main point this post has over the other is that it will work with older versions of python. locale.currency was introduced in python 2.5.


回答 9

#以类似于“ 9,348.237”的格式打印变量“总计:”

print ('Total:',   '{:7,.3f}'.format(zum1))

其中“ {:7,.3f}”是用于格式化数字的空格数,在这种情况下为百万,带有3个小数点。然后添加’.format(zum1)。zum1是tha变量,具有特定程序中所有数字之和的大数字。变量可以是您决定使用的任何东西。

#printing the variable ‘Total:’ in a format that looks like this ‘9,348.237’

print ('Total:',   '{:7,.3f}'.format(zum1))

where the ‘{:7,.3f}’ es the number of spaces for formatting the number in this case is a million with 3 decimal points. Then you add the ‘.format(zum1). The zum1 is tha variable that has the big number for the sum of all number in my particular program. Variable can be anything that you decide to use.


回答 10

受以上代码启发:D

def money_format(value):
value = str(value).split('.')
money = ''
count = 1

for digit in value[0][::-1]:
    if count != 3:
        money += digit
        count += 1
    else:
        money += f'{digit},'
        count = 1

if len(value) == 1:
    money = ('$' + money[::-1]).replace('$-','-$')
else:
    money = ('$' + money[::-1] + '.' + value[1]).replace('$-','-$')

return money

Inspired by the code above :D

def money_format(value):
    value = str(value).split('.')
    money = ''
    count = 1

    for digit in value[0][::-1]:
        if count != 3:
            money += digit
            count += 1
        else:
            money += f'{digit},'
            count = 1

    if len(value) == 1:
        money = ('$' + money[::-1]).replace('$-','-$')
    else:
        money = ('$' + money[::-1] + '.' + value[1]).replace('$-','-$')

    return money

回答 11

我来看看同一件事,发现python-money尚未真正使用它,但也许将两者混合会很好

I’ve come to look at the same thing and found python-money not really used it yet but maybe a mix of the two would be good


回答 12

在@Nate 答案的帮助下,用于在函数内部进行计算的lambda

converter = lambda amount, currency: "%s%s%s" %(
    "-" if amount < 0 else "", 
    currency, 
    ('{:%d,.2f}'%(len(str(amount))+3)).format(abs(amount)).lstrip())

然后,

>>> converter(123132132.13, "$")
'$123,132,132.13'

>>> converter(-123132132.13, "$")
'-$123,132,132.13'

A lambda for calculating it inside a function, with help from @Nate’s answer

converter = lambda amount, currency: "%s%s%s" %(
    "-" if amount < 0 else "", 
    currency, 
    ('{:%d,.2f}'%(len(str(amount))+3)).format(abs(amount)).lstrip())

and then,

>>> converter(123132132.13, "$")
'$123,132,132.13'

>>> converter(-123132132.13, "$")
'-$123,132,132.13'

回答 13

简单的python代码!

def format_us_currency(value):
    value=str(value)
    if value.count(',')==0:
        b,n,v='',1,value
        value=value[:value.rfind('.')]
        for i in value[::-1]:
            b=','+i+b if n==3 else i+b
            n=1 if n==3 else n+1
        b=b[1:] if b[0]==',' else b
        value=b+v[v.rfind('.'):]
    return '$'+(value.rstrip('0').rstrip('.') if '.' in value else value)

Simple python code!

def format_us_currency(value):
    value=str(value)
    if value.count(',')==0:
        b,n,v='',1,value
        value=value[:value.rfind('.')]
        for i in value[::-1]:
            b=','+i+b if n==3 else i+b
            n=1 if n==3 else n+1
        b=b[1:] if b[0]==',' else b
        value=b+v[v.rfind('.'):]
    return '$'+(value.rstrip('0').rstrip('.') if '.' in value else value)

转换为二进制并在Python中保持前导零

问题:转换为二进制并在Python中保持前导零

我正在尝试使用Python中的bin()函数将整数转换为二进制。但是,它总是删除我实际需要的前导零,因此结果始终是8位:

例:

bin(1) -> 0b1

# What I would like:
bin(1) -> 0b00000001

有办法吗?

I’m trying to convert an integer to binary using the bin() function in Python. However, it always removes the leading zeros, which I actually need, such that the result is always 8-bit:

Example:

bin(1) -> 0b1

# What I would like:
bin(1) -> 0b00000001

Is there a way of doing this?


回答 0

使用format()功能

>>> format(14, '#010b')
'0b00001110'

format()函数仅遵循格式规范迷你语言来格式化输入。在#使格式包括0b前缀,而010大小格式的输出,以适应在10个字符宽,与0填充; 2个字符0b前缀,其他8个二进制数字。

这是最紧凑,最直接的选择。

如果将结果放入较大的字符串中,请使用格式化的字符串文字(3.6+)或使用str.format()并将format()函数的第二个参数放在占位符冒号后面{:..}

>>> value = 14
>>> f'The produced output, in binary, is: {value:#010b}'
'The produced output, in binary, is: 0b00001110'
>>> 'The produced output, in binary, is: {:#010b}'.format(value)
'The produced output, in binary, is: 0b00001110'

碰巧的是,即使仅格式化单个值(这样就不必将结果放入更大的字符串中),使用格式化的字符串文字比使用format()以下格式的速度更快:

>>> import timeit
>>> timeit.timeit("f_(v, '#010b')", "v = 14; f_ = format")  # use a local for performance
0.40298633499332936
>>> timeit.timeit("f'{v:#010b}'", "v = 14")
0.2850222919951193

但是只有在紧密循环中的性能很重要的情况下,我才会使用它,例如 format(...)可以更好地传达意图。

如果您不希望使用0b前缀,只需删除#并调整字段的长度即可:

>>> format(14, '08b')
'00001110'

Use the format() function:

>>> format(14, '#010b')
'0b00001110'

The format() function simply formats the input following the Format Specification mini language. The # makes the format include the 0b prefix, and the 010 size formats the output to fit in 10 characters width, with 0 padding; 2 characters for the 0b prefix, the other 8 for the binary digits.

This is the most compact and direct option.

If you are putting the result in a larger string, use an formatted string literal (3.6+) or use str.format() and put the second argument for the format() function after the colon of the placeholder {:..}:

>>> value = 14
>>> f'The produced output, in binary, is: {value:#010b}'
'The produced output, in binary, is: 0b00001110'
>>> 'The produced output, in binary, is: {:#010b}'.format(value)
'The produced output, in binary, is: 0b00001110'

As it happens, even for just formatting a single value (so without putting the result in a larger string), using a formatted string literal is faster than using format():

>>> import timeit
>>> timeit.timeit("f_(v, '#010b')", "v = 14; f_ = format")  # use a local for performance
0.40298633499332936
>>> timeit.timeit("f'{v:#010b}'", "v = 14")
0.2850222919951193

But I’d use that only if performance in a tight loop matters, as format(...) communicates the intent better.

If you did not want the 0b prefix, simply drop the # and adjust the length of the field:

>>> format(14, '08b')
'00001110'

回答 1

>>> '{:08b}'.format(1)
'00000001'

请参阅:格式规范迷你语言


请注意,对于python 2.6或更早版本,您不能在之前省略位置参数标识符:,因此请使用

>>> '{0:08b}'.format(1)
'00000001'      
>>> '{:08b}'.format(1)
'00000001'

See: Format Specification Mini-Language


Note for Python 2.6 or older, you cannot omit the positional argument identifier before :, so use

>>> '{0:08b}'.format(1)
'00000001'      

回答 2

我在用

bin(1)[2:].zfill(8)

将打印

'00000001'

I am using

bin(1)[2:].zfill(8)

will print

'00000001'

回答 3

您可以使用字符串格式的迷你语言:

def binary(num, pre='0b', length=8, spacer=0):
    return '{0}{{:{1}>{2}}}'.format(pre, spacer, length).format(bin(num)[2:])

演示:

print binary(1)

输出:

'0b00000001'

编辑: 基于@Martijn Pieters的想法

def binary(num, length=8):
    return format(num, '#0{}b'.format(length + 2))

You can use the string formatting mini language:

def binary(num, pre='0b', length=8, spacer=0):
    return '{0}{{:{1}>{2}}}'.format(pre, spacer, length).format(bin(num)[2:])

Demo:

print binary(1)

Output:

'0b00000001'

EDIT: based on @Martijn Pieters idea

def binary(num, length=8):
    return format(num, '#0{}b'.format(length + 2))

回答 4

使用Python时>= 3.6,最干净的方法是使用带字符串格式的f 字符串

>>> var = 23
>>> f"{var:#010b}"
'0b00010111'

说明:

  • var 要格式化的变量
  • : 之后的所有内容都是格式说明符
  • #使用其他形式(添加0b前缀)
  • 0 用零填充
  • 10 填充到总长度不超过10(包括2个字符 0b
  • b 使用二进制表示形式

When using Python >= 3.6, the cleanest way is to use f-strings with string formatting:

>>> var = 23
>>> f"{var:#010b}"
'0b00010111'

Explanation:

  • var the variable to format
  • : everything after this is the format specifier
  • # use the alternative form (adds the 0b prefix)
  • 0 pad with zeros
  • 10 pad to a total length off 10 (this includes the 2 chars for 0b)
  • b use binary representation for the number

回答 5

有时,您只需要一个简单的班轮:

binary = ''.join(['{0:08b}'.format(ord(x)) for x in input])

Python 3

Sometimes you just want a simple one liner:

binary = ''.join(['{0:08b}'.format(ord(x)) for x in input])

Python 3


回答 6

你可以用这样的东西

("{:0%db}"%length).format(num)

You can use something like this

("{:0%db}"%length).format(num)

回答 7

您可以使用python语法的rjust字符串方法:string.rjust(length,fillchar)fillchar是可选的

对于您的问题,您可以这样写

'0b'+ '1'.rjust(8,'0)

因此它将是“ 0b00000001”

you can use rjust string method of python syntax: string.rjust(length, fillchar) fillchar is optional

and for your Question you acn write like this

'0b'+ '1'.rjust(8,'0)

so it wil be ‘0b00000001’


回答 8

您可以使用zfill:

print str(1).zfill(2) 
print str(10).zfill(2) 
print str(100).zfill(2)

印刷品:

01
10
100

我喜欢这种解决方案,因为它不仅在输出数字时有用,而且在需要将其分配给变量时也有帮助…例如-x = str(datetime.date.today()。month).zfill(2)将在2月中,将x返回为“ 02”。

You can use zfill:

print str(1).zfill(2) 
print str(10).zfill(2) 
print str(100).zfill(2)

prints:

01
10
100

I like this solution, as it helps not only when outputting the number, but when you need to assign it to a variable… e.g. – x = str(datetime.date.today().month).zfill(2) will return x as ’02’ for the month of feb.


如何自定义Python日志记录的时间格式?

问题:如何自定义Python日志记录的时间格式?

我是Python日志记录包的新手,并计划将其用于我的项目。我想根据自己的喜好定制时间格式。这是我从教程中复制的简短代码:

import logging

# create logger
logger = logging.getLogger("logging_tryout2")
logger.setLevel(logging.DEBUG)

# create console handler and set level to debug
ch = logging.StreamHandler()
ch.setLevel(logging.DEBUG)

# create formatter
formatter = logging.Formatter("%(asctime)s;%(levelname)s;%(message)s")

# add formatter to ch
ch.setFormatter(formatter)

# add ch to logger
logger.addHandler(ch)

# "application" code
logger.debug("debug message")
logger.info("info message")
logger.warn("warn message")
logger.error("error message")
logger.critical("critical message")

这是输出:

2010-07-10 10:46:28,811;DEBUG;debug message
2010-07-10 10:46:28,812;INFO;info message
2010-07-10 10:46:28,812;WARNING;warn message
2010-07-10 10:46:28,812;ERROR;error message
2010-07-10 10:46:28,813;CRITICAL;critical message

我想将时间格式缩短为:“ 2010-07-10 10:46:28”,删除毫秒后缀。我看着Formatter.formatTime,但是很困惑。感谢您为实现我的目标所提供的帮助。谢谢。

I am new to Python’s logging package and plan to use it for my project. I would like to customize the time format to my taste. Here is a short code I copied from a tutorial:

import logging

# create logger
logger = logging.getLogger("logging_tryout2")
logger.setLevel(logging.DEBUG)

# create console handler and set level to debug
ch = logging.StreamHandler()
ch.setLevel(logging.DEBUG)

# create formatter
formatter = logging.Formatter("%(asctime)s;%(levelname)s;%(message)s")

# add formatter to ch
ch.setFormatter(formatter)

# add ch to logger
logger.addHandler(ch)

# "application" code
logger.debug("debug message")
logger.info("info message")
logger.warn("warn message")
logger.error("error message")
logger.critical("critical message")

And here is the output:

2010-07-10 10:46:28,811;DEBUG;debug message
2010-07-10 10:46:28,812;INFO;info message
2010-07-10 10:46:28,812;WARNING;warn message
2010-07-10 10:46:28,812;ERROR;error message
2010-07-10 10:46:28,813;CRITICAL;critical message

I would like to shorten the time format to just: ‘2010-07-10 10:46:28‘, dropping the mili-second suffix. I looked at the Formatter.formatTime, but confused. I appreciate your help to achieve my goal. Thank you.


回答 0

从有关Formatter类的官方文档中

构造函数采用两个可选参数:消息格式字符串和日期格式字符串。

所以改变

# create formatter
formatter = logging.Formatter("%(asctime)s;%(levelname)s;%(message)s")

# create formatter
formatter = logging.Formatter("%(asctime)s;%(levelname)s;%(message)s",
                              "%Y-%m-%d %H:%M:%S")

From the official documentation regarding the Formatter class:

The constructor takes two optional arguments: a message format string and a date format string.

So change

# create formatter
formatter = logging.Formatter("%(asctime)s;%(levelname)s;%(message)s")

to

# create formatter
formatter = logging.Formatter("%(asctime)s;%(levelname)s;%(message)s",
                              "%Y-%m-%d %H:%M:%S")

回答 1

使用logging.basicConfig,以下示例对我有用:

logging.basicConfig(
    filename='HISTORYlistener.log',
    level=logging.DEBUG,
    format='%(asctime)s.%(msecs)03d %(levelname)s %(module)s - %(funcName)s: %(message)s',
    datefmt='%Y-%m-%d %H:%M:%S',
)

这样一来,您就可以全部格式化和配置文件。生成的日志记录如下所示:

2014-05-26 12:22:52.376 CRITICAL historylistener - main: History log failed to start

Using logging.basicConfig, the following example works for me:

logging.basicConfig(
    filename='HISTORYlistener.log',
    level=logging.DEBUG,
    format='%(asctime)s.%(msecs)03d %(levelname)s %(module)s - %(funcName)s: %(message)s',
    datefmt='%Y-%m-%d %H:%M:%S',
)

This allows you to format & config all in one line. A resulting log record looks as follows:

2014-05-26 12:22:52.376 CRITICAL historylistener - main: History log failed to start

回答 2

如果将logging.config.fileConfig与配置文件一起使用,请使用以下内容:

[formatter_simpleFormatter]
format=%(asctime)s - %(name)s - %(levelname)s - %(message)s
datefmt=%Y-%m-%d %H:%M:%S

if using logging.config.fileConfig with a configuration file use something like:

[formatter_simpleFormatter]
format=%(asctime)s - %(name)s - %(levelname)s - %(message)s
datefmt=%Y-%m-%d %H:%M:%S

回答 3

为了增加其他答案,这是Python文档中的变量列表

Directive   Meaning Notes

%a  Locales abbreviated weekday name.   
%A  Locales full weekday name.  
%b  Locales abbreviated month name.     
%B  Locales full month name.    
%c  Locales appropriate date and time representation.   
%d  Day of the month as a decimal number [01,31].    
%H  Hour (24-hour clock) as a decimal number [00,23].    
%I  Hour (12-hour clock) as a decimal number [01,12].    
%j  Day of the year as a decimal number [001,366].   
%m  Month as a decimal number [01,12].   
%M  Minute as a decimal number [00,59].  
%p  Locales equivalent of either AM or PM. (1)
%S  Second as a decimal number [00,61]. (2)
%U  Week number of the year (Sunday as the first day of the week) as a decimal number [00,53]. All days in a new year preceding the first Sunday are considered to be in week 0.    (3)
%w  Weekday as a decimal number [0(Sunday),6].   
%W  Week number of the year (Monday as the first day of the week) as a decimal number [00,53]. All days in a new year preceding the first Monday are considered to be in week 0.    (3)
%x  Locales appropriate date representation.    
%X  Locales appropriate time representation.    
%y  Year without century as a decimal number [00,99].    
%Y  Year with century as a decimal number.   
%z  Time zone offset indicating a positive or negative time difference from UTC/GMT of the form +HHMM or -HHMM, where H represents decimal hour digits and M represents decimal minute digits [-23:59, +23:59].  
%Z  Time zone name (no characters if no time zone exists).   
%%  A literal '%' character.     

To add to the other answers, here are the variable list from Python Documentation.

Directive   Meaning Notes

%a  Locale’s abbreviated weekday name.   
%A  Locale’s full weekday name.  
%b  Locale’s abbreviated month name.     
%B  Locale’s full month name.    
%c  Locale’s appropriate date and time representation.   
%d  Day of the month as a decimal number [01,31].    
%H  Hour (24-hour clock) as a decimal number [00,23].    
%I  Hour (12-hour clock) as a decimal number [01,12].    
%j  Day of the year as a decimal number [001,366].   
%m  Month as a decimal number [01,12].   
%M  Minute as a decimal number [00,59].  
%p  Locale’s equivalent of either AM or PM. (1)
%S  Second as a decimal number [00,61]. (2)
%U  Week number of the year (Sunday as the first day of the week) as a decimal number [00,53]. All days in a new year preceding the first Sunday are considered to be in week 0.    (3)
%w  Weekday as a decimal number [0(Sunday),6].   
%W  Week number of the year (Monday as the first day of the week) as a decimal number [00,53]. All days in a new year preceding the first Monday are considered to be in week 0.    (3)
%x  Locale’s appropriate date representation.    
%X  Locale’s appropriate time representation.    
%y  Year without century as a decimal number [00,99].    
%Y  Year with century as a decimal number.   
%z  Time zone offset indicating a positive or negative time difference from UTC/GMT of the form +HHMM or -HHMM, where H represents decimal hour digits and M represents decimal minute digits [-23:59, +23:59].  
%Z  Time zone name (no characters if no time zone exists).   
%%  A literal '%' character.     

格式化浮点数而不尾随零

问题:格式化浮点数而不尾随零

如何格式化浮点数,使其不包含尾随零?换句话说,我希望结果字符串尽可能短。

例如:

3 -> "3"
3. -> "3"
3.0 -> "3"
3.1 -> "3.1"
3.14 -> "3.14"
3.140 -> "3.14"

How can I format a float so that it doesn’t contain trailing zeros? In other words, I want the resulting string to be as short as possible.

For example:

3 -> "3"
3. -> "3"
3.0 -> "3"
3.1 -> "3.1"
3.14 -> "3.14"
3.140 -> "3.14"

回答 0

我,我会做的('%f' % x).rstrip('0').rstrip('.')-保证定点格式,而不是科学记数法,等等等等呀,还不如光滑和优雅的%g,但是,它的工作原理(我不知道如何强制%g从不使用科学记数法; -)。

Me, I’d do ('%f' % x).rstrip('0').rstrip('.') — guarantees fixed-point formatting rather than scientific notation, etc etc. Yeah, not as slick and elegant as %g, but, it works (and I don’t know how to force %g to never use scientific notation;-).


回答 1

您可以%g用来实现以下目的:

'%g'%(3.140)

或者,对于Python 2.6或更高版本:

'{0:g}'.format(3.140)

文档中查找formatg原因(除其他外)

从有效数中删除不重要的尾随零,并且如果在其后没有剩余数字,则也删除小数点。

You could use %g to achieve this:

'%g'%(3.140)

or, for Python 2.6 or better:

'{0:g}'.format(3.140)

From the docs for format: g causes (among other things)

insignificant trailing zeros [to be] removed from the significand, and the decimal point is also removed if there are no remaining digits following it.


回答 2

尝试最简单且可能最有效的方法呢?方法normalize()删除所有最右边的尾随零。

from decimal import Decimal

print (Decimal('0.001000').normalize())
# Result: 0.001

适用于Python 2Python 3

– 更新 –

@ BobStein-VisiBone指出的唯一问题是,数字10、100、1000 …将以指数形式显示。可以使用以下函数轻松解决此问题:

from decimal import Decimal


def format_float(f):
    d = Decimal(str(f));
    return d.quantize(Decimal(1)) if d == d.to_integral() else d.normalize()

What about trying the easiest and probably most effective approach? The method normalize() removes all the rightmost trailing zeros.

from decimal import Decimal

print (Decimal('0.001000').normalize())
# Result: 0.001

Works in Python 2 and Python 3.

— Updated —

The only problem as @BobStein-VisiBone pointed out, is that numbers like 10, 100, 1000… will be displayed in exponential representation. This can be easily fixed using the following function instead:

from decimal import Decimal


def format_float(f):
    d = Decimal(str(f));
    return d.quantize(Decimal(1)) if d == d.to_integral() else d.normalize()

回答 3

在查看了几个类似问题的答案之后,这似乎是我的最佳解决方案:

def floatToString(inputValue):
    return ('%.15f' % inputValue).rstrip('0').rstrip('.')

我的推理:

%g 没有摆脱科学计数法。

>>> '%g' % 0.000035
'3.5e-05'

小数点后15位似乎可以避免发生奇怪的行为,并且可以满足我的许多要求。

>>> ('%.15f' % 1.35).rstrip('0').rstrip('.')
'1.35'
>>> ('%.16f' % 1.35).rstrip('0').rstrip('.')
'1.3500000000000001'

我本可以使用format(inputValue, '.15f').来代替'%.15f' % inputValue,但是会慢一些(〜30%)。

我本可以使用Decimal(inputValue).normalize(),但这也有一些问题。例如,速度要慢很多(〜11x)。我还发现,尽管它具有很高的精度,但使用时仍然会遭受精度损失normalize()

>>> Decimal('0.21000000000000000000000000006').normalize()
Decimal('0.2100000000000000000000000001')
>>> Decimal('0.21000000000000000000000000006')
Decimal('0.21000000000000000000000000006')

最重要的是,我仍然会转换为Decimal从,float这样您最终可以得到的不是您输入的数字。我认为Decimal当算术停留在DecimalDecimal使用字符串初始化时效果最佳。

>>> Decimal(1.35)
Decimal('1.350000000000000088817841970012523233890533447265625')
>>> Decimal('1.35')
Decimal('1.35')

我确信Decimal.normalize()可以使用上下文设置将的精度问题调整为所需的值,但是考虑到速度已经很慢并且不需要可笑的精度,而且无论如何我仍然会从浮点数转换而失去精度,我没有认为这不值得追求。

我不担心可能的“ -0”结果,因为-0.0是有效的浮点数,并且无论如何它都可能很少出现,但是由于您确实提到要保持字符串结果尽可能短,因此总是可以以很少的额外速度成本使用额外的条件。

def floatToString(inputValue):
    result = ('%.15f' % inputValue).rstrip('0').rstrip('.')
    return '0' if result == '-0' else result

After looking over answers to several similar questions, this seems to be the best solution for me:

def floatToString(inputValue):
    return ('%.15f' % inputValue).rstrip('0').rstrip('.')

My reasoning:

%g doesn’t get rid of scientific notation.

>>> '%g' % 0.000035
'3.5e-05'

15 decimal places seems to avoid strange behavior and has plenty of precision for my needs.

>>> ('%.15f' % 1.35).rstrip('0').rstrip('.')
'1.35'
>>> ('%.16f' % 1.35).rstrip('0').rstrip('.')
'1.3500000000000001'

I could have used format(inputValue, '.15f'). instead of '%.15f' % inputValue, but that is a bit slower (~30%).

I could have used Decimal(inputValue).normalize(), but this has a few issues as well. For one, it is A LOT slower (~11x). I also found that although it has pretty great precision, it still suffers from precision loss when using normalize().

>>> Decimal('0.21000000000000000000000000006').normalize()
Decimal('0.2100000000000000000000000001')
>>> Decimal('0.21000000000000000000000000006')
Decimal('0.21000000000000000000000000006')

Most importantly, I would still be converting to Decimal from a float which can make you end up with something other than the number you put in there. I think Decimal works best when the arithmetic stays in Decimal and the Decimal is initialized with a string.

>>> Decimal(1.35)
Decimal('1.350000000000000088817841970012523233890533447265625')
>>> Decimal('1.35')
Decimal('1.35')

I’m sure the precision issue of Decimal.normalize() can be adjusted to what is needed using context settings, but considering the already slow speed and not needing ridiculous precision and the fact that I’d still be converting from a float and losing precision anyway, I didn’t think it was worth pursuing.

I’m not concerned with the possible “-0” result since -0.0 is a valid floating point number and it would probably be a rare occurrence anyway, but since you did mention you want to keep the string result as short as possible, you could always use an extra conditional at very little extra speed cost.

def floatToString(inputValue):
    result = ('%.15f' % inputValue).rstrip('0').rstrip('.')
    return '0' if result == '-0' else result

回答 4

这是一个对我有用的解决方案。这是一个混合的解决方案通过多网并使用新的.format() 语法

for num in 3, 3., 3.0, 3.1, 3.14, 3.140:
    print('{0:.2f}'.format(num).rstrip('0').rstrip('.'))

输出

3
3
3
3.1
3.14
3.14

Here’s a solution that worked for me. It’s a blend of the solution by PolyMesh and use of the new .format() syntax.

for num in 3, 3., 3.0, 3.1, 3.14, 3.140:
    print('{0:.2f}'.format(num).rstrip('0').rstrip('.'))

Output:

3
3
3
3.1
3.14
3.14

回答 5

您可以简单地使用format()实现此目的:

format(3.140, '.10g') 其中10是您想要的精度。

You can simply use format() to achieve this:

format(3.140, '.10g') where 10 is the precision you want.


回答 6

>>> str(a if a % 1 else int(a))
>>> str(a if a % 1 else int(a))

回答 7

尽管格式化可能是大多数Python方式,但这里是使用该more_itertools.rstrip工具的替代解决方案。

import more_itertools as mit


def fmt(num, pred=None):
    iterable = str(num)
    predicate = pred if pred is not None else lambda x: x in {".", "0"}
    return "".join(mit.rstrip(iterable, predicate))

assert fmt(3) == "3"
assert fmt(3.) == "3"
assert fmt(3.0) == "3"
assert fmt(3.1) == "3.1"
assert fmt(3.14) == "3.14"
assert fmt(3.140) == "3.14"
assert fmt(3.14000) == "3.14"
assert fmt("3,0", pred=lambda x: x in set(",0")) == "3"

该数字将转换为字符串,该字符串将除去满足谓词的结尾字符。函数定义fmt不是必需的,但是在这里用于测试所有通过的断言。注意:它适用于字符串输入并接受可选谓词。

另请参阅有关此第三方库的详细信息more_itertools

While formatting is likely that most Pythonic way, here is an alternate solution using the more_itertools.rstrip tool.

import more_itertools as mit


def fmt(num, pred=None):
    iterable = str(num)
    predicate = pred if pred is not None else lambda x: x in {".", "0"}
    return "".join(mit.rstrip(iterable, predicate))

assert fmt(3) == "3"
assert fmt(3.) == "3"
assert fmt(3.0) == "3"
assert fmt(3.1) == "3.1"
assert fmt(3.14) == "3.14"
assert fmt(3.140) == "3.14"
assert fmt(3.14000) == "3.14"
assert fmt("3,0", pred=lambda x: x in set(",0")) == "3"

The number is converted to a string, which is stripped of trailing characters that satisfy a predicate. The function definition fmt is not required, but it is used here to test assertions, which all pass. Note: it works on string inputs and accepts optional predicates.

See also details on this third-party library, more_itertools.


回答 8

如果您可以将3.和3.0都显示为“ 3.0”,那么这是一种非常简单的方法,可将浮点数表示形式的零右移:

print("%s"%3.140)

(感谢@ellimilial指出exceptions)

If you can live with 3. and 3.0 appearing as “3.0”, a very simple approach that right-strips zeros from float representations:

print("%s"%3.140)

(thanks @ellimilial for pointing out the exceptions)


回答 9

您可以选择使用QuantiPhy软件包。通常,在处理带有单位和SI比例因子的数字时,会使用QuantiPhy,但它具有多种不错的数字格式设置选项。

    >>> from quantiphy import Quantity

    >>> cases = '3 3. 3.0 3.1 3.14 3.140 3.14000'.split()
    >>> for case in cases:
    ...    q = Quantity(case)
    ...    print(f'{case:>7} -> {q:p}')
          3 -> 3
         3. -> 3
        3.0 -> 3
        3.1 -> 3.1
       3.14 -> 3.14
      3.140 -> 3.14
    3.14000 -> 3.14

在这种情况下,它将不使用电子符号:

    >>> cases = '3.14e-9 3.14 3.14e9'.split()
    >>> for case in cases:
    ...    q = Quantity(case)
    ...    print(f'{case:>7} -> {q:,p}')
    3.14e-9 -> 0
       3.14 -> 3.14
     3.14e9 -> 3,140,000,000

您可能更喜欢的替代方法是使用SI比例因子,也许使用单位。

    >>> cases = '3e-9 3.14e-9 3 3.14 3e9 3.14e9'.split()
    >>> for case in cases:
    ...    q = Quantity(case, 'm')
    ...    print(f'{case:>7} -> {q}')
       3e-9 -> 3 nm
    3.14e-9 -> 3.14 nm
          3 -> 3 m
       3.14 -> 3.14 m
        3e9 -> 3 Gm
     3.14e9 -> 3.14 Gm

Using the QuantiPhy package is an option. Normally QuantiPhy is used when working with numbers with units and SI scale factors, but it has a variety of nice number formatting options.

    >>> from quantiphy import Quantity

    >>> cases = '3 3. 3.0 3.1 3.14 3.140 3.14000'.split()
    >>> for case in cases:
    ...    q = Quantity(case)
    ...    print(f'{case:>7} -> {q:p}')
          3 -> 3
         3. -> 3
        3.0 -> 3
        3.1 -> 3.1
       3.14 -> 3.14
      3.140 -> 3.14
    3.14000 -> 3.14

And it will not use e-notation in this situation:

    >>> cases = '3.14e-9 3.14 3.14e9'.split()
    >>> for case in cases:
    ...    q = Quantity(case)
    ...    print(f'{case:>7} -> {q:,p}')
    3.14e-9 -> 0
       3.14 -> 3.14
     3.14e9 -> 3,140,000,000

An alternative you might prefer is to use SI scale factors, perhaps with units.

    >>> cases = '3e-9 3.14e-9 3 3.14 3e9 3.14e9'.split()
    >>> for case in cases:
    ...    q = Quantity(case, 'm')
    ...    print(f'{case:>7} -> {q}')
       3e-9 -> 3 nm
    3.14e-9 -> 3.14 nm
          3 -> 3 m
       3.14 -> 3.14 m
        3e9 -> 3 Gm
     3.14e9 -> 3.14 Gm

回答 10

OP希望删除多余的零,并使生成的字符串尽可能短。

我发现%g指数格式会缩短结果字符串的大小和数值。对于不需要指数表示法的值(例如128.0)来说,问题来了,它既不是很大也不是很小。

这是将数字格式化为短字符串的一种方法,仅当Decimal.normalize创建的字符串过长时才使用%g指数表示法。这可能不是最快的解决方案(因为它确实使用Decimal.normalize)

def floatToString (inputValue, precision = 3):
    rc = str(Decimal(inputValue).normalize())
    if 'E' in rc or len(rc) > 5:
        rc = '{0:.{1}g}'.format(inputValue, precision)        
    return rc

inputs = [128.0, 32768.0, 65536, 65536 * 2, 31.5, 1.000, 10.0]

outputs = [floatToString(i) for i in inputs]

print(outputs)

# ['128', '32768', '65536', '1.31e+05', '31.5', '1', '10']

OP would like to remove superflouous zeros and make the resulting string as short as possible.

I find the %g exponential formatting shortens the resulting string for very large and very small values. The problem comes for values that don’t need exponential notation, like 128.0, which is neither very large or very small.

Here is one way to format numbers as short strings that uses %g exponential notation only when Decimal.normalize creates strings that are too long. This might not be the fastest solution (since it does use Decimal.normalize)

def floatToString (inputValue, precision = 3):
    rc = str(Decimal(inputValue).normalize())
    if 'E' in rc or len(rc) > 5:
        rc = '{0:.{1}g}'.format(inputValue, precision)        
    return rc

inputs = [128.0, 32768.0, 65536, 65536 * 2, 31.5, 1.000, 10.0]

outputs = [floatToString(i) for i in inputs]

print(outputs)

# ['128', '32768', '65536', '1.31e+05', '31.5', '1', '10']

回答 11

对于float,您可以使用以下代码:

def format_float(num):
    return ('%i' if num == int(num) else '%s') % num

测试一下:

>>> format_float(1.00000)
'1'
>>> format_float(1.1234567890000000000)
'1.123456789'

对于十进制,请在此处查看解决方案:https : //stackoverflow.com/a/42668598/5917543

For float you could use this:

def format_float(num):
    return ('%i' if num == int(num) else '%s') % num

Test it:

>>> format_float(1.00000)
'1'
>>> format_float(1.1234567890000000000)
'1.123456789'

For Decimal see solution here: https://stackoverflow.com/a/42668598/5917543


回答 12

"{:.5g}".format(x)

我用它来格式化浮点数以尾随零。

"{:.5g}".format(x)

I use this to format floats to trail zeros.


回答 13

答案是:

import numpy

num1 = 3.1400
num2 = 3.000
numpy.format_float_positional(num1, 3, trim='-')
numpy.format_float_positional(num2, 3, trim='-')

输出“ 3.14”和“ 3”

trim='-' 删除尾随零和小数。

Here’s the answer:

import numpy

num1 = 3.1400
num2 = 3.000
numpy.format_float_positional(num1, 3, trim='-')
numpy.format_float_positional(num2, 3, trim='-')

output “3.14” and “3”

trim='-' removes both the trailing zero’s, and the decimal.


回答 14

使用宽度足够大的%g,例如’%.99g’。对于任何较大的数字,它将以定点表示法打印。

编辑:这不起作用

>>> '%.99g' % 0.0000001
'9.99999999999999954748111825886258685613938723690807819366455078125e-08'

Use %g with big enough width, for example ‘%.99g’. It will print in fixed-point notation for any reasonably big number.

EDIT: it doesn’t work

>>> '%.99g' % 0.0000001
'9.99999999999999954748111825886258685613938723690807819366455078125e-08'

回答 15

您可以这样使用max()

print(max(int(x), x))

You can use max() like this:

print(max(int(x), x))


回答 16

您可以通过以下大多数pythonic方式实现该目标:

python3:

"{:0.0f}".format(num)

You can achieve that in most pythonic way like that:

python3:

"{:0.0f}".format(num)

回答 17

处理%f,您应该放

%.2f

,其中:.2f == .00浮动。

例:

打印“价格:%.2f”%价格[产品]

输出:

价格:1.50

Handling %f and you should put

%.2f

, where: .2f == .00 floats.

Example:

print “Price: %.2f” % prices[product]

output:

Price: 1.50