Python 2.7 中的对象实例化极其缓慢
Extremely slow object instantiation in Python 2.7
我最近不得不完成一项使用大量坐标操作的作业。为了节省时间和简化我的代码,我定义了一个 class 来封装坐标对的行为。 class 看起来像这样:
class Vector (tuple) :
def __init__ (self, value) :
tuple.__init__ (self, value)
def __add__ (self, other) :
return Vector ((self [0] + other [0], self [1] + other [1]))
这让我可以编写这样的代码(例如):
def translate (pointList, displacement) :
return [point + displacement for point in pointList]
但是我的应用程序非常慢。比其他任务慢得多。我在算法的实现中找不到任何低效的地方,所以我做了一个简单的测试来查看 Vector class 的开销是多少。我预计在 5% 到 15% 之间。
我对 Vector class 的测试如下所示:
v = Vector ((0, 0))
d = Vector ((1, -1))
loopIdx = 3000000
while loopIdx > 0 :
v = v + d
loopIdx -= 1
print (v)
这 运行 秒(通常)在这种时间:
real 0m8.440s
user 0m8.367s
sys 0m0.016s
为了比较我运行这段代码:
v = (0, 0)
dX = 1
dY = -1
loopIdx = 3000000
while loopIdx > 0 :
v = ( v [0] + dX, v [1] + dY )
loopIdx -= 1
print (v)
运行 此代码的时间是:
real 0m1.004s
user 0m0.995s
sys 0m0.006s
我是不是做错了什么,或者在 Python 中使用 class 对象是否真的意味着您的应用程序将花费 运行 的 8 倍?
不是真正的答案如何让你 class 更快,而是更多的选择。
而不是 subclassing tuple
并自己编写所有这些 add
、sub
等方法,你只需要使用 Python's bultin complex
二维坐标的数字类型,它已经内置了所有这些操作,正确且超快。
>>> %timeit vector_no_init()
1 loops, best of 3: 1.39 s per loop
>>> %timeit plain_tuple()
1 loops, best of 3: 403 ms per loop
>>> %timeit complex_nums()
1 loops, best of 3: 171 ms per loop
对于旋转,您可以使用复数乘法:只需将您的复数坐标乘以一个复数,该复数的极坐标形式绝对值为 1,相位等于您要旋转的角度。
要旋转 90 度,只需乘以 1j
(逆时针)或 -1j
(顺时针)即可。对于所有其他角度,使用 cmath
模块来转换极坐标形式。
>>> c = complex(4, 2)
>>> c * cmath.rect(1, math.radians(45))
(1.4142135623730954+4.242640687119285j)
但是,我建议 不要 subclass complex
使 rotate
成为 class 的方法,因为在这种情况下,您还必须覆盖所有其他方法,例如 add
,否则加法的结果将是一个常规复数,而不提供 rotate
方法。这将抵消所有这些性能提升,使其与您的 Vector
class 一样慢。相反,只需创建一个函数 rotate(complex, angle) -> complex
.
我最近不得不完成一项使用大量坐标操作的作业。为了节省时间和简化我的代码,我定义了一个 class 来封装坐标对的行为。 class 看起来像这样:
class Vector (tuple) :
def __init__ (self, value) :
tuple.__init__ (self, value)
def __add__ (self, other) :
return Vector ((self [0] + other [0], self [1] + other [1]))
这让我可以编写这样的代码(例如):
def translate (pointList, displacement) :
return [point + displacement for point in pointList]
但是我的应用程序非常慢。比其他任务慢得多。我在算法的实现中找不到任何低效的地方,所以我做了一个简单的测试来查看 Vector class 的开销是多少。我预计在 5% 到 15% 之间。
我对 Vector class 的测试如下所示:
v = Vector ((0, 0))
d = Vector ((1, -1))
loopIdx = 3000000
while loopIdx > 0 :
v = v + d
loopIdx -= 1
print (v)
这 运行 秒(通常)在这种时间:
real 0m8.440s
user 0m8.367s
sys 0m0.016s
为了比较我运行这段代码:
v = (0, 0)
dX = 1
dY = -1
loopIdx = 3000000
while loopIdx > 0 :
v = ( v [0] + dX, v [1] + dY )
loopIdx -= 1
print (v)
运行 此代码的时间是:
real 0m1.004s
user 0m0.995s
sys 0m0.006s
我是不是做错了什么,或者在 Python 中使用 class 对象是否真的意味着您的应用程序将花费 运行 的 8 倍?
不是真正的答案如何让你 class 更快,而是更多的选择。
而不是 subclassing tuple
并自己编写所有这些 add
、sub
等方法,你只需要使用 Python's bultin complex
二维坐标的数字类型,它已经内置了所有这些操作,正确且超快。
>>> %timeit vector_no_init()
1 loops, best of 3: 1.39 s per loop
>>> %timeit plain_tuple()
1 loops, best of 3: 403 ms per loop
>>> %timeit complex_nums()
1 loops, best of 3: 171 ms per loop
对于旋转,您可以使用复数乘法:只需将您的复数坐标乘以一个复数,该复数的极坐标形式绝对值为 1,相位等于您要旋转的角度。
要旋转 90 度,只需乘以 1j
(逆时针)或 -1j
(顺时针)即可。对于所有其他角度,使用 cmath
模块来转换极坐标形式。
>>> c = complex(4, 2)
>>> c * cmath.rect(1, math.radians(45))
(1.4142135623730954+4.242640687119285j)
但是,我建议 不要 subclass complex
使 rotate
成为 class 的方法,因为在这种情况下,您还必须覆盖所有其他方法,例如 add
,否则加法的结果将是一个常规复数,而不提供 rotate
方法。这将抵消所有这些性能提升,使其与您的 Vector
class 一样慢。相反,只需创建一个函数 rotate(complex, angle) -> complex
.