分类目录归档:知识问答

全面的初学者的virtualenv教程?[关闭]

问题:全面的初学者的virtualenv教程?[关闭]

我最近一直在听到有关virtualenv的嗡嗡声,我对此很感兴趣。但是我所听到的只是一点点赞美,而对它的含义或用法却不甚了解。

我正在寻找(理想情况下)后续教程,该教程可以使我从没有Python的Windows或Linux中学习,并解释其每个步骤(无特定顺序):

  • 我应该怎么做才能开始使用 virtualenv
  • 使用virtualenv一个好主意的具体原因
  • 我不能/不能使用的情况 virtualenv
  • 我不应该使用的情况 virtualenv

并逐步(全面)介绍应有可能的几种示例情况。

那么,有什么好的教程可以涵盖这些内容?或者,如果您有时间和兴趣,也许您可​​以在这里回答其中一些问题。在您的答案中,或作为指向答案的教程的链接,这些都是我想知道的。

I’ve been hearing the buzz about virtualenv lately, and I’m interested. But all I’ve heard is a smattering of praise, and don’t have a clear understanding of what it is or how to use it.

I’m looking for (ideally) a follow-along tutorial that can take me from Windows or Linux with no Python on it, and explain every step of (in no particular order):

  • what I should do to be able to start using virtualenv
  • specific reasons why using virtualenv is a good idea
  • situations where I can/can’t use virtualenv
  • situations where I should/shouldn’t use virtualenv

And step through (comprehensively) a couple sample situations of the should+can variety.

So what are some good tutorials to cover this stuff? Or if you have the time and interest, perhaps you can answer a few of those questions here. Either in your answer, or as a link to tutorials that answer it, these are the things I’d like to know.


回答 0


回答 1

Virtualenv是用于创建隔离的 Python环境的工具。

假设您在两个不同的项目A和B中工作。项目A是一个Web项目,团队正在使用以下软件包:

  • Python 2.8.x
  • Django 1.6.x

项目B也是一个Web项目,但是您的团队正在使用:

  • Python 2.7.x
  • Django 1.4.x

您正在使用的机器没有django的任何版本,该怎么办?安装django 1.4?Django 1.6?如果在全局安装django 1.4,很容易将django 1.6指向在项目A中工作?

Virtualenv是您的解决方案!您可以创建2个不同的virtualenv,一个用于项目A,另一个用于项目B。现在,当您需要在项目A中工作时,只需为项目A激活virtualenv,反之亦然。

使用virtualenv时,一个更好的技巧是安装virtualenvwrapper来轻松管理您拥有的所有virtualenv。它是用于创建,工作和删除virtualenv的包装器。

Virtualenv is a tool to create isolated Python environments.

Let’s say you’re working in 2 different projects, A and B. Project A is a web project and the team is using the following packages:

  • Python 2.8.x
  • Django 1.6.x

The project B is also a web project but your team is using:

  • Python 2.7.x
  • Django 1.4.x

The machine that you’re working doesn’t have any version of django, what should you do? Install django 1.4? django 1.6? If you install django 1.4 globally would be easy to point to django 1.6 to work in project A?

Virtualenv is your solution! You can create 2 different virtualenv’s, one for project A and another for project B. Now, when you need to work in project A, just activate the virtualenv for project A, and vice-versa.

A better tip when using virtualenv is to install virtualenvwrapper to manage all the virtualenv’s that you have, easily. It’s a wrapper for creating, working, removing virtualenv’s.


回答 2

这是另一个好方法:http : //www.saltycrane.com/blog/2009/05/notes-using-pip-and-virtualenv-django/

这说明了如何使用pipvirtualenv和pip需求文件;Scobal的两个建议的教程都非常有帮助,但都是以easy_install中心为中心的。

请注意,这些教程都没有解释如何在virtualenv中运行不同版本的Python-为此,请参见以下SO问题:对virtualenv使用不同的Python版本

Here’s another good one: http://www.saltycrane.com/blog/2009/05/notes-using-pip-and-virtualenv-django/

This one shows how to use pip and a pip requirements file with virtualenv; Scobal‘s two suggested tutorials are both very helpful but are both easy_install-centric.

Note that none of these tutorials explain how to run a different version of Python within a virtualenv – for this, see this SO question: Use different Python version with virtualenv


回答 3

为了在干净的Ubuntu安装上设置virtualenv,我发现此zookeeper教程是最好的-您可以忽略有关zookeper本身的部分。该virtualenvwrapper文档提供类似的内容,但它是在告诉你到底该怎么把你有点稀缺的.bashrc文件。

For setting up virtualenv on a clean Ubuntu installation, I found this zookeeper tutorial to be the best – you can ignore the parts about zookeper itself. The virtualenvwrapper documentation offers similar content, but it’s a bit scarce on telling you what exactly to put into your .bashrc file.


Python-创建数字在2个值之间的列表?

问题:Python-创建数字在2个值之间的列表?

如何创建一个包含两个输入值之间的值的列表?例如,将为11到16的值生成以下列表。

list = [11, 12, 13, 14, 15, 16]

How would I create a list with values between two values I put in? For example, the following list is generated for values from 11 to 16:

list = [11, 12, 13, 14, 15, 16]

回答 0

使用range。在Python 2.x中,它返回一个列表,因此您需要做的是:

>>> range(11, 17)
[11, 12, 13, 14, 15, 16]

在Python 3.x中range是迭代器。因此,您需要将其转换为列表:

>>> list(range(11, 17))
[11, 12, 13, 14, 15, 16]

注意:第二个数字是唯一的。因此,这里需要为16+1=17

编辑:

要回答有关增加by的问题0.5,最简单的选择可能是使用numpy arange().tolist()

>>> import numpy as np
>>> np.arange(11, 17, 0.5).tolist()

[11.0, 11.5, 12.0, 12.5, 13.0, 13.5,
 14.0, 14.5, 15.0, 15.5, 16.0, 16.5]

Use range. In Python 2.x it returns a list so all you need is:

>>> range(11, 17)
[11, 12, 13, 14, 15, 16]

In Python 3.x range is a iterator. So, you need to convert it to a list:

>>> list(range(11, 17))
[11, 12, 13, 14, 15, 16]

Note: The second number is exclusive. So, here it needs to be 16+1 = 17

EDIT:

To respond to the question about incrementing by 0.5, the easiest option would probably be to use numpy’s arange() and .tolist():

>>> import numpy as np
>>> np.arange(11, 17, 0.5).tolist()

[11.0, 11.5, 12.0, 12.5, 13.0, 13.5,
 14.0, 14.5, 15.0, 15.5, 16.0, 16.5]

回答 1

您似乎在寻找range()

>>> x1=11
>>> x2=16
>>> range(x1, x2+1)
[11, 12, 13, 14, 15, 16]
>>> list1 = range(x1, x2+1)
>>> list1
[11, 12, 13, 14, 15, 16]

要以0.5代替递增1,请说:

>>> list2 = [x*0.5 for x in range(2*x1, 2*x2+1)]
>>> list2
[11.0, 11.5, 12.0, 12.5, 13.0, 13.5, 14.0, 14.5, 15.0, 15.5, 16.0]

You seem to be looking for range():

>>> x1=11
>>> x2=16
>>> range(x1, x2+1)
[11, 12, 13, 14, 15, 16]
>>> list1 = range(x1, x2+1)
>>> list1
[11, 12, 13, 14, 15, 16]

For incrementing by 0.5 instead of 1, say:

>>> list2 = [x*0.5 for x in range(2*x1, 2*x2+1)]
>>> list2
[11.0, 11.5, 12.0, 12.5, 13.0, 13.5, 14.0, 14.5, 15.0, 15.5, 16.0]

回答 2

尝试:

range(x1,x2+1)  

那是Python 2.x中的一个列表,其行为与Python 3.x中的一个列表非常相似。如果您正在运行Python 3,并且需要可以修改的列表,请使用:

list(range(x1,x2+1))

Try:

range(x1,x2+1)  

That is a list in Python 2.x and behaves mostly like a list in Python 3.x. If you are running Python 3 and need a list that you can modify, then use:

list(range(x1,x2+1))

回答 3

如果您正在寻找适用于浮点类型的范围之类的函数,那么这是一篇非常不错的文章

def frange(start, stop, step=1.0):
    ''' "range()" like function which accept float type''' 
    i = start
    while i < stop:
        yield i
        i += step
# Generate one element at a time.
# Preferred when you don't need all generated elements at the same time. 
# This will save memory.
for i in frange(1.0, 2.0, 0.5):
    print i   # Use generated element.
# Generate all elements at once.
# Preferred when generated list ought to be small.
print list(frange(1.0, 10.0, 0.5))    

输出:

1.0
1.5
[1.0, 1.5, 2.0, 2.5, 3.0, 3.5, 4.0, 4.5, 5.0, 5.5, 6.0, 6.5, 7.0, 7.5, 8.0, 8.5, 9.0, 9.5]

If you are looking for range like function which works for float type, then here is a very good article.

def frange(start, stop, step=1.0):
    ''' "range()" like function which accept float type''' 
    i = start
    while i < stop:
        yield i
        i += step
# Generate one element at a time.
# Preferred when you don't need all generated elements at the same time. 
# This will save memory.
for i in frange(1.0, 2.0, 0.5):
    print i   # Use generated element.
# Generate all elements at once.
# Preferred when generated list ought to be small.
print list(frange(1.0, 10.0, 0.5))    

Output:

1.0
1.5
[1.0, 1.5, 2.0, 2.5, 3.0, 3.5, 4.0, 4.5, 5.0, 5.5, 6.0, 6.5, 7.0, 7.5, 8.0, 8.5, 9.0, 9.5]

回答 4

在python中使用列表理解。由于您也希望列表中有16个。使用x2 + 1。范围功能不包括该功能的上限。

list = [x表示x在范围(x1,x2 + 1)中]

Use list comprehension in python. Since you want 16 in the list too.. Use x2+1. Range function excludes the higher limit in the function.

list=[x for x in range(x1,x2+1)]


回答 5

假设您希望x到y之间的范围

range(x,y+1)

>>> range(11,17)
[11, 12, 13, 14, 15, 16]
>>>

3.x支持使用列表

assuming you want to have a range between x to y

range(x,y+1)

>>> range(11,17)
[11, 12, 13, 14, 15, 16]
>>>

use list for 3.x support


回答 6

在python中,您可以非常轻松地执行此操作

start=0
end=10
arr=list(range(start,end+1))
output: arr=[0,1,2,3,4,5,6,7,8,9,10]

或者,您可以创建一个递归函数,该函数返回一个数组,并返回给定的数字:

ar=[]
def diff(start,end):
    if start==end:
        d.append(end)
        return ar
    else:
        ar.append(end)
        return diff(start-1,end) 

输出:ar = [10,9,8,7,6,5,4,3,2,1,0]

In python you can do this very eaisly

start=0
end=10
arr=list(range(start,end+1))
output: arr=[0,1,2,3,4,5,6,7,8,9,10]

or you can create a recursive function that returns an array upto a given number:

ar=[]
def diff(start,end):
    if start==end:
        d.append(end)
        return ar
    else:
        ar.append(end)
        return diff(start-1,end) 

output: ar=[10,9,8,7,6,5,4,3,2,1,0]


回答 7

最优雅的方法是使用range函数,但是,如果您想重新创建此逻辑,则可以执行以下操作:

def custom_range(*args):
    s = slice(*args)
    start, stop, step = s.start, s.stop, s.step
    if 0 == step:
        raise ValueError("range() arg 3 must not be zero")
    i = start
    while i < stop if step > 0 else i > stop:
        yield i
        i += step

>>> [x for x in custom_range(10, 3, -1)]

产生输出:

[10, 9, 8, 7, 6, 5, 4]

正如之前@Jared表示,最好的办法是使用rangenumpy.arrange不过,我觉得要共享的代码有趣。

The most elegant way to do this is by using the range function however if you want to re-create this logic you can do something like this :

def custom_range(*args):
    s = slice(*args)
    start, stop, step = s.start, s.stop, s.step
    if 0 == step:
        raise ValueError("range() arg 3 must not be zero")
    i = start
    while i < stop if step > 0 else i > stop:
        yield i
        i += step

>>> [x for x in custom_range(10, 3, -1)]

This produces the output:

[10, 9, 8, 7, 6, 5, 4]

As expressed before by @Jared, the best way is to use the range or numpy.arrange however I find the code interesting to be shared.


回答 8

上面的每个答案都假定范围仅是正数。这是返回连续数字列表的解决方案,其中参数可以是任意值(正数或负数),并且可以设置可选的步长值(默认值= 1)。

def any_number_range(a,b,s=1):
""" Generate consecutive values list between two numbers with optional step (default=1)."""
if (a == b):
    return a
else:
    mx = max(a,b)
    mn = min(a,b)
    result = []
    # inclusive upper limit. If not needed, delete '+1' in the line below
    while(mn < mx + 1):
        # if step is positive we go from min to max
        if s > 0:
            result.append(mn)
            mn += s
        # if step is negative we go from max to min
        if s < 0:
            result.append(mx)
            mx += s
    return result

例如,标准命令list(range(1,-3))返回空列表[],而此函数将返回[-3,-2,-1,0,1]

更新:现在步骤可能为负。感谢@Michael的评论。

Every answer above assumes range is of positive numbers only. Here is the solution to return list of consecutive numbers where arguments can be any (positive or negative), with the possibility to set optional step value (default = 1).

def any_number_range(a,b,s=1):
""" Generate consecutive values list between two numbers with optional step (default=1)."""
if (a == b):
    return a
else:
    mx = max(a,b)
    mn = min(a,b)
    result = []
    # inclusive upper limit. If not needed, delete '+1' in the line below
    while(mn < mx + 1):
        # if step is positive we go from min to max
        if s > 0:
            result.append(mn)
            mn += s
        # if step is negative we go from max to min
        if s < 0:
            result.append(mx)
            mx += s
    return result

For instance, standard command list(range(1,-3)) returns empty list [], while this function will return [-3,-2,-1,0,1]

Updated: now step may be negative. Thanks @Michael for his comment.


Python图形库[关闭]

问题:Python图形库[关闭]

我正在编写一个将大量使用图形数据结构的python应用程序。没什么可怕的,但是我在想某种图形/图形算法库可以帮助我。我已经四处搜寻,但没有发现特别令我惊讶的地方。

有人有什么好的建议吗?

I’m writing a python application that will make heavy use of a graph data structure. Nothing horribly complex, but I’m thinking some sort of graph/graph-algorithms library would help me out. I’ve googled around, but I don’t find anything that particularly leaps out at me.

Anyone have any good recommendations?


回答 0

有两个绝佳的选择:

网络X

我喜欢NetworkX,但我也阅读有关igraph的好文章。我通常将NetworkX与具有1百万个节点的图一起使用而没有问题(这是大小为V + E的字典的开销的两倍)

如果要进行功能比较,请从Networkx讨论列表中查看

功能比较线程

There are two excellent choices:

NetworkX

and

igraph

I like NetworkX, but I read good things about igraph as well. I routinely use NetworkX with graphs with 1 million nodes with no problem (it’s about double the overhead of a dict of size V + E)

If you want a feature comparison, see this from the Networkx-discuss list

Feature comparison thread


回答 1

我想插入自己的图python库: graph-tool

它非常快,因为它是通过Boost Graph库的C ++实现的,并且包含许多算法和大量文档。

I would like to plug my own graph python library: graph-tool.

It is very fast, since it is implemented in C++ with the Boost Graph Library, and it contains lots of algorithms and extensive documentation.


回答 2

你看过python-graph吗?我自己没有使用过,但是项目页面看起来很有希望。

Have you looked at python-graph? I haven’t used it myself, but the project page looks promising.


回答 3

另外,您可能想看看NetworkX

Also, you might want to take a look at NetworkX


回答 4


回答 5

看一下有关在python中实现图形的页面

您也可以在sourceforge上查看pygraphlib

Take a look at this page on implementing graphs in python.

You could also take a look at pygraphlib on sourceforge.


回答 6

我的pydot最幸运。其他一些很难在像Win 7这样的不同平台上安装和配置。

http://code.google.com/p/pydot/

I’m having the most luck with pydot. Some of the others are hard to install and configure on different platforms like Win 7.

http://code.google.com/p/pydot/


回答 7

我第二次zweiterlinde建议使用python-graph。我已将其用作我正在从事的基于图的研究项目的基础。该库编写良好,稳定且具有良好的接口。作者还可以迅速答复查询和报告。

I second zweiterlinde’s suggestion to use python-graph. I’ve used it as the basis of a graph-based research project that I’m working on. The library is well written, stable, and has a good interface. The authors are also quick to respond to inquiries and reports.


记录器配置以记录到文件并打印到stdout

问题:记录器配置以记录到文件并打印到stdout

我正在使用Python的日志记录模块将一些调试字符串记录到运行良好的文件中。现在,此外,我想使用此模块还将字符串输出到stdout。我该怎么做呢?为了将我的字符串记录到文件中,我使用以下代码:

import logging
import logging.handlers
logger = logging.getLogger("")
logger.setLevel(logging.DEBUG)
handler = logging.handlers.RotatingFileHandler(
    LOGFILE, maxBytes=(1048576*5), backupCount=7
)
formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
handler.setFormatter(formatter)
logger.addHandler(handler)

然后调用记录器功能,例如

logger.debug("I am written to the file")

谢谢您的帮助!

I’m using Python’s logging module to log some debug strings to a file which works pretty well. Now in addition, I’d like to use this module to also print the strings out to stdout. How do I do this? In order to log my strings to a file I use following code:

import logging
import logging.handlers
logger = logging.getLogger("")
logger.setLevel(logging.DEBUG)
handler = logging.handlers.RotatingFileHandler(
    LOGFILE, maxBytes=(1048576*5), backupCount=7
)
formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
handler.setFormatter(formatter)
logger.addHandler(handler)

and then call a logger function like

logger.debug("I am written to the file")

Thank you for some help here!


回答 0

只需获取根记录器的句柄并添加即可StreamHandler。在StreamHandler写至标准错误。不知道您是否真的需要stdout而不是stderr,但这是我在设置Python记录器时使用的方法,我也添加了FileHandler它。然后,我所有的日志都转到两个地方(这听起来像您想要的)。

import logging
logging.getLogger().addHandler(logging.StreamHandler())

如果要输出到stdout而不是stderr,则只需将其指定给StreamHandler构造函数。

import sys
# ...
logging.getLogger().addHandler(logging.StreamHandler(sys.stdout))

您也可以在其中添加一个Formatter,以便所有日志行都有一个公共标题。

即:

import logging
logFormatter = logging.Formatter("%(asctime)s [%(threadName)-12.12s] [%(levelname)-5.5s]  %(message)s")
rootLogger = logging.getLogger()

fileHandler = logging.FileHandler("{0}/{1}.log".format(logPath, fileName))
fileHandler.setFormatter(logFormatter)
rootLogger.addHandler(fileHandler)

consoleHandler = logging.StreamHandler()
consoleHandler.setFormatter(logFormatter)
rootLogger.addHandler(consoleHandler)

打印为以下格式:

2012-12-05 16:58:26,618 [MainThread  ] [INFO ]  my message

Just get a handle to the root logger and add the StreamHandler. The StreamHandler writes to stderr. Not sure if you really need stdout over stderr, but this is what I use when I setup the Python logger and I also add the FileHandler as well. Then all my logs go to both places (which is what it sounds like you want).

import logging
logging.getLogger().addHandler(logging.StreamHandler())

If you want to output to stdout instead of stderr, you just need to specify it to the StreamHandler constructor.

import sys
# ...
logging.getLogger().addHandler(logging.StreamHandler(sys.stdout))

You could also add a Formatter to it so all your log lines have a common header.

ie:

import logging
logFormatter = logging.Formatter("%(asctime)s [%(threadName)-12.12s] [%(levelname)-5.5s]  %(message)s")
rootLogger = logging.getLogger()

fileHandler = logging.FileHandler("{0}/{1}.log".format(logPath, fileName))
fileHandler.setFormatter(logFormatter)
rootLogger.addHandler(fileHandler)

consoleHandler = logging.StreamHandler()
consoleHandler.setFormatter(logFormatter)
rootLogger.addHandler(consoleHandler)

Prints to the format of:

2012-12-05 16:58:26,618 [MainThread  ] [INFO ]  my message

回答 1

logging.basicConfig()handlers从Python 3.3开始可以使用关键字参数,这大大简化了日志记录设置,尤其是在使用同一格式化程序设置多个处理程序时:

handlers–如果指定,则这应该是已经创建的处理程序的迭代,以添加到根记录器。任何尚未设置格式器的处理程序都将被分配此函数中创建的默认格式器。

因此,整个设置可以通过以下单个调用完成:

import logging

logging.basicConfig(
    level=logging.INFO,
    format="%(asctime)s [%(levelname)s] %(message)s",
    handlers=[
        logging.FileHandler("debug.log"),
        logging.StreamHandler()
    ]
)

(或按照原始问题的要求加上import sys+ StreamHandler(sys.stdout)– StreamHandler的默认值是写入stderr。查看LogRecord属性如果要自定义日志格式并添加诸如文件名/行,线程信息等内容,请。)

上面的设置只需要在脚本开始处执行一次即可。您可以稍后在代码库的所有其他位置使用日志记录,如下所示:

logging.info('Useful message')
logging.error('Something bad happened')
...

注意:如果它不起作用,则可能是其他人已经用不同的方式初始化了日志系统。建议logging.root.handlers = []在调用之前先做评论basicConfig()

logging.basicConfig() can take a keyword argument handlers since Python 3.3, which simplifies logging setup a lot, especially when setting up multiple handlers with the same formatter:

handlers – If specified, this should be an iterable of already created handlers to add to the root logger. Any handlers which don’t already have a formatter set will be assigned the default formatter created in this function.

The whole setup can therefore be done with a single call like this:

import logging

logging.basicConfig(
    level=logging.INFO,
    format="%(asctime)s [%(levelname)s] %(message)s",
    handlers=[
        logging.FileHandler("debug.log"),
        logging.StreamHandler()
    ]
)

(Or with import sys + StreamHandler(sys.stdout) per original question’s requirements – the default for StreamHandler is to write to stderr. Look at LogRecord attributes if you want to customize the log format and add things like filename/line, thread info etc.)

The setup above needs to be done only once near the beginning of the script. You can use the logging from all other places in the codebase later like this:

logging.info('Useful message')
logging.error('Something bad happened')
...

Note: If it doesn’t work, someone else has probably already initialized the logging system differently. Comments suggest doing logging.root.handlers = [] before the call to basicConfig().


回答 2

添加不带参数的StreamHandler转到stderr而不是stdout。如果某些其他进程依赖于stdout转储(即,在编写NRPE插件时),则请确保明确指定stdout,否则您可能会遇到一些意想不到的麻烦。

这是一个快速示例,该示例重用了问题中的假定值和LOGFILE:

import logging
from logging.handlers import RotatingFileHandler
from logging import handlers
import sys

log = logging.getLogger('')
log.setLevel(logging.DEBUG)
format = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")

ch = logging.StreamHandler(sys.stdout)
ch.setFormatter(format)
log.addHandler(ch)

fh = handlers.RotatingFileHandler(LOGFILE, maxBytes=(1048576*5), backupCount=7)
fh.setFormatter(format)
log.addHandler(fh)

Adding a StreamHandler without arguments goes to stderr instead of stdout. If some other process has a dependency on the stdout dump (i.e. when writing an NRPE plugin), then make sure to specify stdout explicitly or you might run into some unexpected troubles.

Here’s a quick example reusing the assumed values and LOGFILE from the question:

import logging
from logging.handlers import RotatingFileHandler
from logging import handlers
import sys

log = logging.getLogger('')
log.setLevel(logging.DEBUG)
format = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")

ch = logging.StreamHandler(sys.stdout)
ch.setFormatter(format)
log.addHandler(ch)

fh = handlers.RotatingFileHandler(LOGFILE, maxBytes=(1048576*5), backupCount=7)
fh.setFormatter(format)
log.addHandler(fh)

回答 3

在设置任何其他处理程序或记录任何消息之前,可以basicConfig使用stream=sys.stdout作为参数运行,或者手动添加一个StreamHandler将消息推入stdout的根记录器(或与此相关的任何其他记录器)。

Either run basicConfig with stream=sys.stdout as the argument prior to setting up any other handlers or logging any messages, or manually add a StreamHandler that pushes messages to stdout to the root logger (or any other logger you want, for that matter).


回答 4

在多个Python包中反复使用Waterboy的代码后,我最终将其转换为一个很小的独立Python包,您可以在这里找到:

https://github.com/acschaefer/duallog

该代码有据可查且易于使用。只需下载.py文件并将其包含在您的项目中,或通过安装整个软件包pip install duallog

After having used Waterboy’s code over and over in multiple Python packages, I finally cast it into a tiny standalone Python package, which you can find here:

https://github.com/acschaefer/duallog

The code is well documented and easy to use. Simply download the .py file and include it in your project, or install the whole package via pip install duallog.


回答 5

登录stdoutrotating file使用不同的级别和格式:

import logging
import logging.handlers
import sys

if __name__ == "__main__":

    # Change root logger level from WARNING (default) to NOTSET in order for all messages to be delegated.
    logging.getLogger().setLevel(logging.NOTSET)

    # Add stdout handler, with level INFO
    console = logging.StreamHandler(sys.stdout)
    console.setLevel(logging.INFO)
    formater = logging.Formatter('%(name)-13s: %(levelname)-8s %(message)s')
    console.setFormatter(formater)
    logging.getLogger().addHandler(console)

    # Add file rotating handler, with level DEBUG
    rotatingHandler = logging.handlers.RotatingFileHandler(filename='rotating.log', maxBytes=1000, backupCount=5)
    rotatingHandler.setLevel(logging.DEBUG)
    formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
    rotatingHandler.setFormatter(formatter)
    logging.getLogger().addHandler(rotatingHandler)

    log = logging.getLogger("app." + __name__)

    log.debug('Debug message, should only appear in the file.')
    log.info('Info message, should appear in file and stdout.')
    log.warning('Warning message, should appear in file and stdout.')
    log.error('Error message, should appear in file and stdout.')

Logging to stdout and rotating file with different levels and formats:

import logging
import logging.handlers
import sys

if __name__ == "__main__":

    # Change root logger level from WARNING (default) to NOTSET in order for all messages to be delegated.
    logging.getLogger().setLevel(logging.NOTSET)

    # Add stdout handler, with level INFO
    console = logging.StreamHandler(sys.stdout)
    console.setLevel(logging.INFO)
    formater = logging.Formatter('%(name)-13s: %(levelname)-8s %(message)s')
    console.setFormatter(formater)
    logging.getLogger().addHandler(console)

    # Add file rotating handler, with level DEBUG
    rotatingHandler = logging.handlers.RotatingFileHandler(filename='rotating.log', maxBytes=1000, backupCount=5)
    rotatingHandler.setLevel(logging.DEBUG)
    formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
    rotatingHandler.setFormatter(formatter)
    logging.getLogger().addHandler(rotatingHandler)

    log = logging.getLogger("app." + __name__)

    log.debug('Debug message, should only appear in the file.')
    log.info('Info message, should appear in file and stdout.')
    log.warning('Warning message, should appear in file and stdout.')
    log.error('Error message, should appear in file and stdout.')

回答 6

这是一个完整的包装好的解决方案,基于Waterboy的答案和其他各种来源。它支持同时记录到控制台和日志文件,允许进行不同的日志级别设置,提供彩色输出,并且易于配置(也可以作为Gist使用):

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

# -------------------------------------------------------------------------------
#                                                                               -
#  Python dual-logging setup (console and log file),                            -
#  supporting different log levels and colorized output                         -
#                                                                               -
#  Created by Fonic <https://github.com/fonic>                                  -
#  Date: 04/05/20                                                               -
#                                                                               -
#  Based on:                                                                    -
#  https://stackoverflow.com/a/13733863/1976617                                 -
#  https://uran198.github.io/en/python/2016/07/12/colorful-python-logging.html  -
#  https://en.wikipedia.org/wiki/ANSI_escape_code#Colors                        -
#                                                                               -
# -------------------------------------------------------------------------------

# Imports
import os
import sys
import logging

# Logging formatter supporting colored output
class LogFormatter(logging.Formatter):

    COLOR_CODES = {
        logging.CRITICAL: "\033[1;35m", # bright/bold magenta
        logging.ERROR:    "\033[1;31m", # bright/bold red
        logging.WARNING:  "\033[1;33m", # bright/bold yellow
        logging.INFO:     "\033[0;37m", # white / light gray
        logging.DEBUG:    "\033[1;30m"  # bright/bold black / dark gray
    }

    RESET_CODE = "\033[0m"

    def __init__(self, color, *args, **kwargs):
        super(LogFormatter, self).__init__(*args, **kwargs)
        self.color = color

    def format(self, record, *args, **kwargs):
        if (self.color == True and record.levelno in self.COLOR_CODES):
            record.color_on  = self.COLOR_CODES[record.levelno]
            record.color_off = self.RESET_CODE
        else:
            record.color_on  = ""
            record.color_off = ""
        return super(LogFormatter, self).format(record, *args, **kwargs)

