假设我在python中有一个函数,该函数返回带有一些对象的字典。
class MyObj:
pass
def my_func():
o = MyObj()
return {'some string' : o, 'additional info': 'some other text'}
在某些时候,我注意到重命名密钥'some string'
是有意义的,因为它具有误导性,并且不能很好地描述此密钥实际存储的内容。但是,如果我只是更改密钥,那么使用这部分代码的人会很生气,因为我没有在折旧期间给他们时间来适应他们的代码。
所以我考虑实施弃用警告的方法是在dict周围使用薄包装器:
from warnings import warn
class MyDict(dict):
def __getitem__(self, key):
if key == 'some string':
warn('Please use the new key: `some object` instead of `some string`')
return super().__getitem__(key)
这样,我可以使用旧键和新键指向同一对象来创建字典
class MyObj:
pass
def my_func():
o = MyObj()
return MyDict({'some string' : o, 'some object' : o, 'additional info': 'some other text'})
答案 0 :(得分:11)
说实话,我认为您的解决方案没有什么特别的错误或反模式,除了my_func
必须复制每个已弃用的键及其替换(见下文)外。
您甚至可以泛化一下(以防万一您决定弃用其他键):
class MyDict(dict):
old_keys_to_new_keys = {'some string': 'some object'}
def __getitem__(self, key):
if key in self.old_keys_to_new_keys:
msg = 'Please use the new key: `{}` instead of `{}`'.format(self.old_keys_to_new_keys[key], key)
warn(msg)
return super().__getitem__(key)
class MyObj:
pass
def my_func():
o = MyObj()
return MyDict({'some string' : o, 'some object': o, 'additional info': 'some other text'})
然后
>> my_func()['some string'])
UserWarning: Please use the new key: `some object` instead of `some string`
现在要想“弃用”更多键,您要做的就是更新old_keys_to_new_keys
。
my_func
必须复制每个已弃用的键及其替换。这违反了DRY原则,并且在确实需要弃用更多键的情况下会使代码混乱(并且您将必须记住同时更新MyDict.old_keys_to_new_keys
和 my_func
)。如果可以引用Raymond Hettinger:
必须有更好的方法
可以通过对__getitem__
进行以下更改来补救:
def __getitem__(self, old_key):
if old_key in self.old_keys_to_new_keys:
new_key = self.old_keys_to_new_keys[old_key]
msg = 'Please use the new key: `{}` instead of `{}`'.format(new_key, old_key)
warn(msg)
self[old_key] = self[new_key] # be warned - this will cause infinite recursion if
# old_key == new_key but that should not really happen
# (unless you mess up old_keys_to_new_keys)
return super().__getitem__(old_key)
然后my_func
只能使用新密钥:
def my_func():
o = MyObj()
return MyDict({'some object': o, 'additional info': 'some other text'})
行为相同,使用不推荐使用的键的任何代码都将得到警告(当然,访问新键也可以):
print(my_func()['some string'])
# UserWarning: Please use the new key: `some object` instead of `some string`
# <__main__.MyObj object at 0x000002FBFF4D73C8>
print(my_func()['some object'])
# <__main__.MyObj object at 0x000002C36FCA2F28>
答案 1 :(得分:2)
正如其他人所说,您当前的方法似乎已经很好。我看到的唯一可能的警告是,MyDict
类集中了有关不赞成使用的值的所有知识。根据您的用例,您可能更愿意在定义的地方定义不推荐使用的内容。例如,您可以按照以下方式做一些事情:
from warnings import warn
class MyDict(dict):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self._deprecated_keys = {}
def __getitem__(self, key):
if key in self._deprecated_keys:
new_key = self._deprecated_keys[key]
if new_key:
warn(f'Please use the new key: `{new_key}` instead of `{key}`.')
else:
warn(f'Deprecated key: `{key}`.')
return super().__getitem__(key)
# Option A
def put_deprecated(self, key, value, new_key=None):
self._deprecated_keys[key] = new_key
self[key] = value
# Option B
def put(self, key, value, deprecated_keys=None):
self[key] = value
for deprecated_key in (deprecated_keys or []):
self[deprecated_key] = value
self._deprecated_keys[deprecated_key] = key
my_dict = MyDict()
# Option A
my_dict['new_key'] = 'value'
my_dict.put_deprecated('old_key', 'value', new_key='new_key')
# Option B
my_dict.put('new_key', 'value', deprecated_keys=['old_key'])
my_dict['old_key']
# UserWarning: Please use the new key: `new_key` instead of `old_key`.
选项A需要重复,但允许使用不推荐使用的密钥而无需替换,而选项B则更为简洁。这样做的好处是,在分配键和值的时候就可以定义新键并弃用旧键,而无需更改MyDict
。