像swift或objective-c中那样使用继承在python中实现委派安全吗



来自Objective-C&Swift我在Python中实现了以下模式,以便在Python中使用基于协议的委派,就像我在Swift或Obj-C:中那样

class DataReceiverDelegate:
def data_receiver_did_receive_data(self, data):
pass
def data_receiver_did_update_settings(self):
pass
class DataReceiver
def __init__(self, delegate = None):
self.delegate = delegate
def received_data(self, data):
self.delegate.data_receiver_did_receive_data(data)

这就是我使用它的方式:

应该充当委托的类只是从DataReceiverDelegate类继承而来。

class DataParser(object, DataReceiverDelegate):
def __init__(self):
self.data_receiver = DataReceiver(self)
# or -> self.data_receiver.delegate = self
def data_receiver_did_receive_data(self, data):
print data

这一点没有问题,而且与Swift非常相似,协议也是类(或结构,不太确定)。

我担心在Python中实现这样的委派会导致奇怪的问题,或者是绝对不可能的。那么,这样处理代表团是否安全?

由于我目前对Python中的内存处理一无所知,我不确定这是否会因为"强引用循环"而导致死锁。那么,有没有一种方法可以定义像"弱"类型的东西呢?

唯一的问题是,委托给(本例中为DataReceiver)的类不知道self.delegate是什么类型,因此编辑器中的代码完成不起作用。

编辑:

这是一个简单的例子,只是为了展示我追求的东西。在现实世界中,可以添加检查以确保委托不是None。与Swift中的"安全解锁期权"非常相似。

您所做的看起来不错。您甚至可以省略DataReceiverDelegate类,因为方法只能在运行时找到,或者您可以将其作为一个抽象基类来进行一些额外的安全性和静态分析。

Python2.7可以在其垃圾收集中处理引用周期,尽管周期越少可能越好。如果你担心的话,你也可以查看薄弱的参考资料。

PyCharm中的类型提示可以指示self.delegate的类型

我也在做同样的事情,因为我在swift库中看到了这些例子,我认为这种模式真的可以帮助我。目前,我还担心强引用中的内存泄漏。但我在这里看到,python中的这些类型的链接被称为循环引用

。。。它创建了一个类B的实例,该实例将自身传递给类A,然后类A设置对类B的引用并产生循环引用。

这里有几乎相同的循环引用

对于是否必须手动处理循环引用,我并没有找到一个明确的解释,所以我决定提供一个实验。

这是我试图适应你的例子的实验(只是更改了类名:)

from memory_profiler import profile

class DataReceiver:
def __init__(self, delegate):
self.workload = " " * 128 * 1024 * 1024 # this is 'memory weight'
self.delegate = delegate

class DataParser:
def __init__(self):
self.data_receiver = DataReceiver(self)

@profile
def monitor():
for _ in range(60):
parser = DataParser()
print("monitoring at the last point")

if __name__ == "__main__":
monitor()

输出:

Line #    Mem usage    Increment   Line Contents
================================================
74     64.0 MiB     64.0 MiB   @profile
75                             def monitor():
76   3557.1 MiB      0.0 MiB       for _ in range(60):
77   3557.1 MiB    128.0 MiB           parser = DataParser()
78                             
79   3515.4 MiB      0.0 MiB       print("monitoring at the last point")

似乎在60次迭代之后,它消耗了大约3.5GB的内存,并且无法摆脱这些强引用。

之后,我们做了完全相同的事情,但使用weakref模块来实现弱引用:

import weakref
...
class DataReceiver:
def __init__(self, delegate):
self.workload = " " * 128 * 1024 * 1024
self.delegate = weakref.ref(delegate) # only here is difference: delegate is weak reference
...

输出:

Line #    Mem usage    Increment   Line Contents
================================================
73     64.1 MiB     64.1 MiB   @profile
74                             def monitor():
75    192.1 MiB      0.0 MiB       for _ in range(60):
76    192.1 MiB    128.0 MiB           parser = DataParser()
77                             
78    192.1 MiB      0.0 MiB       print("monitoring at the last point")

现在看来,在每次迭代之后,垃圾收集器能够在每次迭代后删除本地实例,因为不再有强引用了。

同样重要的是:

你再也不能这样调用你的方法了:

receiver.delegate.method_name_bluh_bluh()

因为weakref.ref是一个可调用的类型,并且要获得引用的对象,您需要调用ref属性:

receiver.delegate().method_name_bluh_bluh()

你可以这样做:

class DataReceiver:
def __init__(self, delegate):
self.workload = " " * 128 * 1024 * 1024
self._delegate = weakref.ref(delegate)
@property
def delegate(self):
return self._delegate()

所以现在你可以做:

receiver.delegate.method_name_bluh_bluh()

最新更新