【问题标题】:What is the dictionary equivalent of inheritance in python?python中继承的字典等价物是什么?
【发布时间】:2020-05-12 00:24:57
【问题描述】:

你会如何把这两个类写成字典?

class Base:
   x = 10

class Derived(Base):
   y = 20

对于第一类,等效的字典是:

Base = {"x": 10}

第二个呢?

【问题讨论】:

  • 在字典中Base 只是一个名字,它与类无关。请更好地解释您要做什么。
  • 如果你喜欢这种类比,那么它可能类似于Derived = Base.update({'y': 20})
  • 如果你正在寻找类似字典的基类,你应该看看这个:docs.python.org/3/library/…
  • 字典不等同于类。没有直接等价于继承。当然,你可以重新实现这样的东西,但你为什么要这样做呢?
  • 没有真正意义上的“等价物”。很可能实现您所追求的任何行为,但不清楚具体是什么,因为您的问题建立在一个错误的假设之上,即类和数据对象(如 dicts)通常以这种方式复杂化。如果您可以更详细地描述您对“字典继承”的期望行为,那将有助于回答

标签: python class dictionary inheritance


【解决方案1】:

如果你所说的“字典等价物”是指类和实例属性必须存储在映射中,那么你是对的。如果你认为 Python 数据模型像字典一样简单,那你就错了。

映射有一个特殊的属性__dict__

object.__dict__

用于存储对象(可写)属性的字典或其他映射对象。

但是你必须区分类属性和实例属性:

>>> class A:
...    class_attr = 5
...    def __init__(self): self.instance_attr = 10
...
>>> A.__dict__
mappingproxy({'__module__': '__main__', 'class_attr': 5, '__init__': <function A.__init__ at ...>, '__dict__': <attribute '__dict__' of 'A' objects>, '__weakref__': <attribute '__weakref__' of 'A' objects>, '__doc__': None})
>>> a = A()
>>> a.__dict__
{'instance_attr': 10}

如您所见,class_attrclass A 的映射中,而instance_attrinstance a 的映射中。

你可以从实例上升到它的类:

>>> a.__class__.__dict__
mappingproxy({'__module__': '__main__', 'class_attr': 5, '__init__': <function A.__init__ at ...>, '__dict__': <attribute '__dict__' of 'A' objects>, '__weakref__': <attribute '__weakref__' of 'A' objects>, '__doc__': None})

但是Python可以解析来自实例的类属性调用,即使属性不在实例的映射中

>>> a.class_attr
5

这是黑魔法!

让我们试试你的例子:

>>> class Base:
...     x = 10
...
>>> class Derived(Base):
...     y = 20
...
>>> Base.__dict__
mappingproxy({'__module__': '__main__', 'x': 10, '__dict__': <attribute '__dict__' of 'Base' objects>, '__weakref__': <attribute '__weakref__' of 'Base' objects>, '__doc__': None})
>>> Derived.__dict__
mappingproxy({'__module__': '__main__', 'y': 20, '__doc__': None})

如您所见,字典是正交的,因为 xy 是类实例。因此,x 不在Derived 的映射中。让我们回答您提出的问题:Derived 等效字典是:

Derived: {"y": 10}

但是:

>>> Derived.x
10

更多的黑魔法!

让我们试试实例属性:

>>> class Base:
...     def __init__(self):
...         self.x = 10
...
>>> class Derived(Base):
...     def __init__(self):
...         self.y = 20
...
>>> b = Base()
>>> b.__dict__
{'x': 10}
>>> d = Derived()
>>> d.__dict__
{'y': 20}

但这一次,它不会像上面那样工作:

>>> d.x
Traceback (most recent call last):
...
AttributeError: 'Derived' object has no attribute 'x'

您需要通过显式调用其父初始化程序来修复Derived.__init__ 方法:

>>> class Derived(Base):
...     def __init__(self):
...         super(Derived, self).__init__()
...         self.y = 20
...

你得到:

>>> d = Derived()
>>> d.__dict__
{'x': 10, 'y': 20}
>>> d.x
10

我希望您确信实例、类和字典是完全不同的,尽管它们共享映射的概念。 Python Data Model documentation 中解释了主要区别(即“黑魔法”),您可以在 SO:What is getattr() exactly and how do I use it? 上找到一些有用的问题/答案。


奖励:您在评论中写道

class A:
    def __init__(self):
        self.x = 10

a = A()
print(a.x)

可以写成:

def init(obj):
    obj["x"] = 10

A = {"init": init}
a = dict(A)
a["init"](a)
print(a["x"])

但这是类和实例属性之间的混淆,因为init 不是实例a 的属性,而是类A 的属性。你应该写:

A = {"init": init}
a = {}
A["init"](a)
print(a["x"])

请注意,a = {} 行在 A.__init__ 方法中没有可见的等效项。当您编写self.x = 10 时,self 已经初始化。这是A.__new__ 方法的工作:

>>> class A:
...     def __new__(cls):
...         o = super().__new__(cls) # this is the equivalent of a = {}
...         cls.last_created = o # store the object for test below
...         return o
...
...     def __init__(self):
...         self.x = 10
...
>>> A.last_created
Traceback (most recent call last):
...
AttributeError: type object 'A' has no attribute 'last_created'
>>> a = A()
>>> a == A.last_created
True

【讨论】:

    猜你喜欢
    • 2017-12-25
    • 2016-07-04
    • 2016-03-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-01-10
    • 1970-01-01
    相关资源
    最近更新 更多