Chapter 2. In-depth classes and objects

Chapter 2. In-depth classes and objects

2.1. Duck types and polymorphism

"When you see a bird that walks like a duck, swims like a duck, and calls like a duck, then this bird can be called a duck."

 We don't care about the type of the object, whether it is a duck or not, we only care about behavior.

Example 1:

# Simple example of duck type and polymorphism

class Dog(object):
    def say(self):
        print('a dog')


class Cat(object):
    def say(self):
        print('a cat')

class Duck(object):
    def say(self):
        print('a duck')

animal_list = [Dog,Cat,Duck]

for animal in animal_list:
    animal().say()


#operation result

a dog
a cat
a duck

 Example 2:

As long as the class implements the __getitem__ method, it is iterable and does not care about the object itself, only the behavior, and then it can be used as the parameter of extend.

class Company(object):
    def __init__(self, employee_list):
        self.employee = employee_list

    def __getitem__(self, item):
        return self.employee[item]


company = Company(["11", "22", "33"])

a = ['derek1','derek2']

name_set = set()

name_set.add('tom1')
name_set.add(('tom2'))

#extend parameter introduction
#def extend(self, iterable): # real signature unknown; restored from __doc__
    #""" L.extend(iterable) -> None - extend list by appending elements from the iterable """


#extend can add any iterable parameters, add a magic function __getitem__ to the class, the class becomes iterable, so you can extend it in
a.extend(company)
print(a) #['derek1','derek2', '11', '22', '33']

a.extend(name_set)
print(a) #['derek1','derek2', '11', '22', '33','tom2','tom1']

2.2. Abstract base class (abc module)

The role of abstract base class is similar to the interface in JAVA. Define various methods in the interface, and then inherit the various classes of the interface to implement specific methods. An abstract base class is a class that defines various methods without specific implementation. Any class that inherits from an abstract base class must implement these methods, otherwise it cannot be instantiated

(1) There is a certain attribute when judging the class

#Determine whether the class has a certain attribute

class Company(object):
    def __init__(self, employee_list):
        self.employee = employee_list

    def __len__(self):
        return len(self.employee)

com = Company(["11", "22", "33"])

#hasattr Determine whether a class has certain attributes, and methods are also attributes of the class
print(hasattr(com,"__len__")) #True

#Although it can be judged by hasattr, the correct way is to define an abstract base class

#In some cases, we want to determine the type of an object, we can use the abstract base class
from collections.abc import Sized
print(isinstance(com,Sized)) #True
# print(len(com))
class Sized(metaclass=ABCMeta):

    __slots__ = ()

    @abstractmethod
    def __len__(self):
        return 0

    @classmethod
    def __subclasshook__(cls, C):
        if cls is Sized:
            return _check_methods(C, "__len__")
        return NotImplemented

(2) abc module

Simple abstract base class instance

#Simulate an abstract base class

#Write an abstract base class, its subclass must rewrite the method in the abstract base class
import abc
#Define an abstract base class
class CacheBase(metaclass=abc.ABCMeta):
    @abc.abstractclassmethod
    def get(self,key):
        pass

    @abc.abstractclassmethod
    def set(self,key,value):
        pass

#Subclass, there must be methods in the abstract base class, get and set
#False entry without writing the set method will report an error

class RedisCache(CacheBase):
    def get(self,key):
        pass
    
    # def set(self,key,value):
    # pass

redis_cache = RedisCache()
#Simulate an abstract base class

#Write an abstract base class, its subclass must rewrite the method in the abstract base class
import abc
#Define an abstract base class
class CacheBase(metaclass=abc.ABCMeta):
    @abc.abstractclassmethod
    def get(self,key):
        pass

    @abc.abstractclassmethod
    def set(self,key,value):
        pass

#Subclass, there must be methods in the abstract base class, get and set
#False entry without writing the set method will report an error

class RedisCache(CacheBase):
    def get(self,key):
        pass

    def set(self,key,value):
        pass

redis_cache = RedisCache()

 (3) All abstract base classes in abc

from collections.abc import *

All abstract base classes

# Copyright 2007 Google, Inc. All Rights Reserved.
# Licensed to PSF under a Contributor Agreement.

