Объектно-ориентированный Python - строительные блоки

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

Наборы классов: поведение и состояние

Класс позволит вам связать поведение и состояние объекта. Для лучшего понимания обратите внимание на следующую диаграмму -

При обсуждении наборов классов следует обратить внимание на следующие моменты:

  • Слово behavior идентичен function - это фрагмент кода, который что-то делает (или реализует поведение)

  • Слово state идентичен variables - это место для хранения значений внутри класса.

  • Когда мы вместе утверждаем поведение и состояние класса, это означает, что класс упаковывает функции и переменные.

У классов есть методы и атрибуты

В Python создание метода определяет поведение класса. Слово метод - это ООП-имя, присвоенное функции, определенной в классе. Подводя итог -

  • Class functions - синоним methods

  • Class variables - синоним name attributes.

  • Class - план экземпляра с точным поведением.

  • Object - один из экземпляров класса, выполняющий функции, определенные в классе.

  • Type - указывает класс, к которому принадлежит экземпляр

  • Attribute - Любое значение объекта: object.attribute

  • Method - «вызываемый атрибут», определенный в классе

Например, обратите внимание на следующий фрагмент кода -

var = “Hello, John”
print( type (var)) # ‘str’> or <class 'str'>
print(var.upper()) # upper() method is called, HELLO, JOHN

Создание и воплощение

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

class MyClass(object):
   pass
# Create first instance of MyClass
this_obj = MyClass()
print(this_obj)
# Another instance of MyClass
that_obj = MyClass()
print (that_obj)

Здесь мы создали класс под названием MyClassи который не выполняет никаких задач. Аргументobject в MyClass class включает наследование классов и будет обсуждаться в следующих главах. pass в приведенном выше коде указывает, что этот блок пуст, то есть это определение пустого класса.

Создадим экземпляр this_obj из MyClass() class и распечатайте его, как показано -

<__main__.MyClass object at 0x03B08E10>
<__main__.MyClass object at 0x0369D390>

Здесь мы создали экземпляр MyClass.Шестнадцатеричный код относится к адресу, где хранится объект. Другой экземпляр указывает на другой адрес.

Теперь давайте определим одну переменную внутри класса MyClass() и получите переменную из экземпляра этого класса, как показано в следующем коде -

class MyClass(object):
   var = 9

# Create first instance of MyClass
this_obj = MyClass()
print(this_obj.var)

# Another instance of MyClass

that_obj = MyClass()
print (that_obj.var)

Вывод

Вы можете наблюдать следующий результат, когда выполняете приведенный выше код -

9
9

Поскольку экземпляр знает, из какого класса он создается, поэтому при запросе атрибута из экземпляра экземпляр ищет атрибут и класс. Это называетсяattribute lookup.

Методы экземпляра

Функция, определенная в классе, называется method.Метод экземпляра требует экземпляра для его вызова и не требует декоратора. При создании метода экземпляра первым параметром всегда являетсяself. Хотя мы можем называть его (себя) любым другим именем, рекомендуется использовать self, поскольку это соглашение об именах.

class MyClass(object):
   var = 9
   def firstM(self):
      print("hello, World")
obj = MyClass()
print(obj.var)
obj.firstM()

Вывод

Вы можете наблюдать следующий результат, когда выполняете приведенный выше код -

9
hello, World

Обратите внимание, что в приведенной выше программе мы определили метод с self в качестве аргумента. Но мы не можем вызвать метод, поскольку не объявили для него никаких аргументов.

class MyClass(object):
   def firstM(self):
      print("hello, World")
      print(self)
obj = MyClass()
obj.firstM()
print(obj)

Вывод

Вы можете наблюдать следующий результат, когда выполняете приведенный выше код -

hello, World
<__main__.MyClass object at 0x036A8E10>
<__main__.MyClass object at 0x036A8E10>

Инкапсуляция

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

  • Упрощает и упрощает понимание использования объекта, не зная внутреннего устройства.

  • Любое изменение легко управляемо.

Объектно-ориентированное программирование сильно зависит от инкапсуляции. Термины инкапсуляция и абстракция (также называемые сокрытием данных) часто используются как синонимы. Они почти синонимичны, поскольку абстракция достигается за счет инкапсуляции.

Инкапсуляция предоставляет нам механизм ограничения доступа к некоторым компонентам объекта, это означает, что внутреннее представление объекта не может быть видно извне определения объекта. Доступ к этим данным обычно достигается с помощью специальных методов -Getters и Setters.

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

class MyClass(object):
   def setAge(self, num):
      self.age = num

   def getAge(self):
      return self.age

zack = MyClass()
zack.setAge(45)
print(zack.getAge())

zack.setAge("Fourty Five")
print(zack.getAge())

Вывод

Вы можете наблюдать следующий результат, когда выполняете приведенный выше код -

45
Fourty Five

Данные должны храниться только в том случае, если они верны и действительны, с использованием конструкций обработки исключений. Как мы видим выше, нет ограничений на ввод пользователем метода setAge (). Это может быть строка, число или список. Поэтому нам нужно проверить приведенный выше код, чтобы убедиться в правильности сохранения.

class MyClass(object):
   def setAge(self, num):
      self.age = num

   def getAge(self):
      return self.age
