Python 如何“完美”覆盖字典?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/3387691/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to "perfectly" override a dict?
提问by Paul Biggar
How can I make as "perfect" a subclass of dictas possible? The end goal is to have a simple dictin which the keys are lowercase.
如何使dict的子类尽可能“完美” ?最终目标是有一个简单的字典,其中的键是小写的。
It would seem that there should be some tiny set of primitives I can override to make this work, but according to all my research and attempts it seem like this isn't the case:
似乎应该有一些我可以覆盖的一小部分原语来完成这项工作,但根据我所有的研究和尝试,情况似乎并非如此:
If I override
__getitem__/__setitem__, thenget/setdon't work. How can I make them work? Surely I don't need to implement them individually?Am I preventing pickling from working, and do I need to implement
__setstate__etc?Should I just use mutablemapping(it seems one shouldn't use
UserDictorDictMixin)? If so, how? The docs aren't exactly enlightening.
如果我覆盖
__getitem__/__setitem__,则get/set不起作用。我怎样才能让它们工作?我当然不需要单独实现它们吗?我是否阻止酸洗工作,我是否需要实施
__setstate__等?我应该只使用可变映射(似乎不应该使用
UserDict或DictMixin)?如果是这样,如何?这些文档并不完全具有启发性。
Here is my first go at it, get()doesn't work and no doubt there are many other minor problems:
这是我的第一次尝试,get()不起作用,毫无疑问还有许多其他小问题:
class arbitrary_dict(dict):
"""A dictionary that applies an arbitrary key-altering function
before accessing the keys."""
def __keytransform__(self, key):
return key
# Overridden methods. List from
# https://stackoverflow.com/questions/2390827/how-to-properly-subclass-dict
def __init__(self, *args, **kwargs):
self.update(*args, **kwargs)
# Note: I'm using dict directly, since super(dict, self) doesn't work.
# I'm not sure why, perhaps dict is not a new-style class.
def __getitem__(self, key):
return dict.__getitem__(self, self.__keytransform__(key))
def __setitem__(self, key, value):
return dict.__setitem__(self, self.__keytransform__(key), value)
def __delitem__(self, key):
return dict.__delitem__(self, self.__keytransform__(key))
def __contains__(self, key):
return dict.__contains__(self, self.__keytransform__(key))
class lcdict(arbitrary_dict):
def __keytransform__(self, key):
return str(key).lower()
采纳答案by Jochen Ritzel
You can write an object that behaves like a dictquite easily with ABCs (Abstract Base Classes) from the collections.abcmodule. It even tells you if you missed a method, so below is the minimal version that shuts the ABC up.
你可以写一个对象,就像一个dict很容易与ABCS(抽象基类)从collections.abc模块。它甚至会告诉你是否错过了一个方法,所以下面是关闭 ABC 的最小版本。
from collections.abc import MutableMapping
class TransformedDict(MutableMapping):
"""A dictionary that applies an arbitrary key-altering
function before accessing the keys"""
def __init__(self, *args, **kwargs):
self.store = dict()
self.update(dict(*args, **kwargs)) # use the free update to set keys
def __getitem__(self, key):
return self.store[self.__keytransform__(key)]
def __setitem__(self, key, value):
self.store[self.__keytransform__(key)] = value
def __delitem__(self, key):
del self.store[self.__keytransform__(key)]
def __iter__(self):
return iter(self.store)
def __len__(self):
return len(self.store)
def __keytransform__(self, key):
return key
You get a few free methods from the ABC:
您可以从 ABC 获得一些免费方法:
class MyTransformedDict(TransformedDict):
def __keytransform__(self, key):
return key.lower()
s = MyTransformedDict([('Test', 'test')])
assert s.get('TEST') is s['test'] # free get
assert 'TeSt' in s # free __contains__
# free setdefault, __eq__, and so on
import pickle
# works too since we just use a normal dict
assert pickle.loads(pickle.dumps(s)) == s
I wouldn't subclass dict(or other builtins) directly. It often makes no sense, because what you actually want to do is implement the interface of a dict. And that is exactly what ABCs are for.
我不会直接子类化dict(或其他内置函数)。这通常没有意义,因为您真正想做的是实现dict. 而这正是 ABC 的用途。
回答by Aaron Hall
How can I make as "perfect" a subclass of dict as possible?
The end goal is to have a simple dict in which the keys are lowercase.
If I override
__getitem__/__setitem__, then get/set don't work. How do I make them work? Surely I don't need to implement them individually?Am I preventing pickling from working, and do I need to implement
__setstate__etc?Do I need repr, update and
__init__?Should I just use
mutablemapping(it seems one shouldn't useUserDictorDictMixin)? If so, how? The docs aren't exactly enlightening.
如何使 dict 的子类尽可能“完美”?
最终目标是有一个简单的字典,其中的键是小写的。
如果我覆盖
__getitem__/__setitem__,则 get/set 不起作用。我如何让它们工作?我当然不需要单独实现它们吗?我是否阻止酸洗工作,我是否需要实施
__setstate__等?我需要代表、更新和
__init__吗?我应该只使用
mutablemapping(似乎不应该使用UserDict或DictMixin)?如果是这样,如何?这些文档并不完全具有启发性。
The accepted answer would be my first approach, but since it has some issues,
and since no one has addressed the alternative, actually subclassing a dict, I'm going to do that here.
接受的答案将是我的第一种方法,但由于它有一些问题,而且由于没有人解决替代方案,实际上是对 a 进行子类化dict,我将在这里这样做。
What's wrong with the accepted answer?
接受的答案有什么问题?
This seems like a rather simple request to me:
这对我来说似乎是一个相当简单的要求:
How can I make as "perfect" a subclass of dict as possible? The end goal is to have a simple dict in which the keys are lowercase.
如何使 dict 的子类尽可能“完美”?最终目标是有一个简单的字典,其中的键是小写的。
The accepted answer doesn't actually subclass dict, and a test for this fails:
接受的答案实际上并没有 subclass dict,并且对此的测试失败了:
>>> isinstance(MyTransformedDict([('Test', 'test')]), dict)
False
Ideally, any type-checking code would be testing for the interface we expect, or an abstract base class, but if our data objects are being passed into functions that are testing for dict- and we can't "fix" those functions, this code will fail.
理想情况下,任何类型检查代码都将测试我们期望的接口或抽象基类,但是如果我们的数据对象被传递到正在测试的函数中dict- 而我们无法“修复”这些函数,这段代码将失败。
Other quibbles one might make:
人们可能会提出的其他狡辩:
- The accepted answer is also missing the classmethod:
fromkeys. The accepted answer also has a redundant
__dict__- therefore taking up more space in memory:>>> s.foo = 'bar' >>> s.__dict__ {'foo': 'bar', 'store': {'test': 'test'}}
- 接受的答案也缺少 classmethod:
fromkeys。 接受的答案也有多余的
__dict__- 因此在内存中占用更多空间:>>> s.foo = 'bar' >>> s.__dict__ {'foo': 'bar', 'store': {'test': 'test'}}
Actually subclassing dict
实际上子类化 dict
We can reuse the dict methods through inheritance. All we need to do is create an interface layer that ensures keys are passed into the dict in lowercase form if they are strings.
我们可以通过继承重用dict方法。我们需要做的就是创建一个接口层,确保键以小写形式传递到字典中,如果它们是字符串。
If I override
__getitem__/__setitem__, then get/set don't work. How do I make them work? Surely I don't need to implement them individually?
如果我覆盖
__getitem__/__setitem__,则 get/set 不起作用。我如何让它们工作?我当然不需要单独实现它们吗?
Well, implementing them each individually is the downside to this approach and the upside to using MutableMapping(see the accepted answer), but it's really not that much more work.
好吧,单独实现它们是这种方法的缺点和使用的优点MutableMapping(请参阅已接受的答案),但实际上并没有那么多工作。
First, let's factor out the difference between Python 2 and 3, create a singleton (_RaiseKeyError) to make sure we know if we actually get an argument to dict.pop, and create a function to ensure our string keys are lowercase:
首先,让我们找出 Python 2 和 3 之间的差异,创建一个单例 ( _RaiseKeyError) 以确保我们知道我们是否确实获得了 的参数dict.pop,并创建了一个函数以确保我们的字符串键是小写的:
from itertools import chain
try: # Python 2
str_base = basestring
items = 'iteritems'
except NameError: # Python 3
str_base = str, bytes, bytearray
items = 'items'
_RaiseKeyError = object() # singleton for no-default behavior
def ensure_lower(maybe_str):
"""dict keys can be any hashable object - only call lower if str"""
return maybe_str.lower() if isinstance(maybe_str, str_base) else maybe_str
Now we implement - I'm using superwith the full arguments so that this code works for Python 2 and 3:
现在我们实现 - 我使用super完整参数,以便此代码适用于 Python 2 和 3:
class LowerDict(dict): # dicts take a mapping or iterable as their optional first argument
__slots__ = () # no __dict__ - that would be redundant
@staticmethod # because this doesn't make sense as a global function.
def _process_args(mapping=(), **kwargs):
if hasattr(mapping, items):
mapping = getattr(mapping, items)()
return ((ensure_lower(k), v) for k, v in chain(mapping, getattr(kwargs, items)()))
def __init__(self, mapping=(), **kwargs):
super(LowerDict, self).__init__(self._process_args(mapping, **kwargs))
def __getitem__(self, k):
return super(LowerDict, self).__getitem__(ensure_lower(k))
def __setitem__(self, k, v):
return super(LowerDict, self).__setitem__(ensure_lower(k), v)
def __delitem__(self, k):
return super(LowerDict, self).__delitem__(ensure_lower(k))
def get(self, k, default=None):
return super(LowerDict, self).get(ensure_lower(k), default)
def setdefault(self, k, default=None):
return super(LowerDict, self).setdefault(ensure_lower(k), default)
def pop(self, k, v=_RaiseKeyError):
if v is _RaiseKeyError:
return super(LowerDict, self).pop(ensure_lower(k))
return super(LowerDict, self).pop(ensure_lower(k), v)
def update(self, mapping=(), **kwargs):
super(LowerDict, self).update(self._process_args(mapping, **kwargs))
def __contains__(self, k):
return super(LowerDict, self).__contains__(ensure_lower(k))
def copy(self): # don't delegate w/ super - dict.copy() -> dict :(
return type(self)(self)
@classmethod
def fromkeys(cls, keys, v=None):
return super(LowerDict, cls).fromkeys((ensure_lower(k) for k in keys), v)
def __repr__(self):
return '{0}({1})'.format(type(self).__name__, super(LowerDict, self).__repr__())
We use an almost boiler-plate approach for any method or special method that references a key, but otherwise, by inheritance, we get methods: len, clear, items, keys, popitem, and valuesfor free. While this required some careful thought to get right, it is trivial to see that this works.
我们使用的样板化的做法对任何方法或特殊方法引用的关键,但在其他方面,通过继承,我们获得方法:len,clear,items,keys,popitem,和values是免费的。虽然这需要一些仔细的思考才能正确,但很容易看到它的工作原理。
(Note that haskeywas deprecated in Python 2, removed in Python 3.)
(请注意,haskey它在 Python 2中已弃用,在 Python 3 中删除。)
Here's some usage:
下面是一些用法:
>>> ld = LowerDict(dict(foo='bar'))
>>> ld['FOO']
'bar'
>>> ld['foo']
'bar'
>>> ld.pop('FoO')
'bar'
>>> ld.setdefault('Foo')
>>> ld
{'foo': None}
>>> ld.get('Bar')
>>> ld.setdefault('Bar')
>>> ld
{'bar': None, 'foo': None}
>>> ld.popitem()
('bar', None)
Am I preventing pickling from working, and do I need to implement
__setstate__etc?
我是否阻止酸洗工作,我是否需要实施
__setstate__等?
pickling
酸洗
And the dict subclass pickles just fine:
而 dict 子类泡菜就好了:
>>> import pickle
>>> pickle.dumps(ld)
b'\x80\x03c__main__\nLowerDict\nq\x00)\x81q\x01X\x03\x00\x00\x00fooq\x02Ns.'
>>> pickle.loads(pickle.dumps(ld))
{'foo': None}
>>> type(pickle.loads(pickle.dumps(ld)))
<class '__main__.LowerDict'>
__repr__
__repr__
Do I need repr, update and
__init__?
我需要代表、更新和
__init__吗?
We defined updateand __init__, but you have a beautiful __repr__by default:
我们定义了updateand __init__,但__repr__默认情况下你有一个漂亮的:
>>> ld # without __repr__ defined for the class, we get this
{'foo': None}
However, it's good to write a __repr__to improve the debugability of your code. The ideal test is eval(repr(obj)) == obj. If it's easy to do for your code, I strongly recommend it:
但是,最好编写一个__repr__以提高代码的可调试性。理想的测试是eval(repr(obj)) == obj. 如果你的代码很容易做到,我强烈推荐它:
>>> ld = LowerDict({})
>>> eval(repr(ld)) == ld
True
>>> ld = LowerDict(dict(a=1, b=2, c=3))
>>> eval(repr(ld)) == ld
True
You see, it's exactly what we need to recreate an equivalent object - this is something that might show up in our logs or in backtraces:
你看,这正是我们需要重新创建一个等效对象 - 这可能会出现在我们的日志或回溯中:
>>> ld
LowerDict({'a': 1, 'c': 3, 'b': 2})
Conclusion
结论
Should I just use
mutablemapping(it seems one shouldn't useUserDictorDictMixin)? If so, how? The docs aren't exactly enlightening.
我应该只使用
mutablemapping(似乎不应该使用UserDict或DictMixin)?如果是这样,如何?这些文档并不完全具有启发性。
Yeah, these are a few more lines of code, but they're intended to be comprehensive. My first inclination would be to use the accepted answer, and if there were issues with it, I'd then look at my answer - as it's a little more complicated, and there's no ABC to help me get my interface right.
是的,这些代码还有几行,但它们的目的是全面的。我的第一个倾向是使用已接受的答案,如果有问题,我会查看我的答案 - 因为它有点复杂,而且没有 ABC 可以帮助我正确设置界面。
Premature optimization is going for greater complexity in search of performance.
MutableMappingis simpler - so it gets an immediate edge, all else being equal. Nevertheless, to lay out all the differences, let's compare and contrast.
过早的优化会增加搜索性能的复杂性。
MutableMapping更简单 - 所以它获得了直接的优势,其他一切都是平等的。尽管如此,为了列出所有差异,让我们进行比较和对比。
I should add that there was a push to put a similar dictionary into the collectionsmodule, but it was rejected. You should probably just do this instead:
我应该补充一点,有人试图将类似的字典放入collections模块中,但被拒绝了。你可能应该这样做:
my_dict[transform(key)]
It should be far more easily debugable.
它应该更容易调试。
Compare and contrast
比较和对比
There are 6 interface functions implemented with the MutableMapping(which is missing fromkeys) and 11 with the dictsubclass. I don't need to implement __iter__or __len__, but instead I have to implement get, setdefault, pop, update, copy, __contains__, and fromkeys- but these are fairly trivial, since I can use inheritance for most of those implementations.
有 6 个接口函数用MutableMapping(缺少fromkeys)实现,11 个用dict子类实现。我不需要实现__iter__or __len__,而是我必须实现get, setdefault, pop, update, copy, __contains__, and fromkeys- 但这些都是相当简单的,因为我可以对大多数这些实现使用继承。
The MutableMappingimplements some things in Python that dictimplements in C - so I would expect a dictsubclass to be more performant in some cases.
在MutableMapping实现了一些东西在Python是dict工具用C -所以我希望一个dict子类在某些情况下更好的性能。
We get a free __eq__in both approaches - both of which assume equality only if another dict is all lowercase - but again, I think the dictsubclass will compare more quickly.
我们__eq__在这两种方法中都获得了免费- 只有当另一个 dict 全部小写时,这两种方法才假定相等 - 但同样,我认为dict子类将比较得更快。
Summary:
概括:
- subclassing
MutableMappingis simpler with fewer opportunities for bugs, but slower, takes more memory (see redundant dict), and failsisinstance(x, dict) - subclassing
dictis faster, uses less memory, and passesisinstance(x, dict), but it has greater complexity to implement.
- 子类化
MutableMapping更简单,出现错误的机会更少,但速度更慢,占用更多内存(参见冗余字典),并且失败isinstance(x, dict) - 子类化
dict更快,使用更少的内存,并且通过isinstance(x, dict),但实现起来更复杂。
Which is more perfect? That depends on your definition of perfect.
哪个更完美?这取决于你对完美的定义。
回答by Mr_and_Mrs_D
My requirements were a bit stricter:
我的要求有点严格:
- I had to retain case info (the strings are paths to files displayed to the user, but it's a windows app so internally all operations must be case insensitive)
- I needed keys to be as small as possible (it didmake a difference in memory performance, chopped off 110 mb out of 370). This meant that caching lowercase version of keys is not an option.
- I needed creation of the data structures to be as fast as possible (again made a difference in performance, speed this time). I had to go with a builtin
- 我必须保留案例信息(字符串是显示给用户的文件的路径,但它是一个 Windows 应用程序,因此内部所有操作必须不区分大小写)
- 我需要尽可能小的键(它确实对内存性能产生了影响,从 370 中砍掉了 110 mb)。这意味着缓存小写版本的密钥不是一种选择。
- 我需要尽可能快地创建数据结构(这次再次在性能和速度上产生了差异)。我不得不使用内置
My initial thought was to substitute our clunky Path class for a case insensitive unicode subclass - but:
我最初的想法是用我们笨拙的 Path 类替换不区分大小写的 unicode 子类 - 但是:
- proved hard to get that right - see: A case insensitive string class in python
- turns out that explicit dict keys handling makes code verbose and messy - and error prone (structures are passed hither and thither, and it is not clear if they have CIStr instances as keys/elements, easy to forget plus
some_dict[CIstr(path)]is ugly)
- 事实证明很难做到这一点 - 请参阅:python 中不区分大小写的字符串类
- 事实证明,显式的 dict 键处理使代码变得冗长和凌乱 - 并且容易出错(结构被到处传递,并且不清楚它们是否将 CIStr 实例作为键/元素,容易忘记加上
some_dict[CIstr(path)]是丑陋的)
So I had finally to write down that case insensitive dict. Thanks to codeby @AaronHall that was made 10 times easier.
所以我最终不得不写下那个不区分大小写的字典。多亏了@AaronHall 的代码,这让代码变得简单了 10 倍。
class CIstr(unicode):
"""See https://stackoverflow.com/a/43122305/281545, especially for inlines"""
__slots__ = () # does make a difference in memory performance
#--Hash/Compare
def __hash__(self):
return hash(self.lower())
def __eq__(self, other):
if isinstance(other, CIstr):
return self.lower() == other.lower()
return NotImplemented
def __ne__(self, other):
if isinstance(other, CIstr):
return self.lower() != other.lower()
return NotImplemented
def __lt__(self, other):
if isinstance(other, CIstr):
return self.lower() < other.lower()
return NotImplemented
def __ge__(self, other):
if isinstance(other, CIstr):
return self.lower() >= other.lower()
return NotImplemented
def __gt__(self, other):
if isinstance(other, CIstr):
return self.lower() > other.lower()
return NotImplemented
def __le__(self, other):
if isinstance(other, CIstr):
return self.lower() <= other.lower()
return NotImplemented
#--repr
def __repr__(self):
return '{0}({1})'.format(type(self).__name__,
super(CIstr, self).__repr__())
def _ci_str(maybe_str):
"""dict keys can be any hashable object - only call CIstr if str"""
return CIstr(maybe_str) if isinstance(maybe_str, basestring) else maybe_str
class LowerDict(dict):
"""Dictionary that transforms its keys to CIstr instances.
Adapted from: https://stackoverflow.com/a/39375731/281545
"""
__slots__ = () # no __dict__ - that would be redundant
@staticmethod # because this doesn't make sense as a global function.
def _process_args(mapping=(), **kwargs):
if hasattr(mapping, 'iteritems'):
mapping = getattr(mapping, 'iteritems')()
return ((_ci_str(k), v) for k, v in
chain(mapping, getattr(kwargs, 'iteritems')()))
def __init__(self, mapping=(), **kwargs):
# dicts take a mapping or iterable as their optional first argument
super(LowerDict, self).__init__(self._process_args(mapping, **kwargs))
def __getitem__(self, k):
return super(LowerDict, self).__getitem__(_ci_str(k))
def __setitem__(self, k, v):
return super(LowerDict, self).__setitem__(_ci_str(k), v)
def __delitem__(self, k):
return super(LowerDict, self).__delitem__(_ci_str(k))
def copy(self): # don't delegate w/ super - dict.copy() -> dict :(
return type(self)(self)
def get(self, k, default=None):
return super(LowerDict, self).get(_ci_str(k), default)
def setdefault(self, k, default=None):
return super(LowerDict, self).setdefault(_ci_str(k), default)
__no_default = object()
def pop(self, k, v=__no_default):
if v is LowerDict.__no_default:
# super will raise KeyError if no default and key does not exist
return super(LowerDict, self).pop(_ci_str(k))
return super(LowerDict, self).pop(_ci_str(k), v)
def update(self, mapping=(), **kwargs):
super(LowerDict, self).update(self._process_args(mapping, **kwargs))
def __contains__(self, k):
return super(LowerDict, self).__contains__(_ci_str(k))
@classmethod
def fromkeys(cls, keys, v=None):
return super(LowerDict, cls).fromkeys((_ci_str(k) for k in keys), v)
def __repr__(self):
return '{0}({1})'.format(type(self).__name__,
super(LowerDict, self).__repr__())
Implicit vs explicit is still a problem, but once dust settles, renaming of attributes/variables to start with ci (and a big fat doc comment explaining that ci stands for case insensitive) I think is a perfect solution - as readers of the code must be fully aware that we are dealing with case insensitive underlying data structures. This will hopefully fix some hard to reproduce bugs, which I suspect boil down to case sensitivity.
隐式 vs 显式仍然是一个问题,但一旦尘埃落定,重命名属性/变量以 ci 开头(以及解释 ci 代表不区分大小写的大文档注释)我认为是一个完美的解决方案 - 因为代码的读者必须请充分意识到我们正在处理不区分大小写的底层数据结构。这有望修复一些难以重现的错误,我怀疑归结为区分大小写。
Comments/corrections welcome :)
欢迎评论/更正:)
回答by ravi404
All you will have to do is
你所要做的就是
class BatchCollection(dict):
def __init__(self, *args, **kwargs):
dict.__init__(*args, **kwargs)
OR
或者
class BatchCollection(dict):
def __init__(self, inpt={}):
super(BatchCollection, self).__init__(inpt)
A sample usage for my personal use
我个人使用的示例用法
### EXAMPLE
class BatchCollection(dict):
def __init__(self, inpt={}):
dict.__init__(*args, **kwargs)
def __setitem__(self, key, item):
if (isinstance(key, tuple) and len(key) == 2
and isinstance(item, collections.Iterable)):
# self.__dict__[key] = item
super(BatchCollection, self).__setitem__(key, item)
else:
raise Exception(
"Valid key should be a tuple (database_name, table_name) "
"and value should be iterable")
Note: tested only in python3
注意:仅在 python3 中测试
回答by Groxx
After trying out both of the toptwosuggestions, I've settled on a shady-looking middle route for Python 2.7. Maybe 3 is saner, but for me:
在尝试了前两个建议之后,我已经为 Python 2.7 确定了一条阴暗的中间路线。也许 3 更理智,但对我来说:
class MyDict(MutableMapping):
# ... the few __methods__ that mutablemapping requires
# and then this monstrosity
@property
def __class__(self):
return dict
which I really hate, but seems to fit my needs, which are:
我真的很讨厌,但似乎符合我的需求,它们是:
- can override
**my_dict- if you inherit from
dict, this bypasses your code. try it out. - this makes #2unacceptable for me at all times, as this is quite common in python code
- if you inherit from
- masquerades as
isinstance(my_dict, dict) - fully controllable behavior
- so I cannot inherit from
dict
- so I cannot inherit from
- 可以覆盖
**my_dict- 如果您继承自
dict,这将绕过您的代码。试试看。 - 这让#2在任何时候对我来说都是不可接受的,因为这在 python 代码中很常见
- 如果您继承自
- 伪装成
isinstance(my_dict, dict) - 完全可控的行为
- 所以我不能继承
dict
- 所以我不能继承
If you need to tell yourself apart from others, personally I use something like this (though I'd recommend better names):
如果您需要将自己与其他人区分开来,我个人会使用这样的方法(尽管我会推荐更好的名字):
def __am_i_me(self):
return True
@classmethod
def __is_it_me(cls, other):
try:
return other.__am_i_me()
except Exception:
return False
As long as you only need to recognize yourself internally, this way it's harder to accidentally call __am_i_medue to python's name-munging (this is renamed to _MyDict__am_i_mefrom anything calling outside this class). Slightly more private than _methods, both in practice and culturally.
只要你只需要在内部识别你自己,这样就更难__am_i_me由于 python 的名称调整而意外调用(这被重命名为_MyDict__am_i_me来自该类之外的任何调用)。_method在实践和文化上都比s更私密。
So far I have no complaints, aside from the seriously-shady-looking __class__override. I'd be thrilledto hear of any problems that others encounter with this though, I don't fully understand the consequences. But so far I've had no problems whatsoever, and this allowed me to migrate a lot of middling-quality code in lots of locations without needing any changes.
到目前为止,除了看起来非常阴暗的__class__覆盖之外,我没有任何抱怨。我很高兴听到其他人遇到的任何问题,但我不完全理解后果。但到目前为止,我没有遇到任何问题,这使我能够在许多位置迁移许多中等质量的代码,而无需进行任何更改。
As evidence: https://repl.it/repls/TraumaticToughCockatoo
作为证据:https: //repl.it/repls/TraumaticToughCockatoo
Basically: copy the current #2 option, add print 'method_name'lines to every method, and then try this and watch the output:
基本上:复制当前的 #2 option,print 'method_name'向每个方法添加行,然后尝试此操作并观察输出:
d = LowerDict() # prints "init", or whatever your print statement said
print '------'
splatted = dict(**d) # note that there are no prints here
You'll see similar behavior for other scenarios. Say your fake-dictis a wrapper around some other datatype, so there's no reasonable way to store the data in the backing-dict; **your_dictwill be empty, regardless of what every other method does.
对于其他场景,您会看到类似的行为。假设您的 fake-dict是一些其他数据类型的包装器,因此没有合理的方法将数据存储在 backing-dict 中;**your_dict将是空的,不管其他方法做什么。
This works correctly for MutableMapping, but as soon as you inherit from dictit becomes uncontrollable.
这适用于MutableMapping,但是一旦您继承dict它就变得无法控制。
Edit: as an update, this has been running without a single issue for almost two years now, on several hundred thousand (eh, might be a couple million) lines of complicated, legacy-ridden python. So I'm pretty happy with it :)
编辑:作为更新,这已经运行了近两年没有任何问题,在几十万(嗯,可能是几百万)行复杂的、遗留问题的 python 上。所以我很满意:)
Edit 2: apparently I mis-copied this or something long ago. @classmethod __class__does not work for isinstancechecks - @property __class__does: https://repl.it/repls/UnitedScientificSequence
编辑 2:显然我很久以前就错误地复制了这个或其他东西。 @classmethod __class__不适用于isinstance检查 -@property __class__是否:https: //repl.it/repls/UnitedScientificSequence