# Setup logging
def setup_logging(console_log_output, console_log_level, console_log_color, logfile_file, logfile_log_level, logfile_log_color, log_line_template):

    # Create logger
    # For simplicity, we use the root logger, i.e. call 'logging.getLogger()'
    # without name argument. This way we can simply use module methods for
    # for logging throughout the script. An alternative would be exporting
    # the logger, i.e. 'global logger; logger = logging.getLogger("<name>")'
    logger = logging.getLogger()

    # Set global log level to 'debug' (required for handler levels to work)
    logger.setLevel(logging.DEBUG)

    # Create console handler
    console_log_output = console_log_output.lower()
    if (console_log_output == "stdout"):
        console_log_output = sys.stdout
    elif (console_log_output == "stderr"):
        console_log_output = sys.stderr
    else:
        print("Failed to set console output: invalid output: '%s'" % console_log_output)
        return False
    console_handler = logging.StreamHandler(console_log_output)

    # Set console log level
    try:
        console_handler.setLevel(console_log_level.upper()) # only accepts uppercase level names
    except:
        print("Failed to set console log level: invalid level: '%s'" % console_log_level)
        return False

    # Create and set formatter, add console handler to logger
    console_formatter = LogFormatter(fmt=log_line_template, color=console_log_color)
    console_handler.setFormatter(console_formatter)
    logger.addHandler(console_handler)

    # Create log file handler
    try:
        logfile_handler = logging.FileHandler(logfile_file)
    except Exception as exception:
        print("Failed to set up log file: %s" % str(exception))
        return False

    # Set log file log level
    try:
        logfile_handler.setLevel(logfile_log_level.upper()) # only accepts uppercase level names
    except:
        print("Failed to set log file log level: invalid level: '%s'" % logfile_log_level)
        return False

    # Create and set formatter, add log file handler to logger
    logfile_formatter = LogFormatter(fmt=log_line_template, color=logfile_log_color)
    logfile_handler.setFormatter(logfile_formatter)
    logger.addHandler(logfile_handler)

    # Success
    return True

# Main function
def main():

    # Setup logging
    script_name = os.path.splitext(os.path.basename(sys.argv[0]))[0]
    if (not setup_logging(console_log_output="stdout", console_log_level="warning", console_log_color=True,
                        logfile_file=script_name + ".log", logfile_log_level="debug", logfile_log_color=False,
                        log_line_template="%(color_on)s[%(created)d] [%(threadName)s] [%(levelname)-8s] %(message)s%(color_off)s")):
        print("Failed to setup logging, aborting.")
        return 1

    # Log some messages
    logging.debug("Debug message")
    logging.info("Info message")
    logging.warning("Warning message")
    logging.error("Error message")
    logging.critical("Critical message")

# Call main function
if (__name__ == "__main__"):
    sys.exit(main())

Here is a complete, nicely wrapped solution based on Waterboy’s answer and various other sources. It supports logging to both console and log file, allows for different log level settings, provides colorized output and is easily configurable (also available as Gist):

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

# -------------------------------------------------------------------------------
#                                                                               -
#  Python dual-logging setup (console and log file),                            -
#  supporting different log levels and colorized output                         -
#                                                                               -
#  Created by Fonic <https://github.com/fonic>                                  -
#  Date: 04/05/20                                                               -
#                                                                               -
#  Based on:                                                                    -
#  https://stackoverflow.com/a/13733863/1976617                                 -
#  https://uran198.github.io/en/python/2016/07/12/colorful-python-logging.html  -
#  https://en.wikipedia.org/wiki/ANSI_escape_code#Colors                        -
#                                                                               -
# -------------------------------------------------------------------------------

# Imports
import os
import sys
import logging

# Logging formatter supporting colored output
class LogFormatter(logging.Formatter):

    COLOR_CODES = {
        logging.CRITICAL: "\033[1;35m", # bright/bold magenta
        logging.ERROR:    "\033[1;31m", # bright/bold red
        logging.WARNING:  "\033[1;33m", # bright/bold yellow
        logging.INFO:     "\033[0;37m", # white / light gray
        logging.DEBUG:    "\033[1;30m"  # bright/bold black / dark gray
    }

    RESET_CODE = "\033[0m"

    def __init__(self, color, *args, **kwargs):
        super(LogFormatter, self).__init__(*args, **kwargs)
        self.color = color

    def format(self, record, *args, **kwargs):
        if (self.color == True and record.levelno in self.COLOR_CODES):
            record.color_on  = self.COLOR_CODES[record.levelno]
            record.color_off = self.RESET_CODE
        else:
            record.color_on  = ""
            record.color_off = ""
        return super(LogFormatter, self).format(record, *args, **kwargs)

# Setup logging
def setup_logging(console_log_output, console_log_level, console_log_color, logfile_file, logfile_log_level, logfile_log_color, log_line_template):

    # Create logger
    # For simplicity, we use the root logger, i.e. call 'logging.getLogger()'
    # without name argument. This way we can simply use module methods for
    # for logging throughout the script. An alternative would be exporting
    # the logger, i.e. 'global logger; logger = logging.getLogger("<name>")'
    logger = logging.getLogger()

    # Set global log level to 'debug' (required for handler levels to work)
    logger.setLevel(logging.DEBUG)

    # Create console handler
    console_log_output = console_log_output.lower()
    if (console_log_output == "stdout"):
        console_log_output = sys.stdout
    elif (console_log_output == "stderr"):
        console_log_output = sys.stderr
    else:
        print("Failed to set console output: invalid output: '%s'" % console_log_output)
        return False
    console_handler = logging.StreamHandler(console_log_output)

    # Set console log level
    try:
        console_handler.setLevel(console_log_level.upper()) # only accepts uppercase level names
    except:
        print("Failed to set console log level: invalid level: '%s'" % console_log_level)
        return False

    # Create and set formatter, add console handler to logger
    console_formatter = LogFormatter(fmt=log_line_template, color=console_log_color)
    console_handler.setFormatter(console_formatter)
    logger.addHandler(console_handler)

    # Create log file handler
    try:
        logfile_handler = logging.FileHandler(logfile_file)
    except Exception as exception:
        print("Failed to set up log file: %s" % str(exception))
        return False

    # Set log file log level
    try:
        logfile_handler.setLevel(logfile_log_level.upper()) # only accepts uppercase level names
    except:
        print("Failed to set log file log level: invalid level: '%s'" % logfile_log_level)
        return False

    # Create and set formatter, add log file handler to logger
    logfile_formatter = LogFormatter(fmt=log_line_template, color=logfile_log_color)
    logfile_handler.setFormatter(logfile_formatter)
    logger.addHandler(logfile_handler)

    # Success
    return True

# Main function
def main():

    # Setup logging
    script_name = os.path.splitext(os.path.basename(sys.argv[0]))[0]
    if (not setup_logging(console_log_output="stdout", console_log_level="warning", console_log_color=True,
                        logfile_file=script_name + ".log", logfile_log_level="debug", logfile_log_color=False,
                        log_line_template="%(color_on)s[%(created)d] [%(threadName)s] [%(levelname)-8s] %(message)s%(color_off)s")):
        print("Failed to setup logging, aborting.")
        return 1

    # Log some messages
    logging.debug("Debug message")
    logging.info("Info message")
    logging.warning("Warning message")
    logging.error("Error message")
    logging.critical("Critical message")

# Call main function
if (__name__ == "__main__"):
    sys.exit(main())

回答 7

对于2.7,请尝试以下操作:

fh = logging.handlers.RotatingFileHandler(LOGFILE, maxBytes=(1048576*5), backupCount=7)

For 2.7, try the following:

fh = logging.handlers.RotatingFileHandler(LOGFILE, maxBytes=(1048576*5), backupCount=7)

如何在python中找到两个日期时间对象之间的时差?

问题:如何在python中找到两个日期时间对象之间的时差?

如何分辨两个datetime物体之间的时间差(分钟)?

How do I tell the time difference in minutes between two datetime objects?


回答 0

>>> import datetime
>>> first_time = datetime.datetime.now()
>>> later_time = datetime.datetime.now()
>>> difference = later_time - first_time
>>> seconds_in_day = 24 * 60 * 60
datetime.timedelta(0, 8, 562000)
>>> divmod(difference.days * seconds_in_day + difference.seconds, 60)
(0, 8)      # 0 minutes, 8 seconds

从第一次减去之后的时间将difference = later_time - first_time创建一个仅保留时差的datetime对象。在上面的示例中,它是0分钟,8秒和562000微秒。

>>> import datetime
>>> first_time = datetime.datetime.now()
>>> later_time = datetime.datetime.now()
>>> difference = later_time - first_time
>>> seconds_in_day = 24 * 60 * 60
datetime.timedelta(0, 8, 562000)
>>> divmod(difference.days * seconds_in_day + difference.seconds, 60)
(0, 8)      # 0 minutes, 8 seconds

Subtracting the later time from the first time difference = later_time - first_time creates a datetime object that only holds the difference. In the example above it is 0 minutes, 8 seconds and 562000 microseconds.


回答 1

timedelta实例方法是Python 2.7的新功能.total_seconds()。在Python文档中,这等效于(td.microseconds + (td.seconds + td.days * 24 * 3600) * 10**6) / 10**6

参考:http : //docs.python.org/2/library/datetime.html#datetime.timedelta.total_seconds

>>> import datetime
>>> time1 = datetime.datetime.now()
>>> time2 = datetime.datetime.now() # waited a few minutes before pressing enter
>>> elapsedTime = time2 - time1
>>> elapsedTime
datetime.timedelta(0, 125, 749430)
>>> divmod(elapsedTime.total_seconds(), 60)
(2.0, 5.749430000000004) # divmod returns quotient and remainder
# 2 minutes, 5.74943 seconds

New at Python 2.7 is the timedelta instance method .total_seconds(). From the Python docs, this is equivalent to (td.microseconds + (td.seconds + td.days * 24 * 3600) * 10**6) / 10**6.

Reference: http://docs.python.org/2/library/datetime.html#datetime.timedelta.total_seconds

>>> import datetime
>>> time1 = datetime.datetime.now()
>>> time2 = datetime.datetime.now() # waited a few minutes before pressing enter
>>> elapsedTime = time2 - time1
>>> elapsedTime
datetime.timedelta(0, 125, 749430)
>>> divmod(elapsedTime.total_seconds(), 60)
(2.0, 5.749430000000004) # divmod returns quotient and remainder
# 2 minutes, 5.74943 seconds

回答 2

使用日期时间示例

>>> from datetime import datetime
>>> then = datetime(2012, 3, 5, 23, 8, 15)        # Random date in the past
>>> now  = datetime.now()                         # Now
>>> duration = now - then                         # For build-in functions
>>> duration_in_s = duration.total_seconds()      # Total number of seconds between dates

持续时间(年)

>>> years = divmod(duration_in_s, 31536000)[0]    # Seconds in a year=365*24*60*60 = 31536000.

持续天数

>>> days  = duration.days                         # Build-in datetime function
>>> days  = divmod(duration_in_s, 86400)[0]       # Seconds in a day = 86400

持续时间(小时)

>>> hours = divmod(duration_in_s, 3600)[0]        # Seconds in an hour = 3600

持续时间(分钟)

>>> minutes = divmod(duration_in_s, 60)[0]        # Seconds in a minute = 60

持续时间(秒)

>>> seconds = duration.seconds                    # Build-in datetime function
>>> seconds = duration_in_s

持续时间(以微秒为单位)

>>> microseconds = duration.microseconds          # Build-in datetime function  

两个日期之间的总持续时间

>>> days    = divmod(duration_in_s, 86400)        # Get days (without [0]!)
>>> hours   = divmod(days[1], 3600)               # Use remainder of days to calc hours
>>> minutes = divmod(hours[1], 60)                # Use remainder of hours to calc minutes
>>> seconds = divmod(minutes[1], 1)               # Use remainder of minutes to calc seconds
>>> print("Time between dates: %d days, %d hours, %d minutes and %d seconds" % (days[0], hours[0], minutes[0], seconds[0]))

或者简单地:

>>> print(now - then)

编辑2019 由于此答案已受到关注,因此我将添加一个函数,该函数可能会简化某些应用程序的用法

from datetime import datetime

def getDuration(then, now = datetime.now(), interval = "default"):

    # Returns a duration as specified by variable interval
    # Functions, except totalDuration, returns [quotient, remainder]

    duration = now - then # For build-in functions
    duration_in_s = duration.total_seconds() 

    def years():
      return divmod(duration_in_s, 31536000) # Seconds in a year=31536000.

    def days(seconds = None):
      return divmod(seconds if seconds != None else duration_in_s, 86400) # Seconds in a day = 86400

    def hours(seconds = None):
      return divmod(seconds if seconds != None else duration_in_s, 3600) # Seconds in an hour = 3600

    def minutes(seconds = None):
      return divmod(seconds if seconds != None else duration_in_s, 60) # Seconds in a minute = 60

    def seconds(seconds = None):
      if seconds != None:
        return divmod(seconds, 1)   
      return duration_in_s

    def totalDuration():
        y = years()
        d = days(y[1]) # Use remainder to calculate next variable
        h = hours(d[1])
        m = minutes(h[1])
        s = seconds(m[1])

        return "Time between dates: {} years, {} days, {} hours, {} minutes and {} seconds".format(int(y[0]), int(d[0]), int(h[0]), int(m[0]), int(s[0]))

    return {
        'years': int(years()[0]),
        'days': int(days()[0]),
        'hours': int(hours()[0]),
        'minutes': int(minutes()[0]),
        'seconds': int(seconds()),
        'default': totalDuration()
    }[interval]

# Example usage
then = datetime(2012, 3, 5, 23, 8, 15)
now = datetime.now()

print(getDuration(then)) # E.g. Time between dates: 7 years, 208 days, 21 hours, 19 minutes and 15 seconds
print(getDuration(then, now, 'years'))      # Prints duration in years
print(getDuration(then, now, 'days'))       #                    days
print(getDuration(then, now, 'hours'))      #                    hours
print(getDuration(then, now, 'minutes'))    #                    minutes
print(getDuration(then, now, 'seconds'))    #                    seconds

Using datetime example

>>> from datetime import datetime
>>> then = datetime(2012, 3, 5, 23, 8, 15)        # Random date in the past
>>> now  = datetime.now()                         # Now
>>> duration = now - then                         # For build-in functions
>>> duration_in_s = duration.total_seconds()      # Total number of seconds between dates

Duration in years

>>> years = divmod(duration_in_s, 31536000)[0]    # Seconds in a year=365*24*60*60 = 31536000.

Duration in days

>>> days  = duration.days                         # Build-in datetime function
>>> days  = divmod(duration_in_s, 86400)[0]       # Seconds in a day = 86400

Duration in hours

>>> hours = divmod(duration_in_s, 3600)[0]        # Seconds in an hour = 3600

Duration in minutes

>>> minutes = divmod(duration_in_s, 60)[0]        # Seconds in a minute = 60

Duration in seconds

>>> seconds = duration.seconds                    # Build-in datetime function
>>> seconds = duration_in_s

Duration in microseconds

>>> microseconds = duration.microseconds          # Build-in datetime function  

Total duration between the two dates

>>> days    = divmod(duration_in_s, 86400)        # Get days (without [0]!)
>>> hours   = divmod(days[1], 3600)               # Use remainder of days to calc hours
>>> minutes = divmod(hours[1], 60)                # Use remainder of hours to calc minutes
>>> seconds = divmod(minutes[1], 1)               # Use remainder of minutes to calc seconds
>>> print("Time between dates: %d days, %d hours, %d minutes and %d seconds" % (days[0], hours[0], minutes[0], seconds[0]))

or simply:

>>> print(now - then)

Edit 2019 Since this answer has gained traction, I’ll add a function, which might simplify the usage for some

from datetime import datetime

def getDuration(then, now = datetime.now(), interval = "default"):

    # Returns a duration as specified by variable interval
    # Functions, except totalDuration, returns [quotient, remainder]

    duration = now - then # For build-in functions
    duration_in_s = duration.total_seconds() 

    def years():
      return divmod(duration_in_s, 31536000) # Seconds in a year=31536000.

    def days(seconds = None):
      return divmod(seconds if seconds != None else duration_in_s, 86400) # Seconds in a day = 86400

    def hours(seconds = None):
      return divmod(seconds if seconds != None else duration_in_s, 3600) # Seconds in an hour = 3600

    def minutes(seconds = None):
      return divmod(seconds if seconds != None else duration_in_s, 60) # Seconds in a minute = 60

    def seconds(seconds = None):
      if seconds != None:
        return divmod(seconds, 1)   
      return duration_in_s

    def totalDuration():
        y = years()
        d = days(y[1]) # Use remainder to calculate next variable
        h = hours(d[1])
        m = minutes(h[1])
        s = seconds(m[1])

        return "Time between dates: {} years, {} days, {} hours, {} minutes and {} seconds".format(int(y[0]), int(d[0]), int(h[0]), int(m[0]), int(s[0]))

    return {
        'years': int(years()[0]),
        'days': int(days()[0]),
        'hours': int(hours()[0]),
        'minutes': int(minutes()[0]),
        'seconds': int(seconds()),
        'default': totalDuration()
    }[interval]

# Example usage
then = datetime(2012, 3, 5, 23, 8, 15)
now = datetime.now()

print(getDuration(then)) # E.g. Time between dates: 7 years, 208 days, 21 hours, 19 minutes and 15 seconds
print(getDuration(then, now, 'years'))      # Prints duration in years
print(getDuration(then, now, 'days'))       #                    days
print(getDuration(then, now, 'hours'))      #                    hours
print(getDuration(then, now, 'minutes'))    #                    minutes
print(getDuration(then, now, 'seconds'))    #                    seconds

回答 3

只需从另一个中减去一个即可。您会得到一个timedelta与众不同的对象。

>>> import datetime
>>> d1 = datetime.datetime.now()
>>> d2 = datetime.datetime.now() # after a 5-second or so pause
>>> d2 - d1
datetime.timedelta(0, 5, 203000)

你可以转换dd.daysdd.secondsdd.microseconds以分钟。

Just subtract one from the other. You get a timedelta object with the difference.

>>> import datetime
>>> d1 = datetime.datetime.now()
>>> d2 = datetime.datetime.now() # after a 5-second or so pause
>>> d2 - d1
datetime.timedelta(0, 5, 203000)

You can convert dd.days, dd.seconds and dd.microseconds to minutes.


回答 4

如果ab是datetime对象,然后找到在Python 3它们之间的时间差:

from datetime import timedelta

time_difference = a - b
time_difference_in_minutes = time_difference / timedelta(minutes=1)

在早期的Python版本上:

time_difference_in_minutes = time_difference.total_seconds() / 60

如果ab是通过返回的天真的日期时间对象,datetime.now()那么结果可能是错的,如果该对象表示本地时间与UTC不同偏移例如,围绕DST转换或过去/将来的日期。更多详细信息:查找日期时间之间是否经过了24小时-Python

为了获得可靠的结果,请使用UTC时间或可识别时区的datetime对象。

If a, b are datetime objects then to find the time difference between them in Python 3:

from datetime import timedelta

time_difference = a - b
time_difference_in_minutes = time_difference / timedelta(minutes=1)

On earlier Python versions:

time_difference_in_minutes = time_difference.total_seconds() / 60

If a, b are naive datetime objects such as returned by datetime.now() then the result may be wrong if the objects represent local time with different UTC offsets e.g., around DST transitions or for past/future dates. More details: Find if 24 hrs have passed between datetimes – Python.

To get reliable results, use UTC time or timezone-aware datetime objects.


回答 5

使用divmod:

now = int(time.time()) # epoch seconds
then = now - 90000 # some time in the past

d = divmod(now-then,86400)  # days
h = divmod(d[1],3600)  # hours
m = divmod(h[1],60)  # minutes
s = m[1]  # seconds

print '%d days, %d hours, %d minutes, %d seconds' % (d[0],h[0],m[0],s)

Use divmod:

now = int(time.time()) # epoch seconds
then = now - 90000 # some time in the past

d = divmod(now-then,86400)  # days
h = divmod(d[1],3600)  # hours
m = divmod(h[1],60)  # minutes
s = m[1]  # seconds

print '%d days, %d hours, %d minutes, %d seconds' % (d[0],h[0],m[0],s)

回答 6

这是我如何获取两个datetime.datetime对象之间经过的小时数:

before = datetime.datetime.now()
after  = datetime.datetime.now()
hours  = math.floor(((after - before).seconds) / 3600)

This is how I get the number of hours that elapsed between two datetime.datetime objects:

before = datetime.datetime.now()
after  = datetime.datetime.now()
hours  = math.floor(((after - before).seconds) / 3600)

回答 7

仅查找天数:timedelta具有“天数”属性。您可以简单地查询。

>>>from datetime import datetime, timedelta
>>>d1 = datetime(2015, 9, 12, 13, 9, 45)
>>>d2 = datetime(2015, 8, 29, 21, 10, 12)
>>>d3 = d1- d2
>>>print d3
13 days, 15:59:33
>>>print d3.days
13

To just find the number of days: timedelta has a ‘days’ attribute. You can simply query that.

>>>from datetime import datetime, timedelta
>>>d1 = datetime(2015, 9, 12, 13, 9, 45)
>>>d2 = datetime(2015, 8, 29, 21, 10, 12)
>>>d3 = d1- d2
>>>print d3
13 days, 15:59:33
>>>print d3.days
13

回答 8

只是认为考虑到timedelta的格式问题也很有用。strptime()根据格式解析表示时间的字符串。

from datetime import datetime

datetimeFormat = '%Y/%m/%d %H:%M:%S.%f'    
time1 = '2016/03/16 10:01:28.585'
time2 = '2016/03/16 09:56:28.067'  
time_dif = datetime.strptime(time1, datetimeFormat) - datetime.strptime(time2,datetimeFormat)
print(time_dif)

这将输出:0:05:00.518000

Just thought it might be useful to mention formatting as well in regards to timedelta. strptime() parses a string representing a time according to a format.

from datetime import datetime

datetimeFormat = '%Y/%m/%d %H:%M:%S.%f'    
time1 = '2016/03/16 10:01:28.585'
time2 = '2016/03/16 09:56:28.067'  
time_dif = datetime.strptime(time1, datetimeFormat) - datetime.strptime(time2,datetimeFormat)
print(time_dif)

This will output: 0:05:00.518000


回答 9

我用这样的东西:

from datetime import datetime

def check_time_difference(t1: datetime, t2: datetime):
    t1_date = datetime(
        t1.year,
        t1.month,
        t1.day,
        t1.hour,
        t1.minute,
        t1.second)

    t2_date = datetime(
        t2.year,
        t2.month,
        t2.day,
        t2.hour,
        t2.minute,
        t2.second)

    t_elapsed = t1_date - t2_date

    return t_elapsed

# usage 
f = "%Y-%m-%d %H:%M:%S+01:00"
t1 = datetime.strptime("2018-03-07 22:56:57+01:00", f)
t2 = datetime.strptime("2018-03-07 22:48:05+01:00", f)
elapsed_time = check_time_difference(t1, t2)

print(elapsed_time)
#return : 0:08:52

I use somethign like this :

from datetime import datetime

def check_time_difference(t1: datetime, t2: datetime):
    t1_date = datetime(
        t1.year,
        t1.month,
        t1.day,
        t1.hour,
        t1.minute,
        t1.second)

    t2_date = datetime(
        t2.year,
        t2.month,
        t2.day,
        t2.hour,
        t2.minute,
        t2.second)

    t_elapsed = t1_date - t2_date

    return t_elapsed

# usage 
f = "%Y-%m-%d %H:%M:%S+01:00"
t1 = datetime.strptime("2018-03-07 22:56:57+01:00", f)
t2 = datetime.strptime("2018-03-07 22:48:05+01:00", f)
elapsed_time = check_time_difference(t1, t2)

print(elapsed_time)
#return : 0:08:52

回答 10

这是为了找出当前时间和9.30 am之间的时差

t=datetime.now()-datetime.now().replace(hour=9,minute=30)

this is to find the difference between current time and 9.30 am

t=datetime.now()-datetime.now().replace(hour=9,minute=30)

回答 11

这是我使用mktime的方法。

from datetime import datetime, timedelta
from time import mktime

yesterday = datetime.now() - timedelta(days=1)
today = datetime.now()

difference_in_seconds = abs(mktime(yesterday.timetuple()) - mktime(today.timetuple()))
difference_in_minutes = difference_in_seconds / 60

This is my approach using mktime.

from datetime import datetime, timedelta
from time import mktime

yesterday = datetime.now() - timedelta(days=1)
today = datetime.now()

difference_in_seconds = abs(mktime(yesterday.timetuple()) - mktime(today.timetuple()))
difference_in_minutes = difference_in_seconds / 60

回答 12

以其他方式获取日期之间的差异;

import dateutil.parser
import datetime
last_sent_date = "" # date string
timeDifference = current_date - dateutil.parser.parse(last_sent_date)
time_difference_in_minutes = (int(timeDifference.days) * 24 * 60) + int((timeDifference.seconds) / 60)

因此,以分钟为单位获取输出。

谢谢

In Other ways to get difference between date;

import dateutil.parser
import datetime
last_sent_date = "" # date string
timeDifference = current_date - dateutil.parser.parse(last_sent_date)
time_difference_in_minutes = (int(timeDifference.days) * 24 * 60) + int((timeDifference.seconds) / 60)

So get output in Min.

Thanks


回答 13

我使用时差进行连续集成测试,以检查和改进我的功能。如果有人需要,这是简单的代码

from datetime import datetime

class TimeLogger:
    time_cursor = None

    def pin_time(self):
        global time_cursor
        time_cursor = datetime.now()

    def log(self, text=None) -> float:
        global time_cursor

        if not time_cursor:
            time_cursor = datetime.now()

        now = datetime.now()
        t_delta = now - time_cursor

        seconds = t_delta.total_seconds()

        result = str(now) + ' tl -----------> %.5f' % seconds
        if text:
            result += "   " + text
        print(result)

        self.pin_time()

        return seconds


time_logger = TimeLogger()

使用:

from .tests_time_logger import time_logger
class Tests(TestCase):
    def test_workflow(self):
    time_logger.pin_time()

    ... my functions here ...

    time_logger.log()

    ... other function(s) ...

    time_logger.log(text='Tests finished')

我在日志输出中有类似的东西

2019-12-20 17:19:23.635297 tl -----------> 0.00007
2019-12-20 17:19:28.147656 tl -----------> 4.51234   Tests finished

I have used time differences for continuous integration tests to check and improve my functions. Here is simple code if somebody need it

from datetime import datetime

class TimeLogger:
    time_cursor = None

    def pin_time(self):
        global time_cursor
        time_cursor = datetime.now()

    def log(self, text=None) -> float:
        global time_cursor

        if not time_cursor:
            time_cursor = datetime.now()

        now = datetime.now()
        t_delta = now - time_cursor

        seconds = t_delta.total_seconds()

        result = str(now) + ' tl -----------> %.5f' % seconds
        if text:
            result += "   " + text
        print(result)

        self.pin_time()

        return seconds


time_logger = TimeLogger()

Using:

from .tests_time_logger import time_logger
class Tests(TestCase):
    def test_workflow(self):
    time_logger.pin_time()

    ... my functions here ...

    time_logger.log()

    ... other function(s) ...

    time_logger.log(text='Tests finished')

and i have something like that in log output

2019-12-20 17:19:23.635297 tl -----------> 0.00007
2019-12-20 17:19:28.147656 tl -----------> 4.51234   Tests finished

回答 14

基于@Attaque很好的答案,我提出了一个日期时间差计算器的简化版本:

seconds_mapping = {
    'y': 31536000,
    'm': 2628002.88, # this is approximate, 365 / 12; use with caution
    'w': 604800,
    'd': 86400,
    'h': 3600,
    'min': 60,
    's': 1,
    'mil': 0.001,
}

def get_duration(d1, d2, interval, with_reminder=False):
    if with_reminder:
        return divmod((d2 - d1).total_seconds(), seconds_mapping[interval])
    else:
        return (d2 - d1).total_seconds() / seconds_mapping[interval]

我已对其进行了更改,以避免声明重复功能,删除了漂亮的打印默认时间间隔,并增加了对毫秒,周和ISO个月的支持(请注意,月份仅是近似值,假设每个月都等于365/12)。

生成:

d1 = datetime(2011, 3, 1, 1, 1, 1, 1000)
d2 = datetime(2011, 4, 1, 1, 1, 1, 2500)

print(get_duration(d1, d2, 'y', True))      # => (0.0, 2678400.0015)
print(get_duration(d1, d2, 'm', True))      # => (1.0, 50397.12149999989)
print(get_duration(d1, d2, 'w', True))      # => (4.0, 259200.00149999978)
print(get_duration(d1, d2, 'd', True))      # => (31.0, 0.0014999997802078724)
print(get_duration(d1, d2, 'h', True))      # => (744.0, 0.0014999997802078724)
print(get_duration(d1, d2, 'min', True))    # => (44640.0, 0.0014999997802078724)
print(get_duration(d1, d2, 's', True))      # => (2678400.0, 0.0014999997802078724)
print(get_duration(d1, d2, 'mil', True))    # => (2678400001.0, 0.0004999997244524721)