zack = MyClass()
zack.setAge(45)
print(zack.getAge())
zack.setAge("Fourty Five")
print(zack.getAge())

Конструктор инициализации

__init__ метод неявно вызывается, как только создается экземпляр объекта класса. Это инициализирует объект.

x = MyClass()

Строка кода, показанная выше, создаст новый экземпляр и назначит этот объект локальной переменной x.

Операция создания экземпляра, то есть calling a class object, создает пустой объект. Многие классы любят создавать объекты с экземплярами, настроенными для определенного начального состояния. Следовательно, класс может определять специальный метод с именем __init __ (), как показано:

def __init__(self):
   self.data = []

Python вызывает __init__ во время создания экземпляра, чтобы определить дополнительный атрибут, который должен возникать при создании экземпляра класса, который может устанавливать некоторые начальные значения для этого объекта или запускать процедуру, требуемую при создании экземпляра. Итак, в этом примере новый инициализированный экземпляр можно получить:

x = MyClass()

Метод __init __ () может иметь один или несколько аргументов для большей гибкости. Init означает инициализацию, поскольку он инициализирует атрибуты экземпляра. Он называется конструктором класса.

class myclass(object):
   def __init__(self,aaa, bbb):
      self.a = aaa
      self.b = bbb

x = myclass(4.5, 3)
print(x.a, x.b)

Вывод

4.5 3

Атрибуты класса

Атрибут, определенный в классе, называется «атрибутами класса», а атрибуты, определенные в функции, называются «атрибутами экземпляра». При определении эти атрибуты не имеют префикса self, так как это свойство класса, а не конкретного экземпляра.

К атрибутам класса может получить доступ сам класс (имя_класса.Имя атрибута), а также экземпляры класса (имя_атрибута). Таким образом, экземпляры имеют доступ как к атрибутам экземпляра, так и к атрибутам класса.

>>> class myclass():
   age = 21
>>> myclass.age
21
>>> x = myclass()
>>> x.age
21
>>>

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

В Python есть путь поиска атрибутов. Первый - это метод, определенный внутри класса, а затем класс над ним.

>>> class myclass(object):
   classy = 'class value'
>>> dd = myclass()
>>> print (dd.classy) # This should return the string 'class value'
class value
>>>
>>> dd.classy = "Instance Value"
>>> print(dd.classy) # Return the string "Instance Value"
Instance Value
>>>
>>> # This will delete the value set for 'dd.classy' in the instance.
>>> del dd.classy
>>> >>> # Since the overriding attribute was deleted, this will print 'class
value'.

>>> print(dd.classy)
class value
>>>

Мы переопределяем атрибут класса classy в экземпляре dd. Когда он переопределен, интерпретатор Python считывает переопределенное значение. Но как только новое значение удаляется с помощью 'del', переопределенное значение больше не присутствует в экземпляре, и, следовательно, поиск идет на уровень выше и получает его из класса.

Работа с данными классов и экземпляров

В этом разделе давайте разберемся, как данные класса связаны с данными экземпляра. Мы можем хранить данные либо в классе, либо в экземпляре. Когда мы проектируем класс, мы решаем, какие данные принадлежат экземпляру и какие данные должны храниться в общем классе.

Экземпляр может получить доступ к данным класса. Если мы создадим несколько экземпляров, то эти экземпляры смогут получить доступ к своим индивидуальным значениям атрибутов, а также ко всем данным класса.

Таким образом, данные класса - это данные, которые используются всеми экземплярами. Соблюдайте приведенный ниже код для лучшего понимания -

class InstanceCounter(object):
   count = 0 # class attribute, will be accessible to all instances
   def __init__(self, val):
      self.val = val
      InstanceCounter.count +=1 # Increment the value of class attribute, accessible through class name
# In above line, class ('InstanceCounter') act as an object
   def set_val(self, newval):
      self.val = newval

   def get_val(self):
      return self.val

   def get_count(self):
      return InstanceCounter.count
a = InstanceCounter(9)
b = InstanceCounter(18)
c = InstanceCounter(27)

for obj in (a, b, c):
   print ('val of obj: %s' %(obj.get_val())) # Initialized value ( 9, 18, 27)
   print ('count: %s' %(obj.get_count())) # always 3

Вывод

val of obj: 9
count: 3
val of obj: 18
count: 3
val of obj: 27
count: 3

Короче говоря, атрибуты класса одинаковы для всех экземпляров класса, тогда как атрибуты экземпляра являются индивидуальными для каждого экземпляра. Для двух разных экземпляров у нас будет два разных атрибута экземпляра.

class myClass:
   class_attribute = 99

   def class_method(self):
      self.instance_attribute = 'I am instance attribute'

print (myClass.__dict__)

Вывод

Вы можете наблюдать следующий результат, когда выполняете приведенный выше код -

{'__module__': '__main__', 'class_attribute': 99, 'class_method': 
      
       , '__dict__': 
       
        , '__weakref__': 
        
         , '__doc__': None} 
        
       
      

The instance attribute myClass.__dict__ as shown −

>>> a = myClass()
>>> a.class_method()
>>> print(a.__dict__)
{'instance_attribute': 'I am instance attribute'}