分类目录归档:知识问答

Numpy的array()和asarray()函数有什么区别?

问题:Numpy的array()和asarray()函数有什么区别?

Numpy array()asarray()函数之间有什么区别?什么时候应该使用一个而不是另一个?他们似乎为我能想到的所有输入生成相同的输出。

What is the difference between Numpy’s array() and asarray() functions? When should you use one rather than the other? They seem to generate identical output for all the inputs I can think of.


回答 0

由于将其他问题重定向到这个询问问题asanyarray其他数组创建例程的问题,因此可能有必要简要概述每个问题的作法。

区别主要在于何时返回不变的输入,而不是将新数组作为副本。

array提供了多种选择(大多数其他功能都是围绕它的薄包装器),包括用于确定何时复制的标志。完整的解释将和文档一样长(请参阅Array Creation,但是简要地,这里有一些示例:

假设andarray,并且mmatrix,并且它们都具有dtypefloat32

  • np.array(a)并且np.array(m)将复制两个,因为这是默认行为。
  • np.array(a, copy=False)并且np.array(m, copy=False)将复制m但不复制a,因为m不是ndarray
  • np.array(a, copy=False, subok=True),并且np.array(m, copy=False, subok=True)不会复制任何内容,因为mmatrix,这是的子类ndarray
  • np.array(a, dtype=int, copy=False, subok=True)将同时复制两者,因为与dtype不兼容。

其他大多数功能都是array在复制发生时围绕该控件的薄包装器:

  • asarray:如果兼容ndarraycopy=False),则输入将返回未复制的状态。
  • asanyarray:如果输入是兼容的ndarray或子类matrix(如copy=Falsesubok=True),则输入将不被复制。
  • ascontiguousarray:如果输入是兼容ndarray的连续C顺序(copy=False,,则将返回未复制的输入order='C')
  • asfortranarray:如果输入与ndarray连续的Fortran顺序(copy=Falseorder='F')兼容,则将返回未复制的输入。
  • require:如果输入与指定的需求字符串兼容,则输入将不复制而返回。
  • copy:总是复制输入。
  • fromiter:输入被视为可迭代的(例如,您可以从迭代器的元素构造数组,而不是object使用迭代器的数组);始终复制。

还有一些便利功能,例如asarray_chkfinite(与复制规则相同asarray,但复制规则与相同,但是ValueError如果有naninf值,则会提高),以及子类的构造函数(例如matrix或特殊情况下的记录数组),当然还有实际的ndarray构造函数(可让您直接创建数组)超出缓冲区)。

Since other questions are being redirected to this one which ask about asanyarray or other array creation routines, it’s probably worth having a brief summary of what each of them does.

The differences are mainly about when to return the input unchanged, as opposed to making a new array as a copy.

array offers a wide variety of options (most of the other functions are thin wrappers around it), including flags to determine when to copy. A full explanation would take just as long as the docs (see Array Creation, but briefly, here are some examples:

Assume a is an ndarray, and m is a matrix, and they both have a dtype of float32:

  • np.array(a) and np.array(m) will copy both, because that’s the default behavior.
  • np.array(a, copy=False) and np.array(m, copy=False) will copy m but not a, because m is not an ndarray.
  • np.array(a, copy=False, subok=True) and np.array(m, copy=False, subok=True) will copy neither, because m is a matrix, which is a subclass of ndarray.
  • np.array(a, dtype=int, copy=False, subok=True) will copy both, because the dtype is not compatible.

Most of the other functions are thin wrappers around array that control when copying happens:

  • asarray: The input will be returned uncopied iff it’s a compatible ndarray (copy=False).
  • asanyarray: The input will be returned uncopied iff it’s a compatible ndarray or subclass like matrix (copy=False, subok=True).
  • ascontiguousarray: The input will be returned uncopied iff it’s a compatible ndarray in contiguous C order (copy=False, order='C').
  • asfortranarray: The input will be returned uncopied iff it’s a compatible ndarray in contiguous Fortran order (copy=False, order='F').
  • require: The input will be returned uncopied iff it’s compatible with the specified requirements string.
  • copy: The input is always copied.
  • fromiter: The input is treated as an iterable (so, e.g., you can construct an array from an iterator’s elements, instead of an object array with the iterator); always copied.

There are also convenience functions, like asarray_chkfinite (same copying rules as asarray, but raises ValueError if there are any nan or inf values), and constructors for subclasses like matrix or for special cases like record arrays, and of course the actual ndarray constructor (which lets you create an array directly out of strides over a buffer).


回答 1

定义asarray是:

def asarray(a, dtype=None, order=None):
    return array(a, dtype, copy=False, order=order)

就像array,除了它的选项更少,和copy=Falsearraycopy=True默认。

主要区别在于array(默认情况下)将复制对象,而asarray除非有必要,否则不会复制。

The definition of asarray is:

def asarray(a, dtype=None, order=None):
    return array(a, dtype, copy=False, order=order)

So it is like array, except it has fewer options, and copy=False. array has copy=True by default.

The main difference is that array (by default) will make a copy of the object, while asarray will not unless necessary.


回答 2

可以通过以下示例证明差异:

  1. 产生矩阵

    >>> A = numpy.matrix(numpy.ones((3,3)))
    >>> A
    matrix([[ 1.,  1.,  1.],
            [ 1.,  1.,  1.],
            [ 1.,  1.,  1.]])
  2. 用于numpy.array修改A。不起作用,因为您正在修改副本

    >>> numpy.array(A)[2]=2
    >>> A
    matrix([[ 1.,  1.,  1.],
            [ 1.,  1.,  1.],
            [ 1.,  1.,  1.]])
  3. 用于numpy.asarray修改A。之所以有效,是因为您正在修改A自己

    >>> numpy.asarray(A)[2]=2
    >>> A
    matrix([[ 1.,  1.,  1.],
            [ 1.,  1.,  1.],
            [ 2.,  2.,  2.]])

希望这可以帮助!

The difference can be demonstrated by this example:

  1. generate a matrix

    >>> A = numpy.matrix(numpy.ones((3,3)))
    >>> A
    matrix([[ 1.,  1.,  1.],
            [ 1.,  1.,  1.],
            [ 1.,  1.,  1.]])
    
  2. use numpy.array to modify A. Doesn’t work because you are modifying a copy

    >>> numpy.array(A)[2]=2
    >>> A
    matrix([[ 1.,  1.,  1.],
            [ 1.,  1.,  1.],
            [ 1.,  1.,  1.]])
    
  3. use numpy.asarray to modify A. It worked because you are modifying A itself

    >>> numpy.asarray(A)[2]=2
    >>> A
    matrix([[ 1.,  1.,  1.],
            [ 1.,  1.,  1.],
            [ 2.,  2.,  2.]])
    

Hope this helps!


回答 3

array和的文档中非常清楚地提到了差异asarray。不同之处在于参数列表,因此取决于这些参数的功能作用。

函数定义为:

numpy.array(object, dtype=None, copy=True, order=None, subok=False, ndmin=0)

numpy.asarray(a, dtype=None, order=None)

以下参数是可能传递给文档的参数,array不是 asarray文档中提到的参数:

copy:bool,可选如果为true(默认),则复制对象。否则,仅当__array__返回一个副本,obj是一个嵌套序列或需要一个副本以满足其他任何要求(dtype,order等)时,才创建副本。

subok:bool,可选如果为True,则子类将被传递,否则返回的数组将被强制为基类数组(默认)。

ndmin:int,可选指定结果数组应具有的最小维数。可以根据需要预先添加形状。

The differences are mentioned quite clearly in the documentation of array and asarray. The differences lie in the argument list and hence the action of the function depending on those parameters.

The function definitions are :

numpy.array(object, dtype=None, copy=True, order=None, subok=False, ndmin=0)

and

numpy.asarray(a, dtype=None, order=None)

The following arguments are those that may be passed to array and not asarray as mentioned in the documentation :

copy : bool, optional If true (default), then the object is copied. Otherwise, a copy will only be made if __array__ returns a copy, if obj is a nested sequence, or if a copy is needed to satisfy any of the other requirements (dtype, order, etc.).

subok : bool, optional If True, then sub-classes will be passed-through, otherwise the returned array will be forced to be a base-class array (default).

ndmin : int, optional Specifies the minimum number of dimensions that the resulting array should have. Ones will be pre-pended to the shape as needed to meet this requirement.


回答 4

这是一个可以证明差异的简单示例。

主要区别在于数组将复制原始数据,并且使用不同的对象,我们可以修改原始数组中的数据。

import numpy as np
a = np.arange(0.0, 10.2, 0.12)
int_cvr = np.asarray(a, dtype = np.int64)

数组(a)中的内容保持不变,但仍然可以使用另一个对象对数据执行任何操作,而无需修改原始数组中的内容。

Here’s a simple example that can demonstrate the difference.

The main difference is that array will make a copy of the original data and using different object we can modify the data in the original array.

import numpy as np
a = np.arange(0.0, 10.2, 0.12)
int_cvr = np.asarray(a, dtype = np.int64)

The contents in array (a), remain untouched, and still, we can perform any operation on the data using another object without modifying the content in original array.


回答 5

asarray(x) 就好像 array(x, copy=False)

asarray(x)当您要确保x在执行任何其他操作之前将其设为数组时使用。如果x已经是数组,则不会进行任何复制。这不会造成冗余的性能损失。

这是确保x首先转换为数组的函数示例。

def mysum(x):
    return np.asarray(x).sum()

asarray(x) is like array(x, copy=False)

Use asarray(x) when you want to ensure that x will be an array before any other operations are done. If x is already an array then no copy would be done. It would not cause a redundant performance hit.

Here is an example of a function that ensure x is converted into an array first.

def mysum(x):
    return np.asarray(x).sum()

如何在Django中管理本地和生产设置?

问题:如何在Django中管理本地和生产设置?

建议使用什么方式处理本地开发和生产服务器的设置?它们中的某些(例如常量等)都可以更改/访问,但是其中一些(例如静态文件的路径)需要保持不同,因此,每次部署新代码时都不应覆盖它们。

当前,我将所有常量添加到中settings.py。但是每次我在本地更改一些常量时,都必须将其复制到生产服务器并编辑文件以进行生产特定更改… :(

编辑:这个问题似乎没有标准答案,我已经接受了最受欢迎的方法。

What is the recommended way of handling settings for local development and the production server? Some of them (like constants, etc) can be changed/accessed in both, but some of them (like paths to static files) need to remain different, and hence should not be overwritten every time the new code is deployed.

Currently, I am adding all constants to settings.py. But every time I change some constant locally, I have to copy it to the production server and edit the file for production specific changes… :(

Edit: looks like there is no standard answer to this question, I’ve accepted the most popular method.


回答 0

settings.py

try:
    from local_settings import *
except ImportError as e:
    pass

您可以覆盖local_settings.py;中的需要;然后,它应该脱离版本控制。但是由于您提到了复制,我猜您没有使用;)

In settings.py:

try:
    from local_settings import *
except ImportError as e:
    pass

You can override what needed in local_settings.py; it should stay out of your version control then. But since you mention copying I’m guessing you use none ;)


回答 1

Django的两个摘要:Django 1.5最佳实践建议对您的设置文件使用版本控制并将文件存储在单独的目录中:

project/
    app1/
    app2/
    project/
        __init__.py
        settings/
            __init__.py
            base.py
            local.py
            production.py
    manage.py

base.py文件包含常用的设置(如MEDIA_ROOT或ADMIN),而local.pyproduction.py有网站特定的设置:

在基本文件中settings/base.py

INSTALLED_APPS = (
    # common apps...
)

在本地开发设置文件中settings/local.py

from project.settings.base import *

DEBUG = True
INSTALLED_APPS += (
    'debug_toolbar', # and other apps for local development
)

在文件生产设置文件中settings/production.py

from project.settings.base import *

DEBUG = False
INSTALLED_APPS += (
    # other apps for production site
)

然后,在运行django时,添加以下--settings选项:

# Running django for local development
$ ./manage.py runserver 0:8000 --settings=project.settings.local

# Running django shell on the production site
$ ./manage.py shell --settings=project.settings.production

该书的作者还在Github上提供了一个示例项目布局模板

Two Scoops of Django: Best Practices for Django 1.5 suggests using version control for your settings files and storing the files in a separate directory:

project/
    app1/
    app2/
    project/
        __init__.py
        settings/
            __init__.py
            base.py
            local.py
            production.py
    manage.py

The base.py file contains common settings (such as MEDIA_ROOT or ADMIN), while local.py and production.py have site-specific settings:

In the base file settings/base.py:

INSTALLED_APPS = (
    # common apps...
)

In the local development settings file settings/local.py:

from project.settings.base import *

DEBUG = True
INSTALLED_APPS += (
    'debug_toolbar', # and other apps for local development
)

In the file production settings file settings/production.py:

from project.settings.base import *

DEBUG = False
INSTALLED_APPS += (
    # other apps for production site
)

Then when you run django, you add the --settings option:

# Running django for local development
$ ./manage.py runserver 0:8000 --settings=project.settings.local

# Running django shell on the production site
$ ./manage.py shell --settings=project.settings.production

The authors of the book have also put up a sample project layout template on Github.


回答 2

代替settings.py使用以下布局:

.
└── settings/
    ├── __init__.py  <= not versioned
    ├── common.py
    ├── dev.py
    └── prod.py

common.py 是大多数配置的所在地。

prod.py 从common导入所有内容,并覆盖需要覆盖的所有内容:

from __future__ import absolute_import # optional, but I like it
from .common import *

# Production overrides
DEBUG = False
#...

同样,dev.py从中导入所有内容common.py并覆盖其需要覆盖的所有内容。

最后,__init__.py是您决定加载哪些设置的地方,也是存储机密的地方(因此,不应对该文件进行版本控制):

from __future__ import absolute_import
from .prod import *  # or .dev if you want dev

##### DJANGO SECRETS
SECRET_KEY = '(3gd6shenud@&57...'
DATABASES['default']['PASSWORD'] = 'f9kGH...'

##### OTHER SECRETS
AWS_SECRET_ACCESS_KEY = "h50fH..."

我喜欢这个解决方案的地方是:

  1. 除秘密外,一切都在您的版本控制系统中
  2. 大多数配置都集中在一个位置:common.py
  3. 特定于产品的东西进入了prod.py,特定于开发器的东西进入了dev.py。这很简单。
  4. 您可以从覆盖的东西common.pyprod.py或者dev.py,你可以覆盖任何东西__init__.py
  5. 这是简单易懂的python。没有重新导入的黑客。

Instead of settings.py, use this layout:

.
└── settings/
    ├── __init__.py  <= not versioned
    ├── common.py
    ├── dev.py
    └── prod.py

common.py is where most of your configuration lives.

prod.py imports everything from common, and overrides whatever it needs to override:

from __future__ import absolute_import # optional, but I like it
from .common import *

# Production overrides
DEBUG = False
#...

Similarly, dev.py imports everything from common.py and overrides whatever it needs to override.

Finally, __init__.py is where you decide which settings to load, and it’s also where you store secrets (therefore this file should not be versioned):

from __future__ import absolute_import
from .prod import *  # or .dev if you want dev

##### DJANGO SECRETS
SECRET_KEY = '(3gd6shenud@&57...'
DATABASES['default']['PASSWORD'] = 'f9kGH...'

##### OTHER SECRETS
AWS_SECRET_ACCESS_KEY = "h50fH..."

What I like about this solution is:

  1. Everything is in your versioning system, except secrets
  2. Most configuration is in one place: common.py.
  3. Prod-specific things go in prod.py, dev-specific things go in dev.py. It’s simple.
  4. You can override stuff from common.py in prod.py or dev.py, and you can override anything in __init__.py.
  5. It’s straightforward python. No re-import hacks.

回答 3

我使用Harper Shelby发布的“ if DEBUG”样式的设置的稍微修改的版本。显然,取决于环境(win / linux / etc),可能需要对代码进行一些调整。

我以前使用的是“ if DEBUG”,但发现偶尔需要将DEUBG设置为False进行测试。我真正想区分的是环境是生产环境还是开发环境,这使我可以自由选择DEBUG级别。

PRODUCTION_SERVERS = ['WEBSERVER1','WEBSERVER2',]
if os.environ['COMPUTERNAME'] in PRODUCTION_SERVERS:
    PRODUCTION = True
else:
    PRODUCTION = False

DEBUG = not PRODUCTION
TEMPLATE_DEBUG = DEBUG

# ...

if PRODUCTION:
    DATABASE_HOST = '192.168.1.1'
else:
    DATABASE_HOST = 'localhost'

我仍然会考虑以这种方式设置正在进行的工作。我还没有一种方法可以处理涵盖所有基础的Django设置,但同时也没有设置的麻烦(我对5x设置文件方法并不感到失望)。

I use a slightly modified version of the “if DEBUG” style of settings that Harper Shelby posted. Obviously depending on the environment (win/linux/etc.) the code might need to be tweaked a bit.

I was in the past using the “if DEBUG” but I found that occasionally I needed to do testing with DEUBG set to False. What I really wanted to distinguish if the environment was production or development, which gave me the freedom to choose the DEBUG level.

PRODUCTION_SERVERS = ['WEBSERVER1','WEBSERVER2',]
if os.environ['COMPUTERNAME'] in PRODUCTION_SERVERS:
    PRODUCTION = True
else:
    PRODUCTION = False

DEBUG = not PRODUCTION
TEMPLATE_DEBUG = DEBUG

# ...

if PRODUCTION:
    DATABASE_HOST = '192.168.1.1'
else:
    DATABASE_HOST = 'localhost'

I’d still consider this way of settings a work in progress. I haven’t seen any one way to handling Django settings that covered all the bases and at the same time wasn’t a total hassle to setup (I’m not down with the 5x settings files methods).


回答 4

我使用settings_local.py和settings_production.py。尝试了几个选项之后,我发现,简单地拥有两个设置文件就容易又快速,很容易在复杂的解决方案上浪费时间。

当对您的Django项目使用mod_python / mod_wsgi时,需要将其指向您的设置文件。如果将其指向本地服务器上的app / settings_local.py和生产服务器上的app / settings_production.py,那么生活会变得很轻松。只需编辑适当的设置文件并重新启动服务器(Django开发服务器将自动重新启动)。

I use a settings_local.py and a settings_production.py. After trying several options I’ve found that it’s easy to waste time with complex solutions when simply having two settings files feels easy and fast.

When you use mod_python/mod_wsgi for your Django project you need to point it to your settings file. If you point it to app/settings_local.py on your local server and app/settings_production.py on your production server then life becomes easy. Just edit the appropriate settings file and restart the server (Django development server will restart automatically).


回答 5

TL; DR:诀窍是os.environment在导入settings/base.py任何文件之前进行修改settings/<purpose>.py,这将大大简化事情。


仅考虑所有这些相互交织的文件,就让我头疼。合并,导入(有时是有条件的),覆盖,修补已设置的内容,以防DEBUG稍后设置更改。什么样的恶梦!

这些年来,我经历了所有不同的解决方案。它们都有些起作用,但是管理起来非常痛苦。WTF!我们真的需要所有麻烦吗?我们从一个settings.py文件开始。现在,我们需要一个文档来将所有这些以正确的顺序正确地组合在一起!

我希望我最终能通过以下解决方法达到(我的)最佳解决方案。

让我们回顾一下目标(一些普通的,一些我的)

  1. 保守秘密-不要将其存储在仓库中!

  2. 通过环境设置(12因子样式)设置/读取密钥和机密。

  3. 具有合理的后备默认设置。理想情况下,对于本地开发,除默认值外,您不需要任何其他内容。

  4. …但请尝试确保默认生产的安全。最好不要在本地错过设置替代,而不必记住要调整默认设置以确保生产安全。

  5. 能够以DEBUG可能影响其他设置的方式打开/关闭(例如,是否使用javascript压缩)。

  6. 在目标设置(例如本地/测试/过渡/生产)之间切换时,仅应基于,而仅此DJANGO_SETTINGS_MODULE而已。

  7. …但是可以通过环境设置(例如)进行进一步的参数设置DATABASE_URL

  8. …还允许他们使用不同的用途设置,并在本地并排运行它们,例如 在本地开发人员机器上进行生产设置,以访问生产数据库或对压缩样式表进行烟雾测试。

  9. 如果未明确设置环境变量(至少需要一个空值),则失败,例如在生产中尤其如此。EMAIL_HOST_PASSWORD

  10. DJANGO_SETTINGS_MODULEdjango-admin startproject期间响应manage.py中的默认设置

  11. 条件语句保持到最低限度,如果条件旨意环境类型(例如,用于生产一系列的日志文件,它的自转),在相关的旨意设置文件中的设置优先。

  1. 不要让django从文件中读取DJANGO_SETTINGS_MODULE设置。
    啊! 想想这是元数据。如果您需要一个文件(例如docker env),请在启动django进程之前将其读入环境。

  2. 不要在您的项目/应用代码中覆盖DJANGO_SETTINGS_MODULE,例如。基于主机名或进程名。
    如果您懒于设置环境变量(例如setup.py test),请在运行项目代码之前在工具中进行设置。

  3. 避免使用django读取其设置的魔术和修补程序,对设置进行预处理,但之后不要干预。

  4. 没有复杂的基于逻辑的废话。配置应该是固定的,不能实时计算。提供备用默认值仅是这里的逻辑。
    您是否真的要调试,为什么要在本地设置正确的设置集,而在远程服务器上的生产环境中(在一台一百台计算机上)计算的结果却有所不同?哦! 单元测试?进行设定?认真吗

我的策略是由优秀的Django的ENVIRON与使用的ini样式文件,提供os.environment当地发展的默认设置,一些最起码的,短settings/<purpose>.py的是有一个文件 import settings/base.py os.environment是从一个设置INI文件。这有效地为我们提供了一种设置注入。

这里的窍门是os.environment在导入之前进行修改settings/base.py

要查看完整的示例,请执行回购:https : //github.com/wooyek/django-settings-strategy

.
   manage.py
├───data
└───website
    ├───settings
          __init__.py   <-- imports local for compatibility
          base.py       <-- almost all the settings, reads from proces environment 
          local.py      <-- a few modifications for local development
          production.py <-- ideally is empty and everything is in base 
          testing.py    <-- mimics production with a reasonable exeptions
          .env          <-- for local use, not kept in repo
       __init__.py
       urls.py
       wsgi.py

设置/.env

本地开发的默认设置。一个秘密文件,主要用于设置所需的环境变量。如果在本地开发中不需要它们,请将它们设置为空值。我们在这里提供默认值,settings/base.py如果环境中缺少其他默认值,则不会在其他任何机器上失败。

设置/ local.py

这里发生的是从中加载环境settings/.env,然后从中导入通用设置settings/base.py。之后,我们可以覆盖一些以简化本地开发。

import logging
import environ

logging.debug("Settings loading: %s" % __file__)

# This will read missing environment variables from a file
# We wan to do this before loading a base settings as they may depend on environment
environ.Env.read_env(DEBUG='True')

from .base import *

ALLOWED_HOSTS += [
    '127.0.0.1',
    'localhost',
    '.example.com',
    'vagrant',
    ]

# https://docs.djangoproject.com/en/1.6/topics/email/#console-backend
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
# EMAIL_BACKEND = 'django.core.mail.backends.dummy.EmailBackend'

LOGGING['handlers']['mail_admins']['email_backend'] = 'django.core.mail.backends.dummy.EmailBackend'

# Sync task testing
# http://docs.celeryproject.org/en/2.5/configuration.html?highlight=celery_always_eager#celery-always-eager

CELERY_ALWAYS_EAGER = True
CELERY_EAGER_PROPAGATES_EXCEPTIONS = True

settings / production.py

对于生产环境,我们不应该期望环境文件,但是如果我们正在测试某些东西,那么拥有一个环境文件会更容易。但是无论如何,免得内联提供很少的默认值,因此settings/base.py可以做出相应的响应。

environ.Env.read_env(Path(__file__) / "production.env", DEBUG='False', ASSETS_DEBUG='False')
from .base import *

这里的主要关注点是DEBUGASSETS_DEBUG覆盖,os.environ仅当它们从环境和文件中丢失时,它们才会应用于python 。

这些将是我们的生产默认值,无需将它们放在环境或文件中,但是如果需要可以覆盖它们。整齐!

设置/ base.py

这些是您最常用的django设置,有一些条件和很多从环境中读取它们的条件。几乎所有内容都在这里,使所有目标环境保持一致并尽可能相似。

主要区别如下(我希望这些是自我解释):

import environ

# https://github.com/joke2k/django-environ
env = environ.Env()

# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
BASE_DIR = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))