print(get_duration(d1, d2, 'y', False))     # => 0.08493150689687975
print(get_duration(d1, d2, 'm', False))     # => 1.019176965856293
print(get_duration(d1, d2, 'w', False))     # => 4.428571431051587
print(get_duration(d1, d2, 'd', False))     # => 31.00000001736111
print(get_duration(d1, d2, 'h', False))     # => 744.0000004166666
print(get_duration(d1, d2, 'min', False))   # => 44640.000024999994
print(get_duration(d1, d2, 's', False))     # => 2678400.0015
print(get_duration(d1, d2, 'mil', False))   # => 2678400001.4999995

Based on @Attaque great answer, I propose a shorter simplified version of the datetime difference calculator:

seconds_mapping = {
    'y': 31536000,
    'm': 2628002.88, # this is approximate, 365 / 12; use with caution
    'w': 604800,
    'd': 86400,
    'h': 3600,
    'min': 60,
    's': 1,
    'mil': 0.001,
}

def get_duration(d1, d2, interval, with_reminder=False):
    if with_reminder:
        return divmod((d2 - d1).total_seconds(), seconds_mapping[interval])
    else:
        return (d2 - d1).total_seconds() / seconds_mapping[interval]

I’ve changed it to avoid declaring repetetive functions, removed the pretty print default interval and added support for milliseconds, weeks and ISO months (bare in mind months are just approximate, based on assumption that each month is equal to 365/12).

Which produces:

d1 = datetime(2011, 3, 1, 1, 1, 1, 1000)
d2 = datetime(2011, 4, 1, 1, 1, 1, 2500)

print(get_duration(d1, d2, 'y', True))      # => (0.0, 2678400.0015)
print(get_duration(d1, d2, 'm', True))      # => (1.0, 50397.12149999989)
print(get_duration(d1, d2, 'w', True))      # => (4.0, 259200.00149999978)
print(get_duration(d1, d2, 'd', True))      # => (31.0, 0.0014999997802078724)
print(get_duration(d1, d2, 'h', True))      # => (744.0, 0.0014999997802078724)
print(get_duration(d1, d2, 'min', True))    # => (44640.0, 0.0014999997802078724)
print(get_duration(d1, d2, 's', True))      # => (2678400.0, 0.0014999997802078724)
print(get_duration(d1, d2, 'mil', True))    # => (2678400001.0, 0.0004999997244524721)

print(get_duration(d1, d2, 'y', False))     # => 0.08493150689687975
print(get_duration(d1, d2, 'm', False))     # => 1.019176965856293
print(get_duration(d1, d2, 'w', False))     # => 4.428571431051587
print(get_duration(d1, d2, 'd', False))     # => 31.00000001736111
print(get_duration(d1, d2, 'h', False))     # => 744.0000004166666
print(get_duration(d1, d2, 'min', False))   # => 44640.000024999994
print(get_duration(d1, d2, 's', False))     # => 2678400.0015
print(get_duration(d1, d2, 'mil', False))   # => 2678400001.4999995

如何将日期时间对象转换为自epoch(unix时间)以来的毫秒数?

问题:如何将日期时间对象转换为自epoch(unix时间)以来的毫秒数?

我有一个Python datetime对象,我想将其转换为Unix时间,即自1970年以来的秒/毫秒。

我该怎么做呢?

I have a Python datetime object that I want to convert to unix time, or seconds/milliseconds since the 1970 epoch.

How do I do this?


回答 0

在我看来,最简单的方法是

import datetime

epoch = datetime.datetime.utcfromtimestamp(0)

def unix_time_millis(dt):
    return (dt - epoch).total_seconds() * 1000.0

It appears to me that the simplest way to do this is

import datetime

epoch = datetime.datetime.utcfromtimestamp(0)

def unix_time_millis(dt):
    return (dt - epoch).total_seconds() * 1000.0

回答 1

在Python 3.3中,添加了新方法timestamp

import datetime
seconds_since_epoch = datetime.datetime.now().timestamp()

您的问题表明您需要毫秒,您可以这样获得:

milliseconds_since_epoch = datetime.datetime.now().timestamp() * 1000

如果您使用 timestamp在朴素的datetime对象上使用,则假定它在本地时区中。如果这不是您打算发生的事情,请使用可识别时区的日期时间对象。

In Python 3.3, added new method timestamp:

import datetime
seconds_since_epoch = datetime.datetime.now().timestamp()

Your question stated that you needed milliseconds, which you can get like this:

milliseconds_since_epoch = datetime.datetime.now().timestamp() * 1000

If you use timestamp on a naive datetime object, then it assumed that it is in the local timezone. Use timezone-aware datetime objects if this is not what you intend to happen.


回答 2

>>> import datetime
>>> # replace datetime.datetime.now() with your datetime object
>>> int(datetime.datetime.now().strftime("%s")) * 1000 
1312908481000

或时间模块的帮助(不带日期格式):

>>> import datetime, time
>>> # replace datetime.datetime.now() with your datetime object
>>> time.mktime(datetime.datetime.now().timetuple()) * 1000
1312908681000.0

在以下网站的帮助下回答:http//pleac.sourceforge.net/pleac_python/datesandtimes.html

说明文件:

>>> import datetime
>>> # replace datetime.datetime.now() with your datetime object
>>> int(datetime.datetime.now().strftime("%s")) * 1000 
1312908481000

Or the help of the time module (and without date formatting):

>>> import datetime, time
>>> # replace datetime.datetime.now() with your datetime object
>>> time.mktime(datetime.datetime.now().timetuple()) * 1000
1312908681000.0

Answered with help from: http://pleac.sourceforge.net/pleac_python/datesandtimes.html

Documentation:


回答 3

您可以使用Delorean来时空旅行!

import datetime
import delorean
dt = datetime.datetime.utcnow()
delorean.Delorean(dt, timezone="UTC").epoch

http://delorean.readthedocs.org/en/latest/quickstart.html  

You can use Delorean to travel in space and time!

import datetime
import delorean
dt = datetime.datetime.utcnow()
delorean.Delorean(dt, timezone="UTC").epoch

http://delorean.readthedocs.org/en/latest/quickstart.html  


回答 4

这是我的方法:

from datetime import datetime
from time import mktime

dt = datetime.now()
sec_since_epoch = mktime(dt.timetuple()) + dt.microsecond/1000000.0

millis_since_epoch = sec_since_epoch * 1000

This is how I do it:

from datetime import datetime
from time import mktime

dt = datetime.now()
sec_since_epoch = mktime(dt.timetuple()) + dt.microsecond/1000000.0

millis_since_epoch = sec_since_epoch * 1000

回答 5

从Recommendedations 的Python 2.7文档的time模块

在时间表示之间进行转换


回答 6

from datetime import datetime
from calendar import timegm

# Note: if you pass in a naive dttm object it's assumed to already be in UTC
def unix_time(dttm=None):
    if dttm is None:
       dttm = datetime.utcnow()

    return timegm(dttm.utctimetuple())

print "Unix time now: %d" % unix_time()
print "Unix timestamp from an existing dttm: %d" % unix_time(datetime(2014, 12, 30, 12, 0))
from datetime import datetime
from calendar import timegm

# Note: if you pass in a naive dttm object it's assumed to already be in UTC
def unix_time(dttm=None):
    if dttm is None:
       dttm = datetime.utcnow()

    return timegm(dttm.utctimetuple())

print "Unix time now: %d" % unix_time()
print "Unix timestamp from an existing dttm: %d" % unix_time(datetime(2014, 12, 30, 12, 0))

回答 7

>>> import datetime
>>> import time
>>> import calendar

>>> #your datetime object
>>> now = datetime.datetime.now()
>>> now
datetime.datetime(2013, 3, 19, 13, 0, 9, 351812)

>>> #use datetime module's timetuple method to get a `time.struct_time` object.[1]
>>> tt = datetime.datetime.timetuple(now)
>>> tt
time.struct_time(tm_year=2013, tm_mon=3, tm_mday=19, tm_hour=13, tm_min=0, tm_sec=9,     tm_wday=1, tm_yday=78, tm_isdst=-1)

>>> #If your datetime object is in utc you do this way. [2](see the first table on docs)
>>> sec_epoch_utc = calendar.timegm(tt) * 1000
>>> sec_epoch_utc
1363698009

>>> #If your datetime object is in local timeformat you do this way
>>> sec_epoch_loc = time.mktime(tt) * 1000
>>> sec_epoch_loc
1363678209.0

[1] http://docs.python.org/2/library/datetime.html#datetime.date.timetuple

[2] http://docs.python.org/2/library/time.html

>>> import datetime
>>> import time
>>> import calendar

>>> #your datetime object
>>> now = datetime.datetime.now()
>>> now
datetime.datetime(2013, 3, 19, 13, 0, 9, 351812)

>>> #use datetime module's timetuple method to get a `time.struct_time` object.[1]
>>> tt = datetime.datetime.timetuple(now)
>>> tt
time.struct_time(tm_year=2013, tm_mon=3, tm_mday=19, tm_hour=13, tm_min=0, tm_sec=9,     tm_wday=1, tm_yday=78, tm_isdst=-1)

>>> #If your datetime object is in utc you do this way. [2](see the first table on docs)
>>> sec_epoch_utc = calendar.timegm(tt) * 1000
>>> sec_epoch_utc
1363698009

>>> #If your datetime object is in local timeformat you do this way
>>> sec_epoch_loc = time.mktime(tt) * 1000
>>> sec_epoch_loc
1363678209.0

[1] http://docs.python.org/2/library/datetime.html#datetime.date.timetuple

[2] http://docs.python.org/2/library/time.html


回答 8

这是时间对象标准化的另一种解决方案形式:

def to_unix_time(timestamp):
    epoch = datetime.datetime.utcfromtimestamp(0) # start of epoch time
    my_time = datetime.datetime.strptime(timestamp, "%Y/%m/%d %H:%M:%S.%f") # plugin your time object
    delta = my_time - epoch
    return delta.total_seconds() * 1000.0

Here’s another form of a solution with normalization of your time object:

def to_unix_time(timestamp):
    epoch = datetime.datetime.utcfromtimestamp(0) # start of epoch time
    my_time = datetime.datetime.strptime(timestamp, "%Y/%m/%d %H:%M:%S.%f") # plugin your time object
    delta = my_time - epoch
    return delta.total_seconds() * 1000.0

回答 9

一点熊猫代码:

import pandas

def to_millis(dt):
    return int(pandas.to_datetime(dt).value / 1000000)

A bit of pandas code:

import pandas

def to_millis(dt):
    return int(pandas.to_datetime(dt).value / 1000000)

回答 10

import time
seconds_since_epoch = time.mktime(your_datetime.timetuple()) * 1000
import time
seconds_since_epoch = time.mktime(your_datetime.timetuple()) * 1000

回答 11

这是我根据以上答案做出的功能

def getDateToEpoch(myDateTime):
    res = (datetime.datetime(myDateTime.year,myDateTime.month,myDateTime.day,myDateTime.hour,myDateTime.minute,myDateTime.second) - datetime.datetime(1970,1,1)).total_seconds()
    return res

您可以像这样包装返回的值:str(int(res))要返回它而没有用作字符串的十进制值或仅用作int(不带str)

Here is a function I made based on the answer above

def getDateToEpoch(myDateTime):
    res = (datetime.datetime(myDateTime.year,myDateTime.month,myDateTime.day,myDateTime.hour,myDateTime.minute,myDateTime.second) - datetime.datetime(1970,1,1)).total_seconds()
    return res

You can wrap the returned value like this : str(int(res)) To return it without a decimal value to be used as string or just int (without the str)


回答 12

这是unixtimestampmillis的秘密日期时间的另一种解决方案。

private static readonly DateTime UnixEpoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);

    public static long GetCurrentUnixTimestampMillis()
    {
        DateTime localDateTime, univDateTime;
        localDateTime = DateTime.Now;          
        univDateTime = localDateTime.ToUniversalTime();
        return (long)(univDateTime - UnixEpoch).TotalMilliseconds;
    } 

This other solution for covert datetime to unixtimestampmillis.

private static readonly DateTime UnixEpoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);

    public static long GetCurrentUnixTimestampMillis()
    {
        DateTime localDateTime, univDateTime;
        localDateTime = DateTime.Now;          
        univDateTime = localDateTime.ToUniversalTime();
        return (long)(univDateTime - UnixEpoch).TotalMilliseconds;
    } 

如何判断字符串是否在Python中重复?

问题:如何判断字符串是否在Python中重复?

我正在寻找一种方法来测试给定的字符串是否对整个字符串重复。

例子:

[
    '0045662100456621004566210045662100456621',             # '00456621'
    '0072992700729927007299270072992700729927',             # '00729927'
    '001443001443001443001443001443001443001443',           # '001443'
    '037037037037037037037037037037037037037037037',        # '037'
    '047619047619047619047619047619047619047619',           # '047619'
    '002457002457002457002457002457002457002457',           # '002457'
    '001221001221001221001221001221001221001221',           # '001221'
    '001230012300123001230012300123001230012300123',        # '00123'
    '0013947001394700139470013947001394700139470013947',    # '0013947'
    '001001001001001001001001001001001001001001001001001',  # '001'
    '001406469760900140646976090014064697609',              # '0014064697609'
]

是重复的字符串,并且

[
    '004608294930875576036866359447',
    '00469483568075117370892018779342723',
    '004739336492890995260663507109',
    '001508295625942684766214177978883861236802413273',
    '007518796992481203',
    '0071942446043165467625899280575539568345323741',
    '0434782608695652173913',
    '0344827586206896551724137931',
    '002481389578163771712158808933',
    '002932551319648093841642228739',
    '0035587188612099644128113879',
    '003484320557491289198606271777',
    '00115074798619102416570771',
]

是那些没有的例子。

我得到的字符串的重复部分可能会很长,并且字符串本身可以是500个或更多字符,因此循环遍历每个字符以尝试构建模式,然后检查模式与字符串的其余部分似乎很慢。将其乘以可能的数百个字符串,就看不到任何直观的解决方案。

我对正则表达式进行了一些研究,当您知道要查找的内容时,或者至少在寻找所需模式的长度时,它们似乎非常有用。不幸的是,我都不知道。

我怎么知道一个字符串是否在重复本身,如果是,最短的重复子序列是什么?

I’m looking for a way to test whether or not a given string repeats itself for the entire string or not.

Examples:

[
    '0045662100456621004566210045662100456621',             # '00456621'
    '0072992700729927007299270072992700729927',             # '00729927'
    '001443001443001443001443001443001443001443',           # '001443'
    '037037037037037037037037037037037037037037037',        # '037'
    '047619047619047619047619047619047619047619',           # '047619'
    '002457002457002457002457002457002457002457',           # '002457'
    '001221001221001221001221001221001221001221',           # '001221'
    '001230012300123001230012300123001230012300123',        # '00123'
    '0013947001394700139470013947001394700139470013947',    # '0013947'
    '001001001001001001001001001001001001001001001001001',  # '001'
    '001406469760900140646976090014064697609',              # '0014064697609'
]

are strings which repeat themselves, and

[
    '004608294930875576036866359447',
    '00469483568075117370892018779342723',
    '004739336492890995260663507109',
    '001508295625942684766214177978883861236802413273',
    '007518796992481203',
    '0071942446043165467625899280575539568345323741',
    '0434782608695652173913',
    '0344827586206896551724137931',
    '002481389578163771712158808933',
    '002932551319648093841642228739',
    '0035587188612099644128113879',
    '003484320557491289198606271777',
    '00115074798619102416570771',
]

are examples of ones that do not.

The repeating sections of the strings I’m given can be quite long, and the strings themselves can be 500 or more characters, so looping through each character trying to build a pattern then checking the pattern vs the rest of the string seems awful slow. Multiply that by potentially hundreds of strings and I can’t see any intuitive solution.

I’ve looked into regexes a bit and they seem good for when you know what you’re looking for, or at least the length of the pattern you’re looking for. Unfortunately, I know neither.

How can I tell if a string is repeating itself and if it is, what the shortest repeating subsequence is?


回答 0

这是一个简洁的解决方案,它避免了正则表达式和缓慢的Python内循环:

def principal_period(s):
    i = (s+s).find(s, 1, -1)
    return None if i == -1 else s[:i]

有关基准测试结果,请参阅由@davidism发起的Community Wiki答案。综上所述,

David Zhang的解决方案无疑是赢家,对于大型示例集,其性能至少比其他同类产品高出5倍。

(那个答案的话,不是我的。)

这是基于这样的观察,即且仅当字符串等于其自身的非平凡旋转时,它才是周期性的。@AleksiTorhamo的荣誉,意识到我们可以从sin 的第一次出现的索引中恢复本金周期(s+s)[1:-1],并告知我Python的可选参数start和自end变量string.find

Here’s a concise solution which avoids regular expressions and slow in-Python loops:

def principal_period(s):
    i = (s+s).find(s, 1, -1)
    return None if i == -1 else s[:i]

See the Community Wiki answer started by @davidism for benchmark results. In summary,

David Zhang’s solution is the clear winner, outperforming all others by at least 5x for the large example set.

(That answer’s words, not mine.)

This is based on the observation that a string is periodic if and only if it is equal to a nontrivial rotation of itself. Kudos to @AleksiTorhamo for realizing that we can then recover the principal period from the index of the first occurrence of s in (s+s)[1:-1], and for informing me of the optional start and end arguments of Python’s string.find.


回答 1

这是使用正则表达式的解决方案。

import re

REPEATER = re.compile(r"(.+?)\1+$")

def repeated(s):
    match = REPEATER.match(s)
    return match.group(1) if match else None

迭代问题中的示例:

examples = [
    '0045662100456621004566210045662100456621',
    '0072992700729927007299270072992700729927',
    '001443001443001443001443001443001443001443',
    '037037037037037037037037037037037037037037037',
    '047619047619047619047619047619047619047619',
    '002457002457002457002457002457002457002457',
    '001221001221001221001221001221001221001221',
    '001230012300123001230012300123001230012300123',
    '0013947001394700139470013947001394700139470013947',
    '001001001001001001001001001001001001001001001001001',
    '001406469760900140646976090014064697609',
    '004608294930875576036866359447',
    '00469483568075117370892018779342723',
    '004739336492890995260663507109',
    '001508295625942684766214177978883861236802413273',
    '007518796992481203',
    '0071942446043165467625899280575539568345323741',
    '0434782608695652173913',
    '0344827586206896551724137931',
    '002481389578163771712158808933',
    '002932551319648093841642228739',
    '0035587188612099644128113879',
    '003484320557491289198606271777',
    '00115074798619102416570771',
]

for e in examples:
    sub = repeated(e)
    if sub:
        print("%r: %r" % (e, sub))
    else:
        print("%r does not repeat." % e)

…产生以下输出:

'0045662100456621004566210045662100456621': '00456621'
'0072992700729927007299270072992700729927': '00729927'
'001443001443001443001443001443001443001443': '001443'
'037037037037037037037037037037037037037037037': '037'
'047619047619047619047619047619047619047619': '047619'
'002457002457002457002457002457002457002457': '002457'
'001221001221001221001221001221001221001221': '001221'
'001230012300123001230012300123001230012300123': '00123'
'0013947001394700139470013947001394700139470013947': '0013947'
'001001001001001001001001001001001001001001001001001': '001'
'001406469760900140646976090014064697609': '0014064697609'
'004608294930875576036866359447' does not repeat.
'00469483568075117370892018779342723' does not repeat.
'004739336492890995260663507109' does not repeat.
'001508295625942684766214177978883861236802413273' does not repeat.
'007518796992481203' does not repeat.
'0071942446043165467625899280575539568345323741' does not repeat.
'0434782608695652173913' does not repeat.
'0344827586206896551724137931' does not repeat.
'002481389578163771712158808933' does not repeat.
'002932551319648093841642228739' does not repeat.
'0035587188612099644128113879' does not repeat.
'003484320557491289198606271777' does not repeat.
'00115074798619102416570771' does not repeat.

正则表达式(.+?)\1+$分为三个部分:

  1. (.+?)是一个匹配组,其中包含至少一个(但尽可能少)任何字符(因为+?不是贪婪)。

  2. \1+ 在第一部分中检查匹配组的至少一个重复。

  3. $检查字符串的结尾,以确保在重复的子字符串之后没有多余的,非重复的内容(并使用re.match()确保在重复的子字符串之前没有非重复的文本)。

在Python 3.4和更高版本中,您可以删除$re.fullmatch()来代替,或者(在任何Python中至少可以追溯到2.3)使用另一种方式并re.search()与regex一起使用^(.+?)\1+$,所有这些都比其他人更受个人喜好。

Here’s a solution using regular expressions.

import re

REPEATER = re.compile(r"(.+?)\1+$")

def repeated(s):
    match = REPEATER.match(s)
    return match.group(1) if match else None

Iterating over the examples in the question:

examples = [
    '0045662100456621004566210045662100456621',
    '0072992700729927007299270072992700729927',
    '001443001443001443001443001443001443001443',
    '037037037037037037037037037037037037037037037',
    '047619047619047619047619047619047619047619',
    '002457002457002457002457002457002457002457',
    '001221001221001221001221001221001221001221',
    '001230012300123001230012300123001230012300123',
    '0013947001394700139470013947001394700139470013947',
    '001001001001001001001001001001001001001001001001001',
    '001406469760900140646976090014064697609',
    '004608294930875576036866359447',
    '00469483568075117370892018779342723',
    '004739336492890995260663507109',
    '001508295625942684766214177978883861236802413273',
    '007518796992481203',
    '0071942446043165467625899280575539568345323741',
    '0434782608695652173913',
    '0344827586206896551724137931',
    '002481389578163771712158808933',
    '002932551319648093841642228739',
    '0035587188612099644128113879',
    '003484320557491289198606271777',
    '00115074798619102416570771',
]

for e in examples:
    sub = repeated(e)
    if sub:
        print("%r: %r" % (e, sub))
    else:
        print("%r does not repeat." % e)

… produces this output:

'0045662100456621004566210045662100456621': '00456621'
'0072992700729927007299270072992700729927': '00729927'
'001443001443001443001443001443001443001443': '001443'
'037037037037037037037037037037037037037037037': '037'
'047619047619047619047619047619047619047619': '047619'
'002457002457002457002457002457002457002457': '002457'
'001221001221001221001221001221001221001221': '001221'
'001230012300123001230012300123001230012300123': '00123'
'0013947001394700139470013947001394700139470013947': '0013947'
'001001001001001001001001001001001001001001001001001': '001'
'001406469760900140646976090014064697609': '0014064697609'
'004608294930875576036866359447' does not repeat.
'00469483568075117370892018779342723' does not repeat.
'004739336492890995260663507109' does not repeat.
'001508295625942684766214177978883861236802413273' does not repeat.
'007518796992481203' does not repeat.
'0071942446043165467625899280575539568345323741' does not repeat.
'0434782608695652173913' does not repeat.
'0344827586206896551724137931' does not repeat.
'002481389578163771712158808933' does not repeat.
'002932551319648093841642228739' does not repeat.
'0035587188612099644128113879' does not repeat.
'003484320557491289198606271777' does not repeat.
'00115074798619102416570771' does not repeat.

The regular expression (.+?)\1+$ is divided into three parts:

  1. (.+?) is a matching group containing at least one (but as few as possible) of any character (because +? is non-greedy).

  2. \1+ checks for at least one repetition of the matching group in the first part.

  3. $ checks for the end of the string, to ensure that there’s no extra, non-repeating content after the repeated substrings (and using re.match() ensures that there’s no non-repeating text before the repeated substrings).

In Python 3.4 and later, you could drop the $ and use re.fullmatch() instead, or (in any Python at least as far back as 2.3) go the other way and use re.search() with the regex ^(.+?)\1+$, all of which are more down to personal taste than anything else.


回答 2

您可以观察到对于要考虑重复的字符串,必须将其长度除以重复序列的长度。鉴于此,这是一个生成长度为从1n / 2包括的长度的除数的解决方案,将原始字符串分成具有除数长度的子字符串,并测试结果集的相等性:

from math import sqrt, floor

def divquot(n):
    if n > 1:
        yield 1, n
    swapped = []
    for d in range(2, int(floor(sqrt(n))) + 1):
        q, r = divmod(n, d)
        if r == 0:
            yield d, q
            swapped.append((q, d))
    while swapped:
        yield swapped.pop()

def repeats(s):
    n = len(s)
    for d, q in divquot(n):
        sl = s[0:d]
        if sl * q == s:
            return sl
    return None

编辑:在Python 3中,/运算符已更改为默认情况下进行浮点除法。要从intPython 2中进行除法,可以改用//运算符。感谢@ TigerhawkT3引起我的注意。

//运算符在Python 2和Python 3中都执行整数除法,因此我更新了答案以支持这两个版本。现在,我们测试以查看所有子串是否相等的部分是使用all和生成器表达式的短路操作。

更新:为响应原始问题的更改,现在对代码进行了更新,以返回最小的重复子字符串(如果存在),None如果不存在,则返回最小。@godlygeek建议使用divmod减少divisors生成器上的迭代次数,并且代码也已更新为与此匹配。现在,它n以升序返回所有正数除数,不包括n其本身。

进一步更新以提高性能:经过多次测试,我得出的结论是,简单地测试字符串相等性具有Python中任何切片或迭代器解决方案中最好的性能。因此,我从@ TigerhawkT3的书中抽出了叶子,并更新了我的解决方案。现在它的速度是以前的6倍,比Tigerhawk的解决方案快得多,但比David的解决方案慢。

You can make the observation that for a string to be considered repeating, its length must be divisible by the length of its repeated sequence. Given that, here is a solution that generates divisors of the length from 1 to n / 2 inclusive, divides the original string into substrings with the length of the divisors, and tests the equality of the result set:

from math import sqrt, floor

def divquot(n):
    if n > 1:
        yield 1, n
    swapped = []
    for d in range(2, int(floor(sqrt(n))) + 1):
        q, r = divmod(n, d)
        if r == 0:
            yield d, q
            swapped.append((q, d))
    while swapped:
        yield swapped.pop()

def repeats(s):
    n = len(s)
    for d, q in divquot(n):
        sl = s[0:d]
        if sl * q == s:
            return sl
    return None

EDIT: In Python 3, the / operator has changed to do float division by default. To get the int division from Python 2, you can use the // operator instead. Thank you to @TigerhawkT3 for bringing this to my attention.

The // operator performs integer division in both Python 2 and Python 3, so I’ve updated the answer to support both versions. The part where we test to see if all the substrings are equal is now a short-circuiting operation using all and a generator expression.

UPDATE: In response to a change in the original question, the code has now been updated to return the smallest repeating substring if it exists and None if it does not. @godlygeek has suggested using divmod to reduce the number of iterations on the divisors generator, and the code has been updated to match that as well. It now returns all positive divisors of n in ascending order, exclusive of n itself.

Further update for high performance: After multiple tests, I’ve come to the conclusion that simply testing for string equality has the best performance out of any slicing or iterator solution in Python. Thus, I’ve taken a leaf out of @TigerhawkT3 ‘s book and updated my solution. It’s now over 6x as fast as before, noticably faster than Tigerhawk’s solution but slower than David’s.


回答 3

以下是针对此问题的各种答案的一些基准。有一些令人惊讶的结果,包括不同的性能,具体取决于所测试的字符串。

