我只是想使用描述符模式,但它似乎没有那么好用。 这是一个简短的例子(没有任何实际用途,只是为了展示):
class Num(object):
def__init__(self, val=0):
self.val = val
def __get__(self, instance, owner):
return self.val
def __set__(self, instance, val):
self.val = val
def __str__(self):
return "Num(%s)" % self.val
def __repr__(self):
return self.__str__()
class Test(object):
def __init__(self, num=Num()):
self.num = num
和测试:
>>>t = Test()
>>>t.num # OK
Num(0)
>>>t.num + 3 #OK i know how to fix that, but I thought __get__.(t.num, t, Test) will be called
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unsupported operand type(s) for +: 'Num' and 'int'
>>> t.num = 4 # why isn't __set__(t.num, t, 4) called here?
>>> t.num
4
我在这里有什么误解?
答案 0 :(得分:5)
描述符仅在它们是类的属性时才起作用,而不是实例。如果您将班级更改为:
class Test(object):
num = Num()
。 。 。然后描述符将起作用。
但是,因为必须在类上设置描述符,这意味着描述符只有一个实例,因此描述符将其值存储在self
上可能不是一个好主意。这些值将在类的所有实例中共享。而是将值设置为instance
。
另请注意,您的__str__
和__repr__
可能无法按照您的意愿行事。调用t.num
将激活描述符并返回其val
,因此t.num
的结果将是普通数字0,而不是Num
实例。描述符的重点是透明地返回__get__
的结果,而不使描述符对象本身可见。
以下是一些说明性示例:
>>> t1 = Test()
>>> t2 = Test()
>>> t1.num
0
>>> Test.num
0
# Accessing the descriptor object itself
>>> Test.__dict__['num']
Num(0)
>>> t1.num = 10
>>> t1.num
10
# setting the value changed it everywhere
>>> t2.num
10
>>> Test.num
10
使用描述符的替代版本:
class Num(object):
def __init__(self, val=0):
self.val = val
def __get__(self, instance, owner):
try:
return instance._hidden_val
except AttributeError:
# use self.val as default
return self.val
def __set__(self, instance, val):
instance._hidden_val = val
class Test(object):
num = Num()
>>> t1 = Test()
>>> t2 = Test()
>>> t1.num
0
>>> t1.num = 10
>>> t1.num
10
# Now there is a separate value per instance
>>> t2.num
0
答案 1 :(得分:0)
正如BrenBarn所说,描述符似乎是用于类变量。您可能有兴趣查看Pythons属性。
class GenericItem(object):
"""Generic item descriptor"""
def __init__(self, value=None, name=""):
super().__init__()
self.value = value
self.name = name
# end Constructor
def __get__(self, obj, objtype):
# print(self, obj, objtype)
return self.value
# end __get__
def __set__(self, obj, value):
# print(self, obj, value)
self.value = value
# end __set__
def __str__(self):
if self.name is None or self.name == "":
return str(self.value)
return self.name +"("+str(self.value)+")"
# end __str__
# end class Num
class Test(object):
def __init__(self, value=0):
super().__init__()
self._num = GenericItem(value, "Number")
# end Constructor
@property
def num(self):
"""This is a number"""
return self._num
@num.setter
def num(self, value):
self._num.__set__(None, value)
# end num property
# end class Test
if __name__ == "__main__":
g = GenericItem(1, "Generic")
print(g)
g = 5
print(g)
t = Test()
print(t.num)
try:
t.num + 3 # We didn't implement any sort of addition __add__
except:
pass
t.num = 4
print(t.num)
结果:
Generic(1)
5
Number(0)
Number(4)
属性有助于控制实例变量的设置方式。