Python装饰器与实例化时间变量?

时间:2010-01-05 17:19:06

标签: python decorator

我想创建一个装饰器来创建一个使用对象obj的新函数/方法。如果装饰对象是函数,则在创建函数时必须实例化obj。如果装饰对象是方法,则必须实例化新的obj并将其绑定到方法装饰的类的每个实例。我无法将装饰放在__init__中,因为装饰器修改了函数文档。我现在有这样的东西,但它只实例化time一次,这不是我想要的:

__all__ = ['dec', 'A']

from time import time
import inspect

def dec(f):
    obj = time() # want to set on object instantiation
    def new(*args, **kwargs):
        f(*args, **kwargs) # Validate against definition so it doesn't go
                           # out of sync
        print obj
        # ...
    try:
        d = inspect.getsourcelines(f)
    except IOError:
        d = "<unable to fetch definition>"
    else:
        d = d[0][1].rstrip('\n').rstrip(':').lstrip(' ').lstrip('def')
    new.__doc__ = d + "\n" + (f.__doc__ or '')
    return new

class A(object):
    @dec
    def f(self, x):
        """something"""
        print '%s.f(%s)' % (self, x)

if __name__ == '__main__':
    A().f(123)
    A().f(123)
    A().f(123)

我想解决这个问题的方法是检查传递给装饰器的对象是否带有参数self,如果是这样,返回一个将obj绑定到self的方法,如果它还没有在那里,然后使用self.obj。然后,如果传递给装饰器的对象没有self参数,只需在装饰器中实例化obj并返回一个使用它的函数。

然而......我所说的并不适合我,因为在我的真装饰中,我返回一个派生自list且具有__call__属性的对象。此外,在真正的装饰器中,self甚至没有在由它装饰的对象中定义,因为它们没有使用它们的实例变量(我真正装饰的东西只是要由外部对象订阅的事件,事件都有记录的签名)。

编辑:实际上,如果有办法让list子类实例绑定到一个实例,以便它的__call__属性隐式接收类实例(就像在任何普通的实例方法中一样),将是一个完美的解决方案,这是我最初试图弄清楚如何做的。但也许有一个更好的解决方案,以便我不必使用self属性定义装饰方法?要么是完美的。

4 个答案:

答案 0 :(得分:1)

因为装饰者只是用来说

的语法糖
def func():
   ...
func = decorator(func)

为什么不在对象构造函数中执行此操作?

class A(object):
    def __init__(self):
        # apply decorator at instance creation
        self.f = dec(self.f)

    def f(self, x):
        """something"""
        print '%s.f(%s)' % (self, x)

答案 1 :(得分:1)

要弄明白你到底是什么有点困难。列表和__call__的内容令我困惑,所以我主要坚持你的第一段:

__all__ = ['dec', 'A']

from types import InstanceType
from functools import wraps
import inspect

def dec(func):

   #get the sig of the function
   sig = []
   @wraps(func)
   def wrapper(*args, **kwargs):
      ret = None
      #if this is a method belonging to an object...
      if args and getattr(args[0], func.__name__, None):
         instance, args = args[0], args[1:]
         #if sig of object is not already set
         if not hasattr(instance, "sig"):
            instance.sig = []
         ret = func(instance, *args, **kwargs)
         print "Sig of %s is %s" % (func.__name__, id(instance.sig))
      #else this is a function
      else:
         ret = func(*args, **kwargs)
         print "Sig of %s is %s" % (func.__name__, id(sig))
      return ret

   #modify the doc string
   try:
      docs = inspect.getsourcelines(func)
   except:
      docs = "<unable to fetch defintion>"
   else:
      docs = docs[0][1].rstrip('\n').rstrip(':').lstrip(' ').lstrip('def')
   wrapper.__doc__ = docs + "\n" + (func.__doc__ or '')
   return wrapper

class A(object):
   def __init__(self):
      super(A, self).__init__()

   @dec
   def f(self, x):
      """something"""
      print '%s.f(%s)' % (self, x)


@dec
def myfunc():
   print "myfunc"

@dec
def myfunc2():
   print "myfunc2"

@dec
def myfunc3():
   print "myfunc3"

if __name__ == "__main__":
   list = []
   for x in xrange(3):
      list.append(A())

   [a.f(123) for a in list]
   myfunc()
   myfunc()
   myfunc2()
   myfunc2()
   myfunc3()
   myfunc3()

输出:

<__main__.A object at 0x00B9F2D0>.f(123)
Sig of f is 11932616
<__main__.A object at 0x00B9F430>.f(123)
Sig of f is 11925464
<__main__.A object at 0x00B9F450>.f(123)
Sig of f is 11918112
myfunc
Sig of myfunc is 11925624
myfunc
Sig of myfunc is 11925624
myfunc2
Sig of myfunc2 is 11794592
myfunc2
Sig of myfunc2 is 11794592
myfunc3
Sig of myfunc3 is 11925144
myfunc3
Sig of myfunc3 is 11925144

