Глубокое понимание классов и объектов в python.

Java задняя часть Python алгоритм

Когда вы впервые изучаете Python или другие объектно-ориентированные языки программирования, вы неизбежно не будете хорошо понимать классы и объекты. поэтому сегодняПоделитесь с вами классами и объектами в python и получите глубокое понимание классов и объектов в python.

1. Утиный тип

Когда вы видите птицу, которая ходит, как утка, плавает, как утка, и крякает, как утка, тогда эту птицу можно назвать уткой. Это определение утиного типа.В питоне неважно, какого типа объект, только его поведение. Тип объекта выводится из поведения. Например, списки, кортежи, словари и т. д. Эти классы являются итерируемыми, поэтому они являются итерируемыми объектами.

from collections import Iterable
l = [1, ]
t = (1, )
d = {'d': 3}
print(isinstance(l, Iterable))
print(isinstance(t, Iterable))
print(isinstance(d, Iterable))

# 结果
True
True
True

2. Переменные класса и переменные экземпляра

Переменные класса определены в классе, но не в методе, и префикс не имеет ссылки на себя. Переменная экземпляра — это переменная, которая существует в классе со ссылкой на себя. Переменные класса являются общими для всех объектов, и при изменении в классе другие объекты также будут изменяться. Однако следует отметить, что если объект используется для ссылки на переменную класса для модификации, здесь заново создается только переменная экземпляра с тем же именем, что и у переменной класса, и она не модифицируется. Код объясняется ниже.

class Student(object):
   conutry = 'China'  # 这个是类变量

   def __init__(self, name, sex):
       self.name = name  # 这个是实例变量,也就是对象变量
       self.sex = sex  # 对象变量


s1 = Student('张三', 'man')
s2 = Student('里斯', 'woman')
print(s1.conutry)
print(s2.conutry)
print(Student.conutry)

Вышеприведенные результаты - это все три China, это легко понять, когда класс используется для ссылки на изменение

Student.conutry = 'cn'  # 这个是用类引用来进行修改

После модификации следующие три результата будут напечатаны как измененные результаты. Но как насчет того, что ниже?

s1.conutry = 'zhongguo'  # 用实例来引用进行修改

На этот раз результат другой, изменилась только переменная класса s1, а две другие не изменились. Почему это? Как упоминалось выше, когда ссылка на экземпляр используется для изменения переменной класса, это не модификация, а новая переменная. А поскольку python ищет переменные снизу вверх, он сначала найдет только что созданную переменную.

3. Порядок доступа между атрибутами класса и атрибутами экземпляра

Свойства класса — это методы и переменные, определенные в классе, а также свойства экземпляра. Порядок доступа ищется снизу вверх, и вы можете испытать его с помощью кода.

class A():
   name = 'A'
   def __init__(self):
       self.name = 'a'

a = A()
print(a.name)
# 结果
a

Поскольку сначала загружается переменная класса, а затем инициализируется объект, запускается метод __init__(), поэтому результат, очевидно, будет a. Поскольку класс не имеет наследования, он не очень сложен, но когда происходит множественное наследование, между несколькими классами становится очень сложно, и порядок доступа в это время намного сложнее. Давайте поговорим об этих двух ситуациях ниже.После освоения этих двух ситуаций, в принципе, нет проблем с другой.

(1. Подходит для поиска в глубину

A наследует B, C, B, C наследует D, E соответственно. Поиск в глубину заключается в том, чтобы сначала перейти к A, если такого атрибута в A нет, перейти к B, а затем перейти к D, чтобы найти его. Если вы не можете найти его в D, перейдите в C, чтобы найти его. Этот случай поиска в порядке, но другой случай - нет.

2) Подходит для поиска в ширину

Это то, что A наследует B, C, а B и C наследуют D. Если при этом используется алгоритм поиска в глубину, возникнет проблема, поскольку порядок поиска в глубину следующий: A->B->D->C. Это неразумно. Когда метод в D перегружен в C, B не перегружен. Если вы хотите найти метод в C, вы можете найти исходный метод в D только с помощью алгоритма поиска в глубину, поэтому это неправильно чтобы сказать, что в настоящее время необходимо использовать алгоритм поиска в ширину В настоящее время порядок поиска A->B->C->D. Но когда возникает описанная выше ситуация, ошибка возникает снова. Что мне делать тогда? python3 объединяет все алгоритмы поиска атрибутов в один алгоритм: алгоритм C3, который здесь не обсуждается, потому что он слишком сложен:) Соответствующий алгоритм будет реализован согласно соответствующей ситуации, и следующий код будет использоваться для проверки вышеописанного два соответственно ситуация

class E():
   pass

class D():
   pass

class C(E):
   pass

class B(D):
   pass

class A(B, C):
   pass

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

Атрибут __mro__ — это порядок поиска для получения атрибутов.Видно, что, как мы сказали выше, используется алгоритм поиска в глубину. посмотри на другой

class D():
   pass

class C(D):
   pass

class B(D):
   pass

class A(B, C):
   pass

print(A.__mro__)
# 结果
(<class '__main__.A'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.D'>, <class 'object'>)

В настоящее время используется алгоритм поиска в ширину, который соответствует тому, что мы только что сказали, и это алгоритм C3.

4. Действительно ли super вызывает родительский класс?

Любой, кто изучал Java, знает, что метод super() должен вызывать метод родительского класса, но не обязательно в python. Давайте сначала посмотрим на использование супер

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

class B(A):
   def __init__(self):
       # python2做法是
       # super(A, self).__init__()
       super().__init__()  # 调用父类的初始化方法
       print('B')

b = B()
# 结果
A
B

Выше приведено использование.Использование python2 и python3 отличается.Здесь мы используем только python3 для работы. Далее смотрим на реальный вызов супер.

class 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()

Вышеуказанное является множественным наследованием, подходящим для алгоритма в ширину, о котором мы упоминали ранее.Согласно нашему предыдущему пониманию, super вызывает родительскую функцию, тогда результатом будет:

          A   B   C   D

Видимо неправильно, результат такой

Вы находите это странным, но знакомым? Да, это также выполняется в том же порядке поиска, что и сейчас. Если вы не верите, мы можем напечатать __mro__, чтобы узнать

Это просто воспоминание? Поскольку мы сначала печатаем родительский класс, а затем собственный, порядок обратный. Давайте посмотрим на другую ситуацию, также осуществимую

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

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

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

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

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

e = E()
print(E.__mro__)
# 结果
A
C
B
D
E
(<class '__main__.E'>, <class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>)

Также как и ожидалось. Как правило, super не обязательно вызывает родительский класс, и его последовательность вызова также следует алгоритму mro, который представляет собой алгоритм поиска атрибутов, совместимый с упомянутым выше алгоритмом C3.

Если у вас есть какие-либо вопросы, задавайте их в области сообщений или указывайте на неуместные места.

p.s. Если вы считаете, что статья хорошая, ставьте лайк и перешлите в поддержку

Изучайте Python каждый день

Код — это не только баги, но и красота и веселье