# Where BASE_DIR is a django source root, ROOT_DIR is a whole project root
# It may differ BASE_DIR for eg. when your django project code is in `src` folder
# This may help to separate python modules and *django apps* from other stuff
# like documentation, fixtures, docker settings
ROOT_DIR = BASE_DIR

# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/1.11/howto/deployment/checklist/

# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = env('SECRET_KEY')

# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = env('DEBUG', default=False)

INTERNAL_IPS = [
    '127.0.0.1',
]

ALLOWED_HOSTS = []

if 'ALLOWED_HOSTS' in os.environ:
    hosts = os.environ['ALLOWED_HOSTS'].split(" ")
    BASE_URL = "https://" + hosts[0]
    for host in hosts:
        host = host.strip()
        if host:
            ALLOWED_HOSTS.append(host)

SECURE_SSL_REDIRECT = env.bool('SECURE_SSL_REDIRECT', default=False)

# Database
# https://docs.djangoproject.com/en/1.11/ref/settings/#databases

if "DATABASE_URL" in os.environ:  # pragma: no cover
    # Enable database config through environment
    DATABASES = {
        # Raises ImproperlyConfigured exception if DATABASE_URL not in os.environ
        'default': env.db(),
    }

    # Make sure we use have all settings we need
    # DATABASES['default']['ENGINE'] = 'django.contrib.gis.db.backends.postgis'
    DATABASES['default']['TEST'] = {'NAME': os.environ.get("DATABASE_TEST_NAME", None)}
    DATABASES['default']['OPTIONS'] = {
        'options': '-c search_path=gis,public,pg_catalog',
        'sslmode': 'require',
    }
else:
    DATABASES = {
        'default': {
            'ENGINE': 'django.db.backends.sqlite3',
            # 'ENGINE': 'django.contrib.gis.db.backends.spatialite',
            'NAME': os.path.join(ROOT_DIR, 'data', 'db.dev.sqlite3'),
            'TEST': {
                'NAME': os.path.join(ROOT_DIR, 'data', 'db.test.sqlite3'),
            }
        }
    }

STATIC_ROOT = os.path.join(ROOT_DIR, 'static')

# django-assets
# http://django-assets.readthedocs.org/en/latest/settings.html

ASSETS_LOAD_PATH = STATIC_ROOT
ASSETS_ROOT = os.path.join(ROOT_DIR, 'assets', "compressed")
ASSETS_DEBUG = env('ASSETS_DEBUG', default=DEBUG)  # Disable when testing compressed file in DEBUG mode
if ASSETS_DEBUG:
    ASSETS_URL = STATIC_URL
    ASSETS_MANIFEST = "json:{}".format(os.path.join(ASSETS_ROOT, "manifest.json"))
else:
    ASSETS_URL = STATIC_URL + "assets/compressed/"
    ASSETS_MANIFEST = "json:{}".format(os.path.join(STATIC_ROOT, 'assets', "compressed", "manifest.json"))
ASSETS_AUTO_BUILD = ASSETS_DEBUG
ASSETS_MODULES = ('website.assets',)

最后一位显示此处的功率。ASSETS_DEBUG有一个合理的默认值,可以将其覆盖settings/production.py,甚至可以由环境设置覆盖!好极了!

实际上,我们具有不同的重要性等级:

  1. settings / .py-根据目的设置默认值,不存储秘密
  2. settings / base.py-主要由环境控制
  3. 过程环境设置-12要素宝贝!
  4. settings / .env-本地默认设置,易于启动

TL;DR: The trick is to modify os.environment before you import settings/base.py in any settings/<purpose>.py, this will greatly simplify things.


Just thinking about all these intertwining files gives me a headache. Combining, importing (sometimes conditionally), overriding, patching of what was already set in case DEBUG setting changed later on. What a nightmare!

Through the years I went through all different solutions. They all somewhat work, but are so painful to manage. WTF! Do we really need all that hassle? We started with just one settings.py file. Now we need a documentation just to correctly combine all these together in a correct order!

I hope I finally hit the (my) sweet spot with the solution below.

Let’s recap the goals (some common, some mine)

  1. Keep secrets a secret — don’t store them in a repo!

  2. Set/read keys and secrets through environment settings, 12 factor style.

  3. Have sensible fallback defaults. Ideally for local development you don’t need anything more beside defaults.

  4. …but try to keep defaults production safe. It’s better to miss a setting override locally, than having to remember to adjust default settings safe for production.

  5. Have the ability to switch DEBUG on/off in a way that can have an effect on other settings (eg. using javascript compressed or not).

  6. Switching between purpose settings, like local/testing/staging/production, should be based only on DJANGO_SETTINGS_MODULE, nothing more.

  7. …but allow further parameterization through environment settings like DATABASE_URL.

  8. …also allow them to use different purpose settings and run them locally side by side, eg. production setup on local developer machine, to access production database or smoke test compressed style sheets.

  9. Fail if an environment variable is not explicitly set (requiring an empty value at minimum), especially in production, eg. EMAIL_HOST_PASSWORD.

  10. Respond to default DJANGO_SETTINGS_MODULE set in manage.py during django-admin startproject

  11. Keep conditionals to a minimum, if the condition is the purposed environment type (eg. for production set log file and it’s rotation), override settings in associated purposed settings file.

Do not’s

  1. Do not let django read DJANGO_SETTINGS_MODULE setting form a file.
    Ugh! Think of how meta this is. If you need to have a file (like docker env) read that into the environment before staring up a django process.

  2. Do not override DJANGO_SETTINGS_MODULE in your project/app code, eg. based on hostname or process name.
    If you are lazy to set environment variable (like for setup.py test) do it in tooling just before you run your project code.

  3. Avoid magic and patching of how django reads it’s settings, preprocess the settings but do not interfere afterwards.

  4. No complicated logic based nonsense. Configuration should be fixed and materialized not computed on the fly. Providing a fallback defaults is just enough logic here.
    Do you really want to debug, why locally you have correct set of settings but in production on a remote server, on one of hundred machines, something computed differently? Oh! Unit tests? For settings? Seriously?

Solution

My strategy consists of excellent django-environ used with ini style files, providing os.environment defaults for local development, some minimal and short settings/<purpose>.py files that have an import settings/base.py AFTER the os.environment was set from an INI file. This effectively give us a kind of settings injection.

The trick here is to modify os.environment before you import settings/base.py.

To see the full example go do the repo: https://github.com/wooyek/django-settings-strategy

.
│   manage.py
├───data
└───website
    ├───settings
    │   │   __init__.py   <-- imports local for compatibility
    │   │   base.py       <-- almost all the settings, reads from proces environment 
    │   │   local.py      <-- a few modifications for local development
    │   │   production.py <-- ideally is empty and everything is in base 
    │   │   testing.py    <-- mimics production with a reasonable exeptions
    │   │   .env          <-- for local use, not kept in repo
    │   __init__.py
    │   urls.py
    │   wsgi.py

settings/.env

A defaults for local development. A secret file, to mostly set required environment variables. Set them to empty values if they are not required in local development. We provide defaults here and not in settings/base.py to fail on any other machine if the’re missing from the environment.

settings/local.py

What happens in here, is loading environment from settings/.env, then importing common settings from settings/base.py. After that we can override a few to ease local development.

import logging
import environ

logging.debug("Settings loading: %s" % __file__)

# This will read missing environment variables from a file
# We wan to do this before loading a base settings as they may depend on environment
environ.Env.read_env(DEBUG='True')

from .base import *

ALLOWED_HOSTS += [
    '127.0.0.1',
    'localhost',
    '.example.com',
    'vagrant',
    ]

# https://docs.djangoproject.com/en/1.6/topics/email/#console-backend
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
# EMAIL_BACKEND = 'django.core.mail.backends.dummy.EmailBackend'

LOGGING['handlers']['mail_admins']['email_backend'] = 'django.core.mail.backends.dummy.EmailBackend'

# Sync task testing
# http://docs.celeryproject.org/en/2.5/configuration.html?highlight=celery_always_eager#celery-always-eager

CELERY_ALWAYS_EAGER = True
CELERY_EAGER_PROPAGATES_EXCEPTIONS = True

settings/production.py

For production we should not expect an environment file, but it’s easier to have one if we’re testing something. But anyway, lest’s provide few defaults inline, so settings/base.py can respond accordingly.

environ.Env.read_env(Path(__file__) / "production.env", DEBUG='False', ASSETS_DEBUG='False')
from .base import *

The main point of interest here are DEBUG and ASSETS_DEBUG overrides, they will be applied to the python os.environ ONLY if they are MISSING from the environment and the file.

These will be our production defaults, no need to put them in the environment or file, but they can be overridden if needed. Neat!

settings/base.py

These are your mostly vanilla django settings, with a few conditionals and lot’s of reading them from the environment. Almost everything is in here, keeping all the purposed environments consistent and as similar as possible.

The main differences are below (I hope these are self explanatory):

import environ

# https://github.com/joke2k/django-environ
env = environ.Env()

# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
BASE_DIR = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))

# Where BASE_DIR is a django source root, ROOT_DIR is a whole project root
# It may differ BASE_DIR for eg. when your django project code is in `src` folder
# This may help to separate python modules and *django apps* from other stuff
# like documentation, fixtures, docker settings
ROOT_DIR = BASE_DIR

# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/1.11/howto/deployment/checklist/

# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = env('SECRET_KEY')

# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = env('DEBUG', default=False)

INTERNAL_IPS = [
    '127.0.0.1',
]

ALLOWED_HOSTS = []

if 'ALLOWED_HOSTS' in os.environ:
    hosts = os.environ['ALLOWED_HOSTS'].split(" ")
    BASE_URL = "https://" + hosts[0]
    for host in hosts:
        host = host.strip()
        if host:
            ALLOWED_HOSTS.append(host)

SECURE_SSL_REDIRECT = env.bool('SECURE_SSL_REDIRECT', default=False)

# Database
# https://docs.djangoproject.com/en/1.11/ref/settings/#databases

if "DATABASE_URL" in os.environ:  # pragma: no cover
    # Enable database config through environment
    DATABASES = {
        # Raises ImproperlyConfigured exception if DATABASE_URL not in os.environ
        'default': env.db(),
    }

    # Make sure we use have all settings we need
    # DATABASES['default']['ENGINE'] = 'django.contrib.gis.db.backends.postgis'
    DATABASES['default']['TEST'] = {'NAME': os.environ.get("DATABASE_TEST_NAME", None)}
    DATABASES['default']['OPTIONS'] = {
        'options': '-c search_path=gis,public,pg_catalog',
        'sslmode': 'require',
    }
else:
    DATABASES = {
        'default': {
            'ENGINE': 'django.db.backends.sqlite3',
            # 'ENGINE': 'django.contrib.gis.db.backends.spatialite',
            'NAME': os.path.join(ROOT_DIR, 'data', 'db.dev.sqlite3'),
            'TEST': {
                'NAME': os.path.join(ROOT_DIR, 'data', 'db.test.sqlite3'),
            }
        }
    }

STATIC_ROOT = os.path.join(ROOT_DIR, 'static')

# django-assets
# http://django-assets.readthedocs.org/en/latest/settings.html

ASSETS_LOAD_PATH = STATIC_ROOT
ASSETS_ROOT = os.path.join(ROOT_DIR, 'assets', "compressed")
ASSETS_DEBUG = env('ASSETS_DEBUG', default=DEBUG)  # Disable when testing compressed file in DEBUG mode
if ASSETS_DEBUG:
    ASSETS_URL = STATIC_URL
    ASSETS_MANIFEST = "json:{}".format(os.path.join(ASSETS_ROOT, "manifest.json"))
else:
    ASSETS_URL = STATIC_URL + "assets/compressed/"
    ASSETS_MANIFEST = "json:{}".format(os.path.join(STATIC_ROOT, 'assets', "compressed", "manifest.json"))
ASSETS_AUTO_BUILD = ASSETS_DEBUG
ASSETS_MODULES = ('website.assets',)

The last bit shows the power here. ASSETS_DEBUG has a sensible default, which can be overridden in settings/production.py and even that that can be overridden by an environment setting! Yay!

In effect we have a mixed hierarchy of importance:

  1. settings/.py – sets defaults based on purpose, does not store secrets
  2. settings/base.py – is mostly controlled by environment
  3. process environment settings – 12 factor baby!
  4. settings/.env – local defaults for easy startup

回答 6

我在django-split-settings的帮助下管理我的配置。

它是默认设置的替代品。它很简单,但可配置。并且不需要重构您的现有设置。

这是一个小示例(文件example/settings/__init__.py):

from split_settings.tools import optional, include
import os

if os.environ['DJANGO_SETTINGS_MODULE'] == 'example.settings':
    include(
        'components/default.py',
        'components/database.py',
        # This file may be missing:
        optional('local_settings.py'),

        scope=globals()
    )

而已。

更新资料

我写了一篇博客文章,介绍如何使用来管理django设置django-split-sttings。看一看!

I manage my configurations with the help of django-split-settings.

It is a drop-in replacement for the default settings. It is simple, yet configurable. And refactoring of your exisitng settings is not required.

Here’s a small example (file example/settings/__init__.py):

from split_settings.tools import optional, include
import os

if os.environ['DJANGO_SETTINGS_MODULE'] == 'example.settings':
    include(
        'components/default.py',
        'components/database.py',
        # This file may be missing:
        optional('local_settings.py'),

        scope=globals()
    )

That’s it.

Update

I wrote a blog post about managing django‘s settings with django-split-sttings. Have a look!


回答 7

这些解决方案中的大多数问题是您在本地设置之前之后应用了本地设置。

因此,不可能覆盖诸如

  • 特定于环境的设置定义了内存缓存池的地址,在主设置文件中,此值用于配置缓存后端
  • 特定于环境的设置将应用程序/中间件添加或删除为默认设置

与此同时。

可以使用带有ConfigParser类的“ ini”样式的配置文件来实现一种解决方案。它支持多个文件,惰性字符串插值,默认值和许多其他功能。一旦加载了许多文件,就可以加载更多文件,并且它们的值将覆盖先前的文件(如果有)。

您加载一个或多个配置文件,具体取决于机器地址,环境变量甚至以前加载的配置文件中的值。然后,您只需使用解析后的值来填充设置。

我成功使用的一种策略是:

  • 加载默认defaults.ini文件
  • 检查机器名称,并加载与反向FQDN匹配的所有文件,从最短匹配到最长匹配(因此,我先加载net.ini,然后加载,然后net.domain.ini再加载net.domain.webserver01.ini,每个可能覆盖前一个值)。此帐户也用于开发人员的计算机,因此每个人都可以设置其首选的数据库驱动程序等以进行本地开发
  • 检查是否声明了“集群名称”,在这种情况下cluster.cluster_name.ini,请检查load ,它可以定义数据库和缓存IP之类的内容

作为可以实现此目标的示例,您可以为每个环境定义一个“子域”值,然后在默认设置(如hostname: %(subdomain).whatever.net)中使用它来定义django需要工作的所有必需的主机名和cookie。

这是我可以得到的DRY,大多数(现有)文件只有3或4个设置。最重要的是,我必须管理客户配置,因此存在另外一组配置文件(带有数据库名称,用户和密码,分配的子域等),每个客户一个或多个。

可以根据需要将其缩放为低或高,您只需将要在每个环境中配置的密钥放入配置文件中,然后在需要新配置时,将先前的值放入默认配置中,然后覆盖它即可。在必要时。

该系统已经证明是可靠的,并且可以与版本控制一起很好地工作。它已长期用于管理两个单独的应用程序集群(每台机器15个或更多django站点的单独实例),拥有50多个客户,这些集群根据sysadmin的心情来改变大小和成员。 。

The problem with most of these solutions is that you either have your local settings applied before the common ones, or after them.

So it’s impossible to override things like

  • the env-specific settings define the addresses for the memcached pool, and in the main settings file this value is used to configure the cache backend
  • the env-specific settings add or remove apps/middleware to the default one

at the same time.

One solution can be implemented using “ini”-style config files with the ConfigParser class. It supports multiple files, lazy string interpolation, default values and a lot of other goodies. Once a number of files have been loaded, more files can be loaded and their values will override the previous ones, if any.

You load one or more config files, depending on the machine address, environment variables and even values in previously loaded config files. Then you just use the parsed values to populate the settings.

One strategy I have successfully used has been:

  • Load a default defaults.ini file
  • Check the machine name, and load all files which matched the reversed FQDN, from the shortest match to the longest match (so, I loaded net.ini, then net.domain.ini, then net.domain.webserver01.ini, each one possibly overriding values of the previous). This account also for developers’ machines, so each one could set up its preferred database driver, etc. for local development
  • Check if there is a “cluster name” declared, and in that case load cluster.cluster_name.ini, which can define things like database and cache IPs

