zoukankan      html  css  js  c++  java
  • Inheritance: For Good or For Worse

    1. Subclassing Built-In Types Is Tricky

    Since Python 2.2, subclassing built-in types such as list or dict can be done but there is a major caveat: the code of the built-ins (written in C) does not call special methods overridden by user-defined classes.

    A good short description of the problem is in the documentation for PyPy, shown as below:

    Officially, CPython has no rule at all for when exactly overridden method of subclasses of built-in types get implicitly called or not. As an approximation, these methods are never called by other built-in methods of the same object. For example, an overridden __getitem__() in a subclass of dict will not be called e.g. the built-in get() method.

    Example 12-1. Our __setitem__ override is ignored by the __init__ and __update__ methods of the built-in dict.

    >>> class DoppelDict(dict):
    ...     def __setitem__(self, key, value):
    ...         super().__setitem__(key, [value] * 2)
    ...
    >>> dd = DoppelDict(one=1)      # The __init__ method inherited from dict clearly ignored that __setitem__ was overridden: the value of 'one' is not duplicated.
    >>> dd
    {'one': 1}
    >>> dd['two'] = 2   # The [] operator calls our __setitem__ and works as expected: 'two' maps to the duplicated value [2, 2]
    >>> dd
    {'one': 1, 'two': [2, 2]}
    >>> dd.update(three=3)      # The update method from dict does not use our version of __setitem__ either: the value of 'three' was not duplicated.
    >>> dd
    {'one': 1, 'two': [2, 2], 'three': 3}

    This built-in behavior is a violation of a basic rule of object-oriented programming: the search for methods should always start from the class of the target instance(self), even when the call happens inside a method implemented in a superclass. In this sad state of affairs, the __missing__ method works as documented only because it's handled as a special case.

    This problem is not limited to calls within an instance, but also happens with overridden methods of other classes that should be called by the built-in methods.

    Example 12-2. The __getitem__ of AnswerDict is bypassed by dict.update

    >>> class AnswerDict(dict):
    ...     def __getitem__(self, key):
    ...         return 42
    ... 
    >>> ad = AnswerDict(a='foo')
    >>> ad
    {'a': 'foo'}
    >>> ad['a']     # ad['a'] returns 42, as expected.
    42
    >>> d = {}
    >>> d.update(ad)
    >>> d['a']      # The dict.update method ignored our AnswerDict.__getitem__
    'foo'
    >>> d
    {'a': 'foo'}
    
    
    """
    Subclassing built-in types like dict or list or str directly is error-prone because the built-in methods mostly ignore 
    user-defined override. Instead of subclassing the built-ins, derive your classes from the collections module using
    UserDict, UserList, and UserString, which are designed to be easily extended.
    """

    If you subclass collections.UserDict instead of dict, the issues exposed in Eamples 12-1 and 12-2 are both fixed. See Example 12-3.

    Example 12-3. DoppelDict2 and AnswerDict2 work as expected because they extend UserDict and not dict.

    >>> import collections
    >>>
    >>> class DoppelDict2(collections.UserDict):
    ...     def __setitem__(self, key, value):
    ...         super().__setitem__(key, [value] * 2)
    ...
    >>> dd = DoppelDict2(one=1)
    >>> dd
    {'one': [1, 1]}
    >>> dd['two'] = 2
    >>> dd
    {'one': [1, 1], 'two': [2, 2]}
    >>> dd.update(three=3)
    >>> dd
    {'one': [1, 1], 'two': [2, 2], 'three': [3, 3]}
    >>>
    >>>
    >>> class AnswerDict2(collections.UserDict):
    ...     def __getitem__(self, key):
    ...         return 42
    ...
    >>> ad = AnswerDict2(a='foo')
    >>> ad['a']
    42
    >>> d = {}
    >>> d.update(ad)
    >>> d['a']
    42
    >>> d
    {'a': 42}
    
    """
    The problem described in this section applies only to method delegation within the C language implementation of the built-in types, and only affects user-defined classes derived directly from those types. If you subclass from a class coded in Python such as UserDict or MutableMapping, you will not be troubled by this.
    """

    2. Multiple Inheritance and Method Resolution Order

    I often check the __mro__ of classes interactively when I am studing them. Example 12-8 has some examples using familiar classes.

    Example 12-8. Inspecting the __mro__ attributes in several classes.

    >>> bool.__mro__
    (<class 'bool'>, <class 'int'>, <class 'object'>)
    >>> 
    >>> def print_mro(cls):
    ...     print(', '.join(c.__name__ for c in cls.__mro__))
    ... 
    >>> print_mro(bool)
    bool, int, object
    >>> 
    >>> import numbers
    >>> print_mro(numbers.Integral)
    Integral, Rational, Real, Complex, Number, object
    >>> 
    >>> import io
    >>> print_mro(io.BytesIO)
    BytesIO, _BufferedIOBase, _IOBase, object
    >>> print_mro(io.BytesIO)
    BytesIO, _BufferedIOBase, _IOBase, object
    
    """
    bool inherits methods and attributes from int and object.
    """

    end...

  • 相关阅读:
    类型“System.Windows.Markup.IUriContext”在未被引用的程序集中定义 解决办法
    c# 根据文件流查看文件真实格式
    WPF ListBoxItem 使用Command命令添加双击事件
    MVVM 在使用 ItemsSource 之前,项集合必须为空
    WPF 自定义TextBox,可控制键盘输入内容
    百万数据如何在前端快速流畅显示?
    NodeJS npm 包装包失败的解决方案
    node.js express 4.x 安装指南(Express不是内部或外部命令解决方案)
    IIS8 不能在此路径中使用此配置节。如果在父级别上锁定了该节
    Npoi操作excel
  • 原文地址:https://www.cnblogs.com/neozheng/p/12359845.html
Copyright © 2011-2022 走看看