<p>一堆有用的链接:</p>
<ol>
<li><a href="https://docs.python.org/3/reference/datamodel.html#emulating-container-types" rel="nofollow noreferrer">[Python 3.Docs]: Data model - Emulating container types</a></li>
<li><a href="https://docs.python.org/3/library/stdtypes.html#iterator-types" rel="nofollow noreferrer">[Python 3.Docs]: Built-in Types - Iterator Types</a></li>
<li><a href="https://docs.python.org/3/library/functions.html#iter" rel="nofollow noreferrer">[Python 3.Docs]: Built-in Functions - <strong>iter</strong>(<em>object[, sentinel]</em>)</a></li>
<li><a href="https://stackoverflow.com/questions/41474829/why-does-list-ask-about-len">[SO]: Why does list ask about __len__?</a>(所有答案)</li>
</ol>
<p>关键的一点是<em>list</em>构造函数使用(iterable)参数的<em>\uu len\ueem>((如果提供)来计算新的容器长度),<strong>但随后对其进行迭代(通过迭代器协议)。你知道吗</p>
<p>您的示例是以这种方式工作的(迭代了所有键,但未能找到与字典长度相等的键),因为发生了一个可怕的巧合(请记住,<em>dict</em>支持迭代器协议,并且这种情况发生在它的键(这是一个序列)上):</p>
<ul>
<li><strong>您的字典只有<em>int</em>键(以及更多)</li>
<li><strong>它们的值与它们的索引相同(按顺序)</li>
</ul>
<p>改变上述两个项目符号所表示的任何条件,都会使实际错误更具说服力。你知道吗</p>
<p>两个对象(<em>dict</em>和<em>list</em>(属于<em>tensor</em>s))都支持迭代器协议。为了使事情正常工作,您应该将它包装在<em>数据集</em>类中,并稍微调整映射类型(使用值而不是键)。<br/>代码(<em>key\u func</em>相关部分)有点复杂,但只是易于配置(如果您想更改某些内容-出于演示目的)。你知道吗</p>
<p>代码00.py:</p>
<pre class="lang-py prettyprint-override"><code>#!/usr/bin/env python3
import sys
import torch
from torch.utils.data import Dataset
from random import randint
class SimpleDataset(Dataset):
def __init__(self, x):
self.__iter = None
self.x = x
def __len__(self):
print(" __len__()")
return len(self.x)
def __getitem__(self, key):
print(" __getitem__({0:}({1:s}))".format(key, key.__class__.__name__))
try:
val = self.x[key]
print(" {0:}".format(val))
return val
except:
print(" exc")
raise #IndexError
def __iter__(self):
print(" __iter__()")
self.__iter = iter(self.x)
return self
def __next__(self):
print(" __next__()")
if self.__iter is None:
raise StopIteration
val = next(self.__iter)
if isinstance(self.x, (dict,)): # Special handling for dictionaries
val = self.x[val]
return val
def key_transformer(int_key):
return str(int_key) # You could `return int_key` to see that it also works on your original example
def dataset_example(inner, key_func=None):
if key_func is None:
key_func = lambda x: x
print("\nInner object: {0:}".format(inner))
sd = SimpleDataset(inner)
print("Dataset length: {0:d}".format(len(sd)))
print("\nIterating (old fashion way):")
for i in range(len(sd)):
print(" {0:}: {1:}".format(key_func(i), sd[key_func(i)]))
print("\nIterating (Python (iterator protocol) way):")
for element in sd:
print(" {0:}".format(element))
print("\nTry building the list:")
l = list(sd)
print(" List: {0:}\n".format(l))
def main():
dict_size = 2
for inner, func in [
(torch.randn(2, 2), None),
({key_transformer(i): randint(0, 100) for i in reversed(range(dict_size))}, key_transformer), # Reversed the key order (since Python 3.7, dicts are ordered), to test int keys
]:
dataset_example(inner, key_func=func)
if __name__ == "__main__":
print("Python {0:s} {1:d}bit on {2:s}\n".format(" ".join(item.strip() for item in sys.version.split("\n")), 64 if sys.maxsize > 0x100000000 else 32, sys.platform))
main()
print("\nDone.")
</code></pre>
<p><strong>输出</strong>:</p>
<blockquote>
<pre class="lang-bat prettyprint-override"><code>[cfati@CFATI-5510-0:e:\Work\Dev\StackOverflow\q059091544]> "e:\Work\Dev\VEnvs\py_064_03.07.03_test0\Scripts\python.exe" code00.py
Python 3.7.3 (v3.7.3:ef4ec6ed12, Mar 25 2019, 22:22:05) [MSC v.1916 64 bit (AMD64)] 64bit on win32
Inner object: tensor([[ 0.6626, 0.1107],
[-0.1118, 0.6177]])
__len__()
Dataset length: 2
Iterating (old fashion way):
__len__()
__getitem__(0(int))
tensor([0.6626, 0.1107])
0: tensor([0.6626, 0.1107])
__getitem__(1(int))
tensor([-0.1118, 0.6177])
1: tensor([-0.1118, 0.6177])
Iterating (Python (iterator protocol) way):
__iter__()
__next__()
tensor([0.6626, 0.1107])
__next__()
tensor([-0.1118, 0.6177])
__next__()
Try building the list:
__iter__()
__len__()
__next__()
__next__()
__next__()
List: [tensor([0.6626, 0.1107]), tensor([-0.1118, 0.6177])]
Inner object: {'1': 86, '0': 25}
__len__()
Dataset length: 2
Iterating (old fashion way):
__len__()
__getitem__(0(str))
25
0: 25
__getitem__(1(str))
86
1: 86
Iterating (Python (iterator protocol) way):
__iter__()
__next__()
86
__next__()
25
__next__()
Try building the list:
__iter__()
__len__()
__next__()
__next__()
__next__()
List: [86, 25]
Done.
</code></pre>
</blockquote>
<p>您可能还需要检查<a href="https://pytorch.org/docs/stable/_modules/torch/utils/data/dataset.html" rel="nofollow noreferrer">[PyTorch]: SOURCE CODE FOR TORCH.UTILS.DATA.DATASET</a>(<em>IterableDataset</em>)。你知道吗</p>