我如何知道调用方法的位置?

时间:2013-03-06 07:06:50

标签: python

我正在尝试在Python中干净地实现Objective-C的类别,并发现this answer类似于我的问题。我复制了以下代码:

categories.py

class category(object):
    def __init__(self, mainModule, override = True):
        self.mainModule = mainModule
        self.override = override

    def __call__(self, function):
        if self.override or function.__name__ not in dir(self.mainModule):
            setattr(self.mainModule, function.__name__, function)

但我不想浪费命名空间。 通过使用这个“类别”,仍然存在一个变量NoneType对象,如下所示:

>>> from categories import category
>>> class Test(object):
...     pass
... 
>>> @category(Test)
... def foobar(self, msg):
...     print msg
... 
>>> test = Test()
>>> test.foobar('hello world')
hello world
>>> type(foobar)
<type 'NoneType'>
>>> 

我希望它像下面一样

>>> from categories import category
>>> class Test(object):
...     pass
... 
>>> @category(Test)
... def foobar(self, msg):
...     print msg
... 
>>> test = Test()
>>> test.foobar('hello world')
hello world
>>> type(foobar)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'foobar' is not defined
>>> 

无论如何都要自动将其删除,如下所示?

    def __call__(self, function):
        if self.override or function.__name__ not in dir(self.mainModule):
            setattr(self.mainModule, function.__name__, function)
            del(somewhere.function.__name__)

我发现sys._getframe给了我一些有用的信息。但我不能自己做。

2 个答案:

答案 0 :(得分:3)

不,没有办法自动做到这一点。之后您必须手动删除名称。 category这里是一个装饰器,意思是

@category(Test)
def f():
    ...

相同
def f():
   ...
f = category(Test)(f)

即使从category内部删除了外部作用域中的名称,也是不够的,因为在装饰器执行后,该名称会被反弹。

您滥用装饰器语法链接到边框的代码。装饰器旨在提供修改或扩展它们所装饰的函数的方法,但该代码依赖于装饰器的副作用(即,将函数指定为类的方法),然后丢弃该函数。但它只能通过返回None来“丢弃”它,所以没有像你看到的那样仍然绑定到函数的名称。

我建议您按照该问题的最高投票答案的建议,并简单地为您的班级分配新方法。 Python中没有真正需要类似“基础结构”的类别,因为您可以随时直接向现有类添加新方法。

答案 1 :(得分:0)

虽然我完全同意BrenBarn的说法,但您可以将功能删除拆分为后续步骤。问题是在装饰器执行后,重新分配变量。所以你不能在装饰器本身内执行删除。

然而,您可以记住这些功能,并在稍后将其从模块中删除。

class category(object):
    functions = []

    def __init__(self, mainModule, override = True):
        self.mainModule = mainModule
        self.override = override

    def __call__(self, function):
        if self.override or function.__name__ not in dir(self.mainModule):
            setattr(self.mainModule, function.__name__, function)
            self.functions.append((inspect.getmodule(function), function.__name__))
            return self.dummy

    @staticmethod
    def dummy():
        pass

    @classmethod
    def cleanUp(cls):
        for module, name in cls.functions:
            if hasattr(module, name) and getattr(module, name) == cls.dummy:
                delattr(module, name)
        cls.functions = []

category类型将记住它所装饰的函数,并存储它们所属的名称和模块,以便稍后进行清理。装饰器还返回一个特殊的虚函数,以便清理可以确保稍后不重新赋值变量。

>>> class Test(object): pass
>>> @category(Test)
def foobar(self, msg):
    print(msg)

>>> @category(Test)
def hello_world(self):
    print('Hello world')

>>> test = Test()
>>> test.foobar('xy')
xy
>>> test.hello_world()
Hello world

>>> type(foobar)
<class 'function'>
>>> type(hello_world)
<class 'function'>

>>> category.cleanUp()

>>> type(foobar)
Traceback (most recent call last):
  File "<pyshell#26>", line 1, in <module>
    type(foobar)
NameError: name 'foobar' is not defined
>>> type(hello_world)
Traceback (most recent call last):
  File "<pyshell#27>", line 1, in <module>
    type(hello_world)
NameError: name 'hello_world' is not defined