同步阅读进度,多语言翻译,过滤屏幕蓝光,评论分享,更多完整功能,更好读书体验,试试 阅读 ‧ 电子书库
Using State Information in Descriptors
If you study the two descriptor examples we’ve written so far, you might notice that they get their information from different places—the first (the name attribute example) uses data stored on the client instance, and the second (the attribute squaring example) uses data attached to the descriptor object itself. In fact, descriptors can use both instance state and descriptor state, or any combination thereof:
Descriptor methods may use either, but descriptor state often makes it unnecessary to use special naming conventions to avoid name collisions for descriptor data stored on an instance. For example, the following descriptor attaches information to its own instance, so it doesn’t clash with that on the client class’s instance:
class DescState: # Use descriptor state
def __init__(self, value):
self.value = value
def __get__(self, instance, owner): # On attr fetch
print('DescState get')
return self.value * 10
def __set__(self, instance, value): # On attr assign
print('DescState set')
self.value = value
# Client class
class CalcAttrs:
X = DescState(2) # Descriptor class attr
Y = 3 # Class attr
def __init__(self):
self.Z = 4 # Instance attr
obj = CalcAttrs()
print(obj.X, obj.Y, obj.Z) # X is computed, others are not
obj.X = 5 # X assignment is intercepted
obj.Y = 6
obj.Z = 7
print(obj.X, obj.Y, obj.Z)
This code’s value information lives only in the descriptor, so there won’t be a collision if the same name is used in the client’s instance. Notice that only the descriptor attribute is managed here—get and set accesses to X are intercepted, but accesses to Y and Z are not (Y is attached to the client class and Z to the instance). When this code is run, X is computed when fetched:
DescState get
20 3 4
DescState set
DescState get
50 6 7
It’s also feasible for a descriptor to store or use an attribute attached to the client class’s instance, instead of itself. The descriptor in the following example assumes the instance has an attribute _Y attached by the client class, and uses it to compute the value of the attribute it represents:
class InstState: # Using instance state
def __get__(self, instance, owner):
print('InstState get') # Assume set by client class
return instance._Y * 100
def __set__(self, instance, value):
print('InstState set')
instance._Y = value
# Client class
class CalcAttrs:
X = DescState(2) # Descriptor class attr
Y = InstState() # Descriptor class attr
def __init__(self):
self._Y = 3 # Instance attr
self.Z = 4 # Instance attr
obj = CalcAttrs()
print(obj.X, obj.Y, obj.Z) # X and Y are computed, Z is not
obj.X = 5 # X and Y assignments intercepted
obj.Y = 6
obj.Z = 7
print(obj.X, obj.Y, obj.Z)
This time, X and Y are both assigned to descriptors and computed when fetched (X is assigned the descriptor of the prior example). The new descriptor here has no information itself, but it uses an attribute assumed to exist in the instance—that attribute is named _Y, to avoid collisions with the name of the descriptor itself. When this version is run the results are similar, but a second attribute is managed, using state that lives in the instance instead of the descriptor:
DescState get
InstState get
20 300 4
DescState set
InstState set
DescState get
InstState get
50 600 7
Both descriptor and instance state have roles. In fact, this is a general advantage that descriptors have over properties—because they have state of their own, they can easily retain data internally, without adding it to the namespace of the client instance object.
请支持我们,让我们可以支付服务器费用。
使用微信支付打赏