标签归档:python-3.5

Python中的’@ =’符号是什么?

问题:Python中的’@ =’符号是什么?

我知道@是给装饰器用的,但是@=Python有什么用呢?只是保留一些未来的想法吗?

这只是我阅读时遇到的许多问题之一tokenizer.py

I know @ is for decorators, but what is @= for in Python? Is it just reservation for some future idea?

This is just one of my many questions while reading tokenizer.py.


回答 0

文档

@(在)操作者意图被用于矩阵乘法。没有内置的Python类型实现此运算符。

@运算符是在Python 3.5中引入的。@=正如您所期望的那样,是矩阵乘法,后跟赋值。它们映射到__matmul____rmatmul____imatmul__类似于如何++=映射__add____radd____iadd__

PEP 465中详细讨论了操作员及其背后的原理。

From the documentation:

The @ (at) operator is intended to be used for matrix multiplication. No builtin Python types implement this operator.

The @ operator was introduced in Python 3.5. @= is matrix multiplication followed by assignment, as you would expect. They map to __matmul__, __rmatmul__ or __imatmul__ similar to how + and += map to __add__, __radd__ or __iadd__.

The operator and the rationale behind it are discussed in detail in PEP 465.


回答 1

@=@是Python 3.5中引入的用于执行矩阵乘法的新运算符。它们的目的是澄清迄今为止​​与运算符之间存在的混淆,该运算符*根据该特定库/代码中采用的约定用于元素方式乘法或矩阵乘法。结果,将来,运营商*只能用于按元素乘法。

PEP0465中所述,引入了两个运算符:

  • 一个新的二进制运算符A @ B,与A * B
  • 就地版本A @= B,与A *= B

矩阵乘法与按元素乘法

为了快速突出区别,对于两个矩阵:

A = [[1, 2],    B = [[11, 12],
     [3, 4]]         [13, 14]]
  • 逐元素乘法将生成:

    A * B = [[1 * 11,   2 * 12], 
             [3 * 13,   4 * 14]]
  • 矩阵乘法将生成:

    A @ B  =  [[1 * 11 + 2 * 13,   1 * 12 + 2 * 14],
               [3 * 11 + 4 * 13,   3 * 12 + 4 * 14]]

在Numpy中使用

到目前为止,Numpy使用以下约定:

@运算符的引入使涉及矩阵乘法的代码更易于阅读。PEP0465举了一个例子:

# Current implementation of matrix multiplications using dot function
S = np.dot((np.dot(H, beta) - r).T,
            np.dot(inv(np.dot(np.dot(H, V), H.T)), np.dot(H, beta) - r))

# Current implementation of matrix multiplications using dot method
S = (H.dot(beta) - r).T.dot(inv(H.dot(V).dot(H.T))).dot(H.dot(beta) - r)

# Using the @ operator instead
S = (H @ beta - r).T @ inv(H @ V @ H.T) @ (H @ beta - r)

显然,最后一种实现更易于阅读和解释为等式。

@= and @ are new operators introduced in Python 3.5 performing matrix multiplication. They are meant to clarify the confusion which existed so far with the operator * which was used either for element-wise multiplication or matrix multiplication depending on the convention employed in that particular library/code. As a result, in the future, the operator * is meant to be used for element-wise multiplication only.

As explained in PEP0465, two operators were introduced:

  • A new binary operator A @ B, used similarly as A * B
  • An in-place version A @= B, used similarly as A *= B

Matrix Multiplication vs Element-wise Multiplication

To quickly highlight the difference, for two matrices:

A = [[1, 2],    B = [[11, 12],
     [3, 4]]         [13, 14]]
  • Element-wise multiplication will yield:

    A * B = [[1 * 11,   2 * 12], 
             [3 * 13,   4 * 14]]
    
  • Matrix multiplication will yield:

    A @ B  =  [[1 * 11 + 2 * 13,   1 * 12 + 2 * 14],
               [3 * 11 + 4 * 13,   3 * 12 + 4 * 14]]
    

Usage in Numpy

So far, Numpy used the following convention:

Introduction of the @ operator makes the code involving matrix multiplications much easier to read. PEP0465 gives us an example:

# Current implementation of matrix multiplications using dot function
S = np.dot((np.dot(H, beta) - r).T,
            np.dot(inv(np.dot(np.dot(H, V), H.T)), np.dot(H, beta) - r))

# Current implementation of matrix multiplications using dot method
S = (H.dot(beta) - r).T.dot(inv(H.dot(V).dot(H.T))).dot(H.dot(beta) - r)

# Using the @ operator instead
S = (H @ beta - r).T @ inv(H @ V @ H.T) @ (H @ beta - r)

Clearly, the last implementation is much easier to read and interpret as an equation.


回答 2

@是Python3.5中新增的矩阵乘法运算符

参考:https : //docs.python.org/3/whatsnew/3.5.html#whatsnew-pep-465

C = A @ B

@ is the new operator for Matrix Multiplication added in Python3.5

Reference: https://docs.python.org/3/whatsnew/3.5.html#whatsnew-pep-465

Example

C = A @ B

如何在类型提示中指定函数类型?

问题:如何在类型提示中指定函数类型?

我想在当前的Python 3.5项目中使用类型提示。我的函数应该接收一个函数作为参数。

如何在类型提示中指定类型函数?

import typing

def my_function(name:typing.AnyStr, func: typing.Function) -> None:
    # However, typing.Function does not exist.
    # How can I specify the type function for the parameter `func`?

    # do some processing
    pass

我检查了PEP 483,但在那里找不到函数类型提示。

I want to use type hints in my current Python 3.5 project. My function should receive a function as parameter.

How can I specify the type function in my type hints?

import typing

def my_function(name:typing.AnyStr, func: typing.Function) -> None:
    # However, typing.Function does not exist.
    # How can I specify the type function for the parameter `func`?

    # do some processing
    pass

I checked PEP 483, but could not find a function type hint there.


回答 0

正如@jonrsharpe在评论中指出的,可以使用以下方法完成typing.Callable

from typing import AnyStr, Callable

def my_function(name: AnyStr, func: Callable) -> None:

问题是,Callable将其本身翻译为Callable[..., Any]

一个Callable接受任意数量的/类型的参数,并返回任何类型的值。在大多数情况下,这不是您想要的,因为您几乎可以允许传递任何函数。您也希望提示函数参数和返回类型。

这就是为什么许多typesin typing重载以支持表示这些额外类型的子脚本的原因。因此,例如,如果您有一个函数sum接受两个ints并返回一个int

def sum(a: int, b: int) -> int: return a+b

您的注释为:

Callable[[int, int], int]

也就是说,参数在外部订阅中带有下标,返回类型作为外部订阅中的第二个元素。一般来说:

Callable[[ParamType1, ParamType2, .., ParamTypeN], ReturnType]

As @jonrsharpe noted in a comment, this can be done with typing.Callable:

from typing import AnyStr, Callable

def my_function(name: AnyStr, func: Callable) -> None:

Issue is, Callable on it’s own is translated to Callable[..., Any] which means:

A callable takes any number of/type of arguments and returns a value of any type. In most cases, this isn’t what you want since you’ll allow pretty much any function to be passed. You want the function parameters and return types to be hinted too.

That’s why many types in typing have been overloaded to support sub-scripting which denotes these extra types. So if, for example, you had a function sum that takes two ints and returns an int:

def sum(a: int, b: int) -> int: return a+b

Your annotation for it would be:

Callable[[int, int], int]

that is, the parameters are sub-scripted in the outer subscription with the return type as the second element in the outer subscription. In general:

Callable[[ParamType1, ParamType2, .., ParamTypeN], ReturnType]

回答 1

另一个需要注意的有趣点是,您可以使用内置函数type()来获取内置函数的类型并使用它。所以你可以

def f(my_function: type(abs)) -> int:
    return my_function(100)

或那种形式的东西

Another interesting point to note is that you can use the built in function type() to get the type of a built in function and use that. So you could have

def f(my_function: type(abs)) -> int:
    return my_function(100)

Or something of that form


numpy dot()和Python 3.5+矩阵乘法@之间的区别

问题:numpy dot()和Python 3.5+矩阵乘法@之间的区别

我最近使用Python 3.5,注意到新的矩阵乘法运算符(@)有时与numpy点运算符的行为有所不同。例如,对于3d阵列:

import numpy as np

a = np.random.rand(8,13,13)
b = np.random.rand(8,13,13)
c = a @ b  # Python 3.5+
d = np.dot(a, b)

@运算符返回形状的阵列:

c.shape
(8, 13, 13)

np.dot()函数返回时:

d.shape
(8, 13, 8, 13)

如何用numpy点重现相同的结果?还有其他重大区别吗?

I recently moved to Python 3.5 and noticed the new matrix multiplication operator (@) sometimes behaves differently from the numpy dot operator. In example, for 3d arrays:

import numpy as np

a = np.random.rand(8,13,13)
b = np.random.rand(8,13,13)
c = a @ b  # Python 3.5+
d = np.dot(a, b)

The @ operator returns an array of shape:

c.shape
(8, 13, 13)

while the np.dot() function returns:

d.shape
(8, 13, 8, 13)

How can I reproduce the same result with numpy dot? Are there any other significant differences?


回答 0

@运营商称阵列的__matmul__方法,而不是dot。此方法在API中也作为函数存在np.matmul

>>> a = np.random.rand(8,13,13)
>>> b = np.random.rand(8,13,13)
>>> np.matmul(a, b).shape
(8, 13, 13)

从文档中:

matmul区别在于dot两个重要方面。

  • 标量不能相乘。
  • 将矩阵堆栈一起广播,就好像矩阵是元素一样。

最后一点很清楚,当传递3D(或更高维)数组时,dotmatmul方法的行为会有所不同。从文档中引用更多内容:

对于matmul

如果任何一个参数为ND,N> 2,则将其视为驻留在最后两个索引中的一组矩阵,并进行相应广播。

对于np.dot

对于2-D数组,它等效于矩阵乘法,对于1-D数组,其等效于向量的内积(无复共轭)。对于N维,它是a的最后一个轴和b的倒数第二个轴的和积

The @ operator calls the array’s __matmul__ method, not dot. This method is also present in the API as the function np.matmul.

>>> a = np.random.rand(8,13,13)
>>> b = np.random.rand(8,13,13)
>>> np.matmul(a, b).shape
(8, 13, 13)

From the documentation:

matmul differs from dot in two important ways.

  • Multiplication by scalars is not allowed.
  • Stacks of matrices are broadcast together as if the matrices were elements.

The last point makes it clear that dot and matmul methods behave differently when passed 3D (or higher dimensional) arrays. Quoting from the documentation some more:

For matmul:

If either argument is N-D, N > 2, it is treated as a stack of matrices residing in the last two indexes and broadcast accordingly.

For np.dot:

For 2-D arrays it is equivalent to matrix multiplication, and for 1-D arrays to inner product of vectors (without complex conjugation). For N dimensions it is a sum product over the last axis of a and the second-to-last of b