答案 2 :(得分:0)

你的写作风格真的难以阅读。正常句子是你的一半:P

你想要这个还是什么?

__all__ = ['dec', 'A']

from time import time, sleep
import inspect

def dec(f):
    def new(self, *args, **kwargs):
        print self.initiated # print the time the objecte was initiated ...
        return f(self, *args, **kwargs) # Validate against definition so it doesn't go
                           # out of sync
    try:
        d = inspect.getsourcelines(f)
    except IOError:
        d = "<unable to fetch definition>"
    else:
        d = d[0][1].rstrip('\n').rstrip(':').lstrip(' ').lstrip('def')
    new.__doc__ = d + "\n" + (f.__doc__ or '')
    return new

class A(object):
    def __init__(self):
        self.initiated = time() # save the time the object was initiated


    @dec
    def f(self, x):
        """something"""
        print '%s.f(%s)' % (self, x)

if __name__ == '__main__':
    A().f(123)
    sleep(1)
    A().f(123)
    sleep(1)
    A().f(123)

答案 3 :(得分:0)

好的,根据manifest的代码,我有一个解决方案。

正如你所看到的,pydoc仍然会有关于装饰物的文档:

class A(__builtin__.object)
 |  Methods defined here:
 |  
 |  f(*args, **kwargs)
 |      f(self, x)
 |      something

同样,带有装饰方法的类的每个实例都将具有不同的obj。每个函数都有自己的obj

 <__main__.A object at 0x7c209bee4a50>.f(123)
 obj of <__main__.A object at 0x7c209bee4a50>.f is 136479497243752
 <__main__.A object at 0x7c209bee4a10>.f(123)
 obj of <__main__.A object at 0x7c209bee4a10>.f is 136479497250720
 <__main__.A object at 0x7c209bee4a90>.f(123)
 obj of <__main__.A object at 0x7c209bee4a90>.f is 136479497446824
 myfunc
 obj of myfunc is 136479497243392
 myfunc
 obj of myfunc is 136479497243392
 myfunc2
 obj of myfunc2 is 136479497245688
 myfunc2
 obj of myfunc2 is 136479497245688
 myfunc3
 obj of myfunc3 is 136479497246408
 myfunc3
 obj of myfunc3 is 136479497246408

以下是代码:

__all__ = ['dec', 'A']

from functools import wraps
import inspect

def dec(cls=None):
    # cls will be closed in subdec
    def subdec(func):
        # closed in wrapper, guaranteed to be unique per decorator evaluation
        obj = []

        @wraps(func)
        def wrapper(*args, **kwargs):
            if (args and type(args[0]) == cls):
                instance = args[0]
                # We will attach to instance a dict _objs of
                # function_name:obj. This way we don't pollute the namespace
                # when decorating many functions.

                # Alternatively, you could make a dict external to instance
                # of instance:{function_name:obj}, but that takes more work
                # because you need to make it not prevent garbage collection
                # of instance.
                if not hasattr(instance, "_objs"):
                    instance._objs = {}
                if func.__name__ not in instance._objs:
                    instance._objs[func.__name__] = []
                func(*args, **kwargs) # This is only used to check the arity.
                                      # My real code is all to do with
                                      # manipulating obj.
                print "obj of %s.%s is %s" % (
                    instance,
                    func.__name__,
                    id(instance._objs[func.__name__])
                )
            else:
                # Functions are identified by the closed obj
                func(*args, **kwargs)
                print "obj of %s is %s" % (func.__name__, id(obj))

        # Find function/method signature and prepend it to the new object's doc
        try:
            doc = inspect.getsourcelines(func)
        except IOError:
            line = "<unable to fetch definition>"
        else:
            line = '@'
            i = 0
            while line.lstrip(' ').startswith("@"):
                try:
                    line = doc[0][i]
                except IndexError:
                    line = "<unable to fetch definition>"
                i += 1
        line = line.rstrip('\n').rstrip(':').lstrip(' ').lstrip('def')
        wrapper.__doc__ = line + "\n" + (func.__doc__ or '')

        return wrapper
    return subdec

class A(object):
    def f(self, x):
        """something"""
        print '%s.f(%s)' % (self, x)

A.f = dec(A)(A.f)

@dec()
def myfunc():
    print "myfunc"

@dec()
def myfunc2():
    print "myfunc2"

@dec()
def myfunc3():
    print "myfunc3"

if __name__ == "__main__":
    a, b, c = A(), A(), A()
    # a, b, and c each have their own instance of obj:
    a.f(123)
    b.f(123)
    c.f(123)
    myfunc()
    myfunc()
    myfunc2()
    myfunc2()
    myfunc3()
    myfunc3()

添加的外部装饰器cls仅用于获取类的标识,因此装饰函数可以确定天气它是一个函数或方法。虽然我不确定[多重]继承会有多好...也许显示检查该部分更好的想法。