As an example of something you can achieve with this, you can define a “subdomain” value per-env, which is then used in the default settings (as hostname: %(subdomain).whatever.net) to define all the necessary hostnames and cookie things django needs to work.

This is as DRY I could get, most (existing) files had just 3 or 4 settings. On top of this I had to manage customer configuration, so an additional set of configuration files (with things like database names, users and passwords, assigned subdomain etc) existed, one or more per customer.

One can scale this as low or as high as necessary, you just put in the config file the keys you want to configure per-environment, and once there’s need for a new config, put the previous value in the default config, and override it where necessary.

This system has proven reliable and works well with version control. It has been used for long time managing two separate clusters of applications (15 or more separate instances of the django site per machine), with more than 50 customers, where the clusters were changing size and members depending on the mood of the sysadmin…


回答 8

我也在与Laravel合作,我喜欢在那里的实现。我试图模仿它,并将其与T.Stone提出的解决方案结合起来(见上):

PRODUCTION_SERVERS = ['*.webfaction.com','*.whatever.com',]

def check_env():
    for item in PRODUCTION_SERVERS:
        match = re.match(r"(^." + item + "$)", socket.gethostname())
        if match:
            return True

if check_env():
    PRODUCTION = True
else:
    PRODUCTION = False

DEBUG = not PRODUCTION

也许这样的事情会帮助您。

I am also working with Laravel and I like the implementation there. I tried to mimic it and combining it with the solution proposed by T. Stone (look above):

PRODUCTION_SERVERS = ['*.webfaction.com','*.whatever.com',]

def check_env():
    for item in PRODUCTION_SERVERS:
        match = re.match(r"(^." + item + "$)", socket.gethostname())
        if match:
            return True

if check_env():
    PRODUCTION = True
else:
    PRODUCTION = False

DEBUG = not PRODUCTION

Maybe something like this would help you.


回答 9

请记住,settings.py是实时代码文件。假设您没有在生产环境中设置DEBUG(这是最佳做法),则可以执行以下操作:

if DEBUG:
    STATIC_PATH = /path/to/dev/files
else:
    STATIC_PATH = /path/to/production/files

这很基本,但是从理论上讲,您可以仅根据DEBUG的值-或要使用的任何其他变量或代码检查,将复杂性提高到任何水平。

Remember that settings.py is a live code file. Assuming that you don’t have DEBUG set on production (which is a best practice), you can do something like:

if DEBUG:
    STATIC_PATH = /path/to/dev/files
else:
    STATIC_PATH = /path/to/production/files

Pretty basic, but you could, in theory, go up to any level of complexity based on just the value of DEBUG – or any other variable or code check you wanted to use.


回答 10

对于我的大多数项目,我使用以下模式:

  1. 在我存储所有环境通用设置的位置创建settings_base.py
  2. 每当需要使用具有特定要求的新环境时,我都会创建一个新的设置文件(例如settings_local.py),该文件将继承settings_base.py的内容并覆盖/添加适当的设置变量(from settings_base import *

(要使用自定义设置运行manage.py文件只需使用–settings命令选项:manage.py <command> --settings=settings_you_wish_to_use.py

For most of my projects I use following pattern:

  1. Create settings_base.py where I store settings that are common for all environments
  2. Whenever I need to use new environment with specific requirements I create new settings file (eg. settings_local.py) which inherits contents of settings_base.py and overrides/adds proper settings variables (from settings_base import *)

(To run manage.py with custom settings file you simply use –settings command option: manage.py <command> --settings=settings_you_wish_to_use.py)


回答 11

我对这个问题的解决方案在某种程度上也是这里已经提到的一些解决方案的混合:

  • 我保留了一个名为dev和prod中local_settings.py内容的文件USING_LOCAL = TrueUSING_LOCAL = False
  • settings.py我对该文件进行导入以获取USING_LOCAL设置

然后,我将所有与环境相关的设置都基于该设置:

DEBUG = USING_LOCAL
if USING_LOCAL:
    # dev database settings
else:
    # prod database settings

我宁愿拥有两个需要维护的单独的settings.py文件,因为与将它们分散在多个文件中相比,可以将设置结构化为一个文件。这样,当我更新设置时,我不会忘记在两种环境下都进行设置。

当然,每种方法都有其缺点,这一方法也不exceptions。这里的问题是,local_settings.py每当我将更改推送到生产环境时,我都无法覆盖文件,这意味着我不能盲目地复制所有文件,但这是我可以忍受的。

My solution to that problem is also somewhat of a mix of some solutions already stated here:

  • I keep a file called local_settings.py that has the content USING_LOCAL = True in dev and USING_LOCAL = False in prod
  • In settings.py I do an import on that file to get the USING_LOCAL setting

I then base all my environment-dependent settings on that one:

DEBUG = USING_LOCAL
if USING_LOCAL:
    # dev database settings
else:
    # prod database settings

I prefer this to having two separate settings.py files that I need to maintain as I can keep my settings structured in a single file easier than having them spread across several files. Like this, when I update a setting I don’t forget to do it for both environments.

Of course that every method has its disadvantages and this one is no exception. The problem here is that I can’t overwrite the local_settings.py file whenever I push my changes into production, meaning I can’t just copy all files blindly, but that’s something I can live with.


回答 12

我使用了上面提到的jpartogi的一种变体,发现它略短一些:

import platform
from django.core.management import execute_manager 

computername = platform.node()

try:
  settings = __import__(computername + '_settings')
except ImportError: 
  import sys
  sys.stderr.write("Error: Can't find the file '%r_settings.py' in the directory containing %r. It appears you've customized things.\nYou'll have to run django-admin.py, passing it your settings module.\n(If the file local_settings.py does indeed exist, it's causing an ImportError somehow.)\n" % (computername, __file__))
  sys.exit(1)

if __name__ == "__main__":
  execute_manager(settings)

基本上在每台计算机(开发或生产)上,我都有适当的hostname_settings.py文件,该文件会动态加载。

I use a variation of what jpartogi mentioned above, that I find a little shorter:

import platform
from django.core.management import execute_manager 

computername = platform.node()

try:
  settings = __import__(computername + '_settings')
except ImportError: 
  import sys
  sys.stderr.write("Error: Can't find the file '%r_settings.py' in the directory containing %r. It appears you've customized things.\nYou'll have to run django-admin.py, passing it your settings module.\n(If the file local_settings.py does indeed exist, it's causing an ImportError somehow.)\n" % (computername, __file__))
  sys.exit(1)

if __name__ == "__main__":
  execute_manager(settings)

Basically on each computer (development or production) I have the appropriate hostname_settings.py file that gets dynamically loaded.


回答 13

也有Django Classy Settings。我个人是它的忠实拥护者。它是由Django IRC上最活跃的人之一构建的。您将使用环境变量进行设置。

http://django-classy-settings.readthedocs.io/en/latest/

There is also Django Classy Settings. I personally am a big fan of it. It’s built by one of the most active people on the Django IRC. You would use environment vars to set things.

http://django-classy-settings.readthedocs.io/en/latest/


回答 14

1-在您的应用程序内创建一个新文件夹,并为其命名设置。

2-现在__init__.py在其中创建一个新文件,并在其中写入

from .base import *

try:
    from .local import *
except:
    pass

try:
    from .production import *
except:
    pass

3 -创建在设置三个新文件夹的名称local.pyproduction.pybase.py

4-在内部base.py,复制上一个settings.py文件夹的所有内容,并用其他不同的名称重命名old_settings.py

5-在base.py中,更改BASE_DIR路径以指向新的设置路径

旧路径-> BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))

新路径-> BASE_DIR = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))

这样,可以在生产和本地开发之间构建项目目录并对其进行管理。

1 – Create a new folder inside your app and name settings to it.

2 – Now create a new __init__.py file in it and inside it write

from .base import *

try:
    from .local import *
except:
    pass

try:
    from .production import *
except:
    pass

3 – Create three new files in the settings folder name local.py and production.py and base.py.

4 – Inside base.py, copy all the content of previous settings.py folder and rename it with something different, let’s say old_settings.py.

5 – In base.py change your BASE_DIR path to point to your new path of setting

Old path-> BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))

New path -> BASE_DIR = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))

This way, the project dir can be structured and can be manageable among production and local development.


回答 15

为了settings在不同的环境上使用不同的配置,请创建不同的设置文件。然后,在部署脚本中,使用--settings=<my-settings.py>参数启动服务器,通过该参数可以在不同的环境上使用不同的设置

使用这种方法的好处

  1. 您的设置将根据每个环境进行模块化

  2. 您可以在中导入master_settings.py包含基本配置的内容,environmnet_configuration.py并覆盖要在该环境中更改的值。

  3. 如果您有庞大的团队,则每个开发人员可能都有自己的团队,可以将其local_settings.py添加到代码存储库中,而无需修改服务器配置。您可以添加这些本地设置,.gitnore如果您使用的git或者.hginore,如果你的Mercurial版本控制(或任何其他)。这样,本地设置甚至不会成为保持干净的实际代码库的一部分。

In order to use different settings configuration on different environment, create different settings file. And in your deployment script, start the server using --settings=<my-settings.py> parameter, via which you can use different settings on different environment.

Benefits of using this approach:

  1. Your settings will be modular based on each environment

  2. You may import the master_settings.py containing the base configuration in the environmnet_configuration.py and override the values that you want to change in that environment.

  3. If you have huge team, each developer may have their own local_settings.py which they can add to the code repository without any risk of modifying the server configuration. You can add these local settings to .gitnore if you use git or .hginore if you Mercurial for Version Control (or any other). That way local settings won’t even be the part of actual code base keeping it clean.


回答 16

我的设置如下拆分

settings/
     |
     |- base.py
     |- dev.py
     |- prod.py  

我们有3个环境

  • 开发者
  • 分期
  • 生产

现在显然,登台和生产应该具有最大可能的相似环境。所以我们都坚持prod.py

但是在某些情况下,我不得不确定正在运行的服务器是生产服务器。@T。斯通的答案帮助我写了以下支票。

from socket import gethostname, gethostbyname  
PROD_HOSTS = ["webserver1", "webserver2"]

DEBUG = False
ALLOWED_HOSTS = [gethostname(), gethostbyname(gethostname()),]


if any(host in PROD_HOSTS for host in ALLOWED_HOSTS):
    SESSION_COOKIE_SECURE = True
    CSRF_COOKIE_SECURE = True  

I had my settings split as follows

settings/
     |
     |- base.py
     |- dev.py
     |- prod.py  

We have 3 environments

  • dev
  • staging
  • production

Now obviously staging and production should have the maximum possible similar environment. So we kept prod.py for both.

But there was a case where I had to identify running server is a production server. @T. Stone ‘s answer helped me write check as follows.

from socket import gethostname, gethostbyname  
PROD_HOSTS = ["webserver1", "webserver2"]

DEBUG = False
ALLOWED_HOSTS = [gethostname(), gethostbyname(gethostname()),]


if any(host in PROD_HOSTS for host in ALLOWED_HOSTS):
    SESSION_COOKIE_SECURE = True
    CSRF_COOKIE_SECURE = True  

回答 17

我在manage.py中对其进行区分,并创建了两个单独的设置文件:local_settings.py和prod_settings.py。

在manage.py中,我检查服务器是本地服务器还是生产服务器。如果是本地服务器,则将加载local_settings.py,如果是生产服务器,则将加载prod_settings.py。基本上是这样的:

#!/usr/bin/env python
import sys
import socket
from django.core.management import execute_manager 

ipaddress = socket.gethostbyname( socket.gethostname() )
if ipaddress == '127.0.0.1':
    try:
        import local_settings # Assumed to be in the same directory.
        settings = local_settings
    except ImportError:
        import sys
        sys.stderr.write("Error: Can't find the file 'local_settings.py' in the directory containing %r. It appears you've customized things.\nYou'll have to run django-admin.py, passing it your settings module.\n(If the file local_settings.py does indeed exist, it's causing an ImportError somehow.)\n" % __file__)
        sys.exit(1)
else:
    try:
        import prod_settings # Assumed to be in the same directory.
        settings = prod_settings    
    except ImportError:
        import sys
        sys.stderr.write("Error: Can't find the file 'prod_settings.py' in the directory containing %r. It appears you've customized things.\nYou'll have to run django-admin.py, passing it your settings module.\n(If the file prod_settings.py does indeed exist, it's causing an ImportError somehow.)\n" % __file__)
        sys.exit(1)

if __name__ == "__main__":
    execute_manager(settings)

我发现将设置文件分为两个单独的文件比在设置文件中进行大量的ifs更为容易。

I differentiate it in manage.py and created two separate settings file: local_settings.py and prod_settings.py.

In manage.py I check whether the server is local server or production server. If it is a local server it would load up local_settings.py and it is a production server it would load up prod_settings.py. Basically this is how it would look like:

#!/usr/bin/env python
import sys
import socket
from django.core.management import execute_manager 

ipaddress = socket.gethostbyname( socket.gethostname() )
if ipaddress == '127.0.0.1':
    try:
        import local_settings # Assumed to be in the same directory.
        settings = local_settings
    except ImportError:
        import sys
        sys.stderr.write("Error: Can't find the file 'local_settings.py' in the directory containing %r. It appears you've customized things.\nYou'll have to run django-admin.py, passing it your settings module.\n(If the file local_settings.py does indeed exist, it's causing an ImportError somehow.)\n" % __file__)
        sys.exit(1)
else:
    try:
        import prod_settings # Assumed to be in the same directory.
        settings = prod_settings    
    except ImportError:
        import sys
        sys.stderr.write("Error: Can't find the file 'prod_settings.py' in the directory containing %r. It appears you've customized things.\nYou'll have to run django-admin.py, passing it your settings module.\n(If the file prod_settings.py does indeed exist, it's causing an ImportError somehow.)\n" % __file__)
        sys.exit(1)

if __name__ == "__main__":
    execute_manager(settings)

I found it to be easier to separate the settings file into two separate file instead of doing lots of ifs inside the settings file.


回答 18

如果愿意,也可以选择维护其他文件:如果使用git或任何其他VCS将代码从本地推送到服务器,则可以将设置文件添加到.gitignore。

这样您就可以在两个地方都拥有不同的内容,而不会出现任何问题。因此,在服务器上,您可以配置独立版本的settings.py,对本地所做的任何更改都不会反映在服务器上,反之亦然。

另外,它还会从github上删除settings.py文件,这是一个很大的错误,我看到很多新手都在这样做。

As an alternative to maintain different file if you wiil: If you are using git or any other VCS to push codes from local to server, what you can do is add the settings file to .gitignore.

This will allow you to have different content in both places without any problem. SO on server you can configure an independent version of settings.py and any changes made on the local wont reflect on server and vice versa.

In addition, it will remove the settings.py file from github also, the big fault, which i have seen many newbies doing.


回答 19

制作settings.py的多个版本是12 Factor App方法的反模式。使用python-decoupledjango-environ代替。

Making multiple versions of settings.py is an anti pattern for 12 Factor App methodology. use python-decouple or django-environ instead.


回答 20

我认为最好的解决方案是@T建议的。斯通,但我不知道为什么不在Django中使用DEBUG标志。我为我的网站编写以下代码:

if DEBUG:
    from .local_settings import *

总是简单的解决方案比复杂的解决方案好。

I think the best solution is suggested by @T. Stone, but I don’t know why just don’t use the DEBUG flag in Django. I Write the below code for my website:

if DEBUG:
    from .local_settings import *

Always the simple solutions are better than complex ones.


回答 21

我发现这里的回复非常有帮助。(是否已更明确地解决了此问题?上次答复是一年前。)在考虑了列出的所有方法之后,我想出了一个我未在此处列出的解决方案。

我的标准是:

  • 一切都应该在源代码控制中。我不喜欢随便摆姿势。
  • 理想情况下,将设置保存在一个文件中。如果我没看对的话我会忘记的:)
  • 无需手动编辑即可部署。应该能够使用单个结构命令进行测试/推送/部署。
  • 避免将开发设置泄漏到生产中。
  • 保持尽可能接近“标准”(*咳嗽*)Django布局。

我以为打开主机是有道理的,但后来发现这里的真正问题是针对不同环境的不同设置,并且花了很多时间。我把这个代码在结束我的settings.py文件中:

try:
    os.environ['DJANGO_DEVELOPMENT_SERVER'] # throws error if unset
    DEBUG = True
    TEMPLATE_DEBUG = True
    # This is naive but possible. Could also redeclare full app set to control ordering. 
    # Note that it requires a list rather than the generated tuple.
    INSTALLED_APPS.extend([
        'debug_toolbar',
        'django_nose',
    ])
    # Production database settings, alternate static/media paths, etc...
except KeyError: 
    print 'DJANGO_DEVELOPMENT_SERVER environment var not set; using production settings'

这样,该应用程序默认为生产设置,这意味着您将开发环境明确“列入白名单”。与在相反的情况下忘记在本地设置环境变量相比,如果忘记在生产环境中设置某些内容并使用某些开发设置,则要安全得多。

从外壳或.bash_profile或任何地方进行本地开发时:

$ export DJANGO_DEVELOPMENT_SERVER=yep

(或者,如果您是在Windows上进行开发,则可以通过“控制面板”或目前称为“控制面板”的工具进行设置。Windows总是使它晦涩难懂,因此您可以设置环境变量。)

使用这种方法,开发人员设置全部集中在一个(标准)位置,并在需要时仅覆盖生产设置。任何与开发设置有关的更改都应该完全安全地进行源代码控制,而不会影响生产。

I found the responses here very helpful. (Has this been more definitively solved? The last response was a year ago.) After considering all the approaches listed, I came up with a solution that I didn’t see listed here.

My criteria were:

  • Everything should be in source control. I don’t like fiddly bits lying around.
  • Ideally, keep settings in one file. I forget things if I’m not looking right at them :)
  • No manual edits to deploy. Should be able to test/push/deploy with a single fabric command.
  • Avoid leaking development settings into production.
  • Keep as close as possible to “standard” (*cough*) Django layout as possible.

I thought switching on the host machine made some sense, but then figured the real issue here is different settings for different environments, and had an aha moment. I put this code at the end of my settings.py file:

try:
    os.environ['DJANGO_DEVELOPMENT_SERVER'] # throws error if unset
    DEBUG = True
    TEMPLATE_DEBUG = True
    # This is naive but possible. Could also redeclare full app set to control ordering. 
    # Note that it requires a list rather than the generated tuple.
    INSTALLED_APPS.extend([
        'debug_toolbar',
        'django_nose',
    ])
    # Production database settings, alternate static/media paths, etc...
except KeyError: 
    print 'DJANGO_DEVELOPMENT_SERVER environment var not set; using production settings'

This way, the app defaults to production settings, which means you are explicitly “whitelisting” your development environment. It is much safer to forget to set the environment variable locally than if it were the other way around and you forgot to set something in production and let some dev settings be used.

When developing locally, either from the shell or in a .bash_profile or wherever:

$ export DJANGO_DEVELOPMENT_SERVER=yep

(Or if you’re developing on Windows, set via the Control Panel or whatever its called these days… Windows always made it so obscure that you could set environment variables.)

With this approach, the dev settings are all in one (standard) place, and simply override the production ones where needed. Any mucking around with development settings should be completely safe to commit to source control with no impact on production.


Django ModelAdmin中的“ list_display”可以显示ForeignKey字段的属性吗?

问题:Django ModelAdmin中的“ list_display”可以显示ForeignKey字段的属性吗?

我有一个Person模型,它与有一个外键关系Book,该模型有许多字段,但我最关心的是author(标准CharField)。

话虽如此,在我的PersonAdmin模型中,我想book.author使用显示list_display

class PersonAdmin(admin.ModelAdmin):
    list_display = ['book.author',]

我已经尝试了所有显而易见的方法来执行此操作,但是似乎没有任何效果。

有什么建议么?

I have a Person model that has a foreign key relationship to Book, which has a number of fields, but I’m most concerned about author (a standard CharField).

With that being said, in my PersonAdmin model, I’d like to display book.author using list_display:

class PersonAdmin(admin.ModelAdmin):
    list_display = ['book.author',]

I’ve tried all of the obvious methods for doing so, but nothing seems to work.

Any suggestions?


回答 0

作为另一种选择,您可以进行如下查找:

class UserAdmin(admin.ModelAdmin):
    list_display = (..., 'get_author')

    def get_author(self, obj):
        return obj.book.author
    get_author.short_description = 'Author'
    get_author.admin_order_field = 'book__author'

