Python 如何使用decorator将函数绑定到对象实例?
我正在尝试将函数绑定到对象实例。例如,我有一个对象,我试图将一个函数绑定到condition属性。 问题是我想用一个装饰师来做这件事:Python 如何使用decorator将函数绑定到对象实例?,python,python-3.x,decorator,Python,Python 3.x,Decorator,我正在尝试将函数绑定到对象实例。例如,我有一个对象,我试图将一个函数绑定到condition属性。 问题是我想用一个装饰师来做这件事: class Transition(object): def __init__(self, enter, exit, condition=None): if not isinstance(enter, State) or not isinstance(exit, State): raise TypeError("Pa
class Transition(object):
def __init__(self, enter, exit, condition=None):
if not isinstance(enter, State) or not isinstance(exit, State):
raise TypeError("Parameters should be an instance of the State class")
self.enter = enter
self.exit = exit
self.condition = None
self.effect = None
def __repr__(self):
print("Going from {} to {} with condition {} and effect {}".format(self.enter, self.exit, self.condition.__name__, self.effect.__name__))
def __eq__(self, other):
return self.enter == other.enter and self.exit == other.exit
def is_enpoint(self, endpoints):
"""
:parameter --> 'endpoints' is a tuple indicating where the transition flows(from, to)
:return --> boolean
:description --> check if the given endpoints are valid
"""
return self.enter == endpoints[0] and self.exit == endpoints[1]
然后我将一个函数绑定到对象的实例
@bind
my_condition():
return True
在此之后,如果我们查看对象的实例条件属性,我们应该有一个对给定函数的引用
编辑1:
f实例应该有一个对条件1函数的引用,而f2实例应该有一个对条件属性的条件2的引用,我在这里只是猜测,但是您可以使用修饰符将函数绑定到类,例如:
class Foo:
pass
def bind(fn):
setattr(Foo, fn.__name__, fn)
return fn
@bind
def bar(self):
return 7
用法:
>>> f = Foo()
>>> f.bar()
7
@bind(Foo)
def bar(self):
return 7
@bind(Foo)
@property
def bat(self):
import random
return random.randint(1, 6)
>>> f = Foo()
>>> f.bar()
7
>>> f.bat
4
支持属性和绑定到自定义类的更高级示例:
def bind(cls):
def decorator(fn):
name = fn.fget.__name__ if isinstance(fn, property) else fn.__name__
setattr(cls, name, fn)
return fn
return decorator
用法:
>>> f = Foo()
>>> f.bar()
7
@bind(Foo)
def bar(self):
return 7
@bind(Foo)
@property
def bat(self):
import random
return random.randint(1, 6)
>>> f = Foo()
>>> f.bar()
7
>>> f.bat
4
最后,您绝对不应该使用的解决方案,但我无法阻止自己:
from functools import partial
def bind(clsname, first, second=None):
if second is None: # class binding
cls = globals()[clsname]
fn = first
name = fn.fget.__name__ if isinstance(fn, property) else fn.__name__
setattr(cls, name, fn)
else: # instance binding
self = first
fn = second
name = fn.fget.__name__ if isinstance(fn, property) else fn.__name__
setattr(self, name, partial(fn, self))
class BindableMeta(type):
def __new__(cls, name, bases, dct):
def inner(*args):
return bind(name, *args)
dct["bind"] = inner
return type.__new__(cls, name, bases, dct)
class Bindable(metaclass=BindableMeta):
pass
class Foo(Bindable):
pass
f = Foo()
g = Foo()
@Foo.bind
def bar(self):
return 5
@f.bind
def bat(self):
return 5
@Foo.bind
@property
def prop(self):
return 5
assert f.bar() == 5
assert f.bat() == 5
try:
assert g.bat() == 5
except AttributeError:
pass
else:
raise RuntimeError("shouldn't work")
assert f.prop == 5
当然,decorator必须将它要绑定到的实例作为一个参数,而且,如果修饰函数本身也有一个参数作为它绑定的实例传递,那么代码将更干净:如果它被定义为一个方法,则相当于
self
。Python不会自动插入它,但它可能被称为self
,因此易于阅读
class Transition:
...
f = Transition
def bind(instance):
def decorator(func):
def wrapper (*args, **kwargs):
return func(instance, *args, **kwargs)
setattr(instance, func.__name__, wrapper)
return wrapper
return decorator
@bind(f)
def condition(self, ...):
...
如果您想要一个更扁平的装饰器,您可以使用functools.partial
-然后我也使用functools.wrapps
,因为优秀的装饰器无论如何都应该使用它:
import functools
...
def bind(func, instance=None):
if instance is None:
return functools.partial(bind, instance=func)
@functools.wraps(func)
def wrapper(*args, **kw):
return func(instance, *args, **kw)
setattr(instance, func.__name__, wrapper)
return wrapper
这是因为在Python中,当一个函数直接归属于一个实例时,它的行为与任何普通属性完全相同:Python将检索该函数,然后调用它,不做任何修改——不将其更改为方法,也不插入
self
参数)。我们通过将实例作为非局部变量保存在decorator代码中来实现这一点。我想我以前写过一篇关于类似问题的博客文章。它解释了如何将两个python类属性绑定在一起,以便更新一个属性会自动更新另一个属性。我举例说明了解决这个问题的两种可能的方法。第一个是解释在博客文章与实际例子。您可能还计划将文章中所述的概念用于其他目的。您的问题不清楚。。。您希望所有其他对象正常地查找其属性,但一个特定对象应该为其使用属性/函数?为什么要使用decorator语法呢?是否要将此函数作为该类中的方法?您如何定义该属性?欢迎使用SO。恐怕你的问题不是一个真正的问题-你说了你想做什么,但你没有解释你的实际问题是什么。请注意,如果问题是“如何做到这一点”,你应该先自己尝试一下。我也在网上搜索了答案,但没有找到。@AlexandreManeta“你做了”什么?解释你的实际问题?很抱歉,您没有这样做-您的帖子中完全没有提到您为实现您的功能所做的工作,也没有提到您遇到的问题。您可能需要阅读帮助部分,特别是这个:和这个:这适用于Foo类的每个实例,还是只适用于Foo类的一个实例。例如,如果您有一个f变量和一个f1,它们是否都将此函数作为attribute@AlexandreManeta我添加了一个更通用的版本。@AlexandreManeta这适用于Foo类的每个实例。如果您希望它只应用于一个实例,那么您需要以某种方式告诉装饰程序该实例是哪个实例(属性根本不起作用)。您的第一个示例几乎也适用于实例-您可以在实例上设置属性,就像这样。所缺少的只是告诉函数在运行时它也绑定了实例的方法。(Python通过插入self
参数所做的事情)@jsbuenosetattr(实例,fn.\uuuuu name\uuuuuuuuuuu,functools.partial(fn,实例))
将起到一定的作用