Python对象生存期特征 [英] Python Object Lifetime Characteristics

查看:87
本文介绍了Python对象生存期特征的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

注意:如果您知道任何(未详细说明的)库代码都可以满足我的要求,请启发C/C ++程序员,我将接受它作为答案.

Note : if you know of any (non-elaborate) library code that does what I want please enlighten a C/C++ programmer, I'll accept that as answer.

我将全局变量设置为以下类的实例.目的是让我设置一些手动中断点,以便将一些快速且肮脏的printf样式的调试点放置在一个抓痒的蜘蛛中(我特别需要在满足某些条件时才能中断以调试解析器,其中有一些极为罕见的输入数据异常)-改编自.

I have a global variable set to an instance of the following class. It's purpose is to allow me to set some manual interruption points to place some quick and dirty printf style debug points in a scrapy spider (I specifically need to break when certain criteria are met to tune a parser, there are some extremely rare input data anomalies) -- Adapted from this.

Os是OS X 10.8.

Os is OS X 10.8.

import termios, fcntl, sys, os

class DebugWaitKeypress(object):
    def __init__(self):
        self.fd = sys.stdin.fileno()
        self.oldterm = termios.tcgetattr(self.fd)
        self.newattr = termios.tcgetattr(self.fd)
        self.newattr[3] = self.newattr[3] & ~termios.ICANON & ~termios.ECHO
        termios.tcsetattr(self.fd, termios.TCSANOW, self.newattr)

        self.oldflags = fcntl.fcntl(self.fd, fcntl.F_GETFL)
        fcntl.fcntl(self.fd, fcntl.F_SETFL, self.oldflags | os.O_NONBLOCK)

    def wait(self):
        sys.stdin.read(1)

    def __del__(self):
        print "called del"
        termios.tcsetattr(self.fd, termios.TCSAFLUSH, self.oldterm)
        fcntl.fcntl(self.fd, fcntl.F_SETFL, self.oldflags)

当我按Ctrl-C组合键并且进程正在结束时,出现以下异常:

When I press Ctrl-C and the process is unwinding I get the following exception :

Exception AttributeError: "'NoneType' object has no attribute 'tcsetattr'" in <bound method DebugWaitKeypress.__del__ of <hon.spiders.custom_debug.DebugWaitKeypress object at 0x108985e50>> ignored

我想念一些关于对象生命周期的机制吗?如何纠正这种情况. AFAIK任何类实例都应在导入代码之前销毁,不是吗?以声明/定义的相反顺序.

I'm missing something about the mechanics of object lifetimes I guess ? How do remedy the situation. AFAIK any class instances should be destroyed before the imported code does, no ? in reverse order of declaration/definition.

如果在进程退出后没有将终端弄坏,我将忽略此点

I would just ignore this if the terminal wasn't screwed up after the process exits :D

Delian对seth答案的评论使我明白,我需要使用类似C main()的函数,或使用任何其他作为主函数并在其中初始化上下文的函数/生成器.这样,当进程关闭时,将调用上下文管理器的__exit__方法.而且,我不必在每次wait()调用时都重新编程终端流.

Delian's comment on seth's answer led me to understand that I need to use a C main() like function, or any other function/generator which dominates as a root function and initialise the context there. This way When the process is going down the __exit__ method of the context manager will get called. And I won't have to reprogram the terminal stream on each wait() call.

尽管重新编程的成本可能无关紧要,但还是很高兴知道这些基本的C/C ++语义在python中是怎么回事.

Although the cost of the reprogramming is potentially immaterial, it is good to know how one would these essential C/C++ semantics in python.

修改2:

在与stdin混淆时,Twisted(使用不当使用)会失效.所以我不得不解决文件IO的问题.

Twisted (which scrapy uses) goes apeshit when messing with stdin. So I had to solve the problem with file IO.

推荐答案

长话短说:__del__对于此目的(和几乎所有其他目的;没有用;您应该忘记它的存在)是无用的.如果要确定性清除,请使用上下文管理器.

Long story short: __del__ is useless for this purpose (and pretty much any other purpose; you should probably forget it exists). If you want deterministic cleanup, use a context manager.

