我知道list
对象可以就地更改,但list()
每次调用它时都会返回一个独立的list
对象。现在我对输出感到困惑:
L = [1, 2, 3, 4]
# list() should
D = dict.fromkeys(L, list())
print(D) # {1: [], 2: [], 3: [], 4: []}
D[1].append(1989)
print(D) # {1: [1989], 2: [1989], 3: [1989], 4: [1989]}
所需的输出应为:
{1: [1989], 2: [], 3: [], 4: []}
答案 0 :(得分:4)
代码的问题是fromkeys
构造函数为每个键重用相同的值。因此,list()
仅评估一次。
你可以使用dict-comprehension:
D = {x:list() for x in L}
或者,如果您知道您将始终使用字典中的列表,请使用defaultdict
工厂:
from collections import defaultdict
D = defaultdict(list)
D[1].append(1989)
D[2].append(2001)
print(D) # {1: [1989], 2: [2001]}
答案 1 :(得分:2)
public static class Program
{
[STAThread]
public static void Main(params String[] arguments)
{
var bindingList = new BindingList<Record>();
Action<Object> action = (argument) =>
{
var synchronizationContext = argument as SynchronizationContext;
var dispatcher = argument as Dispatcher;
if ((synchronizationContext == null) && (dispatcher == null))
{
throw new ArgumentException(@"argument");
}
else
{
var random = new Random();
while (true)
{
Action<Object> sendOrPostCallbackAction = (state) =>
{
var record = new Record();
bindingList.Add(record);
};
var sendOrPostCallback = new SendOrPostCallback(sendOrPostCallbackAction);
// WindowsFormsSynchronizationContext.Current.Send(sendOrPostCallback, new Object());
// *** NullReferenceException
// *** Object reference not set to an instance of an object...
// *** Thought this could be found not matter where.
// *** Cause the winforms application is already started at that point in the code.
// Dispatcher.CurrentDispatcher.Invoke(sendOrPostCallback, (Object)null);
// *** InvalidOperationException
// *** Cross-thread operation not valid: Control '' accessed from a thread other than the thread it was created on.
if (dispatcher != null)
{
// Only Works if the synchronizationContext is the one used by the UI Thread...
dispatcher.Invoke(sendOrPostCallback, (Object)null);
}
else
{
// Only Works if the synchronizationContext is the one used by the UI Thread...
synchronizationContext.Send(sendOrPostCallback, (Object)null);
}
Thread.Sleep(100);
}
}
};
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
var mainForm = new MainForm
{
DataGridView = { DataSource = bindingList },
};
mainForm.ButtonPassSynchronizationContext.Click += (sender, args) =>
{
mainForm.ButtonPassSynchronizationContext.Enabled = false;
mainForm.ButtonPassDispatcher.Enabled = false;
// Of course here the synchronization context is the one used by the UI Thread...
// WindowsFormsSynchronizationContext.Current should be equal here.
var areTheSameSyncrhonizationContexts = SynchronizationContext.Current == WindowsFormsSynchronizationContext.Current;
Debug.WriteLineIf(areTheSameSyncrhonizationContexts, @"The sychronization contexts are the same.");
Task.Factory.StartNew(action, SynchronizationContext.Current);
};
mainForm.ButtonPassDispatcher.Click += (sender, args) =>
{
mainForm.ButtonPassSynchronizationContext.Enabled = false;
mainForm.ButtonPassDispatcher.Enabled = false;
// The dispatcher is indeed not null, since it is the one used by the UI Thread...
// Dispatcher.CurrentDispatcher should be not null here.
var isDispatcherNotNull = Dispatcher.CurrentDispatcher != null;
Debug.WriteLineIf(isDispatcherNotNull, @"The Current Dispatcher is not null.");
Task.Factory.StartNew(action, Dispatcher.CurrentDispatcher);
};
Application.Run(mainForm);
}
public class Record
{
public Record()
{
var random = new Random();
this._dummyProperty = random.Next(0, 100);
}
private readonly Int32 _dummyProperty;
public Int32 DummyProperty
{
get { return this._dummyProperty; }
}
}
public class MainForm : Form
{
public MainForm()
{
this.InitializeComponent();
}
public Button ButtonPassSynchronizationContext { get; private set; }
public Button ButtonPassDispatcher { get; private set; }
public DataGridView DataGridView { get; private set; }
private void InitializeComponent()
{
this.Text = @"Main Form";
this.StartPosition = FormStartPosition.CenterScreen;
this.DataGridView = new DataGridView
{
Dock = DockStyle.Fill,
AllowUserToAddRows = false,
AllowUserToDeleteRows = false,
DefaultCellStyle = { Alignment = DataGridViewContentAlignment.MiddleCenter },
AlternatingRowsDefaultCellStyle = { BackColor = Color.LightGoldenrodYellow },
};
this.ButtonPassSynchronizationContext = new Button()
{
Dock = DockStyle.Bottom,
Text = @"Pass SynchronizationContext",
};
this.ButtonPassDispatcher = new Button()
{
Dock = DockStyle.Bottom,
Text = @"Pass Dispatcher",
};
this.Controls.Add(this.DataGridView);
this.Controls.Add(this.ButtonPassSynchronizationContext);
this.Controls.Add(this.ButtonPassDispatcher);
}
}
}
被调用一次,同一list()
被用作所有键的值。
控制台记录(对于所有值,您可以看到相同的list
):
id
答案 2 :(得分:2)
您可以考虑使用defaultdict
中的collections
,它可能更合适,至少值得了解。
import collections
D = collections.defaultdict(list)
D[1].append(1989)
D[1].append(1990)
print D[1]
print D
print D[2]
print D
这将打印以下内容:
[1989, 1990]
defaultdict(<type 'list'>, {1: [1989, 1990]})
[]
defaultdict(<type 'list'>, {1: [1989, 1990], 2: []})
答案 3 :(得分:1)
list()
只被调用一次。对于旧版本的Python,您可以使用以下事实:您可以从键/值对列表初始化dict:
D = dict([(x, list()) for x in L]
从Python 2.5开始,您可以使用生成器并省略上一个示例中的列表构造:
D = dict((x, list()) for x in L)
从python 2.7和3.0开始,您可以使用最初被拒绝的python 2.3,来自PEP 274的dict理解:
D = {x: list() for x in L}