我一直在研究python脚本,其中每次更改某些值时,也应该修改并行值。例如:
self.empire.gold = 345
self.empire.update_times.gold = time.time()
每次执行此操作都变得很痛苦,因此我一直在研究如何自动执行此操作。这是我想出的:
import time
class boring_object:
pass
class Object:
def __init__(self, gold):
self._gold = gold
self._updates = boring_object()
@property
def gold(self):
return self._gold
@gold.setter
def gold(self, value):
self._gold = value
self._updates.gold = time.time()
使用此功能,我可以自动更新时间。
但是,我也想将类似的设置方法应用于其他资源。
我可以将该代码重复五遍以覆盖gold
,grain
,potatoes
等,但是我只想将一组资源名称传递给构造函数,并让他们共享通用的getter和setters 。有可能吗?
在执行任何此类操作之前,我使用的是更接近boring_object()
的内容:
class Object:
def __init__(self, array):
for each in array:
setattr(self, each, 0)
除了缺少属性功能外,print()
也不是很好。该解决方案的优点是可以同时使用通用的getter和setter并在没有太多额外代码的情况下很好地打印。
写完这篇文章后,我发现this question涉及吸气剂和吸气剂。一个答案建议使用using __getattr__
and __setattr__
,这将使我更接近目标,但我不想存储更新时间并更改最终在该对象上设置的所有属性(仅当初始化它时命名的属性) 。其他属性应该表现得好像没有自定义的getter / setter逻辑一样。
答案 0 :(得分:2)
__getattr__
等是不错的神奇方法,可让您变得聪明。但是,我建议你不要这样做。它们使您的代码难以理解和遵循。想像一下其他人(或一年后的你)试图理解what you did there and why。如果有一种明确且易于理解的方法可以完成工作,那么我将始终倾向于这样做。
如果您想限制对属性的访问或向其中添加一些小的逻辑(例如在此处存储更新时间),则Python @property
是很好的选择。但是,属性实际上并不适合您的情况,因为您需要动态的东西,并且可以在运行时进行配置。那么为什么不使用显而易见的呢?仅仅存储帝国资源的一些“私有”字典(请参阅下面的_possessions
和_updates
)怎么样?
因此,这是我对如何解决您的问题的建议:
import time
class Empire(object):
def __init__(self, resources):
self._possessions = {r: 0 for r in resources}
self._updates = {r: None for r in resources}
def update(self, resource, value):
if resource not in self._possessions:
raise ValueError('Your mighty Empire has no clue what {}'
' really is and how to handle'
' it.'.format(resource))
self._possessions[resource] = value
self._updates[resource] = time.time()
def get(self, resource):
if resource not in self._possessions:
raise ValueError('Your poor empire has no {}.'.format(resource))
return self._possessions[resource]
# Some use cases:
my_little_kingdom = Empire(('gold', 'celebrities', 'vanilla_ice'))
print(my_little_kingdom._updates['gold'])
my_little_kingdom.update('gold', 10)
print(my_little_kingdom.get('gold'))
print(my_little_kingdom._updates['gold'])
try:
my_little_kingdom.update('bitcoin', 42)
except ValueError as e:
print(e)
如果真的要使用奇特的__getattr__
和__setattr__
方法,可以轻松地通过以下方式扩展类:
def __getattr__(self, item):
try:
return self.get(item)
except ValueError as e:
# We are trying to get an attribute, so better
# raise the corresponding error here.
raise AttributeError(str(e))
def __setattr__(self, key, value):
if key in ('_possessions', '_updates'):
# We need to do this to avoid infinite loops.
# You do see how quickly this gets really complicated!?
return super().__setattr__(key, value)
try:
self.update(key, value)
except ValueError as e:
raise AttributeError(str(e))
__setattr__
的问题在这里变得很明显。我们需要检查 real 属性,以避免无限循环。这样的东西使您的代码非常复杂。无论如何,如果您将这两种魔术方法添加到Empire
中,则可以立即执行以下操作:
my_little_kingdom.gold = 123
print(my_little_kingdom.gold)
print(my_little_kingdom._updates['gold'])
try:
my_little_kingdom.balloons = 99
except AttributeError as e:
print(e)
当然,要创建一个漂亮的可打印帝国,只需添加以下方法:
def __str__(self):
possessions = ', '.join('{value} {key}'.format(value=p[1],
key=p[0])
for p in self._possessions.items())
return ('A glorious and filthy rich empire '
'owning {}.'.format(possessions))
现在您可以通过以下方式打印王国
print(my_little_kingdom)
看到这个:
A glorious and filthy rich empire owning 123 gold, 0 celebrities, 0 vanilla_ice.
答案 1 :(得分:0)
只要您在运行时不需要添加新资源(例如黄金,白银等),我就同意SmCaterpillar,我会避免使用__(g|s)etattr__
魔术,而应使用建议的{ {1}}方法。