回答 1

@ajcr的答案说明了dotand matmul(由@符号调用)之间的区别。通过看一个简单的例子,可以清楚地看到两者在“矩阵堆栈”或张量上进行操作时的行为有何不同。

为了弄清差异,采用4×4数组,然后将dot乘积和matmul乘积返回3x4x2的“矩阵堆栈”或张量。

import numpy as np
fourbyfour = np.array([
                       [1,2,3,4],
                       [3,2,1,4],
                       [5,4,6,7],
                       [11,12,13,14]
                      ])


threebyfourbytwo = np.array([
                             [[2,3],[11,9],[32,21],[28,17]],
                             [[2,3],[1,9],[3,21],[28,7]],
                             [[2,3],[1,9],[3,21],[28,7]],
                            ])

print('4x4*3x4x2 dot:\n {}\n'.format(np.dot(fourbyfour,twobyfourbythree)))
print('4x4*3x4x2 matmul:\n {}\n'.format(np.matmul(fourbyfour,twobyfourbythree)))

每个操作的结果如下所示。注意点积如何

… a的最后一个轴与b的倒数第二个和的乘积

以及如何通过一起广播矩阵来形成矩阵乘积。

4x4*3x4x2 dot:
 [[[232 152]
  [125 112]
  [125 112]]

 [[172 116]
  [123  76]
  [123  76]]

 [[442 296]
  [228 226]
  [228 226]]

 [[962 652]
  [465 512]
  [465 512]]]

4x4*3x4x2 matmul:
 [[[232 152]
  [172 116]
  [442 296]
  [962 652]]

 [[125 112]
  [123  76]
  [228 226]
  [465 512]]

 [[125 112]
  [123  76]
  [228 226]
  [465 512]]]

The answer by @ajcr explains how the dot and matmul (invoked by the @ symbol) differ. By looking at a simple example, one clearly sees how the two behave differently when operating on ‘stacks of matricies’ or tensors.

To clarify the differences take a 4×4 array and return the dot product and matmul product with a 3x4x2 ‘stack of matricies’ or tensor.

import numpy as np
fourbyfour = np.array([
                       [1,2,3,4],
                       [3,2,1,4],
                       [5,4,6,7],
                       [11,12,13,14]
                      ])


threebyfourbytwo = np.array([
                             [[2,3],[11,9],[32,21],[28,17]],
                             [[2,3],[1,9],[3,21],[28,7]],
                             [[2,3],[1,9],[3,21],[28,7]],
                            ])

print('4x4*3x4x2 dot:\n {}\n'.format(np.dot(fourbyfour,threebyfourbytwo)))
print('4x4*3x4x2 matmul:\n {}\n'.format(np.matmul(fourbyfour,threebyfourbytwo)))

The products of each operation appear below. Notice how the dot product is,

…a sum product over the last axis of a and the second-to-last of b

and how the matrix product is formed by broadcasting the matrix together.

4x4*3x4x2 dot:
 [[[232 152]
  [125 112]
  [125 112]]

 [[172 116]
  [123  76]
  [123  76]]

 [[442 296]
  [228 226]
  [228 226]]

 [[962 652]
  [465 512]
  [465 512]]]

4x4*3x4x2 matmul:
 [[[232 152]
  [172 116]
  [442 296]
  [962 652]]

 [[125 112]
  [123  76]
  [228 226]
  [465 512]]

 [[125 112]
  [123  76]
  [228 226]
  [465 512]]]

回答 2

仅供参考,@其numpy的等价物dot,并matmul都大致一样快。(用我的一个项目perfplot创建的图。)

复制剧情的代码:

import perfplot
import numpy


def setup(n):
    A = numpy.random.rand(n, n)
    x = numpy.random.rand(n)
    return A, x


def at(data):
    A, x = data
    return A @ x


def numpy_dot(data):
    A, x = data
    return numpy.dot(A, x)


def numpy_matmul(data):
    A, x = data
    return numpy.matmul(A, x)


perfplot.show(
    setup=setup,
    kernels=[at, numpy_dot, numpy_matmul],
    n_range=[2 ** k for k in range(12)],
    logx=True,
    logy=True,
)

Just FYI, @ and its numpy equivalents dot and matmul are all equally fast. (Plot created with perfplot, a project of mine.)

Code to reproduce the plot:

import perfplot
import numpy


def setup(n):
    A = numpy.random.rand(n, n)
    x = numpy.random.rand(n)
    return A, x


def at(data):
    A, x = data
    return A @ x


def numpy_dot(data):
    A, x = data
    return numpy.dot(A, x)


def numpy_matmul(data):
    A, x = data
    return numpy.matmul(A, x)


perfplot.show(
    setup=setup,
    kernels=[at, numpy_dot, numpy_matmul],
    n_range=[2 ** k for k in range(15)],
)

回答 3

在数学上,我认为numpy中的更有意义

(a,b)_ {i,j,k,a,b,c} =

因为当a和b是向量时它给出点积,或者当a和b是矩阵时给出矩阵乘积


对于numpy中的matmul操作,它由结果的一部分组成,可以定义为

> matmul(a,b)_ {i,j,k,c} =

因此,您可以看到matmul(a,b)返回的数组形状较小,从而减少了内存消耗,并在应用程序中更有意义。特别是结合广播,您可以获得

matmul(a,b)_ {i,j,k,l} =

例如。


从以上两个定义中,您可以看到使用这两个操作的要求。假设a.shape =(s1,s2,s3,s4)b.shape =(t1,t2,t3,t4)

  • 要使用点(a,b),您需要

    1. t3 = s4 ;
  • 要使用matmul(a,b),您需要

    1. t3 = s4
    2. t2 = s2或t2和s2之一为1
    3. t1 = s1或t1和s1之一为1

使用以下代码说服自己。

代码样例

import numpy as np
for it in xrange(10000):
    a = np.random.rand(5,6,2,4)
    b = np.random.rand(6,4,3)
    c = np.matmul(a,b)
    d = np.dot(a,b)
    #print 'c shape: ', c.shape,'d shape:', d.shape

    for i in range(5):
        for j in range(6):
            for k in range(2):
                for l in range(3):
                    if not c[i,j,k,l] == d[i,j,k,j,l]:
                        print it,i,j,k,l,c[i,j,k,l]==d[i,j,k,j,l] #you will not see them

In mathematics, I think the dot in numpy makes more sense

dot(a,b)_{i,j,k,a,b,c} =

since it gives the dot product when a and b are vectors, or the matrix multiplication when a and b are matrices


As for matmul operation in numpy, it consists of parts of dot result, and it can be defined as

>matmul(a,b)_{i,j,k,c} =

So, you can see that matmul(a,b) returns an array with a small shape, which has smaller memory consumption and make more sense in applications. In particular, combining with broadcasting, you can get

matmul(a,b)_{i,j,k,l} =

for example.


From the above two definitions, you can see the requirements to use those two operations. Assume a.shape=(s1,s2,s3,s4) and b.shape=(t1,t2,t3,t4)

  • To use dot(a,b) you need

    1. t3=s4;
  • To use matmul(a,b) you need

    1. t3=s4
    2. t2=s2, or one of t2 and s2 is 1
    3. t1=s1, or one of t1 and s1 is 1

Use the following piece of code to convince yourself.

Code sample

import numpy as np
for it in xrange(10000):
    a = np.random.rand(5,6,2,4)
    b = np.random.rand(6,4,3)
    c = np.matmul(a,b)
    d = np.dot(a,b)
    #print 'c shape: ', c.shape,'d shape:', d.shape

    for i in range(5):
        for j in range(6):
            for k in range(2):
                for l in range(3):
                    if not c[i,j,k,l] == d[i,j,k,j,l]:
                        print it,i,j,k,l,c[i,j,k,l]==d[i,j,k,j,l] #you will not see them

回答 4

这是与的比较,np.einsum以显示索引的投影方式

np.allclose(np.einsum('ijk,ijk->ijk', a,b), a*b)        # True 
np.allclose(np.einsum('ijk,ikl->ijl', a,b), a@b)        # True
np.allclose(np.einsum('ijk,lkm->ijlm',a,b), a.dot(b))   # True

Here is a comparison with np.einsum to show how the indices are projected

np.allclose(np.einsum('ijk,ijk->ijk', a,b), a*b)        # True 
np.allclose(np.einsum('ijk,ikl->ijl', a,b), a@b)        # True
np.allclose(np.einsum('ijk,lkm->ijlm',a,b), a.dot(b))   # True

回答 5

我对MATMUL和DOT的经验

尝试使用MATMUL时,我经常收到“ ValueError:传递的值的形状为(200,1),索引表示(200,3)”。我想要一个快速的解决方法,并发现DOT可以提供相同的功能。使用DOT我没有任何错误。我得到正确的答案

与MATMUL

X.shape
>>>(200, 3)

type(X)

>>>pandas.core.frame.DataFrame

w

>>>array([0.37454012, 0.95071431, 0.73199394])

YY = np.matmul(X,w)

>>>  ValueError: Shape of passed values is (200, 1), indices imply (200, 3)"

与DOT