As another option, you can do look ups like:

class UserAdmin(admin.ModelAdmin):
    list_display = (..., 'get_author')

    def get_author(self, obj):
        return obj.book.author
    get_author.short_description = 'Author'
    get_author.admin_order_field = 'book__author'

回答 1

尽管上面有很多很棒的答案,但由于我是Django的新手,所以我仍然受困。这是我从一个新手的角度进行的解释。

models.py

class Author(models.Model):
    name = models.CharField(max_length=255)

class Book(models.Model):
    author = models.ForeignKey(Author)
    title = models.CharField(max_length=255)

admin.py(不正确的方式) -您认为使用’model__field’进行引用可以正常工作,但它不起作用

class BookAdmin(admin.ModelAdmin):
    model = Book
    list_display = ['title', 'author__name', ]

admin.site.register(Book, BookAdmin)

admin.py(正确的方式) -这就是您以Django方式引用外键名称的方式

class BookAdmin(admin.ModelAdmin):
    model = Book
    list_display = ['title', 'get_name', ]

    def get_name(self, obj):
        return obj.author.name
    get_name.admin_order_field  = 'author'  #Allows column order sorting
    get_name.short_description = 'Author Name'  #Renames column head

    #Filtering on side - for some reason, this works
    #list_filter = ['title', 'author__name']

admin.site.register(Book, BookAdmin)

有关其他参考,请参见此处的Django模型链接

Despite all the great answers above and due to me being new to Django, I was still stuck. Here’s my explanation from a very newbie perspective.

models.py

class Author(models.Model):
    name = models.CharField(max_length=255)

class Book(models.Model):
    author = models.ForeignKey(Author)
    title = models.CharField(max_length=255)

admin.py (Incorrect Way) – you think it would work by using ‘model__field’ to reference, but it doesn’t

class BookAdmin(admin.ModelAdmin):
    model = Book
    list_display = ['title', 'author__name', ]

admin.site.register(Book, BookAdmin)

admin.py (Correct Way) – this is how you reference a foreign key name the Django way

class BookAdmin(admin.ModelAdmin):
    model = Book
    list_display = ['title', 'get_name', ]

    def get_name(self, obj):
        return obj.author.name
    get_name.admin_order_field  = 'author'  #Allows column order sorting
    get_name.short_description = 'Author Name'  #Renames column head

    #Filtering on side - for some reason, this works
    #list_filter = ['title', 'author__name']

admin.site.register(Book, BookAdmin)

For additional reference, see the Django model link here


回答 2

和其余的一样,我也使用可调用对象。但是它们有一个缺点:默认情况下,您无法订购它们。幸运的是,有一个解决方案:

的Django> = 1.8

def author(self, obj):
    return obj.book.author
author.admin_order_field  = 'book__author'

Django <1.8

def author(self):
    return self.book.author
author.admin_order_field  = 'book__author'

Like the rest, I went with callables too. But they have one downside: by default, you can’t order on them. Fortunately, there is a solution for that:

Django >= 1.8

def author(self, obj):
    return obj.book.author
author.admin_order_field  = 'book__author'

Django < 1.8

def author(self):
    return self.book.author
author.admin_order_field  = 'book__author'

回答 3

请注意,添加该get_author功能会减慢admin中的list_display速度,因为显示每个人都会进行SQL查询。

为了避免这种情况,您需要get_queryset在PersonAdmin中修改方法,例如:

def get_queryset(self, request):
    return super(PersonAdmin,self).get_queryset(request).select_related('book')

之前:36.02毫秒内有73个查询(管理员中有67个重复查询)

之后:10.81毫秒内进行6次查询

Please note that adding the get_author function would slow the list_display in the admin, because showing each person would make a SQL query.

To avoid this, you need to modify get_queryset method in PersonAdmin, for example:

def get_queryset(self, request):
    return super(PersonAdmin,self).get_queryset(request).select_related('book')

Before: 73 queries in 36.02ms (67 duplicated queries in admin)

After: 6 queries in 10.81ms


回答 4

根据文档,您只能显示外键的__unicode__表示形式:

http://docs.djangoproject.com/en/dev/ref/contrib/admin/#list-display

似乎很奇怪,它不支持'book__author'DB API其他地方使用的样式格式。

原来有一张用于此功能的票证,标记为“无法修复”。

According to the documentation, you can only display the __unicode__ representation of a ForeignKey:

http://docs.djangoproject.com/en/dev/ref/contrib/admin/#list-display

Seems odd that it doesn’t support the 'book__author' style format which is used everywhere else in the DB API.

Turns out there’s a ticket for this feature, which is marked as Won’t Fix.


回答 5

我刚刚发布了一个片段,使admin.ModelAdmin支持’__’语法:

http://djangosnippets.org/snippets/2887/

因此,您可以执行以下操作:

class PersonAdmin(RelatedFieldAdmin):
    list_display = ['book__author',]

基本上,这只是在做其他答案中描述的相同操作,但是它会自动处理(1)设置admin_order_field(2)设置short_description以及(3)修改查询集以避免每一行都有数据库命中。

I just posted a snippet that makes admin.ModelAdmin support ‘__’ syntax:

http://djangosnippets.org/snippets/2887/

So you can do:

class PersonAdmin(RelatedFieldAdmin):
    list_display = ['book__author',]

This is basically just doing the same thing described in the other answers, but it automatically takes care of (1) setting admin_order_field (2) setting short_description and (3) modifying the queryset to avoid a database hit for each row.


回答 6

您可以使用可调用对象在列表显示中显示所需的任何内容。它看起来像这样:

def book_author(object):
  返回object.book.author

类PersonAdmin(admin.ModelAdmin):
  list_display = [book_author,]

You can show whatever you want in list display by using a callable. It would look like this:


def book_author(object):
  return object.book.author

class PersonAdmin(admin.ModelAdmin):
  list_display = [book_author,]

回答 7

这个已经被接受了,但是如果还有其他假人(像我一样)没有立即从当前接受的答案中得到答案,那么这里有更多细节。

ForeignKey需要引用的模型类在其中需要一个__unicode__方法,例如:

class Category(models.Model):
    name = models.CharField(max_length=50)

    def __unicode__(self):
        return self.name

这对我来说很重要,应该适用于上述情况。这适用于Django 1.0.2。

This one’s already accepted, but if there are any other dummies out there (like me) that didn’t immediately get it from the presently accepted answer, here’s a bit more detail.

The model class referenced by the ForeignKey needs to have a __unicode__ method within it, like here:

class Category(models.Model):
    name = models.CharField(max_length=50)

    def __unicode__(self):
        return self.name

That made the difference for me, and should apply to the above scenario. This works on Django 1.0.2.


回答 8

如果您有很多关联属性字段供使用,list_display并且不想为每个函数创建一个函数(及其属性),那么一个肮脏而简单的解决方案将覆盖ModelAdmininstace __getattr__方法,即刻创建可调用对象:

class DynamicLookupMixin(object):
    '''
    a mixin to add dynamic callable attributes like 'book__author' which
    return a function that return the instance.book.author value
    '''

    def __getattr__(self, attr):
        if ('__' in attr
            and not attr.startswith('_')
            and not attr.endswith('_boolean')
            and not attr.endswith('_short_description')):

            def dyn_lookup(instance):
                # traverse all __ lookups
                return reduce(lambda parent, child: getattr(parent, child),
                              attr.split('__'),
                              instance)

            # get admin_order_field, boolean and short_description
            dyn_lookup.admin_order_field = attr
            dyn_lookup.boolean = getattr(self, '{}_boolean'.format(attr), False)
            dyn_lookup.short_description = getattr(
                self, '{}_short_description'.format(attr),
                attr.replace('_', ' ').capitalize())

            return dyn_lookup

        # not dynamic lookup, default behaviour
        return self.__getattribute__(attr)


# use examples    

@admin.register(models.Person)
class PersonAdmin(admin.ModelAdmin, DynamicLookupMixin):
    list_display = ['book__author', 'book__publisher__name',
                    'book__publisher__country']

    # custom short description
    book__publisher__country_short_description = 'Publisher Country'


@admin.register(models.Product)
class ProductAdmin(admin.ModelAdmin, DynamicLookupMixin):
    list_display = ('name', 'category__is_new')

    # to show as boolean field
    category__is_new_boolean = True

作为要点

可调用的特殊属性,例如booleanshort_description必须定义为ModelAdmin属性,例如book__author_verbose_name = 'Author name'category__is_new_boolean = True

callable admin_order_field属性是自动定义的。

不要忘记在您的列表中使用list_select_related属性,ModelAdmin以使Django避免常规查询。

If you have a lot of relation attribute fields to use in list_display and do not want create a function (and it’s attributes) for each one, a dirt but simple solution would be override the ModelAdmin instace __getattr__ method, creating the callables on the fly:

class DynamicLookupMixin(object):
    '''
    a mixin to add dynamic callable attributes like 'book__author' which
    return a function that return the instance.book.author value
    '''

    def __getattr__(self, attr):
        if ('__' in attr
            and not attr.startswith('_')
            and not attr.endswith('_boolean')
            and not attr.endswith('_short_description')):

            def dyn_lookup(instance):
                # traverse all __ lookups
                return reduce(lambda parent, child: getattr(parent, child),
                              attr.split('__'),
                              instance)

            # get admin_order_field, boolean and short_description
            dyn_lookup.admin_order_field = attr
            dyn_lookup.boolean = getattr(self, '{}_boolean'.format(attr), False)
            dyn_lookup.short_description = getattr(
                self, '{}_short_description'.format(attr),
                attr.replace('_', ' ').capitalize())

            return dyn_lookup

        # not dynamic lookup, default behaviour
        return self.__getattribute__(attr)


# use examples    

@admin.register(models.Person)
class PersonAdmin(admin.ModelAdmin, DynamicLookupMixin):
    list_display = ['book__author', 'book__publisher__name',
                    'book__publisher__country']

    # custom short description
    book__publisher__country_short_description = 'Publisher Country'


@admin.register(models.Product)
class ProductAdmin(admin.ModelAdmin, DynamicLookupMixin):
    list_display = ('name', 'category__is_new')

    # to show as boolean field
    category__is_new_boolean = True

As gist here

Callable especial attributes like boolean and short_description must be defined as ModelAdmin attributes, eg book__author_verbose_name = 'Author name' and category__is_new_boolean = True.

The callable admin_order_field attribute is defined automatically.

Don’t forget to use the list_select_related attribute in your ModelAdmin to make Django avoid aditional queries.


回答 9

PyPI中有一个非常易于使用的软件包,可以准确地处理该软件包:django-related-admin。您还可以在GitHub中查看代码

使用此功能,您想要实现的过程很简单:

class PersonAdmin(RelatedFieldAdmin):
    list_display = ['book__author',]

这两个链接均包含安装和使用的完整详细信息,因此,如果它们发生更改,我就不会在此处粘贴它们。

顺便提一句,如果您已经使用了其他工具model.Admin(例如,我正在使用SimpleHistoryAdmin),则可以执行以下操作:class MyAdmin(SimpleHistoryAdmin, RelatedFieldAdmin)

There is a very easy to use package available in PyPI that handles exactly that: django-related-admin. You can also see the code in GitHub.

Using this, what you want to achieve is as simple as:

class PersonAdmin(RelatedFieldAdmin):
    list_display = ['book__author',]

Both links contain full details of installation and usage so I won’t paste them here in case they change.

Just as a side note, if you’re already using something other than model.Admin (e.g. I was using SimpleHistoryAdmin instead), you can do this: class MyAdmin(SimpleHistoryAdmin, RelatedFieldAdmin).


回答 10

如果您在Inline中尝试,除非以下条件,否则您不会成功:

在您的内联中:

class AddInline(admin.TabularInline):
    readonly_fields = ['localname',]
    model = MyModel
    fields = ('localname',)

在您的模型(MyModel)中:

class MyModel(models.Model):
    localization = models.ForeignKey(Localizations)

    def localname(self):
        return self.localization.name

if you try it in Inline, you wont succeed unless:

in your inline:

class AddInline(admin.TabularInline):
    readonly_fields = ['localname',]
    model = MyModel
    fields = ('localname',)

in your model (MyModel):

class MyModel(models.Model):
    localization = models.ForeignKey(Localizations)

    def localname(self):
        return self.localization.name

回答 11

AlexRobbins的答案对我有用,除了前两行需要在模型中(也许这是假设的?),并且应该引用self:

def book_author(self):
  return self.book.author

然后管理部分可以很好地工作。

AlexRobbins’ answer worked for me, except that the first two lines need to be in the model (perhaps this was assumed?), and should reference self:

def book_author(self):
  return self.book.author

Then the admin part works nicely.


回答 12

我更喜欢这样:

class CoolAdmin(admin.ModelAdmin):
    list_display = ('pk', 'submodel__field')

    @staticmethod
    def submodel__field(obj):
        return obj.submodel.field

I prefer this:

class CoolAdmin(admin.ModelAdmin):
    list_display = ('pk', 'submodel__field')

    @staticmethod
    def submodel__field(obj):
        return obj.submodel.field

将NumPy数组转换为Python List结构?

问题:将NumPy数组转换为Python List结构?

如何将NumPy数组转换为Python列表(例如[[1,2,3],[4,5,6]]),并且速度相当快?

How do I convert a NumPy array to a Python List (for example [[1,2,3],[4,5,6]] ), and do it reasonably fast?


回答 0

用途tolist()

import numpy as np
>>> np.array([[1,2,3],[4,5,6]]).tolist()
[[1, 2, 3], [4, 5, 6]]

请注意,这会将值从它们可能具有的任何numpy类型(例如np.int32或np.float32)转换为“最近兼容的Python类型”(在列表中)。如果要保留numpy数据类型,则可以在数组上调用list(),最后得到numpy标量列表。(感谢Mr_and_Mrs_D在评论中指出这一点。)

Use tolist():

import numpy as np
>>> np.array([[1,2,3],[4,5,6]]).tolist()
[[1, 2, 3], [4, 5, 6]]

Note that this converts the values from whatever numpy type they may have (e.g. np.int32 or np.float32) to the “nearest compatible Python type” (in a list). If you want to preserve the numpy data types, you could call list() on your array instead, and you’ll end up with a list of numpy scalars. (Thanks to Mr_and_Mrs_D for pointing that out in a comment.)


回答 1

如果numpy数组形状为2D,则numpy .tolist方法将生成嵌套列表。

如果需要平面列表,则可以使用以下方法。

import numpy as np
from itertools import chain

a = [1,2,3,4,5,6,7,8,9]
print type(a), len(a), a
npa = np.asarray(a)
print type(npa), npa.shape, "\n", npa
npa = npa.reshape((3, 3))
print type(npa), npa.shape, "\n", npa
a = list(chain.from_iterable(npa))
print type(a), len(a), a`

The numpy .tolist method produces nested lists if the numpy array shape is 2D.

if flat lists are desired, the method below works.

import numpy as np
from itertools import chain

a = [1,2,3,4,5,6,7,8,9]
print type(a), len(a), a
npa = np.asarray(a)
print type(npa), npa.shape, "\n", npa
npa = npa.reshape((3, 3))
print type(npa), npa.shape, "\n", npa
a = list(chain.from_iterable(npa))
print type(a), len(a), a`

回答 2

tolist()熊猫说,即使遇到嵌套数组,也可以正常工作DataFrame

my_list = [0,1,2,3,4,5,4,3,2,1,0]
my_dt = pd.DataFrame(my_list)
new_list = [i[0] for i in my_dt.values.tolist()]

print(type(my_list),type(my_dt),type(new_list))

tolist() works fine even if encountered a nested array, say a pandas DataFrame;

my_list = [0,1,2,3,4,5,4,3,2,1,0]
my_dt = pd.DataFrame(my_list)
new_list = [i[0] for i in my_dt.values.tolist()]

print(type(my_list),type(my_dt),type(new_list))

回答 3

someList = [list(map(int, input().split())) for i in range(N)]

someList = [list(map(int, input().split())) for i in range(N)]


回答 4

c = np.array([[1,2,3],[4,5,6]])

list(c.flatten())

c = np.array([[1,2,3],[4,5,6]])

list(c.flatten())

在迭代字典时如何从字典中删除项目?

问题:在迭代字典时如何从字典中删除项目?

在Python上进行迭代时从字典中删除项目是否合法?

例如:

for k, v in mydict.iteritems():
   if k == val:
     del mydict[k]

这个想法是从字典中删除不满足特定条件的元素,而不是创建一个新字典,该字典是被迭代的字典的子集。

这是一个好的解决方案吗?有没有更优雅/更有效的方法?

Is it legitimate to delete items from a dictionary in Python while iterating over it?

For example:

for k, v in mydict.iteritems():
   if k == val:
     del mydict[k]

The idea is to remove elements that don’t meet a certain condition from the dictionary, instead of creating a new dictionary that’s a subset of the one being iterated over.

Is this a good solution? Are there more elegant/efficient ways?


回答 0

编辑:

此答案不适用于Python3,并且会给出RuntimeError

RuntimeError:词典在迭代过程中更改了大小。

发生这种情况是因为mydict.keys()返回的是迭代器而不是列表。正如注释中所指出的那样,只需将其转换mydict.keys()为列表即可list(mydict.keys()),它应该可以工作。


控制台中的一个简单测试显示,在迭代字典时您无法修改字典:

>>> mydict = {'one': 1, 'two': 2, 'three': 3, 'four': 4}
>>> for k, v in mydict.iteritems():
...    if k == 'two':
...        del mydict[k]
...
------------------------------------------------------------
Traceback (most recent call last):
  File "<ipython console>", line 1, in <module>
RuntimeError: dictionary changed size during iteration

如delnan的回答所述,当迭代器尝试移至下一个条目时,删除条目会导致问题。而是使用keys()方法获取键列表并进行处理:

>>> for k in mydict.keys():
...    if k == 'two':
...        del mydict[k]
...
>>> mydict
{'four': 4, 'three': 3, 'one': 1}

如果需要根据项目值删除,请使用items()方法:

>>> for k, v in mydict.items():
...     if v == 3:
...         del mydict[k]
...
>>> mydict
{'four': 4, 'one': 1}

EDIT:

This answer will not work for Python3 and will give a RuntimeError.

RuntimeError: dictionary changed size during iteration.

This happens because mydict.keys() returns an iterator not a list. As pointed out in comments simply convert mydict.keys() to a list by list(mydict.keys()) and it should work.


A simple test in the console shows you cannot modify a dictionary while iterating over it:

>>> mydict = {'one': 1, 'two': 2, 'three': 3, 'four': 4}
>>> for k, v in mydict.iteritems():
...    if k == 'two':
...        del mydict[k]
...
------------------------------------------------------------
Traceback (most recent call last):
  File "<ipython console>", line 1, in <module>
RuntimeError: dictionary changed size during iteration

As stated in delnan’s answer, deleting entries causes problems when the iterator tries to move onto the next entry. Instead, use the keys() method to get a list of the keys and work with that:

>>> for k in mydict.keys():
...    if k == 'two':
...        del mydict[k]
...
>>> mydict
{'four': 4, 'three': 3, 'one': 1}

If you need to delete based on the items value, use the items() method instead:

>>> for k, v in mydict.items():
...     if v == 3:
...         del mydict[k]
...
>>> mydict
{'four': 4, 'one': 1}

回答 1

您也可以分两个步骤进行操作:

remove = [k for k in mydict if k == val]
for k in remove: del mydict[k]

我最喜欢的方法通常是做出一个新的决定:

# Python 2.7 and 3.x
mydict = { k:v for k,v in mydict.items() if k!=val }
# before Python 2.7
mydict = dict((k,v) for k,v in mydict.iteritems() if k!=val)

You could also do it in two steps:

remove = [k for k in mydict if k == val]
for k in remove: del mydict[k]

My favorite approach is usually to just make a new dict:

# Python 2.7 and 3.x
mydict = { k:v for k,v in mydict.items() if k!=val }
# before Python 2.7
mydict = dict((k,v) for k,v in mydict.iteritems() if k!=val)

回答 2

迭代时不能修改集合。那就是疯狂-最为明显的是,如果允许您删除和删除当前项目,则迭代器将必须继续(+1),下一次调用next将使您超出该范围(+2),因此您会最终跳过了一个元素(删除的元素后面的一个)。您有两种选择:

  • 复制所有键(或值,或两者,取决于您的需要),然后遍历这些键。您可以.keys()为此使用et al(在Python 3中,将生成的迭代器传递给list)。但是在空间上可能会非常浪费。
  • mydict照常进行迭代,将要保存的密钥保存在单独的collection中to_delete。当你完成迭代mydict,删除所有项目to_deletemydict。与第一种方法相比,可以节省一些(取决于删除的键数和剩余的键数)空间,但还需要多几行。

