我正在尝试将函数绑定到对象实例。例如,我有一个对象,并且试图将一个函数绑定到condition属性。 问题是我想使用装饰器来做到这一点:
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
此后,如果我们查看对象的实例 condition属性
,则应该对给定函数有一个引用f = Transition()
f1 = Transition()
@bind(f)
def condition1():
return True
@bind(f1)
def condition2():
return False
f实例应引用 condition1 函数,而f2实例应引用 condition 属性的 condition2 。
答案 0 :(得分:1)
当然,装饰器必须将要绑定到的实例作为参数获取-并且,如果装饰的函数本身在绑定的实例中传递一个参数,则代码将更加简洁。同样:如果定义为方法,则等效于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.wraps
,因为好的装饰器仍然应该使用它:
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
参数)。为此,我们将实例作为装饰器代码中的非本地变量保存。
答案 1 :(得分:0)
我只是在这里猜测,但是您可以使用装饰器将函数绑定到类,例如:
class Foo:
pass
def bind(fn):
setattr(Foo, fn.__name__, fn)
return fn
@bind
def bar(self):
return 7
用法:
>>> f = Foo()
>>> f.bar()
7
支持属性并绑定到自定义类的更高级示例:
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
用法:
@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
答案 2 :(得分:0)
我想我以前写过一篇关于类似问题的博客文章。它说明了如何将2个python类属性绑定在一起,以便更新一个自动更新另一个。我说明了解决此问题的两种可能方法。第一个问题在this博客文章中进行了举例说明。您可能还打算将帖子中说明的概念用于其他目的。