Python full stack advanced programming skills 1. Class and object depth

Keywords: Python Attribute Java C

Article directory

1, Duck type and polymorphism

The concept of polymorphism is applied to strongly typed languages such as Java and C, while Python advocates "duck type".
When a dynamic language calls an instance method, it does not check the type. As long as the method exists and the parameters are correct, it can be called. This is the "duck type" of dynamic language, which does not require strict inheritance system. As long as an object "looks like a duck and walks like a duck", it can be regarded as a duck.

a = [1,2]
b = [3,4]
c = (5,7)
d = {7,8}

a.extend(b)
print(a)
for item in c:
    print(item)

Printing

[1, 2, 3, 4]
5
7

The source code of extend(self,iterable) method is

def extend(self, *args, **kwargs): # real signature unknown
    """ Extend list by appending elements from the iterable. """
    pass

There is no concrete code, the bottom layer is encapsulated in C language.
iterable:
for the object that can be iterated.
The parameters of the extend method are iteratable objects, so lists, tuples, and sets can be used as parameters of the extend method.

a = [1,2]
b = [3,4]
c = (5,7)
d = {7,8}

a.extend(c)
print(a)
b.extend(d)
print(b)

Printing

[1, 2, 5, 7]
[3, 4, 8, 7]

Polymorphism:
The type of the definition time is not the same as the type of the runtime, which becomes polymorphic.

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


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


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


animal_list = [Cat, Dog, Duck]
for animal in animal_list:
    animal().say()

Printing

I am a cat
I am a dog
I am a duck

2, Abstract base class (abc module)

1. definition

Abstract base class (ABC):
Abstract base class is a class in which pure virtual member functions are defined. The pure virtual function only provides the interface, and has no concrete implementation.
In other words, an abstract base class is a class that defines various methods without specific implementation. Any class that inherits from the abstract base class must implement these methods, otherwise it cannot be instantiated.
Characteristic:

  • Abstract base classes cannot be instantiated (objects cannot be created), usually as base classes for subclasses to inherit (cannot be instantiated);
  • The virtual function is rewritten in the subclass to implement the specific interface (the method must be rewritten in the subclass).

2. Application scenario

Determine the type of an object

For example: Sized determines whether a class implements the \\\\\\\\.

from collections.abc import Sized

class Demo(object):
    def __init__(self,name):
        self.name = name

    def __len__(self):
        #Magic method, can only use len() method after joining
        return len(self.name)

    def test(self):
        pass

d = Demo(['Tom','Jack'])
print(len(d))
#The hasattr() method is used to determine whether an object has a method
print(hasattr(d,'__len__'))
print(hasattr(d,'test'))
print(isinstance(d,Demo))
print(isinstance(d,Sized))

Printing

2
True
True
True

Forcing a subclass to implement certain methods

A mandatory subclass must implement a method:

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

    def set(self, key, value):
        pass


class RedisBase(CacheBase):
    pass


r = RedisBase()
r.get('Tom')

Method 1: throw an exception in the parent method

class CacheBase(object):
    def get(self,key):
        # pass
        raise ValueError

    def set(self,key,value):
        # pass
        raise NotImplementedError

class RedisBase(CacheBase):
    pass

r = RedisBase()
r.get('Tom')

Error will be reported during operation

Traceback (most recent call last):
  File "xxx/demo.py", line 89, in <module>
    r.get('Tom')
  File "xxx/demo.py", line 79, in get
    raise ValueError
ValueError

Override methods in the parent class:

class CacheBase(object):
    def get(self,key):
        # pass
        raise ValueError

    def set(self,key,value):
        # pass
        raise NotImplementedError

class RedisBase(CacheBase):
    # pass
    #Override method in parent class
    def get(self,key):
        pass

r = RedisBase()
r.get('Tom')

Run again without error.
Method 2: the parent class inherits the abstract base class and the method is decorated with a decorator
The parent class inherits the abstract base class so that the child class must inherit all the abstract methods specified by the parent class.

import abc

class CacheBase(metaclass=abc.ABCMeta):
    @abc.abstractmethod
    def get(self,key):
        pass
        # raise ValueError

    @abc.abstractmethod
    def set(self,key,value):
        pass
        # raise NotImplementedError

class RedisBase(CacheBase):
    #Override method in parent class
    def get(self,key):
        pass

r = RedisBase()
r.get('Tom')

Error will be reported during operation

