我希望对特定代码部分的作者评论提供更好的见解。详细介绍一下,我将举例说明。
class DoppelDict(dict):
def __setitem__(self, key, value):
super().__setitem__(key, [value] * 2)
# case 1.
dd = DoppelDict(one=1)
print(dd) # {'one':1}
# case 2.
dd['two'] = 2
print(dd) # {'one':1,'two':[2,2]}
以上示例摘自一本书,作者评论说:“内置行为违反了面向对象编程的基本规则:对方法的搜索应始终从目标实例(自身)的类开始,即使调用发生在超类中实现的方法内部也是如此。
我相信作者正在尝试传达信息,因为python忽略了用户定义类重写的特殊方法,这违反了OOP。我想知道我的解释是否正确?您对作者的评论还有其他解释吗?
答案 0 :(得分:5)
我不能真正评论“内置行为违反了面向对象编程的基本规则:”。但是在您的代码中,发生了两个独立且截然不同的事情。
完成时
dd = DoppelDict(one=1)
这会在__init__
中查找MRO
,因为您的类没有覆盖__init__
,所以__init__
类的super
方法是{ {1}}被调用。
但是什么时候做
dict
python在dd['two'] = 2
中寻找已被覆盖的__setitem__
方法,因此该方法被调用并获得预期的结果。
所有与MRO
和super
有关。您只需检查MRO
属性即可轻松查看任何类的MRO
。
__mro__
以上示例仅适用于内置类,但其他任何自定义类也适用。
答案 1 :(得分:2)
这是一个实现细节问题-很大程度上取决于基类的超级构造函数的作用-在这种情况下,它不会调用__setitem__
。
您可以通过以下方式解决它:
class DoppelDict(dict):
# force it to use setitem instead of update
def __init__(self, *kargs, **kwargs):
# probably also should do something with kargs
for k,w in kwargs.items():
super().__setitem__(k,[w]*2) # see Graipher comment - for less code duplication
# one could use self[k] = w .. plus 1 function call
# but less code duplication for the special logic
def __setitem__(self, key, value):
super().__setitem__(key, [value] * 2)
# case 1.
dd = DoppelDict(one=1)
print(dd) # {'one': [1, 1]}
# case 2.
dd['two'] = 2
print(dd) # {'one': [1, 1], 'two': [2, 2]}
在python字典情况下,它不使用__setitem__
。
您可能在“完全” OOP语言中遇到相同的问题,例如在C#中:
public class Base
{
public Base(Dictionary<string, int> d)
{
// if the base constructor internally uses SetItem(..) it works as expected
// if you overload SetItem in the Child-Klasses:
foreach (KeyValuePair<string, int> kvp in d)
SetItem(kvp);
// if the base constructor does _not_ use SetItem(..) it does not work by
// overloading child classes SetItem() method:
// foreach (KeyValuePair<string, int> kvp in d)
// D[kvp.Key] = kvp.Value;
}
public Dictionary<string, int> D { get; } = new Dictionary<string, int>();
public override string ToString()
=> string.Join(",", D.Select(kvp => $"{kvp.Key}={kvp.Value}"));
protected virtual void SetItem(KeyValuePair<string, int> kvp) => D[kvp.Key] = kvp.Value;
}
public class Other : Base
{
public Other(Dictionary<string, int> d) : base(d) { }
// overridden implementation doubling the incoming value
protected override void SetItem(KeyValuePair<string, int> kvp)
=> D[kvp.Key] = 2 * kvp.Value;
}
您可以使用
对此进行测试public static void Main(string[] args)
{
Dictionary<string, int> d = new Dictionary<string, int> { ["a"] = 1, ["b"] = 3 };
Base b = new Base(d);
Other o = new Other(d);
Console.WriteLine(b.ToString());
Console.WriteLine(o.ToString());
Console.ReadLine();
}
并评论Base()
-ctor实现之一。
您要么得到(不使用SetItem(..)
)
a=1,b=3
a=1,b=3
或(使用SetItem(..)
)
a=1,b=3
a=1,b=6
作为输出。