YY = np.dot(X,w)
# no error message
YY
>>>array([ 2.59206877,  1.06842193,  2.18533396,  2.11366346,  0.28505879, 

YY.shape

>>> (200, )

My experience with MATMUL and DOT

I was constantly getting “ValueError: Shape of passed values is (200, 1), indices imply (200, 3)” when trying to use MATMUL. I wanted a quick workaround and found DOT to deliver the same functionality. I don’t get any error using DOT. I get the correct answer

with MATMUL

X.shape
>>>(200, 3)

type(X)

>>>pandas.core.frame.DataFrame

w

>>>array([0.37454012, 0.95071431, 0.73199394])

YY = np.matmul(X,w)

>>>  ValueError: Shape of passed values is (200, 1), indices imply (200, 3)"

with DOT

YY = np.dot(X,w)
# no error message
YY
>>>array([ 2.59206877,  1.06842193,  2.18533396,  2.11366346,  0.28505879, …

YY.shape

>>> (200, )

为什么在Python 3.5中str.translate比Python 3.4更快?

问题:为什么在Python 3.5中str.translate比Python 3.4更快?

我试图使用text.translate()Python 3.4 从给定的字符串中删除不需要的字符。

最小的代码是:

import sys 
s = 'abcde12345@#@$#%$'
mapper = dict.fromkeys(i for i in range(sys.maxunicode) if chr(i) in '@#$')
print(s.translate(mapper))

它按预期工作。但是,在Python 3.4和Python 3.5中执行相同的程序会产生很大的不同。

计算时间的代码是

python3 -m timeit -s "import sys;s = 'abcde12345@#@$#%$'*1000 ; mapper = dict.fromkeys(i for i in range(sys.maxunicode) if chr(i) in '@#$'); "   "s.translate(mapper)"

Python 3.4程序花费1.3毫秒,而Python 3.5中的同一程序仅花费26.4μs

Python 3.5中有哪些改进使其比Python 3.4更快?

I was trying to remove unwanted characters from a given string using text.translate() in Python 3.4.

The minimal code is:

import sys 
s = 'abcde12345@#@$#%$'
mapper = dict.fromkeys(i for i in range(sys.maxunicode) if chr(i) in '@#$')
print(s.translate(mapper))

It works as expected. However the same program when executed in Python 3.4 and Python 3.5 gives a large difference.

The code to calculate timings is

python3 -m timeit -s "import sys;s = 'abcde12345@#@$#%$'*1000 ; mapper = dict.fromkeys(i for i in range(sys.maxunicode) if chr(i) in '@#$'); "   "s.translate(mapper)"

The Python 3.4 program takes 1.3ms whereas the same program in Python 3.5 takes only 26.4μs.

What has improved in Python 3.5 that makes it faster compared to Python 3.4?


回答 0

TL; DR- 问题21118


长篇故事

Josh Rosenberg发现str.translate()与相比,该功能非常慢bytes.translate,他提出了一个问题,并指出:

在Python 3中,str.translate()通常是性能悲观,而不是优化。

为什么str.translate()慢呢?

str.translate()速度很慢的主要原因是查找曾经在Python字典中进行。

使用maketrans此问题使情况变得更糟。类似的方法是使用bytes256个项目构建一个C数组以快速查找表。因此,较高级别的Python的使用dict使str.translate()Python 3.4中的速度非常慢。

现在发生什么事?

第一种方法是添加一个小的补丁,translate_writer,但是速度的提高并不令人满意。很快又测试了另一个补丁fast_translate,它产生了非常好的结果,加速了55%。

从文件中可以看到的主要变化是Python字典查找已更改为C级查找。

现在的速度几乎与 bytes

                                unpatched           patched

str.translate                   4.55125927699919    0.7898181750006188
str.translate from bytes trans  1.8910855210015143  0.779950579000797

这里需要注意的一点是,性能增强仅在ASCII字符串中突出。

正如JFSebastian在下面的注释中提到的,在3.5之前,对于ASCII和非ASCII情况,转换以前都以相同的方式工作。但是从3.5 ASCII起,大小写要快得多。

早期的ASCII与非ASCII几乎相同,但是现在我们可以看到性能有了很大的变化。

答案所示,它可以从71.6μs改善到2.33μs 。

以下代码演示了这一点

python3.5 -m timeit -s "text = 'mJssissippi'*100; d=dict(J='i')" "text.translate(d)"
100000 loops, best of 3: 2.3 usec per loop
python3.5 -m timeit -s "text = 'm\U0001F602ssissippi'*100; d={'\U0001F602': 'i'}" "text.translate(d)"
10000 loops, best of 3: 117 usec per loop

python3 -m timeit -s "text = 'm\U0001F602ssissippi'*100; d={'\U0001F602': 'i'}" "text.translate(d)"
10000 loops, best of 3: 91.2 usec per loop
python3 -m timeit -s "text = 'mJssissippi'*100; d=dict(J='i')" "text.translate(d)"
10000 loops, best of 3: 101 usec per loop

结果列表:

         Python 3.4    Python 3.5  
Ascii     91.2          2.3 
Unicode   101           117

TL;DR – ISSUE 21118


The long Story

Josh Rosenberg found out that the str.translate() function is very slow compared to the bytes.translate, he raised an issue, stating that:

In Python 3, str.translate() is usually a performance pessimization, not optimization.

Why was str.translate() slow?

The main reason for str.translate() to be very slow was that the lookup used to be in a Python dictionary.

The usage of maketrans made this problem worse. The similar approach using bytes builds a C array of 256 items to fast table lookup. Hence the usage of higher level Python dict makes the str.translate() in Python 3.4 very slow.

What happened now?

The first approach was to add a small patch, translate_writer, However the speed increase was not that pleasing. Soon another patch fast_translate was tested and it yielded very nice results of up to 55% speedup.

The main change as can be seen from the file is that the Python dictionary lookup is changed into a C level lookup.

The speeds now are almost the same as bytes

                                unpatched           patched

str.translate                   4.55125927699919    0.7898181750006188
str.translate from bytes trans  1.8910855210015143  0.779950579000797

A small note here is that the performance enhancement is only prominent in ASCII strings.

As J.F.Sebastian mentions in a comment below, Before 3.5, translate used to work in the same way for both ASCII and non-ASCII cases. However from 3.5 ASCII case is much faster.

Earlier ASCII vs non-ascii used to be almost same, however now we can see a great change in the performance.

It can be an improvement from 71.6μs to 2.33μs as seen in this answer.

The following code demonstrates this

python3.5 -m timeit -s "text = 'mJssissippi'*100; d=dict(J='i')" "text.translate(d)"
100000 loops, best of 3: 2.3 usec per loop
python3.5 -m timeit -s "text = 'm\U0001F602ssissippi'*100; d={'\U0001F602': 'i'}" "text.translate(d)"
10000 loops, best of 3: 117 usec per loop

python3 -m timeit -s "text = 'm\U0001F602ssissippi'*100; d={'\U0001F602': 'i'}" "text.translate(d)"
10000 loops, best of 3: 91.2 usec per loop
python3 -m timeit -s "text = 'mJssissippi'*100; d=dict(J='i')" "text.translate(d)"
10000 loops, best of 3: 101 usec per loop

Tabulation of the results:

         Python 3.4    Python 3.5  
Ascii     91.2          2.3 
Unicode   101           117

“失火” Python异步/等待

问题:“失火” Python异步/等待

有时需要执行一些非关键性的异步操作,但我不想等待它完成。在Tornado的协程实现中,您可以通过简单地省略yield关键字来“触发并忘记”一个异步函数。

我一直在尝试找出如何使用Python 3.5中发布的新语法async/ 来“激发并忘记” await。例如,一个简化的代码片段:

async def async_foo():
    print("Do some stuff asynchronously here...")

def bar():
    async_foo()  # fire and forget "async_foo()"

bar()

但是,发生的事情是bar()永远不会执行,而是收到运行时警告:

RuntimeWarning: coroutine 'async_foo' was never awaited
  async_foo()  # fire and forget "async_foo()"

Sometimes there is some non-critical asynchronous operation that needs to happen but I don’t want to wait for it to complete. In Tornado’s coroutine implementation you can “fire & forget” an asynchronous function by simply ommitting the yield key-word.

I’ve been trying to figure out how to “fire & forget” with the new async/await syntax released in Python 3.5. E.g., a simplified code snippet:

async def async_foo():
    print("Do some stuff asynchronously here...")

def bar():
    async_foo()  # fire and forget "async_foo()"

bar()

What happens though is that bar() never executes and instead we get a runtime warning:

RuntimeWarning: coroutine 'async_foo' was never awaited
  async_foo()  # fire and forget "async_foo()"

回答 0

更新:

如果您使用的是Python> = 3.7,请在任何地方替换asyncio.ensure_futureasyncio.create_task最新的,更好的派生task的方法


asyncio。“解雇”的任务

根据python docs的asyncio.Task说法,有可能启动一些协程以“在后台”执行asyncio.ensure_future 函数创建的任务不会阻止执行(因此函数将立即返回!)。这似乎是您要求的一种“解雇”的方法。

import asyncio


async def async_foo():
    print("async_foo started")
    await asyncio.sleep(1)
    print("async_foo done")


async def main():
    asyncio.ensure_future(async_foo())  # fire and forget async_foo()

    # btw, you can also create tasks inside non-async funcs

    print('Do some actions 1')
    await asyncio.sleep(1)
    print('Do some actions 2')
    await asyncio.sleep(1)
    print('Do some actions 3')


if __name__ == '__main__':
    loop = asyncio.get_event_loop()
    loop.run_until_complete(main())

输出:

Do some actions 1
async_foo started
Do some actions 2
async_foo done
Do some actions 3

如果事件循环完成后正在执行任务怎么办?

请注意,asyncio期望任务将在事件循环完成时完成。因此,如果您更改main()为:

async def main():
    asyncio.ensure_future(async_foo())  # fire and forget

    print('Do some actions 1')
    await asyncio.sleep(0.1)
    print('Do some actions 2')

程序完成后,您会收到以下警告:

Task was destroyed but it is pending!
task: <Task pending coro=<async_foo() running at [...]

为防止这种情况,您可以在事件循环完成后等待所有待处理的任务

async def main():
    asyncio.ensure_future(async_foo())  # fire and forget

    print('Do some actions 1')
    await asyncio.sleep(0.1)
    print('Do some actions 2')


if __name__ == '__main__':
    loop = asyncio.get_event_loop()
    loop.run_until_complete(main())

    # Let's also finish all running tasks:
    pending = asyncio.Task.all_tasks()
    loop.run_until_complete(asyncio.gather(*pending))

杀死任务而不是等待任务

有时,您不想等待任务完成(例如,某些任务可能创建为永久运行)。在这种情况下,您可以只取消()而不是等待它们:

import asyncio
from contextlib import suppress


async def echo_forever():
    while True:
        print("echo")
        await asyncio.sleep(1)


async def main():
    asyncio.ensure_future(echo_forever())  # fire and forget

    print('Do some actions 1')
    await asyncio.sleep(1)
    print('Do some actions 2')
    await asyncio.sleep(1)
    print('Do some actions 3')


if __name__ == '__main__':
    loop = asyncio.get_event_loop()
    loop.run_until_complete(main())

    # Let's also cancel all running tasks:
    pending = asyncio.Task.all_tasks()
    for task in pending:
        task.cancel()
        # Now we should await task to execute it's cancellation.
        # Cancelled task raises asyncio.CancelledError that we can suppress:
        with suppress(asyncio.CancelledError):
            loop.run_until_complete(task)

输出:

Do some actions 1
echo
Do some actions 2
echo
Do some actions 3
echo

Upd:

Replace asyncio.ensure_future with asyncio.create_task everywhere if you’re using Python >= 3.7 It’s newer, nicer way to spawn task.


asyncio.Task to “fire and forget”

According to python docs for asyncio.Task it is possible to start some coroutine to execute “in background”. The task created by asyncio.ensure_future function won’t block the execution (therefore the function will return immediately!). This looks like a way to “fire and forget” as you requested.

import asyncio


async def async_foo():
    print("async_foo started")
    await asyncio.sleep(1)
    print("async_foo done")


async def main():
    asyncio.ensure_future(async_foo())  # fire and forget async_foo()

    # btw, you can also create tasks inside non-async funcs

    print('Do some actions 1')
    await asyncio.sleep(1)
    print('Do some actions 2')
    await asyncio.sleep(1)
    print('Do some actions 3')


if __name__ == '__main__':
    loop = asyncio.get_event_loop()
    loop.run_until_complete(main())

Output:

Do some actions 1
async_foo started
Do some actions 2
async_foo done
Do some actions 3

What if tasks are executing after event loop complete?

Note that asyncio expects task would be completed at the moment event loop completed. So if you’ll change main() to:

async def main():
    asyncio.ensure_future(async_foo())  # fire and forget

    print('Do some actions 1')
    await asyncio.sleep(0.1)
    print('Do some actions 2')

You’ll get this warning after the program finished:

Task was destroyed but it is pending!
task: <Task pending coro=<async_foo() running at [...]

To prevent that you can just await all pending tasks after event loop completed:

async def main():
    asyncio.ensure_future(async_foo())  # fire and forget

    print('Do some actions 1')
    await asyncio.sleep(0.1)
    print('Do some actions 2')


if __name__ == '__main__':
    loop = asyncio.get_event_loop()
    loop.run_until_complete(main())

    # Let's also finish all running tasks:
    pending = asyncio.Task.all_tasks()
    loop.run_until_complete(asyncio.gather(*pending))

Kill tasks instead of awaiting them

Sometimes you don’t want to await tasks to be done (for example, some tasks may be created to run forever). In that case, you can just cancel() them instead of awaiting them:

import asyncio
from contextlib import suppress


async def echo_forever():
    while True:
        print("echo")
        await asyncio.sleep(1)


async def main():
    asyncio.ensure_future(echo_forever())  # fire and forget

    print('Do some actions 1')
    await asyncio.sleep(1)
    print('Do some actions 2')
    await asyncio.sleep(1)
    print('Do some actions 3')


if __name__ == '__main__':
    loop = asyncio.get_event_loop()
    loop.run_until_complete(main())

    # Let's also cancel all running tasks:
    pending = asyncio.Task.all_tasks()
    for task in pending:
        task.cancel()
        # Now we should await task to execute it's cancellation.
        # Cancelled task raises asyncio.CancelledError that we can suppress:
        with suppress(asyncio.CancelledError):
            loop.run_until_complete(task)

Output:

Do some actions 1
echo
Do some actions 2
echo
Do some actions 3
echo

回答 1

谢谢谢尔盖的简洁回答。这是相同的装饰版。

import asyncio
import time

def fire_and_forget(f):
    def wrapped(*args, **kwargs):
        return asyncio.get_event_loop().run_in_executor(None, f, *args, *kwargs)

    return wrapped

@fire_and_forget
def foo():
    time.sleep(1)
    print("foo() completed")

print("Hello")
foo()
print("I didn't wait for foo()")

产生

>>> Hello
>>> foo() started
>>> I didn't wait for foo()
>>> foo() completed

注意:检查我的其他答案,使用普通线程也可以做到这一点。

Thank you Sergey for the succint answer. Here is the decorated version of the same.

import asyncio
import time

def fire_and_forget(f):
    def wrapped(*args, **kwargs):
        return asyncio.get_event_loop().run_in_executor(None, f, *args, *kwargs)

    return wrapped

@fire_and_forget
def foo():
    time.sleep(1)
    print("foo() completed")

print("Hello")
foo()
print("I didn't wait for foo()")

Produces

>>> Hello
>>> foo() started
>>> I didn't wait for foo()
>>> foo() completed

Note: Check my other answer which does the same using plain threads.


回答 2

这不是完全异步执行,但也许run_in_executor()适合您。

def fire_and_forget(task, *args, **kwargs):
    loop = asyncio.get_event_loop()
    if callable(task):
        return loop.run_in_executor(None, task, *args, **kwargs)
    else:    
        raise TypeError('Task must be a callable')

def foo():
    #asynchronous stuff here


fire_and_forget(foo)

This is not entirely asynchronous execution, but maybe run_in_executor() is suitable for you.

def fire_and_forget(task, *args, **kwargs):
    loop = asyncio.get_event_loop()
    if callable(task):
        return loop.run_in_executor(None, task, *args, **kwargs)
    else:    
        raise TypeError('Task must be a callable')

def foo():
    #asynchronous stuff here


fire_and_forget(foo)

回答 3

由于某种原因,如果您无法使用,asyncio那么这里是使用普通线程的实现。检查我的其他答案和谢尔盖的答案。

import threading

def fire_and_forget(f):
    def wrapped():
        threading.Thread(target=f).start()

    return wrapped

@fire_and_forget
def foo():
    time.sleep(1)
    print("foo() completed")

print("Hello")
foo()
print("I didn't wait for foo()")

For some reason if you are unable to use asyncio then here is the implementation using plain threads. Check my other answers and Sergey’s answer too.

import threading

def fire_and_forget(f):
    def wrapped():
        threading.Thread(target=f).start()

    return wrapped

@fire_and_forget
def foo():
    time.sleep(1)
    print("foo() completed")

print("Hello")
foo()
print("I didn't wait for foo()")

asyncio.ensure_future与BaseEventLoop.create_task与简单协程?

问题:asyncio.ensure_future与BaseEventLoop.create_task与简单协程?

我看过一些关于asyncio的基本Python 3.5教程,以各种方式进行相同的操作。在此代码中:

import asyncio  

async def doit(i):
    print("Start %d" % i)
    await asyncio.sleep(3)
    print("End %d" % i)
    return i

if __name__ == '__main__':
    loop = asyncio.get_event_loop()
    #futures = [asyncio.ensure_future(doit(i), loop=loop) for i in range(10)]
    #futures = [loop.create_task(doit(i)) for i in range(10)]
    futures = [doit(i) for i in range(10)]
    result = loop.run_until_complete(asyncio.gather(*futures))
    print(result)

上面定义futures变量的所有三个变体都可以达到相同的结果。我可以看到的唯一区别是,在第三个变体中,执行是乱序的(在大多数情况下不重要)。还有其他区别吗?在某些情况下,我不能仅使用最简单的变体(协程的简单列表)吗?

I’ve seen several basic Python 3.5 tutorials on asyncio doing the same operation in various flavours. In this code:

import asyncio  

async def doit(i):
    print("Start %d" % i)
    await asyncio.sleep(3)
    print("End %d" % i)
    return i

if __name__ == '__main__':
    loop = asyncio.get_event_loop()
    #futures = [asyncio.ensure_future(doit(i), loop=loop) for i in range(10)]
    #futures = [loop.create_task(doit(i)) for i in range(10)]
    futures = [doit(i) for i in range(10)]
    result = loop.run_until_complete(asyncio.gather(*futures))
    print(result)

All the three variants above that define the futures variable achieve the same result; the only difference I can see is that with the third variant the execution is out of order (which should not matter in most cases). Is there any other difference? Are there cases where I can’t just use the simplest variant (plain list of coroutines)?


回答 0

实际信息:

从Python 3.7开始,为此添加asyncio.create_task(coro)高级功能。

您应该使用它代替其他从Coroutime创建任务的方式。但是,如果您需要从任意等待中创建任务,则应使用asyncio.ensure_future(obj)


旧信息:

ensure_futurecreate_task

ensure_future是一种Task从创建的方法coroutine。它基于参数(包括create_task对协程和类似未来的对象使用of)以不同的方式创建任务。

create_task是的抽象方法AbstractEventLoop。不同的事件循环可以以不同的方式实现此功能。

您应该ensure_future用来创建任务。create_task仅在要实现自己的事件循环类型时才需要。

更新:

@ bj0指向Guido对此主题的回答

的要点ensure_future()是,如果您拥有某种可能是协程或a的东西Future(后者包含a,Task因为这是的子类Future),并且您希望能够在其上调用仅在其上定义的方法Future(可能是唯一的)有用的示例cancel())。当它已经是Future(或Task)时,则不执行任何操作;当它是一个协程时,它将它包裹在一个Task

如果您知道有一个协程,并且希望对其进行调度,则使用的正确API是create_task()。唯一应该调用的时间ensure_future()是在提供接受协程或a的API(如asyncio自己的大多数API)时,Future您需要对其进行一些操作,要求您拥有a Future

然后:

最后,我仍然相信这ensure_future()是一个很少需要的功能的适当模糊的名称。从协程创建任务时,应使用适当命名的 loop.create_task()。也许应该为此起别名 asyncio.create_task()

我感到惊讶。我一直使用的主要动机ensure_future是,与loop的成员相比,它是更高层的函数create_task(讨论中包含了诸如add asyncio.spawn或的想法asyncio.create_task)。

我还可以指出,在我看来,使用可以处理任何Awaitable而非协程的通用函数非常方便。

但是,Guido的答案很明确:“从协程创建任务时,应使用名称正确的loop.create_task()

什么时候应该将协程包裹在任务中?

将协程包装在Task中-是一种在后台启动此协程的方法。例子如下:

import asyncio


async def msg(text):
    await asyncio.sleep(0.1)
    print(text)


async def long_operation():
    print('long_operation started')
    await asyncio.sleep(3)
    print('long_operation finished')


async def main():
    await msg('first')

    # Now you want to start long_operation, but you don't want to wait it finised:
    # long_operation should be started, but second msg should be printed immediately.
    # Create task to do so:
    task = asyncio.ensure_future(long_operation())

    await msg('second')

    # Now, when you want, you can await task finised:
    await task


if __name__ == "__main__":
    loop = asyncio.get_event_loop()
    loop.run_until_complete(main())

输出:

first
long_operation started
second
long_operation finished

您可以替换asyncio.ensure_future(long_operation())await long_operation()来感受不同。

Actual info:

Starting from Python 3.7 asyncio.create_task(coro) high-level function was added for this purpose.

You should use it instead other ways of creating tasks from coroutimes. However if you need to create task from arbitrary awaitable, you should use asyncio.ensure_future(obj).


Old info:

ensure_future vs create_task

ensure_future is a method to create Task from coroutine. It creates tasks in different ways based on argument (including using of create_task for coroutines and future-like objects).

create_task is an abstract method of AbstractEventLoop. Different event loops can implement this function different ways.

You should use ensure_future to create tasks. You’ll need create_task only if you’re going to implement your own event loop type.

Upd:

@bj0 pointed at Guido’s answer on this topic:

The point of ensure_future() is if you have something that could either be a coroutine or a Future (the latter includes a Task because that’s a subclass of Future), and you want to be able to call a method on it that is only defined on Future (probably about the only useful example being cancel()). When it is already a Future (or Task) this does nothing; when it is a coroutine it wraps it in a Task.

If you know that you have a coroutine and you want it to be scheduled, the correct API to use is create_task(). The only time when you should be calling ensure_future() is when you are providing an API (like most of asyncio’s own APIs) that accepts either a coroutine or a Future and you need to do something to it that requires you to have a Future.

and later:

In the end I still believe that ensure_future() is an appropriately obscure name for a rarely-needed piece of functionality. When creating a task from a coroutine you should use the appropriately-named loop.create_task(). Maybe there should be an alias for that asyncio.create_task()?

It’s surprising to me. My main motivation to use ensure_future all along was that it’s higher-level function comparing to loop’s member create_task (discussion contains some ideas like adding asyncio.spawn or asyncio.create_task).

I can also point that in my opinion it’s pretty convenient to use universal function that can handle any Awaitable rather than coroutines only.

However, Guido’s answer is clear: “When creating a task from a coroutine you should use the appropriately-named loop.create_task()

When coroutines should be wrapped in tasks?

Wrap coroutine in a Task – is a way to start this coroutine “in background”. Here’s example:

import asyncio


async def msg(text):
    await asyncio.sleep(0.1)
    print(text)


async def long_operation():
    print('long_operation started')
    await asyncio.sleep(3)
    print('long_operation finished')


async def main():
    await msg('first')

    # Now you want to start long_operation, but you don't want to wait it finised:
    # long_operation should be started, but second msg should be printed immediately.
    # Create task to do so:
    task = asyncio.ensure_future(long_operation())

    await msg('second')

    # Now, when you want, you can await task finised:
    await task


if __name__ == "__main__":
    loop = asyncio.get_event_loop()
    loop.run_until_complete(main())

Output:

first
long_operation started
second
long_operation finished

You can replace asyncio.ensure_future(long_operation()) with just await long_operation() to feel the difference.


回答 1

create_task()

  • 接受协程,
  • 返回任务,
  • 它在循环上下文中调用。

ensure_future()

  • 接受期货,协程,等待对象,
  • 返回Task(如果Future通过,则返回Future)。
  • 如果给定的arg是协程,则使用create_task
  • 可以传递循环对象。

如您所见,create_task更具体。


async 没有create_task或sure_future的函数

简单的调用async函数返回协程

>>> async def doit(i):
...     await asyncio.sleep(3)
...     return i
>>> doit(4)   
<coroutine object doit at 0x7f91e8e80ba0>

并且由于gather幕后确保(ensure_future)args为期货,因此明确地ensure_future是多余的。

类似的问题loop.create_task,asyncio.async / ensure_future和Task有什么区别?

create_task()

  • accepts coroutines,
  • returns Task,
  • it is invoked in context of the loop.

ensure_future()

  • accepts Futures, coroutines, awaitable objects,
  • returns Task (or Future if Future passed).
  • if the given arg is a coroutine it uses create_task,
  • loop object can be passed.

As you can see the create_task is more specific.


async function without create_task or ensure_future

Simple invoking async function returns coroutine

>>> async def doit(i):
...     await asyncio.sleep(3)
...     return i
>>> doit(4)   
<coroutine object doit at 0x7f91e8e80ba0>

And since the gather under the hood ensures (ensure_future) that args are futures, explicitly ensure_future is redundant.

Similar question What’s the difference between loop.create_task, asyncio.async/ensure_future and Task?


回答 2

注意:仅对Python 3.7有效(对于Python 3.5,请参考前面的答案)。

从官方文档:

asyncio.create_task(在Python 3.7中添加)是生成新任务的替代方法,而不是ensure_future()


详情:

因此,现在,在Python 3.7及更高版本中,有2个顶级包装函数(相似但不同):

好吧,这两个包装函数最好都可以帮助您调用BaseEventLoop.create_task。唯一的区别是ensure_future接受任何awaitable对象并帮助您将其转换为Future。另外,您还可以在中提供自己的event_loop参数ensure_future。而且,根据您是否需要这些功能,您可以简单地选择要使用的包装器。

Note: Only valid for Python 3.7 (for Python 3.5 refer to the earlier answer).

From the official docs:

asyncio.create_task (added in Python 3.7) is the preferable way for spawning new tasks instead of ensure_future().


Detail:

So now, in Python 3.7 onwards, there are 2 top-level wrapper function (similar but different):

Well, utlimately both of these wrapper functions will help you call BaseEventLoop.create_task. The only difference is ensure_future accept any awaitable object and help you convert it into a Future. And also you can provide your own event_loop parameter in ensure_future. And depending if you need those capability or not, you can simply choose which wrapper to use.


回答 3

在您的示例中,所有这三种类型都是异步执行的。唯一的区别是,在第三个示例中,您预先生成了所有10个协程,然后一起提交给循环。因此只有最后一个随机输出。

for your example, all the three types execute asynchronously. the only difference is that, in the third example, you pre-generated all 10 coroutines, and submitted to the loop together. so only the last one gives output randomly.


如何注释多个返回值的类型?

问题:如何注释多个返回值的类型?

我如何使用类型提示来注释一个返回Iterable总是返回两个值的函数:abool和a str?提示Tuple[bool, str]很接近,除了将返回值类型限制为元组,而不是生成器或其他可迭代类型。

我主要是好奇的,因为我想注释一个foo()用于返回多个值的函数,如下所示:

always_a_bool, always_a_str = foo()

通常函数喜欢foo()做这样的事情return a, b(它返回一个元组),但我喜欢的类型暗示要足够灵活,以取代生成器或列表或别的东西返回的元组。

How do I use type hints to annotate a function that returns an Iterable that always yields two values: a bool and a str? The hint Tuple[bool, str] is close, except that it limits the return value type to a tuple, not a generator or other type of iterable.

I’m mostly curious because I would like to annotate a function foo() that is used to return multiple values like this:

always_a_bool, always_a_str = foo()

Usually functions like foo() do something like return a, b (which returns a tuple), but I would like the type hint to be flexible enough to replace the returned tuple with a generator or list or something else.


回答 0

您总是返回一个对象;使用return one, two只需返回一个元组。

是的,-> Tuple[bool, str]完全正确。

只有Tuple类型可以指定一个固定数量的元素,每一个不同的类型。如果您的函数产生固定数量的返回值,尤其是当这些值是特定的,不同的类型时,确实应该总是返回一个元组。

期望其他序列类型具有可变数量元素的一种类型规范,因此typing.Sequence此处不适用。另请参阅列表和元组之间有什么区别?

元组是异构数据结构(即,它们的条目具有不同的含义),而列表是同类序列。元组具有结构,列表具有顺序。

Python的类型提示系统遵循这一理念,目前尚无语法来指定固定长度的可迭代对象,并在特定位置包含特定类型。

如果必须指定任何可迭代的对象,那么最好的方法是:

-> Iterable[Union[bool, str]]

在这一点上,调用者可以期望布尔值和字符串以任意顺序,并且长度未知(0到无穷大之间)。

You are always returning one object; using return one, two simply returns a tuple.

So yes, -> Tuple[bool, str] is entirely correct.

Only the Tuple type lets you specify a fixed number of elements, each with a distinct type. You really should be returning a tuple, always, if your function produces a fixed number of return values, especially when those values are specific, distinct types.

Other sequence types are expected to have one type specification for a variable number of elements, so typing.Sequence is not suitable here. Also see What’s the difference between lists and tuples?

Tuples are heterogeneous data structures (i.e., their entries have different meanings), while lists are homogeneous sequences. Tuples have structure, lists have order.

Python’s type hint system adheres to that philosophy, there is currently no syntax to specify an iterable of fixed length and containing specific types at specific positions.

If you must specify that any iterable will do, then the best you can do is:

-> Iterable[Union[bool, str]]

at which point the caller can expect booleans and strings in any order, and of unknown length (anywhere between 0 and infinity).


没有循环导入的Python类型提示

问题:没有循环导入的Python类型提示

我正试图将我的大班分成两部分;好吧,基本上是进入“主”类和具有其他功能的mixin的,就像这样:

main.py 文件:

import mymixin.py

class Main(object, MyMixin):
    def func1(self, xxx):
        ...

mymixin.py 文件:

class MyMixin(object):
    def func2(self: Main, xxx):  # <--- note the type hint
        ...

现在,尽管这很好用,但是类型提示MyMixin.func2当然不起作用。我无法导入main.py,因为会进行周期性导入,并且没有提示,我的编辑器(PyCharm)无法分辨出什么self

我使用的是Python 3.4,如果在那里有解决方案,我愿意移至3.5。

有什么办法可以将我的Class分成两个文件并保留所有“连接”,以便我的IDE仍然可以自动完成以及知道类型的所有其他优点。

I’m trying to split my huge class into two; well, basically into the “main” class and a mixin with additional functions, like so:

main.py file:

import mymixin.py

class Main(object, MyMixin):
    def func1(self, xxx):
        ...

mymixin.py file:

class MyMixin(object):
    def func2(self: Main, xxx):  # <--- note the type hint
        ...

Now, while this works just fine, the type hint in MyMixin.func2 of course can’t work. I can’t import main.py, because I’d get a cyclic import and without the hint, my editor (PyCharm) can’t tell what self is.

I’m using Python 3.4, willing to move to 3.5 if a solution is available there.

Is there any way I can split my class into two files and keep all the “connections” so that my IDE still offers me auto completion & all the other goodies that come from it knowing the types?


回答 0

恐怕通常没有一种非常优雅的方式来处理导入周期。您的选择是重新设计代码以消除循环依赖性,或者如果不可行,请执行以下操作:

# some_file.py

from typing import TYPE_CHECKING
if TYPE_CHECKING:
    from main import Main

class MyObject(object):
    def func2(self, some_param: 'Main'):
        ...

TYPE_CHECKING常量始终False在运行时运行,因此不会评估导入,但是mypy(和其他类型检查工具)将评估该块的内容。

我们还需要将Main类型注释放入字符串中,以有效地向前声明它,因为该Main符号在运行时不可用。

如果您使用的是Python 3.7+,我们至少可以通过利用PEP 563来跳过必须提供显式字符串注释的情况:

# some_file.py

from __future__ import annotations
from typing import TYPE_CHECKING
if TYPE_CHECKING:
    from main import Main

class MyObject(object):
    # Hooray, cleaner annotations!
    def func2(self, some_param: Main):
        ...

from __future__ import annotations进口将使所有类型提示弦而跳过评估他们。这可以使我们的代码更符合人体工程学。

综上所述,与mypy一起使用mixins可能需要比您现在拥有的结构更多的结构。Mypy 建议一种基本上就是deceze所描述的方法-创建一个ABC,您的类MainMyMixin类都继承。如果您最终需要做一些类似的事情以使Pycharm的检查器满意,我不会感到惊讶。

There isn’t a hugely elegant way to handle import cycles in general, I’m afraid. Your choices are to either redesign your code to remove the cyclic dependency, or if it isn’t feasible, do something like this:

# some_file.py

from typing import TYPE_CHECKING
if TYPE_CHECKING:
    from main import Main

class MyObject(object):
    def func2(self, some_param: 'Main'):
        ...

The TYPE_CHECKING constant is always False at runtime, so the import won’t be evaluated, but mypy (and other type-checking tools) will evaluate the contents of that block.

We also need to make the Main type annotation into a string, effectively forward declaring it since the Main symbol isn’t available at runtime.

If you are using Python 3.7+, we can at least skip having to provide an explicit string annotation by taking advantage of PEP 563:

# some_file.py

from __future__ import annotations
from typing import TYPE_CHECKING
if TYPE_CHECKING:
    from main import Main

class MyObject(object):
    # Hooray, cleaner annotations!
    def func2(self, some_param: Main):
        ...

The from __future__ import annotations import will make all type hints be strings and skip evaluating them. This can help make our code here mildly more ergonomic.

All that said, using mixins with mypy will likely require a bit more structure then you currently have. Mypy recommends an approach that’s basically what deceze is describing — to create an ABC that both your Main and MyMixin classes inherit. I wouldn’t be surprised if you ended up needing to do something similar in order to make Pycharm’s checker happy.


回答 1

对于仅在导入类以进行类型检查时陷入困境的人们:您可能希望使用前向引用(PEP 484-类型提示):

当类型提示包含尚未定义的名称时,该定义可以表示为字符串文字,以便稍后解析。

所以代替:

class Tree:
    def __init__(self, left: Tree, right: Tree):
        self.left = left
        self.right = right

你做:

class Tree:
    def __init__(self, left: 'Tree', right: 'Tree'):
        self.left = left
        self.right = right

For people struggling with cyclic imports when importing class only for Type checking: you will likely want to use a Forward Reference (PEP 484 – Type Hints):

When a type hint contains names that have not been defined yet, that definition may be expressed as a string literal, to be resolved later.

So instead of:

class Tree:
    def __init__(self, left: Tree, right: Tree):
        self.left = left
        self.right = right

you do:

class Tree:
    def __init__(self, left: 'Tree', right: 'Tree'):
        self.left = left
        self.right = right

回答 2

更大的问题是,您的类型一开始并不理智。MyMixin进行硬编码的假设是将其混合到中Main,而可以将其混合到任何其他数量的类中,在这种情况下,它可能会损坏。如果将mixin硬编码为混合到一个特定的类中,则不妨将方法直接写入该类中,而不用将它们分开。

要使用合理的输入方式正确执行此操作,MyMixin应使用Python的说法对interface或abstract class 进行编码:

import abc


class MixinDependencyInterface(abc.ABC):
    @abc.abstractmethod
    def foo(self):
        pass


class MyMixin:
    def func2(self: MixinDependencyInterface, xxx):
        self.foo()  # ← mixin only depends on the interface


class Main(MixinDependencyInterface, MyMixin):
    def foo(self):
        print('bar')

The bigger issue is that your types aren’t sane to begin with. MyMixin makes a hardcoded assumption that it will be mixed into Main, whereas it could be mixed into any number of other classes, in which case it would probably break. If your mixin is hardcoded to be mixed into one specific class, you may as well write the methods directly into that class instead of separating them out.

To properly do this with sane typing, MyMixin should be coded against an interface, or abstract class in Python parlance:

import abc


class MixinDependencyInterface(abc.ABC):
    @abc.abstractmethod
    def foo(self):
        pass


class MyMixin:
    def func2(self: MixinDependencyInterface, xxx):
        self.foo()  # ← mixin only depends on the interface


class Main(MixinDependencyInterface, MyMixin):
    def foo(self):
        print('bar')

回答 3

事实证明,我最初的尝试也非常接近解决方案。这是我目前正在使用的:

# main.py
import mymixin.py

class Main(object, MyMixin):
    def func1(self, xxx):
        ...


# mymixin.py
if False:
    from main import Main

class MyMixin(object):
    def func2(self: 'Main', xxx):  # <--- note the type hint
        ...

请注意,import inside if False语句永远不会被导入(但IDE仍然知道它),并且将该Main类用作字符串,因为在运行时不知道。

Turns out my original attempt was quite close to the solution as well. This is what I’m currently using:

# main.py
import mymixin.py

class Main(object, MyMixin):
    def func1(self, xxx):
        ...


# mymixin.py
if False:
    from main import Main

class MyMixin(object):
    def func2(self: 'Main', xxx):  # <--- note the type hint
        ...

Note the import within if False statement that never gets imported (but IDE knows about it anyway) and using the Main class as string because it’s not known at runtime.


回答 4

我认为,完美的方法应该是将所有类和依赖项导入文件(如__init__.py),然后再导入所有from __init__ import *其他文件。

在这种情况下

  1. 避免对这些文件和类的多次引用,并且
  2. 也只需在其他每个文件中添加一行
  3. 第三个是知道您可能使用的所有类的pycharm。

I think the perfect way should be to import all the classes and dependencies in a file (like __init__.py) and then from __init__ import * in all the other files.

In this case you are

  1. avoiding multiple references to those files and classes and
  2. also only have to add one line in each of the other files and
  3. the third would be the pycharm knowing about all of the classes that you might use.

协程和Python 3.5中的future / task之间的区别?

问题:协程和Python 3.5中的future / task之间的区别?

假设我们有一个虚拟函数:

async def foo(arg):
    result = await some_remote_call(arg)
    return result.upper()

之间有什么区别:

coros = []
for i in range(5):
    coros.append(foo(i))

loop = get_event_loop()
loop.run_until_complete(wait(coros))

和:

from asyncio import ensure_future

futures = []
for i in range(5):
    futures.append(ensure_future(foo(i)))

loop = get_event_loop()
loop.run_until_complete(wait(futures))

注意:该示例返回结果,但这不是问题的重点。如果返回值很重要,请使用gather()代替wait()

无论返回值如何,我都希望在上保持清晰ensure_future()wait(coros)并且wait(futures)都运行协程,那么何时以及为什么要包装协程ensure_future

基本上,使用Python 3.5运行一堆非阻塞操作的正确方法(tm)是async什么?

为了获得额外的抵免额,如果我要批量处理电话,该怎么办?例如,我需要拨打some_remote_call(...)1000次,但我不想同时连接1000个连接而粉碎Web服务器/数据库/等。这对于线程或进程池是可行的,但是有没有办法做到这一点asyncio

Let’s say we have a dummy function:

async def foo(arg):
    result = await some_remote_call(arg)
    return result.upper()

What’s the difference between:

import asyncio    

coros = []
for i in range(5):
    coros.append(foo(i))

loop = asyncio.get_event_loop()
loop.run_until_complete(asyncio.wait(coros))

And:

import asyncio

futures = []
for i in range(5):
    futures.append(asyncio.ensure_future(foo(i)))

loop = asyncio.get_event_loop()
loop.run_until_complete(asyncio.wait(futures))

Note: The example returns a result, but this isn’t the focus of the question. When return value matters, use gather() instead of wait().

Regardless of return value, I’m looking for clarity on ensure_future(). wait(coros) and wait(futures) both run the coroutines, so when and why should a coroutine be wrapped in ensure_future?

Basically, what’s the Right Way ™ to run a bunch of non-blocking operations using Python 3.5’s async?

For extra credit, what if I want to batch the calls? For example, I need to call some_remote_call(...) 1000 times, but I don’t want to crush the web server/database/etc with 1000 simultaneous connections. This is doable with a thread or process pool, but is there a way to do this with asyncio?

2020 update (Python 3.7+): Don’t use these snippets. Instead use:

import asyncio

async def do_something_async():
    tasks = []
    for i in range(5):
        tasks.append(asyncio.create_task(foo(i)))
    await asyncio.gather(*tasks)

def do_something():
    asyncio.run(do_something_async)

Also consider using Trio, a robust 3rd party alternative to asyncio.


回答 0

协程是生成器函数,它既可以产生值也可以从外部接受值。使用协程的好处是我们可以暂停函数的执行并在以后恢复它。在网络操作的情况下,在我们等待响应的同时暂停函数的执行是有意义的。我们可以花时间运行其他功能。

未来就像PromiseJavascript中的对象一样。它就像一个占位符,代表着将在未来实现的价值。在上述情况下,在等待网络I / O时,一个函数可以给我们一个容器,保证在操作完成时它将用值填充该容器。我们保留了将来的对象,当它满足时,我们可以在其上调用方法以检索实际结果。

直接回答:你并不需要ensure_future,如果你不想要的结果。如果您需要结果或检索发生的异常,它们会很好。

额外积分:我将选择run_in_executor并传递一个Executor实例来控制最大工人数。

说明和示例代码

在第一个示例中,您正在使用协程。该wait函数接收一堆协程并将它们组合在一起。这样就wait()完成了所有协程的耗尽(返回所有值的完成/完成)。

loop = get_event_loop() # 
loop.run_until_complete(wait(coros))

run_until_complete方法将确保循环有效直到执行完成。请注意在这种情况下您如何无法获得异步执行的结果。

在第二个示例中,您将使用ensure_future函数包装协程并返回的Task对象Future。协程计划在您调用时在主事件循环中执行ensure_future。返回的future / task对象还没有值,但是随着时间的推移,当网络操作完成时,future对象将保存操作的结果。

from asyncio import ensure_future

futures = []
for i in range(5):
    futures.append(ensure_future(foo(i)))

loop = get_event_loop()
loop.run_until_complete(wait(futures))

因此,在此示例中,我们正在做相同的事情,除了使用期货而不是仅使用协程。

让我们看一下如何使用asyncio /协程/期货的示例:

import asyncio


async def slow_operation():
    await asyncio.sleep(1)
    return 'Future is done!'


def got_result(future):
    print(future.result())

    # We have result, so let's stop
    loop.stop()


loop = asyncio.get_event_loop()
task = loop.create_task(slow_operation())
task.add_done_callback(got_result)

# We run forever
loop.run_forever()

在这里,我们create_taskloop对象上使用了方法。ensure_future将在主事件循环中安排任务。这种方法使我们能够在选择的循环中安排协程。

我们还看到了add_done_callback在任务对象上使用方法添加回调的概念。

A Taskdone当协程返回值,引发异常或被取消时。有检查这些事件的方法。

我写了一些有关这些主题的博客文章,可能会有所帮助:

当然,您可以在官方手册上找到更多详细信息:https : //docs.python.org/3/library/asyncio.html

A coroutine is a generator function that can both yield values and accept values from the outside. The benefit of using a coroutine is that we can pause the execution of a function and resume it later. In case of a network operation, it makes sense to pause the execution of a function while we’re waiting for the response. We can use the time to run some other functions.

A future is like the Promise objects from Javascript. It is like a placeholder for a value that will be materialized in the future. In the above-mentioned case, while waiting on network I/O, a function can give us a container, a promise that it will fill the container with the value when the operation completes. We hold on to the future object and when it’s fulfilled, we can call a method on it to retrieve the actual result.

Direct Answer: You don’t need ensure_future if you don’t need the results. They are good if you need the results or retrieve exceptions occurred.

Extra Credits: I would choose run_in_executor and pass an Executor instance to control the number of max workers.

Explanations and Sample codes

In the first example, you are using coroutines. The wait function takes a bunch of coroutines and combines them together. So wait() finishes when all the coroutines are exhausted (completed/finished returning all the values).

loop = get_event_loop() # 
loop.run_until_complete(wait(coros))

The run_until_complete method would make sure that the loop is alive until the execution is finished. Please notice how you are not getting the results of the async execution in this case.

In the second example, you are using the ensure_future function to wrap a coroutine and return a Task object which is a kind of Future. The coroutine is scheduled to be executed in the main event loop when you call ensure_future. The returned future/task object doesn’t yet have a value but over time, when the network operations finish, the future object will hold the result of the operation.

from asyncio import ensure_future

futures = []
for i in range(5):
    futures.append(ensure_future(foo(i)))

loop = get_event_loop()
loop.run_until_complete(wait(futures))

So in this example, we’re doing the same thing except we’re using futures instead of just using coroutines.

Let’s look at an example of how to use asyncio/coroutines/futures:

import asyncio


async def slow_operation():
    await asyncio.sleep(1)
    return 'Future is done!'


def got_result(future):
    print(future.result())

    # We have result, so let's stop
    loop.stop()


loop = asyncio.get_event_loop()
task = loop.create_task(slow_operation())
task.add_done_callback(got_result)

# We run forever
loop.run_forever()

Here, we have used the create_task method on the loop object. ensure_future would schedule the task in the main event loop. This method enables us to schedule a coroutine on a loop we choose.

We also see the concept of adding a callback using the add_done_callback method on the task object.

A Task is done when the coroutine returns a value, raises an exception or gets canceled. There are methods to check these incidents.

I have written some blog posts on these topics which might help:

Of course, you can find more details on the official manual: https://docs.python.org/3/library/asyncio.html


回答 1

简单的答案

  • 调用协程函数(async def)不会运行它。它返回一个协程对象,就像生成器函数返回生成器对象一样。
  • await 从协程中检索值,即“调用”协程
  • eusure_future/create_task 安排协程在下一次迭代时在事件循环上运行(尽管不等待它们完成,就像守护线程一样)。

一些代码示例

让我们先清除一些术语:

  • 协程功能,您所需要async def的;
  • 协程对象,当您“调用”协程函数时得到的内容;
  • 任务,一个包裹在协程对象上的对象在事件循环上运行。

案例1,await在协程上

我们创建两个协程,await一个协程,并用于create_task运行另一个协程。

import asyncio
import time

# coroutine function
async def p(word):
    print(f'{time.time()} - {word}')


async def main():
    loop = asyncio.get_event_loop()
    coro = p('await')  # coroutine
    task2 = loop.create_task(p('create_task'))  # <- runs in next iteration
    await coro  # <-- run directly
    await task2

if __name__ == "__main__":
    loop = asyncio.get_event_loop()
    loop.run_until_complete(main())

您将得到结果:

1539486251.7055213 - await
1539486251.7055705 - create_task

说明:

task1直接执行,而task2在以下迭代中执行。

情况2,将控制权交给事件循环

如果替换main函数,则会看到不同的结果:

async def main():
    loop = asyncio.get_event_loop()
    coro = p('await')
    task2 = loop.create_task(p('create_task'))  # scheduled to next iteration
    await asyncio.sleep(1)  # loop got control, and runs task2
    await coro  # run coro
    await task2

您将得到结果:

-> % python coro.py
1539486378.5244057 - create_task
1539486379.5252144 - await  # note the delay

说明:

调用时asyncio.sleep(1),该控件已退回到事件循环,该循环检查要运行的任务,然后运行由创建的任务create_task

请注意,我们首先调用协程函数,但不是await,因此,我们只创建了一个协程,而不使其运行。然后,我们再次调用协程函数,并将其包装在create_task调用中,creat_task实际上将调度协程在下一次迭代中运行。因此,结果create task是在之前执行await

实际上,这里的重点是将控制权交还给循环,您可以asyncio.sleep(0)用来查看相同的结果。

引擎盖下

loop.create_task实际通话asyncio.tasks.Task(),将会通话loop.call_soon。并将loop.call_soon任务放入loop._ready。在循环的每次迭代期间,它将检查loop._ready中的每个回调并运行它。

asyncio.waitasyncio.ensure_future并且asyncio.gather实际上loop.create_task直接或间接调用。

另请注意文档

回调按注册顺序调用。每个回调将仅被调用一次。

Simple answer

  • Invoking a coroutine function(async def) does NOT run it. It returns a coroutine objects, like generator function returns generator objects.
  • await retrieves values from coroutines, i.e. “calls” the coroutine
  • eusure_future/create_task schedule the coroutine to run on the event loop on next iteration(although not waiting them to finish, like a daemon thread).

Some code examples

Let’s first clear some terms:

  • coroutine function, the one you async defs;
  • coroutine object, what you got when you “call” a coroutine function;
  • task, a object wrapped around a coroutine object to run on the event loop.

Case 1, await on a coroutine

We create two coroutines, await one, and use create_task to run the other one.

import asyncio
import time

# coroutine function
async def p(word):
    print(f'{time.time()} - {word}')


async def main():
    loop = asyncio.get_event_loop()
    coro = p('await')  # coroutine
    task2 = loop.create_task(p('create_task'))  # <- runs in next iteration
    await coro  # <-- run directly
    await task2

if __name__ == "__main__":
    loop = asyncio.get_event_loop()
    loop.run_until_complete(main())

you will get result:

1539486251.7055213 - await
1539486251.7055705 - create_task

Explain:

task1 was executed directly, and task2 was executed in the following iteration.

Case 2, yielding control to event loop

If we replace the main function, we can see a different result:

async def main():
    loop = asyncio.get_event_loop()
    coro = p('await')
    task2 = loop.create_task(p('create_task'))  # scheduled to next iteration
    await asyncio.sleep(1)  # loop got control, and runs task2
    await coro  # run coro
    await task2

you will get result:

-> % python coro.py
1539486378.5244057 - create_task
1539486379.5252144 - await  # note the delay

Explain:

When calling asyncio.sleep(1), the control was yielded back to the event loop, and the loop checks for tasks to run, then it runs the task created by create_task.

Note that, we first invoke the coroutine function, but not await it, so we just created a single coroutine, and not make it running. Then, we call the coroutine function again, and wrap it in a create_task call, creat_task will actually schedule the coroutine to run on next iteration. So, in the result, create task is executed before await.

Actually, the point here is to give back control to the loop, you could use asyncio.sleep(0) to see the same result.

Under the hood

loop.create_task actually calls asyncio.tasks.Task(), which will call loop.call_soon. And loop.call_soon will put the task in loop._ready. During each iteration of the loop, it checks for every callbacks in loop._ready and runs it.

asyncio.wait, asyncio.ensure_future and asyncio.gather actually call loop.create_task directly or indirectly.

Also note in the docs:

Callbacks are called in the order in which they are registered. Each callback will be called exactly once.


回答 2

文森特(Vincent)的评论链接到https://github.com/python/asyncio/blob/master/asyncio/tasks.py#L346,显示为您wait()包装了协程ensure_future()

换句话说,我们确实需要未来,协程将默默地转变为它们。

当我找到有关如何协程/期货的明确解释时,我将更新此答案。

A comment by Vincent linked to https://github.com/python/asyncio/blob/master/asyncio/tasks.py#L346, which shows that wait() wraps the coroutines in ensure_future() for you!

In other words, we do need a future, and coroutines will be silently transformed into them.

I’ll update this answer when I find a definitive explanation of how to batch coroutines/futures.


回答 3

摘自BDFL [2013]

任务

  • 这是包裹在未来的协程
  • Task类是Future类的子类
  • 因此,它与工作的await呢!

  • 它与裸协程有何不同?
  • 无需等待就可以取得进步
    • 只要您等待其他事情,即
      • 等待 [something_else]

考虑到这一点,ensure_future将其作为创建任务的名称是有意义的,因为无论您是否等待它(只要您等待某事),都会计算Future的结果。这使事件循环可以在您等待其他事情时完成您的任务。请注意,Python 3.7 create_task确保未来的首选方法。

注意:出于现代性的考虑,我将Guido幻灯片中的“收益率从”更改为“等待”。

From the BDFL [2013]

Tasks

  • It’s a coroutine wrapped in a Future
  • class Task is a subclass of class Future
  • So it works with await too!

  • How does it differ from a bare coroutine?
  • It can make progress without waiting for it
    • As long as you wait for something else, i.e.
      • await [something_else]

With this in mind, ensure_future makes sense as a name for creating a Task since the Future’s result will be computed whether or not you await it (as long as you await something). This allows the event loop to complete your Task while you’re waiting on other things. Note that in Python 3.7 create_task is the preferred way ensure a future.

Note: I changed “yield from” in Guido’s slides to “await” here for modernity.


为什么x ** 4.0比Python 3中的x ** 4快?

问题:为什么x ** 4.0比Python 3中的x ** 4快?

为什么x**4.0要比x**4?我正在使用CPython 3.5.2。

$ python -m timeit "for x in range(100):" " x**4.0"
  10000 loops, best of 3: 24.2 usec per loop

$ python -m timeit "for x in range(100):" " x**4"
  10000 loops, best of 3: 30.6 usec per loop

我尝试更改所提高的力量以查看其作用方式,例如,如果我将x提高至10或16的力量,它会从30跳到35,但是如果我以浮点数提高10.0,那只是在移动大约24.1〜4。

我想这可能与浮点转换和2的幂有关,但我真的不知道。

我注意到在这两种情况下2的幂都更快,因为这些计算对于解释器/计算机而言更本机/更容易,所以我想。但尽管如此,浮子几乎没有动。2.0 => 24.1~4 & 128.0 => 24.1~4 2 => 29 & 128 => 62


TigerhawkT3指出,它不会在循环之外发生。我检查了一下,这种情况只发生在基地抬高时(从我所看到的情况)。有什么想法吗?

Why is x**4.0 faster than x**4? I am using CPython 3.5.2.

$ python -m timeit "for x in range(100):" " x**4.0"
  10000 loops, best of 3: 24.2 usec per loop

$ python -m timeit "for x in range(100):" " x**4"
  10000 loops, best of 3: 30.6 usec per loop

I tried changing the power I raised by to see how it acts, and for example if I raise x to the power of 10 or 16 it’s jumping from 30 to 35, but if I’m raising by 10.0 as a float, it’s just moving around 24.1~4.

I guess it has something to do with float conversion and powers of 2 maybe, but I don’t really know.

I noticed that in both cases powers of 2 are faster, I guess since those calculations are more native/easy for the interpreter/computer. But still, with floats it’s almost not moving. 2.0 => 24.1~4 & 128.0 => 24.1~4 but 2 => 29 & 128 => 62


TigerhawkT3 pointed out that it doesn’t happen outside of the loop. I checked and the situation only occurs (from what I’ve seen) when the base is getting raised. Any idea about that?

回答 0

为什么比Python 3 * x**4.0 更快x**4

Python 3 int对象是成熟的对象,旨在支持任意大小。因此,它们是在C级别上进行处理的(请参阅如何在中将所有变量声明为PyLongObject *type long_pow)。这也使它们的取幂变得更加棘手乏味,因为您需要ob_digit使用它用来表示其值的数组进行操作。(勇敢的人士来了。-有关s的更多信息,请参见:了解Python中大整数的内存分配PyLongObject。)

float相反,可以将 Python 对象转换为C double类型(通过使用PyFloat_AsDouble),并且可以使用这些本机类型执行操作。这很棒,因为在检查了相关的边缘情况之后,它允许Python 使用平台的powC pow,即)来处理实际的幂运算:

/* Now iv and iw are finite, iw is nonzero, and iv is
 * positive and not equal to 1.0.  We finally allow
 * the platform pow to step in and do the rest.
 */
errno = 0;
PyFPE_START_PROTECT("pow", return NULL)
ix = pow(iv, iw); 

其中,iviw是我们的原PyFloatObjectS作为Ç double秒。

对于它的价值:Python 2.7.13对我而言是一个2~3更快的因子,并且显示出相反的行为。

先前的事实也解释了Python 2和3之间的差异,因此,我认为我也将解决此评论,因为它很有趣。

在Python 2中,您使用的旧int对象与intPython 3中的对象不同(int3.x中的所有对象都是PyLongObject类型)。在Python 2中,有一个区别取决于对象的值(或者,如果使用后缀L/l):

# Python 2
type(30)  # <type 'int'>
type(30L) # <type 'long'>

<type 'int'>你看到这里做同样的事情float就做,它被安全地转换成C long 时,就可以进行幂(中int_pow也暗示编译器放你的歌在寄存器中,如果能够这样做,这样可以有所作为) :

static PyObject *
int_pow(PyIntObject *v, PyIntObject *w, PyIntObject *z)
{
    register long iv, iw, iz=0, ix, temp, prev;
/* Snipped for brevity */    

这样可以提高速度。

要查看<type 'long'>s与<type 'int'>s 相比是否比较慢,如果将x名称包装long在Python 2 中的调用中(实质上是强制将其long_pow像在Python 3中那样使用),则速度增益会消失:

# <type 'int'>
(python2)  python -m timeit "for x in range(1000):" " x**2"       
10000 loops, best of 3: 116 usec per loop
# <type 'long'> 
(python2)  python -m timeit "for x in range(1000):" " long(x)**2"
100 loops, best of 3: 2.12 msec per loop

请注意,尽管一个代码段将转换为intlong而另一个代码段未将其转换为(如@pydsinger所指出的),但这种转换并不是减慢速度的推动力。执行long_pow是。(仅long(x)将时间与语句一起查看)。

[…]它不会在循环之外发生。[…]有什么想法吗?

这是CPython的窥孔优化器,可以为您折叠常量。无论哪种情况,您都会获得相同的精确计时,因为没有实际的计算来找到幂运算的结果,只加载值:

dis.dis(compile('4 ** 4', '', 'exec'))
  1           0 LOAD_CONST               2 (256)
              3 POP_TOP
              4 LOAD_CONST               1 (None)
              7 RETURN_VALUE

生成相同的字节码'4 ** 4.',唯一的区别是LOAD_CONST加载的是float 256.0而不是int 256

dis.dis(compile('4 ** 4.', '', 'exec'))
  1           0 LOAD_CONST               3 (256.0)
              2 POP_TOP
              4 LOAD_CONST               2 (None)
              6 RETURN_VALUE

所以时代是一样的。


*以上所有内容仅适用于CPython(Python的参考实现)。其他实现可能会有所不同。

Why is x**4.0 faster than x**4 in Python 3*?

Python 3 int objects are a full fledged object designed to support an arbitrary size; due to that fact, they are handled as such on the C level (see how all variables are declared as PyLongObject * type in long_pow). This also makes their exponentiation a lot more trickier and tedious since you need to play around with the ob_digit array it uses to represent its value to perform it. (Source for the brave. — See: Understanding memory allocation for large integers in Python for more on PyLongObjects.)

Python float objects, on the contrary, can be transformed to a C double type (by using PyFloat_AsDouble) and operations can be performed using those native types. This is great because, after checking for relevant edge-cases, it allows Python to use the platforms’ pow (C’s pow, that is) to handle the actual exponentiation:

/* Now iv and iw are finite, iw is nonzero, and iv is
 * positive and not equal to 1.0.  We finally allow
 * the platform pow to step in and do the rest.
 */
errno = 0;
PyFPE_START_PROTECT("pow", return NULL)
ix = pow(iv, iw); 

where iv and iw are our original PyFloatObjects as C doubles.

For what it’s worth: Python 2.7.13 for me is a factor 2~3 faster, and shows the inverse behaviour.

The previous fact also explains the discrepancy between Python 2 and 3 so, I thought I’d address this comment too because it is interesting.

In Python 2, you’re using the old int object that differs from the int object in Python 3 (all int objects in 3.x are of PyLongObject type). In Python 2, there’s a distinction that depends on the value of the object (or, if you use the suffix L/l):

# Python 2
type(30)  # <type 'int'>
type(30L) # <type 'long'>

The <type 'int'> you see here does the same thing floats do, it gets safely converted into a C long when exponentiation is performed on it (The int_pow also hints the compiler to put ’em in a register if it can do so, so that could make a difference):

static PyObject *
int_pow(PyIntObject *v, PyIntObject *w, PyIntObject *z)
{
    register long iv, iw, iz=0, ix, temp, prev;
/* Snipped for brevity */    

this allows for a good speed gain.

To see how sluggish <type 'long'>s are in comparison to <type 'int'>s, if you wrapped the x name in a long call in Python 2 (essentially forcing it to use long_pow as in Python 3), the speed gain disappears:

# <type 'int'>
(python2) ➜ python -m timeit "for x in range(1000):" " x**2"       
10000 loops, best of 3: 116 usec per loop
# <type 'long'> 
(python2) ➜ python -m timeit "for x in range(1000):" " long(x)**2"
100 loops, best of 3: 2.12 msec per loop

Take note that, though the one snippet transforms the int to long while the other does not (as pointed out by @pydsinger), this cast is not the contributing force behind the slowdown. The implementation of long_pow is. (Time the statements solely with long(x) to see).

[…] it doesn’t happen outside of the loop. […] Any idea about that?

This is CPython’s peephole optimizer folding the constants for you. You get the same exact timings either case since there’s no actual computation to find the result of the exponentiation, only loading of values:

dis.dis(compile('4 ** 4', '', 'exec'))
  1           0 LOAD_CONST               2 (256)
              3 POP_TOP
              4 LOAD_CONST               1 (None)
              7 RETURN_VALUE

Identical byte-code is generated for '4 ** 4.' with the only difference being that the LOAD_CONST loads the float 256.0 instead of the int 256:

dis.dis(compile('4 ** 4.', '', 'exec'))
  1           0 LOAD_CONST               3 (256.0)
              2 POP_TOP
              4 LOAD_CONST               2 (None)
              6 RETURN_VALUE

So the times are identical.


*All of the above apply solely for CPython, the reference implementation of Python. Other implementations might perform differently.


回答 1

如果我们查看字节码,我们可以看到表达式是完全相同的。唯一的区别是常量的类型,它将是的自变量BINARY_POWER。因此,最肯定的是由于它int被转换为浮点数。

>>> def func(n):
...    return n**4
... 
>>> def func1(n):
...    return n**4.0
... 
>>> from dis import dis
>>> dis(func)
  2           0 LOAD_FAST                0 (n)
              3 LOAD_CONST               1 (4)
              6 BINARY_POWER
              7 RETURN_VALUE
>>> dis(func1)
  2           0 LOAD_FAST                0 (n)
              3 LOAD_CONST               1 (4.0)
              6 BINARY_POWER
              7 RETURN_VALUE

更新:让我们看一下CPython源代码中的Objects / abstract.c

PyObject *
PyNumber_Power(PyObject *v, PyObject *w, PyObject *z)
{
    return ternary_op(v, w, z, NB_SLOT(nb_power), "** or pow()");
}

PyNumber_Power呼叫ternary_op,这太长了,无法粘贴到这里,因此这是链接

它调用的nb_power广告位xy作为参数传递。

最后,在Objects / floatobject.c的float_pow()第686行,我们看到在实际操作之前将参数转换为C :double

static PyObject *
float_pow(PyObject *v, PyObject *w, PyObject *z)
{
    double iv, iw, ix;
    int negate_result = 0;

    if ((PyObject *)z != Py_None) {
        PyErr_SetString(PyExc_TypeError, "pow() 3rd argument not "
            "allowed unless all arguments are integers");
        return NULL;
    }

    CONVERT_TO_DOUBLE(v, iv);
    CONVERT_TO_DOUBLE(w, iw);
    ...

If we look at the bytecode, we can see that the expressions are purely identical. The only difference is a type of a constant that will be an argument of BINARY_POWER. So it’s most certainly due to an int being converted to a floating point number down the line.

>>> def func(n):
...    return n**4
... 
>>> def func1(n):
...    return n**4.0
... 
>>> from dis import dis
>>> dis(func)
  2           0 LOAD_FAST                0 (n)
              3 LOAD_CONST               1 (4)
              6 BINARY_POWER
              7 RETURN_VALUE
>>> dis(func1)
  2           0 LOAD_FAST                0 (n)
              3 LOAD_CONST               1 (4.0)
              6 BINARY_POWER
              7 RETURN_VALUE

Update: let’s take a look at Objects/abstract.c in the CPython source code:

PyObject *
PyNumber_Power(PyObject *v, PyObject *w, PyObject *z)
{
    return ternary_op(v, w, z, NB_SLOT(nb_power), "** or pow()");
}

PyNumber_Power calls ternary_op, which is too long to paste here, so here’s the link.

It calls the nb_power slot of x, passing y as an argument.

Finally, in float_pow() at line 686 of Objects/floatobject.c we see that arguments are converted to a C double right before the actual operation:

static PyObject *
float_pow(PyObject *v, PyObject *w, PyObject *z)
{
    double iv, iw, ix;
    int negate_result = 0;

    if ((PyObject *)z != Py_None) {
        PyErr_SetString(PyExc_TypeError, "pow() 3rd argument not "
            "allowed unless all arguments are integers");
        return NULL;
    }

    CONVERT_TO_DOUBLE(v, iv);
    CONVERT_TO_DOUBLE(w, iw);
    ...

回答 2

因为一个是正确的,所以另一个是近似值。

>>> 334453647687345435634784453567231654765 ** 4.0
1.2512490121794596e+154
>>> 334453647687345435634784453567231654765 ** 4
125124901217945966595797084130108863452053981325370920366144
719991392270482919860036990488994139314813986665699000071678
41534843695972182197917378267300625

Because one is correct, another is approximation.

>>> 334453647687345435634784453567231654765 ** 4.0
1.2512490121794596e+154
>>> 334453647687345435634784453567231654765 ** 4
125124901217945966595797084130108863452053981325370920366144
719991392270482919860036990488994139314813986665699000071678
41534843695972182197917378267300625