Python: performance issue implemenating nested classes with staticmethod
请检查下面的示例工作代码:我们使用混合staticmethods实现nested classes。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| import time
class A:
def __init__(self):
time.sleep(0.1)
def a1(self):
print 'a1'
@staticmethod
def a2():
print 'a2'
class B:
@staticmethod
def b2():
A.a2() #fine.
A().a1() #very bad solution, computation cost is high !!!
@staticmethod
def x():
t = time.clock()
for i in xrange(100):
A.B.b2()
print 'elapsed: %0.1fs'%(time.clock()-t) |
用作:
上述方法是可行的,但正如您可能注意到的,在A().a1()行中,我们试图从容器类A访问非静态方法a1,计算成本太高。我们通过在A初始化中使用sleep来强调这一点。因此,您得到了这一点,例如,它可以是我们工作中所必需的任何耗时的初始化。因此,我们认为在上述行中实例化类A不是一个好的选择。如何不在上面例示A,而是像上面那样做。尽管相关问题包括这个和那个,但上面的问题已经在任何地方得到了解答。
编辑:我们对诸如why use it等建议不感兴趣。上面明确指出的我们感兴趣的问题是如何改进这一行
这就是全部。
根据Martijn Pieters给出的建议,按以下方式解决。我们之所以接受这个答案,是因为能说明解决问题的方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| class B:
@staticmethod
def b2(Ai):
A.a2() #fine.
if Ai is None: Ai = A()
Ai.a1() #now is fine.
@staticmethod
def x():
t = time.clock()
Ai = A()
for i in xrange(100):
A.B.b2(Ai=Ai)
print 'elapsed: %0.1fs'%(time.clock()-t) |
谢谢您。
- 对不起,什么?您想在不通知A的情况下执行需要A实例的代码吗?
- 我完全不清楚您在问什么,但是嵌套静态方法的行为与非嵌套静态方法没有任何不同。没有额外的性能惩罚(除了属性查找,这并不完全昂贵)会使情况变得异常。
- @MartijnPieers注意到,在上面的代码中,我们正在寻找一种解决方案,不只是为了访问方法a1,而实例化A百万次。应该只有一个实例才能做到这一点。
- 如果A初始化需要很长时间,那么只需执行一次,即缓存实例。您可以使用一个非常简单的__new__方法来完成此操作。
- 这只是一个缓存问题;与静态方法或嵌套对象无关。
- 在您的问题中,您没有说明初始化对于a1方法并不重要,或者a1方法没有副作用,因此您可以使用A的单个实例。
- @MartijnPieers如果您运行上面的示例,即A.B.x(),您将看到这一点。首先,你可以评论行A().a1(),你会看到100次重复的反应是立即的。但是,如果您有一个正在创建的A的().a1()100实例,这对于上述目的来说是不必要的。
- @开发人员:我看到了代码的作用,我只是说,如果将问题应用到常规的全局函数中,那么它不会改变。您反复调用一个创建昂贵对象的函数。缓存那个昂贵对象的创建,问题就消失了。
- @开发人员:换句话说,您的问题与使用三个全局函数x()、a2()和b2()有什么不同,它们的代码完全相同,但不是指全局名称?
- @马蒂·皮耶特还好。你是指使用__new__作为Bakuriu评论的cacheing还是其他建议?这是最佳解决方案吗?
- @开发者:这是一个"医生,当我按这里的时候,它会疼!"问题:根据这里的信息,我能给你的唯一答案是:"那就不要按那里!".如何缓存A()的实例化完全取决于在什么情况下可以缓存A()。
- @马提尼彼得斯看,你几乎没有解决办法!所以这个问题并不简单。在我们提到的上述问题中,我们希望保持原样,除了EDOCX1号线(10号线),我们认为应该有更好的方法。
您的问题与静态方法或嵌套类无关。全局函数也存在同样的问题:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| class A:
def __init__(self):
time.sleep(0.1)
def a1(self):
print 'a1'
def a2():
print 'a2'
def b2():
a2()
A().a1()
def x():
t = time.clock()
for i in xrange(100):
b2()
print 'elapsed: %0.1fs'%(time.clock()-t) |
调用x()仍然会导致在b2()之外的循环中创建昂贵的A()实例。
您需要设计一个缓存策略来创建A()。这里总结起来有太多的方法,当然在重用A()的实例时没有太多的信息,创建一个新实例是可以接受的。
如果您只想为x()中的循环重用A()的实例,那么将其作为可选参数传递:
1 2 3 4 5 6 7 8 9 10 11 12
| def x():
t = time.clock()
instance = A()
for i in xrange(100):
b2(instance)
print 'elapsed: %0.1fs'%(time.clock()-t)
def b2(instance=None):
a2()
if instance is None:
instance = A()
instance.a1() |
现在,该实例在函数x()的持续时间内被缓存。
- 恐怕这不是我们想要的。我们对更改代码的结构不感兴趣。你看,缺少的一点是没有容器结构。A.B.x()在哪里?
- 您已经知道,问题中的示例是为了演示我们的目的,而不是原始代码,它可能包含数千个方法和结构。看清要点。不允许更改结构。我们很想补充一下我们对上面提到的A().a1()行所知的不足。
- 我简化了情况以说明这与遏制无关。把它加回去。嵌套类的结构与您的问题无关。
- 您需要弄清楚何时可以重用A()的实例,然后再这样做;重用那些实例。不管您如何使用实例,嵌套结构和我简化的扁平结构都会遇到同样的问题。
- 现在我们可以解决这个问题了。我们的解决方案是在问题的末尾添加的。非常感谢。