Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/351.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Dict的子类化违反了python中面向对象编程的基本规则_Python_Oop - Fatal编程技术网

Dict的子类化违反了python中面向对象编程的基本规则

Dict的子类化违反了python中面向对象编程的基本规则,python,oop,Python,Oop,我要求更好地了解作者对特定代码部分的评论。为了更详细,我将用一个例子来说明 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 pr

我要求更好地了解作者对特定代码部分的评论。为了更详细,我将用一个例子来说明

 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]}
上面的例子摘自一本书,作者评论说“内置行为违反了面向对象编程的基本规则:搜索方法应该始终从目标实例(self)的类开始,即使调用发生在超类中实现的方法内部”


我相信作者试图传达的是,由于python忽略了由用户定义类重写的特殊方法,这违反了OOP。我想知道我的解释是否正确?。你对作者的评论还有其他解释吗?

我不能真正评论“内置行为违反了面向对象编程的基本规则:”。但是在你的代码中有两种不同的事情发生

当你这样做的时候

dd = DoppelDict(one=1)
dd['two'] = 2
这将在
MRO
中查找
\uuuuuu init\uuuuu
,并且由于您的类没有重写
\uuuuu init\uuuuu
,因此调用
超级
类的
\uuuu init\uuu
方法,即
dict

然而当你这样做的时候

dd = DoppelDict(one=1)
dd['two'] = 2
python在您已重写的
MRO
中查找
\uuuuuuu setitem\uuuuuuu
方法,因此将调用该方法并获得预期结果

它都与
super
MRO
有关。通过简单地检查
\uuu MRO\uuu
属性,可以轻松查看任何类的
MRO

In[5]: a = 100
In[6]: a.__class__.__mro__
Out[6]: (int, object)

上述示例仅适用于内置类,但也适用于任何其他自定义类。

这是一个实现细节问题-这在很大程度上取决于基类的超级构造函数所做的事情-在这种情况下,它不调用
\uuu setitem\uuu

您可以通过以下方式进行修复:

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]}
在pythons dict的情况下,它不使用
\uuuu setitem\uuuu


在“完全”OOP语言中,例如在C#中,您可能会遇到同样的问题:

公共类基
{
公共基础(字典d)
{
//如果基本构造函数在内部使用SetItem(..),它将按预期工作
//如果在子Klass中重载SetItem:
foreach(d中的KeyValuePair kvp)
设置项(kvp);
//如果基本构造函数不使用SetItem(..),则它不工作
//重载子类SetItem()方法:
//foreach(d中的KeyValuePair kvp)
//D[kvp.Key]=kvp.Value;
}
公共字典D{get;}=new Dictionary();
公共重写字符串ToString()
=>string.Join(“,”,D.Select(kvp=>$”{kvp.Key}={kvp.Value}”);
受保护的虚拟void SetItem(KeyValuePair kvp)=>D[kvp.Key]=kvp.Value;
}
公共类其他:基本类
{
公共其他(字典d):基本(d){}
//重写的实现将传入值加倍
受保护的覆盖无效设置项(KeyValuePair kvp)
=>D[kvp.Key]=2*kvp.Value;
}
您可以使用

publicstaticvoidmain(字符串[]args)
{
字典d=新字典{[“a”]=1、[“b”]=3};
基准b=新基准(d);
其他o=新的其他(d);
Console.WriteLine(b.ToString());
Console.WriteLine(o.ToString());
Console.ReadLine();
}
以及注释
Base()
-ctor实现中的任何一个

您可以获取(不使用
SetItem(..)

或(使用设置项(..)


作为输出。

否。您有责任-如果您更改
\uuuuuu setitem\uuuuuu
的行为,您还必须覆盖
\uuuuuu init\uuuuuu
。如果不是,您的参数只是传递到super()类上,那么它应该如何“看到”您更改的实现?@Anakhand这不是真的,当您执行
DoppelDict(one=1)
时,会调用super类的
\uuuu init\uuuuuu
,因为它没有被重写,而且当您实际执行
dd['two']
,正在调用在实例类本身中找到的
\uuuuuuu setitem\uuuuu
。这一切都与MRO有关。是的,就像帕特里卡特纳和罗希特说的那样。尽管您是orverriden setitem,但也需要重写init方法。我很好奇,你在读什么书?我想这本书的作者希望
dict
\uuuu init\uuuuu
方法在创建dict时会使用
\uuuuuu setitem\uuuu
,但事实并非如此。查看源代码(如果我查看了正确的函数…),它是使用
更新来完成的。“基本规则”是“不要猜测!源代码总是正确的,如果有疑问,请阅读源代码。”。我理解这个线程更多的是关于你描述的行为是否符合OOP原则。我不认为这是一般的OOP行为,而是python特有的行为。@Rohit,如果你重写init方法,你能实现类似的行为吗#2@s326280当然,我只是强调了背景中发生了什么,显然对于类似DICT的东西,你需要重写这两种方法。如果有帮助的话,请考虑支持和接受答案。在固定的<代码> DoppError字典<代码>中,它不应该是<代码> [K]=W < /代码>,而不是<代码>()。
?由于它确实使用了
\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu
@Graipher,这取决于在我的重写
\uuuuuuuuuuuuuuuuuuuu但是我从没有调用另一个函数中得到了一个更少的stackframe开关(如果你构造了数百万个东西,这可能很好)。。。一般来说,我倾向于你关于减少维护的建议——这里所需的逻辑很简单,而且它可以双向工作。@Graipher将你的建议编辑为代码Common