首先定义一个类,作用是获取代理
class Proxies:
def __init__(self):
self.bin_file = Path(__name__).resolve().parent / 'deque.bin'
if self.bin_file.exists():
with open(self.bin_file.as_posix(), 'rb') as fpr:
self.qproxy = pickle.load(fpr)
print('deque loaded')
else:
self.qproxy = deque()
print('deque created')
self.get_ip = "API"
self.get_proxies()
def __del__(self):
with open(self.bin_file.as_posix(), 'wb') as fpw:
pickle.dump(self.qproxy, fpw)
print('deque dumped')
def get_proxies(self):
logging.info("Getting proxies from API.")
with HTMLSession() as ip_session:
with ip_session.get(self.get_ip) as resp_ip:
resp_ip2 = resp_ip.text.split('\r\n')
proxies_http = [{"http": "http://{}".format(i), "cd": 3
} for i in resp_ip2 if i is not None]
proxies_https = [{"https": "https://{}".format(i), "cd": 3
} for i in resp_ip2 if i is not None]
[self.put(p) for p in proxies_http]
[self.put(ps) for ps in proxies_https]
logging.info("Proxies get.")
def get(self):
if len(self.qproxy) <= 1:
self.get_proxies()
return self.qproxy.popleft()
def put(self, p):
self.qproxy.append(p)
return None
@property
def size(self):
return len(self.qproxy)
在此之前我已经创建一个实例 p = Proxies() 然后打印出了 deque created 没说明 bin 文件已经被创建,创建后实例获取了 40 个代理 然后执行 del p 然而此时 deque dumped 没有被打印出来,也就是析构函数没有被执行
然后从新创建一个实例 p = Proxies() 输出 deque loaded deque dumped
deque dumped 在此时打印了出来,这是为什么? 此时 p.size 为 40,这是正确的
然后手动获取代理 p.get_proxies() 输出 deque dumped 此时为什么又输出了析构函数中的 print ?
p.size 输出 80,这个也是正确的
del p 输出 deque dumped 此时析构函数又正常执行了。。。。
python 版本 3.7
谁能给分析分析是为什么吗
1
owenliang 2018-10-30 12:55:45 +08:00
描述没怎么看懂,你贴一段调用的完整复现代码看看?
|
2
no1xsyzy 2018-10-30 13:11:18 +08:00
你是在 REPL 里执行后面这些的吗?
REPL 会把最后一次非 None 返回值保存到 `_` 上。 |
3
kindjeff 2018-10-30 13:14:35 +08:00 via iPhone
垃圾回收的时候才会触发__del__,换句话说,你没法保证什么时候__del__被触发,所以不要把有用的逻辑写在__del__里。
|
4
princelai OP @no1xsyzy 是的,在 ipython 里,我看流畅的 python 确实说过_为 None 的时候才会执行析构,但是之后也是同样的操作,也没有输出 None,为什么又正常了?而且我在第二次执行 p.get_proxies() 时,这里也出现了一次执行析构函数
|
6
princelai OP @owenliang 大致意思就是两次执行了创建实例和手动销毁实例,第一次的销毁没有触发析构,第二次手动销毁正确的触发了析构,但是多出了一次莫名其妙的执行析构函数
|
7
owenliang 2018-10-30 13:29:02 +08:00
流程还是没怎么理解。
但是对于这种准确回收的问题,可以了解一下 weakref 观察者模式,当程序中不再持有对象的强引用之后可以 callback 通知到观察者,这种实现应该比__del__靠谱。 |
8
owenliang 2018-10-30 13:29:15 +08:00
|
9
princelai OP @owenliang 谢谢,我本来是想让__del__自动帮我执行 pickle,没想到这么不靠谱,还是写个函数手动调用吧
|
10
no1xsyzy 2018-10-30 13:46:31 +08:00 1
@princelai #4 你输出 p.size 的时候覆盖了原本的 _,在第二个 del 前你可以看一眼 _,应该是 80,int 是个 immutable。看上去将同一块析了两次,iPython 还会以奇怪的方式保留引用。
#5 del 并不能触发,那个只是解引用(从 top frame 中删去 p )。 #6 析构的触发没有保证,和具体实现有关。 > It is implementation-dependent whether __del__() is called a second time when a resurrected object is about to be destroyed; the current CPython implementation only calls it once. > It is not guaranteed that __del__() methods are called for objects that still exist when the interpreter exits. |
11
princelai OP # 第一次执行
In[67]: p = Proxies() deque created <---这个是 print 出来的 In[68]: p.size Out[69]: 40 In[70]: p.get() Out[71]: {'http': 'http://110.74.199.68:47218', 'cd': 3} In[72]: del p <---这里没输出任何东西 # 第二次执行 In[78]: p = Proxies() deque loaded deque dumped <---怀疑这里是第一次的__del__执行,但是注意是在 loaded 后面输出的 In[79]: p.size Out[79]: 40 <---如果是先 loaded 再执行第一次的 dumped,这里应该是 0,而不是 40 In[81]: p.get_proxies() deque dumped <---这里不知道为什么又有一次 In[82]: p.size Out[82]: 80 In[83]: del p deque dumped <---这里正确执行了__del__ |
12
owenliang 2018-10-30 13:51:56 +08:00
有点奇怪,怎么会多析构一次。。
|
13
princelai OP @no1xsyzy 谢谢了,可能真的和 ipython 也有关系。另外想问一下,只要在整个程序完全结束前,__del__能被执行就可以了,这样__del__能保证肯定会被执行一次吗?
|
15
kuroismith 2018-11-04 17:33:54 +08:00
所以一定要用 __del__ 的理由是?
想要序列化的话写一个方法显示调用不好吗 |
16
princelai OP @kuroismith 程序退出自动序列化,但是事实证明不可行,只能手动调用了
|