AFAIK任何类实例都应在导入代码之前销毁,不是吗?以声明/定义的相反顺序.

AFAIK any class instances should be destroyed before the imported code does, no ? in reverse order of declaration/definition.

那是C ++.算了吧. Python并不在乎这一点,实际上,它甚至根本不在乎执行此操作所需的大多数事情.整个Python语言都没有声明,模块级变量存储在本质上是无序的关联数组中.变量不存储对象,它们存储引用(不是 C ++引用,它们基本上是没有指针算术的指针)-对象在堆上,对变量,绑定一无所知,语句或语句顺序.

That's C++. Forget it. Python does not care about this, in fact it does not even care about most things which are requirements for doing it. There is no such thing as a declaration in the entire Python language, and module-level variables are stored in what is essentially an unordered associative array. Variables do not store objects, they store references (which are not C++ references, they're basically pointers without pointer arithmetic) -- objects are on the heap and don't know a thing about variables, bindings, statements, or order of statements.

此外,当对象被垃圾回收时,是否被gc处理,都是不确定的.由于引用计数的原因,您在CPython中获得了主要是确定性图片,但即使在那一秒,它也会在您拥有循环的那一刻掉下来.结果是__del__可以在任何时间点(包括模块的一半已被拆除时)调用或根本不调用.定义__del__的多个对象相互引用也很麻烦,尽管某些GC努力做正确的事.

Moreover, when an objects is garbage collected, and whether it is gc'd at all, is undefined. You get a mostly deterministic picture in CPython due to reference counting, but even there it falls down the second you have cycles. The consequence is that __del__ may be called at any point in time (including when half of the module is already torn down) or not at all. Multiple objects defining __del__ referencing each other are also trouble, although some GCs try hard to do the right thing.

最重要的是,您可以在运行__del__时承担很少的责任,因此您不能做太多事情.您将获得处置本应通过另一种方法清理但没有清理的资源的最后一枪,仅此而已.经验法则:从不依靠它来强制执行任何.

Bottom line is, you can assume very little at the time __del__ runs, so you can't do very much. You get a last shot at disposing resources that should have been cleaned up via another method but weren't, and that's pretty much it. Rule of thumb: Never rely on it for anything mandatory.

相反,创建一个上下文管理器,并通过 .您将获得确定性的清理,而不必担心对象的生存期.说实在的,因为对象生存期和资源生存期是两个完全不同的事物,并且仅缠在C ++中,因为这是在该环境中进行资源管理的最佳方法.在Python中,RAII不适用,而是这样的:

Instead, create a context manager and use it via with. You get deterministic cleanup, without worrying about object lifetime. Because, truth to be told, object lifetime and resource lifetime are two entirely different things, and only entangled in C++ because it's the best way to do resource management in that environment. In Python, RAII does not apply, instead we have this:

with <context manager> as var:
    # do something
# "context closed", whatever that means - for resources, usually cleanup

顺便说一句,您可以通过 contextlib (从您的版本中翻译过来的文字,可能包含错误或丑陋的内容):

By the way, you can define it far more conveniently via contextlib (quickly transliterated from your version, may contain errors or ugliness):

from contextlib import contextmanager


@contextmanager
def debug_wait_keypress():
    fd = sys.stdin.fileno()
    oldterm = termios.tcgetattr(fd)
    newattr = termios.tcgetattr(fd)
    newattr[3] = newattr[3] & ~termios.ICANON & ~termios.ECHO
    termios.tcsetattr(fd, termios.TCSANOW, newattr)
    oldflags = fcntl.fcntl(fd, fcntl.F_GETFL)
    fcntl.fcntl(fd, fcntl.F_SETFL, oldflags | os.O_NONBLOCK)
    try:
        yield
    finally:
        termios.tcsetattr(fd, termios.TCSAFLUSH, oldterm)
        fcntl.fcntl(fd, fcntl.F_SETFL, oldflags)

您的wait方法变为免费功能.

Your wait method becomes a free function.

这篇关于Python对象生存期特征的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