You can’t modify a collection while iterating it. That way lies madness – most notably, if you were allowed to delete and deleted the current item, the iterator would have to move on (+1) and the next call to next would take you beyond that (+2), so you’d end up skipping one element (the one right behind the one you deleted). You have two options:

  • Copy all keys (or values, or both, depending on what you need), then iterate over those. You can use .keys() et al for this (in Python 3, pass the resulting iterator to list). Could be highly wasteful space-wise though.
  • Iterate over mydict as usual, saving the keys to delete in a seperate collection to_delete. When you’re done iterating mydict, delete all items in to_delete from mydict. Saves some (depending on how many keys are deleted and how many stay) space over the first approach, but also requires a few more lines.

回答 3

而是遍历一个副本,例如items()

for k, v in list(mydict.items()):

Iterate over a copy instead, such as the one returned by items():

for k, v in list(mydict.items()):

回答 4

使用起来最干净list(mydict)

>>> mydict = {'one': 1, 'two': 2, 'three': 3, 'four': 4}
>>> for k in list(mydict):
...     if k == 'three':
...         del mydict[k]
... 
>>> mydict
{'four': 4, 'two': 2, 'one': 1}

这对应于列表的并行结构:

>>> mylist = ['one', 'two', 'three', 'four']
>>> for k in list(mylist):                            # or mylist[:]
...     if k == 'three':
...         mylist.remove(k)
... 
>>> mylist
['one', 'two', 'four']

两者都在python2和python3中工作。

It’s cleanest to use list(mydict):

>>> mydict = {'one': 1, 'two': 2, 'three': 3, 'four': 4}
>>> for k in list(mydict):
...     if k == 'three':
...         del mydict[k]
... 
>>> mydict
{'four': 4, 'two': 2, 'one': 1}

This corresponds to a parallel structure for lists:

>>> mylist = ['one', 'two', 'three', 'four']
>>> for k in list(mylist):                            # or mylist[:]
...     if k == 'three':
...         mylist.remove(k)
... 
>>> mylist
['one', 'two', 'four']

Both work in python2 and python3.


回答 5

您可以使用字典理解。

d = {k:d[k] for k in d if d[k] != val}

You can use a dictionary comprehension.

d = {k:d[k] for k in d if d[k] != val}


回答 6

使用python3,在dic.keys()上进行迭代将引发字典大小错误。您可以使用这种替代方式:

使用python3进行测试,它可以正常工作,并且不会引发错误“ 字典在迭代期间更改大小 ”:

my_dic = { 1:10, 2:20, 3:30 }
# Is important here to cast because ".keys()" method returns a dict_keys object.
key_list = list( my_dic.keys() )

# Iterate on the list:
for k in key_list:
    print(key_list)
    print(my_dic)
    del( my_dic[k] )


print( my_dic )
# {}

With python3, iterate on dic.keys() will raise the dictionary size error. You can use this alternative way:

Tested with python3, it works fine and the Error “dictionary changed size during iteration” is not raised:

my_dic = { 1:10, 2:20, 3:30 }
# Is important here to cast because ".keys()" method returns a dict_keys object.
key_list = list( my_dic.keys() )

# Iterate on the list:
for k in key_list:
    print(key_list)
    print(my_dic)
    del( my_dic[k] )


print( my_dic )
# {}

回答 7

您可以先构建要删除的键列表,然后遍历该列表以删除它们。

dict = {'one' : 1, 'two' : 2, 'three' : 3, 'four' : 4}
delete = []
for k,v in dict.items():
    if v%2 == 1:
        delete.append(k)
for i in delete:
    del dict[i]

You could first build a list of keys to delete, and then iterate over that list deleting them.

dict = {'one' : 1, 'two' : 2, 'three' : 3, 'four' : 4}
delete = []
for k,v in dict.items():
    if v%2 == 1:
        delete.append(k)
for i in delete:
    del dict[i]

回答 8

如果您要删除的项目始终位于dict迭代的“开始”,则有一种方法可能合适

while mydict:
    key, value = next(iter(mydict.items()))
    if should_delete(key, value):
       del mydict[key]
    else:
       break

仅保证“开始”对于某些Python版本/实现是一致的。例如,Python 3.7新增功能

dict对象的插入顺序保留性质已声明是Python语言规范的正式组成部分。

这种方式避免了很多其他答案所暗示的dict副本,至少在Python 3中如此。

There is a way that may be suitable if the items you want to delete are always at the “beginning” of the dict iteration

while mydict:
    key, value = next(iter(mydict.items()))
    if should_delete(key, value):
       del mydict[key]
    else:
       break

The “beginning” is only guaranteed to be consistent for certain Python versions/implementations. For example from What’s New In Python 3.7

the insertion-order preservation nature of dict objects has been declared to be an official part of the Python language spec.

This way avoids a copy of the dict that a lot of the other answers suggest, at least in Python 3.


回答 9

我在Python3中尝试了上述解决方案,但在将对象存储在dict中时,似乎这是唯一对我有用的解决方案。基本上,您会复制dict()并对其进行迭代,同时删除原始词典中的条目。

        tmpDict = realDict.copy()
        for key, value in tmpDict.items():
            if value:
                del(realDict[key])

I tried the above solutions in Python3 but this one seems to be the only one working for me when storing objects in a dict. Basically you make a copy of your dict() and iterate over that while deleting the entries in your original dictionary.

        tmpDict = realDict.copy()
        for key, value in tmpDict.items():
            if value:
                del(realDict[key])

在Python中将datetime.date转换为UTC时间戳

问题:在Python中将datetime.date转换为UTC时间戳

我正在使用Python处理日期,因此需要将其转换为UTC时间戳以在Javascript中使用。以下代码不起作用:

>>> d = datetime.date(2011,01,01)
>>> datetime.datetime.utcfromtimestamp(time.mktime(d.timetuple()))
datetime.datetime(2010, 12, 31, 23, 0)

首先将日期对象转换为datetime也无济于事。我从以下链接尝试了此示例,但是:

from pytz import utc, timezone
from datetime import datetime
from time import mktime
input_date = datetime(year=2011, month=1, day=15)

现在要么:

mktime(utc.localize(input_date).utctimetuple())

要么

mktime(timezone('US/Eastern').localize(input_date).utctimetuple())

确实有效。

如此普遍的问题:如何根据UTC将日期转换为自纪元以来的秒数?

I am dealing with dates in Python and I need to convert them to UTC timestamps to be used inside Javascript. The following code does not work:

>>> d = datetime.date(2011,01,01)
>>> datetime.datetime.utcfromtimestamp(time.mktime(d.timetuple()))
datetime.datetime(2010, 12, 31, 23, 0)

Converting the date object first to datetime also does not help. I tried the example at this link from, but:

from pytz import utc, timezone
from datetime import datetime
from time import mktime
input_date = datetime(year=2011, month=1, day=15)

and now either:

mktime(utc.localize(input_date).utctimetuple())

or

mktime(timezone('US/Eastern').localize(input_date).utctimetuple())

does work.

So general question: how can I get a date converted to seconds since epoch according to UTC?


回答 0

如果d = date(2011, 1, 1)使用UTC:

>>> from datetime import datetime, date
>>> import calendar
>>> timestamp1 = calendar.timegm(d.timetuple())
>>> datetime.utcfromtimestamp(timestamp1)
datetime.datetime(2011, 1, 1, 0, 0)

如果d在当地时区:

>>> import time
>>> timestamp2 = time.mktime(d.timetuple()) # DO NOT USE IT WITH UTC DATE
>>> datetime.fromtimestamp(timestamp2)
datetime.datetime(2011, 1, 1, 0, 0)

timestamp1timestamp2如果午夜在本地时区是不一样的时间实例作为午夜UTC可能会有所不同。

mktime()如果d对应于一个不明确的本地时间(例如,在DST过渡期间),或者d是utc偏移可能已经不同并且 C mktime()无法访问给定平台上的tz数据库的过去(未来)日期,则可能返回错误结果。您可以使用pytz模块(例如via tzlocal.get_localzone())来访问所有平台上的tz数据库。此外,如果使用timezone,则utcfromtimestamp()可能会失败并mktime()可能返回非POSIX时间戳"right"


要转换datetime.date不使用UTC表示日期的对象calendar.timegm()

DAY = 24*60*60 # POSIX day in seconds (exact value)
timestamp = (utc_date.toordinal() - date(1970, 1, 1).toordinal()) * DAY
timestamp = (utc_date - date(1970, 1, 1)).days * DAY

如何根据UTC将日期转换为自纪元以来的秒数?

将已经以UTC表示时间的对象datetime.datetime(不是datetime.date)转换为相应的POSIX时间戳(a float)。

Python 3.3以上

datetime.timestamp()

from datetime import timezone

timestamp = dt.replace(tzinfo=timezone.utc).timestamp()

注意:有必要timezone.utc明确地提供其他条件,.timestamp()假设您朴素的datetime对象位于本地时区。

Python 3(<3.3)

从文档中获取datetime.utcfromtimestamp()

没有从日期时间实例获取时间戳的方法,但是可以很容易地如下计算对应于日期时间实例dt的POSIX时间戳。对于幼稚的dt:

timestamp = (dt - datetime(1970, 1, 1)) / timedelta(seconds=1)

对于有意识的dt:

timestamp = (dt - datetime(1970,1,1, tzinfo=timezone.utc)) / timedelta(seconds=1)

有趣的读物:大纪元时间与一天中的时间之间的时差是几点?多少秒过去了?

另请参见:datetime需要一种“时代”方法

Python 2

为了使以上代码适用于Python 2:

timestamp = (dt - datetime(1970, 1, 1)).total_seconds()

其中timedelta.total_seconds()等于在(td.microseconds + (td.seconds + td.days * 24 * 3600) * 10**6) / 10**6启用真除法的情况下进行的计算。

from __future__ import division
from datetime import datetime, timedelta

def totimestamp(dt, epoch=datetime(1970,1,1)):
    td = dt - epoch
    # return td.total_seconds()
    return (td.microseconds + (td.seconds + td.days * 86400) * 10**6) / 10**6 

now = datetime.utcnow()
print now
print totimestamp(now)

当心浮点问题

输出量

2012-01-08 15:34:10.022403
1326036850.02

如何将感知datetime对象转换为POSIX时间戳

assert dt.tzinfo is not None and dt.utcoffset() is not None
timestamp = dt.timestamp() # Python 3.3+

在Python 3上:

from datetime import datetime, timedelta, timezone

epoch = datetime(1970, 1, 1, tzinfo=timezone.utc)
timestamp = (dt - epoch) / timedelta(seconds=1)
integer_timestamp = (dt - epoch) // timedelta(seconds=1)

在Python 2上:

# utc time = local time              - utc offset
utc_naive  = dt.replace(tzinfo=None) - dt.utcoffset()
timestamp = (utc_naive - datetime(1970, 1, 1)).total_seconds()

If d = date(2011, 1, 1) is in UTC:

>>> from datetime import datetime, date
>>> import calendar
>>> timestamp1 = calendar.timegm(d.timetuple())
>>> datetime.utcfromtimestamp(timestamp1)
datetime.datetime(2011, 1, 1, 0, 0)

If d is in local timezone:

>>> import time
>>> timestamp2 = time.mktime(d.timetuple()) # DO NOT USE IT WITH UTC DATE
>>> datetime.fromtimestamp(timestamp2)
datetime.datetime(2011, 1, 1, 0, 0)

timestamp1 and timestamp2 may differ if midnight in the local timezone is not the same time instance as midnight in UTC.

mktime() may return a wrong result if d corresponds to an ambiguous local time (e.g., during DST transition) or if d is a past(future) date when the utc offset might have been different and the C mktime() has no access to the tz database on the given platform. You could use pytz module (e.g., via tzlocal.get_localzone()) to get access to the tz database on all platforms. Also, utcfromtimestamp() may fail and mktime() may return non-POSIX timestamp if "right" timezone is used.


To convert datetime.date object that represents date in UTC without calendar.timegm():

DAY = 24*60*60 # POSIX day in seconds (exact value)
timestamp = (utc_date.toordinal() - date(1970, 1, 1).toordinal()) * DAY
timestamp = (utc_date - date(1970, 1, 1)).days * DAY

How can I get a date converted to seconds since epoch according to UTC?

To convert datetime.datetime (not datetime.date) object that already represents time in UTC to the corresponding POSIX timestamp (a float).

Python 3.3+

datetime.timestamp():

from datetime import timezone

timestamp = dt.replace(tzinfo=timezone.utc).timestamp()

Note: It is necessary to supply timezone.utc explicitly otherwise .timestamp() assume that your naive datetime object is in local timezone.

Python 3 (< 3.3)

From the docs for datetime.utcfromtimestamp():

There is no method to obtain the timestamp from a datetime instance, but POSIX timestamp corresponding to a datetime instance dt can be easily calculated as follows. For a naive dt:

timestamp = (dt - datetime(1970, 1, 1)) / timedelta(seconds=1)

And for an aware dt:

timestamp = (dt - datetime(1970,1,1, tzinfo=timezone.utc)) / timedelta(seconds=1)

Interesting read: Epoch time vs. time of day on the difference between What time is it? and How many seconds have elapsed?

See also: datetime needs an “epoch” method

Python 2

To adapt the above code for Python 2:

timestamp = (dt - datetime(1970, 1, 1)).total_seconds()

where timedelta.total_seconds() is equivalent to (td.microseconds + (td.seconds + td.days * 24 * 3600) * 10**6) / 10**6 computed with true division enabled.

Example

from __future__ import division
from datetime import datetime, timedelta

def totimestamp(dt, epoch=datetime(1970,1,1)):
    td = dt - epoch
    # return td.total_seconds()
    return (td.microseconds + (td.seconds + td.days * 86400) * 10**6) / 10**6 

now = datetime.utcnow()
print now
print totimestamp(now)

Beware of floating-point issues.

Output

2012-01-08 15:34:10.022403
1326036850.02

How to convert an aware datetime object to POSIX timestamp

assert dt.tzinfo is not None and dt.utcoffset() is not None
timestamp = dt.timestamp() # Python 3.3+

On Python 3:

from datetime import datetime, timedelta, timezone

epoch = datetime(1970, 1, 1, tzinfo=timezone.utc)
timestamp = (dt - epoch) / timedelta(seconds=1)
integer_timestamp = (dt - epoch) // timedelta(seconds=1)

On Python 2:

# utc time = local time              - utc offset
utc_naive  = dt.replace(tzinfo=None) - dt.utcoffset()
timestamp = (utc_naive - datetime(1970, 1, 1)).total_seconds()

回答 1

适用于Unix系统

>>> import datetime
>>> d = datetime.date(2011,01,01)
>>> d.strftime("%s")  # <-- THIS IS THE CODE YOU WANT
'1293832800'

注1: dizzyf观察到这适用于本地时区。不要在生产中使用。

注意2: JakubNarębski指出,即使对于具有偏移量的日期时间,它也会忽略时区信息(已针对Python 2.7测试)。

For unix systems only:

>>> import datetime
>>> d = datetime.date(2011,01,01)
>>> d.strftime("%s")  # <-- THIS IS THE CODE YOU WANT
'1293832800'

Note 1: dizzyf observed that this applies localized timezones. Don’t use in production.

Note 2: Jakub Narębski noted that this ignores timezone information even for offset-aware datetime (tested for Python 2.7).


回答 2

  • 假设1:您正在尝试将日期转换为时间戳,但是由于日期涵盖24小时,因此没有一个代表该日期的时间戳。我假设您要代表该日期的时间戳记为午夜(00:00:00.000)。

  • 假设2:您提供的日期与特定时区没有关联,但是您想确定与特定时区(UTC)的偏移量。如果不知道日期所在的时区,就无法为特定时区计算时间戳。我假设您想将日期当作本地​​系统时区中的日期。

首先,您可以使用timetuple()成员将日期实例转换为代表各种时间成分的元组:

dtt = d.timetuple() # time.struct_time(tm_year=2011, tm_mon=1, tm_mday=1, tm_hour=0, tm_min=0, tm_sec=0, tm_wday=5, tm_yday=1, tm_isdst=-1)

然后,您可以使用将该时间戳转换为时间戳time.mktime

ts = time.mktime(dtt) # 1293868800.0

您可以通过使用纪元时间本身(1970-01-01)对其进行测试来验证此方法,在这种情况下,该函数应返回该日期的本地时区的时区偏移量:

d = datetime.date(1970,1,1)
dtt = d.timetuple() # time.struct_time(tm_year=1970, tm_mon=1, tm_mday=1, tm_hour=0, tm_min=0, tm_sec=0, tm_wday=3, tm_yday=1, tm_isdst=-1)
ts = time.mktime(dtt) # 28800.0

28800.0 是8个小时,这对于太平洋时区(我所在的时间)是正确的。

  • Assumption 1: You’re attempting to convert a date to a timestamp, however since a date covers a 24 hour period, there isn’t a single timestamp that represents that date. I’ll assume that you want to represent the timestamp of that date at midnight (00:00:00.000).

  • Assumption 2: The date you present is not associated with a particular time zone, however you want to determine the offset from a particular time zone (UTC). Without knowing the time zone the date is in, it isn’t possible to calculate a timestamp for a specific time zone. I’ll assume that you want to treat the date as if it is in the local system time zone.

First, you can convert the date instance into a tuple representing the various time components using the timetuple() member:

dtt = d.timetuple() # time.struct_time(tm_year=2011, tm_mon=1, tm_mday=1, tm_hour=0, tm_min=0, tm_sec=0, tm_wday=5, tm_yday=1, tm_isdst=-1)

You can then convert that into a timestamp using time.mktime:

ts = time.mktime(dtt) # 1293868800.0

You can verify this method by testing it with the epoch time itself (1970-01-01), in which case the function should return the timezone offset for the local time zone on that date:

d = datetime.date(1970,1,1)
dtt = d.timetuple() # time.struct_time(tm_year=1970, tm_mon=1, tm_mday=1, tm_hour=0, tm_min=0, tm_sec=0, tm_wday=3, tm_yday=1, tm_isdst=-1)
ts = time.mktime(dtt) # 28800.0

28800.0 is 8 hours, which would be correct for the Pacific time zone (where I’m at).


回答 3

按照python2.7文档,您必须使用calendar.timegm()而不是time.mktime()

>>> d = datetime.date(2011,01,01)
>>> datetime.datetime.utcfromtimestamp(calendar.timegm(d.timetuple()))
datetime.datetime(2011, 1, 1, 0, 0)

follow the python2.7 document, you have to use calendar.timegm() instead of time.mktime()

>>> d = datetime.date(2011,01,01)
>>> datetime.datetime.utcfromtimestamp(calendar.timegm(d.timetuple()))
datetime.datetime(2011, 1, 1, 0, 0)

回答 4

我定义了我自己的两个功能

  • utc_time2datetime(utc_time,tz =无)
  • datetime2utc_time(日期时间)

这里:

import time
import datetime
from pytz import timezone
import calendar
import pytz


def utc_time2datetime(utc_time, tz=None):
    # convert utc time to utc datetime
    utc_datetime = datetime.datetime.fromtimestamp(utc_time)

    # add time zone to utc datetime
    if tz is None:
        tz_datetime = utc_datetime.astimezone(timezone('utc'))
    else:
        tz_datetime = utc_datetime.astimezone(tz)

    return tz_datetime


def datetime2utc_time(datetime):
    # add utc time zone if no time zone is set
    if datetime.tzinfo is None:
        datetime = datetime.replace(tzinfo=timezone('utc'))

    # convert to utc time zone from whatever time zone the datetime is set to
    utc_datetime = datetime.astimezone(timezone('utc')).replace(tzinfo=None)

    # create a time tuple from datetime
    utc_timetuple = utc_datetime.timetuple()

    # create a time element from the tuple an add microseconds
    utc_time = calendar.timegm(utc_timetuple) + datetime.microsecond / 1E6

    return utc_time

I defined my own two functions

  • utc_time2datetime(utc_time, tz=None)
  • datetime2utc_time(datetime)

here:

import time
import datetime
from pytz import timezone
import calendar
import pytz


def utc_time2datetime(utc_time, tz=None):
    # convert utc time to utc datetime
    utc_datetime = datetime.datetime.fromtimestamp(utc_time)

    # add time zone to utc datetime
    if tz is None:
        tz_datetime = utc_datetime.astimezone(timezone('utc'))
    else:
        tz_datetime = utc_datetime.astimezone(tz)

    return tz_datetime


