Skip to content

Latest commit

 

History

History
403 lines (291 loc) · 14.8 KB

Object_2.md

File metadata and controls

403 lines (291 loc) · 14.8 KB

Объектно ориентированное программирование

Классы обь являются при помощи ключ слова class название класса должно начинаться с большой буквы.

    class Sample(object):
        pass

    print(x)
    print(type(x))

    # Вывод
    # <__main__.Sample object at 0x7f8d2b1b0518>
    # <class '__main__.Sample'>

При выводе класса можно увидеть его место положение в памяти компьютера.


Атрибуты и методы Объекта

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

Атрибуты создаются при помощи ключ слова self так образ можно обьявить атрибут или поле, которое будет принадлежать объекту класса.

self.atribute = something

Однако есть одна особенность конкретно языка питон, если в php мы могли обьявлять статические и динамические переменные вне какого либо метода, просто описывая его в классе.

В питоне требуется чтобы каждый динамический атрибут был определен в методе конструкторе __init__() и только там.

Методы создаются так же как и обычные функции, но в качестве первого аргумента должен идти аргумент self

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


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

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

Форма записи атрибутов класса, такова:

    class MyClass():

        static_atribute = "Это атрибут класса, он статичен"

        def __init__(self):
            print(MyClass.static_atribute)

    print(MyClass.static_atribute)
    x = MyClass()

Интересен тот факт, что к атрибуту класса можно обращаться при помощи не только способа MyClass.static_atribute но и через его объект, к примеру так:

    class MyClass():
        
        static_atribute = "Атрибут класса"
        def __init__(self):
            self.name = "атрибут обьекта"
        
    x = MyClass
    print(x.static_atribute)
    print(MyClass.static_atribute)

    # Вывод
    # Атрибут класса
    # Атрибут класса

Таким образом мы можем иметь доступ к атрибутам класса, как через сам класс, так и через его обьект.


Атрибут объекта и класса __dict__

У каждого объекта есть спец поле __dict__ в котором содержится таблица символов объекта, или просто его переменные.

Существует 2 атрибута __dict__ для объектов и для самого класса, есть такой класс:

    class My_Class():

        static_var = 'Это статичная переменная класса'        

        def __init__(self, name, my_list)
            self.name = name 
            self.my_list = my_list

В этом классе мы определили 2 переменные объекта и одну переменную класса, атрибут __dict__ для объекта будет иметь 2 переменные которые определены для объекта, то время как переменная класса будет доступна для __dict__ если мы вызываем его у класса.

Вот как это выглядит:

    var1 = My_Class('Название Первое', [1, 2, 3, 4, 5])
    var2 = My_Class('Название Второе', ['one', 'two'])

    print(var1.__dict__)
    print(var2.__dict__)
    print(My_Class.__dict__)

    # Вывод
    
    # var1.__dict__
    # {'name': 'Название Первое', 'my_list': [1, 2, 3, 4, 5]}

    # var2.__dict__
    # {'name': 'Название Второе', 'my_list': ['one', 'two']}

    # My_Class.__dict__
    # {
    #     '__module__': '__main__', 
    #     'static_var': 'Это статичная переменная класса', 
    #     '__init__': <function func8.<locals>.My_Class.__init__ at 0x7f96168df0d0>, 
    #     '__dict__': <attribute '__dict__' of 'My_Class' objects>,
    #     '__weakref__': <attribute '__weakref__' of 'My_Class' objects>,
    #     '__doc__': None
    # }

Так видим что когда мы обращаемся к объекту var2.__dict__ то и получаем переменные объекта, обращаясь к классу My_Class.__dict__ получаем все переменные класса.

В данном примере мы обращались на прямую к __dict__ но также функция vars() при получении аргумента в виде объекта, тоже обращается к атрибуту __dict__ этого объекта.

Два следующих примера полностью равнозначны:

    print(vars(var1))
    print(vars(var2))
    print(vars(My_Class))

    print(var1.__dict__)
    print(var2.__dict__)
    print(My_Class.__dict__)

Объект

Можно оперировать 2 сущностями, объектами и классами, мы можем создать объект класса, для создания объекта класса, записываем название класса и круглые скобки, что и означает создание объекта:

    class Point():
        def __init__(self, name):
            self.name = name

    obj = Point('Название')

    print(obj)
    print(type(obj))

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

Значение: <__main__.func5.<locals>.Point object at 0x7f09ab561e80>
Тип:      <class '__main__.func5.<locals>.Point'>

