![](/img/trans.png)
[英]How to get all attributes of an object and the attributes of its subobjects?
[英]Is it possible to create an object that actualize all its attributes when modified?
在这个例子中,应该怎么做才能使print(left_hand.number_of_fingers)
返回4
而不是5
?
class Hand:
def __init__(self, fingers:list):
self.fingers = fingers
self.number_of_fingers = len(fingers)
left_hand = Hand(["thumb", "index", "middle", "ring", "pinkie"])
left_hand.fingers.pop()
print(left_hand.number_of_fingers) # I want this to actualize and be 4, not 5
我找到了一个使用@property
的解决方案
class Hand:
def __init__(self, fingers:list):
self.fingers = fingers
@property
def number_of_fingers(self):
return len(self.fingers)
但我并不满意,因为如果计算number_of_fingers
很昂贵,我们只想在修改fingers
时计算它,而不是每次用户要求属性number_of_fingers
时计算它。
因此,在第一个代码中(不使用@property
),您将获得 5 而不是 4 的输出,因为您只是在初始化Hand
对象时将len(fingers)
的值分配给number_of_fingers
属性,而number_of_fingers
属性不是越来越多的fingers
。
所以即使在代码之间修改了left_hand.fingers
,也不会影响number_of_fingers
的值。 人们无法改变这种行为。
另外你不需要那个@property
,我测试了发现不写也不会出错。
终于来到
但我并不满意,因为如果计算
number_of_fingers
很昂贵,我们只想在修改fingers
时计算它,而不是每次用户要求属性number_of_fingers
时计算它。
哪里需要这么大的计算能力?
问题是Hand
类中的底层列表(即self.fingers
)没有被充分封装,因此任何用户都可以修改它,例如通过调用left_hand.fingers.pop()
甚至left_hand.fingers.pop()
分配一个新列表. 因此,您不能假设它在调用number_of_fingers
之间没有被修改,因此您别无选择,只能在该调用中计算其长度。
解决方案是控制您班级的客户可以做什么和不可以做什么。 最简单的方法是使用name mangling 。 也就是说,您用两个前导下划线字符作为属性名称的前缀。 这使得您的类的客户端很难(尽管并非不可能)从类外部访问这些属性(我们假设您的用户不是故意恶意的)。 因此我们现在必须提供一个pop
方法:
class Hand:
def __init__(self, fingers:list):
self.__fingers = fingers
self.__number_of_fingers = len(fingers)
def pop(self):
assert(self.__fingers)
self.__number_of_fingers -= 1
return self.__fingers.pop()
@property
def number_of_fingers(self):
return self.__number_of_fingers
left_hand = Hand(["thumb", "index", "middle", "ring", "pinkie"])
print(left_hand.pop())
print(left_hand.number_of_fingers)
印刷:
pinkie
4
我并不是建议您实际执行以下操作,但是如果您愿意,可以通过创建特殊的类装饰器@Private
和@Public
来获得更详细的@Private
,它们会将您的类包装在一个新类中并检查对您的属性的访问以确保您是不访问定义为私有的那些属性。 您可以使用@Private
装饰器来定义那些私有的属性/方法(其他一切都被认为是公共的),或者使用@Public
装饰器来定义那些公共的属性/方法(其他一切都被认为是私有的),但不能同时使用两者。 您通常会使用前导的单个下划线命名您的私有属性,这是告诉用户该属性/方法将被视为私有的约定。
这更多地是为了捕捉无意中访问本应是私有的属性。 如果使用-O Python 标志执行代码,则不会进行运行时检查。
def accessControl(failIf):
def onDecorator(aClass):
if not __debug__:
return aClass
else:
class onInstance:
def __init__(self, *args, **kargs):
self.__wrapped = aClass(*args, **kargs)
def __getattr__(self, attr):
if failIf(attr):
raise TypeError('private attribute fetch: ' + attr)
else:
return getattr(self.__wrapped, attr)
def __setattr__(self, attr, value):
if attr == '_onInstance__wrapped':
self.__dict__[attr] = value
elif failIf(attr):
raise TypeError('private attribute change: ' + attr)
else:
setattr(self.__wrapped, attr, value)
return onInstance
return onDecorator
def Private(*attributes):
return accessControl(failIf=(lambda attr: attr in attributes))
def Public(*attributes):
return accessControl(failIf=(lambda attr: attr not in attributes))
@Private('_fingers', '_number_of_fingers')
class Hand:
def __init__(self, fingers:list):
self._fingers = fingers
self._number_of_fingers = len(fingers)
def pop(self):
assert(self._fingers)
self._number_of_fingers -= 1
return self._fingers.pop()
@property
def number_of_fingers(self):
return self._number_of_fingers
left_hand = Hand(["thumb", "index", "middle", "ring", "pinkie"])
print(left_hand.pop())
print(left_hand.number_of_fingers)
# Thsis will throw an exception:
print(left_hand._fingers)
印刷:
pinkie
4
Traceback (most recent call last):
File "C:\Booboo\test\test.py", line 50, in <module>
print(left_hand._fingers)
File "C:\Booboo\test\test.py", line 9, in __getattr__
raise TypeError('private attribute fetch: ' + attr)
TypeError: private attribute fetch: _fingers
印刷:
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.