"""Abstract Base Classes (ABCs) for collections, according to PEP 3119.

Unit tests are in test_collections.
"""

from abc import ABCMeta, abstractmethod
import sys

__all__ = ["Awaitable", "Coroutine",
           "AsyncIterable", "AsyncIterator", "AsyncGenerator",
           "Hashable", "Iterable", "Iterator", "Generator", "Reversible",
           "Sized", "Container", "Callable", "Collection",
           "Set", "MutableSet",
           "Mapping", "MutableMapping",
           "MappingView", "KeysView", "ItemsView", "ValuesView",
           "Sequence", "MutableSequence",
           "ByteString",
           ]

# This module has been renamed from collections.abc to _collections_abc to
# speed up interpreter startup. Some of the types such as MutableMapping are
# required early but collections module imports a lot of other modules.
# See issue #19218
__name__ = "collections.abc"

# Private list of types that we want to register with the various ABCs
# so that they will pass tests like:
# it = iter(somebytearray)
# assert isinstance(it, Iterable)
# Note: in other implementations, these types might not be distinct
# and they may have their own implementation specific types that
# are not included on this list.
bytes_iterator = type(iter(b''))
bytearray_iterator = type(iter(bytearray()))
#callable_iterator = ???
dict_keyiterator = type(iter({}.keys()))
dict_valueiterator = type(iter({}.values()))
dict_itemiterator = type(iter({}.items()))
list_iterator = type(iter([]))
list_reverseiterator = type(iter(reversed([])))
range_iterator = type(iter(range(0)))
longrange_iterator = type(iter(range(1 << 1000)))
set_iterator = type(iter(set()))
str_iterator = type(iter(""))
tuple_iterator = type(iter(()))
zip_iterator = type(iter(zip()))
## views ##
dict_keys = type({}.keys())
dict_values ​​= type({}.values())
dict_items = type({}.items())
## misc ##
mappingproxy = type(type.__dict__)
generator = type((lambda: (yield))())
## coroutine ##
async def _coro(): pass
_coro = _coro()
coroutine = type(_coro)
_coro.close() # Prevent ResourceWarning
del _coro
## asynchronous generator ##
async def _ag(): yield
_ag = _ag()
async_generator = type(_ag)
del _ag


### ONE-TRICK PONIES ###

def _check_methods(C, *methods):
    mro = C.__mro__
    for method in methods:
        for B in mro:
            if method in B.__dict__:
                if B.__dict__[method] is None:
                    return NotImplemented
                break
        else:
            return NotImplemented
    return True

class Hashable(metaclass=ABCMeta):

    __slots__ = ()

    @abstractmethod
    def __hash__(self):
        return 0

    @classmethod
    def __subclasshook__(cls, C):
        if cls is Hashable:
            return _check_methods(C, "__hash__")
        return NotImplemented


class Awaitable(metaclass=ABCMeta):

    __slots__ = ()

    @abstractmethod
    def __await__(self):
        yield

    @classmethod
    def __subclasshook__(cls, C):
        if cls is Awaitable:
            return _check_methods(C, "__await__")
        return NotImplemented


class Coroutine(Awaitable):

    __slots__ = ()

    @abstractmethod
    def send(self, value):
        """Send a value into the coroutine.
        Return next yielded value or raise StopIteration.
        """
        raise StopIteration

    @abstractmethod
    def throw(self, typ, val=None, tb=None):
        """Raise an exception in the coroutine.
        Return next yielded value or raise StopIteration.
        """
        if val is None:
            if tb is None:
                raise typ
            val = typ()
        if tb is not None:
            val = val.with_traceback(tb)
        raise val

    def close(self):
        """Raise GeneratorExit inside coroutine.
        """
        try:
            self.throw(GeneratorExit)
        except (GeneratorExit, StopIteration):
            pass
        else:
            raise RuntimeError("coroutine ignored GeneratorExit")

    @classmethod
    def __subclasshook__(cls, C):
        if cls is Coroutine:
            return _check_methods(C,'__await__','send','throw','close')
        return NotImplemented


Coroutine.register(coroutine)