修改了某些功能以使其与Python 3兼容(主要是通过替换///以确保整数除法)。如果发现错误,请添加功能或添加另一个测试字符串,请在Python聊天室中 ping @ZeroPiraeus 。

总结:对于此处由OP提供的大量示例数据,最佳和最差解决方案之间存在大约50倍的差异(通过评论)。David Zhang的解决方案无疑是赢家,对于大型示例集,其解决方案比其他所有解决方案都高出约5倍。

在极大的“不匹配”情况下,几个答案非常慢。否则,根据测试,功能似乎是相等的,或者是明显的赢家。

以下是结果,包括使用matplotlib和seaborn绘制的图以显示不同的分布:


语料库1(提供的示例-小集)

mean performance:
 0.0003  david_zhang
 0.0009  zero
 0.0013  antti
 0.0013  tigerhawk_2
 0.0015  carpetpython
 0.0029  tigerhawk_1
 0.0031  davidism
 0.0035  saksham
 0.0046  shashank
 0.0052  riad
 0.0056  piotr

median performance:
 0.0003  david_zhang
 0.0008  zero
 0.0013  antti
 0.0013  tigerhawk_2
 0.0014  carpetpython
 0.0027  tigerhawk_1
 0.0031  davidism
 0.0038  saksham
 0.0044  shashank
 0.0054  riad
 0.0058  piotr

语料库1图


语料库2(提供的示例-大集合)

mean performance:
 0.0006  david_zhang
 0.0036  tigerhawk_2
 0.0036  antti
 0.0037  zero
 0.0039  carpetpython
 0.0052  shashank
 0.0056  piotr
 0.0066  davidism
 0.0120  tigerhawk_1
 0.0177  riad
 0.0283  saksham

median performance:
 0.0004  david_zhang
 0.0018  zero
 0.0022  tigerhawk_2
 0.0022  antti
 0.0024  carpetpython
 0.0043  davidism
 0.0049  shashank
 0.0055  piotr
 0.0061  tigerhawk_1
 0.0077  riad
 0.0109  saksham

语料库1图


语料库3(边缘案例)

mean performance:
 0.0123  shashank
 0.0375  david_zhang
 0.0376  piotr
 0.0394  carpetpython
 0.0479  antti
 0.0488  tigerhawk_2
 0.2269  tigerhawk_1
 0.2336  davidism
 0.7239  saksham
 3.6265  zero
 6.0111  riad

median performance:
 0.0107  tigerhawk_2
 0.0108  antti
 0.0109  carpetpython
 0.0135  david_zhang
 0.0137  tigerhawk_1
 0.0150  shashank
 0.0229  saksham
 0.0255  piotr
 0.0721  davidism
 0.1080  zero
 1.8539  riad

语料库3图


在此处获得测试和原始结果。

Here are some benchmarks for the various answers to this question. There were some surprising results, including wildly different performance depending on the string being tested.

Some functions were modified to work with Python 3 (mainly by replacing / with // to ensure integer division). If you see something wrong, want to add your function, or want to add another test string, ping @ZeroPiraeus in the Python chatroom.

In summary: there’s about a 50x difference between the best- and worst-performing solutions for the large set of example data supplied by OP here (via this comment). David Zhang’s solution is the clear winner, outperforming all others by around 5x for the large example set.

A couple of the answers are very slow in extremely large “no match” cases. Otherwise, the functions seem to be equally matched or clear winners depending on the test.

Here are the results, including plots made using matplotlib and seaborn to show the different distributions:


Corpus 1 (supplied examples – small set)

mean performance:
 0.0003  david_zhang
 0.0009  zero
 0.0013  antti
 0.0013  tigerhawk_2
 0.0015  carpetpython
 0.0029  tigerhawk_1
 0.0031  davidism
 0.0035  saksham
 0.0046  shashank
 0.0052  riad
 0.0056  piotr

median performance:
 0.0003  david_zhang
 0.0008  zero
 0.0013  antti
 0.0013  tigerhawk_2
 0.0014  carpetpython
 0.0027  tigerhawk_1
 0.0031  davidism
 0.0038  saksham
 0.0044  shashank
 0.0054  riad
 0.0058  piotr

Corpus 1 graph


Corpus 2 (supplied examples – large set)

mean performance:
 0.0006  david_zhang
 0.0036  tigerhawk_2
 0.0036  antti
 0.0037  zero
 0.0039  carpetpython
 0.0052  shashank
 0.0056  piotr
 0.0066  davidism
 0.0120  tigerhawk_1
 0.0177  riad
 0.0283  saksham

median performance:
 0.0004  david_zhang
 0.0018  zero
 0.0022  tigerhawk_2
 0.0022  antti
 0.0024  carpetpython
 0.0043  davidism
 0.0049  shashank
 0.0055  piotr
 0.0061  tigerhawk_1
 0.0077  riad
 0.0109  saksham

Corpus 1 graph


Corpus 3 (edge cases)

mean performance:
 0.0123  shashank
 0.0375  david_zhang
 0.0376  piotr
 0.0394  carpetpython
 0.0479  antti
 0.0488  tigerhawk_2
 0.2269  tigerhawk_1
 0.2336  davidism
 0.7239  saksham
 3.6265  zero
 6.0111  riad

median performance:
 0.0107  tigerhawk_2
 0.0108  antti
 0.0109  carpetpython
 0.0135  david_zhang
 0.0137  tigerhawk_1
 0.0150  shashank
 0.0229  saksham
 0.0255  piotr
 0.0721  davidism
 0.1080  zero
 1.8539  riad

Corpus 3 graph


The tests and raw results are available here.


回答 4

非正则表达式解决方案:

def repeat(string):
    for i in range(1, len(string)//2+1):
        if not len(string)%len(string[0:i]) and string[0:i]*(len(string)//len(string[0:i])) == string:
            return string[0:i]

更快的非正则表达式解决方案,这要感谢@ThatWeirdo(请参见评论):

def repeat(string):
    l = len(string)
    for i in range(1, len(string)//2+1):
        if l%i: continue
        s = string[0:i]
        if s*(l//i) == string:
            return s

上面的解决方案很少比原始解决方案慢几个百分点,但通常会快很多-有时快很多。对于较长的字符串,它仍然不比davidism的快,而对于短字符串,zero的正则表达式解决方案更胜一筹。它以大约1000-1500个字符的字符串显示出来,速度最快(根据github上davidism的测试-请参见他的回答)。无论如何,在我测试的所有情况下,它都是第二快的(或更好的)。谢谢,ThatWeirdo。

测试:

print(repeat('009009009'))
print(repeat('254725472547'))
print(repeat('abcdeabcdeabcdeabcde'))
print(repeat('abcdefg'))
print(repeat('09099099909999'))
print(repeat('02589675192'))

结果:

009
2547
abcde
None
None
None

Non-regex solution:

def repeat(string):
    for i in range(1, len(string)//2+1):
        if not len(string)%len(string[0:i]) and string[0:i]*(len(string)//len(string[0:i])) == string:
            return string[0:i]

Faster non-regex solution, thanks to @ThatWeirdo (see comments):

def repeat(string):
    l = len(string)
    for i in range(1, len(string)//2+1):
        if l%i: continue
        s = string[0:i]
        if s*(l//i) == string:
            return s

The above solution is very rarely slower than the original by a few percent, but it’s usually a good bit faster – sometimes a whole lot faster. It’s still not faster than davidism’s for longer strings, and zero’s regex solution is superior for short strings. It comes out to the fastest (according to davidism’s test on github – see his answer) with strings of about 1000-1500 characters. Regardless, it’s reliably second-fastest (or better) in all cases I tested. Thanks, ThatWeirdo.

Test:

print(repeat('009009009'))
print(repeat('254725472547'))
print(repeat('abcdeabcdeabcdeabcde'))
print(repeat('abcdefg'))
print(repeat('09099099909999'))
print(repeat('02589675192'))

Results:

009
2547
abcde
None
None
None

回答 5

首先,将字符串减半,只要它是“ 2部分”重复项即可。如果重复数为偶数,则会减少搜索空间。然后,继续寻找最小的重复字符串,检查是否通过将越来越大的子字符串拆分成完整的字符串而只得到空值。仅length // 2需要测试最多的子字符串,因为任何重复的内容都不会重复。

def shortest_repeat(orig_value):
    if not orig_value:
        return None

    value = orig_value

    while True:
        len_half = len(value) // 2
        first_half = value[:len_half]

        if first_half != value[len_half:]:
            break

        value = first_half

    len_value = len(value)
    split = value.split

    for i in (i for i in range(1, len_value // 2) if len_value % i == 0):
        if not any(split(value[:i])):
            return value[:i]

    return value if value != orig_value else None

这将返回最短匹配项,如果没有匹配项,则返回None。

First, halve the string as long as it’s a “2 part” duplicate. This reduces the search space if there are an even number of repeats. Then, working forwards to find the smallest repeating string, check if splitting the full string by increasingly larger sub-string results in only empty values. Only sub-strings up to length // 2 need to be tested since anything over that would have no repeats.

def shortest_repeat(orig_value):
    if not orig_value:
        return None

    value = orig_value

    while True:
        len_half = len(value) // 2
        first_half = value[:len_half]

        if first_half != value[len_half:]:
            break

        value = first_half

    len_value = len(value)
    split = value.split

    for i in (i for i in range(1, len_value // 2) if len_value % i == 0):
        if not any(split(value[:i])):
            return value[:i]

    return value if value != orig_value else None

This returns the shortest match or None if there is no match.


回答 6

O(n)在最坏的情况下,也可以使用前缀功能解决该问题。

请注意,这可能是在一般的情况下慢(UPD:是慢得多),比取决于除数的一些其他的解决方案n,我认为不好的情况下,他们一会,但通常会发现失败越早aaa....aab,那里n - 1 = 2 * 3 * 5 * 7 ... *p_n - 1 a

首先需要计算前缀函数

def prefix_function(s):
    n = len(s)
    pi = [0] * n
    for i in xrange(1, n):
        j = pi[i - 1]
        while(j > 0 and s[i] != s[j]):
            j = pi[j - 1]
        if (s[i] == s[j]):
            j += 1
        pi[i] = j;
    return pi

那么要么没有答案,要么最短的时间是

k = len(s) - prefix_function(s[-1])

并且您只需要检查是否k != n and n % k == 0(如果k != n and n % k == 0答案为s[:k],则没有答案

您可以在此处检查证明(俄语,但在线翻译可能会解决问题)

def riad(s):
    n = len(s)
    pi = [0] * n
    for i in xrange(1, n):
        j = pi[i - 1]
        while(j > 0 and s[i] != s[j]):
            j = pi[j - 1]
        if (s[i] == s[j]):
            j += 1
        pi[i] = j;
    k = n - pi[-1]
    return s[:k] if (n != k and n % k == 0) else None

The problem may also be solved in O(n) in worst case with prefix function.

Note, it may be slower in general case(UPD: and is much slower) than other solutions which depend on number of divisors of n, but usually find fails sooner, I think one of bad cases for them will be aaa....aab, where there are n - 1 = 2 * 3 * 5 * 7 ... *p_n - 1 a‘s

First of all you need to calculate prefix function

def prefix_function(s):
    n = len(s)
    pi = [0] * n
    for i in xrange(1, n):
        j = pi[i - 1]
        while(j > 0 and s[i] != s[j]):
            j = pi[j - 1]
        if (s[i] == s[j]):
            j += 1
        pi[i] = j;
    return pi

then either there’s no answer or the shortest period is

k = len(s) - prefix_function(s[-1])

and you just have to check if k != n and n % k == 0 (if k != n and n % k == 0 then answer is s[:k], else there’s no answer

You may check the proof here (in Russian, but online translator will probably do the trick)

def riad(s):
    n = len(s)
    pi = [0] * n
    for i in xrange(1, n):
        j = pi[i - 1]
        while(j > 0 and s[i] != s[j]):
            j = pi[j - 1]
        if (s[i] == s[j]):
            j += 1
        pi[i] = j;
    k = n - pi[-1]
    return s[:k] if (n != k and n % k == 0) else None

回答 7

这个版本只尝试那些影响字符串长度的候选序列长度。并使用*运算符从候选序列中构建一个全长字符串:

def get_shortest_repeat(string):
    length = len(string)
    for i in range(1, length // 2 + 1):
        if length % i:  # skip non-factors early
            continue

        candidate = string[:i]
        if string == candidate * (length // i):
            return candidate

    return None

感谢TigerhawkT3注意到,length // 2如果不+ 1这样做,将无法匹配abab案件。

This version tries only those candidate sequence lengths that are factors of the string length; and uses the * operator to build a full-length string from the candidate sequence:

def get_shortest_repeat(string):
    length = len(string)
    for i in range(1, length // 2 + 1):
        if length % i:  # skip non-factors early
            continue

        candidate = string[:i]
        if string == candidate * (length // i):
            return candidate

    return None

Thanks to TigerhawkT3 for noticing that length // 2 without + 1 would fail to match the abab case.


回答 8

这是没有正则表达式的直接解决方案。

对于s从零索引开始的,长度为1到的len(s)子字符串,请检查该子字符串substr是否为重复模式。可以通过将substr其自身的ratio时间串联在一起来执行此检查,以使由此形成的字符串的长度等于的长度s。因此ratio=len(s)/len(substr)

当找到第一个这样的子字符串时返回。如果存在,这将提供最小的子字符串。

def check_repeat(s):
    for i in range(1, len(s)):
        substr = s[:i]
        ratio = len(s)/len(substr)
        if substr * ratio == s:
            print 'Repeating on "%s"' % substr
            return
    print 'Non repeating'

>>> check_repeat('254725472547')
Repeating on "2547"
>>> check_repeat('abcdeabcdeabcdeabcde')
Repeating on "abcde"

Here’s a straight forward solution, without regexes.

For substrings of s starting from zeroth index, of lengths 1 through len(s), check if that substring, substr is the repeated pattern. This check can be performed by concatenating substr with itself ratio times, such that the length of the string thus formed is equal to the length of s. Hence ratio=len(s)/len(substr).

Return when first such substring is found. This would provide the smallest possible substring, if one exists.

def check_repeat(s):
    for i in range(1, len(s)):
        substr = s[:i]
        ratio = len(s)/len(substr)
        if substr * ratio == s:
            print 'Repeating on "%s"' % substr
            return
    print 'Non repeating'

>>> check_repeat('254725472547')
Repeating on "2547"
>>> check_repeat('abcdeabcdeabcdeabcde')
Repeating on "abcde"

回答 9

我从八个以上的解决方案开始。一些基于正则表达式(match,findall,split),一些基于字符串切片和测试,而另一些基于字符串方法(find,count,split)。每种代码在代码清晰度,代码大小,速度和内存消耗方面都有好处。当我注意到执行速度被列为重要事项时,我将在此处发布答案,因此我进行了更多测试和改进以得出结论:

def repeating(s):
    size = len(s)
    incr = size % 2 + 1
    for n in xrange(1, size//2+1, incr):
        if size % n == 0:
            if s[:n] * (size//n) == s:
                return s[:n]

该答案似乎与此处的其他一些答案相似,但是它具有一些其他人未使用的速度优化:

  • xrange 在这个应用程序中速度更快,
  • 如果输入字符串是奇数长度,请不要检查任何偶数长度的子字符串,
  • 通过s[:n]直接使用,我们避免在每个循环中创建变量。

我很想看看它在常见硬件的标准测试中如何执行。我相信,在大多数测试中,这将远远超出David Zhang的出色算法,但否则应该很快。

我发现这个问题非常违反直觉。我认为很快的解决方案很慢。看起来很慢的解决方案很快!看起来,使用乘法运算符和字符串比较对Python的字符串创建进行了高度优化。

I started with more than eight solutions to this problem. Some were bases on regex (match, findall, split), some of string slicing and testing, and some with string methods (find, count, split). Each had benefits in code clarity, code size, speed and memory consumption. I was going to post my answer here when I noticed that execution speed was ranked as important, so I did more testing and improvement to arrive at this:

def repeating(s):
    size = len(s)
    incr = size % 2 + 1
    for n in xrange(1, size//2+1, incr):
        if size % n == 0:
            if s[:n] * (size//n) == s:
                return s[:n]

This answer seems similar to a few other answers here, but it has a few speed optimisations others have not used:

  • xrange is a little faster in this application,
  • if an input string is an odd length, do not check any even length substrings,
  • by using s[:n] directly, we avoid creating a variable in each loop.

I would be interested to see how this performs in the standard tests with common hardware. I believe it will be well short of David Zhang’s excellent algorithm in most tests, but should be quite fast otherwise.

I found this problem to be very counter-intuitive. The solutions I thought would be fast were slow. The solutions that looked slow were fast! It seems that Python’s string creation with the multiply operator and string comparisons are highly optimised.


回答 10

此功能运行非常快(经过测试,在超过10万个字符的字符串上,此功能比最快的解决方案快3倍以上,并且重复模式越长,差异越大)。它试图最小化获得答案所需的比较次数:

def repeats(string):
    n = len(string)
    tried = set([])
    best = None
    nums = [i for i in  xrange(2, int(n**0.5) + 1) if n % i == 0]
    nums = [n/i for i in nums if n/i!=i] + list(reversed(nums)) + [1]
    for s in nums:
        if all(t%s for t in tried):
            print 'Trying repeating string of length:', s
            if string[:s]*(n/s)==string:
                best = s
            else:
                tried.add(s)
    if best:
        return string[:best]

请注意,例如对于长度为8的字符串,它仅检查大小为4的片段,并且不必进一步测试,因为长度为1或2的模式将导致重复长度为4的模式:

>>> repeats('12345678')
Trying repeating string of length: 4
None

# for this one we need only 2 checks 
>>> repeats('1234567812345678')
Trying repeating string of length: 8
Trying repeating string of length: 4
'12345678'

This function runs very quickly (tested and it’s over 3 times faster than fastest solution here on strings with over 100k characters and the difference gets bigger the longer the repeating pattern is). It tries to minimise the number of comparisons needed to get the answer:

def repeats(string):
    n = len(string)
    tried = set([])
    best = None
    nums = [i for i in  xrange(2, int(n**0.5) + 1) if n % i == 0]
    nums = [n/i for i in nums if n/i!=i] + list(reversed(nums)) + [1]
    for s in nums:
        if all(t%s for t in tried):
            print 'Trying repeating string of length:', s
            if string[:s]*(n/s)==string:
                best = s
            else:
                tried.add(s)
    if best:
        return string[:best]

Note that for example for string of length 8 it checks only fragment of size 4 and it does not have to test further because pattern of length 1 or 2 would result in repeating pattern of length 4:

>>> repeats('12345678')
Trying repeating string of length: 4
None

# for this one we need only 2 checks 
>>> repeats('1234567812345678')
Trying repeating string of length: 8
Trying repeating string of length: 4
'12345678'

回答 11

在David Zhang的回答中,如果我们有某种循环缓冲区,这将不起作用:principal_period('6210045662100456621004566210045662100456621')由于开始的原因621,我希望将其吐出:00456621

扩展他的解决方案,我们可以使用以下方法:

def principal_period(s):
    for j in range(int(len(s)/2)):
        idx = (s[j:]+s[j:]).find(s[j:], 1, -1)
        if idx != -1:
            # Make sure that the first substring is part of pattern
            if s[:j] == s[j:][:idx][-j:]:
                break

    return None if idx == -1 else s[j:][:idx]

principal_period('6210045662100456621004566210045662100456621')
>>> '00456621'

In David Zhang’s answer if we have some sort of circular buffer this will not work: principal_period('6210045662100456621004566210045662100456621') due to the starting 621, where I would have liked it to spit out: 00456621.

Extending his solution we can use the following:

def principal_period(s):
    for j in range(int(len(s)/2)):
        idx = (s[j:]+s[j:]).find(s[j:], 1, -1)
        if idx != -1:
            # Make sure that the first substring is part of pattern
            if s[:j] == s[j:][:idx][-j:]:
                break

    return None if idx == -1 else s[j:][:idx]

principal_period('6210045662100456621004566210045662100456621')
>>> '00456621'

回答 12

这是python中的代码,用于检查用户给定的主字符串中子字符串的重复

print "Enter a string...."
#mainstring = String given by user
mainstring=raw_input(">")
if(mainstring==''):
    print "Invalid string"
    exit()
#charlist = Character list of mainstring
charlist=list(mainstring)
strarr=''
print "Length of your string :",len(mainstring)
for i in range(0,len(mainstring)):
    strarr=strarr+charlist[i]
    splitlist=mainstring.split(strarr)
    count = 0
    for j in splitlist:
        if j =='':
            count+=1
    if count == len(splitlist):
        break
if count == len(splitlist):
    if count == 2:
        print "No repeating Sub-String found in string %r"%(mainstring)

    else:
        print "Sub-String %r repeats in string %r"%(strarr,mainstring)
else :
    print "No repeating Sub-String found in string %r"%(mainstring)

输入

0045662100456621004566210045662100456621

输出

琴弦长度:40

子字符串’00456621’在字符串’0045662100456621004566210045662100456621’中重复

输入

004608294930875576​​036866359447

输出

字符串长度:30

在字符串’004608294930875576​​576036866359447’中找不到重复的子字符串

Here is the code in python that checks for repetition of sub string in the main string given by the user.

print "Enter a string...."
#mainstring = String given by user
mainstring=raw_input(">")
if(mainstring==''):
    print "Invalid string"
    exit()
#charlist = Character list of mainstring
charlist=list(mainstring)
strarr=''
print "Length of your string :",len(mainstring)
for i in range(0,len(mainstring)):
    strarr=strarr+charlist[i]
    splitlist=mainstring.split(strarr)
    count = 0
    for j in splitlist:
        if j =='':
            count+=1
    if count == len(splitlist):
        break
if count == len(splitlist):
    if count == 2:
        print "No repeating Sub-String found in string %r"%(mainstring)

    else:
        print "Sub-String %r repeats in string %r"%(strarr,mainstring)
else :
    print "No repeating Sub-String found in string %r"%(mainstring)

Input:

0045662100456621004566210045662100456621

Output :

Length of your string : 40

Sub-String ‘00456621’ repeats in string ‘0045662100456621004566210045662100456621’

Input :

004608294930875576036866359447

Output:

Length of your string : 30

No repeating Sub-String found in string ‘004608294930875576036866359447’


python 3中execfile的替代方法是什么?

问题:python 3中execfile的替代方法是什么?

看来他们在Python 3中取消了通过删除以下命令快速加载脚本的所有简便方法 execfile()

我是否有明显的替代品?

It seems they canceled in Python 3 all the easy way to quickly load a script by removing execfile()

Is there an obvious alternative I’m missing?


回答 0

根据文档,而不是

execfile("./filename") 

采用

exec(open("./filename").read())

看到:

According to the documentation, instead of

execfile("./filename") 

Use

exec(open("./filename").read())

See:


回答 1

您只应该自己读取文件并执行代码。2to3电流替代

execfile("somefile.py", global_vars, local_vars)

with open("somefile.py") as f:
    code = compile(f.read(), "somefile.py", 'exec')
    exec(code, global_vars, local_vars)

(并非严格要求进行编译调用,但是它将文件名与代码对象相关联,从而使调试更加容易。)

看到:

You are just supposed to read the file and exec the code yourself. 2to3 current replaces

execfile("somefile.py", global_vars, local_vars)

as

with open("somefile.py") as f:
    code = compile(f.read(), "somefile.py", 'exec')
    exec(code, global_vars, local_vars)

(The compile call isn’t strictly needed, but it associates the filename with the code object making debugging a little easier.)

See:


回答 2

尽管exec(open("filename").read())通常被用作的替代方案execfile("filename"),但它错过了execfile支持的重要细节。

Python3.x的以下功能与直接执行文件具有相同的行为。匹配运行python /path/to/somefile.py

def execfile(filepath, globals=None, locals=None):
    if globals is None:
        globals = {}
    globals.update({
        "__file__": filepath,
        "__name__": "__main__",
    })
    with open(filepath, 'rb') as file:
        exec(compile(file.read(), filepath, 'exec'), globals, locals)

# execute the file
execfile("/path/to/somefile.py")

笔记:

  • 使用二进制读取以避免编码问题
  • 保证关闭文件(Python3.x警告)
  • 定义__main__,某些脚本依赖于此来检查它们是否作为模块加载,例如。if __name__ == "__main__"
  • 设置__file__异常消息更好,某些脚本用于__file__获取其他文件相对于它们的路径。
  • 接受可选的globals和locals参数,就地进行修改execfile-您可以在运行后通过读回变量来访问定义的任何变量。

  • 与Python2不同,execfile这默认情况下不会修改当前命名空间。为此,您必须显式传入globals()locals()

While exec(open("filename").read()) is often given as an alternative to execfile("filename"), it misses important details that execfile supported.

The following function for Python3.x is as close as I could get to having the same behavior as executing a file directly. That matches running python /path/to/somefile.py.

def execfile(filepath, globals=None, locals=None):
    if globals is None:
        globals = {}
    globals.update({
        "__file__": filepath,
        "__name__": "__main__",
    })
    with open(filepath, 'rb') as file:
        exec(compile(file.read(), filepath, 'exec'), globals, locals)

# execute the file
execfile("/path/to/somefile.py")

Notes:

  • Uses binary reading to avoid encoding issues
  • Guaranteed to close the file (Python3.x warns about this)
  • Defines __main__, some scripts depend on this to check if they are loading as a module or not for eg. if __name__ == "__main__"
  • Setting __file__ is nicer for exception messages and some scripts use __file__ to get the paths of other files relative to them.
  • Takes optional globals & locals arguments, modifying them in-place as execfile does – so you can access any variables defined by reading back the variables after running.

  • Unlike Python2’s execfile this does not modify the current namespace by default. For that you have to explicitly pass in globals() & locals().


回答 3

正如最近在python-dev邮件列表上建议那样runpy模块可能是一个可行的替代方案。引用该消息:

https://docs.python.org/3/library/runpy.html#runpy.run_path

import runpy
file_globals = runpy.run_path("file.py")

与以下内容有细微的差异execfile

  • run_path总是创建一个新的命名空间。它作为模块执行代码,因此全局变量和局部变量之间没有区别(这就是为什么只有init_globals参数的原因)。返回全局变量。

    execfile在当前命名空间或给定命名空间中执行。的语义localsglobals,如果给定的,类似于当地人和全局类定义中。

  • run_path 不仅可以执行文件,还可以执行蛋和目录(有关详细信息,请参阅其文档)。

As suggested on the python-dev mailinglist recently, the runpy module might be a viable alternative. Quoting from that message:

https://docs.python.org/3/library/runpy.html#runpy.run_path

import runpy
file_globals = runpy.run_path("file.py")

There are subtle differences to execfile:

  • run_path always creates a new namespace. It executes the code as a module, so there is no difference between globals and locals (which is why there is only a init_globals argument). The globals are returned.

    execfile executed in the current namespace or the given namespace. The semantics of locals and globals, if given, were similar to locals and globals inside a class definition.

  • run_path can not only execute files, but also eggs and directories (refer to its documentation for details).


回答 4

这是更好的方法,因为它从调用者那里获取了全局变量和本地变量:

import sys
def execfile(filename, globals=None, locals=None):
    if globals is None:
        globals = sys._getframe(1).f_globals
    if locals is None:
        locals = sys._getframe(1).f_locals
    with open(filename, "r") as fh:
        exec(fh.read()+"\n", globals, locals)

This one is better, since it takes the globals and locals from the caller:

import sys
def execfile(filename, globals=None, locals=None):
    if globals is None:
        globals = sys._getframe(1).f_globals
    if locals is None:
        locals = sys._getframe(1).f_locals
    with open(filename, "r") as fh:
        exec(fh.read()+"\n", globals, locals)

回答 5

您可以编写自己的函数:

def xfile(afile, globalz=None, localz=None):
    with open(afile, "r") as fh:
        exec(fh.read(), globalz, localz)

如果您真的需要…

You could write your own function:

def xfile(afile, globalz=None, localz=None):
    with open(afile, "r") as fh:
        exec(fh.read(), globalz, localz)

If you really needed to…


回答 6

如果要加载的脚本与运行的脚本位于同一目录中,则也许“ import”可以完成此工作?

如果您需要动态导入代码,则值得研究内置函数__ import__和模块imp

>>> import sys
>>> sys.path = ['/path/to/script'] + sys.path
>>> __import__('test')
<module 'test' from '/path/to/script/test.pyc'>
>>> __import__('test').run()
'Hello world!'

test.py:

def run():
        return "Hello world!"

如果您使用的是Python 3.1或更高版本,则还应该看看importlib

If the script you want to load is in the same directory than the one you run, maybe “import” will do the job ?

If you need to dynamically import code the built-in function __ import__ and the module imp are worth looking at.

>>> import sys
>>> sys.path = ['/path/to/script'] + sys.path
>>> __import__('test')
<module 'test' from '/path/to/script/test.pyc'>
>>> __import__('test').run()
'Hello world!'

test.py:

def run():
        return "Hello world!"

If you’re using Python 3.1 or later, you should also take a look at importlib.


回答 7

这就是我所拥有的(file两个示例中的源代码都已将其分配给文件的路径):

execfile(file)

这是我替换为的内容:

exec(compile(open(file).read(), file, 'exec'))

我最喜欢的部分:第二个版本在Python 2和3中都可以正常工作,这意味着不必添加依赖于版本的逻辑。

Here’s what I had (file is already assigned to the path to the file with the source code in both examples):

execfile(file)

Here’s what I replaced it with:

exec(compile(open(file).read(), file, 'exec'))

My favorite part: the second version works just fine in both Python 2 and 3, meaning it’s not necessary to add in version dependent logic.


回答 8

请注意,如果您使用的不是非ascii或utf-8的PEP-263编码声明,上述模式将失败。您需要找到数据的编码,并在将其交给exec()之前对其进行正确的编码。

class python3Execfile(object):
    def _get_file_encoding(self, filename):
        with open(filename, 'rb') as fp:
            try:
                return tokenize.detect_encoding(fp.readline)[0]
            except SyntaxError:
                return "utf-8"

    def my_execfile(filename):
        globals['__file__'] = filename
        with open(filename, 'r', encoding=self._get_file_encoding(filename)) as fp:
            contents = fp.read()
        if not contents.endswith("\n"):
            # http://bugs.python.org/issue10204
            contents += "\n"
        exec(contents, globals, globals)

Note that the above pattern will fail if you’re using PEP-263 encoding declarations that aren’t ascii or utf-8. You need to find the encoding of the data, and encode it correctly before handing it to exec().

class python3Execfile(object):
    def _get_file_encoding(self, filename):
        with open(filename, 'rb') as fp:
            try:
                return tokenize.detect_encoding(fp.readline)[0]
            except SyntaxError:
                return "utf-8"

    def my_execfile(filename):
        globals['__file__'] = filename
        with open(filename, 'r', encoding=self._get_file_encoding(filename)) as fp:
            contents = fp.read()
        if not contents.endswith("\n"):
            # http://bugs.python.org/issue10204
            contents += "\n"
        exec(contents, globals, globals)

回答 9

另外,虽然不是纯粹的Python解决方案,但如果您使用的是IPython(无论如何也应该这样做),则可以执行以下操作:

%run /path/to/filename.py

这同样容易。

Also, while not a pure Python solution, if you’re using IPython (as you probably should anyway), you can do:

%run /path/to/filename.py

Which is equally easy.


回答 10

我只是这里的新手,所以如果发现了这个,也许纯粹是运气:

尝试使用命令从解释器提示符>>>运行脚本后

    execfile('filename.py')

为此,我得到了“ NameError:名称’execfile’未定义”,我尝试了一个非常基本的

    import filename

它运作良好:-)

我希望这会有所帮助,并感谢大家提供的出色提示,示例以及所有那些对新手有很大启发的精妙注释的代码!

我使用Ubuntu 16.014 LTS x64。 Linux上的Python 3.5.2(默认值,2016年11月17日,17:05:23)[GCC 5.4.0 20160609]

I’m just a newbie here so maybe it’s pure luck if I found this :

After trying to run a script from the interpreter prompt >>> with the command

    execfile('filename.py')

for which I got a “NameError: name ‘execfile’ is not defined” I tried a very basic

    import filename

it worked well :-)

I hope this can be helpful and thank you all for the great hints, examples and all those masterly commented pieces of code that are a great inspiration for newcomers !

I use Ubuntu 16.014 LTS x64. Python 3.5.2 (default, Nov 17 2016, 17:05:23) [GCC 5.4.0 20160609] on linux


回答 11

对我来说,最干净的方法是importlib按路径使用和导入文件作为模块,如下所示:

from importlib import util

def load_file(name, path):
    spec = util.spec_from_file_location(name, path)
    module = util.module_from_spec(spec)
    spec.loader.exec_module(module)
    return module

使用范例

让我们有一个文件foo.py

print('i got imported')
def hello():
    print('hello from foo')

现在就像导入普通模块一样使用它:

>>> foo = load_file('foo', './foo.py')
i got imported
>>> foo.hello()
hello from foo

与直接方法相比,我更喜欢这种技术,exec(open(...))因为它不会使您的命名空间混乱或不必要地造成混乱$PATH

To me, the cleanest approach is to use importlib and import the file as a module by path, like so:

from importlib import util

def load_file(name, path):
    spec = util.spec_from_file_location(name, path)
    module = util.module_from_spec(spec)
    spec.loader.exec_module(module)
    return module

Usage example

Let’s have a file foo.py:

print('i got imported')
def hello():
    print('hello from foo')

Now just import and use it like a normal module:

>>> foo = load_file('foo', './foo.py')
i got imported
>>> foo.hello()
hello from foo

I prefer this technique over direct approaches like exec(open(...)) because it does not clutter your namespaces or unnecessarily messes with $PATH.


如何为Python日志输出着色?

问题:如何为Python日志输出着色?

前一段时间,我看到一个带有彩色输出的Mono应用程序,大概是因为它的日志系统(因为所有消息都是标准化的)。

现在,Python具有该logging模块,您可以使用该模块指定许多选项来定制输出。因此,我在想使用Python可以实现类似的效果,但是我找不到在任何地方执行此操作的方法。

有什么方法可以使Python logging模块以彩色输出吗?

我想要的(例如)红色错误,蓝色或黄色调试消息,等等。

当然,这可能需要兼容的终端(大多数现代终端都需要)。但是logging如果不支持颜色,我可能会退回到原始输出。

有什么想法可以使用日志记录模块获取彩色输出吗?

Some time ago, I saw a Mono application with colored output, presumably because of its log system (because all the messages were standardized).

Now, Python has the logging module, which lets you specify a lot of options to customize output. So, I’m imagining something similar would be possible with Python, but I can’t find out how to do this anywhere.

Is there any way to make the Python logging module output in color?

What I want (for instance) errors in red, debug messages in blue or yellow, and so on.

Of course this would probably require a compatible terminal (most modern terminals are); but I could fallback to the original logging output if color isn’t supported.

Any ideas how I can get colored output with the logging module?


回答 0

我已经知道颜色转义,我不久前在bash提示中使用了它们。不管怎么说,还是要谢谢你。
我想要的是将其与日志记录模块集成在一起,但经过几次尝试和错误,我最终还是将其集成了。
这是我最终得到的结果:

BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE = range(8)

#The background is set with 40 plus the number of the color, and the foreground with 30

#These are the sequences need to get colored ouput
RESET_SEQ = "\033[0m"
COLOR_SEQ = "\033[1;%dm"
BOLD_SEQ = "\033[1m"

def formatter_message(message, use_color = True):
    if use_color:
        message = message.replace("$RESET", RESET_SEQ).replace("$BOLD", BOLD_SEQ)
    else:
        message = message.replace("$RESET", "").replace("$BOLD", "")
    return message

COLORS = {
    'WARNING': YELLOW,
    'INFO': WHITE,
    'DEBUG': BLUE,
    'CRITICAL': YELLOW,
    'ERROR': RED
}

class ColoredFormatter(logging.Formatter):
    def __init__(self, msg, use_color = True):
        logging.Formatter.__init__(self, msg)
        self.use_color = use_color

    def format(self, record):
        levelname = record.levelname
        if self.use_color and levelname in COLORS:
            levelname_color = COLOR_SEQ % (30 + COLORS[levelname]) + levelname + RESET_SEQ
            record.levelname = levelname_color
        return logging.Formatter.format(self, record)

并使用它,创建自己的Logger:

# Custom logger class with multiple destinations
class ColoredLogger(logging.Logger):
    FORMAT = "[$BOLD%(name)-20s$RESET][%(levelname)-18s]  %(message)s ($BOLD%(filename)s$RESET:%(lineno)d)"
    COLOR_FORMAT = formatter_message(FORMAT, True)
    def __init__(self, name):
        logging.Logger.__init__(self, name, logging.DEBUG)                

        color_formatter = ColoredFormatter(self.COLOR_FORMAT)

        console = logging.StreamHandler()
        console.setFormatter(color_formatter)

        self.addHandler(console)
        return


logging.setLoggerClass(ColoredLogger)

以防万一其他人需要它。

如果您使用多个记录器或处理程序,请小心:ColoredFormatter正在更改记录对象,该记录对象将进一步传递给其他处理程序或传播到其他记录器。如果配置了文件记录器等,则可能不想在日志文件中使用颜色。为避免这种情况,最好是在操作levelname属性之前简单地创建一个recordwith 的副本copy.copy(),或者在返回格式化的字符串之前,将levelname重置为先前的值(在注释中归因于Michael)。

I already knew about the color escapes, I used them in my bash prompt a while ago. Thanks anyway.
What I wanted was to integrate it with the logging module, which I eventually did after a couple of tries and errors.
Here is what I end up with:

BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE = range(8)

#The background is set with 40 plus the number of the color, and the foreground with 30

#These are the sequences need to get colored ouput
RESET_SEQ = "\033[0m"
COLOR_SEQ = "\033[1;%dm"
BOLD_SEQ = "\033[1m"

def formatter_message(message, use_color = True):
    if use_color:
        message = message.replace("$RESET", RESET_SEQ).replace("$BOLD", BOLD_SEQ)
    else:
        message = message.replace("$RESET", "").replace("$BOLD", "")
    return message

COLORS = {
    'WARNING': YELLOW,
    'INFO': WHITE,
    'DEBUG': BLUE,
    'CRITICAL': YELLOW,
    'ERROR': RED
}

class ColoredFormatter(logging.Formatter):
    def __init__(self, msg, use_color = True):
        logging.Formatter.__init__(self, msg)
        self.use_color = use_color

    def format(self, record):
        levelname = record.levelname
        if self.use_color and levelname in COLORS:
            levelname_color = COLOR_SEQ % (30 + COLORS[levelname]) + levelname + RESET_SEQ
            record.levelname = levelname_color
        return logging.Formatter.format(self, record)

And to use it, create your own Logger:

# Custom logger class with multiple destinations
class ColoredLogger(logging.Logger):
    FORMAT = "[$BOLD%(name)-20s$RESET][%(levelname)-18s]  %(message)s ($BOLD%(filename)s$RESET:%(lineno)d)"
    COLOR_FORMAT = formatter_message(FORMAT, True)
    def __init__(self, name):
        logging.Logger.__init__(self, name, logging.DEBUG)                

        color_formatter = ColoredFormatter(self.COLOR_FORMAT)

        console = logging.StreamHandler()
        console.setFormatter(color_formatter)

        self.addHandler(console)
        return


logging.setLoggerClass(ColoredLogger)

Just in case anyone else needs it.

Be careful if you’re using more than one logger or handler: ColoredFormatter is changing the record object, which is passed further to other handlers or propagated to other loggers. If you have configured file loggers etc. you probably don’t want to have the colors in the log files. To avoid that, it’s probably best to simply create a copy of record with copy.copy() before manipulating the levelname attribute, or to reset the levelname to the previous value, before returning the formatted string (credit to Michael in the comments).


回答 1

多年前,我写了一个彩色的流处理程序供自己使用。然后我浏览了此页面,发现了人们正在复制/粘贴的代码片段集合:-(。我的流处理程序当前仅在UNIX(Linux,Mac OS X)上可用,但优点是可以在PyPI(和GitHub)上使用),而且使用起来非常简单,还具有Vim语法模式:-)。将来我可能会将其扩展到Windows上。

要安装软件包:

$ pip install coloredlogs

确认其有效:

$ coloredlogs --demo

要开始使用自己的代码:

$ python
> import coloredlogs, logging
> coloredlogs.install()
> logging.info("It works!")
2014-07-30 21:21:26 peter-macbook root[7471] INFO It works!

上例中显示的默认日志格式包含日期,时间,主机名,记录器的名称,PID,日志级别和日志消息。实际上是这样的:

彩色日志输出的屏幕截图

注意:使用带有MinTTY的Git Bash时

Windows上的Git Bash有一些已记录的怪癖: Winpty和Git Bash

对于ANSI转义码以及ncurses样式字符重写和动画,您需要在命令前面加上winpty

$ winpty coloredlogs --demo
$ winpty python your_colored_logs_script.py

Years ago I wrote a colored stream handler for my own use. Then I came across this page and found a collection of code snippets that people are copy/pasting :-(. My stream handler currently only works on UNIX (Linux, Mac OS X) but the advantage is that it’s available on PyPI (and GitHub) and it’s dead simple to use. It also has a Vim syntax mode :-). In the future I might extend it to work on Windows.

To install the package:

$ pip install coloredlogs

To confirm that it works:

$ coloredlogs --demo

To get started with your own code:

$ python
> import coloredlogs, logging
> coloredlogs.install()
> logging.info("It works!")
2014-07-30 21:21:26 peter-macbook root[7471] INFO It works!

The default log format shown in the above example contains the date, time, hostname, the name of the logger, the PID, the log level and the log message. This is what it looks like in practice:

Screenshot of coloredlogs output

NOTE: When using Git Bash w/ MinTTY

Git Bash on windows has some documented quirks: Winpty and Git Bash

Which for ANSI escape codes and for ncurses style character rewriting and animations, you need to prefix commands with winpty.

$ winpty coloredlogs --demo
$ winpty python your_colored_logs_script.py

回答 2

这是一个可以在任何平台上运行的解决方案。如果不只是告诉我,我会进行更新。

工作原理:在支持ANSI转义的平台上正在使用它们(非Windows),而在Windows上,它确实使用API​​调用来更改控制台颜色。

该脚本确实从标准库中破解了logging.StreamHandler.emit方法,并为其添加了一个包装器。

TestColorer.py

# Usage: add Colorer.py near you script and import it.
import logging
import Colorer

logging.warn("a warning")
logging.error("some error")
logging.info("some info")

着色器

#!/usr/bin/env python
# encoding: utf-8
import logging
# now we patch Python code to add color support to logging.StreamHandler
def add_coloring_to_emit_windows(fn):
        # add methods we need to the class
    def _out_handle(self):
        import ctypes
        return ctypes.windll.kernel32.GetStdHandle(self.STD_OUTPUT_HANDLE)
    out_handle = property(_out_handle)

    def _set_color(self, code):
        import ctypes
        # Constants from the Windows API
        self.STD_OUTPUT_HANDLE = -11
        hdl = ctypes.windll.kernel32.GetStdHandle(self.STD_OUTPUT_HANDLE)
        ctypes.windll.kernel32.SetConsoleTextAttribute(hdl, code)

    setattr(logging.StreamHandler, '_set_color', _set_color)

    def new(*args):
        FOREGROUND_BLUE      = 0x0001 # text color contains blue.
        FOREGROUND_GREEN     = 0x0002 # text color contains green.
        FOREGROUND_RED       = 0x0004 # text color contains red.
        FOREGROUND_INTENSITY = 0x0008 # text color is intensified.
        FOREGROUND_WHITE     = FOREGROUND_BLUE|FOREGROUND_GREEN |FOREGROUND_RED
       # winbase.h
        STD_INPUT_HANDLE = -10
        STD_OUTPUT_HANDLE = -11
        STD_ERROR_HANDLE = -12

        # wincon.h
        FOREGROUND_BLACK     = 0x0000
        FOREGROUND_BLUE      = 0x0001
        FOREGROUND_GREEN     = 0x0002
        FOREGROUND_CYAN      = 0x0003
        FOREGROUND_RED       = 0x0004
        FOREGROUND_MAGENTA   = 0x0005
        FOREGROUND_YELLOW    = 0x0006
        FOREGROUND_GREY      = 0x0007
        FOREGROUND_INTENSITY = 0x0008 # foreground color is intensified.

        BACKGROUND_BLACK     = 0x0000
        BACKGROUND_BLUE      = 0x0010
        BACKGROUND_GREEN     = 0x0020
        BACKGROUND_CYAN      = 0x0030
        BACKGROUND_RED       = 0x0040
        BACKGROUND_MAGENTA   = 0x0050
        BACKGROUND_YELLOW    = 0x0060
        BACKGROUND_GREY      = 0x0070
        BACKGROUND_INTENSITY = 0x0080 # background color is intensified.     

        levelno = args[1].levelno
        if(levelno>=50):
            color = BACKGROUND_YELLOW | FOREGROUND_RED | FOREGROUND_INTENSITY | BACKGROUND_INTENSITY 
        elif(levelno>=40):
            color = FOREGROUND_RED | FOREGROUND_INTENSITY
        elif(levelno>=30):
            color = FOREGROUND_YELLOW | FOREGROUND_INTENSITY
        elif(levelno>=20):
            color = FOREGROUND_GREEN
        elif(levelno>=10):
            color = FOREGROUND_MAGENTA
        else:
            color =  FOREGROUND_WHITE
        args[0]._set_color(color)

        ret = fn(*args)
        args[0]._set_color( FOREGROUND_WHITE )
        #print "after"
        return ret
    return new

def add_coloring_to_emit_ansi(fn):
    # add methods we need to the class
    def new(*args):
        levelno = args[1].levelno
        if(levelno>=50):
            color = '\x1b[31m' # red
        elif(levelno>=40):
            color = '\x1b[31m' # red
        elif(levelno>=30):
            color = '\x1b[33m' # yellow
        elif(levelno>=20):
            color = '\x1b[32m' # green 
        elif(levelno>=10):
            color = '\x1b[35m' # pink
        else:
            color = '\x1b[0m' # normal
        args[1].msg = color + args[1].msg +  '\x1b[0m'  # normal
        #print "after"
        return fn(*args)
    return new

import platform
if platform.system()=='Windows':
    # Windows does not support ANSI escapes and we are using API calls to set the console color
    logging.StreamHandler.emit = add_coloring_to_emit_windows(logging.StreamHandler.emit)
else:
    # all non-Windows platforms are supporting ANSI escapes so we use them
    logging.StreamHandler.emit = add_coloring_to_emit_ansi(logging.StreamHandler.emit)
    #log = logging.getLogger()
    #log.addFilter(log_filter())
    #//hdlr = logging.StreamHandler()
    #//hdlr.setFormatter(formatter())

Here is a solution that should work on any platform. If it doesn’t just tell me and I will update it.

How it works: on platform supporting ANSI escapes is using them (non-Windows) and on Windows it does use API calls to change the console colors.

The script does hack the logging.StreamHandler.emit method from standard library adding a wrapper to it.

TestColorer.py

# Usage: add Colorer.py near you script and import it.
import logging
import Colorer

logging.warn("a warning")
logging.error("some error")
logging.info("some info")

Colorer.py

#!/usr/bin/env python
# encoding: utf-8
import logging
# now we patch Python code to add color support to logging.StreamHandler
def add_coloring_to_emit_windows(fn):
        # add methods we need to the class
    def _out_handle(self):
        import ctypes
        return ctypes.windll.kernel32.GetStdHandle(self.STD_OUTPUT_HANDLE)
    out_handle = property(_out_handle)

    def _set_color(self, code):
        import ctypes
        # Constants from the Windows API
        self.STD_OUTPUT_HANDLE = -11
        hdl = ctypes.windll.kernel32.GetStdHandle(self.STD_OUTPUT_HANDLE)
        ctypes.windll.kernel32.SetConsoleTextAttribute(hdl, code)

    setattr(logging.StreamHandler, '_set_color', _set_color)

    def new(*args):
        FOREGROUND_BLUE      = 0x0001 # text color contains blue.
        FOREGROUND_GREEN     = 0x0002 # text color contains green.
        FOREGROUND_RED       = 0x0004 # text color contains red.
        FOREGROUND_INTENSITY = 0x0008 # text color is intensified.
        FOREGROUND_WHITE     = FOREGROUND_BLUE|FOREGROUND_GREEN |FOREGROUND_RED
       # winbase.h
        STD_INPUT_HANDLE = -10
        STD_OUTPUT_HANDLE = -11
        STD_ERROR_HANDLE = -12

        # wincon.h
        FOREGROUND_BLACK     = 0x0000
        FOREGROUND_BLUE      = 0x0001
        FOREGROUND_GREEN     = 0x0002
        FOREGROUND_CYAN      = 0x0003
        FOREGROUND_RED       = 0x0004
        FOREGROUND_MAGENTA   = 0x0005
        FOREGROUND_YELLOW    = 0x0006
        FOREGROUND_GREY      = 0x0007
        FOREGROUND_INTENSITY = 0x0008 # foreground color is intensified.

        BACKGROUND_BLACK     = 0x0000
        BACKGROUND_BLUE      = 0x0010
        BACKGROUND_GREEN     = 0x0020
        BACKGROUND_CYAN      = 0x0030
        BACKGROUND_RED       = 0x0040
        BACKGROUND_MAGENTA   = 0x0050
        BACKGROUND_YELLOW    = 0x0060
        BACKGROUND_GREY      = 0x0070
        BACKGROUND_INTENSITY = 0x0080 # background color is intensified.     

        levelno = args[1].levelno
        if(levelno>=50):
            color = BACKGROUND_YELLOW | FOREGROUND_RED | FOREGROUND_INTENSITY | BACKGROUND_INTENSITY 
        elif(levelno>=40):
            color = FOREGROUND_RED | FOREGROUND_INTENSITY
        elif(levelno>=30):
            color = FOREGROUND_YELLOW | FOREGROUND_INTENSITY
        elif(levelno>=20):
            color = FOREGROUND_GREEN
        elif(levelno>=10):
            color = FOREGROUND_MAGENTA
        else:
            color =  FOREGROUND_WHITE
        args[0]._set_color(color)

        ret = fn(*args)
        args[0]._set_color( FOREGROUND_WHITE )
        #print "after"
        return ret
    return new

def add_coloring_to_emit_ansi(fn):
    # add methods we need to the class
    def new(*args):
        levelno = args[1].levelno
        if(levelno>=50):
            color = '\x1b[31m' # red
        elif(levelno>=40):
            color = '\x1b[31m' # red
        elif(levelno>=30):
            color = '\x1b[33m' # yellow
        elif(levelno>=20):
            color = '\x1b[32m' # green 
        elif(levelno>=10):
            color = '\x1b[35m' # pink
        else:
            color = '\x1b[0m' # normal
        args[1].msg = color + args[1].msg +  '\x1b[0m'  # normal
        #print "after"
        return fn(*args)
    return new

import platform
if platform.system()=='Windows':
    # Windows does not support ANSI escapes and we are using API calls to set the console color
    logging.StreamHandler.emit = add_coloring_to_emit_windows(logging.StreamHandler.emit)
else:
    # all non-Windows platforms are supporting ANSI escapes so we use them
    logging.StreamHandler.emit = add_coloring_to_emit_ansi(logging.StreamHandler.emit)
    #log = logging.getLogger()
    #log.addFilter(log_filter())
    #//hdlr = logging.StreamHandler()
    #//hdlr.setFormatter(formatter())

回答 3

更新:因为这是我很久以来一直想抓痒的痒,所以我继续为像我这样的懒人编写了一个库,这些人只想简单地做事: zenlog

Colorlog对此非常有用。它可以在PyPI上使用(因此可以通过安装pip install colorlog),并可以进行积极维护

这是一个快速复制粘贴的代码段,用于设置日志记录并打印看起来不错的日志消息:

import logging
LOG_LEVEL = logging.DEBUG
LOGFORMAT = "  %(log_color)s%(levelname)-8s%(reset)s | %(log_color)s%(message)s%(reset)s"
from colorlog import ColoredFormatter
logging.root.setLevel(LOG_LEVEL)
formatter = ColoredFormatter(LOGFORMAT)
stream = logging.StreamHandler()
stream.setLevel(LOG_LEVEL)
stream.setFormatter(formatter)
log = logging.getLogger('pythonConfig')
log.setLevel(LOG_LEVEL)
log.addHandler(stream)

log.debug("A quirky message only developers care about")
log.info("Curious users might want to know this")
log.warn("Something is wrong and any user should be informed")
log.error("Serious stuff, this is red for a reason")
log.critical("OH NO everything is on fire")

输出:

Colorlog输出

Update: Because this is an itch that I’ve been meaning to scratch for so long, I went ahead and wrote a library for lazy people like me who just want simple ways to do things: zenlog

Colorlog is excellent for this. It’s available on PyPI (and thus installable through pip install colorlog) and is actively maintained.

Here’s a quick copy-and-pasteable snippet to set up logging and print decent-looking log messages:

import logging
LOG_LEVEL = logging.DEBUG
LOGFORMAT = "  %(log_color)s%(levelname)-8s%(reset)s | %(log_color)s%(message)s%(reset)s"
from colorlog import ColoredFormatter
logging.root.setLevel(LOG_LEVEL)
formatter = ColoredFormatter(LOGFORMAT)
stream = logging.StreamHandler()
stream.setLevel(LOG_LEVEL)
stream.setFormatter(formatter)
log = logging.getLogger('pythonConfig')
log.setLevel(LOG_LEVEL)
log.addHandler(stream)

log.debug("A quirky message only developers care about")
log.info("Curious users might want to know this")
log.warn("Something is wrong and any user should be informed")
log.error("Serious stuff, this is red for a reason")
log.critical("OH NO everything is on fire")

Output:

Colorlog output


回答 4

快速而肮脏的解决方案,用于预定义的日志级别,而无需定义新类。

logging.addLevelName( logging.WARNING, "\033[1;31m%s\033[1;0m" % logging.getLevelName(logging.WARNING))
logging.addLevelName( logging.ERROR, "\033[1;41m%s\033[1;0m" % logging.getLevelName(logging.ERROR))

Quick and dirty solution for predefined log levels and without defining a new class.

logging.addLevelName( logging.WARNING, "\033[1;31m%s\033[1;0m" % logging.getLevelName(logging.WARNING))
logging.addLevelName( logging.ERROR, "\033[1;41m%s\033[1;0m" % logging.getLevelName(logging.ERROR))

回答 5

2020代码,无需其他软件包,Python 3

定义一个Class

import logging

class CustomFormatter(logging.Formatter):
    """Logging Formatter to add colors and count warning / errors"""

    grey = "\x1b[38;21m"
    yellow = "\x1b[33;21m"
    red = "\x1b[31;21m"
    bold_red = "\x1b[31;1m"
    reset = "\x1b[0m"
    format = "%(asctime)s - %(name)s - %(levelname)s - %(message)s (%(filename)s:%(lineno)d)"

    FORMATS = {
        logging.DEBUG: grey + format + reset,
        logging.INFO: grey + format + reset,
        logging.WARNING: yellow + format + reset,
        logging.ERROR: red + format + reset,
        logging.CRITICAL: bold_red + format + reset
    }

    def format(self, record):
        log_fmt = self.FORMATS.get(record.levelno)
        formatter = logging.Formatter(log_fmt)
        return formatter.format(record)

实例化记录器

# create logger with 'spam_application'
logger = logging.getLogger("My_app")
logger.setLevel(logging.DEBUG)

# create console handler with a higher log level
ch = logging.StreamHandler()
ch.setLevel(logging.DEBUG)

ch.setFormatter(CustomFormatter())

logger.addHandler(ch)

并使用!

logger.debug("debug message")
logger.info("info message")
logger.warning("warning message")
logger.error("error message")
logger.critical("critical message")

结果 在此处输入图片说明

全彩方案 在此处输入图片说明

对于窗户

此解决方案可在Mac OS,IDE终端上使用。看起来window的命令提示符在默认情况下根本没有颜色。以下是如何启用它们的说明,我没有尝试过这些说明https://www.howtogeek.com/322432/how-to-customize-your-command-prompts-color-scheme-with-microsofts-colortool/

2020 code, no additional packages required, Python 3

Define a class

import logging

class CustomFormatter(logging.Formatter):
    """Logging Formatter to add colors and count warning / errors"""

    grey = "\x1b[38;21m"
    yellow = "\x1b[33;21m"
    red = "\x1b[31;21m"
    bold_red = "\x1b[31;1m"
    reset = "\x1b[0m"
    format = "%(asctime)s - %(name)s - %(levelname)s - %(message)s (%(filename)s:%(lineno)d)"

    FORMATS = {
        logging.DEBUG: grey + format + reset,
        logging.INFO: grey + format + reset,
        logging.WARNING: yellow + format + reset,
        logging.ERROR: red + format + reset,
        logging.CRITICAL: bold_red + format + reset
    }

    def format(self, record):
        log_fmt = self.FORMATS.get(record.levelno)
        formatter = logging.Formatter(log_fmt)
        return formatter.format(record)

Instantiate logger

# create logger with 'spam_application'
logger = logging.getLogger("My_app")
logger.setLevel(logging.DEBUG)

# create console handler with a higher log level
ch = logging.StreamHandler()
ch.setLevel(logging.DEBUG)

ch.setFormatter(CustomFormatter())

logger.addHandler(ch)

And use!

logger.debug("debug message")
logger.info("info message")
logger.warning("warning message")
logger.error("error message")
logger.critical("critical message")

Result enter image description here

The full color scheme enter image description here

For windows

This solution works on Mac OS, IDE terminals. Looks like window’s command prompt doesn’t have colors at all by default. Here are instructions how to enable them, which I haven’t try https://www.howtogeek.com/322432/how-to-customize-your-command-prompts-color-scheme-with-microsofts-colortool/


回答 6

好吧,我想我也应该添加我的彩色记录器版本。

这没什么花哨的,但是它使用起来非常简单并且不会更改记录对象,从而避免了在使用文件处理程序的情况下将ANSI转义序列记录到日志文件中。它不会影响日志消息的格式。

如果您已经在使用日志记录模块的Formatter,则要获取彩色级别名称,只需将您的顾问处理程序Formatter替换为ColoredFormatter即可。如果要记录整个应用程序,则只需要对顶级记录器执行此操作。

Colored_log.py

#!/usr/bin/env python

from copy import copy
from logging import Formatter

MAPPING = {
    'DEBUG'   : 37, # white
    'INFO'    : 36, # cyan
    'WARNING' : 33, # yellow
    'ERROR'   : 31, # red
    'CRITICAL': 41, # white on red bg
}

PREFIX = '\033['
SUFFIX = '\033[0m'

class ColoredFormatter(Formatter):

    def __init__(self, patern):
        Formatter.__init__(self, patern)

    def format(self, record):
        colored_record = copy(record)
        levelname = colored_record.levelname
        seq = MAPPING.get(levelname, 37) # default white
        colored_levelname = ('{0}{1}m{2}{3}') \
            .format(PREFIX, seq, levelname, SUFFIX)
        colored_record.levelname = colored_levelname
        return Formatter.format(self, colored_record)

用法示例

app.py

#!/usr/bin/env python

import logging
from colored_log import ColoredFormatter

# Create top level logger
log = logging.getLogger("main")

# Add console handler using our custom ColoredFormatter
ch = logging.StreamHandler()
ch.setLevel(logging.DEBUG)
cf = ColoredFormatter("[%(name)s][%(levelname)s]  %(message)s (%(filename)s:%(lineno)d)")
ch.setFormatter(cf)
log.addHandler(ch)

# Add file handler
fh = logging.FileHandler('app.log')
fh.setLevel(logging.DEBUG)
ff = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
fh.setFormatter(ff)
log.addHandler(fh)

# Set log level
log.setLevel(logging.DEBUG)

# Log some stuff
log.debug("app has started")
log.info("Logging to 'app.log' in the script dir")
log.warning("This is my last warning, take heed")
log.error("This is an error")
log.critical("He's dead, Jim")

# Import a sub-module 
import sub_module

sub_module.py

#!/usr/bin/env python

import logging
log = logging.getLogger('main.sub_module')

log.debug("Hello from the sub module")

结果

终端输出

终端输出

app.log内容

2017-09-29 00:32:23,434 - main - DEBUG - app has started
2017-09-29 00:32:23,434 - main - INFO - Logging to 'app.log' in the script dir
2017-09-29 00:32:23,435 - main - WARNING - This is my last warning, take heed
2017-09-29 00:32:23,435 - main - ERROR - This is an error
2017-09-29 00:32:23,435 - main - CRITICAL - He's dead, Jim
2017-09-29 00:32:23,435 - main.sub_module - DEBUG - Hello from the sub module

当然,通过格式化终端和日志文件输出,您可以得到所需的效果。仅日志级别将被着色。

我希望有人觉得这有用,并且不仅仅是太多了。:)

可以从GitHub Gist下载Python示例文件:https : //gist.github.com/KurtJacobson/48e750701acec40c7161b5a2f79e6bfd

Well, I guess I might as well add my variation of the colored logger.

This is nothing fancy, but it is very simple to use and does not change the record object, thereby avoids logging the ANSI escape sequences to a log file if a file handler is used. It does not effect the log message formatting.

If you are already using the logging module’s Formatter, all you have to do to get colored level names is to replace your counsel handlers Formatter with the ColoredFormatter. If you are logging an entire app you only need to do this for the top level logger.

colored_log.py

#!/usr/bin/env python

from copy import copy
from logging import Formatter

MAPPING = {
    'DEBUG'   : 37, # white
    'INFO'    : 36, # cyan
    'WARNING' : 33, # yellow
    'ERROR'   : 31, # red
    'CRITICAL': 41, # white on red bg
}

PREFIX = '\033['
SUFFIX = '\033[0m'

class ColoredFormatter(Formatter):

    def __init__(self, patern):
        Formatter.__init__(self, patern)

    def format(self, record):
        colored_record = copy(record)
        levelname = colored_record.levelname
        seq = MAPPING.get(levelname, 37) # default white
        colored_levelname = ('{0}{1}m{2}{3}') \
            .format(PREFIX, seq, levelname, SUFFIX)
        colored_record.levelname = colored_levelname
        return Formatter.format(self, colored_record)

Example usage

app.py

#!/usr/bin/env python

import logging
from colored_log import ColoredFormatter

# Create top level logger
log = logging.getLogger("main")

# Add console handler using our custom ColoredFormatter
ch = logging.StreamHandler()
ch.setLevel(logging.DEBUG)
cf = ColoredFormatter("[%(name)s][%(levelname)s]  %(message)s (%(filename)s:%(lineno)d)")
ch.setFormatter(cf)
log.addHandler(ch)

# Add file handler
fh = logging.FileHandler('app.log')
fh.setLevel(logging.DEBUG)
ff = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
fh.setFormatter(ff)
log.addHandler(fh)

# Set log level
log.setLevel(logging.DEBUG)

# Log some stuff
log.debug("app has started")
log.info("Logging to 'app.log' in the script dir")
log.warning("This is my last warning, take heed")
log.error("This is an error")
log.critical("He's dead, Jim")

# Import a sub-module 
import sub_module

sub_module.py

#!/usr/bin/env python

import logging
log = logging.getLogger('main.sub_module')

log.debug("Hello from the sub module")

Results

Terminal output

Terminal output

app.log content

2017-09-29 00:32:23,434 - main - DEBUG - app has started
2017-09-29 00:32:23,434 - main - INFO - Logging to 'app.log' in the script dir
2017-09-29 00:32:23,435 - main - WARNING - This is my last warning, take heed
2017-09-29 00:32:23,435 - main - ERROR - This is an error
2017-09-29 00:32:23,435 - main - CRITICAL - He's dead, Jim
2017-09-29 00:32:23,435 - main.sub_module - DEBUG - Hello from the sub module

Of course you can get as fancy as you want with formatting the terminal and log file outputs. Only the log level will be colorized.

I hope somebody finds this useful and it is not just too much more of the same. :)

The Python example files can be downloaded from this GitHub Gist: https://gist.github.com/KurtJacobson/48e750701acec40c7161b5a2f79e6bfd


回答 7

我从airmind支持前景和背景标签更新了该示例。只需在日志格式化程序字符串中使用颜色变量$ BLACK-$ WHITE。要设置背景,只需使用$ BG-BLACK-$ BG-WHITE。

import logging

BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE = range(8)

COLORS = {
    'WARNING'  : YELLOW,
    'INFO'     : WHITE,
    'DEBUG'    : BLUE,
    'CRITICAL' : YELLOW,
    'ERROR'    : RED,
    'RED'      : RED,
    'GREEN'    : GREEN,
    'YELLOW'   : YELLOW,
    'BLUE'     : BLUE,
    'MAGENTA'  : MAGENTA,
    'CYAN'     : CYAN,
    'WHITE'    : WHITE,
}

RESET_SEQ = "\033[0m"
COLOR_SEQ = "\033[1;%dm"
BOLD_SEQ  = "\033[1m"

class ColorFormatter(logging.Formatter):

    def __init__(self, *args, **kwargs):
        # can't do super(...) here because Formatter is an old school class
        logging.Formatter.__init__(self, *args, **kwargs)

    def format(self, record):
        levelname = record.levelname
        color     = COLOR_SEQ % (30 + COLORS[levelname])
        message   = logging.Formatter.format(self, record)
        message   = message.replace("$RESET", RESET_SEQ)\
                           .replace("$BOLD",  BOLD_SEQ)\
                           .replace("$COLOR", color)
        for k,v in COLORS.items():
            message = message.replace("$" + k,    COLOR_SEQ % (v+30))\
                             .replace("$BG" + k,  COLOR_SEQ % (v+40))\
                             .replace("$BG-" + k, COLOR_SEQ % (v+40))
        return message + RESET_SEQ

logging.ColorFormatter = ColorFormatter

因此,现在您可以在配置文件中简单地执行以下操作:

[formatter_colorFormatter]
class=logging.ColorFormatter
format= $COLOR%(levelname)s $RESET %(asctime)s $BOLD$COLOR%(name)s$RESET %(message)s

I updated the example from airmind supporting tags for foreground and background. Just use the color variables $BLACK – $WHITE in your log formatter string. To set the background just use $BG-BLACK – $BG-WHITE.

import logging

BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE = range(8)

COLORS = {
    'WARNING'  : YELLOW,
    'INFO'     : WHITE,
    'DEBUG'    : BLUE,
    'CRITICAL' : YELLOW,
    'ERROR'    : RED,
    'RED'      : RED,
    'GREEN'    : GREEN,
    'YELLOW'   : YELLOW,
    'BLUE'     : BLUE,
    'MAGENTA'  : MAGENTA,
    'CYAN'     : CYAN,
    'WHITE'    : WHITE,
}

RESET_SEQ = "\033[0m"
COLOR_SEQ = "\033[1;%dm"
BOLD_SEQ  = "\033[1m"

class ColorFormatter(logging.Formatter):

    def __init__(self, *args, **kwargs):
        # can't do super(...) here because Formatter is an old school class
        logging.Formatter.__init__(self, *args, **kwargs)

    def format(self, record):
        levelname = record.levelname
        color     = COLOR_SEQ % (30 + COLORS[levelname])
        message   = logging.Formatter.format(self, record)
        message   = message.replace("$RESET", RESET_SEQ)\
                           .replace("$BOLD",  BOLD_SEQ)\
                           .replace("$COLOR", color)
        for k,v in COLORS.items():
            message = message.replace("$" + k,    COLOR_SEQ % (v+30))\
                             .replace("$BG" + k,  COLOR_SEQ % (v+40))\
                             .replace("$BG-" + k, COLOR_SEQ % (v+40))
        return message + RESET_SEQ

logging.ColorFormatter = ColorFormatter

So now you can simple do the following in your config file:

[formatter_colorFormatter]
class=logging.ColorFormatter
format= $COLOR%(levelname)s $RESET %(asctime)s $BOLD$COLOR%(name)s$RESET %(message)s

回答 8

您可以导入colorlog模块并使用其ColoredFormatter用于为日志消息着色。

主模块样板:

import logging
import os
import sys
try:
    import colorlog
except ImportError:
    pass

def setup_logging():
    root = logging.getLogger()
    root.setLevel(logging.DEBUG)
    format      = '%(asctime)s - %(levelname)-8s - %(message)s'
    date_format = '%Y-%m-%d %H:%M:%S'
    if 'colorlog' in sys.modules and os.isatty(2):
        cformat = '%(log_color)s' + format
        f = colorlog.ColoredFormatter(cformat, date_format,
              log_colors = { 'DEBUG'   : 'reset',       'INFO' : 'reset',
                             'WARNING' : 'bold_yellow', 'ERROR': 'bold_red',
                             'CRITICAL': 'bold_red' })
    else:
        f = logging.Formatter(format, date_format)
    ch = logging.StreamHandler()
    ch.setFormatter(f)
    root.addHandler(ch)

setup_logging()
log = logging.getLogger(__name__)

该代码仅在安装了colorlog模块并且输出实际到达终端时才在日志消息中启用颜色。这样可以避免在重定向日志输出时将转义序列写入文件。

另外,设置了自定义配色方案,该方案更适合于深色背景的终端。

一些记录调用示例:

log.debug   ('Hello Debug')
log.info    ('Hello Info')
log.warn    ('Hello Warn')
log.error   ('Hello Error')
log.critical('Hello Critical')

输出:

在此处输入图片说明

You can import the colorlog module and use its ColoredFormatter for colorizing log messages.

Example

Boilerplate for main module:

import logging
import os
import sys
try:
    import colorlog
except ImportError:
    pass

def setup_logging():
    root = logging.getLogger()
    root.setLevel(logging.DEBUG)
    format      = '%(asctime)s - %(levelname)-8s - %(message)s'
    date_format = '%Y-%m-%d %H:%M:%S'
    if 'colorlog' in sys.modules and os.isatty(2):
        cformat = '%(log_color)s' + format
        f = colorlog.ColoredFormatter(cformat, date_format,
              log_colors = { 'DEBUG'   : 'reset',       'INFO' : 'reset',
                             'WARNING' : 'bold_yellow', 'ERROR': 'bold_red',
                             'CRITICAL': 'bold_red' })
    else:
        f = logging.Formatter(format, date_format)
    ch = logging.StreamHandler()
    ch.setFormatter(f)
    root.addHandler(ch)

setup_logging()
log = logging.getLogger(__name__)

The code only enables colors in log messages, if the colorlog module is installed and if the output actually goes to a terminal. This avoids escape sequences being written to a file when the log output is redirected.

Also, a custom color scheme is setup that is better suited for terminals with dark background.

Some example logging calls:

log.debug   ('Hello Debug')
log.info    ('Hello Info')
log.warn    ('Hello Warn')
log.error   ('Hello Error')
log.critical('Hello Critical')

Output:

enter image description here


回答 9

查看以下解决方案。流处理程序应该是进行着色的东西,然后您可以选择对单词进行着色,而不仅仅是对整个行(使用Formatter)进行着色。

http://plumberjack.blogspot.com/2010/12/colorizing-logging-output-in-terminals.html

Look at the following solution. The stream handler should be the thing doing the colouring, then you have the option of colouring words rather than just the whole line (with the Formatter).

http://plumberjack.blogspot.com/2010/12/colorizing-logging-output-in-terminals.html


回答 10

我修改了Sorin提供的原始示例,并将StreamHandler子类化为ColorizedConsoleHandler。

他们解决方案的缺点是它修改了消息,并且因为这是在修改实际的日志消息,所以其他任何处理程序也将获得修改后的消息。

在我们的例子中,这导致在日志文件中带有颜色代码,因为我们使用了多个记录器。

下面的类仅在支持ansi的平台上起作用,但是向其添加Windows颜色代码应该是微不足道的。

import copy
import logging


class ColoredConsoleHandler(logging.StreamHandler):
    def emit(self, record):
        # Need to make a actual copy of the record
        # to prevent altering the message for other loggers
        myrecord = copy.copy(record)
        levelno = myrecord.levelno
        if(levelno >= 50):  # CRITICAL / FATAL
            color = '\x1b[31m'  # red
        elif(levelno >= 40):  # ERROR
            color = '\x1b[31m'  # red
        elif(levelno >= 30):  # WARNING
            color = '\x1b[33m'  # yellow
        elif(levelno >= 20):  # INFO
            color = '\x1b[32m'  # green
        elif(levelno >= 10):  # DEBUG
            color = '\x1b[35m'  # pink
        else:  # NOTSET and anything else
            color = '\x1b[0m'  # normal
        myrecord.msg = color + str(myrecord.msg) + '\x1b[0m'  # normal
        logging.StreamHandler.emit(self, myrecord)

I modified the original example provided by Sorin and subclassed StreamHandler to a ColorizedConsoleHandler.

The downside of their solution is that it modifies the message, and because that is modifying the actual logmessage any other handlers will get the modified message as well.

This resulted in logfiles with colorcodes in them in our case because we use multiple loggers.

The class below only works on platforms that support ansi, but it should be trivial to add the windows colorcodes to it.

import copy
import logging


class ColoredConsoleHandler(logging.StreamHandler):
    def emit(self, record):
        # Need to make a actual copy of the record
        # to prevent altering the message for other loggers
        myrecord = copy.copy(record)
        levelno = myrecord.levelno
        if(levelno >= 50):  # CRITICAL / FATAL
            color = '\x1b[31m'  # red
        elif(levelno >= 40):  # ERROR
            color = '\x1b[31m'  # red
        elif(levelno >= 30):  # WARNING
            color = '\x1b[33m'  # yellow
        elif(levelno >= 20):  # INFO
            color = '\x1b[32m'  # green
        elif(levelno >= 10):  # DEBUG
            color = '\x1b[35m'  # pink
        else:  # NOTSET and anything else
            color = '\x1b[0m'  # normal
        myrecord.msg = color + str(myrecord.msg) + '\x1b[0m'  # normal
        logging.StreamHandler.emit(self, myrecord)

回答 11

现在有一个已发布的PyPi模块,用于自定义彩色日志输出:

https://pypi.python.org/pypi/rainbow_logging_handler/

https://github.com/laysakura/rainbow_logging_handler

  • 支持Windows

  • 支持Django

  • 可自定义的颜色

由于它是作为Python鸡蛋分发的,因此对于任何Python应用程序都非常容易安装。

Now there is a released PyPi module for customizable colored logging output:

https://pypi.python.org/pypi/rainbow_logging_handler/

and

https://github.com/laysakura/rainbow_logging_handler

  • Supports Windows

  • Supports Django

  • Customizable colors

As this is distributed as a Python egg, it is very easy to install for any Python application.


回答 12

有很多回应。但是没有人在谈论装饰器。所以这是我的。

因为它要简单得多。

无需导入任何内容,也无需编写任何子类:

#!/usr/bin/env python
# -*- coding: utf-8 -*-


import logging


NO_COLOR = "\33[m"
RED, GREEN, ORANGE, BLUE, PURPLE, LBLUE, GREY = \
    map("\33[%dm".__mod__, range(31, 38))

logging.basicConfig(format="%(message)s", level=logging.DEBUG)
logger = logging.getLogger(__name__)

# the decorator to apply on the logger methods info, warn, ...
def add_color(logger_method, color):
  def wrapper(message, *args, **kwargs):
    return logger_method(
      # the coloring is applied here.
      color+message+NO_COLOR,
      *args, **kwargs
    )
  return wrapper

for level, color in zip((
  "info", "warn", "error", "debug"), (
  GREEN, ORANGE, RED, BLUE
)):
  setattr(logger, level, add_color(getattr(logger, level), color))

# this is displayed in red.
logger.error("Launching %s." % __file__)

这会将错误设置为红色,将调试消息设置为蓝色,依此类推。喜欢问的问题。

我们甚至可以使包装纸适应 color参数来动态设置消息的颜色:logger.debug("message", color=GREY)

编辑:因此,这是在运行时设置颜色的适应性装饰器:

def add_color(logger_method, _color):
  def wrapper(message, *args, **kwargs):
    color = kwargs.pop("color", _color)
    if isinstance(color, int):
      color = "\33[%dm" % color
    return logger_method(
      # the coloring is applied here.
      color+message+NO_COLOR,
      *args, **kwargs
    )
  return wrapper

# blah blah, apply the decorator...

# this is displayed in red.
logger.error("Launching %s." % __file__)
# this is displayed in blue
logger.error("Launching %s." % __file__, color=34)
# and this, in grey
logger.error("Launching %s." % __file__, color=GREY)

There are tons of responses. But none is talking about decorators. So here’s mine.

Because it is a lot more simple.

There’s no need to import anything, nor to write any subclass:

#!/usr/bin/env python
# -*- coding: utf-8 -*-


import logging


NO_COLOR = "\33[m"
RED, GREEN, ORANGE, BLUE, PURPLE, LBLUE, GREY = \
    map("\33[%dm".__mod__, range(31, 38))

logging.basicConfig(format="%(message)s", level=logging.DEBUG)
logger = logging.getLogger(__name__)

# the decorator to apply on the logger methods info, warn, ...
def add_color(logger_method, color):
  def wrapper(message, *args, **kwargs):
    return logger_method(
      # the coloring is applied here.
      color+message+NO_COLOR,
      *args, **kwargs
    )
  return wrapper

for level, color in zip((
  "info", "warn", "error", "debug"), (
  GREEN, ORANGE, RED, BLUE
)):
  setattr(logger, level, add_color(getattr(logger, level), color))

# this is displayed in red.
logger.error("Launching %s." % __file__)

This set the errors in red, debug messages in blue, and so on. Like asked in the question.

We could even adapt the wrapper to take a color argument to dynamicaly set the message’s color using logger.debug("message", color=GREY)

EDIT: So here’s the adapted decorator to set colors at runtime:

def add_color(logger_method, _color):
  def wrapper(message, *args, **kwargs):
    color = kwargs.pop("color", _color)
    if isinstance(color, int):
      color = "\33[%dm" % color
    return logger_method(
      # the coloring is applied here.
      color+message+NO_COLOR,
      *args, **kwargs
    )
  return wrapper

# blah blah, apply the decorator...

# this is displayed in red.
logger.error("Launching %s." % __file__)
# this is displayed in blue
logger.error("Launching %s." % __file__, color=34)
# and this, in grey
logger.error("Launching %s." % __file__, color=GREY)

回答 13

Airmind方法的另一种小混搭,使所有内容都保持同一类:

class ColorFormatter(logging.Formatter):
  FORMAT = ("[$BOLD%(name)-20s$RESET][%(levelname)-18s]  "
            "%(message)s "
            "($BOLD%(filename)s$RESET:%(lineno)d)")

  BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE = range(8)

  RESET_SEQ = "\033[0m"
  COLOR_SEQ = "\033[1;%dm"
  BOLD_SEQ = "\033[1m"

  COLORS = {
    'WARNING': YELLOW,
    'INFO': WHITE,
    'DEBUG': BLUE,
    'CRITICAL': YELLOW,
    'ERROR': RED
  }

  def formatter_msg(self, msg, use_color = True):
    if use_color:
      msg = msg.replace("$RESET", self.RESET_SEQ).replace("$BOLD", self.BOLD_SEQ)
    else:
      msg = msg.replace("$RESET", "").replace("$BOLD", "")
    return msg

  def __init__(self, use_color=True):
    msg = self.formatter_msg(self.FORMAT, use_color)
    logging.Formatter.__init__(self, msg)
    self.use_color = use_color

  def format(self, record):
    levelname = record.levelname
    if self.use_color and levelname in self.COLORS:
      fore_color = 30 + self.COLORS[levelname]
      levelname_color = self.COLOR_SEQ % fore_color + levelname + self.RESET_SEQ
      record.levelname = levelname_color
    return logging.Formatter.format(self, record)

要将格式化程序附加到处理程序,例如:

handler.setFormatter(ColorFormatter())
logger.addHandler(handler)

Another minor remix of airmind’s approach that keeps everything in one class:

class ColorFormatter(logging.Formatter):
  FORMAT = ("[$BOLD%(name)-20s$RESET][%(levelname)-18s]  "
            "%(message)s "
            "($BOLD%(filename)s$RESET:%(lineno)d)")

  BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE = range(8)

  RESET_SEQ = "\033[0m"
  COLOR_SEQ = "\033[1;%dm"
  BOLD_SEQ = "\033[1m"

  COLORS = {
    'WARNING': YELLOW,
    'INFO': WHITE,
    'DEBUG': BLUE,
    'CRITICAL': YELLOW,
    'ERROR': RED
  }

  def formatter_msg(self, msg, use_color = True):
    if use_color:
      msg = msg.replace("$RESET", self.RESET_SEQ).replace("$BOLD", self.BOLD_SEQ)
    else:
      msg = msg.replace("$RESET", "").replace("$BOLD", "")
    return msg

  def __init__(self, use_color=True):
    msg = self.formatter_msg(self.FORMAT, use_color)
    logging.Formatter.__init__(self, msg)
    self.use_color = use_color

  def format(self, record):
    levelname = record.levelname
    if self.use_color and levelname in self.COLORS:
      fore_color = 30 + self.COLORS[levelname]
      levelname_color = self.COLOR_SEQ % fore_color + levelname + self.RESET_SEQ
      record.levelname = levelname_color
    return logging.Formatter.format(self, record)

To use attach the formatter to a handler, something like:

handler.setFormatter(ColorFormatter())
logger.addHandler(handler)

回答 14

一个简单但非常灵活的工具,可以为任何终端文本加上颜色是’ colout ‘。

pip install colout
myprocess | colout REGEX_WITH_GROUPS color1,color2...

如果“ myprocess”的输出中与正则表达式的第1组匹配的任何文本将使用color1进行着色,将第2组使用color2进行着色,等等。

例如:

tail -f /var/log/mylogfile | colout '^(\w+ \d+ [\d:]+)|(\w+\.py:\d+ .+\(\)): (.+)$' white,black,cyan bold,bold,normal

也就是说,第一个正则表达式组(parens)与日志文件中的初始日期匹配,第二个正则表达式组与python文件名,行号和函数名称匹配,而第三组则匹配之后的日志消息。我还使用了并行的“粗体/法线”序列以及颜色序列。看起来像:

彩色格式的日志文件

请注意,与我的任何正则表达式都不匹配的行或行的部分仍会回显,因此这与’grep –color’并不一样-输出不会过滤掉任何内容。

显然,这足够灵活,您可以将其用于任何进程,而不仅仅是尾随日志文件。我通常想在任何时候给某物上色的过程中,都会立即制作一个新的正则表达式。因此,我喜欢使用colout而不是任何自定义日志文件着色工具,因为无论我要着色什么,我都只需要学习一种工具:日志记录,测试输出,终端中语法高亮的代码片段等。

它还避免实际在日志文件本身中转储ANSI代码,这是个不好的主意,因为除非您始终记得在grep regex中匹配ANSI代码,否则它会破坏对日志文件中的模式进行grepping之类的操作。

A simple but very flexible tool for coloring ANY terminal text is ‘colout‘.

pip install colout
myprocess | colout REGEX_WITH_GROUPS color1,color2...

Where any text in the output of ‘myprocess’ which matches group 1 of the regex will be colored with color1, group 2 with color2, etc.

For example:

tail -f /var/log/mylogfile | colout '^(\w+ \d+ [\d:]+)|(\w+\.py:\d+ .+\(\)): (.+)$' white,black,cyan bold,bold,normal

i.e. the first regex group (parens) matches the initial date in the logfile, the second group matches a python filename, line number and function name, and the third group matches the log message that comes after that. I also use a parallel sequence of ‘bold/normals’ as well as the sequence of colors. This looks like:

logfile with colored formatting

Note that lines or parts of lines which don’t match any of my regex are still echoed, so this isn’t like ‘grep –color’ – nothing is filtered out of the output.

Obviously this is flexible enough that you can use it with any process, not just tailing logfiles. I usually just whip up a new regex on the fly any time I want to colorize something. For this reason, I prefer colout to any custom logfile-coloring tool, because I only need to learn one tool, regardless of what I’m coloring: logging, test output, syntax highlighting snippets of code in the terminal, etc.

It also avoids actually dumping ANSI codes in the logfile itself, which IMHO is a bad idea, because it will break things like grepping for patterns in the logfile unless you always remember to match the ANSI codes in your grep regex.


回答 15

import logging
import sys

colors = {'pink': '\033[95m', 'blue': '\033[94m', 'green': '\033[92m', 'yellow': '\033[93m', 'red': '\033[91m',
      'ENDC': '\033[0m', 'bold': '\033[1m', 'underline': '\033[4m'}

logging.basicConfig(stream=sys.stdout, level=logging.DEBUG)


def str_color(color, data):
    return colors[color] + str(data) + colors['ENDC']

params = {'param1': id1, 'param2': id2}

logging.info('\nParams:' + str_color("blue", str(params)))`
import logging
import sys

colors = {'pink': '\033[95m', 'blue': '\033[94m', 'green': '\033[92m', 'yellow': '\033[93m', 'red': '\033[91m',
      'ENDC': '\033[0m', 'bold': '\033[1m', 'underline': '\033[4m'}

logging.basicConfig(stream=sys.stdout, level=logging.DEBUG)


def str_color(color, data):
    return colors[color] + str(data) + colors['ENDC']

params = {'param1': id1, 'param2': id2}

logging.info('\nParams:' + str_color("blue", str(params)))`

回答 16

这是我的解决方案:

class ColouredFormatter(logging.Formatter):
    RESET = '\x1B[0m'
    RED = '\x1B[31m'
    YELLOW = '\x1B[33m'
    BRGREEN = '\x1B[01;32m'  # grey in solarized for terminals

    def format(self, record, colour=False):
        message = super().format(record)

        if not colour:
            return message

        level_no = record.levelno
        if level_no >= logging.CRITICAL:
            colour = self.RED
        elif level_no >= logging.ERROR:
            colour = self.RED
        elif level_no >= logging.WARNING:
            colour = self.YELLOW
        elif level_no >= logging.INFO:
            colour = self.RESET
        elif level_no >= logging.DEBUG:
            colour = self.BRGREEN
        else:
            colour = self.RESET

        message = colour + message + self.RESET

        return message


class ColouredHandler(logging.StreamHandler):
    def __init__(self, stream=sys.stdout):
        super().__init__(stream)

    def format(self, record, colour=False):
        if not isinstance(self.formatter, ColouredFormatter):
            self.formatter = ColouredFormatter()

        return self.formatter.format(record, colour)

    def emit(self, record):
        stream = self.stream
        try:
            msg = self.format(record, stream.isatty())
            stream.write(msg)
            stream.write(self.terminator)
            self.flush()
        except Exception:
            self.handleError(record)


h = ColouredHandler()
h.formatter = ColouredFormatter('{asctime} {levelname:8} {message}', '%Y-%m-%d %H:%M:%S', '{')
logging.basicConfig(level=logging.DEBUG, handlers=[h])

Here’s my solution:

class ColouredFormatter(logging.Formatter):
    RESET = '\x1B[0m'
    RED = '\x1B[31m'
    YELLOW = '\x1B[33m'
    BRGREEN = '\x1B[01;32m'  # grey in solarized for terminals

    def format(self, record, colour=False):
        message = super().format(record)

        if not colour:
            return message

        level_no = record.levelno
        if level_no >= logging.CRITICAL:
            colour = self.RED
        elif level_no >= logging.ERROR:
            colour = self.RED
        elif level_no >= logging.WARNING:
            colour = self.YELLOW
        elif level_no >= logging.INFO:
            colour = self.RESET
        elif level_no >= logging.DEBUG:
            colour = self.BRGREEN
        else:
            colour = self.RESET

        message = colour + message + self.RESET

        return message


class ColouredHandler(logging.StreamHandler):
    def __init__(self, stream=sys.stdout):
        super().__init__(stream)

    def format(self, record, colour=False):
        if not isinstance(self.formatter, ColouredFormatter):
            self.formatter = ColouredFormatter()

        return self.formatter.format(record, colour)

    def emit(self, record):
        stream = self.stream
        try:
            msg = self.format(record, stream.isatty())
            stream.write(msg)
            stream.write(self.terminator)
            self.flush()
        except Exception:
            self.handleError(record)


h = ColouredHandler()
h.formatter = ColouredFormatter('{asctime} {levelname:8} {message}', '%Y-%m-%d %H:%M:%S', '{')
logging.basicConfig(level=logging.DEBUG, handlers=[h])

回答 17

我遇到的麻烦是正确设置了格式化程序:

class ColouredFormatter(logging.Formatter):    
    def __init__(self, msg):
        logging.Formatter.__init__(self, msg)
        self._init_colour = _get_colour()

    def close(self):
        # restore the colour information to what it was
        _set_colour(self._init_colour)

    def format(self, record):        
        # Add your own colourer based on the other examples
        _set_colour( LOG_LEVEL_COLOUR[record.levelno] )
        return logging.Formatter.format(self, record)         

def init():
    # Set up the formatter. Needs to be first thing done.
    rootLogger = logging.getLogger()
    hdlr = logging.StreamHandler()
    fmt = ColouredFormatter('%(message)s')
    hdlr.setFormatter(fmt)
    rootLogger.addHandler(hdlr)

然后使用:

import coloured_log
import logging

coloured_log.init()
logging.info("info")    
logging.debug("debug")    

coloured_log.close()    # restore colours

The bit I had trouble with was setting up the formatter properly:

class ColouredFormatter(logging.Formatter):    
    def __init__(self, msg):
        logging.Formatter.__init__(self, msg)
        self._init_colour = _get_colour()

    def close(self):
        # restore the colour information to what it was
        _set_colour(self._init_colour)

    def format(self, record):        
        # Add your own colourer based on the other examples
        _set_colour( LOG_LEVEL_COLOUR[record.levelno] )
        return logging.Formatter.format(self, record)         

def init():
    # Set up the formatter. Needs to be first thing done.
    rootLogger = logging.getLogger()
    hdlr = logging.StreamHandler()
    fmt = ColouredFormatter('%(message)s')
    hdlr.setFormatter(fmt)
    rootLogger.addHandler(hdlr)

And then to use:

import coloured_log
import logging

coloured_log.init()
logging.info("info")    
logging.debug("debug")    

coloured_log.close()    # restore colours

回答 18

尽管其他解决方案看起来不错,但它们仍有一些问题。有些会为整个行上色,有些时候是不需要的,有些则忽略了您可能一起拥有的任何配置。下面的解决方案只影响消息本身,什么都不会影响。

class ColoredFormatter(logging.Formatter):
    def format(self, record):
        if record.levelno == logging.WARNING:
            record.msg = '\033[93m%s\033[0m' % record.msg
        elif record.levelno == logging.ERROR:
            record.msg = '\033[91m%s\033[0m' % record.msg
        return logging.Formatter.format(self, record)

logger = logging.getLogger('mylogger')
handler = logging.StreamHandler()

log_format = '[%(asctime)s]:%(levelname)-7s:%(message)s'
time_format = '%H:%M:%S'
formatter = ColoredFormatter(log_format, datefmt=time_format)
handler.setFormatter(formatter)
logger.addHandler(handler)

logger.warn('this should be yellow')
logger.error('this should be red')

输出量

[17:01:36]:WARNING:this should be yellow
[17:01:37]:ERROR  :this should be red

如您所见,其他所有内容仍会输出,并保持其初始颜色。如果您想更改消息以外的任何内容,则只需log_format在示例中将颜色代码传递给即可。

While the other solutions seem fine they have some issues. Some do colour the whole lines which some times is not wanted and some omit any configuration you might have all together. The solution below doesn’t affect anything but the message itself.

Code

class ColoredFormatter(logging.Formatter):
    def format(self, record):
        if record.levelno == logging.WARNING:
            record.msg = '\033[93m%s\033[0m' % record.msg
        elif record.levelno == logging.ERROR:
            record.msg = '\033[91m%s\033[0m' % record.msg
        return logging.Formatter.format(self, record)

Example

logger = logging.getLogger('mylogger')
handler = logging.StreamHandler()

log_format = '[%(asctime)s]:%(levelname)-7s:%(message)s'
time_format = '%H:%M:%S'
formatter = ColoredFormatter(log_format, datefmt=time_format)
handler.setFormatter(formatter)
logger.addHandler(handler)

logger.warn('this should be yellow')
logger.error('this should be red')

Output

[17:01:36]:WARNING:this should be yellow
[17:01:37]:ERROR  :this should be red

As you see, everything else still gets outputted and remain in their initial color. If you want to change anything else than the message you can simply pass the color codes to log_format in the example.


回答 19

我要添加两个提交,其中一个仅使消息着色(ColoredFormatter),而另一个使整个行着色(ColorizingStreamHandler)。与以前的解决方案相比,它们还包含更多的ANSI颜色代码。

一些内容来自(经过修改):上面的帖子,以及http://plumberjack.blogspot.com/2010/12/colorizing-logging-output-in-terminals.html

仅使消息着色:

class ColoredFormatter(logging.Formatter):
    """Special custom formatter for colorizing log messages!"""

    BLACK = '\033[0;30m'
    RED = '\033[0;31m'
    GREEN = '\033[0;32m'
    BROWN = '\033[0;33m'
    BLUE = '\033[0;34m'
    PURPLE = '\033[0;35m'
    CYAN = '\033[0;36m'
    GREY = '\033[0;37m'

    DARK_GREY = '\033[1;30m'
    LIGHT_RED = '\033[1;31m'
    LIGHT_GREEN = '\033[1;32m'
    YELLOW = '\033[1;33m'
    LIGHT_BLUE = '\033[1;34m'
    LIGHT_PURPLE = '\033[1;35m'
    LIGHT_CYAN = '\033[1;36m'
    WHITE = '\033[1;37m'

    RESET = "\033[0m"

    def __init__(self, *args, **kwargs):
        self._colors = {logging.DEBUG: self.DARK_GREY,
                        logging.INFO: self.RESET,
                        logging.WARNING: self.BROWN,
                        logging.ERROR: self.RED,
                        logging.CRITICAL: self.LIGHT_RED}
        super(ColoredFormatter, self).__init__(*args, **kwargs)

    def format(self, record):
        """Applies the color formats"""
        record.msg = self._colors[record.levelno] + record.msg + self.RESET
        return logging.Formatter.format(self, record)

    def setLevelColor(self, logging_level, escaped_ansi_code):
        self._colors[logging_level] = escaped_ansi_code

使整个行着色:

class ColorizingStreamHandler(logging.StreamHandler):

    BLACK = '\033[0;30m'
    RED = '\033[0;31m'
    GREEN = '\033[0;32m'
    BROWN = '\033[0;33m'
    BLUE = '\033[0;34m'
    PURPLE = '\033[0;35m'
    CYAN = '\033[0;36m'
    GREY = '\033[0;37m'

    DARK_GREY = '\033[1;30m'
    LIGHT_RED = '\033[1;31m'
    LIGHT_GREEN = '\033[1;32m'
    YELLOW = '\033[1;33m'
    LIGHT_BLUE = '\033[1;34m'
    LIGHT_PURPLE = '\033[1;35m'
    LIGHT_CYAN = '\033[1;36m'
    WHITE = '\033[1;37m'

    RESET = "\033[0m"

    def __init__(self, *args, **kwargs):
        self._colors = {logging.DEBUG: self.DARK_GREY,
                        logging.INFO: self.RESET,
                        logging.WARNING: self.BROWN,
                        logging.ERROR: self.RED,
                        logging.CRITICAL: self.LIGHT_RED}
        super(ColorizingStreamHandler, self).__init__(*args, **kwargs)

    @property
    def is_tty(self):
        isatty = getattr(self.stream, 'isatty', None)
        return isatty and isatty()

    def emit(self, record):
        try:
            message = self.format(record)
            stream = self.stream
            if not self.is_tty:
                stream.write(message)
            else:
                message = self._colors[record.levelno] + message + self.RESET
                stream.write(message)
            stream.write(getattr(self, 'terminator', '\n'))
            self.flush()
        except (KeyboardInterrupt, SystemExit):
            raise
        except:
            self.handleError(record)

    def setLevelColor(self, logging_level, escaped_ansi_code):
        self._colors[logging_level] = escaped_ansi_code

I have two submissions to add, one of which colorizes just the message (ColoredFormatter), and one of which colorizes the entire line (ColorizingStreamHandler). These also include more ANSI color codes than previous solutions.

Some content has been sourced (with modification) from: The post above, and http://plumberjack.blogspot.com/2010/12/colorizing-logging-output-in-terminals.html.

Colorizes the message only:

class ColoredFormatter(logging.Formatter):
    """Special custom formatter for colorizing log messages!"""

    BLACK = '\033[0;30m'
    RED = '\033[0;31m'
    GREEN = '\033[0;32m'
    BROWN = '\033[0;33m'
    BLUE = '\033[0;34m'
    PURPLE = '\033[0;35m'
    CYAN = '\033[0;36m'
    GREY = '\033[0;37m'

    DARK_GREY = '\033[1;30m'
    LIGHT_RED = '\033[1;31m'
    LIGHT_GREEN = '\033[1;32m'
    YELLOW = '\033[1;33m'
    LIGHT_BLUE = '\033[1;34m'
    LIGHT_PURPLE = '\033[1;35m'
    LIGHT_CYAN = '\033[1;36m'
    WHITE = '\033[1;37m'

    RESET = "\033[0m"

    def __init__(self, *args, **kwargs):
        self._colors = {logging.DEBUG: self.DARK_GREY,
                        logging.INFO: self.RESET,
                        logging.WARNING: self.BROWN,
                        logging.ERROR: self.RED,
                        logging.CRITICAL: self.LIGHT_RED}
        super(ColoredFormatter, self).__init__(*args, **kwargs)

    def format(self, record):
        """Applies the color formats"""
        record.msg = self._colors[record.levelno] + record.msg + self.RESET
        return logging.Formatter.format(self, record)

    def setLevelColor(self, logging_level, escaped_ansi_code):
        self._colors[logging_level] = escaped_ansi_code

Colorizes the whole line:

class ColorizingStreamHandler(logging.StreamHandler):

    BLACK = '\033[0;30m'
    RED = '\033[0;31m'
    GREEN = '\033[0;32m'
    BROWN = '\033[0;33m'
    BLUE = '\033[0;34m'
    PURPLE = '\033[0;35m'
    CYAN = '\033[0;36m'
    GREY = '\033[0;37m'

    DARK_GREY = '\033[1;30m'
    LIGHT_RED = '\033[1;31m'
    LIGHT_GREEN = '\033[1;32m'
    YELLOW = '\033[1;33m'
    LIGHT_BLUE = '\033[1;34m'
    LIGHT_PURPLE = '\033[1;35m'
    LIGHT_CYAN = '\033[1;36m'
    WHITE = '\033[1;37m'

    RESET = "\033[0m"

    def __init__(self, *args, **kwargs):
        self._colors = {logging.DEBUG: self.DARK_GREY,
                        logging.INFO: self.RESET,
                        logging.WARNING: self.BROWN,
                        logging.ERROR: self.RED,
                        logging.CRITICAL: self.LIGHT_RED}
        super(ColorizingStreamHandler, self).__init__(*args, **kwargs)

    @property
    def is_tty(self):
        isatty = getattr(self.stream, 'isatty', None)
        return isatty and isatty()

    def emit(self, record):
        try:
            message = self.format(record)
            stream = self.stream
            if not self.is_tty:
                stream.write(message)
            else:
                message = self._colors[record.levelno] + message + self.RESET
                stream.write(message)
            stream.write(getattr(self, 'terminator', '\n'))
            self.flush()
        except (KeyboardInterrupt, SystemExit):
            raise
        except:
            self.handleError(record)

    def setLevelColor(self, logging_level, escaped_ansi_code):
        self._colors[logging_level] = escaped_ansi_code

回答 20

刚刚回答了同样的类似的问题:Python的| 更改外壳中的文本颜色

这个想法是使用clint库。它支持MAC,Linux和Windows Shell(CLI)。

Just answered the same on similar question: Python | change text color in shell

The idea is to use the clint library. Which has support for MAC, Linux and Windows shells (CLI).


回答 21

这是一个包含颜色代码的枚举:

class TerminalColour:
    """
    Terminal colour formatting codes
    """
    # /programming/287871/print-in-terminal-with-colors
    MAGENTA = '\033[95m'
    BLUE = '\033[94m'
    GREEN = '\033[92m'
    YELLOW = '\033[93m'
    RED = '\033[91m'
    GREY = '\033[0m'  # normal
    WHITE = '\033[1m'  # bright white
    UNDERLINE = '\033[4m'

这可以应用于每个日志级别的名称请注意,这是一个可怕的骇客。

logging.addLevelName(logging.INFO, "{}{}{}".format(TerminalColour.WHITE, logging.getLevelName(logging.INFO), TerminalColour.GREY))
logging.addLevelName(logging.WARNING, "{}{}{}".format(TerminalColour.YELLOW, logging.getLevelName(logging.WARNING), TerminalColour.GREY))
logging.addLevelName(logging.ERROR, "{}{}{}".format(TerminalColour.RED, logging.getLevelName(logging.ERROR), TerminalColour.GREY))
logging.addLevelName(logging.CRITICAL, "{}{}{}".format(TerminalColour.MAGENTA, logging.getLevelName(logging.CRITICAL), .GREY))

请注意,您的日志格式化程序必须包含日志级别的名称

%(levelname)

例如:

    LOGGING = {
...
        'verbose': {
            'format': '%(asctime)s %(levelname)s %(name)s:%(lineno)s %(module)s %(process)d %(thread)d %(message)s'
        },
        'simple': {
            'format': '[%(asctime)s] %(levelname)s %(name)s %(message)s'
        },

This is an Enum containing the colour codes:

class TerminalColour:
    """
    Terminal colour formatting codes
    """
    # https://stackoverflow.com/questions/287871/print-in-terminal-with-colors
    MAGENTA = '\033[95m'
    BLUE = '\033[94m'
    GREEN = '\033[92m'
    YELLOW = '\033[93m'
    RED = '\033[91m'
    GREY = '\033[0m'  # normal
    WHITE = '\033[1m'  # bright white
    UNDERLINE = '\033[4m'

This may be applied to the names of each log level. Be aware that this is a monstrous hack.

logging.addLevelName(logging.INFO, "{}{}{}".format(TerminalColour.WHITE, logging.getLevelName(logging.INFO), TerminalColour.GREY))
logging.addLevelName(logging.WARNING, "{}{}{}".format(TerminalColour.YELLOW, logging.getLevelName(logging.WARNING), TerminalColour.GREY))
logging.addLevelName(logging.ERROR, "{}{}{}".format(TerminalColour.RED, logging.getLevelName(logging.ERROR), TerminalColour.GREY))
logging.addLevelName(logging.CRITICAL, "{}{}{}".format(TerminalColour.MAGENTA, logging.getLevelName(logging.CRITICAL), .GREY))

Note that your log formatter must include the name of the log level

%(levelname)

for example:

    LOGGING = {
...
        'verbose': {
            'format': '%(asctime)s %(levelname)s %(name)s:%(lineno)s %(module)s %(process)d %(thread)d %(message)s'
        },
        'simple': {
            'format': '[%(asctime)s] %(levelname)s %(name)s %(message)s'
        },

回答 22

FriendlyLog是另一种选择。它可以在Linux,Windows和MacOS下与Python 2和3一起使用。

FriendlyLog is another alternative. It works with Python 2 & 3 under Linux, Windows and MacOS.


回答 23

除了按级别着色外,高亮显示日志消息参数时还应使用交替的颜色吗?我最近为此编写了简单的代码。另一个优点是,日志调用是使用Python 3大括号样式的格式进行的。("{}")。

在这里查看最新的代码和示例:https : //github.com/davidohana/colargulog

样本记录代码:

root_logger = logging.getLogger()
console_handler = logging.StreamHandler(stream=sys.stdout)
console_format = "%(asctime)s - %(levelname)-8s - %(name)-25s - %(message)s"
colored_formatter = ColorizedArgsFormatter(console_format)
console_handler.setFormatter(colored_formatter)
root_logger.addHandler(console_handler)

logger = logging.getLogger(__name__)
logger.info("Hello World")
logger.info("Request from {} handled in {:.3f} ms", socket.gethostname(), 11)
logger.info("Request from {} handled in {:.3f} ms", "127.0.0.1", 33.1)
logger.info("My favorite drinks are {}, {}, {}, {}", "milk", "wine", "tea", "beer")
logger.debug("this is a {} message", logging.getLevelName(logging.DEBUG))
logger.info("this is a {} message", logging.getLevelName(logging.INFO))
logger.warning("this is a {} message", logging.getLevelName(logging.WARNING))
logger.error("this is a {} message", logging.getLevelName(logging.ERROR))
logger.critical("this is a {} message", logging.getLevelName(logging.CRITICAL))
logger.info("Does old-style formatting also work? %s it is, but no colors (yet)", True)

输出:

在此处输入图片说明

实现方式:

"""
colargulog - Python3 Logging with Colored Arguments and new string formatting style

Written by david.ohana@ibm.com
License: Apache-2.0
"""

import logging
import logging.handlers
import re


class ColorCodes:
    grey = "\x1b[38;21m"
    green = "\x1b[1;32m"
    yellow = "\x1b[33;21m"
    red = "\x1b[31;21m"
    bold_red = "\x1b[31;1m"
    blue = "\x1b[1;34m"
    light_blue = "\x1b[1;36m"
    purple = "\x1b[1;35m"
    reset = "\x1b[0m"


class ColorizedArgsFormatter(logging.Formatter):
    arg_colors = [ColorCodes.purple, ColorCodes.light_blue]
    level_fields = ["levelname", "levelno"]
    level_to_color = {
        logging.DEBUG: ColorCodes.grey,
        logging.INFO: ColorCodes.green,
        logging.WARNING: ColorCodes.yellow,
        logging.ERROR: ColorCodes.red,
        logging.CRITICAL: ColorCodes.bold_red,
    }

    def __init__(self, fmt: str):
        super().__init__()
        self.level_to_formatter = {}

        def add_color_format(level: int):
            color = ColorizedArgsFormatter.level_to_color[level]
            _format = fmt
            for fld in ColorizedArgsFormatter.level_fields:
                search = "(%\(" + fld + "\).*?s)"
                _format = re.sub(search, f"{color}\\1{ColorCodes.reset}", _format)
            formatter = logging.Formatter(_format)
            self.level_to_formatter[level] = formatter

        add_color_format(logging.DEBUG)
        add_color_format(logging.INFO)
        add_color_format(logging.WARNING)
        add_color_format(logging.ERROR)
        add_color_format(logging.CRITICAL)

    @staticmethod
    def rewrite_record(record: logging.LogRecord):
        if not BraceFormatStyleFormatter.is_brace_format_style(record):
            return

        msg = record.msg
        msg = msg.replace("{", "_{{")
        msg = msg.replace("}", "_}}")
        placeholder_count = 0
        # add ANSI escape code for next alternating color before each formatting parameter
        # and reset color after it.
        while True:
            if "_{{" not in msg:
                break
            color_index = placeholder_count % len(ColorizedArgsFormatter.arg_colors)
            color = ColorizedArgsFormatter.arg_colors[color_index]
            msg = msg.replace("_{{", color + "{", 1)
            msg = msg.replace("_}}", "}" + ColorCodes.reset, 1)
            placeholder_count += 1

        record.msg = msg.format(*record.args)
        record.args = []

    def format(self, record):
        orig_msg = record.msg
        orig_args = record.args
        formatter = self.level_to_formatter.get(record.levelno)
        self.rewrite_record(record)
        formatted = formatter.format(record)

        # restore log record to original state for other handlers
        record.msg = orig_msg
        record.args = orig_args
        return formatted


class BraceFormatStyleFormatter(logging.Formatter):
    def __init__(self, fmt: str):
        super().__init__()
        self.formatter = logging.Formatter(fmt)

    @staticmethod
    def is_brace_format_style(record: logging.LogRecord):
        if len(record.args) == 0:
            return False

        msg = record.msg
        if '%' in msg:
            return False

        count_of_start_param = msg.count("{")
        count_of_end_param = msg.count("}")

        if count_of_start_param != count_of_end_param:
            return False

        if count_of_start_param != len(record.args):
            return False

        return True

    @staticmethod
    def rewrite_record(record: logging.LogRecord):
        if not BraceFormatStyleFormatter.is_brace_format_style(record):
            return

        record.msg = record.msg.format(*record.args)
        record.args = []

    def format(self, record):
        orig_msg = record.msg
        orig_args = record.args
        self.rewrite_record(record)
        formatted = self.formatter.format(record)

        # restore log record to original state for other handlers
        record.msg = orig_msg
        record.args = orig_args
        return formatted

What about highlighting also log message arguments with alternating colors, in addition to coloring by level? I recently wrote simple code for that. Another advantage is that log call is made with Python 3 brace-style formatting. ("{}").

See latest code and examples here: https://github.com/davidohana/colargulog

Sample Logging code:

root_logger = logging.getLogger()
console_handler = logging.StreamHandler(stream=sys.stdout)
console_format = "%(asctime)s - %(levelname)-8s - %(name)-25s - %(message)s"
colored_formatter = ColorizedArgsFormatter(console_format)
console_handler.setFormatter(colored_formatter)
root_logger.addHandler(console_handler)

logger = logging.getLogger(__name__)
logger.info("Hello World")
logger.info("Request from {} handled in {:.3f} ms", socket.gethostname(), 11)
logger.info("Request from {} handled in {:.3f} ms", "127.0.0.1", 33.1)
logger.info("My favorite drinks are {}, {}, {}, {}", "milk", "wine", "tea", "beer")
logger.debug("this is a {} message", logging.getLevelName(logging.DEBUG))
logger.info("this is a {} message", logging.getLevelName(logging.INFO))
logger.warning("this is a {} message", logging.getLevelName(logging.WARNING))
logger.error("this is a {} message", logging.getLevelName(logging.ERROR))
logger.critical("this is a {} message", logging.getLevelName(logging.CRITICAL))
logger.info("Does old-style formatting also work? %s it is, but no colors (yet)", True)

Output:

enter image description here

Implementation:

"""
colargulog - Python3 Logging with Colored Arguments and new string formatting style

Written by david.ohana@ibm.com
License: Apache-2.0
"""

import logging
import logging.handlers
import re


class ColorCodes:
    grey = "\x1b[38;21m"
    green = "\x1b[1;32m"
    yellow = "\x1b[33;21m"
    red = "\x1b[31;21m"
    bold_red = "\x1b[31;1m"
    blue = "\x1b[1;34m"
    light_blue = "\x1b[1;36m"
    purple = "\x1b[1;35m"
    reset = "\x1b[0m"


class ColorizedArgsFormatter(logging.Formatter):
    arg_colors = [ColorCodes.purple, ColorCodes.light_blue]
    level_fields = ["levelname", "levelno"]
    level_to_color = {
        logging.DEBUG: ColorCodes.grey,
        logging.INFO: ColorCodes.green,
        logging.WARNING: ColorCodes.yellow,
        logging.ERROR: ColorCodes.red,
        logging.CRITICAL: ColorCodes.bold_red,
    }

    def __init__(self, fmt: str):
        super().__init__()
        self.level_to_formatter = {}

        def add_color_format(level: int):
            color = ColorizedArgsFormatter.level_to_color[level]
            _format = fmt
            for fld in ColorizedArgsFormatter.level_fields:
                search = "(%\(" + fld + "\).*?s)"
                _format = re.sub(search, f"{color}\\1{ColorCodes.reset}", _format)
            formatter = logging.Formatter(_format)
            self.level_to_formatter[level] = formatter

        add_color_format(logging.DEBUG)
        add_color_format(logging.INFO)
        add_color_format(logging.WARNING)
        add_color_format(logging.ERROR)
        add_color_format(logging.CRITICAL)

    @staticmethod
    def rewrite_record(record: logging.LogRecord):
        if not BraceFormatStyleFormatter.is_brace_format_style(record):
            return

        msg = record.msg
        msg = msg.replace("{", "_{{")
        msg = msg.replace("}", "_}}")
        placeholder_count = 0
        # add ANSI escape code for next alternating color before each formatting parameter
        # and reset color after it.
        while True:
            if "_{{" not in msg:
                break
            color_index = placeholder_count % len(ColorizedArgsFormatter.arg_colors)
            color = ColorizedArgsFormatter.arg_colors[color_index]
            msg = msg.replace("_{{", color + "{", 1)
            msg = msg.replace("_}}", "}" + ColorCodes.reset, 1)
            placeholder_count += 1

        record.msg = msg.format(*record.args)
        record.args = []

    def format(self, record):
        orig_msg = record.msg
        orig_args = record.args
        formatter = self.level_to_formatter.get(record.levelno)
        self.rewrite_record(record)
        formatted = formatter.format(record)

        # restore log record to original state for other handlers
        record.msg = orig_msg
        record.args = orig_args
        return formatted


class BraceFormatStyleFormatter(logging.Formatter):
    def __init__(self, fmt: str):
        super().__init__()
        self.formatter = logging.Formatter(fmt)

    @staticmethod
    def is_brace_format_style(record: logging.LogRecord):
        if len(record.args) == 0:
            return False

        msg = record.msg
        if '%' in msg:
            return False

        count_of_start_param = msg.count("{")
        count_of_end_param = msg.count("}")

        if count_of_start_param != count_of_end_param:
            return False

        if count_of_start_param != len(record.args):
            return False

        return True

    @staticmethod
    def rewrite_record(record: logging.LogRecord):
        if not BraceFormatStyleFormatter.is_brace_format_style(record):
            return

        record.msg = record.msg.format(*record.args)
        record.args = []

    def format(self, record):
        orig_msg = record.msg
        orig_args = record.args
        self.rewrite_record(record)
        formatted = self.formatter.format(record)

        # restore log record to original state for other handlers
        record.msg = orig_msg
        record.args = orig_args
        return formatted

回答 24

使用pyfancy

例:

print(pyfancy.RED + "Hello Red!" + pyfancy.END)

Use pyfancy.

Example:

print(pyfancy.RED + "Hello Red!" + pyfancy.END)

回答 25

另一个解决方案,使用ZetaSyanthis的颜色:

def config_log(log_level):

    def set_color(level, code):
        level_fmt = "\033[1;" + str(code) + "m%s\033[1;0m" 
        logging.addLevelName( level, level_fmt % logging.getLevelName(level) )

    std_stream = sys.stdout
    isatty = getattr(std_stream, 'isatty', None)
    if isatty and isatty():
        levels = [logging.DEBUG, logging.CRITICAL, logging.WARNING, logging.ERROR]
        for idx, level in enumerate(levels):
            set_color(level, 30 + idx )
        set_color(logging.DEBUG, 0)
    logging.basicConfig(stream=std_stream, level=log_level)

从您的__main__函数调用一次。我在那里有这样的东西:

options, arguments = p.parse_args()
log_level = logging.DEBUG if options.verbose else logging.WARNING
config_log(log_level)

它还会验证输出是否为控制台,否则不使用任何颜色。

Just another solution, with the colors of ZetaSyanthis:

def config_log(log_level):

    def set_color(level, code):
        level_fmt = "\033[1;" + str(code) + "m%s\033[1;0m" 
        logging.addLevelName( level, level_fmt % logging.getLevelName(level) )

    std_stream = sys.stdout
    isatty = getattr(std_stream, 'isatty', None)
    if isatty and isatty():
        levels = [logging.DEBUG, logging.CRITICAL, logging.WARNING, logging.ERROR]
        for idx, level in enumerate(levels):
            set_color(level, 30 + idx )
        set_color(logging.DEBUG, 0)
    logging.basicConfig(stream=std_stream, level=log_level)

call it once from your __main__ function. I have something like this there:

options, arguments = p.parse_args()
log_level = logging.DEBUG if options.verbose else logging.WARNING
config_log(log_level)

it also verifies that the output is a console, otherwise no colors are used.


回答 26

import logging

logging.basicConfig(filename="f.log" filemode='w', level=logging.INFO,
                    format = "%(logger_name)s %(color)s  %(message)s %(endColor)s")


class Logger(object):
    __GREEN = "\033[92m"
    __RED = '\033[91m'
    __ENDC = '\033[0m'

    def __init__(self, name):
        self.logger = logging.getLogger(name)
        self.extra={'logger_name': name, 'endColor': self.__ENDC, 'color': self.__GREEN}


    def info(self, msg):
        self.extra['color'] = self.__GREEN
        self.logger.info(msg, extra=self.extra)

    def error(self, msg):
        self.extra['color'] = self.__RED
        self.logger.error(msg, extra=self.extra)

用法

Logger("File Name").info("This shows green text")

import logging

logging.basicConfig(filename="f.log" filemode='w', level=logging.INFO,
                    format = "%(logger_name)s %(color)s  %(message)s %(endColor)s")


class Logger(object):
    __GREEN = "\033[92m"
    __RED = '\033[91m'
    __ENDC = '\033[0m'

    def __init__(self, name):
        self.logger = logging.getLogger(name)
        self.extra={'logger_name': name, 'endColor': self.__ENDC, 'color': self.__GREEN}


    def info(self, msg):
        self.extra['color'] = self.__GREEN
        self.logger.info(msg, extra=self.extra)

    def error(self, msg):
        self.extra['color'] = self.__RED
        self.logger.error(msg, extra=self.extra)

Usage

Logger("File Name").info("This shows green text")


回答 27

以下解决方案仅适用于python 3,但对我而言,它看起来最为清晰。

想法是使用日志记录工厂向日志记录对象添加“彩色”属性,然后以日志格式使用这些“彩色”属性。

import logging
logger = logging.getLogger(__name__)

def configure_logging(level):

    # add 'levelname_c' attribute to log resords
    orig_record_factory = logging.getLogRecordFactory()
    log_colors = {
        logging.DEBUG:     "\033[1;34m",  # blue
        logging.INFO:      "\033[1;32m",  # green
        logging.WARNING:   "\033[1;35m",  # magenta
        logging.ERROR:     "\033[1;31m",  # red
        logging.CRITICAL:  "\033[1;41m",  # red reverted
    }
    def record_factory(*args, **kwargs):
        record = orig_record_factory(*args, **kwargs)
        record.levelname_c = "{}{}{}".format(
            log_colors[record.levelno], record.levelname, "\033[0m")
        return record

    logging.setLogRecordFactory(record_factory)

    # now each log record object would contain 'levelname_c' attribute
    # and you can use this attribute when configuring logging using your favorite
    # method.
    # for demo purposes I configure stderr log right here

    formatter_c = logging.Formatter("[%(asctime)s] %(levelname_c)s:%(name)s:%(message)s")

    stderr_handler = logging.StreamHandler()
    stderr_handler.setLevel(level)
    stderr_handler.setFormatter(formatter_c)

    root_logger = logging.getLogger('')
    root_logger.setLevel(logging.DEBUG)
    root_logger.addHandler(stderr_handler)


def main():
    configure_logging(logging.DEBUG)

    logger.debug("debug message")
    logger.info("info message")
    logger.critical("something unusual happened")


if __name__ == '__main__':
    main()

您可以轻松地修改此示例以创建其他彩色属性(fe message_c),然后使用这些属性在需要的位置获取彩色文本(仅)。

(我最近发现的一个有用的技巧:我有一个带有彩色调试日志的文件,每当我想临时增加应用程序的日志级别时,我只是tail -f在不同终端中的日志文件,并在屏幕上看到调试日志,而无需更改任何配置并重新启动应用程序)

The following solution works with python 3 only, but for me it looks most clear.

The idea is to use log record factory to add ‘colored’ attributes to log record objects and than use these ‘colored’ attributes in log format.

import logging
logger = logging.getLogger(__name__)

def configure_logging(level):

    # add 'levelname_c' attribute to log resords
    orig_record_factory = logging.getLogRecordFactory()
    log_colors = {
        logging.DEBUG:     "\033[1;34m",  # blue
        logging.INFO:      "\033[1;32m",  # green
        logging.WARNING:   "\033[1;35m",  # magenta
        logging.ERROR:     "\033[1;31m",  # red
        logging.CRITICAL:  "\033[1;41m",  # red reverted
    }
    def record_factory(*args, **kwargs):
        record = orig_record_factory(*args, **kwargs)
        record.levelname_c = "{}{}{}".format(
            log_colors[record.levelno], record.levelname, "\033[0m")
        return record

    logging.setLogRecordFactory(record_factory)

    # now each log record object would contain 'levelname_c' attribute
    # and you can use this attribute when configuring logging using your favorite
    # method.
    # for demo purposes I configure stderr log right here

    formatter_c = logging.Formatter("[%(asctime)s] %(levelname_c)s:%(name)s:%(message)s")

    stderr_handler = logging.StreamHandler()
    stderr_handler.setLevel(level)
    stderr_handler.setFormatter(formatter_c)

    root_logger = logging.getLogger('')
    root_logger.setLevel(logging.DEBUG)
    root_logger.addHandler(stderr_handler)


def main():
    configure_logging(logging.DEBUG)

    logger.debug("debug message")
    logger.info("info message")
    logger.critical("something unusual happened")


if __name__ == '__main__':
    main()

You can easily modify this example to create other colored attributes (f.e. message_c) and then use these attributes to get colored text (only) where you want.

(handy trick I discovered recently: I have a file with colored debug logs and whenever I want temporary increase the log level of my application I just tail -f the log file in different terminal and see debug logs on screen w/o changing any configuration and restarting application)


回答 28

这是airmind示例的另一个Python3变体。我想要一些其他示例中没有的特定功能

  • 为终端使用颜色,但不要在文件处理程序中写入不可打印的字符(为此我定义了2个格式化程序)
  • 能够覆盖特定日志消息的颜色
  • 从文件(在这种情况下为Yaml)配置记录器

注意:我使用了colorama,但是您可以对其进行修改,因此不是必需的。同样,在我的测试中,我只是运行python文件,所以我的类在模块中。__main__您必须更改(): __main__.ColoredFormatter为任何模块。

pip install colorama pyyaml

logging.yaml

---
version: 1
disable_existing_loggers: False
formatters:
  simple:
    format: "%(threadName)s - %(name)s - %(levelname)s - %(message)s"
  color:
    format: "%(threadName)s - %(name)s - %(levelname)s - %(message)s"
    (): __main__.ColoredFormatter
    use_color: true

handlers:
  console:
    class: logging.StreamHandler
    level: DEBUG
    formatter: color
    stream: ext://sys.stdout

  info_file_handler:
    class: logging.handlers.RotatingFileHandler
    level: INFO
    formatter: simple
    filename: app.log
    maxBytes: 20971520 
    backupCount: 20
    encoding: utf8

  error_file_handler:
    class: logging.handlers.RotatingFileHandler
    level: ERROR
    formatter: simple
    filename: errors.log
    maxBytes: 10485760 
    backupCount: 20
    encoding: utf8

root:
  level: DEBUG
  handlers: [console, info_file_handler, error_file_handler]

main.py

import logging
import logging.config
import os
from logging import Logger

import colorama
import yaml
from colorama import Back, Fore, Style

COLORS = {
    "WARNING": Fore.YELLOW,
    "INFO": Fore.CYAN,
    "DEBUG": Fore.BLUE,
    "CRITICAL": Fore.YELLOW,
    "ERROR": Fore.RED,
}


class ColoredFormatter(logging.Formatter):
    def __init__(self, *, format, use_color):
        logging.Formatter.__init__(self, fmt=format)
        self.use_color = use_color

    def format(self, record):
        msg = super().format(record)
        if self.use_color:
            levelname = record.levelname
            if hasattr(record, "color"):
                return f"{record.color}{msg}{Style.RESET_ALL}"
            if levelname in COLORS:
                return f"{COLORS[levelname]}{msg}{Style.RESET_ALL}"
        return msg


with open("logging.yaml", "rt") as f:
    config = yaml.safe_load(f.read())
    logging.config.dictConfig(config)

logger: Logger = logging.getLogger(__name__)
logger.info("Test INFO", extra={"color": Back.RED})
logger.info("Test INFO", extra={"color": f"{Style.BRIGHT}{Back.RED}"})
logger.info("Test INFO")
logger.debug("Test DEBUG")
logger.warning("Test WARN")

输出:

输出

This is another Python3 variant of airmind’s example. I wanted some specific features I didn’t see in the other examples

  • use colors for the terminal but do not write non-printable characters in the file handlers (I defined 2 formatters for this)
  • ability to override the color for a specific log message
  • configure the logger from a file (yaml in this case)

Notes: I used colorama but you could modify this so it is not required. Also for my testing I was just running python file so my class is in module __main__ You would have to change (): __main__.ColoredFormatter to whatever your module is.

pip install colorama pyyaml

logging.yaml

---
version: 1
disable_existing_loggers: False
formatters:
  simple:
    format: "%(threadName)s - %(name)s - %(levelname)s - %(message)s"
  color:
    format: "%(threadName)s - %(name)s - %(levelname)s - %(message)s"
    (): __main__.ColoredFormatter
    use_color: true

handlers:
  console:
    class: logging.StreamHandler
    level: DEBUG
    formatter: color
    stream: ext://sys.stdout

  info_file_handler:
    class: logging.handlers.RotatingFileHandler
    level: INFO
    formatter: simple
    filename: app.log
    maxBytes: 20971520 
    backupCount: 20
    encoding: utf8

  error_file_handler:
    class: logging.handlers.RotatingFileHandler
    level: ERROR
    formatter: simple
    filename: errors.log
    maxBytes: 10485760 
    backupCount: 20
    encoding: utf8

root:
  level: DEBUG
  handlers: [console, info_file_handler, error_file_handler]

main.py

import logging
import logging.config
import os
from logging import Logger

import colorama
import yaml
from colorama import Back, Fore, Style

COLORS = {
    "WARNING": Fore.YELLOW,
    "INFO": Fore.CYAN,
    "DEBUG": Fore.BLUE,
    "CRITICAL": Fore.YELLOW,
    "ERROR": Fore.RED,
}


class ColoredFormatter(logging.Formatter):
    def __init__(self, *, format, use_color):
        logging.Formatter.__init__(self, fmt=format)
        self.use_color = use_color

    def format(self, record):
        msg = super().format(record)
        if self.use_color:
            levelname = record.levelname
            if hasattr(record, "color"):
                return f"{record.color}{msg}{Style.RESET_ALL}"
            if levelname in COLORS:
                return f"{COLORS[levelname]}{msg}{Style.RESET_ALL}"
        return msg


with open("logging.yaml", "rt") as f:
    config = yaml.safe_load(f.read())
    logging.config.dictConfig(config)

logger: Logger = logging.getLogger(__name__)
logger.info("Test INFO", extra={"color": Back.RED})
logger.info("Test INFO", extra={"color": f"{Style.BRIGHT}{Back.RED}"})
logger.info("Test INFO")
logger.debug("Test DEBUG")
logger.warning("Test WARN")

output:

output


使用Python实现触摸?

问题:使用Python实现触摸?

touch是Unix实用程序,用于将文件的修改和访问时间设置为当前时间。如果文件不存在,则使用默认权限创建。

您如何将其实现为Python函数?尝试跨平台并完善。

(Google当前针对“ python触摸文件”的搜索结果并不理想,但指向os.utime。)

touch is a Unix utility that sets the modification and access times of files to the current time of day. If the file doesn’t exist, it is created with default permissions.

How would you implement it as a Python function? Try to be cross platform and complete.

(Current Google results for “python touch file” are not that great, but point to os.utime.)


回答 0

看起来这是Python 3.4-中的新增功能pathlib

from pathlib import Path

Path('path/to/file.txt').touch()

这将创建一个 file.txt在路径上。

Path.touch(mode = 0o777,exist_ok = True)

在此给定路径下创建一个文件。如果指定了mode,则将其与进程的umask值组合以确定文件模式和访问标志。如果文件已经存在,并且如果exist_ok为true(并且其修改时间已更新为当前时间),则该函数将成功执行,否则将引发FileExistsError。

Looks like this is new as of Python 3.4 – pathlib.

from pathlib import Path

Path('path/to/file.txt').touch()

This will create a file.txt at the path.

Path.touch(mode=0o777, exist_ok=True)

Create a file at this given path. If mode is given, it is combined with the process’ umask value to determine the file mode and access flags. If the file already exists, the function succeeds if exist_ok is true (and its modification time is updated to the current time), otherwise FileExistsError is raised.


回答 1

与其他解决方案相比,这要使比赛更加自由。(该with关键字是Python 2.5中的新增功能。)

import os
def touch(fname, times=None):
    with open(fname, 'a'):
        os.utime(fname, times)

大致相当于这个。

import os
def touch(fname, times=None):
    fhandle = open(fname, 'a')
    try:
        os.utime(fname, times)
    finally:
        fhandle.close()

现在,要使其真正摆脱竞争,您需要使用futimes并更改打开的文件句柄的时间戳,而不是打开文件,然后更改文件名(可能已重命名)的时间戳。不幸的是,Python似乎没有提供一种futimes无需经过ctypes任何类似操作即可调用的方法…


编辑

Nate Parsons所述,Python 3.3将为诸如之类的函数添加 指定文件描述符(when os.supports_fdos.utime,该函数将使用futimessyscall而不是内部的utimessyscall。换一种说法:

import os
def touch(fname, mode=0o666, dir_fd=None, **kwargs):
    flags = os.O_CREAT | os.O_APPEND
    with os.fdopen(os.open(fname, flags=flags, mode=mode, dir_fd=dir_fd)) as f:
        os.utime(f.fileno() if os.utime in os.supports_fd else fname,
            dir_fd=None if os.supports_fd else dir_fd, **kwargs)

This tries to be a little more race-free than the other solutions. (The with keyword is new in Python 2.5.)

import os
def touch(fname, times=None):
    with open(fname, 'a'):
        os.utime(fname, times)

Roughly equivalent to this.

import os
def touch(fname, times=None):
    fhandle = open(fname, 'a')
    try:
        os.utime(fname, times)
    finally:
        fhandle.close()

Now, to really make it race-free, you need to use futimes and change the timestamp of the open filehandle, instead of opening the file and then changing the timestamp on the filename (which may have been renamed). Unfortunately, Python doesn’t seem to provide a way to call futimes without going through ctypes or similar…


EDIT

As noted by Nate Parsons, Python 3.3 will add specifying a file descriptor (when os.supports_fd) to functions such as os.utime, which will use the futimes syscall instead of the utimes syscall under the hood. In other words:

import os
def touch(fname, mode=0o666, dir_fd=None, **kwargs):
    flags = os.O_CREAT | os.O_APPEND
    with os.fdopen(os.open(fname, flags=flags, mode=mode, dir_fd=dir_fd)) as f:
        os.utime(f.fileno() if os.utime in os.supports_fd else fname,
            dir_fd=None if os.supports_fd else dir_fd, **kwargs)

回答 2

def touch(fname):
    if os.path.exists(fname):
        os.utime(fname, None)
    else:
        open(fname, 'a').close()
def touch(fname):
    if os.path.exists(fname):
        os.utime(fname, None)
    else:
        open(fname, 'a').close()

回答 3

为什么不试试呢?:

import os

def touch(fname):
    try:
        os.utime(fname, None)
    except OSError:
        open(fname, 'a').close()

我相信这可以消除任何重要的比赛条件。如果该文件不存在,则将引发异常。

唯一可能的竞争条件是,如果文件是在调用open()之前但在os.utime()之后创建的。但这无关紧要,因为在这种情况下,修改时间将是预期的,因为修改时间必须在调用touch()的过程中发生。

Why not try this?:

import os

def touch(fname):
    try:
        os.utime(fname, None)
    except OSError:
        open(fname, 'a').close()

I believe this eliminates any race condition that matters. If the file does not exist then an exception will be thrown.

The only possible race condition here is if the file is created before open() is called but after os.utime(). But this does not matter because in this case the modification time will be as expected since it must have happened during the call to touch().


回答 4

以下是一些使用ctypes的代码(仅在Linux上经过测试):

from ctypes import *
libc = CDLL("libc.so.6")

#  struct timespec {
#             time_t tv_sec;        /* seconds */
#             long   tv_nsec;       /* nanoseconds */
#         };
# int futimens(int fd, const struct timespec times[2]);

class c_timespec(Structure):
    _fields_ = [('tv_sec', c_long), ('tv_nsec', c_long)]

class c_utimbuf(Structure):
    _fields_ = [('atime', c_timespec), ('mtime', c_timespec)]

utimens = CFUNCTYPE(c_int, c_char_p, POINTER(c_utimbuf))
futimens = CFUNCTYPE(c_int, c_char_p, POINTER(c_utimbuf)) 

# from /usr/include/i386-linux-gnu/bits/stat.h
UTIME_NOW  = ((1l << 30) - 1l)
UTIME_OMIT = ((1l << 30) - 2l)
now  = c_timespec(0,UTIME_NOW)
omit = c_timespec(0,UTIME_OMIT)

# wrappers
def update_atime(fileno):
        assert(isinstance(fileno, int))
        libc.futimens(fileno, byref(c_utimbuf(now, omit)))
def update_mtime(fileno):
        assert(isinstance(fileno, int))
        libc.futimens(fileno, byref(c_utimbuf(omit, now)))

# usage example:
#
# f = open("/tmp/test")
# update_mtime(f.fileno())

Here’s some code that uses ctypes (only tested on Linux):

from ctypes import *
libc = CDLL("libc.so.6")

#  struct timespec {
#             time_t tv_sec;        /* seconds */
#             long   tv_nsec;       /* nanoseconds */
#         };
# int futimens(int fd, const struct timespec times[2]);

class c_timespec(Structure):
    _fields_ = [('tv_sec', c_long), ('tv_nsec', c_long)]

class c_utimbuf(Structure):
    _fields_ = [('atime', c_timespec), ('mtime', c_timespec)]

utimens = CFUNCTYPE(c_int, c_char_p, POINTER(c_utimbuf))
futimens = CFUNCTYPE(c_int, c_char_p, POINTER(c_utimbuf)) 

# from /usr/include/i386-linux-gnu/bits/stat.h
UTIME_NOW  = ((1l << 30) - 1l)
UTIME_OMIT = ((1l << 30) - 2l)
now  = c_timespec(0,UTIME_NOW)
omit = c_timespec(0,UTIME_OMIT)

# wrappers
def update_atime(fileno):
        assert(isinstance(fileno, int))
        libc.futimens(fileno, byref(c_utimbuf(now, omit)))
def update_mtime(fileno):
        assert(isinstance(fileno, int))
        libc.futimens(fileno, byref(c_utimbuf(omit, now)))

# usage example:
#
# f = open("/tmp/test")
# update_mtime(f.fileno())

回答 5

自从发布Python-2.5关键字以来,此答案与所有​​版本兼容with

1.如果不存在则创建文件+设置当前时间
(与command完全相同touch

import os

fname = 'directory/filename.txt'
with open(fname, 'a'):     # Create file if does not exist
    os.utime(fname, None)  # Set access/modified times to now
                           # May raise OSError if file does not exist

一个更强大的版本:

import os

with open(fname, 'a'):
  try:                     # Whatever if file was already existing
    os.utime(fname, None)  # => Set current time anyway
  except OSError:
    pass  # File deleted between open() and os.utime() calls

2.如果不存在,则仅创建文件
(不更新时间)

with open(fname, 'a'):  # Create file if does not exist
    pass

3.只需更新文件访问/修改时间
(如果不存在则不创建文件)

import os

try:
    os.utime(fname, None)  # Set access/modified times to now
except OSError:
    pass  # File does not exist (or no permission)

使用os.path.exists()不会简化代码:

from __future__ import (absolute_import, division, print_function)
import os

if os.path.exists(fname):
  try:
    os.utime(fname, None)  # Set access/modified times to now
  except OSError:
    pass  # File deleted between exists() and utime() calls
          # (or no permission)

奖励:目录中所有文件的更新时间

from __future__ import (absolute_import, division, print_function)
import os

number_of_files = 0

#   Current directory which is "walked through"
#   |     Directories in root
#   |     |  Files in root       Working directory
#   |     |  |                     |
for root, _, filenames in os.walk('.'):
  for fname in filenames:
    pathname = os.path.join(root, fname)
    try:
      os.utime(pathname, None)  # Set access/modified times to now
      number_of_files += 1
    except OSError as why:
      print('Cannot change time of %r because %r', pathname, why)

print('Changed time of %i files', number_of_files)

This answer is compatible with all versions since Python-2.5 when keyword with has been released.

1. Create file if does not exist + Set current time
(exactly same as command touch)

import os

fname = 'directory/filename.txt'
with open(fname, 'a'):     # Create file if does not exist
    os.utime(fname, None)  # Set access/modified times to now
                           # May raise OSError if file does not exist

A more robust version:

import os

with open(fname, 'a'):
  try:                     # Whatever if file was already existing
    os.utime(fname, None)  # => Set current time anyway
  except OSError:
    pass  # File deleted between open() and os.utime() calls

2. Just create the file if does not exist
(does not update time)

with open(fname, 'a'):  # Create file if does not exist
    pass

3. Just update file access/modified times
(does not create file if not existing)

import os

try:
    os.utime(fname, None)  # Set access/modified times to now
except OSError:
    pass  # File does not exist (or no permission)

Using os.path.exists() does not simplify the code:

from __future__ import (absolute_import, division, print_function)
import os

if os.path.exists(fname):
  try:
    os.utime(fname, None)  # Set access/modified times to now
  except OSError:
    pass  # File deleted between exists() and utime() calls
          # (or no permission)

Bonus: Update time of all files in a directory

from __future__ import (absolute_import, division, print_function)
import os

number_of_files = 0

#   Current directory which is "walked through"
#   |     Directories in root
#   |     |  Files in root       Working directory
#   |     |  |                     |
for root, _, filenames in os.walk('.'):
  for fname in filenames:
    pathname = os.path.join(root, fname)
    try:
      os.utime(pathname, None)  # Set access/modified times to now
      number_of_files += 1
    except OSError as why:
      print('Cannot change time of %r because %r', pathname, why)

print('Changed time of %i files', number_of_files)

回答 6

with open(file_name,'a') as f: 
    pass
with open(file_name,'a') as f: 
    pass

回答 7

简单化:

def touch(fname):
    open(fname, 'a').close()
    os.utime(fname, None)
  • open确保有一个文件存在
  • utime该时间戳更新,确保

从理论上讲,有人可能会在之后删除文件open,从而导致utime引发异常。但是可以说是可以的,因为确实发生了一些不好的事情。

Simplistic:

def touch(fname):
    open(fname, 'a').close()
    os.utime(fname, None)
  • The open ensures there is a file there
  • the utime ensures that the timestamps are updated

Theoretically, it’s possible someone will delete the file after the open, causing utime to raise an exception. But arguably that’s OK, since something bad did happen.


回答 8

复杂(可能是越野车):

def utime(fname, atime=None, mtime=None)
    if type(atime) is tuple:
        atime, mtime = atime

    if atime is None or mtime is None:
        statinfo = os.stat(fname)
        if atime is None:
            atime = statinfo.st_atime
        if mtime is None:
            mtime = statinfo.st_mtime

    os.utime(fname, (atime, mtime))


def touch(fname, atime=None, mtime=None):
    if type(atime) is tuple:
        atime, mtime = atime

    open(fname, 'a').close()
    utime(fname, atime, mtime)

这也尝试允许设置访问或修改时间,例如GNU touch。

Complex (possibly buggy):

def utime(fname, atime=None, mtime=None)
    if type(atime) is tuple:
        atime, mtime = atime

    if atime is None or mtime is None:
        statinfo = os.stat(fname)
        if atime is None:
            atime = statinfo.st_atime
        if mtime is None:
            mtime = statinfo.st_mtime

    os.utime(fname, (atime, mtime))


def touch(fname, atime=None, mtime=None):
    if type(atime) is tuple:
        atime, mtime = atime

    open(fname, 'a').close()
    utime(fname, atime, mtime)

This tries to also allow setting the access or modification time, like GNU touch.


回答 9

用所需的变量创建一个字符串并将其传递给os.system似乎是合乎逻辑的:

touch = 'touch ' + dir + '/' + fileName
os.system(touch)

这在许多方面都是不够的(例如,它不处理空格),因此请不要这样做。

一种更可靠的方法是使用子过程:

subprocess.call(['touch', os.path.join(dirname, fileName)])

尽管这比使用子shell(使用os.system)要好得多,但它仍然仅适用于快捷脚本。将接受的答案用于跨平台程序。

It might seem logical to create a string with the desired variables, and pass it to os.system:

touch = 'touch ' + dir + '/' + fileName
os.system(touch)

This is inadequate in a number of ways (e.g.,it doesn’t handle whitespace), so don’t do it.

A more robust method is to use subprocess :

subprocess.call(['touch', os.path.join(dirname, fileName)])

While this is much better than using a subshell (with os.system), it is still only suitable for quick-and-dirty scripts; use the accepted answer for cross-platform programs.


回答 10

“ open(file_name,’a’)。close()”在Windows上的Python 2.7中对我不起作用。“ os.utime(file_name,None)”工作得很好。

另外,我还需要递归地触摸目录中某个日期早于某个日期的所有文件。我根据ephemient的非常有用的回复创建了以下关注者。

def touch(file_name):
    # Update the modified timestamp of a file to now.
    if not os.path.exists(file_name):
        return
    try:
        os.utime(file_name, None)
    except Exception:
        open(file_name, 'a').close()

def midas_touch(root_path, older_than=dt.now(), pattern='**', recursive=False):
    '''
    midas_touch updates the modified timestamp of a file or files in a 
                directory (folder)

    Arguements:
        root_path (str): file name or folder name of file-like object to touch
        older_than (datetime): only touch files with datetime older than this 
                   datetime
        pattern (str): filter files with this pattern (ignored if root_path is
                a single file)
        recursive (boolean): search sub-diretories (ignored if root_path is a 
                  single file)
    '''
    # if root_path NOT exist, exit
    if not os.path.exists(root_path):
        return
    # if root_path DOES exist, continue.
    else:
        # if root_path is a directory, touch all files in root_path
        if os.path.isdir(root_path):
            # get a directory list (list of files in directory)
            dir_list=find_files(root_path, pattern='**', recursive=False)
            # loop through list of files
            for f in dir_list:
                # if the file modified date is older thatn older_than, touch the file
                if dt.fromtimestamp(os.path.getmtime(f)) < older_than:
                    touch(f)
                    print "Touched ", f
        # if root_path is a file, touch the file
        else:
            # if the file modified date is older thatn older_than, touch the file
            if dt.fromtimestamp(os.path.getmtime(f)) < older_than:
                touch(root_path)

“open(file_name, ‘a’).close()” did not work for me in Python 2.7 on Windows. “os.utime(file_name, None)” worked just fine.

Also, I had a need to recursively touch all files in a directory with a date older than some date. I created hte following based on ephemient’s very helpful response.

def touch(file_name):
    # Update the modified timestamp of a file to now.
    if not os.path.exists(file_name):
        return
    try:
        os.utime(file_name, None)
    except Exception:
        open(file_name, 'a').close()

def midas_touch(root_path, older_than=dt.now(), pattern='**', recursive=False):
    '''
    midas_touch updates the modified timestamp of a file or files in a 
                directory (folder)

    Arguements:
        root_path (str): file name or folder name of file-like object to touch
        older_than (datetime): only touch files with datetime older than this 
                   datetime
        pattern (str): filter files with this pattern (ignored if root_path is
                a single file)
        recursive (boolean): search sub-diretories (ignored if root_path is a 
                  single file)
    '''
    # if root_path NOT exist, exit
    if not os.path.exists(root_path):
        return
    # if root_path DOES exist, continue.
    else:
        # if root_path is a directory, touch all files in root_path
        if os.path.isdir(root_path):
            # get a directory list (list of files in directory)
            dir_list=find_files(root_path, pattern='**', recursive=False)
            # loop through list of files
            for f in dir_list:
                # if the file modified date is older thatn older_than, touch the file
                if dt.fromtimestamp(os.path.getmtime(f)) < older_than:
                    touch(f)
                    print "Touched ", f
        # if root_path is a file, touch the file
        else:
            # if the file modified date is older thatn older_than, touch the file
            if dt.fromtimestamp(os.path.getmtime(f)) < older_than:
                touch(root_path)

回答 11

你为什么不尝试:newfile.py

#!/usr/bin/env python
import sys
inputfile = sys.argv[1]

with open(inputfile, 'w') as file:
    pass

python newfile.py foobar.txt

要么

使用子过程:

import subprocess
subprocess.call(["touch", "barfoo.txt"])

Why don’t you try: newfile.py

#!/usr/bin/env python
import sys
inputfile = sys.argv[1]

with open(inputfile, 'w') as file:
    pass

python newfile.py foobar.txt

or

use subprocess:

import subprocess
subprocess.call(["touch", "barfoo.txt"])

回答 12

以下内容就足够了:

import os
def func(filename):
    if os.path.exists(filename):
        os.utime(filename)
    else:
        with open(filename,'a') as f:
            pass

如果要设置触摸的特定时间,请按以下方式使用os.utime:

os.utime(filename,(atime,mtime))

在这里,atime和mtime都应为int / float,并且应等于以秒为单位的纪元时间到您要设置的时间。

The following is sufficient:

import os
def func(filename):
    if os.path.exists(filename):
        os.utime(filename)
    else:
        with open(filename,'a') as f:
            pass

If you want to set a specific time for touch, use os.utime as follows:

os.utime(filename,(atime,mtime))

Here, atime and mtime both should be int/float and should be equal to epoch time in seconds to the time which you want to set.


回答 13

如果您不介意尝试,那么…

def touch_dir(folder_path):
    try:
        os.mkdir(folder_path)
    except FileExistsError:
        pass

不过要注意的一件事是,如果存在一个具有相同名称的文件,则该文件将无法工作,并且将以静默方式失败。

If you don’t mind the try-except then…

def touch_dir(folder_path):
    try:
        os.mkdir(folder_path)
    except FileExistsError:
        pass

One thing to note though, if a file exists with the same name then it won’t work and will fail silently.


回答 14

write_text()pathlib.Path可以使用。

>>> from pathlib import Path
>>> Path('aa.txt').write_text("")
0

write_text() from pathlib.Path can be used.

>>> from pathlib import Path
>>> Path('aa.txt').write_text("")
0