如何在创建后立即从全局中删除一个类?

Posted

技术标签:

【中文标题】如何在创建后立即从全局中删除一个类?【英文标题】:How to delete a class from globals right after creation? 【发布时间】:2014-03-22 00:42:01 【问题描述】:

我有一个元类,它只是为我的模块上的类附加一个前缀。到目前为止一切顺利,问题是我想删除调用元类的类的定义。例如,如果类 APrefixA 为前缀,我将以我的全局变量上的两个类结束,但我只想要 PrefixA 就可以了。现在为了实现我的目标,我正在做这样的事情:

class PrefixChanger(type):

    prefix = 'Prefix'
    to_delete = []

    def __new__(cls, clsname, bases, attrs):
        new_cls = type.__new__(cls,cls.prefix+clsname,bases,attrs)
        globals()[cls.prefix+clsname] = new_cls
        cls.to_delete.append(clsname)
        return new_cls

    @classmethod
    def do_clean(cls):
        gl = globals() 
        for name in cls.to_delete:            
            del gl[name]                

class A(object):
    __metaclass__ = PrefixChanger        

class B(PrefixA):
    pass

PrefixChanger.do_clean()

这种方法的问题是我必须将最后一行 PrefixChanger.do_clean() 放在每个使用我的元类的模块的末尾。

那么,有什么方法可以正确地做到这一点吗?我的意思是元类(或某种黑魔法)会尽快从全局中删除不需要的类(可能就在它们的定义之后)。

我有兴趣以这种方式完成任务,但也欢迎提出使用其他方法完成任务的建议。

我的解决方案也打破了这种情况:

class B(object):
    class C(object):
        __metaclass__ = PrefixChanger

给解决它的人加分!

【问题讨论】:

这在很多层面上都是一个糟糕的主意.. 嗯,我觉得你是对的,这很奇怪,但是……有解决办法吗? @MartijnPieters Elaborate,我对这种想法可能出现的问题很感兴趣(除非您只是说使用元类只是为使用该元类的类的名称添加前缀是非常愚蠢,只有一个包含前缀为名称的类的模块会更简单)。 最不意外的原则。可发现性、自我记录等,在这里都被违反了。 @JAB:这就是我要做的;提供该前缀、提供新绑定并从全局变量中清除原始名称所必须经历的痛苦对我来说是不值得的、愚蠢的、太多的工作却没有收获,使代码库不必要地复杂化,使其更难阅读代码(PrefixA 是从哪里出现的?),使得使用代码检查工具等变得更加困难。 【参考方案1】:

可能性 3

使用队列阻塞并从其引用者中删除类来启动线程。 您可以使用gc 模块获取引用对象。 gc.get_referrers

哈哈

### License: You may only use this code if it is for the good of humanity.
### Any copy of the code requires this license to be copied with it.

>>> class T(type):
    prefix = 'meep'
    def __new__(cls, name, bases, attrs):
        new_cls = type.__new__(cls, cls.prefix+name,bases,attrs)
        globals()[cls.prefix+name] = new_cls # interesting
        l = []
        import threading
        def t():
            import gc
            while 1:
                for ref in gc.get_referrers(l):
                    try: del ref[name];break
                    except:pass
        threading.Thread(target = t).start()
        return l


>>> class C(object):
    __metaclass__ = T


>>> C

Traceback (most recent call last):
  File "<pyshell#30>", line 1, in <module>
    C
NameError: name 'C' is not defined
>>> meepC
<class '__main__.meepC'>

不可能2

不要在里面放一个类那么你就不需要删除它:

class PrefixChanger(type):
    # ...

    def __new__(cls, clsname, bases, attrs):
        # ...
        return None

不可能 1 怎么样

PrefixChanger('A', (object,), )

您不需要删除它。我的意思是.. 你的课程没有内容,但如果应该有,那么这当然不是一个选择。

【讨论】:

是的,对于它的工作示例,但正如你所说,我想在其他有内容的类上使用它 Impossibility 2 是我的第一个想法,但正如问题所说,我不想将 None 放在全局变量上,我只想放弃课程。这种方法会导致更多的错误,就像@MartijnPieters 指出的那样。 Impossibility 3 真的是 impossible 因为这个想法是将带有PrefixChanger 的模块用作普通模块(实际上在我的方法中它是工作,你可以从另一个导入模块,一切都很好,即使代码完成在我的 IDE 上也能正常工作) 没有。有可能的。我将重命名它.. 就这样做吧。 :) 我希望你能想到一些对你如此温柔的语言。但是一个不错的奇怪任务!【参考方案2】:

在创建后立即删除它们的最直接方法是在创建后立即执行 - 似乎没有任何类或模块创建“钩子”(软件拦截)提供隐式执行此操作的方法。

以下是一个折衷方案:它无需在每个模块的末尾调用do_clean() 函数,而是必须在每个类定义之后调用函数。 另一方面,这可能并不比必须添加一个

    __metaclass__ = PrefixChanger

每个类定义中的语句...

import textwrap  # to help make the exec code more readable

def Prefix(cls, prefix='Prefix'):
    exec(textwrap.dedent('''\
        global prefixclassname
        prefixclassname = classname
        prefixclassname.__name__ = "prefixclassname"
        del globals()["classname"]
        '''.format(prefix=prefix, classname=cls.__name__))
    )

class A(object):
    pass
Prefix(A)

class B(PrefixA):
    pass
Prefix(B)

if __name__ == '__main__':
    try:
        print 'A:', A
    except NameError:
        print 'there\'s nothing named "A"'
    else:
        print 'error: there shouldn\'t be anything named "A"'

    print 'PrefixA:', PrefixA
    print 'PrefixB:', PrefixB

输出:

A: there's nothing named "A"
PrefixA: <class '__main__.PrefixA'>
PrefixB: <class '__main__.PrefixB'>

【讨论】:

以上是关于如何在创建后立即从全局中删除一个类?的主要内容,如果未能解决你的问题,请参考以下文章

按下按钮后如何延迟删除课程?

压缩后如何立即使用 Phar 解压缩存档?

雨后清风教你将回收站设置为在Windows10中立即永久删除文件

如何一次从CloudFormation中删除多个全局二级索引?

如何在 Cake 上做一个全局模型 afterSave()?

从文件系统的目录中删除后如何重新创建 ATR 文件?