【问题标题】:Is it a good practice to have an intensive __init__ method in a python class?在 python 类中使用密集的 __init__ 方法是一种好习惯吗?
【发布时间】:2020-02-05 07:43:32
【问题描述】:

我是 python 类的新手。我正在编写一个将 id 作为参数的类,但是它必须计算另一个依赖于 id 的属性,并且计算可能很激烈。

我的方法是在初始化阶段计算所有内容,尽管这需要时间,为了节省时间,将之前计算的所有内容保存在腌制字典中,如下所示。

import pickle

def intensecomputation(id):
    # Compute otherattr, based on id
    ...
    ...
    return(otherattr)

class myclass:
    def __init__(self, id):
        self.id = id
        # Need to compute self.otherattr that depends on self.id
        # Check if I have computed that already
        mydict = pickle.load( open( "mydict.p", "rb" ) )
        if self.id in mydict:
            self.otherattr = mydict[self.id]
        else:
            self.otherattr = intensecomputation(id)
            # Save for later
            mydict[self.id] = self.otherattr
            pickle.dump( mydict, open( "mydict.p", "wb" ) )

myobject = myclass(10)
# Wait some time here (unless the id 10 is already precalculated in the past and is in the pickled dictionary)
print(myobject.id)
print(myobject.otherattr)

我正在做的事情是一个好习惯吗? __init__ 有什么理由不应该复杂和激烈?我在想,如果是这种情况,那么我可以将intensecomputation 实现为myclass 的一个方法并调用它来填充self.otherattr,如下所示:

myobject = myclass(10)
# myobject.otherattr is empty
print(myobject.id)
myobject.intensecomputation()
# Now myobject.otherattr is created
print(myobject.otherattr)

无论如何,鉴于我的情况,如果有人能向我解释实施myclass 的最佳实践,我将不胜感激。

【问题讨论】:

  • 这真的取决于计算是什么以及你的类代表什么。关键决定并不是真的“我是否在__init__ 中进行此计算”;诸如“我的对象是否代表其他事物的一部分”和“我的对象实际上是否应该代表其他事物”之类的决定更为重要。
  • 不过,拥有一个计算成本高昂的 __init__ 本身并没有什么坏处。
  • 我个人不会将文件 i/o 放入 __init__ 并且我可能会将 otherattr 设为属性,这是在您第一次访问文件时从文件中计算/读取的。

标签: python class initialization


【解决方案1】:

涉及的 init 方法没有任何问题,特别是如果您尝试不遵循 RAII 样式,其中对象构造是初始化,这通常是一个好习惯。这样你就知道对象在构造时是有效的(如果这很重要),而不是在你不认为它们很昂贵的时候执行潜在的昂贵操作。

pickle 的想法很好,但是我建议您将 mydict 设为一个类变量,如果它适合在初始化之间共享,或者它会在每次 init 调用后超出范围并且 mydict.p 没有改变.我可能在这里做的唯一改变是让你自己的初始化.otherattr 成为一个函数。并改变结构,这样你总是做同样的最后一块

#snippet in your init

if myclass.mydict.empty() :
  ## load from the pickle so its shared for all class instantiations OR perhaps pull this out and make it part of class's init. otherwise every construction is reloading the pickle which could be as costly or more costly then your calculation itself. (File IO is slow) 

do_dump = false # track whether I make changes that require a pickle dump update
if self.id !not in mydict:
  mydict[self.id] = intensecomputation(id) # calc and memoize at once
  do_dump = true;
  pass

# now this is universal
self.otherattr = mydict[self.id] 

#other stuff... which might also need to be pickled

#before end
#re-pickle if important; ensuring to do so potentially via a try catch finally wrapper.

提到的另一种可能性是在 getter 周围为 otherattr 成员使用装饰器,该成员在使用时返回或计算并记住结果。好处将真正归结为数据的使用方式。如果您稍后在某种管道中使用许多myclass 实例进行大量批处理,那么如果值准备好可以这么说,它会执行得更好,因此计算您拥有的任何管道的指令缓存可以保持热而不是每隔几次就必须中断管道并计算此值的错过分支预测。

【讨论】:

    猜你喜欢
    • 2017-06-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-11-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多