在类中使用python中的__init__调用方法与使用字段之间的区别?

时间:2017-10-12 11:55:56

标签: python python-3.x oop

所以我有一个类,定义了几个方法:

class Recognizer(object):

    def __init__(self):
        self.image = None
        self.reduced_image = None

    def load_image(self, path):
        self.image = cv2.imread(path)
        return self.image

我想要添加第三个使用load_image()返回值的方法。我应该这样定义:

    def shrink_image(self):
        self.reduced_img = cv2.resize(self.image, (300, 300))
        return self.reduced_img

或者我应该这样定义:

    def shrink_image(self, path):
        reduced_img = cv2.resize(self.load_image(path), (300, 300))
        return reduced_img

这两者究竟有什么区别?我可以看到我可以从我在该类中声明的任何方法访问 init 中的字段,所以我想如果我更新 init 中的字段我会是能够在给定时间访问实例的那些字段。 对于哪种方式更好有共识吗?

2 个答案:

答案 0 :(得分:1)

在第二种方式中,变量的范围限定为shrink_image函数。

第一种方式是将变量限定为对象生命周期,并且设置self.reduced_img是方法的side-effect

只看到你的代码样本而没有看到客户端,第二种情况是“更好”,因为reduced_img没有在其他任何地方使用,并且不必将它绑定到实例。 def可能是一个用例,你需要坚持上一次self.reduced_img调用,使其成为必要的副作用。

一般来说,尽量减少副作用是非常有帮助的。有副作用,特别是那些改变状态的副作用可以使你的程序的推理更加困难。

当您拥有多个对象访问者时,尤其如此。

想象一下拥有第一个shrink_image,你发布了你的程序,你在一个调用shrink_object的程序的单个调用站点中有一个客户端,很容易。通话结束后,self.reduced_img将成为结果。

想象一下,在多个呼叫站点之间共享对象?它引入了时间耦合:您可能无法再对reduced_img是什么做出假设,并且在调用shrink_image之前访问它可能不再是None,因为那里可能是其他来电者!!!

将此与第二个缩小图像进行比较,调用者不再具有可变状态,并且更容易推断Recognizer个调用中shrink_image个实例的状态。

引入多个并发调用时,第一个例子确实发生了一些问题。它从难以理解并且可能在逻辑上不正确而成为同步和数据竞争问题。

如果没有并发呼叫者,这不会成为问题。但这是可能的,如果您在Web框架中使用此调用并且创建单个实例以在多个Web工作进程之间共享,则可以获得此隐式并发,并且可能可能受到竞争条件的影响:p

答案 1 :(得分:1)

  

这两者究竟有什么区别?

在Python中,带有签名__init__的函数是对象的构造函数,在通过()调用它时会隐式调用它,例如Recognizer()

术语“更好”是模糊的,因为在前一个示例中,您将图像保存为对象的属性,从而使对象更大。

但在第二个例子中,您只是从函数返回数据,供调用者使用。

所以这是一个背景和风格问题。

一个简单的经验法则是,如果您要在Recognizer对象的上下文中使用属性 reduced_img ,那么将它作为属性保存在对象上是理想的,可以通过自己访问。如果调用者只是使用 reduced_img 并且识别器不知道任何状态更改,那么只需从函数返回它就可以了。