def datetime2utc_time(datetime):
    # add utc time zone if no time zone is set
    if datetime.tzinfo is None:
        datetime = datetime.replace(tzinfo=timezone('utc'))

    # convert to utc time zone from whatever time zone the datetime is set to
    utc_datetime = datetime.astimezone(timezone('utc')).replace(tzinfo=None)

    # create a time tuple from datetime
    utc_timetuple = utc_datetime.timetuple()

    # create a time element from the tuple an add microseconds
    utc_time = calendar.timegm(utc_timetuple) + datetime.microsecond / 1E6

    return utc_time

回答 5

这个问题有点困惑。时间戳不是UTC,而是Unix。日期可能是UTC?假设是这样,并且如果您使用的是Python 3.2+,则简单日期使此操作变得无关紧要:

>>> SimpleDate(date(2011,1,1), tz='utc').timestamp
1293840000.0

如果您实际上有年,月和日,则无需创建date

>>> SimpleDate(2011,1,1, tz='utc').timestamp
1293840000.0

以及日期是否在其他时区中(这很重要,因为我们假设午夜没有相关的时间):

>>> SimpleDate(date(2011,1,1), tz='America/New_York').timestamp
1293858000.0

[simple-date背后的想法是将所有python的日期和时间收集在一个一致的类中,因此您可以进行任何转换。因此,例如,它也会沿相反方向前进:

>>> SimpleDate(1293858000, tz='utc').date
datetime.date(2011, 1, 1)

]

the question is a little confused. timestamps are not UTC – they’re a Unix thing. the date might be UTC? assuming it is, and if you’re using Python 3.2+, simple-date makes this trivial:

>>> SimpleDate(date(2011,1,1), tz='utc').timestamp
1293840000.0

if you actually have the year, month and day you don’t need to create the date:

>>> SimpleDate(2011,1,1, tz='utc').timestamp
1293840000.0

and if the date is in some other timezone (this matters because we’re assuming midnight without an associated time):

>>> SimpleDate(date(2011,1,1), tz='America/New_York').timestamp
1293858000.0

[the idea behind simple-date is to collect all python’s date and time stuff in one consistent class, so you can do any conversion. so, for example, it will also go the other way:

>>> SimpleDate(1293858000, tz='utc').date
datetime.date(2011, 1, 1)

]


回答 6

使用箭头包:

>>> import arrow
>>> arrow.get(2010, 12, 31).timestamp
1293753600
>>> time.gmtime(1293753600)
time.struct_time(tm_year=2010, tm_mon=12, tm_mday=31, 
    tm_hour=0, tm_min=0, tm_sec=0, 
    tm_wday=4, tm_yday=365, tm_isdst=0)

Using the arrow package:

>>> import arrow
>>> arrow.get(2010, 12, 31).timestamp
1293753600
>>> time.gmtime(1293753600)
time.struct_time(tm_year=2010, tm_mon=12, tm_mday=31, 
    tm_hour=0, tm_min=0, tm_sec=0, 
    tm_wday=4, tm_yday=365, tm_isdst=0)

回答 7

完整的时间字符串包含:

  • 日期
  • 时间
  • utcoffset [+HHMM or -HHMM]

例如:

1970-01-01 06:00:00 +0500 == 1970-01-01 01:00:00 +0000 == UNIX timestamp:3600

$ python3
>>> from datetime import datetime
>>> from calendar import timegm
>>> tm = '1970-01-01 06:00:00 +0500'
>>> fmt = '%Y-%m-%d %H:%M:%S %z'
>>> timegm(datetime.strptime(tm, fmt).utctimetuple())
3600

注意:

UNIX timestamp是一个浮点数,以纪元以来的秒数表示,单位为UTC


编辑:

$ python3
>>> from datetime import datetime, timezone, timedelta
>>> from calendar import timegm
>>> dt = datetime(1970, 1, 1, 6, 0)
>>> tz = timezone(timedelta(hours=5))
>>> timegm(dt.replace(tzinfo=tz).utctimetuple())
3600

A complete time-string contains:

  • date
  • time
  • utcoffset [+HHMM or -HHMM]

For example:

1970-01-01 06:00:00 +0500 == 1970-01-01 01:00:00 +0000 == UNIX timestamp:3600

$ python3
>>> from datetime import datetime
>>> from calendar import timegm
>>> tm = '1970-01-01 06:00:00 +0500'
>>> fmt = '%Y-%m-%d %H:%M:%S %z'
>>> timegm(datetime.strptime(tm, fmt).utctimetuple())
3600

Note:

UNIX timestamp is a floating point number expressed in seconds since the epoch, in UTC.


Edit:

$ python3
>>> from datetime import datetime, timezone, timedelta
>>> from calendar import timegm
>>> dt = datetime(1970, 1, 1, 6, 0)
>>> tz = timezone(timedelta(hours=5))
>>> timegm(dt.replace(tzinfo=tz).utctimetuple())
3600

回答 8

考虑到您有一个datetime名为的对象d,请使用以下命令获取UTC中的时间戳记:

d.strftime("%Y-%m-%dT%H:%M:%S.%fZ")

对于相反的方向,请使用以下命令:

d = datetime.strptime("2008-09-03T20:56:35.450686Z", "%Y-%m-%dT%H:%M:%S.%fZ")

Considering you have a datetime object called d, use the following to get the timestamp in UTC:

d.strftime("%Y-%m-%dT%H:%M:%S.%fZ")

And for the opposite direction, use following :

d = datetime.strptime("2008-09-03T20:56:35.450686Z", "%Y-%m-%dT%H:%M:%S.%fZ")

回答 9

我对深入的讨论印象深刻。

我的2美分:

从datetime导入datetime导入时间

utc中的时间戳为:

timestamp = \
(datetime.utcnow() - datetime(1970,1,1)).total_seconds()

要么,

timestamp = time.time()

如果现在是从datetime.now()返回的,则在同一DST中

utcoffset = (datetime.now() - datetime.utcnow()).total_seconds()
timestamp = \
(now - datetime(1970,1,1)).total_seconds() - utcoffset

i’m impressed of the deep discussion.

my 2 cents:

from datetime import datetime import time

the timestamp in utc is:

timestamp = \
(datetime.utcnow() - datetime(1970,1,1)).total_seconds()

or,

timestamp = time.time()

if now results from datetime.now(), in the same DST

utcoffset = (datetime.now() - datetime.utcnow()).total_seconds()
timestamp = \
(now - datetime(1970,1,1)).total_seconds() - utcoffset

到底什么是getattr(),我该如何使用它?

问题:到底什么是getattr(),我该如何使用它?

我最近阅读了有关该getattr()功能的信息。问题是我仍然无法理解其用法。据我所知的唯一的事情getattr()getattr(li, "pop")相同调用li.pop

当书中提到您是如何使用它来获得对函数的引用时,直到运行时才知道它的名称,我不明白。总的来说,也许这是我在编程方面的菜鸟。有人可以阐明这个话题吗?我什么时候以及如何使用它?

I’ve recently read about the getattr() function. The problem is that I still can’t grasp the idea of its usage. The only thing I understand about getattr() is that getattr(li, "pop") is the same as calling li.pop.

I didn’t understand when the book mentioned how you use it to get a reference to a function without knowing its name until run-time. Maybe this is me being a noob in programming, in general. Could anyone shed some light on the subject? When and how do I use this exactly?


回答 0

getattr(object, 'x') 完全等同object.x

只有两种情况,其中getattr可能是有用的。

  • 您无法编写object.x,因为您事先不知道想要哪个属性(它来自字符串)。对于元编程非常有用。
  • 您想要提供一个默认值。如果没有object.y则将引发一个。但是会回来的。AttributeErrorygetattr(object, 'y', 5)5

getattr(object, 'x') is completely equivalent to object.x.

There are only two cases where getattr can be useful.

  • you can’t write object.x, because you don’t know in advance which attribute you want (it comes from a string). Very useful for meta-programming.
  • you want to provide a default value. object.y will raise an AttributeError if there’s no y. But getattr(object, 'y', 5) will return 5.

回答 1

Python中的对象可以具有属性-数据属性和与之配合使用的方法(方法)。实际上,每个对象都有内置的属性。

例如你有一个对象person,一个具有多个属性:namegender,等。

您可以访问这些属性(无论是方法或数据对象),通常写作:person.nameperson.genderperson.the_method()等。

但是,如果在编写程序时不知道属性名称怎么办?例如,您将属性名称存储在名为的变量中attr_name

如果

attr_name = 'gender'

然后,而不是写

gender = person.gender

你可以写

gender = getattr(person, attr_name)

一些实践:

Python 3.4.0 (default, Apr 11 2014, 13:05:11)

>>> class Person():
...     name = 'Victor'
...     def say(self, what):
...         print(self.name, what)
... 
>>> getattr(Person, 'name')
'Victor'
>>> attr_name = 'name'
>>> person = Person()
>>> getattr(person, attr_name)
'Victor'
>>> getattr(person, 'say')('Hello')
Victor Hello

getattrAttributeError如果对象中不存在具有给定名称的属性,则将引发:

>>> getattr(person, 'age')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'Person' object has no attribute 'age'

但是您可以传递一个默认值作为第三个参数,如果该属性不存在,则将返回该默认值:

>>> getattr(person, 'age', 0)
0

您可以使用getattrdir来迭代所有属性名称并获取它们的值:

>>> dir(1000)
['__abs__', '__add__', ..., '__trunc__', '__xor__', 'bit_length', 'conjugate', 'denominator', 'from_bytes', 'imag', 'numerator', 'real', 'to_bytes']

>>> obj = 1000
>>> for attr_name in dir(obj):
...     attr_value = getattr(obj, attr_name)
...     print(attr_name, attr_value, callable(attr_value))
... 
__abs__ <method-wrapper '__abs__' of int object at 0x7f4e927c2f90> True
...
bit_length <built-in method bit_length of int object at 0x7f4e927c2f90> True
...

>>> getattr(1000, 'bit_length')()
10

这样做的实际用途是查找名称以开头的所有方法test调用它们

类似getattr还有setattr它允许你设定有其名称的对象的属性:

>>> setattr(person, 'name', 'Andrew')
>>> person.name  # accessing instance attribute
'Andrew'
>>> Person.name  # accessing class attribute
'Victor'
>>>

Objects in Python can have attributes — data attributes and functions to work with those (methods). Actually, every object has built-in attributes.

For example you have an object person, that has several attributes: name, gender, etc.

You access these attributes (be it methods or data objects) usually writing: person.name, person.gender, person.the_method(), etc.

But what if you don’t know the attribute’s name at the time you write the program? For example you have attribute’s name stored in a variable called attr_name.

if

attr_name = 'gender'

then, instead of writing

gender = person.gender

you can write

gender = getattr(person, attr_name)

Some practice:

Python 3.4.0 (default, Apr 11 2014, 13:05:11)

>>> class Person():
...     name = 'Victor'
...     def say(self, what):
...         print(self.name, what)
... 
>>> getattr(Person, 'name')
'Victor'
>>> attr_name = 'name'
>>> person = Person()
>>> getattr(person, attr_name)
'Victor'
>>> getattr(person, 'say')('Hello')
Victor Hello

getattr will raise AttributeError if attribute with the given name does not exist in the object:

>>> getattr(person, 'age')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'Person' object has no attribute 'age'

But you can pass a default value as the third argument, which will be returned if such attribute does not exist:

>>> getattr(person, 'age', 0)
0

You can use getattr along with dir to iterate over all attribute names and get their values:

>>> dir(1000)
['__abs__', '__add__', ..., '__trunc__', '__xor__', 'bit_length', 'conjugate', 'denominator', 'from_bytes', 'imag', 'numerator', 'real', 'to_bytes']

>>> obj = 1000
>>> for attr_name in dir(obj):
...     attr_value = getattr(obj, attr_name)
...     print(attr_name, attr_value, callable(attr_value))
... 
__abs__ <method-wrapper '__abs__' of int object at 0x7f4e927c2f90> True
...
bit_length <built-in method bit_length of int object at 0x7f4e927c2f90> True
...

>>> getattr(1000, 'bit_length')()
10

A practical use for this would be to find all methods whose names start with test and call them.

Similar to getattr there is setattr which allows you to set an attribute of an object having its name:

>>> setattr(person, 'name', 'Andrew')
>>> person.name  # accessing instance attribute
'Andrew'
>>> Person.name  # accessing class attribute
'Victor'
>>>

回答 2

对我来说,getattr用这种方式最容易解释:

它使您可以基于字符串的内容来调用方法,而不用键入方法名称。

例如,您不能执行以下操作:

obj = MyObject()
for x in ['foo', 'bar']:
    obj.x()

因为x不是类型builtin而是str。但是,您可以这样做:

obj = MyObject()
for x in ['foo', 'bar']:
    getattr(obj, x)()

它使您可以根据输入动态连接对象。在处理自定义对象和模块时,我发现它很有用。

For me, getattr is easiest to explain this way:

It allows you to call methods based on the contents of a string instead of typing the method name.

For example, you cannot do this:

obj = MyObject()
for x in ['foo', 'bar']:
    obj.x()

because x is not of the type builtin, but str. However, you CAN do this:

obj = MyObject()
for x in ['foo', 'bar']:
    getattr(obj, x)()

It allows you to dynamically connect with objects based on your input. I’ve found it useful when dealing with custom objects and modules.


回答 3

一个很常见的用例getattr是将数据映射到函数。

例如,在Django或Pylons之类的Web框架中,getattr可以很容易地将Web请求的URL映射到将要处理它的函数。例如,如果您看一下Pylons路由的内幕,您会发现(至少默认情况下)它会截取请求的URL,例如:

http://www.example.com/customers/list

分为“客户”和“列表”。然后,它搜索名为的控制器类CustomerController。假设找到该类,则创建该类的实例,然后用于getattr获取其list方法。然后,它将调用该方法,并将请求作为参数传递给该方法。

一旦掌握了这个想法,就可以轻松扩展Web应用程序的功能:只需将新方法添加到控制器类中,然后在页面中创建链接,并为这些方法使用适当的URL。所有这些都可以通过来实现getattr

A pretty common use case for getattr is mapping data to functions.

For instance, in a web framework like Django or Pylons, getattr makes it straightforward to map a web request’s URL to the function that’s going to handle it. If you look under the hood of Pylons’s routing, for instance, you’ll see that (by default, at least) it chops up a request’s URL, like:

http://www.example.com/customers/list

into “customers” and “list”. Then it searches for a controller class named CustomerController. Assuming it finds the class, it creates an instance of the class and then uses getattr to get its list method. It then calls that method, passing it the request as an argument.

Once you grasp this idea, it becomes really easy to extend the functionality of a web application: just add new methods to the controller classes, and then create links in your pages that use the appropriate URLs for those methods. All of this is made possible by getattr.


回答 4

这是一个简单而又肮脏的示例,它说明了一个类如何根据使用的操作系统来触发不同版本的save方法getattr()

import os

class Log(object):
    def __init__(self):
        self.os = os.name
    def __getattr__(self, name):
        """ look for a 'save' attribute, or just 
          return whatever attribute was specified """
        if name == 'save':
            try:
                # try to dynamically return a save 
                # method appropriate for the user's system
                return getattr(self, self.os)
            except:
                # bail and try to return 
                # a default save method
                return getattr(self, '_save')
        else:
            return getattr(self, name)

    # each of these methods could have save logic specific to 
    # the system on which the script is executed
    def posix(self): print 'saving on a posix machine'
    def nt(self): print 'saving on an nt machine'
    def os2(self): print 'saving on an os2 machine'
    def ce(self): print 'saving on a ce machine'
    def java(self): print 'saving on a java machine'
    def riscos(self): print 'saving on a riscos machine'
    def _save(self): print 'saving on an unknown operating system'

    def which_os(self): print os.name

现在,在示例中使用此类:

logger = Log()

# Now you can do one of two things:
save_func = logger.save
# and execute it, or pass it along 
# somewhere else as 1st class:
save_func()

# or you can just call it directly:
logger.save()

# other attributes will hit the else 
# statement and still work as expected
logger.which_os()

Here’s a quick and dirty example of how a class could fire different versions of a save method depending on which operating system it’s being executed on using getattr().

import os

class Log(object):
    def __init__(self):
        self.os = os.name
    def __getattr__(self, name):
        """ look for a 'save' attribute, or just 
          return whatever attribute was specified """
        if name == 'save':
            try:
                # try to dynamically return a save 
                # method appropriate for the user's system
                return getattr(self, self.os)
            except:
                # bail and try to return 
                # a default save method
                return getattr(self, '_save')
        else:
            return getattr(self, name)

    # each of these methods could have save logic specific to 
    # the system on which the script is executed
    def posix(self): print 'saving on a posix machine'
    def nt(self): print 'saving on an nt machine'
    def os2(self): print 'saving on an os2 machine'
    def ce(self): print 'saving on a ce machine'
    def java(self): print 'saving on a java machine'
    def riscos(self): print 'saving on a riscos machine'
    def _save(self): print 'saving on an unknown operating system'

    def which_os(self): print os.name

Now let’s use this class in an example:

logger = Log()

# Now you can do one of two things:
save_func = logger.save
# and execute it, or pass it along 
# somewhere else as 1st class:
save_func()

# or you can just call it directly:
logger.save()

# other attributes will hit the else 
# statement and still work as expected
logger.which_os()

回答 5

除了这里所有令人惊奇的答案之外,还有一种方法可以getattr用来保存大量的代码行并保持其紧密。这种想法是在有时可能需要用可怕的代码表示法之后提出的。

情境

假设您的目录结构如下:

- superheroes.py
- properties.py

而且,你有功能,让有关的信息ThorIron ManDoctor Strangesuperheroes.py。你很巧妙地写下所有这些的性质properties.py在一个紧凑的dict,然后访问它们。

properties.py

thor = {
    'about': 'Asgardian god of thunder',
    'weapon': 'Mjolnir',
    'powers': ['invulnerability', 'keen senses', 'vortex breath'], # and many more
}
iron_man = {
    'about': 'A wealthy American business magnate, playboy, and ingenious scientist',
    'weapon': 'Armor',
    'powers': ['intellect', 'armor suit', 'interface with wireless connections', 'money'],
}
doctor_strange = {
    'about': ' primary protector of Earth against magical and mystical threats',
    'weapon': 'Magic',
    'powers': ['magic', 'intellect', 'martial arts'],
}

现在,假设您想按需返回每个工具的功能superheroes.py。因此,有类似

from .properties import thor, iron_man, doctor_strange


def get_thor_weapon():
    return thor['weapon']


def get_iron_man_bio():
    return iron_man['about']


def get_thor_powers():
    return thor['powers']

…以及更多基于键和超级英雄返回不同值的函数。

在的帮助下getattr,您可以执行以下操作:

from . import properties


def get_superhero_weapon(hero):
    superhero = getattr(properties, hero)
    return superhero['weapon']


def get_superhero_powers(hero):
    superhero = getattr(properties, hero)
    return superhero['powers']

您大大减少了代码,函数和重复的行数!

哦,当然,如果您有诸如properties_of_thor变量之类的坏名,只需执行以下操作即可创建和访问它们

def get_superhero_weapon(hero):
    superhero = 'properties_of_{}'.format(hero)
    all_properties = getattr(properties, superhero)
    return all_properties['weapon']

注意:对于此特定问题,可以有更聪明的方法来处理这种情况,但其目的是提供有关getattr在正确的位置使用代码编写更清晰代码的见解。

Other than all the amazing answers here, there is a way to use getattr to save copious lines of code and keeping it snug. This thought came following the dreadful representation of code that sometimes might be a necessity.

Scenario

Suppose your directory structure is as follows:

- superheroes.py
- properties.py

And, you have functions for getting information about Thor, Iron Man, Doctor Strange in superheroes.py. You very smartly write down the properties of all of them in properties.py in a compact dict and then access them.

properties.py

thor = {
    'about': 'Asgardian god of thunder',
    'weapon': 'Mjolnir',
    'powers': ['invulnerability', 'keen senses', 'vortex breath'], # and many more
}
iron_man = {
    'about': 'A wealthy American business magnate, playboy, and ingenious scientist',
    'weapon': 'Armor',
    'powers': ['intellect', 'armor suit', 'interface with wireless connections', 'money'],
}
doctor_strange = {
    'about': ' primary protector of Earth against magical and mystical threats',
    'weapon': 'Magic',
    'powers': ['magic', 'intellect', 'martial arts'],
}

Now, let’s say you want to return capabilities of each of them on demand in superheroes.py. So, there are functions like

from .properties import thor, iron_man, doctor_strange


def get_thor_weapon():
    return thor['weapon']