class AsyncIterable(metaclass=ABCMeta):

    __slots__ = ()

    @abstractmethod
    def __aiter__(self):
        return AsyncIterator()

    @classmethod
    def __subclasshook__(cls, C):
        if cls is AsyncIterable:
            return _check_methods(C, "__aiter__")
        return NotImplemented


class AsyncIterator(AsyncIterable):

    __slots__ = ()

    @abstractmethod
    async def __anext__(self):
        """Return the next item or raise StopAsyncIteration when exhausted."""
        raise StopAsyncIteration

    def __aiter__(self):
        return self

    @classmethod
    def __subclasshook__(cls, C):
        if cls is AsyncIterator:
            return _check_methods(C, "__anext__", "__aiter__")
        return NotImplemented


class AsyncGenerator(AsyncIterator):

    __slots__ = ()

    async def __anext__(self):
        """Return the next item from the asynchronous generator.
        When exhausted, raise StopAsyncIteration.
        """
        return await self.asend(None)

    @abstractmethod
    async def asend(self, value):
        """Send a value into the asynchronous generator.
        Return next yielded value or raise StopAsyncIteration.
        """
        raise StopAsyncIteration

    @abstractmethod
    async def athrow(self, typ, val=None, tb=None):
        """Raise an exception in the asynchronous generator.
        Return next yielded value or raise StopAsyncIteration.
        """
        if val is None:
            if tb is None:
                raise typ
            val = typ()
        if tb is not None:
            val = val.with_traceback(tb)
        raise val

    async def aclose(self):
        """Raise GeneratorExit inside coroutine.
        """
        try:
            await self.athrow(GeneratorExit)
        except (GeneratorExit, StopAsyncIteration):
            pass
        else:
            raise RuntimeError("asynchronous generator ignored GeneratorExit")

    @classmethod
    def __subclasshook__(cls, C):
        if cls is AsyncGenerator:
            return _check_methods(C,'__aiter__','__anext__',
                                  'asend','athrow','aclose')
        return NotImplemented


AsyncGenerator.register(async_generator)


class Iterable(metaclass=ABCMeta):

    __slots__ = ()

    @abstractmethod
    def __iter__(self):
        while False:
            yield None

    @classmethod
    def __subclasshook__(cls, C):
        if cls is Iterable:
            return _check_methods(C, "__iter__")
        return NotImplemented


class Iterator(Iterable):

    __slots__ = ()

    @abstractmethod
    def __next__(self):
        'Return the next item from the iterator. When exhausted, raise StopIteration'
        raise StopIteration

    def __iter__(self):
        return self

    @classmethod
    def __subclasshook__(cls, C):
        if cls is Iterator:
            return _check_methods(C,'__iter__','__next__')
        return NotImplemented

Iterator.register(bytes_iterator)
Iterator.register(bytearray_iterator)
#Iterator.register(callable_iterator)
Iterator.register(dict_keyiterator)
Iterator.register(dict_valueiterator)
Iterator.register(dict_itemiterator)
Iterator.register(list_iterator)
Iterator.register(list_reverseiterator)
Iterator.register(range_iterator)
Iterator.register(longrange_iterator)
Iterator.register(set_iterator)
Iterator.register(str_iterator)
Iterator.register(tuple_iterator)
Iterator.register(zip_iterator)


class Reversible(Iterable):

    __slots__ = ()

    @abstractmethod
    def __reversed__(self):
        while False:
            yield None

    @classmethod
    def __subclasshook__(cls, C):
        if cls is Reversible:
            return _check_methods(C, "__reversed__", "__iter__")
        return NotImplemented


class Generator(Iterator):

    __slots__ = ()

    def __next__(self):
        """Return the next item from the generator.
        When exhausted, raise StopIteration.
        """
        return self.send(None)

    @abstractmethod
    def send(self, value):
        """Send a value into the generator.
        Return next yielded value or raise StopIteration.
        """
        raise StopIteration

    @abstractmethod
    def throw(self, typ, val=None, tb=None):
        """Raise an exception in the generator.
        Return next yielded value or raise StopIteration.
        """
        if val is None:
            if tb is None:
                raise typ
            val = typ()
        if tb is not None:
            val = val.with_traceback(tb)
        raise val

    def close(self):
        """Raise GeneratorExit inside generator.
        """
        try:
            self.throw(GeneratorExit)
        except (GeneratorExit, StopIteration):
            pass
        else:
            raise RuntimeError("generator ignored GeneratorExit")

    @classmethod
    def __subclasshook__(cls, C):
        if cls is Generator:
            return _check_methods(C,'__iter__','__next__',
                                  'send','throw','close')
        return NotImplemented

