我不了解这种python __del__行为

如何解决我不了解这种python __del__行为

| 有人可以解释为什么以下代码会如此行为:
import types

class Dummy():
    def __init__(self,name):
        self.name = name
    def __del__(self):
        print \"delete\",self.name

d1 = Dummy(\"d1\")
del d1
d1 = None
print \"after d1\"

d2 = Dummy(\"d2\")
def func(self):
    print \"func called\"
d2.func = types.MethodType(func,d2)
d2.func()
del d2
d2 = None
print \"after d2\"

d3 = Dummy(\"d3\")
def func(self):
    print \"func called\"
d3.func = types.MethodType(func,d3)
d3.func()
d3.func = None
del d3
d3 = None
print \"after d3\"
输出(请注意,永远不会调用d2的析构函数)是(python 2.7)
delete d1
after d1
func called
after d2
func called
delete d3
after d3
有没有一种方法可以“修复”代码,以便在不删除添加的方法的情况下调用析构函数?我的意思是,放置d2.func = None的最佳位置将在析构函数中! 谢谢 [编辑]根据前几个答案,我想澄清一下,我并不是在询问使用
__del__
的优点(或不足之处)。我试图创建最短的函数来证明我认为是非直觉的行为。我假设已经创建了循环引用,但是我不确定为什么。如果可能的话,我想知道如何避免使用循环引用。     

解决方法

您不能假设会调用
__del__
-它不是一个希望自动分配资源的地方。如果要确保释放(非内存)资源,则应使用
release()
或类似方法,然后显式调用它(或在Thanatos在下面的注释中指出的上下文管理器中使用它)。 至少您应该非常仔细地阅读
__del__
文档,然后您可能不应该尝试使用
__del__
。 (有关
__del__
的其他不良信息,请参阅
gc.garbage
文档)     ,我提供自己的答案是因为,尽管我喜欢避免使用
__del__
的建议,但我的问题是如何使它在所提供的代码示例中正常工作。 简短版本:以下代码使用ѭ10来避免循环引用。我以为在发布问题之前已经尝试过此操作,但是我想我一定做错了什么。
import types,weakref

class Dummy():
    def __init__(self,name):
        self.name = name
    def __del__(self):
        print \"delete\",self.name

d2 = Dummy(\"d2\")
def func(self):
    print \"func called\"
d2.func = types.MethodType(func,weakref.ref(d2)) #This works
#d2.func = func.__get__(weakref.ref(d2),Dummy) #This works too
d2.func()
del d2
d2 = None
print \"after d2\"
较长版本: 当我发布问题时,我确实搜索了类似的问题。我知道您可以改用
with
,而普遍的看法是
__del__
不好。 使用
with
是有意义的,但仅在某些情况下才适用。打开文件,读取文件并将其关闭是一个很好的示例,其中ѭ12是一个很好的解决方案。您已经找到了需要对象的特定代码块,并且想要清理对象和代码块的结尾。 似乎经常使用数据库连接作为示例,但在使用t12 doesn时效果不佳,因为您通常需要离开创建连接的代码部分,并以更多的事件驱动(而不是顺序驱动)关闭连接) 大体时间。 如果
with
不是正确的解决方案,我会看到两种选择: 您确保
__del__
可以正常工作(有关详情,请参见此博客 描述的用法) 程序关闭时,使用
atexit
模块运行回调。例如,请参阅本主题。 当我尝试提供简化的代码时,我的真正问题更多是由事件驱动的,因此
with
不是一个合适的解决方案(
with
适合简化代码)。我也想避免使用ѭ19,因为我的程序可以长时间运行,并且希望能够尽快执行清理。 因此,在这种特定情况下,我发现它是使用ѭ10并防止会阻止ѭ2起作用的循环引用的最佳解决方案。 这可能是该规则的例外,但是在某些情况下,使用HO10ѭ和
__del__
是正确的实现,恕我直言。     ,您可以使用
with
运算符代替del。 http://effbot.org/zone/python-with-statement.htm 就像文件类型对象一样,您可以
with Dummy(\'d1\') as d:
    #stuff
#d is guaranteed to be out of scope
    ,
del
不叫
__del__
you29ѭ使用的方式将删除局部变量。销毁对象时会调用“ 2”。 Python作为一种语言无法保证何时销毁对象。 CPython是Python最常见的实现,它使用引用计数。因此,del通常会按您期望的那样工作。但是,如果您有参考循环,则该功能将无效。
d3 -> d3.func -> d3
Python无法检测到该错误,因此不会立即对其进行清理。而且它不只是参考周期。如果引发异常,则可能仍要调用析构函数。但是,Python通常会保留局部变量作为其回溯的一部分。 解决方案不取决于
__del__
方法。而是使用上下文管理器。
class Dummy:
   def __enter__(self):
       return self

   def __exit__(self,type,value,traceback):
       print \"Destroying\",self

with Dummy() as dummy:
    # Do whatever you want with dummy in here
# __exit__ will be called before you get here
这样可以保证正常工作,您甚至可以检查参数以查看是否正在处理异常并在这种情况下执行其他操作。     ,上下文管理器的完整示例。
class Dummy(object):
    def __init__(self,name):
        self.name = name
    def __enter__(self):
        return self
    def __exit__(self,exct_type,exce_value,traceback):
        print \'cleanup:\',d
    def __repr__(self):
        return \'Dummy(%r)\' % (self.name,)

with Dummy(\"foo\") as d:
    print \'using:\',d

print \'later:\',d
    ,在我看来,这件事的真正核心是在这里:   添加功能是动态的(在运行时),并且事先未知 我感觉到,您真正追求的是一种将不同功能绑定到表示程序状态的对象(也称为多态)的灵活方法。 Python做到的很好,不是通过附加/分离方法,而是通过实例化不同的类。我建议您再看一下班级组织。也许您需要将核心的持久数据对象与瞬态对象分开。使用has-a模式而不是is-a:每次状态更改时,您要么将核心数据包装在状态对象中,要么将新的状态对象分配给核心的属性。 如果确定不能使用这种pythonic OOP,则仍然可以通过定义类中的所有函数并随后将它们绑定到其他实例属性来另一种方法来解决问题(除非您\正在从用户输入中即时编译这些功能):
class LongRunning(object):
    def bark_loudly(self):
        print(\"WOOF WOOF\")

    def bark_softly(self):
        print(\"woof woof\")


while True:
    d = LongRunning()
    d.bark = d.bark_loudly
    d.bark()

    d.bark = d.bark_softly
    d.bark()
    ,使用
weakref
的另一种解决方案是仅在通过在类上重写
__getattr__
或ѭ40to来返回
func.__get__(self,type(self))
而不是仅将
func
绑定到实例的函数时,才将函数动态绑定到实例。这就是在类上定义的函数的行为方式。不幸的是(对于某些用例),python对实例本身附加的功能执行的逻辑不同,但是您可以对其进行修改以执行此操作。我对绑定到实例的描述符也遇到了类似的问题。这里的性能可能不如使用
weakref
好,但是它是一个选项,该选项对于仅使用python内置函数的任何动态分配函数都可以透明地工作。 如果您发现自己经常这样做,则可能需要一个自定义元类,它可以对实例级功能进行动态绑定。 另一种选择是将函数直接添加到类中,该类将在调用时正确执行绑定。在很多用例中,这将涉及一些麻烦:即,正确命名函数的空间,以免它们冲突。不过,实例ID可以用于此目的,因为不能保证cPython中的ID在程序的整个生命周期内都是唯一的,因此您需要仔细考虑一下以确保它适用于您的用例。特别是,当对象超出范围时,您可能需要确保删除类函数,因此其ID /内存地址可以再次使用。
__del__
非常适合:)。另外,您可以清除对象创建时命名为实例的所有方法(在
__init__
__new__
中)。 另一个选择(而不是弄乱python magic方法)是显式添加一种用于调用动态绑定函数的方法。缺点是您的用户无法使用常规python语法调用您的函数:
class MyClass(object):
    def dynamic_func(self,func_name):
        return getattr(self,func_name).__get__(self,type(self))

    def call_dynamic_func(self,func_name,*args,**kwargs):
        return getattr(self,type(self))(*args,**kwargs)

    \"\"\"
    Alternate without using descriptor functionality:
    def call_dynamic_func(self,func_name)(self,**kwargs)
    \"\"\"
为了使这篇文章更完整,我还将展示您的
weakref
选项:
import weakref
inst = MyClass()
def func(self):
    print \'My func\'
#  You could also use the types modules,but the descriptor method is cleaner IMO
inst.func = func.__get__(weakref.ref(inst),type(inst))
    

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


依赖报错 idea导入项目后依赖报错,解决方案:https://blog.csdn.net/weixin_42420249/article/details/81191861 依赖版本报错:更换其他版本 无法下载依赖可参考:https://blog.csdn.net/weixin_42628809/a
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下 2021-12-03 13:33:33.927 ERROR 7228 [ main] o.s.b.d.LoggingFailureAnalysisReporter : *************************** APPL
错误1:gradle项目控制台输出为乱码 # 解决方案:https://blog.csdn.net/weixin_43501566/article/details/112482302 # 在gradle-wrapper.properties 添加以下内容 org.gradle.jvmargs=-Df
错误还原:在查询的过程中,传入的workType为0时,该条件不起作用 <select id="xxx"> SELECT di.id, di.name, di.work_type, di.updated... <where> <if test=&qu
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct redisServer’没有名为‘server_cpulist’的成员 redisSetCpuAffinity(server.server_cpulist); ^ server.c: 在函数‘hasActiveC
解决方案1 1、改项目中.idea/workspace.xml配置文件,增加dynamic.classpath参数 2、搜索PropertiesComponent,添加如下 <property name="dynamic.classpath" value="tru
删除根组件app.vue中的默认代码后报错:Module Error (from ./node_modules/eslint-loader/index.js): 解决方案:关闭ESlint代码检测,在项目根目录创建vue.config.js,在文件中添加 module.exports = { lin
查看spark默认的python版本 [root@master day27]# pyspark /home/software/spark-2.3.4-bin-hadoop2.7/conf/spark-env.sh: line 2: /usr/local/hadoop/bin/hadoop: No s
使用本地python环境可以成功执行 import pandas as pd import matplotlib.pyplot as plt # 设置字体 plt.rcParams['font.sans-serif'] = ['SimHei'] # 能正确显示负号 p
错误1:Request method ‘DELETE‘ not supported 错误还原:controller层有一个接口,访问该接口时报错:Request method ‘DELETE‘ not supported 错误原因:没有接收到前端传入的参数,修改为如下 参考 错误2:cannot r
错误1:启动docker镜像时报错:Error response from daemon: driver failed programming external connectivity on endpoint quirky_allen 解决方法:重启docker -> systemctl r
错误1:private field ‘xxx‘ is never assigned 按Altʾnter快捷键,选择第2项 参考:https://blog.csdn.net/shi_hong_fei_hei/article/details/88814070 错误2:启动时报错,不能找到主启动类 #
报错如下,通过源不能下载,最后警告pip需升级版本 Requirement already satisfied: pip in c:\users\ychen\appdata\local\programs\python\python310\lib\site-packages (22.0.4) Coll
错误1:maven打包报错 错误还原:使用maven打包项目时报错如下 [ERROR] Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.2.0:resources (default-resources)
错误1:服务调用时报错 服务消费者模块assess通过openFeign调用服务提供者模块hires 如下为服务提供者模块hires的控制层接口 @RestController @RequestMapping("/hires") public class FeignControl
错误1:运行项目后报如下错误 解决方案 报错2:Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project sb 解决方案:在pom.
参考 错误原因 过滤器或拦截器在生效时,redisTemplate还没有注入 解决方案:在注入容器时就生效 @Component //项目运行时就注入Spring容器 public class RedisBean { @Resource private RedisTemplate<String
使用vite构建项目报错 C:\Users\ychen\work>npm init @vitejs/app @vitejs/create-app is deprecated, use npm init vite instead C:\Users\ychen\AppData\Local\npm-