Учебники

Объектно-ориентированный Python — Расширенные возможности

В этом мы рассмотрим некоторые из расширенных функций, которые предоставляет Python

Основной синтаксис в нашем дизайне класса

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

>>> var1 = 'Hello'
>>> var2 = ' World!'
>>> var1 + var2
'Hello World!'
>>>
>>> var1.__add__(var2)
'Hello World!'
>>> num1 = 45
>>> num2 = 60
>>> num1.__add__(num2)
105
>>> var3 = ['a', 'b']
>>> var4 = ['hello', ' John']
>>> var3.__add__(var4)
['a', 'b', 'hello', ' John']

Так что, если нам нужно добавить магический метод __add__ к нашим собственным классам, мы могли бы сделать это тоже. Давайте попробуем сделать это.

У нас есть класс Sumlist, у которого есть конструктор __init__, который принимает список в качестве аргумента my_list.

class SumList(object):
   def __init__(self, my_list):
      self.mylist = my_list
   def __add__(self, other):
     new_list = [ x + y for x, y in zip(self.mylist, other.mylist)]

     return SumList(new_list)
   
   def __repr__(self):
      return str(self.mylist)

aa = SumList([3,6, 9, 12, 15])

bb = SumList([100, 200, 300, 400, 500])
cc = aa + bb # aa.__add__(bb)
print(cc) # should gives us a list ([103, 206, 309, 412, 515])

Выход

[103, 206, 309, 412, 515]

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

'abc' in var # var.__contains__('abc')
var == 'abc' # var.__eq__('abc')
var[1] # var.__getitem__(1)
var[1:3] # var.__getslice__(1, 3)
len(var) # var.__len__()
print(var) # var.__repr__()

Наследование от встроенных типов

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

В следующем примере мы наследуем от словаря, но затем мы реализуем один из его методов __setitem__. Это (setitem) вызывается, когда мы устанавливаем ключ и значение в словаре. Поскольку это магический метод, он будет вызываться неявно.

class MyDict(dict):

   def __setitem__(self, key, val):
      print('setting a key and value!')
      dict.__setitem__(self, key, val)

dd = MyDict()
dd['a'] = 10
dd['b'] = 20

for key in dd.keys():
   print('{0} = {1}'.format(key, dd[key]))

Выход

setting a key and value!
setting a key and value!
a = 10
b = 20

Давайте расширим наш предыдущий пример. Ниже мы назвали два магических метода __getitem__ и __setitem__, которые лучше вызывать, когда мы имеем дело с индексом списка.

# Mylist inherits from 'list' object but indexes from 1 instead for 0!
class Mylist(list): # inherits from list
   def __getitem__(self, index):
      if index == 0:
         raise IndexError
      if index > 0:
         index = index - 1
         return list.__getitem__(self, index) # this method is called when

# we access a value with subscript like x[1]
   def __setitem__(self, index, value):
      if index == 0:
         raise IndexError
      if index > 0:
      index = index - 1
      list.__setitem__(self, index, value)

x = Mylist(['a', 'b', 'c']) # __init__() inherited from builtin list

print(x) # __repr__() inherited from builtin list

x.append('HELLO'); # append() inherited from builtin list

print(x[1]) # 'a' (Mylist.__getitem__ cutomizes list superclass
               # method. index is 1, but reflects 0!

print (x[4]) # 'HELLO' (index is 4 but reflects 3!

Выход

['a', 'b', 'c']
a
HELLO

В приведенном выше примере мы устанавливаем список из трех элементов в Mylist и неявно вызывается метод __init__, а когда мы печатаем элемент x, мы получаем список из трех элементов ([‘a’, ‘b’, ‘c’]). Затем мы добавляем еще один элемент в этот список. Позже мы запрашиваем индекс 1 и индекс 4. Но если вы видите выходные данные, мы получаем элемент из (index-1) того, что мы просили. Как мы знаем, индексация списка начинается с 0, но здесь индексация начинается с 1 (поэтому мы получаем первый элемент списка).

Соглашения об именах

В этом мы рассмотрим имена, которые мы будем использовать для переменных, особенно частных переменных, и соглашений, используемых программистами Python по всему миру. Хотя переменные обозначены как приватные, но в Python нет конфиденциальности, и это сделано специально. Как и любые другие хорошо документированные языки, Python имеет соглашения об именах и стилях, которые он продвигает, хотя и не применяет их. Существует руководство по стилю, написанное « Гвидо ван Россумом», создателем Python, которое описывает лучшие практики и использование имени и называется PEP8. Вот ссылка для этого, https://www.python.org/dev/peps/pep-0008/

PEP расшифровывается как предложение по улучшению Python и представляет собой серию документации, которая распространяется сообществом Python для обсуждения предлагаемых изменений. Например, рекомендуется все,

  • Имена модулей — all_lower_case
  • Имена классов и имена исключений — CamelCase
  • Глобальные и локальные имена — all_lower_case
  • Функции и имена методов — all_lower_case
  • Константы — ALL_UPPER_CASE

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

Зачем соответствовать конвенции?

Мы можем следовать рекомендации PEP, которую мы можем получить,

  • Более знакомы подавляющему большинству разработчиков
  • Понятнее для большинства читателей вашего кода.
  • Будет соответствовать стилю других участников, которые работают на той же кодовой базе.
  • Знак профессионального разработчика программного обеспечения
  • Все вас примут.

Именование переменных — «Public» и «Private»

В Python, когда мы имеем дело с модулями и классами, мы обозначаем некоторые переменные или атрибут как закрытые. В Python не существует переменной экземпляра «Private», к которой нельзя получить доступ, кроме как внутри объекта. Приватный просто означает, что они просто не предназначены для использования пользователями кода, а предназначены для внутреннего использования. В общем, большинству разработчиков Python следует соглашение, например, имя с префиксом подчеркивания. _attrval (пример ниже) следует рассматривать как непубличную часть API или любого кода Python, будь то функция, метод или элемент данных. Ниже приведено соглашение об именах, которому мы следуем,

Открытые атрибуты или переменные (предназначенные для использования импортером этого модуля или пользователем этого класса) — normal_lower_case

Закрытые атрибуты или переменные (внутреннее использование модулем или классом) — _single_leading_underscore

Закрытые атрибуты, которые не должны быть разделены на подклассы — __double_leading_underscore

Магические атрибуты — __double_underscores__ (используйте их, не создавайте их)