Generator.register(generator)


class Sized(metaclass=ABCMeta):

    __slots__ = ()

    @abstractmethod
    def __len__(self):
        return 0

    @classmethod
    def __subclasshook__(cls, C):
        if cls is Sized:
            return _check_methods(C, "__len__")
        return NotImplemented


class Container(metaclass=ABCMeta):

    __slots__ = ()

    @abstractmethod
    def __contains__(self, x):
        return False

    @classmethod
    def __subclasshook__(cls, C):
        if cls is Container:
            return _check_methods(C, "__contains__")
        return NotImplemented

class Collection(Sized, Iterable, Container):

    __slots__ = ()

    @classmethod
    def __subclasshook__(cls, C):
        if cls is Collection:
            return _check_methods(C, "__len__", "__iter__", "__contains__")
        return NotImplemented

class Callable(metaclass=ABCMeta):

    __slots__ = ()

    @abstractmethod
    def __call__(self, *args, **kwds):
        return False

    @classmethod
    def __subclasshook__(cls, C):
        if cls is Callable:
            return _check_methods(C, "__call__")
        return NotImplemented


### SETS ###


class Set(Collection):

    """A set is a finite, iterable container.

    This class provides concrete generic implementations of all
    methods except for __contains__, __iter__ and __len__.

    To override the comparisons (presumably for speed, as the
    semantics are fixed), redefine __le__ and __ge__,
    then the other operations will automatically follow suit.
    """

    __slots__ = ()

    def __le__(self, other):
        if not isinstance(other, Set):
            return NotImplemented
        if len(self)> len(other):
            return False
        for elem in self:
            if elem not in other:
                return False
        return True

    def __lt__(self, other):
        if not isinstance(other, Set):
            return NotImplemented
        return len(self) <len(other) and self.__le__(other)

    def __gt__(self, other):
        if not isinstance(other, Set):
            return NotImplemented
        return len(self)> len(other) and self.__ge__(other)

    def __ge__(self, other):
        if not isinstance(other, Set):
            return NotImplemented
        if len(self) <len(other):
            return False
        for elem in other:
            if elem not in self:
                return False
        return True

    def __eq__(self, other):
        if not isinstance(other, Set):
            return NotImplemented
        return len(self) == len(other) and self.__le__(other)

    @classmethod
    def _from_iterable(cls, it):
        '''Construct an instance of the class from any iterable input.

        Must override this method if the class constructor signature
        does not accept an iterable for an input.
        '''
        return cls(it)

    def __and__(self, other):
        if not isinstance(other, Iterable):
            return NotImplemented
        return self._from_iterable(value for value in other if value in self)

    __rand__ = __and__

    def isdisjoint(self, other):
        'Return True if two sets have a null intersection.'
        for value in other:
            if value in self:
                return False
        return True

    def __or__(self, other):
        if not isinstance(other, Iterable):
            return NotImplemented
        chain = (e for s in (self, other) for e in s)
        return self._from_iterable(chain)

    __ror__ = __or__

    def __sub__(self, other):
        if not isinstance(other, Set):
            if not isinstance(other, Iterable):
                return NotImplemented
            other = self._from_iterable(other)
        return self._from_iterable(value for value in self
                                   if value not in other)

    def __rsub__(self, other):
        if not isinstance(other, Set):
            if not isinstance(other, Iterable):
                return NotImplemented
            other = self._from_iterable(other)
        return self._from_iterable(value for value in other
                                   if value not in self)

    def __xor__(self, other):
        if not isinstance(other, Set):
            if not isinstance(other, Iterable):
                return NotImplemented
            other = self._from_iterable(other)
        return (self-other) | (other-self)

    __rxor__ = __xor__

    def _hash(self):
        """Compute the hash value of a set.

        Note that we don't define __hash__: not all sets are hashable.
        But if you define a hashable set type, its __hash__ should
        call this function.

        This must be compatible __eq__.

        All sets ought to compare equal if they contain the same
        elements, regardless of how they are implemented, and
        regardless of the order of the elements; so there's not much
        freedom for __eq__ or __hash__. We match the algorithm used
        by the built-in frozenset type.
        """
        MAX = sys.maxsize
        MASK = 2 * MAX + 1
        n = len(self)
        h = 1927868237 * (n + 1)
        h &= MASK
        for x in self:
            hx = hash(x)
            h ^= (hx ^ (hx << 16) ^ 89869747) * 3644798167
            h &= MASK
        h = h * 69069 + 907133923
        h &= MASK
        if h> MAX:
            h -= MASK + 1
        if h == -1:
            h = 590923713
        return h