Traceback (most recent call last):
  File "xxx/demo.py", line 94, in <module>
    r = RedisBase()
TypeError: Can't instantiate abstract class RedisBase with abstract methods set

After the subclass inherits all the methods of the parent class

import abc

class CacheBase(metaclass=abc.ABCMeta):
    @abc.abstractmethod
    def get(self,key):
        pass
        # raise ValueError

    @abc.abstractmethod
    def set(self,key,value):
        pass
        # raise NotImplementedError

class RedisBase(CacheBase):
    #Override method in parent class
    def get(self,key):
        pass

    def set(self,key,value):
        pass

r = RedisBase()
r.get('Tom')

Run again without error.
The parent method with decorator @ abc.abstractmethod must be overridden by the child class inheritance.

3, Two pairs of concept discrimination

1.isinstance and type

i = 1
s = 'hello'
print(isinstance(i,int))
print(isinstance(s,int))
print(isinstance(s,str))

Printing

True
False
True

The isinstance() method considers the inheritance relationship of the class.

class A(object):
    pass

class B(A):
    pass

b = B()
print(isinstance(b,B))
print(isinstance(b,A))

Printing

True
True

type() does not consider the inheritance relationship of the class.

class A(object):
    pass

class B(A):
    pass

b = B()
print(type(b) is B)
print(type(b) is A)

Printing

True
False

2. Class and object variables

Class properties can be found up, instance properties cannot be found down.

class A:
    #Class attribute
    aa = 1

    #Example method
    def __init__(self,x,y):
        #Instance attribute
        self.x = x
        self.y = y

a = A(1,2)
print(a.x,a.y,a.aa)
try:
    print(A.x)
except Exception as e:
    print(e.args[0])

A.aa = 11
#Equivalent to increasing self.aa = 22 when instantiating
a.aa = 22
print(a.aa)
print(A.aa)

b = A(1,2)
print(b.aa)

Printing

1 2 1
type object 'A' has no attribute 'x'
22
11
11

4, Search order and introspection mechanism of MRO algorithm

1.MRO algorithm

class A(object):
    name = 'Corley'

    def __init__(self):
        self.name = 'cl'

a =A()
print(a.name)
print(A.name)

Printing

cl
Corley

(1) Algorithm before Python 2.2: Jindian class


DFS(deep first search): A->B->D->C->E

class D(object):
    pass

class B(D):
    pass

class E(object):
    pass

class C(E):
    pass

class A(B,C):
    pass

print(A.__mro__)

Printing

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

The order is a - > b - > D - > C - > e - > object.

(2) After Python 2.2, BFS (breadth first search) was introduced


BFS:A->B->C->D

class D(object):
    pass

class B(D):
    pass

class C(D):
    pass

class A(B,C):
    pass

print(A.__mro__)

Printing

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

The order is a - > b - > C - > D - > object.

(3) After Python 2.3, python uses the C3 algorithm

C3 algorithm of Python's new class inheritance: https://www.cnblogs.com/blackmatrix/p/5644023.html.

2. Introspection mechanism

Introspection is to query the internal structure of an object through a certain mechanism.
The common introspection mechanisms (function usage) in Python are:
dir(),type(), hasattr(), isinstance().
Through these functions, we can know the type of the object, judge whether there is a property in the object and access the property of the object when the program is running.

class Person(object):
    name = 'Corley'


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

user = Student('CUFE')
#__dict does not include parent properties
print(user.__dict__)
print(user.name)
print(dir(user))

Printing

{'school_name': 'CUFE'}
Corley
['__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_name']

Again as

a = [1,2]
try:
    print(a.__dict__)
except Exception as e:
    print(e.args[0])
print(dir(a))
print(list.__dict__)

Printing