def get_iron_man_bio():
    return iron_man['about']


def get_thor_powers():
    return thor['powers']

…and more functions returning different values based on the keys and superhero.

With the help of getattr, you could do something like:

from . import properties


def get_superhero_weapon(hero):
    superhero = getattr(properties, hero)
    return superhero['weapon']


def get_superhero_powers(hero):
    superhero = getattr(properties, hero)
    return superhero['powers']

You considerably reduced the number of lines of code, functions and repetition!

Oh and of course, if you have bad names like properties_of_thor for variables , they can be made and accessed by simply doing

def get_superhero_weapon(hero):
    superhero = 'properties_of_{}'.format(hero)
    all_properties = getattr(properties, superhero)
    return all_properties['weapon']

NOTE: For this particular problem, there can be smarter ways to deal with the situation, but the idea is to give an insight about using getattr in right places to write cleaner code.


回答 6

# getattr

class hithere():

    def french(self):
        print 'bonjour'

    def english(self):
        print 'hello'

    def german(self):
        print 'hallo'

    def czech(self):
        print 'ahoj'

    def noidea(self):
        print 'unknown language'


def dispatch(language):
    try:
        getattr(hithere(),language)()
    except:
        getattr(hithere(),'noidea')()
        # note, do better error handling than this

dispatch('french')
dispatch('english')
dispatch('german')
dispatch('czech')
dispatch('spanish')
# getattr

class hithere():

    def french(self):
        print 'bonjour'

    def english(self):
        print 'hello'

    def german(self):
        print 'hallo'

    def czech(self):
        print 'ahoj'

    def noidea(self):
        print 'unknown language'


def dispatch(language):
    try:
        getattr(hithere(),language)()
    except:
        getattr(hithere(),'noidea')()
        # note, do better error handling than this

dispatch('french')
dispatch('english')
dispatch('german')
dispatch('czech')
dispatch('spanish')

回答 7

我有时会getattr(..)在代码中使用次要属性之前就懒惰地初始化它们。

比较以下内容:

class Graph(object):
    def __init__(self):
        self.n_calls_to_plot = 0

    #...
    #A lot of code here
    #...

    def plot(self):
        self.n_calls_to_plot += 1

对此:

class Graph(object):
    def plot(self):
        self.n_calls_to_plot = 1 + getattr(self, "n_calls_to_plot", 0)

第二种方法的优点是n_calls_to_plot仅在使用它的代码中出现。这有利于提高可读性,因为(1)在阅读用法时可以立即看到它以什么值开头;(2)它不会使__init__(..)方法分神,理想情况下应该是关于类的概念状态的,而不是某些实用程序计数器,该实用程序计数器出于技术原因(例如优化)仅由函数的一种方法使用,并且与对象的含义无关。

I sometimes use getattr(..) to lazily initialise attributes of secondary importance just before they are used in the code.

Compare the following:

class Graph(object):
    def __init__(self):
        self.n_calls_to_plot = 0

    #...
    #A lot of code here
    #...

    def plot(self):
        self.n_calls_to_plot += 1

To this:

class Graph(object):
    def plot(self):
        self.n_calls_to_plot = 1 + getattr(self, "n_calls_to_plot", 0)

The advantage of the second way is that n_calls_to_plot only appears around the place in the code where it is used. This is good for readability, because (1) you can immediately see what value it starts with when reading how it’s used, (2) it doesn’t introduce a distraction into the __init__(..) method, which ideally should be about the conceptual state of the class, rather than some utility counter that is only used by one of the function’s methods for technical reasons, such as optimisation, and has nothing to do with the meaning of the object.


回答 8

当我从存储在类中的数据创建XML文件时,如果属性不存在或类型是经常会收到错误None。在这种情况下,我的问题不是不知道您的问题中所说的属性名称是什么,而是数据曾经存储在该属性中。

class Pet:
    def __init__(self):
        self.hair = None
        self.color = None

如果我曾经hasattr这样做,True即使属性值是类型,它也会返回None,这将导致ElementTree set命令失败。

hasattr(temp, 'hair')
>>True

如果属性值为type Nonegetattr还将返回它,这将导致我的ElementTree set命令失败。

c = getattr(temp, 'hair')
type(c)
>> NoneType

我现在使用以下方法来处理这些情况:

def getRealAttr(class_obj, class_attr, default = ''):
    temp = getattr(class_obj, class_attr, default)
    if temp is None:
        temp = default
    elif type(temp) != str:
        temp = str(temp)
    return temp

这是我何时及如何使用getattr

Quite frequently when I am creating an XML file from data stored in a class I would frequently receive errors if the attribute didn’t exist or was of type None. In this case, my issue wasn’t not knowing what the attribute name was, as stated in your question, but rather was data ever stored in that attribute.

class Pet:
    def __init__(self):
        self.hair = None
        self.color = None

If I used hasattr to do this, it would return True even if the attribute value was of type None and this would cause my ElementTree set command to fail.

hasattr(temp, 'hair')
>>True

If the attribute value was of type None, getattr would also return it which would cause my ElementTree set command to fail.

c = getattr(temp, 'hair')
type(c)
>> NoneType

I use the following method to take care of these cases now:

def getRealAttr(class_obj, class_attr, default = ''):
    temp = getattr(class_obj, class_attr, default)
    if temp is None:
        temp = default
    elif type(temp) != str:
        temp = str(temp)
    return temp

This is when and how I use getattr.


回答 9

getattr()在Python中实现switch语句的另一种用法。它使用两种反射来获取案例类型。

import sys

class SwitchStatement(object):
    """ a class to implement switch statement and a way to show how to use gettattr in Pythion"""

    def case_1(self):
        return "value for case_1"

    def case_2(self):
        return "value for case_2"

    def case_3(self):
        return "value for case_3"

    def case_4(self):
        return "value for case_4"

    def case_value(self, case_type=1):
        """This is the main dispatchmethod, that uses gettattr"""
        case_method = 'case_' + str(case_type)
        # fetch the relevant method name
        # Get the method from 'self'. Default to a lambda.
        method = getattr(self, case_method, lambda: "Invalid case type")
        # Call the method as we return it
        return method()

def main(_):
    switch = SwitchStatement()
    print swtich.case_value(_)

if __name__ == '__main__':
    main(int(sys.argv[1]))

Another use of getattr() in implementing a switch statement in Python. It uses both reflection to get the case type.

import sys

class SwitchStatement(object):
    """ a class to implement switch statement and a way to show how to use gettattr in Pythion"""

    def case_1(self):
        return "value for case_1"

    def case_2(self):
        return "value for case_2"

    def case_3(self):
        return "value for case_3"

    def case_4(self):
        return "value for case_4"

    def case_value(self, case_type=1):
        """This is the main dispatchmethod, that uses gettattr"""
        case_method = 'case_' + str(case_type)
        # fetch the relevant method name
        # Get the method from 'self'. Default to a lambda.
        method = getattr(self, case_method, lambda: "Invalid case type")
        # Call the method as we return it
        return method()

def main(_):
    switch = SwitchStatement()
    print swtich.case_value(_)

if __name__ == '__main__':
    main(int(sys.argv[1]))

回答 10

setattr()

我们使用setattr将属性添加到我们的类实例。我们传递类实例,属性名称和值。

getattr()

使用getattr,我们可以检索这些值

例如

Employee = type("Employee", (object,), dict())

employee = Employee()

# Set salary to 1000
setattr(employee,"salary", 1000 )

# Get the Salary
value = getattr(employee, "salary")

print(value)

setattr()

We use setattr to add an attribute to our class instance. We pass the class instance, the attribute name, and the value.

getattr()

With getattr we retrive these values

For example

Employee = type("Employee", (object,), dict())

employee = Employee()

# Set salary to 1000
setattr(employee,"salary", 1000 )

# Get the Salary
value = getattr(employee, "salary")

print(value)

回答 11

我认为这个例子是不言自明的。它运行第一个参数的方法,其名称在第二个参数中给出。

class MyClass:
   def __init__(self):
      pass
   def MyMethod(self):
      print("Method ran")

# Create an object
object = MyClass()
# Get all the methods of a class
method_list = [func for func in dir(MyClass) if callable(getattr(MyClass, func))]
# You can use any of the methods in method_list
# "MyMethod" is the one we want to use right now

# This is the same as running "object.MyMethod()"
getattr(object,'MyMethod')()

I think this example is self explanatory. It runs the method of first parameter, whose name is given in the second parameter.

class MyClass:
   def __init__(self):
      pass
   def MyMethod(self):
      print("Method ran")

# Create an object
object = MyClass()
# Get all the methods of a class
method_list = [func for func in dir(MyClass) if callable(getattr(MyClass, func))]
# You can use any of the methods in method_list
# "MyMethod" is the one we want to use right now

# This is the same as running "object.MyMethod()"
getattr(object,'MyMethod')()

回答 12

它也在https://www.programiz.com/python-programming/methods/built-in/getattr中阐明

class Person:
    age = 23
    name = "Adam"

person = Person()
print('The age is:', getattr(person, "age"))
print('The age is:', person.age)

年龄是:23

年龄是:23

class Person:
    age = 23
    name = "Adam"

person = Person()

# when default value is provided
print('The sex is:', getattr(person, 'sex', 'Male'))

# when no default value is provided
print('The sex is:', getattr(person, 'sex'))

性别是:男

AttributeError:“人员”对象没有属性“性别”

It is also clarifying from https://www.programiz.com/python-programming/methods/built-in/getattr

class Person:
    age = 23
    name = "Adam"

person = Person()
print('The age is:', getattr(person, "age"))
print('The age is:', person.age)

The age is: 23

The age is: 23

class Person:
    age = 23
    name = "Adam"

person = Person()

# when default value is provided
print('The sex is:', getattr(person, 'sex', 'Male'))

# when no default value is provided
print('The sex is:', getattr(person, 'sex'))

The sex is: Male

AttributeError: ‘Person’ object has no attribute ‘sex’


回答 13

我已经在Python2.7.17中尝试过

一些同胞已经回答了。但是,我尝试调用getattr(obj,’set_value’),但没有执行set_value方法,因此我将其更改为getattr(obj,’set_value’)()->这有助于调用相同的方法。

示例代码:

范例1:

    class GETATT_VERIFY():
       name = "siva"
       def __init__(self):
           print "Ok"
       def set_value(self):
           self.value = "myself"
           print "oooh"
    obj = GETATT_VERIFY()
    print getattr(GETATT_VERIFY, 'name')
    getattr(obj, 'set_value')()
    print obj.value

I have tried in Python2.7.17

Some of the fellow folks already answered. However I have tried to call getattr(obj, ‘set_value’) and this didn’t execute the set_value method, So i changed to getattr(obj, ‘set_value’)() –> This helps to invoke the same.

Example Code:

Example 1:

    class GETATT_VERIFY():
       name = "siva"
       def __init__(self):
           print "Ok"
       def set_value(self):
           self.value = "myself"
           print "oooh"
    obj = GETATT_VERIFY()
    print getattr(GETATT_VERIFY, 'name')
    getattr(obj, 'set_value')()
    print obj.value

如何检查pandas DataFrame是否为空?

问题:如何检查pandas DataFrame是否为空?

如何检查大熊猫是否DataFrame为空?就我而言,如果终端DataFrame为空,我想在终端打印一些消息。

How to check whether a pandas DataFrame is empty? In my case I want to print some message in terminal if the DataFrame is empty.


回答 0

您可以使用该属性df.empty检查其是否为空:

if df.empty:
    print('DataFrame is empty!')

资料来源:熊猫文件

You can use the attribute df.empty to check whether it’s empty or not:

if df.empty:
    print('DataFrame is empty!')

Source: Pandas Documentation


回答 1

我使用的len功能。它比快得多emptylen(df.index)甚至更快。

import pandas as pd
import numpy as np

df = pd.DataFrame(np.random.randn(10000, 4), columns=list('ABCD'))

def empty(df):
    return df.empty

def lenz(df):
    return len(df) == 0

def lenzi(df):
    return len(df.index) == 0

'''
%timeit empty(df)
%timeit lenz(df)
%timeit lenzi(df)

10000 loops, best of 3: 13.9 µs per loop
100000 loops, best of 3: 2.34 µs per loop
1000000 loops, best of 3: 695 ns per loop

len on index seems to be faster
'''

I use the len function. It’s much faster than empty. len(df.index) is even faster.

import pandas as pd
import numpy as np

df = pd.DataFrame(np.random.randn(10000, 4), columns=list('ABCD'))

def empty(df):
    return df.empty

def lenz(df):
    return len(df) == 0

def lenzi(df):
    return len(df.index) == 0

'''
%timeit empty(df)
%timeit lenz(df)
%timeit lenzi(df)

10000 loops, best of 3: 13.9 µs per loop
100000 loops, best of 3: 2.34 µs per loop
1000000 loops, best of 3: 695 ns per loop

len on index seems to be faster
'''

回答 2

我更喜欢长途旅行。这些是我为避免使用try-except子句而进行的检查-

  1. 检查变量是否不为None
  2. 然后检查其是否为数据框和
  3. 确保它不为空

DATA是可疑变量-

DATA is not None and isinstance(DATA, pd.DataFrame) and not DATA.empty

I prefer going the long route. These are the checks I follow to avoid using a try-except clause –

  1. check if variable is not None
  2. then check if its a dataframe and
  3. make sure its not empty

Here, DATA is the suspect variable –

DATA is not None and isinstance(DATA, pd.DataFrame) and not DATA.empty

回答 3

似乎在该线程中接受的空定义是仅具有零行的数据帧。但是在零行零列空数据框和零行零列至少一列空数据框之间有区别。在每种情况下,索引的长度都是0,并且empty = True,如下所示:

示例1:具有0行和0列的空数据框

In [1]: import pandas as pd
        df1 = pd.DataFrame()
        df1
Out[1]: Empty DataFrame
        Columns: []
        Index: []

In [2]: len(df1.index)
Out[2]: 0

In [3]: df1.empty
Out[3]: True

示例2:具有0行和至少1列的空数据框

In [4]: df2 = pd.DataFrame({'AA' : [], 'BB' : []})
        df2
Out[4]: Empty DataFrame
        Columns: [AA, BB]
        Index: []

In [5]: len(df2.index)
Out[5]: 0

In [6]: df2.empty
Out[6]: True

区分没有标题和数据数据帧或只是没有数据数据帧的一种方法是测试列索引的长度。第一个加载的数据帧返回零列,第二个数据帧返回空列数。

In [7]: len(df1.columns)
Out[7]: 0

In [8]: len(df2.columns)
Out[8]: 2

To see if a dataframe is empty, I argue that one should test for the length of a dataframe’s columns index:

if len(df.columns) == 0: 1

Reason:

According to the Pandas Reference API, there is a distinction between:

  • an empty dataframe with 0 rows and 0 columns
  • an empty dataframe with rows containing NaN hence at least 1 column

Arguably, they are not the same. The other answers are imprecise in that df.empty, len(df), or len(df.index) make no distinction and return index is 0 and empty is True in both cases.

Examples

Example 1: An empty dataframe with 0 rows and 0 columns

In [1]: import pandas as pd
        df1 = pd.DataFrame()
        df1
Out[1]: Empty DataFrame
        Columns: []
        Index: []

In [2]: len(df1.index)  # or len(df1)
Out[2]: 0

In [3]: df1.empty
Out[3]: True

Example 2: A dataframe which is emptied to 0 rows but still retains n columns

In [4]: df2 = pd.DataFrame({'AA' : [1, 2, 3], 'BB' : [11, 22, 33]})
        df2
Out[4]:    AA  BB
        0   1  11
        1   2  22
        2   3  33

In [5]: df2 = df2[df2['AA'] == 5]
        df2
Out[5]: Empty DataFrame
        Columns: [AA, BB]
        Index: []

In [6]: len(df2.index)  # or len(df2)
Out[6]: 0

In [7]: df2.empty
Out[7]: True

Now, building on the previous examples, in which the index is 0 and empty is True. When reading the length of the columns index for the first loaded dataframe df1, it returns 0 columns to prove that it is indeed empty.

In [8]: len(df1.columns)
Out[8]: 0

In [9]: len(df2.columns)
Out[9]: 2

Critically, while the second dataframe df2 contains no data, it is not completely empty because it returns the amount of empty columns that persist.

Why it matters

Let’s add a new column to these dataframes to understand the implications:

# As expected, the empty column displays 1 series
In [10]: df1['CC'] = [111, 222, 333]
         df1
Out[10]:    CC
         0 111
         1 222
         2 333
In [11]: len(df1.columns)
Out[11]: 1

# Note the persisting series with rows containing `NaN` values in df2
In [12]: df2['CC'] = [111, 222, 333]
         df2
Out[12]:    AA  BB   CC
         0 NaN NaN  111
         1 NaN NaN  222
         2 NaN NaN  333
In [13]: len(df2.columns)
Out[13]: 3

It is evident that the original columns in df2 have re-surfaced. Therefore, it is prudent to instead read the length of the columns index with len(pandas.core.frame.DataFrame.columns) to see if a dataframe is empty.

Practical solution

# New dataframe df
In [1]: df = pd.DataFrame({'AA' : [1, 2, 3], 'BB' : [11, 22, 33]})
        df
Out[1]:    AA  BB
        0   1  11
        1   2  22
        2   3  33

# This data manipulation approach results in an empty df
# because of a subset of values that are not available (`NaN`)
In [2]: df = df[df['AA'] == 5]
        df
Out[2]: Empty DataFrame
        Columns: [AA, BB]
        Index: []

# NOTE: the df is empty, BUT the columns are persistent
In [3]: len(df.columns)
Out[3]: 2

# And accordingly, the other answers on this page
In [4]: len(df.index)  # or len(df)
Out[4]: 0

In [5]: df.empty
Out[5]: True
# SOLUTION: conditionally check for empty columns
In [6]: if len(df.columns) != 0:  # <--- here
            # Do something, e.g. 
            # drop any columns containing rows with `NaN`
            # to make the df really empty
            df = df.dropna(how='all', axis=1)
        df
Out[6]: Empty DataFrame
        Columns: []
        Index: []

# Testing shows it is indeed empty now
In [7]: len(df.columns)
Out[7]: 0

Adding a new data series works as expected without the re-surfacing of empty columns (factually, without any series that were containing rows with only NaN):

In [8]: df['CC'] = [111, 222, 333]
         df
Out[8]:    CC
         0 111
         1 222
         2 333
In [9]: len(df.columns)
Out[9]: 1

回答 4

1)如果一个DataFrame具有Nan和Non Null值,并且您想查找该DataFrame是否
是否为空,然后尝试此代码。
2)什么时候会发生这种情况? 
使用单个函数绘制多个DataFrame时会发生这种情况 
作为参数传递的参数。在这种情况下,该函数甚至尝试绘制数据 
当DataFrame为空并因此绘制一个空图时!
如果仅显示“ DataFrame has no data”消息,将很有意义。
3)为什么? 
如果DataFrame为空(即完全不包含任何数据。请使用Nan值来提醒您DataFrame) 
被认为是非空的),那么最好不要绘制而是显示一条消息:
假设我们有两个DataFrames df1和df2。
函数myfunc接受任何DataFrame(在这种情况下为df1和df2)并打印一条消息 
如果DataFrame为空(而不是绘制):
df1                     df2
col1 col2           col1 col2 
Nan   2              Nan  Nan 
2     Nan            Nan  Nan  

和功能:

def myfunc(df):
  if (df.count().sum())>0: ##count the total number of non Nan values.Equal to 0 if DataFrame is empty
     print('not empty')
     df.plot(kind='barh')
  else:
     display a message instead of plotting if it is empty
     print('empty')
1) If a DataFrame has got Nan and Non Null values and you want to find whether the DataFrame
is empty or not then try this code.
2) when this situation can happen? 
This situation happens when a single function is used to plot more than one DataFrame 
which are passed as parameter.In such a situation the function try to plot the data even 
when a DataFrame is empty and thus plot an empty figure!.
It will make sense if simply display 'DataFrame has no data' message.
3) why? 
if a DataFrame is empty(i.e. contain no data at all.Mind you DataFrame with Nan values 
is considered non empty) then it is desirable not to plot but put out a message :
Suppose we have two DataFrames df1 and df2.
The function myfunc takes any DataFrame(df1 and df2 in this case) and print a message 
if a DataFrame is empty(instead of plotting):
df1                     df2
col1 col2           col1 col2 
Nan   2              Nan  Nan 
2     Nan            Nan  Nan  

and the function:

