这是Python 2.5,它也是GAE,并不重要.
我有以下代码.我正在使用dec_check类作为装饰器在bar中装饰foo()方法.
class dec_check(object):
def __init__(self, f):
self.func = f
def __call__(self):
print 'In dec_check.__init__()'
self.func()
class bar(object):
@dec_check
def foo(self):
print 'In bar.foo()'
b = bar()
b.foo()
Run Code Online (Sandbox Code Playgroud)
执行此操作时,我希望看到:
In dec_check.__init__()
In bar.foo()
Run Code Online (Sandbox Code Playgroud)
但我得到" TypeError: foo() takes exactly 1 argument (0 given)"作为.foo()一种对象方法,以自我为参数.我猜测问题是bar当我执行装饰器代码时,实例并不存在.
那么如何将一个实例传递bar给装饰器类呢?
好吧,这是现实世界的场景:我正在编写一个应用程序,我有一个代表某种类型文件的类(在我的例子中,这是照片,但细节与问题无关).Photos类的每个实例对于照片的文件名应该是唯一的.
问题是,当用户告诉我的应用程序加载文件时,我需要能够识别文件何时已加载,并使用现有实例作为该文件名,而不是在同一文件名上创建重复实例.
对我而言,使用memoization似乎是一个很好的情况,并且有很多例子,但在这种情况下,我不只是记住一个普通的函数,我需要记忆__init__().这造成了一个问题,因为在__init__()被调用的时候已经太晚了,因为已经创建了一个新实例.
在我的研究中,我发现了Python的__new__()方法,我实际上能够编写一个简单的工作示例,但是当我尝试在我的真实世界对象上使用它时它就崩溃了,我不知道为什么(我唯一可以做到的)我想到的是我的真实世界对象是我无法控制的其他对象的子类,因此这种方法存在一些不兼容性.这就是我所拥有的:
class Flub(object):
instances = {}
def __new__(cls, flubid):
try:
self = Flub.instances[flubid]
except KeyError:
self = Flub.instances[flubid] = super(Flub, cls).__new__(cls)
print 'making a new one!'
self.flubid = flubid
print id(self)
return self
@staticmethod
def destroy_all():
for flub in Flub.instances.values():
print 'killing', flub
a = Flub('foo')
b = Flub('foo')
c = Flub('bar')
print a
print b
print c
print a is b, b is c
Flub.destroy_all()
Run Code Online (Sandbox Code Playgroud)
哪个输出:
making a new one! …Run Code Online (Sandbox Code Playgroud) 将类的方法包装在“样板”Python 装饰器中会将该方法视为常规函数,并使其失去__self__引用类实例对象的属性。这可以避免吗?
参加以下课程:
class MyClass(object):
def __init__(self, a=1, b=2):
self.a = a
self.b = b
def meth(self):
pass
Run Code Online (Sandbox Code Playgroud)
如果meth()未修饰,MyClass().meth.__self__则引用实例方法并启用类似setattr(my_class_object.meth.__self__, 'a', 5).
但是当将任何东西包装在装饰器中时,只传递函数对象;它实际绑定的对象不会随之传递。(请参阅此答案。)
import functools
def decorate(method):
@functools.wraps(method)
def wrapper(*args, **kwargs):
# Do something to method.__self__ such as setattr
print(hasattr(method, '__self__'))
result = method(*args, **kwargs)
return result
return wrapper
class MyClass(object):
def __init__(self, a=1, b=2):
self.a = a
self.b = b
@decorate
def meth(self):
pass
MyClass().meth()
# False <--------
Run Code Online (Sandbox Code Playgroud)
这可以被覆盖吗?
虽然有很多关于使用类作为装饰器的资源,但我还没有找到任何处理装饰方法问题的资源。这个问题的目标是解决这个问题。我将发布我自己的解决方案,但当然也邀请其他所有人发布他们的解决方案。
标准装饰器类实现的问题在于,python 不会创建被装饰函数的绑定方法:
class Deco:
def __init__(self, func):
self.func= func
def __call__(self, *args):
self.func(*args)
class Class:
@Deco
def hello(self):
print('hello world')
Class().hello() # throws TypeError: hello() missing 1 required positional argument: 'self'
Run Code Online (Sandbox Code Playgroud)
方法装饰器需要克服这个障碍。
从前面的示例中获取类,预计以下事情会起作用:
>>> i= Class()
>>> i.hello()
hello world
>>> i.hello
<__main__.Deco object at 0x7f4ae8b518d0>
>>> Class.hello is Class().hello
False
>>> Class().hello is Class().hello
False
>>> i.hello is i.hello
True
Run Code Online (Sandbox Code Playgroud)
理想情况下,函数__doc__和签名以及类似的属性也被保留。
python ×4
caching ×1
class ×1
decorator ×1
memoization ×1
python-3.x ×1
singleton ×1
unique ×1