说明
《Python 教程》 持续更新中,提供建议、纠错、催更等加作者微信: gairuo123(备注:pandas教程)和关注公众号「盖若」ID: gairuo。跟作者学习,请进入 Python学习课程。欢迎关注作者出版的书籍:《深入浅出Pandas》 和 《Python之光》。
如果要自定义数据类型,继承 Python 内置 collections 模块的 UserDict,继承原生的 Dict 更加方便,这也是它们存在的意义。它们封装了相关对象,简化了子类化。Python 支持像集合模块中存在的名为 UserDict 的容器这样的字典。 此类充当字典对象的包装类。 当一个人想要使用一些修改过的功能或一些新功能创建自己的字典时,这个类很有用。 它可以被视为一种向字典添加新行为的方式。 该类将字典实例作为参数并模拟保存在常规字典中的字典。 字典可以通过这个类的 data 属性访问。
为什么不能直接从 Dict、List、String 继承呢?(以下以 UserDict 为为例,UserList、UserString 类似)
由于 Python 的内置类型可以子类化供继承,但是有个比较麻烦的问题,那就是这些都是用 C 语言编写内置类型,不会调用用户自己定义的类覆盖的特殊方法。下边我们定义了一个新的字典类,重写了 __setitem__()
特殊方法,把读取值的工作委托给内置 dict 的 __setitem__()
特殊方法,我们仅实现了将设置的值转为字符类型:
class MyDict0(dict):
def __setitem__(self, key, value):
super().__setitem__(key, str(value))
d0 = dict(a=1, b=2)
d0
# {'a': 1, 'b': 2}
d = MyDict0(a=1, b=2)
d
# {'a': 1, 'b': 2}
d.update(c=3)
d
# {'a': 1, 'b': 2, 'c': 3}
似乎出现了问题,内置类型 dict 的 __init__
和 __update__
方法会忽略我们覆盖的 __setitem__
方法。如果查看 UserDict 的源代码,会发现 update 方法是单独定义的。__init__
方法只调用自己的更新方法,该方法调用 .data 属性的更新,这使得代码更加模块化和易于维护,任何其他从 UserDict 继承的类都很容易扩展或重写 update 方法。
内置的 dict 等有时会在一些方法的实现上走一些捷径,导致我们不得不在它的子类中重写这些方法,但是 UserDict 就不会带来这些问题。
直接子类化内置类型(如 dict、list 或 str)容易出错,因为内置类型的方法通常会忽略用户覆盖的方法。不要子类化内置类型,用户自己定义的类应该继承 collections 模块
中的类,例如 UserDict、UserList 和 UserString,这些类做了特殊设计,因此易于扩展。
针对上例中的问题,我们用 UserDict 来解决:
import collections
class MyDict1(collections.UserDict):
def __setitem__(self, key, value):
super().__setitem__(key, str(value))
d = MyDict1(a=1, b=2)
d
# {'a': '1', 'b': '2'}
d.update(c=3)
d
# {'a': '1', 'b': '2', 'c': '3'}
这样才完美在实现了我们期望的需求。
UserDict 实例提供了 data 属性作为扩展,它是一个真实的字典,用于保存 UserDict 类的内容。
d.data
# {'a': '1', 'b': '2', 'c': '3'}
type(d.data)
# dict
type(d)
# __main__.MyDict1
要继承字典相关的功能不要使用内置的 dict,应该使用 UserDict,UserDict 自身不是继承 dict 而是 MutableMapping。这些问题只发生在 C 语言实现的内置类型内部的方法委托
上,但是为了让程序更加兼容有更好的执行效果,应该避免继承 dict。在这方面 PyPy 的行为比 CPython 较好些。
这个实例的内容保存在一个常规字典中,它可以通过 UserDict 实例的 data 属性来访问。 如果提供了 initialdata(初始数据),则 data 会用其内容来初始化;请注意对 initialdata 的引用将不会被保留,以允许它被用于其他目的。如果直接定义数据,如 collections.UserDict(a=1, b=2, c=3)
则意义不大。
UserDict、UserList 和 UserString 虽然没有内置类型的速度快,但是易于扩展。
简单说,内置类型的特殊方法是由 C 语言独立实现的,它们在 Python 语言界面中不存在调用关系,因此在内置类型子类化时,被重写的特殊方法只会影响该方法本身,不会影响其它特殊方法的效果。
更新时间:Nov. 12, 2021, 11:49 p.m. 标签:python dict