When you first start learning Python or any other object-oriented programming language, it is inevitable that you will have a poor understanding of classes and objects. So todayI’m going to share a little bit about classes and objects in Python.

1. Duck type

When a bird is seen walking like a duck, swimming like a duck, and quacking like a duck, it can be called a duck. This is the definition of a duck type, and in Python, you don’t care what type the object is, you just care about its behavior. Deduce the type of the object from the behavior. Lists, tuples, dict, and so on are all iterable, so they are iterable.

from collections import可迭代

l = [1,]

t = (1.)

d = {'d'3}

print(isinstance(l, Iterable))

print(isinstance(t, Iterable))

print(isinstance(d, Iterable))



# the results

True

True

TrueCopy the code

2. Class and instance variables

Class variables are defined within the class, but not within the method, and are prefixed without self as a reference. An instance variable is a variable in a class that has self as a reference. Class variables are shared by all objects, and when you change them in a class, other objects change as well. Note, however, that if an object is used to reference a class variable, only a new instance variable with the same name as the class variable is created, not modified to. Let’s explain it in code.

class Student(object):

   conutry = 'China'  # This is a class variable



   def __init__(self, name, sex):

       self.name = name  This is an instance variable, that is, an object variable

       self.sex = sex  # object variable





s1 = Student('Joe'.'man')

s2 = Student('里斯'.'woman')

print(s1.conutry)

print(s2.conutry)

print(Student.conutry)Copy the code

The result above is all three Chinas, which is easy to know when using the class reference to change

Student.conutry = 'cn'  This is modified with a class reference

Copy the code

Print after modification The next three results are modified results. But what about this one down here?

S1. conutry = 'zhongguo' #

This time the result is different, only s1 class variable is changed, the other two are unchanged. Why is that? As mentioned above, when you modify a class variable with an instance reference, you don’t modify it; you create a new variable. And since Python looks up variables from the bottom up, it looks up new variables first.

3. Access order between class attributes and instance attributes

Class attributes are methods and variables defined in a class, as are instance attributes. The order of access is to look up from the bottom, so feel it in code.

class A(a):

   name = 'A'

   def __init__(self):

       self.name = 'a'



a = A()

print(a.name)

# the results

aCopy the code

The __init__() method is run because the class variable is loaded first and then the object is initialized, so the result is obviously a. This is not very complicated because the class has no inheritance, but when multiple inheritance occurs, it becomes very complicated between several classes, and the access order becomes much more difficult. Now let’s talk about these two cases, master these two cases, the other basically no problem.

(1. Suitable for depth-first search

A inherits B,C, B, and C inherits D and E, respectively. Depth-first search is to go to A first, if A does not have the attribute, go to B, and if not, go to D. If you can’t find it in D, go to C. This lookup case is fine, but the other case is not.

2) Suitable for breadth-first search

This is A inheriting from B,C, B, and C all inheriting from D. If the depth-first algorithm is used, there is A problem because the depth-first search order is A->B->D->C. When C overloads A method in D, B does not. If you want to find the method in C, the depth-first algorithm can only find the original method in D, so this is not correct. At this time, the breadth-first algorithm is needed, and the search order is A->B->C->D. But it can go wrong again when it comes to the above situation. What to do? Python3 consolates all attribute search algorithms into one: the C3 algorithm, which I won’t expand here because it is too complicated:) implements the corresponding algorithm according to the corresponding case, using the code to experience the above two cases respectively

class E() :

   pass



class D() :

   pass



class C(E) :

   pass



class B(D) :

   pass



class A(B.C) :

   pass



print(A.__mro__)

# the results

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

The __mro__ attribute is the search order in which the attributes are retrieved, as seen above, using the depth-first algorithm. Let’s do another one

class D() :

   pass



class C(D) :

   pass



class B(D) :

   pass



class A(B.C) :

   pass



print(A.__mro__)

# the results

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

So we’re using the breadth-first algorithm, which corresponds to what we just said, which is the C3 algorithm.

4. Is super really calling a parent class?

If you’ve learned Java, you know that super() is a method that calls a superclass method, but in Python this is not necessarily the case. Let’s look at the use of super

class A(a):

   def __init__(self):

       print('A')



class B(A):

   def __init__(self):

       # python2

       # super(A, self).__init__()

       super().__init__()  Call the initialization method of the parent class

       print('B')



b = B()

# the results

A

BCopy the code

So that’s the usage, python2 is different from python3, so we’re just going to use python3. Let’s see how super is actually called.

class A(a):

   def __init__(self):

       print('A')



class B(A):

   def __init__(self):

       super().__init__()

       print('B')



class C(A):

   def __init__(self):

       super().__init__()

       print('C')



class D(B, C):

   def __init__(self):

       super().__init__()

       print('D')



d = D()Copy the code

The top one is the multiple inheritance for the breadth-first algorithm that we talked about earlier. According to our previous understanding, super calls the parent function, so the result would be:

          A   B   C   D

It’s obviously a mistake. It’s this

Strange, but familiar? Yes, this is executed in the same order as before, if you don’t believe us we print __mro__ to find out

Is it just a flashback? Because we print the parent first and then our own, the order is reversed. It’s also possible to look at the other case

class A() :

   def __init__(self):

       print('A')



class B() :

   def __init__(self):

       super().__init__(a)

       print('B')



class C(A) :

   def __init__(self):

       super().__init__(a)

       print('C')



class D(B) :

   def __init__(self):

       super().__init__(a)

       print('D')



class E(D.C) :

   def __init__(self):

       super().__init__(a)

       print('E')



e = E()

print(E.__mro__)

# the results

A

C

B

D

E

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

It was as expected. In general, super does not necessarily call the parent class, and its call order also follows the MRO algorithm, namely the property lookup algorithm, which is the same as C3 algorithm mentioned above.

If you have any questions, please ask them in the comments section, or point out any inappropriate points

Ps: If you think the article is good, welcome to like and forward support

Daily learning python

Code is not just buggy, it’s beautiful and fun