Set.register(frozenset)


class MutableSet(Set):
    """A mutable set is a finite, iterable container.

    This class provides concrete generic implementations of all
    methods except for __contains__, __iter__, __len__,
    add(), and discard().

    To override the comparisons (presumably for speed, as the
    semantics are fixed), all you have to do is redefine __le__ and
    then the other operations will automatically follow suit.
    """

    __slots__ = ()

    @abstractmethod
    def add(self, value):
        """Add an element."""
        raise NotImplementedError

    @abstractmethod
    def discard(self, value):
        """Remove an element. Do not raise an exception if absent."""
        raise NotImplementedError

    def remove(self, value):
        """Remove an element. If not a member, raise a KeyError."""
        if value not in self:
            raise KeyError(value)
        self.discard(value)

    def pop(self):
        """Return the popped value. Raise KeyError if empty."""
        it = iter(self)
        try:
            value = next(it)
        except StopIteration:
            raise KeyError
        self.discard(value)
        return value

    def clear(self):
        """This is slow (creates N new iterators!) but effective."""
        try:
            while True:
                self.pop()
        except KeyError:
            pass

    def __ior__(self, it):
        for value in it:
            self.add(value)
        return self

    def __iand__(self, it):
        for value in (self-it):
            self.discard(value)
        return self

    def __ixor__(self, it):
        if it is self:
            self.clear()
        else:
            if not isinstance(it, Set):
                it = self._from_iterable(it)
            for value in it:
                if value in self:
                    self.discard(value)
                else:
                    self.add(value)
        return self

    def __isub__(self, it):
        if it is self:
            self.clear()
        else:
            for value in it:
                self.discard(value)
        return self

MutableSet.register(set)


### MAPPINGS ###


class Mapping(Collection):

    __slots__ = ()

    """A Mapping is a generic container for associating key/value
    pairs.

    This class provides concrete generic implementations of all
    methods except for __getitem__, __iter__, and __len__.

    """

    @abstractmethod
    def __getitem__(self, key):
        raise KeyError

    def get(self, key, default=None):
        'D.get(k[,d]) -> D[k] if k in D, else d. d defaults to None.'
        try:
            return self[key]
        except KeyError:
            return default

    def __contains__(self, key):
        try:
            self[key]
        except KeyError:
            return False
        else:
            return True

    def keys(self):
        "D.keys() -> a set-like object providing a view on D's keys"
        return KeysView(self)

    def items(self):
        "D.items() -> a set-like object providing a view on D's items"
        return ItemsView(self)

    def values(self):
        "D.values() -> an object providing a view on D's values"
        return ValuesView(self)

    def __eq__(self, other):
        if not isinstance(other, Mapping):
            return NotImplemented
        return dict(self.items()) == dict(other.items())

    __reversed__ = None

Mapping.register(mappingproxy)


class MappingView(Sized):

    __slots__ ='_mapping',

    def __init__(self, mapping):
        self._mapping = mapping

    def __len__(self):
        return len(self._mapping)

    def __repr__(self):
        return'{0.__class__.__name__}({0._mapping!r})'.format(self)


class KeysView(MappingView, Set):

    __slots__ = ()

    @classmethod
    def _from_iterable(self, it):
        return set(it)

    def __contains__(self, key):
        return key in self._mapping

    def __iter__(self):
        yield from self._mapping

KeysView.register(dict_keys)


