我有一个用例,我希望Python @property
在被调用为方法(即末尾括号)时的行为与在没有括号的情况下调用它时的行为不同。这样的事情是可能的。
class Sequence:
@property
def first(self):
return self._first
@first.setter
def first(self, v):
self._first = v
# This won't work
@first.method
def first(self):
# Do something different than the setter and the getter since
# `first` is being called as a method.
return 4321
seq = Sequence()
seq.first = 1234
# Setting and getting the first property works fine
assert seq.first == 1234
# Calling the first property as a function fails
assert seq.first() == 4321
答案 0 :(得分:1)
好吧,你可以让getter返回一个Proxy,它的行为类似于从getter
返回的值,或者就像你调用它时的行为一样。如果你从getter中返回可以调用的东西,那将是不明确的,但在你的情况下你会返回整数(不可调用)。它仍然是不可取的,但你可以让它工作(基于the Python descriptor how-to中提到的property
模拟器):
def make_callable_proxy(val, call_func):
class CallableProxy(type(val)): # subclass the class of value
__call__ = call_func
return CallableProxy(val)
class CallableProperty(object):
def __init__(self, fget=None, fset=None, fdel=None, fcall=None, doc=None):
self.fget = fget
self.fset = fset
self.fdel = fdel
self.fcall = fcall
if doc is None and fget is not None:
doc = fget.__doc__
self.__doc__ = doc
def __get__(self, obj, objtype=None):
if obj is None:
return self
if self.fget is None:
raise AttributeError("unreadable attribute")
# The following implements the "callable part".
if self.fcall is None:
return self.fget(obj)
value = make_callable_proxy(self.fget(obj), self.fcall)
return value
def __set__(self, obj, value):
if self.fset is None:
raise AttributeError("can't set attribute")
self.fset(obj, value)
def __delete__(self, obj):
if self.fdel is None:
raise AttributeError("can't delete attribute")
self.fdel(obj)
def getter(self, fget):
return type(self)(fget, self.fset, self.fdel, self.fcall, self.__doc__)
def setter(self, fset):
return type(self)(self.fget, fset, self.fdel, self.fcall, self.__doc__)
def deleter(self, fdel):
return type(self)(self.fget, self.fset, fdel, self.fcall, self.__doc__)
def method(self, fcall):
return type(self)(self.fget, self.fset, self.fdel, fcall, self.__doc__)
class Sequence(object):
@CallableProperty
def first(self):
return self._first
@first.setter
def first(self, v):
self._first = v
# THIS WILL WORK NOW
@first.method
def first(self):
# Do something different than the setter and the getter since
# `first` is being called as a method.
return 4321
seq = Sequence()
seq.first = 1234
# Setting and getting the first property works fine
assert seq.first == 1234
# Calling the first property as a function fails
assert seq.first() == 4321
这可以通过使用真实的代理类(如wrapt.ObjectProxy
)而不是CallableProxy
类来进一步细化。但这取决于此类软件包的可用性。如果你有wrapt
这就是它的样子:
from wrapt import ObjectProxy
def make_callable_proxy(val, call_func):
class CallableProxy(ObjectProxy):
__call__ = call_func
__repr__ = val.__repr__ # just for a nicer representation
return CallableProxy(val)
答案 1 :(得分:0)
您需要了解装饰器语法如何去除:
@dec
def foo():
pass
变为
def foo():
pass
foo = dec(foo)
,而
@dec(1)
def foo():
pass
变为
d = dec(1)
def foo():
pass
foo = d(foo)
在第一种情况下,装饰器是一个用装饰函数作为参数调用的函数。在第二个中,使用1
作为参数调用装饰器,返回一个将装饰函数作为参数的函数。
换句话说,您在询问是否有办法定义函数dec
,其行为取决于其返回值的使用方式。这个功能看起来不太可能。
答案 2 :(得分:0)
我认为没有办法让@property
以这种方式工作,但根据您的使用情况,一种可能的替代方法是让property
返回一些类型为ducktypes的东西,但实际上是一个可调用函数返回函数的值:
class CallableWrapper:
def __init__(self, value):
self._value = value
def __call__(self):
return 4321
def __eq__(self, other):
return self._value == other
def __getattr__(self, name):
return getattr(self._value, name)
class Sequence:
@property
def first(self):
return CallableWrapper(self._first)
@first.setter
def first(self, v):
self._first = v
seq = Sequence()
seq.first = 1234
# Setting and getting the first property works fine
assert seq.first == 1234
# Calling the first property
assert seq.first() == 4321
您可以采用额外的魔法来获得与您为将__call__()
方法分配给包装器所描述的语法非常相似的语法。只有当你愿意让属性返回一个不同类型的包装器而不是原始值本身时,这才有用。
也就是说,尝试重载此类属性可能不是一个好主意,因为它可能会使您的代码更难以阅读和理解。