'list' object has no attribute '__dict__'
['__add__', '__class__', '__contains__', '__delattr__', '__delitem__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__iadd__', '__imul__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__reversed__', '__rmul__', '__setattr__', '__setitem__', '__sizeof__', '__str__', '__subclasshook__', 'append', 'clear', 'copy', 'count', 'extend', 'index', 'insert', 'pop', 'remove', 'reverse', 'sort']
{'__repr__': <slot wrapper '__repr__' of 'list' objects>, '__hash__': None, '__getattribute__': <slot wrapper '__getattribute__' of 'list' objects>, '__lt__': <slot wrapper '__lt__' of 'list' objects>, '__le__': <slot wrapper '__le__' of 'list' objects>, '__eq__': <slot wrapper '__eq__' of 'list' objects>, '__ne__': <slot wrapper '__ne__' of 'list' objects>, '__gt__': <slot wrapper '__gt__' of 'list' objects>, '__ge__': <slot wrapper '__ge__' of 'list' objects>, '__iter__': <slot wrapper '__iter__' of 'list' objects>, '__init__': <slot wrapper '__init__' of 'list' objects>, '__len__': <slot wrapper '__len__' of 'list' objects>, '__getitem__': <method '__getitem__' of 'list' objects>, '__setitem__': <slot wrapper '__setitem__' of 'list' objects>, '__delitem__': <slot wrapper '__delitem__' of 'list' objects>, '__add__': <slot wrapper '__add__' of 'list' objects>, '__mul__': <slot wrapper '__mul__' of 'list' objects>, '__rmul__': <slot wrapper '__rmul__' of 'list' objects>, '__contains__': <slot wrapper '__contains__' of 'list' objects>, '__iadd__': <slot wrapper '__iadd__' of 'list' objects>, '__imul__': <slot wrapper '__imul__' of 'list' objects>, '__new__': <built-in method __new__ of type object at 0x00007FFDDE682D30>, '__reversed__': <method '__reversed__' of 'list' objects>, '__sizeof__': <method '__sizeof__' of 'list' objects>, 'clear': <method 'clear' of 'list' objects>, 'copy': <method 'copy' of 'list' objects>, 'append': <method 'append' of 'list' objects>, 'insert': <method 'insert' of 'list' objects>, 'extend': <method 'extend' of 'list' objects>, 'pop': <method 'pop' of 'list' objects>, 'remove': <method 'remove' of 'list' objects>, 'index': <method 'index' of 'list' objects>, 'count': <method 'count' of 'list' objects>, 'reverse': <method 'reverse' of 'list' objects>, 'sort': <method 'sort' of 'list' objects>, '__doc__': 'Built-in mutable sequence.\n\nIf no argument is given, the constructor creates a new empty list.\nThe argument must be an iterable if specified.'}

It is easy to know that the list does not have a dict attribute, and the list object has a dict attribute.

5, super function

In class inheritance, if a method is redefined, it will overwrite the method with the same name of the parent class.
But sometimes, we want to realize the function of the parent class at the same time. At this time, we need to call the method of the parent class, which can be realized by using super.

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

class B(A):
    def __init__(self):
        print('B')
        #Inherit the parent method and print out A
        super().__init__() #Super (B, self) in Python 2

b = B()

Printing

B
A

Reflection:

Why override B's constructor and call super?

class People(object):
    def __init__(self,name,age,weight):
        self.name = name
        self.age = age
        self.weight = weight

    def speak(self):
        print('%s says: I\'m %d years old'%(self.name,self.age))


class Student(People):
    def __init__(self,name,age,weight,grade):
        super().__init__(name,age,weight)
        self.grade = grade

s = Student('Tom',18,30,3)
s.speak()

Printing

Tom says: I'm 18 years old

Or you can use the parent class name to initialize the child class, and you need the self parameter.

class People(object):
    def __init__(self,name,age,weight):
        self.name = name
        self.age = age
        self.weight = weight

    def speak(self):
        print('%s says: I\'m %d years old'%(self.name,self.age))


class Student(People):
    def __init__(self,name,age,weight,grade):
        People.__init__(self,name,age,weight)
        self.grade = grade

    def speak(self):
        print('%s says: I\'m %d years old, and I\'m in grade %d' % (self.name, self.age,self.grade))

s = Student('Tom',18,30,3)
s.speak()

Printing

Tom says: I'm 18 years old, and I'm in grade 3

Conclusion:
super is called to:
Save instantiation parameter code and avoid attributes;
Extended properties.

What is the order of super execution?

class A(object):
    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().__init__()

d = D()
print(D.__mro__)

Printing

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

Conclusion:
super calls the methods in the parent class not according to the general method, but according to the MRO algorithm, which is also the order of the MRO algorithm.
Again as

class A(object):
    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 E(object):
    def __init__(self):
        print('E')

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

d = D()
print(D.__mro__)

Printing

D
B
C
A
(<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class '__main__.E'>, <class 'object'>)
62 original articles published, 273 praised, 60000 visitors+
Private letter follow

Posted by stiano on Wed, 29 Jan 2020 04:13:51 -0800