是什么导致[* a]总体化?

问题:是什么导致[* a]总体化?

显然list(a)不是总归,[x for x in a]在某些时候[*a]总归,始终都是总归吗?

这是从0到12的大小n,以及三种方法的结果大小(以字节为单位):

0 56 56 56
1 64 88 88
2 72 88 96
3 80 88 104
4 88 88 112
5 96 120 120
6 104 120 128
7 112 120 136
8 120 120 152
9 128 184 184
10 136 184 192
11 144 184 200
12 152 184 208

这样计算,可以使用Python 3 在repl.it中重现。8

from sys import getsizeof

for n in range(13):
    a = [None] * n
    print(n, getsizeof(list(a)),
             getsizeof([x for x in a]),
             getsizeof([*a]))

因此:这是如何工作的?如何整体化[*a]?实际上,它使用什么机制从给定的输入创建结果列表?它是否使用了迭代器a并使用了类似的东西list.append?源代码在哪里?

产生图像的数据和代码协作。)

放大到较小的n:

缩小到较大的n:

Apparently list(a) doesn’t overallocate, [x for x in a] overallocates at some points, and [*a] overallocates all the time?

Here are sizes n from 0 to 12 and the resulting sizes in bytes for the three methods:

0 56 56 56
1 64 88 88
2 72 88 96
3 80 88 104
4 88 88 112
5 96 120 120
6 104 120 128
7 112 120 136
8 120 120 152
9 128 184 184
10 136 184 192
11 144 184 200
12 152 184 208

Computed like this, reproducable at repl.it, using Python 3.8:

from sys import getsizeof

for n in range(13):
    a = [None] * n
    print(n, getsizeof(list(a)),
             getsizeof([x for x in a]),
             getsizeof([*a]))

So: How does this work? How does [*a] overallocate? Actually, what mechanism does it use to create the result list from the given input? Does it use an iterator over a and use something like list.append? Where is the source code?

(Colab with data and code that produced the images.)

Zooming in to smaller n:

Zooming out to larger n:


回答 0

[*a] 在内部执行C等效于

  1. 新建一个空的 list
  2. 呼叫 newlist.extend(a)
  3. 返回list

因此,如果您将测试扩展到:

from sys import getsizeof

for n in range(13):
    a = [None] * n
    l = []
    l.extend(a)
    print(n, getsizeof(list(a)),
             getsizeof([x for x in a]),
             getsizeof([*a]),
             getsizeof(l))

在线尝试!

你会看到的结果getsizeof([*a])l = []; l.extend(a); getsizeof(l)是相同的。

通常这是正确的做法;在extending时,您通常希望以后再添加,对于一般的解压缩,类似的情况是,假设要在一个接一个的基础上添加多个内容。[*a]这不是正常情况;Python假定list[*a, b, c, *d])中添加了多个项目或可迭代项,因此在常见情况下,过度分配可以节省工作。

相比之下,list由单个预先确定的可迭代大小(带有list())构成的结构在使用过程中可能不会增长或收缩,并且积算过早,除非另外证明。Python最近修复了一个错误,该错误使构造函数甚至可以对已知大小的输入进行整体分配

至于list理解,它们实际上等效于重复的appends,因此当您一次添加一个元素时,您会看到正常的过度分配增长模式的最终结果。

需要明确的是,这都不是语言保证。这就是CPython实现它的方式。Python语言规范通常与特定的增长模式无关list(除了保证从末期开始摊销O(1) appends和pops)。如评论中所述,具体实现在3.9中再次更改;尽管这不会影响[*a],但可能会影响其他情况,这些情况曾经是“构建tuple单个项目的临时内容,然后extend使用tuple”,现在变成的多个应用程序LIST_APPEND,当发生超额分配以及要计算的数字是多少时,该应用程序可能会发生变化。

[*a] is internally doing the C equivalent of:

  1. Make a new, empty list
  2. Call newlist.extend(a)
  3. Returns list.

So if you expand your test to:

from sys import getsizeof

for n in range(13):
    a = [None] * n
    l = []
    l.extend(a)
    print(n, getsizeof(list(a)),
             getsizeof([x for x in a]),
             getsizeof([*a]),
             getsizeof(l))

Try it online!

you’ll see the results for getsizeof([*a]) and l = []; l.extend(a); getsizeof(l) are the same.

This is usually the right thing to do; when extending you’re usually expecting to add more later, and similarly for generalized unpacking, it’s assumed that multiple things will be added one after the other. [*a] is not the normal case; Python assumes there are multiple items or iterables being added to the list ([*a, b, c, *d]), so overallocation saves work in the common case.

By contrast, a list constructed from a single, presized iterable (with list()) may not grow or shrink during use, and overallocating is premature until proven otherwise; Python recently fixed a bug that made the constructor overallocate even for inputs with known size.

As for list comprehensions, they’re effectively equivalent to repeated appends, so you’re seeing the final result of the normal overallocation growth pattern when adding an element at a time.

To be clear, none of this is a language guarantee. It’s just how CPython implements it. The Python language spec is generally unconcerned with specific growth patterns in list (aside from guaranteeing amortized O(1) appends and pops from the end). As noted in the comments, the specific implementation changes again in 3.9; while it won’t affect [*a], it could affect other cases where what used to be “build a temporary tuple of individual items and then extend with the tuple” now becomes multiple applications of LIST_APPEND, which can change when the overallocation occurs and what numbers go into the calculation.


回答 1

的全貌是什么情况,建立在其他的答案和评论(尤其是ShadowRanger的答案,这也解释了为什么它这样做)。

拆卸显示已BUILD_LIST_UNPACK被使用:

>>> import dis
>>> dis.dis('[*a]')
  1           0 LOAD_NAME                0 (a)
              2 BUILD_LIST_UNPACK        1
              4 RETURN_VALUE

