所以我有一个类,定义了几个方法:
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 中的字段我会是能够在给定时间访问实例的那些字段。 对于哪种方式更好有共识吗?
答案 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 并且识别器不知道任何状态更改,那么只需从函数返回它就可以了。