问题
I'm trying to make a dict-like class in Python.
When you make a class, you have certain methods that tell Python how to make a built-in class. For example, overriding the __int__ method tells Python what to return if the user uses int() on an instance of the class. Same for __float__. You can even control how Python would make an iterable object of the class by overriding the __iter__ method (which can help Python make lists and tuples of your class). My question is how would you tell Python how to make a dict of your custom class? There is no special __dict__ method, so how would you go about doing it? I want something like the following:
class Foo():
def __dict__(self):
return {
'this': 'is',
'a': 'dict'
}
foo = Foo()
dict(foo) # would return {'this': 'is', 'a': 'dict'}
I've tried making the class inherit from dict, but it raises an error later in the code because of subclasses trying to inherit from dict and type, so inheriting from dict isn't a possibility. Is there any other way to do it?
Also, I've overridden the __iter__ method already so that it would return a dict_keyiterator object (what gets returned when you use iter() on a dict), but it still doesn't seem to work how it should.
回答1:
dict can be called with an iterable of pairs, so if you design your __iter__ to return an iterable of tuples, your example works as you'd like:
class Foo:
def __iter__(self):
yield from {
'this': 'is',
'a': 'dict'
}.items()
dict(Foo())
{'a': 'dict', 'this': 'is'}
If you want your class to behave like a python dictionary, in that iterating over an instance iterates over its keys, you can implement the interface defined by abc.Mapping.
You can do this either by implementing __getitem__, __iter__, and __len__, and inheriting from abc.Mapping, or by implementing all of __getitem__, __iter__, __len__ __contains__, keys, items, values, get, __eq__, and __ne__.
回答2:
Although the approach in the answer from @ForeverWintr is fairly clever and works, I think it's a little obscure since it takes advantage of some of the arcane details about the attributes of the argument passed to the dict class constructor.
For that reason a better approach might be what I was saying in my comments about just adding a method that does what you want (plus shows how simply giving it a name indicating exactly what's going on).
Example code:
class Foo:
def to_dict(self):
return {'this': 'is', 'more': 'clear'}
print( Foo().to_dict() ) # -> {'this': 'is', 'more': 'clear'}
来源:https://stackoverflow.com/questions/46626955/override-dict-on-class