Создав объект мы можем оперировать его статическими и динамическими атрибутами.


Класс

Также помимо создания объекта, мы можем оперировать самим классом, для этого берем сам класс, для этого записываем название самого класса:

    class Point():
        def __init__(self, name):
            self.name = name

    print(Point)
    print(type(Point))

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

Значение:  <class '__main__.func5.<locals>.Point'>
Тип:       <class 'type'>

Модификаторы доступа public, protected, private

Стандартные модификаторы доступа работают так же как и в других языках программирования, public дает доступ всем, protected дает доступ в классе и его дочерних классах, но не из вне, private дает доступ только в самом классе и больше нигде.

В Python эти модификаторы указываются при помощи нижних подчеркиваний:

  1. public - любое обычное написание переменной atribut
  2. protected _ одно нижнее подчеркивание _atribut
  3. private - __ два нижних подчеркивания __atribut

Статические свойства/поля

Статические переменные класса можно указывать в самом классе, а не в методе инициализаторе __init__(self) для вызова этой статической переменной класса, требуется указать название самого класса и обратится к переменной, делается это так:

    class My_class():
        """Это статическая переменная класса"""
        static_var = "Статическая переменная класса"

        def __init__(self, x, y):
            """Это динамические переменные обьекта"""
            self.x = x
            self.y = y

        def show(self):
            print("Динамические:", self.x, self.y)
            print("Статические:", My_class.static_var)

    print("Статические:", My_class.static_var)
    obj = My_class(10, 20)
    obj.show()

    # Вывод
    # Статические: Статическая переменная класса
    # Динамические: 10 20
    # Статические: Статическая переменная класса

Мы можем обращаться к статическим переменным как внутри класса так и снаружи, также все статически переменные поддерживают модификаторы доступа.


Статические методы

Вот тут очень интересно, обычно требуется создать метод и дать ему декоратор @staticmethod также такие методы не требуют получения self как первый параметр.

Однако любой метод без первого аргумента self будет вести себя как статический метод, даже если он не обернут @staticmethod

По идеи любой метод обь явленный в классе, ведет себя как статический, То етсь будут работать оба варианта, что через метод без self и без @staticmethod что без self но с @staticmethod к примеру так:

    class Point():
        static_count = 10

        def __init__(self, x, y):
            self.x = x
            self.y = y

        def setCoords(self, x, y):
            self.x = x
            self.y = y

        def getCoords(self):
            print(f"getCoords {self.x}, {self.y}")

        @staticmethod
        def geCountOne():
            print(f"geCountOne = {Point.static_count}")

        def geCountTwo():
            print(f"geCountTwo = {Point.static_count}")

    pt = Point(1, 2)
    
    pt.getCoords()
    pt.setCoords(10, 20)
    pt.getCoords()
    
    Point.geCountOne()
    Point.geCountTwo()

    # Вывод
    # getCoords 1, 2
    # getCoords 10, 20
    # geCountOne = 10
    # geCountTwo = 10

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

Основа механизма инкапсуляции это ограничение доступа к методам и полям объекта и класса.

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

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

    class Point():

        def __init__(self):
            print("Класс создан")
        
        def setCoords(self, x, y):
            self.__x = x
            self.__y = y

        def getCoords(self):
            return self.__x, self.__y

    x = Point()
    x.setCoords(10, 20)
    print(x.getCoords())

Таким образом через промежуточные методы мы можем установить данные даже в закрытые переменные, также в сеттеры можно устанавливать проверку на то какие именно данные будут внесены в закрытые переменные.

К примеру такая проверка:

    def setCoords(self, x, y):
        if (isinstance(x, int) or isinstance(x, float)) and \
            (isinstance(y, int) or isinstance(y, float)) :
            self.__x = x
            self.__y = y    else:
            print("Координаты должны быть числами")

При такой проверке мы не сможем вставить в переменную неправильные данные, также можно вынести валидацию данных в отдельный метод, и проверять их так:

    class Point():

        def __init__(self):
            print("Класс Point создан")

        def __checkValue(x):
            if isinstance(x, int) or isinstance(x, float):
                return True
            return False

        def setCoords(self, x, y):
            if __checkValue(x) and __checkValue(y):
                self.__x = x
                self.__y = y 
            else:
                print("Координаты должны быть числами")

        def getCoords(self):
            return self.__x, self.__y