class ItemsView(MappingView, Set):

    __slots__ = ()

    @classmethod
    def _from_iterable(self, it):
        return set(it)

    def __contains__(self, item):
        key, value = item
        try:
            v = self._mapping[key]
        except KeyError:
            return False
        else:
            return v is value or v == value

    def __iter__(self):
        for key in self._mapping:
            yield (key, self._mapping[key])

ItemsView.register(dict_items)


class ValuesView(MappingView):

    __slots__ = ()

    def __contains__(self, value):
        for key in self._mapping:
            v = self._mapping[key]
            if v is value or v == value:
                return True
        return False

    def __iter__(self):
        for key in self._mapping:
            yield self._mapping[key]

ValuesView.register(dict_values)


class MutableMapping(Mapping):

    __slots__ = ()

    """A MutableMapping is a generic container for associating
    key/value pairs.

    This class provides concrete generic implementations of all
    methods except for __getitem__, __setitem__, __delitem__,
    __iter__, and __len__.

    """

    @abstractmethod
    def __setitem__(self, key, value):
        raise KeyError

    @abstractmethod
    def __delitem__(self, key):
        raise KeyError

    __marker = object()

    def pop(self, key, default=__marker):
        '''D.pop(k[,d]) -> v, remove specified key and return the corresponding value.
          If key is not found, d is returned if given, otherwise KeyError is raised.
        '''
        try:
            value = self[key]
        except KeyError:
            if default is self.__marker:
                raise
            return default
        else:
            del self[key]
            return value

    def popitem(self):
        '''D.popitem() -> (k, v), remove and return some (key, value) pair
           as a 2-tuple; but raise KeyError if D is empty.
        '''
        try:
            key = next(iter(self))
        except StopIteration:
            raise KeyError
        value = self[key]
        del self[key]
        return key, value

    def clear(self):
        'D.clear() -> None. Remove all items from D.'
        try:
            while True:
                self.popitem()
        except KeyError:
            pass

    def update(*args, **kwds):
        ''' D.update([E, ]**F) -> None. Update D from mapping/iterable E and F.
            If E present and has a .keys() method, does: for k in E: D[k] = E[k]
            If E present and lacks .keys() method, does: for (k, v) in E: D[k] = v
            In either case, this is followed by: for k, v in F.items(): D[k] = v
        '''
        if not args:
            raise TypeError("descriptor'update' of'MutableMapping' object "
                            "needs an argument")
        self, *args = args
        if len(args)> 1:
            raise TypeError('update expected at most 1 arguments, got %d'%
                            len(args))
        if args:
            other = args[0]
            if isinstance(other, Mapping):
                for key in other:
                    self[key] = other[key]
            elif hasattr(other, "keys"):
                for key in other.keys():
                    self[key] = other[key]
            else:
                for key, value in other:
                    self[key] = value
        for key, value in kwds.items():
            self[key] = value

    def setdefault(self, key, default=None):
        'D.setdefault(k[,d]) -> D.get(k,d), also set D[k]=d if k not in D'
        try:
            return self[key]
        except KeyError:
            self[key] = default
        return default

MutableMapping.register(dict)


### SEQUENCES ###


class Sequence(Reversible, Collection):

    """All the operations on a read-only sequence.

    Concrete subclasses must override __new__ or __init__,
    __getitem__, and __len__.
    """

    __slots__ = ()

    @abstractmethod
    def __getitem__(self, index):
        raise IndexError

    def __iter__(self):
        i = 0
        try:
            while True:
                v = self[i]
                yield v
                i += 1
        except IndexError:
            return

    def __contains__(self, value):
        for v in self:
            if v is value or v == value:
                return True
        return False

    def __reversed__(self):
        for i in reversed(range(len(self))):
            yield self[i]

    def index(self, value, start=0, stop=None):
        '''S.index(value, [start, [stop]]) -> integer - return first index of value.
           Raises ValueError if the value is not present.
        '''
        if start is not None and start <0:
            start = max(len(self) + start, 0)
        if stop is not None and stop <0:
            stop += len(self)

        i = start
        while stop is None or i <stop:
            try:
                v = self[i]
                if v is value or v == value:
                    return i
            except IndexError:
                break
            i += 1
        raise ValueError

    def count(self, value):
        'S.count(value) -> integer - return number of occurrences of value'
        return sum(1 for v in self if v is value or v == value)