这是在中ceval.c处理,它会构建一个空列表并将其扩展(带有a):

        case TARGET(BUILD_LIST_UNPACK): {
            ...
            PyObject *sum = PyList_New(0);
              ...
                none_val = _PyList_Extend((PyListObject *)sum, PEEK(i));

_PyList_Extend 用途 list_extend

_PyList_Extend(PyListObject *self, PyObject *iterable)
{
    return list_extend(self, iterable);
}

其中调用list_resize的总和为

list_extend(PyListObject *self, PyObject *iterable)
    ...
        n = PySequence_Fast_GET_SIZE(iterable);
        ...
        m = Py_SIZE(self);
        ...
        if (list_resize(self, m + n) < 0) {

overallocates如下:

list_resize(PyListObject *self, Py_ssize_t newsize)
{
  ...
    new_allocated = (size_t)newsize + (newsize >> 3) + (newsize < 9 ? 3 : 6);

让我们检查一下。用上面的公式计算预期的点数,并通过将预期的字节数乘以8(如我在此处使用64位Python)并添加一个空列表的字节数(即列表对象的固定开销)来计算预期的字节数:

from sys import getsizeof
for n in range(13):
    a = [None] * n
    expected_spots = n + (n >> 3) + (3 if n < 9 else 6)
    expected_bytesize = getsizeof([]) + expected_spots * 8
    real_bytesize = getsizeof([*a])
    print(n,
          expected_bytesize,
          real_bytesize,
          real_bytesize == expected_bytesize)

输出:

0 80 56 False
1 88 88 True
2 96 96 True
3 104 104 True
4 112 112 True
5 120 120 True
6 128 128 True
7 136 136 True
8 152 152 True
9 184 184 True
10 192 192 True
11 200 200 True
12 208 208 True

匹配,除了n = 0list_extend实际上是快捷方式,因此实际上也匹配:

        if (n == 0) {
            ...
            Py_RETURN_NONE;
        }
        ...
        if (list_resize(self, m + n) < 0) {

Full picture of what happens, building on the other answers and comments (especially ShadowRanger’s answer, which also explains why it’s done like that).

Disassembling shows that BUILD_LIST_UNPACK gets used:

>>> import dis
>>> dis.dis('[*a]')
  1           0 LOAD_NAME                0 (a)
              2 BUILD_LIST_UNPACK        1
              4 RETURN_VALUE

That’s handled in ceval.c, which builds an empty list and extends it (with a):

        case TARGET(BUILD_LIST_UNPACK): {
            ...
            PyObject *sum = PyList_New(0);
              ...
                none_val = _PyList_Extend((PyListObject *)sum, PEEK(i));

_PyList_Extend uses list_extend:

_PyList_Extend(PyListObject *self, PyObject *iterable)
{
    return list_extend(self, iterable);
}

Which calls list_resize with the sum of the sizes:

list_extend(PyListObject *self, PyObject *iterable)
    ...
        n = PySequence_Fast_GET_SIZE(iterable);
        ...
        m = Py_SIZE(self);
        ...
        if (list_resize(self, m + n) < 0) {

And that overallocates as follows:

list_resize(PyListObject *self, Py_ssize_t newsize)
{
  ...
    new_allocated = (size_t)newsize + (newsize >> 3) + (newsize < 9 ? 3 : 6);

Let’s check that. Compute the expected number of spots with the formula above, and compute the expected byte size by multiplying it with 8 (as I’m using 64-bit Python here) and adding an empty list’s byte size (i.e., a list object’s constant overhead):

from sys import getsizeof
for n in range(13):
    a = [None] * n
    expected_spots = n + (n >> 3) + (3 if n < 9 else 6)
    expected_bytesize = getsizeof([]) + expected_spots * 8
    real_bytesize = getsizeof([*a])
    print(n,
          expected_bytesize,
          real_bytesize,
          real_bytesize == expected_bytesize)

Output:

0 80 56 False
1 88 88 True
2 96 96 True
3 104 104 True
4 112 112 True
5 120 120 True
6 128 128 True
7 136 136 True
8 152 152 True
9 184 184 True
10 192 192 True
11 200 200 True
12 208 208 True

Matches except for n = 0, which list_extend actually shortcuts, so actually that matches, too:

        if (n == 0) {
            ...
            Py_RETURN_NONE;
        }
        ...
        if (list_resize(self, m + n) < 0) {

回答 2

这些将是CPython解释器的实现细节,因此在其他解释器之间可能不一致。

也就是说,您可以list(a)在这里看到理解和行为的位置:

https://github.com/python/cpython/blob/master/Objects/listobject.c#L36

专为理解:

 * The growth pattern is:  0, 4, 8, 16, 25, 35, 46, 58, 72, 88, ...
...

new_allocated = (size_t)newsize + (newsize >> 3) + (newsize < 9 ? 3 : 6);

在这些行的下面,有list_preallocate_exact调用时使用的行list(a)

These are going to be implementation details of the CPython interpreter, and so may not be consistent across other interpreters.

That said, you can see where the comprehension and list(a) behaviors come in here:

https://github.com/python/cpython/blob/master/Objects/listobject.c#L36

Specifically for the comprehension:

 * The growth pattern is:  0, 4, 8, 16, 25, 35, 46, 58, 72, 88, ...
...

new_allocated = (size_t)newsize + (newsize >> 3) + (newsize < 9 ? 3 : 6);

Just below those lines, there is list_preallocate_exact which is used when calling list(a).


简洁的书写方式(a + b == c或a + c == b或b + c == a)

问题:简洁的书写方式(a + b == c或a + c == b或b + c == a)

是否有更紧凑或Pythonic的方式来编写布尔表达式

a + b == c or a + c == b or b + c == a

我想出了

a + b + c in (2*a, 2*b, 2*c)

但这有点奇怪。

Is there a more compact or pythonic way to write the boolean expression

a + b == c or a + c == b or b + c == a

I came up with

a + b + c in (2*a, 2*b, 2*c)

but that is a little strange.


回答 0

如果我们看一下Python的Zen,请强调一下:

提姆·彼得斯(Tim Peters)撰写的《 Python之禅》

美丽胜于丑陋。
显式胜于隐式。
简单胜于复杂。
复杂胜于复杂。
扁平比嵌套更好。
稀疏胜于密集。
可读性很重要。
特殊情况还不足以打破规则。
尽管实用性胜过纯度。
错误绝不能默默传递。
除非明确地保持沉默。
面对模棱两可的想法,拒绝猜测的诱惑。
应该有一种-最好只有一种-显而易见的方法。
尽管除非您是荷兰人,否则一开始这种方式可能并不明显。
现在总比没有好。
虽然从来没有比这更好正确的现在。
如果实现难以解释,那是个坏主意。
如果实现易于解释,则可能是个好主意。
命名空间是一个很棒的主意-让我们做更多这些吧!

最Python化的解决方案是最清晰,最简单和最容易解释的解决方案:

a + b == c or a + c == b or b + c == a

更好的是,您甚至不需要了解Python就能理解此代码!就这么简单。这是毫无保留的最佳解决方案。其他一切都是智力上的自慰。

此外,这可能也是性能最佳的解决方案,因为它是所有短路建议中的唯一解决方案。如果为a + b == c,则仅进行一次加法和比较。

If we look at the Zen of Python, emphasis mine:

The Zen of Python, by Tim Peters

Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren’t special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one– and preferably only one –obvious way to do it.
Although that way may not be obvious at first unless you’re Dutch.
Now is better than never.
Although never is often better than right now.
If the implementation is hard to explain, it’s a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea — let’s do more of those!

The most Pythonic solution is the one that is clearest, simplest, and easiest to explain:

a + b == c or a + c == b or b + c == a

Even better, you don’t even need to know Python to understand this code! It’s that easy. This is, without reservation, the best solution. Anything else is intellectual masturbation.

Furthermore, this is likely the best performing solution as well, as it is the only one out of all the proposals that short circuits. If a + b == c, only a single addition and comparison is done.


回答 1

解决以下三个等式:

a in (b+c, b-c, c-b)

Solving the three equalities for a:

a in (b+c, b-c, c-b)

回答 2

Python具有对序列的所有元素any执行的功能or。在这里,我已经将您的陈述转换为3元素元组。

any((a + b == c, a + c == b, b + c == a))

请注意,这or是短路的,因此,如果计算单个条件的成本很高,则最好保留原始结构。

Python has an any function that does an or on all the elements of a sequence. Here I’ve converted your statement into a 3-element tuple.

any((a + b == c, a + c == b, b + c == a))

Note that or is short circuiting, so if calculating the individual conditions is expensive it might be better to keep your original construct.


回答 3

如果您知道只处理正数,则可以使用,并且很干净:

a, b, c = sorted((a, b, c))
if a + b == c:
    do_stuff()

正如我所说,这仅适用于正数;但是,如果您知道它们将是肯定的,那么这是一个非常易读的IMO解决方案,即使直接在代码中而不是在函数中。

您可以执行此操作,这可能需要进行一些重复的计算。但是您没有将性能指定为目标:

from itertools import permutations

if any(x + y == z for x, y, z in permutations((a, b, c), 3)):
    do_stuff()

有无permutations()重复计算的可能性:

if any(x + y == z for x, y, z in [(a, b, c), (a, c, b), (b, c, a)]:
    do_stuff()

我可能会将此函数或任何其他解决方案放入函数中。然后,您可以在代码中干净地调用该函数。

就个人而言,除非我需要代码提供更多的灵活性,否则我只会在您的问题中使用第一种方法。简单高效。我仍然可以将其放入函数中:

def two_add_to_third(a, b, c):
    return a + b == c or a + c == b or b + c == a

if two_add_to_third(a, b, c):
    do_stuff()

那是相当Pythonic的,并且可能是最有效的方式(除了额外的函数调用);尽管您无论如何也不必太担心性能,除非它确实引起了问题。

If you know you’re only dealing with positive numbers, this will work, and is pretty clean:

a, b, c = sorted((a, b, c))
if a + b == c:
    do_stuff()

As I said, this only works for positive numbers; but if you know they’re going to be positive, this is a very readable solution IMO, even directly in the code as opposed to in a function.

You could do this, which might do a bit of repeated computation; but you didn’t specify performance as your goal:

from itertools import permutations

if any(x + y == z for x, y, z in permutations((a, b, c), 3)):
    do_stuff()

Or without permutations() and the possibility of repeated computations:

if any(x + y == z for x, y, z in [(a, b, c), (a, c, b), (b, c, a)]:
    do_stuff()

I would probably put this, or any other solution, into a function. Then you can just cleanly call the function in your code.

Personally, unless I needed more flexibility from the code, I would just use the first method in your question. It’s simple and efficient. I still might put it into a function:

def two_add_to_third(a, b, c):
    return a + b == c or a + c == b or b + c == a

if two_add_to_third(a, b, c):
    do_stuff()

That’s pretty Pythonic, and it’s quite possibly the most efficient way to do it (the extra function call aside); although you shouldn’t worry too much about performance anyway, unless it’s actually causing an issue.


回答 4

如果仅使用三个变量,则使用初始方法:

a + b == c or a + c == b or b + c == a

已经很pythonic了。

如果您打算使用更多的变量,那么您的推理方法如下:

a + b + c in (2*a, 2*b, 2*c)

非常聪明,但请考虑一下原因。为什么这样做?
通过一些简单的算法,我们看到:

a + b = c
c = c
a + b + c == c + c == 2*c
a + b + c == 2*c

而这将不得不为任何一个,b或c保持为真,这意味着是的,它会等于2*a2*b2*c。任何数量的变量都是如此。

因此,快速编写此代码的一种好方法是只包含一个变量列表,并对照一倍的值列表检查它们的总和。

values = [a,b,c,d,e,...]
any(sum(values) in [2*x for x in values])

这样,要将更多变量添加到方程式中,您要做的就是通过“ n”个新变量来编辑值列表,而不是编写“ n”个方程式

If you will only be using three variables then your initial method:

a + b == c or a + c == b or b + c == a

Is already very pythonic.

If you plan on using more variables then your method of reasoning with:

a + b + c in (2*a, 2*b, 2*c)

Is very smart but lets think about why. Why does this work?
Well through some simple arithmetic we see that:

a + b = c
c = c
a + b + c == c + c == 2*c
a + b + c == 2*c

And this will have to hold true for either a,b, or c, meaning that yes it will equal 2*a, 2*b, or 2*c. This will be true for any number of variables.

So a good way to write this quickly would be to simply have a list of your variables and check their sum against a list of the doubled values.

values = [a,b,c,d,e,...]
any(sum(values) in [2*x for x in values])

This way, to add more variables into the equation all you have to do is edit your values list by ‘n’ new variables, not write ‘n’ equations


回答 5

以下代码可用于将每个元素与其他元素的总和进行迭代比较,这是根据整个列表的总和(不包括该元素)计算得出的。

 l = [a,b,c]
 any(sum(l)-e == e for e in l)

The following code can be used to iteratively compare each element with the sum of the others, which is computed from sum of the whole list, excluding that element.

 l = [a,b,c]
 any(sum(l)-e == e for e in l)

回答 6

不要尝试简化它。取而代之的是使用函数命名

def any_two_sum_to_third(a, b, c):
  return a + b == c or a + c == b or b + c == a

if any_two_sum_to_third(foo, bar, baz):
  ...

将条件替换为“聪明”可能会使它更短,但不会使其更具可读性。但是,如何保留它也不是很容易理解,因为要知道为什么要一眼检查这三个条件是很棘手的。这使您可以清楚地确定要检查的内容。

关于性能,这种方法的确增加了函数调用的开销,但是除非您发现了绝对必须解决的瓶颈,否则不要牺牲性能的可读性。并始终进行测量,因为某些巧妙的实现能够在某些情况下优化并内联某些函数调用。

Don’t try and simplify it. Instead, name what you’re doing with a function:

def any_two_sum_to_third(a, b, c):
  return a + b == c or a + c == b or b + c == a

if any_two_sum_to_third(foo, bar, baz):
  ...

Replace the condition with something “clever” might make it shorter, but it won’t make it more readable. Leaving it how it is isn’t very readable either however, because it’s tricky to know why you’re checking those three conditions at a glance. This makes it absolutely crystal clear what you’re checking for.

Regarding performance, this approach does add the overhead of a function call, but never sacrifice readability for performance unless you’ve found a bottleneck you absolutely must fix. And always measure, as some clever implementations are capable of optimizing away and inlining some function calls in some circumstances.


回答 7

Python 3:

(a+b+c)/2 in (a,b,c)
(a+b+c+d)/2 in (a,b,c,d)
...

它可以缩放为任意数量的变量:

arr = [a,b,c,d,...]
sum(arr)/2 in arr

但是,总的来说,我同意除非您拥有三个以上的变量,否则原始版本更具可读性。

Python 3:

(a+b+c)/2 in (a,b,c)
(a+b+c+d)/2 in (a,b,c,d)
...

It scales to any number of variables:

arr = [a,b,c,d,...]
sum(arr)/2 in arr

However, in general I agree that unless you have more than three variables, the original version is more readable.


回答 8

(a+b-c)*(a+c-b)*(b+c-a) == 0

如果任意两项的总和等于第三项,则其中一个因子将为零,从而使整个乘积为零。

(a+b-c)*(a+c-b)*(b+c-a) == 0

If the sum of any two terms is equal to the third term, then one of the factors will be zero, making the entire product zero.


回答 9

怎么样:

a == b + c or abs(a) == abs(b - c)

请注意,如果变量是无符号的,这将不起作用。

从代码优化的角度(至少在x86平台上),这似乎是最有效的解决方案。

现代编译器将内联两个abs()函数调用,并通过使用巧妙的CDQ,XOR和SUB指令序列来避免符号测试和随后的条件分支。因此,上面的高级代码将仅由低延迟,高吞吐量的ALU指令和仅两个条件表示。

How about just:

a == b + c or abs(a) == abs(b - c)

Note that this won’t work if variables are unsigned.

From the viewpoint of code optimization (at least on x86 platform) this seems to be the most efficient solution.

Modern compilers will inline both abs() function calls and avoid sign testing and subsequent conditional branch by using a clever sequence of CDQ, XOR, and SUB instructions. The above high-level code will thus be represented with only low-latency, high-throughput ALU instructions and just two conditionals.


回答 10

Alex Varga提供的解决方案“ a in(b + c,bc,cb)”是紧凑的,并且在数学上很漂亮,但实际上我不会那样写代码,因为下一个开发人员不会立即理解代码的目的。

马克·兰瑟姆(Mark Ransom)的解决方案

any((a + b == c, a + c == b, b + c == a))

比它更清晰,但不比它更简洁

a + b == c or a + c == b or b + c == a

当编写代码时,别人不得不去看,或者当我忘记了编写代码时的想法时,我将不得不花很长时间去看,太短或太聪明往往弊大于利。代码应可读。简洁是件好事,但不是那么简洁,以致下一个程序员无法理解。

The solution provided by Alex Varga “a in (b+c, b-c, c-b)” is compact and mathematically beautiful, but I wouldn’t actually write code that way because the next developer coming along would not immediately understand the purpose of the code.

Mark Ransom’s solution of

any((a + b == c, a + c == b, b + c == a))

is more clear but not much more succinct than

a + b == c or a + c == b or b + c == a

When writing code that someone else will have to look at, or that I will have to look at a long time later when I have forgotten what I was thinking when I wrote it, being too short or clever tends to do more harm than good. Code should be readable. So succinct is good, but not so succinct that the next programmer can’t understand it.


回答 11

请求的是更紧凑或更Pythonic-我尝试了更紧凑。

给定

import functools, itertools
f = functools.partial(itertools.permutations, r = 3)
def g(x,y,z):
    return x + y == z

比原版少2个字符

any(g(*args) for args in f((a,b,c)))

测试:

assert any(g(*args) for args in f((a,b,c))) == (a + b == c or a + c == b or b + c == a)

另外,给出:

h = functools.partial(itertools.starmap, g)

这是等效的

any(h(f((a,b,c))))

Request is for more compact OR more pythonic – I tried my hand at more compact.

given

import functools, itertools
f = functools.partial(itertools.permutations, r = 3)
def g(x,y,z):
    return x + y == z

This is 2 characters less than the original

any(g(*args) for args in f((a,b,c)))

test with:

assert any(g(*args) for args in f((a,b,c))) == (a + b == c or a + c == b or b + c == a)

additionally, given:

h = functools.partial(itertools.starmap, g)

This is equivalent

any(h(f((a,b,c))))

回答 12

我想介绍一下我认为是最pythonic的答案:

def one_number_is_the_sum_of_the_others(a, b, c):
    return any((a == b + c, b == a + c, c == a + b))

一般情况,未优化:

def one_number_is_the_sum_of_the_others(numbers):
    for idx in range(len(numbers)):
        remaining_numbers = numbers[:]
        sum_candidate = remaining_numbers.pop(idx)
        if sum_candidate == sum(remaining_numbers):
            return True
    return False 

就Python Zen而言,我认为强调的陈述比其他答案更受关注:

提姆·彼得斯(Tim Peters)撰写的《 Python之禅》

美丽胜于丑陋。
显式胜于隐式。
简单胜于复杂。
复杂胜于复杂。
扁平比嵌套更好。
稀疏胜于密集。
可读性很重要。
特殊情况还不足以打破规则。
尽管实用性胜过纯度。
错误绝不能默默传递。
除非明确地保持沉默。
面对模棱两可的想法,拒绝猜测的诱惑。
应该有一种-最好只有一种-显而易见的方法。
尽管除非您是荷兰人,否则一开始这种方式可能并不明显。
现在总比没有好。
虽然从来没有比这更好正确的现在。
如果实现难以解释,那是个坏主意。
如果实现易于解释,则可能是个好主意。
命名空间是一个很棒的主意-让我们做更多这些吧!

I want to present what I see as the most pythonic answer:

def one_number_is_the_sum_of_the_others(a, b, c):
    return any((a == b + c, b == a + c, c == a + b))

The general case, non-optimized:

def one_number_is_the_sum_of_the_others(numbers):
    for idx in range(len(numbers)):
        remaining_numbers = numbers[:]
        sum_candidate = remaining_numbers.pop(idx)
        if sum_candidate == sum(remaining_numbers):
            return True
    return False 

In terms of the Zen of Python I think the emphasized statements are more followed than from other answer:

The Zen of Python, by Tim Peters

Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren’t special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one– and preferably only one –obvious way to do it.
Although that way may not be obvious at first unless you’re Dutch.
Now is better than never.
Although never is often better than right now.
If the implementation is hard to explain, it’s a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea — let’s do more of those!


回答 13

作为我编程的一个老习惯,我认为将复杂的表达式放在子句中可以使其更具可读性,如下所示:

a == b+c or b == a+c or c == a+b

加号()

((a == b+c) or (b == a+c) or (c == a+b))

而且我认为使用多行也可以使更多的感觉像这样:

((a == b+c) or 
 (b == a+c) or 
 (c == a+b))

As an old habit of my programming, I think placing complex expression at right in a clause can make it more readable like this:

a == b+c or b == a+c or c == a+b

Plus ():

((a == b+c) or (b == a+c) or (c == a+b))

And also I think using multi-lines can also make more senses like this:

((a == b+c) or 
 (b == a+c) or 
 (c == a+b))

回答 14

以一般方式

m = a+b-c;
if (m == 0 || m == 2*a || m == 2*b) do_stuff ();

如果您可以操作输入变量,

c = a+b-c;
if (c==0 || c == 2*a || c == 2*b) do_stuff ();

如果要使用位hack进行利用,则可以使用“!”,“ >> 1”和“ << 1”

我避免了除法,尽管它可以避免两次乘法以避免舍入错误。但是,检查溢出

In a generic way,

m = a+b-c;
if (m == 0 || m == 2*a || m == 2*b) do_stuff ();

if, manipulating an input variable is OK for you,

c = a+b-c;
if (c==0 || c == 2*a || c == 2*b) do_stuff ();

if you want to exploit using bit hacks, you can use “!”, “>> 1” and “<< 1”

I avoided division though it enables use to avoid two multiplications to avoid round off errors. However, check for overflows


回答 15

def any_sum_of_others (*nums):
    num_elements = len(nums)
    for i in range(num_elements):
        discriminating_map = map(lambda j: -1 if j == i else 1, range(num_elements))
        if sum(n * u for n, u in zip(nums, discriminating_map)) == 0:
            return True
    return False

print(any_sum_of_others(0, 0, 0)) # True
print(any_sum_of_others(1, 2, 3)) # True
print(any_sum_of_others(7, 12, 5)) # True
print(any_sum_of_others(4, 2, 2)) # True
print(any_sum_of_others(1, -1, 0)) # True
print(any_sum_of_others(9, 8, -4)) # False
print(any_sum_of_others(4, 3, 2)) # False
print(any_sum_of_others(1, 1, 1, 1, 4)) # True
print(any_sum_of_others(0)) # True
print(any_sum_of_others(1)) # False
def any_sum_of_others (*nums):
    num_elements = len(nums)
    for i in range(num_elements):
        discriminating_map = map(lambda j: -1 if j == i else 1, range(num_elements))
        if sum(n * u for n, u in zip(nums, discriminating_map)) == 0:
            return True
    return False

print(any_sum_of_others(0, 0, 0)) # True
print(any_sum_of_others(1, 2, 3)) # True
print(any_sum_of_others(7, 12, 5)) # True
print(any_sum_of_others(4, 2, 2)) # True
print(any_sum_of_others(1, -1, 0)) # True
print(any_sum_of_others(9, 8, -4)) # False
print(any_sum_of_others(4, 3, 2)) # False
print(any_sum_of_others(1, 1, 1, 1, 4)) # True
print(any_sum_of_others(0)) # True
print(any_sum_of_others(1)) # False

在python中连接字符串和整数

问题:在python中连接字符串和整数

用python说你有

s = "string"
i = 0
print s+i

会给你错误,所以你写

print s+str(i) 

不会出错。

我认为这是处理int和字符串连接的笨拙方式。甚至Java也不需要显式转换为String来进行这种连接。有没有更好的方法来进行这种串联,即在Python中无需显式转换?

In python say you have

s = "string"
i = 0
print s+i

will give you error so you write

print s+str(i) 

to not get error.

I think this is quite a clumsy way to handle int and string concatenation. Even Java does not need explicit casting to String to do this sort of concatenation. Is there a better way to do this sort of concatenation i.e without explicit casting in Python?


回答 0

现代字符串格式:

"{} and {}".format("string", 1)

Modern string formatting:

"{} and {}".format("string", 1)

回答 1

没有字符串格式:

>> print 'Foo',0
Foo 0

No string formatting:

>> print 'Foo',0
Foo 0

回答 2

使用新样式.format()方法(默认使用.format()提供)进行字符串格式化:

 '{}{}'.format(s, i)

或更旧的,但“仍然存在”,- %格式化:

 '%s%d' %(s, i)

在以上两个示例中,串联的两个项目之间没有空格。如果需要空间,可以简单地将其添加到格式字符串中。

这些提供了 如何串联项目,它们之间的空间等很多控制和灵活性。有关的详细信息格式规范的请参见此

String formatting, using the new-style .format() method (with the defaults .format() provides):

 '{}{}'.format(s, i)

Or the older, but “still sticking around”, %-formatting:

 '%s%d' %(s, i)

In both examples above there’s no space between the two items concatenated. If space is needed, it can simply be added in the format strings.

These provide a lot of control and flexibility about how to concatenate items, the space between them etc. For details about format specifications see this.


回答 3

Python是一种有趣的语言,尽管通常有一种(或两种)“显而易见”的方式来完成任何给定的任务,但灵活性仍然存在。

s = "string"
i = 0

print (s + repr(i))

上面的代码段使用Python3语法编写,但始终允许打印后的括号(可选),直到版本3强制使用为止。

希望这可以帮助。

凯特琳

Python is an interesting language in that while there is usually one (or two) “obvious” ways to accomplish any given task, flexibility still exists.

s = "string"
i = 0

print (s + repr(i))

The above code snippet is written in Python3 syntax but the parentheses after print were always allowed (optional) until version 3 made them mandatory.

Hope this helps.

Caitlin


回答 4

format()方法可用于连接字符串和整数

print(s+"{}".format(i))

format() method can be used to concatenate string and integer

print(s+"{}".format(i))

回答 5

如果您只想打印哟,可以这样做:

print(s , i)

if you only want to print yo can do this:

print(s , i)

回答 6

在python 3.6及更高版本中,您可以像这样格式化它:

new_string = f'{s} {i}'
print(new_string)

要不就:

print(f'{s} {i}')

in python 3.6 and newer, you can format it just like this:

new_string = f'{s} {i}'
print(new_string)

or just:

print(f'{s} {i}')

减少Django的内存使用量。低挂水果?

问题:减少Django的内存使用量。低挂水果?

我的内存使用量随着时间的推移而增加,并且重新启动Django对用户而言并不友好。

我不确定如何分析内存使用情况,但是一些有关如何开始测量的提示将很有用。

我感觉有些简单的步骤可以带来很大的收益。确保将“调试”设置为“假”是显而易见的。

有人可以建议别人吗?在低流量的网站上缓存会带来多少改善?

在这种情况下,我使用mod_python在Apache 2.x下运行。我听说mod_wsgi较为精简,但在此阶段进行切换将非常棘手,除非我知道收益会很大。

编辑:感谢到目前为止的提示。关于如何发现内存用尽的任何建议?是否有任何有关Python内存分析的指南?

同样如前所述,有些事情会使切换到mod_wsgi变得很棘手,因此我想对在朝这个方向努力之前所能获得的收益有所了解。

编辑:卡尔在这里发布了更详细的回复,值得一读:Django部署:减少Apache的开销

编辑: Graham Dumpleton的文章是我在MPM和mod_wsgi相关的东西上找到的最好的文章。我很失望,但是没人能提供有关调试应用程序本身的内存使用情况的任何信息。

最终编辑:好吧,我一直在与Webfaction讨论这个问题,看他们是否可以协助重新编译Apache,这就是他们的话:

“我真的认为切换到MPM Worker + mod_wsgi设置不会给您带来太大的好处。我估计您可能可以节省20MB左右,但可能不超过20MB。”

所以!这使我回到了最初的问题(我仍然不明智)。如何确定问题所在?这是一个众所周知的准则,如果不进行测试以查看需要优化的地方就不会进行优化,但是关于测量Python内存使用情况的教程的方式很少,而针对Django的教程则完全没有。

感谢大家的帮助,但我认为这个问题仍然悬而未决!

另一个最终编辑;-)

我在django-users列表上问了这个,并得到了一些非常有帮助的回复

老实说,有史以来最后一次更新!

这是刚刚发布。可能是迄今为止最好的解决方案:使用Pympler分析Django对象的大小和内存使用情况

My memory usage increases over time and restarting Django is not kind to users.

I am unsure how to go about profiling the memory usage but some tips on how to start measuring would be useful.

I have a feeling that there are some simple steps that could produce big gains. Ensuring ‘debug’ is set to ‘False’ is an obvious biggie.

Can anyone suggest others? How much improvement would caching on low-traffic sites?

In this case I’m running under Apache 2.x with mod_python. I’ve heard mod_wsgi is a bit leaner but it would be tricky to switch at this stage unless I know the gains would be significant.

Edit: Thanks for the tips so far. Any suggestions how to discover what’s using up the memory? Are there any guides to Python memory profiling?

Also as mentioned there’s a few things that will make it tricky to switch to mod_wsgi so I’d like to have some idea of the gains I could expect before ploughing forwards in that direction.

Edit: Carl posted a slightly more detailed reply here that is worth reading: Django Deployment: Cutting Apache’s Overhead

Edit: Graham Dumpleton’s article is the best I’ve found on the MPM and mod_wsgi related stuff. I am rather disappointed that no-one could provide any info on debugging the memory usage in the app itself though.

Final Edit: Well I have been discussing this with Webfaction to see if they could assist with recompiling Apache and this is their word on the matter:

“I really don’t think that you will get much of a benefit by switching to an MPM Worker + mod_wsgi setup. I estimate that you might be able to save around 20MB, but probably not much more than that.”

So! This brings me back to my original question (which I am still none the wiser about). How does one go about identifying where the problems lies? It’s a well known maxim that you don’t optimize without testing to see where you need to optimize but there is very little in the way of tutorials on measuring Python memory usage and none at all specific to Django.

Thanks for everyone’s assistance but I think this question is still open!

Another final edit ;-)

I asked this on the django-users list and got some very helpful replies

Honestly the last update ever!

This was just released. Could be the best solution yet: Profiling Django object size and memory usage with Pympler


回答 0

确保您没有保留对数据的全局引用。这样可以防止python垃圾回收器释放内存。

不要使用mod_python。它在apache中加载一个解释器。如果需要使用apache,请mod_wsgi改用。切换并不困难。这很容易。与django -dead相比,为djangomod_wsgi进行配置更容易mod_python

如果您可以从需求中删除apache,那对您的记忆会更好。spawning似乎是运行python Web应用程序的新的快速可扩展方式。

编辑:我看不到如何切换到mod_wsgi可能是“ 棘手的 ”。这应该是一个非常容易的任务。请详细说明您在使用交换机时遇到的问题。

Make sure you are not keeping global references to data. That prevents the python garbage collector from releasing the memory.

Don’t use mod_python. It loads an interpreter inside apache. If you need to use apache, use mod_wsgi instead. It is not tricky to switch. It is very easy. mod_wsgi is way easier to configure for django than brain-dead mod_python.

If you can remove apache from your requirements, that would be even better to your memory. spawning seems to be the new fast scalable way to run python web applications.

EDIT: I don’t see how switching to mod_wsgi could be “tricky“. It should be a very easy task. Please elaborate on the problem you are having with the switch.


回答 1

如果您在mod_wsgi下运行,并且由于它是WSGI兼容的,则大概是在生成的,您可以使用Dozer查看您的内存使用情况。

在mod_wsgi下,只需将其添加到WSGI脚本的底部:

from dozer import Dozer
application = Dozer(application)

然后将浏览器指向http:// domain / _dozer / index以查看所有内存分配的列表。

我还要添加对mod_wsgi的支持之声。与mod_python相比,它在性能和内存使用方面有很大的不同。Graham Dumpleton对mod_wsgi的支持非常出色,无论是在积极开发方面还是在帮助邮件列表中的人员优化安装方面均如此。curse.com上的David Cramer 发布了一些图表(不幸的是,现在似乎找不到),显示了在该高流量站点上切换到mod_wsgi后cpu和内存使用量的急剧下降。django开发人员中有几个已经切换。说真的,这很容易:)

If you are running under mod_wsgi, and presumably spawning since it is WSGI compliant, you can use Dozer to look at your memory usage.

Under mod_wsgi just add this at the bottom of your WSGI script:

from dozer import Dozer
application = Dozer(application)

Then point your browser at http://domain/_dozer/index to see a list of all your memory allocations.

I’ll also just add my voice of support for mod_wsgi. It makes a world of difference in terms of performance and memory usage over mod_python. Graham Dumpleton’s support for mod_wsgi is outstanding, both in terms of active development and in helping people on the mailing list to optimize their installations. David Cramer at curse.com has posted some charts (which I can’t seem to find now unfortunately) showing the drastic reduction in cpu and memory usage after they switched to mod_wsgi on that high traffic site. Several of the django devs have switched. Seriously, it’s a no-brainer :)


回答 2

这些是我知道的Python内存探查器解决方案(与Django无关):

免责声明:我与后者有一定关系。

各个项目的文档应使您了解如何使用这些工具来分析Python应用程序的内存行为。

以下是一个不错的“战争故事”,还提供了一些有用的指导:

These are the Python memory profiler solutions I’m aware of (not Django related):

Disclaimer: I have a stake in the latter.

The individual project’s documentation should give you an idea of how to use these tools to analyze memory behavior of Python applications.

The following is a nice “war story” that also gives some helpful pointers:


回答 3

此外,检查是否不使用任何已知的泄漏器。由于Unicode处理中的错误,MySQLdb会泄漏Django的大量内存。除此之外,Django Debug Toolbar可能会帮助您跟踪猪。

Additionally, check if you do not use any of known leakers. MySQLdb is known to leak enormous amounts of memory with Django due to bug in unicode handling. Other than that, Django Debug Toolbar might help you to track the hogs.


回答 4

除了不保留对大型数据对象的全局引用之外,还应尽可能避免将大型数据集加载到内存中。

在守护程序模式下切换到mod_wsgi,并使用Apache的worker mpm代替prefork。后面的步骤可以使您以更少的内存开销为更多的并发用户提供服务。

In addition to not keeping around global references to large data objects, try to avoid loading large datasets into memory at all wherever possible.

Switch to mod_wsgi in daemon mode, and use Apache’s worker mpm instead of prefork. This latter step can allow you to serve many more concurrent users with much less memory overhead.


回答 5

Webfaction实际上有一些技巧可以降低Django的内存使用量。

要点:

  • 确保将debug设置为false(您已经知道)。
  • 在您的Apache配置中使用“ ServerLimit”
  • 检查内存中没有大对象
  • 考虑在单独的进程或服务器中提供静态内容。
  • 在您的apache配置中使用“ MaxRequestsPerChild”
  • 找出并了解您正在使用多少内存

Webfaction actually has some tips for keeping django memory usage down.

The major points:

  • Make sure debug is set to false (you already know that).
  • Use “ServerLimit” in your apache config
  • Check that no big objects are being loaded in memory
  • Consider serving static content in a separate process or server.
  • Use “MaxRequestsPerChild” in your apache config
  • Find out and understand how much memory you’re using

回答 6

mod_wsgi的另一个优点:maximum-requestsWSGIDaemonProcess指令中设置一个参数,mod_wsgi会每隔很长时间就重新启动守护进程。对用户来说,应该没有可见的效果,除了第一次刷新新进程时页面加载缓慢之外,因为它将把Django和您的应用程序代码加载到内存中。

但是,即使确实有内存泄漏,也应避免进程过大,而不必中断对用户的服务。

Another plus for mod_wsgi: set a maximum-requests parameter in your WSGIDaemonProcess directive and mod_wsgi will restart the daemon process every so often. There should be no visible effect for the user, other than a slow page load the first time a fresh process is hit, as it’ll be loading Django and your application code into memory.

But even if you do have memory leaks, that should keep the process size from getting too large, without having to interrupt service to your users.


回答 7

这是我用于mod_wsgi的脚本(称为wsgi.py,并放在django项目的根目录中):

import os
import sys
import django.core.handlers.wsgi

from os import path

sys.stdout = open('/dev/null', 'a+')
sys.stderr = open('/dev/null', 'a+')

sys.path.append(path.join(path.dirname(__file__), '..'))

os.environ['DJANGO_SETTINGS_MODULE'] = 'myproject.settings'
application = django.core.handlers.wsgi.WSGIHandler()

根据需要调整myproject.settings和路径。我将所有输出重定向到/ dev / null,因为默认情况下mod_wsgi阻止打印。请改用日志记录。

对于apache:

<VirtualHost *>
   ServerName myhost.com

   ErrorLog /var/log/apache2/error-myhost.log
   CustomLog /var/log/apache2/access-myhost.log common

   DocumentRoot "/var/www"

   WSGIScriptAlias / /path/to/my/wsgi.py

</VirtualHost>

希望这至少应该可以帮助您设置mod_wsgi,以便您查看它是否有所作为。

Here is the script I use for mod_wsgi (called wsgi.py, and put in the root off my django project):

import os
import sys
import django.core.handlers.wsgi

from os import path

sys.stdout = open('/dev/null', 'a+')
sys.stderr = open('/dev/null', 'a+')

sys.path.append(path.join(path.dirname(__file__), '..'))

os.environ['DJANGO_SETTINGS_MODULE'] = 'myproject.settings'
application = django.core.handlers.wsgi.WSGIHandler()

Adjust myproject.settings and the path as needed. I redirect all output to /dev/null since mod_wsgi by default prevents printing. Use logging instead.

For apache:

<VirtualHost *>
   ServerName myhost.com

   ErrorLog /var/log/apache2/error-myhost.log
   CustomLog /var/log/apache2/access-myhost.log common

   DocumentRoot "/var/www"

   WSGIScriptAlias / /path/to/my/wsgi.py

</VirtualHost>

Hopefully this should at least help you set up mod_wsgi so you can see if it makes a difference.


回答 8

缓存:确保已将其刷新。它很容易将某些东西放到缓存中,但是由于缓存引用而永远不会被GC。

Swig’d代码:确保任何内存管理都正确完成,这真的很容易在python中丢失,尤其是在第三方库中

监视:如果可以,获取有关内存使用率和命中率的数据。通常,您会看到某种类型的请求与内存使用之间的关联。

Caches: make sure they’re being flushed. Its easy for something to land in a cache, but never be GC’d because of the cache reference.

Swig’d code: Make sure any memory management is being done correctly, its really easy to miss these in python, especially with third party libraries

Monitoring: If you can, get data about memory usage and hits. Usually you’ll see a correlation between a certain type of request and memory usage.


回答 9

我们偶然发现了Django中包含大型站点地图(10000个项)的错误。似乎Django在生成站点地图时正在尝试将它们全部加载到内存中:http : //code.djangoproject.com/ticket/11572-当Google对该网站进行访问时,有效地终止了Apache进程。

We stumbled over a bug in Django with big sitemaps (10.000 items). Seems Django is trying to load them all in memory when generating the sitemap: http://code.djangoproject.com/ticket/11572 – effectively kills the apache process when Google pays a visit to the site.


创建动态选择字段

问题:创建动态选择字段

我在尝试了解如何在Django中创建动态选择字段时遇到了一些麻烦。我有一个模型设置类似:

class rider(models.Model):
     user = models.ForeignKey(User)
     waypoint = models.ManyToManyField(Waypoint)

class Waypoint(models.Model):
     lat = models.FloatField()
     lng = models.FloatField()

我想做的是创建一个选择字段whos的值是与该骑手相关联的航点(将是登录的人)。

目前,我以如下形式覆盖init:

class waypointForm(forms.Form):
     def __init__(self, *args, **kwargs):
          super(joinTripForm, self).__init__(*args, **kwargs)
          self.fields['waypoints'] = forms.ChoiceField(choices=[ (o.id, str(o)) for o in Waypoint.objects.all()])

但是所有要做的就是列出所有路标,它们与任何特定的骑手都没有关联。有任何想法吗?谢谢。

I’m having some trouble trying to understand how to create a dynamic choice field in django. I have a model set up something like:

class rider(models.Model):
     user = models.ForeignKey(User)
     waypoint = models.ManyToManyField(Waypoint)

class Waypoint(models.Model):
     lat = models.FloatField()
     lng = models.FloatField()

What I’m trying to do is create a choice Field whos values are the waypoints associated with that rider (which would be the person logged in).

Currently I’m overriding init in my forms like so:

class waypointForm(forms.Form):
     def __init__(self, *args, **kwargs):
          super(joinTripForm, self).__init__(*args, **kwargs)
          self.fields['waypoints'] = forms.ChoiceField(choices=[ (o.id, str(o)) for o in Waypoint.objects.all()])

But all that does is list all the waypoints, they’re not associated with any particular rider. Any ideas? Thanks.


回答 0

您可以通过将用户传递给表单init来过滤航点

class waypointForm(forms.Form):
    def __init__(self, user, *args, **kwargs):
        super(waypointForm, self).__init__(*args, **kwargs)
        self.fields['waypoints'] = forms.ChoiceField(
            choices=[(o.id, str(o)) for o in Waypoint.objects.filter(user=user)]
        )

您在启动表单时的视线通过了用户

form = waypointForm(user)

在模型形式的情况下

class waypointForm(forms.ModelForm):
    def __init__(self, user, *args, **kwargs):
        super(waypointForm, self).__init__(*args, **kwargs)
        self.fields['waypoints'] = forms.ModelChoiceField(
            queryset=Waypoint.objects.filter(user=user)
        )

    class Meta:
        model = Waypoint

you can filter the waypoints by passing the user to the form init

class waypointForm(forms.Form):
    def __init__(self, user, *args, **kwargs):
        super(waypointForm, self).__init__(*args, **kwargs)
        self.fields['waypoints'] = forms.ChoiceField(
            choices=[(o.id, str(o)) for o in Waypoint.objects.filter(user=user)]
        )

from your view while initiating the form pass the user

form = waypointForm(user)

in case of model form

class waypointForm(forms.ModelForm):
    def __init__(self, user, *args, **kwargs):
        super(waypointForm, self).__init__(*args, **kwargs)
        self.fields['waypoints'] = forms.ModelChoiceField(
            queryset=Waypoint.objects.filter(user=user)
        )

    class Meta:
        model = Waypoint

回答 1

有针对您的问题的内置解决方案:ModelChoiceField

通常,ModelForm当您需要创建/更改数据库对象时,总是值得尝试使用。在95%的情况下都有效,并且比创建自己的实现要干净得多。

There’s built-in solution for your problem: ModelChoiceField.

Generally, it’s always worth trying to use ModelForm when you need to create/change database objects. Works in 95% of the cases and it’s much cleaner than creating your own implementation.


回答 2

问题是你什么时候做

def __init__(self, user, *args, **kwargs):
    super(waypointForm, self).__init__(*args, **kwargs)
    self.fields['waypoints'] = forms.ChoiceField(choices=[ (o.id, str(o)) for o in Waypoint.objects.filter(user=user)])

在更新请求中,先前的值将丢失!

the problem is when you do

def __init__(self, user, *args, **kwargs):
    super(waypointForm, self).__init__(*args, **kwargs)
    self.fields['waypoints'] = forms.ChoiceField(choices=[ (o.id, str(o)) for o in Waypoint.objects.filter(user=user)])

in a update request, the previous value will lost!


回答 3

在初始化骑乘者实例时将其传递给表单怎么样?

class WaypointForm(forms.Form):
    def __init__(self, rider, *args, **kwargs):
      super(joinTripForm, self).__init__(*args, **kwargs)
      qs = rider.Waypoint_set.all()
      self.fields['waypoints'] = forms.ChoiceField(choices=[(o.id, str(o)) for o in qs])

# In view:
rider = request.user
form = WaypointForm(rider) 

How about passing the rider instance to the form while initializing it?

class WaypointForm(forms.Form):
    def __init__(self, rider, *args, **kwargs):
      super(joinTripForm, self).__init__(*args, **kwargs)
      qs = rider.Waypoint_set.all()
      self.fields['waypoints'] = forms.ChoiceField(choices=[(o.id, str(o)) for o in qs])

# In view:
rider = request.user
form = WaypointForm(rider) 

回答 4

在正常选择字段下的工作解决方案下。我的问题是,每个用户都基于很少的条件有自己的CUSTOM选择字段选项。

class SupportForm(BaseForm):

    affiliated = ChoiceField(required=False, label='Fieldname', choices=[], widget=Select(attrs={'onchange': 'sysAdminCheck();'}))

    def __init__(self, *args, **kwargs):

        self.request = kwargs.pop('request', None)
        grid_id = get_user_from_request(self.request)
        for l in get_all_choices().filter(user=user_id):
            admin = 'y' if l in self.core else 'n'
            choice = (('%s_%s' % (l.name, admin)), ('%s' % l.name))
            self.affiliated_choices.append(choice)
        super(SupportForm, self).__init__(*args, **kwargs)
        self.fields['affiliated'].choices = self.affiliated_choice

Underneath working solution with normal choice field. my problem was that each user have their own CUSTOM choicefield options based on few conditions.

class SupportForm(BaseForm):

    affiliated = ChoiceField(required=False, label='Fieldname', choices=[], widget=Select(attrs={'onchange': 'sysAdminCheck();'}))

    def __init__(self, *args, **kwargs):

        self.request = kwargs.pop('request', None)
        grid_id = get_user_from_request(self.request)
        for l in get_all_choices().filter(user=user_id):
            admin = 'y' if l in self.core else 'n'
            choice = (('%s_%s' % (l.name, admin)), ('%s' % l.name))
            self.affiliated_choices.append(choice)
        super(SupportForm, self).__init__(*args, **kwargs)
        self.fields['affiliated'].choices = self.affiliated_choice

回答 5

正如Breedly和Liang指出的那样,Ashok的解决方案将阻止您在发布表单时获取选择值。

一种稍微不同但仍然不完善的解决方法是:

class waypointForm(forms.Form):
    def __init__(self, user, *args, **kwargs):
        self.base_fields['waypoints'].choices = self._do_the_choicy_thing()
        super(waypointForm, self).__init__(*args, **kwargs)

但是,这可能会导致一些并发问题。

As pointed by Breedly and Liang, Ashok’s solution will prevent you from getting the select value when posting the form.

One slightly different, but still imperfect, way to solve that would be:

class waypointForm(forms.Form):
    def __init__(self, user, *args, **kwargs):
        self.base_fields['waypoints'].choices = self._do_the_choicy_thing()
        super(waypointForm, self).__init__(*args, **kwargs)

This could cause some concurrence problems, though.


回答 6

您可以将字段声明为表单的一流属性,并且可以动态设置选项:

class WaypointForm(forms.Form):
    waypoints = forms.ChoiceField(choices=[])

    def __init__(self, user, *args, **kwargs):
        super().__init__(*args, **kwargs)
        waypoint_choices = [(o.id, str(o)) for o in Waypoint.objects.filter(user=user)]
        self.fields['waypoints'].choices = waypoint_choices

您也可以使用ModelChoiceField并以类似方式在init上设置queryset。

You can declare the field as a first-class attribute of your form and just set choices dynamically:

class WaypointForm(forms.Form):
    waypoints = forms.ChoiceField(choices=[])

    def __init__(self, user, *args, **kwargs):
        super().__init__(*args, **kwargs)
        waypoint_choices = [(o.id, str(o)) for o in Waypoint.objects.filter(user=user)]
        self.fields['waypoints'].choices = waypoint_choices

You can also use a ModelChoiceField and set the queryset on init in a similar manner.


回答 7

如果您需要django admin中的动态选择字段;这适用于Django> = 2.1。

class CarAdminForm(forms.ModelForm):
    class Meta:
        model = Car

    def __init__(self, *args, **kwargs):
        super(CarForm, self).__init__(*args, **kwargs)

        # Now you can make it dynamic.
        choices = (
            ('audi', 'Audi'),
            ('tesla', 'Tesla')
        )

        self.fields.get('car_field').choices = choices

    car_field = forms.ChoiceField(choices=[])

@admin.register(Car)
class CarAdmin(admin.ModelAdmin):
    form = CarAdminForm

希望这可以帮助。

If you need a dynamic choice field in django admin; This works for django >=2.1.

class CarAdminForm(forms.ModelForm):
    class Meta:
        model = Car

    def __init__(self, *args, **kwargs):
        super(CarForm, self).__init__(*args, **kwargs)

        # Now you can make it dynamic.
        choices = (
            ('audi', 'Audi'),
            ('tesla', 'Tesla')
        )

        self.fields.get('car_field').choices = choices

    car_field = forms.ChoiceField(choices=[])

@admin.register(Car)
class CarAdmin(admin.ModelAdmin):
    form = CarAdminForm

Hope this helps.


除非分配输出,为什么调用Python字符串方法不做任何事情?

问题:除非分配输出,为什么调用Python字符串方法不做任何事情?

我尝试做一个简单的字符串替换,但是我不知道为什么它似乎不起作用:

X = "hello world"
X.replace("hello", "goodbye")

我想将单词更改hellogoodbye,因此应将字符串更改"hello world""goodbye world"。但是X仍然存在"hello world"。为什么我的代码不起作用?

I try to do a simple string replacement, but I don’t know why it doesn’t seem to work:

X = "hello world"
X.replace("hello", "goodbye")

I want to change the word hello to goodbye, thus it should change the string "hello world" to "goodbye world". But X just remains "hello world". Why is my code not working?


回答 0

这是因为字符串在Python中是不可变的

这意味着将X.replace("hello","goodbye")返回的副本,X其中包含已替换的副本。因此,您需要替换此行:

X.replace("hello", "goodbye")

用这一行:

X = X.replace("hello", "goodbye")

更广泛地说,这是所有Python字符串的方法是“就地”修改字符串的内容真实,例如replacestriptranslatelower/ upperjoin,…

如果要使用它而不要将其丢弃,则必须将其输出分配给某些东西。

X  = X.strip(' \t')
X2 = X.translate(...)
Y  = X.lower()
Z  = X.upper()
A  = X.join(':')
B  = X.capitalize()
C  = X.casefold()

等等。

This is because strings are immutable in Python.

Which means that X.replace("hello","goodbye") returns a copy of X with replacements made. Because of that you need replace this line:

X.replace("hello", "goodbye")

with this line:

X = X.replace("hello", "goodbye")

More broadly, this is true for all Python string methods that change a string’s content “in-place”, e.g. replace,strip,translate,lower/upper,join,…

You must assign their output to something if you want to use it and not throw it away, e.g.

X  = X.strip(' \t')
X2 = X.translate(...)
Y  = X.lower()
Z  = X.upper()
A  = X.join(':')
B  = X.capitalize()
C  = X.casefold()

and so on.


回答 1

所有的字符串功能lowerupperstrip而没有修改原始返回的字符串。如果您尝试修改一个字符串(可能会想到)well it is an iterable,它将失败。

x = 'hello'
x[0] = 'i' #'str' object does not support item assignment

关于字符串不可更改的重要性的文章很好读:为什么Python字符串不可更改?使用它们的最佳实践

All string functions as lower, upper, strip are returning a string without modifying the original. If you try to modify a string, as you might think well it is an iterable, it will fail.

x = 'hello'
x[0] = 'i' #'str' object does not support item assignment

There is a good reading about the importance of strings being immutable: Why are Python strings immutable? Best practices for using them


virtualenv –no-site-packages和pip仍在查找全局软件包吗?

问题:virtualenv –no-site-packages和pip仍在查找全局软件包吗?

我印象中virtualenv --no-site-packages会创建一个完全独立和隔离的Python环境,但事实并非如此。

例如,我在全局安装了python-django,但希望使用其他Django版本创建virtualenv。

$ virtualenv --no-site-packages foo       
New python executable in foo/bin/python
Installing setuptools............done.
$ pip -E foo install Django
Requirement already satisfied: Django in /usr/share/pyshared
Installing collected packages: Django
Successfully installed Django

据我所知,pip -E foo install以上内容应该重新安装新版本的Django。另外,如果我告诉pip冻结环境,则会得到很多软件包。我希望对于一个新鲜的环境来说--no-site-packages这是空白?

$ pip -E foo freeze
4Suite-XML==1.0.2
BeautifulSoup==3.1.0.1
Brlapi==0.5.3
BzrTools==1.17.0
Django==1.1
... and so on ...

我是否误解了--no-site-packages应该如何工作?

I was under the impression that virtualenv --no-site-packages would create a completely separate and isolated Python environment, but it doesn’t seem to.

For example, I have python-django installed globally, but wish to create a virtualenv with a different Django version.

$ virtualenv --no-site-packages foo       
New python executable in foo/bin/python
Installing setuptools............done.
$ pip -E foo install Django
Requirement already satisfied: Django in /usr/share/pyshared
Installing collected packages: Django
Successfully installed Django

From what I can tell, the pip -E foo install above is supposed to re-install a new version of Django. Also, if I tell pip to freeze the environment, I get a whole lot of packages. I would expect that for a fresh environment with --no-site-packages this would be blank?

$ pip -E foo freeze
4Suite-XML==1.0.2
BeautifulSoup==3.1.0.1
Brlapi==0.5.3
BzrTools==1.17.0
Django==1.1
... and so on ...

Am I misunderstanding how --no-site-packages is supposed to work?


回答 0

我遇到了这样的问题,直到意识到(早于发现virtualenv),我就开始在.bashrc文件中的PYTHONPATH中添加目录。由于已经过去一年多了,所以我没有马上想到。

I had a problem like this, until I realized that (long before I had discovered virtualenv), I had gone adding directories to the PYTHONPATH in my .bashrc file. As it had been over a year beforehand, I didn’t think of that straight away.


回答 1

您必须确保pip在创建的虚拟环境中而不是全局环境中运行二进制文件。

env/bin/pip freeze

查看测试:

我们使用以下--no-site-packages选项创建virtualenv :

$ virtualenv --no-site-packages -p /usr/local/bin/python mytest
Running virtualenv with interpreter /usr/local/bin/python
New python executable in mytest/bin/python
Installing setuptools, pip, wheel...done.

我们检查freeze新创建的的输出pip

$ mytest/bin/pip freeze
argparse==1.3.0
wheel==0.24.0

但是,如果我们使用global pip,那么我们将得到:

$ pip freeze
...
pyxdg==0.25
...
range==1.0.0
...
virtualenv==13.1.2

即,pip已在整个系统中安装的所有软件包。通过检查,which pip我们得到了(至少在我的情况下)类似的东西/usr/local/bin/pip,这意味着当我们这样做时pip freeze,将调用此二进制文件而不是mytest/bin/pip

You have to make sure you are running the pip binary in the virtual environment you created, not the global one.

env/bin/pip freeze

See a test:

We create the virtualenv with the --no-site-packages option:

$ virtualenv --no-site-packages -p /usr/local/bin/python mytest
Running virtualenv with interpreter /usr/local/bin/python
New python executable in mytest/bin/python
Installing setuptools, pip, wheel...done.

We check the output of freeze from the newly created pip:

$ mytest/bin/pip freeze
argparse==1.3.0
wheel==0.24.0

But if we do use the global pip, this is what we get:

$ pip freeze
...
pyxdg==0.25
...
range==1.0.0
...
virtualenv==13.1.2

That is, all the packages that pip has installed in the whole system. By checking which pip we get (at least in my case) something like /usr/local/bin/pip, meaning that when we do pip freeze it is calling this binary instead of mytest/bin/pip.


回答 2

最终,我发现无论出于什么原因,pip -E都无法正常工作。但是,如果我实际上激活了virtualenv,并使用virtualenv提供的easy_install来安装pip,然后直接从内部使用pip,那么它似乎可以正常工作,并且只显示virtualenv中的软件包。

Eventually I found that, for whatever reason, pip -E was not working. However, if I actually activate the virtualenv, and use easy_install provided by virtualenv to install pip, then use pip directly from within, it seems to work as expected and only show the packages in the virtualenv


回答 3

我知道这是一个非常老的问题,但是对于那些来到这里寻求解决方案的人来说:

运行之前不要忘记激活virtualenvsource bin/activatepip freeze。否则,您将获得所有全局软件包的列表。

I know this is a very old question but for those arriving here looking for a solution:

Don’t forget to activate the virtualenv (source bin/activate) before running pip freeze. Otherwise you’ll get a list of all global packages.


回答 4

暂时清除PYTHONPATH带有:

export PYTHONPATH=

然后创建并激活虚拟环境:

virtualenv foo
. foo/bin/activate

只有这样:

pip freeze

Temporarily clear the PYTHONPATH with:

export PYTHONPATH=

Then create and activate the virtual environment:

virtualenv foo
. foo/bin/activate

Only then:

pip freeze

回答 5

--no-site-packages顾名思义,应该从中删除标准site-packages目录sys.path。保留在标准Python路径中的所有其他内容都将保留在那里。

--no-site-packages should, as the name suggests, remove the standard site-packages directory from sys.path. Anything else that lives in the standard Python path will remain there.


回答 6

如果直接调用脚本script.py,则在Windows 上可能会发生类似的问题,因为脚本随后使用Windows默认打开器并在虚拟环境之外打开Python。调用它将python script.py在虚拟环境中使用Python。

A similar problem can occur on Windows if you call scripts directly as script.py which then uses the Windows default opener and opens Python outside the virtual environment. Calling it with python script.py will use Python with the virtual environment.


回答 7

当您将virtualenv目录移动到另一个目录(在linux上)或重命名父目录时,似乎也会发生这种情况。

This also seems to happen when you move the virtualenv directory to another directory (on linux), or rename a parent directory.


回答 8

我遇到了同样的问题。对我来说(在Ubuntu上)的问题是我的路径名包含$。当我在$ dir之外创建一个virtualenv时,它工作正常。

奇怪的。

I was having this same problem. The issue for me (on Ubuntu) was that my path name contained $. When I created a virtualenv outside of the $ dir, it worked fine.

Weird.


回答 9

virtualenv pip无法正常工作的可能原因之一是,如果任何父文件夹的名称中都有空格/Documents/project name/app 重命名以/Documents/projectName/app解决该问题。

One of the possible reasons why virtualenv pip won’t work is if any of the parent folders had space in its name /Documents/project name/app renaming it to /Documents/projectName/app solves the problem.


回答 10

我遇到了同样的问题,即venv中的点仍然可以用作全局点。
搜索很多页面后,我以这种方式解决了。
1.通过virtualenv使用选项“ –no-site-packages”创建一个新的venv

virtualenv --no-site-packages --python=/xx/xx/bin/python my_env_nmae

请注意,尽管从Virtualenv的doc文件中的1.7.0版本开始,“-no-site-packages”选项默认为true,但是我发现它除非您手动将其设置为不起作用。为了获得纯净的venv,我强烈建议打开2。激活您创建的新env

source ./my_env_name/bin/activate
  1. 检查您的pip位置和python位置,并确保这两个命令在虚拟环境下
pip --version
which python
  1. 在虚拟环境下使用pip安装不受全局软件包中断影响的软件包
pip install package_name

希望这个答案对您有帮助!

I came accross the same problem where pip in venv still works as global pip.
After searching many pages, i figure it out this way.
1. Create a new venv by virtualenv with option “–no-site-packages”

virtualenv --no-site-packages --python=/xx/xx/bin/python my_env_nmae

please note that although the “–no-site-packages” option was default true since 1.7.0 in the doc file of virtualenv, but i found it not working unless you set it on manually. In order to get a pure venv, i strongly suggest turning this option on 2. Activate the new env you created

source ./my_env_name/bin/activate
  1. Check your pip location and python location and make sure these two commands are under virtual envirement
pip --version
which python
  1. Use pip under virtual env to install packages free from the global packages interuption
pip install package_name

Wish this answer helps you!


回答 11

这是所有pip安装选项的列表-我没有找到任何’ -E‘选项,可能是较旧的版本。下面,我virtualenv将为即将到来的SO用户提供简单的英语用法和使用方法。


一切似乎都不错,请接受激活virtualenvfoo)。它所要做的就是允许我们拥有多个(和不同的)Python环境,即各种Python版本,各种Django版本或任何其他Python包-如果我们有生产中的先前版本,并且想用我们的测试最新的Django版本应用。

简而言之,创建和使用(激活)虚拟环境(virtualenv)使得可以使用不同的Python解释器(即python 2.7和3.3)运行或测试我们的应用程序或简单的python脚本-可以全新安装(使用--no-site-packages选项),也可以使用现有的所有软件包/ last设置(使用--system-site-packages选项)。要使用它,我们必须激活它:

$ pip install django 将其安装到全局站点程序包中,并类似地获取 pip freeze will会给出全局站点程序包的名称。

而在venv dir(foo)内部执行$ source /bin/activate将激活venv,即,现在使用pip安装的所有内容都只会安装在虚拟env中,并且只有现在pip冻结将不会提供全局站点软件包python软件包的列表。一旦激活:

$ virtualenv --no-site-packages foo       
New python executable in foo/bin/python
Installing setuptools............done.
$ cd foo
$ source bin/activate 
(foo)$ pip install django

(foo)$符号表明我们正在使用虚拟python环境之前,即任何带有pip的东西-安装,冻结,卸载将仅限于该venv,并且对全局/默认Python安装/软件包没有影响。

Here’s the list of all the pip install options – I didn’t find any ‘-E‘ option, may be older version had it. Below I am sharing a plain english usage and working of virtualenv for the upcoming SO users.


Every thing seems fine, accept activating the virtualenv (foo). All it does is allow us to have multiple (and varying) python environment i.e. various Python versions, or various Django versions, or any other Python package – in case we have a previous version in production and want to test the latest Django release with our application.

In short creating and using (activating) virtual environment (virtualenv) makes it possible to run or test our application or simple python scripts with different Python interpreter i.e. Python 2.7 and 3.3 – can be a fresh installation (using --no-site-packages option) or all the packages from existing/last setup (using --system-site-packages option). To use it we have to activate it:

$ pip install django will install it into the global site-packages, and similarly getting the pip freeze will give names of the global site-packages.

while inside the venv dir (foo) executing $ source /bin/activate will activate venv i.e. now anything installed with pip will only be installed in the virtual env, and only now the pip freeze will not give the list of global site-packages python packages. Once activated:

$ virtualenv --no-site-packages foo       
New python executable in foo/bin/python
Installing setuptools............done.
$ cd foo
$ source bin/activate 
(foo)$ pip install django

(foo) before the $ sign indicates we are using a virtual python environment i.e. any thing with pip – install, freeze, uninstall will be limited to this venv, and no effect on global/default Python installation/packages.


为什么表达式0 <0 == 0在Python中返回False?

问题:为什么表达式0 <0 == 0在Python中返回False?

在Python 2.6中查看Queue.py时,我发现这个构造有点奇怪:

def full(self):
    """Return True if the queue is full, False otherwise
    (not reliable!)."""
    self.mutex.acquire()
    n = 0 < self.maxsize == self._qsize()
    self.mutex.release()
    return n

如果maxsize为0,则队列永远不会满。

我的问题是在这种情况下如何运作?如何0 < 0 == 0被认为是错误的?

>>> 0 < 0 == 0
False
>>> (0) < (0 == 0)
True
>>> (0 < 0) == 0
True
>>> 0 < (0 == 0)
True

Looking into Queue.py in Python 2.6, I found this construct that I found a bit strange:

def full(self):
    """Return True if the queue is full, False otherwise
    (not reliable!)."""
    self.mutex.acquire()
    n = 0 < self.maxsize == self._qsize()
    self.mutex.release()
    return n

If maxsize is 0 the queue is never full.

My question is how does it work for this case? How 0 < 0 == 0 is considered False?

>>> 0 < 0 == 0
False
>>> (0) < (0 == 0)
True
>>> (0 < 0) == 0
True
>>> 0 < (0 == 0)
True

回答 0

我相信Python对关系运算符的序列有特殊的处理方式,以使范围比较易于表达。可以说更好0 < x <= 5比好得多(0 < x) and (x <= 5)

这些称为链接比较。这是他们文档的链接。

在您谈论的其他情况下,括号会强制在一个关系运算符之前应用一个关系运算符,因此它们不再是链式比较。并且由于True和的False值都是整数,因此您可以从括号中得到答案。

I believe Python has special case handling for sequences of relational operators to make range comparisons easy to express. It’s much nicer to be able to say 0 < x <= 5 than to say (0 < x) and (x <= 5).

These are called chained comparisons. And that’s a link to the documentation for them.

With the other cases you talk about, the parenthesis force one relational operator to be applied before the other, and so they are no longer chained comparisons. And since True and False have values as integers you get the answers you do out of the parenthesized versions.


回答 1

因为

(0 < 0) and (0 == 0)

False。您可以将比较运算符链接在一起,它们会自动扩展为成对比较。


编辑-关于Python中正确与错误的说明

在Python中TrueFalse仅仅是的实例bool,它是的子类int。换句话说,True确实只有1。

这样做的目的是,您可以像完全使用整数一样使用布尔比较的结果。这导致诸如

>>> (1==1)+(1==1)
2
>>> (2<1)<1
True

但是,只有在您对比较加括号以使它们首先被评估时,这些情况才会发生。否则,Python将扩展比较运算符。

Because

(0 < 0) and (0 == 0)

is False. You can chain together comparison operators and they are automatically expanded out into the pairwise comparisons.


EDIT — clarification about True and False in Python

In Python True and False are just instances of bool, which is a subclass of int. In other words, True really is just 1.

The point of this is that you can use the result of a boolean comparison exactly like an integer. This leads to confusing things like

>>> (1==1)+(1==1)
2
>>> (2<1)<1
True

But these will only happen if you parenthesise the comparisons so that they are evaluated first. Otherwise Python will expand out the comparison operators.


回答 2

您遇到的奇怪行为来自python链接条件的能力。由于发现0不小于0,因此它决定整个表达式的计算结果为false。一旦将其分解为单独的条件,就在更改功能。最初,它实际上是在测试a < b && b == c您对的原始声明a < b == c

另一个例子:

>>> 1 < 5 < 3
False

>>> (1 < 5) < 3
True

The strange behavior your experiencing comes from pythons ability to chain conditions. Since it finds 0 is not less than 0, it decides the entire expression evaluates to false. As soon as you break this apart into seperate conditions, you’re changing the functionality. It initially is essentially testing that a < b && b == c for your original statement of a < b == c.

Another example:

>>> 1 < 5 < 3
False

>>> (1 < 5) < 3
True

回答 3

>>> 0 < 0 == 0
False

这是一个链式比较。如果每个成对比较依次为true,则返回true。相当于(0 < 0) and (0 == 0)

>>> (0) < (0 == 0)
True

这等效0 < True于其值为True。

>>> (0 < 0) == 0
True

这等效False == 0于其值为True。

>>> 0 < (0 == 0)
True

0 < True如上所述,与之等效的结果为True。

>>> 0 < 0 == 0
False

This is a chained comparison. It returns true if each pairwise comparison in turn is true. It is the equivalent to (0 < 0) and (0 == 0)

>>> (0) < (0 == 0)
True

This is equivalent to 0 < True which evaluates to True.

>>> (0 < 0) == 0
True

This is equivalent to False == 0 which evaluates to True.

>>> 0 < (0 == 0)
True

Equivalent to 0 < True which, as above, evaluates to True.


回答 4

查看反汇编(字节码),很明显为什么0 < 0 == 0False

这是对此表达式的分析:

>>>import dis

>>>def f():
...    0 < 0 == 0

>>>dis.dis(f)
  2      0 LOAD_CONST               1 (0)
         3 LOAD_CONST               1 (0)
         6 DUP_TOP
         7 ROT_THREE
         8 COMPARE_OP               0 (<)
        11 JUMP_IF_FALSE_OR_POP    23
        14 LOAD_CONST               1 (0)
        17 COMPARE_OP               2 (==)
        20 JUMP_FORWARD             2 (to 25)
   >>   23 ROT_TWO
        24 POP_TOP
   >>   25 POP_TOP
        26 LOAD_CONST               0 (None)
        29 RETURN_VALUE

注意第0-8行:这些行检查是否0 < 0明显返回False了python堆栈。

现在注意第11行:JUMP_IF_FALSE_OR_POP 23 这意味着如果0 < 0返回,则False跳到第23行。

现在,0 < 0False,因此进行了跳转,即使堆栈中的部分甚至没有被检查,它也会False为堆栈留下a ,这是整个表达式的返回值。0 < 0 == 0== 0

因此,总而言之,答案就像对该问题的其他答案中所说的那样。 0 < 0 == 0有特殊的意义。编译器将其评估为两个术语:0 < 00 == 0。与任何复杂的布尔表达式一样and它们之间的一样,如果第一个失败,则甚至不会检查第二个。

希望这对我们有所启发,并且我真的希望我用来分析这种意外行为的方法会鼓励其他人将来尝试相同的方法。

Looking at the disassembly (the bytes codes) it is obvious why 0 < 0 == 0 is False.

Here is an analysis of this expression:

>>>import dis

>>>def f():
...    0 < 0 == 0

>>>dis.dis(f)
  2      0 LOAD_CONST               1 (0)
         3 LOAD_CONST               1 (0)
         6 DUP_TOP
         7 ROT_THREE
         8 COMPARE_OP               0 (<)
        11 JUMP_IF_FALSE_OR_POP    23
        14 LOAD_CONST               1 (0)
        17 COMPARE_OP               2 (==)
        20 JUMP_FORWARD             2 (to 25)
   >>   23 ROT_TWO
        24 POP_TOP
   >>   25 POP_TOP
        26 LOAD_CONST               0 (None)
        29 RETURN_VALUE

Notice lines 0-8: These lines check if 0 < 0 which obviously returns False onto the python stack.

Now notice line 11: JUMP_IF_FALSE_OR_POP 23 This means that if 0 < 0 returns False perform a jump to line 23.

Now, 0 < 0 is False, so the jump is taken, which leaves the stack with a False which is the return value for the whole expression 0 < 0 == 0, even though the == 0 part isn’t even checked.

So, to conclude, the answer is like said in other answers to this question. 0 < 0 == 0 has a special meaning. The compiler evaluates this to two terms: 0 < 0 and 0 == 0. As with any complex boolean expressions with and between them, if the first fails then the second one isn’t even checked.

Hopes this enlightens things up a bit, and I really hope that the method I used to analyse this unexpected behavior will encourage others to try the same in the future.


回答 5

正如其他人提到的那样x comparison_operator y comparison_operator z,语法糖(x comparison_operator y) and (y comparison_operator z)的优势在于y仅被评估一次。

因此,您的表情0 < 0 == 0是真的(0 < 0) and (0 == 0),它的评估False and True结果是公正的False

As other’s mentioned x comparison_operator y comparison_operator z is syntactical sugar for (x comparison_operator y) and (y comparison_operator z) with the bonus that y is only evaluated once.

So your expression 0 < 0 == 0 is really (0 < 0) and (0 == 0), which evaluates to False and True which is just False.


回答 6

也许从这个摘录文档可以帮助:

这些是所谓的“丰富比较”方法,在__cmp__()下面优先于比较运算符。运算符符号和方法名之间的对应关系如下:x<y呼叫 x.__lt__(y)x<=y呼叫x.__le__(y)x==y呼叫x.__eq__(y)x!=yx<>y 呼叫x.__ne__(y)x>y呼叫 x.__gt__(y),和x>=y呼叫 x.__ge__(y)

如果富比较方法NotImplemented未实现给定参数对的操作,则可能返回单例。按照惯例,False并将True其返回以进行成功比较。但是,这些方法可以返回任何值,因此,如果在布尔上下文中使用比较运算符(例如,在if语句的条件下),Python将调用bool()该值以确定结果是true还是false。

比较运算符之间没有隐含的关系。的真相x==y并不意味着那x!=y 是错误的。因此,在定义时 __eq__(),还应该定义一个,__ne__()以便操作符能够按预期运行。有关__hash__()创建可哈希对象(支持自定义比较操作并可用作字典键)的一些重要说明,请参见上的段落。

这些方法没有交换参数版本(当left参数不支持该操作但right参数支持该操作时使用);相反,__lt__()and __gt__() 是彼此的反射,__le__() and __ge__()是彼此的反射,and __eq__()and __ne__() 是自己的反射。

丰富比较方法的论点永远不会被强迫。

这些是比较,但是由于要链接比较,因此您应该知道:

可以任意链接比较,例如x < y <= z与等效x < y and y <= z,除了y仅被评估一次(但是在两种情况下,当x <y为假时,z都不被评估)。

形式上,如果a,b,c,…,y,z是表达式,而op1,op2,…,opN是比较运算符,则op1 b op2 c … y opN z等效于op1 b和b op2 c和… y opN z,除了每个表达式最多计算一次。

maybe this excerpt from the docs can help:

These are the so-called “rich comparison” methods, and are called for comparison operators in preference to __cmp__() below. The correspondence between operator symbols and method names is as follows: x<y calls x.__lt__(y), x<=y calls x.__le__(y), x==y calls x.__eq__(y), x!=y and x<>y call x.__ne__(y), x>y calls x.__gt__(y), and x>=y calls x.__ge__(y).

A rich comparison method may return the singleton NotImplemented if it does not implement the operation for a given pair of arguments. By convention, False and True are returned for a successful comparison. However, these methods can return any value, so if the comparison operator is used in a Boolean context (e.g., in the condition of an if statement), Python will call bool() on the value to determine if the result is true or false.

There are no implied relationships among the comparison operators. The truth of x==y does not imply that x!=y is false. Accordingly, when defining __eq__(), one should also define __ne__() so that the operators will behave as expected. See the paragraph on __hash__() for some important notes on creating hashable objects which support custom comparison operations and are usable as dictionary keys.

There are no swapped-argument versions of these methods (to be used when the left argument does not support the operation but the right argument does); rather, __lt__() and __gt__() are each other’s reflection, __le__() and __ge__() are each other’s reflection, and __eq__() and __ne__() are their own reflection.

Arguments to rich comparison methods are never coerced.

These were comparisons but since you are chaining comparisons you should know that:

Comparisons can be chained arbitrarily, e.g., x < y <= z is equivalent to x < y and y <= z, except that y is evaluated only once (but in both cases z is not evaluated at all when x < y is found to be false).

Formally, if a, b, c, …, y, z are expressions and op1, op2, …, opN are comparison operators, then a op1 b op2 c … y opN z is equivalent to a op1 b and b op2 c and … y opN z, except that each expression is evaluated at most once.


回答 7

这就是它的全部荣耀。

>>> class showme(object):
...   def __init__(self, name, value):
...     self.name, self.value = name, value
...   def __repr__(self):
...     return "<showme %s:%s>" % (self.name, self.value)
...   def __cmp__(self, other):
...     print "cmp(%r, %r)" % (self, other)
...     if type(other) == showme:
...       return cmp(self.value, other.value)
...     else:
...       return cmp(self.value, other)
... 
>>> showme(1,0) < showme(2,0) == showme(3,0)
cmp(<showme 1:0>, <showme 2:0>)
False
>>> (showme(1,0) < showme(2,0)) == showme(3,0)
cmp(<showme 1:0>, <showme 2:0>)
cmp(<showme 3:0>, False)
True
>>> showme(1,0) < (showme(2,0) == showme(3,0))
cmp(<showme 2:0>, <showme 3:0>)
cmp(<showme 1:0>, True)
True
>>> 

Here it is, in all its glory.

>>> class showme(object):
...   def __init__(self, name, value):
...     self.name, self.value = name, value
...   def __repr__(self):
...     return "<showme %s:%s>" % (self.name, self.value)
...   def __cmp__(self, other):
...     print "cmp(%r, %r)" % (self, other)
...     if type(other) == showme:
...       return cmp(self.value, other.value)
...     else:
...       return cmp(self.value, other)
... 
>>> showme(1,0) < showme(2,0) == showme(3,0)
cmp(<showme 1:0>, <showme 2:0>)
False
>>> (showme(1,0) < showme(2,0)) == showme(3,0)
cmp(<showme 1:0>, <showme 2:0>)
cmp(<showme 3:0>, False)
True
>>> showme(1,0) < (showme(2,0) == showme(3,0))
cmp(<showme 2:0>, <showme 3:0>)
cmp(<showme 1:0>, True)
True
>>> 

回答 8

我在想Python在魔术之间做得很奇怪。与1 < 2 < 3均值2 相同,介于1和3之间。

在这种情况下,我认为它正在执行[中间0]大于[左0]并等于[右0]。中间0不大于左边0,因此它的值为false。

I’m thinking Python is doing it’s weird between magic. Same as 1 < 2 < 3 means 2 is between 1 and 3.

In this case, I think it’s doing [middle 0] is greater than [left 0] and equal to [right 0]. Middle 0 is not greater than left 0, so it evaluates to false.


Python中的EAFP原理是什么?

问题:Python中的EAFP原理是什么?

Python中的“使用EAFP原理”是什么意思?你能提供一些例子吗?

What is meant by “using the EAFP principle” in Python? Could you provide any examples?


回答 0

词汇表中

寻求宽恕比允许容易。这种通用的Python编码风格假设存在有效的键或属性,并且在假设被证明为假的情况下捕获异常。这种干净快捷的样式的特点是存在许多tryexcept声明。该技术与C等其他许多语言通用的LBYL风格形成对比。

一个示例是尝试访问字典键。

EAFP:

try:
    x = my_dict["key"]
except KeyError:
    # handle missing key

LBYL:

if "key" in my_dict:
    x = my_dict["key"]
else:
    # handle missing key

LBYL版本必须在字典中搜索关键字两次,并且可能还被认为可读性较差。

From the glossary:

Easier to ask for forgiveness than permission. This common Python coding style assumes the existence of valid keys or attributes and catches exceptions if the assumption proves false. This clean and fast style is characterized by the presence of many try and except statements. The technique contrasts with the LBYL style common to many other languages such as C.

An example would be an attempt to access a dictionary key.

EAFP:

try:
    x = my_dict["key"]
except KeyError:
    # handle missing key

LBYL:

if "key" in my_dict:
    x = my_dict["key"]
else:
    # handle missing key

The LBYL version has to search the key inside the dictionary twice, and might also be considered slightly less readable.


回答 1

我将尝试通过另一个示例对其进行解释。

在这里,我们尝试访问文件并在控制台中打印内容。

LBYL-飞跃前先看看:

我们可能要检查是否可以访问该文件,如果可以,我们将其打开并打印内容。如果我们无法访问该文件,我们将发挥else作用。之所以成为竞争条件,是因为我们首先进行访问检查。到我们到达的时候with open(my_file) as f:,由于某些权限问题(例如,另一个进程获得了独占文件锁定),我们可能无法再访问它。该代码可能会引发错误,并且由于我们认为可以访问该文件,因此无法捕获该错误。

import os

my_file = "/path/to/my/file.txt"

# Race condition
if os.access(my_file, os.R_OK):
    with open(my_file) as f:
        print(f.read())
else:
    print("File can't be accessed")

EAFP-较宽容更容易寻求宽恕:

在此示例中,我们只是尝试打开文件,如果无法打开文件,则会抛出IOError。如果可以,我们将打开文件并打印内容。因此,我们不是在什么,而是在尝试做。如果有效,那就太好了!如果不是,我们将捕获错误并进行处理。

# # No race condition
try:
    f = open(my_file)
except IOError as e:
    print("File can't be accessed")
else:
    with f:
        print(f.read())

I’ll try to explain it with another example.

Here we’re trying to access the file and print the contents in console.

LBYL – Look Before You Leap :

We might want to check if we can access the file and if we can, we’ll open it and print the contents. If we can’t access the file we’ll hit the else part. The reason that this is a race condition is because we first make an access-check. By the time we reach with open(my_file) as f: maybe we can’t access it anymore due to some permission issues (for example another process gains an exclusive file lock). This code will likely throw an error and we won’t be able to catch that error because we thought that we could access the file.

import os

my_file = "/path/to/my/file.txt"

# Race condition
if os.access(my_file, os.R_OK):
    with open(my_file) as f:
        print(f.read())
else:
    print("File can't be accessed")

EAFP – Easier to Ask for Forgiveness than Permission :

In this example, we’re just trying to open the file and if we can’t open it, it’ll throw an IOError. If we can, we’ll open the file and print the contents. So instead of asking something we’re trying to do it. If it works, great! If it doesn’t we catch the error and handle it.

# # No race condition
try:
    f = open(my_file)
except IOError as e:
    print("File can't be accessed")
else:
    with f:
        print(f.read())

回答 2

我称之为“乐观编程”。这个想法是,大多数时候人们会做正确的事,错误应该很少。因此,首先编写代码以使“正确的事情”发生,然后,如果没有,则捕获错误。

我的感觉是,如果用户要犯错误,那么他们应该是遭受时间后果的人。正确使用该工具的人会被激怒。

I call it “optimistic programming”. The idea is that most times people will do the right thing, and errors should be few. So code first for the “right thing” to happen, and then catch the errors if they don’t.

My feeling is that if a user is going to be making mistakes, they should be the one to suffer the time consequences. People who use the tool the right way are sped through.


我如何知道是否可以禁用SQLALCHEMY_TRACK_MODIFICATIONS?

问题:我如何知道是否可以禁用SQLALCHEMY_TRACK_MODIFICATIONS?

每次我运行使用Flask-SQLAlchemy的应用程序时,都会收到以下警告,提示该SQLALCHEMY_TRACK_MODIFICATIONS选项将被禁用。

/home/david/.virtualenvs/flask-sqlalchemy/lib/python3.5/site-packages/flask_sqlalchemy/__init__.py:800: UserWarning: SQLALCHEMY_TRACK_MODIFICATIONS adds significant overhead and will be disabled by default in the future.  Set it to True to suppress this warning.
  warnings.warn('SQLALCHEMY_TRACK_MODIFICATIONS adds significant overhead and will be disabled by default in the future.  Set it to True to suppress this warning.')

我试图找出此选项的作用,但是Flask-SQLAlchemy文档尚不清楚该跟踪的用途。

SQLALCHEMY_TRACK_MODIFICATIONS

如果设置为True(默认值),Flask-SQLAlchemy将跟踪对象的修改并发出信号。这需要额外的内存,如果不需要,可以将其禁用。

如何确定我的项目是否需要,SQLALCHEMY_TRACK_MODIFICATIONS = True或者是否可以安全地禁用此功能并在服务器上节省内存?

Every time I run my app that uses Flask-SQLAlchemy I get the following warning that the SQLALCHEMY_TRACK_MODIFICATIONS option will be disabled.

/home/david/.virtualenvs/flask-sqlalchemy/lib/python3.5/site-packages/flask_sqlalchemy/__init__.py:800: UserWarning: SQLALCHEMY_TRACK_MODIFICATIONS adds significant overhead and will be disabled by default in the future.  Set it to True to suppress this warning.
  warnings.warn('SQLALCHEMY_TRACK_MODIFICATIONS adds significant overhead and will be disabled by default in the future.  Set it to True to suppress this warning.')

I tried to find out what this option does, but the Flask-SQLAlchemy documentation isn’t clear about what uses this tracking.

SQLALCHEMY_TRACK_MODIFICATIONS

If set to True (the default) Flask-SQLAlchemy will track modifications of objects and emit signals. This requires extra memory and can be disabled if not needed.

How do I find out if my project requires SQLALCHEMY_TRACK_MODIFICATIONS = True or if I can safely disable this feature and save memory on my server?


回答 0

您的应用程序很可能没有使用Flask-SQLAlchemy事件系统,因此可以安全地关闭它。您需要审核代码以进行验证-您正在寻找与models_committedbefore_models_committed挂钩的任何内容。如果确实发现您正在使用Flask-SQLAlchemy事件系统,则可能应该更新代码以使用SQLAlchemy的内置事件系统。

要关闭Flask-SQLAlchemy事件系统(并禁用警告),只需添加:

SQLALCHEMY_TRACK_MODIFICATIONS = False

更改为您的应用程序配置,直到更改默认设置为止(很有可能在Flask-SQLAlchemy v3中)。


背景-警告告诉您的是以下内容:

Flask-SQLAlchemy有自己的事件通知系统,该系统在SQLAlchemy之上分层。为此,它跟踪对SQLAlchemy会话的修改。这需要额外的资源,因此该选项SQLALCHEMY_TRACK_MODIFICATIONS允许您禁用修改跟踪系统。当前,该选项默认为True,但将来该默认值将更改为False,从而禁用事件系统。

据我了解,更改的理由有三点:

  1. 使用Flask-SQLAlchemy的事件系统的人并不多,但是大多数人没有意识到他们可以通过禁用它来节省系统资源。因此,更明智的默认设置是禁用它,想要它的人可以打开它。

  2. Flask-SQLAlchemy中的事件系统存在相当多的错误(请参阅下面提到的请求请求中与之相关的问题),需要为很少有人使用的功能进行额外的维护。

  3. 在v0.7中,SQLAlchemy本身添加了一个强大的事件系统,其中包括创建自定义事件的功能。理想情况下,Flask-SQLAlchemy事件系统除了创建一些自定义的SQLAlchemy事件挂钩和侦听器外,无所不用其事,然后让SQLAlchemy自己管理事件触发器。

您可以在有关拉动请求的讨论中看到更多信息,该请求开始触发此警告

Most likely your application doesn’t use the Flask-SQLAlchemy event system, so you’re probably safe to turn off. You’ll need to audit the code to verify–you’re looking for anything that hooks into models_committed or before_models_committed. If you do find that you’re using the Flask-SQLAlchemy event system, you probably should update the code to use SQLAlchemy’s built-in event system instead.

To turn off the Flask-SQLAlchemy event system (and disable the warning), just add:

SQLALCHEMY_TRACK_MODIFICATIONS = False

to your app config until the default is changed (most likely in Flask-SQLAlchemy v3).


Background–here’s what the warning is telling you:

Flask-SQLAlchemy has its own event notification system that gets layered on top of SQLAlchemy. To do this, it tracks modifications to the SQLAlchemy session. This takes extra resources, so the option SQLALCHEMY_TRACK_MODIFICATIONS allows you to disable the modification tracking system. Currently the option defaults to True, but in the future, that default will change to False, thereby disabling the event system.

As far as I understand, the rationale for the change is three-fold:

  1. Not many people use Flask-SQLAlchemy’s event system, but most people don’t realize they can save system resources by disabling it. So a saner default is to disable it and those who want it can turn it on.

  2. The event system in Flask-SQLAlchemy has been rather buggy (see issues linked to in the pull request mentioned below), requiring additional maintenance for a feature that few people use.

  3. In v0.7, SQLAlchemy itself added a powerful event system including the ability to create custom events. Ideally, the Flask-SQLAlchemy event system should do nothing more than create a few custom SQLAlchemy event hooks and listeners, and then let SQLAlchemy itself manage the event trigger.

You can see more in the discussion around the pull request that started triggering this warning.


回答 1

Jeff Widman的详细解释非常完美。

由于在完成此操作之前我曾进行过一些“复制粘贴”的操作,因此我想使下一个穿鞋的操作变得更容易。

在您的代码中,紧接在

app = Flask(__name__)

如果要启用轨道修改,只需添加:

app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = True

否则,如果您使用此功能,则可能需要将值更改为False,以免浪费系统资源。由于您仍在显式设置配置,因此这仍然会使警告保持沉默。

这是具有False值的相同代码段:

app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False

感谢Jeff Widman提出的建议和详细信息。

Jeff Widman’s detailed explanation is simply perfect.

Since I had some copy’n’paste fights before getting this right I’d like to make it easier for the next one that will be in my shoes.

In your code, immediately after:

app = Flask(__name__)

If you want to enable track modifications simply add:

app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = True

Otherwise, if you are not using this feature, you may want to change the value to False in order not to waste system resources. This will still silence the warning since you’re anyway explicitly setting the config.

Here’s the same snippet with False value:

app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False

Thanks to Jeff Widman for this added suggestion and details.


回答 2

上面的答案看起来不错。但是,我想在Flask-SQLAlchemy文档中指出这一行,因为SQLALCHEMY_TRACK_MODIFICATIONS = False在我的应用程序配置中设置后,我仍然收到这些警告。

在此页面上:http : //flask-sqlalchemy.pocoo.org/2.3/config/

Flask-SQLAlchemy存在以下配置值。Flask-SQLAlchemy从您的主要Flask配置中加载这些值,可以通过多种方式填充。请注意,其中一些不能在创建引擎后进行修改,因此请确保尽早进行配置,并且不要在运行时进行修改。

换句话说,app.config 创建Flask-SQLAlchemy数据库之前,请确保设置您的数据库。

例如,如果您将应用程序配置为set SQLALCHEMY_TRACK_MODIFICATIONS = False

from flask import Flask
app = Flask(__name__)
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False

db = SQLAlchemy(app)

The above answers look good. However, I wanted to point out this line in the Flask-SQLAlchemy documentation because I was still getting these warnings after setting SQLALCHEMY_TRACK_MODIFICATIONS = False in my application config.

On this page: http://flask-sqlalchemy.pocoo.org/2.3/config/

The following configuration values exist for Flask-SQLAlchemy. Flask-SQLAlchemy loads these values from your main Flask config which can be populated in various ways. Note that some of those cannot be modified after the engine was created so make sure to configure as early as possible and to not modify them at runtime.

In other words, make sure to set up your app.config before creating your Flask-SQLAlchemy database.

For example, if you are configuring your application to set SQLALCHEMY_TRACK_MODIFICATIONS = False:

from flask import Flask
app = Flask(__name__)
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False

db = SQLAlchemy(app)

有趣好用的Python教程

退出移动版
微信支付
请使用 微信 扫码支付