我正在尝试实现一个自定义类,该类在调用 as list(c)
or时返回不同的值dict(c)
。然而,我的印象是既list(c)
和dict(c)
使用c.__iter__()
引擎盖下?如果是这种情况,我怎样才能获得不同的行为调用list(c)
和dict(c)
?我知道这是可能的,因为 Python 字典和 Pandas DataFrames 有不同的 hevariours。
例如:
Foo类: def __init__(self): self._keys = ['a', 'b', 'd', 'd', 'e'] self._data = [10, 20, 30, 40, 50] def __iter__(self): 对于密钥,zip 中的值(self._keys, self._data): yield 键,值
打电话dict(c)
我得到我想要的:
>>> f = Foo() >>> dict(f) {'a':10,'b':20,'d':40,'e':50}
但是,我无法list(c)
打印出键(或值)列表,而是同时获取:
>>> f = Foo() >>> 列表(f) [('a', 10), ('b', 20), ('d', 30), ('d', 40), ('e', 50)]
字典的等效代码要清晰得多:
>>> f = {'a':10,'b':20,'c':30,'d':40,'e':50} >>> dict(f) {'a':10,'b':20,'c':30,'d':40,'e':50} >>> 列表(f) ['a', 'b', 'c', 'd', 'e']
显然__iter__
必须只返回键,否则list(f)
将无法工作。
Python 文档说明了dict
构造函数的以下内容:
如果给出了一个位置参数并且它是一个映射对象,则使用与映射对象相同的键值对创建一个字典。
现在,问题是什么是dict
构造函数足够的“映射” ?DataFrame
不从任何映射类继承,也不向抽象基类注册。事实证明,我们只需要支持该keys
方法:如果传递给dict
构造函数的对象有一个名为 的方法keys
,则调用该方法以提供可迭代的键[CPython 源代码]。对于每个键,值是通过索引获取的。
即dict
构造函数执行以下逻辑等效项:
if hasattr(source, 'keys'):
for k in source.keys():
self[k] = source[k]
else:
self.update(iter(source))
Run Code Online (Sandbox Code Playgroud)
使用这个我们得到
class Foo:
def __init__(self):
self._keys = ['a', 'b', 'd', 'd', 'e']
self._data = [10, 20, 30, 40, 50]
def __iter__(self):
return iter(self.keys)
def __getitem__(self, key):
idx = self._keys.index(key)
return self._data[idx]
def keys(self):
return self._keys
Run Code Online (Sandbox Code Playgroud)
测试:
>>> f = Foo()
>>> list(f)
['a', 'b', 'd', 'd', 'e']
>>> dict(f)
{'d': 30, 'e': 50, 'a': 10, 'b': 20}
Run Code Online (Sandbox Code Playgroud)
(从上面的代码可以看出,实际上没有必要继承任何东西)
但是,不能保证所有映射构造函数都以相同的方式运行——其他一些可能会调用items
——因此最兼容的方法是实现collections.abc.Mapping
它所需的所有方法并从中继承。即足以做
class Foo(collections.abc.Mapping):
...
def __getitem__(self, key):
idx = self._keys.index(key)
return self._data[idx]
def __iter__(self):
return iter(self._keys)
def __len__(self):
return len(self._keys)
Run Code Online (Sandbox Code Playgroud)