def myfunc(df):
  if (df.count().sum())>0: ##count the total number of non Nan values.Equal to 0 if DataFrame is empty
     print('not empty')
     df.plot(kind='barh')
  else:
     display a message instead of plotting if it is empty
     print('empty')

如何在Python中启动后台进程?

问题:如何在Python中启动后台进程?

我正在尝试将Shell脚本移植到可读性更高的python版本。原始的shell脚本在后台使用“&”启动多个进程(实用程序,监视器等)。如何在python中达到相同的效果?我希望这些过程在Python脚本完成后不会消失。我敢肯定它与守护程序的概念有关,但是我找不到如何轻松实现此目的。

I’m trying to port a shell script to the much more readable python version. The original shell script starts several processes (utilities, monitors, etc.) in the background with “&”. How can I achieve the same effect in python? I’d like these processes not to die when the python scripts complete. I am sure it’s related to the concept of a daemon somehow, but I couldn’t find how to do this easily.


回答 0

注意:此答案的最新版本比2009年发布时要少。subprocess现在建议在文档中使用其他答案中显示的模块

(请注意,子流程模块提供了更强大的工具来生成新流程并检索其结果;使用该模块比使用这些功能更可取。)


如果您希望您的进程在后台启动,则可以使用system()与您的Shell脚本相同的方式来使用和调用它,也可以spawn

import os
os.spawnl(os.P_DETACH, 'some_long_running_command')

(或者,您也可以尝试使用便携性较差的os.P_NOWAIT标志)。

请参阅此处文档

Note: This answer is less current than it was when posted in 2009. Using the subprocess module shown in other answers is now recommended in the docs

(Note that the subprocess module provides more powerful facilities for spawning new processes and retrieving their results; using that module is preferable to using these functions.)


If you want your process to start in the background you can either use system() and call it in the same way your shell script did, or you can spawn it:

import os
os.spawnl(os.P_DETACH, 'some_long_running_command')

(or, alternatively, you may try the less portable os.P_NOWAIT flag).

See the documentation here.


回答 1

尽管jkp的解决方案有效,但是更新的方式(以及文档建议的方式)是使用subprocess模块。对于简单的命令,它等效,但是如果您要执行复杂的操作,它提供了更多选项。

您的案例示例:

import subprocess
subprocess.Popen(["rm","-r","some.file"])

这将rm -r somefile在后台运行。请注意,调用.communicate()从返回的对象Popen将一直阻塞,直到完成为止,因此,如果要使其在后台运行,请不要这样做:

import subprocess
ls_output=subprocess.Popen(["sleep", "30"])
ls_output.communicate()  # Will block for 30 seconds

请参阅此处的文档。

另外,有一点需要澄清:这里使用的“背景”纯粹是一个外壳概念;从技术上讲,您的意思是希望在等待进程完成时生成一个没有阻塞的进程。但是,我在这里使用“背景”来指代类似外壳背景的行为。

While jkp‘s solution works, the newer way of doing things (and the way the documentation recommends) is to use the subprocess module. For simple commands its equivalent, but it offers more options if you want to do something complicated.

Example for your case:

import subprocess
subprocess.Popen(["rm","-r","some.file"])

This will run rm -r some.file in the background. Note that calling .communicate() on the object returned from Popen will block until it completes, so don’t do that if you want it to run in the background:

import subprocess
ls_output=subprocess.Popen(["sleep", "30"])
ls_output.communicate()  # Will block for 30 seconds

See the documentation here.

Also, a point of clarification: “Background” as you use it here is purely a shell concept; technically, what you mean is that you want to spawn a process without blocking while you wait for it to complete. However, I’ve used “background” here to refer to shell-background-like behavior.


回答 2

您可能需要答案“如何在Python中调用外部命令”

最简单的方法是使用该os.system函数,例如:

import os
os.system("some_command &")

基本上,传递给system函数的所有内容都将与将其传递给脚本中的shell一样执行。

You probably want the answer to “How to call an external command in Python”.

The simplest approach is to use the os.system function, e.g.:

import os
os.system("some_command &")

Basically, whatever you pass to the system function will be executed the same as if you’d passed it to the shell in a script.


回答 3

我在这里找到这个:

在Windows(win xp)上,父进程longtask.py只有在完成工作后才能完成。这不是您想要的CGI脚本。问题并非特定于Python,在PHP社区中,问题是相同的。

解决方案是将DETACHED_PROCESS 过程创建标志传递给CreateProcesswin API中的基础函数。如果碰巧安装了pywin32,则可以从win32process模块​​中导入该标志,否则,您应该自己定义它:

DETACHED_PROCESS = 0x00000008

pid = subprocess.Popen([sys.executable, "longtask.py"],
                       creationflags=DETACHED_PROCESS).pid

I found this here:

On windows (win xp), the parent process will not finish until the longtask.py has finished its work. It is not what you want in CGI-script. The problem is not specific to Python, in PHP community the problems are the same.

The solution is to pass DETACHED_PROCESS Process Creation Flag to the underlying CreateProcess function in win API. If you happen to have installed pywin32 you can import the flag from the win32process module, otherwise you should define it yourself:

DETACHED_PROCESS = 0x00000008

pid = subprocess.Popen([sys.executable, "longtask.py"],
                       creationflags=DETACHED_PROCESS).pid

回答 4

subprocess.Popen()close_fds=True参数一起使用,这将允许将生成的子流程与Python流程本身分离,甚至在Python退出后也可以继续运行。

https://gist.github.com/yinjimmy/d6ad0742d03d54518e9f

import os, time, sys, subprocess

if len(sys.argv) == 2:
    time.sleep(5)
    print 'track end'
    if sys.platform == 'darwin':
        subprocess.Popen(['say', 'hello'])
else:
    print 'main begin'
    subprocess.Popen(['python', os.path.realpath(__file__), '0'], close_fds=True)
    print 'main end'

Use subprocess.Popen() with the close_fds=True parameter, which will allow the spawned subprocess to be detached from the Python process itself and continue running even after Python exits.

https://gist.github.com/yinjimmy/d6ad0742d03d54518e9f

import os, time, sys, subprocess

if len(sys.argv) == 2:
    time.sleep(5)
    print 'track end'
    if sys.platform == 'darwin':
        subprocess.Popen(['say', 'hello'])
else:
    print 'main begin'
    subprocess.Popen(['python', os.path.realpath(__file__), '0'], close_fds=True)
    print 'main end'

回答 5

您可能想开始研究os模块以派生不同的线程(通过打开交互式会话并发出help(os))。相关功能是fork和任何exec功能。为了让您了解如何启动,请在执行fork的函数中放入类似的内容(该函数需要使用列表或元组’args’作为包含程序名称及其参数的参数;您可能还需要为新线程定义stdin,out和err):

try:
    pid = os.fork()
except OSError, e:
    ## some debug output
    sys.exit(1)
if pid == 0:
    ## eventually use os.putenv(..) to set environment variables
    ## os.execv strips of args[0] for the arguments
    os.execv(args[0], args)

You probably want to start investigating the os module for forking different threads (by opening an interactive session and issuing help(os)). The relevant functions are fork and any of the exec ones. To give you an idea on how to start, put something like this in a function that performs the fork (the function needs to take a list or tuple ‘args’ as an argument that contains the program’s name and its parameters; you may also want to define stdin, out and err for the new thread):

try:
    pid = os.fork()
except OSError, e:
    ## some debug output
    sys.exit(1)
if pid == 0:
    ## eventually use os.putenv(..) to set environment variables
    ## os.execv strips of args[0] for the arguments
    os.execv(args[0], args)

回答 6

捕获输出并在后台运行 threading

本答案所述,如果您使用捕获输出,stdout=然后尝试进行read(),则该过程将阻塞。

但是,在某些情况下您需要这样做。例如,我想启动两个进程,它们通过它们之间的端口进行通信,并将它们的stdout保存到日志文件和stdout中。

threading模块使我们能够做到这一点。

首先,看看如何在此问题中单独完成输出重定向:Python Popen:同时写入stdout和日志文件

然后:

main.py

#!/usr/bin/env python3

import os
import subprocess
import sys
import threading

def output_reader(proc, file):
    while True:
        byte = proc.stdout.read(1)
        if byte:
            sys.stdout.buffer.write(byte)
            sys.stdout.flush()
            file.buffer.write(byte)
        else:
            break

with subprocess.Popen(['./sleep.py', '0'], stdout=subprocess.PIPE, stderr=subprocess.PIPE) as proc1, \
     subprocess.Popen(['./sleep.py', '10'], stdout=subprocess.PIPE, stderr=subprocess.PIPE) as proc2, \
     open('log1.log', 'w') as file1, \
     open('log2.log', 'w') as file2:
    t1 = threading.Thread(target=output_reader, args=(proc1, file1))
    t2 = threading.Thread(target=output_reader, args=(proc2, file2))
    t1.start()
    t2.start()
    t1.join()
    t2.join()

sleep.py

#!/usr/bin/env python3

import sys
import time

for i in range(4):
    print(i + int(sys.argv[1]))
    sys.stdout.flush()
    time.sleep(0.5)

运行后:

./main.py

标准输出每0.5秒更新一次,每两行包含一次:

0
10
1
11
2
12
3
13

每个日志文件都包含给定进程的相应日志。

灵感来源:https//eli.thegreenplace.net/2017/interacting-with-a-long-running-child-process-in-python/

已在Ubuntu 18.04,Python 3.6.7上测试。

Both capture output and run on background with threading

As mentioned on this answer, if you capture the output with stdout= and then try to read(), then the process blocks.

However, there are cases where you need this. For example, I wanted to launch two processes that talk over a port between them, and save their stdout to a log file and stdout.

The threading module allows us to do that.

First, have a look at how to do the output redirection part alone in this question: Python Popen: Write to stdout AND log file simultaneously

Then:

main.py

#!/usr/bin/env python3

import os
import subprocess
import sys
import threading

def output_reader(proc, file):
    while True:
        byte = proc.stdout.read(1)
        if byte:
            sys.stdout.buffer.write(byte)
            sys.stdout.flush()
            file.buffer.write(byte)
        else:
            break

with subprocess.Popen(['./sleep.py', '0'], stdout=subprocess.PIPE, stderr=subprocess.PIPE) as proc1, \
     subprocess.Popen(['./sleep.py', '10'], stdout=subprocess.PIPE, stderr=subprocess.PIPE) as proc2, \
     open('log1.log', 'w') as file1, \
     open('log2.log', 'w') as file2:
    t1 = threading.Thread(target=output_reader, args=(proc1, file1))
    t2 = threading.Thread(target=output_reader, args=(proc2, file2))
    t1.start()
    t2.start()
    t1.join()
    t2.join()

sleep.py

#!/usr/bin/env python3

import sys
import time

for i in range(4):
    print(i + int(sys.argv[1]))
    sys.stdout.flush()
    time.sleep(0.5)

After running:

./main.py

stdout get updated every 0.5 seconds for every two lines to contain:

0
10
1
11
2
12
3
13

and each log file contains the respective log for a given process.

Inspired by: https://eli.thegreenplace.net/2017/interacting-with-a-long-running-child-process-in-python/

Tested on Ubuntu 18.04, Python 3.6.7.


检查给定键是否已存在于字典中并递增

问题:检查给定键是否已存在于字典中并递增

给定字典,我如何找出该字典中的给定键是否已设置为非值?

即,我想这样做:

my_dict = {}

if (my_dict[key] != None):
  my_dict[key] = 1
else:
  my_dict[key] += 1

即,如果要已有一个,我想增加该值,否则,请将该值设置为1。

Given a dictionary, how can I find out if a given key in that dictionary has already been set to a non-None value?

I.e., I want to do this:

my_dict = {}

if (my_dict[key] != None):
  my_dict[key] = 1
else:
  my_dict[key] += 1

I.e., I want to increment the value if there’s already one there, or set it to 1 otherwise.


回答 0

您正在寻找collections.defaultdict(适用于Python 2.5+)。这个

from collections import defaultdict

my_dict = defaultdict(int)
my_dict[key] += 1

会做你想要的。

对于常规Python而言dict,如果给定键没有值,则访问dict时不会获得结果NoneKeyError将会引发a。因此,如果您想使用Regular dict而不是代码,则可以使用

if key in my_dict:
    my_dict[key] += 1
else:
    my_dict[key] = 1

You are looking for collections.defaultdict (available for Python 2.5+). This

from collections import defaultdict

my_dict = defaultdict(int)
my_dict[key] += 1

will do what you want.

For regular Python dicts, if there is no value for a given key, you will not get None when accessing the dict — a KeyError will be raised. So if you want to use a regular dict, instead of your code you would use

if key in my_dict:
    my_dict[key] += 1
else:
    my_dict[key] = 1

回答 1

我更喜欢用一行代码来做到这一点。

my_dict = {}

my_dict [some_key] = my_dict.get(some_key,0)+ 1

字典具有一个函数get,该函数带有两个参数-所需的键和默认值(如果不存在)。我更喜欢这种方法作为defaultdict,因为您只想处理在这一行代码中不存在该键,而不是在所有地方都不存在该键的情况。

I prefer to do this in one line of code.

my_dict = {}

my_dict[some_key] = my_dict.get(some_key, 0) + 1

Dictionaries have a function, get, which takes two parameters – the key you want, and a default value if it doesn’t exist. I prefer this method to defaultdict as you only want to handle the case where the key doesn’t exist in this one line of code, not everywhere.


回答 2

我个人喜欢使用 setdefault()

my_dict = {}

my_dict.setdefault(some_key, 0)
my_dict[some_key] += 1

I personally like using setdefault()

my_dict = {}

my_dict.setdefault(some_key, 0)
my_dict[some_key] += 1

回答 3

您需要这样的key in dict成语。

if key in my_dict and not (my_dict[key] is None):
  # do something
else:
  # do something else

但是,您可能应该考虑使用defaultdict(按dF建议)。

You need the key in dict idiom for that.

if key in my_dict and not (my_dict[key] is None):
  # do something
else:
  # do something else

However, you should probably consider using defaultdict (as dF suggested).


回答 4

要回答“ 我如何找出该字典中的给定索引是否已设置为非值 ”的问题,我希望这样做:

try:
  nonNone = my_dict[key] is not None
except KeyError:
  nonNone = False

这符合已被引用的EAFP概念(更容易先请求宽恕然后再允许)。它也避免了字典中重复的键查找,因为key in my_dict and my_dict[key] is not None如果查找很昂贵,那会很有趣。

对于您提出的实际问题,即增加一个int(如果存在),或者将其设置为默认值,我也建议

my_dict[key] = my_dict.get(key, default) + 1

就像安德鲁·威尔金森(Andrew Wilkinson)的回答一样。

如果要在字典中存储可修改的对象,则有第三种解决方案。一个常见的示例是multimap,您可以在其中存储键的元素列表。在这种情况下,您可以使用:

my_dict.setdefault(key, []).append(item)

如果字典中不存在key的值,则setdefault方法会将其设置为setdefault的第二个参数。它的行为就像标准的my_dict [key]一样,返回键的值(可能是新设置的值)。

To answer the question “how can I find out if a given index in that dict has already been set to a non-None value“, I would prefer this:

try:
  nonNone = my_dict[key] is not None
except KeyError:
  nonNone = False

This conforms to the already invoked concept of EAFP (easier to ask forgiveness then permission). It also avoids the duplicate key lookup in the dictionary as it would in key in my_dict and my_dict[key] is not None what is interesting if lookup is expensive.

For the actual problem that you have posed, i.e. incrementing an int if it exists, or setting it to a default value otherwise, I also recommend the

my_dict[key] = my_dict.get(key, default) + 1

as in the answer of Andrew Wilkinson.

There is a third solution if you are storing modifyable objects in your dictionary. A common example for this is a multimap, where you store a list of elements for your keys. In that case, you can use:

my_dict.setdefault(key, []).append(item)

If a value for key does not exist in the dictionary, the setdefault method will set it to the second parameter of setdefault. It behaves just like a standard my_dict[key], returning the value for the key (which may be the newly set value).


回答 5

同意cgoldberg。我是怎么做的:

try:
    dict[key] += 1
except KeyError:
    dict[key] = 1

因此,要么如上所述,要么使用其他人建议的默认字典。不要使用if语句。那不是Pythonic。

Agreed with cgoldberg. How I do it is:

try:
    dict[key] += 1
except KeyError:
    dict[key] = 1

So either do it as above, or use a default dict as others have suggested. Don’t use if statements. That’s not Pythonic.


回答 6

从许多答案中可以看出,有几种解决方案。has_key()方法尚未提及LBYL的一个实例(三步前进)。

my_dict = {}

def add (key):
    if my_dict.has_key(key):
        my_dict[key] += 1
    else:
        my_dict[key] = 1

if __name__ == '__main__':
    add("foo")
    add("bar")
    add("foo")
    print my_dict

As you can see from the many answers, there are several solutions. One instance of LBYL (look before you leap) has not been mentioned yet, the has_key() method:

my_dict = {}

def add (key):
    if my_dict.has_key(key):
        my_dict[key] += 1
    else:
        my_dict[key] = 1

if __name__ == '__main__':
    add("foo")
    add("bar")
    add("foo")
    print my_dict

回答 7

您尝试执行此操作的方法称为LBYL(跳前先查看),因为您在尝试尝试增加值之前正在检查条件。

另一种方法称为EAFP(更容易先请求宽恕然后再允许)。在这种情况下,您只需尝试操作(增加值)。如果失败,则捕获该异常并将其值设置为1。这是使用Python的方式稍多一些(IMO)。

http://mail.python.org/pipermail/python-list/2003-May/205182.html

The way you are trying to do it is called LBYL (look before you leap), since you are checking conditions before trying to increment your value.

The other approach is called EAFP (easier to ask forgiveness then permission). In that case, you would just try the operation (increment the value). If it fails, you catch the exception and set the value to 1. This is a slightly more Pythonic way to do it (IMO).

http://mail.python.org/pipermail/python-list/2003-May/205182.html


回答 8

有点晚了,但这应该可行。

my_dict = {}
my_dict[key] = my_dict[key] + 1 if key in my_dict else 1

A bit late but this should work.

my_dict = {}
my_dict[key] = my_dict[key] + 1 if key in my_dict else 1

回答 9

这不是直接回答问题,但对我来说,您似乎可能需要collections.Counter的功能。

from collections import Counter

to_count = ["foo", "foo", "bar", "baz", "foo", "bar"]

count = Counter(to_count)

print(count)

print("acts just like the desired dictionary:")
print("bar occurs {} times".format(count["bar"]))

print("any item that does not occur in the list is set to 0:")
print("dog occurs {} times".format(count["dog"]))

print("can iterate over items from most frequent to least:")
for item, times in count.most_common():
    print("{} occurs {} times".format(item, times))

这导致输出

Counter({'foo': 3, 'bar': 2, 'baz': 1})
acts just like the desired dictionary:
bar occurs 2 times
any item that does not occur in the list is set to 0:
dog occurs 0 times
can iterate over items from most frequent to least:
foo occurs 3 times
bar occurs 2 times
baz occurs 1 times

This isn’t directly answering the question, but to me, it looks like you might want the functionality of collections.Counter.

from collections import Counter

to_count = ["foo", "foo", "bar", "baz", "foo", "bar"]

count = Counter(to_count)

print(count)

print("acts just like the desired dictionary:")
print("bar occurs {} times".format(count["bar"]))

print("any item that does not occur in the list is set to 0:")
print("dog occurs {} times".format(count["dog"]))

print("can iterate over items from most frequent to least:")
for item, times in count.most_common():
    print("{} occurs {} times".format(item, times))

This results in the output

Counter({'foo': 3, 'bar': 2, 'baz': 1})
acts just like the desired dictionary:
bar occurs 2 times
any item that does not occur in the list is set to 0:
dog occurs 0 times
can iterate over items from most frequent to least:
foo occurs 3 times
bar occurs 2 times
baz occurs 1 times

回答 10

这是我最近为解决此问题而想出的一种方法。它基于setdefault词典方法:

my_dict = {}
my_dict[key] = my_dict.setdefault(key, 0) + 1

Here’s one-liner that I came up with recently for solving this problem. It’s based on the setdefault dictionary method:

my_dict = {}
my_dict[key] = my_dict.setdefault(key, 0) + 1

回答 11

我一直在寻找它,没有在网上找到它,然后尝试使用Try / Error运气并找到了它

my_dict = {}

if my_dict.__contains__(some_key):
  my_dict[some_key] += 1
else:
  my_dict[some_key] = 1

I was looking for it, didn’t found it on web then tried my luck with Try/Error and found it

my_dict = {}

if my_dict.__contains__(some_key):
  my_dict[some_key] += 1
else:
  my_dict[some_key] = 1