Sequence.register(tuple)
Sequence.register(str)
Sequence.register(range)
Sequence.register(memoryview)


class ByteString(Sequence):

    """This unifies bytes and bytearray.

    XXX Should add all their methods.
    """

    __slots__ = ()

ByteString.register(bytes)
ByteString.register(bytearray)


class MutableSequence(Sequence):

    __slots__ = ()

    """All the operations on a read-write sequence.

    Concrete subclasses must provide __new__ or __init__,
    __getitem__, __setitem__, __delitem__, __len__, and insert().

    """

    @abstractmethod
    def __setitem__(self, index, value):
        raise IndexError

    @abstractmethod
    def __delitem__(self, index):
        raise IndexError

    @abstractmethod
    def insert(self, index, value):
        'S.insert(index, value) - insert value before index'
        raise IndexError

    def append(self, value):
        'S.append(value) - append value to the end of the sequence'
        self.insert(len(self), value)

    def clear(self):
        'S.clear() -> None - remove all items from S'
        try:
            while True:
                self.pop()
        except IndexError:
            pass

    def reverse(self):
        'S.reverse() - reverse *IN PLACE*'
        n = len(self)
        for i in range(n//2):
            self[i], self[ni-1] = self[ni-1], self[i]

    def extend(self, values):
        'S.extend(iterable) - extend sequence by appending elements from the iterable'
        for v in values:
            self.append(v)

    def pop(self, index=-1):
        '''S.pop([index]) -> item - remove and return item at index (default last).
           Raise IndexError if list is empty or index is out of range.
        '''
        v = self[index]
        del self[index]
        return v

    def remove(self, value):
        '''S.remove(value) - remove first occurrence of value.
           Raise ValueError if the value is not present.
        '''
        del self[self.index(value)]

    def __iadd__(self, values):
        self.extend(values)
        return self

MutableSequence.register(list)
MutableSequence.register(bytearray) # Multiply inheriting, see ByteString

2.3. Use isinstance instead of type

(1) Grammar:

isinstance(object, classinfo)

Among them, object is a variable, classinfo is a type (tuple, dict, int, float, list, bool, etc.) and class

If the parameter object is an instance of classinfo class, or object is an instance of a subclass of classinfo class, return True. If object is not an object of a given type, the return result is always False.

If classinfo is not a data type or a tuple composed of data types, a TypeError exception will be raised.

(2) Simple usage of isinstance

>>> isinstance(1,int)
True
>>> 
>>> isinstance('1',str)
True
>>> 
>>> isinstance(1,list)
False

(3) The difference between type() and isinstance():

  • Common point : Both can determine the object type
  • Difference : For the type judgment of a subclass object of a class, type is not enough, but isinstance can.
class A:
    pass

class B(A):
    pass

b = B()

#Judging whether b is the type of B
print(isinstance(b,B)) #True
# b is the type of A? Yes
#Because B inherits A, isinstance will check the inheritance chain internally
print(isinstance(b,A)) #True

print(type(b) is B) #True
#b points to the B() object, although A is the parent of B, but A is another object, and their ids are not equal
print(type(b) is A) #False

2.4. Class variables and instance variables

 Python class variables and instance variables, as the name implies, class variables refer to variables associated with the class, and instance variables refer to variables associated with specific instances of the class

class A:
    #Class Variables
    bb = 11
    def __init__(self,x,y):
        #Instance variables
        self.x = x
        self.y = y

a = A(2,3)
A.bb = 111111
print(ax,ay,a.bb) # 2 3 111111
print(A.bb) # 111111

a.bb = 2222 #In fact, a new attribute bb will be created in the instance object a
print(a.bb) # 2222
print(A.bb) # 111111

2.5. Search order of class and instance attributes

class D:
    pass

class C(D):
    pass

class B(D):
    pass

class A(B,C):
    pass

#Sequence: A,B,C,D
#__mro__, Attribute lookup order of the class
print(A.__mro__) #(<class'__main__.A'>, <class'__main__.B'>, <class'__main__.C'>, <class'__main__.D'>, <class'object'> )
class D:
    pass

class E:
    pass

class C(E):
    pass

class B(D):
    pass

class A(B,C):
    pass

#Sequence: A,B,D,C,E
#__mro__, Attribute lookup order of the class
print(A.__mro__)      

#(<class'__main__.A'>, <class'__main__.B'>, <class'__main__.D'>, <class'__main__.C'>, <class'__main__.E'>, <class' object'>)

2.6. Class methods, static methods, and instance methods

Examples:

class Date():
    #Constructor
    def __init__(self,year,month,day):
        self.year = year
        self.month = month
        self.day = day

    #Instance Method
    def tomorrow(self):
        self.day += 1

    # Static method does not need to write self
    @staticmethod
    def parse_from_string(date_str):
        year, month, day = tuple(date_str.split("-"))
        # The bad thing about static methods is that they are hard-coded. If you use class methods, they won’t.
        return Date(int(year), int(month), int(day))

    #Class Method
    @classmethod
    def from_string(cls, date_str):
        year, month, day = tuple(date_str.split("-"))
        # cls: The incoming class, instead of writing the class dead like a static method
        return cls(int(year), int(month), int(day))

    def __str__(self):
        return'%s/%s/%s'%(self.year,self.month,self.day)

if __name__ == "__main__":
    new_day = Date(2018,5,9)
    #Instance Method
    new_day.tomorrow()
    print(new_day) #2018/5/10

    #Static method
    date_str = '2018-05-09'
    new_day = Date.parse_from_string(date_str)
    print(new_day) #2018/5/9

    # Class method
    date_str = '2018-05-09'
    new_day = Date.from_string(date_str)
    print(new_day) # 2018/5/9

2.7. Introspection mechanism of python objects

 In computer programming, introspection refers to the ability to examine something to determine what it is, what it knows, and what it can do. Introspection provides programmers with great flexibility and control.

class Person:
    '''Humanity'''
    name = "user"


class Student(Person):
    def __init__(self,school_name):
        self.school_name = school_name

if __name__ == "__main__":

    user = Student('仙剑')
    #Check what attributes are available through __dict__
    print(user.__dict__) #{'school_name':'仙剑'}

    print(Person.__dict__) #{'__module__':'__main__','__doc__':'human','name':'user','__dict__': <attribute'__dict__' of'Person' objects>,'__weakref__ ': <attribute'__weakref__' of'Person' objects>}

    print(Person.__doc__) #human

    #Can add attributes
    user.__dict__['school_addr'] ='Beijing'
    print(user.school_addr) #Beijing


    #dir can also view attributes, which is more powerful than __dict__
    print(dir(user))
#['__class__','__delattr__','__dict__','__dir__','__doc__','__eq__','__format__','__ge__','__getattribute__','__gt__','__hash__','__init__', '__init_subclass__','__le__','__lt__','__module__','__ne__','__new__','__reduce__','__reduce_ex__','__repr__','__setattr__','__sizeof__','__str__','__subclasshook__ ','__weakref__','name','school_addr','school_name']

2.8.super function

Super execution order

class A:
    def __init__(self):
        print('A')

class B(A):
    def __init__(self):
        print('B')
        super().__init__()


class C(A):
    def __init__(self):
        print('C')
        super().__init__()


class D(B,C):
    def __init__(self):
        print('D')
        super(D, self).__init__()

if __name__ =='__main__':
    print(D.__mro__) #(<class'__main__.D'>, <class'__main__.B'>, <class'__main__.C'>, <class'__main__.A'>, <class'object'> )
    d = D()
    
    
#Results of the
D
B
C
A

2.9. with statement (context manager)

#Context Manager
class Sample:
    def __enter__(self):
        print('enter')
        #Access to resources
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        #Release resources
        print('exit')

    def do_something(self):
        print('doing something')

# Will automatically execute enter and exit methods
with Sample() as sample:
    sample.do_something()


# operation result
enter
doing something
exit
Reference: https://cloud.tencent.com/developer/article/1139363 Chapter 2, In-Depth Classes and Objects-Cloud + Community-Tencent Cloud